+ 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 int 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;
+
+ /* 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 sequentialy.
+ */
+ 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);
+ }