generic: test MADV_POPULATE_READ with IO errors
[xfstests-dev.git] / common / rc
1 ##/bin/bash
2 # SPDX-License-Identifier: GPL-2.0+
3 # Copyright (c) 2000-2006 Silicon Graphics, Inc.  All Rights Reserved.
4
5 . common/config
6
7 BC="$(type -P bc)" || BC=
8
9 _require_math()
10 {
11         if [ -z "$BC" ]; then
12                 _notrun "this test requires 'bc' tool for doing math operations"
13         fi
14 }
15
16 _math()
17 {
18         [ $# -le 0 ] && return
19         LANG=C echo "scale=0; $@" | "$BC" -q 2> /dev/null
20 }
21
22 dd()
23 {
24         command dd --help 2>&1 | grep noxfer >/dev/null
25         if [ "$?" -eq 0 ]
26             then
27                 command dd status=noxfer $@
28             else
29                 command dd $@
30         fi
31 }
32
33 # Prints the md5 checksum of a given file
34 _md5_checksum()
35 {
36         md5sum $1 | cut -d ' ' -f1
37 }
38
39 # Write a byte into a range of a file
40 _pwrite_byte() {
41         local pattern="$1"
42         local offset="$2"
43         local len="$3"
44         local file="$4"
45         local xfs_io_args="$5"
46
47         $XFS_IO_PROG $xfs_io_args -f -c "pwrite -S $pattern $offset $len" "$file"
48 }
49
50 # mmap-write a byte into a range of a file
51 _mwrite_byte() {
52         local pattern="$1"
53         local offset="$2"
54         local len="$3"
55         local mmap_len="$4"
56         local file="$5"
57
58         $XFS_IO_PROG -f -c "mmap -rw 0 $mmap_len" -c "mwrite -S $pattern $offset $len" "$file"
59 }
60
61 # ls -l w/ selinux sometimes puts a dot at the end:
62 # -rwxrw-r--. id1 id2 file1
63 # Also filter out lost+found directory on extN file system if present
64
65 _ls_l()
66 {
67         ls -l $* | sed "s/\(^[-rwxdlbcpsStT]*\)\. /\1 /" | grep -v 'lost+found'
68 }
69
70 _dump_err()
71 {
72     _err_msg="$*"
73     echo "$_err_msg"
74 }
75
76 _dump_err_cont()
77 {
78     _err_msg="$*"
79     echo -n "$_err_msg"
80 }
81
82 _dump_err2()
83 {
84     _err_msg="$*"
85     >&2 echo "$_err_msg"
86 }
87
88 _log_err()
89 {
90     _err_msg="$*"
91     echo "$_err_msg" | tee -a $seqres.full
92     echo "(see $seqres.full for details)"
93 }
94
95 # make sure we have a standard umask
96 umask 022
97
98 # check for correct setup and source the $FSTYP specific functions now
99 case "$FSTYP" in
100     xfs)
101          [ "$XFS_LOGPRINT_PROG" = "" ] && _fatal "xfs_logprint not found"
102          [ "$XFS_REPAIR_PROG" = "" ] && _fatal "xfs_repair not found"
103          [ "$XFS_DB_PROG" = "" ] && _fatal "xfs_db not found"
104          [ "$MKFS_XFS_PROG" = "" ] && _fatal "mkfs_xfs not found"
105          [ "$XFS_INFO_PROG" = "" ] && _fatal "xfs_info not found"
106
107          . ./common/xfs
108          ;;
109     udf)
110          [ "$MKFS_UDF_PROG" = "" ] && _fatal "mkfs_udf/mkudffs not found"
111          ;;
112     btrfs)
113          [ "$MKFS_BTRFS_PROG" = "" ] && _fatal "mkfs.btrfs not found"
114
115          . ./common/btrfs
116          ;;
117     ext4)
118          [ "$MKFS_EXT4_PROG" = "" ] && _fatal "mkfs.ext4 not found"
119          ;;
120     f2fs)
121          [ "$MKFS_F2FS_PROG" = "" ] && _fatal "mkfs.f2fs not found"
122          ;;
123     nfs)
124          . ./common/nfs
125          ;;
126     cifs)
127          ;;
128     9p)
129          ;;
130     ceph)
131          . ./common/ceph
132          ;;
133     glusterfs)
134          ;;
135     overlay)
136          . ./common/overlay
137          ;;
138     reiser4)
139          [ "$MKFS_REISER4_PROG" = "" ] && _fatal "mkfs.reiser4 not found"
140          ;;
141     pvfs2)
142         ;;
143     ubifs)
144         [ "$UBIUPDATEVOL_PROG" = "" ] && _fatal "ubiupdatevol not found"
145         ;;
146 esac
147
148 if [ ! -z "$REPORT_LIST" ]; then
149         . ./common/report
150         _assert_report_list
151 fi
152
153 _get_filesize()
154 {
155     stat -c %s "$1"
156 }
157
158 # Get hugepagesize in bytes
159 _get_hugepagesize()
160 {
161         local hugepgsz=$(awk '/Hugepagesize/ {print $2}' /proc/meminfo)
162         # Call _notrun if $hugepgsz is not a number
163         echo "$hugepgsz" | egrep -q ^[0-9]+$ || \
164                 _notrun "Cannot get the value of Hugepagesize"
165         echo $((hugepgsz * 1024))
166 }
167
168 _mount()
169 {
170     $MOUNT_PROG `_mount_ops_filter $*`
171 }
172
173 # Call _mount to do mount operation but also save mountpoint to
174 # MOUNTED_POINT_STACK. Note that the mount point must be the last parameter
175 _get_mount()
176 {
177         local mnt_point=${!#}
178         local mnt_dev=${@:(-2):1}
179         local scratch_opts=""
180         if [ "$mnt_dev" = "$SCRATCH_DEV" ]; then
181                 _scratch_options mount
182                 scratch_opts="$SCRATCH_OPTIONS"
183         fi
184
185         _mount $scratch_opts $*
186         if [ $? -eq 0 ]; then
187                 # mount --move operation updates the mountpoint, so remove
188                 # the old one and insert the new one
189                 if [[ "$*" =~ --move|-M ]]; then
190                         MOUNTED_POINT_STACK=`echo $MOUNTED_POINT_STACK | \
191                                                 cut -d\  -f2-`
192                 fi
193                 MOUNTED_POINT_STACK="$mnt_point $MOUNTED_POINT_STACK"
194         else
195                 return 1
196         fi
197 }
198
199 # Unmount the last mounted mountpoint in MOUNTED_POINT_STACK
200 # and return it to caller
201 _put_mount()
202 {
203         local last_mnt=`echo $MOUNTED_POINT_STACK | awk '{print $1}'`
204
205         if [ -n "$last_mnt" ]; then
206                 $UMOUNT_PROG $last_mnt
207         fi
208         MOUNTED_POINT_STACK=`echo $MOUNTED_POINT_STACK | cut -d\  -f2-`
209 }
210
211 # Unmount all mountpoints in MOUNTED_POINT_STACK and clear the stack
212 _clear_mount_stack()
213 {
214         if [ -n "$MOUNTED_POINT_STACK" ]; then
215                 $UMOUNT_PROG $MOUNTED_POINT_STACK
216         fi
217         MOUNTED_POINT_STACK=""
218 }
219
220 _scratch_options()
221 {
222     local type=$1
223     local rt_opt=""
224     local log_opt=""
225     SCRATCH_OPTIONS=""
226
227     if [ "$FSTYP" != "xfs" ]; then
228         return
229     fi
230
231     case $type in
232     mkfs)
233         SCRATCH_OPTIONS="$SCRATCH_OPTIONS -f"
234         rt_opt="-r"
235         log_opt="-l"
236         ;;
237     mount)
238         rt_opt="-o"
239         log_opt="-o"
240         ;;
241     esac
242     [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
243         SCRATCH_OPTIONS="$SCRATCH_OPTIONS ${rt_opt}rtdev=$SCRATCH_RTDEV"
244     [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
245         SCRATCH_OPTIONS="$SCRATCH_OPTIONS ${log_opt}logdev=$SCRATCH_LOGDEV"
246 }
247
248 _test_options()
249 {
250     local type=$1
251     local rt_opt=""
252     local log_opt=""
253     TEST_OPTIONS=""
254
255     if [ "$FSTYP" != "xfs" ]; then
256         return
257     fi
258
259     case $type in
260     mkfs)
261         rt_opt="-r"
262         log_opt="-l"
263         ;;
264     mount)
265         rt_opt="-o"
266         log_opt="-o"
267         ;;
268     esac
269     [ "$USE_EXTERNAL" = yes -a ! -z "$TEST_RTDEV" ] && \
270         TEST_OPTIONS="$TEST_OPTIONS ${rt_opt}rtdev=$TEST_RTDEV"
271     [ "$USE_EXTERNAL" = yes -a ! -z "$TEST_LOGDEV" ] && \
272         TEST_OPTIONS="$TEST_OPTIONS ${log_opt}logdev=$TEST_LOGDEV"
273 }
274
275 _mount_ops_filter()
276 {
277     local params="$*"
278     local last_index=$(( $# - 1 ))
279
280     [ $last_index -gt 0 ] && shift $last_index
281     local fs_escaped=$1
282
283     echo $params | \
284         $PERL_PROG -ne "s#mtpt=[^,|^\n|^\s]*#mtpt=$fs_escaped\1\2#; print;"
285
286 }
287
288 # Used for mounting non-scratch devices (e.g. loop, dm constructs)
289 # with the safe set of scratch mount options (e.g. loop image may be
290 # hosted on $SCRATCH_DEV, so can't use external scratch devices).
291 _common_dev_mount_options()
292 {
293         echo $MOUNT_OPTIONS $SELINUX_MOUNT_OPTIONS $*
294 }
295
296 _scratch_mount_options()
297 {
298         _scratch_options mount
299
300         echo `_common_dev_mount_options $*` $SCRATCH_OPTIONS \
301                                         $SCRATCH_DEV $SCRATCH_MNT
302 }
303
304 _supports_filetype()
305 {
306         local dir=$1
307
308         local fstyp=`$DF_PROG $dir | tail -1 | $AWK_PROG '{print $2}'`
309         case "$fstyp" in
310         xfs)
311                 $XFS_INFO_PROG $dir | grep -q "ftype=1"
312                 ;;
313         ext2|ext3|ext4)
314                 local dev=`$DF_PROG $dir | tail -1 | $AWK_PROG '{print $1}'`
315                 tune2fs -l $dev | grep -q filetype
316                 ;;
317         *)
318                 local testfile=$dir/$$.ftype
319                 touch $testfile
320                 # look for DT_UNKNOWN files
321                 local unknowns=$($here/src/t_dir_type $dir u | wc -l)
322                 rm $testfile
323                 # 0 unknowns is success
324                 return $unknowns
325                 ;;
326         esac
327 }
328
329 # mount scratch device with given options but don't check mount status
330 _try_scratch_mount()
331 {
332         local mount_ret
333
334         if [ "$FSTYP" == "overlay" ]; then
335                 _overlay_scratch_mount $*
336                 return $?
337         fi
338         _mount -t $FSTYP `_scratch_mount_options $*`
339         mount_ret=$?
340         [ $mount_ret -ne 0 ] && return $mount_ret
341         _idmapped_mount $SCRATCH_DEV $SCRATCH_MNT
342 }
343
344 # mount scratch device with given options and _fail if mount fails
345 _scratch_mount()
346 {
347         _try_scratch_mount $* || _fail "mount $(_scratch_mount_options $*) failed"
348 }
349
350 _scratch_mount_idmapped()
351 {
352         local type="$1"
353         local id="$2"
354
355         if [ "$type" = "u" ]; then
356                 # This means root will be able to create files as uid %id in
357                 # the underlying filesystem by going through the idmapped mount.
358                 $here/src/idmapped-mounts/mount-idmapped --map-mount u:0:$id:1 \
359                                                          --map-mount u:$id:0:1 \
360                                                          --map-mount g:0:0:1 \
361                                                          "$SCRATCH_MNT" "$SCRATCH_MNT" || _fail "mount-idmapped failed"
362         elif [ "$type" = "g" ]; then
363                 # This means root will be able to create files as gid %id in
364                 # the underlying filesystem by going through the idmapped mount.
365                 $here/src/idmapped-mounts/mount-idmapped --map-mount g:0:$id:1 \
366                                                          --map-mount g:$id:0:1 \
367                                                          --map-mount u:0:0:1 \
368                                                          "$SCRATCH_MNT" "$SCRATCH_MNT" || _fail "mount-idmapped failed"
369         elif [ "$type" = "b" ]; then
370                 # This means root will be able to create files as uid and gid
371                 # %id in the underlying filesystem by going through the idmapped mount.
372                 $here/src/idmapped-mounts/mount-idmapped --map-mount b:0:$id:1 \
373                                                          --map-mount b:$id:0:1 \
374                                                          "$SCRATCH_MNT" "$SCRATCH_MNT" || _fail "mount-idmapped failed"
375         else
376                 _fail "usage: either \"u\" (uid), \"g\" (gid), or \"b\" (uid and gid) must be specified "
377         fi
378 }
379
380 _scratch_unmount()
381 {
382         case "$FSTYP" in
383         overlay)
384                 _overlay_scratch_unmount
385                 ;;
386         btrfs)
387                 $UMOUNT_PROG $SCRATCH_MNT
388                 ;;
389         *)
390                 $UMOUNT_PROG $SCRATCH_DEV
391                 ;;
392         esac
393 }
394
395 _scratch_umount_idmapped()
396 {
397         $UMOUNT_PROG $SCRATCH_MNT
398 }
399
400 _scratch_remount()
401 {
402     local opts="$1"
403
404     if test -n "$opts"; then
405         _try_scratch_mount "-o remount,$opts"
406     fi
407 }
408
409 _scratch_cycle_mount()
410 {
411     local opts="$1"
412
413     if [ "$FSTYP" = tmpfs ]; then
414         _scratch_remount "$opts"
415         return
416     fi
417     if test -n "$opts"; then
418         opts="-o $opts"
419     fi
420     _scratch_unmount
421     _try_scratch_mount "$opts" || _fail "cycle mount failed"
422 }
423
424 _scratch_shutdown()
425 {
426         if [ $FSTYP = "overlay" ]; then
427                 # In lagacy overlay usage, it may specify directory as
428                 # SCRATCH_DEV, in this case OVL_BASE_SCRATCH_DEV
429                 # will be null, so check OVL_BASE_SCRATCH_DEV before
430                 # running shutdown to avoid shutting down base fs accidently.
431                 if [ -z $OVL_BASE_SCRATCH_DEV ]; then
432                         _fail "_scratch_shutdown: call _require_scratch_shutdown first in test"
433                 else
434                         $here/src/godown $* $OVL_BASE_SCRATCH_MNT
435                 fi
436         else
437                 $here/src/godown $* $SCRATCH_MNT
438         fi
439 }
440
441 # Return a file path that can be used to shut down the scratch filesystem.
442 # Caller should _require_scratch_shutdown before using this.
443 _scratch_shutdown_handle()
444 {
445         if [ $FSTYP = "overlay" ]; then
446                 echo $OVL_BASE_SCRATCH_MNT
447         else
448                 echo $SCRATCH_MNT
449         fi
450 }
451
452 _move_mount()
453 {
454         local mnt=$1
455         local tmp=$2
456
457         # Replace $mnt with $tmp. Use a temporary bind-mount because
458         # mount --move will fail with certain mount propagation layouts.
459         $UMOUNT_PROG $mnt || _fail "Failed to unmount $mnt"
460         _mount --bind $tmp $mnt || _fail "Failed to bind-mount $tmp to $mnt"
461         $UMOUNT_PROG $tmp || _fail "Failed to unmount $tmp"
462         rmdir $tmp
463 }
464
465 _idmapped_mount()
466 {
467         [ "$IDMAPPED_MOUNTS" = "true" ] || return 0
468
469         local dev=$1
470         local mnt=$2
471         local status=0
472         local tmp=`mktemp -d`
473
474         local mount_rec=`findmnt -rncv -S $dev -o OPTIONS`
475         if [[ "$mount_rec" == *"idmapped"* ]]; then
476                 return 0
477         fi
478
479         # We create an idmapped mount where {g,u}id 0 writes to disk as
480         # {g,u}id 10000000 and $(id -u fsgqa) + 10000000. We change ownership
481         # of $mnt so {g,u} id 0 can actually create objects in there.
482         chown 10000000:10000000 $mnt || return 1
483         $here/src/idmapped-mounts/mount-idmapped \
484                 --map-mount b:10000000:0:100000000000 \
485                 $mnt $tmp
486         if [ $? -ne 0 ]; then
487                 rmdir $tmp
488                 return 1
489         fi
490
491         # The next call ensures we don't end up stacking an idmapped mount on
492         # top of the original mount. Instead we fully replace the original
493         # mount with the idmapped mount. This will not just allow a clean mount
494         # layout it also makes unmount and remounting way simpler.
495         _move_mount $mnt $tmp
496         return $?
497 }
498
499 _test_mount()
500 {
501     local mount_ret
502
503     if [ "$FSTYP" == "ceph-fuse" ]; then
504         $CEPH_FUSE_BIN_PATH $TEST_FS_MOUNT_OPTS $TEST_DIR 2> /dev/null
505         return $?
506     elif [ "$FSTYP" == "overlay" ]; then
507         _overlay_test_mount $*
508         return $?
509     fi
510
511     _test_options mount
512     _mount -t $FSTYP $TEST_OPTIONS $TEST_FS_MOUNT_OPTS $SELINUX_MOUNT_OPTIONS $* $TEST_DEV $TEST_DIR
513     mount_ret=$?
514     [ $mount_ret -ne 0 ] && return $mount_ret
515     _idmapped_mount $TEST_DEV $TEST_DIR
516 }
517
518 _test_unmount()
519 {
520         if [ "$FSTYP" == "overlay" ]; then
521                 _overlay_test_unmount
522         else
523                 $UMOUNT_PROG $TEST_DEV
524         fi
525 }
526
527 _test_cycle_mount()
528 {
529     if [ "$FSTYP" = tmpfs ]; then
530         return
531     fi
532     _test_unmount
533     _test_mount
534 }
535
536 _scratch_mkfs_options()
537 {
538     _scratch_options mkfs
539     echo $SCRATCH_OPTIONS $MKFS_OPTIONS $* $SCRATCH_DEV
540 }
541
542 # Do the actual mkfs work on SCRATCH_DEV. Firstly mkfs with both MKFS_OPTIONS
543 # and user specified mkfs options, if that fails (due to conflicts between mkfs
544 # options), do a second mkfs with only user provided mkfs options.
545 #
546 # First param is the mkfs command without any mkfs options and device.
547 # Second param is the filter to remove unnecessary messages from mkfs stderr.
548 # Other extra mkfs options are followed.
549 _scratch_do_mkfs()
550 {
551         local mkfs_cmd=$1
552         local mkfs_filter=$2
553         shift 2
554         local extra_mkfs_options=$*
555         local mkfs_status
556         local tmp=`mktemp -u`
557
558         # save mkfs output in case conflict means we need to run again.
559         # only the output for the mkfs that applies should be shown
560         eval "$mkfs_cmd $MKFS_OPTIONS $extra_mkfs_options $SCRATCH_DEV" \
561                 2>$tmp.mkfserr 1>$tmp.mkfsstd
562         mkfs_status=$?
563
564         # a mkfs failure may be caused by conflicts between $MKFS_OPTIONS and
565         # $extra_mkfs_options
566         if [ $mkfs_status -ne 0 -a -n "$extra_mkfs_options" ]; then
567                 (
568                 echo -n "** mkfs failed with extra mkfs options "
569                 echo "added to \"$MKFS_OPTIONS\" by test $seq **"
570                 echo -n "** attempting to mkfs using only test $seq "
571                 echo "options: $extra_mkfs_options **"
572                 ) >> $seqres.full
573
574                 # running mkfs again. overwrite previous mkfs output files
575                 eval "$mkfs_cmd $extra_mkfs_options $SCRATCH_DEV" \
576                         2>$tmp.mkfserr 1>$tmp.mkfsstd
577                 mkfs_status=$?
578         fi
579
580         # output stored mkfs output, filtering unnecessary output from stderr
581         cat $tmp.mkfsstd
582         eval "cat $tmp.mkfserr | $mkfs_filter" >&2
583
584         rm -f $tmp.mkfserr $tmp.mkfsstd
585         return $mkfs_status
586 }
587
588 _setup_large_ext4_fs()
589 {
590         local fs_size=$1
591         local tmp_dir=/tmp/
592
593         [ "$LARGE_SCRATCH_DEV" != yes ] && return 0
594         [ -z "$SCRATCH_DEV_EMPTY_SPACE" ] && SCRATCH_DEV_EMPTY_SPACE=0
595         [ $SCRATCH_DEV_EMPTY_SPACE -ge $fs_size ] && return 0
596
597         # Default free space in the FS is 50GB, but you can specify more via
598         # SCRATCH_DEV_EMPTY_SPACE
599         local space_to_consume=$(($fs_size - 50*1024*1024*1024 - $SCRATCH_DEV_EMPTY_SPACE))
600
601         # mount the filesystem and create 16TB - 4KB files until we consume
602         # all the necessary space.
603         _try_scratch_mount 2>&1 >$tmp_dir/mnt.err
604         local status=$?
605         if [ $status -ne 0 ]; then
606                 echo "mount failed"
607                 cat $tmp_dir/mnt.err >&2
608                 rm -f $tmp_dir/mnt.err
609                 return $status
610         fi
611         rm -f $tmp_dir/mnt.err
612
613         local file_size=$((16*1024*1024*1024*1024 - 4096))
614         local nfiles=0
615         while [ $space_to_consume -gt $file_size ]; do
616
617                 xfs_io -F -f \
618                         -c "truncate $file_size" \
619                         -c "falloc -k 0 $file_size" \
620                         $SCRATCH_MNT/.use_space.$nfiles 2>&1
621                 status=$?
622                 if [ $status -ne 0 ]; then
623                         break;
624                 fi
625
626                 space_to_consume=$(( $space_to_consume - $file_size ))
627                 nfiles=$(($nfiles + 1))
628         done
629
630         # consume the remaining space.
631         if [ $space_to_consume -gt 0 ]; then
632                 xfs_io -F -f \
633                         -c "truncate $space_to_consume" \
634                         -c "falloc -k 0 $space_to_consume" \
635                         $SCRATCH_MNT/.use_space.$nfiles 2>&1
636                 status=$?
637         fi
638         export NUM_SPACE_FILES=$nfiles
639
640         _scratch_unmount
641         if [ $status -ne 0 ]; then
642                 echo "large file prealloc failed"
643                 cat $tmp_dir/mnt.err >&2
644                 return $status
645         fi
646         return 0
647 }
648
649 _scratch_mkfs_ext4()
650 {
651         local mkfs_cmd="$MKFS_EXT4_PROG -F"
652         local mkfs_filter="grep -v -e ^Warning: -e \"^mke2fs \" | grep -v \"^$\""
653         local tmp=`mktemp -u`
654         local mkfs_status
655
656         [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
657             $mkfs_cmd -O journal_dev $MKFS_OPTIONS $SCRATCH_LOGDEV && \
658             mkfs_cmd="$mkfs_cmd -J device=$SCRATCH_LOGDEV"
659
660         _scratch_do_mkfs "$mkfs_cmd" "$mkfs_filter" $* 2>$tmp.mkfserr 1>$tmp.mkfsstd
661         mkfs_status=$?
662
663         if [ $mkfs_status -eq 0 -a "$LARGE_SCRATCH_DEV" = yes ]; then
664                 # manually parse the mkfs output to get the fs size in bytes
665                 local fs_size=`cat $tmp.mkfsstd | awk ' \
666                         /^Block size/ { split($2, a, "="); bs = a[2] ; } \
667                         / inodes, / { blks = $3 } \
668                         /reserved for the super user/ { resv = $1 } \
669                         END { fssize = bs * blks - resv; print fssize }'`
670
671                 _setup_large_ext4_fs $fs_size
672                 mkfs_status=$?
673         fi
674
675         # output mkfs stdout and stderr
676         cat $tmp.mkfsstd
677         cat $tmp.mkfserr >&2
678         rm -f $tmp.mkfserr $tmp.mkfsstd
679
680         return $mkfs_status
681 }
682
683 _ext4_metadump()
684 {
685         local device="$1"
686         local dumpfile="$2"
687         local compressopt="$3"
688
689         test -n "$E2IMAGE_PROG" || _fail "e2image not installed"
690         $E2IMAGE_PROG -Q "$device" "$dumpfile"
691         [ "$compressopt" = "compress" ] && [ -n "$DUMP_COMPRESSOR" ] &&
692                 $DUMP_COMPRESSOR -f "$dumpfile" &>> "$seqres.full"
693 }
694
695 # Capture the metadata of a filesystem in a dump file for offline analysis.
696 # This is not supported by all filesystem types, so this function should only
697 # be used after a test has already failed.
698 _metadump_dev() {
699         local device="$1"
700         local dumpfile="$2"
701         local compressopt="$3"
702
703         test "$DUMP_CORRUPT_FS" = 1 || return 0
704
705         case "$FSTYP" in
706         btrfs)
707                 _btrfs_metadump $device $dumpfile
708                 ;;
709         ext*)
710                 _ext4_metadump $device $dumpfile $compressopt
711                 ;;
712         xfs)
713                 _xfs_metadump $dumpfile $device none $compressopt
714                 ;;
715         *)
716                 echo "Don't know how to metadump $FSTYP"
717                 return 1
718                 ;;
719         esac
720 }
721
722 _test_mkfs()
723 {
724     case $FSTYP in
725     nfs*)
726         # do nothing for nfs
727         ;;
728     cifs)
729         # do nothing for cifs
730         ;;
731     9p)
732         # do nothing for 9p
733         ;;
734     virtiofs)
735         # do nothing for virtiofs
736         ;;
737     ceph)
738         # do nothing for ceph
739         ;;
740     glusterfs)
741         # do nothing for glusterfs
742         ;;
743     overlay)
744         # do nothing for overlay
745         ;;
746     pvfs2)
747         # do nothing for pvfs2
748         ;;
749     udf)
750         $MKFS_UDF_PROG $MKFS_OPTIONS $* $TEST_DEV > /dev/null
751         ;;
752     btrfs)
753         $MKFS_BTRFS_PROG $MKFS_OPTIONS $* $TEST_DEV > /dev/null
754         ;;
755     ext2|ext3|ext4)
756         $MKFS_PROG -t $FSTYP -- -F $MKFS_OPTIONS $* $TEST_DEV
757         ;;
758     *)
759         yes | $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS $* $TEST_DEV
760         ;;
761     esac
762 }
763
764 _mkfs_dev()
765 {
766     local tmp=`mktemp -u`
767     case $FSTYP in
768     nfs*)
769         # do nothing for nfs
770         ;;
771     9p)
772         # do nothing for 9p
773         ;;
774     virtiofs)
775         # do nothing for virtiofs
776         ;;
777     overlay)
778         # do nothing for overlay
779         ;;
780     pvfs2)
781         # do nothing for pvfs2
782         ;;
783     udf)
784         $MKFS_UDF_PROG $MKFS_OPTIONS $* 2>$tmp.mkfserr 1>$tmp.mkfsstd
785         ;;
786     btrfs)
787         $MKFS_BTRFS_PROG $MKFS_OPTIONS $* 2>$tmp.mkfserr 1>$tmp.mkfsstd
788         ;;
789     ext2|ext3|ext4)
790         $MKFS_PROG -t $FSTYP -- -F $MKFS_OPTIONS $* \
791                 2>$tmp.mkfserr 1>$tmp.mkfsstd
792         ;;
793     xfs)
794         $MKFS_PROG -t $FSTYP -- -f $MKFS_OPTIONS $* \
795                 2>$tmp.mkfserr 1>$tmp.mkfsstd
796         ;;
797     *)
798         yes | $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS $* \
799                 2>$tmp.mkfserr 1>$tmp.mkfsstd
800         ;;
801     esac
802
803     if [ $? -ne 0 ]; then
804         # output stored mkfs output
805         cat $tmp.mkfserr >&2
806         cat $tmp.mkfsstd
807         status=1
808         exit 1
809     fi
810     rm -f $tmp.mkfserr $tmp.mkfsstd
811 }
812
813 # remove all files in $SCRATCH_MNT, useful when testing on NFS/CIFS
814 _scratch_cleanup_files()
815 {
816         case $FSTYP in
817         overlay)
818                 # Avoid rm -rf /* if we messed up
819                 [ -n "$OVL_BASE_SCRATCH_MNT" ] || return 1
820                 _overlay_base_scratch_mount || return 1
821                 rm -rf $OVL_BASE_SCRATCH_MNT/* || return 1
822                 _overlay_mkdirs $OVL_BASE_SCRATCH_MNT
823                 # leave base fs mouted so tests can setup lower/upper dir files
824                 ;;
825         *)
826                 [ -n "$SCRATCH_MNT" ] || return 1
827                 _scratch_mount
828                 rm -rf $SCRATCH_MNT/*
829                 _scratch_unmount
830                 ;;
831         esac
832 }
833
834 _scratch_mkfs()
835 {
836         local mkfs_cmd=""
837         local mkfs_filter=""
838         local mkfs_status
839
840         case $FSTYP in
841         nfs*|cifs|ceph|overlay|glusterfs|pvfs2|9p|virtiofs)
842                 # unable to re-create this fstyp, just remove all files in
843                 # $SCRATCH_MNT to avoid EEXIST caused by the leftover files
844                 # created in previous runs
845                 _scratch_cleanup_files
846                 return $?
847                 ;;
848         tmpfs)
849                 # do nothing for tmpfs
850                 return 0
851                 ;;
852         ubifs)
853                 # erase the UBI volume; reformated automatically on next mount
854                 $UBIUPDATEVOL_PROG ${SCRATCH_DEV} -t
855                 return 0
856                 ;;
857         ext4)
858                 _scratch_mkfs_ext4 $*
859                 return $?
860                 ;;
861         xfs)
862                 _scratch_mkfs_xfs $*
863                 return $?
864                 ;;
865         udf)
866                 mkfs_cmd="$MKFS_UDF_PROG"
867                 mkfs_filter="cat"
868                 ;;
869         btrfs)
870                 mkfs_cmd="$MKFS_BTRFS_PROG"
871                 mkfs_filter="cat"
872                 ;;
873         ext3)
874                 mkfs_cmd="$MKFS_PROG -t $FSTYP -- -F"
875                 mkfs_filter="grep -v -e ^Warning: -e \"^mke2fs \""
876
877                 # put journal on separate device?
878                 [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
879                 $mkfs_cmd -O journal_dev $MKFS_OPTIONS $SCRATCH_LOGDEV && \
880                 mkfs_cmd="$mkfs_cmd -J device=$SCRATCH_LOGDEV"
881                 ;;
882         ext2)
883                 mkfs_cmd="$MKFS_PROG -t $FSTYP -- -F"
884                 mkfs_filter="grep -v -e ^Warning: -e \"^mke2fs \""
885                 ;;
886         f2fs)
887                 mkfs_cmd="$MKFS_F2FS_PROG"
888                 mkfs_filter="cat"
889                 ;;
890         ocfs2)
891                 mkfs_cmd="yes | $MKFS_PROG -t $FSTYP --"
892                 mkfs_filter="grep -v -e ^mkfs\.ocfs2"
893                 ;;
894         *)
895                 mkfs_cmd="yes | $MKFS_PROG -t $FSTYP --"
896                 mkfs_filter="cat"
897                 ;;
898         esac
899
900         _scratch_do_mkfs "$mkfs_cmd" "$mkfs_filter" $*
901         return $?
902 }
903
904 # Helper function to get a spare or replace-target device from
905 # configured SCRATCH_DEV_POLL, must call _scratch_dev_pool_get()
906 # before _spare_dev_get(). Replace-target-device/Spare-device will
907 # be assigned to SPARE_DEV.
908 # As of now only one replace-target-device/spare-device can be
909 # assigned.
910 #
911 # Usage:
912 #  _scratch_dev_pool_get() <ndevs>
913 #     _spare_dev_get()
914 #     :: do stuff
915 #     _spare_dev_put()
916 #  _scratch_dev_pool_put()
917 #
918 _spare_dev_get()
919 {
920         typeset -p SCRATCH_DEV_POOL_SAVED >/dev/null 2>&1
921         if [ $? -ne 0 ]; then
922                 _fail "Bug: unset val, must call _scratch_dev_pool_get before _spare_dev_get"
923         fi
924
925         if [ -z "$SCRATCH_DEV_POOL_SAVED" ]; then
926                 _fail "Bug: str empty, must call _scratch_dev_pool_get before _spare_dev_get"
927         fi
928
929         # Check if the spare is already assigned
930         typeset -p SPARE_DEV >/dev/null 2>&1
931         if [ $? -eq 0 ]; then
932                 if [ ! -z "$SPARE_DEV" ]; then
933                         _fail "Bug: SPARE_DEV = $SPARE_DEV already assigned"
934                 fi
935         fi
936
937         local ndevs=`echo $SCRATCH_DEV_POOL| wc -w`
938         local config_ndevs=`echo $SCRATCH_DEV_POOL_SAVED| wc -w`
939
940         if [ $ndevs -eq $config_ndevs ]; then
941                 _notrun "All devs used no spare"
942         fi
943         # Get a dev that is not used
944         local -a devs="( $SCRATCH_DEV_POOL_SAVED )"
945         SPARE_DEV=${devs[@]:$ndevs:1}
946         export SPARE_DEV
947 }
948
949 _spare_dev_put()
950 {
951         typeset -p SPARE_DEV >/dev/null 2>&1
952         if [ $? -ne 0 ]; then
953                 _fail "Bug: unset val, must call _spare_dev_get before its put"
954         fi
955
956         if [ -z "$SPARE_DEV" ]; then
957                 _fail "Bug: str empty, must call _spare_dev_get before its put"
958         fi
959
960         export SPARE_DEV=""
961 }
962
963 #
964 # Generally test cases will have..
965 #   _require_scratch_dev_pool X
966 # to make sure it has the enough scratch devices including
967 # replace-target and spare device. Now arg1 here is the
968 # required number of scratch devices by a-test-case excluding
969 # the replace-target and spare device. So this function will
970 # set SCRATCH_DEV_POOL to the specified number of devices.
971 #
972 # Usage:
973 #  _scratch_dev_pool_get() <ndevs>
974 #     :: do stuff
975 #
976 #  _scratch_dev_pool_put()
977 #
978 _scratch_dev_pool_get()
979 {
980         if [ $# -ne 1 ]; then
981                 _fail "Usage: _scratch_dev_pool_get ndevs"
982         fi
983
984         typeset -p SCRATCH_DEV_POOL >/dev/null 2>&1
985         if [ $? -ne 0 ]; then
986                 _fail "Bug: cant find SCRATCH_DEV_POOL ndevs"
987         fi
988
989         local test_ndevs=$1
990         local config_ndevs=`echo $SCRATCH_DEV_POOL| wc -w`
991         local -a devs="( $SCRATCH_DEV_POOL )"
992
993         if [ $config_ndevs -lt $test_ndevs ]; then
994                 _notrun "Need at least test requested number of ndevs $test_ndevs"
995         fi
996
997         SCRATCH_DEV_POOL_SAVED=${SCRATCH_DEV_POOL}
998         export SCRATCH_DEV_POOL_SAVED
999         SCRATCH_DEV_POOL=${devs[@]:0:$test_ndevs}
1000         export SCRATCH_DEV_POOL
1001 }
1002
1003 _scratch_dev_pool_put()
1004 {
1005         typeset -p SCRATCH_DEV_POOL_SAVED >/dev/null 2>&1
1006         if [ $? -ne 0 ]; then
1007                 _fail "Bug: unset val, must call _scratch_dev_pool_get before _scratch_dev_pool_put"
1008         fi
1009
1010         if [ -z "$SCRATCH_DEV_POOL_SAVED" ]; then
1011                 _fail "Bug: str empty, must call _scratch_dev_pool_get before _scratch_dev_pool_put"
1012         fi
1013
1014         export SCRATCH_DEV_POOL=$SCRATCH_DEV_POOL_SAVED
1015         export SCRATCH_DEV_POOL_SAVED=""
1016 }
1017
1018 _scratch_pool_mkfs()
1019 {
1020     case $FSTYP in
1021     btrfs)
1022         $MKFS_BTRFS_PROG $MKFS_OPTIONS $* $SCRATCH_DEV_POOL > /dev/null
1023         ;;
1024     *)
1025         echo "_scratch_pool_mkfs is not implemented for $FSTYP" 1>&2
1026         ;;
1027     esac
1028 }
1029
1030 # Return the amount of free memory available on the system
1031 _free_memory_bytes()
1032 {
1033     free -b | grep ^Mem | awk '{print $4}'
1034 }
1035
1036 _available_memory_bytes()
1037 {
1038         nf=`free -b | grep ^Mem | awk '{print NF}'`
1039         if [[ nf -lt 7 ]]; then
1040                 # Doesn't have available field. Fallback.
1041                 _free_memory_bytes
1042         else
1043                 free -b | grep ^Mem | awk '{print $7}'
1044         fi
1045 }
1046
1047 _check_minimal_fs_size()
1048 {
1049         local fssize=$1
1050
1051         if [ -n "$MIN_FSSIZE" ]; then
1052                 [ $MIN_FSSIZE -gt "$fssize" ] &&
1053                         _notrun "specified filesystem size is too small"
1054         fi
1055 }
1056
1057 # Create fs of certain size on scratch device
1058 # _scratch_mkfs_sized <size in bytes> [optional blocksize]
1059 _scratch_mkfs_sized()
1060 {
1061         local fssize=$1
1062         local blocksize=$2
1063         local def_blksz
1064
1065         case $FSTYP in
1066         xfs)
1067                 def_blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*-b ?size= ?+([0-9]+).*/\1/p'`
1068                 ;;
1069         btrfs)
1070                 def_blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*-s ?+([0-9]+).*/\1/p'`
1071                 ;;
1072         ext2|ext3|ext4|ext4dev|udf|reiser4|ocfs2|reiserfs)
1073                 def_blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*-b ?+([0-9]+).*/\1/p'`
1074                 ;;
1075         jfs)
1076                 def_blksz=4096
1077                 ;;
1078         esac
1079
1080         [ -n "$def_blksz" ] && blocksize=$def_blksz
1081         [ -z "$blocksize" ] && blocksize=4096
1082
1083         local re='^[0-9]+$'
1084         if ! [[ $fssize =~ $re ]] ; then
1085                 _notrun "error: _scratch_mkfs_sized: fs size \"$fssize\" not an integer."
1086         fi
1087         if ! [[ $blocksize =~ $re ]] ; then
1088                 _notrun "error: _scratch_mkfs_sized: block size \"$blocksize\" not an integer."
1089         fi
1090
1091         local blocks=`expr $fssize / $blocksize`
1092
1093         _check_minimal_fs_size $fssize
1094
1095         if [ -b "$SCRATCH_DEV" ]; then
1096                 local devsize=`blockdev --getsize64 $SCRATCH_DEV`
1097                 [ "$fssize" -gt "$devsize" ] && _notrun "Scratch device too small"
1098         fi
1099
1100         if [ "$FSTYP" = "xfs" ] && [ -b "$SCRATCH_RTDEV" ]; then
1101                 local rtdevsize=`blockdev --getsize64 $SCRATCH_RTDEV`
1102                 [ "$fssize" -gt "$rtdevsize" ] && _notrun "Scratch rt device too small"
1103                 rt_ops="-r size=$fssize"
1104         fi
1105
1106         case $FSTYP in
1107         xfs)
1108                 # don't override MKFS_OPTIONS that set a block size.
1109                 echo $MKFS_OPTIONS |egrep -q "b?size="
1110                 if [ $? -eq 0 ]; then
1111                         _scratch_mkfs_xfs -d size=$fssize $rt_ops
1112                 else
1113                         _scratch_mkfs_xfs -d size=$fssize $rt_ops -b size=$blocksize
1114                 fi
1115                 ;;
1116         ext2|ext3|ext4|ext4dev)
1117                 ${MKFS_PROG} -t $FSTYP -F $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
1118                 ;;
1119         gfs2)
1120                 # mkfs.gfs2 doesn't automatically shrink journal files on small
1121                 # filesystems, so the journal files may end up being bigger than the
1122                 # filesystem, which will cause mkfs.gfs2 to fail.  Until that's fixed,
1123                 # shrink the journal size to at most one eigth of the filesystem and at
1124                 # least 8 MiB, the minimum size allowed.
1125                 local min_journal_size=8
1126                 local default_journal_size=128
1127                 if (( fssize/8 / (1024*1024) < default_journal_size )); then
1128                         local journal_size=$(( fssize/8 / (1024*1024) ))
1129                         (( journal_size >= min_journal_size )) || journal_size=$min_journal_size
1130                         MKFS_OPTIONS="-J $journal_size $MKFS_OPTIONS"
1131                 fi
1132                 ${MKFS_PROG} -t $FSTYP $MKFS_OPTIONS -O -b $blocksize $SCRATCH_DEV $blocks
1133                 ;;
1134         ocfs2)
1135                 yes | ${MKFS_PROG} -t $FSTYP -F $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
1136                 ;;
1137         udf)
1138                 $MKFS_UDF_PROG $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
1139                 ;;
1140         btrfs)
1141                 local mixed_opt=
1142                 # Mixed option is required when the filesystem size is small and
1143                 # the device is not zoned. Ref: btrfs-progs: btrfs_min_dev_size()
1144                 (( fssize < $((256 * 1024 * 1024)) )) &&
1145                         ! _scratch_btrfs_is_zoned && mixed_opt='--mixed'
1146                 $MKFS_BTRFS_PROG $MKFS_OPTIONS $mixed_opt -b $fssize $SCRATCH_DEV
1147                 ;;
1148         jfs)
1149                 ${MKFS_PROG} -t $FSTYP $MKFS_OPTIONS $SCRATCH_DEV $blocks
1150                 ;;
1151         reiserfs)
1152                 ${MKFS_PROG} -t $FSTYP $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
1153                 ;;
1154         reiser4)
1155                 # mkfs.resier4 requires size in KB as input for creating filesystem
1156                 $MKFS_REISER4_PROG $MKFS_OPTIONS -y -b $blocksize $SCRATCH_DEV \
1157                                    `expr $fssize / 1024`
1158                 ;;
1159         f2fs)
1160                 # mkfs.f2fs requires # of sectors as an input for the size
1161                 local sector_size=`blockdev --getss $SCRATCH_DEV`
1162                 $MKFS_F2FS_PROG $MKFS_OPTIONS $SCRATCH_DEV `expr $fssize / $sector_size`
1163                 ;;
1164         tmpfs)
1165                 local free_mem=`_free_memory_bytes`
1166                 if [ "$free_mem" -lt "$fssize" ] ; then
1167                    _notrun "Not enough memory ($free_mem) for tmpfs with $fssize bytes"
1168                 fi
1169                 export MOUNT_OPTIONS="-o size=$fssize $TMPFS_MOUNT_OPTIONS"
1170                 ;;
1171         bcachefs)
1172                 $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS --fs_size=$fssize --block_size=$blocksize $SCRATCH_DEV
1173                 ;;
1174         *)
1175                 _notrun "Filesystem $FSTYP not supported in _scratch_mkfs_sized"
1176                 ;;
1177         esac
1178 }
1179
1180 # Emulate an N-data-disk stripe w/ various stripe units
1181 # _scratch_mkfs_geom <sunit bytes> <swidth multiplier> [optional blocksize]
1182 _scratch_mkfs_geom()
1183 {
1184     local sunit_bytes=$1
1185     local swidth_mult=$2
1186     local blocksize=$3
1187     [ -z "$blocksize" ] && blocksize=4096
1188
1189     local sunit_blocks=$(( sunit_bytes / blocksize ))
1190     local swidth_blocks=$(( sunit_blocks * swidth_mult ))
1191
1192     case $FSTYP in
1193     xfs)
1194         if echo "$MKFS_OPTIONS" | egrep -q "b?size="; then
1195                 MKFS_OPTIONS=$(echo "$MKFS_OPTIONS" | sed -r "s/(b?size=)[0-9]+k?/\1$blocksize/")
1196         else
1197                 MKFS_OPTIONS+=" -b size=$blocksize"
1198         fi
1199
1200         if echo "$MKFS_OPTIONS" | egrep -q "(su|sunit|sw|swidth)="; then
1201                 MKFS_OPTIONS=$(echo "$MKFS_OPTIONS" | sed -r \
1202                         -e "s/(su|sunit)=[0-9kmg]+/su=$sunit_bytes/" \
1203                         -e "s/(sw|swidth)=[0-9kmg]+/sw=$swidth_mult/")
1204         else
1205                 MKFS_OPTIONS+=" -d su=$sunit_bytes,sw=$swidth_mult"
1206         fi
1207         ;;
1208     ext4|ext4dev)
1209         MKFS_OPTIONS+=" -b $blocksize -E stride=$sunit_blocks,stripe_width=$swidth_blocks"
1210         ;;
1211     *)
1212         _notrun "can't mkfs $FSTYP with geometry"
1213         ;;
1214     esac
1215     _scratch_mkfs
1216 }
1217
1218 # Create fs of certain blocksize on scratch device
1219 # _scratch_mkfs_blocksized blocksize
1220 _scratch_mkfs_blocksized()
1221 {
1222         local blocksize=$1
1223
1224         local re='^[0-9]+$'
1225         if ! [[ $blocksize =~ $re ]] ; then
1226                 _notrun "error: _scratch_mkfs_sized: block size \"$blocksize\" not an integer."
1227         fi
1228
1229         case $FSTYP in
1230         btrfs)
1231                 test -f /sys/fs/btrfs/features/supported_sectorsizes || \
1232                 _notrun "Subpage sectorsize support is not found in $FSTYP"
1233
1234                 grep -wq $blocksize /sys/fs/btrfs/features/supported_sectorsizes || \
1235                 _notrun "$FSTYP does not support sectorsize=$blocksize yet"
1236
1237                 _scratch_mkfs --sectorsize=$blocksize
1238                 ;;
1239         xfs)
1240                 _scratch_mkfs_xfs $MKFS_OPTIONS -b size=$blocksize
1241                 ;;
1242         ext2|ext3|ext4)
1243                 ${MKFS_PROG} -t $FSTYP -F $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV
1244                 ;;
1245         gfs2)
1246                 ${MKFS_PROG} -t $FSTYP $MKFS_OPTIONS -O -b $blocksize $SCRATCH_DEV
1247                 ;;
1248         ocfs2)
1249                 yes | ${MKFS_PROG} -t $FSTYP -F $MKFS_OPTIONS -b $blocksize \
1250                                                 -C $blocksize $SCRATCH_DEV
1251                 ;;
1252         bcachefs)
1253                 ${MKFS_PROG} -t $FSTYP $MKFS_OPTIONS --block_size=$blocksize \
1254                                                                 $SCRATCH_DEV
1255                 ;;
1256         *)
1257                 _notrun "Filesystem $FSTYP not supported in _scratch_mkfs_blocksized"
1258                 ;;
1259         esac
1260 }
1261
1262 _scratch_resvblks()
1263 {
1264         case $FSTYP in
1265         xfs)
1266                 xfs_io -x -c "resblks $1" $SCRATCH_MNT
1267                 ;;
1268         *)
1269                 ;;
1270         esac
1271 }
1272
1273
1274 # Repair scratch filesystem.  Returns 0 if the FS is good to go (either no
1275 # errors found or errors were fixed) and nonzero otherwise; also spits out
1276 # a complaint on stderr if fsck didn't tell us that the FS is good to go.
1277 _repair_scratch_fs()
1278 {
1279     case $FSTYP in
1280     xfs)
1281         _scratch_xfs_repair "$@" 2>&1
1282         local res=$?
1283         if [ "$res" -ne 0 ]; then
1284                 echo "xfs_repair returns $res; replay log?"
1285                 _try_scratch_mount
1286                 res=$?
1287                 if [ "$res" -gt 0 ]; then
1288                         echo "mount returns $res; zap log?"
1289                         _scratch_xfs_repair -L 2>&1
1290                         echo "log zap returns $?"
1291                 else
1292                         umount "$SCRATCH_MNT"
1293                 fi
1294                 _scratch_xfs_repair "$@" 2>&1
1295                 res=$?
1296         fi
1297         if [ $res -ne 0 ]; then
1298                 _dump_err2 "xfs_repair failed, err=$res"
1299         fi
1300         return $res
1301         ;;
1302     bcachefs)
1303         # With bcachefs, if fsck detects any errors we consider it a bug and we
1304         # want the test to fail:
1305         _check_scratch_fs
1306         ;;
1307     *)
1308         local dev=$SCRATCH_DEV
1309         local fstyp=$FSTYP
1310         if [ $FSTYP = "overlay" -a -n "$OVL_BASE_SCRATCH_DEV" ]; then
1311                 _repair_overlay_scratch_fs
1312                 # Fall through to repair base fs
1313                 dev=$OVL_BASE_SCRATCH_DEV
1314                 fstyp=$OVL_BASE_FSTYP
1315                 $UMOUNT_PROG $OVL_BASE_SCRATCH_MNT
1316         fi
1317         # Let's hope fsck -y suffices...
1318         fsck -t $fstyp -y $dev 2>&1
1319         local res=$?
1320         case $res in
1321         $FSCK_OK|$FSCK_NONDESTRUCT|$FSCK_REBOOT)
1322                 res=0
1323                 ;;
1324         *)
1325                 _dump_err2 "fsck.$FSTYP failed, err=$res"
1326                 ;;
1327         esac
1328         return $res
1329         ;;
1330     esac
1331 }
1332
1333 _get_pids_by_name()
1334 {
1335     if [ $# -ne 1 ]
1336     then
1337         echo "Usage: _get_pids_by_name process-name" 1>&2
1338         exit 1
1339     fi
1340
1341     # Algorithm ... all ps(1) variants have a time of the form MM:SS or
1342     # HH:MM:SS before the psargs field, use this as the search anchor.
1343     #
1344     # Matches with $1 (process-name) occur if the first psarg is $1
1345     # or ends in /$1 ... the matching uses sed's regular expressions,
1346     # so passing a regex into $1 will work.
1347
1348     ps $PS_ALL_FLAGS \
1349     | sed -n \
1350         -e 's/$/ /' \
1351         -e 's/[         ][      ]*/ /g' \
1352         -e 's/^ //' \
1353         -e 's/^[^ ]* //' \
1354         -e "/[0-9]:[0-9][0-9]  *[^ ]*\/$1 /s/ .*//p" \
1355         -e "/[0-9]:[0-9][0-9]  *$1 /s/ .*//p"
1356 }
1357
1358 #
1359 # _df_device : get an IRIX style df line for a given device
1360 #
1361 #       - returns "" if not mounted
1362 #       - returns fs type in field two (ala IRIX)
1363 #       - joins line together if split by fancy df formatting
1364 #       - strips header etc
1365 #
1366
1367 _df_device()
1368 {
1369     if [ $# -ne 1 ]
1370     then
1371         echo "Usage: _df_device device" 1>&2
1372         exit 1
1373     fi
1374
1375     # Note that we use "==" here so awk doesn't try to interpret an NFS over
1376     # IPv6 server as a regular expression.
1377     $DF_PROG 2>/dev/null | $AWK_PROG -v what=$1 '
1378         ($1==what) && (NF==1) {
1379             v=$1
1380             getline
1381             print v, $0
1382             exit
1383         }
1384         ($1==what) {
1385             print
1386             exit
1387         }
1388     '
1389 }
1390
1391 #
1392 # _df_dir : get an IRIX style df line for device where a directory resides
1393 #
1394 #       - returns fs type in field two (ala IRIX)
1395 #       - joins line together if split by fancy df formatting
1396 #       - strips header etc
1397 #
1398
1399 _df_dir()
1400 {
1401     if [ $# -ne 1 ]
1402     then
1403         echo "Usage: _df_dir device" 1>&2
1404         exit 1
1405     fi
1406
1407     $DF_PROG $1 2>/dev/null | $AWK_PROG -v what=$1 '
1408         NR == 2 && NF==1 {
1409             v=$1
1410             getline
1411             print v, $0;
1412             exit 0
1413         }
1414         NR == 2 {
1415             print;
1416             exit 0
1417         }
1418         {}
1419     '
1420     # otherwise, nada
1421 }
1422
1423 # return percentage used disk space for mounted device
1424
1425 _used()
1426 {
1427     if [ $# -ne 1 ]
1428     then
1429         echo "Usage: _used device" 1>&2
1430         exit 1
1431     fi
1432
1433     _df_device $1 | $AWK_PROG '{ sub("%", "") ; print $6 }'
1434 }
1435
1436 # return the FS type of a mounted device
1437 #
1438 _fs_type()
1439 {
1440     if [ $# -ne 1 ]
1441     then
1442         echo "Usage: _fs_type device" 1>&2
1443         exit 1
1444     fi
1445
1446     #
1447     # The Linux kernel shows NFSv4 filesystems in df output as
1448     # filesystem type nfs4, although we mounted it as nfs earlier.
1449     # Fix the filesystem type up here so that the callers don't
1450     # have to bother with this quirk.
1451     #
1452     _df_device $1 | $AWK_PROG '{ print $2 }' | \
1453         sed -e 's/nfs4/nfs/' -e 's/fuse.glusterfs/glusterfs/' \
1454             -e 's/fuse.ceph-fuse/ceph-fuse/'
1455 }
1456
1457 # return the FS mount options of a mounted device
1458 #
1459 # should write a version which just parses the output of mount for IRIX
1460 # compatibility, but since this isn't used at all, at the moment I'll leave
1461 # this for now
1462 #
1463 _fs_options()
1464 {
1465     if [ $# -ne 1 ]
1466     then
1467         echo "Usage: _fs_options device" 1>&2
1468         exit 1
1469     fi
1470
1471     $AWK_PROG -v dev=$1 '
1472         match($1,dev) { print $4 }
1473     ' </proc/mounts
1474 }
1475
1476 # returns device number if a file is a block device
1477 #
1478 _is_block_dev()
1479 {
1480     if [ $# -ne 1 ]
1481     then
1482         echo "Usage: _is_block_dev dev" 1>&2
1483         exit 1
1484     fi
1485
1486     local dev=$1
1487     if [ -L "$dev" ]; then
1488         dev=`readlink -f "$dev"`
1489     fi
1490
1491     if [ -b "$dev" ]; then
1492         $here/src/lstat64 "$dev" | $AWK_PROG '/Device type:/ { print $9 }'
1493     fi
1494 }
1495
1496 # returns device number if a file is a character device
1497 #
1498 _is_char_dev()
1499 {
1500         if [ $# -ne 1 ]; then
1501                 echo "Usage: _is_char_dev dev" 1>&2
1502                 exit 1
1503         fi
1504
1505         local dev=$1
1506         if [ -L "$dev" ]; then
1507                 dev=`readlink -f "$dev"`
1508         fi
1509
1510         if [ -c "$dev" ]; then
1511                 $here/src/lstat64 "$dev" | $AWK_PROG '/Device type:/ { print $9 }'
1512         fi
1513 }
1514
1515 # Do a command, log it to $seqres.full, optionally test return status
1516 # and die if command fails. If called with one argument _do executes the
1517 # command, logs it, and returns its exit status. With two arguments _do
1518 # first prints the message passed in the first argument, and then "done"
1519 # or "fail" depending on the return status of the command passed in the
1520 # second argument. If the command fails and the variable _do_die_on_error
1521 # is set to "always" or the two argument form is used and _do_die_on_error
1522 # is set to "message_only" _do will print an error message to
1523 # $seqres.out and exit.
1524
1525 _do()
1526 {
1527     if [ $# -eq 1 ]; then
1528         local cmd=$1
1529     elif [ $# -eq 2 ]; then
1530         local note=$1
1531         local cmd=$2
1532         echo -n "$note... "
1533     else
1534         echo "Usage: _do [note] cmd" 1>&2
1535         status=1; exit
1536     fi
1537
1538     (eval "echo '---' \"$cmd\"") >>$seqres.full
1539     (eval "$cmd") >$tmp._out 2>&1
1540     local ret=$?
1541     cat $tmp._out >>$seqres.full
1542     rm -f $tmp._out
1543     if [ $# -eq 2 ]; then
1544         if [ $ret -eq 0 ]; then
1545             echo "done"
1546         else
1547             echo "fail"
1548         fi
1549     fi
1550     if [ $ret -ne 0  ] \
1551         && [ "$_do_die_on_error" = "always" \
1552             -o \( $# -eq 2 -a "$_do_die_on_error" = "message_only" \) ]
1553     then
1554         [ $# -ne 2 ] && echo
1555         eval "echo \"$cmd\" failed \(returned $ret\): see $seqres.full"
1556         status=1; exit
1557     fi
1558
1559     return $ret
1560 }
1561
1562 # bail out, setting up .notrun file. Need to kill the filesystem check files
1563 # here, otherwise they are set incorrectly for the next test.
1564 #
1565 _notrun()
1566 {
1567     echo "$*" > $seqres.notrun
1568     echo "$seq not run: $*"
1569     rm -f ${RESULT_DIR}/require_test*
1570     rm -f ${RESULT_DIR}/require_scratch*
1571
1572     status=0
1573     exit
1574 }
1575
1576 # just plain bail out
1577 #
1578 _fail()
1579 {
1580     echo "$*" | tee -a $seqres.full
1581     echo "(see $seqres.full for details)"
1582     status=1
1583     exit 1
1584 }
1585
1586 # tests whether $FSTYP is one of the supported filesystems for a test
1587 #
1588 _check_supported_fs()
1589 {
1590         local res=1
1591         local f
1592
1593         for f; do
1594                 # ^FS means black listed fs
1595                 if [ "$f" = "^$FSTYP" ]; then
1596                         return 1
1597                 elif [ "$f" = "generic" ] || [[ "$f" == "^"* ]]; then
1598                         # ^FS implies "generic ^FS"
1599                         res=0
1600                 elif [ "$f" = "$FSTYP" ]; then
1601                         return 0
1602                 fi
1603         done
1604         return $res
1605 }
1606
1607 _supported_fs()
1608 {
1609         _check_supported_fs $* || \
1610                 _notrun "not suitable for this filesystem type: $FSTYP"
1611 }
1612
1613 _fixed_in_version()
1614 {
1615         local pkg=$1
1616         local ver=$2
1617
1618         echo "HINT: You _MAY_ be hit by a known issue on $pkg version < $ver." >> $seqres.hints
1619         echo >> $seqres.hints
1620 }
1621
1622 _fixed_in_kernel_version()
1623 {
1624         _fixed_in_version kernel $*
1625 }
1626
1627 _fixed_by_git_commit()
1628 {
1629         local pkg=$1
1630         shift
1631
1632         echo "HINT: You _MAY_ be missing $pkg fix:" >> $seqres.hints
1633         echo "      $*" >> $seqres.hints
1634         echo >> $seqres.hints
1635 }
1636
1637 _fixed_by_kernel_commit()
1638 {
1639         _fixed_by_git_commit kernel $*
1640 }
1641
1642 _check_if_dev_already_mounted()
1643 {
1644         local dev=$1
1645         local mnt=$2
1646
1647         # find $dev as the source, and print result in "$dev $mnt" format
1648         local mount_rec=`findmnt -rncv -S $dev -o SOURCE,TARGET`
1649         [ -n "$mount_rec" ] || return 1 # 1 = not mounted
1650
1651         # if it's mounted, make sure its on $mnt
1652         if [ "$mount_rec" != "$dev $mnt" ]; then
1653                 echo "$devname=$dev is mounted but not on $mntname=$mnt - aborting"
1654                 echo "Already mounted result:"
1655                 echo $mount_rec
1656                 return 2 # 2 = mounted on wrong mnt
1657         fi
1658 }
1659
1660 # check if a FS on a device is mounted
1661 # if so, verify that it is mounted on mount point
1662 # if fstype is given as argument, verify that it is also
1663 # mounted with correct fs type
1664 #
1665 _check_mounted_on()
1666 {
1667         local devname=$1
1668         local dev=$2
1669         local mntname=$3
1670         local mnt=$4
1671         local type=$5
1672
1673         # this check doesn't work on ceph-fuse
1674         if [ "$dev" != "ceph-fuse" ]; then
1675                 _check_if_dev_already_mounted $dev $mnt
1676                 dev_already_mounted=$?
1677
1678                 if [ $dev_already_mounted -ne 0 ]; then
1679                         return $dev_already_mounted
1680                 fi
1681         fi
1682
1683         if [ -n "$type" -a "`_fs_type $dev`" != "$type" ]; then
1684                 echo "$devname=$dev is mounted but not a type $type filesystem"
1685                 # raw $DF_PROG cannot handle NFS/CIFS/overlay correctly
1686                 _df_device $dev
1687                 return 3 # 3 = mounted as wrong type
1688         fi
1689         return 0 # 0 = mounted as expected
1690 }
1691
1692 # this test needs a scratch partition - check we're ok & unmount it
1693 # No post-test check of the device is required. e.g. the test intentionally
1694 # finishes the test with the filesystem in a corrupt state
1695 _require_scratch_nocheck()
1696 {
1697     case "$FSTYP" in
1698         glusterfs)
1699                 echo $SCRATCH_DEV | egrep -q ":/?" > /dev/null 2>&1
1700                 if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
1701                         _notrun "this test requires a valid \$SCRATCH_DEV"
1702                 fi
1703                 if [ ! -d "$SCRATCH_MNT" ]; then
1704                         _notrun "this test requires a valid \$SCRATCH_MNT"
1705                 fi
1706                 ;;
1707         9p|virtiofs)
1708                 if [ -z "$SCRATCH_DEV" ]; then
1709                         _notrun "this test requires a valid \$SCRATCH_DEV"
1710                 fi
1711                 if [ ! -d "$SCRATCH_MNT" ]; then
1712                         _notrun "this test requires a valid \$SCRATCH_MNT"
1713                 fi
1714                 ;;
1715         nfs*)
1716                 echo $SCRATCH_DEV | grep -q ":/" > /dev/null 2>&1
1717                 if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
1718                         _notrun "this test requires a valid \$SCRATCH_DEV"
1719                 fi
1720                 if [ ! -d "$SCRATCH_MNT" ]; then
1721                         _notrun "this test requires a valid \$SCRATCH_MNT"
1722                 fi
1723                 ;;
1724         ceph)
1725                 echo $SCRATCH_DEV | grep -qE "=/|:/" > /dev/null 2>&1
1726                 if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
1727                         _notrun "this test requires a valid \$SCRATCH_DEV"
1728                 fi
1729                 if [ ! -d "$SCRATCH_MNT" ]; then
1730                         _notrun "this test requires a valid \$SCRATCH_MNT"
1731                 fi
1732                 ;;
1733         pvfs2)
1734                 echo $SCRATCH_DEV | grep -q "://" > /dev/null 2>&1
1735                 if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
1736                         _notrun "this test requires a valid \$SCRATCH_DEV"
1737                 fi
1738                 if [ ! -d "$SCRATCH_MNT" ]; then
1739                         _notrun "this test requires a valid \$SCRATCH_MNT"
1740                 fi
1741                 ;;
1742         cifs)
1743                 echo $SCRATCH_DEV | grep -q "//" > /dev/null 2>&1
1744                 if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
1745                         _notrun "this test requires a valid \$SCRATCH_DEV"
1746                 fi
1747                 if [ ! -d "$SCRATCH_MNT" ]; then
1748                      _notrun "this test requires a valid \$SCRATCH_MNT"
1749                 fi
1750                 ;;
1751         overlay)
1752                 if [ -z "$OVL_BASE_SCRATCH_MNT" -o ! -d "$OVL_BASE_SCRATCH_MNT" ]; then
1753                         _notrun "this test requires a valid \$OVL_BASE_SCRATCH_MNT as ovl base dir"
1754                 fi
1755                 # if $SCRATCH_MNT is derived from $OVL_BASE_SCRATCH_MNT then
1756                 # don't check $SCRATCH_MNT dir here because base fs may not be mounted
1757                 # and we will create the mount point anyway on _overlay_mount
1758                 if [ "$SCRATCH_MNT" != "$OVL_BASE_SCRATCH_MNT/$OVL_MNT" -a ! -d "$SCRATCH_MNT" ]; then
1759                         _notrun "this test requires a valid \$SCRATCH_MNT"
1760                 fi
1761                 ;;
1762         tmpfs)
1763                 if [ -z "$SCRATCH_DEV" -o ! -d "$SCRATCH_MNT" ];
1764                 then
1765                     _notrun "this test requires a valid \$SCRATCH_MNT and unique \$SCRATCH_DEV"
1766                 fi
1767                 ;;
1768         ubifs)
1769                 # ubifs needs an UBI volume. This will be a char device, not a block device.
1770                 if [ ! -c "$SCRATCH_DEV" ]; then
1771                         _notrun "this test requires a valid UBI volume for \$SCRATCH_DEV"
1772                 fi
1773                 if [ ! -d "$SCRATCH_MNT" ]; then
1774                         _notrun "this test requires a valid \$SCRATCH_MNT"
1775                 fi
1776                 ;;
1777         *)
1778                  if [ -z "$SCRATCH_DEV" -o "`_is_block_dev "$SCRATCH_DEV"`" = "" ]
1779                  then
1780                      _notrun "this test requires a valid \$SCRATCH_DEV"
1781                  fi
1782                  if [ "`_is_block_dev "$SCRATCH_DEV"`" = "`_is_block_dev "$TEST_DEV"`" ]
1783                  then
1784                      _notrun "this test requires a valid \$SCRATCH_DEV"
1785                  fi
1786                 if [ ! -d "$SCRATCH_MNT" ]
1787                 then
1788                      _notrun "this test requires a valid \$SCRATCH_MNT"
1789                 fi
1790                  ;;
1791     esac
1792
1793     _check_mounted_on SCRATCH_DEV $SCRATCH_DEV SCRATCH_MNT $SCRATCH_MNT
1794     local err=$?
1795     [ $err -le 1 ] || exit 1
1796     if [ $err -eq 0 ]
1797     then
1798         # if it's mounted, unmount it
1799         if ! _scratch_unmount
1800         then
1801             echo "failed to unmount $SCRATCH_DEV"
1802             exit 1
1803         fi
1804     fi
1805     rm -f ${RESULT_DIR}/require_scratch
1806 }
1807
1808 # we need the scratch device and it needs to not be an lvm device
1809 _require_scratch_nolvm()
1810 {
1811         _require_scratch_nocheck
1812
1813         # This works if we don't have LVM, all we want is to skip if the scratch
1814         # device is an lvm device.
1815         $LVM_PROG lvdisplay $SCRATCH_DEV > /dev/null 2>&1
1816         [ $? -eq 0 ] && _notrun "test requires a non-lvm scratch device"
1817 }
1818
1819 _require_no_compress()
1820 {
1821         case "$FSTYP" in
1822         btrfs)
1823                 _require_btrfs_no_compress
1824                 ;;
1825         *)
1826                 ;;
1827         esac
1828 }
1829
1830 # we need the scratch device and it should be checked post test.
1831 _require_scratch()
1832 {
1833         _require_scratch_nocheck
1834         touch ${RESULT_DIR}/require_scratch
1835 }
1836
1837 # require a scratch dev of a minimum size (in kb)
1838 _require_scratch_size()
1839 {
1840         [ $# -eq 1 ] || _fail "_require_scratch_size: expected size param"
1841
1842         _require_scratch
1843         local devsize=`_get_device_size $SCRATCH_DEV`
1844         [ $devsize -lt $1 ] && _notrun "scratch dev too small"
1845 }
1846
1847 # require a scratch dev of a minimum size (in kb) and should not be checked
1848 # post test
1849 _require_scratch_size_nocheck()
1850 {
1851         [ $# -eq 1 ] || _fail "_require_scratch_size: expected size param"
1852
1853         _require_scratch_nocheck
1854         local devsize=`_get_device_size $SCRATCH_DEV`
1855         [ $devsize -lt $1 ] && _notrun "scratch dev too small"
1856 }
1857
1858 # Require scratch fs which supports >16T of filesystem size.
1859 # _require_scratch must be called before this function is called.
1860 _require_scratch_16T_support()
1861 {
1862         case $FSTYP in
1863         ext2|ext3|f2fs)
1864                 _notrun "$FSTYP doesn't support >16T filesystem"
1865                 ;;
1866         ext4)
1867                 _scratch_mkfs >> $seqres.full 2>&1
1868                 _scratch_mount
1869                 local blocksize=$(_get_block_size $SCRATCH_MNT)
1870                 if [ $blocksize -lt 4096 ]; then
1871                         _notrun "This test requires >16T fs support"
1872                 fi
1873                 _scratch_unmount
1874                 ;;
1875         *)
1876                 ;;
1877         esac
1878 }
1879
1880 # Require scratch fs supports delay allocation.
1881 _require_scratch_delalloc()
1882 {
1883         _require_command "$FILEFRAG_PROG" filefrag
1884
1885         _scratch_mkfs > $seqres.full
1886         _scratch_mount
1887         $XFS_IO_PROG -f -c 'pwrite 0 64k' $SCRATCH_MNT/testy &> /dev/null
1888         $FILEFRAG_PROG -v $SCRATCH_MNT/testy 2>&1 | grep -q delalloc || \
1889                 _notrun "test requires delayed allocation buffered writes"
1890         _scratch_unmount
1891 }
1892
1893 # this test needs a test partition - check we're ok & mount it
1894 #
1895 _require_test()
1896 {
1897     case "$FSTYP" in
1898         glusterfs)
1899                 echo $TEST_DEV | egrep -q ":/?" > /dev/null 2>&1
1900                 if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
1901                         _notrun "this test requires a valid \$TEST_DEV"
1902                 fi
1903                 if [ ! -d "$TEST_DIR" ]; then
1904                         _notrun "this test requires a valid \$TEST_DIR"
1905                 fi
1906                 ;;
1907         9p|virtiofs)
1908                 if [ -z "$TEST_DEV" ]; then
1909                         _notrun "this test requires a valid \$TEST_DEV"
1910                 fi
1911                 if [ ! -d "$TEST_DIR" ]; then
1912                         _notrun "this test requires a valid \$TEST_DIR"
1913                 fi
1914                 ;;
1915         nfs*)
1916                 echo $TEST_DEV | grep -q ":/" > /dev/null 2>&1
1917                 if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
1918                         _notrun "this test requires a valid \$TEST_DEV"
1919                 fi
1920                 if [ ! -d "$TEST_DIR" ]; then
1921                         _notrun "this test requires a valid \$TEST_DIR"
1922                 fi
1923                 ;;
1924         ceph)
1925                 echo $TEST_DEV | grep -qE "=/|:/" > /dev/null 2>&1
1926                 if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
1927                         _notrun "this test requires a valid \$TEST_DEV"
1928                 fi
1929                 if [ ! -d "$TEST_DIR" ]; then
1930                         _notrun "this test requires a valid \$TEST_DIR"
1931                 fi
1932                 ;;
1933         ceph-fuse)
1934                 ;;
1935         cifs)
1936                 echo $TEST_DEV | grep -q "//" > /dev/null 2>&1
1937                 if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
1938                         _notrun "this test requires a valid \$TEST_DEV"
1939                 fi
1940                 if [ ! -d "$TEST_DIR" ]; then
1941                      _notrun "this test requires a valid \$TEST_DIR"
1942                 fi
1943                 ;;
1944         pvfs2)
1945                 echo $TEST_DEV | grep -q "://" > /dev/null 2>&1
1946                 if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
1947                         _notrun "this test requires a valid \$TEST_DIR"
1948                 fi
1949                 if [ ! -d "$TEST_DIR" ]; then
1950                         _notrun "this test requires a valid \$TEST_DIR"
1951                 fi
1952                 ;;
1953         overlay)
1954                 if [ -z "$OVL_BASE_TEST_DIR" -o ! -d "$OVL_BASE_TEST_DIR" ]; then
1955                         _notrun "this test requires a valid \$TEST_DIR as ovl base dir"
1956                 fi
1957                 if [ ! -d "$TEST_DIR" ]; then
1958                         _notrun "this test requires a valid \$TEST_DIR"
1959                 fi
1960                 ;;
1961         tmpfs)
1962                 if [ -z "$TEST_DEV" -o ! -d "$TEST_DIR" ];
1963                 then
1964                     _notrun "this test requires a valid \$TEST_DIR and unique $TEST_DEV"
1965                 fi
1966                 ;;
1967         ubifs)
1968                 # ubifs needs an UBI volume. This will be a char device, not a block device.
1969                 if [ ! -c "$TEST_DEV" ]; then
1970                         _notrun "this test requires a valid UBI volume for \$TEST_DEV"
1971                 fi
1972                 if [ ! -d "$TEST_DIR" ]; then
1973                         _notrun "this test requires a valid \$TEST_DIR"
1974                 fi
1975                 ;;
1976         *)
1977                  if [ -z "$TEST_DEV" ] || [ "`_is_block_dev "$TEST_DEV"`" = "" ]
1978                  then
1979                      _notrun "this test requires a valid \$TEST_DEV"
1980                  fi
1981                  if [ "`_is_block_dev "$SCRATCH_DEV"`" = "`_is_block_dev "$TEST_DEV"`" ]
1982                  then
1983                      _notrun "this test requires a valid \$TEST_DEV"
1984                  fi
1985                 if [ ! -d "$TEST_DIR" ]
1986                 then
1987                      _notrun "this test requires a valid \$TEST_DIR"
1988                 fi
1989                  ;;
1990     esac
1991
1992     _check_mounted_on TEST_DEV $TEST_DEV TEST_DIR $TEST_DIR
1993     local err=$?
1994     [ $err -le 1 ] || exit 1
1995     if [ $err -ne 0 ]
1996     then
1997         if ! _test_mount
1998         then
1999                 echo "!!! failed to mount $TEST_DEV on $TEST_DIR"
2000                 exit 1
2001         fi
2002     fi
2003     touch ${RESULT_DIR}/require_test
2004 }
2005
2006 _has_logdev()
2007 {
2008         local ret=0
2009         [ -z "$SCRATCH_LOGDEV" -o ! -b "$SCRATCH_LOGDEV" ] && ret=1
2010         [ "$USE_EXTERNAL" != yes ] && ret=1
2011
2012         return $ret
2013 }
2014
2015 # this test needs a logdev
2016 #
2017 _require_logdev()
2018 {
2019     [ -z "$SCRATCH_LOGDEV" -o ! -b "$SCRATCH_LOGDEV" ] && \
2020         _notrun "This test requires a valid \$SCRATCH_LOGDEV"
2021     [ "$USE_EXTERNAL" != yes ] && \
2022         _notrun "This test requires USE_EXTERNAL to be enabled"
2023
2024     # ensure its not mounted
2025     $UMOUNT_PROG $SCRATCH_LOGDEV 2>/dev/null
2026 }
2027
2028 # this test requires loopback device support
2029 #
2030 _require_loop()
2031 {
2032     modprobe loop >/dev/null 2>&1
2033     if grep loop /proc/devices >/dev/null 2>&1
2034     then
2035         :
2036     else
2037         _notrun "This test requires loopback device support"
2038     fi
2039
2040     # loop device does not handle zone information
2041     _require_non_zoned_device ${TEST_DEV}
2042 }
2043
2044 # this test requires kernel support for a secondary filesystem
2045 #
2046 _require_extra_fs()
2047 {
2048         modprobe "$1" >/dev/null 2>&1
2049         grep -q -w "$1" /proc/filesystems ||
2050                 _notrun "this test requires $1 support"
2051 }
2052
2053 # this test requires that (large) loopback device files are not in use
2054 #
2055 _require_no_large_scratch_dev()
2056 {
2057     [ "$LARGE_SCRATCH_DEV" = yes ] && \
2058         _notrun "Large filesystem testing in progress, skipped this test"
2059 }
2060
2061 # this test requires that a realtime subvolume is in use, and
2062 # that the kernel supports realtime as well.
2063 #
2064 _require_realtime()
2065 {
2066     [ "$USE_EXTERNAL" = yes ] || \
2067         _notrun "External volumes not in use, skipped this test"
2068     [ "$SCRATCH_RTDEV" = "" ] && \
2069         _notrun "Realtime device required, skipped this test"
2070 }
2071
2072 # This test requires that a realtime subvolume is not in use
2073 #
2074 _require_no_realtime()
2075 {
2076         [ "$USE_EXTERNAL" = "yes" ] && [ -n "$SCRATCH_RTDEV" ] && \
2077                 _notrun "Test not compatible with realtime subvolumes, skipped this test"
2078 }
2079
2080 # this test requires that a specified command (executable) exists
2081 # $1 - command, $2 - name for error message
2082 #
2083 # Note: the command string might have parameters, so strip them before checking
2084 # whether it is executable.
2085 _require_command()
2086 {
2087         if [ $# -eq 2 ]; then
2088                 local name="$2"
2089         elif [ $# -eq 1 ]; then
2090                 local name="$1"
2091         else
2092                 _fail "usage: _require_command <command> [<name>]"
2093         fi
2094
2095         local command=`echo "$1" | awk '{ print $1 }'`
2096         if [ ! -x "$command" ]; then
2097                 _notrun "$name utility required, skipped this test"
2098         fi
2099 }
2100
2101 # this test requires the device to be valid block device
2102 # $1 - device
2103 _require_block_device()
2104 {
2105         if [ -z "$1" ]; then
2106                 echo "Usage: _require_block_device <dev>" 1>&2
2107                 exit 1
2108         fi
2109         if [ "`_is_block_dev "$1"`" == "" ]; then
2110                 _notrun "require $1 to be valid block disk"
2111         fi
2112 }
2113
2114 # this test requires a path to refere to a local block or character device
2115 # $1 - device
2116 _require_local_device()
2117 {
2118         if [ -z "$1" ]; then
2119                 echo "Usage: _require_local_device <dev>" 1>&2
2120                 exit 1
2121         fi
2122         if [ "`_is_block_dev "$1"`" != "" ]; then
2123                 return 0
2124         fi
2125         if [ "`_is_char_dev "$1"`" != "" ]; then
2126                 return 0
2127         fi
2128         _notrun "require $1 to be local device"
2129 }
2130
2131 # brd based ram disks erase the device when they receive a flush command when no
2132 # active references are present. This causes problems for DM devices sitting on
2133 # top of brd devices as DM doesn't hold active references to the brd device.
2134 _require_sane_bdev_flush()
2135 {
2136         echo $1 | grep -q "^/dev/ram[0-9]\+$"
2137         if [ $? -eq 0 ]; then
2138                 _notrun "This test requires a sane block device flush"
2139         fi
2140 }
2141
2142 # Decide if the scratch filesystem is likely to be mounted in fsdax mode.
2143 # It goes 3 ways based on mount options::
2144 #       1. "dax" or "dax=always" means always test using DAX
2145 #       2. "dax=never" means we'll never use DAX
2146 #       3. "dax=inode" or nothing means "use scratch dev capability" to
2147 #           determine whether DAX is going to be used.
2148 #
2149 # Returns 0 if DAX will be used, 1 if DAX is not going to be used.
2150 __scratch_uses_fsdax()
2151 {
2152         local ops=$(_normalize_mount_options "$MOUNT_OPTIONS")
2153
2154         echo $ops | egrep -qw "dax(=always| |$)" && return 0
2155         echo $ops | grep -qw "dax=never" && return 1
2156
2157         local sysfs="/sys/block/$(_short_dev $SCRATCH_DEV)"
2158         test -e "${sysfs}/dax" && return 0
2159         test "$(cat "${sysfs}/queue/dax" 2>/dev/null)" = "1" && return 0
2160         return 1
2161 }
2162
2163 # this test requires a specific device mapper target
2164 _require_dm_target()
2165 {
2166         local target=$1
2167         local fsdax
2168         local bdevdax
2169
2170         # require SCRATCH_DEV to be a valid block device with sane BLKFLSBUF
2171         # behaviour
2172         _require_block_device $SCRATCH_DEV
2173         _require_sane_bdev_flush $SCRATCH_DEV
2174         _require_command "$DMSETUP_PROG" dmsetup
2175
2176         if __scratch_uses_fsdax; then
2177                 case $target in
2178                 stripe|linear|log-writes)
2179                         ;;
2180                 *)
2181                         _notrun "Cannot run tests with DAX on $target devices."
2182                         ;;
2183                 esac
2184         fi
2185
2186         modprobe dm-$target >/dev/null 2>&1
2187
2188         $DMSETUP_PROG targets 2>&1 | grep -q ^$target
2189         if [ $? -ne 0 ]; then
2190                 _notrun "This test requires dm $target support"
2191         fi
2192
2193         # dm-error cannot handle the zone information
2194         #
2195         # dm-snapshot and dm-thin-pool cannot ensure sequential writes on
2196         # the backing device
2197         case $target in
2198         error|snapshot|thin-pool)
2199                 _require_non_zoned_device ${SCRATCH_DEV}
2200                 ;;
2201         esac
2202 }
2203
2204 _zone_type()
2205 {
2206         local target=$1
2207         if [ -z $target ]; then
2208                 echo "Usage: _zone_type <device>"
2209                 exit 1
2210         fi
2211         local sdev=`_short_dev $target`
2212
2213         if [ -e /sys/block/${sdev}/queue/zoned ]; then
2214                 cat /sys/block/${sdev}/queue/zoned
2215         else
2216                 echo none
2217         fi
2218 }
2219
2220 _require_zoned_device()
2221 {
2222         local target=$1
2223         if [ -z $target ]; then
2224                 echo "Usage: _require_zoned_device <device>"
2225                 exit 1
2226         fi
2227
2228         local type=`_zone_type ${target}`
2229         if [ "${type}" = "none" ]; then
2230                 _notrun "this test require zoned block device"
2231         fi
2232 }
2233
2234 _require_non_zoned_device()
2235 {
2236         local target=$1
2237         if [ -z $target ]; then
2238                 echo "Usage: _require_non_zoned_device <device>"
2239                 exit 1
2240         fi
2241
2242         local type=`_zone_type ${target}`
2243         if [ "${type}" != "none" ]; then
2244                 _notrun "this test require non-zoned block device"
2245         fi
2246 }
2247
2248 # this test requires the ext4 kernel support crc feature on scratch device
2249 #
2250 _require_scratch_ext4_crc()
2251 {
2252         _scratch_mkfs_ext4 >/dev/null 2>&1
2253         dumpe2fs -h $SCRATCH_DEV 2> /dev/null | grep -q metadata_csum || _notrun "metadata_csum not supported by this filesystem"
2254         _try_scratch_mount >/dev/null 2>&1 \
2255            || _notrun "Kernel doesn't support metadata_csum feature"
2256         _scratch_unmount
2257 }
2258
2259 # Check whether the specified feature whether it is supported by
2260 # mkfs.ext4 and the kernel.
2261 _require_scratch_ext4_feature()
2262 {
2263     if [ -z "$1" ]; then
2264         echo "Usage: _require_scratch_ext4_feature feature"
2265         exit 1
2266     fi
2267     $MKFS_EXT4_PROG -F $MKFS_OPTIONS -O "$1" \
2268                     $SCRATCH_DEV 512m >/dev/null 2>&1 \
2269         || _notrun "mkfs.ext4 doesn't support $1 feature"
2270     _try_scratch_mount >/dev/null 2>&1 \
2271         || _notrun "Kernel doesn't support the ext4 feature(s): $1"
2272     _scratch_unmount
2273 }
2274
2275 # this test requires that external log/realtime devices are not in use
2276 #
2277 _require_nonexternal()
2278 {
2279     [ "$USE_EXTERNAL" = yes ] && \
2280         _notrun "External device testing in progress, skipped this test"
2281 }
2282
2283 # this test requires that the kernel supports asynchronous I/O
2284 _require_aio()
2285 {
2286         $here/src/feature -A
2287         case $? in
2288         0)
2289                 ;;
2290         1)
2291                 _notrun "kernel does not support asynchronous I/O"
2292                 ;;
2293         *)
2294                 _fail "unexpected error testing for asynchronous I/O support"
2295                 ;;
2296         esac
2297 }
2298
2299 # this test requires that a (specified) aio-dio executable exists
2300 # and that the kernel supports asynchronous I/O.
2301 # $1 - command (optional)
2302 #
2303 _require_aiodio()
2304 {
2305     if [ -z "$1" ]
2306     then
2307         AIO_TEST=$here/src/aio-dio-regress/aiodio_sparse2
2308         [ -x $AIO_TEST ] || _notrun "aio-dio utilities required"
2309     else
2310         AIO_TEST=$here/src/aio-dio-regress/$1
2311         [ -x $AIO_TEST ] || _notrun "$AIO_TEST not built"
2312     fi
2313     _require_aio
2314     _require_odirect
2315 }
2316
2317 # this test requires that the kernel supports IO_URING
2318 _require_io_uring()
2319 {
2320         $here/src/feature -R
2321         case $? in
2322         0)
2323                 ;;
2324         1)
2325                 _notrun "kernel does not support IO_URING"
2326                 ;;
2327         *)
2328                 _fail "unexpected error testing for IO_URING support"
2329                 ;;
2330         esac
2331 }
2332
2333 # test whether the mount_setattr syscall is available
2334 _require_mount_setattr()
2335 {
2336         $here/src/feature -r
2337         case $? in
2338         0)
2339                 ;;
2340         1)
2341                 _notrun "kernel does not support mount_setattr syscall"
2342                 ;;
2343         *)
2344                 _fail "unexpected error testing for mount_setattr support"
2345                 ;;
2346         esac
2347 }
2348
2349 # test whether idmapped mounts are supported
2350 _require_idmapped_mounts()
2351 {
2352         IDMAPPED_MOUNTS_TEST=$here/src/idmapped-mounts/idmapped-mounts
2353         [ -x $IDMAPPED_MOUNTS_TEST ] || _notrun "idmapped-mounts utilities required"
2354
2355         _require_mount_setattr
2356
2357         $here/src/idmapped-mounts/idmapped-mounts --supported \
2358                 --device "$TEST_DEV" \
2359                 --mount "$TEST_DIR" \
2360                 --fstype "$FSTYP"
2361
2362         if [ $? -ne 0 ]; then
2363                 _notrun "idmapped-mounts not support by $FSTYP"
2364         fi
2365 }
2366
2367 # this test requires that a test program exists under src/
2368 # $1 - command (require)
2369 #
2370 _require_test_program()
2371 {
2372     local prog=$here/src/$1
2373     [ -x $prog ] || _notrun "$prog not built"
2374 }
2375
2376 # run an aio-dio program
2377 # $1 - command
2378 _run_aiodio()
2379 {
2380     if [ -z "$1" ]
2381     then
2382         echo "usage: _run_aiodio command_name" 2>&1
2383         status=1; exit 1
2384     fi
2385
2386     _require_aiodio $1
2387
2388     local testtemp=$TEST_DIR/aio-testfile
2389     rm -f $testtemp
2390     $AIO_TEST $testtemp 2>&1
2391     status=$?
2392     rm -f $testtemp
2393
2394     return $status
2395 }
2396
2397 _require_timestamp_range()
2398 {
2399         local device=${1:-$TEST_DEV}
2400
2401         local tsmin tsmax
2402         read tsmin tsmax <<<$(_filesystem_timestamp_range $device)
2403         if [ $tsmin -eq -1 -a $tsmax -eq -1 ]; then
2404                 _notrun "filesystem $FSTYP timestamp bounds are unknown"
2405         fi
2406
2407         # expect console warning from rw scratch mount if fs limit is near
2408         if [ $tsmax -le $((1<<31)) ] && \
2409                 ! _check_dmesg_for "filesystem being mounted at .* supports timestamps until"
2410         then
2411                 _notrun "Kernel does not support timestamp limits"
2412         fi
2413 }
2414
2415 _filesystem_timestamp_range()
2416 {
2417         local device=${1:-$TEST_DEV}
2418         local fstyp=${2:-$FSTYP}
2419         u32max=$(((1<<32)-1))
2420         s32min=-$((1<<31))
2421         s32max=$(((1<<31)-1))
2422         s64max=$(((1<<63)-1))
2423         s64min=$((1<<63))
2424
2425         case $fstyp in
2426         ext2)
2427                 echo "$s32min $s32max"
2428                 ;;
2429         ext3|ext4)
2430                 if [ $(dumpe2fs -h $device 2>/dev/null | grep "Inode size:" | cut -d: -f2) -gt 128 ]; then
2431                         printf "%d %d\n" $s32min 0x37fffffff
2432                 else
2433                         echo "$s32min $s32max"
2434                 fi
2435                 ;;
2436
2437         jfs)
2438                 echo "0 $u32max"
2439                 ;;
2440         xfs)
2441                 _xfs_timestamp_range "$device"
2442                 ;;
2443         btrfs)
2444                 echo "$s64min $s64max"
2445                 ;;
2446         overlay)
2447                 if [ ! -z $OVL_BASE_FSTYP -a $OVL_BASE_FSTYP != "overlay" ]; then
2448                         _filesystem_timestamp_range $OVL_BASE_TEST_DEV $OVL_BASE_FSTYP
2449                 else
2450                         echo "-1 -1"
2451                 fi
2452                 ;;
2453         *)
2454                 echo "-1 -1"
2455                 ;;
2456         esac
2457 }
2458
2459 # indicate whether YP/NIS is active or not
2460 #
2461 _yp_active()
2462 {
2463         local dn
2464         dn=$(domainname 2>/dev/null)
2465         local ypcat=$(type -P ypcat)
2466         local rpcinfo=$(type -P rpcinfo)
2467         test -n "${dn}" -a "${dn}" != "(none)" -a "${dn}" != "localdomain" -a -n "${ypcat}" -a -n "${rpcinfo}" && \
2468                 "${rpcinfo}" -p localhost 2>/dev/null | grep -q ypbind
2469         echo $?
2470 }
2471
2472 # cat the password file
2473 #
2474 _cat_passwd()
2475 {
2476         [ $(_yp_active) -eq 0 ] && ypcat passwd
2477         cat /etc/passwd
2478 }
2479
2480 # cat the group file
2481 #
2482 _cat_group()
2483 {
2484         [ $(_yp_active) -eq 0 ] && ypcat group
2485         cat /etc/group
2486 }
2487
2488 # check if a user exists in the system
2489 #
2490 _require_user_exists()
2491 {
2492         local user=$1
2493         _cat_passwd | grep -q "^$user:"
2494         [ "$?" == "0" ] || _notrun "$user user not defined."
2495 }
2496
2497 # check if a user exists and is able to execute commands.
2498 # Uses 'fsgqa' user as default.
2499 #
2500 _require_user()
2501 {
2502         qa_user=fsgqa
2503         if [ -n "$1" ];then
2504                 qa_user=$1
2505         fi
2506         _require_user_exists $qa_user
2507         echo /bin/true | su $qa_user
2508         [ "$?" == "0" ] || _notrun "$qa_user cannot execute commands."
2509 }
2510
2511 # check for a chown support
2512 #
2513 _require_chown()
2514 {
2515         local rnd_uid=4242
2516         local file="$TEST_DIR/chown_testfile"
2517
2518         rm -f $file
2519         touch $file
2520         chown ${rnd_uid}:${rnd_uid} $file >/dev/null 2>&1 \
2521                 || _notrun "chown is not supported ${FSTYP}"
2522 }
2523
2524
2525 # check for a chmod support
2526 # Since chmod sometimes fails silently actual functionality test is done
2527 #
2528 _require_chmod()
2529 {
2530         local file="$TEST_DIR/chmod_testfile"
2531
2532         rm -f $file
2533         touch $file
2534
2535         # get original file mode
2536         local mode=`stat --format="0%a" $file`
2537         # flip the user's read bit
2538         let mode^=0400
2539         chmod `printf '%o' "$mode"` $file
2540         # check that the chmod actually flipped the bit
2541         [ `stat --format="0%a" $file` == `printf '0%o' "$mode"` ] \
2542                 || _notrun "chmod is not supported ${FSTYP}"
2543 }
2544
2545 # check for a group on the machine, fsgqa as default
2546 #
2547 _require_group()
2548 {
2549     qa_group=fsgqa
2550     if [ -n "$1" ];then
2551         qa_group=$1
2552     fi
2553     _cat_group | grep -q "^$qa_group:"
2554     [ "$?" == "0" ] || _notrun "$qa_group group not defined."
2555 }
2556
2557 _filter_user_do()
2558 {
2559         perl -ne "
2560 s,.*Permission\sdenied.*,Permission denied,;
2561 s,.*no\saccess\sto\stty.*,,;
2562 s,.*no\sjob\scontrol\sin\sthis\sshell.*,,;
2563 s,^\s*$,,;
2564         print;"
2565 }
2566
2567 _user_do()
2568 {
2569         echo $1 | su -s /bin/bash $qa_user 2>&1 | _filter_user_do
2570 }
2571
2572 _require_xfs_io_command()
2573 {
2574         if [ -z "$1" ]
2575         then
2576                 echo "Usage: _require_xfs_io_command command [switch]" 1>&2
2577                 exit 1
2578         fi
2579         local command=$1
2580         shift
2581         local param="$*"
2582         local param_checked=""
2583         local opts=""
2584         local attr_info=""
2585
2586         local testfile=$TEST_DIR/$$.xfs_io
2587         local testio
2588         case $command in
2589         "lsattr")
2590                 # Test xfs_io lsattr support and filesystem FS_IOC_FSSETXATTR
2591                 # support.
2592                 testio=`$XFS_IO_PROG -F -f -c "lsattr $param" $testfile 2>&1`
2593                 param_checked="$param"
2594                 ;;
2595         "chattr")
2596                 local testdir=$TEST_DIR/$$.attr_dir
2597                 mkdir $TEST_DIR/$$.attr_dir
2598                 if [ -z "$param" ]; then
2599                         param=s
2600                 fi
2601                 # Test xfs_io chattr support AND
2602                 # filesystem FS_IOC_FSSETXATTR support
2603                 # 'tPnE' flags are only valid for a directory so check them on a directory.
2604                 if echo "$param" | egrep -q 't|P|n|E'; then
2605                         testio=`$XFS_IO_PROG -F -c "chattr +$param" $testdir 2>&1`
2606                         attr_info=`$XFS_IO_PROG -F -r -c "lsattr" $testdir | awk '{print $1}'`
2607                         $XFS_IO_PROG -F -r -c "chattr -$param" $testdir 2>&1
2608                 else
2609                         testio=`$XFS_IO_PROG -F -f -c "chattr +$param" $testfile 2>&1`
2610                         attr_info=`$XFS_IO_PROG -F -r -c "lsattr" $testfile | awk '{print $1}'`
2611                         $XFS_IO_PROG -F -r -c "chattr -$param" $testfile 2>&1
2612                 fi
2613                 param_checked="+$param"
2614                 rm -rf $testdir 2>&1 > /dev/null
2615                 ;;
2616         "chproj")
2617                 testio=`$XFS_IO_PROG -F -f -c "chproj 0" $testfile 2>&1`
2618                 ;;
2619         "copy_range")
2620                 local testcopy=$TEST_DIR/$$.copy.xfs_io
2621                 local copy_opts=$testfile
2622                 if [ "$param" == "-f" ]; then
2623                         # source file is the open destination file
2624                         testcopy=$testfile
2625                         copy_opts="0 -d 4k -l 4k"
2626                 fi
2627                 $XFS_IO_PROG -F -f -c "pwrite 0 4k" $testfile > /dev/null 2>&1
2628                 testio=`$XFS_IO_PROG -F -f -c "copy_range $param $copy_opts" $testcopy 2>&1`
2629                 rm -f $testcopy > /dev/null 2>&1
2630                 param_checked="$param"
2631                 ;;
2632         "falloc"|"allocsp")
2633                 testio=`$XFS_IO_PROG -F -f -c "$command $param 0 1m" $testfile 2>&1`
2634                 param_checked="$param"
2635                 ;;
2636         "fpunch" | "fcollapse" | "zero" | "fzero" | "finsert" | "funshare")
2637                 local blocksize=$(_get_block_size $TEST_DIR)
2638                 testio=`$XFS_IO_PROG -F -f -c "pwrite 0 $((5 * $blocksize))" \
2639                         -c "fsync" -c "$command $blocksize $((2 * $blocksize))" \
2640                         $testfile 2>&1`
2641                 ;;
2642         "fiemap")
2643                 # If 'ranged' is passed as argument then we check to see if fiemap supports
2644                 # ranged query params
2645                 if echo "$param" | grep -q "ranged"; then
2646                         param=$(echo "$param" | sed "s/ranged//")
2647                         $XFS_IO_PROG -c "help fiemap" | grep -q "\[offset \[len\]\]"
2648                         [ $? -eq 0 ] || _notrun "xfs_io $command ranged support is missing"
2649                 fi
2650                 testio=`$XFS_IO_PROG -F -f -c "pwrite 0 20k" -c "fsync" \
2651                         -c "fiemap -v $param" $testfile 2>&1`
2652                 param_checked="$param"
2653                 ;;
2654         "flink")
2655                 local testlink=$TEST_DIR/$$.link.xfs_io
2656                 testio=`$XFS_IO_PROG -F -f -c "flink $testlink" $testfile 2>&1`
2657                 rm -f $testlink > /dev/null 2>&1
2658                 ;;
2659         "-T")
2660                 # Check O_TMPFILE support in xfs_io, kernel and fs
2661                 testio=`$XFS_IO_PROG -T -c quit $TEST_DIR 2>&1`
2662                 echo $testio | egrep -q "invalid option|Is a directory" && \
2663                         _notrun "xfs_io $command support is missing"
2664                 echo $testio | grep -q "Operation not supported" && \
2665                         _notrun "O_TMPFILE is not supported"
2666                 ;;
2667         "fsmap")
2668                 testio=`$XFS_IO_PROG -f -c "fsmap" $testfile 2>&1`
2669                 echo $testio | grep -q "Inappropriate ioctl" && \
2670                         _notrun "xfs_io $command support is missing"
2671                 ;;
2672         "label")
2673                 testio=`$XFS_IO_PROG -c "label" $TEST_DIR 2>&1`
2674                 ;;
2675         "open")
2676                 # -c "open $f" is broken in xfs_io <= 4.8. Along with the fix,
2677                 # a new -C flag was introduced to execute one shot commands.
2678                 # Check for -C flag support as an indication for the bug fix.
2679                 testio=`$XFS_IO_PROG -F -f -C "open $testfile" $testfile 2>&1`
2680                 echo $testio | grep -q "invalid option" && \
2681                         _notrun "xfs_io $command support is missing"
2682                 ;;
2683         "pwrite")
2684                 # -N (RWF_NOWAIT) only works with direct vectored I/O writes
2685                 local pwrite_opts=" "
2686                 if [ "$param" == "-N" ]; then
2687                         opts+=" -d"
2688                         pwrite_opts+="-V 1 -b 4k"
2689                 fi
2690                 testio=`$XFS_IO_PROG -f $opts -c \
2691                         "pwrite $pwrite_opts $param 0 4k" $testfile 2>&1`
2692                 param_checked="$pwrite_opts $param"
2693                 ;;
2694         "scrub"|"repair")
2695                 testio=`$XFS_IO_PROG -x -c "$command probe" $TEST_DIR 2>&1`
2696                 echo $testio | grep -q "Inappropriate ioctl" && \
2697                         _notrun "xfs_io $command support is missing"
2698                 ;;
2699         "utimes" )
2700                 testio=`$XFS_IO_PROG -f -c "utimes 0 0 0 0" $testfile 2>&1`
2701                 ;;
2702         "syncfs")
2703                 touch $testfile
2704                 testio=`$XFS_IO_PROG -c "syncfs" $testfile 2>&1`
2705                 ;;
2706         *)
2707                 testio=`$XFS_IO_PROG -c "help $command" 2>&1`
2708         esac
2709
2710         rm -f $testfile 2>&1 > /dev/null
2711         echo $testio | grep -q "not found" && \
2712                 _notrun "xfs_io $command $param_checked support is missing"
2713         echo $testio | grep -q "Operation not supported\|Inappropriate ioctl" && \
2714                 _notrun "xfs_io $command $param_checked failed (old kernel/wrong fs?)"
2715         echo $testio | grep -q "Invalid" && \
2716                 _notrun "xfs_io $command $param_checked failed (old kernel/wrong fs/bad args?)"
2717         echo $testio | grep -q "foreign file active" && \
2718                 _notrun "xfs_io $command $param_checked not supported on $FSTYP"
2719         echo $testio | grep -q "Function not implemented" && \
2720                 _notrun "xfs_io $command $param_checked support is missing (missing syscall?)"
2721         echo $testio | grep -q "unknown flag" && \
2722                 _notrun "xfs_io $command $param_checked support is missing (unknown flag)"
2723
2724         [ -n "$param" ] || return
2725
2726         if [ -z "$param_checked" ]; then
2727                 $XFS_IO_PROG -c "help $command" | grep -E -q "^ $param ([a-zA-Z_]+ )?--" || \
2728                         _notrun "xfs_io $command doesn't support $param"
2729         else
2730                 # xfs_io could result in "command %c not supported" if it was
2731                 # built on kernels not supporting pwritev2() calls
2732                 echo $testio | grep -q "\(invalid option\|not supported\)" && \
2733                         _notrun "xfs_io $command doesn't support $param"
2734         fi
2735
2736         # On XFS, ioctl(FSSETXATTR)(called by xfs_io -c "chattr") maskes off unsupported
2737         # or invalid flags silently so need to check these flags by extra ioctl(FSGETXATTR)
2738         # (called by xfs_io -c "lsattr").
2739         # The following URL explains why we don't change the behavior of XFS.
2740         # https://www.spinics.net/lists/linux-xfs/msg44725.html
2741         if [ -n "$attr_info" -a "$FSTYP" = "xfs" ]; then
2742                 local num=${#param}
2743                 for i in $(seq 0 $((num-1))); do
2744                         echo $attr_info | grep -q "${param:$i:1}" || \
2745                                 _notrun "xfs_io $command +${param:$i:1} support is missing (unknown flag in kernel)"
2746                 done
2747         fi
2748 }
2749
2750 # check that kernel and filesystem support direct I/O
2751 _require_odirect()
2752 {
2753         if [ $FSTYP = "ext4" ] || [ $FSTYP = "f2fs" ] ; then
2754                 if echo "$MOUNT_OPTIONS" | grep -q "test_dummy_encryption"; then
2755                         _notrun "$FSTYP encryption doesn't support O_DIRECT"
2756                 fi
2757         fi
2758         if [ $FSTYP = "ext4" ] ; then
2759                 if echo "$MOUNT_OPTIONS" | grep -q "data=journal"; then
2760                         _notrun "ext4 data journaling doesn't support O_DIRECT"
2761                 fi
2762         fi
2763         local testfile=$TEST_DIR/$$.direct
2764         $XFS_IO_PROG -F -f -d -c "pwrite 0 20k" $testfile > /dev/null 2>&1
2765         if [ $? -ne 0 ]; then
2766                 _notrun "O_DIRECT is not supported"
2767         fi
2768         rm -f $testfile 2>&1 > /dev/null
2769 }
2770
2771 # Format a swapfile and return its size in bytes
2772 _format_swapfile() {
2773         local fname="$1"
2774         local sz="$2"
2775         local swap_log=""
2776
2777         rm -f "$fname"
2778         touch "$fname"
2779         chmod 0600 "$fname"
2780         # Swap files must be nocow on Btrfs.
2781         $CHATTR_PROG +C "$fname" > /dev/null 2>&1
2782         _pwrite_byte 0x61 0 "$sz" "$fname" >> $seqres.full
2783         # Ignore permission complaints on filesystems that don't support perms
2784         swap_log=$($MKSWAP_PROG "$fname" 2>&1 | grep -v "insecure permission")
2785         echo $swap_log >> $seqres.full
2786
2787         echo $swap_log | grep -oP '\w+(?= bytes)'
2788 }
2789
2790 _swapon_file() {
2791         local fname="$1"
2792
2793         # Ignore permission complaints on filesystems that don't support perms
2794         $(swapon "$fname" 2> >(grep -v "insecure permissions" >&2))
2795 }
2796
2797 # Check that the filesystem supports swapfiles
2798 _require_scratch_swapfile()
2799 {
2800         _require_scratch
2801         _require_command "$MKSWAP_PROG" "mkswap"
2802
2803         _scratch_mkfs >/dev/null 2>&1
2804
2805         # With mounting SELinux context(e.g. system_u:object_r:root_t:s0),
2806         # standard mkswap tried to reset the type of default context to
2807         # swapfile_t if it's not swapfile_t, and then it failed and returned
2808         # ENOTSUP expectedly as we don't want to create any SELinux attr on
2809         # purpose.  standard mkswap ignored this relabel error by commit
2810         # d97dc0e of util-linux, but it still reported the error before
2811         # commit d97dc0e.  We mount swapfile context directly to skip the
2812         # reset step.
2813         [ -n "$SELINUX_MOUNT_OPTIONS" ] && export \
2814                 SELINUX_MOUNT_OPTIONS="-o context=system_u:object_r:swapfile_t:s0"
2815
2816         _scratch_mount
2817
2818         # Minimum size for mkswap is 10 pages
2819         _format_swapfile "$SCRATCH_MNT/swap" $(($(get_page_size) * 10)) > /dev/null
2820
2821         # ext* has supported all variants of swap files since their
2822         # introduction, so swapon should not fail.
2823         case "$FSTYP" in
2824         ext2|ext3|ext4)
2825                 if ! swapon "$SCRATCH_MNT/swap" >/dev/null 2>&1; then
2826                         if _check_s_dax "$SCRATCH_MNT/swap" 1 >/dev/null; then
2827                                 _scratch_unmount
2828                                 _notrun "swapfiles are not supported"
2829                         else
2830                                 _scratch_unmount
2831                                 _fail "swapon failed for $FSTYP"
2832                         fi
2833                 fi
2834                 ;;
2835         *)
2836                 if ! swapon "$SCRATCH_MNT/swap" >/dev/null 2>&1; then
2837                         _scratch_unmount
2838                         _notrun "swapfiles are not supported"
2839                 fi
2840                 ;;
2841         esac
2842
2843         swapoff "$SCRATCH_MNT/swap" >/dev/null 2>&1
2844         _scratch_unmount
2845 }
2846
2847 # Check that a fs has enough free space (in 1024b blocks)
2848 #
2849 _require_fs_space()
2850 {
2851         local mnt=$1
2852         local blocks=$2 # in units of 1024
2853         local gb=$(( blocks / 1024 / 1024 ))
2854
2855         local free_blocks=`df -kP $mnt | grep -v Filesystem | awk '{print $4}'`
2856         [ $free_blocks -lt $blocks ] && \
2857                 _notrun "This test requires at least ${gb}GB free on $mnt to run"
2858 }
2859
2860 #
2861 # Check if the filesystem supports sparse files.
2862 #
2863 # Unfortunately there is no better way to do this than a manual black list.
2864 #
2865 _require_sparse_files()
2866 {
2867     case $FSTYP in
2868     hfsplus|exfat)
2869         _notrun "Sparse files not supported by this filesystem type: $FSTYP"
2870         ;;
2871     *)
2872         ;;
2873     esac
2874 }
2875
2876 _require_debugfs()
2877 {
2878     #boot_params always present in debugfs
2879     [ -d "$DEBUGFS_MNT/boot_params" ] || _notrun "Debugfs not mounted"
2880 }
2881
2882 _require_fail_make_request()
2883 {
2884     [ -f "$DEBUGFS_MNT/fail_make_request/probability" ] \
2885         || _notrun "$DEBUGFS_MNT/fail_make_request \
2886  not found. Seems that CONFIG_FAULT_INJECTION_DEBUG_FS kernel config option not enabled"
2887 }
2888
2889 # Disable extent zeroing for ext4 on the given device
2890 _ext4_disable_extent_zeroout()
2891 {
2892         local dev=${1:-$TEST_DEV}
2893         local sdev=`_short_dev $dev`
2894
2895         [ -f /sys/fs/ext4/$sdev/extent_max_zeroout_kb ] && \
2896                 echo 0 >/sys/fs/ext4/$sdev/extent_max_zeroout_kb
2897 }
2898
2899 # The default behavior of SEEK_HOLE is to always return EOF.
2900 # Filesystems that implement non-default behavior return the offset
2901 # of holes with SEEK_HOLE. There is no way to query the filesystem
2902 # of which behavior it is implementing.
2903 # We use this whitelist FSTYP, to set expectation and avoid silent
2904 # regression of filesystem seek hole behavior.
2905 #
2906 # Return 0 for true
2907 _fstyp_has_non_default_seek_data_hole()
2908 {
2909         if [ -z $1 ]; then
2910                 local fstyp=$FSTYP
2911         else
2912                 local fstyp=$1
2913         fi
2914
2915         case "$fstyp" in
2916         btrfs|ext4|xfs|cifs|f2fs|gfs2|ocfs2|tmpfs)
2917                 return 0
2918                 ;;
2919         nfs*)
2920                 # NFSv2, NFSv3, and NFSv4.0/4.1 only support default behavior of SEEK_HOLE,
2921                 # while NFSv4.2 supports non-default behavior
2922                 local nfsvers=`_mount() | grep $TEST_DEV | sed -n 's/^.*vers=\([0-9.]*\).*$/\1/p'`
2923                 [ "$nfsvers" = "4.2" ]
2924                 return $?
2925                 ;;
2926         overlay)
2927                 if [ ! -z $OVL_BASE_FSTYP -a $OVL_BASE_FSTYP != "overlay" ]; then
2928                         _fstyp_has_non_default_seek_data_hole $OVL_BASE_FSTYP
2929                         return $?
2930                 else
2931                         # Assume that base fs has default behavior
2932                         return 1
2933                 fi
2934                 ;;
2935         *)
2936                 # by default fstyp has default SEEK_HOLE behavior;
2937                 # if your fs has non-default behavior, add it to whitelist above!
2938                 return 1
2939                 ;;
2940         esac
2941 }
2942
2943 # Run seek sanity test with predefined expectation for SEEK_DATA/HOLE behavior
2944 _run_seek_sanity_test()
2945 {
2946         local testseekargs
2947         if _fstyp_has_non_default_seek_data_hole; then
2948                 testseekargs+="-f"
2949         fi
2950         $here/src/seek_sanity_test $testseekargs $*
2951 }
2952
2953 # Check if the file system supports seek_data/hole
2954 _require_seek_data_hole()
2955 {
2956         local dev=${1:-$TEST_DEV}
2957         local testfile=$TEST_DIR/$$.seek
2958         local testseek=`$here/src/seek_sanity_test -t $testfile 2>&1`
2959
2960         rm -f $testfile &>/dev/null
2961         echo $testseek | grep -q "Kernel does not support" && \
2962                 _notrun "File system does not support llseek(2) SEEK_DATA/HOLE"
2963         # Disable extent zeroing for ext4 as that change where holes are
2964         # created
2965         if [ "$FSTYP" = "ext4" ]; then
2966                 _ext4_disable_extent_zeroout $dev
2967         fi
2968 }
2969
2970 _require_runas()
2971 {
2972         _require_test_program "runas"
2973 }
2974
2975 _runas()
2976 {
2977         "$here/src/runas" "$@"
2978 }
2979
2980 _require_richacl_prog()
2981 {
2982         _require_command "$GETRICHACL_PROG" getrichacl
2983         _require_command "$SETRICHACL_PROG" setrichacl
2984 }
2985
2986 _require_scratch_richacl_xfs()
2987 {
2988         _scratch_mkfs_xfs_supported -m richacl=1 >/dev/null 2>&1 \
2989                 || _notrun "mkfs.xfs doesn't have richacl feature"
2990         _scratch_mkfs_xfs -m richacl=1 >/dev/null 2>&1
2991         _try_scratch_mount >/dev/null 2>&1 \
2992                 || _notrun "kernel doesn't support richacl feature on $FSTYP"
2993         _scratch_unmount
2994 }
2995
2996 _require_scratch_richacl_ext4()
2997 {
2998         _scratch_mkfs -O richacl >/dev/null 2>&1 \
2999                 || _notrun "can't mkfs $FSTYP with option -O richacl"
3000         _try_scratch_mount >/dev/null 2>&1 \
3001                 || _notrun "kernel doesn't support richacl feature on $FSTYP"
3002         _scratch_unmount
3003 }
3004
3005 _require_scratch_richacl_support()
3006 {
3007         _scratch_mount
3008         $GETFATTR_PROG -n system.richacl >/dev/null 2>&1 \
3009                 || _notrun "this test requires richacl support on \$SCRATCH_DEV"
3010         _scratch_unmount
3011 }
3012
3013 _require_scratch_richacl()
3014 {
3015         case "$FSTYP" in
3016         xfs)    _require_scratch_richacl_xfs
3017                 ;;
3018         ext4)   _require_scratch_richacl_ext4
3019                 ;;
3020         nfs*|cifs|overlay)
3021                 _require_scratch_richacl_support
3022                 ;;
3023         *)      _notrun "this test requires richacl support on \$SCRATCH_DEV"
3024                 ;;
3025         esac
3026 }
3027
3028 _scratch_mkfs_richacl()
3029 {
3030         case "$FSTYP" in
3031         xfs)    _scratch_mkfs_xfs -m richacl=1
3032                 ;;
3033         ext4)   _scratch_mkfs -O richacl
3034                 ;;
3035         nfs*|cifs|overlay)
3036                 _scratch_mkfs
3037                 ;;
3038         esac
3039 }
3040
3041 # check if the given device is mounted, if so, return mount point
3042 _is_dev_mounted()
3043 {
3044         local dev=$1
3045         local fstype=${2:-$FSTYP}
3046
3047         if [ $# -lt 1 ]; then
3048                 echo "Usage: _is_dev_mounted <device> [fstype]" 1>&2
3049                 exit 1
3050         fi
3051
3052         findmnt -rncv -S $dev -t $fstype -o TARGET | head -1
3053 }
3054
3055 # check if the given dir is a mount point, if so, return mount point
3056 _is_dir_mountpoint()
3057 {
3058         local dir=$1
3059         local fstype=${2:-$FSTYP}
3060
3061         if [ $# -lt 1 ]; then
3062                 echo "Uasge: _is_dir_mountpoint <dir> [fstype]" 1>&2
3063                 exit 1
3064         fi
3065
3066         findmnt -rncv -t $fstype -o TARGET $dir | head -1
3067 }
3068
3069 # remount a FS to a new mode (ro or rw)
3070 #
3071 _remount()
3072 {
3073     if [ $# -ne 2 ]
3074     then
3075         echo "Usage: _remount device ro/rw" 1>&2
3076         exit 1
3077     fi
3078     local device=$1
3079     local mode=$2
3080
3081     if ! mount -o remount,$mode $device
3082     then
3083         echo "_remount: failed to remount filesystem on $device as $mode"
3084         exit 1
3085     fi
3086 }
3087
3088 # Run the appropriate repair/check on a filesystem
3089 #
3090 # if the filesystem is mounted, it's either remounted ro before being
3091 # checked or it's unmounted and then remounted
3092 #
3093
3094 # If set, we remount ro instead of unmounting for fsck
3095 USE_REMOUNT=0
3096
3097 _umount_or_remount_ro()
3098 {
3099     if [ $# -ne 1 ]
3100     then
3101         echo "Usage: _umount_or_remount_ro <device>" 1>&2
3102         exit 1
3103     fi
3104
3105     local device=$1
3106     local mountpoint=`_is_dev_mounted $device`
3107
3108     if [ $USE_REMOUNT -eq 0 ]; then
3109         $UMOUNT_PROG $device
3110     else
3111         _remount $device ro
3112     fi
3113     echo "$mountpoint"
3114 }
3115
3116 _mount_or_remount_rw()
3117 {
3118         if [ $# -ne 3 ]; then
3119                 echo "Usage: _mount_or_remount_rw <opts> <dev> <mnt>" 1>&2
3120                 exit 1
3121         fi
3122         local mount_opts=$1
3123         local device=$2
3124         local mountpoint=$3
3125
3126         if [ $USE_REMOUNT -eq 0 ]; then
3127                 if [ "$FSTYP" != "overlay" ]; then
3128                         _mount -t $FSTYP $mount_opts $device $mountpoint
3129                         _idmapped_mount $device $mountpoint
3130                 else
3131                         _overlay_mount $device $mountpoint
3132                 fi
3133                 if [ $? -ne 0 ]; then
3134                         _dump_err "!!! failed to remount $device on $mountpoint"
3135                         return 0 # ok=0
3136                 fi
3137         else
3138                 _remount $device rw
3139         fi
3140
3141         return 1 # ok=1
3142 }
3143
3144 # Check a generic filesystem in no-op mode; this assumes that the
3145 # underlying fsck program accepts "-n" for a no-op (check-only) run,
3146 # and that it will still return an errno for corruption in this mode.
3147 #
3148 # Filesystems which don't support this will need to define their
3149 # own check routine.
3150 #
3151 _check_generic_filesystem()
3152 {
3153     local device=$1
3154
3155     # If type is set, we're mounted
3156     local type=`_fs_type $device`
3157     local ok=1
3158
3159     if [ "$type" = "$FSTYP" ]
3160     then
3161         # mounted ...
3162         local mountpoint=`_umount_or_remount_ro $device`
3163     fi
3164
3165     fsck -t $FSTYP $FSCK_OPTIONS $device >$tmp.fsck 2>&1
3166     if [ $? -ne 0 ]
3167     then
3168         _log_err "_check_generic_filesystem: filesystem on $device is inconsistent"
3169         echo "*** fsck.$FSTYP output ***"       >>$seqres.full
3170         cat $tmp.fsck                           >>$seqres.full
3171         echo "*** end fsck.$FSTYP output"       >>$seqres.full
3172
3173         ok=0
3174     fi
3175     rm -f $tmp.fsck
3176
3177     if [ $ok -eq 0 ] && [ -n "$DUMP_CORRUPT_FS" ]; then
3178         case "$FSTYP" in
3179         ext*)
3180             local flatdev="$(basename "$device")"
3181             _ext4_metadump "$seqres.$flatdev.check.qcow2" "$device" compress
3182             ;;
3183         esac
3184     fi
3185
3186     if [ $ok -eq 0 ]
3187     then
3188         echo "*** mount output ***"             >>$seqres.full
3189         _mount                                  >>$seqres.full
3190         echo "*** end mount output"             >>$seqres.full
3191     elif [ "$type" = "$FSTYP" ]
3192     then
3193         # was mounted ...
3194         _mount_or_remount_rw "$MOUNT_OPTIONS" $device $mountpoint
3195         ok=$?
3196     fi
3197
3198     if [ $ok -eq 0 ]; then
3199         status=1
3200         if [ "$iam" != "check" ]; then
3201                 exit 1
3202         fi
3203         return 1
3204     fi
3205
3206     return 0
3207 }
3208
3209 # Filter the knowen errors the UDF Verifier reports.
3210 _udf_test_known_error_filter()
3211 {
3212         egrep -v "PVD  60  Error: Interchange Level: 1, Maximum Interchange Level: 0|FSD  28  Error: Interchange Level: 1, Maximum Interchange Level: 1,|PVD  72  Warning: Volume Set Identifier: \"\*IRIX UDF\",|Warning: [0-9]+ unused blocks NOT marked as unallocated."
3213
3214 }
3215
3216 _check_udf_filesystem()
3217 {
3218     [ "$DISABLE_UDF_TEST" == "1" ] && return
3219
3220     if [ $# -ne 1 -a $# -ne 2 ]
3221     then
3222         echo "Usage: _check_udf_filesystem device [last_block]" 1>&2
3223         exit 1
3224     fi
3225
3226     if [ ! -x $here/src/udf_test ]
3227     then
3228         echo "udf_test not installed, please download and build the Philips"
3229         echo "UDF Verification Software from http://www.extra.research.philips.com/udf/."
3230         echo "Then copy the udf_test binary to $here/src/."
3231         echo "If you do not wish to run udf_test then set environment variable DISABLE_UDF_TEST"
3232         echo "to 1."
3233         return
3234     fi
3235
3236     local device=$1
3237     local opt_arg=""
3238     if [ $# -eq 2 ]; then
3239         opt_arg="-lastvalidblock $(( $2 - 1 ))"
3240     fi
3241
3242     rm -f $seqres.checkfs
3243     sleep 1 # Due to a problem with time stamps in udf_test
3244     $here/src/udf_test $opt_arg $device | tee $seqres.checkfs | egrep "Error|Warning" | \
3245         _udf_test_known_error_filter | \
3246         egrep -iv "Error count:.*[0-9]+.*total occurrences:.*[0-9]+|Warning count:.*[0-9]+.*total occurrences:.*[0-9]+" && \
3247         echo "Warning UDF Verifier reported errors see $seqres.checkfs." && return 1
3248     return 0
3249 }
3250
3251 _check_test_fs()
3252 {
3253     case $FSTYP in
3254     xfs)
3255         _check_xfs_test_fs
3256         ;;
3257     nfs)
3258         # no way to check consistency for nfs
3259         ;;
3260     cifs)
3261         # no way to check consistency for cifs
3262         ;;
3263     9p)
3264         # no way to check consistency for 9p
3265         ;;
3266     virtiofs)
3267         # no way to check consistency for virtiofs
3268         ;;
3269     ceph|ceph-fuse)
3270         # no way to check consistency for CephFS
3271         ;;
3272     glusterfs)
3273         # no way to check consistency for GlusterFS
3274         ;;
3275     overlay)
3276         _check_overlay_test_fs
3277         ;;
3278     pvfs2)
3279         ;;
3280     udf)
3281         # do nothing for now
3282         ;;
3283     btrfs)
3284         _check_btrfs_filesystem $TEST_DEV
3285         ;;
3286     tmpfs)
3287         # no way to check consistency for tmpfs
3288         ;;
3289     ubifs)
3290         # there is no fsck program for ubifs yet
3291         ;;
3292     *)
3293         _check_generic_filesystem $TEST_DEV
3294         ;;
3295     esac
3296 }
3297
3298 _check_scratch_fs()
3299 {
3300     local device=$SCRATCH_DEV
3301     [ $# -eq 1 ] && device=$1
3302
3303     case $FSTYP in
3304     xfs)
3305         local scratch_log="none"
3306         local scratch_rt="none"
3307         [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
3308             scratch_log="$SCRATCH_LOGDEV"
3309
3310         [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
3311             scratch_rt="$SCRATCH_RTDEV"
3312
3313         _check_xfs_filesystem $device $scratch_log $scratch_rt
3314         ;;
3315     udf)
3316         _check_udf_filesystem $device $udf_fsize
3317         ;;
3318     nfs*)
3319         # Don't know how to check an NFS filesystem, yet.
3320         ;;
3321     cifs)
3322         # Don't know how to check a CIFS filesystem, yet.
3323         ;;
3324     9p)
3325         # no way to check consistency for 9p
3326         ;;
3327     virtiofs)
3328         # no way to check consistency for virtiofs
3329         ;;
3330     ceph)
3331         # no way to check consistency for CephFS
3332         ;;
3333     glusterfs)
3334         # no way to check consistency for GlusterFS
3335         ;;
3336     overlay)
3337         _check_overlay_scratch_fs
3338         ;;
3339     pvfs2)
3340         ;;
3341     btrfs)
3342         _check_btrfs_filesystem $device
3343         ;;
3344     tmpfs)
3345         # no way to check consistency for tmpfs
3346         ;;
3347     ubifs)
3348         # there is no fsck program for ubifs yet
3349         ;;
3350     *)
3351         _check_generic_filesystem $device
3352         ;;
3353     esac
3354 }
3355
3356 _full_fstyp_details()
3357 {
3358      [ -z "$FSTYP" ] && FSTYP=xfs
3359      if [ $FSTYP = xfs ]; then
3360         if [ -d /proc/fs/xfs ]; then
3361             if grep -q 'debug 0' /proc/fs/xfs/stat; then
3362                 FSTYP="$FSTYP (non-debug)"
3363             elif grep -q 'debug 1' /proc/fs/xfs/stat; then
3364                 FSTYP="$FSTYP (debug)"
3365             fi
3366         else
3367             if uname -a | grep -qi 'debug'; then
3368                 FSTYP="$FSTYP (debug)"
3369             else
3370                 FSTYP="$FSTYP (non-debug)"
3371             fi
3372         fi
3373      fi
3374      echo $FSTYP
3375 }
3376
3377 _full_platform_details()
3378 {
3379      local os=`uname -s`
3380      local host=`hostname -s`
3381      local kernel=`uname -rv`
3382      local platform=`uname -m`
3383      echo "$os/$platform $host $kernel"
3384 }
3385
3386 _get_os_name()
3387 {
3388         if [ "`uname`" == "Linux" ]; then
3389                 echo 'linux'
3390         else
3391                 echo Unknown operating system: `uname`
3392                 exit
3393         fi
3394 }
3395
3396 _link_out_file_named()
3397 {
3398         local features=$2
3399         local suffix=$(FEATURES="$features" perl -e '
3400                 my %feathash;
3401                 my $feature, $result, $suffix, $opts;
3402
3403                 foreach $feature (split(/,/, $ENV{"FEATURES"})) {
3404                         $feathash{$feature} = 1;
3405                 }
3406                 $result = "default";
3407                 while (<>) {
3408                         my $found = 1;
3409
3410                         chomp;
3411                         ($opts, $suffix) = split(/ *: */);
3412                         foreach my $opt (split(/,/, $opts)) {
3413                                 if (!exists($feathash{$opt})) {
3414                                         $found = 0;
3415                                         last;
3416                                 }
3417                         }
3418                         if ($found == 1) {
3419                                 $result = $suffix;
3420                                 last;
3421                         }
3422                 }
3423                 print $result
3424                 ' <$seqfull.cfg)
3425         rm -f $1
3426         ln -fs $(basename $1).$suffix $1
3427 }
3428
3429 _link_out_file()
3430 {
3431         local features
3432
3433         if [ $# -eq 0 ]; then
3434                 features="$(_get_os_name),$FSTYP"
3435                 if [ -n "$MOUNT_OPTIONS" ]; then
3436                         features=$features,${MOUNT_OPTIONS##"-o "}
3437                 fi
3438         else
3439                 features=$1
3440         fi
3441
3442         _link_out_file_named $seqfull.out "$features"
3443 }
3444
3445 _die()
3446 {
3447         echo $@
3448         exit 1
3449 }
3450
3451 # convert urandom incompressible data to compressible text data
3452 _ddt()
3453 {
3454         od /dev/urandom | dd iflag=fullblock ${*}
3455 }
3456
3457 #takes files, randomdata
3458 _nfiles()
3459 {
3460         local f=0
3461         while [ $f -lt $1 ]
3462         do
3463                 local file=f$f
3464                 echo > $file
3465                 if [ $size -gt 0 ]; then
3466                     if [ "$2" == "false" ]; then
3467                         dd if=/dev/zero of=$file bs=1024 count=$size 2>&1 | _filter_dd
3468                     elif [ "$2" == "comp" ]; then
3469                         _ddt of=$file bs=1024 count=$size 2>&1 | _filter_dd
3470                     else
3471                         dd if=/dev/urandom of=$file bs=1024 count=$size 2>&1 | _filter_dd
3472                     fi
3473                 fi
3474                 let f=$f+1
3475         done
3476 }
3477
3478 # takes dirname, depth, randomdata
3479 _descend()
3480 {
3481         local dirname=$1 depth=$2 randomdata=$3
3482         mkdir $dirname  || die "mkdir $dirname failed"
3483         cd $dirname
3484
3485         _nfiles $files $randomdata          # files for this dir and data type
3486
3487         [ $depth -eq 0 ] && return
3488         local deep=$(( depth - 1 )) # go 1 down
3489
3490         [ $verbose = true ] && echo "descending, depth from leaves = $deep"
3491
3492         local d=0
3493         while [ $d -lt $dirs ]
3494         do
3495                 _descend d$d $deep &
3496                 let d=$d+1
3497                 wait
3498         done
3499 }
3500
3501 # Populate a filesystem with inodes for performance experiments
3502 #
3503 # usage: populate [-v] [-n ndirs] [-f nfiles] [-d depth] [-r root] [-s size] [-x]
3504 #
3505 _populate_fs()
3506 {
3507     local here=`pwd`
3508     local dirs=5          # ndirs in each subdir till leaves
3509     local size=0          # sizeof files in K
3510     local files=100       # num files in _each_ subdir
3511     local depth=2         # depth of tree from root to leaves
3512     local verbose=false
3513     local root=root       # path of initial root of directory tree
3514     local randomdata=false # -x data type urandom, zero or compressible
3515     local c
3516
3517     OPTIND=1
3518     while getopts "d:f:n:r:s:v:x:c" c
3519     do
3520         case $c in
3521         d)      depth=$OPTARG;;
3522         n)      dirs=$OPTARG;;
3523         f)      files=$OPTARG;;
3524         s)      size=$OPTARG;;
3525         v)      verbose=true;;
3526         r)      root=$OPTARG;;
3527         x)      randomdata=true;;
3528         c)      randomdata=comp;;
3529         esac
3530     done
3531
3532     _descend $root $depth $randomdata
3533     wait
3534
3535     cd $here
3536
3537     [ $verbose = true ] && echo done
3538 }
3539
3540 # query whether the given file has the given inode flag set
3541 #
3542 _test_inode_flag()
3543 {
3544         local flag=$1
3545         local file=$2
3546
3547         if $XFS_IO_PROG -r -c 'lsattr -v' "$file" | grep -q "$flag" ; then
3548                 return 0
3549         fi
3550         return 1
3551 }
3552
3553 # query the given files extsize allocator hint in bytes (if any)
3554 #
3555 _test_inode_extsz()
3556 {
3557         local file=$1
3558         local blocks=""
3559
3560         blocks=`$XFS_IO_PROG -r -c 'stat' "$file" | \
3561                 awk '/^xattr.extsize =/ { print $3 }'`
3562         [ -z "$blocks" ] && blocks="0"
3563         echo $blocks
3564 }
3565
3566 # scratch_dev_pool should contain the disks pool for the btrfs raid
3567 _require_scratch_dev_pool()
3568 {
3569         local i
3570         local ndevs
3571
3572         if [ -z "$SCRATCH_DEV_POOL" ]; then
3573                 _notrun "this test requires a valid \$SCRATCH_DEV_POOL"
3574         fi
3575
3576         if [ -z "$1" ]; then
3577                 ndevs=2
3578         else
3579                 ndevs=$1
3580         fi
3581
3582         # btrfs test case needs ndevs or more scratch_dev_pool; other FS not sure
3583         # so fail it
3584         case $FSTYP in
3585         btrfs)
3586                 if [ "`echo $SCRATCH_DEV_POOL|wc -w`" -lt $ndevs ]; then
3587                         _notrun "btrfs and this test needs $ndevs or more disks in SCRATCH_DEV_POOL"
3588                 fi
3589         ;;
3590         *)
3591                 _notrun "dev_pool is not supported by fstype \"$FSTYP\""
3592         ;;
3593         esac
3594
3595         for i in $SCRATCH_DEV_POOL; do
3596                 if [ "`_is_block_dev "$i"`" = "" ]; then
3597                         _notrun "this test requires valid block disk $i"
3598                 fi
3599                 if [ "`_is_block_dev "$i"`" = "`_is_block_dev "$TEST_DEV"`" ]; then
3600                         _notrun "$i is part of TEST_DEV, this test requires unique disks"
3601                 fi
3602                 if _mount | grep -q $i; then
3603                         if ! $UMOUNT_PROG $i; then
3604                             echo "failed to unmount $i - aborting"
3605                             exit 1
3606                         fi
3607                 fi
3608                 # to help better debug when something fails, we remove
3609                 # traces of previous btrfs FS on the dev.
3610                 dd if=/dev/zero of=$i bs=4096 count=100 > /dev/null 2>&1
3611         done
3612 }
3613
3614 # ensure devices in SCRATCH_DEV_POOL are of the same size
3615 # must be called after _require_scratch_dev_pool
3616 _require_scratch_dev_pool_equal_size()
3617 {
3618         local size
3619         local newsize
3620         local dev
3621
3622         # SCRATCH_DEV has been set to the first device in SCRATCH_DEV_POOL
3623         size=`_get_device_size $SCRATCH_DEV`
3624         for dev in $SCRATCH_DEV_POOL; do
3625                 newsize=`_get_device_size $dev`
3626                 if [ $size -ne $newsize ]; then
3627                         _notrun "This test requires devices in SCRATCH_DEV_POOL have the same size"
3628                 fi
3629         done
3630 }
3631
3632
3633 # Check that fio is present, and it is able to execute given jobfile
3634 _require_fio()
3635 {
3636         local job=$1
3637
3638         _require_command "$FIO_PROG" fio
3639         if [ -z "$1" ]; then
3640                 return 1;
3641         fi
3642
3643         $FIO_PROG --warnings-fatal --showcmd $job >> $seqres.full 2>&1
3644         [ $? -eq 0 ] || _notrun "$FIO_PROG too old, see $seqres.full"
3645 }
3646
3647 # Does freeze work on this fs?
3648 _require_freeze()
3649 {
3650         xfs_freeze -f "$TEST_DIR" >/dev/null 2>&1
3651         local result=$?
3652         xfs_freeze -u "$TEST_DIR" >/dev/null 2>&1
3653         [ $result -eq 0 ] || _notrun "$FSTYP does not support freezing"
3654 }
3655
3656 # Does NFS export work on this fs?
3657 _require_exportfs()
3658 {
3659         _require_test_program "open_by_handle"
3660
3661         # virtiofs doesn't support open_by_handle_at(2) yet, though the syscall
3662         # doesn't return error. It requires stable FUSE nodeids.
3663         # This feature is expected to be implemented in the future in virtiofs,
3664         # and this check should be removed by then.
3665         [ $FSTYP == "virtiofs" ] && \
3666                 _notrun "$FSTYP doesn't support open_by_handle_at(2)"
3667
3668         mkdir -p "$TEST_DIR"/exportfs_test
3669         $here/src/open_by_handle -c "$TEST_DIR"/exportfs_test 2>&1 \
3670                 || _notrun "$FSTYP does not support NFS export"
3671 }
3672
3673
3674 # Does shutdown work on this fs?
3675 _require_scratch_shutdown()
3676 {
3677         [ -x $here/src/godown ] || _notrun "src/godown executable not found"
3678
3679         _scratch_mkfs > /dev/null 2>&1 || _notrun "_scratch_mkfs failed on $SCRATCH_DEV"
3680         _scratch_mount
3681
3682         if [ $FSTYP = "overlay" ]; then
3683                 if [ -z $OVL_BASE_SCRATCH_DEV ]; then
3684                         # In lagacy overlay usage, it may specify directory as
3685                         # SCRATCH_DEV, in this case OVL_BASE_SCRATCH_DEV
3686                         # will be null, so check OVL_BASE_SCRATCH_DEV before
3687                         # running shutdown to avoid shutting down base fs accidently.
3688                         _notrun "This test requires a valid $OVL_BASE_SCRATCH_DEV as ovl base fs"
3689                 else
3690                         $here/src/godown -f $OVL_BASE_SCRATCH_MNT 2>&1 \
3691                         || _notrun "Underlying filesystem does not support shutdown"
3692                 fi
3693         else
3694                 $here/src/godown -f $SCRATCH_MNT 2>&1 \
3695                         || _notrun "$FSTYP does not support shutdown"
3696         fi
3697
3698         _scratch_unmount
3699 }
3700
3701 _check_s_dax()
3702 {
3703         local target=$1
3704         local exp_s_dax=$2
3705         local ret=0
3706
3707         local attributes=$($XFS_IO_PROG -c 'statx -r' $target | awk '/stat.attributes / { print $3 }')
3708
3709         # The original attribute bit value, STATX_ATTR_DAX (0x2000), conflicted
3710         # with STATX_ATTR_MOUNT_ROOT.  Therefore, STATX_ATTR_DAX was changed to
3711         # 0x00200000.
3712         #
3713         # Because DAX tests do not run on root mounts, STATX_ATTR_MOUNT_ROOT
3714         # should always be 0.  Check for the old flag and fail the test if that
3715         # occurs.
3716
3717         if [ $(( attributes & 0x2000 )) -ne 0 ]; then
3718                 echo "$target has an unexpected STATX_ATTR_MOUNT_ROOT flag set"
3719                 echo "which used to be STATX_ATTR_DAX"
3720                 echo "     This test should not be running on the root inode..."
3721                 echo "     Does the kernel have the following patch?"
3722                 echo "     72d1249e2ffd uapi: fix statx attribute value overlap for DAX & MOUNT_ROOT"
3723         fi
3724
3725         if [ $exp_s_dax -eq 0 ]; then
3726                 if (( attributes & 0x00200000 )); then
3727                         echo "$target has unexpected S_DAX flag"
3728                         ret=1
3729                 fi
3730         else
3731                 if ! (( attributes & 0x00200000 )); then
3732                         echo "$target doesn't have expected S_DAX flag"
3733                         ret=2
3734                 fi
3735         fi
3736         return $ret
3737 }
3738
3739 _check_xflag()
3740 {
3741         local target=$1
3742         local exp_xflag=$2
3743
3744         if [ $exp_xflag -eq 0 ]; then
3745                 _test_inode_flag dax $target && echo "$target has unexpected FS_XFLAG_DAX flag"
3746         else
3747                 _test_inode_flag dax $target || echo "$target doesn't have expected FS_XFLAG_DAX flag"
3748         fi
3749 }
3750
3751 # Check if dax mount options are supported
3752 #
3753 # $1 can be either 'dax=always' or 'dax'
3754 #
3755 # dax=always
3756 #      Check for the new dax options (dax=inode, dax=always or dax=never)
3757 #      by passing "dax=always".
3758 # dax
3759 #      Check for the old dax or new dax=always by passing "dax".
3760 #
3761 # This only accepts 'dax=always' because dax=always, dax=inode and
3762 # dax=never are always supported together.  So if the other options are
3763 # required checking for 'dax=always' indicates support for the other 2.
3764 #
3765 # Return 0 if filesystem/device supports the specified dax option.
3766 # Return 1 if mount fails with the specified dax option.
3767 # Return 2 if /proc/mounts shows wrong dax option.
3768 _check_scratch_dax_mountopt()
3769 {
3770         local option=$1
3771
3772         _require_scratch
3773         _scratch_mkfs > /dev/null 2>&1
3774
3775         _try_scratch_mount "-o $option" > /dev/null 2>&1 || return 1
3776
3777         if _fs_options $SCRATCH_DEV | egrep -q "dax(=always|,|$)"; then
3778                 _scratch_unmount
3779                 return 0
3780         else
3781                 _scratch_unmount
3782                 return 2
3783         fi
3784 }
3785
3786 # Throw notrun if _check_scratch_dax_mountopt() returns a non-zero value.
3787 _require_scratch_dax_mountopt()
3788 {
3789         local mountopt=$1
3790
3791         _check_scratch_dax_mountopt "$mountopt"
3792         local res=$?
3793
3794         [ $res -eq 1 ] && _notrun "mount $SCRATCH_DEV with $mountopt failed"
3795         [ $res -eq 2 ] && _notrun "$SCRATCH_DEV $FSTYP does not support -o $mountopt"
3796 }
3797
3798 _require_dax_iflag()
3799 {
3800         _require_xfs_io_command "chattr" "x"
3801 }
3802
3803 # Does norecovery support by this fs?
3804 _require_norecovery()
3805 {
3806         _try_scratch_mount -o ro,norecovery || \
3807                 _notrun "$FSTYP does not support norecovery"
3808         _scratch_unmount
3809 }
3810
3811 # Does this filesystem support metadata journaling?
3812 # We exclude ones here that don't; otherwise we assume that it does, so the
3813 # test will run, fail, and motivate someone to update this test for a new
3814 # filesystem.
3815 #
3816 # It's possible that TEST_DEV and SCRATCH_DEV have different features (it'd be
3817 # odd, but possible) so check $TEST_DEV by default, but we can optionall pass
3818 # any dev we want.
3819 _has_metadata_journaling()
3820 {
3821         if [ -z $1 ]; then
3822                 local dev=$TEST_DEV
3823         else
3824                 local dev=$1
3825         fi
3826
3827         case "$FSTYP" in
3828         ext2|vfat|msdos|udf|exfat|tmpfs)
3829                 echo "$FSTYP does not support metadata journaling"
3830                 return 1
3831                 ;;
3832         ext4)
3833                 # ext4 could be mkfs'd without a journal...
3834                 _require_dumpe2fs
3835                 $DUMPE2FS_PROG -h $dev 2>&1 | grep -q has_journal || {
3836                         echo "$FSTYP on $dev not configured with metadata journaling"
3837                         return 1
3838                 }
3839                 # ext4 might not load a journal
3840                 if _normalize_mount_options "$MOUNT_OPTIONS" | grep -qw "noload"; then
3841                         echo "mount option \"noload\" not allowed in this test"
3842                         return 1
3843                 fi
3844                 ;;
3845         overlay)
3846                 # metadata journaling check is based on base filesystem configurations
3847                 # and  because -overlay option saves those configurations to OVL_BASE_*,
3848                 # adding restore/override the configurations before/after the check.
3849                 if [ ! -z $OVL_BASE_FSTYP -a $OVL_BASE_FSTYP != "overlay" ]; then
3850                         local ret
3851
3852                         _overlay_config_restore
3853                         _has_metadata_journaling
3854                         ret=$?
3855                         _overlay_config_override
3856                         return $ret
3857                 else
3858                         echo "No metadata journaling support for legacy overlay setup"
3859                         return 1
3860                 fi
3861                 ;;
3862         *)
3863                 # by default we pass; if you need to, add your fs above!
3864                 ;;
3865         esac
3866         return 0
3867 }
3868
3869 _require_metadata_journaling()
3870 {
3871         local msg=$(_has_metadata_journaling $@)
3872         if [ -n "$msg" ]; then
3873                 _notrun "$msg"
3874         fi
3875 }
3876
3877 _count_extents()
3878 {
3879         $XFS_IO_PROG -r -c "fiemap" $1 | tail -n +2 | grep -v hole | wc -l
3880 }
3881
3882 # Similar to _count_extents() but if any extent is shared multiples times in
3883 # the file (reflinked to different file offsets), it is accounted as 1 extent
3884 # instead of N extents.
3885 _count_exclusive_extents()
3886 {
3887         $XFS_IO_PROG -r -c "fiemap" $1 | tail -n +2 | grep -v hole | \
3888                 cut -d ' ' -f 3 | sort | uniq | wc -l
3889 }
3890
3891 _count_holes()
3892 {
3893         $XFS_IO_PROG -r -c "fiemap" $1 | tail -n +2 | grep hole | wc -l
3894 }
3895
3896 _count_attr_extents()
3897 {
3898         $XFS_IO_PROG -c "fiemap -a" $1 | tail -n +2 | grep -v hole | wc -l
3899 }
3900
3901 # Get the sector number of the extent at @offset of @file
3902 _get_file_extent_sector()
3903 {
3904         local file=$1
3905         local offset=$2
3906         local result
3907
3908         result=$($XFS_IO_PROG -c "fiemap $offset" "$file" | \
3909                  _filter_xfs_io_fiemap | head -n1 | $AWK_PROG '{print $3}')
3910
3911         # xfs_io fiemap will output nothing if there is only hole, so here
3912         # to replace the empty string with "hole" instead
3913         if [ -z "$result" ]; then
3914                 result="hole"
3915         fi
3916         echo "$result"
3917 }
3918
3919 # arg 1 is dev to remove and is output of the below eg.
3920 # ls -l /sys/class/block/sdd | rev | cut -d "/" -f 3 | rev
3921 _devmgt_remove()
3922 {
3923         local lun=$1
3924         local disk=$2
3925
3926         echo 1 > /sys/class/scsi_device/${lun}/device/delete || _fail "Remove disk failed"
3927
3928         stat $disk > /dev/null 2>&1
3929         while [ $? -eq 0 ]; do
3930                 sleep 1
3931                 stat $disk > /dev/null 2>&1
3932         done
3933 }
3934
3935 # arg 1 is dev to add and is output of the below eg.
3936 # ls -l /sys/class/block/sdd | rev | cut -d "/" -f 3 | rev
3937 _devmgt_add()
3938 {
3939         local h
3940         local tdl
3941         # arg 1 will be in h:t:d:l format now in the h and "t d l" format
3942         h=`echo ${1} | cut -d":" -f 1`
3943         tdl=`echo ${1} | cut -d":" -f 2-|sed 's/:/ /g'`
3944
3945         echo ${tdl} >  /sys/class/scsi_host/host${h}/scan || _fail "Add disk failed"
3946
3947         # ensure the device comes online
3948         local dev_back_oneline=0
3949         local i
3950         for i in `seq 1 10`; do
3951                 if [ -d /sys/class/scsi_device/${1}/device/block ]; then
3952                         local dev=`ls /sys/class/scsi_device/${1}/device/block`
3953                         local j
3954                         for j in `seq 1 10`;
3955                         do
3956                                 stat /dev/$dev > /dev/null 2>&1
3957                                 if [ $? -eq 0 ]; then
3958                                         dev_back_oneline=1
3959                                         break
3960                                 fi
3961                                 sleep 1
3962                         done
3963                         break
3964                 else
3965                         sleep 1
3966                 fi
3967         done
3968         if [ $dev_back_oneline -eq 0 ]; then
3969                 echo "/dev/$dev online failed" >> $seqres.full
3970         else
3971                 echo "/dev/$dev is back online" >> $seqres.full
3972         fi
3973 }
3974
3975 _require_fstrim()
3976 {
3977         if [ -z "$FSTRIM_PROG" ]; then
3978                 _notrun "This test requires fstrim utility."
3979         fi
3980 }
3981
3982 _require_batched_discard()
3983 {
3984         if [ $# -ne 1 ]; then
3985                 echo "Usage: _require_batched_discard mnt_point" 1>&2
3986                 exit 1
3987         fi
3988         _require_fstrim
3989         $FSTRIM_PROG $1 > /dev/null 2>&1 || _notrun "FITRIM not supported on $1"
3990 }
3991
3992 _require_dumpe2fs()
3993 {
3994         if [ -z "$DUMPE2FS_PROG" ]; then
3995                 _notrun "This test requires dumpe2fs utility."
3996         fi
3997 }
3998
3999 _require_ugid_map()
4000 {
4001         if [ ! -e /proc/self/uid_map ]; then
4002                 _notrun "This test requires procfs uid_map support."
4003         fi
4004         if [ ! -e /proc/self/gid_map ]; then
4005                 _notrun "This test requires procfs gid_map support."
4006         fi
4007 }
4008
4009 _require_fssum()
4010 {
4011         FSSUM_PROG=$here/src/fssum
4012         [ -x $FSSUM_PROG ] || _notrun "fssum not built"
4013 }
4014
4015 _require_cloner()
4016 {
4017         CLONER_PROG=$here/src/cloner
4018         [ -x $CLONER_PROG ] || \
4019                 _notrun "cloner binary not present at $CLONER_PROG"
4020 }
4021
4022 # Normalize mount options from the option string in $1
4023 # Convert options like "-o opt1,opt2 -oopt3" to "opt1 opt2 opt3"
4024 _normalize_mount_options()
4025 {
4026         echo "$1" | sed -n 's/-o\s*\(\S*\)/\1/gp'| sed 's/,/ /g'
4027 }
4028
4029 # skip test if $1 contains the given strings in trailing arguments
4030 # Both dax and dax=always are excluded if dax or dax=always is passed
4031 _exclude_mount_option()
4032 {
4033         local mnt_opts=$(_normalize_mount_options "$1")
4034
4035         shift
4036         while [ $# -gt 0 ]; do
4037                 local pattern=$1
4038                 echo "$pattern" | egrep -q "dax(=always|$)" && \
4039                         pattern="dax(=always| |$)"
4040                 if echo $mnt_opts | egrep -q "$pattern"; then
4041                         _notrun "mount option \"$1\" not allowed in this test"
4042                 fi
4043                 shift
4044         done
4045 }
4046
4047 _exclude_scratch_mount_option()
4048 {
4049         _exclude_mount_option "$MOUNT_OPTIONS" $@
4050 }
4051
4052 _exclude_test_mount_option()
4053 {
4054         _exclude_mount_option "$TEST_FS_MOUNT_OPTS" $@
4055 }
4056
4057 _require_atime()
4058 {
4059         _exclude_scratch_mount_option "noatime"
4060         case $FSTYP in
4061         nfs|cifs|virtiofs)
4062                 _notrun "atime related mount options have no effect on $FSTYP"
4063                 ;;
4064         esac
4065
4066 }
4067
4068 _require_relatime()
4069 {
4070         _scratch_mkfs > /dev/null 2>&1
4071         _try_scratch_mount -o relatime || \
4072                 _notrun "relatime not supported by the current kernel"
4073         _scratch_unmount
4074 }
4075
4076 _require_userns()
4077 {
4078         [ -x $here/src/nsexec ] || _notrun "src/nsexec executable not found"
4079         $here/src/nsexec -U true 2>/dev/null || _notrun "userns not supported by this kernel"
4080 }
4081
4082 _create_loop_device()
4083 {
4084         local file=$1 dev
4085         dev=`losetup -f --show $file` || _fail "Cannot assign $file to a loop device"
4086
4087         # Try to enable asynchronous directio mode on the loopback device so
4088         # that writeback started by a filesystem mounted on the loop device
4089         # won't be throttled by buffered writes to the lower filesystem.  This
4090         # is a performance optimization for tests that want to write a lot of
4091         # data, so it isn't required to work.
4092         test -b "$dev" && losetup --direct-io=on $dev 2> /dev/null
4093
4094         echo $dev
4095 }
4096
4097 _destroy_loop_device()
4098 {
4099         local dev=$1
4100         losetup -d $dev || _fail "Cannot destroy loop device $dev"
4101 }
4102
4103 _scale_fsstress_args()
4104 {
4105     local args=""
4106     while [ $# -gt 0 ]; do
4107         case "$1" in
4108             -n) args="$args $1 $(($2 * $TIME_FACTOR))"; shift ;;
4109             -p) args="$args $1 $(($2 * $LOAD_FACTOR))"; shift ;;
4110             *) args="$args $1" ;;
4111         esac
4112         shift
4113     done
4114     printf '%s\n' "$args"
4115 }
4116
4117 #
4118 # Return the logical block size if running on a block device,
4119 # else substitute the page size.
4120 #
4121 _min_dio_alignment()
4122 {
4123     local dev=$1
4124
4125     if [ -b "$dev" ]; then
4126         blockdev --getss $dev
4127     else
4128         $here/src/feature -s
4129     fi
4130 }
4131
4132 run_check()
4133 {
4134         echo "# $@" >> $seqres.full 2>&1
4135         "$@" >> $seqres.full 2>&1 || _fail "failed: '$@'"
4136 }
4137
4138 _require_symlinks()
4139 {
4140         local target=`mktemp -p $TEST_DIR`
4141         local link=`mktemp -p $TEST_DIR -u`
4142         ln -s `basename $target` $link
4143         if [ "$?" -ne 0 ]; then
4144                 rm -f $target
4145                 _notrun "Require symlinks support"
4146         fi
4147         rm -f $target $link
4148 }
4149
4150 _require_hardlinks()
4151 {
4152         local target=`mktemp -p $TEST_DIR`
4153         local link=`mktemp -p $TEST_DIR -u`
4154         ln $target $link
4155         if [ "$?" -ne 0 ]; then
4156                 rm -f $target
4157                 _notrun "No hardlink support"
4158         fi
4159         rm -f $target $link
4160 }
4161
4162 _require_test_fcntl_advisory_locks()
4163 {
4164         [ "$FSTYP" != "cifs" ] && return 0
4165         cat /proc/mounts | grep $TEST_DEV | grep cifs | grep -q "nobrl" && return 0
4166         cat /proc/mounts | grep $TEST_DEV | grep cifs | grep -qE "nounix|forcemand" && \
4167                 _notrun "Require fcntl advisory locks support"
4168 }
4169
4170 _require_test_fcntl_setlease()
4171 {
4172         _require_test_program "locktest"
4173         touch $TEST_DIR/setlease_testfile
4174         $here/src/locktest -t $TEST_DIR/setlease_testfile >/dev/null 2>&1
4175         local ret=$?
4176         [ $ret -eq 22 ] && _notrun "Require fcntl setlease support"
4177         [ "$FSTYP" == "nfs" -a $ret -eq 11 ] && \
4178                 _notrun "NFS requires delegation before setlease"
4179 }
4180
4181 _require_ofd_locks()
4182 {
4183         # Give a test run by getlk wrlck on testfile.
4184         # If the running kernel does not support OFD locks,
4185         # EINVAL will be returned.
4186         _require_test_program "t_ofd_locks"
4187         touch $TEST_DIR/ofd_testfile
4188         $here/src/t_ofd_locks -t $TEST_DIR/ofd_testfile > /dev/null 2>&1
4189         [ $? -eq 22 ] && _notrun "Require OFD locks support"
4190 }
4191
4192 _require_test_lsattr()
4193 {
4194         local testio=$(lsattr -d $TEST_DIR 2>&1)
4195         echo $testio | grep -q "Operation not supported" && \
4196                 _notrun "lsattr not supported by test filesystem type: $FSTYP"
4197         echo $testio | grep -q "Inappropriate ioctl for device" && \
4198                 _notrun "lsattr not supported by test filesystem type: $FSTYP"
4199 }
4200
4201 _require_chattr()
4202 {
4203         if [ -z "$1" ]; then
4204                 echo "Usage: _require_chattr <attr>"
4205                 exit 1
4206         fi
4207         local attribute=$1
4208
4209         touch $TEST_DIR/syscalltest
4210         chattr "+$attribute" $TEST_DIR/syscalltest > $TEST_DIR/syscalltest.out 2>&1
4211         local ret=$?
4212         chattr "-$attribute" $TEST_DIR/syscalltest > $TEST_DIR/syscalltest.out 2>&1
4213         if [ "$ret" -ne 0 ]; then
4214                 _notrun "file system doesn't support chattr +$attribute"
4215         fi
4216         cat $TEST_DIR/syscalltest.out >> $seqres.full
4217         rm -f $TEST_DIR/syscalltest.out
4218 }
4219
4220 _get_total_inode()
4221 {
4222         if [ -z "$1" ]; then
4223                 echo "Usage: _get_total_inode <mnt>"
4224                 exit 1
4225         fi
4226         local nr_inode;
4227         nr_inode=`$DF_PROG -i $1 | tail -1 | awk '{print $3}'`
4228         echo $nr_inode
4229 }
4230
4231 _get_used_inode()
4232 {
4233         if [ -z "$1" ]; then
4234                 echo "Usage: _get_used_inode <mnt>"
4235                 exit 1
4236         fi
4237         local nr_inode;
4238         nr_inode=`$DF_PROG -i $1 | tail -1 | awk '{print $4}'`
4239         echo $nr_inode
4240 }
4241
4242 _get_used_inode_percent()
4243 {
4244         if [ -z "$1" ]; then
4245                 echo "Usage: _get_used_inode_percent <mnt>"
4246                 exit 1
4247         fi
4248         local pct_inode;
4249         pct_inode=`$DF_PROG -i $1 | tail -1 | awk '{ print $6 }' | \
4250                    sed -e 's/%//'`
4251         echo $pct_inode
4252 }
4253
4254 _get_free_inode()
4255 {
4256         if [ -z "$1" ]; then
4257                 echo "Usage: _get_free_inode <mnt>"
4258                 exit 1
4259         fi
4260         local nr_inode;
4261         nr_inode=`$DF_PROG -i $1 | tail -1 | awk '{print $5}'`
4262         echo $nr_inode
4263 }
4264
4265 # get the available space in bytes
4266 #
4267 _get_available_space()
4268 {
4269         if [ -z "$1" ]; then
4270                 echo "Usage: _get_available_space <mnt>"
4271                 exit 1
4272         fi
4273         local avail_kb;
4274         avail_kb=`$DF_PROG $1 | tail -n1 | awk '{ print $5 }'`
4275         echo $((avail_kb * 1024))
4276 }
4277
4278 # return device size in kb
4279 _get_device_size()
4280 {
4281         echo $(($(blockdev --getsz $1) >> 1))
4282 }
4283
4284 # Make sure we actually have dmesg checking set up.
4285 _require_check_dmesg()
4286 {
4287         test -w /dev/kmsg || \
4288                 _notrun "Test requires writable /dev/kmsg."
4289 }
4290
4291 # Return the dmesg log since the start of this test.  Caller must ensure that
4292 # /dev/kmsg was writable when the test was started so that we can find the
4293 # beginning of this test's log messages; _require_check_dmesg does this.
4294 _dmesg_since_test_start()
4295 {
4296         # search the dmesg log of last run of $seqnum for possible failures
4297         # use sed \cregexpc address type, since $seqnum contains "/"
4298         dmesg | tac | sed -ne "0,\#run fstests $seqnum at $date_time#p" | \
4299                 tac
4300 }
4301
4302 # check dmesg log for a specific string, subject to the same requirements as
4303 # _dmesg_since_test_start.
4304 _check_dmesg_for()
4305 {
4306         _dmesg_since_test_start | egrep -q "$1"
4307 }
4308
4309 # Default filter for dmesg scanning.
4310 # Ignore lockdep complaining about its own bugginess when scanning dmesg
4311 # output, because we shouldn't be failing filesystem tests on account of
4312 # lockdep.
4313 _check_dmesg_filter()
4314 {
4315         egrep -v -e "BUG: MAX_LOCKDEP_CHAIN_HLOCKS too low" \
4316                 -e "BUG: MAX_STACK_TRACE_ENTRIES too low"
4317 }
4318
4319 # check dmesg log for WARNING/Oops/etc.
4320 _check_dmesg()
4321 {
4322         if [ ! -f ${RESULT_DIR}/check_dmesg ]; then
4323                 return 0
4324         fi
4325         rm -f ${RESULT_DIR}/check_dmesg
4326
4327         # default filter is a simple cat command, caller could provide a
4328         # customized filter and pass the name through the first argument, to
4329         # filter out intentional WARNINGs or Oopses
4330         local filter=${1:-_check_dmesg_filter}
4331
4332         _dmesg_since_test_start | $filter >$seqres.dmesg
4333         egrep -q -e "kernel BUG at" \
4334              -e "WARNING:" \
4335              -e "\bBUG:" \
4336              -e "Oops:" \
4337              -e "possible recursive locking detected" \
4338              -e "Internal error" \
4339              -e "(INFO|ERR): suspicious RCU usage" \
4340              -e "INFO: possible circular locking dependency detected" \
4341              -e "general protection fault:" \
4342              -e "BUG .* remaining" \
4343              -e "UBSAN:" \
4344              $seqres.dmesg
4345         if [ $? -eq 0 ]; then
4346                 _dump_err "_check_dmesg: something found in dmesg (see $seqres.dmesg)"
4347                 return 1
4348         else
4349                 if [ "$KEEP_DMESG" != "yes" ]; then
4350                         rm -f $seqres.dmesg
4351                 fi
4352                 return 0
4353         fi
4354 }
4355
4356 # capture the kmemleak report
4357 _capture_kmemleak()
4358 {
4359         local kern_knob="$DEBUGFS_MNT/kmemleak"
4360         local leak_file="$1"
4361
4362         # Tell the kernel to scan for memory leaks.  Apparently the write
4363         # returns before the scan is complete, so do it twice in the hopes
4364         # that twice is enough to capture all the leaks.
4365         echo "scan" > "$kern_knob"
4366         cat "$kern_knob" > /dev/null
4367         echo "scan" > "$kern_knob"
4368         cat "$kern_knob" > "$leak_file.tmp"
4369         if [ -s "$leak_file.tmp" ]; then
4370                 cat > "$leak_file" << ENDL
4371 EXPERIMENTAL kmemleak reported some memory leaks!  Due to the way kmemleak
4372 works, the leak might be from an earlier test, or something totally unrelated.
4373 ENDL
4374                 cat "$leak_file.tmp" >> "$leak_file"
4375         fi
4376         rm -rf "$leak_file.tmp"
4377         echo "clear" > "$kern_knob"
4378 }
4379
4380 # Figure out if the running kernel supports kmemleak; if it does, clear out
4381 # anything that leaked before we even started testing.  The leak checker only
4382 # needs to be primed like this once per ./check invocation.
4383 _detect_kmemleak()
4384 {
4385         local kern_knob="$DEBUGFS_MNT/kmemleak"
4386         KMEMLEAK_CHECK_FILE="/tmp/check_kmemleak"
4387
4388         # Since kernel v4.19-rc3, the kmemleak knob exists even if kmemleak is
4389         # disabled, but returns EBUSY on write. So instead of relying on
4390         # existance of writable knob file, we use a test file to indicate that
4391         # _check_kmemleak() is enabled only if we actually managed to write to
4392         # the knob file.
4393         rm -f "$KMEMLEAK_CHECK_FILE"
4394
4395         if [ ! -w "$kern_knob" ]; then
4396                 return 0
4397         fi
4398
4399         # Disable the automatic scan so that we can control it completely,
4400         # then dump all the leaks recorded so far.
4401         if echo "scan=off" > "$kern_knob" 2>/dev/null; then
4402                 _capture_kmemleak /dev/null
4403                 touch "$KMEMLEAK_CHECK_FILE"
4404         fi
4405 }
4406
4407 # Kick the kmemleak checker to scan for leaks.  Background leak scan mode is
4408 # not enabled, so we must call the kernel to ask for a scan and deal with the
4409 # results appropriately.  This we do after every test completes, whether or not
4410 # it was successful.
4411 _check_kmemleak()
4412 {
4413         local kern_knob="$DEBUGFS_MNT/kmemleak"
4414         local leak_file="$seqres.kmemleak"
4415
4416         if [ ! -f "$KMEMLEAK_CHECK_FILE" ]; then
4417                 return 0
4418         fi
4419
4420         # Not enabled, so discard any report of leaks found.
4421         if [ "$USE_KMEMLEAK" != "yes" ]; then
4422                 _capture_kmemleak /dev/null
4423                 return 0
4424         fi
4425
4426         # Capture and report any leaks
4427         _capture_kmemleak "$leak_file"
4428         if [ -s "$leak_file" ]; then
4429                 _dump_err "_check_kmemleak: something found in kmemleak (see $leak_file)"
4430                 return 1
4431         else
4432                 rm -f "$leak_file"
4433                 return 0
4434         fi
4435 }
4436
4437 # don't check dmesg log after test
4438 _disable_dmesg_check()
4439 {
4440         rm -f ${RESULT_DIR}/check_dmesg
4441 }
4442
4443 init_rc()
4444 {
4445         # make some further configuration checks here
4446         if [ "$TEST_DEV" = ""  ]
4447         then
4448                 echo "common/rc: Error: \$TEST_DEV is not set"
4449                 exit 1
4450         fi
4451
4452         # if $TEST_DEV is not mounted, mount it now as XFS
4453         if [ -z "`_fs_type $TEST_DEV`" ]
4454         then
4455                 # $TEST_DEV is not mounted
4456                 if ! _test_mount
4457                 then
4458                         echo "common/rc: retrying test device mount with external set"
4459                         [ "$USE_EXTERNAL" != "yes" ] && export USE_EXTERNAL=yes
4460                         if ! _test_mount
4461                         then
4462                                 echo "common/rc: could not mount $TEST_DEV on $TEST_DIR"
4463                                 exit 1
4464                         fi
4465                 fi
4466         fi
4467
4468         # Sanity check that TEST partition is not mounted at another mount point
4469         # or as another fs type
4470         _check_mounted_on TEST_DEV $TEST_DEV TEST_DIR $TEST_DIR $FSTYP || exit 1
4471         if [ -n "$SCRATCH_DEV" ]; then
4472                 # Sanity check that SCRATCH partition is not mounted at another
4473                 # mount point, because it is about to be unmounted and formatted.
4474                 # Another fs type for scratch is fine (bye bye old fs type).
4475                 _check_mounted_on SCRATCH_DEV $SCRATCH_DEV SCRATCH_MNT $SCRATCH_MNT
4476                 [ $? -le 1 ] || exit 1
4477         fi
4478
4479         # Figure out if we need to add -F ("foreign", deprecated) option to xfs_io
4480         $XFS_IO_PROG -c stat $TEST_DIR 2>&1 | grep -q "is not on an XFS filesystem" && \
4481                 export XFS_IO_PROG="$XFS_IO_PROG -F"
4482
4483         # xfs_io -i option starts an idle thread for xfs_io.
4484         # With single threaded process, the file table is not shared
4485         # and file structs are not reference counted.
4486         # Spawning an idle thread can help detecting file struct
4487         # reference leaks, so we want to enable the option whenever
4488         # it is supported.
4489         $XFS_IO_PROG -i -c quit 2>/dev/null && \
4490                 export XFS_IO_PROG="$XFS_IO_PROG -i"
4491
4492         # xfs_copy on v5 filesystems do not require the "-d" option if xfs_db
4493         # can change the UUID on v5 filesystems
4494         if [ "$FSTYP" == "xfs" ]; then
4495                 touch /tmp/$$.img
4496                 $MKFS_XFS_PROG -d file,name=/tmp/$$.img,size=512m >/dev/null 2>&1
4497                 # xfs_db will return 0 even if it can't generate a new uuid, so
4498                 # check the output to make sure if it can change UUID of V5 xfs
4499                 $XFS_DB_PROG -x -c "uuid generate" /tmp/$$.img \
4500                         | grep -q "invalid UUID\|supported on V5 fs" \
4501                         && export XFS_COPY_PROG="$XFS_COPY_PROG -d"
4502                 rm -f /tmp/$$.img
4503         fi
4504 }
4505
4506 # get real device path name by following link
4507 _real_dev()
4508 {
4509         local dev=$1
4510         if [ -b "$dev" ] && [ -L "$dev" ]; then
4511                 dev=`readlink -f "$dev"`
4512         fi
4513         echo $dev
4514 }
4515
4516 # basename of a device
4517 _short_dev()
4518 {
4519         echo `basename $(_real_dev $1)`
4520 }
4521
4522 _sysfs_dev()
4523 {
4524         local dev=`_real_dev $1`
4525         local maj=$(stat -c%t $dev | tr [:lower:] [:upper:])
4526         local min=$(stat -c%T $dev | tr [:lower:] [:upper:])
4527         maj=$(echo "ibase=16; $maj" | bc)
4528         min=$(echo "ibase=16; $min" | bc)
4529         echo /sys/dev/block/$maj:$min
4530 }
4531
4532 # Get the minimum block size of a file.  Usually this is the
4533 # minimum fs block size, but some filesystems (ocfs2) do block
4534 # mappings in larger units.
4535 _get_file_block_size()
4536 {
4537         if [ -z $1 ] || [ ! -d $1 ]; then
4538                 echo "Missing mount point argument for _get_file_block_size"
4539                 exit 1
4540         fi
4541
4542         case "$FSTYP" in
4543         "ocfs2")
4544                 stat -c '%o' $1
4545                 ;;
4546         "xfs")
4547                 _xfs_get_file_block_size $1
4548                 ;;
4549         *)
4550                 _get_block_size $1
4551                 ;;
4552         esac
4553 }
4554
4555 # Get the minimum block size of an fs.
4556 _get_block_size()
4557 {
4558         if [ -z $1 ] || [ ! -d $1 ]; then
4559                 echo "Missing mount point argument for _get_block_size"
4560                 exit 1
4561         fi
4562         stat -f -c %S $1
4563 }
4564
4565 # Require that the fundamental allocation unit of a file is the same as the
4566 # filesystem block size.  The sole parameter must be the root dir of a
4567 # filesystem.
4568 _require_file_block_size_equals_fs_block_size()
4569 {
4570         local file_alloc_unit="$(_get_file_block_size $1)"
4571         local fs_block_size="$(_get_block_size $1)"
4572         test "$file_alloc_unit" != "$fs_block_size" && \
4573                 _notrun "File allocation unit is larger than a filesystem block"
4574 }
4575
4576 get_page_size()
4577 {
4578         echo $(getconf PAGE_SIZE)
4579 }
4580
4581
4582 run_fsx()
4583 {
4584         echo fsx $@
4585         local args=`echo $@ | sed -e "s/ BSIZE / $bsize /g" -e "s/ PSIZE / $psize /g"`
4586         set -- $here/ltp/fsx $args $FSX_AVOID $TEST_DIR/junk
4587         echo "$@" >>$seqres.full
4588         rm -f $TEST_DIR/junk
4589         "$@" 2>&1 | tee -a $seqres.full >$tmp.fsx
4590         if [ ${PIPESTATUS[0]} -ne 0 ]; then
4591                 cat $tmp.fsx
4592                 rm -f $tmp.fsx
4593                 exit 1
4594         fi
4595         rm -f $tmp.fsx
4596 }
4597
4598 # Test for the existence of a sysfs entry at /sys/fs/$FSTYP/DEV/$ATTR
4599 #
4600 # Only one argument is needed:
4601 #  - attr: path name under /sys/fs/$FSTYP/DEV
4602 #
4603 # Usage example:
4604 #   _require_fs_sysfs error/fail_at_unmount
4605 _require_fs_sysfs()
4606 {
4607         local attr=$1
4608         local dname
4609
4610         case "$FSTYP" in
4611         btrfs)
4612                 dname=$(findmnt -n -o UUID $TEST_DEV) ;;
4613         *)
4614                 dname=$(_short_dev $TEST_DEV) ;;
4615         esac
4616
4617         if [ -z "$attr" -o -z "$dname" ];then
4618                 _fail "Usage: _require_fs_sysfs <sysfs_attr_path>"
4619         fi
4620
4621         if [ ! -e /sys/fs/${FSTYP}/${dname}/${attr} ];then
4622                 _notrun "This test requires /sys/fs/${FSTYP}/${dname}/${attr}"
4623         fi
4624 }
4625
4626 _require_statx()
4627 {
4628         $here/src/stat_test --check-statx ||
4629         _notrun "This test requires the statx system call"
4630 }
4631
4632 # Write "content" into /sys/fs/$FSTYP/$DEV/$ATTR
4633 #
4634 # All arguments are necessary, and in this order:
4635 #  - dev: device name, e.g. $SCRATCH_DEV
4636 #  - attr: path name under /sys/fs/$FSTYP/$dev
4637 #  - content: the content of $attr
4638 #
4639 # Usage example:
4640 #   _set_fs_sysfs_attr /dev/mapper/scratch-dev error/fail_at_unmount 0
4641 _set_fs_sysfs_attr()
4642 {
4643         local dev=$1
4644         shift
4645         local attr=$1
4646         shift
4647         local content="$*"
4648
4649         if [ ! -b "$dev" -o -z "$attr" -o -z "$content" ];then
4650                 _fail "Usage: _set_fs_sysfs_attr <mounted_device> <attr> <content>"
4651         fi
4652
4653         local dname
4654         case "$FSTYP" in
4655         btrfs)
4656                 dname=$(findmnt -n -o UUID ${dev}) ;;
4657         *)
4658                 dname=$(_short_dev $dev) ;;
4659         esac
4660
4661         echo "$content" > /sys/fs/${FSTYP}/${dname}/${attr}
4662 }
4663
4664 # Print the content of /sys/fs/$FSTYP/$DEV/$ATTR
4665 #
4666 # All arguments are necessary, and in this order:
4667 #  - dev: device name, e.g. $SCRATCH_DEV
4668 #  - attr: path name under /sys/fs/$FSTYP/$dev
4669 #
4670 # Usage example:
4671 #   _get_fs_sysfs_attr /dev/mapper/scratch-dev error/fail_at_unmount
4672 _get_fs_sysfs_attr()
4673 {
4674         local dev=$1
4675         local attr=$2
4676
4677         if [ ! -b "$dev" -o -z "$attr" ];then
4678                 _fail "Usage: _get_fs_sysfs_attr <mounted_device> <attr>"
4679         fi
4680
4681         local dname
4682         case "$FSTYP" in
4683         btrfs)
4684                 dname=$(findmnt -n -o UUID ${dev}) ;;
4685         *)
4686                 dname=$(_short_dev $dev) ;;
4687         esac
4688
4689         cat /sys/fs/${FSTYP}/${dname}/${attr}
4690 }
4691
4692 # Generic test for specific filesystem feature.
4693 # Currently only implemented to test overlayfs features.
4694 _require_scratch_feature()
4695 {
4696         local feature=$1
4697
4698         case "$FSTYP" in
4699         overlay)
4700                 _require_scratch_overlay_features ${feature}
4701                 ;;
4702         *)
4703                 _fail "Test for feature '${feature}' of ${FSTYP} is not implemented"
4704                 ;;
4705         esac
4706 }
4707
4708 # Get the maximum size of a file in $TEST_DIR (s_maxbytes).  On ext4 this will
4709 # be UINT32_MAX * block_size, but other filesystems may allow up to LLONG_MAX.
4710 _get_max_file_size()
4711 {
4712         local testfile=$TEST_DIR/maxfilesize.$seq
4713         local l=0
4714         local r=9223372036854775807 # LLONG_MAX
4715
4716         rm -f $testfile
4717         while (( l < r )); do
4718                 # Use _math() to avoid signed integer overflow.
4719                 local m=$(_math "($l + $r + 1) / 2")
4720                 if $XFS_IO_PROG -f -c "truncate $m" $testfile \
4721                         |& grep -q 'File too large'
4722                 then
4723                         r=$(( m - 1 ))
4724                 else
4725                         l=$m
4726                 fi
4727         done
4728         echo $l
4729 }
4730
4731 # get MAX_LFS_FILESIZE
4732 _get_max_lfs_filesize()
4733 {
4734         case "$(getconf LONG_BIT)" in
4735         "32")
4736                 local ulong_max=$(getconf ULONG_MAX)
4737                 local page_size=$(getconf PAGE_SIZE)
4738                 echo $(( ulong_max * page_size ))
4739                 ;;
4740         "64")
4741                 echo 9223372036854775807
4742                 ;;
4743         *)
4744                 _fail "sizeof(long) == $(getconf LONG_BIT)?"
4745                 ;;
4746         esac
4747 }
4748
4749 # The maximum filesystem label length, /not/ including terminating NULL
4750 _label_get_max()
4751 {
4752         case $FSTYP in
4753         xfs)
4754                 echo 12
4755                 ;;
4756         btrfs)
4757                 echo 255
4758                 ;;
4759         f2fs)
4760                 echo 255
4761                 ;;
4762         ext2|ext3|ext4)
4763                 echo 16
4764                 ;;
4765         *)
4766                 _notrun "$FSTYP does not define maximum label length"
4767                 ;;
4768         esac
4769 }
4770
4771 # Helper to check above early in a script
4772 _require_label_get_max()
4773 {
4774         # Just call _label_get_max which will notrun if appropriate
4775         dummy=$(_label_get_max)
4776 }
4777
4778 _dmsetup_remove()
4779 {
4780         $UDEV_SETTLE_PROG >/dev/null 2>&1
4781         $DMSETUP_PROG remove "$@" >>$seqres.full 2>&1
4782         $DMSETUP_PROG mknodes >/dev/null 2>&1
4783 }
4784
4785 _dmsetup_create()
4786 {
4787         $DMSETUP_PROG create "$@" >>$seqres.full 2>&1 || return 1
4788         $DMSETUP_PROG mknodes >/dev/null 2>&1
4789         $UDEV_SETTLE_PROG >/dev/null 2>&1
4790 }
4791
4792 _require_btime()
4793 {
4794         # Note: filesystems are not required to report btime (creation time)
4795         # if the caller doesn't ask for it, so we define STATX_BTIME here and
4796         # pass it in to the statx command.
4797         export STATX_BTIME=0x800
4798         $XFS_IO_PROG -f $TEST_DIR/test_creation_time -c "statx -m $STATX_BTIME -v" \
4799                 | grep btime >>$seqres.full 2>&1 || \
4800                 _notrun "inode creation time not supported by this filesystem"
4801         rm -f $TEST_DIR/test_creation_time
4802 }
4803
4804 _require_scratch_btime()
4805 {
4806         _require_scratch
4807         _scratch_mkfs > /dev/null 2>&1
4808         _scratch_mount
4809
4810         # Note: filesystems are not required to report btime (creation time)
4811         # if the caller doesn't ask for it, so we define STATX_BTIME here and
4812         # pass it in to the statx command.
4813         export STATX_BTIME=0x800
4814         $XFS_IO_PROG -f $SCRATCH_MNT/test_creation_time -c "statx -m $STATX_BTIME -v" \
4815                 | grep btime >>$seqres.full 2>&1 || \
4816                 _notrun "inode creation time not supported by this filesystem"
4817
4818         _scratch_unmount
4819 }
4820
4821 _require_inode_limits()
4822 {
4823         if [ $(_get_free_inode $TEST_DIR) -eq 0 ]; then
4824                 _notrun "$FSTYP does not have a fixed number of inodes available"
4825         fi
4826 }
4827
4828 _require_filefrag_options()
4829 {
4830         _require_command "$FILEFRAG_PROG" filefrag
4831
4832         local options=$1
4833         local file="$TEST_DIR/options_testfile"
4834
4835         echo "XX" > $file
4836         ${FILEFRAG_PROG} -$options $file 2>&1 | grep -q "invalid option" && \
4837                 _notrun "filefrag doesn't support $options option"
4838         rm -f $file
4839 }
4840
4841 _require_fibmap()
4842 {
4843         _require_filefrag_options "B"
4844
4845         local file="$TEST_DIR/fibmap_testfile"
4846
4847         echo "XX" > $file
4848         ${FILEFRAG_PROG} -B $file 2>&1 | grep -q "FIBMAP[[:space:]]*unsupported"
4849         if [ $? -eq 0 ]; then
4850                 _notrun "FIBMAP not supported by this filesystem"
4851         fi
4852         rm -f $file
4853 }
4854
4855 _try_wipe_scratch_devs()
4856 {
4857         test -x "$WIPEFS_PROG" || return 0
4858
4859         # Do specified filesystem wipe at first
4860         case "$FSTYP" in
4861         "xfs")
4862                 _try_wipe_scratch_xfs
4863                 ;;
4864         esac
4865
4866         # Then do wipefs on all scratch devices
4867         for dev in $SCRATCH_DEV_POOL $SCRATCH_DEV $SCRATCH_LOGDEV $SCRATCH_RTDEV; do
4868                 test -b $dev && $WIPEFS_PROG -a $dev
4869         done
4870 }
4871
4872 # Only run this on xfs if xfs_scrub is available and has the unicode checker
4873 _check_xfs_scrub_does_unicode() {
4874         [ "${FSTYP}" == "xfs" ] || return 1
4875
4876         local mount="$1"
4877         local dev="$2"
4878
4879         _supports_xfs_scrub "${mount}" "${dev}" || return 1
4880
4881         # Newer versions of xfs_scrub advertise whether or not it supports
4882         # Unicode name checks.
4883         local xfs_scrub_ver="$("${XFS_SCRUB_PROG}" -VV)"
4884
4885         if echo "${xfs_scrub_ver}" | grep -q -- '-Unicode'; then
4886                 return 1
4887         fi
4888
4889         if echo "${xfs_scrub_ver}" | grep -q -- '+Unicode'; then
4890                 return 0
4891         fi
4892
4893         # If the xfs_scrub binary contains the string "Unicode name.*%s", then
4894         # we know that it has the ability to complain about improper Unicode
4895         # names.
4896         if strings "${XFS_SCRUB_PROG}" | grep -q 'Unicode name.*%s'; then
4897                 return 0
4898         fi
4899
4900         # If the xfs_scrub binary is linked against the libicui18n Unicode
4901         # library, then we surmise that it contains the Unicode name checker.
4902         if type ldd > /dev/null 2>&1 && \
4903            ldd "${XFS_SCRUB_PROG}" 2> /dev/null | grep -q libicui18n; then
4904                 return 0
4905         fi
4906
4907         # We could not establish that xfs_scrub supports unicode names.
4908         return 1
4909 }
4910
4911 # exfat timestamps start at 1980 and cannot be prior to epoch
4912 _require_negative_timestamps() {
4913         case "$FSTYP" in
4914         ceph|exfat)
4915                 _notrun "$FSTYP does not support negative timestamps"
4916                 ;;
4917         esac
4918 }
4919
4920 # Require the 'accton' userspace tool and CONFIG_BSD_PROCESS_ACCT=y.
4921 _require_bsd_process_accounting()
4922 {
4923         _require_command "$ACCTON_PROG" accton
4924         $ACCTON_PROG on &> $tmp.test_accton
4925         cat $tmp.test_accton >> $seqres.full
4926         if grep 'Function not implemented' $tmp.test_accton; then
4927                 _notrun "BSD process accounting support unavailable"
4928         fi
4929         $ACCTON_PROG off >> $seqres.full
4930 }
4931
4932 _require_sysctl_variable()
4933 {
4934         local name=$1
4935         sysctl $name &>/dev/null || _notrun "$name sysctl unavailable"
4936 }
4937
4938 _require_mknod()
4939 {
4940         mknod $TEST_DIR/$seq.null c 1 3 \
4941                 || _notrun "$FSTYP does not support mknod/mkfifo"
4942         rm -f $TEST_DIR/$seq.null
4943 }
4944
4945 _getcap()
4946 {
4947         $GETCAP_PROG "$@" | _filter_getcap
4948         return ${PIPESTATUS[0]}
4949 }
4950
4951 _require_od_endian_flag()
4952 {
4953         od --endian=little < /dev/null > /dev/null 2>&1 || \
4954                 _notrun "od does not support endian flag"
4955 }
4956
4957 # Skip this test unless the filesystem treats names (directory entries,
4958 # fs labels, and extended attribute names) as raw byte sequences.
4959 _require_names_are_bytes() {
4960         case "$FSTYP" in
4961         ext2|ext3|ext4|f2fs|xfs|btrfs)
4962                 # do nothing
4963                 ;;
4964         *)
4965                 _notrun "$FSTYP does not allow unrestricted byte streams for names"
4966                 ;;
4967         esac
4968 }
4969
4970 _has_kernel_config()
4971 {
4972         local option=$1
4973         local uname=$(uname -r)
4974         local config_list="$KCONFIG_PATH
4975                      /proc/config.gz
4976                      /lib/modules/$uname/build/.config
4977                      /boot/config-$uname
4978                      /lib/kernel/config-$uname"
4979
4980         for config in $config_list; do
4981                 [ ! -f $config ] && continue
4982                 [ $config = "/proc/config.gz" ] && break
4983                 grep -qE "^${option}=[my]" $config
4984                 return
4985         done
4986
4987         [ ! -f $config ] && _notrun "Could not locate kernel config file"
4988
4989         # We can only get here with /proc/config.gz
4990         _require_command "$GZIP_PROG" gzip
4991         $GZIP_PROG -cd $config | grep -qE "^${option}=[my]"
4992 }
4993
4994 _require_kernel_config()
4995 {
4996         _has_kernel_config $1 || _notrun "Installed kernel not built with $1"
4997 }
4998
4999 _hexdump()
5000 {
5001         # Hex format address and data output
5002         od -Ax -t x1z $*
5003 }
5004
5005 # Disable hexdump, turn to use "od" command in _hexdump
5006 hexdump()
5007 {
5008         _fail "Use _hexdump(), please!"
5009 }
5010
5011 init_rc
5012
5013 ################################################################################
5014 # make sure this script returns success
5015 /bin/true