aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--ChangeLog.rst15
-rw-r--r--Makefile.am15
-rw-r--r--example/hello_ll.c2
-rw-r--r--example/notify_inval_entry.c2
-rw-r--r--example/notify_inval_inode.c2
-rw-r--r--example/notify_store_retrieve.c2
-rw-r--r--example/passthrough_ll.c2
-rw-r--r--include/fuse.h4
-rw-r--r--include/fuse_lowlevel.h21
-rw-r--r--lib/cuse_lowlevel.c2
-rw-r--r--lib/fuse.c4
-rw-r--r--lib/fuse_i.h20
-rw-r--r--lib/fuse_loop_mt.c8
-rw-r--r--lib/fuse_lowlevel.c73
-rw-r--r--lib/helper.c4
-rwxr-xr-xtest/test_examples.py9
-rw-r--r--test/util.py23
18 files changed, 133 insertions, 77 deletions
diff --git a/.travis.yml b/.travis.yml
index 6d2f313..b7bed8f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -30,4 +30,4 @@ script:
- ./configure
- CFLAGS="-fsanitize=address,undefined -g -O1 -Wall -Werror" make
- doxygen doc/Doxyfile
- - python -m pytest test/
+ - make test
diff --git a/ChangeLog.rst b/ChangeLog.rst
index 4f355ce..a498273 100644
--- a/ChangeLog.rst
+++ b/ChangeLog.rst
@@ -1,6 +1,14 @@
Unreleased Changes
==================
+* The `fuse_session_new` function no longer accepts the ``-o
+ clone_fd`` option. Instead, this has become a parameter of the
+ `fuse_session_loop_mt` and ``fuse_loop_mt` functions.
+
+* For low-level file systems that implement the `write_buf` handler,
+ the `splice_read` option is now enabled by default. As usual, this
+ can be changed in the file system's `init` handler.
+
* `fuse_session_new` now treats low-level options more consistently:
First, options are used to modify FUSE defaults. Second, the file
system may inspect and/or adjust the settings in its `init`
@@ -132,6 +140,13 @@ FUSE 3.0.0pre0 (2016-10-03)
descriptor for each processing thread, which might improve
performance.
+* Added *writeback_cache* option. With kernel 3.14 and newer this
+ enables write-back caching which can significantly improve
+ performance.
+
+* Added *async_dio* option. With kernel 3.13 and newer, this allows
+ direct I/O to be done asynchronously.
+
* The (high- and low-level) `rename` handlers now takes a *flags*
parameter (with values corresponding to the *renameat2* system call
introduced in Linux 3.15).
diff --git a/Makefile.am b/Makefile.am
index 54aee13..25d88d0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -15,6 +15,19 @@ pkgconfig_DATA = fuse3.pc
$(pkgconfig_DATA): config.status
+.PHONY: setuid_fusermount
+setuid_fusermount:
+ @echo "Attempting to use sudo to make util/fusermount setuid root"
+ @echo "If this fails, set permissions manually and re-run make test"
+ test $$(ls -n util/fusermount | awk 'NR==1 {print $$3}') -eq 0 || \
+ sudo chown root util/fusermount
+ test -u util/fusermount || \
+ sudo chmod u+s util/fusermount
+
+# If we are not root, util/fusermount needs to be setuid root
+# for tests to work.
+
+test_deps = $(shell [ "$${UID}" -eq 0 ] || echo setuid_fusermount)
.PHONY: test
-test: all
+test: all $(test_deps)
python3 -m pytest test/
diff --git a/example/hello_ll.c b/example/hello_ll.c
index b830cb2..e0ce610 100644
--- a/example/hello_ll.c
+++ b/example/hello_ll.c
@@ -224,7 +224,7 @@ int main(int argc, char *argv[])
if (opts.singlethread)
ret = fuse_session_loop(se);
else
- ret = fuse_session_loop_mt(se);
+ ret = fuse_session_loop_mt(se, opts.clone_fd);
fuse_session_unmount(se);
err_out3:
diff --git a/example/notify_inval_entry.c b/example/notify_inval_entry.c
index 898eff1..1c2a6c9 100644
--- a/example/notify_inval_entry.c
+++ b/example/notify_inval_entry.c
@@ -327,7 +327,7 @@ int main(int argc, char *argv[]) {
if (opts.singlethread)
ret = fuse_session_loop(se);
else
- ret = fuse_session_loop_mt(se);
+ ret = fuse_session_loop_mt(se, opts.clone_fd);
fuse_session_unmount(se);
err_out3:
diff --git a/example/notify_inval_inode.c b/example/notify_inval_inode.c
index 1b813a0..c9ab4d8 100644
--- a/example/notify_inval_inode.c
+++ b/example/notify_inval_inode.c
@@ -350,7 +350,7 @@ int main(int argc, char *argv[]) {
if (opts.singlethread)
ret = fuse_session_loop(se);
else
- ret = fuse_session_loop_mt(se);
+ ret = fuse_session_loop_mt(se, opts.clone_fd);
fuse_session_unmount(se);
err_out3:
diff --git a/example/notify_store_retrieve.c b/example/notify_store_retrieve.c
index 76f3291..5b5fa63 100644
--- a/example/notify_store_retrieve.c
+++ b/example/notify_store_retrieve.c
@@ -393,7 +393,7 @@ int main(int argc, char *argv[]) {
if (opts.singlethread)
ret = fuse_session_loop(se);
else
- ret = fuse_session_loop_mt(se);
+ ret = fuse_session_loop_mt(se, opts.clone_fd);
assert(retrieve_status != 1);
fuse_session_unmount(se);
diff --git a/example/passthrough_ll.c b/example/passthrough_ll.c
index 66f92cf..df9d7d3 100644
--- a/example/passthrough_ll.c
+++ b/example/passthrough_ll.c
@@ -504,7 +504,7 @@ int main(int argc, char *argv[])
if (opts.singlethread)
ret = fuse_session_loop(se);
else
- ret = fuse_session_loop_mt(se);
+ ret = fuse_session_loop_mt(se, opts.clone_fd);
fuse_session_unmount(se);
err_out3:
diff --git a/include/fuse.h b/include/fuse.h
index 8943835..5b9082b 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -748,11 +748,13 @@ void fuse_exit(struct fuse *f);
* in the callback function of fuse_operations is also thread-safe.
*
* @param f the FUSE handle
+ * @param clone_fd whether to use separate device fds for each thread
+ * (may increase performance)
* @return 0 if no error occurred, -1 otherwise
*
* See also: fuse_loop()
*/
-int fuse_loop_mt(struct fuse *f);
+int fuse_loop_mt(struct fuse *f, int clone_fd);
/**
* Get the current context
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index bad1d45..0b7ee2b 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -420,12 +420,18 @@ struct fuse_lowlevel_ops {
/**
* Open a file
*
- * Open flags (with the exception of O_CREAT, O_EXCL, O_NOCTTY and
- * O_TRUNC) are available in fi->flags.
+ * Open flags are available in fi->flags. Creation (O_CREAT,
+ * O_EXCL, O_NOCTTY) and by default also truncation (O_TRUNC)
+ * flags will be filtered out. If an application specifies
+ * O_TRUNC, fuse first calls truncate() and then open().
*
- * Filesystem may store an arbitrary file handle (pointer, index,
- * etc) in fi->fh, and use this in other all other file operations
- * (read, write, flush, release, fsync).
+ * If filesystem is able to handle O_TRUNC directly, the
+ * init() handler should set the `FUSE_CAP_ATOMIC_O_TRUNC` bit
+ * in ``conn->want``.
+ *
+ * Filesystem may store an arbitrary file handle (pointer,
+ * index, etc) in fi->fh, and use this in other all other file
+ * operations (read, write, flush, release, fsync).
*
* Filesystem may also implement stateless file I/O and not store
* anything in fi->fh.
@@ -1651,6 +1657,7 @@ struct fuse_cmdline_opts {
char *mountpoint;
int show_version;
int show_help;
+ int clone_fd;
};
/**
@@ -1721,9 +1728,11 @@ int fuse_session_loop(struct fuse_session *se);
* Enter a multi-threaded event loop
*
* @param se the session
+ * @param clone_fd whether to use separate device fds for each thread
+ * (may increase performance)
* @return 0 on success, -1 on error
*/
-int fuse_session_loop_mt(struct fuse_session *se);
+int fuse_session_loop_mt(struct fuse_session *se, int clone_fd);
/**
* Flag a session as terminated.
diff --git a/lib/cuse_lowlevel.c b/lib/cuse_lowlevel.c
index c9aa47d..49fc7d4 100644
--- a/lib/cuse_lowlevel.c
+++ b/lib/cuse_lowlevel.c
@@ -350,7 +350,7 @@ int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
return 1;
if (multithreaded)
- res = fuse_session_loop_mt(se);
+ res = fuse_session_loop_mt(se, 0);
else
res = fuse_session_loop(se);
diff --git a/lib/fuse.c b/lib/fuse.c
index 3304d68..fb15b04 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -4378,7 +4378,7 @@ int fuse_loop(struct fuse *f)
return fuse_session_loop(f->se);
}
-int fuse_loop_mt(struct fuse *f)
+int fuse_loop_mt(struct fuse *f, int clone_fd)
{
if (f == NULL)
return -1;
@@ -4387,7 +4387,7 @@ int fuse_loop_mt(struct fuse *f)
if (res)
return -1;
- res = fuse_session_loop_mt(fuse_get_session(f));
+ res = fuse_session_loop_mt(fuse_get_session(f), clone_fd);
fuse_stop_cleanup_thread(f);
return res;
}
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index c8aa279..5ed23c7 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -41,14 +41,7 @@ struct fuse_notify_req {
struct fuse_notify_req *prev;
};
-struct fuse_session {
- char *mountpoint;
- volatile int exited;
- int fd;
- struct mount_opts *mo;
-
- int debug;
- int allow_root;
+struct session_opts {
int atomic_o_trunc;
int no_remote_posix_lock;
int no_remote_flock;
@@ -66,9 +59,18 @@ struct fuse_session {
int no_async_dio;
int writeback_cache;
int no_writeback_cache;
- int clone_fd;
int async_read;
int sync_read;
+};
+
+struct fuse_session {
+ char *mountpoint;
+ volatile int exited;
+ int fd;
+ struct mount_opts *mo;
+ struct session_opts opts;
+ int debug;
+ int allow_root;
struct fuse_lowlevel_ops op;
int got_init;
struct cuse_data *cuse_data;
diff --git a/lib/fuse_loop_mt.c b/lib/fuse_loop_mt.c
index e6e2263..54fb56d 100644
--- a/lib/fuse_loop_mt.c
+++ b/lib/fuse_loop_mt.c
@@ -47,6 +47,7 @@ struct fuse_mt {
sem_t finish;
int exit;
int error;
+ int clone_fd;
};
static struct fuse_chan *fuse_chan_new(int fd)
@@ -265,13 +266,13 @@ static int fuse_loop_start_thread(struct fuse_mt *mt)
w->mt = mt;
w->ch = NULL;
- if (mt->se->clone_fd) {
+ if (mt->clone_fd) {
w->ch = fuse_clone_chan(mt);
if(!w->ch) {
/* Don't attempt this again */
fprintf(stderr, "fuse: trying to continue "
"without -o clone_fd.\n");
- mt->se->clone_fd = 0;
+ mt->clone_fd = 0;
}
}
@@ -299,7 +300,7 @@ static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w)
free(w);
}
-int fuse_session_loop_mt(struct fuse_session *se)
+int fuse_session_loop_mt(struct fuse_session *se, int clone_fd)
{
int err;
struct fuse_mt mt;
@@ -307,6 +308,7 @@ int fuse_session_loop_mt(struct fuse_session *se)
memset(&mt, 0, sizeof(struct fuse_mt));
mt.se = se;
+ mt.clone_fd = clone_fd;
mt.error = 0;
mt.numworker = 0;
mt.numavail = 0;
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index abde850..a96f3a5 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -1809,8 +1809,8 @@ static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
fuse_reply_err(req, ENOSYS);
}
-static void apply_want_options(struct fuse_session *opts,
- struct fuse_conn_info *conn)
+static void apply_want_options(struct session_opts *opts,
+ struct fuse_conn_info *conn)
{
#define LL_ENABLE(cond,cap) \
if (cond) conn->want |= (cap)
@@ -1931,6 +1931,7 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
if ((cond) && (f->conn.capable & (cap))) \
f->conn.want |= (cap)
LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ);
+ LL_SET_DEFAULT(f->op.write_buf, FUSE_CAP_SPLICE_READ);
LL_SET_DEFAULT(f->op.getlk && f->op.setlk,
FUSE_CAP_POSIX_LOCKS);
LL_SET_DEFAULT(f->op.flock, FUSE_CAP_FLOCK_LOCKS);
@@ -1951,7 +1952,7 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
/* Apply command-line options (so that init() handler has
an idea about user preferences */
- apply_want_options(f, &f->conn);
+ apply_want_options(&f->opts, &f->conn);
/* Allow file-system to overwrite defaults */
if (f->op.init)
@@ -1959,7 +1960,7 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
/* Now explicitly overwrite file-system's decision
with command-line options */
- apply_want_options(f, &f->conn);
+ apply_want_options(&f->opts, &f->conn);
/* Always enable big writes, this is superseded
by the max_write option */
@@ -2573,32 +2574,31 @@ static const struct fuse_opt fuse_ll_opts[] = {
LL_OPTION("max_readahead=%u", conn.max_readahead, 0),
LL_OPTION("max_background=%u", conn.max_background, 0),
LL_OPTION("congestion_threshold=%u", conn.congestion_threshold, 0),
- LL_OPTION("sync_read", sync_read, 1),
- LL_OPTION("async_read", async_read, 1),
- LL_OPTION("atomic_o_trunc", atomic_o_trunc, 1),
- LL_OPTION("no_remote_lock", no_remote_posix_lock, 1),
- LL_OPTION("no_remote_lock", no_remote_flock, 1),
- LL_OPTION("no_remote_flock", no_remote_flock, 1),
- LL_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1),
- LL_OPTION("splice_write", splice_write, 1),
- LL_OPTION("no_splice_write", no_splice_write, 1),
- LL_OPTION("splice_move", splice_move, 1),
- LL_OPTION("no_splice_move", no_splice_move, 1),
- LL_OPTION("splice_read", splice_read, 1),
- LL_OPTION("no_splice_read", no_splice_read, 1),
- LL_OPTION("auto_inval_data", auto_inval_data, 1),
- LL_OPTION("no_auto_inval_data", no_auto_inval_data, 1),
- LL_OPTION("readdirplus=no", no_readdirplus, 1),
- LL_OPTION("readdirplus=yes", no_readdirplus, 0),
- LL_OPTION("readdirplus=yes", no_readdirplus_auto, 1),
- LL_OPTION("readdirplus=auto", no_readdirplus, 0),
- LL_OPTION("readdirplus=auto", no_readdirplus_auto, 0),
- LL_OPTION("async_dio", async_dio, 1),
- LL_OPTION("no_async_dio", no_async_dio, 1),
- LL_OPTION("writeback_cache", writeback_cache, 1),
- LL_OPTION("no_writeback_cache", no_writeback_cache, 1),
+ LL_OPTION("sync_read", opts.sync_read, 1),
+ LL_OPTION("async_read", opts.async_read, 1),
+ LL_OPTION("atomic_o_trunc", opts.atomic_o_trunc, 1),
+ LL_OPTION("no_remote_lock", opts.no_remote_posix_lock, 1),
+ LL_OPTION("no_remote_lock", opts.no_remote_flock, 1),
+ LL_OPTION("no_remote_flock", opts.no_remote_flock, 1),
+ LL_OPTION("no_remote_posix_lock", opts.no_remote_posix_lock, 1),
+ LL_OPTION("splice_write", opts.splice_write, 1),
+ LL_OPTION("no_splice_write", opts.no_splice_write, 1),
+ LL_OPTION("splice_move", opts.splice_move, 1),
+ LL_OPTION("no_splice_move", opts.no_splice_move, 1),
+ LL_OPTION("splice_read", opts.splice_read, 1),
+ LL_OPTION("no_splice_read", opts.no_splice_read, 1),
+ LL_OPTION("auto_inval_data", opts.auto_inval_data, 1),
+ LL_OPTION("no_auto_inval_data", opts.no_auto_inval_data, 1),
+ LL_OPTION("readdirplus=no", opts.no_readdirplus, 1),
+ LL_OPTION("readdirplus=yes", opts.no_readdirplus, 0),
+ LL_OPTION("readdirplus=yes", opts.no_readdirplus_auto, 1),
+ LL_OPTION("readdirplus=auto", opts.no_readdirplus, 0),
+ LL_OPTION("readdirplus=auto", opts.no_readdirplus_auto, 0),
+ LL_OPTION("async_dio", opts.async_dio, 1),
+ LL_OPTION("no_async_dio", opts.no_async_dio, 1),
+ LL_OPTION("writeback_cache", opts.writeback_cache, 1),
+ LL_OPTION("no_writeback_cache", opts.no_writeback_cache, 1),
LL_OPTION("time_gran=%u", conn.time_gran, 0),
- LL_OPTION("clone_fd", clone_fd, 1),
FUSE_OPT_END
};
@@ -2629,17 +2629,7 @@ void fuse_lowlevel_help(void)
" -o readdirplus=S control readdirplus use (yes|no|auto)\n"
" -o [no_]async_dio asynchronous direct I/O\n"
" -o [no_]writeback_cache asynchronous, buffered writes\n"
-" -o time_gran=N time granularity in nsec\n"
-" -o clone_fd clone fuse device file descriptors\n\n");
-}
-
-static int fuse_ll_opt_proc(void *data, const char *arg, int key,
- struct fuse_args *outargs)
-{
- (void) data; (void) outargs; (void) key; (void) arg;
-
- /* Passthrough unknown options */
- return 1;
+" -o time_gran=N time granularity in nsec\n\n");
}
void fuse_session_destroy(struct fuse_session *se)
@@ -2842,13 +2832,12 @@ struct fuse_session *fuse_session_new(struct fuse_args *args,
}
se->conn.max_write = UINT_MAX;
se->conn.max_readahead = UINT_MAX;
- se->atomic_o_trunc = 0;
/* Parse options */
mo = parse_mount_opts(args);
if (mo == NULL)
goto out2;
- if(fuse_opt_parse(args, se, fuse_ll_opts, fuse_ll_opt_proc) == -1)
+ if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1)
goto out3;
if (args->argc != 1) {
int i;
diff --git a/lib/helper.c b/lib/helper.c
index cc5cefc..ea06d81 100644
--- a/lib/helper.c
+++ b/lib/helper.c
@@ -45,6 +45,7 @@ static const struct fuse_opt fuse_helper_opts[] = {
FUSE_HELPER_OPT("subtype=", nodefault_subtype),
FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP),
FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP),
+ FUSE_HELPER_OPT("clone_fd", clone_fd),
FUSE_OPT_END
};
@@ -56,6 +57,7 @@ void fuse_cmdline_help(void)
" -d -o debug enable debug output (implies -f)\n"
" -f foreground operation\n"
" -s disable multi-threaded operation\n"
+ " -o clone_fd use separate fuse device fd for each thread\n"
"\n");
}
@@ -246,7 +248,7 @@ int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
if (opts.singlethread)
res = fuse_loop(fuse);
else
- res = fuse_loop_mt(fuse);
+ res = fuse_loop_mt(fuse, opts.clone_fd);
if (res)
res = 1;
diff --git a/test/test_examples.py b/test/test_examples.py
index e0f9be4..a8064c3 100755
--- a/test/test_examples.py
+++ b/test/test_examples.py
@@ -13,12 +13,13 @@ import stat
import shutil
import filecmp
import errno
+import platform
+from distutils.version import LooseVersion
from tempfile import NamedTemporaryFile
from util import (wait_for_mount, umount, cleanup, base_cmdline,
- safe_sleep)
+ safe_sleep, basename)
from os.path import join as pjoin
-basename = pjoin(os.path.dirname(__file__), '..')
TEST_FILE = __file__
with open(TEST_FILE, 'rb') as fh:
@@ -206,8 +207,10 @@ def test_notify_inval_entry(tmpdir, notify):
else:
umount(mount_process, mnt_dir)
-@pytest.mark.parametrize("writeback", (True, False))
+@pytest.mark.parametrize("writeback", (False, True))
def test_write_cache(tmpdir, writeback):
+ if writeback and LooseVersion(platform.release()) < '3.14':
+ pytest.skip('Requires kernel 3.14 or newer')
# This test hangs under Valgrind when running close(fd)
# test_write_cache.c:test_fs(). Most likely this is because of an internal
# deadlock in valgrind, it probably assumes that until close() returns,
diff --git a/test/util.py b/test/util.py
index 2160f70..baba20b 100644
--- a/test/util.py
+++ b/test/util.py
@@ -3,6 +3,9 @@ import subprocess
import pytest
import os
import time
+from os.path import join as pjoin
+
+basename = pjoin(os.path.dirname(__file__), '..')
def wait_for_mount(mount_process, mnt_dir,
test_fn=os.path.ismount):
@@ -17,12 +20,24 @@ def wait_for_mount(mount_process, mnt_dir,
pytest.fail("mountpoint failed to come up")
def cleanup(mnt_dir):
- subprocess.call(['fusermount', '-z', '-u', mnt_dir],
+ # Don't bother trying Valgrind if things already went wrong
+
+ subprocess.call([pjoin(basename, 'util', 'fusermount'),
+ '-z', '-u', mnt_dir],
stdout=subprocess.DEVNULL,
stderr=subprocess.STDOUT)
def umount(mount_process, mnt_dir):
- subprocess.check_call(['fusermount', '-z', '-u', mnt_dir])
+ # fusermount will be setuid root, so we can only trace it with
+ # valgrind if we're root
+ if os.getuid() == 0:
+ cmdline = base_cmdline
+ else:
+ cmdline = []
+
+ cmdline = cmdline + [ pjoin(basename, 'util', 'fusermount'),
+ '-z', '-u', mnt_dir ]
+ subprocess.check_call(cmdline)
assert not os.path.ismount(mnt_dir)
# Give mount process a little while to terminate. Popen.wait(timeout)
@@ -68,3 +83,7 @@ if has_program('valgrind') and has_program('libtool'):
'valgrind', '-q', '--' ]
else:
base_cmdline = []
+
+
+# Try to use local fusermount
+os.environ['PATH'] = '%s:%s' % (pjoin(basename, 'util'), os.environ['PATH'])