mirror of
https://github.com/vdsm/virtual-dsm.git
synced 2026-11-11 08:42:42 +01:00
663 lines
17 KiB
Bash
663 lines
17 KiB
Bash
#!/usr/bin/env bash
|
||
set -Eeuo pipefail
|
||
|
||
: "${TZ:=""}" # System timezone
|
||
: "${COUNTRY:=""}" # Country code for mirror
|
||
: "${URL:=""}" # URL of the PAT file to be downloaded.
|
||
|
||
TZ=$(strip "$TZ")
|
||
COUNTRY=$(strip "$COUNTRY")
|
||
|
||
checkDsmFilesystem() {
|
||
|
||
local fs="$1"
|
||
|
||
if [[ "${fs,,}" == "overlay"* && "${ENGINE,,}" == "docker" ]]; then
|
||
warn "the filesystem of $STORAGE is OverlayFS, this usually means it was binded to an invalid path!"
|
||
fi
|
||
|
||
if [[ "${fs,,}" == "fuse"* ]]; then
|
||
warn "the filesystem of $STORAGE is FUSE, this extra layer will negatively affect performance!"
|
||
fi
|
||
|
||
if [[ "${fs,,}" == "ecryptfs" || "${fs,,}" == "tmpfs" ]]; then
|
||
warn "the filesystem of $STORAGE is $fs, which does not support O_DIRECT mode, adjusting settings..."
|
||
fi
|
||
|
||
if [[ "${fs,,}" == "fat"* || "${fs,,}" == "vfat"* || "${fs,,}" == "msdos"* ]]; then
|
||
error "Unable to install on $fs filesystems, please use a different filesystem for /storage."
|
||
return 61
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
checkDsmSpace() {
|
||
|
||
local rootSpace=536870912
|
||
local minSpace=15032385536
|
||
local space available
|
||
|
||
space=$(df --output=avail -B 1 / | tail -n 1) || return $?
|
||
available=$(formatBytes "$space" "down") || return $?
|
||
|
||
if (( rootSpace > space )); then
|
||
error "Not enough free space inside the container, have $available available but need at least 500 MB."
|
||
return 96
|
||
fi
|
||
|
||
space=$(df --output=avail -B 1 "$STORAGE" | tail -n 1) || return $?
|
||
available=$(formatBytes "$space") || return $?
|
||
|
||
if (( minSpace > space )); then
|
||
error "Not enough free space for installation in $STORAGE, have $available available but need at least 14 GB."
|
||
return 94
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
downloadFile() {
|
||
|
||
local url="$1"
|
||
local pat="$2"
|
||
local msg="$3"
|
||
local connections="${4:-1}"
|
||
|
||
downloadToFile \
|
||
"$url" \
|
||
"$pat" \
|
||
"$msg" \
|
||
"0" \
|
||
"$connections" \
|
||
"Y"
|
||
}
|
||
|
||
downloadPat() {
|
||
|
||
local pat="$1"
|
||
local msg err
|
||
|
||
if [[ "$URL" == "file://"* ]]; then
|
||
msg="Copying DSM"
|
||
err="Failed to copy ${URL:7}"
|
||
info "Install: Copying installation image..."
|
||
html "$msg..." || return $?
|
||
|
||
rm -f "$pat" || return $?
|
||
|
||
if [ ! -f "${URL:7}" ]; then
|
||
error "File '${URL:7}' does not exist!"
|
||
return 65
|
||
fi
|
||
|
||
cp "${URL:7}" "$pat" || return $?
|
||
|
||
else
|
||
|
||
msg="Downloading DSM"
|
||
err="Failed to download $URL"
|
||
info "Install: Downloading $BASE.pat..."
|
||
|
||
downloadRetry \
|
||
"$pat" \
|
||
"$CONNECTIONS" \
|
||
"5" \
|
||
"$BASE.pat" \
|
||
"0" \
|
||
"$URL" \
|
||
"$pat" \
|
||
"$msg" || return 69
|
||
fi
|
||
|
||
if [ ! -s "$pat" ]; then
|
||
error "$err"
|
||
return 69
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
extractPat() {
|
||
|
||
local pat="$1"
|
||
local tmp="$2"
|
||
local size="$3"
|
||
local msg="Extracting installation image..."
|
||
local rc
|
||
|
||
info "Install: $msg" && html "$msg" || return $?
|
||
/run/progress.sh "$tmp" "$size" "$msg ([P])..." &
|
||
|
||
# Newer PAT files are normal tar archives; older encrypted/proprietary forms
|
||
# require the bundled extractor as a compatibility fallback.
|
||
if { tar tf "$pat"; } >/dev/null 2>&1; then
|
||
|
||
tar xpf "$pat" -C "$tmp/." || {
|
||
rc=$?
|
||
fKill "progress.sh"
|
||
return "$rc"
|
||
}
|
||
|
||
else
|
||
|
||
{ (cd "$tmp" && python3 /run/extract.py -i "$pat" -d 2>/run/extract.log); rc=$?; } || :
|
||
|
||
if (( rc != 0 )); then
|
||
fKill "progress.sh"
|
||
cat /run/extract.log
|
||
error "Failed to extract PAT file, reason $rc"
|
||
return 63
|
||
fi
|
||
|
||
fi
|
||
|
||
fKill "progress.sh"
|
||
return 0
|
||
}
|
||
|
||
createSystemImage() {
|
||
|
||
local tmp="$1"
|
||
local fs="$2"
|
||
local msg="Preparing system partition..."
|
||
|
||
info "Install: $msg" && html "$msg" || return $?
|
||
|
||
# The PAT boot archive becomes the persistent QEMU boot disk after its
|
||
# companion system partition has been assembled.
|
||
DSM_BOOT=$(find "$tmp" -name "*.bin.zip" -print -quit) || return $?
|
||
|
||
if [ -z "$DSM_BOOT" ]; then
|
||
error "The PAT file contains no boot image."
|
||
return 67
|
||
fi
|
||
|
||
if [ ! -s "$DSM_BOOT" ]; then
|
||
error "The PAT boot image archive is empty."
|
||
return 67
|
||
fi
|
||
|
||
local bootTmp="$tmp/boot" bootBase
|
||
bootBase="$(basename "${DSM_BOOT%.zip}")"
|
||
|
||
rm -rf "$bootTmp" || return $?
|
||
|
||
if ! makeDir "$bootTmp"; then
|
||
error "Failed to create directory \"$bootTmp\" !"
|
||
return 93
|
||
fi
|
||
|
||
if [[ "${fs,,}" == "btrfs" ]]; then
|
||
{ chattr +C "$bootTmp"; } || :
|
||
fi
|
||
|
||
7z x -y -o"$bootTmp" "$DSM_BOOT" >/dev/null || return $?
|
||
DSM_BOOT="$bootTmp/$bootBase"
|
||
|
||
if [ ! -s "$DSM_BOOT" ]; then
|
||
error "The PAT boot image could not be extracted."
|
||
return 67
|
||
fi
|
||
|
||
if [[ "${fs,,}" == "btrfs" ]]; then
|
||
local attrs
|
||
attrs=$(lsattr "$DSM_BOOT") || return $?
|
||
if [[ "$attrs" != *"C"* ]]; then
|
||
error "Failed to disable COW for boot image $DSM_BOOT on ${fs^^} filesystem."
|
||
fi
|
||
fi
|
||
|
||
SYSTEM="$STORAGE/$BASE.system.img"
|
||
rm -f "$SYSTEM" || return $?
|
||
|
||
# Check free diskspace
|
||
local systemSize=10738466816
|
||
local space available
|
||
|
||
space=$(df --output=avail -B 1 "$STORAGE" | tail -n 1) || return $?
|
||
available=$(formatBytes "$space") || return $?
|
||
|
||
if (( systemSize > space )); then
|
||
error "Not enough free space in $STORAGE to create a 10 GB system disk, have only $available available."
|
||
return 97
|
||
fi
|
||
|
||
if ! touch "$SYSTEM"; then
|
||
error "Could not create file $SYSTEM for the system disk."
|
||
return 98
|
||
fi
|
||
|
||
setOwner "$SYSTEM" || warn "failed to set the owner for \"$SYSTEM\" !"
|
||
|
||
if [[ "${fs,,}" == "btrfs" ]]; then
|
||
{ chattr +C "$SYSTEM"; } || :
|
||
local attrs
|
||
attrs=$(lsattr "$SYSTEM") || return $?
|
||
if [[ "$attrs" != *"C"* ]]; then
|
||
error "Failed to disable COW for system image $SYSTEM on ${fs^^} filesystem."
|
||
fi
|
||
fi
|
||
|
||
if ! fallocate -l "$systemSize" "$SYSTEM" &>/dev/null; then
|
||
if ! fallocate -l -x "$systemSize" "$SYSTEM"; then
|
||
if ! truncate -s "$systemSize" "$SYSTEM"; then
|
||
rm -f "$SYSTEM"
|
||
error "Could not allocate file $SYSTEM for the system disk."
|
||
return 98
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# Recreate Synology's expected DOS partition layout inside the fixed 10 GiB
|
||
# system image before populating the ext4 root partition.
|
||
local part="$tmp/partition.fdisk"
|
||
|
||
if ! {
|
||
echo "label: dos"
|
||
echo "label-id: 0x6f9ee2e9"
|
||
echo "device: $SYSTEM"
|
||
echo "unit: sectors"
|
||
echo "sector-size: 512"
|
||
echo ""
|
||
echo "${SYSTEM}1 : start= 2048, size= 16777216, type=83"
|
||
echo "${SYSTEM}2 : start= 16779264, size= 4194304, type=82"
|
||
} > "$part"; then
|
||
return 1
|
||
fi
|
||
|
||
sfdisk -q "$SYSTEM" < "$part" || return $?
|
||
|
||
local mount="$tmp/system"
|
||
rm -rf "$mount" || return $?
|
||
|
||
if ! makeDir "$mount"; then
|
||
error "Failed to create directory \"$mount\" !"
|
||
return 93
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
installSystemPartition() {
|
||
|
||
local tmp="$1"
|
||
local mount="$tmp/system"
|
||
local msg="Extracting system partition..."
|
||
|
||
info "Install: $msg" && html "$msg" || return $?
|
||
|
||
local hda="$tmp/hda1"
|
||
local idb="$tmp/indexdb"
|
||
local pkg="$tmp/packages"
|
||
local hdp="$tmp/synohdpack_img"
|
||
|
||
if [ ! -s "$hda.tgz" ]; then
|
||
error "The PAT file contains no OS image."
|
||
return 64
|
||
fi
|
||
|
||
mv "$hda.tgz" "$hda.txz" || return $?
|
||
|
||
if [ -d "$pkg" ]; then
|
||
mv "$pkg/" "$mount/.SynoUpgradePackages/" || return $?
|
||
fi
|
||
|
||
rm -f "$mount/.SynoUpgradePackages/ActiveInsight-"* || return $?
|
||
|
||
local indexDb="$mount/usr/syno/synoman/indexdb"
|
||
|
||
if [ -s "$idb.txz" ]; then
|
||
mkdir -p "$indexDb" || return $?
|
||
fi
|
||
|
||
local label="1.44.1-42218"
|
||
local offset="1048576" # 2048 * 512
|
||
local numBlocks="2097152" # (16777216 * 512) / 4096
|
||
local rc
|
||
msg="Installing system partition..."
|
||
|
||
# Build the ext4 filesystem directly from the extracted tree under fakeroot,
|
||
# preserving archive ownership without mounting a loop device.
|
||
if fakeroot -- bash -c "set -Eeu;\
|
||
[ -s $hdp.txz ] && tar xpfJ $hdp.txz --absolute-names -C $mount/;\
|
||
[ -s $idb.txz ] && tar xpfJ $idb.txz --absolute-names -C $indexDb/;\
|
||
tar xpfJ $hda.txz --absolute-names --skip-old-files -C $mount/;\
|
||
printf '%b%s%b' '\E[1;34m❯ \E[1;36m' 'Install: $msg' '\E[0m\n';\
|
||
mke2fs -q -t ext4 -b 4096 -d $mount/ -L $label -F -E offset=$offset $SYSTEM $numBlocks"; then
|
||
:
|
||
else
|
||
rc=$?
|
||
return "$rc"
|
||
fi
|
||
|
||
rm -rf "$mount" || return $?
|
||
return 0
|
||
}
|
||
|
||
checkSse42() {
|
||
|
||
if disabled "$KVM" || [[ "${PLATFORM,,}" != "x64" ]]; then
|
||
return 0
|
||
fi
|
||
|
||
if ! hasFlag "sse4_2"; then
|
||
error "Your CPU does not have the SSE4.2 instruction set that Virtual DSM requires!"
|
||
enabled "$DEBUG" || return 88
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
sanitizePatBase() {
|
||
|
||
local source="$1"
|
||
local base
|
||
|
||
base=$(basename "${source%%\?*}" .pat) || return 1
|
||
printf -v base '%b' "${base//%/\\x}" || return 1
|
||
base="${base//[!A-Za-z0-9._-]/_}"
|
||
|
||
printf '%s' "$base"
|
||
}
|
||
|
||
reservePorts() {
|
||
|
||
local port ports=""
|
||
local hostPorts="${HOST_PORTS:-}"
|
||
|
||
# Older configurations may reserve DSM's web ports for the container.
|
||
# They now need to be forwarded to the guest instead.
|
||
for port in ${hostPorts//,/ }; do
|
||
|
||
case "${port,,}" in
|
||
5000|5000/tcp|5001|5001/tcp)
|
||
continue ;;
|
||
esac
|
||
|
||
ports+="${ports:+,}$port"
|
||
|
||
done
|
||
|
||
HOST_PORTS="$ports"
|
||
|
||
# NAT can fall back to user-mode networking after this point,
|
||
# so always add the DSM web interface ports for non-DHCP networking.
|
||
USER_PORTS="${USER_PORTS:+$USER_PORTS,}5000/tcp,5001/tcp,80/tcp,443/tcp,445/tcp"
|
||
|
||
return 0
|
||
}
|
||
|
||
prepareMac() {
|
||
|
||
local file="$STORAGE/$PROCESS.mac"
|
||
local legacy="$STORAGE/dsm.mac"
|
||
local container mac=""
|
||
|
||
# An explicitly configured MAC remains network.sh's responsibility.
|
||
[ -n "${MAC:-}" ] && return 0
|
||
[ -s "$file" ] && return 0
|
||
|
||
# Preserve the address generated by older Virtual DSM releases.
|
||
if [ -s "$legacy" ]; then
|
||
mac=$(readFile "$legacy") || return $?
|
||
fi
|
||
|
||
if [ -z "$mac" ]; then
|
||
container=$(containerID) || return $?
|
||
mac=$(echo "$container" | md5sum |
|
||
sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:11:32:\3:\4:\5/') || return $?
|
||
fi
|
||
|
||
if ! writeState "mac" "${mac^^}"; then
|
||
error "Failed to write MAC address to \"$file\" !"
|
||
return 28
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
getCountry() {
|
||
|
||
local url=$1
|
||
local query=$2
|
||
local json result
|
||
|
||
{ json=$(curl -m 5 -H "Accept: application/json" -sfk "$url"); local rc=$?; } || :
|
||
(( rc != 0 )) && return 0
|
||
|
||
{ result=$(echo "$json" | jq -r "$query" 2> /dev/null); rc=$?; } || :
|
||
(( rc != 0 )) && return 0
|
||
|
||
[[ ${#result} -ne 2 ]] && return 0
|
||
[[ "${result^^}" == "XX" ]] && return 0
|
||
|
||
COUNTRY="${result^^}"
|
||
|
||
return 0
|
||
}
|
||
|
||
setCountry() {
|
||
|
||
[[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN"
|
||
[[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN"
|
||
[[ "${TZ,,}" == "asia/urumqi" ]] && COUNTRY="CN"
|
||
[[ "${TZ,,}" == "asia/kashgar" ]] && COUNTRY="CN"
|
||
[[ "${TZ,,}" == "asia/shanghai" ]] && COUNTRY="CN"
|
||
[[ "${TZ,,}" == "asia/chongqing" ]] && COUNTRY="CN"
|
||
|
||
# Country detection is best-effort and tries independent services in order;
|
||
# failure leaves mirror selection at its global default.
|
||
[ -z "$COUNTRY" ] && getCountry "https://api.ipapi.is" ".location.country_code"
|
||
[ -z "$COUNTRY" ] && getCountry "https://ifconfig.co/json" ".country_iso"
|
||
[ -z "$COUNTRY" ] && getCountry "https://api.ip2location.io" ".country_code"
|
||
[ -z "$COUNTRY" ] && getCountry "https://ipinfo.io/json" ".country"
|
||
[ -z "$COUNTRY" ] && getCountry "https://api.ipquery.io/?format=json" ".location.country_code"
|
||
[ -z "$COUNTRY" ] && getCountry "https://api.myip.com" ".cc"
|
||
|
||
return 0
|
||
}
|
||
|
||
finishDsmInstall() {
|
||
|
||
local pat="$1"
|
||
local tmp="$2"
|
||
|
||
echo "$BASE" > "$STORAGE/dsm.ver" || return 1
|
||
setOwner "$STORAGE/dsm.ver" || warn "failed to set the owner for \"$STORAGE/dsm.ver\" !"
|
||
|
||
# Do not keep a second copy when the source PAT already lives in storage;
|
||
# downloaded or externally mounted sources are cached for later reuse.
|
||
if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then
|
||
rm -f "$pat" || return $?
|
||
else
|
||
mv -f "$pat" "$STORAGE/$BASE.pat" || return $?
|
||
fi
|
||
|
||
if [ -f "$STORAGE/$BASE.pat" ]; then
|
||
setOwner "$STORAGE/$BASE.pat" || warn "failed to set the owner for \"$STORAGE/$BASE.pat\" !"
|
||
fi
|
||
|
||
mv -f "$DSM_BOOT" "$STORAGE/$BASE.boot.img" || return $?
|
||
setOwner "$STORAGE/$BASE.boot.img" || warn "failed to set the owner for \"$STORAGE/$BASE.boot.img\" !"
|
||
|
||
rm -rf "$tmp" || return $?
|
||
return 0
|
||
}
|
||
|
||
installDSM() {
|
||
|
||
rm -rf /tmp/dsm || return $?
|
||
rm -f "$QEMU_DIR/dsm.url" || return $?
|
||
|
||
checkSse42 || return $?
|
||
reservePorts || return $?
|
||
prepareMac || return $?
|
||
|
||
local patName="boot.pat"
|
||
local patDir patFile
|
||
|
||
# Persist the exact PAT base name so future starts reopen the matching boot,
|
||
# system, and cached installation files.
|
||
if [ -f "$STORAGE/dsm.ver" ]; then
|
||
BASE=$(<"$STORAGE/dsm.ver") || return $?
|
||
BASE="${BASE//[![:print:]]/}"
|
||
[ -z "$BASE" ] && BASE="DSM_VirtualDSM_69057"
|
||
else
|
||
# Fallback for old installs
|
||
BASE="DSM_VirtualDSM_42962"
|
||
fi
|
||
|
||
patDir=$(find / -maxdepth 1 -type d -iname "$patName" -print -quit) || return $?
|
||
|
||
if [ ! -d "$patDir" ]; then
|
||
patDir=$(find "$STORAGE" -maxdepth 1 -type d -iname "$patName" -print -quit) || return $?
|
||
fi
|
||
|
||
# A boot.pat directory bind represents already extracted boot and system
|
||
# images and therefore takes precedence over PAT file or URL discovery.
|
||
if [ -d "$patDir" ]; then
|
||
BASE="DSM_VirtualDSM"
|
||
URL="file://$patDir"
|
||
|
||
if [[ ! -s "$STORAGE/$BASE.boot.img" || ! -s "$STORAGE/$BASE.system.img" ]]; then
|
||
error "The bind $patDir maps to a file that does not exist!"
|
||
return 65
|
||
fi
|
||
fi
|
||
|
||
patFile=$(find / -maxdepth 1 -type f -iname "$patName" -print -quit) || return $?
|
||
|
||
if [ ! -s "$patFile" ]; then
|
||
patFile=$(find "$STORAGE" -maxdepth 1 -type f -iname "$patName" -print -quit) || return $?
|
||
fi
|
||
|
||
if [ -s "$patFile" ]; then
|
||
BASE="DSM_VirtualDSM"
|
||
URL="file://$patFile"
|
||
fi
|
||
|
||
URL=$(strip "$URL") || return $?
|
||
|
||
# Derive a filesystem-safe identity from the URL only when no local boot.pat
|
||
# source was supplied; preserve an existing system image identity if present.
|
||
if [ -n "$URL" ] && [ ! -s "$patFile" ] && [ ! -d "$patDir" ]; then
|
||
BASE=$(basename "$URL" .pat) || return $?
|
||
|
||
if [ ! -s "$STORAGE/$BASE.system.img" ]; then
|
||
BASE=$(sanitizePatBase "$URL") || return $?
|
||
fi
|
||
|
||
if [[ "${URL,,}" != "http"* && "${URL,,}" != "file:"* ]]; then
|
||
if [ ! -s "$STORAGE/$BASE.pat" ]; then
|
||
error "Invalid URL: $URL"
|
||
return 65
|
||
fi
|
||
|
||
URL="file://$STORAGE/$BASE.pat"
|
||
fi
|
||
fi
|
||
|
||
# A complete matching image pair is the installation marker; the cached PAT
|
||
# itself is optional after installation.
|
||
if [[ -s "$STORAGE/$BASE.boot.img" && -s "$STORAGE/$BASE.system.img" ]]; then
|
||
return 0 # Previous installation found
|
||
fi
|
||
|
||
html "Please wait while Virtual DSM is being installed..." || return $?
|
||
|
||
local mirror=""
|
||
local chinaMirror="https://cndl.synology.cn/download/DSM"
|
||
local globalMirror="https://global.synologydownload.com/download/DSM"
|
||
|
||
[[ "${URL,,}" == *"cndl.synology"* ]] && mirror="$chinaMirror"
|
||
[[ "${URL,,}" == *"global.synology"* ]] && mirror="$globalMirror"
|
||
|
||
# Honor an explicitly selected Synology mirror first, otherwise choose the
|
||
# China or global endpoint from the detected country.
|
||
if [ -z "$mirror" ]; then
|
||
if [ -z "$COUNTRY" ]; then
|
||
setCountry || return $?
|
||
fi
|
||
|
||
[ -z "$COUNTRY" ] && info "Warning: could not detect country to select mirror!"
|
||
[[ "${COUNTRY^^}" == "CN" ]] && mirror="$chinaMirror" || mirror="$globalMirror"
|
||
fi
|
||
|
||
if [ -z "$URL" ]; then
|
||
URL="$mirror/release/7.2.2/72806/DSM_VirtualDSM_72806.pat"
|
||
fi
|
||
|
||
if [ ! -s "$patFile" ]; then
|
||
BASE=$(sanitizePatBase "$URL") || return $?
|
||
fi
|
||
|
||
if [[ "$URL" != "file://$STORAGE/$BASE.pat" ]]; then
|
||
rm -f "$STORAGE/$BASE.pat" || return $?
|
||
fi
|
||
|
||
rm -f "$STORAGE/$BASE.agent" || return $?
|
||
rm -f "$STORAGE/$BASE.boot.img" || return $?
|
||
rm -f "$STORAGE/$BASE.system.img" || return $?
|
||
|
||
# Check filesystem
|
||
local fs
|
||
fs=$(stat -f -c %T "$STORAGE") || return $?
|
||
checkDsmFilesystem "$fs" || return $?
|
||
|
||
local tmp
|
||
|
||
# Extract beside storage on Unix filesystems to avoid container-space limits;
|
||
# use /tmp for filesystems that cannot safely host the installer workspace.
|
||
if [[ "${fs,,}" != "exfat"* && "${fs,,}" != "ntfs"* && "${fs,,}" != "unknown"* ]]; then
|
||
tmp="$STORAGE/tmp"
|
||
rm -rf "$tmp" || return $?
|
||
|
||
if ! makeDir "$tmp"; then
|
||
error "Failed to create directory \"$tmp\" !"
|
||
return 93
|
||
fi
|
||
else
|
||
tmp="/tmp/dsm"
|
||
local tmpSpace=2147483648
|
||
local space available
|
||
|
||
space=$(df --output=avail -B 1 /tmp | tail -n 1) || return $?
|
||
available=$(formatBytes "$space") || return $?
|
||
|
||
if (( tmpSpace > space )); then
|
||
error "Not enough free space inside the container, have $available available but need at least 2 GB."
|
||
return 93
|
||
fi
|
||
|
||
rm -rf "$tmp" || return $?
|
||
mkdir -p "$tmp" || return $?
|
||
fi
|
||
|
||
# Check free diskspace
|
||
checkDsmSpace || return $?
|
||
|
||
local pat="/$BASE.pat"
|
||
downloadPat "$pat" || return $?
|
||
|
||
local size
|
||
size=$(stat -c%s "$pat") || return $?
|
||
|
||
# Full Virtual DSM PAT files are substantially larger than update packs;
|
||
# reject undersized inputs before attempting destructive image preparation.
|
||
if (( size < 250000000 )); then
|
||
error "The specified PAT file is probably an update pack as it's too small."
|
||
return 62
|
||
fi
|
||
|
||
extractPat "$pat" "$tmp" "$size" || return $?
|
||
createSystemImage "$tmp" "$fs" || return $?
|
||
installSystemPartition "$tmp" || return $?
|
||
finishDsmInstall "$pat" "$tmp" || return $?
|
||
|
||
return 0
|
||
}
|
||
|
||
installDSM || exit $?
|
||
return 0
|