#!/bin/sh

#==============================================================================
# LiveCD/USB/HD init script
# (C) 2009-2014 BitJam for antiX <antiX@operamail.com>
# Inspired by the work of  Klaus Knopper
#
# License: GPLv3 or later
#==============================================================================

# SUFFIX NOTE: _MP     absolute mountpoint
#              _NAME   filename with no path
#              _PATH   relative path but no filename
#              _FILE   relative path with filename
#              _FULL   absolute mountpoint and path and filename
#              _DIR    varies

                VERSION="7.12.1"
           VERSION_DATE="Thu Nov  6 21:00:00 MST 2014"
              DEVELOPER="BitJam"
  DISTRO_BUG_REPORT_URL="http://antix.freeforums.org"

        RETRY_TIME=10
     PERSIST_RETRY=10

           RW_MODE="ro"
       DISTRO_NAME="Linux"
            PRINTK=0

          LIVE_DIR="/live"
         FINAL_DIR="/live"
          LIVE_BIN="$LIVE_DIR/bin"

          AUFS_DIR="aufs"

       EXTRA_FILES="deb xtra xtra.tgz desktop"
       MIN_SYS_RAM=80
      MIN_AUFS_RAM=80

           LOG_DIR="/var/log/live"
          LOG_FILE="$LOG_DIR/bootstrap.log"

            MY_LOG=/init.log
           VID_DIR="/etc/live/version"
          VID_FILE="$VID_DIR/linuxfs.ver"
          MAKE_OLD="rootfs xtra deb xtra.tgz"
           MOD_DIR="/lib/modules/$(uname -r)"

         FROM_TYPE="usb,cd"
     FROM_TYPE_ALL="usb,cd,hd"
          TOP_DIRS="bin boot etc lib lib64 opt root run sbin tmp usr var"
      TOP_BIN_DIRS="bin lib lib64 sbin usr"

      FRUGAL_FILES="linuxfs linuxfs.md5 vmlinuz vmlinuz.md5 initrd.gz initrd.gz.md5 xtra xtra.tgz ../boot"

     MAX_MOUNT_CNT=30
       RETRY_DELAY=50               # in hundredths of a second
           VERBOSE=5

        DO_HOTPLUG=true
        LOAD_FIRST="usb-common usbcore ehci-hcd fusbh200-hcd fotg210-hcd"

          VID_NAME=VID
      LINUXFS_NAME=linuxfs

           NO_LOAD=
           DO_LOAD=
      DISABLE_LOAD=

    DEFAULT_SQFILE=/antiX/linuxfs
  DEFAULT_ISO_FILE=/antiX/antiX.iso

  LIVE_X64_LD_PATH="/lib:/lib/x86_64-linux-gnu:/usr/lib"
  LIVE_386_LD_PATH="/lib:/lib/i386-linux-gnu:/usr/lib"

MKFS_FREE_MARGIN=20
  rootfs_SIZE_PARAM="min_size=64 mid_size=400 max_size=1000 factor=50"
  homefs_SIZE_PARAM="min_size=64 mid_size=400 max_size=1000 factor=25"

         MKFS_SIZES="65 100 150 200 250 375 500 750 1000 1500 2000 2500 3000 4000"
      MAX_MKFS_SIZE=${MKFS_SIZES##* }

      LIVE_SCRIPTS="live-L10n live-init persist-password persist-autosave live-disable-services"

set_live_dirs() {
    local dir=$1

         FRUGAL_MP="$dir/frugal"
           BOOT_MP="$dir/boot-dev"
        ISO_DEV_MP="$dir/iso-dev"
       ISO_FILE_MP="$dir/iso-file"
          TORAM_MP="$dir/to-ram"
       AUFS_RAM_MP="$dir/aufs-ram"
           AUFS_MP="$dir/$AUFS_DIR"
         ROOTFS_MP="$dir/persist-root"
        PERSIST_MP="$dir/persist-dev"
           SQFS_MP="$dir/linux"         # where the linuxfs file gets *mounted*
      WRAP_FILE_MP="$dir/wrapper"
        OUTPUT_DIR="$dir/config"
        LOCALE_DIR="$dir/locale"
        CUSTOM_DIR="$dir/custom"
   GFX_SAVE_SCRIPT="$dir/bin/gfxsave.sh"
 DISABLED_USB_FILE="$OUTPUT_DIR/usb-disabled"
}


hbar="======================================================================"
tbar="----------------------------------------------------------------------"

main_wrapper() {

    local black blue green cyan red purple brown lt_gray dk_gray lt_blue
    local lt_green lt_cyan lt_red magenta yellow white rev_red
    local cheat_co cmd_co dev_co err_co from_co to_co head_co
    local hi_co mp_co m_co nc_co num_co ok_co bold_co

    PATH=$LIVE_BIN
    HOME=/
    TERM=linux
    PWD=/

    set_live_dirs $LIVE_DIR
    mkdir -p $OUTPUT_DIR

    # Don't allow interrupts to ruin our day
    #       HUP INT QUIT SEGV TERM
    trap "" 1   2   3    11   15

    umask 022

    system_mount
    local time_0=$(cut -d" " -f22 /proc/$$/stat)

    make_nodes /dev

    read_distro_release /etc/initrd-release
    custom_code 0 'before reading boot codes'

    read_cmdline_params

    FREE_MEM=$(mem_info MemFree)

    set_colors "$NO_COLOR" "$LOW_COLOR"

    read_xlat init $LANG
    clean_xlat

    # disable console screen blanking
    printf "\e[9;0]\e[14;0]"

    echo "$PRINTK" > /proc/sys/kernel/printk

    SCREEN_WIDTH=$(stty size 2>/dev/null | cut -d" " -f2)

    [ -z "${FINAL_DIR##/*}" ] || FINAL_DIR="/$FINAL_DIR"
    NEW_ROOT=$AUFS_MP

    if [ "$NO_ERR_LOG" ]; then
        main_core
    else
        main_core 17>&1 1>&2 2>&17 | while read line; do echo "$line" \
            | sed -e "s/^/${yellow}Error: $red/" -e "s/$/$nc_co/" | tee -a $MY_LOG; done
    fi

    # Nota Bene: when error log is enabled, main_core() runs in a subshell
    # so it cannot affect our variables.

    write_log_files $MY_LOG $NEW_ROOT/$LOG_FILE

    # Need to run persist-password outside of our error catching

#--    # FIXME remove after squashfs gets updated
#--    #----------------------------------------------------------------
#--    local f
#--    for f in halt reboot; do
#--        rm -f $NEW_ROOT/usr/local/bin/$f
#--    done
#--
#--    rm -f  $NEW_ROOT/etc/rc2.d/S01slim
#--    rm -f  $NEW_ROOT/sbin/make-fstab
#--    rm -f  $NEW_ROOT/usr/sbin/buildxconfig
#--    #rm -rf $NEW_ROOT/usr/share/antiX/lib
#--
#--    rm -rf /usr/share/antiX/init-xlat
#--
#--    for f in $LIVE_SCRIPTS live-restore-services; do
#--        rm -f $NEW_ROOT/etc/init.d/$f
#--        rm -f $NEW_ROOT/etc/rc?.d/S*[0-9]$f
#--    done
#--
#--    #----------------------------------------------------------------
    
    breakpoint 8 "before running init.d scripts"

    PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/live/bin
    run_init_scripts $NEW_ROOT $LIVE_SCRIPTS
    PATH=$LIVE_BIN

    local time_1=$(get_time)
    local dt=$((time_1 - time_0))
    vmsg 6
    vmsg 5  "$_The_X_program_took_Y_seconds_" $0 $(nq $(get_seconds $dt))
    vmsg 6 $hbar

    breakpoint 9 "right before starting init"

    # re-enable console screen blanking at 15 minutes
    [ "$VT_BLANK" ] && printf "\e[9;$VT_BLANK]\e[14;$VT_BLANK]"

    if find_init_prog INIT_PROG $NEW_ROOT; then

        vmsg 4 'Run %s instead of %s' $white$INIT_PROG$m_co $white/sbin/init$m_co
        vmsg 5 "${hi_co}%s"  "$_Please_exit_shells_normally_to_allow_for_a_clean_unmount_"

        CHROOT_BIN=$FINAL_DIR/bin
        chroot $NEW_ROOT $CHROOT_BIN/setsid $CHROOT_BIN/cttyhack $INIT_PROG

        breakpoint i "after init= chroot"
        safe_shutdown poweroff ask

    else
        vmsg 6  "$_Start_X_process_" init
        exec $LIVE_BIN/switch_root -c /dev/console $NEW_ROOT /sbin/init "$@"
    fi
}

main_core() {
    [ "$NO_CLEAR" ] || printf "\e[1J"       # Clear and reset screen
    vmsg 6 "=== $0 bootstrap =================================================="

    vmsg 6 '%s started at %s seconds' $0 $(nq $(get_seconds $time_0))

    select_breakpoints

    breakpoint 1 "before loading modules"

    start_hotplug "$DO_HOTPLUG" "$TRACE_LOAD"
    load_kernel_modules "$NO_LOAD" "$DO_LOAD"

    do_welcome "$DISTRO_PRETTY_NAME" "$VERSION" "$VERSION_DATE"

    show_bootcodes "$UNKNOWN_BOOTCODES"
    show_integer_errors "$INTEGER_ERRORS"

    : ${SQFILE_FILE:=$DEFAULT_SQFILE}
    : ${SQFILE_NAME:=${SQFILE_FILE##*/}}

    [ -z "${SQFILE_FILE##*/*}" ] && : ${BOOT_DIR:=${SQFILE_FILE%/*}}

    [ "$SQFILE_EXT" ] && SQFILE_NAME="$SQFILE_NAME.$SQFILE_EXT"
    SQFILE_FILE="$BOOT_DIR/$SQFILE_NAME"
    SQFILE_FILE=${SQFILE_FILE#/}

    # Wait and only do this in the find_files() loop so the usb report
    # will always work
    #[ "$NO_EHCI" ] && disable_hcd ehci

    breakpoint 2 "before looking for linuxfs file"

    RW_MODE=ro

    if [ "$FRUGAL_ID" ]; then
        find_frugal_file "$FRUGAL_ID" || frugal_error $?
    else
        find_linuxfs_file
    fi

    mount -o remount,rw $BOOT_MP &>/dev/null
    dir_has_param "$BOOT_MP" rw && REMASTERABLE=true
    [ "$DID_ISO" ] && REMASTERABLE=

    BOOT_UUID=$(device_uuid $SQFILE_DEV)
    vmsg 6 "boot device uuid: $BOOT_UUID"
    breakpoint 3 "after mounting boot device"

    mount -o remount,rw $SQFILE_DEV 2>/dev/null
    dir_has_param "$SQFILE_MP" rw || REMASTERABLE=
    [ "$REMASTERABLE" ] && do_remaster "$SQFILE_FULL"

    mount_linuxfs "$SQFS_MP" "$SQFILE_FULL" "$WRAP_FILE_MP"

    prep_ld_path

    RW_MODE=rw
    # FIXME: this makes debugging persist_makefs simpler
    #[ "$PERSIST_FILES" ] && mount -o remount,rw $SQFILE_DEV
    mount -o remount,rw $SQFILE_DEV

    breakpoint 4 "after mounting linuxfs file"

    prepare_persistence

    mount_persist_device "$PERSIST_FILES" "$FROM_PERSIST"

    stop_hotplug
    list_modules

    # Check for ehci warning
    dmesg | grep 'Warning.*ehci' >&2

    breakpoint 5 "after mounting persistence device"

    fsck_boot_dev "$SQFILE_DEV" "$SQFILE_MP"

    ORIG_SQFILE_MP=$SQFILE_MP
    ORIG_SQFILE_DIR=$SQFILE_PATH
    ORIG_SQFILE_FULL=$ORIG_SQFILE_DIR/$SQFILE_NAME

    # Check md5 of files in directory containing linuxfs file
    # 'fromiso' causes us to check md5sums in two places
    [ "$CHECK_MD5" ] && check_md5 $SQFILE_DIR

    [ "$TO_RAM" ] && copy_to_ram "$SQFILE_FULL" $TORAM_MP

    check_kernel_version $SQFS_MP

    mount -o remount,rw $SQFILE_DEV 2>/dev/null
    fsck_persist_dev "$PERSIST_FILES" "$PERSIST_DEVICE" "$PERSIST_MP"

    [ "$WANT_ROOTFS" ] && remaster_rootfs "$PERSIST_MP" "$PERSIST_FULL_PATH"

    mount_aufs_ram "$AUFS_RAM_MP" "$MIN_AUFS_RAM" "$FREE_MEM"

    mount_and_copy_rootfs

    [ "$GFX_SAVE" ] && update_gfx_menu "$GFX_SAVE_SCRIPT" "$GFX_SAVE" "$BOOT_MP/boot"

    [ "$STATIC_ROOT" ] && log_cmd umount $AUFS_RAM_MP

    mount_aufs "$AUFS_MP" "$SQFS_MP" "$AUFS_RAM_MP" "$ROOTFS_MP"

    SYSTEM_D=
    local sbin_init=$(readlink $NEW_ROOT/sbin/init)
    [ -n "$sbin_init" -a -z "${sbin_init##*systemd}" ] && SYSTEM_D=true
    [ "$SYSTEM_D" ] && msg  "$_Detected_systemd_init_system_"

    breakpoint 6 "after mounting aufs"

    [ "$WANT_HOMEFS" ] && copy_homefs $PERSIST_FULL_PATH

    if mount_persist_file 'homefs' $NEW_ROOT/home 'home' "$WANT_HOMEFS" "$NEED_HOMEFS"; then
        HOMEFS_DEV=$PERSIST_DEVICE
        msg  "$_Enabled_X_persistence_" "$(cq home)"
    else
        [ "$NEED_HOMEFS" ] && non_fatal  "$_Could_not_enable_required_X_persistence_" "$(pqh home!)"
    fi

#--    # FIXME: remove Dave's stuff before adding xtra files
#--    if [ -x $DEFAULT_DIR/xtra/usr/local/bin/desktop-session ]; then
#--        rm -f $AUFS_MP/usr/local/bin/desktop-session*
#--        rm -f $AUFS_MP/usr/local/bin/antiX-FileManager.sh 
#--        rm -f $AUFS_MP/usr/local/bin/desktop-menu-apt-update
#--    fi

    [ "$DO_XTRA"       ] && copy_xtra    $DEFAULT_DIR
    [ "$DO_AUTO_LOGIN" ] && auto_login   "$AUTO_LOGIN_PROG" "$AUTO_LOGIN_TERMS" "$NEW_ROOT"
    [ "$DO_FANCY"      ] && fancy_prompt "$FANCY_PROMPT" "$NEW_ROOT"

    write_output_files     $OUTPUT_DIR
    old_write_output_files $OUTPUT_DIR

#--    # FIXME: remove after syslinux, isolinux are updated in iso file
#--    local boot_dir=/live/boot-dev/boot/syslinux
#--    if [ -n "$REMASTERABLE" -a -d $boot_dir -a ! -e $boot_dir/back640.jpg ]; then
#--        msg "Update bootloader"
#--        cp -a /live/syslinux/* $boot_dir
#--        rm -r /live/syslinux
#--    fi

    # FIXME: since we are using the real persist-save we can do these either
    # before or after we run the init scripts!

    late_umount $PERSIST_DEVICE
    late_umount $SQFILE_DEV

    breakpoint 7 "before prepare switch_root"

    prepare_switch_root $NEW_ROOT $LIVE_DIR $FINAL_DIR
}

#------------------------------------------------------------------------------
# Getting started
#------------------------------------------------------------------------------

system_mount() {
    dir=$1

    mkdir -p $dir/proc $dir/sys $dir/dev

    mount -t proc   proc  $dir/proc
    mount -t sysfs  sys   $dir/sys

    mount -t devtmpfs devtmpfs $dir/dev
    mkdir -p $dir/dev/pts
    mount -t devpts devpts $dir/dev/pts
}


make_nodes() {
    local dir=$1
    mkdir -p $dir

    [ -e $dir/console ] || mknod $dir/console c 5 1
    [ -e $dir/null    ] || mknod $dir/null    c 1 3
    [ -e $dir/tty0    ] || mknod $dir/tty0    c 4 0
}

read_distro_release() {
    local file=$1
    sed -r -n 's/^\s*([A-Z0-9_]+=)/DISTRO_\1/p' $file
    eval $(sed -r -n 's/^\s*([A-Z0-9_]+=)/DISTRO_\1/p' $file)
}

#------------------------------------------------------------------------------
# Function read_cmdline_params
#
# Gather all boot codes
#------------------------------------------------------------------------------
read_cmdline_params()
{
    local ev param value

    # unset almost all env variables
    for ev in $(printenv | sed 's/=.*//'); do
        case $ev in
            HOME|PATH|PWD|TERM) continue;;
        esac
        unset $ev
    done

    for param in $(cat /proc/cmdline); do
        value=${param#*=}

        case $param in
               bootdir=*|bdir=*) BOOT_DIR=$value                                 ;;
    bootlabel=*|blabel=*|blab=*) BOOT_ID=label=$value                            ;;
             bootuuid=*|buuid=*) BOOT_ID=uuid=$value                             ;;
               bootdev=*|bdev=*) BOOT_ID=name=$value                             ;;

                  try=*|retry=*) valid_int RETRY_TIME $param                     ;;

            persistdir=*|pdir=*) PERSIST_PATH=${value#/}                         ;;

 persistlabel=*|plabel=*|plab=*) PERSIST_ID=label=$value  ;  SET_PERSIST=true    ;;
          persistuuid=*|puuid=*) PERSIST_ID=uuid=$value   ;  SET_PERSIST=true    ;;
            persistdev=*|pdev=*) PERSIST_ID=name=$value   ;  SET_PERSIST=true    ;;

 persistretry=*|pretry=*|ptry=*) valid_int PERSIST_RETRY $param                  ;;

                          dbv=*) DBV=$value ;;
                        fneed=*) FRUGAL_NEEDED=$value                            ;;
                         fforce) FORCE_FRUGAL=true                               ;;
                         frugal) FRUGAL_ID=label=$DISTRO_NAME-Frugal ; FRUGAL=true                   ;;
                       frugal=*) FRUGAL_ID=label=$DISTRO_NAME-Frugal ; FRUGAL=true  ; PERSIST=$value ;;
                         flab=*) FRUGAL_ID=label=$value         ; FRUGAL=        ;;
                         fdev=*) FRUGAL_ID=name=$value          ; FRUGAL=        ;;
                        fuuid=*) FRUGAL_ID=uuid=$value          ; FRUGAL=        ;;

                iso=*|fromiso=*) ISO_FILE=${value/#}                             ;;
                    iso|fromiso) FROM_ISO=true                                   ;;

                         from=*) FROM_TYPE=$value     ; ALWAYS_SCAN=true         ;;

                  persist=*|p=*) PERSIST=$PERSIST,$value                         ;;
                        persist) PERSIST=$PERSIST,root,home                      ;;

                        sqext=*) SQFILE_EXT=$value                               ;;
                       sqname=*) SQFILE_NAME=${value#/}                          ;;
                           sq=*) SQFILE_FILE=$value                              ;;

           verbose=*|verb=*|v=*) valid_int VERBOSE $param                        ;;
                           bp=*) BREAK_POINTS=$BREAK_POINTS,$value               ;;
                           pk=*) PRINTK=$value                                   ;;

             check|md5|checkmd5) CHECK_MD5=true                                  ;;
                 hico|highcolor) LOW_COLOR=                                      ;;
                  loco|lowcolor) LOW_COLOR=true                                  ;;
                   noco|nocolor) NO_COLOR=true                                   ;;

                         noxtra) DO_XTRA=                                        ;;
                         doxtra) DO_XTRA=true                                    ;;

                            db+) DO_AUTO_LOGIN=true; DO_FANCY=true; DB_PLUS=true ;;

            fancyprompt|fprompt) DO_FANCY=true                                   ;;
               autologin|alogin) DO_AUTO_LOGIN=true                              ;;

                          toram) TO_RAM=true                                     ;;

                     noremaster) NO_REMASTER=true                                ;;
                       rollback) ROLLBACK=true                                   ;;
                         lang=*) LANG=$value                                     ;;

                        noclear) NO_CLEAR=true                                   ;;
                      gfxsave=*) GFX_SAVE=$value                                 ;;

                        checkfs) FORCE_FSCK=true                                 ;;
                      nocheckfs) DO_FSCK=                                        ;;

#                         load=*) MODULE_LIST=$MODULE_LIST,$value                 ;;
#                           load) DO_LOAD=true                                    ;;
#                         noload) DO_LOAD=                                        ;;
                          noload) DISABLE_LOAD=true                              ;;
                        noload=*) NO_LOAD=$NO_LOAD,$value                        ;;

                      nohotplug) DO_HOTPLUG=                                     ;;
                      traceload) TRACE_LOAD=true                                 ;;
                 autoload|aload) FULL_AUTOLOAD=true                              ;;

                         init=*) INIT_PROG=$value                                ;;

                  nousb2|noehci) NO_EHCI=true                                    ;;

                       noerrlog) NO_ERR_LOG=true                                 ;;
                        errtest) TEST_ERRORS=true                                ;;
                      errtest=*) TEST_FATAL=$value                               ;;
                        noerr=*) NO_ERROR=$value                                 ;;

                      vtblank=*) valid_int VT_BLANK $param                       ;;

               livedir=*|ldir=*) FINAL_DIR=$value                                ;;

        #----- Some known codes -------------------------

        [sS1-6]|BOOT_IMAGE=*);;
        video.*);;

        # Most kernel codes from version 3.8
        forcepae|systemd.*);;
        acpi=*|acpi_rsdp=*|acpi_apic_instance=*|acpi_backlight=*);;
        acpi.debug_layer=*|acpi.debug_level=*|acpi_irq_balance);;
        acpi_irq_nobalance|acpi_irq_isa=*|acpi_irq_pci=*|acpi_no_auto_ssdt);;
        acpi_os_name=*|acpi_osi=*|acpi_pm_good|acpi_sci=*|acpi_serialize);;
        acpi_skip_timer_override|acpi_sleep=*|acpi_use_timer_override);;
        acpi_enforce_resources=*|add_efi_memmap|agp=*|ALSA|alignment=*);;
        align_va_addr=*|amd_iommu=*|amd_iommu_dump=*|amijoy.map=*);;
        analog.map=*|apc=*|apic=*|autoconf=*|show_lapic=*|apm=*|arcrimi=*);;
        ataflop=*|atarimouse=*|atkbd.extra=*|atkbd.reset=*|atkbd.set=*);;
        atkbd.scroll=*|atkbd.softraw=*|atkbd.softrepeat=*|baycom_epp=*);;
        baycom_par=*|baycom_ser_fdx=*|baycom_ser_hdx=*|boot_delay=*);;
        bootmem_debug|bttv.card=*|bttv.radio=*|bttv.pll=*|bttv.tuner=*);;
        bulk_remove=*|c101=*|cachesize=*|ccw_timeout_log|cgroup_disable=*);;
        checkreqprot|cio_ignore=*|clock=*|clocksource=*|clearcpuid=*|cma=*);;
        cmo_free_hint=*|coherent_pool=*|code_bytes|com20020=*|com90io=*);;
        com90xx=*|condev=*|conmode=*|console=*|consoleblank=*);;
        coredump_filter=*|cpuidle.off=*|cpcihp_generic=*|crashkernel=*);;
        cs89x0_dma=*|cs89x0_media=*|dasd=*|db9.dev[23]=*|ddebug_query=*|debug);;
        debug_locks_verbose=*|debug_objects|no_debug_objects);;
        debug_guardpage_minorder=*|debugpat|decnet.addr=*);;
        default_hugepagesz=*|dhash_entries=*|digi=*|digiepca=*|disable=*);;
        disable_ddw|disable_ipv6=*|disable_mtrr_cleanup|disable_mtrr_trim);;
        disable_timer_pin_1|dma_debug=*|dma_debug_entries=*);;
        dma_debug_driver=*|drm_kms_helper.edid_firmware=*|dscc4.setup=*);;
        dyndbg|dyndbg=*|module.dyndbg|module.dyndbg=*|earlycon=*);;
        earlyprintk=*|ekgdboc=*|edd=*|eisa_irq_edge=*|elanfreq=*|elevator=*);;
        elfcorehdr=*|enable_mtrr_cleanup|enable_timer_pin_1|enforcing);;
        erst_disable|ether=*|evm=*|failslab=*|fail_page_alloc=*);;
        fail_make_request=*|floppy=*|force_pal_cache_flush|ftrace=*);;
        ftrace_dump_on_oops|ftrace_dump_on_oops=*|ftrace_filter=*);;
        ftrace_notrace=*|ftrace_graph_filter=*|gamecon.map[23]=*|gamma=*);;
        gart_fix_e820=*|gcov_persist=*|gpt|grcan.enable0=*|grcan.enable1=*);;
        grcan.select=*|grcan.txsize=*|grcan.rxsize=*|hashdist=*|hcl=*|hd=*);;
        hest_disable|highmem=*|highres=*|hisax=*|hlt|hpet=*|hugepages=*);;
        hugepagesz=*|hvc_iucv=*|hvc_iucv_allow=*|keep_bootcon|i2c_bus=*);;
        i8042.debug|i8042.direct|i8042.dumbkbd|i8042.noaux|i8042.nokbd);;
        i8042.noloop|i8042.nomux|i8042.nopnp|i8042.notimeout|i8042.reset);;
        i8042.unlock|i810=*|i8k.ignore_dmi|i8k.force|i8k.power_status);;
        i8k.restricted|i915.invert_brightness=*|icn=*|ide-core.nodma=*);;
        ide-pci-generic.all-generic-ide|idle=*|ignore_loglevel);;
        ihash_entries=*|ima_appraise=*|ima_appraise_tcb|ima_audit=*);;
        ima_hash=*|ima_tcb|init=*|initcall_debug|initrd=*|inport.irq=*);;
        intel_iommu=*|intel_idle.max_cstate=*|intremap=*|iomem=*|iommu=*);;
        io7=*|io_delay=*|ip=*|ip2=*|irqfixup|irqpoll|isapnp=*|isolcpus=*);;
        iucv=*|js=*|keepinitrd|kernelcore=*|kgdbdbgp=*|kgdboc=*|kgdbwait);;
        kmac=*|kmemleak=*|kstack=*|kvm.ignore_msrs=*|kvm.mmu_audit=*);;
        kvm-amd.nested=*|kvm-amd.npt=*|kvm-intel.ept=*);;
        kvm-intel.emulate_invalid_guest_state=*|kvm-intel.flexpriority=*);;
        kvm-intel.nested=*|kvm-intel.unrestricted_guest=*|kvm-intel.vpid=*);;
        l2cr=*|l3cr=*|lapic|lapic=*|lapic_timer_c2_ok|libata.dma=*);;
        libata.ignore_hpa=*|libata.noacpi|libata.force=*|memblock=*);;
        load_ramdisk=*|lockd.nlm_grace_period=*|lockd.nlm_tcpport=*);;
        lockd.nlm_timeout=*|lockd.nlm_udpport=*|logibm.irq=*|loglevel=*);;
        log_buf_len=*|logo.nologo|lp=*|lpj=*|ltpc=*|machvec=*|machtype=*);;
        max_addr=*|maxcpus=*|max_loop=*|mce|mce=*|md=*);;
        mdacon=*|mem=*|memchunk=*|memmap=*|memory_corruption_check=*);;
        memory_corruption_check_size=*|memory_corruption_check_period=*);;
        memtest=*|meye.*=*|mfgpt_irq=*|mfgptfix|mga=*|min_addr=*|mini2440=*);;
        mminit_loglevel=*|module.sig_enforce|mousedev.tap_time=*);;
        mousedev.xres=*|mousedev.yres=*|movablecore=*|MTD_Partition=*);;
        MTD_Region=*|mtdparts=*|multitce=*|onenand.bdry=*|mtdset=*);;
        mtouchusb.raw_coordinates=*|mtrr_chunk_size=*|mtrr_gran_size=*);;
        mtrr_spare_reg_nr=*|n2=*|netdev=*|nf_conntrack.acct=*|nfsaddrs=*);;
        nfsroot=*|nfsrootdebug|nfs.callback_tcpport=*|nfs.cache_getent=*);;
        nfs.cache_getent_timeout=*|nfs.idmap_cache_timeout=*);;
        nfs.enable_ino64=*|nfs.max_session_slots=*);;
        nfs.nfs4_disable_idmapping=*|nfs.nfs4_unique_id=*);;
        nfs.send_implementation_id|nfsd.nfs4_disable_idmapping=*);;
        objlayoutdriver.osd_login_prog=*|nmi_debug=*|nmi_watchdog=*);;
        netpoll.carrier_timeout=*|no387|no_console_suspend|noaliencache);;
        noalign|noapic|noautogroup|nobats|nocache|noclflush|nodelayacct);;
        nodisconnect|nodsp|noefi|noexec|nosmap|nosmep|noexec32|nofpu|nofxsr);;
        noxsave|eagerfpu=*|nohlt|no-hlt|no_file_caps|nohalt|nohz=*|noiotrap);;
        noirqdebug|no_timer_check|noisapnp|noinitrd|nointremap|nointroute);;
        nojitter|no-kvmclock|no-kvmapf|no-steal-acc|nolapic|nolapic_timer);;
        noltlbs|nomca|nomce|nomfgpt|nonmi_ipi|nomodule|nopat|norandmaps);;
        noreplace-paravirt|noreplace-smp|noresidual|nordrand|noresume);;
        no-scroll|nosbagart|nosep|nosmp|nosoftlockup|nosync|notsc|nousb);;
        nowatchdog|nowb|nox2apic|cpu0_hotplug|nptcg=*|nr_cpus=*|nr_uarts=*);;
        numa_balancing=*|numa_zonelist_order=*|ohci1394_dma=*);;
        olpc_ec_timeout=*|omap_mux=*|oprofile.timer=*|oprofile.cpu_type=*);;
        oops=*|OSS|panic=*|parkbd.port=*|parkbd.mode=*|parport=*);;
        parport_init_mode=*|pause_on_oops=*|pcbit=*|pcd.|pci=*|pcie_aspm=*);;
        pcie_hp=*|pcie_ports=*|pcie_pme=*|pcmv=*|pd.|pdcchassis=*);;
        percpu_alloc=*|pf.|pg.|pirq=*|plip=*|pmtmr=*|pnp.debug=*|pnpacpi=*);;
        pnpbios=*|pnp_reserve_irq=*|pnp_reserve_dma=*|pnp_reserve_io=*);;
        pnp_reserve_mem=*|ports=*|print-fatal-signals=*);;
        printk.always_kmsg_dump=*|printk.time=*|processor.max_cstate=*);;
        processor.nocst|profile=*|prompt_ramdisk=*|psmouse.proto=*);;
        psmouse.rate=*|psmouse.resetafter=*|psmouse.resolution=*);;
        psmouse.smartscroll=*|pstore.backend=*|pt.|pty.legacy_count=*|quiet);;
        r128=*|raid=*|ramdisk_blocksize=*|ramdisk_size=*|rcu_nocbs=*);;
        rcu_nocb_poll|rcutree.blimit=*|rcutree.fanout_leaf=*);;
        rcutree.qhimark=*|rcutree.qlowmark=*|rcutree.rcu_cpu_stall_suppress=*);;
        rcutree.rcu_cpu_stall_timeout=*|rcutree.jiffies_till_first_fqs=*);;
        rcutree.jiffies_till_next_fqs=*|rcutorture.fqs_duration=*);;
        rcutorture.fqs_holdoff=*|rcutorture.fqs_stutter=*);;
        rcutorture.irqreader=*|rcutorture.n_barrier_cbs=*);;
        rcutorture.nfakewriters=*|rcutorture.nreaders=*);;
        rcutorture.onoff_holdoff=*|rcutorture.onoff_interval=*);;
        rcutorture.shuffle_interval=*|rcutorture.shutdown_secs=*);;
        rcutorture.stall_cpu=*|rcutorture.stall_cpu_holdoff=*);;
        rcutorture.stat_interval=*|rcutorture.stutter=*);;
        rcutorture.test_boost=*|rcutorture.test_boost_duration=*);;
        rcutorture.test_boost_interval=*|rcutorture.test_no_idle_hz=*);;
        rcutorture.torture_type=*|rcutorture.verbose=*|rdinit=*|reboot=*);;
        relax_domain_level=*|reserve=*|reservetop=*|reservelow=*);;
        reset_devices|resume=*|resume_offset=*|resumedelay=*|resumewait);;
        hibernate=*|retain_initrd|rhash_entries=*|riscom8=*|ro|root=*);;
        rootdelay=*|rootflags=*|rootfstype=*|rootwait|rw|S|sa1100ir|sbni=*);;
        sched_debug|skew_tick=*|security=*|selinux=*|apparmor=*|serialnumber);;
        shapers=*|show_msr=*|simeth=*|simscsi=*|slram=*|slab_max_order=*);;
        slub_debug|slub_debug=*|slub_max_order=*|slub_min_objects=*);;
        slub_min_order=*|slub_nomerge|smart2=*|smsc-ircc2.nopnp);;
        smsc-ircc2.ircc_cfg=*|smsc-ircc2.ircc_sir=*|smsc-ircc2.ircc_fir=*);;
        smsc-ircc2.ircc_irq=*|smsc-ircc2.ircc_dma=*);;
        smsc-ircc2.ircc_transceiver=*|softlockup_panic=*|sonypi.*=*);;
        specialix=*|spia_io_base=*|spia_fio_base=*|spia_pedr=*|spia_peddr=*);;
        stacktrace|stacktrace_filter=*|sti=*|sti_font=*|stifb=*);;
        sunrpc.min_resvport=*|sunrpc.max_resvport=*|sunrpc.pool_mode=*);;
        sunrpc.tcp_slot_table_entries=*|sunrpc.udp_slot_table_entries=*);;
        swapaccount|swapaccount=*|swiotlb=*|switches=*|sysfs.deprecated=*);;
        sysrq_always_enabled|tdfx=*|test_suspend=*|thash_entries=*);;
        thermal.act=*|thermal.crt=*|thermal.nocrt=*|thermal.off=*);;
        thermal.psv=*|thermal.tzp=*|threadirqs|topology=*|tp720=*);;
        tpm_suspend_pcr=*|trace_buf_size=*|trace_event=*|trace_options=*);;
        transparent_hugepage=*|tsc=*|turbografx.map[23]=*|udbg-immortal);;
        uhash_entries=*|uhci-hcd.ignore_oc=*|unknown_nmi_panic);;
        usbcore.authorized_default=*|usbcore.autosuspend=*);;
        usbcore.usbfs_snoop=*|usbcore.blinkenlights=*);;
        usbcore.old_scheme_first=*|usbcore.usbfs_memory_mb=*);;
        usbcore.use_both_schemes=*|usbcore.initial_descriptor_timeout=*);;
        usbhid.mousepoll=*|usb-storage.delay_use=*|usb-storage.quirks=*);;
        user_debug=*|userpte=*|vdso=*|vdso32=*|vector=*|video=*);;
        virtio_mmio.device=*|vga=*|vmalloc=*|vmhalt=*|vmpanic=*|vmpoff=*);;
        vsyscall=*|vt.cur_default=*|vt.default_blu=*|vt.default_grn=*);;
        vt.default_red=*|vt.default_utf8=*|vt.global_cursor_default=*);;
        watchdog|x2apic_phys|x86_mrst_timer=*|xd=*|xd_geo=*|xen_emul_unplug=*);;
        xirc2ps_cs=*);;

        nomodeset|*.modeset=*);;
        nosplash|splash=*|fbcon=*);;

        #----------------------------------------------------------------------
        # NOTE: see /live/custom/$DISTRO_NAME/1.sh for distro specific boot codes
        #----------------------------------------------------------------------

        *) UNKNOWN_BOOTCODES="$UNKNOWN_BOOTCODES $param"
        esac
    done
}

valid_int() {
    local var=$1 val=${2#*=} name=${2%%=*} param=$2

    case $val in
                      "")                   return ;;
        [0-9]|[0-9][0-9]) eval $var=\$val;  return ;;

    esac
    INTEGER_ERRORS="$INTEGER_ERRORS $param"
}

start_hotplug() {
    local do_hotplug=$1  trace_load=$2

    [ "$DISABLE_LOAD" ] && return

    # Set up file for recording sequence of kernel events sent to mdev
    echo > /dev/mdev.seq
    if [ "$trace_load" ]; then
        echo $LIVE_BIN/mdev-trace > /proc/sys/kernel/hotplug
    elif [ -n "$do_hotplug" ]; then
        echo $LIVE_BIN/mdev-hotplug > /proc/sys/kernel/hotplug
    fi
}

stop_hotplug() { echo > /proc/sys/kernel/hotplug; }

list_modules() {
    local mod list cnt=0
    list=$( echo $(lsmod | grep "^[a-z]" | cut -d" " -f1) | sort)
    cnt=$(echo $list | wc -w)

    #. loaded <count> modules(s)
    vmsg 6  "$_Loaded_X_module_s_" $(nq $cnt)

    [ "$list" ] && vmsg 7 "$white$list"

    [ "$ERR_MODULES" ] || return
    local ecnt=$(echo "$ERR_MODULES" | wc -w)

    #. <count> module(s) failed to load
    vmsg 6  "$_X_module_s_failed_to_load_" $(nq $ecnt)

    vmsg 7 "$white$ERR_MODULES"

}

select_breakpoints() {
    case $BREAK_POINTS in
        *\?*|*ask*)
            echo
            echo "${m_co}Remaining $hi_co$0$m_co breakpoints:$nc_co"
            echo
            sed -rn 's/^\s*breakpoint ([1-9a-z][0-9a-z]?)/  \1)/p' $0 | sed "s/['\"]//g" | sort
            echo
            echo "${m_co}Use \"a\" to set most breakpoints$nc_co"
            echo "${m_co}Use \"A\" to set all breakpoints$nc_co"
            echo
            printf "${ok_co}%s$m_co: $nc_co"  "$_Enter_breakpoint_s_separated_by_commas_"
            setsid cttyhack > /dev/null
            read BREAK_POINTS;;
    esac
}

mem_info() {
    local info amt kb
    while read info amt kb; do
        [ ! "$info" = "$1:" ] && continue
        echo $((amt / 1024))
        return
    done </proc/meminfo
}

clean_xlat() {
    find $LOCALE_DIR/xlat -name init.xlat -delete
    rm -rf $LOCALE_DIR/fonts
}

read_xlat() {
    local prog=$1  lang=$2
    local xdir=$LOCALE_DIR/xlat
    local fdir=$LOCALE_DIR/fonts

    local xlat=$xdir/en/$prog.xlat
    [ -r $xlat ] && . $xlat

    [ "$lang" ] || return

    lang=$(echo $lang | sed 's/_.*//')

    xlat=$xdir/$lang/$prog.xlat
    [ -r "$xlat" ] || return
    . $xlat
    vmsg 7 'Translate to %s' $lang

    local font=$fdir/$lang
    [ -e "$font" ] && log_cmd setfont $font -C $(tty)
}

do_welcome() {

    [ "$NO_CLEAR" ] || clear   # Clear and reset screen

    local pretty_name=$1 version=$2  v_date=$3

    vmsg 2  "$_Welcome_to_X_" "$(pq $(uname -m) $pretty_name)!"

    # Get total ramsize, and available real ram in MB. We need this later.

    local total_mem=$(mem_info MemTotal)
    local used_mem=$((total_mem - FREE_MEM))

    vmsg 6 "$white  $(busybox | head -n 1)"

    [ -x /bin/ntfs-3g ] && vmsg 6 "$white  $(ntfs-3g --version 2>&1)"

    vmsg 6 "%25s: $white%s"  "$0 version" "$version"
    vmsg 6 "%25s: $white%s"  "$0 built"   "$v_date"

    # Print meminfo.
    local mem_format="${m_co}%s: $num_co%5d${m_co} M"
    vmsg 6 "$mem_format"   "$_Total_Memory_" $total_mem
    # vmsg 6 "$mem_format"  "              Free Memory"  $FREE_MEM
    vmsg 6 "$mem_format"   "$_Used_Memory_"  $used_mem

    local cpu_format="%s:$white %s"
    vmsg 6 "$cpu_format"   "$_Linux_kernel_" "$(uname -r)"
    vmsg 6 "$cpu_format"   "$_Screen_width_" "$SCREEN_WIDTH"
    vmsg 6 "$cpu_format"   "$_Kernel_arch_" "$(uname -m)"

    # vmsg 6  "$cpu_format" "                      CPU" "$(cpu_param 'model name' unknown)"
    # vmsg 6  "$cpu_format" "                    Cores" "$num_co$(cpu_param 'cpu cores'  1)"
    # vmsg 6  "$cpu_format" "                    Cache" "$(cpu_param 'cache size' unknown)"
}

show_bootcodes() {
    local unknown=$1

    vmsg 5 "%s:"  "$_Current_boot_codes_"
    vmsg 5 "    $white $(cat /proc/cmdline)"

    [ -n "$unknown"  -a -n "$CHECK_BOOTCODES" ] || return

    vmsg 4
    vmsg 4 "$warn_co%s:"  "$_Possibly_unknown_or_misspelled_boot_codes_"
    vmsg 4 "(%s)"  "$_dont_take_this_too_seriously_"
    local code
    for code in $unknown; do
        vmsg 4 "$hi_co    $code"
    done
    vmsg 4

}

show_integer_errors() {
    local param name value params=$1

    #. Only small integer values are allowed for <name>.  Will ignore <name=value>
    for param in $params; do
        name=${param%%=*}
        value=${param#*=}
        warn  "$_Only_small_integer_values_are_allowed_for_X_Will_ignore_Y_" \
            $(pqw $name) $(pqw $param)
    done
}

cpu_param() {
    local result=$(grep "^$1" /proc/cpuinfo | head -n 1 | sed 's/.*\t: //')
    [ "$result" ] || result=$2
    echo "$result"
}

disable_hcd() {
    local type=$1
    local dir=/sys/bus/pci/drivers/${type}_hcd
    local symlink kmsg=/dev/kmsg
    for symlink in $(find $dir -name "0*" | sed 's=.*/=='); do
        echo $symlink >> $DISABLED_USB_FILE.$type
        msg 'disable %s bus: %s' $(nq $type) $(pq $symlink)
        [ -e $kmsg ] && echo "$0 disabled $type: $symlink" >> $kmsg
        echo $symlink > $dir/unbind
    done
}

load_kernel_modules() {
    local no_load=$1 do_load=$2  file=$CUSTOM_DIR/$DISTRO_NAME/modules.load

    if [ "$no_load" ]; then
        no_load=$(echo $no_load | sed 's/VIDEO/nouveau,nvidia,i915,radeon/');
        mkdir -p /etc/modprobe.d/
        echo $no_load | sed 's/,/\n/g' | sed -r 's/^([a-z])/blacklist \1/' >> /etc/modprobe.d/initrd.conf
    fi

    [ "$DISABLE_LOAD" ] && return

    mkdir -p $MOD_DIR
    depmod &>/dev/null

    # These ones must be loaded before the others
    local mod
    for mod in $LOAD_FIRST; do
        modprobe -q -b $mod 2>/dev/null
    done

    if [ -r $file -a -n "$do_load" ]; then
        vmsg 7 'Load modules from file %s' "$(fq $file)"
        for module in $(grep -v "^\s*#" $file | sed 's/\s*#.*//' ); do
            modprobe -q -b $module
        done
    fi
    autoload_modules 6
}

load_all_modules() {

    msg  "$_Loading_all_live_modules_"
    local list=$(find $MOD_DIR/live -name "*.ko" | sed -e 's=.*/==' -e 's/\.ko$//')
    local module
    for module in $list; do
        modprobe -q -b $module
    done
}

autoload_modules() {
    local alias  module  count  verb=$1
    [ "$DISABLE_LOAD" ] && return

    [ "$verb" ] && vmsg $verb  "$_Load_hardware_specific_modules_"
    for alias in $(find /sys/devices -name modalias -print0 | xargs -0 cat 2>/dev/null| sort -u); do
        $LIVE_BIN/modprobe -q -b $alias &>/dev/null
        #[ "$TRACE_LOAD" ] && echo $alias >> /live/config/auto-load.log
        local log_file=/live/config/auto-load.log
        grep -q "^$alias$" $log_file 2>/dev/null || echo $alias >> $log_file
    done
}

#==============================================================================
# Find the squashfs file
#==============================================================================

#------------------------------------------------------------------------------
# Function: is_usb_or_removable <device>
#------------------------------------------------------------------------------

is_usb_or_removable() {
    local short_dev=${1##*/}
    local dir=/sys/block/${short_dev%%[0-9]*}/
    [ "$(cat $dir/removable 2>/dev/null)" = 1 ] && return 0
    local devpath=$(readlink -f $dir/device)
    [ "$devpath" ] || return 1
    echo $devpath | grep -q /usb
    return $?
}

#------------------------------------------------------------------------------
# Function: from_filter <device-list>
#
# Filter and order devices in list according to types listed in $FROM_TYPE.
# Output goes into FILTERED_LIST so we can give error messages from within.
# Return true if there is at least one item in the FILTERED_LIST otherwise
# return false.  Error out if there is an invalid from= type.
#
# See LINUX ALLOCATED DEVICES for device numbers
# https://www.kernel.org/doc/Documentation/devices.txt
#------------------------------------------------------------------------------
from_filter() {

    FILTERED_LIST=

    local from_type invalid_scan
    case ,$FROM_TYPE, in
        *,all,*) from_type=$FROM_TYPE_ALL;;
              *) from_type=$FROM_TYPE;;
    esac

    # First segregate devices by type
    local dev cd_devs hd_devs usb_devs
    for dev; do
        [ -b "$dev" ] || continue
        case $(stat -c %t $dev) in

            b) cd_devs="$cd_devs $dev" ;;
         [38]) if is_usb_or_removable $dev; then
                    usb_devs="$usb_devs $dev"
                else
                    hd_devs="$hd_devs $dev"
                fi ;;
        esac
    done

    local type
    for type in $(echo $from_type | sed 's/,\+/ /g'); do
        case $type in

            cd) FILTERED_LIST="$FILTERED_LIST$cd_devs"
                cd_devs= ;;

            usb) FILTERED_LIST="$FILTERED_LIST$usb_devs"
                usb_devs= ;;

            hd) FILTERED_LIST="$FILTERED_LIST$hd_devs"
                hd_devs= ;;

           "") ;;

            *) invalid_scan="$invalid_scan $type";;
        esac
    done

    #. Invalid <parameter-name> value(s) <bad values>
    [ "$invalid_scan" ] \
        && _fatal "$(printf  "$_Invalid_X_values_s_Y_" "$(pqh from=)" "$(pqh $invalid_scan)")" \
        "$(printf  "$_Valid_values_are_X_" "$(cq cd hd usb all)")"

    [ "$FILTERED_LIST" ]

    return $?
}

find_boot_file() {
    local ret
    find_files boot "$@"
    ret=$?
    local file=$1

    case $ret in
        10) fatal  "$_No_X_devices_found_" block             ;;
        20) fatal  "$_No_X_devices_found_" $(pqh $FROM_TYPE) ;;
        30) _fatal "" \
            "$(printf  "$_The_X_parameter_is_blocking_device_Y_" \
            $(pqh from=$FROM_TYPE) "$(pqh $DEVICE_LIST)")"     \
            "$(printf  "$_Even_though_device_X_has_Y_" "$(pqh $DEVICE_LIST)" "$(pqh $device_id)")" \
            "$(printf  "$_Remove_the_X_boot_parameter_to_allow_that_device_to_be_scanned_" \
            $(cq $FROM_TYPE))" ;;
        40) fatal  "$_Device_found_but_could_not_find_X_file_on_device_"  $(pqh $file) ;;
    esac

    return $ret
}

#------------------------------------------------------------------------------
# Function: find_files <files> <mntpnt> <dev> <label> <uuid> <retry>
#
# The outer wrapper lets us show the loop times regardless of how we left the
# main code in _find_files().
#------------------------------------------------------------------------------

find_files() {
    local ret loop_times start_t=$(get_time) t2 elapsed  find_type=$1 files=$2 device_id=$4
    _find_files "$@"
    ret=$?
    t2=$(get_time)
    elapsed=$(get_seconds $((t2 - start_t)))
    vmsg 6  "$_Spent_X_seconds_looking_for_Y_file_s_Z_" $(nq $elapsed) $(fq $find_type) "$(fq $files)"
    [ "$loop_times" ] && vmsg 6 "loop times:$num_co$loop_times"

    [ "$find_type" != boot ] && return $ret

    return $ret
}

_find_files() {
    local find_type=$1 files=$2  mp_orig=$3  device_id=$4  retry_time=$5

    local short_files f
    if [ ${#files} -gt 20 ]; then
        for f in $files; do
            short_files="$short_files$(basename $f) "
        done
    else
        short_files=$files
    fi

    local last_list root_device partition partition_error

    if [ -n "$device_id" -a -z "${device_id##name=*}" ]; then
        partition=${device_id##name=}
        partition=/dev/${partition##*/}
        root_device=${partition%[0-9]}
        root_device=${root_device%[0-9]}
    fi

    unset FOUND_DEV FOUND_MP

    local be_verbose vthresh=8
    [ "$VERBOSE" -ge $vthresh ] && be_verbose=true

    mkdir -p "$mp_orig"

    local end_t  loop_t1  usleep  dt
    local start_t=$(get_time)  current_t=0
    local try=-1  have_devices  final_try  dot=.  dt_secs

    # Stay in loop until success or time goal_t is reached
    local goal_t=$((start_t + retry_time * 100))

    local do_delay said_retry mounted_device

    while [ -z "$final_try" ]; do

        try=$((try + 1))
         [ "$be_verbose" ] && cnt_up=$(nq "$(printf "%2d" $try)")

        #-- delay every time except inside first second
        if [ "$do_delay" ]; then
            if [ -z "$said_retry" ]; then
                vmsgN 4  "$_Retry_for_X_seconds_" $(nq $retry_time)
                vmsg_if $vthresh " ..."
                said_retry=true
            fi

            [ "$FULL_AUTOLOAD" ] && autoload_modules

            # Normally just print out a dot for each iteration
            [ "$be_verbose" ] || msgN $dot

            # Adjust sleep time dynamically to account for previous time
            # through the loop

            current_t=$(get_time)
            dt=$((current_t - loop_t1))
            usleep=$(( ($RETRY_DELAY - dt ) * 10000))
            dt_secs=$(get_seconds $dt)
            loop_times="$loop_times $dt_secs"
            vmsg_if 9 "loop time:$num_co $dt_secs"

            # Once we have exceeded the time limit we go through the
            # loop one final time.
            [ $current_t -gt $goal_t ] && final_try=true

            # Only sleep long enough so total time through loop is constant
            [ $usleep -gt 0 ] && usleep $usleep
        else
            [ $(($(get_time) - start_t)) -gt 99 ] && do_delay=true
        fi

        loop_t1=$(get_time)

        # This disables usb-2
        [ "$NO_EHCI" ] && disable_hcd ehci

        unset DEVICE_LIST FILTERED_LIST

        #----------------------------------------------------------------------
        # Either scan all available block devices because no uuid, label, or
        # device was specified ...
        #----------------------------------------------------------------------
        if [ -z "$device_id" ]; then

            #. Scan <type> devices for <type> file(s) <file names>
            [ "$try" = "0" ] \
                && vmsg 5  "$_Scan_X_devices_Look_for_Y_file_s_Z_"  $(dq $FROM_TYPE) $find_type "$(fq $short_files)"

            DEVICE_LIST=$(most_block_devices)

            if [ -z "$DEVICE_LIST" ]; then
                [ "$final_try" ] && return 10

                vmsg_if $vthresh "%s No %s devices found" "$cnt_up" block
                continue
            fi

            # Filter and order devices according to from= parameter
            if ! from_filter $DEVICE_LIST; then
                [ "$final_try" ] && return 20

                vmsg_if $vthresh "%s No %s devices found" "$cnt_up" $(pq $FROM_TYPE)
                continue
            fi

            DEVICE_LIST=$FILTERED_LIST

            # pure window dressing
            if [ -z "$have_devices" ]; then
                [ $try -gt 0 ] && vmsg 5
                vmsg 5  "$_Filtered_devices_X_" "$white$DEVICE_LIST"
            else
                vmsg_if $vthresh "%s Filtered devices %s" "$cnt_up" "$(pq $DEVICE_LIST)"
            fi

        #----------------------------------------------------------------------
        # ... Or look for specific devices because a uuid, label, or device was
        # specified
        #----------------------------------------------------------------------
        else
            if [ "$mounted_device" ]; then
                FOUND_ID_DEVICE=$mounted_device
                return 40
            fi

            # Give up early if the root device is found but the partition is not
            # But wait a second in case there is a delay for the partitions to show
            if [ "$partition_error" ]; then
                warn  "$_Found_root_device_X_but_not_partition_Y_" "$(pqw $root_device)" "$(pqw $partition)"
                sleep 1
                final_try=true
            fi

            [ "$root_device" -a -e "$root_device" -a ! -e $partition ] && partition_error=true

            find_device "$find_type" "$try" "$final_try" "$device_id" || return $?
            if [ -z "$DEVICE_LIST" ]; then
                vmsg_if $vthresh "%s No %s devices yet found" "$cnt_up" $(pq $FROM_TYPE)
                continue
            fi

            #------------------------------------------------------------------
            # When a device has been specified via label, uuid, or /dev/node
            # then only filter and order if from= parameter is given explicitly
            #------------------------------------------------------------------
            if [ "$ALWAYS_SCAN" ]; then
                if ! from_filter $DEVICE_LIST; then
                    [ "$final_try" ] && return 30
                    continue
                fi

                DEVICE_LIST=$FILTERED_LIST
            fi

            # pure window dressing
            if [ -z "$have_devices" ]; then
                [ "$try" != "0" ] && msg
                #. Look for files(s) <file-list> on device(s) <device list>
                vmsg 4  "$_Look_for_file_s_X_on_device_s_Y_" "$(pq $short_files)" "$(dq $DEVICE_LIST)"
            else
                #vmsg_if $vthresh
                vmsg_if $vthresh "%s Look for %s on %s" "$cnt_up" "$(pq $short_files)" "$(dq $DEVICE_LIST)"
            fi
        fi

        #----------------------------------------------------------------------
        # Now we have some device or devices to mount and search
        #----------------------------------------------------------------------

        # more window dressing
        if [ -n "$have_devices" -a "$last_list" != "$DEVICE_LIST" ]; then
            msg
            msg  "$_Found_new_device_s_X_" "$(hq $DEVICE_LIST)"
        fi
        last_list=$DEVICE_LIST

        have_devices=true

        #-- now try to mount each device and find at least one of the $files ...
        local dev fname mntpnt mounted already
        for dev in $DEVICE_LIST; do

            # use existing mountpoint if device is already mounted
            mntpnt="$(get_mountpoint $dev)"
            if [ "$mntpnt" ]; then
                mounted=
                mounted_device=$dev
                [ "$already" ] || vmsg 5  "$_Device_X_is_already_mounted_at_Y_"  "$(dq $dev)" "$(mpq $mntpnt)"
                already=true
            else
                try_mount $dev $mp_orig || continue
                mounted_device=$dev
                mounted=true
                mntpnt=$mp_orig

            fi
            if [ -n "$device_id" -a "$find_type" = persist ]; then
                if [ "$DID_FRUGAL" ]; then
                    create_persist_files "$mntpnt" "$PERSIST_PATH" "$WANT_ROOTFS" "$WANT_HOMEFS"
                else
                    create_persist_files "$mntpnt" "$PERSIST_PATH" "$NEED_ROOTFS" "$NEED_HOMEFS"
                fi
            fi

            for fname in $files; do
                [ -f "$mntpnt/$fname" ] || continue
                if [ "$mntpnt" = "$mp_orig" ]; then
                    #. Mounted <type> <device> at <mount point>
                    vmsg 4  "$_Mounted_X_device_Mounted_device_Y_at_Z_" \
                        $find_type "$(fq $dev)" "${to_co}$mntpnt${m_co}"

                    #. <type> device filesystem: <filesystem-name>
                    vmsg 5  "$_X_device_filesystem_Y_" $find_type \
                        "$(cq $(grep " $mntpnt " /proc/mounts | cut -d" " -f3))"

                    local short_dev=${dev##*/}
                    local model=$(cat /sys/block/${short_dev%%[0-9]*}/device/model 2>/dev/null)

                    #. <type> device model: <model-name>
                    [ "$model" ] && vmsg 6  "$_X_device_model_Y_" $find_type "$(cq $model)"

                else
                    rmdir $mp_orig
                fi
                FOUND_DEV=$dev
                FOUND_MP=$mntpnt
                return 0
            done
            [ "$mounted" ] && mountpoint -q $mp_orig && umount $mp_orig
        done
    done
    [ "$try" != "0" ] && msg
    return 90
}

#------------------------------------------------------------------------------
# Function find_device: <type> <try> <final_try> <device_id>
#
# Find the device(s) associated with a device name, a label, or a uuid.  Puts
# the result in $DEVICE_LIST so are free to echo error messages.  Returns true
# on success or if we need to retry.  Returns false if there was a final error.
#------------------------------------------------------------------------------

find_device() {
    local err_ret find_type=$1  try=$2  final_try=$3  type=${4%%=*}  value=${4#*=}

    DEVICE_LIST=

    #. Look for <type> devices with <attribute X>
    [ "$try" = 0 ] && msg  "$_Look_for_X_device_with_Y_" "$(fq $find_type)" "$type $(cq $value)"

    case $type in
        name) DEVICE_LIST=$(cleanse_dev  $value)  ; err_ret=1 ;;
       label) DEVICE_LIST=$(label_to_dev $value)  ; err_ret=2 ;;
        uuid) DEVICE_LIST=$(uuid_to_dev  $value)  ; err_ret=3 ;;
           *) fatal 'Internal error: find_device() type=%s value=%s' "$type" "$value" ;;
    esac

    [ "$DEVICE_LIST" ] && return 0
    [ "$final_try" ]   || return 0

    [ "$try" != 0 ] && msg

    if [ "$find_type" != boot ]; then
        return 100
    fi

    [ "$AUTO_PERSIST"       ] && return 100
    [ "$find_type" = frugal ] && return 100

    general_device_error "$find_type" "$type" "$value"
    return $err_ret
}

device_uuid() {
    local device=$1 LABEL TYPE UUID
    eval $(blkid $device | sed 's/^[^:]*://')
    echo $UUID
}

device_label() {
    local device=$1 LABEL TYPE UUID
    eval $(blkid $device | sed 's/^[^:]*://')
    echo $LABEL
}

device_fstype() {
    local device=$1 LABEL TYPE UUID
    eval $(blkid $device | sed 's/^[^:]*://')
    echo $TYPE
}

frugal_error() {
    local mp

    for mp in $FRUGAL_MP $SQFS_MP; do
        mountpoint -q $mp && umount $mp
        [ -d $mp ] && rmdir $mp
    done

    non_fatal  "$_Frugal_install_failed_or_was_teminated_"

}

find_frugal_file() {
    local frugal_id=$1  bdir=$DISTRO_NAME-Frugal-$(uname -r)
    local sqfile=$bdir/$SQFILE_NAME need_to_ask
    local device name=${frugal_id%%=*}  value=${frugal_id#*=}

    find_files frugal "$sqfile" "$BOOT_MP" "$frugal_id" "$RETRY_TIME"
    local ret=$?

    if [ $ret -eq 0  -a -z "$FORCE_FRUGAL" ]; then
        msg  "$_Found_existing_frugal_install_"
        SQFILE_MP=$BOOT_MP
        SQFILE_DEV=$FOUND_DEV

        SQFILE_FULL=$BOOT_MP/$sqfile
        SQFILE_PATH=${sqfile%/*}
        DEFAULT_PERSIST_PATH=$SQFILE_PATH
        SQFILE_DIR=$(dirname $SQFILE_FULL)
        DEFAULT_DIR=$SQFILE_DIR

        return 0

    elif [ $ret -eq 40 ]; then
        device=$FOUND_ID_DEVICE
        need_to_ask=true

        warn
        warn  "$_Found_X_device_with_Y_" $(cqw frugal)  "$name $(cqw $value)"
        warn  "$_But_did_not_find_file_X_on_the_device_" "$(cqw $sqfile)"
        warn  "$_Will_start_normal_boot_to_do_frugal_install_"
        warn

    elif [ "$FORCE_FRUGAL" ]; then
        msg  "$_Force_frugal_install_"
        if [ $ret -eq 0  ]; then
            device=$FOUND_DEV
            umount $FOUND_DEV
        fi

    else
        warn
        warn  "$_Could_not_find_X_device_with_Y_" $(cqw frugal) "$name $(cqw $value)"
        warn  "$_Will_start_normal_boot_to_do_frugal_install_"
        warn
    fi

    find_boot_file "$SQFILE_FILE" "$BOOT_MP" "$BOOT_ID" "$RETRY_TIME" \
        || linuxfs_error  "$_Could_not_find_X_file_Y_" "$LINUXFS_NAME" "$(fqe \"$SQFILE_FILE\")"

    SQFILE_MP=$BOOT_MP
    SQFILE_DEV=$FOUND_DEV

    SQFILE_FULL=$BOOT_MP/$SQFILE_FILE
    SQFILE_PATH=${SQFILE_FILE%/*}
    DEFAULT_PERSIST_PATH=$SQFILE_PATH

    mount_linuxfs "$SQFS_MP" "$SQFILE_FULL" "$WRAP_FILE_MP"

    check_kernel_version $SQFS_MP

    prep_ld_path

    local old_dir=$(dirname $SQFILE_FULL)
    local actual=$(file_usage_m $old_dir $FRUGAL_FILES)

    # Safety first!
    local needed=$((actual + 10))

    [ "$FRUGAL_NEEDED" ] && needed=$FRUGAL_NEEDED

    if  [ -z "$device" ]; then

        msg  "$_Looking_for_partitions_with_at_least_X_free_Please_wait_" "$(nq $(size_label_m $needed))"

        select_device  "$_frugal_" device $needed not_mounted || return 2

        local ret=$?

        [ -z "$device" -o "$device" =  "$_quit_" ] && return 1

        [ "$FRUGAL" ] && label_device $device "$DISTRO_NAME-Frugal"
    fi

    if ! mkdir -p $FRUGAL_MP; then
        err  "$_Could_not_make_frugal_directory_"
        return 10
    fi

    if ! try_mount $device $FRUGAL_MP rw; then
        err  "$_Could_not_mount_frugal_device_X_" $(pqe $device)
        return 20
    fi

    local sqfile_full=$FRUGAL_MP/$sqfile
    local sqfile_dir=$(dirname $sqfile_full)

    [ "$FORCE_FRUGAL" ] && rm -rf $sqfile_dir

    breakpoint f "in frugal install"

    if [ -e $sqfile_full ]; then
        msg  "$_Found_existing_frugal_install_"
    else

        local free=$(free_space $FRUGAL_MP)

        vmsg 5 'Device %s has %s free' $(pq $device) "$(nq $(size_label_m $free))"

        if [ $free -lt $needed ]; then
            err  "$_Not_enough_free_space_on_device_X_to_do_install_" $(pqe $device)
            return 20
        fi

        if [ "$need_to_ask" ]; then
            YES_no  "$_Do_you_want_to_do_a_frugal_on_the_X_device_" $(pq $device) || return 2
        fi

        heading  "$_Frugal_install_please_be_patient_"
        log_cmd mkdir -p $sqfile_dir || return 30

        msg  "$_copy_X_to_Y_" $(pq $(size_label_m $actual)) $(pq $sqfile_dir/)

        if ! time_cmd copy_files $old_dir $sqfile_dir $FRUGAL_FILES; then
            warn  "$_Copy_failed_Erasing_partial_copy_"
            log_cmd rm -f $sqfile_full
            return 40
        fi

        warn  "$_The_frugal_install_succeeded_"
        DID_FRUGAL=true
    fi

    log_cmd umount $SQFS_MP
    log_cmd umount $BOOT_MP

    mount --move $FRUGAL_MP $BOOT_MP
    rmdir $FRUGAL_MP

    SQFILE_MP=$BOOT_MP
    SQFILE_DEV=$device

    SQFILE_FULL=$BOOT_MP/$sqfile
    SQFILE_PATH=${sqfile%/*}
    DEFAULT_PERSIST_PATH=$SQFILE_PATH
    SQFILE_DIR=$(dirname $SQFILE_FULL)
    DEFAULT_DIR=$SQFILE_DIR

    return 0
}

find_linuxfs_file() {

    if [ -n "$ISO_FILE" -o -n "$FROM_ISO" ]; then

        : ${ISO_FILE:=$DEFAULT_ISO_FILE}
        ISO_FILE=${ISO_FILE#/}

        heading "${cheat_co}fromiso"

        #. Could not find <type> file <file name>
        find_boot_file "$ISO_FILE" "$ISO_DEV_MP" "$BOOT_ID" "$RETRY_TIME" \
            || fatal  "$_Could_not_find_X_file_Y_" iso "$(pqh $ISO_FILE)"

        local iso_full=$ISO_DEV_MP/$ISO_FILE
        DEFAULT_PERSIST_PATH=${ISO_FILE%/*}

        # Check md5 of files in directory containing iso file
        [ "$CHECK_MD5" ] && check_md5 $iso_full

        # Mount the iso file
        mkdir -p $ISO_FILE_MP
        mount -t iso9660 -o loop,ro $iso_full $ISO_FILE_MP \
            || fatal  "$_Could_not_mount_X_as_a_Y_file_" "$(pqh $iso_full)" 'iso'

        # Find the linuxfs file
        SQFILE_FULL="$ISO_FILE_MP/$SQFILE_FILE"
        [ -f "$SQFILE_FULL" ] \
            || linuxfs_error  "$_File_X_not_found_on_device_Y_" "$LINUXFS_NAME" "$SQFILE_FULL" "$FOUND_DEV"

        SQFILE_MP=$ISO_FILE_MP
        SQFILE_DEV=$FOUND_DEV   # Used for toram-eject
        SQFILE_PATH=${SQFILE_FILE%/*}

        DID_ISO=true
    else

        find_boot_file "$SQFILE_FILE" "$BOOT_MP" "$BOOT_ID" "$RETRY_TIME" \
            || linuxfs_error  "$_Could_not_find_X_file_Y_" "$LINUXFS_NAME" "$(fqe \"$SQFILE_FILE\")"

        SQFILE_MP=$BOOT_MP

        SQFILE_DEV=$FOUND_DEV

        SQFILE_FULL=$BOOT_MP/$SQFILE_FILE
        SQFILE_PATH=${SQFILE_FILE%/*}
        DEFAULT_PERSIST_PATH=$SQFILE_PATH

    fi

    SQFILE_DIR=$(dirname $SQFILE_FULL)
    DEFAULT_DIR=$SQFILE_DIR
}

linuxfs_error() {
    local msg=$(printf "$@")  device_list=$(echo $DEVICE_LIST)
    local all_devices=$(echo $(most_block_devices));

    _fatal "$msg" \
        "$(printf  "$_Searched_devices_X_" "${hi_co}$device_list")" \
        "$(printf  "$_Searched_types_X_" "${hi_co}$FROM_TYPE")"   \
        "$(printf  "$_All_block_devices_X_" "${hi_co}$all_devices")" \
        "" \
        "$(printf  "$_Please_contact_X_at_Y_" "$DEVELOPER" "$DISTRO_BUG_REPORT_URL")"
}

check_kernel_version() {
    local mp=$1
    local kversion=$(uname -r)
    local mod_dir=/lib/modules/$kversion

    [ -d $mp$mod_dir ] && return

    _non_fatal  "$_Kernel_version_mismatch_" \
        "$(printf  "$_Kernel_version_X_" $(pqh $kversion))" \
        "$(printf  "$_The_module_directory_X_is_missing_" $(pqh $mp$mod_dir))"
}

#==============================================================================
# Mount things
#==============================================================================

mount_linuxfs() {

    local sqfs_mp=${1:-$SQFS_MP}  sqfile_full=${2:-$SQFILE_FULL}
    local wrap_mp=${3:-$WRAP_FILE_MP}  vid_file=$1$VID_FILE

    mkdir -p $sqfs_mp
    vmsg 6  "$_Mount_file_X_at_Y_" "$(fq $sqfile_full)" "$(fq $sqfs_mp)"
    #. Could not mount <file name> as a <type> filesystem
    log_cmd mount -t squashfs -o loop,ro $sqfile_full $sqfs_mp \
        || fatal  "$_Could_not_mount_X_as_a_Y_filesystem_" $sqfile_full squashfs

    if [ -n "$wrap_mp" -a -e "$sqfs_mp/$SQFILE_NAME" -a ! -d "$sqfs_mp/usr" ]; then
        msg 'Found wrapped filesystem. Move wrapper ...'
        mkdir -p $wrap_mp
        mount --move $sqfs_mp $wrap_mp

        try_file_mount  $wrap_mp/$SQFILE_NAME $sqfs_mp ro \
            || fatal  "$_Could_not_mount_X_as_a_Y_file_" $sqfile_full 'filesystem'
    fi

    LINUXFS_DEV=$(df $sqfile_full | tail -n1 | cut -d" " -f1)

    SQFS_VID=$(get_vid $vid_file)

    if [ "$SQFS_VID" ]; then
        vmsg 7 'sqfs_vid: %s' "$nc_co$SQFS_VID"
    else
        vmsg 7  "$_No_X_found_at_Y_" "$VID_NAME" "$white$vid_file$m_co"
    fi
    SQFS_VID_FILE=$vid_file
}

mount_aufs_ram() {
    local aufs_ram_mp=$1  min_aufs_ram=$2  free_mem=$3
    # Default tempfs size for tempfs = 80% of FREE_MEM
    AUFS_RAM_SIZE=$((8 * free_mem / 10))

    [ "$AUFS_RAM_SIZE" -lt "$MIN_AUFS_RAM" ] && AUFS_RAM_SIZE=$MIN_AUFS_RAM
    mkdir -p $aufs_ram_mp

    mount_tmpfs $aufs_ram_mp $AUFS_RAM_SIZE 'aufs ram' || fatal  "$_Create_X_failed_" 'aufs tmpfs'
}

mount_aufs() {
    local  aufs_arg  aufs_mp=$1  sqfs_mp=$2  aufs_ram_mp=$3  rootfs_mp=$4

    mkdir -p $aufs_mp

    if [ -n "$PERSIST_ROOT" -a -n "$STATIC_ROOT" ]; then
        #. Enable <type> persistence
        msg  "$_Enable_X_persistence_" "$(pq static rootfs)"
        aufs_arg="$rootfs_mp:$sqfs_mp=ro"
    else
        STATIC_ROOT=
        aufs_arg="$aufs_ram_mp:$sqfs_mp=ro"
    fi

    # Do the actual aufs mount
    vmsg 6  "$_Mount_X_at_Y_" 'aufs' "$(fq aufs_mp)"

    log_cmd mount -t aufs -o "br:$aufs_arg" $aufs_mp $aufs_mp \
        || fatal  "$_Could_not_mount_X_" aufs

    # Grab what we need from /etc before it gets nuked
    cp -a /etc/mtab $aufs_mp/etc/

    # Always a good idea
    mkdir -p   $aufs_mp/tmp /var/tmp
    chmod 1777 $aufs_mp/tmp /var/tmp
}

link_top_dirs() {
    local mp=$1  dir  mp_dir
    shift
    vmsgN 7 'symlink: '
    for dir; do
        mp_dir=$mp/$dir
        [ -d $mp_dir ] || continue
        rm -rf /$dir
        ln -s $mp_dir /$dir
        vmsgN 7 "$white/$dir "
    done
    vmsg 7
}

try_mount() {
    local dev=$1  mp=$2  rw=${3:-$RW_MODE}
    local LABEL UUID TYPE
    eval local $(blkid $dev | sed -r 's/^[^:]+://')
    [ "$TYPE" ] || return 1
    case $TYPE in
            vfat) mount -t vfat -o $rw,umask=000,shortname=winnt  "$dev" "$mp" 2>/dev/null ;;
         iso9660) mount -t iso9660 -o ro                          "$dev" "$mp" 2>/dev/null ;;
            ntfs) ntfs-3g -o umask=000,force,"$rw"                "$dev" "$mp" 2>/dev/null 17>/dev/null || \
                  mount -t ntfs -o umask=000,ro                   "$dev" "$mp" 2>/dev/null ;;
               *) mount -t $TYPE -o "$rw"                         "$dev" "$mp" 2>/dev/null ;;
    esac
    return $?
}

try_mount_old() {
    local dev=$1  mp=$2  rw=${3:-$RW_MODE}

    mount -t vfat -o $rw,umask=000,shortname=winnt    "$dev" "$mp" 2>/dev/null || \
        mount -t iso9660                     -o ro    "$dev" "$mp" 2>/dev/null || \
            ntfs-3g -o umask=000,force,"$rw"          "$dev" "$mp" 2>/dev/null || \
            mount -t ntfs -o umask=000,ro             "$dev" "$mp" 2>/dev/null || \
                mount -t reiserfs            -o "$rw" "$dev" "$mp" 2>/dev/null || \
                    mount -t ext4            -o "$rw" "$dev" "$mp" 2>/dev/null || \
                        mount -t ext3        -o "$rw" "$dev" "$mp" 2>/dev/null || \
                            mount -t ext2    -o "$rw" "$dev" "$mp" 2>/dev/null
    return $?
}

try_file_mount() {
    local file=$1  mp=$2  rw=${3:-$RW_MODE}

    mount -t reiserfs         -o loop,$rw "$file" "$mp"  2>/dev/null || \
        mount -t ext4         -o loop,$rw "$file" "$mp"  2>/dev/null || \
            mount -t ext3     -o loop,$rw "$file" "$mp"  2>/dev/null || \
                mount -t ext2 -o loop,$rw "$file" "$mp"  2>/dev/null
    return $?
}

#------------------------------------------------------------------------------
# Function: mount_tmpfs <mountpoint> <size> <name> <mode>
#------------------------------------------------------------------------------
mount_tmpfs() {
    local mp=$1  size=$2  name=$3  opts=${4+,}$4
    mkdir -p $mp
    vmsg 6  "$_Create_X_at_Y_" "${hi_co}$name tmpfs${m_co}" "$(fq $mp) (${num_co}$size ${m_co}MB)"
    log_cmd $LIVE_BIN/mount -t tmpfs -o size=${size}m$opts tmpfs $mp || return 1
    return 0
}

#==============================================================================
# Check Filesystems
#==============================================================================

read_superblock() {
    local dev=$1
    local word_size=$((2**16))
    local addr f1 f2 f3 f4 f5 f6 f7 f8

    while read addr f1 f2 f3 f4 f5 f6 f7 f8; do
        case $addr in
            0000030)
                echo SB_MOUNT_CNT=$f3
                echo SB_MAX_MOUNT_CNT=$f4
                echo SB_MAGIC=$f5
                echo SB_STATE=$f6
                ;;
            0000040)
                echo SB_LASTCHECK=$((    f2 * word_size + f1))
                echo SB_CHECKINTERVAL=$((f4 * word_size + f3))
                ;;
        esac
    done <<DD_Hexdump
$(dd if=$dev bs=1 skip=1024 count=128 2>/dev/null| hexdump -d | sed -r "s/ 0+([0-9])/ \1/g")
DD_Hexdump
}

should_fsck() {
    local dev=$1  sdev=${1##*/}  name=$2  max_count=$MAX_MOUNT_CNT
    [ "$DO_FSCK" ] || return 1

    case ,$DID_FSCK, in
        *,$dev,*) return 1
    esac

    DID_FSCK=$DID_FSCK,$dev

    local SB_MOUNT_CNT SB_MAX_MOUNT_CNT SB_MAGIC SB_STATE SB_LASTCHECK SB_CHECKINTERVAL

    eval $(read_superblock $dev)

    if [ "$SB_MAGIC" != 61267 ]; then
        [ "$FORCE_FSCK" ] \
            && msg 'Cannot check %s filesystem on device %s.  Not an ext2/3/4 filesystem.' \
                "$(pq $name)" "$(pq $sdev)"
        return 1
    fi

    local days=$((SB_CHECKINTERVAL /60/60/24))

    vmsg 7  "$_Check_filesystem_on_X_"                        $(dq $dev)
    vmsg 9 "${green}         Device:$white %s"             $dev
    vmsg 9 "${green}    Mount count:$white %s"             $SB_MOUNT_CNT
    vmsg 9 "${green}Max mount count:$white %s"             $SB_MAX_MOUNT_CNT
    vmsg 9 "${green}   Last checked:$white %s"             "$(date --date=@$SB_LASTCHECK)"
    vmsg 9 "${green}   Magic number:$white 0x%04X"         $SB_MAGIC
    vmsg 9 "${green} Check interval:$white %d$m_co (days)" $days

    [ "$FORCE_FSCK" ] && return 0

    if [ $SB_STATE != 1 ]; then
        warn  "$_X_device_Y_not_cleanly_unmounted_Check_forced_" "$(pqw $name)" $(pqw $sdev)
        return 0
    fi

    [ "$SB_MAX_MOUNT_CNT" != 65535 ] && max_count=$SB_MAX_MOUNT_CNT

    if [ $SB_MOUNT_CNT -ge $max_count ]; then
        warn  "$_X_device_Y_has_not_been_checked_in_Z_mounts_Check_forced_" \
            "$(pqw $name)" $sdev "$(nq $SB_MOUNT_CNT)"
        return 0
    fi

    [ $SB_CHECKINTERVAL -gt 0 ] || return 1

    [ $((SB_LASTCHECK + SB_CHECKINTERVAL)) -ge $(date +%s) ] || return 1

    warn  "$_X_device_Y_has_not_been_checked_in_Z_days_Check_forced_" \
        $(pqw $name) $(pqw $sdev) "$(nqw $days)"
    return 1
    return 0
}

do_fsck() {
    local err ret  name=$1 dev=$2  sdev=$(pq ${2##*/}) opts=${3:--n} filefs=$4

    # Use a fifo so we can get the return status and tee the output
    local fifo=/tmp/pipe
    rm -f $fifo
    mkdir -p $(dirname $fifo)
    mkfifo $fifo
    tee -a $MY_LOG < $fifo &

    msg  "$_check_X_filesystem_" "$(pq $name)"

    breakpoint F "Before fsck"

    vmsg 2 "fsck$white $dev"
    ld_path /sbin/e2fsck $opts $dev &> $fifo
    ret=$?

    rm -f $fifo

    ld_path /sbin/tune2fs -C 1 $dev

    breakpoint F "After fsck"

    case $ret in
	    0) vmsg 1     "$_There_were_no_errors_on_X_"            "$sdev" ; err=0 ;;
	    1) vmsg 1    'Filesystem %s repaired'                "$sdev" ; err=0 ;;
	  2|3) vmsg 1    'Filesystem %s repaired'                "$sdev" ; err=0 ;;
	    4) non_fatal 'Errors on %s left uncorrected'         "$sdev" ; err=1 ;;
	    8) non_fatal '%s operational error on %s'       fsck "$sdev" ; err=1 ;;
	   12) non_fatal '%s interrupted on %s'             fsck "$sdev" ; err=1 ;;
	   32) non_fatal '%s cancelled on %s'               fsck "$sdev" ; err=1 ;;
	    *) non_fatal 'Filesystem %s could not be fixed'      "$sdev" ; err=1 ;;
	esac

    return $err
}

