Do not fail if ISO doesn't have grub.cfg inside efi.img
[grml2usb.git] / grml2usb
index ac913c0..d206f1d 100755 (executable)
--- a/grml2usb
+++ b/grml2usb
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 # pylint: disable-msg=C0302
 """
 grml2usb
 ~~~~~~~~
 
 # -*- coding: utf-8 -*-
 # pylint: disable-msg=C0302
 """
 grml2usb
 ~~~~~~~~
 
-This script installs a grml system (either a running system or ISO[s]) to a USB device
+This script installs a Grml system (either a running system or ISO[s]) to a USB device
 
 
-:copyright: (c) 2009, 2010, 2011 by Michael Prokop <mika@grml.org>
+:copyright: (c) 2009-2019 by Michael Prokop <mika@grml.org>
 :license: GPL v2 or any later version
 :bugreports: http://grml.org/bugs/
 
 """
 
 :license: GPL v2 or any later version
 :bugreports: http://grml.org/bugs/
 
 """
 
-from optparse import OptionParser
-from inspect import isroutine, isclass
-import datetime, logging, os, re, subprocess, sys, tempfile, time, os.path
+
+import datetime
 import fileinput
 import glob
 import fileinput
 import glob
-import uuid
+import logging
+import os
+import os.path
+import re
+import shutil
 import struct
 import struct
+import subprocess
+import sys
+import tempfile
+import time
+import uuid
+from inspect import isclass, isroutine
+from optparse import OptionParser
+
+# The line following this line is patched by debian/rules and tarball.sh.
+PROG_VERSION = "***UNKNOWN***"
+
+# when running from inside git, try to report version information via git-describe
+try:
+    git_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
+    with open(os.devnull, "w") as devnull:
+        PROG_VERSION = (
+            subprocess.check_output(
+                ["git", "-C", git_dir, "describe", "--always", "--dirty"],
+                stderr=devnull,
+            )
+            .strip()
+            .decode("utf-8", errors="replace")
+            + " (git)"
+        )
+except Exception:
+    pass
 
 # global variables
 
 # global variables
-PROG_VERSION = "0.9.30"
 MOUNTED = set()  # register mountpoints
 MOUNTED = set()  # register mountpoints
-TMPFILES = set() # register tmpfiles
-DATESTAMP = time.mktime(datetime.datetime.now().timetuple()) # unique identifier for syslinux.cfg
-GRML_FLAVOURS = set() # which flavours are being installed?
+TMPFILES = set()  # register tmpfiles
+DATESTAMP = time.mktime(
+    datetime.datetime.now().timetuple()
+)  # unique identifier for syslinux.cfg
+GRML_FLAVOURS = set()  # which flavours are being installed?
 GRML_DEFAULT = None
 UUID = None
 GRML_DEFAULT = None
 UUID = None
-SYSLINUX_LIBS = "/usr/lib/syslinux/"
+SYSLINUX_LIBS = [
+    "/usr/lib/syslinux/modules/bios/",  # Debian
+    "/usr/lib/syslinux/bios/",  # Arch Linux
+]
+GPT_HEADER = b"\x55\xaa\x45\x46\x49\x20\x50\x41\x52\x54"  # original GPT header
+GRUB_INSTALL = None
+
+RE_PARTITION = re.compile(r"([a-z/]*?)(\d+)$")
+RE_P_PARTITION = re.compile(r"(.*?\d+)p(\d+)$")
+RE_LOOP_DEVICE = re.compile(r"/dev/loop\d+$")
+
 
 def syslinux_warning(option, opt, value, opt_parser):
     """A helper function for printing a warning about deprecated option
     """
     # pylint: disable-msg=W0613
 
 def syslinux_warning(option, opt, value, opt_parser):
     """A helper function for printing a warning about deprecated option
     """
     # pylint: disable-msg=W0613
-    sys.stderr.write("Note: the --syslinux option is deprecated as syslinux "
-                     "is grml2usb's default. Continuing anyway.\n")
+    sys.stderr.write(
+        "Note: the --syslinux option is deprecated as syslinux "
+        "is grml2usb's default. Continuing anyway.\n"
+    )
     setattr(opt_parser.values, option.dest, True)
 
     setattr(opt_parser.values, option.dest, True)
 
+
 # if grub option is set, unset syslinux option
 def grub_option(option, opt, value, opt_parser):
     """A helper function adjusting other option values
     """
     # pylint: disable-msg=W0613
     setattr(opt_parser.values, option.dest, True)
 # if grub option is set, unset syslinux option
 def grub_option(option, opt, value, opt_parser):
     """A helper function adjusting other option values
     """
     # pylint: disable-msg=W0613
     setattr(opt_parser.values, option.dest, True)
-    setattr(opt_parser.values, 'syslinux', False)
+    setattr(opt_parser.values, "syslinux", False)
+
 
 # cmdline parsing
 
 # cmdline parsing
-USAGE = "Usage: %prog [options] <[ISO[s] | /live/image]> </dev/sdX#>\n\
+USAGE = "Usage: %prog [options] <[ISO[s] | /run/live/medium]> </dev/sdX#>\n\
 \n\
 \n\
-%prog installs grml ISO[s] to an USB device to be able to boot from it.\n\
-Make sure you have at least one grml ISO or a running grml system (/live/image),\n\
+%prog installs Grml ISO[s] to an USB device to be able to boot from it.\n\
+Make sure you have at least one Grml ISO or a running Grml system (/run/live/medium),\n\
 grub or syslinux and root access.\n\
 \n\
 Run %prog --help for usage hints, further information via: man grml2usb"
 grub or syslinux and root access.\n\
 \n\
 Run %prog --help for usage hints, further information via: man grml2usb"
@@ -59,62 +103,174 @@ Run %prog --help for usage hints, further information via: man grml2usb"
 # pylint: disable-msg=C0103
 # pylint: disable-msg=W0603
 parser = OptionParser(usage=USAGE)
 # pylint: disable-msg=C0103
 # pylint: disable-msg=W0603
 parser = OptionParser(usage=USAGE)
-parser.add_option("--bootoptions", dest="bootoptions",
-                  action="append", type="string",
-                  help="use specified bootoptions as default")
-parser.add_option("--bootloader-only", dest="bootloaderonly", action="store_true",
-                  help="do not copy files but just install a bootloader")
-parser.add_option("--copy-only", dest="copyonly", action="store_true",
-                  help="copy files only but do not install bootloader")
-parser.add_option("--dry-run", dest="dryrun", action="store_true",
-                  help="avoid executing commands")
-parser.add_option("--fat16", dest="fat16", action="store_true",
-                  help="format specified partition with FAT16")
-parser.add_option("--force", dest="force", action="store_true",
-                  help="force any actions requiring manual interaction")
-parser.add_option("--grub", dest="grub", action="callback",
-                  callback=grub_option,
-                  help="install grub bootloader instead of (default) syslinux")
-parser.add_option("--grub-mbr", dest="grubmbr", action="store_true",
-                  help="install grub into MBR instead of (default) PBR")
-parser.add_option("--lilo-binary", dest="lilobin",  action="store", type="string",
-                  help="lilo executable to be used for installing MBR")
-parser.add_option("--mbr-menu", dest="mbrmenu", action="store_true",
-                  help="enable interactive boot menu in MBR")
-parser.add_option("--quiet", dest="quiet", action="store_true",
-                  help="do not output anything but just errors on console")
-parser.add_option("--remove-bootoption", dest="removeoption", action="append",
-                  help="regex for removing existing bootoptions")
-parser.add_option("--skip-addons", dest="skipaddons", action="store_true",
-                  help="do not install /boot/addons/ files")
-parser.add_option("--skip-grub-config", dest="skipgrubconfig", action="store_true",
-                  help="skip generation of grub configuration files")
-parser.add_option("--skip-mbr", dest="skipmbr", action="store_true",
-                  help="do not install a master boot record (MBR) on the device")
-parser.add_option("--skip-syslinux-config", dest="skipsyslinuxconfig", action="store_true",
-                  help="skip generation of syslinux configuration files")
-parser.add_option("--syslinux", dest="syslinux", action="callback", default=True,
-                  callback=syslinux_warning,
-                  help="install syslinux bootloader (deprecated as it's the default)")
-parser.add_option("--syslinux-mbr", dest="syslinuxmbr", action="store_true",
-                  help="install syslinux master boot record (MBR) instead of default")
-parser.add_option("--verbose", dest="verbose", action="store_true",
-                  help="enable verbose mode")
-parser.add_option("-v", "--version", dest="version", action="store_true",
-                  help="display version and exit")
+parser.add_option(
+    "--bootoptions",
+    dest="bootoptions",
+    action="append",
+    type="string",
+    help="use specified bootoptions as default",
+)
+parser.add_option(
+    "--bootloader-only",
+    dest="bootloaderonly",
+    action="store_true",
+    help="do not copy files but just install a bootloader",
+)
+parser.add_option(
+    "--copy-only",
+    dest="copyonly",
+    action="store_true",
+    help="copy files only but do not install bootloader",
+)
+parser.add_option(
+    "--dry-run", dest="dryrun", action="store_true", help="avoid executing commands"
+)
+parser.add_option(
+    "--fat16",
+    dest="fat16",
+    action="store_true",
+    help="format specified partition with FAT16",
+)
+parser.add_option(
+    "--force",
+    dest="force",
+    action="store_true",
+    help="force any actions requiring manual interaction",
+)
+parser.add_option(
+    "--grub",
+    dest="grub",
+    action="callback",
+    callback=grub_option,
+    help="install grub bootloader instead of (default) syslinux",
+)
+parser.add_option(
+    "--grub-mbr",
+    dest="grubmbr",
+    action="store_true",
+    help="install grub into MBR instead of (default) PBR",
+)
+parser.add_option(
+    "--mbr-menu",
+    dest="mbrmenu",
+    action="store_true",
+    help="enable interactive boot menu in MBR",
+)
+parser.add_option(
+    "--quiet",
+    dest="quiet",
+    action="store_true",
+    help="do not output anything but just errors on console",
+)
+parser.add_option(
+    "--remove-bootoption",
+    dest="removeoption",
+    action="append",
+    help="regex for removing existing bootoptions",
+)
+parser.add_option(
+    "--rw-blockdev",
+    dest="rwblockdev",
+    action="store_true",
+    help="enforce read-write mode on involved block devices",
+)
+parser.add_option(
+    "--skip-addons",
+    dest="skipaddons",
+    action="store_true",
+    help="do not install /boot/addons/ files",
+)
+parser.add_option(
+    "--skip-bootflag",
+    dest="skipbootflag",
+    action="store_true",
+    help="do not try to check whether the destination has the boot flag set",
+)
+parser.add_option(
+    "--skip-grub-config",
+    dest="skipgrubconfig",
+    action="store_true",
+    help="skip generation of grub configuration files",
+)
+parser.add_option(
+    "--skip-mbr",
+    dest="skipmbr",
+    action="store_true",
+    help="do not install a master boot record (MBR) on the device",
+)
+parser.add_option(
+    "--skip-syslinux-config",
+    dest="skipsyslinuxconfig",
+    action="store_true",
+    help="skip generation of syslinux configuration files",
+)
+parser.add_option(
+    "--skip-usb-check",
+    dest="skipusbcheck",
+    action="store_true",
+    help="skip check to verify whether given device is removable",
+)
+parser.add_option(
+    "--syslinux",
+    dest="syslinux",
+    action="callback",
+    default=True,
+    callback=syslinux_warning,
+    help="install syslinux bootloader (deprecated as it's the default)",
+)
+parser.add_option(
+    "--syslinux-mbr",
+    dest="syslinuxmbr",
+    action="store_true",
+    help="install syslinux master boot record (MBR) instead of default",
+)
+parser.add_option(
+    "--syslinux-libs",
+    dest="syslinuxlibs",
+    action="append",
+    default=[],
+    help="syslinux modules directory path",
+)
+parser.add_option(
+    "--tmpdir",
+    dest="tmpdir",
+    default="/tmp",
+    help="directory to be used for temporary files",
+)
+parser.add_option(
+    "--verbose", dest="verbose", action="store_true", help="enable verbose mode"
+)
+parser.add_option(
+    "-v",
+    "--version",
+    dest="version",
+    action="store_true",
+    help="display version and exit",
+)
 (options, args) = parser.parse_args()
 
 
 (options, args) = parser.parse_args()
 
 
-GRML2USB_BASE = '/usr/share/grml2usb'
+GRML2USB_BASE = "/usr/share/grml2usb"
 if not os.path.isdir(GRML2USB_BASE):
     GRML2USB_BASE = os.path.dirname(os.path.realpath(__file__))
 
 
 if not os.path.isdir(GRML2USB_BASE):
     GRML2USB_BASE = os.path.dirname(os.path.realpath(__file__))
 
 
+class HodorException(Exception):
+    """Throw exception if the exact error is not known and not fatal.
+
+    @Exception: message"""
+
+
 class CriticalException(Exception):
 class CriticalException(Exception):
-    """Throw critical exception if the exact error is not known but fatal."
+    """Throw critical exception if the exact error is not known but fatal.
+
+    @Exception: message"""
+
+
+class VerifyException(Exception):
+    """Throw critical exception if there is an fatal error when verifying something.
 
     @Exception: message"""
 
     @Exception: message"""
-    pass
 
 
 # The following two functions help to operate on strings as
 
 
 # The following two functions help to operate on strings as
@@ -138,18 +294,35 @@ def cleanup():
     """Cleanup function to make sure there aren't any mounted devices left behind.
     """
 
     """Cleanup function to make sure there aren't any mounted devices left behind.
     """
 
+    def del_failed(fn, path, exc):
+        logging.warn("Deletion of %s failed in temporary folder" % path)
+
     logging.info("Cleaning up before exiting...")
     proc = subprocess.Popen(["sync"])
     proc.wait()
 
     logging.info("Cleaning up before exiting...")
     proc = subprocess.Popen(["sync"])
     proc.wait()
 
-    try:
-        for device in MOUNTED:
+    for device in MOUNTED.copy():
+        try:
             unmount(device, "")
             unmount(device, "")
-        for tmpfile in TMPFILES:
-            os.unlink(tmpfile)
-    # ignore: RuntimeError: Set changed size during iteration
-    except RuntimeError:
-        logging.debug('caught expection RuntimeError, ignoring')
+            logging.debug("Unmounted %s" % device)
+        except Exception:
+            logging.debug("RuntimeError while umount %s, ignoring" % device)
+
+    for tmppath in TMPFILES.copy():
+        try:
+            if os.path.isdir(tmppath) and not os.path.islink(tmppath):
+                # symbolic links to directories are ignored
+                # without the check it will throw an OSError
+                shutil.rmtree(tmppath, onerror=del_failed)
+                logging.debug("temporary directory %s deleted" % tmppath)
+                unregister_tmpfile(tmppath)
+            elif os.path.isfile:
+                os.unlink(tmppath)
+                logging.debug("temporary file %s deleted" % tmppath)
+                unregister_tmpfile(tmppath)
+        except Exception:
+            msg = "RuntimeError while removing temporary %s, ignoring"
+            logging.debug(msg % tmppath)
 
 
 def register_tmpfile(path):
 
 
 def register_tmpfile(path):
@@ -191,13 +364,24 @@ def unregister_mountpoint(target):
 
 
 def get_function_name(obj):
 
 
 def get_function_name(obj):
-    """Helper function for use in execute() to retrive name of a function
+    """Helper function for use in execute() to retrieve name of a function
 
     @obj: the function object
     """
     if not (isroutine(obj) or isclass(obj)):
         obj = type(obj)
 
     @obj: the function object
     """
     if not (isroutine(obj) or isclass(obj)):
         obj = type(obj)
-    return obj.__module__ + '.' + obj.__name__
+    return obj.__module__ + "." + obj.__name__
+
+
+def set_rw(device):
+    if not options.rwblockdev:
+        return
+
+    logging.debug("executing: blockdev --setrw %s", device)
+    proc = subprocess.Popen(["blockdev", "--setrw", device])
+    proc.wait()
+    if proc.returncode != 0:
+        raise Exception("error executing blockdev on %s" % device)
 
 
 def execute(f, *exec_arguments):
 
 
 def execute(f, *exec_arguments):
