]>
Commit | Line | Data |
---|---|---|
bf0449b1 LC |
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 | ||
f1644c32 | 26 | #ifndef _LARGEFILE_SOURCE |
bf0449b1 | 27 | #define _LARGEFILE_SOURCE |
f1644c32 TT |
28 | #endif |
29 | #ifndef _LARGEFILE64_SOURCE | |
bf0449b1 | 30 | #define _LARGEFILE64_SOURCE |
f1644c32 | 31 | #endif |
bf0449b1 | 32 | |
d1154eb4 | 33 | #include "config.h" |
bf0449b1 LC |
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 | ||
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 | ||
6a26b38a DW |
67 | if (ext2fs_llseek(fd, 0, SEEK_SET < 0)) { |
68 | ext2fs_free_mem(&buffer); | |
bf0449b1 | 69 | return NULL; |
6a26b38a | 70 | } |
bf0449b1 LC |
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 | ||
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 | ||
6a26b38a DW |
100 | if (ext2fs_llseek(fd, img->l1_offset, SEEK_SET) < 0) { |
101 | ext2fs_free_mem(&table); | |
bf0449b1 | 102 | return errno; |
6a26b38a | 103 | } |
bf0449b1 LC |
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 | ||
116 | static int qcow2_read_l2_table(struct ext2_qcow2_image *img, | |
193cf9be | 117 | __u64 offset, blk64_t **l2_table) |
bf0449b1 LC |
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 | ||
193cf9be TT |
134 | static int qcow2_copy_data(int fdin, int fdout, __u64 off_in, |
135 | __u64 off_out, void *buf, size_t count) | |
bf0449b1 LC |
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 | ||
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; | |
193cf9be | 165 | __u64 offset; |
00eb0eee | 166 | blk64_t *l1_table, *l2_table = NULL; |
bf0449b1 LC |
167 | void *copy_buf = NULL; |
168 | size_t size; | |
1ff009af | 169 | unsigned int max_l1_size; |
bf0449b1 LC |
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); | |
1ff009af TT |
179 | if (img.cluster_bits < 9 || img.cluster_bits > 31) |
180 | return -QCOW_CORRUPTED; | |
bf0449b1 LC |
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 | ||
1ff009af TT |
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; | |
bf0449b1 LC |
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, ©_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++) { | |
193cf9be | 215 | __u64 off_out; |
bf0449b1 LC |
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 = (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 */ | |
ece2d588 TT |
252 | if (ext2fs_llseek(raw_fd, img.image_size - 1, SEEK_SET) < 0) { |
253 | ret = errno; | |
254 | goto out; | |
255 | } | |
bf0449b1 LC |
256 | |
257 | ((char *)copy_buf)[0] = 0; | |
258 | size = write(raw_fd, copy_buf, 1); | |
6a26b38a DW |
259 | if (size != 1) { |
260 | ret = errno; | |
261 | goto out; | |
262 | } | |
bf0449b1 LC |
263 | |
264 | out: | |
265 | if (copy_buf) | |
266 | ext2fs_free_mem(©_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 | } |