grml2usb docs: drop references to deprecated grml-medium master
authorMichael Prokop <mika@grml.org>
Wed, 13 Mar 2024 11:36:30 +0000 (12:36 +0100)
committerMichael Prokop <mika@grml.org>
Wed, 13 Mar 2024 11:36:30 +0000 (12:36 +0100)
grml-medium is gone since many years, so there's no point in further
referring to it

Thanks to Christoph Biedl for spotting

19 files changed:
.github/workflows/check-full.yml [new file with mode: 0644]
Makefile
debian/changelog
debian/control
debian/grml2usb.dirs [new file with mode: 0644]
debian/grml2usb.install [new file with mode: 0644]
debian/grml2usb.manpages [new file with mode: 0644]
debian/rules
debian/tests/control
debian/tests/smoke-grml2usb-py2 [deleted file]
grml2iso
grml2iso.8.txt
grml2usb
grml2usb.8.txt
pytest.ini [new file with mode: 0644]
setup.cfg [new file with mode: 0644]
tarball.sh
test/grml2usb.py [new symlink]
test/grml2usb_test.py [new file with mode: 0644]

diff --git a/.github/workflows/check-full.yml b/.github/workflows/check-full.yml
new file mode 100644 (file)
index 0000000..4536d0f
--- /dev/null
@@ -0,0 +1,39 @@
+name: Code Testing
+
+on:
+  push:
+  pull_request:
+  schedule:
+    - cron: '42 1 * * *'
+
+jobs:
+  codecheck:
+    runs-on: ubuntu-latest
+    name: Run codecheck
+
+    steps:
+      - name: Checkout source
+        uses: actions/checkout@v2
+
+      - name: pip install wheel (to make install black work)
+        run: pip3 install wheel
+
+      - name: pip install flake8, isort + black, vulture
+        run: pip3 install flake8 isort black vulture
+
+      - name: Codecheck execution
+        run: make codecheck
+
+  unittests:
+    runs-on: ubuntu-latest
+    name: Run unit tests
+
+    steps:
+      - name: Checkout source
+        uses: actions/checkout@v2
+
+      - name: Install pytest
+        run: pip3 install pytest
+
+      - name: Run Pytest
+        run: pytest
index b8f1e05..dd12e74 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-all: doc
+all: codecheck doc
 
 doc: doc_man doc_html
 
@@ -14,22 +14,10 @@ doc_man: man-stamp
 man-stamp: grml2usb.8.txt grml2iso.8.txt
        # grml2usb:
        asciidoc -d manpage -b docbook grml2usb.8.txt
-       sed -i 's/<emphasis role="strong">/<emphasis role="bold">/' grml2usb.8.xml
        xsltproc --novalid --nonet /usr/share/xml/docbook/stylesheet/nwalsh/manpages/docbook.xsl grml2usb.8.xml
-       # ugly hack to avoid duplicate empty lines in manpage
-       # notice: docbook-xsl 1.71.0.dfsg.1-1 is broken! make sure you use 1.68.1.dfsg.1-0.2!
-       cp grml2usb.8 grml2usb.8.tmp
-       uniq grml2usb.8.tmp > grml2usb.8
-       rm grml2usb.8.tmp
        # grml2iso:
        asciidoc -d manpage -b docbook grml2iso.8.txt
-       sed -i 's/<emphasis role="strong">/<emphasis role="bold">/' grml2iso.8.xml
        xsltproc --novalid --nonet /usr/share/xml/docbook/stylesheet/nwalsh/manpages/docbook.xsl grml2iso.8.xml
-       # ugly hack to avoid duplicate empty lines in manpage
-       # notice: docbook-xsl 1.71.0.dfsg.1-1 is broken! make sure you use 1.68.1.dfsg.1-0.2!
-       cp grml2iso.8 grml2iso.8.tmp
-       uniq grml2iso.8.tmp > grml2iso.8
-       rm grml2iso.8.tmp
        # we're done
        touch man-stamp
 
@@ -45,16 +33,19 @@ prepare-release:
        ./tarball.sh --no-gpg
 
 clean:
+       $(MAKE) -C mbr clean
        rm -rf grml2usb.8.html grml2usb.8.xml grml2usb.8
        rm -rf grml2iso.8.html grml2iso.8.xml grml2iso.8
        rm -rf html-stamp man-stamp grml2usb.tar.gz grml2usb.tgz grml2usb.tgz.md5.asc
 
 codecheck:
-       pyflakes grml2usb
-       pylint --include-ids=y --max-line-length=120 grml2usb
-       # pylint --include-ids=y --disable-msg-cat=C0301 --disable-msg-cat=W0511 grml2usb
-       # pylint --reports=n --include-ids=y --disable-msg-cat=C0301 grml2usb
-       pep8 --repeat --ignore E125,E126,E127,E128,E501 grml2usb
+       flake8 grml2usb
+       isort --check-only grml2usb
+       black --check grml2usb
+       vulture grml2usb test/grml2usb_test.py
+
+test:
+       pytest
 
 # graph:
 #      sudo pycallgraph grml2usb /grml/isos/grml-small_2008.11.iso /dev/sdb1
