dcache: close d_move race in d_splice_alias
authorJ. Bruce Fields <bfields@redhat.com>
Mon, 17 Feb 2014 22:45:56 +0000 (17:45 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Thu, 7 Aug 2014 18:40:10 +0000 (14:40 -0400)
d_splice_alias will d_move an IS_ROOT() directory dentry into place if
one exists.  This should be safe as long as the dentry remains IS_ROOT,
but I can't see what guarantees that: once we drop the i_lock all we
hold here is the i_mutex on an unrelated parent directory.

Instead copy the logic of d_materialise_unique.

Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/dcache.c

index 8bdae36a095fde36f9dd0acb7fbd8ec2bc5fb6dc..8c09db9bb2a40bf66a0cecebe9eabb3f6599fc5b 100644 (file)
@@ -2676,9 +2676,14 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
                new = __d_find_alias(inode, 1);
                if (new) {
                        BUG_ON(!(new->d_flags & DCACHE_DISCONNECTED));
+                       write_seqlock(&rename_lock);
+                       __d_materialise_dentry(dentry, new);
+                       write_sequnlock(&rename_lock);
+                       __d_drop(new);
+                       _d_rehash(new);
+                       spin_unlock(&new->d_lock);
                        spin_unlock(&inode->i_lock);
                        security_d_instantiate(new, inode);
-                       d_move(new, dentry);
                        iput(inode);
                } else {
                        /* already taking inode->i_lock, so d_add() by hand */