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