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"
|