MegaLab.it
Stampa Articolo
Aperiodico gratuito di informatica
 
20090625121531_705979872_20090625121450_342713463_sl_sync.png

Guida all'utilizzo di dd

a cura di masterz3d
30/01/2010 - articolo
Linux & Open Source - Nella suite di GNU Coreutils è presente un piccolo programmino, semplice e versatile, che può salvarvi la giornata... o essere la vostra rovina.

Dataset definition (dd) è uno strumento potente, piccolo e molto semplice, per convertire, leggere e scrivere dati da qualsiasi supporto. La presente guida vuole più che altro essere una specie di "codice di condotta" per poterlo utilizzare al meglio e non ritrovarsi con brutte sorprese.

Da ricordare: dd è molto potente

In GNU/Linux molto spesso ciò che è potente per un utente esperto è pericoloso per un utente alle prime armi. Un po' come una bestia feroce, quando si impara a conoscerla se ne riesce a vedere la forza e la bellezza, e si impara a non averne paura.

Prima di andare avanti dovete ricordare una cosa, tra le più importanti: Se gli vengono date le opzioni giuste (o le opzioni sbagliate, a seconda dei punti di vista), dd ignora del tutto la struttura dei filesystem e delle partizioni, e vi ritrovate in mano un programmino che può distruggere tutti i contenuti di una unità di memorizzazione sulla quale sia possibile scrivere, o che può cancellare in modo sicuro i dati che vi interessa rimuovere, in modo da renderli irrecuperabili con strumenti software comuni.

Prima di cominciare: cenni sulle periferiche in /dev

Prima di andare avanti occorre prima fare un po' di chiarezza sulla struttura della directory /dev, ed in particolare sulle suddivisioni operate su una unità fisica tramite il partizionamento.

In GNU/Linux, uno degli assiomi più comuni recita: In Linux, ogni cosa è un file.

Dove, con "ogni cosa" intendo proprio "ogni cosa": tastiere, mouse, webcam, schermi, terminali, schede audio, schede video... dischi rigidi e le loro partizioni.

Tutti questi file sono raggruppati nella directory /dev. Le periferiche sono raggiungibili ed è possibile cambiarne lo stato semplicemente manipolandole in /dev manualmente (tramite cat, echo o dd stesso) o usando alcuni tra i molti programmi di configurazione periferiche esistenti, come, ad esempio, ifconfig o iwconfig nel caso di schede di rete.

Ed è molto importante anche tenere presente che il potere di modifica o manipolazione delle periferiche si può raggiungere, ad un livello relativamente avanzato, "riversando" dei dati in un determinato blocco di periferica.

Un esempio molto carino e semplice da fare, consiste nel sentire il "suono" di un file (come, nell'esempio, il kernel Linux) attraverso la scheda audio, semplicemente prendendone il contenuto e riversandolo in /dev/dsp nel modo seguente:

dd if=/boot/bzImage of=/dev/dsp

Oppure

cat /boot/bzImage > /dev/dsp

Sentirete una sequenza incomprensibile di sibili, crepitii, basse vibrazioni e fruscii, ma avrete una dimostrazione concreta di come si possa manipolare facilmente una qualsiasi periferica semplicemente reindirizzando un file (o anche l'output di un programma...) ad essa con dd od una redirezione di cat.

Ricordate comunque che "giochetti" di questo tipo sono alquanto sconsigliabili, specialmente se il blocco di destinazione è una partizione o un disco fisico, a meno che non siate assolutamente sicuri di quello che state facendo... o non lo facciate sotto la supervisione di un esperto.

Allo stesso modo dei blocchi "classici" di periferica che indirizzano schede video, schede audio, stampanti, scanner, eccetera, i blocchi che indicizzano partizioni (che potremo chiamare anche "dischi logici") e dischi fisici possono essere manipolati, in lettura o scrittura, attraverso programmi come cat o dd. Ed ora arriviamo ad una distinzione estremamente importante, tra disco fisico (il supporto materiale su cui sono memorizzate delle quantità di dati) e disco logico (o la partizione creata per potervi installare un filesystem).

Qualche parola su dischi fisici e logici

Partizionare un disco significa suddividerlo in "fette" che vengono poi configurate tramite la costruzione di filesystem su di essi. Un disco rigido viene visto dal kernel Linux con un nome fisico (che può essere sda o hda) e uno o più nomi di partizioni, o unità logiche (sda1, sda2...) che designano le partizioni stesse ed i filesystem al loro interno.

Riferirsi ai nomi "fisici" (sda o hda) significa riferirsi all'intera unità, dal primo all'ultimo settore. Quindi, volendo leggere tutto il disco sda, dal primo all'ultimo settore, con dd potremo usare un comando come il seguente:

dd if=/dev/sda bs=512k >/dev/null

Oppure, con cat:

cat /dev/sda >/dev/null

In questo caso, il pregio di dd è la possibilità di effettuare un test di velocità di lettura del disco sda, potendo verificare la velocità del supporto fisico al quale è connesso (IDE, SATA, SCSI, eccetera) come specificato dall'output del programma, controllando nello stesso tempo che il supporto che andiamo a leggere non abbia dei settori danneggiati. Usando cat si può solo verificare che il disco non abbia degli errori in scrittura (verificabili in entrambi gli scenari tramite dmesg cercando di messaggi di I/O error). Ma in entrambi i casi servono a dimostrare che, conoscendo i comandi giusti, in GNU/Linux possiamo piegare l'intero sistema al nostro volere.

Riferirsi ai nomi di partizione vuol dire invece prendere in considerazione tutti e soli i settori della partizione esaminata, come descritto nella tabella di partizione (nel settore 0 LBA, o Master Boot Record), leggibile tramite una semplice riga di sfdisk:

sfdisk -l /dev/sda

Prendendo in considerazione la prima partizione del primo disco rigido installato nel sistema (sda1) possiamo leggerla con uno dei due comandi seguenti:

dd if=/dev/sda1 >/dev/null

cat /dev/sda1 >/dev/null

E allora, a che serve tutto questo?

Alla luce degli elementi evidenziati, se intendete ripulire una sola partizione e sulla riga di comando di dd vedete scritto un nome fisico significa che c'è qualcosa che non va e dovete prestare attenzione per non combinare disastri irreparabili. Ecco perché è estremamente importante, se volete "giocare" con dd, saper distinguere un disco fisico da un disco logico (o partizione) in /dev. È meglio sentirselo ripetere una volta di più, anche se lo si sa già.

Utilizzo delle opzioni

Prima di tutto è importante notare che, a differenza della stragrande maggioranza di software GNU/Linux, che utilizzano una notazione --opzione=valore o --opzione valore come rappresentazione "lunga" compatibile con gli standard POSIX, le opzioni di dd seguono una notazione del tipo opzione=valore, come nell'esempio.

dd if=/dev/random of=/dev/null conv=notrunc bs=4096

Unità di misura e ordini di grandezza

I valori numerici accettati da dd in opzioni come bs, obs, ibs, seek, skip eccetera si possono rappresentare anche con ordini di magnitudine (kilo, mega, giga, tera...), ma è altresì importante ricordare che ogni ordine di magnitudine è di tipo informatico, ovvero:

Come correttamente approfondito nell' articolo sulle unità di misura informatiche di MegaLab.it, quindi in tutto il resto dell'articolo invece di kilobyte si parlerà di kibibyte, invece di megabyte si parlerà di megabyte, e così via.

Analogamente, per rappresentare 4194304 byte (4 megabyte), invece di 4M si può usare la notazione 4096k (4096 kibibyte).

Tipi di dati in entrata ed in uscita

Tra le opzioni più comuni sulla riga di comando di dd vi è un file sorgente, specificato dall'opzione if, da cui leggere ed un file destinazione, identificato da of, in cui scrivere.

In assenza di questi due parametri, dd prende in entrata un flusso di dati proveniente da un altro programma ed in uscita genera un flusso di dati analizzabile con un altro software. Per il flusso in entrata si parla di standard input (o stdin, o descrittore 0) e il flusso di uscita è chiamato in gergo standard output (o stdout, o descrittore 1).

In altre parole, l'utilizzo più semplice di dd è una "traduzione" di dati che, senza alcuna opzione, viene restituita senza variazioni, quando stdout è uguale a stdin:

cat file | dd 2>/dev/null

È esattamente uguale a

cat file

Utilizzo con altri programmi

Oltre ad essere potente, dd è estremamente versatile e il suo stdout può essere usato come stdin di altri software che accettino un flusso di dati in entrata. Esempi molto comuni sono hexdump (converte il codice macchina in entrata in un formato esadecimale ordinato intelligibile), file (determina il tipo del file preso in esame), gzip (comprime dei dati in entrata, compreso un flusso, in un file di formato LZ77-Huffman), sfdisk (programma per partizionare dischi rigidi), tr (converte un determinato set di caratteri in un altro set di caratteri), sed (stream editor), eccetera.

Adesso che abbiamo fatto un po' di chiarezza sulle caratteristiche generali di dd, possiamo passare a fare un po' di pratica.

Piccole prove

Adesso salviamo un piccolo file ASCII per le nostre prove. Entriamo nella directory home dell'utente correntemente attivo:

cd ~

Creiamo un piccolo file di testo:

echo "Questo è un test per dd" > test.dd

Ora vogliamo far leggere direttamente a dd il file creato.

dd if=test.dd

L'output sarà simile al seguente:

Questo è un test per dd
0+1 records in
0+1 records out
25 bytes (25 B) copied, 3.6385e-05 s, 687 kB/s

Le ultime tre righe sono un messaggio interno di dd che informano l'utente dei dati copiati, con le caratteristiche dei blocchi in entrata e in uscita, il tempo impiegato e la velocità dell'operazione espressa in kB/s. Le informazioni sono inviate a standard error (stderr o descrittore 2).

Il testo del file potrebbe anche venire scritto dopo il messaggio di status, ma ciò è irrilevante.

Se volete sopprimere queste "righe di stato" dovete aggiungere la dicitura 2>/dev/null alla fine della riga:

dd if=test.dd 2>/dev/null

E avrete il testo pulito:

Questo è un test per dd

Analogie con cat e redirezioni

Probabilmente i più attenti tra voi si saranno accorti che l'ultimo comando è esattamente uguale ad una lettura tramite cat:

cat test.dd

Così come la lettura del testo con un semplice cat è equivalente al comando seguente:

cat test.dd | dd 2>/dev/null

Il che ci dimostra che dd da solo prende i dati in standard input e li riversa in standard output, come abbiamo evidenziato nella pagina precedente.

Ora leggiamo e scriviamo come in un vaso comunicante, dal testo di esempio ad un file di destinazione:

dd if=test.dd of=test2.dd 2>/dev/null

Si noti che il trasferimento dei dati con le opzioni if e of è equivalente alla redirezione della shell:

dd if=test.dd >test2.dd 2>/dev/null

Oppure:

cat test.dd | dd >test2.dd 2>/dev/null

Oppure, ancora, solo usando cat:

cat test.dd > test2.dd

Leggiamo poi il risultato con dd:

dd if=test2.dd 2>/dev/null

Questo è un test per dd

Sono tutti procedimenti equivalenti: ve li ho fatti vedere per dimostrarvi che molto spesso in GNU/Linux è possibile raggiungere lo stesso risultato usando strade diverse, con procedure diverse e programmi diversi. Notate che non ho mai usato una comando di copia file:

cp test.dd test2.dd

Sebbene l'effetto sperimentato con dd sia stato esattamente lo stesso.

Introduzione a bs, seek, skip e count

Quattro opzioni molto usate di dd sono:

Volendo fare una piccola prova con il file di testo che abbiamo creato, possiamo incorporare tre delle opzioni sopra descritte.

dd if=test.dd of=test3.dd count=2 bs=5 skip=1 2>/dev/null

Partendo dal file origine test.dd e leggendo con cat test3.dd avremo il seguente risultato:

o è un te

Analizziamo il risultato:

E non viene considerato il carattere di newline alla fine del testo generato con echo quando lo abbiamo creato; quindi il prompt viene posizionato sulla stessa riga di stdout, subito dopo l'ultimo carattere di destra.

Attenzione!! Potreste obiettare che i caratteri risultanti, compresi gli spazi, siano nove. Ebbene, avete ragione. Ma dovete sapere che la e accentata, in alcuni sistemi, può arrivare a misurare due byte (ovvero 16 bit, e dipende dal locale, dalla distribuzione, dalla versione di glibc...).

Se il risultato che vedete è di nove caratteri potete provare a convertire la e accentata in una e non accentata e ripetere il procedimento. Il risultato sarà:

o e un tes

E conteremo esattamente 10 caratteri.

Block Size in Entrata ed in Uscita

L'opzione bs (Block Size) può essere divisa in due, una per la dimensione dei blocchi in entrata (ibs, Input Block Size) e una per la dimensione dei blocchi in uscita (obs, Output Block Size).

In presenza di queste due opzioni dd leggerà in una volta il numero di byte specificati in ibs e scriverà in una volta il numero di byte specificati in obs. Questo permette di selezionare una determinata dimensione di blocco in entrata ed in uscita.

Sembra una caratteristica triviale, ma sappiate che esistono filesystem (come ISO9660) dove, in presenza di un numero di byte per blocco differente da una determinata specifica tale filesystem può risultare illeggibile da alcuni lettori multimediali. Ecco che allora è necessario leggere un flusso di dati ibs byte per volta, e scriverlo obs byte per volta.

File speciali

Il potere più grande di dd è quello di rendere possibile la scrittura e la lettura di precisione da e per file speciali in /dev, tra cui /dev/zero, /dev/null, /dev/random e i molti descrittori di unità fisiche e di partizioni.

Una semplice implementazione di lettura di un carattere speciale è la lettura di /dev/zero per creare file vuoti:

dd if=/dev/zero of=file_vuoto bs=4k count=1k

Oppure

dd if=/dev/zero of=file_vuoto bs=4096M

Crea un file vuoto (ovvero pieno di zeri) grande 4 MiB (4096*1024=4194304 byte) in cui è possibile persino creare un filesystem su file, per usi avanzati come ramdisk, partizioni virtuali, partizioni criptate su disco, macchine virtuali...

Procedure di base

Attenzione!! L'esecuzione dei comandi seguenti, se non correttamente riportati, può provocare danni irreparabili ai vostri filesystem!

Nota: L'esecuzione di alcuni dei comandi sotto elencati può richiedere l'utilizzo di sudo.

Dd può risultare utile per fare dei backup 1: 1 di un disco rigido completo:

dd if=/dev/sda bs=4k | gzip -9 > disk.gz

Chiaramente, dovrete avere spazio sufficiente per contenere tutti i dati di un intero disco rigido.

Con questo comando prendiamo tutti i byte di un disco, dal primo all'ultimo, in blocchi da 4096, e li comprimiamo con gzip. Allo stesso modo possiamo fare il backup di una singola partizione di un disco:

dd if=/dev/sda1 bs=4k | gzip -9 > disk1.gz

Il comando è identico, solo che ora consideriamo la prima partizione del disco /dev/sda.

Oppure possiamo, seguendo lo stesso procedimento, fare un backup compresso di un disco DVD:

dd if=/dev/sr0 bs=2k | gzip -9 > dvdrom.gz

Dove /dev/sr0 è l'unità DVD-ROM considerata, così come il kernel Linux la identifica.

Per rimettere i dati al loro posto usiamo i seguenti comandi, rispettivamente per il disco rigido:

zcat disk.gz | dd of=/dev/sda bs=4k

Per la partizione:

zcat disk1.gz | dd of=/dev/sda1 bs=4k

Per quanto riguarda il DVD dobbiamo prima decomprimere l'archivio gzip:

zcat dvdrom.gz > dvdrom.iso ; rm dvdrom.gz

E poi scriverlo su un altro disco DVD con un programma come growisofs:

growisofs -Z /dev/dvd=dvdrom.iso.

Copie di unità ottiche

Probabilmente vi sarete accorti che ora potete fare delle copie perfette di un CD o un DVD soltanto usando dd, come in un esempio precedente, che riporto:

dd if=/dev/sr0 bs=2k > disco.iso

L'estensione è .iso perché il file è, a tutti gli effetti, un filesystem ISO, che può essere montato come se fosse un disco reale inserito nel lettore CD/DVD del vostro computer. E per fare questo potete tornare al mio articolo di MegaLab.it che spiega come navigare in un filesystem ISO su GNU/Linux usando soltanto mount.

Questi filesystem possono essere comodamente custoditi su un disco rigido per poi essere riscritti su un nuovo supporto CD o DVD, qualora voi lo vogliate, usando growisofs o cdrecord.

Notate che è possibile raggiungere lo stesso risultato, in modo ancora più semplice e raffinato, con una semplicissima invocazione di cat:

cat /dev/sr0 > disco.iso

E vi ho dimostrato ancora una volta che in GNU/Linux è possibile raggiungere uno stesso traguardo lungo strade diverse.

Cancellazione sicura di un disco

La massima potenza di dd si manifesta quando vogliamo cancellare irreparabilmente dei dati, per renderli anche irrecuperabili da altre persone.

Per cancellare completamente dei dati da un supporto fisico:

dd if=/dev/zero of=/dev/sda bs=512

In questo caso viene azzerato tutti il contenuto del disco fisico sda, con un block size di 512 byte, dal primo all'ultimo blocco del disco. I dati non saranno più recuperabili con supporti convenzionali!!

Ricordando che un settore di un disco rigido contiene 512 byte, vediamo che un blocco equivale ad un settore, quindi l'azzeramento viene fatto settore per settore. Che un blocco sia di 512, 4096 o 16384 byte non si ha un reale incremento della velocità; generalmente dipende tutto dalla velocità di lettura e scrittura del supporto fisico.

Se volete riempire il disco con dei dati casuali, potete usare /dev/urandom:

dd if=/dev/urandom of=/dev/sda bs=512

Per una maggiore sicurezza è possibile ripetere la cancellazione tutte le volte che si ritiene siano necessarie per una buona eradicazione dei dati. Ricordate sempre che la cancellazione richiede un certo tempo, che dipende dalla dimensione del supporto da cancellare (sia esso un disco, una partizione o un file), dalla velocità del bus in scrittura, dalla velocità della RAM e del processore... in poche parole, non aspettatevi di cancellare un disco rigido da 160GB in cinque minuti!

Cancellazione sicura di un file

Il file di blocco di of può essere sostituito con un qualsiasi file su un filesystem, e la cancellazione sicura verrà fatta su quell'unico file. Ad esempio, se vogliamo cancellare un file in modo sicuro, lanciamo il seguente comando:

dd if=/dev/zero of=some_file bs=4k

Oppure lo possiamo rendere inservibile riempiendolo di dati casuali:

dd if=/dev/urandom of=some_file bs=4k

Il procedimento potrà essere ripetuto a piacere per un effetto più marcato della cancellazione. Dobbiamo infine scollegare la voce dal filesystem con un semplice rm:

rm some_file.

Rimuovere il primo stadio del bootloader

Per azzerare i primi 446 byte di un Master Boot Record da eventuali malware che infettano il settore 0:

dd if=/dev/zero of=/dev/sda bs=446 count=1

Ricordate poi di reinstallare il bootloader, sia esso GRUB o il bootloader di un sistema operativo Microsoft, altrimenti potreste avere un sistema non avviabile!!!

Se invece volessimo azzerare l'intero MBR potremmo usare questo comando:

dd if=/dev/zero of=/dev/sda bs=512 count=1

Rimuovendo l'intero MBR rimuoverete sia il bootloader che la tabella di partizione, e dovrete ripetere sia il partizionamento del disco che l'installazione del bootloader.

Rimozione di una tabella di partizione dal Master Boot Record

Viceversa, un utilizzo più avanzato di dd riguarda la rimozione di una tabella di partizione lasciando invariati tutti gli altri dati (bootloader, disk signature, eccetera):

dd if=/dev/zero of=/dev/sda ibs=2 obs=2 seek=223 count=32

Dove pone le dimensioni dei blocchi in lettura a 2 byte, uguale alla dimensione dei blocchi in scrittura. Salta i primi 2*223 byte (446 byte) e sovrascrive con zeri (/dev/zero) i successivi 2*32 byte (i 64 byte della tabella di partizione).

Oppure

dd if=/dev/zero of=/dev/sda ibs=64 obs=2 seek=223 count=1

Dove sostanzialmente l'effetto è lo stesso, ma invece di cancellare 32 blocchi da 2 byte cancella direttamente 1 blocco da 64.

Controllate sempre le opzioni di dd!! Devono corrispondere agli esempi riportati!!

Per maggiori informazioni sul Master Boot Record e sulla sua struttura, potete andare alla scoperta del Master Boot Record in quest'articolo.

Verifica della velocità di dd

In qualsiasi momento potete controllare che cosa stia facendo dd inviandone il segnale SIGUSR1 attraverso kill da un altro terminale. Dovete usare un altro terminale poiché, a meno che non abbiate relegato dd in background (posponendo il carattere & alle righe di comando), esso si prenderà il terminale in cui è stato invocato e, nel caso di scritture particolarmente lunghe potreste aver il bisogno di controllare a che punto sia.

Allora andate in un altro terminale e invocate kill con la seguente riga:

kill -USR1 $(pidof dd)

Ora non dovete fare altro che tornare allo stesso terminale di invocazione di dd e leggere l'output di stderr, lo stesso che compare al termine della scrittura.

Ricordate però che se avete reindirizzato stderr in /dev/null nella riga di invocazione non vedrete scritto nulla da nessuna parte, non importa quante volte ripeterete il kill, né dove andiate a vedere o quanto tempo aspettiate.

Conclusioni

È inutile sottolineare che l'utilizzo di dd è da consigliare solo sotto controllo di un utente esperto, o da parte di uno stesso utente esperto. dd non considera la struttura di un filesystem, ma agisce direttamente sul supporto di memorizzazione fisico e ignora del tutto il supporto logico, che è rappresentato dalle partizioni o dai filesystem stessi, qualora si specifichi una periferica di memorizzazione fisica (sda, sdb, sdc...), quindi è necessaria una gestione responsabile del programma e dei suoi effetti.

Non mi stancherò mai di ripeterlo: dd può diventare uno strumento molto avanzato, e non lo usate se non siete sicuri di ciò che fate.

MegaLab.it rispetta la tua privacy. Per esercitare i tuoi diritti scrivi a: privacy@megalab.it .

Copyright 2008 MegaLab.it - Tutti i diritti sono riservati