btrfs: add test for multiple fsync with adjacent preallocated extents
[xfstests-dev.git] / common / punch
1 ##/bin/bash
2 # SPDX-License-Identifier: GPL-2.0
3 # Copyright (c) 2007 Silicon Graphics, Inc.  All Rights Reserved.
4 #
5 # common functions for excersizing hole punches with extent size hints etc.
6
7 _spawn_test_file() {
8         echo "# spawning test file with $*"
9         local blksize=$1
10         local file_size=`expr $2 \* $blksize`
11         local extent_size_hint=`expr $3 \* $blksize`
12         local test_file=$4
13         local reserve_space=$5
14
15         if [ $extent_size_hint -ne 0 ]; then
16                 echo "+ setting extent size hint to $extent_size_hint"
17                 $XFS_IO_PROG -f \
18                 -c "extsize $extent_size_hint" \
19                 $test_file
20         fi
21         # print extent size hint for $test_file
22         $XFS_IO_PROG -f \
23         -c "extsize" \
24         $test_file
25
26         if [ "$reserve_space" == "noresv" ]; then
27                 echo "+ not using resvsp at file creation"
28                 $XFS_IO_PROG -f \
29                 -c "truncate $file_size" \
30                 $test_file
31         else
32                 $XFS_IO_PROG -f \
33                 -c "truncate $file_size" \
34                 -c "resvsp 0 $file_size" \
35                 $test_file
36         fi
37 }
38
39 _do_write() {
40         echo "# writing with $*"
41         local blksize=$1
42         local write_offset=`expr $2 \* $blksize`
43         local write_size=`expr $3 \* $blksize`
44         local test_file=$4
45
46         $XFS_IO_PROG -f \
47         -c "pwrite $write_offset $write_size" \
48         $test_file >/dev/null
49 }
50
51 _do_bmap() {
52         echo "# showing file state $*"
53         local test_file=$1
54
55         $XFS_IO_PROG -f \
56         -c "bmap -vvp" \
57         $test_file
58 }
59
60 _coalesce_extents()
61 {
62         block_size=$1
63
64         [[ -z $block_size ]] && block_size=512
65
66         awk -v block_size="$block_size" -F: '
67         {
68                 range = $2;
69                 type = $3;
70
71                 split(range, bounds, "[\\[ \\.\\]]");
72                 low = bounds[3];
73                 high = bounds[5];
74
75                 if (type != prev_type) {
76                         if (prev_type != "")
77                                 printf("%u]:%s\n", (low * 512 / block_size) - 1,
78                                         prev_type);
79                         printf("%u: [%u..", out_count++,
80                                 (low * 512) / block_size);
81                         prev_type = type;
82                 }
83         }
84         END {
85                 if (prev_type != "")
86                         printf("%u]:%s\n", ((high + 1) * 512 / block_size) - 1,
87                                 prev_type);
88         }'
89 }
90
91 _filter_fiemap()
92 {
93         block_size=$1
94
95         $AWK_PROG '
96                 $3 ~ /hole/ {
97                         print $1, $2, $3;
98                         next;
99                 }
100                 $5 ~ /0x[[:xdigit:]]*8[[:xdigit:]][[:xdigit:]]/ {
101                         print $1, $2, "unwritten";
102                         next;
103                 }
104                 $5 ~ /0x[[:xdigit:]]+/ {
105                         print $1, $2, "data";
106                 }' |
107         _coalesce_extents $block_size
108 }
109
110 _filter_fiemap_flags()
111 {
112         $AWK_PROG '
113                 $3 ~ /hole/ {
114                         print $1, $2, $3;
115                         next;
116                 }
117                 $5 ~ /0x[[:xdigit:]]*8[[:xdigit:]][[:xdigit:]]/ {
118                         print $1, $2, "unwritten";
119                         next;
120                 }
121                 $5 ~ /0x[[:xdigit:]]+/ {
122                         print $1, $2, $5;
123                 }' |
124         _coalesce_extents
125 }
126
127 # Filters fiemap output to only print the 
128 # file offset column and whether or not
129 # it is an extent or a hole
130 _filter_hole_fiemap()
131 {
132         $AWK_PROG '
133                 $3 ~ /hole/ {
134                         print $1, $2, $3; 
135                         next;
136                 }   
137                 $5 ~ /0x[[:xdigit:]]+/ {
138                         print $1, $2, "extent";
139                 }' |
140         _coalesce_extents
141 }
142
143 #     10000 Unwritten preallocated extent
144 #     01000 Doesn't begin on stripe unit
145 #     00100 Doesn't end   on stripe unit
146 #     00010 Doesn't begin on stripe width
147 #     00001 Doesn't end   on stripe width
148 _filter_bmap()
149 {
150         awk '
151                 $3 ~ /hole/ {
152                         print $1, $2, $3;
153                         next;
154                 }
155                 $7 ~ /1[01][01][01][01]/ {
156                         print $1, $2, "unwritten";
157                         next;
158                 }
159                 $7 ~ /0[01][01][01][01]/ {
160                         print $1, $2, "data"
161                 }' |
162         _coalesce_extents
163 }
164
165 die_now()
166 {
167         status=1
168         exit
169 }
170
171 # test the different corner cases for zeroing a range:
172 #
173 #       1. into a hole
174 #       2. into allocated space
175 #       3. into unwritten space
176 #       4. hole -> data
177 #       5. hole -> unwritten
178 #       6. data -> hole
179 #       7. data -> unwritten
180 #       8. unwritten -> hole
181 #       9. unwritten -> data
182 #       10. hole -> data -> hole
183 #       11. data -> hole -> data
184 #       12. unwritten -> data -> unwritten
185 #       13. data -> unwritten -> data
186 #       14. data -> hole @ EOF
187 #       15. data -> hole @ 0
188 #       16. data -> cache cold ->hole
189 #       17. data -> hole in single block file
190 #
191 # Test file is removed, created and sync'd between tests.
192 #
193 # Use -k flag to keep the file between tests.  This will
194 # test the handling of pre-existing holes.
195 #
196 # Use the -d flag to not sync the file between tests.
197 # This will test the handling of delayed extents
198 #
199 # Use the -u flag to not run unwritten tests.
200 # This will eliminate some unnecessary information.
201 #
202 _test_generic_punch()
203 {
204
205         remove_testfile=1
206         sync_cmd="-c fsync"
207         unwritten_tests=1
208         OPTIND=1
209         while getopts 'dku' OPTION
210         do
211                 case $OPTION in
212                 k)      remove_testfile=
213                 ;;
214                 d)      sync_cmd=
215                 ;;
216                 u)      unwritten_tests=
217                 ;;
218                 ?)      echo Invalid flag
219                 exit 1
220                 ;;
221                 esac
222         done
223         shift $(($OPTIND - 1))
224
225         alloc_cmd=$1
226         punch_cmd=$2
227         zero_cmd=$3     #if not testing zero just set to punch
228         map_cmd=$4
229         filter_cmd=$5
230         testfile=$6
231
232         # The punch hole tests needs multiple of the largest extent size being
233         # tested, with multiple=16 it can test extent size upto 64k.
234         multiple=16
235         _4k="$((multiple * 4))k"
236         _8k="$((multiple * 8))k"
237         _12k="$((multiple * 12))k"
238         _20k="$((multiple * 20))k"
239
240         # initial test state must be defined, otherwise the first test can fail
241         # due ot stale file state left from previous tests.
242         rm -f $testfile
243
244         echo "  1. into a hole"
245         $XFS_IO_PROG -f -c "truncate $_20k" \
246                 -c "$zero_cmd $_4k $_8k" \
247                 -c "$map_cmd -v" $testfile | $filter_cmd
248         [ $? -ne 0 ] && die_now
249         _md5_checksum $testfile
250
251         echo "  2. into allocated space"
252         if [ "$remove_testfile" ]; then
253                 rm -f $testfile
254         fi
255         $XFS_IO_PROG -f -c "truncate $_20k" \
256                 -c "pwrite 0 $_20k" $sync_cmd \
257                 -c "$zero_cmd $_4k $_8k" \
258                 -c "$map_cmd -v" $testfile | $filter_cmd
259         [ $? -ne 0 ] && die_now
260         _md5_checksum $testfile
261
262         if [ "$unwritten_tests" ]; then
263                 echo "  3. into unwritten space"
264                 if [ "$remove_testfile" ]; then
265                         rm -f $testfile
266                 fi
267                 $XFS_IO_PROG -f -c "truncate $_20k" \
268                         -c "$alloc_cmd 0 $_20k" \
269                         -c "$zero_cmd $_4k $_8k" \
270                         -c "$map_cmd -v" $testfile | $filter_cmd
271                 [ $? -ne 0 ] && die_now
272                 _md5_checksum $testfile
273         fi
274
275         echo "  4. hole -> data"
276         if [ "$remove_testfile" ]; then
277                 rm -f $testfile
278         fi
279         $XFS_IO_PROG -f -c "truncate $_20k" \
280                 -c "pwrite $_8k $_8k" $sync_cmd \
281                 -c "$zero_cmd $_4k $_8k" \
282                 -c "$map_cmd -v" $testfile | $filter_cmd
283         [ $? -ne 0 ] && die_now
284         _md5_checksum $testfile
285
286         if [ "$unwritten_tests" ]; then
287                 echo "  5. hole -> unwritten"
288                 if [ "$remove_testfile" ]; then
289                         rm -f $testfile
290                 fi
291                 $XFS_IO_PROG -f -c "truncate $_20k" \
292                         -c "$alloc_cmd $_8k $_8k" \
293                         -c "$zero_cmd $_4k $_8k" \
294                         -c "$map_cmd -v" $testfile | $filter_cmd
295                 [ $? -ne 0 ] && die_now
296                 _md5_checksum $testfile
297         fi
298
299         echo "  6. data -> hole"
300         if [ "$remove_testfile" ]; then
301                 rm -f $testfile
302         fi
303         $XFS_IO_PROG -f -c "truncate $_20k" \
304                 -c "pwrite 0 $_8k" $sync_cmd \
305                  -c "$zero_cmd $_4k $_8k" \
306                 -c "$map_cmd -v" $testfile | $filter_cmd
307         [ $? -ne 0 ] && die_now
308         _md5_checksum $testfile
309
310         if [ "$unwritten_tests" ]; then
311                 echo "  7. data -> unwritten"
312                 if [ "$remove_testfile" ]; then
313                         rm -f $testfile
314                 fi
315                 $XFS_IO_PROG -f -c "truncate $_20k" \
316                         -c "pwrite 0 $_8k" $sync_cmd \
317                         -c "$alloc_cmd $_8k $_8k" \
318                         -c "$zero_cmd $_4k $_8k" \
319                         -c "$map_cmd -v" $testfile | $filter_cmd
320                 [ $? -ne 0 ] && die_now
321                 _md5_checksum $testfile
322
323                 echo "  8. unwritten -> hole"
324                 if [ "$remove_testfile" ]; then
325                         rm -f $testfile
326                 fi
327                 $XFS_IO_PROG -f -c "truncate $_20k" \
328                         -c "$alloc_cmd 0 $_8k" \
329                         -c "$zero_cmd $_4k $_8k" \
330                         -c "$map_cmd -v" $testfile | $filter_cmd
331                 [ $? -ne 0 ] && die_now
332                 _md5_checksum $testfile
333
334                 echo "  9. unwritten -> data"
335                 if [ "$remove_testfile" ]; then
336                         rm -f $testfile
337                 fi
338                 $XFS_IO_PROG -f -c "truncate $_20k" \
339                         -c "$alloc_cmd 0 $_8k" \
340                         -c "pwrite $_8k $_8k" $sync_cmd \
341                         -c "$zero_cmd $_4k $_8k" \
342                         -c "$map_cmd -v" $testfile | $filter_cmd
343                 [ $? -ne 0 ] && die_now
344                 _md5_checksum $testfile
345         fi
346
347         echo "  10. hole -> data -> hole"
348         if [ "$remove_testfile" ]; then
349                 rm -f $testfile
350         fi
351         $XFS_IO_PROG -f -c "truncate $_20k" \
352                 -c "pwrite $_8k $_4k" $sync_cmd \
353                 -c "$zero_cmd $_4k $_12k" \
354                 -c "$map_cmd -v" $testfile | $filter_cmd
355         [ $? -ne 0 ] && die_now
356         _md5_checksum $testfile
357
358         echo "  11. data -> hole -> data"
359         if [ "$remove_testfile" ]; then
360                 rm -f $testfile
361         fi
362         $XFS_IO_PROG -f -c "truncate $_20k" \
363                 -c "$alloc_cmd 0 $_20k" \
364                 -c "pwrite 0 $_8k" \
365                 -c "pwrite $_12k $_8k" $sync_cmd \
366                 -c "$punch_cmd $_8k $_4k" \
367                 -c "$zero_cmd $_4k $_12k" \
368                 -c "$map_cmd -v" $testfile | $filter_cmd
369         [ $? -ne 0 ] && die_now
370         _md5_checksum $testfile
371
372         if [ "$unwritten_tests" ]; then
373                 echo "  12. unwritten -> data -> unwritten"
374                 if [ "$remove_testfile" ]; then
375                         rm -f $testfile
376                 fi
377                 $XFS_IO_PROG -f -c "truncate $_20k" \
378                         -c "$alloc_cmd 0 $_20k" \
379                         -c "pwrite $_8k $_4k" $sync_cmd \
380                         -c "$zero_cmd $_4k $_12k" \
381                         -c "$map_cmd -v" $testfile | $filter_cmd
382                 [ $? -ne 0 ] && die_now
383                 _md5_checksum $testfile
384
385                 echo "  13. data -> unwritten -> data"
386                 if [ "$remove_testfile" ]; then
387                         rm -f $testfile
388                 fi
389                 $XFS_IO_PROG -f -c "truncate $_20k" \
390                         -c "$alloc_cmd 0 $_20k" \
391                         -c "pwrite 0k $_4k" $sync_cmd \
392                         -c "pwrite $_12k $_8k" -c "fsync" \
393                         -c "$zero_cmd $_4k $_12k" \
394                         -c "$map_cmd -v" $testfile | $filter_cmd
395                 [ $? -ne 0 ] && die_now
396                 _md5_checksum $testfile
397         fi
398
399         # Don't need to check EOF case for collapse range.
400         # VFS layer return invalid error in this case,
401         # So it is not a proper case for collapse range test of each local fs.
402         if [ "$zero_cmd" != "fcollapse" ]; then
403                 echo "  14. data -> hole @ EOF"
404                 rm -f $testfile
405                 $XFS_IO_PROG -f -c "truncate $_20k" \
406                         -c "pwrite 0 $_20k" $sync_cmd \
407                         -c "$zero_cmd $_12k $_8k" \
408                         -c "$map_cmd -v" $testfile | $filter_cmd
409                 [ $? -ne 0 ] && die_now
410                 _md5_checksum $testfile
411         fi
412
413         if [ "$zero_cmd" == "fcollapse" ]; then
414                 echo "  14. data -> hole @ 0"
415         else
416                 echo "  15. data -> hole @ 0"
417         fi
418
419         if [ "$remove_testfile" ]; then
420                 rm -f $testfile
421         fi
422         $XFS_IO_PROG -f -c "truncate $_20k" \
423                 -c "pwrite 0 $_20k" $sync_cmd \
424                 -c "$zero_cmd 0 $_8k" \
425                 -c "$map_cmd -v" $testfile | $filter_cmd
426         [ $? -ne 0 ] && die_now
427         _md5_checksum $testfile
428
429         # If zero_cmd is fcollpase, don't check unaligned offsets
430         if [ "$zero_cmd" == "fcollapse" ]; then
431                 return
432         fi
433
434         # If zero_cmd is finsert, don't check unaligned offsets
435         if [ "$zero_cmd" == "finsert" ]; then
436                 return
437         fi
438
439         echo "  16. data -> cache cold ->hole"
440         if [ "$remove_testfile" ]; then
441                 rm -f $testfile
442                 rm -f $testfile.2
443         else
444                 cp $testfile $testfile.2
445         fi
446         $XFS_IO_PROG -f -c "truncate $_20k" \
447                 -c "pwrite $_8k $_12k" -c "fsync" $testfile.2 \
448                 > /dev/null
449         $XFS_IO_PROG -f -c "truncate $_20k" \
450                 -c "pwrite 0 $_20k" $sync_cmd \
451                 -c "$zero_cmd 0k $_8k" \
452                 -c "fadvise -d" \
453                 -c "$map_cmd -v" $testfile | $filter_cmd
454         diff $testfile $testfile.2
455         [ $? -ne 0 ] && die_now
456         rm -f $testfile.2
457         _md5_checksum $testfile
458
459         # different file sizes mean we can't use md5sum to check the hole is
460         # valid. Hence use hexdump to dump the contents and chop off the last
461         # line of output that indicates the file size. We also have to fudge
462         # the extent size as that will change with file size, too - that's what
463         # the sed line noise does - it will always result in an output of [0..7]
464         # so it matches 4k block size...
465         echo "  17. data -> hole in single block file"
466         if [ "$remove_testfile" ]; then
467                 rm -f $testfile
468         fi
469         block_size=`_get_block_size $TEST_DIR`
470         $XFS_IO_PROG -f -c "truncate $block_size" \
471                 -c "pwrite 0 $block_size" $sync_cmd \
472                 -c "$zero_cmd 128 128" \
473                 -c "$map_cmd -v" $testfile | $filter_cmd | \
474                          sed -e "s/\.\.[0-9]*\]/..7\]/"
475         [ $? -ne 0 ] && die_now
476         od -x $testfile | head -n -1
477 }
478
479 _test_block_boundaries()
480 {
481
482         remove_testfile=1
483         sync_cmd="-c fsync"
484         unwritten_tests=1
485         OPTIND=1
486         while getopts 'dk' OPTION
487         do
488                 case $OPTION in
489                 k)      remove_testfile=
490                 ;;
491                 d)      sync_cmd=
492                 ;;
493                 ?)      echo Invalid flag
494                 exit 1
495                 ;;
496                 esac
497         done
498         shift $(($OPTIND - 1))
499
500         bs=$1
501         zero_cmd=$2
502         filter_cmd=$3
503         testfile=$4
504
505         # Block size plus 1
506         bs_p1=$(($bs + 1))
507         # Block size plus 2
508         bs_p2=$(($bs + 2))
509
510         # Block size minus 1
511         bs_m1=$(($bs - 1))
512
513         # Block size multiplied by 2
514         bs_t2=$(($bs * 2))
515
516         # Block size divided by 2
517         bs_d2=$(($bs / 2))
518
519         echo "zero 0, 1"
520         $XFS_IO_PROG -f -t -c "pwrite -S 0x41 0 $bs" \
521                            -c "pwrite -S 0x42 $bs $bs" \
522                            -c "$zero_cmd 0 1" \
523                            -c "pread -v 0 $bs_t2" \
524                            $testfile | $filter_cmd
525
526         echo "zero 0, $bs_m1"
527         $XFS_IO_PROG -f -t -c "pwrite -S 0x41 0 $bs" \
528                            -c "pwrite -S 0x42 $bs $bs" \
529                            -c "$zero_cmd 0 $bs_m1" \
530                            -c "pread -v 0 $bs_t2" \
531                            $testfile | $filter_cmd
532
533         echo "zero 0, $bs"
534         $XFS_IO_PROG -f -t -c "pwrite -S 0x41 0 $bs" \
535                            -c "pwrite -S 0x42 $bs $bs" \
536                            -c "$zero_cmd 0 $bs" \
537                            -c "pread -v 0 $bs_t2" \
538                            $testfile | $filter_cmd
539
540         echo "zero 0, $bs_p1"
541         $XFS_IO_PROG -f -t -c "pwrite -S 0x41 0 $bs" \
542                            -c "pwrite -S 0x42 $bs $bs" \
543                            -c "$zero_cmd 0 $bs_p1" \
544                            -c "pread -v 0 $bs_t2" \
545                            $testfile | $filter_cmd
546
547         echo "zero $bs_m1, $bs"
548         $XFS_IO_PROG -f -t -c "pwrite -S 0x41 0 $bs" \
549                            -c "pwrite -S 0x42 $bs $bs" \
550                            -c "$zero_cmd $bs_m1 $bs" \
551                            -c "pread -v 0 $bs_t2" \
552                            $testfile | $filter_cmd
553
554         echo "zero $bs_m1, $bs_p1"
555         $XFS_IO_PROG -f -t -c "pwrite -S 0x41 0 $bs" \
556                            -c "pwrite -S 0x42 $bs $bs" \
557                            -c "$zero_cmd $bs_m1 $bs_p1" \
558                            -c "pread -v 0 $bs_t2" \
559                            $testfile | $filter_cmd
560
561         echo "zero $bs_m1, $bs_p2"
562         $XFS_IO_PROG -f -t -c "pwrite -S 0x41 0 $bs" \
563                            -c "pwrite -S 0x42 $bs $bs" \
564                            -c "$zero_cmd $bs_m1 $bs_p2" \
565                            -c "pread -v 0 $bs_t2" \
566                            $testfile | $filter_cmd
567
568
569         echo "zero $bs, $bs"
570         $XFS_IO_PROG -f -t -c "pwrite -S 0x41 0 $bs" \
571                            -c "pwrite -S 0x42 $bs $bs" \
572                            -c "$zero_cmd $bs $bs" \
573                            -c "pread -v 0 $bs_t2" \
574                            $testfile | $filter_cmd
575
576
577         echo "zero $bs_d2 , $bs"
578         $XFS_IO_PROG -f -t -c "pwrite -S 0x41 0 $bs" \
579                            -c "pwrite -S 0x42 $bs $bs" \
580                            -c "$zero_cmd $bs_d2 $bs" \
581                            -c "pread -v 0 $bs_t2" \
582                            $testfile | $filter_cmd
583 }