return req;
}
+/* This can return NULL, but only in case it's interrupted by a SIGKILL */
struct fuse_req *fuse_get_request(struct fuse_conn *fc)
-{
- if (down_interruptible(&fc->outstanding_sem))
- return NULL;
- return do_get_request(fc);
-}
-
-/*
- * Non-interruptible version of the above function is for operations
- * which can't legally return -ERESTART{SYS,NOINTR}. This can still
- * return NULL, but only in case the signal is SIGKILL.
- */
-struct fuse_req *fuse_get_request_nonint(struct fuse_conn *fc)
{
int intr;
sigset_t oldset;
spin_unlock(&fuse_lock);
}
+static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
+{
+ int i;
+ struct fuse_init_out *arg = &req->misc.init_out;
+
+ if (arg->major != FUSE_KERNEL_VERSION)
+ fc->conn_error = 1;
+ else {
+ fc->minor = arg->minor;
+ fc->max_write = arg->minor < 5 ? 4096 : arg->max_write;
+ }
+
+ /* After INIT reply is received other requests can go
+ out. So do (FUSE_MAX_OUTSTANDING - 1) number of
+ up()s on outstanding_sem. The last up() is done in
+ fuse_putback_request() */
+ for (i = 1; i < FUSE_MAX_OUTSTANDING; i++)
+ up(&fc->outstanding_sem);
+}
+
/*
* This function is called when a request is finished. Either a reply
* has arrived or it was interrupted (and not yet sent) or some error
- * occured during communication with userspace, or the device file was
- * closed. It decreases the referece count for the request. In case
- * of a background request the referece to the stored objects are
+ * occurred during communication with userspace, or the device file was
+ * closed. It decreases the reference count for the request. In case
+ * of a background request the reference to the stored objects are
* released. The requester thread is woken up (if still waiting), and
* finally the request is either freed or put on the unused_list
*
up_read(&fc->sbput_sem);
}
wake_up(&req->waitq);
- if (req->in.h.opcode == FUSE_INIT) {
- int i;
-
- if (req->misc.init_in_out.major != FUSE_KERNEL_VERSION)
- fc->conn_error = 1;
-
- /* After INIT reply is received other requests can go
- out. So do (FUSE_MAX_OUTSTANDING - 1) number of
- up()s on outstanding_sem. The last up() is done in
- fuse_putback_request() */
- for (i = 1; i < FUSE_MAX_OUTSTANDING; i++)
- up(&fc->outstanding_sem);
+ if (req->in.h.opcode == FUSE_INIT)
+ process_init_reply(fc, req);
+ else if (req->in.h.opcode == FUSE_RELEASE && req->inode == NULL) {
+ /* Special case for failed iget in CREATE */
+ u64 nodeid = req->in.h.nodeid;
+ __fuse_get_request(req);
+ fuse_reset_request(req);
+ fuse_send_forget(fc, req, nodeid, 1);
+ putback = 0;
}
if (putback)
fuse_putback_request(fc, req);
get_file(req->file);
}
-static int request_wait_answer_nonint(struct fuse_req *req)
-{
- int err;
- sigset_t oldset;
- block_sigs(&oldset);
- err = wait_event_interruptible(req->waitq, req->finished);
- restore_sigs(&oldset);
- return err;
-}
-
/* Called with fuse_lock held. Releases, and then reacquires it. */
-static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req,
- int interruptible)
+static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
{
- int intr;
+ sigset_t oldset;
spin_unlock(&fuse_lock);
- if (interruptible)
- intr = wait_event_interruptible(req->waitq, req->finished);
- else
- intr = request_wait_answer_nonint(req);
+ block_sigs(&oldset);
+ wait_event_interruptible(req->waitq, req->finished);
+ restore_sigs(&oldset);
spin_lock(&fuse_lock);
- if (intr && interruptible && req->sent) {
- /* If request is already in userspace, only allow KILL
- signal to interrupt */
- spin_unlock(&fuse_lock);
- intr = request_wait_answer_nonint(req);
- spin_lock(&fuse_lock);
- }
- if (!intr)
+ if (req->finished)
return;
- if (!interruptible || req->sent)
- req->out.h.error = -EINTR;
- else
- req->out.h.error = -ERESTARTNOINTR;
-
+ req->out.h.error = -EINTR;
req->interrupted = 1;
if (req->locked) {
/* This is uninterruptible sleep, because data is
wake_up(&fc->waitq);
}
-static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req,
- int interruptible)
+/*
+ * This can only be interrupted by a SIGKILL
+ */
+void request_send(struct fuse_conn *fc, struct fuse_req *req)
{
req->isreply = 1;
spin_lock(&fuse_lock);
after request_end() */
__fuse_get_request(req);
- request_wait_answer(fc, req, interruptible);
+ request_wait_answer(fc, req);
}
spin_unlock(&fuse_lock);
}
-void request_send(struct fuse_conn *fc, struct fuse_req *req)
-{
- request_send_wait(fc, req, 1);
-}
-
-/*
- * Non-interruptible version of the above function is for operations
- * which can't legally return -ERESTART{SYS,NOINTR}. This can still
- * be interrupted but only with SIGKILL.
- */
-void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req)
-{
- request_send_wait(fc, req, 0);
-}
-
static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
{
spin_lock(&fuse_lock);
/* This is called from fuse_read_super() so there's guaranteed
to be a request available */
struct fuse_req *req = do_get_request(fc);
- struct fuse_init_in_out *arg = &req->misc.init_in_out;
+ struct fuse_init_in *arg = &req->misc.init_in;
arg->major = FUSE_KERNEL_VERSION;
arg->minor = FUSE_KERNEL_MINOR_VERSION;
req->in.h.opcode = FUSE_INIT;
req->in.args[0].size = sizeof(*arg);
req->in.args[0].value = arg;
req->out.numargs = 1;
- req->out.args[0].size = sizeof(*arg);
- req->out.args[0].value = arg;
+ /* Variable length arguement used for backward compatibility
+ with interface version < 7.5. Rest of init_out is zeroed
+ by do_get_request(), so a short reply is not a problem */
+ req->out.argvar = 1;
+ req->out.args[0].size = sizeof(struct fuse_init_out);
+ req->out.args[0].value = &req->misc.init_out;
request_send_background(fc, req);
}
struct fuse_copy_state cs;
unsigned reqsize;
+ restart:
spin_lock(&fuse_lock);
fc = file->private_data;
err = -EPERM;
req = list_entry(fc->pending.next, struct fuse_req, list);
list_del_init(&req->list);
- spin_unlock(&fuse_lock);
in = &req->in;
- reqsize = req->in.h.len;
- fuse_copy_init(&cs, 1, req, iov, nr_segs);
- err = -EINVAL;
- if (iov_length(iov, nr_segs) >= reqsize) {
- err = fuse_copy_one(&cs, &in->h, sizeof(in->h));
- if (!err)
- err = fuse_copy_args(&cs, in->numargs, in->argpages,
- (struct fuse_arg *) in->args, 0);
+ reqsize = in->h.len;
+ /* If request is too large, reply with an error and restart the read */
+ if (iov_length(iov, nr_segs) < reqsize) {
+ req->out.h.error = -EIO;
+ /* SETXATTR is special, since it may contain too large data */
+ if (in->h.opcode == FUSE_SETXATTR)
+ req->out.h.error = -E2BIG;
+ request_end(fc, req);
+ goto restart;
}
+ spin_unlock(&fuse_lock);
+ fuse_copy_init(&cs, 1, req, iov, nr_segs);
+ err = fuse_copy_one(&cs, &in->h, sizeof(in->h));
+ if (!err)
+ err = fuse_copy_args(&cs, in->numargs, in->argpages,
+ (struct fuse_arg *) in->args, 0);
fuse_copy_finish(&cs);
-
spin_lock(&fuse_lock);
req->locked = 0;
if (!err && req->interrupted)
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)
{
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);