#!/bin/bash
# flash-knoppix - install KNOPPIX on flash disk
# (C) Klaus Knopper 2008-2014
# License: GPL V2
#
# Changes for Knoppix 7.0:
# - Replacing Xdialog (GTK1) by accessible zenity
# - Skip copy of persistent home when installing from flash to flash

PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin:/usr/local/bin"
export PATH

[ -r /etc/default/locale ] && . /etc/default/locale
[ -r /etc/sysconfig/i18n ] && . /etc/sysconfig/i18n

case "$LANG" in
 de*)
  MSG_INTRO="flash-knoppix: Mit diesem Programm wird eine bootfhige Kopie von\nKNOPPIX auf einem USB-Datenspeicher oder einer Festplatte und\noptional ein Overlay zur Speicherung persnlicher Daten auf dem\nZielmedium erzeugt, als Container-Datei oder separate Partition,\nmit oder ohne Verschlsselung.\n\nBitte whlen Sie die Optionen aus:" 
  MSG_ALLOWHD="Installation auch auf internen Festplatten erlauben"
  MSG_NOALLOWHD="Installation nur auf Wechselmedien erlauben"
  MSG_TARGET="flash-knoppix: KNOPPIX komprimiert auf Flash Disk installieren.\n\nBitte das gewnschte Ziel angeben:"
  INFO_TARGET="Ziel:"
  MSG_READY="Fertig.
Sie knnen den Datentrger jetzt entfernen."
  MSG_MOUNTED="ist noch gemountet."
  MSG_CHECKING="Untersuche"
  MSG_SELECT="Bitte das Verzeichnis angeben, in dem sich die KNOPPIX-Daten befinden."
  MSG_BOOTREC="Bootrecord einrichten auf" 
  MSG_NOTFOUND="Keine Bootkonfiguration gefunden (weder boot/isolinux/isolinux.cfg noch boot/syslinux/syslinux.cfg), der Bootlader wird vermutlich nicht funktionieren."
  MSG_WRITING="Schreibe Daten..."
  MSG_SIZE_OVERLAY="KNOPPIX kann eine \"Overlay\"-Datei verwenden, um Daten permanent zu speichern.\nWenn gewnscht, bitte Gre angeben (mindestens 200MB, frei: %sMB)."
  MSG_SIZE_OVERLAY_ADVANCED="Bitte die Gre der Overlay-Partition angeben, auf der Ihre\nDaten permanent gespeichert werden.\n(mindestens 400MB, frei: %sMB)."
  MSG_CRYPT_OVERLAY="Sollen Ihre eigenen Daten mit AES256 (=Advanced Encryption Standard 256bit, s.a.\nhttp://csrc.nist.gov/encryption/aes/) verschlsselt werden?\n\nHierzu ist die Eingabe eines Passwortes beim Einrichten sowie beim\nSystemstart erforderlich.\n\nOhne Kenntnis des korrekten Passwortes ist der Zugriff auf die Daten\nnicht mglich, dadurch sind die mglicherweise sensiblen Daten einerseits bei\nVerlust des Datentrgers vor unbefugtem Zugriff geschtzt, andererseits sind\nsie bei vergessenem Passwort auch nicht mehr vefgbar."
  MSG_PASSWORD="AES256 Verschlsselungs Passwort (mindestens 8 Zeichen):"
  MSG_PASSWORD2="Bitte Passwort zur Sicherheit noch einmal eingeben:"
  MSG_PASSWORDS_NOMATCH="Die beiden Passwrter stimmen nicht berein oder sind zu kurz.\nBitte noch einmal versuchen."
  MSG_OVERLAY="Erzeuge Overlay [%s MB]..."
  MSG_QUESTION_PARTITION="Datentrger partitionieren und formatieren?\nWARNUNG: Alle Daten werden dabei gelscht!\nNein = nur KNOPPIX-Daten auf ggf. vorhandene Partition kopieren"
  MSG_QUESTION_PARTITION_ADVANCED="Datentrger neu partitionieren und formatieren?
WARNUNG: Alle Daten werden dabei gelscht!\nNein = Abbrechen"
  MSG_QUESTION_REALLY_FORMAT="Mchten Sie dies wirklich tun?
WARNUNG: Alle Daten auf dem Datentrger werden dabei gelscht!"
  MSG_FORMAT="Formatiere"
  MSG_PARTITION="Partitioniere"
  MSG_ERROR_NODISK="Keine Flashdisk(s) gefunden."
  MSG_ERROR_NODISK="Keine Flashdisk(s) gefunden."
  MSG_ERROR_INCOMPATIBLE="Das Flash-Medium ist nicht richtig partitioniert.\nDie erste (primre) Partition muss DOS/FAT32 sein."
  MSG_FAT_EXISTS="Ein FAT32-Dateisystem existiert auf"
  MSG_KNOPPIX_EXISTS="Ein installiertes KNOPPIX-System existiert auf"
  MSG_OVERLAY_IMAGE_EXISTS="Ein Overlay-Image ist vorhanden."
  MSG_ENCRYPTED_OVERLAY_IMAGE_EXISTS="Ein verschlsseltes Overlay-Image ist vorhanden."
  MSG_OVERLAY_PARTITION_EXISTS="Eine Overlay-Partition ist vorhanden."
  MSG_UPDATE_SELECT="Mchten Sie die bestehende KNOPPIX-Installation aktualisieren?"
  MSG_UPDATE_CLEAN="NUR persnliche Daten und Einstellungen (\$HOME) behalten. (empfohlen)"
  MSG_UPDATE_KEEP="ALLE, auch inkompatible Modifikationen behalten (nicht empfohlen)"
  MSG_UPDATE_DELETE="Alles lschen / Neu (alle alten Daten gehen verloren!)"
  MSG_METHOD_SELECT="Bitte whlen Sie die Installationsmethode:"
  METHOD_NEW_IMAGE="Installation auf FAT32 mit (optionaler) Overlay-Datei <4GB."
  METHOD_NEW_PARTITION="Installation auf FAT32 mit zustzlicher Overlay-Partition. (empfohlen)"
  METHOD_NOTHING="Kein Overlay (read-only wie CD/DVD)."
  MSG_SIZE_OVERLAY_PARTITION="Bitte geben Sie die gewnschte Gre der Overlay-Partition ein (MB)."
  MSG_ERROR_TOOSMAL="Die Overlay-Gre ist zu klein!\n(mindestens 400MB erforderlich)"
  MSG_ERROR_BOOT_TOOSMAL="Die Gre der Bootpartition ist zu klein fr das Update!"
  MSG_ERROR_CANNOT_MOUNT_OVERLAY="Kann das Overlay auf dem Zieldatentrger nicht einbinden!\nFalsches Passwort?"
  ;;
 *)
  MSG_INTRO="flash-knoppix: This program creates a bootable copy of KNOPPIX\non an USB flash disk or hard disk, optionally with an overlay\nfor storing personal data in form of a container or separate\npartition on the target media, with or without encryption.\n\nPlease select options:" 
  MSG_ALLOWHD="Allow installation also on fixed hard disks"
  MSG_NOALLOWHD="Allow installation only on removable devices"
  MSG_TARGET="flash-knoppix: Install KNOPPIX compressed to flash disk.\n\nPlease select desired target device:"
  INFO_TARGET="Target:"
  MSG_READY="Finished.
