aboutsummaryrefslogtreecommitdiff
path: root/testcases/kernel/fs/fs_bind/fs_bind_lib.sh
blob: 2dd9ef08fac8a87cacde08e6fbc0132dce9c999c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (c) International Business Machines  Corp., 2005
# Copyright (c) 2021 Joerg Vehlow <joerg.vehlow@aox-tech.de>
# Based on work by: Avantika Mathur (mathurav@us.ibm.com)

TST_NEEDS_TMPDIR=1
TST_NEEDS_ROOT=1
TST_MIN_KVER=2.6.15
TST_SETUP="${TST_SETUP:-fs_bind_setup}"
TST_CLEANUP="${TST_CLEANUP:-fs_bind_cleanup}"
TST_TESTFUNC=fs_bind_test
TST_NEEDS_CMDS="mount umount awk sed"

# Test interface:
#
# FS_BIND_TESTFUNC is the real testfunction. Please do not use
# TST_TESTFUNC in the test. FS_BIND_TESTFUNC is used to wrap some additional logic.
# TST_CNT can be used as usual

FS_BIND_SANDBOX="sandbox"
FS_BIND_DISK1="disk1"
FS_BIND_DISK2="disk2"
FS_BIND_DISK3="disk3"
FS_BIND_DISK4="disk4"

FS_BIND_MNTNS_PID=

# Creates a directory and bind-mounts it to itself.
# usage: fs_bind_makedir share_mode directory
# where
#  share_mode is one of the --make-{shared,priv,...}
#  supported by mount
#  directory is directory where to be created/configured.
#  If it does not exist, it will be created
fs_bind_makedir()
{
	local bind_type dir

	bind_type="$1"
	dir="$2"

	case "$bind_type" in
	shared|private|rshared|slave|rslave|rprivate|runbindable) ;;
	*) tst_brk TBROK "Unknown share type \"$bind_type\""
	esac

	if [ -e "$dir" ]; then
		tst_brk TBROK "An entry by the name \"$dir\" exists already"
	fi

	ROD mkdir -p "$dir"

	# Most mount implementations can use --make-* in the same command as bind,
	# but busybox mount, fails at least to set --make-private
	EXPECT_PASS mount --bind "$dir" "$dir"
	EXPECT_PASS mount --make-$bind_type "$dir"
}

# Verifies that subtrees contain the same content
# usage: fs_bind_check [-n] dir1 dirn...
# where
#  -n  If set, expectes the subtrees to not be equal
#  -s  Run check in mount namespace
# dir1 dirn... An arbitraty number of directories, that are compared.
#              If -n is given, only two directories are allowed.
fs_bind_check()
{
	local OPTIND expect_diff use_ns args msg dir1 dir2 fail output
	expect_diff=0
	use_ns=0
	while getopts "ns" args; do
		case "$args" in
		n) expect_diff=1; ;;
		s) use_ns=1; ;;
		esac
	done
	shift $((OPTIND-1))
	msg="Check"
	[ $expect_diff -eq 1 ] && msg="$msg no"
	msg="$msg propagation"
	if [ $use_ns -eq 1 ]; then
		[ -z "$FS_BIND_MNTNS_PID" ] && tst_brk TBROK "Namespace does not exist"
		msg="$msg in mnt namespace"
	fi
	msg="$msg $*"

	if [ $# -lt 2 ] || ( [ $expect_diff -eq 1 ] && [ $# -ne 2 ] ); then
		tst_brk TBROK "Insufficient arguments"
	fi

	dir1=$1
	shift

	for dir2 in "$@"; do
		# compare adjacent pairs of directory trees

	    if [ ! -d "$dir1" ]; then
	        tst_res TFAIL "$msg: \"$dir1\" does not exist"
	        return 1
	    elif [ ! -d "$dir2" ]; then
	        if [ $expect_diff -eq 1 ]; then
	            tst_res TPASS "$msg"
	            return 0
	        else
	            tst_res TFAIL "$msg: \"$dir2\" does not exist"
	            return 1
	        fi
	    fi

		if [ $use_ns -eq 1 ]; then
			output="$(ns_exec ${FS_BIND_MNTNS_PID} mnt diff -r "$PWD/$dir1" "$PWD/$dir2" 2> /dev/null)"
		else
			output="$(diff -r "$dir1" "$dir2" 2> /dev/null)"
		fi

		if [ $? -ne 0 ]; then
			if [ $expect_diff -eq 1 ]; then
	            # Since expect_diff=1 allows only two directories
	            # to be compared, we are done after finding the first diff
				tst_res TPASS "$msg"
				return 0
			else
				tst_res TFAIL "$msg:"
	            tst_res TFAIL "\"$dir1\" \"$dir2\" differ:\n$output"
				return 1
			fi
		fi
		dir1="$dir2"
	done

	if [ $expect_diff -eq 1 ]; then
		tst_res TFAIL "$msg"
		return 1
	else
		tst_res TPASS "$msg"
		return 0
	fi
}

