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
