ext3/4의 특정 오프셋에 파일 할당

ext3/4의 특정 오프셋에 파일 할당

벤치마킹 및 테스트 목적으로 파티션 시작 부분의 특정 오프셋에 파일을 할당할 수 있어야 합니다. 일반적으로 새 파일을 생성하면 해당 블록은 파일 시스템이 결정하는 곳에 배치되지만 이를 제어하고 싶습니다. 즉, 파일에 할당할 블록을 수동으로 선택하고 싶습니다.

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)

스크립트는 다음과 같이 오프셋 200GB에 1GB 파일을 생성합니다(루트 사용자여야 함).

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

바라보다! 파일 시스템 이미지는 그다지 크지 않으며 원하는 위치에 블록이 하나만 있습니다. 분배하다.

복구하려면 물리적 디스크 블록과 정확히 동일한 수/크기로 파티션을 생성하십시오.

bzip2 -d < /tmp/my-fs-image.bz2|dd of=/dev/sdq1

첫 번째 이동은 매우 번거로울 것입니다.

관련 정보