ベンチマークとテストの目的で、パーティションの先頭にある特定のオフセットにファイルを割り当てることができる必要があります。通常、新しいファイルを作成すると、そのブロックはファイルシステムが決定する場所に配置されますが、それを制御したいと思います。つまり、ファイルに割り当てるブロックを手動で選択したいのです。
debugfsを見ましたが、私が望むことをする方法を見つけることができません。ブロックは割り当て済みとしてマークし、inodeを変更できますが、これは最初の12ブロックでのみ機能します。その後、debugfsには機能がないような間接ブロックとデュアル間接ブロックを作成できるはずです。
これを行う方法はありますか?私に役立つツールがありますか?ファイルシステムがext3またはext4で再フォーマットされていると想定できます(他のファイルは存在しません)。
よろしくお願いします。
答え1
私はこれを行う方法を見つけました。debugfs
ファイルに必要な必要なブロック数(間接ブロックを含む)を見つけるために最初に使用されるPythonスクリプトを使用します。その後、手動で間接ブロックをディスクに書き込み、再度呼び出してそのdebugfs
ブロックを使用済みとしてマークし、ファイルのinodeを更新します。
唯一の問題は、debugfs
使用時にブロックグループの空きブロック数が更新されないことですsetb
。パラメータを手動で設定できますが、現在の値を印刷する方法がないため、正確な値を計算できません。私が知っている限り、実際には否定的な結果はなく、必要に応じて値をfsck.ext3
変更するために使用できるため、ベンチマーク目的で作業を実行します。
他のファイルシステムの一貫性の問題を見逃した場合は、お知らせください。しかし、fsck.ext3
間違った空きブロック数以外は何も報告されないので安全です。
import sys
import tempfile
import struct
import subprocess
SECTOR_SIZE = 512
BLOCK_SIZE = 4096
DIRECT_BLOCKS = 12
BLOCKS_PER_INDIRECT_BLOCK = BLOCK_SIZE / 4
def write_indirect_block(device, indirect_block, blocks):
print "writing indirect block ", indirect_block
dev = open(device, "wb")
dev.seek(indirect_block * BLOCK_SIZE)
# Write blocks
for block in blocks:
bin_block = struct.pack("<I", int(block))
dev.write(bin_block)
zero = struct.pack("<I", 0)
# Zero out the rest of the block
for x in range(len(blocks), BLOCKS_PER_INDIRECT_BLOCK):
dev.write(zero)
dev.close()
def main(argv):
if len(argv) < 5:
print "Usage: ext3allocfile.py [device] [file] [sizeInMB] [offsetInMB]"
return
device = argv[1] # device containing the ext3 file system, e.g. "/dev/sdb1"
file = argv[2] # file name relative to the root of the device, e.g. "/myfile"
size = int(argv[3]) * 1024 * 1024 # Size in MB
offset = int(argv[4]) * 1024 * 1024 # Offset from the start of the device in MB
if size > 0xFFFFFFFF:
# Supporting this requires two things: triple indirect block support, and proper handling of size_high when changing the inode
print "Unable to allocate files over 4GB."
return
# Because size is specified in MB, it should always be exactly divisable by BLOCK_SIZE.
size_blocks = size / BLOCK_SIZE
# We need 1 indirect block for each 1024 blocks over 12 blocks.
ind_blocks = (size_blocks - DIRECT_BLOCKS) / BLOCKS_PER_INDIRECT_BLOCK
if (size_blocks - DIRECT_BLOCKS) % BLOCKS_PER_INDIRECT_BLOCK != 0:
ind_blocks += 1
# We need a double indirect block if we have more than one indirect block
has_dind_block = ind_blocks > 1
total_blocks = size_blocks + ind_blocks
if has_dind_block:
total_blocks += 1
# Find free blocks we can use at the offset
offset_block = offset / BLOCK_SIZE
print "Finding ", total_blocks, " free blocks from block ", offset_block
process = subprocess.Popen(["debugfs", device, "-R", "ffb %d %d" % (total_blocks, offset_block)], stdout=subprocess.PIPE)
output = process.stdout
# The first three entries after splitting are "Free", "blocks", "found:", so we skip those.
blocks = output.readline().split(" ")[3:]
output.close()
# The last entry may contain a line-break. Removing it this way to be safe.
blocks = filter(lambda x: len(x.strip(" \n")) > 0, blocks)
if len(blocks) != total_blocks:
print "Not enough free blocks found for the file."
return
# The direct blocks in the inode are blocks 0-11
# Write the first indirect block, listing the blocks for file blocks 12-1035 (inclusive)
if ind_blocks > 0:
write_indirect_block(device, int(blocks[DIRECT_BLOCKS]), blocks[DIRECT_BLOCKS + 1 : DIRECT_BLOCKS + 1 + BLOCKS_PER_INDIRECT_BLOCK])
if has_dind_block:
dind_block_index = DIRECT_BLOCKS + 1 + BLOCKS_PER_INDIRECT_BLOCK
dind_block = blocks[dind_block_index]
ind_block_indices = [dind_block_index+1+(i*(BLOCKS_PER_INDIRECT_BLOCK+1)) for i in range(ind_blocks-1)]
# Write the double indirect block, listing the blocks for the remaining indirect block
write_indirect_block(device, int(dind_block), [blocks[i] for i in ind_block_indices])
# Write the remaining indirect blocks, listing the relevant file blocks
for i in ind_block_indices:
write_indirect_block(device, int(blocks[i]), blocks[i+1:i+1+BLOCKS_PER_INDIRECT_BLOCK])
# Time to generate a script for debugfs
script = tempfile.NamedTemporaryFile(mode = "w", delete = False)
# Mark all the blocks as in-use
for block in blocks:
script.write("setb %s\n" % (block,))
# Change direct blocks in the inode
for i in range(DIRECT_BLOCKS):
script.write("sif %s block[%d] %s\n" % (file, i, blocks[i]))
# Change indirect block in the inode
if size_blocks > DIRECT_BLOCKS:
script.write("sif %s block[IND] %s\n" % (file, blocks[DIRECT_BLOCKS]))
# Change double indirect block in the inode
if has_dind_block:
script.write("sif %s block[DIND] %s\n" % (file, dind_block))
# Set total number of blocks in the inode (this value seems to actually be sectors
script.write("sif %s blocks %d\n" % (file, total_blocks * (BLOCK_SIZE / SECTOR_SIZE)))
# Set file size in the inode
# TODO: Need support of size_high for large files
script.write("sif %s size %d\n" % (file, size))
script.close()
# execute the script
print "Modifying file"
subprocess.call(["debugfs", "-w", device, "-f", script.name])
script.unlink(script.name)
if __name__ == "__main__":
main(sys.argv)
スクリプトは、次のようにオフセット200 GBに1 GBのファイルを生成します(rootユーザーである必要があります)。
touch /mount/point/myfile
sync
python ext3allocfile.py /dev/sdb1 /myfile 1024 204800
umount /dev/sdb1
mount /dev/sdb1
システムが変更を認識するには、umount / mountの組み合わせを使用する必要があります。スクリプトを呼び出す前にアンロードできますが、これにより呼び出しがdebugfs
遅くなります。
誰かがこれを使用したい場合:私はそれが機能することを保証せず、あなたがデータを失っても私は責任を負いません。通常、重要な内容を含むファイルシステムでは使用しないでください。
答え2
これはあなたが望む答えではないことを知っています。ただし、この方法はファイルシステムとは何の関係もありません。
一度だけ
ブロックサイズを見つけて、そのブロックサイズのファイルを生成します。完了したら、目的のファイルを除くすべてのファイルを削除します。
完了したら、デバイスのソースを圧縮ファイルにコピーします。
dd if=/dev/sdp1 |bzip2 -9 > /tmp/my-fs-image.bz2
望むより!ファイルシステムイメージはそれほど大きくなく、目的の場所にブロックが1つしかありません。分配する
回復するには、物理ディスクブロックとまったく同じ数/サイズのパーティションを作成します。
bzip2 -d < /tmp/my-fs-image.bz2|dd of=/dev/sdq1
最初の移動は非常に面倒です。