Arch Linux en WSL2: anatomía y recuperación de un E_UNEXPECTED
Resultado
Arch seguía registrada en WSL2, pero cualquier intento de iniciarla terminaba en
Wsl/Service/E_UNEXPECTED. El mensaje de Windows escondía un fallo bastante más concreto dentro de Linux:systemdypacmanno podían cargar varias bibliotecas ELF truncadas a cero bytes, y parte de/var/lib/pacman/localhabía perdido sus metadatos. La distribución se recuperó sin desregistrarla ni sustituir su VHDX: copia de seguridad, acceso mediantewsl --system, restauración de OpenSSL yacl, reparación de la base local y actualización completa.
Alcance
La corrupción técnica quedó demostrada; su desencadenante exacto, no. La actualización reciente de ArchWSL y los ~4 GB libres que quedaban en
C:son antecedentes plausibles, pero esta nota no los convierte en causa sin evidencia.
Lo interesante del caso no es solo la secuencia de comandos. Es el cambio de
perspectiva: E_UNEXPECTED parecía un error opaco de Windows hasta que se separó
la capa WSL de la distribución y se consiguió ejecutar el sistema desde fuera.
El sistema detrás del error
WSL2 no guarda Arch como una carpeta corriente. Windows registra la distribución,
WSL arranca una máquina virtual ligera y el sistema Linux vive dentro de un VHDX
con ext4. En este equipo, Arch tenía además systemd=true, así que WSL debía
poder ejecutar /usr/lib/systemd/systemd como PID 1 antes de entregar una shell.
flowchart LR WIN["Windows 11"] --> WSL["Servicio WSL2"] WSL --> VM["VM ligera + kernel Linux"] VM --> VHDX["ext4.vhdx"] VHDX --> ROOT["Raíz de Arch"] ROOT --> PID1["systemd · PID 1"] PID1 --> SHELL["Bash / sesión de usuario"] ROOT --> PACMAN["Pacman + base local"] ROOT --> ELF["Loader y bibliotecas ELF"] ELF --> PID1 ELF --> PACMAN
Este mapa permite leer el síntoma con más precisión. Si otra distribución
arranca, la VM y el kernel siguen vivos. Si Arch falla incluso como root y sin
perfiles, el problema aparece antes de la shell. Y si un chroot muestra
libcrypto.so.3: file too short, el error genérico ya tiene una causa observable:
el proceso que WSL necesita arrancar no puede resolver sus dependencias.
Flujo técnico de diagnóstico y recuperación
flowchart TD A["E_UNEXPECTED"] --> B{"¿Otra distro inicia?"} B -- "No" --> C["Diagnosticar host WSL"] B -- "Sí" --> D["Aislar Arch"] D --> E["Backup del VHDX"] E --> F["wsl --system + chroot"] F --> G["Bibliotecas a 0 bytes"] G --> H["Restaurar OpenSSL y acl"] H --> I["Pacman vuelve a ejecutar"] I --> J["Reconstruir base local"] J --> K["pacman -Syu + Qkk"] K --> L["Reinicio y validación"]
1. Alcance
Este documento describe la recuperación concreta de una distribución Arch Linux sobre WSL2 con las siguientes propiedades:
WSL: 2.7.11.0
Kernel: 6.18.33.2-2
WSLg: 1.0.73.2
Windows: 10.0.26200.8875
Distribución: Arch
Otra distribución: docker-desktop
systemd en Arch: habilitadoVHDX:
C:\Applications\Scoop\persist\archwsl\data\ext4.vhdxTamaño físico observado:
96,63 GiBCapacidad virtual observada:
1 TiB2. Síntoma inicial
wsl -d ArchError catastrófico
Código de error: Wsl/Service/E_UNEXPECTEDTambién fallaba:
wsl -d Arch -u root --exec /usr/bin/bash --noprofile --norcPor tanto, el fallo no dependía de:
- Usuario predeterminado.
- Zsh.
.bashrc..zshrc.- Perfil interactivo.
- Lanzador
Arch.exe.
3. Hipótesis iniciales
| Hipótesis | Prueba | Resultado |
|---|---|---|
| WSL roto globalmente | Arrancar docker-desktop | Descartada |
| Servicio WSL bloqueado | Reiniciar WslService, vmcompute, hns | Sin efecto |
Nuevo Arch.exe defectuoso | Usar wsl -d Arch directamente | No era la causa |
| Usuario o shell dañados | Forzar root y Bash sin perfiles | Descartada |
| VHDX ausente | Inspeccionar registro y ruta | Descartada |
| ext4 gravemente corrupto | e2fsck | Sin bloques defectuosos |
| PID 1 o dependencia ELF rota | chroot y ejecutar systemd | Confirmada |
4. Confirmar que WSL funciona
wsl --shutdown
wsl --version
wsl --status
wsl -l -vPrueba de control:
wsl -d docker-desktop -u root --exec /bin/sh -c "echo WSL_VM_OK"Resultado:
WSL_VM_OKInterpretación
Si otra distribución arranca, la infraestructura WSL2 está operativa. El análisis debe desplazarse a:
- Registro específico de la distribución.
- VHDX.
- Sistema de archivos.
- PID 1.
- Dependencias dinámicas.
- Base de paquetes.
5. Localizar y respaldar el VHDX
Consulta del registro:
$archKey = Get-ChildItem `
'HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss' |
Where-Object {
$_.GetValue('DistributionName') -eq 'Arch'
}
$base = [Environment]::ExpandEnvironmentVariables(
$archKey.GetValue('BasePath')
)
$vhd = Join-Path $base 'ext4.vhdx'
Get-Item -LiteralPath $vhd |
Select-Object FullName,
@{N='GiB';E={[math]::Round($_.Length / 1GB, 2)}},
LastWriteTimeAntes de cualquier reparación:
wsl --shutdown
Copy-Item `
-LiteralPath $vhd `
-Destination 'D:\WSL-Backups\Arch-ext4-20260805.vhdx'Danger
No ejecutar
wsl --unregister Arch. Esa operación elimina la distribución registrada y sus datos.
6. Inspección mediante wsl --system
wsl --system -d Arch -u root -- sh -lc '
df -h /mnt/wslg/distro
mount | grep /mnt/wslg/distro
ls -l /mnt/wslg/distro/bin/sh
ls -l /mnt/wslg/distro/usr/bin/bash
ls -l /mnt/wslg/distro/usr/lib/systemd/systemd
ls -l /mnt/wslg/distro/usr/lib/ld-linux-x86-64.so.2
cat /mnt/wslg/distro/etc/wsl.conf
'Configuración encontrada:
[boot]
systemd=true
[network]
generateResolvConf=false7. Comprobación de ext4
Adjuntar el VHDX:
$vhd = 'C:\Applications\Scoop\persist\archwsl\data\ext4.vhdx'
wsl --shutdown
wsl --mount "$vhd" --vhd --bareIdentificar el dispositivo desde otra distribución:
wsl -d docker-desktop -u root -- \
lsblk -o NAME,PATH,SIZE,FSTYPE,RO,MOUNTPOINTSResultado relevante:
sdc /dev/sdc 1T ext4 0Reparación:
wsl -d docker-desktop -u root -- e2fsck -f -v /dev/sdcResultado:
FILE SYSTEM WAS MODIFIED
0 bad blocksDespués:
wsl --unmount
wsl --shutdownArch continuaba sin arrancar. e2fsck no era la solución principal.
8. Obtener el error Linux real mediante chroot
wsl --system -d Arch -u root -- \
chroot /mnt/wslg/distro /bin/bash --noprofile --norc -lc '
echo CHROOT_OK
/usr/lib/systemd/systemd --version
pacman --version
'Resultado:
CHROOT_OK
/usr/lib/systemd/systemd: error while loading shared libraries:
/usr/lib/libcrypto.so.3: file too short
pacman: error while loading shared libraries:
/usr/lib/libcrypto.so.3: file too shortComprobación:
/usr/lib/libcrypto.so.3 — 0 bytes
/usr/lib/libssl.so.3 — 0 bytesDiagnóstico
systemd no podía cargar OpenSSL. Al no poder ejecutar el PID 1 configurado, WSL devolvía el error genérico E_UNEXPECTED.
9. Restaurar OpenSSL desde la caché de Pacman
Paquetes encontrados:
openssl-3.6.1-1-x86_64.pkg.tar.zst
openssl-3.6.2-2-x86_64.pkg.tar.zst
openssl-3.6.3-1-x86_64.pkg.tar.zstComo docker-desktop no incluía bsdtar, zstd ni unzstd, se copió el paquete a Windows:
@'
set -eu
ROOT=/mnt/arch-repair
DEVICE=/dev/sdc
DEST=/mnt/host/c/Temp/arch-openssl-repair
mkdir -p "$ROOT" "$DEST"
mount -t ext4 -o rw "$DEVICE" "$ROOT"
PKG="$(ls -1t \
"$ROOT"/var/cache/pacman/pkg/openssl-*-x86_64.pkg.tar.zst |
head -n 1)"
cp "$PKG" "$DEST/openssl.pkg.tar.zst"
sync
umount "$ROOT"
'@ | wsl -d docker-desktop -u root -- sh -sExtracción en Windows:
$repair = 'C:\Temp\arch-openssl-repair'
$pkg = "$repair\openssl.pkg.tar.zst"
$extract = "$repair\extract"
New-Item -ItemType Directory -Path $extract -Force | Out-Null
tar.exe -xpf $pkg `
-C $extract `
usr/lib/libcrypto.so.3 `
usr/lib/libssl.so.3Restauración en el VHDX:
@'
set -eu
ROOT=/mnt/arch-repair
DEVICE=/dev/sdc
SRC=/mnt/host/c/Temp/arch-openssl-repair/extract/usr/lib
mkdir -p "$ROOT"
mount -t ext4 -o rw "$DEVICE" "$ROOT"
cp -f "$SRC/libcrypto.so.3" "$ROOT/usr/lib/libcrypto.so.3"
cp -f "$SRC/libssl.so.3" "$ROOT/usr/lib/libssl.so.3"
chown 0:0 \
"$ROOT/usr/lib/libcrypto.so.3" \
"$ROOT/usr/lib/libssl.so.3"
chmod 755 \
"$ROOT/usr/lib/libcrypto.so.3" \
"$ROOT/usr/lib/libssl.so.3"
sync
stat -c '%n — %s bytes' \
"$ROOT/usr/lib/libcrypto.so.3" \
"$ROOT/usr/lib/libssl.so.3"
od -An -tx1 -N4 "$ROOT/usr/lib/libcrypto.so.3"
umount "$ROOT"
'@ | wsl -d docker-desktop -u root -- sh -sResultado:
libcrypto.so.3 — 5922312 bytes
libssl.so.3 — 1011672 bytes
7f 45 4c 467f 45 4c 46 es la cabecera ELF.
10. Segundo fallo: libacl.so.1
Después de recuperar OpenSSL:
/usr/bin/pacman: error while loading shared libraries:
/usr/lib/libacl.so.1: file too shortSe descubrió que libacl.so.1 también estaba truncada.
11. Recuperación con pacman-static
Se copió un binario estático a:
C:\Temp\arch-repair\pacman-staticProblemas iniciales:
GPGME error: Invalid crypto engine
failed to synchronize all databases
unable to lock databaseConfiguración temporal
Se usó exclusivamente para paquetes locales ya descargados:
[options]
Architecture = x86_64
DBPath = /var/lib/pacman/
CacheDir = /var/cache/pacman/pkg/
LogFile = /var/log/pacman.log
SigLevel = Never
LocalFileSigLevel = Never
RemoteFileSigLevel = NeverWarning
Esta configuración no debe utilizarse para una actualización general desde repositorios. Se empleó solo para recuperar paquetes ya presentes en la caché y romper la dependencia circular.
El bloqueo se eliminó:
rm -f /var/lib/pacman/db.lckSe reinstaló acl desde la caché:
pacman-static \
--config /tmp/pacman-recovery.conf \
-U \
--noconfirm \
--noscriptlet \
--overwrite '*' \
/var/cache/pacman/pkg/acl-2.4.0-1-x86_64.pkg.tar.zstResultado:
Pacman v7.1.0
systemd 260Arch volvió a iniciar:
wsl -d Arch -u root --exec /bin/bash --noprofile --norcbash-5.3#12. Reconstruir Bash en la base local
Estado inicial:
pacman -Q bash
pacman -Qo /usr/bin/bash
ls -la /var/lib/pacman/local/bash-*Resultado:
bash 5.3.15-1
No package owns /usr/bin/bash
desc → 0 bytes
files → 0 bytesReinstalación:
rm -f /var/lib/pacman/db.lck
pacman -S --overwrite '*' bashValidación:
pacman -Qo /usr/bin/bash
pacman -Qkk bash/usr/bin/bash is owned by bash 5.3.15-1
bash: 270 total files, 0 altered files13. Reconstruir Ansible y Ansible Core
Entrada dañada de Ansible:
/var/lib/pacman/local/ansible-14.2.0-1/descSe apartó:
mkdir -p /root/pacman-db-recovery
mv /var/lib/pacman/local/ansible-14.2.0-1 \
/root/pacman-db-recovery/ansible-14.2.0-1.brokenReinstalación:
pacman -S --overwrite '*' ansibleValidación:
pacman -Qkk ansibleansible: 51231 total files, 0 altered files/usr/bin/ansible seguía sin propietario porque pertenece a ansible-core. Su entrada también tenía desc y files a cero.
mv /var/lib/pacman/local/ansible-core-2.21.2-1 \
/root/pacman-db-recovery/ansible-core-2.21.2-1.broken
pacman -S --overwrite '*' ansible-coreValidación:
/usr/bin/ansible is owned by ansible-core 2.21.2-1
ansible-core: 3068 total files, 0 altered files14. Reconstrucción masiva de /var/lib/pacman/local
Se detectaron muchas entradas con:
desc=0
files=0Afectaban, entre otros, a:
- Paquetes Perl.
- Paquetes Python.
7zip.pacman-contrib.plocate.powerline.- Herramientas de seguridad.
- Dependencias auxiliares.
Criterio correcto de corrupción
Una entrada es sospechosa cuando:
desc ausente o vacíoUn archivo files vacío no implica por sí solo corrupción. Algunos metapaquetes legítimos no contienen archivos.
Ejemplos válidos:
base: 0 total files, 0 altered files
base-devel: 0 total files, 0 altered files
ca-certificates: 0 total files, 0 altered files
gcc-libs: 0 total files, 0 altered files
linux-firmware: 0 total files, 0 altered files
texlive-meta: 0 total files, 0 altered filesEsquema de reconstrucción
set -euo pipefail
STAMP="$(date +%Y%m%d-%H%M%S)"
RECOVERY="/root/pacman-db-recovery/$STAMP"
LOCAL_DB="/var/lib/pacman/local"
CACHE="/var/cache/pacman/pkg"
mkdir -p "$RECOVERY/entries"
tar -C /var/lib/pacman \
-czf "$RECOVERY/local-before-repair.tar.gz" \
localPara cada entrada con desc vacío:
- Inferir el nombre del paquete.
- Mover la entrada a
$RECOVERY/entries. - Reinstalar desde repositorio si existe.
- Si no, buscar el paquete en
/var/cache/pacman/pkg. - Conservar una lista de pendientes.
El proceso dejó solo seis paquetes con files=0, todos verificados como legítimos metapaquetes.
15. Actualización completa
Después de reparar Bash y la base local:
rm -f /var/lib/pacman/db.lck
pacman -SyuLa primera ejecución detectó 509 actualizaciones, pero falló por conflictos de Bash antes de que su registro fuese reconstruido.
Después de reparar las entradas afectadas, la actualización pudo completarse.
16. Auditoría de archivos vacíos
Una búsqueda general:
find /usr/bin /usr/lib -type f -size 0produce mucho ruido legítimo:
__init__.pypy.typed.keep.stamp- fixtures
- datos de prueba
- archivos de bloqueo
- marcadores
La auditoría útil es:
echo '=== EJECUTABLES VACÍOS ==='
find /usr/bin \
-xdev \
-type f \
-size 0 \
-perm /111 \
-print
echo '=== BIBLIOTECAS COMPARTIDAS VACÍAS ==='
find /usr/lib \
-xdev \
-type f \
-size 0 \
\( -name '*.so' -o -name '*.so.*' \) \
-print17. Restos huérfanos de Protobuf
Se encontraron:
/usr/bin/protoc-33.1.0
/usr/bin/protoc-gen-upb-33.1.0
/usr/bin/protoc-gen-upb_minitable-33.1.0
/usr/bin/protoc-gen-upbdefs-33.1.0Características:
0 bytes
sin propietario PacmanLa versión vigente estaba íntegra:
protobuf 35.1-1
protobuf: 385 total files, 0 altered filesArchivos actuales:
/usr/bin/protoc-35.1.0
/usr/bin/protoc-gen-upb-35.1.0
/usr/bin/protoc-gen-upb_minitable-35.1.0
/usr/bin/protoc-gen-upbdefs-35.1.0Los cuatro restos 33.1.0 se eliminaron.
18. Validaciones finales
Base local
for d in /var/lib/pacman/local/*; do
[[ -d "$d" ]] || continue
entry="${d##*/}"
[[ "$entry" == "ALPM_DB_VERSION" ]] && continue
if [[ ! -s "$d/desc" ]]; then
echo "DAÑADO: $entry"
fi
doneResultado esperado:
sin salidaPaquetes reparados
pacman -Qkk bash
pacman -Qkk openssl
pacman -Qkk acl
pacman -Qkk ansible
pacman -Qkk ansible-core
pacman -Qkk protobufEjecutables esenciales
pacman --version
systemctl --version
protoc --versionReinicio
exitwsl --shutdown
wsl -d Arch19. Causa raíz y nivel de certeza
Causa raíz técnica confirmada
- Bibliotecas ELF críticas a cero bytes.
- Registros
descyfilesde la base local de Pacman vacíos o ausentes. - Archivos antiguos huérfanos a cero bytes.
- Coherencia rota entre sistema de archivos y base local de paquetes.
Causa desencadenante probable
Una operación de actualización o escritura quedó interrumpida y creó una corrupción parcial y localizada.
No demostrado
No se demostró que la actualización de ArchWSL 25.3.19.0 → 26.4.2.0 causara la corrupción. Fue una correlación temporal, no una relación causal probada.
20. Árbol de decisión reutilizable
wsl -d DISTRO falla
│
├─ ¿Otra distribución WSL arranca?
│ ├─ No → revisar WSL, virtualización y servicios Windows
│ └─ Sí → fallo aislado a DISTRO
│
├─ ¿Arranca como root y shell mínimo?
│ ├─ Sí → usuario, shell o perfiles
│ └─ No → fallo anterior al proceso solicitado
│
├─ ¿wsl --system permite leer la raíz?
│ ├─ No → VHDX, ruta, permisos o ext4
│ └─ Sí → inspeccionar PID 1 y dependencias
│
├─ ¿chroot ejecuta Bash?
│ ├─ No → glibc, loader o Bash
│ └─ Sí → ejecutar systemd y pacman
│
├─ ¿Aparece "file too short"?
│ ├─ Sí → localizar archivo, medirlo y restaurar paquete
│ └─ No → revisar logs de WSL y configuración
│
├─ ¿pacman puede ejecutarse?
│ ├─ No → restaurar bibliotecas o usar pacman-static
│ └─ Sí → validar /var/lib/pacman/local
│
├─ ¿desc está vacío o ausente?
│ ├─ Sí → apartar entrada y reinstalar paquete
│ └─ No → ejecutar pacman -Qkk
│
└─ Auditar ejecutables y bibliotecas vacías21. Comandos que deben evitarse durante el diagnóstico
wsl --unregister Arch
scoop uninstall archwslY, salvo reparación controlada:
pacman -Syu --overwrite '*'22. Medidas preventivas
-
Exportar periódicamente la distribución:
wsl --export Arch D:\WSL-Backups\Arch-YYYYMMDD.tar -
Mantener una copia apagada del
ext4.vhdx. -
No interrumpir
pacman -Syu. -
Comprobar espacio libre antes de grandes actualizaciones:
df -h / -
Revisar el log de Pacman:
tail -n 200 /var/log/pacman.log -
Auditar la base local tras una recuperación:
pacman -Qkk -
Conservar paquetes recientes en
/var/cache/pacman/pkg. -
Tener disponible una distribución auxiliar WSL o un binario
pacman-static.