#!/bin/bash
#
# This file was automatically created by shellScriptFlatten
#
# DO NOT EDIT!
#

# Installs or upgrades software on Axia Engine.
#
# Copyright (C) 2008 - 2013, Axia Audio
#
# This program is distributed in the hope that it will be useful,
# but without any warranty; without even the implied warranty of
# merchantability or fitness for a particular purpose.
 
# ==============================================================================

# Preconditions: script executed by root

# ==============================================================================

declare -r scriptVersion=0.4.0
declare -r scriptSvnRevisionText="$Revision: 1358 $Revision"
declare -ir scriptSvnRevision="${scriptSvnRevisionText##:}"
declare -r this=$(basename "$0")

declare -ir errorGeneral=1
declare -ir errorSyntax=16
declare -ir errorPackageBootloaderTooOld=96
declare -ir errorBootloaderBadVersionId=97
declare -ir errorPackageBootloaderVersionUnknown=98
declare -ir errorPackageApplicationTooOld=100
declare -ir errorApplicationBadVersionId=101
declare -ir errorPackageApplicationVersionUnknown=102
declare -ir errorPackageProductTypeWrong=103
declare -ir errorPackageProductHardwareTypeWrong=104
declare -ir errorPackageProductCpuClassWrong=105
declare -ir errorPackageProductTypeNotSupported=106
declare -ir errorPackageProductCpuClassNotSupported=107
declare -ir errorPackageComponentSumDiscrepancy=108
declare -ir errorPackageComponentSumCalculationFailed=109
declare -ir errorPackageComponentSumRetrievalFailed=110
declare -ir errorPackageComponentDeploymentFailed=111
declare -ir errorPackageMissingNestedPackage=112
declare -ir errorPackageFormatNotSupported=113
declare -ir errorTargetTooSmall=120
declare -ir errorBootloaderNotAccessible=121

declare -ir systemCount=2
declare -ir bootloaderPartitionSizeMin=$(( 2 * 1024 * 1024 ))

# the lowest allowed percentage of disk usable capacity remaining after
# leaving free space at the end of disk to ensure safe image copying across
# different disk geometries for "same" size Compact Flash cards.
declare -ir storageUsableCapacityPercentageMin=96

# ==============================================================================

# Prints usage information
#
usagePrint() {
	cat << EOF
Usage: $this [OPTIONS] PACKAGE TARGET

Install or upgrade of software on Axia Engine.

  -h, --help               prints this message and exit.
  -e, --erase              erases TARGET completely before installation.
  -f, --full-install       performs a full install on TARGET.
  -p=SIZE, --persistent-rw-fs-size=SIZE
                           specifies size of persistent read/write partition.
  -n=NAME, --target-final-name=NAME
                           specifies name of the TARGET on the system
                           it will be used at the end instead of filesystem labels.
  -s=SIZE, --target-size=SIZE
                           specifies size of TARGET when creating image.
  -g=C:H:S, --target-geometry=C:H:S
                           specifies geometry of TARGET when creating image.
  -t=TEMPLATE_DEVICE, --target-template=TEMPLATE_DEVICE
                           specifies device whose geometry to
                           use as template of TARGET when creating image.
  -b=BOOTLOADER_DIRECTORY, --bootloader-directory=BOOTLOADER_DIRECTORY
                           specifies directory where bootloader partition is
                           mounted; this is needed for version compatibility checks.

SIZE parameters may be followed by the following multiplicative suffixes:
kB = 1000, K = 1024, MB = 1000 * 1000, M = 1024 * 1024,
GB = 1000 * 1000 * 1000, G = 1024 * 1024 * 1024

PACKAGE is an Engine software release package.

TARGET is a targed image file, partition or disk device;
- if TARGET appears to be a partition no options are expected;
- if TARGET appears to be the entire disk it must be full install and
  appropriate option (-f or --full-install) is expected,
  in the same time NONE of TARGET size specifying options
  (-s, --target-size, -g, --target-geometry, -t, --target-template)
  is expected;
- if TARGET appears to be loop device or regular file it is treated as disk image,
  full install is assumed and -f or --full-install option expected;
  in the same time ONE of TARGET size specifying options
  (-s, --target-size, -g, --target-geometry, -t, --target-template)
  is expected;
  name option of the TARGET in the final system (-n or --target-final-name)
  can be provided;

if name of the TARGET in the final system is not provided
(via options -n or --target-final-name ), filesystem labels are used
by bootloader to specify root devices (root=LABEL=ROOT_<index>)

if BOOTLOADER_DIRECTORY is not specified '/boot/grub' is assumed;

WARNING: all existing content of TARGET will be lost!
NO confirmation will be asked!


Exit codes:
0 - Success
$errorGeneral - Unspecified failure
$errorSyntax - Usage or syntax error
$errorPackageBootloaderTooOld - Version check failed: bootloader from the package is too old
$errorBootloaderBadVersionId - Version check failed: invalid bootloader version numbers encountered
$errorPackageBootloaderVersionUnknown - Version check failed: no usr/etc/bootloader-version.txt in package
$errorPackageApplicationTooOld - Version check failed: application from the package is too old
$errorApplicationBadVersionId - Version check failed: invalid application version numbers encountered
$errorPackageApplicationVersionUnknown - Version check failed: no etc/Axia/sysVersion.txt in package
$errorPackageProductTypeWrong - Version check failed: wrong package product type (e.g. PowerStation, iPort etc)
$errorPackageProductHardwareTypeWrong - Version check failed: wrong package product target hardware type (e.g. SOM-5786, SOM-5790 etc)
$errorPackageProductCpuClassWrong - Version check failed: wrong package product target cpu class (e.g. P4, Core i5 etc)
$errorPackageProductTypeNotSupported - Version check: product type not supported by package
$errorPackageProductCpuClassNotSupported - Version check: product cpu class not supported by package
$errorPackageComponentSumDiscrepancy - The calculated sha1sum of package component does not match the enclosed sha1sum check file
$errorPackageComponentSumCalculationFailed - Failed to calculate sha1sum of the component
$errorPackageComponentSumRetrievalFailed - Failed to retrieve the enclosed sha1sum check file
$errorPackageComponentDeploymentFailed - Failed to deploy package component
$errorPackageMissingNestedPackage - The nested package not found inside of format 2 package
$errorPackageFormatNotSupported - The format of package is not supported
$errorTargetTooSmall - Insufficient space on the target device
$errorBootloaderNotAccessible - Version check: bootloader partition is not accessible (probably not mounted) for verification

Report bugs to <armands@temporarius.com>
EOF
}

