Commit 05efe46eaa337ce7680a22c5e034686957dc3032
1 parent
dbda808a
VMware 4 disk images support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@927 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
3 changed files
with
167 additions
and
60 deletions
qemu-doc.texi
... | ... | @@ -574,6 +574,14 @@ Since holes are used, the displayed size of the COW disk image is not |
574 | 574 | the real one. To know it, use the @code{ls -ls} command. |
575 | 575 | @end enumerate |
576 | 576 | |
577 | +@subsection Convert VMware disk images to raw disk images | |
578 | + | |
579 | +You can use the tool @file{vmdk2raw} to convert VMware disk images to | |
580 | +raw disk images directly usable by QEMU. The syntax is: | |
581 | +@example | |
582 | +vmdk2raw vmware_image output_image | |
583 | +@end example | |
584 | + | |
577 | 585 | @section Network emulation |
578 | 586 | |
579 | 587 | QEMU simulates up to 6 networks cards (NE2000 boards). Each card can | ... | ... |
vmdk.h
... | ... | @@ -32,12 +32,11 @@ |
32 | 32 | |
33 | 33 | struct cowdisk_header |
34 | 34 | { |
35 | - char magic[4]; /* COWD */ | |
36 | 35 | uint32_t version; |
37 | 36 | uint32_t flags; |
38 | 37 | uint32_t disk_sectors; |
39 | 38 | uint32_t granularity; |
40 | - uint32_t l1dir_sector; | |
39 | + uint32_t l1dir_offset; | |
41 | 40 | uint32_t l1dir_size; |
42 | 41 | uint32_t file_sectors; |
43 | 42 | uint32_t cylinders; |
... | ... | @@ -50,3 +49,24 @@ struct cowdisk_header2 |
50 | 49 | uint32_t parent_ts; |
51 | 50 | uint32_t timestamp; |
52 | 51 | }; |
52 | + | |
53 | +/* based on vdk 3.1 10-11-2003 by Ken Kato */ | |
54 | + | |
55 | +struct vmdisk_header | |
56 | +{ | |
57 | + uint32_t version; | |
58 | + uint32_t flags; | |
59 | + | |
60 | + int64_t capacity; | |
61 | + int64_t granularity; | |
62 | + int64_t desc_offset; | |
63 | + int64_t desc_size; | |
64 | + int32_t num_gtes_per_gte; | |
65 | + int64_t rgd_offset; | |
66 | + int64_t gd_offset; | |
67 | + int64_t grain_offset; | |
68 | + | |
69 | + char filler[1]; | |
70 | + | |
71 | + char check_bytes[4]; | |
72 | +}; | ... | ... |
vmdk2raw.c
... | ... | @@ -30,131 +30,210 @@ |
30 | 30 | #include "vmdk.h" |
31 | 31 | #include "config-host.h" |
32 | 32 | |
33 | -struct cowdisk_header header; | |
34 | -struct cowdisk_header2 header2; | |
35 | -off_t disk_base, disk_limit; | |
36 | -unsigned int granule_size; | |
37 | -uint32_t l1dir[L1_SIZE]; | |
38 | - | |
39 | -unsigned int cached_l2dir; | |
40 | -uint32_t l2dir[L2_SIZE]; | |
41 | - | |
42 | -size_t read_physical(int fd, off64_t offset, size_t length, void *buffer) | |
33 | +static struct cowdisk_header header; | |
34 | +static struct vmdisk_header header4; | |
35 | +static off64_t disk_limit; | |
36 | +static unsigned int granule_size; | |
37 | +static uint32_t *l1dir; | |
38 | + | |
39 | +static unsigned int cached_l2dir; | |
40 | +static uint32_t l2dir[L2_SIZE]; | |
41 | + | |
42 | +static struct vmdk_prm { | |
43 | + uint32_t grain_table_size; | |
44 | + uint32_t sectors_per_grain; | |
45 | + uint32_t sectors_per_table; | |
46 | + uint32_t directory_size; | |
47 | +} vdsk; | |
48 | + | |
49 | +static size_t read_physical(int fd, off64_t offset, size_t length, void *buffer) | |
43 | 50 | { |
44 | 51 | size_t n; |
45 | 52 | |
46 | - if (lseek64(fd, offset, SEEK_SET) == -1) | |
47 | - { | |
48 | - perror("lseek"); | |
53 | + if (lseek64(fd, offset, SEEK_SET) == -1) { | |
54 | + printf(" error trying to seek lseek to %lld", offset); | |
49 | 55 | return -1; |
50 | 56 | } |
51 | 57 | |
52 | 58 | n = read(fd, buffer, length); |
53 | - if (n == -1) | |
54 | - { | |
55 | - perror("read from disk"); | |
59 | + | |
60 | + if (n == -1) { | |
61 | + printf("read from disk %lld", offset); | |
56 | 62 | return -1; |
57 | 63 | } |
58 | 64 | |
59 | 65 | return n; |
60 | 66 | } |
61 | 67 | |
62 | -size_t copy_virtual(int in_fd, int out_fd, off64_t offset, void *buffer, size_t length) | |
68 | +static int read_l1dir(int fd, size_t offset, int num) | |
69 | +{ | |
70 | + l1dir = malloc(sizeof(*l1dir) * num); | |
71 | + if (!l1dir) | |
72 | + return -1; | |
73 | + return read_physical(fd, offset << SECTOR_BITS, sizeof(*l1dir) * num, (char *)l1dir) != (sizeof(*l1dir) * num); | |
74 | +} | |
75 | + | |
76 | +static int read_l2dir(int fd, size_t offset, int num) | |
63 | 77 | { |
64 | - unsigned int granule_index, granule_offset; | |
65 | - unsigned int l1index, l2index; | |
78 | + return read_physical(fd, offset << SECTOR_BITS, sizeof(l2dir[0]) * num, (char *)l2dir) != sizeof(l2dir); | |
79 | +} | |
66 | 80 | |
67 | - granule_index = offset / granule_size; | |
81 | +static size_t copy_virtual(struct vmdk_prm *dsk, int in_fd, int out_fd, off64_t offset, void *buffer, size_t length) | |
82 | +{ | |
83 | + | |
84 | + unsigned int granule_offset; | |
85 | + unsigned int grain_index; | |
86 | + unsigned int sector_map_idx; | |
87 | + | |
68 | 88 | granule_offset = offset % granule_size; |
69 | 89 | length = MIN(length, granule_size - granule_offset); |
70 | 90 | length = MIN(length, disk_limit - offset); |
71 | 91 | |
72 | - l1index = (granule_index >> L2_BITS) & L1_MASK; | |
73 | - l2index = granule_index & L2_MASK; | |
92 | + sector_map_idx = (offset >> SECTOR_BITS) / dsk->sectors_per_table; | |
93 | + | |
94 | + if (sector_map_idx >= dsk->directory_size) { | |
95 | + fprintf(stderr, "cannot locate grain table for %d in %d\n", sector_map_idx, dsk->directory_size); | |
96 | + return -1; | |
97 | + } | |
74 | 98 | |
75 | - if (l1dir[l1index] == 0) | |
99 | + if (l1dir[sector_map_idx] == 0) | |
76 | 100 | goto zero_fill; |
101 | + | |
102 | + if (sector_map_idx != cached_l2dir) { | |
103 | + if (read_l2dir(in_fd, l1dir[sector_map_idx], dsk->grain_table_size)) { | |
104 | + fprintf(stderr, "read failed\n"); | |
105 | + return -1; | |
106 | + } | |
107 | + cached_l2dir = sector_map_idx; | |
108 | + } | |
77 | 109 | |
78 | - if (l1index != cached_l2dir) | |
79 | - { | |
80 | - if (read_physical(in_fd, (l1dir[l1index] << SECTOR_BITS), sizeof(l2dir), (char *)l2dir) != sizeof(l2dir)) | |
81 | - return 0; | |
82 | - | |
83 | - cached_l2dir = l1index; | |
110 | + grain_index = ((offset >> SECTOR_BITS) % dsk->sectors_per_table) / dsk->sectors_per_grain; | |
111 | + | |
112 | + if (grain_index >= dsk->grain_table_size) { | |
113 | + fprintf(stderr, "grain to large"); | |
114 | + return -1; | |
84 | 115 | } |
85 | 116 | |
86 | - if (l2dir[l2index] == 0) | |
117 | + if (l2dir[grain_index] == 0) | |
87 | 118 | goto zero_fill; |
88 | 119 | |
89 | - if (read_physical(in_fd, (l2dir[l2index] << SECTOR_BITS) + granule_offset, length, buffer) != length) | |
90 | - return 0; | |
91 | - | |
120 | + if (read_physical(in_fd, (l2dir[grain_index] << SECTOR_BITS) + granule_offset, length, buffer) != length) { | |
121 | + fprintf(stderr, "read error 2\n"); | |
122 | + return -1; | |
123 | + } | |
124 | + | |
92 | 125 | write(out_fd, buffer, length); |
93 | 126 | return length; |
94 | 127 | |
95 | 128 | zero_fill: |
96 | 129 | /* the last chunk of the file can not be sparse |
97 | 130 | * or the file will be truncated */ |
98 | - if (offset + length < disk_limit) { | |
99 | - memset(buffer, 0, length); | |
100 | - write(out_fd, buffer, length); | |
131 | + if (offset + length >= disk_limit) { | |
132 | + if (lseek64(out_fd, length-1, SEEK_CUR) == (off_t)-1) | |
133 | + perror("lseek"); | |
134 | + /* write the last NULL byte instead of seeking */ | |
135 | + const char nil = 0; | |
136 | + write(out_fd, &nil, 1); | |
101 | 137 | } else { |
102 | - if (lseek(out_fd, length, SEEK_CUR) == (off_t)-1) | |
138 | + if (lseek64(out_fd, length, SEEK_CUR) == (off_t)-1) | |
103 | 139 | perror("lseek"); |
104 | 140 | } |
105 | 141 | return length; |
106 | 142 | } |
107 | 143 | |
108 | - | |
109 | -int open_vmdk(const char *filename) | |
144 | +static int open_vmdk4(int fd) | |
110 | 145 | { |
111 | - int fd = open(filename, O_RDONLY | O_LARGEFILE); | |
112 | - if (fd == -1) | |
113 | - { | |
114 | - perror(filename); | |
146 | + if (read(fd, &header4, sizeof(header4)) != sizeof(header4)) { | |
147 | + perror("read from disk"); | |
115 | 148 | return -1; |
116 | 149 | } |
150 | + | |
151 | + granule_size = header4.granularity << SECTOR_BITS; | |
152 | + disk_limit = header4.capacity << SECTOR_BITS; | |
153 | + | |
154 | + cached_l2dir = -1; | |
155 | + vdsk.grain_table_size = header4.num_gtes_per_gte; | |
156 | + vdsk.sectors_per_grain = header4.granularity; | |
157 | + vdsk.sectors_per_table = vdsk.grain_table_size * vdsk.sectors_per_grain; | |
158 | + vdsk.directory_size = (header4.capacity + vdsk.sectors_per_table - 1) / vdsk.sectors_per_table + 1; | |
117 | 159 | |
118 | - if (read(fd, &header, sizeof(header)) != sizeof(header)) | |
119 | - { | |
120 | - perror("read from disk"); | |
160 | + if (read_l1dir(fd, header4.rgd_offset, vdsk.directory_size)) | |
121 | 161 | return -1; |
122 | - } | |
162 | + | |
163 | + return 0; | |
164 | + | |
165 | +} | |
123 | 166 | |
124 | - if (memcmp(header.magic, "COWD", 4) != 0) | |
125 | - { | |
126 | - fprintf(stderr, "%s is not a VMware virtual disk image\n", filename); | |
167 | +static int open_vmdk3(int fd) | |
168 | +{ | |
169 | + if (read(fd, &header, sizeof(header)) != sizeof(header)) { | |
170 | + perror("read from disk\n"); | |
127 | 171 | return -1; |
128 | 172 | } |
129 | - | |
130 | 173 | granule_size = header.granularity << SECTOR_BITS; |
131 | - if (read_physical(fd, header.l1dir_sector << SECTOR_BITS, sizeof(l1dir), (char *)l1dir) != sizeof(l1dir)) | |
174 | + vdsk.sectors_per_grain = header.granularity; | |
175 | + vdsk.grain_table_size = L2_SIZE; | |
176 | + vdsk.sectors_per_table = vdsk.grain_table_size * vdsk.sectors_per_grain; | |
177 | + vdsk.directory_size = L1_SIZE; | |
178 | + if (read_l1dir(fd, header.l1dir_offset, L1_SIZE)) | |
132 | 179 | return -1; |
133 | 180 | |
134 | 181 | disk_limit = header.disk_sectors << SECTOR_BITS; |
135 | 182 | |
183 | + return fd; | |
184 | +} | |
185 | + | |
186 | +static int open_vmdk(const char *filename) | |
187 | +{ | |
188 | + int fd = open(filename, O_RDONLY | O_LARGEFILE); | |
189 | + if (fd == -1) { | |
190 | + perror(filename); | |
191 | + return -1; | |
192 | + } | |
193 | + | |
194 | + char magic[4]; | |
195 | + if (read(fd, &magic, sizeof(magic)) != sizeof(magic)) { | |
196 | + perror("read from disk"); | |
197 | + return -1; | |
198 | + } | |
199 | + | |
200 | + if (!memcmp(magic, "KDMV", sizeof(magic))) { | |
201 | + open_vmdk4(fd); | |
202 | + } else if (!memcmp(magic, "COWD", sizeof(magic))) { | |
203 | + open_vmdk3(fd); | |
204 | + } else { | |
205 | + fprintf(stderr, "%s is not a VMware virtual disk image\n", filename); | |
206 | + return -1; | |
207 | + } | |
208 | + | |
136 | 209 | cached_l2dir = -1; |
137 | 210 | return fd; |
138 | 211 | } |
139 | 212 | |
140 | -void help(void) | |
213 | +static void help(void) | |
141 | 214 | { |
142 | 215 | printf("vmdk2raw\n" |
143 | 216 | "usage: vmdk2raw vmware_image output_image\n" |
144 | 217 | "\n" |
145 | - "vmware_image a vmware 2.x/3.x cow image\n" | |
218 | + "vmware_image a vmware cow image\n" | |
146 | 219 | "output_image the created disk image\n" |
147 | 220 | ); |
148 | 221 | exit(1); |
149 | 222 | } |
150 | 223 | |
151 | -#define BUF_SIZE granule_size | |
152 | -void copy_disk(in_fd, out_fd) | |
224 | +#define BUF_SIZE 0x10000 | |
225 | +static void copy_disk(in_fd, out_fd) | |
153 | 226 | { |
154 | 227 | char buf[BUF_SIZE]; |
155 | 228 | off64_t i = 0; |
229 | + int ret; | |
156 | 230 | while (i < disk_limit) { |
157 | - i += copy_virtual(in_fd, out_fd, i, buf, sizeof(buf)); | |
231 | + ret = copy_virtual(&vdsk, in_fd, out_fd, i, buf, sizeof(buf)); | |
232 | + if (ret < 0) { | |
233 | + fprintf(stderr, "copying failed\n"); | |
234 | + exit(-1); | |
235 | + } | |
236 | + i += ret; | |
158 | 237 | } |
159 | 238 | } |
160 | 239 | |
... | ... | @@ -170,7 +249,7 @@ int main(int argc, char **argv) |
170 | 249 | return -1; |
171 | 250 | } |
172 | 251 | |
173 | - out_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); | |
252 | + out_fd = open(argv[2], O_WRONLY | O_LARGEFILE | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); | |
174 | 253 | if (out_fd < 0) { |
175 | 254 | perror(argv[2]); |
176 | 255 | return -1; | ... | ... |