123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- /*
- Caching file system proxy
- Copyright (C) 2004 Miklos Szeredi <miklos@szeredi.hu>
- This program can be distributed under the terms of the GNU GPL.
- See the file COPYING.
- */
- #include "cache.h"
- #include <stdio.h>
- #include <assert.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include <glib.h>
- #include <pthread.h>
- struct cache {
- int on;
- unsigned int stat_timeout_secs;
- unsigned int dir_timeout_secs;
- unsigned int link_timeout_secs;
- unsigned int max_size;
- unsigned int clean_interval_secs;
- unsigned int min_clean_interval_secs;
- struct fuse_operations *next_oper;
- GHashTable *table;
- pthread_mutex_t lock;
- time_t last_cleaned;
- uint64_t write_ctr;
- };
- static struct cache cache;
- struct node {
- struct stat stat;
- time_t stat_valid;
- char **dir;
- time_t dir_valid;
- char *link;
- time_t link_valid;
- time_t valid;
- };
- struct readdir_handle {
- const char *path;
- void *buf;
- fuse_fill_dir_t filler;
- GPtrArray *dir;
- uint64_t wrctr;
- };
- struct file_handle {
- /* Did we send an open request to the underlying fs? */
- int is_open;
- /* If so, this will hold its handle */
- unsigned long fs_fh;
- };
- static void free_node(gpointer node_)
- {
- struct node *node = (struct node *) node_;
- g_strfreev(node->dir);
- g_free(node);
- }
- static int cache_clean_entry(void *key_, struct node *node, time_t *now)
- {
- (void) key_;
- if (*now > node->valid)
- return TRUE;
- else
- return FALSE;
- }
- static void cache_clean(void)
- {
- time_t now = time(NULL);
- if (now > cache.last_cleaned + cache.min_clean_interval_secs &&
- (g_hash_table_size(cache.table) > cache.max_size ||
- now > cache.last_cleaned + cache.clean_interval_secs)) {
- g_hash_table_foreach_remove(cache.table,
- (GHRFunc) cache_clean_entry, &now);
- cache.last_cleaned = now;
- }
- }
- static struct node *cache_lookup(const char *path)
- {
- return (struct node *) g_hash_table_lookup(cache.table, path);
- }
- static void cache_purge(const char *path)
- {
- g_hash_table_remove(cache.table, path);
- }
- static void cache_purge_parent(const char *path)
- {
- const char *s = strrchr(path, '/');
- if (s) {
- if (s == path)
- g_hash_table_remove(cache.table, "/");
- else {
- char *parent = g_strndup(path, s - path);
- cache_purge(parent);
- g_free(parent);
- }
- }
- }
- void cache_invalidate(const char *path)
- {
- pthread_mutex_lock(&cache.lock);
- cache_purge(path);
- pthread_mutex_unlock(&cache.lock);
- }
- static void cache_invalidate_write(const char *path)
- {
- pthread_mutex_lock(&cache.lock);
- cache_purge(path);
- cache.write_ctr++;
- pthread_mutex_unlock(&cache.lock);
- }
- static void cache_invalidate_dir(const char *path)
- {
- pthread_mutex_lock(&cache.lock);
- cache_purge(path);
- cache_purge_parent(path);
- pthread_mutex_unlock(&cache.lock);
- }
- static int cache_del_children(const char *key, void *val_, const char *path)
- {
- (void) val_;
- if (strncmp(key, path, strlen(path)) == 0)
- return TRUE;
- else
- return FALSE;
- }
- static void cache_do_rename(const char *from, const char *to)
- {
- pthread_mutex_lock(&cache.lock);
- g_hash_table_foreach_remove(cache.table, (GHRFunc) cache_del_children,
- (char *) from);
- cache_purge(from);
- cache_purge(to);
- cache_purge_parent(from);
- cache_purge_parent(to);
- pthread_mutex_unlock(&cache.lock);
- }
- static struct node *cache_get(const char *path)
- {
- struct node *node = cache_lookup(path);
- if (node == NULL) {
- char *pathcopy = g_strdup(path);
- node = g_new0(struct node, 1);
- g_hash_table_insert(cache.table, pathcopy, node);
- }
- return node;
- }
- void cache_add_attr(const char *path, const struct stat *stbuf, uint64_t wrctr)
- {
- struct node *node;
- pthread_mutex_lock(&cache.lock);
- if (wrctr == cache.write_ctr) {
- node = cache_get(path);
- node->stat = *stbuf;
- node->stat_valid = time(NULL) + cache.stat_timeout_secs;
- if (node->stat_valid > node->valid)
- node->valid = node->stat_valid;
- cache_clean();
- }
- pthread_mutex_unlock(&cache.lock);
- }
- static void cache_add_dir(const char *path, char **dir)
- {
- struct node *node;
- pthread_mutex_lock(&cache.lock);
- node = cache_get(path);
- g_strfreev(node->dir);
- node->dir = dir;
- node->dir_valid = time(NULL) + cache.dir_timeout_secs;
- if (node->dir_valid > node->valid)
- node->valid = node->dir_valid;
- cache_clean();
- pthread_mutex_unlock(&cache.lock);
- }
- static size_t my_strnlen(const char *s, size_t maxsize)
- {
- const char *p;
- for (p = s; maxsize && *p; maxsize--, p++);
- return p - s;
- }
- void cache_add_link(const char *path, const char *link, size_t size)
- {
- struct node *node;
- pthread_mutex_lock(&cache.lock);
- node = cache_get(path);
- g_free(node->link);
- node->link = g_strndup(link, my_strnlen(link, size-1));
- node->link_valid = time(NULL) + cache.link_timeout_secs;
- if (node->link_valid > node->valid)
- node->valid = node->link_valid;
- cache_clean();
- pthread_mutex_unlock(&cache.lock);
- }
- static int cache_get_attr(const char *path, struct stat *stbuf)
- {
- struct node *node;
- int err = -EAGAIN;
- pthread_mutex_lock(&cache.lock);
- node = cache_lookup(path);
- if (node != NULL) {
- time_t now = time(NULL);
- if (node->stat_valid - now >= 0) {
- *stbuf = node->stat;
- err = 0;
- }
- }
- pthread_mutex_unlock(&cache.lock);
- return err;
- }
- uint64_t cache_get_write_ctr(void)
- {
- uint64_t res;
- pthread_mutex_lock(&cache.lock);
- res = cache.write_ctr;
- pthread_mutex_unlock(&cache.lock);
- return res;
- }
- static void *cache_init(struct fuse_conn_info *conn,
- struct fuse_config *cfg)
- {
- void *res;
- res = cache.next_oper->init(conn, cfg);
- // Cache requires a path for each request
- cfg->nullpath_ok = 0;
- return res;
- }
- static int cache_getattr(const char *path, struct stat *stbuf,
- struct fuse_file_info *fi)
- {
- int err = cache_get_attr(path, stbuf);
- if (err) {
- uint64_t wrctr = cache_get_write_ctr();
- err = cache.next_oper->getattr(path, stbuf, fi);
- if (!err)
- cache_add_attr(path, stbuf, wrctr);
- }
- return err;
- }
- static int cache_readlink(const char *path, char *buf, size_t size)
- {
- struct node *node;
- int err;
- pthread_mutex_lock(&cache.lock);
- node = cache_lookup(path);
- if (node != NULL) {
- time_t now = time(NULL);
- if (node->link_valid - now >= 0) {
- strncpy(buf, node->link, size-1);
- buf[size-1] = '\0';
- pthread_mutex_unlock(&cache.lock);
- return 0;
- }
- }
- pthread_mutex_unlock(&cache.lock);
- err = cache.next_oper->readlink(path, buf, size);
- if (!err)
- cache_add_link(path, buf, size);
- return err;
- }
- static int cache_opendir(const char *path, struct fuse_file_info *fi)
- {
- (void) path;
- struct file_handle *cfi;
- cfi = malloc(sizeof(struct file_handle));
- if(cfi == NULL)
- return -ENOMEM;
- cfi->is_open = 0;
- fi->fh = (unsigned long) cfi;
- return 0;
- }
- /* There's no dirs to release */
- static int cache_releasedir(const char *path, struct fuse_file_info *fi) {
- (void) path;
- (void) fi;
- return 0;
- }
- static int cache_dirfill (void *buf, const char *name,
- const struct stat *stbuf, off_t off,
- enum fuse_fill_dir_flags flags)
- {
- int err;
- struct readdir_handle *ch;
- ch = (struct readdir_handle*) buf;
- err = ch->filler(ch->buf, name, stbuf, off, flags);
- if (!err) {
- g_ptr_array_add(ch->dir, g_strdup(name));
- if (stbuf->st_mode & S_IFMT) {
- char *fullpath;
- const char *basepath = !ch->path[1] ? "" : ch->path;
- fullpath = g_strdup_printf("%s/%s", basepath, name);
- cache_add_attr(fullpath, stbuf, ch->wrctr);
- g_free(fullpath);
- }
- }
- return err;
- }
- static int cache_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
- off_t offset, struct fuse_file_info *fi,
- enum fuse_readdir_flags flags)
- {
- struct readdir_handle ch;
- struct file_handle *cfi;
- int err;
- char **dir;
- struct node *node;
- assert(offset == 0);
- pthread_mutex_lock(&cache.lock);
- node = cache_lookup(path);
- if (node != NULL && node->dir != NULL) {
- time_t now = time(NULL);
- if (node->dir_valid - now >= 0) {
- for(dir = node->dir; *dir != NULL; dir++)
- // FIXME: What about st_mode?
- filler(buf, *dir, NULL, 0, 0);
- pthread_mutex_unlock(&cache.lock);
- return 0;
- }
- }
- pthread_mutex_unlock(&cache.lock);
- cfi = (struct file_handle *)(intptr_t) fi->fh;
- if(cfi->is_open)
- fi->fh = cfi->fs_fh;
- else {
- if(cache.next_oper->opendir) {
- err = cache.next_oper->opendir(path, fi);
- if(err)
- return err;
- }
- cfi->is_open = 1;
- cfi->fs_fh = fi->fh;
- }
- ch.path = path;
- ch.buf = buf;
- ch.filler = filler;
- ch.dir = g_ptr_array_new();
- ch.wrctr = cache_get_write_ctr();
- err = cache.next_oper->readdir(path, &ch, cache_dirfill, offset, fi, flags);
- g_ptr_array_add(ch.dir, NULL);
- dir = (char **) ch.dir->pdata;
- if (!err) {
- cache_add_dir(path, dir);
- } else {
- g_strfreev(dir);
- }
- g_ptr_array_free(ch.dir, FALSE);
- return err;
- }
- static int cache_mknod(const char *path, mode_t mode, dev_t rdev)
- {
- int err = cache.next_oper->mknod(path, mode, rdev);
- if (!err)
- cache_invalidate_dir(path);
- return err;
- }
- static int cache_mkdir(const char *path, mode_t mode)
- {
- int err = cache.next_oper->mkdir(path, mode);
- if (!err)
- cache_invalidate_dir(path);
- return err;
- }
- static int cache_unlink(const char *path)
- {
- int err = cache.next_oper->unlink(path);
- if (!err)
- cache_invalidate_dir(path);
- return err;
- }
- static int cache_rmdir(const char *path)
- {
- int err = cache.next_oper->rmdir(path);
- if (!err)
- cache_invalidate_dir(path);
- return err;
- }
- static int cache_symlink(const char *from, const char *to)
- {
- int err = cache.next_oper->symlink(from, to);
- if (!err)
- cache_invalidate_dir(to);
- return err;
- }
- static int cache_rename(const char *from, const char *to, unsigned int flags)
- {
- int err = cache.next_oper->rename(from, to, flags);
- if (!err)
- cache_do_rename(from, to);
- return err;
- }
- static int cache_link(const char *from, const char *to)
- {
- int err = cache.next_oper->link(from, to);
- if (!err) {
- cache_invalidate(from);
- cache_invalidate_dir(to);
- }
- return err;
- }
- static int cache_chmod(const char *path, mode_t mode,
- struct fuse_file_info *fi)
- {
- int err = cache.next_oper->chmod(path, mode, fi);
- if (!err)
- cache_invalidate(path);
- return err;
- }
- static int cache_chown(const char *path, uid_t uid, gid_t gid,
- struct fuse_file_info *fi)
- {
- int err = cache.next_oper->chown(path, uid, gid, fi);
- if (!err)
- cache_invalidate(path);
- return err;
- }
- static int cache_utimens(const char *path, const struct timespec tv[2],
- struct fuse_file_info *fi)
- {
- int err = cache.next_oper->utimens(path, tv, fi);
- if (!err)
- cache_invalidate(path);
- return err;
- }
- static int cache_write(const char *path, const char *buf, size_t size,
- off_t offset, struct fuse_file_info *fi)
- {
- int res = cache.next_oper->write(path, buf, size, offset, fi);
- if (res >= 0)
- cache_invalidate_write(path);
- return res;
- }
- static int cache_create(const char *path, mode_t mode,
- struct fuse_file_info *fi)
- {
- int err = cache.next_oper->create(path, mode, fi);
- if (!err)
- cache_invalidate_dir(path);
- return err;
- }
- static int cache_truncate(const char *path, off_t size,
- struct fuse_file_info *fi)
- {
- int err = cache.next_oper->truncate(path, size, fi);
- if (!err)
- cache_invalidate(path);
- return err;
- }
- static void cache_fill(struct fuse_operations *oper,
- struct fuse_operations *cache_oper)
- {
- cache_oper->access = oper->access;
- cache_oper->chmod = oper->chmod ? cache_chmod : NULL;
- cache_oper->chown = oper->chown ? cache_chown : NULL;
- cache_oper->create = oper->create ? cache_create : NULL;
- cache_oper->flush = oper->flush;
- cache_oper->fsync = oper->fsync;
- cache_oper->getattr = oper->getattr ? cache_getattr : NULL;
- cache_oper->getxattr = oper->getxattr;
- cache_oper->init = cache_init;
- cache_oper->link = oper->link ? cache_link : NULL;
- cache_oper->listxattr = oper->listxattr;
- cache_oper->mkdir = oper->mkdir ? cache_mkdir : NULL;
- cache_oper->mknod = oper->mknod ? cache_mknod : NULL;
- cache_oper->open = oper->open;
- cache_oper->opendir = cache_opendir;
- cache_oper->read = oper->read;
- cache_oper->readdir = oper->readdir ? cache_readdir : NULL;
- cache_oper->readlink = oper->readlink ? cache_readlink : NULL;
- cache_oper->release = oper->release;
- cache_oper->releasedir = cache_releasedir;
- cache_oper->removexattr = oper->removexattr;
- cache_oper->rename = oper->rename ? cache_rename : NULL;
- cache_oper->rmdir = oper->rmdir ? cache_rmdir : NULL;
- cache_oper->setxattr = oper->setxattr;
- cache_oper->statfs = oper->statfs;
- cache_oper->symlink = oper->symlink ? cache_symlink : NULL;
- cache_oper->truncate = oper->truncate ? cache_truncate : NULL;
- cache_oper->unlink = oper->unlink ? cache_unlink : NULL;
- cache_oper->utimens = oper->utimens ? cache_utimens : NULL;
- cache_oper->write = oper->write ? cache_write : NULL;
- }
- struct fuse_operations *cache_wrap(struct fuse_operations *oper) {
- static struct fuse_operations cache_oper;
- cache.next_oper = oper;
- cache_fill(oper, &cache_oper);
- pthread_mutex_init(&cache.lock, NULL);
- cache.table = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, free_node);
- if (cache.table == NULL) {
- fprintf(stderr, "failed to create cache\n");
- return NULL;
- }
- return &cache_oper;
- }
- static const struct fuse_opt cache_opts[] = {
- { "dcache_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 },
- { "dcache_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 },
- { "dcache_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 },
- { "dcache_stat_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 },
- { "dcache_dir_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 },
- { "dcache_link_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 },
- { "dcache_max_size=%u", offsetof(struct cache, max_size), 0 },
- { "dcache_clean_interval=%u", offsetof(struct cache,
- clean_interval_secs), 0 },
- { "dcache_min_clean_interval=%u", offsetof(struct cache,
- min_clean_interval_secs), 0 },
- /* For backwards compatibility */
- { "cache_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 },
- { "cache_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 },
- { "cache_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 },
- { "cache_stat_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 },
- { "cache_dir_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 },
- { "cache_link_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 },
- { "cache_max_size=%u", offsetof(struct cache, max_size), 0 },
- { "cache_clean_interval=%u", offsetof(struct cache,
- clean_interval_secs), 0 },
- { "cache_min_clean_interval=%u", offsetof(struct cache,
- min_clean_interval_secs), 0 },
- FUSE_OPT_END
- };
- int cache_parse_options(struct fuse_args *args)
- {
- cache.stat_timeout_secs = DEFAULT_CACHE_TIMEOUT_SECS;
- cache.dir_timeout_secs = DEFAULT_CACHE_TIMEOUT_SECS;
- cache.link_timeout_secs = DEFAULT_CACHE_TIMEOUT_SECS;
- cache.max_size = DEFAULT_MAX_CACHE_SIZE;
- cache.clean_interval_secs = DEFAULT_CACHE_CLEAN_INTERVAL_SECS;
- cache.min_clean_interval_secs = DEFAULT_MIN_CACHE_CLEAN_INTERVAL_SECS;
- return fuse_opt_parse(args, &cache, cache_opts, NULL);
- }
|