proc_readdir_de() and proc_sys_readdir() both resume a listing after
the last entry it emitted and each remembers that entry in the open
file with a cursor of its own: the same struct, the same way of
setting it, the same release. Keep one in generic.c and let sysctl
use it.

No functional changes.

Signed-off-by: Christian Brauner (Amutable) <[email protected]>
---
 fs/proc/generic.c     | 29 ++++++++++++-----------------
 fs/proc/internal.h    | 25 +++++++++++++++++++++++++
 fs/proc/proc_sysctl.c | 45 +++++----------------------------------------
 3 files changed, 42 insertions(+), 57 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index e7264f0cf5ab..f6d620a5c3d2 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -301,22 +301,16 @@ struct dentry *proc_lookup(struct inode *dir, struct 
dentry *dentry,
 }
 
 /*
- * Where the next readdir resumes. The entries are counted, so one that is
- * added or removed in front of the position would shift what the next call
- * returns. The name of the last entry emitted doesn't move.
+ * Remember @name as the entry the listing is behind at @pos. If this can't
+ * the next call counts from the start.
  */
-struct proc_dir_cursor {
-       loff_t pos;                     /* the position behind the entry */
-       unsigned int len;
-       char name[NAME_MAX + 1];
-};
-
-/* If this can't remember the entry the next call counts from the start. */
-static void proc_dir_cursor_set(struct file *file, struct proc_dir_entry *de,
-                               loff_t pos)
+void proc_dir_cursor_set(struct file *file, const char *name, unsigned int len,
+                        loff_t pos)
 {
        struct proc_dir_cursor *cursor = file->private_data;
 
+       if (len > NAME_MAX)
+               return;
        if (!cursor) {
                cursor = kmalloc(sizeof(*cursor), GFP_KERNEL);
                if (!cursor)
@@ -324,8 +318,8 @@ static void proc_dir_cursor_set(struct file *file, struct 
proc_dir_entry *de,
                file->private_data = cursor;
        }
        cursor->pos = pos;
-       cursor->len = de->namelen;
-       memcpy(cursor->name, de->name, de->namelen);
+       cursor->len = len;
+       memcpy(cursor->name, name, len);
 }
 
 int proc_dir_release(struct inode *inode, struct file *file)
@@ -346,7 +340,7 @@ int proc_dir_release(struct inode *inode, struct file *file)
 int proc_readdir_de(struct file *file, struct dir_context *ctx,
                    struct proc_dir_entry *de)
 {
-       struct proc_dir_cursor *cursor = file->private_data;
+       const struct proc_dir_cursor *cursor;
        struct proc_dir_entry *dir = de;
        int i;
 
@@ -354,7 +348,8 @@ int proc_readdir_de(struct file *file, struct dir_context 
*ctx,
                return 0;
 
        read_lock(&proc_subdir_lock);
-       if (cursor && cursor->pos == ctx->pos) {
+       cursor = proc_dir_cursor_at(file, ctx->pos);
+       if (cursor) {
                /* Resume after the last entry emitted, wherever it is now. */
                de = pde_subdir_after(dir, cursor->name, cursor->len);
        } else {
@@ -378,7 +373,7 @@ int proc_readdir_de(struct file *file, struct dir_context 
*ctx,
                        return 0;
                }
                ctx->pos++;
-               proc_dir_cursor_set(file, de, ctx->pos);
+               proc_dir_cursor_set(file, de->name, de->namelen, ctx->pos);
                read_lock(&proc_subdir_lock);
                /* Removed in the meantime, go on after its name. */
                if (RB_EMPTY_NODE(&de->subdir_node))
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 268a0523be12..6b61d09ac5e5 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -278,6 +278,31 @@ extern struct dentry *proc_lookup(struct inode *, struct 
dentry *, unsigned int)
 struct dentry *proc_lookup_de(struct inode *, struct dentry *, struct 
proc_dir_entry *);
 extern int proc_readdir(struct file *, struct dir_context *);
 int proc_readdir_de(struct file *, struct dir_context *, struct proc_dir_entry 
*);
+
+/*
+ * Where the next readdir resumes. The entries are counted, so one that is
+ * added or removed in front of the position would shift what the next call
+ * returns. The name of the last entry emitted doesn't move. The directory's
+ * file keeps it, see proc_dir_release().
+ */
+struct proc_dir_cursor {
+       loff_t pos;                     /* the position behind the entry */
+       unsigned int len;
+       char name[NAME_MAX + 1];
+};
+
+/* The entry to resume after, if the position is still the one behind it. */
+static inline const struct proc_dir_cursor *
+proc_dir_cursor_at(struct file *file, loff_t pos)
+{
+       const struct proc_dir_cursor *cursor = file->private_data;
+
+       if (cursor && cursor->pos == pos)
+               return cursor;
+       return NULL;
+}
+void proc_dir_cursor_set(struct file *file, const char *name, unsigned int len,
+                        loff_t pos);
 int proc_dir_release(struct inode *, struct file *);
 
 static inline void pde_get(struct proc_dir_entry *pde)
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 6a92c33db9b5..c1e41a3f8f6b 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -787,36 +787,6 @@ out:
        return ret;
 }
 
-/*
- * Where the next readdir resumes. The entries are counted, so one that is
- * registered or unregistered in front of the position would shift what
- * the next call returns. The name of the last entry emitted doesn't move.
- */
-struct sysctl_cursor {
-       loff_t pos;                     /* the position behind the entry */
-       int len;
-       char name[NAME_MAX + 1];
-};
-
-/* If this can't remember the entry the next call counts from the start. */
-static void sysctl_cursor_set(struct file *file, const char *name, int len,
-                             loff_t pos)
-{
-       struct sysctl_cursor *cursor = file->private_data;
-
-       if (len > NAME_MAX)
-               return;
-       if (!cursor) {
-               cursor = kmalloc(sizeof(*cursor), GFP_KERNEL);
-               if (!cursor)
-                       return;
-               file->private_data = cursor;
-       }
-       cursor->pos = pos;
-       cursor->len = len;
-       memcpy(cursor->name, name, len);
-}
-
 /*
  * The inode number and type of the entry @d holds, instantiating its dentry
  * if there is none. Needs the header pinned, proc_sys_make_inode() checks
@@ -871,7 +841,7 @@ static int proc_sys_fill_dirent(struct file *file, struct 
sysctl_dirent *d)
 static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
 {
        struct ctl_table_header *head = PROC_I(file_inode(file))->sysctl;
-       struct sysctl_cursor *cursor = file->private_data;
+       const struct proc_dir_cursor *cursor;
        struct sysctl_dirent d = {};
        struct ctl_dir *ctl_dir;
        int err;
@@ -888,7 +858,8 @@ static int proc_sys_readdir(struct file *file, struct 
dir_context *ctx)
         * that moved the position away from it counts from the start, like
         * seeking to an arbitrary position always did.
         */
-       if (cursor && cursor->pos == ctx->pos) {
+       cursor = proc_dir_cursor_at(file, ctx->pos);
+       if (cursor) {
                err = sysctl_dir_next(ctl_dir, &d, cursor->name, cursor->len);
        } else {
                loff_t pos = 2;
@@ -910,19 +881,13 @@ static int proc_sys_readdir(struct file *file, struct 
dir_context *ctx)
                                break;
                }
                ctx->pos++;
-               sysctl_cursor_set(file, d.name, d.len, ctx->pos);
+               proc_dir_cursor_set(file, d.name, d.len, ctx->pos);
                err = sysctl_dir_next(ctl_dir, &d, d.name, d.len);
        }
        sysctl_dir_done(&d);
        return err;
 }
 
-static int proc_sys_dir_release(struct inode *inode, struct file *file)
-{
-       kfree(file->private_data);
-       return 0;
-}
-
 static int proc_sys_permission(const struct mnt_idmap *idmap,
                               struct inode *inode, int mask)
 {
@@ -1002,7 +967,7 @@ static const struct file_operations 
proc_sys_dir_file_operations = {
        .read           = generic_read_dir,
        .iterate_shared = proc_sys_readdir,
        .llseek         = generic_file_llseek,
-       .release        = proc_sys_dir_release,
+       .release        = proc_dir_release,
 };
 
 static const struct inode_operations proc_sys_inode_operations = {

-- 
2.53.0

Reply via email to