Skip to content
Snippets Groups Projects
proc_mounts.patch 3.23 KiB
Newer Older
Philip Müller's avatar
Philip Müller committed
SPDX-License-Identifier: GPL-2.0

diff --git a/fs/mount.h b/fs/mount.h
index 711a4093e475..a5703d5c7205 100644
--- a/fs/mount.h
+++ b/fs/mount.h
@@ -133,9 +133,7 @@ struct proc_mounts {
 	struct mnt_namespace *ns;
 	struct path root;
 	int (*show)(struct seq_file *, struct vfsmount *);
-	void *cached_mount;
-	u64 cached_event;
-	loff_t cached_index;
+	size_t filled;
 };
 
 extern const struct seq_operations mounts_op;
diff --git a/fs/namespace.c b/fs/namespace.c
index 85b5f7bea82e..ff398173c36f 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1246,46 +1246,80 @@ struct vfsmount *mnt_clone_internal(const struct path *path)
 
 #ifdef CONFIG_PROC_FS
 /* iterator; we want it to have access to namespace_sem, thus here... */
-static void *m_start(struct seq_file *m, loff_t *pos)
+static int m_start_fill(struct seq_file *m)
 {
+	int err;
+	size_t last_count;
+	char *buf;
+	struct mount *r;
 	struct proc_mounts *p = m->private;
 
+	err = -ENODATA;
+	m->count = 0;
 	down_read(&namespace_sem);
-	if (p->cached_event == p->ns->event) {
-		void *v = p->cached_mount;
-		if (*pos == p->cached_index)
-			return v;
-		if (*pos == p->cached_index + 1) {
-			v = seq_list_next(v, &p->ns->list, &p->cached_index);
-			return p->cached_mount = v;
+	list_for_each_entry(r, &p->ns->list, mnt_list) {
+		last_count = m->count;
+		err = p->show(m, &r->mnt);
+		if (unlikely(err < 0))
+			break;
+		if (!seq_has_overflowed(m))
+			continue;
+
+		/* expand the buffer */
+		buf = kvmalloc(m->size + PAGE_SIZE, GFP_KERNEL);
+		if (unlikely(!buf)) {
+			err = -ENOMEM;
+			break;
+		}
+		memcpy(buf, m->buf, last_count);
+		kvfree(m->buf);
+		m->buf = buf;
+		m->size += PAGE_SIZE;
+		m->count = last_count;
+
+#if 0 /* todo: consider the limit */
+		err = p->show(m, &r->mnt);
+		if (unlikely(err < 0))
+			break;
+		else if (unlikely(seq_has_overflowed(m))) {
+			err = -EOVERFLOW;
+			break;
 		}
+#endif
 	}
+	up_read(&namespace_sem);
 
-	p->cached_event = p->ns->event;
-	p->cached_mount = seq_list_start(&p->ns->list, *pos);
-	p->cached_index = *pos;
-	return p->cached_mount;
+	if (!err)
+		p->filled = m->count;
+	return err;
 }
 
-static void *m_next(struct seq_file *m, void *v, loff_t *pos)
+static void *m_start(struct seq_file *m, loff_t *pos)
 {
-	struct proc_mounts *p = m->private;
+	int err;
 
-	p->cached_mount = seq_list_next(v, &p->ns->list, pos);
-	p->cached_index = *pos;
-	return p->cached_mount;
+	if (!*pos) {
+		err = m_start_fill(m);
+		if (unlikely(err))
+			return ERR_PTR(err);
+	}
+
+	return m_start; /* any valid pointer */
+}
+
+static void *m_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	return NULL;
 }
 
 static void m_stop(struct seq_file *m, void *v)
 {
-	up_read(&namespace_sem);
+	/* empty */
 }
 
 static int m_show(struct seq_file *m, void *v)
 {
-	struct proc_mounts *p = m->private;
-	struct mount *r = list_entry(v, struct mount, mnt_list);
-	return p->show(m, &r->mnt);
+	return 0;
 }
 
 const struct seq_operations mounts_op = {
diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c
index 273ee82d8aa9..e5b22c50ca59 100644
--- a/fs/proc_namespace.c
+++ b/fs/proc_namespace.c
@@ -279,7 +279,6 @@ static int mounts_open_common(struct inode *inode, struct file *file,
 	p->ns = ns;
 	p->root = root;
 	p->show = show;
-	p->cached_event = ~0ULL;
 
 	return 0;