aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/fuse/dev.c9
-rw-r--r--fs/fuse/dir.c84
-rw-r--r--fs/fuse/file.c38
-rw-r--r--fs/fuse/fuse_i.h22
-rw-r--r--include/linux/fuse.h10
5 files changed, 73 insertions, 90 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index ca6fc0e96d7c..e4ada021d087 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -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);
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 65da6e1b6de5..cf5d1faed7af 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -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)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 6bc3fb26de39..224453557cf6 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -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);
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 0af1ac646927..8593d5bae7a6 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -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;
@@ -334,6 +329,23 @@ 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
*/
void fuse_init_file_inode(struct inode *inode);
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index e9b814e16c58..cdfaa51b9018 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -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;