]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: implement ro[=vfs,fs]
authorKarel Zak <kzak@redhat.com>
Tue, 29 Oct 2024 14:56:31 +0000 (15:56 +0100)
committerKarel Zak <kzak@redhat.com>
Tue, 29 Oct 2024 15:04:20 +0000 (16:04 +0100)
The new mount API allows for specifying whether to use a read-only
setting on the VFS or FS. Let's export this feature to the mount(8)
command line and fstab by adding new optional arguments "fs" and "vfs"
for the readonly mount flags. If nothing is specified, then the
default will be to mount as read-only on both layers for a normal
mount.

 # mount -o ro=vfs /dev/sdc /mnt/test
 ...
 fsopen("ext4", FSOPEN_CLOEXEC)          = 3
 fsconfig(3, FSCONFIG_SET_STRING, "source", "/dev/sdc", 0) = 0
 fsconfig(3, FSCONFIG_CMD_CREATE, NULL, NULL, 0) = 0
 mount_setattr(4, "", AT_EMPTY_PATH, {attr_set=MOUNT_ATTR_RDONLY, attr_clr=0, propagation=0 /* MS_??? */, userns_fd=0}, 32) = 0
 move_mount(4, "", AT_FDCWD, "/mnt/test", MOVE_MOUNT_F_EMPTY_PATH) = 0

 # mount -o ro=fs /dev/sdc /mnt/test
 ...
 fsopen("ext4", FSOPEN_CLOEXEC)          = 3
 fsconfig(3, FSCONFIG_SET_STRING, "source", "/dev/sdc", 0) = 0
 fsconfig(3, FSCONFIG_SET_FLAG, "ro", NULL, 0) = 0
 fsconfig(3, FSCONFIG_CMD_CREATE, NULL, NULL, 0) = 0
 move_mount(4, "", AT_FDCWD, "/mnt/test", MOVE_MOUNT_F_EMPTY_PATH) = 0

 # mount -o ro /dev/sdc /mnt/test
 ...
 fsopen("ext4", FSOPEN_CLOEXEC)          = 3
 fsconfig(3, FSCONFIG_SET_STRING, "source", "/dev/sdc", 0) = 0
 fsconfig(3, FSCONFIG_SET_FLAG, "ro", NULL, 0) = 0
 fsconfig(3, FSCONFIG_CMD_CREATE, NULL, NULL, 0) = 0
 mount_setattr(4, "", AT_EMPTY_PATH, {attr_set=MOUNT_ATTR_RDONLY, attr_clr=0, propagation=0 /* MS_??? */, userns_fd=0}, 32) = 0
 move_mount(4, "", AT_FDCWD, "/mnt/test", MOVE_MOUNT_F_EMPTY_PATH) = 0

The patch improves also the mount(8) man page to provide clearer
information about VFS.

Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/src/hook_mount.c
libmount/src/mountP.h
libmount/src/optlist.c
sys-utils/mount.8.adoc

