Skip to content

Commit a96fbc7

Browse files
Liu BoJosef Bacik
authored andcommitted
Btrfs: allow file data clone within a file
We did not allow file data clone within the same file because of deadlock issues. However, we now use nested lock to avoid deadlock between the parent directory and the child file. So it's safe to do file clone within the same file when the two ranges are not overlapped. Reviewed-by: David Sterba <[email protected]> Signed-off-by: Liu Bo <[email protected]> Signed-off-by: Josef Bacik <[email protected]>
1 parent b7394eb commit a96fbc7

File tree

1 file changed

+19
-7
lines changed

1 file changed

+19
-7
lines changed

fs/btrfs/ioctl.c

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2486,6 +2486,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
24862486
int ret;
24872487
u64 len = olen;
24882488
u64 bs = root->fs_info->sb->s_blocksize;
2489+
int same_inode = 0;
24892490

24902491
/*
24912492
* TODO:
@@ -2522,7 +2523,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
25222523

25232524
ret = -EINVAL;
25242525
if (src == inode)
2525-
goto out_fput;
2526+
same_inode = 1;
25262527

25272528
/* the src must be open for reading */
25282529
if (!(src_file.file->f_mode & FMODE_READ))
@@ -2553,12 +2554,16 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
25532554
}
25542555
path->reada = 2;
25552556

2556-
if (inode < src) {
2557-
mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
2558-
mutex_lock_nested(&src->i_mutex, I_MUTEX_CHILD);
2557+
if (!same_inode) {
2558+
if (inode < src) {
2559+
mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
2560+
mutex_lock_nested(&src->i_mutex, I_MUTEX_CHILD);
2561+
} else {
2562+
mutex_lock_nested(&src->i_mutex, I_MUTEX_PARENT);
2563+
mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
2564+
}
25592565
} else {
2560-
mutex_lock_nested(&src->i_mutex, I_MUTEX_PARENT);
2561-
mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
2566+
mutex_lock(&src->i_mutex);
25622567
}
25632568

25642569
/* determine range to clone */
@@ -2576,6 +2581,12 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
25762581
!IS_ALIGNED(destoff, bs))
25772582
goto out_unlock;
25782583

2584+
/* verify if ranges are overlapped within the same file */
2585+
if (same_inode) {
2586+
if (destoff + len > off && destoff < off + len)
2587+
goto out_unlock;
2588+
}
2589+
25792590
if (destoff > inode->i_size) {
25802591
ret = btrfs_cont_expand(inode, inode->i_size, destoff);
25812592
if (ret)
@@ -2852,7 +2863,8 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
28522863
unlock_extent(&BTRFS_I(src)->io_tree, off, off + len - 1);
28532864
out_unlock:
28542865
mutex_unlock(&src->i_mutex);
2855-
mutex_unlock(&inode->i_mutex);
2866+
if (!same_inode)
2867+
mutex_unlock(&inode->i_mutex);
28562868
vfree(buf);
28572869
btrfs_free_path(path);
28582870
out_fput:

0 commit comments

Comments
 (0)