Nelle più recenti distribuzioni Linux è presente smartd
(8), una potente utility per il monitoraggio dei dischi ATA, IDE e SCSI-3.
SMART (Self-Monitoring, Analysis and Reporting Technology) è una tecnologia sviluppata per monitorare l’affidabilità degli hard disk, eseguire dei test di funzionamento e quindi tentare di prevedere eventuali failure.
Talvolta ci sarà capitato di rilevare errori riportati dal kernel, dal demone smartd o tramite sistemi di altra natura, del tipo:
Apr 17 16:20:31 seti kernel: hde: dma_intr: status=0x51 { DriveReady SeekComplete Error } Apr 17 16:20:31 seti kernel: hde: dma_intr: error=0x40 { UncorrectableError }, LBAsect=6305975, sector=6305901
Questo genere di errori sono tipicamente corrispondenti ad errori presenti in lettura o scrittura sul nostro hard disk.
Inoltre tale errori, in ambiente SMART, corrispondono a messaggi di log del tipo:
Apr 18 09:05:03 seti smartd[2253]: Device: /dev/hde, 1 Currently unreadable (pending) sectors
che inequivocabilmente ci informano circa l’impossibilità di accedere in lettura o in scrittura ad un settore (danneggiato) del disco.
Arrivati però a questo punto, cosa possiamo fare?
Nel nostro esempio, uno dei casi più complessi, il settore danneggiato si trova in una partizione LVM; sarà quindi nostro obiettivo cercare di individuare il relativo blocco fisico danneggiato e quindi riparare, se possibile, l’errore.
In generale è bene ricordare che il settore è la minima unità gestibile contenente dati mentre il blocco è un multiplo della dimensione del settore del disco stesso; allo stesso tempo il contenuto di un file può essere distribuito su uno o più blocchi di dati.
Cominciamo con un semplice test eseguito sul disco sul quale sono presenti gli errori sopraesposti:
# smartctl -t long /dev/hde
In questo caso attraverso l’utility smartctl(8) sarà eseguito un test approfondito, dalla durata di circa mezz’ora, sul device /dev/hde.
Al termine dello stesso, sarà possibile recuperare i risultati del test appena terminato, in questo modo:
# smartctl -l selftest /dev/hde [...] === START OF READ SMART DATA SECTION === SMART Self-test log structure revision number 1 Num Test_Description Status Remaining LBA_of_first_error # 1 Extended offline Completed: read failure 40% 6305975
Come si può notare dall’output restituito, il test appena eseguito si è interrotto al 40% avendo individuato un errore nell’indirizzamento di blocco logico (Logical Block Addressing – LBA) corrispondente al settore numero 6305975.
Andiamo a controllare come è partizionato il nostro disco fisso:
# fdisk -lu /dev/hde Disk /dev/hde: 61.4 GB, 61492838400 bytes 255 heads, 63 sectors/track, 7476 cylinders, total 120103200 sectors Units = sectors of 1 * 512 = 512 bytes Device Boot Start End Blocks Id System /dev/hde1 * 63 208844 104391 83 Linux /dev/hde2 208845 120101939 59946547+ 8e Linux LVM
La prima cosa che facilmente si nota è che l’indirizzo logico del blocco si riferisce ad un settore presente nella partizione Linux LVM /dev/hde2.
A questo punto però si deve fare attenzione: l’LBA dell’errore restituito in precedenza, 6305975, costituisce il valore assoluto di indirizzamento logico della memoria di massa complessiva.
A noi invece, visto che si sta andando a lavorare all’interno di una singola partizione, occorrerà determinare l’offset corrispondente: di fatto sottrarremo dal valore dell’indirizzo riportato in precedenza il valore dell’indirizzo dal quale inizia la partizione LVM interessata.
Quindi, nel nostro esempio, l’offset cercato sarà:
6305975 – 208845 = 6097130 (FS block offset)
Inoltre, ricordando che ogni partizione LVM verrà poi organizzata in Physical Extent (PE) della stessa grandezza, ed essendo in presenza di allocazione lineare, il secondo passo consisterà nel determinare la dimensione (qui di seguito espressa in KB), di ogni PE tramite il comando:
# part=/dev/hde2; pvdisplay -c $part | awk -F: '{print $8}' 32768
Considerando che il physical block size del sistema corrisponde a 512 bytes (cioè metà KB), si avrà che la dimensione di ogni PE sarà uguale a:
32768 * 2 = 65536 (PE physical size)
Quindi, per determinare in quale PE si trovi il blocco logico rovinato, sarà sufficiente dividere l’FS block offset precedentemente calcolato per la dimensione di ogni PE appena ottenuta:
6097130 / 65536 = 93.034 -> 93 (#PE)
Sappiamo perciò che il blocco logico in questione risiede sul 93-esimo PE.
Di fatto, attraverso l’output del comando:
# lvdisplay --maps |egrep 'Physical|LV Name|Type' LV Name /dev/VolGroup00/LogVol00 Type linear Physical volume /dev/hde2 Physical extents 0 to 1800 LV Name /dev/VolGroup00/LogVol01 Type linear Physical volume /dev/hde2 Physical extents 1801 to 1828
possiamo conseguentemente determinare che il PE considerato appartiene al primo Logical Volume (LV) chiamato /dev/VolGroup00/LogVol00
del Volume Group (VG) /dev/VolGroup00
.
Visto che ora si andrà ad intervenire scendendo dal livello di partizione LVM a livello di LV, ci occorrerà anche sapere l’offset iniziale di quest’ultimo, ottenuto sommando il blocco logico di partenza del VG e tutti i blocchi logici relativi ai PE degli LV precedenti. Tramite il comando:
# grep pe_start $(grep -l $part /etc/lvm/backup/*) pe_start = 384
sarà quindi prima possibile determinare il blocco logico di partenza del VG; poi, osservando che il nostro LV è di fatto il primo LV del VG, si determina che il primo PE di /dev/VolGroup00/LogVol00 è uguale a 0 (mentre se ad esempio avessimo dovuto lavorare sul secondo LV sarebbe stato uguale a 1801). Quindi risulterà:
384 + (0 * 65536) = 384 (LV offset)
Finalmente sarà possibile determinare il blocco fisico danneggiato del file-system secondo la formula:
((FS block offset) - (LV offset)) / ((PE physical size) / (physical block size))
ovvero:
((6097130 - 384) / (65536 / 512)) = (6096746 / 128) = 47630.828 -> 47630
Nel calcolo è da notare come il valore 128 corrisponda al numero di blocchi fisici presenti in ogni PE.
Siamo finalmente arrivati!
Testiamo ora che effettivamente il blocco fisico #47630 sia danneggiato attraverso il comando dd(1) e l’opzione ‘skip’ che consente di leggere l’input saltando i primi 47630 blocchi fisici (ovvero tentando di leggere esattamente il blocco fisico danneggiato):
# dd if=/dev/VolGroup00/LogVol00 of=/tmp/block-47630 bs=65536 count=1 skip=47630 dd: reading `/dev/VolGroup00/LogVol00': Input/output error 0+0 records in 0+0 records out 0 bytes (0 B) copied, 18.6989 seconds, 0.0 kB/s
Non ci resta altro da fare che formattare il blocco fisico danneggiato (‘scrivendo zeri’ per una lunghezza pari alla dimensione del PE physical size) attraverso il comando dd(1) e l’opzione ‘seek’ che consente di scrivere in output saltando i primi 47630 blocchi fisici (ovvero accedendo esattamente al blocco fisico danneggiato):
dd if=/dev/zero of=/dev/VolGroup00/LogVol00 count=1 bs=65536 seek=47630
ed il gioco (si fa per dire) è fatto!
Possiamo infine, come verifica, dopo aver ripetuto il testo iniziale attraverso il comando smartctl -t long /dev/hde
interrogare il relativo log ottenendo il seguente risultato:
# smartctl -l selftest /dev/hde [...] === START OF READ SMART DATA SECTION === SMART Self-test log structure revision number 1 Num Test_Description Status Remaining LBA_of_first_error # 1 Extended offline Completed without error 00% - # 2 Extended offline Completed: read failure 40% 6305975
che ci mostra come quest’ultimo test (Num #1) sia stato portato a termine senza errori, e che quindi l’errore del nostro hard disk sia stato efficacemente corretto.