aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--Makefile.am2
-rw-r--r--cache.c244
-rw-r--r--cache.h21
-rw-r--r--sshfs.c179
5 files changed, 304 insertions, 146 deletions
diff --git a/ChangeLog b/ChangeLog
index 9b4b4a4..7d0e25f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2005-02-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Separate attribute caching to a separate layer
+
2005-02-03 Miklos Szeredi <miklos@szeredi.hu>
Fix PKG_CONFIG_PATH setting in configure.ac (reported by Alpar
diff --git a/Makefile.am b/Makefile.am
index e8c3665..4f75792 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
## Process this file with automake to produce Makefile.in
bin_PROGRAMS = sshfs
-sshfs_SOURCES = sshfs.c
+sshfs_SOURCES = sshfs.c cache.c
diff --git a/cache.c b/cache.c
new file mode 100644
index 0000000..0ef110d
--- /dev/null
+++ b/cache.c
@@ -0,0 +1,244 @@
+/*
+ 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.
+*/
+
+#define FUSE_USE_VERSION 22
+#include "cache.h"
+#include <stdio.h>
+#include <errno.h>
+#include <glib.h>
+#include <pthread.h>
+
+#define CACHE_TIMEOUT 20
+#define MAX_CACHE_SIZE 10000
+#define CACHE_CLEAN_INTERVAL 60
+
+struct node_attr {
+ struct stat stat;
+ time_t stat_valid;
+ time_t valid;
+};
+
+struct fuse_cache_dirhandle {
+ const char *path;
+ fuse_dirh_t h;
+ fuse_dirfil_t filler;
+};
+
+static struct fuse_cache_operations *next_oper;
+static GHashTable *cache;
+static pthread_mutex_t cache_lock;
+static time_t last_cleaned;
+
+static int cache_clean_entry(void *_key, struct node_attr *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 (g_hash_table_size(cache) > MAX_CACHE_SIZE ||
+ now > last_cleaned + CACHE_CLEAN_INTERVAL) {
+ g_hash_table_foreach_remove(cache, (GHRFunc) cache_clean_entry, &now);
+ last_cleaned = now;
+ }
+}
+
+static struct node_attr *cache_lookup(const char *path)
+{
+ return (struct node_attr *) g_hash_table_lookup(cache, path);
+}
+
+static void cache_remove(const char *path)
+{
+ pthread_mutex_lock(&cache_lock);
+ g_hash_table_remove(cache, path);
+ pthread_mutex_unlock(&cache_lock);
+}
+
+static void cache_invalidate(const char *path)
+{
+ cache_remove(path);
+}
+
+static void cache_do_rename(const char *from, const char *to)
+{
+ cache_remove(from);
+ cache_remove(to);
+}
+
+static struct node_attr *cache_get(const char *path)
+{
+ struct node_attr *node = cache_lookup(path);
+ if (node == NULL) {
+ char *pathcopy = g_strdup(path);
+ node = g_new0(struct node_attr, 1);
+ g_hash_table_insert(cache, pathcopy, node);
+ }
+ return node;
+}
+
+static void cache_add_attr(const char *path, const struct stat *stbuf)
+{
+ struct node_attr *node;
+ time_t now;
+
+ pthread_mutex_lock(&cache_lock);
+ node = cache_get(path);
+ now = time(NULL);
+ node->stat = *stbuf;
+ node->stat_valid = time(NULL) + CACHE_TIMEOUT;
+ if (node->stat_valid > node->valid)
+ node->valid = node->stat_valid;
+ cache_clean();
+ pthread_mutex_unlock(&cache_lock);
+}
+
+static int cache_getattr(const char *path, struct stat *stbuf)
+{
+ struct node_attr *node;
+ int err;
+
+ pthread_mutex_lock(&cache_lock);
+ node = cache_lookup(path);
+ if (node != NULL) {
+ time_t now = time(NULL);
+ if (node->valid - now >= 0) {
+ *stbuf = node->stat;
+ pthread_mutex_unlock(&cache_lock);
+ return 0;
+ }
+ }
+ pthread_mutex_unlock(&cache_lock);
+ err = next_oper->oper.getattr(path, stbuf);
+ if (!err)
+ cache_add_attr(path, stbuf);
+
+ return err;
+}
+
+static int cache_dirfill(fuse_cache_dirh_t ch, const char *name,
+ const struct stat *stbuf)
+{
+ int err = ch->filler(ch->h, name, 0, 0);
+ if (!err) {
+ char *fullpath = g_strdup_printf("%s/%s",
+ !ch->path[1] ? "" : ch->path, name);
+ cache_add_attr(fullpath, stbuf);
+ g_free(fullpath);
+ }
+ return err;
+}
+
+static int cache_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
+{
+ struct fuse_cache_dirhandle ch;
+ ch.path = path;
+ ch.h = h;
+ ch.filler = filler;
+ return next_oper->cache_getdir(path, &ch, cache_dirfill);
+}
+
+static int cache_unlink(const char *path)
+{
+ int err = next_oper->oper.unlink(path);
+ if (!err)
+ cache_remove(path);
+ return err;
+}
+
+static int cache_rmdir(const char *path)
+{
+ int err = next_oper->oper.rmdir(path);
+ if (!err)
+ cache_remove(path);
+ return err;
+}
+
+static int cache_rename(const char *from, const char *to)
+{
+ int err = next_oper->oper.rename(from, to);
+ if (!err)
+ cache_do_rename(from, to);
+ return err;
+}
+
+static int cache_chmod(const char *path, mode_t mode)
+{
+ int err = next_oper->oper.chmod(path, mode);
+ if (!err)
+ cache_invalidate(path);
+ return err;
+}
+
+static int cache_chown(const char *path, uid_t uid, gid_t gid)
+{
+ int err = next_oper->oper.chown(path, uid, gid);
+ if (!err)
+ cache_invalidate(path);
+ return err;
+}
+
+static int cache_truncate(const char *path, off_t size)
+{
+ int err = next_oper->oper.truncate(path, size);
+ if (!err)
+ cache_invalidate(path);
+ return err;
+}
+
+static int cache_utime(const char *path, struct utimbuf *buf)
+{
+ int err = next_oper->oper.utime(path, buf);
+ if (!err)
+ cache_invalidate(path);
+ return err;
+}
+
+
+struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
+{
+ static struct fuse_operations cache_oper;
+ next_oper = oper;
+
+ cache_oper.getattr = oper->oper.getattr ? cache_getattr : NULL;
+ cache_oper.readlink = oper->oper.readlink;
+ cache_oper.getdir = oper->cache_getdir ? cache_getdir : oper->oper.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_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_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.open = oper->oper.open;
+ cache_oper.read = oper->oper.read;
+ cache_oper.write = oper->oper.write;
+ cache_oper.statfs = oper->oper.statfs;
+ cache_oper.release = oper->oper.release;
+ cache_oper.fsync = oper->oper.fsync;
+ cache_oper.setxattr = oper->oper.setxattr;
+ cache_oper.getxattr = oper->oper.getxattr;
+ cache_oper.listxattr = oper->oper.listxattr;
+ cache_oper.removexattr = oper->oper.removexattr;
+ pthread_mutex_init(&cache_lock, NULL);
+ cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+ if (cache == NULL) {
+ fprintf(stderr, "failed to create cache\n");
+ return NULL;
+ }
+ return &cache_oper;
+}
diff --git a/cache.h b/cache.h
new file mode 100644
index 0000000..1466a1b
--- /dev/null
+++ b/cache.h
@@ -0,0 +1,21 @@
+/*
+ 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 <fuse.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);
diff --git a/sshfs.c b/sshfs.c
index dc68653..48eb7f3 100644
--- a/sshfs.c
+++ b/sshfs.c
@@ -23,6 +23,8 @@
#include <netinet/in.h>
#include <glib.h>
+#include "cache.h"
+
#define SSH_FXP_INIT 1
#define SSH_FXP_VERSION 2
#define SSH_FXP_OPEN 3
@@ -79,9 +81,6 @@
#define MY_EOF 1
#define MAX_REPLY_LEN (1 << 17)
-#define CACHE_TIMEOUT 20
-#define MAX_CACHE_SIZE 10000
-#define CACHE_CLEAN_INTERVAL 60
static int infd;
static int outfd;
@@ -110,18 +109,11 @@ struct openfile {
struct buffer write_handle;
};
-struct node_attr {
- struct stat stat;
- time_t updated;
-};
-
struct sshfs_file {
struct buffer handle;
};
static GHashTable *reqtab;
-static GHashTable *cache;
-static time_t last_cleaned;
static pthread_mutex_t lock;
static int processing_thread_started;
@@ -379,75 +371,8 @@ static int buf_get_attrs(struct buffer *buf, struct stat *stbuf)
return 0;
}
-static int cache_clean_entry(void *_key, struct node_attr *node, time_t *now)
-{
- (void) _key;
- if (*now > node->updated + CACHE_TIMEOUT)
- return TRUE;
- else
- return FALSE;
-}
-
-static void cache_clean(void)
-{
- time_t now = time(NULL);
- if (g_hash_table_size(cache) > MAX_CACHE_SIZE ||
- now > last_cleaned + CACHE_CLEAN_INTERVAL) {
- g_hash_table_foreach_remove(cache, (GHRFunc) cache_clean_entry, &now);
- last_cleaned = now;
- }
-}
-
-static struct node_attr *cache_lookup(const char *path)
-{
- return (struct node_attr *) g_hash_table_lookup(cache, path);
-}
-
-static void cache_remove(const char *path)
-{
- pthread_mutex_lock(&lock);
- g_hash_table_remove(cache, path);
- pthread_mutex_unlock(&lock);
-}
-
-static void cache_invalidate(const char *path)
-{
- cache_remove(path);
-}
-
-static void cache_rename(const char *from, const char *to)
-{
- cache_remove(from);
- cache_remove(to);
-}
-
-static struct node_attr *cache_get(const char *path)
-{
- struct node_attr *node = cache_lookup(path);
- if (node == NULL) {
- char *pathcopy = g_strdup(path);
- node = g_new0(struct node_attr, 1);
- g_hash_table_insert(cache, pathcopy, node);
- }
- return node;
-}
-
-static void cache_add_attr(const char *path, const struct stat *stbuf)
-{
- struct node_attr *node;
- time_t now;
-
- pthread_mutex_lock(&lock);
- node = cache_get(path);
- now = time(NULL);
- node->stat = *stbuf;
- node->updated = time(NULL);
- cache_clean();
- pthread_mutex_unlock(&lock);
-}
-
-static int buf_get_entries(struct buffer *buf, fuse_dirh_t h,
- fuse_dirfil_t filler, const char *path)
+static int buf_get_entries(struct buffer *buf, fuse_cache_dirh_t h,
+ fuse_cache_dirfil_t filler)
{
uint32_t count;
unsigned i;
@@ -465,11 +390,7 @@ static int buf_get_entries(struct buffer *buf, fuse_dirh_t h,
if (buf_get_string(buf, &longname) != -1) {
free(longname);
if (buf_get_attrs(buf, &stbuf) != -1) {
- char *fullpath;
- filler(h, name, stbuf.st_mode >> 12, 0);
- fullpath = g_strdup_printf("%s/%s", !path[1] ? "" : path, name);
- cache_add_attr(fullpath, &stbuf);
- g_free(fullpath);
+ filler(h, name, &stbuf);
err = 0;
}
}
@@ -780,7 +701,7 @@ static int sftp_request(uint8_t type, const struct buffer *buf,
}
-static int sshfs_send_getattr(const char *path, struct stat *stbuf)
+static int sshfs_getattr(const char *path, struct stat *stbuf)
{
int err;
struct buffer buf;
@@ -794,29 +715,9 @@ static int sshfs_send_getattr(const char *path, struct stat *stbuf)
buf_free(&outbuf);
}
buf_free(&buf);
- if (!err)
- cache_add_attr(path, stbuf);
return err;
}
-static int sshfs_getattr(const char *path, struct stat *stbuf)
-{
- struct node_attr *node;
-
- pthread_mutex_lock(&lock);
- node = cache_lookup(path);
- if (node != NULL) {
- time_t now = time(NULL);
- if (now - node->updated < CACHE_TIMEOUT) {
- *stbuf = node->stat;
- pthread_mutex_unlock(&lock);
- return 0;
- }
- }
- pthread_mutex_unlock(&lock);
- return sshfs_send_getattr(path, stbuf);
-}
-
static int sshfs_readlink(const char *path, char *linkbuf, size_t size)
{
int err;
@@ -842,7 +743,8 @@ static int sshfs_readlink(const char *path, char *linkbuf, size_t size)
return err;
}
-static int sshfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
+static int sshfs_getdir(const char *path, fuse_cache_dirh_t h,
+ fuse_cache_dirfil_t filler)
{
int err;
struct buffer buf;
@@ -857,7 +759,7 @@ static int sshfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
struct buffer name;
err = sftp_request(SSH_FXP_READDIR, &handle, SSH_FXP_NAME, &name);
if (!err) {
- if (buf_get_entries(&name, h, filler, path) == -1)
+ if (buf_get_entries(&name, h, filler) == -1)
err = -EPROTO;
buf_free(&name);
}
@@ -935,8 +837,6 @@ static int sshfs_unlink(const char *path)
buf_init(&buf, 0);
buf_add_path(&buf, path);
err = sftp_request(SSH_FXP_REMOVE, &buf, SSH_FXP_STATUS, NULL);
- if (!err)
- cache_remove(path);
buf_free(&buf);
return err;
}
@@ -948,8 +848,6 @@ static int sshfs_rmdir(const char *path)
buf_init(&buf, 0);
buf_add_path(&buf, path);
err = sftp_request(SSH_FXP_RMDIR, &buf, SSH_FXP_STATUS, NULL);
- if (!err)
- cache_remove(path);
buf_free(&buf);
return err;
}
@@ -962,8 +860,6 @@ static int sshfs_rename(const char *from, const char *to)
buf_add_path(&buf, from);
buf_add_path(&buf, to);
err = sftp_request(SSH_FXP_RENAME, &buf, SSH_FXP_STATUS, NULL);
- if (!err)
- cache_rename(from, to);
buf_free(&buf);
return err;
}
@@ -977,8 +873,6 @@ static int sshfs_chmod(const char *path, mode_t mode)
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_PERMISSIONS);
buf_add_uint32(&buf, mode);
err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL);
- if (!err)
- cache_invalidate(path);
buf_free(&buf);
return err;
}
@@ -1006,8 +900,6 @@ static int sshfs_truncate(const char *path, off_t size)
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_SIZE);
buf_add_uint64(&buf, size);
err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL);
- if (!err)
- cache_invalidate(path);
buf_free(&buf);
return err;
}
@@ -1016,15 +908,12 @@ static int sshfs_utime(const char *path, struct utimbuf *ubuf)
{
int err;
struct buffer buf;
- cache_remove(path);
buf_init(&buf, 0);
buf_add_path(&buf, path);
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_ACMODTIME);
buf_add_uint32(&buf, ubuf->actime);
buf_add_uint32(&buf, ubuf->modtime);
err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL);
- if (!err)
- cache_invalidate(path);
buf_free(&buf);
return err;
}
@@ -1110,6 +999,7 @@ static int sshfs_write(const char *path, const char *wbuf, size_t size,
struct buffer data;
struct sshfs_file *sf = (struct sshfs_file *) fi->fh;
struct buffer *handle = &sf->handle;
+ (void) path;
data.p = (uint8_t *) wbuf;
data.len = size;
buf_init(&buf, 0);
@@ -1117,8 +1007,6 @@ static int sshfs_write(const char *path, const char *wbuf, size_t size,
buf_add_uint64(&buf, offset);
buf_add_data(&buf, &data);
err = sftp_request(SSH_FXP_WRITE, &buf, SSH_FXP_STATUS, NULL);
- if (!err)
- cache_invalidate(path);
buf_free(&buf);
return err ? err : (int) size;
}
@@ -1157,32 +1045,33 @@ static int processing_init(void)
{
pthread_mutex_init(&lock, NULL);
reqtab = g_hash_table_new(NULL, NULL);
- cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
- if (!reqtab || !cache) {
- fprintf(stderr, "failed to create hash tables\n");
+ if (!reqtab) {
+ fprintf(stderr, "failed to create hash table\n");
return -1;
}
return 0;
}
-static struct fuse_operations sshfs_oper = {
- .getattr = sshfs_getattr,
- .readlink = sshfs_readlink,
- .getdir = sshfs_getdir,
- .mknod = sshfs_mknod,
- .mkdir = sshfs_mkdir,
- .symlink = sshfs_symlink,
- .unlink = sshfs_unlink,
- .rmdir = sshfs_rmdir,
- .rename = sshfs_rename,
- .chmod = sshfs_chmod,
- .chown = sshfs_chown,
- .truncate = sshfs_truncate,
- .utime = sshfs_utime,
- .open = sshfs_open,
- .release = sshfs_release,
- .read = sshfs_read,
- .write = sshfs_write,
+static struct fuse_cache_operations sshfs_oper = {
+ .oper = {
+ .getattr = sshfs_getattr,
+ .readlink = sshfs_readlink,
+ .mknod = sshfs_mknod,
+ .mkdir = sshfs_mkdir,
+ .symlink = sshfs_symlink,
+ .unlink = sshfs_unlink,
+ .rmdir = sshfs_rmdir,
+ .rename = sshfs_rename,
+ .chmod = sshfs_chmod,
+ .chown = sshfs_chown,
+ .truncate = sshfs_truncate,
+ .utime = sshfs_utime,
+ .open = sshfs_open,
+ .release = sshfs_release,
+ .read = sshfs_read,
+ .write = sshfs_write,
+ },
+ .cache_getdir = sshfs_getdir,
};
static void usage(const char *progname)
@@ -1196,7 +1085,7 @@ static void usage(const char *progname)
" -p port remote port\n"
" -c port directly connect to port bypassing ssh\n"
"\n", progname);
- fuse_main(2, (char **) fusehelp, &sshfs_oper);
+ fuse_main(2, (char **) fusehelp, NULL);
exit(1);
}
@@ -1278,5 +1167,5 @@ int main(int argc, char *argv[])
newargv[newargc++] = g_strdup_printf("-ofsname=sshfs#%s", fsname);
g_free(fsname);
newargv[newargc] = NULL;
- return fuse_main(newargc, newargv, &sshfs_oper);
+ return fuse_main(newargc, newargv, cache_init(&sshfs_oper));
}