Unverified Commit edf8bf21 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!11339 Fix xfs fsmap error

Merge Pull Request from: @ci-robot 
 
PR sync from: Zizhi Wo <wozizhi@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/V4WENREKXYPYBHFLJQM7EYP5GBBGJ76G/ 
xfs fsmap bugfix.

Darrick J. Wong (2):
  xfs: fix interval filtering in multi-step fsmap queries
  xfs: fix an agbno overflow in __xfs_getfsmap_datadev


-- 
2.39.2
 
https://gitee.com/openeuler/kernel/issues/IA470G 
 
Link:https://gitee.com/openeuler/kernel/pulls/11339

 

Reviewed-by: default avatarzhangyi (F) <yi.zhang@huawei.com>
Reviewed-by: default avatarLi Nan <linan122@huawei.com>
Signed-off-by: default avatarLi Nan <linan122@huawei.com>
parents cfc1a823 c387d360
Loading
Loading
Loading
Loading
+60 −20
Original line number Diff line number Diff line
@@ -162,7 +162,14 @@ struct xfs_getfsmap_info {
	u64			missing_owner;	/* owner of holes */
	u32			dev;		/* device id */
	xfs_agnumber_t		agno;		/* AG number, if applicable */
	struct xfs_rmap_irec	low;		/* low rmap key */
	/*
	 * Low rmap key for the query.  If low.rm_blockcount is nonzero, this
	 * is the second (or later) call to retrieve the recordset in pieces.
	 * xfs_getfsmap_rec_before_start will compare all records retrieved
	 * by the rmapbt query to filter out any records that start before
	 * the last record.
	 */
	struct xfs_rmap_irec	low;
	struct xfs_rmap_irec	high;		/* high rmap key */
	bool			last;		/* last extent? */
};
@@ -239,6 +246,17 @@ xfs_getfsmap_format(
	xfs_fsmap_from_internal(rec, xfm);
}

static inline bool
xfs_getfsmap_rec_before_start(
	struct xfs_getfsmap_info	*info,
	const struct xfs_rmap_irec	*rec,
	xfs_daddr_t			rec_daddr)
{
	if (info->low.rm_blockcount)
		return xfs_rmap_compare(rec, &info->low) < 0;
	return false;
}

/*
 * Format a reverse mapping for getfsmap, having translated rm_startblock
 * into the appropriate daddr units.
@@ -262,7 +280,7 @@ xfs_getfsmap_helper(
	 * Filter out records that start before our startpoint, if the
	 * caller requested that.
	 */
	if (xfs_rmap_compare(rec, &info->low) < 0) {
	if (xfs_getfsmap_rec_before_start(info, rec, rec_daddr)) {
		rec_daddr += XFS_FSB_TO_BB(mp, rec->rm_blockcount);
		if (info->next_daddr < rec_daddr)
			info->next_daddr = rec_daddr;
@@ -581,6 +599,19 @@ xfs_getfsmap_rtdev_rtbitmap(
}
#endif /* CONFIG_XFS_RT */

static inline bool
rmap_not_shareable(struct xfs_mount *mp, const struct xfs_rmap_irec *r)
{
	if (!xfs_has_reflink(mp))
		return true;
	if (XFS_RMAP_NON_INODE_OWNER(r->rm_owner))
		return true;
	if (r->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK |
			   XFS_RMAP_UNWRITTEN))
		return true;
	return false;
}

/* Execute a getfsmap query against the regular data device. */
STATIC int
__xfs_getfsmap_datadev(
@@ -613,14 +644,30 @@ __xfs_getfsmap_datadev(
	 * low to the fsmap low key and max out the high key to the end
	 * of the AG.
	 */
	info->low.rm_startblock = XFS_FSB_TO_AGBNO(mp, start_fsb);
	info->low.rm_offset = XFS_BB_TO_FSBT(mp, keys[0].fmr_offset);
	error = xfs_fsmap_owner_to_rmap(&info->low, &keys[0]);
	if (error)
		return error;
	info->low.rm_blockcount = 0;
	info->low.rm_blockcount = XFS_BB_TO_FSBT(mp, keys[0].fmr_length);
	xfs_getfsmap_set_irec_flags(&info->low, &keys[0]);

	/* Adjust the low key if we are continuing from where we left off. */
	if (info->low.rm_blockcount == 0) {
		/* No previous record from which to continue */
	} else if (rmap_not_shareable(mp, &info->low)) {
		/* Last record seen was an unshareable extent */
		info->low.rm_owner = 0;
		info->low.rm_offset = 0;

		start_fsb += info->low.rm_blockcount;
		if (XFS_FSB_TO_DADDR(mp, start_fsb) >= eofs)
			return 0;
	} else {
		/* Last record seen was a shareable file data extent */
		info->low.rm_offset += info->low.rm_blockcount;
	}
	info->low.rm_startblock = XFS_FSB_TO_AGBNO(mp, start_fsb);

	info->high.rm_startblock = -1U;
	info->high.rm_owner = ULLONG_MAX;
	info->high.rm_offset = ULLONG_MAX;
@@ -671,12 +718,8 @@ __xfs_getfsmap_datadev(
		 * Set the AG low key to the start of the AG prior to
		 * moving on to the next AG.
		 */
		if (info->agno == start_ag) {
			info->low.rm_startblock = 0;
			info->low.rm_owner = 0;
			info->low.rm_offset = 0;
			info->low.rm_flags = 0;
		}
		if (info->agno == start_ag)
			memset(&info->low, 0, sizeof(info->low));
	}

	/* Report any gap at the end of the AG */
@@ -904,21 +947,17 @@ xfs_getfsmap(
	 * blocks could be mapped to several other files/offsets.
	 * According to rmapbt record ordering, the minimal next
	 * possible record for the block range is the next starting
	 * offset in the same inode. Therefore, bump the file offset to
	 * continue the search appropriately.  For all other low key
	 * mapping types (attr blocks, metadata), bump the physical
	 * offset as there can be no other mapping for the same physical
	 * block range.
	 * offset in the same inode. Therefore, each fsmap backend bumps
	 * the file offset to continue the search appropriately.  For
	 * all other low key mapping types (attr blocks, metadata), each
	 * fsmap backend bumps the physical offset as there can be no
	 * other mapping for the same physical block range.
	 */
	dkeys[0] = head->fmh_keys[0];
	if (dkeys[0].fmr_flags & (FMR_OF_SPECIAL_OWNER | FMR_OF_EXTENT_MAP)) {
		dkeys[0].fmr_physical += dkeys[0].fmr_length;
		dkeys[0].fmr_owner = 0;
		if (dkeys[0].fmr_offset)
			return -EINVAL;
	} else
		dkeys[0].fmr_offset += dkeys[0].fmr_length;
	dkeys[0].fmr_length = 0;
	}
	memset(&dkeys[1], 0xFF, sizeof(struct xfs_fsmap));

	if (!xfs_getfsmap_check_keys(dkeys, &head->fmh_keys[1]))
@@ -970,6 +1009,7 @@ xfs_getfsmap(
		info.dev = handlers[i].dev;
		info.last = false;
		info.agno = NULLAGNUMBER;
		info.low.rm_blockcount = 0;
		error = handlers[i].fn(tp, dkeys, &info);
		if (error)
			break;