The VFS permits filesystems to implement read(2) on directories, but FUSE unconditionally returns -EISDIR for it. Expose this to userspace as FUSE_DIR_READ and continue returning the old behavior if the filesystem doesn't implement it. Signed-off-by: Emily Maier <emily@xxxxxxxxxxxxxx> --- fs/fuse/dir.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++- fs/fuse/fuse_i.h | 6 ++++++ include/uapi/linux/fuse.h | 18 ++++++++++++++++- 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 4b855b6..dbae010 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1870,6 +1870,55 @@ static int fuse_removexattr(struct dentry *entry, const char *name) return err; } +static ssize_t fuse_dir_read(struct file *file, char __user *buf, size_t size, + loff_t *ppos) +{ + struct inode *inode = file_inode(file); + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_dir_read_in inarg; + ssize_t ret; + struct fuse_file *ff = file->private_data; + char *kbuf; + FUSE_ARGS(args); + + if (fc->no_dir_read || fc->minor < 25) + return -EISDIR; + + if (size > FUSE_DIR_READ_MAX) + size = FUSE_DIR_READ_MAX; + kbuf = kmalloc(size, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + memset(&inarg, 0, sizeof(inarg)); + inarg.size = size; + inarg.off = *ppos; + inarg.fh = ff->fh; + args.in.h.opcode = FUSE_DIR_READ; + args.in.h.nodeid = get_node_id(inode); + args.in.numargs = 1; + args.in.args[0].size = sizeof(inarg); + args.in.args[0].value = &inarg; + args.out.numargs = 1; + args.out.argvar = 1; + args.out.args[0].size = size; + args.out.args[0].value = kbuf; + ret = fuse_simple_request(fc, &args); + if (ret == -ENOSYS) { + fc->no_dir_read = 1; + ret = -EISDIR; + } + if (ret > 0) { + if (copy_to_user(buf, kbuf, ret)) + ret = -EFAULT; + else + *ppos += ret; + } + fuse_invalidate_atime(inode); + kfree(kbuf); + return ret; +} + static const struct inode_operations fuse_dir_inode_operations = { .lookup = fuse_lookup, .mkdir = fuse_mkdir, @@ -1892,7 +1941,7 @@ static const struct inode_operations fuse_dir_inode_operations = { static const struct file_operations fuse_dir_operations = { .llseek = generic_file_llseek, - .read = generic_read_dir, + .read = fuse_dir_read, .iterate = fuse_readdir, .open = fuse_dir_open, .release = fuse_dir_release, diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index eddbe02..a953910 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -48,6 +48,9 @@ /** Number of page pointers embedded in fuse_req */ #define FUSE_REQ_INLINE_PAGES 1 +/** Maximum length of a read from a directory */ +#define FUSE_DIR_READ_MAX 4096 + /** List of active connections */ extern struct list_head fuse_conn_list; @@ -658,6 +661,9 @@ struct fuse_conn { /** List of device instances belonging to this connection */ struct list_head devices; + + /** Is dir_read not implemented by fs? */ + unsigned no_dir_read:1; }; static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb) diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 5974fae..50f5fa5 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -105,6 +105,9 @@ * * 7.24 * - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support + * + * 7.25 + * - add FUSE_DIR_READ */ #ifndef _LINUX_FUSE_H @@ -140,7 +143,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 24 +#define FUSE_KERNEL_MINOR_VERSION 25 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -362,6 +365,7 @@ enum fuse_opcode { FUSE_READDIRPLUS = 44, FUSE_RENAME2 = 45, FUSE_LSEEK = 46, + FUSE_DIR_READ = 47, /* CUSE specific operations */ CUSE_INIT = 4096, @@ -773,4 +777,16 @@ struct fuse_lseek_out { uint64_t offset; }; +struct fuse_dir_read_in { + uint32_t size; + uint32_t padding; + int64_t off; + uint64_t fh; +}; + +struct fuse_dir_read_out { + uint32_t size; + uint32_t padding; +}; + #endif /* _LINUX_FUSE_H */ -- 2.5.5 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html