ia64/xen-unstable

view tools/blktap2/vhd/lib/libvhd.c @ 19647:1c627434605e

blktap2: a completely rewritten blktap implementation

Benefits to blktap2 over the old version of blktap:

* Isolation from xenstore - Blktap devices are now created directly on
the linux dom0 command line, rather than being spawned in response
to XenStore events. This is handy for debugging, makes blktap
generally easier to work with, and is a step toward a generic
user-level block device implementation that is not Xen-specific.

* Improved tapdisk infrastructure: simpler request forwarding, new
request scheduler, request merging, more efficient use of AIO.

* Improved tapdisk error handling and memory management. No
allocations on the block data path, IO retry logic to protect
guests
transient block device failures. This has been tested and is known
to work on weird environments such as NFS soft mounts.

* Pause and snapshot of live virtual disks (see xmsnap script).

* VHD support. The VHD code in this release has been rigorously
tested, and represents a very mature implementation of the VHD
image
format.

* No more duplication of mechanism with blkback. The blktap kernel
module has changed dramatically from the original blktap. Blkback
is now always used to talk to Xen guests, blktap just presents a
Linux gendisk that blkback can export. This is done while
preserving the zero-copy data path from domU to physical device.

These patches deprecate the old blktap code, which can hopefully be
removed from the tree completely at some point in the future.