# Suggests to look into documentation
#
usageSuggest() {
	cat << EOF

Usage: $this [OPTIONS] PACKAGE TARGET

Try '$this --help' for more information.
EOF
}

# ------------------------------------------------------------------------------

failureReport() {
	declare -r failureReport_command=$BASH_COMMAND
	if (( ${BASH_VERSINFO[0]} > 3
			|| (${BASH_VERSINFO[0]} == 3 && ${BASH_VERSINFO[1]} > 1)
			|| (${BASH_VERSINFO[0]} == 3 && ${BASH_VERSINFO[1]} == 1 && ${BASH_VERSINFO[2]} > 17) )); then
		echo -e "\n$1: FAILURE @ $2: $failureReport_command"
	else
		echo -e "\n$1: FAILURE @ $2"
	fi
}

# ------------------------------------------------------------------------------
# . trace
# BEGINNING: trace

#
# Some debugging and testing support
#

# ==============================================================================

function try() {
	local r=0
	local caller=$(caller)
	local script=${caller#[0-9]* }
	local lineNr=${caller%% *}
	eval $@ || { 
		r=$?
		echo "$script: FAILURE @ $lineNr: $@" >&2
	}
	return $r
}

function tryVerbosely() {
	local r=0
	local caller=$(caller)
	local script=${caller#[0-9]* }
	local lineNr=${caller%% *}
	echo "   $@"
	eval $@ || { 
		r=$?
		echo "$script: FAILURE @ $lineNr: $@" >&2
	}
	return $r
}

function assert() {
	local r=0
	local caller=$(caller)
	local script=${caller#[0-9]* }
	local lineNr=${caller%% *}
	eval $@ || { 
		r=$?
		echo "$script: ASSERTION @ $lineNr FAILED: $@"
		failureCount=$(( failureCount + 1 ))
	}
	return $r
}

# EOF
# ENDING: trace
# ------------------------------------------------------------------------------

# ------------------------------------------------------------------------------
# . ../bootloader/util/diskTools
# BEGINNING: ../bootloader/util/diskTools

# Miscelaneous disk utilities
#
# Copyright (C) 2010, Axia Audio
#
# This program is distributed in the hope that it will be useful,
# but without any warranty; without even the implied warranty of
# merchantability or fitness for a particular purpose.

# converts to number of bytes size specified with multiplicative suffix
# $1 - size specifier
#
function sizeNormalize() {
	local size=`echo "$1" | sed "s/[[:space:]]//g"`
	if echo "$size" | grep -q "^[[:digit:]]\+GB$"; then
		echo $(( `echo $size | sed "s/[[:alpha:]]\+//"` * 1000 * 1000 * 1000 ))
		return
	fi
	if echo "$size" | grep -q "^[[:digit:]]\+G$"; then
		echo $(( `echo $size | sed "s/[[:alpha:]]\+//"` * 1024 * 1024 * 1024 ))
		return
	fi
	if echo "$size" | grep -q "^[[:digit:]]\+MB$"; then
		echo $(( `echo $size | sed "s/[[:alpha:]]\+//"` * 1000 * 1000 ))
		return
	fi
	if echo "$size" | grep -q "^[[:digit:]]\+M$"; then
		echo $(( `echo $size | sed "s/[[:alpha:]]\+//"` * 1024 * 1024 ))
		return
	fi
	if echo "$size" | grep -q "^[[:digit:]]\+kB$"; then
		echo $(( `echo $size | sed "s/[[:alpha:]]\+//"` * 1000 ))
		return
	fi
	if echo "$size" | grep -q "^[[:digit:]]\+K$"; then
		echo $(( `echo $size | sed "s/[[:alpha:]]\+//"` * 1024 ))
		return
	fi
	if echo "$size" | grep -q "^[[:digit:]]\+$"; then
		echo $size
		return
	fi
	echo 0
	return 1
}

# clones block or character device node
# $1 - source device node
# $2 - destination device node name
#
function nodeClone() {
	declare -a entry
	entry=( `ls -gG $1 | grep '^[bc]' | sed 's/,//g'` ) || return $?
	if [ "${entry[0]}" -a "${entry[2]}" -a "${entry[3]}" ]; then
		try mknod \"$2\" ${entry[0]:0:1} ${entry[2]} ${entry[3]} || return $?
		try chmod --reference=\"$1\" \"$2\" || return $?
		try chown --reference=\"$1\" \"$2\"
		return $?
	fi
	echo "$this: ERR: Failed to query $1!" >&2
	return 1
}

# proposes disk geometry according to the required sector count
# $1 - maximal sector count
#
function diskGeometryPropose() {
	local sectorCount=$1
	local cylinderCount=0
	local trackCount=0
	local headCount=0
	local trackSectorCount=0
	# calculating possible geometry (CHS)
	# taken from Microsoft Virtual Hard Disk Image Format Specification Version 1.0
	if [ $sectorCount -ge $(( 65535 * 16 * 63 )) ]; then
		trackSectorCount=255
		headCount=16
		trackCount=$(( $sectorCount / $trackSectorCount ))
	else
		trackSectorCount=17
		trackCount=$(( $sectorCount / $trackSectorCount ))
		headCount=$(( ($trackCount - 1) / 1024 + 1 ))
		if [ $headCount -lt 4 ]; then
			headCount=4
		fi
		if [ $trackCount -ge $(( $headCount * 1024 )) -o $headCount -gt 16 ]; then
			trackSectorCount=31
			headCount=16
			trackCount=$(( $sectorCount / $trackSectorCount ))
		fi
		if [ $trackCount -ge $(( $headCount * 1024 )) ]; then
			trackSectorCount=63
			headCount=16
			trackCount=$(( $sectorCount / $trackSectorCount ))
		fi
	fi
	cylinderCount=$(( $trackCount / $headCount ))
	if [ $cylinderCount -gt 65535 ]; then
		echo "$this: ERR: the resulting disk image is too large!" >&2
		exit 1 
	fi
	echo $cylinderCount $headCount $trackSectorCount
}

# checks if file is IDE device
# $1 - file to query
#
function fileIsIdeDevice() {
	declare deviceMajor
	deviceMajor=$(( 0x`stat -c %t $1 2> /dev/null || echo 0` ))
	declare -a majorList
	majorList=( 3 22 33 34 56 57 88 89 90 91 )
	for major in ${majorList[*]}; do
		[ $deviceMajor -eq $major ] && return 0
	done
	return 1
}

# checks if file is SCSI device
# $1 - file to query
#
function fileIsScsiDevice() {
	declare deviceMajor
	deviceMajor=$(( 0x`stat -c %t $1 2> /dev/null || echo 0` ))
	declare -a majorList
	majorList=( 8 65 66 67 68 69 70 71 128 129 130 131 132 133 134 135 )
	for major in ${majorList[*]}; do
		[ $deviceMajor -eq $major ] && return 0
	done
	return 1
}

# gets disk minor for storage device file
# $1 - file to query
#
function diskMinorGet() {
	declare deviceMinor
	deviceMinor=`stat -c %T $1 2> /dev/null` || return $?
	if fileIsIdeDevice $1; then
		echo $(( 0x${deviceMinor} & 0xC0 ))
		return $?
	fi
	if fileIsScsiDevice $1; then
		echo $(( 0x${deviceMinor} & 0xF0 ))
		return $?
	fi
	return 1
}

# gets partition index for storage device file
# $1 - file to query
#
function partitionIndexGet() {
	declare deviceMinor
	deviceMinor=`stat -c %T $1 2> /dev/null` || return $?
	if fileIsIdeDevice $1; then
		echo $(( 0x${deviceMinor} & 0x3F ))
		return $?
	fi
	if fileIsScsiDevice $1; then
		echo $(( 0x${deviceMinor} & 0x0F ))
		return $?
	fi
	return 1
}

# checks if file is disk device
# $1 - file to query
#
function fileIsDisk() {
	declare partitionIndex
	partitionIndex=`partitionIndexGet $1` || return $?
	[ $partitionIndex -eq 0 ] && return 0
	return 1
}

# checks if file is partition device
# $1 - file to query
#
function fileIsPartition() {
	declare partitionIndex
	partitionIndex=`partitionIndexGet $1` || return $?
	[ $partitionIndex -gt 0 ] && return 0
	return 1
}

# queries disk geometry
# $1 - device to query
#
function diskGeometryQuery() {
	declare -a geometry
	geometry=( `try sfdisk -q -g \"$1\" 2> /dev/null` ) || return $?
	local sectorCount=$(( ${geometry[1]} * ${geometry[3]} * ${geometry[5]} ))
	if [ $sectorCount -gt 0 ]; then
		echo ${geometry[1]} ${geometry[3]} ${geometry[5]}
		return 0
	fi
	echo "$this: ERR: Failed to get disk $1 geometry!" >&2
	return 1
}

# gets location of partition from disk image file 
# $1 - disk image file
# $2 - index of partition to query
#
function diskImagePartitionLocationGet() {
	declare -a entry
	entry=( `try sfdisk -q -l -uS -d \"$1\" 2> /dev/null | grep 'start=' \
			| sed -n "$2{s/.*://;s/,* *[A-Za-z]*=/ /g;p;q}"` ) \
		|| return $?
	if [ "${entry[0]}" -gt 0 -a "${entry[1]}" -gt 0 ]; then
		# partition looks plausible
		echo ${entry[0]} ${entry[1]}
		return 0
	fi
	echo "$this: ERR: Partition $2 of $1 is inconsistent!" >&2
	return 1
}

# maps file part via loop device
# $1 - file to map
# $2 - index of first 512 bytes block to map
# $3 - number of 512 bytes block to map
#
function fileLoopMap() {
	local offset=0
	local sizeSpecifier=
	declare device
	[ $2 ] && offset=$(( $2 * 512 ))
	[ $3 ] && sizeSpecifier="-s $(( $3 * 512 ))"
	for device in /dev/loop*; do
		if losetup -o $offset $sizeSpecifier $device $1 2> /dev/null; then
			echo $device
			return 0
		fi
	done
	echo "$this: ERR: Failed to allocate loop device!" >&2
	return 1	
}                     

# maps partition of disk image file via loop device
# $1 - disk image
# $2 - index of partition to map
#
function diskImagePartitionLoopMap() {
	declare -a location
	location=( `try diskImagePartitionLocationGet \"$1\" $2` ) || return $?
	try fileLoopMap \"$1\" ${location[0]} ${location[1]}
}

# invokes GRUB shell with parameters supplied and checks for errors
function grubShellInvoke() {
	# WARNING! GRUB uses to terminate with 0 even on failure
	( grub $@ || ( echo "Error 0: " && false ) ) \
		| if grep "Error [0-9]*: "; then false; fi
	return $?
}

# EOF
# ENDING: ../bootloader/util/diskTools
# ------------------------------------------------------------------------------

fileIsLoop() {
	[[ -e $1 ]] && (( 0x$(stat -c %t "$1") == 7 ))
}

diskPartitionGet() {
	if fileIsDisk "$1"; then
		echo "${1}${2}"
	else
		diskImagePartitionLoopMap "$1" "$2"
	fi
}

# truncates cylinder count of disk geometry in a sane manner
# $1 - disk geometry cylinder count
# $2 - disk geometry head count
# $3 - disk geometry track sector count
# $4 - the lowest allowed percentage of disk capacity to retain
#
diskCylinderCountTruncate() {
	declare -ir cylinderCount=$1
	declare -ir headCount=$2
	declare -ir trackSectorCount=$3
	declare -ir capacityPercentageMin=$4
	declare -i result=$cylinderCount
	# the minimal number of cylinders for proposal to qualify
	declare -i cylinderCountMin=$(( cylinderCount - (cylinderCount * capacityPercentageMin / 100) ))
	declare -ai list geometry
	declare -i proposal sectorCount length i

	for (( i = 0; i < 2; i++ )); do
		proposal=$(( cylinderCount - i ))
		length="${#list[@]}"
		list[$length]=$proposal
		sectorCount=$(( proposal * headCount * trackSectorCount ))
		# proposal to truncate to fit optimal geometry
		geometry=( $(diskGeometryPropose $sectorCount) )
		proposal=$(( ${geometry[0]} * ${geometry[1]} * ${geometry[2]} / headCount / trackSectorCount ))
		length="${#list[@]}"
		list[$length]=$proposal
		# proposal to truncate to fit geometry with largest cylinder size
		proposal=$(( (sectorCount / 255 / 63) * 255 * 63 / headCount / trackSectorCount ))
		length="${#list[@]}"
		list[$length]=$proposal
	done
	for proposal in "${list[@]}"; do
		if (( proposal < cylinderCountMin )); then
			continue
		fi
		if (( proposal < result )); then
			# proposal qualifies and is the lowest so far
			result=$proposal
		fi
	done
	echo $result
}

# repartitions disk
# $1 - target disk device to repartition
# $2 - target geometry: number of cylinders
# $3 - target geometry: number of heads
# $4 - target geometry: number of sectors per track
# $5 - number of target sectors to erase
# $6 - size of persistent read/write partition
#
diskRepartition() {
	declare -r target=$1
	declare -air geometry=( $2 $3 $4 )
	declare -ir headCount=$3
	declare -ir trackSectorCount=$4
	declare -i erasureSectorCount=$5 erasureSectorOffset=0
	declare -ir dataPartitionSize=$6
	declare -ir cylinderSize=$(( headCount * trackSectorCount * 512 ))

	# reserving free space at the end of disk
	declare -ir cylinderCount=$(diskCylinderCountTruncate ${geometry[*]} $storageUsableCapacityPercentageMin)
	echo "using first $cylinderCount cylinders of $target"

	# configuration partition cylinder count
	declare -ir extendedPartitionSize=$(( dataPartitionSize * 2 ))
	declare -i extendedPartitionCylinderCount=$(( (extendedPartitionSize - 1) / cylinderSize + 1 ))
	if (( extendedPartitionCylinderCount < 2 )); then
		# adjust for minimum
		extendedPartitionCylinderCount=2
	fi

	# bootloader partition cylinder count
	declare -ir bootloaderPartitionCylinderCount=$(( (bootloaderPartitionSizeMin - 1) / cylinderSize + 1 ))

	# number of cylinders for each system partition
	declare -ir remainingCylinderCount=$(( cylinderCount - bootloaderPartitionCylinderCount - extendedPartitionCylinderCount ))
	declare -ir systemPartitionCylinderCount=$(( remainingCylinderCount / systemCount ))
	declare -ir remainder=$(( remainingCylinderCount % systemCount ))
	if (( systemPartitionCylinderCount > 0 )); then
		(( extendedPartitionCylinderCount += remainder ))
	else
		echo "$this: ERR: insufficient space on $target !" >&2
		exit $errorTargetTooSmall
	fi

	# persistent read/write partition cylinder count
	declare -ir dataPartitionCylinderCount=$(( extendedPartitionCylinderCount / 2 ))

	printf "repartitioning %s ... " "$target"
	# unconditionally erase existing MBR and entire disk (optionally)
	if (( erasureSectorCount == 0 )) || [[ ! -e $target || -f $target ]]; then
		erasureSectorCount=$(( ${geometry[0]} * ${geometry[1]} * ${geometry[2]} ))
		if [[ ! -e $target || -f $target ]]; then
			erasureSectorOffset=$erasureSectorCount
			erasureSectorCount=0
		fi
	fi
	dd if=/dev/zero of="$target" status=noxfer bs=512 seek=$erasureSectorOffset count=$erasureSectorCount 2> /dev/null

	declare options
	fileIsDisk "$target" || options=--no-reread

	# create partition table
	sfdisk -q $options -C ${geometry[0]} -H ${geometry[1]} -S ${geometry[2]} "$target" > /dev/null 2> /dev/null << EOF
,$bootloaderPartitionCylinderCount,L,*
,$systemPartitionCylinderCount,L
,$systemPartitionCylinderCount,L
,$extendedPartitionCylinderCount,E
,$dataPartitionCylinderCount,L
,,L
EOF
	echo ok
}

# queries cpu class
# $1 - factory file of version details
#
cpuClassQuery() {
	declare token
	declare -i cpuClass=0
	[[ -r $1 ]] && token=$(sed -n '8,1{p;q}' "$1") && [[ $token =~ ^[[:digit:]]+$ ]] && cpuClass=$token
	if (( cpuClass == 0 )) && which sysQuery > /dev/null 2>&1; then
		# no note on target cpu class made during production, detecting now
		case $(sysQuery -t) in
		1)
			# ADVANTECH_SOM_5786 -> Core
			cpuClass=1
			;;
		2)
			# ADVANTECH_SOM_5790 -> Core i5
			cpuClass=5
			;;
		3)
			# INTEL_D865GLC -> Pentium4
			cpuClass=4
			;;
		4)
			# ASUS_N4L_VM -> Core
			cpuClass=1
			;;
		5)
			# BCM_MX965GME -> Core
			cpuClass=1
			;;
		10)
			# CCT_AM31X -> Core i7
			cpuClass=7
			;;
		*)
			# assuming Core
			cpuClass=1
			;;
		esac
	fi
	echo $cpuClass
}

# check if an upgrade package to be applied is compatible with the hardware of the current system
# see doc/version-compatibility.txt
# $1 - reference file of version details
# $2 - candidate file of version details
#
hardwareCompatibilityCheck() {
	declare -r reference=$1
	declare -r candidate=$2
	declare -ir referenceCpuClass=$(cpuClassQuery "$reference")
	declare -i candidateCpuClass=0
	declare token
	[[ -r $candidate ]] && token=$(sed -n '8,1{p;q}' "$candidate") && [[ $token =~ ^[[:digit:]]+$ ]] && candidateCpuClass=$token
	if (( candidateCpuClass == 0 )); then
		# specific cpu class not enforced, any will fit
		candidateCpuClass=$referenceCpuClass
	fi
	if (( candidateCpuClass != referenceCpuClass )); then
		echo "$this: ERR: Version check: wrong package product target cpu class ($candidateCpuClass != $referenceCpuClass) !" >&2
		exit $errorPackageProductCpuClassWrong
	fi
}

# check if the version of an upgrade package to be applied is compatible the current system
# see doc/version-compatibility.txt
# $1 - reference file of version details
# $2 - candidate file of version details
#
versionCompatibilityCheck() {
	declare -r reference=$1
	declare -r candidate=$2
	declare token
	token=$(sed -n '4,1{p;q}' "$reference")
	[[ $token =~ ^[[:digit:]]+$ ]] || token=0
	declare -ir referenceVersion="10#"$token
	token=$(sed -n '4,1{p;q}' "$candidate")
	[[ $token =~ ^[[:digit:]]+$ ]] || token=0
	declare -ir candidateVersion="10#"$token
	if (( candidateVersion > 0 && referenceVersion > 0 )); then
		# product type must match (see RM2241)
		token=$(sed -n '1,1{p;q}' "$reference")
		declare -r referenceProductType=${token:0:3}
		token=$(sed -n '1,1{p;q}' "$candidate")
		declare -r candidateProductType=${token:0:3}
		if [[ $candidateProductType == "$referenceProductType" ]]; then
			# product type does match, ok to check version numbers
			if (( candidateVersion < referenceVersion )); then 
				echo "$this: ERR: Version check: application from the package is too old ($candidateVersion < $referenceVersion) !" >&2
				exit $errorPackageApplicationTooOld
			fi
		else
			echo "$this: ERR: Version check: wrong package product type ($candidateProductType != $referenceProductType) !" >&2
			exit $errorPackageProductTypeWrong
		fi
	else
		echo "$this: ERR: Version check: invalid application version numbers encountered !" >&2
		exit $errorApplicationBadVersionId
	fi
}

