]>
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, | |
117 | ext2_off64_t 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 | ||
134 | static int qcow2_copy_data(int fdin, int fdout, ext2_off64_t off_in, | |
135 | ext2_off64_t 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 | ||
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 | ext2_off64_t offset; | |
00eb0eee | 166 | blk64_t *l1_table, *l2_table = NULL; |
bf0449b1 LC |
167 | void *copy_buf = NULL; |
168 | size_t size; | |
169 | ||
170 | if (hdr->crypt_method) | |
171 | return -QCOW_ENCRYPTED; | |
172 | ||
173 | img.fd = qcow2_fd; | |
174 | img.hdr = hdr; | |
175 | img.l2_cache = NULL; | |
176 | img.l1_table = NULL; | |
177 | img.cluster_bits = ext2fs_be32_to_cpu(hdr->cluster_bits); | |
178 | img.cluster_size = 1 << img.cluster_bits; | |
179 | img.l1_size = ext2fs_be32_to_cpu(hdr->l1_size); | |
180 | img.l1_offset = ext2fs_be64_to_cpu(hdr->l1_table_offset); | |
181 | img.l2_size = 1 << (img.cluster_bits - 3); | |
182 | img.image_size = ext2fs_be64_to_cpu(hdr->size); | |
183 | ||
184 | ||
185 | ret = ext2fs_get_memzero(img.cluster_size, &l2_table); | |
186 | if (ret) | |
187 | goto out; | |
188 | ||
189 | ret = ext2fs_get_memzero(1 << img.cluster_bits, ©_buf); | |
190 | if (ret) | |
191 | goto out; | |
192 | ||
193 | if (ext2fs_llseek(raw_fd, 0, SEEK_SET) < 0) { | |
194 | ret = errno; | |
195 | goto out; | |
196 | } | |
197 | ||
198 | ret = qcow2_read_l1_table(&img); | |
199 | if (ret) | |
200 | goto out; | |
201 | ||
202 | l1_table = img.l1_table; | |
203 | /* Walk through l1 table */ | |
204 | for (l1_index = 0; l1_index < img.l1_size; l1_index++) { | |
205 | ext2_off64_t off_out; | |
206 | ||
207 | offset = ext2fs_be64_to_cpu(l1_table[l1_index]) & | |
208 | ~QCOW_OFLAG_COPIED; | |
209 | ||
210 | if ((offset > img.image_size) || | |
211 | (offset <= 0)) | |
212 | continue; | |
213 | ||
214 | if (offset & QCOW_OFLAG_COMPRESSED) { | |
215 | ret = -QCOW_COMPRESSED; | |
216 | goto out; | |
217 | } | |
218 | ||
219 | ret = qcow2_read_l2_table(&img, offset, &l2_table); | |
220 | if (ret) | |
221 | break; | |
222 | ||
223 | /* Walk through l2 table and copy data blocks into raw image */ | |
224 | for (l2_index = 0; l2_index < img.l2_size; l2_index++) { | |
225 | offset = ext2fs_be64_to_cpu(l2_table[l2_index]) & | |
226 | ~QCOW_OFLAG_COPIED; | |
227 | ||
228 | if (offset == 0) | |
229 | continue; | |
230 | ||
231 | off_out = (l1_index * img.l2_size) + | |
232 | l2_index; | |
233 | off_out <<= img.cluster_bits; | |
234 | ret = qcow2_copy_data(qcow2_fd, raw_fd, offset, | |
235 | off_out, copy_buf, img.cluster_size); | |
236 | if (ret) | |
237 | goto out; | |
238 | } | |
239 | } | |
240 | ||
241 | /* Resize the output image to the filesystem size */ | |
ece2d588 TT |
242 | if (ext2fs_llseek(raw_fd, img.image_size - 1, SEEK_SET) < 0) { |
243 | ret = errno; | |
244 | goto out; | |
245 | } | |
bf0449b1 LC |
246 | |
247 | ((char *)copy_buf)[0] = 0; | |
248 | size = write(raw_fd, copy_buf, 1); | |
6a26b38a DW |
249 | if (size != 1) { |
250 | ret = errno; | |
251 | goto out; | |
252 | } | |
bf0449b1 LC |
253 | |
254 | out: | |
255 | if (copy_buf) | |
256 | ext2fs_free_mem(©_buf); | |
257 | if (img.l1_table) | |
258 | ext2fs_free_mem(&img.l1_table); | |
259 | if (l2_table) | |
260 | ext2fs_free_mem(&l2_table); | |
261 | return ret; | |
262 | } |