]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - lib/ext2fs/qcow2.c
libext2fs: use __u64 instead of ext2_off64_t in qcow.c
[thirdparty/e2fsprogs.git] / lib / ext2fs / qcow2.c
CommitLineData
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
55struct 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
89static 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
116static 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
134static 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
159int 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, &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++) {
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
264out:
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}