improve error handling
[grml-debootstrap.git] / grml-debootstrap
index 7e88c11..6ed8511 100755 (executable)
@@ -2,7 +2,7 @@
 # Filename:      grml-debootstrap
 # Purpose:       wrapper around debootstrap for installing plain Debian via Grml
 # Authors:       grml-team (grml.org), (c) Michael Prokop <mika@grml.org>
 # Filename:      grml-debootstrap
 # Purpose:       wrapper around debootstrap for installing plain Debian via Grml
 # Authors:       grml-team (grml.org), (c) Michael Prokop <mika@grml.org>
-# Bug-Reports:   see http://grml.org/bugs/
+# Bug-Reports:   see https://grml.org/bugs/
 # License:       This file is licensed under the GPL v2+
 ################################################################################
 
 # License:       This file is licensed under the GPL v2+
 ################################################################################
 
@@ -31,6 +31,7 @@ last bash command: $last_bash_command"
 }
 
 if [ "$REPORT_TRAP_ERR" = "yes" ] || [ "$FAIL_TRAP_ERR" = "yes" ]; then
 }
 
 if [ "$REPORT_TRAP_ERR" = "yes" ] || [ "$FAIL_TRAP_ERR" = "yes" ]; then
+   set -e
    set -E
    set -o pipefail
    trap "error_handler" ERR
    set -E
    set -o pipefail
    trap "error_handler" ERR
@@ -40,8 +41,8 @@ fi
 
 # variables {{{
 PN="$(basename "$0")"
 
 # variables {{{
 PN="$(basename "$0")"
-if [[ -d "$(dirname "$(which "$0")")"/.git ]]; then
-  VERSION="$(git describe | sed 's|^v||')"
+if [[ -d "$(dirname "$(command -v "$0")")"/.git ]]; then
+  VERSION="$(git --git-dir $(dirname "$(command -v "$0")")/.git describe | sed 's|^v||')"
 else
   VERSION="$(dpkg-query --show --showformat='${Version}' "$PN")"
 fi
 else
   VERSION="$(dpkg-query --show --showformat='${Version}' "$PN")"
 fi
@@ -58,11 +59,13 @@ MNTPOINT="/mnt/debootstrap.$$"
 [ -n "$DEFAULT_LOCALES" ] || DEFAULT_LOCALES='en_US.UTF-8'
 [ -n "$DISK_IDENTIFIER" ] || DISK_IDENTIFIER='26ada0c0-1165-4098-884d-aafd2220c2c6'
 [ -n "$EXTRAPACKAGES" ] || EXTRAPACKAGES='yes'
 [ -n "$DEFAULT_LOCALES" ] || DEFAULT_LOCALES='en_US.UTF-8'
 [ -n "$DISK_IDENTIFIER" ] || DISK_IDENTIFIER='26ada0c0-1165-4098-884d-aafd2220c2c6'
 [ -n "$EXTRAPACKAGES" ] || EXTRAPACKAGES='yes'
-[ -n "$FALLBACK_MIRROR" ] || FALLBACK_MIRROR='http://httpredir.debian.org/debian'
+[ -n "$FALLBACK_MIRROR" ] || FALLBACK_MIRROR='http://deb.debian.org/debian'
 [ -n "$FIXED_DISK_IDENTIFIERS" ] || FIXED_DISK_IDENTIFIERS="no"
 [ -n "$FORCE" ] || FORCE=''
 [ -n "$HOSTNAME" ] || HOSTNAME='grml'
 [ -n "$INITRD" ] || INITRD='yes'
 [ -n "$FIXED_DISK_IDENTIFIERS" ] || FIXED_DISK_IDENTIFIERS="no"
 [ -n "$FORCE" ] || FORCE=''
 [ -n "$HOSTNAME" ] || HOSTNAME='grml'
 [ -n "$INITRD" ] || INITRD='yes'
+[ -n "$INITRD_GENERATOR" ] || INITRD_GENERATOR='initramfs-tools'
+[ -n "$INITRD_GENERATOR_OPTS" ] || INITRD_GENERATOR_OPTS=''
 [ -n "$INSTALL_NOTES" ] || INSTALL_NOTES='/etc/debootstrap/install_notes'
 [ -n "$LOCALES" ] || LOCALES='yes'
 [ -n "$MIRROR" ] || MIRROR="$FALLBACK_MIRROR"
 [ -n "$INSTALL_NOTES" ] || INSTALL_NOTES='/etc/debootstrap/install_notes'
 [ -n "$LOCALES" ] || LOCALES='yes'
 [ -n "$MIRROR" ] || MIRROR="$FALLBACK_MIRROR"
@@ -72,7 +75,7 @@ MNTPOINT="/mnt/debootstrap.$$"
 [ -n "$POST_SCRIPTS" ] || POST_SCRIPTS='yes'
 [ -n "$PRE_SCRIPTS" ] || PRE_SCRIPTS='yes'
 [ -n "$RECONFIGURE" ] || RECONFIGURE='console-data'
 [ -n "$POST_SCRIPTS" ] || POST_SCRIPTS='yes'
 [ -n "$PRE_SCRIPTS" ] || PRE_SCRIPTS='yes'
 [ -n "$RECONFIGURE" ] || RECONFIGURE='console-data'
-[ -n "$RELEASE" ] || RELEASE='stretch'
+[ -n "$RELEASE" ] || RELEASE='bookworm'
 [ -n "$RM_APTCACHE" ] || RM_APTCACHE='yes'
 [ -n "$SCRIPTS" ] || SCRIPTS='no' # deprecated, replaced by POST_SCRIPTS
 [ -n "$SECURE" ] || SECURE='yes'
 [ -n "$RM_APTCACHE" ] || RM_APTCACHE='yes'
 [ -n "$SCRIPTS" ] || SCRIPTS='no' # deprecated, replaced by POST_SCRIPTS
 [ -n "$SECURE" ] || SECURE='yes'
@@ -80,6 +83,7 @@ MNTPOINT="/mnt/debootstrap.$$"
 [ -n "$TUNE2FS" ] || TUNE2FS='tune2fs -c0 -i0'
 [ -n "$UPGRADE_SYSTEM" ] || UPGRADE_SYSTEM='yes'
 [ -n "$VMSIZE" ] || VMSIZE="2G"
 [ -n "$TUNE2FS" ] || TUNE2FS='tune2fs -c0 -i0'
 [ -n "$UPGRADE_SYSTEM" ] || UPGRADE_SYSTEM='yes'
 [ -n "$VMSIZE" ] || VMSIZE="2G"
+[ -n "$GRUB_INSTALL" ] || GRUB_INSTALL='yes'
 
 # inside the chroot system locales might not be available, so use minimum:
 export LANG=C
 
 # inside the chroot system locales might not be available, so use minimum:
 export LANG=C
@@ -102,7 +106,7 @@ Bootstrap options:
   -m, --mirror <URL>     Mirror which should be used for apt-get/aptitude.
   -i, --iso <mnt>        Mountpoint where a Debian ISO is mounted to, for use
                          instead of fetching packages from a mirror.
   -m, --mirror <URL>     Mirror which should be used for apt-get/aptitude.
   -i, --iso <mnt>        Mountpoint where a Debian ISO is mounted to, for use
                          instead of fetching packages from a mirror.
-  -r, --release <name>   Release of new Debian system (default: stretch).
+  -r, --release <name>   Release of new Debian system (default: bullseye).
   -t, --target <target>  Target partition (/dev/...) or directory where the
                          system should be installed to.
   -p, --mntpoint <mnt>   Mountpoint used for mounting the target system,
   -t, --target <target>  Target partition (/dev/...) or directory where the
                          system should be installed to.
   -p, --mntpoint <mnt>   Mountpoint used for mounting the target system,
@@ -128,6 +132,7 @@ Options for Virtual Machine deployment:
                          Example: --vmfile --target /mnt/sda1/qemu.img
       --vmsize <size>    Use specified size for size of VM file (default: 2G).
                          Syntax as supported by qemu-img, like: --vmsize 3G
                          Example: --vmfile --target /mnt/sda1/qemu.img
       --vmsize <size>    Use specified size for size of VM file (default: 2G).
                          Syntax as supported by qemu-img, like: --vmsize 3G
+      --vmefi            Create an EFI boot partition for the VM.
 
 Configuration options:
 
 
 Configuration options:
 
@@ -150,10 +155,12 @@ Configuration options:
       --backportrepos      Enable Debian's backports repository (backports.debian.org).
       --keep_src_list      Do not overwrite user provided apt sources.list.
       --contrib            Enable 'contrib' in COMPONENTS (defaults to 'main' only).
       --backportrepos      Enable Debian's backports repository (backports.debian.org).
       --keep_src_list      Do not overwrite user provided apt sources.list.
       --contrib            Enable 'contrib' in COMPONENTS (defaults to 'main' only).
-      --non-free           Enable non-free in COMPONENTS (defaults to 'main' only).
+      --non-free           Enable non-free / non-free-firmware in COMPONENTS (defaults to 'main' only).
       --hostname <name>    Hostname of Debian system.
       --nopassword         Do not prompt for the root password.
       --password <pwd>     Use specified password as password for user root.
       --hostname <name>    Hostname of Debian system.
       --nopassword         Do not prompt for the root password.
       --password <pwd>     Use specified password as password for user root.
+      --sshcopyauth        Use ${HOME}/.ssh/authorized_keys to authorise root login on the target system.
+      --sshcopyid          Use locally available public keys to authorise root login on the target system.
       --bootappend <line>  Add specified appendline to kernel whilst booting.
       --chroot-scripts <d> Execute chroot scripts from specified directory.
       --pre-scripts <dir>  Execute scripts from specified directory (before chroot-scripts).
       --bootappend <line>  Add specified appendline to kernel whilst booting.
       --chroot-scripts <d> Execute chroot scripts from specified directory.
       --pre-scripts <dir>  Execute scripts from specified directory (before chroot-scripts).
@@ -168,7 +175,7 @@ Other options:
   -V, --version            Show summary of options and exit.
 
 Usage examples can be found in the grml-debootstrap manpage.
   -V, --version            Show summary of options and exit.
 
 Usage examples can be found in the grml-debootstrap manpage.
-Send bugreports to the grml-team: bugs (at) grml.org || http://grml.org/bugs/
+Send bugreports to the grml-team: bugs (at) grml.org || https://grml.org/bugs/
 "
 }
 
 "
 }
 
@@ -180,13 +187,21 @@ fi
 # }}}
 
 # early helper functions {{{
 # }}}
 
 # early helper functions {{{
-GOOD='\e[32;01m'
-BAD='\e[31;01m'
-WARN='\e[33;01m'
-NORMAL='\e[0m'
+# skip colors when running within a dumb terminal
+if [ "${TERM}" = "dumb" ] ; then
+  GOOD=
+  BAD=
+  WARN=
+  NORMAL=
+else
+  GOOD='\e[32;01m'
+  BAD='\e[31;01m'
+  WARN='\e[33;01m'
+  NORMAL='\e[0m'
+fi
 
 einfo() {
 
 einfo() {
-  einfon "$1\n"
+  einfon "$1\\n"
   return 0
 }
 
   return 0
 }
 
@@ -198,13 +213,13 @@ einfon() {
 }
 
 ewarn() {
 }
 
 ewarn() {
-  printf " %s*%s $*\n" "${WARN}" "${NORMAL}"
+  printf " %s*%s $*\\n" "${WARN}" "${NORMAL}"
   return 0
 }
 
 eerror() {
   [ "${RC_ENDCOL}" != "yes" ] && [ "${LAST_E_CMD}" = "ebegin" ] && echo
   return 0
 }
 
 eerror() {
   [ "${RC_ENDCOL}" != "yes" ] && [ "${LAST_E_CMD}" = "ebegin" ] && echo
-  printf " %s*%s $*\n" "${BAD}" "${NORMAL}" >&2
+  printf " %s*%s $*\\n" "${BAD}" "${NORMAL}" >&2
   LAST_E_CMD=eerror
   return 0
 }
   LAST_E_CMD=eerror
   return 0
 }
@@ -213,7 +228,7 @@ eend() {
   local retval="${1:-0}"
   shift
   if [ "$retval" -gt 0 ]; then
   local retval="${1:-0}"
   shift
   if [ "$retval" -gt 0 ]; then
-    printf " %s-> Failed (rc=%s)%s\n" "${BAD}" "${retval}" "${NORMAL}"
+    printf " %s-> Failed (rc=%s)%s\\n" "${BAD}" "${retval}" "${NORMAL}"
   fi
   return "$retval"
 }
   fi
   return "$retval"
 }
@@ -227,7 +242,7 @@ check4root(){
 check4progs(){
   local RC=''
   for arg in "$@" ; do
 check4progs(){
   local RC=''
   for arg in "$@" ; do
-    which "$arg" >/dev/null 2>&1 || RC="$arg"
+    command -v "$arg" >/dev/null 2>&1 || RC="$arg"
   done
   if [ -n "$RC" ] ; then
      echo "$RC not installed"
   done
   if [ -n "$RC" ] ; then
      echo "$RC not installed"
@@ -265,7 +280,7 @@ cleanup() {
 
       # ugly, but make sure we really don't leave anything (/proc /proc and
       # /dev /dev are intended, trying to work around timing issues, see #657023)
 
       # ugly, but make sure we really don't leave anything (/proc /proc and
       # /dev /dev are intended, trying to work around timing issues, see #657023)
-      for ARG in /sys /proc /proc /dev/pts /dev/pts /dev /dev ; do
+      for ARG in /run/udev /sys /proc /proc /dev/pts /dev/pts /dev /dev ; do
         [ -x "$MNTPOINT"/bin/umount ] && chroot "$MNTPOINT" umount $ARG >/dev/null 2>&1
         umount "$MNTPOINT"/$ARG >/dev/null 2>&1
       done
         [ -x "$MNTPOINT"/bin/umount ] && chroot "$MNTPOINT" umount $ARG >/dev/null 2>&1
         umount "$MNTPOINT"/$ARG >/dev/null 2>&1
       done
@@ -298,7 +313,13 @@ cleanup() {
 
   if [ -n "${ORIG_TARGET}" ] ; then
     einfo "Removing loopback mount of file ${ORIG_TARGET}."
 
   if [ -n "${ORIG_TARGET}" ] ; then
     einfo "Removing loopback mount of file ${ORIG_TARGET}."
-    kpartx -d "${ORIG_TARGET}" ; eend $?
+    kpartx -d "${ORIG_TARGET}"
+    # Workaround for a bug in kpartx which doesn't clean up properly,
+    # see Debian Bug #891077 and Github-PR grml/grml-debootstrap#112
+    if dmsetup ls | grep -q "^${LOOP_PART} "; then
+      kpartx -d "/dev/${LOOP_DISK}" >/dev/null
+    fi
+    eend $?
   fi
 }
 
   fi
 }
 
@@ -327,10 +348,6 @@ stage() {
 }
 # }}}
 
 }
 # }}}
 
-# make sure we have what we need {{{
-check4progs debootstrap || bailout 1
-# }}}
-
 # source main configuration file {{{
 if [ -r /etc/debootstrap/config ] ; then
   # shellcheck disable=SC1091
 # source main configuration file {{{
 if [ -r /etc/debootstrap/config ] ; then
   # shellcheck disable=SC1091
@@ -339,7 +356,7 @@ fi
 # }}}
 
 # cmdline handling {{{
 # }}}
 
 # cmdline handling {{{
-CMDLINE_OPTS=mirror:,iso:,release:,target:,mntpoint:,debopt:,defaultinterfaces,interactive,nodebootstrap,nointerfaces,nokernel,nopackages,filesystem:,config:,confdir:,packages:,chroot-scripts:,scripts:,post-scripts:,pre-scripts:,debconf:,vm,vmfile,vmsize:,keep_src_list,hostname:,password:,nopassword,grmlrepos,backportrepos,bootappend:,grub:,efi:,arch:,insecure,verbose,help,version,force,debug,contrib,non-free,remove-configs
+CMDLINE_OPTS=mirror:,iso:,release:,target:,mntpoint:,debopt:,defaultinterfaces,interactive,nodebootstrap,nointerfaces,nokernel,nopackages,filesystem:,config:,confdir:,packages:,chroot-scripts:,scripts:,post-scripts:,pre-scripts:,debconf:,vm,vmfile,vmsize:,vmefi,keep_src_list,hostname:,password:,nopassword,grmlrepos,backportrepos,bootappend:,grub:,efi:,arch:,insecure,verbose,help,version,force,debug,contrib,non-free,remove-configs,sshcopyid,sshcopyauth
 
 _opt_temp=$(getopt --name grml-debootstrap -o +m:i:r:t:p:c:d:vhV --long \
   $CMDLINE_OPTS -- "$@")
 
 _opt_temp=$(getopt --name grml-debootstrap -o +m:i:r:t:p:c:d:vhV --long \
   $CMDLINE_OPTS -- "$@")
@@ -374,6 +391,9 @@ while :; do
   --vmsize)            # size of Virtual machine file
     shift; _opt_vmsize="$1"
     ;;
   --vmsize)            # size of Virtual machine file
     shift; _opt_vmsize="$1"
     ;;
+  --vmefi)            # Create an EFI boot partition for the VM
+    _opt_vmefi="T"
+    ;;
   --mntpoint|-p)       # Mountpoint used for mounting the target system
     shift; _opt_mntpoint="$1"
     ;;
   --mntpoint|-p)       # Mountpoint used for mounting the target system
     shift; _opt_mntpoint="$1"
     ;;
@@ -381,7 +401,7 @@ while :; do
     shift; _opt_debopt="$1"
     ;;
   --filesystem)        # Filesystem that should be used
     shift; _opt_debopt="$1"
     ;;
   --filesystem)        # Filesystem that should be used
-    shift; _opt_filesystem="$1"
+    shift; _opt_filesystem="$1" ; FILESYSTEM="${_opt_filesystem}"
     ;;
   --interactive)       # Use interactive mode (frontend)
     _opt_interactive=T
     ;;
   --interactive)       # Use interactive mode (frontend)
     _opt_interactive=T
@@ -453,6 +473,12 @@ while :; do
   --nopassword)        # Skip password dialog
     _opt_nopassword=T
     ;;
   --nopassword)        # Skip password dialog
     _opt_nopassword=T
     ;;
