diff options
Diffstat (limited to 'fs/fuse/dev.c')
-rw-r--r-- | fs/fuse/dev.c | 72 |
1 files changed, 46 insertions, 26 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 8f873e621f41..e08ab4702d97 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -148,6 +148,26 @@ void fuse_release_background(struct fuse_req *req) | |||
148 | spin_unlock(&fuse_lock); | 148 | spin_unlock(&fuse_lock); |
149 | } | 149 | } |
150 | 150 | ||
151 | static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) | ||
152 | { | ||
153 | int i; | ||
154 | struct fuse_init_out *arg = &req->misc.init_out; | ||
155 | |||
156 | if (arg->major != FUSE_KERNEL_VERSION) | ||
157 | fc->conn_error = 1; | ||
158 | else { | ||
159 | fc->minor = arg->minor; | ||
160 | fc->max_write = arg->minor < 5 ? 4096 : arg->max_write; | ||
161 | } | ||
162 | |||
163 | /* After INIT reply is received other requests can go | ||
164 | out. So do (FUSE_MAX_OUTSTANDING - 1) number of | ||
165 | up()s on outstanding_sem. The last up() is done in | ||
166 | fuse_putback_request() */ | ||
167 | for (i = 1; i < FUSE_MAX_OUTSTANDING; i++) | ||
168 | up(&fc->outstanding_sem); | ||
169 | } | ||
170 | |||
151 | /* | 171 | /* |
152 | * This function is called when a request is finished. Either a reply | 172 | * This function is called when a request is finished. Either a reply |
153 | * has arrived or it was interrupted (and not yet sent) or some error | 173 | * has arrived or it was interrupted (and not yet sent) or some error |
@@ -172,19 +192,9 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) | |||
172 | up_read(&fc->sbput_sem); | 192 | up_read(&fc->sbput_sem); |
173 | } | 193 | } |
174 | wake_up(&req->waitq); | 194 | wake_up(&req->waitq); |
175 | if (req->in.h.opcode == FUSE_INIT) { | 195 | if (req->in.h.opcode == FUSE_INIT) |
176 | int i; | 196 | process_init_reply(fc, req); |
177 | 197 | else if (req->in.h.opcode == FUSE_RELEASE && req->inode == NULL) { | |
178 | if (req->misc.init_in_out.major != FUSE_KERNEL_VERSION) | ||
179 | fc->conn_error = 1; | ||
180 | |||
181 | /* After INIT reply is received other requests can go | ||
182 | out. So do (FUSE_MAX_OUTSTANDING - 1) number of | ||
183 | up()s on outstanding_sem. The last up() is done in | ||
184 | fuse_putback_request() */ | ||
185 | for (i = 1; i < FUSE_MAX_OUTSTANDING; i++) | ||
186 | up(&fc->outstanding_sem); | ||
187 | } else if (req->in.h.opcode == FUSE_RELEASE && req->inode == NULL) { | ||
188 | /* Special case for failed iget in CREATE */ | 198 | /* Special case for failed iget in CREATE */ |
189 | u64 nodeid = req->in.h.nodeid; | 199 | u64 nodeid = req->in.h.nodeid; |
190 | __fuse_get_request(req); | 200 | __fuse_get_request(req); |
@@ -357,7 +367,7 @@ void fuse_send_init(struct fuse_conn *fc) | |||
357 | /* This is called from fuse_read_super() so there's guaranteed | 367 | /* This is called from fuse_read_super() so there's guaranteed |
358 | to be a request available */ | 368 | to be a request available */ |
359 | struct fuse_req *req = do_get_request(fc); | 369 | struct fuse_req *req = do_get_request(fc); |
360 | struct fuse_init_in_out *arg = &req->misc.init_in_out; | 370 | struct fuse_init_in *arg = &req->misc.init_in; |
361 | arg->major = FUSE_KERNEL_VERSION; | 371 | arg->major = FUSE_KERNEL_VERSION; |
362 | arg->minor = FUSE_KERNEL_MINOR_VERSION; | 372 | arg->minor = FUSE_KERNEL_MINOR_VERSION; |
363 | req->in.h.opcode = FUSE_INIT; | 373 | req->in.h.opcode = FUSE_INIT; |
@@ -365,8 +375,12 @@ void fuse_send_init(struct fuse_conn *fc) | |||
365 | req->in.args[0].size = sizeof(*arg); | 375 | req->in.args[0].size = sizeof(*arg); |
366 | req->in.args[0].value = arg; | 376 | req->in.args[0].value = arg; |
367 | req->out.numargs = 1; | 377 | req->out.numargs = 1; |
368 | req->out.args[0].size = sizeof(*arg); | 378 | /* Variable length arguement used for backward compatibility |
369 | req->out.args[0].value = arg; | 379 | with interface version < 7.5. Rest of init_out is zeroed |
380 | by do_get_request(), so a short reply is not a problem */ | ||
381 | req->out.argvar = 1; | ||
382 | req->out.args[0].size = sizeof(struct fuse_init_out); | ||
383 | req->out.args[0].value = &req->misc.init_out; | ||
370 | request_send_background(fc, req); | 384 | request_send_background(fc, req); |
371 | } | 385 | } |
372 | 386 | ||
@@ -615,6 +629,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, | |||
615 | struct fuse_copy_state cs; | 629 | struct fuse_copy_state cs; |
616 | unsigned reqsize; | 630 | unsigned reqsize; |
617 | 631 | ||
632 | restart: | ||
618 | spin_lock(&fuse_lock); | 633 | spin_lock(&fuse_lock); |
619 | fc = file->private_data; | 634 | fc = file->private_data; |
620 | err = -EPERM; | 635 | err = -EPERM; |
@@ -630,20 +645,25 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, | |||
630 | 645 | ||
631 | req = list_entry(fc->pending.next, struct fuse_req, list); | 646 | req = list_entry(fc->pending.next, struct fuse_req, list); |
632 | list_del_init(&req->list); | 647 | list_del_init(&req->list); |
633 | spin_unlock(&fuse_lock); | ||
634 | 648 | ||
635 | in = &req->in; | 649 | in = &req->in; |
636 | reqsize = req->in.h.len; | 650 | reqsize = in->h.len; |
637 | fuse_copy_init(&cs, 1, req, iov, nr_segs); | 651 | /* If request is too large, reply with an error and restart the read */ |
638 | err = -EINVAL; | 652 | if (iov_length(iov, nr_segs) < reqsize) { |
639 | if (iov_length(iov, nr_segs) >= reqsize) { | 653 | req->out.h.error = -EIO; |
640 | err = fuse_copy_one(&cs, &in->h, sizeof(in->h)); | 654 | /* SETXATTR is special, since it may contain too large data */ |
641 | if (!err) | 655 | if (in->h.opcode == FUSE_SETXATTR) |
642 | err = fuse_copy_args(&cs, in->numargs, in->argpages, | 656 | req->out.h.error = -E2BIG; |
643 | (struct fuse_arg *) in->args, 0); | 657 | request_end(fc, req); |
658 | goto restart; | ||
644 | } | 659 | } |
660 | spin_unlock(&fuse_lock); | ||
661 | fuse_copy_init(&cs, 1, req, iov, nr_segs); | ||
662 | err = fuse_copy_one(&cs, &in->h, sizeof(in->h)); | ||
663 | if (!err) | ||
664 | err = fuse_copy_args(&cs, in->numargs, in->argpages, | ||
665 | (struct fuse_arg *) in->args, 0); | ||
645 | fuse_copy_finish(&cs); | 666 | fuse_copy_finish(&cs); |
646 | |||
647 | spin_lock(&fuse_lock); | 667 | spin_lock(&fuse_lock); |
648 | req->locked = 0; | 668 | req->locked = 0; |
649 | if (!err && req->interrupted) | 669 | if (!err && req->interrupted) |