fs_bind_setup()
{
	fs_bind_makedir private "$FS_BIND_SANDBOX"

	cd $FS_BIND_SANDBOX
	mkdir -p "$FS_BIND_DISK1" "$FS_BIND_DISK2" "$FS_BIND_DISK3" "$FS_BIND_DISK4"
	mkdir "$FS_BIND_DISK1/a" "$FS_BIND_DISK1/b" "$FS_BIND_DISK1/c"
	mkdir "$FS_BIND_DISK2/d" "$FS_BIND_DISK2/e" "$FS_BIND_DISK2/f"
	mkdir "$FS_BIND_DISK3/g" "$FS_BIND_DISK3/h" "$FS_BIND_DISK3/i"
	mkdir "$FS_BIND_DISK4/j" "$FS_BIND_DISK4/k" "$FS_BIND_DISK4/l"
}

_fs_bind_unmount_all()
{
	local mounts

	cd "$TST_TMPDIR"

	# Cleanup leftover mounts from the test
	# sed '1!G;h;$!d' is used to reverse /proc/mounts.
	# unmounting in reverse order requires significantly less iterations
	# There is a slight chance, this loop does not terminate, if a mount
	# cannot be unmounted for some reason. In that case the timeout
	# will kill the test, but we cannot restore the system anyway
	while true; do
		mounts=$( sed '1!G;h;$!d' /proc/mounts \
			| awk -vtmp="$TST_TMPDIR/$FS_BIND_SANDBOX/" \
			'index($2, tmp) {print $2}' )
		[ -z "$mounts" ] && break
		echo $mounts | xargs umount 2>/dev/null
	done
}

fs_bind_cleanup()
{
	_fs_bind_unmount_all
	umount "$FS_BIND_SANDBOX"
}

_fs_bind_setup_test()
{
	local e

	cd "$TST_TMPDIR/$FS_BIND_SANDBOX" || tst_brk "Unable to cd into sandbox"

	for e in ls *; do
		if   [ "$e" = "$FS_BIND_DISK1" ] \
		  || [ "$e" = "$FS_BIND_DISK2" ] \
		  || [ "$e" = "$FS_BIND_DISK3" ] \
		  || [ "$e" = "$FS_BIND_DISK4" ]; then
		  continue
		fi
		rm -rf "$e"
	done
}

fs_bind_create_ns()
{
	[ -n "$FS_BIND_MNTNS_PID" ] && tst_brk TBROK "Namespace exist already"
	FS_BIND_MNTNS_PID=$(ns_create mnt)
}

fs_bind_exec_ns()
{
	[ -z "$FS_BIND_MNTNS_PID" ] && tst_brk TBROK "Namespace does not exist"
	EXPECT_PASS ns_exec $FS_BIND_MNTNS_PID mnt "$@"
}

fs_bind_destroy_ns()
{
	[ -n "$FS_BIND_MNTNS_PID" ] && kill $FS_BIND_MNTNS_PID 2>/dev/null
	FS_BIND_MNTNS_PID=
}

_fs_bind_cleanup_test()
{
	local mounts

	fs_bind_destroy_ns

	mounts=$( awk -v tmp="$TST_TMPDIR/$FS_BIND_SANDBOX/" '
		index($2, tmp) {
			print substr($2, length(tmp) + 1)
		}
	' /proc/mounts )
	if [ -n "$mounts" ]; then
		tst_res TFAIL "There are still mounts in the sandbox:\n$mounts"
	fi
	_fs_bind_unmount_all
}

fs_bind_test()
{
	_fs_bind_setup_test

	if type ${FS_BIND_TESTFUNC}1 > /dev/null 2>&1; then
		"$FS_BIND_TESTFUNC$_tst_i" $1
	else
		"$FS_BIND_TESTFUNC" $1
	fi

	_fs_bind_cleanup_test
}

. tst_test.sh
[ -z "$FS_BIND_TESTFUNC" ] && tst_brk TBROK "Please set FS_BIND_TESTFUNC before sourcing fs_bind_lib.sh"