Whamcloud - gitweb
libext2fs: explicitly ignore a possible unlink failure in ext2fs_free_icount
[tools/e2fsprogs.git] / misc / e2image.c
index 7abce29..d52accf 100644 (file)
  * %End-Header%
  */
 
+#ifndef _LARGEFILE_SOURCE
 #define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
 #define _LARGEFILE64_SOURCE
+#endif
 
+#include "config.h"
 #include <fcntl.h>
 #include <grp.h>
 #ifdef HAVE_GETOPT_H
@@ -33,58 +38,197 @@ extern int optind;
 #include <errno.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <assert.h>
+#include <signal.h>
 
 #include "ext2fs/ext2_fs.h"
 #include "ext2fs/ext2fs.h"
+#include "ext2fs/ext2fsP.h"
 #include "et/com_err.h"
 #include "uuid/uuid.h"
 #include "e2p/e2p.h"
 #include "ext2fs/e2image.h"
+#include "ext2fs/qcow2.h"
 
+#include "support/nls-enable.h"
+#include "support/plausible.h"
 #include "../version.h"
-#include "nls-enable.h"
 
-const char * program_name = "e2image";
-char * device_name = NULL;
+#define QCOW_OFLAG_COPIED     (1LL << 63)
+#define NO_BLK ((blk64_t) -1)
+
+/* Image types */
+#define E2IMAGE_RAW    1
+#define E2IMAGE_QCOW2  2
+
+/* Image flags */
+#define E2IMAGE_INSTALL_FLAG   1
+#define E2IMAGE_SCRAMBLE_FLAG  2
+#define E2IMAGE_IS_QCOW2_FLAG  4
+#define E2IMAGE_CHECK_ZERO_FLAG        8
+
+static const char * program_name = "e2image";
+static char * device_name = NULL;
+static char all_data;
+static char output_is_blk;
+static char nop_flag;
+/* writing to blk device: don't skip zeroed blocks */
+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)
+{
+       return (offset + n - 1) & ~((blk64_t) n - 1);
+}
+
+static int get_bits_from_size(size_t size)
+{
+       int res = 0;
+
+       if (size == 0)
+               return -1;
+
+       while (size != 1) {
+               /* Not a power of two */
+               if (size & 1)
+                       return -1;
+
+               size >>= 1;
+               res++;
+       }
+       return res;
+}
 
 static void usage(void)
 {
-       fprintf(stderr, _("Usage: %s [-rsI] device image_file\n"),
+       fprintf(stderr, _("Usage: %s [ -r|Q ] [ -f ] device image-file\n"),
+               program_name);
+       fprintf(stderr, _("       %s -I device image-file\n"), program_name);
+       fprintf(stderr, _("       %s -ra  [  -cfnp  ] [ -o src_offset ] "
+                         "[ -O dest_offset ] src_fs [ dest_fs ]\n"),
                program_name);
        exit (1);
 }
 
-static void write_header(int fd, struct ext2_image_hdr *hdr, int blocksize)
+static ext2_loff_t seek_relative(int fd, int offset)
 {
-       char *header_buf;
-       int actual;
-
-       header_buf = malloc(blocksize);
-       if (!header_buf) {
-               fputs(_("Couldn't allocate header buffer\n"), stderr);
+       ext2_loff_t ret = ext2fs_llseek(fd, offset, SEEK_CUR);
+       if (ret < 0) {
+               perror("seek_relative");
                exit(1);
        }
+       return ret;
+}
 
-       if (lseek(fd, 0, SEEK_SET) < 0) {
-               perror("lseek while writing header");
+static ext2_loff_t seek_set(int fd, ext2_loff_t offset)
+{
+       ext2_loff_t ret = ext2fs_llseek(fd, offset, SEEK_SET);
+       if (ret < 0) {
+               perror("seek_set");
                exit(1);
        }
-       memset(header_buf, 0, blocksize);
+       return ret;
+}
 
-       if (hdr)
-               memcpy(header_buf, hdr, sizeof(struct ext2_image_hdr));
+/*
+ * 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;
+       errcode_t err;
+
+       if (!blocksize)
+               return;
+
+       if (!buf) {
+               free_buf = 1;
+               err = ext2fs_get_arrayzero(1, blocksize, &buf);
+               if (err) {
+                       com_err(program_name, err, "%s",
+                               _("while allocating buffer"));
+                       exit(1);
+               }
+       }
+       if (nop_flag) {
+               printf(_("Writing block %llu\n"), (unsigned long long) block);
+               if (fd != 1)
+                       seek_relative(fd, blocksize);
+               goto free_and_return;
+       }
+       count = write(fd, buf, blocksize);
+       if (count != blocksize) {
+               if (count == -1)
+                       err = errno;
+               else
+                       err = 0;
+
+               if (block)
+                       com_err(program_name, err,
+                               _("error writing block %llu"), block);
+               else
+                       com_err(program_name, err, "%s",
+                               _("error in generic_write()"));
 
-       actual = write(fd, header_buf, blocksize);
-       if (actual < 0) {
-               perror("write header");
                exit(1);
        }
-       if (actual != blocksize) {
-               fprintf(stderr, _("short write (only %d bytes) for "
-                                 "writing image header"), actual);
+free_and_return:
+       if (free_buf)
+               ext2fs_free_mem(&buf);
+}
+
+static void write_header(int fd, void *hdr, int hdr_size, int wrt_size)
+{
+       char *header_buf;
+       int ret;
+
+       /* Sanity check */
+       if (hdr_size > wrt_size) {
+               fprintf(stderr, "%s",
+                       _("Error: header size is bigger than wrt_size\n"));
+       }
+
+       ret = ext2fs_get_mem(wrt_size, &header_buf);
+       if (ret) {
+               fputs(_("Couldn't allocate header buffer\n"), stderr);
                exit(1);
        }
-       free(header_buf);
+
+       seek_set(fd, 0);
+       memset(header_buf, 0, wrt_size);
+
+       if (hdr)
+               memcpy(header_buf, hdr, hdr_size);
+
+       generic_write(fd, header_buf, wrt_size, NO_BLK);
+
+       ext2fs_free_mem(&header_buf);
 }
 
 static void write_image_file(ext2_filsys fs, int fd)
@@ -93,63 +237,68 @@ static void write_image_file(ext2_filsys fs, int fd)
        struct stat             st;
        errcode_t               retval;
 
-       write_header(fd, NULL, fs->blocksize);
+       write_header(fd, NULL, sizeof(struct ext2_image_hdr), fs->blocksize);
        memset(&hdr, 0, sizeof(struct ext2_image_hdr));
 
-       hdr.offset_super = lseek(fd, 0, SEEK_CUR);
+       hdr.offset_super = ext2fs_cpu_to_le32(seek_relative(fd, 0));
        retval = ext2fs_image_super_write(fs, fd, 0);
        if (retval) {
-               com_err(program_name, retval, _("while writing superblock"));
+               com_err(program_name, retval, "%s",
+                       _("while writing superblock"));
                exit(1);
        }
 
-       hdr.offset_inode = lseek(fd, 0, SEEK_CUR);
+       hdr.offset_inode = ext2fs_cpu_to_le32(seek_relative(fd, 0));
        retval = ext2fs_image_inode_write(fs, fd,
                                  (fd != 1) ? IMAGER_FLAG_SPARSEWRITE : 0);
        if (retval) {
-               com_err(program_name, retval, _("while writing inode table"));
+               com_err(program_name, retval, "%s",
+                       _("while writing inode table"));
                exit(1);
        }
 
-       hdr.offset_blockmap = lseek(fd, 0, SEEK_CUR);
+       hdr.offset_blockmap = ext2fs_cpu_to_le32(seek_relative(fd, 0));
        retval = ext2fs_image_bitmap_write(fs, fd, 0);
        if (retval) {
-               com_err(program_name, retval, _("while writing block bitmap"));
+               com_err(program_name, retval, "%s",
+                       _("while writing block bitmap"));
                exit(1);
        }
 
-       hdr.offset_inodemap = lseek(fd, 0, SEEK_CUR);
+       hdr.offset_inodemap = ext2fs_cpu_to_le32(seek_relative(fd, 0));
        retval = ext2fs_image_bitmap_write(fs, fd, IMAGER_FLAG_INODEMAP);
        if (retval) {
-               com_err(program_name, retval, _("while writing inode bitmap"));
+               com_err(program_name, retval, "%s",
+                       _("while writing inode bitmap"));
                exit(1);
        }
 
-       hdr.magic_number = EXT2_ET_MAGIC_E2IMAGE;
+       hdr.magic_number = ext2fs_cpu_to_le32(EXT2_ET_MAGIC_E2IMAGE);
        strcpy(hdr.magic_descriptor, "Ext2 Image 1.0");
        gethostname(hdr.fs_hostname, sizeof(hdr.fs_hostname));
        strncpy(hdr.fs_device_name, device_name, sizeof(hdr.fs_device_name)-1);
        hdr.fs_device_name[sizeof(hdr.fs_device_name) - 1] = 0;
-       hdr.fs_blocksize = fs->blocksize;
+       hdr.fs_blocksize = ext2fs_cpu_to_le32(fs->blocksize);
 
        if (stat(device_name, &st) == 0)
-               hdr.fs_device = st.st_rdev;
+               hdr.fs_device = ext2fs_cpu_to_le32(st.st_rdev);
 
        if (fstat(fd, &st) == 0) {
-               hdr.image_device = st.st_dev;
-               hdr.image_inode = st.st_ino;
+               hdr.image_device = ext2fs_cpu_to_le32(st.st_dev);
+               hdr.image_inode = ext2fs_cpu_to_le32(st.st_ino);
        }
        memcpy(hdr.fs_uuid, fs->super->s_uuid, sizeof(hdr.fs_uuid));
 
-       hdr.image_time = time(0);
-       write_header(fd, &hdr, fs->blocksize);
+       hdr.image_time = ext2fs_cpu_to_le32(time(0));
+       write_header(fd, &hdr, sizeof(struct ext2_image_hdr), fs->blocksize);
 }
 
 /*
  * These set of functions are used to write a RAW image file.
  */
-ext2fs_block_bitmap meta_block_map;
-ext2fs_block_bitmap scramble_block_map;        /* Directory blocks to be scrambled */
+static ext2fs_block_bitmap meta_block_map;
+static ext2fs_block_bitmap scramble_block_map; /* Directory blocks to be scrambled */
+static blk64_t meta_blocks_count;
 
 struct process_block_struct {
        ext2_ino_t      ino;
@@ -200,9 +349,9 @@ static errcode_t meta_read_inode(ext2_filsys fs EXT2FS_ATTR((unused)),
        return 0;
 }
 
-static void use_inode_shortcuts(ext2_filsys fs, int bool)
+static void use_inode_shortcuts(ext2_filsys fs, int use_shortcuts)
 {
-       if (bool) {
+       if (use_shortcuts) {
                fs->get_blocks = meta_get_blocks;
                fs->check_directory = meta_check_directory;
                fs->read_inode = meta_read_inode;
@@ -215,9 +364,9 @@ static void use_inode_shortcuts(ext2_filsys fs, int bool)
 }
 
 static int process_dir_block(ext2_filsys fs EXT2FS_ATTR((unused)),
-                            blk_t *block_nr,
+                            blk64_t *block_nr,
                             e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
-                            blk_t ref_block EXT2FS_ATTR((unused)),
+                            blk64_t ref_block EXT2FS_ATTR((unused)),
                             int ref_offset EXT2FS_ATTR((unused)),
                             void *priv_data EXT2FS_ATTR((unused)))
 {
@@ -226,27 +375,29 @@ static int process_dir_block(ext2_filsys fs EXT2FS_ATTR((unused)),
        p = (struct process_block_struct *) priv_data;
 
        ext2fs_mark_block_bitmap2(meta_block_map, *block_nr);
+       meta_blocks_count++;
        if (scramble_block_map && p->is_dir && blockcnt >= 0)
                ext2fs_mark_block_bitmap2(scramble_block_map, *block_nr);
        return 0;
 }
 
 static int process_file_block(ext2_filsys fs EXT2FS_ATTR((unused)),
-                             blk_t *block_nr,
+                             blk64_t *block_nr,
                              e2_blkcnt_t blockcnt,
-                             blk_t ref_block EXT2FS_ATTR((unused)),
+                             blk64_t ref_block EXT2FS_ATTR((unused)),
                              int ref_offset EXT2FS_ATTR((unused)),
                              void *priv_data EXT2FS_ATTR((unused)))
 {
-       if (blockcnt < 0) {
+       if (blockcnt < 0 || all_data) {
                ext2fs_mark_block_bitmap2(meta_block_map, *block_nr);
+               meta_blocks_count++;
        }
        return 0;
 }
 
 static void mark_table_blocks(ext2_filsys fs)
 {
-       blk_t   first_block, b;
+       blk64_t first_block, b;
        unsigned int    i,j;
 
        first_block = fs->super->s_first_data_block;
@@ -254,40 +405,55 @@ static void mark_table_blocks(ext2_filsys fs)
         * Mark primary superblock
         */
        ext2fs_mark_block_bitmap2(meta_block_map, first_block);
+       meta_blocks_count++;
 
        /*
         * Mark the primary superblock descriptors
         */
        for (j = 0; j < fs->desc_blocks; j++) {
                ext2fs_mark_block_bitmap2(meta_block_map,
-                        ext2fs_descriptor_block_loc(fs, first_block, j));
+                        ext2fs_descriptor_block_loc2(fs, first_block, j));
        }
+       meta_blocks_count += fs->desc_blocks;
 
        for (i = 0; i < fs->group_desc_count; i++) {
                /*
                 * Mark the blocks used for the inode table
                 */
-               if (fs->group_desc[i].bg_inode_table) {
-                       for (j = 0, b = fs->group_desc[i].bg_inode_table;
-                            j < (unsigned) fs->inode_blocks_per_group;
-                            j++, b++)
+               if ((output_is_blk ||
+                    !ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT)) &&
+                   ext2fs_inode_table_loc(fs, i)) {
+                       unsigned int end = (unsigned) fs->inode_blocks_per_group;
+                       /* skip unused blocks */
+                       if (!output_is_blk && ext2fs_has_group_desc_csum(fs))
+                               end -= (ext2fs_bg_itable_unused(fs, i) /
+                                       EXT2_INODES_PER_BLOCK(fs->super));
+                       for (j = 0, b = ext2fs_inode_table_loc(fs, i);
+                            j < end;
+                            j++, b++) {
                                ext2fs_mark_block_bitmap2(meta_block_map, b);
+                               meta_blocks_count++;
+                       }
                }
 
                /*
                 * Mark block used for the block bitmap
                 */
-               if (fs->group_desc[i].bg_block_bitmap) {
+               if (!ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) &&
+                   ext2fs_block_bitmap_loc(fs, i)) {
                        ext2fs_mark_block_bitmap2(meta_block_map,
-                                    fs->group_desc[i].bg_block_bitmap);
+                                    ext2fs_block_bitmap_loc(fs, i));
+                       meta_blocks_count++;
                }
 
                /*
                 * Mark block used for the inode bitmap
                 */
-               if (fs->group_desc[i].bg_inode_bitmap) {
+               if (!ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) &&
+                   ext2fs_inode_bitmap_loc(fs, i)) {
                        ext2fs_mark_block_bitmap2(meta_block_map,
-                                fs->group_desc[i].bg_inode_bitmap);
+                                ext2fs_inode_bitmap_loc(fs, i));
+                       meta_blocks_count++;
                }
        }
 }
@@ -300,6 +466,8 @@ static int check_zero_block(char *buf, int blocksize)
        char    *cp = buf;
        int     left = blocksize;
 
+       if (output_is_blk)
+               return 0;
        while (left > 0) {
                if (*cp++)
                        return 0;
@@ -308,40 +476,11 @@ static int check_zero_block(char *buf, int blocksize)
        return 1;
 }
 
-static void write_block(int fd, char *buf, int sparse_offset,
-                       int blocksize, blk_t block)
-{
-       int             count;
-       errcode_t       err;
-
-       if (sparse_offset) {
-#ifdef HAVE_LSEEK64
-               if (lseek64(fd, sparse_offset, SEEK_CUR) < 0)
-                       perror("lseek");
-#else
-               if (lseek(fd, sparse_offset, SEEK_CUR) < 0)
-                       perror("lseek");
-#endif
-       }
-       if (blocksize) {
-               count = write(fd, buf, blocksize);
-               if (count != blocksize) {
-                       if (count == -1)
-                               err = errno;
-                       else
-                               err = 0;
-                       com_err(program_name, err, "error writing block %u",
-                               block);
-                       exit(1);
-               }
-       }
-}
-
-int name_id[256];
+static int name_id[256];
 
 #define EXT4_MAX_REC_LEN               ((1<<16)-1)
 
-static void scramble_dir_block(ext2_filsys fs, blk_t blk, char *buf)
+static void scramble_dir_block(ext2_filsys fs, blk64_t blk, char *buf)
 {
        char *p, *end, *cp;
        struct ext2_dir_entry_2 *dirent;
@@ -364,9 +503,9 @@ static void scramble_dir_block(ext2_filsys fs, blk_t blk, char *buf)
 #endif
                if (rec_len < 8 || (rec_len % 4) ||
                    (p+rec_len > end)) {
-                       printf("Corrupt directory block %lu: "
-                              "bad rec_len (%d)\n", (unsigned long) blk,
-                              rec_len);
+                       printf(_("Corrupt directory block %llu: "
+                                "bad rec_len (%d)\n"),
+                              (unsigned long long) blk, rec_len);
                        rec_len = end - p;
                        (void) ext2fs_set_rec_len(fs, rec_len,
                                        (struct ext2_dir_entry *) dirent);
@@ -375,10 +514,10 @@ static void scramble_dir_block(ext2_filsys fs, blk_t blk, char *buf)
 #endif
                        continue;
                }
-               if (dirent->name_len + 8 > rec_len) {
-                       printf("Corrupt directory block %lu: "
-                              "bad name_len (%d)\n", (unsigned long) blk,
-                              dirent->name_len);
+               if (dirent->name_len + 8U > rec_len) {
+                       printf(_("Corrupt directory block %llu: "
+                                "bad name_len (%d)\n"),
+                              (unsigned long long) blk, dirent->name_len);
                        dirent->name_len = rec_len - 8;
                        continue;
                }
@@ -403,60 +542,724 @@ static void scramble_dir_block(ext2_filsys fs, blk_t blk, char *buf)
        }
 }
 
-static void output_meta_data_blocks(ext2_filsys fs, int fd)
+static char got_sigint;
+
+static void sigint_handler(int unused EXT2FS_ATTR((unused)))
+{
+       got_sigint = 1;
+       signal (SIGINT, SIG_DFL);
+}
+
+#define calc_percent(a, b) ((int) ((100.0 * (((float) (a)) / \
+                                            ((float) (b)))) + 0.5))
+#define calc_rate(t, b, d) (((float)(t) / ((float)(1024 * 1024) / (b))) / (d))
+
+static int print_progress(blk64_t num, blk64_t total)
+{
+       return fprintf(stderr, _("%llu / %llu blocks (%d%%)"), num, total,
+                     calc_percent(num, total));
+}
+
+static void output_meta_data_blocks(ext2_filsys fs, int fd, int flags)
 {
        errcode_t       retval;
-       blk_t           blk;
+       blk64_t         blk;
        char            *buf, *zero_buf;
        int             sparse = 0;
-
-       buf = malloc(fs->blocksize);
-       if (!buf) {
-               com_err(program_name, ENOMEM, "while allocating buffer");
+       blk64_t         start = 0;
+       blk64_t         distance = 0;
+       blk64_t         end = ext2fs_blocks_count(fs->super);
+       time_t          last_update = 0;
+       time_t          start_time = 0;
+       blk64_t         total_written = 0;
+       int             bscount = 0;
+
+       retval = ext2fs_get_mem(fs->blocksize, &buf);
+       if (retval) {
+               com_err(program_name, retval, "%s",
+                       _("while allocating buffer"));
                exit(1);
        }
-       zero_buf = malloc(fs->blocksize);
-       if (!zero_buf) {
-               com_err(program_name, ENOMEM, "while allocating buffer");
+       retval = ext2fs_get_memzero(fs->blocksize, &zero_buf);
+       if (retval) {
+               com_err(program_name, retval, "%s",
+                       _("while allocating buffer"));
                exit(1);
        }
-       memset(zero_buf, 0, fs->blocksize);
-       for (blk = 0; blk < fs->super->s_blocks_count; blk++) {
+       if (show_progress) {
+               fprintf(stderr, "%s", _("Copying "));
+               bscount = print_progress(total_written, meta_blocks_count);
+               fflush(stderr);
+               last_update = time(NULL);
+               start_time = time(NULL);
+       }
+       /* when doing an in place move to the right, you can't start
+          at the beginning or you will overwrite data, so instead
+          divide the fs up into distance size chunks and write them
+          in reverse. */
+       if (move_mode && dest_offset > source_offset) {
+               distance = (dest_offset - source_offset) / fs->blocksize;
+               if (distance < ext2fs_blocks_count(fs->super))
+                       start = ext2fs_blocks_count(fs->super) - distance;
+       }
+       if (move_mode)
+               signal (SIGINT, sigint_handler);
+more_blocks:
+       if (distance)
+               seek_set(fd, (start * fs->blocksize) + dest_offset);
+       for (blk = start; blk < end; blk++) {
+               if (got_sigint) {
+                       if (distance) {
+                               /* moving to the right */
+                               if (distance >= ext2fs_blocks_count(fs->super)||
+                                   start == ext2fs_blocks_count(fs->super) -
+                                               distance)
+                                       kill(getpid(), SIGINT);
+                       } else {
+                               /* moving to the left */
+                               if (blk < (source_offset - dest_offset) /
+                                   fs->blocksize)
+                                       kill(getpid(), SIGINT);
+                       }
+                       if (show_progress)
+                               fputc('\r', stderr);
+                       fprintf(stderr, "%s",
+                               _("Stopping now will destroy the filesystem, "
+                                "interrupt again if you are sure\n"));
+                       if (show_progress) {
+                               fprintf(stderr, "%s", _("Copying "));
+                               bscount = print_progress(total_written,
+                                                        meta_blocks_count);
+                               fflush(stderr);
+                       }
+
+                       got_sigint = 0;
+               }
+               if (show_progress && last_update != time(NULL)) {
+                       time_t duration;
+                       last_update = time(NULL);
+                       while (bscount--)
+                               fputc('\b', stderr);
+                       bscount = print_progress(total_written,
+                                                meta_blocks_count);
+                       duration = time(NULL) - start_time;
+                       if (duration > 5 && total_written) {
+                               time_t est = (duration * meta_blocks_count /
+                                             total_written) - duration;
+                               char buff[30];
+                               strftime(buff, 30, "%T", gmtime(&est));
+                               bscount +=
+                                       fprintf(stderr,
+                                               _(" %s remaining at %.2f MB/s"),
+                                               buff, calc_rate(total_written,
+                                                               fs->blocksize,
+                                                               duration));
+                       }
+                       fflush (stderr);
+               }
                if ((blk >= fs->super->s_first_data_block) &&
                    ext2fs_test_block_bitmap2(meta_block_map, blk)) {
-                       retval = io_channel_read_blk(fs->io, blk, 1, buf);
+                       retval = io_channel_read_blk64(fs->io, blk, 1, buf);
                        if (retval) {
                                com_err(program_name, retval,
-                                       "error reading block %u", blk);
+                                       _("error reading block %llu"), blk);
                        }
+                       total_written++;
                        if (scramble_block_map &&
                            ext2fs_test_block_bitmap2(scramble_block_map, blk))
                                scramble_dir_block(fs, blk, buf);
-                       if ((fd != 1) && check_zero_block(buf, fs->blocksize))
+                       if ((flags & E2IMAGE_CHECK_ZERO_FLAG) &&
+                           check_zero_block(buf, fs->blocksize))
                                goto sparse_write;
-                       write_block(fd, buf, sparse, fs->blocksize, blk);
+                       if (sparse)
+                               seek_relative(fd, sparse);
                        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) {
-                               write_block(fd, zero_buf, 0,
-                                           fs->blocksize, blk);
+                               if (!nop_flag)
+                                       generic_write(fd, zero_buf,
+                                                     fs->blocksize, blk);
                                continue;
                        }
                        sparse += fs->blocksize;
-                       if (sparse >= 1024*1024) {
-                               write_block(fd, 0, sparse, 0, 0);
-                               sparse = 0;
+                       if (sparse > 1024*1024) {
+                               seek_relative(fd, 1024*1024);
+                               sparse -= 1024*1024;
+                       }
+               }
+       }
+       if (distance && start) {
+               if (start < distance) {
+                       end = start;
+                       start = 0;
+               } else {
+                       end -= distance;
+                       start -= distance;
+                       if (end < distance) {
+                               /* past overlap, do rest in one go */
+                               end = start;
+                               start = 0;
+                       }
+               }
+               sparse = 0;
+               goto more_blocks;
+       }
+       signal (SIGINT, SIG_DFL);
+       if (show_progress) {
+               time_t duration = time(NULL) - start_time;
+               char buff[30];
+               fputc('\r', stderr);
+               strftime(buff, 30, "%T", gmtime(&duration));
+               fprintf(stderr, _("Copied %llu / %llu blocks (%d%%) in %s "),
+                       total_written, meta_blocks_count,
+                       calc_percent(total_written, meta_blocks_count), buff);
+               if (duration)
+                       fprintf(stderr, _("at %.2f MB/s"),
+                               calc_rate(total_written, fs->blocksize, duration));
+               fputs("       \n", stderr);
+       }
+#ifdef HAVE_FTRUNCATE64
+       if (sparse) {
+               ext2_loff_t offset;
+               if (distance)
+                       offset = seek_set(fd,
+                                         fs->blocksize * ext2fs_blocks_count(fs->super) + dest_offset);
+               else
+                       offset = seek_relative(fd, sparse);
+
+               if (ftruncate64(fd, offset) < 0) {
+                       seek_relative(fd, -1);
+                       generic_write(fd, zero_buf, 1, NO_BLK);
+               }
+       }
+#else
+       if (sparse && !distance) {
+               seek_relative(fd, sparse-1);
+               generic_write(fd, zero_buf, 1, NO_BLK);
+       }
+#endif
+       ext2fs_free_mem(&zero_buf);
+       ext2fs_free_mem(&buf);
+}
+
+static void init_l1_table(struct ext2_qcow2_image *image)
+{
+       __u64 *l1_table;
+       errcode_t ret;
+
+       ret = ext2fs_get_arrayzero(image->l1_size, sizeof(__u64), &l1_table);
+       if (ret) {
+               com_err(program_name, ret, "%s",
+                       _("while allocating l1 table"));
+               exit(1);
+       }
+
+       image->l1_table = l1_table;
+}
+
+static void init_l2_cache(struct ext2_qcow2_image *image)
+{
+       unsigned int count, i;
+       struct ext2_qcow2_l2_cache *cache;
+       struct ext2_qcow2_l2_table *table;
+       errcode_t ret;
+
+       ret = ext2fs_get_arrayzero(1, sizeof(struct ext2_qcow2_l2_cache),
+                                  &cache);
+       if (ret)
+               goto alloc_err;
+
+       count = (image->l1_size > L2_CACHE_PREALLOC) ? L2_CACHE_PREALLOC :
+                image->l1_size;
+
+       cache->count = count;
+       cache->free = count;
+       cache->next_offset = image->l2_offset;
+
+       for (i = 0; i < count; i++) {
+               ret = ext2fs_get_arrayzero(1,
+                               sizeof(struct ext2_qcow2_l2_table), &table);
+               if (ret)
+                       goto alloc_err;
+
+               ret = ext2fs_get_arrayzero(image->l2_size,
+                                                  sizeof(__u64), &table->data);
+               if (ret)
+                       goto alloc_err;
+
+               table->next = cache->free_head;
+               cache->free_head = table;
+       }
+
+       image->l2_cache = cache;
+       return;
+
+alloc_err:
+       com_err(program_name, ret, "%s", _("while allocating l2 cache"));
+       exit(1);
+}
+
+static void put_l2_cache(struct ext2_qcow2_image *image)
+{
+       struct ext2_qcow2_l2_cache *cache = image->l2_cache;
+       struct ext2_qcow2_l2_table *tmp, *table;
+
+       if (!cache)
+               return;
+
+       table = cache->free_head;
+       cache->free_head = NULL;
+again:
+       while (table) {
+               tmp = table;
+               table = table->next;
+               ext2fs_free_mem(&tmp->data);
+               ext2fs_free_mem(&tmp);
+       }
+
+       if (cache->free != cache->count) {
+               fprintf(stderr, "%s", _("Warning: There are still tables in "
+                                       "the cache while putting the cache, "
+                                       "data will be lost so the image may "
+                                       "not be valid.\n"));
+               table = cache->used_head;
+               cache->used_head = NULL;
+               goto again;
+       }
+
+       ext2fs_free_mem(&cache);
+}
+
+static int init_refcount(struct ext2_qcow2_image *img, blk64_t table_offset)
+{
+       struct  ext2_qcow2_refcount     *ref;
+       blk64_t table_clusters;
+       errcode_t ret;
+
+       ref = &(img->refcount);
+
+       /*
+        * One refcount block addresses 2048 clusters, one refcount table
+        * addresses cluster/sizeof(__u64) refcount blocks, and we need
+        * to address meta_blocks_count clusters + qcow2 metadata clusters
+        * in the worst case.
+        */
+       table_clusters = meta_blocks_count + (table_offset >>
+                                             img->cluster_bits);
+       table_clusters >>= (img->cluster_bits + 6 - 1);
+       table_clusters = (table_clusters == 0) ? 1 : table_clusters;
+
+       ref->refcount_table_offset = table_offset;
+       ref->refcount_table_clusters = table_clusters;
+       ref->refcount_table_index = 0;
+       ref->refcount_block_index = 0;
+
+       /* Allocate refcount table */
+       ret = ext2fs_get_arrayzero(ref->refcount_table_clusters,
+                                  img->cluster_size, &ref->refcount_table);
+       if (ret)
+               return ret;
+
+       /* Allocate refcount block */
+       ret = ext2fs_get_arrayzero(1, img->cluster_size, &ref->refcount_block);
+       if (ret)
+               ext2fs_free_mem(&ref->refcount_table);
+
+       return ret;
+}
+
+static errcode_t initialize_qcow2_image(int fd, ext2_filsys fs,
+                                       struct ext2_qcow2_image *image)
+{
+       struct ext2_qcow2_hdr *header;
+       blk64_t total_size, offset;
+       int shift, l2_bits, header_size, l1_size, ret;
+       int cluster_bits = get_bits_from_size(fs->blocksize);
+       struct ext2_super_block *sb = fs->super;
+
+       if (fs->blocksize < 1024)
+               return EINVAL;  /* Can never happen, but just in case... */
+
+       /* Allocate header */
+       ret = ext2fs_get_memzero(sizeof(struct ext2_qcow2_hdr), &header);
+       if (ret)
+               return ret;
+
+       total_size = ext2fs_blocks_count(sb) << cluster_bits;
+       image->cluster_size = fs->blocksize;
+       image->l2_size = 1 << (cluster_bits - 3);
+       image->cluster_bits = cluster_bits;
+       image->fd = fd;
+
+       header->magic = ext2fs_cpu_to_be32(QCOW_MAGIC);
+       header->version = ext2fs_cpu_to_be32(QCOW_VERSION);
+       header->size = ext2fs_cpu_to_be64(total_size);
+       header->cluster_bits = ext2fs_cpu_to_be32(cluster_bits);
+
+       header_size = (sizeof(struct ext2_qcow2_hdr) + 7) & ~7;
+       offset = align_offset(header_size, image->cluster_size);
+
+       header->l1_table_offset = ext2fs_cpu_to_be64(offset);
+       image->l1_offset = offset;
+
+       l2_bits = cluster_bits - 3;
+       shift = cluster_bits + l2_bits;
+       l1_size = ((total_size + (1LL << shift) - 1) >> shift);
+       header->l1_size = ext2fs_cpu_to_be32(l1_size);
+       image->l1_size = l1_size;
+
+       /* Make space for L1 table */
+       offset += align_offset(l1_size * sizeof(blk64_t), image->cluster_size);
+
+       /* Initialize refcounting */
+       ret = init_refcount(image, offset);
+       if (ret) {
+               ext2fs_free_mem(&header);
+               return ret;
+       }
+       header->refcount_table_offset = ext2fs_cpu_to_be64(offset);
+       header->refcount_table_clusters =
+               ext2fs_cpu_to_be32(image->refcount.refcount_table_clusters);
+       offset += image->cluster_size;
+       offset += image->refcount.refcount_table_clusters <<
+               image->cluster_bits;
+
+       /* Make space for L2 tables */
+       image->l2_offset = offset;
+       offset += image->cluster_size;
+
+       /* Make space for first refcount block */
+       image->refcount.refcount_block_offset = offset;
+
+       image->hdr = header;
+       /* Initialize l1 and l2 tables */
+       init_l1_table(image);
+       init_l2_cache(image);
+
+       return 0;
+}
+
+static void free_qcow2_image(struct ext2_qcow2_image *img)
+{
+       if (!img)
+               return;
+
+       if (img->hdr)
+               ext2fs_free_mem(&img->hdr);
+
+       if (img->l1_table)
+               ext2fs_free_mem(&img->l1_table);
+
+       if (img->refcount.refcount_table)
+               ext2fs_free_mem(&img->refcount.refcount_table);
+       if (img->refcount.refcount_block)
+               ext2fs_free_mem(&img->refcount.refcount_block);
+
+       put_l2_cache(img);
+
+       ext2fs_free_mem(&img);
+}
+
+/**
+ * Put table from used list (used_head) into free list (free_head).
+ * l2_table is used to return pointer to the next used table (used_head).
+ */
+static void put_used_table(struct ext2_qcow2_image *img,
+                         struct ext2_qcow2_l2_table **l2_table)
+{
+       struct ext2_qcow2_l2_cache *cache = img->l2_cache;
+       struct ext2_qcow2_l2_table *table;
+
+       table = cache->used_head;
+       cache->used_head = table->next;
+
+       assert(table);
+       if (!table->next)
+               cache->used_tail = NULL;
+
+       /* Clean the table for case we will need to use it again */
+       memset(table->data, 0, img->cluster_size);
+       table->next = cache->free_head;
+       cache->free_head = table;
+
+       cache->free++;
+
+       *l2_table = cache->used_head;
+}
+
+static void flush_l2_cache(struct ext2_qcow2_image *image)
+{
+       blk64_t seek = 0;
+       ext2_loff_t offset;
+       struct ext2_qcow2_l2_cache *cache = image->l2_cache;
+       struct ext2_qcow2_l2_table *table = cache->used_head;
+       int fd = image->fd;
+
+       /* Store current position */
+       offset = seek_relative(fd, 0);
+
+       assert(table);
+       while (cache->free < cache->count) {
+               if (seek != table->offset) {
+                       seek_set(fd, table->offset);
+                       seek = table->offset;
+               }
+
+               generic_write(fd, (char *)table->data, image->cluster_size,
+                             NO_BLK);
+               put_used_table(image, &table);
+               seek += image->cluster_size;
+       }
+
+       /* Restore previous position */
+       seek_set(fd, offset);
+}
+
+/**
+ * Get first free table (from free_head) and put it into tail of used list
+ * (to used_tail).
+ * l2_table is used to return pointer to moved table.
+ * Returns 1 if the cache is full, 0 otherwise.
+ */
+static void get_free_table(struct ext2_qcow2_image *image,
+                         struct ext2_qcow2_l2_table **l2_table)
+{
+       struct ext2_qcow2_l2_table *table;
+       struct ext2_qcow2_l2_cache *cache = image->l2_cache;
+
+       if (0 == cache->free)
+               flush_l2_cache(image);
+
+       table = cache->free_head;
+       assert(table);
+       cache->free_head = table->next;
+
+       if (cache->used_tail)
+               cache->used_tail->next = table;
+       else
+               /* First item in the used list */
+               cache->used_head = table;
+
+       cache->used_tail = table;
+       cache->free--;
+
+       *l2_table = table;
+}
+
+static int add_l2_item(struct ext2_qcow2_image *img, blk64_t blk,
+                      blk64_t data, blk64_t next)
+{
+       struct ext2_qcow2_l2_cache *cache = img->l2_cache;
+       struct ext2_qcow2_l2_table *table = cache->used_tail;
+       blk64_t l1_index = blk / img->l2_size;
+       blk64_t l2_index = blk & (img->l2_size - 1);
+       int ret = 0;
+
+       /*
+        * Need to create new table if it does not exist,
+        * or if it is full
+        */
+       if (!table || (table->l1_index != l1_index)) {
+               get_free_table(img, &table);
+               table->l1_index = l1_index;
+               table->offset = cache->next_offset;
+               cache->next_offset = next;
+               img->l1_table[l1_index] =
+                       ext2fs_cpu_to_be64(table->offset | QCOW_OFLAG_COPIED);
+               ret++;
+       }
+
+       table->data[l2_index] = ext2fs_cpu_to_be64(data | QCOW_OFLAG_COPIED);
+       return ret;
+}
+
+static int update_refcount(int fd, struct ext2_qcow2_image *img,
+                          blk64_t offset, blk64_t rfblk_pos)
+{
+       struct  ext2_qcow2_refcount     *ref;
+       __u32   table_index;
+       int ret = 0;
+
+       ref = &(img->refcount);
+       table_index = offset >> (2 * img->cluster_bits - 1);
+
+       /*
+        * Need to create new refcount block when the offset addresses
+        * another item in the refcount table
+        */
+       if (table_index != ref->refcount_table_index) {
+
+               seek_set(fd, ref->refcount_block_offset);
+
+               generic_write(fd, (char *)ref->refcount_block,
+                             img->cluster_size, NO_BLK);
+               memset(ref->refcount_block, 0, img->cluster_size);
+
+               ref->refcount_table[ref->refcount_table_index] =
+                       ext2fs_cpu_to_be64(ref->refcount_block_offset);
+               ref->refcount_block_offset = rfblk_pos;
+               ref->refcount_block_index = 0;
+               ref->refcount_table_index = table_index;
+               ret++;
+       }
+
+       /*
+        * We are relying on the fact that we are creating the qcow2
+        * image sequentially, hence we will always allocate refcount
+        * block items sequentially.
+        */
+       ref->refcount_block[ref->refcount_block_index] = ext2fs_cpu_to_be16(1);
+       ref->refcount_block_index++;
+       return ret;
+}
+
+static int sync_refcount(int fd, struct ext2_qcow2_image *img)
+{
+       struct  ext2_qcow2_refcount     *ref;
+
+       ref = &(img->refcount);
+
+       ref->refcount_table[ref->refcount_table_index] =
+               ext2fs_cpu_to_be64(ref->refcount_block_offset);
+       seek_set(fd, ref->refcount_table_offset);
+       generic_write(fd, (char *)ref->refcount_table,
+               ref->refcount_table_clusters << img->cluster_bits, NO_BLK);
+
+       seek_set(fd, ref->refcount_block_offset);
+       generic_write(fd, (char *)ref->refcount_block, img->cluster_size,
+                     NO_BLK);
+       return 0;
+}
+
+static void output_qcow2_meta_data_blocks(ext2_filsys fs, int fd)
+{
+       errcode_t               retval;
+       blk64_t                 blk, offset, size, end;
+       char                    *buf;
+       struct ext2_qcow2_image *img;
+       unsigned int            header_size;
+
+       /* allocate  struct ext2_qcow2_image */
+       retval = ext2fs_get_mem(sizeof(struct ext2_qcow2_image), &img);
+       if (retval) {
+               com_err(program_name, retval, "%s",
+                       _("while allocating ext2_qcow2_image"));
+               exit(1);
+       }
+
+       retval = initialize_qcow2_image(fd, fs, img);
+       if (retval) {
+               com_err(program_name, retval, "%s",
+                       _("while initializing ext2_qcow2_image"));
+               exit(1);
+       }
+       header_size = align_offset(sizeof(struct ext2_qcow2_hdr),
+                                  img->cluster_size);
+       write_header(fd, img->hdr, sizeof(struct ext2_qcow2_hdr), header_size);
+
+       /* Refcount all qcow2 related metadata up to refcount_block_offset */
+       end = img->refcount.refcount_block_offset;
+       seek_set(fd, end);
+       blk = end + img->cluster_size;
+       for (offset = 0; offset <= end; offset += img->cluster_size) {
+               if (update_refcount(fd, img, offset, blk)) {
+                       blk += img->cluster_size;
+                       /*
+                        * If we create new refcount block, we need to refcount
+                        * it as well.
+                        */
+                       end += img->cluster_size;
+               }
+       }
+       seek_set(fd, offset);
+
+       retval = ext2fs_get_mem(fs->blocksize, &buf);
+       if (retval) {
+               com_err(program_name, retval, "%s",
+                       _("while allocating buffer"));
+               exit(1);
+       }
+       /* Write qcow2 data blocks */
+       for (blk = 0; blk < ext2fs_blocks_count(fs->super); blk++) {
+               if ((blk >= fs->super->s_first_data_block) &&
+                   ext2fs_test_block_bitmap2(meta_block_map, blk)) {
+                       retval = io_channel_read_blk64(fs->io, blk, 1, buf);
+                       if (retval) {
+                               com_err(program_name, retval,
+                                       _("error reading block %llu"), blk);
+                               continue;
+                       }
+                       if (scramble_block_map &&
+                           ext2fs_test_block_bitmap2(scramble_block_map, blk))
+                               scramble_dir_block(fs, blk, buf);
+                       if (check_zero_block(buf, fs->blocksize))
+                               continue;
+
+                       if (update_refcount(fd, img, offset, offset)) {
+                               /* Make space for another refcount block */
+                               offset += img->cluster_size;
+                               seek_set(fd, offset);
+                               /*
+                                * We have created the new refcount block, this
+                                * means that we need to refcount it as well.
+                                * So the previous update_refcount refcounted
+                                * the block itself and now we are going to
+                                * create refcount for data. New refcount
+                                * block should not be created!
+                                */
+                               if (update_refcount(fd, img, offset, offset)) {
+                                       fprintf(stderr, "%s",
+                                               _("Programming error: multiple "
+                                                 "sequential refcount blocks "
+                                                 "created!\n"));
+                                       exit(1);
+                               }
+                       }
+
+                       generic_write(fd, buf, fs->blocksize, blk);
+
+                       if (add_l2_item(img, blk, offset,
+                                       offset + img->cluster_size)) {
+                               offset += img->cluster_size;
+                               if (update_refcount(fd, img, offset,
+                                       offset + img->cluster_size)) {
+                                       offset += img->cluster_size;
+                                       if (update_refcount(fd, img, offset,
+                                                           offset)) {
+                                               fprintf(stderr, "%s",
+                       _("Programming error: multiple sequential refcount "
+                         "blocks created!\n"));
+                                               exit(1);
+                                       }
+                               }
+                               offset += img->cluster_size;
+                               seek_set(fd, offset);
+                               continue;
                        }
+
+                       offset += img->cluster_size;
                }
        }
-       if (sparse)
-               write_block(fd, zero_buf, sparse-1, 1, -1);
-       free(zero_buf);
-       free(buf);
+       update_refcount(fd, img, offset, offset);
+       flush_l2_cache(img);
+       sync_refcount(fd, img);
+
+       /* Write l1_table*/
+       seek_set(fd, img->l1_offset);
+       size = img->l1_size * sizeof(__u64);
+       generic_write(fd, (char *)img->l1_table, size, NO_BLK);
+
+       ext2fs_free_mem(&buf);
+       free_qcow2_image(img);
 }
 
-static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag)
+static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags)
 {
        struct process_block_struct     pb;
        struct ext2_inode               inode;
@@ -465,34 +1268,40 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag)
        errcode_t                       retval;
        char *                          block_buf;
 
