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