#!/bin/ksh

#========================================
#
# usbfadm - USB Flash drive ADMinistration tool
# KAWAMATA, Yoshihiro / kaw@on.rim.or.jp
#
# $Id: usbfadm,v 1.101 2024/01/01 02:46:35 kaw Exp $
#
#========================================

# Copyright (c) 2005--2024
# Yoshihiro Kawamata
#
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
#   * Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
# 
#   * Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in
#     the documentation and/or other materials provided with the
#     distribution.
# 
#   * Neither the name of Yoshihiro Kawamata nor the names of its
#     contributors may be used to endorse or promote products derived
#     from this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#==================================
# Utility functions and subcommands
#==================================

#-------------------
# clean-ups
#
function clear_exit {
    if mount | grep -q "^$devname on /mnt" ;then
        umount $devname
    fi
    rm -f $enc_ppfile
    rm -rf $lockdir
    exit $1
}

#-------------------
# Wait for ENTER key pressed
# outputs results to stdout
#
#     usage: wait_enter prompt
#
#       output ... 1: ENTER key pressed
#                 -1: Key pressed but not ENTER
#                 -2: Error occured
#
function wait_enter {
    local prompt="$1"
    local line

    echo -n "$prompt -> " >&2; read line

    if [ X"$line" = X ]; then
        echo 1
    else
        echo -1
    fi
}

#-------------------
# ask user yes or no
# outputs answer to stdout
#
#     usage: ask_yn prompt yn
#
#       yn ... y: defaults to yes
#              n: defaults to no
#              r: no default ... ask again
#              else: no default ... return -1 if answered not yn
#
#       output ... 1: yes, 0: no, -1: else yn, -2: error occured
#
function ask_yn {

    if [ -z "$2" ]; then
        echo -2
        return
    fi

    local prompt="$1"; shift
    local yn_default="$1"; shift
    local yn_ans

    case X"$yn_default"X in
        X[Yy]X) yn_default=Y; prompt="$prompt [Y/n] -> " ;;
        X[Nn]X) yn_default=N; prompt="$prompt [y/N] -> " ;;
        X[Rr]X) yn_default=R; prompt="$prompt [y/n] -> " ;;
        *)      yn_default=E; prompt="$prompt [y/n] -> " ;;
    esac

    while :; do
        echo -n "$prompt" >&2; read yn_ans

        case X"$yn_ans"X in
            X[Yy]X) echo 1; return;;
            X[Nn]X) echo 0; return;;
            XX)
                case X"$yn_default"X in
                    XYX) echo 1;  return;;
                    XNX) echo 0;  return;;
                    XRX) continue;;
                    *)   echo -1; return;;
                esac;;
            *)
                continue;;
        esac
    done
}

#-------------------
# ask selection out of multiple items
# outputs answer to stdout
#
#     usage: ask_which prompt default item1 item2 ...
#
#       Note: although user's choice is one originated
#             default is zero originated
#
#       output: first word of selected item
#               returns empty line unless selected normally
#
function ask_which {
    if [ $# -lt 3 ]; then
        return
    fi

    local prompt="$1";  shift
    local default="$1"; shift
    local i item val

    # skip null item
    #
    i=0
    for val in "$@"; do
        if [ -n "$val" ]; then
           item[$i]="$val"
           i=$((i+1))
        fi
    done

    # only one item is default itself
    #
    [ "${#item[@]}" = 1 ] && default=${item[0]}

    i=0
    while [ -n "${item[$i]}" ]; do
        if [ "$default" = "${item[$i]}" ]; then
            OIFS="$IFS"
            IFS= prompt="$prompt\n"`printf '%3d: [%s]' $((i+1)) ${item[$i]}`
            IFS="$OIFS"
        else
            OIFS="$IFS"
            IFS= prompt="$prompt\n"`printf '%3d:  %s' $((i+1)) ${item[$i]}`
            IFS="$OIFS"
        fi
        i=$((i+1))
    done
    echo "$prompt" >&2

    local ans
    ans=`rl_wread '' ''`

    # take first argument
    #
    set -- $ans
    ans=$1
    
    # return selected item
    #
    if expr "$ans" : '^[0-9][0-9]*$' >/dev/null && \
       [ "$ans" -le ${#item[@]} ]; then
        set -- ${item[$((ans-1))]}
        echo $1
    elif [ -n "$default" -a -z "$ans" ]; then
        set -- $default
        echo $1
    fi
}

#-------------------
# read user's input with readline functionality
# outputs echoed to stdout
#
#     usage: rl_wread prompt-str default-str [completion words ....]
#
function rl_wread {
    local prompt="$1";  shift
    local default="$1"; shift
    local retval

    # check if rlwrap is available
    #   When control tty is missing (in /etc/rc.shutdown for example),
    #   rlwrap in command substitution "$(rlwrap ...) " fails.
    if retval=$(/usr/local/bin/rlwrap true) 2>/dev/null; then
        echo "$@" > $lockdir/rl_words
        rlwrap -b '' \
               -f $lockdir/rl_words \
               -P "$default" \
               sh -f -c 'echo -n "'"$prompt"'->" >&2 ; read w || echo EOF; echo $w' || echo RL_ERR
    else
        #-------------------
        # fallback to dumb input
        #
        if [ X"$default" = X ]; then
            echo -n "${prompt}->" >&2
            read w
        else
            echo -n "$prompt [$default] -> " >&2
            read w
            if [ X"$w" = X ]; then
              w="$default"
            fi
        fi
        echo $w
    fi
}

function notice {
    echo
    echo ========================================
    echo = "$@"
    echo =
}

#-------------------
# convert bytes to sectors
# bytes can be suffixed by 'k', 'm', 'g' or 't'
#
# Outputs to stdout
#   null string unless converted sucessfully
#
function byte2sect {
    local args=$@  # concat all arguments as a string
    local val
    val=$(expr "$args" : '0*\([1-9][0-9]*\) *[Tt][Bb]*$') && { echo "$((1024*1024*1024*1024*val/sect_size))"; return; }
    val=$(expr "$args" : '0*\([1-9][0-9]*\) *[Gg][Bb]*$') && { echo "$((1024*1024*1024*val/sect_size))";      return; }
    val=$(expr "$args" : '0*\([1-9][0-9]*\) *[Mm][Bb]*$') && { echo "$((1024*1024*val/sect_size))";           return; }
    val=$(expr "$args" : '0*\([1-9][0-9]*\) *[Kk][Bb]*$') && { echo "$((1024*val/sect_size))";                return; }
    val=$(expr "$args" : '0*\([1-9][0-9]*\) *[Bb]*$')     && { echo "$((val/sect_size+1))";                   return; }
    val=$(expr "$args" : '00*') && { echo 0; return; }
}

#-------------------
# convert sectors to bytes
#
# Outputs to stdout
#   null string unless converted sucessfully
#
function sect2byte {
    local val
    val=$(expr "$1" : '00*$') && { echo "0B"; return; }
    local scale
    case "$2" in
        2) scale=$((1024*1024));;
        3) scale=$((1024*1024*1024));;
        *) scale=1024;;
    esac
    if val=$(expr "$1" : '\([1-9][0-9]*\)'); then
        val=$((sect_size*val))
        [ "$val" -lt $scale ] && { echo "${val}B"; return; }
        val=$((val/1024)); [ "$val" -lt $scale ] && { echo "${val}KB"; return; }
        val=$((val/1024)); [ "$val" -lt $scale ] && { echo "${val}MB"; return; }
        val=$((val/1024)); [ "$val" -lt $scale ] && { echo "${val}GB"; return; }
        val=$((val/1024)); echo "${val}TB"; return
    fi
}

