As with openat2(2), this allows userspace to easily figure out what flags and fields are supported by mount_setattr(2). As with clone3(2), for fields which are not flag-based, we simply set every bit in the field so that a naive bitwise-and would show that any value of the field is valid.
The intended way of using this interface to get feature information looks something like the following:
static bool mountattr_nosymfollow_supported; static bool mountattr_idmap_supported;
int check_clone3_support(void) { int err; struct mount_attr attr = {};
err = mount_attr(-EBADF, "", 0, &args, CHECK_FIELDS | sizeof(args)); assert(err < 0); switch (errno) { case EFAULT: case E2BIG: /* Old kernel... */ check_support_the_old_way(); break; case EEXTSYS_NOOP: mountattr_nosymfollow_supported = ((attr.attr_clr | attr.attr_set) & MOUNT_ATTR_NOSYMFOLLOW); mountattr_idmap_supported = ((attr.attr_clr | attr.attr_set) & MOUNT_ATTR_IDMAP) && (attr.userns_fd != 0); break; } }
Signed-off-by: Aleksa Sarai cyphar@cyphar.com --- fs/namespace.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/fs/namespace.c b/fs/namespace.c index 328087a4df8a..c7ae8d96b7b7 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -4771,6 +4771,7 @@ SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path, size_t, usize) { int err; + bool check_fields; struct path target; struct mount_attr attr; struct mount_kattr kattr; @@ -4783,11 +4784,27 @@ SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path, AT_NO_AUTOMOUNT)) return -EINVAL;
+ check_fields = usize & CHECK_FIELDS; + usize &= ~CHECK_FIELDS; + if (unlikely(usize > PAGE_SIZE)) return -E2BIG; if (unlikely(usize < MOUNT_ATTR_SIZE_VER0)) return -EINVAL;
+ if (unlikely(check_fields)) { + memset(&attr, 0, sizeof(attr)); + attr = (struct mount_attr) { + .attr_set = MOUNT_SETATTR_VALID_FLAGS, + .attr_clr = MOUNT_SETATTR_VALID_FLAGS, + .propagation = MOUNT_SETATTR_PROPAGATION_FLAGS, + .userns_fd = 0xFFFFFFFFFFFFFFFF, + }; + + err = copy_struct_to_user(uattr, usize, &attr, sizeof(attr), NULL); + return err ?: -EEXTSYS_NOOP; + } + if (!may_mount()) return -EPERM;