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) ...@@ -279,14 +279,18 @@ struct dentry * dget_locked(struct dentry *dentry)
/** /**
* d_find_alias - grab a hashed alias of inode * d_find_alias - grab a hashed alias of inode
* @inode: inode in question * @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 * If inode has a hashed alias, or is a directory and has any alias,
* return it. Otherwise return NULL. Notice that if inode is a directory * acquire the reference to alias and return it. Otherwise return NULL.
* there can be only one alias and it can be unhashed only if it has * Notice that if inode is a directory there can be only one alias and
* no children. * 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 * 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) 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) ...@@ -301,7 +305,7 @@ static struct dentry * __d_find_alias(struct inode *inode, int want_discon)
next = tmp->next; next = tmp->next;
prefetch(next); prefetch(next);
alias = list_entry(tmp, struct dentry, d_alias); 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) if (alias->d_flags & DCACHE_DISCONNECTED)
discon_alias = alias; discon_alias = alias;
else if (!want_discon) { 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