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
3232 #include <linux/fs_context.h>
3333 #include <linux/shmem_fs.h>
3434 #include <linux/mnt_idmapping.h>
35+#include <linux/nospec.h>
3536
3637 #include "pnode.h"
3738 #include "internal.h"
@@ -1009,7 +1010,7 @@ void mnt_change_mountpoint(struct mount *parent, struct mountpoint *mp, struct m · 971 unmodified lines
10091010
10101011 static inline struct mount *node_to_mount(struct rb_node *node)
10111012 {
1012− return rb_entry(node, struct mount, mnt_node);
1013+ return node ? rb_entry(node, struct mount, mnt_node) : NULL;
10131014 }
10141015
10151016 static 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
49454946 return -EFAULT;
49464947
49474948 memset(ks, 0, sizeof(*ks));
4948− ks->mask = kreq->request_mask;
4949+ ks->mask = kreq->param;
49494950 ks->buf = buf;
49504951 ks->bufsize = bufsize;
49514952 ks->seq.size = seq_size;
@@ -4999,6 +5000,87 @@ retry: · 47 unmodified lines
49995000 return ret;
50005001 }
50015002
5003+static struct mount *listmnt_next(struct mount *curr)
5004+{
5005+ return node_to_mount(rb_next(&curr->mnt_node));
5006+}
5007+
5008+static 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+
5042+SYSCALL_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);
5077+err:
5078+ path_put(&root);
5079+ up_read(&namespace_sem);
5080+ return ret;
5081+}
5082+
5083+
50025084 static void __init init_mount_tree(void)
50035085 {
50045086 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
412412 asmlinkage long sys_statmount(const struct mnt_id_req __user *req,
413413 struct statmount __user *buf, size_t bufsize,
414414 unsigned int flags);
415+asmlinkage long sys_listmount(const struct mnt_id_req __user *req,
416+ u64 __user *buf, size_t bufsize,
417+ unsigned int flags);
415418 asmlinkage long sys_truncate(const char __user *path, long length);
416419 asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length);
417420 #if BITS_PER_LONG == 32
include/uapi/linux/mount.h+13−1
@@ -176,9 +176,16 @@ struct statmount { · 175 unmodified lines
176176 char str[]; /* Variable size part containing strings */
177177 };
178178
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+ */
179186 struct mnt_id_req {
180187 __u64 mnt_id;
181− __u64 request_mask;
188+ __u64 param;
182189 };
183190
184191 /*
@@ -191,4 +198,9 @@ struct mnt_id_req { · 6 unmodified lines
191198 #define STATMOUNT_MNT_POINT 0x00000010U /* Want/got mnt_point */
192199 #define STATMOUNT_FS_TYPE 0x00000020U /* Want/got fs_type */
193200
201+/*
202+ * Special @mnt_id values that can be passed to listmount
203+ */
204+#define LSMT_ROOT 0xffffffffffffffff /* root mount */
205+
194206 #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