index 4c6c9df..f818fc7 100644 (file)
@@ -1,3 +1,124 @@
+grml2usb (0.19.2) unstable; urgency=medium
+
+  * [2955aaf] codecheck: reformat with black, version 23.1.0
+    (Closes: #1031466)
+
+ -- Michael Prokop <mika@grml.org>  Mon, 20 Feb 2023 11:33:34 +0100
+
+grml2usb (0.19.1) unstable; urgency=medium
+
+  * [27eba4a] Replace egrep usage with grep -E
+  * [ec28a50] Bump Standards-Version to 4.6.2
+
+ -- Michael Prokop <mika@grml.org>  Wed, 08 Feb 2023 08:27:50 +0100
+
+grml2usb (0.19.0) unstable; urgency=medium
+
+  [ Manuel Rom ]
+  * [40221eb] Add Github action workflows for CI/CD
+
+  [ Michael Prokop ]
+  * [3f77679] Fix vulture usage and add vulture to Build-Depends
+  * [a6cce22] Github action: do not install virtualenv +
+    python3-setuptools. Thanks to Chris Hofstaedtler
+  * [78ae858] grml2iso: support parallel execution
+  * [8126bbf] grml2iso: execute under pipefail
+
+ -- Michael Prokop <mika@grml.org>  Mon, 21 Mar 2022 16:19:54 +0100
+
+grml2usb (0.18.5) unstable; urgency=medium
+
+  * [01ed11d] Fix --grub and --syslinux handling. Thanks to Ralf Moll for
+    the bugreport
+
+ -- Michael Prokop <mika@grml.org>  Fri, 22 Jan 2021 10:32:43 +0100
+
+grml2usb (0.18.4) unstable; urgency=medium
+
+  [ Michael Prokop ]
+  * [3664e4a] Code cleanups. Thanks to Chris Hofstaedtler for review +
+    feedback
+  * [1dfee7d] Use "GRML" as FAT label when creating the file system
+  * [272b66e] Fix race condition with blockdev/BLKRRPART due to lack of
+    fsync. Thanks to dann frazier <dannf@dannf.org> for bug report and
+    patch (Closes: #975015)
+  * [100a957] Bump Standards-Version to 4.5.1
+
+  [ Darshaka Pathirana ]
+  * [7656c18] Use consistent case of 'USB' and 'Grml'
+
+  [ Manuel Rom ]
+  * [6f3eb52] Add unit testing capabilities and basic tests for
+    check_for_usbdevice
+
+ -- Michael Prokop <mika@grml.org>  Fri, 27 Nov 2020 17:56:02 +0100
+
+grml2usb (0.18.3) unstable; urgency=medium
+
+  * [9e7dd96] codecheck: fix flake8 issues with versions >=3.8.3. Thanks
+    to Florian Apolloner for feedback (Closes: #963454)
+  * [ec5eec1] setup.cfg: adjust flake8 section so invoking it without
+    arguments works. Thanks to Florian Apolloner
+  * [92ffc08] Support Grml's new Secure Boot approach
+  * [b1798d1] Do not fail if ISO doesn't have grub.cfg inside efi.img
+  * [f052436] grml2iso: switch from isohybrid to xorriso/isohybrid to
+    properly support EFI boot
+  * [9b96f21] docs: drop no longer needed strong->bold workaround from
+    asciidoc/xsltproc toolchain
+  * [ded5534] docs: rework grml2iso docs, including note about order of
+    ISOs
+
+ -- Michael Prokop <mika@grml.org>  Wed, 24 Jun 2020 11:27:28 +0200
+
+grml2usb (0.18.2) unstable; urgency=medium
+
+  * [657b880] Skip boot flag check when installing to directory
+
+ -- Michael Prokop <mika@grml.org>  Sat, 06 Jun 2020 15:09:37 +0200
+
+grml2usb (0.18.1) unstable; urgency=medium
+
+  [ Michael Prokop ]
+  * [705a96b] Drop old bootflag detection + be more verbose about its
+    execution. Thanks to Darshaka Pathirana for bug report and debugging
+  * [aed7d83] Use "boot flag" instead of "bootflag" also in docs +
+    exceptions
+  * [fcf63b7] Reread partition table after installing MBR to ensure
+    devices exist. Thanks to Darshaka Pathirana for debugging
+
+  [ Darshaka Pathirana ]
+  * [8b59cb0] Check for boot flag before possibly creating FAT16 on the
+    partition
+
+ -- Michael Prokop <mika@grml.org>  Wed, 03 Jun 2020 16:55:00 +0200
+
+grml2usb (0.18.0) unstable; urgency=medium
+
+  [ Tomáš Virtus ]
+  * [25c3bf7] Fix Python 3 syntax warning (is vs ==)
+  * [ed5b633] Support more Syslinux module locations
+  * [f0e3c93] Fix duplicate Syslinux entries
+  * [99ecee2] Add mbr/ output files to gitignore
+  * [480b974] Fix duplicate Syslinux entries (2)
+
+  [ Alexei Colin ]
+  * [4d81cdb] tarball.sh: spaces in pattern for PROG_VERSION
+
+  [ Michael Prokop ]
+  * [d01d9d8] Coding style: fix pep8 + flake8 issues
+  * [b188002] Coding style: execute black + isort and fix undefined 'path'
+  * [f1d1ce8] codecheck: rely on flake8, isort + black during build time.
+    Thanks to Florian Apolloner for the feedback
+  * [31853ed] Bump Standards-Version to 4.5.0
+  * [c921dc1] debian: execute `wrap-and-sort -a -t -s`
+  * [1fa8aac] autopkgtests: drop python2 support and depend on python3
+    only (Closes: #936663)
+  * [aaaef1a] debian: switch to debhelper minimal style
+  * [263374b] Get rid of docbook-xsl workaround regarding duplicate empty
+    lines
+
+ -- Michael Prokop <mika@grml.org>  Tue, 12 May 2020 17:16:34 +0200
+
 grml2usb (0.17.0) unstable; urgency=medium
 
   [ Sven Joachim ]
index 8d01d35..c60f8fb 100644 (file)
@@ -2,32 +2,42 @@ Source: grml2usb
 Section: admin
 Priority: optional
 Maintainer: Grml Team <team@grml.org>
-Uploaders: Michael Prokop <mika@debian.org>,
-           Alexander Wirt <formorer@debian.org>,
-           Christian Hofstaedtler <zeha@debian.org>,
-           Ulrich Dangel <mru@spamt.net>
-Build-Depends: asciidoc,
-               debhelper-compat (= 12),
-               docbook-xsl,
-               xsltproc
-Standards-Version: 4.4.1
+Uploaders:
+ Michael Prokop <mika@debian.org>,
+ Alexander Wirt <formorer@debian.org>,
+ Christian Hofstaedtler <zeha@debian.org>,
+ Ulrich Dangel <mru@spamt.net>,
+Build-Depends:
+ asciidoc,
+ black,
+ debhelper-compat (= 12),
+ docbook-xsl,
+ flake8,
+ isort,
+ vulture,
+ xsltproc,
+Standards-Version: 4.6.2
 Homepage: https://grml.org/grml2usb/
 Vcs-git: git://git.grml.org/grml2usb.git
 Vcs-Browser: https://git.grml.org/?p=grml2usb.git
 
 Package: grml2usb
 Architecture: amd64 i386
-Depends: kmod,
-         mtools,
-         python3,
-         python3-parted,
-         rsync,
-         syslinux | grub2-common, syslinux | grub-pc-bin,
-         ${misc:Depends},
-         ${shlibs:Depends}
-Recommends: syslinux,
-            syslinux-utils,
-            xorriso | genisoimage
+Depends:
+ kmod,
+ mtools,
+ python3,
+ python3-parted,
+ rsync,
+ syslinux | grub-pc-bin,
+ syslinux | grub2-common,
+ ${misc:Depends},
+ ${shlibs:Depends},
+Recommends:
+ isolinux (>= 3:6.03+dfsg-5+deb8u1~),
+ syslinux,
+ syslinux-utils,
+ xorriso | genisoimage,
 Description: install Grml system / ISO to usb device
  This script installs a Grml ISO to an USB device to be able
  to boot from it.  Make sure you have at least one Grml ISO
diff --git a/debian/grml2usb.dirs b/debian/grml2usb.dirs
new file mode 100644 (file)
index 0000000..eeb5992
--- /dev/null
@@ -0,0 +1,2 @@
+usr/share/grml2usb/grub
+usr/share/grml2usb/mbr
diff --git a/debian/grml2usb.install b/debian/grml2usb.install
new file mode 100644 (file)
index 0000000..1dd0901
--- /dev/null
@@ -0,0 +1,5 @@
+grml2iso usr/sbin/
+grml2usb usr/sbin/
+grub/ usr/share/grml2usb/
+mbr/mbrldr usr/share/grml2usb/mbr/
+mbr/mbrmgr usr/share/grml2usb/mbr/
diff --git a/debian/grml2usb.manpages b/debian/grml2usb.manpages
new file mode 100644 (file)
index 0000000..eaf2a74
--- /dev/null
@@ -0,0 +1,2 @@
+grml2iso.8
+grml2usb.8
index f8d5ced..7d696b5 100755 (executable)
@@ -1,71 +1,24 @@
 #!/usr/bin/make -f
-# -*- makefile -*-
-# Sample debian/rules that uses debhelper.
-# This file was originally written by Joey Hess and Craig Small.
-# As a special exception, when this file is copied by dh-make into a
-# dh-make output file, you may use that output file without restriction.
-# This special exception was added by Craig Small in version 0.37 of dh-make.
 
 # Uncomment this to turn on verbose mode.
 #export DH_VERBOSE=1
 
 VERSION:=$(shell dpkg-parsechangelog | awk '/Version: / { print $$2 }')
 
-build: build-arch build-indep
-build-arch: build-stamp
-build-indep: build-stamp
+%:
+       dh $@
 
-build-stamp:
+override_dh_auto_build:
        dh_testdir
-
-       egrep -q "^PROG_VERSION = '\*\*\*UNKNOWN\*\*\*'" grml2usb || (echo "PROG_VERSION in grml2usb wrong." && exit 2)
+       grep -qE '^PROG_VERSION = "\*\*\*UNKNOWN\*\*\*"' grml2usb || (echo "PROG_VERSION in grml2usb wrong." && exit 2)
+       $(MAKE) -C mbr
        $(MAKE)
-       cd mbr && $(MAKE) && cd ..
-       touch build-stamp
-
-clean:
-       dh_testdir
-       dh_testroot
-       rm -f build-stamp
 
-       # Add here commands to clean up after the build process.
-       $(MAKE) clean
-       cd mbr && $(MAKE) clean && cd ..
-       dh_clean
-
-install: build
-       dh_testdir
-       dh_testroot
-       dh_prep
-       dh_installdirs usr/share/grml2usb/grub usr/share/grml2usb/mbr
-
-       # Add here commands to install the package into debian/grml2usb.
-       install -m 755 grml2usb               debian/grml2usb/usr/sbin/grml2usb
-       sed -i -e "s/^PROG_VERSION = '\*\*\*UNKNOWN\*\*\*'/PROG_VERSION = '$(VERSION)'/" debian/grml2usb/usr/sbin/grml2usb
-       install -m 755 grml2iso               debian/grml2usb/usr/sbin/grml2iso
-       install -m 644 mbr/mbrmgr             debian/grml2usb/usr/share/grml2usb/mbr/mbrmgr
-       install -m 644 mbr/mbrldr             debian/grml2usb/usr/share/grml2usb/mbr/mbrldr
-       cp -a          grub/                  debian/grml2usb/usr/share/grml2usb/
-
-# Build architecture-dependent files here.
-binary-arch: build-arch install
-       dh_testdir
-       dh_testroot
-       dh_installchangelogs
-       dh_installdocs TODO
-       dh_installman grml2usb.8 grml2iso.8
-       dh_strip
-       dh_compress
+override_dh_fixperms:
+       chmod 664 ./debian/grml2usb/usr/share/grml2usb/mbr/mbrldr
+       chmod 664 ./debian/grml2usb/usr/share/grml2usb/mbr/mbrmgr
        dh_fixperms
-       dh_installdeb
-       dh_shlibdeps
-       dh_gencontrol
-       dh_md5sums
-       dh_builddeb
-
-# Build architecture-independent files here.
-binary-indep: build-indep install
-# Nothing to do.
 
-binary: binary-indep binary-arch
-.PHONY: build clean binary-indep binary-arch binary install
+override_dh_install:
+       dh_install
+       sed -i -e "s/^PROG_VERSION = \"\*\*\*UNKNOWN\*\*\*\"/PROG_VERSION = \"$(VERSION)\"/" ./debian/grml2usb/usr/sbin/grml2usb
index c6aeeed..7df3b9e 100644 (file)
@@ -1,21 +1,11 @@
-Tests: smoke-grml2usb-py2
-Depends: dosfstools,
-         kpartx,
-         isolinux,
-         python2,
-         syslinux,
-         syslinux-common,
-         xorriso,
-         @
-Restrictions: needs-root, isolation-machine, breaks-testbed
-
 Tests: smoke-grml2usb-py3
-Depends: dosfstools,
-         kpartx,
-         isolinux,
-         python2,
-         syslinux,
-         syslinux-common,
-         xorriso,
-         @
+Depends:
+ dosfstools,
+ isolinux,
+ kpartx,
+ python3,
+ syslinux,
+ syslinux-common,
+ xorriso,
+ @,
 Restrictions: needs-root, isolation-machine, breaks-testbed
diff --git a/debian/tests/smoke-grml2usb-py2 b/debian/tests/smoke-grml2usb-py2
deleted file mode 100755 (executable)
index 67b45d1..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-exec 2>&1
-set -ex
-
-TMPDIR=$(mktemp -d)
-LODEV=$(losetup -f)
-cleanup() {
-  kpartx -d "$LODEV" || true
-  losetup -d "$LODEV" || true
-  rm -rf "$TMPDIR"
-}
-trap cleanup EXIT
-
-mkdir "$TMPDIR"/isoroot "$TMPDIR"/isoroot/boot "$TMPDIR"/isoroot/boot/isolinux
-cp /usr/lib/ISOLINUX/isolinux.bin "$TMPDIR"/isoroot/boot/isolinux/
-echo 'FAKE' > "$TMPDIR"/isoroot/grml-version
-echo 'LOGO' > "$TMPDIR"/isoroot/boot/logo.16
-touch "$TMPDIR"/isoroot/boot/isolinux/FAKE_default.cfg
-touch "$TMPDIR"/isoroot/boot/isolinux/FAKE_grml.cfg
-touch "$TMPDIR"/isoroot/boot/isolinux/hidden.cfg
-xorriso -as mkisofs -l -r -J -no-pad -no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat -o "$TMPDIR"/fake.iso "$TMPDIR"/isoroot
-xorriso -dev "$TMPDIR"/fake.iso -ls
-
-dd if=/dev/zero of="$TMPDIR"/blockdev bs=1M count=50
-
-sfdisk "$TMPDIR"/blockdev <<EOT
-label: dos
-label-id: 0x00000000
-unit: sectors
-
-p1 : start=        2048, size=      100352, type=6, bootable
-EOT
-
-losetup -P "$LODEV" "$TMPDIR"/blockdev
-
-python2 $(which grml2usb) --bootloader-only --verbose --skip-usb-check --force --fat16 "$TMPDIR"/fake.iso "$LODEV"p1
index ecf9c82..3e48688 100755 (executable)
--- a/grml2iso
+++ b/grml2iso
@@ -1,29 +1,20 @@
 #!/usr/bin/env bash
 # Filename:      grml2iso
 # Purpose:       create a multiboot grml ISO using grml2usb
-# Authors:       Michael Prokop <mika@grml.org>,
-#                Thorsten Glaser <tg@mirbsd.org>
+# Authors:       Michael Prokop <mika@grml.org>
 # Bug-Reports:   see http://grml.org/bugs/
 # License:       This file is licensed under the GPL v2 or any later version.
 ################################################################################
 
+set -e -o pipefail
+
 # make sure we have the sbin directories in our PATH to find grml2usb ootb
 PATH="${PATH}:/sbin:/usr/local/sbin:/usr/sbin"
 
-# define function getfilesize before "set -e" {{{
-  if stat --help >/dev/null 2>&1; then
-    getfilesize='stat -c %s'        # GNU stat
-  else
-    getfilesize='stat -f %z'        # BSD stat
-  fi
-# }}}
-
 # adjust variables if necessary through environment {{{
 # path to the grml2usb script you'd like to use
-  [ -n "$GRML2USB" ] || GRML2USB='grml2usb'
-# work directory for creating the filesystem
-  [ -n "$TMPDIR" ]   && WRKDIR="${TMPDIR}/grml2iso.tmp"
-  [ -n "$WRKDIR" ]   || WRKDIR='/tmp/grml2iso.tmp'
+[ -n "$GRML2USB" ] || GRML2USB='grml2usb'
+
 # support mkisofs as well as genisoimage
 if which xorriso >/dev/null 2>&1 ; then
   MKISOFS='xorriso -as mkisofs'
@@ -43,8 +34,6 @@ fi
 # }}}
 
 # helper stuff {{{
-  set -e
-
   usage() {
     echo >&2 "Usage: $0 [OPTIONS] -o target.iso source1.iso [source2.iso ...]"
     echo >&2 "
@@ -63,10 +52,11 @@ Options:
                          restrictions in the bootprocess only IPs are allowed.
                          Supported protocols are: http and ftp
      -t Directory        Directory that should be used for temporary files
-                         during build. Defaults to /tmp/grml2iso.tmp if unset.
+                         during build, instead of using a temporary directory
+                         created by mktemp(1).
 
      Examples:
-     $0 -s http://192.168.23.42:8000/grml/ -o small.iso grml64-small_2018.12.iso 
+     $0 -s http://192.168.23.42:8000/grml/ -o small.iso grml64-small_2021.07.iso
 
      Will generate a file small.iso which tries to download the squashfs file from
      http://192.168.23.42:8000/grml/ - the squashfs file is placed in the same
@@ -104,10 +94,6 @@ Options:
     GRML2USB_OPTS+=(--bootoptions="fetch=$URI")
   fi
 
-  if [ -n "$WRKDIR" ] ; then
-    GRML2USB_OPTS+=(--tmpdir="$WRKDIR")
-  fi
-
 # make sure -o is specified
   [ -n "$ISOFILE" ] || usage 1
 
@@ -152,8 +138,16 @@ Options:
   esac
 # }}}
 
-# create necessary stuff under WRKDIR {{{
-  [ -d "$WRKDIR" ] && WRKDIR_EXISTED='true' || WRKDIR_EXISTED='false'
+# ensure to properly set up working directory {{{
+  WRKDIR_EXISTED='false'
+  if [ -z "$WRKDIR" ] ; then
+    WRKDIR="$(mktemp -d)"
+  else
+    [ -d "$WRKDIR" ] && WRKDIR_EXISTED='true'
+  fi
+
+  GRML2USB_OPTS+=(--tmpdir="$WRKDIR")
+
   rm -rf "$WRKDIR/cddir" "$WRKDIR/grub_tmp"
   mkdir -p "$WRKDIR/cddir"
 # }}}}
@@ -198,7 +192,14 @@ Options:
           UEFI_ENABLE=false
         else
           echo "/boot/efi.img found, extending boot arguments for (U)EFI boot."
-          BOOT_ARGS="$BOOT_ARGS -boot-info-table -eltorito-alt-boot -e boot/efi.img -no-emul-boot"
+          if ! [ -r /usr/lib/ISOLINUX/isohdpfx.bin ] ; then
+            echo "Error: /usr/lib/ISOLINUX/isohdpfx.bin not available, required for xorriso/isohybrid though." >&2
+            echo "Hint:  make sure isolinux is installed." >&2
+            exit 1
+          else
+            BOOT_ARGS+=" -boot-info-table -eltorito-alt-boot -e boot/efi.img -no-emul-boot"
+            BOOT_ARGS+=" -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin -eltorito-alt-boot -e boot/efi.img -no-emul-boot -isohybrid-gpt-basdat -no-pad"
+          fi
         fi
       fi
       ;;
@@ -272,33 +273,10 @@ Options:
 # }}}
 
 # generate the CD/DVD ISO {{{
-  $MKISOFS -V 'grml-multiboot' -l -r -J -no-pad $BOOT_ARGS \
+  $MKISOFS -V 'grml-multiboot' -l -r -J $BOOT_ARGS \
     -o "$ISOFILE" .
 # }}}
 
-# pad the output ISO to multiples of 256 KiB for partition table support {{{
-  siz=$($getfilesize "$ISOFILE")
-  cyls=$(($siz / 512 / 32 / 16 + 1))  # C=$cyls H=16 S=32
-  ofs=$(($cyls * 16 * 32 * 512 - 1))  # padding offset (size - 1)
-  dd if=/dev/zero bs=1 count=1 seek=$ofs of="$ISOFILE" 2>/dev/null
-# }}}
-
-# make ISO dd-able {{{
-  if ! $UEFI_ENABLE ; then
-    echo "Skipping check for --uefi option in isohybrid since prerequisites are not fulfilled."
-  else
-    if ! isohybrid --help | grep -q -- --uefi ; then
-      echo "isohybrid version does NOT support --uefi option, disabling"
-    else
-      echo "isohybrid version supports --uefi option"
-      ISOHYBRID_OPTIONS=--uefi
-    fi
-  fi
-
-  echo "Creating dd-able ISO using isohybrid"
-  isohybrid $ISOHYBRID_OPTIONS "$ISOFILE"
-# }}}
-
 # cleanup {{{
   cd "$ORIG_DIR"
   sync
index 14bbdd4..3c574c8 100644 (file)
@@ -1,5 +1,5 @@
 grml2iso(8)
-==========
+===========
 
 Name
 ----
@@ -10,16 +10,25 @@ Synopsis
 grml2iso -o <target.iso> <ISO[s]>
 
 *******************************************************************************
-Important! The grml team does not take responsibility for loss of any data!
+Important! The Grml team does not take responsibility for loss of any data!
 *******************************************************************************
 
 Introduction
 ------------
 
-grml2iso allows you to create a multiboot Grml ISO. You can specify
-two or more Grml ISOs and will get one single multiboot ISO as a result.
-grml2iso requires and uses grml2usb for this task and installs grub2
-as bootmanager on the multiboot ISO.
+grml2iso allows you to create a multiboot Grml ISO.
+You can specify two or more Grml ISOs and will get one single multiboot ISO as a result.
+grml2iso requires and uses grml2usb for this task.
+
+Important
+---------
+
+The order of the provided ISOs matters for two reasons:
+
+1) order in boot menu entries: the first ISO specified is listed as first (default) option in the boot menu, further ISOs will be listed below as next options
+
+2) EFI image to be used: the EFI image for booting via (U)EFI will be taken from the last provided ISO.
+Therefore to be able to boot on 64bit EFI systems, you need to provide a 64bit ISO (like grml64-small or grml64-full) as _last_ ISO in the grml2iso command line.
 
 Options
 -------
@@ -27,19 +36,16 @@ Options
 grml2iso supports the environment variables GRML2USB and WRKDIR.
 GRML2USB specifies the path to the grml2usb script you'd like to use.
 WRKDIR specifies the work directory for creating the filesystem.
-The work directory needs at least as much free disk space as the sum
-of all specified ISOs.
+The work directory needs at least as much free disk space as the sum of all specified ISOs.
 
   *-o <target.iso>*::
 
-This option is mandatory and specifies where the resulting multiboot Grml ISO
-should be placed. Note that (to avoid any possible data loss) grml2iso will exit
-if the specified target.iso exists already.
+This option is mandatory and specifies where the resulting multiboot Grml ISO should be placed.
+Note that (to avoid any possible data loss) grml2iso will exit if the specified target.iso exists already.
 
   *-c <directory>*::
 
-The content of the specified directory will be copied to the resulting
-multiboot Grml ISO.
+The content of the specified directory will be copied to the resulting multiboot Grml ISO.
 
   *-b <boot params>*::
 
@@ -51,63 +57,53 @@ Force the program to run and overwrite an existing ISO image.
 
   *-r <boot param>*::
 
-Remove specified boot parameter from existing command line. Could be specified multiple times.
+Remove specified boot parameter from existing command line.
+Can be specified multiple times.
 
   *-p <grml2usb param>*::
 
-Execute grml2usb with the specified parameters. For a list of valid parameters have a look at the link:http://grml.org/grml2usb/[grml2usb webpage] or the grml2usb manpage
+Execute grml2usb with the specified parameters.
+For a list of valid parameters have a look at the grml2usb(8) manual page.
 
   *-s <URI>*::
 
-Generate a small ISO file which downloads the squashfs file from the
-specified URI. Due  to current limitations in busyboxs wget and DNS
-resolution, an URL can not contain a hostname but an IP only. This is
-useful if you want to boot systems which support booting ISO image from
-your local system. Besides the iso image this command also copies the
-squashfs file to the output directory.
+Generate a small ISO file which downloads the squashfs file from the specified URI.
+Due to current limitations in busyboxs wget and DNS resolution, an URL can not contain a hostname but an IP only.
+This is useful if you want to boot systems which support booting ISO image from your local system.
+Besides the ISO image this command also copies the squashfs file to the output directory.
 
 Usage examples
 --------------
 
-  # grml2iso -o /tmp/grml.iso grml_2009.05.iso grml64_2009.05.iso
+  # grml2iso -o /tmp/grml-multiboot.iso grml32-small_2020.06.iso grml64-small_2020.06.iso
 
-Create multiboot ISO /tmp/grml.iso with grml_2009.05.iso and grml64_2009.05.iso.
+Create multiboot ISO /tmp/grml-multiboot.iso with grml32-small_2020.06.iso and grml64-small_2020.06.iso.
 
- # grml2iso -b 'lang=de ssh=passwd' -c /tmp/grml-content -o /srv/grml.iso /srv/grml/grml_2009.10.iso
+ # grml2iso -b 'lang=de ssh=passwd' -c /tmp/grml-content -o /srv/grml-multiboot.iso /srv/grml/grml32-small_2020.06.iso
 
-Create a new ISO with additional boot parameters and copy the content
-from /tmp/grml-content to the generated ISO image.
+Create a new ISO with additional boot parameters and copy the content from /tmp/grml-content to the generated ISO image.
 
-  # grml2iso -r quiet -r vga=791 -o /srv/grml.iso /srv/grml-small_2009.10.iso
+  # grml2iso -r quiet -r vga=791 -o /srv/grml-multiboot.iso /srv/grml64-small_2020.06.iso
 
 Create a new ISO and remove existing boot parameters quiet and vga=791.
 
-  # GRML2USB=/srv/git/grml2usb grml2iso -o /srv/grml.iso /srv/grml/grml_2009.05.iso /srv/grml/grml64-medium_2009.05.iso
+  # GRML2USB=/srv/git/grml2usb/grml2usb grml2iso -o /srv/grml-multiboot.iso /srv/grml/grml32-small_2020.06.iso /srv/grml/grml64-medium_2020.06.iso
 
-Create multiboot ISO /srv/grml.iso with grml_2009.05.iso and
-grml64-medium_2009.05.iso using /srv/git/grml2usb as grml2usb script.
+Create multiboot ISO /srv/grml-multiboot.iso with grml32-small_2020.06.iso and grml64-medium_2020.06.iso using /srv/git/grml2usb/grml2usb as grml2usb script.
 
-  # WRKDIR=/mnt/test/grml-tmp grml2iso -o /mnt/test/grml.iso grml_2009.05.iso grml64_2009.05.iso
+  # WRKDIR=/mnt/test/grml-tmp grml2iso -o /mnt/test/grml-multiboot.iso grml32-small_2020.06.iso grml64-small_2020.06.iso
 
-Use /mnt/test/grml-tmp as working directory for creating the multiboot ISO
-/mnt/test/grml.iso with grml_2009.05.iso and grml64_2009.05.iso.
+Use /mnt/test/grml-tmp as working directory for creating the multiboot ISO /mnt/test/grml-multiboot.iso with grml32-small_2020.06.iso and grml64-small_2020.06.iso.
 
-  # grml2iso -p --skip-addons -o /srv/grml.iso /srv/grml-small_2009.10.iso /srv/grml64-small_2009.10.iso
+  # grml2iso -p --skip-addons -o /srv/grml-multiboot.iso /srv/grml64-small_2020.06.iso /srv/grml64-small_2020.06.iso
 
 Don't copy the addons from the specified ISO images
 
-
-Online Resources
-----------------
-
-Check out the link:http://grml.org/grml2usb/[grml2usb webpage] and the
-link:http://git.grml.org/?p=grml2usb.git[grml2usb git repository].
-
 Bugs
 ----
-Please report feedback, bugreports and wishes <<X7,to the author>>.
+Please report feedback, bug reports and wishes at https://github.com/grml/grml2usb/
 
 [[X7]]
-Authors
--------
-Michael Prokop <mika@grml.org> and Thorsten Glaser <tg@mirbsd.org>
+Author
+------
+Michael Prokop <mika@grml.org>
index 6cf456a..5ac6e76 100755 (executable)
--- a/grml2usb
+++ b/grml2usb
@@ -14,76 +14,72 @@ This script installs a Grml system (either a running system or ISO[s]) to a USB
 """
 
 
-from optparse import OptionParser
-from inspect import isroutine, isclass
-import datetime
 import fileinput
 import glob
 import logging
 import os
 import os.path
 import re
-import struct
+import shutil
 import subprocess
 import sys
 import tempfile
-import time
 import uuid
-import shutil
+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***'
+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)"
+    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
-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?
 GRML_DEFAULT = None
 UUID = None
 SYSLINUX_LIBS = [
-    "/usr/lib/syslinux/modules/bios/", # Debian
-    "/usr/lib/syslinux/bios/", # Arch Linux
+    "/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+$')
+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
-    """
+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)
 
 
 # if grub option is set, unset syslinux option
-def grub_option(option, opt, value, opt_parser):
-    """A helper function adjusting other option values
-    """
+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
@@ -98,61 +94,154 @@ Run %prog --help for usage hints, further information via: man grml2usb"
 # 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("--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 bootflag 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")
+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()
 
 
-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__))
 