You can now remove device from drive."
  MSG_MOUNTED="is already mounted."
  MSG_CHECKING="Checking"
  MSG_SELECT="Please enter directory containing KNOPPIX files."
  MSG_BOOTREC="Creating boot record on"
  MSG_NOTFOUND="No boot configuration found (no boot/isolinux/isolinux.cfg), bootloader will probably not work."
  MSG_WRITING="Writing data..."
  MSG_SIZE_OVERLAY="KNOPPIX can use an \"Overlay\"-file for storing data permanently.\nIf you want this, please enter desired size (minimum 200MB, free: %sMB)."
  MSG_SIZE_OVERLAY_ADVANCED="Please enter the size of the Overlay partition\nfor storing your data permanently.\n(minimum 400MB, free: %sMB)."
  MSG_CRYPT_OVERLAY="Would you like to encrypt the overlay using AES256 (=Advanced Encryption\nStandard 256bit, http://csrc.nist.gov/encryption/aes/)?\n\nFor this type of encryption, entering a password for creation as well as\nat system start is necessary.\n\nWithout knowing this password, reading saved data is not possible.\nBecause of that, encrypted data is still safe against unauthorized\naccess in case the storage device containig the data gets lost or stolen.\nOn the other hand, all saved data is inaccessible if you forget the password,\nand there is no way to recover."
  MSG_PASSWORD="AES256 Encryption Key (minimum 8 chars):"
  MSG_PASSWORD2="Please enter the same password again, just to be sure:"
  MSG_PASSWORDS_NOMATCH="Passwords are not identical, or just too short.\nPlease try again."
  MSG_OVERLAY="Creating overlay [%s MB]..."
  MSG_QUESTION_PARTITION="Partition and format device?\nCAUTION: All data will be erased!\nNo = only copy KNOPPIX data to existing partition"
  MSG_QUESTION_PARTITION_ADVANCED="Repartition and format device?\nCAUTION: All data will be erased!\nNo = Exit program"
  MSG_QUESTION_REALLY_FORMAT="Do you really want to do this?\nWARNING: ALL DATA ON WILL BE ERASED!"
  MSG_FORMAT="Formatting"
  MSG_PARTITION="Partitioning"
  MSG_ERROR_NODISK="No flash disk(s) found."
  MSG_ERROR_INCOMPATIBLE="Flashdisk is not partitioned properly.\nThe first (primary) partition must be DOS/FAT32."
  MSG_FAT_EXISTS="A FAT32 file system exists on"
  MSG_KNOPPIX_EXISTS="An installed KNOPPIX system exists on"
  MSG_OVERLAY_IMAGE_EXISTS="An Overlay image is present."
  MSG_ENCRYPTED_OVERLAY_IMAGE_EXISTS="An encrypted Overlay image is present."
  MSG_OVERLAY_PARTITION_EXISTS="An overlay partition is present."
  MSG_METHOD_SELECT="Please select installation method:"
  MSG_UPDATE_SELECT="Do you want to update the currently installed KNOPPIX installation?"
  MSG_UPDATE_CLEAN="Keep ONLY personal data and settings (\$HOME) (recommended)"
  MSG_UPDATE_KEEP="Keep ALL modifications, even if incompatible (not recommended)"
  MSG_UPDATE_DELETE="Delete everything / New (all old data will be lost!)"
  METHOD_NEW_IMAGE="Installation on FAT32 with (optional) overlay file <4GB."
  METHOD_NEW_PARTITION="Installation on FAT32 with additional overlay partition."
  METHOD_NOTHING="No Overlay (read-only like CD/DVD)."
  MSG_SIZE_OVERLAY_PARTITION="Please enter desired size of overlay partition (MB)."
  MSG_ERROR_BOOT_TOOSMAL="Size of boot partition is too small for update!"
  MSG_ERROR_TOOSMAL="Overlay size is too small!\n(minimum 400MB required)"
  MSG_ERROR_CANNOT_MOUNT_OVERLAY="Cannot mount overlay from target device!\nWrong password?"
;;
esac

TMP="/tmp/flash-knoppix.$$.tmp"
TMPMOUNT="/tmp/flash-knoppix.$$.tmpmount"
TMPMOUNT2="/tmp/flash-knoppix.$$.tmpmount2"
TMPSRCMOUNT="/tmp/flash-knoppix.$$.src.tmpmount"
PARTTYPE=msdos
# PARTTYPE=gpt

FLASH=""
MAPDEV="home$$"

TITLE="flash-knoppix"

if [ -n "$DISPLAY" ] && type -p zenity >/dev/null 2>&1; then
 ICON="/usr/share/icons/flash-knoppix.xpm"
 [ -r "$ICON" ] && ICON="--window-icon=$ICON" || ICON=""
fi

# dialog_yesno title info
function dialog_yesno(){
 local title="$1" info="$2"
 if [ -n "$DISPLAY" ]; then
  zenity $ICON --question --no-wrap --width=600 --title="$title" --text="$info" 1>&2 2>/dev/null
 else
  dialog --backtitle "$TITLE" --title "$title" --yesno "$info" 23 75
 fi
}

