A prima vista, è un semplice dd
:
dd if=sparsefile of=sparsefile conv=notrunc bs=1M
Questo legge l'intero file e vi riscrive l'intero contenuto.
Per scrivere solo il buco stesso, devi prima determinare dove si trovano quei buchi. Puoi farlo usando filefrag
o hdparm
:
filefrag:
# filefrag -e sparsefile
Filesystem type is: 58465342
File size of sparsefile is 10737418240 (2621440 blocks of 4096 bytes)
ext: logical_offset: physical_offset: length: expected: flags:
0: 0.. 1048575: 187357696.. 188406271: 1048576:
1: 1572864.. 2621439: 200704128.. 201752703: 1048576: 188406272: last,eof
sparsefile: 2 extents found
hdparm:
# hdparm --fibmap sparsefile
sparsefile:
filesystem blocksize 4096, begins at LBA 0; assuming 512 byte sectors.
byte_offset begin_LBA end_LBA sectors
0 1498861568 1507250175 8388608
6442450944 1605633024 1614021631 8388608
Questo file di esempio è, come dici tu, 10G
di dimensioni con un 2G
buco. Ha due estensioni, la prima che copre 0-1048575
, la seconda 1572864-2621439
, il che significa che il foro è 1048576-1572864
(in blocchi di dimensioni 4k, come mostrato da filefrag
). Le informazioni mostrate da hdparm
sono le stesse, visualizzate in modo diverso (la prima estensione copre 8388608
settori a 512 byte a partire da 0, quindi sono 0-4294967295
byte, quindi il foro è 4294967296-6442450944
in byte.
Tieni presente che in caso di frammentazione potrebbero essere mostrate molte più estensioni. Sfortunatamente, nessun comando mostra direttamente i buchi e non ne conosco uno che lo faccia, quindi devi dedurlo dagli offset logici mostrati.
Ora, riempiendo quel 1048576-1572864
buco con dd
come mostrato sopra, può essere fatto aggiungendo appropriati (identici) seek
/ skip
valori e count
. Si noti che è bs=
stato adattato per utilizzare i 4k
settori utilizzati in filefrag
precedenza. (Per bs=1M
, dovresti adattare i valori seek / skip / count per riflettere 1M
blocchi di dimensioni).
dd if=sparsefile of=sparsefile conv=notrunc \
bs=4k seek=1048576 skip=1048576 count=$((-1048576+1572864))
Mentre potresti riempire i buchi /dev/zero
invece di leggere il buco del file stesso (il che produrrà anche zero), è comunque più sicuro leggere da sparsefile
così non corromperai i tuoi dati nel caso in cui tu abbia sbagliato un offset.
Nelle versioni più recenti di GNU dd
, è possibile attenersi a una dimensione di blocco più grande e specificare tutti i valori in byte:
dd if=sparsefile of=sparsefile conv=notrunc bs=1M \
iflag=skip_bytes,count_bytes oflag=seek_bytes \
seek=4294967296 skip=4294967296 count=$((-4294967296+6442450944))
filefrag
dopo averlo eseguito:
# sync
# filefrag -e sparsefile
Filesystem type is: 58465342
File size of sparsefile is 10737418240 (2621440 blocks of 4096 bytes)
ext: logical_offset: physical_offset: length: expected: flags:
0: 0.. 1572863: 187357696.. 188930559: 1572864:
1: 1572864.. 2621439: 200704128.. 201752703: 1048576: 188930560: last,eof
sparsefile: 2 extents found
A causa della frammentazione, sono ancora due estensioni. Tuttavia, gli offset logici mostrano che questa volta non ci sono buchi, quindi il file non è più scarso.
Naturalmente, questa dd
soluzione è l'approccio molto manuale alle cose. Se ne hai bisogno su base regolare, sarebbe facile scrivere un piccolo programma che colmi tali lacune. Se esiste già come strumento standard, non ne ho ancora sentito parlare.
Dopotutto c'è uno strumento, fallocate
sembra funzionare, dopo una moda:
fallocate -l $(stat --format="%s" sparsefile) sparsefile
Tuttavia, alla fine, nel caso di XFS, mentre alloca area fisica per questo file, non lo azzera. filefrag
mostra le estensioni assegnate, ma non scritte.
2: 3.. 15: 7628851.. 7628863: 13: 7629020: unwritten
Questo non è abbastanza buono se l'intenzione è quella di poter leggere i dati corretti direttamente dal dispositivo a blocchi. Si riserva solo lo spazio di archiviazione necessario per le scritture future.
cat sparsefile 1<> sparsefile
. Potresti essere in grado di utilizzarefallocate
su Linux per evitare di dover scrivere quei byte NUL se tutto ciò che vuoi è lo spazio da allocare.