Whamcloud - gitweb
libextr2fs: handle short reads/writes while creating the qcow file
[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 #ifndef _LARGEFILE_SOURCE
27 #define _LARGEFILE_SOURCE
28 #endif
29 #ifndef _LARGEFILE64_SOURCE
30 #define _LARGEFILE64_SOURCE
31 #endif
32
33 #include "config.h"
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
67         if (ext2fs_llseek(fd, 0, SEEK_SET < 0)) {
68                 ext2fs_free_mem(&buffer);
69                 return NULL;
70         }
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
100         if (ext2fs_llseek(fd, img->l1_offset, SEEK_SET) < 0) {
101                 ext2fs_free_mem(&table);
102                 return errno;
103         }
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                                __u64 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, __u64 off_in,
135                            __u64 off_out, void *buf, size_t count)
136 {
137         ssize_t c1, c2, c;
138         void *ptr;
139         int retries = 10;
140
141         assert(buf);
142
143         if (ext2fs_llseek(fdout, off_out, SEEK_SET) < 0)
144                 return errno;
145
146         if (ext2fs_llseek(fdin, off_in, SEEK_SET) < 0)
147                 return errno;
148
149         while (count > 0) {
150                 errno = 0;
151                 c1 = read(fdin, buf, count);
152                 if (c1 < 0 || ((c1 == 0) && errno))
153                         return errno;
154                 if (c1 == 0)
155                         break;  /* EOF */
156
157                 for (ptr = buf, c = c1; c > 0; ptr += c2, c -= c2) {
158                         errno = 0;
159                         c2 = write(fdout, ptr, c1);
160                         if (c2 < 0 || ((c2 == 0) && errno))
161                                 return errno;
162                         if (c2 == 0 && --retries <= 0)
163                                 break; /* This should never happen...  */
164                 }
165                 count -= c1;
166         }
167         return 0;
168 }
169
170
171 int qcow2_write_raw_image(int qcow2_fd, int raw_fd,
172                               struct ext2_qcow2_hdr *hdr)
173 {
174         struct ext2_qcow2_image img;
175         errcode_t ret = 0;
176         unsigned int l1_index, l2_index;
177         __u64 offset;
178         blk64_t *l1_table, *l2_table = NULL;
179         void *copy_buf = NULL;
180         size_t size;
181         unsigned int max_l1_size;
182
183         if (hdr->crypt_method)
184                 return -QCOW_ENCRYPTED;
185
186         img.fd = qcow2_fd;
187         img.hdr = hdr;
188         img.l2_cache = NULL;
189         img.l1_table = NULL;
190         img.cluster_bits = ext2fs_be32_to_cpu(hdr->cluster_bits);
191         if (img.cluster_bits < 9 || img.cluster_bits > 31)
192                 return -QCOW_CORRUPTED;
193         img.cluster_size = 1 << img.cluster_bits;
194         img.l1_size = ext2fs_be32_to_cpu(hdr->l1_size);
195         img.l1_offset = ext2fs_be64_to_cpu(hdr->l1_table_offset);
196         img.l2_size = 1 << (img.cluster_bits - 3);
197         img.image_size = ext2fs_be64_to_cpu(hdr->size);
198
199         if (img.l1_offset & (img.cluster_size - 1))
200                 return -QCOW_CORRUPTED;
201
202         max_l1_size = (img.image_size >> ((2 * img.cluster_bits) - 3)) +
203                 img.cluster_size;
204         if (img.l1_size > max_l1_size)
205                 return -QCOW_CORRUPTED;
206
207         ret = ext2fs_get_memzero(img.cluster_size, &l2_table);
208         if (ret)
209                 goto out;
210
211         ret = ext2fs_get_memzero(1 << img.cluster_bits, &copy_buf);
212         if (ret)
213                 goto out;
214
215         if (ext2fs_llseek(raw_fd, 0, SEEK_SET) < 0) {
216                 ret = errno;
217                 goto out;
218         }
219
220         ret = qcow2_read_l1_table(&img);
221         if (ret)
222                 goto out;
223
224         l1_table = img.l1_table;
225         /* Walk through l1 table */
226         for (l1_index = 0; l1_index < img.l1_size; l1_index++) {
227                 __u64 off_out;
228
229                 offset = ext2fs_be64_to_cpu(l1_table[l1_index]) &
230                          ~QCOW_OFLAG_COPIED;
231
232                 if ((offset > img.image_size) ||
233                     (offset <= 0))
234                         continue;
235
236                 if (offset & QCOW_OFLAG_COMPRESSED) {
237                         ret = -QCOW_COMPRESSED;
238                         goto out;
239                 }
240
241                 ret = qcow2_read_l2_table(&img, offset, &l2_table);
242                 if (ret)
243                         break;
244
245                 /* Walk through l2 table and copy data blocks into raw image */
246                 for (l2_index = 0; l2_index < img.l2_size; l2_index++) {
247                         offset = ext2fs_be64_to_cpu(l2_table[l2_index]) &
248                                  ~QCOW_OFLAG_COPIED;
249
250                         if (offset == 0)
251                                 continue;
252
253                         off_out = ((__u64)l1_index * img.l2_size) +
254                                   l2_index;
255                         off_out <<= img.cluster_bits;
256                         ret = qcow2_copy_data(qcow2_fd, raw_fd, offset,
257                                         off_out, copy_buf, img.cluster_size);
258                         if (ret)
259                                 goto out;
260                 }
261         }
262
263         /* Resize the output image to the filesystem size */
264         if (ext2fs_llseek(raw_fd, img.image_size - 1, SEEK_SET) < 0) {
265                 ret = errno;
266                 goto out;
267         }
268
269         ((char *)copy_buf)[0] = 0;
270         size = write(raw_fd, copy_buf, 1);
271         if (size != 1) {
272                 ret = errno;
273                 goto out;
274         }
275
276 out:
277         if (copy_buf)
278                 ext2fs_free_mem(&copy_buf);
279         if (img.l1_table)
280                 ext2fs_free_mem(&img.l1_table);
281         if (l2_table)
282                 ext2fs_free_mem(&l2_table);
283         return ret;
284 }