# check if upgrade package to be applied is compatible with the current system
# see doc/version-compatibility.txt
# $1 - reference directory with the initial version files from the factory
# $2 - candidate work directory
# $3 - package
#
packageCompatibilityCheck() {
	declare -r package=$1
	declare -r reference=$2
	declare -r workspace=$3
	if [[ -r ${reference}/bootloader-version.txt ]]; then
		# there is bootloader version number as it comes from factory available
		if tar -C "$workspace" --strip-components=3 -xf "$package" ./usr/etc/bootloader-version.txt; then
			# there is bootloader version number available from package
			declare token
			token=$(sed -n '2,1{s/^.*: //;s/ .*//;p;q}' "${reference}/bootloader-version.txt")
			[[ $token =~ ^[[:digit:]]+$ ]] || token=0
			declare -ir referenceVersion="10#"$token
			token=$(sed -n '2,1{s/^.*: //;s/ .*//;p;q}' "${workspace}/bootloader-version.txt")
			[[ $token =~ ^[[:digit:]]+$ ]] || token=0
			declare -ir candidateVersion="10#"$token
			if (( candidateVersion > 0 && referenceVersion > 0 )); then
				if (( candidateVersion < referenceVersion )); then
					echo "$this: ERR: Version check: bootloader from the package is too old ($candidateVersion < $referenceVersion) !" >&2
					exit $errorPackageBootloaderTooOld
				fi
			else
				echo "$this: ERR: Version check: invalid bootloader version numbers encountered !" >&2
				exit $errorBootloaderBadVersionId
			fi
		else
			echo "$this: ERR: Version check: no usr/etc/bootloader-version.txt in package !" >&2
			exit $errorPackageBootloaderVersionUnknown
		fi
	fi
	if tar -C "$workspace" --strip-components=3 -xf "$package" ./etc/Axia/sysVersion.txt; then
		# there are version details available from package
		if [[ -r ${reference}/sysVersion.txt ]]; then
			# there are version details available from factory
			versionCompatibilityCheck "${reference}/sysVersion.txt" "${workspace}/sysVersion.txt"
		fi
		hardwareCompatibilityCheck "${reference}/sysVersion.txt" "${workspace}/sysVersion.txt"
	else
		echo "$this: ERR: Version check: no etc/Axia/sysVersion.txt in package !" >&2
		exit $errorPackageApplicationVersionUnknown
	fi
}

