gioser

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…

TOML 1463Shell 152Markdown 90Rust 853028 Sutichaykuna

⎇ bea665954c5a6c6d4a76f10b87e827050d773a13 scripts/takana-install-partition.sh Shell · 207 siq'ikuna · 13.5 KB Ñawpa willay ch'uya

instalador por PARTICIÓN: takana llega de visita a un disco que es de otro bea665954 · Sergio ·

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"