#!/bin/bash

umask 002
export LANG=C
export USE_IDLESS=yes
export LC_ALL=C
export PATH="${PATH}":/sbin:/usr/sbin
export JQ="jq --monochrome-output --ascii-output --unbuffered --sort-keys --raw-output --exit-status "
export STMP=/tmp

ARG0="$0"
MOD="$(gbasename $ARG0 | tr -d /)"
MYDATE="$(date +'%Y%m%d-%H%M%S%z-%Z-%s-Day_%j-%A')"

export SID=$$
export SIDP=$PPID
if gtest -z "${P2V_SYS_SZ}"
then
  export P2V_SYS_SZ=104857600
  # P2V_SYS_SZ=367001600
fi
export BOOTMB_SIZE=1048576

gtest -z "${P2V_ROOT}" && export P2V_ROOT=/usr/local/src/P2V/Home/."${MOD}"

if gtest -z "${UUID}"
then
    if gtest -r "${P2V_ROOT}"/uuid
    then
        export UUID="$(cat "${P2V_ROOT}"/uuid | tail -n 1 | tr A-F a-f | tr -cd 0-9a-f)"
    else
        export UUID=0000000000000000
    fi
fi

gtest -z "${P2V_BASE}" && export P2V_BASE="${P2V_ROOT}"/"${UUID}"
gtest -z "${P2V_BOOTMB}" && export P2V_BOOTMB="${P2V_ROOT}"/bootmb

if gtest -z "${SNAP}" && gtest -e "${P2V_BASE}"/snap
then
    export SNAP="$(grep . < ${P2V_BASE}/snap | tail -n 1)"
fi

if gtest -n "${COLLECTION}"
then
    if gtest -e "${P2V_ROOT}"/coll
    then
        export COLLECTION=$(pcat "${P2V_ROOT}"/coll | tail -n 1 | tr -cd 0-9A-Za-z)
    fi
fi

if gtest -z "${FAILOVER_BASE}"
then
    if gtest -r "${P2V_ROOT}"/failover_base
    then
        export FAILOVER_BASE=$(tail -n 1 "${P2V_ROOT}"/failover_base)
    else
        export FAILOVER_BASE=/usr/local/src/P2V/Fuse
    fi
fi

if gtest -z "${TMP}"
then
    export TMP="${STMP}"
fi

function log () {
    gecho '***' -- "$@" | gfmt --prefix='*** ' 1>&2
}

# SUDO_FLAGS=" -S -n "
SUDO_FLAGS=

if TERM=dumb EDITOR=false SUDO_EDITOR=false VISUAL=false SUDO_ASKPASS=false sudo ${SUDO_FLAGS} -- true </dev/null >/dev/null 2>/dev/null
then
  # SUDO_EXEC="env TERM=dumb SUDO_ASKPASS=false sudo ${SUDO_FLAGS} --"
  SUDO_EXEC="sudo --"
else
  # SUDO_EXEC='sudo --'
  SUDO_EXEC='false --'
  log sudo fails
fi

function usage () {
    log Example 1: 'Set up environment for P2V; check that the host conforms'
    gecho 1>&2
    gecho Example 1 Usage: "${MOD}" env conform
    gecho 1>&2

    log Example 2: 'Set UUID, SERIAL, and SNAP defaults (note that SNAP can remain undefined):'
    gecho 1>&2
    gecho Example 2 Usage: "${MOD}" uuid=deadbeefdeadbeef serial=beefdeadbeefdead snap=snapX
    gecho 1>&2

    log Example 3: 'Initialise, set disk, and walk through dependencies manually:'
    gecho 1>&2
    gecho Example 3 Usage: "${MOD}" init disk=disk.json prep dmsetup qcow2 mod conf
    gecho 1>&2

    log Example 4: 'Same as above, but automatically process dependencies, and then teardown at end:'
    gecho 1>&2
    gecho Example 4 Usage: "${MOD}" disk=disk.json conf -dmsetup
    gecho 1>&2

    log Example 5: 'Setup and teardown pair, post-configuration'
    gecho 1>&2
    gecho Example 5 Usage: "${MOD}" qcow2 -dmsetup
    gecho 1>&2
    log '(Note that qcow2 implies dmsetup, so -dmsetup implies -qcow2)'

    log Example 6: 'Full example using populate directive, which derives disk-N.json automatically:'
    gecho 1>&2
    gecho Example 6 Usage: env FAILOVER_BASE=/mnt/ax/store/mugen/mpoint "${MOD}" coll=1 uuid=5 snap=142424242 populate prep qcow2
    gecho 1>&2
}

# This function parse p2v table file - input file for dmsetup util
#
# p2v table file contains number of lines in format:
# <offset> <size> linear <block device> <start position>
# to map existing block device to corresponding part of newly created block device
# or
# <offset> <size> zero
# to fill corresponding part of newly created block device with zeros
#
# All lines must be sorted by <offset> field
# If there is a gap between adjacent areas it must be filled with zeros
#
# First input parameter assumed to be an unsorted file with only 'linear' fields
# This function sorts it and adds necessary 'zero' fields to fill gaps
function p2v_table_parse()
{
    declare -a p2v_table

    # Delete non linear entries and sort lines
    sed -i '/linear/!d' $1
    sort -n $1 -o $1

    while read line; do
        p2v_table[${#p2v_table[@]}]="$line"
    done < <(cut -d' ' -f1,2 $1)

    for ((i=0;i<${#p2v_table[@]}-1;i++)); do
        read offset1 size1 <<< ${p2v_table[$i]}
        read offset2 size2 <<< ${p2v_table[$i+1]}
        if ((offset2!=offset1+size1)); then
            echo "$((offset1+size1)) $((offset2-offset1-size1)) zero"   >> $1
        fi
    done

    # Sort again - since we've added new entries
    sort -n $1 -o $1
}

# Process an anomaly.  The first argument is the return value or exit status; but
# if that is '-' then the process continues.
function anomaly () {
    status="$1"
    shift
    log "$@" 1>&2
    if echo -- foo"${status}" | grep foo-
    then
        :
    elif echo -- foo"${status}" | grep foox
    then
        (sleep 0.5; kill -TERM -"${SID}"; sleep 0.5; kill "${SID}"; sleep 0.5; kill -9 "${SID}") &
        kill -INT "${SID}"
        kill "${SID}"
        exit 2
    else
        log Anomaly exiting with status "${status}" 1>&2
        (sleep 0.5; kill -TERM -"${SID}"; sleep 0.5; kill "${SID}"; sleep 0.5; kill -9 "${SID}") &
        disown
        exit "${status}"
    fi
}

function pcat () {
    file="$1"
    shift

    if gtest -r "${file}"
    then
        gcat -- "${file}" "$@"
        return $?
    elif gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${file}"
    then
        gcat -- "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${file}" "$@"
        return $?
    elif gtest -r "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${file}"
    then
        gcat -- "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${file}" "$@"
        return $?
    elif gtest -r "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/C:/ntfsroot/Axcient/"${file}"
    then
        gcat -- "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/C:/ntfsroot/Axcient/"${file}" "$@"
        return $?
    else
        anomaly 2 "could not access file \"${file}\""
    fi
}

function jf () {
    file="$1"
    shift
    tmpf="${TMP}"/tmp."${RANDOM}-${RANDOM}-${RANDOM}"

    export LOC=''
    if gtest -r "${file}"
    then
        :
    elif gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${file}"
    then
        export LOC="${P2V_BASE}"/"${SNAP}${VARIANT}"/
    elif gtest -r "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${file}"
    then
        export LOC="${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/
    fi

    echo running ${JQ} "$@" 1>&2
    pcat "${file}" | ${JQ} "$@" > "${tmpf}"
    ES=$?

    if gtest "${ES}" '=' 0 -a -s "${tmpf}"
    then
        pcat "${tmpf}" > "${LOC}${file}"
        unset LOC
        rm -f "${tmpf}"
        return 0
    else
        rm -f "${tmpf}"

        anomaly 3 "jf returns ${ES}"
        exit "${ES}"
        return "${ES}"
    fi
}

function checkFileExist()
{
    file_path="$1"
    local res=true
    if [ ! -e "${file_path}" ]
    then
        log "could not find file \"${file_path}\""
        res=false
    else
        log "file ${file_path} exist"
    fi
    echo $res
}

function checkExport()
{
    file_export="$1"
    local flag="$2"
    local res=true
    if [[ "$flag" -eq 1 ]]; then
        hivexregedit --unsafe --export --prefix Select ${file_export} "Select"
    else
        hivexregedit --unsafe --export --prefix Select ${file_export} "\Microsoft\Windows\CurrentVersion\Run"
    fi
    if [ $? -ne 0 ]; then
        log "could not export value from file \"${file_export}\""
        res=false
    fi
    echo $res
}
#Checking files existence and the ability to perform operations on them
function preparationOperations()
{
    system_path="${P2V_BASE}/${SNAP}${VARIANT}/os.mount/${WINDIR}/System32/config/SYSTEM"
    software_path="${P2V_BASE}/${SNAP}${VARIANT}/os.mount/${WINDIR}/System32/config/SOFTWARE"
    local res=true
    if [[ $(checkFileExist "${system_path}") == false ]]; then
        res=false
        echo $res
        return
    fi
    if [[ $(checkFileExist "${software_path}") == false ]]; then
        res=false
        echo $res
        return
    fi
    if [[ $(checkExport "${system_path}" "1") == false ]]; then
        res=false
        echo $res
        return
    fi
    if [[ $(checkExport "${software_path}" "2") == false ]]; then
        res=false
        echo $res
        return
    fi
    echo $res
}

function sz () {
    img="$1"
    log "Processing sz for input ${img}"
    if gtest -b "${img}"
    then
        lsblk --bytes --nodeps --noheadings -- "${img}"
    else
        if gtest -e "${img}"
        then
            gstat --format=%s --dereference -- "${img}"
        elif gtest -e "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${img}"
        then
            gstat --format=%s --dereference -- "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${img}"
        elif gtest -e "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${img}"
        then
            gstat --format=%s --dereference -- "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${img}"
        else
            log "Could not stat ${img}"
            false
        fi
    fi

    # blockdev --getsize64
    # /sys/block/${uniq}/size
}

# Process dependencies for a given phase/directive.
function deps () {
    STATUS=0
    DEPS=""

    gtest -d "${P2V_BASE}"/"${SNAP}${VARIANT}"/ || ${SUDO_EXEC} mkdir -p "${P2V_BASE}"/"${SNAP}${VARIANT}"/ >/dev/null 2>&1
    ${SUDO_EXEC} chown -R "${USER}" "${P2V_BASE}"/ >/dev/null 2>&1

    for dep in "$@"
    do
        if echo "${DEPS}" | fgrep -- "${dep}" || fgrep -- "${dep}" "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        then
            :
        else
            STATUS="$(gexpr ${STATUS} + 1)"
            DEPS="${DEPS} ${dep}"
        fi
    done

    echo "${DEPS}" | sed 's,  , ,g' | sed 's,^ *,,' | sed 's, *$,,' | tr -d '\n'
    return "${STATUS}"
}

#---------------------------------------------
#           CP-6520. EFI Dell replace
#---------------------------------------------
TMP_MNT_EFI=

function clear_mnt_efi() {
    if [[ -z "$TMP_MNT_EFI" ]]; then
        return 0
    fi

    log "Clear EFI mnt"
    if mountpoint -q "${TMP_MNT_EFI}"; then
        if ! fusermount -uz "${TMP_MNT_EFI}"; then
            log "Cannot umount EFI mnt"
        fi
    fi

    rmdir "${TMP_MNT_EFI}"
    TMP_MNT_EFI=
}

function create_mnt_efi() {
    if [[ -n "$TMP_MNT_EFI" ]]; then
        clear_mnt_efi
    fi

    TMP_MNT_EFI=$(mktemp -d)
}

function patch_dell_efi(){
    local json_file=$1
    local loop_id=$(jq -r '.partition ."1" .live .loop' "$json_file")

    if [[ -z "$loop_id" ]]; then
        log "Cannot detect loop device for system EFI mnt"
        return 1
    fi

    if ! create_mnt_efi; then
        log "Cannot create EFI mnt"
        return 1
    fi

    # NOTE: offset=0 is hack for avoid "/dev/loopX already mounted or /tmp/XXX busy"
    if ! mount -o offset=0 $loop_id "${TMP_MNT_EFI}"; then
        log "Cannot mount $loop_id system to EFI mnt"
        clear_mnt_efi
        return 1
    fi


    if [ -d "${TMP_MNT_EFI}/EFI/Dell" ]; then
        cp -f /mnt/ax/app/bin/ax-executor-helpers/bootx64.efi "${TMP_MNT_EFI}/EFI/Boot/bootx64.efi"
        log "EFI Dell replaced, res=$?"
    else
        log "It is not Dell machine with patched EFI"
    fi

    clear_mnt_efi
}

function is_gpt(){
    local json_file=$1
    local gpt_test=$(jq -r '.disk .type' "$json_file")
    if [[ "${gpt_test}" == "gpt" ]]; then
        return 0
    fi

    return 1
}

function patch_dell() {
    local json_file=$1
    if is_gpt "$json_file"; then
        patch_dell_efi "$json_file"
    fi
}

trap clear_mnt_efi EXIT

if gtest -z "${RC}"
then
    export RC=0
fi

if gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/mem
then
    export MEM=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/mem| tr -cd 0-9)
else
    export MEM=1280
fi

if gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/hypervisor
then
    export HYPERVISOR=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/hypervisor | tr A-Z a-z | tr -cd a-z)
else
    export HYPERVISOR=kvm
fi

# Process command arguments in sequence.
while true
do
    # Set ARG to the argument currently being processed
    ARG="$1"
    if gtest -z "${ARG}"
    then
        exit "${RC}"
    fi

    # Let the UUID be specified here (as well as in the environment).  Once set, this
    # value persists in the local environment.
    if echo -- "${ARG}" | egrep -i 'uuid='
    then
        PREVUUID="${UUID}"
        export UUID="$(echo ${ARG} | sed 's,uuid=,,')"
        export P2V_BASE="${P2V_ROOT}"/"${UUID}"
        ${SUDO_EXEC} mkdir -p "${P2V_BASE}" >/dev/null 2>&1
        ${SUDO_EXEC} chown -R "${USER}" "${P2V_BASE}" >/dev/null 2>&1
        # gtest -e "${P2V_ROOT}"/uuid || ...
        echo "${UUID}" >> "${P2V_ROOT}"/uuid
        if gtest "${PREVUUID}" '!=' "${UUID}"
        then
            if gtest -e "${P2V_BASE}"/snap
            then
                export SNAP="$(grep . < ${P2V_BASE}/snap | tail -n 1)"
            else
                unset SNAP
            fi
        fi
        unset PREVUUID
    # Similarly, set SERIAL.
    elif echo -- "${ARG}" | egrep -i 'serial='
    then
        export SERIAL="$(echo ${ARG} | sed 's,serial=,,')"
    elif echo -- "${ARG}" | egrep -i 'snap='
    then
        export SNAP="$(echo ${ARG} | sed 's,snap=,,')"
        # export P2V_BASE="${P2V_ROOT}"/"${UUID}"
        ${SUDO_EXEC} mkdir -p "${P2V_BASE}"/"${SNAP}${VARIANT}"/.Attic/ >/dev/null 2>&1
        ${SUDO_EXEC} chown -R "${USER}" "${P2V_BASE}"/"${SNAP}${VARIANT}"/ >/dev/null 2>&1
        # gtest -e "${P2V_BASE}"/snap || ...
        echo "${SNAP}" >> "${P2V_BASE}"/snap
    elif echo -- "${ARG}" | egrep -i 'coll(ection)?='
    then
        if gtest -n "${COLLECTION}"
        then
            anomaly 1 "Collection already specified"
        fi

        export COLLECTION="$(echo ${ARG} | sed 's,^collection=,,' | sed 's,^coll=,,')"
        echo "${COLLECTION}" >> "${P2V_ROOT}"/coll
    elif echo -- "${ARG}" | egrep -i 'exec'
    then
        shift
        exec "${MOD}" "$@"
    # Set up the P2V environment for the whole system (not specific to a particular UUID).
    elif echo -- "${ARG}" | egrep -i 'env'
    then
        for mod in aes serpent loop dm_mod ntfs nbd fuse dm-snapshot dm-raid rm-thin-pool dm-crypt dm-mirror dm-cache
        do
            ${SUDO_EXEC} modprobe "${mod}" >/dev/null 2>&1
        done

        if gtest ${USER} '=' root
        then
            :
        else
            for GRP in disk fuse kvm sambashare libvirtd pkgsrc
            do
                if grep "^${USER}:.*:${GRP}$" /etc/group
                then
                    :
                else
                    log consider: adduser "${USER}" "${GRP}"
                fi
            done
        fi

        ${SUDO_EXEC} mkdir -p "${P2V_ROOT}"/
        ${SUDO_EXEC} chown -R "${USER}" "${P2V_ROOT}"/
        gtest -e "${P2V_ROOT}"/uuid || echo "${UUID}" >> "${P2V_ROOT}"/uuid
        log "Environment is set up"
        # rm stale
    # Check whether the system along with its state will effectively support this P2V functionality.
    elif echo -- "${ARG}" | egrep -i 'conform'
    then
        log "Checking for host support of P2V features."
        # optional: json mkntfs-uuid virt-win-reg guestfish
        for tool in losetup lsmod dmsetup dc hivexregedit qemu-nbd qemu-img kpartx ntfslabel fdisk fallocate mkfs.ntfs jq ntfsinfo ntfslabel file blockdev lsblk qemu-system-x86_64
        do
            which "${tool}" || anomaly 1 "No ${tool}"
        done

        ${SUDO_EXEC} /sbin/losetup -a || anomaly 1 "losetup does not succeed"
        ${SUDO_EXEC} /sbin/losetup -f || anomaly 1 "No free loop devices"

        # Test for the presence of /dev/loop* devices.  Most systems will populate at least devices 0-7.
        # This is just a sanity check confirming that the device nodes themselves are present within that range.
        # The actual device used may be beyond this value (the next available device is chosen).
        for i in 0 1 2 3 4 5 6 7
        do
            gtest -b /dev/loop"${i}" || gtest -b /dev/loop/"${i}" || anomaly 1 "Expecting a /dev/loop${i}"
        done

        grep ntfs /proc/filesystems || anomaly 1 "Expecting NTFS support as a module"

        gtest -d "${P2V_ROOT}" || anomaly 1 "P2V root ${P2V_ROOT} does not exist"

        ${SUDO_EXEC} /sbin/lsmod 2>/dev/null | grep '^dm' >/dev/null 2>/dev/null || anomaly 1 "device mapper not loaded"

        if gtest -d "${P2V_BASE}"/"${SNAP}${VARIANT}"/
        then
            echo conform >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        fi

        export DEP="${DEP} conform"
    # Initialise the environment and P2V process for a specific provided UUID.
    elif echo -- "${ARG}" | egrep -i 'init'
    then
        log "Initialising"
        ${SUDO_EXEC} mkdir -p "${P2V_BASE}"/"${SNAP}${VARIANT}"/.Attic/
        ${SUDO_EXEC} chown -R "${USER}" "${P2V_BASE}"/"${SNAP}${VARIANT}"/

        if gtest -e "${P2V_BASE}"/"${SNAP}${VARIANT}"/bootmb
        then
            :
        else
            gtest -e "${P2V_BOOTMB}" && linstall "${P2V_BOOTMB}" "${P2V_BASE}"/"${SNAP}${VARIANT}"/bootmb || true
        fi

        echo init >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        export DEP="${DEP} init"
    # Set bootmb (the first megabyte of the virtual disk) to point to a specific
    # location, or otherwise to point to the default location.
    elif echo -- "${ARG}" | egrep -i 'test'
    then
        touch "${P2V_BASE}"/"${SNAP}${VARIANT}"/test
    elif echo -- "${ARG}" | egrep -i 'bootmb'
    then
        if echo -- "${ARG}" | egrep -i '=' && gtest -e "$(echo ${ARG} | sed 's,bootmb=,,')"
        then
            rm -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/bootmb
            linstall "$(echo ${ARG} | sed 's,bootmb=,,')" "${P2V_BASE}"/"${SNAP}${VARIANT}"/bootmb
        else
            gtest -e "${P2V_BASE}"/"${SNAP}${VARIANT}"/bootmb || linstall "${P2V_BOOTMB}" "${P2V_BASE}"/"${SNAP}${VARIANT}"/bootmb
        fi

        SZ=$(gstat --format=%s --dereference -- "${P2V_BASE}"/"${SNAP}${VARIANT}"/bootmb)
        gtest "${SZ}" -eq "${BOOTMB_SIZE}" || anomaly 1 "Size of bootmb is ${SZ}, which is not ${BOOTMB_SIZE}"

        echo "bootmb" >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        export DEP="${DEP} bootmb"
    elif echo -- "${ARG}" | egrep -i 'vnc='
    then
        VNCP="$(echo ${ARG} | sed 's,^vnc=,,')"
        echo "${VNCP}" > "${P2V_BASE}"/"${SNAP}${VARIANT}"/vnc
    elif echo -- "${ARG}" | egrep -i 'port='
    then
        PORT="$(echo ${ARG} | sed 's,^port=,,')"
        echo "${PORT}" > "${P2V_BASE}"/"${SNAP}${VARIANT}"/port
    elif echo -- "${ARG}" | egrep -i 'mem='
    then
        export MEM="$(echo ${ARG} | sed 's,^mem=,,')"
        echo "${MEM}" > "${P2V_BASE}"/"${SNAP}${VARIANT}"/mem
    elif echo -- "${ARG}" | egrep -i 'hypervisor='
    then
        export HYPERVISOR="$(echo ${ARG} | sed 's,^hypervisor=,,')"
        echo "${HYPERVISOR}" > "${P2V_BASE}"/"${SNAP}${VARIANT}"/hypervisor
    elif echo -- "${ARG}" | egrep -i 'populate'
    then
        if grep populate "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        then
            :
        else
            DEPS="init"
            if deps $DEPS; then :; else set -- $(deps ${DEPS}) "$@"; continue; fi

            gtest -r "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/C:/ntfsroot/Axcient/info.json || gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/info.json || anomaly x "Metadata not found"

            if gtest -e "${P2V_BASE}"/"${SNAP}${VARIANT}"/info.json
            then
                :
            else
                linstall "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/C:/ntfsroot/Axcient/info.json "${P2V_BASE}"/"${SNAP}${VARIANT}"/info.json
            fi

            DB=info.json

            VS=$(pcat "${DB}" | ${JQ} ".volumes | keys" | grep .. | grep ' ' | tr -d '", ' | sort -n | tr '\n' ' ')
            rm -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/bd
            echo no >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/bd
            rm -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/diskn
            log processing vols ${VS}
            for vol in ${VS}
            do
                log processing vol $vol

                # pcat info.json | ${JQ} '.diskext | .[].extent."1".disk' # 1 1 null 0
                # pcat info.json | ${JQ} '.diskext | .[].extent."1".offset' | nl # 1-1m 2-100m 3-null 4-135m
                # pcat info.json | ${JQ} '.diskext | keys' # order of keys

                VG=$(pcat "${DB}" | ${JQ} ".volumes[${vol}].vguid" | tr -cd 0-9a-fA-F- | tr A-F a-f)
                VD=$(pcat "${DB}" | ${JQ} ".diskext.\"${VG}\".extent.\"1\".disk" | tr -cd 0-9)
                VL=$(pcat "${DB}" | ${JQ} ".diskext.\"${VG}\".mount" | tr -cd A-Znul)

                if echo "${VL}" | grep '[A-Z]'
                then
                    pcat "${DB}" | ${JQ} ".\"WMI-disk-info\" | .[] | .[].\"${VL}:\"" > "${P2V_BASE}"/"${SNAP}${VARIANT}"/corresp1
                    pcat "${DB}" | ${JQ} ".\"WMI-disk-info\" | .[] | keys" | grep .. | grep ' '  | grep Disk | grep Partition | sed 's,.*Disk #\([0-9][0-9]*\).*Partition #\([0-9][0-9]*\).*,\1-\2,' > "${P2V_BASE}"/"${SNAP}${VARIANT}"/corresp2
                    VRN=$(paste "${P2V_BASE}"/"${SNAP}${VARIANT}"/corresp1 "${P2V_BASE}"/"${SNAP}${VARIANT}"/corresp2 | grep -v null | wc -c)
                    VR=$(paste "${P2V_BASE}"/"${SNAP}${VARIANT}"/corresp1 "${P2V_BASE}"/"${SNAP}${VARIANT}"/corresp2 | grep -v null | awk '{print $2}')
                    rm -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/corresp1 "${P2V_BASE}"/"${SNAP}${VARIANT}"/corresp2

                    if gtest "${VRN}" -eq 0
                    then
                        :
                    else
                        if gtest "${VD}" '!=' "$(echo ${VR} | sed 's,-.*,,')"
                        then
                            anomaly 1 Discrepancy
                        fi
                    fi

                    VP=$(echo "${VR}" | sed 's,^[0-9][0-9]*-,,')
                else
                    VP=$(pcat "${DB}" | ${JQ} '."WMI-disk-info" | .[]' | grep '{}' | grep Disk | grep Partition | sed 's,.*Disk #\([0-9][0-9]*\).*Partition #\([0-9][0-9]*\).*,\2,')
                fi

                log VP ${VP}
                log VG ${VG}
                log VD ${VD}
                log VL ${VL}
                log VR ${VR}

                # VSY=$(pcat "${DB}" | ${JQ} ".volumes[${vol}].flag.sys" | sed 's,null,0,' | tr -cd 0-9)
                VSY=0
                if echo "${VL}" |grep C
                then
                    VSY=1
                fi
                log vol ${vol} VSY ${VSY}
                DJ="${P2V_BASE}"/"${SNAP}${VARIANT}"/disk-"${VD}".json
                echo DJ ${DJ}
                if gtest -e "${DJ}"
                then
                    :
                    log dj was there already
                else
                    echo '{}' >> "${DJ}"
                    log dj newly created
                fi

                VP=$(gexpr "${VP}" + 1)
                jf "${DJ}" ".partition.\"${VP}\".filesystem.volume.guid//=\"${VG}\""
                jf "${DJ}" ".partition.\"${VP}\".image.location//=\"${FAILOVER_BASE}/${COLLECTION}/${UUID}/${SNAP}${VARIANT}/images/{${VG}}\""

                if echo "${VSY}"| grep 1
                then
                    jf "${DJ}" ".partition.\"${VP}\".flag.sys//=${VSY}"
                fi

                echo "${VD}" >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/diskn
            done

            export DS=$(cat "${P2V_BASE}"/"${SNAP}${VARIANT}"/diskn | sort -n | uniq | tr '\n' ' ')

            for disk in ${DS}
            do
                # BL=$(pcat "${DB}" | ${JQ} ".disk.\"${disk}\".boot.location")
                DJ="${P2V_BASE}"/"${SNAP}${VARIANT}"/disk-"${disk}".json
                if echo "${disk}" | grep 0
                then
                    DN=
                else
                    DN=_"${disk}"
                fi

                jf "${DJ}" ".disk.boot.location//=\"${FAILOVER_BASE}/${COLLECTION}/${UUID}/${SNAP}${VARIANT}/C:/ntfsroot/Axcient/boot_header${DN}\"" # alt is boot_header.img
                if gtest $(pcat "${DB}" | ${JQ} ".disk.\"${disk}\".flag.boot") '=' 1
                then
                    if cat "${P2V_BASE}"/"${SNAP}${VARIANT}"/bd | grep no
                    then
                        rm -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/bd
                        echo "${disk}" > "${P2V_BASE}"/"${SNAP}${VARIANT}"/bd
                    else
                        anomaly 1 "More than one disk flagged with boot"
                    fi
                fi
            done

            DEPS="$(echo ${DS} | tr ' ' '\n' | grep . | sed 's,^,disk=disk-,' | sed 's,$,.json,' | tr '\n' ' ')"
            cat "${P2V_BASE}"/"${SNAP}${VARIANT}"/diskn | tr ' ' '\n' | uniq | grep . | sed 's,^,disk-,' | sed 's,$,.json,' >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk
            export DISK=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk 2>/dev/null | tr '\n' ' ' | sed 's,  *$,,')
            export BD=$(cat "${P2V_BASE}"/"${SNAP}${VARIANT}"/bd | head -n 1)
            if cat "${P2V_BASE}"/"${SNAP}${VARIANT}"/bd | grep no
            then
                log 1 "Boot disk not specified; defaulting to 0"
                BD=0
            fi

            DJ="${P2V_BASE}"/"${SNAP}${VARIANT}"/disk-"${BD}".json
            jf "${DJ}" ".disk.flag.boot//=1"

            echo "populate" >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
            export DEP="${DEP} populate"
            rm -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/diskn
            rm -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/bd

            # set -- ${DEPS} "$@"
        fi

    elif echo -- "${ARG}" | egrep -i 'disk='
    then
        DEPS="init"
        if deps $DEPS; then :; else set -- $(deps ${DEPS}) "$@"; continue; fi

        DISKS="$(echo ${ARG} | sed 's,^ *disk= *,,')"
        for disk in ${DISKS} ${DISK}
        do
            export VALID=0
            if echo -- "${disk}" | grep :
            then
                gtest -r "$(echo ${disk} | tr : '\n' | grep / | head -n 1)" && export VALID=1
                gtest -r "$(echo ${disk} | tr : '\n' | grep / | sed 's/,.*//' | head -n 1)" && export VALID=1
                echo -- "${disk}" |grep 'nbd:' && export VALID=1
                echo -- "${disk}" |grep 'iscsi:' && export VALID=1
                echo -- "${disk}" |grep 'npiv:' && export VALID=1
            else
                gtest -b "${disk}" -a -r "${disk}" && export VALID=1
                if gtest -r "${disk}"
                then
                    export VALID=1
                elif gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${disk}"
                then
                    export VALID=1
                    disk="${P2V_BASE}"/"${SNAP}${VARIANT}"/"${disk}"
                fi

                if echo "${disk}" | grep '^[^/]'
                then
                    gtest -r "${PWD}"/"${disk}" || anomaly 1 "Could not reach relative path \"${disk}\""
                    disk="${PWD}"/"${disk}"
                fi
            fi

            gtest "${VALID}" '=' 1 || anomaly 1 "Could not validate disk \"${disk}\""
            unset VALID
            echo "${disk}" >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk
        done

        pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk 2>/dev/null | tr ' ' '\n' | grep . | sort | uniq > "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk.
        mv -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk. "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk
        export DISK=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk 2>/dev/null | tr '\n' ' ' | sed 's,  *$,,')

    # Convert volumes from a mount point to an image if applicable.
    elif echo -- "${ARG}" | egrep -i 'prep'
    then
        gtest -z "${SERIAL}" && export SERIAL="${UUID}"

        export H="$(echo "${UUID}" "${SERIAL}" : "${SNAP}" : "$(date +%s%N)" | gsha1sum - | tr -cd 0-9A-Fa-f)"

        if gtest -e "${P2V_BASE}"/"${SNAP}${VARIANT}"/hash
        then
            export H=$(tail -n 1 "${P2V_BASE}"/"${SNAP}${VARIANT}"/hash | tr A-F a-f | tr -cd '0-9a-fA-F')
        else
            echo "${H}" >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/hash
        fi

        export DISK=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk 2>/dev/null | tr '\n' ' ' | sed 's,  *$,,')

        if grep prep "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        then
            :
        else
            export IDX=0
            echo ${DISK} | tr ' ' '\n' | while read disk
            do
                if echo "${disk}" | grep '\.json'
                then
                    BOOTMB=$(pcat "${disk}" | ${JQ} ".disk.boot.location")
                    gtest -r "${BOOTMB}" || gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${BOOTMB}" || gtest -r "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${BOOTMB}" || anomaly 1 "Cannot read \"${BOOTMB}\""
                    export t=block
                    if gtest -r "${BOOTMB}"
                    then
                        :
                    elif gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${BOOTMB}"
                    then
                        export BOOTMB="${P2V_BASE}"/"${SNAP}${VARIANT}"/"${BOOTMB}"
                    elif gtest -r "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${BOOTMB}"
                    then
                        export BOOTMB="${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${BOOTMB}"
                    fi

                    if gtest -b "${BOOTMB}"
                    then
                        if echo "${BOOTMB}" | grep '^/dev/loop'
                        then
                            t=${t}/loop
                        elif echo "${BOOTMB}" | grep '^/dev/mapper/'
                        then
                            t=${t}/mapper
                        elif echo "${BOOTMB}" | grep '^/dev/nbd'
                        then
                            t=${t}/nbd
                        else
                            :
                        fi
                    else
                        t=image
                    fi

                    jf "${disk}" ".disk.boot.type//=\"${t}\""

                    NODE="$(basename ${disk} | sed 's,\.json,,')"

                    SZ=$(sz "${BOOTMB}")
                    gtest "${SZ}" -eq "${BOOTMB_SIZE}" || log anomaly 1 Size of bootmb is ${SZ}, which is not "${BOOTMB_SIZE}" -- considered warning
                    export BOOTMB_SIZE="${SZ}"

                    jf "${disk}" ".disk.boot.size//=${SZ}"
                    jf "${disk}" ".disk.live.node//=\"${NODE}\""
                    export BOOTMB_TMP="${P2V_BASE}"/"${SNAP}${VARIANT}"/bootmb-tmp-"$(gbasename ${disk} | sed 's,\.json$,,')"-"${RANDOM}-${RANDOM}"
                    dd if=/dev/zero of="${BOOTMB_TMP}" bs=1048576 count=0 seek=1048576 2>/dev/null || anomaly 1 dd
                    dd if="${BOOTMB}" of="${BOOTMB_TMP}" bs=1048576 count=1 conv=notrunc 2>/dev/null || anomaly 1 dd

                    if fdisk -l "${BOOTMB_TMP}" 2>&1 | grep "GUID Partition Table"
                    then
                        export PRESUMED_GPT=1
                    else
                        export PRESUMED_GPT=0
                    fi

                    if fdisk -l "${BOOTMB_TMP}" 2>&1 | sed -ne '/Device.*Boot.*Start/,$p' | grep ' e[ef]  *GPT'
                    then
                        log "There appears to be a protective MBR, suggesting an underlying GPT partition."
                        NP=$(fdisk -l "${BOOTMB_TMP}" 2>&1 | sed -ne '/Device.*Boot.*Start/,$p' | grep -v 'Device.*Boot.*Start' | grep '[a-z]' | wc -l)

                        if test "${NP}" -ne 1
                        then
                            log Expected there to be exactly one GPT partition as reported by fdisk -l.
                        fi

                        export GPT_MBR_WRAPPER=1
                    else
                        export GPT_MBR_WRAPPER=0
                        log "There does not appear to be a protective MBR, so a real MBR likely exists on this drive and this is likely to be the identifying partition table (rather than GPT)"
                    fi

                    if gtest '(' "${PRESUMED_GPT}" -eq 1 ')' -a '(' "${GPT_MBR_WRAPPER}" -eq 0 ')'
                    then
                        log "Because this disk header presumes to have GPT but on the other hand contains a non-trivial MBR table, we are dealing with a particular special case"
                        export MBR_BROKEN=1
                    else
                        export MBR_BROKEN=0
                    fi

                    if gtest '(' "${PRESUMED_GPT}" -eq 1 ')' -a '(' "${GPT_MBR_WRAPPER}" -eq 1 ')'
                    then
                        jf "${disk}" ".disk.type//=\"gpt\""
                        GUID=$(sgdisk -p "${BOOTMB_TMP}" 2>&1 | grep "Disk identifier..GUID" | cut -d: -f2 | sed 's,^ *,,' | sed 's, *$,,' | tr a-f A-F | tr -cd 0-9A-F-)
                        export DT=gpt
                        jf "${disk}" ".disk.guid//=\"${GUID}\""
                        sgdisk -p "${BOOTMB_TMP}" | sed -ne '/^Number/,$p' | tail -n +2 | grep '^ *[0-9][0-9]*  *' | awk '{print $1}' | while read part
                        do
                            jf "${disk}" ".partition.\"${part}\".seen//=1"
                        done
                    else
                        jf "${disk}" ".disk.type//=\"mbr\""
                        DID=0x$(dd if="${BOOTMB_TMP}" bs=4 count=1 skip=110 2>/dev/null | god -v -t x1 | grep ' ' | awk '{print $4, $5, $2, $3}' | tr A-F a-f | tr -d ' ')
                        export DT=mbr
                        rm -f "${STMP}"/disk
                        ln -s "${BOOTMB_TMP}" "${STMP}"/disk
                        parts=$(sfdisk -l "${STMP}"/disk 2>/dev/null | sed -ne '/Device.*Boot.*Start/,$p' | tail -n +2 | grep -v Empty | awk '{print $1}' | sed 's,.*disk\([0-9][0-9]*\)$,\1,')
                        rm -f "${STMP}"/disk
                        echo ${parts} | tr ' ' '\n' | while read part
                        do
                            log "Processing part ${part}"
                            jf "${disk}" ".partition.\"${part}\".seen//=1"
                        done
                        # reconfirm with sgdisk

                        jf "${disk}" ".disk.diskid//=\"${DID}\""
                        unset parts
                    fi

                    jf "${disk}" ".disk.index//=${IDX}"
                    export IDX=$(gexpr ${IDX} + 1)

                    export TSZ=0

                    for p in $(pcat "${disk}" | ${JQ} ".partition | keys" | grep '"' | tr -d '"' | tr -d '", ' | tr '\n' ' ')
                    do
                        log processing partition "${p}" within disk "${disk}"
                        log TSZ is "${TSZ}"
                        export DP=$(pcat "${disk}" | ${JQ} ".partition.\"${p}\".image.location")
                        gtest -r "${DP}" || gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${DP}" || gtest -r "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${DP}" || anomaly x "Could not confirm location ${DP} for part ${p} disk ${disk}"
                        export t=block

                        if gtest -b "${DP}"
                        then
                            if echo "${DP}" | grep '^/dev/loop'
                            then
                                t=${t}/loop
                            elif echo "${DP}" | grep '^/dev/mapper/'
                            then
                                t=${t}/mapper
                            elif echo "${DP}" | grep '^/dev/nbd'
                            then
                                t=${t}/nbd
                            else
                                :
                            fi
                        else
                            t=image
                        fi

                        jf "${disk}" ".partition.\"${p}\".image.type//=\"${t}\""

                        if gtest "${MBR_BROKEN}" -eq 0
                        then

                            SZC=$(sz "${DP}")
                            log "DEBUG: sgdisk on -i${p} and ${BOOTMB_TMP}"
                            SZ=$(sgdisk -i"${p}" "${BOOTMB_TMP}" 2>&1 | grep "^Partition size:" | awk '{print $3}' | tr -cd 0-9)
                            log retrieved SZ before mul is "${SZ}" and SZC is "${SZC}"
                            SZ=$(gexpr "${SZ}" '*' 512)
                            gtest "${SZ}" -eq "${SZC}" || log anomaly 1 Sizes "${SZ}" '(partition table query)' and "${SZC}" '(raw query)' do not agree - considered warning for now

                            jf "${disk}" ".partition.\"${p}\".size//=${SZ}"
                            OFF=$(sgdisk -i"${p}" "${BOOTMB_TMP}" 2>&1 | grep "^First sector:" | awk '{print $3}' | tr -cd 0-9)
                            OFF=$(gexpr "${OFF}" '*' 512)
                            jf "${disk}" ".partition.\"${p}\".offset//=${OFF}"

                            NM=$(sgdisk -i"${p}" "${BOOTMB_TMP}" 2>&1 | grep "^Partition name:" | sed 's,^[^:]*: ,,' | tr -cd "A-Za-z0-9_. +@#%-")
                            jf "${disk}" ".partition.\"${p}\".label//=\"${NM}\""

                            ATR=$(sgdisk -i"${p}" "${BOOTMB_TMP}" 2>&1 | grep "^Attribute flags:" | awk '{print $3}' | tr -cd 0-9A-Fa-f)
                            jf "${disk}" ".partition.\"${p}\".flag.attribute//=\"${ATR}\""

                            if echo "${DT}" | grep gpt
                            then
                                PG=$(sgdisk -i"${p}" "${BOOTMB_TMP}" 2>&1 | grep "^Partition unique GUID:" | awk '{print $4}' | tr -cd 0-9A-Fa-f-)
                                jf "${disk}" ".partition.\"${p}\".guid//=\"${PG}\""
                            fi

                            PGT=$(sgdisk -i"${p}" "${BOOTMB_TMP}" 2>&1 | grep "^Partition GUID code:" | awk '{print $4}' | tr -cd 0-9A-Fa-f-)
                            jf "${disk}" ".partition.\"${p}\".type.guid//=\"${PGT}\""
                            PGS=$(sgdisk -i"${p}" "${BOOTMB_TMP}" 2>&1 | grep "^Partition GUID code:" | sed 's,^[^:]*: [^ ]* ,,')
                            jf "${disk}" ".partition.\"${p}\".type.string//=\"${PGS}\""
                            PGC=$(sgdisk -p "${BOOTMB_TMP}" 2>&1 | sed -ne '/^Number/,$p' | tail -n +2 | grep '^  *'"${p}"' ' | awk '{print $6}'| tr -cd 0-9A-F)
                            jf "${disk}" ".partition.\"${p}\".type.code//=\"${PGC}\""

                        else
                            SZC=$(sz "${DP}")
                            log "DEBUG: sgdisk on -i${p} and ${BOOTMB_TMP}"
                            SZS=$(fdisk -l "${BOOTMB_TMP}" 2>&1 | sed -ne '/Device.*Boot/,$p' | tail -n +2 | sed -ne "${p}"p | sed 's,^[^ ][^ ]*[ *][ *]*,,' | awk '{print $1}' | tr -cd 0-9)
                            SZE=$(fdisk -l "${BOOTMB_TMP}" 2>&1 | sed -ne '/Device.*Boot/,$p' | tail -n +2 | sed -ne "${p}"p | sed 's,^[^ ][^ ]*[ *][ *]*,,' | awk '{print $2}' | tr -cd 0-9)
                            SZ=$(gexpr "${SZE}" - "${SZS}" + 1)
                            log retrieved SZ before mul is "${SZ}" and SZC is "${SZC}"
                            SZ=$(gexpr "${SZ}" '*' 512)
                            gtest "${SZ}" -eq "${SZC}" || log anomaly 1 Sizes "${SZ}" '(partition table query)' and "${SZC}" '(raw query)' do not agree - considered warning for now

                            jf "${disk}" ".partition.\"${p}\".size//=${SZ}"
                            OFF=$(fdisk -l "${BOOTMB_TMP}" 2>&1 | sed -ne '/Device.*Boot/,$p' | tail -n +2 | sed -ne "${p}"p | sed 's,^[^ ][^ ]*[ *][ *]*,,' | awk '{print $1}' | tr -cd 0-9)
                            OFF=$(gexpr "${OFF}" '*' 512)
                            jf "${disk}" ".partition.\"${p}\".offset//=${OFF}"

                            NM=$(fdisk -l "${BOOTMB_TMP}" 2>&1 | sed -ne '/Device.*Boot/,$p' | tail -n +2 | sed -ne "${p}"p | sed 's,^[^ ][^ ]*[ *][ *]*,,' | awk '{print $5}' | tr -cd "A-Za-z0-9_. +@#%-")
                            jf "${disk}" ".partition.\"${p}\".label//=\"${NM}\""

                            jf "${disk}" ".partition.\"${p}\".flag.attribute//=\"1\""

                            PGT=$(sgdisk -i"${p}" "${BOOTMB_TMP}" 2>&1 | grep "^Partition GUID code:" | awk '{print $4}' | tr -cd 0-9A-Fa-f-)
                            jf "${disk}" ".partition.\"${p}\".type.guid//=\"x\""
                            PGS=$(fdisk -l "${BOOTMB_TMP}" 2>&1 | sed -ne '/Device.*Boot/,$p' | tail -n +2 | sed -ne "${p}"p | sed 's,^[^ ][^ ]*[ *][ *]*,,' | awk '{print $4}' | tr -cd "0-9a-fA-F")
                            jf "${disk}" ".partition.\"${p}\".type.string//=\"${PGS}\""
                            jf "${disk}" ".partition.\"${p}\".type.code//=\"${PGS}\""

                        fi
                        # export TSZ=$(gexpr "${TSZ}" + $(${JQ} ".partition.\"${p}\".size"))
                        # log TSZ is now "${TSZ}"
                    done

                    PT_LOC=$(pcat "${disk}" | ${JQ} ".disk.boot.location")
		    VNTFS_IMG=$(dirname ${PT_LOC})/vntfs.img	
		    TSZ=$(sz "${VNTFS_IMG}")
                    log TSZ is "${TSZ}"
                    export TSZ=${TSZ}
                    jf "${disk}" ".disk.size//=${TSZ}" # compare

                    rm -f "${BOOTMB_TMP}"
                    jf "${disk}" ".disk.live.shadow//=\"${NODE}.qcow2\""
                fi
            done

            log "Writing prep state"
        fi

        echo "prep" >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        export DEP="${DEP} prep"
    # After the state has been properly set up, this repeatable phase will prepare the images
    # so that they can be mounted and used by a virtual machine subsystem.  The opposite directive
    # begins with a hyphen, and effectively unmounts the images (see below).
    elif echo -- -- "${ARG}" | egrep -i ' dmsetup'
    then
        if grep dmsetup "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        then
            :
        else
            DEPS="prep" # bootmb
            if deps $DEPS; then :; else set -- $(deps ${DEPS}) "$@"; continue; fi

            if gtest -e "${P2V_BASE}"/"${SNAP}${VARIANT}"/node
            then
                if gtest -n "${N}"
                then
                    if gtest "${N}" '!=' $(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/node | tr -d '\n')
                    then
                        anomaly 1 "node differs: ${N} vs $(pcat ${P2V_BASE}/${SNAP}${VARIANT}/node)"
                    fi
                else
                    export N=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/node | tr -d '\n')
                fi
            else
                export N=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/hash | tr -cd 0-9A-Fa-f | tr A-F a-f | tail -c 16)
                  # -"$(echo ${MYDATE} | gsha1sum - | tr -cd 0-9A-Fa-f | tr A-F a-f | tail -c 8)"
            fi

            export DISK=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk 2>/dev/null | tr '\n' ' ' | sed 's,  *$,,')
            for disk in ${DISK}
            do
                if echo "${disk}" | grep '\.json *$'
                then
                    gtest -r "${disk}" || gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${disk}" || gtest -r "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${disk}" || anomaly 1 "Could not read JSON file ${disk}"

                    BOOTMB=$(pcat "${disk}" | ${JQ} ".disk.boot.location")
                    gtest -r "${BOOTMB}" || gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${BOOTMB}" || gtest -r "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${BOOTMB}" || anomaly 1 "Could not read boot"

                    if gtest -b "${BOOTMB}"
                    then
                        export LOOP="${BOOTMB}"
                    else
                        if gtest -r "${BOOTMB}"
                        then
                            :
                        elif gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${BOOTMB}"
                        then
                            export BOOTMB="${P2V_BASE}"/"${SNAP}${VARIANT}"/"${BOOTMB}"
                        elif gtest -r "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${BOOTMB}"
                        then
                            export BOOTMB="${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${BOOTMB}"
                        fi

                        LOOP=`${SUDO_EXEC} losetup -f --show "${BOOTMB}"` || anomaly 1 "Could not associate loop"
                        export LOOP
                    fi

                    jf "${disk}" ".disk.boot.live.loop=\"${LOOP}\""
                    NODE=p2v-"${N}"-"$(pcat ${disk} | ${JQ} .disk.live.node)"
                    P2V_TABLE_PATH="${P2V_BASE}"/"${SNAP}${VARIANT}"/table-"${NODE}"
                    mv -f "${P2V_TABLE_PATH}" "${P2V_BASE}"/"${SNAP}${VARIANT}"/.Attic/table-"${NODE}-prev-${RANDOM}-${RANDOM}" >/dev/null 2>&1
                    export DBSZ=$(pcat "${disk}" | ${JQ} ".disk.boot.size" | tr -cd 0-9)
                    export OFF=$(gexpr "${DBSZ}" / 512)
                    echo 0 "${OFF}" linear "${LOOP}" 0 >> "${P2V_TABLE_PATH}"

                    if gtest $(pcat "${disk}" | ${JQ} ".partition.\"1\".offset") -ne "${DBSZ}"
                    then
                        export OFF=$(gexpr $(pcat "${disk}" | ${JQ} ".partition.\"1\".offset") / 512)
                        if gtest "${OFF}" -lt $(gexpr "${DBSZ}" / 512)
                        then
                            anomaly 1 "Provided offset ${OFF} is less than $(gexpr "${DBSZ}" / 512) sectors"
                        fi
                    fi

                    for p in $(pcat "${disk}" | ${JQ} ".partition | keys" | grep '"' | tr -d '"' | tr -d '", ' | tr '\n' ' ')
                    do
                        RAWSRC=$(pcat "${disk}" | ${JQ} ".partition.\"${p}\".image.location")
                        gtest -r "${RAWSRC}" || gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${RAWSRC}" || gtest -r "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${RAWSRC}" || anomaly 1 "Could not access raw or block source ${RAWSRC}"
                        SZ=$(gexpr $(pcat "${disk}" | ${JQ} ".partition.\"${p}\".size") / 512)

                        if pcat "${disk}" | ${JQ} ".partition.\"${p}\".offset" | grep '^[0-9]*$'
                        then
                            if gtest $(gexpr $(pcat "${disk}" | ${JQ} ".partition.\"${p}\".offset") / 512) -ne "${OFF}"
                            then
                                log "Inconsistent or non-contiguous specification in partition sizes/offsets - ${OFF} compared to:" $(pcat "${disk}" | ${JQ} ".partition.\"${p}\".offset")
                                OFF=$(gexpr $(pcat "${disk}" | ${JQ} ".partition.\"${p}\".offset") / 512)
                            fi
                        fi

                        if gtest -b "${RAWSRC}"
                        then
                            export LOOP="${RAWSRC}"
                            gtest -r "${RAWSRC}" || anomaly 1 "Could not read \"${RAWSRC}\""
                        else
                            if gtest -r "${RAWSRC}"
                            then
                                :
                            elif gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${RAWSRC}"
                            then
                                export RAWSRC="${P2V_BASE}"/"${SNAP}${VARIANT}"/"${RAWSRC}"
                            elif gtest -r "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${RAWSRC}"
                            then
                                export RAWSRC="${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${RAWSRC}"
                            fi

                            LOOP=`${SUDO_EXEC} losetup -f --show "${RAWSRC}"` || anomaly 1 "losetup fails"
                            export LOOP
                        fi

                        jf "${disk}" ".partition.\"${p}\".live.loop=\"${LOOP}\""
                        echo "${OFF}" "${SZ}" linear "${LOOP}" 0 >> "${P2V_TABLE_PATH}"
                        export OFF=$(gexpr ${OFF} + ${SZ})
                    done
                    p2v_table_parse "${P2V_TABLE_PATH}"
                    ${SUDO_EXEC} dmsetup create "${NODE}" "${P2V_TABLE_PATH}"

                    unset OFF
                    unset NODE
                    unset LOOP
                fi
            done

            echo "dmsetup" >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
            export DEP="${DEP} dmsetup"

            (sync &)
            echo "${N}" > "${P2V_BASE}"/"${SNAP}${VARIANT}"/node
        fi

    # Effectively unmount the images.  Assumes that they are not currently being read
    # from and written to.
    elif echo -- -- "${ARG}" | egrep -i ' -dmsetup'
    then
	if [ -f ${P2V_ROOT}/Class.reg ]; then
		rm ${P2V_ROOT}/Class.reg
	fi
	if [ -f ${P2V_ROOT}/Services.reg ]; then
		rm ${P2V_ROOT}/Services.reg
	fi
        if grep "dmsetup" "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        then
            if grep "qcow2" "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep >/dev/null 2>&1
            then
                DEPS="prep -qcow2"
            else
                DEPS="prep"
            fi

            if deps $DEPS; then :; else set -- $(deps ${DEPS}) "$@"; continue; fi

            if gtest -e "${P2V_BASE}"/"${SNAP}${VARIANT}"/node
            then
                if gtest -n "${N}"
                then
                    if gtest "${N}" '!=' $(cat "${P2V_BASE}"/"${SNAP}${VARIANT}"/node | tr -d '\n')
                    then
                        anomaly 1 "node differs: ${N} vs $(cat ${P2V_BASE}/${SNAP}${VARIANT}/node)"
                    fi
                else
                    export N=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/node | tr -d '\n')
                fi
            else
                log "Node does not exist"
                unset N
            fi

            export DISK=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk 2>/dev/null | tr '\n' ' ' | sed 's,  *$,,')
            for disk in ${DISK}
            do
                if echo "${disk}" | grep '\.json *$'
                then
                    gtest -r "${disk}" || gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${disk}" || gtest -r "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${disk}" || anomaly 1 "Could not read JSON file ${disk}"

                    NODE=$(pcat "${disk}" | ${JQ} ".disk.live.node")
                    if gtest -e /dev/mapper/p2v-"${N}"-"${NODE}"
                    then
                        ${SUDO_EXEC} dmsetup remove p2v-"${N}"-"${NODE}" || anomaly 1 "dmsetup remove fails for node \"${NODE}\""

                        for p in $(pcat "${disk}" | ${JQ} '.partition | keys' | grep '"' | tr -d '"' | tr -d '", ' | tr '\n' ' ')
                        do
                            export LOOP=$(pcat "${disk}" | ${JQ} ".partition.\"${p}\".live.loop")
                            gtest -b "${LOOP}" || anomaly 1 "Could not validate loop ${LOOP}"
                            if echo "${LOOP}" | grep '^/dev/loop'
                            then
                                ${SUDO_EXEC} losetup -d "${LOOP}" || anomaly 1 "losetup-d fails"
                            fi
                            jf "${disk}" "del(.partition.\"${p}\".live.loop)"
                        done

                        export LOOP=$(pcat "${disk}" | ${JQ} ".disk.boot.live.loop")
                        gtest -b "${LOOP}" || anomaly 1 "Could not validate loop ${LOOP}"
                        if echo "${LOOP}" | grep '^/dev/loop'
                        then
                            ${SUDO_EXEC} losetup -d "${LOOP}" || anomaly 1 "losetup-d fails"
                        fi
                        jf "${disk}" "del(.disk.boot.live.loop)"

                        mv -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/table-p2v-"${N}"-"${NODE}" "${P2V_BASE}"/"${SNAP}${VARIANT}"/.Attic/table-p2v-"${N}"-"${NODE}"-OFF-"${RANDOM}"-"${RANDOM}"
                    else
                        log "/dev/mapper/p2v-${N}-${NODE} not present; assuming it (along with its dependents) has been otherwise cleaned up"
                        for p in $(pcat "${disk}" | ${JQ} '.partition | keys' | grep '"' | tr -d '"' | tr -d '", ' | tr '\n' ' ')
                        do
                            export LOOP=$(pcat "${disk}" | ${JQ} ".partition.\"${p}\".live.loop")
                            jf "${disk}" "del(.partition.\"${p}\".live.loop)"
                        done

                        jf "${disk}" "del(.disk.boot.live.loop)"
                        mv -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/table-p2v-"${N}"-"${NODE}" "${P2V_BASE}"/"${SNAP}${VARIANT}"/.Attic/table-externally-p2v-"${N}"-"${NODE}"-OFF-"${RANDOM}"-"${RANDOM}"
                    fi
                fi
            done

            # rm -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/node
            unset N
            unset LOOP
            pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep | sed 's,dmsetup, ,g' | sed 's,^ *,,' | sed 's, *$,,' > "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep. && mv -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep. "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
            export DEP="$(echo ${DEP} | sed 's,dmsetup, ,g' | sed 's,^ *,,' | sed 's, *$,,')"
        else
            :
        fi

    # Further setup the device as a QCOW2 image which is mapped as a delta from the
    # raw device (with local changes).  Further, set up a NBD serving this image,
    # and offer a per-partition device view through kpartx.
    elif echo -- -- "${ARG}" | egrep -i ' qcow2'
    then
        modprobe nbd
        if grep qcow2 "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        then
            :
        else
            DEPS="dmsetup"
            if deps $DEPS; then :; else set -- $(deps ${DEPS}) "$@"; continue; fi

            if gtest -e "${P2V_BASE}"/"${SNAP}${VARIANT}"/node
            then
                if gtest -n "${N}"
                then
                    sleep 1
                    sync
                    if gtest "${N}" '!=' $(cat "${P2V_BASE}"/"${SNAP}${VARIANT}"/node | tr -d '\n')
                    then
                        :
                        # log 1 "node differs from .${N}."
                    fi
                else
                    export N=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/node | tr -d '\n')
                fi
            fi

            if gtest -e "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${N}".qcow2 && gtest -b /dev/nbd$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/nbd 2>/dev/null) && gtest -b /dev/mapper/nbd$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/nbd)p1
            then
                :
            else
                export DISK=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk 2>/dev/null | tr '\n' ' ' | sed 's,  *$,,')
                for disk in ${DISK}
                do
                    if echo "${disk}" | grep '\.json *$'
                    then
                        gtest -r "${disk}" || gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${disk}" || gtest -r "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${disk}" || anomaly 1 "Could not read JSON file ${disk}"



                        SHADOW="${P2V_BASE}"/"${SNAP}${VARIANT}"/$(pcat "${disk}" | ${JQ} ".disk.live.shadow")
                        NODE=$(pcat "${disk}" | ${JQ} ".disk.live.node")

			# Adjust disk size to account for nbd bug with odd sector size rounding
			SIZE=$(pcat "${disk}" | ${JQ} ".disk.size")
			CLUSTERS=$(gexpr ${SIZE} / 512)
			if [ $((CLUSTERS%2)) -ne 0 ]
			then
				SIZE=$(gexpr ${SIZE} + 512)
			fi

                        if gtest -n "${SHADOW}" && gtest -r "${SHADOW}"
                        then
                            :
                        else
                            ${SUDO_EXEC} qemu-img create -f qcow2 -o backing_file=/dev/mapper/p2v-"${N}"-"${NODE}",size=${SIZE} "${SHADOW}" || anomaly 1 qemu-img
                            ${SUDO_EXEC} chown "${USER}" "${SHADOW}"
                        fi

                        export SUDO_EXEC
                        export SHADOW
                        readonly timeout_get_free_nbd=$((60*60))
                        NI=`flock -o -x -w ${timeout_get_free_nbd} /tmp/p2v_nbd.lock -c 'nbd_counter=0
                            while true
                            do
                                if gtest -e /sys/class/block/nbd"${nbd_counter}" && grep '^0$' /sys/class/block/nbd"${nbd_counter}"/size 2>&1 > /dev/null && gtest -b /dev/nbd"${nbd_counter}"
                                then
                                    $SUDO_EXEC qemu-nbd --fork --verbose --connect=/dev/nbd"${nbd_counter}" "${SHADOW}" 1>&2
                                    break
                                fi

                                if [ "$nbd_counter" -gt "255" ]
                                then
                                    nbd_counter=-2
                                    break
                                fi

                                nbd_counter=$(($nbd_counter + 1))
                            done
                            echo "$nbd_counter" ' || echo -1`

                        if [ "${NI}" -lt 0 ]; then
                              anomaly 1 "Could not find free nbd. Error: ${NI}"
                        fi

                        disown # or ()
                        ${SUDO_EXEC} kpartx -av /dev/nbd"${NI}" || anomaly 1 kpartx 2
                        jf "${disk}" ".disk.live.nbd=${NI}"

                        unset NODE
                    fi
                done

                echo "qcow2" >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
                export DEP="${DEP} qcow2"
            fi
        fi

    # Tear down the state from the 'qcow2' phase.
    elif echo -- -- "${ARG}" | egrep -i ' -qcow2'
    then
        if grep "qcow2" "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        then
            DEPS="prep"
            if deps $DEPS; then :; else set -- $(deps ${DEPS}) "$@"; continue; fi

            if gtest -e "${P2V_BASE}"/"${SNAP}${VARIANT}"/node
            then
                if gtest -n "${N}"
                then
                    if gtest "${N}" '!=' $(cat "${P2V_BASE}"/"${SNAP}${VARIANT}"/node | tr -d '\n')
                    then
                        anomaly 1 "node differs: ${N} vs $(cat ${P2V_BASE}/${SNAP}${VARIANT}/node)"
                    fi
                else
                    export N=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/node | tr -d '\n')
                fi
            fi

            export DISK=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk 2>/dev/null | tr '\n' ' ' | sed 's,  *$,,')
            for disk in ${DISK}
            do
                if echo "${disk}" | grep '\.json *$'
                then
                    gtest -r "${disk}" || gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${disk}" || gtest -r "${FAILOVER_BASE}"/"${COLLECTION}"/"${UUID}"/"${SNAP}${VARIANT}"/"${disk}" || anomaly 1 "Could not read JSON file ${disk}"

                    # We must check os.mount directory and umount it in case of it exists (Canceling render task may be the cause of such situation. CC-1708)
                    # First check: os.mount directory that this script creates while rendering server
                    if grep --quiet --no-messages "${P2V_BASE}"/"${SNAP}${VARIANT}"/os.mount /proc/mounts
                    then
                        ${SUDO_EXEC} umount "${P2V_BASE}"/"${SNAP}${VARIANT}"/os.mount/ || anomaly 1 umount
                        # we need some time to give lowntfs-3g to terminate
                        sleep 2
                        rmdir "${P2V_BASE}"/"${SNAP}${VARIANT}"/os.mount
                    fi

                    # Second check: os.mount directory that install_p2v_utils.sh script creates while rendering server
                    if grep --quiet --no-messages "${P2V_ROOT}"/os.mount /proc/mounts
                    then
                        ${SUDO_EXEC} umount "${P2V_ROOT}"/os.mount/ || anomaly 1 umount
                        # we need some time to give lowntfs-3g to terminate
                        sleep 2
                        rmdir "${P2V_BASE}"/os.mount
                    fi

                    NI=$(pcat "${disk}" | ${JQ} ".disk.live.nbd")
                    # kpartx returns SUCCESS_EXIT_CODE in any cases and we must grep error from STDERR and STDOUT to detect this tool failure
                    # if kpartx fails then probably orphaned NBD partitions are the cause and we must remove them via dmsetup
                    ${SUDO_EXEC} kpartx -dv /dev/nbd"${NI}" 2>&1 | grep --quiet --invert-match error || ! gtest -e  /dev/mapper/nbd"${NI}" || ${SUDO_EXEC} dmsetup remove /dev/mapper/nbd"${NI}"* || anomaly 1 dmsetup 3
                    sync
                    sleep 2
                    ${SUDO_EXEC} qemu-nbd --verbose --disconnect /dev/nbd"${NI}" || ! gtest -e  /dev/mapper/nbd"${NI}" || anomaly 1 qemu-nbd
                    sleep 2

                    jf "${disk}" "del(.disk.live.nbd)"

                    unset NI
                fi
            done

            pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep | sed 's,qcow2, ,g' | sed 's,^ *,,' | sed 's, *$,,' > "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep. && mv -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep. "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
            export DEP="$(echo ${DEP} | sed 's,qcow2, ,g' | sed 's,^ *,,' | sed 's, *$,,')"
        fi
    elif echo -- "${ARG}" | egrep -i 'windir='
    then
        export WINDIR="$(echo ${ARG} | sed 's,windir=,,')"
    # This phase performs local modifications to the OS/application volume (the C: drive)
    # so that the OS may be booted up properly in a virtual environment like Xen.  Since
    # this stage has "prep" as a prerequisite, this operates on an image and writes the changes
    # in place after a temporary mount.  Once the image has been modified, this state is logged
    # permanently as a property of the instance so that it is not needed to run this again.
    # However it is harmless to repeat the process.
    elif echo -- "${ARG}" | egrep -i 'mod'
    then
        if grep -i mod "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        then
            :
        else
            DEPS="qcow2"
            if deps $DEPS; then :; else set -- $(deps ${DEPS}) "$@"; continue; fi

            export BD=""
            for d in $(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk | tr '\n' ' ')
            do
                if pcat "${d}" | ${JQ} ".disk.flag.boot" | grep 1
                then
                    export BD="${d}"
                fi
            done
            if gtest -z "${BD}"
            then
                export BD=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk | tr ' ' '\n' | head -n 1)
                jf "${BD}" ".disk.flag.boot//=1"
            fi

            export NI=$(pcat "${BD}" | ${JQ} ".disk.live.nbd")
            export SP=0

            for p in $(pcat "${BD}" | ${JQ} '.partition | keys' | grep '"' | tr -d '"' | tr -d '", ' | tr '\n' ' ')
            do
                if pcat "${BD}" | ${JQ} ".partition.\"${p}\".flag.sys" | grep 1
                then
                    export SP="${p}"
                    break
                fi
            done
            gtest "${SP}" -ne 0 || anomaly 1 "No partition flagged as system"

            gtest -b /dev/nbd"${NI}" -a -b /dev/mapper/nbd"${NI}"p"${SP}" || anomaly 1 "Could not confirm active NBD association"

            patch_dell "${BD}"

            mkdir -p "${P2V_BASE}"/"${SNAP}${VARIANT}"/os.mount/
            ${SUDO_EXEC} lowntfs-3g -o ignore_case /dev/mapper/nbd"${NI}"p"${SP}" "${P2V_BASE}"/"${SNAP}${VARIANT}"/os.mount/ || anomaly 1 mount
            if [[ $(preparationOperations) == true ]]; then
                hivexregedit --unsafe --export "${P2V_BASE}"/"${SNAP}${VARIANT}"/os.mount/"${WINDIR}"/System32/config/SYSTEM ControlSet001\\services\\intelide > "${P2V_BASE}"/"${SNAP}${VARIANT}"/os.system
                perl -pi -e 'm/Start/ && m/dword/ && s,dword:00000003,dword:00000000,' "${P2V_BASE}"/"${SNAP}${VARIANT}"/os.system
                hivexregedit --unsafe --merge "${P2V_BASE}"/"${SNAP}${VARIANT}"/os.mount/"${WINDIR}"/System32/config/SYSTEM --prefix ControlSet001\\services\\intelide "${P2V_BASE}"/"${SNAP}${VARIANT}"/os.system
                ${SUDO_EXEC} umount "${P2V_BASE}"/"${SNAP}${VARIANT}"/os.mount/ || anomaly 1 umount
                rmdir "${P2V_BASE}"/"${SNAP}${VARIANT}"/os.mount
            else
                ${SUDO_EXEC} umount "${P2V_BASE}"/"${SNAP}${VARIANT}"/os.mount/ || anomaly 1 umount
                rmdir "${P2V_BASE}"/"${SNAP}${VARIANT}"/os.mount
                anomaly 2 "preparationOperations failed"
            fi
            (sync &)

            echo "mod" >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
            export DEP="${DEP} mod"
        fi
    elif echo -- "${ARG}" | egrep -i 'conf' # config configure
    then
        if grep -i config "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        then
            export DEP="${DEP} mod"
            continue
        fi

        DEPS="mod" # qcow2
        if deps $DEPS; then :; else set -- $(deps ${DEPS}) "$@"; continue; fi

        if gtest -e "${P2V_BASE}"/"${SNAP}${VARIANT}"/node
        then
            if gtest -n "${N}"
            then
                if gtest "${N}" '!=' $(cat "${P2V_BASE}"/"${SNAP}${VARIANT}"/node | tr -d '\n')
                then
                    anomaly 1 "node differs: ${N} vs $(cat ${P2V_BASE}/${SNAP}${VARIANT}/node)"
                fi
            else
                export N=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/node | tr -d '\n')
            fi
        fi

        if gtest -e  "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.conf
        then
            :
        else
            if gtest -n "${VNCP}"
            then
                if gtest -e "${P2V_BASE}"/"${SNAP}${VARIANT}"/vnc
                then
                    VNCP=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/vnc)
                else
                    VNCP="Welcome1"
                fi
            fi

            if gtest -n "${PORT}"
            then
                if gtest -e "${P2V_BASE}"/"${SNAP}${VARIANT}"/port
                then
                    PORT=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/port)
                else
                    PORT="-1"
                fi
            fi
            if gtest x"${PORT}" '=' x-1
            then
                export AUTOPORT=yes
            else
                export AUTOPORT=no
            fi

            export cpu_count=1
            export shadow_memory=$((6 + 2*$cpu_count))

            if gtest -e "${P2V_BASE}"/"${SNAP}${VARIANT}"/test
            then
                export bridge=axbrlocal0
            else
                export bridge=axbr0
            fi

            if echo "${UUID}" |egrep '^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$'
            then
                export VUUID="$(echo ${UUID} | tr a-f A-F | tr -cd 0-9A-F-)"
            else
                export VUUID="$(echo ${UUID} | openssl sha512 | tr a-f A-F | tr -cd 0-9A-F- | sed 's,^\(........\)\(....\)\(....\)\(....\)\(............\).*,\1-\2-\3-\4-\5,')"
            fi

            export hypervisor=kvm
            if gtest "${HYPERVISOR}" -eq qemu
            then
                export hypervisor=qemu
                export hypervisor_path=/usr/bin/qemu-system-x86_64
                export hypervisor_disk_prefix=h
                export hypervisor_disk_driver=ide
            else
                export hypervisor_path=/usr/bin/kvm
                export hypervisor_disk_prefix=v
                export hypervisor_disk_driver=virtio
            fi

            gtee -a "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${N}".libvirt.xml <<-EOT
	<domain type='${hypervisor}'>
	  <name>${UUID}</name>
	  <uuid>${VUUID}</uuid>
	  <memory unit='MiB'>${MEM}</memory>
	  <currentMemory unit='MiB'>${MEM}</currentMemory>
	  <vcpu placement='static'>1</vcpu>
	  <os>
	    <type arch='x86_64' machine='pc-1.1'>hvm</type>
	    <boot dev='hd'/>
	  </os>
	  <features>
	    <acpi/>
	    <apic/>
	    <pae/>
	  </features>
	  <clock offset='localtime'/>
	  <on_poweroff>destroy</on_poweroff>
	  <on_reboot>restart</on_reboot>
	  <on_crash>restart</on_crash>
	  <devices>
	    <emulator>${hypervisor_path}</emulator>
	    <controller type='usb' index='0'>
	      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
	    </controller>
	    <controller type='pci' index='0' model='pci-root'/>
	    <controller type='ide' index='0'>
	      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
	    </controller>
	    <controller type='scsi' index='0'>
	      <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
	    </controller>
	    <serial type='pty'>
	      <target port='0'/>
	    </serial>
	    <console type='pty'>
	      <target type='serial' port='0'/>
	    </console>
	    <input type='tablet' bus='usb'/>
	    <input type='mouse' bus='ps2'/>
	    <input type='keyboard' bus='ps2'/>
	    <graphics type='vnc' port='${PORT}' autoport='${AUTOPORT}' listen='0.0.0.0' passwd='${VNCP}'/>
	    <sound model='ich6'>
	      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
	    </sound>
	    <video>
	      <model type='vga' vram='9216' heads='1'/>
	      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
	    </video>
	    <memballoon model='virtio'>
	      <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
	    </memballoon>

EOT

            gtee -a "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.conf <<-EOT
		kernel = '/usr/lib/xen/boot/hvmloader'
		builder = 'hvm'
		memory = ${MEM}
		name = "${N}"
		vcpus = ${cpu_count}
		shadow_memory = ${shadow_memory}
		acpi = 1
		apic = 1
		localtime = 1
		boot = 'dc'
		sdl = 0
		vnc = "1"
		vnconsole = '1'
		vncpasswd = '${VNCP}'
		serial = 'pty'
		usbdevice = 'tablet'
		on_reboot = 'restart'
		on_crash = 'destroy'
		on_poweroff = 'destroy'
		device_model = '/usr/lib/xen/bin/qemu-dm'
EOT
            # Net

            if gtest -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/net.conf && gtest -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/virtnet.conf
            then
                pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/net.conf | gtee -a "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.conf
                pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/virtnet.conf | gtee -a "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${N}".libvirt.xml
            elif gtest -r "${P2V_BASE}"/"${SNAP}${VARIANT}"/info.json
            then
                gtee -a "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.conf <<-EOT
			vif = [
EOT

                pcat info.json | ${JQ} ".networks | .[].mac" | grep . | while read NETMAC
                do
                    if gtest "${NETMAC}" '=' 'null'
                    then
                        export NETMAC=52:54:00:25:$(echo $RANDOM $RANDOM | tr -cd 0-9 | head -c 2):$(echo $RANDOM $RANDOM | tr -cd 0-9 | head -c 2)
                        gtee -a "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.conf <<-EOT
				'type=ioemu, bridge=${bridge}',
EOT
                    else
                        export NETMAC="$(echo ${NETMAC} | tr a-f A-F | tr -cd 0-9A-F | perl -pe 's,(..),$1:,g' | sed 's,:$,,')"
                        gtee -a "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.conf <<-EOT
				'type=ioemu, bridge=${bridge}, mac=${NETMAC}',
EOT
                    fi
                    gtee -a "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${N}".libvirt.xml <<-EOT
		    <interface type='network'>
		      <mac address='${NETMAC}'/>
		      <source network='default'/>
		      <model type='rtl8139'/>
		      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
		    </interface>
EOT
                done

                gtee -a "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.conf <<-EOT
			]
EOT
            else
                echo "vif = [ type=ioemu, bridge=${bridge}, mac=00:FA:CA:DE:CA:FE ]" >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.conf
            fi

            # Disk

            gtee -a "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.conf <<-EOT
		disk = [
EOT
#			'tap:qcow2:${P2V_BASE}/${SNAP}${VARIANT}/${N}.qcow2,sda,w',
#			'phy:/dev/nbdX,sda,w',

            export DISKNUM=da
            export DISKS=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/disk 2>/dev/null | tr '\n' ' ' | sed 's,  *$,,')

            for disk in ${DISKS}
            do
                if gtest -b "${disk}" && echo "${disk}" | grep '^[^:][^:]*$'
                then
                    echo "'"'phy:'"${disk}"','s"${DISKNUM}"',w'"'," >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.conf

                elif echo "${disk}" | grep :
                then
                    echo "${disk}" | sed 's,sdX,'s"${DISKNUM}"',' >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.conf
                elif echo "${disk}" | grep -i '\.'qcow2
                then
                    echo "'"'tap:qcow2:'"${disk}"','s"${DISKNUM}"',w'"'," >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.conf
                elif echo "${disk}" | grep -i '\.json *$'
                then
                    SHADOW=$(pcat "${disk}" | ${JQ} .disk.live.shadow)
                    gtest -r "${SHADOW}" || export SHADOW="${P2V_BASE}"/"${SNAP}${VARIANT}"/"${SHADOW}"
                    gtest -r "${SHADOW}" || anomaly 1 "Could not confirm shadow association"
                    echo "	'"'tap:qcow2:'"${SHADOW}"','s"${DISKNUM}"',w'"'," >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.conf

                    gtee -a "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${N}".libvirt.xml <<-EOT
		    <disk type='file' device='disk'>
		      <driver name='qemu' type='qcow2'/>
		      <source file='${SHADOW}'/>
		      <target dev='${hypervisor_disk_prefix}${DISKNUM}' bus='${hypervisor_disk_driver}'/>
		      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
		    </disk>
EOT
                    # <target dev='${DISKNUM}' bus='scsi'/>
                    # <target dev='hda' bus='ide'/>
                fi

                export DISKNUM=$(perl -le '$disknum="'"${DISKNUM}"'"; $disknum ++; print $disknum' | tr -d '\n' | tr -cd a-z)
            done

            gtee -a "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.conf <<-EOT
		]
EOT
            gtee -a "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${N}".libvirt.xml <<-EOT
	  </devices>
	</domain>
EOT

            echo "config" >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
            export DEP="${DEP} config"
        fi
    elif echo -- "${ARG}" | egrep -i 'restate' # sync/refresh - restately property
    then
        :
    elif echo -- "${ARG}" | egrep -i 'instantiate'
    then
        DEPS="config qcow2"
        if deps $DEPS; then :; else set -- $(deps ${DEPS}) "$@"; continue; fi

        if egrep -i -- 'instantiate' "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        then
            :
        else
            if gtest -z "${N}"
            then
                export N=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/node | tr -d '\n')
            fi

            #${SUDO_EXEC} xm create "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.conf 2>&1 | tee -a "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.out
            #grep -- "Started domain ${N}" "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.out || anomaly 1 "Xen VM may not have been started properly"
            #pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.out | grep "Started domain " | tail -n 1 | sed 's,.*id=\([0-9][0-9]*\)),\1,' | tr -cd 0-9 > "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.id
            #${SUDO_EXEC} xm list | grep -- "${N}" | sed s,'^'"${N}"'  *\([0-9][0-9]*\) .*,\1,' | tr -cd 0-9 > "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.id.
            #cmp "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.id "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.id. || anomaly 1 "Could not find unique running instance"
            #rm -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.id. "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.out
            #export XID=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.id | tr -cd 0-9)

            ${SUDO_EXEC} virsh create "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${N}".libvirt.xml || anomaly 1 "Could not instantiate using virsh"
            sleep 8
            sync
            export XID=$( ${SUDO_EXEC} virsh list | grep -v 'Id.*Name.*State' | grep -v -- --------- |fgrep "${UUID}" | grep running | sed 's,^ *\([0-9][0-9]*\)  *\('"${UUID}"'\)  *.*,\1,')
            mv -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.id "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.id."${RANDOM}" 2>/dev/null >/dev/null
            echo "${XID}" >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.id

            echo "instantiate" >> "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
            export DEP="${DEP} instantiate"
        fi
    elif echo -- "${ARG}" | egrep -i 'shutdown'
    then
        if grep -- "instantiate" "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        then
            export XID=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.id | tr -cd 0-9)
            echo "${XID}" | grep '[0-9]' || anomaly 1 "Could not determine Xen ID"

            if gtest -z "${N}"
            then
                export N=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/node | tr -d '\n')
            fi

            if gtest -z "${XID}"
            then
                export XID=$(pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.id | tr -cd 0-9)
            fi

            ${SUDO_EXEC} virsh shutdown "${XID}"
            sleep 20
            ${SUDO_EXEC} virsh destroy "${XID}"
            sleep 16
            ${SUDO_EXEC} virsh list
            unset XID
            mv -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.id "${P2V_BASE}"/"${SNAP}${VARIANT}"/xen.id."${RANDOM}" 2>/dev/null >/dev/null

            # ${SUDO_EXEC} xm list | grep '^'"${N}"'  *'"${XID}"' ' || anomaly 1 "ID ${XID} on node ${N} does not seem to be active"
            # ${SUDO_EXEC} xm shutdown -w "${XID}" || anomaly 1 "Did not complete shutdown"

            pcat "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep | sed 's,instantiate, ,g' | sed 's,^ *,,' | sed 's, *$,,' > "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep. && mv -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep. "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
            export DEP="$(echo ${DEP} | sed 's,instantiate, ,g' | sed 's,^ *,,' | sed 's, *$,,')"

            shift
            set -- -dmsetup "$@"
            continue
        fi
    elif echo -- "${ARG}" | egrep -i 'destroy'
    then
        if grep -- "instantiate" "${P2V_BASE}"/"${SNAP}${VARIANT}"/dep
        then
            DEPS="shutdown" # -dmsetup
        else
            DEPS=""
        fi

        if deps $DEPS; then :; else set -- $(deps ${DEPS}) "$@"; continue; fi

        rm -f "${P2V_BASE}"/"${SNAP}${VARIANT}"/"${N}".qcow2
        yes n | mv -i "${P2V_BASE}/" "${P2V_BASE}-Attic-${RANDOM}-${RANDOM}-${PPID}-$$/"
    elif echo -- "${ARG}" | egrep -i usage
    then
        usage
    elif echo -- "${ARG}" | egrep '\.json:'
    then
        file="$(echo ${ARG} | sed 's,:.*,,' | tr -d ' ')"
        gtest -w "${file}" || anomaly 1 "Could not write to file ${file}"
        jf "${file}" "$(echo ${ARG} | sed 's,^[^:]*:,,')"
        ES=$?
        gtest "${ES}" '=' 0 || anomaly 1 "Return of transformation is nonzero ${ES}"
    else
        log "Unknown directive, plus remaining:" "$@"
        RC=2
    fi

    shift
done
