aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Pärtel <martin.partel@gmail.com>2016-07-31 19:39:11 +0100
committerMartin Pärtel <martin.partel@gmail.com>2016-07-31 19:39:11 +0100
commitbc7e55aee064c06709dcc3cb329158cb6b75f935 (patch)
tree9b9d05f13f3e39714909014d666a6f8833176037
parente8a36fedff1e2836716a2345bd5c6b3565e990a1 (diff)
downloadbindfs-bc7e55aee064c06709dcc3cb329158cb6b75f935.tar.gz
Implemented --enable-lock-forwarding.
This should address #36.
-rw-r--r--.gitignore1
-rw-r--r--src/bindfs.117
-rw-r--r--src/bindfs.c59
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/fcntl_locker.c67
-rwxr-xr-xtests/test_bindfs.rb41
6 files changed, 184 insertions, 4 deletions
diff --git a/.gitignore b/.gitignore
index 0d665a8..c7ff7e0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,6 +34,7 @@ Makefile
# Products
src/bindfs
+tests/fcntl_locker
tests/readdir_inode
tests/utimens_nofollow
tests/*.log
diff --git a/src/bindfs.1 b/src/bindfs.1
index fd2095d..f74cfc8 100644
--- a/src/bindfs.1
+++ b/src/bindfs.1
@@ -316,6 +316,23 @@ will be reflected in a mirrored file's ctime.
The underlying file's ctime will still be updated normally.
.TP
+.B \-\-enable\-lock\-forwarding, \-o enable\-lock\-forwarding
+Forwards \fBflock\fP and \fBfcntl\fP locking requests to the source directory.
+This way, locking a file in the bindfs mount will also lock the file in the
+source directory.
+
+This option \fBmust\fP be used with \fB\-\-multithreaded\fP because otherwise
+bindfs will deadlock as soon as there is lock contention. However, see
+\fB\%BUGS\fP below for caveats about \fB\-\-multithreaded\fP with the current
+implementation.
+
+.TP
+.B \-\-disable\-lock\-forwarding, \-o disable\-lock\-forwarding
+Currently does nothing, but a future release may default to enabling lock
+forwarding. If you depend on this behaviour, it's recommended to set this flag
+explicitly.
+
+.TP
.B \-\-enable\-ioctl, \-o enable\-ioctl
Enables forwarding of ioctl, which is needed for some advanced features such as
append-only files (\fBchattr +a\fP). Note that the ioctl action will be
diff --git a/src/bindfs.c b/src/bindfs.c
index 8ba92bf..82cbe0d 100644
--- a/src/bindfs.c
+++ b/src/bindfs.c
@@ -53,6 +53,7 @@
#endif
#include <sys/time.h>
#include <sys/statvfs.h>
+#include <sys/file.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
@@ -171,6 +172,8 @@ static struct Settings {
int ctime_from_mtime;
+ int enable_lock_forwarding;
+
int enable_ioctl;
uid_t uid_offset;
@@ -236,6 +239,9 @@ static int bindfs_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi);
static int bindfs_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi);
+static int bindfs_lock(const char *path, struct fuse_file_info *fi, int cmd,
+ struct flock *lock);
+static int bindfs_flock(const char *path, struct fuse_file_info *fi, int op);
static int bindfs_ioctl(const char *path, int cmd, void *arg,
struct fuse_file_info *fi, unsigned int flags,
void *data);
@@ -1096,15 +1102,34 @@ static int bindfs_write(const char *path, const char *buf, size_t size,
return res;
}
+static int bindfs_lock(const char *path, struct fuse_file_info *fi, int cmd,
+ struct flock *lock)
+{
+ int res = fcntl(fi->fh, cmd, lock);
+ if (res == -1) {
+ return -errno;
+ }
+ return 0;
+}
+
+static int bindfs_flock(const char *path, struct fuse_file_info *fi, int op)
+{
+ int res = flock(fi->fh, op);
+ if (res == -1) {
+ return -errno;
+ }
+ return 0;
+}
+
static int bindfs_ioctl(const char *path, int cmd, void *arg,
struct fuse_file_info *fi, unsigned int flags,
void *data)
{
- int ret = ioctl(fi->fh, cmd, data);
- if (ret == -1) {
+ int res = ioctl(fi->fh, cmd, data);
+ if (res == -1) {
return -errno;
}
- return ret;
+ return res;
}
static int bindfs_statfs(const char *path, struct statvfs *stbuf)
@@ -1352,6 +1377,8 @@ static struct fuse_operations bindfs_oper = {
.open = bindfs_open,
.read = bindfs_read,
.write = bindfs_write,
+ .lock = bindfs_lock,
+ .flock = bindfs_flock,
.ioctl = bindfs_ioctl,
.statfs = bindfs_statfs,
.release = bindfs_release,
@@ -1428,6 +1455,7 @@ static void print_usage(const char *progname)
" --realistic-permissions Hide permission bits for actions mounter can't do.\n"
" --ctime-from-mtime Read file properties' change time\n"
" from file content modification time.\n"
+ " --enable-lock-forwarding Forward locks to the underlying FS.\n"
" --enable-ioctl Forward ioctl() calls (as the mounter).\n"
" --hide-hard-links Always report a hard link count of 1.\n"
" --resolve-symlinks Resolve symbolic links.\n"
@@ -1469,6 +1497,8 @@ enum OptionKey {
OPTKEY_XATTR_READ_WRITE,
OPTKEY_REALISTIC_PERMISSIONS,
OPTKEY_CTIME_FROM_MTIME,
+ OPTKEY_ENABLE_LOCK_FORWARDING,
+ OPTKEY_DISABLE_LOCK_FORWARDING,
OPTKEY_ENABLE_IOCTL,
OPTKEY_HIDE_HARD_LINKS,
OPTKEY_RESOLVE_SYMLINKS,
@@ -1550,6 +1580,12 @@ static int process_option(void *data, const char *arg, int key,
case OPTKEY_CTIME_FROM_MTIME:
settings.ctime_from_mtime = 1;
return 0;
+ case OPTKEY_ENABLE_LOCK_FORWARDING:
+ settings.enable_lock_forwarding = 1;
+ return 0;
+ case OPTKEY_DISABLE_LOCK_FORWARDING:
+ settings.enable_lock_forwarding = 0;
+ return 0;
case OPTKEY_ENABLE_IOCTL:
settings.enable_ioctl = 1;
return 0;
@@ -1893,6 +1929,8 @@ int main(int argc, char *argv[])
OPT2("--realistic-permissions", "realistic-permissions", OPTKEY_REALISTIC_PERMISSIONS),
OPT2("--ctime-from-mtime", "ctime-from-mtime", OPTKEY_CTIME_FROM_MTIME),
+ OPT2("--enable-lock-forwarding", "enable-lock-forwarding", OPTKEY_ENABLE_LOCK_FORWARDING),
+ OPT2("--disable-lock-forwarding", "disable-lock-forwarding", OPTKEY_DISABLE_LOCK_FORWARDING),
OPT2("--enable-ioctl", "enable-ioctl", OPTKEY_ENABLE_IOCTL),
OPT_OFFSET2("--multithreaded", "multithreaded", multithreaded, -1),
@@ -1938,6 +1976,7 @@ int main(int argc, char *argv[])
settings.resolved_symlink_deletion_policy = RESOLVED_SYMLINK_DELETION_SYMLINK_ONLY;
settings.realistic_permissions = 0;
settings.ctime_from_mtime = 0;
+ settings.enable_lock_forwarding = 0;
settings.enable_ioctl = 0;
settings.uid_offset = 0;
settings.gid_offset = 0;
@@ -2180,6 +2219,20 @@ int main(int argc, char *argv[])
bindfs_oper.removexattr = NULL;
}
+ /* Check that lock forwarding is not enabled in single-threaded mode. */
+ if (settings.enable_lock_forwarding && !od.multithreaded) {
+ fprintf(stderr, "To use --enable-lock-forwarding, you must use "
+ "--multithreaded, but see the man page for caveats!\n");
+ return 1;
+ }
+
+ /* Remove the locking implementation unless the user has enabled lock
+ forwarding. FUSE implements locking inside the mountpoint by default. */
+ if (!settings.enable_lock_forwarding) {
+ bindfs_oper.lock = NULL;
+ bindfs_oper.flock = NULL;
+ }
+
/* Remove the ioctl implementation unless the user has enabled it */
if (!settings.enable_ioctl) {
bindfs_oper.ioctl = NULL;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index eca8563..66713e2 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,9 +1,10 @@
UNAME_S := $(shell uname -s)
-noinst_PROGRAMS = readdir_inode utimens_nofollow
+noinst_PROGRAMS = readdir_inode utimens_nofollow fcntl_locker
readdir_inode_SOURCES = readdir_inode.c
utimens_nofollow_SOURCES = utimens_nofollow.c
+fcntl_locker_SOURCES = fcntl_locker.c
TESTS = test_bindfs.rb
SUBDIRS = internals
diff --git a/tests/fcntl_locker.c b/tests/fcntl_locker.c
new file mode 100644
index 0000000..fa2e9dc
--- /dev/null
+++ b/tests/fcntl_locker.c
@@ -0,0 +1,67 @@
+// Takes two files and exits with 0 if fcntl-locking one fcntl-locks the other.
+// If the files don't fcntl-lock each other, returns 1.
+// If any other error occurs, returns 2.
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int main(int argc, const char* argv[]) {
+ if (argc != 3) {
+ fprintf(stderr, "expecting exactly two arguments\n");
+ return 2;
+ }
+
+ int fd1 = -1;
+ int fd2 = -1;
+ int result = 2;
+
+ fd1 = open(argv[1], O_RDWR);
+ if (fd1 == -1) {
+ perror("failed to open the first file");
+ goto exit;
+ }
+ fd2 = open(argv[2], O_RDWR);
+ if (fd2 == -1) {
+ perror("failed to open the second file");
+ goto exit;
+ }
+
+ struct flock lock;
+ memset(&lock, 0, sizeof(lock));
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ if (fcntl(fd1, F_SETLK, &lock) == -1) {
+ perror("fcntl F_SETLK on the first file failed");
+ goto exit;
+ }
+
+ memset(&lock, 0, sizeof(lock));
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ if (fcntl(fd2, F_SETLK, &lock) == -1) {
+ if (errno == EACCES || errno == EAGAIN) {
+ result = 0;
+ goto exit;
+ } else {
+ perror("fcntl F_SETLK on the second file failed");
+ goto exit;
+ }
+ } else {
+ result = 1;
+ goto exit;
+ }
+
+exit:
+ close(fd1);
+ close(fd2);
+ return result;
+}
diff --git a/tests/test_bindfs.rb b/tests/test_bindfs.rb
index 04c853d..c0ae5dd 100755
--- a/tests/test_bindfs.rb
+++ b/tests/test_bindfs.rb
@@ -616,6 +616,47 @@ testenv("", :title => "many files in a directory") do
assert { Dir.entries('mnt/dir').sort == expected_entries.sort }
end
+testenv("--enable-lock-forwarding --multithreaded", :title => "lock forwarding") do
+ File.write('src/file', 'some contents for fcntl lockng')
+ # (this test passes with an empty file as well, but this way is clearer)
+
+ # flock
+ File.open('mnt/file') do |f1|
+ File.open('src/file') do |f2|
+ assert { f1.flock(File::LOCK_EX | File::LOCK_NB) }
+ assert { !f2.flock(File::LOCK_EX | File::LOCK_NB) }
+ assert { f1.flock(File::LOCK_UN) }
+
+ assert { f2.flock(File::LOCK_EX | File::LOCK_NB) }
+ assert { !f1.flock(File::LOCK_EX | File::LOCK_NB) }
+ end
+ assert { f1.flock(File::LOCK_EX | File::LOCK_NB) }
+ end
+
+ # fcntl locking
+ system("#{$tests_dir}/fcntl_locker src/file mnt/file")
+ raise "fcntl lock sharing test failed" unless $?.success?
+end
+
+testenv("--disable-lock-forwarding", :title => "no lock forwarding") do
+ File.write('src/file', 'some contents for fcntl lockng')
+
+ # flock
+ File.open('mnt/file') do |f1|
+ File.open('src/file') do |f2|
+ assert { f1.flock(File::LOCK_EX | File::LOCK_NB) }
+ assert { f2.flock(File::LOCK_EX | File::LOCK_NB) }
+ end
+ File.open('mnt/file') do |f2|
+ assert { !f2.flock(File::LOCK_EX | File::LOCK_NB) }
+ end
+ end
+
+ # fcntl locking
+ system("#{$tests_dir}/fcntl_locker src/file mnt/file")
+ raise "fcntl lock sharing test failed" unless $?.exitstatus == 1
+end
+
# Issue #37
root_testenv("--enable-ioctl", :title => "append-only ioctl") do
touch('mnt/file')