Lo scorso fine settimana, un mio amico mi ha chiamato a causa di un problema con l'harddisk di suo padre: il computer, a quanto pare, era vicino ad una finestra e a causa di un semi-diluvio universale la finestra si è aperta ed il computer si è bagnato facendo dare i numeri all'alimentatore con il risultato che l'harddisk ha avuto problemi fisici.
Gli ho fatto portare l'harddisk da un amico con cui collaboro occasionalmente che ha un negozio di computer e ho tentato di recuperare il contenuto usando l'ottimo Easy Recovery: purtroppo arrivati ad un certo punto, circa il 3%, windows si bloccava inesorabilmente! Dopo svariati tentativi ho deciso di usare le maniere forti! Attaccato il disco ad una linux-box con su ubuntu 8.04 server (odio le interfacce grafiche) ho provato ad accedere al disco prima usando ntfs-3g, per tentare di effettuare una copia dei dati e, ricevendo una marea di errori, poi usando dd per fare una copia byte per byte dell'intero disco su un'altro disco! Il risultato è stato al quanto deludente: dd dava errore e anche dicendogli di bypassare, dopo un pò di errori si bloccava tutta la macchina costringendomi a riavviare.
Non può essere che anche linux fa cilecca :)
Mi sono messo a giocare un pò con hdparm per vedere se potevo in qualche modo disabilitare qualche ottimizzazione per la lettura dei dati e, per caso, mi sono accorto del parametro --read-sector di hdparm. Ho fatto una semplice implementazione in php per utilizzare hdparm per leggere settore per settore ... tah-da! Il sistema non si bloccava più.
Ho scaricato i sorgenti di hdparm (grazie linux e grazie open source!) ho preso i file sgio.c e sgio.h, che si occupano dell'interfacciamento con il relativo modulo del kernel, ho copiato il codice della funzione di copia del settore e ho riscritto un pò il codice per ottimizzarne il funzionamento!
Il risultato è stato che in circa 4 ore sono riuscito a fare una copia byte per byte del disco rotto (e vi assicuro che i click si sentivano belli forti) su un altro disco riuscendo poi con easy recovery a prendere tutti i dati servivano! Purtroppo non ho potuto copiarli normalmente perché la fine del disco a quanto pare era seriamente danneggiata e l'ntfs salva delle informazioni abb. importanti alla fine del disco.
In ogni caso, direi che sono abbastanza soddisfatto, anche se voglio implementare la lettura multipla dei settori per migliorare sensibilmente la copia dei dati! Inoltre sarebbe utile creare una semplice UI, anche usando le ncurses.
Qui uno stralcio dell'output
Read sector failed: Input/output error Error while reading 6161064 sector Recovery progress at 2%, copied 2.98 GB Read sector failed: Input/output error Error while reading 6311712 sector Read sector failed: Input/output error Error while reading 6311720 sector Read sector failed: Input/output error Error while reading 6311728 sector Recovery progress at 3%, copied 4.47 GB Recovery progress at 4%, copied 5.96 GB Recovery progress at 5%, copied 7.45 GB Recovery progress at 6%, copied 8.94 GB Recovery progress at 7%, copied 10.43 GB Recovery progress at 8%, copied 11.92 GB
Mentre qui la riga di comando usata per avviarlo
sudo time ./hdrecovery /dev/sdb /dev/sdc 0 $(cat /sys/block/sdc/size)
Com'è possibile vedere l'interfaccia utente è abbastanza minimale e inflessibile! I parametri, nell'ordine, indicano la destinazione dei dati copiati, che può essere un file o, come in questo caso, un altro disco, il disco da cui copiare i dati, il settore iniziale da cui partire a leggere ed il settore finale del disco, presente nel filesystem sys sotto la path block/NOME_DEVICE/size. La stringa $(cat /sys/block/sdc/size) è usabile se come shell si usa la bash o una compatibile (ad es. la dash) e serve ad acquisire il valore e sostituirlo alla stringa.
Il contenuto di size dovrebbe essere il doppio dei kb disponibili del disco questo perché un settore è 512 byte ed un kb è 1024 byte, infatti per il disco di 160gb (ovvero 149gb usando il moltiplicatore 1024) da cui sto leggendo i dati riporta 312581808 ed usando un semplice calcolo del tipo 312581808 / (2 * 1024 * 1024) si ottiene la dimensione in gigabyte!
Il fulcro del programma è composto da due spezzoni di codice, il primo:
static int read_sector(int fd, struct hdio_taskfile* command, __u64 sector_address) { // Initialize command init_hdio_taskfile(command, (sector_address >> 28) ? ATA_OP_READ_PIO_EXT : ATA_OP_READ_PIO, RW_READ, LBA28_OK, sector_address, 1, 512); // Do command if (do_taskfile_cmd(fd, command, 12)) { // Advise the user perror("Read sector failed"); // Error return -1; } // Sector readed return 0; }
Una semplice funzione che acquisisce dei parametri, tra cui il puntatore ad una struttura contenente le informazioni necessarie per inviare il comando al modulo sg ed il settore da leggere, e che restituisce un valore di tipo -1/0 per indicare il fallimento/riuscita dell'operazione
Mentre il secondo spezzone di codice
for(sector_index = start_sector; sector_index <= end_sector; sector_index+=8) { // Internal switches int skip_sector = 0; int write_sector = 0; // Check if all sectors have been readed if (sector_index < end_sector) { . . . // Try to read a sector int read_sector_result = read_sector(fd_block_device, command, sector_index); // Check read sector result if (read_sector_result == 0) { // Check if sector is empty . . . } // Can't read the sector! else { // Fill sector data with zero memset(command->data, 0, 512); // Set write switch write_sector = 1; // Set error switch error_founded = 1; // Advise the user } } // Check if must write sector, if there are too much sectors to skip in one time (to drop // overflow problems) or if all sectors have been readed and there are sectors to skip to // write if ( . . . ) { // Move forward if (lseek(fd_output, (off_t)(sectors_to_skip * 512), SEEK_CUR) == -1) { // Report the problem perror("Reason"); // Exit exit(-5); } // Reset sectors to skip sectors_to_skip = 0; . . . } // Check if needs to write data if (write_sector == 1) { // Write sector data int return_value = write(fd_output, command->data, 512); . . . } }
Si occupa di effettuare un ciclo for per tentare di copiare i settori uno ad uno sul disco di destinazione, controllando la presenza di eventuali errori o la mancanza di dati per poter saltare delle operazioni.
Nel caso in cui il settore letto è danneggiato vengono scritti dei valori null, ovvero dei byte con valore zero, per assicurarsi che eventuali riferimenti del filesystem in quelle posizioni non leggano dati residui, cosa molto facile se la destinazione è un device e non un file.
I sorgenti vanno scaricati dal seguente indirizzo:
http://www.dacci.it/Progetti/HDRecovery

Invia nuovo commento