public inbox for linux-fsdevel@vger.kernel.org
 help / color / mirror / Atom feed
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;
> +}
> 
> 

  reply	other threads:[~2026-04-28 18:10 UTC|newest]

Thread overview: 49+ 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-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 v5 2/2] fuse4fs: run " 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-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox