All of lore.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: 74+ 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-04-18 18:26     ` Bernd Schubert
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-04-19 17:45     ` Bernd Schubert
2026-04-20 15:48       ` 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-04-19 22:36     ` Bernd Schubert
2026-04-20 15:53       ` Darrick J. Wong
2026-04-20 16:40         ` Bernd Schubert
2026-04-20 16:43           ` 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-04-19 23:18     ` Bernd Schubert
2026-04-20 15:55       ` Darrick J. Wong
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-04-20  0:02     ` Bernd Schubert
2026-04-20 16:31       ` 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-04-20  0:21     ` Bernd Schubert
2026-04-20 16:39       ` 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
2026-04-07 12:04 ` fuse-devel list on kernel.org Amir Goldstein
2026-04-07 12:25   ` Bernd Schubert
2026-04-07 12:29     ` Amir Goldstein
2026-04-07 18:05       ` Bernd Schubert
2026-04-07 19:10         ` Darrick J. Wong
2026-04-08  8:21           ` Amir Goldstein
2026-04-13 12:23             ` Amir Goldstein
2026-04-13 12:36               ` Bernd Schubert

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 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.