From b052a1a1b894c4bcd9b4e70dfceb70e340bbb781 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 28 Jun 2006 14:51:20 +0000 Subject: [PATCH] file locking --- ChangeLog | 8 + include/fuse.h | 5 +- include/fuse_common.h | 2 +- include/fuse_lowlevel.h | 97 ++++++++-- kernel/fuse_kernel.h | 35 +++- lib/fuse.c | 3 +- lib/fuse_lowlevel.c | 386 ++++++++++++++++++++++++++++++---------- 7 files changed, 424 insertions(+), 112 deletions(-) diff --git a/ChangeLog b/ChangeLog index 681b579..e3cec01 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2006-06-28 Miklos Szeredi + + * Add POSIX file locking support + + * Add request interruption + + * The above need 2.6.17-git9 or later to be usable + 2006-06-06 Miklos Szeredi * Add missing pthread_rwlock_destroy(). Patch from Remy Blank diff --git a/include/fuse.h b/include/fuse.h index f7dba7f..fe52531 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -95,8 +95,9 @@ struct fuse_operations { /** Create a file node * - * There is no create() operation, mknod() will be called for - * creation of all non-directory, non-symlink nodes. + * If the filesystem doesn't define a create() operation, mknod() + * will be called for creation of all non-directory, non-symlink + * nodes. */ int (*mknod) (const char *, mode_t, dev_t); diff --git a/include/fuse_common.h b/include/fuse_common.h index c1b2806..73cc0df 100644 --- a/include/fuse_common.h +++ b/include/fuse_common.h @@ -163,10 +163,10 @@ void fuse_remove_signal_handlers(struct fuse_session *se); # include "fuse_common_compat.h" # undef FUSE_MINOR_VERSION # undef fuse_main -# define fuse_mount fuse_mount_compat25 # define fuse_unmount fuse_unmount_compat22 # if FUSE_USE_VERSION == 25 # define FUSE_MINOR_VERSION 5 +# define fuse_mount fuse_mount_compat25 # elif FUSE_USE_VERSION == 24 || FUSE_USE_VERSION == 22 # define FUSE_MINOR_VERSION 4 # define fuse_mount fuse_mount_compat22 diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index f06fca3..cf1fe0c 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -24,6 +24,7 @@ #include "fuse_common.h" #include +#include #include #include #include @@ -158,8 +159,8 @@ struct fuse_lowlevel_ops { * Look up a directory entry by name * * Valid replies: - * fuse_reply_entry() - * fuse_reply_err() + * fuse_reply_entry + * fuse_reply_err * * @param req request handle * @param parent inode number of the parent directory @@ -184,7 +185,7 @@ struct fuse_lowlevel_ops { * will receive a forget message. * * Valid replies: - * fuse_reply_none() + * fuse_reply_none * * @param req request handle * @param ino the inode number @@ -196,8 +197,8 @@ struct fuse_lowlevel_ops { * Get file attributes * * Valid replies: - * fuse_reply_attr() - * fuse_reply_err() + * fuse_reply_attr + * fuse_reply_err * * @param req request handle * @param ino the inode number @@ -220,8 +221,8 @@ struct fuse_lowlevel_ops { * parameter will be NULL. * * Valid replies: - * fuse_reply_attr() - * fuse_reply_err() + * fuse_reply_attr + * fuse_reply_err * * @param req request handle * @param ino the inode number @@ -447,14 +448,19 @@ struct fuse_lowlevel_ops { * One reason to flush data, is if the filesystem wants to return * write errors. * + * If the filesystem supports file locking operations (setlk, + * getlk) it should remove all locks belonging to 'owner'. + * * Valid replies: * fuse_reply_err * * @param req request handle * @param ino the inode number * @param fi file information + * @param owner lock owner id */ - void (*flush) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + void (*flush) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + uint64_t owner); /** * Release an open file @@ -722,6 +728,43 @@ struct fuse_lowlevel_ops { */ void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi); + + /** + * Test for a POSIX file lock + * + * Valid replies: + * fuse_reply_lock + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * @param lock the region/type to test + * @param owner lock owner id of caller + */ + void (*getlk) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + struct flock *lock, uint64_t owner); + + /** + * Acquire, modify or release a POSIX file lock + * + * For POSIX threads (NPTL) there's a 1-1 relation between pid and + * owner, but this is not always the case. For checking lock + * ownership, 'owner' must be used. The l_pid field in 'struct + * flock' should only be used to fill in this field in getlk(). + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * @param lock the region/type to test + * @param owner lock owner id of caller + * @param sleep locking operation may sleep + */ + void (*setlk) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + struct flock *lock, uint64_t owner, int sleep); }; /** @@ -730,8 +773,8 @@ struct fuse_lowlevel_ops { * Possible requests: * all except forget * - * unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr - * and removexattr may send a zero code + * unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr, + * removexattr and setlk may send a zero code * * @param req request handle * @param err the positive error value, or zero for success @@ -868,6 +911,18 @@ int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf); */ int fuse_reply_xattr(fuse_req_t req, size_t count); +/** + * Reply with file lock information + * + * Possible requests: + * getlk + * + * @param req request handle + * @param lock the lock information + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_lock(fuse_req_t req, struct flock *lock); + /* ----------------------------------------------------------- * * Filling a buffer in readdir * * ----------------------------------------------------------- */ @@ -923,6 +978,28 @@ void *fuse_req_userdata(fuse_req_t req); */ const struct fuse_ctx *fuse_req_ctx(fuse_req_t req); +/** + * Callback function for an interrupt + * + * @param req interrupted request + * @param data user data + */ +typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data); + +/** + * Register/unregister callback for an interrupt + * + * If an interrupt has already happened, then the callback function is + * called from within this function, hence it's not possible for + * interrupts to be lost. + * + * @param req request handle + * @param func the callback function or NULL for unregister + * @parm data user data passed to the callback function + */ +void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, + void *data); + /* ----------------------------------------------------------- * * Filesystem setup * * ----------------------------------------------------------- */ diff --git a/kernel/fuse_kernel.h b/kernel/fuse_kernel.h index bd37693..179c7eb 100644 --- a/kernel/fuse_kernel.h +++ b/kernel/fuse_kernel.h @@ -43,19 +43,20 @@ #define __s32 int32_t #else #include +#include #endif /** Version number of this interface */ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 6 +#define FUSE_KERNEL_MINOR_VERSION 7 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 /** The major number of the fuse character device */ -#define FUSE_MAJOR 10 +#define FUSE_MAJOR MISC_MAJOR /** The minor number of the fuse character device */ #define FUSE_MINOR 229 @@ -93,6 +94,13 @@ struct fuse_kstatfs { __u32 spare[6]; }; +struct fuse_file_lock { + __u64 start; + __u64 end; + __u32 type; + __u32 pid; /* tgid */ +}; + /** * Bitmasks for fuse_setattr_in.valid */ @@ -117,6 +125,7 @@ struct fuse_kstatfs { * INIT request/reply flags */ #define FUSE_ASYNC_READ (1 << 0) +#define FUSE_POSIX_LOCKS (1 << 1) enum fuse_opcode { FUSE_LOOKUP = 1, @@ -147,11 +156,12 @@ enum fuse_opcode { FUSE_READDIR = 28, FUSE_RELEASEDIR = 29, FUSE_FSYNCDIR = 30, + FUSE_GETLK = 31, + FUSE_SETLK = 32, + FUSE_SETLKW = 33, FUSE_ACCESS = 34, FUSE_CREATE = 35, - - /* Keep at the end: */ - FUSE_MAXOP + FUSE_INTERRUPT = 36, }; /* The read buffer is required to be at least 8k, but may be much larger */ @@ -237,6 +247,7 @@ struct fuse_flush_in { __u64 fh; __u32 flush_flags; __u32 padding; + __u64 lock_owner; }; struct fuse_read_in { @@ -285,6 +296,16 @@ struct fuse_getxattr_out { __u32 padding; }; +struct fuse_lk_in { + __u64 fh; + __u64 owner; + struct fuse_file_lock lk; +}; + +struct fuse_lk_out { + struct fuse_file_lock lk; +}; + struct fuse_access_in { __u32 mask; __u32 padding; @@ -306,6 +327,10 @@ struct fuse_init_out { __u32 max_write; }; +struct fuse_interrupt_in { + __u64 unique; +}; + struct fuse_in_header { __u32 len; __u32 opcode; diff --git a/lib/fuse.c b/lib/fuse.c index cfedc28..2fc6fa4 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -1306,12 +1306,13 @@ static void fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf, } static void fuse_flush(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info *fi) + struct fuse_file_info *fi, uint64_t owner) { struct fuse *f = req_fuse_prepare(req); char *path; int err; + (void) owner; err = -ENOENT; pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index 20436ed..5076a5b 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -19,8 +19,31 @@ #include #include #include +#include #define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) +#define OFFSET_MAX 0x7fffffffffffffffLL + +struct fuse_ll; + +struct fuse_req { + struct fuse_ll *f; + uint64_t unique; + struct fuse_ctx ctx; + struct fuse_chan *ch; + int interrupted; + union { + struct { + uint64_t unique; + } i; + struct { + fuse_interrupt_func_t func; + void *data; + } ni; + } u; + struct fuse_req *next; + struct fuse_req *prev; +}; struct fuse_ll { int debug; @@ -30,51 +53,24 @@ struct fuse_ll { void *userdata; uid_t owner; struct fuse_conn_info conn; + struct fuse_req list; + struct fuse_req interrupts; + pthread_mutex_t lock; }; -struct fuse_req { - struct fuse_ll *f; - uint64_t unique; - struct fuse_ctx ctx; - struct fuse_chan *ch; -}; - -static const char *opname(enum fuse_opcode opcode) +#ifndef USE_UCLIBC +#define mutex_init(mut) pthread_mutex_init(mut, NULL) +#else +static void mutex_init(pthread_mutex_t *mut) { - switch (opcode) { - case FUSE_LOOKUP: return "LOOKUP"; - case FUSE_FORGET: return "FORGET"; - case FUSE_GETATTR: return "GETATTR"; - case FUSE_SETATTR: return "SETATTR"; - case FUSE_READLINK: return "READLINK"; - case FUSE_SYMLINK: return "SYMLINK"; - case FUSE_MKNOD: return "MKNOD"; - case FUSE_MKDIR: return "MKDIR"; - case FUSE_UNLINK: return "UNLINK"; - case FUSE_RMDIR: return "RMDIR"; - case FUSE_RENAME: return "RENAME"; - case FUSE_LINK: return "LINK"; - case FUSE_OPEN: return "OPEN"; - case FUSE_READ: return "READ"; - case FUSE_WRITE: return "WRITE"; - case FUSE_STATFS: return "STATFS"; - case FUSE_FLUSH: return "FLUSH"; - case FUSE_RELEASE: return "RELEASE"; - case FUSE_FSYNC: return "FSYNC"; - case FUSE_SETXATTR: return "SETXATTR"; - case FUSE_GETXATTR: return "GETXATTR"; - case FUSE_LISTXATTR: return "LISTXATTR"; - case FUSE_REMOVEXATTR: return "REMOVEXATTR"; - case FUSE_INIT: return "INIT"; - case FUSE_OPENDIR: return "OPENDIR"; - case FUSE_READDIR: return "READDIR"; - case FUSE_RELEASEDIR: return "RELEASEDIR"; - case FUSE_FSYNCDIR: return "FSYNCDIR"; - case FUSE_ACCESS: return "ACCESS"; - case FUSE_CREATE: return "CREATE"; - default: return "???"; - } + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); + pthread_mutex_init(mut, &attr); + pthread_mutexattr_destroy(&attr); } +#endif + static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr) { @@ -120,8 +116,34 @@ static size_t iov_length(const struct iovec *iov, size_t count) return ret; } +static void list_init_req(struct fuse_req *req) +{ + req->next = req; + req->prev = req; +} + +static void list_del_req(struct fuse_req *req) +{ + struct fuse_req *prev = req->prev; + struct fuse_req *next = req->next; + prev->next = next; + next->prev = prev; +} + +static void list_add_req(struct fuse_req *req, struct fuse_req *next) +{ + struct fuse_req *prev = next->prev; + req->next = next; + req->prev = prev; + prev->next = req; + next->prev = req; +} + static void free_req(fuse_req_t req) { + pthread_mutex_lock(&req->f->lock); + list_del_req(req); + pthread_mutex_unlock(&req->f->lock); free(req); } @@ -361,10 +383,27 @@ int fuse_reply_xattr(fuse_req_t req, size_t count) return send_reply_ok(req, &arg, sizeof(arg)); } +int fuse_reply_lock(fuse_req_t req, struct flock *lock) +{ + struct fuse_lk_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.lk.type = lock->l_type; + if (lock->l_type != F_UNLCK) { + arg.lk.start = lock->l_start; + if (lock->l_len == 0) + arg.lk.end = OFFSET_MAX; + else + arg.lk.end = lock->l_start + lock->l_len - 1; + } + arg.lk.pid = lock->l_pid; + return send_reply_ok(req, &arg, sizeof(arg)); +} + static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { char *name = (char *) inarg; - + if (req->f->op.lookup) req->f->op.lookup(req, nodeid, name); else @@ -374,7 +413,7 @@ static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg; - + if (req->f->op.forget) req->f->op.forget(req, nodeid, arg->nlookup); } @@ -392,7 +431,7 @@ static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg; - + if (req->f->op.setattr) { struct fuse_file_info *fi = NULL; struct fuse_file_info fi_store; @@ -414,7 +453,7 @@ static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_access_in *arg = (struct fuse_access_in *) inarg; - + if (req->f->op.access) req->f->op.access(req, nodeid, arg->mask); else @@ -434,7 +473,7 @@ static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg; - + if (req->f->op.mknod) req->f->op.mknod(req, nodeid, PARAM(arg), arg->mode, arg->rdev); else @@ -444,7 +483,7 @@ static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg; - + if (req->f->op.mkdir) req->f->op.mkdir(req, nodeid, PARAM(arg), arg->mode); else @@ -454,7 +493,7 @@ static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { char *name = (char *) inarg; - + if (req->f->op.unlink) req->f->op.unlink(req, nodeid, name); else @@ -464,7 +503,7 @@ static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { char *name = (char *) inarg; - + if (req->f->op.rmdir) req->f->op.rmdir(req, nodeid, name); else @@ -475,7 +514,7 @@ static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { char *name = (char *) inarg; char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1; - + if (req->f->op.symlink) req->f->op.symlink(req, linkname, nodeid, name); else @@ -497,7 +536,7 @@ static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_link_in *arg = (struct fuse_link_in *) inarg; - + if (req->f->op.link) req->f->op.link(req, arg->oldnodeid, nodeid, PARAM(arg)); else @@ -507,7 +546,7 @@ static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_open_in *arg = (struct fuse_open_in *) inarg; - + if (req->f->op.create) { struct fuse_file_info fi; @@ -536,7 +575,7 @@ static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_read_in *arg = (struct fuse_read_in *) inarg; - + if (req->f->op.read) { struct fuse_file_info fi; @@ -568,13 +607,17 @@ static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg; struct fuse_file_info fi; + uint64_t lock_owner = 0; + + if (req->f->conn.proto_minor >= 7) + lock_owner = arg->lock_owner; memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; fi.fh_old = fi.fh; if (req->f->op.flush) - req->f->op.flush(req, nodeid, &fi); + req->f->op.flush(req, nodeid, &fi, lock_owner); else fuse_reply_err(req, ENOSYS); } @@ -702,7 +745,7 @@ static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; - + if (req->f->op.getxattr) req->f->op.getxattr(req, nodeid, PARAM(arg), arg->size); else @@ -712,7 +755,7 @@ static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; - + if (req->f->op.listxattr) req->f->op.listxattr(req, nodeid, arg->size); else @@ -722,13 +765,126 @@ static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { char *name = (char *) inarg; - + if (req->f->op.removexattr) req->f->op.removexattr(req, nodeid, name); else fuse_reply_err(req, ENOSYS); } +static void convert_fuse_file_lock(struct fuse_file_lock *fl, + struct flock *flock) +{ + memset(flock, 0, sizeof(struct flock)); + flock->l_type = fl->type; + flock->l_whence = SEEK_SET; + flock->l_start = fl->start; + if (fl->end == OFFSET_MAX) + flock->l_len = 0; + else + flock->l_len = fl->end - fl->start + 1; + flock->l_pid = fl->pid; +} + +static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg; + struct fuse_file_info fi; + struct flock flock; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + + convert_fuse_file_lock(&arg->lk, &flock); + if (req->f->op.getlk) + req->f->op.getlk(req, nodeid, &fi, &flock, arg->owner); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, + const void *inarg, int sleep) +{ + struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg; + struct fuse_file_info fi; + struct flock flock; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + + convert_fuse_file_lock(&arg->lk, &flock); + if (req->f->op.setlk) + req->f->op.setlk(req, nodeid, &fi, &flock, arg->owner, sleep); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + do_setlk_common(req, nodeid, inarg, 0); +} + +static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + do_setlk_common(req, nodeid, inarg, 1); +} + +static int find_interrupted(struct fuse_ll *f, struct fuse_req *req) +{ + struct fuse_req *curr; + + for (curr = f->list.next; curr != &f->list; curr = curr->next) { + if (curr->unique == req->u.i.unique) { + curr->interrupted = 1; + if (curr->u.ni.func) + curr->u.ni.func(curr, curr->u.ni.data); + return 1; + } + } + for (curr = f->interrupts.next; curr != &f->interrupts; curr = curr->next) { + if (curr->u.i.unique == req->u.i.unique) + return 1; + } + return 0; +} + +static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg; + struct fuse_ll *f = req->f; + + (void) nodeid; + if (f->debug) { + printf("INTERRUPT: %llu\n", (unsigned long long) arg->unique); + fflush(stdout); + } + + req->u.i.unique = arg->unique; + + pthread_mutex_lock(&f->lock); + if (find_interrupted(f, req)) + free(req); + else + list_add_req(req, &f->interrupts); + pthread_mutex_unlock(&f->lock); +} + +static void check_interrupt(struct fuse_ll *f, struct fuse_req *req) +{ + struct fuse_req *curr; + + for (curr = f->interrupts.next; curr != &f->interrupts; curr = curr->next) { + if (curr->u.i.unique == req->unique) { + list_del_req(curr); + free(curr); + return; + } + } + curr = f->interrupts.next; + if (curr != &f->interrupts) + fuse_reply_err(curr, EAGAIN); +} + static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_init_in *arg = (struct fuse_init_in *) inarg; @@ -776,6 +932,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) outarg.minor = FUSE_KERNEL_MINOR_VERSION; if (f->conn.async_read) outarg.flags |= FUSE_ASYNC_READ; + if (f->op.getlk && f->op.setlk) + outarg.flags |= FUSE_POSIX_LOCKS; outarg.max_readahead = f->conn.max_readahead; outarg.max_write = f->conn.max_write; @@ -800,39 +958,69 @@ const struct fuse_ctx *fuse_req_ctx(fuse_req_t req) return &req->ctx; } -static void (*fuse_ll_ops[FUSE_MAXOP])(fuse_req_t, fuse_ino_t, const void *) = { - [FUSE_LOOKUP] = do_lookup, - [FUSE_FORGET] = do_forget, - [FUSE_GETATTR] = do_getattr, - [FUSE_SETATTR] = do_setattr, - [FUSE_READLINK] = do_readlink, - [FUSE_SYMLINK] = do_symlink, - [FUSE_MKNOD] = do_mknod, - [FUSE_MKDIR] = do_mkdir, - [FUSE_UNLINK] = do_unlink, - [FUSE_RMDIR] = do_rmdir, - [FUSE_RENAME] = do_rename, - [FUSE_LINK] = do_link, - [FUSE_OPEN] = do_open, - [FUSE_READ] = do_read, - [FUSE_WRITE] = do_write, - [FUSE_STATFS] = do_statfs, - [FUSE_RELEASE] = do_release, - [FUSE_FSYNC] = do_fsync, - [FUSE_SETXATTR] = do_setxattr, - [FUSE_GETXATTR] = do_getxattr, - [FUSE_LISTXATTR] = do_listxattr, - [FUSE_REMOVEXATTR] = do_removexattr, - [FUSE_FLUSH] = do_flush, - [FUSE_INIT] = do_init, - [FUSE_OPENDIR] = do_opendir, - [FUSE_READDIR] = do_readdir, - [FUSE_RELEASEDIR] = do_releasedir, - [FUSE_FSYNCDIR] = do_fsyncdir, - [FUSE_ACCESS] = do_access, - [FUSE_CREATE] = do_create, +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); + req->u.ni.func = func; + req->u.ni.data = data; + if (req->interrupted && func) + func(req, data); + pthread_mutex_unlock(&f->lock); +} + +static struct { + void (*func)(fuse_req_t, fuse_ino_t, const void *); + const char *name; +} fuse_ll_ops[] = { + [FUSE_LOOKUP] = { do_lookup, "LOOKUP" }, + [FUSE_FORGET] = { do_forget, "FORGET" }, + [FUSE_GETATTR] = { do_getattr, "GETATTR" }, + [FUSE_SETATTR] = { do_setattr, "SETATTR" }, + [FUSE_READLINK] = { do_readlink, "READLINK" }, + [FUSE_SYMLINK] = { do_symlink, "SYMLINK" }, + [FUSE_MKNOD] = { do_mknod, "MKNOD" }, + [FUSE_MKDIR] = { do_mkdir, "MKDIR" }, + [FUSE_UNLINK] = { do_unlink, "UNLINK" }, + [FUSE_RMDIR] = { do_rmdir, "RMDIR" }, + [FUSE_RENAME] = { do_rename, "RENAME" }, + [FUSE_LINK] = { do_link, "LINK" }, + [FUSE_OPEN] = { do_open, "OPEN" }, + [FUSE_READ] = { do_read, "READ" }, + [FUSE_WRITE] = { do_write, "WRITE" }, + [FUSE_STATFS] = { do_statfs, "STATFS" }, + [FUSE_RELEASE] = { do_release, "RELEASE" }, + [FUSE_FSYNC] = { do_fsync, "FSYNC" }, + [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" }, + [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" }, + [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" }, + [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" }, + [FUSE_FLUSH] = { do_flush, "FLUSH" }, + [FUSE_INIT] = { do_init, "INIT" }, + [FUSE_OPENDIR] = { do_opendir, "OPENDIR" }, + [FUSE_READDIR] = { do_readdir, "READDIR" }, + [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" }, + [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" }, + [FUSE_GETLK] = { do_getlk, "GETLK" }, + [FUSE_SETLK] = { do_setlk, "SETLK" }, + [FUSE_SETLKW] = { do_setlkw, "SETLKW" }, + [FUSE_ACCESS] = { do_access, "ACCESS" }, + [FUSE_CREATE] = { do_create, "CREATE" }, + [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" }, }; +#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) + +static const char *opname(enum fuse_opcode opcode) +{ + if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name) + return "???"; + else + return fuse_ll_ops[opcode].name; +} + static void fuse_ll_process(void *data, const char *buf, size_t len, struct fuse_chan *ch) { @@ -843,12 +1031,13 @@ static void fuse_ll_process(void *data, const char *buf, size_t len, if (f->debug) { printf("unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %i\n", - in->unique, opname((enum fuse_opcode) in->opcode), in->opcode, + (unsigned long long) in->unique, + opname((enum fuse_opcode) in->opcode), in->opcode, (unsigned long) in->nodeid, len); fflush(stdout); } - req = (struct fuse_req *) malloc(sizeof(struct fuse_req)); + req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req)); if (req == NULL) { fprintf(stderr, "fuse: failed to allocate request\n"); return; @@ -860,6 +1049,7 @@ 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; + list_init_req(req); if (!f->got_init && in->opcode != FUSE_INIT) fuse_reply_err(req, EIO); @@ -869,10 +1059,17 @@ static void fuse_ll_process(void *data, const char *buf, size_t len, in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR && in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR) { fuse_reply_err(req, EACCES); - } else if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode]) + } else if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) fuse_reply_err(req, ENOSYS); - else - fuse_ll_ops[in->opcode](req, in->nodeid, inarg); + else { + if (in->opcode != FUSE_INTERRUPT) { + pthread_mutex_lock(&f->lock); + check_interrupt(f, req); + list_add_req(req, &f->list); + pthread_mutex_unlock(&f->lock); + } + fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); + } } enum { @@ -977,6 +1174,9 @@ struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args, f->conn.async_read = 1; f->conn.max_write = UINT_MAX; f->conn.max_readahead = UINT_MAX; + list_init_req(&f->list); + list_init_req(&f->interrupts); + mutex_init(&f->lock); if (fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1) goto out_free; -- 2.30.2