]>
Commit | Line | Data |
---|---|---|
6a0f9e82 FB |
1 | /* |
2 | * Block driver for Conectix/Microsoft Virtual PC images | |
5fafdf24 | 3 | * |
6a0f9e82 | 4 | * Copyright (c) 2005 Alex Beregszaszi |
15d35bc5 | 5 | * Copyright (c) 2009 Kevin Wolf <kwolf@suse.de> |
5fafdf24 | 6 | * |
6a0f9e82 FB |
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
8 | * of this software and associated documentation files (the "Software"), to deal | |
9 | * in the Software without restriction, including without limitation the rights | |
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 | * copies of the Software, and to permit persons to whom the Software is | |
12 | * furnished to do so, subject to the following conditions: | |
13 | * | |
14 | * The above copyright notice and this permission notice shall be included in | |
15 | * all copies or substantial portions of the Software. | |
16 | * | |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
23 | * THE SOFTWARE. | |
24 | */ | |
faf07963 | 25 | #include "qemu-common.h" |
6a0f9e82 FB |
26 | #include "block_int.h" |
27 | ||
28 | /**************************************************************/ | |
29 | ||
30 | #define HEADER_SIZE 512 | |
31 | ||
32 | //#define CACHE | |
33 | ||
2cfacb62 AL |
34 | enum vhd_type { |
35 | VHD_FIXED = 2, | |
36 | VHD_DYNAMIC = 3, | |
37 | VHD_DIFFERENCING = 4, | |
38 | }; | |
39 | ||
6a0f9e82 | 40 | // always big-endian |
b9fa33a6 | 41 | struct vhd_footer { |
2cfacb62 AL |
42 | char creator[8]; // "conectix" |
43 | uint32_t features; | |
44 | uint32_t version; | |
45 | ||
46 | // Offset of next header structure, 0xFFFFFFFF if none | |
47 | uint64_t data_offset; | |
48 | ||
49 | // Seconds since Jan 1, 2000 0:00:00 (UTC) | |
50 | uint32_t timestamp; | |
51 | ||
52 | char creator_app[4]; // "vpc " | |
53 | uint16_t major; | |
54 | uint16_t minor; | |
55 | char creator_os[4]; // "Wi2k" | |
56 | ||
57 | uint64_t orig_size; | |
58 | uint64_t size; | |
59 | ||
60 | uint16_t cyls; | |
61 | uint8_t heads; | |
62 | uint8_t secs_per_cyl; | |
63 | ||
64 | uint32_t type; | |
65 | ||
66 | // Checksum of the Hard Disk Footer ("one's complement of the sum of all | |
67 | // the bytes in the footer without the checksum field") | |
68 | uint32_t checksum; | |
69 | ||
70 | // UUID used to identify a parent hard disk (backing file) | |
71 | uint8_t uuid[16]; | |
72 | ||
73 | uint8_t in_saved_state; | |
b9fa33a6 AL |
74 | }; |
75 | ||
76 | struct vhd_dyndisk_header { | |
2cfacb62 AL |
77 | char magic[8]; // "cxsparse" |
78 | ||
79 | // Offset of next header structure, 0xFFFFFFFF if none | |
80 | uint64_t data_offset; | |
81 | ||
82 | // Offset of the Block Allocation Table (BAT) | |
83 | uint64_t table_offset; | |
84 | ||
85 | uint32_t version; | |
86 | uint32_t max_table_entries; // 32bit/entry | |
87 | ||
88 | // 2 MB by default, must be a power of two | |
89 | uint32_t block_size; | |
90 | ||
91 | uint32_t checksum; | |
92 | uint8_t parent_uuid[16]; | |
93 | uint32_t parent_timestamp; | |
94 | uint32_t reserved; | |
95 | ||
96 | // Backing file name (in UTF-16) | |
97 | uint8_t parent_name[512]; | |
98 | ||
99 | struct { | |
100 | uint32_t platform; | |
101 | uint32_t data_space; | |
102 | uint32_t data_length; | |
103 | uint32_t reserved; | |
104 | uint64_t data_offset; | |
105 | } parent_locator[8]; | |
6a0f9e82 FB |
106 | }; |
107 | ||
108 | typedef struct BDRVVPCState { | |
b71d1c2e | 109 | BlockDriverState *hd; |
3b46e624 | 110 | |
15d35bc5 AL |
111 | uint8_t footer_buf[HEADER_SIZE]; |
112 | uint64_t free_data_block_offset; | |
2cfacb62 | 113 | int max_table_entries; |
6a0f9e82 | 114 | uint32_t *pagetable; |
15d35bc5 AL |
115 | uint64_t bat_offset; |
116 | uint64_t last_bitmap_offset; | |
6a0f9e82 | 117 | |
2cfacb62 | 118 | uint32_t block_size; |
15d35bc5 AL |
119 | uint32_t bitmap_size; |
120 | ||
6a0f9e82 FB |
121 | #ifdef CACHE |
122 | uint8_t *pageentry_u8; | |
123 | uint32_t *pageentry_u32; | |
124 | uint16_t *pageentry_u16; | |
3b46e624 | 125 | |
6a0f9e82 FB |
126 | uint64_t last_bitmap; |
127 | #endif | |
128 | } BDRVVPCState; | |
129 | ||
130 | static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) | |
131 | { | |
ffe8ab83 | 132 | if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8)) |
6a0f9e82 | 133 | return 100; |
6a0f9e82 FB |
134 | return 0; |
135 | } | |
136 | ||
83f64091 | 137 | static int vpc_open(BlockDriverState *bs, const char *filename, int flags) |
6a0f9e82 FB |
138 | { |
139 | BDRVVPCState *s = bs->opaque; | |
b71d1c2e | 140 | int ret, i; |
b9fa33a6 AL |
141 | struct vhd_footer* footer; |
142 | struct vhd_dyndisk_header* dyndisk_header; | |
143 | uint8_t buf[HEADER_SIZE]; | |
6a0f9e82 | 144 | |
b71d1c2e AL |
145 | ret = bdrv_file_open(&s->hd, filename, flags); |
146 | if (ret < 0) | |
147 | return ret; | |
6a0f9e82 | 148 | |
15d35bc5 | 149 | if (bdrv_pread(s->hd, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE) |
6a0f9e82 FB |
150 | goto fail; |
151 | ||
15d35bc5 | 152 | footer = (struct vhd_footer*) s->footer_buf; |
b9fa33a6 | 153 | if (strncmp(footer->creator, "conectix", 8)) |
6a0f9e82 | 154 | goto fail; |
6a0f9e82 | 155 | |
1fa79228 AL |
156 | // The visible size of a image in Virtual PC depends on the geometry |
157 | // rather than on the size stored in the footer (the size in the footer | |
158 | // is too large usually) | |
159 | bs->total_sectors = (int64_t) | |
160 | be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; | |
161 | ||
b71d1c2e AL |
162 | if (bdrv_pread(s->hd, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE) |
163 | != HEADER_SIZE) | |
6a0f9e82 FB |
164 | goto fail; |
165 | ||
b9fa33a6 AL |
166 | dyndisk_header = (struct vhd_dyndisk_header*) buf; |
167 | ||
168 | if (strncmp(dyndisk_header->magic, "cxsparse", 8)) | |
169 | goto fail; | |
6a0f9e82 | 170 | |
6a0f9e82 | 171 | |
15d35bc5 AL |
172 | s->block_size = be32_to_cpu(dyndisk_header->block_size); |
173 | s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511; | |
174 | ||
2cfacb62 AL |
175 | s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries); |
176 | s->pagetable = qemu_malloc(s->max_table_entries * 4); | |
6a0f9e82 | 177 | if (!s->pagetable) |
b71d1c2e AL |
178 | goto fail; |
179 | ||
15d35bc5 AL |
180 | s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); |
181 | if (bdrv_pread(s->hd, s->bat_offset, s->pagetable, | |
182 | s->max_table_entries * 4) != s->max_table_entries * 4) | |
b71d1c2e AL |
183 | goto fail; |
184 | ||
15d35bc5 AL |
185 | s->free_data_block_offset = |
186 | (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511; | |
187 | ||
188 | for (i = 0; i < s->max_table_entries; i++) { | |
189 | be32_to_cpus(&s->pagetable[i]); | |
190 | if (s->pagetable[i] != 0xFFFFFFFF) { | |
191 | int64_t next = (512 * (int64_t) s->pagetable[i]) + | |
192 | s->bitmap_size + s->block_size; | |
193 | ||
194 | if (next> s->free_data_block_offset) | |
195 | s->free_data_block_offset = next; | |
196 | } | |
197 | } | |
198 | ||
199 | s->last_bitmap_offset = (int64_t) -1; | |
6a0f9e82 | 200 | |
6a0f9e82 FB |
201 | #ifdef CACHE |
202 | s->pageentry_u8 = qemu_malloc(512); | |
203 | if (!s->pageentry_u8) | |
204 | goto fail; | |
205 | s->pageentry_u32 = s->pageentry_u8; | |
206 | s->pageentry_u16 = s->pageentry_u8; | |
207 | s->last_pagetable = -1; | |
208 | #endif | |
209 | ||
210 | return 0; | |
211 | fail: | |
b71d1c2e | 212 | bdrv_delete(s->hd); |
6a0f9e82 FB |
213 | return -1; |
214 | } | |
215 | ||
b71d1c2e AL |
216 | /* |
217 | * Returns the absolute byte offset of the given sector in the image file. | |
218 | * If the sector is not allocated, -1 is returned instead. | |
15d35bc5 AL |
219 | * |
220 | * The parameter write must be 1 if the offset will be used for a write | |
221 | * operation (the block bitmaps is updated then), 0 otherwise. | |
b71d1c2e | 222 | */ |
15d35bc5 AL |
223 | static inline int64_t get_sector_offset(BlockDriverState *bs, |
224 | int64_t sector_num, int write) | |
6a0f9e82 FB |
225 | { |
226 | BDRVVPCState *s = bs->opaque; | |
227 | uint64_t offset = sector_num * 512; | |
228 | uint64_t bitmap_offset, block_offset; | |
229 | uint32_t pagetable_index, pageentry_index; | |
230 | ||
2cfacb62 AL |
231 | pagetable_index = offset / s->block_size; |
232 | pageentry_index = (offset % s->block_size) / 512; | |
3b46e624 | 233 | |
15d35bc5 AL |
234 | if (pagetable_index >= s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff) |
235 | return -1; // not allocated | |
6a0f9e82 FB |
236 | |
237 | bitmap_offset = 512 * s->pagetable[pagetable_index]; | |
15d35bc5 AL |
238 | block_offset = bitmap_offset + s->bitmap_size + (512 * pageentry_index); |
239 | ||
240 | // We must ensure that we don't write to any sectors which are marked as | |
241 | // unused in the bitmap. We get away with setting all bits in the block | |
242 | // bitmap each time we write to a new block. This might cause Virtual PC to | |
243 | // miss sparse read optimization, but it's not a problem in terms of | |
244 | // correctness. | |
245 | if (write && (s->last_bitmap_offset != bitmap_offset)) { | |
246 | uint8_t bitmap[s->bitmap_size]; | |
247 | ||
248 | s->last_bitmap_offset = bitmap_offset; | |
249 | memset(bitmap, 0xff, s->bitmap_size); | |
250 | bdrv_pwrite(s->hd, bitmap_offset, bitmap, s->bitmap_size); | |
251 | } | |
3b46e624 | 252 | |
26a76461 | 253 | // printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n", |
6a0f9e82 FB |
254 | // sector_num, pagetable_index, pageentry_index, |
255 | // bitmap_offset, block_offset); | |
256 | ||
257 | // disabled by reason | |
258 | #if 0 | |
259 | #ifdef CACHE | |
260 | if (bitmap_offset != s->last_bitmap) | |
261 | { | |
262 | lseek(s->fd, bitmap_offset, SEEK_SET); | |
263 | ||
264 | s->last_bitmap = bitmap_offset; | |
5fafdf24 | 265 | |
6a0f9e82 FB |
266 | // Scary! Bitmap is stored as big endian 32bit entries, |
267 | // while we used to look it up byte by byte | |
268 | read(s->fd, s->pageentry_u8, 512); | |
269 | for (i = 0; i < 128; i++) | |
270 | be32_to_cpus(&s->pageentry_u32[i]); | |
271 | } | |
272 | ||
273 | if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1) | |
274 | return -1; | |
275 | #else | |
276 | lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET); | |
5fafdf24 | 277 | |
6a0f9e82 FB |
278 | read(s->fd, &bitmap_entry, 1); |
279 | ||
280 | if ((bitmap_entry >> (pageentry_index % 8)) & 1) | |
281 | return -1; // not allocated | |
282 | #endif | |
283 | #endif | |
6a0f9e82 | 284 | |
b71d1c2e | 285 | return block_offset; |
6a0f9e82 FB |
286 | } |
287 | ||
15d35bc5 AL |
288 | /* |
289 | * Writes the footer to the end of the image file. This is needed when the | |
290 | * file grows as it overwrites the old footer | |
291 | * | |
292 | * Returns 0 on success and < 0 on error | |
293 | */ | |
294 | static int rewrite_footer(BlockDriverState* bs) | |
295 | { | |
296 | int ret; | |
297 | BDRVVPCState *s = bs->opaque; | |
298 | int64_t offset = s->free_data_block_offset; | |
299 | ||
300 | ret = bdrv_pwrite(s->hd, offset, s->footer_buf, HEADER_SIZE); | |
301 | if (ret < 0) | |
302 | return ret; | |
303 | ||
304 | return 0; | |
305 | } | |
306 | ||
307 | /* | |
308 | * Allocates a new block. This involves writing a new footer and updating | |
309 | * the Block Allocation Table to use the space at the old end of the image | |
310 | * file (overwriting the old footer) | |
311 | * | |
312 | * Returns the sectors' offset in the image file on success and < 0 on error | |
313 | */ | |
314 | static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num) | |
315 | { | |
316 | BDRVVPCState *s = bs->opaque; | |
317 | int64_t bat_offset; | |
318 | uint32_t index, bat_value; | |
319 | int ret; | |
320 | uint8_t bitmap[s->bitmap_size]; | |
321 | ||
322 | // Check if sector_num is valid | |
323 | if ((sector_num < 0) || (sector_num > bs->total_sectors)) | |
324 | return -1; | |
325 | ||
326 | // Write entry into in-memory BAT | |
327 | index = (sector_num * 512) / s->block_size; | |
328 | if (s->pagetable[index] != 0xFFFFFFFF) | |
329 | return -1; | |
330 | ||
331 | s->pagetable[index] = s->free_data_block_offset / 512; | |
332 | ||
333 | // Initialize the block's bitmap | |
334 | memset(bitmap, 0xff, s->bitmap_size); | |
335 | bdrv_pwrite(s->hd, s->free_data_block_offset, bitmap, s->bitmap_size); | |
336 | ||
337 | // Write new footer (the old one will be overwritten) | |
338 | s->free_data_block_offset += s->block_size + s->bitmap_size; | |
339 | ret = rewrite_footer(bs); | |
340 | if (ret < 0) | |
341 | goto fail; | |
342 | ||
343 | // Write BAT entry to disk | |
344 | bat_offset = s->bat_offset + (4 * index); | |
345 | bat_value = be32_to_cpu(s->pagetable[index]); | |
346 | ret = bdrv_pwrite(s->hd, bat_offset, &bat_value, 4); | |
347 | if (ret < 0) | |
348 | goto fail; | |
349 | ||
350 | return get_sector_offset(bs, sector_num, 0); | |
351 | ||
352 | fail: | |
353 | s->free_data_block_offset -= (s->block_size + s->bitmap_size); | |
354 | return -1; | |
355 | } | |
356 | ||
5fafdf24 | 357 | static int vpc_read(BlockDriverState *bs, int64_t sector_num, |
6a0f9e82 FB |
358 | uint8_t *buf, int nb_sectors) |
359 | { | |
360 | BDRVVPCState *s = bs->opaque; | |
361 | int ret; | |
b71d1c2e | 362 | int64_t offset; |
6a0f9e82 FB |
363 | |
364 | while (nb_sectors > 0) { | |
15d35bc5 | 365 | offset = get_sector_offset(bs, sector_num, 0); |
b71d1c2e AL |
366 | |
367 | if (offset == -1) { | |
6a0f9e82 | 368 | memset(buf, 0, 512); |
b71d1c2e AL |
369 | } else { |
370 | ret = bdrv_pread(s->hd, offset, buf, 512); | |
371 | if (ret != 512) | |
372 | return -1; | |
373 | } | |
374 | ||
6a0f9e82 FB |
375 | nb_sectors--; |
376 | sector_num++; | |
377 | buf += 512; | |
378 | } | |
379 | return 0; | |
380 | } | |
381 | ||
15d35bc5 AL |
382 | static int vpc_write(BlockDriverState *bs, int64_t sector_num, |
383 | const uint8_t *buf, int nb_sectors) | |
384 | { | |
385 | BDRVVPCState *s = bs->opaque; | |
386 | int64_t offset; | |
387 | int ret; | |
388 | ||
389 | while (nb_sectors > 0) { | |
390 | offset = get_sector_offset(bs, sector_num, 1); | |
391 | ||
392 | if (offset == -1) { | |
393 | offset = alloc_block(bs, sector_num); | |
394 | if (offset < 0) | |
395 | return -1; | |
396 | } | |
397 | ||
398 | ret = bdrv_pwrite(s->hd, offset, buf, 512); | |
399 | if (ret != 512) | |
400 | return -1; | |
401 | ||
402 | nb_sectors--; | |
403 | sector_num++; | |
404 | buf += 512; | |
405 | } | |
406 | ||
407 | return 0; | |
408 | } | |
409 | ||
6a0f9e82 FB |
410 | static void vpc_close(BlockDriverState *bs) |
411 | { | |
412 | BDRVVPCState *s = bs->opaque; | |
413 | qemu_free(s->pagetable); | |
414 | #ifdef CACHE | |
415 | qemu_free(s->pageentry_u8); | |
416 | #endif | |
b71d1c2e | 417 | bdrv_delete(s->hd); |
6a0f9e82 FB |
418 | } |
419 | ||
420 | BlockDriver bdrv_vpc = { | |
421 | "vpc", | |
422 | sizeof(BDRVVPCState), | |
423 | vpc_probe, | |
424 | vpc_open, | |
425 | vpc_read, | |
15d35bc5 | 426 | vpc_write, |
6a0f9e82 FB |
427 | vpc_close, |
428 | }; |