[PATCH] fuse: transfer readdir data through device
authorMiklos Szeredi <miklos@szeredi.hu>
Fri, 9 Sep 2005 20:10:36 +0000 (13:10 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Fri, 9 Sep 2005 21:03:47 +0000 (14:03 -0700)
This patch removes a long lasting "hack" in FUSE, which used a separate
channel (a file descriptor refering to a disk-file) to transfer directory
contents from userspace to the kernel.

The patch adds three new operations (OPENDIR, READDIR, RELEASEDIR), which
have semantics and implementation exactly maching the respective file
operations (OPEN, READ, RELEASE).

This simplifies the directory reading code.  Also disk space is not
necessary, which can be important in embedded systems.

Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/fuse/dev.c
fs/fuse/dir.c
fs/fuse/file.c
fs/fuse/fuse_i.h
include/linux/fuse.h

index ca6fc0e96d7c77762e26dd96a106541f07e3c529..e4ada021d0876ec0f13dab80935bc65d8bd8d8b3 100644 (file)
@@ -731,13 +731,6 @@ static struct fuse_req *request_find(struct fuse_conn *fc, u64 unique)
        return NULL;
 }
 
-/* fget() needs to be done in this context */
-static void process_getdir(struct fuse_req *req)
-{
-       struct fuse_getdir_out_i *arg = req->out.args[0].value;
-       arg->file = fget(arg->fd);
-}
-
 static int copy_out_args(struct fuse_copy_state *cs, struct fuse_out *out,
                         unsigned nbytes)
 {
@@ -817,8 +810,6 @@ static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov,
        if (!err) {
                if (req->interrupted)
                        err = -ENOENT;
-               else if (req->in.h.opcode == FUSE_GETDIR && !oh.error)
-                       process_getdir(req);
        } else if (!req->interrupted)
                req->out.h.error = -EIO;
        request_end(fc, req);
index 65da6e1b6de54d88ad13f8d241e0219fec128eec..cf5d1faed7af44b64993cfd085c5e668836a9dc8 100644 (file)
@@ -519,70 +519,40 @@ static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
        return 0;
 }
 
-static int fuse_checkdir(struct file *cfile, struct file *file)
+static inline size_t fuse_send_readdir(struct fuse_req *req, struct file *file,
+                                      struct inode *inode, loff_t pos,
+                                      size_t count)
 {
-       struct inode *inode;
-       if (!cfile)
-               return -EIO;
-       inode = cfile->f_dentry->d_inode;
-       if (!S_ISREG(inode->i_mode)) {
-               fput(cfile);
-               return -EIO;
-       }
-
-       file->private_data = cfile;
-       return 0;
+       return fuse_send_read_common(req, file, inode, pos, count, 1);
 }
 
