|
@@ -2486,6 +2486,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
|
int ret;
|
|
int ret;
|
|
u64 len = olen;
|
|
u64 len = olen;
|
|
u64 bs = root->fs_info->sb->s_blocksize;
|
|
u64 bs = root->fs_info->sb->s_blocksize;
|
|
|
|
+ int same_inode = 0;
|
|
|
|
|
|
/*
|
|
/*
|
|
* TODO:
|
|
* TODO:
|
|
@@ -2522,7 +2523,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
|
|
|
|
|
ret = -EINVAL;
|
|
ret = -EINVAL;
|
|
if (src == inode)
|
|
if (src == inode)
|
|
- goto out_fput;
|
|
|
|
|
|
+ same_inode = 1;
|
|
|
|
|
|
/* the src must be open for reading */
|
|
/* the src must be open for reading */
|
|
if (!(src_file.file->f_mode & FMODE_READ))
|
|
if (!(src_file.file->f_mode & FMODE_READ))
|
|
@@ -2553,12 +2554,16 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
|
}
|
|
}
|
|
path->reada = 2;
|
|
path->reada = 2;
|
|
|
|
|
|
- if (inode < src) {
|
|
|
|
- mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
|
|
|
|
- mutex_lock_nested(&src->i_mutex, I_MUTEX_CHILD);
|
|
|
|
|
|
+ if (!same_inode) {
|
|
|
|
+ if (inode < src) {
|
|
|
|
+ mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
|
|
|
|
+ mutex_lock_nested(&src->i_mutex, I_MUTEX_CHILD);
|
|
|
|
+ } else {
|
|
|
|
+ mutex_lock_nested(&src->i_mutex, I_MUTEX_PARENT);
|
|
|
|
+ mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
|
|
|
|
+ }
|
|
} else {
|
|
} else {
|
|
- mutex_lock_nested(&src->i_mutex, I_MUTEX_PARENT);
|
|
|
|
- mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
|
|
|
|
|
|
+ mutex_lock(&src->i_mutex);
|
|
}
|
|
}
|
|
|
|
|
|
/* determine range to clone */
|
|
/* determine range to clone */
|
|
@@ -2576,6 +2581,12 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
|
!IS_ALIGNED(destoff, bs))
|
|
!IS_ALIGNED(destoff, bs))
|
|
goto out_unlock;
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
+ /* verify if ranges are overlapped within the same file */
|
|
|
|
+ if (same_inode) {
|
|
|
|
+ if (destoff + len > off && destoff < off + len)
|
|
|
|
+ goto out_unlock;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (destoff > inode->i_size) {
|
|
if (destoff > inode->i_size) {
|
|
ret = btrfs_cont_expand(inode, inode->i_size, destoff);
|
|
ret = btrfs_cont_expand(inode, inode->i_size, destoff);
|
|
if (ret)
|
|
if (ret)
|
|
@@ -2852,7 +2863,8 @@ out:
|
|
unlock_extent(&BTRFS_I(src)->io_tree, off, off + len - 1);
|
|
unlock_extent(&BTRFS_I(src)->io_tree, off, off + len - 1);
|
|
out_unlock:
|
|
out_unlock:
|
|
mutex_unlock(&src->i_mutex);
|
|
mutex_unlock(&src->i_mutex);
|
|
- mutex_unlock(&inode->i_mutex);
|
|
|
|
|
|
+ if (!same_inode)
|
|
|
|
+ mutex_unlock(&inode->i_mutex);
|
|
vfree(buf);
|
|
vfree(buf);
|
|
btrfs_free_path(path);
|
|
btrfs_free_path(path);
|
|
out_fput:
|
|
out_fput:
|