@@ -207,7 +391,11 @@ def execute(f, *exec_arguments):
     # usage: execute(subprocess.Popen, (["ls", "-la"]))
     if options.dryrun:
         # pylint: disable-msg=W0141
     # usage: execute(subprocess.Popen, (["ls", "-la"]))
     if options.dryrun:
         # pylint: disable-msg=W0141
-        logging.debug('dry-run only: %s(%s)', get_function_name(f), ', '.join(map(repr, exec_arguments)))
+        logging.debug(
+            "dry-run only: %s(%s)",
+            get_function_name(f),
+            ", ".join(map(repr, exec_arguments)),
+        )
     else:
         # pylint: disable-msg=W0142
         return f(*exec_arguments)
     else:
         # pylint: disable-msg=W0142
         return f(*exec_arguments)
@@ -241,50 +429,109 @@ def which(program):
 def get_defaults_file(iso_mount, flavour, name):
     """get the default file for syslinux
     """
 def get_defaults_file(iso_mount, flavour, name):
     """get the default file for syslinux
     """
-    bootloader_dirs = ['/boot/isolinux/', '/boot/syslinux/']
+    bootloader_dirs = ["/boot/isolinux/", "/boot/syslinux/"]
     for directory in bootloader_dirs:
     for directory in bootloader_dirs:
-        for name in name, \
-        "%s_%s" % (get_flavour_filename(flavour), name):
+        for name in name, "%s_%s" % (get_flavour_filename(flavour), name):
             if os.path.isfile(iso_mount + directory + name):
                 return (directory, name)
             if os.path.isfile(iso_mount + directory + name):
                 return (directory, name)
-    return ('','')
+    return ("", "")
+
 
 
-def search_file(filename, search_path='/bin' + os.pathsep + '/usr/bin'):
+def search_file(
+    filename,
+    search_path="/bin" + os.pathsep + "/usr/bin",
+    lst_return=False,
+    required=False,
+):
     """Given a search path, find file
 
     @filename: name of file to search for
     """Given a search path, find file
 
     @filename: name of file to search for
-    @search_path: path where searching for the specified filename"""
-    file_found = 0
+    @search_path: path where searching for the specified filename
+    @lst_return: return list of matching files instead one file"""
     paths = search_path.split(os.pathsep)
     paths = search_path.split(os.pathsep)
-    current_dir = '' # make pylint happy :)
+    current_dir = ""  # make pylint happy :)
+    retval = []
 
     def match_file(cwd):
         """Helper function ffor testing if specified file exists in cwd
 
         @cwd: current working directory
         """
 
     def match_file(cwd):
         """Helper function ffor testing if specified file exists in cwd
 
         @cwd: current working directory
         """
-        return  os.path.exists(os.path.join(cwd, filename))
+        return os.path.exists(os.path.join(cwd, filename))
 
     for path in paths:
         current_dir = path
         if match_file(current_dir):
 
     for path in paths:
         current_dir = path
         if match_file(current_dir):
-            file_found = 1
-            break
+            retval.append(os.path.abspath(os.path.join(current_dir, filename)))
+            if not lst_return:
+                break
         # pylint: disable-msg=W0612
         for current_dir, directories, files in os.walk(path):
             if match_file(current_dir):
         # pylint: disable-msg=W0612
         for current_dir, directories, files in os.walk(path):
             if match_file(current_dir):
-                file_found = 1
-                break
-    if file_found:
-        return os.path.abspath(os.path.join(current_dir, filename))
+                retval.append(os.path.abspath(os.path.join(current_dir, filename)))
+                if not lst_return:
+                    break
+
+    if required and not retval:
+        raise CriticalException(
+            "Required file %s not found in %s" % (filename, search_path)
+        )
+
+    if lst_return:
+        return retval
+    elif retval:
+        return retval[0]
     else:
         return None
 
 
 def check_uid_root():
     """Check for root permissions"""
     else:
         return None
 
 
 def check_uid_root():
     """Check for root permissions"""
-    if not os.geteuid()==0:
-        sys.exit("Error: please run this script with uid 0 (root).")
+    if not os.geteuid() == 0:
+        raise CriticalException("please run this script with uid 0 (root).")
+
+
+# for usage inside check_boot_flag
+def get_partition_for_path(path):
+    import parted
+
+    boot_dev, x = get_device_from_partition(path)
+
+    d = parted.getDevice(boot_dev)
+    disk = parted.Disk(d)
+    return disk.getPartitionByPath(path)
+
+
+def check_boot_flag(device):
+    if os.path.isdir(device):
+        logging.debug(
+            "Device %s is a directory, skipping check for boot flag." % device
+        )
+        return
+
+    boot_dev, x = get_device_from_partition(device)
+
+    logging.info("Checking for boot flag")
+    try:
+        import parted
+
+        part = get_partition_for_path(device)
+        if part is None:
+            raise HodorException("parted could not find partition")
+        if part.getFlag(parted.PARTITION_BOOT):
+            logging.debug("boot flag is enabled on %s" % device)
+            return
+        else:
+            logging.debug("boot flag is NOT enabled on %s" % device)
+            raise VerifyException(
+                "Device %s does not have the boot flag set. "
+                "Please enable it to be able to boot." % device
+            )
+    except ImportError:
+        raise VerifyException(
+            "Could not import parted to verify boot flag on %s, please make sure python3-parted is installed."
+            % device
+        )
 
 
 def mkfs_fat16(device):
 
 
 def mkfs_fat16(device):
@@ -304,282 +551,6 @@ def mkfs_fat16(device):
         raise CriticalException("error executing mkfs.vfat")
 
 
         raise CriticalException("error executing mkfs.vfat")
 
 
-def generate_main_grub2_config(grml_flavour, bootoptions):
-    """Generate grub2 configuration for use via grub.cfg
-
-    @grml_flavour: name of grml flavour the configuration should be generated for
-    @bootoptions: additional bootoptions that should be used by default"""
-
-    local_datestamp = DATESTAMP
-
-    return("""\
-## main grub2 configuration - generated by grml2usb [main config generated at: %(local_datestamp)s]
-set default=0
-set timeout=10
-
-insmod fat
-
-if loadfont /boot/grub/ascii.pf2 ; then
-   insmod png
-   set gfxmode=640x480
-   insmod gfxterm
-   insmod vbe
-   if terminal_output gfxterm ; then true ; else
-    # For backward compatibility with versions of terminal.mod that don't
-    # understand terminal_output
-    terminal gfxterm
-   fi
-fi
-
-if background_image /boot/grub/grml.png ; then
-  set color_normal=black/black
-  set color_highlight=red/black
-else
-  set menu_color_normal=white/black
-  set menu_color_highlight=black/yellow
-fi
-
-menuentry "%(grml_flavour)s (default)" {
-    set gfxpayload=1024x768x16,1024x768
-    linux   /boot/release/%(flavour_filename)s/linux26 apm=power-off quiet boot=live nomce live-media-path=/live/%(grml_flavour)s/ bootid=%(uid)s %(bootoptions)s
-    initrd  /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-menuentry "Memory test (memtest86+)" {
-    linux16   /boot/addons/memtest
-}
-
-menuentry "Boot Grub (all in one image)" {
-    linux   /boot/addons/memdisk
-    initrd  /boot/addons/allinone.img
-}
-
-menuentry "Boot FreeDOS" {
-    linux   /boot/addons/memdisk
-    initrd  /boot/addons/balder10.imz
-}
-
-if [ ${iso_path} ] ; then
-    # assume loopback.cfg boot
-    if [ -e /boot/addons/bsd4grml/loopback.0 ] ; then
-        # bsd4grml 20100815 and later
-        menuentry "Boot MirOS bsd4grml" {
-            multiboot /boot/addons/bsd4grml/ldbsd.com
-            module /boot/addons/bsd4grml/bsd.rd bsd
-            module /boot/addons/bsd4grml/loopback.0 boot.cfg
-            module /boot/addons/bsd4grml/loopback.1 boot.1
-            module /boot/addons/bsd4grml/loopback.2 boot.2
-            module /boot/addons/bsd4grml/loopback.3 boot.3
-            module /boot/addons/bsd4grml/loopback.4 boot.4
-            module /boot/addons/bsd4grml/loopback.5 boot.5
-            module /boot/addons/bsd4grml/loopback.6 boot.6
-        }
-    else
-        # old bsd4grml
-        menuentry "Boot MirOS bsd4grml" {
-            multiboot /boot/addons/bsd4grml/ldbsd.com
-            module /boot/addons/bsd4grml/bsd.rd bsd.rd
-            module /boot/addons/bsd4grml/boot.cfg boot.cfg
-            module /boot/addons/bsd4grml/boot.1 boot.1
-            module /boot/addons/bsd4grml/boot.2 boot.2
-            module /boot/addons/bsd4grml/boot.3 boot.3
-            module /boot/addons/bsd4grml/boot.4 boot.4
-            module /boot/addons/bsd4grml/boot.5 boot.5
-        }
-    fi
-else
-    # assume grub.cfg boot
-    menuentry "Boot MirOS bsd4grml" {
-        multiboot /boot/addons/bsd4grml/ldbsd.com
-        module /boot/addons/bsd4grml/bsd.rd bsd.rd
-        module /boot/addons/bsd4grml/boot.cfg boot.cfg
-        module /boot/addons/bsd4grml/boot.1 boot.1
-        module /boot/addons/bsd4grml/boot.2 boot.2
-        module /boot/addons/bsd4grml/boot.3 boot.3
-        module /boot/addons/bsd4grml/boot.4 boot.4
-        module /boot/addons/bsd4grml/boot.5 boot.5
-        module /boot/addons/bsd4grml/boot.6 boot.6
-    }
-fi
-
-menuentry "Boot OS of first partition on first disk" {
-    set root=(hd0,1)
-    chainloader +1
-}
-
-""" % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp,
-       'flavour_filename': grml_flavour.replace('-', ''),
-       'uid': UUID, 'bootoptions': bootoptions } )
-
-
-def generate_flavour_specific_grub2_config(grml_flavour, bootoptions):
-    """Generate grub2 configuration for use via grub.cfg
-
-    @grml_flavour: name of grml flavour the configuration should be generated for
-    @bootoptions: additional bootoptions that should be used by default"""
-
-    local_datestamp = DATESTAMP
-
-    return("""\
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s            - boot in default mode" {
-    set gfxpayload=1024x768x16,1024x768
-    linux  /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ bootid=%(uid)s %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s-persistent - enable persistency feature" {
-    set gfxpayload=1024x768x16,1024x768
-    linux  /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet persistent live-media-path=/live/%(grml_flavour)s/ bootid=%(uid)s %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s2ram        - copy compressed grml file to RAM" {
-    set gfxpayload=1024x768x16,1024x768
-    linux  /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ toram=%(grml_flavour)s.squashfs bootid=%(uid)s %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s-debug      - enable debugging options" {
-    set gfxpayload=1024x768x16,1024x768
-    linux /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ debug bootid=%(uid)s initcall_debug %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s-x          - start X Window System" {
-    set gfxpayload=1024x768x16,1024x768
-    linux  /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ startx=wm-ng bootid=%(uid)s %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s-nofb       - disable framebuffer" {
-    linux  /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ vga=normal video=ofonly bootid=%(uid)s %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s-failsafe   - disable hardware detection" {
-    linux /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ vga=normal noautoconfig atapicd noapic noacpi acpi=off nomodules nofirewire noudev nousb nohotplug noapm nopcmcia nosmp maxcpus=0 noscsi noagp nodma ide=nodma noswap nofstab nosound nogpm nosyslog nodhcp nocpu nodisc nomodem xmodule=vesa noraid nolvm noresume selinux=0 edd=off pci=nomsi bootid=%(uid)s %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s-forensic   - do not touch harddisks during hw recognition" {
-    set gfxpayload=1024x768x16,1024x768
-    linux /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ nofstab noraid nolvm noautoconfig noswap raid=noautodetect forensic readonly bootid=%(uid)s %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-""" % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp,
-       'flavour_filename': grml_flavour.replace('-', ''),
-       'uid': UUID, 'bootoptions': bootoptions } )
-
-
-def generate_flavour_specific_grub1_config(grml_flavour, install_partition, bootoptions):
-    """Generate grub1 configuration for use via menu.lst
-
-    @grml_flavour: name of grml flavour the configuration should be generated for
-    @install_partition: partition number for use in (hd0,X)
-    @bootoptions: additional bootoptions that should be used by default"""
-
-    local_datestamp = DATESTAMP
-
-    return("""\
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce vga=791 quiet live-media-path=/live/%(grml_flavour)s/ bootid=%(uid)s %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s-persistent
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce vga=791 quiet persistent live-media-path=/live/%(grml_flavour)s/ bootid=%(uid)s %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s2ram
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce vga=791 quiet live-media-path=/live/%(grml_flavour)s/ toram=%(grml_flavour)s.squashfs bootid=%(uid)s %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s-debug
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce vga=791 quiet live-media-path=/live/%(grml_flavour)s/ debug initcall_debug bootid=%(uid)s %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s-x
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce vga=791 quiet live-media-path=/live/%(grml_flavour)s/ startx=wm-ng bootid=%(uid)s %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s-nofb
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ vga=normal video=ofonly bootid=%(uid)s %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s-failsafe
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ vga=normal noautoconfig atapicd noapic noacpi acpi=off nomodules nofirewire noudev nousb nohotplug noapm nopcmcia nosmp maxcpus=0 noscsi noagp nodma ide=nodma noswap nofstab nosound nogpm nosyslog nodhcp nocpu nodisc nomodem xmodule=vesa noraid nolvm noresume selinux=0 edd=off pci=nomsi bootid=%(uid)s %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s-forensic
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce vga=791 quiet live-media-path=/live/%(grml_flavour)s/ nofstab noraid nolvm noautoconfig noswap raid=noautodetect forensic readonly bootid=%(uid)s %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s-serial
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ vga=normal video=vesafb:off console=tty1 console=ttyS0,9600n8 bootid=%(uid)s %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-""" % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp,
-       'flavour_filename': grml_flavour.replace('-', ''), 'uid': UUID,
-       'bootoptions': bootoptions, 'install_partition': install_partition } )
-
-
-def generate_main_grub1_config(grml_flavour, install_partition, bootoptions):
-    """Generate grub1 configuration for use via menu.lst
-
-    @grml_flavour: name of grml flavour the configuration should be generated for"""
-
-    local_datestamp = DATESTAMP
-
-    return("""\
-## main grub1 configuration - generated by grml2usb [main config generated at: %(local_datestamp)s]
-# misc options:
-timeout 30
-# color red/blue green/black
-splashimage=(hd0,%(install_partition)s)/boot/grub/splash.xpm.gz
-foreground  = 000000
-background  = FFCC33
-
-# define entries:
-title %(grml_flavour)s  - Default boot (using 1024x768 framebuffer)
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off vga=791 quiet boot=live nomce live-media-path=/live/%(grml_flavour)s/ bootid=%(uid)s %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-title Memory test (memtest86+)
-kernel (hd0,%(install_partition)s)/boot/addons/memtest
-
-title Grub - all in one image
-kernel (hd0,%(install_partition)s)/boot/addons/memdisk
-initrd (hd0,%(install_partition)s)/boot/addons/allinone.img
-
-title FreeDOS
-kernel (hd0,%(install_partition)s)/boot/addons/memdisk
-initrd (hd0,%(install_partition)s)/boot/addons/balder10.imz
-
-title MirOS BSD
-kernel (hd0,%(install_partition)s)/boot/addons/bsd4grml/ldbsd.com
-
-""" % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp,
-       'flavour_filename': grml_flavour.replace('-', ''), 'uid': UUID,
-       'bootoptions': bootoptions, 'install_partition': install_partition } )
-
-
 def generate_isolinux_splash(grml_flavour):
     """Generate bootsplash for isolinux/syslinux
 
 def generate_isolinux_splash(grml_flavour):
     """Generate bootsplash for isolinux/syslinux
 