-static int fuse_getdir(struct file *file)
+static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
 {
+       int err;
+       size_t nbytes;
+       struct page *page;
        struct inode *inode = file->f_dentry->d_inode;
        struct fuse_conn *fc = get_fuse_conn(inode);
-       struct fuse_req *req = fuse_get_request(fc);
-       struct fuse_getdir_out_i outarg;
-       int err;
-
+       struct fuse_req *req = fuse_get_request_nonint(fc);
        if (!req)
-               return -ERESTARTNOINTR;
+               return -EINTR;
 
-       req->in.h.opcode = FUSE_GETDIR;
-       req->in.h.nodeid = get_node_id(inode);
-       req->inode = inode;
-       req->out.numargs = 1;
-       req->out.args[0].size = sizeof(struct fuse_getdir_out);
-       req->out.args[0].value = &outarg;
-       request_send(fc, req);
+       page = alloc_page(GFP_KERNEL);
+       if (!page) {
+               fuse_put_request(fc, req);
+               return -ENOMEM;
+       }
+       req->num_pages = 1;
+       req->pages[0] = page;
+       nbytes = fuse_send_readdir(req, file, inode, file->f_pos, PAGE_SIZE);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (!err)
-               err = fuse_checkdir(outarg.file, file);
-       return err;
-}
-
-static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
-{
-       struct file *cfile = file->private_data;
-       char *buf;
-       int ret;
-
-       if (!cfile) {
-               ret = fuse_getdir(file);
-               if (ret)
-                       return ret;
-
-               cfile = file->private_data;
-       }
+               err = parse_dirfile(page_address(page), nbytes, file, dstbuf,
+                                   filldir);
 
-       buf = (char *) __get_free_page(GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       ret = kernel_read(cfile, file->f_pos, buf, PAGE_SIZE);
-       if (ret > 0)
-               ret = parse_dirfile(buf, ret, file, dstbuf, filldir);
-
-       free_page((unsigned long) buf);
-       return ret;
+       __free_page(page);
+       return err;
 }
 
 static char *read_link(struct dentry *dentry)
@@ -637,18 +607,12 @@ static void fuse_put_link(struct dentry *dentry, struct nameidata *nd, void *c)
 
 static int fuse_dir_open(struct inode *inode, struct file *file)
 {
-       file->private_data = NULL;
-       return 0;
+       return fuse_open_common(inode, file, 1);
 }
 
 static int fuse_dir_release(struct inode *inode, struct file *file)
 {
-       struct file *cfile = file->private_data;
-
-       if (cfile)
-               fput(cfile);
-
-       return 0;
+       return fuse_release_common(inode, file, 1);
 }
 
 static unsigned iattr_to_fattr(struct iattr *iattr, struct fuse_attr *fattr)
index 6bc3fb26de39970d6edfb3f81aabcc333acea132..224453557cf657698700aafd0630038cba00172a 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/slab.h>
 #include <linux/kernel.h>
 
-static int fuse_open(struct inode *inode, struct file *file)
+int fuse_open_common(struct inode *inode, struct file *file, int isdir)
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_req *req;
@@ -56,7 +56,7 @@ static int fuse_open(struct inode *inode, struct file *file)
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
-       req->in.h.opcode = FUSE_OPEN;
+       req->in.h.opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN;
        req->in.h.nodeid = get_node_id(inode);
        req->inode = inode;
        req->in.numargs = 1;
@@ -85,7 +85,7 @@ static int fuse_open(struct inode *inode, struct file *file)
        return err;
 }
 
-static int fuse_release(struct inode *inode, struct file *file)
+int fuse_release_common(struct inode *inode, struct file *file, int isdir)
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_file *ff = file->private_data;
@@ -94,7 +94,7 @@ static int fuse_release(struct inode *inode, struct file *file)
 
        inarg->fh = ff->fh;
        inarg->flags = file->f_flags & ~O_EXCL;
-       req->in.h.opcode = FUSE_RELEASE;
+       req->in.h.opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE;
        req->in.h.nodeid = get_node_id(inode);
        req->inode = inode;
        req->in.numargs = 1;
@@ -107,6 +107,16 @@ static int fuse_release(struct inode *inode, struct file *file)
        return 0;
 }
 
+static int fuse_open(struct inode *inode, struct file *file)
+{
+       return fuse_open_common(inode, file, 0);
+}
+
+static int fuse_release(struct inode *inode, struct file *file)
+{
+       return fuse_release_common(inode, file, 0);
+}
+
 static int fuse_flush(struct file *file)
 {
        struct inode *inode = file->f_dentry->d_inode;
@@ -178,8 +188,9 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
        return err;
 }
 
