fstests: print symbolic names for fiemap flags
[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                         flags = strtonum($5);
123                         flag_str = "none";
124                         set = 0;
125
126                         if (and(flags, 0x2000)) {
127                                 flag_str = "shared";
128                                 set = 1;
129                         }
130                         if (and(flags, 0x1)) {
131                                 if (set) {
132                                         flag_str = flag_str"|";
133                                 }
134                                 flag_str = flag_str"last";
135                         }
136                         print $1, $2, flag_str
137                 }' |
138         _coalesce_extents
139 }
140
141 # Filters fiemap output to only print the 
142 # file offset column and whether or not
143 # it is an extent or a hole
144 _filter_hole_fiemap()
145 {
146         $AWK_PROG '
147                 $3 ~ /hole/ {
148                         print $1, $2, $3; 
149                         next;
150                 }   
151                 $5 ~ /0x[[:xdigit:]]+/ {
152                         print $1, $2, "extent";
153                 }' |
154         _coalesce_extents
155 }
156
157 #     10000 Unwritten preallocated extent
158 #     01000 Doesn't begin on stripe unit
159 #     00100 Doesn't end   on stripe unit
160 #     00010 Doesn't begin on stripe width
161 #     00001 Doesn't end   on stripe width
162 _filter_bmap()
163 {
164         awk '
165                 $3 ~ /hole/ {
166                         print $1, $2, $3;
167                         next;
168                 }
169                 $7 ~ /1[01][01][01][01]/ {
170                         print $1, $2, "unwritten";
171                         next;
172                 }
173                 $7 ~ /0[01][01][01][01]/ {
174                         print $1, $2, "data"
175                 }' |
176         _coalesce_extents
177 }
178
179 die_now()
180 {
181         status=1
182         exit
183 }
184
185 # test the different corner cases for zeroing a range:
186 #
187 #       1. into a hole
188 #       2. into allocated space
189 #       3. into unwritten space
190 #       4. hole -> data
191 #       5. hole -> unwritten
192 #       6. data -> hole
193 #       7. data -> unwritten
194 #       8. unwritten -> hole
195 #       9. unwritten -> data
196 #       10. hole -> data -> hole
197 #       11. data -> hole -> data
198 #       12. unwritten -> data -> unwritten
199 #       13. data -> unwritten -> data
200 #       14. data -> hole @ EOF
201 #       15. data -> hole @ 0
202 #       16. data -> cache cold ->hole
203 #       17. data -> hole in single block file
204 #
205 # Test file is removed, created and sync'd between tests.
206 #
207 # Use -k flag to keep the file between tests.  This will
208 # test the handling of pre-existing holes.
209 #
210 # Use the -d flag to not sync the file between tests.
211 # This will test the handling of delayed extents
212 #
213 # Use the -u flag to not run unwritten tests.
214 # This will eliminate some unnecessary information.
215 #
216 _test_generic_punch()
217 {
218
219         remove_testfile=1
220         sync_cmd="-c fsync"
221         unwritten_tests=1
222         OPTIND=1
223         while getopts 'dku' OPTION
224         do
225                 case $OPTION in
226                 k)      remove_testfile=
227                 ;;
228                 d)      sync_cmd=
229                 ;;
230                 u)      unwritten_tests=
231                 ;;
232                 ?)      echo Invalid flag
233                 exit 1
234                 ;;
235                 esac
236         done
237         shift $(($OPTIND - 1))
238
239         alloc_cmd=$1
240         punch_cmd=$2
241         zero_cmd=$3     #if not testing zero just set to punch
242         map_cmd=$4
243         filter_cmd=$5
244         testfile=$6
245
246         # The punch hole tests needs multiple of the largest extent size being
247         # tested, with multiple=16 it can test extent size upto 64k.
248         multiple=16
249         _4k="$((multiple * 4))k"
250         _8k="$((multiple * 8))k"
251         _12k="$((multiple * 12))k"
252         _20k="$((multiple * 20))k"
253
254         # initial test state must be defined, otherwise the first test can fail
255         # due ot stale file state left from previous tests.
256         rm -f $testfile
257
258         echo "  1. into a hole"
259         $XFS_IO_PROG -f -c "truncate $_20k" \
260                 -c "$zero_cmd $_4k $_8k" \
261                 -c "$map_cmd -v" $testfile | $filter_cmd
262         [ $? -ne 0 ] && die_now
263         _md5_checksum $testfile
264
265         echo "  2. into allocated space"
266         if [ "$remove_testfile" ]; then
267                 rm -f $testfile
268         fi
269         $XFS_IO_PROG -f -c "truncate $_20k" \
270                 -c "pwrite 0 $_20k" $sync_cmd \
271                 -c "$zero_cmd $_4k $_8k" \
272                 -c "$map_cmd -v" $testfile | $filter_cmd
273         [ $? -ne 0 ] && die_now
274         _md5_checksum $testfile
275
276         if [ "$unwritten_tests" ]; then
277                 echo "  3. into unwritten space"
278                 if [ "$remove_testfile" ]; then
279                         rm -f $testfile
280                 fi
281                 $XFS_IO_PROG -f -c "truncate $_20k" \
282                         -c "$alloc_cmd 0 $_20k" \
283                         -c "$zero_cmd $_4k $_8k" \
284                         -c "$map_cmd -v" $testfile | $filter_cmd
285                 [ $? -ne 0 ] && die_now
286                 _md5_checksum $testfile
287         fi
288
289         echo "  4. hole -> data"
290         if [ "$remove_testfile" ]; then
291                 rm -f $testfile
292         fi
293         $XFS_IO_PROG -f -c "truncate $_20k" \
294                 -c "pwrite $_8k $_8k" $sync_cmd \
295                 -c "$zero_cmd $_4k $_8k" \
296                 -c "$map_cmd -v" $testfile | $filter_cmd
297         [ $? -ne 0 ] && die_now
298         _md5_checksum $testfile
299
300         if [ "$unwritten_tests" ]; then
301                 echo "  5. hole -> unwritten"
302                 if [ "$remove_testfile" ]; then
303                         rm -f $testfile
304                 fi
305                 $XFS_IO_PROG -f -c "truncate $_20k" \
306                         -c "$alloc_cmd $_8k $_8k" \
307                         -c "$zero_cmd $_4k $_8k" \
308                         -c "$map_cmd -v" $testfile | $filter_cmd
309                 [ $? -ne 0 ] && die_now
310                 _md5_checksum $testfile
311         fi
312
313         echo "  6. data -> hole"
314         if [ "$remove_testfile" ]; then
315                 rm -f $testfile
316         fi
317         $XFS_IO_PROG -f -c "truncate $_20k" \
318                 -c "pwrite 0 $_8k" $sync_cmd \
319                  -c "$zero_cmd $_4k $_8k" \
320                 -c "$map_cmd -v" $testfile | $filter_cmd
321         [ $? -ne 0 ] && die_now
322         _md5_checksum $testfile
323
324         if [ "$unwritten_tests" ]; then
325                 echo "  7. data -> unwritten"
326                 if [ "$remove_testfile" ]; then
327                         rm -f $testfile
328                 fi
329                 $XFS_IO_PROG -f -c "truncate $_20k" \
330                         -c "pwrite 0 $_8k" $sync_cmd \
331                         -c "$alloc_cmd $_8k $_8k" \
332                         -c "$zero_cmd $_4k $_8k" \
333                         -c "$map_cmd -v" $testfile | $filter_cmd
334                 [ $? -ne 0 ] && die_now
335                 _md5_checksum $testfile
336
337                 echo "  8. unwritten -> hole"
338                 if [ "$remove_testfile" ]; then
339                         rm -f $testfile
340                 fi
341                 $XFS_IO_PROG -f -c "truncate $_20k" \
342                         -c "$alloc_cmd 0 $_8k" \
343                         -c "$zero_cmd $_4k $_8k" \
344                         -c "$map_cmd -v" $testfile | $filter_cmd
345                 [ $? -ne 0 ] && die_now
346                 _md5_checksum $testfile
347
348                 echo "  9. unwritten -> data"
349                 if [ "$remove_testfile" ]; then
350                         rm -f $testfile
351                 fi
352                 $XFS_IO_PROG -f -c "truncate $_20k" \
353                         -c "$alloc_cmd 0 $_8k" \
354                         -c "pwrite $_8k $_8k" $sync_cmd \
355                         -c "$zero_cmd $_4k $_8k" \
356                         -c "$map_cmd -v" $testfile | $filter_cmd
357                 [ $? -ne 0 ] && die_now
358                 _md5_checksum $testfile
359         fi
360
361         echo "  10. hole -> data -> hole"
362         if [ "$remove_testfile" ]; then
363                 rm -f $testfile
364         fi
365         $XFS_IO_PROG -f -c "truncate $_20k" \
366                 -c "pwrite $_8k $_4k" $sync_cmd \
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         echo "  11. data -> hole -> data"
373         if [ "$remove_testfile" ]; then
374                 rm -f $testfile
375         fi
376         $XFS_IO_PROG -f -c "truncate $_20k" \
377                 -c "$alloc_cmd 0 $_20k" \
378                 -c "pwrite 0 $_8k" \
379                 -c "pwrite $_12k $_8k" $sync_cmd \
380                 -c "$punch_cmd $_8k $_4k" \
381                 -c "$zero_cmd $_4k $_12k" \
382                 -c "$map_cmd -v" $testfile | $filter_cmd
383         [ $? -ne 0 ] && die_now
384         _md5_checksum $testfile
385
386         if [ "$unwritten_tests" ]; then
387                 echo "  12. unwritten -> data -> unwritten"
388                 if [ "$remove_testfile" ]; then
389                         rm -f $testfile
390                 fi
391                 $XFS_IO_PROG -f -c "truncate $_20k" \
392                         -c "$alloc_cmd 0 $_20k" \
393                         -c "pwrite $_8k $_4k" $sync_cmd \
394                         -c "$zero_cmd $_4k $_12k" \
395                         -c "$map_cmd -v" $testfile | $filter_cmd
396                 [ $? -ne 0 ] && die_now
397                 _md5_checksum $testfile
398
399                 echo "  13. data -> unwritten -> data"
400                 if [ "$remove_testfile" ]; then
401                         rm -f $testfile
402                 fi
403                 $XFS_IO_PROG -f -c "truncate $_20k" \
404                         -c "$alloc_cmd 0 $_20k" \
405                         -c "pwrite 0k $_4k" $sync_cmd \
406                         -c "pwrite $_12k $_8k" -c "fsync" \
407                         -c "$zero_cmd $_4k $_12k" \
408                         -c "$map_cmd -v" $testfile | $filter_cmd
409                 [ $? -ne 0 ] && die_now
410                 _md5_checksum $testfile
411         fi
412
413         # Don't need to check EOF case for collapse range.
414         # VFS layer return invalid error in this case,
415         # So it is not a proper case for collapse range test of each local fs.
416         if [ "$zero_cmd" != "fcollapse" ]; then
417                 echo "  14. data -> hole @ EOF"
418                 rm -f $testfile
419                 $XFS_IO_PROG -f -c "truncate $_20k" \
420                         -c "pwrite 0 $_20k" $sync_cmd \
421                         -c "$zero_cmd $_12k $_8k" \
422                         -c "$map_cmd -v" $testfile | $filter_cmd
423                 [ $? -ne 0 ] && die_now
424                 _md5_checksum $testfile
425         fi
426
427         if [ "$zero_cmd" == "fcollapse" ]; then
428                 echo "  14. data -> hole @ 0"
429         else
430                 echo "  15. data -> hole @ 0"
431         fi
432
433         if [ "$remove_testfile" ]; then
434                 rm -f $testfile
435         fi
436         $XFS_IO_PROG -f -c "truncate $_20k" \
437                 -c "pwrite 0 $_20k" $sync_cmd \
438                 -c "$zero_cmd 0 $_8k" \
439                 -c "$map_cmd -v" $testfile | $filter_cmd
440         [ $? -ne 0 ] && die_now
441         _md5_checksum $testfile
442
443         # If zero_cmd is fcollpase, don't check unaligned offsets
444         if [ "$zero_cmd" == "fcollapse" ]; then
445                 return
446         fi
447
448         # If zero_cmd is finsert, don't check unaligned offsets
449         if [ "$zero_cmd" == "finsert" ]; then
450                 return
451         fi
452
453         echo "  16. data -> cache cold ->hole"
454         if [ "$remove_testfile" ]; then
455                 rm -f $testfile
456                 rm -f $testfile.2
457         else
458                 cp $testfile $testfile.2
459         fi
460         $XFS_IO_PROG -f -c "truncate $_20k" \
461                 -c "pwrite $_8k $_12k" -c "fsync" $testfile.2 \
462                 > /dev/null
463         $XFS_IO_PROG -f -c "truncate $_20k" \
464                 -c "pwrite 0 $_20k" $sync_cmd \
465                 -c "$zero_cmd 0k $_8k" \
466                 -c "fadvise -d" \
467                 -c "$map_cmd -v" $testfile | $filter_cmd
468         diff $testfile $testfile.2
469         [ $? -ne 0 ] && die_now
470         rm -f $testfile.2
471         _md5_checksum $testfile
472
473         # different file sizes mean we can't use md5sum to check the hole is
474         # valid. Hence use hexdump to dump the contents and chop off the last
475         # line of output that indicates the file size. We also have to fudge
476         # the extent size as that will change with file size, too - that's what
477         # the sed line noise does - it will always result in an output of [0..7]
478         # so it matches 4k block size...
479         echo "  17. data -> hole in single block file"
480         if [ "$remove_testfile" ]; then
481                 rm -f $testfile
482         fi
483         block_size=`_get_block_size $TEST_DIR`
484         $XFS_IO_PROG -f -c "truncate $block_size" \
485                 -c "pwrite 0 $block_size" $sync_cmd \
486                 -c "$zero_cmd 128 128" \
487                 -c "$map_cmd -v" $testfile | $filter_cmd | \
488                          sed -e "s/\.\.[0-9]*\]/..7\]/"
489         [ $? -ne 0 ] && die_now
490         od -x $testfile | head -n -1
491 }
492
493 _test_block_boundaries()
494 {
495
496         remove_testfile=1
497         sync_cmd="-c fsync"
498         unwritten_tests=1
499         OPTIND=1
500         while getopts 'dk' OPTION
501         do
502                 case $OPTION in
503                 k)      remove_testfile=
504                 ;;
505                 d)      sync_cmd=
506                 ;;
507                 ?)      echo Invalid flag
508                 exit 1
509                 ;;
510                 esac
511         done
512         shift $(($OPTIND - 1))
513
514         bs=$1
515         zero_cmd=$2
516         filter_cmd=$3
517         testfile=$4
518
519         # Block size plus 1
520         bs_p1=$(($bs + 1))
521         # Block size plus 2
522         bs_p2=$(($bs + 2))
523
524         # Block size minus 1
525         bs_m1=$(($bs - 1))
526
527         # Block size multiplied by 2
528         bs_t2=$(($bs * 2))
529
530         # Block size divided by 2
531         bs_d2=$(($bs / 2))
532
533         echo "zero 0, 1"
534         $XFS_IO_PROG -f -t -c "pwrite -S 0x41 0 $bs" \
535                            -c "pwrite -S 0x42 $bs $bs" \
536                            -c "$zero_cmd 0 1" \
537                            -c "pread -v 0 $bs_t2" \
538                            $testfile | $filter_cmd
539
540         echo "zero 0, $bs_m1"
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_m1" \
544                            -c "pread -v 0 $bs_t2" \
545                            $testfile | $filter_cmd
546
547         echo "zero 0, $bs"
548         $XFS_IO_PROG -f -t -c "pwrite -S 0x41 0 $bs" \
549                            -c "pwrite -S 0x42 $bs $bs" \
550                            -c "$zero_cmd 0 $bs" \
551                            -c "pread -v 0 $bs_t2" \
552                            $testfile | $filter_cmd
553
554         echo "zero 0, $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 0 $bs_p1" \
558                            -c "pread -v 0 $bs_t2" \
559                            $testfile | $filter_cmd
560
561         echo "zero $bs_m1, $bs"
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" \
565                            -c "pread -v 0 $bs_t2" \
566                            $testfile | $filter_cmd
567
568         echo "zero $bs_m1, $bs_p1"
569         $XFS_IO_PROG -f -t -c "pwrite -S 0x41 0 $bs" \
570                            -c "pwrite -S 0x42 $bs $bs" \
571                            -c "$zero_cmd $bs_m1 $bs_p1" \
572                            -c "pread -v 0 $bs_t2" \
573                            $testfile | $filter_cmd
574
575         echo "zero $bs_m1, $bs_p2"
576         $XFS_IO_PROG -f -t -c "pwrite -S 0x41 0 $bs" \
577                            -c "pwrite -S 0x42 $bs $bs" \
578                            -c "$zero_cmd $bs_m1 $bs_p2" \
579                            -c "pread -v 0 $bs_t2" \
580                            $testfile | $filter_cmd
581
582
583         echo "zero $bs, $bs"
584         $XFS_IO_PROG -f -t -c "pwrite -S 0x41 0 $bs" \
585                            -c "pwrite -S 0x42 $bs $bs" \
586                            -c "$zero_cmd $bs $bs" \
587                            -c "pread -v 0 $bs_t2" \
588                            $testfile | $filter_cmd
589
590
591         echo "zero $bs_d2 , $bs"
592         $XFS_IO_PROG -f -t -c "pwrite -S 0x41 0 $bs" \
593                            -c "pwrite -S 0x42 $bs $bs" \
594                            -c "$zero_cmd $bs_d2 $bs" \
595                            -c "pread -v 0 $bs_t2" \
596                            $testfile | $filter_cmd
597 }