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>
This commit is contained in:
Christoph Hellwig
2007-10-21 16:42:05 -07:00
committed by Linus Torvalds
parent 6e91ea2bb0
commit 2596110a39
3 changed files with 249 additions and 7 deletions

View File

@@ -514,17 +514,141 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
int (*acceptable)(void *, struct dentry *), void *context)
{
struct export_operations *nop = mnt->mnt_sb->s_export_op;
struct dentry *result;
struct dentry *result, *alias;
int err;
if (nop->decode_fh) {
result = nop->decode_fh(mnt->mnt_sb, fid->raw, fh_len,
/*
* Old way of doing things. Will go away soon.
*/
if (!nop->fh_to_dentry) {
if (nop->decode_fh) {
return nop->decode_fh(mnt->mnt_sb, fid->raw, fh_len,
fileid_type, acceptable, context);
} else {
result = export_decode_fh(mnt->mnt_sb, fid->raw, fh_len,
fileid_type, acceptable, context);
} else {
return export_decode_fh(mnt->mnt_sb, fid->raw, fh_len,
fileid_type, acceptable, context);
}
}
return result;
/*
* Try to get any dentry for the given file handle from the filesystem.
*/
result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type);
if (!result)
result = ERR_PTR(-ESTALE);
if (IS_ERR(result))
return result;
if (S_ISDIR(result->d_inode->i_mode)) {
/*
* This request is for a directory.
*
* On the positive side there is only one dentry for each
* directory inode. On the negative side this implies that we
* to ensure our dentry is connected all the way up to the
* filesystem root.
*/
if (result->d_flags & DCACHE_DISCONNECTED) {
err = reconnect_path(mnt->mnt_sb, result);
if (err)
goto err_result;
}
if (!acceptable(context, result)) {
err = -EACCES;
goto err_result;
}
return result;
} else {
/*
* It's not a directory. Life is a little more complicated.
*/
struct dentry *target_dir, *nresult;
char nbuf[NAME_MAX+1];
/*
* See if either the dentry we just got from the filesystem
* or any alias for it is acceptable. This is always true
* if this filesystem is exported without the subtreecheck
* option. If the filesystem is exported with the subtree
* check option there's a fair chance we need to look at
* the parent directory in the file handle and make sure
* it's connected to the filesystem root.
*/
alias = find_acceptable_alias(result, acceptable, context);
if (alias)
return alias;
/*
* Try to extract a dentry for the parent directory from the
* file handle. If this fails we'll have to give up.
*/
err = -ESTALE;
if (!nop->fh_to_parent)
goto err_result;
target_dir = nop->fh_to_parent(mnt->mnt_sb, fid,
fh_len, fileid_type);
if (!target_dir)
goto err_result;
err = PTR_ERR(target_dir);
if (IS_ERR(target_dir))
goto err_result;
/*
* And as usual we need to make sure the parent directory is
* connected to the filesystem root. The VFS really doesn't
* like disconnected directories..
*/
err = reconnect_path(mnt->mnt_sb, target_dir);
if (err) {
dput(target_dir);
goto err_result;
}
/*
* Now that we've got both a well-connected parent and a
* dentry for the inode we're after, make sure that our
* inode is actually connected to the parent.
*/
err = exportfs_get_name(target_dir, nbuf, result);
if (!err) {
mutex_lock(&target_dir->d_inode->i_mutex);
nresult = lookup_one_len(nbuf, target_dir,
strlen(nbuf));
mutex_unlock(&target_dir->d_inode->i_mutex);
if (!IS_ERR(nresult)) {
if (nresult->d_inode) {
dput(result);
result = nresult;
} else
dput(nresult);
}
}
/*
* At this point we are done with the parent, but it's pinned
* by the child dentry anyway.
*/
dput(target_dir);
/*
* And finally make sure the dentry is actually acceptable
* to NFSD.
*/
alias = find_acceptable_alias(result, acceptable, context);
if (!alias) {
err = -EACCES;
goto err_result;
}
return alias;
}
err_result:
dput(result);
return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(exportfs_decode_fh);