-static ssize_t fuse_send_read(struct fuse_req *req, struct file *file,
-                             struct inode *inode, loff_t pos,  size_t count)
+size_t fuse_send_read_common(struct fuse_req *req, struct file *file,
+                            struct inode *inode, loff_t pos, size_t count,
+                            int isdir)
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_file *ff = file->private_data;
@@ -189,7 +200,7 @@ static ssize_t fuse_send_read(struct fuse_req *req, struct file *file,
        inarg.fh = ff->fh;
        inarg.offset = pos;
        inarg.size = count;
-       req->in.h.opcode = FUSE_READ;
+       req->in.h.opcode = isdir ? FUSE_READDIR : FUSE_READ;
        req->in.h.nodeid = get_node_id(inode);
        req->inode = inode;
        req->file = file;
@@ -204,6 +215,13 @@ static ssize_t fuse_send_read(struct fuse_req *req, struct file *file,
        return req->out.args[0].size;
 }
 
+static inline size_t fuse_send_read(struct fuse_req *req, struct file *file,
+                                   struct inode *inode, loff_t pos,
+                                   size_t count)
+{
+       return fuse_send_read_common(req, file, inode, pos, count, 0);
+}
+
 static int fuse_readpage(struct file *file, struct page *page)
 {
        struct inode *inode = page->mapping->host;
@@ -293,8 +311,8 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
        return err;
 }
 
-static ssize_t fuse_send_write(struct fuse_req *req, struct file *file,
-                              struct inode *inode, loff_t pos, size_t count)
+static size_t fuse_send_write(struct fuse_req *req, struct file *file,
+                             struct inode *inode, loff_t pos, size_t count)
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_file *ff = file->private_data;
@@ -332,7 +350,7 @@ static int fuse_commit_write(struct file *file, struct page *page,
                             unsigned offset, unsigned to)
 {
        int err;
-       ssize_t nres;
+       size_t nres;
        unsigned count = to - offset;
        struct inode *inode = page->mapping->host;
        struct fuse_conn *fc = get_fuse_conn(inode);
index 0af1ac64692781d4b4bd3744c37b8ac3e7447317..8593d5bae7a604284605df0799f94425023c4bdd 100644 (file)
@@ -273,11 +273,6 @@ struct fuse_conn {
        struct backing_dev_info bdi;
 };
 
-struct fuse_getdir_out_i {
-       int fd;
-       void *file; /* Used by kernel only */
-};
-
 static inline struct fuse_conn **get_fuse_conn_super_p(struct super_block *sb)
 {
        return (struct fuse_conn **) &sb->s_fs_info;
@@ -333,6 +328,23 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
 void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
                      unsigned long nodeid, u64 nlookup);
 
+/**
+ * Send READ or READDIR request
+ */
+size_t fuse_send_read_common(struct fuse_req *req, struct file *file,
+                            struct inode *inode, loff_t pos, size_t count,
+                            int isdir);
+
+/**
+ * Send OPEN or OPENDIR request
+ */
+int fuse_open_common(struct inode *inode, struct file *file, int isdir);
+
+/**
+ * Send RELEASE or RELEASEDIR request
+ */
+int fuse_release_common(struct inode *inode, struct file *file, int isdir);
+
 /**
  * Initialise file operations on a regular file
  */
index e9b814e16c5863b5ef6d68db23cd0bd7b2090186..cdfaa51b90186a5b9de05c1eca3c45e5cc62d3e2 100644 (file)
@@ -70,7 +70,6 @@ enum fuse_opcode {
        FUSE_SETATTR       = 4,
        FUSE_READLINK      = 5,
        FUSE_SYMLINK       = 6,
-       FUSE_GETDIR        = 7,
        FUSE_MKNOD         = 8,
        FUSE_MKDIR         = 9,
        FUSE_UNLINK        = 10,
@@ -88,7 +87,10 @@ enum fuse_opcode {
        FUSE_LISTXATTR     = 23,
        FUSE_REMOVEXATTR   = 24,
        FUSE_FLUSH         = 25,
-       FUSE_INIT          = 26
+       FUSE_INIT          = 26,
+       FUSE_OPENDIR       = 27,
+       FUSE_READDIR       = 28,
+       FUSE_RELEASEDIR    = 29
 };
 
 /* Conservative buffer size for the client */
@@ -120,10 +122,6 @@ struct fuse_attr_out {
        struct fuse_attr attr;
 };
 
-struct fuse_getdir_out {
-       __u32   fd;
-};
-
 struct fuse_mknod_in {
        __u32   mode;
        __u32   rdev;