Re: proposed libc interface and man page for statmount(2)

Miklos Szeredi
2023-11-20T11:55:26Z

On Fri, Nov 17, 2023 at 04:50:25PM +0100, Miklos Szeredi wrote:
> I wonder... Is there a reason this shouldn't be done statelessly by
> adding an "continue after this ID" argument to listmount(2)? The
> caller will just need to pass the last mount ID received in the array
> to the next listmount(2) call and iterate until a short count is
> returned.

No comments so far... maybe more explanation is needed.

New signature of listmount() would be:

ssize_t listmount(uint64_t mnt_id, uint64_t last_mnt_id,
uint64_t *buf, size_t bufsize, unsigned int flags);

And the usage would be:

for (last = 0; nres == bufsize; last = buf[bufsize-1]) {
nres = listmount(parent, last, buf, bufsize, flags);
for (i = 0; i < nres; i++) {
/* process buf[i] */
}
}

Here's a kernel patch against the version in Christian's tree. The syscall
signature doesn't need changing, since we have a spare u64 in the mnt_id_req for
listmount.

The major difference is in the order that the mount ID's are listed, which is
now strictly increasing. Doing the recursive listing in DFS order is nicer, but
I don't think it's important enough.

Comments?

Thanks,
Miklos

---
fs/namespace.c | 41 +++++++++++++++++++++++++----------------
include/uapi/linux/mount.h | 5 ++++-
2 files changed, 29 insertions(+), 17 deletions(-)

--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1009,7 +1009,7 @@ void mnt_change_mountpoint(struct mount

static inline struct mount *node_to_mount(struct rb_node *node)
{
- return rb_entry(node, struct mount, mnt_node);
+ return node ? rb_entry(node, struct mount, mnt_node) : NULL;
}

static void mnt_add_to_ns(struct mnt_namespace *ns, struct mount *mnt)
@@ -4960,21 +4960,22 @@ SYSCALL_DEFINE4(statmount, const struct
return ret;
}

-static struct mount *listmnt_first(struct mount *root)
+static struct mount *listmnt_next(struct mount *curr)
{
- return list_first_entry_or_null(&root->mnt_mounts, struct mount, mnt_child);
+ return node_to_mount(rb_next(&curr->mnt_node));
}

-static struct mount *listmnt_next(struct mount *curr, struct mount *root, bool recurse)
+static bool is_submount(struct mount *sub, struct mount *mnt)
{
- if (recurse)
- return next_mnt(curr, root);
- if (!list_is_head(curr->mnt_child.next, &root->mnt_mounts))
- return list_next_entry(curr, mnt_child);
- return NULL;
+ for (; sub != mnt; sub = sub->mnt_parent) {
+ if (sub->mnt_parent == sub)
+ return false;
+ }
+ return true;
}

-static long do_listmount(struct vfsmount *mnt, u64 __user *buf, size_t bufsize,
+static long do_listmount(struct vfsmount *mnt, struct mount *last,
+ u64 __user *buf, size_t bufsize,
const struct path *root, unsigned int flags)
{
struct mount *r, *m = real_mount(mnt);
@@ -5000,13 +5001,16 @@ static long do_listmount(struct vfsmount
if (err)
return err;

- for (r = listmnt_first(m); r; r = listmnt_next(r, m, recurse)) {
+ for (r = last; (r = listmnt_next(r)) != NULL && ctr < bufsize;) {
+ if (recurse && !is_submount(r, m))
+ continue;
+ if (!recurse && r->mnt_parent != m)
+ continue;
+
if (reachable_only &&
!is_path_reachable(r, r->mnt.mnt_root, root))
continue;

- if (ctr >= bufsize)
- return -EOVERFLOW;
if (put_user(r->mnt_id_unique, buf + ctr))
return -EFAULT;
ctr++;
@@ -5021,6 +5025,7 @@ SYSCALL_DEFINE4(listmount, const struct
{
struct mnt_id_req kreq;
struct vfsmount *mnt;
+ struct mount *last;
struct path root;
u64 mnt_id;
long err;
@@ -5030,8 +5035,6 @@ SYSCALL_DEFINE4(listmount, const struct

if (copy_from_user(&kreq, req, sizeof(kreq)))
return -EFAULT;
- if (kreq.request_mask != 0)
- return -EINVAL;
mnt_id = kreq.mnt_id;

down_read(&namespace_sem);
@@ -5040,13 +5043,19 @@ SYSCALL_DEFINE4(listmount, const struct
else
mnt = lookup_mnt_in_ns(mnt_id, current->nsproxy->mnt_ns);

+ if (!kreq.last_mnt_id) {
+ last = real_mount(mnt);
+ } else {
+ last = mnt_find_id_at(current->nsproxy->mnt_ns, kreq.last_mnt_id);
+ }
+
err = -ENOENT;
if (mnt) {
get_fs_root(current->fs, &root);
/* Skip unreachable for LSMT_ROOT */
if (mnt_id == LSMT_ROOT && !(flags & LISTMOUNT_UNREACHABLE))
mnt = root.mnt;
- err = do_listmount(mnt, buf, bufsize, &root, flags);
+ err = do_listmount(mnt, last, buf, bufsize, &root, flags);
path_put(&root);
}
up_read(&namespace_sem);
--- a/include/uapi/linux/mount.h
+++ b/include/uapi/linux/mount.h
@@ -178,7 +178,10 @@ struct statmount {

struct mnt_id_req {
__u64 mnt_id;
- __u64 request_mask;
+ union {
+ __u64 request_mask;
+ __u64 last_mnt_id;
+ };
};

/*