From 288ed4ebcea335c77793ee3d207c7466d55c4f71 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 7 Sep 2006 06:02:44 +0000 Subject: [PATCH] interrupt support --- ChangeLog | 14 + include/fuse.h | 14 +- include/fuse_lowlevel.h | 8 + lib/fuse.c | 704 ++++++++++++++++++++++++++++++++-------- lib/fuse_i.h | 1 + lib/fuse_loop_mt.c | 23 +- lib/fuse_lowlevel.c | 63 +++- lib/fuse_mt.c | 73 +---- lib/fuse_signals.c | 4 +- lib/fuse_versionscript | 6 +- 10 files changed, 672 insertions(+), 238 deletions(-) diff --git a/ChangeLog b/ChangeLog index d35312f..7392a5e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2006-09-07 Miklos Szeredi + + * lib: Add interrupt support to high level library, which may be + enabled with the 'intr' mount option. + + * When an operation is interrupted the thread handling that + operation will receive SIGUSR1 (or other signal specified with the + 'intr_signal=N' option). The library installs a no-op signal + handler for this signal, unless there's already a handler + installed. + + * The filesystem may query interrupt status (regardless of 'intr') + with the fuse_interrupted() function. + 2006-09-03 Miklos Szeredi * lib: Multithreaded loop now allows unlimited number of threads. diff --git a/include/fuse.h b/include/fuse.h index 8a18914..b05075d 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -513,11 +513,18 @@ int fuse_loop_mt(struct fuse *f); * The context is only valid for the duration of a filesystem * operation, and thus must not be stored and used later. * - * @param f the FUSE handle * @return the context */ struct fuse_context *fuse_get_context(void); +/** + * Check if a request has already been interrupted + * + * @param req request handle + * @return 1 if the request has been interrupted, 0 otherwise + */ +int fuse_interrupted(void); + /** * Obsolete, doesn't do anything * @@ -540,6 +547,9 @@ int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, * Advanced API for event handling, don't worry about this... * * ----------------------------------------------------------- */ +/* NOTE: the following functions are deprecated, and will be removed + from the 3.0 API. Use the lowlevel session functions instead */ + /** Function type used to process commands */ typedef void (*fuse_processor_t)(struct fuse *, struct fuse_cmd *, void *); @@ -566,7 +576,7 @@ int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data); called */ int fuse_exited(struct fuse *f); -/** Set function which can be used to get the current context */ +/** This function is obsolete and implemented as a no-op */ void fuse_set_getcontext_func(struct fuse_context *(*func)(void)); /** Get session from fuse object */ diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index a676830..58f568c 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -1006,6 +1006,14 @@ typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data); void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data); +/** + * Check if a request has already been interrupted + * + * @param req request handle + * @return 1 if the request has been interrupted, 0 otherwise + */ +int fuse_req_interrupted(fuse_req_t req); + /* ----------------------------------------------------------- * * Filesystem setup * * ----------------------------------------------------------- */ diff --git a/lib/fuse.c b/lib/fuse.c index cb492d3..434e40c 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -24,11 +24,14 @@ #include #include #include +#include #include #include #include +#include #define FUSE_MAX_PATH 4096 +#define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1 #define FUSE_UNKNOWN_INO 0xffffffff @@ -51,6 +54,8 @@ struct fuse_config { int direct_io; int kernel_cache; int auto_cache; + int intr; + int intr_signal; }; struct fuse { @@ -68,6 +73,7 @@ struct fuse { pthread_rwlock_t tree_lock; void *user_data; struct fuse_config conf; + int intr_installed; }; struct node { @@ -102,12 +108,22 @@ struct fuse_dirhandle { fuse_ino_t nodeid; }; -static struct fuse_context *(*fuse_getcontext)(void) = NULL; +struct fuse_context_i { + struct fuse_context ctx; + fuse_req_t req; +}; + +static pthread_key_t fuse_context_key; +static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER; +static int fuse_context_ref; -static int fuse_do_open(struct fuse *, char *, struct fuse_file_info *); -static void fuse_do_release(struct fuse *, char *, struct fuse_file_info *); -static int fuse_do_opendir(struct fuse *, char *, struct fuse_file_info *); -static int fuse_do_statfs(struct fuse *, struct statvfs *); +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 struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid) { @@ -410,6 +426,195 @@ static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf) stbuf->st_gid = f->conf.gid; } +static struct fuse *req_fuse(fuse_req_t req) +{ + return (struct fuse *) fuse_req_userdata(req); +} + +static void fuse_intr_sighandler(int sig) +{ + (void) sig; + /* Nothing to do */ +} + +struct fuse_intr_data { + pthread_t id; + pthread_cond_t cond; + int finished; +}; + +static void fuse_interrupt(fuse_req_t req, void *d_) +{ + struct fuse_intr_data *d = d_; + struct fuse *f = req_fuse(req); + + if (d->id == pthread_self()) + return; + + pthread_mutex_lock(&f->lock); + while (!d->finished) { + struct timeval now; + struct timespec timeout; + + pthread_kill(d->id, f->conf.intr_signal); + gettimeofday(&now, NULL); + timeout.tv_sec = now.tv_sec + 1; + timeout.tv_nsec = now.tv_usec * 1000; + pthread_cond_timedwait(&d->cond, &f->lock, &timeout); + } + pthread_mutex_unlock(&f->lock); +} + +static void fuse_do_finish_interrupt(struct fuse *f, fuse_req_t req, + struct fuse_intr_data *d) +{ + pthread_mutex_lock(&f->lock); + d->finished = 1; + pthread_cond_broadcast(&d->cond); + pthread_mutex_unlock(&f->lock); + fuse_req_interrupt_func(req, NULL, NULL); + pthread_cond_destroy(&d->cond); +} + +static void fuse_do_prepare_interrupt(fuse_req_t req, struct fuse_intr_data *d) +{ + d->id = pthread_self(); + pthread_cond_init(&d->cond, NULL); + d->finished = 0; + fuse_req_interrupt_func(req, fuse_interrupt, d); +} + +static inline void fuse_finish_interrupt(struct fuse *f, fuse_req_t req, + struct fuse_intr_data *d) +{ + if (f->conf.intr) + fuse_do_finish_interrupt(f, req, d); +} + +static inline void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req, + struct fuse_intr_data *d) +{ + if (f->conf.intr) + fuse_do_prepare_interrupt(req, d); +} + +static int fuse_do_getattr(struct fuse *f, fuse_req_t req, const char *path, + struct stat *buf) +{ + 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; +} + +static int fuse_do_fgetattr(struct fuse *f, fuse_req_t req, 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.fgetattr(path, buf, fi); + fuse_finish_interrupt(f, req, &d); + return res; +} + +static int fuse_do_rename(struct fuse *f, fuse_req_t req, const char *oldpath, + const char *newpath) +{ + 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; +} + +static int fuse_do_unlink(struct fuse *f, fuse_req_t req, const char *path) +{ + 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; +} + +static void fuse_do_release(struct fuse *f, fuse_req_t req, const char *path, + struct fuse_file_info *fi) +{ + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + f->op.release(path, fi); + fuse_finish_interrupt(f, req, &d); +} + +static int fuse_do_opendir(struct fuse *f, fuse_req_t req, char *path, + struct fuse_file_info *fi) +{ + 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; +} + +static int fuse_do_open(struct fuse *f, fuse_req_t req, 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; +} + +static int fuse_do_statfs(struct fuse *f, fuse_req_t req, const char *path, + struct statvfs *buf) +{ + 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; +} + +static void fuse_do_releasedir(struct fuse *f, fuse_req_t req, + const char *path, struct fuse_file_info *fi) +{ + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + f->op.releasedir(path, fi); + fuse_finish_interrupt(f, req, &d); +} + +static int fuse_do_create(struct fuse *f, fuse_req_t req, const char *path, + mode_t mode, struct fuse_file_info *fi) +{ + 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; +} + +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, + uint64_t owner) +{ + int res; + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + res = f->op.lock(path, fi, cmd, lock, owner); + fuse_finish_interrupt(f, req, &d); + return res; +} + static int is_open(struct fuse *f, fuse_ino_t dir, const char *name) { struct node *node; @@ -422,8 +627,8 @@ static int is_open(struct fuse *f, fuse_ino_t dir, const char *name) return isopen; } -static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname, - char *newname, size_t bufsize) +static char *hidden_name(struct fuse *f, fuse_req_t req, fuse_ino_t dir, + const char *oldname, char *newname, size_t bufsize) { struct stat buf; struct node *node; @@ -454,7 +659,7 @@ static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname, if (!newpath) break; - res = f->op.getattr(newpath, &buf); + res = fuse_do_getattr(f, req, newpath, &buf); if (res != 0) break; free(newpath); @@ -464,17 +669,17 @@ static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname, return newpath; } -static int hide_node(struct fuse *f, const char *oldpath, fuse_ino_t dir, - const char *oldname) +static int hide_node(struct fuse *f, fuse_req_t req, const char *oldpath, + fuse_ino_t dir, const char *oldname) { char newname[64]; char *newpath; int err = -EBUSY; if (f->op.rename && f->op.unlink) { - newpath = hidden_name(f, dir, oldname, newname, sizeof(newname)); + newpath = hidden_name(f, req, dir, oldname, newname, sizeof(newname)); if (newpath) { - int res = f->op.rename(oldpath, newpath); + int res = fuse_do_rename(f, req, oldpath, newpath); if (res == 0) err = rename_node(f, dir, oldname, dir, newname, 1); free(newpath); @@ -529,17 +734,17 @@ static void update_stat(struct node *node, const struct stat *stbuf) curr_time(&node->stat_updated); } -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) +static int lookup_path(struct fuse *f, fuse_req_t req, 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 = f->op.fgetattr(path, &e->attr, fi); + res = fuse_do_fgetattr(f, req, path, &e->attr, fi); else - res = f->op.getattr(path, &e->attr); + res = fuse_do_getattr(f, req, path, &e->attr); if (res == 0) { struct node *node; @@ -566,22 +771,70 @@ static int lookup_path(struct fuse *f, fuse_ino_t nodeid, const char *name, return res; } -static struct fuse *req_fuse(fuse_req_t req) +static struct fuse_context_i *fuse_get_context_internal(void) { - return (struct fuse *) fuse_req_userdata(req); + struct fuse_context_i *c; + + c = (struct fuse_context_i *) pthread_getspecific(fuse_context_key); + if (c == NULL) { + c = (struct fuse_context_i *) malloc(sizeof(struct fuse_context_i)); + if (c == NULL) { + /* This is hard to deal with properly, so just abort. If + memory is so low that the context cannot be allocated, + there's not much hope for the filesystem anyway */ + fprintf(stderr, "fuse: failed to allocate thread specific data\n"); + abort(); + } + pthread_setspecific(fuse_context_key, c); + } + return c; +} + +static void fuse_freecontext(void *data) +{ + free(data); +} + +static int fuse_create_context_key(void) +{ + int err = 0; + pthread_mutex_lock(&fuse_context_lock); + if (!fuse_context_ref) { + err = pthread_key_create(&fuse_context_key, fuse_freecontext); + if (err) { + fprintf(stderr, "fuse: failed to create thread specific key: %s\n", + strerror(err)); + pthread_mutex_unlock(&fuse_context_lock); + return -1; + } + } + fuse_context_ref++; + pthread_mutex_unlock(&fuse_context_lock); + return 0; +} + +static void fuse_delete_context_key(void) +{ + pthread_mutex_lock(&fuse_context_lock); + fuse_context_ref--; + if (!fuse_context_ref) { + free(pthread_getspecific(fuse_context_key)); + pthread_key_delete(fuse_context_key); + } + pthread_mutex_unlock(&fuse_context_lock); } static struct fuse *req_fuse_prepare(fuse_req_t req) { - struct fuse_context *c = fuse_get_context(); + struct fuse_context_i *c = fuse_get_context_internal(); const struct fuse_ctx *ctx = fuse_req_ctx(req); - c->fuse = req_fuse(req); - c->uid = ctx->uid; - c->gid = ctx->gid; - c->pid = ctx->pid; - c->private_data = c->fuse->user_data; - - return c->fuse; + c->req = req; + c->ctx.fuse = req_fuse(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; } static inline void reply_err(fuse_req_t req, int err) @@ -604,11 +857,11 @@ static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e, static void fuse_data_init(void *data, struct fuse_conn_info *conn) { struct fuse *f = (struct fuse *) data; - struct fuse_context *c = fuse_get_context(); + struct fuse_context_i *c = fuse_get_context_internal(); memset(c, 0, sizeof(*c)); - c->fuse = f; - c->private_data = f->user_data; + c->ctx.fuse = f; + c->ctx.private_data = f->user_data; if (f->op.init) f->user_data = f->op.init(conn); @@ -617,11 +870,11 @@ static void fuse_data_init(void *data, struct fuse_conn_info *conn) static void fuse_data_destroy(void *data) { struct fuse *f = (struct fuse *) data; - struct fuse_context *c = fuse_get_context(); + struct fuse_context_i *c = fuse_get_context_internal(); memset(c, 0, sizeof(*c)); - c->fuse = f; - c->private_data = f->user_data; + c->ctx.fuse = f; + c->ctx.private_data = f->user_data; if (f->op.destroy) f->op.destroy(f->user_data); @@ -644,7 +897,7 @@ static void fuse_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) } err = -ENOSYS; if (f->op.getattr) { - err = lookup_path(f, parent, name, path, &e, NULL); + 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; @@ -685,7 +938,7 @@ static void fuse_getattr(fuse_req_t req, fuse_ino_t ino, if (path != NULL) { err = -ENOSYS; if (f->op.getattr) - err = f->op.getattr(path, &buf); + err = fuse_do_getattr(f, req, path, &buf); free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -701,60 +954,80 @@ static void fuse_getattr(fuse_req_t req, fuse_ino_t ino, reply_err(req, err); } -static int do_chmod(struct fuse *f, const char *path, struct stat *attr) +static int do_chmod(struct fuse *f, fuse_req_t req, const char *path, + struct stat *attr) { int err; err = -ENOSYS; - if (f->op.chmod) + 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, const char *path, struct stat *attr, - int valid) +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) + 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, const char *path, struct stat *attr, - struct fuse_file_info *fi) +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) + if (fi && f->op.ftruncate) { + fuse_prepare_interrupt(f, req, &d); err = f->op.ftruncate(path, attr->st_size, fi); - else if (f->op.truncate) + 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_utimes(struct fuse *f, const char *path, struct stat *attr) +static int do_utimes(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.utimes) { struct timespec tv[2]; tv[0] = attr->st_atim; tv[1] = attr->st_mtim; + fuse_prepare_interrupt(f, req, &d); err = f->op.utimes(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; @@ -776,15 +1049,15 @@ static void fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, if (f->op.getattr) { err = 0; if (!err && (valid & FUSE_SET_ATTR_MODE)) - err = do_chmod(f, path, attr); + err = do_chmod(f, req, path, attr); if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) - err = do_chown(f, path, attr, valid); + err = do_chown(f, req, path, attr, valid); if (!err && (valid & FUSE_SET_ATTR_SIZE)) - err = do_truncate(f, path, attr, fi); + 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_utimes(f, path, attr); + err = do_utimes(f, req, path, attr); if (!err) - err = f->op.getattr(path, &buf); + err = fuse_do_getattr(f, req, path, &buf); } free(path); } @@ -816,8 +1089,12 @@ static void fuse_access(fuse_req_t req, fuse_ino_t ino, int mask) fflush(stdout); } err = -ENOSYS; - if (f->op.access) + 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); + } free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -836,8 +1113,12 @@ static void fuse_readlink(fuse_req_t req, fuse_ino_t ino) path = get_path(f, ino); if (path != NULL) { err = -ENOSYS; - if (f->op.readlink) + 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); + } free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -870,16 +1151,19 @@ static void fuse_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, memset(&fi, 0, sizeof(fi)); fi.flags = O_CREAT | O_EXCL | O_WRONLY; - err = f->op.create(path, mode, &fi); + err = fuse_do_create(f, req, path, mode, &fi); if (!err) { - err = lookup_path(f, parent, name, path, &e, &fi); + err = lookup_path(f, req, parent, name, path, &e, &fi); if (f->op.release) - f->op.release(path, &fi); + fuse_do_release(f, req, 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) - err = lookup_path(f, parent, name, path, &e, NULL); + err = lookup_path(f, req, parent, name, path, &e, NULL); } free(path); } @@ -905,9 +1189,12 @@ static void fuse_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, } 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, parent, name, path, &e, NULL); + err = lookup_path(f, req, parent, name, path, &e, NULL); } free(path); } @@ -932,9 +1219,9 @@ static void fuse_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) err = -ENOSYS; if (f->op.unlink) { if (!f->conf.hard_remove && is_open(f, parent, name)) - err = hide_node(f, path, parent, name); + err = hide_node(f, req, path, parent, name); else { - err = f->op.unlink(path); + err = fuse_do_unlink(f, req, path); if (!err) remove_node(f, parent, name); } @@ -961,7 +1248,10 @@ static void fuse_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) } 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); } @@ -989,9 +1279,12 @@ static void fuse_symlink(fuse_req_t req, const char *linkname, } 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, parent, name, path, &e, NULL); + err = lookup_path(f, req, parent, name, path, &e, NULL); } free(path); } @@ -1022,9 +1315,9 @@ static void fuse_rename(fuse_req_t req, fuse_ino_t olddir, const char *oldname, err = 0; if (!f->conf.hard_remove && is_open(f, newdir, newname)) - err = hide_node(f, newpath, newdir, newname); + err = hide_node(f, req, newpath, newdir, newname); if (!err) { - err = f->op.rename(oldpath, newpath); + fuse_do_rename(f, req, oldpath, newpath); if (!err) err = rename_node(f, olddir, oldname, newdir, newname, 0); } @@ -1058,9 +1351,13 @@ static void fuse_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, } 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, newparent, newname, newpath, &e, NULL); + err = lookup_path(f, req, newparent, newname, newpath, &e, + NULL); } free(newpath); } @@ -1084,21 +1381,21 @@ static void fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, if (path != NULL) { err = -ENOSYS; if (f->op.create && f->op.getattr) { - err = f->op.create(path, mode, fi); + 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, parent, name, path, &e, fi); + err = lookup_path(f, req, parent, name, path, &e, fi); if (err) { if (f->op.release) - f->op.release(path, fi); + fuse_do_release(f, req, path, fi); } else if (!S_ISREG(e.attr.st_mode)) { err = -EIO; if (f->op.release) - f->op.release(path, fi); + fuse_do_release(f, req, path, fi); forget_node(f, e.ino, 1); } } @@ -1115,7 +1412,7 @@ static void fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, if (fuse_reply_create(req, &e, fi) == -ENOENT) { /* The open syscall was interrupted, so it must be cancelled */ if(f->op.release) - f->op.release(path, fi); + fuse_do_release(f, req, path, fi); forget_node(f, e.ino, 1); } else { struct node *node = get_node(f, e.ino); @@ -1137,8 +1434,8 @@ 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_ino_t ino, const char *path, - struct fuse_file_info *fi) +static void open_auto_cache(struct fuse *f, fuse_req_t req, fuse_ino_t ino, + const char *path, struct fuse_file_info *fi) { struct node *node = get_node(f, ino); if (node->cache_valid) { @@ -1150,9 +1447,9 @@ static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path, int err; if (f->op.fgetattr) - err = f->op.fgetattr(path, &stbuf, fi); + err = fuse_do_fgetattr(f, req, path, &stbuf, fi); else - err = f->op.getattr(path, &stbuf); + err = fuse_do_getattr(f, req, path, &stbuf); if (!err) update_stat(node, &stbuf); @@ -1178,7 +1475,7 @@ static void fuse_open(fuse_req_t req, fuse_ino_t ino, err = -ENOENT; path = get_path(f, ino); if (path != NULL) - err = fuse_do_open(f, path, fi); + err = fuse_compat_open(f, req, path, fi); } if (!err) { if (f->conf.debug) { @@ -1194,12 +1491,12 @@ static void fuse_open(fuse_req_t req, fuse_ino_t ino, pthread_mutex_lock(&f->lock); if (f->conf.auto_cache) - open_auto_cache(f, ino, path, fi); + open_auto_cache(f, req, ino, path, fi); if (fuse_reply_open(req, fi) == -ENOENT) { /* The open syscall was interrupted, so it must be cancelled */ if(f->op.release && path != NULL) - fuse_do_release(f, path, fi); + fuse_compat_release(f, req, path, fi); } else { struct node *node = get_node(f, ino); node->open_count ++; @@ -1239,8 +1536,12 @@ static void fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, } res = -ENOSYS; - if (f->op.read) + 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); + } free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -1279,8 +1580,12 @@ static void fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf, } res = -ENOSYS; - if (f->op.write) + 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); + } free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -1315,8 +1620,12 @@ static void fuse_flush(fuse_req_t req, fuse_ino_t ino, fflush(stdout); } err = -ENOSYS; - if (f->op.flush) + if (f->op.flush) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); err = f->op.flush(path, fi); + fuse_finish_interrupt(f, req, &d); + } free(path); } if (f->op.lock) { @@ -1324,7 +1633,7 @@ static void fuse_flush(fuse_req_t req, fuse_ino_t ino, memset(&lock, 0, sizeof(lock)); lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; - f->op.lock(path, fi, F_SETLK, &lock, owner); + fuse_do_lock(f, req, path, fi, F_SETLK, &lock, owner); if (err == -ENOSYS) err = 0; } @@ -1348,7 +1657,7 @@ static void fuse_release(fuse_req_t req, fuse_ino_t ino, fflush(stdout); } if (f->op.release) - fuse_do_release(f, path, fi); + fuse_compat_release(f, req, path, fi); pthread_mutex_lock(&f->lock); node = get_node(f, ino); @@ -1358,7 +1667,7 @@ static void fuse_release(fuse_req_t req, fuse_ino_t ino, pthread_mutex_unlock(&f->lock); if(unlink_hidden && path) - f->op.unlink(path); + fuse_do_unlink(f, req, path); if (path) free(path); @@ -1383,8 +1692,12 @@ static void fuse_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, fflush(stdout); } err = -ENOSYS; - if (f->op.fsync) + 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); + } free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -1434,7 +1747,7 @@ static void fuse_opendir(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_opendir(f, path, &fi); + err = fuse_compat_opendir(f, req, path, &fi); dh->fh = fi.fh; } if (!err) { @@ -1443,7 +1756,7 @@ static void fuse_opendir(fuse_req_t req, fuse_ino_t ino, /* The opendir syscall was interrupted, so it must be cancelled */ if(f->op.releasedir) - f->op.releasedir(path, &fi); + fuse_do_releasedir(f, req, path, &fi); pthread_mutex_destroy(&dh->lock); free(dh); } @@ -1560,10 +1873,17 @@ static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino, dh->filled = 1; dh->req = req; err = -ENOSYS; - if (f->op.readdir) + 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); - else if (f->op.getdir) + 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 = NULL; if (!err) err = dh->error; @@ -1621,7 +1941,7 @@ static void fuse_releasedir(fuse_req_t req, fuse_ino_t ino, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); - f->op.releasedir(path ? path : "-", &fi); + fuse_do_releasedir(f, req, path ? path : "-", &fi); free(path); pthread_rwlock_unlock(&f->tree_lock); } @@ -1648,8 +1968,12 @@ static void fuse_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, path = get_path(f, ino); if (path != NULL) { err = -ENOSYS; - if (f->op.fsyncdir) + 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); + } free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -1677,12 +2001,12 @@ static void fuse_statfs(fuse_req_t req, fuse_ino_t ino) err = -ENOENT; path = get_path(f, ino); if (path) { - err = f->op.statfs(path, &buf); + err = fuse_do_statfs(f, req, path, &buf); free(path); } pthread_rwlock_unlock(&f->tree_lock); } else - err = fuse_do_statfs(f, &buf); + err = fuse_compat_statfs(f, req, &buf); } else err = default_statfs(&buf); @@ -1704,16 +2028,20 @@ static void fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, path = get_path(f, ino); if (path != NULL) { err = -ENOSYS; - if (f->op.setxattr) + 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); + } free(path); } pthread_rwlock_unlock(&f->tree_lock); reply_err(req, err); } -static int common_getxattr(struct fuse *f, fuse_ino_t ino, const char *name, - char *value, size_t size) +static int common_getxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino, + const char *name, char *value, size_t size) { int err; char *path; @@ -1723,8 +2051,12 @@ static int common_getxattr(struct fuse *f, fuse_ino_t ino, const char *name, path = get_path(f, ino); if (path != NULL) { err = -ENOSYS; - if (f->op.getxattr) + 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); + } free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -1743,14 +2075,14 @@ static void fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, reply_err(req, -ENOMEM); return; } - res = common_getxattr(f, ino, name, value, size); + res = common_getxattr(f, req, ino, name, value, size); if (res > 0) fuse_reply_buf(req, value, res); else reply_err(req, res); free(value); } else { - res = common_getxattr(f, ino, name, NULL, 0); + res = common_getxattr(f, req, ino, name, NULL, 0); if (res >= 0) fuse_reply_xattr(req, res); else @@ -1758,8 +2090,8 @@ static void fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, } } -static int common_listxattr(struct fuse *f, fuse_ino_t ino, char *list, - size_t size) +static int common_listxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino, + char *list, size_t size) { char *path; int err; @@ -1769,8 +2101,12 @@ static int common_listxattr(struct fuse *f, fuse_ino_t ino, char *list, path = get_path(f, ino); if (path != NULL) { err = -ENOSYS; - if (f->op.listxattr) + 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); + } free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -1788,14 +2124,14 @@ static void fuse_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) reply_err(req, -ENOMEM); return; } - res = common_listxattr(f, ino, list, size); + res = common_listxattr(f, req, ino, list, size); if (res > 0) fuse_reply_buf(req, list, res); else reply_err(req, res); free(list); } else { - res = common_listxattr(f, ino, NULL, 0); + res = common_listxattr(f, req, ino, NULL, 0); if (res >= 0) fuse_reply_xattr(req, res); else @@ -1814,8 +2150,12 @@ static void fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) path = get_path(f, ino); if (path != NULL) { err = -ENOSYS; - if (f->op.removexattr) + 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); + } free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -1834,7 +2174,7 @@ 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 = f->op.lock(path, fi, cmd, lock, owner); + fuse_do_lock(f, req, path, fi, cmd, lock, owner); free(path); } pthread_rwlock_unlock(&f->tree_lock); @@ -1973,18 +2313,20 @@ void fuse_exit(struct fuse *f) fuse_session_exit(f->se); } -struct fuse_context *fuse_get_context() +struct fuse_context *fuse_get_context(void) { - static struct fuse_context context; - if (fuse_getcontext) - return fuse_getcontext(); - else - return &context; + return &fuse_get_context_internal()->ctx; +} + +int fuse_interrupted(void) +{ + return fuse_req_interrupted(fuse_get_context_internal()->req); } void fuse_set_getcontext_func(struct fuse_context *(*func)(void)) { - fuse_getcontext = func; + (void) func; + /* no-op */ } enum { @@ -2018,6 +2360,8 @@ static const struct fuse_opt fuse_lib_opts[] = { FUSE_LIB_OPT("ac_attr_timeout=%lf", ac_attr_timeout, 0), FUSE_LIB_OPT("ac_attr_timeout=", ac_attr_timeout_set, 1), 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_OPT_END }; @@ -2037,7 +2381,9 @@ static void fuse_lib_help(void) " -o negative_timeout=T cache timeout for deleted names (0.0s)\n" " -o attr_timeout=T cache timeout for attributes (1.0s)\n" " -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n" -"\n"); +" -o intr allow requests to be interrupted\n" +" -o intr_signal=NUM signal to send on interrupt (%i)\n" +"\n", FUSE_DEFAULT_INTR_SIGNAL); } static int fuse_lib_opt_proc(void *data, const char *arg, int key, @@ -2058,6 +2404,43 @@ int fuse_is_lib_option(const char *opt) fuse_opt_match(fuse_lib_opts, opt); } +static int fuse_init_intr_signal(int signum, int *installed) +{ + struct sigaction old_sa; + + if (sigaction(signum, NULL, &old_sa) == -1) { + perror("fuse: cannot get old signal handler"); + return -1; + } + + if (old_sa.sa_handler == SIG_DFL) { + struct sigaction sa; + + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = fuse_intr_sighandler; + sigemptyset(&(sa.sa_mask)); + sa.sa_flags = 0; + + if (sigaction(signum, &sa, NULL) == -1) { + perror("fuse: cannot set interrupt signal handler"); + return -1; + } + *installed = 1; + } + return 0; +} + +static void fuse_restore_intr_signal(int signum, int installed) +{ + if (installed) { + struct sigaction sa; + + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = SIG_DFL; + sigaction(signum, &sa, NULL); + } +} + 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) @@ -2071,16 +2454,20 @@ struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, op_size = sizeof(struct fuse_operations); } + if (fuse_create_context_key() == -1) + goto out; + f = (struct fuse *) calloc(1, sizeof(struct fuse)); if (f == NULL) { fprintf(stderr, "fuse: failed to allocate fuse object\n"); - goto out; + goto out_delete_context_key; } f->user_data = user_data; 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; @@ -2107,7 +2494,7 @@ struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, llop.setlk = NULL; } - f->se = fuse_lowlevel_new_common(args, &llop,sizeof(llop), f); + f->se = fuse_lowlevel_new_common(args, &llop, sizeof(llop), f); if (f->se == NULL) goto out_free; @@ -2148,6 +2535,10 @@ struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, goto out_free_root; } + if (f->conf.intr && + fuse_init_intr_signal(f->conf.intr_signal, &f->intr_installed) == -1) + goto out_free_root_name; + root->parent = 0; root->nodeid = FUSE_ROOT_ID; root->generation = 0; @@ -2157,6 +2548,8 @@ struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, return f; + out_free_root_name: + free(root->name); out_free_root: free(root); out_free_id_table: @@ -2167,6 +2560,8 @@ struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, fuse_session_destroy(f->se); out_free: free(f); + out_delete_context_key: + fuse_delete_context_key(); out: return NULL; } @@ -2181,11 +2576,14 @@ struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args, void fuse_destroy(struct fuse *f) { size_t i; - struct fuse_context *c = fuse_get_context(); + 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->fuse = f; - c->private_data = f->user_data; + c->ctx.fuse = f; + c->ctx.private_data = f->user_data; for (i = 0; i < f->id_table_size; i++) { struct node *node; @@ -2215,6 +2613,7 @@ void fuse_destroy(struct fuse *f) pthread_rwlock_destroy(&f->tree_lock); fuse_session_destroy(f->se); free(f); + fuse_delete_context_key(); } #include "fuse_compat.h" @@ -2234,42 +2633,56 @@ static struct fuse *fuse_new_common_compat25(int fd, struct fuse_args *args, #ifndef __FreeBSD__ -static int fuse_do_open(struct fuse *f, char *path, struct fuse_file_info *fi) +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) - return f->op.open(path, fi); + err = fuse_do_open(f, req, path, fi); else if (f->compat == 22) { - int err; struct fuse_file_info_compat22 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; - return err; - } else - return + } 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_do_release(struct fuse *f, char *path, - struct fuse_file_info *fi) +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) - f->op.release(path ? path : "-", fi); - else if (path) + 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_do_opendir(struct fuse *f, char *path, - struct fuse_file_info *fi) +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 f->op.opendir(path, fi); + return fuse_do_opendir(f, req, path, fi); } else { int err; struct fuse_file_info_compat22 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; @@ -2299,21 +2712,27 @@ static void convert_statfs_old(struct statfs *oldbuf, struct statvfs *stbuf) stbuf->f_namemax = oldbuf->f_namelen; } -static int fuse_do_statfs(struct fuse *f, struct statvfs *buf) +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 = f->op.statfs("/", buf); + 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); } @@ -2374,26 +2793,27 @@ __asm__(".symver fuse_new_compat22,fuse_new@FUSE_2.2"); #else /* __FreeBSD__ */ -static int fuse_do_open(struct fuse *f, char *path, struct fuse_file_info *fi) +static int fuse_compat_open(struct fuse *f, fuse_req_t req, char *path, + struct fuse_file_info *fi) { - return f->op.open(path, fi); + return fuse_do_open(f, req, path, fi); } -static void fuse_do_release(struct fuse *f, char *path, - struct fuse_file_info *fi) +static void fuse_compat_release(struct fuse *f, fuse_req_t req, char *path, + struct fuse_file_info *fi) { - f->op.release(path ? path : "-", fi); + fuse_do_release(f, req, path ? path : "-", fi); } -static int fuse_do_opendir(struct fuse *f, char *path, - struct fuse_file_info *fi) +static int fuse_compat_opendir(struct fuse *f, fuse_req_t req, char *path, + struct fuse_file_info *fi) { - return f->op.opendir(path, fi); + return fuse_do_opendir(f, req, path, fi); } -static int fuse_do_statfs(struct fuse *f, struct statvfs *buf) +static int fuse_do_statfs(struct fuse *f, fuse_req_t req, struct statvfs *buf) { - return f->op.statfs("/", buf); + return fuse_do_statfs(f, req, "/", buf); } #endif /* __FreeBSD__ */ diff --git a/lib/fuse_i.h b/lib/fuse_i.h index 268bc41..c2c8358 100644 --- a/lib/fuse_i.h +++ b/lib/fuse_i.h @@ -11,6 +11,7 @@ struct fuse_session; struct fuse_chan; struct fuse_lowlevel_ops; +struct fuse_req; struct fuse_cmd { char *buf; diff --git a/lib/fuse_loop_mt.c b/lib/fuse_loop_mt.c index 47fca46..d1e327c 100644 --- a/lib/fuse_loop_mt.c +++ b/lib/fuse_loop_mt.c @@ -179,21 +179,24 @@ int fuse_session_loop_mt(struct fuse_session *se) fuse_mutex_init(&mt.lock); pthread_mutex_lock(&mt.lock); - fuse_start_thread(&mt); + err = fuse_start_thread(&mt); pthread_mutex_unlock(&mt.lock); - while (!fuse_session_exited(se)) - pause(); + if (!err) { + while (!fuse_session_exited(se)) + pause(); - for (w = mt.main.next; w != &mt.main; w = w->next) - pthread_cancel(w->thread_id); - mt.exit = 1; - pthread_mutex_unlock(&mt.lock); + for (w = mt.main.next; w != &mt.main; w = w->next) + pthread_cancel(w->thread_id); + mt.exit = 1; + pthread_mutex_unlock(&mt.lock); + + while (mt.main.next != &mt.main) + fuse_join_worker(&mt, mt.main.next); - while (mt.main.next != &mt.main) - fuse_join_worker(&mt, mt.main.next); + err = mt.error; + } pthread_mutex_destroy(&mt.lock); - err = mt.error; fuse_session_reset(se); return err; } diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index 8550165..e7044d3 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -28,6 +28,8 @@ struct fuse_ll; struct fuse_req { struct fuse_ll *f; uint64_t unique; + int ctr; + pthread_mutex_t lock; struct fuse_ctx ctx; struct fuse_chan *ch; int interrupted; @@ -124,12 +126,28 @@ static void list_add_req(struct fuse_req *req, struct fuse_req *next) next->prev = req; } +static void destroy_req(fuse_req_t req) +{ + pthread_mutex_destroy(&req->lock); + free(req); +} + static void free_req(fuse_req_t req) { - pthread_mutex_lock(&req->f->lock); + int ctr; + struct fuse_ll *f = req->f; + + pthread_mutex_lock(&req->lock); + req->u.ni.func = NULL; + req->u.ni.data = NULL; + pthread_mutex_unlock(&req->lock); + + pthread_mutex_lock(&f->lock); list_del_req(req); - pthread_mutex_unlock(&req->f->lock); - free(req); + ctr = --req->ctr; + pthread_mutex_unlock(&f->lock); + if (!ctr) + destroy_req(req); } static int send_reply(fuse_req_t req, int error, const void *arg, @@ -819,13 +837,28 @@ static int find_interrupted(struct fuse_ll *f, struct fuse_req *req) for (curr = f->list.next; curr != &f->list; curr = curr->next) { if (curr->unique == req->u.i.unique) { + curr->ctr++; + pthread_mutex_unlock(&f->lock); + + /* Ugh, ugly locking */ + pthread_mutex_lock(&curr->lock); + pthread_mutex_lock(&f->lock); curr->interrupted = 1; + pthread_mutex_unlock(&f->lock); if (curr->u.ni.func) curr->u.ni.func(curr, curr->u.ni.data); + pthread_mutex_unlock(&curr->lock); + + pthread_mutex_lock(&f->lock); + curr->ctr--; + if (!curr->ctr) + destroy_req(curr); + return 1; } } - for (curr = f->interrupts.next; curr != &f->interrupts; curr = curr->next) { + for (curr = f->interrupts.next; curr != &f->interrupts; + curr = curr->next) { if (curr->u.i.unique == req->u.i.unique) return 1; } @@ -847,7 +880,7 @@ static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) pthread_mutex_lock(&f->lock); if (find_interrupted(f, req)) - free(req); + destroy_req(req); else list_add_req(req, &f->interrupts); pthread_mutex_unlock(&f->lock); @@ -950,14 +983,23 @@ const struct fuse_ctx *fuse_req_ctx(fuse_req_t req) void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data) { - struct fuse_ll *f = req->f; - - pthread_mutex_lock(&f->lock); + pthread_mutex_lock(&req->lock); req->u.ni.func = func; req->u.ni.data = data; if (req->interrupted && func) func(req, data); - pthread_mutex_unlock(&f->lock); + pthread_mutex_unlock(&req->lock); +} + +int fuse_req_interrupted(fuse_req_t req) +{ + int interrupted; + + pthread_mutex_lock(&req->f->lock); + interrupted = req->interrupted; + pthread_mutex_unlock(&req->f->lock); + + return interrupted; } static struct { @@ -1038,7 +1080,9 @@ static void fuse_ll_process(void *data, const char *buf, size_t len, req->ctx.gid = in->gid; req->ctx.pid = in->pid; req->ch = ch; + req->ctr = 1; list_init_req(req); + fuse_mutex_init(&req->lock); if (!f->got_init && in->opcode != FUSE_INIT) fuse_reply_err(req, EIO); @@ -1133,6 +1177,7 @@ static void fuse_ll_destroy(void *data) if (f->op.destroy) f->op.destroy(f->userdata); + pthread_mutex_destroy(&f->lock); free(f); } diff --git a/lib/fuse_mt.c b/lib/fuse_mt.c index b0b31dd..ee8ef40 100644 --- a/lib/fuse_mt.c +++ b/lib/fuse_mt.c @@ -15,61 +15,6 @@ #include #include -static pthread_key_t context_key; -static pthread_mutex_t context_lock = PTHREAD_MUTEX_INITIALIZER; -static int context_ref; - -static struct fuse_context *mt_getcontext(void) -{ - struct fuse_context *ctx; - - ctx = (struct fuse_context *) pthread_getspecific(context_key); - if (ctx == NULL) { - ctx = (struct fuse_context *) malloc(sizeof(struct fuse_context)); - if (ctx == NULL) { - fprintf(stderr, "fuse: failed to allocate thread specific data\n"); - return NULL; - } - pthread_setspecific(context_key, ctx); - } - return ctx; -} - -static void mt_freecontext(void *data) -{ - free(data); -} - -static int mt_create_context_key(void) -{ - int err = 0; - pthread_mutex_lock(&context_lock); - if (!context_ref) { - err = pthread_key_create(&context_key, mt_freecontext); - if (err) - fprintf(stderr, "fuse: failed to create thread specific key: %s\n", - strerror(err)); - else - fuse_set_getcontext_func(mt_getcontext); - } - if (!err) - context_ref ++; - pthread_mutex_unlock(&context_lock); - return err; -} - -static void mt_delete_context_key(void) -{ - pthread_mutex_lock(&context_lock); - context_ref--; - if (!context_ref) { - fuse_set_getcontext_func(NULL); - free(pthread_getspecific(context_key)); - pthread_key_delete(context_key); - } - pthread_mutex_unlock(&context_lock); -} - struct procdata { struct fuse *f; struct fuse_chan *prevch; @@ -162,33 +107,17 @@ int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data) return -1; } fuse_session_add_chan(se, ch); - - if (mt_create_context_key() != 0) { - fuse_session_destroy(se); - return -1; - } - res = fuse_session_loop_mt(se); - - mt_delete_context_key(); fuse_session_destroy(se); return res; } int fuse_loop_mt(struct fuse *f) { - int res; - if (f == NULL) return -1; - if (mt_create_context_key() != 0) - return -1; - - res = fuse_session_loop_mt(fuse_get_session(f)); - - mt_delete_context_key(); - return res; + return fuse_session_loop_mt(fuse_get_session(f)); } __asm__(".symver fuse_loop_mt_proc,__fuse_loop_mt@"); diff --git a/lib/fuse_signals.c b/lib/fuse_signals.c index 38f407d..6154110 100644 --- a/lib/fuse_signals.c +++ b/lib/fuse_signals.c @@ -32,13 +32,13 @@ static int set_one_signal_handler(int sig, void (*handler)(int)) sa.sa_flags = 0; if (sigaction(sig, NULL, &old_sa) == -1) { - perror("FUSE: cannot get old signal handler"); + perror("fuse: cannot get old signal handler"); return -1; } if (old_sa.sa_handler == SIG_DFL && sigaction(sig, &sa, NULL) == -1) { - perror("Cannot set signal handler"); + perror("fuse: cannot set signal handler"); return -1; } return 0; diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript index 147067f..df0c7d1 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -85,8 +85,10 @@ FUSE_2.6 { fuse_add_direntry; fuse_chan_new; fuse_chan_new_compat24; + fuse_chan_recv; fuse_daemonize; fuse_get_session; + fuse_interrupted; fuse_lowlevel_new; fuse_lowlevel_new_compat25; fuse_main_real; @@ -96,6 +98,9 @@ FUSE_2.6 { fuse_new; fuse_new_compat25; fuse_opt_insert_arg; + fuse_reply_lock; + fuse_req_interrupt_func; + fuse_req_interrupted; fuse_session_remove_chan; fuse_setup; fuse_setup_compat25; @@ -103,7 +108,6 @@ FUSE_2.6 { fuse_teardown_compat22; fuse_unmount; fuse_unmount_compat22; - fuse_chan_recv; local: *; -- 2.30.2