From 3a7c00ec0c156123c47b53ec1cd7ead001fa4dfb Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Sat, 3 Feb 2007 23:32:47 +0000 Subject: [PATCH] Add filesystem stacking support to high level API --- ChangeLog | 10 + configure.in | 5 +- example/Makefile.am | 4 +- include/fuse.h | 87 ++ include/fuse_common.h | 2 +- kernel/configure.ac | 2 +- lib/Makefile.am | 6 +- lib/fuse.c | 2101 +++++++++++++++++++++++----------------- lib/fuse_lowlevel.c | 14 +- lib/fuse_misc.h | 24 +- lib/fuse_versionscript | 46 +- lib/modules/iconv.c | 701 ++++++++++++++ lib/modules/subdir.c | 655 +++++++++++++ 13 files changed, 2734 insertions(+), 923 deletions(-) create mode 100644 lib/modules/iconv.c create mode 100644 lib/modules/subdir.c diff --git a/ChangeLog b/ChangeLog index db6f731..80398c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2007-02-03 Miklos Szeredi + + * Add filesystem stacking support to high level API. Filesystem + modules can be built into libfuse or loaded from shared object + (.so) files + + * Add 'subdir' and 'iconv' built in modules + + * lib/fuse.c: Fix locking for the reply code in create and open + 2007-02-02 Miklos Szeredi * kernel: make it compile on "strange" kernels which have emulated diff --git a/configure.in b/configure.in index a4a465b..3020f39 100644 --- a/configure.in +++ b/configure.in @@ -1,4 +1,4 @@ -AC_INIT(fuse, 2.6.1) +AC_INIT(fuse, 2.7.0-pre0) AC_CANONICAL_TARGET AM_INIT_AUTOMAKE AM_CONFIG_HEADER(include/config.h) @@ -62,8 +62,9 @@ AC_CHECK_FUNCS([fork setxattr fdatasync]) AC_CHECK_MEMBERS([struct stat.st_atim]) AC_CHECK_MEMBERS([struct stat.st_atimespec]) -libfuse_libs=-pthread +libfuse_libs="-pthread" LIBS= +AC_SEARCH_LIBS(dlopen, [dl]) AC_SEARCH_LIBS(clock_gettime, [rt]) libfuse_libs="$libfuse_libs $LIBS" LIBS= diff --git a/example/Makefile.am b/example/Makefile.am index 469dcb4..91f33cc 100644 --- a/example/Makefile.am +++ b/example/Makefile.am @@ -3,5 +3,5 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -D_FILE_OFFSET_BITS=64 -D_REENTRANT noinst_PROGRAMS = fusexmp fusexmp_fh null hello hello_ll -LDADD = ../lib/libfuse.la -lpthread -fusexmp_fh_LDADD = ../lib/libfuse.la ../lib/libulockmgr.la -lpthread +LDADD = ../lib/libfuse.la @libfuse_libs@ +fusexmp_fh_LDADD = ../lib/libfuse.la ../lib/libulockmgr.la @libfuse_libs@ diff --git a/include/fuse.h b/include/fuse.h index 83b6f21..f89ee13 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -572,6 +572,93 @@ int fuse_is_lib_option(const char *opt); int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, void *user_data); +/* + * + */ + +struct fuse_fs; +int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf); +int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf, + struct fuse_file_info *fi); +int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, + const char *newpath); +int fuse_fs_unlink(struct fuse_fs *fs, const char *path); +int fuse_fs_rmdir(struct fuse_fs *fs, const char *path); +int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, + const char *path); +int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath); +int fuse_fs_release(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); +int fuse_fs_open(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); +int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size, + off_t off, struct fuse_file_info *fi); +int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf, + size_t size, off_t off, struct fuse_file_info *fi); +int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync, + struct fuse_file_info *fi); +int fuse_fs_flush(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); +int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf); +int fuse_fs_opendir(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); +int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf, + fuse_fill_dir_t filler, off_t off, + struct fuse_file_info *fi); +int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync, + struct fuse_file_info *fi); +int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); +int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode, + struct fuse_file_info *fi); +int fuse_fs_lock(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi, int cmd, struct flock *lock); +int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode); +int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid); +int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size); +int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, off_t size, + struct fuse_file_info *fi); +int fuse_fs_utimens(struct fuse_fs *fs, const char *path, + const struct timespec tv[2]); +int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask); +int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf, + size_t len); +int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode, + dev_t rdev); +int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode); +int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name, + const char *value, size_t size, int flags); +int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name, + char *value, size_t size); +int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list, + size_t size); +int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, + const char *name); +int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize, + uint64_t *idx); +void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn); +void fuse_fs_destroy(struct fuse_fs *fs); + +struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, + void *user_data); + +struct fuse_module { + const char *name; + struct fuse_fs *(*factory)(struct fuse_args *, struct fuse_fs *[]); + struct fuse_module *next; + struct fusemod_so *so; + int ctr; +}; + +void fuse_register_module(struct fuse_module *mod); +#define FUSE_REGISTER_MODULE(name_, factory_) \ +static __attribute__((constructor)) void name_ ## _register(void) \ +{ \ + static struct fuse_module mod = { .name = #name_, .factory = factory_ }; \ + fuse_register_module(&mod); \ +} + + /* ----------------------------------------------------------- * * Advanced API for event handling, don't worry about this... * * ----------------------------------------------------------- */ diff --git a/include/fuse_common.h b/include/fuse_common.h index 8dc7bc2..1432917 100644 --- a/include/fuse_common.h +++ b/include/fuse_common.h @@ -20,7 +20,7 @@ #define FUSE_MAJOR_VERSION 2 /** Minor version of FUSE library interface */ -#define FUSE_MINOR_VERSION 6 +#define FUSE_MINOR_VERSION 7 #define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) #define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) diff --git a/kernel/configure.ac b/kernel/configure.ac index d54b711..eb8a4fa 100644 --- a/kernel/configure.ac +++ b/kernel/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(fuse-kernel, 2.6.1) +AC_INIT(fuse-kernel, 2.7.0-pre0) AC_CONFIG_HEADERS([config.h]) AC_PROG_INSTALL diff --git a/lib/Makefile.am b/lib/Makefile.am index 15f5ae6..983c01f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -24,12 +24,14 @@ libfuse_la_SOURCES = \ fuse_session.c \ fuse_signals.c \ helper.c \ + modules/subdir.c \ + modules/iconv.c \ $(mount_source) -libfuse_la_LDFLAGS = $(libfuse_libs) -version-number 2:6:1 \ +libfuse_la_LDFLAGS = @libfuse_libs@ -version-number 2:7:0 \ -Wl,--version-script,$(srcdir)/fuse_versionscript libulockmgr_la_SOURCES = ulockmgr.c -libulockmgr_la_LDFLAGS = -version-number 1:0:0 +libulockmgr_la_LDFLAGS = -version-number 1:0:1 EXTRA_DIST = fuse_versionscript diff --git a/lib/fuse.c b/lib/fuse.c index 994a31f..bc944c7 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -14,6 +14,8 @@ #include "fuse_lowlevel.h" #include "fuse_opt.h" #include "fuse_misc.h" +#include "fuse_common_compat.h" +#include "fuse_compat.h" #include #include @@ -25,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -57,12 +60,24 @@ struct fuse_config { int auto_cache; int intr; int intr_signal; + int help; + char *modules; }; -struct fuse { - struct fuse_session *se; +struct fuse_fs { struct fuse_operations op; + struct fuse_module *m; + void *user_data; int compat; +}; + +struct fusemod_so { + void *handle; + int ctr; +}; + +struct fuse { + struct fuse_session *se; struct node **name_table; size_t name_table_size; struct node **id_table; @@ -72,9 +87,9 @@ struct fuse { unsigned int hidectr; pthread_mutex_t lock; pthread_rwlock_t tree_lock; - void *user_data; struct fuse_config conf; int intr_installed; + struct fuse_fs *fs; }; struct lock { @@ -104,7 +119,7 @@ struct node { struct lock *locks; }; -struct fuse_dirhandle { +struct fuse_dh { pthread_mutex_t lock; struct fuse *fuse; fuse_req_t req; @@ -119,6 +134,12 @@ struct fuse_dirhandle { fuse_ino_t nodeid; }; +/* old dir handle */ +struct fuse_dirhandle { + fuse_fill_dir_t filler; + void *buf; +}; + struct fuse_context_i { struct fuse_context ctx; fuse_req_t req; @@ -127,14 +148,107 @@ struct fuse_context_i { static pthread_key_t fuse_context_key; static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER; static int fuse_context_ref; +static struct fusemod_so *fuse_current_so; +static struct fuse_module *fuse_modules; + +static int fuse_load_so_name(const char *soname) +{ + struct fusemod_so *so; + + so = calloc(1, sizeof(struct fusemod_so)); + if (!so) { + fprintf(stderr, "fuse: memory allocation failed\n"); + return -1; + } + + pthread_mutex_lock(&fuse_context_lock); + fuse_current_so = so; + so->handle = dlopen(soname, RTLD_NOW); + fuse_current_so = NULL; + pthread_mutex_unlock(&fuse_context_lock); + if (!so->handle) { + fprintf(stderr, "fuse: %s\n", dlerror()); + goto err; + } + if (!so->ctr) { + fprintf(stderr, "fuse: %s did not register any modules", soname); + goto err; + } + return 0; + + err: + if (so->handle) + dlclose(so->handle); + free(so); + return -1; +} + +static int fuse_load_so_module(const char *module) +{ + int res; + char *soname = malloc(strlen(module) + 64); + if (!soname) { + fprintf(stderr, "fuse: memory allocation failed\n"); + return -1; + } + if (soname) + sprintf(soname, "libfusemod_%s.so", module); + + res = fuse_load_so_name(soname); + free(soname); + return res; +} + +static struct fuse_module *fuse_find_module(const char *module) +{ + struct fuse_module *m; + for (m = fuse_modules; m; m = m->next) { + if (strcmp(module, m->name) == 0) { + m->ctr++; + break; + } + } + return m; +} + +static struct fuse_module *fuse_get_module(const char *module) +{ + struct fuse_module *m; + + pthread_mutex_lock(&fuse_context_lock); + m = fuse_find_module(module); + if (!m) { + int err = fuse_load_so_module(module); + if (!err) + m = fuse_find_module(module); + } + pthread_mutex_unlock(&fuse_context_lock); + return m; +} -static int fuse_compat_open(struct fuse *, fuse_req_t, char *, - struct fuse_file_info *); -static void fuse_compat_release(struct fuse *, fuse_req_t, char *, - struct fuse_file_info *); -static int fuse_compat_opendir(struct fuse *, fuse_req_t, char *, - struct fuse_file_info *); -static int fuse_compat_statfs(struct fuse *, fuse_req_t, struct statvfs *); +static void fuse_put_module(struct fuse_module *m) +{ + pthread_mutex_lock(&fuse_context_lock); + assert(m->ctr > 0); + m->ctr--; + if (!m->ctr && m->so) { + struct fusemod_so *so = m->so; + assert(so->ctr > 0); + so->ctr--; + if (!so->ctr) { + struct fuse_module **mp; + for (mp = &fuse_modules; *mp;) { + if ((*mp)->so == so) + *mp = (*mp)->next; + else + mp = &(*mp)->next; + } + dlclose(so->handle); + free(so); + } + } + pthread_mutex_unlock(&fuse_context_lock); +} static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid) { @@ -184,7 +298,8 @@ static void hash_id(struct fuse *f, struct node *node) f->id_table[hash] = node; } -static unsigned int name_hash(struct fuse *f, fuse_ino_t parent, const char *name) +static unsigned int name_hash(struct fuse *f, fuse_ino_t parent, + const char *name) { unsigned int hash = *name; @@ -510,231 +625,559 @@ static inline void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req, fuse_do_prepare_interrupt(req, d); } -static int fuse_do_getattr(struct fuse *f, fuse_req_t req, const char *path, - struct stat *buf) +#ifndef __FreeBSD__ + +static int fuse_compat_open(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) { - int res; - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - res = f->op.getattr(path, buf); - fuse_finish_interrupt(f, req, &d); - return res; + int err; + if (!fs->compat || fs->compat >= 25) + err = fs->op.open(path, fi); + else if (fs->compat == 22) { + struct fuse_file_info_compat tmp; + memcpy(&tmp, fi, sizeof(tmp)); + err = ((struct fuse_operations_compat22 *) &fs->op)->open(path, &tmp); + memcpy(fi, &tmp, sizeof(tmp)); + fi->fh = tmp.fh; + } else + err = ((struct fuse_operations_compat2 *) &fs->op) + ->open(path, fi->flags); + return err; } -static int fuse_do_fgetattr(struct fuse *f, fuse_req_t req, const char *path, - struct stat *buf, struct fuse_file_info *fi) +static int fuse_compat_release(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) { - int res; - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - res = f->op.fgetattr(path, buf, fi); - fuse_finish_interrupt(f, req, &d); - return res; + if (!fs->compat || fs->compat >= 22) + return fs->op.release(path, fi); + else + return ((struct fuse_operations_compat2 *) &fs->op) + ->release(path, fi->flags); } -static int fuse_do_rename(struct fuse *f, fuse_req_t req, const char *oldpath, - const char *newpath) +static int fuse_compat_opendir(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) { - int res; - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - res = f->op.rename(oldpath, newpath); - fuse_finish_interrupt(f, req, &d); - return res; + if (!fs->compat || fs->compat >= 25) + return fs->op.opendir(path, fi); + else { + int err; + struct fuse_file_info_compat tmp; + memcpy(&tmp, fi, sizeof(tmp)); + err = ((struct fuse_operations_compat22 *) &fs->op) + ->opendir(path, &tmp); + memcpy(fi, &tmp, sizeof(tmp)); + fi->fh = tmp.fh; + return err; + } } -static int fuse_do_unlink(struct fuse *f, fuse_req_t req, const char *path) +static void convert_statfs_compat(struct fuse_statfs_compat1 *compatbuf, + struct statvfs *stbuf) { - int res; - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - res = f->op.unlink(path); - fuse_finish_interrupt(f, req, &d); - return res; + stbuf->f_bsize = compatbuf->block_size; + stbuf->f_blocks = compatbuf->blocks; + stbuf->f_bfree = compatbuf->blocks_free; + stbuf->f_bavail = compatbuf->blocks_free; + stbuf->f_files = compatbuf->files; + stbuf->f_ffree = compatbuf->files_free; + stbuf->f_namemax = compatbuf->namelen; } -static void fuse_do_release(struct fuse *f, fuse_req_t req, const char *path, - struct fuse_file_info *fi) +static void convert_statfs_old(struct statfs *oldbuf, struct statvfs *stbuf) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - f->op.release(path, fi); - fuse_finish_interrupt(f, req, &d); + stbuf->f_bsize = oldbuf->f_bsize; + stbuf->f_blocks = oldbuf->f_blocks; + stbuf->f_bfree = oldbuf->f_bfree; + stbuf->f_bavail = oldbuf->f_bavail; + stbuf->f_files = oldbuf->f_files; + stbuf->f_ffree = oldbuf->f_ffree; + stbuf->f_namemax = oldbuf->f_namelen; } -static int fuse_do_opendir(struct fuse *f, fuse_req_t req, char *path, - struct fuse_file_info *fi) +static int fuse_compat_statfs(struct fuse_fs *fs, const char *path, + struct statvfs *buf) { - int res; - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - res = f->op.opendir(path, fi); - fuse_finish_interrupt(f, req, &d); - return res; + int err; + + if (!fs->compat || fs->compat >= 25) { + err = fs->op.statfs(fs->compat == 25 ? "/" : path, buf); + } else if (fs->compat > 11) { + struct statfs oldbuf; + err = ((struct fuse_operations_compat22 *) &fs->op) + ->statfs("/", &oldbuf); + if (!err) + convert_statfs_old(&oldbuf, buf); + } else { + struct fuse_statfs_compat1 compatbuf; + memset(&compatbuf, 0, sizeof(struct fuse_statfs_compat1)); + err = ((struct fuse_operations_compat1 *) &fs->op)->statfs(&compatbuf); + if (!err) + convert_statfs_compat(&compatbuf, buf); + } + return err; } -static int fuse_do_open(struct fuse *f, fuse_req_t req, char *path, - struct fuse_file_info *fi) +#else /* __FreeBSD__ */ + +static inline int fuse_compat_open(struct fuse_fs *fs, char *path, + struct fuse_file_info *fi) { - int res; - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - res = f->op.open(path, fi); - fuse_finish_interrupt(f, req, &d); - return res; + return fs->op.open(path, fi); } -static int fuse_do_flush(struct fuse *f, fuse_req_t req, const char *path, - struct fuse_file_info *fi) +static inline int fuse_compat_release(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) { - int res; - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - res = f->op.flush(path, fi); - fuse_finish_interrupt(f, req, &d); - return res; + return fs->op.release(path, fi); } -static int fuse_do_statfs(struct fuse *f, fuse_req_t req, const char *path, - struct statvfs *buf) +static inline int fuse_compat_opendir(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) { - int res; - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - res = f->op.statfs(path, buf); - fuse_finish_interrupt(f, req, &d); - return res; + return fs->op.opendir(path, fi); } -static void fuse_do_releasedir(struct fuse *f, fuse_req_t req, - const char *path, struct fuse_file_info *fi) +static inline int fuse_compat_statfs(struct fuse_fs *fs, const char *path, + struct statvfs *buf) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - f->op.releasedir(path, fi); - fuse_finish_interrupt(f, req, &d); + return fs->op.statfs(fs->compat == 25 ? "/" : path, buf); } -static int fuse_do_create(struct fuse *f, fuse_req_t req, const char *path, - mode_t mode, struct fuse_file_info *fi) +#endif /* __FreeBSD__ */ + +int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf) { - int res; - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - res = f->op.create(path, mode, fi); - fuse_finish_interrupt(f, req, &d); - return res; + fuse_get_context()->private_data = fs->user_data; + if (fs->op.getattr) + return fs->op.getattr(path, buf); + else + return -ENOSYS; } -static int fuse_do_lock(struct fuse *f, fuse_req_t req, const char *path, - struct fuse_file_info *fi, int cmd, struct flock *lock) +int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf, + struct fuse_file_info *fi) { - int res; - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - res = f->op.lock(path, fi, cmd, lock); - fuse_finish_interrupt(f, req, &d); - return res; + fuse_get_context()->private_data = fs->user_data; + if (fs->op.fgetattr) + return fs->op.fgetattr(path, buf, fi); + else if (fs->op.getattr) + return fs->op.getattr(path, buf); + else + return -ENOSYS; } -static int is_open(struct fuse *f, fuse_ino_t dir, const char *name) +int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, + const char *newpath) { - struct node *node; - int isopen = 0; - pthread_mutex_lock(&f->lock); - node = lookup_node(f, dir, name); - if (node && node->open_count > 0) - isopen = 1; - pthread_mutex_unlock(&f->lock); - return isopen; + fuse_get_context()->private_data = fs->user_data; + if (fs->op.rename) + return fs->op.rename(oldpath, newpath); + else + return -ENOSYS; } -static char *hidden_name(struct fuse *f, fuse_req_t req, fuse_ino_t dir, - const char *oldname, char *newname, size_t bufsize) + +int fuse_fs_unlink(struct fuse_fs *fs, const char *path) { - struct stat buf; - struct node *node; - struct node *newnode; - char *newpath; - int res; - int failctr = 10; + fuse_get_context()->private_data = fs->user_data; + if (fs->op.unlink) + return fs->op.unlink(path); + else + return -ENOSYS; +} - if (!f->op.getattr) - return NULL; +int fuse_fs_rmdir(struct fuse_fs *fs, const char *path) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.rmdir) + return fs->op.rmdir(path); + else + return -ENOSYS; +} - do { - pthread_mutex_lock(&f->lock); - node = lookup_node(f, dir, oldname); - if (node == NULL) { - pthread_mutex_unlock(&f->lock); - return NULL; - } - do { - f->hidectr ++; - snprintf(newname, bufsize, ".fuse_hidden%08x%08x", - (unsigned int) node->nodeid, f->hidectr); - newnode = lookup_node(f, dir, newname); - } while(newnode); - pthread_mutex_unlock(&f->lock); +int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.symlink) + return fs->op.symlink(linkname, path); + else + return -ENOSYS; +} - newpath = get_path_name(f, dir, newname); - if (!newpath) - break; +int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.link) + return fs->op.link(oldpath, newpath); + else + return -ENOSYS; +} - res = fuse_do_getattr(f, req, newpath, &buf); - if (res != 0) - break; - free(newpath); - newpath = NULL; - } while(--failctr); +int fuse_fs_release(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.release) + return fuse_compat_release(fs, path, fi); + else + return 0; +} - return newpath; +int fuse_fs_opendir(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.opendir) + return fuse_compat_opendir(fs, path, fi); + else + return 0; } -static int hide_node(struct fuse *f, fuse_req_t req, const char *oldpath, - fuse_ino_t dir, const char *oldname) +int fuse_fs_open(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) { - char newname[64]; - char *newpath; - int err = -EBUSY; + fuse_get_context()->private_data = fs->user_data; + if (fs->op.open) + return fuse_compat_open(fs, path, fi); + else + return 0; +} - if (f->op.rename && f->op.unlink) { - newpath = hidden_name(f, req, dir, oldname, newname, sizeof(newname)); - if (newpath) { - err = fuse_do_rename(f, req, oldpath, newpath); - if (!err) - err = rename_node(f, dir, oldname, dir, newname, 1); - free(newpath); - } - } - return err; +int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size, + off_t off, struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.read) + return fs->op.read(path, buf, size, off, fi); + else + return -ENOSYS; } -static int mtime_eq(const struct stat *stbuf, const struct timespec *ts) +int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf, + size_t size, off_t off, struct fuse_file_info *fi) { - return stbuf->st_mtime == ts->tv_sec -#ifdef FUSE_STAT_HAS_NANOSEC - && ST_MTIM(stbuf).tv_nsec == ts->tv_nsec -#endif - ; + fuse_get_context()->private_data = fs->user_data; + if (fs->op.write) + return fs->op.write(path, buf, size, off, fi); + else + return -ENOSYS; } -static void mtime_set(const struct stat *stbuf, struct timespec *ts) +int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync, + struct fuse_file_info *fi) { -#ifdef FUSE_STAT_HAS_NANOSEC - *ts = ST_MTIM(stbuf); -#else - ts->tv_sec = stbuf->st_mtime; -#endif + fuse_get_context()->private_data = fs->user_data; + if (fs->op.fsync) + return fs->op.fsync(path, datasync, fi); + else + return -ENOSYS; } -#ifndef CLOCK_MONOTONIC -#define CLOCK_MONOTONIC CLOCK_REALTIME -#endif +int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.fsyncdir) + return fs->op.fsyncdir(path, datasync, fi); + else + return -ENOSYS; +} -static void curr_time(struct timespec *now) +int fuse_fs_flush(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) { - static clockid_t clockid = CLOCK_MONOTONIC; + fuse_get_context()->private_data = fs->user_data; + if (fs->op.flush) + return fs->op.flush(path, fi); + else + return -ENOSYS; +} + +int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.statfs) + return fuse_compat_statfs(fs, path, buf); + else { + buf->f_namemax = 255; + buf->f_bsize = 512; + return 0; + } +} + +int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.releasedir) + return fs->op.releasedir(path, fi); + else + return 0; +} + +static int fill_dir_old(struct fuse_dirhandle *dh, const char *name, int type, + ino_t ino) +{ + int res; + struct stat stbuf; + + memset(&stbuf, 0, sizeof(stbuf)); + stbuf.st_mode = type << 12; + stbuf.st_ino = ino; + + res = dh->filler(dh->buf, name, &stbuf, 0); + return res ? -ENOMEM : 0; +} + +int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf, + fuse_fill_dir_t filler, off_t off, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.readdir) + return fs->op.readdir(path, buf, filler, off, fi); + else if (fs->op.getdir) { + struct fuse_dirhandle dh; + dh.filler = filler; + dh.buf = buf; + return fs->op.getdir(path, &dh, fill_dir_old); + } else + return -ENOSYS; +} + +int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.create) + return fs->op.create(path, mode, fi); + else + return -ENOSYS; +} + +int fuse_fs_lock(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi, int cmd, struct flock *lock) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.lock) + return fs->op.lock(path, fi, cmd, lock); + else + return -ENOSYS; +} + +int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.chown) + return fs->op.chown(path, uid, gid); + else + return -ENOSYS; +} + +int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.truncate) + return fs->op.truncate(path, size); + else + return -ENOSYS; +} + +int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, off_t size, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.ftruncate) + return fs->op.ftruncate(path, size, fi); + else if (fs->op.truncate) + return fs->op.truncate(path, size); + else + return -ENOSYS; +} + +int fuse_fs_utimens(struct fuse_fs *fs, const char *path, + const struct timespec tv[2]) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.utimens) + return fs->op.utimens(path, tv); + else if(fs->op.utime) { + struct utimbuf buf; + buf.actime = tv[0].tv_sec; + buf.modtime = tv[1].tv_sec; + return fs->op.utime(path, &buf); + } else + return -ENOSYS; +} + +int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.access) + return fs->op.access(path, mask); + else + return -ENOSYS; +} + +int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf, + size_t len) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.readlink) + return fs->op.readlink(path, buf, len); + else + return -ENOSYS; +} + +int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode, + dev_t rdev) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.mknod) + return fs->op.mknod(path, mode, rdev); + else + return -ENOSYS; +} + +int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.mkdir) + return fs->op.mkdir(path, mode); + else + return -ENOSYS; +} + +int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name, + const char *value, size_t size, int flags) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.setxattr) + return fs->op.setxattr(path, name, value, size, flags); + else + return -ENOSYS; +} + +int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name, + char *value, size_t size) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.getxattr) + return fs->op.getxattr(path, name, value, size); + else + return -ENOSYS; +} + +int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list, + size_t size) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.listxattr) + return fs->op.listxattr(path, list, size); + else + return -ENOSYS; +} + +int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize, + uint64_t *idx) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.bmap) + return fs->op.bmap(path, blocksize, idx); + else + return -ENOSYS; +} + +int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.removexattr) + return fs->op.removexattr(path, name); + else + return -ENOSYS; +} + +static int is_open(struct fuse *f, fuse_ino_t dir, const char *name) +{ + struct node *node; + int isopen = 0; + pthread_mutex_lock(&f->lock); + node = lookup_node(f, dir, name); + if (node && node->open_count > 0) + isopen = 1; + pthread_mutex_unlock(&f->lock); + return isopen; +} + +static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname, + char *newname, size_t bufsize) +{ + struct stat buf; + struct node *node; + struct node *newnode; + char *newpath; + int res; + int failctr = 10; + + do { + pthread_mutex_lock(&f->lock); + node = lookup_node(f, dir, oldname); + if (node == NULL) { + pthread_mutex_unlock(&f->lock); + return NULL; + } + do { + f->hidectr ++; + snprintf(newname, bufsize, ".fuse_hidden%08x%08x", + (unsigned int) node->nodeid, f->hidectr); + newnode = lookup_node(f, dir, newname); + } while(newnode); + pthread_mutex_unlock(&f->lock); + + newpath = get_path_name(f, dir, newname); + if (!newpath) + break; + + res = fuse_fs_getattr(f->fs, newpath, &buf); + if (res == -ENOENT) + break; + free(newpath); + newpath = NULL; + } while(res == 0 && --failctr); + + return newpath; +} + +static int hide_node(struct fuse *f, const char *oldpath, + fuse_ino_t dir, const char *oldname) +{ + char newname[64]; + char *newpath; + int err = -EBUSY; + + newpath = hidden_name(f, dir, oldname, newname, sizeof(newname)); + if (newpath) { + err = fuse_fs_rename(f->fs, oldpath, newpath); + if (!err) + err = rename_node(f, dir, oldname, dir, newname, 1); + free(newpath); + } + return err; +} + +static int mtime_eq(const struct stat *stbuf, const struct timespec *ts) +{ + return stbuf->st_mtime == ts->tv_sec && ST_MTIM_NSEC(stbuf) == ts->tv_nsec; +} + +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC CLOCK_REALTIME +#endif + +static void curr_time(struct timespec *now) +{ + static clockid_t clockid = CLOCK_MONOTONIC; int res = clock_gettime(clockid, now); if (res == -1 && errno == EINVAL) { clockid = CLOCK_REALTIME; @@ -751,22 +1194,23 @@ static void update_stat(struct node *node, const struct stat *stbuf) if (node->cache_valid && (!mtime_eq(stbuf, &node->mtime) || stbuf->st_size != node->size)) node->cache_valid = 0; - mtime_set(stbuf, &node->mtime); + node->mtime.tv_sec = stbuf->st_mtime; + node->mtime.tv_nsec = ST_MTIM_NSEC(stbuf); node->size = stbuf->st_size; curr_time(&node->stat_updated); } -static int lookup_path(struct fuse *f, fuse_req_t req, fuse_ino_t nodeid, +static int lookup_path(struct fuse *f, fuse_ino_t nodeid, const char *name, const char *path, struct fuse_entry_param *e, struct fuse_file_info *fi) { int res; memset(e, 0, sizeof(struct fuse_entry_param)); - if (fi && f->op.fgetattr) - res = fuse_do_fgetattr(f, req, path, &e->attr, fi); + if (fi) + res = fuse_fs_fgetattr(f->fs, path, &e->attr, fi); else - res = fuse_do_getattr(f, req, path, &e->attr); + res = fuse_fs_getattr(f->fs, path, &e->attr); if (res == 0) { struct node *node; @@ -855,7 +1299,6 @@ static struct fuse *req_fuse_prepare(fuse_req_t req) c->ctx.uid = ctx->uid; c->ctx.gid = ctx->gid; c->ctx.pid = ctx->pid; - c->ctx.private_data = c->ctx.fuse->user_data; return c->ctx.fuse; } @@ -876,33 +1319,46 @@ static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e, reply_err(req, err); } -static void fuse_data_init(void *data, struct fuse_conn_info *conn) +void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.init) + fs->user_data = fs->op.init(conn); +} + +static void fuse_lib_init(void *data, struct fuse_conn_info *conn) { struct fuse *f = (struct fuse *) data; struct fuse_context_i *c = fuse_get_context_internal(); memset(c, 0, sizeof(*c)); c->ctx.fuse = f; - c->ctx.private_data = f->user_data; + fuse_fs_init(f->fs, conn); +} - if (f->op.init) - f->user_data = f->op.init(conn); +void fuse_fs_destroy(struct fuse_fs *fs) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.destroy) + fs->op.destroy(fs->user_data); + if (fs->m) + fuse_put_module(fs->m); + free(fs); } -static void fuse_data_destroy(void *data) +static void fuse_lib_destroy(void *data) { struct fuse *f = (struct fuse *) data; struct fuse_context_i *c = fuse_get_context_internal(); memset(c, 0, sizeof(*c)); c->ctx.fuse = f; - c->ctx.private_data = f->user_data; - - if (f->op.destroy) - f->op.destroy(f->user_data); + fuse_fs_destroy(f->fs); + f->fs = NULL; } -static void fuse_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) +static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent, + const char *name) { struct fuse *f = req_fuse_prepare(req); struct fuse_entry_param e; @@ -913,26 +1369,27 @@ static void fuse_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) pthread_rwlock_rdlock(&f->tree_lock); path = get_path_name(f, parent, name); if (path != NULL) { + struct fuse_intr_data d; if (f->conf.debug) { printf("LOOKUP %s\n", path); fflush(stdout); } - err = -ENOSYS; - if (f->op.getattr) { - err = lookup_path(f, req, parent, name, path, &e, NULL); - if (err == -ENOENT && f->conf.negative_timeout != 0.0) { - e.ino = 0; - e.entry_timeout = f->conf.negative_timeout; - err = 0; - } + fuse_prepare_interrupt(f, req, &d); + err = lookup_path(f, parent, name, path, &e, NULL); + if (err == -ENOENT && f->conf.negative_timeout != 0.0) { + e.ino = 0; + e.entry_timeout = f->conf.negative_timeout; + err = 0; } + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); reply_entry(req, &e, err); } -static void fuse_forget(fuse_req_t req, fuse_ino_t ino, unsigned long nlookup) +static void fuse_lib_forget(fuse_req_t req, fuse_ino_t ino, + unsigned long nlookup) { struct fuse *f = req_fuse(req); if (f->conf.debug) { @@ -943,8 +1400,8 @@ static void fuse_forget(fuse_req_t req, fuse_ino_t ino, unsigned long nlookup) fuse_reply_none(req); } -static void fuse_getattr(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info *fi) +static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); struct stat buf; @@ -958,9 +1415,10 @@ static void fuse_getattr(fuse_req_t req, fuse_ino_t ino, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - err = -ENOSYS; - if (f->op.getattr) - err = fuse_do_getattr(f, req, path, &buf); + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_getattr(f->fs, path, &buf); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -976,94 +1434,16 @@ static void fuse_getattr(fuse_req_t req, fuse_ino_t ino, reply_err(req, err); } -static int do_chmod(struct fuse *f, fuse_req_t req, const char *path, - struct stat *attr) +int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode) { - int err; - - err = -ENOSYS; - if (f->op.chmod) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.chmod(path, attr->st_mode); - fuse_finish_interrupt(f, req, &d); - } - - return err; -} - -static int do_chown(struct fuse *f, fuse_req_t req, const char *path, - struct stat *attr, int valid) -{ - int err; - uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t) -1; - gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t) -1; - - err = -ENOSYS; - if (f->op.chown) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.chown(path, uid, gid); - fuse_finish_interrupt(f, req, &d); - } - - return err; -} - -static int do_truncate(struct fuse *f, fuse_req_t req, const char *path, - struct stat *attr, struct fuse_file_info *fi) -{ - int err; - struct fuse_intr_data d; - - err = -ENOSYS; - if (fi && f->op.ftruncate) { - fuse_prepare_interrupt(f, req, &d); - err = f->op.ftruncate(path, attr->st_size, fi); - fuse_finish_interrupt(f, req, &d); - } else if (f->op.truncate) { - fuse_prepare_interrupt(f, req, &d); - err = f->op.truncate(path, attr->st_size); - fuse_finish_interrupt(f, req, &d); - } - return err; -} - -static int do_utimens(struct fuse *f, fuse_req_t req, const char *path, - struct stat *attr) -{ - int err; - struct fuse_intr_data d; - - err = -ENOSYS; - if (f->op.utimens) { - struct timespec tv[2]; -#ifdef FUSE_STAT_HAS_NANOSEC - tv[0] = ST_ATIM(attr); - tv[1] = ST_MTIM(attr); -#else - tv[0].tv_sec = attr->st_atime; - tv[0].tv_nsec = 0; - tv[1].tv_sec = attr->st_mtime; - tv[1].tv_nsec = 0; -#endif - fuse_prepare_interrupt(f, req, &d); - err = f->op.utimens(path, tv); - fuse_finish_interrupt(f, req, &d); - } else if (f->op.utime) { - struct utimbuf buf; - buf.actime = attr->st_atime; - buf.modtime = attr->st_mtime; - fuse_prepare_interrupt(f, req, &d); - err = f->op.utime(path, &buf); - fuse_finish_interrupt(f, req, &d); - } - - return err; + if (fs->op.chmod) + return fs->op.chmod(path, mode); + else + return -ENOSYS; } -static void fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, - int valid, struct fuse_file_info *fi) +static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + int valid, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); struct stat buf; @@ -1074,20 +1454,36 @@ static void fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - err = -ENOSYS; - if (f->op.getattr) { - err = 0; - if (!err && (valid & FUSE_SET_ATTR_MODE)) - err = do_chmod(f, req, path, attr); - if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) - err = do_chown(f, req, path, attr, valid); - if (!err && (valid & FUSE_SET_ATTR_SIZE)) - err = do_truncate(f, req, path, attr, fi); - if (!err && (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) == (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) - err = do_utimens(f, req, path, attr); - if (!err) - err = fuse_do_getattr(f, req, path, &buf); + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = 0; + if (!err && (valid & FUSE_SET_ATTR_MODE)) + err = fuse_fs_chmod(f->fs, path, attr->st_mode); + if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) { + uid_t uid = + (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t) -1; + gid_t gid = + (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t) -1; + err = fuse_fs_chown(f->fs, path, uid, gid); + } + if (!err && (valid & FUSE_SET_ATTR_SIZE)) { + if (fi) + err = fuse_fs_ftruncate(f->fs, path, attr->st_size, fi); + else + err = fuse_fs_truncate(f->fs, path, attr->st_size); + } + if (!err && (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) == + (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { + struct timespec tv[2]; + tv[0].tv_sec = attr->st_atime; + tv[0].tv_nsec = ST_ATIM_NSEC(attr); + tv[1].tv_sec = attr->st_mtime; + tv[1].tv_nsec = ST_MTIM_NSEC(attr); + err = fuse_fs_utimens(f->fs, path, tv); } + if (!err) + err = fuse_fs_getattr(f->fs, path, &buf); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -1103,7 +1499,7 @@ static void fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, reply_err(req, err); } -static void fuse_access(fuse_req_t req, fuse_ino_t ino, int mask) +static void fuse_lib_access(fuse_req_t req, fuse_ino_t ino, int mask) { struct fuse *f = req_fuse_prepare(req); char *path; @@ -1113,24 +1509,21 @@ static void fuse_access(fuse_req_t req, fuse_ino_t ino, int mask) pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { + struct fuse_intr_data d; if (f->conf.debug) { printf("ACCESS %s 0%o\n", path, mask); fflush(stdout); } - err = -ENOSYS; - if (f->op.access) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.access(path, mask); - fuse_finish_interrupt(f, req, &d); - } + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_access(f->fs, path, mask); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); reply_err(req, err); } -static void fuse_readlink(fuse_req_t req, fuse_ino_t ino) +static void fuse_lib_readlink(fuse_req_t req, fuse_ino_t ino) { struct fuse *f = req_fuse_prepare(req); char linkname[PATH_MAX + 1]; @@ -1141,13 +1534,10 @@ static void fuse_readlink(fuse_req_t req, fuse_ino_t ino) pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - err = -ENOSYS; - if (f->op.readlink) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.readlink(path, linkname, sizeof(linkname)); - fuse_finish_interrupt(f, req, &d); - } + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_readlink(f->fs, path, linkname, sizeof(linkname)); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -1158,8 +1548,8 @@ static void fuse_readlink(fuse_req_t req, fuse_ino_t ino) reply_err(req, err); } -static void fuse_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, - mode_t mode, dev_t rdev) +static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, dev_t rdev) { struct fuse *f = req_fuse_prepare(req); struct fuse_entry_param e; @@ -1169,39 +1559,39 @@ static void fuse_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, err = -ENOENT; pthread_rwlock_rdlock(&f->tree_lock); path = get_path_name(f, parent, name); - if (path != NULL) { + if (path) { + struct fuse_intr_data d; if (f->conf.debug) { printf("MKNOD %s\n", path); fflush(stdout); } + fuse_prepare_interrupt(f, req, &d); err = -ENOSYS; - if (S_ISREG(mode) && f->op.create && f->op.getattr) { + if (S_ISREG(mode)) { struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.flags = O_CREAT | O_EXCL | O_WRONLY; - err = fuse_do_create(f, req, path, mode, &fi); + err = fuse_fs_create(f->fs, path, mode, &fi); if (!err) { - err = lookup_path(f, req, parent, name, path, &e, &fi); - if (f->op.release) - fuse_do_release(f, req, path, &fi); + err = lookup_path(f, parent, name, path, &e, &fi); + fuse_fs_release(f->fs, path, &fi); } - } else if (f->op.mknod && f->op.getattr) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.mknod(path, mode, rdev); - fuse_finish_interrupt(f, req, &d); + } + if (err == -ENOSYS) { + err = fuse_fs_mknod(f->fs, path, mode, rdev); if (!err) - err = lookup_path(f, req, parent, name, path, &e, NULL); + err = lookup_path(f, parent, name, path, &e, NULL); } + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); reply_entry(req, &e, err); } -static void fuse_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, - mode_t mode) +static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode) { struct fuse *f = req_fuse_prepare(req); struct fuse_entry_param e; @@ -1212,26 +1602,24 @@ static void fuse_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, pthread_rwlock_rdlock(&f->tree_lock); path = get_path_name(f, parent, name); if (path != NULL) { + struct fuse_intr_data d; if (f->conf.debug) { printf("MKDIR %s\n", path); fflush(stdout); } - err = -ENOSYS; - if (f->op.mkdir && f->op.getattr) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.mkdir(path, mode); - fuse_finish_interrupt(f, req, &d); - if (!err) - err = lookup_path(f, req, parent, name, path, &e, NULL); - } + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_mkdir(f->fs, path, mode); + if (!err) + err = lookup_path(f, parent, name, path, &e, NULL); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); reply_entry(req, &e, err); } -static void fuse_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) +static void fuse_lib_unlink(fuse_req_t req, fuse_ino_t parent, + const char *name) { struct fuse *f = req_fuse_prepare(req); char *path; @@ -1241,27 +1629,27 @@ static void fuse_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) pthread_rwlock_wrlock(&f->tree_lock); path = get_path_name(f, parent, name); if (path != NULL) { + struct fuse_intr_data d; if (f->conf.debug) { printf("UNLINK %s\n", path); fflush(stdout); } - err = -ENOSYS; - if (f->op.unlink) { - if (!f->conf.hard_remove && is_open(f, parent, name)) - err = hide_node(f, req, path, parent, name); - else { - err = fuse_do_unlink(f, req, path); - if (!err) - remove_node(f, parent, name); - } + fuse_prepare_interrupt(f, req, &d); + if (!f->conf.hard_remove && is_open(f, parent, name)) + err = hide_node(f, path, parent, name); + else { + err = fuse_fs_unlink(f->fs, path); + if (!err) + remove_node(f, parent, name); } + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); reply_err(req, err); } -static void fuse_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) +static void fuse_lib_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) { struct fuse *f = req_fuse_prepare(req); char *path; @@ -1271,27 +1659,24 @@ static void fuse_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) pthread_rwlock_wrlock(&f->tree_lock); path = get_path_name(f, parent, name); if (path != NULL) { + struct fuse_intr_data d; if (f->conf.debug) { printf("RMDIR %s\n", path); fflush(stdout); } - err = -ENOSYS; - if (f->op.rmdir) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.rmdir(path); - fuse_finish_interrupt(f, req, &d); - if (!err) - remove_node(f, parent, name); - } + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_rmdir(f->fs, path); + fuse_finish_interrupt(f, req, &d); + if (!err) + remove_node(f, parent, name); free(path); } pthread_rwlock_unlock(&f->tree_lock); reply_err(req, err); } -static void fuse_symlink(fuse_req_t req, const char *linkname, - fuse_ino_t parent, const char *name) +static void fuse_lib_symlink(fuse_req_t req, const char *linkname, + fuse_ino_t parent, const char *name) { struct fuse *f = req_fuse_prepare(req); struct fuse_entry_param e; @@ -1302,27 +1687,25 @@ static void fuse_symlink(fuse_req_t req, const char *linkname, pthread_rwlock_rdlock(&f->tree_lock); path = get_path_name(f, parent, name); if (path != NULL) { + struct fuse_intr_data d; if (f->conf.debug) { printf("SYMLINK %s\n", path); fflush(stdout); } - err = -ENOSYS; - if (f->op.symlink && f->op.getattr) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.symlink(linkname, path); - fuse_finish_interrupt(f, req, &d); - if (!err) - err = lookup_path(f, req, parent, name, path, &e, NULL); - } + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_symlink(f->fs, linkname, path); + if (!err) + err = lookup_path(f, parent, name, path, &e, NULL); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); reply_entry(req, &e, err); } -static void fuse_rename(fuse_req_t req, fuse_ino_t olddir, const char *oldname, - fuse_ino_t newdir, const char *newname) +static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir, + const char *oldname, fuse_ino_t newdir, + const char *newname) { struct fuse *f = req_fuse_prepare(req); char *oldpath; @@ -1335,22 +1718,21 @@ static void fuse_rename(fuse_req_t req, fuse_ino_t olddir, const char *oldname, if (oldpath != NULL) { newpath = get_path_name(f, newdir, newname); if (newpath != NULL) { + struct fuse_intr_data d; if (f->conf.debug) { printf("RENAME %s -> %s\n", oldpath, newpath); fflush(stdout); } - err = -ENOSYS; - if (f->op.rename) { - err = 0; - if (!f->conf.hard_remove && - is_open(f, newdir, newname)) - err = hide_node(f, req, newpath, newdir, newname); - if (!err) { - err = fuse_do_rename(f, req, oldpath, newpath); - if (!err) - err = rename_node(f, olddir, oldname, newdir, newname, 0); - } + err = 0; + fuse_prepare_interrupt(f, req, &d); + if (!f->conf.hard_remove && is_open(f, newdir, newname)) + err = hide_node(f, newpath, newdir, newname); + if (!err) { + err = fuse_fs_rename(f->fs, oldpath, newpath); + if (!err) + err = rename_node(f, olddir, oldname, newdir, newname, 0); } + fuse_finish_interrupt(f, req, &d); free(newpath); } free(oldpath); @@ -1359,8 +1741,8 @@ static void fuse_rename(fuse_req_t req, fuse_ino_t olddir, const char *oldname, reply_err(req, err); } -static void fuse_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, - const char *newname) +static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, + const char *newname) { struct fuse *f = req_fuse_prepare(req); struct fuse_entry_param e; @@ -1374,20 +1756,16 @@ static void fuse_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, if (oldpath != NULL) { newpath = get_path_name(f, newparent, newname); if (newpath != NULL) { + struct fuse_intr_data d; if (f->conf.debug) { printf("LINK %s\n", newpath); fflush(stdout); } - err = -ENOSYS; - if (f->op.link && f->op.getattr) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.link(oldpath, newpath); - fuse_finish_interrupt(f, req, &d); - if (!err) - err = lookup_path(f, req, newparent, newname, newpath, &e, - NULL); - } + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_link(f->fs, oldpath, newpath); + if (!err) + err = lookup_path(f, newparent, newname, newpath, &e, NULL); + fuse_finish_interrupt(f, req, &d); free(newpath); } free(oldpath); @@ -1396,10 +1774,34 @@ static void fuse_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, reply_entry(req, &e, err); } -static void fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, - mode_t mode, struct fuse_file_info *fi) +static void fuse_do_release(struct fuse *f, fuse_ino_t ino, const char *path, + struct fuse_file_info *fi) +{ + struct node *node; + int unlink_hidden = 0; + + fuse_fs_release(f->fs, path ? path : "-", fi); + + pthread_mutex_lock(&f->lock); + node = get_node(f, ino); + assert(node->open_count > 0); + --node->open_count; + if (node->is_hidden && !node->open_count) { + unlink_hidden = 1; + node->is_hidden = 0; + } + pthread_mutex_unlock(&f->lock); + + if(unlink_hidden && path) + fuse_fs_unlink(f->fs, path); +} + +static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent, + const char *name, mode_t mode, + struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; struct fuse_entry_param e; char *path; int err; @@ -1407,53 +1809,48 @@ static void fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, err = -ENOENT; pthread_rwlock_rdlock(&f->tree_lock); path = get_path_name(f, parent, name); - if (path != NULL) { - err = -ENOSYS; - if (f->op.create && f->op.getattr) { - err = fuse_do_create(f, req, path, mode, fi); - if (!err) { - if (f->conf.debug) { - printf("CREATE[%llu] flags: 0x%x %s\n", - (unsigned long long) fi->fh, fi->flags, path); - fflush(stdout); - } - err = lookup_path(f, req, parent, name, path, &e, fi); - if (err) { - if (f->op.release) - fuse_do_release(f, req, path, fi); - } else if (!S_ISREG(e.attr.st_mode)) { - err = -EIO; - if (f->op.release) - fuse_do_release(f, req, path, fi); - forget_node(f, e.ino, 1); - } + if (path) { + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_create(f->fs, path, mode, fi); + if (!err) { + err = lookup_path(f, parent, name, path, &e, fi); + if (err) + fuse_fs_release(f->fs, path, fi); + else if (!S_ISREG(e.attr.st_mode)) { + err = -EIO; + fuse_fs_release(f->fs, path, fi); + forget_node(f, e.ino, 1); + } else { + if (f->conf.direct_io) + fi->direct_io = 1; + if (f->conf.kernel_cache) + fi->keep_cache = 1; + } } + fuse_finish_interrupt(f, req, &d); } - if (!err) { - if (f->conf.direct_io) - fi->direct_io = 1; - if (f->conf.kernel_cache) - fi->keep_cache = 1; - pthread_mutex_lock(&f->lock); + get_node(f, e.ino)->open_count++; + pthread_mutex_unlock(&f->lock); if (fuse_reply_create(req, &e, fi) == -ENOENT) { /* The open syscall was interrupted, so it must be cancelled */ - if(f->op.release) - fuse_do_release(f, req, path, fi); - pthread_mutex_unlock(&f->lock); + fuse_prepare_interrupt(f, req, &d); + fuse_do_release(f, e.ino, path, fi); + fuse_finish_interrupt(f, req, &d); forget_node(f, e.ino, 1); - } else { - struct node *node = get_node(f, e.ino); - node->open_count ++; - pthread_mutex_unlock(&f->lock); + } else if (f->conf.debug) { + printf(" CREATE[%llu] flags: 0x%x %s\n", + (unsigned long long) fi->fh, fi->flags, path); + fflush(stdout); } } else reply_err(req, err); if (path) free(path); + pthread_rwlock_unlock(&f->tree_lock); } @@ -1464,10 +1861,13 @@ static double diff_timespec(const struct timespec *t1, ((double) t1->tv_nsec - (double) t2->tv_nsec) / 1000000000.0; } -static void open_auto_cache(struct fuse *f, fuse_req_t req, fuse_ino_t ino, - const char *path, struct fuse_file_info *fi) +static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path, + struct fuse_file_info *fi) { - struct node *node = get_node(f, ino); + struct node *node; + + pthread_mutex_lock(&f->lock); + node = get_node(f, ino); if (node->cache_valid) { struct timespec now; @@ -1475,12 +1875,9 @@ static void open_auto_cache(struct fuse *f, fuse_req_t req, fuse_ino_t ino, if (diff_timespec(&now, &node->stat_updated) > f->conf.ac_attr_timeout) { struct stat stbuf; int err; - - if (f->op.fgetattr) - err = fuse_do_fgetattr(f, req, path, &stbuf, fi); - else - err = fuse_do_getattr(f, req, path, &stbuf); - + pthread_mutex_unlock(&f->lock); + err = fuse_fs_fgetattr(f->fs, path, &stbuf, fi); + pthread_mutex_lock(&f->lock); if (!err) update_stat(node, &stbuf); else @@ -1491,47 +1888,48 @@ static void open_auto_cache(struct fuse *f, fuse_req_t req, fuse_ino_t ino, fi->keep_cache = 1; node->cache_valid = 1; + pthread_mutex_unlock(&f->lock); } -static void fuse_open(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info *fi) +static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; char *path = NULL; int err = 0; + err = -ENOENT; pthread_rwlock_rdlock(&f->tree_lock); - if (f->op.open) { - err = -ENOENT; - path = get_path(f, ino); - if (path != NULL) - err = fuse_compat_open(f, req, path, fi); + path = get_path(f, ino); + if (path) { + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_open(f->fs, path, fi); + if (!err) { + if (f->conf.direct_io) + fi->direct_io = 1; + if (f->conf.kernel_cache) + fi->keep_cache = 1; + + if (f->conf.auto_cache) + open_auto_cache(f, ino, path, fi); + } + fuse_finish_interrupt(f, req, &d); } if (!err) { - if (f->conf.debug) { - printf("OPEN[%llu] flags: 0x%x\n", (unsigned long long) fi->fh, - fi->flags); - fflush(stdout); - } - - if (f->conf.direct_io) - fi->direct_io = 1; - if (f->conf.kernel_cache) - fi->keep_cache = 1; - pthread_mutex_lock(&f->lock); - if (f->conf.auto_cache) - open_auto_cache(f, req, ino, path, fi); - + get_node(f, ino)->open_count++; + pthread_mutex_unlock(&f->lock); if (fuse_reply_open(req, fi) == -ENOENT) { /* The open syscall was interrupted, so it must be cancelled */ - if(f->op.release && path != NULL) - fuse_compat_release(f, req, path, fi); - } else { - struct node *node = get_node(f, ino); - node->open_count ++; + fuse_prepare_interrupt(f, req, &d); + fuse_do_release(f, ino, path, fi); + fuse_finish_interrupt(f, req, &d); + } else if (f->conf.debug) { + printf("OPEN[%llu] flags: 0x%x %s\n", + (unsigned long long) fi->fh, fi->flags, path); + fflush(stdout); } - pthread_mutex_unlock(&f->lock); } else reply_err(req, err); @@ -1540,8 +1938,8 @@ static void fuse_open(fuse_req_t req, fuse_ino_t ino, pthread_rwlock_unlock(&f->tree_lock); } -static void fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, - struct fuse_file_info *fi) +static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); char *path; @@ -1558,6 +1956,7 @@ static void fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { + struct fuse_intr_data d; if (f->conf.debug) { printf("READ[%llu] %lu bytes from %llu\n", (unsigned long long) fi->fh, (unsigned long) size, @@ -1565,13 +1964,9 @@ static void fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, fflush(stdout); } - res = -ENOSYS; - if (f->op.read) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - res = f->op.read(path, buf, size, off, fi); - fuse_finish_interrupt(f, req, &d); - } + fuse_prepare_interrupt(f, req, &d); + res = fuse_fs_read(f->fs, path, buf, size, off, fi); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -1591,7 +1986,7 @@ static void fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, free(buf); } -static void fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf, +static void fuse_lib_write(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); @@ -1602,6 +1997,7 @@ static void fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { + struct fuse_intr_data d; if (f->conf.debug) { printf("WRITE%s[%llu] %lu bytes to %llu\n", fi->writepage ? "PAGE" : "", (unsigned long long) fi->fh, @@ -1609,13 +2005,9 @@ static void fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf, fflush(stdout); } - res = -ENOSYS; - if (f->op.write) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - res = f->op.write(path, buf, size, off, fi); - fuse_finish_interrupt(f, req, &d); - } + fuse_prepare_interrupt(f, req, &d); + res = fuse_fs_write(f->fs, path, buf, size, off, fi); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -1634,13 +2026,12 @@ static void fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf, reply_err(req, res); } -static void fuse_release(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info *fi) +static void fuse_lib_release(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; char *path; - struct node *node; - int unlink_hidden = 0; int err = 0; pthread_rwlock_rdlock(&f->tree_lock); @@ -1650,23 +2041,14 @@ static void fuse_release(fuse_req_t req, fuse_ino_t ino, (unsigned long long) fi->fh, fi->flags); fflush(stdout); } - if (fi->flush && path && f->op.flush) - err = fuse_do_flush(f, req, path, fi); - if (f->op.release) - fuse_compat_release(f, req, path, fi); - - pthread_mutex_lock(&f->lock); - node = get_node(f, ino); - assert(node->open_count > 0); - --node->open_count; - if (node->is_hidden && !node->open_count) { - unlink_hidden = 1; - node->is_hidden = 0; + fuse_prepare_interrupt(f, req, &d); + if (fi->flush && path) { + err = fuse_fs_flush(f->fs, path, fi); + if (err == -ENOSYS) + err = 0; } - pthread_mutex_unlock(&f->lock); - - if(unlink_hidden && path) - fuse_do_unlink(f, req, path); + fuse_do_release(f, ino, path, fi); + fuse_finish_interrupt(f, req, &d); if (path) free(path); @@ -1675,7 +2057,7 @@ static void fuse_release(fuse_req_t req, fuse_ino_t ino, reply_err(req, err); } -static void fuse_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, +static void fuse_lib_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); @@ -1686,45 +2068,46 @@ static void fuse_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { + struct fuse_intr_data d; if (f->conf.debug) { printf("FSYNC[%llu]\n", (unsigned long long) fi->fh); fflush(stdout); } - err = -ENOSYS; - if (f->op.fsync) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.fsync(path, datasync, fi); - fuse_finish_interrupt(f, req, &d); - } + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_fsync(f->fs, path, datasync, fi); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); reply_err(req, err); } -static struct fuse_dirhandle *get_dirhandle(const struct fuse_file_info *llfi, - struct fuse_file_info *fi) +static struct fuse_dh *get_dirhandle(const struct fuse_file_info *llfi, + struct fuse_file_info *fi) { - struct fuse_dirhandle *dh = (struct fuse_dirhandle *) (uintptr_t) llfi->fh; + struct fuse_dh *dh = (struct fuse_dh *) (uintptr_t) llfi->fh; memset(fi, 0, sizeof(struct fuse_file_info)); fi->fh = dh->fh; fi->fh_old = dh->fh; return dh; } -static void fuse_opendir(fuse_req_t req, fuse_ino_t ino, +static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *llfi) { struct fuse *f = req_fuse_prepare(req); - struct fuse_dirhandle *dh; + struct fuse_intr_data d; + struct fuse_dh *dh; + struct fuse_file_info fi; + char *path; + int err; - dh = (struct fuse_dirhandle *) malloc(sizeof(struct fuse_dirhandle)); + dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh)); if (dh == NULL) { reply_err(req, -ENOMEM); return; } - memset(dh, 0, sizeof(struct fuse_dirhandle)); + memset(dh, 0, sizeof(struct fuse_dh)); dh->fuse = f; dh->contents = NULL; dh->len = 0; @@ -1734,43 +2117,36 @@ static void fuse_opendir(fuse_req_t req, fuse_ino_t ino, llfi->fh = (uintptr_t) dh; - if (f->op.opendir) { - struct fuse_file_info fi; - char *path; - int err; - - memset(&fi, 0, sizeof(fi)); - fi.flags = llfi->flags; + memset(&fi, 0, sizeof(fi)); + fi.flags = llfi->flags; - err = -ENOENT; - pthread_rwlock_rdlock(&f->tree_lock); - path = get_path(f, ino); - if (path != NULL) { - err = fuse_compat_opendir(f, req, path, &fi); - dh->fh = fi.fh; - } - if (!err) { - pthread_mutex_lock(&f->lock); - if (fuse_reply_open(req, llfi) == -ENOENT) { - /* The opendir syscall was interrupted, so it must be - cancelled */ - if(f->op.releasedir) - fuse_do_releasedir(f, req, path, &fi); - pthread_mutex_destroy(&dh->lock); - free(dh); - } - pthread_mutex_unlock(&f->lock); - } else { - reply_err(req, err); + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_opendir(f->fs, path, &fi); + fuse_finish_interrupt(f, req, &d); + dh->fh = fi.fh; + } + if (!err) { + if (fuse_reply_open(req, llfi) == -ENOENT) { + /* The opendir syscall was interrupted, so it must be cancelled */ + fuse_prepare_interrupt(f, req, &d); + fuse_fs_releasedir(f->fs, path, &fi); + fuse_finish_interrupt(f, req, &d); + pthread_mutex_destroy(&dh->lock); free(dh); } - free(path); - pthread_rwlock_unlock(&f->tree_lock); - } else - fuse_reply_open(req, llfi); + } else { + reply_err(req, err); + free(dh); + } + free(path); + pthread_rwlock_unlock(&f->tree_lock); } -static int extend_contents(struct fuse_dirhandle *dh, unsigned minsize) +static int extend_contents(struct fuse_dh *dh, unsigned minsize) { if (minsize > dh->size) { char *newptr; @@ -1791,9 +2167,10 @@ static int extend_contents(struct fuse_dirhandle *dh, unsigned minsize) return 0; } -static int fill_dir_common(struct fuse_dirhandle *dh, const char *name, - const struct stat *statp, off_t off) +static int fill_dir(void *dh_, const char *name, const struct stat *statp, + off_t off) { + struct fuse_dh *dh = (struct fuse_dh *) dh_; struct stat stbuf; size_t newlen; @@ -1838,27 +2215,8 @@ static int fill_dir_common(struct fuse_dirhandle *dh, const char *name, return 0; } -static int fill_dir(void *buf, const char *name, const struct stat *stbuf, - off_t off) -{ - return fill_dir_common((struct fuse_dirhandle *) buf, name, stbuf, off); -} - -static int fill_dir_old(struct fuse_dirhandle *dh, const char *name, int type, - ino_t ino) -{ - struct stat stbuf; - - memset(&stbuf, 0, sizeof(stbuf)); - stbuf.st_mode = type << 12; - stbuf.st_ino = ino; - - fill_dir_common(dh, name, &stbuf, 0); - return dh->error; -} - static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino, - size_t size, off_t off, struct fuse_dirhandle *dh, + size_t size, off_t off, struct fuse_dh *dh, struct fuse_file_info *fi) { int err = -ENOENT; @@ -1866,23 +2224,16 @@ static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { + struct fuse_intr_data d; + dh->len = 0; dh->error = 0; dh->needlen = size; dh->filled = 1; - dh->req = req; - err = -ENOSYS; - if (f->op.readdir) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.readdir(path, dh, fill_dir, off, fi); - fuse_finish_interrupt(f, req, &d); - } else if (f->op.getdir) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.getdir(path, dh, fill_dir_old); - fuse_finish_interrupt(f, req, &d); - } + dh->req = req; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_readdir(f->fs, path, dh, fill_dir, off, fi); + fuse_finish_interrupt(f, req, &d); dh->req = NULL; if (!err) err = dh->error; @@ -1894,12 +2245,12 @@ static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino, return err; } -static void fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, - off_t off, struct fuse_file_info *llfi) +static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *llfi) { struct fuse *f = req_fuse_prepare(req); struct fuse_file_info fi; - struct fuse_dirhandle *dh = get_dirhandle(llfi, &fi); + struct fuse_dh *dh = get_dirhandle(llfi, &fi); pthread_mutex_lock(&dh->lock); /* According to SUS, directory contents need to be refreshed on @@ -1929,21 +2280,23 @@ static void fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, pthread_mutex_unlock(&dh->lock); } -static void fuse_releasedir(fuse_req_t req, fuse_ino_t ino, +static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *llfi) { struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; struct fuse_file_info fi; - struct fuse_dirhandle *dh = get_dirhandle(llfi, &fi); - if (f->op.releasedir) { - char *path; + struct fuse_dh *dh = get_dirhandle(llfi, &fi); + char *path; - pthread_rwlock_rdlock(&f->tree_lock); - path = get_path(f, ino); - fuse_do_releasedir(f, req, path ? path : "-", &fi); + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + fuse_prepare_interrupt(f, req, &d); + fuse_fs_releasedir(f->fs, path ? path : "-", &fi); + fuse_finish_interrupt(f, req, &d); + if (path) free(path); - pthread_rwlock_unlock(&f->tree_lock); - } + pthread_rwlock_unlock(&f->tree_lock); pthread_mutex_lock(&dh->lock); pthread_mutex_unlock(&dh->lock); pthread_mutex_destroy(&dh->lock); @@ -1952,7 +2305,7 @@ static void fuse_releasedir(fuse_req_t req, fuse_ino_t ino, reply_err(req, 0); } -static void fuse_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, +static void fuse_lib_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *llfi) { struct fuse *f = req_fuse_prepare(req); @@ -1966,48 +2319,40 @@ static void fuse_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - err = -ENOSYS; - if (f->op.fsyncdir) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.fsyncdir(path, datasync, &fi); - fuse_finish_interrupt(f, req, &d); - } + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_fsyncdir(f->fs, path, datasync, &fi); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); reply_err(req, err); } -static int default_statfs(struct statvfs *buf) -{ - buf->f_namemax = 255; - buf->f_bsize = 512; - return 0; -} - -static void fuse_statfs(fuse_req_t req, fuse_ino_t ino) +static void fuse_lib_statfs(fuse_req_t req, fuse_ino_t ino) { struct fuse *f = req_fuse_prepare(req); struct statvfs buf; + char *path; int err; memset(&buf, 0, sizeof(buf)); - if (f->op.statfs) { - if (ino && (!f->compat || f->compat >= 26)) { - char *path; - pthread_rwlock_rdlock(&f->tree_lock); - err = -ENOENT; - path = get_path(f, ino); - if (path) { - err = fuse_do_statfs(f, req, path, &buf); - free(path); - } - pthread_rwlock_unlock(&f->tree_lock); - } else - err = fuse_compat_statfs(f, req, &buf); - } else - err = default_statfs(&buf); + pthread_rwlock_rdlock(&f->tree_lock); + if (!ino) { + err = -ENOMEM; + path = strdup("/"); + } else { + err = -ENOENT; + path = get_path(f, ino); + } + if (path) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_statfs(f->fs, path, &buf); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); if (!err) fuse_reply_statfs(req, &buf); @@ -2015,8 +2360,8 @@ static void fuse_statfs(fuse_req_t req, fuse_ino_t ino) reply_err(req, err); } -static void fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, - const char *value, size_t size, int flags) +static void fuse_lib_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags) { struct fuse *f = req_fuse_prepare(req); char *path; @@ -2026,13 +2371,10 @@ static void fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - err = -ENOSYS; - if (f->op.setxattr) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.setxattr(path, name, value, size, flags); - fuse_finish_interrupt(f, req, &d); - } + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_setxattr(f->fs, path, name, value, size, flags); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -2049,21 +2391,18 @@ static int common_getxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - err = -ENOSYS; - if (f->op.getxattr) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.getxattr(path, name, value, size); - fuse_finish_interrupt(f, req, &d); - } + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_getxattr(f->fs, path, name, value, size); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); return err; } -static void fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, - size_t size) +static void fuse_lib_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + size_t size) { struct fuse *f = req_fuse_prepare(req); int res; @@ -2099,20 +2438,17 @@ static int common_listxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - err = -ENOSYS; - if (f->op.listxattr) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.listxattr(path, list, size); - fuse_finish_interrupt(f, req, &d); - } + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_listxattr(f->fs, path, list, size); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); return err; } -static void fuse_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) +static void fuse_lib_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) { struct fuse *f = req_fuse_prepare(req); int res; @@ -2138,7 +2474,8 @@ static void fuse_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) } } -static void fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) +static void fuse_lib_removexattr(fuse_req_t req, fuse_ino_t ino, + const char *name) { struct fuse *f = req_fuse_prepare(req); char *path; @@ -2148,13 +2485,10 @@ static void fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - err = -ENOSYS; - if (f->op.removexattr) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - err = f->op.removexattr(path, name); - fuse_finish_interrupt(f, req, &d); - } + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_removexattr(f->fs, path, name); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -2277,32 +2611,34 @@ static void lock_to_flock(struct lock *lock, struct flock *flock) flock->l_pid = lock->pid; } -static void fuse_flush(fuse_req_t req, fuse_ino_t ino, +static void fuse_lib_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + struct flock lock; + struct lock l; char *path; int err; + int errlock; err = -ENOENT; pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); - if (path != NULL) { - if (f->conf.debug) { - printf("FLUSH[%llu]\n", (unsigned long long) fi->fh); - fflush(stdout); - } - err = -ENOSYS; - if (f->op.flush) - err = fuse_do_flush(f, req, path, fi); + if (path && f->conf.debug) { + printf("FLUSH[%llu]\n", (unsigned long long) fi->fh); + fflush(stdout); } - if (f->op.lock) { - struct flock lock; - struct lock l; - memset(&lock, 0, sizeof(lock)); - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - fuse_do_lock(f, req, path, fi, F_SETLK, &lock); + fuse_prepare_interrupt(f, req, &d); + if (path) + err = fuse_fs_flush(f->fs, path, fi); + + memset(&lock, 0, sizeof(lock)); + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + errlock = fuse_fs_lock(f->fs, path, fi, F_SETLK, &lock); + fuse_finish_interrupt(f, req, &d); + if (errlock != ENOSYS) { flock_to_lock(&lock, &l); l.owner = fi->lock_owner; pthread_mutex_lock(&f->lock); @@ -2313,7 +2649,8 @@ static void fuse_flush(fuse_req_t req, fuse_ino_t ino, if (err == -ENOSYS) err = 0; } - free(path); + if (path) + free(path); pthread_rwlock_unlock(&f->tree_lock); reply_err(req, err); } @@ -2330,15 +2667,18 @@ static int fuse_lock_common(fuse_req_t req, fuse_ino_t ino, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - err = fuse_do_lock(f, req, path, fi, cmd, lock); + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_lock(f->fs, path, fi, cmd, lock); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); return err; } -static void fuse_getlk(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info *fi, struct flock *lock) +static void fuse_lib_getlk(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, struct flock *lock) { int err; struct lock l; @@ -2363,9 +2703,9 @@ static void fuse_getlk(fuse_req_t req, fuse_ino_t ino, reply_err(req, err); } -static void fuse_setlk(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info *fi, struct flock *lock, - int sleep) +static void fuse_lib_setlk(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, struct flock *lock, + int sleep) { int err = fuse_lock_common(req, ino, fi, lock, sleep ? F_SETLKW : F_SETLK); if (!err) { @@ -2380,10 +2720,11 @@ static void fuse_setlk(fuse_req_t req, fuse_ino_t ino, reply_err(req, err); } -static void fuse_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize, - uint64_t idx) +static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize, + uint64_t idx) { struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; char *path; int err; @@ -2391,9 +2732,9 @@ static void fuse_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - err = -ENOSYS; - if (f->op.bmap) - err = f->op.bmap(path, blocksize, &idx); + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_bmap(f->fs, path, blocksize, &idx); + fuse_finish_interrupt(f, req, &d); free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -2404,40 +2745,40 @@ static void fuse_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize, } static struct fuse_lowlevel_ops fuse_path_ops = { - .init = fuse_data_init, - .destroy = fuse_data_destroy, - .lookup = fuse_lookup, - .forget = fuse_forget, - .getattr = fuse_getattr, - .setattr = fuse_setattr, - .access = fuse_access, - .readlink = fuse_readlink, - .mknod = fuse_mknod, - .mkdir = fuse_mkdir, - .unlink = fuse_unlink, - .rmdir = fuse_rmdir, - .symlink = fuse_symlink, - .rename = fuse_rename, - .link = fuse_link, - .create = fuse_create, - .open = fuse_open, - .read = fuse_read, - .write = fuse_write, - .flush = fuse_flush, - .release = fuse_release, - .fsync = fuse_fsync, - .opendir = fuse_opendir, - .readdir = fuse_readdir, - .releasedir = fuse_releasedir, - .fsyncdir = fuse_fsyncdir, - .statfs = fuse_statfs, - .setxattr = fuse_setxattr, - .getxattr = fuse_getxattr, - .listxattr = fuse_listxattr, - .removexattr = fuse_removexattr, - .getlk = fuse_getlk, - .setlk = fuse_setlk, - .bmap = fuse_bmap, + .init = fuse_lib_init, + .destroy = fuse_lib_destroy, + .lookup = fuse_lib_lookup, + .forget = fuse_lib_forget, + .getattr = fuse_lib_getattr, + .setattr = fuse_lib_setattr, + .access = fuse_lib_access, + .readlink = fuse_lib_readlink, + .mknod = fuse_lib_mknod, + .mkdir = fuse_lib_mkdir, + .unlink = fuse_lib_unlink, + .rmdir = fuse_lib_rmdir, + .symlink = fuse_lib_symlink, + .rename = fuse_lib_rename, + .link = fuse_lib_link, + .create = fuse_lib_create, + .open = fuse_lib_open, + .read = fuse_lib_read, + .write = fuse_lib_write, + .flush = fuse_lib_flush, + .release = fuse_lib_release, + .fsync = fuse_lib_fsync, + .opendir = fuse_lib_opendir, + .readdir = fuse_lib_readdir, + .releasedir = fuse_lib_releasedir, + .fsyncdir = fuse_lib_fsyncdir, + .statfs = fuse_lib_statfs, + .setxattr = fuse_lib_setxattr, + .getxattr = fuse_lib_getxattr, + .listxattr = fuse_lib_listxattr, + .removexattr = fuse_lib_removexattr, + .getlk = fuse_lib_getlk, + .setlk = fuse_lib_setlk, + .bmap = fuse_lib_bmap, }; static void free_cmd(struct fuse_cmd *cmd) @@ -2566,6 +2907,7 @@ static const struct fuse_opt fuse_lib_opts[] = { FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0), FUSE_LIB_OPT("intr", intr, 1), FUSE_LIB_OPT("intr_signal=%d", intr_signal, 0), + FUSE_LIB_OPT("modules=%s", modules, 0), FUSE_OPT_END }; @@ -2587,21 +2929,44 @@ static void fuse_lib_help(void) " -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n" " -o intr allow requests to be interrupted\n" " -o intr_signal=NUM signal to send on interrupt (%i)\n" +" -o modules=M1[:M2...] names of modules to push onto filesystem stack\n" "\n", FUSE_DEFAULT_INTR_SIGNAL); } +static void fuse_lib_help_modules(void) +{ + struct fuse_module *m; + fprintf(stderr, "\nModule options:\n"); + pthread_mutex_lock(&fuse_context_lock); + for (m = fuse_modules; m; m = m->next) { + struct fuse_fs *fs = NULL; + struct fuse_fs *newfs; + struct fuse_args args = FUSE_ARGS_INIT(0, NULL); + if (fuse_opt_add_arg(&args, "") != -1 && + fuse_opt_add_arg(&args, "-h") != -1) { + fprintf(stderr, "\n[%s]\n", m->name); + newfs = m->factory(&args, &fs); + assert(newfs == NULL); + } + fuse_opt_free_args(&args); + } + pthread_mutex_unlock(&fuse_context_lock); +} + static int fuse_lib_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) { - (void) data; (void) arg; (void) outargs; + (void) arg; (void) outargs; - if (key == KEY_HELP) + if (key == KEY_HELP) { + struct fuse_config *conf = (struct fuse_config *) data; fuse_lib_help(); + conf->help = 1; + } return 1; } - int fuse_is_lib_option(const char *opt) { return fuse_lowlevel_is_lib_option(opt) || @@ -2642,19 +3007,57 @@ static void fuse_restore_intr_signal(int signum) sigaction(signum, &sa, NULL); } + +static int fuse_push_module(struct fuse *f, const char *module, + struct fuse_args *args) +{ + struct fuse_fs *fs[2] = { f->fs, NULL }; + struct fuse_fs *newfs; + struct fuse_module *m = fuse_get_module(module); + + if (!m) + return -1; + + newfs = m->factory(args, fs); + if (!newfs) { + fuse_put_module(m); + return -1; + } + newfs->m = m; + f->fs = newfs; + return 0; +} + +struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, + void *user_data) +{ + struct fuse_fs *fs; + + if (sizeof(struct fuse_operations) < op_size) { + fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n"); + op_size = sizeof(struct fuse_operations); + } + + fs = (struct fuse_fs *) calloc(1, sizeof(struct fuse_fs)); + if (!fs) { + fprintf(stderr, "fuse: failed to allocate fuse_fs object\n"); + return NULL; + } + + fs->user_data = user_data; + memcpy(&fs->op, op, op_size); + return fs; +} + struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, const struct fuse_operations *op, size_t op_size, void *user_data, int compat) { struct fuse *f; struct node *root; + struct fuse_fs *fs; struct fuse_lowlevel_ops llop = fuse_path_ops; - if (sizeof(struct fuse_operations) < op_size) { - fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n"); - op_size = sizeof(struct fuse_operations); - } - if (fuse_create_context_key() == -1) goto out; @@ -2664,14 +3067,40 @@ struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, goto out_delete_context_key; } - f->user_data = user_data; + fs = fuse_fs_new(op, op_size, user_data); + if (!fs) + goto out_free; + + fs->compat = compat; + f->fs = fs; + + /* Oh f**k, this is ugly! */ + if (!fs->op.lock) { + llop.getlk = NULL; + llop.setlk = NULL; + } + f->conf.entry_timeout = 1.0; f->conf.attr_timeout = 1.0; f->conf.negative_timeout = 0.0; f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL; if (fuse_opt_parse(args, &f->conf, fuse_lib_opts, fuse_lib_opt_proc) == -1) - goto out_free; + goto out_free_fs; + + if (f->conf.modules) { + char *module; + char *next; + + for (module = f->conf.modules; module; module = next) { + char *p; + for (p = module; *p && *p != ':'; p++); + next = *p ? p + 1 : NULL; + *p = '\0'; + if (module[0] && fuse_push_module(f, module, args) == -1) + goto out_free_fs; + } + } if (!f->conf.ac_attr_timeout_set) f->conf.ac_attr_timeout = f->conf.attr_timeout; @@ -2686,18 +3115,15 @@ struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, if (compat && compat <= 25) { if (fuse_sync_compat_args(args) == -1) - goto out_free; - } - - memcpy(&f->op, op, op_size); - if (!f->op.lock) { - llop.getlk = NULL; - llop.setlk = NULL; + goto out_free_fs; } f->se = fuse_lowlevel_new_common(args, &llop, sizeof(llop), f); - if (f->se == NULL) - goto out_free; + if (f->se == NULL) { + if (f->conf.help) + fuse_lib_help_modules(); + goto out_free_fs; + } fuse_session_add_chan(f->se, ch); @@ -2722,7 +3148,6 @@ struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, fuse_mutex_init(&f->lock); pthread_rwlock_init(&f->tree_lock, NULL); - f->compat = compat; root = (struct node *) calloc(1, sizeof(struct node)); if (root == NULL) { @@ -2759,6 +3184,11 @@ struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, free(f->name_table); out_free_session: fuse_session_destroy(f->se); + out_free_fs: + /* Horrible compatibility hack to stop the destructor from being + called on the filesystem without init being called first */ + fs->op.destroy = NULL; + fuse_fs_destroy(f->fs); out_free: free(f); out_delete_context_key: @@ -2777,24 +3207,26 @@ struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args, void fuse_destroy(struct fuse *f) { size_t i; - struct fuse_context_i *c = fuse_get_context_internal(); if (f->conf.intr && f->intr_installed) fuse_restore_intr_signal(f->conf.intr_signal); - memset(c, 0, sizeof(*c)); - c->ctx.fuse = f; - c->ctx.private_data = f->user_data; + if (f->fs) { + struct fuse_context_i *c = fuse_get_context_internal(); - for (i = 0; i < f->id_table_size; i++) { - struct node *node; + memset(c, 0, sizeof(*c)); + c->ctx.fuse = f; + + for (i = 0; i < f->id_table_size; i++) { + struct node *node; - for (node = f->id_table[i]; node != NULL; node = node->id_next) { - if (node->is_hidden) { - char *path = get_path(f, node->nodeid); - if (path) { - f->op.unlink(path); - free(path); + for (node = f->id_table[i]; node != NULL; node = node->id_next) { + if (node->is_hidden) { + char *path = get_path(f, node->nodeid); + if (path) { + fuse_fs_unlink(f->fs, path); + free(path); + } } } } @@ -2817,9 +3249,6 @@ void fuse_destroy(struct fuse *f) fuse_delete_context_key(); } -#include "fuse_common_compat.h" -#include "fuse_compat.h" - static struct fuse *fuse_new_common_compat25(int fd, struct fuse_args *args, const struct fuse_operations *op, size_t op_size, int compat) @@ -2833,113 +3262,18 @@ static struct fuse *fuse_new_common_compat25(int fd, struct fuse_args *args, return f; } -#ifndef __FreeBSD__ - -static int fuse_compat_open(struct fuse *f, fuse_req_t req, char *path, - struct fuse_file_info *fi) -{ - int err; - struct fuse_intr_data d; - if (!f->compat || f->compat >= 25) - err = fuse_do_open(f, req, path, fi); - else if (f->compat == 22) { - struct fuse_file_info_compat tmp; - memcpy(&tmp, fi, sizeof(tmp)); - fuse_prepare_interrupt(f, req, &d); - err = ((struct fuse_operations_compat22 *) &f->op)->open(path, &tmp); - fuse_finish_interrupt(f, req, &d); - memcpy(fi, &tmp, sizeof(tmp)); - fi->fh = tmp.fh; - } else { - fuse_prepare_interrupt(f, req, &d); - err = - ((struct fuse_operations_compat2 *) &f->op)->open(path, fi->flags); - fuse_finish_interrupt(f, req, &d); - } - return err; -} - -static void fuse_compat_release(struct fuse *f, fuse_req_t req, char *path, - struct fuse_file_info *fi) -{ - if (!f->compat || f->compat >= 22) - fuse_do_release(f, req, path ? path : "-", fi); - else if (path) { - struct fuse_intr_data d; - fuse_prepare_interrupt(f, req, &d); - ((struct fuse_operations_compat2 *) &f->op)->release(path, fi->flags); - fuse_finish_interrupt(f, req, &d); - } -} - -static int fuse_compat_opendir(struct fuse *f, fuse_req_t req, char *path, - struct fuse_file_info *fi) -{ - if (!f->compat || f->compat >= 25) { - return fuse_do_opendir(f, req, path, fi); - } else { - int err; - struct fuse_file_info_compat tmp; - struct fuse_intr_data d; - memcpy(&tmp, fi, sizeof(tmp)); - fuse_prepare_interrupt(f, req, &d); - err = ((struct fuse_operations_compat22 *) &f->op)->opendir(path, &tmp); - fuse_finish_interrupt(f, req, &d); - memcpy(fi, &tmp, sizeof(tmp)); - fi->fh = tmp.fh; - return err; - } -} - -static void convert_statfs_compat(struct fuse_statfs_compat1 *compatbuf, - struct statvfs *stbuf) -{ - stbuf->f_bsize = compatbuf->block_size; - stbuf->f_blocks = compatbuf->blocks; - stbuf->f_bfree = compatbuf->blocks_free; - stbuf->f_bavail = compatbuf->blocks_free; - stbuf->f_files = compatbuf->files; - stbuf->f_ffree = compatbuf->files_free; - stbuf->f_namemax = compatbuf->namelen; -} - -static void convert_statfs_old(struct statfs *oldbuf, struct statvfs *stbuf) +/* called with fuse_context_lock held or during initialization (before + main() has been called) */ +void fuse_register_module(struct fuse_module *mod) { - stbuf->f_bsize = oldbuf->f_bsize; - stbuf->f_blocks = oldbuf->f_blocks; - stbuf->f_bfree = oldbuf->f_bfree; - stbuf->f_bavail = oldbuf->f_bavail; - stbuf->f_files = oldbuf->f_files; - stbuf->f_ffree = oldbuf->f_ffree; - stbuf->f_namemax = oldbuf->f_namelen; + mod->so = fuse_current_so; + if (mod->so) + mod->so->ctr++; + mod->next = fuse_modules; + fuse_modules = mod; } -static int fuse_compat_statfs(struct fuse *f, fuse_req_t req, - struct statvfs *buf) -{ - int err; - struct fuse_intr_data d; - - if (!f->compat || f->compat >= 25) { - err = fuse_do_statfs(f, req, "/", buf); - } else if (f->compat > 11) { - struct statfs oldbuf; - fuse_prepare_interrupt(f, req, &d); - err = ((struct fuse_operations_compat22 *) &f->op)->statfs("/", &oldbuf); - fuse_finish_interrupt(f, req, &d); - if (!err) - convert_statfs_old(&oldbuf, buf); - } else { - struct fuse_statfs_compat1 compatbuf; - memset(&compatbuf, 0, sizeof(struct fuse_statfs_compat1)); - fuse_prepare_interrupt(f, req, &d); - err = ((struct fuse_operations_compat1 *) &f->op)->statfs(&compatbuf); - fuse_finish_interrupt(f, req, &d); - if (!err) - convert_statfs_compat(&compatbuf, buf); - } - return err; -} +#ifndef __FreeBSD__ static struct fuse *fuse_new_common_compat(int fd, const char *opts, const struct fuse_operations *op, @@ -2994,31 +3328,6 @@ __asm__(".symver fuse_set_getcontext_func,__fuse_set_getcontext_func@"); __asm__(".symver fuse_new_compat2,fuse_new@"); __asm__(".symver fuse_new_compat22,fuse_new@FUSE_2.2"); -#else /* __FreeBSD__ */ - -static int fuse_compat_open(struct fuse *f, fuse_req_t req, char *path, - struct fuse_file_info *fi) -{ - return fuse_do_open(f, req, path, fi); -} - -static void fuse_compat_release(struct fuse *f, fuse_req_t req, char *path, - struct fuse_file_info *fi) -{ - fuse_do_release(f, req, path ? path : "-", fi); -} - -static int fuse_compat_opendir(struct fuse *f, fuse_req_t req, char *path, - struct fuse_file_info *fi) -{ - return fuse_do_opendir(f, req, path, fi); -} - -static int fuse_compat_statfs(struct fuse *f, fuse_req_t req, struct statvfs *buf) -{ - return fuse_do_statfs(f, req, "/", buf); -} - #endif /* __FreeBSD__ */ struct fuse *fuse_new_compat25(int fd, struct fuse_args *args, diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index 682c503..28468df 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -75,11 +75,9 @@ static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr) attr->atime = stbuf->st_atime; attr->mtime = stbuf->st_mtime; attr->ctime = stbuf->st_ctime; -#ifdef FUSE_STAT_HAS_NANOSEC - attr->atimensec = ST_ATIM(stbuf).tv_nsec; - attr->mtimensec = ST_MTIM(stbuf).tv_nsec; - attr->ctimensec = ST_CTIM(stbuf).tv_nsec; -#endif + attr->atimensec = ST_ATIM_NSEC(stbuf); + attr->mtimensec = ST_MTIM_NSEC(stbuf); + attr->ctimensec = ST_CTIM_NSEC(stbuf); } static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf) @@ -90,10 +88,8 @@ static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf) stbuf->st_size = attr->size; stbuf->st_atime = attr->atime; stbuf->st_mtime = attr->mtime; -#ifdef FUSE_STAT_HAS_NANOSEC - ST_ATIM(stbuf).tv_nsec = attr->atimensec; - ST_MTIM(stbuf).tv_nsec = attr->mtimensec; -#endif + ST_ATIM_NSEC_SET(stbuf, attr->atimensec); + ST_MTIM_NSEC_SET(stbuf, attr->mtimensec); } static size_t iov_length(const struct iovec *iov, size_t count) diff --git a/lib/fuse_misc.h b/lib/fuse_misc.h index 57a1e37..0fabfe4 100644 --- a/lib/fuse_misc.h +++ b/lib/fuse_misc.h @@ -24,14 +24,22 @@ static inline void fuse_mutex_init(pthread_mutex_t *mut) #ifdef HAVE_STRUCT_STAT_ST_ATIM /* Linux */ -#define ST_ATIM(stbuf) (stbuf)->st_atim -#define ST_CTIM(stbuf) (stbuf)->st_ctim -#define ST_MTIM(stbuf) (stbuf)->st_mtim -#define FUSE_STAT_HAS_NANOSEC 1 +#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec) +#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec) +#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec) +#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val) +#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val) #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC) /* FreeBSD */ -#define ST_ATIM(stbuf) (stbuf)->st_atimespec -#define ST_CTIM(stbuf) (stbuf)->st_ctimespec -#define ST_MTIM(stbuf) (stbuf)->st_mtimespec -#define FUSE_STAT_HAS_NANOSEC 1 +#define ST_ATIM(stbuf) ((stbuf)->st_atimespec.tv_nsec) +#define ST_CTIM(stbuf) ((stbuf)->st_ctimespec.tv_nsec) +#define ST_MTIM(stbuf) ((stbuf)->st_mtimespec.tv_nsec) +#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val) +#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val) +#else +#define ST_ATIM_NSEC(stbuf) 0 +#define ST_CTIM_NSEC(stbuf) 0 +#define ST_MTIM_NSEC(stbuf) 0 +#define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0) +#define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0) #endif diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript index df0c7d1..cbce074 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -108,7 +108,49 @@ FUSE_2.6 { fuse_teardown_compat22; fuse_unmount; fuse_unmount_compat22; +} FUSE_2.5; + +FUSE_2.7 { + global: + fuse_fs_access; + fuse_fs_bmap; + fuse_fs_chmod; + fuse_fs_chown; + fuse_fs_create; + fuse_fs_destroy; + fuse_fs_fgetattr; + fuse_fs_flush; + fuse_fs_fsync; + fuse_fs_fsyncdir; + fuse_fs_ftruncate; + fuse_fs_getattr; + fuse_fs_getxattr; + fuse_fs_init; + fuse_fs_link; + fuse_fs_listxattr; + fuse_fs_lock; + fuse_fs_mkdir; + fuse_fs_mknod; + fuse_fs_new; + fuse_fs_open; + fuse_fs_opendir; + fuse_fs_read; + fuse_fs_readdir; + fuse_fs_readlink; + fuse_fs_release; + fuse_fs_releasedir; + fuse_fs_removexattr; + fuse_fs_rename; + fuse_fs_rmdir; + fuse_fs_setxattr; + fuse_fs_statfs; + fuse_fs_symlink; + fuse_fs_truncate; + fuse_fs_unlink; + fuse_fs_utimens; + fuse_fs_write; + fuse_register_module; local: - *; -} FUSE_2.5; + *; +} FUSE_2.6; diff --git a/lib/modules/iconv.c b/lib/modules/iconv.c new file mode 100644 index 0000000..eec0205 --- /dev/null +++ b/lib/modules/iconv.c @@ -0,0 +1,701 @@ +#define FUSE_USE_VERSION 26 +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct iconv { + struct fuse_fs *next; + pthread_mutex_t lock; + char *from_code; + char *to_code; + iconv_t tofs; + iconv_t fromfs; +}; + +struct iconv_dh { + struct iconv *ic; + void *prev_buf; + fuse_fill_dir_t prev_filler; +}; + +static struct iconv *iconv_get(void) +{ + return fuse_get_context()->private_data; +} + +static int iconv_convpath(struct iconv *ic, const char *path, char **newpathp, + int fromfs) +{ + size_t pathlen = strlen(path); + size_t newpathlen = pathlen; /* FIXME after testing */ + char *newpath = malloc(newpathlen + 1); + size_t plen = newpathlen; + char *p = newpath; + size_t res; + int err; + + if (!newpath) + return -ENOMEM; + + pthread_mutex_lock(&ic->lock); + do { + res = iconv(fromfs ? ic->fromfs : ic->tofs, (char **) &path, &pathlen, + &p, &plen); + if (res == (size_t) -1) { + char *tmp; + size_t inc; + + err = -EILSEQ; + if (errno != E2BIG) + goto err; + + inc = (pathlen + 1) * 4; + newpathlen += inc; + tmp = realloc(newpath, newpathlen + 1); + err = -ENOMEM; + if (!tmp) + goto err; + + p = tmp + (p - newpath); + plen += inc; + newpath = tmp; + } + } while (res == (size_t) -1); + pthread_mutex_unlock(&ic->lock); + *p = '\0'; + *newpathp = newpath; + return 0; + + err: + iconv(fromfs ? ic->fromfs : ic->tofs, NULL, NULL, NULL, NULL); + pthread_mutex_unlock(&ic->lock); + free(newpath); + return err; +} + +static int iconv_getattr(const char *path, struct stat *stbuf) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_getattr(ic->next, newpath, stbuf); + free(newpath); + } + return err; +} + +static int iconv_fgetattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_fgetattr(ic->next, newpath, stbuf, fi); + free(newpath); + } + return err; +} + +static int iconv_access(const char *path, int mask) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_access(ic->next, newpath, mask); + free(newpath); + } + return err; +} + +static int iconv_readlink(const char *path, char *buf, size_t size) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_readlink(ic->next, newpath, buf, size); + if (!err) { + char *newlink; + err = iconv_convpath(ic, buf, &newlink, 1); + if (!err) { + strncpy(buf, newlink, size - 1); + buf[size - 1] = '\0'; + free(newlink); + } + } + free(newpath); + } + return err; +} + +static int iconv_opendir(const char *path, struct fuse_file_info *fi) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_opendir(ic->next, newpath, fi); + free(newpath); + } + return err; +} + +static int iconv_dir_fill(void *buf, const char *name, + const struct stat *stbuf, off_t off) +{ + struct iconv_dh *dh = buf; + char *newname; + int res = 0; + if (iconv_convpath(dh->ic, name, &newname, 1) == 0) { + res = dh->prev_filler(dh->prev_buf, newname, stbuf, off); + free(newname); + } + return res; +} + +static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + struct iconv_dh dh; + dh.ic = ic; + dh.prev_buf = buf; + dh.prev_filler = filler; + err = fuse_fs_readdir(ic->next, newpath, &dh, iconv_dir_fill, offset, + fi); + free(newpath); + } + return err; +} + +static int iconv_releasedir(const char *path, struct fuse_file_info *fi) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_releasedir(ic->next, newpath, fi); + free(newpath); + } + return err; +} + +static int iconv_mknod(const char *path, mode_t mode, dev_t rdev) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_mknod(ic->next, newpath, mode, rdev); + free(newpath); + } + return err; +} + +static int iconv_mkdir(const char *path, mode_t mode) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_mkdir(ic->next, newpath, mode); + free(newpath); + } + return err; +} + +static int iconv_unlink(const char *path) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_unlink(ic->next, newpath); + free(newpath); + } + return err; +} + +static int iconv_rmdir(const char *path) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_rmdir(ic->next, newpath); + free(newpath); + } + return err; +} + +static int iconv_symlink(const char *from, const char *to) +{ + struct iconv *ic = iconv_get(); + char *newfrom; + char *newto; + int err = iconv_convpath(ic, from, &newfrom, 0); + if (!err) { + err = iconv_convpath(ic, to, &newto, 0); + if (!err) { + err = fuse_fs_symlink(ic->next, newfrom, newto); + free(newto); + } + free(newfrom); + } + return err; +} + +static int iconv_rename(const char *from, const char *to) +{ + struct iconv *ic = iconv_get(); + char *newfrom; + char *newto; + int err = iconv_convpath(ic, from, &newfrom, 0); + if (!err) { + err = iconv_convpath(ic, to, &newto, 0); + if (!err) { + err = fuse_fs_rename(ic->next, newfrom, newto); + free(newto); + } + free(newfrom); + } + return err; +} + +static int iconv_link(const char *from, const char *to) +{ + struct iconv *ic = iconv_get(); + char *newfrom; + char *newto; + int err = iconv_convpath(ic, from, &newfrom, 0); + if (!err) { + err = iconv_convpath(ic, to, &newto, 0); + if (!err) { + err = fuse_fs_link(ic->next, newfrom, newto); + free(newto); + } + free(newfrom); + } + return err; +} + +static int iconv_chmod(const char *path, mode_t mode) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_chmod(ic->next, newpath, mode); + free(newpath); + } + return err; +} + +static int iconv_chown(const char *path, uid_t uid, gid_t gid) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_chown(ic->next, newpath, uid, gid); + free(newpath); + } + return err; +} + +static int iconv_truncate(const char *path, off_t size) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_truncate(ic->next, newpath, size); + free(newpath); + } + return err; +} + +static int iconv_ftruncate(const char *path, off_t size, + struct fuse_file_info *fi) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_ftruncate(ic->next, newpath, size, fi); + free(newpath); + } + return err; +} + +static int iconv_utimens(const char *path, const struct timespec ts[2]) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_utimens(ic->next, newpath, ts); + free(newpath); + } + return err; +} + +static int iconv_create(const char *path, mode_t mode, + struct fuse_file_info *fi) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_create(ic->next, newpath, mode, fi); + free(newpath); + } + return err; +} + +static int iconv_open_file(const char *path, struct fuse_file_info *fi) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_open(ic->next, newpath, fi); + free(newpath); + } + return err; +} + +static int iconv_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_read(ic->next, newpath, buf, size, offset, fi); + free(newpath); + } + return err; +} + +static int iconv_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_write(ic->next, newpath, buf, size, offset, fi); + free(newpath); + } + return err; +} + +static int iconv_statfs(const char *path, struct statvfs *stbuf) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_statfs(ic->next, newpath, stbuf); + free(newpath); + } + return err; +} + +static int iconv_flush(const char *path, struct fuse_file_info *fi) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_flush(ic->next, newpath, fi); + free(newpath); + } + return err; +} + +static int iconv_release(const char *path, struct fuse_file_info *fi) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_release(ic->next, newpath, fi); + free(newpath); + } + return err; +} + +static int iconv_fsync(const char *path, int isdatasync, + struct fuse_file_info *fi) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_fsync(ic->next, newpath, isdatasync, fi); + free(newpath); + } + return err; +} + +static int iconv_fsyncdir(const char *path, int isdatasync, + struct fuse_file_info *fi) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_fsyncdir(ic->next, newpath, isdatasync, fi); + free(newpath); + } + return err; +} + +static int iconv_setxattr(const char *path, const char *name, + const char *value, size_t size, int flags) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_setxattr(ic->next, newpath, name, value, size, flags); + free(newpath); + } + return err; +} + +static int iconv_getxattr(const char *path, const char *name, char *value, + size_t size) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_getxattr(ic->next, newpath, name, value, size); + free(newpath); + } + return err; +} + +static int iconv_listxattr(const char *path, char *list, size_t size) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_listxattr(ic->next, newpath, list, size); + free(newpath); + } + return err; +} + +static int iconv_removexattr(const char *path, const char *name) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_removexattr(ic->next, newpath, name); + free(newpath); + } + return err; +} + +static int iconv_lock(const char *path, struct fuse_file_info *fi, int cmd, + struct flock *lock) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_lock(ic->next, newpath, fi, cmd, lock); + free(newpath); + } + return err; +} + +static int iconv_bmap(const char *path, size_t blocksize, uint64_t *idx) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_bmap(ic->next, newpath, blocksize, idx); + free(newpath); + } + return err; +} + +static void *iconv_init(struct fuse_conn_info *conn) +{ + struct iconv *ic = iconv_get(); + fuse_fs_init(ic->next, conn); + return ic; +} + +static void iconv_destroy(void *data) +{ + struct iconv *ic = data; + fuse_fs_destroy(ic->next); + iconv_close(ic->tofs); + iconv_close(ic->fromfs); + pthread_mutex_destroy(&ic->lock); + free(ic->from_code); + free(ic->to_code); + free(ic); +} + +static struct fuse_operations iconv_oper = { + .destroy = iconv_destroy, + .init = iconv_init, + .getattr = iconv_getattr, + .fgetattr = iconv_fgetattr, + .access = iconv_access, + .readlink = iconv_readlink, + .opendir = iconv_opendir, + .readdir = iconv_readdir, + .releasedir = iconv_releasedir, + .mknod = iconv_mknod, + .mkdir = iconv_mkdir, + .symlink = iconv_symlink, + .unlink = iconv_unlink, + .rmdir = iconv_rmdir, + .rename = iconv_rename, + .link = iconv_link, + .chmod = iconv_chmod, + .chown = iconv_chown, + .truncate = iconv_truncate, + .ftruncate = iconv_ftruncate, + .utimens = iconv_utimens, + .create = iconv_create, + .open = iconv_open_file, + .read = iconv_read, + .write = iconv_write, + .statfs = iconv_statfs, + .flush = iconv_flush, + .release = iconv_release, + .fsync = iconv_fsync, + .fsyncdir = iconv_fsyncdir, + .setxattr = iconv_setxattr, + .getxattr = iconv_getxattr, + .listxattr = iconv_listxattr, + .removexattr= iconv_removexattr, + .lock = iconv_lock, + .bmap = iconv_bmap, +}; + +static struct fuse_opt iconv_opts[] = { + FUSE_OPT_KEY("-h", 0), + FUSE_OPT_KEY("--help", 0), + { "from_code=%s", offsetof(struct iconv, from_code), 0 }, + { "to_code=%s", offsetof(struct iconv, to_code), 1 }, + FUSE_OPT_END +}; + +static void iconv_help(void) +{ + char *old = strdup(setlocale(LC_CTYPE, "")); + char *charmap = strdup(nl_langinfo(CODESET)); + setlocale(LC_CTYPE, old); + free(old); + fprintf(stderr, +" -o from_code=CHARSET original encoding of file names (default: UTF-8)\n" +" -o to_code=CHARSET new encoding of the file names (default: %s)\n", + charmap); + free(charmap); +} + +static int iconv_opt_proc(void *data, const char *arg, int key, + struct fuse_args *outargs) +{ + (void) data; (void) arg; (void) outargs; + + if (!key) { + iconv_help(); + return -1; + } + + return 1; +} + +static struct fuse_fs *iconv_new(struct fuse_args *args, + struct fuse_fs *next[]) +{ + struct fuse_fs *fs; + struct iconv *ic; + char *old = NULL; + const char *from; + const char *to; + + ic = calloc(1, sizeof(struct iconv)); + if (ic == NULL) { + fprintf(stderr, "fuse-iconv: memory allocation failed\n"); + return NULL; + } + + if (fuse_opt_parse(args, ic, iconv_opts, iconv_opt_proc) == -1) + goto out_free; + + if (!next[0] || next[1]) { + fprintf(stderr, "fuse-iconv: exactly one next filesystem required\n"); + goto out_free; + } + + from = ic->from_code ? ic->from_code : "UTF-8"; + to = ic->to_code ? ic->to_code : ""; + /* FIXME: detect charset equivalence? */ + if (!to[0]) + old = strdup(setlocale(LC_CTYPE, "")); + ic->tofs = iconv_open(from, to); + if (ic->tofs == (iconv_t) -1) { + fprintf(stderr, "fuse-iconv: cannot convert from %s to %s\n", + to, from); + goto out_free; + } + ic->fromfs = iconv_open(to, from); + if (ic->tofs == (iconv_t) -1) { + fprintf(stderr, "fuse-iconv: cannot convert from %s to %s\n", + from, to); + goto out_iconv_close_to; + } + if (old) { + setlocale(LC_CTYPE, old); + free(old); + } + + ic->next = next[0]; + fs = fuse_fs_new(&iconv_oper, sizeof(iconv_oper), ic); + if (!fs) + goto out_iconv_close_from; + + return fs; + + out_iconv_close_from: + iconv_close(ic->fromfs); + out_iconv_close_to: + iconv_close(ic->tofs); + out_free: + free(ic->from_code); + free(ic->to_code); + free(ic); + return NULL; +} + +FUSE_REGISTER_MODULE(iconv, iconv_new); diff --git a/lib/modules/subdir.c b/lib/modules/subdir.c new file mode 100644 index 0000000..eaba99d --- /dev/null +++ b/lib/modules/subdir.c @@ -0,0 +1,655 @@ +#define FUSE_USE_VERSION 26 +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include +#include + +struct subdir { + char *base; + size_t baselen; + int rellinks; + struct fuse_fs *next; +}; + +static struct subdir *subdir_get(void) +{ + return fuse_get_context()->private_data; +} + +static char *subdir_addpath(struct subdir *d, const char *path) +{ + unsigned newlen = d->baselen + strlen(path); + char *newpath = malloc(newlen + 2); + if (newpath) { + if (path[0] == '/') + path++; + strcpy(newpath, d->base); + strcpy(newpath + d->baselen, path); + if (!newpath[0]) + strcpy(newpath, "."); + } + return newpath; +} + +static int subdir_getattr(const char *path, struct stat *stbuf) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_getattr(d->next, newpath, stbuf); + free(newpath); + } + return err; +} + +static int subdir_fgetattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_fgetattr(d->next, newpath, stbuf, fi); + free(newpath); + } + return err; +} + +static int subdir_access(const char *path, int mask) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_access(d->next, newpath, mask); + free(newpath); + } + return err; +} + + +static int count_components(const char *p) +{ + int ctr; + + for (; *p == '/'; p++); + for (ctr = 0; *p; ctr++) { + for (; *p && *p != '/'; p++); + for (; *p == '/'; p++); + } + return ctr; +} + +static void strip_common(const char **sp, const char **tp) +{ + const char *s = *sp; + const char *t = *tp; + do { + for (; *s == '/'; s++); + for (; *t == '/'; t++); + *tp = t; + *sp = s; + for (; *s == *t && *s && *s != '/'; s++, t++); + } while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t)); +} + +static void transform_symlink(struct subdir *d, const char *path, + char *buf, size_t size) +{ + const char *l = buf; + size_t llen; + char *s; + int dotdots; + int i; + + if (l[0] != '/' || d->base[0] != '/') + return; + + strip_common(&l, &path); + if (l - buf < d->baselen) + return; + + dotdots = count_components(path); + if (!dotdots) + return; + dotdots--; + + llen = strlen(l); + if (dotdots * 3 + llen + 2 > size) + return; + + s = buf + dotdots * 3; + if (llen) + memmove(s, l, llen + 1); + else if (!dotdots) + strcpy(s, "."); + else + *s = '\0'; + + for (s = buf, i = 0; i < dotdots; i++, s += 3) + memcpy(s, "../", 3); +} + + +static int subdir_readlink(const char *path, char *buf, size_t size) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_readlink(d->next, newpath, buf, size); + if (!err && d->rellinks) + transform_symlink(d, newpath, buf, size); + free(newpath); + } + return err; +} + +static int subdir_opendir(const char *path, struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_opendir(d->next, newpath, fi); + free(newpath); + } + return err; +} + +static int subdir_readdir(const char *path, void *buf, + fuse_fill_dir_t filler, off_t offset, + struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_readdir(d->next, newpath, buf, filler, offset, fi); + free(newpath); + } + return err; +} + +static int subdir_releasedir(const char *path, struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_releasedir(d->next, newpath, fi); + free(newpath); + } + return err; +} + +static int subdir_mknod(const char *path, mode_t mode, dev_t rdev) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_mknod(d->next, newpath, mode, rdev); + free(newpath); + } + return err; +} + +static int subdir_mkdir(const char *path, mode_t mode) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_mkdir(d->next, newpath, mode); + free(newpath); + } + return err; +} + +static int subdir_unlink(const char *path) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_unlink(d->next, newpath); + free(newpath); + } + return err; +} + +static int subdir_rmdir(const char *path) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_rmdir(d->next, newpath); + free(newpath); + } + return err; +} + +static int subdir_symlink(const char *from, const char *path) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_symlink(d->next, from, newpath); + free(newpath); + } + return err; +} + +static int subdir_rename(const char *from, const char *to) +{ + struct subdir *d = subdir_get(); + char *newfrom = subdir_addpath(d, from); + char *newto = subdir_addpath(d, to); + int err = -ENOMEM; + if (newfrom && newto) + err = fuse_fs_rename(d->next, newfrom, newto); + free(newfrom); + free(newto); + return err; +} + +static int subdir_link(const char *from, const char *to) +{ + struct subdir *d = subdir_get(); + char *newfrom = subdir_addpath(d, from); + char *newto = subdir_addpath(d, to); + int err = -ENOMEM; + if (newfrom && newto) + err = fuse_fs_link(d->next, newfrom, newto); + free(newfrom); + free(newto); + return err; +} + +static int subdir_chmod(const char *path, mode_t mode) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_chmod(d->next, newpath, mode); + free(newpath); + } + return err; +} + +static int subdir_chown(const char *path, uid_t uid, gid_t gid) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_chown(d->next, newpath, uid, gid); + free(newpath); + } + return err; +} + +static int subdir_truncate(const char *path, off_t size) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_truncate(d->next, newpath, size); + free(newpath); + } + return err; +} + +static int subdir_ftruncate(const char *path, off_t size, + struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_ftruncate(d->next, newpath, size, fi); + free(newpath); + } + return err; +} + +static int subdir_utimens(const char *path, const struct timespec ts[2]) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_utimens(d->next, newpath, ts); + free(newpath); + } + return err; +} + +static int subdir_create(const char *path, mode_t mode, struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_create(d->next, newpath, mode, fi); + free(newpath); + } + return err; +} + +static int subdir_open(const char *path, struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_open(d->next, newpath, fi); + free(newpath); + } + return err; +} + +static int subdir_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_read(d->next, newpath, buf, size, offset, fi); + free(newpath); + } + return err; +} + +static int subdir_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_write(d->next, newpath, buf, size, offset, fi); + free(newpath); + } + return err; +} + +static int subdir_statfs(const char *path, struct statvfs *stbuf) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_statfs(d->next, newpath, stbuf); + free(newpath); + } + return err; +} + +static int subdir_flush(const char *path, struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_flush(d->next, newpath, fi); + free(newpath); + } + return err; +} + +static int subdir_release(const char *path, struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_release(d->next, newpath, fi); + free(newpath); + } + return err; +} + +static int subdir_fsync(const char *path, int isdatasync, + struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_fsync(d->next, newpath, isdatasync, fi); + free(newpath); + } + return err; +} + +static int subdir_fsyncdir(const char *path, int isdatasync, + struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_fsyncdir(d->next, newpath, isdatasync, fi); + free(newpath); + } + return err; +} + +static int subdir_setxattr(const char *path, const char *name, const char *value, + size_t size, int flags) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_setxattr(d->next, newpath, name, value, size, flags); + free(newpath); + } + return err; +} + +static int subdir_getxattr(const char *path, const char *name, char *value, + size_t size) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_getxattr(d->next, newpath, name, value, size); + free(newpath); + } + return err; +} + +static int subdir_listxattr(const char *path, char *list, size_t size) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_listxattr(d->next, newpath, list, size); + free(newpath); + } + return err; +} + +static int subdir_removexattr(const char *path, const char *name) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_removexattr(d->next, newpath, name); + free(newpath); + } + return err; +} + +static int subdir_lock(const char *path, struct fuse_file_info *fi, int cmd, + struct flock *lock) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_lock(d->next, newpath, fi, cmd, lock); + free(newpath); + } + return err; +} + +static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx) +{ + struct subdir *d = subdir_get(); + char *newpath = subdir_addpath(d, path); + int err = -ENOMEM; + if (newpath) { + err = fuse_fs_bmap(d->next, newpath, blocksize, idx); + free(newpath); + } + return err; +} + +static void *subdir_init(struct fuse_conn_info *conn) +{ + struct subdir *d = subdir_get(); + fuse_fs_init(d->next, conn); + return d; +} + +static void subdir_destroy(void *data) +{ + struct subdir *d = data; + fuse_fs_destroy(d->next); + free(d->base); + free(d); +} + +static struct fuse_operations subdir_oper = { + .destroy = subdir_destroy, + .init = subdir_init, + .getattr = subdir_getattr, + .fgetattr = subdir_fgetattr, + .access = subdir_access, + .readlink = subdir_readlink, + .opendir = subdir_opendir, + .readdir = subdir_readdir, + .releasedir = subdir_releasedir, + .mknod = subdir_mknod, + .mkdir = subdir_mkdir, + .symlink = subdir_symlink, + .unlink = subdir_unlink, + .rmdir = subdir_rmdir, + .rename = subdir_rename, + .link = subdir_link, + .chmod = subdir_chmod, + .chown = subdir_chown, + .truncate = subdir_truncate, + .ftruncate = subdir_ftruncate, + .utimens = subdir_utimens, + .create = subdir_create, + .open = subdir_open, + .read = subdir_read, + .write = subdir_write, + .statfs = subdir_statfs, + .flush = subdir_flush, + .release = subdir_release, + .fsync = subdir_fsync, + .fsyncdir = subdir_fsyncdir, + .setxattr = subdir_setxattr, + .getxattr = subdir_getxattr, + .listxattr = subdir_listxattr, + .removexattr= subdir_removexattr, + .lock = subdir_lock, + .bmap = subdir_bmap, +}; + +static struct fuse_opt subdir_opts[] = { + FUSE_OPT_KEY("-h", 0), + FUSE_OPT_KEY("--help", 0), + { "subdir=%s", offsetof(struct subdir, base), 0 }, + { "rellinks", offsetof(struct subdir, rellinks), 1 }, + { "norellinks", offsetof(struct subdir, rellinks), 0 }, + FUSE_OPT_END +}; + +static void subdir_help(void) +{ + fprintf(stderr, +" -o subdir=DIR prepend this directory to all paths (mandatory)\n" +" -o [no]rellinks transform absolute symlinks to relative\n"); +} + +static int subdir_opt_proc(void *data, const char *arg, int key, + struct fuse_args *outargs) +{ + (void) data; (void) arg; (void) outargs; + + if (!key) { + subdir_help(); + return -1; + } + + return 1; +} + +static struct fuse_fs *subdir_new(struct fuse_args *args, + struct fuse_fs *next[]) +{ + struct fuse_fs *fs; + struct subdir *d; + + d = calloc(1, sizeof(struct subdir)); + if (d == NULL) { + fprintf(stderr, "fuse-subdir: memory allocation failed\n"); + return NULL; + } + + if (fuse_opt_parse(args, d, subdir_opts, subdir_opt_proc) == -1) + goto out_free; + + if (!next[0] || next[1]) { + fprintf(stderr, "fuse-subdir: exactly one next filesystem required\n"); + goto out_free; + } + + if (!d->base) { + fprintf(stderr, "fuse-subdir: missing 'subdir' option\n"); + goto out_free; + } + + if (d->base[0] && d->base[strlen(d->base)-1] != '/') { + char *tmp = realloc(d->base, strlen(d->base) + 2); + if (!tmp) { + fprintf(stderr, "fuse-subdir: memory allocation failed\n"); + goto out_free; + } + d->base = tmp; + strcat(d->base, "/"); + } + d->baselen = strlen(d->base); + d->next = next[0]; + fs = fuse_fs_new(&subdir_oper, sizeof(subdir_oper), d); + if (!fs) + goto out_free; + return fs; + + out_free: + free(d->base); + free(d); + return NULL; +} + +FUSE_REGISTER_MODULE(subdir, subdir_new); -- 2.30.2