diff options
| author | Miklos Szeredi <mszeredi@suse.cz> | 2015-01-06 04:45:35 -0500 |
|---|---|---|
| committer | Miklos Szeredi <mszeredi@suse.cz> | 2015-01-06 04:45:35 -0500 |
| commit | 21f621741a770c119e7529a3f5c0e6b7c91383a3 (patch) | |
| tree | 4393194807e351d0c2922673d44409a656a87259 /fs/fuse | |
| parent | b1940cd21c0f4abdce101253e860feff547291b0 (diff) | |
fuse: fix LOOKUP vs INIT compat handling
Analysis from Marc:
"Commit 7078187a795f ("fuse: introduce fuse_simple_request() helper")
from the above pull request triggers some EIO errors for me in some tests
that rely on fuse
Looking at the code changes and a bit of debugging info I think there's a
general problem here that fuse_get_req checks and possibly waits for
fc->initialized, and this was always called first. But this commit
changes the ordering and in many places fc->minor is now possibly used
before fuse_get_req, and we can't be sure that fc has been initialized.
In my case fuse_lookup_init sets req->out.args[0].size to the wrong size
because fc->minor at that point is still 0, leading to the EIO error."
Fix by moving the compat adjustments into fuse_simple_request() to after
fuse_get_req().
This is also more readable than the original, since now compatibility is
handled in a single function instead of cluttering each operation.
Reported-by: Marc Dionne <marc.c.dionne@gmail.com>
Tested-by: Marc Dionne <marc.c.dionne@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Fixes: 7078187a795f ("fuse: introduce fuse_simple_request() helper")
Diffstat (limited to 'fs/fuse')
| -rw-r--r-- | fs/fuse/dev.c | 36 | ||||
| -rw-r--r-- | fs/fuse/dir.c | 31 | ||||
| -rw-r--r-- | fs/fuse/inode.c | 3 |
3 files changed, 44 insertions, 26 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index ba1107977f2e..c847d6b225e2 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
| @@ -511,6 +511,39 @@ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) | |||
| 511 | } | 511 | } |
| 512 | EXPORT_SYMBOL_GPL(fuse_request_send); | 512 | EXPORT_SYMBOL_GPL(fuse_request_send); |
| 513 | 513 | ||
| 514 | static void fuse_adjust_compat(struct fuse_conn *fc, struct fuse_args *args) | ||
| 515 | { | ||
| 516 | if (fc->minor < 4 && args->in.h.opcode == FUSE_STATFS) | ||
| 517 | args->out.args[0].size = FUSE_COMPAT_STATFS_SIZE; | ||
| 518 | |||
| 519 | if (fc->minor < 9) { | ||
| 520 | switch (args->in.h.opcode) { | ||
| 521 | case FUSE_LOOKUP: | ||
| 522 | case FUSE_CREATE: | ||
| 523 | case FUSE_MKNOD: | ||
| 524 | case FUSE_MKDIR: | ||
| 525 | case FUSE_SYMLINK: | ||
| 526 | case FUSE_LINK: | ||
| 527 | args->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; | ||
| 528 | break; | ||
| 529 | case FUSE_GETATTR: | ||
| 530 | case FUSE_SETATTR: | ||
| 531 | args->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE; | ||
| 532 | break; | ||
| 533 | } | ||
| 534 | } | ||
| 535 | if (fc->minor < 12) { | ||
| 536 | switch (args->in.h.opcode) { | ||
| 537 | case FUSE_CREATE: | ||
| 538 | args->in.args[0].size = sizeof(struct fuse_open_in); | ||
| 539 | break; | ||
| 540 | case FUSE_MKNOD: | ||
| 541 | args->in.args[0].size = FUSE_COMPAT_MKNOD_IN_SIZE; | ||
| 542 | break; | ||
| 543 | } | ||
| 544 | } | ||
| 545 | } | ||
| 546 | |||
| 514 | ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args) | 547 | ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args) |
| 515 | { | 548 | { |
| 516 | struct fuse_req *req; | 549 | struct fuse_req *req; |
| @@ -520,6 +553,9 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args) | |||
| 520 | if (IS_ERR(req)) | 553 | if (IS_ERR(req)) |
| 521 | return PTR_ERR(req); | 554 | return PTR_ERR(req); |
| 522 | 555 | ||
| 556 | /* Needs to be done after fuse_get_req() so that fc->minor is valid */ | ||
| 557 | fuse_adjust_compat(fc, args); | ||
| 558 | |||
| 523 | req->in.h.opcode = args->in.h.opcode; | 559 | req->in.h.opcode = args->in.h.opcode; |
| 524 | req->in.h.nodeid = args->in.h.nodeid; | 560 | req->in.h.nodeid = args->in.h.nodeid; |
| 525 | req->in.numargs = args->in.numargs; | 561 | req->in.numargs = args->in.numargs; |
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 252b8a5de8b5..08e7b1a9d5d0 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
| @@ -156,10 +156,7 @@ static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args, | |||
| 156 | args->in.args[0].size = name->len + 1; | 156 | args->in.args[0].size = name->len + 1; |
| 157 | args->in.args[0].value = name->name; | 157 | args->in.args[0].value = name->name; |
| 158 | args->out.numargs = 1; | 158 | args->out.numargs = 1; |
| 159 | if (fc->minor < 9) | 159 | args->out.args[0].size = sizeof(struct fuse_entry_out); |
| 160 | args->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; | ||
| 161 | else | ||
| 162 | args->out.args[0].size = sizeof(struct fuse_entry_out); | ||
| 163 | args->out.args[0].value = outarg; | 160 | args->out.args[0].value = outarg; |
| 164 | } | 161 | } |
| 165 | 162 | ||
| @@ -422,16 +419,12 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, | |||
| 422 | args.in.h.opcode = FUSE_CREATE; | 419 | args.in.h.opcode = FUSE_CREATE; |
| 423 | args.in.h.nodeid = get_node_id(dir); | 420 | args.in.h.nodeid = get_node_id(dir); |
| 424 | args.in.numargs = 2; | 421 | args.in.numargs = 2; |
| 425 | args.in.args[0].size = fc->minor < 12 ? sizeof(struct fuse_open_in) : | 422 | args.in.args[0].size = sizeof(inarg); |
| 426 | sizeof(inarg); | ||
| 427 | args.in.args[0].value = &inarg; | 423 | args.in.args[0].value = &inarg; |
| 428 | args.in.args[1].size = entry->d_name.len + 1; | 424 | args.in.args[1].size = entry->d_name.len + 1; |
| 429 | args.in.args[1].value = entry->d_name.name; | 425 | args.in.args[1].value = entry->d_name.name; |
| 430 | args.out.numargs = 2; | 426 | args.out.numargs = 2; |
| 431 | if (fc->minor < 9) | 427 | args.out.args[0].size = sizeof(outentry); |
| 432 | args.out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; | ||
| 433 | else | ||
| 434 | args.out.args[0].size = sizeof(outentry); | ||
| 435 | args.out.args[0].value = &outentry; | 428 | args.out.args[0].value = &outentry; |
| 436 | args.out.args[1].size = sizeof(outopen); | 429 | args.out.args[1].size = sizeof(outopen); |
| 437 | args.out.args[1].value = &outopen; | 430 | args.out.args[1].value = &outopen; |
| @@ -539,10 +532,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_args *args, | |||
| 539 | memset(&outarg, 0, sizeof(outarg)); | 532 | memset(&outarg, 0, sizeof(outarg)); |
| 540 | args->in.h.nodeid = get_node_id(dir); | 533 | args->in.h.nodeid = get_node_id(dir); |
| 541 | args->out.numargs = 1; | 534 | args->out.numargs = 1; |
| 542 | if (fc->minor < 9) | 535 | args->out.args[0].size = sizeof(outarg); |
| 543 | args->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; | ||
| 544 | else | ||
| 545 | args->out.args[0].size = sizeof(outarg); | ||
| 546 | args->out.args[0].value = &outarg; | 536 | args->out.args[0].value = &outarg; |
| 547 | err = fuse_simple_request(fc, args); | 537 | err = fuse_simple_request(fc, args); |
| 548 | if (err) | 538 | if (err) |
| @@ -592,8 +582,7 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, umode_t mode, | |||
| 592 | inarg.umask = current_umask(); | 582 | inarg.umask = current_umask(); |
| 593 | args.in.h.opcode = FUSE_MKNOD; | 583 | args.in.h.opcode = FUSE_MKNOD; |
| 594 | args.in.numargs = 2; | 584 | args.in.numargs = 2; |
| 595 | args.in.args[0].size = fc->minor < 12 ? FUSE_COMPAT_MKNOD_IN_SIZE : | 585 | args.in.args[0].size = sizeof(inarg); |
| 596 | sizeof(inarg); | ||
| 597 | args.in.args[0].value = &inarg; | 586 | args.in.args[0].value = &inarg; |
| 598 | args.in.args[1].size = entry->d_name.len + 1; | 587 | args.in.args[1].size = entry->d_name.len + 1; |
| 599 | args.in.args[1].value = entry->d_name.name; | 588 | args.in.args[1].value = entry->d_name.name; |
| @@ -899,10 +888,7 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat, | |||
| 899 | args.in.args[0].size = sizeof(inarg); | 888 | args.in.args[0].size = sizeof(inarg); |
| 900 | args.in.args[0].value = &inarg; | 889 | args.in.args[0].value = &inarg; |
| 901 | args.out.numargs = 1; | 890 | args.out.numargs = 1; |
| 902 | if (fc->minor < 9) | 891 | args.out.args[0].size = sizeof(outarg); |
| 903 | args.out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE; | ||
| 904 | else | ||
| 905 | args.out.args[0].size = sizeof(outarg); | ||
| 906 | args.out.args[0].value = &outarg; | 892 | args.out.args[0].value = &outarg; |
| 907 | err = fuse_simple_request(fc, &args); | 893 | err = fuse_simple_request(fc, &args); |
| 908 | if (!err) { | 894 | if (!err) { |
| @@ -1574,10 +1560,7 @@ static void fuse_setattr_fill(struct fuse_conn *fc, struct fuse_args *args, | |||
| 1574 | args->in.args[0].size = sizeof(*inarg_p); | 1560 | args->in.args[0].size = sizeof(*inarg_p); |
| 1575 | args->in.args[0].value = inarg_p; | 1561 | args->in.args[0].value = inarg_p; |
| 1576 | args->out.numargs = 1; | 1562 | args->out.numargs = 1; |
| 1577 | if (fc->minor < 9) | 1563 | args->out.args[0].size = sizeof(*outarg_p); |
| 1578 | args->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE; | ||
| 1579 | else | ||
| 1580 | args->out.args[0].size = sizeof(*outarg_p); | ||
| 1581 | args->out.args[0].value = outarg_p; | 1564 | args->out.args[0].value = outarg_p; |
| 1582 | } | 1565 | } |
| 1583 | 1566 | ||
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 6749109f255d..6a20f2ff2c2e 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
| @@ -424,8 +424,7 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf) | |||
| 424 | args.in.h.opcode = FUSE_STATFS; | 424 | args.in.h.opcode = FUSE_STATFS; |
| 425 | args.in.h.nodeid = get_node_id(dentry->d_inode); | 425 | args.in.h.nodeid = get_node_id(dentry->d_inode); |
| 426 | args.out.numargs = 1; | 426 | args.out.numargs = 1; |
| 427 | args.out.args[0].size = | 427 | args.out.args[0].size = sizeof(outarg); |
| 428 | fc->minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(outarg); | ||
| 429 | args.out.args[0].value = &outarg; | 428 | args.out.args[0].value = &outarg; |
| 430 | err = fuse_simple_request(fc, &args); | 429 | err = fuse_simple_request(fc, &args); |
| 431 | if (!err) | 430 | if (!err) |
