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) |