@@ -175,29 +264,11 @@ class VerifyException(Exception):
     @Exception: message"""
 
 
-# The following two functions help to operate on strings as
-# array (list) of bytes (octets). In Python 3000, the bytes
-# datatype will need to be used. This is intended for using
-# with manipulation of files on the octet level, like shell
-# arrays, e.g. in MBR creation.
-
-
-def array2string(*a):
-    """Convert a list of integers [0;255] to a string."""
-    return struct.pack("%sB" % len(a), *a)
-
-
-def string2array(s):
-    """Convert a (bytes) string into a list of integers."""
-    return struct.unpack("%sB" % len(s), s)
-
-
 def cleanup():
-    """Cleanup function to make sure there aren't any mounted devices left behind.
-    """
-    def del_failed(fn, filepath, exc):
-        msg = "Deletion of %s failed in temporary folder %s"
-        logging.warn(msg % (filepath, path))
+    """Cleanup function to make sure there aren't any mounted devices left behind."""
+
+    def del_failed(_fn, path, _exc):
+        logging.warning("Deletion of %s failed in temporary folder", path)
 
     logging.info("Cleaning up before exiting...")
     proc = subprocess.Popen(["sync"])
@@ -206,9 +277,9 @@ def cleanup():
     for device in MOUNTED.copy():
         try:
             unmount(device, "")