# chooses an item from a compound pack to install
# $1 - package to select from
# $2 - reference directory with the initial version files from the factory
# $3 - workspace directory
#
compoundPackItemSelect() {
	declare -r package=$1
	declare -r reference=$2
	declare -r workspace=$3
	declare referenceProductType productType
	declare -i referenceCpuClass=0 cpuClass=0
	declare -i referenceVersion=0 version=0 selectionVersion=0
	declare -i referenceBootloaderVersion=0 bootloaderVersion=0 selectionBootloaderVersion=0
	declare message selectionMessage="Version check: no sysVersion.txt found in package !"
	declare -i error=0 selectionError=$errorPackageApplicationVersionUnknown
	declare -i suitability=0 selectionSuitability=0
	declare -i selection=-1
	declare token item candidate
	declare -a list

	# version information from factory is to be considered
	if [[ -r ${reference}/sysVersion.txt ]]; then
		token=$(sed -n '1,1{p;q}' "${reference}/sysVersion.txt")
		referenceProductType=${token:0:3}
		token=$(sed -n '4,1{p;q}' "${reference}/sysVersion.txt")
		[[ $token =~ ^[[:digit:]]+$ ]] && referenceVersion="10#"$token
	fi
	referenceCpuClass=$(cpuClassQuery "${reference}/sysVersion.txt")
	if [[ -r ${reference}/bootloader-version.txt ]]; then
		token=$(sed -n '2,1{s/^.*: //;s/ .*//;p;q}' "${reference}/bootloader-version.txt")
		[[ $token =~ ^[[:digit:]]+$ ]] && referenceBootloaderVersion="10#"$token
	fi

	# obtaining all the version information available
	if list=( $(tar -tf "$package" | grep '^\(\./\)\?[[:digit:]]\+/\(sysVersion\|bootloader-version\)\.txt$') ); then
		tar -C "$workspace" -xf "$package" "${list[@]}"
	fi

	# selecting the best fit
	list=( $(find "$workspace" -mindepth 1 -type d -printf "%f\n") )
	for item in "${list[@]}"; do
		[[ $item =~ ^[[:digit:]]+$ ]] || continue
		suitability=0
		version=0
		bootloaderVersion=0
		message=""
		error=0
		candidate=${workspace}/${item}
		# evaluating the candidate
		if [[ -r ${candidate}/sysVersion.txt ]]; then
			# product type must match (see RM2241)
			suitability=1
			token=$(sed -n '1,1{p;q}' "${candidate}/sysVersion.txt")
			productType=${token:0:3}
			if [[ $productType == "$referenceProductType" || ! $referenceProductType ]] ; then
				# product type does match or is unknow, ok to check hardware compatibility
				suitability=2
				token=$(sed -n '8,1{p;q}' "${candidate}/sysVersion.txt")
				[[ $token =~ ^[[:digit:]]+$ ]] || token=0
				cpuClass=$token
				if (( cpuClass == 0 )); then
					# specific cpu class not enforced, any will fit
					cpuClass=$referenceCpuClass
				fi
				if (( cpuClass == referenceCpuClass )); then
					# product cpu class does match, ok to check version numbers
					suitability=3
					token=$(sed -n '4,1{p;q}' "${candidate}/sysVersion.txt")
					[[ $token =~ ^[[:digit:]]+$ ]] || token=0
					version="10#"$token
					if (( version < referenceVersion )); then
						message="Version check: application from the package is too old ($version < $referenceVersion) !"
						error=$errorPackageApplicationTooOld
					else
						# version is recent enough, checking bootloader
						suitability=4
						if (( referenceBootloaderVersion > 0 )); then
							# there is bootloader version number specified by factory
							if [[ -r ${candidate}/bootloader-version.txt ]]; then
								# there is bootloader version number available from package
								suitability=5
								token=$(sed -n '2,1{s/^.*: //;s/ .*//;p;q}' "${candidate}/bootloader-version.txt")
								[[ $token =~ ^[[:digit:]]+$ ]] || token=0
								bootloaderVersion="10#"$token
							else
								message="Version check: no bootloader-version.txt found in package !"
								error=$errorPackageBootloaderVersionUnknown
							fi
						fi
						if (( bootloaderVersion < referenceBootloaderVersion )); then
							message="Version check: bootloader from the package is too old ($bootloaderVersion < $referenceBootloaderVersion) !"
							error=$errorPackageBootloaderTooOld
						else
							# bootloader version is recent enough
							suitability=6
						fi
					fi
				else
					message="Version check: product cpu class $referenceCpuClass not supported by package !"
					error=$errorPackageProductCpuClassNotSupported
				fi
			else
				message="Version check: product type $referenceProductType not supported by package !"
				error=$errorPackageProductTypeNotSupported
			fi
		else
			if [[ -r ${reference}/sysVersion.txt ]]; then
				# version information is provided by factory therefore it must also be present in package
				message="Version check: no sysVersion.txt found in package !"
				error=$errorPackageApplicationVersionUnknown
			else
				# version information not provided by factory, anything would fit
				suitability=6
			fi
		fi
		# candidate evaluation complete, comparing with the current selection
		if (( suitability < selectionSuitability )); then
			# current selection is better, it stays
			continue
		fi
		if (( suitability == selectionSuitability )); then
			# the candidate might be a better fit
			if (( suitability < 3 )); then
				# the candidate is not be a better fit
				continue
			fi
			if (( version < selectionVersion )); then
				# the candidate is not be a better fit
				continue
			fi
			if (( version == selectionVersion )); then
				# the candidate scores equally so far
				if (( suitability < 5 )); then
					# the candidate is not be a better fit
					continue
				fi
				if (( bootloaderVersion < selectionBootloaderVersion )); then
					# the candidate is not be a better fit
					continue
				fi
			fi
		fi
		# the candidate is a better fit 
		selectionSuitability=$suitability
		selectionVersion=$version
		selectionBootloaderVersion=$bootloaderVersion
		selectionMessage=$message
		selectionError=$error
		selection=$item
	done

	# candidate selected
	if (( selectionSuitability == 6 && selectionError == 0 )); then
		echo $selection
	else
		# the selected candidate (if any) is not good enough
		echo "$this: ERR: $selectionMessage" >&2
		exit $selectionError
	fi
}

# installs an item from a compound pack
# $1 - target directory
# $2 - source package
# $3 - index of the item within source to install
#
systemCompoundPackItemInstall() {
	declare -r workspace=$1
	declare -r package=$2
	declare -ir index=$3
	declare -ir mask=$(( 1 << index ))
	declare -i component=0 count=0
	declare -a files components=( "" )
	declare file name reference sum

	# determining and verifying all the components required to build the item
	printf "verifying components: "
	files=( $(tar -tf "$package" | grep '^\(\./\)\?[[:xdigit:]]\.tbz2$') )
	for file in "${files[@]}"; do
		name=$(basename "$file")
		component=0x"${name%%.*}"
		if (( (component & mask) == 0 )); then
			# component does not participate in this item
			continue
		fi
		printf "%s " "$name"
		if reference=$(tar -xf "$package" -O "${file}.sha1"); then
			# the embedded sha1sum of component obtained
			if sum=$(tar -xf "$package" -O "$file" | sha1sum -b -); then
				# sha1sum of component obtained
				if [[ ${sum%% *} != ${reference%% *} ]]; then
					# checksum failed, item is unavailable
					echo "$this: ERR: The sha1sum of $package:$file does not match $package:${file}.sha1 !" >&2
					exit $errorPackageComponentSumDiscrepancy
				fi
			else
				echo "$this: ERR: Failed to calculate $package:$file sha1sum !" >&2
				exit $errorPackageComponentSumCalculationFailed
			fi
		else
			echo "$this: ERR: Failed to retrieve $package:${file}.sha1 !" >&2
			exit $errorPackageComponentSumRetrievalFailed
		fi
		if (( component == mask )); then
			# component defines item structure and therefore shall be deployed as first
			components[0]=$file
			continue
		fi
		count="${#components[@]}"
		components[$count]=$file
	done
	echo ok

	# deploying components
	printf "deploying components: "
	for file in "${components[@]}"; do
		[[ $file ]] || continue
		printf "%s " "$(basename "$file")"
		if ! tar -xf "$package" -O "$file" | tar -C "$workspace" -xjp --atime-preserve; then
			echo "$this: ERR: Failed to deploy $package:$file !" >&2
			exit "$errorPackageComponentDeploymentFailed"
		fi
	done
	echo ok

	# completing by putting sysVersion.txt in place
	if file=$(tar -tf "$package" | grep "^\(\./\)\?${index}/sysVersion\.txt\$"); then
		mkdir -p --mode=755 "${workspace}/etc/Axia"
		tar -xf "$package" -O "$file" > "${workspace}/etc/Axia/$(basename "$file")"
		chmod 444 "${workspace}/etc/Axia/sysVersion.txt"
	fi
}

