1 /*
2 * vpc.c
3 * Layered data source for Virtual PC hard disk images.
4 *
5 * Copyright (c) 2003 Christoph Pfisterer
6 *
7 * Permission is hereby granted, free of charge, to any person
8 * obtaining a copy of this software and associated documentation
9 * files (the "Software"), to deal in the Software without
10 * restriction, including without limitation the rights to use, copy,
11 * modify, merge, publish, distribute, sublicense, and/or sell copies
12 * of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28 #include "global.h"
29
30 /*
31 * types
32 */
33
34 typedef struct vhd_chunk {
35 int present;
36 u8 off;
37 u1 bitmap[1];
38 } VHD_CHUNK;
39
40 typedef struct vhd_source {
41 SOURCE c;
42 u8 off;
43 u4 chunk_size;
44 u4 chunk_count;
45 u4 *raw_map;
46 VHD_CHUNK **chunks;
47 } VHD_SOURCE;
48
49 /*
50 * helper functions
51 */
52
53 static SOURCE *init_vhd_source(SECTION *section, int level,
54 u8 total_size, u8 sparse_offset);
55 static int read_block_vhd(SOURCE *s, u8 pos, void *buf);
56 static void close_vhd(SOURCE *s);
57
58 /*
59 * cd image detection
60 */
61
detect_vhd(SECTION * section,int level)62 void detect_vhd(SECTION *section, int level)
63 {
64 unsigned char *buf;
65 int found, type;
66 u8 sparse_offset, total_size;
67 char s[256];
68 SOURCE *src;
69
70 found = 0;
71
72 /* check for info block at the beginning */
73 if (get_buffer(section, 0, 511, (void **)&buf) < 511)
74 return;
75 if (memcmp(buf, "conectix", 8) == 0) {
76 found = 1;
77 }
78
79 /* check for info block at the end if possible */
80 if (!found && section->size > 1024 && !section->source->sequential) {
81 if (get_buffer(section, section->size - 511, 511, (void **)&buf) < 511)
82 return;
83 if (memcmp(buf, "conectix", 8) == 0) {
84 found = 1;
85 }
86 }
87
88 if (!found)
89 return;
90 /* okay, now buf points to the info block, wherever it was found */
91
92 type = get_be_long(buf + 0x3c);
93 total_size = get_be_quad(buf + 0x28); /* copy at 0x30 ... ??? */
94
95 if (type == 2) {
96 print_line(level, "Connectix Virtual PC hard disk image, fixed size");
97 } else if (type == 3) {
98 print_line(level, "Connectix Virtual PC hard disk image, dynamic size");
99 } else if (type == 4) {
100 print_line(level, "Connectix Virtual PC hard disk image, differential");
101 } else {
102 print_line(level, "Connectix Virtual PC hard disk image, unknown type %d",
103 type);
104 }
105 format_size_verbose(s, total_size);
106 print_line(level + 1, "Disk size %s", s);
107
108 if (type == 3) {
109 /* dynamically sized, set up a mapping data source */
110 sparse_offset = get_be_quad(buf + 16);
111
112 src = init_vhd_source(section, level, total_size, sparse_offset);
113
114 if (src != NULL) {
115 /* analyze it */
116 analyze_source(src, level);
117 close_source(src);
118 }
119 }
120
121 if (type == 3 || type == 4)
122 stop_detect();
123 }
124
125 /*
126 * initialize the mapping source
127 */
128
init_vhd_source(SECTION * section,int level,u8 total_size,u8 sparse_offset)129 static SOURCE *init_vhd_source(SECTION *section, int level,
130 u8 total_size, u8 sparse_offset)
131 {
132 VHD_SOURCE *vs;
133 unsigned char *buf;
134 u8 map_offset;
135 u4 map_size;
136 char s[256];
137
138 /* allocate and init source structure */
139 vs = (VHD_SOURCE *)malloc(sizeof(VHD_SOURCE));
140 if (vs == NULL)
141 bailout("Out of memory");
142 memset(vs, 0, sizeof(VHD_SOURCE));
143
144 vs->c.size_known = 1;
145 vs->c.size = total_size;
146 vs->c.blocksize = 512;
147 vs->c.foundation = section->source;
148 vs->c.read_block = read_block_vhd;
149 vs->c.close = close_vhd;
150 vs->off = section->pos;
151
152 /* read sparse information block */
153 if (get_buffer(section, sparse_offset, 512, (void **)&buf) < 512) {
154 print_line(level + 1, "Error reading the sparse image info block");
155 goto errorexit;
156 }
157 map_offset = get_be_quad(buf + 16);
158 vs->chunk_count = get_be_long(buf + 28);
159 vs->chunk_size = get_be_long(buf + 32);
160
161 format_size(s, vs->chunk_size);
162 print_line(level + 1, "Dynamic sizing uses %lu chunks of %s",
163 vs->chunk_count, s);
164
165 if ((u8)vs->chunk_count * vs->chunk_size < total_size) {
166 print_line(level + 1, "Error: Sparse parameters don't match total size");
167 goto errorexit;
168 }
169 if (vs->chunk_size < 4096) {
170 print_line(level + 1, "Error: Sparse chunk size too small (%lu bytes)",
171 vs->chunk_size);
172 goto errorexit;
173 }
174 if (vs->chunk_size > 2*1024*1024) {
175 /* written-to bitmap wouldn't fit in one sector */
176 print_line(level + 1, "Error: Sparse chunk size too large (%lu bytes)",
177 vs->chunk_size);
178 goto errorexit;
179 }
180
181 /* allocate further data structures */
182 map_size = vs->chunk_count * 4;
183 vs->raw_map = (u4 *)malloc(map_size);
184 if (vs->raw_map == NULL)
185 bailout("Out of memory");
186 vs->chunks = (VHD_CHUNK **)malloc(vs->chunk_count * sizeof(VHD_CHUNK *));
187 if (vs->chunks == NULL)
188 bailout("Out of memory");
189 memset(vs->chunks, 0, vs->chunk_count * sizeof(VHD_CHUNK *));
190
191 /* read the chunk map */
192 if (get_buffer_real(section->source, vs->off + map_offset, map_size,
193 (void *)vs->raw_map, NULL) < map_size) {
194 print_line(level + 1, "Error reading the sparse image map");
195 goto errorexit;
196 }
197
198 return (SOURCE *)vs;
199
200 errorexit:
201 close_vhd((SOURCE *)vs);
202 free(vs);
203 return NULL;
204 }
205
206 /*
207 * mapping read
208 */
209
read_block_vhd(SOURCE * s,u8 pos,void * buf)210 static int read_block_vhd(SOURCE *s, u8 pos, void *buf)
211 {
212 VHD_SOURCE *vs = (VHD_SOURCE *)s;
213 SOURCE *fs = s->foundation;
214 u4 chunk, chunk_start_sector, sector;
215 u8 chunk_disk_off, sector_pos;
216 unsigned char *filebuf;
217 int present;
218
219 chunk = (u4)(pos / vs->chunk_size);
220 if (chunk >= vs->chunk_count)
221 return 0;
222
223 if (vs->chunks[chunk] == NULL) {
224 /* create data structure for the chunk */
225
226 chunk_start_sector = get_be_long(vs->raw_map + chunk);
227 /* NOTE: raw_map is a u4*, so C does the arithmetics for us */
228
229 if (chunk_start_sector == 0xffffffff) {
230 present = 0;
231 } else {
232 chunk_disk_off = vs->off + (u8)chunk_start_sector * 512;
233 if (get_buffer_real(fs, chunk_disk_off, 512,
234 NULL, (void **)&filebuf) < 512)
235 present = 0;
236 else
237 present = 1;
238 }
239
240 if (!present) {
241 vs->chunks[chunk] = (VHD_CHUNK *)malloc(sizeof(VHD_CHUNK));
242 if (vs->chunks[chunk] == NULL)
243 bailout("Out of memory");
244 vs->chunks[chunk]->present = 0;
245 } else {
246 vs->chunks[chunk] = (VHD_CHUNK *)malloc(sizeof(VHD_CHUNK) + 512);
247 if (vs->chunks[chunk] == NULL)
248 bailout("Out of memory");
249 vs->chunks[chunk]->present = 1;
250 vs->chunks[chunk]->off = chunk_disk_off + 512;
251 memcpy(vs->chunks[chunk]->bitmap, filebuf, 512);
252 }
253 }
254
255 if (!vs->chunks[chunk]->present) {
256 /* whole chunk is missing */
257 memset(buf, 0, 512);
258 return 1;
259 }
260
261 sector = (u4)((pos - (u8)chunk * vs->chunk_size) / 512);
262 if (vs->chunks[chunk]->bitmap[sector >> 3] & (128 >> (sector & 7))) {
263 /* sector is present and in use */
264 sector_pos = vs->chunks[chunk]->off + (u8)sector * 512;
265 if (get_buffer_real(fs, sector_pos, 512, buf, NULL) < 512)
266 return 0;
267 } else {
268 /* sector has not been written to (although it's present on disk) */
269 memset(buf, 0, 512);
270 }
271 return 1;
272 }
273
274 /*
275 * cleanup
276 */
277
close_vhd(SOURCE * s)278 static void close_vhd(SOURCE *s)
279 {
280 VHD_SOURCE *vs = (VHD_SOURCE *)s;
281 u4 chunk;
282
283 /* free raw chunk map */
284 if (vs->raw_map != NULL)
285 free(vs->raw_map);
286
287 /* free chunk info data */
288 if (vs->chunks != NULL) {
289 for (chunk = 0; chunk < vs->chunk_count; chunk++) {
290 if (vs->chunks[chunk] != NULL)
291 free(vs->chunks[chunk]);
292 }
293 free(vs->chunks);
294 }
295 }
296
297 /* EOF */
298