-            logging.debug('Unmounted %s' % device)
+            logging.debug("Unmounted %s" % device)
         except Exception:
-            logging.debug('RuntimeError while umount %s, ignoring' % device)
+            logging.debug("RuntimeError while umount %s, ignoring" % device)
 
     for tmppath in TMPFILES.copy():
         try:
@@ -216,14 +287,14 @@ def cleanup():
                 # 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)
+                logging.debug("temporary directory %s deleted" % tmppath)
                 unregister_tmpfile(tmppath)
             elif os.path.isfile:
                 os.unlink(tmppath)
-                logging.debug('temporary file %s deleted' % tmppath)
+                logging.debug("temporary file %s deleted" % tmppath)
                 unregister_tmpfile(tmppath)
         except Exception:
-            msg = 'RuntimeError while removing temporary %s, ignoring'
+            msg = "RuntimeError while removing temporary %s, ignoring"
             logging.debug(msg % tmppath)
 
 
@@ -272,7 +343,7 @@ def get_function_name(obj):
     """
     if not (isroutine(obj) or isclass(obj)):
         obj = type(obj)
-    return obj.__module__ + '.' + obj.__name__
+    return obj.__module__ + "." + obj.__name__
 
 
 def set_rw(device):
@@ -293,7 +364,11 @@ def execute(f, *exec_arguments):
     # 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)
@@ -325,24 +400,28 @@ def which(program):
 
 
 def get_defaults_file(iso_mount, flavour, name):
-    """get the default file for syslinux
-    """
-    bootloader_dirs = ['/boot/isolinux/', '/boot/syslinux/']
+    """get the default file for syslinux"""
+    bootloader_dirs = ["/boot/isolinux/", "/boot/syslinux/"]
     for directory in bootloader_dirs:
         for name in name, "%s_%s" % (get_flavour_filename(flavour), 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', lst_return=False, required=False):
+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
     @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)
-    current_dir = ''  # make pylint happy :)
+    current_dir = ""  # make pylint happy :)
     retval = []
 
     def match_file(cwd):
@@ -358,15 +437,16 @@ def search_file(filename, search_path='/bin' + os.pathsep + '/usr/bin', lst_retu
             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):
+        for current_dir, _directories, _files in os.walk(path):
             if match_file(current_dir):
                 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))
+        raise CriticalException(
+            "Required file %s not found in %s" % (filename, search_path)
+        )
 
     if lst_return:
         return retval
@@ -394,34 +474,35 @@ def get_partition_for_path(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("bootflag is enabled on %s" % device)
+            logging.debug("boot flag is enabled on %s" % device)
             return
-    except HodorException as e:
-        logging.info("%s, falling back to old bootflag detection", e)
-    except ImportError as e:
-        logging.debug("could not import parted, falling back to old bootflag detection")
-
-    with open(boot_dev, 'rb') as image:
-        data = image.read(520)
-        bootcode = data[440:]
-        gpt_data = bootcode[70:80]
-
-        if gpt_data == GPT_HEADER:
-            logging.info("GPT detected, skipping bootflag check")
-        elif bootcode[6] == b"\x80":
-            logging.debug("bootflag is enabled")
         else:
-            logging.debug("bootflag is NOT enabled")
-            raise VerifyException("Device %s does not have the bootflag set. "
-                "Please enable it to be able to boot." % device)
+            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):
@@ -435,35 +516,16 @@ def mkfs_fat16(device):
 
     logging.info("Formating partition with fat16 filesystem")
     logging.debug("mkfs.vfat -F 16 %s", device)
-    proc = subprocess.Popen(["mkfs.vfat", "-F", "16", device])
+    proc = subprocess.Popen(["mkfs.vfat", "-n", "GRML", "-F", "16", device])
     proc.wait()
     if proc.returncode != 0:
         raise CriticalException("error executing mkfs.vfat")
 
 
-def generate_isolinux_splash(grml_flavour):
-    """Generate bootsplash for isolinux/syslinux
-
-    @grml_flavour: name of grml flavour the configuration should be generated for"""
-
-    grml_name = grml_flavour
-
-    return("""\
-\ 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})
-
-
-def generate_main_syslinux_config(*arg):
-    """Generate main configuration for use in syslinux.cfg
+def generate_main_syslinux_config():
+    """Generate main configuration for use in syslinux.cfg"""
 
-    @*arg: just for backward compatibility"""
-    # pylint: disable-msg=W0613
-    # remove warning about unused arg
-
-    return("""\
+    return """\
 label -
 menu label Default boot modes:
 menu disable
@@ -493,7 +555,7 @@ label help
 
 
 include hiddens.cfg
-""")
+"""
 
 
 def generate_flavour_specific_syslinux_config(grml_flavour):
@@ -501,7 +563,7 @@ def generate_flavour_specific_syslinux_config(grml_flavour):
 
     @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
@@ -512,7 +574,10 @@ menu begin grml %(grml_flavour)s
     include %(grml_flavour)s_grml.cfg
     menu hide
 menu end
-""" % {'grml_flavour': grml_flavour, 'display_name': get_flavour_filename(grml_flavour)})
+""" % {
+        "grml_flavour": grml_flavour,
+        "display_name": get_flavour_filename(grml_flavour),
+    }
 
 
 def install_grub(device):
@@ -522,7 +587,9 @@ def install_grub(device):
     @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)