# creates a system on partition and optionally installs bootloader
# $1 - target partition
# $2 - label for file system on the target partition
# $3 - bootloader directory for version compatibility check
# $4 - source package
# $5 - index of item within source package to install
# $6 - workspace directory
# $7 - target device on which to install the bootloader
# $8 - name of target device to use in the bootloader menu
#
systemCreate() {
	declare -r target=$1
	declare -r label=$2
	declare -r bootloader=$3
	declare -r package=$4
	declare -i index=$5
	declare -r workspace=$6
	declare -r device=$7
	declare -r deviceName=$8
	declare -i format=0 r=0
	declare file token reference
	[[ ! $bootloader ]] || reference=${bootloader}/factory
	
	# package format
	printf "package %s format ... " "$(basename "$package")"
	if file=$(tar -tf "$package" | grep '^\(\./\)\?format\.txt$'); then
		# format specification encountered
		token=$(tar -xf "$package" -O "$file" | sed -n '1,1{p;q}')
		[[ $token =~ ^[[:digit:]]+$ ]] && format=$token
	fi
	if (( format == 0 || format == 2 )); then
		if file=$(tar -tf "$package" | grep '^\(\./\)\?.*\.tbz2$'); then
			# nested package encountered, assuming version format 2
			format=2
		else
			if (( format == 2 )); then
				# nested package must be there
				echo "$this: ERR: format 2 package $package missing the nested package." >&2
				exit $errorPackageMissingNestedPackage
			fi
			# no nested package, assuming format version 1
			format=1
		fi
	fi
	echo $format
	if (( format < 1 || format > 3 )); then
		echo "$this: ERR: the format ($format) of package $package is not supported." >&2
		exit $errorPackageFormatNotSupported
	fi

	mkdir -p "${workspace}/package"
	if (( format == 3 )); then
		if (( index < 0 )); then
			# selecting item within source
			printf "selecting item ... "
			index=$(compoundPackItemSelect "$package" "$reference" "${workspace}/package")
			echo "$index"
		fi
	else
		if [[ $bootloader ]]; then
			# checking package compatibility
			printf "checking compatibility ... "
			if [[ -d $reference || -f ${bootloader}/stage2 ]]; then
				packageCompatibilityCheck "$package" "$reference" "${workspace}/package"
			else
				echo "$this: ERR: Version check: bootloader partition is not accessible !" >&2
				exit $errorBootloaderNotAccessible
			fi
			echo ok
		fi
	fi
	rm -Rf "${workspace}/package"

	printf "creating system on %s ... " "$label"
	mkdir -p "${workspace}/target"
	mke2fs -q "$target"
	tune2fs -c 0 "$target" -L "$label" > /dev/null
	mount "$target" "${workspace}/target"
	echo ok

	case $format in
	1)
		printf "deploying system on %s ... " "$label"
		tar -C "${workspace}/target" -xp --atime-preserve -f "$package" --exclude ./etc/Axia/sysVersion.txt
		# ensuring that sysVersion.txt is the last file deployed
		tar -C "${workspace}/target" -xp --atime-preserve -f "$package" ./etc/Axia/sysVersion.txt
		echo ok
		;;
	2)
		printf "deploying system on %s ... " "$label"
		tar -xf "$package" -O "$file" | tar -C "${workspace}/target" -xjp --atime-preserve --exclude ./etc/Axia/sysVersion.txt
		# ensuring that sysVersion.txt is the last file deployed
		tar -C "${workspace}/target" -xp --atime-preserve -f "$package" ./etc/Axia/sysVersion.txt
		echo ok
		;;
	3)
		systemCompoundPackItemInstall "${workspace}/target" "$package" "$index"
		;;
	esac

	if [[ $device ]]; then
		# bootloader shall be installed from this system
		printf "installing bootloader on %s ... \n" "$device"
		mkdir -p "${workspace}/bootloader/output/factory"
		cp -p "${workspace}/target/etc/Axia/sysVersion.txt" "${workspace}/bootloader/output/factory"
		"${workspace}/target/usr/sbin/bootloader-install" --full-install \
			--target-final-name="$deviceName" --work-directory="${workspace}/bootloader" \
			"$device" "${workspace}/target"
		rm -Rf "${workspace}/bootloader"
		echo "... ok"
	fi

	umount "${workspace}/target"
	rm -Rf "${workspace}/target"
	sync

	printf "checking %s ... " "$label"
	fsck -T -p "$target" > /dev/null || r=$?
	if (( (r & 0xFE) == 0 )); then
		# system partition now is ready
		echo ok
	else
		# error other than correctable file system error was encountered
		printf "nok (%02X)\n" $r
		exit $r
	fi
}

# cleans system start failure counter
# $1 - target directory to mount the target partition on
# $2 - target partition
# $3 - index of system to clean
#
systemBootClean() {
	declare -r directory=$1
	declare -r partition=$2
	declare -ir index=$3
	printf "cleaning boot for system %d ... " "$index"
	mount -r "$partition" "$directory"
	PATH=${directory}${PATH//:/:$directory}:$PATH bootClean "$index" || true
	umount "$directory"
	echo ok
}

# ------------------------------------------------------------------------------

# cleans up on termination
#
finish() {
	set +e
	trap - ERR
	printf "cleaning up ... "
	if [[ -d $1 ]]; then
		if [[ -d ${1}/target ]]; then
			umount "${1}/target"
		fi
		rm -Rf "$1"
	fi
	if fileIsLoop "$2"; then
		losetup -d "$2"
	fi
	echo "done"
}

# ==============================================================================

# 0 - full installation
# 1 - update
declare -i mode=1

declare -i dataPartitionSize=$(( 20 * 1024 * 1024 ))
declare -i targetErasureSectorCount=1

# number of different ways how target geometry is specified
declare -i targetGeometrySpecifierCount=0

declare -i targetSize=0
declare -ai targetGeometry
declare targetTemplateDevice
declare targetFinalName

declare bootloader=/boot/grub

declare token target package

# ------------------------------------------------------------------------------

# - inherit traps in functions
#
# - terminate on errors
# note that function execution is interrupted only if the function is called as
# a simple command or as the last command of '&&' or '||' list
#   
# - insist on declarations
#
# - strict on pipeline exit status
set -Eeu -o pipefail

echo "$this (version $scriptVersion, $scriptSvnRevision)"

trap 'failureReport "$this" $LINENO' ERR

# retrieve parameters
for token in "$@"; do
	case $token in
	-h | --help)
		usagePrint
		exit 0
		;;
	-e | --erase)
		targetErasureSectorCount=0
		;;
	-f | --full-install)
		mode=0
		;;
	-p=* | --persistent-rw-fs-size=*)
		if ! dataPartitionSize=$(sizeNormalize "${token##*=}") || (( dataPartitionSize < 1 )); then
			echo "$this: ERR: Bad data partition size specified." >&2
			usageSuggest
			exit $errorSyntax
		fi
		;;
	-n=* | --target-final-name=*)
		targetFinalName="${token##*=}"
		;;
	-s=* | --target-size=*)
		(( targetGeometrySpecifierCount++ ))
		if ! targetSize=$(sizeNormalize "${token##*=}") || (( targetSize < 1 )); then
			echo "$this: ERR: Bad target size specified." >&2
			usageSuggest
			exit $errorSyntax
		fi
		;;
	-g=* | --target-geometry=*)
		(( targetGeometrySpecifierCount++ ))
		token="${token##*=}"
		if [[ $token =~ ^[[:digit:]]+:[[:digit:]]+:[[:digit:]]+$ ]]; then
			IFS=":"
			targetGeometry=( $token )
			IFS=
			if (( "${#targetGeometry[@]}" != 3
					|| targetGeometry[0] < 1 || targetGeometry[0] > 65535
					|| targetGeometry[1] < 1 || targetGeometry[1] > 16
					|| targetGeometry[2] < 1 || targetGeometry[2] > 255 )); then
				echo "$this: ERR: Bad geometry specified." >&2
				usageSuggest
				exit $errorSyntax
			fi
		else
			echo "$this: ERR: Bad geometry specified." >&2
			usageSuggest
			exit $errorSyntax
		fi
		;;
	-t=* | --target-template=*)
		(( targetGeometrySpecifierCount++ ))
		targetTemplateDevice="${token##*=}"
		;;
	-b=* | --bootloader-directory=*)
		bootloader=$(readlink -f "${token##*=}")
		;;
	-*)
		echo "$this: ERR: Unrecognized option." >&2
		usageSuggest
		exit $errorSyntax
		;;
	*)
		if [[ $package ]]; then
			if [[ $target ]]; then
				echo "$this: ERR: Unexpected argument." >&2
				usageSuggest
				exit $errorSyntax
			else
				target=$token
			fi
		else
			package=$(readlink -f "$token")
		fi
		;;
	esac
