web-dev-qa-db-ja.com

スパースファイルを非スパースファイルにインプレースで変換する

Linuxでは、スパースファイルが与えられた場合、そのファイルを非スパースにする方法を教えてください。
それはcp --sparse=never ...、ただし、ファイルが10Gで、ホールが2Gの場合(つまり、割り当てられたスペースが8Gの場合)、元の8Gを新しいファイルにコピーせずに、ファイルシステムに残りの2Gを割り当てる方法は?

8
Ivan

一見すると、それは単純な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のサイズです。これには2つのエクステントがあり、1つ目は0-1048575をカバーし、2つ目は1572864-2621439をカバーします。これは、ホールが1048576-1572864であることを意味します(filefragで示される4kサイズのブロック)。 hdparmで表示される情報は同じで、表示が異なるだけです(最初のエクステントは、0から始まる8388608 512バイトセクターをカバーしているため、0-4294967295バイトなので、ホールは4294967296-6442450944バイトです。

断片化があると、とにかくかなり多くのエクステントが表示される場合があることに注意してください。残念ながら、どちらのコマンドもホールを直接表示せず、そのようなホールを私は知らないので、表示された論理オフセットからそれを推定する必要があります。

ここで、上記のように1048576-1572864ホールをddで埋めるには、適切な(同一の)seek/skip値とcountを追加します。 bs=は、上記のfilefragで使用される4kセクターを使用するように調整されていることに注意してください。 (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

断片化のため、それはまだ2つのエクステントです。ただし、論理オフセットは、今回はホールがないため、ファイルがスパースではなくなったことを示しています。

当然、このddソリューションは、物事への非常に手動のアプローチです。これを定期的に必要とする場合、そのようなギャップを埋める小さなプログラムを書くのは簡単でしょう。標準ツールとしてすでに存在している場合、まだ聞いていません。


結局のところ、ツールがあります。fallocateは、ファッションの後に機能するようです。

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

ただし、ついにXFSの場合、このファイルに物理領域を割り当てますが、実際にはゼロになりません。 filefragは、そのようなエクステントが割り当てられているが、書き込まれていないことを示します。

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

これは、ブロックデバイスから直接正しいデータを読み取ることができる場合には、十分ではありません。将来の書き込みに必要なストレージ領域を予約するだけです。

11
frostschutz