The MountOption enum allows the caller to provide arbitrary (UTF-8) strings as mount options. This can be a problem because such strings may contain special characters - comma, backslash, and NUL - that currently are handled in inconsistent ways.
For example, suppose that you specify MountOption::FSName("foo,rw".into()). In most cases, this will not be interpreted as "the name of the filesystem is foo,rw". Instead, it will be interpreted as two options "the name of the filesystem is foo" and "mount the filesystem read-write". This is bad.
In order to understand the issue, I've tried to dig through the various ways that MountOption may be used:
- Options may be handled by
fuser itself, and passed to the mount system call.
- Options may be passed to the
fusermount or fusermount3 executable, which passes them to the mount system call.
- Options may be passed to the
fuse_session_new function (fuse3). fuse_session_new may in turn call mount or may invoke fusermount3.
- Options may be passed to the
fuse_mount_compat25 function (fuse2). fuse_mount_compat25 may in turn call mount or may invoke fusermount.
I've ignored (for now) questions about how options are represented in /etc/fstab, /etc/mtab, and /proc/mounts. Those questions are somewhat tangential to fuser.
In Linux
The "source" of a FUSE mount can be any zero-terminated string. (In /proc/mounts, any special characters will be displayed as escape sequences.)
The "subtype" of a FUSE mount can likewise be any zero-terminated string.
Other mount options are passed to the kernel as a zero-terminated, comma-separated string, and therefore an option cannot contain NUL or comma.
(See generic_parse_monolithic in Linux's fs/fs_context.c.)
In fusermount and fusermount3
The behavior of fusermount and fusermount3 appears to be the same.
All mount options, including "fsname" and "subtype", must be passed as a single comma-separated command-line argument.
Arguments to the fsname and subtype options may use a backslash to escape the following character. For all other options, backslash is not special and comma marks the end of the option. Note that fusermount rejects unknown future options.
(See do_mount in FUSE's util/fusermount.c.)
In fuse_session_new (libfuse3)
All -o argument strings are decoded in the same way:
- Backslash followed by three octal digits (000 to 377) represents an arbitrary byte.
- Backslash followed by anything else represents a literal character.
- Otherwise, comma separates options.
(See process_real_option_group in FUSE's lib/fuse_opt.c.)
If the options are passed to fusermount3, they are re-encoded:
- The
fsname option is escaped using fuse_opt_add_opt_escaped.
- Other options, including
subtype, are not escaped.
(See fuse_mount_opt_proc and fuse_mount_opts in lib/mount.c. See fuse_kern_mount for how the results are used.)
In fuse_mount_compat25 (libfuse2)
I haven't tested it, but the behavior looks the same as libfuse3.
-
Decoding logic (process_real_option_group in lib/fuse_opt.c) looks the same.
-
Re-encoding (fuse_mount_opt_proc in lib/mount.c) looks the same except for some obsolete options that fuse3 doesn't support. In particular, it looks like fsname is re-escaped and subtype isn't.
What should fuser do?
fuser should not silently pass option strings that will not be reliably interpreted by the lower layers (libfuse, fusermount, and kernel.) Nor should the caller be expected to do its own escaping, because the rules are complicated and unpredictable.
I also don't think fuser ought to panic (as it currently does when an option contains a NUL.)
If the caller passes a mount option containing special characters, then fuser should escape the special characters if it can, or return an error if not.
Based on my notes above, it appears to me that (on Linux):
-
MountOption::FSName should be allowed to contain any characters except NUL. When the option is passed to fuse_session_new, fuse_mount_compat25, or to fusermount or fusermount3, the value should be escaped by replacing , with \, and \ with \\. When it's passed to the kernel directly, it should not be escaped.
-
MountOption::Subtype should not be allowed to contain NUL, comma, or backslash (because it's not possible to handle these consistently when using libfuse.) An invalid string should result in an error.
-
MountOption::CUSTOM should not be allowed to contain NUL or comma (because the ABI doesn't support them), or backslash (because libfuse will mangle them.)
An entirely separate question is whether the deprecated fuser::mount and fuser::spawn_mount should support libfuse-like option escaping. Probably these functions should be kept as they are.
BSD and MacOS?
If I'm reading correctly, fuse_mount_opt_proc (FUSE's lib/mount_bsd.c) doesn't appear to do any escaping at all, and mount_fusefs (FreeBSD's sbin/mount_fusefs/mount_fusefs.c) doesn't appear to support any escaping. So commas should be forbidden in all options. Backslashes seem dicey.
macFUSE is non-free so I leave it to those who care about it to figure out how to support it.
The MountOption enum allows the caller to provide arbitrary (UTF-8) strings as mount options. This can be a problem because such strings may contain special characters - comma, backslash, and NUL - that currently are handled in inconsistent ways.
For example, suppose that you specify
MountOption::FSName("foo,rw".into()). In most cases, this will not be interpreted as "the name of the filesystem isfoo,rw". Instead, it will be interpreted as two options "the name of the filesystem isfoo" and "mount the filesystem read-write". This is bad.In order to understand the issue, I've tried to dig through the various ways that MountOption may be used:
fuseritself, and passed to themountsystem call.fusermountorfusermount3executable, which passes them to themountsystem call.fuse_session_newfunction (fuse3).fuse_session_newmay in turn callmountor may invokefusermount3.fuse_mount_compat25function (fuse2).fuse_mount_compat25may in turn callmountor may invokefusermount.I've ignored (for now) questions about how options are represented in
/etc/fstab,/etc/mtab, and/proc/mounts. Those questions are somewhat tangential tofuser.In Linux
The "source" of a FUSE mount can be any zero-terminated string. (In
/proc/mounts, any special characters will be displayed as escape sequences.)The "subtype" of a FUSE mount can likewise be any zero-terminated string.
Other mount options are passed to the kernel as a zero-terminated, comma-separated string, and therefore an option cannot contain NUL or comma.
(See
generic_parse_monolithicin Linux'sfs/fs_context.c.)In fusermount and fusermount3
The behavior of fusermount and fusermount3 appears to be the same.
All mount options, including "fsname" and "subtype", must be passed as a single comma-separated command-line argument.
Arguments to the
fsnameandsubtypeoptions may use a backslash to escape the following character. For all other options, backslash is not special and comma marks the end of the option. Note that fusermount rejects unknown future options.(See
do_mountin FUSE'sutil/fusermount.c.)In fuse_session_new (libfuse3)
All
-oargument strings are decoded in the same way:(See
process_real_option_groupin FUSE'slib/fuse_opt.c.)If the options are passed to
fusermount3, they are re-encoded:fsnameoption is escaped usingfuse_opt_add_opt_escaped.subtype, are not escaped.(See
fuse_mount_opt_procandfuse_mount_optsinlib/mount.c. Seefuse_kern_mountfor how the results are used.)In fuse_mount_compat25 (libfuse2)
I haven't tested it, but the behavior looks the same as libfuse3.
Decoding logic (
process_real_option_groupinlib/fuse_opt.c) looks the same.Re-encoding (
fuse_mount_opt_procinlib/mount.c) looks the same except for some obsolete options that fuse3 doesn't support. In particular, it looks likefsnameis re-escaped andsubtypeisn't.What should fuser do?
fusershould not silently pass option strings that will not be reliably interpreted by the lower layers (libfuse, fusermount, and kernel.) Nor should the caller be expected to do its own escaping, because the rules are complicated and unpredictable.I also don't think
fuserought to panic (as it currently does when an option contains a NUL.)If the caller passes a mount option containing special characters, then
fusershould escape the special characters if it can, or return an error if not.Based on my notes above, it appears to me that (on Linux):
MountOption::FSNameshould be allowed to contain any characters except NUL. When the option is passed tofuse_session_new,fuse_mount_compat25, or tofusermountorfusermount3, the value should be escaped by replacing,with\,and\with\\. When it's passed to the kernel directly, it should not be escaped.MountOption::Subtypeshould not be allowed to contain NUL, comma, or backslash (because it's not possible to handle these consistently when using libfuse.) An invalid string should result in an error.MountOption::CUSTOMshould not be allowed to contain NUL or comma (because the ABI doesn't support them), or backslash (because libfuse will mangle them.)An entirely separate question is whether the deprecated
fuser::mountandfuser::spawn_mountshould support libfuse-like option escaping. Probably these functions should be kept as they are.BSD and MacOS?
If I'm reading correctly,
fuse_mount_opt_proc(FUSE'slib/mount_bsd.c) doesn't appear to do any escaping at all, andmount_fusefs(FreeBSD'ssbin/mount_fusefs/mount_fusefs.c) doesn't appear to support any escaping. So commas should be forbidden in all options. Backslashes seem dicey.macFUSE is non-free so I leave it to those who care about it to figure out how to support it.