#! /bin/bash # FSQA Test No. 276 # # Run fsstress to create a reasonably strange file system, make a # snapshot and run more fsstress. Then select some files from that fs, # run filefrag to get the extent mapping and follow the backrefs. # We check to end up back at the original file with the correct offset. # #----------------------------------------------------------------------- # Copyright (C) 2011 STRATO. 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 # #----------------------------------------------------------------------- # # creator owner=list.btrfs@jan-o-sch.net seq=`basename $0` echo "QA output created by $seq" here=`pwd` tmp=/tmp/$$ status=1 _cleanup() { echo "*** unmount" umount $SCRATCH_MNT 2>/dev/null 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 _need_to_be_root _supported_fs btrfs _supported_os Linux _require_scratch _require_nobigloopfs _require_btrfs inspect-internal _require_command "/usr/sbin/filefrag" 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, " "' # this makes filefrag output script readable by using a perl helper. # output is one extent per line, with three numbers separated by '#' # the numbers are: physical, length, logical (all in bytes) # sample output: "1234#10#5678" -> physical 1234, length 10, logical 5678 _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 } # use a logical address and walk the backrefs back to the inode. # compare to the expected result. # returns 0 on success, 1 on error (with output made) _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 } # use an inode number and walk the backrefs back to the file name. # compare to the expected result. # returns 0 on success, 1 on error (with output made) _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 $physical $logical $inum $file ret=$? if [ $ret -eq 0 ]; then _btrfs_inspect_inum $file $inum $snap_name ret=$? fi return $ret } run_check() { echo "# $@" >> $seq.full 2>&1 "$@" >> $seq.full 2>&1 || _fail "failed: '$@'" } workout() { fsz=$1 nfiles=$2 procs=$3 snap_name=$4 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" run_check _scratch_mount # -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" # make some noise but ensure we're not touching existing data # extents. run_check $FSSTRESS_PROG -d $SCRATCH_MNT -p $procs -n 2000 \ -z -f chown=3 -f link=1 -f mkdir=2 -f mknod=2 \ -f rename=2 -f setxattr=1 -f symlink=2 clean_dir="$SCRATCH_MNT/next" mkdir $clean_dir # now make more files to get a higher tree run_check $FSSTRESS_PROG -d $clean_dir -w -p $procs -n 2000 \ $FSSTRESS_AVOID run_check umount $SCRATCH_DEV >/dev/null 2>&1 run_check _scratch_mount "-o atime" 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 _fail "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