Conversión de archivo disperso a no disperso en su lugar

8

En Linux, dado un archivo disperso, ¿cómo hacer que no sea disperso, en su lugar?
Se podría copiar cp --sparse=never ..., pero si el archivo es 10G y el agujero es 2G (es decir, el espacio asignado es 8G), ¿cómo hacer que el sistema de archivos asigne el 2G restante sin copiar el 8G original a un nuevo archivo?

Ivan
fuente

Respuestas:

11

A primera vista, es un simple dd:

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

Eso lee todo el archivo y vuelve a escribir todo el contenido.

Para escribir solo el agujero en sí, primero debes determinar dónde están esos agujeros. Puede hacerlo usando filefrago 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

Este archivo de ejemplo es, como usted dice, 10Gde tamaño con un 2Gagujero. Tiene dos extensiones, la primera cubierta 0-1048575, la segunda 1572864-2621439, lo que significa que el agujero es 1048576-1572864(en bloques de tamaño 4k, como se muestra en la figura filefrag). La información que se muestra hdparmes la misma, solo se muestra de manera diferente (la primera extensión cubre 8388608sectores de 512 bytes comenzando desde 0, por lo que son 0-4294967295bytes, por lo que el agujero está 4294967296-6442450944en bytes.

Tenga en cuenta que de todos modos se le puede mostrar considerablemente más extensiones si hay alguna fragmentación. Desafortunadamente, ninguno de los comandos muestra los agujeros directamente, y no conozco uno que lo haga, por lo que debe deducirlo de las compensaciones lógicas que se muestran.

Ahora, llenando ese 1048576-1572864agujero con el ddque se muestra arriba, puede hacerse agregando los valores seek/ idénticos y apropiados . Tenga en cuenta que se adaptó para usar los sectores como se utilizó anteriormente. (Para , tendría que adaptar los valores de búsqueda / omisión / recuento para reflejar bloques de tamaño).skipcountbs=4kfilefragbs=1M1M

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

Si bien podría llenar los agujeros en /dev/zerolugar de leer el agujero del archivo en sí (lo que también generará ceros), es más seguro leerlo de sparsefiletodos modos para que no corrompa sus datos en caso de que obtenga un desplazamiento incorrecto.

En las versiones más recientes de GNU dd, puede mantener un tamaño de bloque más grande y especificar todos los valores en bytes:

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 después de ejecutar eso:

# 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

Debido a la fragmentación, todavía son dos extensiones. Sin embargo, los desplazamientos lógicos muestran que esta vez no hay agujeros, por lo que el archivo ya no es escaso.

Naturalmente, esta ddsolución es el enfoque muy manual de las cosas. Si necesita esto de forma regular, sería fácil escribir un pequeño programa que llene esos vacíos. Si ya existe como herramienta estándar, aún no he oído hablar de él.


Hay una herramienta, después de todo, fallocateparece funcionar, de una manera:

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

Sin embargo, finalmente en el caso de XFS, si bien asigna un área física para este archivo, en realidad no lo pone a cero. filefragmuestra las extensiones asignadas, pero no escritas.

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

Esto no es lo suficientemente bueno si la intención es poder leer los datos correctos directamente desde el dispositivo de bloque. Solo reserva el espacio de almacenamiento necesario para futuras escrituras.

Frostschutz
fuente
1
O cat sparsefile 1<> sparsefile. Es posible que pueda usar fallocateen Linux para evitar tener que escribir esos bytes NUL si todo lo que desea es el espacio que se asignará.
Stéphane Chazelas
@ StéphaneChazelas, gracias, se olvidó fallocate. Tiene --dig-holespero no --fill-holes. Sin embargo, parece funcionar lo suficientemente bien cuando especifica el tamaño. Editaré mi respuesta.
frostschutz
En NFS o ext3 Falocate no es compatible.
Ivan
Las más nuevas fallocatetienen una -zque se puede usar en Linux 3.14 y superior en ext4 y xfs ( supongo que deberá ejecutarlo con -oy -lpara todas las secciones dispersas).
Stéphane Chazelas
@ StéphaneChazelas, sí, pero esto -zno conserva sus datos si se obtiene un desplazamiento incorrecto, así que me quedaré ddallí ...
frostschutz