#-------------------
# convert device name to DUID
#
# Usage: dev2duid device_name
#
#     device_name: e.g. 'sd1'
#
#   outputs DUID to stdout
#   or no output if conversion failed
#
dev2duid () {

    # check device name
    #
    if ! expr "$1" : '[ws]d[0-9]' >/dev/null && \
       ! expr "$1" : 'vnd[0-9]' >/dev/null; then
        return
    fi
    local _dev="$1"

    # scan list of devices
    #
    local _dev_duid
    for _dev_duid in $(sysctl -n hw.disknames | tr , ' '); do
        if expr $_dev_duid : $_dev >/dev/null; then
            echo ${_dev_duid##*:}
            return
        fi
    done
}

#-------------------
# copy directory tree with progress display
#
# Usage: copydirs srcdir destdir
#
copydirs () {
    local srcdir;  srcdir="$1"
    local destdir; destdir="$2"
    [ -d "$srcdir" ]  || { echo "source directory $srcdir not found";       return; }
    [ -d "$destdir" ] || { echo "destination directory $destdir not found"; return; }
    if [ -x /usr/local/bin/pv ]; then
        local srcsize; srcsize=$(df -k /ram | awk '/ \/ram$/ { print $3 "K"}')
        echo "copying $srcdir to $destdir (${srcsize}B approx.):"
        (cd "$srcdir" && pax -wX -x cpio .) | (pv -s $srcsize; echo -n "waiting for pax to finish ... " >&2) | (cd "$destdir" && pax -rpe)
        (echo -n "syncing ... "; sync; sync; sync; echo done.) >&2
    else
        (while :; do sleep 15; pkill -INFO dd || exit; done) &
        pax -wX -x cpio "$srcdir" | dd bs=1m | (cd "$destdir" && pax -rpe)
    fi
}

#-------------------
# scan specified disk
# then outputs its parameters with form of sh var assignment
#
# Usage: eval `disk_scan disk`
#
# Outputs: scandev    : name of device, e.g. 'sd0'
#          sect_size  : sector size - bytes/sector
#          sects_trk  : number of sectors per track
#          sects_total: number of total sectors
#          diskscan   : result of scan - 'ok' or 'ng'
#
function disk_scan {
    [ -z "$1" ] && return
    disklabel -c "$1" | awk '
BEGIN {
    paramfound = 0
    print "scandev='"$1"'"
}
$1 == "bytes/sector:"  { sect_size=$2;   print "sect_size=" sect_size; paramfound++; }
$1 == "sectors/track:" { print "sects_trk=" $2; paramfound++ }
$1 == "boundstart:"    { print "bound_start=" $2; paramfound++ }
$1 == "boundend:"      { print "bound_end=" $2; paramfound++ }
/^total sectors: /     { sects_total=$3; print "sects_total=" $3; paramfound++; }
END {
    if (paramfound==5) {
        print "diskscan=ok"
    } else
        print "diskscan=ng"
}'
}

#-------------------
# show global parameters
#
# Usage: showparams ['verbose']
#
#   verbose is for debug output
#
function showparams {

    # global variables for newdrive subcommand
    # 
    # global parameters
    #    bootmode   : initial boot mode - e.g. 'usbflash'
    #    devname    : path of target device for sync - e.g. '/dev/sd0d'
    #    devnames   : all disk names attached to this machine - e.g. ' sd1d sd2d'
    #    uconf      : name of configuration - e.g. 'userdemo'
    # 
    # constants
    #    fuguita_sys_mb : system size of FuguIta in MB
    #    fuguita_uefi_kb: Partition size for EFI Sys
    #    osrel      : OpenBSD's release - e.g. '6.4'
    #    hwmac      : name of the platform - e.g. 'amd64'
    #    verarch    : $osrel/$hwmac
    # 
    # target disks
    #    newdev     : device name to newdrive - e.g. 'sd0'
    #    parttype   : partition type - MBR/GPT/Hybrid
    #    instsys    : boot type - none/mbr (should be none/Legacy/UEFI)
    # 
    # results of disk_scan()
    #    scandev    : name of device, e.g. 'sd0'
    #    sect_size  : sector size - bytes/sector
    #    sects_trk  : number of sectors per track
    #    sects_total: number of total sectors
    #    bound_start: start of OpenBSD boundary
    #    bound_end  : end of OpenBSD boundary
    #    diskscan   : result of scan - 'ok' or 'ng'
    # 
    # partition sectors (ps_ for short)
    #    ps_free : available sectors for user's partitioning
    #    ps_hgap : head gap (for MBR/GPT and disklabel)
    #    ps_uefi : sectors for UEFI (partition i)
    #    ps_fisys: sectors for FuguIta's system (partition a)
    #    ps_fiswp: sectors for FuguIta's Swap (partition b)
    #    ps_fidat: sectors for FuguIta's Data Store (partition d)
    #    ps_fat  : sectors for Data Exchange (partition i or j)
    #    ps_tgap : tail gap (for GPT)
    #
    # crypto stuffs
    #    enc_fidat : Is FuguIta's Data Store encrypted?
    #    enc_pp1   : Primary entered passphrase
    #    enc_pp2   : Secondary entered passphrase (cleared after check)
    #    enc_ppfile: file containing passphrase
    # 
    # vnode devices
    #
    #    vnfile  : raw disk image file
    # 
    # memory-based file systems
    #
    #    memfstype  : FS type of /ram on currently running system
    #    memfs_new  : FS type of /ram for newdrive

    echo
    echo "target disk: $scandev"
    echo "  partition type=$parttype"
    echo "       boot type=$instsys"
    echo
    if [ "$1" = verbose ]; then
        cat<<EOT | awk 'BEGIN{FS=","} {printf("%20s : %10s\n", $1, $2)}'
--------------------,----------
partition,size
--------------------,----------
whole disk,$sects_total
partition tables,${ps_hgap}+${ps_tgap}
UEFI system,$ps_uefi
FuguIta system,$ps_fisys
FuguIta swap,$ps_fiswp
FuguIta user data,$ps_fidat
MSDOS FAT,$ps_fat
--------------------,----------
EOT
        echo
        echo '===== fdisk =========='
        fdisk -v $scandev | egrep '[0-9]: |^(Primary GPT|Secondary GPT|MBR):'
        echo
        echo '===== disklabel ======'
        disklabel -c $scandev | egrep '^  [a-p]:|^bound'
    else
        cat<<EOT | awk 'BEGIN{FS=","} {printf("%20s : %6s\n", $1, $2)}'
--------------------,------
partition,size
--------------------,------
whole disk,`sect2byte $sects_total`
partition tables,`sect2byte $((ps_hgap+ps_tgap))`
UEFI system,`sect2byte $ps_uefi`
FuguIta system,`sect2byte $ps_fisys`
FuguIta swap,`sect2byte $ps_fiswp`
FuguIta user data,`sect2byte $ps_fidat`
MSDOS FAT,`sect2byte $ps_fat`
--------------------,------
EOT
    fi
}

#-------------------
# zero-filling specified disk 1MB from the head
#
#     Usage: zerofill_head
#
# results of previous execution of disk_scan affects this.
#
function zerofill_head {
    notice "Clearing MBR, GPT and BSD disklabel"
    [ "$diskscan" = ok ]  || return

    # clear partition table and disklabel
    dd if=/dev/zero of=/dev/r"$scandev"c bs=$((2048*sect_size)) count=$(((bound_start+`byte2sect 1M`)/2048))
}

#-------------------
# setup MBR type fdisk partitions
#
#     Usage: fdisk_init partition-type
#
# results of previous execution of disk_scan affects this.
#
function fdisk_init {
    [ "$diskscan" = ok ]  || return
    [ -z "$1" ] && return

    local ptype="$1"

    if [ ! $secs_total = $((ps_hgap+ps_uefi+ps_fisys+ps_fiswp+ps_fidat+ps_fat+ps_tgap)) ]; then
        echo "Inconsistent sector sizes: $secs_total != $ps_hgap + $ps_uefi + $ps_fisys + $ps_fiswp + $ps_fidat + $ps_fat + $ps_tgap"
        return
    fi

    notice "Setting up fdisk partitions"

    if [ "$debugfile" ]; then
        { echo
          echo "////////// fdisk_init ////////////////////////////////////////"
          showparams verbose
        } >> "$debugfile"
    fi

    # - initialize partition table
    # - build input string for fdisk
    #
    local fdisk_input=''
    case "$ptype" in
        MBR)
            fdisk -iy $scandev
            [ 0 -lt $ps_uefi ] && fdisk_input="e 0\nEF\nn\n${ps_hgap}\n${ps_uefi}\n"
            [ 0 -lt $ps_fat ]  && fdisk_input="${fdisk_input}e 2\n0C\nn\n$((ps_hgap+ps_uefi+ps_fisys+ps_fiswp+ps_fidat))\n${ps_fat}\n"
            [ 0 -lt $((ps_fisys+ps_fiswp+ps_fidat)) ] && fdisk_input="${fdisk_input}e 3\nA6\nn\n$((ps_hgap+ps_uefi))\n$((ps_fisys+ps_fiswp+ps_fidat))\n"
            fdisk_input="${fdisk_input}quit\n"
            ;;
        GPT)
            fdisk -igy $scandev
            fdisk_input="e 0\n0\n"
            [ 0 -lt $ps_uefi ] && fdisk_input="${fdisk_input}e 1\nEF\n${ps_hgap}\n${ps_uefi}\nUEFI Boot\n"
            [ 0 -lt $ps_fat ]  && fdisk_input="${fdisk_input}e 2\n0C\n$((ps_hgap+ps_uefi+ps_fisys+ps_fiswp+ps_fidat))\n$ps_fat\nMSDOS FAT\n"
            [ 0 -lt $((ps_fisys+ps_fiswp+ps_fidat)) ] && fdisk_input="${fdisk_input}e 3\nA6\n$((ps_hgap+ps_uefi))\n$((ps_fisys+ps_fiswp+ps_fidat))\nOpenBSD Area\n"
            fdisk_input="${fdisk_input}quit\n"
            ;;
        Hybrid)
            fdisk -iy $scandev
            fdisk_input="e 0\nEE\nn\n1\n*\n"
            [ 0 -lt $ps_uefi ] && fdisk_input="${fdisk_input}e 1\nEF\nn\n${ps_hgap}\n${ps_uefi}\n"
            [ 0 -lt $ps_fat ]  && fdisk_input="${fdisk_input}e 2\n0C\nn\n$((ps_hgap+ps_uefi+ps_fisys+ps_fiswp+ps_fidat))\n$ps_fat\n"
            [ 0 -lt $((ps_fisys+ps_fiswp+ps_fidat)) ] && fdisk_input="${fdisk_input}e 3\nA6\nn\n$((ps_hgap+ps_uefi))\n$((ps_fisys+ps_fiswp+ps_fidat))\n"
            fdisk_input="${fdisk_input}quit\n"
            ;;
    esac

    # invoke fdisk
    #
    if [ -n "$fdisk_input" ]; then
        if [ "$debugfile" ]; then
            { echo
              echo "===== fdisk input ====="
              echo "$fdisk_input"
            } >> "$debugfile"
        fi
        echo -n "$fdisk_input" | fdisk -e "$scandev" >/dev/null
    fi
}

