qcow.c revision 271965
1/*-
2 * Copyright (c) 2014 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/usr.bin/mkimg/qcow.c 271965 2014-09-22 15:05:28Z marcel $");
29
30#include <sys/types.h>
31#include <sys/endian.h>
32#include <sys/errno.h>
33#include <stdint.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <unistd.h>
38
39#include "image.h"
40#include "format.h"
41#include "mkimg.h"
42
43#undef	QCOW_SUPPORT_QCOW2
44
45/* Default cluster sizes. */
46#define	QCOW1_CLSTR_LOG2SZ	12	/* 4KB */
47#define	QCOW2_CLSTR_LOG2SZ	16	/* 64KB */
48
49struct qcow_header {
50	uint32_t	magic;
51#define	QCOW_MAGIC		0x514649fb
52	uint32_t	version;
53#define	QCOW_VERSION_1		1
54#define	QCOW_VERSION_2		2
55	uint64_t	path_offset;
56	uint32_t	path_length;
57	uint32_t	clstr_log2sz;	/* v2 only */
58	uint64_t	disk_size;
59	union {
60		struct {
61			uint8_t		clstr_log2sz;
62			uint8_t		l2_log2sz;
63			uint16_t	_pad;
64			uint32_t	encryption;
65			uint64_t	l1_offset;
66		} v1;
67		struct {
68			uint32_t	encryption;
69			uint32_t	l1_entries;
70			uint64_t	l1_offset;
71			uint64_t	refcnt_offset;
72			uint32_t	refcnt_entries;
73			uint32_t	snapshot_count;
74			uint64_t	snapshot_offset;
75		} v2;
76	} u;
77};
78
79static u_int clstr_log2sz;
80
81static uint64_t
82round_clstr(uint64_t ofs)
83{
84	uint64_t clstrsz;
85
86	clstrsz = 1UL << clstr_log2sz;
87	return ((ofs + clstrsz - 1) & ~(clstrsz - 1));
88}
89
90static int
91qcow_resize(lba_t imgsz, u_int version)
92{
93	uint64_t clstrsz, imagesz;
94
95	switch (version) {
96	case QCOW_VERSION_1:
97		clstr_log2sz = QCOW1_CLSTR_LOG2SZ;
98		break;
99	case QCOW_VERSION_2:
100		clstr_log2sz = QCOW2_CLSTR_LOG2SZ;
101		break;
102	default:
103		return (EDOOFUS);
104	}
105
106	clstrsz = 1UL << clstr_log2sz;
107	imagesz = round_clstr(imgsz * secsz);
108
109	if (verbose)
110		fprintf(stderr, "QCOW: image size = %ju, cluster size = %ju\n",
111		    (uintmax_t)imagesz, (uintmax_t)clstrsz);
112
113	return (image_set_size(imagesz / secsz));
114}
115
116static int
117qcow1_resize(lba_t imgsz)
118{
119
120	return (qcow_resize(imgsz, QCOW_VERSION_1));
121}
122
123#ifdef QCOW_SUPPORT_QCOW2
124static int
125qcow2_resize(lba_t imgsz)
126{
127
128	return (qcow_resize(imgsz, QCOW_VERSION_2));
129}
130#endif
131
132static int
133qcow_write(int fd, u_int version)
134{
135	struct qcow_header *hdr;
136	uint64_t *l1tbl, *l2tbl;
137	uint16_t *rctbl;
138	uint64_t n, clstrsz, imagesz, nclstrs;
139	uint64_t l1ofs, l2ofs, ofs, rcofs;
140	lba_t blk, blkofs, blkcnt, imgsz;
141	u_int l1idx, l2idx, l2clstrs;
142	int error;
143
144	if (clstr_log2sz == 0)
145		return (EDOOFUS);
146
147	clstrsz = 1UL << clstr_log2sz;
148	blkcnt = clstrsz / secsz;
149	imgsz = image_get_size();
150	imagesz = imgsz * secsz;
151	nclstrs = imagesz >> clstr_log2sz;
152	l2clstrs = (nclstrs * 8 + clstrsz - 1) > clstr_log2sz;
153
154	l1ofs = clstrsz;
155	rcofs = round_clstr(l1ofs + l2clstrs * 8);
156
157	hdr = calloc(1, clstrsz);
158	if (hdr == NULL)
159		return (errno);
160
161	be32enc(&hdr->magic, QCOW_MAGIC);
162	be32enc(&hdr->version, version);
163	be64enc(&hdr->disk_size, imagesz);
164	switch (version) {
165	case QCOW_VERSION_1:
166		l2ofs = rcofs;	/* No reference counting. */
167		hdr->u.v1.clstr_log2sz = clstr_log2sz;
168		hdr->u.v1.l2_log2sz = clstr_log2sz - 3;
169		be64enc(&hdr->u.v1.l1_offset, l1ofs);
170		break;
171	case QCOW_VERSION_2:
172		l2ofs = round_clstr(rcofs + (nclstrs + l2clstrs) * 2);
173		be32enc(&hdr->clstr_log2sz, clstr_log2sz);
174		be32enc(&hdr->u.v2.l1_entries, l2clstrs);
175		be64enc(&hdr->u.v2.l1_offset, l1ofs);
176		be64enc(&hdr->u.v2.refcnt_offset, rcofs);
177		be32enc(&hdr->u.v2.refcnt_entries, l2clstrs);
178		break;
179	default:
180		return (EDOOFUS);
181	}
182
183	l2tbl = l1tbl = NULL;
184	rctbl = NULL;
185
186	l1tbl = calloc(1, (size_t)(rcofs - l1ofs));
187	if (l1tbl == NULL) {
188		error = ENOMEM;
189		goto out;
190	}
191	if (l2ofs != rcofs) {
192		rctbl = calloc(1, (size_t)(l2ofs - rcofs));
193		if (rctbl == NULL) {
194			error = ENOMEM;
195			goto out;
196		}
197	}
198
199	ofs = l2ofs;
200	for (n = 0; n < nclstrs; n++) {
201		l1idx = n >> (clstr_log2sz - 3);
202		if (l1tbl[l1idx] != 0UL)
203			continue;
204		blk = n * blkcnt;
205		if (image_data(blk, blkcnt)) {
206			be64enc(l1tbl + l1idx, ofs);
207			ofs += clstrsz;
208		}
209	}
210
211	error = 0;
212	if (!error && sparse_write(fd, hdr, clstrsz) < 0)
213		error = errno;
214	if (!error && sparse_write(fd, l1tbl, (size_t)(rcofs - l1ofs)) < 0)
215		error = errno;
216	/* XXX refcnt table. */
217	if (error)
218		goto out;
219
220	free(hdr);
221	hdr = NULL;
222	if (rctbl != NULL) {
223		free(rctbl);
224		rctbl = NULL;
225	}
226
227	l2tbl = malloc(clstrsz);
228	if (l2tbl == NULL) {
229		error = ENOMEM;
230		goto out;
231	}
232
233	for (l1idx = 0; l1idx < l2clstrs; l1idx++) {
234		if (l1tbl[l1idx] == 0)
235			continue;
236		memset(l2tbl, 0, clstrsz);
237		blkofs = (lba_t)l1idx * (clstrsz * (clstrsz >> 3));
238		for (l2idx = 0; l2idx < (clstrsz >> 3); l2idx++) {
239			blk = blkofs + (lba_t)l2idx * blkcnt;
240			if (blk >= imgsz)
241				break;
242			if (image_data(blk, blkcnt)) {
243				be64enc(l2tbl + l2idx, ofs);
244				ofs += clstrsz;
245			}
246		}
247		if (sparse_write(fd, l2tbl, clstrsz) < 0) {
248			error = errno;
249			goto out;
250		}
251	}
252
253	free(l2tbl);
254	l2tbl = NULL;
255	free(l1tbl);
256	l1tbl = NULL;
257
258	error = 0;
259	for (n = 0; n < nclstrs; n++) {
260		blk = n * blkcnt;
261		if (image_data(blk, blkcnt)) {
262			error = image_copyout_region(fd, blk, blkcnt);
263			if (error)
264				break;
265		}
266	}
267	if (!error)
268		error = image_copyout_done(fd);
269
270 out:
271	if (l2tbl != NULL)
272		free(l2tbl);
273	if (rctbl != NULL)
274		free(rctbl);
275	if (l1tbl != NULL)
276		free(l1tbl);
277	if (hdr != NULL)
278		free(hdr);
279	return (error);
280}
281
282static int
283qcow1_write(int fd)
284{
285
286	return (qcow_write(fd, QCOW_VERSION_1));
287}
288
289#ifdef QCOW_SUPPORT_QCOW2
290static int
291qcow2_write(int fd)
292{
293
294	return (qcow_write(fd, QCOW_VERSION_2));
295}
296#endif
297
298static struct mkimg_format qcow1_format = {
299	.name = "qcow",
300	.description = "QEMU Copy-On-Write, version 1",
301	.resize = qcow1_resize,
302	.write = qcow1_write,
303};
304FORMAT_DEFINE(qcow1_format);
305
306#ifdef QCOW_SUPPORT_QCOW2
307static struct mkimg_format qcow2_format = {
308	.name = "qcow2",
309	.description = "QEMU Copy-On-Write, version 2",
310	.resize = qcow2_resize,
311	.write = qcow2_write,
312};
313FORMAT_DEFINE(qcow2_format);
314#endif
315