2 # ==============================================================================
3 # portsreinstall-chroot library script
4 # Overlay onto lib/libfs.sh for portsreinstall-chroot
5 # - File system operations -
6 # Copyright (C) 2018 Mamoru Sakaue, MwGhennndo, All Rights Reserved.
7 # This software is distributed under the 2-Clause BSD License.
8 # ==============================================================================
10 FS_UNMOUNT_RETIAL_MAXNUM=5 # Number of retrial of unmounting
11 FS_UNMOUNT_RETIAL_WAIT=3 # Wait seconds in retrial of unmounting
13 # ============= Check the safety of the base directory of builder chroot environment =============
14 fs_chk_safety_basedir ()
18 [ -n "$basedir" ] || return
20 /|/bin|/boot|/compat|/dev|/entropy|/etc|/home|/host|/lib|/libexec|/net|/proc|/rescue|/root|/sbin|/sys|/tmp|/usr|/var)
24 expr "$basedir" : '^/boot/.*' > /dev/null && return 1
25 expr "$basedir" : '^/compat/.*' > /dev/null && return 1
26 expr "$basedir" : '^/dev/.*' > /dev/null && return 1
27 expr "$basedir" : '^/etc/.*' > /dev/null && return 1
28 expr "$basedir" : '^/lib/.*' > /dev/null && return 1
29 expr "$basedir" : '^/libexec/.*' > /dev/null && return 1
30 expr "$basedir" : '^/proc/.*' > /dev/null && return 1
31 expr "$basedir" : '^/sbin/.*' > /dev/null && return 1
35 # ============= Safeguard of the base directory of builder chroot environment =============
36 fs_safeguard_basedir ()
40 fs_chk_safety_basedir "$basedir" && return
41 message_echo "ERROR: The base directory [$opt_basedir] is not safe." >&2
45 # ============= Build the file systems for the builder chroot environment =============
50 [ -e "${DBDIR}/mount_manifest" ] && return
51 message_echo "Building the file systems for builder chroot environment (if not yet)."
52 fs_safeguard_basedir "$opt_basedir"
53 fs_unmount "$systembase" || return
54 mkdir -p "$systembase/$opt_basedir"
55 # Prescan the f file system of the original environment
56 cp /dev/null "${TMPDIR}/fs_build_chroot:directories"
59 echo bin compat etc lib libexec root sbin sys usr var | tr ' ' '\n'
60 echo "$opt_extra_dirs" | tr "$opt_extra_dirs_delim" '\n'
61 } | grep -v '^[[:space:]]*$' | sort -u > ${TMPDIR}/fs_build_chroot:sys_dirs
62 sysdirs_ptn="^/*(`str_escape_regexp_filter < ${TMPDIR}/fs_build_chroot:sys_dirs | tr '\n' '|' | sed 's/\|$//'`)/+"
65 [ -e "$systembase"/$directory ] || continue
66 if [ -L "$systembase"/$directory ]
68 src_mountpoint_real=`realpath "$systembase"/$directory`
69 printf '%s\t%s\n' link $directory >> ${TMPDIR}/fs_build_chroot:directories
70 if ! echo "$src_mountpoint_real/" | grep -qE "$sysdirs_ptn"
72 printf '%s\t%s\n' real "$src_mountpoint_real" >> ${TMPDIR}/fs_build_chroot:directories
73 tmpdir_descendant=${TMPDIR}/fs_build_chroot:descendant/$src_mountpoint_real
74 mkdir -p "$tmpdir_descendant"
75 fs_get_descendant_mount_info "$systembase/$src_mountpoint_real" > $tmpdir_descendant/list
77 elif [ -d "$systembase"/$directory ]
79 printf '%s\t%s\n' real $directory >> ${TMPDIR}/fs_build_chroot:directories
80 tmpdir_descendant=${TMPDIR}/fs_build_chroot:descendant/$directory
81 mkdir -p "$tmpdir_descendant"
82 fs_get_descendant_mount_info "$systembase/$directory" > $tmpdir_descendant/list
84 done < ${TMPDIR}/fs_build_chroot:sys_dirs
86 # Prepare the grand base of the chroot environment
88 cd "$systembase/$opt_basedir"
89 for directory in builder mask store
91 [ -d $directory ] || mkdir $directory
94 # Directories to share with the host environment by nullfs
95 if [ "x$opt_share_port_pkgs_dirs" = xyes ]
98 echo "$PORTSNAP_WORKDIR"
99 echo "$PKGNG_PKG_CACHEDIR"
100 fi | str_regularize_df_path_filter | grep -v '^[[:space:]]*$' | sort -u > ${DBDIR}/shared_dirs.lst
101 str_escape_regexp_filter < ${DBDIR}/shared_dirs.lst | sed 's|^|^|;s|$|\/|' > ${TMPDIR}/fs_build_chroot:shared_dirs.regexp
102 cp /dev/null "${TMPDIR}/fs_build_chroot:shared_dirs:added"
103 # Build target directories and the manifest for mounting
104 cp /dev/null "${DBDIR}/mount_manifest.tmp"
106 cd "$systembase/$opt_basedir"/builder
109 type=`echo "$srcline" | cut -f 1`
110 directory=`echo "$srcline" | cut -f 2`
113 [ -e "$directory" -o -L "$directory" ] || cp -RpP "$systembase/$directory" .
116 mkdir -p "./$directory"
117 masktarget=$systembase/$opt_basedir/mask/$directory
118 mkdir -p "$masktarget"
119 printf '%s\t%s\t%s\t%s\n' nullfs "$systembase"/$directory $directory ro >> ${DBDIR}/mount_manifest.tmp
120 printf '%s\t%s\t%s\t%s\n' unionfs "$masktarget" $directory rw,noatime >> ${DBDIR}/mount_manifest.tmp
123 fs=`echo "$srcline" | cut -f 1`
124 mp=`echo "$srcline" | cut -f 2 | str_regularize_df_path_filter`
125 relative=`echo "$srcline" | cut -f 3 | str_regularize_df_path_filter`
126 rm -f "${TMPDIR}/fs_build_chroot:shared_dirs:is_under"
127 while read -r shared_path_regexp
129 echo "/$directory/$relative/" | grep -qE "$shared_path_regexp" || continue
130 grep -qFx "/$directory/$relative/" "${DBDIR}/shared_dirs.lst" || continue
131 if ! grep -qFx "/$directory/$relative/" "${TMPDIR}/fs_build_chroot:shared_dirs:added"
133 echo "/$directory/$relative/" >> ${TMPDIR}/fs_build_chroot:shared_dirs:added
134 mp_share=`realpath "$systembase/$directory/$relative"`
135 printf '%s\t%s\t%s\t%s\n' nullfs "$mp_share" "$directory/$relative" rw >> ${DBDIR}/mount_manifest.tmp
137 touch "${TMPDIR}/fs_build_chroot:shared_dirs:is_under"
138 done < ${TMPDIR}/fs_build_chroot:shared_dirs.regexp
141 masktarget=$systembase/$opt_basedir/mask/$directory/$relative
142 mkdir -p "$masktarget"
143 if [ -e "${TMPDIR}/fs_build_chroot:shared_dirs:is_under" ]
145 printf '%s\t%s\t%s\t%s\n' nullfs "$mp" "$directory/$relative" rw >> ${DBDIR}/mount_manifest.tmp
147 printf '%s\t%s\t%s\t%s\n' nullfs "$mp" "$directory/$relative" ro >> ${DBDIR}/mount_manifest.tmp
148 printf '%s\t%s\t%s\t%s\n' unionfs "$masktarget" "$directory/$relative" rw,noatime >> ${DBDIR}/mount_manifest.tmp
152 printf '%s\t%s\t%s\t%s\n' devfs devfs "$directory/$relative" rw >> ${DBDIR}/mount_manifest.tmp
155 printf '%s\t%s\t%s\t%s\n' fdescfs fdesc "$directory/$relative" rw >> ${DBDIR}/mount_manifest.tmp
158 printf '%s\t%s\t%s\t%s\n' procfs proc "$directory/$relative" rw >> ${DBDIR}/mount_manifest.tmp
161 printf '%s\t%s\t%s\t%s\n' linprocfs linproc "$directory/$relative" rw >> ${DBDIR}/mount_manifest.tmp
164 printf '%s\t%s\t%s\t%s\n' tmpfs tmpfs "$directory/$relative" rw,mode=1777 >> ${DBDIR}/mount_manifest.tmp
167 done < ${TMPDIR}/fs_build_chroot:descendant/$directory/list
170 done < ${TMPDIR}/fs_build_chroot:directories
171 for directory in dev proc tmp
173 [ -e $directory ] || mkdir $directory
175 printf '%s\t%s\t%s\t%s\n' devfs devfs dev rw >> ${DBDIR}/mount_manifest.tmp
176 printf '%s\t%s\t%s\t%s\n' fdescfs fdesc dev/fd rw >> ${DBDIR}/mount_manifest.tmp
177 printf '%s\t%s\t%s\t%s\n' procfs proc proc rw >> ${DBDIR}/mount_manifest.tmp
178 printf '%s\t%s\t%s\t%s\n' tmpfs tmpfs tmp rw,mode=1777 >> ${DBDIR}/mount_manifest.tmp
179 mkdir -p ."${PROGRAM}"
180 cd "$systembase/$opt_basedir/mask"
181 if [ ! -e root/.cshrc ]
183 tmp_cshrc=${TMPDIR}/fs_build_chroot:.cshrc
184 [ -d root ] || mkdir root
185 if [ -e "$systembase"/root/.cshrc ]
187 cp -p "$systembase"/root/.cshrc "$tmp_cshrc"
188 cp -p "$systembase"/root/.cshrc "root/.cshrc.bak-${APPNAME}"
189 elif [ -e "$systembase"/usr/share/skel/dot.cshrc ]
191 cp -p "$systembase"/usr/share/skel/dot.cshrc "$tmp_cshrc"
192 touch "root/.cshrc.bak-${APPNAME}"
194 cp /dev/null "$tmp_cshrc"
197 echo 'set prompt="%N@[builder]%m:%~ %# "' >> $tmp_cshrc
198 mv "$tmp_cshrc" root/.cshrc
200 printf '%s\t%s\t%s\t%s\n' nullfs "$systembase/$opt_basedir"/store ".${PROGRAM}" rw >> ${DBDIR}/mount_manifest.tmp
202 mv "${DBDIR}/mount_manifest.tmp" "${DBDIR}/mount_manifest"
205 # ============= Check whether the file systems for the builder chroot environment are all mounted =============
210 [ -e "${DBDIR}/mount_manifest" ] || return
211 rm -rf ${TMPDIR}/fs_chk_mount:remains
214 type=`echo "$srcline" | cut -f 1`
215 target=`echo "$srcline" | cut -f 2`
216 [ "x$type" = xnullfs -o "x$type" = xunionfs ] && target=$systembase/$target
217 directory=`echo "$srcline" | cut -f 3`
218 opt=`echo "$srcline" | cut -f 4`
219 if ! fs_chk_mounted "$type" "$target" "$systembase/$opt_basedir/builder/$directory"
221 touch "${TMPDIR}"/fs_chk_mount:remains
224 done < ${DBDIR}/mount_manifest
225 [ ! -e "${TMPDIR}"/fs_chk_mount:remains ]
228 # ============= Terminate when the file systems for the builder chroot environment cannot be mounted =============
229 fs_terminate_if_mount_unavailable ()
233 fs_chk_mount "$systembase" && return
234 fs_chk_mount_privilege && return
235 temp_terminate_process ()
239 [ $opt_batch_mode = yes ] && return
242 message_echo "Aborted by unexpected error" >&2
246 message_echo "INFO: Terminated for mounting file systems because this utility was executed at a virtual (chroot or jail) environment."
247 message_echo "Execute"
248 basedir=`fs_get_system_basedir`
251 message_echo " $basedir${SHAREDIR}/bin/portsreinstall-chroot-mount"
252 message_echo "at the grand host environment."
254 message_echo " \$BASEDIR${SHAREDIR}/bin/portsreinstall-chroot-mount"
255 message_echo "at the grand host environment, where \$BASEDIR denotes the base directory of this virtual environment."
257 message_echo "After its successful execution, rerun"
258 if [ -n "$COMMAND_RESTART" ]
260 message_echo " ${APPNAME} $COMMAND_RESTART"
262 message_echo " ${APPNAME}"
268 # ============= Generate a custom fstab file for the builder chroot environment =============
273 [ "${DBDIR}/fstab" -nt "${DBDIR}/mount_manifest" ] && return
274 fs_safeguard_basedir "$opt_basedir"
277 type=`echo "$srcline" | cut -f 1`
278 target=`echo "$srcline" | cut -f 2`
279 directory=`echo "$srcline" | cut -f 3`
280 opt=`echo "$srcline" | cut -f 4`
281 [ "x$type" = xnullfs -o "x$type" = xunionfs ] && target=$systembase/$target
282 target=`str_regularize_df_path "$target"`
283 mp=`str_regularize_df_path "$systembase/$opt_basedir/builder/$directory"`
284 opt=`echo "$opt" | sed 's/[[:space:]]/\\040/g'`
285 echo "$target $mp $type $opt 0 0"
286 done < ${DBDIR}/mount_manifest > ${DBDIR}/fstab
289 # ============= Mount the file systems for the builder chroot environment if not yet =============
294 message_echo "Mounting the file systems for builder chroot environment."
296 if ! mount -F "${DBDIR}/fstab" -a || ! fs_chk_mount "$systembase"
298 message_echo "Error: Failed to mount the file systems. Some of them remain unmounted." >&2
301 message_echo "Mounting done."
304 # ============= Check whether the file systems for the builder chroot environment are all unmounted or destroyed =============
309 [ -e "${DBDIR}/mount_manifest" ] || return 0
310 rm -rf ${TMPDIR}/fs_chk_unmount:remains
311 tail -r "${DBDIR}/mount_manifest" | while read srcline
313 type=`echo "$srcline" | cut -f 1`
314 target=`echo "$srcline" | cut -f 2`
315 [ "x$type" = xnullfs -o "x$type" = xunionfs ] && target=$systembase/$target
316 directory=`echo "$srcline" | cut -f 3`
317 opt=`echo "$srcline" | cut -f 4`
318 if fs_chk_mounted "$type" "$target" "$systembase/$opt_basedir/builder/$directory"
320 touch "${TMPDIR}"/fs_chk_unmount:remains
324 [ ! -e "${TMPDIR}"/fs_chk_unmount:remains ]
327 # ============= Terminate when the file systems for the builder chroot environment cannot be unmounted =============
328 fs_terminate_if_unmount_unavailable ()
332 fs_chk_unmount "$systembase" && return
333 fs_chk_unmount_privilege && return
334 temp_terminate_process ()
338 [ $opt_batch_mode = yes ] && return
341 message_echo "Aborted by unexpected error" >&2
345 message_echo "INFO: Terminated for unmounting file systems because this utility was executed at a virtual (chroot or jail) environment."
346 message_echo "Execute"
347 basedir=`fs_get_system_basedir`
350 message_echo " $basedir${SHAREDIR}/bin/portsreinstall-chroot-mount unmount"
351 message_echo "at the grand host environment."
353 message_echo " \$BASEDIR${SHAREDIR}/bin/portsreinstall-chroot-mount unmount"
354 message_echo "at the grand host environment, where \$BASEDIR denotes the base directory of this virtual environment."
356 message_echo "After its successful execution, rerun"
357 if [ -n "$COMMAND_RESTART" ]
359 message_echo " ${APPNAME} $COMMAND_RESTART"
361 message_echo " ${APPNAME}"
367 # ============= Unmount file systems for the chroot environment =============
372 [ ! -d "$systembase/$opt_basedir"/builder ] && return
373 [ -e "${DBDIR}/mount_manifest" ] || return 0
374 message_echo "Unmounting the file systems for builder chroot."
376 umount -F "${DBDIR}/fstab" -af 2> /dev/null || :
377 itrial=${FS_UNMOUNT_RETIAL_MAXNUM}
378 while [ $itrial -gt 0 ]
380 fs_chk_unmount "$systembase" && break
381 message_echo "(Retrying to unmount the file systems...)" >&2
382 sleep ${FS_UNMOUNT_RETIAL_WAIT}
383 umount -F "${DBDIR}/fstab" -af 2> /dev/null || :
384 itrial=$(($itrial-1))
388 message_echo "WARNING: Failed to unmount the file systems. Some of them remain mounted." >&2
391 message_echo "Unmounting done."
394 # ============= Destroy the chroot environment =============
399 fs_chk_safety_basedir "$opt_basedir" || return 0
400 [ ! -d "$opt_basedir" ] && return
401 fs_terminate_if_unmount_unavailable "$systembase"
402 fs_unmount "$systembase" || return
403 chflags -R noschg "$opt_basedir"
404 rm -rf "$opt_basedir"