#-------------------
# setup OpenBSD's partition and all filesystems
#
#     usage: setup_fs
#
# With "Legacy", partition a is prepared and
# FuguIta system will be copied.
#
function setup_fs {
    [ "$diskscan" = ok ]  || return

    if [ 0 -eq `expr "$scandev" : '^[sw]d[0-9]$'` ] &&
       [ 0 -eq `expr "$scandev" : '^vnd[0-9]$'` ]; then
        return
    fi

    notice "Setting up disklabel and FFS partitions"

    if [ "$debugfile" ]; then
        { echo
          echo "////////// setup_fs ////////////////////////////////////////"
          showparams verbose
        } >> "$debugfile"
    fi

    # build input string for disklabel
    #
    local disklabel_input='' part

    # commands to delete existing FFS/RAID/swap partitions
    for part in `disklabel -c $scandev | awk '/^  [a-p]: .*  (4\.2BSD|RAID|swap)/{print $1}' | tr -d :`; do
        disklabel_input="${disklabel_input}d $part\n"
    done

    # commands to create new FFS partitions
    [ 0 -lt $ps_fisys ] && disklabel_input="${disklabel_input}a a\n$((ps_hgap+ps_uefi))\n${ps_fisys}\n4.2BSD\n"
    [ 0 -lt $ps_fiswp ] && disklabel_input="${disklabel_input}a b\n$((ps_hgap+ps_uefi+ps_fisys))\n${ps_fiswp}\nSWAP\n"
    if [ 0 -lt $ps_fidat ]; then
        local fstype='4.2BSD'
        [ "$enc_fidat" = 'yes' ] && fstype='RAID'
        disklabel_input="${disklabel_input}a d\n$((ps_hgap+ps_uefi+ps_fisys+ps_fiswp))\n*\n${fstype}\n"
    fi
    disklabel_input="${disklabel_input}q\ny\n"

    # invoke disklabel
    #
    if [ -n "$disklabel_input" ]; then
        if [ "$debugfile" ]; then
            { echo
              echo "===== disklabel input ====="
              echo "$disklabel_input"
            } >> "$debugfile"
        fi
        echo -n "$disklabel_input" | disklabel -c -E "$scandev" >/dev/null
    fi

    local disklabel=`disklabel -c $scandev`

    echo "$disklabel" | grep -q 'i:.*MSDOS' && newfs_msdos ${scandev}i
    echo "$disklabel" | grep -q 'j:.*MSDOS' && newfs_msdos ${scandev}j

    if echo "$disklabel" | grep -q 'a:.*4\.2BSD'; then
        # very few files ... make inode density low
        newfs -O 1 -m 0 -o space -i$((fuguita_sys_mb*1024*1024/100)) ${scandev}a

        notice "Copying FuguIta system files"

        mount -o async /dev/${scandev}a /mnt
        (cd /sysmedia
         pax -rwvpe boot bsd-fi bsd-fi.mp cdboot cdbr etc /mnt)

        notice "Transferring filesystem image"
        if [ -r /sysmedia/fuguita-${osrel}-${hwmac}.ffsimg ]; then
            if [ -x /usr/local/bin/pv ]; then
                pv /sysmedia/fuguita-${osrel}-${hwmac}.ffsimg | dd of=/mnt/fuguita-${osrel}-${hwmac}.ffsimg bs=1m
            else
                (while :; do sleep 15; pkill -INFO dd || exit; done) &  # to display progress
                dd if=/sysmedia/fuguita-${osrel}-${hwmac}.ffsimg of=/mnt/fuguita-${osrel}-${hwmac}.ffsimg bs=1m
            fi
        fi

        # To change FS type mounted on /ram,
        # rewrite setting in file system image
        #
        if [[ $instsys != none ]]; then
            local vn=$(vnconfig /mnt/fuguita-${osrel}-${hwmac}.ffsimg)
            if [[ -z "$vn" ]]; then
                echo 'no available vnode device, FS type of /ram not chagned' >&2
            else
                mkdir -p /mnt2
                mount /dev/${vn}a /mnt2
                if grep -q "memfstype=$memfs_new" /mnt2/etc/fuguita/global.conf; then
                    :  # same value already - do nothing
                else
                    # rewrite the value
                    sed -i -e '1,$s/memfstype=[a-z][a-z]*fs/memfstype='$memfs_new'/' /mnt2/etc/fuguita/global.conf
                fi
                umount /mnt2
                rmdir /mnt2
                vnconfig -u $vn
            fi
        fi

        case $instsys in
            Legacy|Hybrid)
                notice "Installing BIOS boot loader"
                /usr/sbin/installboot -v -r /mnt ${scandev} /usr/mdec/biosboot /usr/mdec/boot
                umount /mnt
                ;;
            UEFI)
                # if MBR exists, install MBR loader also
                #
                if [ "$parttype" = MBR \
                                 -a -f /fuguita/usr/mdec/biosboot \
                                 -a -f /fuguita/usr/mdec/boot ]; then
                    notice "Installing BIOS loader"
                    /usr/sbin/installboot -v -r /mnt ${scandev} /fuguita/usr/mdec/biosboot /fuguita/usr/mdec/boot
                fi
                umount /mnt

                notice "Installing UEFI boot loader"
                if mount -t msdos -o -l /dev/${scandev}i /mnt; then
                    mkdir -p /mnt/efi/BOOT
                    cp /fuguita/usr/mdec/*.EFI /mnt/efi/BOOT
                    umount /mnt
                fi
                ;;
        esac

        notice "Checking the partition for system files"
        fsck -fy /dev/r${scandev}a
    fi

    local datdev=$scandev  # datdev will be decrypted device if encryption specified

    # setup crypto partition
    #
    if echo "$disklabel" | grep -q 'd:.*RAID'; then
        notice 'Setting up for data store encryption'

        # setup passphrase file
        #
        rm -f $enc_ppfile
        local def_umask=`umask`
        umask 077
        echo "$enc_pp1" > $enc_ppfile
        chmod 0600 $enc_ppfile
        umask $def_umask
        unset enc_pp1

        # create a crypto volume
        #
        bioctl -p $enc_ppfile -c C -l /dev/${scandev}d softraid0
        rm -f $enc_ppfile

        # check and get created device
        #
        datdev=$(bioctl -i softraid0 \
                | awk "/^softraid.*CRYPTO\$/ { dev=\$(NF-1) }
                       /<${scandev}d>\$/     { datdev=dev }
                       END                   { print datdev }")

        # create a FFS partition in the crypto volume
        #
        if [ 0 -lt `expr "$datdev" : '^sd[0-9]'` ]; then
            fdisk -iy $datdev
            echo "a d\n\n*\n4.2BSD\nw\nq\n" | disklabel -c -E $datdev >/dev/null
        else
            return  # crypto volume not found
        fi
    fi

    # setup data store device
    #   datdev is decrypted device if encryption specified
    #   otherwise same as scandev
    #
    if disklabel -c $datdev | grep -q 'd:.*4\.2BSD'; then
        notice "Setting up a partition for user's data"
        if [ $ps_fidat -lt `byte2sect 2G` ]; then
            # adjust inode density for little space
            # because too many symlinks when booted mode 0
            newfs -O 1 -m 0 -o space -i1024 -b4096 -f512 ${datdev}d
        elif [ $ps_fidat -lt `byte2sect 4G` ]; then
            newfs -O 2 -m 0 -o space ${datdev}d
        elif [ $ps_fidat -lt `byte2sect 8G` ]; then
            newfs -O 2 -m 0 ${datdev}d
        else
            newfs -O 2 -m 0 -o time ${datdev}d
        fi

        sys_duid=$(dev2duid $scandev)
        dat_duid=$(dev2duid $datdev)
        mount -o async /dev/${datdev}d /mnt
        mkdir -p /mnt/livecd-config/${verarch}
        local umem="75%"
        [[ "$memfs_new" = 'tmpfs' ]] && umem=0
        cat <<EOT >/mnt/livecd-config/${verarch}/noasks
#
# noasks - parameter settings for non-interactive boot
#
# Make statements uncommented
# Then assign real values
#
#
# FuguIta system device
#${sys_duid:+   - Use one of two lines}
#noask_rdev=${scandev}a  # device name format
#${sys_duid:+noask_rdev=${sys_duid}.a  # DUID format}
#
# max ${memfs_new} size
#noask_umem=${umem}
#
# boot mode
#noask_setup_rw_mode=3
#
# storage device
#${dat_duid:+   - Use one of two lines}
#noask_confdev=${datdev}d  # device name format
#${dat_duid:+noask_confdev=${dat_duid}.d  # DUID format}
#
# data set name in USB flash drive
#noask_confdir=$(hostname -s)
EOT
        umount /mnt

        notice "Checking the partition for persistent storage"
        fsck -fy /dev/r${datdev}d
    fi

    # delete crypto volume
    #
    if echo "$disklabel" | grep -q 'd:.*RAID'; then
        # detach the device
        bioctl -d ${datdev}
    fi
}

#-------------------
# clean-ups for vnode device
#
#     usage: clear_vn vn_device vn_img_rm
#
#    vn_device : vnode to be unattached 
#    vn_img_rm : remove this file if specified
#
function clear_vn {
    local vndev="$1"
    local vnrm="$2"

    case "$vndev" in
        vnd[0-3])
            if vnconfig -l | grep -q "^$vndev: covering "; then
                vnconfig -u "$vndev"
                fi
            ;;
    esac
    [ -n "$vnrm" ] && rm -f $vnrm
}

#==================================
# Active Code from HERE.
#==================================

#-------------------
# systemwide constants
#
  lockdir=/tmp/usbfadm.lock
    osrel=`sysctl -n kern.osrelease`
    hwmac=`sysctl -n hw.machine`
  verarch=${osrel}/${hwmac}

#-------------------
# environment check
#
if [ ! -r /usr/fuguita/version ]; then
    echo "You are not running FuguIta."
    exit 1
fi

if [ ! `id -un` = root ]; then
    echo "${0##*/} must be run as a root."
    exit 1
