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