diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
| -rw-r--r-- | fs/btrfs/ioctl.c | 36 | 
1 files changed, 31 insertions, 5 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 7cf013349941..538f65a79ec5 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2177,6 +2177,11 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  	if (!(src_file->f_mode & FMODE_READ))  		goto out_fput; +	/* don't make the dst file partly checksummed */ +	if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) != +	    (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) +		goto out_fput; +  	ret = -EISDIR;  	if (S_ISDIR(src->i_mode) || S_ISDIR(inode->i_mode))  		goto out_fput; @@ -2220,6 +2225,16 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  	    !IS_ALIGNED(destoff, bs))  		goto out_unlock; +	if (destoff > inode->i_size) { +		ret = btrfs_cont_expand(inode, inode->i_size, destoff); +		if (ret) +			goto out_unlock; +	} + +	/* truncate page cache pages from target inode range */ +	truncate_inode_pages_range(&inode->i_data, destoff, +				   PAGE_CACHE_ALIGN(destoff + len) - 1); +  	/* do any pending delalloc/csum calc on src, one way or  	   another, and lock file content */  	while (1) { @@ -2313,7 +2328,12 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  			else  				new_key.offset = destoff; -			trans = btrfs_start_transaction(root, 1); +			/* +			 * 1 - adjusting old extent (we may have to split it) +			 * 1 - add new extent +			 * 1 - inode update +			 */ +			trans = btrfs_start_transaction(root, 3);  			if (IS_ERR(trans)) {  				ret = PTR_ERR(trans);  				goto out; @@ -2321,14 +2341,21 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  			if (type == BTRFS_FILE_EXTENT_REG ||  			    type == BTRFS_FILE_EXTENT_PREALLOC) { +				/* +				 *    a  | --- range to clone ---|  b +				 * | ------------- extent ------------- | +				 */ + +				/* substract range b */ +				if (key.offset + datal > off + len) +					datal = off + len - key.offset; + +				/* substract range a */  				if (off > key.offset) {  					datao += off - key.offset;  					datal -= off - key.offset;  				} -				if (key.offset + datal > off + len) -					datal = off + len - key.offset; -  				ret = btrfs_drop_extents(trans, inode,  							 new_key.offset,  							 new_key.offset + datal, @@ -2425,7 +2452,6 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  			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);  			btrfs_end_transaction(trans, root);  | 
