diff options
author | Christoph Hellwig <hch@lst.de> | 2007-10-21 19:42:05 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-22 11:13:19 -0400 |
commit | 2596110a3994593f6aa3e2bb76345ad4791b1a14 (patch) | |
tree | 0f1773238265f83e1b5640256176851a60ff5ea8 | |
parent | 6e91ea2bb0b6a3ddf6d4faeb54a9c20d4e20bc42 (diff) |
exportfs: add new methods
Add the guts for the new filesystem API to exportfs.
There's now a fh_to_dentry method that returns a dentry for the object looked
for given a filehandle fragment, and a fh_to_parent operation that returns the
dentry for the encoded parent directory in case the file handle contains it.
There are default implementations for these methods that only take a callback
for an nfs-enhanced iget variant and implement the rest of the semantics.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Cc: Neil Brown <neilb@suse.de>
Cc: "J. Bruce Fields" <bfields@fieldses.org>
Cc: <linux-ext4@vger.kernel.org>
Cc: Dave Kleikamp <shaggy@austin.ibm.com>
Cc: Anton Altaparmakov <aia21@cantab.net>
Cc: David Chinner <dgc@sgi.com>
Cc: Timothy Shimmin <tes@sgi.com>
Cc: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Cc: Hugh Dickins <hugh@veritas.com>
Cc: Chris Mason <mason@suse.com>
Cc: Jeff Mahoney <jeffm@suse.com>
Cc: "Vladimir V. Saveliev" <vs@namesys.com>
Cc: Steven Whitehouse <swhiteho@redhat.com>
Cc: Mark Fasheh <mark.fasheh@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/exportfs/expfs.c | 136 | ||||
-rw-r--r-- | fs/libfs.c | 88 | ||||
-rw-r--r-- | include/linux/exportfs.h | 30 |
3 files changed, 248 insertions, 6 deletions
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index 813011aad700..99294a23cd54 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c | |||
@@ -514,17 +514,141 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, | |||
514 | int (*acceptable)(void *, struct dentry *), void *context) | 514 | int (*acceptable)(void *, struct dentry *), void *context) |
515 | { | 515 | { |
516 | struct export_operations *nop = mnt->mnt_sb->s_export_op; | 516 | struct export_operations *nop = mnt->mnt_sb->s_export_op; |
517 | struct dentry *result; | 517 | struct dentry *result, *alias; |
518 | int err; | ||
518 | 519 | ||
519 | if (nop->decode_fh) { | 520 | /* |
520 | result = nop->decode_fh(mnt->mnt_sb, fid->raw, fh_len, | 521 | * Old way of doing things. Will go away soon. |
522 | */ | ||
523 | if (!nop->fh_to_dentry) { | ||
524 | if (nop->decode_fh) { | ||
525 | return nop->decode_fh(mnt->mnt_sb, fid->raw, fh_len, | ||
521 | fileid_type, acceptable, context); | 526 | fileid_type, acceptable, context); |
527 | } else { | ||
528 | return export_decode_fh(mnt->mnt_sb, fid->raw, fh_len, | ||
529 | fileid_type, acceptable, context); | ||
530 | } | ||
531 | } | ||
532 | |||
533 | /* | ||
534 | * Try to get any dentry for the given file handle from the filesystem. | ||
535 | */ | ||
536 | result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type); | ||
537 | if (!result) | ||
538 | result = ERR_PTR(-ESTALE); | ||
539 | if (IS_ERR(result)) | ||
540 | return result; | ||
541 | |||
542 | if (S_ISDIR(result->d_inode->i_mode)) { | ||
543 | /* | ||
544 | * This request is for a directory. | ||
545 | * | ||
546 | * On the positive side there is only one dentry for each | ||
547 | * directory inode. On the negative side this implies that we | ||
548 | * to ensure our dentry is connected all the way up to the | ||
549 | * filesystem root. | ||
550 | */ | ||
551 | if (result->d_flags & DCACHE_DISCONNECTED) { | ||
552 | err = reconnect_path(mnt->mnt_sb, result); | ||
553 | if (err) | ||
554 | goto err_result; | ||
555 | } | ||
556 | |||
557 | if (!acceptable(context, result)) { | ||
558 | err = -EACCES; | ||
559 | goto err_result; | ||
560 | } | ||
561 | |||
562 | return result; | ||
522 | } else { | 563 | } else { |
523 | result = export_decode_fh(mnt->mnt_sb, fid->raw, fh_len, | 564 | /* |
524 | fileid_type, acceptable, context); | 565 | * It's not a directory. Life is a little more complicated. |
566 | */ | ||
567 | struct dentry *target_dir, *nresult; | ||
568 | char nbuf[NAME_MAX+1]; | ||
569 | |||
570 | /* | ||
571 | * See if either the dentry we just got from the filesystem | ||
572 | * or any alias for it is acceptable. This is always true | ||
573 | * if this filesystem is exported without the subtreecheck | ||
574 | * option. If the filesystem is exported with the subtree | ||
575 | * check option there's a fair chance we need to look at | ||
576 | * the parent directory in the file handle and make sure | ||
577 | * it's connected to the filesystem root. | ||
578 | */ | ||
579 | alias = find_acceptable_alias(result, acceptable, context); | ||
580 | if (alias) | ||
581 | return alias; | ||
582 | |||
583 | /* | ||
584 | * Try to extract a dentry for the parent directory from the | ||
585 | * file handle. If this fails we'll have to give up. | ||
586 | */ | ||
587 | err = -ESTALE; | ||
588 | if (!nop->fh_to_parent) | ||
589 | goto err_result; | ||
590 | |||
591 | target_dir = nop->fh_to_parent(mnt->mnt_sb, fid, | ||
592 | fh_len, fileid_type); | ||
593 | if (!target_dir) | ||
594 | goto err_result; | ||
595 | err = PTR_ERR(target_dir); | ||
596 | if (IS_ERR(target_dir)) | ||
597 | goto err_result; | ||
598 | |||
599 | /* | ||
600 | * And as usual we need to make sure the parent directory is | ||
601 | * connected to the filesystem root. The VFS really doesn't | ||
602 | * like disconnected directories.. | ||
603 | */ | ||
604 | err = reconnect_path(mnt->mnt_sb, target_dir); | ||
605 | if (err) { | ||
606 | dput(target_dir); | ||
607 | goto err_result; | ||
608 | } | ||
609 | |||
610 | /* | ||
611 | * Now that we've got both a well-connected parent and a | ||
612 | * dentry for the inode we're after, make sure that our | ||
613 | * inode is actually connected to the parent. | ||
614 | */ | ||
615 | err = exportfs_get_name(target_dir, nbuf, result); | ||
616 | if (!err) { | ||
617 | mutex_lock(&target_dir->d_inode->i_mutex); | ||
618 | nresult = lookup_one_len(nbuf, target_dir, | ||
619 | strlen(nbuf)); | ||
620 | mutex_unlock(&target_dir->d_inode->i_mutex); | ||
621 | if (!IS_ERR(nresult)) { | ||
622 | if (nresult->d_inode) { | ||
623 | dput(result); | ||
624 | result = nresult; | ||
625 | } else | ||
626 | dput(nresult); | ||
627 | } | ||
628 | } | ||
629 | |||
630 | /* | ||
631 | * At this point we are done with the parent, but it's pinned | ||
632 | * by the child dentry anyway. | ||
633 | */ | ||
634 | dput(target_dir); | ||
635 | |||
636 | /* | ||
637 | * And finally make sure the dentry is actually acceptable | ||
638 | * to NFSD. | ||
639 | */ | ||
640 | alias = find_acceptable_alias(result, acceptable, context); | ||
641 | if (!alias) { | ||
642 | err = -EACCES; | ||
643 | goto err_result; | ||
644 | } | ||
645 | |||
646 | return alias; | ||
525 | } | 647 | } |
526 | 648 | ||
527 | return result; | 649 | err_result: |
650 | dput(result); | ||
651 | return ERR_PTR(err); | ||
528 | } | 652 | } |
529 | EXPORT_SYMBOL_GPL(exportfs_decode_fh); | 653 | EXPORT_SYMBOL_GPL(exportfs_decode_fh); |
530 | 654 | ||
diff --git a/fs/libfs.c b/fs/libfs.c index ae51481e45e5..6e68b700958d 100644 --- a/fs/libfs.c +++ b/fs/libfs.c | |||
@@ -8,6 +8,7 @@ | |||
8 | #include <linux/mount.h> | 8 | #include <linux/mount.h> |
9 | #include <linux/vfs.h> | 9 | #include <linux/vfs.h> |
10 | #include <linux/mutex.h> | 10 | #include <linux/mutex.h> |
11 | #include <linux/exportfs.h> | ||
11 | 12 | ||
12 | #include <asm/uaccess.h> | 13 | #include <asm/uaccess.h> |
13 | 14 | ||
@@ -678,6 +679,93 @@ out: | |||
678 | return ret; | 679 | return ret; |
679 | } | 680 | } |
680 | 681 | ||
682 | /* | ||
683 | * This is what d_alloc_anon should have been. Once the exportfs | ||
684 | * argument transition has been finished I will update d_alloc_anon | ||
685 | * to this prototype and this wrapper will go away. --hch | ||
686 | */ | ||
687 | static struct dentry *exportfs_d_alloc(struct inode *inode) | ||
688 | { | ||
689 | struct dentry *dentry; | ||
690 | |||
691 | if (!inode) | ||
692 | return NULL; | ||
693 | if (IS_ERR(inode)) | ||
694 | return ERR_PTR(PTR_ERR(inode)); | ||
695 | |||
696 | dentry = d_alloc_anon(inode); | ||
697 | if (!dentry) { | ||
698 | iput(inode); | ||
699 | dentry = ERR_PTR(-ENOMEM); | ||
700 | } | ||
701 | return dentry; | ||
702 | } | ||
703 | |||
704 | /** | ||
705 | * generic_fh_to_dentry - generic helper for the fh_to_dentry export operation | ||
706 | * @sb: filesystem to do the file handle conversion on | ||
707 | * @fid: file handle to convert | ||
708 | * @fh_len: length of the file handle in bytes | ||
709 | * @fh_type: type of file handle | ||
710 | * @get_inode: filesystem callback to retrieve inode | ||
711 | * | ||
712 | * This function decodes @fid as long as it has one of the well-known | ||
713 | * Linux filehandle types and calls @get_inode on it to retrieve the | ||
714 | * inode for the object specified in the file handle. | ||
715 | */ | ||
716 | struct dentry *generic_fh_to_dentry(struct super_block *sb, struct fid *fid, | ||
717 | int fh_len, int fh_type, struct inode *(*get_inode) | ||
718 | (struct super_block *sb, u64 ino, u32 gen)) | ||
719 | { | ||
720 | struct inode *inode = NULL; | ||
721 | |||
722 | if (fh_len < 2) | ||
723 | return NULL; | ||
724 | |||
725 | switch (fh_type) { | ||
726 | case FILEID_INO32_GEN: | ||
727 | case FILEID_INO32_GEN_PARENT: | ||
728 | inode = get_inode(sb, fid->i32.ino, fid->i32.gen); | ||
729 | break; | ||
730 | } | ||
731 | |||
732 | return exportfs_d_alloc(inode); | ||
733 | } | ||
734 | EXPORT_SYMBOL_GPL(generic_fh_to_dentry); | ||
735 | |||
736 | /** | ||
737 | * generic_fh_to_dentry - generic helper for the fh_to_parent export operation | ||
738 | * @sb: filesystem to do the file handle conversion on | ||
739 | * @fid: file handle to convert | ||
740 | * @fh_len: length of the file handle in bytes | ||
741 | * @fh_type: type of file handle | ||
742 | * @get_inode: filesystem callback to retrieve inode | ||
743 | * | ||
744 | * This function decodes @fid as long as it has one of the well-known | ||
745 | * Linux filehandle types and calls @get_inode on it to retrieve the | ||
746 | * inode for the _parent_ object specified in the file handle if it | ||
747 | * is specified in the file handle, or NULL otherwise. | ||
748 | */ | ||
749 | struct dentry *generic_fh_to_parent(struct super_block *sb, struct fid *fid, | ||
750 | int fh_len, int fh_type, struct inode *(*get_inode) | ||
751 | (struct super_block *sb, u64 ino, u32 gen)) | ||
752 | { | ||
753 | struct inode *inode = NULL; | ||
754 | |||
755 | if (fh_len <= 2) | ||
756 | return NULL; | ||
757 | |||
758 | switch (fh_type) { | ||
759 | case FILEID_INO32_GEN_PARENT: | ||
760 | inode = get_inode(sb, fid->i32.parent_ino, | ||
761 | (fh_len > 3 ? fid->i32.parent_gen : 0)); | ||
762 | break; | ||
763 | } | ||
764 | |||
765 | return exportfs_d_alloc(inode); | ||
766 | } | ||
767 | EXPORT_SYMBOL_GPL(generic_fh_to_parent); | ||
768 | |||
681 | EXPORT_SYMBOL(dcache_dir_close); | 769 | EXPORT_SYMBOL(dcache_dir_close); |
682 | EXPORT_SYMBOL(dcache_dir_lseek); | 770 | EXPORT_SYMBOL(dcache_dir_lseek); |
683 | EXPORT_SYMBOL(dcache_dir_open); | 771 | EXPORT_SYMBOL(dcache_dir_open); |
diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h index 06178a1336ff..b44f6b6871c8 100644 --- a/include/linux/exportfs.h +++ b/include/linux/exportfs.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | 5 | ||
6 | struct dentry; | 6 | struct dentry; |
7 | struct inode; | ||
7 | struct super_block; | 8 | struct super_block; |
8 | struct vfsmount; | 9 | struct vfsmount; |
9 | 10 | ||
@@ -101,6 +102,21 @@ struct fid { | |||
101 | * the filehandle fragment. encode_fh() should return the number of bytes | 102 | * the filehandle fragment. encode_fh() should return the number of bytes |
102 | * stored or a negative error code such as %-ENOSPC | 103 | * stored or a negative error code such as %-ENOSPC |
103 | * | 104 | * |
105 | * fh_to_dentry: | ||
106 | * @fh_to_dentry is given a &struct super_block (@sb) and a file handle | ||
107 | * fragment (@fh, @fh_len). It should return a &struct dentry which refers | ||
108 | * to the same file that the file handle fragment refers to. If it cannot, | ||
109 | * it should return a %NULL pointer if the file was found but no acceptable | ||
110 | * &dentries were available, or an %ERR_PTR error code indicating why it | ||
111 | * couldn't be found (e.g. %ENOENT or %ENOMEM). Any suitable dentry can be | ||
112 | * returned including, if necessary, a new dentry created with d_alloc_root. | ||
113 | * The caller can then find any other extant dentries by following the | ||
114 | * d_alias links. | ||
115 | * | ||
116 | * fh_to_parent: | ||
117 | * Same as @fh_to_dentry, except that it returns a pointer to the parent | ||
118 | * dentry if it was encoded into the filehandle fragment by @encode_fh. | ||
119 | * | ||
104 | * get_name: | 120 | * get_name: |
105 | * @get_name should find a name for the given @child in the given @parent | 121 | * @get_name should find a name for the given @child in the given @parent |
106 | * directory. The name should be stored in the @name (with the | 122 | * directory. The name should be stored in the @name (with the |
@@ -139,6 +155,10 @@ struct export_operations { | |||
139 | void *context); | 155 | void *context); |
140 | int (*encode_fh)(struct dentry *de, __u32 *fh, int *max_len, | 156 | int (*encode_fh)(struct dentry *de, __u32 *fh, int *max_len, |
141 | int connectable); | 157 | int connectable); |
158 | struct dentry * (*fh_to_dentry)(struct super_block *sb, struct fid *fid, | ||
159 | int fh_len, int fh_type); | ||
160 | struct dentry * (*fh_to_parent)(struct super_block *sb, struct fid *fid, | ||
161 | int fh_len, int fh_type); | ||
142 | int (*get_name)(struct dentry *parent, char *name, | 162 | int (*get_name)(struct dentry *parent, char *name, |
143 | struct dentry *child); | 163 | struct dentry *child); |
144 | struct dentry * (*get_parent)(struct dentry *child); | 164 | struct dentry * (*get_parent)(struct dentry *child); |
@@ -161,4 +181,14 @@ extern struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, | |||
161 | int fh_len, int fileid_type, int (*acceptable)(void *, struct dentry *), | 181 | int fh_len, int fileid_type, int (*acceptable)(void *, struct dentry *), |
162 | void *context); | 182 | void *context); |
163 | 183 | ||
184 | /* | ||
185 | * Generic helpers for filesystems. | ||
186 | */ | ||
187 | extern struct dentry *generic_fh_to_dentry(struct super_block *sb, | ||
188 | struct fid *fid, int fh_len, int fh_type, | ||
189 | struct inode *(*get_inode) (struct super_block *sb, u64 ino, u32 gen)); | ||
190 | extern struct dentry *generic_fh_to_parent(struct super_block *sb, | ||
191 | struct fid *fid, int fh_len, int fh_type, | ||
192 | struct inode *(*get_inode) (struct super_block *sb, u64 ino, u32 gen)); | ||
193 | |||
164 | #endif /* LINUX_EXPORTFS_H */ | 194 | #endif /* LINUX_EXPORTFS_H */ |