]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_repair: only update in-core extent state after scanning full extent
authorDarrick J. Wong <darrick.wong@oracle.com>
Wed, 23 May 2018 21:30:48 +0000 (16:30 -0500)
committerEric Sandeen <sandeen@redhat.com>
Wed, 23 May 2018 21:30:48 +0000 (16:30 -0500)
In process_bmbt_reclist_int, only update the in-core extent state after
clearing the entire extent for conflicts.  If we encounter conflicts
we'll try rebuilding the fork from rmap data and rescanning the fork.
It is essential to avoid polluting the in-memory state with garbage
data so that we don't end up nuking other files needlessly.  Found by
fuzzing recs[1].blockcount = middlebit in xfs/380.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
repair/dinode.c

index 45d8078f77ffc2973e8d5f5595cae65c6438fa25..58a509b0134862d7a356648982fcb801c6131c42 100644 (file)
@@ -751,7 +751,6 @@ _("%s fork in ino %" PRIu64 " claims free block %" PRIu64 "\n"),
                                /* fall through ... */
                        case XR_E_INUSE1:       /* seen by rmap */
                        case XR_E_UNKNOWN:
-                               set_bmap_ext(agno, agbno, blen, XR_E_INUSE);
                                break;
 
                        case XR_E_BAD_STATE:
@@ -773,7 +772,6 @@ _("%s fork in inode %" PRIu64 " claims metadata block %" PRIu64 "\n"),
 
                        case XR_E_INUSE:
                        case XR_E_MULT:
-                               set_bmap_ext(agno, agbno, blen, XR_E_MULT);
                                if (type == XR_INO_DATA &&
                                    xfs_sb_version_hasreflink(&mp->m_sb))
                                        break;
@@ -792,6 +790,34 @@ _("%s fork in %s inode %" PRIu64 " claims CoW block %" PRIu64 "\n"),
                                do_error(
 _("illegal state %d in block map %" PRIu64 "\n"),
                                        state, b);
+                               goto done;
+                       }
+               }
+
+               /*
+                * Update the internal extent map only after we've checked
+                * every block in this extent.  The first time we reject this
+                * data fork we'll try to rebuild the bmbt from rmap data.
+                * After a successful rebuild we'll try this scan again.
+                * (If the rebuild fails we won't come back here.)
+                */
+               agbno = XFS_FSB_TO_AGBNO(mp, irec.br_startblock);
+               ebno = agbno + irec.br_blockcount;
+               for (; agbno < ebno; agbno += blen) {
+                       state = get_bmap_ext(agno, agbno, ebno, &blen);
+                       switch (state)  {
+                       case XR_E_FREE:
+                       case XR_E_FREE1:
+                       case XR_E_INUSE1:
+                       case XR_E_UNKNOWN:
+                               set_bmap_ext(agno, agbno, blen, XR_E_INUSE);
+                               break;
+                       case XR_E_INUSE:
+                       case XR_E_MULT:
+                               set_bmap_ext(agno, agbno, blen, XR_E_MULT);
+                               break;
+                       default:
+                               break;
                        }
                }
                if (collect_rmaps) { /* && !check_dups */