-       retval = ext2fs_allocate_block_bitmap(fs, "in-use block map",
+       meta_blocks_count = 0;
+       retval = ext2fs_allocate_block_bitmap(fs, _("in-use block map"),
                                              &meta_block_map);
        if (retval) {
-               com_err(program_name, retval, "while allocating block bitmap");
+               com_err(program_name, retval, "%s",
+                       _("while allocating block bitmap"));
                exit(1);
        }
 
-       if (scramble_flag) {
+       if (flags & E2IMAGE_SCRAMBLE_FLAG) {
                retval = ext2fs_allocate_block_bitmap(fs, "scramble block map",
                                                      &scramble_block_map);
                if (retval) {
-                       com_err(program_name, retval,
-                               "while allocating scramble block bitmap");
+                       com_err(program_name, retval, "%s",
+                               _("while allocating scramble block bitmap"));
                        exit(1);
                }
        }
 
        mark_table_blocks(fs);
+       if (show_progress)
+               fprintf(stderr, "%s", _("Scanning inodes...\n"));
 
        retval = ext2fs_open_inode_scan(fs, 0, &scan);
        if (retval) {
-               com_err(program_name, retval, _("while opening inode scan"));
+               com_err(program_name, retval, "%s",
+                       _("while opening inode scan"));
                exit(1);
        }
 
-       block_buf = malloc(fs->blocksize * 3);
-       if (!block_buf) {
-               com_err(program_name, 0, "Can't allocate block buffer");
+       retval = ext2fs_get_mem(fs->blocksize * 3, &block_buf);
+       if (retval) {
+               com_err(program_name, 0, "%s",
+                       _("Can't allocate block buffer"));
                exit(1);
        }
 
@@ -503,7 +1312,7 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag)
                if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
                        continue;
                if (retval) {
-                       com_err(program_name, retval,
+                       com_err(program_name, retval, "%s",
                                _("while getting next inode"));
                        exit(1);
                }
@@ -511,11 +1320,12 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag)
                        break;
                if (!inode.i_links_count)
                        continue;
-               if (inode.i_file_acl) {
+               if (ext2fs_file_acl_block(fs, &inode)) {
                        ext2fs_mark_block_bitmap2(meta_block_map,
-                                                inode.i_file_acl);
+                                       ext2fs_file_acl_block(fs, &inode));
+                       meta_blocks_count++;
                }
-               if (!ext2fs_inode_has_valid_blocks(&inode))
+               if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
                        continue;
 
                stashed_ino = ino;
@@ -523,14 +1333,14 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag)
                pb.is_dir = LINUX_S_ISDIR(inode.i_mode);
                if (LINUX_S_ISDIR(inode.i_mode) ||
                    (LINUX_S_ISLNK(inode.i_mode) &&
-                    ext2fs_inode_has_valid_blocks(&inode)) ||
+                    ext2fs_inode_has_valid_blocks2(fs, &inode)) ||
                    ino == fs->super->s_journal_inum) {
-                       retval = ext2fs_block_iterate2(fs, ino,
+                       retval = ext2fs_block_iterate3(fs, ino,
                                        BLOCK_FLAG_READ_ONLY, block_buf,
                                        process_dir_block, &pb);
                        if (retval) {
                                com_err(program_name, retval,
-                                       "while iterating over inode %u",
+                                       _("while iterating over inode %u"),
                                        ino);
                                exit(1);
                        }
@@ -538,34 +1348,45 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag)
                        if ((inode.i_flags & EXT4_EXTENTS_FL) ||
                            inode.i_block[EXT2_IND_BLOCK] ||
                            inode.i_block[EXT2_DIND_BLOCK] ||
-                           inode.i_block[EXT2_TIND_BLOCK]) {
-                               retval = ext2fs_block_iterate2(fs,
+                           inode.i_block[EXT2_TIND_BLOCK] || all_data) {
+                               retval = ext2fs_block_iterate3(fs,
                                       ino, BLOCK_FLAG_READ_ONLY, block_buf,
                                       process_file_block, &pb);
                                if (retval) {
                                        com_err(program_name, retval,
-                                       "while iterating over inode %u", ino);
+                                       _("while iterating over inode %u"), ino);
                                        exit(1);
                                }
                        }
                }
        }
        use_inode_shortcuts(fs, 0);
