allow 32 but unaligned access (aka Win PCI network bug - initial patch by Renzo Davoli)
[qemu] / vmdk2raw.c
1 /*
2    vmdk2raw: convert vmware images to raw disk images
3    Copyright (C) Net Integration Technologies 2004
4    Copyright (C) Matthew Chapman 2003
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <inttypes.h>
26 #include <sys/types.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <errno.h>
30 #include "vmdk.h"
31 #include "config-host.h"
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)
43 {
44     size_t n;
45
46     if (lseek64(fd, offset, SEEK_SET) == -1)
47     {
48         perror("lseek");
49         return -1;
50     }
51
52     n = read(fd, buffer, length);
53     if (n == -1)
54     {
55         perror("read from disk");
56         return -1;
57     }
58
59     return n;
60 }
61
62 size_t copy_virtual(int in_fd, int out_fd, off64_t offset, void *buffer, size_t length)
63 {
64     unsigned int granule_index, granule_offset;
65     unsigned int l1index, l2index;
66
67     granule_index = offset / granule_size;
68     granule_offset = offset % granule_size;
69     length = MIN(length, granule_size - granule_offset);
70     length = MIN(length, disk_limit - offset);
71
72     l1index = (granule_index >> L2_BITS) & L1_MASK;
73     l2index = granule_index & L2_MASK;
74
75     if (l1dir[l1index] == 0)
76         goto zero_fill;
77
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;
84     }
85
86     if (l2dir[l2index] == 0)
87         goto zero_fill;
88
89     if (read_physical(in_fd, (l2dir[l2index] << SECTOR_BITS) + granule_offset, length, buffer) != length)
90         return 0;
91
92     write(out_fd, buffer, length);
93     return length;
94
95 zero_fill:
96     /* the last chunk of the file can not be sparse
97      * or the file will be truncated */
98     if (offset + length < disk_limit) {
99         memset(buffer, 0, length);
100         write(out_fd, buffer, length);
101     } else {
102         if (lseek(out_fd, length, SEEK_CUR) == (off_t)-1)
103             perror("lseek");
104     }
105     return length;
106 }
107
108
109 int open_vmdk(const char *filename)
110 {
111     int fd = open(filename, O_RDONLY | O_LARGEFILE);
112     if (fd == -1)
113     {
114         perror(filename);
115         return -1;
116     }
117
118     if (read(fd, &header, sizeof(header)) != sizeof(header))
119     {
120         perror("read from disk");
121         return -1;
122     }
123
124     if (memcmp(header.magic, "COWD", 4) != 0)
125     {
126         fprintf(stderr, "%s is not a VMware virtual disk image\n", filename);
127         return -1;
128     }
129
130     granule_size = header.granularity << SECTOR_BITS;
131     if (read_physical(fd, header.l1dir_sector << SECTOR_BITS, sizeof(l1dir), (char *)l1dir) != sizeof(l1dir))
132         return -1;
133
134     disk_limit = header.disk_sectors << SECTOR_BITS;
135
136     cached_l2dir = -1;
137     return fd;
138 }
139
140 void help(void)
141 {
142     printf("vmdk2raw\n"
143            "usage: vmdk2raw vmware_image output_image\n"
144            "\n"
145            "vmware_image   a vmware 2.x/3.x cow image\n"
146            "output_image   the created disk image\n" 
147           );
148     exit(1);
149 }
150
151 #define BUF_SIZE granule_size
152 void copy_disk(in_fd, out_fd)
153 {
154     char buf[BUF_SIZE];
155     off64_t i = 0;
156     while (i < disk_limit) {
157         i += copy_virtual(in_fd, out_fd, i, buf, sizeof(buf));
158     }
159 }
160
161 int main(int argc, char **argv)
162 {
163     int out_fd, in_fd;
164
165     if (argc < 3)
166         help();
167
168     in_fd = open_vmdk(argv[1]);
169     if (in_fd < 0) {
170         return -1;
171     }
172
173     out_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
174     if (out_fd < 0) {
175         perror(argv[2]);
176         return -1;
177     }
178
179     copy_disk(in_fd, out_fd);
180     close(out_fd);
181     return 0;
182 }