History

add listmount(2) syscall

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 /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. Folded into this patch are several later improvements. Keeping them separate would make the history pointlessly confusing: * Recursive listing of mounts is the default now (cf. [1]). * Remove explicit LISTMOUNT_UNREACHABLE flag (cf. [1]) and fail if mount is unreachable from current root. This also makes permission checking consistent with statmount() (cf. [3]). * Start listing mounts in unique mount ID order (cf. [2]) to allow continuing listmount() from a midpoint. * Allow to continue listmount(). The @request_mask parameter is renamed and to @param to be usable by both statmount() and listmount(). If @param is set to a mount id then listmount() will continue listing mounts from that id on. This allows listing mounts in multiple listmount invocations without having to resize the buffer. If @param is zero then the listing starts from the beginning (cf. [4]). * Don't return EOVERFLOW, instead return the buffer size which allows to detect a full buffer as well (cf. [4]).

Miklos Szeredi committed 3 years ago

Commit b4c2bea1 parent 68385d73 files · +100 −3Browse files

Unified · Split

3 files
  1. fs/namespace.c +84 −2
  2. include/linux/syscalls.h +3 −0
  3. include/uapi/linux/mount.h +13 −1
fs/namespace.c+84−2
@@ -32,6 +32,7 @@ · 31 unmodified lines
32#include <linux/fs_context.h>32#include <linux/fs_context.h>
33#include <linux/shmem_fs.h>33#include <linux/shmem_fs.h>
34#include <linux/mnt_idmapping.h>34#include <linux/mnt_idmapping.h>
35#include <linux/nospec.h>
35 36
36#include "pnode.h"37#include "pnode.h"
37#include "internal.h"38#include "internal.h"
@@ -1009,7 +1010,7 @@ void mnt_change_mountpoint(struct mount *parent, struct mountpoint *mp, struct m · 971 unmodified lines
1009 1010
1010static inline struct mount *node_to_mount(struct rb_node *node)1011static inline struct mount *node_to_mount(struct rb_node *node)
1011{1012{
1012 return rb_entry(node, struct mount, mnt_node);1013 return node ? rb_entry(node, struct mount, mnt_node) : NULL;
1013}1014}
1014 1015
1015static void mnt_add_to_ns(struct mnt_namespace *ns, struct mount *mnt)1016static void mnt_add_to_ns(struct mnt_namespace *ns, struct mount *mnt)
@@ -4945,7 +4946,7 @@ static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq, · 3929 unmodified lines
4945 return -EFAULT;4946 return -EFAULT;
4946 4947
4947 memset(ks, 0, sizeof(*ks));4948 memset(ks, 0, sizeof(*ks));
4948 ks->mask = kreq->request_mask;4949 ks->mask = kreq->param;
4949 ks->buf = buf;4950 ks->buf = buf;
4950 ks->bufsize = bufsize;4951 ks->bufsize = bufsize;
4951 ks->seq.size = seq_size;4952 ks->seq.size = seq_size;
@@ -4999,6 +5000,87 @@ retry: · 47 unmodified lines
4999 return ret;5000 return ret;
5000}5001}
5001 5002
5003static struct mount *listmnt_next(struct mount *curr)
5004{
5005 return node_to_mount(rb_next(&curr->mnt_node));
5006}
5007
5008static ssize_t do_listmount(struct mount *first, struct path *orig, u64 mnt_id,
5009 u64 __user *buf, size_t bufsize,
5010 const struct path *root)
5011{
5012 struct mount *r;
5013 ssize_t ctr;
5014 int err;
5015
5016 /*
5017 * Don't trigger audit denials. We just want to determine what
5018 * mounts to show users.
5019 */
5020 if (!is_path_reachable(real_mount(orig->mnt), orig->dentry, root) &&
5021 !ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN))
5022 return -EPERM;
5023
5024 err = security_sb_statfs(orig->dentry);
5025 if (err)
5026 return err;
5027
5028 for (ctr = 0, r = first; r && ctr < bufsize; r = listmnt_next(r)) {
5029 if (r->mnt_id_unique == mnt_id)
5030 continue;
5031 if (!is_path_reachable(r, r->mnt.mnt_root, orig))
5032 continue;
5033 ctr = array_index_nospec(ctr, bufsize);
5034 if (put_user(r->mnt_id_unique, buf + ctr))
5035 return -EFAULT;
5036 if (check_add_overflow(ctr, 1, &ctr))
5037 return -ERANGE;
5038 }
5039 return ctr;
5040}
5041
5042SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
5043 u64 __user *, buf, size_t, bufsize, unsigned int, flags)
5044{
5045 struct mnt_namespace *ns = current->nsproxy->mnt_ns;
5046 struct mnt_id_req kreq;
5047 struct mount *first;
5048 struct path root, orig;
5049 u64 mnt_id, last_mnt_id;
5050 ssize_t ret;
5051
5052 if (flags)
5053 return -EINVAL;
5054
5055 if (copy_from_user(&kreq, req, sizeof(kreq)))
5056 return -EFAULT;
5057 mnt_id = kreq.mnt_id;
5058 last_mnt_id = kreq.param;
5059
5060 down_read(&namespace_sem);
5061 get_fs_root(current->fs, &root);
5062 if (mnt_id == LSMT_ROOT) {
5063 orig = root;
5064 } else {
5065 ret = -ENOENT;
5066 orig.mnt = lookup_mnt_in_ns(mnt_id, ns);
5067 if (!orig.mnt)
5068 goto err;
5069 orig.dentry = orig.mnt->mnt_root;
5070 }
5071 if (!last_mnt_id)
5072 first = node_to_mount(rb_first(&ns->mounts));
5073 else
5074 first = mnt_find_id_at(ns, last_mnt_id + 1);
5075
5076 ret = do_listmount(first, &orig, mnt_id, buf, bufsize, &root);
5077err:
5078 path_put(&root);
5079 up_read(&namespace_sem);
5080 return ret;
5081}
5082
5083
5002static void __init init_mount_tree(void)5084static void __init init_mount_tree(void)
5003{5085{
5004 struct vfsmount *mnt;5086 struct vfsmount *mnt;
include/linux/syscalls.h+3−0
@@ -412,6 +412,9 @@ asmlinkage long sys_fstatfs64(unsigned int fd, size_t sz, · 411 unmodified lines
412asmlinkage long sys_statmount(const struct mnt_id_req __user *req,412asmlinkage long sys_statmount(const struct mnt_id_req __user *req,
413 struct statmount __user *buf, size_t bufsize,413 struct statmount __user *buf, size_t bufsize,
414 unsigned int flags);414 unsigned int flags);
415asmlinkage long sys_listmount(const struct mnt_id_req __user *req,
416 u64 __user *buf, size_t bufsize,
417 unsigned int flags);
415asmlinkage long sys_truncate(const char __user *path, long length);418asmlinkage long sys_truncate(const char __user *path, long length);
416asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length);419asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length);
417#if BITS_PER_LONG == 32420#if BITS_PER_LONG == 32
include/uapi/linux/mount.h+13−1
@@ -176,9 +176,16 @@ struct statmount { · 175 unmodified lines
176 char str[]; /* Variable size part containing strings */176 char str[]; /* Variable size part containing strings */
177};177};
178 178
179/*
180 * Structure for passing mount ID and miscellaneous parameters to statmount(2)
181 * and listmount(2).
182 *
183 * For statmount(2) @param represents the request mask.
184 * For listmount(2) @param represents the last listed mount id (or zero).
185 */
179struct mnt_id_req {186struct mnt_id_req {
180 __u64 mnt_id;187 __u64 mnt_id;
181 __u64 request_mask;188 __u64 param;
182};189};
183 190
184/*191/*
@@ -191,4 +198,9 @@ struct mnt_id_req { · 6 unmodified lines
191#define STATMOUNT_MNT_POINT 0x00000010U /* Want/got mnt_point */198#define STATMOUNT_MNT_POINT 0x00000010U /* Want/got mnt_point */
192#define STATMOUNT_FS_TYPE 0x00000020U /* Want/got fs_type */199#define STATMOUNT_FS_TYPE 0x00000020U /* Want/got fs_type */
193 200
201/*
202 * Special @mnt_id values that can be passed to listmount
203 */
204#define LSMT_ROOT 0xffffffffffffffff /* root mount */
205
194#endif /* _UAPI_LINUX_MOUNT_H */206#endif /* _UAPI_LINUX_MOUNT_H */

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t