Commit f1a2bdba authored by Neil Brown's avatar Neil Brown Committed by Linus Torvalds

[PATCH] kNFSd: fix d_find_alias brokenness

10 weeks ago,
  http://linux.bkbits.com:8080/linux-2.5/cset@415b3380pxf4sB97gM8ujLqDxi6GfQ

The patch was mostly right, and fixed a real problem, but missed a bit.

It passed the job of checking if an inode had a current alias off to
d_find_alias instead of open-coding it in d_alloc_anon.  However there is one
case where d_alloc_anon would not return the right dentry.  That case being
when the inode was for the root of the filesystem.

The root is a special case because it is not hashed.  All other dentries that
are not hashed are quite un-interesting: There are "unlinked" but not yet
closed.  The root of a filesystem is unhashed, but is interesting.

Allowing d_find_alias to return an unhashed alias for a directory solves this
problem.  It is safe because callers of d_find_alias on a directory inode
either have a name for the inode already (so finding an unlinked directory by
mistake is impossible) or will soon be looking for a name and will drop the
dentry if a name is not found.
Signed-off-by: default avatarNeil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent d9f868c4
......@@ -279,14 +279,18 @@ struct dentry * dget_locked(struct dentry *dentry)
/**
* d_find_alias - grab a hashed alias of inode
* @inode: inode in question
* @want_discon: flag, used by d_splice_alias, to request
* that only a DISCONNECTED alias be returned.
*
* If inode has a hashed alias - acquire the reference to alias and
* return it. Otherwise return NULL. Notice that if inode is a directory
* there can be only one alias and it can be unhashed only if it has
* no children.
* If inode has a hashed alias, or is a directory and has any alias,
* acquire the reference to alias and return it. Otherwise return NULL.
* Notice that if inode is a directory there can be only one alias and
* it can be unhashed only if it has no children, or if it is the root
* of a filesystem.
*
* If the inode has a DCACHE_DISCONNECTED alias, then prefer
* any other hashed alias over that one.
* any other hashed alias over that one unless @want_discon is set,
* in which case only return a DCACHE_DISCONNECTED alias.
*/
static struct dentry * __d_find_alias(struct inode *inode, int want_discon)
......@@ -301,7 +305,7 @@ static struct dentry * __d_find_alias(struct inode *inode, int want_discon)
next = tmp->next;
prefetch(next);
alias = list_entry(tmp, struct dentry, d_alias);
if (!d_unhashed(alias)) {
if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) {
if (alias->d_flags & DCACHE_DISCONNECTED)
discon_alias = alias;
else if (!want_discon) {
......
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