From: "Darrick J. Wong" <djwong@kernel.org>
To: bschubert@ddn.com
Cc: miklos@szeredi.hu, neal@gompa.dev, linux-fsdevel@vger.kernel.org,
bernd@bsbernd.com, joannelkoong@gmail.com
Subject: Re: [PATCH 09/13] mount.fuse3: integrate systemd service startup
Date: Fri, 17 Apr 2026 15:41:43 -0700 [thread overview]
Message-ID: <20260417224143.GG7727@frogsfrogsfrogs> (raw)
In-Reply-To: <177577270377.2064074.11749808216143618873.stgit@frogsfrogsfrogs>
On Thu, Apr 09, 2026 at 03:22:53PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
>
> Teach mount.fuse3 how to start fuse via systemd service, if present.
>
> Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
> ---
> util/mount_service.h | 8 +++
> doc/fuservicemount3.8 | 10 +++-
> meson.build | 3 +
> util/fuservicemount.c | 48 +++++++++++++++++
> util/meson.build | 14 ++++-
> util/mount.fuse.c | 135 +++++++++++++++++++++++++++++++++++++++++--------
> util/mount_service.c | 28 ++++++++++
> 7 files changed, 219 insertions(+), 27 deletions(-)
>
>
> diff --git a/util/mount_service.h b/util/mount_service.h
> index f1f95e67ee1afe..993414ea3422e9 100644
> --- a/util/mount_service.h
> +++ b/util/mount_service.h
> @@ -36,4 +36,12 @@ int mount_service_main(int argc, char *argv[]);
> */
> const char *mount_service_subtype(const char *fstype);
>
> +/**
> + * Discover if there is a fuse service socket for the given fuse subtype.
> + *
> + * @param subtype the subtype of a fuse filesystem type (e.g. Y from fuse.Y)
> + * @return true if available, false if not
> + */
> +bool mount_service_present(const char *subtype);
> +
> #endif /* MOUNT_SERVICE_H_ */
> diff --git a/doc/fuservicemount3.8 b/doc/fuservicemount3.8
> index e45d6a89c8b81a..aa2167cb4872c6 100644
> --- a/doc/fuservicemount3.8
> +++ b/doc/fuservicemount3.8
> @@ -7,12 +7,20 @@ .SH SYNOPSIS
> .B mountpoint
> .BI -t " fstype"
> [
> -.I options
> +.BI -o " options"
> ]
> +
> +.B fuservicemount3
> +.BI -t " fstype"
> +.B --check
> +
> .SH DESCRIPTION
> Mount a filesystem using a FUSE server that runs as a socket service.
> These servers can be contained using the platform's service management
> framework.
> +
> +The second form checks if there is a FUSE service available for the given
> +filesystem type.
> .SH "AUTHORS"
> .LP
> The author of the fuse socket service code is Darrick J. Wong <djwong@kernel.org>.
> diff --git a/meson.build b/meson.build
> index c8326b79fcee8f..827ec45ad3ad75 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -83,7 +83,8 @@ private_cfg.set('FUSE_SERVICE_SOCKET_PERMS', service_socket_perms)
> # Test for presence of some functions
> test_funcs = [ 'fork', 'fstatat', 'openat', 'readlinkat', 'pipe2',
> 'splice', 'vmsplice', 'posix_fallocate', 'fdatasync',
> - 'utimensat', 'copy_file_range', 'fallocate', 'fspacectl' ]
> + 'utimensat', 'copy_file_range', 'fallocate', 'fspacectl',
> + 'faccessat' ]
> foreach func : test_funcs
> private_cfg.set('HAVE_' + func.to_upper(),
> cc.has_function(func, prefix: include_default, args: args_default))
> diff --git a/util/fuservicemount.c b/util/fuservicemount.c
> index 9c694a4290f94e..d39d9c486c8997 100644
> --- a/util/fuservicemount.c
> +++ b/util/fuservicemount.c
> @@ -9,10 +9,58 @@
> * This program wraps the mounting of FUSE filesystems that run in systemd
> */
> #define _GNU_SOURCE
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <stdlib.h>
> #include "fuse_config.h"
> #include "mount_service.h"
>
> +static int check_service(const char *fstype)
> +{
> + const char *subtype;
> +
> + if (!fstype) {
> + fprintf(stderr,
> + "fuservicemount: expected fs type for --check\n");
> + return EXIT_FAILURE;
> + }
> +
> + subtype = mount_service_subtype(fstype);
> + return mount_service_present(subtype) ? EXIT_SUCCESS : EXIT_FAILURE;
> +}
> +
> int main(int argc, char *argv[])
> {
> + char *fstype = NULL;
> + bool check = false;
> + int i;
> +
> + /*
> + * If the user passes us exactly the args -t FSTYPE --check then
> + * we'll just check if there's a service for the FSTYPE fuse server.
> + */
> + for (i = 1; i < argc; i++) {
> + if (!strcmp(argv[i], "--check")) {
> + if (check) {
> + check = false;
> + break;
> + }
> + check = true;
> + } else if (!strcmp(argv[i], "-t") && i + 1 < argc) {
> + if (fstype) {
> + check = false;
> + break;
> + }
> + fstype = argv[i + 1];
> + i++;
> + } else {
> + check = false;
> + break;
> + }
> + }
> + if (check)
> + return check_service(fstype);
> +
> return mount_service_main(argc, argv);
> }
> diff --git a/util/meson.build b/util/meson.build
> index aa646ef3c77d16..85b54d5d322dcb 100644
> --- a/util/meson.build
> +++ b/util/meson.build
> @@ -6,21 +6,27 @@ executable('fusermount3', ['fusermount.c', '../lib/mount_util.c', '../lib/util.c
> install_dir: get_option('bindir'),
> c_args: '-DFUSE_CONF="@0@"'.format(fuseconf_path))
>
> +mount_service_sources = []
> +mount_service_cflags = []
> if private_cfg.get('HAVE_SERVICEMOUNT', false)
> - executable('fuservicemount3', ['mount_service.c', 'fuservicemount.c', '../lib/mount_util.c', 'fuser_conf.c'],
> + mount_service_sources += ['mount_service.c', '../lib/mount_util.c', 'fuser_conf.c']
> + mount_service_cflags += ['-DFUSE_CONF="@0@"'.format(fuseconf_path)]
> + fuservicemount_path = join_paths(get_option('prefix'), get_option('sbindir'))
> + mount_service_cflags += ['-DFUSERVICEMOUNT_DIR="@0@"'.format(fuservicemount_path)]
> + executable('fuservicemount3', ['fuservicemount.c'] + mount_service_sources,
> include_directories: include_dirs,
> link_with: [ libfuse ],
> install: true,
> install_dir: get_option('sbindir'),
> - c_args: ['-DFUSE_USE_VERSION=319', '-DFUSE_CONF="@0@"'.format(fuseconf_path)])
> + c_args: ['-DFUSE_USE_VERSION=319'] + mount_service_cflags)
> endif
>
> -executable('mount.fuse3', ['mount.fuse.c'],
> +executable('mount.fuse3', ['mount.fuse.c'] + mount_service_sources,
> include_directories: include_dirs,
> link_with: [ libfuse ],
> install: true,
> install_dir: get_option('sbindir'),
> - c_args: '-DFUSE_USE_VERSION=317')
> + c_args: ['-DFUSE_USE_VERSION=319'] + mount_service_cflags)
>
>
> udevrulesdir = get_option('udevrulesdir')
> diff --git a/util/mount.fuse.c b/util/mount.fuse.c
> index ec56198ccc1cf0..8836eba8502fd1 100644
> --- a/util/mount.fuse.c
> +++ b/util/mount.fuse.c
> @@ -49,6 +49,9 @@
> #endif
>
> #include "fuse.h"
> +#ifdef HAVE_SERVICEMOUNT
> +# include "mount_service.h"
> +#endif
>
> static char *progname;
>
> @@ -233,6 +236,77 @@ static void drop_and_lock_capabilities(void)
> }
> #endif
>
> +#ifdef HAVE_SERVICEMOUNT
> +#define FUSERVICEMOUNT_PROG "fuservicemount3"
> +
> +static int try_service_main(char *argv0, char *type, char *source,
> + const char *mountpoint, char *options)
> +{
> + const char *full_path = FUSERVICEMOUNT_DIR "/" FUSERVICEMOUNT_PROG;
> + char **argv;
> + char dash_o[] = "-o";
> + char dash_t[] = "-t";
> + char *mntpt = strdup(mountpoint);
> + int argc = 5; /* argv[0], -t type, mountpoint, and trailing NULL */
> + int i = 0;
> + int ret;
> +
> + if (!mount_service_present(type))
> + return MOUNT_SERVICE_FALLBACK_NEEDED;
> +
> + if (!mntpt) {
> + perror("mountpoint allocation");
> + return -1;
> + }
> +
> + if (source)
> + argc++;
> + if (options)
> + argc += 2;
> +
> + argv = calloc(argc, sizeof(char *));
> + if (!argv) {
> + perror("argv allocation");
> + free(mntpt);
> + return -1;
> + }
> +
> + argv[i++] = argv0;
> + if (source)
> + argv[i++] = source;
Codex noted that some fuse servers actually don't have a "source"
argument. If you invoke mount.fuse3 like so:
$ mount.fuse3 null# /tmp/mnt
Then the service activation fails because source is set to the null
terminator just pass the '#' in "null#", and then gets included in argv.
> + argv[i++] = mntpt;
> + argv[i++] = dash_t;
> + argv[i++] = type;
> + if (options) {
> + argv[i++] = dash_o;
> + argv[i++] = options;
> + }
> + argv[i] = 0;
> +
> + if (getuid() != 0) {
> + /*
> + * First try the install path, then a system install, just like
> + * we do for fusermount.
> + */
> + ret = execvp(full_path, argv);
No need for the $PATH enabled version of execv if we're providing an
absolute path.
> + if (ret)
> + ret = execvp(FUSERVICEMOUNT_PROG, argv);
We shouldn't execvp directly here, because it's possible that the helper
will fail to connect to the service, in which case we could fall back to
the weird sh invocation code below. Since posix_spawn() is already used
elsewhere, let's do that too.
--D
> + if (ret) {
> + fprintf(stderr, "%s: could not start %s helper: %s\n",
> + argv[0], FUSERVICEMOUNT_PROG,
> + strerror(errno));
> + ret = MOUNT_SERVICE_FALLBACK_NEEDED;
> + }
> + } else {
> + /* We're root, just do the mount directly. */
> + ret = mount_service_main(argc - 1, argv);
> + }
> + free(argv);
> + free(mntpt);
> + return ret;
> +}
> +#endif
> +
> int main(int argc, char *argv[])
> {
> char *type = NULL;
> @@ -280,9 +354,7 @@ int main(int argc, char *argv[])
> mountpoint = argv[2];
>
> for (i = 3; i < argc; i++) {
> - if (strcmp(argv[i], "-v") == 0) {
> - continue;
> - } else if (strcmp(argv[i], "-t") == 0) {
> + if (strcmp(argv[i], "-t") == 0) {
> i++;
>
> if (i == argc) {
> @@ -303,6 +375,30 @@ int main(int argc, char *argv[])
> progname);
> exit(1);
> }
> + }
> + }
> +
> + if (!type) {
> + if (source) {
> + dup_source = xstrdup(source);
> + type = dup_source;
> + source = strchr(type, '#');
> + if (source)
> + *source++ = '\0';
> + if (!type[0]) {
> + fprintf(stderr, "%s: empty filesystem type\n",
> + progname);
> + exit(1);
> + }
> + } else {
> + fprintf(stderr, "%s: empty source\n", progname);
> + exit(1);
> + }
> + }
> +
> + for (i = 3; i < argc; i++) {
> + if (strcmp(argv[i], "-v") == 0) {
> + continue;
> } else if (strcmp(argv[i], "-o") == 0) {
> char *opts;
> const char *opt;
> @@ -366,24 +462,6 @@ int main(int argc, char *argv[])
> if (suid)
> options = add_option("suid", options);
>
> - if (!type) {
> - if (source) {
> - dup_source = xstrdup(source);
> - type = dup_source;
> - source = strchr(type, '#');
> - if (source)
> - *source++ = '\0';
> - if (!type[0]) {
> - fprintf(stderr, "%s: empty filesystem type\n",
> - progname);
> - exit(1);
> - }
> - } else {
> - fprintf(stderr, "%s: empty source\n", progname);
> - exit(1);
> - }
> - }
> -
> if (setuid_name && setuid_name[0]) {
> #ifdef linux
> if (drop_privileges) {
> @@ -429,6 +507,21 @@ int main(int argc, char *argv[])
> drop_and_lock_capabilities();
> }
> #endif
> +
> +#ifdef HAVE_SERVICEMOUNT
> + /*
> + * Now that we know the desired filesystem type, see if we can find
> + * a socket service implementing that, if we haven't selected any weird
> + * options that would prevent that.
> + */
> + if (!pass_fuse_fd && !(setuid_name && setuid_name[0])) {
> + int ret = try_service_main(argv[0], type, source, mountpoint,
> + options);
> + if (ret != MOUNT_SERVICE_FALLBACK_NEEDED)
> + return ret;
> + }
> +#endif
> +
> add_arg(&command, type);
> if (source)
> add_arg(&command, source);
> diff --git a/util/mount_service.c b/util/mount_service.c
> index 0f7eae94ada377..f837595078f2d1 100644
> --- a/util/mount_service.c
> +++ b/util/mount_service.c
> @@ -1836,3 +1836,31 @@ int mount_service_main(int argc, char *argv[])
> mount_service_destroy(&mo);
> return ret;
> }
> +
> +bool mount_service_present(const char *fstype)
> +{
> + struct stat stbuf;
> + char path[PATH_MAX];
> + int ret;
> +
> + snprintf(path, sizeof(path), FUSE_SERVICE_SOCKET_DIR "/%s", fstype);
> + ret = stat(path, &stbuf);
> + if (ret)
> + return false;
> +
> + if (!S_ISSOCK(stbuf.st_mode))
> + return false;
> +
> +#ifdef HAVE_FACCESSAT
> + /*
> + * Can we write to the service socket with the real uid? This accounts
> + * for setuid and ACLs on the socket. Note that we connect() to the
> + * socket having dropped setuid privileges.
> + */
> + ret = faccessat(AT_FDCWD, path, W_OK, 0);
> +#else
> + /* Can we write to the service socket with the real uid? */
> + ret = access(path, W_OK);
> +#endif
> + return ret == 0;
> +}
>
>
next prev parent reply other threads:[~2026-04-17 22:41 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-09 22:20 [PATCHSET v4] libfuse: run fuse servers as a contained service Darrick J. Wong
2026-04-09 22:20 ` [PATCH 01/13] Refactor mount code / move common functions to mount_util.c Darrick J. Wong
2026-04-09 22:21 ` [PATCH 02/13] mount_service: add systemd/inetd socket service mounting helper Darrick J. Wong
2026-04-14 1:00 ` Darrick J. Wong
2026-04-14 23:48 ` Darrick J. Wong
2026-04-17 23:19 ` Darrick J. Wong
2026-04-09 22:21 ` [PATCH 03/13] mount_service: create high level fuse helpers Darrick J. Wong
2026-04-14 23:58 ` Darrick J. Wong
2026-04-09 22:21 ` [PATCH 04/13] mount_service: use the new mount api for the mount service Darrick J. Wong
2026-04-17 22:03 ` Darrick J. Wong
2026-04-09 22:21 ` [PATCH 05/13] mount_service: update mtab after a successful mount Darrick J. Wong
2026-04-09 22:22 ` [PATCH 06/13] util: hoist the fuse.conf parsing and setuid mode enforcement code Darrick J. Wong
2026-04-09 22:22 ` [PATCH 07/13] util: fix checkpatch complaints in fuser_conf.[ch] Darrick J. Wong
2026-04-09 22:22 ` [PATCH 08/13] mount_service: enable unprivileged users in the same manner as fusermount Darrick J. Wong
2026-04-14 23:53 ` Darrick J. Wong
2026-04-17 22:01 ` Darrick J. Wong
2026-04-09 22:22 ` [PATCH 09/13] mount.fuse3: integrate systemd service startup Darrick J. Wong
2026-04-17 22:41 ` Darrick J. Wong [this message]
2026-04-09 22:23 ` [PATCH 10/13] mount_service: allow installation as a setuid program Darrick J. Wong
2026-04-09 22:23 ` [PATCH 11/13] example/service_ll: create a sample systemd service fuse server Darrick J. Wong
2026-04-14 23:56 ` Darrick J. Wong
2026-04-17 21:56 ` Darrick J. Wong
2026-04-09 22:23 ` [PATCH 12/13] example/service: create a sample systemd service for a high-level " Darrick J. Wong
2026-04-09 22:23 ` [PATCH 13/13] nullfs: support fuse systemd service mode Darrick J. Wong
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260417224143.GG7727@frogsfrogsfrogs \
--to=djwong@kernel.org \
--cc=bernd@bsbernd.com \
--cc=bschubert@ddn.com \
--cc=joannelkoong@gmail.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=miklos@szeredi.hu \
--cc=neal@gompa.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox