OSDN Git Service

e2image: add -c option to optimize file system copying for flash devices
authorTheodore Ts'o <tytso@mit.edu>
Wed, 25 Dec 2013 21:46:39 +0000 (16:46 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 26 Dec 2013 05:21:13 +0000 (00:21 -0500)
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
misc/e2image.8.in
misc/e2image.c

index 672df0d..cdf63bf 100644 (file)
@@ -8,7 +8,7 @@ e2image \- Save critical ext2/ext3/ext4 filesystem metadata to a file
 .SH SYNOPSIS
 .B e2image
 [
-.B \-rsIQap
+.B \-acfprsIQ
 ]
 [
 .B \-o
@@ -45,7 +45,7 @@ or
 options, the filesystem must be unmounted or be mounted read/only, in order
 for the image file to be in a consistent state.  This requirement can be
 overriden using the
-.B -f
+.B \-f
 option, but the resulting image file is very likely not going to be useful.
 .PP
 If
@@ -209,7 +209,16 @@ give an image that is suitable to use to clone the entire FS or
 for backup purposes.  Note that this option only works with the
 raw or QCOW2 formats.  The
 .B \-p
-switch may be given to show progress.
+switch may be given to show progress.  If the file system is being
+cloned to a flash-based storage device (where reads are very fast and
+where it is desirable to avoid unnecessary writes to reduce write wear
+on the device), the
+.B \-c
+option which cause e2image to try reading a block from the destination
+to see if it is identical to the block which
+.B e2image
+is about to copy.  If the block is already the same, the write can be
+skipped.
 .PP
 .SH OFFSETS
 Normally a filesystem starts at the beginning of a partition, and
index 67dd3e0..7394df5 100644 (file)
@@ -68,6 +68,8 @@ static char output_is_blk;
 static blk64_t source_offset, dest_offset;
 static char move_mode;
 static char show_progress;
+static char *check_buf;
+static int skipped_blocks;
 
 static blk64_t align_offset(blk64_t offset, unsigned int n)
 {
@@ -94,7 +96,8 @@ static int get_bits_from_size(size_t size)
 
 static void usage(void)
 {
-       fprintf(stderr, _("Usage: %s [-rsIQafp] [-o src_offset] [-O dest_offset] device image_file\n"),
+       fprintf(stderr, _("Usage: %s [-acfprsIQ] [-o src_offset] "
+                         "[-O dest_offset] \\\n\tdevice image_file\n"),
                program_name);
        exit (1);
 }
@@ -119,6 +122,32 @@ static ext2_loff_t seek_set(int fd, ext2_loff_t offset)
        return ret;
 }
 
+/*
+ * Returns true if the block we are about to write is identical to
+ * what is already on the disk.
+ */
+static int check_block(int fd, void *buf, void *cbuf, int blocksize)
+{
+       char *cp = cbuf;
+       int count = blocksize, ret;
+
+       if (cbuf == NULL)
+               return 0;
+
+       while (count > 0) {
+               ret = read(fd, cp, count);
+               if (ret < 0) {
+                       perror("check_block");
+                       exit(1);
+               }
+               count -= ret;
+               cp += ret;
+       }
+       ret = memcmp(buf, cbuf, blocksize);
+       seek_relative(fd, -blocksize);
+       return (ret == 0) ? 1 : 0;
+}
+
 static void generic_write(int fd, void *buf, int blocksize, blk64_t block)
 {
        int count, free_buf = 0;
@@ -621,8 +650,12 @@ more_blocks:
                                goto sparse_write;
                        if (sparse)
                                seek_relative(fd, sparse);
-                       generic_write(fd, buf, fs->blocksize, blk);
                        sparse = 0;
+                       if (check_block(fd, buf, check_buf, fs->blocksize)) {
+                               seek_relative(fd, fs->blocksize);
+                               skipped_blocks++;
+                       } else
+                               generic_write(fd, buf, fs->blocksize, blk);
                } else {
                sparse_write:
                        if (fd == 1) {
@@ -1398,6 +1431,7 @@ int main (int argc, char ** argv)
        int fd = 0;
        int ret = 0;
        int ignore_rw_mount = 0;
+       int check = 0;
        struct stat st;
 
 #ifdef ENABLE_NLS
@@ -1412,7 +1446,7 @@ int main (int argc, char ** argv)
        if (argc && *argv)
                program_name = *argv;
        add_error_table(&et_ext2_error_table);
-       while ((c = getopt(argc, argv, "rsIQafo:O:p")) != EOF)
+       while ((c = getopt(argc, argv, "rsIQafo:O:pc")) != EOF)
                switch (c) {
                case 'I':
                        flags |= E2IMAGE_INSTALL_FLAG;
@@ -1445,6 +1479,9 @@ int main (int argc, char ** argv)
                case 'p':
                        show_progress = 1;
                        break;
+               case 'c':
+                       check = 1;
+                       break;
                default:
                        usage();
                }
@@ -1522,7 +1559,7 @@ skip_device:
        if (strcmp(image_fn, "-") == 0)
                fd = 1;
        else {
-               int o_flags = O_CREAT|O_WRONLY;
+               int o_flags = O_CREAT|O_RDWR;
 
                if (img_type != E2IMAGE_RAW)
                        o_flags |= O_TRUNC;
@@ -1568,6 +1605,24 @@ skip_device:
                goto out;
        }
 
+       if (check) {
+               if (img_type != E2IMAGE_RAW) {
+                       fprintf(stderr, _("The -c option only supported "
+                                         "in raw mode\n"));
+                       exit(1);
+               }
+               if (fd == 1) {
+                       fprintf(stderr, _("The -c option is not supported "
+                                         "when writing to stdout\n"));
+                       exit(1);
+               }
+               retval = ext2fs_get_mem(fs->blocksize, &check_buf);
+               if (retval) {
+                       com_err(program_name, retval,
+                               "while allocating check_buf");
+                       exit(1);
+               }
+       }
 
        if (img_type)
                write_raw_image_file(fs, fd, img_type, flags);
@@ -1575,6 +1630,10 @@ skip_device:
                write_image_file(fs, fd);
 
        ext2fs_close (fs);
+       if (check)
+               printf("%d blocks already contained the data to be copied.\n",
+                      skipped_blocks);
+
 out:
        if (header)
                free(header);