]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
mount-util: use modern mount_setattr() syscall for bind_remount_one_with_mountinfo()
authorLennart Poettering <lennart@poettering.net>
Wed, 20 Oct 2021 20:57:22 +0000 (22:57 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 25 Oct 2021 08:41:26 +0000 (10:41 +0200)
New kernels have a nice syscall for changing bind mount flags. Let's use
it. This makes the complex libmount based iteration logic unnecessary.

src/basic/missing_syscall.h
src/shared/mount-util.c
src/test/test-mount-util.c

index 5e80fa79fd0bf97e8318865930cc9bb2f27a9a06..2f67adaec3ff009829d0d294aadeedecfc0e3266 100644 (file)
@@ -465,10 +465,18 @@ struct mount_attr;
 #define MOUNT_ATTR_IDMAP 0x00100000
 #endif
 
+#ifndef MOUNT_ATTR_NOSYMFOLLOW
+#define MOUNT_ATTR_NOSYMFOLLOW 0x00200000
+#endif
+
 #ifndef AT_RECURSIVE
 #define AT_RECURSIVE 0x8000
 #endif
 
+#ifndef MOUNT_ATTR_SIZE_VER0
+#define MOUNT_ATTR_SIZE_VER0 32
+#endif
+
 static inline int missing_mount_setattr(
                 int dfd,
                 const char *path,
index a7217adfa1a23647252be648702061fcdc854dad..25dde7b0e286471ad9ba2545d1f8ca89f72af034 100644 (file)
@@ -134,6 +134,31 @@ int umount_recursive(const char *prefix, int flags) {
         return n;
 }
 
+#define MS_CONVERTIBLE_FLAGS (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_NOSYMFOLLOW)
+
+static uint64_t ms_flags_to_mount_attr(unsigned long a) {
+        uint64_t f = 0;
+
+        if (FLAGS_SET(a, MS_RDONLY))
+                f |= MOUNT_ATTR_RDONLY;
+
+        if (FLAGS_SET(a, MS_NOSUID))
+                f |= MOUNT_ATTR_NOSUID;
+
+        if (FLAGS_SET(a, MS_NODEV))
+                f |= MOUNT_ATTR_NODEV;
+
+        if (FLAGS_SET(a, MS_NOEXEC))
+                f |= MOUNT_ATTR_NOEXEC;
+
+        if (FLAGS_SET(a, MS_NOSYMFOLLOW))
+                f |= MOUNT_ATTR_NOSYMFOLLOW;
+
+        return f;
+}
+
+static bool skip_mount_set_attr = false;
+
 /* Use this function only if you do not have direct access to /proc/self/mountinfo but the caller can open it
  * for you. This is the case when /proc is masked or not mounted. Otherwise, use bind_remount_recursive. */
 int bind_remount_recursive_with_mountinfo(
@@ -374,6 +399,23 @@ int bind_remount_one_with_mountinfo(
         assert(path);
         assert(proc_self_mountinfo);
 
+        if ((flags_mask & ~MS_CONVERTIBLE_FLAGS) == 0 && !skip_mount_set_attr) {
+                /* Let's take a shortcut for all the flags we know how to convert into mount_setattr() flags */
+
+                if (mount_setattr(AT_FDCWD, path, AT_SYMLINK_NOFOLLOW,
+                                  &(struct mount_attr) {
+                                          .attr_set = ms_flags_to_mount_attr(new_flags & flags_mask),
+                                          .attr_clr = ms_flags_to_mount_attr(~new_flags & flags_mask),
+                                  }, MOUNT_ATTR_SIZE_VER0) < 0) {
+
+                        log_debug_errno(errno, "mount_setattr() didn't work, falling back to classic remounting: %m");
+
+                        if (ERRNO_IS_NOT_SUPPORTED(errno)) /* if not supported, then don't bother at all anymore */
+                                skip_mount_set_attr = true;
+                } else
+                        return 0; /* Nice, this worked! */
+        }
+
         rewind(proc_self_mountinfo);
 
         table = mnt_new_table();
index d3d004071bb92b626fab54412cc4650812c7bece..978fd45005dd55417ab21adeb1293d93644f3acb 100644 (file)
@@ -209,6 +209,7 @@ static void test_bind_remount_one(void) {
                 assert_se(fopen_unlocked("/proc/self/mountinfo", "re", &proc_self_mountinfo) >= 0);
 
                 assert_se(bind_remount_one_with_mountinfo("/run", MS_RDONLY, MS_RDONLY, proc_self_mountinfo) >= 0);
+                assert_se(bind_remount_one_with_mountinfo("/run", MS_NOEXEC, MS_RDONLY|MS_NOEXEC, proc_self_mountinfo) >= 0);
                 assert_se(bind_remount_one_with_mountinfo("/proc/idontexist", MS_RDONLY, MS_RDONLY, proc_self_mountinfo) == -ENOENT);
                 assert_se(bind_remount_one_with_mountinfo("/proc/self", MS_RDONLY, MS_RDONLY, proc_self_mountinfo) == -EINVAL);
                 assert_se(bind_remount_one_with_mountinfo("/", MS_RDONLY, MS_RDONLY, proc_self_mountinfo) >= 0);