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
>
>
next prev parent 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