diff -urN 2.2.19pre3/fs/nfsd/nfsfh.c nfsd/fs/nfsd/nfsfh.c --- 2.2.19pre3/fs/nfsd/nfsfh.c Mon Dec 11 16:58:01 2000 +++ nfsd/fs/nfsd/nfsfh.c Sun Dec 24 17:01:28 2000 @@ -114,6 +114,38 @@ return error; } +/* Arrange a dentry for the given inode: + * 1. Prefer an existing connected dentry. + * 2. Settle for an existing disconnected dentry. + * 3. If necessary, create a (disconnected) dentry. + */ +static struct dentry *nfsd_arrange_dentry(struct inode *inode) +{ + struct list_head *lp; + struct dentry *result; + + result = NULL; + for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) { + result = list_entry(lp,struct dentry, d_alias); + if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) + break; + } + if (result) { + dget(result); + iput(inode); + } else { + result = d_alloc_root(inode, NULL); + if (!result) { + iput(inode); + return ERR_PTR(-ENOMEM); + } + result->d_flags |= DCACHE_NFSD_DISCONNECTED; + d_rehash(result); /* so a dput won't loose it */ + } + return result; +} + + /* this should be provided by each filesystem in an nfsd_operations interface as * iget isn't really the right interface */ @@ -128,8 +160,6 @@ * so a generation of 0 means "accept any" */ struct inode *inode; - struct list_head *lp; - struct dentry *result; inode = iget_in_use(sb, ino); if (!inode) { dprintk("nfsd_iget: failed to find ino: %lu on %s\n", @@ -149,25 +179,8 @@ iput(inode); return ERR_PTR(-ESTALE); } - /* now to find a dentry. - * If possible, get a well-connected one - */ - for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) { - result = list_entry(lp,struct dentry, d_alias); - if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) { - dget(result); - iput(inode); - return result; - } - } - result = d_alloc_root(inode, NULL); - if (result == NULL) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - result->d_flags |= DCACHE_NFSD_DISCONNECTED; - d_rehash(result); /* so a dput won't loose it */ - return result; + + return nfsd_arrange_dentry(inode); } /* this routine links an IS_ROOT dentry into the dcache tree. It gains "parent" @@ -228,45 +241,27 @@ */ struct dentry *nfsd_findparent(struct dentry *child) { - struct dentry *tdentry, *pdentry; - tdentry = d_alloc(child, &(const struct qstr) {"..", 2, 0}); - if (!tdentry) + struct dentry *dotdot, *parent; + + dotdot = d_alloc(child, &(const struct qstr) {"..", 2, 0}); + if (!dotdot) return ERR_PTR(-ENOMEM); /* I'm going to assume that if the returned dentry is different, then * it is well connected. But nobody returns different dentrys do they? */ - pdentry = child->d_inode->i_op->lookup(child->d_inode, tdentry); - d_drop(tdentry); /* we never want ".." hashed */ - if (!pdentry) { - /* I don't want to return a ".." dentry. - * I would prefer to return an unconnected "IS_ROOT" dentry, - * though a properly connected dentry is even better - */ - /* if first or last of alias list is not tdentry, use that - * else make a root dentry - */ - struct list_head *aliases = &tdentry->d_inode->i_dentry; - if (aliases->next != aliases) { - pdentry = list_entry(aliases->next, struct dentry, d_alias); - if (pdentry == tdentry) - pdentry = list_entry(aliases->prev, struct dentry, d_alias); - if (pdentry == tdentry) - pdentry = NULL; - if (pdentry) dget(pdentry); - } - if (pdentry == NULL) { - pdentry = d_alloc_root(igrab(tdentry->d_inode), NULL); - if (pdentry) { - pdentry->d_flags |= DCACHE_NFSD_DISCONNECTED; - d_rehash(pdentry); - } - } - if (pdentry == NULL) - pdentry = ERR_PTR(-ENOMEM); + parent = child->d_inode->i_op->lookup(child->d_inode, dotdot); + d_drop(dotdot); /* we never want ".." hashed */ + + if (parent) + dput(dotdot); /* not hashed, thus discarded */ + else { + /* Discard the ".." dentry, then arrange for a better one */ + struct inode *inode = igrab(dotdot->d_inode); + dput(dotdot); /* not hashed, thus discarded */ + parent = nfsd_arrange_dentry(inode); } - dput(tdentry); /* it is not hashed, it will be discarded */ - return pdentry; + return parent; } static struct dentry *splice(struct dentry *child, struct dentry *parent)