Re-enabled cache support.

This commit is contained in:
Nikolaus Rath 2017-06-21 10:59:13 -07:00
parent 493081e8f6
commit c711bf738f
4 changed files with 161 additions and 148 deletions

263
cache.c
View File

@ -8,6 +8,7 @@
#include "cache.h"
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
@ -27,7 +28,7 @@ struct cache {
unsigned int max_size;
unsigned int clean_interval_secs;
unsigned int min_clean_interval_secs;
struct fuse_cache_operations *next_oper;
struct fuse_operations *next_oper;
GHashTable *table;
pthread_mutex_t lock;
time_t last_cleaned;
@ -46,14 +47,22 @@ struct node {
time_t valid;
};
struct fuse_cache_dirhandle {
struct readdir_handle {
const char *path;
fuse_dirh_t h;
fuse_dirfil_t filler;
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_;
@ -108,15 +117,12 @@ static void cache_purge_parent(const char *path)
void cache_invalidate(const char *path)
{
if (!cache.on)
return;
pthread_mutex_lock(&cache.lock);
cache_purge(path);
pthread_mutex_unlock(&cache.lock);
}
void cache_invalidate_write(const char *path)
static void cache_invalidate_write(const char *path)
{
pthread_mutex_lock(&cache.lock);
cache_purge(path);
@ -168,9 +174,6 @@ void cache_add_attr(const char *path, const struct stat *stbuf, uint64_t wrctr)
{
struct node *node;
if (!cache.on)
return;
pthread_mutex_lock(&cache.lock);
if (wrctr == cache.write_ctr) {
node = cache_get(path);
@ -253,7 +256,7 @@ static int cache_getattr(const char *path, struct stat *stbuf)
int err = cache_get_attr(path, stbuf);
if (err) {
uint64_t wrctr = cache_get_write_ctr();
err = cache.next_oper->oper.getattr(path, stbuf);
err = cache.next_oper->getattr(path, stbuf);
if (!err)
cache_add_attr(path, stbuf, wrctr);
}
@ -277,17 +280,52 @@ static int cache_readlink(const char *path, char *buf, size_t size)
}
}
pthread_mutex_unlock(&cache.lock);
err = cache.next_oper->oper.readlink(path, buf, size);
err = cache.next_oper->readlink(path, buf, size);
if (!err)
cache_add_link(path, buf, size);
return err;
}
static int cache_dirfill(fuse_cache_dirh_t ch, const char *name,
const struct stat *stbuf)
static int cache_opendir(const char *path, struct fuse_file_info *fi)
{
int err = ch->filler(ch->h, name, 0, 0);
(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;
}
static int cache_releasedir(const char *path, struct fuse_file_info *fi)
{
int err;
struct file_handle *cfi;
cfi = (struct file_handle*) fi->fh;
if(cfi->is_open) {
fi->fh = cfi->fs_fh;
err = cache.next_oper->releasedir(path, fi);
} else
err = 0;
free(cfi);
return err;
}
static int cache_dirfill (void *buf, const char *name,
const struct stat *stbuf, off_t off)
{
int err;
struct readdir_handle *ch;
ch = (struct readdir_handle*) buf;
err = ch->filler(ch->buf, name, stbuf, off);
if (!err) {
g_ptr_array_add(ch->dir, g_strdup(name));
if (stbuf->st_mode & S_IFMT) {
@ -302,61 +340,65 @@ static int cache_dirfill(fuse_cache_dirh_t ch, const char *name,
return err;
}
static int cache_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
static int cache_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi)
{
struct fuse_cache_dirhandle ch;
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++)
filler(h, *dir, 0, 0);
// FIXME: What about st_mode?
filler(buf, *dir, 0, 0);
pthread_mutex_unlock(&cache.lock);
return 0;
}
}
pthread_mutex_unlock(&cache.lock);
cfi = (struct file_handle*) 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.h = h;
ch.buf = buf;
ch.filler = filler;
ch.dir = g_ptr_array_new();
ch.wrctr = cache_get_write_ctr();
err = cache.next_oper->cache_getdir(path, &ch, cache_dirfill);
err = cache.next_oper->readdir(path, &ch, cache_dirfill, offset, fi);
g_ptr_array_add(ch.dir, NULL);
dir = (char **) ch.dir->pdata;
if (!err)
if (!err) {
cache_add_dir(path, dir);
else
} else {
g_strfreev(dir);
}
g_ptr_array_free(ch.dir, FALSE);
return err;
}
static int cache_unity_dirfill(fuse_cache_dirh_t ch, const char *name,
const struct stat *stbuf)
{
(void) stbuf;
return ch->filler(ch->h, name, 0, 0);
}
static int cache_unity_getdir(const char *path, fuse_dirh_t h,
fuse_dirfil_t filler)
{
struct fuse_cache_dirhandle ch;
ch.h = h;
ch.filler = filler;
return cache.next_oper->cache_getdir(path, &ch, cache_unity_dirfill);
}
static int cache_mknod(const char *path, mode_t mode, dev_t rdev)
{
int err = cache.next_oper->oper.mknod(path, mode, rdev);
int err = cache.next_oper->mknod(path, mode, rdev);
if (!err)
cache_invalidate_dir(path);
return err;
@ -364,7 +406,7 @@ static int cache_mknod(const char *path, mode_t mode, dev_t rdev)
static int cache_mkdir(const char *path, mode_t mode)
{
int err = cache.next_oper->oper.mkdir(path, mode);
int err = cache.next_oper->mkdir(path, mode);
if (!err)
cache_invalidate_dir(path);
return err;
@ -372,7 +414,7 @@ static int cache_mkdir(const char *path, mode_t mode)
static int cache_unlink(const char *path)
{
int err = cache.next_oper->oper.unlink(path);
int err = cache.next_oper->unlink(path);
if (!err)
cache_invalidate_dir(path);
return err;
@ -380,7 +422,7 @@ static int cache_unlink(const char *path)
static int cache_rmdir(const char *path)
{
int err = cache.next_oper->oper.rmdir(path);
int err = cache.next_oper->rmdir(path);
if (!err)
cache_invalidate_dir(path);
return err;
@ -388,7 +430,7 @@ static int cache_rmdir(const char *path)
static int cache_symlink(const char *from, const char *to)
{
int err = cache.next_oper->oper.symlink(from, to);
int err = cache.next_oper->symlink(from, to);
if (!err)
cache_invalidate_dir(to);
return err;
@ -396,7 +438,7 @@ static int cache_symlink(const char *from, const char *to)
static int cache_rename(const char *from, const char *to)
{
int err = cache.next_oper->oper.rename(from, to);
int err = cache.next_oper->rename(from, to);
if (!err)
cache_do_rename(from, to);
return err;
@ -404,7 +446,7 @@ static int cache_rename(const char *from, const char *to)
static int cache_link(const char *from, const char *to)
{
int err = cache.next_oper->oper.link(from, to);
int err = cache.next_oper->link(from, to);
if (!err) {
cache_invalidate(from);
cache_invalidate_dir(to);
@ -414,7 +456,7 @@ static int cache_link(const char *from, const char *to)
static int cache_chmod(const char *path, mode_t mode)
{
int err = cache.next_oper->oper.chmod(path, mode);
int err = cache.next_oper->chmod(path, mode);
if (!err)
cache_invalidate(path);
return err;
@ -422,7 +464,7 @@ static int cache_chmod(const char *path, mode_t mode)
static int cache_chown(const char *path, uid_t uid, gid_t gid)
{
int err = cache.next_oper->oper.chown(path, uid, gid);
int err = cache.next_oper->chown(path, uid, gid);
if (!err)
cache_invalidate(path);
return err;
@ -430,7 +472,7 @@ static int cache_chown(const char *path, uid_t uid, gid_t gid)
static int cache_truncate(const char *path, off_t size)
{
int err = cache.next_oper->oper.truncate(path, size);
int err = cache.next_oper->truncate(path, size);
if (!err)
cache_invalidate(path);
return err;
@ -438,7 +480,7 @@ static int cache_truncate(const char *path, off_t size)
static int cache_utime(const char *path, struct utimbuf *buf)
{
int err = cache.next_oper->oper.utime(path, buf);
int err = cache.next_oper->utime(path, buf);
if (!err)
cache_invalidate(path);
return err;
@ -447,7 +489,7 @@ static int cache_utime(const char *path, struct utimbuf *buf)
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->oper.write(path, buf, size, offset, fi);
int res = cache.next_oper->write(path, buf, size, offset, fi);
if (res >= 0)
cache_invalidate_write(path);
return res;
@ -456,7 +498,7 @@ static int cache_write(const char *path, const char *buf, size_t size,
static int cache_create(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
int err = cache.next_oper->oper.create(path, mode, fi);
int err = cache.next_oper->create(path, mode, fi);
if (!err)
cache_invalidate_dir(path);
return err;
@ -465,7 +507,7 @@ static int cache_create(const char *path, mode_t mode,
static int cache_ftruncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
int err = cache.next_oper->oper.ftruncate(path, size, fi);
int err = cache.next_oper->ftruncate(path, size, fi);
if (!err)
cache_invalidate(path);
return err;
@ -477,97 +519,69 @@ static int cache_fgetattr(const char *path, struct stat *stbuf,
int err = cache_get_attr(path, stbuf);
if (err) {
uint64_t wrctr = cache_get_write_ctr();
err = cache.next_oper->oper.fgetattr(path, stbuf, fi);
err = cache.next_oper->fgetattr(path, stbuf, fi);
if (!err)
cache_add_attr(path, stbuf, wrctr);
}
return err;
}
static void cache_unity_fill(struct fuse_cache_operations *oper,
struct fuse_operations *cache_oper)
{
cache_oper->init = oper->oper.init;
cache_oper->getattr = oper->oper.getattr;
cache_oper->access = oper->oper.access;
cache_oper->readlink = oper->oper.readlink;
cache_oper->getdir = cache_unity_getdir;
cache_oper->mknod = oper->oper.mknod;
cache_oper->mkdir = oper->oper.mkdir;
cache_oper->symlink = oper->oper.symlink;
cache_oper->unlink = oper->oper.unlink;
cache_oper->rmdir = oper->oper.rmdir;
cache_oper->rename = oper->oper.rename;
cache_oper->link = oper->oper.link;
cache_oper->chmod = oper->oper.chmod;
cache_oper->chown = oper->oper.chown;
cache_oper->truncate = oper->oper.truncate;
cache_oper->utime = oper->oper.utime;
cache_oper->open = oper->oper.open;
cache_oper->read = oper->oper.read;
cache_oper->write = oper->oper.write;
cache_oper->flush = oper->oper.flush;
cache_oper->release = oper->oper.release;
cache_oper->fsync = oper->oper.fsync;
cache_oper->statfs = oper->oper.statfs;
cache_oper->setxattr = oper->oper.setxattr;
cache_oper->getxattr = oper->oper.getxattr;
cache_oper->listxattr = oper->oper.listxattr;
cache_oper->removexattr = oper->oper.removexattr;
cache_oper->create = oper->oper.create;
cache_oper->ftruncate = oper->oper.ftruncate;
cache_oper->fgetattr = oper->oper.fgetattr;
cache_oper->flag_nullpath_ok = oper->oper.flag_nullpath_ok;
cache_oper->flag_nopath = oper->oper.flag_nopath;
}
static void cache_fill(struct fuse_cache_operations *oper,
static void cache_fill(struct fuse_operations *oper,
struct fuse_operations *cache_oper)
{
cache_oper->getattr = oper->oper.getattr ? cache_getattr : NULL;
cache_oper->readlink = oper->oper.readlink ? cache_readlink : NULL;
cache_oper->getdir = oper->cache_getdir ? cache_getdir : NULL;
cache_oper->mknod = oper->oper.mknod ? cache_mknod : NULL;
cache_oper->mkdir = oper->oper.mkdir ? cache_mkdir : NULL;
cache_oper->symlink = oper->oper.symlink ? cache_symlink : NULL;
cache_oper->unlink = oper->oper.unlink ? cache_unlink : NULL;
cache_oper->rmdir = oper->oper.rmdir ? cache_rmdir : NULL;
cache_oper->rename = oper->oper.rename ? cache_rename : NULL;
cache_oper->link = oper->oper.link ? cache_link : NULL;
cache_oper->chmod = oper->oper.chmod ? cache_chmod : NULL;
cache_oper->chown = oper->oper.chown ? cache_chown : NULL;
cache_oper->truncate = oper->oper.truncate ? cache_truncate : NULL;
cache_oper->utime = oper->oper.utime ? cache_utime : NULL;
cache_oper->write = oper->oper.write ? cache_write : NULL;
cache_oper->create = oper->oper.create ? cache_create : NULL;
cache_oper->ftruncate = oper->oper.ftruncate ? cache_ftruncate : NULL;
cache_oper->fgetattr = oper->oper.fgetattr ? cache_fgetattr : NULL;
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->fgetattr = oper->fgetattr ? cache_fgetattr : NULL;
cache_oper->flush = oper->flush;
cache_oper->fsync = oper->fsync;
cache_oper->ftruncate = oper->ftruncate ? cache_ftruncate : NULL;
cache_oper->getattr = oper->getattr ? cache_getattr : NULL;
cache_oper->getxattr = oper->getxattr;
cache_oper->init = oper->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->utime = oper->utime ? cache_utime : NULL;
cache_oper->write = oper->write ? cache_write : NULL;
cache_oper->flag_nopath = 0;
cache_oper->flag_nullpath_ok = 0;
cache_oper->flag_nopath = 0;
}
struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
struct fuse_operations *cache_init(struct fuse_operations *oper)
{
static struct fuse_operations cache_oper;
cache.next_oper = oper;
cache_unity_fill(oper, &cache_oper);
if (cache.on) {
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;
}
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[] = {
{ "cache=yes", offsetof(struct cache, on), 1 },
{ "cache=no", offsetof(struct cache, on), 0 },
{ "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 },
@ -590,7 +604,6 @@ int cache_parse_options(struct fuse_args *args)
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;
cache.on = 1;
return fuse_opt_parse(args, &cache, cache_opts, NULL);
}

11
cache.h
View File

@ -9,16 +9,7 @@
#include <fuse.h>
#include <fuse_opt.h>
typedef struct fuse_cache_dirhandle *fuse_cache_dirh_t;
typedef int (*fuse_cache_dirfil_t) (fuse_cache_dirh_t h, const char *name,
const struct stat *stbuf);
struct fuse_cache_operations {
struct fuse_operations oper;
int (*cache_getdir) (const char *, fuse_cache_dirh_t, fuse_cache_dirfil_t);
};
struct fuse_operations *cache_init(struct fuse_cache_operations *oper);
struct fuse_operations *cache_init(struct fuse_operations *oper);
int cache_parse_options(struct fuse_args *args);
void cache_add_attr(const char *path, const struct stat *stbuf, uint64_t wrctr);
void cache_invalidate(const char *path);

32
sshfs.c
View File

@ -224,6 +224,7 @@ struct sshfs {
int idmap;
int nomap;
int disable_hardlink;
int cache;
char *uid_file;
char *gid_file;
GHashTable *uid_map;
@ -403,6 +404,8 @@ static struct fuse_opt sshfs_opts[] = {
SSHFS_OPT("delay_connect", delay_connect, 1),
SSHFS_OPT("slave", slave, 1),
SSHFS_OPT("disable_hardlink", disable_hardlink, 1),
SSHFS_OPT("cache=yes", cache, 1),
SSHFS_OPT("cache=no", cache, 0),
FUSE_OPT_KEY("-p ", KEY_PORT),
FUSE_OPT_KEY("-C", KEY_COMPRESS),
@ -2447,7 +2450,10 @@ static int sshfs_open_common(const char *path, mode_t mode,
uint32_t pflags = 0;
struct iovec iov;
uint8_t type;
uint64_t wrctr = cache_get_write_ctr();
uint64_t wrctr = 0;
if (sshfs.cache)
wrctr = cache_get_write_ctr();
if ((fi->flags & O_ACCMODE) == O_RDONLY)
pflags = SSH_FXF_READ;
@ -2504,11 +2510,13 @@ static int sshfs_open_common(const char *path, mode_t mode,
}
if (!err) {
cache_add_attr(path, &stbuf, wrctr);
if (sshfs.cache)
cache_add_attr(path, &stbuf, wrctr);
buf_finish(&sf->handle);
fi->fh = (unsigned long) sf;
} else {
cache_invalidate(path);
if (sshfs.cache)
cache_invalidate(path);
g_free(sf);
}
buf_free(&buf);
@ -3234,8 +3242,7 @@ static int processing_init(void)
return 0;
}
static struct fuse_cache_operations sshfs_oper = {
.oper = {
static struct fuse_operations sshfs_oper = {
.init = sshfs_init,
.getattr = sshfs_getattr,
.access = sshfs_access,
@ -3266,8 +3273,6 @@ static struct fuse_cache_operations sshfs_oper = {
.fgetattr = sshfs_fgetattr,
.flag_nullpath_ok = 1,
.flag_nopath = 1,
},
// .cache_readdir = sshfs_readdir,
};
static void usage(const char *progname)
@ -3812,6 +3817,7 @@ int main(int argc, char *argv[])
sshfs.rfd = -1;
sshfs.wfd = -1;
sshfs.ptyfd = -1;
sshfs.cache = 1;
sshfs.ptyslavefd = -1;
sshfs.delay_connect = 0;
sshfs.slave = 0;
@ -3837,8 +3843,8 @@ int main(int argc, char *argv[])
// These workarounds require the "path" argument.
if (sshfs.truncate_workaround || sshfs.fstat_workaround) {
sshfs_oper.oper.flag_nullpath_ok = 0;
sshfs_oper.oper.flag_nopath = 0;
sshfs_oper.flag_nullpath_ok = 0;
sshfs_oper.flag_nopath = 0;
}
if (sshfs.idmap == IDMAP_USER)
@ -3977,9 +3983,11 @@ int main(int argc, char *argv[])
perror("WARNING: failed to set FD_CLOEXEC on fuse device");
#endif
sshfs.op = cache_init(&sshfs_oper);
// FIXME: Bypass cache for now
fuse = fuse_new(ch, &args, &sshfs_oper.oper,
if(sshfs.cache)
sshfs.op = cache_init(&sshfs_oper);
else
sshfs.op = &sshfs_oper;
fuse = fuse_new(ch, &args, sshfs.op,
sizeof(struct fuse_operations), NULL);
if (fuse == NULL) {
fuse_unmount(mountpoint, ch);

View File

@ -66,7 +66,8 @@ def test_sshfs(tmpdir, debug, cache_timeout, sync_rd, capfd):
if cache_timeout == 0:
cmdline += [ '-o', 'cache=no' ]
else:
cmdline += [ '-o', 'cache_timeout=%d' % cache_timeout ]
cmdline += [ '-o', 'cache_timeout=%d' % cache_timeout,
'-o', 'cache=yes' ]
# FUSE Cache
cmdline += [ '-o', 'entry_timeout=0',