@@ -541,23 +608,39 @@ def install_grub(device):
             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+"))
+                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.")
+                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 as error:
@@ -587,7 +670,9 @@ def install_syslinux(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):
@@ -619,16 +704,14 @@ def install_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
     "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
 
-    @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
@@ -639,12 +722,15 @@ def install_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
     logging.info("Installing default MBR")
 
     if not os.path.isfile(mbrtemplate):
-        logging.error('Error installing MBR (either try --syslinux-mbr or '
-            'install missing file "%s"?)', mbrtemplate)
+        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 is not None and ((partition < 0) or (partition > 3)):
-        logging.warn("Cannot activate partition %d", partition)
+        logging.warning("Cannot activate partition %d", partition)
         partition = None
 
     if ismirbsdmbr:
@@ -655,16 +741,31 @@ def install_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
     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=open(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)")
 
-    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+"))
+    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)")
@@ -675,16 +776,27 @@ def install_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
 
     if partition is not None:
         if ismirbsdmbr:
-            mbrcode = mbrcode[0:439] + chr(partition).encode('latin-1') + \
-                    mbrcode[440:510] + b"\x55\xAA"
+            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"
+            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()
@@ -693,35 +805,38 @@ def install_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
 
     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+"))
+    logging.debug(
+        "executing: dd if='%s' of='%s' bs=512 count=1 conv=notrunc,fsync",
+        tmpf.name,
+        device,
+    )
+    proc = subprocess.Popen(
+        [
+            "dd",
+            "if=%s" % tmpf.name,
+            "of=%s" % device,
+            "bs=512",
+            "count=1",
+            "conv=notrunc,fsync",
+        ],
+        stderr=open(os.devnull, "r+"),
+    )
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (third run)")
     del tmpf
 
