diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2023-11-20 20:02:11 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2023-11-25 02:54:14 -0500 |
commit | a8b0026847b8c43445c921ad2c85521c92eb175f (patch) | |
tree | 39078ede8594fab57ee0486e522655df86cb131f /fs/smb/server | |
parent | dbd4540df2b2857a91593754275c02f3e415fc30 (diff) | |
download | lwn-a8b0026847b8c43445c921ad2c85521c92eb175f.tar.gz lwn-a8b0026847b8c43445c921ad2c85521c92eb175f.zip |
rename(): avoid a deadlock in the case of parents having no common ancestor
... and fix the directory locking documentation and proof of correctness.
Holding ->s_vfs_rename_mutex *almost* prevents ->d_parent changes; the
case where we really don't want it is splicing the root of disconnected
tree to somewhere.
In other words, ->s_vfs_rename_mutex is sufficient to stabilize "X is an
ancestor of Y" only if X and Y are already in the same tree. Otherwise
it can go from false to true, and one can construct a deadlock on that.
Make lock_two_directories() report an error in such case and update the
callers of lock_rename()/lock_rename_child() to handle such errors.
And yes, such conditions are not impossible to create ;-/
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/smb/server')
-rw-r--r-- | fs/smb/server/vfs.c | 5 |
1 files changed, 5 insertions, 0 deletions
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index c53dea5598fc..4cf8523ad038 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -708,6 +708,10 @@ retry: goto out2; trap = lock_rename_child(old_child, new_path.dentry); + if (IS_ERR(trap)) { + err = PTR_ERR(trap); + goto out_drop_write; + } old_parent = dget(old_child->d_parent); if (d_unhashed(old_child)) { @@ -770,6 +774,7 @@ out4: out3: dput(old_parent); unlock_rename(old_parent, new_path.dentry); +out_drop_write: mnt_drop_write(old_path->mnt); out2: path_put(&new_path); |