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