+  --sshcopyid)         # Use locally available public keys to authorise root login on the target system
+    _opt_sshcopyid=T
+    ;;
+  --sshcopyauth)       # Use .ssh/authorized_keys to authorise root login on the target system
+    _opt_sshcopyauth=T
+    ;;
   --grmlrepos)         # Enable Grml repository
     _opt_grmlrepos=T
     ;;
   --grmlrepos)         # Enable Grml repository
     _opt_grmlrepos=T
     ;;
@@ -480,8 +506,11 @@ while :; do
 
   # == Other options
   --verbose|-v)        # Increase verbosity
 
   # == Other options
   --verbose|-v)        # Increase verbosity
-    if [ "$_opt_verbose" ]; then _opt_verbose=`expr $_opt_verbose + 1`
-    else _opt_verbose=1; fi
+    if [ "$_opt_verbose" ]; then
+      _opt_verbose=$( _opt_verbose + 1 )
+    else
+      _opt_verbose=1
+    fi
     ;;
   --debug)             # Execute in debug mode
     _opt_debug=T
     ;;
   --debug)             # Execute in debug mode
     _opt_debug=T
@@ -530,6 +559,7 @@ done
 [ "$_opt_vm" ]                  && VIRTUAL=1
 [ "$_opt_vmfile" ]              && VMFILE=1 && VIRTUAL=1
 [ "$_opt_vmsize" ]              && VMSIZE=$_opt_vmsize
 [ "$_opt_vm" ]                  && VIRTUAL=1
 [ "$_opt_vmfile" ]              && VMFILE=1 && VIRTUAL=1
 [ "$_opt_vmsize" ]              && VMSIZE=$_opt_vmsize
+[ "$_opt_vmefi" ]               && VMEFI=1
 [ "$_opt_mntpoint" ]            && MNTPOINT=$_opt_mntpoint
 [ "$_opt_debopt" ]              && DEBOOTSTRAP_OPT=$_opt_debopt
 [ "$_opt_interactive" ]         && INTERACTIVE=1
 [ "$_opt_mntpoint" ]            && MNTPOINT=$_opt_mntpoint
 [ "$_opt_debopt" ]              && DEBOOTSTRAP_OPT=$_opt_debopt
 [ "$_opt_interactive" ]         && INTERACTIVE=1
@@ -550,6 +580,8 @@ done
 [ "$_opt_defaultinterfaces" ]   && USE_DEFAULT_INTERFACES="true"
 [ "$_opt_nointerfaces" ]        && NOINTERFACES="true"
 [ "$_opt_nokernel" ]            && NOKERNEL="true"
 [ "$_opt_defaultinterfaces" ]   && USE_DEFAULT_INTERFACES="true"
 [ "$_opt_nointerfaces" ]        && NOINTERFACES="true"
 [ "$_opt_nokernel" ]            && NOKERNEL="true"
+[ "$_opt_sshcopyid" ]           && SSHCOPYID="true"
+[ "$_opt_sshcopyauth" ]         && SSHCOPYAUTH="true"
 [ "$_opt_bootappend" ]          && BOOT_APPEND=$_opt_bootappend
 [ "$_opt_grub" ]                && GRUB=$_opt_grub
 [ "$_opt_efi" ]                 && EFI=$_opt_efi
 [ "$_opt_bootappend" ]          && BOOT_APPEND=$_opt_bootappend
 [ "$_opt_grub" ]                && GRUB=$_opt_grub
 [ "$_opt_efi" ]                 && EFI=$_opt_efi
@@ -563,7 +595,15 @@ done
 # make sure main is always included
 [ -z "$COMPONENTS" ]            && COMPONENTS="main"
 [ "$_opt_contrib" ]             && COMPONENTS="$COMPONENTS contrib"
 # make sure main is always included
 [ -z "$COMPONENTS" ]            && COMPONENTS="main"
 [ "$_opt_contrib" ]             && COMPONENTS="$COMPONENTS contrib"
-[ "$_opt_non_free" ]            && COMPONENTS="$COMPONENTS non-free"
+
+case "${RELEASE}" in
+  lenny|squeeze|wheezy|jessie|stretch|buster|bullseye)
+    [ "$_opt_non_free" ] && COMPONENTS="$COMPONENTS non-free"
+    ;;
+  *)
+    [ "$_opt_non_free" ] && COMPONENTS="$COMPONENTS non-free-firmware non-free"
+    ;;
+esac
 
 # command line option checks
 if [ "$_opt_scripts_set" ] ; then
 
 # command line option checks
 if [ "$_opt_scripts_set" ] ; then
@@ -578,6 +618,18 @@ if [ "$_opt_grub" ] && [ "$_opt_vmfile" ] ; then
   bailout 1
 fi
 
   bailout 1
 fi
 
+if [ "${_opt_sshcopyid}" ] && [ "${_opt_sshcopyauth}" ] ; then
+  eerror "The --sshcopyid option is incompatible with --sshcopyauth, please drop either of them from your command line."
+  eend 1
+  bailout 1
+fi
+
+if [ -n "$ISO" ] && [[ "$DEBOOTSTRAP" =~ mmdebstrap$ ]] ; then
+  eerror "The ISO option is incompatible with usage of mmdebstrap for bootstrapping."
+  eerror "Either drop the --iso ... option or use plain debootstrap instead."
+  eend 1
+  bailout 1
+fi
 
 if [ "$DEBUG" = "true" ] ; then
   set -x
 
 if [ "$DEBUG" = "true" ] ; then
   set -x
@@ -591,7 +643,7 @@ fi
 
 [ "$_opt_version" ] && {
   einfo "$PN - version $VERSION"
 
 [ "$_opt_version" ] && {
   einfo "$PN - version $VERSION"
-  einfo "Send bug reports to bugs@grml.org or http://grml.org/bugs/"
+  einfo "Report bugs via https://github.com/grml/grml-debootstrap/ or https://grml.org/bugs/"
   eend 0
   exit 0
 }
   eend 0
   exit 0
 }
@@ -605,6 +657,8 @@ fi
 # }}}
 
 # make sure we have what we need {{{
 # }}}
 
 # make sure we have what we need {{{
+check4progs "${DEBOOTSTRAP}" || bailout 1
+
 if [ -n "$VIRTUAL" ] ; then
   check4progs kpartx parted qemu-img || bailout 1
 fi
 if [ -n "$VIRTUAL" ] ; then
   check4progs kpartx parted qemu-img || bailout 1
 fi
@@ -749,15 +803,13 @@ prompt_for_bootmanager()
 # ask for Debian release {{{
 prompt_for_release()
 {
 # ask for Debian release {{{
 prompt_for_release()
 {
-  [ -n "$RELEASE" ] && DEFAULT_RELEASE="$RELEASE" || DEFAULT_RELEASE='stretch'
+  [ -n "$RELEASE" ] && DEFAULT_RELEASE="$RELEASE" || DEFAULT_RELEASE='bullseye'
   RELEASE="$(dialog --stdout --title "${PN}" --default-item $DEFAULT_RELEASE --menu \
             "Please enter the Debian release you would like to use for installation:" \
   RELEASE="$(dialog --stdout --title "${PN}" --default-item $DEFAULT_RELEASE --menu \
             "Please enter the Debian release you would like to use for installation:" \
-            0 50 5 \
-            lenny    Debian/5.0 \
-            squeeze  Debian/6.0 \
-            wheezy   Debian/7.0 \
-            jessie   Debian/8.0 \
-            stretch  Debian/9.0 \
+            0 50 8 \
+            buster   Debian/10 \
+            bullseye Debian/11 \
+            bookworm Debian/12 \
             sid      Debian/unstable)"
   [ $? -eq 0 ] || bailout
 }
             sid      Debian/unstable)"
   [ $? -eq 0 ] || bailout
 }
@@ -815,7 +867,7 @@ prompt_for_mirror()
   [ $? -eq 0 ] || bailout
 
   if [ "$CHOOSE_MIRROR" = 'net' ] ; then
   [ $? -eq 0 ] || bailout
 
   if [ "$CHOOSE_MIRROR" = 'net' ] ; then
-     [ -n "$MIRROR" ] || MIRROR='http://httpredir.debian.org/debian'
+     [ -n "$MIRROR" ] || MIRROR='http://deb.debian.org/debian'
      MIRROR="$(dialog --stdout --title "${PN}" --inputbox \
                "Please enter Debian mirror you would like to use for installing packages." \
                0 0 $MIRROR)"
      MIRROR="$(dialog --stdout --title "${PN}" --inputbox \
                "Please enter Debian mirror you would like to use for installing packages." \
                0 0 $MIRROR)"
@@ -925,6 +977,7 @@ Do you want to stop now?" 0 0 2>/dev/null
 }
 # }}}
 
 }
 # }}}
 
