grml-debootstrap man page: point users towards github instead of private mail
[grml-debootstrap.git] / grml-debootstrap
index 7e88c11..9b04baa 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+
 ################################################################################
 
@@ -40,7 +40,7 @@ fi
 
 # variables {{{
 PN="$(basename "$0")"
 
 # variables {{{
 PN="$(basename "$0")"
-if [[ -d "$(dirname "$(which "$0")")"/.git ]]; then
+if [[ -d "$(dirname "$(command -v "$0")")"/.git ]]; then
   VERSION="$(git describe | sed 's|^v||')"
 else
   VERSION="$(dpkg-query --show --showformat='${Version}' "$PN")"
   VERSION="$(git describe | sed 's|^v||')"
 else
   VERSION="$(dpkg-query --show --showformat='${Version}' "$PN")"
@@ -58,7 +58,7 @@ 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 "$FIXED_DISK_IDENTIFIERS" ] || FIXED_DISK_IDENTIFIERS="no"
 [ -n "$FORCE" ] || FORCE=''
 [ -n "$HOSTNAME" ] || HOSTNAME='grml'
@@ -72,7 +72,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='buster'
 [ -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 +80,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 +103,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: buster).
   -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,
@@ -168,7 +169,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/
 "
 }
 
 "
 }
 
@@ -186,7 +187,7 @@ WARN='\e[33;01m'
 NORMAL='\e[0m'
 
 einfo() {
 NORMAL='\e[0m'
 
 einfo() {
-  einfon "$1\n"
+  einfon "$1\\n"
   return 0
 }
 
   return 0
 }
 
@@ -198,13 +199,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 +214,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 +228,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 +266,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 +299,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
 }
 
@@ -328,7 +335,7 @@ stage() {
 # }}}
 
 # make sure we have what we need {{{
 # }}}
 
 # make sure we have what we need {{{
-check4progs debootstrap || bailout 1
+check4progs "${DEBOOTSTRAP}" || bailout 1
 # }}}
 
 # source main configuration file {{{
 # }}}
 
 # source main configuration file {{{
@@ -480,8 +487,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
@@ -578,6 +588,12 @@ if [ "$_opt_grub" ] && [ "$_opt_vmfile" ] ; then
   bailout 1
 fi
 
   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
@@ -749,15 +765,17 @@ 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='buster'
   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 \
+            0 50 8 \
             lenny    Debian/5.0 \
             squeeze  Debian/6.0 \
             wheezy   Debian/7.0 \
             jessie   Debian/8.0 \
             stretch  Debian/9.0 \
             lenny    Debian/5.0 \
             squeeze  Debian/6.0 \
             wheezy   Debian/7.0 \
             jessie   Debian/8.0 \
             stretch  Debian/9.0 \
+            buster   Debian/10.0 \
+            bullseye Debian/11.0 \
             sid      Debian/unstable)"
   [ $? -eq 0 ] || bailout
 }
             sid      Debian/unstable)"
   [ $? -eq 0 ] || bailout
 }
@@ -815,7 +833,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 +943,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
@@ -950,6 +969,7 @@ 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() {
@@ -1124,7 +1144,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 stretch or buster." ; eend 1
    bailout 1
 fi
 # }}}
    bailout 1
 fi
 # }}}
@@ -1175,6 +1195,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
 # }}}
@@ -1284,6 +1310,7 @@ mkfs() {
 }
 # }}}
 
 }
 # }}}
 
+# retrieve ID_FS_UUID {{{
 identify_target_uuid() {
   local device="$1"
 
 identify_target_uuid() {
   local device="$1"
 
@@ -1299,7 +1326,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 +1353,7 @@ mountpoint_to_blockdevice() {
     fi
   done
 }
     fi
   done
 }
+# }}}
 
 # modify filesystem settings {{{
 tunefs() {
 
 # modify filesystem settings {{{
 tunefs() {
@@ -1378,6 +1408,21 @@ prepare_vm() {
      bailout 1
   fi
 
      bailout 1
   fi
 
+  # 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
+
+  # if dm-mod isn't available then kpartx will fail with
+  # "Is device-mapper driver missing from kernel? [...]"
+  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
+
   ORIG_TARGET="$TARGET" # store for later reuse
 
   if [ -n "$VMFILE" ]; then
   ORIG_TARGET="$TARGET" # store for later reuse
 
   if [ -n "$VMFILE" ]; then
@@ -1388,32 +1433,13 @@ prepare_vm() {
     einfo "Adjusting disk signature to a fixed (non-random) value"
     MBRTMPFILE=$(mktemp)
     dd if="${TARGET}" of="${MBRTMPFILE}" bs=512 count=1
     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
+    echo -en "\\x41\\x41\\x41\\x41" | dd of="${MBRTMPFILE}" conv=notrunc seek=440 bs=1
     dd if="${MBRTMPFILE}" of="${TARGET}" conv=notrunc
     eend $?
   fi
   parted -s "${TARGET}" 'mkpart primary ext4 2M -1'
   parted -s "${TARGET}" 'set 1 boot on'
 
     dd if="${MBRTMPFILE}" of="${TARGET}" conv=notrunc
     eend $?
   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 ! 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 $?
-  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
-    fi
-  fi
-
   DEVINFO=$(kpartx -asv "$TARGET") # e.g. 'add map loop0p1 (254:5): 0 20477 linear 7:0 3'
   if [ -z "${DEVINFO}" ] ; then
     eerror "Error setting up loopback device." ; eend 1
   DEVINFO=$(kpartx -asv "$TARGET") # e.g. 'add map loop0p1 (254:5): 0 20477 linear 7:0 3'
   if [ -z "${DEVINFO}" ] ; then
     eerror "Error setting up loopback device." ; eend 1
@@ -1423,6 +1449,7 @@ 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'
+  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 +1459,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,7 +1481,7 @@ 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
   einfo "Installing Grub as bootloader."
 
   if ! chroot "${MNTPOINT}" dpkg --list grub-pc 2>/dev/null | grep -q '^ii' ; then
@@ -1498,15 +1529,30 @@ 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
 
   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
+
+}
+# }}}
+
+# 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
 }
 # }}}
 
 }
 # }}}
 
@@ -1645,7 +1691,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
@@ -1702,6 +1748,13 @@ iface eth0 inet dhcp
      cp /etc/network/interfaces.examples "$MNTPOINT/etc/network/interfaces.examples"
   fi
 
      cp /etc/network/interfaces.examples "$MNTPOINT/etc/network/interfaces.examples"
   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
 }
 # }}}
   eend 0
 }
 # }}}
@@ -1751,6 +1804,7 @@ execute_post_scripts() {
 }
 # }}}
 
 }
 # }}}
 
+# unmount mountpoint {{{
 try_umount() {
   local tries=$1
   local mountpoint="$2"
 try_umount() {
   local tries=$1
   local mountpoint="$2"
@@ -1770,6 +1824,7 @@ try_umount() {
   done
   return 1  # Tried enough
 }
   done
   return 1  # Tried enough
 }
+# }}}
 
 # execute chroot-script {{{
 chrootscript() {
 
 # execute chroot-script {{{
 chrootscript() {
@@ -1824,6 +1879,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,7 +1931,7 @@ 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 "$i" ; then
         stage "${i}" 'done' && rm -f "${STAGES}/${i}"
     if stage "${i}" ; then
       if "$i" ; then
         stage "${i}" 'done' && rm -f "${STAGES}/${i}"