@@ -1664,11 +1664,9 @@ static struct dentry *lookup_dcache(const struct qstr *name,
}
/*
- * Parent directory has inode locked exclusive. This is one
- * and only case when ->lookup() gets called on non in-lookup
- * dentries - as the matter of fact, this only gets called
- * when directory is guaranteed to have no in-lookup children
- * at all.
+ * Parent directory has inode locked exclusive.
+ * If @flags contains any LOOKUP_INTENT_FLAGS then d_lookup_done()
+ * must be called after the intended operation is performed - or aborted.
*/
struct dentry *lookup_one_qstr_excl(const struct qstr *name,
struct dentry *base,
@@ -1685,15 +1683,22 @@ struct dentry *lookup_one_qstr_excl(const struct qstr *name,
if (unlikely(IS_DEADDIR(dir)))
return ERR_PTR(-ENOENT);
- dentry = d_alloc(base, name);
- if (unlikely(!dentry))
+ dentry = d_alloc_parallel(base, name);
+ if (unlikely(IS_ERR_OR_NULL(dentry)))
return ERR_PTR(-ENOMEM);
+ if (!d_in_lookup(dentry))
+ /* Raced with another thread which did the lookup */
+ return dentry;
old = dir->i_op->lookup(dir, dentry, flags);
if (unlikely(old)) {
+ d_lookup_done(dentry);
dput(dentry);
dentry = old;
}
+ if ((flags & LOOKUP_INTENT_FLAGS) == 0)
+ /* ->lookup must have given final answer */
+ d_lookup_done(dentry);
return dentry;
}
EXPORT_SYMBOL(lookup_one_qstr_excl);
@@ -4112,6 +4117,7 @@ static struct dentry *filename_create(int dfd, struct filename *name,
}
return dentry;
fail:
+ d_lookup_done(dentry);
dput(dentry);
dentry = ERR_PTR(error);
unlock:
@@ -5340,6 +5346,7 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
rd.flags = flags;
error = vfs_rename(&rd);
exit5:
+ d_lookup_done(new_dentry);
dput(new_dentry);
exit4:
dput(old_dentry);
@@ -772,6 +772,7 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
ksmbd_debug(VFS, "vfs_rename failed err %d\n", err);
out4:
+ d_lookup_done(new_dentry);
dput(new_dentry);
out3:
dput(old_parent);
@@ -34,6 +34,9 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT};
#define LOOKUP_EXCL 0x0400 /* ... in exclusive creation */
#define LOOKUP_RENAME_TARGET 0x0800 /* ... in destination of rename() */
+#define LOOKUP_INTENT_FLAGS (LOOKUP_OPEN | LOOKUP_CREATE | LOOKUP_EXCL | \
+ LOOKUP_RENAME_TARGET)
+
/* internal use only */
#define LOOKUP_PARENT 0x0010
lookup_one_qstr_excl() is used for lookups prior to directory modifications, whether create, unlink, rename, or whatever. To prepare for allowing modification to happen in parallel, change lookup_one_qstr_excl() to use d_alloc_parallel(). If any for the "intent" LOOKUP flags are passed, the caller must ensure d_lookup_done() is called at an appropriate time. If none are passed then we can be sure ->lookup() will do a real lookup and d_lookup_done() is called internally. Signed-off-by: NeilBrown <neilb@suse.de> --- fs/namei.c | 21 ++++++++++++++------- fs/smb/server/vfs.c | 1 + include/linux/namei.h | 3 +++ 3 files changed, 18 insertions(+), 7 deletions(-)