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