root / vmdk2raw.c @ 3e11db9a
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 | } |