aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2008-07-25 04:49:00 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-07-25 13:53:48 -0400
commitdbd561d236ff16f8143bc727d91758ddd190e8cb (patch)
treeaac8fb4d98c3f425b2f5bf404af7d82182f19bc0
parent0de6256daafa3a97a269995e9b29f956bd419bbf (diff)
fuse: add export operations
Implement export_operations, to allow fuse filesystems to be exported to NFS. This feature has been in the out-of-tree fuse module, and is widely used and tested. It has not been originally merged into mainline, because doing the NFS export in userspace was thought to be a cleaner and more efficient way of doing it, than through the kernel. While that is true, it would also have involved a lot of duplicated effort at reimplementing NFS exporting (all the different versions of the protocol). This effort was unfortunately not undertaken by anyone, so we are left with doing it the easy but less efficient way. If this feature goes in, the out-of-tree fuse module can go away, which would have several advantages: - not having to maintain two versions - less confusion for users - no bugs due to kernel API changes Comment from hch: - Use the same fh_type values as XFS, since we use the same fh encoding. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Cc: Christoph Hellwig <hch@lst.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/fuse/dir.c4
-rw-r--r--fs/fuse/fuse_i.h4
-rw-r--r--fs/fuse/inode.c115
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 */
100static void fuse_invalidate_entry_cache(struct dentry *entry) 100void 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
228static struct dentry_operations fuse_dentry_operations = { 228struct 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 */
465extern const struct file_operations fuse_dev_operations; 465extern const struct file_operations fuse_dev_operations;
466 466
467extern 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 */
605void fuse_invalidate_attr(struct inode *inode); 607void fuse_invalidate_attr(struct inode *inode);
606 608
609void 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
22MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); 23MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
23MODULE_DESCRIPTION("Filesystem in Userspace"); 24MODULE_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
556struct fuse_inode_handle
557{
558 u64 nodeid;
559 u32 generation;
560};
561
562static 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
597static 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
634static 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
648static 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
663static 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
555static const struct super_operations fuse_super_operations = { 669static 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)