diff options
-rw-r--r-- | .travis.yml | 3 | ||||
-rw-r--r-- | AUTHORS | 9 | ||||
-rw-r--r-- | ChangeLog.rst | 28 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r--[-rwxr-xr-x] | example/cusexmp.c | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | example/fioc.c | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | example/fioclient.c | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | example/fsel.c | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | example/fselclient.c | 0 | ||||
-rw-r--r-- | example/fuse_lo-plus.c | 51 | ||||
-rw-r--r--[-rwxr-xr-x] | example/fusexmp.c | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | example/fusexmp_fh.c | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | example/hello.c | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | example/hello_ll.c | 42 | ||||
-rw-r--r--[-rwxr-xr-x] | example/null.c | 0 | ||||
-rw-r--r-- | include/fuse.h | 14 | ||||
-rw-r--r-- | include/fuse_common.h | 28 | ||||
-rw-r--r-- | include/fuse_lowlevel.h | 108 | ||||
-rw-r--r-- | lib/Makefile.am | 6 | ||||
-rw-r--r-- | lib/buffer.c | 3 | ||||
-rw-r--r-- | lib/cuse_lowlevel.c | 33 | ||||
-rw-r--r-- | lib/fuse.c | 103 | ||||
-rw-r--r-- | lib/fuse_i.h | 59 | ||||
-rw-r--r-- | lib/fuse_loop.c | 7 | ||||
-rw-r--r--[-rwxr-xr-x] | lib/fuse_loop_mt.c | 64 | ||||
-rw-r--r--[-rwxr-xr-x] | lib/fuse_lowlevel.c | 209 | ||||
-rw-r--r-- | lib/fuse_mt.c | 25 | ||||
-rw-r--r-- | lib/fuse_opt.c | 3 | ||||
-rw-r--r-- | lib/fuse_session.c | 112 | ||||
-rw-r--r--[-rwxr-xr-x] | lib/fuse_signals.c | 2 | ||||
-rw-r--r-- | lib/fuse_versionscript | 5 | ||||
-rw-r--r-- | lib/helper.c | 242 | ||||
-rw-r--r-- | lib/mount.c | 7 | ||||
-rw-r--r-- | lib/mount_bsd.c | 6 | ||||
-rw-r--r-- | lib/mount_util.c | 2 | ||||
-rw-r--r-- | test/conftest.py | 10 | ||||
-rwxr-xr-x | test/test_examples.py | 106 | ||||
-rwxr-xr-x | test/test_fuse.py | 5 | ||||
-rw-r--r-- | test/util.py | 17 |
39 files changed, 658 insertions, 653 deletions
diff --git a/.travis.yml b/.travis.yml index b90c8bf..6d2f313 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ addons: packages: - doxygen - libtool + - valgrind - automake - autoconf - gcc-6 @@ -23,6 +24,8 @@ install: - sudo python -m pip install pytest script: - $CC --version + - libtool --version + - valgrind --version - ./makeconf.sh - ./configure - CFLAGS="-fsanitize=address,undefined -g -O1 -Wall -Werror" make @@ -16,19 +16,26 @@ Contributors CUSE has been written by Tejun Heo <teheo@suse.de>. Furthermore, the following people have contributed patches (autogenerated list): +Alex Richman <alex@richman.io> Anatol Pomozov <anatol.pomozov@gmail.com> +Antonio SJ Musumeci <trapexit@spawn.link> +bobrofon <ifbossfor@ya.ru> Christopher Harrison <ch12@sanger.ac.uk> Csaba Henk <csaba.henk@creo.hu> cvs2git <> Dalvik Khertel <khertel@outlook.com> Daniel Thau <danthau@bedrocklinux.org> David McNab <david@rebirthing.co.nz> +David Sheets <sheets@alum.mit.edu> Emmanuel Dreyfus <manu@netbsd.org> Enke Chen <enkechen@yahoo.com> +Eric Engestrom <eric@engestrom.ch> Eric Wong <normalperson@yhbt.net> Fabrice Bauzac <fbauzac@amadeus.com> Feng Shuo <steve.shuo.feng@gmail.com> +Hendrik Brueckner <brueckner@linux.vnet.ibm.com> Ikey Doherty <michael.i.doherty@intel.com> +Jan Blumschein <jan@jan-blumschein.de> Joachim Schiele <joachim.schiele@daimler.com> Joachim Schiele <js@lastlog.de> John Muir <john@jmuir.com> @@ -48,5 +55,7 @@ Reuben Hawkins <reubenhwk@gmail.com> Richard W.M. Jones <rjones@redhat.com> Riku Voipio <riku.voipio@linaro.org> Roland Bauerschmidt <rb@debian.org> +Sam Stuewe <halosghost@archlinux.info> Sebastian Pipping <sebastian@pipping.org> therealneworld@gmail.com <therealneworld@gmail.com> +Winfried Koehler <w_scan@gmx-topmail.de> diff --git a/ChangeLog.rst b/ChangeLog.rst index a15b55a..78f6d51 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -1,13 +1,24 @@ -Unreleased Changes -================== +FUSE 3.0.0pre0 (2016-10-03) +============================ + +* This is a preview release. Functionality and API may still change + before the 3.0.0 release. * The `fuse_lowlevel_new` function has been renamed to - `fuse_session_new`. + `fuse_session_new` and no longer interprets the --version or --help + options. To print help or version information, use the new + `fuse_lowlevel_help` and `fuse_lowlevel_version` functions. + +* There are new `fuse_session_unmount` and `fuse_session_mount` + functions that should be used in the low-level API. The `fuse_mount` + and `fuse_unmount` functions should be used with the high-level API + only. -* There are now new `fuse_session_unmount` and `fuse_session_mount` - functions that should be used in the low-level API. The - `fuse_mount` and `fuse_unmount` functions should be used with the - high-level API only. +* Neither `fuse_mount` nor `fuse_session_mount` take struct fuse_opts + parameters anymore. Mount options are parsed by `fuse_new` (for the + high-level API) and `fuse_session_new` (for the low-level API) + instead. To print help or version information, use the new + `fuse_mount_help` and `fuse_mount_version` functions. * The ``fuse_lowlevel_notify_*`` functions now all take a `struct fuse_session` parameter instead of a `struct fuse_chan`. @@ -76,6 +87,9 @@ Unreleased Changes File systems that use `fuse_main` are not affected by this change. + For integration with custom event loops, the new `fuse_session_fd` + function provides the file descriptor that's used for communication + with the kernel. * Added *clone_fd* option. This creates a separate device file descriptor for each processing thread, which might improve diff --git a/configure.ac b/configure.ac index db5852b..39bddc7 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(fuse, 3.0.0_pre0) +AC_INIT(fuse, 3.0.0pre0) AC_PREREQ(2.59d) AC_CONFIG_MACRO_DIR([m4]) AC_CANONICAL_TARGET diff --git a/example/cusexmp.c b/example/cusexmp.c index 8d22075..8d22075 100755..100644 --- a/example/cusexmp.c +++ b/example/cusexmp.c diff --git a/example/fioc.c b/example/fioc.c index 368f807..368f807 100755..100644 --- a/example/fioc.c +++ b/example/fioc.c diff --git a/example/fioclient.c b/example/fioclient.c index 704f24b..704f24b 100755..100644 --- a/example/fioclient.c +++ b/example/fioclient.c diff --git a/example/fsel.c b/example/fsel.c index b496c9a..b496c9a 100755..100644 --- a/example/fsel.c +++ b/example/fsel.c diff --git a/example/fselclient.c b/example/fselclient.c index 637cb07..637cb07 100755..100644 --- a/example/fselclient.c +++ b/example/fselclient.c diff --git a/example/fuse_lo-plus.c b/example/fuse_lo-plus.c index 4171d3e..1aa97b0 100644 --- a/example/fuse_lo-plus.c +++ b/example/fuse_lo-plus.c @@ -442,55 +442,54 @@ static struct fuse_lowlevel_ops lo_oper = { .read = lo_read, }; -#define LO_OPT(t, p, v) { t, offsetof(struct lo_data, p), v } - -static const struct fuse_opt lo_opts[] = { - FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), - FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), - LO_OPT("debug", debug, 1), - LO_OPT("-d", debug, 1), - FUSE_OPT_END -}; - int main(int argc, char *argv[]) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct fuse_session *se; - char *mountpoint; - int ret = -1; + struct fuse_cmdline_opts opts; struct lo_data lo = { .debug = 0 }; + int ret = -1; - if (fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1) - exit(1); + if (fuse_parse_cmdline(&args, &opts) != 0) + return 1; + if (opts.show_help || opts.show_version) { + ret = 1; + goto err_out1; + } + if (!opts.foreground) + fprintf(stderr, "Warning: background operation " + "is not supported\n"); + if (!opts.singlethread) + fprintf(stderr, "Warning: multithreading is not " + "supported\n"); + + lo.debug = opts.debug; lo.root.next = lo.root.prev = &lo.root; lo.root.fd = open("/", O_PATH); lo.root.nlookup = 2; if (lo.root.fd == -1) err(1, "open(\"/\", O_PATH)"); - if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) != 0) - goto err_out; - se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); - fuse_opt_free_args(&args); if (se == NULL) - goto err_out; - - if (fuse_set_signal_handlers(se) != 0) goto err_out1; - if (fuse_session_mount(se, mountpoint) != 0) + if (fuse_set_signal_handlers(se) != 0) goto err_out2; + if (fuse_session_mount(se, opts.mountpoint) != 0) + goto err_out3; + ret = fuse_session_loop(se); fuse_session_unmount(se); -err_out2: +err_out3: fuse_remove_signal_handlers(se); -err_out1: +err_out2: fuse_session_destroy(se); -err_out: - free(mountpoint); +err_out1: + free(opts.mountpoint); + fuse_opt_free_args(&args); while (lo.root.next != &lo.root) lo_free(lo.root.next); diff --git a/example/fusexmp.c b/example/fusexmp.c index eae3562..eae3562 100755..100644 --- a/example/fusexmp.c +++ b/example/fusexmp.c diff --git a/example/fusexmp_fh.c b/example/fusexmp_fh.c index 84fce3f..84fce3f 100755..100644 --- a/example/fusexmp_fh.c +++ b/example/fusexmp_fh.c diff --git a/example/hello.c b/example/hello.c index 3c24c8b..3c24c8b 100755..100644 --- a/example/hello.c +++ b/example/hello.c diff --git a/example/hello_ll.c b/example/hello_ll.c index 07529d1..b7e77cd 100755..100644 --- a/example/hello_ll.c +++ b/example/hello_ll.c @@ -187,35 +187,45 @@ int main(int argc, char *argv[]) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct fuse_session *se; - char *mountpoint; - int err = -1; - - if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) != 0) - goto err_out; + struct fuse_cmdline_opts opts; + int ret = -1; + + if (fuse_parse_cmdline(&args, &opts) != 0) + return 1; + if (opts.show_help || opts.show_version) { + ret = 1; + goto err_out1; + } + if (!opts.foreground) + fprintf(stderr, "Warning: background operation " + "is not supported\n"); + if (!opts.singlethread) + fprintf(stderr, "Warning: multithreading is not " + "supported\n"); se = fuse_session_new(&args, &hello_ll_oper, sizeof(hello_ll_oper), NULL); - fuse_opt_free_args(&args); if (se == NULL) - goto err_out; - - if (fuse_set_signal_handlers(se) != 0) goto err_out1; - if (fuse_session_mount(se, mountpoint) != 0) + if (fuse_set_signal_handlers(se) != 0) goto err_out2; + if (fuse_session_mount(se, opts.mountpoint) != 0) + goto err_out3; + /* Block until ctrl+c or fusermount -u */ - err = fuse_session_loop(se); + ret = fuse_session_loop(se); fuse_session_unmount(se); -err_out2: +err_out3: fuse_remove_signal_handlers(se); -err_out1: +err_out2: fuse_session_destroy(se); -err_out: - free(mountpoint); +err_out1: + free(opts.mountpoint); + fuse_opt_free_args(&args); - return err ? 1 : 0; + return ret ? 1 : 0; } /*! [doxygen_fuse_lowlevel_usage] */ diff --git a/example/null.c b/example/null.c index 1ff1954..1ff1954 100755..100644 --- a/example/null.c +++ b/example/null.c diff --git a/include/fuse.h b/include/fuse.h index c3fea2d..719623a 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -608,8 +608,7 @@ struct fuse_context { * main() function. * * This function does the following: - * - parses command line options (-d -s and -h) - * - passes relevant mount options to the fuse_mount() + * - parses command line options * - installs signal handlers for INT, HUP, TERM and PIPE * - registers an exit handler to unmount the filesystem on program exit * - creates a fuse handle @@ -640,14 +639,13 @@ struct fuse_context { /** * Create a new FUSE filesystem. * - * Known arguments are defined in `struct fuse_opt fuse_lib_opts[]`, + * Known options are defined in `struct fuse_opt fuse_lib_opts[]`, * `struct fuse_opt fuse_mount_opts[]`, and `struct fuse_opt - * fuse_ll_opts[]`. If there are any unknown arguments, an error - * message will be printed to stderr and the function will return - * NULL. + * fuse_ll_opts[]`. If not all options are known, an error message is + * written to stderr and the function returns NULL. * - * If the --help or --version parameters are specified, the function - * prints the requested information to stdout and returns NULL. + * If the --help option is specified, the function writes a help text + * to stdout and returns NULL. * * @param args argument vector * @param op the filesystem operations diff --git a/include/fuse_common.h b/include/fuse_common.h index f39dab3..f32c872 100644 --- a/include/fuse_common.h +++ b/include/fuse_common.h @@ -209,34 +209,6 @@ struct fuse_session; struct fuse_pollhandle; /** - * Utility functions for simple file systems to parse common options. - * - * The following options are parsed: - * - * '-f' foreground - * '-d' '-odebug' foreground, but keep the debug option - * '-s' single threaded - * '-h' '--help' help - * '-ho' help without header - * '-ofsname=..' file system name, if not present, then set to the program - * name - * - * Unknown parameters in `args` are passed through unchanged. Known - * parameters (with the exception of --help and --version) are removed. - * - * All parameters may be NULL (in which case they may still - * be specified on the command line, but will not be set). - * - * @param args argument vector - * @param mountpoint the returned mountpoint, should be freed after use - * @param multithreaded set to 1 unless the '-s' option is present - * @param foreground set to 1 if one of the relevant options is present - * @return 0 on success, -1 on failure - */ -int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint, - int *multithreaded, int *foreground); - -/** * Go into the background * * @param foreground if true, stay in the foreground diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index f0f0e0b..f2d9caa 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -98,7 +98,14 @@ struct fuse_entry_param { double entry_timeout; }; -/** Additional context associated with requests */ +/** + * Additional context associated with requests. + * + * Note that the reported client uid, gid and pid may be zero in some + * situations. For example, if the FUSE file system is running in a + * PID or user namespace but then accessed from outside the namespace, + * there is no valid uid/pid/gid that could be reported. + */ struct fuse_ctx { /** User ID of the calling process */ uid_t uid; @@ -109,7 +116,7 @@ struct fuse_ctx { /** Thread ID of the calling process */ pid_t pid; - /** Umask of the calling process (introduced in version 2.8) */ + /** Umask of the calling process */ mode_t umask; }; @@ -1560,23 +1567,81 @@ void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, */ int fuse_req_interrupted(fuse_req_t req); + +/* ----------------------------------------------------------- * + * Inquiry functions * + * ----------------------------------------------------------- */ + +/** + * Print FUSE library version to stdout. + */ +void fuse_lowlevel_version(void); + +/** + * Print FUSE mount (fusermount) version stdout. + */ +void fuse_mount_version(void); + +/** + * Print available low-level options to stdout. + * These options may be passed to `fuse_session_new()` + */ +void fuse_lowlevel_help(void); + +/** + * Print available mount options to stdout. + * These options may be passed to `fuse_session_new()` + */ +void fuse_mount_help(void); + /* ----------------------------------------------------------- * * Filesystem setup & teardown * * ----------------------------------------------------------- */ +struct fuse_cmdline_opts { + int singlethread; + int foreground; + int debug; + int nodefault_subtype; + char *mountpoint; + int show_version; + int show_help; +}; + +/** + * Utility function to parse common options for simple file systems + * using the low-level API. Available options are listed in `struct + * fuse_opt fuse_helper_opts[]`. A single non-option argument is + * treated as the mountpoint. Multiple (or no) non-option arguments + * will result in an error. + * + * Unknown options are passed through unchanged. Known options (other + * than --debug, which is preserved) and the mountpoint argument are + * removed from *args*. + * + * If --help or --version is specified, the appropriate information is + * printed to stdout and the function proceeds normally. + * + * If neither -o subtype= or -o fsname= options are given, the subtype + * is set to the basename of the program (the fsname defaults to + * "fuse"). + * + * @param args argument vector (input+output) + * @param opts output argument for parsed options + * @return 0 on success, -1 on failure + */ +int fuse_parse_cmdline(struct fuse_args *args, + struct fuse_cmdline_opts *opts); + /** * Create a low level session. * * Returns a session structure suitable for passing to * fuse_session_mount() and fuse_session_loop(). * - * Known arguments are defined in `struct fuse_opt fuse_ll_opts[]` and - * `struct fuse_opt fuse_mount_opts[]`. If there are any unknown - * arguments, an error message will be printed to stderr and the - * function will return NULL. - * - * If the --help or --version parameters are specified, the function - * prints the requsted information to stdout and returns NULL. + * Known options are defined in `struct fuse_opt fuse_ll_opts[]` and + * `struct fuse_opt fuse_mount_opts[]`. If not all options are known, + * an error message is written to stderr and the function returns NULL. * * @param args argument vector * @param op the (low-level) filesystem operations @@ -1663,10 +1728,26 @@ void fuse_session_unmount(struct fuse_session *se); void fuse_session_destroy(struct fuse_session *se); /* ----------------------------------------------------------- * - * Request processing (for custom event loops) * + * Custom event loop support * * ----------------------------------------------------------- */ /** + * Return file descriptor for communication with kernel. + * + * The file selector can be used to integrate FUSE with a custom event + * loop. Whenever data is available for reading on the provided fd, + * the event loop should call `fuse_session_receive_buf` followed by + * `fuse_session_process_buf` to process the request. + * + * The returned file descriptor is valid until `fuse_session_unmount` + * is called. + * + * @param se the session + * @return a file descriptor + */ +int fuse_session_fd(struct fuse_session *se); + +/** * Process a raw request supplied in a generic buffer * * The fuse_buf may contain a memory buffer or a pipe file descriptor. @@ -1679,10 +1760,11 @@ void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf); /** - * Receive a raw request supplied in a generic buffer + * Read a raw request from the kernel into the supplied buffer. * - * The fuse_buf supplied to this function contains a suitably allocated memory - * buffer. This may be overwritten with a file descriptor buffer. + * Depending on file system options, system capabilities, and request + * size the request is either read into a memory buffer or spliced + * into a temporary pipe. * * @param se the session * @param buf the fuse_buf to store the request in diff --git a/lib/Makefile.am b/lib/Makefile.am index 2ad0f15..e7f6fd4 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -17,16 +17,14 @@ else iconv_source = endif -libfuse3_la_SOURCES = \ +libfuse3_la_SOURCES = \ fuse.c \ fuse_i.h \ fuse_loop.c \ fuse_loop_mt.c \ fuse_lowlevel.c \ fuse_misc.h \ - fuse_mt.c \ fuse_opt.c \ - fuse_session.c \ fuse_signals.c \ buffer.c \ cuse_lowlevel.c \ @@ -35,7 +33,7 @@ libfuse3_la_SOURCES = \ $(iconv_source) \ $(mount_source) -libfuse3_la_LDFLAGS = -pthread @libfuse_libs@ -version-number 0:0:0 \ +libfuse3_la_LDFLAGS = -pthread @libfuse_libs@ -version-number 3:0:0 \ -Wl,--version-script,$(srcdir)/fuse_versionscript if NETBSD diff --git a/lib/buffer.c b/lib/buffer.c index 17a595c..85309ac 100644 --- a/lib/buffer.c +++ b/lib/buffer.c @@ -2,6 +2,9 @@ FUSE: Filesystem in Userspace Copyright (C) 2010 Miklos Szeredi <miklos@szeredi.hu> + Functions for dealing with `struct fuse_buf` and `struct + fuse_bufvec`. + This program can be distributed under the terms of the GNU LGPLv2. See the file COPYING.LIB */ diff --git a/lib/cuse_lowlevel.c b/lib/cuse_lowlevel.c index 3dd79d8..03ae9a6 100644 --- a/lib/cuse_lowlevel.c +++ b/lib/cuse_lowlevel.c @@ -275,22 +275,18 @@ struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[], }; struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct fuse_session *se; - struct fuse_chan *ch; + struct fuse_cmdline_opts opts; int fd; - int foreground; int res; - res = fuse_parse_cmdline(&args, NULL, multithreaded, &foreground); - if (res == -1) { - fuse_opt_free_args(&args); + if (fuse_parse_cmdline(&args, &opts) == -1) return NULL; - } + *multithreaded = !opts.singlethread; + /* Remove subtype= option */ res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL); - if (res == -1) { - fuse_opt_free_args(&args); - return NULL; - } + if (res == -1) + goto out1; /* * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos @@ -303,9 +299,8 @@ struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[], } while (fd >= 0 && fd <= 2); se = cuse_lowlevel_new(&args, ci, clop, userdata); - fuse_opt_free_args(&args); if (se == NULL) - return NULL; + goto out1; fd = open(devname, O_RDWR); if (fd == -1) { @@ -316,20 +311,13 @@ struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[], devname, strerror(errno)); goto err_se; } - - ch = fuse_chan_new(fd); - if (!ch) { - close(fd); - goto err_se; - } - - fuse_session_add_chan(se, ch); + se->fd = fd; res = fuse_set_signal_handlers(se); if (res == -1) goto err_se; - res = fuse_daemonize(foreground); + res = fuse_daemonize(opts.foreground); if (res == -1) goto err_sig; @@ -339,6 +327,9 @@ err_sig: fuse_remove_signal_handlers(se); err_se: fuse_session_destroy(se); +out1: + free(opts.mountpoint); + fuse_opt_free_args(&args); return NULL; } @@ -2,6 +2,9 @@ FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + Implementation of the high-level FUSE API on top of the low-level + API. + This program can be distributed under the terms of the GNU LGPLv2. See the file COPYING.LIB */ @@ -77,7 +80,7 @@ struct fuse_config { int auto_cache; int intr; int intr_signal; - int help; + int show_help; char *modules; }; @@ -4318,9 +4321,8 @@ static int fuse_session_loop_remember(struct fuse *f) int res = 0; struct timespec now; time_t next_clean; - struct fuse_chan *ch = fuse_session_chan(se); struct pollfd fds = { - .fd = ch->fd, + .fd = se->fd, .events = POLLIN }; struct fuse_buf fbuf = { @@ -4345,14 +4347,14 @@ static int fuse_session_loop_remember(struct fuse *f) else break; } else if (res > 0) { - res = fuse_session_receive_buf_int(se, &fbuf, ch); + res = fuse_session_receive_buf_int(se, &fbuf, NULL); if (res == -EINTR) continue; if (res <= 0) break; - fuse_session_process_buf_int(se, &fbuf, ch); + fuse_session_process_buf_int(se, &fbuf, NULL); } else { timeout = fuse_clean_cache(f); curr_time(&now); @@ -4376,6 +4378,20 @@ int fuse_loop(struct fuse *f) return fuse_session_loop(f->se); } +int fuse_loop_mt(struct fuse *f) +{ + if (f == NULL) + return -1; + + int res = fuse_start_cleanup_thread(f); + if (res) + return -1; + + res = fuse_session_loop_mt(fuse_get_session(f)); + fuse_stop_cleanup_thread(f); + return res; +} + void fuse_exit(struct fuse *f) { fuse_session_exit(f->se); @@ -4410,15 +4426,11 @@ int fuse_interrupted(void) return 0; } -enum { - KEY_HELP, -}; - #define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v } static const struct fuse_opt fuse_lib_opts[] = { - FUSE_OPT_KEY("-h", KEY_HELP), - FUSE_OPT_KEY("--help", KEY_HELP), + FUSE_LIB_OPT("-h", show_help, 1), + FUSE_LIB_OPT("--help", show_help, 1), FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), FUSE_LIB_OPT("debug", debug, 1), @@ -4453,6 +4465,7 @@ static const struct fuse_opt fuse_lib_opts[] = { static void fuse_lib_help(void) { printf( +"High-level options\n" " -o hard_remove immediate removal (don't hide files)\n" " -o use_ino let filesystem set inode numbers\n" " -o readdir_ino try to fill in d_ino in readdir\n" @@ -4471,8 +4484,8 @@ static void fuse_lib_help(void) " -o nopath don't supply path if not necessary\n" " -o intr allow requests to be interrupted\n" " -o intr_signal=NUM signal to send on interrupt (%i)\n" -" -o modules=M1[:M2...] names of modules to push onto filesystem stack\n" -"\n", FUSE_DEFAULT_INTR_SIGNAL); +" -o modules=M1[:M2...] names of modules to push onto filesystem stack\n\n", + FUSE_DEFAULT_INTR_SIGNAL); } static void fuse_lib_help_modules(void) @@ -4498,14 +4511,9 @@ static void fuse_lib_help_modules(void) static int fuse_lib_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) { - (void) arg; (void) outargs; - - if (key == KEY_HELP) { - struct fuse_config *conf = (struct fuse_config *) data; - fuse_lib_help(); - conf->help = 1; - } + (void) arg; (void) outargs; (void) data; (void) key; + /* Pass through unknown options */ return 1; } @@ -4640,6 +4648,25 @@ struct fuse *fuse_new(struct fuse_args *args, struct fuse_fs *fs; struct fuse_lowlevel_ops llop = fuse_path_ops; + f = (struct fuse *) calloc(1, sizeof(struct fuse)); + if (f == NULL) { + fprintf(stderr, "fuse: failed to allocate fuse object\n"); + goto out; + } + + /* Parse options */ + if (fuse_opt_parse(args, &f->conf, fuse_lib_opts, + fuse_lib_opt_proc) == -1) + goto out_free; + + if (f->conf.show_help) { + fuse_lib_help(); + fuse_lowlevel_help(); + fuse_mount_help(); + /* Defer printing module help until modules + have been loaded */ + } + pthread_mutex_lock(&fuse_context_lock); static int builtin_modules_registered = 0; /* Have the builtin modules already been registered? */ @@ -4651,19 +4678,12 @@ struct fuse *fuse_new(struct fuse_args *args, } pthread_mutex_unlock(&fuse_context_lock); - if (fuse_create_context_key() == -1) - goto out; - - f = (struct fuse *) calloc(1, sizeof(struct fuse)); - if (f == NULL) { - fprintf(stderr, "fuse: failed to allocate fuse object\n"); - goto out_delete_context_key; - } + goto out_free; fs = fuse_fs_new(op, op_size, user_data); if (!fs) - goto out_free; + goto out_delete_context_key; f->fs = fs; f->conf.nopath = fs->op.flag_nopath; @@ -4684,13 +4704,6 @@ struct fuse *fuse_new(struct fuse_args *args, init_list_head(&f->full_slabs); init_list_head(&f->lru_table); - /* When --help or --version are specified, we print messages - to stderr but continue for now (and keep the arguments in - `args` for use below */ - if (fuse_opt_parse(args, &f->conf, fuse_lib_opts, - fuse_lib_opt_proc) == -1) - goto out_free_fs; - if (f->conf.modules) { char *module; char *next; @@ -4706,6 +4719,11 @@ struct fuse *fuse_new(struct fuse_args *args, } } + if(f->conf.show_help) { + fuse_lib_help_modules(); + goto out_free_fs; + } + if (!f->conf.ac_attr_timeout_set) f->conf.ac_attr_timeout = f->conf.attr_timeout; @@ -4717,16 +4735,9 @@ struct fuse *fuse_new(struct fuse_args *args, f->conf.readdir_ino = 1; #endif - /* This function will return NULL if there is an --help - or --version argument in `args` */ f->se = fuse_session_new(args, &llop, sizeof(llop), f); - if (f->se == NULL) { - /* If we've printed help before, add module help at - * the end */ - if (f->conf.help) - fuse_lib_help_modules(); + if (f->se == NULL) goto out_free_fs; - } if (f->conf.debug) { fprintf(stderr, "nopath: %i\n", f->conf.nopath); @@ -4782,10 +4793,10 @@ out_free_fs: fuse_put_module(f->fs->m); free(f->fs); free(f->conf.modules); -out_free: - free(f); out_delete_context_key: fuse_delete_context_key(); +out_free: + free(f); out: return NULL; } diff --git a/lib/fuse_i.h b/lib/fuse_i.h index c4d0709..c968321 100644 --- a/lib/fuse_i.h +++ b/lib/fuse_i.h @@ -9,31 +9,26 @@ #include "fuse.h" #include "fuse_lowlevel.h" -struct fuse_chan; struct fuse_ll; struct mount_opts; struct fuse_session { struct fuse_ll *f; char *mountpoint; - volatile int exited; - - struct fuse_chan *ch; + int fd; struct mount_opts *mo; }; struct fuse_chan { - struct fuse_session *se; - pthread_mutex_t lock; int ctr; int fd; }; - struct fuse_req { struct fuse_ll *f; + struct fuse_session *se; uint64_t unique; int ctr; pthread_mutex_t lock; @@ -116,57 +111,10 @@ struct fuse_module { int ctr; }; -int fuse_chan_clearfd(struct fuse_chan *ch); -void fuse_chan_close(struct fuse_chan *ch); - /* ----------------------------------------------------------- * - * Channel interface * + * Channel interface (when using -o clone_fd) * * ----------------------------------------------------------- */ - /** - * Create a new channel - * - * @param op channel operations - * @param fd file descriptor of the channel - * @return the new channel object, or NULL on failure - */ -struct fuse_chan *fuse_chan_new(int fd); - -/** - * Query the session to which this channel is assigned - * - * @param ch the channel - * @return the session, or NULL if the channel is not assigned - */ -struct fuse_session *fuse_chan_session(struct fuse_chan *ch); - -/** - * Remove the channel from a session - * - * If the channel is not assigned to a session, then this is a no-op - * - * @param ch the channel to remove - */ -void fuse_session_remove_chan(struct fuse_chan *ch); - -/** - * Assign a channel to a session - * - * If a session is destroyed, the assigned channel is also destroyed - * - * @param se the session - * @param ch the channel - */ -void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch); - -/** - * Return channel assigned to the session - * - * @param se the session - * @return the channel - */ -struct fuse_chan *fuse_session_chan(struct fuse_session *se); - /** * Obtain counted reference to the channel * @@ -182,7 +130,6 @@ struct fuse_chan *fuse_chan_get(struct fuse_chan *ch); */ void fuse_chan_put(struct fuse_chan *ch); - struct mount_opts *parse_mount_opts(struct fuse_args *args); void destroy_mount_opts(struct mount_opts *mo); void fuse_mount_help(void); diff --git a/lib/fuse_loop.c b/lib/fuse_loop.c index 6df4a62..4a85b13 100644 --- a/lib/fuse_loop.c +++ b/lib/fuse_loop.c @@ -2,6 +2,8 @@ FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + Implementation of the single-threaded FUSE session loop. + This program can be distributed under the terms of the GNU LGPLv2. See the file COPYING.LIB */ @@ -17,20 +19,19 @@ int fuse_session_loop(struct fuse_session *se) { int res = 0; - struct fuse_chan *ch = fuse_session_chan(se); struct fuse_buf fbuf = { .mem = NULL, }; while (!fuse_session_exited(se)) { - res = fuse_session_receive_buf_int(se, &fbuf, ch); + res = fuse_session_receive_buf_int(se, &fbuf, NULL); if (res == -EINTR) continue; if (res <= 0) break; - fuse_session_process_buf_int(se, &fbuf, ch); + fuse_session_process_buf_int(se, &fbuf, NULL); } free(fbuf.mem); diff --git a/lib/fuse_loop_mt.c b/lib/fuse_loop_mt.c index f482962..c925cd7 100755..100644 --- a/lib/fuse_loop_mt.c +++ b/lib/fuse_loop_mt.c @@ -2,6 +2,8 @@ FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + Implementation of the multi-threaded FUSE session loop. + This program can be distributed under the terms of the GNU LGPLv2. See the file COPYING.LIB. */ @@ -21,6 +23,7 @@ #include <errno.h> #include <sys/time.h> #include <sys/ioctl.h> +#include <assert.h> /* Environment var controlling the thread stack size */ #define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK" @@ -40,13 +43,53 @@ struct fuse_mt { int numworker; int numavail; struct fuse_session *se; - struct fuse_chan *prevch; struct fuse_worker main; sem_t finish; int exit; int error; }; +static struct fuse_chan *fuse_chan_new(int fd) +{ + struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch)); + if (ch == NULL) { + fprintf(stderr, "fuse: failed to allocate channel\n"); + return NULL; + } + + memset(ch, 0, sizeof(*ch)); + ch->fd = fd; + ch->ctr = 1; + fuse_mutex_init(&ch->lock); + + return ch; +} + +struct fuse_chan *fuse_chan_get(struct fuse_chan *ch) +{ + assert(ch->ctr > 0); + pthread_mutex_lock(&ch->lock); + ch->ctr++; + pthread_mutex_unlock(&ch->lock); + + return ch; +} + +void fuse_chan_put(struct fuse_chan *ch) +{ + if (ch == NULL) + return; + pthread_mutex_lock(&ch->lock); + ch->ctr--; + if (!ch->ctr) { + pthread_mutex_unlock(&ch->lock); + close(ch->fd); + pthread_mutex_destroy(&ch->lock); + free(ch); + } else + pthread_mutex_unlock(&ch->lock); +} + static void list_add_worker(struct fuse_worker *w, struct fuse_worker *next) { struct fuse_worker *prev = next->prev; @@ -193,15 +236,13 @@ static struct fuse_chan *fuse_clone_chan(struct fuse_mt *mt) } fcntl(clonefd, F_SETFD, FD_CLOEXEC); - masterfd = mt->prevch->fd; + masterfd = mt->se->fd; res = ioctl(clonefd, FUSE_DEV_IOC_CLONE, &masterfd); if (res == -1) { fprintf(stderr, "fuse: failed to clone device fd: %s\n", strerror(errno)); close(clonefd); - mt->se->f->clone_fd = 0; - - return fuse_chan_get(mt->prevch); + return NULL; } newch = fuse_chan_new(clonefd); if (newch == NULL) @@ -223,13 +264,15 @@ static int fuse_loop_start_thread(struct fuse_mt *mt) w->fbuf.mem = NULL; w->mt = mt; - + w->ch = NULL; if (mt->se->f->clone_fd) { w->ch = fuse_clone_chan(mt); - if (!w->ch) - return -1; - } else { - w->ch = fuse_chan_get(mt->prevch); + if(!w->ch) { + /* Don't attempt this again */ + fprintf(stderr, "fuse: trying to continue " + "without -o clone_fd.\n"); + mt->se->f->clone_fd = 0; + } } res = fuse_start_thread(&w->thread_id, fuse_do_work, w); @@ -264,7 +307,6 @@ int fuse_session_loop_mt(struct fuse_session *se) memset(&mt, 0, sizeof(struct fuse_mt)); mt.se = se; - mt.prevch = fuse_session_chan(se); mt.error = 0; mt.numworker = 0; mt.numavail = 0; diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index 2a7c580..7acc206 100755..100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -2,6 +2,9 @@ FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + Implementation of (most of) the low-level FUSE API. The session loop + functions are implemented in separate files. + This program can be distributed under the terms of the GNU LGPLv2. See the file COPYING.LIB */ @@ -41,8 +44,7 @@ struct fuse_pollhandle { uint64_t kh; - struct fuse_chan *ch; - struct fuse_ll *f; + struct fuse_session *se; }; static size_t pagesize; @@ -141,7 +143,7 @@ void fuse_free_req(fuse_req_t req) destroy_req(req); } -static struct fuse_req *fuse_ll_alloc_req(struct fuse_ll *f) +static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se) { struct fuse_req *req; @@ -149,7 +151,8 @@ static struct fuse_req *fuse_ll_alloc_req(struct fuse_ll *f) if (req == NULL) { fprintf(stderr, "fuse: failed to allocate request\n"); } else { - req->f = f; + req->f = se->f; + req->se = se; req->ctr = 1; list_init_req(req); fuse_mutex_init(&req->lock); @@ -158,18 +161,12 @@ static struct fuse_req *fuse_ll_alloc_req(struct fuse_ll *f) return req; } -void fuse_chan_close(struct fuse_chan *ch) -{ - int fd = ch->fd; - if (fd != -1) - close(fd); -} - - -static int fuse_send_msg(struct fuse_ll *f, struct fuse_chan *ch, +/* Send data. If *ch* is NULL, send via session master fd */ +static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, struct iovec *iov, int count) { struct fuse_out_header *out = iov[0].iov_base; + struct fuse_ll *f = se->f; out->len = iov_length(iov, count); if (f->debug) { @@ -188,12 +185,11 @@ static int fuse_send_msg(struct fuse_ll *f, struct fuse_chan *ch, } } - ssize_t res = writev(ch->fd, iov, count); + ssize_t res = writev(ch ? ch->fd : se->fd, + iov, count); int err = errno; if (res == -1) { - struct fuse_session *se = fuse_chan_session(ch); - assert(se != NULL); /* ENOENT means the operation was interrupted */ @@ -222,7 +218,7 @@ int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, iov[0].iov_base = &out; iov[0].iov_len = sizeof(struct fuse_out_header); - return fuse_send_msg(req->f, req->ch, iov, count); + return fuse_send_msg(req->se, req->ch, iov, count); } static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov, @@ -477,7 +473,8 @@ int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size) return send_reply_ok(req, buf, size); } -static int fuse_send_data_iov_fallback(struct fuse_ll *f, struct fuse_chan *ch, +static int fuse_send_data_iov_fallback(struct fuse_session *se, + struct fuse_chan *ch, struct iovec *iov, int iov_count, struct fuse_bufvec *buf, size_t len) @@ -495,7 +492,7 @@ static int fuse_send_data_iov_fallback(struct fuse_ll *f, struct fuse_chan *ch, iov[iov_count].iov_base = buf->buf[0].mem; iov[iov_count].iov_len = len; iov_count++; - return fuse_send_msg(f, ch, iov, iov_count); + return fuse_send_msg(se, ch, iov, iov_count); } res = posix_memalign(&mbuf, pagesize, len); @@ -513,7 +510,7 @@ static int fuse_send_data_iov_fallback(struct fuse_ll *f, struct fuse_chan *ch, iov[iov_count].iov_base = mbuf; iov[iov_count].iov_len = len; iov_count++; - res = fuse_send_msg(f, ch, iov, iov_count); + res = fuse_send_msg(se, ch, iov, iov_count); free(mbuf); return res; @@ -613,7 +610,7 @@ static int read_back(int fd, char *buf, size_t len) return 0; } -static int fuse_send_data_iov(struct fuse_ll *f, struct fuse_chan *ch, +static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, struct iovec *iov, int iov_count, struct fuse_bufvec *buf, unsigned int flags) { @@ -621,6 +618,7 @@ static int fuse_send_data_iov(struct fuse_ll *f, struct fuse_chan *ch, size_t len = fuse_buf_size(buf); struct fuse_out_header *out = iov[0].iov_base; struct fuse_ll_pipe *llp; + struct fuse_ll *f = se->f; int splice_flags; size_t pipesize; size_t total_fd_size; @@ -766,7 +764,7 @@ static int fuse_send_data_iov(struct fuse_ll *f, struct fuse_chan *ch, iov[iov_count].iov_base = mbuf; iov[iov_count].iov_len = len; iov_count++; - res = fuse_send_msg(f, ch, iov, iov_count); + res = fuse_send_msg(se, ch, iov, iov_count); free(mbuf); return res; } @@ -787,8 +785,8 @@ static int fuse_send_data_iov(struct fuse_ll *f, struct fuse_chan *ch, (f->conn.want & FUSE_CAP_SPLICE_MOVE)) splice_flags |= SPLICE_F_MOVE; - res = splice(llp->pipe[0], NULL, - ch->fd, NULL, out->len, splice_flags); + res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, + NULL, out->len, splice_flags); if (res == -1) { res = -errno; perror("fuse: splice from pipe"); @@ -807,17 +805,17 @@ clear_pipe: return res; fallback: - return fuse_send_data_iov_fallback(f, ch, iov, iov_count, buf, len); + return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); } #else -static int fuse_send_data_iov(struct fuse_ll *f, struct fuse_chan *ch, +static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, struct iovec *iov, int iov_count, struct fuse_bufvec *buf, unsigned int flags) { size_t len = fuse_buf_size(buf); (void) flags; - return fuse_send_data_iov_fallback(f, ch, iov, iov_count, buf, len); + return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); } #endif @@ -834,7 +832,7 @@ int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, out.unique = req->unique; out.error = 0; - res = fuse_send_data_iov(req->f, req->ch, iov, 1, bufv, flags); + res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags); if (res <= 0) { fuse_free_req(req); return res; @@ -1072,7 +1070,7 @@ static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid, struct fuse_forget_one *forget = ¶m[i]; struct fuse_req *dummy_req; - dummy_req = fuse_ll_alloc_req(req->f); + dummy_req = fuse_ll_alloc_req(req->se); if (dummy_req == NULL) break; @@ -1790,8 +1788,7 @@ static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) return; } ph->kh = arg->kh; - ph->ch = req->ch; - ph->f = req->f; + ph->se = req->se; } req->f->op.poll(req, nodeid, &fi, ph); @@ -2079,12 +2076,12 @@ static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid, nreq->reply(nreq, req, nodeid, inarg, buf); } -static int send_notify_iov(struct fuse_ll *f, struct fuse_chan *ch, - int notify_code, struct iovec *iov, int count) +static int send_notify_iov(struct fuse_session *se, int notify_code, + struct iovec *iov, int count) { struct fuse_out_header out; - if (!f->got_init) + if (!se->f->got_init) return -ENOTCONN; out.unique = 0; @@ -2092,7 +2089,7 @@ static int send_notify_iov(struct fuse_ll *f, struct fuse_chan *ch, iov[0].iov_base = &out; iov[0].iov_len = sizeof(struct fuse_out_header); - return fuse_send_msg(f, ch, iov, count); + return fuse_send_msg(se, NULL, iov, count); } int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph) @@ -2106,7 +2103,7 @@ int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph) iov[1].iov_base = &outarg; iov[1].iov_len = sizeof(outarg); - return send_notify_iov(ph->f, ph->ch, FUSE_NOTIFY_POLL, iov, 2); + return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2); } else { return 0; } @@ -2133,7 +2130,7 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, iov[1].iov_base = &outarg; iov[1].iov_len = sizeof(outarg); - return send_notify_iov(f, se->ch, FUSE_NOTIFY_INVAL_INODE, iov, 2); + return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2); } int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, @@ -2159,7 +2156,7 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, iov[2].iov_base = (void *)name; iov[2].iov_len = namelen + 1; - return send_notify_iov(f, se->ch, FUSE_NOTIFY_INVAL_ENTRY, iov, 3); + return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3); } int fuse_lowlevel_notify_delete(struct fuse_session *se, @@ -2190,7 +2187,7 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, iov[2].iov_base = (void *)name; iov[2].iov_len = namelen + 1; - return send_notify_iov(f, se->ch, FUSE_NOTIFY_DELETE, iov, 3); + return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3); } int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, @@ -2227,7 +2224,7 @@ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, iov[1].iov_base = &outarg; iov[1].iov_len = sizeof(outarg); - res = fuse_send_data_iov(f, se->ch, iov, 2, bufv, flags); + res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags); if (res > 0) res = -res; @@ -2316,7 +2313,7 @@ int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, iov[1].iov_base = &outarg; iov[1].iov_len = sizeof(outarg); - err = send_notify_iov(f, se->ch, FUSE_NOTIFY_RETRIEVE, iov, 2); + err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2); if (err) { pthread_mutex_lock(&f->lock); list_del_nreq(&rreq->nreq); @@ -2439,7 +2436,7 @@ static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst, void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf) { - fuse_session_process_buf_int(se, buf, se->ch); + fuse_session_process_buf_int(se, buf, NULL); } void fuse_session_process_buf_int(struct fuse_session *se, @@ -2485,7 +2482,7 @@ void fuse_session_process_buf_int(struct fuse_session *se, (unsigned long long) in->nodeid, buf->size, in->pid); } - req = fuse_ll_alloc_req(f); + req = fuse_ll_alloc_req(se); if (req == NULL) { struct fuse_out_header out = { .unique = in->unique, @@ -2496,7 +2493,7 @@ void fuse_session_process_buf_int(struct fuse_session *se, .iov_len = sizeof(struct fuse_out_header), }; - fuse_send_msg(f, ch, &iov, 1); + fuse_send_msg(se, ch, &iov, 1); goto clear_pipe; } @@ -2504,7 +2501,7 @@ void fuse_session_process_buf_int(struct fuse_session *se, req->ctx.uid = in->uid; req->ctx.gid = in->gid; req->ctx.pid = in->pid; - req->ch = fuse_chan_get(ch); + req->ch = ch ? fuse_chan_get(ch) : NULL; err = EIO; if (!f->got_init) { @@ -2581,11 +2578,6 @@ clear_pipe: goto out_free; } -enum { - KEY_HELP, - KEY_VERSION, -}; - static const struct fuse_opt fuse_ll_opts[] = { { "debug", offsetof(struct fuse_ll, debug), 1 }, { "-d", offsetof(struct fuse_ll, debug), 1 }, @@ -2622,23 +2614,19 @@ static const struct fuse_opt fuse_ll_opts[] = { { "no_writeback_cache", offsetof(struct fuse_ll, no_writeback_cache), 1}, { "time_gran=%u", offsetof(struct fuse_ll, conn.time_gran), 0 }, { "clone_fd", offsetof(struct fuse_ll, clone_fd), 1 }, - FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD), - FUSE_OPT_KEY("-h", KEY_HELP), - FUSE_OPT_KEY("--help", KEY_HELP), - FUSE_OPT_KEY("-V", KEY_VERSION), - FUSE_OPT_KEY("--version", KEY_VERSION), FUSE_OPT_END }; -static void fuse_ll_version(void) +void fuse_lowlevel_version(void) { printf("using FUSE kernel interface version %i.%i\n", FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); } -static void fuse_ll_help(void) +void fuse_lowlevel_help(void) { printf( +"Low-level options\n" " -o max_write=N set maximum size of write requests\n" " -o max_readahead=N set maximum readahead\n" " -o max_background=N set number of maximum background requests\n" @@ -2658,32 +2646,16 @@ static void fuse_ll_help(void) " -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" -); +" -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) arg; - - switch (key) { - case KEY_HELP: - fuse_ll_help(); - fuse_mount_help(); - break; - - case KEY_VERSION: - fuse_ll_version(); - fuse_mount_version(); - break; - - default: - fprintf(stderr, "fuse: unknown option `%s'\n", arg); - } + (void) data; (void) outargs; (void) key; (void) arg; - /* Fail */ - return -1; + /* Passthrough unknown options */ + return 1; } static void fuse_ll_destroy(struct fuse_ll *f) @@ -2706,7 +2678,7 @@ static void fuse_ll_destroy(struct fuse_ll *f) void fuse_session_destroy(struct fuse_session *se) { fuse_ll_destroy(se->f); - fuse_chan_put(se->ch); + close(se->fd); destroy_mount_opts(se->mo); free(se); } @@ -2720,7 +2692,7 @@ static void fuse_ll_pipe_destructor(void *data) int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf) { - return fuse_session_receive_buf_int(se, buf, se->ch); + return fuse_session_receive_buf_int(se, buf, NULL); } int fuse_session_receive_buf_int(struct fuse_session *se, struct fuse_buf *buf, @@ -2754,7 +2726,8 @@ int fuse_session_receive_buf_int(struct fuse_session *se, struct fuse_buf *buf, goto fallback; } - res = splice(ch->fd, NULL, llp->pipe[1], NULL, bufsize, 0); + res = splice(ch ? ch->fd : se->fd, + NULL, llp->pipe[1], NULL, bufsize, 0); err = errno; if (fuse_session_exited(se)) @@ -2838,7 +2811,7 @@ fallback: } restart: - res = read(ch->fd, buf->mem, f->bufsize); + res = read(ch ? ch->fd : se->fd, buf->mem, f->bufsize); err = errno; if (fuse_session_exited(se)) @@ -2889,15 +2862,23 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, f = (struct fuse_ll *) calloc(1, sizeof(struct fuse_ll)); if (f == NULL) { fprintf(stderr, "fuse: failed to allocate fuse object\n"); - goto out; + goto out1; } /* Parse options */ mo = parse_mount_opts(args); if (mo == NULL) - goto out_free0; - if (fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1) - goto out_free; + goto out2; + if(fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1) + goto out3; + if (args->argc != 1) { + int i; + fprintf(stderr, "fuse: unknown option(s): `"); + for(i = 1; i < args->argc-1; i++) + fprintf(stderr, "%s ", args->argv[i]); + fprintf(stderr, "%s'\n", args->argv[i]); + goto out4; + } if (f->debug) fprintf(stderr, "FUSE library version: %s\n", PACKAGE_VERSION); @@ -2919,7 +2900,7 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, if (err) { fprintf(stderr, "fuse: failed to create thread specific key: %s\n", strerror(err)); - goto out_free; + goto out5; } memcpy(&f->op, op, op_size); @@ -2929,27 +2910,29 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, se = (struct fuse_session *) malloc(sizeof(*se)); if (se == NULL) { fprintf(stderr, "fuse: failed to allocate session\n"); - goto out_key_destroy; + goto out6; } memset(se, 0, sizeof(*se)); se->f = f; se->mo = mo; return se; -out_key_destroy: +out6: pthread_key_delete(f->pipe_key); -out_free: - free(mo); -out_free0: +out5: pthread_mutex_destroy(&f->lock); +out4: + fuse_opt_free_args(args); +out3: + free(mo); +out2: free(f); -out: +out1: return NULL; } int fuse_session_mount(struct fuse_session *se, const char *mountpoint) { - struct fuse_chan *ch; int fd; /* @@ -2966,13 +2949,7 @@ int fuse_session_mount(struct fuse_session *se, const char *mountpoint) fd = fuse_kern_mount(mountpoint, se->mo); if (fd == -1) return -1; - - ch = fuse_chan_new(fd); - if (!ch) - goto error_out; - - /* Add channel to session */ - fuse_session_add_chan(se, ch); + se->fd = fd; /* Save mountpoint */ se->mountpoint = strdup(mountpoint); @@ -2986,17 +2963,16 @@ error_out: return -1; } +int fuse_session_fd(struct fuse_session *se) +{ + return se->fd; +} + void fuse_session_unmount(struct fuse_session *se) { - struct fuse_chan *ch = se->ch; - fuse_session_remove_chan(ch); - if (se->mountpoint) { - int fd = ch ? fuse_chan_clearfd(ch) : -1; - fuse_kern_unmount(se->mountpoint, fd); - fuse_chan_put(ch); - free(se->mountpoint); - se->mountpoint = NULL; - } + fuse_kern_unmount(se->mountpoint, se->fd); + free(se->mountpoint); + se->mountpoint = NULL; } #ifdef linux @@ -3067,3 +3043,18 @@ int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) return -ENOSYS; } #endif + +void fuse_session_exit(struct fuse_session *se) +{ + se->exited = 1; +} + +void fuse_session_reset(struct fuse_session *se) +{ + se->exited = 0; +} + +int fuse_session_exited(struct fuse_session *se) +{ + return se->exited; +} diff --git a/lib/fuse_mt.c b/lib/fuse_mt.c deleted file mode 100644 index be5d644..0000000 --- a/lib/fuse_mt.c +++ /dev/null @@ -1,25 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> - - This program can be distributed under the terms of the GNU LGPLv2. - See the file COPYING.LIB. -*/ - -#include "config.h" -#include "fuse.h" -#include "fuse_lowlevel.h" - -int fuse_loop_mt(struct fuse *f) -{ - if (f == NULL) - return -1; - - int res = fuse_start_cleanup_thread(f); - if (res) - return -1; - - res = fuse_session_loop_mt(fuse_get_session(f)); - fuse_stop_cleanup_thread(f); - return res; -} diff --git a/lib/fuse_opt.c b/lib/fuse_opt.c index bd7a6ee..3d4a3dd 100644 --- a/lib/fuse_opt.c +++ b/lib/fuse_opt.c @@ -2,6 +2,9 @@ FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + Implementation of option parsing routines (dealing with `struct + fuse_args`). + This program can be distributed under the terms of the GNU LGPLv2. See the file COPYING.LIB */ diff --git a/lib/fuse_session.c b/lib/fuse_session.c deleted file mode 100644 index cdf20f7..0000000 --- a/lib/fuse_session.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> - - This program can be distributed under the terms of the GNU LGPLv2. - See the file COPYING.LIB -*/ - -#include "config.h" -#include "fuse_i.h" -#include "fuse_misc.h" - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <errno.h> - - -void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch) -{ - assert(se->ch == NULL); - assert(ch->se == NULL); - se->ch = ch; - ch->se = se; -} - -void fuse_session_remove_chan(struct fuse_chan *ch) -{ - struct fuse_session *se = ch->se; - if (se) { - assert(se->ch == ch); - se->ch = NULL; - ch->se = NULL; - } -} - -struct fuse_chan *fuse_session_chan(struct fuse_session *se) -{ - return se->ch; -} - -int fuse_chan_clearfd(struct fuse_chan *ch) -{ - int fd = ch->fd; - ch->fd = -1; - return fd; -} - -void fuse_session_exit(struct fuse_session *se) -{ - se->exited = 1; -} - -void fuse_session_reset(struct fuse_session *se) -{ - se->exited = 0; -} - -int fuse_session_exited(struct fuse_session *se) -{ - return se->exited; -} - -struct fuse_chan *fuse_chan_new(int fd) -{ - struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch)); - if (ch == NULL) { - fprintf(stderr, "fuse: failed to allocate channel\n"); - return NULL; - } - - memset(ch, 0, sizeof(*ch)); - ch->fd = fd; - ch->ctr = 1; - fuse_mutex_init(&ch->lock); - - return ch; -} - -struct fuse_session *fuse_chan_session(struct fuse_chan *ch) -{ - return ch->se; -} - -struct fuse_chan *fuse_chan_get(struct fuse_chan *ch) -{ - assert(ch->ctr > 0); - pthread_mutex_lock(&ch->lock); - ch->ctr++; - pthread_mutex_unlock(&ch->lock); - - return ch; -} - -void fuse_chan_put(struct fuse_chan *ch) -{ - if (ch) { - pthread_mutex_lock(&ch->lock); - ch->ctr--; - if (!ch->ctr) { - pthread_mutex_unlock(&ch->lock); - fuse_session_remove_chan(ch); - fuse_chan_close(ch); - pthread_mutex_destroy(&ch->lock); - free(ch); - } else { - pthread_mutex_unlock(&ch->lock); - } - - } -} diff --git a/lib/fuse_signals.c b/lib/fuse_signals.c index 9fa787c..2261b7b 100755..100644 --- a/lib/fuse_signals.c +++ b/lib/fuse_signals.c @@ -2,6 +2,8 @@ FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + Utility functions for setting signal handlers. + This program can be distributed under the terms of the GNU LGPLv2. See the file COPYING.LIB */ diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript index ee9c9d7..4031664 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -19,6 +19,7 @@ FUSE_3.0 { fuse_session_loop; fuse_session_loop_mt; fuse_session_reset; + fuse_session_fd; fuse_opt_parse; fuse_opt_add_opt; fuse_opt_add_arg; @@ -122,6 +123,10 @@ FUSE_3.0 { fuse_lowlevel_notify_delete; fuse_fs_flock; fuse_fs_fallocate; + fuse_lowlevel_help; + fuse_lowlevel_version; + fuse_mount_help; + fuse_mount_version; local: *; diff --git a/lib/helper.c b/lib/helper.c index 7ee767b..63f26c2 100644 --- a/lib/helper.c +++ b/lib/helper.c @@ -21,36 +21,24 @@ #include <errno.h> #include <sys/param.h> -enum { - KEY_HELP, - KEY_HELP_NOHEADER, - KEY_VERSION, -}; - -struct helper_opts { - int singlethread; - int foreground; - int nodefault_subtype; - char *mountpoint; -}; - -#define FUSE_HELPER_OPT(t, p) { t, offsetof(struct helper_opts, p), 1 } +#define FUSE_HELPER_OPT(t, p) \ + { t, offsetof(struct fuse_cmdline_opts, p), 1 } static const struct fuse_opt fuse_helper_opts[] = { + FUSE_HELPER_OPT("-h", show_help), + FUSE_HELPER_OPT("--help", show_help), + FUSE_HELPER_OPT("-V", show_version), + FUSE_HELPER_OPT("--version", show_version), + FUSE_HELPER_OPT("-d", debug), + FUSE_HELPER_OPT("debug", debug), FUSE_HELPER_OPT("-d", foreground), FUSE_HELPER_OPT("debug", foreground), + FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), + FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), FUSE_HELPER_OPT("-f", foreground), FUSE_HELPER_OPT("-s", singlethread), FUSE_HELPER_OPT("fsname=", nodefault_subtype), FUSE_HELPER_OPT("subtype=", nodefault_subtype), - - FUSE_OPT_KEY("-h", KEY_HELP), - FUSE_OPT_KEY("--help", KEY_HELP), - FUSE_OPT_KEY("-ho", KEY_HELP_NOHEADER), - FUSE_OPT_KEY("-V", KEY_VERSION), - FUSE_OPT_KEY("--version", KEY_VERSION), - FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), - FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), FUSE_OPT_END @@ -59,16 +47,13 @@ static const struct fuse_opt fuse_helper_opts[] = { static void usage(const char *progname) { printf("usage: %s mountpoint [options]\n\n", progname); - printf("general options:\n" - " -o opt,[opt...] mount options\n" - " -h --help print help\n" - " -V --version print version\n" - "\n"); } static void helper_help(void) { - printf("FUSE options:\n" + printf("General options:\n" + " -h --help print help\n" + " -V --version print version\n" " -d -o debug enable debug output (implies -f)\n" " -f foreground operation\n" " -s disable multi-threaded operation\n" @@ -83,23 +68,12 @@ static void helper_version(void) static int fuse_helper_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) { - struct helper_opts *hopts = data; + (void) outargs; + struct fuse_cmdline_opts *opts = data; switch (key) { - case KEY_HELP: - usage(outargs->argv[0]); - /* fall through */ - - case KEY_HELP_NOHEADER: - helper_help(); - return fuse_opt_add_arg(outargs, "-h"); - - case KEY_VERSION: - helper_version(); - return 1; - case FUSE_OPT_KEY_NONOPT: - if (!hopts->mountpoint) { + if (!opts->mountpoint) { char mountpoint[PATH_MAX]; if (realpath(arg, mountpoint) == NULL) { fprintf(stderr, @@ -107,7 +81,7 @@ static int fuse_helper_opt_proc(void *data, const char *arg, int key, arg, strerror(errno)); return -1; } - return fuse_opt_add_opt(&hopts->mountpoint, mountpoint); + return fuse_opt_add_opt(&opts->mountpoint, mountpoint); } else { fprintf(stderr, "fuse: invalid argument `%s'\n", arg); return -1; @@ -140,39 +114,45 @@ static int add_default_subtype(const char *progname, struct fuse_args *args) return res; } -int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint, - int *multithreaded, int *foreground) +int fuse_parse_cmdline(struct fuse_args *args, + struct fuse_cmdline_opts *opts) { - int res; - struct helper_opts hopts; + memset(opts, 0, sizeof(struct fuse_cmdline_opts)); + if (fuse_opt_parse(args, opts, fuse_helper_opts, + fuse_helper_opt_proc) == -1) + return -1; + + if (opts->show_version) { + helper_version(); + fuse_lowlevel_version(); + fuse_mount_version(); + return -1; + } - memset(&hopts, 0, sizeof(hopts)); - res = fuse_opt_parse(args, &hopts, fuse_helper_opts, - fuse_helper_opt_proc); - if (res == -1) + if (opts->show_help) { + usage(args->argv[0]); + helper_help(); + fuse_lowlevel_help(); + fuse_mount_help(); return -1; + } - if (!hopts.nodefault_subtype) { - res = add_default_subtype(args->argv[0], args); - if (res == -1) - goto err; + if (!opts->mountpoint) { + fprintf(stderr, "error: no mountpoint specified\n"); + usage(args->argv[0]); + return -1; } - if (mountpoint) - *mountpoint = hopts.mountpoint; - else - free(hopts.mountpoint); - if (multithreaded) - *multithreaded = !hopts.singlethread; - if (foreground) - *foreground = hopts.foreground; - return 0; + /* If neither -o subtype nor -o fsname are specified, + set subtype to program's basename */ + if (!opts->nodefault_subtype) + if (add_default_subtype(args->argv[0], args) == -1) + return -1; -err: - free(hopts.mountpoint); - return -1; + return 0; } + int fuse_daemonize(int foreground) { if (!foreground) { @@ -227,81 +207,93 @@ int fuse_daemonize(int foreground) return 0; } - -static struct fuse *fuse_setup(int argc, char *argv[], - const struct fuse_operations *op, size_t op_size, - int *multithreaded, void *user_data) +int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, + size_t op_size, void *user_data) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct fuse *fuse; - char *mountpoint; - int foreground; + struct fuse_cmdline_opts opts; int res; - res = fuse_parse_cmdline(&args, &mountpoint, multithreaded, &foreground); - if (res == -1) - return NULL; + memset(&opts, 0, sizeof(opts)); + if (fuse_opt_parse(&args, &opts, fuse_helper_opts, + fuse_helper_opt_proc) == -1) + return 1; - fuse = fuse_new(&args, op, op_size, user_data); - if (fuse == NULL) { - fuse_opt_free_args(&args); - free(mountpoint); - return NULL; + if (opts.show_version) { + helper_version(); + fuse_lowlevel_version(); + fuse_mount_version(); + res = 0; + goto out1; } - res = fuse_mount(fuse, mountpoint); - free(mountpoint); - if (res != 0) - goto err_out1; - - res = fuse_daemonize(foreground); - if (res == -1) - goto err_unmount; + /* Re-add --help for later processing by fuse_new() + (that way we also get help for modules options) */ + if (opts.show_help) { + helper_help(); + if (fuse_opt_add_arg(&args, "--help") == -1) { + res = 1; + goto out1; + } + } - res = fuse_set_signal_handlers(fuse_get_session(fuse)); - if (res == -1) - goto err_unmount; + if (!opts.show_help && + !opts.mountpoint) { + fprintf(stderr, "error: no mountpoint specified\n"); + usage(args.argv[0]); + res = 1; + goto out1; + } - return fuse; + /* If neither -o subtype nor -o fsname are specified, + set subtype to program's basename */ + if (!opts.nodefault_subtype) { + if (add_default_subtype(args.argv[0], &args) == -1) { + res = 1; + goto out1; + } + } -err_unmount: - fuse_unmount(fuse); -err_out1: - fuse_destroy(fuse); - fuse_opt_free_args(&args); - return NULL; -} + /* --help is processed here and will result in NULL */ + fuse = fuse_new(&args, op, op_size, user_data); + if (fuse == NULL) { + res = opts.show_help ? 0 : 1; + goto out1; + } -static void fuse_teardown(struct fuse *fuse) -{ - struct fuse_session *se = fuse_get_session(fuse); - fuse_remove_signal_handlers(se); - fuse_unmount(fuse); - fuse_destroy(fuse); -} + if (fuse_mount(fuse,opts.mountpoint) != 0) { + res = 1; + goto out2; + } -int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, - size_t op_size, void *user_data) -{ - struct fuse *fuse; - int multithreaded; - int res; + if (fuse_daemonize(opts.foreground) != 0) { + res = 1; + goto out3; + } - fuse = fuse_setup(argc, argv, op, op_size, - &multithreaded, user_data); - if (fuse == NULL) - return 1; + struct fuse_session *se = fuse_get_session(fuse); + if (fuse_set_signal_handlers(se) != 0) { + res = 1; + goto out3; + } - if (multithreaded) - res = fuse_loop_mt(fuse); - else + if (opts.singlethread) res = fuse_loop(fuse); + else + res = fuse_loop_mt(fuse); + if (res) + res = 1; - fuse_teardown(fuse); - if (res == -1) - return 1; - - return 0; + fuse_remove_signal_handlers(se); +out3: + fuse_unmount(fuse); +out2: + fuse_destroy(fuse); +out1: + free(opts.mountpoint); + fuse_opt_free_args(&args); + return res; } int fuse_version(void) diff --git a/lib/mount.c b/lib/mount.c index fb17c00..5c892f6 100644 --- a/lib/mount.c +++ b/lib/mount.c @@ -2,6 +2,8 @@ FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + Architecture specific file system mounting (Linux). + This program can be distributed under the terms of the GNU LGPLv2. See the file COPYING.LIB. */ @@ -99,7 +101,6 @@ static const struct fuse_opt fuse_mount_opts[] = { FUSE_OPT_KEY("defcontext=", KEY_KERN_OPT), FUSE_OPT_KEY("rootcontext=", KEY_KERN_OPT), FUSE_OPT_KEY("max_read=", KEY_KERN_OPT), - FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP), FUSE_OPT_KEY("user=", KEY_MTAB_OPT), FUSE_OPT_KEY("-r", KEY_RO), FUSE_OPT_KEY("ro", KEY_KERN_FLAG), @@ -121,6 +122,7 @@ static const struct fuse_opt fuse_mount_opts[] = { void fuse_mount_help(void) { printf( +"Mount options:\n" " -o allow_other allow access to other users\n" " -o allow_root allow access to root\n" " -o auto_unmount auto unmount on process termination\n" @@ -129,8 +131,7 @@ void fuse_mount_help(void) " -o fsname=NAME set filesystem name\n" " -o subtype=NAME set filesystem type\n" " -o large_read issue large read requests (2.4 only)\n" -" -o max_read=N set maximum size of read requests\n" -"\n"); +" -o max_read=N set maximum size of read requests\n\n"); } static void exec_fusermount(const char *argv[]) diff --git a/lib/mount_bsd.c b/lib/mount_bsd.c index 0d886b0..4703d94 100644 --- a/lib/mount_bsd.c +++ b/lib/mount_bsd.c @@ -2,6 +2,8 @@ FUSE: Filesystem in Userspace Copyright (C) 2005-2008 Csaba Henk <csaba.henk@creo.hu> + Architecture specific file system mounting (FreeBSD). + This program can be distributed under the terms of the GNU LGPLv2. See the file COPYING.LIB. */ @@ -95,7 +97,9 @@ static const struct fuse_opt fuse_mount_opts[] = { void fuse_mount_help(void) { - printf(" -o allow_root allow access to root\n"); + printf( +"Mount options:\n" +" -o allow_root allow access to root\n"); system(FUSERMOUNT_PROG " --help"); fputc('\n', stderr); } diff --git a/lib/mount_util.c b/lib/mount_util.c index ad9d38c..a23ab0b 100644 --- a/lib/mount_util.c +++ b/lib/mount_util.c @@ -2,6 +2,8 @@ FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + Architecture-independent mounting code. + This program can be distributed under the terms of the GNU LGPLv2. See the file COPYING.LIB. */ diff --git a/test/conftest.py b/test/conftest.py index 0da2f4b..70cd0c6 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -35,9 +35,13 @@ def check_test_output(capfd): if count == 0 or count - cnt > 0: stderr = cp.sub('', stderr, count=count - cnt) - for pattern in ('exception', 'error', 'warning', 'fatal', 'traceback', - 'fault', 'crash(?:ed)?', 'abort(?:ed)'): - cp = re.compile(r'\b{}\b'.format(pattern), re.IGNORECASE | re.MULTILINE) + patterns = [ r'\b{}\b'.format(x) for x in + ('exception', 'error', 'warning', 'fatal', 'traceback', + 'fault', 'crash(?:ed)?', 'abort(?:ed)', + 'uninitiali[zs]ed') ] + patterns += ['^==[0-9]+== '] + for pattern in patterns: + cp = re.compile(pattern, re.IGNORECASE | re.MULTILINE) hit = cp.search(stderr) if hit: raise AssertionError('Suspicious output to stderr (matched "%s")' % hit.group(0)) diff --git a/test/test_examples.py b/test/test_examples.py index 31d2fae..5ddc860 100755 --- a/test/test_examples.py +++ b/test/test_examples.py @@ -14,9 +14,10 @@ import shutil import filecmp import errno from tempfile import NamedTemporaryFile -from util import wait_for_mount, umount, cleanup +from util import wait_for_mount, umount, cleanup, base_cmdline +from os.path import join as pjoin -basename = os.path.join(os.path.dirname(__file__), '..') +basename = pjoin(os.path.dirname(__file__), '..') TEST_FILE = __file__ with open(TEST_FILE, 'rb') as fh: @@ -29,13 +30,17 @@ def name_generator(__ctr=[0]): @pytest.mark.parametrize("name", ('hello', 'hello_ll')) def test_hello(tmpdir, name): mnt_dir = str(tmpdir) - cmdline = [os.path.join(basename, 'example', name), - '-f', mnt_dir ] + cmdline = base_cmdline + \ + [ pjoin(basename, 'example', name), + '-f', mnt_dir ] + if name == 'hello_ll': + # supports single-threading only + cmdline.append('-s') mount_process = subprocess.Popen(cmdline) try: wait_for_mount(mount_process, mnt_dir) assert os.listdir(mnt_dir) == [ 'hello' ] - filename = os.path.join(mnt_dir, 'hello') + filename = pjoin(mnt_dir, 'hello') with open(filename, 'r') as fh: assert fh.read() == 'Hello World!\n' with pytest.raises(IOError) as exc_info: @@ -50,18 +55,51 @@ def test_hello(tmpdir, name): else: umount(mount_process, mnt_dir) +def test_fuse_lo_plus(tmpdir): + mnt_dir = str(tmpdir.mkdir('mnt')) + src_dir = str(tmpdir.mkdir('src')) + + cmdline = base_cmdline + \ + [ pjoin(basename, 'example', 'fuse_lo-plus'), + '-f', '-s', mnt_dir ] + mount_process = subprocess.Popen(cmdline) + try: + wait_for_mount(mount_process, mnt_dir) + work_dir = pjoin(mnt_dir, src_dir) + tst_write(work_dir) + tst_mkdir(work_dir) + tst_symlink(work_dir) + tst_mknod(work_dir) + if os.getuid() == 0: + tst_chown(work_dir) + # Underlying fs may not have full nanosecond resolution + tst_utimens(work_dir, ns_tol=1000) + tst_link(work_dir) + tst_readdir(work_dir) + tst_statvfs(work_dir) + tst_truncate_path(work_dir) + tst_truncate_fd(work_dir) + tst_unlink(work_dir) + tst_passthrough(src_dir, work_dir) + except: + cleanup(mnt_dir) + raise + else: + umount(mount_process, mnt_dir) + @pytest.mark.parametrize("name", ('fusexmp', 'fusexmp_fh')) def test_fusexmp_fh(tmpdir, name): mnt_dir = str(tmpdir.mkdir('mnt')) src_dir = str(tmpdir.mkdir('src')) - cmdline = [os.path.join(basename, 'example', name), - '-f', '-o' , 'use_ino,readdir_ino,kernel_cache', + cmdline = base_cmdline + \ + [ pjoin(basename, 'example', name), + '-f', '-o', 'use_ino,readdir_ino,kernel_cache', mnt_dir ] mount_process = subprocess.Popen(cmdline) try: wait_for_mount(mount_process, mnt_dir) - work_dir = os.path.join(mnt_dir, src_dir) + work_dir = pjoin(mnt_dir, src_dir) tst_write(work_dir) tst_mkdir(work_dir) tst_symlink(work_dir) @@ -85,20 +123,21 @@ def test_fusexmp_fh(tmpdir, name): def test_fioc(tmpdir): mnt_dir = str(tmpdir) - testfile = os.path.join(mnt_dir, 'fioc') - cmdline = [os.path.join(basename, 'example', 'fioc'), - '-f', mnt_dir ] + testfile = pjoin(mnt_dir, 'fioc') + cmdline = base_cmdline + \ + [pjoin(basename, 'example', 'fioc'), '-f', mnt_dir ] mount_process = subprocess.Popen(cmdline) try: wait_for_mount(mount_process, mnt_dir) - base_cmd = [ os.path.join(basename, 'example', 'fioclient'), - testfile ] - assert subprocess.check_output(base_cmd) == b'0\n' + cmdline = base_cmdline + \ + [ pjoin(basename, 'example', 'fioclient'), + testfile ] + assert subprocess.check_output(cmdline) == b'0\n' with open(testfile, 'wb') as fh: fh.write(b'foobar') - assert subprocess.check_output(base_cmd) == b'6\n' - subprocess.check_call(base_cmd + [ '3' ]) + assert subprocess.check_output(cmdline) == b'6\n' + subprocess.check_call(cmdline + [ '3' ]) with open(testfile, 'rb') as fh: assert fh.read()== b'foo' except: @@ -109,12 +148,13 @@ def test_fioc(tmpdir): def test_fsel(tmpdir): mnt_dir = str(tmpdir) - cmdline = [os.path.join(basename, 'example', 'fsel'), + cmdline = base_cmdline + [pjoin(basename, 'example', 'fsel'), '-f', mnt_dir ] mount_process = subprocess.Popen(cmdline) try: wait_for_mount(mount_process, mnt_dir) - cmdline = [ os.path.join(basename, 'example', 'fselclient') ] + cmdline = base_cmdline + \ + [ pjoin(basename, 'example', 'fselclient') ] subprocess.check_call(cmdline, cwd=mnt_dir) except: cleanup(mnt_dir) @@ -123,7 +163,7 @@ def test_fsel(tmpdir): umount(mount_process, mnt_dir) def checked_unlink(filename, path, isdir=False): - fullname = os.path.join(path, filename) + fullname = pjoin(path, filename) if isdir: os.rmdir(fullname) else: @@ -156,7 +196,7 @@ def tst_symlink(mnt_dir): checked_unlink(linkname, mnt_dir) def tst_mknod(mnt_dir): - filename = os.path.join(mnt_dir, name_generator()) + filename = pjoin(mnt_dir, name_generator()) shutil.copyfile(TEST_FILE, filename) fstat = os.lstat(filename) assert stat.S_ISREG(fstat.st_mode) @@ -166,7 +206,7 @@ def tst_mknod(mnt_dir): checked_unlink(filename, mnt_dir) def tst_chown(mnt_dir): - filename = os.path.join(mnt_dir, name_generator()) + filename = pjoin(mnt_dir, name_generator()) os.mkdir(filename) fstat = os.lstat(filename) uid = fstat.st_uid @@ -187,17 +227,17 @@ def tst_chown(mnt_dir): checked_unlink(filename, mnt_dir, isdir=True) def tst_write(mnt_dir): - name = os.path.join(mnt_dir, name_generator()) + name = pjoin(mnt_dir, name_generator()) shutil.copyfile(TEST_FILE, name) assert filecmp.cmp(name, TEST_FILE, False) checked_unlink(name, mnt_dir) def tst_unlink(mnt_dir): - name = os.path.join(mnt_dir, name_generator()) + name = pjoin(mnt_dir, name_generator()) data1 = b'foo' data2 = b'bar' - with open(os.path.join(mnt_dir, name), 'wb+', buffering=0) as fh: + with open(pjoin(mnt_dir, name), 'wb+', buffering=0) as fh: fh.write(data1) checked_unlink(name, mnt_dir) fh.write(data2) @@ -208,8 +248,8 @@ def tst_statvfs(mnt_dir): os.statvfs(mnt_dir) def tst_link(mnt_dir): - name1 = os.path.join(mnt_dir, name_generator()) - name2 = os.path.join(mnt_dir, name_generator()) + name1 = pjoin(mnt_dir, name_generator()) + name2 = pjoin(mnt_dir, name_generator()) shutil.copyfile(TEST_FILE, name1) assert filecmp.cmp(name1, TEST_FILE, False) os.link(name1, name2) @@ -228,7 +268,7 @@ def tst_link(mnt_dir): os.unlink(name1) def tst_readdir(mnt_dir): - dir_ = os.path.join(mnt_dir, name_generator()) + dir_ = pjoin(mnt_dir, name_generator()) file_ = dir_ + "/" + name_generator() subdir = dir_ + "/" + name_generator() subfile = subdir + "/" + name_generator() @@ -252,7 +292,7 @@ def tst_readdir(mnt_dir): def tst_truncate_path(mnt_dir): assert len(TEST_DATA) > 1024 - filename = os.path.join(mnt_dir, name_generator()) + filename = pjoin(mnt_dir, name_generator()) with open(filename, 'wb') as fh: fh.write(TEST_DATA) @@ -298,7 +338,7 @@ def tst_truncate_fd(mnt_dir): assert fh.read(size) == TEST_DATA[:size-1024] def tst_utimens(mnt_dir, ns_tol=0): - filename = os.path.join(mnt_dir, name_generator()) + filename = pjoin(mnt_dir, name_generator()) os.mkdir(filename) fstat = os.lstat(filename) @@ -323,8 +363,8 @@ def tst_utimens(mnt_dir, ns_tol=0): def tst_passthrough(src_dir, mnt_dir): name = name_generator() - src_name = os.path.join(src_dir, name) - mnt_name = os.path.join(src_dir, name) + src_name = pjoin(src_dir, name) + mnt_name = pjoin(src_dir, name) assert name not in os.listdir(src_dir) assert name not in os.listdir(mnt_dir) with open(src_name, 'w') as fh: @@ -334,8 +374,8 @@ def tst_passthrough(src_dir, mnt_dir): assert os.stat(src_name) == os.stat(mnt_name) name = name_generator() - src_name = os.path.join(src_dir, name) - mnt_name = os.path.join(src_dir, name) + src_name = pjoin(src_dir, name) + mnt_name = pjoin(src_dir, name) assert name not in os.listdir(src_dir) assert name not in os.listdir(mnt_dir) with open(mnt_name, 'w') as fh: diff --git a/test/test_fuse.py b/test/test_fuse.py index bbba6e0..3c60d80 100755 --- a/test/test_fuse.py +++ b/test/test_fuse.py @@ -7,7 +7,7 @@ if __name__ == '__main__': import subprocess import os -from util import wait_for_mount, umount, cleanup +from util import wait_for_mount, umount, cleanup, base_cmdline basename = os.path.join(os.path.dirname(__file__), '..') @@ -15,7 +15,8 @@ def test_fuse(tmpdir): mnt_dir = str(tmpdir.mkdir('mnt')) src_dir = str(tmpdir.mkdir('src')) - cmdline = [ os.path.join(basename, 'example', 'fusexmp_fh'), + cmdline = base_cmdline + \ + [ os.path.join(basename, 'example', 'fusexmp_fh'), '-f', '-o' , 'use_ino,readdir_ino,kernel_cache', mnt_dir ] mount_process = subprocess.Popen(cmdline) diff --git a/test/util.py b/test/util.py index 48ec995..e8bb9c2 100644 --- a/test/util.py +++ b/test/util.py @@ -36,3 +36,20 @@ def umount(mount_process, mnt_dir): time.sleep(0.1) elapsed += 0.1 pytest.fail('mount process did not terminate') + + +# If valgrind and libtool are available, use them +def has_program(name): + try: + ret = subprocess.call([name, '--version'], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + except FileNotFoundError: + return False + return ret == 0 + +if has_program('valgrind') and has_program('libtool'): + base_cmdline = [ 'libtool', '--mode=execute', + 'valgrind', '-q', '--' ] +else: + base_cmdline = [] |