index 736160f0e4888835b0e1064958066595e027d350..7622cc86395a5c5a77f7bb06cd15d240704872c8 100644 (file)
@@ -214,6 +214,10 @@ static int configure_superblock(struct libmnt_context *cxt,
                const int is_linux = ent && mnt_opt_get_map(opt) == cxt->map_linux;
 
                if (is_linux && ent->id == MS_RDONLY) {
+                       /* ignore if specified as "ro=vfs" */
+                       if (mnt_opt_value_with(opt, "vfs")
+                           && !mnt_opt_value_with(opt, "fs"))
+                               continue;
                        /* Use ro/rw for superblock (for backward compatibility) */
                        value = NULL;
                        has_rwro = 1;
index b54f6725920a45367e415aa14002d33d74915b82..893f9e72947a3a10d027df778ebe16b025f79fc7 100644 (file)
@@ -588,6 +588,7 @@ extern int mnt_opt_set_u64value(struct libmnt_opt *opt, uint64_t num);
 extern int mnt_opt_set_quoted_value(struct libmnt_opt *opt, const char *str);
 extern int mnt_opt_is_external(struct libmnt_opt *opt);
 extern int mnt_opt_is_sepnodata(struct libmnt_opt *opt);
+extern int mnt_opt_value_with(struct libmnt_opt *opt, const char *str);
 
 /* fs.c */
 extern int mnt_fs_follow_optlist(struct libmnt_fs *fs, struct libmnt_optlist *ol);
index 2ba294929a731669245a15b81f6cef1bec35942e..4ca3ef4b41047c96d619c9e9c3013884a0282b39 100644 (file)
@@ -481,7 +481,7 @@ static struct libmnt_opt *optlist_new_opt(struct libmnt_optlist *ls,
        }
 #ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT
        if (!opt->recursive && opt->value
-           && is_vfs_opt(opt) && strcmp(opt->value, "recursive") == 0)
+           && is_vfs_opt(opt) && mnt_opt_value_with(opt, "recursive"))
                opt->recursive = 1;
 #endif
        if (ent && map) {
@@ -876,6 +876,10 @@ int mnt_optlist_get_attrs(struct libmnt_optlist *ls, uint64_t *set, uint64_t *cl
                if (flag_to_attr( opt->ent->id, &x) < 0)
                        continue;
 
+               if (x == MOUNT_ATTR_RDONLY && !mnt_opt_value_with(opt, "vfs")
+                                          && mnt_opt_value_with(opt, "fs"))
+                       continue;
+
                if (x && remount_reset)
                        remount_reset &= ~x;
 
@@ -964,6 +968,17 @@ int mnt_optlist_strdup_optstr(struct libmnt_optlist *ls, char **optstr,
                if (!opt->name)
                        continue;
                if (opt->map == ls->linux_map && opt->ent->id == MS_RDONLY) {
+                       /* "ro=fs" -- ignore for VFS (linux map wanted) */
+                       if (map == ls->linux_map
+                           && !mnt_opt_value_with(opt, "vfs")
+                           && mnt_opt_value_with(opt, "fs"))
+                               continue;
+                       /* "ro=vfs" -- ignore for FS */
+                       if (what == MNT_OL_FLTR_UNKNOWN
+                           && !mnt_opt_value_with(opt, "fs")
+                           && mnt_opt_value_with(opt, "vfs"))
+                               continue;
+
                        is_rdonly = opt->ent->mask & MNT_INVERT ? 0 : 1;
                        continue;
                }
@@ -1145,6 +1160,34 @@ const char *mnt_opt_get_value(struct libmnt_opt *opt)
        return opt->value;
 }
 
+/* check if option value is @str or comma separated @str */
+int mnt_opt_value_with(struct libmnt_opt *opt, const char *str)
+{
+       char *p;
+       const char *start = opt->value;
+       size_t len;
+
+       if (!str || !opt->value || !*opt->value)
+               return 0;
+
+       len = strlen(str);
+
+       while (start && *start) {
+               p = strstr(start, str);
+               if (!p)
+                       return 0;
+               start = p + len;
+
+               if (p > opt->value && *(p - 1) != ',')
+                       continue;
+               len = strlen(str);
+               if (*(p + len) != '\0' && *(p + len) != ',')
+                       continue;
+               return 1;
+       }
+       return 0;
+}
+
 const char *mnt_opt_get_name(struct libmnt_opt *opt)
 {
        return opt->name;
@@ -1167,7 +1210,7 @@ int mnt_opt_set_value(struct libmnt_opt *opt, const char *str)
        opt->recursive = 0;
        rc = strdup_to_struct_member(opt, value, str);
 
-       if (rc == 0 && str && strcmp(str, "recursive") == 0)
+       if (rc == 0 && mnt_opt_value_with(opt, "recursive"))
                opt->recursive = 1;
        return rc;
 }
@@ -1454,6 +1497,32 @@ done:
        return rc;
 }
 
+static int test_value_with(struct libmnt_test *ts __attribute__((unused)),
+               int argc, char *argv[])
+{
+       struct libmnt_optlist *ol;
+       struct libmnt_opt *opt;
+       int rc;
+
+       if (argc != 4)
+               return -EINVAL;
+
+       rc = mk_optlist(&ol, argv[1]);
+       if (rc)
+               goto done;
+
+       opt = mnt_optlist_get_named(ol, argv[2], NULL);
+       if (!opt)
+               warnx("not found '%s' option", argv[2]);
+       else if (mnt_opt_value_with(opt, argv[3]))
+               printf("found\n");
+       else
+               printf("'%s' not found within '%s'\n", argv[3], mnt_opt_get_value(opt));
+
+done:
+       mnt_unref_optlist(ol);
+       return rc;
+}
 
 int main(int argc, char *argv[])
 {
@@ -1466,6 +1535,7 @@ int main(int argc, char *argv[])
                { "--get-str",     test_get_str,     "<list> [linux|user]        all options in string" },
                { "--get-flg",     test_get_flg,     "<list>  linux|user         all options by flags" },
                { "--split",       test_split,       "<list>                     split options into key-value pairs"},
+               { "--value-with",  test_value_with,  "<list> <opt> <str>        check if option value contains string"},
 
                { NULL }
        };
index 15b70cc4f4a47bb5704a4006a6cbe43802f08317..f25c1c488a53f5db76d828e8ae379a8aea42d0ef 100644 (file)
@@ -468,37 +468,67 @@ include::man-common/help-version.adoc[]
 
 Some of these options are only useful when they appear in the _/etc/fstab_ file.
 
-Some of these options could be enabled or disabled by default in the system kernel. To check the current setting see the options in _/proc/mounts_. Note that filesystems also have per-filesystem specific default mount options (see for example *tune2fs -l* output for ext__N__ filesystems).
-
-The options *nosuid*, *noexec*, *nodiratime*, *relatime*, *noatime*, *strictatime*, and *nosymfollow* are interpreted only by the abstract VFS kernel layer and applied to the mountpoint node rather than to the filesystem itself. Try:  
+Some of these options could be enabled or disabled by default in the system
+kernel. To check the current setting see the options in _/proc/mounts_. Note
+that filesystems also have per-filesystem specific default mount options (see
+for example *tune2fs -l* output for ext__N__ filesystems).
+
+=== Virtual Filesystem Notes
+
+The Virtual File System (VFS) is the abstract layer in the kernel that provides the
+filesystem interface to userspace programs. It also provides an abstraction within the
+kernel which allows different filesystem implementations to coexist. Some of the mount
+options only apply to this layer.
+
+The options *nosuid*, *noexec*, *nodiratime*, *relatime*, *noatime*,
+*strictatime*, and *nosymfollow* are interpreted only by the
+virtual-filesystem kernel layer and are applied to the mountpoint node rather
+than to the filesystem itself. To get a complete overview of filesystems and VFS options, try:
 ____
 
  findmnt -o TARGET,VFS-OPTIONS,FS-OPTIONS
 ____
 
-to get a complete overview of filesystems and VFS options.
-
-The read-only setting (*ro* or *rw*) is interpreted by VFS and the filesystem
-and depends on how the option is specified on the *mount*(8) command line. The
-default is to interpret it on the filesystem level. The operation "-o bind,remount,ro"
-is applied only to the VFS mountpoint, and operation "-o remount,ro" is applied to
-VFS and filesystem superblock. This semantic allows create a read-only
-mountpoint but keeps the filesystem writable from another mountpoint.
-
-Since v2.39 libmount can use a new kernel mount interface to set the VFS
-options recursive. For backward compatibility, this feature is not enabled by
-default, although recursive operation (e.g. rbind) has been requested. The new
-option argument "recursive" could be specified, for example:
+Since v2.39, libmount can use a new kernel mount interface to set the VFS
+attributes recursively. For backward compatibility, this feature is not enabled by
+default, even if recursive operation (e.g. rbind) has been requested. The new
+option argument "recursive" can be specified, for example:
 ____
 
  mount -orbind,ro=recursive,noexec=recursive,nosuid /foo /bar
 ____
 
-recursively binds filesystems from /foo to /bar, /bar, and all submounts will
-be read-only and noexec, but only /bar itself will be "nosuid". The "recursive"
-optional argument for VFS mount options is an EXPERIMENTAL feature. 
+This recursively binds filesystems from /foo to /bar, making /bar and all submounts
+read-only and noexec, but only /bar itself will be "nosuid". The "recursive"
+optional argument for VFS mount options is an EXPERIMENTAL feature.
+
+=== Read-only Setting Notes
 
-The following options apply to any filesystem that is being mounted (but not every filesystem actually honors them - e.g., the *sync* option today has an effect only for ext2, ext3, ext4, fat, vfat, ufs and xfs):
+The read-only setting (*ro* or *rw*) is interpreted by the virtual-filesystem
+and the filesystem, and it depends on how the option is specified on the
+*mount*(8) command line. For backward compatibility, the default is to use it
+for both layers during standard mount operations.
+
+The operation "-o bind,remount,ro" is applied only to the VFS mountpoint, while
+the operation "-o remount,ro" is applied to both the VFS and filesystem superblock.
+This semantic allows for the creation of a read-only mountpoint while keeping the
+filesystem writable from another mountpoint.
+
+Since version 2.41, libmount has the ability to use optional arguments _vfs_
+and _fs_ (e.g. ro=fs) to specify where the read-only setting should be applied.
+For example, using the command:
+____
+mount -o ro=vfs /dev/sdc1 /A
+____
+will mount the filesystem as read-write on the superblock level, but the /A
+node will be set as read-only. In previous versions, this required an additional "-o
+bind,remount,ro" operation to achieve the same result.
+
+=== Generic Mount Options
+
+The following options apply to any filesystem that is being mounted, but not every
+filesystem actually honors them. For example, the *sync* option only has an effect
+on ext2, ext3, ext4, fat, vfat, ufs, and xfs filesystems.
 
 *async*::
 All I/O to the filesystem should be done asynchronously. (See also the *sync* option.)
@@ -661,10 +691,19 @@ After this call, *mount* reads _fstab_ and merges these options with the options
 +
 remounts all already mounted vfat filesystems in read-only mode. Each of the filesystems is remounted by *mount -o remount,ro* _/dir_ semantic. This means the *mount* command reads _fstab_ or _mtab_ and merges these options with the options from the command line.
 
-*ro*::
-Mount the filesystem read-only.
+*ro*[=_recursive_,_vfs_,_fs_]::
+Mount the filesystem read-only. The optional argument is an experimental feature supported only by
+the file-descriptor based kernel mount API and it is silently ignored for the old *mount*(2) syscall.
++
+The _recursive_ argument forces the VFS attribute to be applied recursively.
++
+The _vfs_ and _fs_ arguments specify the layer where the read-only flag should be applied. The _fs_ specifies
+the filesystem superblock (unique filesystem instance in the kernel), and _vfs_ specifies the mount node. If no attribute is specified, then
+both layers are set to read-only.
++
+For more details, please refer to the *Read-only Setting Notes* section.
 
-*rw*::
+*rw*[=_recursive_,_vfs_,_fs_]::
 Mount the filesystem read-write.
 
 *sync*::