-    # make sure we sync filesystems before returning
-    proc = subprocess.Popen(["sync"])
+    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
+        )
 
     set_rw(device)
 
 
-def is_writeable(device):
-    """Check if the device is writeable for the current user
-
-    @device: partition where bootloader should be installed to"""
-
-    if not device:
-        return False
-
-    if not os.path.exists(device):
-        return False
-
-    return os.access(device, os.W_OK) and os.access(device, os.R_OK)
-
-
 def mount(source, target, mount_options):
     """Mount specified source on given target
 
@@ -732,10 +847,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
 
-    for x in open('/proc/mounts', 'r').readlines():
+    for x in open("/proc/mounts", "r").readlines():
         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)
@@ -745,7 +864,9 @@ def mount(source, target, mount_options):
     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)
@@ -759,7 +880,7 @@ def unmount(target, unmount_options):
 
     # 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):
@@ -778,18 +899,26 @@ def unmount(target, unmount_options):
             unregister_mountpoint(target)
 
 
+def extract_device_name(device):
+    """Extract the device name of a given path
+
+    @device: device name, like /dev/sda1 or /dev/sda
+    """
+    return re.match(r"/dev/(.*?)\d*$", device).group(1)
+
+
 def check_for_usbdevice(device):
     """Check whether the specified device is a removable USB device
 
     @device: device name, like /dev/sda1 or /dev/sda
     """
 
-    usbdevice = re.match(r'/dev/(.*?)\d*$', device).group(1)
+    usbdevice = extract_device_name(device)
     # 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:
-        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()
@@ -805,19 +934,30 @@ def check_for_fat(partition):
     @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:
-        filesystem = subprocess.check_output(["/sbin/blkid", "-s", "TYPE", "-o", "value", partition]).decode().rstrip()
+        filesystem = (
+            subprocess.check_output(
+                ["/sbin/blkid", "-s", "TYPE", "-o", "value", partition]
+            )
+            .decode()
+            .rstrip()
+        )
 
         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:
-        raise CriticalException("Sorry, /sbin/blkid not available (install util-linux?)")
+        raise CriticalException(
+            "Sorry, /sbin/blkid not available (install util-linux?)"
+        )
 
 
 def mkdir(directory):
@@ -857,7 +997,7 @@ def write_uuid(target_file):
     @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()
@@ -874,7 +1014,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 = open(uuid_file_name, 'r')
+            uuid_file = open(uuid_file_name, "r")
             uid = uuid_file.readline().strip()
             uuid_file.close()
             return uid
@@ -889,7 +1029,7 @@ 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)
+    return re.sub(r"[,._-]", "", grml_flavour)
 
 
 def copy_system_files(grml_flavour, iso_mount, target):
@@ -899,89 +1039,46 @@ 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"""
 
-    squashfs = search_file(grml_flavour + '.squashfs', iso_mount)
+    squashfs = search_file(grml_flavour + ".squashfs", iso_mount)
     if squashfs is None:
         logging.error("error locating squashfs file")
-        raise CriticalException("squashfs file not found, please check that your iso is not corrupt")
+        raise CriticalException(
+            "squashfs file not found, please check that your iso is not corrupt"
+        )
     else:
-        squashfs_target = target + '/live/' + grml_flavour + '/'
+        squashfs_target = target + "/live/" + grml_flavour + "/"
         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 + "/", "":
-        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:
         logging.error("error locating filesystem.module file")
         raise CriticalException("filesystem.module not found")
     else:
-        exec_rsync(filesystem_module, squashfs_target + 'filesystem.module')
+        exec_rsync(filesystem_module, squashfs_target + "filesystem.module")
 
     shortname = get_shortname(grml_flavour)
-    if os.path.isdir(iso_mount + '/boot/' + shortname):
-        exec_rsync(iso_mount + '/boot/' + shortname, target + '/boot')
+    if os.path.isdir(iso_mount + "/boot/" + shortname):
+        exec_rsync(iso_mount + "/boot/" + shortname, target + "/boot")
     else:
-        kernel = search_file('vmlinuz', iso_mount)
+        kernel = search_file("vmlinuz", iso_mount)
         if kernel is None:
             # compat for releases < 2011.12
-            kernel = search_file('linux26', iso_mount)
+            kernel = search_file("linux26", iso_mount)
 
         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, '') + '/'
+        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):
-    """Update the grml version file on a cd
-    Returns true if version was updated successfully,
-    False if grml-version does not exist yet on the mountpoint
-
-    @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)
-    if target_grml_version_file:
-        iso_grml_version_file = search_file('grml-version', iso_mount)
-        if not 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 = {}
-            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)
-                if flavour in list(iso_versions.keys()):
-                    print(iso_versions.pop(flavour))
-                else:
-                    print(line.strip())
-            fileinput.close()
-
-            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])
-        except IOError:
-            logging.warn("Warning: Could not write file")
-        finally:
-            iso_file.close()
-            target_file.close()
-        return True
-    else:
-        return False
-
-
 def copy_grml_files(grml_flavour, iso_mount, target):
     """copy some minor grml files to a given target
 
@@ -989,7 +1086,7 @@ def copy_grml_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"""
 
-    grml_target = target + '/grml/'
+    grml_target = target + "/grml/"
     execute(mkdir, grml_target)
 
     grml_prefixe = ["GRML", "grml"]
@@ -999,7 +1096,9 @@ def copy_grml_files(grml_flavour, iso_mount, target):
             exec_rsync(filename, grml_target)
             break
     else:
-        logging.warn("Warning: could not find flavour directory for %s ", grml_flavour)
+        logging.warning(
+            "Warning: could not find flavour directory for %s ", grml_flavour
+        )
 
 
 def copy_addons(iso_mount, target):
@@ -1008,10 +1107,10 @@ 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"""
 
-    addons = target + '/boot/addons/'
+    addons = target + "/boot/addons/"
     execute(mkdir, addons)
 
-    for addon_file in glob.glob(iso_mount + '/boot/addons/*'):
+    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)
@@ -1024,20 +1123,24 @@ def build_loopbackcfg(target):
     @target: target directory
     """
 
-    grub_dir = '/boot/grub/'
+    grub_dir = "/boot/grub/"
     mkdir(os.path.join(target, grub_dir))
 
-    f = open(target + grub_dir + 'loopback.cfg', 'w')
+    f = open(target + grub_dir + "loopback.cfg", "w")
 
     f.write("# grml2usb generated grub2 configuration file\n")
     f.write("source /boot/grub/header.cfg\n")
 
-    for defaults in glob.glob(target + os.path.sep + grub_dir + os.path.sep + "*_default.cfg"):
+    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")
 
-    for ops in glob.glob(target + os.path.sep + grub_dir + os.path.sep + "*_options.cfg"):
+    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")
@@ -1086,33 +1189,36 @@ def copy_bootloader_files(iso_mount, target, 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)
 
-    grub_target = target + '/boot/grub/'
+    grub_target = target + "/boot/grub/"
     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, required=True)
+    exec_rsync(logo, syslinux_target + "logo.16")
 
-    bootx64_efi = search_file('bootx64.efi', iso_mount)
+    bootx64_efi = search_file("bootx64.efi", iso_mount)
     if bootx64_efi:
-        mkdir(target + '/efi/boot/')
-        exec_rsync(bootx64_efi, target + '/efi/boot/bootx64.efi')
+        mkdir(target + "/efi/boot/")
+        exec_rsync(bootx64_efi, target + "/efi/boot/bootx64.efi")
 
-    efi_img = search_file('efi.img', iso_mount)
+    efi_img = search_file("efi.img", iso_mount)
     if efi_img:
-        mkdir(target + '/boot/')
-        exec_rsync(efi_img, target + '/boot/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)]:
+    execute(mkdir, target + "/conf/")
+    glob_and_copy(iso_mount + "/conf/bootfile_*", target + "/conf/")
+
+    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")
-    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")
@@ -1121,15 +1227,29 @@ def copy_bootloader_files(iso_mount, target, grml_flavour):
         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':
+            "       ... 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)
 
     for modules_dir in options.syslinuxlibs + SYSLINUX_LIBS:
@@ -1139,12 +1259,14 @@ def copy_bootloader_files(iso_mount, target, grml_flavour):
             break
 
     # copy the addons_*.cfg file to the new syslinux directory
-    glob_and_copy(iso_mount + source_dir + 'addon*.cfg', syslinux_target)
+    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')
+    search_and_copy(
+        "hidden.cfg", iso_mount + source_dir, syslinux_target + "new_" + "hidden.cfg"
+    )
 
     # copy all grub files from ISO
-    glob_and_copy(iso_mount + '/boot/grub/*', grub_target)
+    glob_and_copy(iso_mount + "/boot/grub/*", grub_target)
 
     # finally (after all GRUB files have been installed) build static loopback.cfg
     build_loopbackcfg(target)
@@ -1172,7 +1294,7 @@ def install_iso_files(grml_flavour, iso_mount, device, target):
             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)
@@ -1184,6 +1306,7 @@ def install_iso_files(grml_flavour, iso_mount, device, target):
             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()
 
@@ -1200,9 +1323,8 @@ def get_device_from_partition(partition):
 
 
 def get_flavour(flavour_str):
-    """Returns the flavour of a grml version string
-    """
-    return re.match(r'[\w-]*', flavour_str).group()
+    """Returns the flavour of a grml version string"""
+    return re.match(r"[\w-]*", flavour_str).group()
 
 
 def identify_grml_flavour(mountpath):
@@ -1211,13 +1333,17 @@ def identify_grml_flavour(mountpath):
     @mountpath: path where the grml ISO is mounted to
     @return: name of grml-flavour"""
 
-    version_files = search_file('grml-version', mountpath, lst_return=True)
+    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=...")
+            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()
@@ -1228,7 +1354,7 @@ def identify_grml_flavour(mountpath):
     for version_file in version_files:
         tmpfile = None
         try:
-            tmpfile = open(version_file, 'r')
+            tmpfile = open(version_file, "r")
             for line in tmpfile.readlines():
                 flavours.append(get_flavour(line))
         finally:
@@ -1263,37 +1389,38 @@ def handle_grub_config(grml_flavour, device, target):
 
     logging.debug("Updating grub configuration")
 
-    grub_target = target + '/boot/grub/'
-    secureboot_target = target + '/EFI/ubuntu/'
+    grub_target = target + "/boot/grub/"
 
-    bootid_re = re.compile("bootid=[\w_-]+")
-    live_media_path_re = re.compile("live-media-path=[\w_/-]+")
+    bootid_re = re.compile(r"bootid=[\w_-]+")
+    live_media_path_re = re.compile(r"live-media-path=[\w_/-]+")
 
     bootopt = get_bootoptions(grml_flavour)
 
     remove_regexes = []
-    option_re = re.compile(r'(.*/boot/.*(linux26|vmlinuz).*)')
+    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') + glob.glob(secureboot_target + '*.cfg'):
+    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)
+                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)
+                    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()), ' ')
+                    line = line.replace(" {} ".format(bootopt.strip()), " ")
                     if line.endswith(bootopt):
-                        line = line[:-len(bootopt)]
-                line = line.rstrip() + r' bootid=%s %s ' % (UUID, bootopt)
+                        line = line[: -len(bootopt)]
+                line = line.rstrip() + r" bootid=%s %s " % (UUID, bootopt)
                 for regex in remove_regexes:
-                    line = regex.sub(' ', line)
+                    line = regex.sub(" ", line)
             print(line)
         fileinput.close()
 
@@ -1339,7 +1466,7 @@ def get_flavour_filename(flavour):
 
     @flavour: grml flavour
     """
-    return flavour.replace('-', '_')
+    return flavour.replace("-", "_")
 
 
 def adjust_syslinux_bootoptions(src, flavour):
@@ -1350,33 +1477,33 @@ def adjust_syslinux_bootoptions(src, flavour):
     @flavour: grml flavour
     """
 
-    append_re = re.compile("^(\s*append.*/boot/.*)$", re.I)
+    append_re = re.compile(r"^(\s*append.*/boot/.*)$", re.I)
     # 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:
-        option_re = re.compile(r'/boot/.*/(initrd.gz|initrd.img)')
+        option_re = re.compile(r"/boot/.*/(initrd.gz|initrd.img)")
 
         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):
         # 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:
-                line = regex.sub(' ', line)
+                line = regex.sub(" ", line)
         sys.stdout.write(line)
     fileinput.close()
 
@@ -1385,7 +1512,7 @@ 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)
@@ -1429,7 +1556,7 @@ def remove_default_entry(filename):
 
     @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
@@ -1444,26 +1571,26 @@ def handle_syslinux_config(grml_flavour, target):
     @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)
-    syslinux_cfg = syslinux_target + 'syslinux.cfg'
+    syslinux_cfg = syslinux_target + "syslinux.cfg"
 
     # install main configuration only *once*, no matter how many ISOs we have:
-    syslinux_config_file = open(syslinux_cfg, 'w')
+    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()
 
-    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)
     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)
 
@@ -1475,25 +1602,24 @@ def handle_syslinux_config(grml_flavour, target):
     else:
         new_hidden_file = "%s/%s_hidden.cfg" % (syslinux_target, flavour_filename)
         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)
-        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)
 
     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_grml_cfg = "%s/%s_grml.cfg" % (syslinux_target, flavour_filename)
 
     if os.path.isfile(defaults_file):
-
         # remove default menu entry in menu
         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)
@@ -1511,41 +1637,59 @@ def handle_secure_boot(target, efi_img):
     @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))
+    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'])
+        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)
 
-    ubuntu_cfg = search_file('grub.cfg', efi_mountpoint + '/EFI/ubuntu')
-    logging.debug("ubuntu_cfg = %s" % ubuntu_cfg)
-    if not ubuntu_cfg:
-        logging.info("No /EFI/ubuntu/grub.cfg found inside EFI image, looks like Secure Boot support is missing.")
+    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 + '/efi/ubuntu')
-        logging.debug("exec_rsync(%s, %s + '/efi/ubuntu/grub.cfg')" % (ubuntu_cfg, target))
-        exec_rsync(ubuntu_cfg, target + '/efi/ubuntu/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')
+        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')
+        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)
+        logging.debug("Unmounted %s" % efi_mountpoint)
         os.rmdir(efi_mountpoint)
-        logging.debug('Removed directory %s' % efi_mountpoint)
+        logging.debug("Removed directory %s" % efi_mountpoint)
     except Exception:
-        logging.critical('RuntimeError while umount %s' % efi_mountpoint)
+        logging.critical("RuntimeError while umount %s" % efi_mountpoint)
         sys.exit(1)
 
 
@@ -1586,18 +1730,22 @@ def install(image, device):
     iso_mountpoint = image
     remove_image_mountpoint = False
     if os.path.isdir(image):
-        if options.force or os.path.exists(os.path.join(image, 'live')):
+        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':
+            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)
-        iso_mountpoint = tempfile.mkdtemp(prefix="grml2usb", dir=os.path.abspath(options.tmpdir))
+        iso_mountpoint = tempfile.mkdtemp(
+            prefix="grml2usb", dir=os.path.abspath(options.tmpdir)
+        )
         register_tmpfile(iso_mountpoint)
         remove_image_mountpoint = True
         try:
@@ -1612,7 +1760,7 @@ def install(image, device):
         if remove_image_mountpoint:
             try:
                 remove_mountpoint(iso_mountpoint)
-            except CriticalException as error:
+            except CriticalException:
                 cleanup()
                 raise
 
@@ -1632,20 +1780,16 @@ def install_grml(mountpoint, device):
         register_tmpfile(device_mountpoint)
         remove_device_mountpoint = True
         try:
-            check_for_fat(device)
-            if not options.skipbootflag:
-                check_boot_flag(device)
-
             set_rw(device)
-            mount(device, device_mountpoint, ['-o', 'utf8,iocharset=iso8859-1'])
-        except CriticalException as error:
+            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")
-            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:
@@ -1654,15 +1798,14 @@ def install_grml(mountpoint, device):
 
 
 def remove_mountpoint(mountpoint):
-    """remove a registered mountpoint
-    """
+    """remove a registered mountpoint"""
 
     try:
         unmount(mountpoint, "")
         if os.path.isdir(mountpoint):
             os.rmdir(mountpoint)
             unregister_tmpfile(mountpoint)
-    except CriticalException as error:
+    except CriticalException:
         cleanup()
         raise
 
@@ -1678,33 +1821,37 @@ def handle_mbr(device):
 
     mbr_device, partition_number = get_device_from_partition(device)
     if partition_number is None:
-        logging.warn("Could not detect partition number, not activating partition")
+        logging.warning("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
-        logging.info("Detected loop device - using %s as MBR device therefore", mbr_device)
+        logging.info(
+            "Detected loop device - using %s as MBR device therefore", mbr_device
+        )
 
-    mbrcode = GRML2USB_BASE + '/mbr/mbrldr'
+    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')
+        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"' % l for l 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)
+            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'
+        mbrcode = GRML2USB_BASE + "/mbr/mbrldr"
 
     try:
         install_mbr(mbrcode, mbr_device, partition_number, True)
@@ -1721,17 +1868,21 @@ 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:
-            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:
             print("Forcing mkfs.fat16 on %s as requested via option --force." % device)
         else:
             # make sure the user is aware of what he is doing
-            f = 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":
-                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:
@@ -1749,11 +1900,20 @@ def handle_vfat(device):
             sys.exit(1)
 
     if options.skipusbcheck:
-        logging.info("Not checking for removable USB device as requested via option --skip-usb-check.")
+        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)
+    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)
@@ -1765,7 +1925,11 @@ 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:
-    if device.startswith("/mnt/external") or device.startswith("/mnt/usb") and not options.force:
+    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.")
@@ -1798,7 +1962,9 @@ def handle_bootloader(device):
 
     # 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:
@@ -1823,14 +1989,18 @@ def check_programs():
         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)")
+            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"):
-            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"):
@@ -1845,12 +2015,14 @@ def load_loop():
         logging.critical("Hint: is /sbin missing in PATH?")
         sys.exit(1)
 
-    proc = subprocess.Popen(["modprobe", "loop"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    proc = subprocess.Popen(
+        ["modprobe", "loop"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
+    )
     proc.communicate()
 
 
 def main():
-    """Main function [make pylint happy :)]"""
+    """Main invocation"""
 
     try:
         if options.version:
@@ -1879,16 +2051,21 @@ def main():
 
         # specified arguments
         device = os.path.realpath(args[len(args) - 1])
-        isos = args[0: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.")
+                    logging.warning(
+                        "Warning: installing on partition number >4, booting *might* fail depending on your system."
+                    )
 
         # provide upgrade path
         handle_compat_warning(device)
 
+        if not options.skipbootflag:
+            check_boot_flag(device)
+
         # check for vfat partition
         handle_vfat(device)
 
@@ -1906,13 +2083,23 @@ def main():
 
         handle_bootloader(device)
 
-        logging.info("Note: grml flavour %s was installed as the default booting system.", GRML_DEFAULT)
+        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)
+            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)
+        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))
index 42b4bcd..ec8a197 100644 (file)
@@ -3,7 +3,7 @@ grml2usb(8)
 
 Name
 ----
-grml2usb - install Grml ISO(s) on usb device for booting
+grml2usb - install Grml ISO(s) on USB device for booting
 
 Synopsis
 --------
@@ -17,7 +17,7 @@ Important! The Grml team does not take responsibility for loss of any data!
 Introduction
 ------------
 
-grml2usb installs Grml on a given partition of your usb device and makes
+grml2usb installs Grml on a given partition of your USB device and makes
 it bootable. It provides multiboot ISO support, meaning you can specify
 several Grml ISOs on the command line at once and select the Grml
 flavour you would like to boot on the bootprompt then. Note that the
@@ -132,7 +132,7 @@ multiple entries for removing different bootoptions at once.
 
   *\--skip-bootflag*::
 
-Do not check for presence of bootflag on target device.
+Do not check for presence of boot flag on target device.
 
   *\--skip-grub-config*::
 
@@ -197,7 +197,7 @@ Developers Corner
 -----------------
 
 [[directory-layout]]
-Directory layout on usb device
+Directory layout on USB device
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
   boot/ ->
@@ -214,12 +214,6 @@ Directory layout on usb device
     |   |-- grml64
     |   |   |-- linux26      [Kernel]
     |   |   |-- initrd.gz    [initramfs]
-    |   |-- grml-medium
-    |   |   |-- linux26      [...]
-    |   |   |-- initrd.gz
-    |   |-- grml64-medium
-    |   |   |-- linux26
-    |   |   |-- initrd.gz
     |   |-- grml-small
     |   |   |-- linux26
     |   |   |-- initrd.gz
@@ -256,11 +250,8 @@ Directory layout on usb device
     |-- grml/
     |   |-- filesystem.module    [module specifying which squashfs should be used for grml]
     |   `-- grml.squashfs        [squashfs file for grml]
-    |-- grml-medium/
-    |   |-- filesystem.module    [module specifying which squashfs should be used for grml-medium]
-    |   `-- grml-medium.squashfs [squashfs file for grml-medium]
     |-- grml-small/
-    |   |-- filesystem.module    [module specifying which squashfs should be used for grml-medium]
+    |   |-- filesystem.module    [module specifying which squashfs should be used for grml-small]
     |   `-- grml-small.squashfs  [squashfs file for grml-small]
     `-- ...
 
@@ -315,9 +306,9 @@ See http://www.methods.co.nz/asciidoc/userguide.html
 [horizontal]
 *Error message*:: ran out of input data. System halted
 
-*Reason*:: Everything OK, except for the filesystem used on your usb device. So
+*Reason*:: Everything OK, except for the filesystem used on your USB device. So
 instead of fat16 you are using for example fat32. Fix: use the appropriate
-filesystem (fat16 for usb pens usually). The Bootsplash might be displayed, the
+filesystem (fat16 for USB flash drive usually). The Bootsplash might be displayed, the
 kernel loads but you very soon get the error message.
 
 *Error message*:: Invalid operating system
@@ -427,7 +418,7 @@ What's grml2iso?
 ~~~~~~~~~~~~~~~~
 
 grml2iso is a script which uses grml2usb to generate a multiboot ISO out of
-several grml ISOs. See 'man grml2iso' for further details.
+several Grml ISOs. See 'man grml2iso' for further details.
 
 [[menu-lst]]
 Why is there a menu.lst and a grub.cfg inside /boot/grub/?
diff --git a/pytest.ini b/pytest.ini
new file mode 100644 (file)
index 0000000..6bba0a2
--- /dev/null
@@ -0,0 +1,3 @@
+[pytest]
+markers =
+    check_for_usbdevice
diff --git a/setup.cfg b/setup.cfg
new file mode 100644 (file)
index 0000000..d27b669
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,3 @@
+[flake8]
+ignore = E203, E501, W503
+filename = ./grml2usb, *.py
index 8857bb4..046ec20 100755 (executable)
@@ -16,7 +16,7 @@ cat > "${DIR}"/README << EOF
 README
 ------
 
-grml2usb installs grml ISO(s) on usb device for booting.
+grml2usb installs Grml ISO(s) on USB device for booting.
 
 This tarball provides all the necessary files for running grml2usb.
 Execute the script install.sh with root permissions to install the
@@ -116,7 +116,7 @@ rm grml2usb-$VERSION/grml2iso.8.txt
 
 # binaries, grub
 cp grml2usb grml2iso mbr/mbrldr mbr/mbrmgr grub/* grml2usb-$VERSION/
-sed -i -e "s/PROG_VERSION='\*\*\*UNKNOWN\*\*\*'/PROG_VERSION='${VERSION}'/" grml2usb-$VERSION/grml2usb
+sed -i -e "s/PROG_VERSION = '\*\*\*UNKNOWN\*\*\*'/PROG_VERSION = '${VERSION}'/" grml2usb-$VERSION/grml2usb
 
 tar zcf grml2usb.tgz "${DIR}"
 
diff --git a/test/grml2usb.py b/test/grml2usb.py
new file mode 120000 (symlink)
index 0000000..07ca4fc
--- /dev/null
@@ -0,0 +1 @@
+../grml2usb
\ No newline at end of file
diff --git a/test/grml2usb_test.py b/test/grml2usb_test.py
new file mode 100644 (file)
index 0000000..56b2085
--- /dev/null
@@ -0,0 +1,40 @@
+"""
+grml2usb basic pytests
+~~~~~~~~~~~~~~~~~~~~~~
+
+This script contains basic "unit" tests, implemented for and executed with pytest.
+
+Requirements:
+pytest (pip install pytest)
+
+Runwith:
+<project root>$ pytest [-m {basic}]
+
+:copyright: (c) 2020 by Manuel Rom <roma@synpro.solutions>
+:license: GPL v2 or any later version
+:bugreports: http://grml.org/bugs/
+"""
+
+
+import importlib
+
+import pytest
+
+grml2usb = importlib.import_module("grml2usb", ".")
+
+
+@pytest.mark.check_for_usbdevice
+def test_extract_device_name():
+    """Assert, that 'extract_device_name' returns a device name for a given path"""
+    assert grml2usb.extract_device_name("/dev/sda") == "sda"
+    assert grml2usb.extract_device_name("/dev/sdb") == "sdb"
+    assert grml2usb.extract_device_name("/dev/sdb4") == "sdb"
+
+
+@pytest.mark.check_for_usbdevice
+def test_extract_device_name_invalid():
+    """Assert, that 'extract_device_name' raises an Error, when given an incorrect string"""
+    with pytest.raises(AttributeError):
+        assert grml2usb.extract_device_name("/dev")
+    with pytest.raises(AttributeError):
+        assert grml2usb.extract_device_name("foobar")