Commit 019ab801 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Linus Torvalds

knfsd: exportfs: split out reconnecting a dentry from find_exported_dentry

There's a clear subfunctionality of reconnecting a given dentry to the main
dentry tree in find_exported_dentry, that can be called both for the dentry
we're looking for or it's parent directory.

This patch splits the subfunctionality out into a separate helper to make the
code more readable and document it's intent.  As a nice side-optimization we
can avoid getting a superfluous dentry reference count in the case we need to
reconnect a directory on it's own.
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarNeil Brown <neilb@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent dd90b509
...@@ -91,101 +91,27 @@ find_disconnected_root(struct dentry *dentry) ...@@ -91,101 +91,27 @@ find_disconnected_root(struct dentry *dentry)
return dentry; return dentry;
} }
/**
* find_exported_dentry - helper routine to implement export_operations->decode_fh /*
* @sb: The &super_block identifying the filesystem * Make sure target_dir is fully connected to the dentry tree.
* @obj: An opaque identifier of the object to be found - passed to
* get_inode
* @parent: An optional opqaue identifier of the parent of the object.
* @acceptable: A function used to test possible &dentries to see if they are
* acceptable
* @context: A parameter to @acceptable so that it knows on what basis to
* judge.
*
* find_exported_dentry is the central helper routine to enable file systems
* to provide the decode_fh() export_operation. It's main task is to take
* an &inode, find or create an appropriate &dentry structure, and possibly
* splice this into the dcache in the correct place.
*
* The decode_fh() operation provided by the filesystem should call
* find_exported_dentry() with the same parameters that it received except
* that instead of the file handle fragment, pointers to opaque identifiers
* for the object and optionally its parent are passed. The default decode_fh
* routine passes one pointer to the start of the filehandle fragment, and
* one 8 bytes into the fragment. It is expected that most filesystems will
* take this approach, though the offset to the parent identifier may well be
* different.
* *
* find_exported_dentry() will call get_dentry to get an dentry pointer from * It may already be, as the flag isn't always updated when connection happens.
* the file system. If any &dentry in the d_alias list is acceptable, it will
* be returned. Otherwise find_exported_dentry() will attempt to splice a new
* &dentry into the dcache using get_name() and get_parent() to find the
* appropriate place.
*/ */
static int
struct dentry * reconnect_path(struct super_block *sb, struct dentry *target_dir)
find_exported_dentry(struct super_block *sb, void *obj, void *parent,
int (*acceptable)(void *context, struct dentry *de),
void *context)
{ {
struct dentry *result = NULL;
struct dentry *target_dir;
int err = -ESTALE;
struct export_operations *nops = sb->s_export_op;
struct dentry *alias;
int noprogress;
char nbuf[NAME_MAX+1]; char nbuf[NAME_MAX+1];
int noprogress = 0;
int err = -ESTALE;
/* /*
* Attempt to find the inode. * It is possible that a confused file system might not let us complete
*/
result = exportfs_get_dentry(sb, obj);
if (IS_ERR(result))
return result;
if (S_ISDIR(result->d_inode->i_mode)) {
if (!(result->d_flags & DCACHE_DISCONNECTED)) {
if (acceptable(context, result))
return result;
err = -EACCES;
goto err_result;
}
target_dir = dget(result);
} else {
alias = find_acceptable_alias(result, acceptable, context);
if (alias)
return alias;
if (parent == NULL)
goto err_result;
target_dir = exportfs_get_dentry(sb,parent);
if (IS_ERR(target_dir)) {
err = PTR_ERR(target_dir);
goto err_result;
}
}
/*
* Now we need to make sure that target_dir is properly connected.
* It may already be, as the flag isn't always updated when connection
* happens.
* So, we walk up parent links until we find a connected directory,
* or we run out of directories. Then we find the parent, find
* the name of the child in that parent, and do a lookup.
* This should connect the child into the parent
* We then repeat.
*/
/* it is possible that a confused file system might not let us complete
* the path to the root. For example, if get_parent returns a directory * the path to the root. For example, if get_parent returns a directory
* in which we cannot find a name for the child. While this implies a * in which we cannot find a name for the child. While this implies a
* very sick filesystem we don't want it to cause knfsd to spin. Hence * very sick filesystem we don't want it to cause knfsd to spin. Hence
* the noprogress counter. If we go through the loop 10 times (2 is * the noprogress counter. If we go through the loop 10 times (2 is
* probably enough) without getting anywhere, we just give up * probably enough) without getting anywhere, we just give up
*/ */
noprogress = 0;
while (target_dir->d_flags & DCACHE_DISCONNECTED && noprogress++ < 10) { while (target_dir->d_flags & DCACHE_DISCONNECTED && noprogress++ < 10) {
struct dentry *pd = find_disconnected_root(target_dir); struct dentry *pd = find_disconnected_root(target_dir);
...@@ -221,18 +147,20 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent, ...@@ -221,18 +147,20 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
struct dentry *npd; struct dentry *npd;
mutex_lock(&pd->d_inode->i_mutex); mutex_lock(&pd->d_inode->i_mutex);
if (nops->get_parent) if (sb->s_export_op->get_parent)
ppd = nops->get_parent(pd); ppd = sb->s_export_op->get_parent(pd);
mutex_unlock(&pd->d_inode->i_mutex); mutex_unlock(&pd->d_inode->i_mutex);
if (IS_ERR(ppd)) { if (IS_ERR(ppd)) {
err = PTR_ERR(ppd); err = PTR_ERR(ppd);
dprintk("find_exported_dentry: get_parent of %ld failed, err %d\n", dprintk("%s: get_parent of %ld failed, err %d\n",
pd->d_inode->i_ino, err); __FUNCTION__, pd->d_inode->i_ino, err);
dput(pd); dput(pd);
break; break;
} }
dprintk("find_exported_dentry: find name of %lu in %lu\n", pd->d_inode->i_ino, ppd->d_inode->i_ino);
dprintk("%s: find name of %lu in %lu\n", __FUNCTION__,
pd->d_inode->i_ino, ppd->d_inode->i_ino);
err = exportfs_get_name(ppd, nbuf, pd); err = exportfs_get_name(ppd, nbuf, pd);
if (err) { if (err) {
dput(ppd); dput(ppd);
...@@ -244,13 +172,14 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent, ...@@ -244,13 +172,14 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
continue; continue;
break; break;
} }
dprintk("find_exported_dentry: found name: %s\n", nbuf); dprintk("%s: found name: %s\n", __FUNCTION__, nbuf);
mutex_lock(&ppd->d_inode->i_mutex); mutex_lock(&ppd->d_inode->i_mutex);
npd = lookup_one_len(nbuf, ppd, strlen(nbuf)); npd = lookup_one_len(nbuf, ppd, strlen(nbuf));
mutex_unlock(&ppd->d_inode->i_mutex); mutex_unlock(&ppd->d_inode->i_mutex);
if (IS_ERR(npd)) { if (IS_ERR(npd)) {
err = PTR_ERR(npd); err = PTR_ERR(npd);
dprintk("find_exported_dentry: lookup failed: %d\n", err); dprintk("%s: lookup failed: %d\n",
__FUNCTION__, err);
dput(ppd); dput(ppd);
dput(pd); dput(pd);
break; break;
...@@ -263,7 +192,7 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent, ...@@ -263,7 +192,7 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
if (npd == pd) if (npd == pd)
noprogress = 0; noprogress = 0;
else else
printk("find_exported_dentry: npd != pd\n"); printk("%s: npd != pd\n", __FUNCTION__);
dput(npd); dput(npd);
dput(ppd); dput(ppd);
if (IS_ROOT(pd)) { if (IS_ROOT(pd)) {
...@@ -279,15 +208,101 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent, ...@@ -279,15 +208,101 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
/* something went wrong - oh-well */ /* something went wrong - oh-well */
if (!err) if (!err)
err = -ESTALE; err = -ESTALE;
goto err_target; return err;
}
return 0;
}
/**
* find_exported_dentry - helper routine to implement export_operations->decode_fh
* @sb: The &super_block identifying the filesystem
* @obj: An opaque identifier of the object to be found - passed to
* get_inode
* @parent: An optional opqaue identifier of the parent of the object.
* @acceptable: A function used to test possible &dentries to see if they are
* acceptable
* @context: A parameter to @acceptable so that it knows on what basis to
* judge.
*
* find_exported_dentry is the central helper routine to enable file systems
* to provide the decode_fh() export_operation. It's main task is to take
* an &inode, find or create an appropriate &dentry structure, and possibly
* splice this into the dcache in the correct place.
*
* The decode_fh() operation provided by the filesystem should call
* find_exported_dentry() with the same parameters that it received except
* that instead of the file handle fragment, pointers to opaque identifiers
* for the object and optionally its parent are passed. The default decode_fh
* routine passes one pointer to the start of the filehandle fragment, and
* one 8 bytes into the fragment. It is expected that most filesystems will
* take this approach, though the offset to the parent identifier may well be
* different.
*
* find_exported_dentry() will call get_dentry to get an dentry pointer from
* the file system. If any &dentry in the d_alias list is acceptable, it will
* be returned. Otherwise find_exported_dentry() will attempt to splice a new
* &dentry into the dcache using get_name() and get_parent() to find the
* appropriate place.
*/
struct dentry *
find_exported_dentry(struct super_block *sb, void *obj, void *parent,
int (*acceptable)(void *context, struct dentry *de),
void *context)
{
struct dentry *result, *alias;
int err = -ESTALE;
/*
* Attempt to find the inode.
*/
result = exportfs_get_dentry(sb, obj);
if (IS_ERR(result))
return result;
if (S_ISDIR(result->d_inode->i_mode)) {
if (!(result->d_flags & DCACHE_DISCONNECTED)) {
if (acceptable(context, result))
return result;
err = -EACCES;
goto err_result;
} }
/* if we weren't after a directory, have one more step to go */
if (result != target_dir) { err = reconnect_path(sb, result);
struct dentry *nresult; if (err)
goto err_result;
} else {
struct dentry *target_dir, *nresult;
char nbuf[NAME_MAX+1];
alias = find_acceptable_alias(result, acceptable, context);
if (alias)
return alias;
if (parent == NULL)
goto err_result;
target_dir = exportfs_get_dentry(sb,parent);
if (IS_ERR(target_dir)) {
err = PTR_ERR(target_dir);
goto err_result;
}
err = reconnect_path(sb, target_dir);
if (err) {
dput(target_dir);
goto err_result;
}
/*
* As we weren't after a directory, have one more step to go.
*/
err = exportfs_get_name(target_dir, nbuf, result); err = exportfs_get_name(target_dir, nbuf, result);
if (!err) { if (!err) {
mutex_lock(&target_dir->d_inode->i_mutex); mutex_lock(&target_dir->d_inode->i_mutex);
nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf)); nresult = lookup_one_len(nbuf, target_dir,
strlen(nbuf));
mutex_unlock(&target_dir->d_inode->i_mutex); mutex_unlock(&target_dir->d_inode->i_mutex);
if (!IS_ERR(nresult)) { if (!IS_ERR(nresult)) {
if (nresult->d_inode) { if (nresult->d_inode) {
...@@ -297,8 +312,8 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent, ...@@ -297,8 +312,8 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
dput(nresult); dput(nresult);
} }
} }
}
dput(target_dir); dput(target_dir);
}
alias = find_acceptable_alias(result, acceptable, context); alias = find_acceptable_alias(result, acceptable, context);
if (alias) if (alias)
...@@ -308,13 +323,11 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent, ...@@ -308,13 +323,11 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
dput(result); dput(result);
/* It might be justifiable to return ESTALE here, /* It might be justifiable to return ESTALE here,
* but the filehandle at-least looks reasonable good * but the filehandle at-least looks reasonable good
* and it just be a permission problem, so returning * and it may just be a permission problem, so returning
* -EACCESS is safer * -EACCESS is safer
*/ */
return ERR_PTR(-EACCES); return ERR_PTR(-EACCES);
err_target:
dput(target_dir);
err_result: err_result:
dput(result); dput(result);
return ERR_PTR(err); return ERR_PTR(err);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment