common/btrfs: rename and enhance _require_btrfs to _require_btrfs_command
[xfstests-dev.git] / tests / btrfs / 004
1 #! /bin/bash
2 # FSQA Test No. btrfs/004
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 seqres=$RESULT_DIR/$seq
30 echo "QA output created by $seq"
31
32 here=`pwd`
33 tmp=/tmp/$$
34 status=1
35 noise_pid=0
36
37 _cleanup()
38 {
39         rm $tmp.running
40         wait
41         rm -f $tmp.*
42 }
43 trap "_cleanup; exit \$status" 0 1 2 3 15
44
45 # get standard environment, filters and checks
46 . ./common/rc
47 . ./common/filter
48
49 # real QA test starts here
50 _supported_fs btrfs
51 _supported_os Linux
52 _require_scratch
53 _require_no_large_scratch_dev
54 _require_btrfs_command inspect-internal logical-resolve
55 _require_btrfs_command inspect-internal inode-resolve
56 _require_command "/usr/sbin/filefrag" filefrag
57
58 rm -f $seqres.full
59
60 FILEFRAG_FILTER='
61         if (/blocks? of (\d+) bytes/) {
62                 $blocksize = $1;
63                 next
64         }
65         ($ext, $logical, $physical, $length) =
66                 (/^\s*(\d+):\s+(\d+)..\s+\d+:\s+(\d+)..\s+\d+:\s+(\d+):/)
67         or next;
68         ($flags) = /.*:\s*(\S*)$/;
69         print $physical * $blocksize, "#",
70               $length * $blocksize, "#",
71               $logical * $blocksize, "#",
72               $flags, " "'
73
74 # this makes filefrag output script readable by using a perl helper.
75 # output is one extent per line, with three numbers separated by '#'
76 # the numbers are: physical, length, logical (all in bytes)
77 # sample output: "1234#10#5678" -> physical 1234, length 10, logical 5678
78 _filter_extents()
79 {
80         tee -a $seqres.full | $PERL_PROG -ne "$FILEFRAG_FILTER"
81 }
82
83 _check_file_extents()
84 {
85         cmd="filefrag -v $1"
86         echo "# $cmd" >> $seqres.full
87         out=`$cmd | _filter_extents`
88         if [ -z "$out" ]; then
89                 return 1
90         fi
91         echo "after filter: $out" >> $seqres.full
92         echo $out
93         return 0
94 }
95
96 # use a logical address and walk the backrefs back to the inode.
97 # compare to the expected result.
98 # returns 0 on success, 1 on error (with output made)
99 _btrfs_inspect_addr()
100 {
101         mp=$1
102         addr=$2
103         expect_addr=$3
104         expect_inum=$4
105         file=$5
106         cmd="$BTRFS_UTIL_PROG inspect-internal logical-resolve -P $addr $mp"
107         echo "# $cmd" >> $seqres.full
108         out=`$cmd`
109         echo "$out" >> $seqres.full
110         grep_expr="inode $expect_inum offset $expect_addr root"
111         echo "$out" | grep "^$grep_expr 5$" >/dev/null
112         ret=$?
113         if [ $ret -eq 0 ]; then
114                 # look for a root number that is not 5
115                 echo "$out" | grep "^$grep_expr \([0-46-9][0-9]*\|5[0-9]\+\)$" \
116                         >/dev/null
117                 ret=$?
118         fi
119         if [ $ret -eq 0 ]; then
120                 return 0
121         fi
122         echo "unexpected output from"
123         echo "  $cmd"
124         echo "expected inum: $expect_inum, expected address: $expect_addr,"\
125                 "file: $file, got:"
126         echo "$out"
127         return 1
128 }
129
130 # use an inode number and walk the backrefs back to the file name.
131 # compare to the expected result.
132 # returns 0 on success, 1 on error (with output made)
133 _btrfs_inspect_inum()
134 {
135         file=$1
136         inum=$2
137         snap_name=$3
138         mp="$SCRATCH_MNT/$snap_name"
139         cmd="$BTRFS_UTIL_PROG inspect-internal inode-resolve $inum $mp"
140         echo "# $cmd" >> $seqres.full
141         out=`$cmd`
142         echo "$out" >> $seqres.full
143         grep_expr="^$file$"
144         cnt=`echo "$out" | grep "$grep_expr" | wc -l`
145         if [ $cnt -ge "1" ]; then
146                 return 0
147         fi
148         echo "unexpected output from"
149         echo "  $cmd"
150         echo "expected path: $file, got:"
151         echo "$out"
152         return 1
153 }
154
155 _btrfs_inspect_check()
156 {
157         file=$1
158         physical=$2
159         length=$3
160         logical=$4
161         snap_name=$5
162         cmd="stat -c %i $file"
163         echo "# $cmd" >> $seqres.full
164         inum=`$cmd`
165         echo "$inum" >> $seqres.full
166         _btrfs_inspect_addr $SCRATCH_MNT $physical $logical $inum $file
167         ret=$?
168         if [ $ret -eq 0 ]; then
169                 _btrfs_inspect_inum $file $inum $snap_name
170                 ret=$?
171         fi
172         return $ret
173 }
174
175 workout()
176 {
177         fsz=$1
178         nfiles=$2
179         procs=$3
180         snap_name=$4
181         do_bg_noise=$5
182
183         _scratch_unmount >/dev/null 2>&1
184         echo "*** mkfs -dsize=$fsz"    >>$seqres.full
185         echo ""                                     >>$seqres.full
186         _scratch_mkfs_sized $fsz >>$seqres.full 2>&1 \
187                 || _fail "size=$fsz mkfs failed"
188         run_check _scratch_mount
189         # -w ensures that the only ops are ones which cause write I/O
190         run_check $FSSTRESS_PROG -d $SCRATCH_MNT -w -p $procs -n 2000 \
191                 $FSSTRESS_AVOID
192
193         _run_btrfs_util_prog subvolume snapshot $SCRATCH_MNT \
194                 $SCRATCH_MNT/$snap_name
195
196         run_check _scratch_unmount >/dev/null 2>&1
197         run_check _scratch_mount "-o compress=lzo"
198
199         # make some noise but ensure we're not touching existing data
200         # extents.
201         run_check $FSSTRESS_PROG -d $SCRATCH_MNT -p $procs -n 4000 \
202                 -z -f chown=3 -f link=1 -f mkdir=2 -f mknod=2 \
203                 -f rename=2 -f setxattr=1 -f symlink=2
204
205         clean_dir="$SCRATCH_MNT/next"
206         mkdir $clean_dir
207         # now make more files to get a higher tree
208         run_check $FSSTRESS_PROG -d $clean_dir -w -p $procs -n 2000 \
209                 $FSSTRESS_AVOID
210         run_check _scratch_unmount >/dev/null 2>&1
211         run_check _scratch_mount "-o atime"
212
213         if [ $do_bg_noise -ne 0 ]; then
214                 # make background noise while backrefs are being walked
215                 while [ -f "$tmp.running" ]; do
216                         echo background fsstress >>$seqres.full
217                         run_check $FSSTRESS_PROG -d $SCRATCH_MNT/bgnoise -n 999
218                         echo background rm >>$seqres.full
219                         rm -rf $SCRATCH_MNT/bgnoise/
220                 done &
221                 noise_pid=`jobs -p %1`
222                 echo "background noise by $noise_pid" >>$seqres.full
223         fi
224
225         cnt=0
226         errcnt=0
227         dir="$SCRATCH_MNT/$snap_name/"
228         for file in `find $dir -name f\* -size +0 | sort -R`; do
229                 extents=`_check_file_extents $file`
230                 ret=$?
231                 if [ $ret -ne 0 ]; then
232                         continue;
233                 fi
234                 for i in $extents; do
235                         physical=`echo $i | cut -d '#' -f 1`
236                         length=`echo $i | cut -d '#' -f 2`
237                         logical=`echo $i | cut -d '#' -f 3`
238                         flags=`echo $i | cut -d '#' -f 4`
239                         # Skip inline extents, otherwise btrfs inspect-internal
240                         # logical-resolve will fail (with errno ENOENT), as it
241                         # can't find an extent with a start address of 0 in the
242                         # extent tree.
243                         if [ $physical -eq 0 ]; then
244                                 echo "$flags" | grep -E '(^|,)inline(,|$)' \
245                                         > /dev/null
246                                 ret=$?
247                                 if [ $ret -ne 0 ]; then
248                                         echo "Unexpected physical address 0 for non-inline extent, file $file, flags $flags"
249                                 fi
250                         else
251                                 _btrfs_inspect_check $file $physical $length \
252                                         $logical $snap_name
253                                 ret=$?
254                         fi
255                         if [ $ret -ne 0 ]; then
256                                 errcnt=`expr $errcnt + 1`
257                         fi
258                 done
259                 cnt=`expr $cnt + 1`
260                 if [ $cnt -ge $nfiles ]; then
261                         break
262                 fi
263         done
264
265         if [ $errcnt -gt 0 ]; then
266                 _fail "test failed: $errcnt error(s)"
267         fi
268 }
269
270 echo "*** test backref walking"
271
272 snap_name="snap1"
273 filesize=`expr 2000 \* 1024 \* 1024`
274 nfiles=4
275 numprocs=1
276 do_bg_noise=1
277
278 touch $tmp.running
279
280 workout $filesize $nfiles $numprocs $snap_name $do_bg_noise
281
282 echo "*** done"
283 status=0
284 exit