Home | History | Annotate | Download | only in ext2fs
      1 /*
      2  * qcow2.c --- Functions to generate qcow2 formatted disk images.  This
      3  * format is used originally by QEMU for virtual machines, and stores the
      4  * filesystem data on disk in a packed format to avoid creating sparse
      5  * image files that need lots of seeking to read and write.
      6  *
      7  * The qcow2 format supports zlib compression, but that is not yet
      8  * implemented.
      9  *
     10  * It is possible to directly mount a qcow2 image using qemu-nbd:
     11  *
     12  * [root]# modprobe nbd max_part=63
     13  * [root]# qemu-nbd -c /dev/nbd0 image.img
     14  * [root]# mount /dev/nbd0p1 /mnt/qemu
     15  *
     16  * Format details at http://people.gnome.org/~markmc/qcow-image-format.html
     17  *
     18  * Copyright (C) 2010 Red Hat, Inc., Lukas Czerner <lczerner (at) redhat.com>
     19  *
     20  * %Begin-Header%
     21  * This file may be redistributed under the terms of the GNU Public
     22  * License.
     23  * %End-Header%
     24  */
     25 
     26 #ifndef _LARGEFILE_SOURCE
     27 #define _LARGEFILE_SOURCE
     28 #endif
     29 #ifndef _LARGEFILE64_SOURCE
     30 #define _LARGEFILE64_SOURCE
     31 #endif
     32 
     33 #include "config.h"
     34 #include <fcntl.h>
     35 #include <grp.h>
     36 #include <pwd.h>
     37 #include <stdio.h>
     38 #ifdef HAVE_STDLIB_H
     39 #include <stdlib.h>
     40 #endif
     41 #include <string.h>
     42 #include <time.h>
     43 #include <unistd.h>
     44 #include <fcntl.h>
     45 #include <errno.h>
     46 #include <sys/stat.h>
     47 #include <sys/types.h>
     48 #include <assert.h>
     49 
     50 #include "ext2fs/ext2fs.h"
     51 #include "qcow2.h"
     52 
     53 /* Functions for converting qcow2 image into raw image */
     54 
     55 struct ext2_qcow2_hdr *qcow2_read_header(int fd)
     56 {
     57 	void *buffer = NULL;
     58 	struct ext2_qcow2_hdr *hdr = NULL;
     59 	size_t size;
     60 	errcode_t ret;
     61 
     62 	ret = ext2fs_get_mem(sizeof(struct ext2_qcow2_hdr), &buffer);
     63 	if (ret)
     64 		return NULL;
     65 	memset(buffer, 0, sizeof(struct ext2_qcow2_hdr));
     66 
     67 	if (ext2fs_llseek(fd, 0, SEEK_SET < 0)) {
     68 		ext2fs_free_mem(&buffer);
     69 		return NULL;
     70 	}
     71 
     72 	size = read(fd, buffer, sizeof(struct ext2_qcow2_hdr));
     73 	if (size != sizeof(struct ext2_qcow2_hdr)) {
     74 		ext2fs_free_mem(&buffer);
     75 		return NULL;
     76 	}
     77 
     78 	hdr = (struct ext2_qcow2_hdr *)(buffer);
     79 
     80 	if ((ext2fs_be32_to_cpu(hdr->magic) != QCOW_MAGIC) ||
     81 	    (ext2fs_be32_to_cpu(hdr->version) != 2)) {
     82 		ext2fs_free_mem(&hdr);
     83 		return NULL;
     84 	}
     85 
     86 	return hdr;
     87 }
     88 
     89 static int qcow2_read_l1_table(struct ext2_qcow2_image *img)
     90 {
     91 	int fd = img->fd;
     92 	size_t size, l1_size = img->l1_size * sizeof(blk64_t);
     93 	blk64_t *table;
     94 	errcode_t ret;
     95 
     96 	ret = ext2fs_get_memzero(l1_size, &table);
     97 	if (ret)
     98 		return ret;
     99 
    100 	if (ext2fs_llseek(fd, img->l1_offset, SEEK_SET) < 0) {
    101 		ext2fs_free_mem(&table);
    102 		return errno;
    103 	}
    104 
    105 	size = read(fd, table, l1_size);
    106 	if (size != l1_size) {
    107 		ext2fs_free_mem(&table);
    108 		return errno;
    109 	}
    110 
    111 	img->l1_table = table;
    112 
    113 	return 0;
    114 }
    115 
    116 static int qcow2_read_l2_table(struct ext2_qcow2_image *img,
    117 			       ext2_off64_t offset, blk64_t **l2_table)
    118 {
    119 	int fd = img->fd;
    120 	size_t size;
    121 
    122 	assert(*l2_table);
    123 
    124 	if (ext2fs_llseek(fd, offset, SEEK_SET) < 0)
    125 		return errno;
    126 
    127 	size = read(fd, *l2_table, img->cluster_size);
    128 	if (size != img->cluster_size)
    129 		return errno;
    130 
    131 	return 0;
    132 }
    133 
    134 static int qcow2_copy_data(int fdin, int fdout, ext2_off64_t off_in,
    135 			   ext2_off64_t off_out, void *buf, size_t count)
    136 {
    137 	size_t size;
    138 
    139 	assert(buf);
    140 
    141 	if (ext2fs_llseek(fdout, off_out, SEEK_SET) < 0)
    142 		return errno;
    143 
    144 	if (ext2fs_llseek(fdin, off_in, SEEK_SET) < 0)
    145 		return errno;
    146 
    147 	size = read(fdin, buf, count);
    148 	if (size != count)
    149 		return errno;
    150 
    151 	size = write(fdout, buf, count);
    152 	if (size != count)
    153 		return errno;
    154 
    155 	return 0;
    156 }
    157 
    158 
    159 int qcow2_write_raw_image(int qcow2_fd, int raw_fd,
    160 			      struct ext2_qcow2_hdr *hdr)
    161 {
    162 	struct ext2_qcow2_image img;
    163 	errcode_t ret = 0;
    164 	unsigned int l1_index, l2_index;
    165 	ext2_off64_t offset;
    166 	blk64_t *l1_table, *l2_table = NULL;
    167 	void *copy_buf = NULL;
    168 	size_t size;
    169 
    170 	if (hdr->crypt_method)
    171 		return -QCOW_ENCRYPTED;
    172 
    173 	img.fd = qcow2_fd;
    174 	img.hdr = hdr;
    175 	img.l2_cache = NULL;
    176 	img.l1_table = NULL;
    177 	img.cluster_bits = ext2fs_be32_to_cpu(hdr->cluster_bits);
    178 	img.cluster_size = 1 << img.cluster_bits;
    179 	img.l1_size = ext2fs_be32_to_cpu(hdr->l1_size);
    180 	img.l1_offset = ext2fs_be64_to_cpu(hdr->l1_table_offset);
    181 	img.l2_size = 1 << (img.cluster_bits - 3);
    182 	img.image_size = ext2fs_be64_to_cpu(hdr->size);
    183 
    184 
    185 	ret = ext2fs_get_memzero(img.cluster_size, &l2_table);
    186 	if (ret)
    187 		goto out;
    188 
    189 	ret = ext2fs_get_memzero(1 << img.cluster_bits, &copy_buf);
    190 	if (ret)
    191 		goto out;
    192 
    193 	if (ext2fs_llseek(raw_fd, 0, SEEK_SET) < 0) {
    194 		ret = errno;
    195 		goto out;
    196 	}
    197 
    198 	ret = qcow2_read_l1_table(&img);
    199 	if (ret)
    200 		goto out;
    201 
    202 	l1_table = img.l1_table;
    203 	/* Walk through l1 table */
    204 	for (l1_index = 0; l1_index < img.l1_size; l1_index++) {
    205 		ext2_off64_t off_out;
    206 
    207 		offset = ext2fs_be64_to_cpu(l1_table[l1_index]) &
    208 			 ~QCOW_OFLAG_COPIED;
    209 
    210 		if ((offset > img.image_size) ||
    211 		    (offset <= 0))
    212 			continue;
    213 
    214 		if (offset & QCOW_OFLAG_COMPRESSED) {
    215 			ret = -QCOW_COMPRESSED;
    216 			goto out;
    217 		}
    218 
    219 		ret = qcow2_read_l2_table(&img, offset, &l2_table);
    220 		if (ret)
    221 			break;
    222 
    223 		/* Walk through l2 table and copy data blocks into raw image */
    224 		for (l2_index = 0; l2_index < img.l2_size; l2_index++) {
    225 			offset = ext2fs_be64_to_cpu(l2_table[l2_index]) &
    226 				 ~QCOW_OFLAG_COPIED;
    227 
    228 			if (offset == 0)
    229 				continue;
    230 
    231 			off_out = (l1_index * img.l2_size) +
    232 				  l2_index;
    233 			off_out <<= img.cluster_bits;
    234 			ret = qcow2_copy_data(qcow2_fd, raw_fd, offset,
    235 					off_out, copy_buf, img.cluster_size);
    236 			if (ret)
    237 				goto out;
    238 		}
    239 	}
    240 
    241 	/* Resize the output image to the filesystem size */
    242 	if (ext2fs_llseek(raw_fd, img.image_size - 1, SEEK_SET) < 0) {
    243 		ret = errno;
    244 		goto out;
    245 	}
    246 
    247 	((char *)copy_buf)[0] = 0;
    248 	size = write(raw_fd, copy_buf, 1);
    249 	if (size != 1) {
    250 		ret = errno;
    251 		goto out;
    252 	}
    253 
    254 out:
    255 	if (copy_buf)
    256 		ext2fs_free_mem(&copy_buf);
    257 	if (img.l1_table)
    258 		ext2fs_free_mem(&img.l1_table);
    259 	if (l2_table)
    260 		ext2fs_free_mem(&l2_table);
    261 	return ret;
    262 }
    263