takana etapa 4: los 10 crates de librería y el CLI pasan a takana-*
hammer-{core,build,bootstrap,overlay,journal,mirror,upgrade,agent,recover,cli}
→ takana-*, con sus deps de workspace, sus identificadores en el fuente y las
referencias -p de los scripts.
VERIFICADO que no mueve nada del corpus: `takana hash recipes/zlib.toml`
devuelve b3:dc363f26… , idéntico a antes del renombre. Los nombres de crate no
entran en hash_inputs, pero eso se comprueba, no se supone. 600 tests en verde.
DOS BINARIOS SE CONGELAN, y no por prolijidad:
- `hammerd` — paquete Y binario. Es componente de Stage 1 de la distro (musl,
busybox, hammerd, arje-zero), lo supervisa arje-zero en el sistema arrancado,
`PRESEED=hammerd` lo nombra en selfhost-verify y sus bytes anclan el baseline
of_tree. El nombre del crate va en los símbolos ⇒ renombrarlo mueve los bytes.
- `hammer-recover` — el PAQUETE se renombra a takana-recover, el BINARIO no.
hammer-live-install.sh lo copia a /usr/sbin/hammer-recover en sistemas ya
instalados y hornea un hook de arranque que lo invoca por ese nombre:
renombrarlo rompe máquinas instaladas, no el repo.
Consecuencia que hay que anotar igual: al renombrar hammer-core, los bytes de
hammerd cambian de todos modos porque linkea contra un crate con otro nombre.
El baseline of_tree del selfhost hay que rehacerlo — es efecto de la etapa 4,
no de un cambio de hammerd.
Las referencias en comentarios de recetas y docs (rutas hammer-core/src/…)
quedan para la etapa 5: son texto, no mueven hash.
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
//! La hidratación no escribe fuera de su root, aunque el FHS ya traiga un symlink que salga.
|
||||
//!
|
||||
//! EL FALLO (medido 2026-08-30, antes de `no_escapa`): hidratar es `target_fhs.join(rel)` y
|
||||
//! escribir por ruta. Un artefacto que trae `usr/share/pkg → /algún/lado` deja ese symlink puesto
|
||||
//! —los symlinks se replican literales, y así debe ser— y la hidratación SIGUIENTE escribía
|
||||
//! `usr/share/pkg/archivo` **fuera del root**, devolviendo `Ok(1)`. Con `hammer hydrate --into`
|
||||
//! sobre una imagen, eso es escribir en el sistema anfitrión diciendo que todo fue bien.
|
||||
//!
|
||||
//! El corpus no lo disparaba (de 160 symlinks absolutos ninguno apunta a un directorio, y de
|
||||
//! ~42 000 relativos ninguno sale de su artefacto), así que esto es una mina desactivada, no un
|
||||
//! incendio apagado. Por eso el test: lo que no dispara nadie tampoco lo nota nadie.
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use takana_build::hydrate::hydrate;
|
||||
use takana_core::LinkMode;
|
||||
|
||||
/// Artefacto que trae `usr/share/pkg` como symlink a `destino`.
|
||||
fn artefacto_con_symlink(dir: &Path, destino: &Path) {
|
||||
std::fs::create_dir_all(dir.join("usr/share")).unwrap();
|
||||
std::os::unix::fs::symlink(destino, dir.join("usr/share/pkg")).unwrap();
|
||||
}
|
||||
|
||||
/// Artefacto que trae un fichero DEBAJO de esa misma ruta.
|
||||
fn artefacto_con_fichero(dir: &Path) {
|
||||
std::fs::create_dir_all(dir.join("usr/share/pkg")).unwrap();
|
||||
std::fs::write(dir.join("usr/share/pkg/archivo"), b"contenido").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn un_symlink_del_fhs_que_sale_del_root_no_deja_escribir() {
|
||||
let fuera = tempfile::tempdir().unwrap();
|
||||
let fhs = tempfile::tempdir().unwrap();
|
||||
let a = tempfile::tempdir().unwrap();
|
||||
let b = tempfile::tempdir().unwrap();
|
||||
|
||||
artefacto_con_symlink(a.path(), fuera.path());
|
||||
hydrate(a.path(), fhs.path(), LinkMode::Static, None).unwrap();
|
||||
|
||||
artefacto_con_fichero(b.path());
|
||||
let r = hydrate(b.path(), fhs.path(), LinkMode::Static, None);
|
||||
|
||||
assert!(r.is_err(), "hidratar a través del symlink tiene que FALLAR, no devolver Ok");
|
||||
let msg = format!("{}", r.unwrap_err());
|
||||
assert!(msg.contains("se saldría del root"), "el error tiene que decir qué pasó: {msg}");
|
||||
assert!(
|
||||
!fuera.path().join("archivo").exists(),
|
||||
"ESCAPÓ: el fichero se escribió fuera del target_fhs"
|
||||
);
|
||||
}
|
||||
|
||||
/// El contrapeso: un symlink de directorio que se queda DENTRO tiene que seguir funcionando.
|
||||
/// Es el caso de usr-merge (`/lib → usr/lib`) y el de los temas de iconos (`22@2x → 22`), o sea
|
||||
/// la mayoría de los symlinks de directorio que hay en el corpus. Un guardián que los rompiera
|
||||
/// sería peor que el agujero que tapa.
|
||||
#[test]
|
||||
fn un_symlink_que_se_queda_dentro_sigue_hidratando() {
|
||||
let fhs = tempfile::tempdir().unwrap();
|
||||
let a = tempfile::tempdir().unwrap();
|
||||
let b = tempfile::tempdir().unwrap();
|
||||
|
||||
// `usr/share/pkg → ../otro` (relativo y dentro), con el destino existiendo.
|
||||
std::fs::create_dir_all(a.path().join("usr/otro")).unwrap();
|
||||
std::fs::write(a.path().join("usr/otro/.marca"), b"x").unwrap();
|
||||
std::fs::create_dir_all(a.path().join("usr/share")).unwrap();
|
||||
std::os::unix::fs::symlink("../otro", a.path().join("usr/share/pkg")).unwrap();
|
||||
hydrate(a.path(), fhs.path(), LinkMode::Static, None).unwrap();
|
||||
|
||||
artefacto_con_fichero(b.path());
|
||||
let r = hydrate(b.path(), fhs.path(), LinkMode::Static, None);
|
||||
assert!(r.is_ok(), "un symlink que no sale del root no puede bloquear la hidratación: {r:?}");
|
||||
assert!(
|
||||
fhs.path().join("usr/otro/archivo").exists(),
|
||||
"el fichero tiene que aterrizar donde apunta el symlink, dentro del root"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user