Skip to content

Latest commit

 

History

History
255 lines (211 loc) · 11.6 KB

File metadata and controls

255 lines (211 loc) · 11.6 KB

DiskRecovery

🇪🇸 Español | 🇬🇧 English

TUI de recuperación y formateo de bajo nivel de discos para macOS, escrita en Rust (ratatui + crossterm). Pensada para el caso de un disco externo (p. ej. un Toshiba de 320GB con sectores lentos/dañados) que Disk Utility.app tarda mucho o se queda colgado intentando montarlo o verificarlo antes de mostrarlo.

⚠️ Esta herramienta formatea y borra discos a bajo nivel. Formatear (GPT/MBR) y el "wipe rápido" son operaciones irreversibles: se pierden todos los datos del disco elegido. Úsala con cuidado y verifica siempre el identificador del disco antes de confirmar.

Por qué no usa Disk Utility por debajo

Disk Utility (y el diskutil "amigable" que usa Finder) intenta montar y a veces verificar el sistema de archivos existente antes de mostrar el disco. Si el disco tiene sectores defectuosos o una tabla de particiones dañada, esa verificación puede tardar minutos o colgarse. DiskRecovery en cambio:

  • Enumera discos con diskutil list -plist (solo lista identificadores, no monta nada).
  • Pide los metadatos de cada disco con diskutil info -plist <id> (fabricante, tamaño, bus, esquema de particiones) sin forzar montaje.
  • Identifica automáticamente con una insignia ★ TOSHIBA 320GB cualquier disco cuyo nombre de medio contenga "TOSHIBA" y cuyo tamaño esté en el rango de 300–340 GB (los fabricantes redondean el tamaño distinto a como lo hace macOS).

Funcionalidad

  • Lista de discos con flechas ↑/↓, mostrando identificador, fabricante, tamaño, bus (USB/SATA/...) y esquema de particiones actual.
  • El disco de arranque del sistema queda siempre oculto (no puede seleccionarse ni borrarse por accidente).
  • Formatear como GPT o MBR, eligiendo sistema de archivos (ExFAT, MS-DOS FAT32, o Mac OS Extended Journaled solo para GPT).
  • Wipe rápido: pone a cero los primeros ~40MB (MBR protector + cabecera GPT primaria + tabla de particiones) y los últimos ~8MB (cabecera GPT de respaldo) del disco, usando dd sobre el dispositivo crudo (/dev/rdiskN). Esto invalida la tabla de particiones y hace que el disco vuelva a aparecer como "sin inicializar", sin necesidad de reescribir los 320GB completos.
  • Chequeo SMART (solo lectura, no modifica el disco): combina diskutil info <id> (que ya incluye SMART Status: Verified/Failing/Not Supported) con smartctl -a /dev/<id> si tienes instalado smartmontools (brew install smartmontools), para ver atributos detallados como Reallocated_Sector_Ct (sectores ya reasignados) o Current_Pending_Sector (sectores sospechosos pendientes de reasignar). Al ser de solo lectura, esta acción no pide confirmación ni identificador del disco: se ejecuta al pulsar Enter sobre ella.
  • Barrido completo (Barrido completo (fuerza reasignación de sectores dañados)): pone a cero el disco entero, sector por sector, usando el mismo mecanismo del wipe rápido pero sin limitarse a cabecera/cola. Forzar una escritura en cada sector hace que, si el firmware del disco encuentra uno que no puede escribir de forma fiable, lo reasigne automáticamente a un sector de repuesto (el "G-list"), si el disco todavía tiene repuestos disponibles. Esto no es una reparación física ni funciona si ya se agotó la reserva de repuestos o el fallo es mecánico (cabezal, motor); en esos casos ninguna herramienta de software puede arreglarlo. Es una operación larga (proporcional al tamaño completo del disco, no solo ~48MB como el wipe rápido) e igual de irreversible, así que pasa por la misma pantalla de confirmación con el identificador del disco. Recomendado: correr un chequeo SMART antes y después para comparar Reallocated_Sector_Ct y confirmar si de verdad se reasignó algo.
  • Confirmación obligatoria: antes de ejecutar cualquier acción destructiva hay que teclear exactamente el identificador del disco (ej. disk4), no un simple sí/no.
  • Modo simulación (--dry-run): recorre toda la interfaz y muestra exactamente qué comandos ejecutaría, sin tocar ningún disco real. Ideal para practicar antes de usarlo en modo real.
  • Progreso en tiempo real durante la ejecución:
    • Wipe rápido: barra de progreso con porcentaje real (bytes escritos / bytes totales, calculado escribiendo los ceros nosotros mismos en vez de delegar en dd), velocidad en MB/s y ETA estimado.
    • Formatear (GPT/MBR): diskutil no expone un porcentaje para eraseDisk, así que se muestra un spinner con tiempo transcurrido y la etapa actual ("Paso 1/2: Desmontando" → "Paso 2/2: Formateando").
    • En ambos casos: si pasan más de 5 segundos sin ninguna novedad de progreso, aparece un aviso de posible estancamiento (útil justamente para un disco con sectores dañados: la barra se detiene exactamente donde el disco empieza a fallar).
    • Al terminar, la pantalla de resultado muestra la duración total de la operación.
    • Importante sobre el 100%: en el wipe rápido, llegar al 100% solo significa que todos los bytes de ceros ya se escribieron (write); después de eso la app llama a fsync para confirmar que el disco los recibió de verdad. En un disco con sectores dañados, ese fsync final es el que puede colgarse (mismo tipo de bloqueo a nivel de kernel que un diskutil unmountDisk colgado). Por eso puedes ver la barra en 100% y el aviso de estancamiento a la vez: no es una contradicción, es el disco fallando justo en la confirmación final. Si eso ocurre, el proceso puede quedar en estado U (uninterruptible sleep) — ni kill -9 lo mata hasta que el disco responda o lo desconectes físicamente por USB.

Requisitos

  • macOS (usa diskutil, dd y /dev/rdiskN, específicos de macOS/BSD).
  • Rust (edición 2021 o superior).
  • Privilegios de administrador (sudo) para el modo real: escribir en /dev/rdiskN y ejecutar diskutil eraseDisk sobre discos internos requiere root.

Instalación

Opción 1: descargar el .app ya compilado

Descarga el .zip más reciente desde la página de Releases, descomprímelo y abre DiskRecovery.app. Como no está firmado con una cuenta de desarrollador de Apple, la primera vez Gatekeeper mostrará un aviso de "desarrollador no identificado": haz clic derecho sobre la app → Abrir, o ejecuta:

xattr -d com.apple.quarantine DiskRecovery.app

El .app abre una ventana de Terminal y ejecuta la TUI dentro (es una herramienta de terminal, no una GUI nativa).

Opción 2: compilar desde el código fuente

cargo build --release

El binario queda en target/release/diskrecovery.

Uso

Explorar la interfaz sin riesgo (no ejecuta ningún comando real):

./target/release/diskrecovery --dry-run

Uso real (formatear / hacer wipe de verdad):

sudo ./target/release/diskrecovery

Controles

Tecla Acción
↑ / ↓ Mover selección
Enter Elegir / confirmar
Esc Volver a la pantalla anterior
r Refrescar la lista de discos (pantalla de lista)
q Salir (deshabilitado mientras escribes una confirmación)

Flujo típico

  1. Selecciona el disco en la lista (busca la insignia ★ TOSHIBA 320GB si es el disco que Disk Utility tarda en mostrar).
  2. Elige una acción: Chequeo SMART, Formatear como GPT, Formatear como MBR, Wipe rápido o Barrido completo.
  3. Si formateas, elige el sistema de archivos.
  4. Lee la pantalla de advertencia y teclea el identificador del disco (ej. disk4) exactamente como se muestra, luego Enter.
  5. Espera a que termine y revisa el resultado (éxito/error con la salida del comando).

Pruebas unitarias

cargo test

Más de 70 pruebas cubren, sin tocar hardware real (usando un MockExecutor en memoria en lugar de diskutil/dd, y un TestBackend de ratatui en memoria en lugar de una terminal real):

  • Parseo de las salidas plist de diskutil list / diskutil info.
  • Detección del disco Toshiba de 320GB por nombre + tamaño.
  • Construcción exacta de los argumentos de diskutil eraseDisk para cada combinación de esquema (GPT/MBR) y sistema de archivos.
  • Cálculo del plan de "wipe rápido" (qué rangos de bytes se ponen a cero), incluyendo el caso límite de discos muy pequeños.
  • Que el "wipe rápido" solo ponga a cero cabecera y cola de un archivo de prueba (dejando intacta la zona intermedia), y que el progreso reportado sea monótonamente creciente hasta llegar al 100%.
  • Que el reloj de duración/estancamiento (executing_started_at, last_progress_at) se inicialice al confirmar una acción y se congele al terminar (éxito o error).
  • Que el disco de arranque del sistema nunca aparezca en la lista seleccionable.
  • Que la confirmación exija el identificador exacto del disco (no acepta mayúsculas/minúsculas distintas ni texto parcial).
  • La máquina de estados completa de la interfaz (navegación, menús, confirmación, ejecución) usando el executor simulado.
  • El renderizado de cada pantalla (lista, menú, opciones de formato, confirmación, ejecución determinada/indeterminada, resultado, error fatal) sobre un TestBackend en memoria, verificando que no hace panic y que muestra el texto esperado.

Diseño del código

  • src/disk.rs — modelo de datos de un disco y parseo de plist.
  • src/executor.rs — abstracción DiskExecutor (trait) con una implementación real (RealExecutor, ejecuta diskutil/dd) y una simulada para pruebas (testing::MockExecutor); construcción pura de argumentos de comando y del plan de wipe rápido.
  • src/safety.rs — guardas de seguridad (disco de arranque protegido, verificación de la frase de confirmación).
  • src/app.rs — máquina de estados de la aplicación (pantallas, navegación, transiciones), independiente de la terminal.
  • src/ui.rs — renderizado con ratatui de cada pantalla.
  • src/main.rs — cableado: terminal (crossterm), bucle de eventos, hilos para no bloquear la interfaz durante operaciones largas, CLI (--dry-run).

Ver .claude/skills/ para guías detalladas de arquitectura, testing, seguridad, i18n, diseño de la TUI y distribución, pensadas para escalar el proyecto manteniendo estos mismos principios.

Compilar el paquete de distribución (.app)

./scripts/build-macos-app.sh

Genera dist/DiskRecovery.app y dist/DiskRecovery-<versión>-macos.zip, listo para adjuntar a un GitHub Release. Ver .claude/skills/release-distribution/SKILL.md para más detalle.

Contribuir

Este proyecto es software libre y de código abierto (FOSS), licenciado bajo MIT (ver LICENSE). Los issues y pull requests son bienvenidos. Antes de mandar un cambio:

  1. cargo test en verde.
  2. cargo clippy --all-targets sin warnings nuevos.
  3. Si el cambio afecta texto visible o funcionalidad, actualiza ambos README (README.md en inglés y README.es.md en español).

Licencia

MIT © 2026 Wonder Diaz.

Advertencia final

Esta herramienta escribe directamente en dispositivos de bloque. Un uso incorrecto (elegir el disco equivocado) puede destruir datos de forma permanente e irrecuperable. Verifica siempre el identificador, el fabricante y el tamaño en la pantalla de confirmación antes de teclear el identificador y pulsar Enter.