summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@suse.com>2016-05-05 03:08:56 +0200
committerFilipe Manana <fdmanana@suse.com>2016-05-13 02:59:28 +0200
commit376e5a57bf7f1466031a957d04bf8b8f6801ee6d (patch)
treed51e2907047414f8b9798a0122e1a344b024781d /fs
parentBtrfs: unpin logs if rename exchange operation fails (diff)
downloadlinux-376e5a57bf7f1466031a957d04bf8b8f6801ee6d.tar.xz
linux-376e5a57bf7f1466031a957d04bf8b8f6801ee6d.zip
Btrfs: pin logs earlier when doing a rename exchange operation
The btrfs_rename_exchange() started as a copy-paste from btrfs_rename(), which had a race fixed by my previous patch titled "Btrfs: pin log earlier when renaming", and so it suffers from the same problem. We pin the logs of the affected roots after we insert the new inode references, leaving a time window where concurrent tasks logging the inodes can end up logging both the new and old references, resulting in log trees that when replayed can turn the metadata into inconsistent states. This behaviour was added to btrfs_rename() in 2009 without any explanation about why not pinning the logs earlier, just leaving a comment about the posibility for the race. As of today it's perfectly safe and sane to pin the logs before we start doing any of the steps involved in the rename operation. Signed-off-by: Filipe Manana <fdmanana@suse.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/inode.c8
1 files changed, 4 insertions, 4 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index c92d9b83bb38..a7db3ed6ac88 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -9458,6 +9458,8 @@ static int btrfs_rename_exchange(struct inode *old_dir,
/* force full log commit if subvolume involved. */
btrfs_set_log_full_commit(root->fs_info, trans);
} else {
+ btrfs_pin_log_trans(root);
+ root_log_pinned = true;
ret = btrfs_insert_inode_ref(trans, dest,
new_dentry->d_name.name,
new_dentry->d_name.len,
@@ -9465,8 +9467,6 @@ static int btrfs_rename_exchange(struct inode *old_dir,
btrfs_ino(new_dir), old_idx);
if (ret)
goto out_fail;
- btrfs_pin_log_trans(root);
- root_log_pinned = true;
}
/* And now for the dest. */
@@ -9474,6 +9474,8 @@ static int btrfs_rename_exchange(struct inode *old_dir,
/* force full log commit if subvolume involved. */
btrfs_set_log_full_commit(dest->fs_info, trans);
} else {
+ btrfs_pin_log_trans(dest);
+ dest_log_pinned = true;
ret = btrfs_insert_inode_ref(trans, root,
old_dentry->d_name.name,
old_dentry->d_name.len,
@@ -9481,8 +9483,6 @@ static int btrfs_rename_exchange(struct inode *old_dir,
btrfs_ino(old_dir), new_idx);
if (ret)
goto out_fail;
- btrfs_pin_log_trans(dest);
- dest_log_pinned = true;
}
/* Update inode version and ctime/mtime. */