• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/bin/sh
2# SPDX-License-Identifier: GPL-2.0-or-later
3# Copyright (c) International Business Machines  Corp., 2005
4# Copyright (c) 2021 Joerg Vehlow <joerg.vehlow@aox-tech.de>
5# Based on work by: Avantika Mathur (mathurav@us.ibm.com)
6
7TST_NEEDS_TMPDIR=1
8TST_NEEDS_ROOT=1
9TST_MIN_KVER=2.6.15
10TST_SETUP="${TST_SETUP:-fs_bind_setup}"
11TST_CLEANUP="${TST_CLEANUP:-fs_bind_cleanup}"
12TST_TESTFUNC=fs_bind_test
13TST_NEEDS_CMDS="mount umount awk sed"
14
15# Test interface:
16#
17# FS_BIND_TESTFUNC is the real testfunction. Please do not use
18# TST_TESTFUNC in the test. FS_BIND_TESTFUNC is used to wrap some additional logic.
19# TST_CNT can be used as usual
20
21FS_BIND_SANDBOX="sandbox"
22FS_BIND_DISK1="disk1"
23FS_BIND_DISK2="disk2"
24FS_BIND_DISK3="disk3"
25FS_BIND_DISK4="disk4"
26
27FS_BIND_MNTNS_PID=
28
29# Creates a directory and bind-mounts it to itself.
30# usage: fs_bind_makedir share_mode directory
31# where
32#  share_mode is one of the --make-{shared,priv,...}
33#  supported by mount
34#  directory is directory where to be created/configured.
35#  If it does not exist, it will be created
36fs_bind_makedir()
37{
38	local bind_type dir
39
40	bind_type="$1"
41	dir="$2"
42
43	case "$bind_type" in
44	shared|private|rshared|slave|rslave|rprivate|runbindable) ;;
45	*) tst_brk TBROK "Unknown share type \"$bind_type\""
46	esac
47
48	if [ -e "$dir" ]; then
49		tst_brk TBROK "An entry by the name \"$dir\" exists already"
50	fi
51
52	ROD mkdir -p "$dir"
53
54	# Most mount implementations can use --make-* in the same command as bind,
55	# but busybox mount, fails at least to set --make-private
56	EXPECT_PASS mount --bind "$dir" "$dir"
57	EXPECT_PASS mount --make-$bind_type "$dir"
58}
59
60# Verifies that subtrees contain the same content
61# usage: fs_bind_check [-n] dir1 dirn...
62# where
63#  -n  If set, expectes the subtrees to not be equal
64#  -s  Run check in mount namespace
65# dir1 dirn... An arbitraty number of directories, that are compared.
66#              If -n is given, only two directories are allowed.
67fs_bind_check()
68{
69	local OPTIND expect_diff use_ns args msg dir1 dir2 fail output
70	expect_diff=0
71	use_ns=0
72	while getopts "ns" args; do
73		case "$args" in
74		n) expect_diff=1; ;;
75		s) use_ns=1; ;;
76		esac
77	done
78	shift $((OPTIND-1))
79	msg="Check"
80	[ $expect_diff -eq 1 ] && msg="$msg no"
81	msg="$msg propagation"
82	if [ $use_ns -eq 1 ]; then
83		[ -z "$FS_BIND_MNTNS_PID" ] && tst_brk TBROK "Namespace does not exist"
84		msg="$msg in mnt namespace"
85	fi
86	msg="$msg $*"
87
88	if [ $# -lt 2 ] || ( [ $expect_diff -eq 1 ] && [ $# -ne 2 ] ); then
89		tst_brk TBROK "Insufficient arguments"
90	fi
91
92	dir1=$1
93	shift
94
95	for dir2 in "$@"; do
96		# compare adjacent pairs of directory trees
97
98	    if [ ! -d "$dir1" ]; then
99	        tst_res TFAIL "$msg: \"$dir1\" does not exist"
100	        return 1
101	    elif [ ! -d "$dir2" ]; then
102	        if [ $expect_diff -eq 1 ]; then
103	            tst_res TPASS "$msg"
104	            return 0
105	        else
106	            tst_res TFAIL "$msg: \"$dir2\" does not exist"
107	            return 1
108	        fi
109	    fi
110
111		if [ $use_ns -eq 1 ]; then
112			output="$(ns_exec ${FS_BIND_MNTNS_PID} mnt diff -r "$PWD/$dir1" "$PWD/$dir2" 2> /dev/null)"
113		else
114			output="$(diff -r "$dir1" "$dir2" 2> /dev/null)"
115		fi
116
117		if [ $? -ne 0 ]; then
118			if [ $expect_diff -eq 1 ]; then
119	            # Since expect_diff=1 allows only two directories
120	            # to be compared, we are done after finding the first diff
121				tst_res TPASS "$msg"
122				return 0
123			else
124				tst_res TFAIL "$msg:"
125	            tst_res TFAIL "\"$dir1\" \"$dir2\" differ:\n$output"
126				return 1
127			fi
128		fi
129		dir1="$dir2"
130	done
131
132	if [ $expect_diff -eq 1 ]; then
133		tst_res TFAIL "$msg"
134		return 1
135	else
136		tst_res TPASS "$msg"
137		return 0
138	fi
139}
140
141fs_bind_setup()
142{
143	fs_bind_makedir private "$FS_BIND_SANDBOX"
144
145	cd $FS_BIND_SANDBOX
146	mkdir -p "$FS_BIND_DISK1" "$FS_BIND_DISK2" "$FS_BIND_DISK3" "$FS_BIND_DISK4"
147	mkdir "$FS_BIND_DISK1/a" "$FS_BIND_DISK1/b" "$FS_BIND_DISK1/c"
148	mkdir "$FS_BIND_DISK2/d" "$FS_BIND_DISK2/e" "$FS_BIND_DISK2/f"
149	mkdir "$FS_BIND_DISK3/g" "$FS_BIND_DISK3/h" "$FS_BIND_DISK3/i"
150	mkdir "$FS_BIND_DISK4/j" "$FS_BIND_DISK4/k" "$FS_BIND_DISK4/l"
151}
152
153_fs_bind_unmount_all()
154{
155	local mounts
156
157	cd "$TST_TMPDIR"
158
159	# Cleanup leftover mounts from the test
160	# sed '1!G;h;$!d' is used to reverse /proc/mounts.
161	# unmounting in reverse order requires significantly less iterations
162	# There is a slight chance, this loop does not terminate, if a mount
163	# cannot be unmounted for some reason. In that case the timeout
164	# will kill the test, but we cannot restore the system anyway
165	while true; do
166		mounts=$( sed '1!G;h;$!d' /proc/mounts \
167			| awk -vtmp="$TST_TMPDIR/$FS_BIND_SANDBOX/" \
168			'index($2, tmp) {print $2}' )
169		[ -z "$mounts" ] && break
170		echo $mounts | xargs umount 2>/dev/null
171	done
172}
173
174fs_bind_cleanup()
175{
176	_fs_bind_unmount_all
177	umount "$FS_BIND_SANDBOX"
178}
179
180_fs_bind_setup_test()
181{
182	local e
183
184	cd "$TST_TMPDIR/$FS_BIND_SANDBOX" || tst_brk "Unable to cd into sandbox"
185
186	for e in ls *; do
187		if   [ "$e" = "$FS_BIND_DISK1" ] \
188		  || [ "$e" = "$FS_BIND_DISK2" ] \
189		  || [ "$e" = "$FS_BIND_DISK3" ] \
190		  || [ "$e" = "$FS_BIND_DISK4" ]; then
191		  continue
192		fi
193		rm -rf "$e"
194	done
195}
196
197fs_bind_create_ns()
198{
199	[ -n "$FS_BIND_MNTNS_PID" ] && tst_brk TBROK "Namespace exist already"
200	FS_BIND_MNTNS_PID=$(ns_create mnt)
201}
202
203fs_bind_exec_ns()
204{
205	[ -z "$FS_BIND_MNTNS_PID" ] && tst_brk TBROK "Namespace does not exist"
206	EXPECT_PASS ns_exec $FS_BIND_MNTNS_PID mnt "$@"
207}
208
209fs_bind_destroy_ns()
210{
211	[ -n "$FS_BIND_MNTNS_PID" ] && kill $FS_BIND_MNTNS_PID 2>/dev/null
212	FS_BIND_MNTNS_PID=
213}
214
215_fs_bind_cleanup_test()
216{
217	local mounts
218
219	fs_bind_destroy_ns
220
221	mounts=$( awk -v tmp="$TST_TMPDIR/$FS_BIND_SANDBOX/" '
222		index($2, tmp) {
223			print substr($2, length(tmp) + 1)
224		}
225	' /proc/mounts )
226	if [ -n "$mounts" ]; then
227		tst_res TFAIL "There are still mounts in the sandbox:\n$mounts"
228	fi
229	_fs_bind_unmount_all
230}
231
232fs_bind_test()
233{
234	_fs_bind_setup_test
235
236	if type ${FS_BIND_TESTFUNC}1 > /dev/null 2>&1; then
237		"$FS_BIND_TESTFUNC$_tst_i" $1
238	else
239		"$FS_BIND_TESTFUNC" $1
240	fi
241
242	_fs_bind_cleanup_test
243}
244
245. tst_test.sh
246[ -z "$FS_BIND_TESTFUNC" ] && tst_brk TBROK "Please set FS_BIND_TESTFUNC before sourcing fs_bind_lib.sh"
247