# dialog_info title info
function dialog_info(){
 local title="$1" info="$2"
 if [ -n "$DISPLAY" ]; then
  zenity $ICON --info --no-wrap --width=600 --title="$title" --text="$info" 1>&2 2>/dev/null
 else
  dialog --backtitle "$TITLE" --title "$title" --msgbox "$info" 23 65
 fi
}

# dialog_info_timeout title info
function dialog_info_timeout(){
 local title="$1" info="$2"
 if [ -n "$DISPLAY" ]; then
  zenity $ICON --timeout=5 --info --no-wrap --width=600 --title="$title" --text="$info" 1>&2 2>/dev/null
 else
  dialog --timeout 5 --backtitle "$TITLE" --title "$title" --msgbox "$info" 23 65
 fi
}

# dialog_scale title info [min [max [current]]]
function dialog_scale(){
 local title="$1" info="$2" min="$3" max="$4" current="$5"
 if [ -n "$DISPLAY" ]; then
  zenity $ICON --scale --width=550 --title="$title" --text="$info" --min-value="$min" --max-value="$max" --step=50 --value="$current" 1>&2 2>/dev/null
 else
  dialog --backtitle "$TITLE" --title "$title" --inputbox "$info" 23 75 "$current"
 fi
}

function dialog_password(){
 local title="$1" info="$2"
 if [ -n "$DISPLAY" ]; then
  zenity $ICON --entry --width=550 --title="$title" --text="$info" 1>&2 2>/dev/null
 else
  dialog --backtitle "$TITLE" --title "$title" --inputbox "$info" 23 75 "$5"
 fi
}

# dialog_error title info
function dialog_error(){
 local title="$1" info="$2"
 if [ -n "$DISPLAY" ]; then
  zenity $ICON --error --no-wrap --width=600 --title="$title" --text="$info" 1>&2 2>/dev/null
 else
  dialog --backtitle "$TITLE" --title "$title" --msgbox "$info" 23 65
 fi
}

# dialog_menu title wrapped_text menuitem1 description1 ...
function dialog_menu(){
 local title="$1" info="$2"
 shift 2
 if [ -n "$DISPLAY" ]; then
  for item in "$@"; do echo "$item"; done | { zenity $ICON --list --width=550 --height=280 --hide-header --title="$title" --text="$info" --column="" --column="" 1>&2 2>/dev/null; }
 else
  dialog --backtitle "$TITLE" --title "$title" --menu "$info" 23 75 8 "$@"
 fi
}

# dialog_progress title info
function dialog_progress(){
 local title="$1" info="$2"
 shift 2
 if [ -n "$DISPLAY" ]; then
  zenity $ICON --progress --width=550 --title="$title" --auto-close --no-cancel --text="$info" 2>/dev/null
 else
  dialog --backtitle "$TITLE" --title "$title" --gauge "$info" 23 75 8 "$@"
 fi
}

# dialog_gauge title info
function dialog_gauge(){
 local title="$1" info="$2"
 shift 2
 if [ -n "$DISPLAY" ]; then
  zenity $ICON --progress --width=550 --title="$title" --auto-close --no-cancel --pulsate --text="$info" 2>/dev/null
 else
  dialog --backtitle "$TITLE" --title "$title" --gauge "$info" 23 75 8 "$@"
 fi
}

# dialog_dselect title info
function dialog_dselect(){
 local title="$1" info="$2"
 if [ -n "$DISPLAY" ]; then
  zenity $ICON --file-selection --filename=. --directory --width=550 --title="$title" 1>&2 2>/dev/null
 else
  dialog --backtitle "$TITLE" --title "$title" --dselect . 23 75
 fi
}

# dialog_options title info checked1 option1 checked2 option2 ...

# zenity --list --checklist --hide-header --text="Bitte Option angeben:" --column=option --column=beschreibung TRUE Eins FALSE Zwei TRUE Drei
function dialog_options(){
 local title="$1" info="$2"
 shift 2
 if [ -n "$DISPLAY" ]; then
  for item in "$@"; do echo "$item"; done | { zenity $ICON --list --checklist --width=550 --height=280 --hide-header --title="$title" --text="$info" --column="" --column="" 1>&2 2>/dev/null; }
 else
  dialog --backtitle "$TITLE" --title "$title" --menu "$info" 23 75 8 "$@"
 fi
}

ERROR=""

bailout(){
 killgauge
 cd $HOME
 umount "$TMPMOUNT" 2>/dev/null
 umount -d "$TMPMOUNT2" 2>/dev/null
 umount -d "$TMPSRCMOUNT" 2>/dev/null
 for i in /dev/loop*; do losetup -d "$i"; done >/dev/null 2>&1
 /sbin/cryptsetup remove "$MAPDEV" >/dev/null 2>&1
 # eject "$FLASH" 2>/dev/null
 rmdir "$TMPMOUNT" 2>/dev/null
 rmdir "$TMPSRCMOUNT" 2>/dev/null
 case "$1" in
  0) # Normal end of program
   dialog_info "$TITLE" "$DEVICE_INFO\n\n$MSG_READY";;
  1) # Cancel with no message
   true;;
  *) # Error
   dialog_error "$TITLE" "$DEVICE_INFO\n\n$ERROR";;
 esac
 rm -f "$TMP" "$TMP.done" "$TMP.err"
 exit $1
}

# progress info total_size_KB destination-files-or-dirs...
# a real size based progress bar
function progress(){
 local info="$1" total_size="$2"; shift 2
 if ! [ -n "$total_size" -a "$total_size" -gt 0 ]; then
  total_size=4000000
 fi
 rm -f "$TMP.done"
 while [ ! -e "$TMP.done" ]; do
  du -sk "$@" 2>/dev/null | awk '{size+=$1}END{print int(size * 100 / '"$total_size"')}'
  sleep 1
 done | dialog_progress "$TITLE" "$info" &
}

# gauge 
function gauge(){
 rm -f "$TMP.done"
 status=0
 while [ ! -e "$TMP.done" ]; do echo "$((status / 100))" ; status="`expr \( 10000 - $status \) / 100 + $status`"; sleep 1; done | dialog_gauge "$TITLE" "$1" &
}

