From: NeilBrown Rather than performing a normal lookup (which will be awkward with future locking changes) use d_alloc_trylock() to find a dentry for an unused name, and then open-code the rest of lookup_slow() to see if it is free on the server. Acked-by: Anna Schumaker Signed-off-by: NeilBrown --- fs/nfs/unlink.c | 59 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 11a46a993e11..0eddc7ee1115 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -451,7 +451,7 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry) static unsigned int sillycounter; unsigned char silly[SILLYNAME_LEN + 1]; unsigned long long fileid; - struct dentry *sdentry; + struct dentry *sdentry, *old; struct inode *inode = d_inode(dentry); struct rpc_task *task; int error = -EBUSY; @@ -468,26 +468,45 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry) fileid = d_inode(dentry)->i_ino; - sdentry = NULL; - do { +newname: + sillycounter++; + scnprintf(silly, sizeof(silly), + SILLYNAME_PREFIX "%0*llx%0*x", + SILLYNAME_FILEID_LEN, fileid, + SILLYNAME_COUNTER_LEN, sillycounter); + + dfprintk(VFS, "NFS: trying to rename %pd to %s\n", dentry, silly); + sdentry = d_alloc_trylock(dentry->d_parent, &QSTR(silly)); + if (sdentry == ERR_PTR(-EWOULDBLOCK)) + /* Name currently being looked up */ + goto newname; + if (IS_ERR(sdentry)) + goto out; + if (!d_in_lookup(sdentry)) { + if (d_really_is_negative(sdentry)) { + /* try to get an in-lookup dentry */ + d_drop(sdentry); + sillycounter--; + } dput(sdentry); - sillycounter++; - scnprintf(silly, sizeof(silly), - SILLYNAME_PREFIX "%0*llx%0*x", - SILLYNAME_FILEID_LEN, fileid, - SILLYNAME_COUNTER_LEN, sillycounter); - - dfprintk(VFS, "NFS: trying to rename %pd to %s\n", - dentry, silly); - - sdentry = lookup_noperm(&QSTR(silly), dentry->d_parent); - /* - * N.B. Better to return EBUSY here ... it could be - * dangerous to delete the file while it's in use. - */ - if (IS_ERR(sdentry)) - goto out; - } while (d_inode(sdentry) != NULL); /* need negative lookup */ + goto newname; + } + /* This name isn't known locally - check on server */ + old = nfs_lookup(dir, sdentry, 0); + d_lookup_done(sdentry); + /* + * N.B. Better to return EBUSY here ... it could be + * dangerous to delete the file while it's in use. + */ + if (IS_ERR(old)) { + dput(sdentry); + goto out; + } + if (old || d_is_positive(sdentry)) { + dput(old); + dput(sdentry); + goto newname; + } ihold(inode); -- 2.50.0.107.gf914562f5916.dirty