Hello community, here is the log from the commit of package bindfs for openSUSE:Factory checked in at 2016-09-27 13:45:30 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/bindfs (Old) and /work/SRC/openSUSE:Factory/.bindfs.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "bindfs" Changes: -------- --- /work/SRC/openSUSE:Factory/bindfs/bindfs.changes 2016-03-21 12:45:34.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.bindfs.new/bindfs.changes 2016-09-27 13:45:31.000000000 +0200 @@ -1,0 +2,7 @@ +Tue Sep 27 07:44:48 UTC 2016 - jeng...@inai.de + +- Update to new upstream release 1.13.2 +* Fix bug in readdir() introduced with 1.13.1. This fixes a bug + when a bindfs mount is exported over NFS. + +------------------------------------------------------------------- Old: ---- bindfs-1.13.1.tar.gz New: ---- bindfs-1.13.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ bindfs.spec ++++++ --- /var/tmp/diff_new_pack.lGyfxS/_old 2016-09-27 13:45:33.000000000 +0200 +++ /var/tmp/diff_new_pack.lGyfxS/_new 2016-09-27 13:45:33.000000000 +0200 @@ -17,7 +17,7 @@ Name: bindfs -Version: 1.13.1 +Version: 1.13.2 Release: 0 Summary: Mount Directories to other Locations and alter Permission Bits License: GPL-2.0+ ++++++ bindfs-1.13.1.tar.gz -> bindfs-1.13.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bindfs-1.13.1/.gitignore new/bindfs-1.13.2/.gitignore --- old/bindfs-1.13.1/.gitignore 2016-02-17 04:52:01.000000000 +0100 +++ new/bindfs-1.13.2/.gitignore 2016-09-25 20:32:36.000000000 +0200 @@ -34,6 +34,7 @@ # Products src/bindfs +tests/fcntl_locker tests/readdir_inode tests/utimens_nofollow tests/*.log diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bindfs-1.13.1/.travis.yml new/bindfs-1.13.2/.travis.yml --- old/bindfs-1.13.1/.travis.yml 2016-02-17 04:52:01.000000000 +0100 +++ new/bindfs-1.13.2/.travis.yml 2016-09-25 20:32:36.000000000 +0200 @@ -4,8 +4,10 @@ compiler: - clang - gcc -script: ./autogen.sh -d && make && sudo make check +script: ./autogen.sh -d && make && make check && sudo make check before_install: - sudo apt-get update - sudo apt-get install -qq fuse libfuse-dev valgrind - sudo /bin/sh -c 'echo user_allow_other > /etc/fuse.conf' + - sudo chmod 0777 /dev/fuse + - sudo chmod 0777 /etc/fuse.conf diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bindfs-1.13.1/ChangeLog new/bindfs-1.13.2/ChangeLog --- old/bindfs-1.13.1/ChangeLog 2016-02-17 04:52:01.000000000 +0100 +++ new/bindfs-1.13.2/ChangeLog 2016-09-25 20:32:36.000000000 +0200 @@ -1,3 +1,18 @@ +2016-09-25 Martin Pärtel <martin dot partel at gmail dot com> + + * Fix bug in readdir() introduced with 1.13.1. This fixes a bug when + a bindfs mount is exported over NFS (issue #39). + * Released 1.13.2 + +2016-07-31 Martin Pärtel <martin dot partel at gmail dot com> + + * Added --enable-ioctl to address issue #37. + * Added --enable-lock-forwarding to address issue #36. + +2016-05-03 Martin Pärtel <martin dot partel at gmail dot com> + + * @chenhaiq added --uid-offset and --gid-offset (PR #31). Thanks! + 2016-02-17 Martin Pärtel <martin dot partel at gmail dot com> * Fixed an issue that some users had with reading large diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bindfs-1.13.1/configure new/bindfs-1.13.2/configure --- old/bindfs-1.13.1/configure 2016-02-17 04:52:05.000000000 +0100 +++ new/bindfs-1.13.2/configure 2016-09-25 20:32:40.000000000 +0200 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for bindfs 1.13.1. +# Generated by GNU Autoconf 2.69 for bindfs 1.13.2. # # Report bugs to <martin.par...@gmail.com>. # @@ -590,8 +590,8 @@ # Identity of this package. PACKAGE_NAME='bindfs' PACKAGE_TARNAME='bindfs' -PACKAGE_VERSION='1.13.1' -PACKAGE_STRING='bindfs 1.13.1' +PACKAGE_VERSION='1.13.2' +PACKAGE_STRING='bindfs 1.13.2' PACKAGE_BUGREPORT='martin.par...@gmail.com' PACKAGE_URL='' @@ -1327,7 +1327,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures bindfs 1.13.1 to adapt to many kinds of systems. +\`configure' configures bindfs 1.13.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1397,7 +1397,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of bindfs 1.13.1:";; + short | recursive ) echo "Configuration of bindfs 1.13.2:";; esac cat <<\_ACEOF @@ -1513,7 +1513,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -bindfs configure 1.13.1 +bindfs configure 1.13.2 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1791,7 +1791,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by bindfs $as_me 1.13.1, which was +It was created by bindfs $as_me 1.13.2, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2655,7 +2655,7 @@ # Define the identity of the package. PACKAGE='bindfs' - VERSION='1.13.1' + VERSION='1.13.2' cat >>confdefs.h <<_ACEOF @@ -12253,7 +12253,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by bindfs $as_me 1.13.1, which was +This file was extended by bindfs $as_me 1.13.2, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -12319,7 +12319,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -bindfs config.status 1.13.1 +bindfs config.status 1.13.2 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bindfs-1.13.1/configure.ac new/bindfs-1.13.2/configure.ac --- old/bindfs-1.13.1/configure.ac 2016-02-17 04:52:01.000000000 +0100 +++ new/bindfs-1.13.2/configure.ac 2016-09-25 20:32:36.000000000 +0200 @@ -1,4 +1,4 @@ -AC_INIT([bindfs],[1.13.1],[martin.par...@gmail.com]) +AC_INIT([bindfs],[1.13.2],[martin.par...@gmail.com]) AM_INIT_AUTOMAKE([foreign serial-tests]) AC_CONFIG_HEADERS([config.h]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bindfs-1.13.1/src/bindfs.1 new/bindfs-1.13.2/src/bindfs.1 --- old/bindfs-1.13.1/src/bindfs.1 2016-02-17 04:52:01.000000000 +0100 +++ new/bindfs-1.13.2/src/bindfs.1 2016-09-25 20:32:36.000000000 +0200 @@ -67,6 +67,21 @@ Requires mounting as root. +.TP +.B \-\-uid\-offset=..., \-o uid\-offset=... +Works like \-\-map, but adds the given number to all file owner user IDs. +For instance, \fB--uid-offset=100000\fP causes a file owned by user \fI123\fP +to be shown as owned by user \fI100123\fP. + +For now, this option cannot be used together with \-\-map. Please file an issue +with the desired semantics if you have a case for using them together. + +Requires mounting as root. + +.TP +.B \-\-gid\-offset=..., \-o gid\-offset=... +Works exactly like \fB--uid-offset\fP but for groups. + .SH FILE CREATION POLICY New files and directories are created so they are owned by the mounter. @@ -301,6 +316,31 @@ 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 +performed as the mounter, not the calling user. No efforts are made to check +whether the calling user would ordinarily have the permissions to make the +ioctl. This may be a security concern, especially when mounting as root. + +.TP .B \-\-multithreaded, \-o multithreaded Run bindfs in multithreaded mode. While bindfs is designed to be otherwise thread-safe, there is currently a race condition that may pose diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bindfs-1.13.1/src/bindfs.c new/bindfs-1.13.2/src/bindfs.c --- old/bindfs-1.13.1/src/bindfs.c 2016-02-17 04:52:01.000000000 +0100 +++ new/bindfs-1.13.2/src/bindfs.c 2016-09-25 20:32:36.000000000 +0200 @@ -53,6 +53,8 @@ #endif #include <sys/time.h> #include <sys/statvfs.h> +#include <sys/file.h> +#include <sys/ioctl.h> #include <unistd.h> #include <fcntl.h> #include <dirent.h> @@ -87,6 +89,12 @@ #define XATTR_APPLE_PREFIX "com.apple." #endif +/* We pessimistically assume signed uid_t and gid_t in our overflow checks, + mostly because supporting both cases would require a bunch more code. */ +static const uid_t UID_T_MAX = ((1LL << (sizeof(uid_t)*8-1)) - 1); +static const gid_t GID_T_MAX = ((1LL << (sizeof(gid_t)*8-1)) - 1); +static const int UID_GID_OVERFLOW_ERRNO = EIO; + /* SETTINGS */ static struct Settings { const char *progname; @@ -164,6 +172,13 @@ int ctime_from_mtime; + int enable_lock_forwarding; + + int enable_ioctl; + + uid_t uid_offset; + gid_t gid_offset; + } settings; @@ -182,11 +197,17 @@ static int getattr_common(const char *path, struct stat *stbuf); /* Chowns a new file if necessary. */ -static void chown_new_file(const char *path, struct fuse_context *fc, int (*chown_func)(const char*, uid_t, gid_t)); +static int chown_new_file(const char *path, struct fuse_context *fc, int (*chown_func)(const char*, uid_t, gid_t)); /* Unified implementation of unlink and rmdir. */ static int delete_file(const char *path, int (*target_delete_func)(const char *)); +/* Apply offsets with overflow checking. */ +static int apply_uid_offset(uid_t *uid); +static int apply_gid_offset(gid_t *gid); +static int unapply_uid_offset(uid_t *uid); +static int unapply_gid_offset(gid_t *gid); + /* FUSE callbacks */ static void *bindfs_init(); static void bindfs_destroy(void *private_data); @@ -218,6 +239,12 @@ 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); static int bindfs_statfs(const char *path, struct statvfs *stbuf); static int bindfs_release(const char *path, struct fuse_file_info *fi); static int bindfs_fsync(const char *path, int isdatasync, @@ -316,6 +343,13 @@ stbuf->st_uid = usermap_get_uid_or_default(settings.usermap, stbuf->st_uid, stbuf->st_uid); stbuf->st_gid = usermap_get_gid_or_default(settings.usermap, stbuf->st_gid, stbuf->st_gid); + if (!apply_uid_offset(&stbuf->st_uid)) { + return -UID_GID_OVERFLOW_ERRNO; + } + if (!apply_gid_offset(&stbuf->st_gid)) { + return -UID_GID_OVERFLOW_ERRNO; + } + /* Report user-defined owner/group if specified */ if (settings.new_uid != -1) stbuf->st_uid = settings.new_uid; @@ -355,7 +389,7 @@ /* FIXME: another thread may race to see the old owner before the chown is done. Is there a scenario where this compromises security? Or application correctness? */ -static void chown_new_file(const char *path, struct fuse_context *fc, int (*chown_func)(const char*, uid_t, gid_t)) +static int chown_new_file(const char *path, struct fuse_context *fc, int (*chown_func)(const char*, uid_t, gid_t)) { uid_t file_owner; gid_t file_group; @@ -381,6 +415,13 @@ file_owner = usermap_get_uid_or_default(settings.usermap_reverse, fc->uid, file_owner); file_group = usermap_get_gid_or_default(settings.usermap_reverse, fc->gid, file_group); + if (!unapply_uid_offset(&file_owner)) { + return -UID_GID_OVERFLOW_ERRNO; + } + if (!unapply_gid_offset(&file_group)) { + return -UID_GID_OVERFLOW_ERRNO; + } + if (settings.create_for_uid != -1) file_owner = settings.create_for_uid; if (settings.create_for_gid != -1) @@ -391,6 +432,8 @@ DPRINTF("Failed to chown new file or directory (%d)", errno); } } + + return 0; } static int delete_file(const char *path, int (*target_delete_func)(const char *)) { @@ -463,6 +506,42 @@ return 0; } +static int apply_uid_offset(uid_t *uid) { + if (*uid > UID_T_MAX - settings.uid_offset) { + DPRINTF("UID %lld overflowed while applying offset", (long long)*uid); + return 0; + } + *uid += settings.uid_offset; + return 1; +} + +static int apply_gid_offset(gid_t *gid) { + if (*gid > GID_T_MAX - settings.gid_offset) { + DPRINTF("GID %lld overflowed while applying offset", (long long)*gid); + return 0; + } + *gid += settings.gid_offset; + return 1; +} + +static int unapply_uid_offset(uid_t *uid) { + if (*uid < settings.uid_offset) { + DPRINTF("UID %lld underflowed while unapplying offset", (long long)*uid); + return 0; + } + *uid -= settings.uid_offset; + return 1; +} + +static int unapply_gid_offset(gid_t *gid) { + if (*gid < settings.gid_offset) { + DPRINTF("GID %lld underflowed while unapplying offset", (long long)*gid); + return 0; + } + *gid -= settings.gid_offset; + return 1; +} + static void *bindfs_init() { @@ -594,7 +673,6 @@ } de_buf = malloc(offsetof(struct dirent, d_name) + pc_ret + 1); - seekdir(dp, offset); while (1) { result = readdir_r(dp, de_buf, &de); if (result != 0) { @@ -609,7 +687,9 @@ st.st_ino = de->d_ino; st.st_mode = de->d_type << 12; - // See issue #28 for why we pass a 0 offset to `filler`. + // See issue #28 for why we pass a 0 offset to `filler` and ignore + // `offset`. + // // Given a 0 offset, `filler` should never return non-zero, so we // consider it an error if it does. It is undocumented whether it sets // errno in that case, so we zero it first and set it ourself if it @@ -655,10 +735,10 @@ } fc = fuse_get_context(); - chown_new_file(real_path, fc, &chown); + res = chown_new_file(real_path, fc, &chown); free(real_path); - return 0; + return res; } static int bindfs_mkdir(const char *path, mode_t mode) @@ -681,10 +761,10 @@ } fc = fuse_get_context(); - chown_new_file(real_path, fc, &chown); + res = chown_new_file(real_path, fc, &chown); free(real_path); - return 0; + return res; } static int bindfs_unlink(const char *path) @@ -717,10 +797,10 @@ } fc = fuse_get_context(); - chown_new_file(real_to, fc, &lchown); + res = chown_new_file(real_to, fc, &lchown); free(real_to); - return 0; + return res; } static int bindfs_rename(const char *from, const char *to) @@ -843,6 +923,9 @@ switch (settings.chown_policy) { case CHOWN_NORMAL: uid = usermap_get_uid_or_default(settings.usermap_reverse, uid, uid); + if (!unapply_uid_offset(&uid)) { + return -UID_GID_OVERFLOW_ERRNO; + } break; case CHOWN_IGNORE: uid = -1; @@ -856,6 +939,9 @@ switch (settings.chgrp_policy) { case CHGRP_NORMAL: gid = usermap_get_gid_or_default(settings.usermap_reverse, gid, gid); + if (!unapply_gid_offset(&gid)) { + return -UID_GID_OVERFLOW_ERRNO; + } break; case CHGRP_IGNORE: gid = -1; @@ -1017,6 +1103,36 @@ 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 res = ioctl(fi->fh, cmd, data); + if (res == -1) { + return -errno; + } + return res; +} + static int bindfs_statfs(const char *path, struct statvfs *stbuf) { int res; @@ -1262,6 +1378,9 @@ .open = bindfs_open, .read = bindfs_read, .write = bindfs_write, + .lock = bindfs_lock, + .flock = bindfs_flock, + .ioctl = bindfs_ioctl, .statfs = bindfs_statfs, .release = bindfs_release, .fsync = bindfs_fsync, @@ -1292,6 +1411,8 @@ " -M --mirror-only=... Like --mirror but disallow access for\n" " all other users.\n" " --map=user1/user2:... Let user2 see files of user1 as his own.\n" + " --uid-offset=... Set file uid = uid + offset.\n" + " --gid-offset=... Set file gid = gid + offset.\n" "\n" "Permission bits:\n" " -p --perms=... Specify permissions, similar to chmod\n" @@ -1335,6 +1456,8 @@ " --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" " --resolved-symlink-deletion=... Decide how to delete resolved symlinks.\n" @@ -1375,6 +1498,9 @@ 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, OPTKEY_MULTITHREADED @@ -1455,6 +1581,15 @@ 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; case OPTKEY_HIDE_HARD_LINKS: settings.hide_hard_links = 1; return 0; @@ -1732,6 +1867,8 @@ char *resolved_symlink_deletion; int no_allow_other; int multithreaded; + char *uid_offset; + char *gid_offset; } od; #define OPT2(one, two, key) \ @@ -1793,7 +1930,13 @@ 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), + + OPT_OFFSET2("--uid-offset=%s", "uid-offset=%s", uid_offset, 0), + OPT_OFFSET2("--gid-offset=%s", "gid-offset=%s", gid_offset, 0), FUSE_OPT_END }; @@ -1834,6 +1977,11 @@ 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; + atexit(&atexit_func); /* Parse options */ @@ -1910,6 +2058,40 @@ } } + if (od.uid_offset) { + if (getuid() != 0) { + fprintf(stderr, "Error: You need to be root to use --uid-offset !\n"); + return 1; + } + if (od.map) { + fprintf(stderr, "Error: Cannot use --uid-offset and --map together!\n"); + return 1; + } + char* endptr = od.uid_offset; + settings.uid_offset = strtoul(od.uid_offset, &endptr, 10); + if (*endptr != '\0') { + fprintf(stderr, "Error: Value of --uid-offset must be a positive integer.\n"); + return 1; + } + } + + if (od.gid_offset) { + if (getuid() != 0) { + fprintf(stderr, "Error: You need to be root to use --gid-offset !\n"); + return 1; + } + if (od.map) { + fprintf(stderr, "Error: Cannot use --gid-offset and --map together!\n"); + return 1; + } + char* endptr = od.gid_offset; + settings.gid_offset = strtoul(od.gid_offset, &endptr, 10); + if (*endptr != '\0') { + fprintf(stderr, "Error: Value of --gid-offset must be a positive integer.\n"); + return 1; + } + } + /* Parse user and group for new creates */ if (od.create_for_user) { if (getuid() != 0) { @@ -2038,6 +2220,25 @@ 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; + } + /* fuse_main will daemonize by fork()'ing. The signal handler will persist. */ setup_signal_handling(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bindfs-1.13.1/tests/Makefile.am new/bindfs-1.13.2/tests/Makefile.am --- old/bindfs-1.13.1/tests/Makefile.am 2016-02-17 04:52:01.000000000 +0100 +++ new/bindfs-1.13.2/tests/Makefile.am 2016-09-25 20:32:36.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bindfs-1.13.1/tests/Makefile.in new/bindfs-1.13.2/tests/Makefile.in --- old/bindfs-1.13.1/tests/Makefile.in 2016-02-17 04:52:06.000000000 +0100 +++ new/bindfs-1.13.2/tests/Makefile.in 2016-09-25 20:32:40.000000000 +0200 @@ -78,7 +78,8 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -noinst_PROGRAMS = readdir_inode$(EXEEXT) utimens_nofollow$(EXEEXT) +noinst_PROGRAMS = readdir_inode$(EXEEXT) utimens_nofollow$(EXEEXT) \ + fcntl_locker$(EXEEXT) subdir = tests DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/depcomp @@ -91,13 +92,16 @@ CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = PROGRAMS = $(noinst_PROGRAMS) -am_readdir_inode_OBJECTS = readdir_inode.$(OBJEXT) -readdir_inode_OBJECTS = $(am_readdir_inode_OBJECTS) -readdir_inode_LDADD = $(LDADD) +am_fcntl_locker_OBJECTS = fcntl_locker.$(OBJEXT) +fcntl_locker_OBJECTS = $(am_fcntl_locker_OBJECTS) +fcntl_locker_LDADD = $(LDADD) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = +am_readdir_inode_OBJECTS = readdir_inode.$(OBJEXT) +readdir_inode_OBJECTS = $(am_readdir_inode_OBJECTS) +readdir_inode_LDADD = $(LDADD) am_utimens_nofollow_OBJECTS = utimens_nofollow.$(OBJEXT) utimens_nofollow_OBJECTS = $(am_utimens_nofollow_OBJECTS) utimens_nofollow_LDADD = $(LDADD) @@ -135,8 +139,10 @@ am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = -SOURCES = $(readdir_inode_SOURCES) $(utimens_nofollow_SOURCES) -DIST_SOURCES = $(readdir_inode_SOURCES) $(utimens_nofollow_SOURCES) +SOURCES = $(fcntl_locker_SOURCES) $(readdir_inode_SOURCES) \ + $(utimens_nofollow_SOURCES) +DIST_SOURCES = $(fcntl_locker_SOURCES) $(readdir_inode_SOURCES) \ + $(utimens_nofollow_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ @@ -351,6 +357,7 @@ UNAME_S := $(shell uname -s) readdir_inode_SOURCES = readdir_inode.c utimens_nofollow_SOURCES = utimens_nofollow.c +fcntl_locker_SOURCES = fcntl_locker.c TESTS = test_bindfs.rb SUBDIRS = internals all: all-recursive @@ -397,6 +404,10 @@ echo " rm -f" $$list; \ rm -f $$list +fcntl_locker$(EXEEXT): $(fcntl_locker_OBJECTS) $(fcntl_locker_DEPENDENCIES) $(EXTRA_fcntl_locker_DEPENDENCIES) + @rm -f fcntl_locker$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(fcntl_locker_OBJECTS) $(fcntl_locker_LDADD) $(LIBS) + readdir_inode$(EXEEXT): $(readdir_inode_OBJECTS) $(readdir_inode_DEPENDENCIES) $(EXTRA_readdir_inode_DEPENDENCIES) @rm -f readdir_inode$(EXEEXT) $(AM_V_CCLD)$(LINK) $(readdir_inode_OBJECTS) $(readdir_inode_LDADD) $(LIBS) @@ -411,6 +422,7 @@ distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fcntl_locker.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readdir_inode.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utimens_nofollow.Po@am__quote@ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bindfs-1.13.1/tests/common.rb new/bindfs-1.13.2/tests/common.rb --- old/bindfs-1.13.1/tests/common.rb 2016-02-17 04:52:01.000000000 +0100 +++ new/bindfs-1.13.2/tests/common.rb 2016-09-25 20:32:36.000000000 +0200 @@ -66,6 +66,15 @@ true end +def with_umask(umask, &block) + old_umask = File.umask(umask) + begin + block.call + ensure + File.umask(old_umask) + end +end + def valgrind_options opt = ARGV.find {|s| s.start_with?('--valgrind') } if opt == nil diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bindfs-1.13.1/tests/fcntl_locker.c new/bindfs-1.13.2/tests/fcntl_locker.c --- old/bindfs-1.13.1/tests/fcntl_locker.c 1970-01-01 01:00:00.000000000 +0100 +++ new/bindfs-1.13.2/tests/fcntl_locker.c 2016-09-25 20:32:36.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bindfs-1.13.1/tests/test_bindfs.rb new/bindfs-1.13.2/tests/test_bindfs.rb --- old/bindfs-1.13.1/tests/test_bindfs.rb 2016-02-17 04:52:01.000000000 +0100 +++ new/bindfs-1.13.2/tests/test_bindfs.rb 2016-09-25 20:32:36.000000000 +0200 @@ -95,11 +95,23 @@ end testenv("--create-with-perms=og=r:ogd+x") do - touch('src/file') - mkdir('src/dir') + with_umask(0077) do + touch('mnt/file') + mkdir('mnt/dir') + end + + assert { File.stat('mnt/file').mode & 0777 == 0644 } + assert { File.stat('mnt/dir').mode & 0777 == 0755 } +end + +testenv("--create-with-perms=g+rD") do + with_umask(0077) do + touch('mnt/file') + mkdir('mnt/dir') + end - assert { File.stat('mnt/file').mode & 0077 == 0044 } - assert { File.stat('mnt/dir').mode & 0077 == 0055 } + assert { File.stat('src/file').mode & 0777 == 0640 } + assert { File.stat('src/dir').mode & 0777 == 0750 } end testenv("-p 0777 --realistic-permissions", :title => '--realistic-permissions') do @@ -337,6 +349,39 @@ assert { File.stat('mnt/file1').gid == 1 } end +root_testenv("--uid-offset=2") do + touch('src/file') + chown(1, nil, 'src/file') + + assert { File.stat('mnt/file').uid == 3 } +end + +root_testenv("--gid-offset=2") do + touch('src/file') + chown(nil, 1, 'src/file') + + assert { File.stat('mnt/file').gid == 3 } +end + +root_testenv("--uid-offset=2 --gid-offset=20", :title => "file creation with --uid-offset and --gid-offset") do + touch('mnt/file') + + assert { File.stat('mnt/file').uid == 2 } + assert { File.stat('mnt/file').gid == 20 } + assert { File.stat('src/file').uid == 0 } + assert { File.stat('src/file').gid == 0 } +end + +root_testenv("--uid-offset=2 --gid-offset=20", :title => "chown/chgrp with --uid-offset and --gid-offset") do + touch('src/file') + chown(6, 25, 'mnt/file') + + assert { File.stat('mnt/file').uid == 6 } + assert { File.stat('mnt/file').gid == 25 } + assert { File.stat('src/file').uid == 4 } + assert { File.stat('src/file').gid == 5 } +end + testenv("", :title => "preserves inode numbers") do touch('src/file') mkdir('src/dir') @@ -571,6 +616,68 @@ 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') + system('chattr +a mnt/file') + raise 'chattr +a failed' unless $?.success? + begin + File.open('mnt/file', 'a') do |f| + f.write('stuff') + end + assert { File.read('mnt/file') == 'stuff' } + assert_exception(EPERM) { File.write('mnt/file', 'newstuff') } + ensure + system('chattr -a mnt/file') + end +end + +root_testenv("", :title => "ioctl not enabled by default") do + touch('mnt/file') + assert { `chattr +a mnt/file 2>&1`; !$?.success? } +end + # FIXME: this stuff around testenv is a hax, and testenv may also exit(), which defeats the 'ensure' below. # the test setup ought to be refactored. It might well use MiniTest or something. if Process.uid == 0 @@ -583,7 +690,7 @@ chown('nobody', nil, 'src/file') assert { File.stat('mnt/file').uid == $nobody_uid } - `usermod root -G bindfs_test_group -a` + `usermod -G bindfs_test_group -a root` raise "Failed to add root to test group" if !$?.success? # Cache not refreshed yet