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:
Mikhail Chusavitin
2026-09-04 15:38:14 +03:00
co-authored by Claude Sonnet 5
parent bc57b85d3f
commit b8c45d54c1
13 changed files with 1056 additions and 13 deletions
+56
View File
@@ -79,8 +79,47 @@ export GOCACHE GOMODCACHE
. "${BUILDER_DIR}/lib/iso-validation.sh"
. "${BUILDER_DIR}/lib/template.sh"
. "${BUILDER_DIR}/lib/bootloader.sh"
. "${BUILDER_DIR}/lib/squashfs-layers.sh"
. "${BUILDER_DIR}/lib/fast-path.sh"
# Whether this variant ships a multi-layer live medium (semantic squashfs
# layers + live/filesystem.module) instead of one monolithic squashfs.
case "${BUILD_VARIANT}" in
nvidia|nvidia-legacy) BEE_MULTILAYER_SQUASHFS=1 ;;
*) BEE_MULTILAYER_SQUASHFS=0 ;;
esac
# bee_split_squashfs_layers <monolith_squashfs> <live_dir>
# Deterministically replace the monolith with the semantic layer set. Runs as a
# set -e subshell: any failed sub-step aborts the build (via run_step) before the
# outer ISO is assembled, so a partial layer set is never published.
bee_split_squashfs_layers() (
set -e
_mono="$1"
_live="$2"
[ -f "${_mono}" ] || { echo "ERROR: monolith squashfs not found: ${_mono}" >&2; exit 1; }
_wd="$(mktemp -d "${CACHE_ROOT}/layer-split-${BUILD_VARIANT}.XXXXXX")"
trap 'rm -rf "${_wd}"' EXIT
_root="${_wd}/root"
_cls="${_wd}/cls"
echo "=== splitting $(basename "${_mono}") into semantic layers ==="
unsquashfs -d "${_root}" "${_mono}"
bee_layer_classify "${_root}" "${BUILD_VARIANT}" "${_cls}"
bee_layer_build "${_root}" "${_cls}" "${_live}" "${PROJECT_VERSION_EFFECTIVE}" "${BUILD_VARIANT}"
bee_layer_verify_each "${_live}" "${PROJECT_VERSION_EFFECTIVE}" "${BUILD_VARIANT}"
bee_layer_merge "${_live}" "${PROJECT_VERSION_EFFECTIVE}" "${BUILD_VARIANT}" "${_wd}/merged"
bee_layer_write_module_file "${_live}" "${PROJECT_VERSION_EFFECTIVE}" "${BUILD_VARIANT}"
# Publish the ownership map on the medium for bee-install / debugging, then
# drop the monolith - only now that every layer exists and verifies.
cp "${_cls}/classify-report.txt" "${_live}/filesystem.layers.txt"
rm -f "${_mono}"
echo "=== semantic layer split complete ==="
ls -la "${_live}"/filesystem-v*.squashfs "${_live}/filesystem.module"
)
resolve_project_version() {
if [ -n "${BEE_VERSION:-}" ]; then
echo "${BEE_VERSION}"
@@ -185,6 +224,10 @@ LOG_OUT="${LOG_DIR}/build.log"
start_build_log
# Fail fast on builder-library regressions before the 30-60 min ISO build.
run_step "builder library tests" "01-lib-tests" sh "${BUILDER_DIR}/test-build-libs.sh"
run_step "squashfs layer tests" "02-squashfs-layer-tests" sh "${BUILDER_DIR}/test-squashfs-layers.sh"
# Auto-detect kernel ABI: refresh apt index, then query current linux-image-amd64 dependency.
# If headers for the detected ABI are not yet installed (kernel updated since image build),
# install them on the fly so NVIDIA modules and ISO kernel always match.
@@ -649,6 +692,8 @@ if ! needs_full_build; then
validate_iso_grub_assets "$ISO_RAW"
validate_iso_nvidia_runtime "$ISO_RAW"
validate_iso_rootfs_layout "$ISO_RAW"
validate_iso_squashfs_layers "$ISO_RAW"
validate_iso_media_integrity "$ISO_RAW"
cp "$ISO_RAW" "$ISO_OUT"
echo ""
echo "=== done (${BUILD_VARIANT}, fast-path) ==="
@@ -677,6 +722,15 @@ if [ -f "${_std_sq}" ] && [ "${_std_sq}" != "${_ver_sq}" ]; then
mv "${_std_sq}" "${_ver_sq}"
echo "=== squashfs renamed: filesystem.squashfs -> ${SQUASHFS_FILENAME} ==="
fi
# Split the monolith into semantic layers before checksums / ISO assembly so
# md5sum.txt covers the layers and the module file, and the outer ISO carries
# them. Single-layer variants (amd, nogpu) keep the monolith untouched.
if [ "${BEE_MULTILAYER_SQUASHFS}" = "1" ]; then
run_step "split squashfs into semantic layers" "90b-squashfs-layers" \
bee_split_squashfs_layers "${_ver_sq}" "${LB_DIR}/binary/live"
fi
reset_live_build_stage "${LB_DIR}" "binary_checksums"
reset_live_build_stage "${LB_DIR}" "binary_iso"
reset_live_build_stage "${LB_DIR}" "binary_zsync"
@@ -711,6 +765,8 @@ if [ -f "$ISO_RAW" ]; then
validate_iso_grub_assets "$ISO_RAW"
validate_iso_nvidia_runtime "$ISO_RAW"
validate_iso_rootfs_layout "$ISO_RAW"
validate_iso_squashfs_layers "$ISO_RAW"
validate_iso_media_integrity "$ISO_RAW"
cp "$ISO_RAW" "$ISO_OUT"
hash_heavy_config > "${FULL_BUILD_HASH_FILE}.new"
printf '%s\n' "${DEBIAN_KERNEL_ABI}" > "${FULL_BUILD_ABI_FILE}.new"