xfs/010: test repair for finobt corruption
authorBrian Foster <bfoster@redhat.com>
Tue, 13 May 2014 05:27:47 +0000 (15:27 +1000)
committerDave Chinner <david@fromorbit.com>
Tue, 13 May 2014 05:27:47 +0000 (15:27 +1000)
The finobt creates a duplicate subset of inode allocation metadata from
the inobt. xfs_repair should detect and repair inconsistencies in the
finobt that could be caused by bugs or corruption. This test uses xfs_db
to cause targeted corruptions in the finobt and verify repair detects
and corrects the filesystem.

In particular, the test corrupts individual finobt records to cause
inconsistency between the inode allocation count fields as well as
causing the finobt to contain a record with no free inodes.

Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
tests/xfs/010 [new file with mode: 0755]
tests/xfs/010.out [new file with mode: 0644]
tests/xfs/group

diff --git a/tests/xfs/010 b/tests/xfs/010
new file mode 100755 (executable)
index 0000000..2b5ad00
--- /dev/null
@@ -0,0 +1,133 @@
+#!/bin/bash
+# FS QA Test No. xfs/010
+#
+# Test xfs_repair of the free inode btree (finobt). Make a couple targeted
+# corruptions and verify that xfs_repair detects and repairs the filesystem to
+# a consistent state.
+#
+#-----------------------------------------------------------------------
+# Copyright (c) 2014 Red Hat, Inc.  All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#
+#-----------------------------------------------------------------------
+#
+
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1       # failure is the default!
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+. ./common/repair
+
+_cleanup()
+{
+       cd /
+       umount $SCRATCH_MNT 2>/dev/null
+       rm -f $tmp.*
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_sparse_inode_populate()
+{
+       dir=$1
+       count=$2
+
+       for i in $(seq 0 $count)
+       do
+               touch $dir/$i
+       done
+
+       # Inode chunks are allocated 64 inodes at a time. If we remove 1 out of
+       # every 32 we allocated above, we'll end up leaving an inode or two free
+       # in each chunk. This ensures that most records are inserted into the
+       # finobt.
+       for i in $(seq 0 32 $count)
+       do
+               rm -f $dir/$i
+       done
+}
+
+_filter_dbval()
+{
+       awk '{ print $3 }'
+}
+
+_corrupt_finobt_records()
+{
+       dev=$1
+
+       # determine the root block of the finobt
+       free_root=`$XFS_DB_PROG -c "agi 0" -c "p free_root" $dev |
+                       _filter_dbval`
+
+       # Corrupt a freecount value. This should never exceed 64.
+       $XFS_DB_PROG -x -c "fsb $free_root" -c "type inobt" \
+               -c "write recs[1].freecount 70" $dev
+
+       # Create a corrupted non-free record, which should never appear in the
+       # finobt.
+       $XFS_DB_PROG -x -c "fsb $free_root" -c "type inobt" \
+                -c "write recs[2].freecount 0" $dev
+       $XFS_DB_PROG -x -c "fsb $free_root" -c "type inobt" \
+               -c "write recs[2].free 0" $dev
+}
+
+_corrupt_finobt_root()
+{
+       dev=$1
+
+       # nuke the agi finobt root fields
+       $XFS_DB_PROG -x -c "agi 0" -c "write free_root 0" $dev
+       $XFS_DB_PROG -x -c "agi 0" -c "write free_level 0" $dev
+}
+
+# real QA test starts here
+_supported_fs xfs
+_supported_os Linux
+
+_require_scratch
+_require_xfs_mkfs_finobt
+_require_xfs_finobt
+
+rm -f $seqres.full
+
+_scratch_mkfs_xfs "-m crc=1,finobt=1 -d agcount=2" | _filter_mkfs 2>$seqres.full
+
+# sparsely populate the fs such that we create records with free inodes
+_scratch_mount
+_sparse_inode_populate $SCRATCH_MNT 999
+umount $SCRATCH_MNT
+
+# corrupt some finobt records
+_corrupt_finobt_records $SCRATCH_DEV
+
+# repair should detect the inconsistencies
+_scratch_xfs_repair 2>&1 | _filter_repair
+_check_scratch_fs
+
+# nuke the finobt root, repair will have to regenerate from the inobt
+_corrupt_finobt_root $SCRATCH_DEV
+
+_scratch_xfs_repair 2>&1 | _filter_repair
+_check_scratch_fs
+
+status=0
+exit
diff --git a/tests/xfs/010.out b/tests/xfs/010.out
new file mode 100644 (file)
index 0000000..ab5e5f3
--- /dev/null
@@ -0,0 +1,57 @@
+QA output created by 010
+meta-data=DDEV isize=XXX agcount=N, agsize=XXX blks
+data     = bsize=XXX blocks=XXX, imaxpct=PCT
+         = sunit=XXX swidth=XXX, unwritten=X
+naming   =VERN bsize=XXX
+log      =LDEV bsize=XXX blocks=XXX
+realtime =RDEV extsz=XXX blocks=XXX, rtextents=XXX
+recs[1].freecount = 70
+recs[2].freecount = 0
+recs[2].free = 0
+Phase 1 - find and verify superblock...
+Phase 2 - using <TYPEOF> log
+        - zero log...
+        - scan filesystem freespace and inode maps...
+finobt ir_freecount/free mismatch, AGNO/INO, freecount 70 nfree 2
+finobt record with no free inodes, AGNO/INO
+        - found root inode chunk
+Phase 3 - for each AG...
+        - scan and clear agi unlinked lists...
+        - process known inodes and perform inode discovery...
+        - process newly discovered inodes...
+Phase 4 - check for duplicate blocks...
+        - setting up duplicate extent list...
+        - check for inodes claiming duplicate blocks...
+Phase 5 - rebuild AG headers and trees...
+        - reset superblock...
+Phase 6 - check inode connectivity...
+        - resetting contents of realtime bitmap and summary inodes
+        - traversing filesystem ...
+        - traversal finished ...
+        - moving disconnected inodes to lost+found ...
+Phase 7 - verify and correct link counts...
+done
+free_root = 0
+free_level = 0
+Phase 1 - find and verify superblock...
+Phase 2 - using <TYPEOF> log
+        - zero log...
+        - scan filesystem freespace and inode maps...
+bad agbno AGBNO for finobt root, agno 0
+        - found root inode chunk
+Phase 3 - for each AG...
+        - scan and clear agi unlinked lists...
+        - process known inodes and perform inode discovery...
+        - process newly discovered inodes...
+Phase 4 - check for duplicate blocks...
+        - setting up duplicate extent list...
+        - check for inodes claiming duplicate blocks...
+Phase 5 - rebuild AG headers and trees...
+        - reset superblock...
+Phase 6 - check inode connectivity...
+        - resetting contents of realtime bitmap and summary inodes
+        - traversing filesystem ...
+        - traversal finished ...
+        - moving disconnected inodes to lost+found ...
+Phase 7 - verify and correct link counts...
+done
index 4624fc3b1fdf555e8c0dc1211fb4d16ecabc9cef..c228a63fe5da740410e05392b389ee25b313f45b 100644 (file)
@@ -7,6 +7,7 @@
 007 auto quota quick
 008 rw ioctl auto quick
 009 rw ioctl auto prealloc quick
+010 auto quick repair
 012 rw auto quick
 016 rw auto quick
 017 mount auto quick stress