qcow2.c revision 6a26b38ac46a0489bd477832dcaebd276c2f489b
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 "config.h" 30#include <fcntl.h> 31#include <grp.h> 32#include <pwd.h> 33#include <stdio.h> 34#ifdef HAVE_STDLIB_H 35#include <stdlib.h> 36#endif 37#include <string.h> 38#include <time.h> 39#include <unistd.h> 40#include <fcntl.h> 41#include <errno.h> 42#include <sys/stat.h> 43#include <sys/types.h> 44#include <assert.h> 45 46#include "ext2fs/ext2fs.h" 47#include "qcow2.h" 48 49/* Functions for converting qcow2 image into raw image */ 50 51struct ext2_qcow2_hdr *qcow2_read_header(int fd) 52{ 53 void *buffer = NULL; 54 struct ext2_qcow2_hdr *hdr = NULL; 55 size_t size; 56 errcode_t ret; 57 58 ret = ext2fs_get_mem(sizeof(struct ext2_qcow2_hdr), &buffer); 59 if (ret) 60 return NULL; 61 memset(buffer, 0, sizeof(struct ext2_qcow2_hdr)); 62 63 if (ext2fs_llseek(fd, 0, SEEK_SET < 0)) { 64 ext2fs_free_mem(&buffer); 65 return NULL; 66 } 67 68 size = read(fd, buffer, sizeof(struct ext2_qcow2_hdr)); 69 if (size != sizeof(struct ext2_qcow2_hdr)) { 70 ext2fs_free_mem(&buffer); 71 return NULL; 72 } 73 74 hdr = (struct ext2_qcow2_hdr *)(buffer); 75 76 if ((ext2fs_be32_to_cpu(hdr->magic) != QCOW_MAGIC) || 77 (ext2fs_be32_to_cpu(hdr->version) != 2)) { 78 ext2fs_free_mem(&hdr); 79 return NULL; 80 } 81 82 return hdr; 83} 84 85static int qcow2_read_l1_table(struct ext2_qcow2_image *img) 86{ 87 int fd = img->fd; 88 size_t size, l1_size = img->l1_size * sizeof(blk64_t); 89 blk64_t *table; 90 errcode_t ret; 91 92 ret = ext2fs_get_memzero(l1_size, &table); 93 if (ret) 94 return ret; 95 96 if (ext2fs_llseek(fd, img->l1_offset, SEEK_SET) < 0) { 97 ext2fs_free_mem(&table); 98 return errno; 99 } 100 101 size = read(fd, table, l1_size); 102 if (size != l1_size) { 103 ext2fs_free_mem(&table); 104 return errno; 105 } 106 107 img->l1_table = table; 108 109 return 0; 110} 111 112static int qcow2_read_l2_table(struct ext2_qcow2_image *img, 113 ext2_off64_t offset, blk64_t **l2_table) 114{ 115 int fd = img->fd; 116 size_t size; 117 118 assert(*l2_table); 119 120 if (ext2fs_llseek(fd, offset, SEEK_SET) < 0) 121 return errno; 122 123 size = read(fd, *l2_table, img->cluster_size); 124 if (size != img->cluster_size) 125 return errno; 126 127 return 0; 128} 129 130static int qcow2_copy_data(int fdin, int fdout, ext2_off64_t off_in, 131 ext2_off64_t off_out, void *buf, size_t count) 132{ 133 size_t size; 134 135 assert(buf); 136 137 if (ext2fs_llseek(fdout, off_out, SEEK_SET) < 0) 138 return errno; 139 140 if (ext2fs_llseek(fdin, off_in, SEEK_SET) < 0) 141 return errno; 142 143 size = read(fdin, buf, count); 144 if (size != count) 145 return errno; 146 147 size = write(fdout, buf, count); 148 if (size != count) 149 return errno; 150 151 return 0; 152} 153 154 155int qcow2_write_raw_image(int qcow2_fd, int raw_fd, 156 struct ext2_qcow2_hdr *hdr) 157{ 158 struct ext2_qcow2_image img; 159 errcode_t ret = 0; 160 unsigned int l1_index, l2_index; 161 ext2_off64_t offset; 162 blk64_t *l1_table, *l2_table = NULL; 163 void *copy_buf = NULL; 164 size_t size; 165 166 if (hdr->crypt_method) 167 return -QCOW_ENCRYPTED; 168 169 img.fd = qcow2_fd; 170 img.hdr = hdr; 171 img.l2_cache = NULL; 172 img.l1_table = NULL; 173 img.cluster_bits = ext2fs_be32_to_cpu(hdr->cluster_bits); 174 img.cluster_size = 1 << img.cluster_bits; 175 img.l1_size = ext2fs_be32_to_cpu(hdr->l1_size); 176 img.l1_offset = ext2fs_be64_to_cpu(hdr->l1_table_offset); 177 img.l2_size = 1 << (img.cluster_bits - 3); 178 img.image_size = ext2fs_be64_to_cpu(hdr->size); 179 180 181 ret = ext2fs_get_memzero(img.cluster_size, &l2_table); 182 if (ret) 183 goto out; 184 185 ret = ext2fs_get_memzero(1 << img.cluster_bits, ©_buf); 186 if (ret) 187 goto out; 188 189 if (ext2fs_llseek(raw_fd, 0, SEEK_SET) < 0) { 190 ret = errno; 191 goto out; 192 } 193 194 ret = qcow2_read_l1_table(&img); 195 if (ret) 196 goto out; 197 198 l1_table = img.l1_table; 199 /* Walk through l1 table */ 200 for (l1_index = 0; l1_index < img.l1_size; l1_index++) { 201 ext2_off64_t off_out; 202 203 offset = ext2fs_be64_to_cpu(l1_table[l1_index]) & 204 ~QCOW_OFLAG_COPIED; 205 206 if ((offset > img.image_size) || 207 (offset <= 0)) 208 continue; 209 210 if (offset & QCOW_OFLAG_COMPRESSED) { 211 ret = -QCOW_COMPRESSED; 212 goto out; 213 } 214 215 ret = qcow2_read_l2_table(&img, offset, &l2_table); 216 if (ret) 217 break; 218 219 /* Walk through l2 table and copy data blocks into raw image */ 220 for (l2_index = 0; l2_index < img.l2_size; l2_index++) { 221 offset = ext2fs_be64_to_cpu(l2_table[l2_index]) & 222 ~QCOW_OFLAG_COPIED; 223 224 if (offset == 0) 225 continue; 226 227 off_out = (l1_index * img.l2_size) + 228 l2_index; 229 off_out <<= img.cluster_bits; 230 ret = qcow2_copy_data(qcow2_fd, raw_fd, offset, 231 off_out, copy_buf, img.cluster_size); 232 if (ret) 233 goto out; 234 } 235 } 236 237 /* Resize the output image to the filesystem size */ 238 if (ext2fs_llseek(raw_fd, img.image_size - 1, SEEK_SET) < 0) 239 return errno; 240 241 ((char *)copy_buf)[0] = 0; 242 size = write(raw_fd, copy_buf, 1); 243 if (size != 1) { 244 ret = errno; 245 goto out; 246 } 247 248out: 249 if (copy_buf) 250 ext2fs_free_mem(©_buf); 251 if (img.l1_table) 252 ext2fs_free_mem(&img.l1_table); 253 if (l2_table) 254 ext2fs_free_mem(&l2_table); 255 return ret; 256} 257