# Stop status bar
function killgauge(){
 touch "$TMP.done" ; wait ; rm -f "$TMP.done"
}

DEVICE_VENDOR=""
DEVICE_MODEL=""
DEVICE_SIZE=""
DEVICE_DEV=""
DEVICE_INFO=""
FAT_EXISTS=""
KNOPPIX_EXISTS=""
OVERLAY_EXISTS=""
OVERLAY_PARTITION=""
OVERLAY_ENCRYPTED=""

# getstatus /dev/device - reports current flash disk status in variables
function getstatus(){
 local dev="${1##*/}"
 DEVICE_DEV="$1"
 DEVICE_VENDOR="$(cat /sys/block/$dev/device/vendor 2>/dev/null)"
 DEVICE_INFO="$dev:"
 [ -n "$DEVICE_VENDOR" ] && DEVICE_INFO="$DEVICE_INFO $(echo $DEVICE_VENDOR)"
 DEVICE_MODEL="$(cat /sys/block/$dev/device/model 2>/dev/null)"
 [ -n "$DEVICE_MODEL" ] && DEVICE_INFO="$DEVICE_INFO $(echo $DEVICE_MODEL)"
 DEVICE_SIZE="$(awk '{print ($1 / 2048)}' /sys/block/$dev/size 2>/dev/null)"
 [ -n "$DEVICE_SIZE" ] && DEVICE_INFO="$DEVICE_INFO ($(echo "${DEVICE_SIZE}MB"))"
 [ "$DEVICE_SIZE" -lt 10 ] >/dev/null 2>&1 || DEVICE_SIZE=0
 mkdir -p "$TMPMOUNT"
 mount -r -t vfat -o shortname=winnt "${1}1" "$TMPMOUNT" >/dev/null 2>&1
 sleep 1
 if mountpoint -q "$TMPMOUNT"; then
  FAT_EXISTS="true"
  if [ -r "$TMPMOUNT"/KNOPPIX/KNOPPIX ]; then
   KNOPPIX_EXISTS="true"
   local ext
   for ext in aes img inf; do
    local datafile="$TMPMOUNT/KNOPPIX/knoppix-data.$ext"
    if [ -r "$datafile" ]; then
     local mp="" fs="" opts=""
     OVERLAY_EXISTS="$ext"
     if [ "$ext" = "inf" ]; then
      while read OVERLAY_PARTITION mp fs opts; do
       if [ -z "$mp" -o "$mp" = "/KNOPPIX-DATA" -o "$mp" = "KNOPPIX-DATA" ]; then
        case "$opts" in *encryption*) OVERLAY_ENCRYPTED="true" ;; esac
        break
       else
        OVERLAY_PARTITION=""
       fi
      done < "$datafile"
     elif [ "$ext" = "aes" ]; then
      OVERLAY_ENCRYPTED="true"
     fi
     break
    fi
   done
  fi
  umount "$TMPMOUNT" >/dev/null 2>&1
 fi
}

function read_method(){
 local TEXT="$DEVICE_INFO\n"
 [ -n "$FAT_EXISTS" ] && TEXT="${TEXT}\n${MSG_FAT_EXISTS} ${DEVICE_DEV}1"
 [ -n "$KNOPPIX_EXISTS" ] && TEXT="${TEXT}\n${MSG_KNOPPIX_EXISTS} ${DEVICE_DEV}1"
 case "$OVERLAY_EXISTS" in
  img) TEXT="${TEXT}\n${MSG_OVERLAY_IMAGE_EXISTS}" ;;
  aes) TEXT="${TEXT}\n${MSG_ENCRYPTED_OVERLAY_IMAGE_EXISTS}" ;;
  inf) TEXT="${TEXT}\n${MSG_OVERLAY_PARTITION_EXISTS}" ;;
 esac
 TEXT="${TEXT}\n\n$MSG_METHOD_SELECT"
 local LIST=( "p" "$METHOD_NEW_PARTITION" "i" "$METHOD_NEW_IMAGE" "n" "$METHOD_NOTHING" )
 rm -f "$TMP"
 dialog_menu "$TITLE" "$TEXT" "${LIST[@]}" 2>"$TMP" || { rm -f "$TMP"; return 1; }
 read "$1" <"$TMP"; rm -f "$TMP"
}

function read_update(){
 local TEXT="$DEVICE_INFO\n"
 [ -n "$FAT_EXISTS" ] && TEXT="${TEXT}\n${MSG_FAT_EXISTS} ${DEVICE_DEV}1"
 [ -n "$KNOPPIX_EXISTS" ] && TEXT="${TEXT}\n${MSG_KNOPPIX_EXISTS} ${DEVICE_DEV}1"
 case "$OVERLAY_EXISTS" in
  img) TEXT="${TEXT}\n${MSG_OVERLAY_IMAGE_EXISTS}" ;;
  aes) TEXT="${TEXT}\n${MSG_ENCRYPTED_OVERLAY_IMAGE_EXISTS}" ;;
  inf) TEXT="${TEXT}\n${MSG_OVERLAY_PARTITION_EXISTS}" ;;
 esac
 TEXT="${TEXT}\n\n$MSG_UPDATE_SELECT"
 local LIST=( "h" "$MSG_UPDATE_CLEAN" "a" "$MSG_UPDATE_KEEP" "n" "$MSG_UPDATE_DELETE" )
 rm -f "$TMP"
 dialog_menu "$TITLE" "$TEXT" "${LIST[@]}" 2>"$TMP" || { rm -f "$TMP"; return 1; }
 read "$1" <"$TMP"; rm -f "$TMP"
}

function intro_read_allow_hd(){
 local LIST=("r" "$MSG_NOALLOWHD" "h" "$MSG_ALLOWHD")
 rm -f "$TMP"
 dialog_menu "$TITLE" "$MSG_INTRO" "${LIST[@]}" 2>"$TMP" || { rm -f "$TMP"; return 1; }
 read "$1" <"$TMP"; rm -f "$TMP"
}

