ia64/xen-unstable

view tools/blktap2/vhd/lib/libvhd.c @ 19832:f1fec38c8228

blktap2: Further netbsd build fixes.

Signed-off-by: Christoph Egger <Christoph.Egger@amd.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Jun 24 14:03:20 2009 +0100 (2009-06-24)
parents b7f73a7f3078
children f3a909c8e8e6
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 strncpy(buf, footer->cookie, sizeof(buf));
254 buf[sizeof(buf)-1]= '\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 strncpy(buf, header->cookie, sizeof(buf));
315 buf[sizeof(buf)-1]= '\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, off_t *_off)
407 {
408 off_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, off_t *end)
596 {
597 int err, i, n;
598 uint32_t bat_bytes;
599 off_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 off_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 off_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, off_t *end)
655 {
656 int i, err;
657 off_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 == (off_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 off_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 == (off_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, off_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 off_t off;
963 err = vhd_seek(ctx, 0, SEEK_END);
964 if (err)
965 return err;
967 off = vhd_position(ctx);
968 if (off == (off_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, off_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 off_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 off_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 off_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 off_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, *uri_utf8, *uri_utf8p, *ret;
1312 const char *urip;
1314 err = 0;
1315 ret = NULL;
1316 *out = NULL;
1317 *outlen = 0;
1318 len = strlen(name) + strlen("file://");
1320 ibl = len;
1321 obl = len;
1323 urip = uri = malloc(ibl + 1);
1324 uri_utf8 = uri_utf8p = malloc(obl);
1326 if (!uri || !uri_utf8)
1327 return -ENOMEM;
1329 cd = iconv_open("UTF-8", "ASCII");
1330 if (cd == (iconv_t)-1) {
1331 err = -errno;
1332 goto out;
1335 snprintf(uri, ibl+1, "file://%s", name);
1337 if (iconv(cd,
1338 #if defined(__linux__) || (__Linux__)
1339 (char **)
1340 #endif
1341 &urip, &ibl, &uri_utf8p, &obl) == (size_t)-1 ||
1342 ibl || obl) {
1343 err = (errno ? -errno : -EIO);
1344 goto out;
1347 ret = malloc(len);
1348 if (!ret) {
1349 err = -ENOMEM;
1350 goto out;
1353 memcpy(ret, uri_utf8, len);
1354 *outlen = len;
1355 *out = ret;
1357 out:
1358 free(uri);
1359 free(uri_utf8);
1360 if (cd != (iconv_t)-1)
1361 iconv_close(cd);
1363 return err;
1366 static int
1367 vhd_w2u_encode_location(char *name, char **out, int *outlen)
1369 iconv_t cd;
1370 int len, err;
1371 size_t ibl, obl;
1372 char *uri, *uri_utf16, *uri_utf16p, *tmp, *ret;
1373 const char *urip;
1375 err = 0;
1376 ret = NULL;
1377 *out = NULL;
1378 *outlen = 0;
1379 cd = (iconv_t) -1;
1381 /*
1382 * MICROSOFT_COMPAT
1383 * relative paths must start with ".\"
1384 */
1385 if (name[0] != '/') {
1386 tmp = strstr(name, "./");
1387 if (tmp == name)
1388 tmp += strlen("./");
1389 else
1390 tmp = name;
1392 err = asprintf(&uri, ".\\%s", tmp);
1393 } else
1394 err = asprintf(&uri, "%s", name);
1396 if (err == -1)
1397 return -ENOMEM;
1399 tmp = uri;
1400 while (*tmp != '\0') {
1401 if (*tmp == '/')
1402 *tmp = '\\';
1403 tmp++;
1406 len = strlen(uri);
1407 ibl = len;
1408 obl = len * 2;
1409 urip = uri;
1411 uri_utf16 = uri_utf16p = malloc(obl);
1412 if (!uri_utf16) {
1413 err = -ENOMEM;
1414 goto out;
1417 /*
1418 * MICROSOFT_COMPAT
1419 * little endian unicode here
1420 */
1421 cd = iconv_open("UTF-16LE", "ASCII");
1422 if (cd == (iconv_t)-1) {
1423 err = -errno;
1424 goto out;
1427 if (iconv(cd,
1428 #if defined(__linux__) || (__Linux__)
1429 (char **)
1430 #endif
1431 &urip, &ibl, &uri_utf16p, &obl) == (size_t)-1 ||
1432 ibl || obl) {
1433 err = (errno ? -errno : -EIO);
1434 goto out;
1437 len = len * 2;
1438 ret = malloc(len);
1439 if (!ret) {
1440 err = -ENOMEM;
1441 goto out;
1444 memcpy(ret, uri_utf16, len);
1445 *outlen = len;
1446 *out = ret;
1447 err = 0;
1449 out:
1450 free(uri);
1451 free(uri_utf16);
1452 if (cd != (iconv_t)-1)
1453 iconv_close(cd);
1455 return err;
1458 static char *
1459 vhd_macx_decode_location(const char *in, char *out, int len)
1461 iconv_t cd;
1462 char *name;
1463 size_t ibl, obl;
1465 name = out;
1466 ibl = obl = len;
1468 cd = iconv_open("ASCII", "UTF-8");
1469 if (cd == (iconv_t)-1)
1470 return NULL;
1472 if (iconv(cd,
1473 #if defined(__linux__) || defined(__Linux__)
1474 (char **)
1475 #endif
1476 &in, &ibl, &out, &obl) == (size_t)-1 || ibl)
1477 return NULL;
1479 iconv_close(cd);
1480 *out = '\0';
1482 if (strstr(name, "file://") != name)
1483 return NULL;
1485 name += strlen("file://");
1487 return strdup(name);
1490 static char *
1491 vhd_w2u_decode_location(const char *in, char *out, int len, char *utf_type)
1493 iconv_t cd;
1494 char *name, *tmp;
1495 size_t ibl, obl;
1497 tmp = name = out;
1498 ibl = obl = len;
1500 cd = iconv_open("ASCII", utf_type);
1501 if (cd == (iconv_t)-1)
1502 return NULL;
1504 if (iconv(cd,
1505 #if defined(__linux__) || defined(__Linux__)
1506 (char **)
1507 #endif
1508 &in, &ibl, &out, &obl) == (size_t)-1 || ibl)
1509 return NULL;
1511 iconv_close(cd);
1512 *out = '\0';
1514 /* TODO: spaces */
1515 while (tmp != out) {
1516 if (*tmp == '\\')
1517 *tmp = '/';
1518 tmp++;
1521 if (strstr(name, "C:") == name || strstr(name, "c:") == name)
1522 name += strlen("c:");
1524 return strdup(name);
1527 int
1528 vhd_header_decode_parent(vhd_context_t *ctx, vhd_header_t *header, char **buf)
1530 char *code, out[512];
1532 if (vhd_creator_tapdisk(ctx) &&
1533 ctx->footer.crtr_ver == VHD_VERSION(0, 1))
1534 code = UTF_16;
1535 else
1536 code = UTF_16BE;
1538 *buf = vhd_w2u_decode_location(header->prt_name, out, 512, code);
1539 return (*buf == NULL ? -EINVAL : 0);
1542 int
1543 vhd_parent_locator_read(vhd_context_t *ctx,
1544 vhd_parent_locator_t *loc, char **parent)
1546 int err, size;
1547 char *raw, *out, *name;
1549 raw = NULL;
1550 out = NULL;
1551 name = NULL;
1552 *parent = NULL;
1554 if (ctx->footer.type != HD_TYPE_DIFF) {
1555 err = -EINVAL;
1556 goto out;
1559 switch (loc->code) {
1560 case PLAT_CODE_MACX:
1561 case PLAT_CODE_W2KU:
1562 case PLAT_CODE_W2RU:
1563 break;
1564 default:
1565 err = -EINVAL;
1566 goto out;
1569 err = vhd_seek(ctx, loc->data_offset, SEEK_SET);
1570 if (err)
1571 goto out;
1573 size = vhd_parent_locator_size(loc);
1574 if (size <= 0) {
1575 err = -EINVAL;
1576 goto out;
1579 err = posix_memalign((void **)&raw, VHD_SECTOR_SIZE, size);
1580 if (err) {
1581 raw = NULL;
1582 err = -err;
1583 goto out;
1586 err = vhd_read(ctx, raw, size);
1587 if (err)
1588 goto out;
1590 out = malloc(loc->data_len + 1);
1591 if (!out) {
1592 err = -ENOMEM;
1593 goto out;
1596 switch (loc->code) {
1597 case PLAT_CODE_MACX:
1598 name = vhd_macx_decode_location(raw, out, loc->data_len);
1599 break;
1600 case PLAT_CODE_W2KU:
1601 case PLAT_CODE_W2RU:
1602 name = vhd_w2u_decode_location(raw, out,
1603 loc->data_len, UTF_16LE);
1604 break;
1607 if (!name) {
1608 err = -EINVAL;
1609 goto out;
1612 err = 0;
1613 *parent = name;
1615 out:
1616 free(raw);
1617 free(out);
1619 if (err) {
1620 VHDLOG("%s: error reading parent locator: %d\n",
1621 ctx->file, err);
1622 VHDLOG("%s: locator: code %u, space 0x%x, len 0x%x, "
1623 "off 0x%"PRIx64"\n", ctx->file, loc->code, loc->data_space,
1624 loc->data_len, loc->data_offset);
1627 return err;
1630 int
1631 vhd_parent_locator_get(vhd_context_t *ctx, char **parent)
1633 int i, n, err;
1634 char *name, *location;
1635 vhd_parent_locator_t *loc;
1637 err = 0;
1638 *parent = NULL;
1640 if (ctx->footer.type != HD_TYPE_DIFF)
1641 return -EINVAL;
1643 n = vhd_parent_locator_count(ctx);
1644 for (i = 0; i < n; i++) {
1645 loc = ctx->header.loc + i;
1646 err = vhd_parent_locator_read(ctx, loc, &name);
1647 if (err)
1648 continue;
1650 err = vhd_find_parent(ctx, name, &location);
1651 if (err)
1652 VHDLOG("%s: couldn't find parent %s (%d)\n",
1653 ctx->file, name, err);
1654 free(name);
1656 if (!err) {
1657 *parent = location;
1658 return 0;
1662 return err;
1665 int
1666 vhd_parent_locator_write_at(vhd_context_t *ctx,
1667 const char *parent, off_t off, uint32_t code,
1668 size_t max_bytes, vhd_parent_locator_t *loc)
1670 struct stat stats;
1671 int err, len, size;
1672 char *absolute_path, *relative_path, *encoded, *block;
1674 memset(loc, 0, sizeof(vhd_parent_locator_t));
1676 if (ctx->footer.type != HD_TYPE_DIFF)
1677 return -EINVAL;
1679 absolute_path = NULL;
1680 relative_path = NULL;
1681 encoded = NULL;
1682 block = NULL;
1683 size = 0;
1684 len = 0;
1686 switch (code) {
1687 case PLAT_CODE_MACX:
1688 case PLAT_CODE_W2KU:
1689 case PLAT_CODE_W2RU:
1690 break;
1691 default:
1692 return -EINVAL;
1695 absolute_path = realpath(parent, NULL);
1696 if (!absolute_path) {
1697 err = -errno;
1698 goto out;
1701 err = stat(absolute_path, &stats);
1702 if (err) {
1703 err = -errno;
1704 goto out;
1707 if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
1708 err = -EINVAL;
1709 goto out;
1712 relative_path = relative_path_to(ctx->file, absolute_path, &err);
1713 if (!relative_path || err) {
1714 err = (err ? err : -EINVAL);
1715 goto out;
1718 switch (code) {
1719 case PLAT_CODE_MACX:
1720 err = vhd_macx_encode_location(relative_path, &encoded, &len);
1721 break;
1722 case PLAT_CODE_W2KU:
1723 case PLAT_CODE_W2RU:
1724 err = vhd_w2u_encode_location(relative_path, &encoded, &len);
1725 break;
1726 default:
1727 err = -EINVAL;
1730 if (err)
1731 goto out;
1733 err = vhd_seek(ctx, off, SEEK_SET);
1734 if (err)
1735 goto out;
1737 size = vhd_bytes_padded(len);
1739 if (max_bytes && size > max_bytes) {
1740 err = -ENAMETOOLONG;
1741 goto out;
1744 err = posix_memalign((void **)&block, VHD_SECTOR_SIZE, size);
1745 if (err) {
1746 block = NULL;
1747 err = -err;
1748 goto out;
1751 memset(block, 0, size);
1752 memcpy(block, encoded, len);
1754 err = vhd_write(ctx, block, size);
1755 if (err)
1756 goto out;
1758 err = 0;
1760 out:
1761 free(absolute_path);
1762 free(relative_path);
1763 free(encoded);
1764 free(block);
1766 if (!err) {
1767 loc->res = 0;
1768 loc->code = code;
1769 loc->data_len = len;
1770 /*
1771 * write number of bytes ('size') instead of number of sectors
1772 * into loc->data_space to be compatible with MSFT, even though
1773 * this goes against the specs
1774 */
1775 loc->data_space = size;
1776 loc->data_offset = off;
1779 return err;
1782 static int
1783 vhd_footer_offset_at_eof(vhd_context_t *ctx, off_t *off)
1785 int err;
1786 if ((err = vhd_seek(ctx, 0, SEEK_END)))
1787 return errno;
1788 *off = vhd_position(ctx) - sizeof(vhd_footer_t);
1789 return 0;
1792 int
1793 vhd_read_bitmap(vhd_context_t *ctx, uint32_t block, char **bufp)
1795 int err;
1796 char *buf;
1797 size_t size;
1798 off_t off;
1799 uint64_t blk;
1801 buf = NULL;
1802 *bufp = NULL;
1804 if (!vhd_type_dynamic(ctx))
1805 return -EINVAL;
1807 err = vhd_get_bat(ctx);
1808 if (err)
1809 return err;
1811 if (block >= ctx->bat.entries)
1812 return -ERANGE;
1814 blk = ctx->bat.bat[block];
1815 if (blk == DD_BLK_UNUSED)
1816 return -EINVAL;
1818 off = vhd_sectors_to_bytes(blk);
1819 size = vhd_bytes_padded(ctx->spb >> 3);
1821 err = vhd_seek(ctx, off, SEEK_SET);
1822 if (err)
1823 return err;
1825 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
1826 if (err)
1827 return -err;
1829 err = vhd_read(ctx, buf, size);
1830 if (err)
1831 goto fail;
1833 *bufp = buf;
1834 return 0;
1836 fail:
1837 free(buf);
1838 return err;
1841 int
1842 vhd_read_block(vhd_context_t *ctx, uint32_t block, char **bufp)
1844 int err;
1845 char *buf;
1846 size_t size;
1847 uint64_t blk;
1848 off_t end, off;
1850 buf = NULL;
1851 *bufp = NULL;
1853 if (!vhd_type_dynamic(ctx))
1854 return -EINVAL;
1856 err = vhd_get_bat(ctx);
1857 if (err)
1858 return err;
1860 if (block >= ctx->bat.entries)
1861 return -ERANGE;
1863 blk = ctx->bat.bat[block];
1864 if (blk == DD_BLK_UNUSED)
1865 return -EINVAL;
1867 off = vhd_sectors_to_bytes(blk + ctx->bm_secs);
1868 size = vhd_sectors_to_bytes(ctx->spb);
1870 err = vhd_footer_offset_at_eof(ctx, &end);
1871 if (err)
1872 return err;
1874 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
1875 if (err) {
1876 err = -err;
1877 goto fail;
1880 if (end < off + ctx->header.block_size) {
1881 size = end - off;
1882 memset(buf + size, 0, ctx->header.block_size - size);
1885 err = vhd_seek(ctx, off, SEEK_SET);
1886 if (err)
1887 goto fail;
1889 err = vhd_read(ctx, buf, size);
1890 if (err)
1891 goto fail;
1893 *bufp = buf;
1894 return 0;
1896 fail:
1897 free(buf);
1898 return err;
1901 int
1902 vhd_write_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off_t off)
1904 int err;
1905 vhd_footer_t *f;
1907 f = NULL;
1909 err = posix_memalign((void **)&f,
1910 VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
1911 if (err) {
1912 f = NULL;
1913 err = -err;
1914 goto out;
1917 memcpy(f, footer, sizeof(vhd_footer_t));
1918 f->checksum = vhd_checksum_footer(f);
1920 err = vhd_validate_footer(f);
1921 if (err)
1922 goto out;
1924 err = vhd_seek(ctx, off, SEEK_SET);
1925 if (err)
1926 goto out;
1928 vhd_footer_out(f);
1930 err = vhd_write(ctx, f, sizeof(vhd_footer_t));
1932 out:
1933 if (err)
1934 VHDLOG("%s: failed writing footer at 0x%08"PRIx64": %d\n",
1935 ctx->file, off, err);
1936 free(f);
1937 return err;
1940 int
1941 vhd_write_footer(vhd_context_t *ctx, vhd_footer_t *footer)
1943 int err;
1944 off_t off;
1946 if (ctx->is_block)
1947 err = vhd_footer_offset_at_eof(ctx, &off);
1948 else
1949 err = vhd_end_of_data(ctx, &off);
1950 if (err)
1951 return err;
1953 err = vhd_write_footer_at(ctx, footer, off);
1954 if (err)
1955 return err;
1957 if (!vhd_type_dynamic(ctx))
1958 return 0;
1960 return vhd_write_footer_at(ctx, footer, 0);
1963 int
1964 vhd_write_header_at(vhd_context_t *ctx, vhd_header_t *header, off_t off)
1966 int err;
1967 vhd_header_t *h;
1969 h = NULL;
1971 if (!vhd_type_dynamic(ctx)) {
1972 err = -EINVAL;
1973 goto out;
1976 err = posix_memalign((void **)&h,
1977 VHD_SECTOR_SIZE, sizeof(vhd_header_t));
1978 if (err) {
1979 h = NULL;
1980 err = -err;
1981 goto out;
1984 memcpy(h, header, sizeof(vhd_header_t));
1986 h->checksum = vhd_checksum_header(h);
1987 err = vhd_validate_header(h);
1988 if (err)
1989 goto out;
1991 vhd_header_out(h);
1993 err = vhd_seek(ctx, off, SEEK_SET);
1994 if (err)
1995 goto out;
1997 err = vhd_write(ctx, h, sizeof(vhd_header_t));
1999 out:
2000 if (err)
2001 VHDLOG("%s: failed writing header at 0x%08"PRIx64": %d\n",
2002 ctx->file, off, err);
2003 free(h);
2004 return err;
2007 int
2008 vhd_write_header(vhd_context_t *ctx, vhd_header_t *header)
2010 int err;
2011 off_t off;
2013 if (!vhd_type_dynamic(ctx))
2014 return -EINVAL;
2016 off = ctx->footer.data_offset;
2017 return vhd_write_header_at(ctx, header, off);
2020 int
2021 vhd_write_bat(vhd_context_t *ctx, vhd_bat_t *bat)
2023 int err;
2024 off_t off;
2025 vhd_bat_t b;
2026 size_t size;
2028 if (!vhd_type_dynamic(ctx))
2029 return -EINVAL;
2031 err = vhd_validate_bat(&ctx->bat);
2032 if (err)
2033 return err;
2035 err = vhd_validate_bat(bat);
2036 if (err)
2037 return err;
2039 memset(&b, 0, sizeof(vhd_bat_t));
2041 off = ctx->header.table_offset;
2042 size = vhd_bytes_padded(bat->entries * sizeof(uint32_t));
2044 err = vhd_seek(ctx, off, SEEK_SET);
2045 if (err)
2046 return err;
2048 err = posix_memalign((void **)&b.bat, VHD_SECTOR_SIZE, size);
2049 if (err)
2050 return -err;
2052 memcpy(b.bat, bat->bat, size);
2053 b.spb = bat->spb;
2054 b.entries = bat->entries;
2055 vhd_bat_out(&b);
2057 err = vhd_write(ctx, b.bat, size);
2058 free(b.bat);
2060 return err;
2063 int
2064 vhd_write_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap)
2066 int err;
2067 off_t off;
2068 vhd_batmap_t b;
2069 char *buf, *map;
2070 size_t size, map_size;
2072 buf = NULL;
2073 map = NULL;
2075 if (!vhd_has_batmap(ctx)) {
2076 err = -EINVAL;
2077 goto out;
2080 b.header = batmap->header;
2081 b.map = batmap->map;
2083 b.header.checksum = vhd_checksum_batmap(&b);
2084 err = vhd_validate_batmap(&b);
2085 if (err)
2086 goto out;
2088 off = b.header.batmap_offset;
2089 map_size = vhd_sectors_to_bytes(b.header.batmap_size);
2091 err = vhd_seek(ctx, off, SEEK_SET);
2092 if (err)
2093 goto out;
2095 err = posix_memalign((void **)&map, VHD_SECTOR_SIZE, map_size);
2096 if (err) {
2097 map = NULL;
2098 err = -err;
2099 goto out;
2102 memcpy(map, b.map, map_size);
2104 err = vhd_write(ctx, map, map_size);
2105 if (err)
2106 goto out;
2108 err = vhd_batmap_header_offset(ctx, &off);
2109 if (err)
2110 goto out;
2112 size = vhd_bytes_padded(sizeof(vhd_batmap_header_t));
2114 err = vhd_seek(ctx, off, SEEK_SET);
2115 if (err)
2116 goto out;
2118 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
2119 if (err) {
2120 err = -err;
2121 buf = NULL;
2122 goto out;
2125 vhd_batmap_header_out(&b);
2126 memset(buf, 0, size);
2127 memcpy(buf, &b.header, sizeof(vhd_batmap_header_t));
2129 err = vhd_write(ctx, buf, size);
2131 out:
2132 if (err)
2133 VHDLOG("%s: failed writing batmap: %d\n", ctx->file, err);
2134 free(buf);
2135 free(map);
2136 return 0;
2139 int
2140 vhd_write_bitmap(vhd_context_t *ctx, uint32_t block, char *bitmap)
2142 int err;
2143 off_t off;
2144 uint64_t blk;
2145 size_t secs, size;
2147 if (!vhd_type_dynamic(ctx))
2148 return -EINVAL;
2150 err = vhd_validate_bat(&ctx->bat);
2151 if (err)
2152 return err;
2154 if (block >= ctx->bat.entries)
2155 return -ERANGE;
2157 if ((unsigned long)bitmap & (VHD_SECTOR_SIZE - 1))
2158 return -EINVAL;
2160 blk = ctx->bat.bat[block];
2161 if (blk == DD_BLK_UNUSED)
2162 return -EINVAL;
2164 off = vhd_sectors_to_bytes(blk);
2165 size = vhd_sectors_to_bytes(ctx->bm_secs);
2167 err = vhd_seek(ctx, off, SEEK_SET);
2168 if (err)
2169 return err;
2171 err = vhd_write(ctx, bitmap, size);
2172 if (err)
2173 return err;
2175 return 0;
2178 int
2179 vhd_write_block(vhd_context_t *ctx, uint32_t block, char *data)
2181 int err;
2182 off_t off;
2183 size_t size;
2184 uint64_t blk;
2186 if (!vhd_type_dynamic(ctx))
2187 return -EINVAL;
2189 err = vhd_validate_bat(&ctx->bat);
2190 if (err)
2191 return err;
2193 if (block >= ctx->bat.entries)
2194 return -ERANGE;
2196 if ((unsigned long)data & ~(VHD_SECTOR_SIZE -1))
2197 return -EINVAL;
2199 blk = ctx->bat.bat[block];
2200 if (blk == DD_BLK_UNUSED)
2201 return -EINVAL;
2203 off = vhd_sectors_to_bytes(blk + ctx->bm_secs);
2204 size = vhd_sectors_to_bytes(ctx->spb);
2206 err = vhd_seek(ctx, off, SEEK_SET);
2207 if (err)
2208 return err;
2210 err = vhd_write(ctx, data, size);
2211 if (err)
2212 return err;
2214 return 0;
2217 static inline int
2218 namedup(char **dup, const char *name)
2220 *dup = NULL;
2222 if (strnlen(name, MAX_NAME_LEN) >= MAX_NAME_LEN)
2223 return -ENAMETOOLONG;
2225 *dup = strdup(name);
2226 if (*dup == NULL)
2227 return -ENOMEM;
2229 return 0;
2232 int
2233 vhd_seek(vhd_context_t *ctx, off_t offset, int whence)
2235 off_t off;
2237 off = lseek(ctx->fd, offset, whence);
2238 if (off == (off_t)-1) {
2239 VHDLOG("%s: seek(0x%08"PRIx64", %d) failed: %d\n",
2240 ctx->file, offset, whence, -errno);
2241 return -errno;
2244 return 0;
2247 off_t
2248 vhd_position(vhd_context_t *ctx)
2250 return lseek(ctx->fd, 0, SEEK_CUR);
2253 int
2254 vhd_read(vhd_context_t *ctx, void *buf, size_t size)
2256 size_t ret;
2258 errno = 0;
2260 ret = read(ctx->fd, buf, size);
2261 if (ret == size)
2262 return 0;
2264 VHDLOG("%s: read of %zu returned %zd, errno: %d\n",
2265 ctx->file, size, ret, -errno);
2267 return (errno ? -errno : -EIO);
2270 int
2271 vhd_write(vhd_context_t *ctx, void *buf, size_t size)
2273 size_t ret;
2275 errno = 0;
2277 ret = write(ctx->fd, buf, size);
2278 if (ret == size)
2279 return 0;
2281 VHDLOG("%s: write of %zu returned %zd, errno: %d\n",
2282 ctx->file, size, ret, -errno);
2284 return (errno ? -errno : -EIO);
2287 int
2288 vhd_offset(vhd_context_t *ctx, uint32_t sector, uint32_t *offset)
2290 int err;
2291 uint32_t block;
2293 if (!vhd_type_dynamic(ctx))
2294 return sector;
2296 err = vhd_get_bat(ctx);
2297 if (err)
2298 return err;
2300 block = sector / ctx->spb;
2301 if (ctx->bat.bat[block] == DD_BLK_UNUSED)
2302 *offset = DD_BLK_UNUSED;
2303 else
2304 *offset = ctx->bat.bat[block] +
2305 ctx->bm_secs + (sector % ctx->spb);
2307 return 0;
2310 int
2311 vhd_open_fast(vhd_context_t *ctx)
2313 int err;
2314 char *buf;
2315 size_t size;
2317 size = sizeof(vhd_footer_t) + sizeof(vhd_header_t);
2318 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
2319 if (err) {
2320 VHDLOG("failed allocating %s: %d\n", ctx->file, -err);
2321 return -err;
2324 err = vhd_read(ctx, buf, size);
2325 if (err) {
2326 VHDLOG("failed reading %s: %d\n", ctx->file, err);
2327 goto out;
2330 memcpy(&ctx->footer, buf, sizeof(vhd_footer_t));
2331 vhd_footer_in(&ctx->footer);
2332 err = vhd_validate_footer(&ctx->footer);
2333 if (err)
2334 goto out;
2336 if (vhd_type_dynamic(ctx)) {
2337 if (ctx->footer.data_offset != sizeof(vhd_footer_t))
2338 err = vhd_read_header(ctx, &ctx->header);
2339 else {
2340 memcpy(&ctx->header,
2341 buf + sizeof(vhd_footer_t),
2342 sizeof(vhd_header_t));
2343 vhd_header_in(&ctx->header);
2344 err = vhd_validate_header(&ctx->header);
2347 if (err)
2348 goto out;
2350 ctx->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
2351 ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3);
2354 out:
2355 free(buf);
2356 return err;
2359 int
2360 vhd_open(vhd_context_t *ctx, const char *file, int flags)
2362 int err, oflags;
2364 if (flags & VHD_OPEN_STRICT)
2365 vhd_flag_clear(flags, VHD_OPEN_FAST);
2367 memset(ctx, 0, sizeof(vhd_context_t));
2368 ctx->fd = -1;
2369 ctx->oflags = flags;
2371 err = namedup(&ctx->file, file);
2372 if (err)
2373 return err;
2375 oflags = O_DIRECT | O_LARGEFILE;
2376 if (flags & VHD_OPEN_RDONLY)
2377 oflags |= O_RDONLY;
2378 if (flags & VHD_OPEN_RDWR)
2379 oflags |= O_RDWR;
2381 ctx->fd = open(ctx->file, oflags, 0644);
2382 if (ctx->fd == -1) {
2383 err = -errno;
2384 VHDLOG("failed to open %s: %d\n", ctx->file, err);
2385 goto fail;
2388 err = vhd_test_file_fixed(ctx->file, &ctx->is_block);
2389 if (err)
2390 goto fail;
2392 if (flags & VHD_OPEN_FAST) {
2393 err = vhd_open_fast(ctx);
2394 if (err)
2395 goto fail;
2397 return 0;
2400 err = vhd_read_footer(ctx, &ctx->footer);
2401 if (err)
2402 goto fail;
2404 if (!(flags & VHD_OPEN_IGNORE_DISABLED) && vhd_disabled(ctx)) {
2405 err = -EINVAL;
2406 goto fail;
2409 if (vhd_type_dynamic(ctx)) {
2410 err = vhd_read_header(ctx, &ctx->header);
2411 if (err)
2412 goto fail;
2414 ctx->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
2415 ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3);
2418 return 0;
2420 fail:
2421 if (ctx->fd != -1)
2422 close(ctx->fd);
2423 free(ctx->file);
2424 memset(ctx, 0, sizeof(vhd_context_t));
2425 return err;
2428 void
2429 vhd_close(vhd_context_t *ctx)
2431 if (ctx->file)
2432 close(ctx->fd);
2433 free(ctx->file);
2434 free(ctx->bat.bat);
2435 free(ctx->batmap.map);
2436 memset(ctx, 0, sizeof(vhd_context_t));
2439 static inline void
2440 vhd_initialize_footer(vhd_context_t *ctx, int type, uint64_t size)
2442 memset(&ctx->footer, 0, sizeof(vhd_footer_t));
2443 memcpy(ctx->footer.cookie, HD_COOKIE, sizeof(ctx->footer.cookie));
2444 ctx->footer.features = HD_RESERVED;
2445 ctx->footer.ff_version = HD_FF_VERSION;
2446 ctx->footer.timestamp = vhd_time(time(NULL));
2447 ctx->footer.crtr_ver = VHD_CURRENT_VERSION;
2448 ctx->footer.crtr_os = 0x00000000;
2449 ctx->footer.orig_size = size;
2450 ctx->footer.curr_size = size;
2451 ctx->footer.geometry = vhd_chs(size);
2452 ctx->footer.type = type;
2453 ctx->footer.saved = 0;
2454 ctx->footer.data_offset = 0xFFFFFFFFFFFFFFFF;
2455 strcpy(ctx->footer.crtr_app, "tap");
2456 blk_uuid_generate(&ctx->footer.uuid);
2459 static int
2460 vhd_initialize_header_parent_name(vhd_context_t *ctx, const char *parent_path)
2462 int err;
2463 iconv_t cd;
2464 size_t ibl, obl;
2465 char *ppath, *dst;
2466 const char *pname;
2468 err = 0;
2469 pname = NULL;
2470 ppath = NULL;
2472 /*
2473 * MICROSOFT_COMPAT
2474 * big endian unicode here
2475 */
2476 cd = iconv_open(UTF_16BE, "ASCII");
2477 if (cd == (iconv_t)-1) {
2478 err = -errno;
2479 goto out;
2482 ppath = strdup(parent_path);
2483 if (!ppath) {
2484 err = -ENOMEM;
2485 goto out;
2488 pname = basename(ppath);
2489 if (!strcmp(pname, "")) {
2490 err = -EINVAL;
2491 goto out;
2494 ibl = strlen(pname);
2495 obl = sizeof(ctx->header.prt_name);
2496 dst = ctx->header.prt_name;
2498 memset(dst, 0, obl);
2500 if (iconv(cd,
2501 #if defined(__linux__) || defined(__Linux__)
2502 (char **)
2503 #endif
2504 &pname, &ibl, &dst, &obl) == (size_t)-1 || ibl)
2505 err = (errno ? -errno : -EINVAL);
2507 out:
2508 iconv_close(cd);
2509 free(ppath);
2510 return err;
2513 static off_t
2514 get_file_size(const char *name)
2516 int fd;
2517 off_t end;
2519 fd = open(name, O_LARGEFILE | O_RDONLY);
2520 if (fd == -1) {
2521 VHDLOG("unable to open '%s': %d\n", name, errno);
2522 return -errno;
2524 end = lseek(fd, 0, SEEK_END);
2525 close(fd);
2526 return end;
2529 static int
2530 vhd_initialize_header(vhd_context_t *ctx, const char *parent_path,
2531 uint64_t size, int raw)
2533 int err;
2534 struct stat stats;
2535 vhd_context_t parent;
2537 if (!vhd_type_dynamic(ctx))
2538 return -EINVAL;
2540 memset(&ctx->header, 0, sizeof(vhd_header_t));
2541 memcpy(ctx->header.cookie, DD_COOKIE, sizeof(ctx->header.cookie));
2542 ctx->header.data_offset = (uint64_t)-1;
2543 ctx->header.table_offset = VHD_SECTOR_SIZE * 3; /* 1 ftr + 2 hdr */
2544 ctx->header.hdr_ver = DD_VERSION;
2545 ctx->header.block_size = VHD_BLOCK_SIZE;
2546 ctx->header.prt_ts = 0;
2547 ctx->header.res1 = 0;
2548 ctx->header.max_bat_size = (ctx->footer.curr_size +
2549 VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
2551 ctx->footer.data_offset = VHD_SECTOR_SIZE;
2553 if (ctx->footer.type == HD_TYPE_DYNAMIC)
2554 return 0;
2556 err = stat(parent_path, &stats);
2557 if (err == -1)
2558 return -errno;
2560 if (raw) {
2561 ctx->header.prt_ts = vhd_time(stats.st_mtime);
2562 if (!size)
2563 size = get_file_size(parent_path);
2565 else {
2566 err = vhd_open(&parent, parent_path, VHD_OPEN_RDONLY);
2567 if (err)
2568 return err;
2570 ctx->header.prt_ts = vhd_time(stats.st_mtime);
2571 blk_uuid_copy(&ctx->header.prt_uuid, &parent.footer.uuid);
2572 if (!size)
2573 size = parent.footer.curr_size;
2574 vhd_close(&parent);
2576 ctx->footer.orig_size = size;
2577 ctx->footer.curr_size = size;
2578 ctx->footer.geometry = vhd_chs(size);
2579 ctx->header.max_bat_size =
2580 (size + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
2582 return vhd_initialize_header_parent_name(ctx, parent_path);
2585 static int
2586 vhd_write_parent_locators(vhd_context_t *ctx, const char *parent)
2588 int i, err;
2589 off_t off;
2590 uint32_t code;
2592 code = PLAT_CODE_NONE;
2594 if (ctx->footer.type != HD_TYPE_DIFF)
2595 return -EINVAL;
2597 off = ctx->batmap.header.batmap_offset +
2598 vhd_sectors_to_bytes(ctx->batmap.header.batmap_size);
2599 if (off & (VHD_SECTOR_SIZE - 1))
2600 off = vhd_bytes_padded(off);
2602 for (i = 0; i < 3; i++) {
2603 switch (i) {
2604 case 0:
2605 code = PLAT_CODE_MACX;
2606 break;
2607 case 1:
2608 code = PLAT_CODE_W2KU;
2609 break;
2610 case 2:
2611 code = PLAT_CODE_W2RU;
2612 break;
2615 err = vhd_parent_locator_write_at(ctx, parent, off, code,
2616 0, ctx->header.loc + i);
2617 if (err)
2618 return err;
2620 off += vhd_parent_locator_size(ctx->header.loc + i);
2623 return 0;
2626 int
2627 vhd_change_parent(vhd_context_t *child, char *parent_path, int raw)
2629 int i, err;
2630 char *ppath;
2631 struct stat stats;
2632 vhd_context_t parent;
2634 ppath = realpath(parent_path, NULL);
2635 if (!ppath) {
2636 VHDLOG("error resolving parent path %s for %s: %d\n",
2637 parent_path, child->file, errno);
2638 return -errno;
2641 err = stat(ppath, &stats);
2642 if (err == -1) {
2643 err = -errno;
2644 goto out;
2647 if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
2648 err = -EINVAL;
2649 goto out;
2652 if (raw) {
2653 blk_uuid_clear(&child->header.prt_uuid);
2654 } else {
2655 err = vhd_open(&parent, ppath, VHD_OPEN_RDONLY);
2656 if (err) {
2657 VHDLOG("error opening parent %s for %s: %d\n",
2658 ppath, child->file, err);
2659 goto out;
2661 blk_uuid_copy(&child->header.prt_uuid, &parent.footer.uuid);
2662 vhd_close(&parent);
2665 vhd_initialize_header_parent_name(child, ppath);
2666 child->header.prt_ts = vhd_time(stats.st_mtime);
2668 for (i = 0; i < vhd_parent_locator_count(child); i++) {
2669 vhd_parent_locator_t *loc = child->header.loc + i;
2670 size_t max = vhd_parent_locator_size(loc);
2672 switch (loc->code) {
2673 case PLAT_CODE_MACX:
2674 case PLAT_CODE_W2KU:
2675 case PLAT_CODE_W2RU:
2676 break;
2677 default:
2678 continue;
2681 err = vhd_parent_locator_write_at(child, ppath,
2682 loc->data_offset,
2683 loc->code, max, loc);
2684 if (err) {
2685 VHDLOG("error writing parent locator %d for %s: %d\n",
2686 i, child->file, err);
2687 goto out;
2691 TEST_FAIL_AT(FAIL_REPARENT_LOCATOR);
2693 err = vhd_write_header(child, &child->header);
2694 if (err) {
2695 VHDLOG("error writing header for %s: %d\n", child->file, err);
2696 goto out;
2699 err = 0;
2701 out:
2702 free(ppath);
2703 return err;
2706 static int
2707 vhd_create_batmap(vhd_context_t *ctx)
2709 off_t off;
2710 int err, map_bytes;
2711 vhd_batmap_header_t *header;
2713 if (!vhd_type_dynamic(ctx))
2714 return -EINVAL;
2716 map_bytes = (ctx->header.max_bat_size + 7) >> 3;
2717 header = &ctx->batmap.header;
2719 memset(header, 0, sizeof(vhd_batmap_header_t));
2720 memcpy(header->cookie, VHD_BATMAP_COOKIE, sizeof(header->cookie));
2722 err = vhd_batmap_header_offset(ctx, &off);
2723 if (err)
2724 return err;
2726 header->batmap_offset = off +
2727 vhd_bytes_padded(sizeof(vhd_batmap_header_t));
2728 header->batmap_size = secs_round_up_no_zero(map_bytes);
2729 header->batmap_version = VHD_BATMAP_CURRENT_VERSION;
2731 map_bytes = vhd_sectors_to_bytes(header->batmap_size);
2733 err = posix_memalign((void **)&ctx->batmap.map,
2734 VHD_SECTOR_SIZE, map_bytes);
2735 if (err) {
2736 ctx->batmap.map = NULL;
2737 return -err;
2740 memset(ctx->batmap.map, 0, map_bytes);
2742 return vhd_write_batmap(ctx, &ctx->batmap);
2745 static int
2746 vhd_create_bat(vhd_context_t *ctx)
2748 int i, err;
2749 size_t size;
2751 if (!vhd_type_dynamic(ctx))
2752 return -EINVAL;
2754 size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
2755 err = posix_memalign((void **)&ctx->bat.bat, VHD_SECTOR_SIZE, size);
2756 if (err) {
2757 ctx->bat.bat = NULL;
2758 return err;
2761 memset(ctx->bat.bat, 0, size);
2762 for (i = 0; i < ctx->header.max_bat_size; i++)
2763 ctx->bat.bat[i] = DD_BLK_UNUSED;
2765 err = vhd_seek(ctx, ctx->header.table_offset, SEEK_SET);
2766 if (err)
2767 return err;
2769 ctx->bat.entries = ctx->header.max_bat_size;
2770 ctx->bat.spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
2772 return vhd_write_bat(ctx, &ctx->bat);
2775 static int
2776 vhd_initialize_fixed_disk(vhd_context_t *ctx)
2778 char *buf;
2779 int i, err;
2781 if (ctx->footer.type != HD_TYPE_FIXED)
2782 return -EINVAL;
2784 err = vhd_seek(ctx, 0, SEEK_SET);
2785 if (err)
2786 return err;
2788 buf = mmap(0, VHD_BLOCK_SIZE, PROT_READ,
2789 MAP_SHARED | MAP_ANON, -1, 0);
2790 if (buf == MAP_FAILED)
2791 return -errno;
2793 for (i = 0; i < ctx->footer.curr_size >> VHD_BLOCK_SHIFT; i++) {
2794 err = vhd_write(ctx, buf, VHD_BLOCK_SIZE);
2795 if (err)
2796 goto out;
2799 err = 0;
2801 out:
2802 munmap(buf, VHD_BLOCK_SIZE);
2803 return err;
2806 int
2807 vhd_get_phys_size(vhd_context_t *ctx, off_t *size)
2809 int err;
2811 if ((err = vhd_end_of_data(ctx, size)))
2812 return err;
2813 *size += sizeof(vhd_footer_t);
2814 return 0;
2817 int
2818 vhd_set_phys_size(vhd_context_t *ctx, off_t size)
2820 off_t phys_size;
2821 int err;
2823 err = vhd_get_phys_size(ctx, &phys_size);
2824 if (err)
2825 return err;
2826 if (size < phys_size) {
2827 // would result in data loss
2828 VHDLOG("ERROR: new size (%"PRIu64") < phys size (%"PRIu64")\n",
2829 size, phys_size);
2830 return -EINVAL;
2832 return vhd_write_footer_at(ctx, &ctx->footer,
2833 size - sizeof(vhd_footer_t));
2836 static int
2837 __vhd_create(const char *name, const char *parent, uint64_t bytes, int type,
2838 vhd_flag_creat_t flags)
2840 int err;
2841 off_t off;
2842 vhd_context_t ctx;
2843 vhd_footer_t *footer;
2844 vhd_header_t *header;
2845 uint64_t size, blks;
2847 switch (type) {
2848 case HD_TYPE_DIFF:
2849 if (!parent)
2850 return -EINVAL;
2851 case HD_TYPE_FIXED:
2852 case HD_TYPE_DYNAMIC:
2853 break;
2854 default:
2855 return -EINVAL;
2858 if (strnlen(name, VHD_MAX_NAME_LEN - 1) == VHD_MAX_NAME_LEN - 1)
2859 return -ENAMETOOLONG;
2861 memset(&ctx, 0, sizeof(vhd_context_t));
2862 footer = &ctx.footer;
2863 header = &ctx.header;
2864 blks = (bytes + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
2865 size = blks << VHD_BLOCK_SHIFT;
2867 ctx.fd = open(name, O_WRONLY | O_CREAT |
2868 O_TRUNC | O_LARGEFILE | O_DIRECT, 0644);
2869 if (ctx.fd == -1)
2870 return -errno;
2872 ctx.file = strdup(name);
2873 if (!ctx.file) {
2874 err = -ENOMEM;
2875 goto out;
2878 err = vhd_test_file_fixed(ctx.file, &ctx.is_block);
2879 if (err)
2880 goto out;
2882 vhd_initialize_footer(&ctx, type, size);
2884 if (type == HD_TYPE_FIXED) {
2885 err = vhd_initialize_fixed_disk(&ctx);
2886 if (err)
2887 goto out;
2888 } else {
2889 int raw = vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW);
2890 err = vhd_initialize_header(&ctx, parent, size, raw);
2891 if (err)
2892 goto out;
2894 err = vhd_write_footer_at(&ctx, &ctx.footer, 0);
2895 if (err)
2896 goto out;
2898 err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
2899 if (err)
2900 goto out;
2902 err = vhd_create_batmap(&ctx);
2903 if (err)
2904 goto out;
2906 err = vhd_create_bat(&ctx);
2907 if (err)
2908 goto out;
2910 if (type == HD_TYPE_DIFF) {
2911 err = vhd_write_parent_locators(&ctx, parent);
2912 if (err)
2913 goto out;
2916 /* write header again since it may have changed */
2917 err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
2918 if (err)
2919 goto out;
2922 err = vhd_seek(&ctx, 0, SEEK_END);
2923 if (err)
2924 goto out;
2926 off = vhd_position(&ctx);
2927 if (off == (off_t)-1) {
2928 err = -errno;
2929 goto out;
2932 if (ctx.is_block)
2933 off -= sizeof(vhd_footer_t);
2935 err = vhd_write_footer_at(&ctx, &ctx.footer, off);
2936 if (err)
2937 goto out;
2939 err = 0;
2941 out:
2942 vhd_close(&ctx);
2943 if (err && !ctx.is_block)
2944 unlink(name);
2945 return err;
2948 int
2949 vhd_create(const char *name, uint64_t bytes, int type, vhd_flag_creat_t flags)
2951 return __vhd_create(name, NULL, bytes, type, flags);
2954 int
2955 vhd_snapshot(const char *name, uint64_t bytes, const char *parent,
2956 vhd_flag_creat_t flags)
2958 return __vhd_create(name, parent, bytes, HD_TYPE_DIFF, flags);
2961 static int
2962 __vhd_io_fixed_read(vhd_context_t *ctx,
2963 char *buf, uint64_t sec, uint32_t secs)
2965 int err;
2967 err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
2968 if (err)
2969 return err;
2971 return vhd_read(ctx, buf, vhd_sectors_to_bytes(secs));
2974 static void
2975 __vhd_io_dynamic_copy_data(vhd_context_t *ctx,
2976 char *map, int map_off,
2977 char *bitmap, int bitmap_off,
2978 char *dst, char *src, int secs)
2980 int i;
2982 for (i = 0; i < secs; i++) {
2983 if (test_bit(map, map_off + i))
2984 goto next;
2986 if (ctx && !vhd_bitmap_test(ctx, bitmap, bitmap_off + i))
2987 goto next;
2989 memcpy(dst, src, VHD_SECTOR_SIZE);
2990 set_bit(map, map_off + i);
2992 next:
2993 src += VHD_SECTOR_SIZE;
2994 dst += VHD_SECTOR_SIZE;
2998 static int
2999 __vhd_io_dynamic_read_link(vhd_context_t *ctx, char *map,
3000 char *buf, uint64_t sector, uint32_t secs)
3002 off_t off;
3003 uint32_t blk, sec;
3004 int err, cnt, map_off;
3005 char *bitmap, *data, *src;
3007 map_off = 0;
3009 do {
3010 blk = sector / ctx->spb;
3011 sec = sector % ctx->spb;
3012 off = ctx->bat.bat[blk];
3013 data = NULL;
3014 bitmap = NULL;
3016 if (off == DD_BLK_UNUSED) {
3017 cnt = MIN(secs, ctx->spb);
3018 goto next;
3021 err = vhd_read_bitmap(ctx, blk, &bitmap);
3022 if (err)
3023 return err;
3025 err = vhd_read_block(ctx, blk, &data);
3026 if (err) {
3027 free(bitmap);
3028 return err;
3031 cnt = MIN(secs, ctx->spb - sec);
3032 src = data + vhd_sectors_to_bytes(sec);
3034 __vhd_io_dynamic_copy_data(ctx,
3035 map, map_off,
3036 bitmap, sec,
3037 buf, src, cnt);
3039 next:
3040 free(data);
3041 free(bitmap);
3043 secs -= cnt;
3044 sector += cnt;
3045 map_off += cnt;
3046 buf += vhd_sectors_to_bytes(cnt);
3048 } while (secs);
3050 return 0;
3053 static int
3054 __raw_read_link(char *filename,
3055 char *map, char *buf, uint64_t sec, uint32_t secs)
3057 int fd, err;
3058 off_t off;
3059 uint64_t size;
3060 char *data;
3062 err = 0;
3063 errno = 0;
3064 fd = open(filename, O_RDONLY | O_DIRECT | O_LARGEFILE);
3065 if (fd == -1) {
3066 VHDLOG("%s: failed to open: %d\n", filename, -errno);
3067 return -errno;
3070 off = lseek(fd, vhd_sectors_to_bytes(sec), SEEK_SET);
3071 if (off == (off_t)-1) {
3072 VHDLOG("%s: seek(0x%08"PRIx64") failed: %d\n",
3073 filename, vhd_sectors_to_bytes(sec), -errno);
3074 err = -errno;
3075 goto close;
3078 size = vhd_sectors_to_bytes(secs);
3079 err = posix_memalign((void **)&data, VHD_SECTOR_SIZE, size);
3080 if (err)
3081 goto close;
3083 err = read(fd, data, size);
3084 if (err != size) {
3085 VHDLOG("%s: reading of %"PRIu64" returned %d, errno: %d\n",
3086 filename, size, err, -errno);
3087 free(data);
3088 err = errno ? -errno : -EIO;
3089 goto close;
3091 __vhd_io_dynamic_copy_data(NULL, map, 0, NULL, 0, buf, data, secs);
3092 free(data);
3093 err = 0;
3095 close:
3096 close(fd);
3097 return err;
3100 static int
3101 __vhd_io_dynamic_read(vhd_context_t *ctx,
3102 char *buf, uint64_t sec, uint32_t secs)
3104 int err;
3105 uint32_t i, done;
3106 char *map, *next;
3107 vhd_context_t parent, *vhd;
3109 err = vhd_get_bat(ctx);
3110 if (err)
3111 return err;
3113 vhd = ctx;
3114 next = NULL;
3115 map = calloc(1, secs << (VHD_SECTOR_SHIFT - 3));
3116 if (!map)
3117 return -ENOMEM;
3119 memset(buf, 0, vhd_sectors_to_bytes(secs));
3121 for (;;) {
3122 err = __vhd_io_dynamic_read_link(vhd, map, buf, sec, secs);
3123 if (err)
3124 goto close;
3126 for (done = 0, i = 0; i < secs; i++)
3127 if (test_bit(map, i))
3128 done++;
3130 if (done == secs) {
3131 err = 0;
3132 goto close;
3135 if (vhd->footer.type == HD_TYPE_DIFF) {
3136 err = vhd_parent_locator_get(vhd, &next);
3137 if (err)
3138 goto close;
3139 if (vhd_parent_raw(vhd)) {
3140 err = __raw_read_link(next, map, buf, sec,
3141 secs);
3142 goto close;
3144 } else {
3145 err = 0;
3146 goto close;
3149 if (vhd != ctx)
3150 vhd_close(vhd);
3151 vhd = &parent;
3153 err = vhd_open(vhd, next, VHD_OPEN_RDONLY);
3154 if (err)
3155 goto out;
3157 err = vhd_get_bat(vhd);
3158 if (err)
3159 goto close;
3161 free(next);
3162 next = NULL;
3165 close:
3166 if (vhd != ctx)
3167 vhd_close(vhd);
3168 out:
3169 free(map);
3170 free(next);
3171 return err;
3174 int
3175 vhd_io_read(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
3177 if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
3178 return -ERANGE;
3180 if (!vhd_type_dynamic(ctx))
3181 return __vhd_io_fixed_read(ctx, buf, sec, secs);
3183 return __vhd_io_dynamic_read(ctx, buf, sec, secs);
3186 static int
3187 __vhd_io_fixed_write(vhd_context_t *ctx,
3188 char *buf, uint64_t sec, uint32_t secs)
3190 int err;
3192 err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
3193 if (err)
3194 return err;
3196 return vhd_write(ctx, buf, vhd_sectors_to_bytes(secs));
3199 static int
3200 __vhd_io_allocate_block(vhd_context_t *ctx, uint32_t block)
3202 char *buf;
3203 size_t size;
3204 off_t off, max;
3205 int i, err, gap, spp;
3207 spp = getpagesize() >> VHD_SECTOR_SHIFT;
3209 err = vhd_end_of_data(ctx, &max);
3210 if (err)
3211 return err;
3213 gap = 0;
3214 off = max;
3215 max >>= VHD_SECTOR_SHIFT;
3217 /* data region of segment should begin on page boundary */
3218 if ((max + ctx->bm_secs) % spp) {
3219 gap = (spp - ((max + ctx->bm_secs) % spp));
3220 max += gap;
3223 err = vhd_seek(ctx, off, SEEK_SET);
3224 if (err)
3225 return err;
3227 size = vhd_sectors_to_bytes(ctx->spb + ctx->bm_secs + gap);
3228 buf = mmap(0, size, PROT_READ, MAP_SHARED | MAP_ANON, -1, 0);
3229 if (buf == MAP_FAILED)
3230 return -errno;
3232 err = vhd_write(ctx, buf, size);
3233 if (err)
3234 goto out;
3236 ctx->bat.bat[block] = max;
3237 err = vhd_write_bat(ctx, &ctx->bat);
3238 if (err)
3239 goto out;
3241 err = 0;
3243 out:
3244 munmap(buf, size);
3245 return err;
3248 static int
3249 __vhd_io_dynamic_write(vhd_context_t *ctx,
3250 char *buf, uint64_t sector, uint32_t secs)
3252 char *map;
3253 off_t off;
3254 uint32_t blk, sec;
3255 int i, err, cnt, ret;
3257 if (vhd_sectors_to_bytes(sector + secs) > ctx->footer.curr_size)
3258 return -ERANGE;
3260 err = vhd_get_bat(ctx);
3261 if (err)
3262 return err;
3264 if (vhd_has_batmap(ctx)) {
3265 err = vhd_get_batmap(ctx);
3266 if (err)
3267 return err;
3270 do {
3271 blk = sector / ctx->spb;
3272 sec = sector % ctx->spb;
3274 off = ctx->bat.bat[blk];
3275 if (off == DD_BLK_UNUSED) {
3276 err = __vhd_io_allocate_block(ctx, blk);
3277 if (err)
3278 return err;
3280 off = ctx->bat.bat[blk];
3283 off += ctx->bm_secs + sec;
3284 err = vhd_seek(ctx, vhd_sectors_to_bytes(off), SEEK_SET);
3285 if (err)
3286 return err;
3288 cnt = MIN(secs, ctx->spb - sec);
3289 err = vhd_write(ctx, buf, vhd_sectors_to_bytes(cnt));
3290 if (err)
3291 return err;
3293 if (vhd_has_batmap(ctx) &&
3294 vhd_batmap_test(ctx, &ctx->batmap, blk))
3295 goto next;
3297 err = vhd_read_bitmap(ctx, blk, &map);
3298 if (err)
3299 return err;
3301 for (i = 0; i < cnt; i++)
3302 vhd_bitmap_set(ctx, map, sec + i);
3304 err = vhd_write_bitmap(ctx, blk, map);
3305 if (err)
3306 goto fail;
3308 if (vhd_has_batmap(ctx)) {
3309 for (i = 0; i < ctx->spb; i++)
3310 if (!vhd_bitmap_test(ctx, map, i)) {
3311 free(map);
3312 goto next;
3315 vhd_batmap_set(ctx, &ctx->batmap, blk);
3316 err = vhd_write_batmap(ctx, &ctx->batmap);
3317 if (err)
3318 goto fail;
3321 free(map);
3322 map = NULL;
3324 next:
3325 secs -= cnt;
3326 sector += cnt;
3327 buf += vhd_sectors_to_bytes(cnt);
3328 } while (secs);
3330 err = 0;
3332 out:
3333 ret = vhd_write_footer(ctx, &ctx->footer);
3334 return (err ? err : ret);
3336 fail:
3337 free(map);
3338 goto out;
3341 int
3342 vhd_io_write(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
3344 if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
3345 return -ERANGE;
3347 if (!vhd_type_dynamic(ctx))
3348 return __vhd_io_fixed_write(ctx, buf, sec, secs);
3350 return __vhd_io_dynamic_write(ctx, buf, sec, secs);