return ret;
}
-static int extent_same_check_offsets(struct inode *inode, u64 off, u64 len)
+static int extent_same_check_offsets(struct inode *inode, u64 off, u64 len,
+ u64 len_aligned)
{
u64 bs = BTRFS_I(inode)->root->fs_info->sb->s_blocksize;
if (off + len > inode->i_size || off + len < off)
return -EINVAL;
+
/* Check that we are block aligned - btrfs_clone() requires this */
- if (!IS_ALIGNED(off, bs) || !IS_ALIGNED(off + len, bs))
+ if (!IS_ALIGNED(off, bs) || !IS_ALIGNED(off + len_aligned, bs))
return -EINVAL;
return 0;
struct inode *dst, u64 dst_loff)
{
int ret;
+ u64 len_aligned = len;
+ u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize;
/*
* btrfs_clone() can't handle extents in the same file
btrfs_double_lock(src, loff, dst, dst_loff, len);
- ret = extent_same_check_offsets(src, loff, len);
+ /* if we extend to both eofs, continue to block boundaries */
+ if (loff + len == src->i_size && dst_loff + len == dst->i_size)
+ len_aligned = ALIGN(src->i_size, bs) - loff;
+
+ ret = extent_same_check_offsets(src, loff, len, len_aligned);
if (ret)
goto out_unlock;
- ret = extent_same_check_offsets(dst, dst_loff, len);
+ ret = extent_same_check_offsets(dst, dst_loff, len, len_aligned);
if (ret)
goto out_unlock;
ret = btrfs_cmp_data(src, loff, dst, dst_loff, len);
if (ret == 0)
- ret = btrfs_clone(src, dst, loff, len, len, dst_loff);
+ ret = btrfs_clone(src, dst, loff, len, len_aligned, dst_loff);
out_unlock:
btrfs_double_unlock(src, loff, dst, dst_loff, len);
* @inode: Inode to clone to
* @off: Offset within source to start clone from
* @olen: Original length, passed by user, of range to clone
- * @olen_aligned: Block-aligned value of olen, extent_same uses
- * identical values here
+ * @olen_aligned: Block-aligned value of olen
* @destoff: Offset within @inode to start clone
*/
static int btrfs_clone(struct inode *src, struct inode *inode,