@samitouri / QOSamiQemu / commits / c2959a8122

fuse: Fix mount options

Since I actually took a look into how mounting with libfuse works[1], I now know that the FUSE mount options are not exactly standard mount system call options. Specifically: - We should add "nosuid,nodev,noatime" because that is going to be translated into the respective MS_ mount flags; and those flags make sense for us. - We can set rw/ro to make the mount writable or not. It makes sense to set this flag to produce a better error message for read-only exports (EROFS instead of EACCES). This changes behavior as can be seen in iotest 308: It is no longer possible to modify metadata of read-only exports. Similarly, in fuse-allow-other, we must now make the export writable to use SETATTR. In addition, in the comment, we can note that the FUSE mount() system call actually expects some more parameters that we can omit because fusermount3 (i.e. libfuse) will figure them out by itself: - fd: /dev/fuse fd - rootmode: Inode mode of the root node - user_id/group_id: Mounter's UID/GID [1] It invokes fusermount3, an SUID libfuse helper program, which parses and processes some mount options before actually invoking the mount() system call. Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Hanna Czenczek <hreitz@redhat.com> Message-ID: <20260309150856.26800-8-hreitz@redhat.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>

Hanna Czenczek committed Mar 9, 2026 at 16:08 UTC c2959a81223ad05f9a469541e2aa0e2fcc7a8404
5 files changed +23 -10
block/export/fuse.c
+11 -3
@@ -246,10 +246,18 @@ static int mount_fuse_export(FuseExport *exp, Error **errp)
246 int ret;
247
248 /*
249 - * max_read needs to match what fuse_init() sets.
250 - * max_write need not be supplied.
249 + * Note that these mount options differ from what we would pass to a direct
250 + * mount() call:
251 + * - nosuid, nodev, and noatime are not understood by the kernel; libfuse
252 + * uses those options to construct the mount flags (MS_*)
253 + * - The FUSE kernel driver requires additional options (fd, rootmode,
254 + * user_id, group_id); these will be set by libfuse.
255 + * Note that max_read is set here, while max_write is set via the FUSE INIT
256 + * operation.
257 */
252 - mount_opts = g_strdup_printf("max_read=%zu,default_permissions%s",
258 + mount_opts = g_strdup_printf("%s,nosuid,nodev,noatime,max_read=%zu,"
259 + "default_permissions%s",
260 + exp->writable ? "rw" : "ro",
261 FUSE_MAX_BOUNCE_BYTES,
262 exp->allow_other ? ",allow_other" : "");
263
tests/qemu-iotests/308
+2 -2
@@ -178,7 +178,7 @@ stat -c 'Permissions pre-chmod: %a' "$EXT_MP"
178 chmod u+w "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt
179 stat -c 'Permissions post-+w: %a' "$EXT_MP"
180
181 -# But that we can set, say, +x (if we are so inclined)
181 +# Same for other flags, like, say +x
182 chmod u+x "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt
183 stat -c 'Permissions post-+x: %a' "$EXT_MP"
184
@@ -236,7 +236,7 @@ output=$($QEMU_IO -f raw -c 'write -P 42 1M 64k' "$TEST_IMG" 2>&1 \
236
237 # Expected reference output: Opening the file fails because it has no
238 # write permission
239 -reference="Could not open 'TEST_DIR/t.IMGFMT': Permission denied"
239 +reference="Could not open 'TEST_DIR/t.IMGFMT': Read-only file system"
240
241 if echo "$output" | grep -q "$reference"; then
242 echo "Writing to read-only export failed: OK"
tests/qemu-iotests/308.out
+2 -1
@@ -53,7 +53,8 @@ Images are identical.
53 Permissions pre-chmod: 400
54 chmod: changing permissions of 'TEST_DIR/t.IMGFMT.fuse': Read-only file system
55 Permissions post-+w: 400
56 -Permissions post-+x: 500
56 +chmod: changing permissions of 'TEST_DIR/t.IMGFMT.fuse': Read-only file system
57 +Permissions post-+x: 400
58
59 === Mount over existing file ===
60 {'execute': 'block-export-add',
tests/qemu-iotests/tests/fuse-allow-other
+2 -1
@@ -101,7 +101,8 @@ run_permission_test()
101
102 fuse_export_add 'export' \
103 "'mountpoint': '$EXT_MP',
104 - 'allow-other': '$1'"
104 + 'allow-other': '$1',
105 + 'writable': true"
106
107 # Should always work
108 echo '(Removing all permissions)'
tests/qemu-iotests/tests/fuse-allow-other.out
+6 -3
@@ -12,7 +12,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536
12 'id': 'export',
13 'node-name': 'node-format',
14 'mountpoint': 'TEST_DIR/fuse-export',
15 - 'allow-other': 'off'
15 + 'allow-other': 'off',
16 + 'writable': true
17 } }
18 {"return": {}}
19 (Removing all permissions)
@@ -41,7 +42,8 @@ stat: cannot statx 'fuse-export': Permission denied
42 'id': 'export',
43 'node-name': 'node-format',
44 'mountpoint': 'TEST_DIR/fuse-export',
44 - 'allow-other': 'on'
45 + 'allow-other': 'on',
46 + 'writable': true
47 } }
48 {"return": {}}
49 (Removing all permissions)
@@ -68,7 +70,8 @@ Permissions seen by nobody: 440
70 'id': 'export',
71 'node-name': 'node-format',
72 'mountpoint': 'TEST_DIR/fuse-export',
71 - 'allow-other': 'auto'
73 + 'allow-other': 'auto',
74 + 'writable': true
75 } }
76 {"return": {}}
77 (Removing all permissions)