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: stable/10/usr.bin/mkimg/qcow.c 315600 2017-03-20 00:55:24Z pfg $");
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/* Default cluster sizes. */
44271965Smarcel#define	QCOW1_CLSTR_LOG2SZ	12	/* 4KB */
45271965Smarcel#define	QCOW2_CLSTR_LOG2SZ	16	/* 64KB */
46271965Smarcel
47272773Smarcel/* Flag bits in cluster offsets */
48272773Smarcel#define	QCOW_CLSTR_COMPRESSED	(1ULL << 62)
49272773Smarcel#define	QCOW_CLSTR_COPIED	(1ULL << 63)
50272773Smarcel
51271965Smarcelstruct qcow_header {
52271965Smarcel	uint32_t	magic;
53271965Smarcel#define	QCOW_MAGIC		0x514649fb
54271965Smarcel	uint32_t	version;
55271965Smarcel#define	QCOW_VERSION_1		1
56271965Smarcel#define	QCOW_VERSION_2		2
57271965Smarcel	uint64_t	path_offset;
58271965Smarcel	uint32_t	path_length;
59271965Smarcel	uint32_t	clstr_log2sz;	/* v2 only */
60271965Smarcel	uint64_t	disk_size;
61271965Smarcel	union {
62271965Smarcel		struct {
63271965Smarcel			uint8_t		clstr_log2sz;
64271965Smarcel			uint8_t		l2_log2sz;
65271965Smarcel			uint16_t	_pad;
66271965Smarcel			uint32_t	encryption;
67271965Smarcel			uint64_t	l1_offset;
68271965Smarcel		} v1;
69271965Smarcel		struct {
70271965Smarcel			uint32_t	encryption;
71271965Smarcel			uint32_t	l1_entries;
72271965Smarcel			uint64_t	l1_offset;
73271965Smarcel			uint64_t	refcnt_offset;
74276337Smarcel			uint32_t	refcnt_clstrs;
75271965Smarcel			uint32_t	snapshot_count;
76271965Smarcel			uint64_t	snapshot_offset;
77271965Smarcel		} v2;
78271965Smarcel	} u;
79271965Smarcel};
80271965Smarcel
81271965Smarcelstatic u_int clstr_log2sz;
82271965Smarcel
83271965Smarcelstatic uint64_t
84271965Smarcelround_clstr(uint64_t ofs)
85271965Smarcel{
86271965Smarcel	uint64_t clstrsz;
87271965Smarcel
88271965Smarcel	clstrsz = 1UL << clstr_log2sz;
89271965Smarcel	return ((ofs + clstrsz - 1) & ~(clstrsz - 1));
90271965Smarcel}
91271965Smarcel
92271965Smarcelstatic int
93271965Smarcelqcow_resize(lba_t imgsz, u_int version)
94271965Smarcel{
95272773Smarcel	uint64_t imagesz;
96271965Smarcel
97271965Smarcel	switch (version) {
98271965Smarcel	case QCOW_VERSION_1:
99271965Smarcel		clstr_log2sz = QCOW1_CLSTR_LOG2SZ;
100271965Smarcel		break;
101271965Smarcel	case QCOW_VERSION_2:
102271965Smarcel		clstr_log2sz = QCOW2_CLSTR_LOG2SZ;
103271965Smarcel		break;
104271965Smarcel	default:
105271965Smarcel		return (EDOOFUS);
106271965Smarcel	}
107271965Smarcel
108271965Smarcel	imagesz = round_clstr(imgsz * secsz);
109271965Smarcel
110271965Smarcel	if (verbose)
111272773Smarcel		fprintf(stderr, "QCOW: image size = %ju, cluster size = %u\n",
112272773Smarcel		    (uintmax_t)imagesz, (u_int)(1U << clstr_log2sz));
113271965Smarcel
114271965Smarcel	return (image_set_size(imagesz / secsz));
115271965Smarcel}
116271965Smarcel
117271965Smarcelstatic int
118271965Smarcelqcow1_resize(lba_t imgsz)
119271965Smarcel{
120271965Smarcel
121271965Smarcel	return (qcow_resize(imgsz, QCOW_VERSION_1));
122271965Smarcel}
123271965Smarcel
124271965Smarcelstatic int
125271965Smarcelqcow2_resize(lba_t imgsz)
126271965Smarcel{
127271965Smarcel
128271965Smarcel	return (qcow_resize(imgsz, QCOW_VERSION_2));
129271965Smarcel}
130271965Smarcel
131271965Smarcelstatic int
132271965Smarcelqcow_write(int fd, u_int version)
133271965Smarcel{
134271965Smarcel	struct qcow_header *hdr;
135272773Smarcel	uint64_t *l1tbl, *l2tbl, *rctbl;
136272773Smarcel	uint16_t *rcblk;
137272773Smarcel	uint64_t clstr_imgsz, clstr_l2tbls, clstr_l1tblsz;
138272773Smarcel	uint64_t clstr_rcblks, clstr_rctblsz;
139272773Smarcel	uint64_t n, imagesz, nclstrs, ofs, ofsflags;
140272773Smarcel	lba_t blk, blkofs, blk_imgsz;
141272773Smarcel	u_int l1clno, l2clno, rcclno;
142276337Smarcel	u_int blk_clstrsz, refcnt_clstrs;
143272773Smarcel	u_int clstrsz, l1idx, l2idx;
144271965Smarcel	int error;
145271965Smarcel
146271965Smarcel	if (clstr_log2sz == 0)
147271965Smarcel		return (EDOOFUS);
148271965Smarcel
149272773Smarcel	clstrsz = 1U << clstr_log2sz;
150272773Smarcel	blk_clstrsz = clstrsz / secsz;
151272773Smarcel	blk_imgsz = image_get_size();
152272773Smarcel	imagesz = blk_imgsz * secsz;
153272773Smarcel	clstr_imgsz = imagesz >> clstr_log2sz;
154272773Smarcel	clstr_l2tbls = round_clstr(clstr_imgsz * 8) >> clstr_log2sz;
155272773Smarcel	clstr_l1tblsz = round_clstr(clstr_l2tbls * 8) >> clstr_log2sz;
156272773Smarcel	nclstrs = clstr_imgsz + clstr_l2tbls + clstr_l1tblsz + 1;
157272773Smarcel	clstr_rcblks = clstr_rctblsz = 0;
158272773Smarcel	do {
159272773Smarcel		n = clstr_rcblks + clstr_rctblsz;
160272773Smarcel		clstr_rcblks = round_clstr((nclstrs + n) * 2) >> clstr_log2sz;
161272773Smarcel		clstr_rctblsz = round_clstr(clstr_rcblks * 8) >> clstr_log2sz;
162272773Smarcel	} while (n < (clstr_rcblks + clstr_rctblsz));
163271965Smarcel
164272773Smarcel	/*
165272773Smarcel	 * We got all the sizes in clusters. Start the layout.
166272773Smarcel	 * 0 - header
167272773Smarcel	 * 1 - L1 table
168272773Smarcel	 * 2 - RC table (v2 only)
169272773Smarcel	 * 3 - L2 tables
170272773Smarcel	 * 4 - RC block (v2 only)
171272773Smarcel	 * 5 - data
172272773Smarcel	 */
173271965Smarcel
174272773Smarcel	l1clno = 1;
175272773Smarcel	rcclno = 0;
176272773Smarcel	rctbl = l2tbl = l1tbl = NULL;
177272773Smarcel	rcblk = NULL;
178272773Smarcel
179271965Smarcel	hdr = calloc(1, clstrsz);
180271965Smarcel	if (hdr == NULL)
181271965Smarcel		return (errno);
182271965Smarcel
183271965Smarcel	be32enc(&hdr->magic, QCOW_MAGIC);
184271965Smarcel	be32enc(&hdr->version, version);
185271965Smarcel	be64enc(&hdr->disk_size, imagesz);
186271965Smarcel	switch (version) {
187271965Smarcel	case QCOW_VERSION_1:
188272773Smarcel		ofsflags = 0;
189272773Smarcel		l2clno = l1clno + clstr_l1tblsz;
190271965Smarcel		hdr->u.v1.clstr_log2sz = clstr_log2sz;
191271965Smarcel		hdr->u.v1.l2_log2sz = clstr_log2sz - 3;
192272773Smarcel		be64enc(&hdr->u.v1.l1_offset, clstrsz * l1clno);
193271965Smarcel		break;
194271965Smarcel	case QCOW_VERSION_2:
195272773Smarcel		ofsflags = QCOW_CLSTR_COPIED;
196272773Smarcel		rcclno = l1clno + clstr_l1tblsz;
197272773Smarcel		l2clno = rcclno + clstr_rctblsz;
198271965Smarcel		be32enc(&hdr->clstr_log2sz, clstr_log2sz);
199272773Smarcel		be32enc(&hdr->u.v2.l1_entries, clstr_l2tbls);
200272773Smarcel		be64enc(&hdr->u.v2.l1_offset, clstrsz * l1clno);
201272773Smarcel		be64enc(&hdr->u.v2.refcnt_offset, clstrsz * rcclno);
202276337Smarcel		refcnt_clstrs = round_clstr(clstr_rcblks * 8) >> clstr_log2sz;
203276337Smarcel		be32enc(&hdr->u.v2.refcnt_clstrs, refcnt_clstrs);
204271965Smarcel		break;
205271965Smarcel	default:
206271965Smarcel		return (EDOOFUS);
207271965Smarcel	}
208271965Smarcel
209272773Smarcel	if (sparse_write(fd, hdr, clstrsz) < 0) {
210276337Smarcel		error = errno;
211272773Smarcel		goto out;
212272773Smarcel	}
213271965Smarcel
214272773Smarcel	free(hdr);
215272773Smarcel	hdr = NULL;
216272773Smarcel
217272773Smarcel	ofs = clstrsz * l2clno;
218272773Smarcel	nclstrs = 1 + clstr_l1tblsz + clstr_rctblsz;
219272773Smarcel
220315600Spfg	l1tbl = calloc(clstr_l1tblsz, clstrsz);
221271965Smarcel	if (l1tbl == NULL) {
222271965Smarcel		error = ENOMEM;
223271965Smarcel		goto out;
224271965Smarcel	}
225271965Smarcel
226272773Smarcel	for (n = 0; n < clstr_imgsz; n++) {
227272773Smarcel		blk = n * blk_clstrsz;
228272773Smarcel		if (image_data(blk, blk_clstrsz)) {
229272773Smarcel			nclstrs++;
230272773Smarcel			l1idx = n >> (clstr_log2sz - 3);
231272773Smarcel			if (l1tbl[l1idx] == 0) {
232272773Smarcel				be64enc(l1tbl + l1idx, ofs + ofsflags);
233272773Smarcel				ofs += clstrsz;
234272773Smarcel				nclstrs++;
235272773Smarcel			}
236271965Smarcel		}
237271965Smarcel	}
238271965Smarcel
239272773Smarcel	if (sparse_write(fd, l1tbl, clstrsz * clstr_l1tblsz) < 0) {
240271965Smarcel		error = errno;
241271965Smarcel		goto out;
242272773Smarcel	}
243271965Smarcel
244272773Smarcel	clstr_rcblks = 0;
245272773Smarcel	do {
246272773Smarcel		n = clstr_rcblks;
247272773Smarcel		clstr_rcblks = round_clstr((nclstrs + n) * 2) >> clstr_log2sz;
248272773Smarcel	} while (n < clstr_rcblks);
249272773Smarcel
250272773Smarcel	if (rcclno > 0) {
251315600Spfg		rctbl = calloc(clstr_rctblsz, clstrsz);
252272773Smarcel		if (rctbl == NULL) {
253272773Smarcel			error = ENOMEM;
254272773Smarcel			goto out;
255272773Smarcel		}
256272773Smarcel		for (n = 0; n < clstr_rcblks; n++) {
257272773Smarcel			be64enc(rctbl + n, ofs);
258272773Smarcel			ofs += clstrsz;
259272773Smarcel			nclstrs++;
260272773Smarcel		}
261272773Smarcel		if (sparse_write(fd, rctbl, clstrsz * clstr_rctblsz) < 0) {
262272773Smarcel			error = errno;
263272773Smarcel			goto out;
264272773Smarcel		}
265271965Smarcel		free(rctbl);
266271965Smarcel		rctbl = NULL;
267271965Smarcel	}
268271965Smarcel
269271965Smarcel	l2tbl = malloc(clstrsz);
270271965Smarcel	if (l2tbl == NULL) {
271271965Smarcel		error = ENOMEM;
272271965Smarcel		goto out;
273271965Smarcel	}
274271965Smarcel
275272773Smarcel	for (l1idx = 0; l1idx < clstr_l2tbls; l1idx++) {
276271965Smarcel		if (l1tbl[l1idx] == 0)
277271965Smarcel			continue;
278271965Smarcel		memset(l2tbl, 0, clstrsz);
279272773Smarcel		blkofs = (lba_t)l1idx * blk_clstrsz * (clstrsz >> 3);
280271965Smarcel		for (l2idx = 0; l2idx < (clstrsz >> 3); l2idx++) {
281272773Smarcel			blk = blkofs + (lba_t)l2idx * blk_clstrsz;
282272773Smarcel			if (blk >= blk_imgsz)
283271965Smarcel				break;
284272773Smarcel			if (image_data(blk, blk_clstrsz)) {
285272773Smarcel				be64enc(l2tbl + l2idx, ofs + ofsflags);
286271965Smarcel				ofs += clstrsz;
287271965Smarcel			}
288271965Smarcel		}
289271965Smarcel		if (sparse_write(fd, l2tbl, clstrsz) < 0) {
290271965Smarcel			error = errno;
291271965Smarcel			goto out;
292271965Smarcel		}
293271965Smarcel	}
294271965Smarcel
295271965Smarcel	free(l2tbl);
296271965Smarcel	l2tbl = NULL;
297271965Smarcel	free(l1tbl);
298271965Smarcel	l1tbl = NULL;
299271965Smarcel
300272773Smarcel	if (rcclno > 0) {
301315600Spfg		rcblk = calloc(clstr_rcblks, clstrsz);
302272773Smarcel		if (rcblk == NULL) {
303272773Smarcel			error = ENOMEM;
304272773Smarcel			goto out;
305272773Smarcel		}
306272773Smarcel		for (n = 0; n < nclstrs; n++)
307272773Smarcel			be16enc(rcblk + n, 1);
308272773Smarcel		if (sparse_write(fd, rcblk, clstrsz * clstr_rcblks) < 0) {
309272773Smarcel			error = errno;
310272773Smarcel			goto out;
311272773Smarcel		}
312272773Smarcel		free(rcblk);
313272773Smarcel		rcblk = NULL;
314272773Smarcel	}
315272773Smarcel
316271965Smarcel	error = 0;
317272773Smarcel	for (n = 0; n < clstr_imgsz; n++) {
318272773Smarcel		blk = n * blk_clstrsz;
319272773Smarcel		if (image_data(blk, blk_clstrsz)) {
320272773Smarcel			error = image_copyout_region(fd, blk, blk_clstrsz);
321271965Smarcel			if (error)
322271965Smarcel				break;
323271965Smarcel		}
324271965Smarcel	}
325271965Smarcel	if (!error)
326271965Smarcel		error = image_copyout_done(fd);
327271965Smarcel
328271965Smarcel out:
329272773Smarcel	if (rcblk != NULL)
330272773Smarcel		free(rcblk);
331271965Smarcel	if (l2tbl != NULL)
332271965Smarcel		free(l2tbl);
333271965Smarcel	if (rctbl != NULL)
334271965Smarcel		free(rctbl);
335271965Smarcel	if (l1tbl != NULL)
336271965Smarcel		free(l1tbl);
337271965Smarcel	if (hdr != NULL)
338271965Smarcel		free(hdr);
339271965Smarcel	return (error);
340271965Smarcel}
341271965Smarcel
342271965Smarcelstatic int
343271965Smarcelqcow1_write(int fd)
344271965Smarcel{
345271965Smarcel
346271965Smarcel	return (qcow_write(fd, QCOW_VERSION_1));
347271965Smarcel}
348271965Smarcel
349271965Smarcelstatic int
350271965Smarcelqcow2_write(int fd)
351271965Smarcel{
352271965Smarcel
353271965Smarcel	return (qcow_write(fd, QCOW_VERSION_2));
354271965Smarcel}
355271965Smarcel
356271965Smarcelstatic struct mkimg_format qcow1_format = {
357271965Smarcel	.name = "qcow",
358271965Smarcel	.description = "QEMU Copy-On-Write, version 1",
359271965Smarcel	.resize = qcow1_resize,
360271965Smarcel	.write = qcow1_write,
361271965Smarcel};
362271965SmarcelFORMAT_DEFINE(qcow1_format);
363271965Smarcel
364271965Smarcelstatic struct mkimg_format qcow2_format = {
365271965Smarcel	.name = "qcow2",
366271965Smarcel	.description = "QEMU Copy-On-Write, version 2",
367271965Smarcel	.resize = qcow2_resize,
368271965Smarcel	.write = qcow2_write,
369271965Smarcel};
370271965SmarcelFORMAT_DEFINE(qcow2_format);
371