Un file da 500 GB dovrebbe richiedere 500 GB di spazio libero: è una conclusione apparentemente ovvia, ma non sempre corretta. Su Linux e Windows, ad esempio, è possibile creare un file che il sistema operativo considera grande centinaia di gigabyte, pur occupando fisicamente sul supporto soltanto pochi kilobyte o megabyte. Il meccanismo si basa sui cosiddetti sparse file, o file sparsi: file nei quali il filesystem evita di allocare fisicamente le regioni che non contengono dati significativi.
Non si tratta di compressione: il file possiede realmente la dimensione dichiarata e un programma può leggere qualsiasi posizione compresa tra il primo e l’ultimo byte. La differenza è che alcune porzioni non esistono materialmente sul disco: il filesystem sa che devono essere considerate pari a zero e restituisce quindi byte 00 quando un’applicazione prova a leggerle.
GNU definisce gli sparse file come file contenenti “holes“, ovvero intervalli di byte pari a zero ai quali non corrispondono blocchi del filesystem. Una normale chiamata di lettura vede comunque quegli intervalli come sequenze di zeri.
Windows e NTFS adottano lo stesso principio: Microsoft descrive uno sparse file come un file contenente una o più aree di dati non allocati: le applicazioni vedono quelle regioni come byte a zero, anche se sul disco non esiste spazio fisico dedicato a rappresentarli.
Conoscere questo tipo di architettura è importante perché gli sparse file compaiono in numerosi ambiti: immagini di dischi virtuali, database, backup, container, sistemi di elaborazione scientifica e applicazioni che devono rappresentare spazi molto grandi senza allocarli interamente fin dall’inizio.
Dimensione del file e spazio occupato sono due cose diverse
Prima di proseguire è bene fare una distinzione tra dimensione logica e spazio allocato. È lo stesso principio che, in Windows, porta a vedere valori differenti nelle voci Dimensioni e Dimensioni su disco: la prima indica quanto è grande logicamente il file, mentre la seconda riflette quanto spazio il filesystem ha effettivamente impegnato sul supporto.
Supponiamo che soltanto 10 MB all’inizio e altri 10 MB verso la fine contengano dati effettivi all’interno del file. Un approccio tradizionale potrebbe richiedere di memorizzare anche tutti gli zeri presenti nella regione intermedia.
Uno sparse file può invece avere questa situazione:
- Dimensione logica: 500 GB
- Spazio realmente allocato: 20 MB
La parte centrale è appunto rappresentata tramite uno o più holes, come spiegato in apertura. Se un programma esegue una lettura proprio dentro un hole, il filesystem restituisce 00 00 00 00 00 00 00 00… ma quei byte non devono necessariamente essere stati letti dall’unità di memorizzazione, ad esempio da SSD.
Microsoft spiega esplicitamente che, leggendo una regione azzerata di uno sparse file, Windows può evitare la lettura fisica dal dispositivo e restituire direttamente all’applicazione un buffer composto da zeri.
Creare un file da 500 GB in pochi istanti su Linux
Linux permette di osservare il fenomeno degli sparse file con un esperimento molto semplice. Creiamo un file di test: il comando di seguito non deve scrivere 500 GB di zeri sul supporto. Modifica la dimensione logica del file e il filesystem può rappresentare la nuova area come non allocata.
truncate -s 500G test.img
Verificando con il comando ls, per Linux il file è realmente pesante 500 GB. Impartendo invece il comando che segue si ottiene 0 byte o comunque un valore estremamente piccolo:
du -h test.img
Il motivo è che i due programmi stanno misurando grandezze differenti: ls mostra la dimensione logica; du, per impostazione predefinita, considera invece lo spazio fisicamente allocato. È anche possibile richiedere la dimensione apparente:
du -h --apparent-size test.img
Un’altra verifica utile consiste nell’eseguire stat test.img: tra le informazioni mostrate troviamo Size e Blocks.
In uno sparse file appena creato potremmo ottenere una situazione simile:
Size: 536870912000 Blocks: 0
Il valore esatto dipende dal filesystem e dalle modalità di allocazione, ma il concetto rimane identico: il file possiede una lunghezza logica importante pur avendo pochissimi blocchi fisici associati.
Il problema della copia: un file da pochi MB può diventare davvero da 500 GB
Supponiamo di avere il file test.img di prima con dimensione logica pari a 500 GB e spazio allocato di 10 MB.
Un programma che conosce gli sparse file può ricostruire nella destinazione la stessa struttura quindi la copia continuerà quindi a occupare pochi megabyte.
Un software non sparse-aware potrebbe invece leggere il file sequenzialmente: quando attraversa gli holes riceve miliardi di zeri e potrebbe scriverli realmente nel file di destinazione. Si potrebbe quindi avere un file originale che occupa appena 10 MB a dispetto delle sue dimensioni importanti e una copia da 500 GB!
Il comando GNU cp dispone di un’apposita opzione che provvede a ricreare gli holes anche nella destinazione:
cp --sparse=always test.img test_copia.img
Con rsync si può invece usare la sintassi seguente:
rsync --sparse origine.img destinazione.img
Al posto di --sparse si può utilizzare la forma compatta -S.
Windows 11 e NTFS: gli sparse file sono supportati nativamente
La stessa tecnica funziona anche su Windows. Il file system NTFS possiede un supporto specifico per gli sparse file e Windows espone API e comandi per gestirli.
Tra le operazioni principali troviamo le seguenti:
FSCTL_SET_SPARSEpermette a un’applicazione di dichiarare esplicitamente che un file deve essere trattato come sparse.FSCTL_SET_ZERO_DATAconsente di trasformare una regione in un intervallo logicamente composto da zeri e può portare alla deallocazione dello spazio fisico.FSCTL_QUERY_ALLOCATED_RANGESconsente invece di interrogare la mappa delle parti effettivamente allocate.
Non serve sviluppare un’applicazione per sperimentare il meccanismo: Windows include già il comando fsutil.
Creare uno sparse file su Windows 11
Microsoft specifica che fsutil richiede privilegi amministrativi e lo considera uno strumento destinato agli utenti avanzati.
Dopo aver creato un nuovo file nella cartella specificata (che deve essere già presente a livello di filesystem) con il comando New-Item -ItemType File -Path C:\SparseTest\test.bin (è necessario procedere da una finestra PowerShell), è possibil contrassegnarlo come sparse:
fsutil sparse setflag C:\SparseTest\test.bin
L’istruzione che segue permette di verificare il risulato:
fsutil sparse queryflag C:\SparseTest\test.bin
Portare il file Windows a 500 GB
Con una semplice cmdlet PowerShell è possibile portare il file sparse a 500 GB senza scrivere tutto quel volume di dati su disco.
$path = "C:\SparseTest\test.bin"
$fs = [System.IO.File]::Open(
$path,
[System.IO.FileMode]::Open,
[System.IO.FileAccess]::ReadWrite,
[System.IO.FileShare]::None
)
$fs.SetLength(500GB)
$fs.Close()
Verificando le proprietà del file con Esplora file e controllando Dimensioni e Dimensioni su disco, si vedrà che nel primo caso il valore mostrato è pari a 500 GB, nel secondo intorno a 0 byte.
Dove si usano davvero gli sparse file
Come accennato nell’introduzione, gli sparse file trovano applicazione soprattutto quando un programma deve rappresentare file molto grandi ma solo parzialmente utilizzati.
Un caso tipico riguarda le macchine virtuali (VM): un’unità virtuale può avere, ad esempio, una capacità logica di 500 GB senza occupare immediatamente 500 GB sul sistema host. Se all’interno della VM sono presenti soltanto 40 GB di dati, il file che rappresenta il disco può utilizzare uno spazio molto più vicino a quella quantità. Formati come VHDX e QCOW2 adottano meccanismi propri per ottenere un risultato simile: allocare fisicamente i blocchi soltanto quando diventano necessari.
Lo stesso principio può tornare utile con database, immagini disco, backup e applicazioni scientifiche che lavorano con grandi spazi di indirizzamento nei quali soltanto alcune regioni contengono dati effettivi.
Il vantaggio: non occupare spazio prima che serva
Il beneficio principale è evidente. Un file può dichiarare una capacità molto elevata senza impegnare immediatamente tutto lo storage corrispondente.
Supponiamo che un SSD disponga di 400 GB liberi e contenga tre dischi virtuali da 300 GB logici ciascuno, che in realtà occupano davvero – esclusivi quindi gli holes – 40-50 MB ciascuno.
Lo spazio non allocato non è riservato: se tutte e tre le VM iniziassero a riempire realmente i propri dischi, lo storage fisico potrebbe esaurirsi molto velocemente.
È lo stesso principio alla base del thin provisioning: assegnare logicamente più capacità di quella effettivamente utilizzata, monitorando però attentamente lo spazio disponibile.
Copie e backup possono far esplodere le dimensioni
Uno dei problemi più concreti degli sparse file riguarda copie e backup. Come già evidenziato in precedenza, un programma sparse-aware riconosce gli holes e li ricrea anche nella destinazione. Un programma che non li supporta può invece leggere quelle regioni come sequenze di zeri e scriverle materialmente. La copia potrebbe quindi arrivare a occupare realmente centinaia di gigabyte.
Per file molto grandi conviene quindi verificare che software e filesystem di destinazione preservino gli sparse file. Se un file che occupava 20 GB ne utilizza improvvisamente 500, la struttura sparse è andata perduta.
Nel caso delle macchine virtuali, inoltre, non bisogna lasciarsi tranquillizzare dalla capacità nominale dei dischi virtuali. Come evidenziato poco fa, se tre VM dispongono complessivamente di 1 TB virtuale ma l’host ha soltanto 200 GB liberi, è lo spazio fisico dell’host che deve essere monitorato: via via che le VM scrivono dati reali, i relativi file possono crescere fino a saturarlo.