]>
git.ipfire.org Git - thirdparty/e2fsprogs.git/blob - lib/ext2fs/qcow2.c
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.
7 * The qcow2 format supports zlib compression, but that is not yet
10 * It is possible to directly mount a qcow2 image using qemu-nbd:
12 * [root]# modprobe nbd max_part=63
13 * [root]# qemu-nbd -c /dev/nbd0 image.img
14 * [root]# mount /dev/nbd0p1 /mnt/qemu
16 * Format details at http://people.gnome.org/~markmc/qcow-image-format.html
18 * Copyright (C) 2010 Red Hat, Inc., Lukas Czerner <lczerner@redhat.com>
21 * This file may be redistributed under the terms of the GNU Public
26 #ifndef _LARGEFILE_SOURCE
27 #define _LARGEFILE_SOURCE
29 #ifndef _LARGEFILE64_SOURCE
30 #define _LARGEFILE64_SOURCE
47 #include <sys/types.h>
50 #include "ext2fs/ext2fs.h"
53 /* Functions for converting qcow2 image into raw image */
55 struct ext2_qcow2_hdr
*qcow2_read_header(int fd
)
58 struct ext2_qcow2_hdr
*hdr
= NULL
;
62 ret
= ext2fs_get_mem(sizeof(struct ext2_qcow2_hdr
), &buffer
);
65 memset(buffer
, 0, sizeof(struct ext2_qcow2_hdr
));
67 if (ext2fs_llseek(fd
, 0, SEEK_SET
< 0)) {
68 ext2fs_free_mem(&buffer
);
72 size
= read(fd
, buffer
, sizeof(struct ext2_qcow2_hdr
));
73 if (size
!= sizeof(struct ext2_qcow2_hdr
)) {
74 ext2fs_free_mem(&buffer
);
78 hdr
= (struct ext2_qcow2_hdr
*)(buffer
);
80 if ((ext2fs_be32_to_cpu(hdr
->magic
) != QCOW_MAGIC
) ||
81 (ext2fs_be32_to_cpu(hdr
->version
) != 2)) {
82 ext2fs_free_mem(&hdr
);
89 static int qcow2_read_l1_table(struct ext2_qcow2_image
*img
)
92 size_t size
, l1_size
= img
->l1_size
* sizeof(blk64_t
);
96 ret
= ext2fs_get_memzero(l1_size
, &table
);
100 if (ext2fs_llseek(fd
, img
->l1_offset
, SEEK_SET
) < 0) {
101 ext2fs_free_mem(&table
);
105 size
= read(fd
, table
, l1_size
);
106 if (size
!= l1_size
) {
107 ext2fs_free_mem(&table
);
111 img
->l1_table
= table
;
116 static int qcow2_read_l2_table(struct ext2_qcow2_image
*img
,
117 ext2_off64_t offset
, blk64_t
**l2_table
)
124 if (ext2fs_llseek(fd
, offset
, SEEK_SET
) < 0)
127 size
= read(fd
, *l2_table
, img
->cluster_size
);
128 if (size
!= img
->cluster_size
)
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
)
141 if (ext2fs_llseek(fdout
, off_out
, SEEK_SET
) < 0)
144 if (ext2fs_llseek(fdin
, off_in
, SEEK_SET
) < 0)
147 size
= read(fdin
, buf
, count
);
151 size
= write(fdout
, buf
, count
);
159 int qcow2_write_raw_image(int qcow2_fd
, int raw_fd
,
160 struct ext2_qcow2_hdr
*hdr
)
162 struct ext2_qcow2_image img
;
164 unsigned int l1_index
, l2_index
;
166 blk64_t
*l1_table
, *l2_table
= NULL
;
167 void *copy_buf
= NULL
;
170 if (hdr
->crypt_method
)
171 return -QCOW_ENCRYPTED
;
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
);
185 ret
= ext2fs_get_memzero(img
.cluster_size
, &l2_table
);
189 ret
= ext2fs_get_memzero(1 << img
.cluster_bits
, ©_buf
);
193 if (ext2fs_llseek(raw_fd
, 0, SEEK_SET
) < 0) {
198 ret
= qcow2_read_l1_table(&img
);
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
;
207 offset
= ext2fs_be64_to_cpu(l1_table
[l1_index
]) &
210 if ((offset
> img
.image_size
) ||
214 if (offset
& QCOW_OFLAG_COMPRESSED
) {
215 ret
= -QCOW_COMPRESSED
;
219 ret
= qcow2_read_l2_table(&img
, offset
, &l2_table
);
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
]) &
231 off_out
= (l1_index
* img
.l2_size
) +
233 off_out
<<= img
.cluster_bits
;
234 ret
= qcow2_copy_data(qcow2_fd
, raw_fd
, offset
,
235 off_out
, copy_buf
, img
.cluster_size
);
241 /* Resize the output image to the filesystem size */
242 if (ext2fs_llseek(raw_fd
, img
.image_size
- 1, SEEK_SET
) < 0) {
247 ((char *)copy_buf
)[0] = 0;
248 size
= write(raw_fd
, copy_buf
, 1);
256 ext2fs_free_mem(©_buf
);
258 ext2fs_free_mem(&img
.l1_table
);
260 ext2fs_free_mem(&l2_table
);