fsck_boot_dev() {
    local dev=$1  mp=$2

    local type=$(grep "^$dev " /proc/mounts | cut -d" " -f3)

    if [ -n "$FORCE_FSCK" -a -n "${type##ext[234]}" ]; then
        msg 'Cannot check %s device filesystem of type %s' "$(pq boot)" "$(pq $type)"
        return
    fi

    should_fsck $dev boot || return

    log_cmd mount -o remount,ro $dev
    do_fsck  "$_boot_device_" $dev -n
}

#==============================================================================
#   Misc Functions
#==============================================================================

set_path() {
    PATH=$1
    hash -r
}

should_break_at() {
    local bp=$1
    case ,$BREAK_POINTS, in
        *,$bp,*) return 0 ;;
          *,A,*) return 0 ;;
          *,a,*)          ;;
              *) return 1 ;;
    esac
    case $bp in
        [e1-9]) return 0 ;;
             *) return 1 ;;
    esac
}

custom_code() {
    local file=$1  desc=$2

    [ "${#file}" -eq 1 ] && file=$CUSTOM_DIR/$DISTRO_NAME/$file.sh

    [ -r $file ] || return

    if $LIVE_BIN/sh -n $file; then
        [ "$desc" ] && vmsg 7 'Run custom code %s: %s' "$white$desc$m_co" "$(fq $file)"
        . $file
    else
        non_fatal 'Errors found in custom code: %s.  Skipping' "$(pqh $file)"
    fi
}

breakpoint() {
    local bp=$1  desc=$2

    custom_code "$bp" "$desc"

    if [ "$DBV" ]; then
        eval local val=\$$DBV
        msg "$bold_co$DBV=$white$val"
    fi

    if ! should_break_at $bp; then

        if [ "$TEST_ERRORS" ]; then
            msg  "$_normal_message_one_"
            echo  "$_test_error_" >&2
            msg  "$_normal_message_two_"
        fi

        [ "$DB_PLUS" ] || return
        vmsg 9 "$green@bp$cyan[$num_co$bp$m_co]$hi_co $(get_seconds)s$m_co: $desc"
        [ "$TEST_FATAL" ] && [ "$TEST_FATAL" = "$bp" ] && non_fatal "Test at bp=$bp $desc"
        return
    fi

    echo "$green$tbar$nc_co"
    echo "$green==>$cyan limited shell @ breakpoint [$num_co$bp$cyan]$white $desc"
    echo "$green    Use the$white exit$green command or press$white Ctrl-d$green to continue"

    local bp_time_1=$(get_time)
    env PS1="${green}bp $red$bp$cyan>$nc_co " BP=$bp PATH=$LIVE_BIN:$PATH \
        setsid cttyhack sh 17>/dev/null
    local bp_time_2=$(get_time)
    time_0=$((time_0 + bp_time_2 - bp_time_1))
    echo
}

db_msg() { vmsg 5 "${green}db+:$hi_co $@" ;}
err()    { vmsg 1 "$err_co$@"             ;}
msg()    { vmsg 5 "$@"                    ;}
msgN()   { vmsgN 5 "$@"                   ;}
msg_nc() { vmsg 5 "$nc_co$@"              ;}
warn()   { vmsg 3 "$warn_co$@"            ;}

bq()     { echo "$yellow$*$m_co"          ;}
cq()     { echo "$cheat_co$*$m_co"        ;}
cqw()    { echo "$cheat_co$*$warn_co"     ;}
cqe()    { echo "$cheat_co$*$err_co"      ;}
dq()     { echo "$dev_co$*$m_co"          ;}
dqe()    { echo "$dev_co$*$err_co"        ;}
fq()     { echo "$from_co$*$m_co"         ;}
fqe()    { echo "$from_co$*$err_co"       ;}
mpq()    { echo "$mp_co$*$m_co"           ;}
nq()     { echo "$num_co$*$m_co"          ;}
nqw()    { echo "$num_co$*$warn_co"       ;}
pq()     { echo "$hi_co$*$m_co"           ;}
pqe()    { echo "$bold_co$*$err_co"       ;}
pqw()    { echo "$hi_co$*$warn_co"        ;}
pqh()    { echo "$m_co$*$hi_co"           ;}
hq()     { echo "$hi_co$*$m_co"           ;}

get_time() { cut -d" " -f22 /proc/self/stat ;}

get_seconds() {
    local dt=${1:-$(get_time)}
    printf "%02d" $dt | sed -r 's/(..)$/.\1/'
}

bogo_meter() {
    local width=${1:-$SCREEN_WIDTH} delay=60  dot=.
    local cnt=$(( width * 80 / 100 ))
    local sleep=$(( 1000000 * delay / cnt ))
    while true; do
        for s in $(seq 1 $cnt); do
            usleep $sleep
            echo -n "$dot"
        done
        echo
    done
}

time_cmd() {
    local t0=$(get_time)
    (bogo_meter)&
    local pid=$!
    "$@"
    local ret=$?
    local t1=$(get_time)
    local secs=$(get_seconds $((t1 - t0)))
    kill -9 $pid
    echo
    vmsg 6  "$_command_X_took_Y_seconds_" "$(pq $*)" "$(nq $secs)"
    return $ret
}

vmsg_old() {
    local msg nflag  level=$1; shift

    [ "$1" = "-n" ] && nflag=true && shift
    local fmt=$1; shift

    if [ "$nflag" ]; then
        msg="$(printf "$m_co$fmt$nc_co" "$@")"
    else
        msg="$(printf "$m_co$fmt$nc_co" "$@")\n"
    fi

    [ "$level" -le "$VERBOSE" ] && printf "$msg"
    echo -ne "$msg" >> $MY_LOG

    return 0
}

vmsg() {
    local level=$1  fmt=$2
    shift 2

    msg=$(printf "$m_co$fmt$nc_co" "$@")

    [ "$level" -le "$VERBOSE" ] && printf "$msg\n"
    echo -e "$msg" >> $MY_LOG
    return 0
}

vmsgN() {
    local level=$1  fmt=$2
    shift 2

    msg=$(printf "$m_co$fmt$nc_co" "$@")

    [ "$level" -le "$VERBOSE" ] && printf "$msg"
    echo -ne "$msg" >> $MY_LOG
    return 0
}

vmsg_if() {
    local level=$1; shift
    [ "$VERBOSE" -ge "$level" ] || return
    vmsg $level "$@"
}

vmsg_nc() {
    local level=$1; shift
    vmsg $level "$nc_co$@"
}

heading() {
    vmsg 6 $head_co$tbar
    #. Begin [doing some sub process]
    vmsgN 3 "$hi_co%s "  "$_Begin_"
    vmsg 3 "$@"
}

log_cmd() {
    vmsg_nc 7 "$*"
    #"$@" >/dev/null
    "$@"
    return $?
}

_error() {
    local type=$1  cmsg1=$2  cmsg2=$3  msg=$4
    shift 3

    local match="$(echo $NO_ERROR | sed 's/_/ /g')"
    if [ "$NO_ERROR" -a -z ${msg##$match*} ]; then
        msg  "$_Skip_Error_X_" "$(bq $msg)"
        return
    fi

    if [ "$type" ]; then
        warn
        #warn $hbar
        err "$type"
        #warn $hbar
        [ "$1" ] && msg "${hi_co}$1"
        shift
        for arg; do
            msg "${hi_co}$arg"
        done
        ### warn $hbar
        err
    fi

    breakpoint e "On possibly fatal error"

    local prompt="${green}Select ${yellow}$cmsg1 p$green or$yellow r$green then press <enter>"

    local fmt="    $yellow%s$cyan =$white %s$nc_co\n"
    local reply
    while [ true ]; do
        ### echo $m_co$tbar$nc_co

        [ "$cmsg1" ] && printf "$fmt" "$cmsg1" "$cmsg2"
        printf "$fmt" p  "$_power_off_"
        printf "$fmt" r  "$_reboot_"

        ### echo $m_co$tbar$nc_co
        echo -n "$prompt$nc_co "
        read reply
        case $reply in
            p*|P*)
                echo  "$_power_off_" " ..."
                safe_shutdown poweroff;;

            r*|R*)
                echo  "$_reboot_"  " ..."
                safe_shutdown reboot;;

             c*|C*)
                 [ "$cmsg1" ] && return;;
        esac
        printf "\n${m_co} %s: >>$yellow$reply$m_co<<. %s:$nc_co\n" \
             "$_Unknown_command_"  "$_Please_try_again_"
    done
}

move_mountpoints() {
    local dir mp old aufs_live=/live/aufs$FINAL_DIR

    [ -d $aufs_live ] || return

    for dir in $(ls $aufs_live); do
        [ "$dir" = aufs ] && continue
        mp=$aufs_live/$dir
        mountpoint -q $mp || continue
        old=/live/$dir
        mkdir -p $old
        # echo mount --move $mp $old
        mount --move $mp $old
    done
}

mountpoint_list() {
    local dev mp other ret=1

    while read dev mp other; do
        case $mp in /|/dev|/dev/*|/proc|/sys) continue;; esac
        echo -n "$mp "
        ret=0
    done << Read_Mounts
$(tac /proc/mounts)
Read_Mounts
    return $ret
}

final_umount() {
    local mp list i
    move_mountpoints

    for i in $(seq 1 4); do
        list=$(mountpoint_list) || return 0
        for mp in $list; do
            umount $mp
        done
    done
    list=$(mountpoint_list) || return 0
    err  "$_Unable_to_unmount_X_" "$(pq $list)"
    return 1
}

safe_shutdown() {
    local cmd=$1  ask=$2

    set_path $LIVE_BIN

    final_umount && vmsg 1  "$_Unmount_successful_"
    breakpoint u "final umount"
    if [ "$ask" ]; then
        vmsgN 1  "$_Press_Enter_to_X_" "$(hq $cmd)"
        read x
    fi
    vmsg -1  "$_Will_now_X_the_system_" "$(hq $cmd)"
    exec $cmd -f -n
}

_fatal()     { _error  "$_Fatal_Error_"     ""  ""          "$@" ;}
_non_fatal() { _error  "$_Non_Fatal_Error_" "c"  "$_continue_"  "$@" ;}
fatal()      { _fatal     "$(printf "$@")"                   ;}
non_fatal()  { _non_fatal "$(printf "$@")"                   ;}

YES_no() {  _yes_no 0  "$_yes_" "$@" ;}
yes_NO() {  _yes_no 1  "$_no_"  "$@" ;}

_yes_no() {
    local def_ret=$1  def_lab=$(cq $2)
    shift 2
    local ans title=$(printf "$m_co$@")
    local yes=$(cq  "$_yes_")  no=$(cq  "$_no_")  y_lett=$(bq y) n_lett=$(bq n)

    local err_msg=$(printf  "$_You_must_answer_X_or_Y_Please_try_again_" $(pqe y) $(pqe n))
    while true; do
        echo -e "$title?"
        printf "${m_co}$y_lett=$yes. $n_lett=$no. %s $def_lab\n"  "$_The_default_is_"
        echo -n "$green>$nc_co "

        read ans
        case x$ans in
            x[Yy]*) return 0;;
            x[Nn]*) return 1;;
        esac

        [ -z "$ans" ] && return $def_ret
        printf "$err_co%s$nc_co\n" "$err_msg"
    done
}

my_select() {
    local title=$1  var=$2  width=${3:-0}  default=$4
    shift 4

    local data display lab cnt=0 dcnt
    for lab; do
        cnt=$((cnt+1))
        dcnt=$cnt

        [ "$lab" =  "$_quit_" ] && dcnt=0
        data="${data}$dcnt:$lab\n"

        [ "$lab" =  "$_quit_" ] && lab=$bold_co$lab$nc_co
        [ $cnt = "$default" ] && lab=$(printf "%${width}s (%s)" "$lab" "$(cq  "$_default_")")
        display="${display}$(printf "%2d) %${width}s" $dcnt "$lab")\n"
    done

    my_select_2 "$title" $var "$default" "$data" "$display"
}

my_select_2() {
    local title=$1  var=$2  default=$3  data=$4  display=$5
    local def_prompt=$(printf  "$_Press_X_for_the_default_selection_" "$(cq  "$_enter_")")

    local val input err_msg
    while [ -z "$val" ]; do

        echo -e "$hi_co$title$nc_co"
        printf "$display" | sed -r -e "s/(^|\t)( ?[0-9]+)(\))/\t$green\2$white\3$cyan/g" -e "s/$/$nc_co/"
        [ "$err_msg" ] && printf "$err_co%s$nc_co\n" "$err_msg"
        [ "$default" ] && printf "$m_co%s$nc_co\n" "$def_prompt"
        echo -n "$green>$nc_co "

        read input
        err_msg=
        [ -z "$input" -a -n "$default" ] && input=$default

        if ! echo "$input" | grep -q "^[0-9]\+$"; then
            err_msg="You must enter a number"
            [ "$default" ] && err_msg="You must enter a number or press <enter>"
            continue
        fi

        val=$(echo -e "$data" | sed -n "s/^$input://p")

        if [ -z "$val" ]; then
            err_msg=$(printf  "$_The_number_X_is_out_of_range_" "$(pqe $input)")
            continue
        fi

        eval $var=\$val
        break
    done
}

size_label_m() { _size_label $1   "M G T" ;}
size_label_k() { _size_label $1 "K M G T" ;}

_size_label() {
    local unit frac  size=$1  units=$2
    for unit in $units; do
        if [ $size -lt 1000 ]; then
            if [ "$frac" -a $size -lt 10 ]; then
                echo "$size.$frac$unit"
            else
                echo "$size$unit"
            fi
            return
        fi
        frac=$(( 10 * ($size % 1024) / 1024 ))
        size=$(( size / 1024))
    done
    echo "$size$unit"
}

select_device() {
    local title_type=$1  var=$2  min_size=${3:-0} not_mounted=$4
    local title=$(printf  "$_Please_select_X_device_" "$title_type")

    local cnt=1 cnt_lab rtype mp=/tmp/mnt  skip
    local size size_lab free free_lab
    local name bytes type label line model last_model data display title
    local size_rev free_rev type_rev name_rev
    #local format="$green%3s $lt_cyan%-5s $magenta%8s $yellow%8s $lt_blue%10s $lt_gray%9s $white%-16s $lt_gray%s\n"
    local format="$green%3s $lt_cyan%s%-5s$nc_co $magenta%s%8s$nc_co $yellow%s%8s$nc_co $lt_blue%s%10s$nc_co $lt_gray%10s  $white%-16s $lt_gray%s\n"
    local head_fmt="${hi_co}%3s %-5s %8s %8s %10s %10s  %-16s %s\n"
    local header=$(printf "$head_fmt" "" name size free fstype removable label model)
    title="$title\n$header"

    mkdir -p $mp
    local rev=$(printf "\e[0;4;7;31m")
    #local rev=$(printf "\e[5;7;31m")
    #local rev=$(printf "\e[1;7m")
    (bogo_meter)&
    local pid=$!

    while read -r line ; do

        bytes=0

        name=$(  echo "$line" | cut -d" " -f1)
        bytes=$( echo "$line" | cut -d" " -f2)
        type=$(  echo "$line" | cut -d" " -f3)
        remove=$(echo "$line" | cut -d" " -f4)
        reado=$( echo "$line" | cut -d" " -f5)
        label=$( echo "$line" | cut -d" " -f6 | sed 's/\\x20/ /g')
        model=$( echo "$line" | cut -d" " -f7 | sed 's/\\x20/ /g')

        local dev=/dev/$name

        [ "$model" ] && last_model=$model

        [ "$reado" = 1 ] && continue

        case $type in
            ""|udf|iso9660|swap|squashfs) continue
        esac

        case $(stat -c %t $dev) in
            [38]) ;;
               *) continue ;;
        esac

        skip=
        size_rev=

        size=$((bytes / 1024 / 1024))
        cnt_lab="$cnt)"
        size_lab=$(size_label_m $size)

        if [ $min_size -gt 0 -a $min_size -gt $size ]; then
            size_rev=$rev
            cnt_lab=
            skip=true
        fi

        name_rev=
        free_rev=
        type_rev=
        free=

        local exist_mp=$(get_mountpoint $dev)
        if [ "$exist_mp" ]; then
            free=$(free_space $exist_mp)
            if [ "$not_mounted" ]; then
                name_rev=$rev
                cnt_lab=
                skip=true
            fi

        # The busybox mount error messages are worthless at best
        elif mount -t $type $dev $mp &>/dev/null; then
            free=$(free_space $mp)
            free_lab=$(size_label_m $free)
            umount $mp
        fi

        if [ "$free" ]; then
            free_lab=$(size_label_m $free)
            if [ $min_size -gt 0 -a $min_size -gt $free ]; then
                free_rev=$rev
                cnt_lab=
                skip=true
            fi
        else
            free_lab="?"
            type_rev=$rev
            cnt_lab=
            skip=true
        fi

        rtype="no"
        [ "$remove" = 1 ] && rtype=${yellow}"yes"

        local this_model=$last_model
        #[ "${#label}" -gt 16 ] && this_model=

        line=$(printf "$format" "$cnt_lab" "$name_rev" "$name" "$size_rev" "$size_lab" \
            "$free_rev" "$free_lab" "$type_rev" "$type" "$rtype" "$label" "$this_model")

        display="${display}$line\n"

        [ -z "$cnt_lab" ] && continue

        data="${data}$cnt:$dev\n"
        cnt=$((cnt + 1))

    done <<Data
$(ld_path /bin/lsblk -rnbo name,size,fstype,rm,ro,label,model)
Data

    kill -9 $pid
    echo

    if [ $cnt -le 1 ]; then
        display="$header\n$display"
        msg_nc "$display"
        warn  "$_No_suitable_devices_were_found_"
        return 1
    fi

    line=$(printf "$format" "0)" "${bold_co}quit" "$m_co($(cq default))")

    display="${display}$line\n"
    data="${data}0:quit\n"
    if [ "$skip" ]; then
        local rev_video=$(printf "$rev%s$nc_co"  "$_reverse_video_")
        display="${display}$(printf  "$_Partitions_that_cant_be_used_are_listed_but_not_selectable_")\n"
        display="${display}$(printf  "$_The_limiting_factors_are_highlighted_in_X_" "$rev_video")\n"
        display="${display}$(printf "A hightlighted name means the device is already mounted")\n"
    fi

    my_select_2 "$title" $var "0" "$data" "$display"
    echo -e "$display" >> $MY_LOG
    return 0
}

label_device() {
    local dev=$1  new=$2
    local type=$(device_fstype $dev)

    local pre post
    case $type in
         ext[234]) pre="tune2fs -L"                  ; post="$dev"   ;;
              xfs) pre="xfs_admin -L"                ; post="$dev"   ;;
              jfs) pre="jfs_tune -L"                 ; post="$dev"   ;;
             ntfs) pre="ntfslabel $dev"              ; post=""       ;;
         vfat|dos) pre="dosfslabel $dev"             ; post=""       ;;
            btrfs) pre="btrfs filesystem label $dev" ; post=""       ;;
         reiserfs) pre="reiserfstune --label"        ; post="$dev"   ;;

               "") warn  "$_Could_not_determine_filesystem_on_X_" $(pqw $dev)
                   return 1                                          ;;

                *) warn  "$_Cannot_label_a_X_filesystem_" $(pqw $type)
                   return 2                                          ;;
    esac

    local cmd=$(echo $pre | cut -d" " -f1)
    local full=$(ld_path_which $cmd)

    if [ -z "$full" ]; then
        warn  "$_Dont_have_the_X_tool_to_label_a_Y_filesystem_" $(pqw $cmd) $(pqw $type)
        return 3
    fi

    local old=$(device_label $dev)

    if [ "$old" ]; then
        vmsg 1  "$_The_device_X_already_has_label_Y_" $(pq $dev) "$(pq $old)"
        yes_NO  "$_Update_label_on_X_from_Y_to_Z_" $(pq $dev) "$(pq "$old")" "$(pq "$new")" || return
    else
        vmsg 1  "$_The_device_X_has_no_label_" $(pq $dev)
        yes_NO  "$_Give_X_the_label_Y_" $(pq $dev) "$(pq "$new")" || return
    fi

    log_cmd ld_path $pre "$new" $post || return

    msg  "$_Label_updated_to_X_" $(pq $new)
}


sync_fs() {
    sync
}

label_to_dev() { any_to_dev 'LABEL' "$@"  ;}
uuid_to_dev()  { any_to_dev 'UUID'  "$@"  ;}

# This is like findfs but this returns a list if there is more than one device
any_to_dev() {

    local field="$1=\"$2\"" devices
    local dev f1 f2 other
    while read dev f1 f2 other; do
        [ "$f1" = "$field" -o "$f2" = "$field" ] && devices="$devices$dev "
    done <<EOT
$(blkid)
EOT
    echo "$devices" | sed 's/://g' | sed 's/ $//'
}

cleanse_dev() {
    case $1 in
        /dev/*)  [ -b "$1"      ] && echo "$1";;
         dev/*)  [ -b "/$1"     ] && echo "/$1";;
            /*)  [ -b "/dev$1"  ] && echo "/dev$1";;
             *)  [ -b "/dev/$1" ] && echo "/dev/$1";;
    esac
}

general_device_error() {
    case $2 in
        uuid|label) label_error  "$@" ;;
              name) device_error "$@" ;;
    esac
}

label_error() {
    local find_type=$1  param=$1$2 name=$2  value=$3  field=$(echo $2 | tr 'a-z' 'A-Z')
    ##warn "$hbar"
    warn
    err  "$_Fatal_Error_"
    err  "$_No_device_with_X_was_found_" "$name $(cqe $value)"

    vmsg_nc 3  "$_Valid_X_" "${name}s:"
    local line val LABEL TYPE UUID
    while read line; do
        eval $field=
        eval local "$line"
        eval val=\$$field
        [ "$val" ] && echo "$white  $param=$val$nc_co"
    done <<EOT
$(blkid | sed 's/^[^:]*://')
EOT
    #warn "$hbar"
    warn
    case $from_type in
        persist) _error "" c  "$_continue_" ;;
              *) _error                 ;;
    esac
}

device_error() {
    local find_type=$1  type=$2  value=$3
    local valid_devs=$(echo $(most_block_devices) | sed "s/^/$white    /");
    if [ "$find_type" = 'persist' ]; then
        _non_fatal "$(printf  "$_Could_not_find_device_X_" "$(pqh $value)")"  "$_Valid_devices_": "$valid_devs"
    else
        _fatal "$(printf  "$_Could_not_find_device_X_" "$(pqh $value)")"  "$_Valid_devices_": "$valid_devs"
    fi
}

cp_file() {
    local src=$1  name=$2  dest=$3  file=$1/$2
    mkdir -p $dest
    echo -n $hi_co
    local tar=$LIVE_BIN/tar
    local progress=$LIVE_BIN/pipe_progress

    if [ ! -d "$src" ]; then
        err 'Source directory %s does not exist' $(pqe $src)
        return 2
    fi

    if [ ! -f "$file" ]; then
        err 'Source file %s does not exist' $(pqe $file)
        return 3
    fi

    $tar -C "$src" -hcf - "$name" | $progress | $tar -C "$dest" -xpf -
    local ret=$?
    echo -n $nc_co
    vmsg 8 "cp_file returned:$num_co $ret"
    return $ret
}

#------------------------------------------------------------------------------
# Function: cp_rm_dest <src> <dest>
#
# Emulate cp --remove-destination in busybox
#------------------------------------------------------------------------------
cp_rm_dest() {
    local src=$1 dest=$2
    rm -f "$dest"
    cp "$src" "$dest"

}

#------------------------------------------------------------------------------
# Function: cp_dir <sorc> <dest>
#------------------------------------------------------------------------------
cp_dir() {
    mkdir -p "$2"
    echo -n $hi_co
    local tar=$LIVE_BIN/tar
    local progress=$LIVE_BIN/pipe_progress
    $tar -C "$1" -cf - . | $progress | $tar -C "$2" -xpf -
    local ret=$?
    echo -n $nc_co
    return $ret
}

# FIXME: should use ld_path and --apparent-size se we aren't fooled by sparseness
file_usage_k() {
    local f file dir=$1
    shift

    [ "$dir" -a "${dir##*/}" ] && dir=$dir/
    for f; do
        file=$dir$f
        [ -e $file ] && list="$list $file"
    done
    du -scxk $list >> $MY_LOG
    du -scxk $list | tail -n 1 | cut -f1
}

file_usage_m() {
    local f file dir=$1
    shift

    [ "$dir" -a "${dir##*/}" ] && dir=$dir/
    for f; do
        file=$dir$f
        [ -e $file ] && list="$list $file"
    done
    du -scxm $list >> $MY_LOG
    du -scxm $list | tail -n 1 | cut -f1
}


xxxfile_usage_m() {
    local k=$(file_usage_k "$@")
    echo $(( k / 1024))
}

copy_files() {
    local f file sorc=$1  dest=$2
    shift 2
    [ "$sorc" -a "${sorc##*/}" ] && sorc=$sorc/
    for f; do
        file=$sorc$f
        [ -e $file ] || continue
        cp -a $sorc$f $dest/ || return $?
    done
    return 0
}

all_space()      { $LIVE_BIN/df -m "$1" | awk '{size=$2}END{print size}' ;}
used_space()     { $LIVE_BIN/df -m "$1" | awk '{size=$3}END{print size}' ;}
free_space()     { $LIVE_BIN/df -m "$1" | awk '{size=$4}END{print size}' ;}
get_mountpoint() { grep "^$1 " /proc/mounts | cut -d" " -f2              ;}

dir_has_param() {
    local dir=$1  want_param=$2

    [ -d "$dir" ] || return 1

    local dev=$(df $dir | tail -n1 | cut -d" " -f1)
    local params=$(grep "^$dev " /proc/mounts | cut -d" " -f4)
    case ,$params, in
        *,$want_param,*) return 0;;
    esac
    return 1
}

#------------------------------------------------------------------------------
# Function: get_vid <file>
#------------------------------------------------------------------------------
get_vid()   { [ -r "$1" ] && grep ^=== $1 | tail -n 1   ;}

vid_error() {
    VID_ERROR=$(printf "$@")
    err "$@"
    WANT_ROOTFS=
    NEED_ROOTfS=
    STATIC_ROOT=
    log_cmd umount $ROOTFS_MP
}

#------------------------------------------------------------------------------
# Function: check_vid <sqfs_vid> <mp> <fname>
#------------------------------------------------------------------------------

check_vid() {
    local sqfs_vid=$1  mp=$2  fname=$3  vid_file="$2$VID_DIR/$3.ver"
    breakpoint v "before check VID"

    local rootfs_vid=$(get_vid $vid_file)

    if [ -n "$sqfs_vid" -a -n "$rootfs_vid" ]; then

        if [ "$rootfs_vid" = "$sqfs_vid" ]; then
            vmsg 6 'Matching %s found in %s' "$VID_NAME" "$(fq $vid_file)"
            return 0
        else
            vid_error  "$_X_mismatch_between_Y_and_Z_" "$VID_NAME" linuxfs rootfs
            vmsg 1 "linuxfs:$white $sqfs_vid"
            vmsg 1 " rootfs:$white $rootfs_vid"
            return 1
        fi

    elif [ "$sqfs_vid" ]; then

        # Pass test and create new rootfs version file if rootfs is empty
        if ! ls $mp | egrep -q 'etc|bin|lib|var|usr'; then
            msg  "$_Copy_X_to_empty_Y_" "$LINUXFS_NAME $VID_NAME" rootfs

            log_cmd mkdir -p "$mp$VID_DIR"      || return 1
            log_cmd cp $SQFS_VID_FILE $vid_file || return 2
            sync_fs
            return 0

        elif ! [ -f "$vid_file" ]; then
            #. <type> file not found <name>
            vid_error  "$_X_file_not_found_Y_" "$VID_NAME" "$vid_file"
            return 1

        else
            vid_error  "$_No_X_found_in_file_Y_" "$VID_NAME" "$vid_file"
            return 1
        fi

    elif [ "$rootfs_vid" ]; then
        vid_error  "$_No_X_found_but_there_is_a_Y_" "$LINUXFS_NAME $VID_NAME" "rootfs $VID_NAME"
        return 1
    else
        vmsg 6  "$_No_X_or_Y_found_" "$LINUXFS_NAME $VID_NAME" "rootfs $VID_NAME"
        return 0
    fi
}

#------------------------------------------------------------------------------
# Function: check_md5 <dir>
#
# Checks the md5 sum of all files in <dir> that have a <name>.md5 file.
#------------------------------------------------------------------------------
check_md5()
{
    local dir=$1 passed="passed"  failed="failed"
    [ ! -d "$dir" ] && dir=$(echo $1 | sed 's|\(.*\)/.*|\1|')
    dir=$(echo $dir | sed 's|/$||')
    if ! [ -d "$dir" ]; then
        msg $tbar
        err  "$_Directory_X_does_not_exist_" "$(fqe $1)"
        err  "$_Cant_perform_X_check_" md5
        msg $tbar
        return 0
    fi

    local fcnt=0

    vmsg 2 $tbar
    vmsg_nc 1  "$_Check_md5_of_files_in_X_directory_Please_be_patient_" "$(fq $dir)"

    local file fname md5_file file_md5 true_md5 error
    for fname in $(ls $dir); do
        file=$dir/$fname
        local size=$(du -sm $file | cut -f1)
        md5_file="$file.md5"
        [ -f "$md5_file" -a -f "$file" ] || continue

        local size_str="$nc_co($num_co$size$nc_co M)"
        # vmsg 1
        vmsg 1 $tbar
        vmsg_nc 1  "$_file_X_" "${nc_co}$fname $size_str"
        fcnt=$(( fcnt + 1 ))
        true_md5="$(head -n 1 $md5_file | cut -d" " -f1)"

        (bogo_meter)&
        local pid=$!
        file_md5=$(md5sum $file | cut -d" " -f1)
        kill -9 $pid

        if [ "$file_md5" == "$true_md5" ]; then
            vmsg_nc 1  "$_result_X_" "${ok_co}$passed"
        else
            vmsg_nc 1  "$_result_X_" "${err_co}$failed"
            error=true
        fi

        vmsg 6  "$_wanted_X_" "$num_co$true_md5"
        vmsg 6  "$_got_X_" "$num_co$file_md5"
    done

    vmsg 1
    if [ "$fcnt" = 0 ]; then
        err  "$_No_md5_checksums_were_found_in_X_Cant_do_any_md5_checks_" "$(fqe $dir)"
    elif [ "$error" ]; then
        non_fatal  "$_At_least_one_md5_checksum_did_not_match_"
    elif [ "$fcnt" = 1 ]; then
        vmsg 1  "$_Success_The_file_passed_"
    else
        #. Success! All <count> file(s) passed
        vmsg 1  "$_Success_All_X_files_passed_" $(nq $fcnt)
    fi
    vmsg 2 $tbar
}

do_delay() {
    local cnt=$1; shift
    [ -z "$cnt" -o "$cnt" = "0" ] && return
    [ $# -gt 0 ] && msg "$@"
    while [ "$cnt" -gt "0" ]; do
        msgN "$hi_co$cnt "
        cnt=$((cnt - 1))
        sleep 1
    done
    msg
}

set_colors() {
    local noco=$1  loco=$2

    [ "$noco" ] && return

    local e=$(printf "\e")
     black="$e[0;30m";    blue="$e[0;34m";    green="$e[0;32m";    cyan="$e[0;36m";
       red="$e[0;31m";  purple="$e[0;35m";    brown="$e[0;33m"; lt_gray="$e[0;37m";
   dk_gray="$e[1;30m"; lt_blue="$e[1;34m"; lt_green="$e[1;32m"; lt_cyan="$e[1;36m";
    lt_red="$e[1;31m"; magenta="$e[1;35m";   yellow="$e[1;33m";   white="$e[1;37m";
     nc_co="$e[0m";

    cheat_co=$white;      err_co=$red;       hi_co=$white;
      cmd_co=$white;     from_co=$lt_green;  mp_co=$magenta;   num_co=$magenta;
      dev_co=$magenta;   head_co=$yellow;     m_co=$lt_cyan;    ok_co=$lt_green;
       to_co=$lt_green;  warn_co=$yellow;  bold_co=$yellow;

    [ "$loco" ] || return

    from_co=$brown
      hi_co=$white
       m_co=$nc_co
     num_co=$white
}

next_device() {
    local base=$(echo $1 | sed 's/[0-9]*$//')
    local num=$(echo $1 | sed 's/^[^0-9]*//')
    while [ "$num" -lt "20" ]; do
        num=$((num + 1))
        device=$base$num
        [ -b "$device" ] || continue
        echo $device
        return 0
    done
    return 1
}

will_remaster() {
    [ -n "$NO_REMASTER" -o "$ROLLBACK" ] && return 1

    local sqfile_path=${1:-$SQFILE_MP/$SQFILE_PATH}
    local sqfile=$sqfile_path/$SQFILE_NAME
    local old_file=$sqfile.old new_file=$sqfile.new bad_file=$sqfile.bad
    [ -e $old_file -o -e $bad_file ] && return 1
    [ -e $new_file ]
    return $?
}

#------------------------------------------------------------------------------
# Function: do_remaster <dir>
#
# If <dir>/linuxfs.new exists we move:
#       linuxfs     -->  linuxfs.old
#       linuxfs.new -->  linuxfs
#
# If the "rollback" cheatcode is used, we instead move:
#       linuxfs     -->  linuxfs.bad
#       linuxfs.old -->  linuxfs
#------------------------------------------------------------------------------
do_remaster() {
    [ "$NO_REMASTER" ] && return 0
    local sqfile_path=$(dirname $1)
    local sqfile="$sqfile_path/$SQFILE_NAME"
    local old_file="$sqfile.old" new_file="$sqfile.new" bad_file="$sqfile.bad"


    if [ "$ROLLBACK" ]; then
        heading 'remaster %s' "$(cq rollback)"

        if ! [ -f "$old_file" ]; then
            err -n  "$_The_boot_parameter_X_was_given_but_no_Y_was_found_" \
                "$(cqe rollback)" "$(fqe $SQFILE_NAME.old)"

            do_delay 10  "$_Wait_X_seconds_" "${num_co}10$m_co"
            return 1
        fi
        msg $tbar
        warn  "$_Roll_back_file_X_to_Y_" "$SQFILE_NAME.old" "$SQFILE_NAME"
        msg $tbar

        local file full
        rm -rf $sqfile_path/xtra.bak $sqfile_path/xtra.bad
        for file in $SQFILE_NAME $MAKE_OLD; do
            full=$sqfile_path/$file
            [ -e $full.bad ] && log_cmd mv -f $full.bad $full.bak
            [ -e $full     ] && log_cmd mv -f $full     $full.bad
            [ -e $full.old ] && log_cmd mv -f $full.old $full
        done

        DID_ROLLBACK=true
        sync_fs
        sleep 2
        return 0
    fi

    [ -e $new_file ] || return 0

    heading 'remaster'

    if [ -e $old_file -o -e $bad_file ]; then
        warn  "$m_co$tbar"
        warn   "$_Will_not_remaster_"
        warn   "$_In_directory_X_one_or_more_of_these_files_already_exist_"  "$sqfile_path"
        warn  "    $SQFILE_NAME.old"
        warn  "    $SQFILE_NAME.bad"
        warn
        warn   "$_Run_the_X_program_to_fix_this_problem_" 'remaster-live'
        warn  "$m_co$tbar"

        non_fatal
        return 2
    fi
    msg $tbar
    warn  "$_Remaster_the_file_X_to_Y_" "$SQFILE_NAME.new" "$SQFILE_NAME"
    msg $tbar

    local file full
    rm -rf $sqfile_path/xtra.old
    for file in $SQFILE_NAME $MAKE_OLD; do
        full=$sqfile_path/$file
        [ -e $full     ] && log_cmd mv -f $full $full.old
        [ -e $full.new ] && log_cmd mv -f $full.new $full
    done

    DID_REMASTER=true
    sync_fs
    sleep 2
    return 0
}

most_block_devices() { find /dev -type b | egrep -v "/ram|/loop|/fd" ; }

#------------------------------------------------------------------------------
# Function: copy_to_ram
#
# Copies the linuxfs file (and a few other files) to RAM
#------------------------------------------------------------------------------
copy_to_ram() {
    local name=$(basename $1) toram_mp=$2

    if _copy_to_ram "$@"; then

        LINUXFS_DEV=
        SQFILE_MP=$toram_mp
        SQFILE_DIR=$toram_mp
        SQFILE_FULL=$toram_mp/$name
        DEFAULT_DIR=$SQFILE_DIR
    else
        breakpoint c "After toram copy file error"
        mountpoint -q $toram_mp && log_cmd umount $toram_mp
        [ -d $toram_mp ]        && rmdir $toram_mp
    fi
}

_copy_to_ram() {
    local from=$1 toram_mp=$2  err_toram=$(cqe toram)
    local name=$(basename $from) dir=$(dirname $from)

    heading  "$_copy_X_file_to_RAM_" "$(fq $name)"
    local real_sq_file=$(readlink -f $from)
    local sq_size_k=$(du -sk $real_sq_file | cut -f 1)

    local format="%15s: %10d k"
    local format2="%s: %10d k"
    vmsg 8 "$format" "$name" $sq_size_k

    local extra ex_full size_k
    for extra in $EXTRA_FILES; do
        ex_full="$(readlink -f $dir/$extra)"
        [ -e "$ex_full" ] || continue
        size_k="$(du -sk $ex_full | cut -f 1)"
        sq_size_k=$((sq_size_k + size_k))
        vmsg 8 "$format" $extra $size_k
        #. Total [RAM needed]
        vmsg 8 "$format2"  "$_Total_"  $sq_size_k
    done

    # Extra space here does not waste ram
    local sq_size_m=$((sq_size_k/1000 + 10))

    local ram_needed=$((sq_size_m + MIN_SYS_RAM));

    if [ "$sq_size_m" -gt "$FREE_MEM" ]; then
        err  "$_Not_enough_RAM_available_to_do_X_" $err_toram
        #. Have <size> RAM, needed <size>
        err  "$_have_X_RAM_needed_Y_" "${num_co}$FREE_MEM ${m_co}M${err_co}" \
            "${num_co}$sq_size_m ${m_co}M"
        return 1
    fi
    if ! mount_tmpfs $toram_mp $sq_size_m toram; then
        err  "$_Unable_to_mount_X_Will_not_do_Y_" tmpfs $err_toram
        return 2
    fi

    local orig=$dir
    local dest=$toram_mp

    mkdir -p $dest

    vmsg 3  "$_Will_copy_files_from_X_to_Y_Please_be_patient_" \
        "$(dq $orig)" "$(dq $dest)"

    for extra in $EXTRA_FILES; do
        local full=$(readlink -f $dir/$extra)
        [ -e "$full" ] && cp -a $full $dest
    done
    [ -e $from ] && cp $from.md5 $dest

    if ! cp_file $orig $name $dest 2>&1; then
        err  "$_Copy_failed_Cannot_do_X_" $err_toram
        return 3
    fi

    if ! [ -f "$dest/$name" ]; then
        err  "$_The_destination_file_is_missing_The_X_has_failed_" $err_toram
        return 4
    fi

    # Rely on file size in bytes to check for errors
    local orig_bytes=$(stat -c "%s" $real_sq_file)
    local dest_bytes=$(stat -c "%s" $dest/$name)

    if [ "$orig_bytes" -ne "$dest_bytes" ]; then
        err  "$_Copy_was_incomplete_The_X_failed_" $err_toram
        format="%s: ${num_co}%12d${m_co} bytes"
        vmsg 1  "$_Size_of_original_X_" $orig_bytes
        vmsg 1  "$_Size_of_copy_X_" $dest_bytes
        vmsg_nc 1 "$(df -m | grep $toram_mp)"
        return 5
    fi

    msg "${ok_co}%s"  "$_Copy_to_RAM_succeeded_"

    # Reduce FREE_MEM by size of tmpfs
    FREE_MEM=$((FREE_MEM - sq_size_m))

    vmsg 6 '    persistence device: %s' $(pq $PERSIST_DEVICE)
    vmsg 6 '           boot device: %s' $(pq $SQFILE_DEV)

    if ! [ "$PERSIST_DEVICE" -a "$PERSIST_DEVICE" = "$SQFILE_DEV" ]; then
        # remount ro to be on the safe side
        log_cmd mount -o remount,ro $toram_mp
    fi

    DID_TORAM=true
    return 0
}

#------------------------------------------------------------------------------
# Function: prepare_persistence
#
# Parse the persist= bootcode but first set defaults.
#------------------------------------------------------------------------------
prepare_persistence() {
    local param persist_files auto_persist=true

    # pdev= and pdir= etc enable persistence even with no persist= bootcode.
    if [ "$PERSIST_ID" ]; then
        : ${PERSIST:=root,home}
        auto_persist=
    fi

    PERSIST_PATH=${PERSIST_PATH:-$DEFAULT_PERSIST_PATH}

    [ "$PERSIST" ] && ! echo $PERSIST | egrep -q 'home|root|r(,|!|$)|h(,|!|$)' && PERSIST="$PERSIST,home,root"

    will_remaster && WILL_REMASTER=true

    local rootfs_file=rootfs
    [ "$WILL_REMASTER" ] && rootfs_file=rootfs.new

    #----- Parse persist= ---------------------------------------------------------

    for param in $(echo "$PERSIST" | sed 's/,/ /g'); do

        case $param in
            s|static)
                STATIC_ROOT=true;;
        esac

        # You can't always get what you want ...
        case $param in
            home|home!|h|h!)
                persist_files="$PERSIST_PATH/homefs $persist_files"
                persist_files="$PERSIST_PATH/homefs.new $persist_files"
                WANT_HOMEFS=true ;;

            root|root!|r|r!)
                persist_files="$PERSIST_PATH/$rootfs_file $persist_files"
                WANT_ROOTFS=true ;;

            auto|a)
                AUTO_MAKE_FS=true;;

            usb|hd)
                FROM_PERSIST="$FROM_PERSIST,$param" ;;

            static|s) ;;

            *)
                # FIXME: should this be an non-fatal error?
                warn
                warn  "$_Invalid_X_values_s_Y_"  $(cqw persist) "$(cqw $param)"
                warn  "$_Valid_values_X_" "$(cqw auto home home! root root! static usb hd a h h! r r! s)" ;;
        esac

        # And if you try sometime ...
        case $param in
            home!|h!)
                NEED_HOMEFS=true;  NEED_PERSIST=true ;;
            root!|r!)
                NEED_ROOTFS=true;  NEED_PERSIST=true ;;
        esac
    done
    PERSIST_FILES=$persist_files

    [ "$auto_persist" ] || return

    # if we are on a read-only device and persist was set then set default
    # persist label if persist device was not explicitly given
    [ -z "$REMASTERABLE" -a -n "$NEED_PERSIST" ] || return

    AUTO_PERSIST="$DISTRO_NAME-Persist"
    warn
    warn  "$_Persistence_was_requested_on_a_read_only_boot_device_"
    warn  "$_Will_search_for_a_persistence_device_with_the_label_X_" "$(pqw $AUTO_PERSIST)"
    warn
    PERSIST_ID=label="$AUTO_PERSIST"

}

mount_persist_device() {
    local persist_files=$1  from_persist=$2

    [ "$persist_files" ] || return

    # Set persist device to boot device if appropriate
    if [ -z "$from_persist" -a -z "$PERSIST_ID" ]; then

        case $(stat -c %t $SQFILE_DEV) in
         [38]) ;;
            *) warn  "$_Cant_use_X_as_a_persistence_device_" $(pqw $SQFILE_DEV)
               return 20 ;;
        esac
        : ${PERSIST_ID:=name=$SQFILE_DEV}
    fi

    heading  "$_Mount_persistence_device_if_needed_"

    ALWAYS_SCAN=$FROM_PERSIST
    FROM_TYPE=${FROM_PERSIST:-usb,hd}

    find_files persist "$persist_files" "$PERSIST_MP" "$PERSIST_ID" "$PERSIST_RETRY"
    local ret=$?

    # FIXME: select persist device here and then run find_files again.
    if [ "$AUTO_PERSIST" -a $ret -ne 0 -a $ret -ne 40 ]; then
        if select_persist_device "$AUTO_PERSIST"; then
            msg "persist-files: $persist_files  id=$PERSIST_ID"
            find_files persist "$persist_files" "$PERSIST_MP" "$PERSIST_ID" "$PERSIST_RETRY"
            ret=$?
        else
            warn  "$_Disable_persistence_"
            unset WANT_ROOTFS WANT_HOMEFS NEED_ROOTFS NEED_HOMEFS
            return
        fi

    fi

    if [ $ret -eq 0 ]; then

        PERSIST_MP=$FOUND_MP
        PERSIST_DEVICE=$FOUND_DEV
        PERSIST_UUID=$(device_uuid $PERSIST_DEVICE)
        vmsg 7 "       persist_mp: $PERSIST_MP"
        vmsg 7 "   persist_device: $PERSIST_DEVICE"
        vmsg 7 "     persist_uuid: $PERSIST_UUID"
        PERSIST_FULL_PATH="$PERSIST_MP/$PERSIST_PATH"

    else

        case $ret in
           40) warn  "$_Could_not_find_any_persistence_files_"  ;;
            *) warn  "$_Could_not_find_persistence_device_"     ;;
        esac

        #. Could not enable required <type> persistence
        [ "$NEED_ROOTFS" ] && non_fatal  "$_Could_not_enable_required_X_persistence_" $(pqh 'root!')
        [ "$NEED_HOMEFS" ] && non_fatal  "$_Could_not_enable_required_X_persistence_" $(pqh 'home!')

        unset WANT_ROOTFS WANT_HOMEFS NEED_ROOTFS NEED_HOMEFS
    fi

}

create_persist_files() {
    local mp=$1 path=$2 dir=$1/$2 need_rootfs=$3  need_homefs=$4
    mount -o remount,rw $mp 2>/dev/null
    dir_has_param "$mp" rw || return

    local make_homefs
    if [ "$need_rootfs" ]; then

        # Do we also make the homefs file?
        make_homefs=$WANT_HOMEFS
        [ -e $dir/rootfs -o -e $dir/rootfs.new ] && make_homefs=

        # Default size for rootfs.new is size of rootfs
        local default_size rootfs_file=rootfs
        if [ "$WILL_REMASTER" ]; then
            default_size=$(stat -c %s $dir/rootfs 2>/dev/null)
            msg 'default size: %s Meg' "$(nq $default_size)"
            [ "$default_size" ] && default_size=$((default_size/1024/1024))
            rootfs_file=rootfs.new
        fi

        persist_makefs root $mp $dir $rootfs_file NEED_ROOTFS $default_size

    fi
    #msg "need_homefs:$need_homefs  make_homefs:$make_homefs"
    [ -n "$need_homefs" -o -n "$make_homefs" ] \
        && persist_makefs home $mp $dir homefs NEED_HOMEFS
}

persist_makefs() {
    local type=$1
    _persist_makefs "$@" && return

    warn  "$_Disable_X_persistence_" $(pqw $type)
}

_persist_makefs() {
    local type=$1  mp=$2  dir=$3  file=$4  needed_var=$5  orig_def_size=$6  full_file=$3/$4

    local wanted_var=WANT${needed_var#NEED}

    [ -e $full_file ] && return 0

    warn
    warn  "$_X_persistence_was_requested_but_no_Y_file_was_found_" $(pqw $type) $(pqw $file)
    warn

    eval local needed=\$$needed_var wanted=\$$wanted_var
    unset $needed_var $wanted_var

    local free=$(free_space $mp)
    local avail=$((free - MKFS_FREE_MARGIN))

    vmsg 6 '      Free space: %s Meg' $(nq $free)
    vmsg 6 ' Available space: %s Meg' $(nq $avail)

    [ "$orig_def_size" ] && [ $orig_def_size -gt $avail ] \
        && warn  "$_Not_enough_room_on_Live_device_to_create_X_file_of_size_Y_Meg_" \
            $(fq $file) $(nq $orig_def_size)

    local base_file=${file%%.*}
    local default_size=$(default_makefs_size $avail ${base_file}_SIZE_PARAM $orig_def_size)

    vmsg 6 '    Default size: %s Meg' $(nq $default_size)

    : ${default_size:=-1}

    # Create a menu of sizes.  Include the default size
    local size sizes default_cnt cnt=1
    for size in $MKFS_SIZES; do
        if [ -z "$default_cnt" -a $default_size -gt 0 -a $size -ge $default_size ]; then
            sizes="$sizes $default_size"
            default_cnt=$cnt
        fi
        cnt=$((cnt + 1))
        [ $size -gt $avail ] && break
        [ $size -eq $default_size ] && continue
        sizes="$sizes $size"
    done

    # Include default size at end of menu if needed
    if [ -z "$default_cnt" -a $default_size -gt 0 ]; then
        sizes="$sizes $default_size"
        default_cnt=$cnt
    fi

    if [ -z "$sizes" -o "$default_size" = -1 ]; then
        if [ "$needed" ]; then
            non_fatal  "$_Not_enough_space_on_device_to_create_X_file_" "$(pqh $file)"
        else
            warn  "$_Not_enough_space_on_device_to_create_X_file_" "$(pqw $file)"
        fi
        return 1
    fi

   # Include all available in menu if it is appropriate
    local max=$(echo "$sizes" | sed 's/.* //')
    [ $max -lt $avail -a $avail -lt $MAX_MKFS_SIZE ] && sizes="$sizes $avail"

    local fs_type fs_size auto_mode create_mode quit=$(printf  "$_quit_")
    #[ "$needed" ] && quit=

    heading  "$_create_X_persistence_file_" "$(fq $file)"
    vmsg 1  "$_Default_size_X_Meg_Default_type_Y_" $(nq $default_size) $(pq ext3)
    warn  "$_Warning_this_operation_can_take_a_few_minutes_"

    if [ "$AUTO_MAKE_FS" ]; then
        auto_mode=true
    else
        local title=$(printf  "$_Create_X_file_manually_or_automatically_" $(fq $file))
        my_select "$title" create_mode 0 1  "$_create_automatically_"  "$_create_custom_" "$quit"
        case $create_mode in
                             "$_quit_") return 1;;
             "$_create_automatically_") auto_mode=true;;
        esac
    fi

    if [ "$auto_mode" ]; then
       fs_type=ext3
        fs_size=$default_size

    else
        local title=$(printf  "$_Please_select_size_of_X_file_" $(pqw $file))

        echo
        vmsg 1  "$_There_is_X_Meg_available_on_the_device_" $(nq $avail)
        my_select "$title" fs_size 4 "$default_cnt" $sizes "$quit"
        [ "$fs_size" =  "$_quit_" ] && return 1

        vmsg 1  "$_Filesystem_size_X_Meg_" $(nq $fs_size)

        title=$(printf  "$_Please_select_file_system_for_X_file_" $(pqw $file))

        echo
        my_select "$title" fs_type 0 2 ext2 ext3 ext4 "$quit"
        [ "$fs_type" =  "$_quit_" ] && return 1

    fi
    vmsg 1  "$_Create_X_file_of_type_Y_and_size_Z_Meg_" "$(fq $file)" "$(pq $fs_type)" "$(nq $fs_size)"

    # Now do the actual work

    mount -o remount,rw $SQFILE_DEV 2>/dev/null

    breakpoint m "before makefs"

    if ! time_cmd makefs $full_file $fs_type $fs_size; then
        err  "$_Failed_to_create_filesystem_Deleting_X_file_" $(pqe $file)
        rm -f $full_file
        return 3
    fi

    do_fsck "new $file file" $full_file -p filefs || return 1

    eval $needed_var=\$needed
    eval $wanted_var=\$wanted

    return 0
}

makefs() {
    local file=$1  type=$2  size=$3
    mkdir -p $(dirname $file)                                     || return 1
    dd if=/dev/zero of=$file bs=1M count=0 seek=$size &>/dev/null || return 2
    ld_path /sbin/mkfs.$type -q -F $file                          || return 3
}

default_makefs_size() {
    local avail=$1  size=$3  params
    eval params=\$$2
    eval local $params
    [ "$avail" -lt $mid_size ] && factor=1
    [ "$size" ] || size=$((mid_size + factor * ( avail - mid_size ) / 100))
    [ $size -gt $max_size ] && size=$max_size
    [ $size -gt $avail    ] && size=$avail
    [ $size -lt $min_size ] && return 1
    echo $size
    return 0
}

select_persist_device() {
    local device label=$1
    warn
    warn  "$_Could_not_find_a_partition_with_label_X_" "$(pqw $label)"
    warn
    msg  "$_Please_wait_while_existing_parititions_are_found_"

    breakpoint x "before select device"

    select_device  "$_persistence_" device 100 || return 1
    local ret=$?

    [ -z "$device" -o "$device" =  "$_quit_" ] && return 1

    label_device $device "$AUTO_PERSIST"
    PERSIST_ID=name=$device
    return $ret
}

fsck_persist_dev() {
    local persist_files=$1; shift
    [ "$persist_files" ] || return
    if ! _fsck_persist_dev "$@"; then
        unset WANT_ROOTFS WANT_HOMEFS
    fi
}

_fsck_persist_dev() {
    local dev=$1  mp=$2

    should_fsck $dev persist || return 0

    if ! umount $mp &> /dev/null; then
        vmsg 6  "$_Not_checking_persist_filesystem_because_it_cant_be_unmounted_"
        return 0
    fi

    do_fsck  "$_persist_device_" $dev -p
    try_mount $dev $mp && return 0

    non_fatal  "$_Could_not_remount_the_persistence_device_"
    return 1
}

mount_and_copy_rootfs() {
    if _mount_and_copy_rootfs "$@"; then
        PERSIST_ROOT=true
        if [ "$STATIC_ROOT" ]; then
            ROOTFS_DEV=$PERSIST_DEVICE
            msg  "$_Enabled_X_persistence_" "$(cq static root)"
        else
            msg  "$_Enabled_X_persistence_" "$(cq dynamic root)"
        fi
    else

        STATIC_ROOT=
        [ "$NEED_ROOTFS" ] && non_fatal  "$_Could_not_enable_required_X_persistence_" "$(pqh root!)"
    fi
}

_mount_and_copy_rootfs() {
    mount_persist_file rootfs "$ROOTFS_MP" root "$WANT_ROOTFS" "$NEED_ROOTFS" || return 1
    PERSIST_ROOT_FULL="$PERSIST_FULL_PATH/rootfs"
    check_vid "$SQFS_VID" "$ROOTFS_MP" rootfs                                 || return 1
    [ "$STATIC_ROOT" ] || copy_persist_to_ram                                 || return 1
    return 0
}

#------------------------------------------------------------------------------
# Function: copy_persist_to_ram
#------------------------------------------------------------------------------

