cache.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. /*
  2. Caching file system proxy
  3. Copyright (C) 2004 Miklos Szeredi <miklos@szeredi.hu>
  4. This program can be distributed under the terms of the GNU GPL.
  5. See the file COPYING.
  6. */
  7. #include "cache.h"
  8. #include <stdio.h>
  9. #include <assert.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <errno.h>
  13. #include <glib.h>
  14. #include <pthread.h>
  15. struct cache {
  16. int on;
  17. unsigned int stat_timeout_secs;
  18. unsigned int dir_timeout_secs;
  19. unsigned int link_timeout_secs;
  20. unsigned int max_size;
  21. unsigned int clean_interval_secs;
  22. unsigned int min_clean_interval_secs;
  23. struct fuse_operations *next_oper;
  24. GHashTable *table;
  25. pthread_mutex_t lock;
  26. time_t last_cleaned;
  27. uint64_t write_ctr;
  28. };
  29. static struct cache cache;
  30. struct node {
  31. struct stat stat;
  32. time_t stat_valid;
  33. char **dir;
  34. time_t dir_valid;
  35. char *link;
  36. time_t link_valid;
  37. time_t valid;
  38. };
  39. struct readdir_handle {
  40. const char *path;
  41. void *buf;
  42. fuse_fill_dir_t filler;
  43. GPtrArray *dir;
  44. uint64_t wrctr;
  45. };
  46. struct file_handle {
  47. /* Did we send an open request to the underlying fs? */
  48. int is_open;
  49. /* If so, this will hold its handle */
  50. unsigned long fs_fh;
  51. };
  52. static void free_node(gpointer node_)
  53. {
  54. struct node *node = (struct node *) node_;
  55. g_strfreev(node->dir);
  56. g_free(node);
  57. }
  58. static int cache_clean_entry(void *key_, struct node *node, time_t *now)
  59. {
  60. (void) key_;
  61. if (*now > node->valid)
  62. return TRUE;
  63. else
  64. return FALSE;
  65. }
  66. static void cache_clean(void)
  67. {
  68. time_t now = time(NULL);
  69. if (now > cache.last_cleaned + cache.min_clean_interval_secs &&
  70. (g_hash_table_size(cache.table) > cache.max_size ||
  71. now > cache.last_cleaned + cache.clean_interval_secs)) {
  72. g_hash_table_foreach_remove(cache.table,
  73. (GHRFunc) cache_clean_entry, &now);
  74. cache.last_cleaned = now;
  75. }
  76. }
  77. static struct node *cache_lookup(const char *path)
  78. {
  79. return (struct node *) g_hash_table_lookup(cache.table, path);
  80. }
  81. static void cache_purge(const char *path)
  82. {
  83. g_hash_table_remove(cache.table, path);
  84. }
  85. static void cache_purge_parent(const char *path)
  86. {
  87. const char *s = strrchr(path, '/');
  88. if (s) {
  89. if (s == path)
  90. g_hash_table_remove(cache.table, "/");
  91. else {
  92. char *parent = g_strndup(path, s - path);
  93. cache_purge(parent);
  94. g_free(parent);
  95. }
  96. }
  97. }
  98. void cache_invalidate(const char *path)
  99. {
  100. pthread_mutex_lock(&cache.lock);
  101. cache_purge(path);
  102. pthread_mutex_unlock(&cache.lock);
  103. }
  104. static void cache_invalidate_write(const char *path)
  105. {
  106. pthread_mutex_lock(&cache.lock);
  107. cache_purge(path);
  108. cache.write_ctr++;
  109. pthread_mutex_unlock(&cache.lock);
  110. }
  111. static void cache_invalidate_dir(const char *path)
  112. {
  113. pthread_mutex_lock(&cache.lock);
  114. cache_purge(path);
  115. cache_purge_parent(path);
  116. pthread_mutex_unlock(&cache.lock);
  117. }
  118. static int cache_del_children(const char *key, void *val_, const char *path)
  119. {
  120. (void) val_;
  121. if (strncmp(key, path, strlen(path)) == 0)
  122. return TRUE;
  123. else
  124. return FALSE;
  125. }
  126. static void cache_do_rename(const char *from, const char *to)
  127. {
  128. pthread_mutex_lock(&cache.lock);
  129. g_hash_table_foreach_remove(cache.table, (GHRFunc) cache_del_children,
  130. (char *) from);
  131. cache_purge(from);
  132. cache_purge(to);
  133. cache_purge_parent(from);
  134. cache_purge_parent(to);
  135. pthread_mutex_unlock(&cache.lock);
  136. }
  137. static struct node *cache_get(const char *path)
  138. {
  139. struct node *node = cache_lookup(path);
  140. if (node == NULL) {
  141. char *pathcopy = g_strdup(path);
  142. node = g_new0(struct node, 1);
  143. g_hash_table_insert(cache.table, pathcopy, node);
  144. }
  145. return node;
  146. }
  147. void cache_add_attr(const char *path, const struct stat *stbuf, uint64_t wrctr)
  148. {
  149. struct node *node;
  150. pthread_mutex_lock(&cache.lock);
  151. if (wrctr == cache.write_ctr) {
  152. node = cache_get(path);
  153. node->stat = *stbuf;
  154. node->stat_valid = time(NULL) + cache.stat_timeout_secs;
  155. if (node->stat_valid > node->valid)
  156. node->valid = node->stat_valid;
  157. cache_clean();
  158. }
  159. pthread_mutex_unlock(&cache.lock);
  160. }
  161. static void cache_add_dir(const char *path, char **dir)
  162. {
  163. struct node *node;
  164. pthread_mutex_lock(&cache.lock);
  165. node = cache_get(path);
  166. g_strfreev(node->dir);
  167. node->dir = dir;
  168. node->dir_valid = time(NULL) + cache.dir_timeout_secs;
  169. if (node->dir_valid > node->valid)
  170. node->valid = node->dir_valid;
  171. cache_clean();
  172. pthread_mutex_unlock(&cache.lock);
  173. }
  174. static size_t my_strnlen(const char *s, size_t maxsize)
  175. {
  176. const char *p;
  177. for (p = s; maxsize && *p; maxsize--, p++);
  178. return p - s;
  179. }
  180. void cache_add_link(const char *path, const char *link, size_t size)
  181. {
  182. struct node *node;
  183. pthread_mutex_lock(&cache.lock);
  184. node = cache_get(path);
  185. g_free(node->link);
  186. node->link = g_strndup(link, my_strnlen(link, size-1));
  187. node->link_valid = time(NULL) + cache.link_timeout_secs;
  188. if (node->link_valid > node->valid)
  189. node->valid = node->link_valid;
  190. cache_clean();
  191. pthread_mutex_unlock(&cache.lock);
  192. }
  193. static int cache_get_attr(const char *path, struct stat *stbuf)
  194. {
  195. struct node *node;
  196. int err = -EAGAIN;
  197. pthread_mutex_lock(&cache.lock);
  198. node = cache_lookup(path);
  199. if (node != NULL) {
  200. time_t now = time(NULL);
  201. if (node->stat_valid - now >= 0) {
  202. *stbuf = node->stat;
  203. err = 0;
  204. }
  205. }
  206. pthread_mutex_unlock(&cache.lock);
  207. return err;
  208. }
  209. uint64_t cache_get_write_ctr(void)
  210. {
  211. uint64_t res;
  212. pthread_mutex_lock(&cache.lock);
  213. res = cache.write_ctr;
  214. pthread_mutex_unlock(&cache.lock);
  215. return res;
  216. }
  217. static void *cache_init(struct fuse_conn_info *conn,
  218. struct fuse_config *cfg)
  219. {
  220. void *res;
  221. res = cache.next_oper->init(conn, cfg);
  222. // Cache requires a path for each request
  223. cfg->nullpath_ok = 0;
  224. return res;
  225. }
  226. static int cache_getattr(const char *path, struct stat *stbuf,
  227. struct fuse_file_info *fi)
  228. {
  229. int err = cache_get_attr(path, stbuf);
  230. if (err) {
  231. uint64_t wrctr = cache_get_write_ctr();
  232. err = cache.next_oper->getattr(path, stbuf, fi);
  233. if (!err)
  234. cache_add_attr(path, stbuf, wrctr);
  235. }
  236. return err;
  237. }
  238. static int cache_readlink(const char *path, char *buf, size_t size)
  239. {
  240. struct node *node;
  241. int err;
  242. pthread_mutex_lock(&cache.lock);
  243. node = cache_lookup(path);
  244. if (node != NULL) {
  245. time_t now = time(NULL);
  246. if (node->link_valid - now >= 0) {
  247. strncpy(buf, node->link, size-1);
  248. buf[size-1] = '\0';
  249. pthread_mutex_unlock(&cache.lock);
  250. return 0;
  251. }
  252. }
  253. pthread_mutex_unlock(&cache.lock);
  254. err = cache.next_oper->readlink(path, buf, size);
  255. if (!err)
  256. cache_add_link(path, buf, size);
  257. return err;
  258. }
  259. static int cache_opendir(const char *path, struct fuse_file_info *fi)
  260. {
  261. (void) path;
  262. struct file_handle *cfi;
  263. cfi = malloc(sizeof(struct file_handle));
  264. if(cfi == NULL)
  265. return -ENOMEM;
  266. cfi->is_open = 0;
  267. fi->fh = (unsigned long) cfi;
  268. return 0;
  269. }
  270. static int cache_releasedir(const char *path, struct fuse_file_info *fi)
  271. {
  272. int err;
  273. struct file_handle *cfi;
  274. cfi = (struct file_handle*) fi->fh;
  275. if(cfi->is_open) {
  276. fi->fh = cfi->fs_fh;
  277. err = cache.next_oper->releasedir(path, fi);
  278. } else
  279. err = 0;
  280. free(cfi);
  281. return err;
  282. }
  283. static int cache_dirfill (void *buf, const char *name,
  284. const struct stat *stbuf, off_t off,
  285. enum fuse_fill_dir_flags flags)
  286. {
  287. int err;
  288. struct readdir_handle *ch;
  289. ch = (struct readdir_handle*) buf;
  290. err = ch->filler(ch->buf, name, stbuf, off, flags);
  291. if (!err) {
  292. g_ptr_array_add(ch->dir, g_strdup(name));
  293. if (stbuf->st_mode & S_IFMT) {
  294. char *fullpath;
  295. const char *basepath = !ch->path[1] ? "" : ch->path;
  296. fullpath = g_strdup_printf("%s/%s", basepath, name);
  297. cache_add_attr(fullpath, stbuf, ch->wrctr);
  298. g_free(fullpath);
  299. }
  300. }
  301. return err;
  302. }
  303. static int cache_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
  304. off_t offset, struct fuse_file_info *fi,
  305. enum fuse_readdir_flags flags)
  306. {
  307. struct readdir_handle ch;
  308. struct file_handle *cfi;
  309. int err;
  310. char **dir;
  311. struct node *node;
  312. assert(offset == 0);
  313. pthread_mutex_lock(&cache.lock);
  314. node = cache_lookup(path);
  315. if (node != NULL && node->dir != NULL) {
  316. time_t now = time(NULL);
  317. if (node->dir_valid - now >= 0) {
  318. for(dir = node->dir; *dir != NULL; dir++)
  319. // FIXME: What about st_mode?
  320. filler(buf, *dir, NULL, 0, 0);
  321. pthread_mutex_unlock(&cache.lock);
  322. return 0;
  323. }
  324. }
  325. pthread_mutex_unlock(&cache.lock);
  326. cfi = (struct file_handle*) fi->fh;
  327. if(cfi->is_open)
  328. fi->fh = cfi->fs_fh;
  329. else {
  330. if(cache.next_oper->opendir) {
  331. err = cache.next_oper->opendir(path, fi);
  332. if(err)
  333. return err;
  334. }
  335. cfi->is_open = 1;
  336. cfi->fs_fh = fi->fh;
  337. }
  338. ch.path = path;
  339. ch.buf = buf;
  340. ch.filler = filler;
  341. ch.dir = g_ptr_array_new();
  342. ch.wrctr = cache_get_write_ctr();
  343. err = cache.next_oper->readdir(path, &ch, cache_dirfill, offset, fi, flags);
  344. g_ptr_array_add(ch.dir, NULL);
  345. dir = (char **) ch.dir->pdata;
  346. if (!err) {
  347. cache_add_dir(path, dir);
  348. } else {
  349. g_strfreev(dir);
  350. }
  351. g_ptr_array_free(ch.dir, FALSE);
  352. return err;
  353. }
  354. static int cache_mknod(const char *path, mode_t mode, dev_t rdev)
  355. {
  356. int err = cache.next_oper->mknod(path, mode, rdev);
  357. if (!err)
  358. cache_invalidate_dir(path);
  359. return err;
  360. }
  361. static int cache_mkdir(const char *path, mode_t mode)
  362. {
  363. int err = cache.next_oper->mkdir(path, mode);
  364. if (!err)
  365. cache_invalidate_dir(path);
  366. return err;
  367. }
  368. static int cache_unlink(const char *path)
  369. {
  370. int err = cache.next_oper->unlink(path);
  371. if (!err)
  372. cache_invalidate_dir(path);
  373. return err;
  374. }
  375. static int cache_rmdir(const char *path)
  376. {
  377. int err = cache.next_oper->rmdir(path);
  378. if (!err)
  379. cache_invalidate_dir(path);
  380. return err;
  381. }
  382. static int cache_symlink(const char *from, const char *to)
  383. {
  384. int err = cache.next_oper->symlink(from, to);
  385. if (!err)
  386. cache_invalidate_dir(to);
  387. return err;
  388. }
  389. static int cache_rename(const char *from, const char *to, unsigned int flags)
  390. {
  391. int err = cache.next_oper->rename(from, to, flags);
  392. if (!err)
  393. cache_do_rename(from, to);
  394. return err;
  395. }
  396. static int cache_link(const char *from, const char *to)
  397. {
  398. int err = cache.next_oper->link(from, to);
  399. if (!err) {
  400. cache_invalidate(from);
  401. cache_invalidate_dir(to);
  402. }
  403. return err;
  404. }
  405. static int cache_chmod(const char *path, mode_t mode,
  406. struct fuse_file_info *fi)
  407. {
  408. int err = cache.next_oper->chmod(path, mode, fi);
  409. if (!err)
  410. cache_invalidate(path);
  411. return err;
  412. }
  413. static int cache_chown(const char *path, uid_t uid, gid_t gid,
  414. struct fuse_file_info *fi)
  415. {
  416. int err = cache.next_oper->chown(path, uid, gid, fi);
  417. if (!err)
  418. cache_invalidate(path);
  419. return err;
  420. }
  421. static int cache_utimens(const char *path, const struct timespec tv[2],
  422. struct fuse_file_info *fi)
  423. {
  424. int err = cache.next_oper->utimens(path, tv, fi);
  425. if (!err)
  426. cache_invalidate(path);
  427. return err;
  428. }
  429. static int cache_write(const char *path, const char *buf, size_t size,
  430. off_t offset, struct fuse_file_info *fi)
  431. {
  432. int res = cache.next_oper->write(path, buf, size, offset, fi);
  433. if (res >= 0)
  434. cache_invalidate_write(path);
  435. return res;
  436. }
  437. static int cache_create(const char *path, mode_t mode,
  438. struct fuse_file_info *fi)
  439. {
  440. int err = cache.next_oper->create(path, mode, fi);
  441. if (!err)
  442. cache_invalidate_dir(path);
  443. return err;
  444. }
  445. static int cache_truncate(const char *path, off_t size,
  446. struct fuse_file_info *fi)
  447. {
  448. int err = cache.next_oper->truncate(path, size, fi);
  449. if (!err)
  450. cache_invalidate(path);
  451. return err;
  452. }
  453. static void cache_fill(struct fuse_operations *oper,
  454. struct fuse_operations *cache_oper)
  455. {
  456. cache_oper->access = oper->access;
  457. cache_oper->chmod = oper->chmod ? cache_chmod : NULL;
  458. cache_oper->chown = oper->chown ? cache_chown : NULL;
  459. cache_oper->create = oper->create ? cache_create : NULL;
  460. cache_oper->flush = oper->flush;
  461. cache_oper->fsync = oper->fsync;
  462. cache_oper->getattr = oper->getattr ? cache_getattr : NULL;
  463. cache_oper->getxattr = oper->getxattr;
  464. cache_oper->init = cache_init;
  465. cache_oper->link = oper->link ? cache_link : NULL;
  466. cache_oper->listxattr = oper->listxattr;
  467. cache_oper->mkdir = oper->mkdir ? cache_mkdir : NULL;
  468. cache_oper->mknod = oper->mknod ? cache_mknod : NULL;
  469. cache_oper->open = oper->open;
  470. cache_oper->opendir = cache_opendir;
  471. cache_oper->read = oper->read;
  472. cache_oper->readdir = oper->readdir ? cache_readdir : NULL;
  473. cache_oper->readlink = oper->readlink ? cache_readlink : NULL;
  474. cache_oper->release = oper->release;
  475. cache_oper->releasedir = cache_releasedir;
  476. cache_oper->removexattr = oper->removexattr;
  477. cache_oper->rename = oper->rename ? cache_rename : NULL;
  478. cache_oper->rmdir = oper->rmdir ? cache_rmdir : NULL;
  479. cache_oper->setxattr = oper->setxattr;
  480. cache_oper->statfs = oper->statfs;
  481. cache_oper->symlink = oper->symlink ? cache_symlink : NULL;
  482. cache_oper->truncate = oper->truncate ? cache_truncate : NULL;
  483. cache_oper->unlink = oper->unlink ? cache_unlink : NULL;
  484. cache_oper->utimens = oper->utimens ? cache_utimens : NULL;
  485. cache_oper->write = oper->write ? cache_write : NULL;
  486. }
  487. struct fuse_operations *cache_wrap(struct fuse_operations *oper)
  488. {
  489. static struct fuse_operations cache_oper;
  490. cache.next_oper = oper;
  491. cache_fill(oper, &cache_oper);
  492. pthread_mutex_init(&cache.lock, NULL);
  493. cache.table = g_hash_table_new_full(g_str_hash, g_str_equal,
  494. g_free, free_node);
  495. if (cache.table == NULL) {
  496. fprintf(stderr, "failed to create cache\n");
  497. return NULL;
  498. }
  499. return &cache_oper;
  500. }
  501. static const struct fuse_opt cache_opts[] = {
  502. { "dcache_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 },
  503. { "dcache_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 },
  504. { "dcache_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 },
  505. { "dcache_stat_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 },
  506. { "dcache_dir_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 },
  507. { "dcache_link_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 },
  508. { "dcache_max_size=%u", offsetof(struct cache, max_size), 0 },
  509. { "dcache_clean_interval=%u", offsetof(struct cache,
  510. clean_interval_secs), 0 },
  511. { "dcache_min_clean_interval=%u", offsetof(struct cache,
  512. min_clean_interval_secs), 0 },
  513. /* For backwards compatibility */
  514. { "cache_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 },
  515. { "cache_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 },
  516. { "cache_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 },
  517. { "cache_stat_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 },
  518. { "cache_dir_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 },
  519. { "cache_link_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 },
  520. { "cache_max_size=%u", offsetof(struct cache, max_size), 0 },
  521. { "cache_clean_interval=%u", offsetof(struct cache,
  522. clean_interval_secs), 0 },
  523. { "cache_min_clean_interval=%u", offsetof(struct cache,
  524. min_clean_interval_secs), 0 },
  525. FUSE_OPT_END
  526. };
  527. int cache_parse_options(struct fuse_args *args)
  528. {
  529. cache.stat_timeout_secs = DEFAULT_CACHE_TIMEOUT_SECS;
  530. cache.dir_timeout_secs = DEFAULT_CACHE_TIMEOUT_SECS;
  531. cache.link_timeout_secs = DEFAULT_CACHE_TIMEOUT_SECS;
  532. cache.max_size = DEFAULT_MAX_CACHE_SIZE;
  533. cache.clean_interval_secs = DEFAULT_CACHE_CLEAN_INTERVAL_SECS;
  534. cache.min_clean_interval_secs = DEFAULT_MIN_CACHE_CLEAN_INTERVAL_SECS;
  535. return fuse_opt_parse(args, &cache, cache_opts, NULL);
  536. }