feat(iso): split the live medium into semantic SquashFS layers
Booting via BMC virtual CD reads the ~2.8 GB filesystem squashfs sequentially during the live-boot toram copy; a mid-read drop of the redirected medium loses the whole copy and fails the boot (v14). Split the rootfs into self-contained semantic layers so a retry re-reads at most one ~500-700 MiB layer, not everything. This is a resilience / reduced-re-read mechanism, not a fix for the virtual-media instability. NVIDIA variants now ship 7 layers (00-base, 05-firmware, 08-desktop, 10-nvidia-driver, 20-nvidia-platform, 30-nvidia-cuda-libs, 40-nvidia-dcgm-cuda) plus an explicit live/filesystem.module that fixes their OverlayFS order; amd/nogpu keep a single squashfs. - lib/squashfs-layers.sh: deterministic classifier (dpkg file ownership plus explicit rules for build.sh-injected files, never a path substring), per-layer mksquashfs, 800 MiB hard ceiling, unsquashfs -s plus strict extraction of every layer, merged-rootfs bootability check. - build.sh: split the monolith after the full lb build, verify and merge, write the module file, delete the monolith only then; abort before ISO assembly on any failure. Runs the builder test suites up front. - fast-path: force a full build for a multi-layer medium; fast_path_repack_squashfs hard-refuses (it would drop layers). - iso-validation.sh: validate_iso_squashfs_layers (module vs layer set match, size ceiling, no lone giant squashfs) and validate_iso_media_integrity (xorriso -check_media). - bee-install: honour filesystem.module order, abort on any layer failure. - 9013-toram-retry: record the real rsync exit code (it printed a false rc=0) and correct the "resumes the tail" comment (rsync without --partial keeps only fully-copied layers). No unsafe partial resume. - tests: test-squashfs-layers.sh plus a multi-layer guard in test-build-libs.sh; both run at the top of every build. - docs: bible-local architecture and decision, iso/README, iso-build-rules. Verified by a full nvidia build: 7 layers 622/199/256/466/37/567/562 MiB, every validator passes, xorriso -check_media good, merged rootfs bootable. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
bc57b85d3f
commit
b8c45d54c1
@@ -645,6 +645,110 @@ rootfs_layout_fail() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
squashfs_layers_fail() {
|
||||
echo "ERROR: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Validate the semantic squashfs layering in the final ISO:
|
||||
# - NVIDIA variants must ship live/filesystem.module and >= 2 layers
|
||||
# - every squashfs in live/ is listed in the module file and vice versa
|
||||
# - no single layer exceeds the agreed compressed-size ceiling
|
||||
# - a single giant squashfs is rejected for a multi-layer variant
|
||||
validate_iso_squashfs_layers() {
|
||||
iso_path="$1"
|
||||
echo "=== validating squashfs layers in ISO ==="
|
||||
|
||||
[ -f "$iso_path" ] || squashfs_layers_fail "ISO not found for squashfs layer validation: $iso_path"
|
||||
require_iso_reader "$iso_path" >/dev/null 2>&1 || squashfs_layers_fail "ISO reader unavailable for squashfs layer validation"
|
||||
|
||||
_files="$(mktemp)"
|
||||
_module="$(mktemp)"
|
||||
iso_list_files "$iso_path" > "$_files" || squashfs_layers_fail "failed to list ISO files"
|
||||
|
||||
_sq_in_iso="$(grep -E '^live/filesystem.*\.squashfs$' "$_files" | sed 's#^live/##' | LC_ALL=C sort)"
|
||||
_sq_count="$(printf '%s\n' "$_sq_in_iso" | grep -c . || true)"
|
||||
_has_module=0
|
||||
grep -qx 'live/filesystem.module' "$_files" && _has_module=1
|
||||
|
||||
if [ "${BEE_MULTILAYER_SQUASHFS:-0}" != "1" ]; then
|
||||
echo "=== single-layer variant: $_sq_count squashfs, module file present=$_has_module ==="
|
||||
[ "$_has_module" = 0 ] || squashfs_layers_fail "single-layer variant unexpectedly ships filesystem.module"
|
||||
rm -f "$_files" "$_module"
|
||||
echo "=== squashfs layer validation OK (single layer) ==="
|
||||
return 0
|
||||
fi
|
||||
|
||||
[ "$_has_module" = 1 ] || squashfs_layers_fail "multi-layer variant is missing live/filesystem.module (monolith slipped through)"
|
||||
iso_read_member "$iso_path" live/filesystem.module "$_module" || squashfs_layers_fail "failed to read live/filesystem.module from ISO"
|
||||
|
||||
_listed="$(grep -vE '^[[:space:]]*(#|$)' "$_module" | LC_ALL=C sort)"
|
||||
_listed_count="$(printf '%s\n' "$_listed" | grep -c . || true)"
|
||||
[ "$_listed_count" -ge 2 ] || squashfs_layers_fail "filesystem.module lists only $_listed_count layer(s), expected >= 2"
|
||||
|
||||
if [ "$(printf '%s\n' "$_listed")" != "$(printf '%s\n' "$_sq_in_iso")" ]; then
|
||||
echo " module lists:" >&2; printf ' %s\n' $_listed >&2
|
||||
echo " ISO carries:" >&2; printf ' %s\n' $_sq_in_iso >&2
|
||||
squashfs_layers_fail "filesystem.module and the squashfs files in live/ do not match exactly"
|
||||
fi
|
||||
|
||||
_limit_mib="${BEE_LAYER_MAX_MIB:-800}"
|
||||
for _name in $_sq_in_iso; do
|
||||
_bytes=""
|
||||
if command -v xorriso >/dev/null 2>&1; then
|
||||
_bytes="$(xorriso -indev "$iso_path" -lsl "/live/${_name}" 2>/dev/null \
|
||||
| awk '$1 ~ /^-/ { print $5; exit }')"
|
||||
fi
|
||||
if ! printf '%s' "$_bytes" | grep -Eq '^[0-9]+$'; then
|
||||
_tmp_sq="$(mktemp)"
|
||||
iso_read_member "$iso_path" "live/${_name}" "$_tmp_sq" || squashfs_layers_fail "failed to extract live/${_name}"
|
||||
_bytes="$(wc -c < "$_tmp_sq" | tr -d ' ')"
|
||||
rm -f "$_tmp_sq"
|
||||
fi
|
||||
_mib=$(( _bytes / 1048576 ))
|
||||
printf ' %-40s %5d MiB\n' "$_name" "$_mib"
|
||||
[ "$_mib" -le "$_limit_mib" ] || squashfs_layers_fail "layer ${_name} is ${_mib} MiB, over the ${_limit_mib} MiB ceiling"
|
||||
case "$_name" in
|
||||
*-00-base.squashfs) : ;;
|
||||
*) [ "$_mib" -ge 1 ] || squashfs_layers_fail "layer ${_name} is suspiciously empty" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$_sq_count" -eq 1 ]; then
|
||||
squashfs_layers_fail "multi-layer variant shipped a single squashfs"
|
||||
fi
|
||||
|
||||
rm -f "$_files" "$_module"
|
||||
echo "=== squashfs layer validation OK ($_sq_count layers) ==="
|
||||
}
|
||||
|
||||
# Read the whole raw ISO back and confirm every sector is readable. Catches a
|
||||
# truncated or corrupt image before it ships - the split path rewrites the
|
||||
# squashfs area and reassembles the ISO, so this is worth a full re-read.
|
||||
validate_iso_media_integrity() {
|
||||
iso_path="$1"
|
||||
echo "=== validating ISO media integrity (xorriso -check_media) ==="
|
||||
|
||||
[ -f "$iso_path" ] || { echo "ERROR: ISO not found for media check: $iso_path" >&2; exit 1; }
|
||||
if ! command -v xorriso >/dev/null 2>&1; then
|
||||
echo "WARNING: xorriso not available, skipping -check_media" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
_out="$(xorriso -indev "$iso_path" -check_media 2>&1)" || {
|
||||
printf '%s\n' "$_out" >&2
|
||||
echo "ERROR: xorriso -check_media failed for $iso_path" >&2
|
||||
exit 1
|
||||
}
|
||||
printf '%s\n' "$_out" | grep -E 'Media region|Bad blocks|checked|md5' || true
|
||||
if printf '%s\n' "$_out" | grep -Eiq 'bad block[^s]|[1-9][0-9]* bad blocks|damaged|not readable'; then
|
||||
printf '%s\n' "$_out" >&2
|
||||
echo "ERROR: xorriso -check_media reported unreadable/bad sectors in $iso_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "=== ISO media integrity OK ==="
|
||||
}
|
||||
|
||||
# Guard against a corrupted merged-usr layout in the live rootfs. On Debian
|
||||
# bookworm /bin, /sbin, /lib and /lib64 are symlinks into /usr; if a build step
|
||||
# stages a real directory of that name into the overlay, `rsync -a` replaces the
|
||||
|
||||
Reference in New Issue
Block a user