done

if [[ ! $package ]]; then
	echo "$this: ERR: Source package is not specified!" >&2
	usageSuggest
	exit $errorSyntax
fi

if [[ ! $target ]]; then
	echo "$this: ERR: Target device is not specified!" >&2
	usageSuggest
	exit $errorSyntax
fi

if (( targetGeometrySpecifierCount > 1 )); then
	# multiple geometry specifying options (-s, -g, -t) provided
	cat >&2 << EOF
$this: ERR: Target device size, geometry and template options
   are mutualy exclusive. More than one of them provided!
EOF
	usageSuggest
	exit $errorSyntax
fi

if (( targetGeometrySpecifierCount > 0 )); then
	# at least one geometry specifying option (-s, -g, -t) provided
	if fileIsDisk "$target" || fileIsPartition "$target"; then
		# the specified target is either disk or partition
		cat >&2 << EOF
$this: ERR: Target device size, geometry and template options
   shall be provided only when target image file is to be composed!
EOF
		usageSuggest
		exit $errorSyntax
	fi
fi

# ------------------------------------------------------------------------------

declare targetPartition
declare -r scriptWorkDirectory=$(mktemp -d -t "${this}.XXXXXXXXXX")
declare workspace=$scriptWorkDirectory

trap 'finish "$scriptWorkDirectory" "$targetPartition"' EXIT INT TERM

echo "working in $workspace"
declare -i partitionIndex=$(partitionIndexGet "$target" || echo -1)
if (( mode == 0 )); then
	# complete installation on disk device
	if (( partitionIndex < 0 )); then
		# not a disk partition; apparently an image file
		if (( targetSize > 0 )); then
			targetGeometry=( $(diskGeometryPropose $(( targetSize / 512 ))) )
		fi
		if [[ $targetTemplateDevice ]]; then
			targetGeometry=( $(diskGeometryQuery "$targetTemplateDevice") )
		fi
		if (( "${#targetGeometry[@]}" < 3 )); then
			echo "$this: ERR: Target is disk image but its size or geometry is not specified!" >&2
			usageSuggest
			exit $errorSyntax
		fi
	else
		if (( partitionIndex == 0 )); then
			# the specified target is disk device
			targetGeometry=( $(diskGeometryQuery "$target") )
		else
			# the specified target is a partition
			echo "$this: ERR: Target device is partition but full install mode is requested!" >&2
			usageSuggest
			exit $errorSyntax
		fi
	fi		

	# repartitioning target
	diskRepartition "$target" ${targetGeometry[*]} $targetErasureSectorCount "$dataPartitionSize"

	# deploying system
	targetPartition=$(diskPartitionGet "$target" 3)
	systemCreate "$targetPartition" ROOT_1 "" "$package" 0 "$workspace" "" ""
	! fileIsLoop "$targetPartition" || losetup -d "$targetPartition"

	# deploying system and installing bootloader
	targetPartition=$(diskPartitionGet "$target" 2)
	systemCreate "$targetPartition" ROOT_0 "" "$package" 0 "$workspace" "$target" "$targetFinalName"
	! fileIsLoop "$targetPartition" || losetup -d "$targetPartition"

	targetPartition=$(diskPartitionGet "$target" 5)
	mke2fs -q -j "$targetPartition"
	tune2fs -c 0 "$targetPartition" -L CONFIG > /dev/null
	! fileIsLoop "$targetPartition" || losetup -d "$targetPartition"

	targetPartition=$(diskPartitionGet "$target" 6)
	mke2fs -q -j "$targetPartition"
	tune2fs -c 0 "$targetPartition" -L BACKUP > /dev/null
	! fileIsLoop "$targetPartition" || losetup -d "$targetPartition"

	targetPartition=
else
	# upgrade of a single partition
	if (( partitionIndex < 2 )); then
		if (( partitionIndex < 0 )); then
			# not a disk partition; apparently an image file
			partitionIndex=2
		else
			if (( partitionIndex == 0 )); then
				# the specified target is disk device
				echo "$this: ERR: Target device is a disk but full install mode is no requested!" >&2
			else
				# the specified target is a partition inappropriate for system
				echo "$this: ERR: Inappropriate partition specified as target!" >&2
			fi
			usageSuggest
			exit $errorSyntax
		fi
	else
		# trying to reset failure counter for target system
		systemBootClean "$workspace" "$target" $(( partitionIndex - 2 )) \
			|| echo "$this: WARNING: systemBootClean failed! Continuing..." >&2
	fi

	# deploying system on requested partition
	systemCreate "$target" ROOT_$(( partitionIndex - 2 )) "$bootloader" "$package" -1 "$workspace" "" ""
fi

# EOF