-       output_meta_data_blocks(fs, fd);
-       free(block_buf);
+
+       if (type & E2IMAGE_QCOW2)
+               output_qcow2_meta_data_blocks(fs, fd);
+       else
+               output_meta_data_blocks(fs, fd, flags);
+
+       ext2fs_free_mem(&block_buf);
+       ext2fs_close_inode_scan(scan);
+       ext2fs_free_block_bitmap(meta_block_map);
+       if (type & E2IMAGE_SCRAMBLE_FLAG)
+               ext2fs_free_block_bitmap(scramble_block_map);
 }
 
-static void install_image(char *device, char *image_fn, int raw_flag)
+static void install_image(char *device, char *image_fn, int type)
 {
        errcode_t retval;
        ext2_filsys fs;
-       int open_flag = EXT2_FLAG_IMAGE_FILE;
+       int open_flag = EXT2_FLAG_IMAGE_FILE | EXT2_FLAG_64BITS |
+                       EXT2_FLAG_IGNORE_CSUM_ERRORS;
        int fd = 0;
        io_manager      io_ptr;
-       io_channel      io, image_io;
+       io_channel      io;
 
-       if (raw_flag) {
-               com_err(program_name, 0, "Raw images cannot be installed");
+       if (type) {
+               com_err(program_name, 0, "%s",
+                       _("Raw and qcow2 images cannot be installed"));
                exit(1);
        }
 
@@ -580,22 +1401,18 @@ static void install_image(char *device, char *image_fn, int raw_flag)
        retval = ext2fs_open (image_fn, open_flag, 0, 0,
                              io_ptr, &fs);
         if (retval) {
-               com_err (program_name, retval, _("while trying to open %s"),
-                        image_fn);
+               com_err(program_name, retval, _("while trying to open %s"),
+                       image_fn);
                exit(1);
        }
 
        retval = ext2fs_read_bitmaps (fs);
        if (retval) {
-               com_err(program_name, retval, "error reading bitmaps");
+               com_err(program_name, retval, "%s", _("error reading bitmaps"));
                exit(1);
        }
 
-#ifdef HAVE_OPEN64
-       fd = open64(image_fn, O_RDONLY);
-#else
-       fd = open(image_fn, O_RDONLY);
-#endif
+       fd = ext2fs_open_file(image_fn, O_RDONLY, 0);
        if (fd < 0) {
                perror(image_fn);
                exit(1);
@@ -603,27 +1420,33 @@ static void install_image(char *device, char *image_fn, int raw_flag)
 
        retval = io_ptr->open(device, IO_FLAG_RW, &io);
        if (retval) {
-               com_err(device, 0, "while opening device file");
+               com_err(device, 0, "%s", _("while opening device file"));
                exit(1);
        }
 
-       image_io = fs->io;
-
        ext2fs_rewrite_to_io(fs, io);
 
-       if (lseek(fd, fs->image_header->offset_inode, SEEK_SET) < 0) {
-               perror("lseek");
-               exit(1);
-       }
+       seek_set(fd, ext2fs_le32_to_cpu(fs->image_header->offset_inode));
 
        retval = ext2fs_image_inode_read(fs, fd, 0);
        if (retval) {
-               com_err(image_fn, 0, "while restoring the image table");
+               com_err(image_fn, 0, "%s",
+                       _("while restoring the image table"));
                exit(1);
        }
 
-       ext2fs_close (fs);
-       exit (0);
+       close(fd);
+       ext2fs_close_free(&fs);
+}
+
+static struct ext2_qcow2_hdr *check_qcow2_image(int *fd, char *name)
+{
+
+       *fd = ext2fs_open_file(name, O_RDONLY, 0600);
+       if (*fd < 0)
+               return NULL;
+
+       return qcow2_read_header(*fd);
 }
 
 int main (int argc, char ** argv)
@@ -631,78 +1454,234 @@ int main (int argc, char ** argv)
        int c;
        errcode_t retval;
        ext2_filsys fs;
-       char *image_fn;
-       int open_flag = 0;
-       int raw_flag = 0;
-       int install_flag = 0;
-       int scramble_flag = 0;
+       char *image_fn, offset_opt[64];
+       struct ext2_qcow2_hdr *header = NULL;
+       int open_flag = EXT2_FLAG_64BITS | EXT2_FLAG_IGNORE_CSUM_ERRORS;
+       int img_type = 0;
+       int flags = 0;
+       int mount_flags = 0;
+       int qcow2_fd = 0;
        int fd = 0;
+       int ret = 0;
+       int ignore_rw_mount = 0;
+       int check = 0;
+       struct stat st;
 
 #ifdef ENABLE_NLS
        setlocale(LC_MESSAGES, "");
        setlocale(LC_CTYPE, "");
        bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
        textdomain(NLS_CAT_NAME);
+       set_com_err_gettext(gettext);
 #endif
        fprintf (stderr, "e2image %s (%s)\n", E2FSPROGS_VERSION,
                 E2FSPROGS_DATE);
        if (argc && *argv)
                program_name = *argv;
        add_error_table(&et_ext2_error_table);
-       while ((c = getopt (argc, argv, "rsI")) != EOF)
+       while ((c = getopt(argc, argv, "nrsIQafo:O:pc")) != EOF)
                switch (c) {
+               case 'I':
+                       flags |= E2IMAGE_INSTALL_FLAG;
+                       break;
+               case 'Q':
+                       if (img_type)
+                               usage();
+                       img_type |= E2IMAGE_QCOW2;
+                       break;
                case 'r':
-                       raw_flag++;
+                       if (img_type)
+                               usage();
+                       img_type |= E2IMAGE_RAW;
                        break;
                case 's':
-                       scramble_flag++;
+                       flags |= E2IMAGE_SCRAMBLE_FLAG;
                        break;
-               case 'I':
-                       install_flag++;
+               case 'a':
+                       all_data = 1;
+                       break;
+               case 'f':
+                       ignore_rw_mount = 1;
+                       break;
+               case 'n':
+                       nop_flag = 1;
+                       break;
+               case 'o':
+                       source_offset = strtoull(optarg, NULL, 0);
+                       break;
+               case 'O':
+                       dest_offset = strtoull(optarg, NULL, 0);
+                       break;
+               case 'p':
+                       show_progress = 1;
+                       break;
+               case 'c':
+                       check = 1;
                        break;
                default:
                        usage();
                }
-       if (optind != argc - 2 )
+       if (optind == argc - 1 &&
+           (source_offset || dest_offset))
+                   move_mode = 1;
+       else if (optind != argc - 2 )
                usage();
+
+       if (all_data && !img_type) {
+               com_err(program_name, 0, "%s", _("-a option can only be used "
+                                                "with raw or QCOW2 images."));
+               exit(1);
+       }
+       if ((source_offset || dest_offset) && img_type != E2IMAGE_RAW) {
+               com_err(program_name, 0, "%s",
+                       _("Offsets are only allowed with raw images."));
+               exit(1);
+       }
+       if (move_mode && img_type != E2IMAGE_RAW) {
+               com_err(program_name, 0, "%s",
+                       _("Move mode is only allowed with raw images."));
+               exit(1);
+       }
+       if (move_mode && !all_data) {
+               com_err(program_name, 0, "%s",
+                       _("Move mode requires all data mode."));
+               exit(1);
+       }
        device_name = argv[optind];
-       image_fn = argv[optind+1];
+       if (move_mode)
+               image_fn = device_name;
+       else image_fn = argv[optind+1];
+
+       retval = ext2fs_check_if_mounted(device_name, &mount_flags);
+       if (retval) {
+               com_err(program_name, retval, "%s", _("checking if mounted"));
+               exit(1);
+       }
 
-       if (install_flag) {
-               install_image(device_name, image_fn, raw_flag);
+       if (img_type && !ignore_rw_mount &&
+           (mount_flags & EXT2_MF_MOUNTED) &&
+          !(mount_flags & EXT2_MF_READONLY)) {
+               fprintf(stderr, "%s", _("\nRunning e2image on a R/W mounted "
+                       "filesystem can result in an\n"
+                       "inconsistent image which will not be useful "
+                       "for debugging purposes.\n"
+                       "Use -f option if you really want to do that.\n"));
+               exit(1);
+       }
+
+       if (flags & E2IMAGE_INSTALL_FLAG) {
+               install_image(device_name, image_fn, img_type);
                exit (0);
        }
 
-       retval = ext2fs_open (device_name, open_flag, 0, 0,
+       if (img_type & E2IMAGE_RAW) {
+               header = check_qcow2_image(&qcow2_fd, device_name);
+               if (header) {
+                       flags |= E2IMAGE_IS_QCOW2_FLAG;
+                       goto skip_device;
+               }
+       }
+       sprintf(offset_opt, "offset=%llu", source_offset);
+       retval = ext2fs_open2(device_name, offset_opt, open_flag, 0, 0,
                              unix_io_manager, &fs);
         if (retval) {
                com_err (program_name, retval, _("while trying to open %s"),
                         device_name);
                fputs(_("Couldn't find valid filesystem superblock.\n"), stdout);
+               if (retval == EXT2_ET_BAD_MAGIC)
+                       check_plausibility(device_name, CHECK_FS_EXIST, NULL);
                exit(1);
        }
 
+skip_device:
        if (strcmp(image_fn, "-") == 0)
                fd = 1;
        else {
-#ifdef HAVE_OPEN64
-               fd = open64(image_fn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
-#else
-               fd = open(image_fn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
-#endif
+               int o_flags = O_CREAT|O_RDWR;
+
+               if (img_type != E2IMAGE_RAW)
+                       o_flags |= O_TRUNC;
+               if (access(image_fn, F_OK) != 0)
+                       flags |= E2IMAGE_CHECK_ZERO_FLAG;
+               fd = ext2fs_open_file(image_fn, o_flags, 0600);
                if (fd < 0) {
                        com_err(program_name, errno,
-                               _("while trying to open %s"), argv[optind+1]);
+                               _("while trying to open %s"), image_fn);
                        exit(1);
                }
        }
+       if (dest_offset)
+               seek_set(fd, dest_offset);
 
-       if (raw_flag)
-               write_raw_image_file(fs, fd, scramble_flag);
+       if ((img_type & E2IMAGE_QCOW2) && (fd == 1)) {
+               com_err(program_name, 0, "%s",
+                       _("QCOW2 image can not be written to the stdout!\n"));
+               exit(1);
+       }
+       if (fd != 1) {
+               if (fstat(fd, &st)) {
+                       com_err(program_name, 0, "%s",
+                               _("Can not stat output\n"));
+                       exit(1);
+               }
+               if (ext2fsP_is_disk_device(st.st_mode))
+                       output_is_blk = 1;
+       }
+       if (flags & E2IMAGE_IS_QCOW2_FLAG) {
+               ret = qcow2_write_raw_image(qcow2_fd, fd, header);
+               if (ret) {
+                       if (ret == -QCOW_COMPRESSED)
+                               fprintf(stderr, _("Image (%s) is compressed\n"),
+                                       image_fn);
+                       if (ret == -QCOW_ENCRYPTED)
+                               fprintf(stderr, _("Image (%s) is encrypted\n"),
+                                       image_fn);
+                       com_err(program_name, ret,
+                               _("while trying to convert qcow2 image"
+                                 " (%s) into raw image (%s)"),
+                               device_name, image_fn);
+               }
+               goto out;
+       }
+
+       if (check) {
+               if (img_type != E2IMAGE_RAW) {
+                       fprintf(stderr, "%s", _("The -c option only supported "
+                                               "in raw mode\n"));
+                       exit(1);
+               }
+               if (fd == 1) {
+                       fprintf(stderr, "%s", _("The -c option not supported "
+                                               "when writing to stdout\n"));
+                       exit(1);
+               }
+               retval = ext2fs_get_mem(fs->blocksize, &check_buf);
+               if (retval) {
+                       com_err(program_name, retval, "%s",
+                               _("while allocating check_buf"));
+                       exit(1);
+               }
+       }
+       if (show_progress && (img_type != E2IMAGE_RAW)) {
+               fprintf(stderr, "%s",
+                       _("The -p option only supported in raw mode\n"));
+               exit(1);
+       }
+       if (img_type)
+               write_raw_image_file(fs, fd, img_type, flags);
        else
                write_image_file(fs, fd);
 
-       ext2fs_close (fs);
+       ext2fs_close_free(&fs);
+       if (check)
+               printf(_("%d blocks already contained the data to be copied\n"),
+                      skipped_blocks);
+
+out:
+       if (header)
+               free(header);
+       if (qcow2_fd)
+               close(qcow2_fd);
        remove_error_table(&et_ext2_error_table);
-       exit (0);
+       return ret;
 }