diff options
| -rw-r--r-- | fs/fuse/dir.c | 4 | ||||
| -rw-r--r-- | fs/fuse/fuse_i.h | 4 | ||||
| -rw-r--r-- | fs/fuse/inode.c | 115 |
3 files changed, 121 insertions, 2 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index e5217b213b44..be5450dd6387 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
| @@ -97,7 +97,7 @@ void fuse_invalidate_attr(struct inode *inode) | |||
| 97 | * timeout is unknown (unlink, rmdir, rename and in some cases | 97 | * timeout is unknown (unlink, rmdir, rename and in some cases |
| 98 | * lookup) | 98 | * lookup) |
| 99 | */ | 99 | */ |
| 100 | static void fuse_invalidate_entry_cache(struct dentry *entry) | 100 | void fuse_invalidate_entry_cache(struct dentry *entry) |
| 101 | { | 101 | { |
| 102 | fuse_dentry_settime(entry, 0); | 102 | fuse_dentry_settime(entry, 0); |
| 103 | } | 103 | } |
| @@ -225,7 +225,7 @@ static int invalid_nodeid(u64 nodeid) | |||
| 225 | return !nodeid || nodeid == FUSE_ROOT_ID; | 225 | return !nodeid || nodeid == FUSE_ROOT_ID; |
| 226 | } | 226 | } |
| 227 | 227 | ||
| 228 | static struct dentry_operations fuse_dentry_operations = { | 228 | struct dentry_operations fuse_dentry_operations = { |
| 229 | .d_revalidate = fuse_dentry_revalidate, | 229 | .d_revalidate = fuse_dentry_revalidate, |
| 230 | }; | 230 | }; |
| 231 | 231 | ||
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index bae948657c4f..5d3146da64e6 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
| @@ -464,6 +464,8 @@ static inline u64 get_node_id(struct inode *inode) | |||
| 464 | /** Device operations */ | 464 | /** Device operations */ |
| 465 | extern const struct file_operations fuse_dev_operations; | 465 | extern const struct file_operations fuse_dev_operations; |
| 466 | 466 | ||
| 467 | extern struct dentry_operations fuse_dentry_operations; | ||
| 468 | |||
| 467 | /** | 469 | /** |
| 468 | * Get a filled in inode | 470 | * Get a filled in inode |
| 469 | */ | 471 | */ |
| @@ -604,6 +606,8 @@ void fuse_abort_conn(struct fuse_conn *fc); | |||
| 604 | */ | 606 | */ |
| 605 | void fuse_invalidate_attr(struct inode *inode); | 607 | void fuse_invalidate_attr(struct inode *inode); |
| 606 | 608 | ||
| 609 | void fuse_invalidate_entry_cache(struct dentry *entry); | ||
| 610 | |||
| 607 | /** | 611 | /** |
| 608 | * Acquire reference to fuse_conn | 612 | * Acquire reference to fuse_conn |
| 609 | */ | 613 | */ |
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 3141690558c8..71fa76a48a31 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <linux/statfs.h> | 18 | #include <linux/statfs.h> |
| 19 | #include <linux/random.h> | 19 | #include <linux/random.h> |
| 20 | #include <linux/sched.h> | 20 | #include <linux/sched.h> |
| 21 | #include <linux/exportfs.h> | ||
| 21 | 22 | ||
| 22 | MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); | 23 | MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); |
| 23 | MODULE_DESCRIPTION("Filesystem in Userspace"); | 24 | MODULE_DESCRIPTION("Filesystem in Userspace"); |
| @@ -552,6 +553,119 @@ static struct inode *get_root_inode(struct super_block *sb, unsigned mode) | |||
| 552 | return fuse_iget(sb, 1, 0, &attr, 0, 0); | 553 | return fuse_iget(sb, 1, 0, &attr, 0, 0); |
| 553 | } | 554 | } |
| 554 | 555 | ||
| 556 | struct fuse_inode_handle | ||
| 557 | { | ||
| 558 | u64 nodeid; | ||
| 559 | u32 generation; | ||
| 560 | }; | ||
| 561 | |||
| 562 | static struct dentry *fuse_get_dentry(struct super_block *sb, | ||
| 563 | struct fuse_inode_handle *handle) | ||
| 564 | { | ||
| 565 | struct inode *inode; | ||
| 566 | struct dentry *entry; | ||
| 567 | int err = -ESTALE; | ||
| 568 | |||
| 569 | if (handle->nodeid == 0) | ||
| 570 | goto out_err; | ||
| 571 | |||
| 572 | inode = ilookup5(sb, handle->nodeid, fuse_inode_eq, &handle->nodeid); | ||
| 573 | if (!inode) | ||
| 574 | goto out_err; | ||
| 575 | err = -ESTALE; | ||
| 576 | if (inode->i_generation != handle->generation) | ||
| 577 | goto out_iput; | ||
| 578 | |||
| 579 | entry = d_alloc_anon(inode); | ||
| 580 | err = -ENOMEM; | ||
| 581 | if (!entry) | ||
| 582 | goto out_iput; | ||
| 583 | |||
| 584 | if (get_node_id(inode) != FUSE_ROOT_ID) { | ||
| 585 | entry->d_op = &fuse_dentry_operations; | ||
| 586 | fuse_invalidate_entry_cache(entry); | ||
| 587 | } | ||
| 588 | |||
| 589 | return entry; | ||
| 590 | |||
| 591 | out_iput: | ||
| 592 | iput(inode); | ||
| 593 | out_err: | ||
| 594 | return ERR_PTR(err); | ||
| 595 | } | ||
| 596 | |||
| 597 | static int fuse_encode_fh(struct dentry *dentry, u32 *fh, int *max_len, | ||
| 598 | int connectable) | ||
| 599 | { | ||
| 600 | struct inode *inode = dentry->d_inode; | ||
| 601 | bool encode_parent = connectable && !S_ISDIR(inode->i_mode); | ||
| 602 | int len = encode_parent ? 6 : 3; | ||
| 603 | u64 nodeid; | ||
| 604 | u32 generation; | ||
| 605 | |||
| 606 | if (*max_len < len) | ||
| 607 | return 255; | ||
| 608 | |||
| 609 | nodeid = get_fuse_inode(inode)->nodeid; | ||
| 610 | generation = inode->i_generation; | ||
| 611 | |||
| 612 | fh[0] = (u32)(nodeid >> 32); | ||
| 613 | fh[1] = (u32)(nodeid & 0xffffffff); | ||
| 614 | fh[2] = generation; | ||
| 615 | |||
| 616 | if (encode_parent) { | ||
| 617 | struct inode *parent; | ||
| 618 | |||
| 619 | spin_lock(&dentry->d_lock); | ||
| 620 | parent = dentry->d_parent->d_inode; | ||
| 621 | nodeid = get_fuse_inode(parent)->nodeid; | ||
| 622 | generation = parent->i_generation; | ||
| 623 | spin_unlock(&dentry->d_lock); | ||
| 624 | |||
| 625 | fh[3] = (u32)(nodeid >> 32); | ||
| 626 | fh[4] = (u32)(nodeid & 0xffffffff); | ||
| 627 | fh[5] = generation; | ||
| 628 | } | ||
| 629 | |||
| 630 | *max_len = len; | ||
| 631 | return encode_parent ? 0x82 : 0x81; | ||
| 632 | } | ||
| 633 | |||
| 634 | static struct dentry *fuse_fh_to_dentry(struct super_block *sb, | ||
| 635 | struct fid *fid, int fh_len, int fh_type) | ||
| 636 | { | ||
| 637 | struct fuse_inode_handle handle; | ||
| 638 | |||
| 639 | if ((fh_type != 0x81 && fh_type != 0x82) || fh_len < 3) | ||
| 640 | return NULL; | ||
| 641 | |||
| 642 | handle.nodeid = (u64) fid->raw[0] << 32; | ||
| 643 | handle.nodeid |= (u64) fid->raw[1]; | ||
| 644 | handle.generation = fid->raw[2]; | ||
| 645 | return fuse_get_dentry(sb, &handle); | ||
| 646 | } | ||
| 647 | |||
| 648 | static struct dentry *fuse_fh_to_parent(struct super_block *sb, | ||
| 649 | struct fid *fid, int fh_len, int fh_type) | ||
| 650 | { | ||
| 651 | struct fuse_inode_handle parent; | ||
| 652 | |||
| 653 | if (fh_type != 0x82 || fh_len < 6) | ||
| 654 | return NULL; | ||
| 655 | |||
| 656 | parent.nodeid = (u64) fid->raw[3] << 32; | ||
| 657 | parent.nodeid |= (u64) fid->raw[4]; | ||
| 658 | parent.generation = fid->raw[5]; | ||
| 659 | return fuse_get_dentry(sb, &parent); | ||
| 660 | } | ||
| 661 | |||
| 662 | |||
| 663 | static const struct export_operations fuse_export_operations = { | ||
| 664 | .fh_to_dentry = fuse_fh_to_dentry, | ||
| 665 | .fh_to_parent = fuse_fh_to_parent, | ||
| 666 | .encode_fh = fuse_encode_fh, | ||
| 667 | }; | ||
| 668 | |||
| 555 | static const struct super_operations fuse_super_operations = { | 669 | static const struct super_operations fuse_super_operations = { |
| 556 | .alloc_inode = fuse_alloc_inode, | 670 | .alloc_inode = fuse_alloc_inode, |
| 557 | .destroy_inode = fuse_destroy_inode, | 671 | .destroy_inode = fuse_destroy_inode, |
| @@ -652,6 +766,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
| 652 | sb->s_magic = FUSE_SUPER_MAGIC; | 766 | sb->s_magic = FUSE_SUPER_MAGIC; |
| 653 | sb->s_op = &fuse_super_operations; | 767 | sb->s_op = &fuse_super_operations; |
| 654 | sb->s_maxbytes = MAX_LFS_FILESIZE; | 768 | sb->s_maxbytes = MAX_LFS_FILESIZE; |
| 769 | sb->s_export_op = &fuse_export_operations; | ||
| 655 | 770 | ||
| 656 | file = fget(d.fd); | 771 | file = fget(d.fd); |
| 657 | if (!file) | 772 | if (!file) |
