Commit f0c3b509 authored by Al Viro's avatar Al Viro

[readdir] convert procfs

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 68c61471
......@@ -542,8 +542,8 @@ static const struct file_operations hppfs_file_fops = {
};
struct hppfs_dirent {
void *vfs_dirent;
filldir_t filldir;
struct dir_context ctx;
struct dir_context *caller;
struct dentry *dentry;
};
......@@ -555,34 +555,29 @@ static int hppfs_filldir(void *d, const char *name, int size,
if (file_removed(dirent->dentry, name))
return 0;
return (*dirent->filldir)(dirent->vfs_dirent, name, size, offset,
inode, type);
dirent->caller->pos = dirent->ctx.pos;
return !dir_emit(dirent->caller, name, size, inode, type);
}
static int hppfs_readdir(struct file *file, void *ent, filldir_t filldir)
static int hppfs_readdir(struct file *file, struct dir_context *ctx)
{
struct hppfs_private *data = file->private_data;
struct file *proc_file = data->proc_file;
int (*readdir)(struct file *, void *, filldir_t);
struct hppfs_dirent dirent = ((struct hppfs_dirent)
{ .vfs_dirent = ent,
.filldir = filldir,
.dentry = file->f_path.dentry
});
struct hppfs_dirent d = {
.ctx.actor = hppfs_filldir,
.caller = ctx,
.dentry = file->f_path.dentry
};
int err;
readdir = file_inode(proc_file)->i_fop->readdir;
proc_file->f_pos = file->f_pos;
err = (*readdir)(proc_file, &dirent, hppfs_filldir);
file->f_pos = proc_file->f_pos;
proc_file->f_pos = ctx->pos;
err = iterate_dir(proc_file, &d.ctx);
ctx->pos = d.ctx.pos;
return err;
}
static const struct file_operations hppfs_dir_fops = {
.owner = NULL,
.readdir = hppfs_readdir,
.iterate = hppfs_readdir,
.open = hppfs_dir_open,
.llseek = default_llseek,
.release = hppfs_release,
......
......@@ -1681,11 +1681,11 @@ const struct dentry_operations pid_dentry_operations =
* reported by readdir in sync with the inode numbers reported
* by stat.
*/
int proc_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
bool proc_fill_cache(struct file *file, struct dir_context *ctx,
const char *name, int len,
instantiate_t instantiate, struct task_struct *task, const void *ptr)
{
struct dentry *child, *dir = filp->f_path.dentry;
struct dentry *child, *dir = file->f_path.dentry;
struct inode *inode;
struct qstr qname;
ino_t ino = 0;
......@@ -1720,7 +1720,7 @@ int proc_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
ino = find_inode_number(dir, &qname);
if (!ino)
ino = 1;
return filldir(dirent, name, len, filp->f_pos, ino, type);
return dir_emit(ctx, name, len, ino, type);
}
#ifdef CONFIG_CHECKPOINT_RESTORE
......@@ -1931,14 +1931,15 @@ static const struct inode_operations proc_map_files_inode_operations = {
};
static int
proc_map_files_readdir(struct file *filp, void *dirent, filldir_t filldir)
proc_map_files_readdir(struct file *file, struct dir_context *ctx)
{
struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode;
struct vm_area_struct *vma;
struct task_struct *task;
struct mm_struct *mm;
ino_t ino;
unsigned long nr_files, pos, i;
struct flex_array *fa = NULL;
struct map_files_info info;
struct map_files_info *p;
int ret;
ret = -EPERM;
......@@ -1946,7 +1947,7 @@ proc_map_files_readdir(struct file *filp, void *dirent, filldir_t filldir)
goto out;
ret = -ENOENT;
task = get_proc_task(inode);
task = get_proc_task(file_inode(file));
if (!task)
goto out;
......@@ -1955,91 +1956,73 @@ proc_map_files_readdir(struct file *filp, void *dirent, filldir_t filldir)
goto out_put_task;
ret = 0;
switch (filp->f_pos) {
case 0:
ino = inode->i_ino;
if (filldir(dirent, ".", 1, 0, ino, DT_DIR) < 0)
goto out_put_task;
filp->f_pos++;
case 1:
ino = parent_ino(dentry);
if (filldir(dirent, "..", 2, 1, ino, DT_DIR) < 0)
goto out_put_task;
filp->f_pos++;
default:
{
unsigned long nr_files, pos, i;
struct flex_array *fa = NULL;
struct map_files_info info;
struct map_files_info *p;
mm = get_task_mm(task);
if (!mm)
goto out_put_task;
down_read(&mm->mmap_sem);
if (!dir_emit_dots(file, ctx))
goto out_put_task;
nr_files = 0;
mm = get_task_mm(task);
if (!mm)
goto out_put_task;
down_read(&mm->mmap_sem);
/*
* We need two passes here:
*
* 1) Collect vmas of mapped files with mmap_sem taken
* 2) Release mmap_sem and instantiate entries
*
* otherwise we get lockdep complained, since filldir()
* routine might require mmap_sem taken in might_fault().
*/
nr_files = 0;
for (vma = mm->mmap, pos = 2; vma; vma = vma->vm_next) {
if (vma->vm_file && ++pos > filp->f_pos)
nr_files++;
}
/*
* We need two passes here:
*
* 1) Collect vmas of mapped files with mmap_sem taken
* 2) Release mmap_sem and instantiate entries
*
* otherwise we get lockdep complained, since filldir()
* routine might require mmap_sem taken in might_fault().
*/
if (nr_files) {
fa = flex_array_alloc(sizeof(info), nr_files,
GFP_KERNEL);
if (!fa || flex_array_prealloc(fa, 0, nr_files,
GFP_KERNEL)) {
ret = -ENOMEM;
if (fa)
flex_array_free(fa);
up_read(&mm->mmap_sem);
mmput(mm);
goto out_put_task;
}
for (i = 0, vma = mm->mmap, pos = 2; vma;
vma = vma->vm_next) {
if (!vma->vm_file)
continue;
if (++pos <= filp->f_pos)
continue;
info.mode = vma->vm_file->f_mode;
info.len = snprintf(info.name,
sizeof(info.name), "%lx-%lx",
vma->vm_start, vma->vm_end);
if (flex_array_put(fa, i++, &info, GFP_KERNEL))
BUG();
}
for (vma = mm->mmap, pos = 2; vma; vma = vma->vm_next) {
if (vma->vm_file && ++pos > ctx->pos)
nr_files++;
}
if (nr_files) {
fa = flex_array_alloc(sizeof(info), nr_files,
GFP_KERNEL);
if (!fa || flex_array_prealloc(fa, 0, nr_files,
GFP_KERNEL)) {
ret = -ENOMEM;
if (fa)
flex_array_free(fa);
up_read(&mm->mmap_sem);
mmput(mm);
goto out_put_task;
}
up_read(&mm->mmap_sem);
for (i = 0; i < nr_files; i++) {
p = flex_array_get(fa, i);
ret = proc_fill_cache(filp, dirent, filldir,
p->name, p->len,
proc_map_files_instantiate,
task,
(void *)(unsigned long)p->mode);
if (ret)
break;
filp->f_pos++;
for (i = 0, vma = mm->mmap, pos = 2; vma;
vma = vma->vm_next) {
if (!vma->vm_file)
continue;
if (++pos <= ctx->pos)
continue;
info.mode = vma->vm_file->f_mode;
info.len = snprintf(info.name,
sizeof(info.name), "%lx-%lx",
vma->vm_start, vma->vm_end);
if (flex_array_put(fa, i++, &info, GFP_KERNEL))
BUG();
}
if (fa)
flex_array_free(fa);
mmput(mm);
}
up_read(&mm->mmap_sem);
for (i = 0; i < nr_files; i++) {
p = flex_array_get(fa, i);
if (!proc_fill_cache(file, ctx,
p->name, p->len,
proc_map_files_instantiate,
task,
(void *)(unsigned long)p->mode))
break;
ctx->pos++;
}
if (fa)
flex_array_free(fa);
mmput(mm);
out_put_task:
put_task_struct(task);
......@@ -2049,7 +2032,7 @@ proc_map_files_readdir(struct file *filp, void *dirent, filldir_t filldir)
static const struct file_operations proc_map_files_operations = {
.read = generic_read_dir,
.readdir = proc_map_files_readdir,
.iterate = proc_map_files_readdir,
.llseek = default_llseek,
};
......@@ -2217,67 +2200,30 @@ static struct dentry *proc_pident_lookup(struct inode *dir,
return error;
}
static int proc_pident_fill_cache(struct file *filp, void *dirent,
filldir_t filldir, struct task_struct *task, const struct pid_entry *p)
{
return proc_fill_cache(filp, dirent, filldir, p->name, p->len,
proc_pident_instantiate, task, p);
}
static int proc_pident_readdir(struct file *filp,
void *dirent, filldir_t filldir,
static int proc_pident_readdir(struct file *file, struct dir_context *ctx,
const struct pid_entry *ents, unsigned int nents)
{
int i;
struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode;
struct task_struct *task = get_proc_task(inode);
const struct pid_entry *p, *last;
ino_t ino;
int ret;
struct task_struct *task = get_proc_task(file_inode(file));
const struct pid_entry *p;
ret = -ENOENT;
if (!task)
goto out_no_task;
return -ENOENT;
ret = 0;
i = filp->f_pos;
switch (i) {
case 0:
ino = inode->i_ino;
if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
goto out;
i++;
filp->f_pos++;
/* fall through */
case 1:
ino = parent_ino(dentry);
if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
goto out;
i++;
filp->f_pos++;
/* fall through */
default:
i -= 2;
if (i >= nents) {
ret = 1;
goto out;
}
p = ents + i;
last = &ents[nents - 1];
while (p <= last) {
if (proc_pident_fill_cache(filp, dirent, filldir, task, p) < 0)
goto out;
filp->f_pos++;
p++;
}
}
if (!dir_emit_dots(file, ctx))
goto out;
if (ctx->pos >= nents + 2)
goto out;
ret = 1;
for (p = ents + (ctx->pos - 2); p <= ents + nents - 1; p++) {
if (!proc_fill_cache(file, ctx, p->name, p->len,
proc_pident_instantiate, task, p))
break;
ctx->pos++;
}
out:
put_task_struct(task);
out_no_task:
return ret;
return 0;
}
#ifdef CONFIG_SECURITY
......@@ -2362,16 +2308,15 @@ static const struct pid_entry attr_dir_stuff[] = {
REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
};
static int proc_attr_dir_readdir(struct file * filp,
void * dirent, filldir_t filldir)
static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx)
{
return proc_pident_readdir(filp,dirent,filldir,
attr_dir_stuff,ARRAY_SIZE(attr_dir_stuff));
return proc_pident_readdir(file, ctx,
attr_dir_stuff, ARRAY_SIZE(attr_dir_stuff));
}
static const struct file_operations proc_attr_dir_operations = {
.read = generic_read_dir,
.readdir = proc_attr_dir_readdir,
.iterate = proc_attr_dir_readdir,
.llseek = default_llseek,
};
......@@ -2725,16 +2670,15 @@ static const struct pid_entry tgid_base_stuff[] = {
#endif
};
static int proc_tgid_base_readdir(struct file * filp,
void * dirent, filldir_t filldir)
static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx)
{
return proc_pident_readdir(filp,dirent,filldir,
tgid_base_stuff,ARRAY_SIZE(tgid_base_stuff));
return proc_pident_readdir(file, ctx,
tgid_base_stuff, ARRAY_SIZE(tgid_base_stuff));
}
static const struct file_operations proc_tgid_base_operations = {
.read = generic_read_dir,
.readdir = proc_tgid_base_readdir,
.iterate = proc_tgid_base_readdir,
.llseek = default_llseek,
};
......@@ -2936,58 +2880,42 @@ static struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter ite
#define TGID_OFFSET (FIRST_PROCESS_ENTRY + 1)
static int proc_pid_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
struct tgid_iter iter)
{
char name[PROC_NUMBUF];
int len = snprintf(name, sizeof(name), "%d", iter.tgid);
return proc_fill_cache(filp, dirent, filldir, name, len,
proc_pid_instantiate, iter.task, NULL);
}
static int fake_filldir(void *buf, const char *name, int namelen,
loff_t offset, u64 ino, unsigned d_type)
{
return 0;
}
/* for the /proc/ directory itself, after non-process stuff has been done */
int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)
int proc_pid_readdir(struct file *file, struct dir_context *ctx)
{
struct tgid_iter iter;
struct pid_namespace *ns;
filldir_t __filldir;
loff_t pos = filp->f_pos;
loff_t pos = ctx->pos;
if (pos >= PID_MAX_LIMIT + TGID_OFFSET)
goto out;
return 0;
if (pos == TGID_OFFSET - 1) {
if (proc_fill_cache(filp, dirent, filldir, "self", 4,
NULL, NULL, NULL) < 0)
goto out;
if (!proc_fill_cache(file, ctx, "self", 4, NULL, NULL, NULL))
return 0;
iter.tgid = 0;
} else {
iter.tgid = pos - TGID_OFFSET;
}
iter.task = NULL;
ns = filp->f_dentry->d_sb->s_fs_info;
ns = file->f_dentry->d_sb->s_fs_info;
for (iter = next_tgid(ns, iter);
iter.task;
iter.tgid += 1, iter = next_tgid(ns, iter)) {
if (has_pid_permissions(ns, iter.task, 2))
__filldir = filldir;
else
__filldir = fake_filldir;
char name[PROC_NUMBUF];
int len;
if (!has_pid_permissions(ns, iter.task, 2))
continue;
filp->f_pos = iter.tgid + TGID_OFFSET;
if (proc_pid_fill_cache(filp, dirent, __filldir, iter) < 0) {
len = snprintf(name, sizeof(name), "%d", iter.tgid);
ctx->pos = iter.tgid + TGID_OFFSET;
if (!proc_fill_cache(file, ctx, name, len,
proc_pid_instantiate, iter.task, NULL)) {
put_task_struct(iter.task);
goto out;
return 0;
}
}
filp->f_pos = PID_MAX_LIMIT + TGID_OFFSET;
out:
ctx->pos = PID_MAX_LIMIT + TGID_OFFSET;
return 0;
}
......@@ -3075,11 +3003,10 @@ static const struct pid_entry tid_base_stuff[] = {
#endif
};
static int proc_tid_base_readdir(struct file * filp,
void * dirent, filldir_t filldir)
static int proc_tid_base_readdir(struct file *file, struct dir_context *ctx)
{
return proc_pident_readdir(filp,dirent,filldir,
tid_base_stuff,ARRAY_SIZE(tid_base_stuff));
return proc_pident_readdir(file, ctx,
tid_base_stuff, ARRAY_SIZE(tid_base_stuff));
}
static struct dentry *proc_tid_base_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
......@@ -3090,7 +3017,7 @@ static struct dentry *proc_tid_base_lookup(struct inode *dir, struct dentry *den
static const struct file_operations proc_tid_base_operations = {
.read = generic_read_dir,
.readdir = proc_tid_base_readdir,
.iterate = proc_tid_base_readdir,
.llseek = default_llseek,
};
......@@ -3231,30 +3158,16 @@ static struct task_struct *next_tid(struct task_struct *start)
return pos;
}
static int proc_task_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
struct task_struct *task, int tid)
{
char name[PROC_NUMBUF];
int len = snprintf(name, sizeof(name), "%d", tid);
return proc_fill_cache(filp, dirent, filldir, name, len,
proc_task_instantiate, task, NULL);
}
/* for the /proc/TGID/task/ directories */
static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldir)
static int proc_task_readdir(struct file *file, struct dir_context *ctx)
{
struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode;
struct task_struct *leader = NULL;
struct task_struct *task;
int retval = -ENOENT;
ino_t ino;
int tid;
struct task_struct *task = get_proc_task(file_inode(file));
struct pid_namespace *ns;
int tid;
task = get_proc_task(inode);
if (!task)
goto out_no_task;
return -ENOENT;
rcu_read_lock();
if (pid_alive(task)) {
leader = task->group_leader;
......@@ -3263,46 +3176,36 @@ static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldi
rcu_read_unlock();
put_task_struct(task);
if (!leader)
goto out_no_task;
retval = 0;
return -ENOENT;
switch ((unsigned long)filp->f_pos) {
case 0:
ino = inode->i_ino;
if (filldir(dirent, ".", 1, filp->f_pos, ino, DT_DIR) < 0)
goto out;
filp->f_pos++;
/* fall through */
case 1:
ino = parent_ino(dentry);
if (filldir(dirent, "..", 2, filp->f_pos, ino, DT_DIR) < 0)
goto out;
filp->f_pos++;
/* fall through */
}
if (!dir_emit_dots(file, ctx))
goto out;
/* f_version caches the tgid value that the last readdir call couldn't
* return. lseek aka telldir automagically resets f_version to 0.
*/
ns = filp->f_dentry->d_sb->s_fs_info;
tid = (int)filp->f_version;
filp->f_version = 0;
for (task = first_tid(leader, tid, filp->f_pos - 2, ns);
ns = file->f_dentry->d_sb->s_fs_info;
tid = (int)file->f_version;
file->f_version = 0;
for (task = first_tid(leader, tid, ctx->pos - 2, ns);
task;
task = next_tid(task), filp->f_pos++) {
task = next_tid(task), ctx->pos++) {
char name[PROC_NUMBUF];
int len;
tid = task_pid_nr_ns(task, ns);
if (proc_task_fill_cache(filp, dirent, filldir, task, tid) < 0) {
len = snprintf(name, sizeof(name), "%d", tid);
if (!proc_fill_cache(file, ctx, name, len,
proc_task_instantiate, task, NULL)) {
/* returning this tgid failed, save it as the first
* pid for the next readir call */
filp->f_version = (u64)tid;
file->f_version = (u64)tid;
put_task_struct(task);
break;
}
}
out:
put_task_struct(leader);
out_no_task:
return retval;
return 0;
}
static int proc_task_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
......@@ -3328,6 +3231,6 @@ static const struct inode_operations proc_task_inode_operations = {
static const struct file_operations proc_task_operations = {
.read = generic_read_dir,
.readdir = proc_task_readdir,
.iterate = proc_task_readdir,
.llseek = default_llseek,
};
......@@ -219,74 +219,58 @@ static struct dentry *proc_lookupfd_common(struct inode *dir,
return result;
}
static int proc_readfd_common(struct file * filp, void * dirent,
filldir_t filldir, instantiate_t instantiate)
static int proc_readfd_common(struct file *file, struct dir_context *ctx,
instantiate_t instantiate)
{
struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode;
struct task_struct *p = get_proc_task(inode);
struct task_struct *p = get_proc_task(file_inode(file));
struct files_struct *files;
unsigned int fd, ino;
int retval;
unsigned int fd;
retval = -ENOENT;
if (!p)
goto out_no_task;