Conversion d'un fichier clairsemé en non clairsemé sur place

8

Sous Linux, étant donné un fichier clairsemé, comment le rendre non clairsemé, en place?
Il pourrait être copié cp --sparse=never ..., mais si le fichier est 10G et que le trou est 2G (c'est-à-dire que l'espace alloué est 8G), comment faire pour que le système de fichiers alloue le 2G restant sans copier le 8G original dans un nouveau fichier?

Ivan
la source

Réponses:

11

À première vue, c'est simple dd:

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

Cela lit l'intégralité du fichier et y réécrit l'intégralité du contenu.

Afin d'écrire uniquement le trou lui-même, vous devez d'abord déterminer où se trouvent ces trous. Vous pouvez le faire en utilisant soit filefragou 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

Cet exemple de fichier est, comme vous le dites, 10Gde taille avec un 2Gtrou. Il a deux étendues, la première couvrant 0-1048575, la seconde 1572864-2621439, ce qui signifie que le trou est 1048576-1572864(en blocs de taille 4k, comme indiqué par filefrag). Les informations affichées par hdparmsont les mêmes, juste affichées différemment (la première étendue couvre les 8388608secteurs de 512 octets à partir de 0, donc ce sont des 0-4294967295octets, donc le trou est 4294967296-6442450944en octets.

Notez que vous pouvez de toute façon afficher beaucoup plus d'étendue en cas de fragmentation. Malheureusement, aucune des commandes n'affiche directement les trous, et je n'en connais aucun qui le fasse, vous devez donc le déduire des décalages logiques indiqués.

Maintenant, remplir ce 1048576-1572864trou avec ddcomme indiqué ci-dessus, peut être fait en ajoutant des valeurs seek/ ( skipet ) appropriées (identiques) count. Notez que le a bs=été adapté pour utiliser les 4ksecteurs tels qu'utilisés filefragci-dessus. (Pour bs=1M, vous devez adapter les valeurs de recherche / saut / comptage pour refléter 1Mles blocs de taille).

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

Bien que vous puissiez remplir des trous au /dev/zerolieu de lire le trou du fichier lui-même (ce qui ne fera que générer des zéros), il est plus sûr de lire de sparsefiletoute façon afin de ne pas corrompre vos données au cas où un décalage serait incorrect.

Dans les versions plus récentes de GNU dd, vous pouvez vous en tenir à une taille de bloc plus grande et spécifier toutes les valeurs en octets:

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 après avoir exécuté cela:

# 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

En raison de la fragmentation, il s'agit toujours de deux extensions. Cependant, les décalages logiques montrent que cette fois, il n'y a pas de trou, donc le fichier n'est plus clairsemé.

Naturellement, cette ddsolution est l'approche très manuelle des choses. Si vous en avez besoin régulièrement, il serait facile d'écrire un petit programme qui comble ces lacunes. S'il existe déjà en tant qu'outil standard, je n'en ai pas encore entendu parler.


Il y a un outil après tout, fallocatesemble fonctionner, à la mode:

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

Cependant, enfin, dans le cas de XFS, bien qu'il alloue de l'espace physique pour ce fichier, il ne le met pas à zéro. filefragaffiche les étendues allouées, mais non écrites.

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

Ce n'est pas suffisant si l'intention est de pouvoir lire les données correctes directement à partir du périphérique de bloc. Il ne réserve que l'espace de stockage nécessaire pour les futures écritures.

frostschutz
la source
1
Ou cat sparsefile 1<> sparsefile. Vous pourrez peut-être utiliser fallocatesous Linux pour éviter d'avoir à écrire ces octets NUL si tout ce que vous voulez, c'est l'espace à allouer.
Stéphane Chazelas
@ StéphaneChazelas, merci, oublié fallocate. Il a --dig-holesmais non --fill-holes. Cependant, cela semble fonctionner assez bien lorsque vous spécifiez la taille. Je vais modifier ma réponse.
frostschutz
Sur NFS ou ext3, fallocate n'est pas pris en charge.
Ivan
Les plus récents en fallocateont un -zqui peut être utilisé sous Linux 3.14 et supérieur sur ext4 et xfs (vous devrez l'exécuter avec -oet -lpour toutes les sections clairsemées je suppose).
Stéphane Chazelas
@ StéphaneChazelas, yup, mais cela -zne garde pas vos données si vous vous trompez de décalage, alors je m'en dd
tiendrai