ia64/xen-unstable

view tools/blktap2/vhd/lib/libvhd.c @ 19817:b7f73a7f3078

blktap2: portability fixes for NetBSD

- Use standard off_t and lseek() instead of non-portable off64_t and
lseek64()
- Use uuid API as documented in DCE 1.1 RPC specification
- Add NetBSD implementation for blk_getimagesize() and
blk_getsectorsize()
- Use blk_getimagesize() and blk_getsectorsize()
- Fix uuid header check

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 Tue Jun 23 17:24:14 2009 +0100 (2009-06-23)
parents 1c627434605e
children f1fec38c8228
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, *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 snprintf(uri, ibl+1, "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(const 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, (char **)&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(const 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, (char **)&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, off_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, off_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 off_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 off_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, off_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 off_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, off_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 off_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 off_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 off_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 off_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 off_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, off_t offset, int whence)
2217 off_t off;
2219 off = lseek(ctx->fd, offset, whence);
2220 if (off == (off_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 off_t
2230 vhd_position(vhd_context_t *ctx)
2232 return lseek(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 *ppath, *dst;
2448 const char *pname;
2450 err = 0;
2451 pname = NULL;
2452 ppath = NULL;
2454 /*
2455 * MICROSOFT_COMPAT
2456 * big endian unicode here
2457 */
2458 cd = iconv_open(UTF_16BE, "ASCII");
2459 if (cd == (iconv_t)-1) {
2460 err = -errno;
2461 goto out;
2464 ppath = strdup(parent_path);
2465 if (!ppath) {
2466 err = -ENOMEM;
2467 goto out;
2470 pname = basename(ppath);
2471 if (!strcmp(pname, "")) {
2472 err = -EINVAL;
2473 goto out;
2476 ibl = strlen(pname);
2477 obl = sizeof(ctx->header.prt_name);
2478 dst = ctx->header.prt_name;
2480 memset(dst, 0, obl);
2482 if (iconv(cd, (char **)&pname, &ibl, &dst, &obl) == (size_t)-1 || ibl)
2483 err = (errno ? -errno : -EINVAL);
2485 out:
2486 iconv_close(cd);
2487 free(ppath);
2488 return err;
2491 static off_t
2492 get_file_size(const char *name)
2494 int fd;
2495 off_t end;
2497 fd = open(name, O_LARGEFILE | O_RDONLY);
2498 if (fd == -1) {
2499 VHDLOG("unable to open '%s': %d\n", name, errno);
2500 return -errno;
2502 end = lseek(fd, 0, SEEK_END);
2503 close(fd);
2504 return end;
2507 static int
2508 vhd_initialize_header(vhd_context_t *ctx, const char *parent_path,
2509 uint64_t size, int raw)
2511 int err;
2512 struct stat stats;
2513 vhd_context_t parent;
2515 if (!vhd_type_dynamic(ctx))
2516 return -EINVAL;
2518 memset(&ctx->header, 0, sizeof(vhd_header_t));
2519 memcpy(ctx->header.cookie, DD_COOKIE, sizeof(ctx->header.cookie));
2520 ctx->header.data_offset = (uint64_t)-1;
2521 ctx->header.table_offset = VHD_SECTOR_SIZE * 3; /* 1 ftr + 2 hdr */
2522 ctx->header.hdr_ver = DD_VERSION;
2523 ctx->header.block_size = VHD_BLOCK_SIZE;
2524 ctx->header.prt_ts = 0;
2525 ctx->header.res1 = 0;
2526 ctx->header.max_bat_size = (ctx->footer.curr_size +
2527 VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
2529 ctx->footer.data_offset = VHD_SECTOR_SIZE;
2531 if (ctx->footer.type == HD_TYPE_DYNAMIC)
2532 return 0;
2534 err = stat(parent_path, &stats);
2535 if (err == -1)
2536 return -errno;
2538 if (raw) {
2539 ctx->header.prt_ts = vhd_time(stats.st_mtime);
2540 if (!size)
2541 size = get_file_size(parent_path);
2543 else {
2544 err = vhd_open(&parent, parent_path, VHD_OPEN_RDONLY);
2545 if (err)
2546 return err;
2548 ctx->header.prt_ts = vhd_time(stats.st_mtime);
2549 uuid_copy(ctx->header.prt_uuid, parent.footer.uuid);
2550 if (!size)
2551 size = parent.footer.curr_size;
2552 vhd_close(&parent);
2554 ctx->footer.orig_size = size;
2555 ctx->footer.curr_size = size;
2556 ctx->footer.geometry = vhd_chs(size);
2557 ctx->header.max_bat_size =
2558 (size + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
2560 return vhd_initialize_header_parent_name(ctx, parent_path);
2563 static int
2564 vhd_write_parent_locators(vhd_context_t *ctx, const char *parent)
2566 int i, err;
2567 off_t off;
2568 uint32_t code;
2570 code = PLAT_CODE_NONE;
2572 if (ctx->footer.type != HD_TYPE_DIFF)
2573 return -EINVAL;
2575 off = ctx->batmap.header.batmap_offset +
2576 vhd_sectors_to_bytes(ctx->batmap.header.batmap_size);
2577 if (off & (VHD_SECTOR_SIZE - 1))
2578 off = vhd_bytes_padded(off);
2580 for (i = 0; i < 3; i++) {
2581 switch (i) {
2582 case 0:
2583 code = PLAT_CODE_MACX;
2584 break;
2585 case 1:
2586 code = PLAT_CODE_W2KU;
2587 break;
2588 case 2:
2589 code = PLAT_CODE_W2RU;
2590 break;
2593 err = vhd_parent_locator_write_at(ctx, parent, off, code,
2594 0, ctx->header.loc + i);
2595 if (err)
2596 return err;
2598 off += vhd_parent_locator_size(ctx->header.loc + i);
2601 return 0;
2604 int
2605 vhd_change_parent(vhd_context_t *child, char *parent_path, int raw)
2607 int i, err;
2608 char *ppath;
2609 struct stat stats;
2610 vhd_context_t parent;
2612 ppath = realpath(parent_path, NULL);
2613 if (!ppath) {
2614 VHDLOG("error resolving parent path %s for %s: %d\n",
2615 parent_path, child->file, errno);
2616 return -errno;
2619 err = stat(ppath, &stats);
2620 if (err == -1) {
2621 err = -errno;
2622 goto out;
2625 if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
2626 err = -EINVAL;
2627 goto out;
2630 if (raw) {
2631 uuid_clear(child->header.prt_uuid);
2632 } else {
2633 err = vhd_open(&parent, ppath, VHD_OPEN_RDONLY);
2634 if (err) {
2635 VHDLOG("error opening parent %s for %s: %d\n",
2636 ppath, child->file, err);
2637 goto out;
2639 uuid_copy(child->header.prt_uuid, parent.footer.uuid);
2640 vhd_close(&parent);
2643 vhd_initialize_header_parent_name(child, ppath);
2644 child->header.prt_ts = vhd_time(stats.st_mtime);
2646 for (i = 0; i < vhd_parent_locator_count(child); i++) {
2647 vhd_parent_locator_t *loc = child->header.loc + i;
2648 size_t max = vhd_parent_locator_size(loc);
2650 switch (loc->code) {
2651 case PLAT_CODE_MACX:
2652 case PLAT_CODE_W2KU:
2653 case PLAT_CODE_W2RU:
2654 break;
2655 default:
2656 continue;
2659 err = vhd_parent_locator_write_at(child, ppath,
2660 loc->data_offset,
2661 loc->code, max, loc);
2662 if (err) {
2663 VHDLOG("error writing parent locator %d for %s: %d\n",
2664 i, child->file, err);
2665 goto out;
2669 TEST_FAIL_AT(FAIL_REPARENT_LOCATOR);
2671 err = vhd_write_header(child, &child->header);
2672 if (err) {
2673 VHDLOG("error writing header for %s: %d\n", child->file, err);
2674 goto out;
2677 err = 0;
2679 out:
2680 free(ppath);
2681 return err;
2684 static int
2685 vhd_create_batmap(vhd_context_t *ctx)
2687 off_t off;
2688 int err, map_bytes;
2689 vhd_batmap_header_t *header;
2691 if (!vhd_type_dynamic(ctx))
2692 return -EINVAL;
2694 map_bytes = (ctx->header.max_bat_size + 7) >> 3;
2695 header = &ctx->batmap.header;
2697 memset(header, 0, sizeof(vhd_batmap_header_t));
2698 memcpy(header->cookie, VHD_BATMAP_COOKIE, sizeof(*header->cookie));
2700 err = vhd_batmap_header_offset(ctx, &off);
2701 if (err)
2702 return err;
2704 header->batmap_offset = off +
2705 vhd_bytes_padded(sizeof(vhd_batmap_header_t));
2706 header->batmap_size = secs_round_up_no_zero(map_bytes);
2707 header->batmap_version = VHD_BATMAP_CURRENT_VERSION;
2709 map_bytes = vhd_sectors_to_bytes(header->batmap_size);
2711 err = posix_memalign((void **)&ctx->batmap.map,
2712 VHD_SECTOR_SIZE, map_bytes);
2713 if (err) {
2714 ctx->batmap.map = NULL;
2715 return -err;
2718 memset(ctx->batmap.map, 0, map_bytes);
2720 return vhd_write_batmap(ctx, &ctx->batmap);
2723 static int
2724 vhd_create_bat(vhd_context_t *ctx)
2726 int i, err;
2727 size_t size;
2729 if (!vhd_type_dynamic(ctx))
2730 return -EINVAL;
2732 size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
2733 err = posix_memalign((void **)&ctx->bat.bat, VHD_SECTOR_SIZE, size);
2734 if (err) {
2735 ctx->bat.bat = NULL;
2736 return err;
2739 memset(ctx->bat.bat, 0, size);
2740 for (i = 0; i < ctx->header.max_bat_size; i++)
2741 ctx->bat.bat[i] = DD_BLK_UNUSED;
2743 err = vhd_seek(ctx, ctx->header.table_offset, SEEK_SET);
2744 if (err)
2745 return err;
2747 ctx->bat.entries = ctx->header.max_bat_size;
2748 ctx->bat.spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
2750 return vhd_write_bat(ctx, &ctx->bat);
2753 static int
2754 vhd_initialize_fixed_disk(vhd_context_t *ctx)
2756 char *buf;
2757 int i, err;
2759 if (ctx->footer.type != HD_TYPE_FIXED)
2760 return -EINVAL;
2762 err = vhd_seek(ctx, 0, SEEK_SET);
2763 if (err)
2764 return err;
2766 buf = mmap(0, VHD_BLOCK_SIZE, PROT_READ,
2767 MAP_SHARED | MAP_ANON, -1, 0);
2768 if (buf == MAP_FAILED)
2769 return -errno;
2771 for (i = 0; i < ctx->footer.curr_size >> VHD_BLOCK_SHIFT; i++) {
2772 err = vhd_write(ctx, buf, VHD_BLOCK_SIZE);
2773 if (err)
2774 goto out;
2777 err = 0;
2779 out:
2780 munmap(buf, VHD_BLOCK_SIZE);
2781 return err;
2784 int
2785 vhd_get_phys_size(vhd_context_t *ctx, off_t *size)
2787 int err;
2789 if ((err = vhd_end_of_data(ctx, size)))
2790 return err;
2791 *size += sizeof(vhd_footer_t);
2792 return 0;
2795 int
2796 vhd_set_phys_size(vhd_context_t *ctx, off_t size)
2798 off_t phys_size;
2799 int err;
2801 err = vhd_get_phys_size(ctx, &phys_size);
2802 if (err)
2803 return err;
2804 if (size < phys_size) {
2805 // would result in data loss
2806 VHDLOG("ERROR: new size (%"PRIu64") < phys size (%"PRIu64")\n",
2807 size, phys_size);
2808 return -EINVAL;
2810 return vhd_write_footer_at(ctx, &ctx->footer,
2811 size - sizeof(vhd_footer_t));
2814 static int
2815 __vhd_create(const char *name, const char *parent, uint64_t bytes, int type,
2816 vhd_flag_creat_t flags)
2818 int err;
2819 off_t off;
2820 vhd_context_t ctx;
2821 vhd_footer_t *footer;
2822 vhd_header_t *header;
2823 uint64_t size, blks;
2825 switch (type) {
2826 case HD_TYPE_DIFF:
2827 if (!parent)
2828 return -EINVAL;
2829 case HD_TYPE_FIXED:
2830 case HD_TYPE_DYNAMIC:
2831 break;
2832 default:
2833 return -EINVAL;
2836 if (strnlen(name, VHD_MAX_NAME_LEN - 1) == VHD_MAX_NAME_LEN - 1)
2837 return -ENAMETOOLONG;
2839 memset(&ctx, 0, sizeof(vhd_context_t));
2840 footer = &ctx.footer;
2841 header = &ctx.header;
2842 blks = (bytes + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
2843 size = blks << VHD_BLOCK_SHIFT;
2845 ctx.fd = open(name, O_WRONLY | O_CREAT |
2846 O_TRUNC | O_LARGEFILE | O_DIRECT, 0644);
2847 if (ctx.fd == -1)
2848 return -errno;
2850 ctx.file = strdup(name);
2851 if (!ctx.file) {
2852 err = -ENOMEM;
2853 goto out;
2856 err = vhd_test_file_fixed(ctx.file, &ctx.is_block);
2857 if (err)
2858 goto out;
2860 vhd_initialize_footer(&ctx, type, size);
2862 if (type == HD_TYPE_FIXED) {
2863 err = vhd_initialize_fixed_disk(&ctx);
2864 if (err)
2865 goto out;
2866 } else {
2867 int raw = vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW);
2868 err = vhd_initialize_header(&ctx, parent, size, raw);
2869 if (err)
2870 goto out;
2872 err = vhd_write_footer_at(&ctx, &ctx.footer, 0);
2873 if (err)
2874 goto out;
2876 err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
2877 if (err)
2878 goto out;
2880 err = vhd_create_batmap(&ctx);
2881 if (err)
2882 goto out;
2884 err = vhd_create_bat(&ctx);
2885 if (err)
2886 goto out;
2888 if (type == HD_TYPE_DIFF) {
2889 err = vhd_write_parent_locators(&ctx, parent);
2890 if (err)
2891 goto out;
2894 /* write header again since it may have changed */
2895 err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
2896 if (err)
2897 goto out;
2900 err = vhd_seek(&ctx, 0, SEEK_END);
2901 if (err)
2902 goto out;
2904 off = vhd_position(&ctx);
2905 if (off == (off_t)-1) {
2906 err = -errno;
2907 goto out;
2910 if (ctx.is_block)
2911 off -= sizeof(vhd_footer_t);
2913 err = vhd_write_footer_at(&ctx, &ctx.footer, off);
2914 if (err)
2915 goto out;
2917 err = 0;
2919 out:
2920 vhd_close(&ctx);
2921 if (err && !ctx.is_block)
2922 unlink(name);
2923 return err;
2926 int
2927 vhd_create(const char *name, uint64_t bytes, int type, vhd_flag_creat_t flags)
2929 return __vhd_create(name, NULL, bytes, type, flags);
2932 int
2933 vhd_snapshot(const char *name, uint64_t bytes, const char *parent,
2934 vhd_flag_creat_t flags)
2936 return __vhd_create(name, parent, bytes, HD_TYPE_DIFF, flags);
2939 static int
2940 __vhd_io_fixed_read(vhd_context_t *ctx,
2941 char *buf, uint64_t sec, uint32_t secs)
2943 int err;
2945 err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
2946 if (err)
2947 return err;
2949 return vhd_read(ctx, buf, vhd_sectors_to_bytes(secs));
2952 static void
2953 __vhd_io_dynamic_copy_data(vhd_context_t *ctx,
2954 char *map, int map_off,
2955 char *bitmap, int bitmap_off,
2956 char *dst, char *src, int secs)
2958 int i;
2960 for (i = 0; i < secs; i++) {
2961 if (test_bit(map, map_off + i))
2962 goto next;
2964 if (ctx && !vhd_bitmap_test(ctx, bitmap, bitmap_off + i))
2965 goto next;
2967 memcpy(dst, src, VHD_SECTOR_SIZE);
2968 set_bit(map, map_off + i);
2970 next:
2971 src += VHD_SECTOR_SIZE;
2972 dst += VHD_SECTOR_SIZE;
2976 static int
2977 __vhd_io_dynamic_read_link(vhd_context_t *ctx, char *map,
2978 char *buf, uint64_t sector, uint32_t secs)
2980 off_t off;
2981 uint32_t blk, sec;
2982 int err, cnt, map_off;
2983 char *bitmap, *data, *src;
2985 map_off = 0;
2987 do {
2988 blk = sector / ctx->spb;
2989 sec = sector % ctx->spb;
2990 off = ctx->bat.bat[blk];
2991 data = NULL;
2992 bitmap = NULL;
2994 if (off == DD_BLK_UNUSED) {
2995 cnt = MIN(secs, ctx->spb);
2996 goto next;
2999 err = vhd_read_bitmap(ctx, blk, &bitmap);
3000 if (err)
3001 return err;
3003 err = vhd_read_block(ctx, blk, &data);
3004 if (err) {
3005 free(bitmap);
3006 return err;
3009 cnt = MIN(secs, ctx->spb - sec);
3010 src = data + vhd_sectors_to_bytes(sec);
3012 __vhd_io_dynamic_copy_data(ctx,
3013 map, map_off,
3014 bitmap, sec,
3015 buf, src, cnt);
3017 next:
3018 free(data);
3019 free(bitmap);
3021 secs -= cnt;
3022 sector += cnt;
3023 map_off += cnt;
3024 buf += vhd_sectors_to_bytes(cnt);
3026 } while (secs);
3028 return 0;
3031 static int
3032 __raw_read_link(char *filename,
3033 char *map, char *buf, uint64_t sec, uint32_t secs)
3035 int fd, err;
3036 off_t off;
3037 uint64_t size;
3038 char *data;
3040 err = 0;
3041 errno = 0;
3042 fd = open(filename, O_RDONLY | O_DIRECT | O_LARGEFILE);
3043 if (fd == -1) {
3044 VHDLOG("%s: failed to open: %d\n", filename, -errno);
3045 return -errno;
3048 off = lseek(fd, vhd_sectors_to_bytes(sec), SEEK_SET);
3049 if (off == (off_t)-1) {
3050 VHDLOG("%s: seek(0x%08"PRIx64") failed: %d\n",
3051 filename, vhd_sectors_to_bytes(sec), -errno);
3052 err = -errno;
3053 goto close;
3056 size = vhd_sectors_to_bytes(secs);
3057 err = posix_memalign((void **)&data, VHD_SECTOR_SIZE, size);
3058 if (err)
3059 goto close;
3061 err = read(fd, data, size);
3062 if (err != size) {
3063 VHDLOG("%s: reading of %"PRIu64" returned %d, errno: %d\n",
3064 filename, size, err, -errno);
3065 free(data);
3066 err = errno ? -errno : -EIO;
3067 goto close;
3069 __vhd_io_dynamic_copy_data(NULL, map, 0, NULL, 0, buf, data, secs);
3070 free(data);
3071 err = 0;
3073 close:
3074 close(fd);
3075 return err;
3078 static int
3079 __vhd_io_dynamic_read(vhd_context_t *ctx,
3080 char *buf, uint64_t sec, uint32_t secs)
3082 int err;
3083 uint32_t i, done;
3084 char *map, *next;
3085 vhd_context_t parent, *vhd;
3087 err = vhd_get_bat(ctx);
3088 if (err)
3089 return err;
3091 vhd = ctx;
3092 next = NULL;
3093 map = calloc(1, secs << (VHD_SECTOR_SHIFT - 3));
3094 if (!map)
3095 return -ENOMEM;
3097 memset(buf, 0, vhd_sectors_to_bytes(secs));
3099 for (;;) {
3100 err = __vhd_io_dynamic_read_link(vhd, map, buf, sec, secs);
3101 if (err)
3102 goto close;
3104 for (done = 0, i = 0; i < secs; i++)
3105 if (test_bit(map, i))
3106 done++;
3108 if (done == secs) {
3109 err = 0;
3110 goto close;
3113 if (vhd->footer.type == HD_TYPE_DIFF) {
3114 err = vhd_parent_locator_get(vhd, &next);
3115 if (err)
3116 goto close;
3117 if (vhd_parent_raw(vhd)) {
3118 err = __raw_read_link(next, map, buf, sec,
3119 secs);
3120 goto close;
3122 } else {
3123 err = 0;
3124 goto close;
3127 if (vhd != ctx)
3128 vhd_close(vhd);
3129 vhd = &parent;
3131 err = vhd_open(vhd, next, VHD_OPEN_RDONLY);
3132 if (err)
3133 goto out;
3135 err = vhd_get_bat(vhd);
3136 if (err)
3137 goto close;
3139 free(next);
3140 next = NULL;
3143 close:
3144 if (vhd != ctx)
3145 vhd_close(vhd);
3146 out:
3147 free(map);
3148 free(next);
3149 return err;
3152 int
3153 vhd_io_read(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
3155 if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
3156 return -ERANGE;
3158 if (!vhd_type_dynamic(ctx))
3159 return __vhd_io_fixed_read(ctx, buf, sec, secs);
3161 return __vhd_io_dynamic_read(ctx, buf, sec, secs);
3164 static int
3165 __vhd_io_fixed_write(vhd_context_t *ctx,
3166 char *buf, uint64_t sec, uint32_t secs)
3168 int err;
3170 err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
3171 if (err)
3172 return err;
3174 return vhd_write(ctx, buf, vhd_sectors_to_bytes(secs));
3177 static int
3178 __vhd_io_allocate_block(vhd_context_t *ctx, uint32_t block)
3180 char *buf;
3181 size_t size;
3182 off_t off, max;
3183 int i, err, gap, spp;
3185 spp = getpagesize() >> VHD_SECTOR_SHIFT;
3187 err = vhd_end_of_data(ctx, &max);
3188 if (err)
3189 return err;
3191 gap = 0;
3192 off = max;
3193 max >>= VHD_SECTOR_SHIFT;
3195 /* data region of segment should begin on page boundary */
3196 if ((max + ctx->bm_secs) % spp) {
3197 gap = (spp - ((max + ctx->bm_secs) % spp));
3198 max += gap;
3201 err = vhd_seek(ctx, off, SEEK_SET);
3202 if (err)
3203 return err;
3205 size = vhd_sectors_to_bytes(ctx->spb + ctx->bm_secs + gap);
3206 buf = mmap(0, size, PROT_READ, MAP_SHARED | MAP_ANON, -1, 0);
3207 if (buf == MAP_FAILED)
3208 return -errno;
3210 err = vhd_write(ctx, buf, size);
3211 if (err)
3212 goto out;
3214 ctx->bat.bat[block] = max;
3215 err = vhd_write_bat(ctx, &ctx->bat);
3216 if (err)
3217 goto out;
3219 err = 0;
3221 out:
3222 munmap(buf, size);
3223 return err;
3226 static int
3227 __vhd_io_dynamic_write(vhd_context_t *ctx,
3228 char *buf, uint64_t sector, uint32_t secs)
3230 char *map;
3231 off_t off;
3232 uint32_t blk, sec;
3233 int i, err, cnt, ret;
3235 if (vhd_sectors_to_bytes(sector + secs) > ctx->footer.curr_size)
3236 return -ERANGE;
3238 err = vhd_get_bat(ctx);
3239 if (err)
3240 return err;
3242 if (vhd_has_batmap(ctx)) {
3243 err = vhd_get_batmap(ctx);
3244 if (err)
3245 return err;
3248 do {
3249 blk = sector / ctx->spb;
3250 sec = sector % ctx->spb;
3252 off = ctx->bat.bat[blk];
3253 if (off == DD_BLK_UNUSED) {
3254 err = __vhd_io_allocate_block(ctx, blk);
3255 if (err)
3256 return err;
3258 off = ctx->bat.bat[blk];
3261 off += ctx->bm_secs + sec;
3262 err = vhd_seek(ctx, vhd_sectors_to_bytes(off), SEEK_SET);
3263 if (err)
3264 return err;
3266 cnt = MIN(secs, ctx->spb - sec);
3267 err = vhd_write(ctx, buf, vhd_sectors_to_bytes(cnt));
3268 if (err)
3269 return err;
3271 if (vhd_has_batmap(ctx) &&
3272 vhd_batmap_test(ctx, &ctx->batmap, blk))
3273 goto next;
3275 err = vhd_read_bitmap(ctx, blk, &map);
3276 if (err)
3277 return err;
3279 for (i = 0; i < cnt; i++)
3280 vhd_bitmap_set(ctx, map, sec + i);
3282 err = vhd_write_bitmap(ctx, blk, map);
3283 if (err)
3284 goto fail;
3286 if (vhd_has_batmap(ctx)) {
3287 for (i = 0; i < ctx->spb; i++)
3288 if (!vhd_bitmap_test(ctx, map, i)) {
3289 free(map);
3290 goto next;
3293 vhd_batmap_set(ctx, &ctx->batmap, blk);
3294 err = vhd_write_batmap(ctx, &ctx->batmap);
3295 if (err)
3296 goto fail;
3299 free(map);
3300 map = NULL;
3302 next:
3303 secs -= cnt;
3304 sector += cnt;
3305 buf += vhd_sectors_to_bytes(cnt);
3306 } while (secs);
3308 err = 0;
3310 out:
3311 ret = vhd_write_footer(ctx, &ctx->footer);
3312 return (err ? err : ret);
3314 fail:
3315 free(map);
3316 goto out;
3319 int
3320 vhd_io_write(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
3322 if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
3323 return -ERANGE;
3325 if (!vhd_type_dynamic(ctx))
3326 return __vhd_io_fixed_write(ctx, buf, sec, secs);
3328 return __vhd_io_dynamic_write(ctx, buf, sec, secs);