censar: «no existe» y «no puedo mirar» se confundían — 572 M invisibles, y el censo de hoy

El censo ya tenía tres estados a propósito (`ausente` / `sin_permiso` / `ok`), pero el tercero sólo
se detectaba sobre la PROPIA ruta: cuando el que no deja pasar es un ANCESTRO, `[ -e "$p" ]`
contesta que no existe y la ruta caía en `ausente`, que el censo descarta sin decir nada.

Medido en gioser: `/root/.local` es 0700 ⇒ un censo corrido como `sergio` daba `/root/.local/bin`
por AUSENTE. Como root son 572 M de binarios puestos a mano que ningún paquete provee — justo la
clase que la mudanza no puede reconstruir.

Arreglado: si un ancestro existe y no es atravesable, el estado es `sin_permiso`. Probado en los dos
sentidos, que es lo que separa un guardián de un adorno:

  ancestro 0700 de otro dueño   →  sin_permiso   (sale en el ⚠ del resumen)
  ruta que NO existe de verdad  →  ausente       (no se reporta)   ← el control que tiene que pasar

Con el arreglo, el censo sin privilegio pasa de 2 rutas ciegas a 13, entre ellas el home entero de
`artix`, que antes no figuraba de ninguna manera.

Y el §6.34 con el estado MEDIDO de la mudanza hoy: de los 29 dominios queda UNO sirviéndose desde
gioser (`sergio.gioser.net`); los otros seis vivos ya contestan 200 contra 2.29.29.217. El rescate
del §6.20 sigue coincidiendo bit a bit con cuatro de los seis procesos que corren desde un inodo
borrado, y los otros dos tienen en disco una versión más nueva. Lo que queda abierto son los pasos 6
y 7: el montón de daemons propios (shuma, willay, pacha, tejido, tupu, matilda…) y los datos.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-09-16 15:11:16 +00:00
co-authored by Claude Opus 5
parent fff28ad53f
commit e2bb4f9b89
2 changed files with 111 additions and 9 deletions
+25 -6
View File
@@ -592,6 +592,14 @@ def fuera_de_git(t):
«no hay nada» de lo que no pudo mirar. Por eso hay `sin_permiso`, que es un dato distinto y va
al resumen: lo ausente se descarta, lo no mirado se vuelve a mirar con quien pueda.
⚠ Y el tercer estado hay que buscarlo TAMBIÉN hacia arriba (2026-09-16). La primera versión sólo
lo detectaba cuando la ruta se podía *stat*ear pero no listar; cuando el que no deja pasar es un
ANCESTRO, `[ -e ]` contesta que no existe y la ruta caía en `ausente`, que el censo descarta sin
decir nada. Medido en gioser: `/root/.local` es 0700, así que un censo corrido como `sergio`
reportaba `/root/.local/bin` como si no estuviera — **572 M de binarios puestos a mano que
ningún paquete provee**, justo la clase que la mudanza no puede reconstruir. Corriendo como
root aparecen. Ahora, si un ancestro existe y no es atravesable, el estado es `sin_permiso`.
Todo en UNA sola llamada (un script generado) y no una por ruta: por SSH, N rutas × M homes son
decenas de conexiones y el censo tarda más que el trabajo que describe.
"""
@@ -610,10 +618,21 @@ def fuera_de_git(t):
q = shlex.quote(ruta)
prog.append(
f'p={q}; '
'if [ ! -e "$p" ]; then st=ausente; '
'elif [ -f "$p" ]; then st=fichero; '
'elif ls -1 "$p" >/dev/null 2>&1; then st=ok; '
'else st=sin_permiso; fi; '
# `-e` responde NO tanto para «no está» como para «no puedo mirar»: si un ANCESTRO no es
# atravesable, el test falla igual que si la ruta no existiera. Medido: /root/.local es
# 0700, y un censo corrido como `sergio` daba /root/.local/bin por AUSENTE — 572 M de
# binarios a mano que ningún paquete provee, desaparecidos en silencio del censo.
'if [ -e "$p" ]; then '
' if [ -f "$p" ]; then st=fichero; '
' elif ls -1 "$p" >/dev/null 2>&1; then st=ok; '
' else st=sin_permiso; fi; '
'else '
' st=ausente; d=${p%/*}; '
' while [ -n "$d" ]; do '
' if [ -e "$d" ]; then [ -x "$d" ] || st=sin_permiso; break; fi; '
' d=${d%/*}; '
' done; '
'fi; '
'sz=$(timeout 20 du -shx "$p" 2>/dev/null | cut -f1); '
'printf "P\t%s\t%s\t%s\n" "$p" "$st" "${sz:-?}"; '
'[ "$st" = ok ] && ls -1 "$p" 2>/dev/null | head -20 | sed "s/^/E\t/"; '
@@ -920,8 +939,8 @@ def resumen(c):
if ciegos:
# Un `ls` sin permiso se lee IGUAL que un directorio ausente. Decirlo es la diferencia
# entre «no hay nada» y «no pude mirar», y sólo una de las dos se puede descartar.
print(f"{len(ciegos)} ruta(s) EXISTEN y el censo no pudo leerlas "
f"(corré el censo como su dueño o con privilegio):")
print(f"{len(ciegos)} ruta(s) que el censo NO PUDO MIRAR — no es lo mismo que "
f"ausentes (corré el censo como su dueño o con privilegio):")
for f in ciegos:
print(f" {f['path']}")
if c["datos"]: