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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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;
+	};
 };
 
 /*




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux