#!/bin/bash

## Copyright (C) 2012 - 2023 ENCRYPTED SUPPORT LP <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

set -x
set -e

true "INFO: Currently running script: $BASH_SOURCE $@"

MYDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

source "$MYDIR/pre"
source "$MYDIR/colors"
source "$MYDIR/variables"

error_handler_chroot-raw() {
   : echo "
${red}${bold}BASH_COMMAND${reset}: $BASH_COMMAND
${red}${bold}ERROR $BASH_SOURCE: | caller: $(caller)${reset}
"
   exit 1
}

main() {
   trap "error_handler_chroot-raw" ERR INT TERM

   sync

   if [ "$dist_build_install_to_root" = "true" ]; then
      true
   else
      devices_mount
      chroot_files_cleanup
      chroot_mount_files
   fi
}

devices_mount() {
   #########
   ## /dev #
   #########
   ## update-grub
   ## W: Couldn't identify type of root file system for fsck hook
   ##
   ## setupcon: None of /etc/default/keyboard, /etc/default/console-setup, /home/user/.console-setup, /home/user/.keyboard exists.
   ##
   ## /usr/share/initramfs-tools/hooks/keymap: 35: /usr/share/initramfs-tools/hooks/keymap: cannot open /var/tmp/mkinitramfs_2xz9rK/morefiles: No such file
   ##
   ## In past we needed /dev to create a swapfile.
   ## dd if=/dev/zero of=/swapfile1 bs=1024 count=524288
   $SUDO_TO_ROOT mkdir --parents "$CHROOT_FOLDER/dev"
   $SUDO_TO_ROOT mount -t devtmpfs udev "$CHROOT_FOLDER/dev"

   ##########
   ## /proc #
   ##########
   $SUDO_TO_ROOT mkdir --parents "$CHROOT_FOLDER/proc"
   $SUDO_TO_ROOT mount -t proc none "$CHROOT_FOLDER/proc"

   #########
   ## /sys #
   #########

   ## required by dracut
   $SUDO_TO_ROOT mkdir --parents "$CHROOT_FOLDER/sys"
   $SUDO_TO_ROOT mount -t sysfs none "$CHROOT_FOLDER/sys"

   #############
   ## /dev/pts #
   #############

   ## Was required in the past by unbuffer (was required by apt-get-wrapper).
   ## Might not be required anymore.
   $SUDO_TO_ROOT mkdir --parents "$CHROOT_FOLDER/dev/pts"
   $SUDO_TO_ROOT mount -t devpts devpts "$CHROOT_FOLDER/dev/pts"

   #########
   ## /run #
   #########

   ## Debugging.
   #$SUDO_TO_ROOT $CHROOT mount | grep /run || true
   #$SUDO_TO_ROOT $CHROOT ls -la /run || true
   #$SUDO_TO_ROOT $CHROOT ls -la /run/udev || true
   #$SUDO_TO_ROOT $CHROOT ls -la /run/udev/data || true

   ## https://piiis.blogspot.com/2013/07/fedora-dracut-in-chroot-environment.html
   $SUDO_TO_ROOT $CHROOT mkdir --parents /run/udev/data

   ## Debugging.
   #$SUDO_TO_ROOT $CHROOT ls -la /run/udev/data || true
}

chroot_files_cleanup() {
   ## Leftover from build process using mmdebstrap.
   if test -f "$CHROOT_FOLDER/etc/apt/apt.conf.d/99mmdebstrap" ; then
      $SUDO_TO_ROOT rm --verbose "$CHROOT_FOLDER/etc/apt/apt.conf.d/99mmdebstrap"
   fi

   ##########################
   ## /etc/apt/sources.list #
   ##########################

   ## XXX: This is not the cleanest solution. Doesn't belong here.
   ## Other options:
   ## - Using `grml-debootstrap` with `--post-scripts`.
   ## - Inventing another build step just for this which would then require another
   ##   mount and umount. Since too many mount/umount actions can lead to triggering bugs,
   ##   this is best avoided.
   ## Both other options seem more complex and error-prone than this hack.

   test -f "$dist_build_sources_list_primary"

   if test -f "$CHROOT_FOLDER/etc/apt/sources.list" ; then
      $SUDO_TO_ROOT chmod o+r "$CHROOT_FOLDER/etc/apt/sources.list"
      if diff "$dist_build_sources_list_primary" "$CHROOT_FOLDER/etc/apt/sources.list" ; then
         true "INFO: Host $dist_build_sources_list_primary matches chroot $CHROOT_FOLDER/etc/apt/sources.list, deleting, ok."
         $SUDO_TO_ROOT rm --verbose "$CHROOT_FOLDER/etc/apt/sources.list"
      else
         error "ERROR: Host $dist_build_sources_list_primary does not match chroot $CHROOT_FOLDER/etc/apt/sources.list"
      fi
   else
      true "INFO: chroot $CHROOT_FOLDER/etc/apt/sources.list, does not exist, ok."
   fi

   ## Delete extraneous, duplicate /etc/apt/sources.list.d/0000debian_stable_current_clearnet.list by mmdebstrap.
   if test -f "$CHROOT_FOLDER/$dist_mmdebstrap_build_sources_list_primary" ; then
      chmod o+r "$CHROOT_FOLDER/$dist_mmdebstrap_build_sources_list_primary"
      if diff "$dist_build_sources_list_primary" "$CHROOT_FOLDER/$dist_mmdebstrap_build_sources_list_primary" ; then
         true "INFO: Host $dist_build_sources_list_primary matches chroot $CHROOT_FOLDER/$dist_mmdebstrap_build_sources_list_primary, deleting, ok."
         $SUDO_TO_ROOT rm --verbose "$CHROOT_FOLDER/$dist_mmdebstrap_build_sources_list_primary"
      else
         error "ERROR: Host $dist_build_sources_list_primary does not match chroot $CHROOT_FOLDER/$dist_mmdebstrap_build_sources_list_primary"
      fi
   else
      true "INFO: chroot $CHROOT_FOLDER/$dist_mmdebstrap_build_sources_list_primary, does not exist, ok."
   fi
}

