]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: check record domain when accessing refcount records
authorDarrick J. Wong <djwong@kernel.org>
Fri, 18 Nov 2022 11:23:57 +0000 (12:23 +0100)
committerCarlos Maiolino <cem@kernel.org>
Mon, 21 Nov 2022 14:26:49 +0000 (15:26 +0100)
Source kernel commit: f62ac3e0ac33d366fe81e194fee81de9be2cd886

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>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
libxfs/xfs_refcount.c

index 192014d00cd1f9b5cdd1cb68321421e8cf9b50cb..179b68679243b7123364ba00b42e9a07436d68c4 100644 (file)
@@ -380,6 +380,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;
 
@@ -431,6 +433,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
@@ -507,6 +512,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,
@@ -568,6 +575,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.
@@ -648,11 +657,9 @@ xfs_refcount_find_left_extents(
                goto out_error;
        }
 
-       if (xfs_refc_next(&tmp) != agbno)
-               return 0;
-       if (domain == XFS_REFC_DOMAIN_SHARED && tmp.rc_refcount < 2)
+       if (tmp.rc_domain != domain)
                return 0;
-       if (domain == XFS_REFC_DOMAIN_COW && tmp.rc_refcount > 1)
+       if (xfs_refc_next(&tmp) != agbno)
                return 0;
        /* We have a left extent; retrieve (or invent) the next right one */
        *left = tmp;
@@ -669,6 +676,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;
@@ -688,6 +698,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.
@@ -739,11 +750,9 @@ xfs_refcount_find_right_extents(
                goto out_error;
        }
 
-       if (tmp.rc_startblock != agbno + aglen)
-               return 0;
-       if (domain == XFS_REFC_DOMAIN_SHARED && tmp.rc_refcount < 2)
+       if (tmp.rc_domain != domain)
                return 0;
-       if (domain == XFS_REFC_DOMAIN_COW && tmp.rc_refcount > 1)
+       if (tmp.rc_startblock != agbno + aglen)
                return 0;
        /* We have a right extent; retrieve (or invent) the next left one */
        *right = tmp;
@@ -760,6 +769,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;
@@ -779,6 +791,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.
@@ -888,7 +901,7 @@ xfs_refcount_merge_extents(
                                aglen);
        }
 
-       return error;
+       return 0;
 }
 
 /*
@@ -960,7 +973,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;
@@ -1399,6 +1412,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) {
@@ -1414,6 +1429,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 */
@@ -1445,7 +1462,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);
@@ -1536,6 +1554,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;
@@ -1745,8 +1768,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;
 }