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