xfs/23[12]: abstractify the XFS cow prealloc trimming interval
[xfstests-dev.git] / common / xfs
1 #
2 # XFS specific common functions.
3 #
4
5 _setup_large_xfs_fs()
6 {
7         fs_size=$1
8         local tmp_dir=/tmp/
9
10         [ "$LARGE_SCRATCH_DEV" != yes ] && return 0
11         [ -z "$SCRATCH_DEV_EMPTY_SPACE" ] && SCRATCH_DEV_EMPTY_SPACE=0
12         [ $SCRATCH_DEV_EMPTY_SPACE -ge $fs_size ] && return 0
13
14         # calculate the size of the file we need to allocate.
15         # Default free space in the FS is 50GB, but you can specify more via
16         # SCRATCH_DEV_EMPTY_SPACE
17         file_size=$(($fs_size - 50*1024*1024*1024))
18         file_size=$(($file_size - $SCRATCH_DEV_EMPTY_SPACE))
19
20         # mount the filesystem, create the file, unmount it
21         _try_scratch_mount 2>&1 >$tmp_dir/mnt.err
22         local status=$?
23         if [ $status -ne 0 ]; then
24                 echo "mount failed"
25                 cat $tmp_dir/mnt.err >&2
26                 rm -f $tmp_dir/mnt.err
27                 return $status
28         fi
29         rm -f $tmp_dir/mnt.err
30
31         xfs_io -F -f \
32                 -c "truncate $file_size" \
33                 -c "falloc -k 0 $file_size" \
34                 -c "chattr +d" \
35                 $SCRATCH_MNT/.use_space 2>&1 > /dev/null
36         export NUM_SPACE_FILES=1
37         status=$?
38         _scratch_unmount
39         if [ $status -ne 0 ]; then
40                 echo "large file prealloc failed"
41                 cat $tmp_dir/mnt.err >&2
42                 return $status
43         fi
44         return 0
45 }
46
47 _scratch_mkfs_xfs_opts()
48 {
49         mkfs_opts=$*
50
51         # remove metadata related mkfs options if mkfs.xfs doesn't them
52         if [ -n "$XFS_MKFS_HAS_NO_META_SUPPORT" ]; then
53                 mkfs_opts=`echo $mkfs_opts | sed "s/-m\s\+\S\+//g"`
54         fi
55
56         _scratch_options mkfs
57
58         echo "$MKFS_XFS_PROG $SCRATCH_OPTIONS $mkfs_opts"
59 }
60
61
62 _scratch_mkfs_xfs_supported()
63 {
64         local mkfs_opts=$*
65
66         _scratch_options mkfs
67
68         $MKFS_XFS_PROG -N $MKFS_OPTIONS $SCRATCH_OPTIONS $mkfs_opts $SCRATCH_DEV
69         local mkfs_status=$?
70
71         # a mkfs failure may be caused by conflicts between $MKFS_OPTIONS and
72         # $mkfs_opts, try again without $MKFS_OPTIONS
73         if [ $mkfs_status -ne 0 -a -n "$mkfs_opts" ]; then
74                 $MKFS_XFS_PROG -N $SCRATCH_OPTIONS $mkfs_opts $SCRATCH_DEV
75                 mkfs_status=$?
76         fi
77         return $mkfs_status
78 }
79
80 # Returns the minimum XFS log size, in units of log blocks.
81 _scratch_find_xfs_min_logblocks()
82 {
83         local mkfs_cmd="`_scratch_mkfs_xfs_opts`"
84
85         # The smallest log size we can specify is 2M (XFS_MIN_LOG_BYTES) so
86         # pass that in and see if mkfs succeeds or tells us what is the
87         # minimum log size.
88         local XFS_MIN_LOG_BYTES=2097152
89
90         # Try formatting the filesystem with all the options given and the
91         # minimum log size.  We hope either that this succeeds or that mkfs
92         # tells us the required minimum log size for the feature set.
93         #
94         # We cannot use _scratch_do_mkfs because it will retry /any/ failed
95         # mkfs with MKFS_OPTIONS removed even if the only "failure" was that
96         # the log was too small.
97         local extra_mkfs_options="$* -N -l size=$XFS_MIN_LOG_BYTES"
98         eval "$mkfs_cmd $MKFS_OPTIONS $extra_mkfs_options $SCRATCH_DEV" \
99                 2>$tmp.mkfserr 1>$tmp.mkfsstd
100         local mkfs_status=$?
101
102         # If the format fails for a reason other than the log being too small,
103         # try again without MKFS_OPTIONS because that's what _scratch_do_mkfs
104         # will do if we pass in the log size option.
105         if [ $mkfs_status -ne 0 ] &&
106            ! egrep -q '(log size.*too small, minimum|external log device.*too small, must be)' $tmp.mkfserr; then
107                 eval "$mkfs_cmd $extra_mkfs_options $SCRATCH_DEV" \
108                         2>$tmp.mkfserr 1>$tmp.mkfsstd
109                 mkfs_status=$?
110         fi
111
112         # mkfs suceeded, so we must pick out the log block size to do the
113         # unit conversion
114         if [ $mkfs_status -eq 0 ]; then
115                 blksz="$(grep '^log.*bsize' $tmp.mkfsstd | \
116                         sed -e 's/log.*bsize=\([0-9]*\).*$/\1/g')"
117                 echo $((XFS_MIN_LOG_BYTES / blksz))
118                 rm -f $tmp.mkfsstd $tmp.mkfserr
119                 return
120         fi
121
122         # Usually mkfs will tell us the minimum log size...
123         if grep -q 'minimum size is' $tmp.mkfserr; then
124                 grep 'minimum size is' $tmp.mkfserr | \
125                         sed -e 's/^.*minimum size is \([0-9]*\) blocks/\1/g'
126                 rm -f $tmp.mkfsstd $tmp.mkfserr
127                 return
128         fi
129         if grep -q 'external log device.*too small, must be' $tmp.mkfserr; then
130                 grep 'external log device.*too small, must be' $tmp.mkfserr | \
131                         sed -e 's/^.*must be at least \([0-9]*\) blocks/\1/g'
132                 rm -f $tmp.mkfsstd $tmp.mkfserr
133                 return
134         fi
135
136         # Don't know what to do, so fail
137         echo "Cannot determine minimum log size" >&2
138         cat $tmp.mkfsstd >> $seqres.full
139         cat $tmp.mkfserr >> $seqres.full
140         rm -f $tmp.mkfsstd $tmp.mkfserr
141 }
142
143 _scratch_mkfs_xfs()
144 {
145         local mkfs_cmd="`_scratch_mkfs_xfs_opts`"
146         local mkfs_filter="sed -e '/less than device physical sector/d' \
147                                -e '/switching to logical sector/d' \
148                                -e '/Default configuration/d'"
149         local tmp=`mktemp -u`
150         local mkfs_status
151
152         _scratch_do_mkfs "$mkfs_cmd" "$mkfs_filter" $* 2>$tmp.mkfserr 1>$tmp.mkfsstd
153         mkfs_status=$?
154
155         grep -q crc=0 $tmp.mkfsstd && _force_xfsv4_mount_options
156
157         if [ $mkfs_status -eq 0 -a "$LARGE_SCRATCH_DEV" = yes ]; then
158                 # manually parse the mkfs output to get the fs size in bytes
159                 local fs_size
160                 fs_size=`cat $tmp.mkfsstd | perl -ne '
161                         if (/^data\s+=\s+bsize=(\d+)\s+blocks=(\d+)/) {
162                                 my $size = $1 * $2;
163                                 print STDOUT "$size\n";
164                         }'`
165                 _setup_large_xfs_fs $fs_size
166                 mkfs_status=$?
167         fi
168
169         # output mkfs stdout and stderr
170         cat $tmp.mkfsstd
171         cat $tmp.mkfserr >&2
172         rm -f $tmp.mkfserr $tmp.mkfsstd
173
174         return $mkfs_status
175 }
176
177 # Get the size of an allocation unit of a file.  Normally this is just the
178 # block size of the file, but for realtime files, this is the realtime extent
179 # size.
180 _xfs_get_file_block_size()
181 {
182         local path="$1"
183
184         if ! ($XFS_IO_PROG -c "stat -v" "$path" 2>&1 | egrep -q '(rt-inherit|realtime)'); then
185                 _get_block_size "$path"
186                 return
187         fi
188
189         # Otherwise, call xfs_info until we find a mount point or the root.
190         path="$(readlink -m "$path")"
191         while ! $XFS_INFO_PROG "$path" &>/dev/null && [ "$path" != "/" ]; do
192                 path="$(dirname "$path")"
193         done
194         $XFS_INFO_PROG "$path" | grep realtime | sed -e 's/^.*extsz=\([0-9]*\).*$/\1/g'
195 }
196
197 _xfs_get_fsxattr()
198 {
199         local field="$1"
200         local path="$2"
201
202         local value=$($XFS_IO_PROG -c "stat" "$path" | grep -w "$field")
203         echo ${value##fsxattr.${field} = }
204 }
205
206 # xfs_check script is planned to be deprecated. But, we want to
207 # be able to invoke "xfs_check" behavior in xfstests in order to
208 # maintain the current verification levels.
209 _xfs_check()
210 {
211         OPTS=" "
212         DBOPTS=" "
213         USAGE="Usage: xfs_check [-fsvV] [-l logdev] [-i ino]... [-b bno]... special"
214
215         OPTIND=1
216         while getopts "b:fi:l:stvV" c; do
217                 case $c in
218                         s) OPTS=$OPTS"-s ";;
219                         t) OPTS=$OPTS"-t ";;
220                         v) OPTS=$OPTS"-v ";;
221                         i) OPTS=$OPTS"-i "$OPTARG" ";;
222                         b) OPTS=$OPTS"-b "$OPTARG" ";;
223                         f) DBOPTS=$DBOPTS" -f";;
224                         l) DBOPTS=$DBOPTS" -l "$OPTARG" ";;
225                         V) $XFS_DB_PROG -p xfs_check -V
226                            return $?
227                            ;;
228                 esac
229         done
230         set -- extra $@
231         shift $OPTIND
232         case $# in
233                 1) ${XFS_DB_PROG}${DBOPTS} -F -i -p xfs_check -c "check$OPTS" $1
234                    status=$?
235                    ;;
236                 2) echo $USAGE 1>&1
237                    status=2
238                    ;;
239         esac
240         return $status
241 }
242
243 _scratch_xfs_db_options()
244 {
245         SCRATCH_OPTIONS=""
246         [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
247                 SCRATCH_OPTIONS="-l$SCRATCH_LOGDEV"
248         echo $SCRATCH_OPTIONS $* $SCRATCH_DEV
249 }
250
251 _scratch_xfs_db()
252 {
253         $XFS_DB_PROG "$@" $(_scratch_xfs_db_options)
254 }
255
256 _test_xfs_db_options()
257 {
258         TEST_OPTIONS=""
259         [ "$USE_EXTERNAL" = yes -a ! -z "$TEST_LOGDEV" ] && \
260                 TEST_OPTIONS="-l$TEST_LOGDEV"
261         echo $TEST_OPTIONS $* $TEST_DEV
262 }
263
264 _test_xfs_db()
265 {
266         $XFS_DB_PROG "$@" $(_test_xfs_db_options)
267 }
268
269 _scratch_xfs_admin()
270 {
271         local options=("$SCRATCH_DEV")
272         [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
273                 options+=("$SCRATCH_LOGDEV")
274         $XFS_ADMIN_PROG "$@" "${options[@]}"
275 }
276
277 _scratch_xfs_logprint()
278 {
279         SCRATCH_OPTIONS=""
280         [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
281                 SCRATCH_OPTIONS="-l$SCRATCH_LOGDEV"
282         $XFS_LOGPRINT_PROG $SCRATCH_OPTIONS $* $SCRATCH_DEV
283 }
284
285 _test_xfs_logprint()
286 {
287         TEST_OPTIONS=""
288         [ "$USE_EXTERNAL" = yes -a ! -z "$TEST_LOGDEV" ] && \
289                 TEST_OPTIONS="-l$TEST_LOGDEV"
290         $XFS_LOGPRINT_PROG $TEST_OPTIONS $* $TEST_DEV
291 }
292
293 _scratch_xfs_check()
294 {
295         SCRATCH_OPTIONS=""
296         [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
297                 SCRATCH_OPTIONS="-l $SCRATCH_LOGDEV"
298         [ "$LARGE_SCRATCH_DEV" = yes ] && \
299                 SCRATCH_OPTIONS=$SCRATCH_OPTIONS" -t"
300         _xfs_check $SCRATCH_OPTIONS $* $SCRATCH_DEV
301 }
302
303 _scratch_xfs_repair()
304 {
305         SCRATCH_OPTIONS=""
306         [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
307                 SCRATCH_OPTIONS="-l$SCRATCH_LOGDEV"
308         [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
309                 SCRATCH_OPTIONS=$SCRATCH_OPTIONS" -r$SCRATCH_RTDEV"
310         $XFS_REPAIR_PROG $SCRATCH_OPTIONS $* $SCRATCH_DEV
311 }
312
313 # this test requires the projid32bit feature to be available in mkfs.xfs.
314 #
315 _require_projid32bit()
316 {
317        _scratch_mkfs_xfs_supported -i projid32bit=1 >/dev/null 2>&1 \
318            || _notrun "mkfs.xfs doesn't have projid32bit feature"
319 }
320
321 _require_projid16bit()
322 {
323         _scratch_mkfs_xfs_supported -i projid32bit=0 >/dev/null 2>&1 \
324            || _notrun "16 bit project IDs not supported on $SCRATCH_DEV"
325 }
326
327 # this test requires the crc feature to be available in mkfs.xfs
328 #
329 _require_xfs_mkfs_crc()
330 {
331         _scratch_mkfs_xfs_supported -m crc=1 >/dev/null 2>&1 \
332            || _notrun "mkfs.xfs doesn't have crc feature"
333 }
334
335 # this test requires the xfs kernel support crc feature
336 #
337 _require_xfs_crc()
338 {
339         _scratch_mkfs_xfs -m crc=1 >/dev/null 2>&1
340         _try_scratch_mount >/dev/null 2>&1 \
341            || _notrun "Kernel doesn't support crc feature"
342         _scratch_unmount
343 }
344
345 # this test requires the xfs kernel support crc feature on scratch device
346 #
347 _require_scratch_xfs_crc()
348 {
349         _scratch_mkfs_xfs >/dev/null 2>&1
350         _try_scratch_mount >/dev/null 2>&1 \
351            || _notrun "Kernel doesn't support crc feature"
352         $XFS_INFO_PROG $SCRATCH_MNT | grep -q 'crc=1' || _notrun "crc feature not supported by this filesystem"
353         _scratch_unmount
354 }
355
356 # this test requires the finobt feature to be available in mkfs.xfs
357 #
358 _require_xfs_mkfs_finobt()
359 {
360         _scratch_mkfs_xfs_supported -m crc=1,finobt=1 >/dev/null 2>&1 \
361            || _notrun "mkfs.xfs doesn't have finobt feature"
362 }
363
364 # this test requires the xfs kernel support finobt feature
365 #
366 _require_xfs_finobt()
367 {
368         _scratch_mkfs_xfs -m crc=1,finobt=1 >/dev/null 2>&1
369         _try_scratch_mount >/dev/null 2>&1 \
370            || _notrun "Kernel doesn't support finobt feature"
371         _scratch_unmount
372 }
373
374 # this test requires xfs sysfs attribute support
375 #
376 _require_xfs_sysfs()
377 {
378         attr=$1
379         sysfsdir=/sys/fs/xfs
380
381         if [ ! -e $sysfsdir ]; then
382                 _notrun "no kernel support for XFS sysfs attributes"
383         fi
384
385         if [ ! -z $1 ] && [ ! -e $sysfsdir/$attr ]; then
386                 _notrun "sysfs attribute '$attr' is not supported"
387         fi
388 }
389
390 # this test requires the xfs sparse inode feature
391 #
392 _require_xfs_sparse_inodes()
393 {
394         _scratch_mkfs_xfs_supported -m crc=1 -i sparse > /dev/null 2>&1 \
395                 || _notrun "mkfs.xfs does not support sparse inodes"
396         _scratch_mkfs_xfs -m crc=1 -i sparse > /dev/null 2>&1
397         _try_scratch_mount >/dev/null 2>&1 \
398                 || _notrun "kernel does not support sparse inodes"
399         _scratch_unmount
400 }
401
402 # check that xfs_db supports a specific command
403 _require_xfs_db_command()
404 {
405         if [ $# -ne 1 ]; then
406                 echo "Usage: _require_xfs_db_command command" 1>&2
407                 exit 1
408         fi
409         command=$1
410
411         _scratch_mkfs_xfs >/dev/null 2>&1
412         _scratch_xfs_db -x -c "help" | grep $command > /dev/null || \
413                 _notrun "xfs_db $command support is missing"
414 }
415
416 # Does the filesystem mounted from a particular device support scrub?
417 _supports_xfs_scrub()
418 {
419         local mountpoint="$1"
420         local device="$2"
421
422         if [ -z "$device" ] || [ -z "$mountpoint" ]; then
423                 echo "Usage: _supports_xfs_scrub mountpoint device"
424                 return 1
425         fi
426
427         if [ ! -b "$device" ] || [ ! -e "$mountpoint" ]; then
428                 return 1
429         fi
430
431         test "$FSTYP" = "xfs" || return 1
432         test -x "$XFS_SCRUB_PROG" || return 1
433
434         # Probe for kernel support...
435         $XFS_IO_PROG -c 'help scrub' 2>&1 | grep -q 'types are:.*probe' || return 1
436         $XFS_IO_PROG -c "scrub probe" "$mountpoint" 2>&1 | grep -q "Inappropriate ioctl" && return 1
437
438         # Scrub can't run on norecovery mounts
439         _fs_options "$device" | grep -q "norecovery" && return 1
440
441         return 0
442 }
443
444 # Save a snapshot of a corrupt xfs filesystem for later debugging.
445 _xfs_metadump() {
446         local metadump="$1"
447         local device="$2"
448         local logdev="$3"
449         local compressopt="$4"
450         shift; shift; shift; shift
451         local options="$@"
452         test -z "$options" && options="-a -o"
453
454         if [ "$logdev" != "none" ]; then
455                 options="$options -l $logdev"
456         fi
457
458         $XFS_METADUMP_PROG $options "$device" "$metadump"
459         res=$?
460         [ "$compressopt" = "compress" ] && [ -n "$DUMP_COMPRESSOR" ] &&
461                 $DUMP_COMPRESSOR "$metadump" &> /dev/null
462         return $res
463 }
464
465 # Snapshot the metadata on the scratch device
466 _scratch_xfs_metadump()
467 {
468         local metadump=$1
469         shift
470         local logdev=none
471
472         [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
473                 logdev=$SCRATCH_LOGDEV
474
475         _xfs_metadump "$metadump" "$SCRATCH_DEV" "$logdev" nocompress "$@"
476 }
477
478 # run xfs_check and friends on a FS.
479 _check_xfs_filesystem()
480 {
481         if [ $# -ne 3 ]; then
482                 echo "Usage: _check_xfs_filesystem device <logdev>|none <rtdev>|none" 1>&2
483                 exit 1
484         fi
485
486         extra_mount_options=""
487         extra_log_options=""
488         extra_options=""
489         device=$1
490         if [ -f $device ]; then
491                 extra_options="-f"
492         fi
493
494         local logdev="$2"
495         if [ "$logdev" != "none" ]; then
496                 extra_log_options="-l$logdev"
497                 extra_mount_options="-ologdev=$logdev"
498         fi
499
500         local rtdev="$3"
501         if [ "$rtdev" != "none" ]; then
502                 extra_rt_options="-r$rtdev"
503                 extra_mount_options=$extra_mount_options" -ortdev=$rtdev"
504         fi
505         extra_mount_options=$extra_mount_options" $MOUNT_OPTIONS"
506
507         [ "$FSTYP" != xfs ] && return 0
508
509         type=`_fs_type $device`
510         ok=1
511
512         # Run online scrub if we can.
513         mntpt="$(_is_dev_mounted $device)"
514         if [ -n "$mntpt" ] && _supports_xfs_scrub "$mntpt" "$device"; then
515                 # Tests can create a scenario in which a call to syncfs() issued
516                 # at the end of the execution of the test script would return an
517                 # error code. xfs_scrub internally calls syncfs() before
518                 # starting the actual online consistency check operation. Since
519                 # such a call to syncfs() fails, xfs_scrub ends up returning
520                 # without performing consistency checks on the test
521                 # filesystem. This can mask a possible on-disk data structure
522                 # corruption. Hence consume such a possible syncfs() failure
523                 # before executing a scrub operation.
524                 $XFS_IO_PROG -c syncfs $mntpt >> $seqres.full 2>&1
525
526                 "$XFS_SCRUB_PROG" $scrubflag -v -d -n $mntpt > $tmp.scrub 2>&1
527                 if [ $? -ne 0 ]; then
528                         _log_err "_check_xfs_filesystem: filesystem on $device failed scrub"
529                         echo "*** xfs_scrub $scrubflag -v -d -n output ***" >> $seqres.full
530                         cat $tmp.scrub >> $seqres.full
531                         echo "*** end xfs_scrub output" >> $serqres.full
532                         ok=0
533                 fi
534                 rm -f $tmp.scrub
535         fi
536
537         if [ "$type" = "xfs" ]; then
538                 # mounted ...
539                 mountpoint=`_umount_or_remount_ro $device`
540         fi
541
542         $XFS_LOGPRINT_PROG -t $extra_log_options $device 2>&1 \
543                 | tee $tmp.logprint | grep -q "<CLEAN>"
544         if [ $? -ne 0 ]; then
545                 _log_err "_check_xfs_filesystem: filesystem on $device has dirty log"
546                 echo "*** xfs_logprint -t output ***"   >>$seqres.full
547                 cat $tmp.logprint                       >>$seqres.full
548                 echo "*** end xfs_logprint output"      >>$seqres.full
549
550                 ok=0
551         fi
552
553         # xfs_check runs out of memory on large files, so even providing the test
554         # option (-t) to avoid indexing the free space trees doesn't make it pass on
555         # large filesystems. Avoid it.
556         if [ "$LARGE_SCRATCH_DEV" != yes ]; then
557                 _xfs_check $extra_log_options $device 2>&1 > $tmp.fs_check
558         fi
559         if [ -s $tmp.fs_check ]; then
560                 _log_err "_check_xfs_filesystem: filesystem on $device is inconsistent (c)"
561                 echo "*** xfs_check output ***"         >>$seqres.full
562                 cat $tmp.fs_check                       >>$seqres.full
563                 echo "*** end xfs_check output"         >>$seqres.full
564
565                 ok=0
566         fi
567
568         $XFS_REPAIR_PROG -n $extra_options $extra_log_options $extra_rt_options $device >$tmp.repair 2>&1
569         if [ $? -ne 0 ]; then
570                 _log_err "_check_xfs_filesystem: filesystem on $device is inconsistent (r)"
571                 echo "*** xfs_repair -n output ***"     >>$seqres.full
572                 cat $tmp.repair                         >>$seqres.full
573                 echo "*** end xfs_repair output"        >>$seqres.full
574
575                 ok=0
576         fi
577         rm -f $tmp.fs_check $tmp.logprint $tmp.repair
578
579         if [ "$ok" -ne 1 ] && [ "$DUMP_CORRUPT_FS" = "1" ]; then
580                 local flatdev="$(basename "$device")"
581                 _xfs_metadump "$seqres.$flatdev.check.md" "$device" "$logdev" \
582                         compress >> $seqres.full
583         fi
584
585         # Optionally test the index rebuilding behavior.
586         if [ -n "$TEST_XFS_REPAIR_REBUILD" ]; then
587                 rebuild_ok=1
588                 $XFS_REPAIR_PROG $extra_options $extra_log_options $extra_rt_options $device >$tmp.repair 2>&1
589                 if [ $? -ne 0 ]; then
590                         _log_err "_check_xfs_filesystem: filesystem on $device is inconsistent (rebuild)"
591                         echo "*** xfs_repair output ***"        >>$seqres.full
592                         cat $tmp.repair                         >>$seqres.full
593                         echo "*** end xfs_repair output"        >>$seqres.full
594
595                         ok=0
596                         rebuild_ok=0
597                 fi
598                 rm -f $tmp.repair
599
600                 $XFS_REPAIR_PROG -n $extra_options $extra_log_options $extra_rt_options $device >$tmp.repair 2>&1
601                 if [ $? -ne 0 ]; then
602                         _log_err "_check_xfs_filesystem: filesystem on $device is inconsistent (rebuild-reverify)"
603                         echo "*** xfs_repair -n output ***"     >>$seqres.full
604                         cat $tmp.repair                         >>$seqres.full
605                         echo "*** end xfs_repair output"        >>$seqres.full
606
607                         ok=0
608                         rebuild_ok=0
609                 fi
610                 rm -f $tmp.repair
611
612                 if [ "$rebuild_ok" -ne 1 ] && [ "$DUMP_CORRUPT_FS" = "1" ]; then
613                         local flatdev="$(basename "$device")"
614                         _xfs_metadump "$seqres.$flatdev.rebuild.md" "$device" \
615                                 "$logdev" compress >> $seqres.full
616                 fi
617         fi
618
619         if [ $ok -eq 0 ]; then
620                 echo "*** mount output ***"             >>$seqres.full
621                 _mount                                  >>$seqres.full
622                 echo "*** end mount output"             >>$seqres.full
623         elif [ "$type" = "xfs" ]; then
624                 _mount_or_remount_rw "$extra_mount_options" $device $mountpoint
625         fi
626
627         if [ $ok -eq 0 ]; then
628                 status=1
629                 if [ "$iam" != "check" ]; then
630                         exit 1
631                 fi
632                 return 1
633         fi
634
635         return 0
636 }
637
638 _check_xfs_test_fs()
639 {
640         TEST_LOG="none"
641         TEST_RT="none"
642         [ "$USE_EXTERNAL" = yes -a ! -z "$TEST_LOGDEV" ] && \
643                 TEST_LOG="$TEST_LOGDEV"
644
645         [ "$USE_EXTERNAL" = yes -a ! -z "$TEST_RTDEV" ] && \
646                 TEST_RT="$TEST_RTDEV"
647
648         _check_xfs_filesystem $TEST_DEV $TEST_LOG $TEST_RT
649         return $?
650 }
651
652 _require_xfs_test_rmapbt()
653 {
654         _require_test
655
656         if [ "$($XFS_INFO_PROG "$TEST_DIR" | grep -c "rmapbt=1")" -ne 1 ]; then
657                 _notrun "rmapbt not supported by test filesystem type: $FSTYP"
658         fi
659 }
660
661 _require_xfs_scratch_rmapbt()
662 {
663         _require_scratch
664
665         _scratch_mkfs > /dev/null
666         _scratch_mount
667         if [ "$($XFS_INFO_PROG "$SCRATCH_MNT" | grep -c "rmapbt=1")" -ne 1 ]; then
668                 _scratch_unmount
669                 _notrun "rmapbt not supported by scratch filesystem type: $FSTYP"
670         fi
671         _scratch_unmount
672 }
673
674 _xfs_bmapx_find()
675 {
676         case "$1" in
677         "attr")
678                 param="a"
679                 ;;
680         "cow")
681                 param="c"
682                 ;;
683         *)
684                 param="e"
685                 ;;
686         esac
687         shift
688         file="$1"
689         shift
690
691         $XFS_IO_PROG -c "bmap -${param}lpv" "$file" | grep -c "$@"
692 }
693
694 # Reset all xfs error handling attributes, set them to original
695 # status.
696 #
697 # Only one argument, and it's mandatory:
698 #  - dev: device name, e.g. $SCRATCH_DEV
699 #
700 # Note: this function only works for XFS
701 _reset_xfs_sysfs_error_handling()
702 {
703         local dev=$1
704
705         if [ ! -b "$dev" -o "$FSTYP" != "xfs" ]; then
706                 _fail "Usage: reset_xfs_sysfs_error_handling <device>"
707         fi
708
709         _set_fs_sysfs_attr $dev error/fail_at_unmount 1
710         echo -n "error/fail_at_unmount="
711         _get_fs_sysfs_attr $dev error/fail_at_unmount
712
713         # Make sure all will be configured to retry forever by default, except
714         # for ENODEV, which is an unrecoverable error, so it will be configured
715         # to not retry on error by default.
716         for e in default EIO ENOSPC; do
717                 _set_fs_sysfs_attr $dev \
718                                    error/metadata/${e}/max_retries -1
719                 echo -n "error/metadata/${e}/max_retries="
720                 _get_fs_sysfs_attr $dev error/metadata/${e}/max_retries
721
722                 _set_fs_sysfs_attr $dev \
723                                    error/metadata/${e}/retry_timeout_seconds 0
724                 echo -n "error/metadata/${e}/retry_timeout_seconds="
725                 _get_fs_sysfs_attr $dev \
726                                    error/metadata/${e}/retry_timeout_seconds
727         done
728 }
729
730 # Skip if we are running an older binary without the stricter input checks.
731 # Make multiple checks to be sure that there is no regression on the one
732 # selected feature check, which would skew the result.
733 #
734 # At first, make a common function that runs the tests and returns
735 # number of failed cases.
736 _xfs_mkfs_validation_check()
737 {
738         local tmpfile=`mktemp`
739         local cmd="$MKFS_XFS_PROG -f -N -d file,name=$tmpfile,size=1g"
740
741         $cmd -s size=8s >/dev/null 2>&1
742         local sum=$?
743
744         $cmd -l version=2,su=260k >/dev/null 2>&1
745         sum=`expr $sum + $?`
746
747         rm -f $tmpfile
748         return $sum
749 }
750
751 # Skip the test if all calls passed - mkfs accepts invalid input
752 _require_xfs_mkfs_validation()
753 {
754         _xfs_mkfs_validation_check
755         if [ "$?" -eq 0 ]; then
756                 _notrun "Requires newer mkfs with stricter input checks: the oldest supported version of xfsprogs is 4.7."
757         fi
758 }
759
760 # The opposite of _require_xfs_mkfs_validation.
761 _require_xfs_mkfs_without_validation()
762 {
763         _xfs_mkfs_validation_check
764         if [ "$?" -ne 0 ]; then
765                 _notrun "Requires older mkfs without strict input checks: the last supported version of xfsprogs is 4.5."
766         fi
767 }
768
769 # XFS ability to change UUIDs on V5/CRC filesystems
770 #
771 _require_meta_uuid()
772 {
773         # This will create a crc fs on $SCRATCH_DEV
774         _require_xfs_crc
775
776         _scratch_xfs_db -x -c "uuid restore" 2>&1 \
777            | grep -q "invalid UUID\|supported on V5 fs" \
778            && _notrun "Userspace doesn't support meta_uuid feature"
779
780         _scratch_xfs_db -x -c "uuid generate" >/dev/null 2>&1
781
782         _try_scratch_mount >/dev/null 2>&1 \
783            || _notrun "Kernel doesn't support meta_uuid feature"
784         _scratch_unmount
785 }
786
787 # this test requires mkfs.xfs have case-insensitive naming support
788 _require_xfs_mkfs_ciname()
789 {
790         _scratch_mkfs_xfs_supported -n version=ci >/dev/null 2>&1 \
791                 || _notrun "need case-insensitive naming support in mkfs.xfs"
792 }
793
794 # this test requires mkfs.xfs have configuration file support
795 _require_xfs_mkfs_cfgfile()
796 {
797         echo > /tmp/a
798         _scratch_mkfs_xfs_supported -c options=/tmp/a >/dev/null 2>&1
799         res=$?
800         rm -rf /tmp/a
801         test $res -eq 0 || _notrun "need configuration file support in mkfs.xfs"
802 }
803
804 # XFS_DEBUG requirements
805 _require_xfs_debug()
806 {
807         if grep -q "debug 0" /proc/fs/xfs/stat; then
808                 _notrun "Require XFS built with CONFIG_XFS_DEBUG"
809         fi
810 }
811 _require_no_xfs_debug()
812 {
813         if grep -q "debug 1" /proc/fs/xfs/stat; then
814                 _notrun "Require XFS built without CONFIG_XFS_DEBUG"
815         fi
816 }
817
818 # Require that assertions will not crash the system.
819 #
820 # Assertions would always crash the system if XFS assert fatal was enabled
821 # (CONFIG_XFS_ASSERT_FATAL=y).  If a test is designed to trigger an assertion,
822 # skip the test on a CONFIG_XFS_ASSERT_FATAL built XFS by default.  Note:
823 # CONFIG_XFS_ASSERT_FATAL can be disabled by setting bug_on_assert to zero if
824 # we want test to run.
825 _require_no_xfs_bug_on_assert()
826 {
827         if [ -f /sys/fs/xfs/debug/bug_on_assert ]; then
828                 grep -q "1" /sys/fs/xfs/debug/bug_on_assert && \
829                    _notrun "test requires XFS bug_on_assert to be off, turn it off to run the test"
830         else
831                 # Note: Prior to the creation of CONFIG_XFS_ASSERT_FATAL (and
832                 # the sysfs knob bug_on_assert), assertions would always crash
833                 # the system if XFS debug was enabled (CONFIG_XFS_DEBUG=y).  If
834                 # a test is designed to trigger an assertion and the test
835                 # designer does not want to hang fstests, skip the test.
836                 _require_no_xfs_debug
837         fi
838 }
839
840 # Get a metadata field
841 # The first arg is the field name
842 # The rest of the arguments are xfs_db commands to find the metadata.
843 _scratch_xfs_get_metadata_field()
844 {
845         local key="$1"
846         shift
847
848         local grep_key="$(echo "${key}" | tr '[]()' '....')"
849         local cmds=()
850         local arg
851         for arg in "$@"; do
852                 cmds+=("-c" "${arg}")
853         done
854         _scratch_xfs_db "${cmds[@]}" -c "print ${key}" | grep "^${grep_key}" | \
855                 sed -e 's/^.* = //g'
856 }
857
858 # Set a metadata field
859 # The first arg is the field name
860 # The second arg is the new value
861 # The rest of the arguments are xfs_db commands to find the metadata.
862 _scratch_xfs_set_metadata_field()
863 {
864         local key="$1"
865         local value="$2"
866         shift; shift
867
868         local cmds=()
869         local arg
870         for arg in "$@"; do
871                 cmds+=("-c" "${arg}")
872         done
873
874         local wr_cmd="write"
875         _scratch_xfs_db -x -c "help write" | egrep -q "(-c|-d)" && value="-- ${value}"
876         _scratch_xfs_db -x -c "help write" | egrep -q "(-d)" && wr_cmd="${wr_cmd} -d"
877         _scratch_xfs_db -x "${cmds[@]}" -c "${wr_cmd} ${key} ${value}"
878 }
879
880 _scratch_xfs_get_sb_field()
881 {
882         _scratch_xfs_get_metadata_field "$1" "sb 0"
883 }
884
885 _scratch_xfs_set_sb_field()
886 {
887         _scratch_xfs_set_metadata_field "$1" "$2" "sb 0"
888 }
889
890 # Before xfsprogs commit 4222d000ed("db: write via array indexing doesn't
891 # work"), xfs_db command to write a specific AGFL index doesn't work. It's a
892 # bug in a diagnostic tool that is only used by XFS developers as a test
893 # infrastructure, so it's fine to treat it as a infrastructure dependency as
894 # all other _require rules.
895 _require_xfs_db_write_array()
896 {
897         local supported=0
898
899         _require_test
900         touch $TEST_DIR/$seq.img
901         $MKFS_XFS_PROG -d file,name=$TEST_DIR/$seq.img,size=512m >/dev/null 2>&1
902         $XFS_DB_PROG -x -c "agfl 0" -c "write bno[32] 78" $TEST_DIR/$seq.img \
903                 >/dev/null 2>&1
904         $XFS_DB_PROG -x -c "agfl 0" -c "print bno[32]" $TEST_DIR/$seq.img \
905                 | grep -q "bno\[32\] = 78" && supported=1
906         rm -f $TEST_DIR/$seq.img
907         [ $supported -eq 0 ] && _notrun "xfs_db write can't support array"
908 }
909
910 _require_xfs_spaceman_command()
911 {
912         if [ -z "$1" ]; then
913                 echo "Usage: _require_xfs_spaceman_command command [switch]" 1>&2
914                 exit 1
915         fi
916         local command=$1
917         shift
918         local param="$*"
919         local param_checked=0
920         local opts=""
921
922         _require_command "$XFS_SPACEMAN_PROG" "xfs_spaceman"
923
924         testfile=$TEST_DIR/$$.xfs_spaceman
925         touch $testfile
926         case $command in
927         "health")
928                 testio=`$XFS_SPACEMAN_PROG -c "health $param" $TEST_DIR 2>&1`
929                 param_checked=1
930                 ;;
931         *)
932                 testio=`$XFS_SPACEMAN_PROG -c "help $command" $TEST_DIR 2>&1`
933         esac
934
935         rm -f $testfile 2>&1 > /dev/null
936         echo $testio | grep -q "not found" && \
937                 _notrun "xfs_spaceman $command support is missing"
938         echo $testio | grep -q "Operation not supported" && \
939                 _notrun "xfs_spaceman $command failed (old kernel/wrong fs?)"
940         echo $testio | grep -q "Invalid" && \
941                 _notrun "xfs_spaceman $command failed (old kernel/wrong fs/bad args?)"
942         echo $testio | grep -q "foreign file active" && \
943                 _notrun "xfs_spaceman $command not supported on $FSTYP"
944         echo $testio | grep -q "Inappropriate ioctl for device" && \
945                 _notrun "xfs_spaceman $command support is missing (missing ioctl?)"
946         echo $testio | grep -q "Function not implemented" && \
947                 _notrun "xfs_spaceman $command support is missing (missing syscall?)"
948
949         [ -n "$param" ] || return
950
951         if [ $param_checked -eq 0 ]; then
952                 $XFS_SPACEMAN_PROG -c "help $command" | grep -q "^ $param --" || \
953                         _notrun "xfs_spaceman $command doesn't support $param"
954         fi
955 }
956
957 _scratch_get_sfdir_prefix() {
958         local dir_ino="$1"
959
960         for prefix in "u.sfdir3" "u.sfdir2" "u3.sfdir3"; do
961                 if [ -n "$(_scratch_xfs_get_metadata_field \
962                                 "${prefix}.hdr.parent.i4" \
963                                 "inode ${dir_ino}")" ]; then
964                         echo "${prefix}"
965                         return 0
966                 fi
967         done
968         _scratch_xfs_db -c "inode ${dir_ino}" -c 'p' >> $seqres.full
969         return 1
970 }
971
972 _scratch_get_bmx_prefix() {
973         local ino="$1"
974
975         for prefix in "u3.bmx" "u.bmx"; do
976                 if [ -n "$(_scratch_xfs_get_metadata_field \
977                                 "${prefix}[0].startblock" \
978                                 "inode ${ino}")" ]; then
979                         echo "${prefix}"
980                         return 0
981                 fi
982         done
983         _scratch_xfs_db -c "inode ${ino}" -c 'p' >> $seqres.full
984         return 1
985 }
986
987 _scratch_get_iext_count()
988 {
989         local ino=$1
990         local whichfork=$2
991         local field=""
992
993         case $whichfork in
994                 "attr")
995                         field=core.naextents
996                         ;;
997                 "data")
998                         field=core.nextents
999                         ;;
1000                 *)
1001                         return 1
1002         esac
1003
1004         _scratch_xfs_get_metadata_field $field "inode $ino"
1005 }
1006
1007 #
1008 # Ensures that we don't pass any mount options incompatible with XFS v4
1009 #
1010 _force_xfsv4_mount_options()
1011 {
1012         local gquota=0
1013         local pquota=0
1014
1015         # Can't have group and project quotas in XFS v4
1016         echo "$MOUNT_OPTIONS" | egrep -q "(gquota|grpquota|grpjquota=|gqnoenforce)" && gquota=1
1017         echo "$MOUNT_OPTIONS" | egrep -q "(\bpquota|prjquota|pqnoenforce)" && pquota=1
1018
1019         if [ $gquota -gt 0 ] && [ $pquota -gt 0 ]; then
1020                 export MOUNT_OPTIONS=$(echo $MOUNT_OPTIONS \
1021                         | sed   -e 's/gquota/QUOTA/g'      \
1022                                 -e 's/grpquota/QUOTA/g'    \
1023                                 -e 's/grpjquota=[^, ]/QUOTA/g' \
1024                                 -e 's/gqnoenforce/QUOTA/g' \
1025                                 -e "s/QUOTA/defaults/g")
1026         fi
1027         echo "MOUNT_OPTIONS = $MOUNT_OPTIONS" >>$seqres.full
1028 }
1029
1030 # Find AG count of mounted filesystem
1031 _xfs_mount_agcount()
1032 {
1033         $XFS_INFO_PROG "$1" | grep agcount= | sed -e 's/^.*agcount=\([0-9]*\),.*$/\1/g'
1034 }
1035
1036 # Wipe the superblock of each XFS AGs
1037 _try_wipe_scratch_xfs()
1038 {
1039         local num='^[0-9]+$'
1040         local agcount
1041         local agsize
1042         local dbsize
1043
1044         # Try to wipe each SB if there's an existed XFS
1045         agcount=`_scratch_xfs_get_sb_field agcount 2>/dev/null`
1046         agsize=`_scratch_xfs_get_sb_field agblocks 2>/dev/null`
1047         dbsize=`_scratch_xfs_get_sb_field blocksize 2>/dev/null`
1048         if [[ $agcount =~ $num && $agsize =~ $num && $dbsize =~ $num ]];then
1049                 for ((i = 0; i < agcount; i++)); do
1050                         $XFS_IO_PROG -c "pwrite $((i * dbsize * agsize)) $dbsize" \
1051                                 $SCRATCH_DEV >/dev/null;
1052                 done
1053         fi
1054
1055         # Try to wipe each SB by default mkfs.xfs geometry
1056         local tmp=`mktemp -u`
1057         unset agcount agsize dbsize
1058         _scratch_mkfs_xfs -N 2>/dev/null | perl -ne '
1059                 if (/^meta-data=.*\s+agcount=(\d+), agsize=(\d+) blks/) {
1060                         print STDOUT "agcount=$1\nagsize=$2\n";
1061                 }
1062                 if (/^data\s+=\s+bsize=(\d+)\s/) {
1063                         print STDOUT "dbsize=$1\n";
1064                 }' > $tmp.mkfs
1065
1066         . $tmp.mkfs
1067         if [[ $agcount =~ $num && $agsize =~ $num && $dbsize =~ $num ]];then
1068                 for ((i = 0; i < agcount; i++)); do
1069                         $XFS_IO_PROG -c "pwrite $((i * dbsize * agsize)) $dbsize" \
1070                                 $SCRATCH_DEV >/dev/null;
1071                 done
1072         fi
1073         rm -f $tmp.mkfs
1074 }
1075
1076 _require_xfs_copy()
1077 {
1078         [ -n "$XFS_COPY_PROG" ] || _notrun "xfs_copy binary not yet installed"
1079         [ "$USE_EXTERNAL" = yes ] && \
1080                 _notrun "Cannot xfs_copy with external devices"
1081 }
1082
1083 __xfs_cowgc_interval_knob1="/proc/sys/fs/xfs/speculative_cow_prealloc_lifetime"
1084 __xfs_cowgc_interval_knob2="/proc/sys/fs/xfs/speculative_prealloc_lifetime"
1085
1086 _xfs_set_cowgc_interval() {
1087         if [ -w $__xfs_cowgc_interval_knob1 ]; then
1088                 echo "$@" > $__xfs_cowgc_interval_knob1
1089         elif [ -w $__xfs_cowgc_interval_knob2 ]; then
1090                 echo "$@" > $__xfs_cowgc_interval_knob2
1091         else
1092                 _fail "Can't find cowgc interval procfs knob?"
1093         fi
1094 }
1095
1096 _xfs_get_cowgc_interval() {
1097         if [ -w $__xfs_cowgc_interval_knob1 ]; then
1098                 cat $__xfs_cowgc_interval_knob1
1099         elif [ -w $__xfs_cowgc_interval_knob2 ]; then
1100                 cat $__xfs_cowgc_interval_knob2
1101         else
1102                 _fail "Can't find cowgc interval procfs knob?"
1103         fi
1104 }