Debian

在 ext3/4 中的特定偏移處分配文件

  • February 26, 2012

出於基準測試和測試目的,我需要能夠在從分區開始的特定偏移量處分配文件。當我正常創建一個新文件時,它的塊被放置在文件系統決定的任何地方,但我想控制它。換句話說,我想手動選擇分配給文件的塊。

我查看了 debugfs,但找不到任何方法來做我想做的事。雖然我可以將塊標記為已分配並修改 inode,但這僅適用於前 12 個塊。之後,我還需要能夠創建間接和雙重間接塊,看起來 debugfs 沒有任何能力。

有沒有辦法做到這一點?有什麼工具可以幫助我嗎?您可以假設文件系統是 ext3 或 ext4 並且它已經被重新格式化(不存在其他文件)。

提前致謝。

我設法找到了一種方法來做到這一點。它使用一個 python 腳本,該腳本首先用於debugfs查找文件需要的必要塊數(包括間接塊)。然後它手動將間接塊寫入磁碟,並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)

該腳本可用於在偏移量 200GB 處創建一個 1GB 文件(您需要是 root):

touch /mount/point/myfile
sync
python ext3allocfile.py /dev/sdb1 /myfile 1024 204800
umount /dev/sdb1
mount /dev/sdb1

umount/mount 組合對於讓系統辨識更改是必要的。您可以在呼叫腳本之前解除安裝,但這會使呼叫debugfs速度變慢。

如果有人想使用它:我不保證它會正常工作,如果您失去任何數據,我不承擔任何責任。通常,不要在包含任何重要內容的文件系統上使用它。

引用自:https://unix.stackexchange.com/questions/20995