将稀疏文件转换为非稀疏文件


8

在Linux上,给定一个稀疏文件,如何使其变为非稀疏文件?
可以使用复制它cp --sparse=never ...,但是如果文件是10G且孔是2G(即分配的空间是8G),那么如何使文件系统分配剩余的2G而不将原始8G复制到新文件?

Answers:


11

从表面上看,这很简单dd

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

那将读取整个文件,并将整个内容写回到该文件。

为了只写孔本身,您首先必须确定这些孔的位置。您可以使用filefrag或进行以下操作hdparm

文件片段:

# 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是相同的,只是显示方式不同(第一个扩展区覆盖8388608从0开始的512字节扇区,因此它是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

如果要直接从块设备读取正确的数据,这还不够好。它仅保留将来写入所需的存储空间。


1
或者cat sparsefile 1<> sparsefilefallocate如果您只想分配空间,则可以在Linux 上使用它来避免写入那些NUL字节。
斯特凡Chazelas

@StéphaneChazelas,谢谢,忘了fallocate。它有--dig-holes但没有--fill-holes。但是,当您指定大小时,它似乎可以很好地工作。我将编辑我的答案。
弗罗斯特斯2014年

在NFS或ext3上,不支持fallocate。
伊万2014年

较新fallocate的版本-z可以在ext4和xfs的Linux 3.14及更高版本中使用(您需要使用它-o-l针对我想的所有稀疏部分运行它)。
斯特凡Chazelas

@StéphaneChazelas,是的,但这-z不会保留您的数据,如果您碰巧错了一个偏移量,那么我会坚持下去dd...
frostschutz 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.