66 lines
2.3 KiB
Diff
66 lines
2.3 KiB
Diff
From: "Eric W. Biederman" <ebiederm@xmission.com>
|
|
Date: Sat, 15 Aug 2015 13:36:41 -0500
|
|
Subject: dcache: Reduce the scope of i_lock in d_splice_alias
|
|
Origin: https://git.kernel.org/linus/a03e283bf5c3d4851b4998122196ce9f849e6dfb
|
|
|
|
i_lock is only needed until __d_find_any_alias calls dget on the alias
|
|
dentry. After that the reference to new ensures that dentry_kill and
|
|
d_delete will not remove the inode from the dentry, and remove the
|
|
dentry from the inode->d_entry list.
|
|
|
|
The inode i_lock came to be held over the the __d_move calls in
|
|
d_splice_alias through a series of introduction of locks with
|
|
increasing smaller scope. First it was the dcache_lock, then
|
|
it was the dcache_inode_lock, and finally inode->i_lock.
|
|
|
|
Furthermore inode->i_lock is not held over any other calls
|
|
to d_move or __d_move so it can not provide any meaningful
|
|
rename protection.
|
|
|
|
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
|
|
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
|
---
|
|
fs/dcache.c | 7 +++----
|
|
1 file changed, 3 insertions(+), 4 deletions(-)
|
|
|
|
--- a/fs/dcache.c
|
|
+++ b/fs/dcache.c
|
|
@@ -2719,7 +2719,7 @@ struct dentry *d_ancestor(struct dentry
|
|
* This helper attempts to cope with remotely renamed directories
|
|
*
|
|
* It assumes that the caller is already holding
|
|
- * dentry->d_parent->d_inode->i_mutex, inode->i_lock and rename_lock
|
|
+ * dentry->d_parent->d_inode->i_mutex, and rename_lock
|
|
*
|
|
* Note: If ever the locking in lock_rename() changes, then please
|
|
* remember to update this too...
|
|
@@ -2745,7 +2745,6 @@ out_unalias:
|
|
__d_move(alias, dentry, false);
|
|
ret = 0;
|
|
out_err:
|
|
- spin_unlock(&inode->i_lock);
|
|
if (m2)
|
|
mutex_unlock(m2);
|
|
if (m1)
|
|
@@ -2791,10 +2790,11 @@ struct dentry *d_splice_alias(struct ino
|
|
if (S_ISDIR(inode->i_mode)) {
|
|
struct dentry *new = __d_find_any_alias(inode);
|
|
if (unlikely(new)) {
|
|
+ /* The reference to new ensures it remains an alias */
|
|
+ spin_unlock(&inode->i_lock);
|
|
write_seqlock(&rename_lock);
|
|
if (unlikely(d_ancestor(new, dentry))) {
|
|
write_sequnlock(&rename_lock);
|
|
- spin_unlock(&inode->i_lock);
|
|
dput(new);
|
|
new = ERR_PTR(-ELOOP);
|
|
pr_warn_ratelimited(
|
|
@@ -2813,7 +2813,6 @@ struct dentry *d_splice_alias(struct ino
|
|
} else {
|
|
__d_move(new, dentry, false);
|
|
write_sequnlock(&rename_lock);
|
|
- spin_unlock(&inode->i_lock);
|
|
security_d_instantiate(new, inode);
|
|
}
|
|
iput(inode);
|