Konvertieren einer Sparse-Datei in eine Non-Sparse-Datei

8

Wie kann man unter Linux bei einer Datei mit geringer Dichte eine nicht spärliche Datei erstellen?
Es könnte kopiert werden cp --sparse=never ..., aber wenn die Datei 10G und das Loch 2G ist (das heißt, der zugewiesene Speicherplatz ist 8G), wie kann das Dateisystem die verbleibenden 2G zuweisen, ohne das ursprüngliche 8G in eine neue Datei zu kopieren?

Ivan
quelle

Antworten:

11

Auf den ersten Blick ist es einfach dd:

dd if=sparsefile of=sparsefile conv=notrunc bs=1M

Das liest die gesamte Datei und schreibt den gesamten Inhalt zurück.

Um nur das Loch selbst zu schreiben, müssen Sie zuerst bestimmen, wo sich diese Löcher befinden. Sie können dies entweder mit filefragoder tun 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

Diese Beispieldatei hat, wie Sie sagen, eine 10GGröße mit einem 2GLoch. Es hat zwei Ausdehnungen, die erste Abdeckung 0-1048575, die zweite 1572864-2621439, was bedeutet, dass das Loch ist 1048576-1572864(in 4k großen Blöcken, wie durch gezeigt filefrag). Die von hdparmangezeigten Informationen sind die gleichen und werden nur unterschiedlich angezeigt (der erste Bereich umfasst 8388608512-Byte-Sektoren ab 0, also 0-4294967295Bytes, also ist das Loch 4294967296-6442450944in Bytes.

Beachten Sie, dass Ihnen bei einer Fragmentierung möglicherweise ohnehin erheblich mehr Ausmaße angezeigt werden. Leider zeigt keiner der Befehle die Löcher direkt an, und ich kenne keinen, der dies tut. Sie müssen ihn daher aus den angezeigten logischen Offsets ableiten.

Das Füllen dieses 1048576-1572864Lochs mit ddwie oben gezeigt kann nun durch Hinzufügen geeigneter (identischer) seek/ skipWerte und erfolgen count. Beachten Sie, dass das bs=angepasst wurde, um die oben verwendeten 4kSektoren zu verwenden filefrag. (Zum Beispiel bs=1Mmüssten Sie die Werte für Suchen / Überspringen / Zählen anpassen, um die 1MGröße der Blöcke widerzuspiegeln .)

dd if=sparsefile of=sparsefile conv=notrunc \
   bs=4k seek=1048576 skip=1048576 count=$((-1048576+1572864))

Während Sie Löcher füllen könnten, /dev/zeroanstatt das Loch der Datei selbst zu lesen (was auch nur Nullen ergibt), ist es sicherer, aus dem zu lesen, sparsefiledamit Sie Ihre Daten nicht beschädigen, falls Sie einen falschen Offset haben.

In neueren Versionen von GNU ddkönnen Sie sich an eine größere Blockgröße halten und alle Werte in Bytes angeben:

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 nachdem ich das ausgeführt habe:

# 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

Aufgrund der Fragmentierung sind es immer noch zwei Bereiche. Die logischen Offsets zeigen jedoch, dass diesmal kein Loch vorhanden ist, sodass die Datei nicht mehr dünn ist.

Natürlich ist diese ddLösung der sehr manuelle Ansatz. Wenn Sie dies regelmäßig benötigen, ist es einfach, ein kleines Programm zu schreiben, das solche Lücken schließt. Wenn es bereits als Standardwerkzeug vorhanden ist, habe ich noch nichts davon gehört.


Es gibt doch ein Werkzeug, fallocatedas auf eine Art und Weise zu funktionieren scheint:

fallocate -l $(stat --format="%s" sparsefile) sparsefile

Im Fall von XFS wird zwar zwar physischer Bereich für diese Datei zugewiesen, dieser jedoch nicht auf Null gesetzt. filefragzeigt solche zugewiesenen, aber ungeschriebenen Ausmaße an.

   2:        3..      15:    7628851..   7628863:     13:    7629020: unwritten

Dies ist nicht gut genug, wenn die richtigen Daten direkt vom Blockgerät gelesen werden sollen. Es reserviert nur den Speicherplatz, der für zukünftige Schreibvorgänge benötigt wird.

Frostschutz
quelle
1
Oder cat sparsefile 1<> sparsefile. Möglicherweise können Sie unter fallocateLinux verwenden, um zu vermeiden, dass diese NUL-Bytes geschrieben werden müssen, wenn Sie nur den zuzuweisenden Speicherplatz benötigen.
Stéphane Chazelas
@ StéphaneChazelas, danke, vergessen fallocate. Es hat --dig-holesaber nein --fill-holes. Es scheint jedoch gut genug zu funktionieren, wenn Sie die Größe angeben. Ich werde meine Antwort bearbeiten.
Frostschutz
Unter NFS oder ext3 wird fallocate nicht unterstützt.
Ivan
Neuere fallocatehaben eine -z, die in Linux 3.14 und höher unter ext4 und xfs verwendet werden kann (Sie müssten sie mit -ound -lfür alle spärlichen Abschnitte ausführen, nehme ich an).
Stéphane Chazelas
@ StéphaneChazelas, yup, aber dies -zspeichert Ihre Daten nicht, wenn Sie einen falschen Offset bekommen, also bleibe ich dddort ...
Frostschutz