diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
| -rw-r--r-- | fs/btrfs/ioctl.c | 20 | 
1 files changed, 15 insertions, 5 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 4dbaf89b133..9254b3d58db 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1458,7 +1458,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  	 */  	/* the destination must be opened for writing */ -	if (!(file->f_mode & FMODE_WRITE)) +	if (!(file->f_mode & FMODE_WRITE) || (file->f_flags & O_APPEND))  		return -EINVAL;  	ret = mnt_want_write(file->f_path.mnt); @@ -1511,7 +1511,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  	/* determine range to clone */  	ret = -EINVAL; -	if (off >= src->i_size || off + len > src->i_size) +	if (off + len > src->i_size || off + len < off)  		goto out_unlock;  	if (len == 0)  		olen = len = src->i_size - off; @@ -1578,6 +1578,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  			u64 disko = 0, diskl = 0;  			u64 datao = 0, datal = 0;  			u8 comp; +			u64 endoff;  			size = btrfs_item_size_nr(leaf, slot);  			read_extent_buffer(leaf, buf, @@ -1712,9 +1713,18 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  			btrfs_release_path(root, path);  			inode->i_mtime = inode->i_ctime = CURRENT_TIME; -			if (new_key.offset + datal > inode->i_size) -				btrfs_i_size_write(inode, -						   new_key.offset + datal); + +			/* +			 * we round up to the block size at eof when +			 * determining which extents to clone above, +			 * but shouldn't round up the file size +			 */ +			endoff = new_key.offset + datal; +			if (endoff > off+olen) +				endoff = off+olen; +			if (endoff > inode->i_size) +				btrfs_i_size_write(inode, endoff); +  			BTRFS_I(inode)->flags = BTRFS_I(src)->flags;  			ret = btrfs_update_inode(trans, root, inode);  			BUG_ON(ret);  |