From: "Darrick J. Wong" <djwong@kernel.org>
To: bernd@bsbernd.com
Cc: neal@gompa.dev, linux-fsdevel@vger.kernel.org,
joannelkoong@gmail.com, miklos@szeredi.hu,
fuse-devel@lists.linux.dev
Subject: Re: [PATCH 09/13] mount.fuse3: integrate systemd service startup
Date: Tue, 28 Apr 2026 11:10:03 -0700 [thread overview]
Message-ID: <20260428181003.GP7739@frogsfrogsfrogs> (raw)
In-Reply-To: <177689988702.3820166.5587510270979728964.stgit@frogsfrogsfrogs>
On Wed, Apr 22, 2026 at 04:21:42PM -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 | 9 +++
> doc/fuservicemount3.8 | 10 +++
> meson.build | 3 +
> util/fuservicemount.c | 47 ++++++++++++++
> util/meson.build | 14 +++-
> util/mount.fuse.c | 169 +++++++++++++++++++++++++++++++++++++++++++------
> util/mount_service.c | 43 ++++++++++++
> 7 files changed, 268 insertions(+), 27 deletions(-)
>
>
> diff --git a/util/mount_service.h b/util/mount_service.h
> index a0b952a15dacf3..ec8008a7f53942 100644
> --- a/util/mount_service.h
> +++ b/util/mount_service.h
> @@ -37,4 +37,13 @@ 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 filesystem type.
> + * The type must not contain a path separator.
> + *
> + * @param fstype the type of a fuse filesystem type (e.g. fuse.Y, fuseblk.Y, or Y)
> + * @return true if available, false if not
> + */
> +bool mount_service_present(const char *fstype);
> +
> #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..4d4cad6cb9253c 100644
> --- a/util/fuservicemount.c
> +++ b/util/fuservicemount.c
> @@ -9,10 +9,57 @@
> * 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)
> +{
> + if (!fstype) {
> + fprintf(stderr,
> + "fuservicemount: expected fs type for --check\n");
> + return EXIT_FAILURE;
> + }
> +
> + return mount_service_present(fstype) ? 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.
> + * This doesn't tell us if the listening socket is actually connected
> + * to anything.
> + */
> + 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..b982c057cff870 100644
> --- a/util/mount.fuse.c
> +++ b/util/mount.fuse.c
> @@ -6,6 +6,9 @@
> See the file GPL2.txt.
> */
>
> +/* For environ */
> +#define _GNU_SOURCE
> +
> #include "fuse_config.h"
>
> #include <stdio.h>
> @@ -17,6 +20,9 @@
> #include <fcntl.h>
> #include <pwd.h>
> #include <sys/wait.h>
> +#ifdef HAVE_SERVICEMOUNT
> +#include <spawn.h>
> +#endif
>
> #ifdef linux
> #include <sys/prctl.h>
> @@ -49,6 +55,9 @@
> #endif
>
> #include "fuse.h"
> +#ifdef HAVE_SERVICEMOUNT
> +# include "mount_service.h"
> +#endif
>
> static char *progname;
>
> @@ -233,6 +242,105 @@ static void drop_and_lock_capabilities(void)
> }
> #endif
>
> +#ifdef HAVE_SERVICEMOUNT
> +#define FUSERVICEMOUNT_PROG "fuservicemount3"
> +
> +static int mount_service_child(char **argv)
> +{
> + const char *full_path = FUSERVICEMOUNT_DIR "/" FUSERVICEMOUNT_PROG;
> + pid_t child_pid;
> + int child_status;
> + int ret;
> +
> + /*
> + * First try the install path, then a system install, just like we do
> + * for fusermount. See man 7 environ for the global environ pointer.
> + */
> + ret = posix_spawn(&child_pid, full_path, NULL, NULL,
> + (char *const *)argv, environ);
> + if (ret)
> + ret = posix_spawnp(&child_pid, FUSERVICEMOUNT_PROG, NULL, NULL,
> + (char * const *)argv, environ);
> + if (ret) {
> + fprintf(stderr, "%s: could not start %s helper: %s\n",
> + argv[0], FUSERVICEMOUNT_PROG, strerror(ret));
> + return MOUNT_SERVICE_FALLBACK_NEEDED;
> + }
> +
> + ret = waitpid(child_pid, &child_status, 0);
Heh. I don't pass WNOHANG here, which means that waitpid could return
EINTR due to a signal being sent to the mount.fuse process. It looks
like glibc wraps that particular case, but the manpage says that a
waitpid implementation can return EINTR so I'll change this to:
do {
ret = waitpid(...);
} while (ret < 0 && errno == EINTR);
--D
> + if (ret < 0) {
> + fprintf(stderr, "%s: could not wait for %s helper: %s\n",
> + argv[0], FUSERVICEMOUNT_PROG, strerror(errno));
> + return MOUNT_SERVICE_FALLBACK_NEEDED;
> + }
> +
> + if (WIFEXITED(child_status))
> + return WEXITSTATUS(child_status);
> +
> + /* terminated due to signal or coredump */
> + return EXIT_FAILURE;
> +}
> +
> +static int try_service_main(char *argv0, char *fstype, char *source,
> + const char *mountpoint, char *options)
> +{
> + char **argv;
> + char dash_o[] = "-o";
> + char dash_t[] = "-t";
> + char *mntpt;
> + int argc = 5; /* argv[0], -t type, mountpoint, and trailing NULL */
> + int i = 0;
> + int ret;
> +
> + if (!mount_service_present(fstype))
> + return MOUNT_SERVICE_FALLBACK_NEEDED;
> +
> + /* This can be an empty string if "mount.fuse3 null# /tmp/a" */
> + if (source && source[0] == 0)
> + source = NULL;
> +
> + mntpt = strdup(mountpoint);
> + 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;
> + argv[i++] = mntpt;
> + argv[i++] = dash_t;
> + argv[i++] = fstype;
> + if (options) {
> + argv[i++] = dash_o;
> + argv[i++] = options;
> + }
> + argv[i] = 0;
> +
> + if (getuid() != 0) {
> + ret = mount_service_child(argv);
> + } 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 +388,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 +409,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 +496,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 +541,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 95de56f2b625fe..bc5940bc900dad 100644
> --- a/util/mount_service.c
> +++ b/util/mount_service.c
> @@ -2066,3 +2066,46 @@ int mount_service_main(int argc, char *argv[])
> mount_service_destroy(&mo);
> return ret;
> }
> +
> +bool mount_service_present(const char *fstype)
> +{
> + struct sockaddr_un name;
> + struct stat stbuf;
> + char path[PATH_MAX];
> + const char *subtype;
> + ssize_t written;
> + int ret;
> +
> + subtype = mount_service_subtype(fstype);
> + if (!subtype)
> + return false;
> +
> + /*
> + * The full path to the socket must fit within the AF_UNIX socket path
> + * buffer, which is much shorter than PATH_MAX.
> + */
> + written = snprintf(path, sizeof(path), FUSE_SERVICE_SOCKET_DIR "/%s",
> + subtype);
> + if (written >= sizeof(name.sun_path) || written >= sizeof(path))
> + return false;
> +
> + 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-28 18:10 UTC|newest]
Thread overview: 55+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-22 23:15 [PATCHBOMB v5] fuse/libfuse/e2fsprogs/etc: containerize ext4 for safer operation Darrick J. Wong
2026-04-22 23:18 ` [PATCHSET v5] libfuse: run fuse servers as a contained service Darrick J. Wong
2026-04-22 23:19 ` [PATCH 01/13] Refactor mount code / move common functions to mount_util.c Darrick J. Wong
2026-04-22 23:19 ` [PATCH 02/13] mount_service: add systemd socket service mounting helper Darrick J. Wong
2026-04-28 18:08 ` Darrick J. Wong
2026-04-29 15:23 ` Darrick J. Wong
2026-04-22 23:20 ` [PATCH 03/13] mount_service: create high level fuse helpers Darrick J. Wong
2026-04-22 23:20 ` [PATCH 04/13] mount_service: use the new mount api for the mount service Darrick J. Wong
2026-04-22 23:20 ` [PATCH 05/13] mount_service: update mtab after a successful mount Darrick J. Wong
2026-04-22 23:20 ` [PATCH 06/13] util: hoist the fuse.conf parsing and setuid mode enforcement code Darrick J. Wong
2026-04-26 20:42 ` Bernd Schubert
2026-04-27 14:40 ` Darrick J. Wong
2026-04-22 23:21 ` [PATCH 07/13] util: fix checkpatch complaints in fuser_conf.[ch] Darrick J. Wong
2026-04-22 23:21 ` [PATCH 08/13] mount_service: enable unprivileged users in a similar manner as fusermount Darrick J. Wong
2026-04-22 23:21 ` [PATCH 09/13] mount.fuse3: integrate systemd service startup Darrick J. Wong
2026-04-28 18:10 ` Darrick J. Wong [this message]
2026-04-22 23:21 ` [PATCH 10/13] mount_service: allow installation as a setuid program Darrick J. Wong
2026-04-22 23:22 ` [PATCH 11/13] example/service_ll: create a sample systemd service fuse server Darrick J. Wong
2026-04-26 21:28 ` Bernd Schubert
2026-04-27 14:51 ` Darrick J. Wong
2026-04-22 23:22 ` [PATCH 12/13] example/service: create a sample systemd service for a high-level " Darrick J. Wong
2026-04-26 21:04 ` Bernd Schubert
2026-04-27 15:04 ` Darrick J. Wong
2026-04-26 21:21 ` Bernd Schubert
2026-04-27 15:13 ` Darrick J. Wong
2026-04-22 23:22 ` [PATCH 13/13] nullfs: support fuse systemd service mode Darrick J. Wong
2026-04-26 16:35 ` [PATCHSET v5] libfuse: run fuse servers as a contained service Bernd Schubert
2026-04-26 16:56 ` Darrick J. Wong
2026-04-26 19:35 ` Bernd Schubert
2026-04-26 20:23 ` Bernd Schubert
2026-04-22 23:19 ` [PATCHSET 1/2] libext2fs: fix some missed fsync calls Darrick J. Wong
2026-04-22 23:23 ` [PATCH 1/3] libext2fs: always fsync the device when flushing the cache Darrick J. Wong
2026-04-22 23:23 ` [PATCH 2/3] libext2fs: always fsync the device when closing the unix IO manager Darrick J. Wong
2026-04-22 23:23 ` [PATCH 3/3] libext2fs: only fsync the unix fd if we wrote to the device Darrick J. Wong
2026-04-22 23:19 ` [PATCHSET v5 2/2] fuse4fs: run servers as a contained service Darrick J. Wong
2026-04-22 23:23 ` [PATCH 01/10] libext2fs: make it possible to extract the fd from an IO manager Darrick J. Wong
2026-04-22 23:24 ` [PATCH 02/10] libext2fs: fix checking for valid fds in mmp.c Darrick J. Wong
2026-04-22 23:24 ` [PATCH 03/10] unix_io: allow passing /dev/fd/XXX paths to the unixfd IO manager Darrick J. Wong
2026-04-22 23:24 ` [PATCH 04/10] libext2fs: fix MMP code to work with " Darrick J. Wong
2026-04-22 23:24 ` [PATCH 05/10] libext2fs: bump libfuse API version to 3.19 Darrick J. Wong
2026-04-22 23:25 ` [PATCH 06/10] fuse4fs: hoist some code out of fuse4fs_main Darrick J. Wong
2026-04-22 23:25 ` [PATCH 07/10] fuse4fs: enable safe service mode Darrick J. Wong
2026-04-22 23:25 ` [PATCH 08/10] fuse4fs: set proc title when in fuse " Darrick J. Wong
2026-04-22 23:25 ` [PATCH 09/10] fuse4fs: make MMP work correctly in safe " Darrick J. Wong
2026-04-22 23:26 ` [PATCH 10/10] debian: update packaging for fuse4fs service Darrick J. Wong
2026-04-22 23:29 ` [RFC PATCH 1/4] fusefatfs: enable fuse systemd service mode Darrick J. Wong
2026-04-22 23:30 ` [RFC PATCH 2/4] exfat: " Darrick J. Wong
2026-04-22 23:32 ` [RFC PATCH 3/4] fuseiso: enable " Darrick J. Wong
2026-04-22 23:32 ` [RFC PATCH 4/4] httpdirfs: enable fuse " Darrick J. Wong
2026-04-23 8:44 ` [PATCHBOMB v5] fuse/libfuse/e2fsprogs/etc: containerize ext4 for safer operation Amir Goldstein
2026-04-23 14:50 ` Darrick J. Wong
-- strict thread matches above, loose matches on Subject: below --
2026-04-30 21:15 [PATCHSET v5.1] libfuse: run fuse servers as a contained service Darrick J. Wong
2026-04-30 21:17 ` [PATCH 09/13] mount.fuse3: integrate systemd service startup Darrick J. Wong
2026-04-09 22:20 [PATCHSET v4] libfuse: run fuse servers as a contained service 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
2026-04-20 23:41 ` 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=20260428181003.GP7739@frogsfrogsfrogs \
--to=djwong@kernel.org \
--cc=bernd@bsbernd.com \
--cc=fuse-devel@lists.linux.dev \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.