Convertendo arquivo esparso para não esparso no lugar

8

No Linux, dado um arquivo esparso, como torná-lo não esparso, no lugar?
Ele pode ser copiado cp --sparse=never ..., mas se o arquivo é 10G e o buraco é 2G (ou seja, o espaço alocado é 8G), como fazer com que o sistema de arquivos aloque o 2G restante sem copiar o 8G original para um novo arquivo?

Ivan
fonte

Respostas:

11

Em face disso, é simples dd:

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

Isso lê o arquivo inteiro e grava todo o conteúdo nele.

Para escrever apenas o próprio furo, primeiro você precisa determinar onde estão esses orifícios. Você pode fazer isso usando 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

Este arquivo de exemplo é, como você diz, 10Gem tamanho com um 2Gfuro. Possui duas extensões, a primeira cobertura 0-1048575e a segunda 1572864-2621439, o que significa que o furo é 1048576-1572864(em blocos de tamanho 4k, conforme mostrado filefrag). As informações mostradas por hdparmsão as mesmas, apenas exibidas de maneira diferente (a primeira extensão abrange 8388608setores de 512 bytes a partir de 0, por isso é 0-4294967295bytes, portanto o furo é 4294967296-6442450944em bytes.

Observe que você pode ser mostrado consideravelmente mais extensões de qualquer maneira, se houver alguma fragmentação. Infelizmente, nenhum dos comandos mostra os furos diretamente, e eu não conheço um que faça isso, então você deve deduzi-lo dos desvios lógicos mostrados.

Agora, preenchendo esse 1048576-1572864buraco com ddcomo mostrado acima, pode ser feito através da adição de adequados (idênticas) seek/ skipvalores e count. Observe que o bs=foi adaptado para usar os 4ksetores, conforme usado filefragacima. (Para bs=1M, você teria que adaptar os valores de busca / pular / contagem para refletir 1Mblocos de tamanho).

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

Embora você possa preencher buracos em /dev/zerovez de ler o buraco do arquivo em si (o que também produzirá zeros), é mais seguro ler o arquivo de sparsefilequalquer maneira, para que você não corrompa seus dados caso ocorra um deslocamento incorreto.

Nas versões mais recentes de GNU dd, você pode manter um tamanho de bloco maior e especificar todos os valores em 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 depois de executar isso:

# 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

Devido à fragmentação, ainda existem duas extensões. No entanto, as compensações lógicas mostram que, desta vez, não há furo; portanto, o arquivo não é mais escasso.

Naturalmente, esta ddsolução é a abordagem muito manual das coisas. Se você precisar disso regularmente, seria fácil escrever um pequeno programa que preencha essas lacunas. Se ele já existe como uma ferramenta padrão, ainda não ouvi falar.


Existe uma ferramenta, afinal, fallocateparece funcionar, de certa forma:

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

No entanto, finalmente, no caso do XFS, embora ele aloque área física para esse arquivo, na verdade não o zera. filefragmostra extensões como alocadas, mas não escritas.

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

Isso não é bom o suficiente se a intenção é poder ler os dados corretos diretamente do dispositivo de bloco. Ele reserva apenas o espaço de armazenamento necessário para gravações futuras.

frostschutz
fonte
1
Or cat sparsefile 1<> sparsefile. Você pode usar fallocateno Linux para evitar a necessidade de gravar esses bytes NUL se tudo o que você deseja é o espaço a ser alocado.
Stéphane Chazelas
@ StéphaneChazelas, obrigado, esqueci fallocate. Tem --dig-holesmas não --fill-holes. No entanto, parece funcionar bem o suficiente quando você especifica o tamanho. Vou editar minha resposta.
frostschutz
No NFS ou ext3, o fallocate não é suportado.
Ivan
Os mais novos fallocatetêm um -zque pode ser usado no Linux 3.14 e superior no ext4 e xfs (você precisaria executá-lo com -oe -lpara todas as seções esparsas, suponho).
Stéphane Chazelas
@ StéphaneChazelas, sim, mas isso -znão mantém seus dados, se você dd
cometer