install-image: la guarda del init estaba INVERTIDA, y el staging no podía cruzar el montaje

Dos cosas encontradas armando la primera imagen del `perfil.servidor` (SDD 28 §1b).

**1. `[ -e "$ROOTFS/sbin/init" ]` rechazaba el rootfs CORRECTO y aceptaba el equivocado.** `-e`
sigue el symlink y lo resuelve contra el HOST. El `/sbin/init` del product-rootfs es un symlink
ABSOLUTO a `/usr/bin/arje-zero`, que en el host no existe ⇒ la guarda daba falso. Y al revés: un
rootfs cuyo init es `../bin/busybox` —symlink RELATIVO, que sí resuelve dentro del árbol— la pasaba
sin chistar. O sea que la única guarda que separaba «esto arranca takana» de «esto arranca otra
cosa» estaba exactamente al revés. Ahora acepta `-L`, y se probó en los dos sentidos: acepta el
symlink absoluto a arje-zero, sigue rechazando un directorio sin init.

Esto no es teórico: hidratar `perfil.servidor` sobre el product-rootfs **pisa `/sbin/init`** —
busybox entra en la clausura y su symlink gana por llegar después—. La imagen habría arrancado
perfecto con el init de busybox en vez de arje-zero, que es peor que no arrancar. Se restaura el
symlink al ensamblar y el cmdline lleva `init=/usr/bin/arje-zero` explícito, por las dos puntas.

**2. `STAGE` estaba cableado a `work/.install-stage`.** El staging HARDLINKEA desde `$ROOTFS`, y un
hardlink no cruza un montaje aunque sea el mismo disco. Con el layout normal de este repo —`store`
y `work/out` son bind-mounts de /dev/sdb, `work/` está en /dev/sdc— el rootfs vive del otro lado y
`cp -al` muere con EXDEV fichero por fichero. Ahora es env, documentado en la cabecera.

De paso: el fichero no tenía bit de ejecución (100644) mientras sus siete hermanos son 100755, así
que su propio ejemplo de uso `./scripts/install-image.sh` no funcionaba. Venía de antes; corregido.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RomoxEGZUhaT4pob1QSX5x
This commit is contained in:
Sergio
2026-09-10 19:56:17 +00:00
co-authored by Claude Opus 5
parent f287b3045c
commit c809a1786b
Regular → Executable
+13 -2
View File
@@ -24,6 +24,8 @@
#
# Variables:
# ROOTFS dir rootfs a empaquetar (default work/builder-rootfs)
# STAGE dir de staging (default work/.install-stage) — DEBE estar en el
# mismo montaje que ROOTFS: el staging hardlinkea y un hardlink no cruza montajes
# PREALLOC 1 ⇒ trata IMG como target pre-dimensionado in-place (no trunca; prueba el camino device)
# KERNEL bzImage a embeber en /boot (default el del store: store/*-linux/boot/bzImage)
# IMG imagen de salida (default work/takana-install.img)
@@ -47,7 +49,12 @@ KERNEL="${KERNEL:-$(ls store/*-linux/boot/bzImage 2>/dev/null | head -1)}"
GRUB_LIB="${GRUB_LIB:-/usr/lib/grub/i386-pc}"
[ -d "$ROOTFS" ] || { echo "no existe ROOTFS: $ROOTFS (corré selfhost-verify para armar el builder)" >&2; exit 1; }
[ -e "$ROOTFS/sbin/init" ] || { echo "ROOTFS sin /sbin/init (arje-zero)" >&2; exit 1; }
# `-e` sigue el symlink y lo resuelve contra el HOST, no contra el rootfs. El `/sbin/init` del
# product-rootfs es un symlink ABSOLUTO a `/usr/bin/arje-zero`, que en el host no existe ⇒ `-e` da
# falso y esta guarda rechazaba el rootfs CORRECTO. Peor: un rootfs con el init equivocado
# (`../bin/busybox`, symlink RELATIVO que sí resuelve dentro del árbol) la pasaba sin chistar.
# O sea que la guarda estaba exactamente invertida. `-L` acepta el symlink por lo que es.
[ -e "$ROOTFS/sbin/init" ] || [ -L "$ROOTFS/sbin/init" ] || { echo "ROOTFS sin /sbin/init (arje-zero)" >&2; exit 1; }
[ -n "$KERNEL" ] && [ -r "$KERNEL" ] || { echo "no encuentro el kernel a embeber (set KERNEL=<bzImage>)" >&2; exit 1; }
[ -d "$GRUB_LIB" ] && [ -r "$GRUB_LIB/boot.img" ] || { echo "faltan los módulos GRUB i386-pc en $GRUB_LIB" >&2; exit 1; }
for t in sfdisk mke2fs grub-mkimage grub-bios-setup; do
@@ -55,7 +62,11 @@ for t in sfdisk mke2fs grub-mkimage grub-bios-setup; do
done
unshare -r true 2>/dev/null || { echo "se requiere 'unshare -r' (ficheros root-owned sin sudo)" >&2; exit 1; }
STAGE="work/.install-stage"
# El staging ENLAZA (hardlink) desde $ROOTFS, y un hardlink no cruza un montaje aunque sea el mismo
# disco. Con el store en un volumen aparte —el layout normal de este repo: `store` y `work/out` son
# bind-mounts de /dev/sdb y `work/` está en /dev/sdc— el rootfs vive del otro lado y `cp -al` muere
# con EXDEV fichero por fichero. Por eso es env: ponelo bajo el MISMO montaje que $ROOTFS.
STAGE="${STAGE:-work/.install-stage}"
rm -rf "$STAGE"
mkdir -p "$STAGE"
ROOT_TREE="$STAGE/root"