# setup_loop file_or_blockdevice [encrypted]
# Returns true and saves loopback device in global variable LOOP
# expects password from stdin
function setup_loop(){
 LOOP=""
 LOOP="$(/sbin/losetup -f 2>>"$TMP.err")"
 if [ -n "$2" ]; then # Encrypted
  local m mods=""
  for m in loop cryptoloop aes_generic aes_i586 cbc; do
   [ -d /sys/module/"$m" ] || mods="$mods $m"
  done
  [ -n "$mods" ] && /sbin/modprobe $mods >/dev/null 2>&1
  tr -d '\n\r' | /sbin/cryptsetup create --key-size 256 --key-file - --cipher aes "$MAPDEV" "$1"
  # /sbin/losetup -p 0 -e aes -k 256 "$LOOP" "$1" 2>>"$TMP.err" || { ERROR="$(<$TMP.err)"; bailout 2; }
 else # No encryption
  /sbin/losetup "$LOOP" "$1" 2>>"$TMP.err" || { ERROR="$(<$TMP.err)"; bailout 2; }
 fi
 return 0
}

usage(){
 echo "Usage: $0 [-a] [-f] [-m i|p|n] [-p mb] [image|dir] [target_device]" >&2
 echo "       -a            Allow fixed disks" >&2
 echo "       -f            Force overwrite, no questions (batch mode)" >&2
 echo "       -m i          Method: Create overlay image" >&2
 echo "       -m p          Method: Create overlay partition (recommended)" >&2
 echo "       -m n          Method: No overlay (read only)" >&2
 echo "       -p mb         Overlay partition or image size in MB >= 200"  >&2
 echo "       image.iso     Loopmount source ISO file" >&2
 echo "       dir           Source data directory containing files" >&2
 echo "       target_device Destination block device (flash disk)"  >&2
 exit 1
}

### MAIN ###

[ "`id -u`" != "0" ] && exec sudo "$0" "$@"

unset SUDO_COMMAND
ALLOWHD=""
SIZE=""
FORCE=""
METHOD=""
# Ask for method, i=overlay image, p=overlay partition

# Parse options
while [ -n "$1" ]; do
 case "$1" in
  --help|-h) usage ;;
  -a) ALLOWHD="true" ;;
  -f) FORCE="true" ;;
  -m) shift; case "$1" in i|p|n) METHOD="$1";; *) usage;; esac ;;
  -p) shift; [ "$1" -ge 200 ] && SIZE="$1" ;;
   *) # ISO file, SRC dir or destination device
     if [ -b "$1" ]; then # Block device, destination
      FLASH="$1"
     elif [ -r "$1" ]; then # It may be an ISO, mount it.
      mkdir -p "$TMPSRCMOUNT"
      mount -o loop,ro "$1" "$TMPSRCMOUNT" && cd "$TMPSRCMOUNT"
     elif [ -d "$1" ]; then
      cd "$1"
     else
      usage
     fi
     ;;
 esac
 shift || break
done
 
if [ ! -n "$FLASH" -o ! -b "$FLASH" ]; then
 if [ -z "$ALLOWHD" ]; then
  intro_read_allow_hd hd || bailout 1
  [ "$hd" = "h" ] && ALLOWHD="true" || ALLOWHD=""
 fi
 DEVICELIST=()
 count=0
 for i in $(ls -1d /sys/block/sd? /sys/block/mmcblk? 2>/dev/null); do
  device="${i##/sys/block/}"
  # Safety check
  if [ "$(cat /sys/block/$device/removable)" = "0" ] && [ -z "$ALLOWHD" -a -z "$FORCE" -a -z "$OVERRIDE" ]; then
   # This is a fixed disk!
   continue
  fi
  # Don't show the USB stick we are running from
  # Hey Mike McC., thanks for the hint about fixing the device name pattern here! :-)
  grep -q "${device}[0-9]* .*/mnt-system" /proc/mounts && continue
  DEVICELIST[$((count++))]="$device"
  DEVICELIST[$((count++))]="$(cat $i/device/vendor 2>/dev/null) $(cat $i/device/model 2>/dev/null) ($(awk '{print ($1 / 2048) "MB"}' $i/size 2>/dev/null))"
 done
 rm -f "$TMP"
 if [ -n "${DEVICELIST[*]}" ]; then
  while ! [ -b "$FLASH" ]; do
   dialog_menu "$TITLE" "
$MSG_TARGET
" "${DEVICELIST[@]}" 2>"$TMP" || bailout 1
   read FLASH <"$TMP"; rm -f "$TMP"
   FLASH="/dev/$FLASH"
  done
 else
  ERROR="$MSG_ERROR_NODISK"
  bailout 2
 fi
fi

if grep -q "^${FLASH}" /proc/mounts && ! umount "${FLASH}1"; then
 ERROR="$FLASH $MSG_MOUNTED"
 bailout 2
fi

if [ ! -r KNOPPIX/KNOPPIX -a -r /mnt-system/KNOPPIX/KNOPPIX ]; then
 SRC=/mnt-system
 cd "$SRC"
else
 while [ ! -r KNOPPIX/KNOPPIX ]; do
  dialog_dselect "$MSG_SELECT" "$MSG_SELECT" 2>"$TMP" || bailout 1
  SRC="$(<$TMP)"
  [ -d "$SRC" ] && cd "$SRC"
 done
fi

# Get flash status
gauge "$MSG_CHECKING $FLASH..."
getstatus "${FLASH}"
killgauge

UPDATE=""
# If already installed, ask about update
if [ -z "$FORCE" -a -n "$KNOPPIX_EXISTS" ]; then
 echo "Ask for update" >&2
 read_update UPDATE || bailout 1
fi

if [ -n "$UPDATE" -a "$UPDATE" != "n" ]; then
 METHOD="$UPDATE"
 # (Re-) check size of boot partition
 total="$(awk '{print int($1 / 2048)}' /sys/block/"${FLASH##*/}/${FLASH##*/}1"/size 2>/dev/null)"
 # Add 10% filesystem overhead
 ksize="$(du -sm --exclude='knoppix-data.*' . 2>/dev/null | awk '{size=$1}END{print int(size*1.1)}')"
 let avail=total-ksize
 if [ "$avail" -lt 100 ]; then
  ERROR="${FLASH}1:\n$MSG_ERROR_BOOT_TOOSMAL"
  bailout 2
 fi
elif [ -z "$METHOD" ]; then
 echo "Ask for install method" >&2
 # Ask for method, i=overlay image, p=overlay partition n=likeDVD
 while true; do
  read_method METHOD || bailout 1
  [ -n "$METHOD" ] && break
 done
fi

mkdir -p "$TMPMOUNT"

# Use ionice, be friendly to our multitasking
IONICE=""
type -p ionice >/dev/null 2>&1 && IONICE="ionice -c 3"

echo "Ask for partitioning" >&2
if [ "i" = "$METHOD" -o "n" = "$METHOD" ]; then
 # Overlay image or "no overlay" method selected
 if [ -n "$FORCE" ] || { dialog_yesno "$TITLE" "$DEVICE_INFO\n\n$MSG_QUESTION_PARTITION" && dialog_yesno "$TITLE" "$DEVICE_INFO\n\n$MSG_QUESTION_REALLY_FORMAT"; }; then
  gauge "$DEVICE_INFO\n\n$MSG_PARTITION $FLASH..."
  dd if=/dev/zero of="$FLASH" bs=4M count=1 >/dev/null 2>&1
  blockdev --flushbufs "$FLASH"
  parted "$FLASH" 2>"$TMP.err" <<EOT
mktable $PARTTYPE
mkpart primary fat32 2048s -0
set 1 boot on
quit
EOT
#  sfdisk -u M "$FLASH" 2>"$TMP.err" <<EOT
#,,c,*
#EOT
  if [ "$?" != 0 ]; then
   ERROR="$(<$TMP.err)"; bailout 2
  fi
  rm -f "$TMP.err"
  blockdev --flushbufs "$FLASH"
  sleep 10
  # udev may need some additional time to settle down after repartitioning
  udevadm settle --timeout=20
  $IONICE mkdosfs -F32 -n "KNOPPIX" "${FLASH}1" 2>"$TMP.err" || { ERROR="$(<$TMP.err)"; bailout 2; }
  $IONICE blockdev --flushbufs "${FLASH}1"
  killgauge
 elif [ -n "$FAT_EXISTS" ]; then # FAT exists -> just set bootable later
  MAKE_BOOTABLE="true"
 else # fail
  ERROR="${FLASH}:\n$MSG_ERROR_INCOMPATIBLE"; bailout 2
 fi
elif [ "p" = "$METHOD" ]; then
 total="$(awk '{print int($1 / 2048)}' /sys/block/"${FLASH##*/}"/size 2>/dev/null)"
 # Add 10% filesystem overhead
 ksize="$(du -sm --exclude='knoppix-data.*' . 2>/dev/null | awk '{size=$1}END{print int(size*1.1)}')"
 let avail=total-ksize
 let avail-=200
 if [ "$avail" -lt 400 ]; then
  ERROR="${FLASH}:\n$MSG_ERROR_TOOSMAL"
  bailout 2
 fi
 if [ -n "$FORCE" ] || { dialog_yesno "$TITLE" "$DEVICE_INFO\n\n$MSG_QUESTION_PARTITION_ADVANCED" && dialog_yesno "$TITLE" "$DEVICE_INFO\n\n$MSG_QUESTION_REALLY_FORMAT"; }; then
  if [ -z "$SIZE" ]; then
   rm -f "$TMP"
   dialog_scale "$TITLE" "$(printf "$MSG_SIZE_OVERLAY_ADVANCED" "$avail")" 400 "$avail" "$avail" 2>"$TMP" || bailout 1
   read SIZE <"$TMP"
   [ "$SIZE" -ge 400 ] 2>/dev/null || SIZE="400"
  fi
  [ "$SIZE" -gt "$avail" ] && SIZE="$avail"
  gauge "$DEVICE_INFO\n$MSG_PARTITION $FLASH..."
  dd if=/dev/zero of="$FLASH" bs=4M count=1 >/dev/null 2>&1
  blockdev --flushbufs "$FLASH"
  parted "$FLASH" 2>"$TMP.err" <<EOT
mktable $PARTTYPE
mkpart primary fat32 2048s $((total-SIZE))M
set 1 boot on
mkpart primary reiserfs $((total-SIZE))M -0
quit
EOT
#  sfdisk -u M "$FLASH" 2>"$TMP.err" <<EOT
#,$((total-SIZE)),c,*
#,,83,
#EOT
  if [ "$?" != 0 ]; then
   ERROR="$(<$TMP.err)"; bailout 2
  fi
  rm -f "$TMP.err"
  blockdev --flushbufs "$FLASH"
  sleep 10
  # udev may need some additional time to settle down after repartitioning
  udevadm settle --timeout=20
  killgauge
  gauge "$DEVICE_INFO\n$MSG_FORMAT $FLASH partition 1..."
  $IONICE mkdosfs -F32 -n "KNOPPIX" "${FLASH}1" 2>"$TMP.err" || { ERROR="$(<$TMP.err)"; bailout 2; }
  $IONICE blockdev --flushbufs "${FLASH}1"
  killgauge
 else
  bailout 1
 fi
fi

echo "(Re-)create boot loader, first remove the old one" >&2
# (Re-)create boot loader, first remove the old one
gauge "$DEVICE_INFO\n$MSG_BOOTREC $FLASH..."
rm -f "$TMP.err"
mount -t vfat -o shortname=winnt "${FLASH}1" "$TMPMOUNT" 2>"$TMP.err" || { killgauge; ERROR="${FLASH}:\n$MSG_ERROR_INCOMPATIBLE"; bailout 2; }
[ -f "$TMPMOUNT"/ldlinux.sys ] && rm -f "$TMPMOUNT"/ldlinux.sys
blockdev --flushbufs "${FLASH}1"
sleep 2
umount "$TMPMOUNT"
rm -f "$TMP.err"
syslinux "${FLASH}1" 2>"$TMP.err" || { ERROR="$(<$TMP.err)"; bailout 2; }
blockdev --flushbufs "${FLASH}1"
sleep 2
killgauge

echo "Mount boot partition" >&2
if [ -r boot/syslinux/syslinux.cfg -o -r boot/isolinux/isolinux.cfg ]; then
 mount -t vfat -o shortname=winnt "${FLASH}1" "$TMPMOUNT" 2>"$TMP.err" || { ERROR="$(<$TMP.err)"; bailout 2; }

 # Update methods
 if [ "$METHOD" = "h" -a -n "$OVERLAY_EXISTS" ]; then
  # Need to mount overlay image or partition and delete all but home
  echo "Mount KNOPPIX overlay" >&2
  mkdir -p "$TMPMOUNT2"
  while true; do # Stay here until mount of overlay succeeds (never lose data)
   rm -f "$TMP"
   if [ -n "$OVERLAY_ENCRYPTED" ]; then
    dialog_password "$TITLE" "$MSG_PASSWORD" 2>"$TMP" || { rm -f "$TMP"; CRYPT=""; break; }
   else
    touch "$TMP"
   fi
   fs=auto
   case "$OVERLAY_EXISTS" in
    img|aes) setup_loop "$TMPMOUNT"/KNOPPIX/knoppix-data."$OVERLAY_EXISTS" "$OVERLAY_ENCRYPTED" <"$TMP"; fs=ext2;;
    inf)     setup_loop "${FLASH}$OVERLAY_PARTITION" "$OVERLAY_ENCRYPTED" <"$TMP"; fs=reiserfs;;
   esac
   if [ -n "$OVERLAY_ENCRYPTED" ]; then
    mount -t "$fs" /dev/mapper/"$MAPDEV" "$TMPMOUNT2" && break
   else
    mount -t "$fs" "$LOOP" "$TMPMOUNT2" && break
   fi
   dialog_info_timeout "$TITLE" "$DEVICE_INFO\n\n$MSG_ERROR_CANNOT_MOUNT_OVERLAY"
   [ -n "$OVERLAY_ENCRYPTED" ] || bailout 1
  done
  echo "Delete overlay data except /home" >&2
  for i in "$TMPMOUNT2"/* "$TMPMOUNT2"/.??*; do
   [ -e "$i" ] || continue
   case "$i" in */home|*/home/) true;; *) rm -rff "$i";; esac
  done
  sleep 2
  umount -d "$TMPMOUNT2" 2>/dev/null
  /sbin/cryptsetup remove "$MAPDEV" >/dev/null 2>&1
 fi

 echo "Now copy all the files (except for overlays)" >&2
 knoppix_src=()
 knoppix_dst=()
 knoppix_cnt=0
 while read name; do
  [ -r "$name" -o -d "$name" ] || continue
  # Skip knoppix-data.*
  case "$name" in
   KNOPPIX/[Kk][Nn][Oo][Pp][Pp][Ii][Xx]-[Dd][Aa][Tt][Aa].*) true;;
   *) knoppix_src[$knoppix_cnt]="$name"; knoppix_dst[$knoppix_cnt]="$TMPMOUNT/$name";;
  esac
  let knoppix_cnt++
 done <<.
