|
@@ -9406,6 +9406,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
|
u64 root_objectid;
|
|
|
int ret;
|
|
|
u64 old_ino = btrfs_ino(old_inode);
|
|
|
+ bool log_pinned = false;
|
|
|
|
|
|
if (btrfs_ino(new_dir) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)
|
|
|
return -EPERM;
|
|
@@ -9493,6 +9494,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
|
* we unlink the name but before we add the new name back in.
|
|
|
*/
|
|
|
btrfs_pin_log_trans(root);
|
|
|
+ log_pinned = true;
|
|
|
}
|
|
|
|
|
|
inode_inc_iversion(old_dir);
|
|
@@ -9559,12 +9561,36 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
|
if (old_inode->i_nlink == 1)
|
|
|
BTRFS_I(old_inode)->dir_index = index;
|
|
|
|
|
|
- if (old_ino != BTRFS_FIRST_FREE_OBJECTID) {
|
|
|
+ if (log_pinned) {
|
|
|
struct dentry *parent = new_dentry->d_parent;
|
|
|
+
|
|
|
btrfs_log_new_name(trans, old_inode, old_dir, parent);
|
|
|
btrfs_end_log_trans(root);
|
|
|
+ log_pinned = false;
|
|
|
}
|
|
|
out_fail:
|
|
|
+ /*
|
|
|
+ * If we have pinned the log and an error happened, we unpin tasks
|
|
|
+ * trying to sync the log and force them to fallback to a transaction
|
|
|
+ * commit if the log currently contains any of the inodes involved in
|
|
|
+ * this rename operation (to ensure we do not persist a log with an
|
|
|
+ * inconsistent state for any of these inodes or leading to any
|
|
|
+ * inconsistencies when replayed). If the transaction was aborted, the
|
|
|
+ * abortion reason is propagated to userspace when attempting to commit
|
|
|
+ * the transaction. If the log does not contain any of these inodes, we
|
|
|
+ * allow the tasks to sync it.
|
|
|
+ */
|
|
|
+ if (ret && log_pinned) {
|
|
|
+ if (btrfs_inode_in_log(old_dir, root->fs_info->generation) ||
|
|
|
+ btrfs_inode_in_log(new_dir, root->fs_info->generation) ||
|
|
|
+ btrfs_inode_in_log(old_inode, root->fs_info->generation) ||
|
|
|
+ (new_inode &&
|
|
|
+ btrfs_inode_in_log(new_inode, root->fs_info->generation)))
|
|
|
+ btrfs_set_log_full_commit(root->fs_info, trans);
|
|
|
+
|
|
|
+ btrfs_end_log_trans(root);
|
|
|
+ log_pinned = false;
|
|
|
+ }
|
|
|
btrfs_end_transaction(trans, root);
|
|
|
out_notrans:
|
|
|
if (old_ino == BTRFS_FIRST_FREE_OBJECTID)
|