fix(iso): stop fast-path repack from clobbering merged-usr /lib

The NVIDIA GSP firmware was staged into the overlay at a bare
lib/firmware/nvidia/<ver>/. On a merged-usr Debian root /lib is a symlink
to usr/lib, so the fast-path repack's `rsync -a` (overlay stage -> the
unsquashfs'd tree) replaced that symlink with a plain directory holding
only the firmware. Everything else reachable only through /lib
(/lib/systemd/systemd, /lib/x86_64-linux-gnu/ld-linux + libc, ...) was
orphaned. The squashfs still packed and mounted, but boot panicked:

    run-init: can't execute '/sbin/init': No such file or directory
    run-init: can't execute '/bin/sh': No such file or directory
    Kernel panic - not syncing: Attempted to kill init

Full `lb build` was unaffected (live-build copies includes.chroot with
cp -a, which follows the /lib symlink), so only fast-path ISOs were bad.

Three fixes:
- build.sh: stage GSP firmware under usr/lib/firmware/nvidia/<ver>/, the
  canonical merged-usr path. Changing the overlay path also makes
  overlay_paths_were_removed() force one full build on the next run.
- fast-path.sh: add `rsync --keep-dirlinks` so a real directory in the
  overlay stage can never again replace a symlink-to-directory in the
  root. Verified: firmware lands in usr/lib/firmware, /lib stays a symlink.
- iso-validation.sh: new validate_iso_rootfs_layout, run for every variant
  on both build paths. Fails the build if the squashfs has a plain-dir
  /bin|/sbin|/lib|/lib64, or if /usr/sbin/init is present without a
  resolvable /usr/lib/systemd/systemd and /lib symlink. Verified it flags
  the broken v14.02-1-g642e686 ISO and passes a correct layout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H2LLuid8PFhBqBPcxXxkQU
This commit is contained in:
Mikhail Chusavitin
2026-09-03 15:32:43 +03:00
co-authored by Claude Sonnet 5
parent 489dd2830c
commit eae5570730
3 changed files with 79 additions and 5 deletions
+12 -4
View File
@@ -452,11 +452,17 @@ if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then
mkdir -p "${OVERLAY_STAGE_DIR}/etc/OpenCL/vendors"
printf 'libnvidia-opencl.so.1\n' > "${OVERLAY_STAGE_DIR}/etc/OpenCL/vendors/nvidia.icd"
# Inject GSP firmware into /lib/firmware/nvidia/<version>/
# Inject GSP firmware into /usr/lib/firmware/nvidia/<version>/.
#
# It MUST be staged under usr/lib, never a bare lib/. On a merged-usr Debian
# root /lib is a symlink to usr/lib; staging a real lib/ directory here makes
# `rsync -a` (fast-path repack) replace that symlink with a plain directory,
# which orphans /lib/systemd/systemd, /lib/x86_64-linux-gnu/* and friends and
# panics the boot with "run-init: can't execute '/sbin/init'".
if [ -d "${NVIDIA_CACHE}/firmware" ] && [ "$(ls -A "${NVIDIA_CACHE}/firmware" 2>/dev/null)" ]; then
mkdir -p "${OVERLAY_STAGE_DIR}/lib/firmware/nvidia/${NVIDIA_DRIVER_VERSION}"
cp "${NVIDIA_CACHE}/firmware/"* "${OVERLAY_STAGE_DIR}/lib/firmware/nvidia/${NVIDIA_DRIVER_VERSION}/"
echo "=== firmware: $(ls "${OVERLAY_STAGE_DIR}/lib/firmware/nvidia/${NVIDIA_DRIVER_VERSION}/" | wc -l) files injected ==="
mkdir -p "${OVERLAY_STAGE_DIR}/usr/lib/firmware/nvidia/${NVIDIA_DRIVER_VERSION}"
cp "${NVIDIA_CACHE}/firmware/"* "${OVERLAY_STAGE_DIR}/usr/lib/firmware/nvidia/${NVIDIA_DRIVER_VERSION}/"
echo "=== firmware: $(ls "${OVERLAY_STAGE_DIR}/usr/lib/firmware/nvidia/${NVIDIA_DRIVER_VERSION}/" | wc -l) files injected ==="
fi
# --- build / download NCCL ---
@@ -642,6 +648,7 @@ if ! needs_full_build; then
validate_iso_live_boot_entries "$ISO_RAW"
validate_iso_grub_assets "$ISO_RAW"
validate_iso_nvidia_runtime "$ISO_RAW"
validate_iso_rootfs_layout "$ISO_RAW"
cp "$ISO_RAW" "$ISO_OUT"
echo ""
echo "=== done (${BUILD_VARIANT}, fast-path) ==="
@@ -703,6 +710,7 @@ if [ -f "$ISO_RAW" ]; then
validate_iso_live_boot_entries "$ISO_RAW"
validate_iso_grub_assets "$ISO_RAW"
validate_iso_nvidia_runtime "$ISO_RAW"
validate_iso_rootfs_layout "$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"