summaryrefslogtreecommitdiffstats
path: root/core/includes/chroot.inc
diff options
context:
space:
mode:
Diffstat (limited to 'core/includes/chroot.inc')
-rw-r--r--core/includes/chroot.inc248
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
+}