public inbox for linux-fsdevel@vger.kernel.org
 help / color / mirror / Atom feed
From: "Darrick J. Wong" <djwong@kernel.org>
To: Bernd Schubert <bernd@bsbernd.com>
Cc: 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'],
> > 
> 
> 

  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