History

listmount: allow listing in reverse order

util-linux is about to implement listmount() and statmount() support. Karel requested the ability to scan the mount table in backwards order because that's what libmount currently does in order to get the latest mount first. We currently don't support this in listmount(). Add a new LISTMOUNT_REVERSE flag to allow listing mounts in reverse order. For example, listing all child mounts of /sys without LISTMOUNT_REVERSE gives: /sys/kernel/security @ mnt_id: 4294968369 /sys/fs/cgroup @ mnt_id: 4294968370 /sys/firmware/efi/efivars @ mnt_id: 4294968371 /sys/fs/bpf @ mnt_id: 4294968372 /sys/kernel/tracing @ mnt_id: 4294968373 /sys/kernel/debug @ mnt_id: 4294968374 /sys/fs/fuse/connections @ mnt_id: 4294968375 /sys/kernel/config @ mnt_id: 4294968376 whereas with LISTMOUNT_REVERSE it gives: /sys/kernel/config @ mnt_id: 4294968376 /sys/fs/fuse/connections @ mnt_id: 4294968375 /sys/kernel/debug @ mnt_id: 4294968374 /sys/kernel/tracing @ mnt_id: 4294968373 /sys/fs/bpf @ mnt_id: 4294968372 /sys/firmware/efi/efivars @ mnt_id: 4294968371 /sys/fs/cgroup @ mnt_id: 4294968370 /sys/kernel/security @ mnt_id: 4294968369

Christian Brauner committed 2 years ago

Commit d04bccd1 parent 17e70162 files · +50 −10Browse files

Unified · Split

2 files
  1. fs/namespace.c +49 −10
  2. include/uapi/linux/mount.h +1 −0
fs/namespace.c+49−10
@@ -1448,6 +1448,30 @@ static struct mount *mnt_find_id_at(struct mnt_namespace *ns, u64 mnt_id) · 1447 unmodified lines
1448 return ret;1448 return ret;
1449}1449}
1450 1450
1451/*
1452 * Returns the mount which either has the specified mnt_id, or has the next
1453 * greater id before the specified one.
1454 */
1455static struct mount *mnt_find_id_at_reverse(struct mnt_namespace *ns, u64 mnt_id)
1456{
1457 struct rb_node *node = ns->mounts.rb_node;
1458 struct mount *ret = NULL;
1459
1460 while (node) {
1461 struct mount *m = node_to_mount(node);
1462
1463 if (mnt_id >= m->mnt_id_unique) {
1464 ret = node_to_mount(node);
1465 if (mnt_id == m->mnt_id_unique)
1466 break;
1467 node = node->rb_right;
1468 } else {
1469 node = node->rb_left;
1470 }
1471 }
1472 return ret;
1473}
1474
1451#ifdef CONFIG_PROC_FS1475#ifdef CONFIG_PROC_FS
1452 1476
1453/* iterator; we want it to have access to namespace_sem, thus here... */1477/* iterator; we want it to have access to namespace_sem, thus here... */
@@ -5042,13 +5066,20 @@ retry: · 3588 unmodified lines
5042 return ret;5066 return ret;
5043}5067}
5044 5068
5045static struct mount *listmnt_next(struct mount *curr)5069static struct mount *listmnt_next(struct mount *curr, bool reverse)
5046{5070{
5047 return node_to_mount(rb_next(&curr->mnt_node));5071 struct rb_node *node;
5072
5073 if (reverse)
5074 node = rb_prev(&curr->mnt_node);
5075 else
5076 node = rb_next(&curr->mnt_node);
5077
5078 return node_to_mount(node);
5048}5079}
5049 5080
5050static ssize_t do_listmount(u64 mnt_parent_id, u64 last_mnt_id, u64 *mnt_ids,5081static ssize_t do_listmount(u64 mnt_parent_id, u64 last_mnt_id, u64 *mnt_ids,
5051 size_t nr_mnt_ids)5082 size_t nr_mnt_ids, bool reverse)
5052{5083{
5053 struct path root __free(path_put) = {};5084 struct path root __free(path_put) = {};
5054 struct mnt_namespace *ns = current->nsproxy->mnt_ns;5085 struct mnt_namespace *ns = current->nsproxy->mnt_ns;
@@ -5080,12 +5111,19 @@ static ssize_t do_listmount(u64 mnt_parent_id, u64 last_mnt_id, u64 *mnt_ids, · 25 unmodified lines
5080 if (ret)5111 if (ret)
5081 return ret;5112 return ret;
5082 5113
5083 if (!last_mnt_id)5114 if (!last_mnt_id) {
5084 first = node_to_mount(rb_first(&ns->mounts));5115 if (reverse)
5085 else5116 first = node_to_mount(rb_last(&ns->mounts));
5086 first = mnt_find_id_at(ns, last_mnt_id + 1);5117 else
5118 first = node_to_mount(rb_first(&ns->mounts));
5119 } else {
5120 if (reverse)
5121 first = mnt_find_id_at_reverse(ns, last_mnt_id - 1);
5122 else
5123 first = mnt_find_id_at(ns, last_mnt_id + 1);
5124 }
5087 5125
5088 for (ret = 0, r = first; r && nr_mnt_ids; r = listmnt_next(r)) {5126 for (ret = 0, r = first; r && nr_mnt_ids; r = listmnt_next(r, reverse)) {
5089 if (r->mnt_id_unique == mnt_parent_id)5127 if (r->mnt_id_unique == mnt_parent_id)
5090 continue;5128 continue;
5091 if (!is_path_reachable(r, r->mnt.mnt_root, &orig))5129 if (!is_path_reachable(r, r->mnt.mnt_root, &orig))
@@ -5106,7 +5144,7 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req, · 14 unmodified lines
5106 struct mnt_id_req kreq;5144 struct mnt_id_req kreq;
5107 ssize_t ret;5145 ssize_t ret;
5108 5146
5109 if (flags)5147 if (flags & ~LISTMOUNT_REVERSE)
5110 return -EINVAL;5148 return -EINVAL;
5111 5149
5112 /*5150 /*
@@ -5130,7 +5168,8 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req, · 17 unmodified lines
5130 return -ENOMEM;5168 return -ENOMEM;
5131 5169
5132 scoped_guard(rwsem_read, &namespace_sem)5170 scoped_guard(rwsem_read, &namespace_sem)
5133 ret = do_listmount(kreq.mnt_id, kreq.param, kmnt_ids, nr_mnt_ids);5171 ret = do_listmount(kreq.mnt_id, kreq.param, kmnt_ids,
5172 nr_mnt_ids, (flags & LISTMOUNT_REVERSE));
5134 5173
5135 if (copy_to_user(mnt_ids, kmnt_ids, ret * sizeof(*mnt_ids)))5174 if (copy_to_user(mnt_ids, kmnt_ids, ret * sizeof(*mnt_ids)))
5136 return -EFAULT;5175 return -EFAULT;
include/uapi/linux/mount.h+1−0
@@ -207,5 +207,6 @@ struct mnt_id_req { · 206 unmodified lines
207 * Special @mnt_id values that can be passed to listmount207 * Special @mnt_id values that can be passed to listmount
208 */208 */
209#define LSMT_ROOT 0xffffffffffffffff /* root mount */209#define LSMT_ROOT 0xffffffffffffffff /* root mount */
210#define LISTMOUNT_REVERSE (1 << 0) /* List later mounts first */
210 211
211#endif /* _UAPI_LINUX_MOUNT_H */212#endif /* _UAPI_LINUX_MOUNT_H */