boot entry backup/restore: el arranque como estado reproducible (ADR 0018 §5)

Guarda las variables de NVRAM CRUDAS —se reescriben tal cual;
decodificarlas para volver a codificarlas al restaurar sería una
oportunidad de perder algo que no entendemos— y de la ESP un manifiesto
con hashes, no los bytes: el kernel ya vive en el store y copiarlo otra
vez sería churn. El manifiesto es evidencia de qué había; para lo que no
esté en el store dice qué falta, en vez de prometer reponerlo.

El ADR se equivocaba en DÓNDE: decía "volcar al store". No va al store,
porque lo barre store-gc.sh, que clasifica por nombre de receta — un
respaldo ahí sería huérfano y se borraría en el primer --huerfanos. Vive
en /var/lib/hammer/boot/, que en las imágenes es su propia partición. El
nombre sale del CONTENIDO, así que un arranque que no cambió produce el
mismo fichero: corre en cada arranque sin llenar la partición de copias.

Lo que encontró la prueba de punta a punta y no estaba en el diseño:
restaurar es volver al pasado, y lo que llegó DESPUÉS no estaba en ese
pasado. Al reponer el BootOrder del respaldo, el Windows instalado más
tarde quedaba FUERA del orden — correcto, y justo lo que el usuario no
espera de algo llamado "restaurar el arranque". Ahora se avisa antes,
con nombre y apellido, y restore NO escribe por defecto: la NVRAM es lo
único de la máquina que no se rehace desde el store.

El lector FAT ganó lectura de ficheros, con su trampa propia: hay que
truncar al tamaño DECLARADO en el directorio, no al final del último
cluster. Un fichero de 1,5 MB en clusters de 1 KiB termina con relleno y
hashear el relleno daría un hash distinto al del mismo fichero en disco
— el síntoma sería "dos respaldos del mismo arranque difieren".
Verificado contra mtools como oráculo (1 500 000 y 900 000 bytes
exactos, mismo sha256 que los originales) y con un test determinista que
fabrica una FAT16 a mano, sin depender de que mtools esté.

79/79 del CLI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHAKWoBqof9XvsYCvDicEj
This commit is contained in:
Sergio
2026-09-12 00:34:21 +00:00
co-authored by Claude Opus 5
parent 61b57ab060
commit bd1fa8bf8a
5 changed files with 464 additions and 2 deletions
+10
View File
@@ -474,6 +474,16 @@ echo "HAMMER-EFI-INIT-OK: arje-zero PID1" > /dev/ttyS0 2>/dev/null || true
# en silencio, el usuario nunca se entera de que algo le está pisando el arranque.
[ -x /usr/bin/hammer ] && /bin/busybox timeout 15 /usr/bin/hammer boot entry reconcile 2>&1 \
| /bin/busybox tee /dev/tty0 > /dev/ttyS0 2>/dev/null || true
# Respaldo del arranque (ADR 0018 §5). Se hace DESPUÉS de reconciliar, así lo que queda guardado es
# el estado bueno y no el que dejó el vecino. El nombre del fichero sale del CONTENIDO: un arranque
# que no cambió produce el mismo fichero, así que esto corre en cada arranque sin llenar la
# partición de estado con copias idénticas.
#
# Vive en /var/lib/hammer, que en las imágenes es su propia partición y sobrevive a una reinstalación
# del sistema. **No va al store**: el store lo barre `store-gc.sh`, que clasifica por nombre de
# receta — un respaldo ahí sería «huérfano» y se borraría en el primer `--huerfanos`.
[ -x /usr/bin/hammer ] && /bin/busybox timeout 15 /usr/bin/hammer boot entry backup > /dev/ttyS0 2>&1 || true
exec /usr/bin/arje-zero
INIT
"$BB" chmod +x "$MNT/sbin/init"