diff options
Diffstat (limited to 'fs/xfs/xfs_reflink.c')
| -rw-r--r-- | fs/xfs/xfs_reflink.c | 39 | 
1 files changed, 20 insertions, 19 deletions
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 4a84c5ea266d..ffe6fe7a7eb5 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -206,11 +206,7 @@ xfs_reflink_trim_around_shared(  	int			error = 0;  	/* Holes, unwritten, and delalloc extents cannot be shared */ -	if (!xfs_is_reflink_inode(ip) || -	    ISUNWRITTEN(irec) || -	    irec->br_startblock == HOLESTARTBLOCK || -	    irec->br_startblock == DELAYSTARTBLOCK || -	    isnullstartblock(irec->br_startblock)) { +	if (!xfs_is_reflink_inode(ip) || !xfs_bmap_is_real_extent(irec)) {  		*shared = false;  		return 0;  	} @@ -709,8 +705,22 @@ xfs_reflink_end_cow(  	offset_fsb = XFS_B_TO_FSBT(ip->i_mount, offset);  	end_fsb = XFS_B_TO_FSB(ip->i_mount, offset + count); -	/* Start a rolling transaction to switch the mappings */ -	resblks = XFS_EXTENTADD_SPACE_RES(ip->i_mount, XFS_DATA_FORK); +	/* +	 * Start a rolling transaction to switch the mappings.  We're +	 * unlikely ever to have to remap 16T worth of single-block +	 * extents, so just cap the worst case extent count to 2^32-1. +	 * Stick a warning in just in case, and avoid 64-bit division. +	 */ +	BUILD_BUG_ON(MAX_RW_COUNT > UINT_MAX); +	if (end_fsb - offset_fsb > UINT_MAX) { +		error = -EFSCORRUPTED; +		xfs_force_shutdown(ip->i_mount, SHUTDOWN_CORRUPT_INCORE); +		ASSERT(0); +		goto out; +	} +	resblks = XFS_NEXTENTADD_SPACE_RES(ip->i_mount, +			(unsigned int)(end_fsb - offset_fsb), +			XFS_DATA_FORK);  	error = xfs_trans_alloc(ip->i_mount, &M_RES(ip->i_mount)->tr_write,  			resblks, 0, 0, &tp);  	if (error) @@ -1045,12 +1055,12 @@ xfs_reflink_remap_extent(  	xfs_off_t		new_isize)  {  	struct xfs_mount	*mp = ip->i_mount; +	bool			real_extent = xfs_bmap_is_real_extent(irec);  	struct xfs_trans	*tp;  	xfs_fsblock_t		firstfsb;  	unsigned int		resblks;  	struct xfs_defer_ops	dfops;  	struct xfs_bmbt_irec	uirec; -	bool			real_extent;  	xfs_filblks_t		rlen;  	xfs_filblks_t		unmap_len;  	xfs_off_t		newlen; @@ -1059,11 +1069,6 @@ xfs_reflink_remap_extent(  	unmap_len = irec->br_startoff + irec->br_blockcount - destoff;  	trace_xfs_reflink_punch_range(ip, destoff, unmap_len); -	/* Only remap normal extents. */ -	real_extent =  (irec->br_startblock != HOLESTARTBLOCK && -			irec->br_startblock != DELAYSTARTBLOCK && -			!ISUNWRITTEN(irec)); -  	/* No reflinking if we're low on space */  	if (real_extent) {  		error = xfs_reflink_ag_has_free_space(mp, @@ -1359,9 +1364,7 @@ xfs_reflink_dirty_extents(  			goto out;  		if (nmaps == 0)  			break; -		if (map[0].br_startblock == HOLESTARTBLOCK || -		    map[0].br_startblock == DELAYSTARTBLOCK || -		    ISUNWRITTEN(&map[0])) +		if (!xfs_bmap_is_real_extent(&map[0]))  			goto next;  		map[1] = map[0]; @@ -1435,9 +1438,7 @@ xfs_reflink_clear_inode_flag(  			return error;  		if (nmaps == 0)  			break; -		if (map.br_startblock == HOLESTARTBLOCK || -		    map.br_startblock == DELAYSTARTBLOCK || -		    ISUNWRITTEN(&map)) +		if (!xfs_bmap_is_real_extent(&map))  			goto next;  		agno = XFS_FSB_TO_AGNO(mp, map.br_startblock);  | 