$(ls -1d KNOPPIX/*)
.

 progress "$DEVICE_INFO\n$MSG_WRITING" $(du -sk boot efi "${knoppix_src[@]}" 2>/dev/null | awk '{size+=$1}END{print size}') "$TMPMOUNT/boot" "$TMPMOUNT/efi" "${knoppix_dst[@]}"
 [ "$METHOD" = "h" ] && rm -ff "$TMPMOUNT"/KNOPPIX/[Kk][Nn][Oo][Pp][Pp][Ii][Xx][0-9] 2>/dev/null
 $IONICE cp -Lrf boot "$TMPMOUNT"/ 2>"$TMP.err" || { ERROR="$(<$TMP.err)"; bailout 2; }
 if [ -r index.html -a ! -e "$TMPMOUNT"/index.html ]; then
  $IONICE cp -Lf index.html "$TMPMOUNT"/ 2>"$TMP.err" || { ERROR="$(<$TMP.err)"; bailout 2; }
 fi
 if [ -d efi ]; then
  $IONICE cp -Lrf efi "$TMPMOUNT"/ 2>"$TMP.err" || { ERROR="$(<$TMP.err)"; bailout 2; }
 fi
 mkdir "$TMPMOUNT"/KNOPPIX 2>/dev/null
 $IONICE cp -Lrf "${knoppix_src[@]}" "$TMPMOUNT"/KNOPPIX/ 2>"$TMP.err" || { ERROR="$(<$TMP.err)"; bailout 2; }
 if [ ! -r boot/syslinux/syslinux.cfg -a -r boot/isolinux/isolinux.cfg ]; then
  [  -d "$TMPMOUNT"/boot/syslinux ] && rm -rf "$TMPMOUNT"/boot/syslinux
  mv -f "$TMPMOUNT"/boot/isolinux "$TMPMOUNT"/boot/syslinux
  mv -f "$TMPMOUNT"/boot/syslinux/isolinux.cfg "$TMPMOUNT"/boot/syslinux/syslinux.cfg
  rm -f "$TMPMOUNT"/boot/syslinux/isolinux.* "$TMPMOUNT"/boot/syslinux/*.cat "$TMPMOUNT"/boot/isolinux.* "$TMPMOUNT"/boot/*.cat
 fi
 killgauge
else
 ERROR="$MSG_NOTFOUND"
 bailout 3
fi

if [ "$METHOD" = "n" -o "$UPDATE" = "n" ]; then
 rm -f "$TMPMOUNT"/KNOPPIX/knoppix-data.*
fi

# If we don't already have an overlay, ask to create one
if [ ! "$METHOD" = "n" -a ! -e "$TMPMOUNT"/KNOPPIX/knoppix-data.img -a ! -e "$TMPMOUNT"/KNOPPIX/knoppix-data.aes -a ! -e "$TMPMOUNT"/KNOPPIX/knoppix-data.inf ]; then
 if [ "i" = "$METHOD" ]; then # Image creation: Ask for size
  avail="$(df -m "$TMPMOUNT" | awk '{size=$4}END{print size - 1}')"
  # At least 200MB, but not more than 4GB is allowed on a FAT32 file system
  [ "$avail" -ge 200 ] 2>/dev/null || bailout 0
  [ "$avail" -le 4000 ] || avail=4000
  rm -f "$TMP"
  if [ -z "$SIZE" ]; then
   dialog_scale "$TITLE" "$(printf "$MSG_SIZE_OVERLAY" "$avail")" 200 "$avail" "$avail" 2>"$TMP" || bailout 1
   read SIZE <"$TMP"
  fi
  [ "$SIZE" -ge 200 ] 2>/dev/null || SIZE="200"
  [ "$SIZE" -le 4000 ] 2>/dev/null || SIZE="$avail"
 else
  SIZE="$(awk '{print int($1 / 2048)}' /sys/block/"${FLASH##*/}"/"${FLASH##*/}2"/size 2>/dev/null)"
 fi
 if [ -z "$FORCE" ]; then
  dialog_yesno "$TITLE" "$MSG_CRYPT_OVERLAY" && CRYPT=yes || CRYPT=""
 fi
 if [ -n "$CRYPT" ]; then
  pw1=""; pw2=""; len=0
  while [ -z "$pw1" -o -z "$pw2" -o x"$pw1" != x"$pw2" -o "$len" -lt 4 ]; do
   rm -f "$TMP"
   [ -n "$pw1" ] && dialog_info_timeout "$TITLE" "$MSG_PASSWORDS_NOMATCH"
   dialog_password "$TITLE" "$MSG_PASSWORD" 2>"$TMP" || { rm -f "$TMP"; CRYPT=""; break; }
   read pw1 <"$TMP"; rm -f "$TMP"
   dialog_password "$TITLE" "$MSG_PASSWORD2" 2>"$TMP"
   read pw2 <"$TMP"; len="$(wc -c "$TMP" | awk '{print $1}')"
   # Don't delete $TMP yet, we need it for losetup
  done
 fi
 gauge "$DEVICE_INFO\n$(printf "$MSG_OVERLAY" "$SIZE")"
 if [ -n "$CRYPT" ]; then
  if [ "i" = "$METHOD" ]; then # Image creation: Create random data
   $IONICE dd if=/dev/urandom of="$TMPMOUNT"/KNOPPIX/knoppix-data.aes bs=1M count="$SIZE" >/dev/null 2>"$TMP.err"
   setup_loop "$TMPMOUNT"/KNOPPIX/knoppix-data.aes encrypted <"$TMP"
   rm -f "$TMP"
   $IONICE mke2fs -F -m 0 /dev/mapper/"$MAPDEV" 2>>"$TMP.err" || { ERROR="$(<$TMP.err)"; bailout 2; }
   $IONICE blockdev --flushbufs /dev/mapper/"$MAPDEV"
   sleep 2
   /sbin/cryptsetup remove "$MAPDEV"
  elif [ "p" = "$METHOD" ]; then # Data partition
   # $IONICE dd if=/dev/urandom of="${FLASH}2" bs=1M >/dev/null 2>"$TMP.err"
   # Overwriting the entire partition with random data may be a waste of time,
   # so let's just format it.
   setup_loop "${FLASH}2" encrypted <"$TMP"
   rm -f "$TMP"
   $IONICE mkreiserfs -s 513 -f -f -l "KNOPPIX-DATA" /dev/mapper/"$MAPDEV" 2>>"$TMP.err" || { ERROR="$(<$TMP.err)"; bailout 2; }
   $IONICE blockdev --flushbufs /dev/mapper/"$MAPDEV"
   /sbin/cryptsetup remove "$MAPDEV"
   # Create fstab style entry
   echo "2 /KNOPPIX-DATA reiserfs encryption=aes" >> "$TMPMOUNT"/KNOPPIX/knoppix-data.inf
  fi
 else # No encryption
  if [ "i" = "$METHOD" ]; then
   $IONICE dd if=/dev/zero of="$TMPMOUNT"/KNOPPIX/knoppix-data.img bs=1M count="$SIZE" >/dev/null 2>"$TMP.err"
   $IONICE mke2fs -F -m 0 "$TMPMOUNT"/KNOPPIX/knoppix-data.img 2>>"$TMP.err" || { ERROR="$(<$TMP.err)"; bailout 2; }
  elif [ "p" = "$METHOD" ]; then
   $IONICE mkreiserfs -s 513 -f -f -l "KNOPPIX-DATA" "${FLASH}2" 2>>"$TMP.err" || { ERROR="$(<$TMP.err)"; bailout 2; }
   $IONICE blockdev --flushbufs "${FLASH}2"
   echo "2 /KNOPPIX-DATA reiserfs" >> "$TMPMOUNT"/KNOPPIX/knoppix-data.inf
  fi
 fi
fi

# Flush and umount file system
blockdev --flushbufs "${FLASH}1"
sleep 2
umount "$TMPMOUNT"

# Create MBR
echo "Creating a master boot record" >&2
if [ "$PARTTYPE" = "msdos" ]; then
 # Don't depend on ms-sys
 # ms-sys -s "${FLASH}" 2>"$TMP.err" || { ERROR="$(<$TMP.err)"; bailout 2; }
 for mbr in /usr/lib/syslinux/mbr.bin /usr/lib/syslinux/mbr/mbr.bin; do
  [ -r "$mbr" ] || continue
  dd if="$mbr" of="${FLASH}" bs=440 count=1 && break
 done
elif [ "$PARTTYPE" = "gpt" ]; then
 for mbr in /usr/lib/syslinux/gptmbr.bin /usr/lib/syslinux/mbr/gptmbr.bin; do
  [ -r "$mbr" ] || continue
  dd if="$mbr" of="${FLASH}" bs=440 count=1 && break
 done
fi
blockdev --flushbufs "${FLASH}"
sleep 2

# Finally mark partition 1 as bootable, if not already done.
# this may cause another udev run, which we can ignore here
if [ -n "$MAKE_BOOTABLE" ]; then
 parted "$FLASH" 2>"$TMP.err" <<EOT
set 1 boot on
quit
EOT
 blockdev --flushbufs "${FLASH}"
fi

# Give block layer time to settle down
udevadm settle --timeout=20
sleep 4

bailout 0
