Jan Schmidt
2011-Dec-07 13:33 UTC
[PATCH] xfstests: new check 276 to ensure btrfs backref integrity
This is a btrfs specific scratch test checking the backref walker. It
creates a file system with compressed and uncompressed data extents, picks
files randomly and uses filefrag to get their extents. It then asks the
btrfs utility (inspect-internal) to do the backref resolving from fs-logical
address (the one filefrag calls "physical") back to the inode number
and
file-logical offset, verifying the result.
Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
---
276 | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
276.out | 4 +
common.config | 1 +
group | 1 +
4 files changed, 236 insertions(+), 0 deletions(-)
create mode 100755 276
create mode 100644 276.out
diff --git a/276 b/276
new file mode 100755
index 0000000..f22d089
--- /dev/null
+++ b/276
@@ -0,0 +1,230 @@
+#! /bin/bash
+
+# creator
+owner=list.btrfs@jan-o-sch.net
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+# 1=production, 0=avoid touching the scratch dev (no mount/umount, no writes)
+fresh=1
+tmp=/tmp/$$
+status=1
+FSTYP=btrfs
+
+_cleanup()
+{
+ if [ $fresh -ne 0 ]; then
+ echo "*** unmount"
+ umount $SCRATCH_MNT 2>/dev/null
+ fi
+ rm -f $tmp.*
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+# real QA test starts here
+_supported_fs btrfs
+_supported_os Linux
+
+if [ $fresh -ne 0 ]; then
+ _require_scratch
+fi
+
+_require_nobigloopfs
+
+[ -n "$BTRFS_UTIL_PROG" ] || _notrun "btrfs executable not
found"
+$BTRFS_UTIL_PROG inspect-internal --help >/dev/null 2>&1
+[ $? -eq 0 ] || _notrun "btrfs executable too old"
+which filefrag >/dev/null 2>&1
+[ $? -eq 0 ] || _notrun "filefrag missing"
+
+
+rm -f $seq.full
+
+FILEFRAG_FILTER=''if (/, blocksize (\d+)/) {$blocksize = $1; next}
($ext, ''\
+''$logical, $physical, $expected, $length, $flags) =
(/^\s*(\d+)\s+(\d+)''\
+''\s+(\d+)\s+(?:(\d+)\s+)?(\d+)\s+(.*)/) or next; $flags =~ ''\
+''/(?:^|,)inline(?:,|$)/ and next; print $physical * $blocksize,
"#", ''\
+''$length * $blocksize, "#", $logical * $blocksize, "
"''
+
+_filter_extents()
+{
+ tee -a $seq.full | $PERL_PROG -ne "$FILEFRAG_FILTER"
+}
+
+_check_file_extents()
+{
+ cmd="filefrag -vx $1"
+ echo "# $cmd" >> $seq.full
+ out=`$cmd | _filter_extents`
+ if [ -z "$out" ]; then
+ return 1
+ fi
+ echo "after filter: $out" >> $seq.full
+ echo $out
+ return 0
+}
+
+_btrfs_inspect_addr()
+{
+ mp=$1
+ addr=$2
+ expect_addr=$3
+ expect_inum=$4
+ file=$5
+ cmd="$BTRFS_UTIL_PROG inspect-internal logical-resolve -P $addr $mp"
+ echo "# $cmd" >> $seq.full
+ out=`$cmd`
+ echo "$out" >> $seq.full
+ grep_expr="inode $expect_inum offset $expect_addr root"
+ echo "$out" | grep "^$grep_expr 5$" >/dev/null
+ ret=$?
+ if [ $ret -eq 0 ]; then
+ # look for a root number that is not 5
+ echo "$out" | grep "^$grep_expr
\([0-46-9][0-9]*\|5[0-9]\+\)$" \
+ >/dev/null
+ ret=$?
+ fi
+ if [ $ret -eq 0 ]; then
+ return 0
+ fi
+ echo "unexpected output from"
+ echo " $cmd"
+ echo "expected inum: $expect_inum, expected address: $expect_addr,"\
+ "file: $file, got:"
+ echo "$out"
+ return 1
+}
+
+_btrfs_inspect_inum()
+{
+ file=$1
+ inum=$2
+ snap_name=$3
+ mp="$SCRATCH_MNT/$snap_name"
+ cmd="$BTRFS_UTIL_PROG inspect-internal inode-resolve $inum $mp"
+ echo "# $cmd" >> $seq.full
+ out=`$cmd`
+ echo "$out" >> $seq.full
+ grep_expr="^$file$"
+ cnt=`echo "$out" | grep "$grep_expr" | wc -l`
+ if [ $cnt -ge "1" ]; then
+ return 0
+ fi
+ echo "unexpected output from"
+ echo " $cmd"
+ echo "expected path: $file, got:"
+ echo "$out"
+ return 1
+}
+
+_btrfs_inspect_check()
+{
+ file=$1
+ physical=$2
+ length=$3
+ logical=$4
+ snap_name=$5
+ cmd="stat -c %i $file"
+ echo "# $cmd" >> $seq.full
+ inum=`$cmd`
+ echo "$inum" >> $seq.full
+ _btrfs_inspect_addr "$SCRATCH_MNT/$snap_name" $physical $logical
$inum\
+ $file
+ ret=$?
+ if [ $ret -eq 0 ]; then
+ _btrfs_inspect_inum $file $inum $snap_name
+ ret=$?
+ fi
+ return $?
+}
+
+run_check()
+{
+ echo "# $@" >> $seq.full 2>&1
+ "$@" >> $seq.full 2>&1 || _fail "failed:
''$@''"
+}
+
+workout()
+{
+ fsz=$1
+ nfiles=$2
+ procs=$3
+ snap_name=$4
+
+ if [ $fresh -ne 0 ]; then
+ umount $SCRATCH_DEV >/dev/null 2>&1
+ echo "*** mkfs -dsize=$fsz" >>$seq.full
+ echo "" >>$seq.full
+ _scratch_mkfs_sized $fsz >>$seq.full 2>&1 \
+ || _fail "size=$fsz mkfs failed"
+ _scratch_mount >>$seq.full 2>&1 \
+ || _fail "mount failed"
+ # -w ensures that the only ops are ones which cause write I/O
+ run_check $FSSTRESS_PROG -d $SCRATCH_MNT -w -p $procs -n 1000 \
+ $FSSTRESS_AVOID
+
+ run_check $BTRFS_UTIL_PROG subvol snap $SCRATCH_MNT \
+ $SCRATCH_MNT/$snap_name
+
+ run_check umount $SCRATCH_DEV >/dev/null 2>&1
+ run_check _scratch_mount "-o compress=lzo"
+
+ # -w ensures that the only ops are ones which cause write I/O
+ run_check $FSSTRESS_PROG -d $SCRATCH_MNT -w -p $procs -n 2000 \
+ $FSSTRESS_AVOID
+ else
+ echo "*** skipping mkfs part" >>$seq.full
+ fi
+
+ cnt=0
+ errcnt=0
+ dir="$SCRATCH_MNT/$snap_name/"
+ for file in `find $dir -name f\* -size +0 | sort -R`; do
+ extents=`_check_file_extents $file`
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ continue;
+ fi
+ for i in $extents; do
+ physical=$i
+ length=$i
+ logical=$i
+ physical=`echo $physical | sed -e ''s/#.*//''`
+ length=`echo $length | sed -e ''s/[^#]+#//''`
+ length=`echo $length | sed -e ''s/#.*//''`
+ logical=`echo $logical | sed -e ''s/.*#//''`
+ _btrfs_inspect_check $file $physical $length $logical \
+ $snap_name
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ errcnt=`expr $errcnt + 1`
+ fi
+ done
+ cnt=`expr $cnt + 1`
+ if [ $cnt -ge $nfiles ]; then
+ break
+ fi
+ done
+ if [ $errcnt -gt 0 ]; then
+ echo "test failed: $errcnt error(s)"
+ fi
+}
+
+echo "*** test backref walking"
+
+snap_name="snap1"
+filesize=`expr 2000 \* 1024 \* 1024`
+nfiles=4
+numprocs=1
+
+workout $filesize $nfiles $numprocs $snap_name
+
+echo "*** done"
+status=0
+exit
diff --git a/276.out b/276.out
new file mode 100644
index 0000000..2032dea
--- /dev/null
+++ b/276.out
@@ -0,0 +1,4 @@
+QA output created by 276
+*** test backref walking
+*** done
+*** unmount
diff --git a/common.config b/common.config
index 1df2bbd..7bed1c5 100644
--- a/common.config
+++ b/common.config
@@ -185,6 +185,7 @@ case "$HOSTOS" in
export MKFS_XFS_PROG="`set_prog_path mkfs.xfs`"
export MKFS_UDF_PROG="`set_prog_path mkudffs`"
export MKFS_BTRFS_PROG="`set_prog_path mkfs.btrfs`"
+ export BTRFS_UTIL_PROG="`set_prog_path btrfs`"
export XFS_FSR_PROG="`set_prog_path xfs_fsr`"
export MKFS_NFS_PROG="false"
;;
diff --git a/group b/group
index 08d999a..dd9f00d 100644
--- a/group
+++ b/group
@@ -389,3 +389,4 @@ deprecated
273 auto rw
274 auto rw
275 auto rw
+276 auto rw metadata
--
1.7.2.2
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs"
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Eric Sandeen
2011-Dec-21 17:48 UTC
Re: [PATCH] xfstests: new check 276 to ensure btrfs backref integrity
On 12/7/11 7:33 AM, Jan Schmidt wrote:> This is a btrfs specific scratch test checking the backref walker. It > creates a file system with compressed and uncompressed data extents, picks > files randomly and uses filefrag to get their extents. It then asks the > btrfs utility (inspect-internal) to do the backref resolving from fs-logical > address (the one filefrag calls "physical") back to the inode number and > file-logical offset, verifying the result. > > Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net> > --- > 276 | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 276.out | 4 + > common.config | 1 + > group | 1 + > 4 files changed, 236 insertions(+), 0 deletions(-) > create mode 100755 276 > create mode 100644 276.out > > diff --git a/276 b/276 > new file mode 100755 > index 0000000..f22d089 > --- /dev/null > +++ b/276 > @@ -0,0 +1,230 @@ > +#! /bin/bashPlease put copyright, license, test description etc here, following other existing tests as a template.> +# creator > +owner=list.btrfs@jan-o-sch.net > + > +seq=`basename $0` > +echo "QA output created by $seq" > + > +here=`pwd` > +# 1=production, 0=avoid touching the scratch dev (no mount/umount, no writes) > +fresh=1I asked this on IRC but it seems unlikely that this will ever be changed. Is this essentially so you can debug a failure? Might be worth a comment about under what conditions you would set it to 0.> +tmp=/tmp/$$ > +status=1 > +FSTYP=btrfsprobably should not be explicitly set; no other test does this.> + > +_cleanup() > +{ > + if [ $fresh -ne 0 ]; then > + echo "*** unmount" > + umount $SCRATCH_MNT 2>/dev/null > + fi > + rm -f $tmp.* > +} > +trap "_cleanup; exit \$status" 0 1 2 3 15 > + > +# get standard environment, filters and checks > +. ./common.rc > +. ./common.filter > + > +# real QA test starts here > +_supported_fs btrfs > +_supported_os Linux > + > +if [ $fresh -ne 0 ]; then > + _require_scratch > +fiIf fresh == 0 then what device gets tested?> +_require_nobigloopfs > + > +[ -n "$BTRFS_UTIL_PROG" ] || _notrun "btrfs executable not found" > +$BTRFS_UTIL_PROG inspect-internal --help >/dev/null 2>&1 > +[ $? -eq 0 ] || _notrun "btrfs executable too old" > +which filefrag >/dev/null 2>&1 > +[ $? -eq 0 ] || _notrun "filefrag missing"It might be nice to put these checks into (a) common.* file(s) if they might be useful in other tests going forward.> + > + > +rm -f $seq.full > +comment on this filter''s function please? :)> +FILEFRAG_FILTER=''if (/, blocksize (\d+)/) {$blocksize = $1; next} ($ext, ''\ > +''$logical, $physical, $expected, $length, $flags) = (/^\s*(\d+)\s+(\d+)''\ > +''\s+(\d+)\s+(?:(\d+)\s+)?(\d+)\s+(.*)/) or next; $flags =~ ''\ > +''/(?:^|,)inline(?:,|$)/ and next; print $physical * $blocksize, "#", ''\ > +''$length * $blocksize, "#", $logical * $blocksize, " "'' > + > +_filter_extents() > +{ > + tee -a $seq.full | $PERL_PROG -ne "$FILEFRAG_FILTER" > +} > + > +_check_file_extents() > +{ > + cmd="filefrag -vx $1" > + echo "# $cmd" >> $seq.full > + out=`$cmd | _filter_extents` > + if [ -z "$out" ]; then > + return 1 > + fi > + echo "after filter: $out" >> $seq.full > + echo $out > + return 0 > +}maybe this should go in a common.* if it might be generically useful (see common.defrag, maybe) Can leave that to your judgement though.> +_btrfs_inspect_addr()I could really do with some comments explaining what this is doing, but then I am woefully btrfs-illiterate.> +{ > + mp=$1 > + addr=$2 > + expect_addr=$3 > + expect_inum=$4 > + file=$5 > + cmd="$BTRFS_UTIL_PROG inspect-internal logical-resolve -P $addr $mp" > + echo "# $cmd" >> $seq.full > + out=`$cmd` > + echo "$out" >> $seq.full > + grep_expr="inode $expect_inum offset $expect_addr root" > + echo "$out" | grep "^$grep_expr 5$" >/dev/null > + ret=$? > + if [ $ret -eq 0 ]; then > + # look for a root number that is not 5 > + echo "$out" | grep "^$grep_expr \([0-46-9][0-9]*\|5[0-9]\+\)$" \ > + >/dev/null > + ret=$? > + fi > + if [ $ret -eq 0 ]; then > + return 0 > + fi > + echo "unexpected output from" > + echo " $cmd" > + echo "expected inum: $expect_inum, expected address: $expect_addr,"\ > + "file: $file, got:" > + echo "$out" > + return 1 > +} > + > +_btrfs_inspect_inum() > +{ > + file=$1 > + inum=$2 > + snap_name=$3 > + mp="$SCRATCH_MNT/$snap_name"Hm you conditionally required scratch, but use it here unconditionally I think? Unless this is only called conditionally ... but I think you get here either way.> + cmd="$BTRFS_UTIL_PROG inspect-internal inode-resolve $inum $mp" > + echo "# $cmd" >> $seq.full > + out=`$cmd` > + echo "$out" >> $seq.full > + grep_expr="^$file$" > + cnt=`echo "$out" | grep "$grep_expr" | wc -l` > + if [ $cnt -ge "1" ]; then > + return 0 > + fi > + echo "unexpected output from" > + echo " $cmd" > + echo "expected path: $file, got:" > + echo "$out" > + return 1 > +} > + > +_btrfs_inspect_check() > +{ > + file=$1 > + physical=$2 > + length=$3 > + logical=$4 > + snap_name=$5 > + cmd="stat -c %i $file" > + echo "# $cmd" >> $seq.full > + inum=`$cmd` > + echo "$inum" >> $seq.full > + _btrfs_inspect_addr "$SCRATCH_MNT/$snap_name" $physical $logical $inum\ > + $filescratch used unconditionally here as well?> + ret=$? > + if [ $ret -eq 0 ]; then > + _btrfs_inspect_inum $file $inum $snap_name > + ret=$? > + fi > + return $? > +} > + > +run_check() > +{ > + echo "# $@" >> $seq.full 2>&1 > + "$@" >> $seq.full 2>&1 || _fail "failed: ''$@''" > +}Hm, that is handy. :)> +workout() > +{ > + fsz=$1 > + nfiles=$2 > + procs=$3 > + snap_name=$4 > + > + if [ $fresh -ne 0 ]; then > + umount $SCRATCH_DEV >/dev/null 2>&1 > + echo "*** mkfs -dsize=$fsz" >>$seq.full > + echo "" >>$seq.full > + _scratch_mkfs_sized $fsz >>$seq.full 2>&1 \ > + || _fail "size=$fsz mkfs failed" > + _scratch_mount >>$seq.full 2>&1 \ > + || _fail "mount failed" > + # -w ensures that the only ops are ones which cause write I/O > + run_check $FSSTRESS_PROG -d $SCRATCH_MNT -w -p $procs -n 1000 \ > + $FSSTRESS_AVOID > + > + run_check $BTRFS_UTIL_PROG subvol snap $SCRATCH_MNT \ > + $SCRATCH_MNT/$snap_name > + > + run_check umount $SCRATCH_DEV >/dev/null 2>&1 > + run_check _scratch_mount "-o compress=lzo" > + > + # -w ensures that the only ops are ones which cause write I/O > + run_check $FSSTRESS_PROG -d $SCRATCH_MNT -w -p $procs -n 2000 \ > + $FSSTRESS_AVOID > + else > + echo "*** skipping mkfs part" >>$seq.full > + fi > + > + cnt=0 > + errcnt=0 > + dir="$SCRATCH_MNT/$snap_name/" > + for file in `find $dir -name f\* -size +0 | sort -R`; do > + extents=`_check_file_extents $file` > + ret=$? > + if [ $ret -ne 0 ]; then > + continue; > + fi > + for i in $extents; do > + physical=$i > + length=$i > + logical=$i > + physical=`echo $physical | sed -e ''s/#.*//''` > + length=`echo $length | sed -e ''s/[^#]+#//''` > + length=`echo $length | sed -e ''s/#.*//''` > + logical=`echo $logical | sed -e ''s/.*#//''` > + _btrfs_inspect_check $file $physical $length $logical \ > + $snap_name > + ret=$? > + if [ $ret -ne 0 ]; then > + errcnt=`expr $errcnt + 1` > + fi > + done > + cnt=`expr $cnt + 1` > + if [ $cnt -ge $nfiles ]; then > + break > + fi > + done > + if [ $errcnt -gt 0 ]; then > + echo "test failed: $errcnt error(s)" > + fi > +} > + > +echo "*** test backref walking" > + > +snap_name="snap1" > +filesize=`expr 2000 \* 1024 \* 1024` > +nfiles=4 > +numprocs=1 > + > +workout $filesize $nfiles $numprocs $snap_name > + > +echo "*** done" > +status=0 > +exit > diff --git a/276.out b/276.out > new file mode 100644 > index 0000000..2032dea > --- /dev/null > +++ b/276.out > @@ -0,0 +1,4 @@ > +QA output created by 276 > +*** test backref walking > +*** done > +*** unmount > diff --git a/common.config b/common.config > index 1df2bbd..7bed1c5 100644 > --- a/common.config > +++ b/common.config > @@ -185,6 +185,7 @@ case "$HOSTOS" in > export MKFS_XFS_PROG="`set_prog_path mkfs.xfs`" > export MKFS_UDF_PROG="`set_prog_path mkudffs`" > export MKFS_BTRFS_PROG="`set_prog_path mkfs.btrfs`" > + export BTRFS_UTIL_PROG="`set_prog_path btrfs`" > export XFS_FSR_PROG="`set_prog_path xfs_fsr`" > export MKFS_NFS_PROG="false" > ;; > diff --git a/group b/group > index 08d999a..dd9f00d 100644 > --- a/group > +++ b/group > @@ -389,3 +389,4 @@ deprecated > 273 auto rw > 274 auto rw > 275 auto rw > +276 auto rw metadata_______________________________________________ xfs mailing list xfs@oss.sgi.com http://oss.sgi.com/mailman/listinfo/xfs
Jan Schmidt
2011-Dec-22 10:59 UTC
Re: [PATCH] xfstests: new check 276 to ensure btrfs backref integrity
Thanks for the feedback. I''ve now removed the $fresh code completely as it''s not meant to be used by anyone but me :-) _require_btrfs will become a new helper in common.rc. Will resend soon as 278 (hoping that number holds). -Jan -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html