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,6 +574,14 @@ Since holes are used, the displayed size of the COW disk image is not | ||
574 | the real one. To know it, use the @code{ls -ls} command. | 574 | the real one. To know it, use the @code{ls -ls} command. |
575 | @end enumerate | 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 | @section Network emulation | 585 | @section Network emulation |
578 | 586 | ||
579 | QEMU simulates up to 6 networks cards (NE2000 boards). Each card can | 587 | QEMU simulates up to 6 networks cards (NE2000 boards). Each card can |
vmdk.h
@@ -32,12 +32,11 @@ | @@ -32,12 +32,11 @@ | ||
32 | 32 | ||
33 | struct cowdisk_header | 33 | struct cowdisk_header |
34 | { | 34 | { |
35 | - char magic[4]; /* COWD */ | ||
36 | uint32_t version; | 35 | uint32_t version; |
37 | uint32_t flags; | 36 | uint32_t flags; |
38 | uint32_t disk_sectors; | 37 | uint32_t disk_sectors; |
39 | uint32_t granularity; | 38 | uint32_t granularity; |
40 | - uint32_t l1dir_sector; | 39 | + uint32_t l1dir_offset; |
41 | uint32_t l1dir_size; | 40 | uint32_t l1dir_size; |
42 | uint32_t file_sectors; | 41 | uint32_t file_sectors; |
43 | uint32_t cylinders; | 42 | uint32_t cylinders; |
@@ -50,3 +49,24 @@ struct cowdisk_header2 | @@ -50,3 +49,24 @@ struct cowdisk_header2 | ||
50 | uint32_t parent_ts; | 49 | uint32_t parent_ts; |
51 | uint32_t timestamp; | 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,131 +30,210 @@ | ||
30 | #include "vmdk.h" | 30 | #include "vmdk.h" |
31 | #include "config-host.h" | 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 | size_t n; | 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 | return -1; | 55 | return -1; |
50 | } | 56 | } |
51 | 57 | ||
52 | n = read(fd, buffer, length); | 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 | return -1; | 62 | return -1; |
57 | } | 63 | } |
58 | 64 | ||
59 | return n; | 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 | granule_offset = offset % granule_size; | 88 | granule_offset = offset % granule_size; |
69 | length = MIN(length, granule_size - granule_offset); | 89 | length = MIN(length, granule_size - granule_offset); |
70 | length = MIN(length, disk_limit - offset); | 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 | goto zero_fill; | 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 | goto zero_fill; | 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 | write(out_fd, buffer, length); | 125 | write(out_fd, buffer, length); |
93 | return length; | 126 | return length; |
94 | 127 | ||
95 | zero_fill: | 128 | zero_fill: |
96 | /* the last chunk of the file can not be sparse | 129 | /* the last chunk of the file can not be sparse |
97 | * or the file will be truncated */ | 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 | } else { | 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 | perror("lseek"); | 139 | perror("lseek"); |
104 | } | 140 | } |
105 | return length; | 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 | return -1; | 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 | return -1; | 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 | return -1; | 171 | return -1; |
128 | } | 172 | } |
129 | - | ||
130 | granule_size = header.granularity << SECTOR_BITS; | 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 | return -1; | 179 | return -1; |
133 | 180 | ||
134 | disk_limit = header.disk_sectors << SECTOR_BITS; | 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 | cached_l2dir = -1; | 209 | cached_l2dir = -1; |
137 | return fd; | 210 | return fd; |
138 | } | 211 | } |
139 | 212 | ||
140 | -void help(void) | 213 | +static void help(void) |
141 | { | 214 | { |
142 | printf("vmdk2raw\n" | 215 | printf("vmdk2raw\n" |
143 | "usage: vmdk2raw vmware_image output_image\n" | 216 | "usage: vmdk2raw vmware_image output_image\n" |
144 | "\n" | 217 | "\n" |
145 | - "vmware_image a vmware 2.x/3.x cow image\n" | 218 | + "vmware_image a vmware cow image\n" |
146 | "output_image the created disk image\n" | 219 | "output_image the created disk image\n" |
147 | ); | 220 | ); |
148 | exit(1); | 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 | char buf[BUF_SIZE]; | 227 | char buf[BUF_SIZE]; |
155 | off64_t i = 0; | 228 | off64_t i = 0; |
229 | + int ret; | ||
156 | while (i < disk_limit) { | 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,7 +249,7 @@ int main(int argc, char **argv) | ||
170 | return -1; | 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 | if (out_fd < 0) { | 253 | if (out_fd < 0) { |
175 | perror(argv[2]); | 254 | perror(argv[2]); |
176 | return -1; | 255 | return -1; |