public inbox for linux-fsdevel@vger.kernel.org
 help / color / mirror / Atom feed
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: Mon, 20 Apr 2026 16:41:48 -0700	[thread overview]
Message-ID: <20260420234148.GQ7727@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;
> +	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);
> +		if (ret)
> +			ret = execvp(FUSERVICEMOUNT_PROG, argv);
> +		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);

The maximum length of a path to a Unix socket is dictated (horrifyingly)
by sockaddr_un::sun_path, which is 112 bytes(!!)

Therefore, the return value of snprintf should be checked by the that
size because otherwise we could trick the --check predicate into
checking for the existence of the wrong socket.

--D

> +	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;
> +}
> 
> 

  parent reply	other threads:[~2026-04-20 23:41 UTC|newest]

Thread overview: 28+ 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
2026-04-20 23: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
  -- strict thread matches above, loose matches on Subject: below --
2026-04-22 23:18 [PATCHSET v5] libfuse: run fuse servers as a contained service 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
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

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=20260420234148.GQ7727@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