[PATCH 06/13] util: hoist the fuse.conf parsing and setuid mode enforcement code

Darrick J. Wong
2026-04-09T22:22:07Z

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 | 52 ++++++++
util/fuser_conf.c | 354 +++++++++++++++++++++++++++++++++++++++++++++++++++++
util/fusermount.c | 324 +------------------------------------------------
util/meson.build | 6 -
4 files changed, 417 insertions(+), 319 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..758580f906748d
--- /dev/null
+++ b/util/fuser_conf.h
@@ -0,0 +1,52 @@
+/*
+ * 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);
+
+#endif /* FUSER_CONF_H_ */
diff --git a/util/fuser_conf.c b/util/fuser_conf.c
new file mode 100644
index 00000000000000..dedb017aecfc45
--- /dev/null
+++ b/util/fuser_conf.c
@@ -0,0 +1,354 @@
+/*
+ * 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 <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;
+}
diff --git a/util/fusermount.c b/util/fusermount.c
index 68370468140a59..e1396b02560d69 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)
@@ -1096,7 +861,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 +890,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 +924,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 +970,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'],