Files
virtual-dsm/src/disk.sh
T

834 lines
21 KiB
Bash

#!/usr/bin/env bash
set -Eeuo pipefail
# Docker environment variables
: "${DISK_IO:="native"}" # I/O Mode, can be set to 'native', 'threads' or 'io_uring'
: "${DISK_FMT:="raw"}" # Disk file format, 'raw' by default for best performance
: "${DISK_TYPE:=""}" # Device type to be used, "sata", "nvme", "blk" or "scsi"
: "${DISK_FLAGS:=""}" # Specifies the options for use with the qcow2 disk format
: "${DISK_CACHE:="none"}" # Caching mode, can be set to 'writeback' for better performance
: "${DISK_DISCARD:="unmap"}" # Controls whether unmap (TRIM) commands are passed to the host.
: "${DISK_ROTATION:="1"}" # Rotation rate, set to 1 for SSD storage and increase for HDD
# Sanitize all variables
DISK_IO=$(strip "$DISK_IO")
DISK_FMT=$(strip "$DISK_FMT")
DISK_TYPE=$(strip "$DISK_TYPE")
DISK_FLAGS=$(strip "$DISK_FLAGS")
DISK_CACHE=$(strip "$DISK_CACHE")
DISK_DISCARD=$(strip "$DISK_DISCARD")
DISK_ROTATION=$(strip "$DISK_ROTATION")
BOOT="$STORAGE/$BASE.boot.img"
SYSTEM="$STORAGE/$BASE.system.img"
[ ! -s "$BOOT" ] && error "Virtual DSM boot-image does not exist ($BOOT)" && exit 81
[ ! -s "$SYSTEM" ] && error "Virtual DSM system-image does not exist ($SYSTEM)" && exit 82
if ! setOwner "$BOOT"; then
warn "failed to set the owner for \"$BOOT\" !"
fi
if ! setOwner "$SYSTEM"; then
warn "failed to set the owner for \"$SYSTEM\" !"
fi
fmt2ext() {
local diskFmt="$1"
case "${diskFmt,,}" in
qcow2) echo "qcow2" ;;
raw) echo "img" ;;
*) error "Unrecognized disk format: $diskFmt" && exit 78 ;;
esac
}
ext2fmt() {
local diskExt="$1"
case "${diskExt,,}" in
qcow2) echo "qcow2" ;;
img) echo "raw" ;;
*) error "Unrecognized file extension: .$diskExt" && exit 78 ;;
esac
}
getSize() {
local diskFile="$1"
local diskExt diskFmt size
diskExt=$(echo "${diskFile//*./}" | sed 's/^.*\.//')
diskFmt=$(ext2fmt "$diskExt")
case "${diskFmt,,}" in
raw)
stat -c%s "$diskFile"
;;
qcow2)
size=$(qemu-img info --output=json -f "$diskFmt" "$diskFile" | jq -r '."virtual-size" // empty')
if [[ ! "$size" =~ ^[0-9]+$ ]]; then
error "Failed to determine virtual size of $diskFile"
exit 78
fi
echo "$size"
;;
*)
error "Unrecognized disk format: $diskFmt"
exit 78
;;
esac
}
isCow() {
local fs="$1"
if [[ "${fs,,}" == "btrfs" ]]; then
return 0
fi
return 1
}
supportsDirect() {
local fs="$1"
if [[ "${fs,,}" == "ecryptfs" || "${fs,,}" == "tmpfs" ]]; then
return 1
fi
return 0
}
validDiskType() {
case "${1,,}" in
"ide" | "sata" | "nvme" | "usb" | "scsi" | "blk" | \
"virtio-blk" | "virtio-scsi" | "auto" | "none" )
return 0
;;
esac
return 1
}
allocateRaw() {
local diskFile="$1"
local dataSize="$2"
if disabled "$ALLOCATE"; then
truncate -s "$dataSize" "$diskFile"
return $?
fi
fallocate -l "$dataSize" "$diskFile" &>/dev/null && return 0
fallocate -l -x "$dataSize" "$diskFile" && return 0
truncate -s "$dataSize" "$diskFile" || return 1
return 0
}
getDiskOptions() {
local fs="$1"
local diskFmt="$2"
local diskParam="$DISK_ALLOC"
isCow "$fs" && diskParam+=",nocow=on"
if [[ "${diskFmt,,}" != "raw" ]]; then
[ -n "$DISK_FLAGS" ] && diskParam+=",$DISK_FLAGS"
fi
echo "$diskParam"
return 0
}
normalizeSize() {
local diskSpace="$1"
local diskDesc="$2"
local dir="$3"
local gb free space
local dataSize spare=1073741824
if [[ "${diskSpace,,}" == "max" || "${diskSpace,,}" == "half" ]]; then
free=$(df --output=avail -B 1 "$dir" | tail -n 1)
if [[ "${diskSpace,,}" == "max" ]]; then
free=$(( free - spare ))
else
free=$(( free / 2 ))
fi
(( free < spare )) && free="$spare"
gb=$(( free / 1073741825 ))
diskSpace="${gb}G"
fi
space="${diskSpace// /}"
[ -z "$space" ] && space="256G"
[ -z "${space//[0-9. ]}" ] && space="${space}G"
space=$(echo "${space^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
if ! numfmt --from=iec "$space" &>/dev/null; then
error "Invalid value for ${diskDesc^^}_SIZE: $diskSpace" && exit 73
fi
dataSize=$(numfmt --from=iec "$space")
if (( dataSize < 6442450944 )); then
error "Please increase the ${diskDesc^^}_SIZE variable to at least 6 GB." && exit 73
fi
echo "$space"
return 0
}
baseDir() {
local path="${1%/}"
[[ -z "$path" || "$path" == "/" ]] && {
echo "/"
return 0
}
path="${path#/}"
path="${path%%/*}"
echo "/$path"
return 0
}
freeSpace() {
local path="$1"
local base
base=$(baseDir "$path")
if ! available=$(df --output=avail -B 1 "$path" | tail -n 1); then
error "Failed to check free space in $base."
exit 76
fi
if [[ ! "$available" =~ ^[0-9]+$ ]]; then
error "Failed to check free space in $base."
exit 76
fi
return 0
}
createDisk() {
local diskFile="$1"
local diskSpace="$2"
local diskDesc="$3"
local diskFmt="$4"
local fs="$5"
local gb dir base
local attributes available
rm -f "$diskFile"
local dataSize
dataSize=$(numfmt --from=iec "$diskSpace")
if ! disabled "$ALLOCATE"; then
# Check free diskspace
dir=$(dirname "$diskFile")
base=$(baseDir "$dir")
freeSpace "$dir"
if (( dataSize > available )); then
gb=$(formatBytes "$available")
error "Not enough free space to create a $diskDesc of ${diskSpace/G/ GB} in $base, it has only $gb available..."
error "Please specify a smaller ${diskDesc^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 76
fi
fi
html "Creating a $diskDesc image..."
info "Creating a ${diskSpace/G/ GB} $DISK_STYLE $diskDesc image in $diskFmt format..."
local failure="Could not create a $DISK_STYLE $diskFmt $diskDesc image of ${diskSpace/G/ GB} ($diskFile)"
case "${diskFmt,,}" in
raw)
if isCow "$fs"; then
if ! touch "$diskFile"; then
error "$failure" && exit 77
fi
{ chattr +C "$diskFile"; } || :
fi
if ! allocateRaw "$diskFile" "$dataSize"; then
rm -f "$diskFile"
error "$failure" && exit 77
fi
;;
qcow2)
local diskParam
diskParam=$(getDiskOptions "$fs" "$diskFmt")
if ! qemu-img create -f "$diskFmt" -o "$diskParam" -- "$diskFile" "$dataSize" ; then
rm -f "$diskFile"
error "$failure" && exit 70
fi
;;
esac
if isCow "$fs"; then
attributes=$(lsattr "$diskFile")
if [[ "$attributes" != *"C"* ]]; then
error "Failed to disable COW for $diskDesc image $diskFile on ${fs^^} filesystem (returned $attributes)"
fi
fi
return 0
}
resizeDisk() {
local diskFile="$1"
local diskSpace="$2"
local diskDesc="$3"
local diskFmt="$4"
local fs="$5"
local gb dir base dataSize
local available currentSize
currentSize=$(getSize "$diskFile") || exit 71
dataSize=$(numfmt --from=iec "$diskSpace")
local required=$(( dataSize - currentSize ))
(( required < 1 )) && error "Shrinking disks is not supported yet, please increase ${diskDesc^^}_SIZE." && exit 71
if ! disabled "$ALLOCATE"; then
# Check free diskspace
dir=$(dirname "$diskFile")
base=$(baseDir "$dir")
freeSpace "$dir"
if (( required > available )); then
gb=$(formatBytes "$available")
error "Not enough free space to resize $diskDesc to ${diskSpace/G/ GB} in $base, it has only $gb available.."
error "Please specify a smaller ${diskDesc^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 74
fi
fi
gb=$(formatBytes "$currentSize")
MSG="Resizing $diskDesc from $gb to ${diskSpace/G/ GB}..."
info "$MSG" && html "$MSG"
local failure="Could not resize the $DISK_STYLE $diskFmt $diskDesc image from ${gb} to ${diskSpace/G/ GB} ($diskFile)"
case "${diskFmt,,}" in
raw)
if ! allocateRaw "$diskFile" "$dataSize"; then
error "$failure" && exit 75
fi
;;
qcow2)
if ! qemu-img resize -f "$diskFmt" "--$DISK_ALLOC" "$diskFile" "$dataSize" ; then
error "$failure" && exit 72
fi
;;
esac
return 0
}
convertDisk() {
local sourceFile="$1"
local sourceFmt="$2"
local destinationFile="$3"
local destinationFmt="$4"
local diskBase="$5"
local diskDesc="$6"
local fs="$7"
local tmpFile="$diskBase.tmp"
local gb dir base attributes
local available currentSize
[ -f "$destinationFile" ] && error "Conversion failed, destination file $destinationFile already exists?" && exit 79
[ ! -f "$sourceFile" ] && error "Conversion failed, source file $sourceFile does not exist?" && exit 79
rm -f "$tmpFile"
dir=$(dirname "$tmpFile")
base=$(baseDir "$dir")
if ! disabled "$ALLOCATE"; then
# Check free diskspace
currentSize=$(getSize "$sourceFile") || exit 79
freeSpace "$dir"
if (( currentSize > available )); then
gb=$(formatBytes "$available")
error "Not enough free space to convert $diskDesc to $destinationFmt in $base, it has only $gb available..."
error "Please free up some disk space or disable preallocation by setting ALLOCATE=N." && exit 76
fi
fi
local msg="Converting $diskDesc to $destinationFmt"
html "$msg..."
info "$msg, please wait until completed..."
local convertFlags="-p"
local diskParam
diskParam=$(getDiskOptions "$fs" "$destinationFmt")
if [[ "$destinationFmt" != "raw" ]]; then
if disabled "$ALLOCATE"; then
convertFlags+=" -c"
fi
fi
# shellcheck disable=SC2086
if ! qemu-img convert -f "$sourceFmt" $convertFlags -o "$diskParam" -O "$destinationFmt" -- "$sourceFile" "$tmpFile"; then
rm -f "$tmpFile"
error "Failed to convert $DISK_STYLE $diskDesc image to $destinationFmt format in $base, is there enough space available?" && exit 79
fi
if [[ "$destinationFmt" == "raw" ]]; then
if ! disabled "$ALLOCATE"; then
# Work around qemu-img bug
if ! currentSize=$(stat -c%s "$tmpFile"); then
error "Failed to determine converted image size: $tmpFile"
exit 79
fi
if ! fallocate -l "$currentSize" "$tmpFile" &>/dev/null; then
if ! fallocate -l -x "$currentSize" "$tmpFile"; then
error "Failed to allocate $currentSize bytes for $diskDesc image $tmpFile"
fi
fi
fi
fi
if ! mv "$tmpFile" "$destinationFile"; then
error "Failed to move converted $diskDesc image to $destinationFile."
exit 79
fi
if ! rm -f "$sourceFile"; then
error "Failed to remove old $diskDesc image $sourceFile."
exit 79
fi
if isCow "$fs"; then
attributes=$(lsattr "$destinationFile")
if [[ "$attributes" != *"C"* ]]; then
error "Failed to disable COW for $diskDesc image $destinationFile on ${fs^^} filesystem (returned $attributes)"
fi
fi
msg="Conversion of $diskDesc"
info "$msg to $destinationFmt completed successfully!"
return 0
}
checkFS () {
local fs="$1"
local diskFile="$2"
local diskDesc="$3"
local dir base
local attributes
dir=$(dirname "$diskFile")
base=$(baseDir "$dir")
[ ! -d "$dir" ] && return 0
if [[ "${fs,,}" == "overlay"* && "${ENGINE,,}" == "docker" ]]; then
warn "the filesystem of $base is OverlayFS, this usually means it was binded to an invalid path!"
fi
if [[ "${fs,,}" == "fuse"* ]]; then
warn "the filesystem of $base is FUSE, this extra layer will negatively affect performance!"
fi
if ! supportsDirect "$fs"; then
warn "the filesystem of $base is $fs, which does not support O_DIRECT mode, adjusting settings..."
fi
if isCow "$fs"; then
if [ -f "$diskFile" ]; then
attributes=$(lsattr "$diskFile")
if [[ "$attributes" != *"C"* ]]; then
warn "COW (copy on write) is not disabled for $diskDesc image file $diskFile, this is recommended on ${fs^^} filesystems!"
fi
fi
fi
return 0
}
createDevice () {
local diskFile="$1"
local diskType="$2"
local diskIndex="$3"
local diskAddress="$4"
local diskFmt="$5"
local diskIo="$6"
local diskCache="$7"
local diskSerial="$8"
local diskSectors="$9"
local bus="${PCI_BUS:-pcie.0}"
[[ -z "${PCI_BUS:-}" && ( "${MACHINE,,}" == pc || "${MACHINE,,}" == pc-i440fx* ) ]] && bus="pci.0"
local bootIndex=""
local diskId="data$diskIndex"
[ -n "$diskIndex" ] && bootIndex=",bootindex=$diskIndex"
local result=" -drive file=$diskFile,id=$diskId,format=$diskFmt,cache=$diskCache,aio=$diskIo,discard=$DISK_DISCARD,detect-zeroes=on"
case "${diskType,,}" in
"none" ) ;;
"auto" )
echo "$result"
;;
"usb" )
result+=",if=none \
-device usb-storage,drive=${diskId}${bootIndex}${diskSerial}${diskSectors}"
echo "$result"
;;
"nvme" )
result+=",if=none \
-device nvme,drive=${diskId}${bootIndex},serial=deadbeaf${diskIndex}${diskSerial}${diskSectors}"
echo "$result"
;;
"ide" | "sata" )
result+=",if=none \
-device ich9-ahci,id=ahci${diskIndex},addr=$diskAddress \
-device ide-hd,drive=${diskId},bus=ahci$diskIndex.0,rotation_rate=$DISK_ROTATION${bootIndex}${diskSerial}${diskSectors}"
echo "$result"
;;
"blk" | "virtio-blk" )
result+=",if=none \
-device virtio-blk-pci,drive=${diskId},bus=$bus,addr=$diskAddress,iothread=io2${bootIndex}${diskSerial}${diskSectors}"
echo "$result"
;;
"scsi" | "virtio-scsi" )
result+=",if=none \
-device virtio-scsi-pci,id=${diskId}b,bus=$bus,addr=$diskAddress,iothread=io2,hotplug=off \
-device scsi-hd,drive=${diskId},bus=${diskId}b.0,channel=0,scsi-id=0,lun=0,rotation_rate=$DISK_ROTATION${bootIndex}${diskSerial}${diskSectors}"
echo "$result"
;;
esac
return 0
}
finishDisks () {
case "${DISK_TYPE,,}" in
"blk" | "scsi" | "virtio-blk" | "virtio-scsi" )
[[ "$DISK_OPTS" != *" -object iothread,id=io2"* ]] && DISK_OPTS+=" -object iothread,id=io2" ;;
esac
return 0
}
addDisk () {
local diskBase="$1"
local diskType="$2"
local diskDesc="$3"
local diskSpace="$4"
local diskIndex="$5"
local diskAddress="$6"
local diskFmt="$7"
local diskIo="$8"
local diskCache="$9"
local fs dir used space
local diskExt diskFile
local dataSize missing
local available currentSize
local previousExt previousFmt
diskExt=$(fmt2ext "$diskFmt")
diskFile="$diskBase.$diskExt"
dir=$(dirname "$diskFile")
[ ! -d "$dir" ] && return 0
space=$(normalizeSize "$diskSpace" "$diskDesc" "$dir")
dataSize=$(numfmt --from=iec "$space")
fs=$(stat -f -c %T "$dir")
checkFS "$fs" "$diskFile" "$diskDesc" || exit $?
if ! supportsDirect "$fs"; then
diskIo="threads"
diskCache="writeback"
fi
if [ ! -s "$diskFile" ] ; then
if [[ "${diskFmt,,}" != "raw" ]]; then
previousFmt="raw"
else
previousFmt="qcow2"
fi
previousExt=$(fmt2ext "$previousFmt")
if [ -s "$diskBase.$previousExt" ] ; then
convertDisk "$diskBase.$previousExt" "$previousFmt" "$diskFile" "$diskFmt" "$diskBase" "$diskDesc" "$fs" || exit $?
fi
fi
if [ -s "$diskFile" ]; then
currentSize=$(getSize "$diskFile") || exit 71
if (( dataSize > currentSize )); then
resizeDisk "$diskFile" "$space" "$diskDesc" "$diskFmt" "$fs" || exit $?
else
if (( dataSize < currentSize )); then
if [[ "${diskSpace,,}" != "max" && "${diskSpace,,}" != "half" ]]; then
info "You decreased the ${diskDesc^^}_SIZE variable to ${diskSpace/G/ GB} but shrinking disks is not supported, will be ignored..."
fi
fi
fi
else
createDisk "$diskFile" "$space" "$diskDesc" "$diskFmt" "$fs" || exit $?
fi
if [ -f "$diskFile" ] && disabled "$ALLOCATE"; then
currentSize=$(getSize "$diskFile") || exit 73
used=$(du -sB 1 "$diskFile" | cut -f1)
available=$(df --output=avail -B 1 "$dir" | tail -n 1)
missing=$(( currentSize - used - available ))
(( missing < 0 )) && missing=0
if (( missing > 0 )); then
local gb base msg
gb=$(formatBytes "$available")
base=$(baseDir "$dir")
missing=$(formatBytes "$missing")
currentSize=$(formatBytes "$currentSize")
msg="The virtual size of the ${diskDesc,,} is $currentSize"
if [ -n "$used" ] && [[ "$used" != "0" ]]; then
used=$(formatBytes "$used")
msg+=" (of which $used is used)"
fi
info "$msg, but there is only $gb of free space remaining in $base now."
info "Please consider making at least $missing more space available in $base for future expansions."
fi
fi
if [ -f "$diskFile" ]; then
if ! setOwner "$diskFile"; then
warn "failed to set the owner for \"$diskFile\" !"
fi
fi
DISK_OPTS+=$(createDevice "$diskFile" "$diskType" "$diskIndex" "$diskAddress" "$diskFmt" "$diskIo" "$diskCache" "" "")
return 0
}
addDevice () {
local diskDev="$1"
local diskType="$2"
local diskIndex="$3"
local diskAddress="$4"
local sectors=""
local devType
devType=$(lsblk -no TYPE "$diskDev" 2>/dev/null | head -n1)
[ -z "$diskDev" ] && return 0
[ ! -b "$diskDev" ] && error "Device $diskDev cannot be found! Please add it to the 'devices' section of your compose file." && exit 55
# Only detect and apply sector sizes for partitions, not whole disks.
# Whole disk passthrough with explicit sector sizes causes DSM not to recognize the disk.
if [[ "$devType" == "part" ]]; then
local result
local logical="" physical=""
result=$(fdisk -l "$diskDev" 2>/dev/null | grep -m 1 -o "(logical/physical): .*" | cut -c 21- || true)
if [ -n "$result" ]; then
logical="${result%% *}"
physical=$(echo "$result" | grep -m 1 -o "/ .*" | cut -c 3- || true)
physical="${physical%% *}"
fi
if [ -z "$logical" ] || [ -z "$physical" ]; then
warn "Failed to determine the sector size for $diskDev"
elif [[ "$physical" != "512" ]]; then
sectors=",logical_block_size=$logical,physical_block_size=$physical"
fi
fi
DISK_OPTS+=$(createDevice "$diskDev" "$diskType" "$diskIndex" "$diskAddress" "raw" "$DISK_IO" "$DISK_CACHE" "" "$sectors")
return 0
}
[ -z "${DISK_OPTS:-}" ] && DISK_OPTS=""
[ -z "${DISK_TYPE:-}" ] && DISK_TYPE="scsi"
[ -z "${DISK_NAME:-}" ] && DISK_NAME="data"
[ -z "${DISK_DISABLE:-}" ] && DISK_DISABLE=""
if ! enabled "$DISK_DISABLE"; then
msg="Initializing disks..."
enabled "$DEBUG" && echo "$msg"
fi
if [[ "${DISK_IO,,}" == "native" && "${DISK_CACHE,,}" != "none" && "${DISK_CACHE,,}" != "directsync" ]]; then
warn "DISK_IO=native requires direct I/O caching, using DISK_IO=threads with DISK_CACHE=$DISK_CACHE."
DISK_IO="threads"
fi
case "${DISK_DISCARD,,}" in
"y" | "yes" | "true" | "1" | "on" | "unmap" )
DISK_DISCARD="unmap" ;;
"n" | "no" | "false" | "0" | "off" | "ignore" )
DISK_DISCARD="ignore" ;;
* )
warn "Invalid DISK_DISCARD value '$DISK_DISCARD', using 'unmap'."
DISK_DISCARD="unmap" ;;
esac
if [[ ! "$DISK_ROTATION" =~ ^[0-9]+$ ]]; then
warn "Invalid DISK_ROTATION value '$DISK_ROTATION', using 1."
DISK_ROTATION="1"
fi
DISK_FMT="${DISK_FMT,,}"
case "$DISK_FMT" in
"raw" | "qcow2" ) ;;
* ) error "Invalid DISK_FMT specified, value \"$DISK_FMT\" is not recognized!" && exit 78 ;;
esac
if ! validDiskType "$DISK_TYPE"; then
error "Invalid DISK_TYPE specified, value \"$DISK_TYPE\" is not recognized!"
exit 80
fi
if [[ "$DISK_FLAGS" =~ [[:space:]] ]]; then
error "Invalid DISK_FLAGS value '$DISK_FLAGS', spaces are not allowed."
exit 78
fi
if [ -z "$ALLOCATE" ]; then
ALLOCATE="N"
fi
if disabled "$ALLOCATE"; then
DISK_STYLE="growable"
DISK_ALLOC="preallocation=off"
else
DISK_STYLE="preallocated"
DISK_ALLOC="preallocation=falloc"
fi
DISK_OPTS+=$(createDevice "$BOOT" "$DISK_TYPE" "1" "0xa" "raw" "$DISK_IO" "$DISK_CACHE" "" "")
DISK_OPTS+=$(createDevice "$SYSTEM" "$DISK_TYPE" "2" "0xb" "raw" "$DISK_IO" "$DISK_CACHE" "" "")
if enabled "$DISK_DISABLE"; then
finishDisks && return 0
fi
DISK1_FILE="$STORAGE/${DISK_NAME}"
DISK2_FILE="/storage2/${DISK_NAME}2"
DISK3_FILE="/storage3/${DISK_NAME}3"
DISK4_FILE="/storage4/${DISK_NAME}4"
: "${DISK2_SIZE:=""}"
: "${DISK3_SIZE:=""}"
: "${DISK4_SIZE:=""}"
: "${DEVICE:=""}" # Docker variables to passthrough a block device, like /dev/vdc1.
: "${DEVICE2:=""}"
: "${DEVICE3:=""}"
: "${DEVICE4:=""}"
[ -z "$DEVICE" ] && [ -b "/disk" ] && DEVICE="/disk"
[ -z "$DEVICE" ] && [ -b "/disk1" ] && DEVICE="/disk1"
[ -z "$DEVICE2" ] && [ -b "/disk2" ] && DEVICE2="/disk2"
[ -z "$DEVICE3" ] && [ -b "/disk3" ] && DEVICE3="/disk3"
[ -z "$DEVICE4" ] && [ -b "/disk4" ] && DEVICE4="/disk4"
[ -z "$DEVICE" ] && [ -b "/dev/disk1" ] && DEVICE="/dev/disk1"
[ -z "$DEVICE2" ] && [ -b "/dev/disk2" ] && DEVICE2="/dev/disk2"
[ -z "$DEVICE3" ] && [ -b "/dev/disk3" ] && DEVICE3="/dev/disk3"
[ -z "$DEVICE4" ] && [ -b "/dev/disk4" ] && DEVICE4="/dev/disk4"
DISK_FILES=( "$DISK1_FILE" "$DISK2_FILE" "$DISK3_FILE" "$DISK4_FILE" )
DISK_DESCS=( "disk" "disk2" "disk3" "disk4" )
DISK_SIZES=( "$DISK_SIZE" "$DISK2_SIZE" "$DISK3_SIZE" "$DISK4_SIZE" )
DISK_DEVICES=( "$DEVICE" "$DEVICE2" "$DEVICE3" "$DEVICE4" )
DISK_INDEXES=( "3" "4" "5" "6" )
DISK_ADDRESSES=( "0xc" "0xd" "0xe" "0xf" )
for i in "${!DISK_FILES[@]}"; do
if [ -n "${DISK_DEVICES[i]}" ]; then
addDevice "${DISK_DEVICES[i]}" "$DISK_TYPE" "${DISK_INDEXES[i]}" "${DISK_ADDRESSES[i]}" || exit $?
else
addDisk "${DISK_FILES[i]}" "$DISK_TYPE" "${DISK_DESCS[i]}" "${DISK_SIZES[i]}" "${DISK_INDEXES[i]}" "${DISK_ADDRESSES[i]}" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
fi
done
finishDisks
return 0