+# format efi partition {{{
 format_efi_partition() {
   if [ -z "$EFI" ] ; then
     return 0
 format_efi_partition() {
   if [ -z "$EFI" ] ; then
     return 0
@@ -939,7 +992,7 @@ format_efi_partition() {
     einfo "EFI partition $EFI seems to have a FAT filesystem, not modifying." ; eend 0
   else
     einfo "EFI partition $EFI doesn't seem to be formatted, creating filesystem."
     einfo "EFI partition $EFI seems to have a FAT filesystem, not modifying." ; eend 0
   else
     einfo "EFI partition $EFI doesn't seem to be formatted, creating filesystem."
-    mkfs.fat -F32 -n "EFI System Partition" "$EFI"
+    mkfs.fat -F32 -n "EFI" "$EFI"
     RC=$?
     if [ $RC -eq 0 ] ; then
       eend 0
     RC=$?
     if [ $RC -eq 0 ] ; then
       eend 0
@@ -950,17 +1003,27 @@ format_efi_partition() {
     fi
   fi
 }
     fi
   fi
 }
+# }}}
 
 # check for EFI support or try to enable it {{{
 efi_support() {
 
 # check for EFI support or try to enable it {{{
 efi_support() {
-  if lsmod | grep -q efivars ; then
+  local efivars_loaded=false
+  # this is for kernels versions before v3.10, which didn't provide efivarfs yet
+  if modprobe efivars &>/dev/null ; then
+    efivars_loaded=true
+  fi
+  # kernel versions v3.10 and newer usually provide efivarfs
+  if modprobe efivarfs &>/dev/null ; then
+    efivars_loaded=true
+  fi
+
+  if [ -d /sys/firmware/efi ] ; then
     einfo "EFI support detected." ; eend 0
     return 0
   fi
 
     einfo "EFI support detected." ; eend 0
     return 0
   fi
 
-  if modprobe efivars &>/dev/null ; then
-    einfo "EFI support enabled now." ; eend 0
-    return 0
+  if ! [ -d /sys/firmware/efi ] && [ "${efivars_loaded:-}" = "true" ] ; then
+    einfo "EFI support detected, but system seems to be running in BIOS mode."
   fi
 
   return 1
   fi
 
   return 1
@@ -1028,6 +1091,7 @@ else # if not running automatic installation display configuration and prompt fo
 
    if [ -n "$VIRTUAL" ] ; then
       echo "   Install grub:    yes"
 
    if [ -n "$VIRTUAL" ] ; then
       echo "   Install grub:    yes"
+      [ -n "$VMEFI" ]   && echo "   Install efi:     yes"   || echo "   Install efi:     no"
    else
      [ -n "$GRUB" ]     && echo "   Install grub:    $GRUB" || echo "   Install grub:    no"
      [ -n "$EFI" ]      && echo "   Install efi:     $EFI"  || echo "   Install efi:     no"
    else
      [ -n "$GRUB" ]     && echo "   Install grub:    $GRUB" || echo "   Install grub:    no"
      [ -n "$EFI" ]      && echo "   Install efi:     $EFI"  || echo "   Install efi:     no"
@@ -1109,6 +1173,12 @@ else
    ARCHCMD="--arch $ARCH"
    ARCHINFO=" (${ARCH})"
 fi
    ARCHCMD="--arch $ARCH"
    ARCHINFO=" (${ARCH})"
 fi
+
+if [ -z "${ARCH:-}" ] ; then
+  eerror 'Architecture neither set (environment variable ARCH), nor could be automatically identified (using dpkg).'
+  eerror 'Consider setting the --arch ... option.' ; eend 1
+  bailout 1
+fi
 # }}}
 
 # It is not possible to build amd64 on i686. {{{
 # }}}
 
 # It is not possible to build amd64 on i686. {{{
@@ -1124,7 +1194,7 @@ fi
 # Support for generic release codenames is unavailable. {{{
 if [ "$RELEASE" = "stable" ] || [ "$RELEASE" = "testing" ] ; then
    eerror "Generic release codenames (stable, testing) are unsupported. \
 # Support for generic release codenames is unavailable. {{{
 if [ "$RELEASE" = "stable" ] || [ "$RELEASE" = "testing" ] ; then
    eerror "Generic release codenames (stable, testing) are unsupported. \
-Please use specific codenames such as lenny, squeeze, wheezy, jessie or stretch." ; eend 1
+Please use specific codenames such as bullseye or bookworm." ; eend 1
    bailout 1
 fi
 # }}}
    bailout 1
 fi
 # }}}
@@ -1175,6 +1245,12 @@ set_target_directory(){
 if [ -b "$TARGET" ] || [ -n "$VIRTUAL" ] ; then
     PARTITION=1
 else
 if [ -b "$TARGET" ] || [ -n "$VIRTUAL" ] ; then
     PARTITION=1
 else
+    # $TARGET was not detected as block device, but we do not want to create target directory in /dev/
+    if [[ $TARGET == "/dev/"* ]]; then
+      eerror "Error: Will not create target directory $TARGET in /dev."
+      eerror "  Please check the partition(s) of the blockdevice."; eend 1
+      bailout 1
+    fi
     set_target_directory
 fi
 # }}}
     set_target_directory
 fi
 # }}}
@@ -1242,6 +1318,24 @@ mkfs() {
     esac
   fi
 
     esac
   fi
 
+  # starting with e2fsprogs v1.47.0 mkfs.ext4 enables the metadata_csum_seed feature
+  # by default, which requires Linux kernel >=4.4, e2fsprogs >=1.43, according GRUB etc.
+  # Disable this feature for Debian releases older than bookworm
+  if [ -n "$MKFS" ] && [ "$MKFS" = "mkfs.ext4" ] ; then
+    case "$RELEASE" in
+      lenny|squeeze|wheezy|jessie|stretch|buster|bullseye)
+        local e2fsprogs_version
+        # assume a more recent version if we can't identify the version via dpkg-query
+        e2fsprogs_version="$(dpkg-query --show --showformat='${Version}' e2fsprogs 2>/dev/null || echo 1.47)"
+        if [ -n "$e2fsprogs_version" ] && dpkg --compare-versions "$e2fsprogs_version" ge '1.43' ; then
+          einfo "Disabling metadata_csum_seed feature for $MKFS as $RELEASE doesn't support it."
+          MKFS_OPTS="$MKFS_OPTS -O ^metadata_csum_seed"
+          eend 0
+        fi
+        ;;
+    esac
+  fi
+
   if [ -n "$MKFS" ] ; then
     einfo "Running $MKFS $MKFS_OPTS on $TARGET"
     # shellcheck disable=SC2086
   if [ -n "$MKFS" ] ; then
     einfo "Running $MKFS $MKFS_OPTS on $TARGET"
     # shellcheck disable=SC2086
@@ -1259,6 +1353,12 @@ mkfs() {
       fi
     fi
 
       fi
     fi
 
+    if [ -n "$VIRTUAL" ] && [ -n "$EFI_TARGET" ]; then
+      einfo "Creating FAT filesystem on $EFI_TARGET"
+      mkfs.fat -F32 -n "EFI" "$EFI_TARGET"
+      eend $?
+    fi
+
     # make sure /dev/disk/by-uuid/... is up2date, otherwise grub
     # will fail to detect the uuid in the chroot
     if [ -n "$VIRTUAL" ] ; then
     # make sure /dev/disk/by-uuid/... is up2date, otherwise grub
     # will fail to detect the uuid in the chroot
     if [ -n "$VIRTUAL" ] ; then
@@ -1284,6 +1384,7 @@ mkfs() {
 }
 # }}}
 
 }
 # }}}
 
+# retrieve ID_FS_UUID {{{
 identify_target_uuid() {
   local device="$1"
 
 identify_target_uuid() {
   local device="$1"
 
@@ -1299,7 +1400,9 @@ identify_target_uuid() {
     return 1
   fi
 }
     return 1
   fi
 }
+# }}}
 
 
+# identify TARGET_UUID {{{
 mountpoint_to_blockdevice() {
   TARGET_UUID=''
 
 mountpoint_to_blockdevice() {
   TARGET_UUID=''
 
@@ -1324,6 +1427,7 @@ mountpoint_to_blockdevice() {
     fi
   done
 }
     fi
   done
 }
+# }}}
 
 # modify filesystem settings {{{
 tunefs() {
 
 # modify filesystem settings {{{
 tunefs() {
@@ -1378,40 +1482,46 @@ prepare_vm() {
      bailout 1
   fi
 
      bailout 1
   fi
 
-  ORIG_TARGET="$TARGET" # store for later reuse
-
-  if [ -n "$VMFILE" ]; then
-    qemu-img create -f raw "${TARGET}" "${VMSIZE}"
-  fi
-  parted -s "${TARGET}" 'mklabel msdos'
-  if [ "$FIXED_DISK_IDENTIFIERS" = "yes" ] ; then
-    einfo "Adjusting disk signature to a fixed (non-random) value"
-    MBRTMPFILE=$(mktemp)
-    dd if="${TARGET}" of="${MBRTMPFILE}" bs=512 count=1
-    echo -en "\x41\x41\x41\x41" | dd of="${MBRTMPFILE}" conv=notrunc seek=440 bs=1
-    dd if="${MBRTMPFILE}" of="${TARGET}" conv=notrunc
-    eend $?
+  # make sure loop module is present and a usable loop device exists
+  modprobe -q loop
+  if ! losetup -f >/dev/null 2>&1; then
+    eerror "Error finding usable loop device" ; eend 1
+    bailout 1
   fi
   fi
-  parted -s "${TARGET}" 'mkpart primary ext4 2M -1'
-  parted -s "${TARGET}" 'set 1 boot on'
 
   # if dm-mod isn't available then kpartx will fail with
   # "Is device-mapper driver missing from kernel? [...]"
 
   # if dm-mod isn't available then kpartx will fail with
   # "Is device-mapper driver missing from kernel? [...]"
-  if ! kpartx -av "$TARGET" >/dev/null 2>&1 || ! grep -q 'device-mapper' /proc/misc >/dev/null 2>&1 ; then
-    einfo "Device-mapper not ready yet, trying to load dm-mod module."
-    modprobe dm-mod ; eend $?
+  modprobe -q dm-mod
+  if ! grep -q 'device-mapper' /proc/misc >/dev/null 2>&1 ; then
+    eerror "Device-mapper support missing in kernel." ; eend 1
+    bailout 1
   fi
 
   fi
 
-  # make sure loop module is present
-  if ! losetup -f >/dev/null 2>&1; then
-    einfo "Can not find a usable loop device, retrying after loading loop module."
-    modprobe loop
-    if losetup -f >/dev/null 2>&1; then
-      einfo "Found a usable loop device now, continuing."
-    else
-      eerror "Error finding usable loop device" ; eend 1
-      bailout 1
+  ORIG_TARGET="$TARGET" # store for later reuse
+
+  if [ -n "$VMFILE" ]; then
+    qemu-img create -f raw "${TARGET}" "${VMSIZE}"
+  fi
+  if [ -n "$VMEFI" ]; then
+    parted -s "${TARGET}" 'mklabel gpt'
+    parted -s "${TARGET}" 'mkpart ESP fat32 1MiB 101MiB'
+    parted -s "${TARGET}" 'set 1 boot on'
+    parted -s "${TARGET}" 'mkpart bios_grub 101MiB 102MiB'
+    parted -s "${TARGET}" 'set 2 bios_grub on'
+    parted -s "${TARGET}" 'mkpart primary ext4 102MiB 100%'
+
+  else
+    parted -s "${TARGET}" 'mklabel msdos'
+    if [ "$FIXED_DISK_IDENTIFIERS" = "yes" ] ; then
+      einfo "Adjusting disk signature to a fixed (non-random) value"
+      MBRTMPFILE=$(mktemp)
+      dd if="${TARGET}" of="${MBRTMPFILE}" bs=512 count=1
+      echo -en "\\x41\\x41\\x41\\x41" | dd of="${MBRTMPFILE}" conv=notrunc seek=440 bs=1
+      dd if="${MBRTMPFILE}" of="${TARGET}" conv=notrunc
+      eend $?
     fi
     fi
+    parted -s "${TARGET}" 'mkpart primary ext4 4MiB 100%'
+    parted -s "${TARGET}" 'set 1 boot on'
   fi
 
   DEVINFO=$(kpartx -asv "$TARGET") # e.g. 'add map loop0p1 (254:5): 0 20477 linear 7:0 3'
   fi
 
   DEVINFO=$(kpartx -asv "$TARGET") # e.g. 'add map loop0p1 (254:5): 0 20477 linear 7:0 3'
@@ -1423,6 +1533,11 @@ prepare_vm() {
   # hopefully this always works as expected
   LOOP_PART="${DEVINFO##add map }" # 'loop0p1 (254:5): 0 20477 linear 7:0 3'
   LOOP_PART="${LOOP_PART// */}"    # 'loop0p1'
   # hopefully this always works as expected
   LOOP_PART="${DEVINFO##add map }" # 'loop0p1 (254:5): 0 20477 linear 7:0 3'
   LOOP_PART="${LOOP_PART// */}"    # 'loop0p1'
+  if [ -n "$VMEFI" ]; then
+    export EFI_TARGET="/dev/mapper/$LOOP_PART" # '/dev/mapper/loop0p1'
+    LOOP_PART="${LOOP_PART%p1}p3"
+  fi
+  LOOP_DISK="${LOOP_PART%p*}"      # 'loop0'
   export TARGET="/dev/mapper/$LOOP_PART" # '/dev/mapper/loop1p1'
 
   if [ -z "$TARGET" ] ; then
   export TARGET="/dev/mapper/$LOOP_PART" # '/dev/mapper/loop1p1'
 
   if [ -z "$TARGET" ] ; then
@@ -1432,11 +1547,15 @@ prepare_vm() {
 }
 # }}}
 
 }
 # }}}
 
-# make VM image bootable and unmount it {{{
-finalize_vm() {
+# make VM image bootable {{{
+grub_install() {
   if [ -z "${VIRTUAL}" ] ; then
      return 0
   fi
   if [ -z "${VIRTUAL}" ] ; then
      return 0
   fi
+  if [ "${GRUB_INSTALL}" != "yes" ] ; then
+    einfo "Not installing GRUB as requested via \$GRUB_INSTALL=$GRUB_INSTALL"
+    return 0
+  fi
 
   if ! mount "${TARGET}" "${MNTPOINT}" ; then
     eerror "Error: Mounting ${TARGET} failed, can not continue." ; eend 1
 
   if ! mount "${TARGET}" "${MNTPOINT}" ; then
     eerror "Error: Mounting ${TARGET} failed, can not continue." ; eend 1
@@ -1450,13 +1569,13 @@ finalize_vm() {
 
 # Has chroot-script installed GRUB to MBR using grub-install (successfully), already?
 # chroot-script skips installation for unset ${GRUB}
 
 # Has chroot-script installed GRUB to MBR using grub-install (successfully), already?
 # chroot-script skips installation for unset ${GRUB}
-if [[ -z "${GRUB}" ]] || ! dd if="${GRUB}" bs=512 count=1 2>/dev/null | cat -v | fgrep -q GRUB; then
+if [[ -z "${GRUB}" ]] || ! dd if="${GRUB}" bs=512 count=1 2>/dev/null | cat -v | grep -Fq GRUB; then
   einfo "Installing Grub as bootloader."
 
   if ! chroot "${MNTPOINT}" dpkg --list grub-pc 2>/dev/null | grep -q '^ii' ; then
     echo "Notice: grub-pc package not present yet, installing it therefore."
     # shellcheck disable=SC2086
   einfo "Installing Grub as bootloader."
 
   if ! chroot "${MNTPOINT}" dpkg --list grub-pc 2>/dev/null | grep -q '^ii' ; then
     echo "Notice: grub-pc package not present yet, installing it therefore."
     # shellcheck disable=SC2086
-    DEBIAN_FRONTEND=$DEBIAN_FRONTEND chroot "$MNTPOINT" apt-get -y install $DPKG_OPTIONS grub-pc
+    DEBIAN_FRONTEND=$DEBIAN_FRONTEND chroot "$MNTPOINT" apt-get -y --no-install-recommends install $DPKG_OPTIONS grub-pc
   fi
 
   mkdir -p "${MNTPOINT}/boot/grub"
   fi
 
   mkdir -p "${MNTPOINT}/boot/grub"
@@ -1473,16 +1592,72 @@ if [[ -z "${GRUB}" ]] || ! dd if="${GRUB}" bs=512 count=1 2>/dev/null | cat -v |
       cp -a "${MNTPOINT}"/usr/lib/grub/i386-pc "${MNTPOINT}/boot/grub/"
       ;;
   esac
       cp -a "${MNTPOINT}"/usr/lib/grub/i386-pc "${MNTPOINT}/boot/grub/"
       ;;
   esac
-  dd if="${MNTPOINT}/usr/lib/grub/i386-pc/boot.img" of="${ORIG_TARGET}" conv=notrunc bs=440 count=1
-  chroot "${MNTPOINT}" grub-mkimage -O i386-pc -p "(hd0,msdos1)/boot/grub" -o /tmp/core.img biosdisk part_msdos ext2
-  dd if="${MNTPOINT}/tmp/core.img" of="${ORIG_TARGET}" conv=notrunc seek=1
-  rm -f "${MNTPOINT}/tmp/core.img"
+
+  if [ -n "$VMEFI" ]; then
+
+    mkdir -p "${MNTPOINT}"/boot/efi
+    mount -t vfat "${EFI_TARGET}" "${MNTPOINT}"/boot/efi
+
+    if ! chroot "${MNTPOINT}" dpkg --list shim-signed 2>/dev/null | grep -q '^ii' ; then
+      echo "Notice: shim-signed package not present yet, installing it therefore."
+      # shellcheck disable=SC2086
+      DEBIAN_FRONTEND=$DEBIAN_FRONTEND chroot "$MNTPOINT" apt-get -y --no-install-recommends install $DPKG_OPTIONS shim-signed
+    fi
+
+    if [ "$(dpkg --print-architecture)" = "arm64" ]; then
+      if ! chroot "${MNTPOINT}" dpkg --list grub-efi-arm64-signed 2>/dev/null | grep -q '^ii' ; then
+        echo "Notice: grub-efi-arm64-signed package not present yet, installing it therefore."
+        # shellcheck disable=SC2086
+        DEBIAN_FRONTEND=$DEBIAN_FRONTEND chroot "$MNTPOINT" apt-get -y --no-install-recommends install $DPKG_OPTIONS grub-efi-arm64-bin grub-efi-arm64-signed
+      fi
+      chroot "$MNTPOINT" grub-install --target=arm64-efi --efi-directory=/boot/efi --uefi-secure-boot --removable "/dev/$LOOP_DISK"
+    elif [ "$(dpkg --print-architecture)" = "i386" ]; then
+      if ! chroot "${MNTPOINT}" dpkg --list grub-efi-ia32-signed 2>/dev/null | grep -q '^ii' ; then
+        echo "Notice: grub-efi-ia32-signed package not present yet, installing it therefore."
+        # shellcheck disable=SC2086
+        DEBIAN_FRONTEND=$DEBIAN_FRONTEND chroot "$MNTPOINT" apt-get -y --no-install-recommends install $DPKG_OPTIONS grub-efi-ia32-bin grub-efi-ia32-signed
+      fi
+      chroot "$MNTPOINT" grub-install --target=i386-efi --efi-directory=/boot/efi --uefi-secure-boot --removable "/dev/$LOOP_DISK"
+      chroot "$MNTPOINT" grub-install --target=i386-pc "/dev/$LOOP_DISK"
+    else
+      if ! chroot "${MNTPOINT}" dpkg --list grub-efi-amd64-signed 2>/dev/null | grep -q '^ii' ; then
+        echo "Notice: grub-efi-amd64-signed package not present yet, installing it therefore."
+        # shellcheck disable=SC2086
+        DEBIAN_FRONTEND=$DEBIAN_FRONTEND chroot "$MNTPOINT" apt-get -y --no-install-recommends install $DPKG_OPTIONS grub-efi-amd64-bin grub-efi-amd64-signed
+      fi
+      chroot "$MNTPOINT" grub-install --target=x86_64-efi --efi-directory=/boot/efi --uefi-secure-boot --removable "/dev/$LOOP_DISK"
+      chroot "$MNTPOINT" grub-install --target=i386-pc "/dev/$LOOP_DISK"
+    fi
+  else
+    dd if="${MNTPOINT}/usr/lib/grub/i386-pc/boot.img" of="${ORIG_TARGET}" conv=notrunc bs=440 count=1
+    case "${_opt_filesystem}" in
+      f2fs)
+        chroot "${MNTPOINT}" grub-mkimage -O i386-pc -p "(hd0,msdos1)/boot/grub" -o /tmp/core.img biosdisk part_msdos f2fs
+        ;;
+      xfs)
+        chroot "${MNTPOINT}" grub-mkimage -O i386-pc -p "(hd0,msdos1)/boot/grub" -o /tmp/core.img biosdisk part_msdos xfs
+        ;;
+      # NOTE - we might need to distinguish between further filesystems
+      *)
+        chroot "${MNTPOINT}" grub-mkimage -O i386-pc -p "(hd0,msdos1)/boot/grub" -o /tmp/core.img biosdisk part_msdos ext2
+        ;;
+    esac
+
+    dd if="${MNTPOINT}/tmp/core.img" of="${ORIG_TARGET}" conv=notrunc seek=1
+    rm -f "${MNTPOINT}/tmp/core.img"
+  fi
 fi
 
 fi
 
-  einfo "Updating grub configuration file."
-  if [ -n "$BOOT_APPEND" ] ; then
-     sed -i "/GRUB_CMDLINE_LINUX_DEFAULT/ s#\"\$# ${BOOT_APPEND}\"#" "${MNTPOINT}"/etc/default/grub
+  # workaround for Debian bug #918590 with lvm + udev:
+  # WARNING: Device /dev/... not initialized in udev database even after waiting 10000000 microseconds
+  if [ -d /run/udev ] ; then
+    einfo "Setting up bind-mount /run/udev"
+    mkdir -p "${MNTPOINT}"/run/udev
+    mount --bind /run/udev "${MNTPOINT}"/run/udev
+    eend $?
   fi
   fi
+
+  einfo "Updating grub configuration file."
   chroot "${MNTPOINT}" update-grub
 
   case "$RELEASE" in
   chroot "${MNTPOINT}" update-grub
 
   case "$RELEASE" in
@@ -1498,15 +1673,42 @@ fi
     ewarn "Please note that your system might NOT be able to properly boot."
   else
     einfo "Adjusting grub.cfg for successful boot sequence."
     ewarn "Please note that your system might NOT be able to properly boot."
   else
     einfo "Adjusting grub.cfg for successful boot sequence."
-    sed -i "s;root=[^ ]\+;root=UUID=$TARGET_UUID;" "${MNTPOINT}"/boot/grub/grub.cfg
+    sed -i "s;root=[^ ]\\+;root=UUID=$TARGET_UUID;" "${MNTPOINT}"/boot/grub/grub.cfg
+  fi
+
+  # workaround for Debian bug #918590 with lvm + udev:
+  # WARNING: Device /dev/... not initialized in udev database even after waiting 10000000 microseconds
+  if mountpoint "${MNTPOINT}"/run/udev &>/dev/null ; then
+    einfo "Unmounting bind-mount /run/udev"
+    umount "${MNTPOINT}"/run/udev
+    eend $?
   fi
 
   umount "${MNTPOINT}"/proc
   umount "${MNTPOINT}"/sys
   umount "${MNTPOINT}"/dev/pts
   try_umount 3 "${MNTPOINT}"/dev
   fi
 
   umount "${MNTPOINT}"/proc
   umount "${MNTPOINT}"/sys
   umount "${MNTPOINT}"/dev/pts
   try_umount 3 "${MNTPOINT}"/dev
+
+  if [ -n "$VMEFI" ]; then
+    umount "${MNTPOINT}"/boot/efi
+  fi
+
+}
+# }}}
+
+# unmount VM image {{{
+umount_target() {
+  if [ -z "${VIRTUAL}" ] ; then
+     return 0
+  fi
+
   umount "${MNTPOINT}"
   kpartx -d "${ORIG_TARGET}" >/dev/null
   umount "${MNTPOINT}"
   kpartx -d "${ORIG_TARGET}" >/dev/null
+  # Workaround for a bug in kpartx which doesn't clean up properly,
+  # see Debian Bug #891077 and Github-PR grml/grml-debootstrap#112
+  if dmsetup ls | grep -q "^${LOOP_PART} "; then
+    kpartx -d "/dev/${LOOP_DISK}" >/dev/null
+  fi
 }
 # }}}
 
 }
 # }}}
 
@@ -1526,15 +1728,15 @@ debootstrap_system() {
 
   if [ -n "$ISO" ] ; then
     einfo "Running $DEBOOTSTRAP $DEBOOTSTRAP_OPT for release ${RELEASE}${ARCHINFO} using ${ISO}"
 
   if [ -n "$ISO" ] ; then
     einfo "Running $DEBOOTSTRAP $DEBOOTSTRAP_OPT for release ${RELEASE}${ARCHINFO} using ${ISO}"
-    einfo "Executing: $DEBOOTSTRAP $ARCHCMD $KEYRING $DEBOOTSTRAP_OPT $RELEASE $MNTPOINT $ISO"
+    einfo "Executing: $DEBOOTSTRAP $ARCHCMD $DEBOOTSTRAP_OPT $RELEASE $MNTPOINT $ISO"
     # shellcheck disable=SC2086
     # shellcheck disable=SC2086
-    "$DEBOOTSTRAP" $ARCHCMD $KEYRING $DEBOOTSTRAP_OPT "$RELEASE" "$MNTPOINT" "$ISO"
+    "$DEBOOTSTRAP" $ARCHCMD $DEBOOTSTRAP_OPT "$RELEASE" "$MNTPOINT" "$ISO"
     RC=$?
   else
     einfo "Running $DEBOOTSTRAP $DEBOOTSTRAP_OPT for release ${RELEASE}${ARCHINFO} using ${MIRROR}"
     RC=$?
   else
     einfo "Running $DEBOOTSTRAP $DEBOOTSTRAP_OPT for release ${RELEASE}${ARCHINFO} using ${MIRROR}"
-    einfo "Executing: $DEBOOTSTRAP $ARCHCMD $KEYRING $DEBOOTSTRAP_OPT $RELEASE $MNTPOINT $MIRROR"
+    einfo "Executing: $DEBOOTSTRAP $ARCHCMD $DEBOOTSTRAP_OPT $RELEASE $MNTPOINT $MIRROR"
     # shellcheck disable=SC2086
     # shellcheck disable=SC2086
-    "$DEBOOTSTRAP" $ARCHCMD $KEYRING $DEBOOTSTRAP_OPT "$RELEASE" "$MNTPOINT" "$MIRROR"
+    "$DEBOOTSTRAP" $ARCHCMD $DEBOOTSTRAP_OPT "$RELEASE" "$MNTPOINT" "$MIRROR"
     RC=$?
   fi
 
     RC=$?
   fi
 
@@ -1564,6 +1766,7 @@ preparechroot() {
   #   e.g. not with 4.2.37(1)-release (a.k.a 4.2+dfsg-0.1+deb7u3) of Debian wheezy
   [ -n "$ARCH" ]                && echo "ARCH='$(sed "s,','\\\\'',g" <<<"${ARCH}")'"                               >> "$CHROOT_VARIABLES"
   [ -n "$BACKPORTREPOS" ]       && echo "BACKPORTREPOS='$(sed "s,','\\\\'',g" <<<"${BACKPORTREPOS}")'"             >> "$CHROOT_VARIABLES"
   #   e.g. not with 4.2.37(1)-release (a.k.a 4.2+dfsg-0.1+deb7u3) of Debian wheezy
   [ -n "$ARCH" ]                && echo "ARCH='$(sed "s,','\\\\'',g" <<<"${ARCH}")'"                               >> "$CHROOT_VARIABLES"
   [ -n "$BACKPORTREPOS" ]       && echo "BACKPORTREPOS='$(sed "s,','\\\\'',g" <<<"${BACKPORTREPOS}")'"             >> "$CHROOT_VARIABLES"
+  [ -n "$BOOT_APPEND" ]         && echo "BOOT_APPEND='$(sed "s,','\\\\'',g" <<<"${BOOT_APPEND}")'"                 >> "$CHROOT_VARIABLES"
   [ -n "$CHROOT_SCRIPTS" ]      && echo "CHROOT_SCRIPTS='$(sed "s,','\\\\'',g" <<<"${CHROOT_SCRIPTS}")'"           >> "$CHROOT_VARIABLES"
   [ -n "$COMPONENTS" ]          && echo "COMPONENTS='$(sed "s,','\\\\'',g" <<<"${COMPONENTS}")'"                   >> "$CHROOT_VARIABLES"
   [ -n "$CONFFILES" ]           && echo "CONFFILES='$(sed "s,','\\\\'',g" <<<"${CONFFILES}")'"                     >> "$CHROOT_VARIABLES"
   [ -n "$CHROOT_SCRIPTS" ]      && echo "CHROOT_SCRIPTS='$(sed "s,','\\\\'',g" <<<"${CHROOT_SCRIPTS}")'"           >> "$CHROOT_VARIABLES"
   [ -n "$COMPONENTS" ]          && echo "COMPONENTS='$(sed "s,','\\\\'',g" <<<"${COMPONENTS}")'"                   >> "$CHROOT_VARIABLES"
   [ -n "$CONFFILES" ]           && echo "CONFFILES='$(sed "s,','\\\\'',g" <<<"${CONFFILES}")'"                     >> "$CHROOT_VARIABLES"
@@ -1575,11 +1778,14 @@ preparechroot() {
   [ -n "$EXTRAPACKAGES" ]       && echo "EXTRAPACKAGES='$(sed "s,','\\\\'',g" <<<"${EXTRAPACKAGES}")'"             >> "$CHROOT_VARIABLES"
   [ -n "$EFI" ]                 && echo "EFI='$(sed "s,','\\\\'',g" <<<"${EFI}")'"                                 >> "$CHROOT_VARIABLES"
   [ -n "$FALLBACK_MIRROR" ]     && echo "FALLBACK_MIRROR='$(sed "s,','\\\\'',g" <<<"${FALLBACK_MIRROR}")'"         >> "$CHROOT_VARIABLES"
   [ -n "$EXTRAPACKAGES" ]       && echo "EXTRAPACKAGES='$(sed "s,','\\\\'',g" <<<"${EXTRAPACKAGES}")'"             >> "$CHROOT_VARIABLES"
   [ -n "$EFI" ]                 && echo "EFI='$(sed "s,','\\\\'',g" <<<"${EFI}")'"                                 >> "$CHROOT_VARIABLES"
   [ -n "$FALLBACK_MIRROR" ]     && echo "FALLBACK_MIRROR='$(sed "s,','\\\\'',g" <<<"${FALLBACK_MIRROR}")'"         >> "$CHROOT_VARIABLES"
+  [ -n "$FILESYSTEM" ]          && echo "FILESYSTEM='$(sed "s,','\\\\'',g" <<<"${FILESYSTEM}")'"                   >> "$CHROOT_VARIABLES"
   [ -n "$FORCE" ]               && echo "FORCE='$(sed "s,','\\\\'',g" <<<"${FORCE}")'"                             >> "$CHROOT_VARIABLES"
   [ -n "$GRMLREPOS" ]           && echo "GRMLREPOS='$(sed "s,','\\\\'',g" <<<"${GRMLREPOS}")'"                     >> "$CHROOT_VARIABLES"
   [ -n "$GRUB" ]                && echo "GRUB='$(sed "s,','\\\\'',g" <<<"${GRUB}")'"                               >> "$CHROOT_VARIABLES"
   [ -n "$HOSTNAME" ]            && echo "HOSTNAME='$(sed "s,','\\\\'',g" <<<"${HOSTNAME}")'"                       >> "$CHROOT_VARIABLES"
   [ -n "$INITRD" ]              && echo "INITRD='$(sed "s,','\\\\'',g" <<<"${INITRD}")'"                           >> "$CHROOT_VARIABLES"
   [ -n "$FORCE" ]               && echo "FORCE='$(sed "s,','\\\\'',g" <<<"${FORCE}")'"                             >> "$CHROOT_VARIABLES"
   [ -n "$GRMLREPOS" ]           && echo "GRMLREPOS='$(sed "s,','\\\\'',g" <<<"${GRMLREPOS}")'"                     >> "$CHROOT_VARIABLES"
   [ -n "$GRUB" ]                && echo "GRUB='$(sed "s,','\\\\'',g" <<<"${GRUB}")'"                               >> "$CHROOT_VARIABLES"
   [ -n "$HOSTNAME" ]            && echo "HOSTNAME='$(sed "s,','\\\\'',g" <<<"${HOSTNAME}")'"                       >> "$CHROOT_VARIABLES"
   [ -n "$INITRD" ]              && echo "INITRD='$(sed "s,','\\\\'',g" <<<"${INITRD}")'"                           >> "$CHROOT_VARIABLES"
+  [ -n "$INITRD_GENERATOR" ]    && echo "INITRD_GENERATOR='$(sed "s,','\\\\'',g" <<<"${INITRD_GENERATOR}")'"       >> "$CHROOT_VARIABLES"
+  [ -n "$INITRD_GENERATOR_OPTS" ] && echo "INITRD_GENERATOR_OPTS='$(sed "s,','\\\\'',g" <<<"${INITRD_GENERATOR_OPTS}")'" >> "$CHROOT_VARIABLES"
   [ -n "$INSTALL_NOTES" ]       && echo "INSTALL_NOTES='$(sed "s,','\\\\'',g" <<<"${INSTALL_NOTES}")'"             >> "$CHROOT_VARIABLES"
   [ -n "$ISODIR" ]              && echo "ISODIR='$(sed "s,','\\\\'',g" <<<"${ISO}")'"                              >> "$CHROOT_VARIABLES"
   [ -n "$ISO" ]                 && echo "ISO='$(sed "s,','\\\\'',g" <<<"${ISO}")'"                                 >> "$CHROOT_VARIABLES"
   [ -n "$INSTALL_NOTES" ]       && echo "INSTALL_NOTES='$(sed "s,','\\\\'',g" <<<"${INSTALL_NOTES}")'"             >> "$CHROOT_VARIABLES"
   [ -n "$ISODIR" ]              && echo "ISODIR='$(sed "s,','\\\\'',g" <<<"${ISO}")'"                              >> "$CHROOT_VARIABLES"
   [ -n "$ISO" ]                 && echo "ISO='$(sed "s,','\\\\'',g" <<<"${ISO}")'"                                 >> "$CHROOT_VARIABLES"
@@ -1645,7 +1851,7 @@ preparechroot() {
 
   cp $VERBOSE -a -L "${CONFFILES}"/extrapackages/ "${MNTPOINT}"/etc/debootstrap/
 
 
   cp $VERBOSE -a -L "${CONFFILES}"/extrapackages/ "${MNTPOINT}"/etc/debootstrap/
 
-  # make sure we can access network [relevant for cdebootstrap]
+  # make sure we can access network [relevant for cdebootstrap/mmdebstrap]
   [ -f "${MNTPOINT}"/etc/resolv.conf ] || cp $VERBOSE /etc/resolv.conf "${MNTPOINT}"/etc/resolv.conf
 
   # setup default locales
   [ -f "${MNTPOINT}"/etc/resolv.conf ] || cp $VERBOSE /etc/resolv.conf "${MNTPOINT}"/etc/resolv.conf
 
   # setup default locales
@@ -1677,29 +1883,121 @@ allow-hotplug eth0
 iface eth0 inet dhcp
 "
 
 iface eth0 inet dhcp
 "
 
+  # add dhcp setting for Predictable Network Interface Names
+  if [ -x /bin/udevadm ]; then
+    tmpfile=$(mktemp)
+    for interface in /sys/class/net/*; do
+      udevadm info --query=all --path="${interface}" > "${tmpfile}"
+      # skip virtual devices, like bridges, vboxnet,...
+      if grep -q 'P: /devices/virtual/net/' "${tmpfile}" ; then
+        continue
+      fi
+
+      # iterate over possible naming policies by precedence (see udev/net/link-config.c),
+      # use and stop on first match to have same behavior as udev's link_config_apply()
+      for property in ID_NET_NAME_FROM_DATABASE ID_NET_NAME_ONBOARD ID_NET_NAME_SLOT ID_NET_NAME_PATH ID_NET_NAME_MAC ; do
+        if grep -q "${property}" "${tmpfile}" ; then
+          interface=$(grep "${property}" "${tmpfile}" | sed -n -e "s/E: ${property}=\([^\$*]\)/\1/p")
+          DEFAULT_INTERFACES="${DEFAULT_INTERFACES}
+allow-hotplug ${interface}
+iface ${interface} inet dhcp
+"
+          break
+        fi
+      done
+    done
+    rm -f "${tmpfile}"
+  fi
+
   if [ -n "$NOINTERFACES" ] ; then
     einfo "Not installing /etc/network/interfaces as requested via --nointerfaces option" ; eend 0
   elif [ -n "$USE_DEFAULT_INTERFACES" ] ; then
     einfo "Installing default /etc/network/interfaces as requested via --defaultinterfaces options."
   if [ -n "$NOINTERFACES" ] ; then
     einfo "Not installing /etc/network/interfaces as requested via --nointerfaces option" ; eend 0
   elif [ -n "$USE_DEFAULT_INTERFACES" ] ; then
     einfo "Installing default /etc/network/interfaces as requested via --defaultinterfaces options."
+    mkdir -p "${MNTPOINT}/etc/network"
     echo "$DEFAULT_INTERFACES" > "${MNTPOINT}/etc/network/interfaces"
     eend $?
   elif [ -n "$VIRTUAL" ] ; then
     einfo "Setting up Virtual Machine, installing default /etc/network/interfaces"
     echo "$DEFAULT_INTERFACES" > "${MNTPOINT}/etc/network/interfaces"
     eend $?
   elif [ -n "$VIRTUAL" ] ; then
     einfo "Setting up Virtual Machine, installing default /etc/network/interfaces"
+    mkdir -p "${MNTPOINT}/etc/network"
     echo "$DEFAULT_INTERFACES" > "${MNTPOINT}/etc/network/interfaces"
     eend $?
   elif [ -r /etc/network/interfaces ] ; then
     einfo "Copying /etc/network/interfaces from host to target system"
     echo "$DEFAULT_INTERFACES" > "${MNTPOINT}/etc/network/interfaces"
     eend $?
   elif [ -r /etc/network/interfaces ] ; then
     einfo "Copying /etc/network/interfaces from host to target system"
+    mkdir -p "${MNTPOINT}/etc/network"
     cp $VERBOSE /etc/network/interfaces "${MNTPOINT}/etc/network/interfaces"
     eend $?
   else
     ewarn "Couldn't read /etc/network/interfaces, installing default /etc/network/interfaces"
     cp $VERBOSE /etc/network/interfaces "${MNTPOINT}/etc/network/interfaces"
     eend $?
   else
     ewarn "Couldn't read /etc/network/interfaces, installing default /etc/network/interfaces"
+    mkdir -p "${MNTPOINT}/etc/network"
     echo "$DEFAULT_INTERFACES" > "${MNTPOINT}/etc/network/interfaces"
     eend $?
   fi
 
   # install config file providing some example entries
   if [ -r /etc/network/interfaces.examples ] && [ ! -r "$MNTPOINT/etc/network/interfaces.examples" ] ; then
     echo "$DEFAULT_INTERFACES" > "${MNTPOINT}/etc/network/interfaces"
     eend $?
   fi
 
   # install config file providing some example entries
   if [ -r /etc/network/interfaces.examples ] && [ ! -r "$MNTPOINT/etc/network/interfaces.examples" ] ; then
-     cp /etc/network/interfaces.examples "$MNTPOINT/etc/network/interfaces.examples"
+    mkdir -p "${MNTPOINT}/etc/network"
+    cp /etc/network/interfaces.examples "$MNTPOINT/etc/network/interfaces.examples"
+  fi
+
+  if [ -n "${SSHCOPYID}" ] ; then
+    AUTHORIZED_KEYS_SOURCE=${AUTHORIZED_KEYS_SOURCE:-$HOME/.ssh/authorized_keys}
+    AUTHORIZED_KEYS_TARGET=${AUTHORIZED_KEYS_TARGET:-$MNTPOINT/root/.ssh/}
+    if ssh-add -L >/dev/null 2>&1 ; then
+      einfo "Use locally available public keys to authorise root login on the target system as requested via --sshcopyid option."
+      mkdir -p "${MNTPOINT}"/root/.ssh
+      chmod 0700 "${MNTPOINT}"/root/.ssh
+      if ssh-add -L >> "${MNTPOINT}"/root/.ssh/authorized_keys ; then
+        eend 0
+      else
+        eerror "Error: executing 'ssh-add -L' failed."
+        eend 1
+        bailout 1
+      fi
+    elif [ -f "$AUTHORIZED_KEYS_SOURCE" ]; then
+      einfo "copying '$AUTHORIZED_KEYS_SOURCE' to '$AUTHORIZED_KEYS_TARGET' as requested via --sshcopyid option."
+      mkdir -p "$AUTHORIZED_KEYS_TARGET"
+      chmod 0700 "$AUTHORIZED_KEYS_TARGET"
+      if cp "$AUTHORIZED_KEYS_SOURCE" "$AUTHORIZED_KEYS_TARGET" ; then
+        eend 0
+      else
+        eerror "Error: copying '$AUTHORIZED_KEYS_SOURCE' to '$AUTHORIZED_KEYS_TARGET' failed"
+        eend 1
+        bailout 1
+      fi
+    else
+      eerror "Error: Could not open a connection to your authentication agent or the agent has no identities."
+      eend 1
+      bailout 1
+    fi
+  fi
+
+  if [ -n "${SSHCOPYAUTH}" ] ; then
+    AUTHORIZED_KEYS_SOURCE=${AUTHORIZED_KEYS_SOURCE:-${HOME}/.ssh/authorized_keys}
+
+    if ! [ -f "${AUTHORIZED_KEYS_SOURCE}" ]; then
+      eerror "Error: could not read '${AUTHORIZED_KEYS_SOURCE}' for setting up SSH key login."
+      eend 1
+      bailout 1
+    fi
+
+    AUTHORIZED_KEYS_TARGET="${MNTPOINT}/root/.ssh/"
+    einfo "Copying '${AUTHORIZED_KEYS_SOURCE}' to '${AUTHORIZED_KEYS_TARGET}' as requested via --sshcopyauth option."
+    mkdir -p "${AUTHORIZED_KEYS_TARGET}"
+    chmod 0700 "${AUTHORIZED_KEYS_TARGET}"
+    if cp "${AUTHORIZED_KEYS_SOURCE}" "${AUTHORIZED_KEYS_TARGET}" ; then
+      eend 0
+    else
+      eerror "Error: copying '${AUTHORIZED_KEYS_SOURCE}' to '${AUTHORIZED_KEYS_TARGET}' failed."
+      eend 1
+      bailout 1
+    fi
+  fi
+
+  if [ -d /run/udev ] ; then
+    einfo "Setting up bind-mount /run/udev"
+    mkdir -p "${MNTPOINT}"/run/udev
+    mount --bind /run/udev "${MNTPOINT}"/run/udev
+    eend $?
   fi
 
   eend 0
   fi
 
   eend 0
@@ -1751,6 +2049,7 @@ execute_post_scripts() {
 }
 # }}}
 
 }
 # }}}
 
+# unmount mountpoint {{{
 try_umount() {
   local tries=$1
   local mountpoint="$2"
 try_umount() {
   local tries=$1
   local mountpoint="$2"
@@ -1770,6 +2069,7 @@ try_umount() {
   done
   return 1  # Tried enough
 }
   done
   return 1  # Tried enough
 }
+# }}}
 
 # execute chroot-script {{{
 chrootscript() {
 
 # execute chroot-script {{{
 chrootscript() {
@@ -1824,6 +2124,12 @@ umount_chroot() {
   fi
 
   if grep -q "$MNTPOINT" /proc/mounts ; then
   fi
 
   if grep -q "$MNTPOINT" /proc/mounts ; then
+    if mountpoint "${MNTPOINT}"/run/udev &>/dev/null ; then
+      einfo "Unmounting bind-mount /run/udev"
+      umount "${MNTPOINT}"/run/udev
+      eend $?
+    fi
+
      if [ -n "$PARTITION" ] ; then
         einfo "Unmount $MNTPOINT"
         umount "$MNTPOINT"
      if [ -n "$PARTITION" ] ; then
         einfo "Unmount $MNTPOINT"
         umount "$MNTPOINT"
@@ -1870,9 +2176,10 @@ remove_configs() {
 for i in format_efi_partition prepare_vm mkfs tunefs \
          mount_target mountpoint_to_blockdevice debootstrap_system \
          preparechroot execute_pre_scripts chrootscript execute_post_scripts \
 for i in format_efi_partition prepare_vm mkfs tunefs \
          mount_target mountpoint_to_blockdevice debootstrap_system \
          preparechroot execute_pre_scripts chrootscript execute_post_scripts \
-         remove_configs umount_chroot finalize_vm fscktool ; do
+         remove_configs umount_chroot grub_install umount_target fscktool ; do
     if stage "${i}" ; then
     if stage "${i}" ; then
-      if "$i" ; then
+      "$i"
+      if [ $? -eq 0 ]; then
         stage "${i}" 'done' && rm -f "${STAGES}/${i}"
       else
         bailout 2 "$i"
         stage "${i}" 'done' && rm -f "${STAGES}/${i}"
       else
         bailout 2 "$i"