diff options
| author | Ingo Molnar <mingo@kernel.org> | 2018-12-03 13:42:17 +0300 | 
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2018-12-03 13:42:17 +0300 | 
| commit | 5f675231e456cb599b283f8361f01cf34b0617df (patch) | |
| tree | afb2bdfd6fdbb18336146f41ba7659120a5ff9d2 /fs/xfs/xfs_reflink.c | |
| parent | 3e184501083c38fa091f640acb13af17a21fd228 (diff) | |
| parent | 2595646791c319cadfdbf271563aac97d0843dc7 (diff) | |
| download | linux-5f675231e456cb599b283f8361f01cf34b0617df.tar.xz | |
Merge tag 'v4.20-rc5' into sched/core, to pick up fixes
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'fs/xfs/xfs_reflink.c')
| -rw-r--r-- | fs/xfs/xfs_reflink.c | 181 | 
1 files changed, 45 insertions, 136 deletions
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 8eaeec9d58ed..322a852ce284 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -296,6 +296,7 @@ xfs_reflink_reserve_cow(  	if (error)  		return error; +	xfs_trim_extent(imap, got.br_startoff, got.br_blockcount);  	trace_xfs_reflink_cow_alloc(ip, &got);  	return 0;  } @@ -913,18 +914,18 @@ out_error:  /*   * Update destination inode size & cowextsize hint, if necessary.   */ -STATIC int +int  xfs_reflink_update_dest(  	struct xfs_inode	*dest,  	xfs_off_t		newlen,  	xfs_extlen_t		cowextsize, -	bool			is_dedupe) +	unsigned int		remap_flags)  {  	struct xfs_mount	*mp = dest->i_mount;  	struct xfs_trans	*tp;  	int			error; -	if (is_dedupe && newlen <= i_size_read(VFS_I(dest)) && cowextsize == 0) +	if (newlen <= i_size_read(VFS_I(dest)) && cowextsize == 0)  		return 0;  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp); @@ -945,10 +946,6 @@ xfs_reflink_update_dest(  		dest->i_d.di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;  	} -	if (!is_dedupe) { -		xfs_trans_ichgtime(tp, dest, -				   XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); -	}  	xfs_trans_log_inode(tp, dest, XFS_ILOG_CORE);  	error = xfs_trans_commit(tp); @@ -1112,19 +1109,28 @@ out:  /*   * Iteratively remap one file's extents (and holes) to another's.   */ -STATIC int +int  xfs_reflink_remap_blocks(  	struct xfs_inode	*src, -	xfs_fileoff_t		srcoff, +	loff_t			pos_in,  	struct xfs_inode	*dest, -	xfs_fileoff_t		destoff, -	xfs_filblks_t		len, -	xfs_off_t		new_isize) +	loff_t			pos_out, +	loff_t			remap_len, +	loff_t			*remapped)  {  	struct xfs_bmbt_irec	imap; +	xfs_fileoff_t		srcoff; +	xfs_fileoff_t		destoff; +	xfs_filblks_t		len; +	xfs_filblks_t		range_len; +	xfs_filblks_t		remapped_len = 0; +	xfs_off_t		new_isize = pos_out + remap_len;  	int			nimaps;  	int			error = 0; -	xfs_filblks_t		range_len; + +	destoff = XFS_B_TO_FSBT(src->i_mount, pos_out); +	srcoff = XFS_B_TO_FSBT(src->i_mount, pos_in); +	len = XFS_B_TO_FSB(src->i_mount, remap_len);  	/* drange = (destoff, destoff + len); srange = (srcoff, srcoff + len) */  	while (len) { @@ -1139,7 +1145,7 @@ xfs_reflink_remap_blocks(  		error = xfs_bmapi_read(src, srcoff, len, &imap, &nimaps, 0);  		xfs_iunlock(src, lock_mode);  		if (error) -			goto err; +			break;  		ASSERT(nimaps == 1);  		trace_xfs_reflink_remap_imap(src, srcoff, len, XFS_IO_OVERWRITE, @@ -1153,23 +1159,24 @@ xfs_reflink_remap_blocks(  		error = xfs_reflink_remap_extent(dest, &imap, destoff,  				new_isize);  		if (error) -			goto err; +			break;  		if (fatal_signal_pending(current)) {  			error = -EINTR; -			goto err; +			break;  		}  		/* Advance drange/srange */  		srcoff += range_len;  		destoff += range_len;  		len -= range_len; +		remapped_len += range_len;  	} -	return 0; - -err: -	trace_xfs_reflink_remap_blocks_error(dest, error, _RET_IP_); +	if (error) +		trace_xfs_reflink_remap_blocks_error(dest, error, _RET_IP_); +	*remapped = min_t(loff_t, remap_len, +			  XFS_FSB_TO_B(src->i_mount, remapped_len));  	return error;  } @@ -1218,7 +1225,7 @@ retry:  }  /* Unlock both inodes after they've been prepped for a range clone. */ -STATIC void +void  xfs_reflink_remap_unlock(  	struct file		*file_in,  	struct file		*file_out) @@ -1286,21 +1293,20 @@ xfs_reflink_zero_posteof(   * stale data in the destination file. Hence we reject these clone attempts with   * -EINVAL in this case.   */ -STATIC int +int  xfs_reflink_remap_prep(  	struct file		*file_in,  	loff_t			pos_in,  	struct file		*file_out,  	loff_t			pos_out, -	u64			*len, -	bool			is_dedupe) +	loff_t			*len, +	unsigned int		remap_flags)  {  	struct inode		*inode_in = file_inode(file_in);  	struct xfs_inode	*src = XFS_I(inode_in);  	struct inode		*inode_out = file_inode(file_out);  	struct xfs_inode	*dest = XFS_I(inode_out);  	bool			same_inode = (inode_in == inode_out); -	u64			blkmask = i_blocksize(inode_in) - 1;  	ssize_t			ret;  	/* Lock both files against IO */ @@ -1323,29 +1329,11 @@ xfs_reflink_remap_prep(  	if (IS_DAX(inode_in) || IS_DAX(inode_out))  		goto out_unlock; -	ret = vfs_clone_file_prep_inodes(inode_in, pos_in, inode_out, pos_out, -			len, is_dedupe); -	if (ret <= 0) +	ret = generic_remap_file_range_prep(file_in, pos_in, file_out, pos_out, +			len, remap_flags); +	if (ret < 0 || *len == 0)  		goto out_unlock; -	/* -	 * If the dedupe data matches, chop off the partial EOF block -	 * from the source file so we don't try to dedupe the partial -	 * EOF block. -	 */ -	if (is_dedupe) { -		*len &= ~blkmask; -	} else if (*len & blkmask) { -		/* -		 * The user is attempting to share a partial EOF block, -		 * if it's inside the destination EOF then reject it. -		 */ -		if (pos_out + *len < i_size_read(inode_out)) { -			ret = -EINVAL; -			goto out_unlock; -		} -	} -  	/* Attach dquots to dest inode before changing block map */  	ret = xfs_qm_dqattach(dest);  	if (ret) @@ -1364,102 +1352,23 @@ xfs_reflink_remap_prep(  	if (ret)  		goto out_unlock; -	/* Zap any page cache for the destination file's range. */ -	truncate_inode_pages_range(&inode_out->i_data, pos_out, -				   PAGE_ALIGN(pos_out + *len) - 1); - -	/* If we're altering the file contents... */ -	if (!is_dedupe) { -		/* -		 * ...update the timestamps (which will grab the ilock again -		 * from xfs_fs_dirty_inode, so we have to call it before we -		 * take the ilock). -		 */ -		if (!(file_out->f_mode & FMODE_NOCMTIME)) { -			ret = file_update_time(file_out); -			if (ret) -				goto out_unlock; -		} - -		/* -		 * ...clear the security bits if the process is not being run -		 * by root.  This keeps people from modifying setuid and setgid -		 * binaries. -		 */ -		ret = file_remove_privs(file_out); -		if (ret) -			goto out_unlock; +	/* +	 * If pos_out > EOF, we may have dirtied blocks between EOF and +	 * pos_out. In that case, we need to extend the flush and unmap to cover +	 * from EOF to the end of the copy length. +	 */ +	if (pos_out > XFS_ISIZE(dest)) { +		loff_t	flen = *len + (pos_out - XFS_ISIZE(dest)); +		ret = xfs_flush_unmap_range(dest, XFS_ISIZE(dest), flen); +	} else { +		ret = xfs_flush_unmap_range(dest, pos_out, *len);  	} - -	return 1; -out_unlock: -	xfs_reflink_remap_unlock(file_in, file_out); -	return ret; -} - -/* - * Link a range of blocks from one file to another. - */ -int -xfs_reflink_remap_range( -	struct file		*file_in, -	loff_t			pos_in, -	struct file		*file_out, -	loff_t			pos_out, -	u64			len, -	bool			is_dedupe) -{ -	struct inode		*inode_in = file_inode(file_in); -	struct xfs_inode	*src = XFS_I(inode_in); -	struct inode		*inode_out = file_inode(file_out); -	struct xfs_inode	*dest = XFS_I(inode_out); -	struct xfs_mount	*mp = src->i_mount; -	xfs_fileoff_t		sfsbno, dfsbno; -	xfs_filblks_t		fsblen; -	xfs_extlen_t		cowextsize; -	ssize_t			ret; - -	if (!xfs_sb_version_hasreflink(&mp->m_sb)) -		return -EOPNOTSUPP; - -	if (XFS_FORCED_SHUTDOWN(mp)) -		return -EIO; - -	/* Prepare and then clone file data. */ -	ret = xfs_reflink_remap_prep(file_in, pos_in, file_out, pos_out, -			&len, is_dedupe); -	if (ret <= 0) -		return ret; - -	trace_xfs_reflink_remap_range(src, pos_in, len, dest, pos_out); - -	dfsbno = XFS_B_TO_FSBT(mp, pos_out); -	sfsbno = XFS_B_TO_FSBT(mp, pos_in); -	fsblen = XFS_B_TO_FSB(mp, len); -	ret = xfs_reflink_remap_blocks(src, sfsbno, dest, dfsbno, fsblen, -			pos_out + len);  	if (ret)  		goto out_unlock; -	/* -	 * Carry the cowextsize hint from src to dest if we're sharing the -	 * entire source file to the entire destination file, the source file -	 * has a cowextsize hint, and the destination file does not. -	 */ -	cowextsize = 0; -	if (pos_in == 0 && len == i_size_read(inode_in) && -	    (src->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE) && -	    pos_out == 0 && len >= i_size_read(inode_out) && -	    !(dest->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE)) -		cowextsize = src->i_d.di_cowextsize; - -	ret = xfs_reflink_update_dest(dest, pos_out + len, cowextsize, -			is_dedupe); - +	return 1;  out_unlock:  	xfs_reflink_remap_unlock(file_in, file_out); -	if (ret) -		trace_xfs_reflink_remap_range_error(dest, ret, _RET_IP_);  	return ret;  }  | 
