From: Lennart Poettering Date: Wed, 20 Oct 2021 20:57:22 +0000 (+0200) Subject: mount-util: use modern mount_setattr() syscall for bind_remount_one_with_mountinfo() X-Git-Tag: v250-rc1~435^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4f5644dba69b1376edc22a389c2140c280cbfd76;p=thirdparty%2Fsystemd.git mount-util: use modern mount_setattr() syscall for bind_remount_one_with_mountinfo() New kernels have a nice syscall for changing bind mount flags. Let's use it. This makes the complex libmount based iteration logic unnecessary. --- diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h index 5e80fa79fd0..2f67adaec3f 100644 --- a/src/basic/missing_syscall.h +++ b/src/basic/missing_syscall.h @@ -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, diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c index a7217adfa1a..25dde7b0e28 100644 --- a/src/shared/mount-util.c +++ b/src/shared/mount-util.c @@ -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(); diff --git a/src/test/test-mount-util.c b/src/test/test-mount-util.c index d3d004071bb..978fd45005d 100644 --- a/src/test/test-mount-util.c +++ b/src/test/test-mount-util.c @@ -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);