fi

# check for cwd
#
if ! pwd >/dev/null 2>&1; then
    # in case of cwd already unmounted
    cd /
fi
if [ 1 -le $(expr X"$(pwd)" : X/mnt) ]; then
    echo
    echo 'You are under /mnt. Please move to other directory.'
    exit 1
fi

umask_o=`umask`; umask 077
if ! mkdir $lockdir 2>/dev/null; then
    umask $umask_o
    echo "another ${0##*/} running (or remove $lockdir)"
    exit 1
fi
umask $umask_o

#-------------------
# initializations
#
trap 'echo ${0##*/}: interrupted. >&2; clear_exit 1' INT

# read status files
#
[ -r /boottmp/boot_mode ]            && bootmode=`cat /boottmp/boot_mode`
[ -r /boottmp/boot_restore_devname ] && devname=`cat /boottmp/boot_restore_devname`
[ -r /boottmp/boot_user_config ]     && uconf=`cat /boottmp/boot_user_config`

# systemwide defaults
#
  disk_head_gap=64
 fuguita_sys_mb=1024
fuguita_uefi_kb=512
     enc_ppfile='/tmp/usbfadm_pp'
      memfstype='mfs'  # mfs or tmpfs

#
# overwrite defaults
#
if [[ -r /etc/fuguita/global.conf ]]; then
    . /etc/fuguita/global.conf
fi


# command line arguments
#
cmdargs=`getopt qritdh $*`
set -- $cmdargs
while [ 1 -lt $# ]; do
    case "$1" in
        -r) opt_mode=resync; shift;;
        -i) opt_mode=info;   shift;;
        -q) opt_quiet=yes;   shift;;
        -t) opt_trace=yes; set -x; shift;;
        -d) debugfile=`pwd`/usbf.debugout  # This is also flag.
            shift;;
        *) cat <<EOT 2>&1