copy_persist_to_ram() {

    vmsg 6  "$_Put_persistent_root_in_RAM_from_X_"  "$ROOTFS_MP"

    local persist_used_k=$(du -sk $ROOTFS_MP | awk '{print $1}')
    local persist_used=$((persist_used_k / 1024))
    local format="%s: $num_co%5d ${m_co}M"

    vmsg 7 "$format" '              Persistence uses' $persist_used

    if [ "$persist_used" -ge "$AUFS_RAM_SIZE" ]; then
        vmsg_nc 3 $tbar
        err   "$_Not_enough_RAM_to_hold_persistent_root_"
        warn  "$_You_need_to_X_or_use_Y_persistence_" remaster-live "$(cq static root)"
        vmsg_nc 3 $tbar
        return 1
    fi
    vmsg 5  "$_Copy_X_Megabytes_to_RAM_" "${num_co}$persist_used${m_co}"
    if ! cp_dir $ROOTFS_MP $AUFS_RAM_MP 2>&1; then
         err  "$_Copy_persistent_root_to_RAM_failed_Will_erase_partial_copy_"
         rm -rf $AUFS_RAM_MP/*
         return 2
     fi
    local ram_used=$(used_space $AUFS_RAM_MP)
    #. AUFS uses [this much RAM]
    vmsg 7 "$format" '                     AUFS uses' $ram_used
    vmsg 7 "$format" '              Persistence uses' $persist_used
    vmsg 6 "${hi_co}%s"  "$_Copy_persistent_root_to_RAM_succeeded_"

    DF_ROOTFS="$(df -P -m $ROOTFS_MP | tail -n 1)"
    log_cmd umount $ROOTFS_MP
    return 0
}

mount_persist_file() {
    local file=$1  mpnt=$2  type=$3  want=$4  need=$5

    [ "$want" ]              || return 1
    [ "$PERSIST_FULL_PATH" ] || return 1

    local full=$(readlink -f $PERSIST_FULL_PATH/$file)

    if ! [ -f "$full" ]; then
        [ "$need" ] && err  "$_Could_not_find_file_X_for_required_Y_persistence_" \
            "$(pqh $file)" "$type"
        return 3
    fi

    if should_fsck $full "$type file"; then
        do_fsck "$file file" $full -p filefs || return 1
    fi

    heading  "$_mount_persistence_file_X_at_Y_" "$(fq $file)" "$(fq $mpnt)"

    mkdir -p $mpnt
    if ! try_file_mount $full $mpnt; then
        #. Could not mount file <filename> for <type> persistence
        err  "$_Could_not_mount_file_X_for_Y_persistence_" "$full" "$type"
        err  "$_Did_not_enable_X_persistence_" "$type"
        return 4
    fi

    PERSISTENCE=$PERSISTENCE,$type
    PERSISTENCE=${PERSISTENCE#,}
    return 0
}

#------------------------------------------------------------------------------
# Function: remaster_rootfs <mountpoint> <full_path>
#
# If we did a remaster or rollback on linuxfs then we perform a similar
# operation on the rootfs file if it exists.  We have already remastered
# the rootfs file in the boot directory so if that is the same as this
# (persist) directory we do nothing here.
#------------------------------------------------------------------------------
remaster_rootfs() {
    local mp=$1  path=$2

    if [ "$DID_REMASTER" -o "$DID_ROLLBACK" ]; then
        if [ "$path" = "$BOOT_MP/$SQFILE_PATH" ]; then
            vmsg 6  "$_Will_not_remaster_X_twice_" rootfs
            return
        fi
    fi

    if [ "$DID_REMASTER" ]; then
        [ -e "$path/rootfs"  ]    && mv $path/rootfs     $path/rootfs.old
        [ -e "$path/rootfs.new" ] && mv $path/rootfs.new $path/rootfs
    fi

    if [ "$DID_ROLLBACK" ]; then
        [ -e "$path/rootfs"  ]    && mv $path/rootfs     $path/rootfs.bad
        [ -e "$path/rootfs.old" ] && mv $path/rootfs.old $path/rootfs
    fi
}


#------------------------------------------------------------------------------
#  Update bootloader menu if asked and able
#------------------------------------------------------------------------------
update_gfx_menu() {
    local script=$1  param=$2  dir=$3
    msg
    heading 'GFX Boot Menu Update'

    if ! [ -x "$script" ]; then
        err  "$_Could_not_find_script_X_" "$script"
        return
    fi

    if ! [ "$REMASTERABLE" ]; then
        if [ "$DB_PLUS" ]; then
            local tdir=$LIVE_DIR/test
            mkdir -p $tdir
            msg "Use $tdir to test $script"
            cp -r $dir/* $tdir
            dir=$tdir
        else
            err  "$_Cant_update_X_on_read_only_boot_media_" "$(pqe gfx boot)"
            return
        fi
    fi

    env VERBOSE=$VERBOSE NO_COLOR=$NO_COLOR LOW_COLOR=$LOW_COLOR \
        GFX_LOG_FILE=$LOG_DIR/gfxsave.log $script $dir $param

    vmsg_nc 6 $tbar
}

copy_homefs() {
    local homefs=$1/homefs

    [ -e "$homefs.new" ] || return

    _copy_homefs "$homefs"  || mv $homefs.new $homefs.bad

}

_copy_homefs() {
    local homefs=$1

    if ! [ -e "$homefs" ]; then
        msg  "$_No_X_file_found_Rename_Y_to_Z_" homefs homefs.new homefs
        mv $homefs.new $homefs
        return 0
    fi

    local new_home_mp=/tmp/homefs.new
    local cur_home_mp=/tmp/homefs

    log_cmd mkdir -p $cur_home_mp $new_home_mp

    if ! log_cmd mount -o loop $homefs $cur_home_mp; then
        err  "$_Could_not_mount_X_for_copy_" homefs
        return 1
    fi

    if ! log_cmd mount -o loop $homefs.new $new_home_mp; then
        err  "$_Could_not_mount_X_for_copy_" homefs.new
        return 1
    fi

    local need_meg=$(used_space $cur_home_mp)
    local have_meg=$(all_space $new_home_mp)
    need_meg=$((need_meg + 10))
    vmsg 6  "$_Have_X_Megabytes_free_Need_Y_" "$num_co $have_meg$m_co" "$num_co$need_meg$m_co"
    if [ "$need_meg" -gt "$have_meg" ]; then
        err  "$_Not_enough_room_to_copy_home_filesystem_"
        return 1
    fi

    msg  "$_Resize_homefs_Copy_contents_of_X_to_Y_" $(pq homefs) $(pq homefs.new)

    msg  "$_Copy_home_filesystem_Please_be_patient_"

    breakpoint r "Before resize homefs rsync"

    time_cmd ld_path rsync -aq --delete /tmp/homefs/ /tmp/homefs.new/
    local ret=$?

    breakpoint r "After resize homefs rsync"

    if [ "$ret" -ne 0 ]; then
        err  "$_The_X_program_failed_Will_not_resize_Y_" $(pqe rsync) $(pqe homefs)
        return 1
    fi

    log_cmd umount $cur_home_mp
    log_cmd umount $new_home_mp

    log_cmd mv -f $homefs $homefs.old
    log_cmd mv -f $homefs.new $homefs

    return 0
}

auto_login() {
    local dir file full prog=$1  terms=$2 root=$3
    [ -n "$prog" -a -n "$terms" ] || return

    db_msg "Enable autologin"

    if [ -z "$SYSTEM_D" ]; then
        sed -i.bak -r "/autologin/! s=^([$terms]:[0-9]+:respawn:[^ ]+)=\1 --autologin root=" $root/etc/inittab
        return
    fi

    local term dir sysd_dir=$root/etc/systemd/system
    [ -d $sysd_dir ] || return
    for term in $(echo $terms | sed -r 's/(.)/\1 /g'); do
        dir=$sysd_dir/getty@tty$term.service.d/
        mkdir -p $dir
        cat << AutoLogin > $dir/autologin.conf
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --noclear %I 38400 linux
AutoLogin

    done
    return

}

fancy_prompt() {
    local prompt=$1  dir=$2
    local bashrc=$dir/etc/skel/.bashrc  fp_prog=/usr/local/bin/fancy-prompts.bash

    local cmd=${prompt%%\ *}
    [ -r $dir/$fp_prog -a -n "$prompt" ] || return

    if grep -q $cmd $bashrc; then
        db_msg "fancy prompts were already enabled"
    else
        db_msg "Enable fancy prompts"
        echo "[ -r $fp_prog ] && source $fp_prog" >> $bashrc
        echo "[ -n \"\$(alias $cmd)\" ] && $prompt" >> $bashrc
    fi

    local file full_file
    for file in .bashrc .profile; do
        full_file=$dir/etc/skel/$file
        [ -f "$full_file" ] && cp $full_file $dir/root
    done
}

copy_xtra() {
    local dir xtra_dir=$1/xtra tarball=$1/xtra.tgz

    if [ -e $tarball ]; then
        msg  "$_Unpack_xtra_tarball_"
        gunzip -c $tarball | tar x -C $NEW_ROOT/
    fi

    [ -d "$xtra_dir" ] || return
    local list=$(ls $xtra_dir)
    [ -n "$list" ] || return

    msgN 'Copy xtra file(s) to:'
    for  dir in $list; do
        [ -d "$xtra_dir/$dir" ] || continue
        msgN " $from_co/$dir"
        cp -a $xtra_dir/$dir $NEW_ROOT/
    done
    msg
}

ld_path_which() {
    local xdir prog=$1
    for xdir in /sbin /bin /usr/sbin /usr/bin /usr/local/bin; do
        [ -x $LD_ROOT_DIR$xdir/$prog ] || continue
        echo $xdir/$prog
        return 0
    done
    return 1
}

ld_path() {
    local xdir found prog=$1

    [ -n "${prog##/*}" ] && prog=$(ld_path_which $prog)

    if [ -z "$prog" ]; then
        echo "${err_co}Program %s not found under $LD_ROOT_DIR" "$(pqe $1)"  >&2
        return 1
    fi

    shift
    LD_LIBRARY_PATH=$LD_PATH $LD_ROOT_DIR$prog "$@"
}

prep_ld_path() {
    local ld_path arch=$(uname -m)  dir=${1:-$SQFS_MP}

    LD_ROOT_DIR=$dir

    case $arch in
          x86_64) prep_lib64 $dir
                  ld_path=$LIVE_X64_LD_PATH ;;

        i[3-8]86) ld_path=$LIVE_386_LD_PATH ;;

               *) warn 'unknown architecture %s. Assuming %s' $(pqw $arch) $(pqw i686)
                  ld_path=$LIVE_386_LD_PATH;;
    esac

    LD_PATH=$(echo $ld_path | sed -r "s=(^|:)=\\1$dir=g")

    vmsg 8 "LD_PATH: $LD_PATH"
}

prep_lib64() {
    local dir=$1
    mkdir -p /lib64
    local loader name dest
    for loader in $dir/lib64/ld-linux-x86-64*; do
        name=$(basename $loader)
        dest=$(readlink $loader)
        ln -sf $dir$dest /lib64/$name
    done
}

mountpoints_under() {
    local dev mp other ret=1 dir=$1

    while read dev mp other; do
        case $mp in $dir/*)
            echo -n "$mp "
            ret=0 ;;
        esac
    done << Read_Mounts
$(tac /proc/mounts)
Read_Mounts
    return $ret
}

chroot_umount() {
    local mp list i  dir=${1:-$LD_ROOT_DIR}

    [ "$dir" ] || return

    for i in $(seq 1 4); do
        list=$(mountpoints_under $dir) || return 0
        vmsg 7 'umount %s' "$(pq $list)"
        for mp in $list; do
            umount $mp
        done
    done
    list=$(mountpoints_under $dir) || return 0
    err  "$_Unable_to_unmount_X_" "$(hq $list)"
    return 1
}

write_output_files() {
    local dir=$1  out_file=$1/init.out
    mkdir -p $dir
    rm -f $dir/* 2>/dev/null

    [ true ] && cat > $out_file <<Bootstrap_Out
#$tbar
#              file: $out_file
# auto-generated by: $0
#   somewhen around: $(date)
#$tbar
AUFS_MP="$AUFS_MP"
AUFS_RAM_MP="$AUFS_RAM_MP"
AUFS_VID_FILE="$AUFS_MP$VID_FILE"
BOOT_DEV="$SQFILE_DEV"
BOOT_MP="$ORIG_SQFILE_MP"
BOOT_UUID="$BOOT_UUID"
DF_ROOTFS="$DF_ROOTFS"
DID_REMASTER="$DID_REMASTER"
DID_ROLLBACK="$DID_ROLLBACK"
DID_TORAM="$DID_TORAM"
DISTRO="$DISTRO_NAME"
DISTRO_CODENAME="$DISTRO_CODENAME"
DISTRO_VERSION="$DISTRO_VERSION"
FULL_DISTRO="$DISTRO_NAME-$DISTRO_VERSION"
LINUXRC_VERSION="$VERSION"
LINUXRC_DATE="$VERSION_DATE"
LIVE_DIR="$FINAL_DIR"
PERSISTENCE="$PERSISTENCE"
PERSIST_DEV="$PERSIST_DEVICE"
PERSIST_UUID="$PERSIST_UUID"
PERSIST_DIR="$PERSIST_FULL_PATH"
PERSIST_FILE="$PERSIST_ROOT_FULL"
PERSIST_MP="$PERSIST_MP"
PERSIST_PATH="$PERSIST_PATH"
REMASTERABLE="$REMASTERABLE"
ROOTFS_MP="$ROOTFS_MP"
RW_MODE="$RW_MODE"
SQFILE_DIR="$ORIG_SQFILE_DIR"
SQFILE_FULL="$ORIG_SQFILE_FULL"
SQFILE_NAME="$SQFILE_NAME"
SQFILE_PATH="$SQFILE_PATH"
SQFS_MP="$SQFS_MP"
SQFS_VID_FILE="$SQFS_VID_FILE"
STATIC_ROOT="$STATIC_ROOT"
TORAM_MP="$TORAM_MP"
VID_ERROR="$VID_ERROR"
VID_FILE="$VID_FILE"
Bootstrap_Out

    echo "$FINAL_DIR" > $dir/live-dir

    [ "$DID_TORAM"    ]                         && touch $dir/did-toram
    [ "$REMASTERABLE" ]                         && touch $dir/remasterable
    [ -n "$PERSIST_ROOT" -a -z "$STATIC_ROOT" ] && touch $dir/save-persist

    echo "$SQFILE_DEV" > $dir/boot-device
}

old_write_output_files() {
    local dir=$1

    mkdir -p $dir
    [ true ] && cat > $dir/linuxrc.out <<Linuxrc_Out
#$tbar
#              file: $dir/linuxrc.out
# auto-generated by: $0
#   somewhen around: $(date)
#$tbar
AUFS_MP="$AUFS_MP"
AUFS_RAM_MP="$AUFS_RAM_MP"
AUFS_VID_FILE="$AUFS_MP$VID_FILE"
BOOT_DEV="$SQFILE_DEV"
BOOT_MP="$ORIG_SQFILE_MP"
BOOT_UUID="$BOOT_UUID"
DF_ROOTFS="$DF_ROOTFS"
DID_REMASTER="$DID_REMASTER"
DID_ROLLBACK="$DID_ROLLBACK"
DID_TORAM="$DID_TORAM"
DISTRO="$DISTRO_NAME"
DISTRO_CODENAME="$DISTRO_CODENAME"
DISTRO_VERSION="$DISTRO_VERSION"
FULL_DISTRO="$DISTRO_NAME-$DISTRO_VERSION"
LINUXRC_VERSION="$VERSION"
LINUXRC_DATE="$VERSION_DATE"
PERSISTENCE="$PERSISTENCE"
PERSIST_DEV="$PERSIST_DEVICE"
PERSIST_DIR="$PERSIST_FULL_PATH"
PERSIST_FILE="$PERSIST_ROOT_FULL"
PERSIST_MP="$PERSIST_MP"
PERSIST_PATH="$PERSIST_PATH"
PERSIST_UUID="$PERSIST_UUID"
REMASTERABLE="$REMASTERABLE"
ROOTFS_MP="$ROOTFS_MP"
RW_MODE="$RW_MODE"
SQFILE_DIR="$ORIG_SQFILE_DIR"
SQFILE_FULL="$ORIG_SQFILE_FULL"
SQFILE_NAME="$SQFILE_NAME"
SQFILE_PATH="$SQFILE_PATH"
SQFS_MP="$SQFS_MP"
SQFS_VID_FILE="$SQFS_VID_FILE"
STATIC_ROOT="$STATIC_ROOT"
TORAM_MP="$TORAM_MP"
VID_ERROR="$VID_ERROR"
VID_FILE="$VID_FILE"
Linuxrc_Out

    [ "$DID_TORAM" ] && cat > $dir/toram-eject.conf  <<Toram_Out
BOOT_DEV="$SQFILE_DEV"
Toram_Out

    [ "$REMASTERABLE" ] && cat > $dir/remaster-live.conf  <<Remaster_Out
AUFS_MP="$AUFS_MP"
AUFS_RAM_MP="$AUFS_RAM_MP"
AUFS_VID_FILE="$AUFS_MP$VID_FILE"
BOOT_DEV="$SQFILE_DEV"
BOOT_MP="$ORIG_SQFILE_MP"
DID_REMASTER="$DID_REMASTER"
DID_ROLLBACK="$DID_ROLLBACK"
SQFILE_DIR="$ORIG_SQFILE_MP/$SQFILE_PATH"
SQFILE_FULL="$ORIG_SQFILE_MP/$SQFILE_PATH/$SQFILE_NAME"
SQFILE_NAME="$SQFILE_NAME"
SQFILE_PATH="$SQFILE_PATH"
SQFS_MP="$SQFS_MP"
SQFS_VID_FILE="$SQFS_VID_FILE"
VID_FILE="$VID_FILE"
Remaster_Out

    [ -n "$PERSIST_ROOT" -a -z "$STATIC_ROOT" ] && cat > $dir/persist-save.conf <<Persist_Out
AUFS_MP="$AUFS_MP"
AUFS_RAM_MP="$AUFS_RAM_MP"
DF_ROOTFS="$DF_ROOTFS"
PERSIST_DEV="$PERSIST_DEVICE"
PERSIST_UUID="$PERSIST_UUID"
PERSIST_FILE="$PERSIST_ROOT_FULL"
PERSIST_MP="$PERSIST_MP"
ROOTFS_MP="$ROOTFS_MP"
SQFS_MP="$SQFS_MP"
Persist_Out
}

write_log_files() {
    local my_log=$1 final_log=$2 e=$(printf "\e")
    mkdir -p $(dirname $final_log)
    sed -r "s/$e\[[0-9;]+[mK]//g" $my_log > $final_log
    cp  $my_log $final_log.color
}

find_init_prog() {
    local d prog  full_prog prog_var=$1  dir=$2
    eval prog=\$$prog_var
    [ "$prog" ] || return 1

    for d in "" /sbin/ /bin/ /usr/sbin/ /usr/bin/ /usr/local/bin/; do
        [ -e $dir$d$prog ] || continue
        full_prog=$dir$d$prog
        eval $prog_var=\$d\$prog
        break
    done

    if [ -z "$full_prog" ]; then
        err 'Could not find program %s. Will use %s' $prog "/sbin/init"
        return 1
    fi

    if [ ! -x $full_prog ]; then
        err 'Program %s is not executable.  Will use %s'  $d$prog "/sbin/init"
        return 1
    fi
    return 0
}

run_init_scripts() {
    local new_root=$1; shift

    mkdir -p $new_root/etc/live/protect $new_root/run/lock

    local script full
    for script; do
        full=/live/etc/init.d/$script
        test -x $new_root/$full || continue
        chroot $new_root $full start
    done

    # Do a persist-save to save new passwords and persist save mode
    [ -f $new_root/live/config/new-passwords       ] || return
    [ -f $new_root/live/config/persist-save.conf   ] || return

    local persist_save=/usr/local/bin/persist-save
    [ -x $new_root$persist_save                    ] || return
    msg  "$_Do_persist_save_"
    chroot $new_root $persist_save --cli --nolog --quiet
}

late_umount() {
    local dev=$1
    [ "$dev" ] || return

    local dep
    for dep in $HOMEFS_DEV $ROOTFS_DEV $LINUXFS_DEV; do
        [ "$dep" = "$dev" ] && return
    done

    # Don't umount if not mounted
    cut -d" " -f1 /proc/mounts | grep -q "^$dev$" || return
    msg  "$_late_umount_X_" $(pq $dev)
    log_cmd umount -l $dev
}

prepare_switch_root() {
    local new_root=$1  live_dir=$2  final_live=$3

    local new_live=$new_root${final_live:-$live_dir}

    # FIXME!
    # new_live=$new_root/run/initramfs

    # Now tell kernel where the real modprobe lives
    echo "/sbin/modprobe" > /proc/sys/kernel/modprobe

    # Avoid PID wrap by setting a large max
    echo 4000000 > /proc/sys/kernel/pid_max 2>/dev/null

    mount_tmpfs $new_root/run 10 /run mode=755,nodev
    mount_tmpfs $new_live 10 $live_dir mode=755

    ln -s . $new_live/live

    cp -a /bin/shutdown $new_live/
    mkdir -p $new_live/oldroot

    local mp new
    for mp in $live_dir/*; do
        [ $mp = $new_root ] && continue
        mountpoint -q $mp   || continue
        new=$new_live/$(basename $mp)
        mkdir -p $new
        log_cmd mount --move $mp $new

        # ignore error on read-only filesystems
        # I'm not sure why the chmod is needed on the others but it is.
        chmod 755 $new 2>/dev/null
    done

    # Remove libs, modules, and programs we won't need to save a little RAM
    rm -f $(find $live_dir/lib -type f -o -type l | egrep -v "/(live-init|ld|libc|libm)[.-][^/]*$")

    (cd $live_dir/bin && rm -rf fbcondecor_helper ntfs-3g)
    rm -rf /$live_dir/etc/splash

    # Work around a bug in fbcondecor_helper
    local fbsys=$live_dir/lib/splash/sys
    mountpoint $fbsys &>/dev/null && umount $fbsys

    local dir old new
    for dir in bin config etc lib locale; do
        old=$live_dir/$dir
        new=$new_live/$dir
        mkdir -p $new
        cp -a $old/* $new
    done

    system_mount $new_root

    umount /dev/pts

    # Create $live_dir/aufs mount in new_root (for historical reasons)
    mkdir -p $new_live/$AUFS_DIR
    mount --bind $new_root $new_live/$AUFS_DIR

    PATH=$new_live/bin:$PATH

    (cd $new_root && mkdir -p sys proc dev/pts)
}

#==============================================================================
# What was last shall be first
main_wrapper "$@"
#==============================================================================
