Commit d04bccd8 authored by Christian Brauner's avatar Christian Brauner

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

Link: https://lore.kernel.org/r/20240607-vfs-listmount-reverse-v1-4-7877a2bfa5e5@kernel.orgReviewed-by: default avatarJosef Bacik <josef@toxicpanda.com>
Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent 17e70161
...@@ -1448,6 +1448,30 @@ static struct mount *mnt_find_id_at(struct mnt_namespace *ns, u64 mnt_id) ...@@ -1448,6 +1448,30 @@ static struct mount *mnt_find_id_at(struct mnt_namespace *ns, u64 mnt_id)
return ret; return ret;
} }
/*
* Returns the mount which either has the specified mnt_id, or has the next
* greater id before the specified one.
*/
static struct mount *mnt_find_id_at_reverse(struct mnt_namespace *ns, u64 mnt_id)
{
struct rb_node *node = ns->mounts.rb_node;
struct mount *ret = NULL;
while (node) {
struct mount *m = node_to_mount(node);
if (mnt_id >= m->mnt_id_unique) {
ret = node_to_mount(node);
if (mnt_id == m->mnt_id_unique)
break;
node = node->rb_right;
} else {
node = node->rb_left;
}
}
return ret;
}
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
/* iterator; we want it to have access to namespace_sem, thus here... */ /* iterator; we want it to have access to namespace_sem, thus here... */
...@@ -5042,13 +5066,20 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req, ...@@ -5042,13 +5066,20 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req,
return ret; return ret;
} }
static struct mount *listmnt_next(struct mount *curr) static struct mount *listmnt_next(struct mount *curr, bool reverse)
{ {
return node_to_mount(rb_next(&curr->mnt_node)); struct rb_node *node;
if (reverse)
node = rb_prev(&curr->mnt_node);
else
node = rb_next(&curr->mnt_node);
return node_to_mount(node);
} }
static ssize_t do_listmount(u64 mnt_parent_id, u64 last_mnt_id, u64 *mnt_ids, static ssize_t do_listmount(u64 mnt_parent_id, u64 last_mnt_id, u64 *mnt_ids,
size_t nr_mnt_ids) size_t nr_mnt_ids, bool reverse)
{ {
struct path root __free(path_put) = {}; struct path root __free(path_put) = {};
struct mnt_namespace *ns = current->nsproxy->mnt_ns; 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, ...@@ -5080,12 +5111,19 @@ static ssize_t do_listmount(u64 mnt_parent_id, u64 last_mnt_id, u64 *mnt_ids,
if (ret) if (ret)
return ret; return ret;
if (!last_mnt_id) if (!last_mnt_id) {
if (reverse)
first = node_to_mount(rb_last(&ns->mounts));
else
first = node_to_mount(rb_first(&ns->mounts)); first = node_to_mount(rb_first(&ns->mounts));
} else {
if (reverse)
first = mnt_find_id_at_reverse(ns, last_mnt_id - 1);
else else
first = mnt_find_id_at(ns, last_mnt_id + 1); first = mnt_find_id_at(ns, last_mnt_id + 1);
}
for (ret = 0, r = first; r && nr_mnt_ids; r = listmnt_next(r)) { for (ret = 0, r = first; r && nr_mnt_ids; r = listmnt_next(r, reverse)) {
if (r->mnt_id_unique == mnt_parent_id) if (r->mnt_id_unique == mnt_parent_id)
continue; continue;
if (!is_path_reachable(r, r->mnt.mnt_root, &orig)) if (!is_path_reachable(r, r->mnt.mnt_root, &orig))
...@@ -5106,7 +5144,7 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req, ...@@ -5106,7 +5144,7 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
struct mnt_id_req kreq; struct mnt_id_req kreq;
ssize_t ret; ssize_t ret;
if (flags) if (flags & ~LISTMOUNT_REVERSE)
return -EINVAL; return -EINVAL;
/* /*
...@@ -5130,7 +5168,8 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req, ...@@ -5130,7 +5168,8 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
return -ENOMEM; return -ENOMEM;
scoped_guard(rwsem_read, &namespace_sem) scoped_guard(rwsem_read, &namespace_sem)
ret = do_listmount(kreq.mnt_id, kreq.param, kmnt_ids, nr_mnt_ids); ret = do_listmount(kreq.mnt_id, kreq.param, kmnt_ids,
nr_mnt_ids, (flags & LISTMOUNT_REVERSE));
if (copy_to_user(mnt_ids, kmnt_ids, ret * sizeof(*mnt_ids))) if (copy_to_user(mnt_ids, kmnt_ids, ret * sizeof(*mnt_ids)))
return -EFAULT; return -EFAULT;
......
...@@ -207,5 +207,6 @@ struct mnt_id_req { ...@@ -207,5 +207,6 @@ struct mnt_id_req {
* Special @mnt_id values that can be passed to listmount * Special @mnt_id values that can be passed to listmount
*/ */
#define LSMT_ROOT 0xffffffffffffffff /* root mount */ #define LSMT_ROOT 0xffffffffffffffff /* root mount */
#define LISTMOUNT_REVERSE (1 << 0) /* List later mounts first */
#endif /* _UAPI_LINUX_MOUNT_H */ #endif /* _UAPI_LINUX_MOUNT_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment