Преобразование разреженного файла в не разреженный на месте

8

В Linux дан разреженный файл, как сделать его не разреженным, на месте?
Его можно скопировать cp --sparse=never ..., но если файл, скажем, 10G, а дыра - 2G (то есть выделенное пространство - 8G), как заставить файловую систему выделить оставшиеся 2G без копирования исходного 8G в новый файл?

Иван
источник

Ответы:

11

На первый взгляд, это просто dd:

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

Это читает весь файл и записывает все содержимое обратно в него.

Для того, чтобы написать только само отверстие, сначала нужно определить, где находятся эти отверстия. Вы можете сделать это с помощью либо filefragили 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

Этот файл примера, как вы говорите, имеет 10Gразмер с 2Gотверстием. Он имеет два экстента, первое покрытие 0-1048575, второе 1572864-2621439, что означает, что отверстие 1048576-1572864(в блоках размером 4k, как показано filefrag). Информация, отображаемая hdparmкак та же самая, просто отображается по-разному (первый экстент охватывает 8388608512-байтовые сектора, начиная с 0, поэтому это 0-4294967295байты, поэтому дыра 4294967296-6442450944в байтах.

Обратите внимание, что вам может быть показано значительно больше экстентов в случае фрагментации. К сожалению, ни одна из команд не показывает дыры напрямую, и я не знаю ни одной, которая делает это, поэтому вы должны вывести ее из показанных логических смещений.

Теперь, заполнив это 1048576-1572864отверстие, ddкак показано выше, можно сделать, добавив соответствующие (идентичные) seek/ skipзначения и count. Обратите внимание, что он bs=был адаптирован для использования 4kсекторов, как filefragуказано выше. (Для bs=1Mэтого вам придется адаптировать значения поиска / пропуска / подсчета, чтобы отразить 1Mразмерные блоки).

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

Хотя вы можете заполнить дыры /dev/zeroвместо того, чтобы читать дыру в самом файле (что также приведет к нулю), безопаснее читать изsparsefile любом случае, так что вы не повредите свои данные в случае неправильного смещения.

В более новых версиях GNU ddвы можете придерживаться большего размера блока и указывать все значения в байтах:

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 после запуска этого:

# 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

Из-за фрагментации это все еще две степени. Однако логические смещения показывают, что на этот раз дыр нет, поэтому файл больше не является разреженным.

Естественно, это ddрешение - очень ручной подход к вещам. Если вам это нужно на регулярной основе, было бы легко написать небольшую программу, которая заполняет такие пробелы. Если он уже существует в качестве стандартного инструмента, я еще не слышал об этом.


В конце концов, есть инструмент, который, fallocateкажется, работает после моды:

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

Однако, наконец, в случае XFS, хотя он выделяет физическую область для этого файла, он фактически не обнуляет его. filefragпоказывает такие экстенты как выделенные, но неписанные.

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

Это недостаточно для того, чтобы иметь возможность считывать правильные данные непосредственно с блочного устройства. Он только резервирует место для хранения, необходимое для будущих записей.

frostschutz
источник
1
Или cat sparsefile 1<> sparsefile. Вы можете использовать fallocateLinux, чтобы избежать необходимости записывать эти байты NUL, если все, что вам нужно, это место, которое будет выделено.
Стефан Шазелас
@ StéphaneChazelas, спасибо, забыл о fallocate. Это имеет, --dig-holesно нет --fill-holes. Тем не менее, кажется, что он работает достаточно хорошо, когда вы указываете размер. Я отредактирую свой ответ.
frostschutz
На NFS или ext3 fallocate не поддерживается.
Иван
Новее fallocateесть , -zкоторые могут быть использованы в Linux 3.14 и выше на ext4 и XFS (вы должны запустить его с -oи -lдля всех разреженных участков я полагаю).
Стефан Шазелас
@ StéphaneChazelas, да, но это -zне сохранит ваши данные, если вам случится ошибочное смещение, поэтому я буду придерживаться ddэтого ...
frostschutz