diff options
Diffstat (limited to 'core/includes/chroot.inc')
-rw-r--r-- | core/includes/chroot.inc | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/core/includes/chroot.inc b/core/includes/chroot.inc new file mode 100644 index 00000000..b7f68f65 --- /dev/null +++ b/core/includes/chroot.inc @@ -0,0 +1,248 @@ +# ----------------------------------------------------------------------------- +# +# Copyright (c) 2014 - OpenSLX GmbH +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# +# Common functions for chrooting +# +# ----------------------------------------------------------------------------- + +declare -rg CHROOT_TEMPDIR="${ROOT_DIR}/tmp/chroot" +declare -rg CHROOT_MOUNTDIR="${CHROOT_TEMPDIR}/rootmount" +declare -rg CHROOT_BINDDIR="${CHROOT_TEMPDIR}/rootbind" +declare -rg CHROOT_LOWERDIR="/" +declare -rg CHROOT_BINDMOUNTS="/dev /proc /sys /run" + + +# Helper function to setup the directory structure +chroot_prepare_dirs() { + # first check if CHROOT_TEMPDIR exists + if [ -d "${CHROOT_TEMPDIR}" ]; then + # try to umount and rmdir CHROOT_MOUNTDIR + umount "${CHROOT_MOUNTDIR}" 2>/dev/null + if [ -d "${CHROOT_MOUNTDIR}" ]; then + rmdir "${CHROOT_MOUNTDIR}" || perror "Could not remove CHROOT_MOUNTDIR '${CHROOT_MOUNTDIR}', meaning it has stuff in it. Aborting..." + fi + + # try to umount and rmdir CHROOT_BINDDIR + umount "${CHROOT_BINDDIR}" 2>/dev/null + if [ -d "${CHROOT_BINDDIR}" ]; then + rmdir "${CHROOT_BINDDIR}" || perror "Could not remove CHROOT_BINDDIR '${CHROOT_BINDDIR}', meaning it has stuff in it. Aborting..." + fi + + # try to rmdir CHROOT_TEMPDIR + if [ -d "${CHROOT_TEMPDIR}" ]; then + rmdir "${CHROOT_TEMPDIR}" || perror "Could not remove CHROOT_TEMPDIR '${CHROOT_TEMPDIR}', meaning it has stuff in it. Aborting..." + fi + fi + + mkdir -p "${CHROOT_TEMPDIR}" || perror "Could not create base directory for mount directories $CHROOT_TEMPDIR." + for DIR in "${CHROOT_BINDDIR}" "${CHROOT_MOUNTDIR}"; do + mkdir -p "${DIR}" || perror "Could not create directory for mount directory $DIR." + done +} + +# Helper to mount the overlay structure: +# - bind mount system / to CHROOT_BINDDIR and make it read-only +# - make an overlay from CHROOT_LOWERDIR CHROOT_UPPERDIR +# - bind mount additional pseudo-fs (as given in CHROOT_BINDMOUNTS) +chroot_prepare_mounts() { + + # first mount / on CHROOT_BINDDIR and remount read-only + mount -o bind "${CHROOT_LOWERDIR}" "${CHROOT_BINDDIR}" \ + || perror "Could not bind-mount CHROOT_LOWERDIR '$CHROOT_LOWERDIR' to CHROOT_BINDDIR '$CHROOT_BINDDIR'." + mount -o remount,ro,bind "${CHROOT_BINDDIR}" || perror "Could not remount CHROOT_BINDDIR '$CHROOT_BINDDIR' read-only." + + # check that it really is read-only + [ "x$(mount | grep -E "^/ on ${CHROOT_BINDDIR}" | grep -v '\(.*ro.*\)')" != "x" ] \ + && perror "CHROOT_BINDDIR '${CHROOT_BINDDIR}' is not read-only! Aborting..." + + # Note: The overlay fs mount syntax seems to be changed between Ubuntu 14.04.2 and 14.04.3 (Kernel 3.13 and 3.19). Instead of + # checking overlay-modinfo (which may fail if overlayfs is not incorporated as module) or kernel versions, we simply try to + # mount 'old school' first and then, if that fails, the new way to mount with workdir. See differences in mount syntax below. + pinfo "Now mounting overlayfs. Trying old mount syntax (up to Kernel 3.13) ..." + mount -t overlayfs overlayfs -o lowerdir="${CHROOT_BINDDIR}",upperdir="${CHROOT_UPPERDIR}" "${CHROOT_MOUNTDIR}" 2>/dev/null + if [ $? -ne 0 ]; then + pinfo "Old mount syntax failed. Trying new mount syntax (Kernel 3.19+) ..." + # We have to use a overlayfs workdir which _must_ be in the same filesystem as CHROOT_UPPERDIR. So + # we traverse to the directory below CHROOT_UPPERDIR and mkdir/mktemp a workdir there. In the possible + # case that CHROOT_UPPERDIR is the root dir of a filesystem there's nothing we can do but exit. + CHROOT_WORKDIR="$(mktemp -d $(dirname ${CHROOT_UPPERDIR})/workdirXXX)" \ + || perror "Could not mkdir overlayfs workdir $CHROOT_WORKDIR for new overlayfs mount syntax." + # Now we try to mount the overlayfs in the new fashion: + mount -t overlayfs overlayfs -o lowerdir="$CHROOT_LOWERDIR",upperdir="${CHROOT_UPPERDIR}",workdir="${CHROOT_WORKDIR}" "${CHROOT_MOUNTDIR}" \ + || perror "Could not mount (overlayfs) $CHROOT_LOWERDIR, $CHROOT_UPPERDIR to $CHROOT_BINDDIR." + pinfo "New overlayfs mount syntax has worked, commencing." + else + pinfo "Old overlayfs mount syntax has worked, commencing." + fi + + # mount pseudo-filesystems + for DIR in $CHROOT_BINDMOUNTS; do + mount -o bind "${DIR}" "${CHROOT_MOUNTDIR}/${DIR}" \ + || perror "Could not bind mount '$DIR' into CHROOT_MOUNTDIR/DIR '$CHROOT_MOUNTDIR/$DIR'." + done +} + +# Helper to generate the mighty autoexec.bat +chroot_gen_autoexec() { + # create the script to be automatically executed. + cat >"${CHROOT_MOUNTDIR}/autoexec.bat"<<-EOF + #!/bin/bash + ####################################################### + # # + # Warning! # + # # + # This file is only meant to be executed within # + # the specially chrooted mltk building environment. # + # # + # Do NOT execute it if you are not sure what you do, # + # it may be very harmful if being run in a normal # + # system environment! # + # # + ####################################################### + echo "chroot started successfully." + EOF + + # dump the piped input to it + cat >> "${CHROOT_MOUNTDIR}/autoexec.bat" + + # make it executable + chmod +x "${CHROOT_MOUNTDIR}/autoexec.bat" || perror "Failed to make '${CHROOT_MOUNTDIR}/autoexec.bat' exeutable." +} + +chroot_handle_whiteouts() { + local WHITEOUT_LIST="${CHROOT_UPPERDIR}/overlay.whiteout.list" + rm -f -- "$WHITEOUT_LIST=" + #mkdir -p "$(dirname "$WHITEOUT_LIST")" || perror "Could not create $(dirname "$WHITEOUT_LIST")" + pdebug "Searching for overlayfs-whiteouts ..." + for WHITEOUT in $(find "$CHROOT_UPPERDIR" -lname "(overlay-whiteout)"); do + pdebug "Whiteout found: $WHITEOUT" + echo "/./${WHITEOUT#$CHROOT_UPPERDIR}" >> "$WHITEOUT_LIST" + rm -f -- "$WHITEOUT" || perror "Could not delete whiteout $WHITEOUT!" + done + pinfo "Whiteout list dumped to '${CHROOT_UPPERDIR}/overlay.whiteout.list'" +} + +############################################################################### +# +# MAIN FUNCTION +# +# Main function to be called from the outside +# Usage: +# chroot_run <build_dir> < <code_to_exec_in_chroot> +# +# Example: +# chroot_run /tmp/chroot_build <<-EOF +# echo "This will be executed inside the chroot" +# EOF +# +# It will run: +# - chroot_prepare +# - chroot $CHROOT_TEMPDIR/rootmount +# - executes $CHROOT_TEMPDIR/rootmount/autoexec.bat +# - chroot_cleanup +chroot_run() { + # check args + [ $# -eq 1 ] || perror "'chroot_run' requires exactly 1 parameter. Given $#. Use 'chroot_run <build_dir>'" + + local CHROOT_UPPERDIR="$1" + mkdir -p "$1" + + # first prepare the dir structure + chroot_prepare_dirs || perror "'chroot_prepare_dirs' failed with $?." + chroot_prepare_mounts || perror "'chroot_prepare_mounts' failed with $?." + + # generate the code to be executed when chroot'ing + chroot_gen_autoexec || perror "'chroot_gen_autoexec' failed with $?." + + # do the chroot + exec 0>&8 # This redirection is used for debugging within a chroot + chroot --userspec root:root "${CHROOT_MOUNTDIR}" /autoexec.bat + local RET=$? + if [ "$RET" -eq 0 ]; then + pinfo "chroot executed '${CHROOT_MOUNTDIR}/autoexec.bat' succeeded." + else + perror "Failed to run '$CHROOT_MOUNTDIR/autoexec.bat' inside the chroot to '$CHROOT_MOUNTDIR' with error code: $RET" + fi + + # handle whiteouts + chroot_handle_whiteouts || perror "'chroot_handle_whiteouts' failed with error code: $?" + + # finally cleanup all the mounting stuff we did previously + chroot_cleanup_mounts || perror "'chroot_cleanup' failed with $?." +} + +############################################################################### +# +# CLEANUP FUNCTIONS +# +# Helper to check if the given path is mounted +chroot_check_mount_point() { + [ "$#" -eq 1 ] || perror "'chroot_check_mount_point' called with $# arguements, only 1 accepted." + local MOUNT="$1" + if [ "x$(mount | grep "$(readlink -f $MOUNT)")" != "x" ]; then + # still mounted + pdebug "'$MOUNT' is mounted!" + return 1 + else + pdebug "'$MOUNT' is not mounted." + return 0 + fi +} + +# Helper to umount the given path +chroot_umount() { + [ "$#" -eq 1 ] || perror "'chroot_umount' called with $# arguments, only 1 accepted." + local MOUNT="$1" + + # check if MOUNT is mounted + if ! chroot_check_mount_point "${MOUNT}"; then + # still mounted + if umount -l "${MOUNT}"; then + pdebug "Successfully umounted '${MOUNT}'." + else + pwarning "Could not umount '${MOUNT}'! Trying again..." + # now it gets ugly + for i in `seq 1 5`; do + umount -l "${MOUNT}" && return 0 + done + perror "Could not umount '${MOUNT}' after 5 tries! This shouldn't happen. Check your scripts." + fi + else + pdebug "'${MOUNT}' is not mounted." + fi + + # better be safe than sorry + chroot_check_mount_point "$MOUNT" || perror "'$MOUNT' is still mounted, exiting before something bad happens..." +} + +# Helper to cleanup the temporary mounts +chroot_cleanup_mounts() { + if [[ "$(mount | grep -c $CHROOT_TEMPDIR)" -gt 0 ]]; then # No point in unmounting then... + for DIR in $CHROOT_BINDMOUNTS; do + chroot_umount "${CHROOT_MOUNTDIR}/${DIR}" + done + chroot_umount "${CHROOT_MOUNTDIR}" + chroot_umount "${CHROOT_BINDDIR}" + else + pinfo "Nothing chroot-related is mounted - exiting." + fi + + # In case of 'new' overlayfs mount - should perhaps be handled via flag... + if [ -d "${CHROOT_WORKDIR}" ]; then + # Too much of a coward to rm -rf somewhere. Both directories should be empty so we use rmdir. + rmdir "${CHROOT_WORKDIR}/work" && pinfo "rmdir-ed CHROOT_WORKDIR/work ${CHROOT_WORKDIR}/work needed for new overlayfs mount syntax." \ + || pinfo "Could not rmdir CHROOT_WORKDIR/work ${CHROOT_WORKDIR}/work - clean it by hand." + rmdir "${CHROOT_WORKDIR}" && pinfo "rmdir-ed CHROOT_WORKDIR ${CHROOT_WORKDIR} needed for new overlayfs mount syntax." \ + || pinfo "Could not rmdir CHROOT_WORKDIR ${CHROOT_WORKDIR} needed for new overlayfs mount syntax - clean by hand." + fi +} |