qcow2.c revision 6a26b38ac46a0489bd477832dcaebd276c2f489b
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@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#define _LARGEFILE_SOURCE
27#define _LARGEFILE64_SOURCE
28
29#include "config.h"
30#include <fcntl.h>
31#include <grp.h>
32#include <pwd.h>
33#include <stdio.h>
34#ifdef HAVE_STDLIB_H
35#include <stdlib.h>
36#endif
37#include <string.h>
38#include <time.h>
39#include <unistd.h>
40#include <fcntl.h>
41#include <errno.h>
42#include <sys/stat.h>
43#include <sys/types.h>
44#include <assert.h>
45
46#include "ext2fs/ext2fs.h"
47#include "qcow2.h"
48
49/* Functions for converting qcow2 image into raw image */
50
51struct ext2_qcow2_hdr *qcow2_read_header(int fd)
52{
53	void *buffer = NULL;
54	struct ext2_qcow2_hdr *hdr = NULL;
55	size_t size;
56	errcode_t ret;
57
58	ret = ext2fs_get_mem(sizeof(struct ext2_qcow2_hdr), &buffer);
59	if (ret)
60		return NULL;
61	memset(buffer, 0, sizeof(struct ext2_qcow2_hdr));
62
63	if (ext2fs_llseek(fd, 0, SEEK_SET < 0)) {
64		ext2fs_free_mem(&buffer);
65		return NULL;
66	}
67
68	size = read(fd, buffer, sizeof(struct ext2_qcow2_hdr));
69	if (size != sizeof(struct ext2_qcow2_hdr)) {
70		ext2fs_free_mem(&buffer);
71		return NULL;
72	}
73
74	hdr = (struct ext2_qcow2_hdr *)(buffer);
75
76	if ((ext2fs_be32_to_cpu(hdr->magic) != QCOW_MAGIC) ||
77	    (ext2fs_be32_to_cpu(hdr->version) != 2)) {
78		ext2fs_free_mem(&hdr);
79		return NULL;
80	}
81
82	return hdr;
83}
84
85static int qcow2_read_l1_table(struct ext2_qcow2_image *img)
86{
87	int fd = img->fd;
88	size_t size, l1_size = img->l1_size * sizeof(blk64_t);
89	blk64_t *table;
90	errcode_t ret;
91
92	ret = ext2fs_get_memzero(l1_size, &table);
93	if (ret)
94		return ret;
95
96	if (ext2fs_llseek(fd, img->l1_offset, SEEK_SET) < 0) {
97		ext2fs_free_mem(&table);
98		return errno;
99	}
100
101	size = read(fd, table, l1_size);
102	if (size != l1_size) {
103		ext2fs_free_mem(&table);
104		return errno;
105	}
106
107	img->l1_table = table;
108
109	return 0;
110}
111
112static int qcow2_read_l2_table(struct ext2_qcow2_image *img,
113			       ext2_off64_t offset, blk64_t **l2_table)
114{
115	int fd = img->fd;
116	size_t size;
117
118	assert(*l2_table);
119
120	if (ext2fs_llseek(fd, offset, SEEK_SET) < 0)
121		return errno;
122
123	size = read(fd, *l2_table, img->cluster_size);
124	if (size != img->cluster_size)
125		return errno;
126
127	return 0;
128}
129
130static int qcow2_copy_data(int fdin, int fdout, ext2_off64_t off_in,
131			   ext2_off64_t off_out, void *buf, size_t count)
132{
133	size_t size;
134
135	assert(buf);
136
137	if (ext2fs_llseek(fdout, off_out, SEEK_SET) < 0)
138		return errno;
139
140	if (ext2fs_llseek(fdin, off_in, SEEK_SET) < 0)
141		return errno;
142
143	size = read(fdin, buf, count);
144	if (size != count)
145		return errno;
146
147	size = write(fdout, buf, count);
148	if (size != count)
149		return errno;
150
151	return 0;
152}
153
154
155int qcow2_write_raw_image(int qcow2_fd, int raw_fd,
156			      struct ext2_qcow2_hdr *hdr)
157{
158	struct ext2_qcow2_image img;
159	errcode_t ret = 0;
160	unsigned int l1_index, l2_index;
161	ext2_off64_t offset;
162	blk64_t *l1_table, *l2_table = NULL;
163	void *copy_buf = NULL;
164	size_t size;
165
166	if (hdr->crypt_method)
167		return -QCOW_ENCRYPTED;
168
169	img.fd = qcow2_fd;
170	img.hdr = hdr;
171	img.l2_cache = NULL;
172	img.l1_table = NULL;
173	img.cluster_bits = ext2fs_be32_to_cpu(hdr->cluster_bits);
174	img.cluster_size = 1 << img.cluster_bits;
175	img.l1_size = ext2fs_be32_to_cpu(hdr->l1_size);
176	img.l1_offset = ext2fs_be64_to_cpu(hdr->l1_table_offset);
177	img.l2_size = 1 << (img.cluster_bits - 3);
178	img.image_size = ext2fs_be64_to_cpu(hdr->size);
179
180
181	ret = ext2fs_get_memzero(img.cluster_size, &l2_table);
182	if (ret)
183		goto out;
184
185	ret = ext2fs_get_memzero(1 << img.cluster_bits, &copy_buf);
186	if (ret)
187		goto out;
188
189	if (ext2fs_llseek(raw_fd, 0, SEEK_SET) < 0) {
190		ret = errno;
191		goto out;
192	}
193
194	ret = qcow2_read_l1_table(&img);
195	if (ret)
196		goto out;
197
198	l1_table = img.l1_table;
199	/* Walk through l1 table */
200	for (l1_index = 0; l1_index < img.l1_size; l1_index++) {
201		ext2_off64_t off_out;
202
203		offset = ext2fs_be64_to_cpu(l1_table[l1_index]) &
204			 ~QCOW_OFLAG_COPIED;
205
206		if ((offset > img.image_size) ||
207		    (offset <= 0))
208			continue;
209
210		if (offset & QCOW_OFLAG_COMPRESSED) {
211			ret = -QCOW_COMPRESSED;
212			goto out;
213		}
214
215		ret = qcow2_read_l2_table(&img, offset, &l2_table);
216		if (ret)
217			break;
218
219		/* Walk through l2 table and copy data blocks into raw image */
220		for (l2_index = 0; l2_index < img.l2_size; l2_index++) {
221			offset = ext2fs_be64_to_cpu(l2_table[l2_index]) &
222				 ~QCOW_OFLAG_COPIED;
223
224			if (offset == 0)
225				continue;
226
227			off_out = (l1_index * img.l2_size) +
228				  l2_index;
229			off_out <<= img.cluster_bits;
230			ret = qcow2_copy_data(qcow2_fd, raw_fd, offset,
231					off_out, copy_buf, img.cluster_size);
232			if (ret)
233				goto out;
234		}
235	}
236
237	/* Resize the output image to the filesystem size */
238	if (ext2fs_llseek(raw_fd, img.image_size - 1, SEEK_SET) < 0)
239		return errno;
240
241	((char *)copy_buf)[0] = 0;
242	size = write(raw_fd, copy_buf, 1);
243	if (size != 1) {
244		ret = errno;
245		goto out;
246	}
247
248out:
249	if (copy_buf)
250		ext2fs_free_mem(&copy_buf);
251	if (img.l1_table)
252		ext2fs_free_mem(&img.l1_table);
253	if (l2_table)
254		ext2fs_free_mem(&l2_table);
255	return ret;
256}
257