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
14481448 return ret;
14491449 }
14501450
1451+/*
1452+ * Returns the mount which either has the specified mnt_id, or has the next
1453+ * greater id before the specified one.
1454+ */
1455+static 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+
14511475 #ifdef CONFIG_PROC_FS
14521476
14531477 /* iterator; we want it to have access to namespace_sem, thus here... */
@@ -5042,13 +5066,20 @@ retry: · 3588 unmodified lines
50425066 return ret;
50435067 }
50445068
5045−static struct mount *listmnt_next(struct mount *curr)
5069+static struct mount *listmnt_next(struct mount *curr, bool reverse)
50465070 {
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);
50485079 }
50495080
50505081 static 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)
50525083 {
50535084 struct path root __free(path_put) = {};
50545085 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
50805111 if (ret)
50815112 return ret;
50825113
5083− if (!last_mnt_id)
5084− first = node_to_mount(rb_first(&ns->mounts));
5085− else
5086− first = mnt_find_id_at(ns, last_mnt_id + 1);
5114+ if (!last_mnt_id) {
5115+ if (reverse)
5116+ first = node_to_mount(rb_last(&ns->mounts));
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+ }
50875125
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)) {
50895127 if (r->mnt_id_unique == mnt_parent_id)
50905128 continue;
50915129 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
51065144 struct mnt_id_req kreq;
51075145 ssize_t ret;
51085146
5109− if (flags)
5147+ if (flags & ~LISTMOUNT_REVERSE)
51105148 return -EINVAL;
51115149
51125150 /*
@@ -5130,7 +5168,8 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req, · 17 unmodified lines
51305168 return -ENOMEM;
51315169
51325170 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));
51345173
51355174 if (copy_to_user(mnt_ids, kmnt_ids, ret * sizeof(*mnt_ids)))
51365175 return -EFAULT;
include/uapi/linux/mount.h+1−0
@@ -207,5 +207,6 @@ struct mnt_id_req { · 206 unmodified lines
207207 * Special @mnt_id values that can be passed to listmount
208208 */
209209 #define LSMT_ROOT 0xffffffffffffffff /* root mount */
210+#define LISTMOUNT_REVERSE (1 << 0) /* List later mounts first */
210211
211212 #endif /* _UAPI_LINUX_MOUNT_H */