3 # This is a bash test case to test lxc-usernsexec.
4 # It basically supports usring lxc-usernsexec to execute itself
5 # and then create files and check that their ownership is as expected.
7 # It requires that the current user has at least 1 value in subuid and /etc/subgid
12 fail
() { echo "$@" 1>&2; exit 1; }
13 error
() { echo "$@" 1>&2; }
19 local level
=${1}; shift;
20 [ "${level}" -gt "${VERBOSITY}" ] && return
25 # collect_owners([--dir=dir], file1, file2 ...)
26 # set _RET to a space delimited array of
27 # <file1>:owner:group <file2>:owner:group ...
28 local out
="" ret
="" dir
=""
29 if [ "${1#--dir=}" != "$1" ]; then
34 # drop the :* so that input can be same as touch_files.
35 out
=$
(stat
--format "%n:%u:%g" "${dir}${arg}") ||
{
36 error
"failed to stat ${arg}"
45 if [ -d "$TEMP_D" ]; then
51 # touch_files tok [tok ...]
52 # tok is filename:chown_id:chown_gid
53 # if chown_id or chown_gid is empty, then chown will do the right thing
54 # and only change the provided value.
55 local args
="" tok
="" fname
="" uidgid
=""
61 : > "$fname" ||
{ error
"failed to create $fname"; return 1; }
62 [ -z "$uidgid" ] && continue
63 chown
$uidgid "$fname" ||
{ error
"failed to chmod '$uidgid' $fname ($?)"; return 1; }
78 FILES
[${#FILES[@]}]="${x%%:*}"
83 # this what gets run inside the usernsexec environment.
84 # basically expects arguments of <filename>:uid:gid
85 # it will create the file, and then chmod it to the provided uid:gid
86 # it writes to file descriptor 5 a single line with space delimited
87 # exit_value uid gid [<filename>:<owner>:<group> ... ]
89 trap inside_cleanup EXIT
90 local uid
="" gid
="" x
=""
92 uid
=$
(id
-u) || fail
"failed execution of id -u"
93 gid
=$
(id
-g) || fail
"failed execution of id -g"
97 touch_files
"$@" || fail
"failed to create files"
99 collect_owners
"${FILES[@]}" || fail
"failed to collect owners"
102 # tell caller we are done.
103 echo "0" "$uid" "$gid" "$result" >&5
106 # let the caller do things while the files are around.
113 # runtest(mydir, nsexec_args, [inside [...]])
114 # - use 'mydir' as a working dir.
115 # - execute lxc-usernsexec $nsexec_args -- <self> inside <inside args>
118 # exit_value inside_exit_value inside_uid:inside_gid <results>
120 # where results are a list of space separated
122 # for each file passed in inside_args
123 [ $# -ge 3 ] ||
{ error
"runtest expects 2 args"; return 1; }
124 local mydir
="$1" nsexec_args
="$2"
126 local ret inside_owners t
=""
129 mkfifo "${mydir}/5" && exec 5<>"${mydir}/5" ||
return
130 mkfifo "${mydir}/6" && exec 6<>"${mydir}/6" ||
return
131 mkdir
--mode=777 "${mydir}/work" ||
return
136 local results
="" oresults
="" iresults
="" iuid
="" igid
="" n
=0
138 error
"$" $USERNSEXEC ${nsexec_args} -- "$MYPATH" inside
"$*"
139 ${USERNSEXEC} ${nsexec_args} -- "$MYPATH" inside
"$@" &
142 [ -d "/proc/$KIDPID" ] ||
{
144 fail
"kid $KIDPID died quickly $?"
147 # if lxc-usernsexec fails to execute MYPATH inside, then
148 # the read below would timeout. To avoid a long timeout,
149 # we do a short timeout and check the pid is alive.
150 while ! read -t 1 ret iuid igid inside_owners
<&5; do
152 if [ ! -d "/proc/$KIDPID" ]; then
154 fail
"kid $KIDPID is gone $?"
156 [ $n -ge 30 ] && fail
"child never wrote to pipe"
158 iresults
=( $inside_owners )
160 collect_owners
"--dir=${mydir}/work/" "${FILES[@]}" ||
return
169 for((i
=0;i
<${#iresults[@]};i
++)); do
170 results
[$i]="${oresults[$i]}:${iresults[$i]#*:}"
173 echo 0 $ret "$iuid:$igid" "${results[@]}"
177 local name
="$1" expected
="$2" nsexec_args
="$3" found
=""
179 mkdir
"${TEMP_D}/$name" || fail
"failed mkdir <TEMP_D>/$name.d"
180 local err
="${TEMP_D}/$name.err"
181 out
=$
("$MYPATH" runtest
"${TEMP_D}/$name" "$nsexec_args" "$@" 2>"$err") ||
{
182 error
"$name: FAIL - runtest failed $?"
183 [ -n "$out" ] && error
" $out"
184 sed 's,^, ,' "$err" 1>&2
185 ERRORS
="${ERRORS} $name"
189 local parentrc
=$1 kidrc
=$2 iuidgid
="$3" found
=""
192 [ "$parentrc" = "0" -a "$kidrc" = "0" ] ||
{
193 error
"$name: FAIL - parentrc=$parentrc kidrc=$kidrc found=$found"
194 ERRORS
="${ERRORS} $name"
197 [ "$expected" = "$found" ] && {
199 PASS
="${PASSES} $name"
202 echo "$name: FAIL expected '$expected' != found '$found'"
203 FAILS
="${FAILS} $name"
209 ${0} setup_and_run [-- run-args]
211 setup the system by creating a user (default is '${asuser:-test-userns}')
212 and then run test as that user. Must be root.
214 If user exists, then do not create the user.
216 -v | --verbose - be more verbose
217 --create-subuid=UID:RANGE
218 --create-subgid=UID:RANGE if adding subuid/subgid use this START:RANGE
219 example (default) 3000000000:5
224 local short_opts
="hv"
225 local long_opts
="help,user:,create-subuid:,create-subgid:,verbose"
227 getopt_out
=$
(getopt
--name "${0##*/}" \
228 --options "${short_opts}" --long "${long_opts}" -- "$@") &&
229 eval set -- "${getopt_out}" ||
230 { bad_Usage
; return; }
232 local cur
="" next
="" asuser
="test-userns"
233 local create_subuid
="3000000000:5" create_subgid
="3000000000:5"
234 while [ $# -ne 0 ]; do
237 -h|
--help) setup_Usage
; exit 0;;
238 --user) asuser
="$next"; shift;;
239 --create-subuid) create_subuid
=$next; shift;;
240 --create-subgid) create_subgid
=$next; shift;;
241 -v|
--verbose) VERBOSITY
=$
((${VERBOSITY}+1));;
250 if [ "$(id -u)" != "0" ]; then
251 error
"Sorry, setup_and_run has to be done as root, not uid=$(id -u)"
255 local home
="/home/$asuser"
256 if [ ! -d "$home" ]; then
257 debug
1 "creating user $asuser"
258 useradd
"$asuser" --create-home "--home-dir=$home" ||
{
259 error
"failed to create $asuser"
263 debug
1 "$asuser existed"
266 local subuid
="" subgid
=""
267 subuid
=$
(awk -F: '$1 == n { print $2; exit(0); }' "n=$asuser" /etc
/subuid
) ||
{
268 error
"failed to read /etc/subuid for $asuser"
272 if [ -n "$subuid" ]; then
273 debug
1 "$asuser already had subuid=$subuid"
275 debug
1 "adding $asuser:$create_subuid to /etc/subuid"
276 echo "$asuser:$create_subuid" >> /etc
/subuid ||
{
277 error
"failed to add $asuser to /etc/subuid"
281 subgid
=$
(awk -F: '$1 == n { print $2; exit(0); }' "n=$asuser" /etc
/subgid
) ||
{
282 error
"failed to read /etc/subgid for $asuser"
285 if [ -n "$subgid" ]; then
286 debug
1 "$asuser already had subgid=$subgid"
288 debug
1 "adding $asuser:$create_subgid to /etc/subgid"
289 echo "$asuser:$create_subgid" >> /etc
/subgid ||
{
290 error
"failed to add $asuser to /etc/subgid"
294 debug
0 "as $asuser executing ${MYPATH} ${pt_args[*]}"
295 sudo
-Hu "$asuser" ASAN_OPTIONS
=${ASAN_OPTIONS:-} UBSAN_OPTIONS=${UBSAN_OPTIONS:-} "${MYPATH}" "${pt_args[@]}"
298 USERNSEXEC=${USERNSEXEC:-lxc-usernsexec}
299 MYPATH=$(readlink -f "$0") || { echo "failed to get full path to self
: $0"; exit 1; }
302 if [ "$1" = "inside
" ]; then
306 elif [ "$1" = "runtest
" ]; then
310 elif [ "$1" = "setup_and_run
" ]; then
316 name=$(id --user --name) || fail "failed to get username
"
317 if [ "$name" = "root
" ]; then
322 subuid=$(awk -F: '$1 == n { print $2; exit(0); }' "n
=$name" /etc/subuid) &&
323 [ -n "$subuid" ] || fail "did not
find $name in /etc
/subuid
"
325 subgid=$(awk -F: '$1 == n { print $2; exit(0); }' "n
=$name" /etc/subgid) &&
326 [ -n "$subgid" ] || fail "did not
find $name in /etc
/subgid
"
329 uid=$(id --user) || fail "failed to get uid
"
330 gid=$(id --group) || fail "failed to get gid
"
335 ver=$(dpkg-query --show lxc-utils | awk '{print $2}')
336 error "uid
=$uid gid
=$gid name
=$name subuid
=$subuid subgid
=$subgid ver
=$ver"
337 error "lxc-utils
=$ver kver
=$
(uname
-r)"
338 error "USERNSEXEC
=$USERNSEXEC"
343 PASSES=""; FAILS=""; ERRORS=""
344 runcheck nouidgid "f0
:$subuid:$subgid:0:0" "" f0
346 runcheck myuidgid "f0
:$uid:$gid:0:0" \
347 "-m$mapuid -m$mapgid" f0
350 "f0
:$subuid:$subgid:0:0" \
351 "-mu:0:$subuid:1 -mg:0:$subgid:1" f0:0:0
353 runcheck bothsets "f0
:$uid:$gid:0:0 f1
:$subuid:$subgid:1:1 f2
:$uid:$subgid:0:1" \
354 "-m$mapuid -m$mapgid -mu:1:$subuid:1 -mg:1:$subgid:1" \
357 runcheck mismatch "f0
:$uid:$subgid:0:0 f1
:$subuid:$gid:15:31" \
358 "-mu:0:$uid:1 -mg:0:$subgid:1 -mu:15:$subuid:1 -mg:31:$gid:1" \
365 [ -z "${FAILS}" ] || error "FAILS
: ${FAILS}"
366 [ -z "${ERRORS}" ] || error "ERRORS
: ${ERRORS}"
367 [ -z "${FAILS}" -a -z "${ERRORS}" ] || exit 1