overlay: el commit no podía desmontar /bin en una máquina viva — y el código prometía el perezoso sin hacerlo
La primera instalación real de un paquete en la caja (`takana install zsh`, sin --prefix) dejó el sistema A MEDIAS: `commit` desmontó 5 de 7 targets y murió con `target is busy` en /bin, con dos overlays montados y el estado sin promocionar. La causa no es un fd abierto: los procesos tienen su EJECUTABLE mapeado desde /bin y /usr/bin —empezando por PID 1, /usr/bin/arje-zero— y un ejecutable mapeado pin-ea el montaje. En un FHS vivo eso no se puede evitar esperando. El comentario de `do_umount_lenient` prometía el desmontaje perezoso «desde la Fase 2» y el código NUNCA pasaba `-l`. Ahora, ante `busy`, reintenta perezoso: desengancha el montaje ya mismo (las rutas vuelven a resolver al directorio real, que es lo que el promote necesita) y libera cuando el último proceso que lo miraba se muere; los vivos ven el mismo contenido porque el commit copia el upper al lower justo después. Probado en la caja con un paquete nuevo: install tree ⇒ overlay; commit ⇒ WARN del perezoso sobre /usr/bin y «commit OK — 1 archivo(s) promocionados»; 0 overlays, /usr/bin/tree real, tree v2.3.2. El SDD 28 §5.10 anota las otras cuatro cosas que ese `sudo takana install zsh` destapó: el repo por defecto que no existía, el lab que no se encontraba desde /root, el binario del sistema incapaz de leer los .tkn nuevos, y —la peor— que la instalación queda PARTIDA porque el overlay cubre siete directorios y /usr/share no es uno: 1296 de los 1331 ficheros fueron directos al FHS real mientras 2 binarios esperaban el commit, y el mensaje decía `apply OK` igual.
This commit is contained in:
@@ -412,8 +412,7 @@ fn do_mount(lower: &Path, upper: &Path, work: &Path) -> Result<()> {
|
||||
}
|
||||
|
||||
fn do_umount_lenient(target: &Path) -> Result<()> {
|
||||
// `umount` retorna 32 para "not mounted"; el flag --lazy fuerza el desmontaje incluso
|
||||
// si hay procesos con file descriptors abiertos sobre el merged. Para Fase 2 nos vale.
|
||||
// `umount` retorna 32 para "not mounted".
|
||||
let out = Command::new("umount")
|
||||
.arg(target)
|
||||
.output()
|
||||
@@ -425,6 +424,40 @@ fn do_umount_lenient(target: &Path) -> Result<()> {
|
||||
if stderr.contains("not mounted") || stderr.contains("not found") {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// ⚠ `target is busy` NO es un error acá, y esto es medido (2026-09-21, caja `takana`): un
|
||||
// `commit` sobre el FHS VIVO falla siempre en `/bin` y `/usr/bin`, porque los procesos de la
|
||||
// máquina —empezando por PID 1, `/usr/bin/arje-zero`— tienen su ejecutable mapeado desde ahí,
|
||||
// y un ejecutable mapeado pin-ea el montaje. Pasó con la primera instalación real de un
|
||||
// paquete en la caja: desmontó 5 de 7 targets, murió en `/bin`, y dejó el sistema **a medias**
|
||||
// —dos overlays montados y el estado sin promocionar— que es el peor de los finales.
|
||||
//
|
||||
// El desmontaje PEREZOSO es la salida correcta y la que este mismo comentario prometía desde
|
||||
// la Fase 2 sin que nadie la hubiera cableado: desengancha el montaje del árbol ya mismo (las
|
||||
// rutas vuelven a resolver al directorio real, que es lo que el promote necesita) y libera
|
||||
// cuando el último proceso que lo mira se muera. Los que siguen vivos ven el mismo contenido,
|
||||
// porque lo primero que hace el commit después es copiar el upper al lower.
|
||||
if stderr.contains("busy") {
|
||||
let lazy = Command::new("umount")
|
||||
.arg("-l")
|
||||
.arg(target)
|
||||
.output()
|
||||
.map_err(|e| Error::Umount(format!("spawn umount -l: {e}")))?;
|
||||
if lazy.status.success() {
|
||||
tracing::warn!(
|
||||
target = %target.display(),
|
||||
"umount: el target estaba ocupado (procesos con ejecutables mapeados ahí, \
|
||||
normal en un FHS vivo) ⇒ desmontaje perezoso"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
return Err(Error::Umount(format!(
|
||||
"umount {} falló ocupado, y el perezoso tampoco pudo: {}",
|
||||
target.display(),
|
||||
String::from_utf8_lossy(&lazy.stderr).trim()
|
||||
)));
|
||||
}
|
||||
|
||||
Err(Error::Umount(format!(
|
||||
"umount {} falló (exit {:?}): {}",
|
||||
target.display(),
|
||||
|
||||
Reference in New Issue
Block a user