From: "Darrick J. Wong" <djwong@kernel.org>
To: Bernd Schubert <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 06/13] util: hoist the fuse.conf parsing and setuid mode enforcement code
Date: Mon, 27 Apr 2026 07:40:44 -0700 [thread overview]
Message-ID: <20260427144044.GK7739@frogsfrogsfrogs> (raw)
In-Reply-To: <dc82e3b8-09ee-4681-932f-2948d8e9a021@bsbernd.com>
On Sun, Apr 26, 2026 at 10:42:51PM +0200, Bernd Schubert wrote:
>
>
> On 4/23/26 01:20, Darrick J. Wong wrote:
> > From: Darrick J. Wong <djwong@kernel.org>
> >
> > Move all the code that parses fuse.conf into a separate file in util/ so
> > that fuservicemount can read the same file, then add the security checks
> > that occur when fusermount is trying to start up a filesystem but is not
> > running as root. We'll want that for fusermount in a moment.
> >
> > Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
> > ---
> > util/fuser_conf.h | 61 ++++++++
> > util/fuser_conf.c | 381 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> > util/fusermount.c | 358 +-------------------------------------------------
> > util/meson.build | 6 -
> > 4 files changed, 453 insertions(+), 353 deletions(-)
> > create mode 100644 util/fuser_conf.h
> > create mode 100644 util/fuser_conf.c
> >
> >
> > diff --git a/util/fuser_conf.h b/util/fuser_conf.h
> > new file mode 100644
> > index 00000000000000..5afe70709c5152
> > --- /dev/null
> > +++ b/util/fuser_conf.h
> > @@ -0,0 +1,61 @@
> > +/*
> > + * FUSE: Filesystem in Userspace
> > + * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
> > + *
> > + * This program can be distributed under the terms of the GNU LGPLv2.
> > + * See the file LGPL2.txt.
> > + */
> > +#ifndef FUSER_CONF_H_
> > +#define FUSER_CONF_H_
> > +
> > +#include <sys/vfs.h>
> > +#include <sys/stat.h>
> > +
> > +extern int user_allow_other;
> > +extern int mount_max;
> > +
> > +void unescape(char *buf);
> > +
> > +#ifdef GETMNTENT_NEEDS_UNESCAPING
> > +#include <stdio.h>
> > +#include <mntent.h>
> > +
> > +static inline struct mntent *GETMNTENT(FILE *stream)
> > +{
> > + struct mntent *entp = getmntent(stream);
> > + if(entp != NULL) {
> > + unescape(entp->mnt_fsname);
> > + unescape(entp->mnt_dir);
> > + unescape(entp->mnt_type);
> > + unescape(entp->mnt_opts);
> > + }
> > + return entp;
> > +}
> > +#else
> > +#define GETMNTENT getmntent
> > +#endif // GETMNTENT_NEEDS_UNESCAPING
> > +
> > +int count_fuse_fs(const char *progname);
> > +
> > +void read_conf(const char *progname);
> > +
> > +void drop_privs(void);
> > +void restore_privs(void);
> > +
> > +int check_nonroot_mount_count(const char *progname);
> > +
> > +int check_nonroot_dir_access(const char *progname, const char *origmnt,
> > + const char *mnt, const struct stat *stbuf);
> > +
> > +int check_nonroot_fstype(const char *progname, const struct statfs *fs_buf);
> > +
> > +struct mount_flags {
> > + const char *opt;
> > + unsigned long flag;
> > + int on;
> > + int safe;
> > +};
> > +
> > +extern const struct mount_flags mount_flags[];
> > +
> > +#endif /* FUSER_CONF_H_ */
> > diff --git a/util/fuser_conf.c b/util/fuser_conf.c
> > new file mode 100644
> > index 00000000000000..dd60afaad31a0f
> > --- /dev/null
> > +++ b/util/fuser_conf.c
> > @@ -0,0 +1,381 @@
> > +/*
> > + * FUSE: Filesystem in Userspace
> > + * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
> > + *
> > + * This program can be distributed under the terms of the GNU GPLv2.
> > + * See the file GPL2.txt.
> > + */
> > +/* This program parses fuse.conf */
> > +#define _GNU_SOURCE
> > +#include "fuse_config.h"
> > +#include "mount_util.h"
> > +#include "util.h"
> > +#include "fuser_conf.h"
>
> #include "fuse_mount_compat.h"
>
> otherwise fails to compile
>
> etc/fuse.conf"' -MD -MQ util/fusermount3.p/fuser_conf.c.o -MF
> util/fusermount3.p/fuser_conf.c.o.d -o util/fusermount3.p/fuser_conf.c.o
> -c ../util/fuser_conf.c
> ../util/fuser_conf.c:357:13: error: use of undeclared identifier 'MS_RDONLY'
> 357 | {"rw", MS_RDONLY, 0, 1},
> | ^
> ../util/fuser_conf.c:358:13: error: use of undeclared identifier 'MS_RDONLY'
> 358 | {"ro", MS_RDONLY, 1, 1},
Fixed in my branch now too.
--D
> > +
> > +#include <string.h>
> > +#include <stddef.h>
> > +#include <ctype.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <errno.h>
> > +#include <mntent.h>
> > +#include <unistd.h>
> > +#include <sys/fsuid.h>
> > +
> > +#if defined HAVE_LISTMOUNT
> > +#include <linux/mount.h>
> > +#include <syscall.h>
> > +#include <stdint.h>
> > +#endif
> > +
> > +int user_allow_other = 0;
> > +int mount_max = 1000;
> > +static uid_t oldfsuid;
> > +static gid_t oldfsgid;
> > +
> > +// Older versions of musl libc don't unescape entries in /etc/mtab
> > +
> > +// unescapes octal sequences like \040 in-place
> > +// That's ok, because unescaping can not extend the length of the string.
> > +void unescape(char *buf)
> > +{
> > + char *src = buf;
> > + char *dest = buf;
> > + while (1) {
> > + char *next_src = strchrnul(src, '\\');
> > + int offset = next_src - src;
> > + memmove(dest, src, offset);
> > + src = next_src;
> > + dest += offset;
> > +
> > + if(*src == '\0') {
> > + *dest = *src;
> > + return;
> > + }
> > + src++;
> > +
> > + if('0' <= src[0] && src[0] < '2' &&
> > + '0' <= src[1] && src[1] < '8' &&
> > + '0' <= src[2] && src[2] < '8') {
> > + *dest++ = (src[0] - '0') << 6
> > + | (src[1] - '0') << 3
> > + | (src[2] - '0') << 0;
> > + src += 3;
> > + } else if (src[0] == '\\') {
> > + *dest++ = '\\';
> > + src += 1;
> > + } else {
> > + *dest++ = '\\';
> > + }
> > + }
> > +}
> > +
> > +#ifndef IGNORE_MTAB
> > +static int count_fuse_fs_mtab(const char *progname)
> > +{
> > + const struct mntent *entp;
> > + int count = 0;
> > + const char *mtab = _PATH_MOUNTED;
> > + FILE *fp = setmntent(mtab, "r");
> > + if (fp == NULL) {
> > + fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
> > + strerror(errno));
> > + return -1;
> > + }
> > + while ((entp = GETMNTENT(fp)) != NULL) {
> > + if (strcmp(entp->mnt_type, "fuse") == 0 ||
> > + strncmp(entp->mnt_type, "fuse.", 5) == 0)
> > + count ++;
> > + }
> > + endmntent(fp);
> > + return count;
> > +}
> > +
> > +#ifdef HAVE_LISTMOUNT
> > +static int count_fuse_fs_ls_mnt(const char *progname)
> > +{
> > + #define SMBUF_SIZE 1024
> > + #define MNT_ID_LEN 128
> > +
> > + int fuse_count = 0;
> > + int n_mounts = 0;
> > + int ret = 0;
> > + uint64_t mnt_ids[MNT_ID_LEN];
> > + unsigned char smbuf[SMBUF_SIZE];
> > + struct mnt_id_req req = {
> > + .size = sizeof(struct mnt_id_req),
> > + };
> > + struct statmount *sm;
> > +
> > + for (;;) {
> > + req.mnt_id = LSMT_ROOT;
> > +
> > + n_mounts = syscall(SYS_listmount, &req, &mnt_ids, MNT_ID_LEN, 0);
> > + if (n_mounts == -1) {
> > + if (errno != ENOSYS) {
> > + fprintf(stderr, "%s: failed to list mounts: %s\n", progname,
> > + strerror(errno));
> > + }
> > + return -1;
> > + }
> > +
> > + for (int i = 0; i < n_mounts; i++) {
> > + req.mnt_id = mnt_ids[i];
> > + req.param = STATMOUNT_FS_TYPE;
> > + ret = syscall(SYS_statmount, &req, &smbuf, SMBUF_SIZE, 0);
> > + if (ret) {
> > + if (errno == ENOENT)
> > + continue;
> > +
> > + fprintf(stderr, "%s: failed to stat mount %lld: %s\n", progname,
> > + req.mnt_id, strerror(errno));
> > + return -1;
> > + }
> > +
> > + sm = (struct statmount *)smbuf;
> > + if (sm->mask & STATMOUNT_FS_TYPE &&
> > + strcmp(&sm->str[sm->fs_type], "fuse") == 0)
> > + fuse_count++;
> > + }
> > +
> > + if (n_mounts < MNT_ID_LEN)
> > + break;
> > + req.param = mnt_ids[MNT_ID_LEN - 1];
> > + }
> > + return fuse_count;
> > +}
> > +
> > +int count_fuse_fs(const char *progname)
> > +{
> > + int count = count_fuse_fs_ls_mnt(progname);
> > +
> > + return count >= 0 ? count : count_fuse_fs_mtab(progname);
> > +}
> > +#else
> > +int count_fuse_fs(const char *progname)
> > +{
> > + return count_fuse_fs_mtab(progname);
> > +}
> > +#endif /* HAVE_LISTMOUNT */
> > +#else
> > +int count_fuse_fs(const char *progname)
> > +{
> > + return 0;
> > +}
> > +#endif /* !IGNORE_MTAB */
> > +
> > +static void strip_line(char *line)
> > +{
> > + char *s = strchr(line, '#');
> > + if (s != NULL)
> > + s[0] = '\0';
> > + for (s = line + strlen(line) - 1;
> > + s >= line && isspace((unsigned char) *s); s--);
> > + s[1] = '\0';
> > + for (s = line; isspace((unsigned char) *s); s++);
> > + if (s != line)
> > + memmove(line, s, strlen(s)+1);
> > +}
> > +
> > +static void parse_line(const char *line, int linenum, const char *progname)
> > +{
> > + int tmp;
> > + if (strcmp(line, "user_allow_other") == 0)
> > + user_allow_other = 1;
> > + else if (sscanf(line, "mount_max = %i", &tmp) == 1)
> > + mount_max = tmp;
> > + else if(line[0])
> > + fprintf(stderr,
> > + "%s: unknown parameter in %s at line %i: '%s'\n",
> > + progname, FUSE_CONF, linenum, line);
> > +}
> > +
> > +void read_conf(const char *progname)
> > +{
> > + FILE *fp = fopen(FUSE_CONF, "r");
> > + if (fp != NULL) {
> > + int linenum = 1;
> > + char line[256];
> > + int isnewline = 1;
> > + while (fgets(line, sizeof(line), fp) != NULL) {
> > + if (isnewline) {
> > + if (line[strlen(line)-1] == '\n') {
> > + strip_line(line);
> > + parse_line(line, linenum, progname);
> > + } else {
> > + isnewline = 0;
> > + }
> > + } else if(line[strlen(line)-1] == '\n') {
> > + fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum);
> > +
> > + isnewline = 1;
> > + }
> > + if (isnewline)
> > + linenum ++;
> > + }
> > + if (!isnewline) {
> > + fprintf(stderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF);
> > +
> > + }
> > + if (ferror(fp)) {
> > + fprintf(stderr, "%s: reading %s: read failed\n", progname, FUSE_CONF);
> > + exit(1);
> > + }
> > + fclose(fp);
> > + } else if (errno != ENOENT) {
> > + bool fatal = (errno != EACCES && errno != ELOOP &&
> > + errno != ENAMETOOLONG && errno != ENOTDIR &&
> > + errno != EOVERFLOW);
> > + fprintf(stderr, "%s: failed to open %s: %s\n",
> > + progname, FUSE_CONF, strerror(errno));
> > + if (fatal)
> > + exit(1);
> > + }
> > +}
> > +
> > +void drop_privs(void)
> > +{
> > + if (getuid() != 0) {
> > + oldfsuid = setfsuid(getuid());
> > + oldfsgid = setfsgid(getgid());
> > + }
> > +}
> > +
> > +void restore_privs(void)
> > +{
> > + if (getuid() != 0) {
> > + setfsuid(oldfsuid);
> > + setfsgid(oldfsgid);
> > + }
> > +}
> > +
> > +int check_nonroot_mount_count(const char *progname)
> > +{
> > + if (mount_max == -1)
> > + return 0;
> > +
> > + int mount_count = count_fuse_fs(progname);
> > +
> > + 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);
> > + return -1;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +int check_nonroot_dir_access(const char *progname, const char *origmnt,
> > + const char *mnt, const struct stat *stbuf)
> > +{
> > + int res;
> > +
> > + if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
> > + fprintf(stderr, "%s: mountpoint %s not owned by user\n",
> > + progname, origmnt);
> > + return -1;
> > + }
> > +
> > + res = access(mnt, W_OK);
> > + if (res == -1) {
> > + fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
> > + progname, origmnt);
> > + return -1;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +int check_nonroot_fstype(const char *progname, const struct statfs *fs_buf)
> > +{
> > + size_t i;
> > +
> > + /* Do not permit mounting over anything in procfs - it has a couple
> > + * places to which we have "write access" without being supposed to be
> > + * able to just put anything we want there.
> > + * Luckily, without allow_other, we can't get other users to actually
> > + * use any fake information we try to put there anyway.
> > + * Use a whitelist to be safe. */
> > +
> > + /* Define permitted filesystems for the mount target. This was
> > + * originally the same list as used by the ecryptfs mount helper
> > + * (https://bazaar.launchpad.net/~ecryptfs/ecryptfs/trunk/view/head:/src/utils/mount.ecryptfs_private.c#L225)
> > + * but got expanded as we found more filesystems that needed to be
> > + * overlaid. */
> > + typeof(fs_buf->f_type) f_type_whitelist[] = {
> > + 0x61756673 /* AUFS_SUPER_MAGIC */,
> > + 0x00000187 /* AUTOFS_SUPER_MAGIC */,
> > + 0xCA451A4E /* BCACHEFS_STATFS_MAGIC */,
> > + 0x9123683E /* BTRFS_SUPER_MAGIC */,
> > + 0x00C36400 /* CEPH_SUPER_MAGIC */,
> > + 0xFF534D42 /* CIFS_MAGIC_NUMBER */,
> > + 0x0000F15F /* ECRYPTFS_SUPER_MAGIC */,
> > + 0X2011BAB0 /* EXFAT_SUPER_MAGIC */,
> > + 0x0000EF53 /* EXT[234]_SUPER_MAGIC */,
> > + 0xF2F52010 /* F2FS_SUPER_MAGIC */,
> > + 0x65735546 /* FUSE_SUPER_MAGIC */,
> > + 0x01161970 /* GFS2_MAGIC */,
> > + 0x47504653 /* GPFS_SUPER_MAGIC */,
> > + 0x0000482b /* HFSPLUS_SUPER_MAGIC */,
> > + 0x000072B6 /* JFFS2_SUPER_MAGIC */,
> > + 0x3153464A /* JFS_SUPER_MAGIC */,
> > + 0x0BD00BD0 /* LL_SUPER_MAGIC */,
> > + 0X00004D44 /* MSDOS_SUPER_MAGIC */,
> > + 0x0000564C /* NCP_SUPER_MAGIC */,
> > + 0x00006969 /* NFS_SUPER_MAGIC */,
> > + 0x00003434 /* NILFS_SUPER_MAGIC */,
> > + 0x5346544E /* NTFS_SB_MAGIC */,
> > + 0x7366746E /* NTFS3_SUPER_MAGIC */,
> > + 0x5346414f /* OPENAFS_SUPER_MAGIC */,
> > + 0x794C7630 /* OVERLAYFS_SUPER_MAGIC */,
> > + 0xAAD7AAEA /* PANFS_SUPER_MAGIC */,
> > + 0x52654973 /* REISERFS_SUPER_MAGIC */,
> > + 0xFE534D42 /* SMB2_SUPER_MAGIC */,
> > + 0x73717368 /* SQUASHFS_MAGIC */,
> > + 0x01021994 /* TMPFS_MAGIC */,
> > + 0x24051905 /* UBIFS_SUPER_MAGIC */,
> > + 0x18031977 /* WEKAFS_SUPER_MAGIC */,
> > +#if __SIZEOF_LONG__ > 4
> > + 0x736675005346544e /* UFSD */,
> > +#endif
> > + 0x58465342 /* XFS_SB_MAGIC */,
> > + 0x2FC12FC1 /* ZFS_SUPER_MAGIC */,
> > + 0x858458f6 /* RAMFS_MAGIC */,
> > + };
> > + for (i = 0; i < sizeof(f_type_whitelist)/sizeof(f_type_whitelist[0]); i++) {
> > + if (f_type_whitelist[i] == fs_buf->f_type)
> > + return 0;
> > + }
> > +
> > + fprintf(stderr, "%s: mounting over filesystem type %#010lx is forbidden\n",
> > + progname, (unsigned long)fs_buf->f_type);
> > + return -1;
> > +}
> > +
> > +const struct mount_flags mount_flags[] = {
> > + {"rw", MS_RDONLY, 0, 1},
> > + {"ro", MS_RDONLY, 1, 1},
> > + {"suid", MS_NOSUID, 0, 0},
> > + {"nosuid", MS_NOSUID, 1, 1},
> > + {"dev", MS_NODEV, 0, 0},
> > + {"nodev", MS_NODEV, 1, 1},
> > + {"exec", MS_NOEXEC, 0, 1},
> > + {"noexec", MS_NOEXEC, 1, 1},
> > + {"async", MS_SYNCHRONOUS, 0, 1},
> > + {"sync", MS_SYNCHRONOUS, 1, 1},
> > + {"atime", MS_NOATIME, 0, 1},
> > + {"noatime", MS_NOATIME, 1, 1},
> > + {"diratime", MS_NODIRATIME, 0, 1},
> > + {"nodiratime", MS_NODIRATIME, 1, 1},
> > + {"lazytime", MS_LAZYTIME, 1, 1},
> > + {"nolazytime", MS_LAZYTIME, 0, 1},
> > + {"relatime", MS_RELATIME, 1, 1},
> > + {"norelatime", MS_RELATIME, 0, 1},
> > + {"strictatime", MS_STRICTATIME, 1, 1},
> > + {"nostrictatime", MS_STRICTATIME, 0, 1},
> > + {"dirsync", MS_DIRSYNC, 1, 1},
> > + {"symfollow", MS_NOSYMFOLLOW, 0, 1},
> > + {"nosymfollow", MS_NOSYMFOLLOW, 1, 1},
> > + {NULL, 0, 0, 0}
> > +};
> > diff --git a/util/fusermount.c b/util/fusermount.c
> > index 68370468140a59..c7905d58a85e32 100644
> > --- a/util/fusermount.c
> > +++ b/util/fusermount.c
> > @@ -11,6 +11,7 @@
> > #include "fuse_config.h"
> > #include "mount_util.h"
> > #include "util.h"
> > +#include "fuser_conf.h"
> >
> > #include <stdio.h>
> > #include <stdlib.h>
> > @@ -50,63 +51,8 @@
> >
> > static const char *progname;
> >
> > -static int user_allow_other = 0;
> > -static int mount_max = 1000;
> > -
> > static int auto_unmount = 0;
> >
> > -#ifdef GETMNTENT_NEEDS_UNESCAPING
> > -// Older versions of musl libc don't unescape entries in /etc/mtab
> > -
> > -// unescapes octal sequences like \040 in-place
> > -// That's ok, because unescaping can not extend the length of the string.
> > -static void unescape(char *buf) {
> > - char *src = buf;
> > - char *dest = buf;
> > - while (1) {
> > - char *next_src = strchrnul(src, '\\');
> > - int offset = next_src - src;
> > - memmove(dest, src, offset);
> > - src = next_src;
> > - dest += offset;
> > -
> > - if(*src == '\0') {
> > - *dest = *src;
> > - return;
> > - }
> > - src++;
> > -
> > - if('0' <= src[0] && src[0] < '2' &&
> > - '0' <= src[1] && src[1] < '8' &&
> > - '0' <= src[2] && src[2] < '8') {
> > - *dest++ = (src[0] - '0') << 6
> > - | (src[1] - '0') << 3
> > - | (src[2] - '0') << 0;
> > - src += 3;
> > - } else if (src[0] == '\\') {
> > - *dest++ = '\\';
> > - src += 1;
> > - } else {
> > - *dest++ = '\\';
> > - }
> > - }
> > -}
> > -
> > -static struct mntent *GETMNTENT(FILE *stream)
> > -{
> > - struct mntent *entp = getmntent(stream);
> > - if(entp != NULL) {
> > - unescape(entp->mnt_fsname);
> > - unescape(entp->mnt_dir);
> > - unescape(entp->mnt_type);
> > - unescape(entp->mnt_opts);
> > - }
> > - return entp;
> > -}
> > -#else
> > -#define GETMNTENT getmntent
> > -#endif // GETMNTENT_NEEDS_UNESCAPING
> > -
> > /*
> > * Take a ',' separated option string and extract "x-" options
> > */
> > @@ -188,25 +134,6 @@ static const char *get_user_name(void)
> > }
> > }
> >
> > -static uid_t oldfsuid;
> > -static gid_t oldfsgid;
> > -
> > -static void drop_privs(void)
> > -{
> > - if (getuid() != 0) {
> > - oldfsuid = setfsuid(getuid());
> > - oldfsgid = setfsgid(getgid());
> > - }
> > -}
> > -
> > -static void restore_privs(void)
> > -{
> > - if (getuid() != 0) {
> > - setfsuid(oldfsuid);
> > - setfsgid(oldfsgid);
> > - }
> > -}
> > -
> > #ifndef IGNORE_MTAB
> > /*
> > * Make sure that /etc/mtab is checked and updated atomically
> > @@ -568,100 +495,7 @@ static int unmount_fuse(const char *mnt, int quiet, int lazy)
> >
> > return res;
> > }
> > -
> > -static int count_fuse_fs_mtab(void)
> > -{
> > - const struct mntent *entp;
> > - int count = 0;
> > - const char *mtab = _PATH_MOUNTED;
> > - FILE *fp = setmntent(mtab, "r");
> > - if (fp == NULL) {
> > - fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
> > - strerror(errno));
> > - return -1;
> > - }
> > - while ((entp = GETMNTENT(fp)) != NULL) {
> > - if (strcmp(entp->mnt_type, "fuse") == 0 ||
> > - strncmp(entp->mnt_type, "fuse.", 5) == 0)
> > - count ++;
> > - }
> > - endmntent(fp);
> > - return count;
> > -}
> > -
> > -#ifdef HAVE_LISTMOUNT
> > -static int count_fuse_fs_ls_mnt(void)
> > -{
> > - #define SMBUF_SIZE 1024
> > - #define MNT_ID_LEN 128
> > -
> > - int fuse_count = 0;
> > - int n_mounts = 0;
> > - int ret = 0;
> > - uint64_t mnt_ids[MNT_ID_LEN];
> > - unsigned char smbuf[SMBUF_SIZE];
> > - struct mnt_id_req req = {
> > - .size = sizeof(struct mnt_id_req),
> > - };
> > - struct statmount *sm;
> > -
> > - for (;;) {
> > - req.mnt_id = LSMT_ROOT;
> > -
> > - n_mounts = syscall(SYS_listmount, &req, &mnt_ids, MNT_ID_LEN, 0);
> > - if (n_mounts == -1) {
> > - if (errno != ENOSYS) {
> > - fprintf(stderr, "%s: failed to list mounts: %s\n", progname,
> > - strerror(errno));
> > - }
> > - return -1;
> > - }
> > -
> > - for (int i = 0; i < n_mounts; i++) {
> > - req.mnt_id = mnt_ids[i];
> > - req.param = STATMOUNT_FS_TYPE;
> > - ret = syscall(SYS_statmount, &req, &smbuf, SMBUF_SIZE, 0);
> > - if (ret) {
> > - if (errno == ENOENT)
> > - continue;
> > -
> > - fprintf(stderr, "%s: failed to stat mount %lld: %s\n", progname,
> > - req.mnt_id, strerror(errno));
> > - return -1;
> > - }
> > -
> > - sm = (struct statmount *)smbuf;
> > - if (sm->mask & STATMOUNT_FS_TYPE &&
> > - strcmp(&sm->str[sm->fs_type], "fuse") == 0)
> > - fuse_count++;
> > - }
> > -
> > - if (n_mounts < MNT_ID_LEN)
> > - break;
> > - req.param = mnt_ids[MNT_ID_LEN - 1];
> > - }
> > - return fuse_count;
> > -}
> > -
> > -static int count_fuse_fs(void)
> > -{
> > - int count = count_fuse_fs_ls_mnt();
> > -
> > - return count >= 0 ? count : count_fuse_fs_mtab();
> > -}
> > -#else
> > -static int count_fuse_fs(void)
> > -{
> > - return count_fuse_fs_mtab();
> > -}
> > -#endif
> > -
> > #else /* IGNORE_MTAB */
> > -static int count_fuse_fs(void)
> > -{
> > - return 0;
> > -}
> > -
> > static int add_mount(const char *source, const char *mnt, const char *type,
> > const char *opts)
> > {
> > @@ -679,75 +513,6 @@ static int unmount_fuse(const char *mnt, int quiet, int lazy)
> > }
> > #endif /* IGNORE_MTAB */
> >
> > -static void strip_line(char *line)
> > -{
> > - char *s = strchr(line, '#');
> > - if (s != NULL)
> > - s[0] = '\0';
> > - for (s = line + strlen(line) - 1;
> > - s >= line && isspace((unsigned char) *s); s--);
> > - s[1] = '\0';
> > - for (s = line; isspace((unsigned char) *s); s++);
> > - if (s != line)
> > - memmove(line, s, strlen(s)+1);
> > -}
> > -
> > -static void parse_line(const char *line, int linenum)
> > -{
> > - int tmp;
> > - if (strcmp(line, "user_allow_other") == 0)
> > - user_allow_other = 1;
> > - else if (sscanf(line, "mount_max = %i", &tmp) == 1)
> > - mount_max = tmp;
> > - else if(line[0])
> > - fprintf(stderr,
> > - "%s: unknown parameter in %s at line %i: '%s'\n",
> > - progname, FUSE_CONF, linenum, line);
> > -}
> > -
> > -static void read_conf(void)
> > -{
> > - FILE *fp = fopen(FUSE_CONF, "r");
> > - if (fp != NULL) {
> > - int linenum = 1;
> > - char line[256];
> > - int isnewline = 1;
> > - while (fgets(line, sizeof(line), fp) != NULL) {
> > - if (isnewline) {
> > - if (line[strlen(line)-1] == '\n') {
> > - strip_line(line);
> > - parse_line(line, linenum);
> > - } else {
> > - isnewline = 0;
> > - }
> > - } else if(line[strlen(line)-1] == '\n') {
> > - fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum);
> > -
> > - isnewline = 1;
> > - }
> > - if (isnewline)
> > - linenum ++;
> > - }
> > - if (!isnewline) {
> > - fprintf(stderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF);
> > -
> > - }
> > - if (ferror(fp)) {
> > - fprintf(stderr, "%s: reading %s: read failed\n", progname, FUSE_CONF);
> > - exit(1);
> > - }
> > - fclose(fp);
> > - } else if (errno != ENOENT) {
> > - bool fatal = (errno != EACCES && errno != ELOOP &&
> > - errno != ENAMETOOLONG && errno != ENOTDIR &&
> > - errno != EOVERFLOW);
> > - fprintf(stderr, "%s: failed to open %s: %s\n",
> > - progname, FUSE_CONF, strerror(errno));
> > - if (fatal)
> > - exit(1);
> > - }
> > -}
> > -
> > static int begins_with(const char *s, const char *beg)
> > {
> > if (strncmp(s, beg, strlen(beg)) == 0)
> > @@ -756,40 +521,6 @@ static int begins_with(const char *s, const char *beg)
> > return 0;
> > }
> >
> > -struct mount_flags {
> > - const char *opt;
> > - unsigned long flag;
> > - int on;
> > - int safe;
> > -};
> > -
> > -static struct mount_flags mount_flags[] = {
> > - {"rw", MS_RDONLY, 0, 1},
> > - {"ro", MS_RDONLY, 1, 1},
> > - {"suid", MS_NOSUID, 0, 0},
> > - {"nosuid", MS_NOSUID, 1, 1},
> > - {"dev", MS_NODEV, 0, 0},
> > - {"nodev", MS_NODEV, 1, 1},
> > - {"exec", MS_NOEXEC, 0, 1},
> > - {"noexec", MS_NOEXEC, 1, 1},
> > - {"async", MS_SYNCHRONOUS, 0, 1},
> > - {"sync", MS_SYNCHRONOUS, 1, 1},
> > - {"atime", MS_NOATIME, 0, 1},
> > - {"noatime", MS_NOATIME, 1, 1},
> > - {"diratime", MS_NODIRATIME, 0, 1},
> > - {"nodiratime", MS_NODIRATIME, 1, 1},
> > - {"lazytime", MS_LAZYTIME, 1, 1},
> > - {"nolazytime", MS_LAZYTIME, 0, 1},
> > - {"relatime", MS_RELATIME, 1, 1},
> > - {"norelatime", MS_RELATIME, 0, 1},
> > - {"strictatime", MS_STRICTATIME, 1, 1},
> > - {"nostrictatime", MS_STRICTATIME, 0, 1},
> > - {"dirsync", MS_DIRSYNC, 1, 1},
> > - {"symfollow", MS_NOSYMFOLLOW, 0, 1},
> > - {"nosymfollow", MS_NOSYMFOLLOW, 1, 1},
> > - {NULL, 0, 0, 0}
> > -};
> > -
> > static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
> > {
> > int i;
> > @@ -1096,7 +827,6 @@ static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
> > const char *mnt = *mntp;
> > const char *origmnt = mnt;
> > struct statfs fs_buf;
> > - size_t i;
> >
> > res = lstat(mnt, stbuf);
> > if (res == -1) {
> > @@ -1126,18 +856,9 @@ static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
> > return -1;
> > }
> >
> > - if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
> > - fprintf(stderr, "%s: mountpoint %s not owned by user\n",
> > - progname, origmnt);
> > - return -1;
> > - }
> > -
> > - res = access(mnt, W_OK);
> > - if (res == -1) {
> > - fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
> > - progname, origmnt);
> > - return -1;
> > - }
> > + res = check_nonroot_dir_access(progname, origmnt, mnt, stbuf);
> > + if (res)
> > + return res;
> > } else if (S_ISREG(stbuf->st_mode)) {
> > static char procfile[256];
> > *mountpoint_fd = open(mnt, O_WRONLY);
> > @@ -1169,71 +890,13 @@ static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
> > return -1;
> > }
> >
> > - /* Do not permit mounting over anything in procfs - it has a couple
> > - * places to which we have "write access" without being supposed to be
> > - * able to just put anything we want there.
> > - * Luckily, without allow_other, we can't get other users to actually
> > - * use any fake information we try to put there anyway.
> > - * Use a whitelist to be safe. */
> > if (statfs(*mntp, &fs_buf)) {
> > fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
> > progname, mnt, strerror(errno));
> > return -1;
> > }
> >
> > - /* Define permitted filesystems for the mount target. This was
> > - * originally the same list as used by the ecryptfs mount helper
> > - * (https://bazaar.launchpad.net/~ecryptfs/ecryptfs/trunk/view/head:/src/utils/mount.ecryptfs_private.c#L225)
> > - * but got expanded as we found more filesystems that needed to be
> > - * overlaid. */
> > - typeof(fs_buf.f_type) f_type_whitelist[] = {
> > - 0x61756673 /* AUFS_SUPER_MAGIC */,
> > - 0x00000187 /* AUTOFS_SUPER_MAGIC */,
> > - 0xCA451A4E /* BCACHEFS_STATFS_MAGIC */,
> > - 0x9123683E /* BTRFS_SUPER_MAGIC */,
> > - 0x00C36400 /* CEPH_SUPER_MAGIC */,
> > - 0xFF534D42 /* CIFS_MAGIC_NUMBER */,
> > - 0x0000F15F /* ECRYPTFS_SUPER_MAGIC */,
> > - 0X2011BAB0 /* EXFAT_SUPER_MAGIC */,
> > - 0x0000EF53 /* EXT[234]_SUPER_MAGIC */,
> > - 0xF2F52010 /* F2FS_SUPER_MAGIC */,
> > - 0x65735546 /* FUSE_SUPER_MAGIC */,
> > - 0x01161970 /* GFS2_MAGIC */,
> > - 0x47504653 /* GPFS_SUPER_MAGIC */,
> > - 0x0000482b /* HFSPLUS_SUPER_MAGIC */,
> > - 0x000072B6 /* JFFS2_SUPER_MAGIC */,
> > - 0x3153464A /* JFS_SUPER_MAGIC */,
> > - 0x0BD00BD0 /* LL_SUPER_MAGIC */,
> > - 0X00004D44 /* MSDOS_SUPER_MAGIC */,
> > - 0x0000564C /* NCP_SUPER_MAGIC */,
> > - 0x00006969 /* NFS_SUPER_MAGIC */,
> > - 0x00003434 /* NILFS_SUPER_MAGIC */,
> > - 0x5346544E /* NTFS_SB_MAGIC */,
> > - 0x7366746E /* NTFS3_SUPER_MAGIC */,
> > - 0x5346414f /* OPENAFS_SUPER_MAGIC */,
> > - 0x794C7630 /* OVERLAYFS_SUPER_MAGIC */,
> > - 0xAAD7AAEA /* PANFS_SUPER_MAGIC */,
> > - 0x52654973 /* REISERFS_SUPER_MAGIC */,
> > - 0xFE534D42 /* SMB2_SUPER_MAGIC */,
> > - 0x73717368 /* SQUASHFS_MAGIC */,
> > - 0x01021994 /* TMPFS_MAGIC */,
> > - 0x24051905 /* UBIFS_SUPER_MAGIC */,
> > - 0x18031977 /* WEKAFS_SUPER_MAGIC */,
> > -#if __SIZEOF_LONG__ > 4
> > - 0x736675005346544e /* UFSD */,
> > -#endif
> > - 0x58465342 /* XFS_SB_MAGIC */,
> > - 0x2FC12FC1 /* ZFS_SUPER_MAGIC */,
> > - 0x858458f6 /* RAMFS_MAGIC */,
> > - };
> > - for (i = 0; i < sizeof(f_type_whitelist)/sizeof(f_type_whitelist[0]); i++) {
> > - if (f_type_whitelist[i] == fs_buf.f_type)
> > - return 0;
> > - }
> > -
> > - fprintf(stderr, "%s: mounting over filesystem type %#010lx is forbidden\n",
> > - progname, (unsigned long)fs_buf.f_type);
> > - return -1;
> > + return check_nonroot_fstype(progname, &fs_buf);
> > }
> >
> > static int open_fuse_device(const char *dev)
> > @@ -1273,15 +936,10 @@ static int mount_fuse(const char *mnt, const char *opts, const char **type)
> > return -1;
> >
> > drop_privs();
> > - read_conf();
> > + read_conf(progname);
> >
> > - 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;
> > - }
> > - }
> > + if (getuid() != 0 && check_nonroot_mount_count(progname) != 0)
> > + goto fail_close_fd;
> >
> > // Extract any options starting with "x-"
> > res= extract_x_options(opts, &do_mount_opts, &x_opts);
> > diff --git a/util/meson.build b/util/meson.build
> > index 04ea5ac201340d..aa646ef3c77d16 100644
> > --- a/util/meson.build
> > +++ b/util/meson.build
> > @@ -1,18 +1,18 @@
> > 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/util.c', 'fuser_conf.c'],
> > include_directories: include_dirs,
> > install: true,
> > install_dir: get_option('bindir'),
> > c_args: '-DFUSE_CONF="@0@"'.format(fuseconf_path))
> >
> > if private_cfg.get('HAVE_SERVICEMOUNT', false)
> > - executable('fuservicemount3', ['mount_service.c', 'fuservicemount.c', '../lib/mount_util.c'],
> > + executable('fuservicemount3', ['mount_service.c', 'fuservicemount.c', '../lib/mount_util.c', 'fuser_conf.c'],
> > include_directories: include_dirs,
> > link_with: [ libfuse ],
> > install: true,
> > install_dir: get_option('sbindir'),
> > - c_args: '-DFUSE_USE_VERSION=319')
> > + c_args: ['-DFUSE_USE_VERSION=319', '-DFUSE_CONF="@0@"'.format(fuseconf_path)])
> > endif
> >
> > executable('mount.fuse3', ['mount.fuse.c'],
> >
>
>
next prev parent reply other threads:[~2026-04-27 14:40 UTC|newest]
Thread overview: 53+ 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-29 15:23 ` 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 [this message]
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
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 1/2] libext2fs: fix some missed fsync calls Darrick J. Wong
2026-04-22 23:23 ` [PATCH 1/3] libext2fs: always fsync the device when flushing the cache Darrick J. Wong
2026-04-22 23:23 ` [PATCH 2/3] libext2fs: always fsync the device when closing the unix IO manager Darrick J. Wong
2026-04-22 23:23 ` [PATCH 3/3] libext2fs: only fsync the unix fd if we wrote to the device Darrick J. Wong
2026-04-22 23:19 ` [PATCHSET v5 2/2] fuse4fs: run servers as a contained service 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-30 21:15 [PATCHSET v5.1] libfuse: run fuse servers as a contained service Darrick J. Wong
2026-04-30 21:16 ` [PATCH 06/13] util: hoist the fuse.conf parsing and setuid mode enforcement code Darrick J. Wong
2026-04-09 22:20 [PATCHSET v4] libfuse: run fuse servers as a contained service 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
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=20260427144044.GK7739@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 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.