diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2013-05-15 18:49:12 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-06-29 04:46:47 -0400 |
commit | bb6f619b3a49f940d7478112500da312d70866eb (patch) | |
tree | 4e17d6ed5b965eaec2e55b11b61145b200d28f0f | |
parent | 5c0ba4e0762e6dabd14a5c276652e2defec38de7 (diff) |
[readdir] introduce ->iterate(), ctx->pos, dir_emit()
New method - ->iterate(file, ctx). That's the replacement for ->readdir();
it takes callback from ctx->actor, uses ctx->pos instead of file->f_pos and
calls dir_emit(ctx, ...) instead of filldir(data, ...). It does *not*
update file->f_pos (or look at it, for that matter); iterate_dir() does the
update.
Note that dir_emit() takes the offset from ctx->pos (and eventually
filldir_t will lose that argument).
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | arch/parisc/hpux/fs.c | 2 | ||||
-rw-r--r-- | fs/coda/dir.c | 19 | ||||
-rw-r--r-- | fs/compat.c | 4 | ||||
-rw-r--r-- | fs/exportfs/expfs.c | 2 | ||||
-rw-r--r-- | fs/nfsd/nfs4recover.c | 14 | ||||
-rw-r--r-- | fs/readdir.c | 15 | ||||
-rw-r--r-- | include/linux/fs.h | 9 |
7 files changed, 47 insertions, 18 deletions
diff --git a/arch/parisc/hpux/fs.c b/arch/parisc/hpux/fs.c index fc2cbee86e34..eca8230267cc 100644 --- a/arch/parisc/hpux/fs.c +++ b/arch/parisc/hpux/fs.c | |||
@@ -129,7 +129,7 @@ int hpux_getdents(unsigned int fd, struct hpux_dirent __user *dirent, unsigned i | |||
129 | error = buf.error; | 129 | error = buf.error; |
130 | lastdirent = buf.previous; | 130 | lastdirent = buf.previous; |
131 | if (lastdirent) { | 131 | if (lastdirent) { |
132 | if (put_user(arg.file->f_pos, &lastdirent->d_off)) | 132 | if (put_user(buf.ctx.pos, &lastdirent->d_off)) |
133 | error = -EFAULT; | 133 | error = -EFAULT; |
134 | else | 134 | else |
135 | error = count - buf.count; | 135 | error = count - buf.count; |
diff --git a/fs/coda/dir.c b/fs/coda/dir.c index b7d3a05c062c..fc66861b3598 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c | |||
@@ -391,8 +391,7 @@ static int coda_readdir(struct file *coda_file, void *buf, filldir_t filldir) | |||
391 | if (!host_file->f_op) | 391 | if (!host_file->f_op) |
392 | return -ENOTDIR; | 392 | return -ENOTDIR; |
393 | 393 | ||
394 | if (host_file->f_op->readdir) | 394 | if (host_file->f_op->readdir) { |
395 | { | ||
396 | /* potemkin case: we were handed a directory inode. | 395 | /* potemkin case: we were handed a directory inode. |
397 | * We can't use vfs_readdir because we have to keep the file | 396 | * We can't use vfs_readdir because we have to keep the file |
398 | * position in sync between the coda_file and the host_file. | 397 | * position in sync between the coda_file and the host_file. |
@@ -410,8 +409,20 @@ static int coda_readdir(struct file *coda_file, void *buf, filldir_t filldir) | |||
410 | 409 | ||
411 | coda_file->f_pos = host_file->f_pos; | 410 | coda_file->f_pos = host_file->f_pos; |
412 | mutex_unlock(&host_inode->i_mutex); | 411 | mutex_unlock(&host_inode->i_mutex); |
413 | } | 412 | } else if (host_file->f_op->iterate) { |
414 | else /* Venus: we must read Venus dirents from a file */ | 413 | struct inode *host_inode = file_inode(host_file); |
414 | struct dir_context *ctx = buf; | ||
415 | |||
416 | mutex_lock(&host_inode->i_mutex); | ||
417 | ret = -ENOENT; | ||
418 | if (!IS_DEADDIR(host_inode)) { | ||
419 | ret = host_file->f_op->iterate(host_file, ctx); | ||
420 | file_accessed(host_file); | ||
421 | } | ||
422 | mutex_unlock(&host_inode->i_mutex); | ||
423 | |||
424 | coda_file->f_pos = ctx->pos; | ||
425 | } else /* Venus: we must read Venus dirents from a file */ | ||
415 | ret = coda_venus_readdir(coda_file, buf, filldir); | 426 | ret = coda_venus_readdir(coda_file, buf, filldir); |
416 | 427 | ||
417 | return ret; | 428 | return ret; |
diff --git a/fs/compat.c b/fs/compat.c index 2279b59e81f2..69ca1e301766 100644 --- a/fs/compat.c +++ b/fs/compat.c | |||
@@ -975,7 +975,7 @@ asmlinkage long compat_sys_getdents(unsigned int fd, | |||
975 | error = buf.error; | 975 | error = buf.error; |
976 | lastdirent = buf.previous; | 976 | lastdirent = buf.previous; |
977 | if (lastdirent) { | 977 | if (lastdirent) { |
978 | if (put_user(f.file->f_pos, &lastdirent->d_off)) | 978 | if (put_user(buf.ctx.pos, &lastdirent->d_off)) |
979 | error = -EFAULT; | 979 | error = -EFAULT; |
980 | else | 980 | else |
981 | error = count - buf.count; | 981 | error = count - buf.count; |
@@ -1062,7 +1062,7 @@ asmlinkage long compat_sys_getdents64(unsigned int fd, | |||
1062 | error = buf.error; | 1062 | error = buf.error; |
1063 | lastdirent = buf.previous; | 1063 | lastdirent = buf.previous; |
1064 | if (lastdirent) { | 1064 | if (lastdirent) { |
1065 | typeof(lastdirent->d_off) d_off = f.file->f_pos; | 1065 | typeof(lastdirent->d_off) d_off = buf.ctx.pos; |
1066 | if (__put_user_unaligned(d_off, &lastdirent->d_off)) | 1066 | if (__put_user_unaligned(d_off, &lastdirent->d_off)) |
1067 | error = -EFAULT; | 1067 | error = -EFAULT; |
1068 | else | 1068 | else |
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index 7cb190426cec..6c8ef1dd4bdf 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c | |||
@@ -272,7 +272,7 @@ static int get_name(const struct path *path, char *name, struct dentry *child) | |||
272 | goto out; | 272 | goto out; |
273 | 273 | ||
274 | error = -EINVAL; | 274 | error = -EINVAL; |
275 | if (!file->f_op->readdir) | 275 | if (!file->f_op->readdir && !file->f_op->iterate) |
276 | goto out_close; | 276 | goto out_close; |
277 | 277 | ||
278 | buffer.name = name; | 278 | buffer.name = name; |
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 4f8cc6ba7c28..2fa2e2eb190b 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c | |||
@@ -240,11 +240,16 @@ struct name_list { | |||
240 | struct list_head list; | 240 | struct list_head list; |
241 | }; | 241 | }; |
242 | 242 | ||
243 | struct nfs4_dir_ctx { | ||
244 | struct dir_context ctx; | ||
245 | struct list_head names; | ||
246 | }; | ||
247 | |||
243 | static int | 248 | static int |
244 | nfsd4_build_namelist(void *arg, const char *name, int namlen, | 249 | nfsd4_build_namelist(void *arg, const char *name, int namlen, |
245 | loff_t offset, u64 ino, unsigned int d_type) | 250 | loff_t offset, u64 ino, unsigned int d_type) |
246 | { | 251 | { |
247 | struct list_head *names = arg; | 252 | struct nfs4_dir_ctx *ctx = arg; |
248 | struct name_list *entry; | 253 | struct name_list *entry; |
249 | 254 | ||
250 | if (namlen != HEXDIR_LEN - 1) | 255 | if (namlen != HEXDIR_LEN - 1) |
@@ -254,7 +259,7 @@ nfsd4_build_namelist(void *arg, const char *name, int namlen, | |||
254 | return -ENOMEM; | 259 | return -ENOMEM; |
255 | memcpy(entry->name, name, HEXDIR_LEN - 1); | 260 | memcpy(entry->name, name, HEXDIR_LEN - 1); |
256 | entry->name[HEXDIR_LEN - 1] = '\0'; | 261 | entry->name[HEXDIR_LEN - 1] = '\0'; |
257 | list_add(&entry->list, names); | 262 | list_add(&entry->list, &ctx->names); |
258 | return 0; | 263 | return 0; |
259 | } | 264 | } |
260 | 265 | ||
@@ -263,10 +268,7 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn) | |||
263 | { | 268 | { |
264 | const struct cred *original_cred; | 269 | const struct cred *original_cred; |
265 | struct dentry *dir = nn->rec_file->f_path.dentry; | 270 | struct dentry *dir = nn->rec_file->f_path.dentry; |
266 | struct { | 271 | struct nfs4_dir_ctx ctx; |
267 | struct dir_context ctx; | ||
268 | struct list_head names; | ||
269 | } ctx; | ||
270 | int status; | 272 | int status; |
271 | 273 | ||
272 | status = nfs4_save_creds(&original_cred); | 274 | status = nfs4_save_creds(&original_cred); |
diff --git a/fs/readdir.c b/fs/readdir.c index 5b620a2b45e6..5d6578affbbf 100644 --- a/fs/readdir.c +++ b/fs/readdir.c | |||
@@ -24,7 +24,7 @@ int iterate_dir(struct file *file, struct dir_context *ctx) | |||
24 | { | 24 | { |
25 | struct inode *inode = file_inode(file); | 25 | struct inode *inode = file_inode(file); |
26 | int res = -ENOTDIR; | 26 | int res = -ENOTDIR; |
27 | if (!file->f_op || !file->f_op->readdir) | 27 | if (!file->f_op || (!file->f_op->readdir && !file->f_op->iterate)) |
28 | goto out; | 28 | goto out; |
29 | 29 | ||
30 | res = security_file_permission(file, MAY_READ); | 30 | res = security_file_permission(file, MAY_READ); |
@@ -37,7 +37,14 @@ int iterate_dir(struct file *file, struct dir_context *ctx) | |||
37 | 37 | ||
38 | res = -ENOENT; | 38 | res = -ENOENT; |
39 | if (!IS_DEADDIR(inode)) { | 39 | if (!IS_DEADDIR(inode)) { |
40 | res = file->f_op->readdir(file, ctx, ctx->actor); | 40 | if (file->f_op->iterate) { |
41 | ctx->pos = file->f_pos; | ||
42 | res = file->f_op->iterate(file, ctx); | ||
43 | file->f_pos = ctx->pos; | ||
44 | } else { | ||
45 | res = file->f_op->readdir(file, ctx, ctx->actor); | ||
46 | ctx->pos = file->f_pos; | ||
47 | } | ||
41 | file_accessed(file); | 48 | file_accessed(file); |
42 | } | 49 | } |
43 | mutex_unlock(&inode->i_mutex); | 50 | mutex_unlock(&inode->i_mutex); |
@@ -214,7 +221,7 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd, | |||
214 | error = buf.error; | 221 | error = buf.error; |
215 | lastdirent = buf.previous; | 222 | lastdirent = buf.previous; |
216 | if (lastdirent) { | 223 | if (lastdirent) { |
217 | if (put_user(f.file->f_pos, &lastdirent->d_off)) | 224 | if (put_user(buf.ctx.pos, &lastdirent->d_off)) |
218 | error = -EFAULT; | 225 | error = -EFAULT; |
219 | else | 226 | else |
220 | error = count - buf.count; | 227 | error = count - buf.count; |
@@ -296,7 +303,7 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd, | |||
296 | error = buf.error; | 303 | error = buf.error; |
297 | lastdirent = buf.previous; | 304 | lastdirent = buf.previous; |
298 | if (lastdirent) { | 305 | if (lastdirent) { |
299 | typeof(lastdirent->d_off) d_off = f.file->f_pos; | 306 | typeof(lastdirent->d_off) d_off = buf.ctx.pos; |
300 | if (__put_user(d_off, &lastdirent->d_off)) | 307 | if (__put_user(d_off, &lastdirent->d_off)) |
301 | error = -EFAULT; | 308 | error = -EFAULT; |
302 | else | 309 | else |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 643e5b6cbaf5..b9641ae68da8 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -1508,7 +1508,15 @@ int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags); | |||
1508 | typedef int (*filldir_t)(void *, const char *, int, loff_t, u64, unsigned); | 1508 | typedef int (*filldir_t)(void *, const char *, int, loff_t, u64, unsigned); |
1509 | struct dir_context { | 1509 | struct dir_context { |
1510 | filldir_t actor; | 1510 | filldir_t actor; |
1511 | loff_t pos; | ||
1511 | }; | 1512 | }; |
1513 | |||
1514 | static inline bool dir_emit(struct dir_context *ctx, | ||
1515 | const char *name, int namelen, | ||
1516 | u64 ino, unsigned type) | ||
1517 | { | ||
1518 | return ctx->actor(ctx, name, namelen, ctx->pos, ino, type) == 0; | ||
1519 | } | ||
1512 | struct block_device_operations; | 1520 | struct block_device_operations; |
1513 | 1521 | ||
1514 | /* These macros are for out of kernel modules to test that | 1522 | /* These macros are for out of kernel modules to test that |
@@ -1525,6 +1533,7 @@ struct file_operations { | |||
1525 | ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); | 1533 | ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); |
1526 | ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); | 1534 | ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); |
1527 | int (*readdir) (struct file *, void *, filldir_t); | 1535 | int (*readdir) (struct file *, void *, filldir_t); |
1536 | int (*iterate) (struct file *, struct dir_context *); | ||
1528 | unsigned int (*poll) (struct file *, struct poll_table_struct *); | 1537 | unsigned int (*poll) (struct file *, struct poll_table_struct *); |
1529 | long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); | 1538 | long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); |
1530 | long (*compat_ioctl) (struct file *, unsigned int, unsigned long); | 1539 | long (*compat_ioctl) (struct file *, unsigned int, unsigned long); |