public inbox for linux-fsdevel@vger.kernel.org
 help / color / mirror / Atom feed
From: "Darrick J. Wong" <djwong@kernel.org>
To: Bernd Schubert <bernd@bsbernd.com>
Cc: linux-fsdevel@vger.kernel.org, Miklos Szeredi <miklos@szeredi.hu>,
	Joanne Koong <joannelkoong@gmail.com>, Kevin Chen <kchen@ddn.com>,
	Bernd Schubert <bschubert@ddn.com>
Subject: Re: [PATCH v2 20/25] Make fusermount work bidirectional for sync init
Date: Mon, 30 Mar 2026 12:03:15 -0700	[thread overview]
Message-ID: <20260330190315.GA6202@frogsfrogsfrogs> (raw)
In-Reply-To: <20260326-fuse-init-before-mount-v2-20-b1ca8fcbf60f@bsbernd.com>

On Thu, Mar 26, 2026 at 10:34:53PM +0100, Bernd Schubert wrote:
> From: Bernd Schubert <bschubert@ddn.com>
> 
> Signed-off-by: Bernd Schubert <bschubert@ddn.com>
> ---
>  util/fusermount.c | 302 +++++++++++++++++++++++++++++++++++++++++++++++++++---
>  util/meson.build  |   2 +-
>  2 files changed, 291 insertions(+), 13 deletions(-)
> 
> diff --git a/util/fusermount.c b/util/fusermount.c
> index d42e16955f6d26f81e6d2a5acb040b1448e20b51..31bf959024ae0cd2a4e50974589bfab30a100b0a 100644
> --- a/util/fusermount.c
> +++ b/util/fusermount.c
> @@ -11,6 +11,9 @@
>  #include "fuse_config.h"
>  #include "mount_util.h"
>  #include "util.h"
> +#if __linux__
> +#include "mount_i_linux.h"
> +#endif
>  
>  #include <stdio.h>
>  #include <stdlib.h>
> @@ -923,6 +926,7 @@ static void free_mount_params(struct mount_params *mp)
>  	free(mp->source);
>  	free(mp->type);
>  	free(mp->mnt_opts);
> +	memset(mp, 0, sizeof(*mp));
>  }
>  
>  /*
> @@ -1309,6 +1313,171 @@ static int open_fuse_device(const char *dev)
>  	return fd;
>  }
>  
> +/*
> + * Context for split mount operation (sync-init mode)
> + */
> +struct mount_context {
> +	int fd;
> +	const char *dev;
> +	struct stat stbuf;
> +	char *source;
> +	char *mnt_opts;
> +	char *x_opts;
> +	const char *type;
> +};
> +
> +/*
> + * Phase 1: Open device and prepare for mount (sync-init mode)
> + * Returns fd on success, -1 on failure
> + */
> +static int mount_fuse_prepare(const char *mnt, const char *opts,
> +			      struct mount_context *ctx)
> +{
> +	int res;
> +	int mountpoint_fd = -1;
> +	char *do_mount_opts = NULL;
> +	const char *real_mnt = mnt;
> +
> +	memset(ctx, 0, sizeof(*ctx));
> +	ctx->dev = getenv(FUSE_KERN_DEVICE_ENV) ?: FUSE_DEV;

Should this use the helper to figure out the fuse device path?

> +
> +	ctx->fd = open_fuse_device(ctx->dev);
> +	if (ctx->fd == -1)
> +		return -1;
> +
> +	drop_privs();
> +	read_conf();
> +
> +	if (getuid() != 0 && mount_max != -1) {
> +		int mount_count = count_fuse_fs();
> +
> +		if (mount_count >= mount_max) {
> +			fprintf(stderr,
> +				"%s: too many FUSE filesystems mounted; mount_max=N can be set in %s\n",
> +				progname, FUSE_CONF);
> +			goto fail_close_fd;
> +		}
> +	}
> +
> +	res = extract_x_options(opts, &do_mount_opts, &ctx->x_opts);
> +	if (res)
> +		goto fail_close_fd;
> +
> +	res = check_perm(&real_mnt, &ctx->stbuf, &mountpoint_fd);
> +	restore_privs();
> +
> +	if (mountpoint_fd != -1)
> +		close(mountpoint_fd);
> +
> +	if (res == -1)
> +		goto fail_close_fd;
> +
> +	free(do_mount_opts);
> +	return ctx->fd;
> +
> +fail_close_fd:
> +	close(ctx->fd);
> +	free(do_mount_opts);
> +	free(ctx->x_opts);
> +	ctx->fd = -1;
> +	return -1;
> +}
> +
> +#ifdef HAVE_NEW_MOUNT_API
> +/*
> + * Phase 2: Perform the actual mount using new mount API (sync-init mode)
> + * Returns 0 on success, -1 on failure
> + */
> +static int mount_fuse_finish_fsmount(const char *mnt, const char *opts,
> +				     struct mount_context *ctx,
> +				     const char **type)
> +{
> +	int res;
> +	char *do_mount_opts = NULL;
> +	char *x_prefixed_opts = NULL;
> +	struct mount_params mp = {
> +		.fd = ctx->fd,
> +		.rootmode = ctx->stbuf.st_mode & S_IFMT,
> +		.dev = ctx->dev,
> +	};
> +	char *final_mnt_opts = NULL;
> +
> +	/* Extract x-options */
> +	res = extract_x_options(opts, &do_mount_opts, &x_prefixed_opts);

Didn't we already extract_x_options() in phase 1?  Can we save
do_mount_opts in ctx instead of calling this twice?

> +	if (res)
> +		goto fail;
> +
> +	res = prepare_mount(do_mount_opts, &mp);
> +	if (res == -1)
> +		goto fail;
> +
> +	/* Merge x-options if running as root */
> +	final_mnt_opts = mp.mnt_opts;
> +	if (geteuid() == 0 && ctx->x_opts && strlen(ctx->x_opts) > 0) {
> +		size_t mnt_opts_len = strlen(mp.mnt_opts);
> +		size_t x_mnt_opts_len = mnt_opts_len + strlen(ctx->x_opts) + 2;
> +		char *x_mnt_opts = calloc(1, x_mnt_opts_len);
> +
> +		if (!x_mnt_opts)
> +			goto fail_free_params;
> +
> +		if (mnt_opts_len) {
> +			strcpy(x_mnt_opts, mp.mnt_opts);
> +			strncat(x_mnt_opts, ",", 2);
> +		}
> +		strncat(x_mnt_opts, ctx->x_opts,
> +			x_mnt_opts_len - mnt_opts_len - 2);

asprintf?

--D

> +
> +		final_mnt_opts = x_mnt_opts;
> +	}
> +
> +	/* Use new mount API */
> +	res = fuse_kern_fsmount(mnt, mp.flags, mp.blkdev,
> +				mp.fsname, mp.subtype, ctx->dev,
> +				mp.optbuf, final_mnt_opts);
> +	if (res == -1)
> +		goto fail_free_merged;
> +
> +	/* Change to root directory */
> +	res = chdir("/");
> +	if (res == -1) {
> +		fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
> +		goto fail_free_merged;
> +	}
> +
> +	/* Store results in context */
> +	ctx->source = mp.source;
> +	ctx->type = mp.type;
> +	ctx->mnt_opts = final_mnt_opts;
> +	*type = mp.type;
> +
> +	res = 0;
> +
> +	/* Only free what is not assigned to ctx */
> +	free(mp.fsname);
> +	free(mp.subtype);
> +	free(mp.optbuf);
> +	if (final_mnt_opts != mp.mnt_opts)
> +		free(mp.mnt_opts);
> +
> +out:
> +	free(do_mount_opts);
> +	free(x_prefixed_opts);
> +
> +	return res;
> +
> +fail_free_merged:
> +	if (final_mnt_opts != mp.mnt_opts)
> +		free(final_mnt_opts);
> +fail_free_params:
> +	free_mount_params(&mp);
> +fail:
> +	res = -1;
> +	goto out;
> +}
> +#endif /* HAVE_NEW_MOUNT_API */
> +
> +
>  static int mount_fuse(const char *mnt, const char *opts, const char **type)
>  {
>  	int res;
> @@ -1404,6 +1573,65 @@ fail_close_fd:
>  	goto out_free;
>  }
>  
> +/* Forward declarations for helper functions */
> +static int send_fd(int sock_fd, int fd);
> +static int wait_for_signal(int sock_fd);
> +
> +#ifdef HAVE_NEW_MOUNT_API
> +/*
> + * Perform sync-init mount using new mount API
> + * Returns 0 on success, -1 on failure
> + */
> +static int mount_fuse_sync_init(const char *mnt, const char *opts,
> +				int cfd, const char **type)
> +{
> +	struct mount_context ctx;
> +	int fd, res;
> +	int32_t status, send_res;
> +
> +	/* Phase 1: Open device and prepare */
> +	fd = mount_fuse_prepare(mnt, opts, &ctx);
> +	if (fd == -1)
> +		return -1;
> +
> +	/* Send fd to caller so it can start worker thread */
> +	res = send_fd(cfd, fd);
> +	if (res != 0) {
> +		close(fd);
> +		free(ctx.x_opts);
> +		return -1;
> +	}
> +
> +	/* Wait for caller to signal that worker thread is ready */
> +	res = wait_for_signal(cfd);
> +	if (res != 0) {
> +		close(fd);
> +		free(ctx.x_opts);
> +		return -1;
> +	}
> +
> +	/* Phase 2: Perform the actual mount using new API */
> +	res = mount_fuse_finish_fsmount(mnt, opts, &ctx, type);
> +
> +	/* Send mount result back to caller (4-byte error code) */
> +	status = (res == 0) ? 0 : -(int32_t)errno;
> +	do {
> +		send_res = send(cfd, &status, sizeof(status), 0);
> +	} while (send_res == -1 && errno == EINTR);
> +	if (send_res != sizeof(status)) {
> +		fprintf(stderr, "%s: failed to send mount status: %s\n",
> +			progname, strerror(errno));
> +	}
> +
> +	close(fd);
> +	free(ctx.source);
> +	free(ctx.mnt_opts);
> +	free(ctx.x_opts);
> +
> +	return res;
> +}
> +#endif /* HAVE_NEW_MOUNT_API */
> +
>  static int send_fd(int sock_fd, int fd)
>  {
>  	int retval;
> @@ -1440,6 +1668,30 @@ static int send_fd(int sock_fd, int fd)
>  	return 0;
>  }
>  
> +/*
> + * Wait for a signal byte from the caller.
> + * Returns 0 on success, -1 on error.
> + */
> +static int wait_for_signal(int sock_fd)
> +{
> +	char buf[1];
> +	int res;
> +
> +	do {
> +		res = recv(sock_fd, buf, sizeof(buf), 0);
> +	} while (res == -1 && errno == EINTR);
> +	if (res != 1) {
> +		if (res == 0)
> +			fprintf(stderr, "%s: connection closed while waiting for signal\n",
> +				progname);
> +		else
> +			fprintf(stderr, "%s: error receiving signal: %s\n",
> +				progname, strerror(errno));
> +		return -1;
> +	}
> +	return 0;
> +}
> +
>  /* Helper for should_auto_unmount
>   *
>   * fusermount typically has the s-bit set - initial open of `mnt` was as root
> @@ -1631,6 +1883,7 @@ int main(int argc, char *argv[])
>  	const char *opts = "";
>  	const char *type = NULL;
>  	int setup_auto_unmount_only = 0;
> +	int sync_init_mode = 0;
>  
>  	static const struct option long_opts[] = {
>  		{"unmount", no_argument, NULL, 'u'},
> @@ -1643,6 +1896,7 @@ int main(int argc, char *argv[])
>  		// They'ne meant for internal use by mount.c
>  		{"auto-unmount", no_argument, NULL, 'U'},
>  		{"comm-fd", required_argument, NULL, 'c'},
> +		{"sync-init", no_argument, NULL, 'S'},
>  		{0, 0, 0, 0}};
>  
>  	progname = strdup(argc > 0 ? argv[0] : "fusermount");
> @@ -1677,6 +1931,9 @@ int main(int argc, char *argv[])
>  		case 'c':
>  			commfd = optarg;
>  			break;
> +		case 'S':
> +			sync_init_mode = 1;
> +			break;
>  		case 'z':
>  			lazy = 1;
>  			break;
> @@ -1754,21 +2011,42 @@ int main(int argc, char *argv[])
>  	if (setup_auto_unmount_only)
>  		goto wait_for_auto_unmount;
>  
> -	fd = mount_fuse(mnt, opts, &type);
> -	if (fd == -1)
> -		goto err_out;
> +	if (sync_init_mode) {
> +#ifdef HAVE_NEW_MOUNT_API
> +		res = mount_fuse_sync_init(mnt, opts, cfd, &type);
> +		if (res == -1)
> +			goto err_out;
>  
> -	res = send_fd(cfd, fd);
> -	if (res != 0) {
> -		umount2(mnt, MNT_DETACH); /* lazy umount */
> +		if (!auto_unmount) {
> +			free(mnt);
> +			free((void *) type);
> +			return 0;
> +		}
> +		/* Continue to auto_unmount handling below */
> +#else
> +		fprintf(stderr, "%s: sync-init mode requires new mount API support\n",
> +			progname);
> +		fprintf(stderr, "%s: kernel or headers too old (need fsopen/fsmount)\n",
> +			progname);
>  		goto err_out;
> -	}
> -	close(fd);
> +#endif
> +	} else {
> +		fd = mount_fuse(mnt, opts, &type);
> +		if (fd == -1)
> +			goto err_out;
>  
> -	if (!auto_unmount) {
> -		free(mnt);
> -		free((void*) type);
> -		return 0;
> +		res = send_fd(cfd, fd);
> +		if (res != 0) {
> +			umount2(mnt, MNT_DETACH); /* lazy umount */
> +			goto err_out;
> +		}
> +		close(fd);
> +
> +		if (!auto_unmount) {
> +			free(mnt);
> +			free((void *) type);
> +			return 0;
> +		}
>  	}
>  
>  wait_for_auto_unmount:
> diff --git a/util/meson.build b/util/meson.build
> index 0e4b1cce95377e73af7dc45655a7088315497ddb..731ef95488461ac21c21b1972a96d58b1187dc5a 100644
> --- a/util/meson.build
> +++ b/util/meson.build
> @@ -1,6 +1,6 @@
>  fuseconf_path = join_paths(get_option('prefix'), get_option('sysconfdir'), 'fuse.conf')
>  
> -executable('fusermount3', ['fusermount.c', '../lib/mount_util.c', '../lib/util.c'],
> +executable('fusermount3', ['fusermount.c', '../lib/mount_util.c', '../lib/mount_fsmount.c', '../lib/util.c'],
>             include_directories: include_dirs,
>             install: true,
>             install_dir: get_option('bindir'),
> 
> -- 
> 2.43.0
> 
> 

  reply	other threads:[~2026-03-30 19:03 UTC|newest]

Thread overview: 53+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-26 21:34 [PATCH v2 00/25] libfuse: Add support for synchronous init Bernd Schubert
2026-03-26 21:34 ` [PATCH v2 01/25] ci-build: Add environment logging Bernd Schubert
2026-03-27  3:20   ` Darrick J. Wong
2026-03-26 21:34 ` [PATCH v2 02/25] Add 'STRCPY' to the checkpatch ignore option Bernd Schubert
2026-03-26 21:34 ` [PATCH v2 03/25] checkpatch.pl: Add _Atomic to $Attribute patttern Bernd Schubert
2026-03-26 21:34 ` [PATCH v2 04/25] Add a new daemonize API Bernd Schubert
2026-03-27 22:06   ` Darrick J. Wong
2026-03-27 23:07     ` Bernd Schubert
2026-03-28  4:01       ` Darrick J. Wong
2026-03-30 17:45       ` Darrick J. Wong
2026-03-30 18:26         ` Bernd Schubert
2026-03-30 21:25           ` Darrick J. Wong
2026-03-30 17:55   ` Darrick J. Wong
2026-03-26 21:34 ` [PATCH v2 05/25] Sync fuse_kernel.h with linux-6.18 Bernd Schubert
2026-03-26 21:34 ` [PATCH v2 06/25] mount.c: Split fuse_mount_sys to prepare privileged sync FUSE_INIT Bernd Schubert
2026-03-26 21:34 ` [PATCH v2 07/25] Add FUSE_MOUNT_FALLBACK_NEEDED define for -2 mount errors Bernd Schubert
2026-03-27  3:20   ` Darrick J. Wong
2026-03-26 21:34 ` [PATCH v2 08/25] Refactor mount code / move common functions to mount_util.c Bernd Schubert
2026-03-26 21:34 ` [PATCH v2 09/25] Use asprintf() for fuse_mnt_build_{source,type} Bernd Schubert
2026-03-27  3:24   ` Darrick J. Wong
2026-03-30 15:34     ` Bernd Schubert
2026-03-26 21:34 ` [PATCH v2 10/25] lib/mount.c: Remove some BSD ifdefs Bernd Schubert
2026-03-27  3:28   ` Darrick J. Wong
2026-03-26 21:34 ` [PATCH v2 11/25] Move 'struct mount_flags' to util.h Bernd Schubert
2026-03-30 18:11   ` Darrick J. Wong
2026-03-26 21:34 ` [PATCH v2 12/25] conftest.py: Add more valgrind filter patterns Bernd Schubert
2026-03-30 18:16   ` Darrick J. Wong
2026-03-26 21:34 ` [PATCH v2 13/25] Add support for the new linux mount API Bernd Schubert
2026-03-30 18:27   ` Darrick J. Wong
2026-03-26 21:34 ` [PATCH v2 14/25] fuse mount: Support synchronous FUSE_INIT (privileged daemon) Bernd Schubert
2026-03-30 18:44   ` Darrick J. Wong
2026-03-26 21:34 ` [PATCH v2 15/25] Add fuse_session_set_debug() to enable debug output without foreground Bernd Schubert
2026-03-26 21:34 ` [PATCH v2 16/25] Move more generic mount code to mount_util.{c,h} Bernd Schubert
2026-03-30 18:47   ` Darrick J. Wong
2026-03-26 21:34 ` [PATCH v2 17/25] Split the fusermount do_mount function Bernd Schubert
2026-03-30 18:48   ` Darrick J. Wong
2026-03-26 21:34 ` [PATCH v2 18/25] fusermout: Remove the large read check Bernd Schubert
2026-03-27  3:32   ` Darrick J. Wong
2026-03-30 15:26     ` Bernd Schubert
2026-03-30 17:57       ` Darrick J. Wong
2026-03-26 21:34 ` [PATCH v2 19/25] fusermount: Refactor extract_x_options Bernd Schubert
2026-03-26 21:34 ` [PATCH v2 20/25] Make fusermount work bidirectional for sync init Bernd Schubert
2026-03-30 19:03   ` Darrick J. Wong [this message]
2026-03-26 21:34 ` [PATCH v2 21/25] New mount API: Filter out "user=" Bernd Schubert
2026-03-27  3:32   ` Darrick J. Wong
2026-03-26 21:34 ` [PATCH v2 22/25] Add support for sync-init of unprivileged daemons Bernd Schubert
2026-03-31  0:54   ` Darrick J. Wong
2026-03-26 21:34 ` [PATCH v2 23/25] Move fuse_mnt_build_{source,type} to mount_util.c Bernd Schubert
2026-03-30 19:04   ` Darrick J. Wong
2026-03-26 21:34 ` [PATCH v2 24/25] Add mount and daemonization README documents Bernd Schubert
2026-03-31  1:17   ` Darrick J. Wong
2026-03-26 21:34 ` [PATCH v2 25/25] Add a background debug option to passthrough hp Bernd Schubert
2026-03-30 19:04   ` 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=20260330190315.GA6202@frogsfrogsfrogs \
    --to=djwong@kernel.org \
    --cc=bernd@bsbernd.com \
    --cc=bschubert@ddn.com \
    --cc=joannelkoong@gmail.com \
    --cc=kchen@ddn.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=miklos@szeredi.hu \
    /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