chroot_mount_files() {
   ## controversy of:
   ## /etc/resolv.conf /etc/hosts /etc/hostname

   ## When we are inside chroot, we need a functional /etc/resolv.conf,
   ## otherwise DNS lookups and subsequently apt-get and curl would be defunct.
   ## On the other hand, we do not want to copy /etc/resolv.conf from the
   ## build machine into chroot, to prevent leaking personal data into chroot.
   ## Therefore we use /etc/resolv.conf from the host inside chroot,
   ## without ever writing it. (Mounting)

   ## When we are inside chroot, we need correct /etc/hosts and /etc/hostname
   ## files, otherwise commands such as:
   ##     sudo -u root echo 'This is a test echo.'
   ## would show for example
   ##     sudo: unable to resolve host debian
   ## and take a long time to complete.

   ## We need two commands to remount an existing file as read only.
   ## Thanks to: https://lwn.net/Articles/281157/
   ## Remount as read only...
   ## Does not work on Debian Wheezy.
   ## Not required anymore. We are not starting any services, DHCP or
   ## networking inside the chroot, so the file should remain untouched.
   #mount -o remount,ro,noload "$CHROOT_FOLDER/etc/resolv.conf"

   sync

   local mount_base_file ok_if_match_file is_ok
   local host_system_file_full_path system_file_copy_full_path

   ## If derivative-maker version already inside chroot, don't delete, keep as is, ok.
   ## If host version inside chroot, delete, ok.
   ## Empty version exists inside chroot in any case, ok.
   ## If dist_chroot_mount_resolv_conf=0: don't mount; empty file.
   ## If dist_chroot_mount_resolv_conf=1 or unset: mount host version inside chroot.

   if [ ! -d "$binary_build_folder_dist/system-files-copy/etc" ]; then
      mkdir --parents "$binary_build_folder_dist/system-files-copy/etc"
   fi

   for mount_base_file in "etc/resolv.conf" "etc/hosts" "etc/hostname" ; do
      ## Backup exiting $mount_base_file, so we can later use it to safely mount it inside chroot.
      ## Safely meaning, even if the chroot overwrites it, the host version remains unchanged.
      host_system_file_full_path="/$mount_base_file"

      host_system_file_base_name="$(basename "$host_system_file_full_path")"
      system_file_copy_full_path="$binary_build_folder_dist/system-files-copy/etc/$host_system_file_base_name"

      if [ -f "$host_system_file_full_path" ]; then
         ## Not using.
         ## Otherwise /etc/apt/sources.list.backup leaks derivative-maker build sources inside VM.
         ## Otherwise host /etc/resolv.conf ends up inside VM.
         #$SUDO_TO_ROOT cp --no-clobber --preserve "$1" "$1.backup"

         $SUDO_TO_ROOT cp --preserve "$host_system_file_full_path" "$system_file_copy_full_path"
      else
         true "INFO: file '$host_system_file_full_path' does not exist, skipping copy, ok."
         if [ -f "$system_file_copy_full_path" ]; then
            true "INFO: Deleting stale version from previous run..."
            $SUDO_TO_ROOT rm --verbose "$system_file_copy_full_path"
         fi
         ## Create empty file to make sure it exists.
         $SUDO_TO_ROOT touch "$system_file_copy_full_path"
      fi
      $SUDO_TO_ROOT chown "$user_name:$user_name" "$system_file_copy_full_path"

      ## Delete inside chroot if host version leaked into it previously through
      ## another process such as perhaps mmdebstrap.
      if diff "$host_system_file_full_path" "$CHROOT_FOLDER/$mount_base_file" >/dev/null ; then
         true "INFO: Host '/$mount_base_file' matches chroot '$CHROOT_FOLDER/$mount_base_file'"

         is_ok=no
         for ok_if_match_file in \
            "$source_code_folder_dist/packages/whonix/whonix-gw-network-conf/etc/resolv.conf.whonix" \
            "$source_code_folder_dist/packages/whonix/whonix-ws-network-conf/etc/resolv.conf.whonix" \
            "$source_code_folder_dist/packages/whonix/whonix-base-files/etc/hosts.whonix" \
            "$source_code_folder_dist/packages/whonix/whonix-base-files/etc/hostname.whonix" \
            ; do
               if diff "$CHROOT_FOLDER/$mount_base_file" "$ok_if_match_file" >/dev/null ; then
                  true "INFO: But '$CHROOT_FOLDER/$mount_base_file' also matches '$ok_if_match_file', ok."
                  is_ok=yes
                  break
               fi
         done
         if [ "$is_ok" = "no" ]; then
            if [ "$dist_build_type_short" = "whonix" ]; then
               true "INFO: dist_build_type_short: $dist_build_type_short Therefore shredding '$CHROOT_FOLDER/$mount_base_file'."
               ## Delete leaked host $mount_base_file (such as perhaps by grml-debootstrap) inside image.
               $SUDO_TO_ROOT shred -u --zero --force --random-source=/dev/random "$CHROOT_FOLDER/$mount_base_file"
            else
               true "INFO: dist_build_type_short: $dist_build_type_short Therefore not touching '$CHROOT_FOLDER/$mount_base_file'."
            fi
         fi
      else
         true "INFO: Host '/$mount_base_file' does not match chroot '$CHROOT_FOLDER/$mount_base_file', ok"
      fi

      ## File needs to exist to be able to mount in next step.
      $SUDO_TO_ROOT touch "$CHROOT_FOLDER/$mount_base_file"

      if [ "$dist_chroot_mount_resolv_conf" = "0" ]; then
         true "${cyan}INFO $BASH_SOURCE: Not mounting /$mount_base_file and others inside chroot, because dist_chroot_mount_resolv_conf is 0.${reset}"
         continue
      fi

      $SUDO_TO_ROOT mount --bind "$binary_build_folder_dist/system-files-copy/$mount_base_file" "$CHROOT_FOLDER/$mount_base_file"
   done

   sync
}

main "$@"
