summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2011-07-06 12:12:01 +0200
committerMiklos Szeredi <mszeredi@suse.cz>2011-07-06 12:12:01 +0200
commit8edeaa3f29b418f621542c72d77ba0c639e6d457 (patch)
tree2ed1297e688d1f37c7b92ce09dfb38025066900c
parentc605b5f73c458eeacdb6653e6899b5ebe7f1fc1a (diff)
downloadfuse-8edeaa3f29b418f621542c72d77ba0c639e6d457.tar.gz
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.
-rw-r--r--ChangeLog10
-rw-r--r--example/fusexmp_fh.c14
-rw-r--r--include/fuse.h23
-rw-r--r--include/fuse_common.h9
-rw-r--r--include/fuse_kernel.h9
-rw-r--r--include/fuse_lowlevel.h21
-rw-r--r--lib/fuse.c44
-rw-r--r--lib/fuse_i.h3
-rw-r--r--lib/fuse_lowlevel.c52
-rw-r--r--lib/modules/iconv.c13
-rw-r--r--lib/modules/subdir.c13
11 files changed, 200 insertions, 11 deletions
diff --git a/ChangeLog b/ChangeLog
index 0af2703..7073061 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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);
};
/**
diff --git a/lib/fuse.c b/lib/fuse.c
index 50f3d0d..f7740c3 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -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,