Usage: ${0##*/} [-riqtdh]

    -r : redo sync non-interactively
        (must run 'sync' at interactive mode before doing this)
    -i : show info about the persistent storage
    -q : quiet mode when redo sync
    -t : trace output (pass -x to shell)
    -d : debug output for newdrive to file 'usbf.debugout'
    -h : print this help
EOT
           clear_exit 0
           ;;
    esac
done
[ "$debugfile" ] && rm -f "$debugfile"

#-------------------
# non-interactive process
#
case "$opt_mode" in
    resync)
        if [ -z "$devname" ]; then
            echo "${0##*/}: Name of device isn't set. Use 'target', then 'sync' in interactive mode."
            clear_exit 1
        elif [ -z "$uconf" ]; then
            echo "${0##*/}: Name of saving data isn't set. Use 'saveas', then 'sync' in interactive mode."
            clear_exit 1
        fi

        if mount -o async,noatime $devname /mnt; then
            if [ "$opt_quiet" = 'yes' ]; then
                rsync -q -rlptgo --devices -xHS --delete --include '*/tmp' --exclude 'tmp/*' /ram/. /mnt/livecd-config/$verarch/$uconf/.
                retval=$?
            else
                notice "Sync ${devname} with current ${memfstype} as ${uconf}"
                rsync --progress -rlptgo --devices -xHS --delete --include '*/tmp' --exclude 'tmp/*' /ram/. /mnt/livecd-config/$verarch/$uconf/.
                retval=$?
            fi
            # find /mnt \! -type d \! -type f \! -type l -print | xargs rm -f
            rm -rf /mnt/livecd-config/$uconf/tmp/{.??*,*}
            sync
            sleep 5
            umount $devname
	    clear_exit $retval
        else
	    clear_exit 1
        fi
        ;;
    info)
        cat <<EOT
FuguIta's persistent storage status:

 Version/Arch: $verarch  (FuguIta-`cat /usr/fuguita/version`)
    Boot mode: $bootmode
Target device: ${devname:-not set}
Data saved as: ${uconf:-not set}

EOT
        if [[ -n "$devname" ]] && mount -r $devname /mnt; then
            (cd /mnt/livecd-config
             echo
             df -hi /mnt
             echo "\nscanning...\n" && du -sh [1-9].[0-9]/*/* [1-9].[0-9]-*-*) 2>/dev/null
            umount $devname
        fi
        clear_exit $?;
        ;;
esac

#-------------------
# interactive process
#   banner and command loop
#
d=$devname ; [ -z "$devname" ] && d='not set'
u=$uconf   ; [ -z "$uconf" ]   && u='not set'
cat <<EOT

Welcome to usbfadm.
USB flash drive administration tool for FuguIta

 Version/Arch: $verarch  (FuguIta-`cat /usr/fuguita/version`)
    Boot mode: $bootmode
Target device: $d
Data saved as: $u
EOT

echo
# check if rlwrap is available
#   When control tty is missing (in /etc/rc.shutdown for example),
#   rlwrap in command substitution "$(rlwrap ...) " fails.
if retval=$(/usr/local/bin/rlwrap true) 2>/dev/null; then
    echo "readline capability available"
    echo "TAB to complete the reserved words"
else
    echo "Sorry, readline capability unavailable"
fi

echo
echo 'Type ? for help.'

while :; do
    #-------------------
    # setup prompt string
    #
    d="${devname#/dev/}" ; [ -z "$devname" ] && d='?'
    u="$uconf"           ; [ -z "$uconf" ]   && u='?'
    echo
    cmd=`rl_wread "$d : $u " '' quit bye exit sync info saveas target newdrive expand help ?`

    set X $cmd

    #-------------------
    # process every command
    #
    case X"$2" in
        #-------------------
        # finish all
        #
        Xq|Xquit|Xbye|Xexit|XEOF|XRL_ERR)
        echo
        echo 'Bye bye...'
        break;
        ;;

        #-------------------
        # write back memory file system
        # to the storage device
        #
        Xsync)
        echo
        if [ X"$devname" = X ]; then
            echo "Name of device not set. Use 'target' to perform this."
            continue
        fi
        if [ X"$uconf" = X ]; then
            echo "Name of saving data not set. Use 'saveas' to perform this."
            continue
        fi

        if [ 1 = `ask_yn "Sync current ${memfstype} as \\\`\\\`$uconf'' , OK?" n` ]; then
            if mount -o async,noatime $devname /mnt; then
                echo
                if [ -d /mnt/livecd-config ]; then
                    if [ -d /mnt/livecd-config/$verarch/$uconf ]; then
                        # previous backup exists ... do differencial copy
                        rsync -v -rlptgo --devices -xHS --delete --include '*/tmp' --exclude 'tmp/*' /ram/. /mnt/livecd-config/$verarch/$uconf/.
                    else
                        # first backup ... use cpio
                        mkdir -p /mnt/livecd-config/$verarch/$uconf
                        copydirs /ram /mnt/livecd-config/$verarch/$uconf
                        rm -rf /mnt/livecd-config/$verarch/$uconf/tmp/{.??*,*}
                    fi
                    echo "$devname" > /boottmp/boot_restore_devname
                    echo "$uconf"   > /boottmp/boot_user_config
                    sync
                    sleep 5
                else
                    echo
                    echo "$devname isn't for FuguIta's backup."
                    echo "(Directory /livecd-config doesn't exist)"
                fi
                umount $devname
            fi
        fi
        ;;

        #-------------------
        # show status of storage device
        #
        Xinfo)
        if [[ -n "$devname" ]] && mount -r $devname /mnt; then
            (cd /mnt/livecd-config
             echo
             df -hi /mnt
             echo "\nscanning...\n" && du -sh [1-9].[0-9]/*/* [1-9].[0-9]-*-*) 2>/dev/null
            umount $devname
        fi
        ;;

        #-------------------
        # set data set name
        # default is our FQDN.
        #
        Xsaveas)
        if [ X"devname" != X ] && mount $devname /mnt; then
            [ -d /mnt/livecd-config/$verarch ] || mkdir -p /mnt/livecd-config/$verarch
            confs=`cd /mnt/livecd-config/$verarch && echo *`
            umount $devname
        fi
        [ X"$uconf" = X ] && uconf=`hostname -s`
        uconf=`rl_wread "Name of saved data" $uconf $confs`
        uconf=`echo $uconf | tr -cd 0-9A-Za-z.,_-+=:%@`  # strip invalid chars
        echo
        if [ X"$uconf" = X ]; then
            echo "Unset the name of saving data"
        else
            echo "Your data will be saved as \`\`$uconf''."
        fi
        ;;

        #-------------------
        # search and set storage device
        #
        Xtarget)
        echo
        echo 'Searching storage device'
        echo 'Please make sure the device inserted.'
        [ `wait_enter 'Then press ENTER'` -lt 1 ] && continue

        #-----------------------
        # search partitions for saved data
        #
        while :; do
            devname=''
            devs_normal=' '
            devs_crypto=' '
            # listing storage devices with investigation
            for rdev in `sysctl -n hw.disknames \
                         | sed -E -e 's/:[0-9a-f]+/:/g; s/[:,]+/ /g;'`; do

                # vnd included
                #(expr $rdev : '^[sw]d[0-9]' || expr $rdev : '^vnd[0-9]' ) >/dev/null \
                #|| continue

                # vnd execluded
                expr $rdev : '^[sw]d[0-9]' >/dev/null || continue

                # $rdev is detected storage name - e.g. "sd2"
                for ptfs in `disklabel -c "$rdev" 2>/dev/null \
                             | awk '/^  [abd-p]:/ { print $1 $4 }'`; do
                    # $ptfs is "partition:fstype" - e.g. "d:4.2BSD"
                    dev="$rdev"${ptfs%:*}
                    fs=${ptfs#*:}
                    case "$fs" in
                        RAID)
                            # check this RAID partition is crypto
                            if dd if=/dev/r$dev bs=1k count=9 2>/dev/null \
                               | fgrep -q 'SR CRYPTO'; then
                                devs_crypto="$devs_crypto$dev "
                                echo -n "?$dev "
                            else
                                echo -n "$dev "
                            fi
                            ;;
                        4.2BSD)
                            # check this FFS partition is for FuguIta's storage
                            if mount -r /dev/$dev /mnt 2>/dev/null; then
                                if [ -d /mnt/livecd-config ]; then
                                    devs_normal="$devs_normal$dev "
                                    def_dev="$dev"
                                    echo -n "+$dev "
                                else
                                    echo -n "$dev "
                                fi
                                umount /mnt
                            else
                                echo -n "$dev "
                            fi
                            ;;
                    esac
                done
            done

            # show result of scanning
            # then prompt to user
            #
            if [ "${devs_normal}${devs_crypto}" = '  ' ]; then
                echo
                echo 'No device available for saving data'
                break
            else
                echo
                dev=`rl_wread "target device" "$def_dev" ${devs_normal}${devs_crypto}`
                if [ 0 -lt `expr "$devs_normal" : ".* $dev "` ]; then
                    devname=/dev/$dev
                    set -- ${devs_normal}${devs_crypto}
                    devnames="$@"  # trim heading/trailing spaces
                    break
                elif [ 0 -lt `expr "$devs_crypto" : ".* $dev "` ]; then
                    bioctl -c C -l /dev/$dev softraid0
                    sleep 2  # wait for kernel message displayed
                else
                    break
                fi
            fi
        done
        ;;

        #-------------------
        # make new FuguIta storage device
        #
        Xnewdrive)

        echo
        if mount | grep -q '^\/dev\/[0-9a-z][0-9a-z]* on \/sysmedia type '
        then
            echo 'Please make sure the device inserted.'
            [ `wait_enter 'Then press ENTER'` -lt 1 ] && continue
        else
            echo 'Sorry, cannot find /sysmedia.  You cannot do newdrive with this boot mode.'
            continue
        fi

        echo
        echo '==== disk(s) and vnode devices  ============================'
        dmesg | sed -e '/^[sw]d[0-9][0-9]*[ :]/!d; s/> .*/>/'
        vnconfig -l
        echo '============================================================'
        newdev=`rl_wread "Enter the name of device which FuguIta will be installed" "" \`sysctl -n hw.disknames|tr :, \\\\\012|grep '^[s]d[0-9]$'\``
        if [ -z "$newdev" ]; then
            echo
            echo "newdrive: no device name"
            continue
        elif [ 0 -eq `expr "$newdev" : '^[sw]d[0-9]$'` ] && \
           [ 0 -eq `expr "$newdev" : '^vnd[0-9]*$'` ]; then
            echo
            echo "newdrive: device $newdev is not supported."
            continue
        fi

        if mount | grep -q "^/dev/$newdev"; then
            echo
            echo "newdrive: $newdev is already mounted."
            continue
        fi

        if swapctl -l | grep -q "^/dev/$newdev"; then
            echo
            echo "newdrive: $newdev is being used for swap."
            echo "          Consider 'swapctl -d' for deactivate the swap partition."
            continue
        fi

        # create vnode device file if specified
        #
        case "$newdev" in
            vnd[0-3])
                if ! vnconfig -l | grep -q "^$newdev: not in use"; then
                    echo
                    echo "newdrive: vnode device $newdev is already used."
                    continue
                fi
                vnfile=FuguIta-$(</usr/fuguita/version).img
                if [ -e "$vnfile" ]; then
                    echo
                    [ 1 -gt `ask_yn "Image file $vnfile already exists\nRemove it and proceed?" r` ] && break
                fi
                while :; do
                    echo
                    echo "Enter size of a vnode device file."
                    echo "You can add suffix K, M, G or T (otherwise considered 'bytes')."
                    ans=`rl_wread '' ''`
                    echo
                    if [ 1 -lt `expr "X$ans" : 'X[0-9][0-9]*'` ]; then
                        sect_size=$((1024*1024)) # block size for dd
                        [ `byte2sect 724M` -le `byte2sect $ans` ] && break
                        [ 1 -le `ask_yn "$ans is very small value.\nDo you really take this value?" n` ] && break
                    fi
                done

                if [ -x /usr/local/bin/pv ]; then
                    sects=`byte2sect $ans`
                    dd if=/dev/zero bs=$sect_size count=$sects | pv --size $((sect_size*sects)) > $vnfile && vnconfig $newdev $vnfile
                    result=$?
                else
                    (while :; do sleep 15; pkill -INFO dd || exit 0; done) &  # to display progress
                    dd if=/dev/zero of=$vnfile bs=$sect_size count=`byte2sect $ans` && vnconfig $newdev $vnfile
                    result=$?
                fi

                if [ 0 -lt $result ]; then
                    clear_vn $newdev $vnfile
                    continue
                fi
                ;;
        esac

        echo
        if ! fdisk $newdev; then
            clear_vn $newdev $vnfile
            continue
        fi

        if [ `fdisk $newdev | grep -c ' Unused$'` != 4 ]; then
            echo
            echo 'This disk seems to have been partitioned already.'
            if [ `ask_yn "Continue anyway?" n` -lt 1 ]; then
                clear_vn $newdev $vnfile
                continue
            fi
        fi

        # get disk parameters
        #
        eval `disk_scan $newdev`
        if [ ! "$diskscan" = ok ]; then
            echo "cannot get parameters of $newdev"
            clear_vn $newdev $vnfile
            continue
        fi
        #
        # from here, $scandev is used as target device name instead of $newdev
        #

        # disable boot items if specified
        #
        legacyitem='Legacy BIOS'
        case "$disable_legacyboot" in [Yy][Ee][Ss]) unset legacyitem;; esac
        uefiitem='UEFI'
        case "$disable_uefiboot" in [Yy][Ee][Ss]) unset uefiitem;; esac
        unset hybriditem
        [ "$legacyitem$uefiitem" = 'Legacy BIOSUEFI' ] && hybriditem='Hybrid'

        #-------------------
        # ask boot method and partition type
        #
        if [ $sects_total -lt `byte2sect 2T` ]; then
            echo
            instsys=`ask_which "Select boot method:" 'UEFI' "$legacyitem" "$uefiitem" 'none (only for save data)' "$hybriditem"`

            echo
            case $instsys in
                Legacy)
                    parttype=MBR
                    ;;
                UEFI)
                    parttype=`ask_which "Select partition type:" MBR MBR GPT`
                    ;;
                none)
                    parttype=`ask_which "Select partition type:" MBR MBR GPT Hybrid`
                    ;;
                Hybrid)
                    cat <<EOT
Notice:
  You have selected 'Hybrid' as boot type.

  This can be booted from either Legacy BIOS or UEFI.
  And has two partition tables both MBR and GPT.

  This is NOT normal configuration.
  Modifying partition table later may cause any problem.

EOT
                    if [ 1 -gt `ask_yn "Proceed anyway?" n` ]; then
                        clear_vn $newdev $vnfile
                        continue
                    fi
                    parttype=Hybrid
                    ;;
                *)
                    echo "Select one of above."
                    clear_vn $newdev $vnfile
                    continue
                    ;;
            esac
            if [ -z "$parttype" ]; then
                echo "Select one of above."
                clear_vn $newdev $vnfile
                continue
            fi
        else
            echo
            instsys=`ask_which "Select boot method:" '' "$uefiitem" 'none (only for save data)'`
            parttype=GPT
        fi

        #-------------------
        # calculate sectors
        #
        ps_hgap=$disk_head_gap
        ps_uefi=0
        ps_fisys=0
        ps_fiswp=0
        ps_fidat=0
        ps_fat=0
        case "$parttype" in
            GPT|Hybrid)
                ps_tgap=$ps_hgap ;;  # for 2nd GPT
            *)
                ps_tgap=0 ;;
        esac

        case "$instsys" in
            Legacy)
                ps_fisys=`byte2sect "${fuguita_sys_mb}M"`
                ;;
            UEFI|Hybrid)
                ps_uefi=`byte2sect "${fuguita_uefi_kb}K"`
                ps_fisys=`byte2sect "${fuguita_sys_mb}M"`
                ;;
            none)
                ;;
            *)
                echo "Select one of above."
                clear_vn $newdev $vnfile
                continue
                ;;
        esac
        ps_free=$((sects_total-(ps_hgap+ps_uefi+ps_fisys+ps_tgap)))

        # change mem-based fs?
        #
        unset memfs_new
        if [ $instsys != none ]; then
            echo
            # want prompt values with upper case
            # and its string must be lower
            memfs_new=$(ask_which 'Type of /ram:' MFS MFS TMPFS | tr A-Z a-z)
        fi

        #-------------------
        # ask and calculate User's data area
        # which is prepared as partition d
        #
        while :; do
            echo
            echo "Enter sizes for swap, user data and extra FAT."
            echo "  You can add suffix K, M, G or T (otherwise considered 'bytes')."
            echo "  '*' implies 'all'"
            echo "  '0' doesn't make this partition."
            echo
            echo "`sect2byte $ps_free` (`sect2byte $ps_free 2`) (${ps_free}sectors) free"

            ans=$(rl_wread 'swap' "$newdrive_defswap" "$newdrive_defswap")
            echo
            if [ 1 -lt `expr "X$ans" : 'X[0-9][0-9]*'` ]; then
                ps_fiswp=`byte2sect "$ans"`
                if [ $ps_free -lt $ps_fiswp ]; then
                    echo 'That size exceeds the limit.'
                    continue
                fi
            elif [ "X$ans" = 'X*' ]; then
                ps_fiswp=$ps_free
                ps_free=0
                break
            else
                echo "$ans ... What?"
                continue
            fi

            ps_free=$((ps_free-ps_fiswp))

            echo
            echo "`sect2byte $ps_free` (`sect2byte $ps_free 2`) (${ps_free}sectors) free"
            ans=`rl_wread 'user data' '*'`
            echo
            if [ 1 -lt `expr "X$ans" : 'X[0-9][0-9]*'` ]; then
                ps_fidat=`byte2sect "$ans"`
                if [ $ps_free -lt $ps_fidat ]; then
                    echo 'That size exceeds the limit.'
                    continue
                elif [ 0 -gt $ps_fidat -a $ps_fidat -lt `byte2sect 16M` ]; then
                    if [ 1 -le `ask_yn "$ans ($ps_fidat sectors) is very small value.\nDo you really take this value?" n` ]; then
                        break
                    else
                        continue
                    fi
                else
                    ps_free=$((ps_free-ps_fidat))
                fi
                break
            elif [ "X$ans" = 'X*' ]; then
                ps_fidat=$ps_free
                ps_free=0
                break
            else
                echo "$ans ... What?"
                continue
            fi
        done

        #  Is FuguIta's Data Store encrypted?
        #
        if [ 0 -lt $ps_fidat ]; then
            if [ 1 -le `ask_yn "user data encryption?" n` ]; then
                enc_fidat='yes'
                cat <<EOT

Enter passphrase twice. They'll be not echoed.

//// CAUTION ////////////////////////////
////   If you lost this passphrase,
////   you'll never access ${scandev}d.
/////////////////////////////////////////

EOT
                stty -echo; 
                echo -n 'Passphrase:'; read enc_pp1; echo
                echo -n 'Passphrase:'; read enc_pp2; echo
                stty echo
                if [ ! "$enc_pp1" = "$enc_pp2" ]; then
                    echo 'passphrase not mathed'
                    clear_vn $newdev $vnfile
                    unset enc_pp1 enc_pp2
                    continue;
                fi
                unset enc_pp2
            else
                enc_fidat='no'
            fi
        fi

        # set unused area to FAT?
        #
        if [ 0 -lt $ps_free ]; then
            echo
            if [ 1 -le `ask_yn "Create an extra FAT partition?" r` ]; then
                ps_fat=$ps_free
                ps_free=0
            fi
        fi

        echo
        showparams

        echo
        echo '***THIS IS THE LAST CHANCE***'
        echo "If you type 'Y' now, all the data on $scandev will be lost."
        if [ `ask_yn "Are you sure to modify disk ${scandev}?" n` -lt 1 ]; then
            clear_vn $newdev $vnfile
            continue
        fi

        #-------------------
        # perform disk modifications
        #
        case "$parttype" in
            MBR|GPT)
                zerofill_head
                fdisk_init $parttype
                setup_fs
                ;;
            Hybrid)
                zerofill_head
                # first, create MBR partitions including UEFI Sys (ID=EF)
                fdisk_init $parttype
                setup_fs

                # save sector 0 (MBR) to a file
                dd if=/dev/r${scandev}c of=$lockdir/hybrid.mbr bs=$sect_size count=1

                # next, create a GPT with same contents as MBR's one
                # (also UEFI bootloader installed)
                fdisk_init GPT

                # install MBR boot loader, too
                if [ "$instsys" = Hybrid \
                       -a `expr "$disable_uefiboot" : '[Yy][Ee][Ss]'` -eq 0 ]; then
                    notice "Installing UEFI boot loader"
                    if mount -t msdos -o -l /dev/${scandev}i /mnt; then
                        mkdir -p /mnt/efi/BOOT
                        cp /fuguita/usr/mdec/*.EFI /mnt/efi/BOOT
                        umount /mnt
                    fi
                fi

                # overwrite protective MBR with saved MBR
                dd if=$lockdir/hybrid.mbr of=/dev/r${scandev}c
                rm $lockdir/hybrid.mbr
                ;;
            *)
                echo "Partition type: '$parttype' - unknown" >&2
                ;;
        esac

        # site-specific post processing
        #
        [ -r /etc/fuguita/usbfadm_postproc.sh ] && . /etc/fuguita/usbfadm_postproc.sh

        if [ "$debugfile" ]; then
            { echo
              echo "////////// End of newdrive ////////////////////////////////////////"
              showparams verbose
            } >> "$debugfile"
        fi
        clear_vn $scandev  # don't remove vn img file
        ;;

        #-------------------
        # expand targeted storage partition to fill the device
        #
        Xexpand)

        echo
        if [[ -z "$devname" ]]; then
            echo "Name of device not set. Use 'target' to perform this."
            continue
        fi

        expdev=${devname#/dev/}  # expdev initially e.g. 'sd2d'
        exppar=${expdev#*[0-9]}  # partition to expand - 'd'
        expdev=${expdev%?}       # device to expand - 'sd2'

        eval $(disk_scan $expdev)  # to get sect_size for byte2sect
        [[ $diskscan != 'ok' ]] && continue

        fdisk_output=$(fdisk $expdev)

        # check partitioning types
        #
        if echo "$fdisk_output" | grep -q '^Disk: .* Usable LBA: '; then
            exptype=gpt  # Only GPT exists
        elif echo "$fdisk_output" | grep -q 'Signature: 0xAA55'; then
            if echo "$fdisk_output" | grep -q '[0123]: EF .* EFI Sys'; then
                if echo "$fdisk_output" | grep -q '[0123]: EE .* EFI GPT'; then
                    exptype=hybrid    # MBR and GPT co-exist
                else
                    exptype=mbr_uefi  # MBR with UEFI bootloader
                fi
            else
                exptype=mbr  # Only MBR exists
            fi
        else
            exptype=unknown  # random stuff
        fi

        # Get OpenBSD's partition ID
        #
        unset partid_obsd
        case "$exptype" in
            "mbr"|"mbr_uefi")
                partid_obsd=$(echo "$fdisk_output" | tr -d '*:' | awk '/A6.*OpenBSD/ {print $1}')
                fdisk_input="e ${partid_obsd}\nA6\nn\n\n*\nw\nq\n"
                ;;
            "gpt")
                partid_obsd=$(echo "$fdisk_output" | awk '/[1-9][0-9]*: OpenBSD/ {print 0+$1}')
                fdisk_input="e ${partid_obsd}\nA6\n\n*\nOpenBSD Area\nw\nq\n"
                ;;
            "hybrid")
                echo "expand: cannot modify hybrid MBR - $expdev"
                continue
                ;;
            "unknown")
                echo "expand: cannot read any partition tables - $expdev"
                continue
                ;;
        esac

        [[ -z "$partid_obsd" ]] && continue  # fail safe

        # check if another fdisk partition exists after a partition in OpenBSD
        # ... if so, cannot be expanded (may be unbootable)
        # 
        if [[ "YES" == $(echo "$fdisk_output" | awk 'BEGIN { maxstart=-1 }  # max start sector

                                                     # determine type of partition table
                                                     /geometry: [0-9]/   { tbtype = "mbr"; next }
                                                     /Usable LBA: [0-9]/ { tbtype = "gpt"; next }

                                                     # OpenBSD on MBR
                                                     tbtype == "mbr" && /^.[0123]: A6 / {
                                                         obsdstart = $11 + 0
                                                         next
                                                     }
                                                     # other on MBR
                                                     tbtype == "mbr" && /^.[0123]: [0-9A-F][0-9A-F] / {
                                                         if (maxstart < $11 + 0)
                                                             maxstart = $11 + 0
                                                         next
                                                     }

                                                     # OpenBSD on GPT
                                                     tbtype == "gpt" && $2 == "OpenBSD" {
                                                         obsdstart = $(NF-2) + 0
                                                         next
                                                     }
                                                     # other on GPT
                                                     tbtype == "gpt" && /^ [ 12][ 0-9][0-9]: / {
                                                         if (maxstart < $(NF-2) + 0)
                                                             maxstart = $(NF-2) + 0
                                                     }
                                                     END {
                                                         # exist other partition after OpenBSD?
                                                         if (obsdstart<maxstart)
                                                             print "YES"
                                                         else
                                                             print "NO"
                                                     }') ]]; then
            echo "$fdisk_output"
            cat <<EOT

There is another fdisk partition after the OpenBSD partition.
If you run expand in this state, partitions may overlap and the system may not boot.
Rather than expanding the partition on this flash device, consider using the newdrive subcommand to build your FuguIta system on another device.
EOT
            continue
        fi

        expcmd=$(ask_which "Select the expansion method for ${expdev}${exppar}:" \
                           'exit without expansion' \
                           'growfs - expands the partition while retaining its contents' \
                           'newfs - expand and format the partition' \
                           'exit without expansion')

        case "$expcmd" in
            growfs)
                echo
                echo "This expands ${expdev}${exppar} as large as possible."
                echo 'It may take time.'
                echo 'It is recommended that you make a backup of your data before proceeding'
                echo 'with the subsequent expansion process.'
                [[ $(ask_yn 'Do you proceed?' n) != 1 ]] && continue
                ;;
            newfs)
                echo
                echo "This makes ${expdev}${exppar} as large as possible."
                echo "Note that all contents in ${expdev}${exppar} will be removed"
                [[ $(ask_yn 'Do you proceed?' n) != 1 ]] && continue
                ;;
            exit)
                continue
                ;;
        esac

        echo

        # expand fdisk or GPT partition
        echo "$fdisk_input" | fdisk -e $expdev > /dev/null
        
        # expand disklabel partition
        echo "b\n\n*\nm ${exppar}\n\n*\n\nw\nq\n" | disklabel -c -E ${expdev}${exppar} > /dev/null

        # setup the filesystem
        #
        case "$expcmd" in
            growfs)
                growfs -y /dev/r${expdev}${exppar}
                fsck -fy /dev/r${expdev}${exppar}
                ;;
            newfs)
                # get number of sectors of the expaneded partition
                expsect=$(disklabel -c ${expdev} | awk "\$1 == \"${exppar}:\" { print int(0.5+\$2) }" )

                if [ $expsect -lt `byte2sect 2G` ]; then
                    # adjust inode density for little space
                    # because too many symlinks when booted mode 0
                    newfs -O 1 -m 0 -o space -i1024 -b4096 -f512 /dev/r${expdev}${exppar}
                elif [ $expsect -lt `byte2sect 4G` ]; then
                    newfs -O 2 -m 0 -o space /dev/r${expdev}${exppar}
                elif [ $expsect -lt `byte2sect 8G` ]; then
                    newfs -O 2 -m 0 /dev/r${expdev}${exppar}
                else
                    newfs -O 2 -m 0 -o time /dev/r${expdev}${exppar}
                fi

                fsck -fy /dev/r${expdev}${exppar}
                if mount /dev/${expdev}${exppar} /mnt; then
                    expduid=$(dev2duid $expdev)  # get DUID after disk_scan is OK
                    mkdir -p /mnt/livecd-config/${verarch}
                    umem="75%"
                    [[ "$memfstype" = 'tmpfs' ]] && umem=0
                    cat <<EOT >/mnt/livecd-config/${verarch}/noasks
#
# noasks - parameter settings for non-interactive boot
#
# Make statements uncommented
# Then assign real values
#
#
# FuguIta system device
#${expduid:+   - Use one of two lines}
#noask_rdev=${expdev}a  # device name format
#${expduid:+noask_rdev=${expduid}.a  # DUID format}
#
# max ${memfstype} size
#noask_umem=${umem}
#
# boot mode
#noask_setup_rw_mode=3
#
# storage device
#${expduid:+   - Use one of two lines}
#noask_confdev=${expdev}${exppar}
#${expduid:+noask_confdev=${expduid}.${exppar}  # DUID format}
#
# data set name in USB flash drive
#noask_confdir=$(hostname -s)
EOT
                    umount /mnt
                fi
                ;;
            *)
                continue
                ;;
        esac
        ;;

        #-------------------
        # null command
        # ... only RET
        X)
        : # do nothing
        ;;

        #-------------------
        # other strings are invalid
        # then for help message
        #
        *)
        echo
        cat<<EOT
Interactive commands are;
    target    -  set the partition for sync, info and expand
    saveas    -  set the name of the data to be saved
    sync      -  sync the target with the current ${memfstype}
    info      -  show info about the target partition
    newdrive  -  make a new FuguIta LiveUSB
    expand    -  expand the target partition as large as possible
    bye, exit, quit
              - end of this utility

Command line options are;
    -r : redo sync non-interactively
        (must run 'sync' at interactive mode before doing this)
    -i : show info about the persistent storage
    -q : quiet mode when redo sync
    -t : trace output (pass -x to shell)
    -d : debug output for newdrive to file 'usbf.debugout'
    -h : print this help
EOT
        ;;
    esac
done

clear_exit 0
