@@ -10,8 +10,6 @@ set -euo pipefail
1010# - AICAGE_HOME: posix host home path
1111# - AICAGE_HOST_IS_LINUX: set to non-empty on Linux hosts; empty otherwise
1212
13- AICAGE_USER_HOME_MOUNTS_DIR=" /aicage/user-home"
14-
1513AICAGE_WORKSPACE=" ${AICAGE_WORKSPACE:-/ workspace} "
1614
1715if [[ -z " ${AICAGE_HOST_IS_LINUX:- } " ]]; then
@@ -40,32 +38,6 @@ is_mountpoint() {
4038 [ " $( stat -c %d " $path " 2> /dev/null) " != " $( stat -c %d " $parent " 2> /dev/null) " ]
4139}
4240
43- is_within_mountpoint () {
44- local path=" $1 "
45- local mountinfo mount_point
46- mountinfo=" /proc/self/mountinfo"
47- if [ -r " ${mountinfo} " ]; then
48- while IFS= read -r mount_point; do
49- if [[ " ${path} " == " ${mount_point} " || " ${path} " == " ${mount_point} /" * ]]; then
50- return 0
51- fi
52- done < <( awk ' {print $5}' " ${mountinfo} " )
53- return 1
54- fi
55- local current
56- current=" $path "
57- while true ; do
58- if [ -e " $current " ] && is_mountpoint " $current " ; then
59- return 0
60- fi
61- if [ " $current " = " /" ]; then
62- break
63- fi
64- current=" $( dirname " $current " ) "
65- done
66- return 1
67- }
68-
6941ensure_home_is_not_mounted () {
7042 local path=" $1 "
7143 local current
@@ -82,28 +54,6 @@ ensure_home_is_not_mounted() {
8254 done
8355}
8456
85- replace_symlink () {
86- local target_path=" $1 "
87- local link_path=" $2 "
88- local resolved_path
89-
90- resolved_path=" $link_path "
91- if [[ -e " ${link_path} " || -L " ${link_path} " ]]; then
92- resolved_path=" $( readlink -f " ${link_path} " 2> /dev/null || printf " %s" " ${link_path} " ) "
93- fi
94- if is_within_mountpoint " $resolved_path " ; then
95- return 0
96- fi
97-
98- if [[ -e " ${link_path} " || -L " ${link_path} " ]]; then
99- local timestamp backup_path
100- timestamp=" $( date +%Y%m%d%H%M%S%N) "
101- backup_path=" ${link_path} .${timestamp} "
102- mv " ${link_path} " " ${backup_path} "
103- fi
104- ln -sfn " ${target_path} " " ${link_path} "
105- }
106-
10757copy_skel_if_safe () {
10858 local home_dir=" $1 "
10959 local uid=" $2 "
@@ -141,34 +91,92 @@ set_target_env() {
14191}
14292
14393list_home_mount_points () {
144- local mountinfo
94+ local mountinfo line mount_point
14595 mountinfo=" /proc/self/mountinfo"
14696 [ -r " ${mountinfo} " ] || return 0
147- # /proc/self/mountinfo format:
148- # field 5 is the mount point path
149- # we only want mount points under /aicage/user-home
150- awk -v base=" ${AICAGE_USER_HOME_MOUNTS_DIR} " ' $5 ~ "^"base {print $5}' " ${mountinfo} "
97+
98+ while IFS= read -r line; do
99+ set -- ${line}
100+ mount_point=" $5 "
101+ if [[ " ${mount_point} " == " ${AICAGE_HOME} " || " ${mount_point} " == " ${AICAGE_HOME} /" * ]]; then
102+ printf ' %s\n' " ${mount_point} "
103+ fi
104+ done < " ${mountinfo} " | sort -u
151105}
152106
153- setup_home_mount_links () {
154- local rel_path mount_point host_link root_link
155- [ -d " ${AICAGE_USER_HOME_MOUNTS_DIR} " ] || return 0
107+ normalize_mount_path () {
108+ local path=" $1 "
109+ if [ -d " ${path} " ]; then
110+ printf ' %s/\n' " ${path%/ } "
111+ else
112+ printf ' %s\n' " ${path} "
113+ fi
114+ }
156115
157- while IFS= read -r mount_point; do
158- if [[ " ${mount_point} " == " ${AICAGE_USER_HOME_MOUNTS_DIR} " ]]; then
159- continue
160- fi
161- rel_path=" ${mount_point# ${AICAGE_USER_HOME_MOUNTS_DIR} / } "
162- host_link=" ${AICAGE_HOME} /${rel_path} "
163- mkdir -p " $( dirname " ${host_link} " ) "
164- replace_symlink " ${mount_point} " " ${host_link} "
165-
166- if [[ -z " ${AICAGE_HOST_IS_LINUX:- } " ]]; then
167- root_link=" ${TARGET_HOME} /${rel_path} "
168- mkdir -p " $( dirname " ${root_link} " ) "
169- replace_symlink " ${mount_point} " " ${root_link} "
116+ filter_nested_mount_points () {
117+ local i j
118+ local is_nested
119+ local -a mount_points
120+
121+ mount_points=(" $@ " )
122+ for i in " ${! mount_points[@]} " ; do
123+ mount_points[i]=" $( normalize_mount_path " ${mount_points[i]} " ) "
124+ done
125+
126+ for i in " ${! mount_points[@]} " ; do
127+ [ -n " ${mount_points[i]} " ] || continue
128+ is_nested=0
129+ for j in " ${! mount_points[@]} " ; do
130+ [ -n " ${mount_points[j]} " ] || continue
131+ if [[ " ${i} " -eq " ${j} " ]]; then
132+ continue
133+ fi
134+ if [[ " ${mount_points[i]} " == " ${mount_points[j]} " * ]]; then
135+ is_nested=1
136+ break
137+ fi
138+ done
139+ if [[ " ${is_nested} " -eq 0 ]]; then
140+ printf ' %s\n' " ${mount_points[i]} "
170141 fi
171- done < <( list_home_mount_points)
142+ done
143+ }
144+
145+ ensure_home_mount_parents_owned () {
146+ local uid=" $1 "
147+ local gid=" $2 "
148+ local current mount_point
149+ local -a mount_points mount_points_filtered
150+ local -A visited_dirs
151+ mapfile -t mount_points < <( list_home_mount_points)
152+ mapfile -t mount_points_filtered < <( filter_nested_mount_points " ${mount_points[@]} " )
153+ visited_dirs=()
154+
155+ if [ -d " ${AICAGE_HOME} " ] && ! is_mountpoint " ${AICAGE_HOME} " ; then
156+ chown " ${uid} :${gid} " " ${AICAGE_HOME} "
157+ fi
158+
159+ for mount_point in " ${mount_points_filtered[@]} " ; do
160+ current=" $( dirname " ${mount_point} " ) "
161+ while [[ " ${current} " == " ${AICAGE_HOME} " || " ${current} " == " ${AICAGE_HOME} /" * ]]; do
162+ if [[ " ${current} " == " ${AICAGE_HOME} " ]]; then
163+ break
164+ fi
165+ if [[ -n " ${visited_dirs[$current]:- } " ]]; then
166+ current=" $( dirname " ${current} " ) "
167+ continue
168+ fi
169+ visited_dirs[" $current " ]=1
170+ if is_mountpoint " ${current} " ; then
171+ current=" $( dirname " ${current} " ) "
172+ continue
173+ fi
174+ if [ -d " ${current} " ]; then
175+ chown " ${uid} :${gid} " " ${current} "
176+ fi
177+ current=" $( dirname " ${current} " ) "
178+ done
179+ done
172180}
173181
174182setup_user_and_group () {
@@ -193,7 +201,11 @@ setup_user_and_group() {
193201 groupadd -g " ${AICAGE_GID} " " ${TARGET_USER} "
194202 fi
195203
196- useradd --create-home -u " ${AICAGE_UID} " -g " ${AICAGE_GID} " -d " ${AICAGE_HOME} " -s /bin/bash " ${TARGET_USER} "
204+ if [[ -d " ${AICAGE_HOME} " ]]; then
205+ useradd --no-create-home -u " ${AICAGE_UID} " -g " ${AICAGE_GID} " -d " ${AICAGE_HOME} " -s /bin/bash " ${TARGET_USER} "
206+ else
207+ useradd --create-home -u " ${AICAGE_UID} " -g " ${AICAGE_GID} " -d " ${AICAGE_HOME} " -s /bin/bash " ${TARGET_USER} "
208+ fi
197209 TARGET_HOME=" ${AICAGE_HOME} "
198210
199211 copy_skel_if_safe " ${AICAGE_HOME} " " ${AICAGE_UID} " " ${AICAGE_GID} "
@@ -224,14 +236,10 @@ setup_docker_group() {
224236}
225237
226238setup_workspace () {
227- if [ -e " ${AICAGE_WORKSPACE} " ]; then
239+ if [ -e " ${AICAGE_WORKSPACE} " ] && ! is_mountpoint " ${AICAGE_WORKSPACE} " ; then
228240 chown " ${AICAGE_UID} :${AICAGE_GID} " " ${AICAGE_WORKSPACE} "
229241 fi
230- if [ -d " ${TARGET_HOME} " ]; then
231- if ! is_mountpoint " /home" && ! is_mountpoint " ${TARGET_HOME} " ; then
232- chown " ${AICAGE_UID} :${AICAGE_GID} " " ${TARGET_HOME} "
233- fi
234- fi
242+ ensure_home_mount_parents_owned " ${AICAGE_UID} " " ${AICAGE_GID} "
235243}
236244
237245ensure_home_is_not_mounted " /home"
247255fi
248256
249257ensure_home_is_not_mounted " ${AICAGE_HOME} "
250- setup_home_mount_links
251258set_target_env " ${TARGET_HOME} " " ${TARGET_USER} "
252259
253260if [[ ! -e " ${AICAGE_WORKSPACE} " ]]; then
0 commit comments