xfstests: move xfs specific tests out of top directory
[xfstests-dev.git] / 276
1 #! /bin/bash
2 # FSQA Test No. 276
3 #
4 # Run fsstress to create a reasonably strange file system, make a
5 # snapshot and run more fsstress. Then select some files from that fs,
6 # run filefrag to get the extent mapping and follow the backrefs.
7 # We check to end up back at the original file with the correct offset.
8 #
9 #-----------------------------------------------------------------------
10 # Copyright (C) 2011 STRATO.  All rights reserved.
11 #
12 # This program is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License as
14 # published by the Free Software Foundation.
15 #
16 # This program is distributed in the hope that it would be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write the Free Software Foundation,
23 # Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
24 #
25 #-----------------------------------------------------------------------
26 #
27
28 seq=`basename $0`
29 echo "QA output created by $seq"
30
31 here=`pwd`
32 tmp=/tmp/$$
33 status=1
34 noise_pid=0
35
36 _cleanup()
37 {
38         if [ $noise_pid -ne 0 ]; then
39                 echo "background noise kill $noise_pid" >>$seq.full
40                 kill $noise_pid
41                 noise_pid=0
42                 wait
43         fi
44         echo "*** unmount"
45         umount $SCRATCH_MNT 2>/dev/null
46         rm -f $tmp.*
47 }
48 trap "_cleanup; exit \$status" 0 1 2 3 15
49
50 # get standard environment, filters and checks
51 . ./common.rc
52 . ./common.filter
53
54 # real QA test starts here
55 _need_to_be_root
56 _supported_fs btrfs
57 _supported_os Linux
58 _require_scratch
59 _require_no_large_scratch_dev
60 _require_btrfs inspect-internal
61 _require_command "/usr/sbin/filefrag"
62
63 rm -f $seq.full
64
65 FILEFRAG_FILTER='if (/, blocksize (\d+)/) {$blocksize = $1; next} ($ext, '\
66 '$logical, $physical, $expected, $length, $flags) = (/^\s*(\d+)\s+(\d+)'\
67 '\s+(\d+)\s+(?:(\d+)\s+)?(\d+)\s+(.*)/) or next; $flags =~ '\
68 '/(?:^|,)inline(?:,|$)/ and next; print $physical * $blocksize, "#", '\
69 '$length * $blocksize, "#", $logical * $blocksize, " "'
70
71 # this makes filefrag output script readable by using a perl helper.
72 # output is one extent per line, with three numbers separated by '#'
73 # the numbers are: physical, length, logical (all in bytes)
74 # sample output: "1234#10#5678" -> physical 1234, length 10, logical 5678
75 _filter_extents()
76 {
77         tee -a $seq.full | $PERL_PROG -ne "$FILEFRAG_FILTER"
78 }
79
80 _check_file_extents()
81 {
82         cmd="filefrag -v $1"
83         echo "# $cmd" >> $seq.full
84         out=`$cmd | _filter_extents`
85         if [ -z "$out" ]; then
86                 return 1
87         fi
88         echo "after filter: $out" >> $seq.full
89         echo $out
90         return 0
91 }
92
93 # use a logical address and walk the backrefs back to the inode.
94 # compare to the expected result.
95 # returns 0 on success, 1 on error (with output made)
96 _btrfs_inspect_addr()
97 {
98         mp=$1
99         addr=$2
100         expect_addr=$3
101         expect_inum=$4
102         file=$5
103         cmd="$BTRFS_UTIL_PROG inspect-internal logical-resolve -P $addr $mp"
104         echo "# $cmd" >> $seq.full
105         out=`$cmd`
106         echo "$out" >> $seq.full
107         grep_expr="inode $expect_inum offset $expect_addr root"
108         echo "$out" | grep "^$grep_expr 5$" >/dev/null
109         ret=$?
110         if [ $ret -eq 0 ]; then
111                 # look for a root number that is not 5
112                 echo "$out" | grep "^$grep_expr \([0-46-9][0-9]*\|5[0-9]\+\)$" \
113                         >/dev/null
114                 ret=$?
115         fi
116         if [ $ret -eq 0 ]; then
117                 return 0
118         fi
119         echo "unexpected output from"
120         echo "  $cmd"
121         echo "expected inum: $expect_inum, expected address: $expect_addr,"\
122                 "file: $file, got:"
123         echo "$out"
124         return 1
125 }
126
127 # use an inode number and walk the backrefs back to the file name.
128 # compare to the expected result.
129 # returns 0 on success, 1 on error (with output made)
130 _btrfs_inspect_inum()
131 {
132         file=$1
133         inum=$2
134         snap_name=$3
135         mp="$SCRATCH_MNT/$snap_name"
136         cmd="$BTRFS_UTIL_PROG inspect-internal inode-resolve $inum $mp"
137         echo "# $cmd" >> $seq.full
138         out=`$cmd`
139         echo "$out" >> $seq.full
140         grep_expr="^$file$"
141         cnt=`echo "$out" | grep "$grep_expr" | wc -l`
142         if [ $cnt -ge "1" ]; then
143                 return 0
144         fi
145         echo "unexpected output from"
146         echo "  $cmd"
147         echo "expected path: $file, got:"
148         echo "$out"
149         return 1
150 }
151
152 _btrfs_inspect_check()
153 {
154         file=$1
155         physical=$2
156         length=$3
157         logical=$4
158         snap_name=$5
159         cmd="stat -c %i $file"
160         echo "# $cmd" >> $seq.full
161         inum=`$cmd`
162         echo "$inum" >> $seq.full
163         _btrfs_inspect_addr $SCRATCH_MNT $physical $logical $inum $file
164         ret=$?
165         if [ $ret -eq 0 ]; then
166                 _btrfs_inspect_inum $file $inum $snap_name
167                 ret=$?
168         fi
169         return $ret
170 }
171
172 workout()
173 {
174         fsz=$1
175         nfiles=$2
176         procs=$3
177         snap_name=$4
178         do_bg_noise=$5
179
180         umount $SCRATCH_DEV >/dev/null 2>&1
181         echo "*** mkfs -dsize=$fsz"    >>$seq.full
182         echo ""                                     >>$seq.full
183         _scratch_mkfs_sized $fsz >>$seq.full 2>&1 \
184                 || _fail "size=$fsz mkfs failed"
185         run_check _scratch_mount
186         # -w ensures that the only ops are ones which cause write I/O
187         run_check $FSSTRESS_PROG -d $SCRATCH_MNT -w -p $procs -n 2000 \
188                 $FSSTRESS_AVOID
189
190         run_check $BTRFS_UTIL_PROG subvol snap $SCRATCH_MNT \
191                 $SCRATCH_MNT/$snap_name
192
193         run_check umount $SCRATCH_DEV >/dev/null 2>&1
194         run_check _scratch_mount "-o compress=lzo"
195
196         # make some noise but ensure we're not touching existing data
197         # extents.
198         run_check $FSSTRESS_PROG -d $SCRATCH_MNT -p $procs -n 4000 \
199                 -z -f chown=3 -f link=1 -f mkdir=2 -f mknod=2 \
200                 -f rename=2 -f setxattr=1 -f symlink=2
201
202         clean_dir="$SCRATCH_MNT/next"
203         mkdir $clean_dir
204         # now make more files to get a higher tree
205         run_check $FSSTRESS_PROG -d $clean_dir -w -p $procs -n 2000 \
206                 $FSSTRESS_AVOID
207         run_check umount $SCRATCH_DEV >/dev/null 2>&1
208         run_check _scratch_mount "-o atime"
209
210         if [ $do_bg_noise -ne 0 ]; then
211                 # make background noise while backrefs are being walked
212                 while /bin/true; do
213                         echo background fsstress >>$seq.full
214                         run_check $FSSTRESS_PROG -d $SCRATCH_MNT/bgnoise -n 999
215                         echo background rm >>$seq.full
216                         rm -rf $SCRATCH_MNT/bgnoise/
217                 done &
218                 noise_pid=`jobs -p %1`
219                 echo "background noise by $noise_pid" >>$seq.full
220         fi
221
222         cnt=0
223         errcnt=0
224         dir="$SCRATCH_MNT/$snap_name/"
225         for file in `find $dir -name f\* -size +0 | sort -R`; do
226                 extents=`_check_file_extents $file`
227                 ret=$?
228                 if [ $ret -ne 0 ]; then
229                         continue;
230                 fi
231                 for i in $extents; do
232                         physical=$i
233                         length=$i
234                         logical=$i
235                         physical=`echo $physical | sed -e 's/#.*//'`
236                         length=`echo $length | sed -e 's/[^#]+#//'`
237                         length=`echo $length | sed -e 's/#.*//'`
238                         logical=`echo $logical | sed -e 's/.*#//'`
239                         _btrfs_inspect_check $file $physical $length $logical \
240                                                 $snap_name
241                         ret=$?
242                         if [ $ret -ne 0 ]; then
243                                 errcnt=`expr $errcnt + 1`
244                         fi
245                 done
246                 cnt=`expr $cnt + 1`
247                 if [ $cnt -ge $nfiles ]; then
248                         break
249                 fi
250         done
251
252         if [ $errcnt -gt 0 ]; then
253                 _fail "test failed: $errcnt error(s)"
254         fi
255 }
256
257 echo "*** test backref walking"
258
259 snap_name="snap1"
260 filesize=`expr 2000 \* 1024 \* 1024`
261 nfiles=4
262 numprocs=1
263 do_bg_noise=1
264
265 workout $filesize $nfiles $numprocs $snap_name $do_bg_noise
266
267 echo "*** done"
268 status=0
269 exit