sergio/takana
> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…
instalador por PARTICIÓN: takana llega de visita a un disco que es de otro
1#!/bin/sh
2# takana-install-partition.sh — instalar takana EN UNA PARTICIÓN, sin tocar la tabla ni las vecinas.
3#
4# ── QUÉ LO SEPARA DE LOS OTROS DOS INSTALADORES ────────────────────────────────────────────────
5# `install-image-efi.sh` fabrica una IMAGEN de disco entera, y `takana-live-install.sh` reparticiona
6# el DISCO entero (`o` en fdisk = tabla nueva). Los dos presuponen que el disco es nuestro. Éste
7# presupone lo contrario: **el disco es de otro y takana llega de visita**.
8#
9# - NO escribe la tabla de particiones. Ni una vez. No invoca fdisk/sfdisk/parted.
10# - Formatea EXACTAMENTE una partición: la que se le nombra.
11# - Reusa la ESP que ya existe, y escribe sólo dentro de `\EFI\takana\` — la ruta de VENDOR, que
12# es nuestra. `\EFI\BOOT\BOOTX64.EFI` es tierra de nadie (Windows la repuebla) y **no se pisa**
13# si ya existe, salvo `--claim-fallback` explícito.
14# - Registra la entrada NVRAM propia (`takana boot entry add`, ADR 0018) tras hacer un `backup`
15# del estado de arranque. Convivir, no desplazar.
16#
17# ⚠ **Lo que este instalador NO hace, a propósito: redimensionar.** Encoger pide desmontar el
18# filesystem (ext4 sólo crece online), y desmontar la partición donde estás corriendo no se puede.
19# Eso es física, no una carencia del script: el redimensionado vive en el instalador que corre desde
20# OTRO medio. Acá la partición destino tiene que existir y estar libre.
21#
22# Uso:
23# sudo scripts/takana-install-partition.sh --root-part /dev/nvme0n1p5 --esp /dev/nvme0n1p1
24# # prueba sin hardware (dos ficheros pre-dimensionados, sin root):
25# scripts/takana-install-partition.sh --root-part work/p-root.img --esp work/p-esp.img --no-nvram
26#
27# Opciones:
28# --root-part <dev|fichero> DESTINO. Se formatea ext4 con LABEL=hammer-root. Lo ÚNICO destructivo.
29# --esp <dev|fichero> ESP existente a poblar (FAT). Sin ella no hay arranque.
30# --disk <dev> --esp-part N para la entrada NVRAM; si no se dan, se deducen del `--esp`.
31# --claim-fallback además, escribir \EFI\BOOT\BOOTX64.EFI (pisa lo que haya ahí).
32# --no-nvram no tocar la NVRAM (pruebas, o firmware que se registra a mano).
33# --yes no preguntar (para guiones; en interactivo pide confirmación).
34# Variables: ROOTFS KERNEL BUSYBOX (como install-image-efi.sh) · STAGE (def work/.install-part-stage)
35set -eu
36ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
37
38DEST=""; ESP=""; DISK=""; ESP_PART=""; CLAIM_FALLBACK=""; NO_NVRAM=""; ASSUME_YES=""
39while [ $# -gt 0 ]; do
40 case "$1" in
41 --root-part) DEST="${2:?}"; shift 2 ;;
42 --esp) ESP="${2:?}"; shift 2 ;;
43 --disk) DISK="${2:?}"; shift 2 ;;
44 --esp-part) ESP_PART="${2:?}"; shift 2 ;;
45 --claim-fallback) CLAIM_FALLBACK=1; shift ;;
46 --no-nvram) NO_NVRAM=1; shift ;;
47 --yes|-y) ASSUME_YES=1; shift ;;
48 -h|--help) sed -n '2,40p' "$0"; exit 0 ;;
49 *) echo "opción desconocida: $1 (--help)" >&2; exit 2 ;;
50 esac
51done
52[ -n "$DEST" ] || { echo "falta --root-part (--help)" >&2; exit 2; }
53
54# ── LAS GUARDAS VAN PRIMERO, Y SON EL PUNTO DE ESTE SCRIPT ─────────────────────────────────────
55# Un instalador que borra de más falla del lado silencioso: el usuario se entera cuando reinicia y
56# le falta su /home. Cada guarda de abajo existe porque su ausencia es irreversible.
57es_bloque() { [ -b "$1" ]; }
58nombre_kernel() { basename "$(readlink -f "$1")"; }
59
60if es_bloque "$DEST"; then
61 _n=$(nombre_kernel "$DEST")
62 # 1) DESTINO TIENE QUE SER UNA PARTICIÓN, NO UN DISCO. `/sys/class/block/<x>/partition` sólo
63 # existe en particiones: es la diferencia entre formatear 200 G y formatear 1,8 T.
64 [ -r "/sys/class/block/$_n/partition" ] || {
65 echo "takana-install-partition: $DEST NO es una partición (¿es el disco entero?)." >&2
66 echo " Este instalador nunca escribe un disco completo." >&2; exit 1; }
67 # 2) NO PUEDE ESTAR MONTADA. mke2fs sobre un fs montado corrompe el que está en uso.
68 if grep -q "^$(readlink -f "$DEST") " /proc/mounts 2>/dev/null; then
69 echo "takana-install-partition: $DEST está MONTADA. Desmontala primero (o elegí otra)." >&2
70 grep "^$(readlink -f "$DEST") " /proc/mounts >&2; exit 1
71 fi
72 # 3) NO PUEDE SER LA RAÍZ VIVA. Es el caso que el usuario pregunta siempre: «¿puedo instalar
73 # encima del Linux que estoy usando?». No desde él: hay que saltar antes a otro kernel/medio.
74 _rootdev=$(awk '$2=="/" {print $1; exit}' /proc/mounts 2>/dev/null || true)
75 [ "$(readlink -f "$DEST")" = "$(readlink -f "${_rootdev:-/nonexistent}" 2>/dev/null || echo /nonexistent)" ] && {
76 echo "takana-install-partition: $DEST es la RAÍZ VIVA de este sistema. No se puede reemplazar" >&2
77 echo " el sistema que está corriendo desde adentro." >&2; exit 1; }
78 # 4) NO PUEDE SER LA ESP que vamos a reusar.
79 [ -n "$ESP" ] && [ "$(readlink -f "$DEST")" = "$(readlink -f "$ESP")" ] && {
80 echo "takana-install-partition: --root-part y --esp son la MISMA partición" >&2; exit 1; }
81else
82 [ -f "$DEST" ] || { echo "takana-install-partition: $DEST no existe (ni bloque ni fichero)" >&2; exit 1; }
83fi
84
85# ── El terreno, DICHO EN VOZ ALTA antes de tocar nada (ADR 0018 §4) ───────────────────────────
86HAMMER="${HAMMER:-$ROOT/target/release/takana}"
87echo "==> destino: $DEST ($( [ -b "$DEST" ] && echo 'partición real' || echo 'fichero de prueba'))"
88if [ -b "$DEST" ] && command -v blkid >/dev/null 2>&1; then
89 echo " lo que hay HOY ahí (se va a PERDER):"
90 blkid "$DEST" 2>/dev/null | sed 's/^/ /' || echo " (sin filesystem reconocible)"
91fi
92if [ -n "$ESP" ] && [ -b "$ESP" ] && [ -x "$HAMMER" ]; then
93 _d="${DISK:-/dev/$(basename "$(readlink -f "/sys/class/block/$(nombre_kernel "$ESP")/..")")}"
94 echo " terreno de $_d (nada de esto se toca):"
95 "$HAMMER" boot entry survey --disk "$_d" 2>&1 | sed 's/^/ /' || true
96fi
97if [ -z "$ASSUME_YES" ] && [ -t 0 ]; then
98 printf " ¿formateo %s y dejo el resto del disco INTACTO? [escribí si] " "$DEST"
99 read -r _r; [ "$_r" = "si" ] || { echo " cancelado."; exit 1; }
100fi
101
102# ── 1) Los árboles (initramfs de pivote + rootfs con su /sbin/init) los arma el script VALIDADO ──
103# No se copian acá: se PIDEN. Ver el comentario de `STAGE_ONLY` en install-image-efi.sh.
104STAGE="${STAGE:-work/.install-part-stage}"
105echo "==> armando árboles (STAGE_ONLY sobre install-image-efi.sh)"
106STAGE_ONLY=1 KEEP_STORE=1 STAGE="$STAGE" ./scripts/install-image-efi.sh
107ROOT_TREE="$STAGE/root"; IRD="$STAGE/initramfs.cpio.gz"
108[ -d "$ROOT_TREE" ] && [ -r "$IRD" ] || { echo "takana-install-partition: el stage no quedó completo" >&2; exit 1; }
109
110. "$(dirname "$0")/lib/herramientas-desde-store.sh"
111MKE2FS=$(herramienta e2fsprogs sbin/mke2fs mke2fs) || exit 1
112MMD=$(herramienta mtools usr/bin/mmd mmd) || exit 1
113MCOPY=$(herramienta mtools usr/bin/mcopy mcopy) || exit 1
114MDIR=$(herramienta mtools usr/bin/mdir mdir) || exit 1
115KERNEL="${KERNEL:-$(ls -dt store/*-linux-metal/boot/bzImage 2>/dev/null | head -1)}"
116[ -n "${KERNEL:-}" ] && [ -r "$KERNEL" ] || { echo "no encuentro el kernel EFI-stub (set KERNEL=)" >&2; exit 1; }
117
118# ── 2) Formatear SÓLO la partición destino, y poblarla SIN MONTARLA ────────────────────────────
119# `mke2fs -d` escribe el árbol dentro del filesystem nuevo sin `mount` y sin loop. Que no haya
120# montaje no es elegancia: un `mount` mal apuntado en un disco ajeno es justamente el accidente que
121# este script existe para no tener. Si no somos root, `unshare -r` da los dueños root sin sudo.
122echo "==> ext4 LABEL=hammer-root en $DEST (única escritura destructiva de todo el script)"
123if [ "$(id -u)" = "0" ]; then
124 "$MKE2FS" -q -t ext4 -L hammer-root -d "$ROOT_TREE" "$DEST"
125else
126 unshare -r "$MKE2FS" -q -t ext4 -L hammer-root -d "$ROOT_TREE" "$DEST"
127fi
128echo " ✓ $DEST formateada y poblada"
129
130# ── 3) La ESP: sólo nuestra carpeta de vendor, con mtools y SIN montar ─────────────────────────
131if [ -n "$ESP" ]; then
132 "$MDIR" -i "$ESP" :: >/dev/null 2>&1 || { echo "takana-install-partition: $ESP no parece FAT (¿es la ESP?)" >&2; exit 1; }
133 # Guardián de capacidad: una ESP de fábrica son 100 MiB y Windows ya vive ahí. El margen se
134 # comprueba ANTES, con los números a la vista, no como un «disk full» a mitad de copiar.
135 # ⚠ `mdir` imprime el espacio libre con SEPARADOR DE MILES en espacios («60 056 064 bytes
136 # free»), así que tomar `$1` devuelve **60** y el guardián aborta una instalación perfectamente
137 # posible. Medido en la primera corrida de este script. Hay que juntar los dígitos de la línea.
138 _free=$("$MDIR" -i "$ESP" :: 2>/dev/null | awk '/bytes free/ {l=$0; sub(/ bytes free.*/,"",l); gsub(/[^0-9]/,"",l); print l}' | tail -1)
139 _need=$(( $(wc -c < "$KERNEL") + $(wc -c < "$IRD") ))
140 [ -n "${_free:-}" ] && [ "$_free" -lt "$_need" ] && {
141 echo "takana-install-partition: la ESP tiene ${_free}B libres y hacen falta ${_need}B." >&2
142 echo " No se escribió nada en ella." >&2; exit 1; }
143 # ⚠ **Un FAT puede declarar MÁS espacio del que su partición tiene, y nadie se queja hasta que
144 # una escritura grande cae más allá del final.** Medido acá el 2026-09-23 fabricando el disco de
145 # prueba: `mformat -i disco.img@@<offset>` formatea usando el tamaño del FICHERO ENTERO, no el de
146 # la partición ⇒ una ESP de 64 MiB con un FAT que anuncia 2,5 G. Y no es un artefacto del banco de
147 # pruebas: cualquier ESP que alguien encogió con una herramienta de particionado sin reformatear
148 # queda igual. El `df` del kernel repite la mentira —la leyó del mismo sector de arranque— así que
149 # NO alcanza con preguntarle al filesystem: hay que contrastar contra el TAMAÑO REAL del
150 # contenedor, que es lo único que no puede mentir.
151 if [ -b "$ESP" ]; then
152 _cap=$(blockdev --getsize64 "$ESP" 2>/dev/null || echo "")
153 [ -z "$_cap" ] && _cap=$(( $(cat "/sys/class/block/$(nombre_kernel "$ESP")/size" 2>/dev/null || echo 0) * 512 ))
154 else
155 _cap=$(wc -c < "$ESP")
156 fi
157 if [ -n "${_free:-}" ] && [ "${_cap:-0}" -gt 0 ] && [ "$_free" -gt "$_cap" ]; then
158 echo " ! el FAT de $ESP dice ${_free}B libres pero la partición mide ${_cap}B: el filesystem" >&2
159 echo " declara más espacio del que existe (ESP encogida sin reformatear, o formateada" >&2
160 echo " sobre un contenedor mayor). Uso el tamaño REAL como cota." >&2
161 _free="$_cap"
162 fi
163 echo "==> ESP $ESP: \\EFI\\takana\\ (${_need}B sobre ${_free:-?}B libres)"
164 "$MMD" -i "$ESP" ::/EFI 2>/dev/null || true
165 "$MMD" -i "$ESP" ::/EFI/takana 2>/dev/null || true
166 "$MCOPY" -o -i "$ESP" "$KERNEL" ::/EFI/takana/takanax64.efi
167 "$MCOPY" -o -i "$ESP" "$IRD" ::/initramfs.cpio.gz
168 # La ruta fallback es tierra de nadie: si YA hay algo, es de otro sistema y no se pisa sin que
169 # el usuario lo pida. Si no hay nada, ocuparla es gratis y salva el caso «el firmware ignora la
170 # entrada NVRAM», que es el modo de fallo más común y el más difícil de diagnosticar.
171 if "$MDIR" -i "$ESP" ::/EFI/BOOT/BOOTX64.EFI >/dev/null 2>&1; then
172 if [ -n "$CLAIM_FALLBACK" ]; then
173 "$MCOPY" -o -i "$ESP" "$KERNEL" ::/EFI/BOOT/BOOTX64.EFI
174 echo " ! \\EFI\\BOOT\\BOOTX64.EFI PISADO (--claim-fallback)"
175 else
176 echo " · \\EFI\\BOOT\\BOOTX64.EFI ya existe y es de otro: NO se toca (--claim-fallback para pisarlo)"
177 fi
178 else
179 "$MMD" -i "$ESP" ::/EFI/BOOT 2>/dev/null || true
180 "$MCOPY" -o -i "$ESP" "$KERNEL" ::/EFI/BOOT/BOOTX64.EFI
181 echo " + \\EFI\\BOOT\\BOOTX64.EFI estaba libre ⇒ ocupada (arranca sin depender de la NVRAM)"
182 fi
183 for _p in ::/EFI/takana/takanax64.efi ::/initramfs.cpio.gz; do
184 "$MDIR" -i "$ESP" "$_p" >/dev/null 2>&1 || { echo "takana-install-partition: falta $_p en la ESP" >&2; exit 1; }
185 done
186 echo " ✓ ESP poblada y verificada (sin montarla)"
187else
188 echo " ! sin --esp: el sistema queda instalado pero NO arrancable todavía"
189fi
190
191# ── 4) La entrada de arranque: convivir con el vecino, con respaldo previo ─────────────────────
192if [ -z "$NO_NVRAM" ] && [ -n "$ESP" ] && [ -b "$ESP" ] && [ -d /sys/firmware/efi/efivars ] && [ -x "$HAMMER" ]; then
193 if [ -z "$DISK" ] || [ -z "$ESP_PART" ]; then
194 _en=$(nombre_kernel "$ESP")
195 DISK="${DISK:-/dev/$(basename "$(readlink -f "/sys/class/block/$_en/..")")}"
196 ESP_PART="${ESP_PART:-$(cat "/sys/class/block/$_en/partition" 2>/dev/null || echo 1)}"
197 fi
198 echo "==> respaldo del arranque ANTES de tocar la NVRAM (ADR 0018 §5)"
199 "$HAMMER" boot entry backup || echo " ! el respaldo falló — seguí sólo si sabés por qué" >&2
200 echo "==> entrada NVRAM: $DISK p$ESP_PART → \\EFI\\takana\\takanax64.efi"
201 "$HAMMER" boot entry add --disk "$DISK" --partition "$ESP_PART" --loader '/EFI/takana/takanax64.efi'
202else
203 echo " · NVRAM no tocada (--no-nvram, sin efivarfs, o destino de prueba)"
204fi
205
206echo "✓ takana instalada en $DEST. La tabla de particiones NO se tocó y las demás particiones siguen como estaban."
207echo " Comprobalo antes de reiniciar: lsblk -o NAME,SIZE,FSTYPE,LABEL · $HAMMER boot entry list"