是否有类似“dd”但尊重输入文件的稀疏性的工具?

是否有类似“dd”但尊重输入文件的稀疏性的工具?

我正在创建带有多个 ext4 分区的磁盘映像。每个 ext4 分区的内容都是用其创建的,mkfs.ext4并且生成的文件系统映像文件是“稀疏的”。

当我dd从所有这些单独的文件系统创建系统映像(一开始有一个分区表)时,生成的映像将不再稀疏。

这样做是错误的,dd conv=sparse因为这会使输出变得稀疏全部-NUL 输出块。但这不是我想要的。如果输入是稀疏的,我希望输出也是稀疏的。

这是必要的,因为最终我希望能够刷新映像,这样bmaptool就不必将“稀疏”的块写入物理设备,从而节省写入映像的时间。如果我将所有只有零的块视为稀疏,那么磁盘上非 NUL 的随机数据将不会被覆盖,这对于文件系统上实际上应该包含零的文件来说是一个问题。

那么,是否有一个工具可以做到这一点dd,并能够根据给定的偏移量将一个文件复制到另一个文件中,但将输入文件中稀疏的块保留在输出文件中?

答案1

由于我无法找到一个可以正常工作dd但又能在输出中重现输入的稀疏性的工具,因此我编写了一个小型 Python 脚本来执行此操作:

#!/usr/bin/env python

import subprocess
import os
import sys


def main():
    infd = os.open(sys.argv[1], os.O_RDONLY)
    inlength = os.lseek(infd, 0, os.SEEK_END)
    outfd = os.open(sys.argv[2], os.O_CREAT | os.O_WRONLY)
    outlength = os.lseek(outfd, 0, os.SEEK_END)
    offset = int(sys.argv[3])
    curr = 0
    while True:
        try:
            data = os.lseek(infd, curr, os.SEEK_DATA)
        except OSError:
            # no more data
            break
        try:
            hole = os.lseek(infd, data, os.SEEK_HOLE)
        except OSError:
            # no hole afterwards, copy until EOF
            hole = inlength
        print(
            f"copying range {data}..{hole} ({100*hole/inlength:.2f}%)", file=sys.stderr
        )
        os.copy_file_range(
            infd, outfd, hole - data, offset_src=data, offset_dst=data + offset
        )
        curr = hole
    if outlength < inlength + offset:
        os.truncate(outfd, inlength + offset)
    os.close(infd)
    os.close(outfd)


if __name__ == "__main__":
    main()

根据du我的最终系统图像现在仅占用 3.7 G 的磁盘空间,而不是图像文件大小的全部 5.1 G。

对于我的 3 G ext4 文件系统,上面的主循环只迭代大约 500 次,因此大部分时间都花在 Python 上copy_file_range()而不是 Python 上,所以我并不担心代码不是用 C 编写的,但将其移植到 C 上可能很简单。

编辑

好的,这是一个用 C 语言编写的版本:

#define _GNU_SOURCE
#define _LARGEFILE64_SOURCE
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>


int main(int argc, char *argv[]) {
    if (argc != 3 && argc != 4) {
        fprintf(stderr, "Usage: %s infile outfile [offset]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    int infd = open(argv[1], O_RDONLY);
    if (infd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }
    off64_t inlength = lseek64(infd, 0, SEEK_END);
    if (inlength == -1) {
        perror("lseek64");
        exit(EXIT_FAILURE);
    }
    int outfd = open(argv[2], O_CREAT | O_WRONLY);
    if (outfd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }
    off64_t outlength = lseek64(outfd, 0, SEEK_END);
    if (outlength == -1) {
        perror("lseek64");
        exit(EXIT_FAILURE);
    }
    long long offset = 0;
    if (argc == 4) {
        offset = strtoll(argv[3], NULL, 10);
        if (errno != 0) {
            perror("strtoll");
            exit(EXIT_FAILURE);
        }
    }
    off64_t curr = 0;
    while (true) {
        off64_t data = lseek64(infd, curr, SEEK_DATA);
        if (data == -1) {
            break;
        }
        off64_t hole = lseek64(infd, data, SEEK_HOLE);
        if (hole == -1) {
            hole = inlength;
        }
        off64_t off_out = data + offset;
        ssize_t ret = copy_file_range(infd, &data, outfd, &off_out, hole - data, 0);
        if (ret == -1) {
            perror("copy_file_range");
            exit(EXIT_FAILURE);
        }
        curr = hole;
    }
    if (outlength < inlength + offset) {
        int ret = ftruncate(outfd, inlength + offset);
        if (ret == -1) {
            perror("ftruncate");
            exit(EXIT_FAILURE);
        }
    }
    close(infd);
    close(outfd);
    exit(EXIT_SUCCESS);
}

相关内容