The API stays the same, the libfuse version comes from
inlined functions, which are defined fuse_lowlevel.h
and fuse.h. As these inlined functions are defined in the header
files they get added into the application, similar as if these
were preprocessor macros.
Macro vs inlined function is then just a style issue - I personally
prefer the latter.
fuse_session_new() -> static inlinei, in the application
_fuse_session_new -> inside of libfuse
fuse_new() -> static inline, in the application
_fuse_new() -> inside of libfuse
Note: Entirely untested is the fuse 30 api - we need a test
for it. And we do not have any ABI tests at all.
Signed-off-by: Bernd Schubert <bernd.schubert@fastmail.fm>
The fuse_main_real() method doesn't apply the max_threads parameter
parsed through the commandline arguments. This commit fixes the wiring
of max_threads argument.
If a program with API before 312 did not set
max_idle_threads the new default from
fuse_parse_cmdline_312() is applied, which sets
UINT_MAX (-1).
Later in compat fuse_session_loop_mt_32 the old
config v1 struct is converted and that conversion
prints a warning if the default unset value was used.
This could have also happened to programs using the current
API, which just apply values struct fuse_cmdline_opts,
without checking if the defaults are set.
This addresses: https://github.com/libfuse/libfuse/issues/724
HAVE_LIBC_VERSIONED_SYMBOLS configures the library if to use
versioned symbols and is set at meson configuration time.
External filesystems (the main target, actually)
include fuse headers and the preprocessor
then acts on HAVE_LIBC_VERSIONED_SYMBOLS. Problem was now that
'config.h' was not distributed with libfuse and so
HAVE_LIBC_VERSIONED_SYMBOLS was never defined with external
tools and the preprocessor did the wrong decision.
This commit also increases the the minimal meson version,
as this depends on meson feature only available in 0.50
<quote 'meson' >
WARNING: Project specifies a minimum meson_
version '>= 0.42' but uses features which were added
in newer versions:
* 0.50.0: {'install arg in configure_file'}
</quote>
Additionally the config file has been renamed to "fuse_config.h"
to avoid clashes - 'config.h' is not very specific.
For __APPLE__ and __ULIBC__, which are assumed to not support
versioned symbols, helper.c has a compat ABI symbol for
fuse_parse_cmdline(). However that ABI symbol was conflicting
with the API macro (which redirects to the right API function
for recompilations against current libfuse).
Additionally the parameter 'opts' had a typo and was called
'out_opts'.
There was a simple typo and sym1 didn't match the function name
with the older __asm__(".symver " sym1 "," sym2) way to define
ABI compatibility.
Witht the newer "__attribute__ ((symver (sym2)))" sym1 is not used
at all and in manual testing the issue didn't come up therefore.
On benchmarking metadata operations with a single threaded bonnie++
and "max_idle_threads" limited to 1, 'top' was showing suspicious
160% cpu usage.
Profiling the system with flame graphs showed that an astonishing
amount of CPU time was spent in thread creation and destruction.
After verifying the code it turned out that fuse_do_work() was
creating a new thread every time all existing idle threads
were already busy. And then just a few lines later after processing
the current request it noticed that it had created too many threads
and destructed the current thread. I.e. there was a thread
creation/destruction ping-pong.
Code is changed to only create new threads if the max number of
threads is not reached.
Furthermore, thread destruction is disabled, as creation/destruction
is expensive in general.
With this change cpu usage of passthrough_hp went from ~160% to
~80% (with different values of max_idle_threads). And bonnie
values got approximately faster by 90%. This is a with single
threaded bonnie++
bonnie++ -x 4 -q -s0 -d <path> -n 30:1:1:10 -r 0
Without this patch, using the default max_idle_threads=10 and just
a single bonnie++ the thread creation/destruction code path is not
triggered. Just one libfuse and one application thread is just
a corner case - the requirement for the issue was just
n-application-threads >= max_idle_threads.
Signed-off-by: Bernd Schubert <bschubert@ddn.com>
struct fuse_loop_config was passed as a plain struct, without any
version identifer. This had two implications
1) Any addition of new parameters required a FUSE_SYMVER for
fuse_session_loop_mt() and fuse_loop_mt() as otherwise a read
beyond end-of previous struct size might have happened.
2) Filesystems also might have been recompiled and the developer
might not have noticed the struct extensions and unexpected for
the developer (or people recomliling the code) uninitialized
parameters would have been passed.
Code is updated to have struct fuse_loop_config as an opaque/private
data type for file systems that want version 312
(FUSE_MAKE_VERSION(3, 12)). The deprecated fuse_loop_config_v1
is visible, but should not be used outside of internal
conversion functions
File systems that want version >= 32 < 312 get the previous
struct (through ifdefs) and the #define of fuse_loop_mt
and fuse_session_loop_mt ensures that these recompiled file
systems call into the previous API, which then converts
the struct. This is similar to existing compiled applications
when just libfuse updated, but binaries it is solved with
the FUSE_SYMVER ABI compact declarations.
Signed-off-by: Bernd Schubert <bschubert@ddn.com>
Introduce an API for custom log handler functions. This allows libfuse
applications to send messages to syslog(3) or other logging systems.
See include/fuse_log.h for details.
Convert libfuse from fprintf(stderr, ...) to log_fuse(level, ...). Most
messages are error messages with FUSE_LOG_ERR log level. There are also
some debug messages which now use the FUSE_LOG_DEBUG log level.
Note that lib/mount_util.c is used by both libfuse and fusermount3.
Since fusermount3 does not link against libfuse, we cannot call
fuse_log() from lib/mount_util.c. This file will continue to use
fprintf(stderr, ...) until someone figures out how to split it up.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
The unprivileged option allows to run the FUSE file system process
without privileges by dropping capabilities and preventing them from
being re-acquired via setuid / fscaps etc. To accomplish this,
mount.fuse sets up the `/dev/fuse` file descriptor and mount itself
and passes the file descriptor via the `/dev/fd/%u` mountpoint syntax
to the FUSE file system.
This adds support for a mode of operation in which a privileged parent
process opens `/dev/fuse` and takes care of mounting. The FUSE file
system daemon can then run as an unprivileged child that merely
processes requests on the FUSE file descriptor, which get passed using
the special `/dev/fd/%u` syntax for the mountpoint parameter.
The main benefit is that no privileged operations need to be performed
by the FUSE file system daemon itself directly or indirectly, so the
FUSE process can run with fully unprivileged and mechanisms like
securebits and no_new_privs can be used to prevent subprocesses from
re-acquiring privilege via setuid, fscaps, etc. This reduces risk in
case the FUSE file system gets exploited by malicious file system
data.
Below is an example that illustrates this. Note that I'm using shell
for presentation purposes, the expectation is that the parent process
will implement the equivalent of the `mount -i` and `capsh` commands.
```
\# example/hello can mount successfully with privilege
$ sudo sh -c "LD_LIBRARY_PATH=build/lib ./example/hello /mnt/tmp"
$ sudo cat /mnt/tmp/hello
Hello World!
$ sudo umount /mnt/tmp
\# example/hello fails to mount without privilege
$ sudo capsh --drop=all --secbits=0x2f -- -c 'LD_LIBRARY_PATH=build/lib ./example/hello -f /mnt/tmp'
fusermount3: mount failed: Operation not permitted
\# Passing FUSE file descriptor via /dev/fd/%u allows example/hello to work without privilege
$ sudo sh -c '
exec 17<>/dev/fuse
mount -i -o nodev,nosuid,noexec,fd=17,rootmode=40000,user_id=0,group_id=0 -t fuse hello /mnt/tmp
capsh --drop=all --secbits=0x2f -- -c "LD_LIBRARY_PATH=build/lib example/hello /dev/fd/17"
'
$ sudo cat /mnt/tmp/hello
Hello World!
$ sudo umount /mnt/tmp
```
The fuse_session_loop_mt() and fuse_loop_mt() symbols are only visible
when linking against the shared object. The code in lib/, however, is
compiled *into* the shared object and should thus use the internal
names of these functions.
Surprisingly enough, the code still worked before - but only when link
time optimization was disabled.
Unfortunately, we still can't compile with LTO because it seems that
enabling LTO somehow makes the tagged symbols vanish.
Without lto, we have:
$ nm lib/libfuse3.so | grep fuse_new
0000000000011070 T fuse_new_30
0000000000010a00 t fuse_new_31
0000000000011070 T fuse_new@FUSE_3.0
0000000000010a00 T fuse_new@@FUSE_3.1
and with LTO:
$ nm lib/libfuse3.so | grep fuse_new
0000000000019a70 T fuse_new_30
0000000000019270 t fuse_new_31
See also issue #198.
Both the BSD and Linux implementation actually accept mostly the same
FUSE-specific mount options. Up to now, the BSD help function appended
the output of ``mount_fusefs --help``, but looking at
http://www.unix.com/man-page/freebsd/8/mount_fusefs/ this is likely more
confusing than helpful (since the user is not actually invoking
mount_fusefs directly, most of the options don't make sense).
We now only list options that are potentially useful for an
end-user (and unlikely to accidentally break a file system). The full
list of FUSE options has been moved to the documentation of the
fuse_new() and fuse_session_new() functions.
Instead of using command line options to modify struct fuse_conn_info
before and after calling the init() handler, we now give the file system
explicit control over this.
There's now a way to inhibit the "usage" line (which actually got lost
in commit 225c12aebf), which makes it easier for simply file-systems
to generate good-looking --help output.
Help and version messages can be generated using the new
fuse_lowlevel_help(), fuse_lowlevel_version(), fuse_mount_help(), and
fuse_mount_version() functions.
The fuse_parse_cmdline() function has been made more powerful
to do this automatically, and is now explicitly intended only
for low-level API users.
This is a code simplication patch. We don't have to parse for --help and
--version in quite as many places, and we no longer have a low-level
initialization function be responsible for the (super-high level) task
of printing a program usage message.
In the high-level API, we can now handle the command line parsing
earlier and avoid running other initialization code if we're just going
to abort later on.
The only struct fuse_chan that's accessible to the user application is
the "master" channel that is returned by fuse_mount and stored in struct
fuse_session.
When using the multi-threaded main loop with the "clone_fd" option, each
worker thread gets its own struct fuse_chan. However, none of these are
available to the user application, nor do they hold references to struct
fuse_session (the pointer is always null).
Therefore, any presence of struct fuse_chan can be removed
without loss of functionality by relying on struct fuse_session instead.
This reduces the number of API functions and removes a potential source
of confusion (since the new API no longer looks as if it might be
possible to add multiple channels to one session, or to share one
channel between multiple sessions).
Fixes issue #17.
Mounting a FUSE file system remotely using SSH in combination with
pseudo-terminal allocation (-t), results in "Transport endpoint is
not connected" errors when trying to access the file system contents.
For example:
# ssh -t root@localhost "cmsfs-fuse /dev/disk/by-path/ccw-0.0.0190 /CMSFS"
Connection to localhost closed.
# ls /CMSFS
ls: cannot access '/CMSFS': Transport endpoint is not connected
The cmsfs-fuse main program (which can also be any other FUSE file
system) calls into the fuse_main() libfuse library function.
The fuse_main() function later calls fuse_daemonize() to fork the
daemon process to handle the FUSE file system I/O.
The fuse_daemonize() function calls fork() as usual. The child
proceeds with setsid() and then redirecting its file descriptors
to /dev/null etc. The parent process, simply exits.
The child's functions and the parent's exit creates a subtle race.
This is seen with an SSH connection. The SSH command above calls
cmsfs-fuse on an allocated pseudo-terminal device (-t option).
If the parent exits, SSH receives the command completion and closes
the connection, that means, it closes the master side of the
pseudo-terminal. This causes a HUP signal being sent to the process
group on the pseudo-terminal. At this point in time, the child might
not have completed the setsid() call and, hence, becomes terminated.
Note that fuse daemon sets up its signal handlers after fuse_daemonize()
has completed.
Even if the child has the chance to disassociate from its parent process
group to become it's own process group with setsid(), the child still
has the pseudo-terminal opened as stdin, stdout, and stderr. So the
pseudo-terminal still behave as controlling terminal and might cause a
SIGHUP at closing the the master side.
To solve the problem, the parent has to wait until the child (the fuse
daemon process) has completed its processing, that means, has become
its own process group with setsid() and closed any file descriptors
pointing to the pseudo-terminal.
Closes: #27
Reported-by: Ofer Baruch <oferba@il.ibm.com>
Reviewed-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
- fuse_kern_unmount closes handle (e.g. 19)
- a thread in my process opens a file - the OS assigns newly freed
handle (i.e. 19)
- fuse_kern_chan_destroy closes the same handle (i.e. 19)
- a thread in my process opens another file - the OS assigns newly
freed handle (i.e. 19)
- * MAYHEM *
Reported by Dan Greenfield