用图像更新块设备,同时只写入 Delta,这可能吗?

用图像更新块设备,同时只写入 Delta,这可能吗?

dd广泛使用它作为软件配置控制的一种手段。这些映像通常部署在闪存盘上以更新设备。我发现我经常对图像文件进行小的增量更新,然后必须将整个图像重新复制到块设备。这是相当耗时的,因为图像的大小通常为 8GB。更糟糕的是,图像(一旦组装)不是易于安装的格式。换句话说,直接在块上进行更改是不可能的。

我试图确定是否有一种方法可以将图像文件与块设备进行比较,并且仅更新需要更新的块。我怀疑这比写入整个磁盘要快得多,因为这可能相当于图像文件中的 10kb 增量。

答案1

以下是一个小型 C 程序的快速破解,该程序能够按块比较两个文件(文件 1、文件 2),如果块不同,则将相应的块从文件 1 复制到文件 2。也适用于文件和块设备。用它做你想做的事,但风险自负!

/*
Small program to blockwise compare two files and write different
blocks from file1 to file2.

Arguments: file1, file2, blocksize in bytes
If blocksize is not given, it is set to 512 (minimum)

No error checking, no intensive tests run - use at your own risk! 

*/

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(argc, argv)
int argc;
char *argv[];
{

  char *fnamein;                  /* Input file name */
  char *fnameout;                 /* Output file name */
  char *bufin;                    /* Input buffer */
  char *bufout;                   /* Output buffer */
  int bufsize;                    /* Buffer size (blocksize) */
  int fdin;                       /* Input file descriptor*/
  int fdout;                      /* Output file descriptor*/
  int cnt;                        /* Current block # */

  /* Argument processing */

  if (argc < 3 || argc > 4) {
    fprintf(stderr,"Usage: %s infile outfile [bufsize]\n", argv[0]);
    exit(1);
  }

  fnamein = argv[1];
  fnameout = argv[2];
  if (argc == 4) {
    bufsize = atoi(argv[3]);
    if (bufsize < 512) {
      fprintf(stderr,"Error: Illegal value for [bufsize]: %s\n", argv[3]);
      exit(1);
    }
  } else {
    bufsize = 512;
  }

  fprintf(stderr, "Copying differing blocks from '%s' to '%s', blocksize is %i\n", fnamein, fnameout, bufsize);

  if (! ((bufin = malloc(bufsize)) && (bufout = malloc(bufsize)))) {
    fprintf(stderr,"Error: Can't allocate buffers: %i\n", bufsize);
    exit(1);  
  }
  fdin = open(fnamein, O_RDONLY);
  if (fdin < 0) {
    fprintf(stderr,"Error: Can't open input file: %s\n", fnamein);
    exit(1);  
  }

  fdout = open(fnameout, O_RDWR | O_SYNC);
  if (fdout < 0) {
    fprintf(stderr,"Error: Can't open ouput file: %s\n", fnameout);
    exit(1);  
  }

  cnt = 0;
  while (read(fdin, bufin, bufsize) == bufsize) {
    if (read(fdout, bufout, bufsize) == bufsize) {
      if (memcmp(bufin, bufout, bufsize) != 0) {
        fprintf(stderr, "Differing blocks at block # %i; writing block to %s\n", cnt, fnameout);
        if (lseek(fdout, -bufsize, SEEK_CUR) > -1) {
          if (write(fdout, bufin, bufsize) != bufsize) {
            fprintf(stderr,"Error: Unable to write to output file %s block # %i\n", fnameout, cnt);
            exit(1);
          }
        } else {
          fprintf(stderr,"Error: Unable to seek to output file %s block # %i\n", fnameout, cnt);
          exit(1);
        }
      }
    } else {
      fprintf(stderr,"Error: Unable to read from ouput file %s block # %i\n", fnameout, cnt);
      exit(1);
    }
    cnt++;
  }

  exit(0);
}

答案2

查看 qemu-img,使用原始文件作为备份文件,并以 qcow2 格式创建差异磁盘。

使用 qemu-nbd 将其作为块设备访问(将其转换为 nbd 设备)。

这会将增量记录到 qcow2 差异磁盘中,并保留原始数据。对于每个更改的块,差异磁盘会以较小的值增长。

然后,要将这些增量应用到一个或多个“原始文件”,请使用“提交”qemu-img 操作。

答案3

好吧,正如在对您的问题的评论中提到的,dd参数bsseekskipcount是您的朋友。此外,我会在逻辑上将您的图像划分为一系列适当大小的块(例如,每个块 10Mb),并维护每个块的 md5sum(或更长的哈希值,如果您担心冲突,我不会)。当新图像出现时,您只需根据哈希值检查新图像(实际上将比较时间减半),然后仅将更改的块复制到磁盘。您甚至可能会发现某些块是相同的(可能全为零),并相应地执行进一步的优化。

相关内容