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