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: 47+ 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-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 v5 2/2] fuse4fs: run " 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-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox