#!/bin/bash
#
# These functions are for dealing with resizing of partitions.
# They currently work for MBR and Extended partition tables.
# THE DO NOT WORK FOR GPT.
# It is assumed that at most 1 extended partition will exist,
# with any number of logical partitions.
# Requires the sfdisk tool.
# Assumes that sfdisk's "unit: sectors" means 512 byte sectors.
#
# $1 is the name of the disk drive
# $2 is name of file to save to.
saveSfdiskPartitions() {
    local disk="$1"
    local file="$2"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $file ]] && handleError "No file to save to (${FUNCNAME[0]})\n   Args Passed: $*"
    sfdisk -d $disk 2>/dev/null > $file
    [[ ! $? -eq 0 ]] && majorDebugEcho "sfdisk failed in (${FUNCNAME[0]})"
}
# $1 is the name of the disk drive
# $2 is name of file to save to.
saveUUIDInformation() {
    local disk="$1"
    local file="$2"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $file ]] && handleError "No file to save to passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local hasgpt=0
    hasGPT "$disk"
    [[ $hasgpt -eq 0 ]] && return
    rm -f $file
    touch $file
    local diskuuid=""
    local partuuid=""
    local partfsuuid=""
    local parts=""
    local part=""
    local part_number=""
    local strtoadd=""
    local is_swap=0
    getDiskUUID "$disk"
    echo "$disk $diskuuid" >> $file
    getPartitions "$disk"
    for part in $parts; do
        getPartitionNumber "$part"
        partitionIsSwap "$part"
        [[ $is_swap -gt 0 ]] && continue
        getPartUUID "$part"
        getPartFSUUID "$part"
        [[ -n $partfsuuid ]] && strtoadd="$part $part_number:$partfsuuid"
        [[ -n $partuuid ]] &&  strtoadd="$strtoadd $part_number:$partuuid"
        echo "$strtoadd" >> $file
        strtoadd=""
    done
}
# $1 is the name of the disk drive
# $2 is name of file to restore from
restoreUUIDInformation() {
    local disk="$1"
    local file="$2"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $file ]] && handleError "No file to load from passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ ! -r $file ]] && return
    local diskuuid=""
    local partuuid=""
    local escape_disk=$(escapeItem $disk)
    local escape_part=""
    local is_swap=0
    diskuuid=$(awk "/^$escape_disk\ /{print \$2}" $file)
    [[ -n $diskuuid ]] && sgdisk -U $diskuuid $disk >/dev/null 2>&1
    [[ ! $? -eq 0 ]] && handleError "Failed to set disk guid (sgdisk -U) (${FUNCNAME[0]})\n   Args Passed: $*"
    getPartitions "$disk"
    for part in $parts; do
        partitionIsSwap "$part"
        [[ $is_swap -gt 0 ]] && continue
        escape_part=$(escapeItem $part)
        local oIFS=$IFS
        local IFS=$'\n'
        read partuuid parttype <<< $(awk "/^$escape_part\ /{printf(\"%s\n%s\",\$2,\$3)}" $file)
        IFS=$oIFS
        [[ -n $parttype ]] && sgdisk -t $parttype $disk >/dev/null 2>&1 || true
        [[ ! $? -eq 0 ]] && handleError " Failed to set partition type (sgdisk -t) (${FUNCNAME[0]})\n   Args Passed: $*"
        [[ -n $partuuid ]] && sgdisk -u $partuuid $disk >/dev/null 2>&1 || true
        [[ ! $? -eq 0 ]] && handleError "Failed to set partition guid (sgdisk -u) (${FUNCNAME[0]})\n   Args Passed: $*"
    done
}
# $1 is the name of the disk drive
# $2 is name of file to load from.
applySfdiskPartitions() {
    local disk="$1"
    local file="$2"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $file ]] && handleError "No file to receive from passed (${FUNCNAME[0]})\n   Args Passed: $*"
    sfdisk $disk < $file >/dev/null 2>&1
    [[ ! $? -eq 0 ]] && majorDebugEcho "sfdisk failed in (${FUNCNAME[0]})"
}
# $1 is the name of the disk drive
# $2 is the name of file to load from.
applySgdiskPartitions() {
    local disk="$1"
    local file="$2"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $file ]] && handleError "No file to receive from passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local escape_disk=$(escapeItem $disk)
    local diskguid=$(awk -F: "/^$escape_disk:/{print \$3}" $file)
    sgdisk -Z $disk >/dev/null 2>&1
    [[ ! $? -eq 0 ]] && handleError "Failed to restore partitions (sgdisk -Z) (${FUNCNAME[0]})\n   Args Passed: $*"
    sgdisk -U $diskguid $disk >/dev/null 2>&1
    [[ ! $? -eq 0 ]] && handleError "Failed to restore partitions (sgdisk -U) (${FUNCNAME[0]})\n   Args Passed: $*"
    local parts=""
    local part=""
    local part_number=""
    local escape_part=""
    local partstart=""
    local partend=""
    local parttype=""
    local partcode=""
    local partname=""
    local awk_part_vars=""
    getPartitions "$disk"
    for part in $parts; do
        escape_part=$(escapeItem $part)
        getParititionNumber "$part"
        awk_part_vars=$(awk -F: "/^$escape_part:/{printf(\"%d %d %d %d\",\$3,\$4,\$5,\$6)}" $file)
        read partcode partstart partend partname <<< $awk_part_vars
        parttype=$(awk -F: "/^part:$part_number:/{print \$5}" $file)
        sgdisk -n $part_number:$partstart:$partend $disk >/dev/null 2>&1
        [[ ! $? -eq 0 ]] && handleError "Failed to restore partition (sgdisk -n) (${FUNCNAME[0]})\n   Args Passed: $*"
        sgdisk -c $part_number:$partname $disk >/dev/null 2>&1
        [[ ! $? -eq 0 ]] && handleError "Failed to restore partition (sgdisk -c) (${FUNCNAME[0]})\n   Args Passed: $*"
        sgdisk -t $part_number:$parttype $disk >/dev/null 2>&1
        [[ ! $? -eq 0 ]] && handleError "Failed to restore partition (sgdisk -t) (${FUNCNAME[0]})\n   Args Passed: $*"
        sgdisk -u $part_number:$partcode $disk >/dev/null 2>&1
        [[ ! $? -eq 0 ]] && handleError "Failed to restore partition (sgdisk -u) (${FUNCNAME[0]})\n   Args Passed: $*"
    done
}
# $1 is the name of the disk drive
# $2 is name of file to load from.
restoreSfdiskPartitions() {
    local disk="$1"
    local file="$2"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $file ]] && handleError "No file to receive from passed (${FUNCNAME[0]})\n   Args Passed: $*"
    applySfdiskPartitions "$disk" "$file"
    fdisk $disk < /usr/share/fog/lib/EOFRESTOREPART >/dev/null 2>&1
    [[ ! $? -eq 0 ]] && majorDebugEcho "fdisk failed in (${FUNCNAME[0]})"
}
# $1 is the name of the disk drive
# $2 is name of file to restore from.
restoreSgdiskPartitions() {
    local disk="$1"
    local file="$2"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $file ]] && handleError "No file to restore from (${FUNCNAME[0]})\n   Args Passed: $*"
    applySgdiskPartitions "$disk" "$file"
}
# $1 is the name of the disk drive
hasExtendedPartition() {
    local disk="$1"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    sfdisk -d $disk 2>/dev/null | egrep '(Id|type)=\ *[5f]' | wc -l
    [[ ! $? -eq 0 ]] && majorDebugEcho "sfdisk failed in (${FUNCNAME[0]})"
}
# $1 is the name of the partition device (e.g. /dev/sda3)
partitionHasEBR() {
    local part="$1"
    [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local part_number=0
    local disk=""
    local parttype=""
    getDiskFromPartition "$part"
    getPartitionNumber "$part"
    getPartType "$part"
    hasEBR=0
    [[ $part_number -ge 5 ]] && hasEBR=1
    [[ $parttype == +(0x5|0xf) ]] && hasEBR=1
}
# $1 is the name of the partition device (e.g. /dev/sda3)
# $2 is the name of the file to save to (e.g. /net/dev/foo/d1p4.ebr)
saveEBR() {
    local part="$1"
    local file="$2"
    [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $file ]] && handleError "No file to receive from passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local disk=""
    getDiskFromPartition "$part"
    local table_type=""
    getPartitionTableType "$disk"
    [[ $table_type != MBR ]] && return
    local hasEBR=0
    partitionHasEBR "$part"
    [[ ! $hasEBR -gt 0 ]] && return
    dots "Saving EBR for ($part)"
    dd if=$part of=$file bs=512 count=1 >/dev/null 2>&1
    case $? in
        0)
            echo "Done"
            debugPause
            ;;
        *)
            echo "Failed"
            debugPause
            handleError "Could not backup EBR (${FUNCNAME[0]})\n   Args Passed: $*"
            ;;
    esac
}
# $1 = DriveName  (e.g. /dev/sdb)
# $2 = DriveNumber  (e.g. 1)
# $3 = ImagePath  (e.g. /net/foo)
saveAllEBRs() {
    local disk="$1"
    local disk_number="$2"
    local imagePath="$3"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local parts=""
    local part=""
    local part_number=0
    local ebrfilename=""
    getPartitions "$disk"
    for part in $parts; do
        getPartitionNumber "$part"
        EBRFileName "$imagePath" "$disk_number" "$part_number"
        saveEBR "$part" "$ebrfilename"
    done
}
# $1 is the name of the partition device (e.g. /dev/sda3)
# $2 is the name of the file to restore from (e.g. /net/foo/d1p4.ebr)
restoreEBR() {
    local part="$1"
    local file="$2"
    [[ -z $part ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $file ]] && handleError "No file to restore from passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local disk=""
    local table_type=""
    getDiskFromPartition "$part"
    getPartitionTableType "$disk"
    [[ $table_type != MBR ]] && return
    local hasEBR=0
    partitionHasEBR "$part"
    [[ ! $hasEBR -gt 0 ]] && return
    [[ ! -e $file ]] && return
    dots "Restoring EBR for ($part)"
    dd of=$part if=$file bs=512 count=1 >/dev/null 2>&1
    case $? in
        0)
            echo "Done"
            debugPause
            ;;
        *)
            echo "Failed"
            debugPause
            handleError "Could not reload EBR data (${FUNCNAME[0]})\n   Args Passed: $*"
            ;;
    esac
}
# $1 = DriveName  (e.g. /dev/sdb)
# $2 = DriveNumber  (e.g. 1)
# $3 = ImagePath  (e.g. /net/foo)
# $4 = ImagePartitionType  (e.g. all, mbr, 1, 2, 3, etc.)
restoreAllEBRs() {
    local disk="$1"
    local disk_number="$2"
    local imagePath="$3"
    local imgPartitionType="$4"
    local ebffilename=""
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $imgPartitionType ]] && handleError "No partition type passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local parts=""
    local part=""
    local part_number=0
    local ebrfilename=""
    getPartitions "$disk"
    for part in $parts; do
        getPartitionNumber "$part"
        [[ $imgPartitionType != all && $imgPartitionType != $part_number ]] && continue
        EBRFileName "$imagePath" "$disk_number" "$part_number"
        restoreEBR "$part" "$ebrfilename"
    done
    runPartprobe "$disk"
}
# $1 is the name of the partition device (e.g. /dev/sda3)
partitionIsSwap() {
    local part="$1"
    [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local fstype=""
    fsTypeSetting "$part"
    is_swap=0
    [[ $fstype == swap ]] && is_swap=1
}
# $1 is the location of the file to store uuids in
# $2 is the partition device name
saveSwapUUID() {
    local file="$1"
    local part="$2"
    [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $file ]] && handleError "No file to receive from passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local is_swap=0
    partitionIsSwap "$part"
    [[ $is_swap -eq 0 ]] && return
    local uuid=$(blkid -s UUID $2 | cut -d\" -f2)
    [[ -z $uuid ]] && return
    echo " * Saving UUID ($uuid) for ($part)"
    echo "$part $uuid" >> $file
}
# Linux swap partition strategy:
#
# Upload:
#
# In "n" mode, the empty swapUUIDFileName is created first.  Then as each
# partition is saved, if it is swap then saveSwapUUID is called.
# In "mps" and "mpa" mode, savePartition is called for each partition.
# savePartition then calles saveSwapUUID if the partition is swap.
#
# When uploading an image, the swapUUIDFileName (e.g. /images/foo/d1.original.swapuuids)
# is created.  For $imgPartitionType == "all", all swap partition UUIDs are saved.
# For $imgPartitionType == "$partnum", the partition's UUID is saved, if it is a swap partition.
# For all others, the swapUUIDFileName will not exist, or will be empty.
#
#
# Download:
#
# When downloading an image, makeAllSwapSystems will be called.
# In "n" mode this is done for those images without special configurations,
#   after normal partition restoration.
# In "mps" mode this is always done
# In "mpa" mode this is always done, for all disks.
# makeAllSwapSystems will determine using
# $imagePartitionType == "all" or == "$partnum" whether to
# process the swapUUIDFileName contents.  For each matching partition,
# mkswap is used, and the UUID is set appropriately.
#
# Relevant functions:
# swapUUIDFileName ImagePath DriveNumber
#   echos the standardized name for the UUID filename
# partitionIsSwap PartitionName
#   echos 1 or 0 if fsTypeSetting says partition is or is not a swap partition.
# makeSwapSystem SwapUUIDFileName PartitionName
#   if it finds partition in UUID file, then calls mkswap
# makeAllSwapSystems DriveName DriveNumber ImagePath ImagePartitionType
#   checks ImagePartitionType for a match before calling makeSwapSystem
# saveSwapUUID SwapUUIDFileName PartitionName
#   checks if paritionIsSwap, if so, obtains UUID and saves it
# saveAllSwapUUIDs DriveName DriveNumber ImagePath
#   checks all partitions if partitionIsSwap, calles saveSwapUUID
# savePartition:
#   calls saveSwapUUID for swap partitions
#
#
# $1 = DriveName  (e.g. /dev/sdb)
# $2 = DriveNumber  (e.g. 1)
# $3 = ImagePath  (e.g. /net/foo)
saveAllSwapUUIDs() {
    local disk="$1"
    local disk_number="$2"
    local imagePath="$3"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local swapuuidfilename=""
    swapUUIDFileName "$imagePath" "$disk_number"
    local parts=""
    local part=""
    local is_swap=0
    getPartitions "$disk"
    for part in $parts; do
        partitionIsSwap "$part"
        [[ $is_swap -eq 0 ]] && continue
        saveSwapUUID "$swapuuidfilename" "$part"
    done
}
# $1 is the location of the file uuids are stored in
# $2 is the partition device name
makeSwapSystem() {
    local file="$1"
    local part="$2"
    [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $file ]] && handleError "No file passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local uuid=""
    local option=""
    local disk=""
    getDiskFromPartition "$part"
    local parttype=0
    local hasgpt=""
    local escape_part=$(escapeItem $part)
    hasGPT "$disk"
    case $hasgpt in
        1)
            uuid=$(awk "/^$escape_part/{print \$2}" $file)
            [[ -n $uuid ]] && parttype=82
            ;;
        0)
            parttype=$(sfdisk -d $disk 2>/dev/null | awk -F[,=] "/^$escape_part/{print \$6}")
            ;;
    esac
    [[ ! $parttype -eq 82 ]] && return
    [[ -n $uuid ]] && option="-U $uuid"
    dots "Restoring swap partition"
    mkswap $option $part >/dev/null 2>&1
    case $? in
        0)
            echo "Done"
            ;;
        *)
            echo "Failed"
            debugPause
            handleError "Could not create swap on $part (${FUNCNAME[0]})\n   Args Passed: $*"
            ;;
    esac
    debugPause
}
# $1 is the partition device (e.g. /dev/sda1)
# $2 is the new desired size in 1024 (1k) blocks
# $3 is the image path (e.g. /net/dev/foo)
resizeSfdiskPartition() {
    local part="$1"
    local size="$2"
    local imagePath="$3"
    [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $size ]] && handleError "No desired size passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local disk=""
    getDiskFromPartition "$part"
    local tmp_file="/tmp/sfdisk.$$"
    local tmp_file2="/tmp/sfdisk2.$$"
    saveSfdiskPartitions "$disk" "$tmp_file"
    processSfdisk "$tmp_file" resize "$part" "$size" > "$tmp_file2"
    if [[ $ismajordebug -gt 0 ]]; then
        majorDebugEcho "Trying to fill the disk with these partitions:"
        cat $tmp_file2
        majorDebugPause
    fi
    applySfdiskPartitions "$disk" "$tmp_file2"
    local sfdiskminimumpartitionfilename=""
    sfdiskMinimumPartitionFileName "$imagePath" 1
    saveSfdiskPartitions "$disk" "$imagePath"
}
# $1 is the disk device (e.g. /dev/sda)
# $2 is the name of the original sfdisk -d output file used as a template
# $3 is the : separated list of fixed size partitions (e.g. 1:2)
#	 swap partitions are automatically added.  Empty string is
#	 ok.
fillSfdiskWithPartitions() {
    local disk="$1"
    local file="$2"
    local fixed="$3"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $file ]] && handleError "No file to use passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local disk_size=$(blockdev --getsize64 $disk | awk '{printf("%d\n",$1/1024);}')
    local tmp_file2="/tmp/sfdisk2.$$"
    processSfdisk "$file" filldisk "$disk" "$disk_size" "$fixed" > "$tmp_file2"
    if [[ $ismajordebug -gt 0 ]]; then
        majorDebugEcho "Trying to fill with the disk with these partititions:"
        cat $tmp_file2
        majorDebugPause
    fi
    [[ $? -eq 0 ]] && applySfdiskPartitions "$disk" "$tmp_file2"
    runPartprobe "$disk"
    rm -f $tmp_file2
    majorDebugEcho "Applied the preceding table."
    majorDebugShowCurrentPartitionTable "$disk" 1
    majorDebugPause
}
#
#  processSfdisk() processes the output of sfdisk -d
#  and creates a new sfdisk -d like output, applying
#  the requested action.  Read below to see the actions
#
# $1 the name of a file that is the output of sfdisk -d
# $2 is the action "resize|other?"
# $3 is the first parameter
# $4 is the second parameter
# ...
#
# actions:
# processSfdisk foo.sfdisk resize /dev/sda1 100000
#	foo.sfdisk = sfdisk -d output
#	resize = action
#	/dev/sda1 = partition to modify
#	100000 = 1024 byte blocks size to make it
#	output: new sfdisk -d like output
#
# processSfdisk foo.sfdisk move /dev/sda1 100000
#	foo.sfdisk = sfdisk -d output
#	move = action
#	/dev/sda1 = partition to modify
#	100000 = 1024 byte blocks size to move it to
#	output: new sfdisk -d like output
#
# processSfdisk foo.sfdisk filldisk /dev/sda 100000 1:3:6
#	foo.sfdisk = sfdisk -d output
#	filldisk = action
#	/dev/sda = disk to modify
#	100000 = 1024 byte blocks size of disk
#	1:3:6 = partition numbers that are fixed in size, : separated
#	output: new sfdisk -d like output
#
# example file data
# /dev/sda1 : start=	 2048, size=   204800, Id= 7, bootable
# /dev/sda2 : start=   206848, size= 50573312, Id= 7
# /dev/sda3 : start= 50780160, size=	 2048, Id=83
# /dev/sda4 : start= 50784254, size= 16322562, Id= 5
# /dev/sda5 : start= 50784256, size=  7811072, Id=83
# /dev/sda6 : start= 58597376, size=  8509440, Id=82
#
processSfdisk() {
    local data="$1"
    local action="$2"
    local target="$3"
    local size="$4"
    local fixed="$5"
    [[ -z $data ]] && handleError "No data passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $action ]] && handleError "No action passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $target ]] && handleError "Device (disk or partition) not passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $size ]] && handleError "No desired size passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local minstart=$(awk -F'[ ,]+' '/start/{if ($4) print $4}' $data | sort -n | head -1)
    local chunksize=""
    getPartBlockSize "$disk" "chunksize"
    case $osid in
        [1-2])
            [[ -z $minstart ]] && chunksize=512
            [[ -z $minstart ]] && minstart=63
            ;;
    esac
    local awkArgs="-v CHUNK_SIZE=$chunksize -v MIN_START=$minstart"
    awkArgs="$awkArgs -v action=$action -v target=$target -v sizePos=$size"
    [[ -n $fixed ]] && awkArgs="$awkArgs -v fixedList=$fixed"
    # process with external awk script
    /usr/share/fog/lib/procsfdisk.awk $awkArgs $data
}
#
# GPT Functions below
#
# $1 : device name of drive
getPartitionTableType() {
    local disk="$1"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local mbr=$(yes '' | gdisk -l $disk | awk '/^\ *MBR:/{print $2}')
    local gpt=$(yes '' | gdisk -l $disk | awk '/^\ *GPT:/{print $2}')
    local type=""
    local mbrtype=""
    local gpttype=""
    case $mbr in
        present|MBR)
            mbrtype="MBR"
            ;;
        hybrid)
            mbrtype="HYBRID"
            ;;
        protective|not)
            mbrtype=""
            ;;
    esac
    case $gpt in
        present|damaged)
            gpttype="GPT"
            ;;
        not)
            gpttype=""
            ;;
    esac
    [[ -z $gpttype && -z $mbrtype ]] && handleError "Cannot determine partition type (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -n $gpttype && -n $mbrtype ]] && table_type="$gpttype-$mbrtype"
    [[ -n $gpttype && -z $mbrtype ]] && table_type="$gpttype"
    [[ -z $gpttype && -n $mbrtype ]] && table_type="$mbrtype"
}
#
# Detect the desired partition table type,
# using the available files in imagePath, don't rely
# on the actual disk.
#
# Assumes GPT or MBR.  Uses first 8 bytes of second block
# which should hold "EFI PART".  (https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_table_header_.28LBA_1.29)
#
# $1 : imagePath   (e.g. /images/foo)
# $2 : disk number (e.g. 1)
getDesiredPartitionTableType() {
    local imagePath="$1"
    local disk_number="$2"
    [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n   Args Passed: $*"
    table_type="MBR"
    local mbrfilename=""
    MBRFileName "$imagePath" "$disk_number" "mbrfilename"
    [[ ! -r $mbrfilename ]] && return
    local tmpfile="/tmp/gptsig"
    dd skip=512 bs=1 if=$mbrfilename of=$tmpfile count=8 >/dev/null 2>&1
    touch $tmpfile
    local gptsig=$(cat $tmpfile)
    [[ $gptsig == "EFI PART" ]] && table_type="GPT"
}
# $1 : device name of drive
hasHybridMBR() {
    local disk="$1"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local mbr=$(gdisk -l $disk | awk '/^\ *MBR:/{print $2}')
    [[ $mbr == hybrid ]] && echo 1 || echo 0
}
# $1 : device name of drive
hasGPT() {
    local disk="$1"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local gpt=$(gdisk -l $disk | awk -F'[(: )]' '/GPT:/ {print $5}')
    [[ $gpt == present ]] &&  hasgpt=1
    [[ $gpt == not ]] && hasgpt=0
}
#
# Detect the partition table type, then call the correct
# resizePartition function
#
# $1 is the partition device (e.g. /dev/sda1)
# $2 is the new desired size in 1024 (1k) blocks
# $3 is the image path (e.g. /net/dev/foo)
resizePartition() {
    local part="$1"
    local size="$2"
    local imagePath="$3"
    [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $size ]] && handleError "No size passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local disk=""
    local table_type=""
    getDiskFromPartition "$part"
    getPartitionTableType "$disk"
    case $table_type in
        MBR|GPT)
            local sfdiskoriginalpartitionfilename=""
            local sfdisklegacyoriginalpartitionfilename=""
            resizeSfdiskPartition "$part" "$size" "$imagePath"
            ;;
        *)
            handleError "Unexpected partition table type: $table_type (${FUNCNAME[0]})\n   Args Passed: $*"
            ;;
    esac
    # make sure kernel knows about the changes
    runPartprobe "$disk"
}
#
# Detect the partition table type, then save all relevant
# partition information
#
# $1 : device name of the drive
# $2 : imagePath
# $3 : disk number
saveOriginalPartitions() {
    local disk="$1"
    local imagePath="$2"
    local disk_number="$3"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local table_type=""
    getPartitionTableType "$disk"
    case $table_type in
        MBR|GPT)
            local sfdiskoriginalpartitionfilename=""
            sfdiskOriginalPartitionFileName "$imagePath" "$disk_number"
            saveSfdiskPartitions "$disk" "$sfdiskoriginalpartitionfilename"
            ;;
        GPT-MBR)
            echo "Failed"
            debugPause
            runFixparts "$disk"
            dots "Retrying to save partition table"
            saveOriginalPartitions "$disk" "$imagePath" "$disk_number"
            ;;
        *)
            echo "Failed"
            debugPause
            handleError "Unexpected partition table type: $table_type (${FUNCNAME[0]})\n   Args Passed: $*"
            ;;
    esac
    runPartprobe "$disk"
}
#
# Detect the partition table type, then restore partition
# sizes, using saved partition information
#
# $1 : device name of the drive
# $2 : imagePath
# $3 : disk number
restoreOriginalPartitions() {
    local disk="$1"
    local imagePath="$2"
    local disk_number="$3"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local table_type=""
    getPartitionTableType "$disk"
    case $table_type in
        MBR|GPT)
            local sfdiskoriginalpartitionfilename=""
            local sfdisklegacyoriginalpartitionfilename=""
            local sgdiskoriginalpartitionfilename=""
            local cmdtorun='restoreSfdiskPartitions'
            sfdiskOriginalPartitionFileName "$imagePath" "$disk_number"
            sfdiskLegacyOriginalPartitionFileName "$imagePath" "$disk_number"
            sgdiskOriginalPartitionFileName "$imagePath" "$disk_number"
            local filename="$sfdiskoriginalpartitionfilename"
            [[ ! -r $filename ]] && filename="$sfdisklegacyoriginalpartitionfilename"
            [[ ! -r $filename ]] && filename="$sgdiskoriginalpartitionfilename" && cmdtorun='restoreSgdiskPartitions'
            [[ ! -r $filename ]] && handleError "Failed to find a restore file (${FUNCNAME[0]})\n   Args Passed: $*"
            $cmdtorun "$disk" "$filename"
            ;;
        *)
            handleError "Unexpected partition table type: $table_type (${FUNCNAME[0]})\n   Args Passed: $*"
            ;;
    esac
    # make sure kernel knows about the changes
    runPartprobe "$disk"
}
#
# Detect the partition table type, the fill the disk with
# the partitions, using the correct routine.
#
# $1 : the disk device (e.g. /dev/sda)
# $2 : imagePath   (e.g. /images/foo)
# $3 : disk number (e.g. 1)
fillDiskWithPartitions() {
    local disk="$1"
    local imagePath="$2"
    local disk_number="$3"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local fixed_size_file=""
    fixedSizePartitionsFileName "$imagePath" "$disk_number"
    [[ -r $fixed_size_file ]] && fixed_size_partitions=$(cat $fixed_size_file)
    local table_type=""
    getDesiredPartitionTableType "$imagePath" "$disk_number"
    local sfdiskoriginalpartitionfilename=""
    local sfdisklegacyoriginalpartitionfilename=""
    local sgdiskoriginalpartitionfilename=""
    case $table_type in
        MBR|GPT)
            sfdiskOriginalPartitionFileName "$imagePath" "$disk_number"
            sfdiskLegacyOriginalPartitionFileName "$imagePath" "$disk_number"
            sgdiskOriginalPartitionFileName "$imagePath" "$disk_number"
            local filename="$sfdiskoriginalpartitionfilename"
            local cmdtorun='fillSfdiskWithPartitions'
            [[ ! -r $filename ]] && filename="$sfdisklegacyoriginalpartitionfilename"
            [[ ! -r $filename ]] && filename="$sgdiskoriginalpartitionfilename"
            [[ $filename == $sgdiskoriginalpartitionfilename ]] && cmdtorun='fillSgdiskWithPartitions'
            [[ ! -r $filename ]] && handleError "Failed to find a restore file (${FUNCNAME[0]})\n   Args Passed: $*"
            $cmdtorun "$disk" "$filename" "$fixed_size_partitions"
            ;;
        *)
            echo "Failed"
            debugPause
            handleError "Unexpected partition table type: $table_type (${FUNCNAME[0]})\n   Args Passed: $*"
            ;;
    esac
    # make sure kernel knows about the changes
    runPartprobe "$disk"
}
#
# Check if it will be ok to call fillDiskWithPartitions
#
# $1 : the disk device (e.g. /dev/sda)
# $2 : imagePath   (e.g. /images/foo)
# $3 : disk number (e.g. 1)
fillDiskWithPartitionsIsOK() {
    local disk="$1"
    local imagePath="$2"
    local disk_number="$3"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local table_type=""
    getDesiredPartitionTableType "$imagePath" "$disk_number"
    local filename=""
    local sfdiskoriginalpartitionfilename=""
    local sfdisklegacyoriginalpartitionfilename=""
    local sgdiskoriginalpartitionfilename=""
    do_fill=1
    case $table_type in
        MBR|GPT)
            sfdiskOriginalPartitionFileName "$imagePath" "$disk_number"
            sfdiskLegacyOriginalPartitionFileName "$imagePath" "$disk_number"
            sgdiskOriginalPartitionFileName "$imagePath" "$disk_number"
            filename="$sfdiskoriginalpartitionfilename"
            [[ ! -r $filename ]] && filename="$sfdisklegacyoriginalpartitionfilename"
            [[ ! -r $filename ]] && filename="$sgdiskoriginalpartitionfilename"
            [[ ! -r $filename ]] && do_fill=0
            ;;
    esac
}
#
# Show the current partition table
#
# $1 : the disk device (e.g. /dev/sda)
# $2 : disk number (e.g. 1)
majorDebugShowCurrentPartitionTable() {
    [[ $ismajordebug -le 0 ]] && return
    local disk="$1"
    local disk_number="$2"
    [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n   Args Passed: $*"
    [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n   Args Passed: $*"
    local table_type=""
    getDesiredPartitionTableType "$imagePath" "$disk_number"
    echo "Current partition table:"
    case $table_type in
        MBR|GPT)
            sfdisk -d $disk
            ;;
    esac
}