Signed-off-by: Jake Wires <jake.wires@citrix.com>
Signed-off-by: Dutch Meyer <dmeyer@cs.ubc.ca>
author Keir Fraser <keir.fraser@citrix.com>
date Tue May 26 11:52:31 2009 +0100 (2009-05-26)
parents
children b7f73a7f3078
line source
1 /* Copyright (c) 2008, XenSource Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 * * Neither the name of XenSource Inc. nor the names of its contributors
12 * may be used to endorse or promote products derived from this software
13 * without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
19 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 #ifndef _GNU_SOURCE
28 #define _GNU_SOURCE
29 #endif
30 #include <stdio.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <libgen.h>
37 #include <iconv.h>
38 #include <sys/mman.h>
40 #include "libvhd.h"
41 #include "relative-path.h"
43 static int libvhd_dbg = 0;
45 void
46 libvhd_set_log_level(int level)
47 {
48 if (level)
49 libvhd_dbg = 1;
50 }
52 #define VHDLOG(_f, _a...) \
53 do { \
54 if (libvhd_dbg) \
55 syslog(LOG_INFO, "libvhd::%s: "_f, \
56 __func__, ##_a); \
57 } while (0)
59 #define BIT_MASK 0x80
61 #ifdef ENABLE_FAILURE_TESTING
62 const char* ENV_VAR_FAIL[NUM_FAIL_TESTS] = {
63 "VHD_UTIL_TEST_FAIL_REPARENT_BEGIN",
64 "VHD_UTIL_TEST_FAIL_REPARENT_LOCATOR",
65 "VHD_UTIL_TEST_FAIL_REPARENT_END",
66 "VHD_UTIL_TEST_FAIL_RESIZE_BEGIN",
67 "VHD_UTIL_TEST_FAIL_RESIZE_DATA_MOVED",
68 "VHD_UTIL_TEST_FAIL_RESIZE_METADATA_MOVED",
69 "VHD_UTIL_TEST_FAIL_RESIZE_END"
70 };
71 int TEST_FAIL[NUM_FAIL_TESTS];
72 #endif // ENABLE_FAILURE_TESTING
74 static inline int
75 test_bit (volatile char *addr, int nr)
76 {
77 return ((addr[nr >> 3] << (nr & 7)) & BIT_MASK) != 0;
78 }
80 static inline void
81 set_bit (volatile char *addr, int nr)
82 {
83 addr[nr >> 3] |= (BIT_MASK >> (nr & 7));
84 }
86 static inline void
87 clear_bit (volatile char *addr, int nr)
88 {
89 addr[nr >> 3] &= ~(BIT_MASK >> (nr & 7));
90 }
92 static inline int
93 old_test_bit(volatile char *addr, int nr)
94 {
95 return (((uint32_t *)addr)[nr >> 5] >> (nr & 31)) & 1;
96 }
98 static inline void
99 old_set_bit(volatile char *addr, int nr)
100 {
101 ((uint32_t *)addr)[nr >> 5] |= (1 << (nr & 31));
102 }
104 static inline void
105 old_clear_bit(volatile char *addr, int nr)
106 {
107 ((uint32_t *)addr)[nr >> 5] &= ~(1 << (nr & 31));
108 }
110 void
111 vhd_footer_in(vhd_footer_t *footer)
112 {
113 BE32_IN(&footer->features);
114 BE32_IN(&footer->ff_version);
115 BE64_IN(&footer->data_offset);
116 BE32_IN(&footer->timestamp);
117 BE32_IN(&footer->crtr_ver);
118 BE32_IN(&footer->crtr_os);
119 BE64_IN(&footer->orig_size);
120 BE64_IN(&footer->curr_size);
121 BE32_IN(&footer->geometry);
122 BE32_IN(&footer->type);
123 BE32_IN(&footer->checksum);
124 }
126 void
127 vhd_footer_out(vhd_footer_t *footer)
128 {
129 BE32_OUT(&footer->features);
130 BE32_OUT(&footer->ff_version);
131 BE64_OUT(&footer->data_offset);
132 BE32_OUT(&footer->timestamp);
133 BE32_OUT(&footer->crtr_ver);
134 BE32_OUT(&footer->crtr_os);
135 BE64_OUT(&footer->orig_size);
136 BE64_OUT(&footer->curr_size);
137 BE32_OUT(&footer->geometry);
138 BE32_OUT(&footer->type);
139 BE32_OUT(&footer->checksum);
140 }
142 void
143 vhd_header_in(vhd_header_t *header)
144 {
145 int i, n;
147 BE64_IN(&header->data_offset);
148 BE64_IN(&header->table_offset);
149 BE32_IN(&header->hdr_ver);
150 BE32_IN(&header->max_bat_size);
151 BE32_IN(&header->block_size);
152 BE32_IN(&header->checksum);
153 BE32_IN(&header->prt_ts);
155 n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
157 for (i = 0; i < n; i++) {
158 BE32_IN(&header->loc[i].code);
159 BE32_IN(&header->loc[i].data_space);
160 BE32_IN(&header->loc[i].data_len);
161 BE64_IN(&header->loc[i].data_offset);
162 }
163 }
165 void
166 vhd_header_out(vhd_header_t *header)
167 {
168 int i, n;
170 BE64_OUT(&header->data_offset);
171 BE64_OUT(&header->table_offset);
172 BE32_OUT(&header->hdr_ver);
173 BE32_OUT(&header->max_bat_size);
174 BE32_OUT(&header->block_size);
175 BE32_OUT(&header->checksum);
176 BE32_OUT(&header->prt_ts);
178 n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
180 for (i = 0; i < n; i++) {
181 BE32_OUT(&header->loc[i].code);
182 BE32_OUT(&header->loc[i].data_space);
183 BE32_OUT(&header->loc[i].data_len);
184 BE64_OUT(&header->loc[i].data_offset);
185 }
186 }
188 void
189 vhd_batmap_header_in(vhd_batmap_t *batmap)
190 {
191 BE64_IN(&batmap->header.batmap_offset);
192 BE32_IN(&batmap->header.batmap_size);
193 BE32_IN(&batmap->header.batmap_version);
194 BE32_IN(&batmap->header.checksum);
195 }
197 void
198 vhd_batmap_header_out(vhd_batmap_t *batmap)
199 {
200 BE64_OUT(&batmap->header.batmap_offset);
201 BE32_OUT(&batmap->header.batmap_size);
202 BE32_OUT(&batmap->header.batmap_version);
203 BE32_OUT(&batmap->header.checksum);
204 }
206 void
207 vhd_bat_in(vhd_bat_t *bat)
208 {
209 int i;
211 for (i = 0; i < bat->entries; i++)
212 BE32_IN(&bat->bat[i]);
213 }
215 void
216 vhd_bat_out(vhd_bat_t *bat)
217 {
218 int i;
220 for (i = 0; i < bat->entries; i++)
221 BE32_OUT(&bat->bat[i]);
222 }
224 uint32_t
225 vhd_checksum_footer(vhd_footer_t *footer)
226 {
227 int i;
228 unsigned char *blob;
229 uint32_t checksum, tmp;
231 checksum = 0;
232 tmp = footer->checksum;
233 footer->checksum = 0;
235 blob = (unsigned char *)footer;
236 for (i = 0; i < sizeof(vhd_footer_t); i++)
237 checksum += (uint32_t)blob[i];
239 footer->checksum = tmp;
240 return ~checksum;
241 }
243 int
244 vhd_validate_footer(vhd_footer_t *footer)
245 {
246 int csize;
247 uint32_t checksum;
249 csize = sizeof(footer->cookie);
250 if (memcmp(footer->cookie, HD_COOKIE, csize) != 0 &&
251 memcmp(footer->cookie, VHD_POISON_COOKIE, csize) != 0) {
252 char buf[9];
253 memcpy(buf, footer->cookie, 8);
254 buf[8]= '\0';
255 VHDLOG("invalid footer cookie: %s\n", buf);
256 return -EINVAL;
257 }
259 checksum = vhd_checksum_footer(footer);
260 if (checksum != footer->checksum) {
261 /*
262 * early td-util did not re-calculate
263 * checksum when marking vhds 'hidden'
264 */
265 if (footer->hidden &&
266 !strncmp(footer->crtr_app, "tap", 3) &&
267 (footer->crtr_ver == VHD_VERSION(0, 1) ||
268 footer->crtr_ver == VHD_VERSION(1, 1))) {
269 char tmp = footer->hidden;
270 footer->hidden = 0;
271 checksum = vhd_checksum_footer(footer);
272 footer->hidden = tmp;
274 if (checksum == footer->checksum)
275 return 0;
276 }
278 VHDLOG("invalid footer checksum: "
279 "footer = 0x%08x, calculated = 0x%08x\n",
280 footer->checksum, checksum);
281 return -EINVAL;
282 }
284 return 0;
285 }
287 uint32_t
288 vhd_checksum_header(vhd_header_t *header)
289 {
290 int i;
291 unsigned char *blob;
292 uint32_t checksum, tmp;
294 checksum = 0;
295 tmp = header->checksum;
296 header->checksum = 0;
298 blob = (unsigned char *)header;
299 for (i = 0; i < sizeof(vhd_header_t); i++)
300 checksum += (uint32_t)blob[i];
302 header->checksum = tmp;
303 return ~checksum;
304 }
306 int
307 vhd_validate_header(vhd_header_t *header)
308 {
309 int i, n;
310 uint32_t checksum;
312 if (memcmp(header->cookie, DD_COOKIE, 8) != 0) {
313 char buf[9];
314 memcpy(buf, header->cookie, 8);
315 buf[8] = '\0';
316 VHDLOG("invalid header cookie: %s\n", buf);
317 return -EINVAL;
318 }
320 if (header->hdr_ver != 0x00010000) {
321 VHDLOG("invalid header version 0x%08x\n", header->hdr_ver);
322 return -EINVAL;
323 }
325 if (header->data_offset != 0xFFFFFFFFFFFFFFFF) {
326 VHDLOG("invalid header data_offset 0x%016"PRIx64"\n",
327 header->data_offset);
328 return -EINVAL;
329 }
331 n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
332 for (i = 0; i < n; i++)
333 if (vhd_validate_platform_code(header->loc[i].code))
334 return -EINVAL;
336 checksum = vhd_checksum_header(header);
337 if (checksum != header->checksum) {
338 VHDLOG("invalid header checksum: "
339 "header = 0x%08x, calculated = 0x%08x\n",
340 header->checksum, checksum);
341 return -EINVAL;
342 }
344 return 0;
345 }
347 static inline int
348 vhd_validate_bat(vhd_bat_t *bat)
349 {
350 if (!bat->bat)
351 return -EINVAL;
353 return 0;
354 }
356 uint32_t
357 vhd_checksum_batmap(vhd_batmap_t *batmap)
358 {
359 int i, n;
360 char *blob;
361 uint32_t checksum;
363 blob = batmap->map;
364 checksum = 0;
366 n = vhd_sectors_to_bytes(batmap->header.batmap_size);
368 for (i = 0; i < n; i++) {
369 if (batmap->header.batmap_version == VHD_BATMAP_VERSION(1, 1))
370 checksum += (uint32_t)blob[i];
371 else
372 checksum += (uint32_t)(unsigned char)blob[i];
373 }
375 return ~checksum;
376 }
378 int
379 vhd_validate_batmap_header(vhd_batmap_t *batmap)
380 {
381 if (memcmp(batmap->header.cookie, VHD_BATMAP_COOKIE, 8))
382 return -EINVAL;
384 if (batmap->header.batmap_version > VHD_BATMAP_CURRENT_VERSION)
385 return -EINVAL;
387 return 0;
388 }
390 int
391 vhd_validate_batmap(vhd_batmap_t *batmap)
392 {
393 uint32_t checksum;
395 if (!batmap->map)
396 return -EINVAL;
398 checksum = vhd_checksum_batmap(batmap);
399 if (checksum != batmap->header.checksum)
400 return -EINVAL;
402 return 0;
403 }
405 int
406 vhd_batmap_header_offset(vhd_context_t *ctx, off64_t *_off)
407 {
408 off64_t off;
409 size_t bat;
411 *_off = 0;
413 off = ctx->header.table_offset;
414 bat = ctx->header.max_bat_size * sizeof(uint32_t);
415 off += vhd_bytes_padded(bat);
417 *_off = off;
418 return 0;
419 }
421 int
422 vhd_validate_platform_code(uint32_t code)
423 {
424 switch (code) {
425 case PLAT_CODE_NONE:
426 case PLAT_CODE_WI2R:
427 case PLAT_CODE_WI2K:
428 case PLAT_CODE_W2RU:
429 case PLAT_CODE_W2KU:
430 case PLAT_CODE_MAC:
431 case PLAT_CODE_MACX:
432 return 0;
433 default:
434 VHDLOG("invalid parent locator code %u\n", code);
435 return -EINVAL;
436 }
437 }
439 int
440 vhd_parent_locator_count(vhd_context_t *ctx)
441 {
442 return (sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t));
443 }
445 int
446 vhd_hidden(vhd_context_t *ctx, int *hidden)
447 {
448 int err;
450 *hidden = 0;
452 if (vhd_type_dynamic(ctx) && vhd_creator_tapdisk(ctx) &&
453 (ctx->footer.crtr_ver == VHD_VERSION(0, 1) ||
454 ctx->footer.crtr_ver == VHD_VERSION(1, 1))) {
455 vhd_footer_t copy;
457 err = vhd_read_footer_at(ctx, &copy, 0);
458 if (err) {
459 VHDLOG("error reading backup footer of %s: %d\n",
460 ctx->file, err);
461 return err;
462 }
463 *hidden = copy.hidden;
464 } else
465 *hidden = ctx->footer.hidden;
467 return 0;
468 }
470 int
471 vhd_chain_depth(vhd_context_t *ctx, int *depth)
472 {
473 char *file;
474 int err, cnt;
475 vhd_context_t vhd, *cur;
477 err = 0;
478 cnt = 0;
479 *depth = 0;
480 file = NULL;
481 cur = ctx;
483 for (;;) {
484 cnt++;
486 if (cur->footer.type != HD_TYPE_DIFF)
487 break;
489 if (vhd_parent_raw(cur)) {
490 cnt++;
491 break;
492 }
494 err = vhd_parent_locator_get(cur, &file);
495 if (err) {
496 file = NULL;
497 break;
498 }
500 if (cur != ctx) {
501 vhd_close(cur);
502 cur = NULL;
503 }
505 err = vhd_open(&vhd, file, VHD_OPEN_RDONLY);
506 if (err)
507 break;
509 cur = &vhd;
510 free(file);
511 file = NULL;
512 }
514 free(file);
515 if (cur && cur != ctx)
516 vhd_close(cur);
518 if (!err)
519 *depth = cnt;
521 return err;
522 }
524 int
525 vhd_batmap_test(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
526 {
527 if (!vhd_has_batmap(ctx) || !batmap->map)
528 return 0;
530 if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
531 return 0;
533 return test_bit(batmap->map, block);
534 }
536 void
537 vhd_batmap_set(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
538 {
539 if (!vhd_has_batmap(ctx) || !batmap->map)
540 return;
542 if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
543 return;
545 set_bit(batmap->map, block);
546 }
548 void
549 vhd_batmap_clear(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
550 {
551 if (!vhd_has_batmap(ctx) || !batmap->map)
552 return;
554 if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
555 return;
557 clear_bit(batmap->map, block);
558 }
560 int
561 vhd_bitmap_test(vhd_context_t *ctx, char *map, uint32_t block)
562 {
563 if (vhd_creator_tapdisk(ctx) &&
564 ctx->footer.crtr_ver == 0x00000001)
565 return old_test_bit(map, block);
567 return test_bit(map, block);
568 }
570 void
571 vhd_bitmap_set(vhd_context_t *ctx, char *map, uint32_t block)
572 {
573 if (vhd_creator_tapdisk(ctx) &&
574 ctx->footer.crtr_ver == 0x00000001)
575 return old_set_bit(map, block);
577 return set_bit(map, block);
578 }
580 void
581 vhd_bitmap_clear(vhd_context_t *ctx, char *map, uint32_t block)
582 {
583 if (vhd_creator_tapdisk(ctx) &&
584 ctx->footer.crtr_ver == 0x00000001)
585 return old_clear_bit(map, block);
587 return clear_bit(map, block);
588 }
590 /*
591 * returns absolute offset of the first
592 * byte of the file which is not vhd metadata
593 */
594 int
595 vhd_end_of_headers(vhd_context_t *ctx, off64_t *end)
596 {
597 int err, i, n;
598 uint32_t bat_bytes;
599 off64_t eom, bat_end;
600 vhd_parent_locator_t *loc;
602 *end = 0;
604 if (!vhd_type_dynamic(ctx))
605 return 0;
607 eom = ctx->footer.data_offset + sizeof(vhd_header_t);
609 bat_bytes = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
610 bat_end = ctx->header.table_offset + bat_bytes;
612 eom = MAX(eom, bat_end);
614 if (vhd_has_batmap(ctx)) {
615 off64_t hdr_end, hdr_secs, map_end, map_secs;
617 err = vhd_get_batmap(ctx);
618 if (err)
619 return err;
621 hdr_secs = secs_round_up_no_zero(sizeof(vhd_batmap_header_t));
622 err = vhd_batmap_header_offset(ctx, &hdr_end);
623 if (err)
624 return err;
626 hdr_end += vhd_sectors_to_bytes(hdr_secs);
627 eom = MAX(eom, hdr_end);
629 map_secs = ctx->batmap.header.batmap_size;
630 map_end = (ctx->batmap.header.batmap_offset +
631 vhd_sectors_to_bytes(map_secs));
632 eom = MAX(eom, map_end);
633 }
635 /* parent locators */
636 n = sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t);
638 for (i = 0; i < n; i++) {
639 off64_t loc_end;
641 loc = &ctx->header.loc[i];
642 if (loc->code == PLAT_CODE_NONE)
643 continue;
645 loc_end = loc->data_offset + vhd_parent_locator_size(loc);
646 eom = MAX(eom, loc_end);
647 }
649 *end = eom;
650 return 0;
651 }
653 int
654 vhd_end_of_data(vhd_context_t *ctx, off64_t *end)
655 {
656 int i, err;
657 off64_t max;
658 uint64_t blk;
660 if (!vhd_type_dynamic(ctx)) {
661 err = vhd_seek(ctx, 0, SEEK_END);
662 if (err)
663 return err;
665 max = vhd_position(ctx);
666 if (max == (off64_t)-1)
667 return -errno;
669 *end = max - sizeof(vhd_footer_t);
670 return 0;
671 }
673 err = vhd_end_of_headers(ctx, &max);
674 if (err)
675 return err;
677 err = vhd_get_bat(ctx);
678 if (err)
679 return err;
681 max >>= VHD_SECTOR_SHIFT;
683 for (i = 0; i < ctx->bat.entries; i++) {
684 blk = ctx->bat.bat[i];
686 if (blk != DD_BLK_UNUSED) {
687 blk += ctx->spb + ctx->bm_secs;
688 max = MAX(blk, max);
689 }
690 }
692 *end = vhd_sectors_to_bytes(max);
693 return 0;
694 }
696 uint32_t
697 vhd_time(time_t time)
698 {
699 struct tm tm;
700 time_t micro_epoch;
702 memset(&tm, 0, sizeof(struct tm));
703 tm.tm_year = 100;
704 tm.tm_mon = 0;
705 tm.tm_mday = 1;
706 micro_epoch = mktime(&tm);
708 return (uint32_t)(time - micro_epoch);
709 }
711 /*
712 * Stringify the VHD timestamp for printing.
713 * As with ctime_r, target must be >=26 bytes.
714 */
715 size_t
716 vhd_time_to_string(uint32_t timestamp, char *target)
717 {
718 char *cr;
719 struct tm tm;
720 time_t t1, t2;
722 memset(&tm, 0, sizeof(struct tm));
724 /* VHD uses an epoch of 12:00AM, Jan 1, 2000. */
725 /* Need to adjust this to the expected epoch of 1970. */
726 tm.tm_year = 100;
727 tm.tm_mon = 0;
728 tm.tm_mday = 1;
730 t1 = mktime(&tm);
731 t2 = t1 + (time_t)timestamp;
732 ctime_r(&t2, target);
734 /* handle mad ctime_r newline appending. */
735 if ((cr = strchr(target, '\n')) != NULL)
736 *cr = '\0';
738 return (strlen(target));
739 }
741 /*
742 * nabbed from vhd specs.
743 */
744 uint32_t
745 vhd_chs(uint64_t size)
746 {
747 uint32_t secs, cylinders, heads, spt, cth;
749 secs = secs_round_up_no_zero(size);
751 if (secs > 65535 * 16 * 255)
752 secs = 65535 * 16 * 255;
754 if (secs >= 65535 * 16 * 63) {
755 spt = 255;
756 cth = secs / spt;
757 heads = 16;
758 } else {
759 spt = 17;
760 cth = secs / spt;
761 heads = (cth + 1023) / 1024;
763 if (heads < 4)
764 heads = 4;
766 if (cth >= (heads * 1024) || heads > 16) {
767 spt = 31;
768 cth = secs / spt;
769 heads = 16;
770 }
772 if (cth >= heads * 1024) {
773 spt = 63;
774 cth = secs / spt;
775 heads = 16;
776 }
777 }
779 cylinders = cth / heads;
781 return GEOM_ENCODE(cylinders, heads, spt);
782 }
784 int
785 vhd_get_footer(vhd_context_t *ctx)
786 {
787 if (!vhd_validate_footer(&ctx->footer))
788 return 0;
790 return vhd_read_footer(ctx, &ctx->footer);
791 }
793 int
794 vhd_get_header(vhd_context_t *ctx)
795 {
796 if (!vhd_type_dynamic(ctx))
797 return -EINVAL;
799 if (!vhd_validate_header(&ctx->header))
800 return 0;
802 return vhd_read_header(ctx, &ctx->header);
803 }
805 int
806 vhd_get_bat(vhd_context_t *ctx)
807 {
808 if (!vhd_type_dynamic(ctx))
809 return -EINVAL;
811 if (!vhd_validate_bat(&ctx->bat))
812 return 0;
814 vhd_put_bat(ctx);
815 return vhd_read_bat(ctx, &ctx->bat);
816 }
818 int
819 vhd_get_batmap(vhd_context_t *ctx)
820 {
821 if (!vhd_has_batmap(ctx))
822 return -EINVAL;
824 if (!vhd_validate_batmap(&ctx->batmap))
825 return 0;
827 vhd_put_batmap(ctx);
828 return vhd_read_batmap(ctx, &ctx->batmap);
829 }
831 void
832 vhd_put_footer(vhd_context_t *ctx)
833 {
834 memset(&ctx->footer, 0, sizeof(vhd_footer_t));
835 }
837 void
838 vhd_put_header(vhd_context_t *ctx)
839 {
840 memset(&ctx->header, 0, sizeof(vhd_header_t));
841 }
843 void
844 vhd_put_bat(vhd_context_t *ctx)
845 {
846 if (!vhd_type_dynamic(ctx))
847 return;
849 free(ctx->bat.bat);
850 memset(&ctx->bat, 0, sizeof(vhd_bat_t));
851 }
853 void
854 vhd_put_batmap(vhd_context_t *ctx)
855 {
856 if (!vhd_type_dynamic(ctx))
857 return;
859 if (!vhd_has_batmap(ctx))
860 return;
862 free(ctx->batmap.map);
863 memset(&ctx->batmap, 0, sizeof(vhd_batmap_t));
864 }
866 /*
867 * look for 511 byte footer at end of file
868 */
869 int
870 vhd_read_short_footer(vhd_context_t *ctx, vhd_footer_t *footer)
871 {
872 int err;
873 char *buf;
874 off64_t eof;
876 buf = NULL;
878 err = vhd_seek(ctx, 0, SEEK_END);
879 if (err)
880 goto out;
882 eof = vhd_position(ctx);
883 if (eof == (off64_t)-1) {
884 err = -errno;
885 goto out;
886 }
888 err = vhd_seek(ctx, eof - 511, SEEK_SET);
889 if (err)
890 goto out;
892 err = posix_memalign((void **)&buf,
893 VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
894 if (err) {
895 buf = NULL;
896 err = -err;
897 goto out;
898 }
900 memset(buf, 0, sizeof(vhd_footer_t));
902 /*
903 * expecting short read here
904 */
905 vhd_read(ctx, buf, sizeof(vhd_footer_t));
907 memcpy(footer, buf, sizeof(vhd_footer_t));
909 vhd_footer_in(footer);
910 err = vhd_validate_footer(footer);
912 out:
913 if (err)
914 VHDLOG("%s: failed reading short footer: %d\n",
915 ctx->file, err);
916 free(buf);
917 return err;
918 }
920 int
921 vhd_read_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off64_t off)
922 {
923 int err;
924 char *buf;
926 buf = NULL;
928 err = vhd_seek(ctx, off, SEEK_SET);
929 if (err)
930 goto out;
932 err = posix_memalign((void **)&buf,
933 VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
934 if (err) {
935 buf = NULL;
936 err = -err;
937 goto out;
938 }
940 err = vhd_read(ctx, buf, sizeof(vhd_footer_t));
941 if (err)
942 goto out;
944 memcpy(footer, buf, sizeof(vhd_footer_t));
946 vhd_footer_in(footer);
947 err = vhd_validate_footer(footer);
949 out:
950 if (err)
951 VHDLOG("%s: reading footer at 0x%08"PRIx64" failed: %d\n",
952 ctx->file, off, err);
953 free(buf);
954 return err;
955 }
957 int
958 vhd_read_footer(vhd_context_t *ctx, vhd_footer_t *footer)
959 {
960 int err;
961 off64_t off;
963 err = vhd_seek(ctx, 0, SEEK_END);
964 if (err)
965 return err;
967 off = vhd_position(ctx);
968 if (off == (off64_t)-1)
969 return -errno;
971 err = vhd_read_footer_at(ctx, footer, off - 512);
972 if (err != -EINVAL)
973 return err;
975 err = vhd_read_short_footer(ctx, footer);
976 if (err != -EINVAL)
977 return err;
979 if (ctx->oflags & VHD_OPEN_STRICT)
980 return -EINVAL;
982 return vhd_read_footer_at(ctx, footer, 0);
983 }
985 int
986 vhd_read_header_at(vhd_context_t *ctx, vhd_header_t *header, off64_t off)
987 {
988 int err;
989 char *buf;
991 buf = NULL;
993 if (!vhd_type_dynamic(ctx)) {
994 err = -EINVAL;
995 goto out;
996 }
998 err = vhd_seek(ctx, off, SEEK_SET);
999 if (err)
1000 goto out;
1002 err = posix_memalign((void **)&buf,
1003 VHD_SECTOR_SIZE, sizeof(vhd_header_t));
1004 if (err) {
1005 buf = NULL;
1006 err = -err;
1007 goto out;
1010 err = vhd_read(ctx, buf, sizeof(vhd_header_t));
1011 if (err)
1012 goto out;
1014 memcpy(header, buf, sizeof(vhd_header_t));
1016 vhd_header_in(header);
1017 err = vhd_validate_header(header);
1019 out:
1020 if (err)
1021 VHDLOG("%s: reading header at 0x%08"PRIx64" failed: %d\n",
1022 ctx->file, off, err);
1023 free(buf);
1024 return err;
1027 int
1028 vhd_read_header(vhd_context_t *ctx, vhd_header_t *header)
1030 int err;
1031 off64_t off;
1033 if (!vhd_type_dynamic(ctx)) {
1034 VHDLOG("%s is not dynamic!\n", ctx->file);
1035 return -EINVAL;
1038 off = ctx->footer.data_offset;
1039 return vhd_read_header_at(ctx, header, off);
1042 int
1043 vhd_read_bat(vhd_context_t *ctx, vhd_bat_t *bat)
1045 int err;
1046 char *buf;
1047 off64_t off;
1048 size_t size;
1050 buf = NULL;
1052 if (!vhd_type_dynamic(ctx)) {
1053 err = -EINVAL;
1054 goto fail;
1057 off = ctx->header.table_offset;
1058 size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
1060 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
1061 if (err) {
1062 buf = NULL;
1063 err = -err;
1064 goto fail;
1067 err = vhd_seek(ctx, off, SEEK_SET);
1068 if (err)
1069 goto fail;
1071 err = vhd_read(ctx, buf, size);
1072 if (err)
1073 goto fail;
1075 bat->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
1076 bat->entries = ctx->header.max_bat_size;
1077 bat->bat = (uint32_t *)buf;
1079 vhd_bat_in(bat);
1081 return 0;
1083 fail:
1084 free(buf);
1085 memset(bat, 0, sizeof(vhd_bat_t));
1086 VHDLOG("%s: failed to read bat: %d\n", ctx->file, err);
1087 return err;
1090 static int
1091 vhd_read_batmap_header(vhd_context_t *ctx, vhd_batmap_t *batmap)
1093 int err;
1094 char *buf;
1095 off64_t off;
1096 size_t size;
1098 buf = NULL;
1100 err = vhd_batmap_header_offset(ctx, &off);
1101 if (err)
1102 goto fail;
1104 err = vhd_seek(ctx, off, SEEK_SET);
1105 if (err)
1106 goto fail;
1108 size = vhd_bytes_padded(sizeof(vhd_batmap_header_t));
1109 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
1110 if (err) {
1111 buf = NULL;
1112 err = -err;
1113 goto fail;
1116 err = vhd_read(ctx, buf, size);
1117 if (err)
1118 goto fail;
1120 memcpy(&batmap->header, buf, sizeof(vhd_batmap_header_t));
1121 free(buf);
1122 buf = NULL;
1124 vhd_batmap_header_in(batmap);
1126 return 0;
1128 fail:
1129 free(buf);
1130 memset(&batmap->header, 0, sizeof(vhd_batmap_header_t));
1131 VHDLOG("%s: failed to read batmap header: %d\n", ctx->file, err);
1132 return err;
1135 static int
1136 vhd_read_batmap_map(vhd_context_t *ctx, vhd_batmap_t *batmap)
1138 int err;
1139 char *buf;
1140 off64_t off;
1141 size_t map_size;
1143 map_size = vhd_sectors_to_bytes(batmap->header.batmap_size);
1145 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, map_size);
1146 if (err) {
1147 buf = NULL;
1148 err = -err;
1149 goto fail;
1152 off = batmap->header.batmap_offset;
1153 err = vhd_seek(ctx, off, SEEK_SET);
1154 if (err)
1155 goto fail;
1157 err = vhd_read(ctx, buf, map_size);
1158 if (err)
1159 goto fail;
1161 batmap->map = buf;
1162 return 0;
1164 fail:
1165 free(buf);
1166 batmap->map = NULL;
1167 VHDLOG("%s: failed to read batmap: %d\n", ctx->file, err);
1168 return err;
1171 int
1172 vhd_read_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap)
1174 int err;
1176 if (!vhd_has_batmap(ctx))
1177 return -EINVAL;
1179 memset(batmap, 0, sizeof(vhd_batmap_t));
1181 err = vhd_read_batmap_header(ctx, batmap);
1182 if (err)
1183 return err;
1185 err = vhd_validate_batmap_header(batmap);
1186 if (err)
1187 return err;
1189 err = vhd_read_batmap_map(ctx, batmap);
1190 if (err)
1191 return err;
1193 err = vhd_validate_batmap(batmap);
1194 if (err)
1195 goto fail;
1197 return 0;
1199 fail:
1200 free(batmap->map);
1201 memset(batmap, 0, sizeof(vhd_batmap_t));
1202 return err;
1205 int
1206 vhd_has_batmap(vhd_context_t *ctx)
1208 if (!vhd_type_dynamic(ctx))
1209 return 0;
1211 if (!vhd_creator_tapdisk(ctx))
1212 return 0;
1214 if (ctx->footer.crtr_ver <= VHD_VERSION(0, 1))
1215 return 0;
1217 if (ctx->footer.crtr_ver >= VHD_VERSION(1, 2))
1218 return 1;
1220 /*
1221 * VHDs of version 1.1 probably have a batmap, but may not
1222 * if they were updated from version 0.1 via vhd-update.
1223 */
1224 if (!vhd_validate_batmap_header(&ctx->batmap))
1225 return 1;
1227 if (vhd_read_batmap_header(ctx, &ctx->batmap))
1228 return 0;
1230 return (!vhd_validate_batmap_header(&ctx->batmap));
1233 /*
1234 * Is this a block device (with a fixed size)? This affects whether the file
1235 * can be truncated and where the footer is written for VHDs.
1236 */
1237 int
1238 vhd_test_file_fixed(const char *file, int *is_block)
1240 int err;
1241 struct stat stats;
1243 err = stat(file, &stats);
1244 if (err == -1)
1245 return -errno;
1247 *is_block = !!(S_ISBLK(stats.st_mode));
1248 return err;
1251 int
1252 vhd_find_parent(vhd_context_t *ctx, const char *parent, char **_location)
1254 int err;
1255 char *location, *cpath, *cdir, *path;
1257 err = 0;
1258 path = NULL;
1259 cpath = NULL;
1260 location = NULL;
1261 *_location = NULL;
1263 if (!parent)
1264 return -EINVAL;
1266 if (parent[0] == '/') {
1267 if (!access(parent, R_OK)) {
1268 path = strdup(parent);
1269 if (!path)
1270 return -ENOMEM;
1271 *_location = path;
1272 return 0;
1276 /* check parent path relative to child's directory */
1277 cpath = realpath(ctx->file, NULL);
1278 if (!cpath) {
1279 err = -errno;
1280 goto out;
1283 cdir = dirname(cpath);
1284 if (asprintf(&location, "%s/%s", cdir, parent) == -1) {
1285 err = -errno;
1286 location = NULL;
1287 goto out;
1290 if (!access(location, R_OK)) {
1291 path = realpath(location, NULL);
1292 if (path) {
1293 *_location = path;
1294 return 0;
1297 err = -errno;
1299 out:
1300 free(location);
1301 free(cpath);
1302 return err;
1305 static int
1306 vhd_macx_encode_location(char *name, char **out, int *outlen)
1308 iconv_t cd;
1309 int len, err;
1310 size_t ibl, obl;
1311 char *uri, *urip, *uri_utf8, *uri_utf8p, *ret;
1313 err = 0;
1314 ret = NULL;
1315 *out = NULL;
1316 *outlen = 0;
1317 len = strlen(name) + strlen("file://");
1319 ibl = len;
1320 obl = len;
1322 uri = urip = malloc(ibl + 1);
1323 uri_utf8 = uri_utf8p = malloc(obl);
1325 if (!uri || !uri_utf8)
1326 return -ENOMEM;
1328 cd = iconv_open("UTF-8", "ASCII");
1329 if (cd == (iconv_t)-1) {
1330 err = -errno;
1331 goto out;
1334 sprintf(uri, "file://%s", name);
1336 if (iconv(cd, &urip, &ibl, &uri_utf8p, &obl) == (size_t)-1 ||
1337 ibl || obl) {
1338 err = (errno ? -errno : -EIO);
1339 goto out;
1342 ret = malloc(len);
1343 if (!ret) {
1344 err = -ENOMEM;
1345 goto out;
1348 memcpy(ret, uri_utf8, len);
1349 *outlen = len;
1350 *out = ret;
1352 out:
1353 free(uri);
1354 free(uri_utf8);
1355 if (cd != (iconv_t)-1)
1356 iconv_close(cd);
1358 return err;
1361 static int
1362 vhd_w2u_encode_location(char *name, char **out, int *outlen)
1364 iconv_t cd;
1365 int len, err;
1366 size_t ibl, obl;
1367 char *uri, *urip, *uri_utf16, *uri_utf16p, *tmp, *ret;
1369 err = 0;
1370 ret = NULL;
1371 *out = NULL;
1372 *outlen = 0;
1373 cd = (iconv_t) -1;
1375 /*
1376 * MICROSOFT_COMPAT
1377 * relative paths must start with ".\"
1378 */
1379 if (name[0] != '/') {
1380 tmp = strstr(name, "./");
1381 if (tmp == name)
1382 tmp += strlen("./");
1383 else
1384 tmp = name;
1386 err = asprintf(&uri, ".\\%s", tmp);
1387 } else
1388 err = asprintf(&uri, "%s", name);
1390 if (err == -1)
1391 return -ENOMEM;
1393 tmp = uri;
1394 while (*tmp != '\0') {
1395 if (*tmp == '/')
1396 *tmp = '\\';
1397 tmp++;
1400 len = strlen(uri);
1401 ibl = len;
1402 obl = len * 2;
1403 urip = uri;
1405 uri_utf16 = uri_utf16p = malloc(obl);
1406 if (!uri_utf16) {
1407 err = -ENOMEM;
1408 goto out;
1411 /*
1412 * MICROSOFT_COMPAT
1413 * little endian unicode here
1414 */
1415 cd = iconv_open("UTF-16LE", "ASCII");
1416 if (cd == (iconv_t)-1) {
1417 err = -errno;
1418 goto out;
1421 if (iconv(cd, &urip, &ibl, &uri_utf16p, &obl) == (size_t)-1 ||
1422 ibl || obl) {
1423 err = (errno ? -errno : -EIO);
1424 goto out;
1427 len = len * 2;
1428 ret = malloc(len);
1429 if (!ret) {
1430 err = -ENOMEM;
1431 goto out;
1434 memcpy(ret, uri_utf16, len);
1435 *outlen = len;
1436 *out = ret;
1437 err = 0;
1439 out:
1440 free(uri);
1441 free(uri_utf16);
1442 if (cd != (iconv_t)-1)
1443 iconv_close(cd);
1445 return err;
1448 static char *
1449 vhd_macx_decode_location(char *in, char *out, int len)
1451 iconv_t cd;
1452 char *name;
1453 size_t ibl, obl;
1455 name = out;
1456 ibl = obl = len;
1458 cd = iconv_open("ASCII", "UTF-8");
1459 if (cd == (iconv_t)-1)
1460 return NULL;
1462 if (iconv(cd, &in, &ibl, &out, &obl) == (size_t)-1 || ibl)
1463 return NULL;
1465 iconv_close(cd);
1466 *out = '\0';
1468 if (strstr(name, "file://") != name)
1469 return NULL;
1471 name += strlen("file://");
1473 return strdup(name);
1476 static char *
1477 vhd_w2u_decode_location(char *in, char *out, int len, char *utf_type)
1479 iconv_t cd;
1480 char *name, *tmp;
1481 size_t ibl, obl;
1483 tmp = name = out;
1484 ibl = obl = len;
1486 cd = iconv_open("ASCII", utf_type);
1487 if (cd == (iconv_t)-1)
1488 return NULL;
1490 if (iconv(cd, &in, &ibl, &out, &obl) == (size_t)-1 || ibl)
1491 return NULL;
1493 iconv_close(cd);
1494 *out = '\0';
1496 /* TODO: spaces */
1497 while (tmp != out) {
1498 if (*tmp == '\\')
1499 *tmp = '/';
1500 tmp++;
1503 if (strstr(name, "C:") == name || strstr(name, "c:") == name)
1504 name += strlen("c:");
1506 return strdup(name);
1509 int
1510 vhd_header_decode_parent(vhd_context_t *ctx, vhd_header_t *header, char **buf)
1512 char *code, out[512];
1514 if (vhd_creator_tapdisk(ctx) &&
1515 ctx->footer.crtr_ver == VHD_VERSION(0, 1))
1516 code = UTF_16;
1517 else
1518 code = UTF_16BE;
1520 *buf = vhd_w2u_decode_location(header->prt_name, out, 512, code);
1521 return (*buf == NULL ? -EINVAL : 0);
1524 int
1525 vhd_parent_locator_read(vhd_context_t *ctx,
1526 vhd_parent_locator_t *loc, char **parent)
1528 int err, size;
1529 char *raw, *out, *name;
1531 raw = NULL;
1532 out = NULL;
1533 name = NULL;
1534 *parent = NULL;
1536 if (ctx->footer.type != HD_TYPE_DIFF) {
1537 err = -EINVAL;
1538 goto out;
1541 switch (loc->code) {
1542 case PLAT_CODE_MACX:
1543 case PLAT_CODE_W2KU:
1544 case PLAT_CODE_W2RU:
1545 break;
1546 default:
1547 err = -EINVAL;
1548 goto out;
1551 err = vhd_seek(ctx, loc->data_offset, SEEK_SET);
1552 if (err)
1553 goto out;
1555 size = vhd_parent_locator_size(loc);
1556 if (size <= 0) {
1557 err = -EINVAL;
1558 goto out;
1561 err = posix_memalign((void **)&raw, VHD_SECTOR_SIZE, size);
1562 if (err) {
1563 raw = NULL;
1564 err = -err;
1565 goto out;
1568 err = vhd_read(ctx, raw, size);
1569 if (err)
1570 goto out;
1572 out = malloc(loc->data_len + 1);
1573 if (!out) {
1574 err = -ENOMEM;
1575 goto out;
1578 switch (loc->code) {
1579 case PLAT_CODE_MACX:
1580 name = vhd_macx_decode_location(raw, out, loc->data_len);
1581 break;
1582 case PLAT_CODE_W2KU:
1583 case PLAT_CODE_W2RU:
1584 name = vhd_w2u_decode_location(raw, out,
1585 loc->data_len, UTF_16LE);
1586 break;
1589 if (!name) {
1590 err = -EINVAL;
1591 goto out;
1594 err = 0;
1595 *parent = name;
1597 out:
1598 free(raw);
1599 free(out);
1601 if (err) {
1602 VHDLOG("%s: error reading parent locator: %d\n",
1603 ctx->file, err);
1604 VHDLOG("%s: locator: code %u, space 0x%x, len 0x%x, "
1605 "off 0x%"PRIx64"\n", ctx->file, loc->code, loc->data_space,
1606 loc->data_len, loc->data_offset);
1609 return err;
1612 int
1613 vhd_parent_locator_get(vhd_context_t *ctx, char **parent)
1615 int i, n, err;
1616 char *name, *location;
1617 vhd_parent_locator_t *loc;
1619 err = 0;
1620 *parent = NULL;
1622 if (ctx->footer.type != HD_TYPE_DIFF)
1623 return -EINVAL;
1625 n = vhd_parent_locator_count(ctx);
1626 for (i = 0; i < n; i++) {
1627 loc = ctx->header.loc + i;
1628 err = vhd_parent_locator_read(ctx, loc, &name);
1629 if (err)
1630 continue;
1632 err = vhd_find_parent(ctx, name, &location);
1633 if (err)
1634 VHDLOG("%s: couldn't find parent %s (%d)\n",
1635 ctx->file, name, err);
1636 free(name);
1638 if (!err) {
1639 *parent = location;
1640 return 0;
1644 return err;
1647 int
1648 vhd_parent_locator_write_at(vhd_context_t *ctx,
1649 const char *parent, off64_t off, uint32_t code,
1650 size_t max_bytes, vhd_parent_locator_t *loc)
1652 struct stat stats;
1653 int err, len, size;
1654 char *absolute_path, *relative_path, *encoded, *block;
1656 memset(loc, 0, sizeof(vhd_parent_locator_t));
1658 if (ctx->footer.type != HD_TYPE_DIFF)
1659 return -EINVAL;
1661 absolute_path = NULL;
1662 relative_path = NULL;
1663 encoded = NULL;
1664 block = NULL;
1665 size = 0;
1666 len = 0;
1668 switch (code) {
1669 case PLAT_CODE_MACX:
1670 case PLAT_CODE_W2KU:
1671 case PLAT_CODE_W2RU:
1672 break;
1673 default:
1674 return -EINVAL;
1677 absolute_path = realpath(parent, NULL);
1678 if (!absolute_path) {
1679 err = -errno;
1680 goto out;
1683 err = stat(absolute_path, &stats);
1684 if (err) {
1685 err = -errno;
1686 goto out;
1689 if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
1690 err = -EINVAL;
1691 goto out;
1694 relative_path = relative_path_to(ctx->file, absolute_path, &err);
1695 if (!relative_path || err) {
1696 err = (err ? err : -EINVAL);
1697 goto out;
1700 switch (code) {
1701 case PLAT_CODE_MACX:
1702 err = vhd_macx_encode_location(relative_path, &encoded, &len);
1703 break;
1704 case PLAT_CODE_W2KU:
1705 case PLAT_CODE_W2RU:
1706 err = vhd_w2u_encode_location(relative_path, &encoded, &len);
1707 break;
1708 default:
1709 err = -EINVAL;
1712 if (err)
1713 goto out;
1715 err = vhd_seek(ctx, off, SEEK_SET);
1716 if (err)
1717 goto out;
1719 size = vhd_bytes_padded(len);
1721 if (max_bytes && size > max_bytes) {
1722 err = -ENAMETOOLONG;
1723 goto out;
1726 err = posix_memalign((void **)&block, VHD_SECTOR_SIZE, size);
1727 if (err) {
1728 block = NULL;
1729 err = -err;
1730 goto out;
1733 memset(block, 0, size);
1734 memcpy(block, encoded, len);
1736 err = vhd_write(ctx, block, size);
1737 if (err)
1738 goto out;
1740 err = 0;
1742 out:
1743 free(absolute_path);
1744 free(relative_path);
1745 free(encoded);
1746 free(block);
1748 if (!err) {
1749 loc->res = 0;
1750 loc->code = code;
1751 loc->data_len = len;
1752 /*
1753 * write number of bytes ('size') instead of number of sectors
1754 * into loc->data_space to be compatible with MSFT, even though
1755 * this goes against the specs
1756 */
1757 loc->data_space = size;
1758 loc->data_offset = off;
1761 return err;
1764 static int
1765 vhd_footer_offset_at_eof(vhd_context_t *ctx, off64_t *off)
1767 int err;
1768 if ((err = vhd_seek(ctx, 0, SEEK_END)))
1769 return errno;
1770 *off = vhd_position(ctx) - sizeof(vhd_footer_t);
1771 return 0;
1774 int
1775 vhd_read_bitmap(vhd_context_t *ctx, uint32_t block, char **bufp)
1777 int err;
1778 char *buf;
1779 size_t size;
1780 off64_t off;
1781 uint64_t blk;
1783 buf = NULL;
1784 *bufp = NULL;
1786 if (!vhd_type_dynamic(ctx))
1787 return -EINVAL;
1789 err = vhd_get_bat(ctx);
1790 if (err)
1791 return err;
1793 if (block >= ctx->bat.entries)
1794 return -ERANGE;
1796 blk = ctx->bat.bat[block];
1797 if (blk == DD_BLK_UNUSED)
1798 return -EINVAL;
1800 off = vhd_sectors_to_bytes(blk);
1801 size = vhd_bytes_padded(ctx->spb >> 3);
1803 err = vhd_seek(ctx, off, SEEK_SET);
1804 if (err)
1805 return err;
1807 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
1808 if (err)
1809 return -err;
1811 err = vhd_read(ctx, buf, size);
1812 if (err)
1813 goto fail;
1815 *bufp = buf;
1816 return 0;
1818 fail:
1819 free(buf);
1820 return err;
1823 int
1824 vhd_read_block(vhd_context_t *ctx, uint32_t block, char **bufp)
1826 int err;
1827 char *buf;
1828 size_t size;
1829 uint64_t blk;
1830 off64_t end, off;
1832 buf = NULL;
1833 *bufp = NULL;
1835 if (!vhd_type_dynamic(ctx))
1836 return -EINVAL;
1838 err = vhd_get_bat(ctx);
1839 if (err)
1840 return err;
1842 if (block >= ctx->bat.entries)
1843 return -ERANGE;
1845 blk = ctx->bat.bat[block];
1846 if (blk == DD_BLK_UNUSED)
1847 return -EINVAL;
1849 off = vhd_sectors_to_bytes(blk + ctx->bm_secs);
1850 size = vhd_sectors_to_bytes(ctx->spb);
1852 err = vhd_footer_offset_at_eof(ctx, &end);
1853 if (err)
1854 return err;
1856 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
1857 if (err) {
1858 err = -err;
1859 goto fail;
1862 if (end < off + ctx->header.block_size) {
1863 size = end - off;
1864 memset(buf + size, 0, ctx->header.block_size - size);
1867 err = vhd_seek(ctx, off, SEEK_SET);
1868 if (err)
1869 goto fail;
1871 err = vhd_read(ctx, buf, size);
1872 if (err)
1873 goto fail;
1875 *bufp = buf;
1876 return 0;
1878 fail:
1879 free(buf);
1880 return err;
1883 int
1884 vhd_write_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off64_t off)
1886 int err;
1887 vhd_footer_t *f;
1889 f = NULL;
1891 err = posix_memalign((void **)&f,
1892 VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
1893 if (err) {
1894 f = NULL;
1895 err = -err;
1896 goto out;
1899 memcpy(f, footer, sizeof(vhd_footer_t));
1900 f->checksum = vhd_checksum_footer(f);
1902 err = vhd_validate_footer(f);
1903 if (err)
1904 goto out;
1906 err = vhd_seek(ctx, off, SEEK_SET);
1907 if (err)
1908 goto out;
1910 vhd_footer_out(f);
1912 err = vhd_write(ctx, f, sizeof(vhd_footer_t));
1914 out:
1915 if (err)
1916 VHDLOG("%s: failed writing footer at 0x%08"PRIx64": %d\n",
1917 ctx->file, off, err);
1918 free(f);
1919 return err;
1922 int
1923 vhd_write_footer(vhd_context_t *ctx, vhd_footer_t *footer)
1925 int err;
1926 off64_t off;
1928 if (ctx->is_block)
1929 err = vhd_footer_offset_at_eof(ctx, &off);
1930 else
1931 err = vhd_end_of_data(ctx, &off);
1932 if (err)
1933 return err;
1935 err = vhd_write_footer_at(ctx, footer, off);
1936 if (err)
1937 return err;
1939 if (!vhd_type_dynamic(ctx))
1940 return 0;
1942 return vhd_write_footer_at(ctx, footer, 0);
1945 int
1946 vhd_write_header_at(vhd_context_t *ctx, vhd_header_t *header, off64_t off)
1948 int err;
1949 vhd_header_t *h;
1951 h = NULL;
1953 if (!vhd_type_dynamic(ctx)) {
1954 err = -EINVAL;
1955 goto out;
1958 err = posix_memalign((void **)&h,
1959 VHD_SECTOR_SIZE, sizeof(vhd_header_t));
1960 if (err) {
1961 h = NULL;
1962 err = -err;
1963 goto out;
1966 memcpy(h, header, sizeof(vhd_header_t));
1968 h->checksum = vhd_checksum_header(h);
1969 err = vhd_validate_header(h);
1970 if (err)
1971 goto out;
1973 vhd_header_out(h);
1975 err = vhd_seek(ctx, off, SEEK_SET);
1976 if (err)
1977 goto out;
1979 err = vhd_write(ctx, h, sizeof(vhd_header_t));
1981 out:
1982 if (err)
1983 VHDLOG("%s: failed writing header at 0x%08"PRIx64": %d\n",
1984 ctx->file, off, err);
1985 free(h);
1986 return err;
1989 int
1990 vhd_write_header(vhd_context_t *ctx, vhd_header_t *header)
1992 int err;
1993 off64_t off;
1995 if (!vhd_type_dynamic(ctx))
1996 return -EINVAL;
1998 off = ctx->footer.data_offset;
1999 return vhd_write_header_at(ctx, header, off);
2002 int
2003 vhd_write_bat(vhd_context_t *ctx, vhd_bat_t *bat)
2005 int err;
2006 off64_t off;
2007 vhd_bat_t b;
2008 size_t size;
2010 if (!vhd_type_dynamic(ctx))
2011 return -EINVAL;
2013 err = vhd_validate_bat(&ctx->bat);
2014 if (err)
2015 return err;
2017 err = vhd_validate_bat(bat);
2018 if (err)
2019 return err;
2021 memset(&b, 0, sizeof(vhd_bat_t));
2023 off = ctx->header.table_offset;
2024 size = vhd_bytes_padded(bat->entries * sizeof(uint32_t));
2026 err = vhd_seek(ctx, off, SEEK_SET);
2027 if (err)
2028 return err;
2030 err = posix_memalign((void **)&b.bat, VHD_SECTOR_SIZE, size);
2031 if (err)
2032 return -err;
2034 memcpy(b.bat, bat->bat, size);
2035 b.spb = bat->spb;
2036 b.entries = bat->entries;
2037 vhd_bat_out(&b);
2039 err = vhd_write(ctx, b.bat, size);
2040 free(b.bat);
2042 return err;
2045 int
2046 vhd_write_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap)
2048 int err;
2049 off64_t off;
2050 vhd_batmap_t b;
2051 char *buf, *map;
2052 size_t size, map_size;
2054 buf = NULL;
2055 map = NULL;
2057 if (!vhd_has_batmap(ctx)) {
2058 err = -EINVAL;
2059 goto out;
2062 b.header = batmap->header;
2063 b.map = batmap->map;
2065 b.header.checksum = vhd_checksum_batmap(&b);
2066 err = vhd_validate_batmap(&b);
2067 if (err)
2068 goto out;
2070 off = b.header.batmap_offset;
2071 map_size = vhd_sectors_to_bytes(b.header.batmap_size);
2073 err = vhd_seek(ctx, off, SEEK_SET);
2074 if (err)
2075 goto out;
2077 err = posix_memalign((void **)&map, VHD_SECTOR_SIZE, map_size);
2078 if (err) {
2079 map = NULL;
2080 err = -err;
2081 goto out;
2084 memcpy(map, b.map, map_size);
2086 err = vhd_write(ctx, map, map_size);
2087 if (err)
2088 goto out;
2090 err = vhd_batmap_header_offset(ctx, &off);
2091 if (err)
2092 goto out;
2094 size = vhd_bytes_padded(sizeof(vhd_batmap_header_t));
2096 err = vhd_seek(ctx, off, SEEK_SET);
2097 if (err)
2098 goto out;
2100 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
2101 if (err) {
2102 err = -err;
2103 buf = NULL;
2104 goto out;
2107 vhd_batmap_header_out(&b);
2108 memset(buf, 0, size);
2109 memcpy(buf, &b.header, sizeof(vhd_batmap_header_t));
2111 err = vhd_write(ctx, buf, size);
2113 out:
2114 if (err)
2115 VHDLOG("%s: failed writing batmap: %d\n", ctx->file, err);
2116 free(buf);
2117 free(map);
2118 return 0;
2121 int
2122 vhd_write_bitmap(vhd_context_t *ctx, uint32_t block, char *bitmap)
2124 int err;
2125 off64_t off;
2126 uint64_t blk;
2127 size_t secs, size;
2129 if (!vhd_type_dynamic(ctx))
2130 return -EINVAL;
2132 err = vhd_validate_bat(&ctx->bat);
2133 if (err)
2134 return err;
2136 if (block >= ctx->bat.entries)
2137 return -ERANGE;
2139 if ((unsigned long)bitmap & (VHD_SECTOR_SIZE - 1))
2140 return -EINVAL;
2142 blk = ctx->bat.bat[block];
2143 if (blk == DD_BLK_UNUSED)
2144 return -EINVAL;
2146 off = vhd_sectors_to_bytes(blk);
2147 size = vhd_sectors_to_bytes(ctx->bm_secs);
2149 err = vhd_seek(ctx, off, SEEK_SET);
2150 if (err)
2151 return err;
2153 err = vhd_write(ctx, bitmap, size);
2154 if (err)
2155 return err;
2157 return 0;
2160 int
2161 vhd_write_block(vhd_context_t *ctx, uint32_t block, char *data)
2163 int err;
2164 off64_t off;
2165 size_t size;
2166 uint64_t blk;
2168 if (!vhd_type_dynamic(ctx))
2169 return -EINVAL;
2171 err = vhd_validate_bat(&ctx->bat);
2172 if (err)
2173 return err;
2175 if (block >= ctx->bat.entries)
2176 return -ERANGE;
2178 if ((unsigned long)data & ~(VHD_SECTOR_SIZE -1))
2179 return -EINVAL;
2181 blk = ctx->bat.bat[block];
2182 if (blk == DD_BLK_UNUSED)
2183 return -EINVAL;
2185 off = vhd_sectors_to_bytes(blk + ctx->bm_secs);
2186 size = vhd_sectors_to_bytes(ctx->spb);
2188 err = vhd_seek(ctx, off, SEEK_SET);
2189 if (err)
2190 return err;
2192 err = vhd_write(ctx, data, size);
2193 if (err)
2194 return err;
2196 return 0;
2199 static inline int
2200 namedup(char **dup, const char *name)
2202 *dup = NULL;
2204 if (strnlen(name, MAX_NAME_LEN) >= MAX_NAME_LEN)
2205 return -ENAMETOOLONG;
2207 *dup = strdup(name);
2208 if (*dup == NULL)
2209 return -ENOMEM;
2211 return 0;
2214 int
2215 vhd_seek(vhd_context_t *ctx, off64_t offset, int whence)
2217 off64_t off;
2219 off = lseek64(ctx->fd, offset, whence);
2220 if (off == (off64_t)-1) {
2221 VHDLOG("%s: seek(0x%08"PRIx64", %d) failed: %d\n",
2222 ctx->file, offset, whence, -errno);
2223 return -errno;
2226 return 0;
2229 off64_t
2230 vhd_position(vhd_context_t *ctx)
2232 return lseek64(ctx->fd, 0, SEEK_CUR);
2235 int
2236 vhd_read(vhd_context_t *ctx, void *buf, size_t size)
2238 size_t ret;
2240 errno = 0;
2242 ret = read(ctx->fd, buf, size);
2243 if (ret == size)
2244 return 0;
2246 VHDLOG("%s: read of %zu returned %zd, errno: %d\n",
2247 ctx->file, size, ret, -errno);
2249 return (errno ? -errno : -EIO);
2252 int
2253 vhd_write(vhd_context_t *ctx, void *buf, size_t size)
2255 size_t ret;
2257 errno = 0;
2259 ret = write(ctx->fd, buf, size);
2260 if (ret == size)
2261 return 0;
2263 VHDLOG("%s: write of %zu returned %zd, errno: %d\n",
2264 ctx->file, size, ret, -errno);
2266 return (errno ? -errno : -EIO);
2269 int
2270 vhd_offset(vhd_context_t *ctx, uint32_t sector, uint32_t *offset)
2272 int err;
2273 uint32_t block;
2275 if (!vhd_type_dynamic(ctx))
2276 return sector;
2278 err = vhd_get_bat(ctx);
2279 if (err)
2280 return err;
2282 block = sector / ctx->spb;
2283 if (ctx->bat.bat[block] == DD_BLK_UNUSED)
2284 *offset = DD_BLK_UNUSED;
2285 else
2286 *offset = ctx->bat.bat[block] +
2287 ctx->bm_secs + (sector % ctx->spb);
2289 return 0;
2292 int
2293 vhd_open_fast(vhd_context_t *ctx)
2295 int err;
2296 char *buf;
2297 size_t size;
2299 size = sizeof(vhd_footer_t) + sizeof(vhd_header_t);
2300 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
2301 if (err) {
2302 VHDLOG("failed allocating %s: %d\n", ctx->file, -err);
2303 return -err;
2306 err = vhd_read(ctx, buf, size);
2307 if (err) {
2308 VHDLOG("failed reading %s: %d\n", ctx->file, err);
2309 goto out;
2312 memcpy(&ctx->footer, buf, sizeof(vhd_footer_t));
2313 vhd_footer_in(&ctx->footer);
2314 err = vhd_validate_footer(&ctx->footer);
2315 if (err)
2316 goto out;
2318 if (vhd_type_dynamic(ctx)) {
2319 if (ctx->footer.data_offset != sizeof(vhd_footer_t))
2320 err = vhd_read_header(ctx, &ctx->header);
2321 else {
2322 memcpy(&ctx->header,
2323 buf + sizeof(vhd_footer_t),
2324 sizeof(vhd_header_t));
2325 vhd_header_in(&ctx->header);
2326 err = vhd_validate_header(&ctx->header);
2329 if (err)
2330 goto out;
2332 ctx->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
2333 ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3);
2336 out:
2337 free(buf);
2338 return err;
2341 int
2342 vhd_open(vhd_context_t *ctx, const char *file, int flags)
2344 int err, oflags;
2346 if (flags & VHD_OPEN_STRICT)
2347 vhd_flag_clear(flags, VHD_OPEN_FAST);
2349 memset(ctx, 0, sizeof(vhd_context_t));
2350 ctx->fd = -1;
2351 ctx->oflags = flags;
2353 err = namedup(&ctx->file, file);
2354 if (err)
2355 return err;
2357 oflags = O_DIRECT | O_LARGEFILE;
2358 if (flags & VHD_OPEN_RDONLY)
2359 oflags |= O_RDONLY;
2360 if (flags & VHD_OPEN_RDWR)
2361 oflags |= O_RDWR;
2363 ctx->fd = open(ctx->file, oflags, 0644);
2364 if (ctx->fd == -1) {
2365 err = -errno;
2366 VHDLOG("failed to open %s: %d\n", ctx->file, err);
2367 goto fail;
2370 err = vhd_test_file_fixed(ctx->file, &ctx->is_block);
2371 if (err)
2372 goto fail;
2374 if (flags & VHD_OPEN_FAST) {
2375 err = vhd_open_fast(ctx);
2376 if (err)
2377 goto fail;
2379 return 0;
2382 err = vhd_read_footer(ctx, &ctx->footer);
2383 if (err)
2384 goto fail;
2386 if (!(flags & VHD_OPEN_IGNORE_DISABLED) && vhd_disabled(ctx)) {
2387 err = -EINVAL;
2388 goto fail;
2391 if (vhd_type_dynamic(ctx)) {
2392 err = vhd_read_header(ctx, &ctx->header);
2393 if (err)
2394 goto fail;
2396 ctx->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
2397 ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3);
2400 return 0;
2402 fail:
2403 if (ctx->fd != -1)
2404 close(ctx->fd);
2405 free(ctx->file);
2406 memset(ctx, 0, sizeof(vhd_context_t));
2407 return err;
2410 void
2411 vhd_close(vhd_context_t *ctx)
2413 if (ctx->file)
2414 close(ctx->fd);
2415 free(ctx->file);
2416 free(ctx->bat.bat);
2417 free(ctx->batmap.map);
2418 memset(ctx, 0, sizeof(vhd_context_t));
2421 static inline void
2422 vhd_initialize_footer(vhd_context_t *ctx, int type, uint64_t size)
2424 memset(&ctx->footer, 0, sizeof(vhd_footer_t));
2425 memcpy(ctx->footer.cookie, HD_COOKIE, sizeof(ctx->footer.cookie));
2426 ctx->footer.features = HD_RESERVED;
2427 ctx->footer.ff_version = HD_FF_VERSION;
2428 ctx->footer.timestamp = vhd_time(time(NULL));
2429 ctx->footer.crtr_ver = VHD_CURRENT_VERSION;
2430 ctx->footer.crtr_os = 0x00000000;
2431 ctx->footer.orig_size = size;
2432 ctx->footer.curr_size = size;
2433 ctx->footer.geometry = vhd_chs(size);
2434 ctx->footer.type = type;
2435 ctx->footer.saved = 0;
2436 ctx->footer.data_offset = 0xFFFFFFFFFFFFFFFF;
2437 strcpy(ctx->footer.crtr_app, "tap");
2438 uuid_generate(ctx->footer.uuid);
2441 static int
2442 vhd_initialize_header_parent_name(vhd_context_t *ctx, const char *parent_path)
2444 int err;
2445 iconv_t cd;
2446 size_t ibl, obl;
2447 char *pname, *ppath, *dst;
2449 err = 0;
2450 pname = NULL;
2451 ppath = NULL;
2453 /*
2454 * MICROSOFT_COMPAT
2455 * big endian unicode here
2456 */
2457 cd = iconv_open(UTF_16BE, "ASCII");
2458 if (cd == (iconv_t)-1) {
2459 err = -errno;
2460 goto out;
2463 ppath = strdup(parent_path);
2464 if (!ppath) {
2465 err = -ENOMEM;
2466 goto out;
2469 pname = basename(ppath);
2470 if (!strcmp(pname, "")) {
2471 err = -EINVAL;
2472 goto out;
2475 ibl = strlen(pname);
2476 obl = sizeof(ctx->header.prt_name);
2477 dst = ctx->header.prt_name;
2479 memset(dst, 0, obl);
2481 if (iconv(cd, &pname, &ibl, &dst, &obl) == (size_t)-1 || ibl)
2482 err = (errno ? -errno : -EINVAL);
2484 out:
2485 iconv_close(cd);
2486 free(ppath);
2487 return err;
2490 static off64_t
2491 get_file_size(const char *name)
2493 int fd;
2494 off64_t end;
2496 fd = open(name, O_LARGEFILE | O_RDONLY);
2497 if (fd == -1) {
2498 VHDLOG("unable to open '%s': %d\n", name, errno);
2499 return -errno;
2501 end = lseek64(fd, 0, SEEK_END);
2502 close(fd);
2503 return end;
2506 static int
2507 vhd_initialize_header(vhd_context_t *ctx, const char *parent_path,
2508 uint64_t size, int raw)
2510 int err;
2511 struct stat stats;
2512 vhd_context_t parent;
2514 if (!vhd_type_dynamic(ctx))
2515 return -EINVAL;
2517 memset(&ctx->header, 0, sizeof(vhd_header_t));
2518 memcpy(ctx->header.cookie, DD_COOKIE, sizeof(ctx->header.cookie));
2519 ctx->header.data_offset = (uint64_t)-1;
2520 ctx->header.table_offset = VHD_SECTOR_SIZE * 3; /* 1 ftr + 2 hdr */
2521 ctx->header.hdr_ver = DD_VERSION;
2522 ctx->header.block_size = VHD_BLOCK_SIZE;
2523 ctx->header.prt_ts = 0;
2524 ctx->header.res1 = 0;
2525 ctx->header.max_bat_size = (ctx->footer.curr_size +
2526 VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
2528 ctx->footer.data_offset = VHD_SECTOR_SIZE;
2530 if (ctx->footer.type == HD_TYPE_DYNAMIC)
2531 return 0;
2533 err = stat(parent_path, &stats);
2534 if (err == -1)
2535 return -errno;
2537 if (raw) {
2538 ctx->header.prt_ts = vhd_time(stats.st_mtime);
2539 if (!size)
2540 size = get_file_size(parent_path);
2542 else {
2543 err = vhd_open(&parent, parent_path, VHD_OPEN_RDONLY);
2544 if (err)
2545 return err;
2547 ctx->header.prt_ts = vhd_time(stats.st_mtime);
2548 uuid_copy(ctx->header.prt_uuid, parent.footer.uuid);
2549 if (!size)
2550 size = parent.footer.curr_size;
2551 vhd_close(&parent);
2553 ctx->footer.orig_size = size;
2554 ctx->footer.curr_size = size;
2555 ctx->footer.geometry = vhd_chs(size);
2556 ctx->header.max_bat_size =
2557 (size + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
2559 return vhd_initialize_header_parent_name(ctx, parent_path);
2562 static int
2563 vhd_write_parent_locators(vhd_context_t *ctx, const char *parent)
2565 int i, err;
2566 off64_t off;
2567 uint32_t code;
2569 code = PLAT_CODE_NONE;
2571 if (ctx->footer.type != HD_TYPE_DIFF)
2572 return -EINVAL;
2574 off = ctx->batmap.header.batmap_offset +
2575 vhd_sectors_to_bytes(ctx->batmap.header.batmap_size);
2576 if (off & (VHD_SECTOR_SIZE - 1))
2577 off = vhd_bytes_padded(off);
2579 for (i = 0; i < 3; i++) {
2580 switch (i) {
2581 case 0:
2582 code = PLAT_CODE_MACX;
2583 break;
2584 case 1:
2585 code = PLAT_CODE_W2KU;
2586 break;
2587 case 2:
2588 code = PLAT_CODE_W2RU;
2589 break;
2592 err = vhd_parent_locator_write_at(ctx, parent, off, code,
2593 0, ctx->header.loc + i);
2594 if (err)
2595 return err;
2597 off += vhd_parent_locator_size(ctx->header.loc + i);
2600 return 0;
2603 int
2604 vhd_change_parent(vhd_context_t *child, char *parent_path, int raw)
2606 int i, err;
2607 char *ppath;
2608 struct stat stats;
2609 vhd_context_t parent;
2611 ppath = realpath(parent_path, NULL);
2612 if (!ppath) {
2613 VHDLOG("error resolving parent path %s for %s: %d\n",
2614 parent_path, child->file, errno);
2615 return -errno;
2618 err = stat(ppath, &stats);
2619 if (err == -1) {
2620 err = -errno;
2621 goto out;
2624 if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
2625 err = -EINVAL;
2626 goto out;
2629 if (raw) {
2630 uuid_clear(child->header.prt_uuid);
2631 } else {
2632 err = vhd_open(&parent, ppath, VHD_OPEN_RDONLY);
2633 if (err) {
2634 VHDLOG("error opening parent %s for %s: %d\n",
2635 ppath, child->file, err);
2636 goto out;
2638 uuid_copy(child->header.prt_uuid, parent.footer.uuid);
2639 vhd_close(&parent);
2642 vhd_initialize_header_parent_name(child, ppath);
2643 child->header.prt_ts = vhd_time(stats.st_mtime);
2645 for (i = 0; i < vhd_parent_locator_count(child); i++) {
2646 vhd_parent_locator_t *loc = child->header.loc + i;
2647 size_t max = vhd_parent_locator_size(loc);
2649 switch (loc->code) {
2650 case PLAT_CODE_MACX:
2651 case PLAT_CODE_W2KU:
2652 case PLAT_CODE_W2RU:
2653 break;
2654 default:
2655 continue;
2658 err = vhd_parent_locator_write_at(child, ppath,
2659 loc->data_offset,
2660 loc->code, max, loc);
2661 if (err) {
2662 VHDLOG("error writing parent locator %d for %s: %d\n",
2663 i, child->file, err);
2664 goto out;
2668 TEST_FAIL_AT(FAIL_REPARENT_LOCATOR);
2670 err = vhd_write_header(child, &child->header);
2671 if (err) {
2672 VHDLOG("error writing header for %s: %d\n", child->file, err);
2673 goto out;
2676 err = 0;
2678 out:
2679 free(ppath);
2680 return err;
2683 static int
2684 vhd_create_batmap(vhd_context_t *ctx)
2686 off64_t off;
2687 int err, map_bytes;
2688 vhd_batmap_header_t *header;
2690 if (!vhd_type_dynamic(ctx))
2691 return -EINVAL;
2693 map_bytes = (ctx->header.max_bat_size + 7) >> 3;
2694 header = &ctx->batmap.header;
2696 memset(header, 0, sizeof(vhd_batmap_header_t));
2697 memcpy(header->cookie, VHD_BATMAP_COOKIE, sizeof(header->cookie));
2699 err = vhd_batmap_header_offset(ctx, &off);
2700 if (err)
2701 return err;
2703 header->batmap_offset = off +
2704 vhd_bytes_padded(sizeof(vhd_batmap_header_t));
2705 header->batmap_size = secs_round_up_no_zero(map_bytes);
2706 header->batmap_version = VHD_BATMAP_CURRENT_VERSION;
2708 map_bytes = vhd_sectors_to_bytes(header->batmap_size);
2710 err = posix_memalign((void **)&ctx->batmap.map,
2711 VHD_SECTOR_SIZE, map_bytes);
2712 if (err) {
2713 ctx->batmap.map = NULL;
2714 return -err;
2717 memset(ctx->batmap.map, 0, map_bytes);
2719 return vhd_write_batmap(ctx, &ctx->batmap);
2722 static int
2723 vhd_create_bat(vhd_context_t *ctx)
2725 int i, err;
2726 size_t size;
2728 if (!vhd_type_dynamic(ctx))
2729 return -EINVAL;
2731 size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
2732 err = posix_memalign((void **)&ctx->bat.bat, VHD_SECTOR_SIZE, size);
2733 if (err) {
2734 ctx->bat.bat = NULL;
2735 return err;
2738 memset(ctx->bat.bat, 0, size);
2739 for (i = 0; i < ctx->header.max_bat_size; i++)
2740 ctx->bat.bat[i] = DD_BLK_UNUSED;
2742 err = vhd_seek(ctx, ctx->header.table_offset, SEEK_SET);
2743 if (err)
2744 return err;
2746 ctx->bat.entries = ctx->header.max_bat_size;
2747 ctx->bat.spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
2749 return vhd_write_bat(ctx, &ctx->bat);
2752 static int
2753 vhd_initialize_fixed_disk(vhd_context_t *ctx)
2755 char *buf;
2756 int i, err;
2758 if (ctx->footer.type != HD_TYPE_FIXED)
2759 return -EINVAL;
2761 err = vhd_seek(ctx, 0, SEEK_SET);
2762 if (err)
2763 return err;
2765 buf = mmap(0, VHD_BLOCK_SIZE, PROT_READ,
2766 MAP_SHARED | MAP_ANONYMOUS, -1, 0);
2767 if (buf == MAP_FAILED)
2768 return -errno;
2770 for (i = 0; i < ctx->footer.curr_size >> VHD_BLOCK_SHIFT; i++) {
2771 err = vhd_write(ctx, buf, VHD_BLOCK_SIZE);
2772 if (err)
2773 goto out;
2776 err = 0;
2778 out:
2779 munmap(buf, VHD_BLOCK_SIZE);
2780 return err;
2783 int
2784 vhd_get_phys_size(vhd_context_t *ctx, off64_t *size)
2786 int err;
2788 if ((err = vhd_end_of_data(ctx, size)))
2789 return err;
2790 *size += sizeof(vhd_footer_t);
2791 return 0;
2794 int
2795 vhd_set_phys_size(vhd_context_t *ctx, off64_t size)
2797 off64_t phys_size;
2798 int err;
2800 err = vhd_get_phys_size(ctx, &phys_size);
2801 if (err)
2802 return err;
2803 if (size < phys_size) {
2804 // would result in data loss
2805 VHDLOG("ERROR: new size (%"PRIu64") < phys size (%"PRIu64")\n",
2806 size, phys_size);
2807 return -EINVAL;
2809 return vhd_write_footer_at(ctx, &ctx->footer,
2810 size - sizeof(vhd_footer_t));
2813 static int
2814 __vhd_create(const char *name, const char *parent, uint64_t bytes, int type,
2815 vhd_flag_creat_t flags)
2817 int err;
2818 off64_t off;
2819 vhd_context_t ctx;
2820 vhd_footer_t *footer;
2821 vhd_header_t *header;
2822 uint64_t size, blks;
2824 switch (type) {
2825 case HD_TYPE_DIFF:
2826 if (!parent)
2827 return -EINVAL;
2828 case HD_TYPE_FIXED:
2829 case HD_TYPE_DYNAMIC:
2830 break;
2831 default:
2832 return -EINVAL;
2835 if (strnlen(name, VHD_MAX_NAME_LEN - 1) == VHD_MAX_NAME_LEN - 1)
2836 return -ENAMETOOLONG;
2838 memset(&ctx, 0, sizeof(vhd_context_t));
2839 footer = &ctx.footer;
2840 header = &ctx.header;
2841 blks = (bytes + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
2842 size = blks << VHD_BLOCK_SHIFT;
2844 ctx.fd = open(name, O_WRONLY | O_CREAT |
2845 O_TRUNC | O_LARGEFILE | O_DIRECT, 0644);
2846 if (ctx.fd == -1)
2847 return -errno;
2849 ctx.file = strdup(name);
2850 if (!ctx.file) {
2851 err = -ENOMEM;
2852 goto out;
2855 err = vhd_test_file_fixed(ctx.file, &ctx.is_block);
2856 if (err)
2857 goto out;
2859 vhd_initialize_footer(&ctx, type, size);
2861 if (type == HD_TYPE_FIXED) {
2862 err = vhd_initialize_fixed_disk(&ctx);
2863 if (err)
2864 goto out;
2865 } else {
2866 int raw = vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW);
2867 err = vhd_initialize_header(&ctx, parent, size, raw);
2868 if (err)
2869 goto out;
2871 err = vhd_write_footer_at(&ctx, &ctx.footer, 0);
2872 if (err)
2873 goto out;
2875 err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
2876 if (err)
2877 goto out;
2879 err = vhd_create_batmap(&ctx);
2880 if (err)
2881 goto out;
2883 err = vhd_create_bat(&ctx);
2884 if (err)
2885 goto out;
2887 if (type == HD_TYPE_DIFF) {
2888 err = vhd_write_parent_locators(&ctx, parent);
2889 if (err)
2890 goto out;
2893 /* write header again since it may have changed */
2894 err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
2895 if (err)
2896 goto out;
2899 err = vhd_seek(&ctx, 0, SEEK_END);
2900 if (err)
2901 goto out;
2903 off = vhd_position(&ctx);
2904 if (off == (off64_t)-1) {
2905 err = -errno;
2906 goto out;
2909 if (ctx.is_block)
2910 off -= sizeof(vhd_footer_t);
2912 err = vhd_write_footer_at(&ctx, &ctx.footer, off);
2913 if (err)
2914 goto out;
2916 err = 0;
2918 out:
2919 vhd_close(&ctx);
2920 if (err && !ctx.is_block)
2921 unlink(name);
2922 return err;
2925 int
2926 vhd_create(const char *name, uint64_t bytes, int type, vhd_flag_creat_t flags)
2928 return __vhd_create(name, NULL, bytes, type, flags);
2931 int
2932 vhd_snapshot(const char *name, uint64_t bytes, const char *parent,
2933 vhd_flag_creat_t flags)
2935 return __vhd_create(name, parent, bytes, HD_TYPE_DIFF, flags);
2938 static int
2939 __vhd_io_fixed_read(vhd_context_t *ctx,
2940 char *buf, uint64_t sec, uint32_t secs)
2942 int err;
2944 err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
2945 if (err)
2946 return err;
2948 return vhd_read(ctx, buf, vhd_sectors_to_bytes(secs));
2951 static void
2952 __vhd_io_dynamic_copy_data(vhd_context_t *ctx,
2953 char *map, int map_off,
2954 char *bitmap, int bitmap_off,
2955 char *dst, char *src, int secs)
2957 int i;
2959 for (i = 0; i < secs; i++) {
2960 if (test_bit(map, map_off + i))
2961 goto next;
2963 if (ctx && !vhd_bitmap_test(ctx, bitmap, bitmap_off + i))
2964 goto next;
2966 memcpy(dst, src, VHD_SECTOR_SIZE);
2967 set_bit(map, map_off + i);
2969 next:
2970 src += VHD_SECTOR_SIZE;
2971 dst += VHD_SECTOR_SIZE;
2975 static int
2976 __vhd_io_dynamic_read_link(vhd_context_t *ctx, char *map,
2977 char *buf, uint64_t sector, uint32_t secs)
2979 off64_t off;
2980 uint32_t blk, sec;
2981 int err, cnt, map_off;
2982 char *bitmap, *data, *src;
2984 map_off = 0;
2986 do {
2987 blk = sector / ctx->spb;
2988 sec = sector % ctx->spb;
2989 off = ctx->bat.bat[blk];
2990 data = NULL;
2991 bitmap = NULL;
2993 if (off == DD_BLK_UNUSED) {
2994 cnt = MIN(secs, ctx->spb);
2995 goto next;
2998 err = vhd_read_bitmap(ctx, blk, &bitmap);
2999 if (err)
3000 return err;
3002 err = vhd_read_block(ctx, blk, &data);
3003 if (err) {
3004 free(bitmap);
3005 return err;
3008 cnt = MIN(secs, ctx->spb - sec);
3009 src = data + vhd_sectors_to_bytes(sec);
3011 __vhd_io_dynamic_copy_data(ctx,
3012 map, map_off,
3013 bitmap, sec,
3014 buf, src, cnt);
3016 next:
3017 free(data);
3018 free(bitmap);
3020 secs -= cnt;
3021 sector += cnt;
3022 map_off += cnt;
3023 buf += vhd_sectors_to_bytes(cnt);
3025 } while (secs);
3027 return 0;
3030 static int
3031 __raw_read_link(char *filename,
3032 char *map, char *buf, uint64_t sec, uint32_t secs)
3034 int fd, err;
3035 off64_t off;
3036 uint64_t size;
3037 char *data;
3039 err = 0;
3040 errno = 0;
3041 fd = open(filename, O_RDONLY | O_DIRECT | O_LARGEFILE);
3042 if (fd == -1) {
3043 VHDLOG("%s: failed to open: %d\n", filename, -errno);
3044 return -errno;
3047 off = lseek64(fd, vhd_sectors_to_bytes(sec), SEEK_SET);
3048 if (off == (off64_t)-1) {
3049 VHDLOG("%s: seek(0x%08"PRIx64") failed: %d\n",
3050 filename, vhd_sectors_to_bytes(sec), -errno);
3051 err = -errno;
3052 goto close;
3055 size = vhd_sectors_to_bytes(secs);
3056 err = posix_memalign((void **)&data, VHD_SECTOR_SIZE, size);
3057 if (err)
3058 goto close;
3060 err = read(fd, data, size);
3061 if (err != size) {
3062 VHDLOG("%s: reading of %"PRIu64" returned %d, errno: %d\n",
3063 filename, size, err, -errno);
3064 free(data);
3065 err = errno ? -errno : -EIO;
3066 goto close;
3068 __vhd_io_dynamic_copy_data(NULL, map, 0, NULL, 0, buf, data, secs);
3069 free(data);
3070 err = 0;
3072 close:
3073 close(fd);
3074 return err;
3077 static int
3078 __vhd_io_dynamic_read(vhd_context_t *ctx,
3079 char *buf, uint64_t sec, uint32_t secs)
3081 int err;
3082 uint32_t i, done;
3083 char *map, *next;
3084 vhd_context_t parent, *vhd;
3086 err = vhd_get_bat(ctx);
3087 if (err)
3088 return err;
3090 vhd = ctx;
3091 next = NULL;
3092 map = calloc(1, secs << (VHD_SECTOR_SHIFT - 3));
3093 if (!map)
3094 return -ENOMEM;
3096 memset(buf, 0, vhd_sectors_to_bytes(secs));
3098 for (;;) {
3099 err = __vhd_io_dynamic_read_link(vhd, map, buf, sec, secs);
3100 if (err)
3101 goto close;
3103 for (done = 0, i = 0; i < secs; i++)
3104 if (test_bit(map, i))
3105 done++;
3107 if (done == secs) {
3108 err = 0;
3109 goto close;
3112 if (vhd->footer.type == HD_TYPE_DIFF) {
3113 err = vhd_parent_locator_get(vhd, &next);
3114 if (err)
3115 goto close;
3116 if (vhd_parent_raw(vhd)) {
3117 err = __raw_read_link(next, map, buf, sec,
3118 secs);
3119 goto close;
3121 } else {
3122 err = 0;
3123 goto close;
3126 if (vhd != ctx)
3127 vhd_close(vhd);
3128 vhd = &parent;
3130 err = vhd_open(vhd, next, VHD_OPEN_RDONLY);
3131 if (err)
3132 goto out;
3134 err = vhd_get_bat(vhd);
3135 if (err)
3136 goto close;
3138 free(next);
3139 next = NULL;
3142 close:
3143 if (vhd != ctx)
3144 vhd_close(vhd);
3145 out:
3146 free(map);
3147 free(next);
3148 return err;
3151 int
3152 vhd_io_read(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
3154 if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
3155 return -ERANGE;
3157 if (!vhd_type_dynamic(ctx))
3158 return __vhd_io_fixed_read(ctx, buf, sec, secs);
3160 return __vhd_io_dynamic_read(ctx, buf, sec, secs);
3163 static int
3164 __vhd_io_fixed_write(vhd_context_t *ctx,
3165 char *buf, uint64_t sec, uint32_t secs)
3167 int err;
3169 err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
3170 if (err)
3171 return err;
3173 return vhd_write(ctx, buf, vhd_sectors_to_bytes(secs));
3176 static int
3177 __vhd_io_allocate_block(vhd_context_t *ctx, uint32_t block)
3179 char *buf;
3180 size_t size;
3181 off64_t off, max;
3182 int i, err, gap, spp;
3184 spp = getpagesize() >> VHD_SECTOR_SHIFT;
3186 err = vhd_end_of_data(ctx, &max);
3187 if (err)
3188 return err;
3190 gap = 0;
3191 off = max;
3192 max >>= VHD_SECTOR_SHIFT;
3194 /* data region of segment should begin on page boundary */
3195 if ((max + ctx->bm_secs) % spp) {
3196 gap = (spp - ((max + ctx->bm_secs) % spp));
3197 max += gap;
3200 err = vhd_seek(ctx, off, SEEK_SET);
3201 if (err)
3202 return err;
3204 size = vhd_sectors_to_bytes(ctx->spb + ctx->bm_secs + gap);
3205 buf = mmap(0, size, PROT_READ, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
3206 if (buf == MAP_FAILED)
3207 return -errno;
3209 err = vhd_write(ctx, buf, size);
3210 if (err)
3211 goto out;
3213 ctx->bat.bat[block] = max;
3214 err = vhd_write_bat(ctx, &ctx->bat);
3215 if (err)
3216 goto out;
3218 err = 0;
3220 out:
3221 munmap(buf, size);
3222 return err;
3225 static int
3226 __vhd_io_dynamic_write(vhd_context_t *ctx,
3227 char *buf, uint64_t sector, uint32_t secs)
3229 char *map;
3230 off64_t off;
3231 uint32_t blk, sec;
3232 int i, err, cnt, ret;
3234 if (vhd_sectors_to_bytes(sector + secs) > ctx->footer.curr_size)
3235 return -ERANGE;
3237 err = vhd_get_bat(ctx);
3238 if (err)
3239 return err;
3241 if (vhd_has_batmap(ctx)) {
3242 err = vhd_get_batmap(ctx);
3243 if (err)
3244 return err;
3247 do {
3248 blk = sector / ctx->spb;
3249 sec = sector % ctx->spb;
3251 off = ctx->bat.bat[blk];
3252 if (off == DD_BLK_UNUSED) {
3253 err = __vhd_io_allocate_block(ctx, blk);
3254 if (err)
3255 return err;
3257 off = ctx->bat.bat[blk];
3260 off += ctx->bm_secs + sec;
3261 err = vhd_seek(ctx, vhd_sectors_to_bytes(off), SEEK_SET);
3262 if (err)
3263 return err;
3265 cnt = MIN(secs, ctx->spb - sec);
3266 err = vhd_write(ctx, buf, vhd_sectors_to_bytes(cnt));
3267 if (err)
3268 return err;
3270 if (vhd_has_batmap(ctx) &&
3271 vhd_batmap_test(ctx, &ctx->batmap, blk))
3272 goto next;
3274 err = vhd_read_bitmap(ctx, blk, &map);
3275 if (err)
3276 return err;
3278 for (i = 0; i < cnt; i++)
3279 vhd_bitmap_set(ctx, map, sec + i);
3281 err = vhd_write_bitmap(ctx, blk, map);
3282 if (err)
3283 goto fail;
3285 if (vhd_has_batmap(ctx)) {
3286 for (i = 0; i < ctx->spb; i++)
3287 if (!vhd_bitmap_test(ctx, map, i)) {
3288 free(map);
3289 goto next;
3292 vhd_batmap_set(ctx, &ctx->batmap, blk);
3293 err = vhd_write_batmap(ctx, &ctx->batmap);
3294 if (err)
3295 goto fail;
3298 free(map);
3299 map = NULL;
3301 next:
3302 secs -= cnt;
3303 sector += cnt;
3304 buf += vhd_sectors_to_bytes(cnt);
3305 } while (secs);
3307 err = 0;
3309 out:
3310 ret = vhd_write_footer(ctx, &ctx->footer);
3311 return (err ? err : ret);
3313 fail:
3314 free(map);
3315 goto out;
3318 int
3319 vhd_io_write(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
3321 if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
3322 return -ERANGE;
3324 if (!vhd_type_dynamic(ctx))
3325 return __vhd_io_fixed_write(ctx, buf, sec, secs);
3327 return __vhd_io_dynamic_write(ctx, buf, sec, secs);