• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/bin/sh
2# SPDX-License-Identifier: GPL-2.0-or-later
3# Copyright (c) Linux Test Project, 2014-2022
4# Author: Cyril Hrubis <chrubis@suse.cz>
5#
6# LTP test library for shell.
7
8[ -n "$TST_LIB_LOADED" ] && return 0
9
10export TST_PASS=0
11export TST_FAIL=0
12export TST_BROK=0
13export TST_WARN=0
14export TST_CONF=0
15export TST_COUNT=1
16export TST_ITERATIONS=1
17export TST_TMPDIR_RHOST=0
18export TST_LIB_LOADED=1
19
20if [ -z "$TST_FS_TYPE" ]; then
21	export TST_FS_TYPE="${LTP_DEV_FS_TYPE:-ext2}"
22fi
23
24. tst_ansi_color.sh
25. tst_security.sh
26
27# default trap function
28trap "tst_brk TBROK 'test interrupted'" INT
29trap "unset _tst_setup_timer_pid; tst_brk TBROK 'test terminated'" TERM
30
31_tst_do_cleanup()
32{
33	if [ -n "$TST_DO_CLEANUP" -a -n "$TST_CLEANUP" -a -z "$LTP_NO_CLEANUP" ]; then
34		if command -v $TST_CLEANUP >/dev/null 2>/dev/null; then
35			TST_DO_CLEANUP=
36			$TST_CLEANUP
37		else
38			tst_res TWARN "TST_CLEANUP=$TST_CLEANUP declared, but function not defined (or cmd not found)"
39		fi
40	fi
41	TST_DO_CLEANUP=
42}
43
44_tst_do_exit()
45{
46	local ret=0
47	TST_DO_EXIT=1
48
49	_tst_do_cleanup
50
51	cd "$LTPROOT"
52	[ "$TST_MOUNT_FLAG" = 1 ] && tst_umount
53
54	if [ "$TST_NEEDS_DEVICE" = 1 -a "$TST_DEVICE_FLAG" = 1 ]; then
55		if ! tst_device release "$TST_DEVICE"; then
56			tst_res TWARN "Failed to release device '$TST_DEVICE'"
57		fi
58	fi
59
60	if [ "$TST_NEEDS_TMPDIR" = 1 -a -n "$TST_TMPDIR" ]; then
61		rm -r "$TST_TMPDIR"
62		[ "$TST_TMPDIR_RHOST" = 1 ] && tst_cleanup_rhost
63	fi
64
65	if [ -n "$TST_NEEDS_CHECKPOINTS" -a -f "$LTP_IPC_PATH" ]; then
66		rm $LTP_IPC_PATH
67	fi
68
69	_tst_cleanup_timer
70
71	if [ $TST_FAIL -gt 0 ]; then
72		ret=$((ret|1))
73	fi
74
75	if [ $TST_BROK -gt 0 ]; then
76		ret=$((ret|2))
77	fi
78
79	if [ $TST_WARN -gt 0 ]; then
80		ret=$((ret|4))
81	fi
82
83	if [ $TST_CONF -gt 0 -a $TST_PASS -eq 0 ]; then
84		ret=$((ret|32))
85	fi
86
87	if [ $TST_BROK -gt 0 -o $TST_FAIL -gt 0 -o $TST_WARN -gt 0 ]; then
88		_tst_check_security_modules
89	fi
90
91	cat >&2 << EOF
92
93Summary:
94passed   $TST_PASS
95failed   $TST_FAIL
96broken   $TST_BROK
97skipped  $TST_CONF
98warnings $TST_WARN
99EOF
100
101	exit $ret
102}
103
104_tst_inc_res()
105{
106	case "$1" in
107	TPASS) TST_PASS=$((TST_PASS+1));;
108	TFAIL) TST_FAIL=$((TST_FAIL+1));;
109	TBROK) TST_BROK=$((TST_BROK+1));;
110	TWARN) TST_WARN=$((TST_WARN+1));;
111	TCONF) TST_CONF=$((TST_CONF+1));;
112	TINFO) ;;
113	*) tst_brk TBROK "Invalid res type '$1'";;
114	esac
115}
116
117tst_res()
118{
119	local res=$1
120	shift
121
122	_tst_inc_res "$res"
123
124	printf "$TST_ID $TST_COUNT " >&2
125	tst_print_colored $res "$res: " >&2
126	echo "$@" >&2
127}
128
129tst_brk()
130{
131	local res=$1
132	shift
133
134	# TBROK => TWARN on cleanup or exit
135	if [ "$res" = TBROK ] && [ "$TST_DO_EXIT" = 1 -o -z "$TST_DO_CLEANUP" -a -n "$TST_CLEANUP" ]; then
136		tst_res TWARN "$@"
137		TST_DO_CLEANUP=
138		return
139	fi
140
141	if [ "$res" != TBROK -a "$res" != TCONF ]; then
142		tst_res TBROK "tst_brk can be called only with TBROK or TCONF ($res)"
143	else
144		tst_res "$res" "$@"
145	fi
146
147	_tst_do_exit
148}
149
150ROD_SILENT()
151{
152	local tst_out
153
154	tst_out="$(tst_rod $@ 2>&1)"
155	if [ $? -ne 0 ]; then
156		echo "$tst_out"
157		tst_brk TBROK "$@ failed"
158	fi
159}
160
161ROD()
162{
163	tst_rod "$@"
164	if [ $? -ne 0 ]; then
165		tst_brk TBROK "$@ failed"
166	fi
167}
168
169_tst_expect_pass()
170{
171	local fnc="$1"
172	shift
173
174	tst_rod "$@"
175	if [ $? -eq 0 ]; then
176		tst_res TPASS "$@ passed as expected"
177		return 0
178	else
179		$fnc TFAIL "$@ failed unexpectedly"
180		return 1
181	fi
182}
183
184_tst_expect_fail()
185{
186	local fnc="$1"
187	shift
188
189	# redirect stderr since we expect the command to fail
190	tst_rod "$@" 2> /dev/null
191	if [ $? -ne 0 ]; then
192		tst_res TPASS "$@ failed as expected"
193		return 0
194	else
195		$fnc TFAIL "$@ passed unexpectedly"
196		return 1
197	fi
198}
199
200EXPECT_PASS()
201{
202	_tst_expect_pass tst_res "$@"
203}
204
205EXPECT_PASS_BRK()
206{
207	_tst_expect_pass tst_brk "$@"
208}
209
210EXPECT_FAIL()
211{
212	_tst_expect_fail tst_res "$@"
213}
214
215EXPECT_FAIL_BRK()
216{
217	_tst_expect_fail tst_brk "$@"
218}
219
220TST_RETRY_FN_EXP_BACKOFF()
221{
222	local tst_fun="$1"
223	local tst_exp=$2
224	local tst_sec=$(($3 * 1000000))
225	local tst_delay=1
226
227	_tst_multiply_timeout tst_sec
228
229	if [ $# -ne 3 ]; then
230		tst_brk TBROK "TST_RETRY_FN_EXP_BACKOFF expects 3 parameters"
231	fi
232
233	if ! tst_is_int "$tst_sec"; then
234		tst_brk TBROK "TST_RETRY_FN_EXP_BACKOFF: tst_sec must be integer ('$tst_sec')"
235	fi
236
237	while true; do
238		eval "$tst_fun"
239		if [ "$?" = "$tst_exp" ]; then
240			break
241		fi
242
243		if [ $tst_delay -lt $tst_sec ]; then
244			tst_sleep ${tst_delay}us
245			tst_delay=$((tst_delay*2))
246		else
247			tst_brk TBROK "\"$tst_fun\" timed out"
248		fi
249	done
250
251	return $tst_exp
252}
253
254TST_RETRY_FUNC()
255{
256	if [ $# -ne 2 ]; then
257		tst_brk TBROK "TST_RETRY_FUNC expects 2 parameters"
258	fi
259
260	TST_RETRY_FN_EXP_BACKOFF "$1" "$2" 1
261	return $2
262}
263
264TST_RTNL_CHK()
265{
266	local msg1="RTNETLINK answers: Function not implemented"
267	local msg2="RTNETLINK answers: Operation not supported"
268	local msg3="RTNETLINK answers: Protocol not supported"
269	local output="$($@ 2>&1 || echo 'LTP_ERR')"
270	local msg
271
272	echo "$output" | grep -q "LTP_ERR" || return 0
273
274	for msg in "$msg1" "$msg2" "$msg3"; do
275		echo "$output" | grep -q "$msg" && tst_brk TCONF "'$@': $msg"
276	done
277
278	tst_brk TBROK "$@ failed: $output"
279}
280
281TST_CHECKPOINT_WAIT()
282{
283	ROD tst_checkpoint wait 10000 "$1"
284}
285
286TST_CHECKPOINT_WAKE()
287{
288	ROD tst_checkpoint wake 10000 "$1" 1
289}
290
291TST_CHECKPOINT_WAKE2()
292{
293	ROD tst_checkpoint wake 10000 "$1" "$2"
294}
295
296TST_CHECKPOINT_WAKE_AND_WAIT()
297{
298	TST_CHECKPOINT_WAKE "$1"
299	TST_CHECKPOINT_WAIT "$1"
300}
301
302tst_mount()
303{
304	local mnt_opt mnt_err mnt_real
305
306	if [ -n "$TST_FS_TYPE" ]; then
307		mnt_opt="-t $TST_FS_TYPE"
308		mnt_err=" $TST_FS_TYPE type"
309	fi
310	local cmd="mount $mnt_opt $TST_DEVICE $TST_MNTPOINT $TST_MNT_PARAMS"
311
312	ROD_SILENT mkdir -p $TST_MNTPOINT
313	tst_res TINFO "Mounting device: $cmd"
314	$cmd
315	local ret=$?
316
317	if [ $ret -eq 32 ]; then
318		tst_brk TCONF "Cannot mount${mnt_err}, missing driver?"
319	fi
320
321	if [ $ret -ne 0 ]; then
322		tst_brk TBROK "Failed to mount device${mnt_err}: mount exit = $ret"
323	fi
324}
325
326tst_umount()
327{
328	local mntpoint="${1:-$TST_MNTPOINT}"
329	local i=0
330
331	[ -z "$mntpoint" ] && return
332
333	if ! echo "$mntpoint" | grep -q ^/; then
334		tst_brk TCONF "The '$mntpoint' is not an absolute path"
335	fi
336
337	if ! grep -q "${mntpoint%/}" /proc/mounts; then
338		tst_res TINFO "The '$mntpoint' is not mounted, skipping umount"
339		return
340	fi
341
342	while [ "$i" -lt 50 ]; do
343		if umount "$mntpoint" > /dev/null; then
344			return
345		fi
346
347		i=$((i+1))
348
349		tst_res TINFO "umount($mntpoint) failed, try $i ..."
350		tst_res TINFO "Likely gvfsd-trash is probing newly mounted "\
351		              "fs, kill it to speed up tests."
352
353		tst_sleep 100ms
354	done
355
356	tst_res TWARN "Failed to umount($mntpoint) after 50 retries"
357}
358
359tst_mkfs()
360{
361	local opts
362	local fs_type=${1:-$TST_FS_TYPE}
363	[ $# -ge 1 ] && shift
364
365	opts="$@"
366
367	if [ "$fs_type" = tmpfs ]; then
368		tst_res TINFO "Skipping mkfs for TMPFS filesystem"
369		return
370	fi
371
372	if [ -z "$opts" ]; then
373		if [ "$TST_NEEDS_DEVICE" != 1 ]; then
374			tst_brk "Using default parameters in tst_mkfs requires TST_NEEDS_DEVICE=1"
375		fi
376		opts="$TST_DEVICE"
377	fi
378
379	tst_require_cmds mkfs.$fs_type
380
381	tst_res TINFO "Formatting $fs_type with opts='$opts'"
382	ROD_SILENT mkfs.$fs_type $opts
383}
384
385# Detect whether running under hypervisor: Microsoft Hyper-V
386# Return 0: running under Hyper-V
387# Return 1: not running under Hyper-V (bare metal, other hypervisor or
388#           failure of detection)
389tst_virt_hyperv()
390{
391	local v
392
393	tst_cmd_available systemd-detect-virt || return 1
394
395	v="$(systemd-detect-virt)"
396
397	[ $? -eq 0 ] || return 1
398	[ "$v" = "microsoft" ] || return 1
399
400	return 0
401}
402
403tst_cmd_available()
404{
405	command -v $1 >/dev/null 2>&1
406}
407
408tst_require_cmds()
409{
410	local cmd
411	for cmd in $*; do
412		tst_cmd_available $cmd || tst_brk TCONF "'$cmd' not found"
413	done
414}
415
416tst_check_cmds()
417{
418	local cmd
419	for cmd in $*; do
420		if ! tst_cmd_available $cmd; then
421			tst_res TCONF "'$cmd' not found"
422			return 1
423		fi
424	done
425	return 0
426}
427
428tst_require_drivers()
429{
430	[ $# -eq 0 ] && return 0
431
432	local drv
433
434	drv="$(tst_check_drivers $@ 2>&1)"
435
436	[ $? -ne 0 ] && tst_brk TCONF "$drv driver not available"
437	return 0
438}
439
440tst_require_kconfigs()
441{
442	local delim
443
444	if [ $# -gt 2 ]; then
445		return 0
446	elif [ $# -eq 1 ]; then
447		delim="$TST_NEEDS_KCONFIGS_IFS"
448	else
449		delim="$2"
450	fi
451
452	[ -z "$1" ] && return 0
453
454	tst_check_kconfigs "$1" "$delim" > /dev/null
455
456	[ $? -ne 0 ] && tst_brk TCONF "Aborting due to unsuitable kernel config, see above!"
457	return 0
458}
459
460tst_is_int()
461{
462	[ "$1" -eq "$1" ] 2>/dev/null
463	return $?
464}
465
466tst_is_num()
467{
468	echo "$1" | grep -Eq '^[-+]?[0-9]+\.?[0-9]*$'
469}
470
471tst_usage()
472{
473	if [ -n "$TST_USAGE" ]; then
474		$TST_USAGE
475	else
476		echo "usage: $0"
477		echo
478		echo "Options"
479		echo "-------"
480	fi
481
482	echo "-h      Prints this help"
483	echo "-i n    Execute test n times"
484
485	cat << EOF
486
487Environment Variables
488---------------------
489KCONFIG_PATH         Specify kernel config file
490KCONFIG_SKIP_CHECK   Skip kernel config check if variable set (not set by default)
491LTPROOT              Prefix for installed LTP (default: /opt/ltp)
492LTP_COLORIZE_OUTPUT  Force colorized output behaviour (y/1 always, n/0: never)
493LTP_DEV              Path to the block device to be used (for .needs_device)
494LTP_DEV_FS_TYPE      Filesystem used for testing (default: ext2)
495LTP_SINGLE_FS_TYPE   Testing only - specifies filesystem instead all supported (for TST_ALL_FILESYSTEMS=1)
496LTP_TIMEOUT_MUL      Timeout multiplier (must be a number >=1, ceiled to int)
497TMPDIR               Base directory for template directory (for .needs_tmpdir, default: /tmp)
498EOF
499}
500
501_tst_resstr()
502{
503	echo "$TST_PASS$TST_FAIL$TST_CONF"
504}
505
506_tst_rescmp()
507{
508	local res=$(_tst_resstr)
509
510	if [ "$1" = "$res" ]; then
511		tst_brk TBROK "Test didn't report any results"
512	fi
513}
514
515_tst_multiply_timeout()
516{
517	[ $# -ne 1 ] && tst_brk TBROK "_tst_multiply_timeout expect 1 parameter"
518	eval "local timeout=\$$1"
519
520	LTP_TIMEOUT_MUL=${LTP_TIMEOUT_MUL:-1}
521
522	local err="LTP_TIMEOUT_MUL must be number >= 1!"
523
524	tst_is_num "$LTP_TIMEOUT_MUL" || tst_brk TBROK "$err ($LTP_TIMEOUT_MUL)"
525
526	if ! tst_is_int "$LTP_TIMEOUT_MUL"; then
527		LTP_TIMEOUT_MUL=$(echo "$LTP_TIMEOUT_MUL" | cut -d. -f1)
528		LTP_TIMEOUT_MUL=$((LTP_TIMEOUT_MUL+1))
529		tst_res TINFO "ceiling LTP_TIMEOUT_MUL to $LTP_TIMEOUT_MUL"
530	fi
531
532	[ "$LTP_TIMEOUT_MUL" -ge 1 ] || tst_brk TBROK "$err ($LTP_TIMEOUT_MUL)"
533	[ "$timeout" -ge 1 ] || tst_brk TBROK "timeout need to be >= 1 ($timeout)"
534
535	eval "$1='$((timeout * LTP_TIMEOUT_MUL))'"
536	return 0
537}
538
539_tst_cleanup_timer()
540{
541	if [ -n "$_tst_setup_timer_pid" ]; then
542		kill -TERM $_tst_setup_timer_pid 2>/dev/null
543		# kill is successful only on test timeout
544		wait $_tst_setup_timer_pid 2>/dev/null || true
545	fi
546}
547
548_tst_setup_timer()
549{
550	TST_TIMEOUT=${TST_TIMEOUT:-300}
551
552	if [ "$TST_TIMEOUT" = -1 ]; then
553		tst_res TINFO "Timeout per run is disabled"
554		return
555	fi
556
557	if ! tst_is_int "$TST_TIMEOUT" || [ "$TST_TIMEOUT" -lt 1 ]; then
558		tst_brk TBROK "TST_TIMEOUT must be int >= 1! ($TST_TIMEOUT)"
559	fi
560
561	local sec=$TST_TIMEOUT
562	_tst_multiply_timeout sec
563	local h=$((sec / 3600))
564	local m=$((sec / 60 % 60))
565	local s=$((sec % 60))
566	local pid=$$
567
568	tst_res TINFO "timeout per run is ${h}h ${m}m ${s}s"
569
570	_tst_cleanup_timer
571
572	tst_timeout_kill $sec $pid &
573
574	_tst_setup_timer_pid=$!
575
576	while true; do
577		local state
578
579		state=$(cut -d' ' -f3 "/proc/$_tst_setup_timer_pid/stat")
580
581		if [ "$state" = "S" ]; then
582			break;
583		fi
584
585		tst_sleep 1ms
586	done
587}
588
589tst_require_root()
590{
591	if [ "$(id -ru)" != 0 ]; then
592		tst_brk TCONF "Must be super/root for this test!"
593	fi
594}
595
596tst_require_module()
597{
598	local _tst_module=$1
599
600	for tst_module in "$_tst_module" \
601	                  "$LTPROOT/testcases/bin/$_tst_module" \
602	                  "$TST_STARTWD/$_tst_module"; do
603
604			if [ -f "$tst_module" ]; then
605				TST_MODPATH="$tst_module"
606				break
607			fi
608	done
609
610	if [ -z "$TST_MODPATH" ]; then
611		tst_brk TCONF "Failed to find module '$_tst_module'"
612	fi
613
614	tst_res TINFO "Found module at '$TST_MODPATH'"
615}
616
617tst_set_timeout()
618{
619	TST_TIMEOUT="$1"
620	_tst_setup_timer
621}
622
623_tst_init_checkpoints()
624{
625	local pagesize
626
627	LTP_IPC_PATH="/dev/shm/ltp_${TST_ID}_$$"
628	pagesize=$(tst_getconf PAGESIZE)
629	if [ $? -ne 0 ]; then
630		tst_brk TBROK "tst_getconf PAGESIZE failed"
631	fi
632	ROD_SILENT dd if=/dev/zero of="$LTP_IPC_PATH" bs="$pagesize" count=1
633	ROD_SILENT chmod 600 "$LTP_IPC_PATH"
634	export LTP_IPC_PATH
635}
636
637_prepare_device()
638{
639	if [ "$TST_FORMAT_DEVICE" = 1 ]; then
640		tst_device clear "$TST_DEVICE"
641		tst_mkfs $TST_FS_TYPE $TST_DEV_FS_OPTS $TST_DEVICE $TST_DEV_EXTRA_OPTS
642	fi
643
644	if [ "$TST_MOUNT_DEVICE" = 1 ]; then
645		tst_mount
646		TST_MOUNT_FLAG=1
647	fi
648}
649
650_tst_run_tcases_per_fs()
651{
652	local fs
653	local filesystems
654
655	filesystems="$(tst_supported_fs -s "$TST_SKIP_FILESYSTEMS")"
656	if [ $? -ne 0 ]; then
657		tst_brk TCONF "There are no supported filesystems or all skipped"
658	fi
659
660	for fs in $filesystems; do
661		tst_res TINFO "=== Testing on $fs ==="
662		TST_FS_TYPE="$fs"
663		_tst_run_iterations
664	done
665}
666
667tst_run()
668{
669	local _tst_i
670	local _tst_data
671	local _tst_max
672	local _tst_name
673	local _tst_pattern='[='\''"} \t\/:`$\;|].*'
674	local ret
675
676	if [ -n "$TST_TEST_PATH" ]; then
677		for _tst_i in $(grep '^[^#]*\bTST_' "$TST_TEST_PATH" | sed "s/.*TST_//; s/$_tst_pattern//"); do
678			case "$_tst_i" in
679			ALL_FILESYSTEMS|DISABLE_APPARMOR|DISABLE_SELINUX);;
680			SETUP|CLEANUP|TESTFUNC|ID|CNT|MIN_KVER);;
681			OPTS|USAGE|PARSE_ARGS|POS_ARGS);;
682			NEEDS_ROOT|NEEDS_TMPDIR|TMPDIR|NEEDS_DEVICE|DEVICE);;
683			NEEDS_CMDS|NEEDS_MODULE|MODPATH|DATAROOT);;
684			NEEDS_DRIVERS|FS_TYPE|MNTPOINT|MNT_PARAMS);;
685			NEEDS_KCONFIGS|NEEDS_KCONFIGS_IFS);;
686			IPV6|IPV6_FLAG|IPVER|TEST_DATA|TEST_DATA_IFS);;
687			RETRY_FUNC|RETRY_FN_EXP_BACKOFF|TIMEOUT);;
688			NET_DATAROOT|NET_MAX_PKT|NET_RHOST_RUN_DEBUG|NETLOAD_CLN_NUMBER);;
689			NET_SKIP_VARIABLE_INIT|NEEDS_CHECKPOINTS);;
690			CHECKPOINT_WAIT|CHECKPOINT_WAKE);;
691			CHECKPOINT_WAKE2|CHECKPOINT_WAKE_AND_WAIT);;
692			DEV_EXTRA_OPTS|DEV_FS_OPTS|FORMAT_DEVICE|MOUNT_DEVICE);;
693			SKIP_FILESYSTEMS|SKIP_IN_LOCKDOWN|SKIP_IN_SECUREBOOT);;
694			DEVICE_SIZE);;
695			*) tst_res TWARN "Reserved variable TST_$_tst_i used!";;
696			esac
697		done
698
699		for _tst_i in $(grep '^[^#]*\b_tst_' "$TST_TEST_PATH" | sed "s/.*_tst_//; s/$_tst_pattern//"); do
700			tst_res TWARN "Private variable or function _tst_$_tst_i used!"
701		done
702	fi
703
704	if ! tst_is_int "$TST_ITERATIONS"; then
705		tst_brk TBROK "Expected number (-i) not '$TST_ITERATIONS'"
706	fi
707
708	if [ "$TST_ITERATIONS" -lt 0 ]; then
709		tst_brk TBROK "Number of iterations (-i) must be >= 0"
710	fi
711
712	[ "$TST_NEEDS_ROOT" = 1 ] && tst_require_root
713
714	if [ "$TST_SKIP_IN_SECUREBOOT" = 1 ] && tst_secureboot_enabled; then
715		tst_brk TCONF "SecureBoot enabled, skipping test"
716	fi
717
718	if [ "$TST_SKIP_IN_LOCKDOWN" = 1 ] && tst_lockdown_enabled; then
719		tst_brk TCONF "Kernel is locked down, skipping test"
720	fi
721
722	[ "$TST_DISABLE_APPARMOR" = 1 ] && tst_disable_apparmor
723	[ "$TST_DISABLE_SELINUX" = 1 ] && tst_disable_selinux
724
725	tst_require_cmds $TST_NEEDS_CMDS
726	tst_require_kconfigs "$TST_NEEDS_KCONFIGS"
727	tst_require_drivers $TST_NEEDS_DRIVERS
728
729	if [ -n "$TST_MIN_KVER" ]; then
730		tst_kvcmp -lt "$TST_MIN_KVER" && \
731			tst_brk TCONF "test requires kernel $TST_MIN_KVER+"
732	fi
733
734	[ -n "$TST_NEEDS_MODULE" ] && tst_require_module "$TST_NEEDS_MODULE"
735
736	[ "$TST_MOUNT_DEVICE" = 1 ] && TST_FORMAT_DEVICE=1
737	[ "$TST_FORMAT_DEVICE" = 1 -o "$TST_ALL_FILESYSTEMS" = 1 ] && TST_NEEDS_DEVICE=1
738	[ "$TST_NEEDS_DEVICE" = 1 ] && TST_NEEDS_TMPDIR=1
739
740	if [ "$TST_NEEDS_TMPDIR" = 1 ]; then
741		if [ -z "$TMPDIR" ]; then
742			export TMPDIR="/tmp"
743		fi
744
745		TST_TMPDIR=$(mktemp -d "$TMPDIR/LTP_$TST_ID.XXXXXXXXXX")
746		# remove possible trailing slash or double slashes from TMPDIR
747		TST_TMPDIR=$(echo "$TST_TMPDIR" | sed 's~/\+~/~g')
748
749		chmod 777 "$TST_TMPDIR"
750
751		TST_STARTWD=$(pwd)
752		cd "$TST_TMPDIR"
753		tst_res TINFO "Using $TST_TMPDIR as tmpdir ($(stat -f -c '%T' $TST_TMPDIR) filesystem)"
754	fi
755
756	# needs to be after cd $TST_TMPDIR to keep test_dev.img under $TST_TMPDIR
757	if [ "$TST_NEEDS_DEVICE" = 1 ]; then
758		TST_DEVICE=$(tst_device acquire $TST_DEVICE_SIZE)
759
760		if [ ! -b "$TST_DEVICE" -o $? -ne 0 ]; then
761			unset TST_DEVICE
762			tst_brk TBROK "Failed to acquire device"
763		fi
764		TST_DEVICE_FLAG=1
765
766		if [ -z "$TST_FS_TYPE" ]; then
767			export TST_FS_TYPE="${LTP_DEV_FS_TYPE:-ext2}"
768		fi
769	fi
770
771	if [ "$TST_ALL_FILESYSTEMS" != 1 -a "$TST_SKIP_FILESYSTEMS" ]; then
772		if ! tst_supported_fs -s "$TST_SKIP_FILESYSTEMS" -d . > /dev/null; then
773			tst_brk TCONF "filesystem is not supported by the test"
774		fi
775
776		tst_res TINFO "filesystem is supported by the test"
777	fi
778
779	[ -n "$TST_NEEDS_CHECKPOINTS" ] && _tst_init_checkpoints
780
781	TST_MNTPOINT="${TST_MNTPOINT:-$PWD/mntpoint}"
782
783	if [ "$TST_ALL_FILESYSTEMS" = 1 ]; then
784		_tst_run_tcases_per_fs
785	else
786		_tst_run_iterations
787	fi
788
789	_tst_do_exit
790}
791
792_tst_run_iterations()
793{
794	local _tst_i=$TST_ITERATIONS
795	local _tst_j
796
797	[ "$TST_NEEDS_TMPDIR" = 1 ] && cd "$TST_TMPDIR"
798
799	_prepare_device
800
801	_tst_setup_timer
802
803	if [ -n "$TST_SETUP" ]; then
804		if command -v $TST_SETUP >/dev/null 2>/dev/null; then
805			TST_DO_CLEANUP=1
806			$TST_SETUP
807		else
808			tst_brk TBROK "TST_SETUP=$TST_SETUP declared, but function not defined (or cmd not found)"
809		fi
810	fi
811
812	#TODO check that test reports some results for each test function call
813	while [ $_tst_i -gt 0 ]; do
814		if [ -n "$TST_TEST_DATA" ]; then
815			tst_require_cmds cut tr wc
816			_tst_max=$(( $(echo $TST_TEST_DATA | tr -cd "$TST_TEST_DATA_IFS" | wc -c) +1))
817			for _tst_j in $(seq $_tst_max); do
818				_tst_data="$(echo "$TST_TEST_DATA" | cut -d"$TST_TEST_DATA_IFS" -f$_tst_j)"
819				_tst_run_tests "$_tst_data"
820			done
821		else
822			_tst_run_tests
823		fi
824		_tst_i=$((_tst_i-1))
825	done
826
827	_tst_do_cleanup
828
829	if [ "$TST_MOUNT_FLAG" = 1 ]; then
830		cd "$LTPROOT"
831		tst_umount
832		TST_MOUNT_FLAG=
833	fi
834}
835
836_tst_run_tests()
837{
838	local _tst_data="$1"
839	local _tst_i
840
841	TST_DO_CLEANUP=1
842	for _tst_i in $(seq ${TST_CNT:-1}); do
843		if command -v ${TST_TESTFUNC}1 > /dev/null 2>&1; then
844			_tst_run_test "$TST_TESTFUNC$_tst_i" $_tst_i "$_tst_data"
845		else
846			_tst_run_test "$TST_TESTFUNC" $_tst_i "$_tst_data"
847		fi
848	done
849}
850
851_tst_run_test()
852{
853	local _tst_res=$(_tst_resstr)
854	local _tst_fnc="$1"
855	shift
856
857	$_tst_fnc "$@"
858	_tst_rescmp "$_tst_res"
859	TST_COUNT=$((TST_COUNT+1))
860}
861
862export LC_ALL=C
863
864if [ -z "$TST_ID" ]; then
865	_tst_filename=$(basename $0) || \
866		tst_brk TCONF "Failed to set TST_ID from \$0 ('$0'), fix it with setting TST_ID before sourcing tst_test.sh"
867	TST_ID=${_tst_filename%%.*}
868fi
869export TST_ID="$TST_ID"
870
871if [ -z "$LTPROOT" ]; then
872	export LTPROOT="$PWD"
873	export TST_DATAROOT="$LTPROOT/datafiles"
874else
875	export TST_DATAROOT="$LTPROOT/testcases/data/$TST_ID"
876fi
877
878if [ -z "$TST_NO_DEFAULT_RUN" ]; then
879	if TST_TEST_PATH=$(command -v $0) 2>/dev/null; then
880		if ! grep -q tst_run "$TST_TEST_PATH"; then
881			tst_brk TBROK "Test $0 must call tst_run!"
882		fi
883	fi
884
885	if [ -z "$TST_TESTFUNC" ]; then
886		tst_brk TBROK "TST_TESTFUNC is not defined"
887	fi
888
889	TST_TEST_DATA_IFS="${TST_TEST_DATA_IFS:- }"
890
891	TST_NEEDS_KCONFIGS_IFS="${TST_NEEDS_KCONFIGS_IFS:-,}"
892
893	if [ -n "$TST_CNT" ]; then
894		if ! tst_is_int "$TST_CNT"; then
895			tst_brk TBROK "TST_CNT must be integer"
896		fi
897
898		if [ "$TST_CNT" -le 0 ]; then
899			tst_brk TBROK "TST_CNT must be > 0"
900		fi
901	fi
902
903	if [ -n "$TST_POS_ARGS" ]; then
904		if ! tst_is_int "$TST_POS_ARGS"; then
905			tst_brk TBROK "TST_POS_ARGS must be integer"
906		fi
907
908		if [ "$TST_POS_ARGS" -le 0 ]; then
909			tst_brk TBROK "TST_POS_ARGS must be > 0"
910		fi
911	fi
912
913	TST_ARGS="$@"
914
915	tst_res TINFO "Running: $(basename $0) $TST_ARGS"
916	tst_res TINFO "Tested kernel: $(uname -a)"
917
918	OPTIND=1
919
920	while getopts ":hi:$TST_OPTS" _tst_name $TST_ARGS; do
921		case $_tst_name in
922		'h') tst_usage; exit 0;;
923		'i') TST_ITERATIONS=$OPTARG;;
924		'?') tst_usage; exit 2;;
925		*) $TST_PARSE_ARGS "$_tst_name" "$OPTARG";;
926		esac
927	done
928
929	shift $((OPTIND - 1))
930
931	if [ -n "$TST_POS_ARGS" ]; then
932		if [ $# -ne "$TST_POS_ARGS" ]; then
933			tst_brk TBROK "Invalid number of positional parameters:"\
934					  "have ($@) $#, expected ${TST_POS_ARGS}"
935		fi
936	else
937		if [ $# -ne 0 ]; then
938			tst_brk TBROK "Unexpected positional arguments '$@'"
939		fi
940	fi
941fi
942