xfs: check record domain when accessing refcount records
Now that we've separated the startblock and CoW/shared extent domain in the incore refcount record structure, check the domain whenever we retrieve a record to ensure that it's still in the domain that we want. Depending on the circumstances, a change in domain either means we're done processing or that we've found a corruption and need to fail out. The refcount check in xchk_xref_is_cow_staging is redundant since _get_rec has done that for a long time now, so we can get rid of it. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Dave Chinner <dchinner@redhat.com>
This commit is contained in:
parent
68d0f38917
commit
f62ac3e0ac
@ -381,6 +381,8 @@ xfs_refcount_split_extent(
|
||||
error = -EFSCORRUPTED;
|
||||
goto out_error;
|
||||
}
|
||||
if (rcext.rc_domain != domain)
|
||||
return 0;
|
||||
if (rcext.rc_startblock == agbno || xfs_refc_next(&rcext) <= agbno)
|
||||
return 0;
|
||||
|
||||
@ -432,6 +434,9 @@ xfs_refcount_merge_center_extents(
|
||||
trace_xfs_refcount_merge_center_extents(cur->bc_mp,
|
||||
cur->bc_ag.pag->pag_agno, left, center, right);
|
||||
|
||||
ASSERT(left->rc_domain == center->rc_domain);
|
||||
ASSERT(right->rc_domain == center->rc_domain);
|
||||
|
||||
/*
|
||||
* Make sure the center and right extents are not in the btree.
|
||||
* If the center extent was synthesized, the first delete call
|
||||
@ -508,6 +513,8 @@ xfs_refcount_merge_left_extent(
|
||||
trace_xfs_refcount_merge_left_extent(cur->bc_mp,
|
||||
cur->bc_ag.pag->pag_agno, left, cleft);
|
||||
|
||||
ASSERT(left->rc_domain == cleft->rc_domain);
|
||||
|
||||
/* If the extent at agbno (cleft) wasn't synthesized, remove it. */
|
||||
if (cleft->rc_refcount > 1) {
|
||||
error = xfs_refcount_lookup_le(cur, cleft->rc_domain,
|
||||
@ -569,6 +576,8 @@ xfs_refcount_merge_right_extent(
|
||||
trace_xfs_refcount_merge_right_extent(cur->bc_mp,
|
||||
cur->bc_ag.pag->pag_agno, cright, right);
|
||||
|
||||
ASSERT(right->rc_domain == cright->rc_domain);
|
||||
|
||||
/*
|
||||
* If the extent ending at agbno+aglen (cright) wasn't synthesized,
|
||||
* remove it.
|
||||
@ -649,12 +658,10 @@ xfs_refcount_find_left_extents(
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
if (tmp.rc_domain != domain)
|
||||
return 0;
|
||||
if (xfs_refc_next(&tmp) != agbno)
|
||||
return 0;
|
||||
if (domain == XFS_REFC_DOMAIN_SHARED && tmp.rc_refcount < 2)
|
||||
return 0;
|
||||
if (domain == XFS_REFC_DOMAIN_COW && tmp.rc_refcount > 1)
|
||||
return 0;
|
||||
/* We have a left extent; retrieve (or invent) the next right one */
|
||||
*left = tmp;
|
||||
|
||||
@ -670,6 +677,9 @@ xfs_refcount_find_left_extents(
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
if (tmp.rc_domain != domain)
|
||||
goto not_found;
|
||||
|
||||
/* if tmp starts at the end of our range, just use that */
|
||||
if (tmp.rc_startblock == agbno)
|
||||
*cleft = tmp;
|
||||
@ -689,6 +699,7 @@ xfs_refcount_find_left_extents(
|
||||
cleft->rc_domain = domain;
|
||||
}
|
||||
} else {
|
||||
not_found:
|
||||
/*
|
||||
* No extents, so pretend that there's one covering the whole
|
||||
* range.
|
||||
@ -740,12 +751,10 @@ xfs_refcount_find_right_extents(
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
if (tmp.rc_domain != domain)
|
||||
return 0;
|
||||
if (tmp.rc_startblock != agbno + aglen)
|
||||
return 0;
|
||||
if (domain == XFS_REFC_DOMAIN_SHARED && tmp.rc_refcount < 2)
|
||||
return 0;
|
||||
if (domain == XFS_REFC_DOMAIN_COW && tmp.rc_refcount > 1)
|
||||
return 0;
|
||||
/* We have a right extent; retrieve (or invent) the next left one */
|
||||
*right = tmp;
|
||||
|
||||
@ -761,6 +770,9 @@ xfs_refcount_find_right_extents(
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
if (tmp.rc_domain != domain)
|
||||
goto not_found;
|
||||
|
||||
/* if tmp ends at the end of our range, just use that */
|
||||
if (xfs_refc_next(&tmp) == agbno + aglen)
|
||||
*cright = tmp;
|
||||
@ -780,6 +792,7 @@ xfs_refcount_find_right_extents(
|
||||
cright->rc_domain = domain;
|
||||
}
|
||||
} else {
|
||||
not_found:
|
||||
/*
|
||||
* No extents, so pretend that there's one covering the whole
|
||||
* range.
|
||||
@ -889,7 +902,7 @@ xfs_refcount_merge_extents(
|
||||
aglen);
|
||||
}
|
||||
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -961,7 +974,7 @@ xfs_refcount_adjust_extents(
|
||||
error = xfs_refcount_get_rec(cur, &ext, &found_rec);
|
||||
if (error)
|
||||
goto out_error;
|
||||
if (!found_rec) {
|
||||
if (!found_rec || ext.rc_domain != XFS_REFC_DOMAIN_SHARED) {
|
||||
ext.rc_startblock = cur->bc_mp->m_sb.sb_agblocks;
|
||||
ext.rc_blockcount = 0;
|
||||
ext.rc_refcount = 0;
|
||||
@ -1400,6 +1413,8 @@ xfs_refcount_find_shared(
|
||||
error = -EFSCORRUPTED;
|
||||
goto out_error;
|
||||
}
|
||||
if (tmp.rc_domain != XFS_REFC_DOMAIN_SHARED)
|
||||
goto done;
|
||||
|
||||
/* If the extent ends before the start, look at the next one */
|
||||
if (tmp.rc_startblock + tmp.rc_blockcount <= agbno) {
|
||||
@ -1415,6 +1430,8 @@ xfs_refcount_find_shared(
|
||||
error = -EFSCORRUPTED;
|
||||
goto out_error;
|
||||
}
|
||||
if (tmp.rc_domain != XFS_REFC_DOMAIN_SHARED)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* If the extent starts after the range we want, bail out */
|
||||
@ -1446,7 +1463,8 @@ xfs_refcount_find_shared(
|
||||
error = -EFSCORRUPTED;
|
||||
goto out_error;
|
||||
}
|
||||
if (tmp.rc_startblock >= agbno + aglen ||
|
||||
if (tmp.rc_domain != XFS_REFC_DOMAIN_SHARED ||
|
||||
tmp.rc_startblock >= agbno + aglen ||
|
||||
tmp.rc_startblock != *fbno + *flen)
|
||||
break;
|
||||
*flen = min(*flen + tmp.rc_blockcount, agbno + aglen - *fbno);
|
||||
@ -1537,6 +1555,11 @@ xfs_refcount_adjust_cow_extents(
|
||||
error = xfs_refcount_get_rec(cur, &ext, &found_rec);
|
||||
if (error)
|
||||
goto out_error;
|
||||
if (XFS_IS_CORRUPT(cur->bc_mp, found_rec &&
|
||||
ext.rc_domain != XFS_REFC_DOMAIN_COW)) {
|
||||
error = -EFSCORRUPTED;
|
||||
goto out_error;
|
||||
}
|
||||
if (!found_rec) {
|
||||
ext.rc_startblock = cur->bc_mp->m_sb.sb_agblocks;
|
||||
ext.rc_blockcount = 0;
|
||||
@ -1746,8 +1769,14 @@ xfs_refcount_recover_extent(
|
||||
|
||||
rr = kmem_alloc(sizeof(struct xfs_refcount_recovery), 0);
|
||||
xfs_refcount_btrec_to_irec(rec, &rr->rr_rrec);
|
||||
list_add_tail(&rr->rr_list, debris);
|
||||
|
||||
if (XFS_IS_CORRUPT(cur->bc_mp,
|
||||
rr->rr_rrec.rc_domain != XFS_REFC_DOMAIN_COW)) {
|
||||
kmem_free(rr);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
list_add_tail(&rr->rr_list, debris);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -439,8 +439,8 @@ xchk_xref_is_cow_staging(
|
||||
return;
|
||||
}
|
||||
|
||||
/* CoW flag must be set, refcount must be 1. */
|
||||
if (rc.rc_domain != XFS_REFC_DOMAIN_COW || rc.rc_refcount != 1)
|
||||
/* CoW lookup returned a shared extent record? */
|
||||
if (rc.rc_domain != XFS_REFC_DOMAIN_COW)
|
||||
xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
|
||||
|
||||
/* Must be at least as long as what was passed in */
|
||||
|
Loading…
Reference in New Issue
Block a user