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; | ... | ... |