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