]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
xfs: xrep_findroot_block should reject root blocks with siblings
authorDarrick J. Wong <darrick.wong@oracle.com>
Thu, 18 Oct 2018 06:20:26 +0000 (17:20 +1100)
committerDave Chinner <david@fromorbit.com>
Thu, 18 Oct 2018 06:20:26 +0000 (17:20 +1100)
In xrep_findroot_block, if we find a candidate root block with sibling
pointers or sibling blocks on the same tree level, we should not return
that block as a tree root because root blocks cannot have siblings.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
fs/xfs/scrub/repair.c

index 9f08dd9bf1d553934bf2aa2f8a108ab8435bc19c..63786341ac2ac412f2512acaca90ecb5a61f4dac 100644 (file)
@@ -692,12 +692,13 @@ xrep_findroot_block(
        struct xrep_find_ag_btree       *fab,
        uint64_t                        owner,
        xfs_agblock_t                   agbno,
-       bool                            *found_it)
+       bool                            *done_with_block)
 {
        struct xfs_mount                *mp = ri->sc->mp;
        struct xfs_buf                  *bp;
        struct xfs_btree_block          *btblock;
        xfs_daddr_t                     daddr;
+       int                             block_level;
        int                             error;
 
        daddr = XFS_AGB_TO_DADDR(mp, ri->sc->sa.agno, agbno);
@@ -735,18 +736,52 @@ xrep_findroot_block(
                goto out;
        bp->b_ops = fab->buf_ops;
 
-       /* Ignore this block if it's lower in the tree than we've seen. */
-       if (fab->root != NULLAGBLOCK &&
-           xfs_btree_get_level(btblock) < fab->height)
-               goto out;
-
        /* Make sure we pass the verifiers. */
        bp->b_ops->verify_read(bp);
        if (bp->b_error)
                goto out;
-       fab->root = agbno;
-       fab->height = xfs_btree_get_level(btblock) + 1;
-       *found_it = true;
+
+       /*
+        * This block passes the magic/uuid and verifier tests for this btree
+        * type.  We don't need the caller to try the other tree types.
+        */
+       *done_with_block = true;
+
+       /*
+        * Compare this btree block's level to the height of the current
+        * candidate root block.
+        *
+        * If the level matches the root we found previously, throw away both
+        * blocks because there can't be two candidate roots.
+        *
+        * If level is lower in the tree than the root we found previously,
+        * ignore this block.
+        */
+       block_level = xfs_btree_get_level(btblock);
+       if (block_level + 1 == fab->height) {
+               fab->root = NULLAGBLOCK;
+               goto out;
+       } else if (block_level < fab->height) {
+               goto out;
+       }
+
+       /*
+        * This is the highest block in the tree that we've found so far.
+        * Update the btree height to reflect what we've learned from this
+        * block.
+        */
+       fab->height = block_level + 1;
+
+       /*
+        * If this block doesn't have sibling pointers, then it's the new root
+        * block candidate.  Otherwise, the root will be found farther up the
+        * tree.
+        */
+       if (btblock->bb_u.s.bb_leftsib == cpu_to_be32(NULLAGBLOCK) &&
+           btblock->bb_u.s.bb_rightsib == cpu_to_be32(NULLAGBLOCK))
+               fab->root = agbno;
+       else
+               fab->root = NULLAGBLOCK;
 
        trace_xrep_findroot_block(mp, ri->sc->sa.agno, agbno,
                        be32_to_cpu(btblock->bb_magic), fab->height - 1);
@@ -768,7 +803,7 @@ xrep_findroot_rmap(
        struct xrep_findroot            *ri = priv;
        struct xrep_find_ag_btree       *fab;
        xfs_agblock_t                   b;
-       bool                            found_it;
+       bool                            done;
        int                             error = 0;
 
        /* Ignore anything that isn't AG metadata. */
@@ -777,16 +812,16 @@ xrep_findroot_rmap(
 
        /* Otherwise scan each block + btree type. */
        for (b = 0; b < rec->rm_blockcount; b++) {
-               found_it = false;
+               done = false;
                for (fab = ri->btree_info; fab->buf_ops; fab++) {
                        if (rec->rm_owner != fab->rmap_owner)
                                continue;
                        error = xrep_findroot_block(ri, fab,
                                        rec->rm_owner, rec->rm_startblock + b,
-                                       &found_it);
+                                       &done);
                        if (error)
                                return error;
-                       if (found_it)
+                       if (done)
                                break;
                }
        }