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