diff options
37 files changed, 530 insertions, 935 deletions
@@ -35,3 +35,7 @@ config.* /patches* /m4 TAGS +/GPATH +/GRTAGS +/GSYMS +/GTAGS diff --git a/ChangeLog.rst b/ChangeLog.rst index a498273..0a6d00e 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -1,6 +1,35 @@ Unreleased Changes ================== +* The help text generated by fuse_lowlevel_help(), fuse_new() (and + indirectly fuse_main()) no longer includes options that are unlikely + to be of interest to end-users. The full list of accepted options is + now included in the respective function's documentation (located in + the fuse.h/fuse_lowlevel.h and doc/html). + +* The ``-o nopath`` option has been dropped - it never actually did + anything (since it is unconditionally overwritten with the value of + the `nopath` flag in `struct fuse_operations). + +* The ``-o large_read`` mount option has been dropped. Hopefully no + one uses a Linux 2.4 kernel anymore. + +* The `-o nonempty` mount point has been removed, mounting over + non-empty directories is now always allowed. This brings the + behavior of FUSE file systems in-line with the behavior of the + regular `mount` command. + + File systems that do not want to allow mounting to non-empty + directories should perform this check themselves before handing + control to libfuse. + +* The chmod, chown, truncate, utimens and getattr handlers of the + high-level API now all receive an additional struct fuse_file_info + pointer. This pointer is NULL if the file is not currently open. + + The fgetattr and ftruncate handlers have become obsolete and have + been removed. + * 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. @@ -9,22 +38,32 @@ Unreleased Changes 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` - handler. Third, command line arguments take precedence over any - modifications made by the `init` handler. +* The treatment of low-level options has been made more consistent: + + Options that can be set in the init() handler (via the + fuse_conn_info parameter) can now be set only here, + i.e. fuse_session_new() no longer accepts arguments that change the + fuse_conn_info object before or after the call do init(). As a side + effect, this removes the ambiguity where some options can be + overwritten by init(), while others overwrite the choices made by + init(). + + For file systems that wish to offer command line options for these + settings, the new fuse_parse_conn_info_opts() and + fuse_apply_conn_info_opts() functions are available. -* Removed the `async_read` field from `struct fuse_conn_info`. To - determine if the kernel supports asynchronous reads, file systems - should check the `FUSE_CAP_ASYNC_READ` bit of the `capable` - field. To enable/disable asynchronous reads, file systems should set - the flag in the `wanted` field. + Consequently, the fuse_lowlevel_help() method has been dropped. + +* The `async_read` field in `struct fuse_conn_info` has been + removed. To determine if the kernel supports asynchronous reads, + file systems should check the `FUSE_CAP_ASYNC_READ` bit of the + `capable` field. To enable/disable asynchronous reads, file systems + should set the flag in the `wanted` field. * The `fuse_parse_cmdline` function no longer prints out help when the ``--verbose`` or ``--help`` flags are given. This needs to be done - by the file system (e.g. using the `fuse_cmdline_help()`, - `fuse_lowlevel_help()` and `fuse_mount_help()` functions). + by the file system (e.g. using the `fuse_cmdline_help()` and + `fuse_lowlevel_help()` functions). * Added ``example/cuse_client.c`` to test ``example/cuse.c``. @@ -43,6 +82,7 @@ Unreleased Changes always active. File systems that want to limit the size of write requests should use the ``-o max_write=<N>`` option instead. + FUSE 3.0.0pre0 (2016-10-03) ============================ @@ -1,303 +0,0 @@ -What is new in 2.9 - - - Add "zero copy" support for kernel 2.6.35 or newer - - - Make maximum background requests tunable on kernel 2.6.32 or newer - - - Require --no-canonicalize in (u)mount (util-linux version 2.18 or - newer) to fix security problems with fusermount - - - Use dynamically sized hash tables in high level library - - - Memory use of filesystem daemon can shrink more easily - - - Add "auto_unmount" option - - - Add "remember" option - - - Add man pages for fusermount, mount.fuse and ulockmgr_server - - - API changes: - - o Introduce "store" and "retrieve" for accessing kernel buffers on - kernel 2.6.36 or newer - - o Introduce abstract buffer for zero copy operations - - o Allow path calculation to be omitted on certain operations - - o Allow batching forget requests - - o Add "flock" method - - o Add support for ioctl on directories - - o Add delete notification - - o Add fallocate operation (linux kernel 3.5 or newer) - - - Bug fixes and small improvements - -============================================================================ - -What is new in 2.8 - - - More scalable directory tree locking - - - Atomic open(O_TRUNC) support - - - Support big write requests on kernels 2.6.26 and newer - - - Out-of-tree fuse module removed - - - Better NFS exporting support - - - New ioctl and poll requests - - - New CUSE (Character Device in Userspace) interface - - - Allow umask processing in userspace - - - Added cache invalidation notifications - - - Bugfixes and small improvements - -============================================================================ - -What is new in 2.7 - - - Stacking support for the high level API - - - Add filename charset conversion module - - - Improved mounting - -============================================================================ - -What is new in 2.6 - - - Improved read characteristics (asynchronous reads) - - - Support for aborting filesystem connection - - - POSIX file locking support - - - Request interruption support - - - Building module for Linux kernels earlier than 2.6.9 not supported - - - Allow block device based filesystems to support swap files - - - Several bugs fixed, including a rare system hang on SMP - -============================================================================ - -What is new in 2.5 - - - Merge library part of FreeBSD port - - - New atomic create+open, access and ftruncate operations - - - On filesystems implementing the new create+open operation, and - running on Linux kernels 2.6.15 or later, the 'cp' operation will - work correctly when copying read-only files. - - - New option parsing interface added to the library - - - Lots of minor improvements and fixes - -============================================================================ - -What is new in 2.4 - - - Simplify device opening. Now '/dev/fuse' is a requirement - - - Allow module auto-loading if user has access to '/dev/fuse' - - - Allow mounting over a regular file for unprivileged users - - - Allow mounting of arbitrary FUSE filesystems from /etc/fstab - - - New mount options: 'umask=M', 'uid=N', 'gid=N' - - - Check for non-empty mountpoint, and refuse mount by default. New - mount option: 'nonempty' - - - Low level (inode based) API added - - - Allow 'direct_io' and 'keep_cache' options to be set on a - case-by-case basis on open. - - - Add 'attr_timeout' and 'entry_timeout' mount options to the - high-level library. Until now these timeouts were fixed at 1 sec. - - - Some bugfixes - -============================================================================ - -What is new in 2.3 - - - Add new directory related operations: opendir(), readdir(), - releasedir() and fsyncdir() - - - Add init() and destroy() operations which are called before the - event loop is started and after it has exited - - - Update kernel ABI so that on dual architectures (e.g. AMD64) 32bit - binaries work under a 64bit kernel - - - Bugfixes - -============================================================================ - -What is new in 2.2 - -Userspace changes: - - - Add fuse_file_info structure to file operations, this allows the - filesystem to return a file handle in open() which is passed to - read(), write(), flush(), fsync() and release(). - - - Add source compatibility with 2.1 and 1.4 releases - - - Binary compatibility with 2.1 release is retained - -Kernel changes: - - - Make requests interruptible. This prevents the filesystem to go - into an unbreakable deadlock with itself. - - - Make readpages() synchronous. Asynchronous requests are deadlock - prone, since they cannot be interrupted (see above) - - - Remove shared-writeable mapping support, which could deadlock the - machine - - - Remove INVALIDATE userspace initiated request - - - Update ABI to be independent of sizeof(long), so dual-size archs - don't cause problems - - - Remove /sys/fs/fuse/version. Version checking is now done through - the fuse device - - - Replace directory reading method on the kernel interface. Instead - of passing an open file descriptor to the kernel, send data through - the FUSE device, like all other operations. - -============================================================================ - -What is new in 2.1 - -* Bug fixes - -* Improved support for filesystems implementing a custom event-loop - -* Add 'pkg-config' support - -* Kernel module can be compiled separately - -============================================================================ - -What is new in 1.9 - -* Lots of bugs fixed - -* Minor modifications to the library API - -* Improvements to the kernel/userspace interface - -* Mounting by non-root made more secure - -* Build shared library in addition to the static one - -* Consolidated mount options - -* Optimized reading under 2.6 kernels - -* Direct I/O support - -* Support file I/O on deleted files - -* Extended attributes support - -============================================================================ - -What is new in 1.3 - -* Thanks to user bugreports and stress testing with LTP and sfx-linux -a number of bugs were fixed, some quite serious. - -* Fix compile problems with recent SuSE kernles - -============================================================================ - -What is new in 1.2 - -* Fix mount problems on recent 2.6 kernels with SELinux enabled - -* Fixed writing files lager than 2GBytes - -* Other bugfixes - -============================================================================ - -What is new in 1.1 - -* Support for the 2.6 kernels - -* Support for exporting filesystem over NFS in 2.6 kernels - -* Read efficiency improvements: read in 64k blocks instead of 4k -(Michael Grigoriev). Can be turned on with '-l' option of fusermount - -* Lazy automatic unmount - -* Added 'fsync()' VFS call to the FUSE interface - -* Bugfixes - -============================================================================ - -What is new in 1.0 - -* Cleanups and bugfixes - -* Added 'release()' VFS call to the FUSE interface - -* 64 bit file offsets (handling of > 4 GByte files) - -* libfuse is now under LGPL - -* New 'statfs' call (Mark Glines) - -* Cleaned up mount procedure (mostly by Mark Glines) - - NOTE: Binaries linked with with a previous version of libavfs may - not work with the new version of the fusermount program. In such - case recompile the program after installing the new libavfs library. - -* Fix for problems under linux kernel 2.4.19 - -============================================================================ - -What is new in 0.95 - -* Optimized read/write operations. Raw throughput has increased to -about 60Mbyte/s on a Celeron/360 - -* Python bindings by Jeff Epler - -* Perl bindings by Mark Glines - -* Improved multithreaded operation - -* Simplified library interface - -* Bugfixes - -============================================================================ - -What is new in 0.9: - -* Everything @@ -70,8 +70,6 @@ create the `configure` script. You'll also need a fuse kernel module (Linux kernels 2.6.14 or later contain FUSE support). -For more details see the file `INSTALL` - Security implications --------------------- @@ -97,8 +95,8 @@ Building your own filesystem ------------------------------ FUSE comes with several example file systems in the `examples` -directory. For example, the *fusexmp* example mirrors the contents of -the root directory under the mountpoint. Start from there and adapt +directory. For example, the *passthrough* examples mirror the contents +of the root directory under the mountpoint. Start from there and adapt the code! The documentation of the API functions and necessary callbacks is diff --git a/doc/Makefile.am b/doc/Makefile.am index 2f2fa0f..1644383 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -2,4 +2,4 @@ dist_man_MANS = fusermount.1 mount.fuse.8 -EXTRA_DIST = how-fuse-works kernel.txt Doxyfile html +EXTRA_DIST = kernel.txt Doxyfile html README.NFS developer-notes.rst diff --git a/README.NFS b/doc/README.NFS index f3348d5..239dcb2 100644 --- a/README.NFS +++ b/doc/README.NFS @@ -15,11 +15,11 @@ using the inode number, otherwise an ESTALE error will be returned. 1) low-level interface -Filesystems need to implement special lookups for the names "." and -"..". The former may be requested on any inode, including -non-directories, while the latter is only requested for directories. -Otherwise these special lookups should behave identically to ordinary -lookups. +Filesystems need to set FUSE_CAP_EXPORT_SUPPORT in conn->wants and +implement special lookups for the names "." and "..". The former may +be requested on any inode, including non-directories, while the latter +is only requested for directories. Otherwise these special lookups +should behave identically to ordinary lookups. 2) high-level interface diff --git a/doc/fusermount.1 b/doc/fusermount.1 index 86998e2..5cd4305 100644 --- a/doc/fusermount.1 +++ b/doc/fusermount.1 @@ -9,7 +9,9 @@ .SH DESCRIPTION Filesystem in Userspace (FUSE) is a simple interface for userspace programs to export a virtual filesystem to the Linux kernel. It also aims to provide a secure method for non privileged users to create and mount their own filesystem implementations. .PP -\fBfusermount\fR is a program to mount and unmount FUSE filesystems. +\fBfusermount\fR is a program to mount and unmount FUSE +filesystems. It should be called directly only for unmounting FUSE +file systems. .SH OPTIONS .IP "\-h" 4 @@ -28,12 +30,14 @@ lazy unmount. .SH SEE ALSO \fImount\fR(8), \fImount.fuse\fR(8), -\fIulockmgr_server\fR(1). .SH HOMEPAGE More information about fusermount and the FUSE project can be found at <\fIhttp://fuse.sourceforge.net/\fR>. -.SH AUTHOR -FUSE was written by Miklos Szeredi <\fImiklos@szeredi.hu\fR>. -.PP -This manual page was written by Daniel Baumann <\fIdaniel.baumann@progress\-technologies.net\fR>. +.SH AUTHORS +.LP +FUSE is currently maintained by Nikolaus Rath <Nikolaus@rath.org> +.LP +The original author of FUSE is Miklos Szeredi <\fImiklos@szeredi.hu\fR>. +.LP +This manual page was originally written by Daniel Baumann <\fIdaniel.baumann@progress\-technologies.net\fR>. diff --git a/doc/how-fuse-works b/doc/how-fuse-works deleted file mode 100644 index a5febe3..0000000 --- a/doc/how-fuse-works +++ /dev/null @@ -1,54 +0,0 @@ - How Fuse-1.3 Works - -[Written by Terje Oseberg] - -1. The fuse library. - -When your user mode program calls fuse_main() (lib/helper.c), -fuse_main() parses the arguments passed to your user mode program, -then calls fuse_mount() (lib/mount.c). - -fuse_mount() creates a UNIX domain socket pair, then forks and execs -fusermount (util/fusermount.c) passing it one end of the socket in the -FUSE_COMMFD_ENV environment variable. - -fusermount (util/fusermount.c) makes sure that the fuse module is -loaded. fusermount then open /dev/fuse and send the file handle over a -UNIX domain socket back to fuse_mount(). - -fuse_mount() returns the filehandle for /dev/fuse to fuse_main(). - -fuse_main() calls fuse_new() (lib/fuse.c) which allocates the struct -fuse datastructure that stores and maintains a cached image of the -filesystem data. - -Lastly, fuse_main() calls either fuse_loop() (lib/fuse.c) or -fuse_loop_mt() (lib/fuse_mt.c) which both start to read the filesystem -system calls from the /dev/fuse, call the usermode functions -stored in struct fuse_operations datastructure before calling -fuse_main(). The results of those calls are then written back to the -/dev/fuse file where they can be forwarded back to the system -calls. - -2. The kernel module. - -The kernel module consists of two parts. First the proc filesystem -component in kernel/dev.c -and second the filesystem system calls -kernel/file.c, kernel/inode.c, and kernel/dir.c - -All the system calls in kernel/file.c, kernel/inode.c, and -kernel/dir.c make calls to either request_send(), -request_send_noreply(), or request_send_nonblock(). Most of the calls -(all but 2) are to request_send(). request_send() adds the request to, -"list of requests" structure (fc->pending), then waits for a response. -request_send_noreply() and request_send_nonblock() are both similar in -function to request_send() except that one is non-blocking, and the -other does not respond with a reply. - -The proc filesystem component in kernel/dev.c responds to file io -requests to the file /dev/fuse. fuse_dev_read() handles the -file reads and returns commands from the "list of requests" structure -to the calling program. fuse_dev_write() handles file writes and takes -the data written and places them into the req->out datastructure where -they can be returned to the system call through the "list of requests" -structure and request_send(). diff --git a/doc/kernel.txt b/doc/kernel.txt index 397a41a..fd3f174 100644 --- a/doc/kernel.txt +++ b/doc/kernel.txt @@ -49,7 +49,7 @@ using the sftp protocol. The userspace library and utilities are available from the FUSE homepage: - http://fuse.sourceforge.net/ + https://github.com/libfuse/libfuse/ Filesystem type ~~~~~~~~~~~~~~~ diff --git a/doc/mount.fuse.8 b/doc/mount.fuse.8 index 88dd3f4..dbe0217 100644 --- a/doc/mount.fuse.8 +++ b/doc/mount.fuse.8 @@ -12,7 +12,17 @@ Set the maximum number of FUSE mounts allowed to non-root users. The default is \fBuser_allow_other\fP Allow non-root users to specify the \fBallow_other\fP or \fBallow_root\fP mount options (see below). .SH OPTIONS -Most of the generic mount options described in \fBmount\fP are supported (\fBro\fP, \fBrw\fP, \fBsuid\fP, \fBnosuid\fP, \fBdev\fP, \fBnodev\fP, \fBexec\fP, \fBnoexec\fP, \fBatime\fP, \fBnoatime\fP, \fBsync\fP, \fBasync\fP, \fBdirsync\fP). Filesystems are mounted with \fBnodev,nosuid\fP by default, which can only be overridden by a privileged user. +Most of the generic mount options described in \fBmount\fP are +supported (\fBro\fP, \fBrw\fP, \fBsuid\fP, \fBnosuid\fP, \fBdev\fP, +\fBnodev\fP, \fBexec\fP, \fBnoexec\fP, \fBatime\fP, \fBnoatime\fP, +\fBsync\fP, \fBasync\fP, \fBdirsync\fP). Filesystems are mounted with +\fBnodev,nosuid\fP by default, which can only be overridden by a +privileged user. +.SS "WARNING" +Many of the mount options described below should not be specified +directly by the user mounting the file system but chosen by the +file-system itself when it calls mount(2). This is because the correct +values depend on implementation details of the file system. .SS "General mount options:" These are FUSE specific mount options that can be specified for all filesystems: .TP @@ -25,23 +35,12 @@ This option overrides the security measure restricting file access to the user m \fBallow_root\fP This option is similar to \fBallow_other\fP but file access is limited to the user mounting the filesystem and root. This option and \fBallow_other\fP are mutually exclusive. .TP -\fBkernel_cache\fP -This option disables flushing the cache of the file contents on every \fBopen\fP(2). This should only be enabled on filesystems, where the file data is never changed externally (not through the mounted FUSE filesystem). Thus it is not suitable for network filesystems and other \fIintermediate\fP filesystems. - -\fBNOTE\fP: if this option is not specified (and neither \fBdirect_io\fP) data is still cached after the \fBopen\fP(2), so a \fBread\fP(2) system call will not always initiate a read operation. -.TP -\fBauto_cache\fP -This option enables automatic flushing of the data cache on \fBopen\fP(2). The cache will only be flushed if the modification time or the size of the file has changed. -.TP -\fBlarge_read\fP -Issue large read requests. This can improve performance for some filesystems, but can also degrade performance. This option is only useful on 2.4.X kernels, as on 2.6 kernels requests size is automatically determined for optimum performance. -.TP -\fBdirect_io\fP -This option disables the use of page cache (file content cache) in the kernel for this filesystem. This has several affects: -.IP 1. -Each \fBread\fP(2) or \fBwrite\fP(2) system call will initiate one or more read or write operations, data will not be cached in the kernel. -.IP 2. -The return value of the read() and write() system calls will correspond to the return values of the read and write operations. This is useful for example if the file size is not known in advance (before reading it). +\fBauto_unmount\fP +This option enables automatic unmounting of the file system if the +FUSE process terminates for any reason. Normally the FUSE process is +responsible for un-mounting the file system, which means that the +mountpoint becomes inaccessible if the file system process terminates +without first unmounting the file system. .TP \fBmax_read=N\fP With this option the maximum size of read operations can be set. The default is infinite. Note that the size of read requests is limited anyway to 32 pages (which is 128kbyte on i386). @@ -50,7 +49,7 @@ With this option the maximum size of read operations can be set. The default is Set the maximum number of bytes to read-ahead. The default is determined by the kernel. On linux-2.6.22 or earlier it's 131072 (128kbytes) .TP \fBmax_write=N\fP -Set the maximum number of bytes in a single write operation. The default is 128kbytes. Note, that due to various limitations, the size of write requests can be much smaller (4kbytes). This limitation will be removed in the future. +Set the maximum number of bytes in a single write operation. The default is 128kbytes. .TP \fBasync_read\fP Perform reads asynchronously. This is the default @@ -58,11 +57,6 @@ Perform reads asynchronously. This is the default \fBsync_read\fP Perform all reads (even read-ahead) synchronously. .TP -\fBhard_remove\fP -The default behavior is that if an open file is deleted, the file is renamed to a hidden file (\fB.fuse_hiddenXXX\fP), and only removed when the file is finally released. This relieves the filesystem implementation of having to deal with this problem. This option disables the hiding behavior, and files are removed immediately in an unlink operation (or in a rename operation which overwrites an existing file). - -It is recommended that you not use the hard_remove option. When hard_remove is set, the following libc functions fail on unlinked files (returning errno of \fBENOENT\fP): \fBread\fP(2), \fBwrite\fP(2), \fBfsync\fP(2), \fBclose\fP(2), \fBf*xattr\fP(2), \fBftruncate\fP(2), \fBfstat\fP(2), \fBfchmod\fP(2), \fBfchown\fP(2) -.TP \fBdebug\fP Turns on debug information printing by the library. .TP @@ -74,6 +68,33 @@ Sets the filesystem type (third field in \fI/etc/mtab\fP). The default is the mo If the kernel doesn't support subtypes, the source filed will be \fBTYPE#NAME\fP, or if \fBfsname\fP option is not specified, just \fBTYPE\fP. .TP +\fBblkdev\fP +Mount a filesystem backed by a block device. This is a privileged option. The device must be specified with the \fBfsname=NAME\fP option. +.SS "High-level mount options:" +These are FUSE specific mount options that can only be specified for +file-systems that use the high-level FUSE API: +.TP +\fBkernel_cache\fP +This option disables flushing the cache of the file contents on every \fBopen\fP(2). This should only be enabled on filesystems, where the file data is never changed externally (not through the mounted FUSE filesystem). Thus it is not suitable for network filesystems and other \fIintermediate\fP filesystems. + +\fBNOTE\fP: if this option is not specified (and neither \fBdirect_io\fP) data is still cached after the \fBopen\fP(2), so a \fBread\fP(2) system call will not always initiate a read operation. +.TP +\fBauto_cache\fP +This option enables automatic flushing of the data cache on \fBopen\fP(2). The cache will only be flushed if the modification time or the size of the file has changed. +.TP +.TP +\fBdirect_io\fP +This option disables the use of page cache (file content cache) in the kernel for this filesystem. This has several affects: +.IP 1. +Each \fBread\fP(2) or \fBwrite\fP(2) system call will initiate one or more read or write operations, data will not be cached in the kernel. +.IP 2. +The return value of the read() and write() system calls will correspond to the return values of the read and write operations. This is useful for example if the file size is not known in advance (before reading it). +.TP +\fBhard_remove\fP +The default behavior is that if an open file is deleted, the file is renamed to a hidden file (\fB.fuse_hiddenXXX\fP), and only removed when the file is finally released. This relieves the filesystem implementation of having to deal with this problem. This option disables the hiding behavior, and files are removed immediately in an unlink operation (or in a rename operation which overwrites an existing file). + +It is recommended that you not use the hard_remove option. When hard_remove is set, the following libc functions fail on unlinked files (returning errno of \fBENOENT\fP): \fBread\fP(2), \fBwrite\fP(2), \fBfsync\fP(2), \fBclose\fP(2), \fBf*xattr\fP(2), \fBftruncate\fP(2), \fBfstat\fP(2), \fBfchmod\fP(2), \fBfchown\fP(2) +.TP \fBuse_ino\fP Honor the \fIst_ino\fP field in kernel functions \fBgetattr()\fP and \fBfill_dir()\fP. This value is used to fill in the \fIst_ino\fP field in the \fBstat\fP(2), \fBlstat\fP(2), \fBfstat\fP(2) functions and the \fId_ino\fP field in the \fBreaddir\fP(2) function. The filesystem does not have to guarantee uniqueness, however some applications rely on this value being unique for the whole filesystem. @@ -81,9 +102,6 @@ Honor the \fIst_ino\fP field in kernel functions \fBgetattr()\fP and \fBfill_dir \fBreaddir_ino\fP If \fBuse_ino\fP option is not given, still try to fill in the \fId_ino\fP field in \fBreaddir\fP(2). If the name was previously looked up, and is still in the cache, the inode number found there will be used. Otherwise it will be set to \fB-1\fP. If \fBuse_ino\fP option is given, this option is ignored. .TP -\fBnonempty\fP -Allows mounts over a non-empty file or directory. By default these mounts are rejected to prevent accidental covering up of data, which could for example prevent automatic backup. -.TP \fBumask=M\fP Override the permission bits in \fIst_mode\fP set by the filesystem. The resulting permission bits are the ones missing from the given umask value. The value is given in octal representation. .TP @@ -93,9 +111,6 @@ Override the \fIst_uid\fP field set by the filesystem (N is numeric). \fBgid=N\fP Override the \fIst_gid\fP field set by the filesystem (N is numeric). .TP -\fBblkdev\fP -Mount a filesystem backed by a block device. This is a privileged option. The device must be specified with the \fBfsname=NAME\fP option. -.TP \fBentry_timeout=T\fP The timeout in seconds for which name lookups will be cached. The default is 1.0 second. For all the timeout options, it is possible to give fractions of a second as well (e.g. \fBentry_timeout=2.8\fP) .TP @@ -112,7 +127,15 @@ The timeout in seconds for which file attributes are cached for the purpose of c Allow requests to be interrupted. Turning on this option may result in unexpected behavior, if the filesystem does not support request interruption. .TP \fBintr_signal=NUM\fP -Specify which signal number to send to the filesystem when a request is interrupted. The default is hardcoded to USR1. +Specify which signal number to send to the filesystem when a request +is interrupted. The default is hardcoded to USR1. +.TP +\fBnoforget\fF +Normally, FUSE assigns inodes to paths only for as long as the kernel +is aware of them. With this option inodes are instead assigned permanently for +the life-time of the file-system process. This will require more +memory, but may be necessary when using applications that make use of +inode numbers. .TP \fBmodules=M1[:M2...]\fP Add modules to the filesystem stack. Modules are pushed in the order they are specified, with the original filesystem being on the bottom of the stack. @@ -152,10 +175,12 @@ No other user (including root) can access the contents of the mounted filesystem FUSE filesystems are unmounted using the \fBfusermount\fP(1) command (\fBfusermount -u mountpoint\fP). .SH "AUTHORS" .LP -The main author of FUSE is Miklos Szeredi <mszeredi@inf.bme.hu>. +FUSE is currently maintained by Nikolaus Rath <Nikolaus@rath.org> +.LP +The original author of FUSE is Miklos Szeredi <mszeredi@inf.bme.hu>. .LP -This man page was written by Bastien Roucaries <roucaries.bastien+debian@gmail.com> for the -Debian GNU/Linux distribution (but it may be used by others) from README file. +This man page was originally written by Bastien Roucaries <roucaries.bastien+debian@gmail.com> for the +Debian GNU/Linux distribution. .SH SEE ALSO fusermount(1) mount(8) diff --git a/example/hello.c b/example/hello.c index bf6ccf4..c587d72 100644 --- a/example/hello.c +++ b/example/hello.c @@ -68,8 +68,10 @@ static const struct fuse_opt option_spec[] = { FUSE_OPT_END }; -static int hello_getattr(const char *path, struct stat *stbuf) +static int hello_getattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi) { + (void) fi; int res = 0; memset(stbuf, 0, sizeof(struct stat)); diff --git a/example/hello_ll.c b/example/hello_ll.c index e0ce610..d673a5d 100644 --- a/example/hello_ll.c +++ b/example/hello_ll.c @@ -196,13 +196,11 @@ int main(int argc, char *argv[]) printf("usage: %s [options] <mountpoint>\n\n", argv[0]); fuse_cmdline_help(); fuse_lowlevel_help(); - fuse_mount_help(); ret = 0; goto err_out1; } else if (opts.show_version) { printf("FUSE library version %s\n", fuse_pkgversion()); fuse_lowlevel_version(); - fuse_mount_version(); ret = 0; goto err_out1; } diff --git a/example/ioctl.c b/example/ioctl.c index ee58f03..0a4c14d 100644 --- a/example/ioctl.c +++ b/example/ioctl.c @@ -83,8 +83,10 @@ static int fioc_file_type(const char *path) return FIOC_NONE; } -static int fioc_getattr(const char *path, struct stat *stbuf) +static int fioc_getattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi) { + (void) fi; stbuf->st_uid = getuid(); stbuf->st_gid = getgid(); stbuf->st_atime = stbuf->st_mtime = time(NULL); @@ -160,8 +162,10 @@ static int fioc_write(const char *path, const char *buf, size_t size, return fioc_do_write(buf, size, offset); } -static int fioc_truncate(const char *path, off_t size) +static int fioc_truncate(const char *path, off_t size, + struct fuse_file_info *fi) { + (void) fi; if (fioc_file_type(path) != FIOC_FILE) return -EINVAL; diff --git a/example/notify_inval_entry.c b/example/notify_inval_entry.c index 1c2a6c9..7c33dc4 100644 --- a/example/notify_inval_entry.c +++ b/example/notify_inval_entry.c @@ -288,13 +288,11 @@ int main(int argc, char *argv[]) { show_help(argv[0]); fuse_cmdline_help(); fuse_lowlevel_help(); - fuse_mount_help(); ret = 0; goto err_out1; } else if (opts.show_version) { printf("FUSE library version %s\n", fuse_pkgversion()); fuse_lowlevel_version(); - fuse_mount_version(); ret = 0; goto err_out1; } diff --git a/example/notify_inval_inode.c b/example/notify_inval_inode.c index c9ab4d8..934be60 100644 --- a/example/notify_inval_inode.c +++ b/example/notify_inval_inode.c @@ -311,13 +311,11 @@ int main(int argc, char *argv[]) { show_help(argv[0]); fuse_cmdline_help(); fuse_lowlevel_help(); - fuse_mount_help(); ret = 0; goto err_out1; } else if (opts.show_version) { printf("FUSE library version %s\n", fuse_pkgversion()); fuse_lowlevel_version(); - fuse_mount_version(); ret = 0; goto err_out1; } diff --git a/example/notify_store_retrieve.c b/example/notify_store_retrieve.c index 5b5fa63..6c8678a 100644 --- a/example/notify_store_retrieve.c +++ b/example/notify_store_retrieve.c @@ -354,13 +354,11 @@ int main(int argc, char *argv[]) { show_help(argv[0]); fuse_cmdline_help(); fuse_lowlevel_help(); - fuse_mount_help(); ret = 0; goto err_out1; } else if (opts.show_version) { printf("FUSE library version %s\n", fuse_pkgversion()); fuse_lowlevel_version(); - fuse_mount_version(); ret = 0; goto err_out1; } diff --git a/example/passthrough.c b/example/passthrough.c index cf72cb2..d3d0fde 100644 --- a/example/passthrough.c +++ b/example/passthrough.c @@ -48,8 +48,10 @@ #include <sys/xattr.h> #endif -static int xmp_getattr(const char *path, struct stat *stbuf) +static int xmp_getattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi) { + (void) fi; int res; res = lstat(path, stbuf); @@ -200,8 +202,10 @@ static int xmp_link(const char *from, const char *to) return 0; } -static int xmp_chmod(const char *path, mode_t mode) +static int xmp_chmod(const char *path, mode_t mode, + struct fuse_file_info *fi) { + (void) fi; int res; res = chmod(path, mode); @@ -211,8 +215,10 @@ static int xmp_chmod(const char *path, mode_t mode) return 0; } -static int xmp_chown(const char *path, uid_t uid, gid_t gid) +static int xmp_chown(const char *path, uid_t uid, gid_t gid, + struct fuse_file_info *fi) { + (void) fi; int res; res = lchown(path, uid, gid); @@ -222,8 +228,10 @@ static int xmp_chown(const char *path, uid_t uid, gid_t gid) return 0; } -static int xmp_truncate(const char *path, off_t size) +static int xmp_truncate(const char *path, off_t size, + struct fuse_file_info *fi) { + (void) fi; int res; res = truncate(path, size); @@ -234,8 +242,10 @@ static int xmp_truncate(const char *path, off_t size) } #ifdef HAVE_UTIMENSAT -static int xmp_utimens(const char *path, const struct timespec ts[2]) +static int xmp_utimens(const char *path, const struct timespec ts[2], + struct fuse_file_info *fi) { + (void) fi; int res; /* don't use utime/utimes since they follow symlinks */ diff --git a/example/passthrough_fh.c b/example/passthrough_fh.c index a179f65..35d909f 100644 --- a/example/passthrough_fh.c +++ b/example/passthrough_fh.c @@ -52,25 +52,17 @@ #endif #include <sys/file.h> /* flock(2) */ -static int xmp_getattr(const char *path, struct stat *stbuf) -{ - int res; - - res = lstat(path, stbuf); - if (res == -1) - return -errno; - - return 0; -} - -static int xmp_fgetattr(const char *path, struct stat *stbuf, +static int xmp_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { int res; (void) path; - res = fstat(fi->fh, stbuf); + if(fi) + res = fstat(fi->fh, stbuf); + else + res = lstat(path, stbuf); if (res == -1) return -errno; @@ -272,47 +264,46 @@ static int xmp_link(const char *from, const char *to) return 0; } -static int xmp_chmod(const char *path, mode_t mode) -{ - int res; - - res = chmod(path, mode); - if (res == -1) - return -errno; - - return 0; -} - -static int xmp_chown(const char *path, uid_t uid, gid_t gid) +static int xmp_chmod(const char *path, mode_t mode, + struct fuse_file_info *fi) { int res; - res = lchown(path, uid, gid); + if(fi) + res = fchmod(fi->fh, mode); + else + res = chmod(path, mode); if (res == -1) return -errno; return 0; } -static int xmp_truncate(const char *path, off_t size) +static int xmp_chown(const char *path, uid_t uid, gid_t gid, + struct fuse_file_info *fi) { int res; - res = truncate(path, size); + if (fi) + res = fchown(fi->fh, uid, gid); + else + res = lchown(path, uid, gid); if (res == -1) return -errno; return 0; } -static int xmp_ftruncate(const char *path, off_t size, +static int xmp_truncate(const char *path, off_t size, struct fuse_file_info *fi) { int res; - (void) path; + if(fi) + res = ftruncate(fi->fh, size); + else + res = truncate(path, size); - res = ftruncate(fi->fh, size); if (res == -1) return -errno; @@ -320,12 +311,16 @@ static int xmp_ftruncate(const char *path, off_t size, } #ifdef HAVE_UTIMENSAT -static int xmp_utimens(const char *path, const struct timespec ts[2]) +static int xmp_utimens(const char *path, const struct timespec ts[2], + struct fuse_file_info *fi) { int res; /* don't use utime/utimes since they follow symlinks */ - res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW); + if (fi) + res = futimens(fi->fh, ts); + else + res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW); if (res == -1) return -errno; @@ -550,7 +545,6 @@ static int xmp_flock(const char *path, struct fuse_file_info *fi, int op) static struct fuse_operations xmp_oper = { .getattr = xmp_getattr, - .fgetattr = xmp_fgetattr, .access = xmp_access, .readlink = xmp_readlink, .opendir = xmp_opendir, @@ -566,7 +560,6 @@ static struct fuse_operations xmp_oper = { .chmod = xmp_chmod, .chown = xmp_chown, .truncate = xmp_truncate, - .ftruncate = xmp_ftruncate, #ifdef HAVE_UTIMENSAT .utimens = xmp_utimens, #endif diff --git a/example/passthrough_ll.c b/example/passthrough_ll.c index df9d7d3..dc5e10a 100644 --- a/example/passthrough_ll.c +++ b/example/passthrough_ll.c @@ -470,13 +470,11 @@ int main(int argc, char *argv[]) printf("usage: %s [options] <mountpoint>\n\n", argv[0]); fuse_cmdline_help(); fuse_lowlevel_help(); - fuse_mount_help(); ret = 0; goto err_out1; } else if (opts.show_version) { printf("FUSE library version %s\n", fuse_pkgversion()); fuse_lowlevel_version(); - fuse_mount_version(); ret = 0; goto err_out1; } diff --git a/example/poll.c b/example/poll.c index 61c5365..10175a3 100644 --- a/example/poll.c +++ b/example/poll.c @@ -64,8 +64,10 @@ static int fsel_path_index(const char *path) return ch <= '9' ? ch - '0' : ch - 'A' + 10; } -static int fsel_getattr(const char *path, struct stat *stbuf) +static int fsel_getattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi) { + (void) fi; int idx; memset(stbuf, 0, sizeof(struct stat)); diff --git a/include/fuse.h b/include/fuse.h index 5b9082b..52c915c 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -105,7 +105,13 @@ struct fuse_operations { * the following operations: * * read, write, flush, release, fsync, readdir, releasedir, - * fsyncdir, ftruncate, fgetattr, lock, ioctl and poll + * fsyncdir, lock, ioctl and poll + * + * For the following operations, the path will not be + * calculated only if the file is currently open (i.e., the + * struct fuse_file_info argument is non-NULL): + * + * truncate, getattr, chmod, chown, utimens * * If this flag is set then the path will not be calculaged even if the * file wasn't unlinked. However the path can still be non-NULL if it @@ -121,10 +127,12 @@ struct fuse_operations { /** Get file attributes. * * Similar to stat(). The 'st_dev' and 'st_blksize' fields are - * ignored. The 'st_ino' field is ignored except if the 'use_ino' + * ignored. The 'st_ino' field is ignored except if the 'use_ino' * mount option is given. + * + * *fi* will be NULL if the file is not currenly opened. */ - int (*getattr) (const char *, struct stat *); + int (*getattr) (const char *, struct stat *, struct fuse_file_info *fi); /** Read the target of a symbolic link * @@ -167,14 +175,23 @@ struct fuse_operations { /** Create a hard link to a file */ int (*link) (const char *, const char *); - /** Change the permission bits of a file */ - int (*chmod) (const char *, mode_t); + /** Change the permission bits of a file + * + * *fi* will be NULL if the file is not currenly opened. + */ + int (*chmod) (const char *, mode_t, struct fuse_file_info *fi); - /** Change the owner and group of a file */ - int (*chown) (const char *, uid_t, gid_t); + /** Change the owner and group of a file + * + * *fi* will be NULL if the file is not currenly opened. + */ + int (*chown) (const char *, uid_t, gid_t, struct fuse_file_info *fi); - /** Change the size of a file */ - int (*truncate) (const char *, off_t); + /** Change the size of a file + * + * *fi* will be NULL if the file is not currenly opened. + */ + int (*truncate) (const char *, off_t, struct fuse_file_info *fi); /** File open operation * @@ -388,34 +405,6 @@ struct fuse_operations { int (*create) (const char *, mode_t, struct fuse_file_info *); /** - * Change the size of an open file - * - * This method is called instead of the truncate() method if the - * truncation was invoked from an ftruncate() system call. - * - * If this method is not implemented or under Linux kernel - * versions earlier than 2.6.15, the truncate() method will be - * called instead. - * - * Introduced in version 2.5 - */ - int (*ftruncate) (const char *, off_t, struct fuse_file_info *); - - /** - * Get attributes from an open file - * - * This method is called instead of the getattr() method if the - * file information is available. - * - * Currently this is only called after the create() method if that - * is implemented (see above). Later it may be called for - * invocations of fstat() too. - * - * Introduced in version 2.5 - */ - int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *); - - /** * Perform POSIX file locking operation * * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW. @@ -457,11 +446,14 @@ struct fuse_operations { * This supersedes the old utime() interface. New applications * should use this. * + * *fi* will be NULL if the file is not currenly opened. + * * See the utimensat(2) man page for details. * * Introduced in version 2.6 */ - int (*utimens) (const char *, const struct timespec tv[2]); + int (*utimens) (const char *, const struct timespec tv[2], + struct fuse_file_info *fi); /** * Map block index within file to block index within device @@ -623,8 +615,8 @@ struct fuse_context { * passing NULL as the processing function). That way, the remaining * options can be passed directly to fuse_main(). * - * To get a list of the options recognized by fuse_main(), look - * at the output when running with ``--help``. + * fuse_main() accepts all options that can be passed to + * fuse_parse_cmdline(), fuse_new(), or fuse_session_new(). * * Normally, fuse_main() includes a basic ``usage: `` message in the * --help output. However, if argv[0] is an empty string, the usage @@ -656,14 +648,16 @@ struct fuse_context { /** * Create a new FUSE filesystem. * - * 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 not all options are known, an error message is - * written to stderr and the function returns NULL. + * This function accepts most file-system independent mount options + * (like context, nodev, ro - see mount(8)), as well as the + * FUSE-specific mount options from mount.fuse(8). * * If the --help option is specified, the function writes a help text * to stdout and returns NULL. * + * If an unknown option is passed in, an error message is written to + * stderr and the function returns NULL. + * * @param args argument vector * @param op the filesystem operations * @param op_size the size of the fuse_operations structure @@ -849,9 +843,8 @@ struct fuse_fs; * fuse_fs_releasedir and fuse_fs_statfs, which return 0. */ -int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf); -int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf, - struct fuse_file_info *fi); +int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf, + struct fuse_file_info *fi); int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, const char *newpath, unsigned int flags); int fuse_fs_unlink(struct fuse_fs *fs, const char *path); @@ -893,13 +886,14 @@ int fuse_fs_lock(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi, int cmd, struct flock *lock); int fuse_fs_flock(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi, int op); -int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode); -int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid); -int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size); -int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, off_t size, - struct fuse_file_info *fi); +int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode, + struct fuse_file_info *fi); +int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid, + struct fuse_file_info *fi); +int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size, + struct fuse_file_info *fi); int fuse_fs_utimens(struct fuse_fs *fs, const char *path, - const struct timespec tv[2]); + const struct timespec tv[2], struct fuse_file_info *fi); int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask); int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf, size_t len); diff --git a/include/fuse_common.h b/include/fuse_common.h index 55f0de2..2a0db73 100644 --- a/include/fuse_common.h +++ b/include/fuse_common.h @@ -205,6 +205,61 @@ struct fuse_conn_info { struct fuse_session; struct fuse_pollhandle; +struct fuse_conn_info_opts; + +/** + * This function parses several command-line options that can be used + * to override elements of struct fuse_conn_info. The pointer returned + * by this function should be passed to the + * fuse_apply_conn_info_opts() method by the file system's init() + * handler. + * + * Before using this function, think twice if you really want these + * parameters to be adjustable from the command line. In most cases, + * they should be determined by the file system internally. + * + * The following options are recognized: + * + * -o max_write=N sets conn->max_write + * -o max_readahead=N sets conn->max_readahead + * -o max_background=N sets conn->max_background + * -o congestion_threshold=N sets conn->congestion_threshold + * -o async_read sets FUSE_CAP_ASYNC_READ in conn->want + * -o sync_read unsets FUSE_CAP_ASYNC_READ in conn->want + * -o atomic_o_trunc sets FUSE_CAP_ATOMIC_O_TRUNC in conn->want + * -o no_remote_lock Equivalent to -o no_remote_flock,no_remote_posix_lock + * -o no_remote_flock Unsets FUSE_CAP_FLOCK_LOCKS in conn->want + * -o no_remote_posix_lock Unsets FUSE_CAP_POSIX_LOCKS in conn->want + * -o [no_]splice_write (un-)sets FUSE_CAP_SPLICE_WRITE in conn->want + * -o [no_]splice_move (un-)sets FUSE_CAP_SPLICE_MOVE in conn->want + * -o [no_]splice_read (un-)sets FUSE_CAP_SPLICE_READ in conn->want + * -o [no_]auto_inval_data (un-)sets FUSE_CAP_AUTO_INVAL_DATA in conn->want + * -o readdirplus=no unsets FUSE_CAP_READDIRPLUS in conn->want + * -o readdirplus=yes sets FUSE_CAP_READDIRPLUS and unsets + * FUSE_CAP_READDIRPLUS_AUTO in conn->want + * -o readdirplus=auto sets FUSE_CAP_READDIRPLUS and + * FUSE_CAP_READDIRPLUS_AUTO in conn->want + * -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in conn->want + * -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in conn->want + * -o time_gran=N sets conn->time_gran + * + * Known options will be removed from *args*, unknown options will be + * passed through unchanged. + * + * @param args argument vector (input+output) + * @return parsed options + **/ +struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args); + +/** + * This function applies the (parsed) parameters in *opts* to the + * *conn* pointer. It may modify the following fields: wants, + * max_write, max_readahead, congestion_threshold, max_background, + * time_gran. A field is only set (or unset) if the corresponding + * option has been explicitly set. + */ +void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, + struct fuse_conn_info *conn); /** * Go into the background diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index 0b7ee2b..db15083 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -14,13 +14,12 @@ * Low level API * * IMPORTANT: you should define FUSE_USE_VERSION before including this - * header. To use the newest API define it to 26 (recommended for any - * new application), to use the old API define it to 24 (default) or - * 25 + * header. To use the newest API define it to 30 (recommended for any + * new application). */ #ifndef FUSE_USE_VERSION -#define FUSE_USE_VERSION 24 +#define FUSE_USE_VERSION 30 #endif #include "fuse_common.h" @@ -1618,29 +1617,18 @@ int fuse_req_interrupted(fuse_req_t req); * ----------------------------------------------------------- */ /** - * Print FUSE library version to stdout. + * Print low-level version information 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()` + * Print available low-level options to stdout. This is not an + * exhaustive list, but includes only those options that may be of + * interest to an end-user of a file system. */ 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); - - -/** * Print available options for `fuse_parse_cmdline()`. */ void fuse_cmdline_help(void); @@ -1684,9 +1672,14 @@ int fuse_parse_cmdline(struct fuse_args *args, * Returns a session structure suitable for passing to * fuse_session_mount() and fuse_session_loop(). * - * 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. + * This function accepts most file-system independent mount options + * (like context, nodev, ro - see mount(8)), as well as the general + * fuse mount options listed in mount.fuse(8) (e.g. -o allow_root and + * -o default_permissions, but not ``-o use_ino``). Instead of `-o + * debug`, debugging may also enabled with `-d` or `--debug`. + * + * 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 @@ -1537,34 +1537,15 @@ static inline void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req, fuse_do_prepare_interrupt(req, d); } -int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf) +int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf, + struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (fs->op.getattr) { if (fs->debug) - fprintf(stderr, "getattr %s\n", path); - - return fs->op.getattr(path, buf); - } else { - return -ENOSYS; - } -} - -int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf, - struct fuse_file_info *fi) -{ - fuse_get_context()->private_data = fs->user_data; - if (fs->op.fgetattr) { - if (fs->debug) - fprintf(stderr, "fgetattr[%llu] %s\n", + fprintf(stderr, "getattr[%llu] %s\n", (unsigned long long) fi->fh, path); - - return fs->op.fgetattr(path, buf, fi); - } else if (path && fs->op.getattr) { - if (fs->debug) - fprintf(stderr, "getattr %s\n", path); - - return fs->op.getattr(path, buf); + return fs->op.getattr(path, buf, fi); } else { return -ENOSYS; } @@ -2023,67 +2004,50 @@ int fuse_fs_flock(struct fuse_fs *fs, const char *path, } } -int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid) +int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, + gid_t gid, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (fs->op.chown) { if (fs->debug) - fprintf(stderr, "chown %s %lu %lu\n", path, + fprintf(stderr, "chown[%llu] %s %lu %lu\n", + (unsigned long long) fi->fh, path, (unsigned long) uid, (unsigned long) gid); - return fs->op.chown(path, uid, gid); - } else { - return -ENOSYS; - } -} - -int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size) -{ - fuse_get_context()->private_data = fs->user_data; - if (fs->op.truncate) { - if (fs->debug) - fprintf(stderr, "truncate %s %llu\n", path, - (unsigned long long) size); - - return fs->op.truncate(path, size); + return fs->op.chown(path, uid, gid, fi); } else { return -ENOSYS; } } -int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, off_t size, +int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; - if (fs->op.ftruncate) { + if (fs->op.truncate) { if (fs->debug) - fprintf(stderr, "ftruncate[%llu] %llu\n", + fprintf(stderr, "truncate[%llu] %llu\n", (unsigned long long) fi->fh, (unsigned long long) size); - return fs->op.ftruncate(path, size, fi); - } else if (path && fs->op.truncate) { - if (fs->debug) - fprintf(stderr, "truncate %s %llu\n", path, - (unsigned long long) size); - - return fs->op.truncate(path, size); + return fs->op.truncate(path, size, fi); } else { return -ENOSYS; } } int fuse_fs_utimens(struct fuse_fs *fs, const char *path, - const struct timespec tv[2]) + const struct timespec tv[2], struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (fs->op.utimens) { if (fs->debug) - fprintf(stderr, "utimens %s %li.%09lu %li.%09lu\n", - path, tv[0].tv_sec, tv[0].tv_nsec, + fprintf(stderr, "utimens[%llu] %s %li.%09lu %li.%09lu\n", + (unsigned long long) fi->fh, path, + tv[0].tv_sec, tv[0].tv_nsec, tv[1].tv_sec, tv[1].tv_nsec); - return fs->op.utimens(path, tv); + return fs->op.utimens(path, tv, fi); } else { return -ENOSYS; } @@ -2318,7 +2282,7 @@ static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname, break; memset(&buf, 0, sizeof(buf)); - res = fuse_fs_getattr(f->fs, newpath, &buf); + res = fuse_fs_getattr(f->fs, newpath, &buf, NULL); if (res == -ENOENT) break; free(newpath); @@ -2409,10 +2373,7 @@ static int lookup_path(struct fuse *f, fuse_ino_t nodeid, int res; memset(e, 0, sizeof(struct fuse_entry_param)); - if (fi) - res = fuse_fs_fgetattr(f->fs, path, &e->attr, fi); - else - res = fuse_fs_getattr(f->fs, path, &e->attr); + res = fuse_fs_getattr(f->fs, path, &e->attr, fi); if (res == 0) { res = do_lookup(f, nodeid, name, e); if (res == 0 && f->conf.debug) { @@ -2652,17 +2613,14 @@ static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino, memset(&buf, 0, sizeof(buf)); - if (fi != NULL && f->fs->op.fgetattr) + if (fi != NULL) err = get_path_nullok(f, ino, &path); else err = get_path(f, ino, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); - if (fi) - err = fuse_fs_fgetattr(f->fs, path, &buf, fi); - else - err = fuse_fs_getattr(f->fs, path, &buf); + err = fuse_fs_getattr(f->fs, path, &buf, fi); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } @@ -2682,11 +2640,12 @@ static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino, reply_err(req, err); } -int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode) +int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode, + struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (fs->op.chmod) - return fs->op.chmod(path, mode); + return fs->op.chmod(path, mode, fi); else return -ENOSYS; } @@ -2700,8 +2659,7 @@ static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int err; memset(&buf, 0, sizeof(buf)); - if (valid == FUSE_SET_ATTR_SIZE && fi != NULL && - f->fs->op.ftruncate && f->fs->op.fgetattr) + if (fi != NULL) err = get_path_nullok(f, ino, &path); else err = get_path(f, ino, &path); @@ -2710,21 +2668,17 @@ static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, fuse_prepare_interrupt(f, req, &d); err = 0; if (!err && (valid & FUSE_SET_ATTR_MODE)) - err = fuse_fs_chmod(f->fs, path, attr->st_mode); + err = fuse_fs_chmod(f->fs, path, attr->st_mode, fi); if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) { uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t) -1; gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t) -1; - err = fuse_fs_chown(f->fs, path, uid, gid); + err = fuse_fs_chown(f->fs, path, uid, gid, fi); } if (!err && (valid & FUSE_SET_ATTR_SIZE)) { - if (fi) - err = fuse_fs_ftruncate(f->fs, path, - attr->st_size, fi); - else - err = fuse_fs_truncate(f->fs, path, - attr->st_size); + err = fuse_fs_truncate(f->fs, path, + attr->st_size, fi); } #ifdef HAVE_UTIMENSAT if (!err && @@ -2746,7 +2700,7 @@ static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, else if (valid & FUSE_SET_ATTR_MTIME) tv[1] = attr->st_mtim; - err = fuse_fs_utimens(f->fs, path, tv); + err = fuse_fs_utimens(f->fs, path, tv, fi); } else #endif if (!err && @@ -2757,13 +2711,10 @@ static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, tv[0].tv_nsec = ST_ATIM_NSEC(attr); tv[1].tv_sec = attr->st_mtime; tv[1].tv_nsec = ST_MTIM_NSEC(attr); - err = fuse_fs_utimens(f->fs, path, tv); + err = fuse_fs_utimens(f->fs, path, tv, fi); } if (!err) { - if (fi) - err = fuse_fs_fgetattr(f->fs, path, &buf, fi); - else - err = fuse_fs_getattr(f->fs, path, &buf); + err = fuse_fs_getattr(f->fs, path, &buf, fi); } fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); @@ -3116,7 +3067,7 @@ static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path, struct stat stbuf; int err; pthread_mutex_unlock(&f->lock); - err = fuse_fs_fgetattr(f->fs, path, &stbuf, fi); + err = fuse_fs_getattr(f->fs, path, &stbuf, fi); pthread_mutex_lock(&f->lock); if (!err) update_stat(node, &stbuf); @@ -4455,7 +4406,6 @@ static const struct fuse_opt fuse_lib_opts[] = { FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0), FUSE_LIB_OPT("noforget", remember, -1), FUSE_LIB_OPT("remember=%u", remember, 0), - FUSE_LIB_OPT("nopath", nopath, 1), FUSE_LIB_OPT("intr", intr, 1), FUSE_LIB_OPT("intr_signal=%d", intr_signal, 0), FUSE_LIB_OPT("modules=%s", modules, 0), @@ -4464,12 +4414,9 @@ static const struct fuse_opt fuse_lib_opts[] = { static void fuse_lib_help(void) { + /* These are not all options, but only the ones that + may be of interest to an end-user */ 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" -" -o direct_io use direct I/O\n" " -o kernel_cache cache files in kernel\n" " -o [no]auto_cache enable caching based on modification times (off)\n" " -o umask=M set file permissions (octal)\n" @@ -4481,11 +4428,7 @@ static void fuse_lib_help(void) " -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n" " -o noforget never forget cached inodes\n" " -o remember=T remember cached inodes for T seconds (0s)\n" -" -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"); } static void fuse_lib_help_modules(void) @@ -4662,7 +4605,6 @@ struct fuse *fuse_new(struct fuse_args *args, if (f->conf.show_help) { fuse_lib_help(); fuse_lowlevel_help(); - fuse_mount_help(); /* Defer printing module help until modules have been loaded */ } diff --git a/lib/fuse_i.h b/lib/fuse_i.h index 5ed23c7..50962e3 100644 --- a/lib/fuse_i.h +++ b/lib/fuse_i.h @@ -41,34 +41,11 @@ struct fuse_notify_req { struct fuse_notify_req *prev; }; -struct session_opts { - int atomic_o_trunc; - int no_remote_posix_lock; - int no_remote_flock; - int splice_write; - int splice_move; - int splice_read; - int no_splice_write; - int no_splice_move; - int no_splice_read; - int auto_inval_data; - int no_auto_inval_data; - int no_readdirplus; - int no_readdirplus_auto; - int async_dio; - int no_async_dio; - int writeback_cache; - int no_writeback_cache; - 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; @@ -130,9 +107,7 @@ 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); void fuse_mount_version(void); - void fuse_kern_unmount(const char *mountpoint, int fd); int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo); diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index 33e0ae5..1dbe6ed 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -1809,39 +1809,6 @@ 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 session_opts *opts, - struct fuse_conn_info *conn) -{ -#define LL_ENABLE(cond,cap) \ - if (cond) conn->want |= (cap) -#define LL_DISABLE(cond,cap) \ - if (cond) conn->want &= ~(cap) - - LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ); - LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ); - - LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE); - LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE); - - LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE); - LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE); - - LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); - LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); - - LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS); - LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO); - - LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO); - LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO); - - LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE); - LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE); - - LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ); - LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ); -} - static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_init_in *arg = (struct fuse_init_in *) inarg; @@ -1946,19 +1913,9 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) f->conn.max_write = bufsize; f->got_init = 1; - - /* Apply command-line options (so that init() handler has - an idea about user preferences */ - apply_want_options(&f->opts, &f->conn); - - /* Allow file-system to overwrite defaults */ if (f->op.init) f->op.init(f->userdata, &f->conn); - /* Now explicitly overwrite file-system's decision - with command-line options */ - apply_want_options(&f->opts, &f->conn); - /* Always enable big writes, this is superseded by the max_write option */ outarg.flags |= FUSE_BIG_WRITES; @@ -2566,36 +2523,8 @@ clear_pipe: static const struct fuse_opt fuse_ll_opts[] = { LL_OPTION("debug", debug, 1), LL_OPTION("-d", debug, 1), + LL_OPTION("--debug", debug, 1), LL_OPTION("allow_root", allow_root, 1), - LL_OPTION("max_write=%u", conn.max_write, 0), - 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", 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), FUSE_OPT_END }; @@ -2603,30 +2532,17 @@ void fuse_lowlevel_version(void) { printf("using FUSE kernel interface version %i.%i\n", FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); + fuse_mount_version(); } void fuse_lowlevel_help(void) { + /* These are not all options, but the ones that are + potentially of interest to an end-user */ 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" -" -o congestion_threshold=N set kernel's congestion threshold\n" -" -o async_read perform reads asynchronously (default)\n" -" -o sync_read perform reads synchronously\n" -" -o atomic_o_trunc enable atomic open+truncate support\n" -" -o no_remote_lock disable remote file locking\n" -" -o no_remote_flock disable remote file locking (BSD)\n" -" -o no_remote_posix_lock disable remote file locking (POSIX)\n" -" -o [no_]splice_write use splice to write to the fuse device\n" -" -o [no_]splice_move move data while splicing to the fuse device\n" -" -o [no_]splice_read use splice to read from the fuse device\n" -" -o [no_]auto_inval_data use automatic kernel cache invalidation logic\n" -" -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\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"); } void fuse_session_destroy(struct fuse_session *se) diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript index 3301b12..1edaf5e 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -126,9 +126,9 @@ FUSE_3.0 { fuse_fs_fallocate; fuse_lowlevel_help; fuse_lowlevel_version; - fuse_mount_help; fuse_cmdline_help; - fuse_mount_version; + fuse_apply_conn_info_opts; + fuse_parse_conn_info_opts; local: *; diff --git a/lib/helper.c b/lib/helper.c index 5663fa9..a76c19d 100644 --- a/lib/helper.c +++ b/lib/helper.c @@ -49,16 +49,78 @@ static const struct fuse_opt fuse_helper_opts[] = { FUSE_OPT_END }; +struct fuse_conn_info_opts { + int atomic_o_trunc; + int no_remote_posix_lock; + int no_remote_flock; + int splice_write; + int splice_move; + int splice_read; + int no_splice_write; + int no_splice_move; + int no_splice_read; + int auto_inval_data; + int no_auto_inval_data; + int no_readdirplus; + int no_readdirplus_auto; + int async_dio; + int no_async_dio; + int writeback_cache; + int no_writeback_cache; + int async_read; + int sync_read; + unsigned max_write; + unsigned max_readahead; + unsigned max_background; + unsigned congestion_threshold; + unsigned time_gran; +}; + +#define CONN_OPTION(t, p, v) \ + { t, offsetof(struct fuse_conn_info_opts, p), v } +static const struct fuse_opt conn_info_opt_spec[] = { + CONN_OPTION("max_write=%u", max_write, 0), + CONN_OPTION("max_readahead=%u", max_readahead, 0), + CONN_OPTION("max_background=%u", max_background, 0), + CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0), + CONN_OPTION("sync_read", sync_read, 1), + CONN_OPTION("async_read", async_read, 1), + CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1), + CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1), + CONN_OPTION("no_remote_lock", no_remote_flock, 1), + CONN_OPTION("no_remote_flock", no_remote_flock, 1), + CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1), + CONN_OPTION("splice_write", splice_write, 1), + CONN_OPTION("no_splice_write", no_splice_write, 1), + CONN_OPTION("splice_move", splice_move, 1), + CONN_OPTION("no_splice_move", no_splice_move, 1), + CONN_OPTION("splice_read", splice_read, 1), + CONN_OPTION("no_splice_read", no_splice_read, 1), + CONN_OPTION("auto_inval_data", auto_inval_data, 1), + CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1), + CONN_OPTION("readdirplus=no", no_readdirplus, 1), + CONN_OPTION("readdirplus=yes", no_readdirplus, 0), + CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1), + CONN_OPTION("readdirplus=auto", no_readdirplus, 0), + CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0), + CONN_OPTION("async_dio", async_dio, 1), + CONN_OPTION("no_async_dio", no_async_dio, 1), + CONN_OPTION("writeback_cache", writeback_cache, 1), + CONN_OPTION("no_writeback_cache", no_writeback_cache, 1), + CONN_OPTION("time_gran=%u", time_gran, 0), + FUSE_OPT_END +}; + + void fuse_cmdline_help(void) { - printf("General options:\n" - " -h --help print help\n" + printf(" -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" " -o clone_fd use separate fuse device fd for each thread\n" - "\n"); + " (may improve performance)\n"); } static int fuse_helper_opt_proc(void *data, const char *arg, int key, @@ -196,7 +258,6 @@ int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, if (opts.show_version) { printf("FUSE library version %s\n", PACKAGE_VERSION); fuse_lowlevel_version(); - fuse_mount_version(); res = 0; goto out1; } @@ -207,6 +268,7 @@ int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, if(args.argv[0] != '\0') printf("usage: %s [options] <mountpoint>\n\n", args.argv[0]); + printf("FUSE options:\n"); fuse_cmdline_help(); if (fuse_opt_add_arg(&args, "--help") == -1) { res = 1; @@ -262,3 +324,67 @@ out1: fuse_opt_free_args(&args); return res; } + + +void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, + struct fuse_conn_info *conn) +{ + if(opts->max_write) + conn->max_write = opts->max_write; + if(opts->max_background) + conn->max_background = opts->max_background; + if(opts->congestion_threshold) + conn->congestion_threshold = opts->congestion_threshold; + if(opts->time_gran) + conn->time_gran = opts->time_gran; + if(opts->max_readahead) + conn->max_readahead = opts->max_readahead; + +#define LL_ENABLE(cond,cap) \ + if (cond) conn->want |= (cap) +#define LL_DISABLE(cond,cap) \ + if (cond) conn->want &= ~(cap) + + LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ); + LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ); + + LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE); + LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE); + + LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE); + LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE); + + LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); + LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); + + LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS); + LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO); + + LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO); + LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO); + + LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE); + LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE); + + LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ); + LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ); + + LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS); + LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS); +} + +struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args) +{ + struct fuse_conn_info_opts *opts; + + opts = calloc(1, sizeof(struct fuse_conn_info_opts)); + if(opts == NULL) { + fprintf(stderr, "calloc failed\n"); + return NULL; + } + if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) { + free(opts); + return NULL; + } + return opts; +} diff --git a/lib/modules/iconv.c b/lib/modules/iconv.c index 174e2b9..41a072c 100644 --- a/lib/modules/iconv.c +++ b/lib/modules/iconv.c @@ -101,26 +101,14 @@ err: return err; } -static int iconv_getattr(const char *path, struct stat *stbuf) +static int iconv_getattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { - err = fuse_fs_getattr(ic->next, newpath, stbuf); - free(newpath); - } - return err; -} - -static int iconv_fgetattr(const char *path, struct stat *stbuf, - struct fuse_file_info *fi) -{ - struct iconv *ic = iconv_get(); - char *newpath; - int err = iconv_convpath(ic, path, &newpath, 0); - if (!err) { - err = fuse_fs_fgetattr(ic->next, newpath, stbuf, fi); + err = fuse_fs_getattr(ic->next, newpath, stbuf, fi); free(newpath); } return err; @@ -315,62 +303,53 @@ static int iconv_link(const char *from, const char *to) return err; } -static int iconv_chmod(const char *path, mode_t mode) -{ - struct iconv *ic = iconv_get(); - char *newpath; - int err = iconv_convpath(ic, path, &newpath, 0); - if (!err) { - err = fuse_fs_chmod(ic->next, newpath, mode); - free(newpath); - } - return err; -} - -static int iconv_chown(const char *path, uid_t uid, gid_t gid) +static int iconv_chmod(const char *path, mode_t mode, + struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { - err = fuse_fs_chown(ic->next, newpath, uid, gid); + err = fuse_fs_chmod(ic->next, newpath, mode, fi); free(newpath); } return err; } -static int iconv_truncate(const char *path, off_t size) +static int iconv_chown(const char *path, uid_t uid, gid_t gid, + struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { - err = fuse_fs_truncate(ic->next, newpath, size); + err = fuse_fs_chown(ic->next, newpath, uid, gid, fi); free(newpath); } return err; } -static int iconv_ftruncate(const char *path, off_t size, +static int iconv_truncate(const char *path, off_t size, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { - err = fuse_fs_ftruncate(ic->next, newpath, size, fi); + err = fuse_fs_truncate(ic->next, newpath, size, fi); free(newpath); } return err; } -static int iconv_utimens(const char *path, const struct timespec ts[2]) +static int iconv_utimens(const char *path, const struct timespec ts[2], + struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { - err = fuse_fs_utimens(ic->next, newpath, ts); + err = fuse_fs_utimens(ic->next, newpath, ts, fi); free(newpath); } return err; @@ -600,7 +579,6 @@ static const struct fuse_operations iconv_oper = { .destroy = iconv_destroy, .init = iconv_init, .getattr = iconv_getattr, - .fgetattr = iconv_fgetattr, .access = iconv_access, .readlink = iconv_readlink, .opendir = iconv_opendir, @@ -616,7 +594,6 @@ static const struct fuse_operations iconv_oper = { .chmod = iconv_chmod, .chown = iconv_chown, .truncate = iconv_truncate, - .ftruncate = iconv_ftruncate, .utimens = iconv_utimens, .create = iconv_create, .open = iconv_open_file, diff --git a/lib/modules/subdir.c b/lib/modules/subdir.c index a039b3c..c264fb4 100644 --- a/lib/modules/subdir.c +++ b/lib/modules/subdir.c @@ -52,26 +52,14 @@ static int subdir_addpath(struct subdir *d, const char *path, char **newpathp) return 0; } -static int subdir_getattr(const char *path, struct stat *stbuf) +static int subdir_getattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { - err = fuse_fs_getattr(d->next, newpath, stbuf); - free(newpath); - } - return err; -} - -static int subdir_fgetattr(const char *path, struct stat *stbuf, - struct fuse_file_info *fi) -{ - struct subdir *d = subdir_get(); - char *newpath; - int err = subdir_addpath(d, path, &newpath); - if (!err) { - err = fuse_fs_fgetattr(d->next, newpath, stbuf, fi); + err = fuse_fs_getattr(d->next, newpath, stbuf, fi); free(newpath); } return err; @@ -301,62 +289,53 @@ static int subdir_link(const char *from, const char *to) return err; } -static int subdir_chmod(const char *path, mode_t mode) -{ - struct subdir *d = subdir_get(); - char *newpath; - int err = subdir_addpath(d, path, &newpath); - if (!err) { - err = fuse_fs_chmod(d->next, newpath, mode); - free(newpath); - } - return err; -} - -static int subdir_chown(const char *path, uid_t uid, gid_t gid) +static int subdir_chmod(const char *path, mode_t mode, + struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { - err = fuse_fs_chown(d->next, newpath, uid, gid); + err = fuse_fs_chmod(d->next, newpath, mode, fi); free(newpath); } return err; } -static int subdir_truncate(const char *path, off_t size) +static int subdir_chown(const char *path, uid_t uid, gid_t gid, + struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { - err = fuse_fs_truncate(d->next, newpath, size); + err = fuse_fs_chown(d->next, newpath, uid, gid, fi); free(newpath); } return err; } -static int subdir_ftruncate(const char *path, off_t size, - struct fuse_file_info *fi) +static int subdir_truncate(const char *path, off_t size, + struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { - err = fuse_fs_ftruncate(d->next, newpath, size, fi); + err = fuse_fs_truncate(d->next, newpath, size, fi); free(newpath); } return err; } -static int subdir_utimens(const char *path, const struct timespec ts[2]) +static int subdir_utimens(const char *path, const struct timespec ts[2], + struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { - err = fuse_fs_utimens(d->next, newpath, ts); + err = fuse_fs_utimens(d->next, newpath, ts, fi); free(newpath); } return err; @@ -582,7 +561,6 @@ static const struct fuse_operations subdir_oper = { .destroy = subdir_destroy, .init = subdir_init, .getattr = subdir_getattr, - .fgetattr = subdir_fgetattr, .access = subdir_access, .readlink = subdir_readlink, .opendir = subdir_opendir, @@ -598,7 +576,6 @@ static const struct fuse_operations subdir_oper = { .chmod = subdir_chmod, .chown = subdir_chown, .truncate = subdir_truncate, - .ftruncate = subdir_ftruncate, .utimens = subdir_utimens, .create = subdir_create, .open = subdir_open, diff --git a/lib/mount.c b/lib/mount.c index 386260c..051b424 100644 --- a/lib/mount.c +++ b/lib/mount.c @@ -65,7 +65,6 @@ struct mount_opts { int allow_other; int allow_root; int flags; - int nonempty; int auto_unmount; int blkdev; char *fsname; @@ -81,19 +80,16 @@ struct mount_opts { static const struct fuse_opt fuse_mount_opts[] = { FUSE_MOUNT_OPT("allow_other", allow_other), FUSE_MOUNT_OPT("allow_root", allow_root), - FUSE_MOUNT_OPT("nonempty", nonempty), FUSE_MOUNT_OPT("blkdev", blkdev), FUSE_MOUNT_OPT("auto_unmount", auto_unmount), FUSE_MOUNT_OPT("fsname=%s", fsname), FUSE_MOUNT_OPT("subtype=%s", subtype), FUSE_OPT_KEY("allow_other", KEY_KERN_OPT), FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT), - FUSE_OPT_KEY("nonempty", KEY_FUSERMOUNT_OPT), FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT), FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT), FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT), FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT), - FUSE_OPT_KEY("large_read", KEY_KERN_OPT), FUSE_OPT_KEY("blksize=", KEY_KERN_OPT), FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT), FUSE_OPT_KEY("context=", KEY_KERN_OPT), @@ -119,21 +115,6 @@ static const struct fuse_opt fuse_mount_opts[] = { FUSE_OPT_END }; -void fuse_mount_help(void) -{ - printf( -"FUSE-specific 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" -" -o nonempty allow mounts over non-empty file/dir\n" -" -o default_permissions enable permission checking by kernel\n" -" -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"); -} - static void exec_fusermount(const char *argv[]) { execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv); @@ -423,13 +404,6 @@ static int fuse_mount_sys(const char *mnt, struct mount_opts *mo, return -1; } - if (!mo->nonempty) { - res = fuse_mnt_check_empty("fuse", mnt, stbuf.st_mode, - stbuf.st_size); - if (res == -1) - return -1; - } - if (mo->auto_unmount) { /* Tell the caller to fallback to fusermount because auto-unmount does not work otherwise. */ diff --git a/lib/mount_bsd.c b/lib/mount_bsd.c index bc3ed89..9cf9dfa 100644 --- a/lib/mount_bsd.c +++ b/lib/mount_bsd.c @@ -90,20 +90,9 @@ static const struct fuse_opt fuse_mount_opts[] = { * handle them */ FUSE_OPT_KEY("fsname=", KEY_KERN), - FUSE_OPT_KEY("nonempty", KEY_KERN), - FUSE_OPT_KEY("large_read", KEY_KERN), FUSE_OPT_END }; -void fuse_mount_help(void) -{ - printf( -"FUSE-specific mount options:\n" -" -o allow_root allow access to root\n"); - system(FUSERMOUNT_PROG " --help"); - fputc('\n', stderr); -} - void fuse_mount_version(void) { system(FUSERMOUNT_PROG " --version"); diff --git a/lib/mount_util.c b/lib/mount_util.c index a23ab0b..8b64ca2 100644 --- a/lib/mount_util.c +++ b/lib/mount_util.c @@ -333,39 +333,6 @@ char *fuse_mnt_resolve_path(const char *progname, const char *orig) return dst; } -int fuse_mnt_check_empty(const char *progname, const char *mnt, - mode_t rootmode, off_t rootsize) -{ - int isempty = 1; - - if (S_ISDIR(rootmode)) { - struct dirent *ent; - DIR *dp = opendir(mnt); - if (dp == NULL) { - fprintf(stderr, - "%s: failed to open mountpoint for reading: %s\n", - progname, strerror(errno)); - return -1; - } - while ((ent = readdir(dp)) != NULL) { - if (strcmp(ent->d_name, ".") != 0 && - strcmp(ent->d_name, "..") != 0) { - isempty = 0; - break; - } - } - closedir(dp); - } else if (rootsize) - isempty = 0; - - if (!isempty) { - fprintf(stderr, "%s: mountpoint is not empty\n", progname); - fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname); - return -1; - } - return 0; -} - int fuse_mnt_check_fuseblk(void) { char buf[256]; diff --git a/lib/mount_util.h b/lib/mount_util.h index dc5c916..55c6c5e 100644 --- a/lib/mount_util.h +++ b/lib/mount_util.h @@ -14,6 +14,4 @@ int fuse_mnt_remove_mount(const char *progname, const char *mnt); int fuse_mnt_umount(const char *progname, const char *abs_mnt, const char *rel_mnt, int lazy); char *fuse_mnt_resolve_path(const char *progname, const char *orig); -int fuse_mnt_check_empty(const char *progname, const char *mnt, - mode_t rootmode, off_t rootsize); int fuse_mnt_check_fuseblk(void); diff --git a/test/test_examples.py b/test/test_examples.py index a8064c3..8868a98 100755 --- a/test/test_examples.py +++ b/test/test_examples.py @@ -29,11 +29,8 @@ def name_generator(__ctr=[0]): __ctr[0] += 1 return 'testfile_%d' % __ctr[0] -LL_OPTIONS = [ ['-o', 'splice_move,splice_write,splice_read' ], - ['-o', 'clone_fd,writeback_cache' ] ] - @pytest.mark.parametrize("name", ('hello', 'hello_ll')) -@pytest.mark.parametrize("options", LL_OPTIONS) +@pytest.mark.parametrize("options", ([], ['-o', 'clone_fd'])) def test_hello(tmpdir, name, options): mnt_dir = str(tmpdir) cmdline = base_cmdline + \ @@ -63,14 +60,13 @@ def test_hello(tmpdir, name, options): @pytest.mark.parametrize("name", ('passthrough', 'passthrough_fh', 'passthrough_ll')) -@pytest.mark.parametrize("options", LL_OPTIONS) -def test_passthrough(tmpdir, name, options): +def test_passthrough(tmpdir, name): mnt_dir = str(tmpdir.mkdir('mnt')) src_dir = str(tmpdir.mkdir('src')) cmdline = base_cmdline + \ [ pjoin(basename, 'example', name), - '-f', mnt_dir ] + options + '-f', mnt_dir ] if not name.endswith('_ll'): cmdline += [ '-o', 'use_ino,readdir_ino,kernel_cache' ] mount_process = subprocess.Popen(cmdline) @@ -146,13 +142,12 @@ def test_poll(tmpdir): @pytest.mark.parametrize("name", ('notify_inval_inode', 'notify_store_retrieve')) -@pytest.mark.parametrize("options", LL_OPTIONS) @pytest.mark.parametrize("notify", (True, False)) -def test_notify1(tmpdir, name, options, notify): +def test_notify1(tmpdir, name, notify): mnt_dir = str(tmpdir) cmdline = base_cmdline + \ [ pjoin(basename, 'example', name), - '-f', '--update-interval=1', mnt_dir ] + options + '-f', '--update-interval=1', mnt_dir ] if not notify: cmdline.append('--no-notify') mount_process = subprocess.Popen(cmdline) diff --git a/test/test_write_cache.c b/test/test_write_cache.c index d2f7004..137cb8d 100644 --- a/test/test_write_cache.c +++ b/test/test_write_cache.c @@ -39,11 +39,20 @@ struct options { static const struct fuse_opt option_spec[] = { OPTION("writeback_cache", writeback), OPTION("--data-size=%d", data_size), - FUSE_OPT_KEY("writeback_cache", FUSE_OPT_KEY_KEEP), FUSE_OPT_END }; static int got_write; +static void tfs_init (void *userdata, struct fuse_conn_info *conn) +{ + (void) userdata; + + if(options.writeback) { + assert(conn->capable & FUSE_CAP_WRITEBACK_CACHE); + conn->want |= FUSE_CAP_WRITEBACK_CACHE; + } +} + static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) { stbuf->st_ino = ino; if (ino == FUSE_ROOT_ID) { @@ -126,6 +135,7 @@ static void tfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf, } static struct fuse_lowlevel_ops tfs_oper = { + .init = tfs_init, .lookup = tfs_lookup, .getattr = tfs_getattr, .open = tfs_open, diff --git a/util/fusermount.c b/util/fusermount.c index b226fbd..8ba4ebf 100644 --- a/util/fusermount.c +++ b/util/fusermount.c @@ -714,7 +714,7 @@ static int get_string_opt(const char *s, unsigned len, const char *opt, static int do_mount(const char *mnt, char **typep, mode_t rootmode, int fd, const char *opts, const char *dev, char **sourcep, - char **mnt_optsp, off_t rootsize) + char **mnt_optsp) { int res; int flags = MS_NOSUID | MS_NODEV; @@ -726,7 +726,6 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, char *subtype = NULL; char *source = NULL; char *type = NULL; - int check_empty = 1; int blkdev = 0; optbuf = (char *) malloc(strlen(opts) + 128); @@ -759,8 +758,6 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, goto err; } blkdev = 1; - } else if (opt_eq(s, len, "nonempty")) { - check_empty = 0; } else if (opt_eq(s, len, "auto_unmount")) { auto_unmount = 1; } else if (!begins_with(s, "fd=") && @@ -813,10 +810,6 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, sprintf(d, "fd=%i,rootmode=%o,user_id=%u,group_id=%u", fd, rootmode, getuid(), getgid()); - if (check_empty && - fuse_mnt_check_empty(progname, mnt, rootmode, rootsize) == -1) - goto err; - source = malloc((fsname ? strlen(fsname) : 0) + (subtype ? strlen(subtype) : 0) + strlen(dev) + 32); @@ -1082,8 +1075,7 @@ static int mount_fuse(const char *mnt, const char *opts) restore_privs(); if (res != -1) res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT, - fd, opts, dev, &source, &mnt_opts, - stbuf.st_size); + fd, opts, dev, &source, &mnt_opts); } else restore_privs(); @@ -1292,7 +1284,7 @@ int main(int argc, char *argv[]) return 0; /* Become a daemon and wait for the parent to exit or die. - ie For the control socket to get closed. + ie For the control socket to get closed. btw We don't want to use daemon() function here because it forks and messes with the file descriptors. */ setsid(); |