Statistics
| Branch: | Revision:

root / vmdk2raw.c @ 05efe46e

History | View | Annotate | Download (6.9 kB)

1 47cea614 bellard
/*
2 47cea614 bellard
   vmdk2raw: convert vmware images to raw disk images
3 47cea614 bellard
   Copyright (C) Net Integration Technologies 2004
4 47cea614 bellard
   Copyright (C) Matthew Chapman 2003
5 47cea614 bellard

6 47cea614 bellard
   This program is free software; you can redistribute it and/or modify
7 47cea614 bellard
   it under the terms of the GNU General Public License as published by
8 47cea614 bellard
   the Free Software Foundation; either version 2 of the License, or
9 47cea614 bellard
   (at your option) any later version.
10 47cea614 bellard

11 47cea614 bellard
   This program is distributed in the hope that it will be useful,
12 47cea614 bellard
   but WITHOUT ANY WARRANTY; without even the implied warranty of
13 47cea614 bellard
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 47cea614 bellard
   GNU General Public License for more details.
15 47cea614 bellard

16 47cea614 bellard
   You should have received a copy of the GNU General Public License
17 47cea614 bellard
   along with this program; if not, write to the Free Software
18 47cea614 bellard
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 47cea614 bellard
*/
20 47cea614 bellard
21 47cea614 bellard
#include <stdlib.h>
22 47cea614 bellard
#include <stdio.h>
23 47cea614 bellard
#include <unistd.h>
24 47cea614 bellard
#include <fcntl.h>
25 47cea614 bellard
#include <inttypes.h>
26 47cea614 bellard
#include <sys/types.h>
27 47cea614 bellard
#include <string.h>
28 47cea614 bellard
#include <sys/stat.h>
29 47cea614 bellard
#include <errno.h>
30 47cea614 bellard
#include "vmdk.h"
31 47cea614 bellard
#include "config-host.h"
32 47cea614 bellard
33 05efe46e bellard
static struct cowdisk_header header;
34 05efe46e bellard
static struct vmdisk_header header4;
35 05efe46e bellard
static off64_t disk_limit;
36 05efe46e bellard
static unsigned int granule_size;
37 05efe46e bellard
static uint32_t *l1dir;
38 05efe46e bellard
39 05efe46e bellard
static unsigned int cached_l2dir;
40 05efe46e bellard
static uint32_t l2dir[L2_SIZE];
41 05efe46e bellard
42 05efe46e bellard
static struct vmdk_prm {
43 05efe46e bellard
    uint32_t grain_table_size;
44 05efe46e bellard
    uint32_t sectors_per_grain;
45 05efe46e bellard
    uint32_t sectors_per_table;
46 05efe46e bellard
    uint32_t directory_size;
47 05efe46e bellard
} vdsk;
48 05efe46e bellard
49 05efe46e bellard
static size_t read_physical(int fd, off64_t offset, size_t length, void *buffer)
50 47cea614 bellard
{
51 47cea614 bellard
    size_t n;
52 47cea614 bellard
53 05efe46e bellard
    if (lseek64(fd, offset, SEEK_SET) == -1) {
54 05efe46e bellard
        printf(" error trying to seek lseek to %lld", offset);
55 47cea614 bellard
        return -1;
56 47cea614 bellard
    }
57 47cea614 bellard
58 47cea614 bellard
    n = read(fd, buffer, length);
59 05efe46e bellard
    
60 05efe46e bellard
    if (n == -1) {
61 05efe46e bellard
        printf("read from disk %lld", offset);
62 47cea614 bellard
        return -1;
63 47cea614 bellard
    }
64 47cea614 bellard
65 47cea614 bellard
    return n;
66 47cea614 bellard
}
67 47cea614 bellard
68 05efe46e bellard
static int read_l1dir(int fd, size_t offset, int num)
69 05efe46e bellard
{
70 05efe46e bellard
    l1dir = malloc(sizeof(*l1dir) * num);
71 05efe46e bellard
    if (!l1dir)
72 05efe46e bellard
        return -1;
73 05efe46e bellard
    return read_physical(fd, offset << SECTOR_BITS, sizeof(*l1dir) * num, (char *)l1dir) != (sizeof(*l1dir) * num);
74 05efe46e bellard
}
75 05efe46e bellard
76 05efe46e bellard
static int read_l2dir(int fd, size_t offset, int num)
77 47cea614 bellard
{
78 05efe46e bellard
    return read_physical(fd, offset << SECTOR_BITS, sizeof(l2dir[0]) * num, (char *)l2dir) != sizeof(l2dir);
79 05efe46e bellard
}
80 47cea614 bellard
81 05efe46e bellard
static size_t copy_virtual(struct vmdk_prm *dsk, int in_fd, int out_fd, off64_t offset, void *buffer, size_t length)
82 05efe46e bellard
{
83 05efe46e bellard
    
84 05efe46e bellard
    unsigned int granule_offset;
85 05efe46e bellard
    unsigned int grain_index;
86 05efe46e bellard
    unsigned int sector_map_idx;
87 05efe46e bellard
    
88 47cea614 bellard
    granule_offset = offset % granule_size;
89 47cea614 bellard
    length = MIN(length, granule_size - granule_offset);
90 47cea614 bellard
    length = MIN(length, disk_limit - offset);
91 47cea614 bellard
92 05efe46e bellard
    sector_map_idx = (offset >> SECTOR_BITS) / dsk->sectors_per_table;
93 05efe46e bellard
    
94 05efe46e bellard
    if (sector_map_idx >= dsk->directory_size) {
95 05efe46e bellard
        fprintf(stderr, "cannot locate grain table for %d in %d\n", sector_map_idx, dsk->directory_size);
96 05efe46e bellard
        return -1;
97 05efe46e bellard
    }
98 47cea614 bellard
99 05efe46e bellard
    if (l1dir[sector_map_idx] == 0)
100 47cea614 bellard
        goto zero_fill;
101 05efe46e bellard
   
102 05efe46e bellard
    if (sector_map_idx != cached_l2dir) {
103 05efe46e bellard
        if (read_l2dir(in_fd, l1dir[sector_map_idx], dsk->grain_table_size)) {
104 05efe46e bellard
            fprintf(stderr, "read failed\n");
105 05efe46e bellard
            return -1;
106 05efe46e bellard
        }
107 05efe46e bellard
        cached_l2dir = sector_map_idx;
108 05efe46e bellard
    }
109 47cea614 bellard
110 05efe46e bellard
    grain_index = ((offset >> SECTOR_BITS) % dsk->sectors_per_table) / dsk->sectors_per_grain;
111 05efe46e bellard
    
112 05efe46e bellard
    if (grain_index >= dsk->grain_table_size) {
113 05efe46e bellard
        fprintf(stderr, "grain to large");
114 05efe46e bellard
        return -1;
115 47cea614 bellard
    }
116 47cea614 bellard
117 05efe46e bellard
    if (l2dir[grain_index] == 0)
118 47cea614 bellard
        goto zero_fill;
119 47cea614 bellard
120 05efe46e bellard
    if (read_physical(in_fd, (l2dir[grain_index] << SECTOR_BITS) + granule_offset, length, buffer) != length) {
121 05efe46e bellard
        fprintf(stderr, "read error 2\n");
122 05efe46e bellard
        return -1;
123 05efe46e bellard
    }
124 05efe46e bellard
    
125 47cea614 bellard
    write(out_fd, buffer, length);
126 47cea614 bellard
    return length;
127 47cea614 bellard
128 47cea614 bellard
zero_fill:
129 47cea614 bellard
    /* the last chunk of the file can not be sparse
130 47cea614 bellard
     * or the file will be truncated */
131 05efe46e bellard
    if (offset + length >= disk_limit) {
132 05efe46e bellard
        if (lseek64(out_fd, length-1, SEEK_CUR) == (off_t)-1)
133 05efe46e bellard
            perror("lseek");
134 05efe46e bellard
        /* write the last NULL byte instead of seeking */
135 05efe46e bellard
        const char nil = 0;
136 05efe46e bellard
        write(out_fd, &nil, 1);
137 47cea614 bellard
    } else {
138 05efe46e bellard
        if (lseek64(out_fd, length, SEEK_CUR) == (off_t)-1)
139 47cea614 bellard
            perror("lseek");
140 47cea614 bellard
    }
141 47cea614 bellard
    return length;
142 47cea614 bellard
}
143 47cea614 bellard
144 05efe46e bellard
static int open_vmdk4(int fd)
145 47cea614 bellard
{
146 05efe46e bellard
    if (read(fd, &header4, sizeof(header4)) != sizeof(header4)) {
147 05efe46e bellard
        perror("read from disk");
148 47cea614 bellard
        return -1;
149 47cea614 bellard
    }
150 05efe46e bellard
    
151 05efe46e bellard
    granule_size = header4.granularity << SECTOR_BITS;
152 05efe46e bellard
    disk_limit = header4.capacity << SECTOR_BITS; 
153 05efe46e bellard
    
154 05efe46e bellard
    cached_l2dir = -1;
155 05efe46e bellard
    vdsk.grain_table_size = header4.num_gtes_per_gte;
156 05efe46e bellard
    vdsk.sectors_per_grain = header4.granularity;
157 05efe46e bellard
    vdsk.sectors_per_table = vdsk.grain_table_size * vdsk.sectors_per_grain;
158 05efe46e bellard
    vdsk.directory_size = (header4.capacity + vdsk.sectors_per_table - 1) / vdsk.sectors_per_table + 1;
159 47cea614 bellard
160 05efe46e bellard
    if (read_l1dir(fd, header4.rgd_offset, vdsk.directory_size))
161 47cea614 bellard
        return -1;
162 05efe46e bellard
    
163 05efe46e bellard
    return 0;
164 05efe46e bellard
    
165 05efe46e bellard
}
166 47cea614 bellard
167 05efe46e bellard
static int open_vmdk3(int fd)
168 05efe46e bellard
{
169 05efe46e bellard
    if (read(fd, &header, sizeof(header)) != sizeof(header)) {
170 05efe46e bellard
        perror("read from disk\n");
171 47cea614 bellard
        return -1;
172 47cea614 bellard
    }
173 47cea614 bellard
    granule_size = header.granularity << SECTOR_BITS;
174 05efe46e bellard
    vdsk.sectors_per_grain = header.granularity;
175 05efe46e bellard
    vdsk.grain_table_size = L2_SIZE;
176 05efe46e bellard
    vdsk.sectors_per_table = vdsk.grain_table_size * vdsk.sectors_per_grain;
177 05efe46e bellard
    vdsk.directory_size = L1_SIZE;
178 05efe46e bellard
    if (read_l1dir(fd, header.l1dir_offset, L1_SIZE))
179 47cea614 bellard
        return -1;
180 47cea614 bellard
181 47cea614 bellard
    disk_limit = header.disk_sectors << SECTOR_BITS;
182 47cea614 bellard
183 05efe46e bellard
    return fd;
184 05efe46e bellard
}
185 05efe46e bellard
186 05efe46e bellard
static int open_vmdk(const char *filename)
187 05efe46e bellard
{
188 05efe46e bellard
    int fd = open(filename, O_RDONLY | O_LARGEFILE);
189 05efe46e bellard
    if (fd == -1) {
190 05efe46e bellard
        perror(filename);
191 05efe46e bellard
        return -1;
192 05efe46e bellard
    }
193 05efe46e bellard
194 05efe46e bellard
    char magic[4];
195 05efe46e bellard
    if (read(fd, &magic, sizeof(magic)) != sizeof(magic)) {
196 05efe46e bellard
        perror("read from disk");
197 05efe46e bellard
        return -1;
198 05efe46e bellard
    }
199 05efe46e bellard
    
200 05efe46e bellard
    if (!memcmp(magic, "KDMV", sizeof(magic))) {
201 05efe46e bellard
        open_vmdk4(fd);
202 05efe46e bellard
    } else if (!memcmp(magic, "COWD", sizeof(magic))) {
203 05efe46e bellard
        open_vmdk3(fd);
204 05efe46e bellard
    } else {
205 05efe46e bellard
        fprintf(stderr, "%s is not a VMware virtual disk image\n", filename);
206 05efe46e bellard
        return -1;
207 05efe46e bellard
    }
208 05efe46e bellard
    
209 47cea614 bellard
    cached_l2dir = -1;
210 47cea614 bellard
    return fd;
211 47cea614 bellard
}
212 47cea614 bellard
213 05efe46e bellard
static void help(void)
214 47cea614 bellard
{
215 47cea614 bellard
    printf("vmdk2raw\n"
216 47cea614 bellard
           "usage: vmdk2raw vmware_image output_image\n"
217 47cea614 bellard
           "\n"
218 05efe46e bellard
           "vmware_image   a vmware cow image\n"
219 47cea614 bellard
           "output_image   the created disk image\n" 
220 47cea614 bellard
          );
221 47cea614 bellard
    exit(1);
222 47cea614 bellard
}
223 47cea614 bellard
224 05efe46e bellard
#define BUF_SIZE 0x10000
225 05efe46e bellard
static void copy_disk(in_fd, out_fd)
226 47cea614 bellard
{
227 47cea614 bellard
    char buf[BUF_SIZE];
228 47cea614 bellard
    off64_t i = 0;
229 05efe46e bellard
    int ret;
230 47cea614 bellard
    while (i < disk_limit) {
231 05efe46e bellard
        ret = copy_virtual(&vdsk, in_fd, out_fd, i, buf, sizeof(buf));
232 05efe46e bellard
        if (ret < 0) {
233 05efe46e bellard
            fprintf(stderr, "copying failed\n");
234 05efe46e bellard
            exit(-1);
235 05efe46e bellard
        }
236 05efe46e bellard
        i += ret;
237 47cea614 bellard
    }
238 47cea614 bellard
}
239 47cea614 bellard
240 47cea614 bellard
int main(int argc, char **argv)
241 47cea614 bellard
{
242 47cea614 bellard
    int out_fd, in_fd;
243 47cea614 bellard
244 47cea614 bellard
    if (argc < 3)
245 47cea614 bellard
        help();
246 47cea614 bellard
247 47cea614 bellard
    in_fd = open_vmdk(argv[1]);
248 47cea614 bellard
    if (in_fd < 0) {
249 47cea614 bellard
        return -1;
250 47cea614 bellard
    }
251 47cea614 bellard
252 05efe46e bellard
    out_fd = open(argv[2], O_WRONLY | O_LARGEFILE | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
253 47cea614 bellard
    if (out_fd < 0) {
254 47cea614 bellard
        perror(argv[2]);
255 47cea614 bellard
        return -1;
256 47cea614 bellard
    }
257 47cea614 bellard
258 47cea614 bellard
    copy_disk(in_fd, out_fd);
259 47cea614 bellard
    close(out_fd);
260 47cea614 bellard
    return 0;
261 47cea614 bellard
}