This commit is contained in:
Miklos Szeredi 2005-02-07 16:12:46 +00:00
parent cdc0da7956
commit 386a7f67ab
5 changed files with 304 additions and 146 deletions

View File

@ -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

View File

@ -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

244
cache.c Normal file
View File

@ -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;
}

21
cache.h Normal file
View File

@ -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);

179
sshfs.c
View File

@ -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));
}