• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/bin/bash
2
3#
4# Copyright (c) International Business Machines  Corp., 2005
5# Authors: Avantika Mathur (mathurav@us.ibm.com)
6#          Matt Helsley (matthltc@us.ibm.com)
7#
8# This library is free software; you can redistribute it and/or
9# modify it under the terms of the GNU Lesser General Public
10# License as published by the Free Software Foundation; either
11# version 2.1 of the License, or (at your option) any later version.
12#
13# This library is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16# Lesser General Public License for more details.
17#
18# You should have received a copy of the GNU Lesser General Public
19# License along with this library; if not, write to the Free Software
20# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21#
22
23if tst_kvcmp -lt "2.6.15"; then
24       tst_resm TCONF "System kernel version is less than 2.6.15"
25       tst_resm TCONF "Cannot execute test"
26       exit 0
27fi
28
29test_setup()
30{
31	#######################################################################
32	## Configure
33	#######################################################################
34	dopts='-dEBb'
35
36	## Remove logged test state depending on results. 0 means do not remove,
37	## 1 means OK to remove.
38	# rm saved state from tests that appear to have cleaned up properly?
39	rm_ok=1
40	# rm saved state from tests that don't appear to have fully cleaned up?
41	rm_err=0
42
43	#######################################################################
44	## Initialize some variables
45	#######################################################################
46	TCID="$0"
47	TST_COUNT=0
48
49	test_dirs=( move bind rbind regression )  #cloneNS
50	nfailed=0
51	nsucceeded=0
52
53	# set the LTPROOT directory
54	cd `dirname $0`
55	LTPROOT="${PWD}"
56	echo "${LTPROOT}" | grep testscripts > /dev/null 2>&1
57	if [ $? -eq 0 ]; then
58		cd ..
59		LTPROOT="${PWD}"
60	fi
61
62	FS_BIND_ROOT="${LTPROOT}/testcases/bin/fs_bind"
63
64	total=0 # total number of tests
65	for dir in "${test_dirs[@]}" ; do
66		((total += `ls "${FS_BIND_ROOT}/${dir}/test"* | wc -l`))
67	done
68	TST_TOTAL=${total}
69
70	# set the PATH to include testcases/bin
71	LTPBIN="${LTPROOT}/testcases/bin"
72	PATH="${PATH}:/usr/sbin:${LTPBIN}:${FS_BIND_ROOT}/bin"
73
74	# Results directory
75	resdir="${LTPROOT}/results/fs_bind"
76	if [ ! -d "${resdir}" ]; then
77		mkdir -p "${resdir}" 2> /dev/null
78	fi
79
80	TMPDIR="${TMPDIR:-/tmp}"
81	# A temporary directory where we can do stuff and that is
82	# safe to remove
83	sandbox="${TMPDIR}/sandbox"
84
85	ERR_MSG=""
86
87	export LTPBIN PATH FS_BIND_ROOT ERR_MSG TCID TST_COUNT TST_TOTAL
88
89	if [ ! -d "${resdir}" ]; then
90		tst_brkm TBROK true "$0: failed to make results directory"
91		exit 1
92	fi
93}
94
95test_prereqs()
96{
97	# Must be root to run the containers testsuite
98	if [ $UID != 0 ]; then
99		tst_brkm TBROK true "FAILED: Must be root to execute this script"
100		exit 1
101	fi
102
103	mkdir "${sandbox}" >& /dev/null
104	if [ ! -d "${sandbox}" -o ! -x "${sandbox}" ]; then
105		tst_brkm TBROK true "$0: failed to make directory \"${sandbox}\""
106		exit -1
107	fi
108
109	mount --bind "${sandbox}" "${sandbox}" >& /dev/null
110	if [ $? -ne 0 ]; then
111		tst_brkm TBROK true "$0: failed to perform bind mount on directory \"${sandbox}\""
112		exit 1
113	fi
114
115	mount --make-private "${sandbox}" >& /dev/null
116	if [ $? -ne 0 ]; then
117		tst_brkm TBROK true "$0: failed to make private mountpoint on directory \"${sandbox}\""
118		exit 1
119	fi
120
121	local mnt_bind=1
122	local mnt_move=1
123
124	pushd "${sandbox}" > /dev/null && {
125		mkdir bind_test move_test && {
126			mount --bind bind_test bind_test && {
127				mnt_bind=0
128				mount --move bind_test move_test && {
129					mnt_move=0
130					umount move_test
131				} || {
132					# bind mount succeeded but move mount
133					# failed
134					umount bind_test
135				}
136			} || {
137				# mount failed -- check if it's because we
138				# don't have privileges we need
139				if [ $? -eq 32 ]; then
140					tst_brkm TBROK true "$0 requires the privilege to use the mount command"
141					exit 32
142				fi
143			}
144			rmdir bind_test move_test
145		}
146		popd > /dev/null
147	}
148
149	if [ ${mnt_bind} -eq 1 -o ${mnt_move} -eq 1 ]; then
150		tst_brkm TBROK true "$0: requires that mount support the --bind and --move options"
151		exit 1
152	fi
153
154	if tst_kvcmp -lt "2.6.15"; then
155		tst_resm TWARN "$0: the remaining tests require 2.6.15 or later"
156		tst_exit 0
157		exit
158	else
159		tst_resm TINFO "$0: kernel >= 2.6.15 detected -- continuing"
160	fi
161
162	mount --make-shared "${sandbox}" > /dev/null 2>&1 || "${FS_BIND_ROOT}/bin/smount" "${sandbox}" shared
163	umount "${sandbox}" || {
164		tst_resm TFAIL "$0: failed to umount simplest shared subtree"
165		exit 1
166	}
167	tst_resm TPASS "$0: umounted simplest shared subtree"
168
169}
170
171# mounts we are concerned with in a well-defined order (helps diff)
172# returns grep return codes
173grep_proc_mounts()
174{
175	local rc=0
176
177	# Save the pipefail shell option
178	shopt -o -q pipefail
179	local save=$?
180	set -o pipefail
181
182	# Grep /proc/mounts which is often more up-to-date than mounts
183	# We use pipefail because if the grep fails we want to pass that along
184	grep -F "${sandbox}" /proc/mounts | sort -b
185	rc=$?
186
187	# Restore the pipefail shell options
188	[ $save -eq 0 ] && shopt -o -s pipefail || shopt -o -u pipefail
189
190	return $rc
191}
192
193# Record the mount state
194save_proc_mounts()
195{
196	touch "$2/proc_mounts.before" >& /dev/null
197	if [ $? -ne 0 ]; then
198		tst_brkm TBROK true "$1: failed to record proc mounts"
199		return 1
200	fi
201
202	grep_proc_mounts 2> /dev/null > "$2/proc_mounts.before"
203	return 0
204}
205
206# Compare mount list after the test with the list from before.
207# If there are no differences then remove the before list and silently
208# return 0. Else print the differences to stderr and return 1.
209check_proc_mounts()
210{
211	local tname="$1"
212
213	if [ ! -r "$2/proc_mounts.before" ]; then
214		tst_brkm TBROK true "${tname}: Could not find pre-test proc mount list"
215		return 1
216	fi
217
218	grep_proc_mounts 2> /dev/null > "$2/proc_mounts.after"
219	# If the mounts are the same then just return
220	diff ${dopts} -q "$2/proc_mounts.before" "$2/proc_mounts.after" >& /dev/null
221	if [ $? -eq 0 ]; then
222		[ $rm_ok -eq 1 ] && rm -f "$2/proc_mounts."{before,after}
223		return 0
224	fi
225
226	tst_resm TWARN "${tname}: did not properly clean up its proc mounts"
227	diff ${dopts} -U 0 "$2/proc_mounts.before" "$2/proc_mounts.after" | grep -vE '^\@\@' 1>&2
228	[ $rm_err -eq 1 ] && rm -f "$2/proc_mounts."{before,after}
229	return 1
230}
231
232# Undo leftover mounts
233restore_proc_mounts()
234{
235	#local tname="$1"
236
237	# do lazy umounts -- we're assuming that tests will only leave
238	# new mounts around and will never remove mounts outside the test
239	# directory
240	( while grep_proc_mounts ; do
241		grep_proc_mounts | awk '{print $2}' | xargs -r -n 1 umount -l
242	done ) >& /dev/null
243
244	# mount list and exit with 0
245	[ $rm_err -eq 1 ] && rm -f "$2/proc_mounts."{before,after} 1>&2 # >& /dev/null
246	return 0
247	# if returning error do this:
248	# tst_brkm TBROK true "${tname}: failed to restore mounts"
249}
250
251# mounts we are concerned with in a well-defined order (helps diff)
252# returns grep return codes
253grep_mounts()
254{
255	local rc=0
256
257	# Save the pipefail shell option
258	shopt -o -q pipefail
259	local save=$?
260	set -o pipefail
261
262	# Grep mount command output (which tends to come from /etc/mtab)
263	# We use pipefail because if the grep fails we want to pass that along
264	mount | grep -F "${sandbox}" | sort -b
265	rc=$?
266
267	# Restore the pipefail shell options
268	[ $save -eq 0 ] && shopt -o -s pipefail || shopt -o -u pipefail
269
270	return $rc
271}
272
273# Record the mount state
274save_mounts()
275{
276	touch "$2/mtab.before" >& /dev/null
277	if [ $? -ne 0 ]; then
278		tst_brkm TBROK true "$1: failed to record mtab mounts"
279		return 1
280	fi
281
282	grep_mounts 2> /dev/null > "$2/mtab.before"
283	return 0
284}
285
286# Compare mount list after the test with the list from before.
287# If there are no differences then remove the before list and silently
288# return 0. Else print the differences to stderr and return 1.
289check_mounts()
290{
291	local tname="$1"
292
293	if [ ! -r "$2/mtab.before" ]; then
294		tst_brkm TBROK true "${tname}: Could not find pre-test mtab mount list"
295		return 1
296	fi
297
298	grep_mounts 2> /dev/null > "$2/mtab.after"
299	# If the mounts are the same then just return
300	diff ${dopts} -q "$2/mtab.before" "$2/mtab.after" >& /dev/null
301	if [ $? -eq 0 ]; then
302		[ $rm_ok -eq 1 ] && rm -f "$2/mtab."{before,after}
303		return 0
304	fi
305
306	tst_resm TWARN "${tname}: did not properly clean up its mtab mounts"
307	diff ${dopts} -U 0 "$2/mtab.before" "$2/mtab.after" | grep -vE '^\@\@' 1>&2
308	[ $rm_err -eq 1 ] && rm -f "$2/mtab."{before,after}
309	return 1
310}
311
312# Undo leftover mounts
313restore_mounts()
314{
315	#local tname="$1"
316
317	# do lazy umounts -- we're assuming that tests will only leave
318	# new mounts around and will never remove mounts outside the test
319	# directory
320	( while grep_mounts ; do
321		grep_mounts | awk '{print $3}' | xargs -r -n 1 umount -l
322	done ) >& /dev/null
323
324	# mount list and exit with 0
325	[ $rm_err -eq 1 ] && rm -f "$2/mtab."{before,after} 1>&2 # >& /dev/null
326	return 0
327	# if returning error do this:
328	# tst_brkm TBROK true "${tname}: failed to restore mounts"
329}
330
331# Record the sandbox state
332# We don't save full sandbox state -- just the names of files and dirs present
333save_sandbox()
334{
335	local when="before"
336	local tname="$1"
337
338	if [ -e "$2/files.before" ]; then
339		if [ -e "$2/files.after" ]; then
340			tst_brkm TBROK true "${tname}: stale catalog of \"${sandbox}\""
341			return 1
342		fi
343		when="after"
344	fi
345
346	( find "${sandbox}" -type d -print | sort > "$2/dirs.$when"
347	  find "${sandbox}" -type f -print | sort | \
348		grep -vE '^'"$2"'/(dirs|files)\.(before|after)$' > "$2/files.$when" ) >& /dev/null
349	return 0
350}
351
352# Save sandbox after test and then compare. If the sandbox state is not
353# clean then print the differences to stderr and return 1. Else remove all
354# saved sandbox state and silently return 0
355check_sandbox()
356{
357	local tname="$1"
358
359	if [ ! -r "$2/files.before" -o ! -r "$2/dirs.before" ]; then
360		tst_brkm TBROK true "${tname} missing saved catalog of \"${sandbox}\""
361		return 1
362	fi
363
364	save_sandbox "${tname} (check)" "$2"
365
366	( diff ${dopts} -q "$2/dirs.before" "$2/dirs.after" && \
367	  diff ${dopts} -q "$2/files.before" "$2/files.after" )  >& /dev/null \
368	  && {
369		[ $rm_ok -eq 1 ] && rm -f "$2/"{files,dirs}.{before,after}
370		return 0
371	}
372
373	tst_resm TWARN "${tname} did not properly clean up \"${sandbox}\""
374	diff ${dopts} -U 0 "$2/dirs.before" "$2/dirs.after" 1>&2
375	diff ${dopts} -U 0 "$2/files.before" "$2/files.after" 1>&2
376	[ $rm_err -eq 1 ] && rm -f "$2/"{files,dirs}.{before,after} 1>&2
377	return 1
378}
379
380# Robust sandbox cleanup
381clean_sandbox()
382{
383	local tname="$1"
384
385	{ rm -rf "${sandbox}" ; mkdir "${sandbox}" ; } >& /dev/null
386	if [ ! -d "${sandbox}" -o ! -x "${sandbox}" ]; then
387		tst_brkm TBROK true "$tname: failed to make directory \"${sandbox}\""
388		return 1
389	fi
390	return 0
391}
392
393# Check file for non-whitespace chars
394is_file_empty()
395{
396	awk '/^[[:space:]]*$/  { next }
397	      { exit 1; }' < "$1"
398}
399
400#
401# Run the specified test script.
402#
403# Return 1 if the test was broken but should not stop the remaining test
404#	categories from being run.
405# Return 2 if the test was broken and no further tests should be run.
406# Return 0 otherwise (if the test was broken but it shouldn't affect other
407#	test runs)
408# Note that this means the return status is not the success or failure of the
409#	test itself.
410#
411run_test()
412{
413	local t="$1"
414	local tname="$(basename "$(dirname "$t")")/$(basename "$t")"
415	local log="$resdir/$tname/log"
416	local errlog="$resdir/$tname/err"
417	local do_break=0
418
419	ERR_MSG=""
420
421	# Pre-test
422	mkdir -p "$resdir/$tname"
423	if [ ! -d "$resdir/$tname" -o ! -x "$resdir/$tname" ]; then
424		tst_brkm TBROK true "$0: can't make or use \"$resdir/$tname\" as a log directory"
425		return 1
426	fi
427
428	save_sandbox "$tname" "$resdir/$tname" || do_break=1
429	save_mounts "$tname" "$resdir/$tname" || do_break=1
430	save_proc_mounts "$tname" "$resdir/$tname" || do_break=1
431	mount --bind "${sandbox}" "${sandbox}" >& /dev/null || do_break=1
432	mount --make-private "${sandbox}" >& /dev/null || do_break=1
433
434	if [ $do_break -eq 1 ]; then
435		umount -l "${sandbox}" >& /dev/null
436		tst_brkm TBROK true "$tname: failed to save pre-test state of \"${sandbox}\""
437		return 2
438	fi
439	pushd "${sandbox}" > /dev/null
440
441	# Run the test
442	(
443		TCID="$tname"
444		declare -r TST_COUNT
445		export LTPBIN PATH FS_BIND_ROOT ERR_MSG TCID TST_COUNT TST_TOTAL
446		"$t" #> "$log" 2> "$errlog"
447	)
448	local rc=$?
449	TCID="$0"
450
451	# Post-test
452	popd > /dev/null
453	if [ $rc -ne 0 ]; then
454		#echo "FAILED"
455		((nfailed++))
456	else
457		#echo "SUCCEEDED"
458		((nsucceeded++))
459	fi
460	umount -l "${sandbox}" >& /dev/null
461	check_proc_mounts "$tname" "$resdir/$tname" || \
462	restore_proc_mounts "$tname" "$resdir/$tname" || do_break=1
463	check_mounts "$tname" "$resdir/$tname" || \
464	restore_mounts "$tname" "$resdir/$tname" || do_break=1
465	check_sandbox "$tname" "$resdir/$tname"
466	clean_sandbox "$tname" || do_break=1
467	if [ $do_break -eq 1 ]; then
468		tst_brkm TBROK true "$tname: failed to restore pre-test state of \"${sandbox}\""
469		return 2
470	fi
471
472	# If we succeeded and the error log is empty remove it
473	if [ $rc -eq 0 -a -w "$errlog" ] && is_file_empty "$errlog" ; then
474		rm -f "$errlog"
475	fi
476	return 0
477}
478
479main()
480{
481	TST_COUNT=1
482	for dir in "${test_dirs[@]}" ; do
483		tests=( $(find "${FS_BIND_ROOT}/${dir}" -type f -name 'test*') )
484		clean_sandbox "$0" || break
485		for t in "${tests[@]}" ; do
486			run_test "$t"
487			local rc=$?
488
489			if [ $rc -ne 0 ]; then
490				break $rc
491			fi
492
493			((TST_COUNT++))
494		done
495	done
496	rm -rf "${sandbox}"
497	return 0
498
499	skipped=$((total - nsucceeded - nfailed))
500	if [ $nfailed -eq 0 -a $skipped -eq 0 ]; then
501		# Use PASSED for the summary rather than SUCCEEDED to make it
502		# easy to determine 100% success from a calling script
503		summary="PASSED"
504	else
505		# Use FAILED to make it easy to find > 0% failure from a
506		# calling script
507		summary="FAILED"
508	fi
509	cat - <<-EOF
510		*********************************
511		RESULTS SUMMARY:
512
513			passed:  $nsucceeded/$total
514			failed:  $nfailed/$total
515			skipped: $skipped/$total
516			summary: $summary
517
518		*********************************
519	EOF
520}
521
522test_setup || exit 1
523test_prereqs || exit 1
524declare -r FS_BIND_ROOT
525declare -r TST_TOTAL
526main  #2> "$resdir/errors" 1> "$resdir/summary"
527