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