diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2023-09-19 12:49:52 +0200 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2023-09-19 12:49:52 +0200 |
commit | 519d04c265dadbaf8c742e3af3141b7caf673412 (patch) | |
tree | 3d6b4078d1f998ea96e654313e9033410cb5a9a4 | |
parent | 6ece52ce99c69f998a436fbd735a0dafe1391eeb (diff) | |
download | vfs-statmount.tar.gz |
add listmount(2) syscallstatmount
Add way to query the children of a particular mount. This is a more
flexible way to iterate the mount tree than having to parse the complete
/proc/self/mountinfo.
Lookup the mount by the new 64bit mount ID. If a mount
needs to be queried based on path, then statx(2) can be used to first query
the mount ID belonging to the path.
Return an array of new (64bit) mount ID's. Without privileges only mounts
are listed which are reachable from the task's root.
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r-- | arch/x86/entry/syscalls/syscall_64.tbl | 1 | ||||
-rw-r--r-- | fs/namespace.c | 57 | ||||
-rw-r--r-- | include/linux/syscalls.h | 2 | ||||
-rw-r--r-- | include/uapi/asm-generic/unistd.h | 5 | ||||
-rw-r--r-- | include/uapi/linux/mount.h | 3 |
5 files changed, 67 insertions, 1 deletions
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl index 7312c440978fb..a1b3ce7d22cc5 100644 --- a/arch/x86/entry/syscalls/syscall_64.tbl +++ b/arch/x86/entry/syscalls/syscall_64.tbl @@ -376,6 +376,7 @@ 452 common fchmodat2 sys_fchmodat2 453 64 map_shadow_stack sys_map_shadow_stack 454 common statmount sys_statmount +455 common listmount sys_listmount # # Due to a historical design error, certain syscalls are numbered differently diff --git a/fs/namespace.c b/fs/namespace.c index 8ebd74cbfe1b3..803003052bfbe 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -4948,6 +4948,63 @@ SYSCALL_DEFINE5(statmount, u64, mnt_id, return err; } +static long do_listmount(struct vfsmount *mnt, u64 __user *buf, size_t bufsize, + const struct path *root, unsigned int flags) +{ + struct mount *r, *m = real_mount(mnt); + struct path rootmnt = { .mnt = root->mnt, .dentry = root->mnt->mnt_root }; + long ctr = 0; + bool reachable_only = true; + + if (flags & LISTMOUNT_UNREACHABLE) { + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reachable_only = false; + } + + if (reachable_only && !is_path_reachable(m, mnt->mnt_root, &rootmnt)) + return capable(CAP_SYS_ADMIN) ? 0 : -EPERM; + + list_for_each_entry(r, &m->mnt_mounts, mnt_child) { + if (reachable_only && + !is_path_reachable(r, r->mnt.mnt_root, root)) + continue; + + if (ctr >= bufsize) + return -EOVERFLOW; + if (put_user(r->mnt_id_unique, buf + ctr)) + return -EFAULT; + ctr++; + if (ctr < 0) + return -ERANGE; + } + return ctr; +} + +SYSCALL_DEFINE4(listmount, u64, mnt_id, u64 __user *, buf, size_t, bufsize, + unsigned int, flags) +{ + struct vfsmount *mnt; + struct path root; + long err; + + if (flags & ~LISTMOUNT_UNREACHABLE) + return -EINVAL; + + down_read(&namespace_sem); + mnt = lookup_mnt_in_ns(mnt_id, current->nsproxy->mnt_ns); + err = -ENOENT; + if (mnt) { + get_fs_root(current->fs, &root); + err = do_listmount(mnt, buf, bufsize, &root, flags); + path_put(&root); + } + up_read(&namespace_sem); + + return err; +} + + static void __init init_mount_tree(void) { struct vfsmount *mnt; diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index e9a50333c037a..a35fb7b2c8420 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -412,6 +412,8 @@ asmlinkage long sys_fstatfs64(unsigned int fd, size_t sz, asmlinkage long sys_statmount(u64 mnt_id, u64 mask, struct statmnt __user *buf, size_t bufsize, unsigned int flags); +asmlinkage long sys_listmount(u64 mnt_id, u64 __user *buf, size_t bufsize, + unsigned int flags); asmlinkage long sys_truncate(const char __user *path, long length); asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length); #if BITS_PER_LONG == 32 diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h index 8f034e934a2e4..8df6a747e21aa 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -826,8 +826,11 @@ __SYSCALL(__NR_fchmodat2, sys_fchmodat2) #define __NR_statmount 454 __SYSCALL(__NR_statmount, sys_statmount) +#define __NR_listmount 455 +__SYSCALL(__NR_listmount, sys_listmount) + #undef __NR_syscalls -#define __NR_syscalls 455 +#define __NR_syscalls 456 /* * 32 bit systems traditionally used different diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h index f457427ee249a..d98b41024507f 100644 --- a/include/uapi/linux/mount.h +++ b/include/uapi/linux/mount.h @@ -167,4 +167,7 @@ struct statmnt { #define STMT_MOUNTPOINT 0x00000010U /* Want/got mountpoint */ #define STMT_FS_TYPE 0x00000020U /* Want/got fs_type */ +/* listmount(2) flags */ +#define LISTMOUNT_UNREACHABLE 0x01 /* List unreachable mounts too */ + #endif /* _UAPI_LINUX_MOUNT_H */ |