@@ -587,12 +558,14 @@ def generate_isolinux_splash(grml_flavour):
 
     grml_name = grml_flavour
 
 
     grml_name = grml_flavour
 
-    return("""\
+    return """\
 \ f17\f\18/boot/syslinux/logo.16
 
 Some information and boot options available via keys F2 - F10. http://grml.org/
 %(grml_name)s
 \ f17\f\18/boot/syslinux/logo.16
 
 Some information and boot options available via keys F2 - F10. http://grml.org/
 %(grml_name)s
-""" % {'grml_name': grml_name} )
+""" % {
+        "grml_name": grml_name
+    }
 
 
 def generate_main_syslinux_config(*arg):
 
 
 def generate_main_syslinux_config(*arg):
@@ -602,7 +575,7 @@ def generate_main_syslinux_config(*arg):
     # pylint: disable-msg=W0613
     # remove warning about unused arg
 
     # pylint: disable-msg=W0613
     # remove warning about unused arg
 
-    return("""\
+    return """\
 label -
 menu label Default boot modes:
 menu disable
 label -
 menu label Default boot modes:
 menu disable
@@ -632,7 +605,7 @@ label help
 
 
 include hiddens.cfg
 
 
 include hiddens.cfg
-""")
+"""
 
 
 def generate_flavour_specific_syslinux_config(grml_flavour):
 
 
 def generate_flavour_specific_syslinux_config(grml_flavour):
@@ -640,8 +613,7 @@ def generate_flavour_specific_syslinux_config(grml_flavour):
 
     @grml_flavour: name of grml flavour the configuration should be generated for"""
 
 
     @grml_flavour: name of grml flavour the configuration should be generated for"""
 
-
-    return("""\
+    return """\
 menu begin grml %(grml_flavour)s
     menu title %(display_name)s
     label mainmenu
 menu begin grml %(grml_flavour)s
     menu title %(display_name)s
     label mainmenu
@@ -652,7 +624,10 @@ menu begin grml %(grml_flavour)s
     include %(grml_flavour)s_grml.cfg
     menu hide
 menu end
     include %(grml_flavour)s_grml.cfg
     menu hide
 menu end
-""" % {'grml_flavour': grml_flavour, 'display_name' : grml_flavour.replace('_', '-') } )
+""" % {
+        "grml_flavour": grml_flavour,
+        "display_name": get_flavour_filename(grml_flavour),
+    }
 
 
 def install_grub(device):
 
 
 def install_grub(device):
@@ -662,48 +637,66 @@ def install_grub(device):
     @device: partition where grub should be installed to"""
 
     if options.dryrun:
     @device: partition where grub should be installed to"""
 
     if options.dryrun:
-        logging.info("Would execute grub-install [--root-directory=mount_point] %s now.", device)
+        logging.info(
+            "Would execute grub-install [--root-directory=mount_point] %s now.", device
+        )
     else:
         device_mountpoint = tempfile.mkdtemp(prefix="grml2usb")
         register_tmpfile(device_mountpoint)
         try:
     else:
         device_mountpoint = tempfile.mkdtemp(prefix="grml2usb")
         register_tmpfile(device_mountpoint)
         try:
-            try:
-                mount(device, device_mountpoint, "")
-
-                # If using --grub-mbr then make sure we install grub in MBR instead of PBR
-                if options.grubmbr:
-                    logging.debug("Using option --grub-mbr ...")
-                    if device[-1:].isdigit():
-                        grub_device = re.match(r'(.*?)\d*$', device).group(1)
-                    else:
-                        grub_device = device
-                else:
-                    grub_device = device
-
-                logging.info("Installing grub as bootloader")
-                for opt in ["", "--force" ]:
-                    logging.debug("grub-install --recheck %s --no-floppy --root-directory=%s %s",
-                                  opt, device_mountpoint, grub_device)
-                    proc = subprocess.Popen(["grub-install", "--recheck", opt, "--no-floppy",
-                                             "--root-directory=%s" % device_mountpoint, grub_device],
-                                            stdout=file(os.devnull, "r+"))
-                    proc.wait()
-                    if proc.returncode == 0:
-                        break
-
-                if proc.returncode != 0:
-                    # raise Exception("error executing grub-install")
-                    logging.critical("Fatal: error executing grub-install "
-                                     + "(please check the grml2usb FAQ or drop the --grub option)")
-                    logging.critical("Note:  if using grub2 consider using "
-                                     + "the --grub-mbr option as grub considers PBR problematic.")
-                    cleanup()
-                    sys.exit(1)
-            except CriticalException, error:
-                logging.critical("Fatal: %s", error)
+            # If using --grub-mbr then make sure we install grub in MBR instead of PBR
+            if options.grubmbr:
+                logging.debug("Using option --grub-mbr ...")
+                grub_device, x = get_device_from_partition(device)
+            else:
+                grub_device = device
+
+            set_rw(device)
+            mount(device, device_mountpoint, "")
+
+            logging.info("Installing grub as bootloader")
+            for opt in ["--", "--force"]:
+                set_rw(device)
+                set_rw(grub_device)
+                logging.debug(
+                    "%s --recheck --no-floppy --target=i386-pc --root-directory=%s %s %s",
+                    GRUB_INSTALL,
+                    device_mountpoint,
+                    opt,
+                    grub_device,
+                )
+                proc = subprocess.Popen(
+                    [
+                        GRUB_INSTALL,
+                        "--recheck",
+                        "--no-floppy",
+                        "--target=i386-pc",
+                        "--root-directory=%s" % device_mountpoint,
+                        opt,
+                        grub_device,
+                    ],
+                    stdout=open(os.devnull, "r+"),
+                )
+                proc.wait()
+                if proc.returncode == 0:
+                    break
+
+            if proc.returncode != 0:
+                # raise Exception("error executing grub-install")
+                logging.critical(
+                    "Fatal: error executing grub-install "
+                    "(please check the grml2usb FAQ or drop the --grub option)"
+                )
+                logging.critical(
+                    "Note:  if using grub2 consider using "
+                    "the --grub-mbr option as grub considers PBR problematic."
+                )
                 cleanup()
                 sys.exit(1)
                 cleanup()
                 sys.exit(1)
-
+        except CriticalException as error:
+            logging.critical("Fatal: %s", error)
+            cleanup()
+            sys.exit(1)
         finally:
             unmount(device_mountpoint, "")
             os.rmdir(device_mountpoint)
         finally:
             unmount(device_mountpoint, "")
             os.rmdir(device_mountpoint)
@@ -719,13 +712,17 @@ def install_syslinux(device):
         logging.info("Would install syslinux as bootloader on %s", device)
         return 0
 
         logging.info("Would install syslinux as bootloader on %s", device)
         return 0
 
+    set_rw(device)
+
     # syslinux -d boot/isolinux /dev/sdb1
     logging.info("Installing syslinux as bootloader")
     logging.debug("syslinux -d boot/syslinux %s", device)
     proc = subprocess.Popen(["syslinux", "-d", "boot/syslinux", device])
     proc.wait()
     if proc.returncode != 0:
     # syslinux -d boot/isolinux /dev/sdb1
     logging.info("Installing syslinux as bootloader")
     logging.debug("syslinux -d boot/syslinux %s", device)
     proc = subprocess.Popen(["syslinux", "-d", "boot/syslinux", device])
     proc.wait()
     if proc.returncode != 0:
-        raise CriticalException("Error executing syslinux (either try --fat16 or use grub?)")
+        raise CriticalException(
+            "Error executing syslinux (either try --fat16 or use grub?)"
+        )
 
 
 def install_bootloader(device):
 
 
 def install_bootloader(device):
@@ -737,81 +734,34 @@ def install_bootloader(device):
     if options.grub:
         try:
             install_grub(device)
     if options.grub:
         try:
             install_grub(device)
-        except CriticalException, error:
+        except CriticalException as error:
             logging.critical("Fatal: %s", error)
             cleanup()
             sys.exit(1)
     else:
         try:
             install_syslinux(device)
             logging.critical("Fatal: %s", error)
             cleanup()
             sys.exit(1)
     else:
         try:
             install_syslinux(device)
-        except CriticalException, error:
+        except CriticalException as error:
             logging.critical("Fatal: %s", error)
             cleanup()
             sys.exit(1)
 
 
             logging.critical("Fatal: %s", error)
             cleanup()
             sys.exit(1)
 
 
-def execute_lilo(lilo, device):
-    """execute lilo for activating the partitions in the MBR
-
-    @lilo: path of lilo executable
-    @device: device where lilo should be executed on"""
-
-    # to support -A for extended partitions:
-    logging.info("Activating partitions in MBR via lilo")
-    logging.debug("%s -S /dev/null -M %s ext", lilo, device)
-    proc = subprocess.Popen([lilo, "-S", "/dev/null", "-M", device, "ext"])
-    proc.wait()
-    if proc.returncode != 0:
-        raise Exception("error executing lilo")
-
-    # activate partition:
-    logging.debug("%s -S /dev/null -A %s 1", lilo, device)
-    proc = subprocess.Popen([lilo, "-S", "/dev/null", "-A", device, "1"])
-    proc.wait()
-    if proc.returncode != 0:
-        raise Exception("error executing lilo")
-
-
-def install_syslinux_mbr(device):
-    """install syslinux's master boot record (MBR) on the specified device
-
-    @device: device where MBR of syslinux should be installed to"""
-
-    # make sure we have syslinux available
-    if not which("syslinux") and not options.copyonly:
-        raise Exception("syslinux not available (either install it or consider using the --grub option)")
-
-    # lilo's mbr is broken, use the one from syslinux instead:
-    if not os.path.isfile("/usr/lib/syslinux/mbr.bin"):
-        raise Exception("/usr/lib/syslinux/mbr.bin can not be read")
-
-    logging.info("Installing syslinux MBR")
-    logging.debug("cat /usr/lib/syslinux/mbr.bin > %s", device)
-    try:
-        retcode = subprocess.call("cat /usr/lib/syslinux/mbr.bin > "+ device, shell=True)
-        if retcode < 0:
-            logging.critical("Error copying MBR to device (%s)", retcode)
-    except OSError, error:
-        logging.critical("Execution failed: %s", error)
-
-
-def install_mir_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
+def install_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
     """install 'mbr' master boot record (MBR) on a device
 
     Retrieve the partition table from "device", install an MBR from the
     "mbrtemplate" file, set the "partition" (0..3) active, and install the
     result back to "device".
 
     """install 'mbr' master boot record (MBR) on a device
 
     Retrieve the partition table from "device", install an MBR from the
     "mbrtemplate" file, set the "partition" (0..3) active, and install the
     result back to "device".
 
-    @mbrtemplate: default MBR file
+    @mbrtemplate: default MBR file (must be a valid MBR file of at least 440
+    (or 439 if ismirbsdmbr) bytes)
 
     @device: name of a file assumed to be a hard disc (or USB stick) image, or
     something like "/dev/sdb"
 
     @partition: must be a number between 0 and 3, inclusive
 
 
     @device: name of a file assumed to be a hard disc (or USB stick) image, or
     something like "/dev/sdb"
 
     @partition: must be a number between 0 and 3, inclusive
 
-    @mbrtemplate: must be a valid MBR file of at least 440 (or 439 if
-    ismirbsdmbr) bytes.
-
     @ismirbsdmbr: if true then ignore the active flag, set the mirbsdmbr
     specific flag to 0/1/2/3 and set the MBR's default value accordingly. If
     false then leave the mirbsdmbr specific flag set to FFh, set all
     @ismirbsdmbr: if true then ignore the active flag, set the mirbsdmbr
     specific flag to 0/1/2/3 and set the MBR's default value accordingly. If
     false then leave the mirbsdmbr specific flag set to FFh, set all
@@ -822,11 +772,16 @@ def install_mir_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
     logging.info("Installing default MBR")
 
     if not os.path.isfile(mbrtemplate):
     logging.info("Installing default MBR")
 
     if not os.path.isfile(mbrtemplate):
-        logging.critical("Error: %s can not be read.", mbrtemplate)
-        raise CriticalException("Error installing MBR (either try --syslinux-mbr or install missing file?)")
+        logging.error(
+            "Error installing MBR (either try --syslinux-mbr or "
+            'install missing file "%s"?)',
+            mbrtemplate,
+        )
+        raise CriticalException("%s can not be read." % mbrtemplate)
 
 
-    if (partition < 0) or (partition > 3):
-        raise ValueError("partition must be between 0 and 3")
+    if partition is not None and ((partition < 0) or (partition > 3)):
+        logging.warn("Cannot activate partition %d", partition)
+        partition = None
 
     if ismirbsdmbr:
         nmbrbytes = 439
 
     if ismirbsdmbr:
         nmbrbytes = 439
@@ -836,16 +791,31 @@ def install_mir_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
     tmpf = tempfile.NamedTemporaryFile()
 
     logging.debug("executing: dd if='%s' of='%s' bs=512 count=1", device, tmpf.name)
     tmpf = tempfile.NamedTemporaryFile()
 
     logging.debug("executing: dd if='%s' of='%s' bs=512 count=1", device, tmpf.name)
-    proc = subprocess.Popen(["dd", "if=%s" % device, "of=%s" % tmpf.name, "bs=512", "count=1"],
-                            stderr=file(os.devnull, "r+"))
+    proc = subprocess.Popen(
+        ["dd", "if=%s" % device, "of=%s" % tmpf.name, "bs=512", "count=1"],
+        stderr=open(os.devnull, "r+"),
+    )
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (first run)")
 
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (first run)")
 
-    logging.debug("executing: dd if=%s of=%s bs=%s count=1 conv=notrunc", mbrtemplate,
-                  tmpf.name, nmbrbytes)
-    proc = subprocess.Popen(["dd", "if=%s" % mbrtemplate, "of=%s" % tmpf.name, "bs=%s" % nmbrbytes,
-                             "count=1", "conv=notrunc"], stderr=file(os.devnull, "r+"))
+    logging.debug(
+        "executing: dd if=%s of=%s bs=%s count=1 conv=notrunc",
+        mbrtemplate,
+        tmpf.name,
+        nmbrbytes,
+    )
+    proc = subprocess.Popen(
+        [
+            "dd",
+            "if=%s" % mbrtemplate,
+            "of=%s" % tmpf.name,
+            "bs=%s" % nmbrbytes,
+            "count=1",
+            "conv=notrunc",
+        ],
+        stderr=open(os.devnull, "r+"),
+    )
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (second run)")
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (second run)")
@@ -854,63 +824,70 @@ def install_mir_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
     if len(mbrcode) < 512:
         raise EOFError("MBR size (%d) < 512" % len(mbrcode))
 
     if len(mbrcode) < 512:
         raise EOFError("MBR size (%d) < 512" % len(mbrcode))
 
-    if ismirbsdmbr:
-        mbrcode = mbrcode[0:439] + chr(partition) + \
-                mbrcode[440:510] + "\x55\xAA"
-    else:
-        actives = ["\x00", "\x00", "\x00", "\x00"]
-        actives[partition] = "\x80"
-        mbrcode = mbrcode[0:446] + actives[0] + \
-                mbrcode[447:462] + actives[1] + \
-                mbrcode[463:478] + actives[2] + \
-                mbrcode[479:494] + actives[3] + \
-                mbrcode[495:510] + "\x55\xAA"
+    if partition is not None:
+        if ismirbsdmbr:
+            mbrcode = (
+                mbrcode[0:439]
+                + chr(partition).encode("latin-1")
+                + mbrcode[440:510]
+                + b"\x55\xAA"
+            )
+        else:
+            actives = [b"\x00", b"\x00", b"\x00", b"\x00"]
+            actives[partition] = b"\x80"
+            mbrcode = (
+                mbrcode[0:446]
+                + actives[0]
+                + mbrcode[447:462]
+                + actives[1]
+                + mbrcode[463:478]
+                + actives[2]
+                + mbrcode[479:494]
+                + actives[3]
+                + mbrcode[495:510]
+                + b"\x55\xAA"
+            )
 
     tmpf.file.seek(0)
     tmpf.file.truncate()
     tmpf.file.write(mbrcode)
     tmpf.file.close()
 
 
     tmpf.file.seek(0)
     tmpf.file.truncate()
     tmpf.file.write(mbrcode)
     tmpf.file.close()
 
-    logging.debug("executing: dd if='%s' of='%s' bs=512 count=1 conv=notrunc", tmpf.name, device)
-    proc = subprocess.Popen(["dd", "if=%s" % tmpf.name, "of=%s" % device, "bs=512", "count=1",
-                             "conv=notrunc"], stderr=file(os.devnull, "r+"))
+    set_rw(device)
+
+    logging.debug(
+        "executing: dd if='%s' of='%s' bs=512 count=1 conv=notrunc", tmpf.name, device
+    )
+    proc = subprocess.Popen(
+        [
+            "dd",
+            "if=%s" % tmpf.name,
+            "of=%s" % device,
+            "bs=512",
+            "count=1",
+            "conv=notrunc",
+        ],
+        stderr=open(os.devnull, "r+"),
+    )
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (third run)")
     del tmpf
 
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (third run)")
     del tmpf
 
+    # make sure we sync filesystems before returning
+    logging.debug("executing: sync")
+    proc = subprocess.Popen(["sync"])
+    proc.wait()
 
 
-def handle_syslinux_mbr(device):
-    """Install syslinux master boot record on given device
-
-    @device: device where MBR should be installed to"""
-
-    if not is_writeable(device):
-        raise IOError("device not writeable for user")
-
-    # try to use system's lilo
-    if which("lilo"):
-        lilo = which("lilo")
-    else:
-        # otherwise fall back to our static version
-        from platform import architecture
-        if architecture()[0] == '64bit':
-            lilo = GRML2USB_BASE + '/lilo/lilo.static.amd64'
-        else:
-            lilo = GRML2USB_BASE + '/lilo/lilo.static.i386'
-    # finally prefer a specified lilo executable
-    if options.lilobin:
-        lilo = options.lilobin
-
-    if not is_exe(lilo):
-        raise Exception("lilo executable can not be execute")
-
-    if options.dryrun:
-        logging.info("Would install MBR running lilo and using syslinux.")
-        return 0
+    logging.debug("Probing device via 'blockdev --rereadpt %s'", device)
+    proc = subprocess.Popen(["blockdev", "--rereadpt", device])
+    proc.wait()
+    if proc.returncode != 0:
+        raise Exception(
+            "Couldn't execute blockdev on '%s' (install util-linux?)", device
+        )
 
 
-    execute_lilo(lilo, device)
-    install_syslinux_mbr(device)
+    set_rw(device)
 
 
 def is_writeable(device):
 
 
 def is_writeable(device):
@@ -920,7 +897,6 @@ def is_writeable(device):
 
     if not device:
         return False
 
     if not device:
         return False
-        #raise Exception("no device for checking write permissions")
 
     if not os.path.exists(device):
         return False
 
     if not os.path.exists(device):
         return False
@@ -938,10 +914,14 @@ def mount(source, target, mount_options):
     # note: options.dryrun does not work here, as we have to
     # locate files and identify the grml flavour
 
     # note: options.dryrun does not work here, as we have to
     # locate files and identify the grml flavour
 
-    for x in file('/proc/mounts').readlines():
+    for x in open("/proc/mounts", "r").readlines():
         if x.startswith(source):
         if x.startswith(source):
-            raise CriticalException("Error executing mount: %s already mounted - " % source
-                                    + "please unmount before invoking grml2usb")
+            raise CriticalException(
+                (
+                    "Error executing mount: {0} already mounted - "
+                    "please unmount before invoking grml2usb"
+                ).format(source)
+            )
 
     if os.path.isdir(source):
         logging.debug("Source %s is not a device, therefore not mounting.", source)
 
     if os.path.isdir(source):
         logging.debug("Source %s is not a device, therefore not mounting.", source)
@@ -951,7 +931,9 @@ def mount(source, target, mount_options):
     proc = subprocess.Popen(["mount"] + list(mount_options) + [source, target])
     proc.wait()
     if proc.returncode != 0:
     proc = subprocess.Popen(["mount"] + list(mount_options) + [source, target])
     proc.wait()
     if proc.returncode != 0:
-        raise CriticalException("Error executing mount (no filesystem on the partition?)")
+        raise CriticalException(
+            "Error executing mount (no filesystem on the partition?)"
+        )
     else:
         logging.debug("register_mountpoint(%s)", target)
         register_mountpoint(target)
     else:
         logging.debug("register_mountpoint(%s)", target)
         register_mountpoint(target)
@@ -965,7 +947,7 @@ def unmount(target, unmount_options):
 
     # make sure we unmount only already mounted targets
     target_unmount = False
 
     # make sure we unmount only already mounted targets
     target_unmount = False
-    mounts = open('/proc/mounts').readlines()
+    mounts = open("/proc/mounts").readlines()
     mountstring = re.compile(".*%s.*" % re.escape(os.path.realpath(target)))
     for line in mounts:
         if re.match(mountstring, line):
     mountstring = re.compile(".*%s.*" % re.escape(os.path.realpath(target)))
     for line in mounts:
         if re.match(mountstring, line):
@@ -990,12 +972,12 @@ def check_for_usbdevice(device):
     @device: device name, like /dev/sda1 or /dev/sda
     """
 
     @device: device name, like /dev/sda1 or /dev/sda
     """
 
-    usbdevice = re.match(r'/dev/(.*?)\d*$', device).group(1)
+    usbdevice = re.match(r"/dev/(.*?)\d*$", device).group(1)
     # newer systems:
     # newer systems:
-    usbdev = os.path.realpath('/sys/class/block/' + usbdevice + '/removable')
+    usbdev = os.path.realpath("/sys/class/block/" + usbdevice + "/removable")
     if not os.path.isfile(usbdev):
         # Ubuntu with kernel 2.6.24 for example:
     if not os.path.isfile(usbdev):
         # Ubuntu with kernel 2.6.24 for example:
-        usbdev = os.path.realpath('/sys/block/' + usbdevice + '/removable')
+        usbdev = os.path.realpath("/sys/block/" + usbdevice + "/removable")
 
     if os.path.isfile(usbdev):
         is_usb = open(usbdev).readline()
 
     if os.path.isfile(usbdev):
         is_usb = open(usbdev).readline()
@@ -1011,21 +993,30 @@ def check_for_fat(partition):
     @partition: device name of partition"""
 
     if not os.access(partition, os.R_OK):
     @partition: device name of partition"""
 
     if not os.access(partition, os.R_OK):
-        raise CriticalException("Failed to read device %s"
-                " (wrong UID/permissions or device/directory not present?)" % partition)
+        raise CriticalException(
+            "Failed to read device %s"
+            " (wrong UID/permissions or device/directory not present?)" % partition
+        )
 
     try:
 
     try:
-        udev_info = subprocess.Popen(["/sbin/blkid", "-s", "TYPE", "-o", "value", partition],
-                                     stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-        filesystem = udev_info.communicate()[0].rstrip()
+        filesystem = (
+            subprocess.check_output(
+                ["/sbin/blkid", "-s", "TYPE", "-o", "value", partition]
+            )
+            .decode()
+            .rstrip()
+        )
 
         if filesystem != "vfat":
             raise CriticalException(
 
         if filesystem != "vfat":
             raise CriticalException(
-                    "Partition %s does not contain a FAT16 filesystem. "
-                    "(Use --fat16 or run mkfs.vfat %s)" % (partition, partition))
+                "Partition %s does not contain a FAT16 filesystem. "
+                "(Use --fat16 or run mkfs.vfat %s)" % (partition, partition)
+            )
 
     except OSError:
 
     except OSError:
-        raise CriticalException("Sorry, /sbin/blkid not available (install e2fsprogs?)")
+        raise CriticalException(
+            "Sorry, /sbin/blkid not available (install util-linux?)"
+        )
 
 
 def mkdir(directory):
 
 
 def mkdir(directory):
@@ -1065,7 +1056,7 @@ def write_uuid(target_file):
     @target_file: filename to write the uuid to
     """
 
     @target_file: filename to write the uuid to
     """
 
-    fileh = open(target_file, 'w')
+    fileh = open(target_file, "w")
     uid = str(uuid.uuid4())
     fileh.write(uid)
     fileh.close()
     uid = str(uuid.uuid4())
     fileh.write(uid)
     fileh.close()
@@ -1082,7 +1073,7 @@ def get_uuid(target):
     uuid_file_name = conf_target + "/bootid.txt"
     if os.path.isdir(conf_target):
         if os.path.isfile(uuid_file_name):
     uuid_file_name = conf_target + "/bootid.txt"
     if os.path.isdir(conf_target):
         if os.path.isfile(uuid_file_name):
-            uuid_file = open(uuid_file_name, 'r')
+            uuid_file = open(uuid_file_name, "r")
             uid = uuid_file.readline().strip()
             uuid_file.close()
             return uid
             uid = uuid_file.readline().strip()
             uuid_file.close()
             return uid
@@ -1093,6 +1084,13 @@ def get_uuid(target):
         return write_uuid(uuid_file_name)
 
 
         return write_uuid(uuid_file_name)
 
 
+def get_shortname(grml_flavour):
+    """Get shortname based from grml_flavour name. The rules applied are the same as in grml-live
+    @grml_flavour: flavour name which shold be translated to shortname"""
+
+    return re.sub(r"[,._-]", "", grml_flavour)
+
+
 def copy_system_files(grml_flavour, iso_mount, target):
     """copy grml's main files (like squashfs, kernel and initrd) to a given target
 
 def copy_system_files(grml_flavour, iso_mount, target):
     """copy grml's main files (like squashfs, kernel and initrd) to a given target
 
@@ -1100,48 +1098,44 @@ def copy_system_files(grml_flavour, iso_mount, target):
     @iso_mount: path where a grml ISO is mounted on
     @target: path where grml's main files should be copied to"""
 
     @iso_mount: path where a grml ISO is mounted on
     @target: path where grml's main files should be copied to"""
 
-    squashfs = search_file(grml_flavour + '.squashfs', iso_mount)
+    squashfs = search_file(grml_flavour + ".squashfs", iso_mount)
     if squashfs is None:
     if squashfs is None:
-        logging.critical("Fatal: squashfs file not found"
-        ", please check that your iso is not corrupt")
-        raise CriticalException("error locating squashfs file")
+        logging.error("error locating squashfs file")
+        raise CriticalException(
+            "squashfs file not found, please check that your iso is not corrupt"
+        )
     else:
     else:
-        squashfs_target = target + '/live/' + grml_flavour + '/'
+        squashfs_target = target + "/live/" + grml_flavour + "/"
         execute(mkdir, squashfs_target)
         execute(mkdir, squashfs_target)
-    exec_rsync(squashfs, squashfs_target + grml_flavour + '.squashfs')
+    exec_rsync(squashfs, squashfs_target + grml_flavour + ".squashfs")
 
     for prefix in grml_flavour + "/", "":
 
     for prefix in grml_flavour + "/", "":
-        filesystem_module = search_file(prefix + 'filesystem.module', iso_mount)
+        filesystem_module = search_file(prefix + "filesystem.module", iso_mount)
         if filesystem_module:
             break
     if filesystem_module is None:
         if filesystem_module:
             break
     if filesystem_module is None:
-        logging.critical("Fatal: filesystem.module not found")
-        raise CriticalException("error locating filesystem.module file")
+        logging.error("error locating filesystem.module file")
+        raise CriticalException("filesystem.module not found")
     else:
     else:
-        exec_rsync(filesystem_module, squashfs_target + 'filesystem.module')
-
+        exec_rsync(filesystem_module, squashfs_target + "filesystem.module")
 
 
-    release_path = 'boot/release/' + grml_flavour.replace('-', '')
-    release_target = target + "/" + release_path
-    execute(mkdir, release_target)
-
-    prefix = ""
-    if os.path.isdir(iso_mount + '/boot/release'):
-        prefix = release_path + '/'
-
-    kernel = search_file(prefix + 'linux26', iso_mount)
-    if kernel is None:
-        logging.critical("Fatal kernel not found")
-        raise CriticalException("error locating kernel file")
+    shortname = get_shortname(grml_flavour)
+    if os.path.isdir(iso_mount + "/boot/" + shortname):
+        exec_rsync(iso_mount + "/boot/" + shortname, target + "/boot")
     else:
     else:
-        exec_rsync(kernel, release_target + '/linux26')
+        kernel = search_file("vmlinuz", iso_mount)
+        if kernel is None:
+            # compat for releases < 2011.12
+            kernel = search_file("linux26", iso_mount)
 
 
-    initrd = search_file(prefix + 'initrd.gz', iso_mount)
-    if initrd is None:
-        logging.critical("Fatal: initrd not found")
-        raise CriticalException("error locating initrd file")
-    else:
-        exec_rsync(initrd, release_target + '/initrd.gz')
+        if kernel is None:
+            logging.error("error locating kernel file")
+            raise CriticalException("Kernel not found")
+
+        source = os.path.dirname(kernel) + "/"
+        dest = target + "/" + os.path.dirname(kernel).replace(iso_mount, "") + "/"
+        execute(mkdir, dest)
+        exec_rsync(source, dest)
 
 
 def update_grml_versions(iso_mount, target):
 
 
 def update_grml_versions(iso_mount, target):
@@ -1152,30 +1146,33 @@ def update_grml_versions(iso_mount, target):
     @iso_mount: string of the iso mount point
     @target: path of the target mount point
     """
     @iso_mount: string of the iso mount point
     @target: path of the target mount point
     """
-    grml_target = target + '/grml/'
-    target_grml_version_file = search_file('grml-version', grml_target)
+    grml_target = target + "/grml/"
+    target_grml_version_file = search_file("grml-version", grml_target)
     if target_grml_version_file:
     if target_grml_version_file:
-        iso_grml_version_file = search_file('grml-version', iso_mount)
+        iso_grml_version_file = search_file("grml-version", iso_mount)
         if not iso_grml_version_file:
         if not iso_grml_version_file:
-            logging.warn("Warning: %s could not be found - can not install it", iso_grml_version_file)
+            logging.warn(
+                "Warning: %s could not be found - can not install it",
+                iso_grml_version_file,
+            )
             return False
         try:
             # read the flavours from the iso image
             iso_versions = {}
             return False
         try:
             # read the flavours from the iso image
             iso_versions = {}
-            iso_file = open(iso_grml_version_file, 'r')
+            iso_file = open(iso_grml_version_file, "r")
             for line in iso_file:
                 iso_versions[get_flavour(line)] = line.strip()
 
             # update the existing flavours on the target
             for line in fileinput.input([target_grml_version_file], inplace=1):
                 flavour = get_flavour(line)
             for line in iso_file:
                 iso_versions[get_flavour(line)] = line.strip()
 
             # update the existing flavours on the target
             for line in fileinput.input([target_grml_version_file], inplace=1):
                 flavour = get_flavour(line)
-                if flavour in iso_versions.keys():
-                    print iso_versions.pop(flavour)
+                if flavour in list(iso_versions.keys()):
+                    print(iso_versions.pop(flavour))
                 else:
                 else:
-                    print line.strip()
+                    print(line.strip())
             fileinput.close()
 
             fileinput.close()
 
-            target_file = open(target_grml_version_file, 'a')
+            target_file = open(target_grml_version_file, "a")
             # add the new flavours from the current iso
             for flavour in iso_versions:
                 target_file.write("%s\n" % iso_versions[flavour])
             # add the new flavours from the current iso
             for flavour in iso_versions:
                 target_file.write("%s\n" % iso_versions[flavour])
@@ -1188,60 +1185,25 @@ def update_grml_versions(iso_mount, target):
     else:
         return False
 
     else:
         return False
 
-def copy_grml_files(iso_mount, target):
+
+def copy_grml_files(grml_flavour, iso_mount, target):
     """copy some minor grml files to a given target
 
     """copy some minor grml files to a given target
 
+    @grml_flavour: the current grml_flavour
     @iso_mount: path where a grml ISO is mounted on
     @target: path where grml's main files should be copied to"""
 
     @iso_mount: path where a grml ISO is mounted on
     @target: path where grml's main files should be copied to"""
 
-    grml_target = target + '/grml/'
+    grml_target = target + "/grml/"
     execute(mkdir, grml_target)
 
     execute(mkdir, grml_target)
 
-    copy_files = [ 'grml-cheatcodes.txt', 'LICENSE.txt', 'md5sums', 'README.txt' ]
-    # handle grml-version
-    if not update_grml_versions(iso_mount, target):
-        copy_files.append('grml-version')
-
-    for myfile in copy_files:
-        grml_file = search_file(myfile, iso_mount)
-        if grml_file is None:
-            logging.warn("Warning: myfile %s could not be found - can not install it", myfile)
-        else:
-            exec_rsync(grml_file, grml_target + myfile)
-
-    grml_web_target = grml_target + '/web/'
-    execute(mkdir, grml_web_target)
-
-    for myfile in 'index.html', 'style.css':
-        grml_file = search_file(myfile, iso_mount)
-        if grml_file is None:
-            logging.warn("Warning: myfile %s could not be found - can not install it")
-        else:
-            exec_rsync(grml_file, grml_web_target + myfile)
-
-    grml_webimg_target = grml_web_target + '/images/'
-    execute(mkdir, grml_webimg_target)
-
-    for myfile in 'button.png', 'favicon.png', 'linux.jpg', 'logo.png':
-        grml_file = search_file(myfile, iso_mount)
-        if grml_file is None:
-            logging.warn("Warning: myfile %s could not be found - can not install it")
-        else:
-            exec_rsync(grml_file, grml_webimg_target + myfile)
-
-
-def handle_addon_copy(filename, dst, iso_mount):
-    """handle copy of optional addons
-
-    @filename: filename of the addon
-    @dst: destination directory
-    @iso_mount: location of the iso mount
-    """
-    file_location = search_file(filename, iso_mount)
-    if file_location is None:
-        logging.warn("Warning: %s not found (that's fine if you don't need it)",  filename)
+    grml_prefixe = ["GRML", "grml"]
+    for prefix in grml_prefixe:
+        filename = "{0}/{1}/{2}".format(iso_mount, prefix, grml_flavour)
+        if os.path.exists(filename):
+            exec_rsync(filename, grml_target)
+            break
     else:
     else:
-        exec_rsync(file_location, dst)
+        logging.warn("Warning: could not find flavour directory for %s ", grml_flavour)
 
 
 def copy_addons(iso_mount, target):
 
 
 def copy_addons(iso_mount, target):
@@ -1250,37 +1212,47 @@ def copy_addons(iso_mount, target):
     @iso_mount: path where a grml ISO is mounted on
     @target: path where grml's main files should be copied to"""
 
     @iso_mount: path where a grml ISO is mounted on
     @target: path where grml's main files should be copied to"""
 
-    addons = target + '/boot/addons/'
+    addons = target + "/boot/addons/"
     execute(mkdir, addons)
 
     execute(mkdir, addons)
 
-    # grub all-in-one image
-    handle_addon_copy('allinone.img', addons, iso_mount)
+    for addon_file in glob.glob(iso_mount + "/boot/addons/*"):
+        filename = os.path.basename(addon_file)
+        src_file = iso_mount + "/boot/addons/" + os.path.basename(addon_file)
+        logging.debug("Copying addon file %s" % filename)
+        exec_rsync(src_file, addons)
 
 
-    # bsd imag
-    handle_addon_copy('bsd4grml', addons, iso_mount)
 
 
-    handle_addon_copy('balder10.imz', addons, iso_mount)
+def build_loopbackcfg(target):
+    """Generate GRUB's loopback.cfg based on existing config files.
 
 
-    # install hdt and pci.ids only when using syslinux (grub doesn't support it)
-    if options.syslinux:
-        # hdt (hardware detection tool) image
-        hdtimg = search_file('hdt.c32', iso_mount)
-        if hdtimg:
-            exec_rsync(hdtimg, addons + '/hdt.c32')
+    @target: target directory
+    """
+
+    grub_dir = "/boot/grub/"
+    mkdir(os.path.join(target, grub_dir))
 
 
-        # pci.ids file
-        picids = search_file('pci.ids', iso_mount)
-        if picids:
-            exec_rsync(picids, addons + '/pci.ids')
+    f = open(target + grub_dir + "loopback.cfg", "w")
 
 
-    # memdisk image
-    handle_addon_copy('memdisk', addons, iso_mount)
+    f.write("# grml2usb generated grub2 configuration file\n")
+    f.write("source /boot/grub/header.cfg\n")
 
 
-    # memtest86+ image
-    handle_addon_copy('memtest', addons, iso_mount)
+    for defaults in glob.glob(
+        target + os.path.sep + grub_dir + os.path.sep + "*_default.cfg"
+    ):
+        sourcefile = defaults.split(target + os.path.sep)[1]
+        logging.debug("Found source file" + sourcefile)
+        os.path.isfile(defaults) and f.write("source " + sourcefile + "\n")
 
 
-    # gpxe.lkrn
-    handle_addon_copy('gpxe.lkrn', addons, iso_mount)
+    for ops in glob.glob(
+        target + os.path.sep + grub_dir + os.path.sep + "*_options.cfg"
+    ):
+        sourcefile = ops.split(target + os.path.sep)[1]
+        logging.debug("Found source file" + sourcefile)
+        os.path.isfile(ops) and f.write("source " + sourcefile + "\n")
+
+    f.write("source /boot/grub/addons.cfg\n")
+    f.write("source /boot/grub/footer.cfg\n")
+    f.close()
 
 
 def glob_and_copy(filepattern, dst):
 
 
 def glob_and_copy(filepattern, dst):
@@ -1292,6 +1264,7 @@ def glob_and_copy(filepattern, dst):
     for name in glob.glob(filepattern):
         copy_if_exist(name, dst)
 
     for name in glob.glob(filepattern):
         copy_if_exist(name, dst)
 
+
 def search_and_copy(filename, search_path, dst):
     """Search for the specified filename at searchpath and copy it to dst
 
 def search_and_copy(filename, search_path, dst):
     """Search for the specified filename at searchpath and copy it to dst
 
@@ -1302,6 +1275,7 @@ def search_and_copy(filename, search_path, dst):
     file_location = search_file(filename, search_path)
     copy_if_exist(file_location, dst)
 
     file_location = search_file(filename, search_path)
     copy_if_exist(file_location, dst)
 
+
 def copy_if_exist(filename, dst):
     """Copy filename to dst if filename is set.
 
 def copy_if_exist(filename, dst):
     """Copy filename to dst if filename is set.
 
@@ -1311,6 +1285,7 @@ def copy_if_exist(filename, dst):
     if filename and (os.path.isfile(filename) or os.path.isdir(filename)):
         exec_rsync(filename, dst)
 
     if filename and (os.path.isfile(filename) or os.path.isdir(filename)):
         exec_rsync(filename, dst)
 
+
 def copy_bootloader_files(iso_mount, target, grml_flavour):
     """Copy grml's bootloader files to a given target
 
 def copy_bootloader_files(iso_mount, target, grml_flavour):
     """Copy grml's bootloader files to a given target
 
@@ -1319,79 +1294,88 @@ def copy_bootloader_files(iso_mount, target, grml_flavour):
     @grml_flavour: name of the current processed grml_flavour
     """
 
     @grml_flavour: name of the current processed grml_flavour
     """
 
-    syslinux_target = target + '/boot/syslinux/'
+    syslinux_target = target + "/boot/syslinux/"
     execute(mkdir, syslinux_target)
 
     execute(mkdir, syslinux_target)
 
-    grub_target = target + '/boot/grub/'
+    grub_target = target + "/boot/grub/"
     execute(mkdir, grub_target)
 
     execute(mkdir, grub_target)
 
+    logo = search_file("logo.16", iso_mount, required=True)
+    exec_rsync(logo, syslinux_target + "logo.16")
 
 
-    logo = search_file('logo.16', iso_mount)
-    exec_rsync(logo, syslinux_target + 'logo.16')
+    bootx64_efi = search_file("bootx64.efi", iso_mount)
+    if bootx64_efi:
+        mkdir(target + "/efi/boot/")
+        exec_rsync(bootx64_efi, target + "/efi/boot/bootx64.efi")
 
 
+    efi_img = search_file("efi.img", iso_mount)
+    if efi_img:
+        mkdir(target + "/boot/")
+        exec_rsync(efi_img, target + "/boot/efi.img")
+        handle_secure_boot(target, efi_img)
 
 
-    for ffile in ['f%d' % number for number in range(1, 11) ]:
-        search_and_copy(ffile, iso_mount, syslinux_target + ffile)
+    execute(mkdir, target + "/conf/")
+    glob_and_copy(iso_mount + "/conf/bootfile_*", target + "/conf/")
 
 
-    loopback_cfg = search_file("loopback.cfg", iso_mount)
-    if loopback_cfg:
-        directory = os.path.dirname(loopback_cfg)
-        directory = directory.replace(iso_mount, "")
-        mkdir(os.path.join(target, directory))
-        exec_rsync(loopback_cfg, target + os.path.sep + directory)
+    for ffile in ["f%d" % number for number in range(1, 11)]:
+        search_and_copy(ffile, iso_mount, syslinux_target + ffile)
 
     # avoid the "file is read only, overwrite anyway (y/n) ?" question
     # of mtools by syslinux ("mmove -D o -D O s:/ldlinux.sys $target_file")
 
     # avoid the "file is read only, overwrite anyway (y/n) ?" question
     # of mtools by syslinux ("mmove -D o -D O s:/ldlinux.sys $target_file")
-    if os.path.isfile(syslinux_target + 'ldlinux.sys'):
-        os.unlink(syslinux_target + 'ldlinux.sys')
+    if os.path.isfile(syslinux_target + "ldlinux.sys"):
+        os.unlink(syslinux_target + "ldlinux.sys")
 
     (source_dir, name) = get_defaults_file(iso_mount, grml_flavour, "default.cfg")
     (source_dir, defaults_file) = get_defaults_file(iso_mount, grml_flavour, "grml.cfg")
 
     if not source_dir:
 
     (source_dir, name) = get_defaults_file(iso_mount, grml_flavour, "default.cfg")
     (source_dir, defaults_file) = get_defaults_file(iso_mount, grml_flavour, "grml.cfg")
 
     if not source_dir:
-        logging.critical("Fatal: file default.cfg could not be found.")
-        logging.critical("Note:  this grml2usb version requires an ISO generated by grml-live >=0.9.24 ...")
-        logging.critical("       ... either use grml releases >=2009.10 or switch to an older grml2usb version.")
-        logging.critical("       Please visit http://grml.org/grml2usb/#grml2usb-compat for further information.")
-        raise
-
-    for expr in name, 'distri.cfg', \
-        defaults_file, 'grml.png', 'hd.cfg', 'isolinux.cfg', 'isolinux.bin', \
-        'isoprompt.cfg', 'options.cfg', \
-        'prompt.cfg', 'vesamenu.cfg', 'grml.png', '*.c32':
+        raise CriticalException(
+            "file default.cfg could not be found.\n"
+            "Note:  this grml2usb version requires an ISO generated by grml-live >=0.9.24 ...\n"
+            "       ... either use grml releases >=2009.10 or switch to an older grml2usb version."
+        )
+
+    if not os.path.exists(iso_mount + "/boot/grub/footer.cfg"):
+        logging.warning(
+            "Warning: Grml releases older than 2011.12 support only one flavour in grub."
+        )
+
+    for expr in (
+        name,
+        "distri.cfg",
+        defaults_file,
+        "grml.png",
+        "hd.cfg",
+        "isolinux.cfg",
+        "isolinux.bin",
+        "isoprompt.cfg",
+        "options.cfg",
+        "prompt.cfg",
+        "vesamenu.cfg",
+        "grml.png",
+        "*.c32",
+    ):
         glob_and_copy(iso_mount + source_dir + expr, syslinux_target)
 
         glob_and_copy(iso_mount + source_dir + expr, syslinux_target)
 
-    for filename in glob.glob1(syslinux_target, "*.c32"):
-        copy_if_exist(os.path.join(SYSLINUX_LIBS, filename), syslinux_target)
-
+    for modules_dir in options.syslinuxlibs + SYSLINUX_LIBS:
+        if os.path.isdir(modules_dir):
+            for filename in glob.glob1(syslinux_target, "*.c32"):
+                copy_if_exist(os.path.join(modules_dir, filename), syslinux_target)
+            break
 
     # copy the addons_*.cfg file to the new syslinux directory
 
     # copy the addons_*.cfg file to the new syslinux directory
-    glob_and_copy(iso_mount + source_dir + 'addon*.cfg', syslinux_target)
-
-    search_and_copy('hidden.cfg', iso_mount + source_dir, syslinux_target + "new_" + 'hidden.cfg')
-
-
-    grub_target = target + '/boot/grub/'
-    execute(mkdir, grub_target)
-
-    if not os.path.isfile(GRML2USB_BASE + "/grub/splash.xpm.gz"):
-        logging.critical("Error: %s/grub/splash.xpm.gz can not be read.", GRML2USB_BASE)
-        logging.critical("Please make sure you've installed the grml2usb (Debian) package!")
-        raise
-    else:
-        exec_rsync(GRML2USB_BASE + '/grub/splash.xpm.gz', grub_target + 'splash.xpm.gz')
+    glob_and_copy(iso_mount + source_dir + "addon*.cfg", syslinux_target)
 
 
-    # grml splash in grub
-    copy_if_exist(GRML2USB_BASE + "/grub/grml.png", grub_target + 'grml.png')
+    search_and_copy(
+        "hidden.cfg", iso_mount + source_dir, syslinux_target + "new_" + "hidden.cfg"
+    )
 
 
-    # font file for graphical bootsplash in grub
-    copy_if_exist('/usr/share/grub/ascii.pf2', grub_target + 'ascii.pf2')
+    # copy all grub files from ISO
+    glob_and_copy(iso_mount + "/boot/grub/*", grub_target)
 
 
-    # always copy grub content as it might be useful
+    # finally (after all GRUB files have been installed) build static loopback.cfg
+    build_loopbackcfg(target)
 
 
-    glob_and_copy(iso_mount + '/boot/grub/*.mod', grub_target)
-    glob_and_copy(iso_mount + '/boot/grub/*.img', grub_target)
-    glob_and_copy(iso_mount + '/boot/grub/stage*', grub_target)
 
 def install_iso_files(grml_flavour, iso_mount, device, target):
     """Copy files from ISO to given target
 
 def install_iso_files(grml_flavour, iso_mount, device, target):
     """Copy files from ISO to given target
@@ -1409,13 +1393,13 @@ def install_iso_files(grml_flavour, iso_mount, device, target):
         logging.info("Copying files. This might take a while....")
         try:
             copy_system_files(grml_flavour, iso_mount, target)
         logging.info("Copying files. This might take a while....")
         try:
             copy_system_files(grml_flavour, iso_mount, target)
-            copy_grml_files(iso_mount, target)
-        except CriticalException, error:
+            copy_grml_files(grml_flavour, iso_mount, target)
+        except CriticalException as error:
             logging.critical("Execution failed: %s", error)
             sys.exit(1)
 
     if not options.skipaddons:
             logging.critical("Execution failed: %s", error)
             sys.exit(1)
 
     if not options.skipaddons:
-        if not search_file('addons', iso_mount):
+        if not search_file("addons", iso_mount):
             logging.info("Could not find addons, therefore not installing.")
         else:
             copy_addons(iso_mount, target)
             logging.info("Could not find addons, therefore not installing.")
         else:
             copy_addons(iso_mount, target)
@@ -1427,14 +1411,27 @@ def install_iso_files(grml_flavour, iso_mount, device, target):
             handle_bootloader_config(grml_flavour, device, target)
 
     # make sure we sync filesystems before returning
             handle_bootloader_config(grml_flavour, device, target)
 
     # make sure we sync filesystems before returning
+    logging.info("Synching data (this might take a while)")
     proc = subprocess.Popen(["sync"])
     proc.wait()
 
 
     proc = subprocess.Popen(["sync"])
     proc.wait()
 
 
+def get_device_from_partition(partition):
+    device = partition
+    partition_number = None
+    if partition[-1].isdigit() and not RE_LOOP_DEVICE.match(partition):
+        m = RE_P_PARTITION.match(partition) or RE_PARTITION.match(partition)
+        if m:
+            device = m.group(1)
+            partition_number = int(m.group(2)) - 1
+    return (device, partition_number)
+
+
 def get_flavour(flavour_str):
     """Returns the flavour of a grml version string
     """
 def get_flavour(flavour_str):
     """Returns the flavour of a grml version string
     """
-    return re.match(r'[\w-]*', flavour_str).group()
+    return re.match(r"[\w-]*", flavour_str).group()
+
 
 def identify_grml_flavour(mountpath):
     """Get name of grml flavour
 
 def identify_grml_flavour(mountpath):
     """Get name of grml flavour
@@ -1442,151 +1439,37 @@ def identify_grml_flavour(mountpath):
     @mountpath: path where the grml ISO is mounted to
     @return: name of grml-flavour"""
 
     @mountpath: path where the grml ISO is mounted to
     @return: name of grml-flavour"""
 
-    version_file = search_file('grml-version', mountpath)
-
-    if version_file == "":
-        logging.critical("Error: could not find grml-version file.")
-        raise
+    version_files = search_file("grml-version", mountpath, lst_return=True)
+
+    if not version_files:
+        if mountpath.startswith("/run/live/medium"):
+            logging.critical("Error: could not find grml-version file.")
+            logging.critical(
+                "Looks like your system is running from RAM but required files are not available."
+            )
+            logging.critical(
+                "Please either boot without toram=... or use boot option toram instead of toram=..."
+            )
+        else:
+            logging.critical("Error: could not find grml-version file.")
+        cleanup()
+        sys.exit(1)
 
     flavours = []
 
     flavours = []
-    tmpfile = None
-    try:
-        tmpfile = open(version_file, 'r')
-        for line in tmpfile.readlines():
-            flavours.append(get_flavour(line))
-    except TypeError, e:
-        raise
-    except Exception, e:
-        logging.critical("Unexpected error: %s", e)
-        raise
-    finally:
-        if tmpfile:
-            tmpfile.close()
+    logging.debug("version_files = %s", version_files)
+    for version_file in version_files:
+        tmpfile = None
+        try:
+            tmpfile = open(version_file, "r")
+            for line in tmpfile.readlines():
+                flavours.append(get_flavour(line))
+        finally:
+            if tmpfile:
+                tmpfile.close()
 
     return flavours
 
 
 
     return flavours
 
 
-def modify_grub_config(filename):
-    """Adjust bootoptions for a grub file
-
-    @filename: filename to modify
-    """
-    if options.removeoption:
-        regexe = []
-        for regex in options.removeoption:
-            regexe.append(re.compile(r'%s' % regex))
-
-        option_re = re.compile(r'(.*/boot/release/.*linux26.*)')
-
-        for line in fileinput.input(filename, inplace=1):
-            if regexe and option_re.search(line):
-                for regex in regexe:
-                    line = regex.sub(' ', line)
-
-            sys.stdout.write(line)
-
-        fileinput.close()
-
-def handle_grub1_config(grml_flavour, install_partition, grub_target, bootopt):
-    """Main handler for generating grub1 configuration
-
-    @grml_flavour: name of grml flavour the configuration should be generated for
-    @install_partition: partition number for use in (hd0,X)
-    @grub_target: path of grub's configuration files
-    @bootoptions: additional bootoptions that should be used by default"""
-
-    # grub1 config
-    grub1_cfg = grub_target + 'menu.lst'
-    logging.debug("Creating grub1 configuration file (menu.lst)")
-
-    # install main configuration only *once*, no matter how many ISOs we have:
-    if os.path.isfile(grub1_cfg):
-        string = open(grub1_cfg).readline()
-        main_identifier = re.compile(".*main config generated at: %s.*" % re.escape(str(DATESTAMP)))
-        if not re.match(main_identifier, string):
-            grub1_config_file = open(grub1_cfg, 'w')
-            grub1_config_file.write(generate_main_grub1_config(grml_flavour, install_partition, bootopt))
-            grub1_config_file.close()
-    else:
-        grub1_config_file = open(grub1_cfg, 'w')
-        grub1_config_file.write(generate_main_grub1_config(grml_flavour, install_partition, bootopt))
-        grub1_config_file.close()
-
-    grub_flavour_config = True
-    if os.path.isfile(grub1_cfg):
-        string = open(grub1_cfg).readlines()
-        flavour = re.compile("grml2usb for %s: %s" % (re.escape(grml_flavour), re.escape(str(DATESTAMP))))
-        for line in string:
-            if flavour.match(line):
-                grub_flavour_config = False
-
-    if grub_flavour_config:
-        grub1_config_file = open(grub1_cfg, 'a')
-        grub1_config_file.write(generate_flavour_specific_grub1_config(grml_flavour, install_partition, bootopt))
-        grub1_config_file.close()
-
-    modify_grub_config(grub1_cfg)
-
-    # make sure grub.conf isn't a symlink but a plain file instead,
-    # otherwise it will break on FAT16 filesystems
-    # this works around grub-install of (at least) Fedora 10
-    if os.path.isfile(grub1_cfg):
-        grubconf = grub_target + 'grub.conf'
-        if not os.path.islink(grubconf):
-            import shutil
-            shutil.copyfile(grub1_cfg, grub_target + 'grub.conf')
-
-def handle_grub2_config(grml_flavour, grub_target, bootopt):
-    """Main handler for generating grub2 configuration
-
-    @grml_flavour: name of grml flavour the configuration should be generated for
-    @grub_target: path of grub's configuration files
-    @bootoptions: additional bootoptions that should be used by default"""
-
-    # grub2 config
-    grub2_cfg = grub_target + 'grub.cfg'
-    logging.debug("Creating grub2 configuration file (grub.cfg)")
-
-    global GRML_DEFAULT
-
-    # install main configuration only *once*, no matter how many ISOs we have:
-    grub_flavour_is_default = False
-    if os.path.isfile(grub2_cfg):
-        string = open(grub2_cfg).readline()
-        main_identifier = re.compile(".*main config generated at: %s.*" % re.escape(str(DATESTAMP)))
-        if not re.match(main_identifier, string):
-            grub2_config_file = open(grub2_cfg, 'w')
-            GRML_DEFAULT = grml_flavour
-            grub_flavour_is_default = True
-            grub2_config_file.write(generate_main_grub2_config(grml_flavour, bootopt))
-            grub2_config_file.close()
-    else:
-        grub2_config_file = open(grub2_cfg, 'w')
-        GRML_DEFAULT = grml_flavour
-        grub_flavour_is_default = True
-        grub2_config_file.write(generate_main_grub2_config(grml_flavour, bootopt))
-        grub2_config_file.close()
-
-    # install flavour specific configuration only *once* as well
-    grub_flavour_config = True
-    if os.path.isfile(grub2_cfg):
-        string = open(grub2_cfg).readlines()
-        flavour = re.compile("grml2usb for %s: %s" % (re.escape(grml_flavour), re.escape(str(DATESTAMP))))
-        for line in string:
-            if flavour.match(line):
-                grub_flavour_config = False
-
-    if grub_flavour_config:
-        grub2_config_file = open(grub2_cfg, 'a')
-        # display only if the grml flavour isn't the default
-        if not grub_flavour_is_default:
-            GRML_FLAVOURS.add(grml_flavour)
-        grub2_config_file.write(generate_flavour_specific_grub2_config(grml_flavour, bootopt))
-        grub2_config_file.close()
-
-    modify_grub_config(grub2_cfg)
-
-
 def get_bootoptions(grml_flavour):
     """Returns bootoptions for specific flavour
 
 def get_bootoptions(grml_flavour):
     """Returns bootoptions for specific flavour
 
@@ -1608,29 +1491,48 @@ def handle_grub_config(grml_flavour, device, target):
     @device: device/partition where grub should be installed to
     @target: path of grub's configuration files"""
 
     @device: device/partition where grub should be installed to
     @target: path of grub's configuration files"""
 
-    logging.debug("Generating grub configuration")
+    global UUID
 
 
-    grub_target = target + '/boot/grub/'
+    logging.debug("Updating grub configuration")
 
 
-    if os.path.isdir(device):
-        install_grub1_partition = None
-    else:
-        if device[-1:].isdigit():
-            install_grub1_partition = int(device[-1:]) - 1
-        else:
-            raise CriticalException("error validating partition schema (raw device?)")
+    grub_target = target + "/boot/grub/"
 
 
+    bootid_re = re.compile(r"bootid=[\w_-]+")
+    live_media_path_re = re.compile(r"live-media-path=[\w_/-]+")
 
     bootopt = get_bootoptions(grml_flavour)
 
 
     bootopt = get_bootoptions(grml_flavour)
 
-    # write menu.lst
-    handle_grub1_config(grml_flavour, install_grub1_partition, grub_target, bootopt)
-    # write grub.cfg
-    handle_grub2_config(grml_flavour, grub_target, bootopt)
+    remove_regexes = []
+    option_re = re.compile(r"(.*/boot/.*(linux26|vmlinuz).*)")
+
+    if options.removeoption:
+        for regex in options.removeoption:
+            remove_regexes.append(re.compile(regex))
+
+    shortname = get_shortname(grml_flavour)
+    for filename in glob.glob(grub_target + "*.cfg"):
+        for line in fileinput.input(filename, inplace=1):
+            line = line.rstrip("\r\n")
+            if option_re.search(line):
+                line = bootid_re.sub("", line)
+                if shortname in filename:
+                    line = live_media_path_re.sub("", line)
+                    line = line.rstrip() + " live-media-path=/live/%s/ " % (
+                        grml_flavour
+                    )
+                if bootopt.strip():
+                    line = line.replace(" {} ".format(bootopt.strip()), " ")
+                    if line.endswith(bootopt):
+                        line = line[: -len(bootopt)]
+                line = line.rstrip() + r" bootid=%s %s " % (UUID, bootopt)
+                for regex in remove_regexes:
+                    line = regex.sub(" ", line)
+            print(line)
+        fileinput.close()
 
 
 def initial_syslinux_config(target):
 
 
 def initial_syslinux_config(target):
-    """Generates intial syslinux configuration
+    """Generates initial syslinux configuration
 
     @target path of syslinux's configuration files"""
 
 
     @target path of syslinux's configuration files"""
 
@@ -1647,13 +1549,15 @@ def initial_syslinux_config(target):
     data.write("include hidden.cfg\n")
     data.close()
 
     data.write("include hidden.cfg\n")
     data.close()
 
+
 def add_entry_if_not_present(filename, entry):
     """Write entry into filename if entry is not already in the file
 
 def add_entry_if_not_present(filename, entry):
     """Write entry into filename if entry is not already in the file
 
-    @filanme: name of the file
+    @filename: name of the file
     @entry: data to write to the file
     """
     data = open(filename, "a+")
     @entry: data to write to the file
     """
     data = open(filename, "a+")
+    data.seek(0)
     for line in data:
         if line == entry:
             break
     for line in data:
         if line == entry:
             break
@@ -1662,12 +1566,14 @@ def add_entry_if_not_present(filename, entry):
 
     data.close()
 
 
     data.close()
 
+
 def get_flavour_filename(flavour):
     """Generate a iso9960 save filename out of the specified flavour
 
     @flavour: grml flavour
     """
 def get_flavour_filename(flavour):
     """Generate a iso9960 save filename out of the specified flavour
 
     @flavour: grml flavour
     """
-    return flavour.replace('-', '_')
+    return flavour.replace("-", "_")
+
 
 def adjust_syslinux_bootoptions(src, flavour):
     """Adjust existing bootoptions of specified syslinux config to
 
 def adjust_syslinux_bootoptions(src, flavour):
     """Adjust existing bootoptions of specified syslinux config to
@@ -1677,43 +1583,42 @@ def adjust_syslinux_bootoptions(src, flavour):
     @flavour: grml flavour
     """
 
     @flavour: grml flavour
     """
 
-    append_re = re.compile("^(\s*append.*/boot/release.*)$", re.I)
-    boot_re = re.compile("/boot/([a-zA-Z0-9_]+/)+([a-zA-Z0-9._]+)")
+    append_re = re.compile(r"^(\s*append.*/boot/.*)$", re.I)
     # flavour_re = re.compile("(label.*)(grml\w+)")
     # flavour_re = re.compile("(label.*)(grml\w+)")
-    default_re = re.compile("(default.cfg)")
-    bootid_re = re.compile("bootid=[\w_-]+")
-    live_media_path_re = re.compile("live-media-path=[\w_/-]+")
+    default_re = re.compile(r"(default.cfg)")
+    bootid_re = re.compile(r"bootid=[\w_-]+")
+    live_media_path_re = re.compile(r"live-media-path=[\w_/-]+")
 
     bootopt = get_bootoptions(flavour)
 
     regexe = []
     option_re = None
     if options.removeoption:
 
     bootopt = get_bootoptions(flavour)
 
     regexe = []
     option_re = None
     if options.removeoption:
-        option_re = re.compile(r'/boot/release/.*/initrd.gz')
+        option_re = re.compile(r"/boot/.*/(initrd.gz|initrd.img)")
 
         for regex in options.removeoption:
 
         for regex in options.removeoption:
-            regexe.append(re.compile(r'%s' % regex))
+            regexe.append(re.compile(r"%s" % regex))
 
     for line in fileinput.input(src, inplace=1):
 
     for line in fileinput.input(src, inplace=1):
-        line = boot_re.sub(r'/boot/release/%s/\2 ' % flavour.replace('-', ''), line)
         # line = flavour_re.sub(r'\1 %s-\2' % flavour, line)
         # line = flavour_re.sub(r'\1 %s-\2' % flavour, line)
-        line = default_re.sub(r'%s-\1' % flavour, line)
-        line = bootid_re.sub('', line)
-        line = live_media_path_re.sub('', line)
-        line = append_re.sub(r'\1 live-media-path=/live/%s/ ' % flavour, line)
-        line = append_re.sub(r'\1 boot=live %s ' % bootopt, line)
-        line = append_re.sub(r'\1 %s=%s ' % ("bootid", UUID), line)
+        line = default_re.sub(r"%s-\1" % flavour, line)
+        line = bootid_re.sub("", line)
+        line = live_media_path_re.sub("", line)
+        line = append_re.sub(r"\1 live-media-path=/live/%s/ " % flavour, line)
+        line = append_re.sub(r"\1 boot=live %s " % bootopt, line)
+        line = append_re.sub(r"\1 %s=%s " % ("bootid", UUID), line)
         if option_re and option_re.search(line):
             for regex in regexe:
         if option_re and option_re.search(line):
             for regex in regexe:
-                line = regex.sub(' ', line)
+                line = regex.sub(" ", line)
         sys.stdout.write(line)
     fileinput.close()
 
         sys.stdout.write(line)
     fileinput.close()
 
+
 def adjust_labels(src, replacement):
     """Adjust the specified labels in the syslinux config file src with
     specified replacement
     """
 def adjust_labels(src, replacement):
     """Adjust the specified labels in the syslinux config file src with
     specified replacement
     """
-    label_re = re.compile("^(\s*label\s*) ([a-zA-Z0-9_-]+)", re.I)
+    label_re = re.compile(r"^(\s*label\s*) ([a-zA-Z0-9_-]+)", re.I)
     for line in fileinput.input(src, inplace=1):
         line = label_re.sub(replacement, line)
         sys.stdout.write(line)
     for line in fileinput.input(src, inplace=1):
         line = label_re.sub(replacement, line)
         sys.stdout.write(line)
@@ -1737,14 +1642,15 @@ def add_syslinux_entry(filename, grml_flavour):
     data.write(generate_flavour_specific_syslinux_config(grml_flavour))
     data.close()
 
     data.write(generate_flavour_specific_syslinux_config(grml_flavour))
     data.close()
 
+
 def modify_filenames(grml_flavour, target, filenames):
 def modify_filenames(grml_flavour, target, filenames):
-    """Replace the standarf filenames with the new ones
+    """Replace the standard filenames with the new ones
 
     @grml_flavour: grml-flavour strin
     @target: directory where the files are located
     @filenames: list of filenames to alter
     """
 
     @grml_flavour: grml-flavour strin
     @target: directory where the files are located
     @filenames: list of filenames to alter
     """
-    grml_filename = grml_flavour.replace('-', '_')
+    grml_filename = get_flavour_filename(grml_flavour)
     for filename in filenames:
         old_filename = "%s/%s" % (target, filename)
         new_filename = "%s/%s_%s" % (target, grml_filename, filename)
     for filename in filenames:
         old_filename = "%s/%s" % (target, filename)
         new_filename = "%s/%s_%s" % (target, grml_filename, filename)
@@ -1756,7 +1662,7 @@ def remove_default_entry(filename):
 
     @filename: syslinux config file
     """
 
     @filename: syslinux config file
     """
-    default_re = re.compile("^(\s*menu\s*default\s*)$", re.I)
+    default_re = re.compile(r"^(\s*menu\s*default\s*)$", re.I)
     for line in fileinput.input(filename, inplace=1):
         if default_re.match(line):
             continue
     for line in fileinput.input(filename, inplace=1):
         if default_re.match(line):
             continue
@@ -1771,51 +1677,47 @@ def handle_syslinux_config(grml_flavour, target):
     @target: path of syslinux's configuration files"""
 
     logging.debug("Generating syslinux configuration")
     @target: path of syslinux's configuration files"""
 
     logging.debug("Generating syslinux configuration")
-    syslinux_target = target + '/boot/syslinux/'
+    syslinux_target = target + "/boot/syslinux/"
     # should be present via  copy_bootloader_files(), but make sure it exits:
     execute(mkdir, syslinux_target)
     # should be present via  copy_bootloader_files(), but make sure it exits:
     execute(mkdir, syslinux_target)
-    syslinux_cfg = syslinux_target + 'syslinux.cfg'
-
+    syslinux_cfg = syslinux_target + "syslinux.cfg"
 
     # install main configuration only *once*, no matter how many ISOs we have:
 
     # install main configuration only *once*, no matter how many ISOs we have:
-    syslinux_config_file = open(syslinux_cfg, 'w')
-    syslinux_config_file.write("TIMEOUT 300\n")
+    syslinux_config_file = open(syslinux_cfg, "w")
+    syslinux_config_file.write("timeout 300\n")
     syslinux_config_file.write("include vesamenu.cfg\n")
     syslinux_config_file.close()
 
     syslinux_config_file.write("include vesamenu.cfg\n")
     syslinux_config_file.close()
 
-    prompt_name = open(syslinux_target + 'promptname.cfg', 'w')
-    prompt_name.write('menu label S^yslinux prompt\n')
+    prompt_name = open(syslinux_target + "promptname.cfg", "w")
+    prompt_name.write("menu label S^yslinux prompt\n")
     prompt_name.close()
 
     initial_syslinux_config(syslinux_target)
     prompt_name.close()
 
     initial_syslinux_config(syslinux_target)
-    flavour_filename = grml_flavour.replace('-', '_')
+    flavour_filename = get_flavour_filename(grml_flavour)
 
 
-    if search_file('default.cfg', syslinux_target):
-        modify_filenames(grml_flavour, syslinux_target, ['grml.cfg', 'default.cfg'])
+    if search_file("default.cfg", syslinux_target):
+        modify_filenames(grml_flavour, syslinux_target, ["grml.cfg", "default.cfg"])
 
     filename = search_file("new_hidden.cfg", syslinux_target)
 
 
     filename = search_file("new_hidden.cfg", syslinux_target)
 
-
     # process hidden file
     if not search_file("hidden.cfg", syslinux_target):
         new_hidden = syslinux_target + "hidden.cfg"
         os.rename(filename, new_hidden)
         adjust_syslinux_bootoptions(new_hidden, grml_flavour)
     else:
     # process hidden file
     if not search_file("hidden.cfg", syslinux_target):
         new_hidden = syslinux_target + "hidden.cfg"
         os.rename(filename, new_hidden)
         adjust_syslinux_bootoptions(new_hidden, grml_flavour)
     else:
-        new_hidden_file =  "%s/%s_hidden.cfg" % (syslinux_target, flavour_filename)
+        new_hidden_file = "%s/%s_hidden.cfg" % (syslinux_target, flavour_filename)
         os.rename(filename, new_hidden_file)
         os.rename(filename, new_hidden_file)
-        adjust_labels(new_hidden_file, r'\1 %s-\2' % grml_flavour)
+        adjust_labels(new_hidden_file, r"\1 %s-\2" % grml_flavour)
         adjust_syslinux_bootoptions(new_hidden_file, grml_flavour)
         adjust_syslinux_bootoptions(new_hidden_file, grml_flavour)
-        entry = 'include %s_hidden.cfg\n' % flavour_filename
+        entry = "include %s_hidden.cfg\n" % flavour_filename
         add_entry_if_not_present("%s/hiddens.cfg" % syslinux_target, entry)
 
         add_entry_if_not_present("%s/hiddens.cfg" % syslinux_target, entry)
 
-
-
     new_default = "%s_default.cfg" % (flavour_filename)
     new_default = "%s_default.cfg" % (flavour_filename)
-    entry = 'include %s\n' % new_default
-    defaults_file = '%s/defaults.cfg' % syslinux_target
+    entry = "include %s\n" % new_default
+    defaults_file = "%s/defaults.cfg" % syslinux_target
     new_default_with_path = "%s/%s" % (syslinux_target, new_default)
     new_default_with_path = "%s/%s" % (syslinux_target, new_default)
-    new_grml_cfg = "%s/%s_grml.cfg" % ( syslinux_target, flavour_filename)
+    new_grml_cfg = "%s/%s_grml.cfg" % (syslinux_target, flavour_filename)
 
     if os.path.isfile(defaults_file):
 
 
     if os.path.isfile(defaults_file):
 
@@ -1823,8 +1725,8 @@ def handle_syslinux_config(grml_flavour, target):
         remove_default_entry(new_default_with_path)
 
         # adjust all labels for additional isos
         remove_default_entry(new_default_with_path)
 
         # adjust all labels for additional isos
-        adjust_labels(new_default_with_path, r'\1 %s' % grml_flavour)
-        adjust_labels(new_grml_cfg, r'\1 %s-\2' % grml_flavour)
+        adjust_labels(new_default_with_path, r"\1 %s" % grml_flavour)
+        adjust_labels(new_grml_cfg, r"\1 %s-\2" % grml_flavour)
 
     # always adjust bootoptions
     adjust_syslinux_bootoptions(new_default_with_path, grml_flavour)
 
     # always adjust bootoptions
     adjust_syslinux_bootoptions(new_default_with_path, grml_flavour)
@@ -1835,6 +1737,69 @@ def handle_syslinux_config(grml_flavour, target):
     add_syslinux_entry("%s/additional.cfg" % syslinux_target, flavour_filename)
 
 
     add_syslinux_entry("%s/additional.cfg" % syslinux_target, flavour_filename)
 
 
+def handle_secure_boot(target, efi_img):
+    """Provide secure boot support by extracting files from /boot/efi.img
+
+    @target: path where grml's main files should be copied to
+    @efi_img: path to the efi.img file that includes the files for secure boot
+    """
+
+    mkdir(target + "/efi/boot/")
+    efi_mountpoint = tempfile.mkdtemp(
+        prefix="grml2usb", dir=os.path.abspath(options.tmpdir)
+    )
+    logging.debug("efi_mountpoint = %s" % efi_mountpoint)
+    register_tmpfile(efi_mountpoint)
+
+    try:
+        logging.debug(
+            "mount(%s, %s, ['-o', 'ro', '-t', 'vfat']" % (efi_img, efi_mountpoint)
+        )
+        mount(efi_img, efi_mountpoint, ["-o", "ro", "-t", "vfat"])
+    except CriticalException as error:
+        logging.critical("Fatal: %s", error)
+        sys.exit(1)
+
+    grub_cfg = search_file("grub.cfg", efi_mountpoint + "/boot/grub/")
+    logging.debug("grub_cfg = %s" % grub_cfg)
+    if not grub_cfg:
+        logging.info(
+            "No /boot/grub/grub.cfg found inside EFI image, looks like Secure Boot support is missing."
+        )
+    else:
+        mkdir(target + "/boot/grub/x86_64-efi/")
+        logging.debug(
+            "exec_rsync(%s, %s + '/boot/grub/x86_64-efi/grub.cfg')" % (grub_cfg, target)
+        )
+        exec_rsync(grub_cfg, target + "/boot/grub/x86_64-efi/grub.cfg")
+
+        logging.debug(
+            "exec_rsync(%s + '/EFI/BOOT/grubx64.efi', %s + '/efi/boot/grubx64.efi')'"
+            % (efi_mountpoint, target)
+        )
+        exec_rsync(
+            efi_mountpoint + "/EFI/BOOT/grubx64.efi", target + "/efi/boot/grubx64.efi"
+        )
+
+        # NOTE - we're overwriting /efi/boot/bootx64.efi from copy_bootloader_files here
+        logging.debug(
+            "exec_rsync(%s + '/EFI/BOOT/bootx64.efi', %s + '/efi/boot/bootx64.efi')'"
+            % (efi_mountpoint, target)
+        )
+        exec_rsync(
+            efi_mountpoint + "/EFI/BOOT/bootx64.efi", target + "/efi/boot/bootx64.efi"
+        )
+
+    try:
+        unmount(efi_mountpoint, "")
+        logging.debug("Unmounted %s" % efi_mountpoint)
+        os.rmdir(efi_mountpoint)
+        logging.debug("Removed directory %s" % efi_mountpoint)
+    except Exception:
+        logging.critical("RuntimeError while umount %s" % efi_mountpoint)
+        sys.exit(1)
+
+
 def handle_bootloader_config(grml_flavour, device, target):
     """Main handler for generating bootloader's configuration
 
 def handle_bootloader_config(grml_flavour, device, target):
     """Main handler for generating bootloader's configuration
 
@@ -1849,7 +1814,7 @@ def handle_bootloader_config(grml_flavour, device, target):
     else:
         try:
             handle_syslinux_config(grml_flavour, target)
     else:
         try:
             handle_syslinux_config(grml_flavour, target)
-        except CriticalException, error:
+        except CriticalException as error:
             logging.critical("Fatal: %s", error)
             sys.exit(1)
 
             logging.critical("Fatal: %s", error)
             sys.exit(1)
 
@@ -1858,12 +1823,11 @@ def handle_bootloader_config(grml_flavour, device, target):
     else:
         try:
             handle_grub_config(grml_flavour, device, target)
     else:
         try:
             handle_grub_config(grml_flavour, device, target)
-        except CriticalException, error:
+        except CriticalException as error:
             logging.critical("Fatal: %s", error)
             sys.exit(1)
 
 
             logging.critical("Fatal: %s", error)
             sys.exit(1)
 
 
-
 def install(image, device):
     """Install a grml image to the specified device
 
 def install(image, device):
     """Install a grml image to the specified device
 
@@ -1873,15 +1837,27 @@ def install(image, device):
     iso_mountpoint = image
     remove_image_mountpoint = False
     if os.path.isdir(image):
     iso_mountpoint = image
     remove_image_mountpoint = False
     if os.path.isdir(image):
-        logging.info("Using %s as install base", image)
+        if options.force or os.path.exists(os.path.join(image, "live")):
+            logging.info("Using %s as install base", image)
+        else:
+            q = input(
+                "%s does not look like a Grml system. "
+                "Do you really want to use this image? y/N " % image
+            )
+            if q.lower() == "y":
+                logging.info("Using %s as install base", image)
+            else:
+                logging.info("Skipping install base %s", image)
     else:
         logging.info("Using ISO %s", image)
     else:
         logging.info("Using ISO %s", image)
-        iso_mountpoint = tempfile.mkdtemp(prefix="grml2usb")
+        iso_mountpoint = tempfile.mkdtemp(
+            prefix="grml2usb", dir=os.path.abspath(options.tmpdir)
+        )
         register_tmpfile(iso_mountpoint)
         remove_image_mountpoint = True
         try:
         register_tmpfile(iso_mountpoint)
         remove_image_mountpoint = True
         try:
-            mount(image, iso_mountpoint, ["-o", "loop", "-t", "iso9660"])
-        except CriticalException, error:
+            mount(image, iso_mountpoint, ["-o", "loop,ro", "-t", "iso9660"])
+        except CriticalException as error:
             logging.critical("Fatal: %s", error)
             sys.exit(1)
 
             logging.critical("Fatal: %s", error)
             sys.exit(1)
 
@@ -1891,49 +1867,45 @@ def install(image, device):
         if remove_image_mountpoint:
             try:
                 remove_mountpoint(iso_mountpoint)
         if remove_image_mountpoint:
             try:
                 remove_mountpoint(iso_mountpoint)
-            except CriticalException, error:
-                logging.critical("Fatal: %s", error)
+            except CriticalException:
                 cleanup()
                 cleanup()
-
+                raise
 
 
 def install_grml(mountpoint, device):
 
 
 def install_grml(mountpoint, device):
-    """Main logic for copying files of the currently running grml system.
+    """Main logic for copying files of the currently running Grml system.
 
 
-    @mountpoin: directory where currently running live system resides (usually /live/image)
+    @mountpoint: directory where currently running live system resides (usually /run/live/medium)
     @device: partition where the specified ISO should be installed to"""
 
     device_mountpoint = device
     if os.path.isdir(device):
     @device: partition where the specified ISO should be installed to"""
 
     device_mountpoint = device
     if os.path.isdir(device):
-        logging.info("Specified device is not a directory, therefore not mounting.")
+        logging.info("Specified device is a directory, therefore not mounting.")
         remove_device_mountpoint = False
     else:
         device_mountpoint = tempfile.mkdtemp(prefix="grml2usb")
         register_tmpfile(device_mountpoint)
         remove_device_mountpoint = True
         try:
         remove_device_mountpoint = False
     else:
         device_mountpoint = tempfile.mkdtemp(prefix="grml2usb")
         register_tmpfile(device_mountpoint)
         remove_device_mountpoint = True
         try:
-            check_for_fat(device)
-            mount(device, device_mountpoint, ['-o', 'utf8,iocharset=iso8859-1'])
-        except CriticalException, error:
-            try:
-                mount(device, device_mountpoint, "")
-            except CriticalException, error:
-                logging.critical("Fatal: %s", error)
-                raise
+            set_rw(device)
+            mount(device, device_mountpoint, ["-o", "utf8,iocharset=iso8859-1"])
+        except CriticalException:
+            mount(device, device_mountpoint, "")
     try:
         grml_flavours = identify_grml_flavour(mountpoint)
         for flavour in set(grml_flavours):
             if not flavour:
                 logging.warning("No valid flavour found, please check your iso")
     try:
         grml_flavours = identify_grml_flavour(mountpoint)
         for flavour in set(grml_flavours):
             if not flavour:
                 logging.warning("No valid flavour found, please check your iso")
-            logging.info("Identified grml flavour \"%s\".", flavour)
+            logging.info('Identified grml flavour "%s".', flavour)
             install_iso_files(flavour, mountpoint, device, device_mountpoint)
             GRML_FLAVOURS.add(flavour)
     finally:
         if remove_device_mountpoint:
             remove_mountpoint(device_mountpoint)
 
             install_iso_files(flavour, mountpoint, device, device_mountpoint)
             GRML_FLAVOURS.add(flavour)
     finally:
         if remove_device_mountpoint:
             remove_mountpoint(device_mountpoint)
 
+
 def remove_mountpoint(mountpoint):
 def remove_mountpoint(mountpoint):
-    """remove a registred mountpoint
+    """remove a registered mountpoint
     """
 
     try:
     """
 
     try:
@@ -1941,9 +1913,10 @@ def remove_mountpoint(mountpoint):
         if os.path.isdir(mountpoint):
             os.rmdir(mountpoint)
             unregister_tmpfile(mountpoint)
         if os.path.isdir(mountpoint):
             os.rmdir(mountpoint)
             unregister_tmpfile(mountpoint)
-    except CriticalException, error:
-        logging.critical("Fatal: %s", error)
+    except CriticalException:
         cleanup()
         cleanup()
+        raise
+
 
 def handle_mbr(device):
     """Main handler for installing master boot record (MBR)
 
 def handle_mbr(device):
     """Main handler for installing master boot record (MBR)
@@ -1954,30 +1927,43 @@ def handle_mbr(device):
         logging.info("Would install MBR")
         return 0
 
         logging.info("Would install MBR")
         return 0
 
-    if device[-1:].isdigit():
-        mbr_device = re.match(r'(.*?)\d*$', device).group(1)
-        partition_number = int(device[-1:]) - 1
-        skip_install_mir_mbr = False
+    mbr_device, partition_number = get_device_from_partition(device)
+    if partition_number is None:
+        logging.warn("Could not detect partition number, not activating partition")
 
     # if we get e.g. /dev/loop1 as device we don't want to put the MBR
     # into /dev/loop of course, therefore use /dev/loop1 as mbr_device
     if mbr_device == "/dev/loop":
         mbr_device = device
 
     # if we get e.g. /dev/loop1 as device we don't want to put the MBR
     # into /dev/loop of course, therefore use /dev/loop1 as mbr_device
     if mbr_device == "/dev/loop":
         mbr_device = device
-        logging.info("Detected loop device - using %s as MBR device therefore", mbr_device)
-        skip_install_mir_mbr = True
+        logging.info(
+            "Detected loop device - using %s as MBR device therefore", mbr_device
+        )
+
+    mbrcode = GRML2USB_BASE + "/mbr/mbrldr"
+    if options.syslinuxmbr:
+        mbrcode = ""
+        mbr_locations = (
+            "/usr/lib/syslinux/mbr.bin",
+            "/usr/lib/syslinux/bios/mbr.bin",
+            "/usr/share/syslinux/mbr.bin",
+        )
+        for mbrpath in mbr_locations:
+            if os.path.isfile(mbrpath):
+                mbrcode = mbrpath
+                break
+
+        if not mbrcode:
+            str_locations = " or ".join(['"%s"' % x for x in mbr_locations])
+            logging.error("Cannot find syslinux MBR, install it at %s)", str_locations)
+            raise CriticalException(
+                "syslinux MBR  can not be found at %s." % str_locations
+            )
+    elif options.mbrmenu:
+        mbrcode = GRML2USB_BASE + "/mbr/mbrldr"
 
     try:
 
     try:
-        if options.syslinuxmbr:
-            handle_syslinux_mbr(mbr_device)
-        elif not skip_install_mir_mbr:
-            if options.mbrmenu:
-                install_mir_mbr(GRML2USB_BASE + '/mbr/mbrmgr', mbr_device, partition_number, True)
-            else:
-                install_mir_mbr(GRML2USB_BASE + '/mbr/mbrldr', mbr_device, partition_number, False)
-    except IOError, error:
-        logging.critical("Execution failed: %s", error)
-        sys.exit(1)
-    except Exception, error:
+        install_mbr(mbrcode, mbr_device, partition_number, True)
+    except (IOError, Exception) as error:
         logging.critical("Execution failed: %s", error)
         sys.exit(1)
 
         logging.critical("Execution failed: %s", error)
         sys.exit(1)
 
@@ -1990,22 +1976,26 @@ def handle_vfat(device):
     # make sure we have mkfs.vfat available
     if options.fat16:
         if not which("mkfs.vfat") and not options.copyonly and not options.dryrun:
     # make sure we have mkfs.vfat available
     if options.fat16:
         if not which("mkfs.vfat") and not options.copyonly and not options.dryrun:
-            logging.critical('Sorry, mkfs.vfat not available. Exiting.')
-            logging.critical('Please make sure to install dosfstools.')
+            logging.critical("Sorry, mkfs.vfat not available. Exiting.")
+            logging.critical("Please make sure to install dosfstools.")
             sys.exit(1)
 
         if options.force:
             sys.exit(1)
 
         if options.force:
-            print "Forcing mkfs.fat16 on %s as requested via option --force." % device
+            print("Forcing mkfs.fat16 on %s as requested via option --force." % device)
         else:
             # make sure the user is aware of what he is doing
         else:
             # make sure the user is aware of what he is doing
-            f = raw_input("Are you sure you want to format the specified partition with fat16? y/N ")
+            f = input(
+                "Are you sure you want to format the specified partition with fat16? y/N "
+            )
             if f == "y" or f == "Y":
             if f == "y" or f == "Y":
-                logging.info("Note: you can skip this question using the option --force")
+                logging.info(
+                    "Note: you can skip this question using the option --force"
+                )
             else:
                 sys.exit(1)
         try:
             mkfs_fat16(device)
             else:
                 sys.exit(1)
         try:
             mkfs_fat16(device)
-        except CriticalException, error:
+        except CriticalException as error:
             logging.critical("Execution failed: %s", error)
             sys.exit(1)
 
             logging.critical("Execution failed: %s", error)
             sys.exit(1)
 
@@ -2013,16 +2003,27 @@ def handle_vfat(device):
     if device is not None and not os.path.isdir(device) and options.syslinux:
         try:
             check_for_fat(device)
     if device is not None and not os.path.isdir(device) and options.syslinux:
         try:
             check_for_fat(device)
-        except CriticalException, error:
+        except CriticalException as error:
             logging.critical("Execution failed: %s", error)
             sys.exit(1)
 
             logging.critical("Execution failed: %s", error)
             sys.exit(1)
 
-    if not os.path.isdir(device) and not check_for_usbdevice(device) and not options.force:
-        print "Warning: the specified device %s does not look like a removable usb device." % device
-        f = raw_input("Do you really want to continue? y/N ")
-        if f == "y" or f == "Y":
-            pass
-        else:
+    if options.skipusbcheck:
+        logging.info(
+            "Not checking for removable USB device as requested via option --skip-usb-check."
+        )
+        return
+
+    if (
+        not os.path.isdir(device)
+        and not check_for_usbdevice(device)
+        and not options.force
+    ):
+        print(
+            "Warning: the specified device %s does not look like a removable usb device."
+            % device
+        )
+        f = input("Do you really want to continue? y/N ")
+        if f.lower() != "y":
             sys.exit(1)
 
 
             sys.exit(1)
 
 
@@ -2032,15 +2033,17 @@ def handle_compat_warning(device):
     @device: device that should be checked"""
 
     # make sure we can replace old grml2usb script and warn user when using old way of life:
     @device: device that should be checked"""
 
     # make sure we can replace old grml2usb script and warn user when using old way of life:
-    if device.startswith("/mnt/external") or device.startswith("/mnt/usb") and not options.force:
-        print "Warning: the semantics of grml2usb has changed."
-        print "Instead of using grml2usb /path/to/iso %s you might" % device
-        print "want to use grml2usb /path/to/iso /dev/... instead."
-        print "Please check out the grml2usb manpage for details."
-        f = raw_input("Do you really want to continue? y/N ")
-        if f == "y" or f == "Y":
-            pass
-        else:
+    if (
+        device.startswith("/mnt/external")
+        or device.startswith("/mnt/usb")
+        and not options.force
+    ):
+        print("Warning: the semantics of grml2usb has changed.")
+        print("Instead of using grml2usb /path/to/iso %s you might" % device)
+        print("want to use grml2usb /path/to/iso /dev/... instead.")
+        print("Please check out the grml2usb manpage for details.")
+        f = input("Do you really want to continue? y/N ")
+        if f.lower() != "y":
             sys.exit(1)
 
 
             sys.exit(1)
 
 
@@ -2050,14 +2053,13 @@ def handle_logging():
     if options.verbose and options.quiet:
         parser.error("please use either verbose (--verbose) or quiet (--quiet) option")
 
     if options.verbose and options.quiet:
         parser.error("please use either verbose (--verbose) or quiet (--quiet) option")
 
+    FORMAT = "%(message)s"
     if options.verbose:
     if options.verbose:
-        FORMAT = "Debug: %(asctime)-15s %(message)s"
+        FORMAT = "%(asctime)-15s %(message)s"
         logging.basicConfig(level=logging.DEBUG, format=FORMAT)
     elif options.quiet:
         logging.basicConfig(level=logging.DEBUG, format=FORMAT)
     elif options.quiet:
-        FORMAT = "Critical: %(message)s"
         logging.basicConfig(level=logging.CRITICAL, format=FORMAT)
     else:
         logging.basicConfig(level=logging.CRITICAL, format=FORMAT)
     else:
-        FORMAT = "%(message)s"
         logging.basicConfig(level=logging.INFO, format=FORMAT)
 
 
         logging.basicConfig(level=logging.INFO, format=FORMAT)
 
 
@@ -2068,7 +2070,9 @@ def handle_bootloader(device):
 
     # Install bootloader only if not using the --copy-only option
     if options.copyonly:
 
     # Install bootloader only if not using the --copy-only option
     if options.copyonly:
-        logging.info("Not installing bootloader and its files as requested via option copyonly.")
+        logging.info(
+            "Not installing bootloader and its files as requested via option copyonly."
+        )
     elif os.path.isdir(device):
         logging.info("Not installing bootloader as %s is a directory.", device)
     else:
     elif os.path.isdir(device):
         logging.info("Not installing bootloader as %s is a directory.", device)
     else:
@@ -2076,95 +2080,140 @@ def handle_bootloader(device):
 
 
 def check_options(opts):
 
 
 def check_options(opts):
-    """Check compability of provided user opts
+    """Check compatibility of provided user opts
 
     @opts option dict from OptionParser
     """
     if opts.grubmbr and not opts.grub:
 
     @opts option dict from OptionParser
     """
     if opts.grubmbr and not opts.grub:
-        logging.critical("Error: --grub-mbr requires --grub option.")
-        sys.exit(1)
+        raise CriticalException("--grub-mbr requires --grub option.")
+
+    if opts.copyonly and opts.grub:
+        raise CriticalException("Cannot use --copy-only and --grub at the same time.")
 
 
 def check_programs():
     """check if all needed programs are installed"""
     if options.grub:
 
 
 def check_programs():
     """check if all needed programs are installed"""
     if options.grub:
-        if not which("grub-install"):
-            logging.critical("Fatal: grub-install not available (please install the "
-                             + "grub package or drop the --grub option)")
+        global GRUB_INSTALL
+        GRUB_INSTALL = which("grub-install") or which("grub2-install")
+        if not GRUB_INSTALL:
+            logging.critical(
+                "Fatal: grub-install not available (please install the "
+                "grub package or drop the --grub option)"
+            )
             sys.exit(1)
 
     if options.syslinux:
         if not which("syslinux"):
             sys.exit(1)
 
     if options.syslinux:
         if not which("syslinux"):
-            logging.critical("Fatal: syslinux not available (please install the "
-                             + "syslinux package or use the --grub option)")
+            logging.critical(
+                "Fatal: syslinux not available (please install the "
+                "syslinux package or use the --grub option)"
+            )
             sys.exit(1)
 
     if not which("rsync"):
         logging.critical("Fatal: rsync not available, can not continue - sorry.")
         sys.exit(1)
 
             sys.exit(1)
 
     if not which("rsync"):
         logging.critical("Fatal: rsync not available, can not continue - sorry.")
         sys.exit(1)
 
+
+def load_loop():
+    """Runs modprobe loop and throws away its output"""
+    if not which("modprobe"):
+        logging.critical("Fatal: modprobe not available, can not continue - sorry.")
+        logging.critical("Hint: is /sbin missing in PATH?")
+        sys.exit(1)
+
+    proc = subprocess.Popen(
+        ["modprobe", "loop"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
+    )
+    proc.communicate()
+
+
 def main():
 def main():
-    """Main function [make pylint happy :)]"""
+    """Main invocation"""
 
 
-    if options.version:
-        print os.path.basename(sys.argv[0]) + " " + PROG_VERSION
-        sys.exit(0)
+    try:
+        if options.version:
+            print(os.path.basename(sys.argv[0]) + " " + PROG_VERSION)
+            sys.exit(0)
 
 
-    if len(args) < 2:
-        parser.error("invalid usage")
+        if len(args) < 2:
+            parser.error("invalid usage")
 
 
-    # log handling
-    handle_logging()
+        # log handling
+        handle_logging()
 
 
-    # make sure we have the appropriate permissions
-    check_uid_root()
+        # make sure we have the appropriate permissions
+        check_uid_root()
 
 
-    check_options(options)
+        check_options(options)
 
 
-    logging.info("Executing grml2usb version %s", PROG_VERSION)
+        load_loop()
 
 
-    if options.dryrun:
-        logging.info("Running in simulation mode as requested via option dry-run.")
+        logging.info("Executing grml2usb version %s", PROG_VERSION)
 
 
-    check_programs()
+        if options.dryrun:
+            logging.info("Running in simulation mode as requested via option dry-run.")
 
 
-    # specified arguments
-    device = args[len(args) - 1]
-    isos = args[0:len(args) - 1]
+        check_programs()
 
 
-    if not os.path.isdir(device):
-        if device[-1:].isdigit():
-            if int(device[-1:]) > 4 or device[-2:].isdigit():
-                logging.critical("Fatal: installation on partition number >4 not supported. (BIOS won't support it.)")
-                sys.exit(1)
-        elif os.path.exists(device):
-            logging.critical("Fatal: installation on raw device not supported. (BIOS won't support it.)")
-            sys.exit(1)
+        # specified arguments
+        device = os.path.realpath(args[len(args) - 1])
+        isos = args[0 : len(args) - 1]
 
 
+        if not os.path.isdir(device):
+            if device[-1:].isdigit():
+                if int(device[-1:]) > 4 or device[-2:].isdigit():
+                    logging.warn(
+                        "Warning: installing on partition number >4, booting *might* fail depending on your system."
+                    )
 
 
-    # provide upgrade path
-    handle_compat_warning(device)
+        # provide upgrade path
+        handle_compat_warning(device)
 
 
-    # check for vfat partition
-    handle_vfat(device)
+        if not options.skipbootflag:
+            check_boot_flag(device)
 
 
-    # main operation (like installing files)
-    for iso in isos:
-        install(iso, device)
+        # check for vfat partition
+        handle_vfat(device)
 
 
-    # install mbr
-    if not options.skipmbr and not os.path.isdir(device):
-        handle_mbr(device)
+        # main operation (like installing files)
+        for iso in isos:
+            install(iso, device)
 
 
-    handle_bootloader(device)
+        # install mbr
+        is_superfloppy = not device[-1:].isdigit()
+        if is_superfloppy:
+            logging.info("Detected superfloppy format - not installing MBR")
 
 
-    logging.info("Note: grml flavour %s was installed as the default booting system.", GRML_DEFAULT)
+        if not options.skipmbr and not os.path.isdir(device) and not is_superfloppy:
+            handle_mbr(device)
 
 
-    for flavour in GRML_FLAVOURS:
-        logging.info("Note: you can boot flavour %s using '%s' on the commandline.", flavour, flavour)
+        handle_bootloader(device)
 
 
-    # finally be politely :)
-    logging.info("Finished execution of grml2usb (%s). Have fun with your grml system.", PROG_VERSION)
+        logging.info(
+            "Note: grml flavour %s was installed as the default booting system.",
+            GRML_DEFAULT,
+        )
+
+        for flavour in GRML_FLAVOURS:
+            logging.info(
+                "Note: you can boot flavour %s using '%s' on the commandline.",
+                flavour,
+                flavour,
+            )
+
+        # finally be polite :)
+        logging.info(
+            "Finished execution of grml2usb (%s). Have fun with your Grml system.",
+            PROG_VERSION,
+        )
+
+    except Exception as error:
+        logging.critical("Fatal: %s", str(error))
+        if options.verbose:
+            logging.exception("Exception:")
+        sys.exit(1)
 
 
 if __name__ == "__main__":
 
 
 if __name__ == "__main__":
@@ -2174,5 +2223,5 @@ if __name__ == "__main__":
         logging.info("Received KeyboardInterrupt")
         cleanup()
 
         logging.info("Received KeyboardInterrupt")
         cleanup()
 
-## END OF FILE #################################################################
+# END OF FILE ##################################################################
 # vim:foldmethod=indent expandtab ai ft=python tw=120 fileencoding=utf-8
 # vim:foldmethod=indent expandtab ai ft=python tw=120 fileencoding=utf-8