diff options
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | example/fusexmp_fh.c | 14 | ||||
-rw-r--r-- | include/fuse.h | 23 | ||||
-rw-r--r-- | include/fuse_common.h | 9 | ||||
-rw-r--r-- | include/fuse_kernel.h | 9 | ||||
-rw-r--r-- | include/fuse_lowlevel.h | 21 | ||||
-rw-r--r-- | lib/fuse.c | 44 | ||||
-rw-r--r-- | lib/fuse_i.h | 3 | ||||
-rw-r--r-- | lib/fuse_lowlevel.c | 52 | ||||
-rw-r--r-- | lib/modules/iconv.c | 13 | ||||
-rw-r--r-- | lib/modules/subdir.c | 13 |
11 files changed, 200 insertions, 11 deletions
@@ -1,3 +1,13 @@ +2011-07-06 Miklos Szeredi <miklos@szeredi.hu> + + * Add ->flock() operation to low and high level interfaces. This + fixes problems with emulating flock() with POSIX locking. + Reported by Sebastian Pipping. As with lock/setlk/getlk most + filesystems don't need to implement this, as the kernel takes care + of file locking. The only reason to implement locking operations + is for network filesystems which want file locking to work between + clients. + 2011-07-02 Sebastian Pipping <sebastian@pipping.org> * Make xmp_utimens of examples "fusexmp" and "fusexmp_fh" diff --git a/example/fusexmp_fh.c b/example/fusexmp_fh.c index 96427b3..046185c 100644 --- a/example/fusexmp_fh.c +++ b/example/fusexmp_fh.c @@ -31,6 +31,7 @@ #ifdef HAVE_SETXATTR #include <sys/xattr.h> #endif +#include <sys/file.h> /* flock(2) */ static int xmp_getattr(const char *path, struct stat *stbuf) { @@ -481,6 +482,18 @@ static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd, sizeof(fi->lock_owner)); } +static int xmp_flock(const char *path, struct fuse_file_info *fi, int op) +{ + int res; + (void) path; + + res = flock(fi->fh, op); + if (res == -1) + return -errno; + + return 0; +} + static struct fuse_operations xmp_oper = { .getattr = xmp_getattr, .fgetattr = xmp_fgetattr, @@ -518,6 +531,7 @@ static struct fuse_operations xmp_oper = { .removexattr = xmp_removexattr, #endif .lock = xmp_lock, + .flock = xmp_flock, .flag_nullpath_ok = 1, }; diff --git a/include/fuse.h b/include/fuse.h index 7e52719..b05152d 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -548,6 +548,27 @@ struct fuse_operations { */ int (*read_buf) (const char *, struct fuse_bufvec **bufp, size_t size, off_t off, struct fuse_file_info *); + /** + * Perform BSD file locking operation + * + * The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN + * + * Nonblocking requests will be indicated by ORing LOCK_NB to + * the above operations + * + * For more information see the flock(2) manual page. + * + * Additionally fi->owner will be set to a value unique to + * this open file. This same value will be supplied to + * ->release() when the file is released. + * + * Note: if this method is not implemented, the kernel will still + * allow file locking to work locally. Hence it is only + * interesting for network filesystems and similar. + * + * Introduced in version 2.9 + */ + int (*flock) (const char *, struct fuse_file_info *, int op); }; /** Extra context that may be needed by some filesystems @@ -813,6 +834,8 @@ 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_flock(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi, int op); 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); diff --git a/include/fuse_common.h b/include/fuse_common.h index db17fa6..76f7200 100644 --- a/include/fuse_common.h +++ b/include/fuse_common.h @@ -70,8 +70,14 @@ struct fuse_file_info { seekable. Introduced in version 2.8 */ unsigned int nonseekable : 1; + /* Indicates that flock locks for this file should be + released. If set, lock_owner shall contain a valid value. + May only be set in ->release(). Introduced in version + 2.9 */ + unsigned int flock_release : 1; + /** Padding. Do not use*/ - unsigned int padding : 28; + unsigned int padding : 27; /** File handle. May be filled in by filesystem in open(). Available in all other file operations */ @@ -103,6 +109,7 @@ struct fuse_file_info { #define FUSE_CAP_SPLICE_WRITE (1 << 7) #define FUSE_CAP_SPLICE_MOVE (1 << 8) #define FUSE_CAP_SPLICE_READ (1 << 9) +#define FUSE_CAP_FLOCK_LOCKS (1 << 10) /** * Ioctl flags diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h index ab8b94e..8b65f46 100644 --- a/include/fuse_kernel.h +++ b/include/fuse_kernel.h @@ -73,6 +73,9 @@ * - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct * fuse_ioctl_iovec' instead of ambiguous 'struct iovec' * - add FUSE_IOCTL_32BIT flag + * + * 7.17 + * - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK */ #ifndef _LINUX_FUSE_H @@ -109,7 +112,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 16 +#define FUSE_KERNEL_MINOR_VERSION 17 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -184,8 +187,10 @@ struct fuse_file_lock { /** * INIT request/reply flags * + * FUSE_POSIX_LOCKS: remote locking for POSIX file locks * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".." * FUSE_DONT_MASK: don't apply umask to file mode on create operations + * FUSE_FLOCK_LOCKS: remote locking for BSD style file locks */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) @@ -194,6 +199,7 @@ struct fuse_file_lock { #define FUSE_EXPORT_SUPPORT (1 << 4) #define FUSE_BIG_WRITES (1 << 5) #define FUSE_DONT_MASK (1 << 6) +#define FUSE_FLOCK_LOCKS (1 << 10) /** * CUSE INIT request/reply flags @@ -206,6 +212,7 @@ struct fuse_file_lock { * Release flags */ #define FUSE_RELEASE_FLUSH (1 << 0) +#define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1) /** * Getattr flags diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index e38fe92..7c00f76 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -796,7 +796,7 @@ struct fuse_lowlevel_ops { * @param req request handle * @param ino the inode number * @param fi file information - * @param lock the region/type to test + * @param lock the region/type to set * @param sleep locking operation may sleep */ void (*setlk) (fuse_req_t req, fuse_ino_t ino, @@ -932,6 +932,25 @@ struct fuse_lowlevel_ops { void (*forget_multi) (fuse_req_t req, size_t count, struct fuse_forget_data *forgets); + /** + * Acquire, modify or release a BSD file lock + * + * Note: if the locking methods are not implemented, the kernel + * will still allow file locking to work locally. Hence these are + * only interesting for network filesystems and similar. + * + * Introduced in version 2.9 + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * @param op the locking operation, see flock(2) + */ + void (*flock) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, int op); }; /** @@ -1933,6 +1933,27 @@ int fuse_fs_lock(struct fuse_fs *fs, const char *path, } } +int fuse_fs_flock(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi, int op) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.flock) { + if (fs->debug) { + int xop = op & ~LOCK_NB; + + fprintf(stderr, "lock[%llu] %s%s\n", + (unsigned long long) fi->fh, + xop == LOCK_SH ? "LOCK_SH" : + (xop == LOCK_EX ? "LOCK_EX" : + (xop == LOCK_UN ? "LOCK_UN" : "???")), + (op & LOCK_NB) ? "|LOCK_NB" : ""); + } + return fs->op.flock(path, fi, op); + } 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; @@ -2410,6 +2431,10 @@ void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn) fuse_get_context()->private_data = fs->user_data; if (!fs->op.write_buf) conn->want &= ~FUSE_CAP_SPLICE_READ; + if (!fs->op.lock) + conn->want &= ~FUSE_CAP_POSIX_LOCKS; + if (!fs->op.flock) + conn->want &= ~FUSE_CAP_FLOCK_LOCKS; if (fs->op.init) fs->user_data = fs->op.init(conn); } @@ -3754,6 +3779,24 @@ static void fuse_lib_setlk(fuse_req_t req, fuse_ino_t ino, reply_err(req, err); } +static void fuse_lib_flock(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, int op) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + err = get_path_nullok(f, ino, &path); + if (err == 0) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_flock(f->fs, path, fi, op); + fuse_finish_interrupt(f, req, &d); + free_path(f, ino, path); + } + reply_err(req, err); +} + static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx) { @@ -3939,6 +3982,7 @@ static struct fuse_lowlevel_ops fuse_path_ops = { .removexattr = fuse_lib_removexattr, .getlk = fuse_lib_getlk, .setlk = fuse_lib_setlk, + .flock = fuse_lib_flock, .bmap = fuse_lib_bmap, .ioctl = fuse_lib_ioctl, .poll = fuse_lib_poll, diff --git a/lib/fuse_i.h b/lib/fuse_i.h index dd98737..78f1467 100644 --- a/lib/fuse_i.h +++ b/lib/fuse_i.h @@ -62,7 +62,8 @@ struct fuse_ll { int debug; int allow_root; int atomic_o_trunc; - int no_remote_lock; + int no_remote_posix_lock; + int no_remote_flock; int big_writes; int splice_write; int splice_move; diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index 3cf747d..b101523 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -1322,6 +1322,10 @@ static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; fi.lock_owner = arg->lock_owner; } + if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) { + fi.flock_release = 1; + fi.lock_owner = arg->lock_owner; + } if (req->f->op.release) req->f->op.release(req, nodeid, &fi); @@ -1505,11 +1509,34 @@ static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, fi.fh = arg->fh; fi.lock_owner = arg->owner; - convert_fuse_file_lock(&arg->lk, &flock); - if (req->f->op.setlk) - req->f->op.setlk(req, nodeid, &fi, &flock, sleep); - else - fuse_reply_err(req, ENOSYS); + if (arg->lk_flags & FUSE_LK_FLOCK) { + int op = 0; + + switch (arg->lk.type) { + case F_RDLCK: + op = LOCK_SH; + break; + case F_WRLCK: + op = LOCK_EX; + break; + case F_UNLCK: + op = LOCK_UN; + break; + } + if (!sleep) + op |= LOCK_NB; + + if (req->f->op.flock) + req->f->op.flock(req, nodeid, &fi, op); + else + fuse_reply_err(req, ENOSYS); + } else { + convert_fuse_file_lock(&arg->lk, &flock); + if (req->f->op.setlk) + req->f->op.setlk(req, nodeid, &fi, &flock, sleep); + else + fuse_reply_err(req, ENOSYS); + } } static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) @@ -1726,6 +1753,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) f->conn.capable |= FUSE_CAP_BIG_WRITES; if (arg->flags & FUSE_DONT_MASK) f->conn.capable |= FUSE_CAP_DONT_MASK; + if (arg->flags & FUSE_FLOCK_LOCKS) + f->conn.capable |= FUSE_CAP_FLOCK_LOCKS; } else { f->conn.async_read = 0; f->conn.max_readahead = 0; @@ -1748,8 +1777,10 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) if (f->atomic_o_trunc) f->conn.want |= FUSE_CAP_ATOMIC_O_TRUNC; - if (f->op.getlk && f->op.setlk && !f->no_remote_lock) + if (f->op.getlk && f->op.setlk && !f->no_remote_posix_lock) f->conn.want |= FUSE_CAP_POSIX_LOCKS; + if (f->op.flock && !f->no_remote_flock) + f->conn.want |= FUSE_CAP_FLOCK_LOCKS; if (f->big_writes) f->conn.want |= FUSE_CAP_BIG_WRITES; @@ -1786,6 +1817,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) outarg.flags |= FUSE_BIG_WRITES; if (f->conn.want & FUSE_CAP_DONT_MASK) outarg.flags |= FUSE_DONT_MASK; + if (f->conn.want & FUSE_CAP_FLOCK_LOCKS) + outarg.flags |= FUSE_FLOCK_LOCKS; outarg.max_readahead = f->conn.max_readahead; outarg.max_write = f->conn.max_write; if (f->conn.proto_minor >= 13) { @@ -2360,7 +2393,10 @@ static struct fuse_opt fuse_ll_opts[] = { { "async_read", offsetof(struct fuse_ll, conn.async_read), 1 }, { "sync_read", offsetof(struct fuse_ll, conn.async_read), 0 }, { "atomic_o_trunc", offsetof(struct fuse_ll, atomic_o_trunc), 1}, - { "no_remote_lock", offsetof(struct fuse_ll, no_remote_lock), 1}, + { "no_remote_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1}, + { "no_remote_lock", offsetof(struct fuse_ll, no_remote_flock), 1}, + { "no_remote_flock", offsetof(struct fuse_ll, no_remote_flock), 1}, + { "no_remote_posix_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1}, { "big_writes", offsetof(struct fuse_ll, big_writes), 1}, { "splice_write", offsetof(struct fuse_ll, splice_write), 1}, { "no_splice_write", offsetof(struct fuse_ll, no_splice_write), 1}, @@ -2394,6 +2430,8 @@ static void fuse_ll_help(void) " -o atomic_o_trunc enable atomic open+truncate support\n" " -o big_writes enable larger than 4kB writes\n" " -o no_remote_lock disable remote file locking\n" +" -o no_remote_flock disable remote file locking (BSD)\n" +" -o no_remote_posix_lock disable remove file locking (POSIX)\n" " -o [no_]splice_write use splice to write to the fuse device\n" " -o [no_]splice_move move data while splicing to the fuse device\n" " -o [no_]splice_read use splice to read from the fuse device\n" diff --git a/lib/modules/iconv.c b/lib/modules/iconv.c index b9946d9..1b78192 100644 --- a/lib/modules/iconv.c +++ b/lib/modules/iconv.c @@ -549,6 +549,18 @@ static int iconv_lock(const char *path, struct fuse_file_info *fi, int cmd, return err; } +static int iconv_flock(const char *path, struct fuse_file_info *fi, int op) +{ + struct iconv *ic = iconv_get(); + char *newpath; + int err = iconv_convpath(ic, path, &newpath, 0); + if (!err) { + err = fuse_fs_flock(ic->next, newpath, fi, op); + free(newpath); + } + return err; +} + static int iconv_bmap(const char *path, size_t blocksize, uint64_t *idx) { struct iconv *ic = iconv_get(); @@ -616,6 +628,7 @@ static struct fuse_operations iconv_oper = { .listxattr = iconv_listxattr, .removexattr = iconv_removexattr, .lock = iconv_lock, + .flock = iconv_flock, .bmap = iconv_bmap, .flag_nullpath_ok = 1, diff --git a/lib/modules/subdir.c b/lib/modules/subdir.c index 6d9ac89..24dede6 100644 --- a/lib/modules/subdir.c +++ b/lib/modules/subdir.c @@ -536,6 +536,18 @@ static int subdir_lock(const char *path, struct fuse_file_info *fi, int cmd, return err; } +static int subdir_flock(const char *path, struct fuse_file_info *fi, int op) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_flock(d->next, newpath, fi, op); + free(newpath); + } + return err; +} + static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx) { struct subdir *d = subdir_get(); @@ -599,6 +611,7 @@ static struct fuse_operations subdir_oper = { .listxattr = subdir_listxattr, .removexattr = subdir_removexattr, .lock = subdir_lock, + .flock = subdir_flock, .bmap = subdir_bmap, .flag_nullpath_ok = 1, |