qcow.c revision 271965
1271965Smarcel/*-
2271965Smarcel * Copyright (c) 2014 Marcel Moolenaar
3271965Smarcel * All rights reserved.
4271965Smarcel *
5271965Smarcel * Redistribution and use in source and binary forms, with or without
6271965Smarcel * modification, are permitted provided that the following conditions
7271965Smarcel * are met:
8271965Smarcel * 1. Redistributions of source code must retain the above copyright
9271965Smarcel *    notice, this list of conditions and the following disclaimer.
10271965Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11271965Smarcel *    notice, this list of conditions and the following disclaimer in the
12271965Smarcel *    documentation and/or other materials provided with the distribution.
13271965Smarcel *
14271965Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15271965Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16271965Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17271965Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18271965Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19271965Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20271965Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21271965Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22271965Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23271965Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24271965Smarcel * SUCH DAMAGE.
25271965Smarcel */
26271965Smarcel
27271965Smarcel#include <sys/cdefs.h>
28271965Smarcel__FBSDID("$FreeBSD: head/usr.bin/mkimg/qcow.c 271965 2014-09-22 15:05:28Z marcel $");
29271965Smarcel
30271965Smarcel#include <sys/types.h>
31271965Smarcel#include <sys/endian.h>
32271965Smarcel#include <sys/errno.h>
33271965Smarcel#include <stdint.h>
34271965Smarcel#include <stdio.h>
35271965Smarcel#include <stdlib.h>
36271965Smarcel#include <string.h>
37271965Smarcel#include <unistd.h>
38271965Smarcel
39271965Smarcel#include "image.h"
40271965Smarcel#include "format.h"
41271965Smarcel#include "mkimg.h"
42271965Smarcel
43271965Smarcel#undef	QCOW_SUPPORT_QCOW2
44271965Smarcel
45271965Smarcel/* Default cluster sizes. */
46271965Smarcel#define	QCOW1_CLSTR_LOG2SZ	12	/* 4KB */
47271965Smarcel#define	QCOW2_CLSTR_LOG2SZ	16	/* 64KB */
48271965Smarcel
49271965Smarcelstruct qcow_header {
50271965Smarcel	uint32_t	magic;
51271965Smarcel#define	QCOW_MAGIC		0x514649fb
52271965Smarcel	uint32_t	version;
53271965Smarcel#define	QCOW_VERSION_1		1
54271965Smarcel#define	QCOW_VERSION_2		2
55271965Smarcel	uint64_t	path_offset;
56271965Smarcel	uint32_t	path_length;
57271965Smarcel	uint32_t	clstr_log2sz;	/* v2 only */
58271965Smarcel	uint64_t	disk_size;
59271965Smarcel	union {
60271965Smarcel		struct {
61271965Smarcel			uint8_t		clstr_log2sz;
62271965Smarcel			uint8_t		l2_log2sz;
63271965Smarcel			uint16_t	_pad;
64271965Smarcel			uint32_t	encryption;
65271965Smarcel			uint64_t	l1_offset;
66271965Smarcel		} v1;
67271965Smarcel		struct {
68271965Smarcel			uint32_t	encryption;
69271965Smarcel			uint32_t	l1_entries;
70271965Smarcel			uint64_t	l1_offset;
71271965Smarcel			uint64_t	refcnt_offset;
72271965Smarcel			uint32_t	refcnt_entries;
73271965Smarcel			uint32_t	snapshot_count;
74271965Smarcel			uint64_t	snapshot_offset;
75271965Smarcel		} v2;
76271965Smarcel	} u;
77271965Smarcel};
78271965Smarcel
79271965Smarcelstatic u_int clstr_log2sz;
80271965Smarcel
81271965Smarcelstatic uint64_t
82271965Smarcelround_clstr(uint64_t ofs)
83271965Smarcel{
84271965Smarcel	uint64_t clstrsz;
85271965Smarcel
86271965Smarcel	clstrsz = 1UL << clstr_log2sz;
87271965Smarcel	return ((ofs + clstrsz - 1) & ~(clstrsz - 1));
88271965Smarcel}
89271965Smarcel
90271965Smarcelstatic int
91271965Smarcelqcow_resize(lba_t imgsz, u_int version)
92271965Smarcel{
93271965Smarcel	uint64_t clstrsz, imagesz;
94271965Smarcel
95271965Smarcel	switch (version) {
96271965Smarcel	case QCOW_VERSION_1:
97271965Smarcel		clstr_log2sz = QCOW1_CLSTR_LOG2SZ;
98271965Smarcel		break;
99271965Smarcel	case QCOW_VERSION_2:
100271965Smarcel		clstr_log2sz = QCOW2_CLSTR_LOG2SZ;
101271965Smarcel		break;
102271965Smarcel	default:
103271965Smarcel		return (EDOOFUS);
104271965Smarcel	}
105271965Smarcel
106271965Smarcel	clstrsz = 1UL << clstr_log2sz;
107271965Smarcel	imagesz = round_clstr(imgsz * secsz);
108271965Smarcel
109271965Smarcel	if (verbose)
110271965Smarcel		fprintf(stderr, "QCOW: image size = %ju, cluster size = %ju\n",
111271965Smarcel		    (uintmax_t)imagesz, (uintmax_t)clstrsz);
112271965Smarcel
113271965Smarcel	return (image_set_size(imagesz / secsz));
114271965Smarcel}
115271965Smarcel
116271965Smarcelstatic int
117271965Smarcelqcow1_resize(lba_t imgsz)
118271965Smarcel{
119271965Smarcel
120271965Smarcel	return (qcow_resize(imgsz, QCOW_VERSION_1));
121271965Smarcel}
122271965Smarcel
123271965Smarcel#ifdef QCOW_SUPPORT_QCOW2
124271965Smarcelstatic int
125271965Smarcelqcow2_resize(lba_t imgsz)
126271965Smarcel{
127271965Smarcel
128271965Smarcel	return (qcow_resize(imgsz, QCOW_VERSION_2));
129271965Smarcel}
130271965Smarcel#endif
131271965Smarcel
132271965Smarcelstatic int
133271965Smarcelqcow_write(int fd, u_int version)
134271965Smarcel{
135271965Smarcel	struct qcow_header *hdr;
136271965Smarcel	uint64_t *l1tbl, *l2tbl;
137271965Smarcel	uint16_t *rctbl;
138271965Smarcel	uint64_t n, clstrsz, imagesz, nclstrs;
139271965Smarcel	uint64_t l1ofs, l2ofs, ofs, rcofs;
140271965Smarcel	lba_t blk, blkofs, blkcnt, imgsz;
141271965Smarcel	u_int l1idx, l2idx, l2clstrs;
142271965Smarcel	int error;
143271965Smarcel
144271965Smarcel	if (clstr_log2sz == 0)
145271965Smarcel		return (EDOOFUS);
146271965Smarcel
147271965Smarcel	clstrsz = 1UL << clstr_log2sz;
148271965Smarcel	blkcnt = clstrsz / secsz;
149271965Smarcel	imgsz = image_get_size();
150271965Smarcel	imagesz = imgsz * secsz;
151271965Smarcel	nclstrs = imagesz >> clstr_log2sz;
152271965Smarcel	l2clstrs = (nclstrs * 8 + clstrsz - 1) > clstr_log2sz;
153271965Smarcel
154271965Smarcel	l1ofs = clstrsz;
155271965Smarcel	rcofs = round_clstr(l1ofs + l2clstrs * 8);
156271965Smarcel
157271965Smarcel	hdr = calloc(1, clstrsz);
158271965Smarcel	if (hdr == NULL)
159271965Smarcel		return (errno);
160271965Smarcel
161271965Smarcel	be32enc(&hdr->magic, QCOW_MAGIC);
162271965Smarcel	be32enc(&hdr->version, version);
163271965Smarcel	be64enc(&hdr->disk_size, imagesz);
164271965Smarcel	switch (version) {
165271965Smarcel	case QCOW_VERSION_1:
166271965Smarcel		l2ofs = rcofs;	/* No reference counting. */
167271965Smarcel		hdr->u.v1.clstr_log2sz = clstr_log2sz;
168271965Smarcel		hdr->u.v1.l2_log2sz = clstr_log2sz - 3;
169271965Smarcel		be64enc(&hdr->u.v1.l1_offset, l1ofs);
170271965Smarcel		break;
171271965Smarcel	case QCOW_VERSION_2:
172271965Smarcel		l2ofs = round_clstr(rcofs + (nclstrs + l2clstrs) * 2);
173271965Smarcel		be32enc(&hdr->clstr_log2sz, clstr_log2sz);
174271965Smarcel		be32enc(&hdr->u.v2.l1_entries, l2clstrs);
175271965Smarcel		be64enc(&hdr->u.v2.l1_offset, l1ofs);
176271965Smarcel		be64enc(&hdr->u.v2.refcnt_offset, rcofs);
177271965Smarcel		be32enc(&hdr->u.v2.refcnt_entries, l2clstrs);
178271965Smarcel		break;
179271965Smarcel	default:
180271965Smarcel		return (EDOOFUS);
181271965Smarcel	}
182271965Smarcel
183271965Smarcel	l2tbl = l1tbl = NULL;
184271965Smarcel	rctbl = NULL;
185271965Smarcel
186271965Smarcel	l1tbl = calloc(1, (size_t)(rcofs - l1ofs));
187271965Smarcel	if (l1tbl == NULL) {
188271965Smarcel		error = ENOMEM;
189271965Smarcel		goto out;
190271965Smarcel	}
191271965Smarcel	if (l2ofs != rcofs) {
192271965Smarcel		rctbl = calloc(1, (size_t)(l2ofs - rcofs));
193271965Smarcel		if (rctbl == NULL) {
194271965Smarcel			error = ENOMEM;
195271965Smarcel			goto out;
196271965Smarcel		}
197271965Smarcel	}
198271965Smarcel
199271965Smarcel	ofs = l2ofs;
200271965Smarcel	for (n = 0; n < nclstrs; n++) {
201271965Smarcel		l1idx = n >> (clstr_log2sz - 3);
202271965Smarcel		if (l1tbl[l1idx] != 0UL)
203271965Smarcel			continue;
204271965Smarcel		blk = n * blkcnt;
205271965Smarcel		if (image_data(blk, blkcnt)) {
206271965Smarcel			be64enc(l1tbl + l1idx, ofs);
207271965Smarcel			ofs += clstrsz;
208271965Smarcel		}
209271965Smarcel	}
210271965Smarcel
211271965Smarcel	error = 0;
212271965Smarcel	if (!error && sparse_write(fd, hdr, clstrsz) < 0)
213271965Smarcel		error = errno;
214271965Smarcel	if (!error && sparse_write(fd, l1tbl, (size_t)(rcofs - l1ofs)) < 0)
215271965Smarcel		error = errno;
216271965Smarcel	/* XXX refcnt table. */
217271965Smarcel	if (error)
218271965Smarcel		goto out;
219271965Smarcel
220271965Smarcel	free(hdr);
221271965Smarcel	hdr = NULL;
222271965Smarcel	if (rctbl != NULL) {
223271965Smarcel		free(rctbl);
224271965Smarcel		rctbl = NULL;
225271965Smarcel	}
226271965Smarcel
227271965Smarcel	l2tbl = malloc(clstrsz);
228271965Smarcel	if (l2tbl == NULL) {
229271965Smarcel		error = ENOMEM;
230271965Smarcel		goto out;
231271965Smarcel	}
232271965Smarcel
233271965Smarcel	for (l1idx = 0; l1idx < l2clstrs; l1idx++) {
234271965Smarcel		if (l1tbl[l1idx] == 0)
235271965Smarcel			continue;
236271965Smarcel		memset(l2tbl, 0, clstrsz);
237271965Smarcel		blkofs = (lba_t)l1idx * (clstrsz * (clstrsz >> 3));
238271965Smarcel		for (l2idx = 0; l2idx < (clstrsz >> 3); l2idx++) {
239271965Smarcel			blk = blkofs + (lba_t)l2idx * blkcnt;
240271965Smarcel			if (blk >= imgsz)
241271965Smarcel				break;
242271965Smarcel			if (image_data(blk, blkcnt)) {
243271965Smarcel				be64enc(l2tbl + l2idx, ofs);
244271965Smarcel				ofs += clstrsz;
245271965Smarcel			}
246271965Smarcel		}
247271965Smarcel		if (sparse_write(fd, l2tbl, clstrsz) < 0) {
248271965Smarcel			error = errno;
249271965Smarcel			goto out;
250271965Smarcel		}
251271965Smarcel	}
252271965Smarcel
253271965Smarcel	free(l2tbl);
254271965Smarcel	l2tbl = NULL;
255271965Smarcel	free(l1tbl);
256271965Smarcel	l1tbl = NULL;
257271965Smarcel
258271965Smarcel	error = 0;
259271965Smarcel	for (n = 0; n < nclstrs; n++) {
260271965Smarcel		blk = n * blkcnt;
261271965Smarcel		if (image_data(blk, blkcnt)) {
262271965Smarcel			error = image_copyout_region(fd, blk, blkcnt);
263271965Smarcel			if (error)
264271965Smarcel				break;
265271965Smarcel		}
266271965Smarcel	}
267271965Smarcel	if (!error)
268271965Smarcel		error = image_copyout_done(fd);
269271965Smarcel
270271965Smarcel out:
271271965Smarcel	if (l2tbl != NULL)
272271965Smarcel		free(l2tbl);
273271965Smarcel	if (rctbl != NULL)
274271965Smarcel		free(rctbl);
275271965Smarcel	if (l1tbl != NULL)
276271965Smarcel		free(l1tbl);
277271965Smarcel	if (hdr != NULL)
278271965Smarcel		free(hdr);
279271965Smarcel	return (error);
280271965Smarcel}
281271965Smarcel
282271965Smarcelstatic int
283271965Smarcelqcow1_write(int fd)
284271965Smarcel{
285271965Smarcel
286271965Smarcel	return (qcow_write(fd, QCOW_VERSION_1));
287271965Smarcel}
288271965Smarcel
289271965Smarcel#ifdef QCOW_SUPPORT_QCOW2
290271965Smarcelstatic int
291271965Smarcelqcow2_write(int fd)
292271965Smarcel{
293271965Smarcel
294271965Smarcel	return (qcow_write(fd, QCOW_VERSION_2));
295271965Smarcel}
296271965Smarcel#endif
297271965Smarcel
298271965Smarcelstatic struct mkimg_format qcow1_format = {
299271965Smarcel	.name = "qcow",
300271965Smarcel	.description = "QEMU Copy-On-Write, version 1",
301271965Smarcel	.resize = qcow1_resize,
302271965Smarcel	.write = qcow1_write,
303271965Smarcel};
304271965SmarcelFORMAT_DEFINE(qcow1_format);
305271965Smarcel
306271965Smarcel#ifdef QCOW_SUPPORT_QCOW2
307271965Smarcelstatic struct mkimg_format qcow2_format = {
308271965Smarcel	.name = "qcow2",
309271965Smarcel	.description = "QEMU Copy-On-Write, version 2",
310271965Smarcel	.resize = qcow2_resize,
311271965Smarcel	.write = qcow2_write,
312271965Smarcel};
313271965SmarcelFORMAT_DEFINE(qcow2_format);
314271965Smarcel#endif
315