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