aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/dev.c494
-rw-r--r--kernel/dir.c198
-rw-r--r--kernel/file.c63
-rw-r--r--kernel/fuse_i.h91
-rw-r--r--kernel/inode.c37
5 files changed, 526 insertions, 357 deletions
diff --git a/kernel/dev.c b/kernel/dev.c
index d4206e1..4395736 100644
--- a/kernel/dev.c
+++ b/kernel/dev.c
@@ -12,13 +12,39 @@
#include <linux/proc_fs.h>
#include <linux/file.h>
-#define IHSIZE sizeof(struct fuse_in_header)
-#define OHSIZE sizeof(struct fuse_out_header)
+/* If more requests are outstanding, then the operation will block */
+#define MAX_OUTSTANDING 10
static struct proc_dir_entry *proc_fs_fuse;
struct proc_dir_entry *proc_fuse_dev;
+static kmem_cache_t *fuse_req_cachep;
-static int interrupt_error(enum fuse_opcode opcode)
+static struct fuse_req *request_new(void)
+{
+ struct fuse_req *req;
+
+ req = (struct fuse_req *) kmem_cache_alloc(fuse_req_cachep, SLAB_NOFS);
+ if(req) {
+ INIT_LIST_HEAD(&req->list);
+ req->issync = 0;
+ req->locked = 0;
+ req->interrupted = 0;
+ req->sent = 0;
+ req->finished = 0;
+ req->in = NULL;
+ req->out = NULL;
+ init_waitqueue_head(&req->waitq);
+ }
+
+ return req;
+}
+
+static void request_free(struct fuse_req *req)
+{
+ kmem_cache_free(fuse_req_cachep, req);
+}
+
+static int request_restartable(enum fuse_opcode opcode)
{
switch(opcode) {
case FUSE_LOOKUP:
@@ -28,222 +54,214 @@ static int interrupt_error(enum fuse_opcode opcode)
case FUSE_OPEN:
case FUSE_READ:
case FUSE_WRITE:
- return -ERESTARTSYS;
+ return 1;
default:
- /* Operations which modify the filesystem cannot be safely
- restarted, because it is uncertain whether the
- operation has completed or not... */
- return -EINTR;
+ return 0;
}
}
-static int request_wait_answer(struct fuse_req *req)
+/* Called with fuse_lock held. Releases, and then reaquires it. */
+static void request_wait_answer(struct fuse_req *req)
{
- int ret = 0;
- DECLARE_WAITQUEUE(wait, current);
+ int intr;
+
+ spin_unlock(&fuse_lock);
+ intr = wait_event_interruptible(req->waitq, req->finished);
+ spin_lock(&fuse_lock);
+ if(!intr)
+ return;
- add_wait_queue(&req->waitq, &wait);
- while(!list_empty(&req->list)) {
- set_current_state(TASK_INTERRUPTIBLE);
- if(signal_pending(current)) {
- ret = interrupt_error(req->opcode);
- break;
- }
+ /* Request interrupted... Wait for it to be unlocked */
+ if(req->locked) {
+ req->interrupted = 1;
spin_unlock(&fuse_lock);
- schedule();
+ wait_event(req->waitq, !req->locked);
spin_lock(&fuse_lock);
}
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&req->waitq, &wait);
+
+ /* Operations which modify the filesystem cannot safely be
+ restarted, because it is uncertain whether the operation has
+ completed or not... */
+ if(req->sent && !request_restartable(req->in->h.opcode))
+ req->out->h.error = -EINTR;
+ else
+ req->out->h.error = -ERESTARTSYS;
+}
- return ret;
+static int get_unique(struct fuse_conn *fc)
+{
+ do fc->reqctr++;
+ while(!fc->reqctr);
+ return fc->reqctr;
}
-static int request_check(struct fuse_req *req, struct fuse_out *outp)
+void request_send(struct fuse_conn *fc, struct fuse_in *in,
+ struct fuse_out *out)
{
- struct fuse_out_header *oh;
- unsigned int size;
+ struct fuse_req *req;
- if(!req->out)
- return -ECONNABORTED;
+ out->h.error = -ERESTARTSYS;
+ if(down_interruptible(&fc->outstanding))
+ return;
- oh = (struct fuse_out_header *) req->out;
- size = req->outsize - OHSIZE;
-
- if (oh->error <= -512 || oh->error > 0) {
- printk("fuse: bad error value: %i\n", oh->error);
- return -EPROTO;
- }
+ out->h.error = -ENOMEM;
+ req = request_new();
+ if(!req)
+ return;
+
+ req->in = in;
+ req->out = out;
+ req->issync = 1;
- if(size > outp->argsize ||
- (oh->error == 0 && !outp->argvar && size != outp->argsize) ||
- (oh->error != 0 && size != 0)) {
- printk("fuse: invalid argument length: %i (%i)\n", size,
- req->opcode);
- return -EPROTO;
+ spin_lock(&fuse_lock);
+ out->h.error = -ENOTCONN;
+ if(fc->file) {
+ in->h.unique = get_unique(fc);
+ list_add_tail(&req->list, &fc->pending);
+ wake_up(&fc->waitq);
+ request_wait_answer(req);
+ list_del(&req->list);
}
-
- memcpy(&outp->h, oh, OHSIZE);
- outp->argsize = size;
- if(size)
- memcpy(outp->arg, req->out + OHSIZE, size);
-
- return oh->error;
-}
+ spin_unlock(&fuse_lock);
+ request_free(req);
-static void request_free(struct fuse_req *req)
-{
- kfree(req->in);
- kfree(req->out);
- kfree(req);
+ up(&fc->outstanding);
}
-static struct fuse_req *request_new(struct fuse_conn *fc, struct fuse_in *inp,
- struct fuse_out *outp)
+static inline void destroy_request(struct fuse_conn *fc, struct fuse_req *req)
{
- struct fuse_req *req;
-
- req = kmalloc(sizeof(*req), GFP_NOFS);
- if(!req)
- return NULL;
+ if(req) {
+ int i;
- if(outp)
- req->outsize = OHSIZE + outp->argsize;
- else
- req->outsize = 0;
- req->out = NULL;
-
- req->insize = IHSIZE + inp->argsize;
- req->in = kmalloc(req->insize, GFP_NOFS);
- if(!req->in) {
+ for(i = 0; i < req->in->numargs; i++)
+ kfree(req->in->args[i].value);
+ kfree(req->in);
request_free(req);
- return NULL;
}
- memcpy(req->in, &inp->h, IHSIZE);
- if(inp->argsize)
- memcpy(req->in + IHSIZE, inp->arg, inp->argsize);
-
- req->opcode = inp->h.opcode;
- init_waitqueue_head(&req->waitq);
-
- return req;
}
-/* If 'outp' is NULL then the request this is asynchronous */
-void request_send(struct fuse_conn *fc, struct fuse_in *inp,
- struct fuse_out *outp)
+/* This one is currently only used for sending the FORGET request, which is
+ a kernel initiated request. So the outstanding semaphore is not used. */
+int request_send_noreply(struct fuse_conn *fc, struct fuse_in *in)
{
- int ret;
- struct fuse_in_header *ih;
struct fuse_req *req;
- ret = -ENOMEM;
- req = request_new(fc, inp, outp);
+ req = request_new();
if(!req)
- goto out;
+ return -ENOMEM;
+
+ req->in = in;
+ req->issync = 0;
spin_lock(&fuse_lock);
- ret = -ENOTCONN;
- if(!fc->file)
- goto out_unlock_free;
-
- ih = (struct fuse_in_header *) req->in;
- if(outp) {
- do fc->reqctr++;
- while(!fc->reqctr);
- ih->unique = req->unique = fc->reqctr;
+ if(!fc->file) {
+ spin_unlock(&fuse_lock);
+ request_free(req);
+ return -ENOTCONN;
}
- else
- ih->unique = req->unique = 0;
list_add_tail(&req->list, &fc->pending);
wake_up(&fc->waitq);
-
- /* Async reqests are freed in fuse_dev_read() */
- if(!outp)
- goto out_unlock;
-
- ret = request_wait_answer(req);
- list_del(&req->list);
- if(!ret)
- ret = request_check(req, outp);
-
- out_unlock_free:
- request_free(req);
- out_unlock:
spin_unlock(&fuse_lock);
- out:
- if(outp)
- outp->h.error = ret;
+ return 0;
}
-static int request_wait(struct fuse_conn *fc)
+static void request_wait(struct fuse_conn *fc)
{
- int ret = 0;
DECLARE_WAITQUEUE(wait, current);
-
+
add_wait_queue_exclusive(&fc->waitq, &wait);
while(list_empty(&fc->pending)) {
set_current_state(TASK_INTERRUPTIBLE);
- if(signal_pending(current)) {
- ret = -ERESTARTSYS;
+ if(signal_pending(current))
break;
- }
+
spin_unlock(&fuse_lock);
schedule();
spin_lock(&fuse_lock);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&fc->waitq, &wait);
+}
- return ret;
+static inline int copy_in_one(const void *src, size_t srclen, char **dstp,
+ size_t *dstlenp)
+{
+ if(*dstlenp < srclen) {
+ printk("fuse_dev_read: buffer too small\n");
+ return -EIO;
+ }
+
+ if(copy_to_user(*dstp, src, srclen))
+ return -EFAULT;
+
+ *dstp += srclen;
+ *dstlenp -= srclen;
+
+ return 0;
}
+static inline int copy_in_args(struct fuse_in *in, char *buf, size_t nbytes)
+{
+ int err;
+ int i;
+ size_t orignbytes = nbytes;
+
+ err = copy_in_one(&in->h, sizeof(in->h), &buf, &nbytes);
+ if(err)
+ return err;
+
+ for(i = 0; i < in->numargs; i++) {
+ struct fuse_in_arg *arg = &in->args[i];
+ err = copy_in_one(arg->value, arg->size, &buf, &nbytes);
+ if(err)
+ return err;
+ }
+
+ return orignbytes - nbytes;
+}
static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes,
loff_t *off)
{
- int ret;
+ ssize_t ret;
struct fuse_conn *fc = DEV_FC(file);
- struct fuse_req *req;
- char *tmpbuf;
- unsigned int size;
+ struct fuse_req *req = NULL;
if(fc->sb == NULL)
return -EPERM;
-
+
spin_lock(&fuse_lock);
- ret = request_wait(fc);
- if(ret)
- goto err;
-
- req = list_entry(fc->pending.next, struct fuse_req, list);
- size = req->insize;
- if(nbytes < size) {
- printk("fuse_dev_read: buffer too small\n");
- ret = -EIO;
- goto err;
+ request_wait(fc);
+ if(!list_empty(&fc->pending)) {
+ req = list_entry(fc->pending.next, struct fuse_req, list);
+ list_del_init(&req->list);
+ req->locked = 1;
}
- tmpbuf = req->in;
- req->in = NULL;
-
- list_del(&req->list);
- if(req->outsize)
- list_add_tail(&req->list, &fc->processing);
- else
- request_free(req);
spin_unlock(&fuse_lock);
+ if(req == NULL)
+ return -ERESTARTSYS;
- if(copy_to_user(buf, tmpbuf, size))
- return -EFAULT;
-
- kfree(tmpbuf);
- return size;
-
- err:
+ ret = copy_in_args(req->in, buf, nbytes);
+ spin_lock(&fuse_lock);
+ if(req->issync || ret < 0) {
+ if(ret < 0)
+ list_add_tail(&req->list, &fc->pending);
+ else {
+ list_add_tail(&req->list, &fc->processing);
+ req->sent = 1;
+ }
+ req->locked = 0;
+ if(req->interrupted)
+ wake_up(&req->waitq);
+
+ req = NULL;
+ }
spin_unlock(&fuse_lock);
+ destroy_request(fc, req);
+
return ret;
}
@@ -255,7 +273,7 @@ static struct fuse_req *request_find(struct fuse_conn *fc, unsigned int unique)
list_for_each(entry, &fc->processing) {
struct fuse_req *tmp;
tmp = list_entry(entry, struct fuse_req, list);
- if(tmp->unique == unique) {
+ if(tmp->in->h.unique == unique) {
req = tmp;
break;
}
@@ -264,58 +282,135 @@ static struct fuse_req *request_find(struct fuse_conn *fc, unsigned int unique)
return req;
}
+static void process_getdir(struct fuse_req *req)
+{
+ struct fuse_getdir_out *arg;
+ arg = (struct fuse_getdir_out *) req->out->args[0].value;
+ arg->file = fget(arg->fd);
+}
+
+static inline int copy_out_one(struct fuse_out_arg *arg, const char **srcp,
+ size_t *srclenp, int allowvar)
+{
+ size_t dstlen = arg->size;
+ if(*srclenp < dstlen) {
+ if(!allowvar) {
+ printk("fuse_dev_write: write is short\n");
+ return -EIO;
+ }
+ dstlen = *srclenp;
+ }
+
+ if(dstlen) {
+ if(copy_from_user(arg->value, *srcp, dstlen))
+ return -EFAULT;
+ }
+
+ *srcp += dstlen;
+ *srclenp -= dstlen;
+ arg->size = dstlen;
+
+ return 0;
+}
+
+static inline int copy_out_args(struct fuse_out *out, const char *buf,
+ size_t nbytes)
+{
+ int err;
+ int i;
+
+ buf += sizeof(struct fuse_out_header);
+ nbytes -= sizeof(struct fuse_out_header);
+
+ if(!out->h.error) {
+ for(i = 0; i < out->numargs; i++) {
+ struct fuse_out_arg *arg = &out->args[i];
+ int allowvar;
+
+ if(out->argvar && i == out->numargs - 1)
+ allowvar = 1;
+ else
+ allowvar = 0;
+
+ err = copy_out_one(arg, &buf, &nbytes, allowvar);
+ if(err)
+ return err;
+ }
+ }
+
+ if(nbytes != 0) {
+ printk("fuse_dev_write: write is long\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static inline int copy_out_header(struct fuse_out_header *oh, const char *buf,
+ size_t nbytes)
+{
+ if(nbytes < sizeof(struct fuse_out_header)) {
+ printk("fuse_dev_write: write is short\n");
+ return -EIO;
+ }
+
+ if(copy_from_user(oh, buf, sizeof(struct fuse_out_header)))
+ return -EFAULT;
+
+ if (oh->error <= -512 || oh->error > 0) {
+ printk("fuse_dev_write: bad error value\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
static ssize_t fuse_dev_write(struct file *file, const char *buf,
size_t nbytes, loff_t *off)
{
- ssize_t ret;
+ int err;
struct fuse_conn *fc = DEV_FC(file);
struct fuse_req *req;
- char *tmpbuf;
- struct fuse_out_header *oh;
+ struct fuse_out_header oh;
if(!fc->sb)
return -EPERM;
- ret = -EIO;
- if(nbytes < OHSIZE || nbytes > OHSIZE + PAGE_SIZE) {
- printk("fuse_dev_write: write is short or long\n");
- goto out;
- }
-
- ret = -ENOMEM;
- tmpbuf = kmalloc(nbytes, GFP_NOFS);
- if(!tmpbuf)
- goto out;
-
- ret = -EFAULT;
- if(copy_from_user(tmpbuf, buf, nbytes))
- goto out_free;
+ err = copy_out_header(&oh, buf, nbytes);
+ if(err)
+ return err;
spin_lock(&fuse_lock);
- oh = (struct fuse_out_header *) tmpbuf;
- req = request_find(fc, oh->unique);
- if(req == NULL) {
- ret = -ENOENT;
- goto out_free_unlock;
+ req = request_find(fc, oh.unique);
+ if(req != NULL) {
+ list_del_init(&req->list);
+ req->locked = 1;
}
- list_del_init(&req->list);
- if(req->opcode == FUSE_GETDIR) {
+ spin_unlock(&fuse_lock);
+ if(!req)
+ return -ENOENT;
+
+ req->out->h = oh;
+ err = copy_out_args(req->out, buf, nbytes);
+
+ spin_lock(&fuse_lock);
+ if(err)
+ list_add_tail(&fc->processing, &req->list);
+ else {
/* fget() needs to be done in this context */
- struct fuse_getdir_out *arg;
- arg = (struct fuse_getdir_out *) (tmpbuf + OHSIZE);
- arg->file = fget(arg->fd);
- }
- req->out = tmpbuf;
- req->outsize = nbytes;
- tmpbuf = NULL;
- ret = nbytes;
- wake_up(&req->waitq);
- out_free_unlock:
+ if(req->in->h.opcode == FUSE_GETDIR && !oh.error)
+ process_getdir(req);
+ req->finished = 1;
+ }
+ req->locked = 0;
+ if(!err || req->interrupted)
+ wake_up(&req->waitq);
spin_unlock(&fuse_lock);
- out_free:
- kfree(tmpbuf);
- out:
- return ret;
+
+ if(!err)
+ return nbytes;
+ else
+ return err;
}
@@ -350,6 +445,7 @@ static struct fuse_conn *new_conn(void)
init_waitqueue_head(&fc->waitq);
INIT_LIST_HEAD(&fc->pending);
INIT_LIST_HEAD(&fc->processing);
+ sema_init(&fc->outstanding, MAX_OUTSTANDING);
fc->reqctr = 1;
}
return fc;
@@ -369,16 +465,18 @@ static int fuse_dev_open(struct inode *inode, struct file *file)
return 0;
}
-static void end_requests(struct list_head *head)
+static void end_requests(struct fuse_conn *fc, struct list_head *head)
{
while(!list_empty(head)) {
struct fuse_req *req;
req = list_entry(head->next, struct fuse_req, list);
list_del_init(&req->list);
- if(req->outsize)
+ if(req->issync) {
+ req->out->h.error = -ECONNABORTED;
wake_up(&req->waitq);
+ }
else
- request_free(req);
+ destroy_request(fc, req);
}
}
@@ -388,8 +486,8 @@ static int fuse_dev_release(struct inode *inode, struct file *file)
spin_lock(&fuse_lock);
fc->file = NULL;
- end_requests(&fc->pending);
- end_requests(&fc->processing);
+ end_requests(fc, &fc->pending);
+ end_requests(fc, &fc->processing);
fuse_release_conn(fc);
spin_unlock(&fuse_lock);
return 0;
@@ -411,6 +509,12 @@ int fuse_dev_init()
proc_fs_fuse = NULL;
proc_fuse_dev = NULL;
+ fuse_req_cachep = kmem_cache_create("fuser_request",
+ sizeof(struct fuse_req),
+ 0, 0, NULL, NULL);
+ if(!fuse_req_cachep)
+ return -ENOMEM;
+
ret = -EIO;
proc_fs_fuse = proc_mkdir("fuse", proc_root_fs);
if(!proc_fs_fuse) {
@@ -440,6 +544,8 @@ void fuse_dev_cleanup()
remove_proc_entry("dev", proc_fs_fuse);
remove_proc_entry("fuse", proc_root_fs);
}
+
+ kmem_cache_destroy(fuse_req_cachep);
}
/*
diff --git a/kernel/dir.c b/kernel/dir.c
index 537b71c..a3f1485 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -79,29 +79,52 @@ struct inode *fuse_iget(struct super_block *sb, ino_t ino,
return inode;
}
+/* If the inode belongs to an existing directory, then it cannot be
+ assigned to new dentry */
+static int inode_ok(struct inode *inode)
+{
+ struct dentry *alias;
+ if(S_ISDIR(inode->i_mode) && (alias = d_find_alias(inode)) != NULL) {
+ dput(alias);
+ printk("fuse: cannot assign an existing directory\n");
+ return 0;
+
+ }
+ return 1;
+}
+
static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
{
int ret;
struct fuse_conn *fc = INO_FC(dir);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
- struct fuse_lookup_out arg;
+ struct fuse_lookup_out outarg;
struct inode *inode;
in.h.opcode = FUSE_LOOKUP;
in.h.ino = dir->i_ino;
- in.argsize = entry->d_name.len + 1;
- in.arg = entry->d_name.name;
- out.argsize = sizeof(arg);
- out.arg = &arg;
+ in.numargs = 1;
+ in.args[0].size = entry->d_name.len + 1;
+ in.args[0].value = entry->d_name.name;
+ out.numargs = 1;
+ out.args[0].size = sizeof(outarg);
+ out.args[0].value = &outarg;
request_send(fc, &in, &out);
inode = NULL;
if(!out.h.error) {
ret = -ENOMEM;
- inode = fuse_iget(dir->i_sb, arg.ino, &arg.attr, out.h.unique);
+ inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr,
+ out.h.unique);
if(!inode)
goto err;
+
+ ret = -EPROTO;
+ if(!inode_ok(inode)) {
+ iput(inode);
+ goto err;
+ }
}
else if(out.h.error != -ENOENT) {
ret = out.h.error;
@@ -126,28 +149,25 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
struct fuse_conn *fc = INO_FC(dir);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
- struct fuse_mknod_in *inarg;
- unsigned int insize;
+ struct fuse_mknod_in inarg;
struct fuse_mknod_out outarg;
struct inode *inode;
-
- insize = offsetof(struct fuse_mknod_in, name) + entry->d_name.len + 1;
- inarg = kmalloc(insize, GFP_KERNEL);
- if(!inarg)
- return -ENOMEM;
-
- inarg->mode = mode;
- inarg->rdev = rdev;
- strcpy(inarg->name, entry->d_name.name);
+
+ memset(&inarg, 0, sizeof(inarg));
+ inarg.mode = mode;
+ inarg.rdev = rdev;
in.h.opcode = FUSE_MKNOD;
in.h.ino = dir->i_ino;
- in.argsize = insize;
- in.arg = inarg;
- out.argsize = sizeof(outarg);
- out.arg = &outarg;
+ in.numargs = 2;
+ in.args[0].size = sizeof(inarg);
+ in.args[0].value = &inarg;
+ in.args[1].size = entry->d_name.len + 1;
+ in.args[1].value = entry->d_name.name;
+ out.numargs = 1;
+ out.args[0].size = sizeof(outarg);
+ out.args[0].value = &outarg;
request_send(fc, &in, &out);
- kfree(inarg);
if(out.h.error)
return out.h.error;
@@ -156,6 +176,18 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
if(!inode)
return -ENOMEM;
+ /* Don't allow userspace to do really stupid things... */
+ if((inode->i_mode ^ mode) & S_IFMT) {
+ iput(inode);
+ printk("fuse_mknod: inode has wrong type\n");
+ return -EPROTO;
+ }
+
+ if(!inode_ok(inode)) {
+ iput(inode);
+ return -EPROTO;
+ }
+
d_instantiate(entry, inode);
return 0;
}
@@ -171,23 +203,19 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
struct fuse_conn *fc = INO_FC(dir);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
- struct fuse_mkdir_in *inarg;
- unsigned int insize;
-
- insize = offsetof(struct fuse_mkdir_in, name) + entry->d_name.len + 1;
- inarg = kmalloc(insize, GFP_KERNEL);
- if(!inarg)
- return -ENOMEM;
-
- inarg->mode = mode;
- strcpy(inarg->name, entry->d_name.name);
+ struct fuse_mkdir_in inarg;
+
+ memset(&inarg, 0, sizeof(inarg));
+ inarg.mode = mode;
in.h.opcode = FUSE_MKDIR;
in.h.ino = dir->i_ino;
- in.argsize = insize;
- in.arg = inarg;
+ in.numargs = 2;
+ in.args[0].size = sizeof(inarg);
+ in.args[0].value = &inarg;
+ in.args[1].size = entry->d_name.len + 1;
+ in.args[1].value = entry->d_name.name;
request_send(fc, &in, &out);
- kfree(inarg);
return out.h.error;
}
@@ -198,24 +226,16 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry,
struct fuse_conn *fc = INO_FC(dir);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
- char *inarg;
- unsigned int insize;
-
- insize = entry->d_name.len + 1 + strlen(link) + 1;
- inarg = kmalloc(insize, GFP_KERNEL);
- if(!inarg)
- return -ENOMEM;
-
- strcpy(inarg, entry->d_name.name);
- strcpy(inarg + entry->d_name.len + 1, link);
in.h.opcode = FUSE_SYMLINK;
in.h.ino = dir->i_ino;
- in.argsize = insize;
- in.arg = inarg;
+ in.numargs = 2;
+ in.args[0].size = entry->d_name.len + 1;
+ in.args[0].value = entry->d_name.name;
+ in.args[1].size = strlen(link) + 1;
+ in.args[1].value = link;
request_send(fc, &in, &out);
- kfree(inarg);
-
+
return out.h.error;
}
@@ -228,9 +248,11 @@ static int fuse_remove(struct inode *dir, struct dentry *entry,
in.h.opcode = op;
in.h.ino = dir->i_ino;
- in.argsize = entry->d_name.len + 1;
- in.arg = entry->d_name.name;
+ in.numargs = 1;
+ in.args[0].size = entry->d_name.len + 1;
+ in.args[0].value = entry->d_name.name;
request_send(fc, &in, &out);
+
return out.h.error;
}
@@ -250,27 +272,21 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
struct fuse_conn *fc = INO_FC(olddir);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
- struct fuse_rename_in *inarg;
- unsigned int oldnamsize = oldent->d_name.len + 1;
- unsigned int newnamsize = newent->d_name.len + 1;
- unsigned int insize;
+ struct fuse_rename_in inarg;
- insize = offsetof(struct fuse_rename_in, names) + oldnamsize +
- newnamsize;
- inarg = kmalloc(insize, GFP_KERNEL);
- if(!inarg)
- return -ENOMEM;
-
- inarg->newdir = newdir->i_ino;
- strcpy(inarg->names, oldent->d_name.name);
- strcpy(inarg->names + oldnamsize, newent->d_name.name);
+ memset(&inarg, 0, sizeof(inarg));
+ inarg.newdir = newdir->i_ino;
in.h.opcode = FUSE_RENAME;
in.h.ino = olddir->i_ino;
- in.argsize = insize;
- in.arg = inarg;
+ in.numargs = 3;
+ in.args[0].size = sizeof(inarg);
+ in.args[0].value = &inarg;
+ in.args[1].size = oldent->d_name.len + 1;
+ in.args[1].value = oldent->d_name.name;
+ in.args[2].size = newent->d_name.len + 1;
+ in.args[2].value = newent->d_name.name;
request_send(fc, &in, &out);
- kfree(inarg);
return out.h.error;
}
@@ -282,23 +298,19 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
struct fuse_conn *fc = INO_FC(inode);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
- struct fuse_link_in *inarg;
- unsigned int insize;
-
- insize = offsetof(struct fuse_link_in, name) + newent->d_name.len + 1;
- inarg = kmalloc(insize, GFP_KERNEL);
- if(!inarg)
- return -ENOMEM;
+ struct fuse_link_in inarg;
- inarg->newdir = newdir->i_ino;
- strcpy(inarg->name, newent->d_name.name);
+ memset(&inarg, 0, sizeof(inarg));
+ inarg.newdir = newdir->i_ino;
in.h.opcode = FUSE_LINK;
in.h.ino = inode->i_ino;
- in.argsize = insize;
- in.arg = inarg;
+ in.numargs = 2;
+ in.args[0].size = sizeof(inarg);
+ in.args[0].value = &inarg;
+ in.args[1].size = newent->d_name.len + 1;
+ in.args[1].value = newent->d_name.name;
request_send(fc, &in, &out);
- kfree(inarg);
return out.h.error;
}
@@ -328,8 +340,9 @@ static int fuse_revalidate(struct dentry *entry)
in.h.opcode = FUSE_GETATTR;
in.h.ino = inode->i_ino;
- out.argsize = sizeof(arg);
- out.arg = &arg;
+ out.numargs = 1;
+ out.args[0].size = sizeof(arg);
+ out.args[0].value = &arg;
request_send(fc, &in, &out);
if(!out.h.error)
@@ -400,16 +413,17 @@ static char *read_link(struct dentry *dentry)
in.h.opcode = FUSE_READLINK;
in.h.ino = inode->i_ino;
- out.arg = link;
- out.argsize = PAGE_SIZE - 1;
out.argvar = 1;
+ out.numargs = 1;
+ out.args[0].size = PAGE_SIZE - 1;
+ out.args[0].value = link;
request_send(fc, &in, &out);
if(out.h.error) {
free_page((unsigned long) link);
return ERR_PTR(out.h.error);
}
- link[out.argsize] = '\0';
+ link[out.args[0].size] = '\0';
return link;
}
@@ -453,8 +467,9 @@ static int fuse_dir_open(struct inode *inode, struct file *file)
in.h.opcode = FUSE_GETDIR;
in.h.ino = inode->i_ino;
- out.argsize = sizeof(outarg);
- out.arg = &outarg;
+ out.numargs = 1;
+ out.args[0].size = sizeof(outarg);
+ out.args[0].value = &outarg;
request_send(fc, &in, &out);
if(!out.h.error) {
struct file *cfile = outarg.file;
@@ -521,14 +536,17 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
struct fuse_setattr_in inarg;
struct fuse_setattr_out outarg;
+ memset(&inarg, 0, sizeof(inarg));
inarg.valid = iattr_to_fattr(attr, &inarg.attr);
in.h.opcode = FUSE_SETATTR;
in.h.ino = inode->i_ino;
- in.argsize = sizeof(inarg);
- in.arg = &inarg;
- out.argsize = sizeof(outarg);
- out.arg = &outarg;
+ in.numargs = 1;
+ in.args[0].size = sizeof(inarg);
+ in.args[0].value = &inarg;
+ out.numargs = 1;
+ out.args[0].size = sizeof(outarg);
+ out.args[0].value = &outarg;
request_send(fc, &in, &out);
if(!out.h.error) {
diff --git a/kernel/file.c b/kernel/file.c
index a67bdcd..b7cb2f4 100644
--- a/kernel/file.c
+++ b/kernel/file.c
@@ -10,19 +10,21 @@
#include <linux/pagemap.h>
#include <linux/slab.h>
-
static int fuse_open(struct inode *inode, struct file *file)
{
struct fuse_conn *fc = INO_FC(inode);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
- struct fuse_open_in arg;
+ struct fuse_open_in inarg;
+
+ memset(&inarg, 0, sizeof(inarg));
+ inarg.flags = file->f_flags & ~O_EXCL;
- arg.flags = file->f_flags & ~O_EXCL;
in.h.opcode = FUSE_OPEN;
in.h.ino = inode->i_ino;
- in.argsize = sizeof(arg);
- in.arg = &arg;
+ in.numargs = 1;
+ in.args[0].size = sizeof(inarg);
+ in.args[0].value = &inarg;
request_send(fc, &in, &out);
if(!out.h.error)
invalidate_inode_pages(inode);
@@ -37,27 +39,30 @@ static int fuse_readpage(struct file *file, struct page *page)
struct fuse_conn *fc = INO_FC(inode);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
- struct fuse_read_in arg;
+ struct fuse_read_in inarg;
char *buffer;
buffer = kmap(page);
-
- arg.offset = page->index << PAGE_CACHE_SHIFT;
- arg.size = PAGE_CACHE_SIZE;
+
+ memset(&inarg, 0, sizeof(inarg));
+ inarg.offset = page->index << PAGE_CACHE_SHIFT;
+ inarg.size = PAGE_CACHE_SIZE;
in.h.opcode = FUSE_READ;
in.h.ino = inode->i_ino;
- in.argsize = sizeof(arg);
- in.arg = &arg;
- out.argsize = PAGE_CACHE_SIZE;
+ in.numargs = 1;
+ in.args[0].size = sizeof(inarg);
+ in.args[0].value = &inarg;
out.argvar = 1;
- out.arg = buffer;
+ out.numargs = 1;
+ out.args[0].size = PAGE_CACHE_SIZE;
+ out.args[0].value = buffer;
request_send(fc, &in, &out);
if(!out.h.error) {
- if(out.argsize < PAGE_CACHE_SIZE)
- memset(buffer + out.argsize, 0,
- PAGE_CACHE_SIZE - out.argsize);
+ size_t outsize = out.args[0].size;
+ if(outsize < PAGE_CACHE_SIZE)
+ memset(buffer + outsize, 0, PAGE_CACHE_SIZE - outsize);
SetPageUptodate(page);
}
@@ -73,27 +78,25 @@ static int write_buffer(struct inode *inode, struct page *page,
struct fuse_conn *fc = INO_FC(inode);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
- struct fuse_write_in *arg;
- size_t argsize;
+ struct fuse_write_in inarg;
char *buffer;
- argsize = offsetof(struct fuse_write_in, buf) + count;
- arg = kmalloc(argsize, GFP_KERNEL);
- if(!arg)
- return -ENOMEM;
-
- arg->offset = (page->index << PAGE_CACHE_SHIFT) + offset;
- arg->size = count;
buffer = kmap(page);
- memcpy(arg->buf, buffer + offset, count);
- kunmap(page);
+
+ memset(&inarg, 0, sizeof(inarg));
+ inarg.offset = (page->index << PAGE_CACHE_SHIFT) + offset;
+ inarg.size = count;
in.h.opcode = FUSE_WRITE;
in.h.ino = inode->i_ino;
- in.argsize = argsize;
- in.arg = arg;
+ in.numargs = 2;
+ in.args[0].size = sizeof(inarg);
+ in.args[0].value = &inarg;
+ in.args[1].size = count;
+ in.args[1].value = buffer + offset;
request_send(fc, &in, &out);
- kfree(arg);
+
+ kunmap(page);
return out.h.error;
}
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index e8dde37..27d8eb3 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -14,14 +14,12 @@
#include <linux/list.h>
#include <linux/spinlock.h>
-#define MAX_CLEARED 256
-
/**
* A Fuse connection.
*
* This structure is created, when the client device is opened, and is
* destroyed, when the client device is closed _and_ the filesystem is
- * umounted.
+ * unmounted.
*/
struct fuse_conn {
/** The superblock of the mounted filesystem */
@@ -36,7 +34,7 @@ struct fuse_conn {
/** The fuse mount flags for this mount */
unsigned int flags;
- /** The client wait queue */
+ /** Readers of the connection are waiting on this */
wait_queue_head_t waitq;
/** The list of pending requests */
@@ -45,10 +43,43 @@ struct fuse_conn {
/** The list of requests being processed */
struct list_head processing;
- /** The request id */
+ /** Controls the maximum number of outstanding requests */
+ struct semaphore outstanding;
+
+ /** The next unique request id */
int reqctr;
};
+/** One input argument of a request */
+struct fuse_in_arg {
+ unsigned int size;
+ const void *value;
+};
+
+/** The request input */
+struct fuse_in {
+ struct fuse_in_header h;
+ unsigned int numargs;
+ struct fuse_in_arg args[3];
+};
+
+/** One output argument of a request */
+struct fuse_out_arg {
+ unsigned int size;
+ void *value;
+};
+
+/** The request output */
+struct fuse_out {
+ struct fuse_out_header h;
+ unsigned int argvar;
+ unsigned int numargs;
+ struct fuse_out_arg args[3];
+};
+
+#define FUSE_IN_INIT { {0, 0, 0}, 0}
+#define FUSE_OUT_INIT { {0, 0}, 0, 0}
+
/**
* A request to the client
*/
@@ -56,25 +87,28 @@ struct fuse_req {
/** The request list */
struct list_head list;
- /** The request ID */
- int unique;
+ /** True if the request is synchronous */
+ unsigned int issync:1;
- /** The opcode */
- enum fuse_opcode opcode;
-
- /** The request input size */
- unsigned int insize;
+ /** The request is locked */
+ unsigned int locked:1;
+
+ /** The request has been interrupted while it was locked */
+ unsigned int interrupted:1;
+
+ /* The request has been sent to the client */
+ unsigned int sent:1;
+
+ /* The request is finished */
+ unsigned int finished:1;
/** The request input */
- char *in;
-
- /** The maximum request output size */
- unsigned int outsize;
+ struct fuse_in *in;
/** The request output */
- char *out;
+ struct fuse_out *out;
- /** The request wait queue */
+ /** Used to wake up the task waiting for completion of request*/
wait_queue_head_t waitq;
};
@@ -82,22 +116,6 @@ struct fuse_req {
#define INO_FC(inode) ((struct fuse_conn *) (inode)->i_sb->u.generic_sbp)
#define DEV_FC(file) ((struct fuse_conn *) (file)->private_data)
-struct fuse_in {
- struct fuse_in_header h;
- unsigned int argsize;
- const void *arg;
-};
-
-struct fuse_out {
- struct fuse_out_header h;
- unsigned int argsize;
- unsigned int argvar;
- void *arg;
-};
-
-#define FUSE_IN_INIT { {0, 0, 0}, 0, 0 }
-#define FUSE_OUT_INIT { {0, 0}, 0, 0, 0 }
-
/**
* The proc entry for the client device ("/proc/fs/fuse/dev")
@@ -155,6 +173,11 @@ void fuse_fs_cleanup(void);
void request_send(struct fuse_conn *fc, struct fuse_in *in,
struct fuse_out *out);
+/**
+ * Send a request for which a reply is not expected
+ */
+int request_send_noreply(struct fuse_conn *fc, struct fuse_in *in);
+
/*
* Local Variables:
* indent-tabs-mode: t
diff --git a/kernel/inode.c b/kernel/inode.c
index 36819b7..df402ba 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -23,17 +23,35 @@ static void fuse_read_inode(struct inode *inode)
static void fuse_clear_inode(struct inode *inode)
{
struct fuse_conn *fc = INO_FC(inode);
- struct fuse_in in = FUSE_IN_INIT;
- struct fuse_forget_in arg;
+ struct fuse_in *in = NULL;
+ struct fuse_forget_in *inarg = NULL;
- arg.version = inode->i_version;
-
- in.h.opcode = FUSE_FORGET;
- in.h.ino = inode->i_ino;
- in.argsize = sizeof(arg);
- in.arg = &arg;
+ if(fc == NULL)
+ return;
+
+ in = kmalloc(sizeof(struct fuse_in), GFP_NOFS);
+ if(!in)
+ return;
+
+ inarg = kmalloc(sizeof(struct fuse_forget_in), GFP_NOFS);
+ if(!inarg)
+ goto out_free;
- request_send(fc, &in, NULL);
+ memset(inarg, 0, sizeof(struct fuse_forget_in));
+ inarg->version = inode->i_version;
+
+ in->h.opcode = FUSE_FORGET;
+ in->h.ino = inode->i_ino;
+ in->numargs = 1;
+ in->args[0].size = sizeof(struct fuse_forget_in);
+ in->args[0].value = inarg;
+
+ if(!request_send_noreply(fc, in))
+ return;
+
+ out_free:
+ kfree(inarg);
+ kfree(in);
}
static void fuse_put_super(struct super_block *sb)
@@ -45,6 +63,7 @@ static void fuse_put_super(struct super_block *sb)
fc->uid = 0;
fc->flags = 0;
fuse_release_conn(fc);
+ sb->u.generic_sbp = NULL;
spin_unlock(&fuse_lock);
}