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>
39 #include <sys/stat.h>
40 #include <langinfo.h>
41
42 #include "libvhd.h"
43 #include "relative-path.h"
44
45 /* VHD uses an epoch of 12:00AM, Jan 1, 2000. This is the Unix timestamp for
46 * the start of the VHD epoch. */
47 #define VHD_EPOCH_START 946684800
48
49 static int libvhd_dbg = 0;
50
51 void
libvhd_set_log_level(int level)52 libvhd_set_log_level(int level)
53 {
54 if (level)
55 libvhd_dbg = 1;
56 }
57
58 #define VHDLOG(_f, _a...) \
59 do { \
60 if (libvhd_dbg) \
61 syslog(LOG_INFO, "libvhd::%s: "_f, \
62 __func__, ##_a); \
63 } while (0)
64
65 #define BIT_MASK 0x80
66
67 #ifdef ENABLE_FAILURE_TESTING
68 const char* ENV_VAR_FAIL[NUM_FAIL_TESTS] = {
69 "VHD_UTIL_TEST_FAIL_REPARENT_BEGIN",
70 "VHD_UTIL_TEST_FAIL_REPARENT_LOCATOR",
71 "VHD_UTIL_TEST_FAIL_REPARENT_END",
72 "VHD_UTIL_TEST_FAIL_RESIZE_BEGIN",
73 "VHD_UTIL_TEST_FAIL_RESIZE_DATA_MOVED",
74 "VHD_UTIL_TEST_FAIL_RESIZE_METADATA_MOVED",
75 "VHD_UTIL_TEST_FAIL_RESIZE_END"
76 };
77 int TEST_FAIL[NUM_FAIL_TESTS];
78 #endif // ENABLE_FAILURE_TESTING
79
80 static inline int
test_bit(volatile char * addr,int nr)81 test_bit (volatile char *addr, int nr)
82 {
83 return ((addr[nr >> 3] << (nr & 7)) & BIT_MASK) != 0;
84 }
85
86 static inline void
set_bit(volatile char * addr,int nr)87 set_bit (volatile char *addr, int nr)
88 {
89 addr[nr >> 3] |= (BIT_MASK >> (nr & 7));
90 }
91
92 static inline void
clear_bit(volatile char * addr,int nr)93 clear_bit (volatile char *addr, int nr)
94 {
95 addr[nr >> 3] &= ~(BIT_MASK >> (nr & 7));
96 }
97
98 static inline int
old_test_bit(volatile char * addr,int nr)99 old_test_bit(volatile char *addr, int nr)
100 {
101 return (((uint32_t *)addr)[nr >> 5] >> (nr & 31)) & 1;
102 }
103
104 static inline void
old_set_bit(volatile char * addr,int nr)105 old_set_bit(volatile char *addr, int nr)
106 {
107 ((uint32_t *)addr)[nr >> 5] |= (1 << (nr & 31));
108 }
109
110 static inline void
old_clear_bit(volatile char * addr,int nr)111 old_clear_bit(volatile char *addr, int nr)
112 {
113 ((uint32_t *)addr)[nr >> 5] &= ~(1 << (nr & 31));
114 }
115
116 void
vhd_footer_in(vhd_footer_t * footer)117 vhd_footer_in(vhd_footer_t *footer)
118 {
119 BE32_IN(&footer->features);
120 BE32_IN(&footer->ff_version);
121 BE64_IN(&footer->data_offset);
122 BE32_IN(&footer->timestamp);
123 BE32_IN(&footer->crtr_ver);
124 BE32_IN(&footer->crtr_os);
125 BE64_IN(&footer->orig_size);
126 BE64_IN(&footer->curr_size);
127 BE32_IN(&footer->geometry);
128 BE32_IN(&footer->type);
129 BE32_IN(&footer->checksum);
130 }
131
132 void
vhd_footer_out(vhd_footer_t * footer)133 vhd_footer_out(vhd_footer_t *footer)
134 {
135 BE32_OUT(&footer->features);
136 BE32_OUT(&footer->ff_version);
137 BE64_OUT(&footer->data_offset);
138 BE32_OUT(&footer->timestamp);
139 BE32_OUT(&footer->crtr_ver);
140 BE32_OUT(&footer->crtr_os);
141 BE64_OUT(&footer->orig_size);
142 BE64_OUT(&footer->curr_size);
143 BE32_OUT(&footer->geometry);
144 BE32_OUT(&footer->type);
145 BE32_OUT(&footer->checksum);
146 }
147
148 void
vhd_header_in(vhd_header_t * header)149 vhd_header_in(vhd_header_t *header)
150 {
151 int i, n;
152
153 BE64_IN(&header->data_offset);
154 BE64_IN(&header->table_offset);
155 BE32_IN(&header->hdr_ver);
156 BE32_IN(&header->max_bat_size);
157 BE32_IN(&header->block_size);
158 BE32_IN(&header->checksum);
159 BE32_IN(&header->prt_ts);
160
161 n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
162
163 for (i = 0; i < n; i++) {
164 BE32_IN(&header->loc[i].code);
165 BE32_IN(&header->loc[i].data_space);
166 BE32_IN(&header->loc[i].data_len);
167 BE64_IN(&header->loc[i].data_offset);
168 }
169 }
170
171 void
vhd_header_out(vhd_header_t * header)172 vhd_header_out(vhd_header_t *header)
173 {
174 int i, n;
175
176 BE64_OUT(&header->data_offset);
177 BE64_OUT(&header->table_offset);
178 BE32_OUT(&header->hdr_ver);
179 BE32_OUT(&header->max_bat_size);
180 BE32_OUT(&header->block_size);
181 BE32_OUT(&header->checksum);
182 BE32_OUT(&header->prt_ts);
183
184 n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
185
186 for (i = 0; i < n; i++) {
187 BE32_OUT(&header->loc[i].code);
188 BE32_OUT(&header->loc[i].data_space);
189 BE32_OUT(&header->loc[i].data_len);
190 BE64_OUT(&header->loc[i].data_offset);
191 }
192 }
193
194 void
vhd_batmap_header_in(vhd_batmap_t * batmap)195 vhd_batmap_header_in(vhd_batmap_t *batmap)
196 {
197 BE64_IN(&batmap->header.batmap_offset);
198 BE32_IN(&batmap->header.batmap_size);
199 BE32_IN(&batmap->header.batmap_version);
200 BE32_IN(&batmap->header.checksum);
201 }
202
203 void
vhd_batmap_header_out(vhd_batmap_t * batmap)204 vhd_batmap_header_out(vhd_batmap_t *batmap)
205 {
206 BE64_OUT(&batmap->header.batmap_offset);
207 BE32_OUT(&batmap->header.batmap_size);
208 BE32_OUT(&batmap->header.batmap_version);
209 BE32_OUT(&batmap->header.checksum);
210 }
211
212 void
vhd_bat_in(vhd_bat_t * bat)213 vhd_bat_in(vhd_bat_t *bat)
214 {
215 int i;
216
217 for (i = 0; i < bat->entries; i++)
218 BE32_IN(&bat->bat[i]);
219 }
220
221 void
vhd_bat_out(vhd_bat_t * bat)222 vhd_bat_out(vhd_bat_t *bat)
223 {
224 int i;
225
226 for (i = 0; i < bat->entries; i++)
227 BE32_OUT(&bat->bat[i]);
228 }
229
230 uint32_t
vhd_checksum_footer(vhd_footer_t * footer)231 vhd_checksum_footer(vhd_footer_t *footer)
232 {
233 int i;
234 unsigned char *blob;
235 uint32_t checksum, tmp;
236
237 checksum = 0;
238 tmp = footer->checksum;
239 footer->checksum = 0;
240
241 blob = (unsigned char *)footer;
242 for (i = 0; i < sizeof(vhd_footer_t); i++)
243 checksum += (uint32_t)blob[i];
244
245 footer->checksum = tmp;
246 return ~checksum;
247 }
248
249 int
vhd_validate_footer(vhd_footer_t * footer)250 vhd_validate_footer(vhd_footer_t *footer)
251 {
252 int csize;
253 uint32_t checksum;
254
255 csize = sizeof(footer->cookie);
256 if (memcmp(footer->cookie, HD_COOKIE, csize) != 0 &&
257 memcmp(footer->cookie, VHD_POISON_COOKIE, csize) != 0) {
258 char buf[9];
259 strncpy(buf, footer->cookie, sizeof(buf));
260 buf[sizeof(buf)-1]= '\0';
261 VHDLOG("invalid footer cookie: %s\n", buf);
262 return -EINVAL;
263 }
264
265 checksum = vhd_checksum_footer(footer);
266 if (checksum != footer->checksum) {
267 /*
268 * early td-util did not re-calculate
269 * checksum when marking vhds 'hidden'
270 */
271 if (footer->hidden &&
272 !strncmp(footer->crtr_app, "tap", 3) &&
273 (footer->crtr_ver == VHD_VERSION(0, 1) ||
274 footer->crtr_ver == VHD_VERSION(1, 1))) {
275 char tmp = footer->hidden;
276 footer->hidden = 0;
277 checksum = vhd_checksum_footer(footer);
278 footer->hidden = tmp;
279
280 if (checksum == footer->checksum)
281 return 0;
282 }
283
284 VHDLOG("invalid footer checksum: "
285 "footer = 0x%08x, calculated = 0x%08x\n",
286 footer->checksum, checksum);
287 return -EINVAL;
288 }
289
290 return 0;
291 }
292
293 uint32_t
vhd_checksum_header(vhd_header_t * header)294 vhd_checksum_header(vhd_header_t *header)
295 {
296 int i;
297 unsigned char *blob;
298 uint32_t checksum, tmp;
299
300 checksum = 0;
301 tmp = header->checksum;
302 header->checksum = 0;
303
304 blob = (unsigned char *)header;
305 for (i = 0; i < sizeof(vhd_header_t); i++)
306 checksum += (uint32_t)blob[i];
307
308 header->checksum = tmp;
309 return ~checksum;
310 }
311
312 int
vhd_validate_header(vhd_header_t * header)313 vhd_validate_header(vhd_header_t *header)
314 {
315 int i, n;
316 uint32_t checksum;
317
318 if (memcmp(header->cookie, DD_COOKIE, 8) != 0) {
319 char buf[9];
320 strncpy(buf, header->cookie, sizeof(buf));
321 buf[sizeof(buf)-1]= '\0';
322 VHDLOG("invalid header cookie: %s\n", buf);
323 return -EINVAL;
324 }
325
326 if (header->hdr_ver != 0x00010000) {
327 VHDLOG("invalid header version 0x%08x\n", header->hdr_ver);
328 return -EINVAL;
329 }
330
331 if (header->data_offset != 0xFFFFFFFFFFFFFFFF) {
332 VHDLOG("invalid header data_offset 0x%016"PRIx64"\n",
333 header->data_offset);
334 return -EINVAL;
335 }
336
337 n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
338 for (i = 0; i < n; i++)
339 if (vhd_validate_platform_code(header->loc[i].code))
340 return -EINVAL;
341
342 checksum = vhd_checksum_header(header);
343 if (checksum != header->checksum) {
344 VHDLOG("invalid header checksum: "
345 "header = 0x%08x, calculated = 0x%08x\n",
346 header->checksum, checksum);
347 return -EINVAL;
348 }
349
350 return 0;
351 }
352
353 static inline int
vhd_validate_bat(vhd_bat_t * bat)354 vhd_validate_bat(vhd_bat_t *bat)
355 {
356 if (!bat->bat)
357 return -EINVAL;
358
359 return 0;
360 }
361
362 uint32_t
vhd_checksum_batmap(vhd_batmap_t * batmap)363 vhd_checksum_batmap(vhd_batmap_t *batmap)
364 {
365 int i, n;
366 char *blob;
367 uint32_t checksum;
368
369 blob = batmap->map;
370 checksum = 0;
371
372 n = vhd_sectors_to_bytes(batmap->header.batmap_size);
373
374 for (i = 0; i < n; i++) {
375 if (batmap->header.batmap_version == VHD_BATMAP_VERSION(1, 1))
376 checksum += (uint32_t)blob[i];
377 else
378 checksum += (uint32_t)(unsigned char)blob[i];
379 }
380
381 return ~checksum;
382 }
383
384 int
vhd_validate_batmap_header(vhd_batmap_t * batmap)385 vhd_validate_batmap_header(vhd_batmap_t *batmap)
386 {
387 if (memcmp(batmap->header.cookie, VHD_BATMAP_COOKIE, 8))
388 return -EINVAL;
389
390 if (batmap->header.batmap_version > VHD_BATMAP_CURRENT_VERSION)
391 return -EINVAL;
392
393 return 0;
394 }
395
396 int
vhd_validate_batmap(vhd_batmap_t * batmap)397 vhd_validate_batmap(vhd_batmap_t *batmap)
398 {
399 uint32_t checksum;
400
401 if (!batmap->map)
402 return -EINVAL;
403
404 checksum = vhd_checksum_batmap(batmap);
405 if (checksum != batmap->header.checksum)
406 return -EINVAL;
407
408 return 0;
409 }
410
411 int
vhd_batmap_header_offset(vhd_context_t * ctx,off_t * _off)412 vhd_batmap_header_offset(vhd_context_t *ctx, off_t *_off)
413 {
414 off_t off;
415 size_t bat;
416
417 *_off = 0;
418
419 off = ctx->header.table_offset;
420 bat = ctx->header.max_bat_size * sizeof(uint32_t);
421 off += vhd_bytes_padded(bat);
422
423 *_off = off;
424 return 0;
425 }
426
427 int
vhd_validate_platform_code(uint32_t code)428 vhd_validate_platform_code(uint32_t code)
429 {
430 switch (code) {
431 case PLAT_CODE_NONE:
432 case PLAT_CODE_WI2R:
433 case PLAT_CODE_WI2K:
434 case PLAT_CODE_W2RU:
435 case PLAT_CODE_W2KU:
436 case PLAT_CODE_MAC:
437 case PLAT_CODE_MACX:
438 return 0;
439 default:
440 VHDLOG("invalid parent locator code %u\n", code);
441 return -EINVAL;
442 }
443 }
444
445 int
vhd_parent_locator_count(vhd_context_t * ctx)446 vhd_parent_locator_count(vhd_context_t *ctx)
447 {
448 return (sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t));
449 }
450
451 int
vhd_hidden(vhd_context_t * ctx,int * hidden)452 vhd_hidden(vhd_context_t *ctx, int *hidden)
453 {
454 int err;
455
456 *hidden = 0;
457
458 if (vhd_type_dynamic(ctx) && vhd_creator_tapdisk(ctx) &&
459 (ctx->footer.crtr_ver == VHD_VERSION(0, 1) ||
460 ctx->footer.crtr_ver == VHD_VERSION(1, 1))) {
461 vhd_footer_t copy;
462
463 err = vhd_read_footer_at(ctx, ©, 0);
464 if (err) {
465 VHDLOG("error reading backup footer of %s: %d\n",
466 ctx->file, err);
467 return err;
468 }
469 *hidden = copy.hidden;
470 } else
471 *hidden = ctx->footer.hidden;
472
473 return 0;
474 }
475
476 int
vhd_chain_depth(vhd_context_t * ctx,int * depth)477 vhd_chain_depth(vhd_context_t *ctx, int *depth)
478 {
479 char *file;
480 int err, cnt;
481 vhd_context_t vhd, *cur;
482
483 err = 0;
484 cnt = 0;
485 *depth = 0;
486 file = NULL;
487 cur = ctx;
488
489 for (;;) {
490 cnt++;
491
492 if (cur->footer.type != HD_TYPE_DIFF)
493 break;
494
495 if (vhd_parent_raw(cur)) {
496 cnt++;
497 break;
498 }
499
500 err = vhd_parent_locator_get(cur, &file);
501 if (err) {
502 file = NULL;
503 break;
504 }
505
506 if (cur != ctx) {
507 vhd_close(cur);
508 cur = NULL;
509 }
510
511 err = vhd_open(&vhd, file, VHD_OPEN_RDONLY);
512 if (err)
513 break;
514
515 cur = &vhd;
516 free(file);
517 file = NULL;
518 }
519
520 free(file);
521 if (cur && cur != ctx)
522 vhd_close(cur);
523
524 if (!err)
525 *depth = cnt;
526
527 return err;
528 }
529
530 int
vhd_batmap_test(vhd_context_t * ctx,vhd_batmap_t * batmap,uint32_t block)531 vhd_batmap_test(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
532 {
533 if (!vhd_has_batmap(ctx) || !batmap->map)
534 return 0;
535
536 if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
537 return 0;
538
539 return test_bit(batmap->map, block);
540 }
541
542 void
vhd_batmap_set(vhd_context_t * ctx,vhd_batmap_t * batmap,uint32_t block)543 vhd_batmap_set(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
544 {
545 if (!vhd_has_batmap(ctx) || !batmap->map)
546 return;
547
548 if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
549 return;
550
551 set_bit(batmap->map, block);
552 }
553
554 void
vhd_batmap_clear(vhd_context_t * ctx,vhd_batmap_t * batmap,uint32_t block)555 vhd_batmap_clear(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
556 {
557 if (!vhd_has_batmap(ctx) || !batmap->map)
558 return;
559
560 if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
561 return;
562
563 clear_bit(batmap->map, block);
564 }
565
566 int
vhd_bitmap_test(vhd_context_t * ctx,char * map,uint32_t block)567 vhd_bitmap_test(vhd_context_t *ctx, char *map, uint32_t block)
568 {
569 if (vhd_creator_tapdisk(ctx) &&
570 ctx->footer.crtr_ver == 0x00000001)
571 return old_test_bit(map, block);
572
573 return test_bit(map, block);
574 }
575
576 void
vhd_bitmap_set(vhd_context_t * ctx,char * map,uint32_t block)577 vhd_bitmap_set(vhd_context_t *ctx, char *map, uint32_t block)
578 {
579 if (vhd_creator_tapdisk(ctx) &&
580 ctx->footer.crtr_ver == 0x00000001)
581 return old_set_bit(map, block);
582
583 return set_bit(map, block);
584 }
585
586 void
vhd_bitmap_clear(vhd_context_t * ctx,char * map,uint32_t block)587 vhd_bitmap_clear(vhd_context_t *ctx, char *map, uint32_t block)
588 {
589 if (vhd_creator_tapdisk(ctx) &&
590 ctx->footer.crtr_ver == 0x00000001)
591 return old_clear_bit(map, block);
592
593 return clear_bit(map, block);
594 }
595
596 /*
597 * returns absolute offset of the first
598 * byte of the file which is not vhd metadata
599 */
600 int
vhd_end_of_headers(vhd_context_t * ctx,off_t * end)601 vhd_end_of_headers(vhd_context_t *ctx, off_t *end)
602 {
603 int err, i, n;
604 uint32_t bat_bytes;
605 off_t eom, bat_end;
606 vhd_parent_locator_t *loc;
607
608 *end = 0;
609
610 if (!vhd_type_dynamic(ctx))
611 return 0;
612
613 eom = ctx->footer.data_offset + sizeof(vhd_header_t);
614
615 bat_bytes = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
616 bat_end = ctx->header.table_offset + bat_bytes;
617
618 eom = MAX(eom, bat_end);
619
620 if (vhd_has_batmap(ctx)) {
621 off_t hdr_end, hdr_secs, map_end, map_secs;
622
623 err = vhd_get_batmap(ctx);
624 if (err)
625 return err;
626
627 hdr_secs = secs_round_up_no_zero(sizeof(vhd_batmap_header_t));
628 err = vhd_batmap_header_offset(ctx, &hdr_end);
629 if (err)
630 return err;
631
632 hdr_end += vhd_sectors_to_bytes(hdr_secs);
633 eom = MAX(eom, hdr_end);
634
635 map_secs = ctx->batmap.header.batmap_size;
636 map_end = (ctx->batmap.header.batmap_offset +
637 vhd_sectors_to_bytes(map_secs));
638 eom = MAX(eom, map_end);
639 }
640
641 /* parent locators */
642 n = sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t);
643
644 for (i = 0; i < n; i++) {
645 off_t loc_end;
646
647 loc = &ctx->header.loc[i];
648 if (loc->code == PLAT_CODE_NONE)
649 continue;
650
651 loc_end = loc->data_offset + vhd_parent_locator_size(loc);
652 eom = MAX(eom, loc_end);
653 }
654
655 *end = eom;
656 return 0;
657 }
658
659 int
vhd_end_of_data(vhd_context_t * ctx,off_t * end)660 vhd_end_of_data(vhd_context_t *ctx, off_t *end)
661 {
662 int i, err;
663 off_t max;
664 uint64_t blk;
665
666 if (!vhd_type_dynamic(ctx)) {
667 err = vhd_seek(ctx, 0, SEEK_END);
668 if (err)
669 return err;
670
671 max = vhd_position(ctx);
672 if (max == (off_t)-1)
673 return -errno;
674
675 *end = max - sizeof(vhd_footer_t);
676 return 0;
677 }
678
679 err = vhd_end_of_headers(ctx, &max);
680 if (err)
681 return err;
682
683 err = vhd_get_bat(ctx);
684 if (err)
685 return err;
686
687 max >>= VHD_SECTOR_SHIFT;
688
689 for (i = 0; i < ctx->bat.entries; i++) {
690 blk = ctx->bat.bat[i];
691
692 if (blk != DD_BLK_UNUSED) {
693 blk += ctx->spb + ctx->bm_secs;
694 max = MAX(blk, max);
695 }
696 }
697
698 *end = vhd_sectors_to_bytes(max);
699 return 0;
700 }
701
702 uint32_t inline
vhd_time(time_t time)703 vhd_time(time_t time)
704 {
705 return (uint32_t)(time - VHD_EPOCH_START);
706 }
707
708 /*
709 * Stringify the VHD timestamp for printing.
710 * As with ctime_r, target must be >=26 bytes.
711 */
712 size_t
vhd_time_to_string(uint32_t timestamp,char * target)713 vhd_time_to_string(uint32_t timestamp, char *target)
714 {
715 char *cr;
716 time_t unix_timestamp;
717
718 unix_timestamp = (time_t)timestamp + VHD_EPOCH_START;
719 ctime_r(&unix_timestamp, target);
720
721 /* handle mad ctime_r newline appending. */
722 if ((cr = strchr(target, '\n')) != NULL)
723 *cr = '\0';
724
725 return (strlen(target));
726 }
727
728 /*
729 * nabbed from vhd specs.
730 */
731 uint32_t
vhd_chs(uint64_t size)732 vhd_chs(uint64_t size)
733 {
734 uint32_t secs, cylinders, heads, spt, cth;
735
736 secs = secs_round_up_no_zero(size);
737
738 if (secs > 65535 * 16 * 255)
739 secs = 65535 * 16 * 255;
740
741 if (secs >= 65535 * 16 * 63) {
742 spt = 255;
743 cth = secs / spt;
744 heads = 16;
745 } else {
746 spt = 17;
747 cth = secs / spt;
748 heads = (cth + 1023) / 1024;
749
750 if (heads < 4)
751 heads = 4;
752
753 if (cth >= (heads * 1024) || heads > 16) {
754 spt = 31;
755 cth = secs / spt;
756 heads = 16;
757 }
758
759 if (cth >= heads * 1024) {
760 spt = 63;
761 cth = secs / spt;
762 heads = 16;
763 }
764 }
765
766 cylinders = cth / heads;
767
768 return GEOM_ENCODE(cylinders, heads, spt);
769 }
770
771 int
vhd_get_footer(vhd_context_t * ctx)772 vhd_get_footer(vhd_context_t *ctx)
773 {
774 if (!vhd_validate_footer(&ctx->footer))
775 return 0;
776
777 return vhd_read_footer(ctx, &ctx->footer);
778 }
779
780 int
vhd_get_header(vhd_context_t * ctx)781 vhd_get_header(vhd_context_t *ctx)
782 {
783 if (!vhd_type_dynamic(ctx))
784 return -EINVAL;
785
786 if (!vhd_validate_header(&ctx->header))
787 return 0;
788
789 return vhd_read_header(ctx, &ctx->header);
790 }
791
792 int
vhd_get_bat(vhd_context_t * ctx)793 vhd_get_bat(vhd_context_t *ctx)
794 {
795 if (!vhd_type_dynamic(ctx))
796 return -EINVAL;
797
798 if (!vhd_validate_bat(&ctx->bat))
799 return 0;
800
801 vhd_put_bat(ctx);
802 return vhd_read_bat(ctx, &ctx->bat);
803 }
804
805 int
vhd_get_batmap(vhd_context_t * ctx)806 vhd_get_batmap(vhd_context_t *ctx)
807 {
808 if (!vhd_has_batmap(ctx))
809 return -EINVAL;
810
811 if (!vhd_validate_batmap(&ctx->batmap))
812 return 0;
813
814 vhd_put_batmap(ctx);
815 return vhd_read_batmap(ctx, &ctx->batmap);
816 }
817
818 void
vhd_put_footer(vhd_context_t * ctx)819 vhd_put_footer(vhd_context_t *ctx)
820 {
821 memset(&ctx->footer, 0, sizeof(vhd_footer_t));
822 }
823
824 void
vhd_put_header(vhd_context_t * ctx)825 vhd_put_header(vhd_context_t *ctx)
826 {
827 memset(&ctx->header, 0, sizeof(vhd_header_t));
828 }
829
830 void
vhd_put_bat(vhd_context_t * ctx)831 vhd_put_bat(vhd_context_t *ctx)
832 {
833 if (!vhd_type_dynamic(ctx))
834 return;
835
836 free(ctx->bat.bat);
837 memset(&ctx->bat, 0, sizeof(vhd_bat_t));
838 }
839
840 void
vhd_put_batmap(vhd_context_t * ctx)841 vhd_put_batmap(vhd_context_t *ctx)
842 {
843 if (!vhd_type_dynamic(ctx))
844 return;
845
846 if (!vhd_has_batmap(ctx))
847 return;
848
849 free(ctx->batmap.map);
850 memset(&ctx->batmap, 0, sizeof(vhd_batmap_t));
851 }
852
853 /*
854 * look for 511 byte footer at end of file
855 */
856 int
vhd_read_short_footer(vhd_context_t * ctx,vhd_footer_t * footer)857 vhd_read_short_footer(vhd_context_t *ctx, vhd_footer_t *footer)
858 {
859 int err;
860 char *buf;
861 off_t eof;
862
863 buf = NULL;
864
865 err = vhd_seek(ctx, 0, SEEK_END);
866 if (err)
867 goto out;
868
869 eof = vhd_position(ctx);
870 if (eof == (off_t)-1) {
871 err = -errno;
872 goto out;
873 }
874
875 err = vhd_seek(ctx, eof - 511, SEEK_SET);
876 if (err)
877 goto out;
878
879 err = posix_memalign((void **)&buf,
880 VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
881 if (err) {
882 buf = NULL;
883 err = -err;
884 goto out;
885 }
886
887 memset(buf, 0, sizeof(vhd_footer_t));
888
889 /*
890 * expecting short read here
891 */
892 vhd_read(ctx, buf, sizeof(vhd_footer_t));
893
894 memcpy(footer, buf, sizeof(vhd_footer_t));
895
896 vhd_footer_in(footer);
897 err = vhd_validate_footer(footer);
898
899 out:
900 if (err)
901 VHDLOG("%s: failed reading short footer: %d\n",
902 ctx->file, err);
903 free(buf);
904 return err;
905 }
906
907 int
vhd_read_footer_at(vhd_context_t * ctx,vhd_footer_t * footer,off_t off)908 vhd_read_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off_t off)
909 {
910 int err;
911 char *buf;
912
913 buf = NULL;
914
915 err = vhd_seek(ctx, off, SEEK_SET);
916 if (err)
917 goto out;
918
919 err = posix_memalign((void **)&buf,
920 VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
921 if (err) {
922 buf = NULL;
923 err = -err;
924 goto out;
925 }
926
927 err = vhd_read(ctx, buf, sizeof(vhd_footer_t));
928 if (err)
929 goto out;
930
931 memcpy(footer, buf, sizeof(vhd_footer_t));
932
933 vhd_footer_in(footer);
934 err = vhd_validate_footer(footer);
935
936 out:
937 if (err)
938 VHDLOG("%s: reading footer at 0x%08"PRIx64" failed: %d\n",
939 ctx->file, off, err);
940 free(buf);
941 return err;
942 }
943
944 int
vhd_read_footer(vhd_context_t * ctx,vhd_footer_t * footer)945 vhd_read_footer(vhd_context_t *ctx, vhd_footer_t *footer)
946 {
947 int err;
948 off_t off;
949
950 err = vhd_seek(ctx, 0, SEEK_END);
951 if (err)
952 return err;
953
954 off = vhd_position(ctx);
955 if (off == (off_t)-1)
956 return -errno;
957
958 err = vhd_read_footer_at(ctx, footer, off - 512);
959 if (err != -EINVAL)
960 return err;
961
962 err = vhd_read_short_footer(ctx, footer);
963 if (err != -EINVAL)
964 return err;
965
966 if (ctx->oflags & VHD_OPEN_STRICT)
967 return -EINVAL;
968
969 return vhd_read_footer_at(ctx, footer, 0);
970 }
971
972 int
vhd_read_header_at(vhd_context_t * ctx,vhd_header_t * header,off_t off)973 vhd_read_header_at(vhd_context_t *ctx, vhd_header_t *header, off_t off)
974 {
975 int err;
976 char *buf;
977
978 buf = NULL;
979
980 if (!vhd_type_dynamic(ctx)) {
981 err = -EINVAL;
982 goto out;
983 }
984
985 err = vhd_seek(ctx, off, SEEK_SET);
986 if (err)
987 goto out;
988
989 err = posix_memalign((void **)&buf,
990 VHD_SECTOR_SIZE, sizeof(vhd_header_t));
991 if (err) {
992 buf = NULL;
993 err = -err;
994 goto out;
995 }
996
997 err = vhd_read(ctx, buf, sizeof(vhd_header_t));
998 if (err)
999 goto out;
1000
1001 memcpy(header, buf, sizeof(vhd_header_t));
1002
1003 vhd_header_in(header);
1004 err = vhd_validate_header(header);
1005
1006 out:
1007 if (err)
1008 VHDLOG("%s: reading header at 0x%08"PRIx64" failed: %d\n",
1009 ctx->file, off, err);
1010 free(buf);
1011 return err;
1012 }
1013
1014 int
vhd_read_header(vhd_context_t * ctx,vhd_header_t * header)1015 vhd_read_header(vhd_context_t *ctx, vhd_header_t *header)
1016 {
1017 int err;
1018 off_t off;
1019
1020 if (!vhd_type_dynamic(ctx)) {
1021 VHDLOG("%s is not dynamic!\n", ctx->file);
1022 return -EINVAL;
1023 }
1024
1025 off = ctx->footer.data_offset;
1026 return vhd_read_header_at(ctx, header, off);
1027 }
1028
1029 int
vhd_read_bat(vhd_context_t * ctx,vhd_bat_t * bat)1030 vhd_read_bat(vhd_context_t *ctx, vhd_bat_t *bat)
1031 {
1032 int err;
1033 char *buf;
1034 off_t off;
1035 size_t size;
1036
1037 buf = NULL;
1038
1039 if (!vhd_type_dynamic(ctx)) {
1040 err = -EINVAL;
1041 goto fail;
1042 }
1043
1044 off = ctx->header.table_offset;
1045 size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
1046
1047 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
1048 if (err) {
1049 buf = NULL;
1050 err = -err;
1051 goto fail;
1052 }
1053
1054 err = vhd_seek(ctx, off, SEEK_SET);
1055 if (err)
1056 goto fail;
1057
1058 err = vhd_read(ctx, buf, size);
1059 if (err)
1060 goto fail;
1061
1062 bat->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
1063 bat->entries = ctx->header.max_bat_size;
1064 bat->bat = (uint32_t *)buf;
1065
1066 vhd_bat_in(bat);
1067
1068 return 0;
1069
1070 fail:
1071 free(buf);
1072 memset(bat, 0, sizeof(vhd_bat_t));
1073 VHDLOG("%s: failed to read bat: %d\n", ctx->file, err);
1074 return err;
1075 }
1076
1077 static int
vhd_read_batmap_header(vhd_context_t * ctx,vhd_batmap_t * batmap)1078 vhd_read_batmap_header(vhd_context_t *ctx, vhd_batmap_t *batmap)
1079 {
1080 int err;
1081 char *buf;
1082 off_t off;
1083 size_t size;
1084
1085 buf = NULL;
1086
1087 err = vhd_batmap_header_offset(ctx, &off);
1088 if (err)
1089 goto fail;
1090
1091 err = vhd_seek(ctx, off, SEEK_SET);
1092 if (err)
1093 goto fail;
1094
1095 size = vhd_bytes_padded(sizeof(vhd_batmap_header_t));
1096 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
1097 if (err) {
1098 buf = NULL;
1099 err = -err;
1100 goto fail;
1101 }
1102
1103 err = vhd_read(ctx, buf, size);
1104 if (err)
1105 goto fail;
1106
1107 memcpy(&batmap->header, buf, sizeof(vhd_batmap_header_t));
1108 free(buf);
1109 buf = NULL;
1110
1111 vhd_batmap_header_in(batmap);
1112
1113 return 0;
1114
1115 fail:
1116 free(buf);
1117 memset(&batmap->header, 0, sizeof(vhd_batmap_header_t));
1118 VHDLOG("%s: failed to read batmap header: %d\n", ctx->file, err);
1119 return err;
1120 }
1121
1122 static int
vhd_read_batmap_map(vhd_context_t * ctx,vhd_batmap_t * batmap)1123 vhd_read_batmap_map(vhd_context_t *ctx, vhd_batmap_t *batmap)
1124 {
1125 int err;
1126 char *buf;
1127 off_t off;
1128 size_t map_size;
1129
1130 map_size = vhd_sectors_to_bytes(batmap->header.batmap_size);
1131
1132 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, map_size);
1133 if (err) {
1134 buf = NULL;
1135 err = -err;
1136 goto fail;
1137 }
1138
1139 off = batmap->header.batmap_offset;
1140 err = vhd_seek(ctx, off, SEEK_SET);
1141 if (err)
1142 goto fail;
1143
1144 err = vhd_read(ctx, buf, map_size);
1145 if (err)
1146 goto fail;
1147
1148 batmap->map = buf;
1149 return 0;
1150
1151 fail:
1152 free(buf);
1153 batmap->map = NULL;
1154 VHDLOG("%s: failed to read batmap: %d\n", ctx->file, err);
1155 return err;
1156 }
1157
1158 int
vhd_read_batmap(vhd_context_t * ctx,vhd_batmap_t * batmap)1159 vhd_read_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap)
1160 {
1161 int err;
1162
1163 if (!vhd_has_batmap(ctx))
1164 return -EINVAL;
1165
1166 memset(batmap, 0, sizeof(vhd_batmap_t));
1167
1168 err = vhd_read_batmap_header(ctx, batmap);
1169 if (err)
1170 return err;
1171
1172 err = vhd_validate_batmap_header(batmap);
1173 if (err)
1174 return err;
1175
1176 err = vhd_read_batmap_map(ctx, batmap);
1177 if (err)
1178 return err;
1179
1180 err = vhd_validate_batmap(batmap);
1181 if (err)
1182 goto fail;
1183
1184 return 0;
1185
1186 fail:
1187 free(batmap->map);
1188 memset(batmap, 0, sizeof(vhd_batmap_t));
1189 return err;
1190 }
1191
1192 int
vhd_has_batmap(vhd_context_t * ctx)1193 vhd_has_batmap(vhd_context_t *ctx)
1194 {
1195 if (!vhd_type_dynamic(ctx))
1196 return 0;
1197
1198 if (!vhd_creator_tapdisk(ctx))
1199 return 0;
1200
1201 if (ctx->footer.crtr_ver <= VHD_VERSION(0, 1))
1202 return 0;
1203
1204 if (ctx->footer.crtr_ver >= VHD_VERSION(1, 2))
1205 return 1;
1206
1207 /*
1208 * VHDs of version 1.1 probably have a batmap, but may not
1209 * if they were updated from version 0.1 via vhd-update.
1210 */
1211 if (!vhd_validate_batmap_header(&ctx->batmap))
1212 return 1;
1213
1214 if (vhd_read_batmap_header(ctx, &ctx->batmap))
1215 return 0;
1216
1217 return (!vhd_validate_batmap_header(&ctx->batmap));
1218 }
1219
1220 /*
1221 * Is this a block device (with a fixed size)? This affects whether the file
1222 * can be truncated and where the footer is written for VHDs.
1223 */
1224 int
vhd_test_file_fixed(const char * file,int * is_block)1225 vhd_test_file_fixed(const char *file, int *is_block)
1226 {
1227 int err;
1228 struct stat stats;
1229
1230 err = stat(file, &stats);
1231 if (err == -1)
1232 return -errno;
1233
1234 *is_block = !!(S_ISBLK(stats.st_mode));
1235 return err;
1236 }
1237
1238 int
vhd_find_parent(vhd_context_t * ctx,const char * parent,char ** _location)1239 vhd_find_parent(vhd_context_t *ctx, const char *parent, char **_location)
1240 {
1241 int err;
1242 char *location, *cpath, *cdir, *path;
1243
1244 err = 0;
1245 path = NULL;
1246 cpath = NULL;
1247 location = NULL;
1248 *_location = NULL;
1249
1250 if (!parent)
1251 return -EINVAL;
1252
1253 if (parent[0] == '/') {
1254 if (!access(parent, R_OK)) {
1255 path = strdup(parent);
1256 if (!path)
1257 return -ENOMEM;
1258 *_location = path;
1259 return 0;
1260 }
1261 }
1262
1263 /* check parent path relative to child's directory */
1264 cpath = realpath(ctx->file, NULL);
1265 if (!cpath) {
1266 err = -errno;
1267 goto out;
1268 }
1269
1270 cdir = dirname(cpath);
1271 if (asprintf(&location, "%s/%s", cdir, parent) == -1) {
1272 err = -errno;
1273 location = NULL;
1274 goto out;
1275 }
1276
1277 if (!access(location, R_OK)) {
1278 path = realpath(location, NULL);
1279 if (path) {
1280 *_location = path;
1281 return 0;
1282 }
1283 }
1284 err = -errno;
1285
1286 out:
1287 free(location);
1288 free(cpath);
1289 return err;
1290 }
1291
1292 static int
vhd_macx_encode_location(char * name,char ** out,int * outlen)1293 vhd_macx_encode_location(char *name, char **out, int *outlen)
1294 {
1295 iconv_t cd;
1296 int len, err;
1297 size_t ibl, obl;
1298 char *uri, *uri_utf8, *uri_utf8p, *ret;
1299 const char *urip;
1300 char *codeset;
1301
1302 err = 0;
1303 ret = NULL;
1304 *out = NULL;
1305 *outlen = 0;
1306 len = strlen(name) + strlen("file://");
1307
1308 ibl = len;
1309 obl = len * 2;
1310
1311 urip = uri = malloc(ibl + 1);
1312 uri_utf8 = uri_utf8p = malloc(obl);
1313
1314 if (!uri || !uri_utf8)
1315 return -ENOMEM;
1316
1317 codeset = nl_langinfo(CODESET);
1318 cd = iconv_open("UTF-8", codeset);
1319 if (cd == (iconv_t)-1) {
1320 err = -errno;
1321 goto out;
1322 }
1323
1324 snprintf(uri, ibl+1, "file://%s", name);
1325
1326 if (iconv(cd,
1327 #ifdef __linux__
1328 (char **)
1329 #endif
1330 &urip, &ibl, &uri_utf8p, &obl) == (size_t)-1 ||
1331 ibl) {
1332 err = (errno ? -errno : -EIO);
1333 goto out;
1334 }
1335
1336 ret = malloc(len);
1337 if (!ret) {
1338 err = -ENOMEM;
1339 goto out;
1340 }
1341
1342 memcpy(ret, uri_utf8, len);
1343 *outlen = len;
1344 *out = ret;
1345
1346 out:
1347 free(uri);
1348 free(uri_utf8);
1349 if (cd != (iconv_t)-1)
1350 iconv_close(cd);
1351
1352 return err;
1353 }
1354
1355 static int
vhd_w2u_encode_location(char * name,char ** out,int * outlen)1356 vhd_w2u_encode_location(char *name, char **out, int *outlen)
1357 {
1358 iconv_t cd;
1359 int len, err;
1360 size_t ibl, obl;
1361 char *uri, *uri_utf16, *uri_utf16p, *tmp, *ret;
1362 const char *urip;
1363 char *codeset;
1364
1365 err = 0;
1366 ret = NULL;
1367 *out = NULL;
1368 *outlen = 0;
1369 cd = (iconv_t) -1;
1370
1371 /*
1372 * MICROSOFT_COMPAT
1373 * relative paths must start with ".\"
1374 */
1375 if (name[0] != '/') {
1376 tmp = strstr(name, "./");
1377 if (tmp == name)
1378 tmp += strlen("./");
1379 else
1380 tmp = name;
1381
1382 err = asprintf(&uri, ".\\%s", tmp);
1383 } else
1384 err = asprintf(&uri, "%s", name);
1385
1386 if (err == -1)
1387 return -ENOMEM;
1388
1389 tmp = uri;
1390 while (*tmp != '\0') {
1391 if (*tmp == '/')
1392 *tmp = '\\';
1393 tmp++;
1394 }
1395
1396 len = strlen(uri);
1397 ibl = len;
1398 obl = len * 2;
1399 urip = uri;
1400
1401 uri_utf16 = uri_utf16p = malloc(obl);
1402 if (!uri_utf16) {
1403 err = -ENOMEM;
1404 goto out;
1405 }
1406
1407 /*
1408 * MICROSOFT_COMPAT
1409 * little endian unicode here
1410 */
1411 codeset = nl_langinfo(CODESET);
1412 cd = iconv_open("UTF-16LE", codeset);
1413 if (cd == (iconv_t)-1) {
1414 err = -errno;
1415 goto out;
1416 }
1417
1418 if (iconv(cd,
1419 #ifdef __linux__
1420 (char **)
1421 #endif
1422 &urip, &ibl, &uri_utf16p, &obl) == (size_t)-1 ||
1423 ibl) {
1424 err = (errno ? -errno : -EIO);
1425 goto out;
1426 }
1427
1428 len = len * 2;
1429 ret = malloc(len);
1430 if (!ret) {
1431 err = -ENOMEM;
1432 goto out;
1433 }
1434
1435 memcpy(ret, uri_utf16, len);
1436 *outlen = len;
1437 *out = ret;
1438 err = 0;
1439
1440 out:
1441 free(uri);
1442 free(uri_utf16);
1443 if (cd != (iconv_t)-1)
1444 iconv_close(cd);
1445
1446 return err;
1447 }
1448
1449 static char *
vhd_macx_decode_location(const char * in,char * out,int len)1450 vhd_macx_decode_location(const char *in, char *out, int len)
1451 {
1452 iconv_t cd;
1453 char *name;
1454 size_t ibl, obl;
1455 char *codeset;
1456
1457 name = out;
1458 ibl = obl = len;
1459
1460 codeset = nl_langinfo(CODESET);
1461 cd = iconv_open(codeset, "UTF-8");
1462 if (cd == (iconv_t)-1)
1463 return NULL;
1464
1465 if (iconv(cd,
1466 #ifdef __linux__
1467 (char **)
1468 #endif
1469 &in, &ibl, &out, &obl) == (size_t)-1 || ibl)
1470 return NULL;
1471
1472 iconv_close(cd);
1473 *out = '\0';
1474
1475 if (strstr(name, "file://") != name)
1476 return NULL;
1477
1478 name += strlen("file://");
1479
1480 return strdup(name);
1481 }
1482
1483 static char *
vhd_w2u_decode_location(const char * in,char * out,int len,char * utf_type)1484 vhd_w2u_decode_location(const char *in, char *out, int len, char *utf_type)
1485 {
1486 iconv_t cd;
1487 char *name, *tmp;
1488 size_t ibl, obl;
1489 char *codeset;
1490
1491 tmp = name = out;
1492 ibl = obl = len;
1493
1494 codeset = nl_langinfo(CODESET);
1495 cd = iconv_open(codeset, utf_type);
1496 if (cd == (iconv_t)-1)
1497 return NULL;
1498
1499 if (iconv(cd,
1500 #ifdef __linux__
1501 (char **)
1502 #endif
1503 &in, &ibl, &out, &obl) == (size_t)-1 || ibl)
1504 return NULL;
1505
1506 iconv_close(cd);
1507 *out = '\0';
1508
1509 /* TODO: spaces */
1510 while (tmp != out) {
1511 if (*tmp == '\\')
1512 *tmp = '/';
1513 tmp++;
1514 }
1515
1516 if (strstr(name, "C:") == name || strstr(name, "c:") == name)
1517 name += strlen("c:");
1518
1519 return strdup(name);
1520 }
1521
1522 int
vhd_header_decode_parent(vhd_context_t * ctx,vhd_header_t * header,char ** buf)1523 vhd_header_decode_parent(vhd_context_t *ctx, vhd_header_t *header, char **buf)
1524 {
1525 char *code, out[512];
1526
1527 if (vhd_creator_tapdisk(ctx) &&
1528 ctx->footer.crtr_ver == VHD_VERSION(0, 1))
1529 code = UTF_16;
1530 else
1531 code = UTF_16BE;
1532
1533 *buf = vhd_w2u_decode_location(header->prt_name, out, 512, code);
1534 return (*buf == NULL ? -EINVAL : 0);
1535 }
1536
1537 int
vhd_parent_locator_read(vhd_context_t * ctx,vhd_parent_locator_t * loc,char ** parent)1538 vhd_parent_locator_read(vhd_context_t *ctx,
1539 vhd_parent_locator_t *loc, char **parent)
1540 {
1541 int err, size;
1542 char *raw, *out, *name;
1543
1544 raw = NULL;
1545 out = NULL;
1546 name = NULL;
1547 *parent = NULL;
1548
1549 if (ctx->footer.type != HD_TYPE_DIFF) {
1550 err = -EINVAL;
1551 goto out;
1552 }
1553
1554 switch (loc->code) {
1555 case PLAT_CODE_MACX:
1556 case PLAT_CODE_W2KU:
1557 case PLAT_CODE_W2RU:
1558 break;
1559 default:
1560 err = -EINVAL;
1561 goto out;
1562 }
1563
1564 err = vhd_seek(ctx, loc->data_offset, SEEK_SET);
1565 if (err)
1566 goto out;
1567
1568 size = vhd_parent_locator_size(loc);
1569 if (size <= 0) {
1570 err = -EINVAL;
1571 goto out;
1572 }
1573
1574 err = posix_memalign((void **)&raw, VHD_SECTOR_SIZE, size);
1575 if (err) {
1576 raw = NULL;
1577 err = -err;
1578 goto out;
1579 }
1580
1581 err = vhd_read(ctx, raw, size);
1582 if (err)
1583 goto out;
1584
1585 out = malloc(loc->data_len + 1);
1586 if (!out) {
1587 err = -ENOMEM;
1588 goto out;
1589 }
1590
1591 switch (loc->code) {
1592 case PLAT_CODE_MACX:
1593 name = vhd_macx_decode_location(raw, out, loc->data_len);
1594 break;
1595 case PLAT_CODE_W2KU:
1596 case PLAT_CODE_W2RU:
1597 name = vhd_w2u_decode_location(raw, out,
1598 loc->data_len, UTF_16LE);
1599 break;
1600 }
1601
1602 if (!name) {
1603 err = -EINVAL;
1604 goto out;
1605 }
1606
1607 err = 0;
1608 *parent = name;
1609
1610 out:
1611 free(raw);
1612 free(out);
1613
1614 if (err) {
1615 VHDLOG("%s: error reading parent locator: %d\n",
1616 ctx->file, err);
1617 VHDLOG("%s: locator: code %u, space 0x%x, len 0x%x, "
1618 "off 0x%"PRIx64"\n", ctx->file, loc->code, loc->data_space,
1619 loc->data_len, loc->data_offset);
1620 }
1621
1622 return err;
1623 }
1624
1625 int
vhd_parent_locator_get(vhd_context_t * ctx,char ** parent)1626 vhd_parent_locator_get(vhd_context_t *ctx, char **parent)
1627 {
1628 int i, n, err;
1629 char *name, *location;
1630 vhd_parent_locator_t *loc;
1631
1632 err = 0;
1633 *parent = NULL;
1634
1635 if (ctx->footer.type != HD_TYPE_DIFF)
1636 return -EINVAL;
1637
1638 n = vhd_parent_locator_count(ctx);
1639 for (i = 0; i < n; i++) {
1640 loc = ctx->header.loc + i;
1641 err = vhd_parent_locator_read(ctx, loc, &name);
1642 if (err)
1643 continue;
1644
1645 err = vhd_find_parent(ctx, name, &location);
1646 if (err)
1647 VHDLOG("%s: couldn't find parent %s (%d)\n",
1648 ctx->file, name, err);
1649 free(name);
1650
1651 if (!err) {
1652 *parent = location;
1653 return 0;
1654 }
1655 }
1656
1657 return err;
1658 }
1659
1660 int
vhd_parent_locator_write_at(vhd_context_t * ctx,const char * parent,off_t off,uint32_t code,size_t max_bytes,vhd_parent_locator_t * loc)1661 vhd_parent_locator_write_at(vhd_context_t *ctx,
1662 const char *parent, off_t off, uint32_t code,
1663 size_t max_bytes, vhd_parent_locator_t *loc)
1664 {
1665 struct stat stats;
1666 int err, len, size;
1667 char *absolute_path, *relative_path, *encoded, *block;
1668
1669 memset(loc, 0, sizeof(vhd_parent_locator_t));
1670
1671 if (ctx->footer.type != HD_TYPE_DIFF)
1672 return -EINVAL;
1673
1674 absolute_path = NULL;
1675 relative_path = NULL;
1676 encoded = NULL;
1677 block = NULL;
1678 size = 0;
1679 len = 0;
1680
1681 switch (code) {
1682 case PLAT_CODE_MACX:
1683 case PLAT_CODE_W2KU:
1684 case PLAT_CODE_W2RU:
1685 break;
1686 default:
1687 return -EINVAL;
1688 }
1689
1690 absolute_path = realpath(parent, NULL);
1691 if (!absolute_path) {
1692 err = -errno;
1693 goto out;
1694 }
1695
1696 err = stat(absolute_path, &stats);
1697 if (err) {
1698 err = -errno;
1699 goto out;
1700 }
1701
1702 if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
1703 err = -EINVAL;
1704 goto out;
1705 }
1706
1707 relative_path = relative_path_to(ctx->file, absolute_path, &err);
1708 if (!relative_path || err) {
1709 err = (err ? err : -EINVAL);
1710 goto out;
1711 }
1712
1713 switch (code) {
1714 case PLAT_CODE_MACX:
1715 err = vhd_macx_encode_location(relative_path, &encoded, &len);
1716 break;
1717 case PLAT_CODE_W2KU:
1718 case PLAT_CODE_W2RU:
1719 err = vhd_w2u_encode_location(relative_path, &encoded, &len);
1720 break;
1721 default:
1722 err = -EINVAL;
1723 }
1724
1725 if (err)
1726 goto out;
1727
1728 err = vhd_seek(ctx, off, SEEK_SET);
1729 if (err)
1730 goto out;
1731
1732 size = vhd_bytes_padded(len);
1733
1734 if (max_bytes && size > max_bytes) {
1735 err = -ENAMETOOLONG;
1736 goto out;
1737 }
1738
1739 err = posix_memalign((void **)&block, VHD_SECTOR_SIZE, size);
1740 if (err) {
1741 block = NULL;
1742 err = -err;
1743 goto out;
1744 }
1745
1746 memset(block, 0, size);
1747 memcpy(block, encoded, len);
1748
1749 err = vhd_write(ctx, block, size);
1750 if (err)
1751 goto out;
1752
1753 err = 0;
1754
1755 out:
1756 free(absolute_path);
1757 free(relative_path);
1758 free(encoded);
1759 free(block);
1760
1761 if (!err) {
1762 loc->res = 0;
1763 loc->code = code;
1764 loc->data_len = len;
1765 /*
1766 * write number of bytes ('size') instead of number of sectors
1767 * into loc->data_space to be compatible with MSFT, even though
1768 * this goes against the specs
1769 */
1770 loc->data_space = size;
1771 loc->data_offset = off;
1772 }
1773
1774 return err;
1775 }
1776
1777 static int
vhd_footer_offset_at_eof(vhd_context_t * ctx,off_t * off)1778 vhd_footer_offset_at_eof(vhd_context_t *ctx, off_t *off)
1779 {
1780 int err;
1781 if ((err = vhd_seek(ctx, 0, SEEK_END)))
1782 return errno;
1783 *off = vhd_position(ctx) - sizeof(vhd_footer_t);
1784 return 0;
1785 }
1786
1787 int
vhd_read_bitmap(vhd_context_t * ctx,uint32_t block,char ** bufp)1788 vhd_read_bitmap(vhd_context_t *ctx, uint32_t block, char **bufp)
1789 {
1790 int err;
1791 char *buf;
1792 size_t size;
1793 off_t off;
1794 uint64_t blk;
1795
1796 buf = NULL;
1797 *bufp = NULL;
1798
1799 if (!vhd_type_dynamic(ctx))
1800 return -EINVAL;
1801
1802 err = vhd_get_bat(ctx);
1803 if (err)
1804 return err;
1805
1806 if (block >= ctx->bat.entries)
1807 return -ERANGE;
1808
1809 blk = ctx->bat.bat[block];
1810 if (blk == DD_BLK_UNUSED)
1811 return -EINVAL;
1812
1813 off = vhd_sectors_to_bytes(blk);
1814 size = vhd_bytes_padded(ctx->spb >> 3);
1815
1816 err = vhd_seek(ctx, off, SEEK_SET);
1817 if (err)
1818 return err;
1819
1820 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
1821 if (err)
1822 return -err;
1823
1824 err = vhd_read(ctx, buf, size);
1825 if (err)
1826 goto fail;
1827
1828 *bufp = buf;
1829 return 0;
1830
1831 fail:
1832 free(buf);
1833 return err;
1834 }
1835
1836 int
vhd_read_block(vhd_context_t * ctx,uint32_t block,char ** bufp)1837 vhd_read_block(vhd_context_t *ctx, uint32_t block, char **bufp)
1838 {
1839 int err;
1840 char *buf;
1841 size_t size;
1842 uint64_t blk;
1843 off_t end, off;
1844
1845 buf = NULL;
1846 *bufp = NULL;
1847
1848 if (!vhd_type_dynamic(ctx))
1849 return -EINVAL;
1850
1851 err = vhd_get_bat(ctx);
1852 if (err)
1853 return err;
1854
1855 if (block >= ctx->bat.entries)
1856 return -ERANGE;
1857
1858 blk = ctx->bat.bat[block];
1859 if (blk == DD_BLK_UNUSED)
1860 return -EINVAL;
1861
1862 off = vhd_sectors_to_bytes(blk + ctx->bm_secs);
1863 size = vhd_sectors_to_bytes(ctx->spb);
1864
1865 err = vhd_footer_offset_at_eof(ctx, &end);
1866 if (err)
1867 return err;
1868
1869 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
1870 if (err) {
1871 err = -err;
1872 goto fail;
1873 }
1874
1875 if (end < off + ctx->header.block_size) {
1876 size = end - off;
1877 memset(buf + size, 0, ctx->header.block_size - size);
1878 }
1879
1880 err = vhd_seek(ctx, off, SEEK_SET);
1881 if (err)
1882 goto fail;
1883
1884 err = vhd_read(ctx, buf, size);
1885 if (err)
1886 goto fail;
1887
1888 *bufp = buf;
1889 return 0;
1890
1891 fail:
1892 free(buf);
1893 return err;
1894 }
1895
1896 int
vhd_write_footer_at(vhd_context_t * ctx,vhd_footer_t * footer,off_t off)1897 vhd_write_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off_t off)
1898 {
1899 int err;
1900 vhd_footer_t *f;
1901
1902 f = NULL;
1903
1904 err = posix_memalign((void **)&f,
1905 VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
1906 if (err) {
1907 f = NULL;
1908 err = -err;
1909 goto out;
1910 }
1911
1912 memcpy(f, footer, sizeof(vhd_footer_t));
1913 f->checksum = vhd_checksum_footer(f);
1914
1915 err = vhd_validate_footer(f);
1916 if (err)
1917 goto out;
1918
1919 err = vhd_seek(ctx, off, SEEK_SET);
1920 if (err)
1921 goto out;
1922
1923 vhd_footer_out(f);
1924
1925 err = vhd_write(ctx, f, sizeof(vhd_footer_t));
1926
1927 out:
1928 if (err)
1929 VHDLOG("%s: failed writing footer at 0x%08"PRIx64": %d\n",
1930 ctx->file, off, err);
1931 free(f);
1932 return err;
1933 }
1934
1935 int
vhd_write_footer(vhd_context_t * ctx,vhd_footer_t * footer)1936 vhd_write_footer(vhd_context_t *ctx, vhd_footer_t *footer)
1937 {
1938 int err;
1939 off_t off;
1940
1941 if (ctx->is_block)
1942 err = vhd_footer_offset_at_eof(ctx, &off);
1943 else
1944 err = vhd_end_of_data(ctx, &off);
1945 if (err)
1946 return err;
1947
1948 err = vhd_write_footer_at(ctx, footer, off);
1949 if (err)
1950 return err;
1951
1952 if (!vhd_type_dynamic(ctx))
1953 return 0;
1954
1955 return vhd_write_footer_at(ctx, footer, 0);
1956 }
1957
1958 int
vhd_write_header_at(vhd_context_t * ctx,vhd_header_t * header,off_t off)1959 vhd_write_header_at(vhd_context_t *ctx, vhd_header_t *header, off_t off)
1960 {
1961 int err;
1962 vhd_header_t *h;
1963
1964 h = NULL;
1965
1966 if (!vhd_type_dynamic(ctx)) {
1967 err = -EINVAL;
1968 goto out;
1969 }
1970
1971 err = posix_memalign((void **)&h,
1972 VHD_SECTOR_SIZE, sizeof(vhd_header_t));
1973 if (err) {
1974 h = NULL;
1975 err = -err;
1976 goto out;
1977 }
1978
1979 memcpy(h, header, sizeof(vhd_header_t));
1980
1981 h->checksum = vhd_checksum_header(h);
1982 err = vhd_validate_header(h);
1983 if (err)
1984 goto out;
1985
1986 vhd_header_out(h);
1987
1988 err = vhd_seek(ctx, off, SEEK_SET);
1989 if (err)
1990 goto out;
1991
1992 err = vhd_write(ctx, h, sizeof(vhd_header_t));
1993
1994 out:
1995 if (err)
1996 VHDLOG("%s: failed writing header at 0x%08"PRIx64": %d\n",
1997 ctx->file, off, err);
1998 free(h);
1999 return err;
2000 }
2001
2002 int
vhd_write_header(vhd_context_t * ctx,vhd_header_t * header)2003 vhd_write_header(vhd_context_t *ctx, vhd_header_t *header)
2004 {
2005 int err;
2006 off_t off;
2007
2008 if (!vhd_type_dynamic(ctx))
2009 return -EINVAL;
2010
2011 off = ctx->footer.data_offset;
2012 return vhd_write_header_at(ctx, header, off);
2013 }
2014
2015 int
vhd_write_bat(vhd_context_t * ctx,vhd_bat_t * bat)2016 vhd_write_bat(vhd_context_t *ctx, vhd_bat_t *bat)
2017 {
2018 int err;
2019 off_t off;
2020 vhd_bat_t b;
2021 size_t size;
2022
2023 if (!vhd_type_dynamic(ctx))
2024 return -EINVAL;
2025
2026 err = vhd_validate_bat(&ctx->bat);
2027 if (err)
2028 return err;
2029
2030 err = vhd_validate_bat(bat);
2031 if (err)
2032 return err;
2033
2034 memset(&b, 0, sizeof(vhd_bat_t));
2035
2036 off = ctx->header.table_offset;
2037 size = vhd_bytes_padded(bat->entries * sizeof(uint32_t));
2038
2039 err = vhd_seek(ctx, off, SEEK_SET);
2040 if (err)
2041 return err;
2042
2043 err = posix_memalign((void **)&b.bat, VHD_SECTOR_SIZE, size);
2044 if (err)
2045 return -err;
2046
2047 memcpy(b.bat, bat->bat, size);
2048 b.spb = bat->spb;
2049 b.entries = bat->entries;
2050 vhd_bat_out(&b);
2051
2052 err = vhd_write(ctx, b.bat, size);
2053 free(b.bat);
2054
2055 return err;
2056 }
2057
2058 int
vhd_write_batmap(vhd_context_t * ctx,vhd_batmap_t * batmap)2059 vhd_write_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap)
2060 {
2061 int err;
2062 off_t off;
2063 vhd_batmap_t b;
2064 char *buf, *map;
2065 size_t size, map_size;
2066
2067 buf = NULL;
2068 map = NULL;
2069
2070 if (!vhd_has_batmap(ctx)) {
2071 err = -EINVAL;
2072 goto out;
2073 }
2074
2075 b.header = batmap->header;
2076 b.map = batmap->map;
2077
2078 b.header.checksum = vhd_checksum_batmap(&b);
2079 err = vhd_validate_batmap(&b);
2080 if (err)
2081 goto out;
2082
2083 off = b.header.batmap_offset;
2084 map_size = vhd_sectors_to_bytes(b.header.batmap_size);
2085
2086 err = vhd_seek(ctx, off, SEEK_SET);
2087 if (err)
2088 goto out;
2089
2090 err = posix_memalign((void **)&map, VHD_SECTOR_SIZE, map_size);
2091 if (err) {
2092 map = NULL;
2093 err = -err;
2094 goto out;
2095 }
2096
2097 memcpy(map, b.map, map_size);
2098
2099 err = vhd_write(ctx, map, map_size);
2100 if (err)
2101 goto out;
2102
2103 err = vhd_batmap_header_offset(ctx, &off);
2104 if (err)
2105 goto out;
2106
2107 size = vhd_bytes_padded(sizeof(vhd_batmap_header_t));
2108
2109 err = vhd_seek(ctx, off, SEEK_SET);
2110 if (err)
2111 goto out;
2112
2113 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
2114 if (err) {
2115 err = -err;
2116 buf = NULL;
2117 goto out;
2118 }
2119
2120 vhd_batmap_header_out(&b);
2121 memset(buf, 0, size);
2122 memcpy(buf, &b.header, sizeof(vhd_batmap_header_t));
2123
2124 err = vhd_write(ctx, buf, size);
2125
2126 out:
2127 if (err)
2128 VHDLOG("%s: failed writing batmap: %d\n", ctx->file, err);
2129 free(buf);
2130 free(map);
2131 return 0;
2132 }
2133
2134 int
vhd_write_bitmap(vhd_context_t * ctx,uint32_t block,char * bitmap)2135 vhd_write_bitmap(vhd_context_t *ctx, uint32_t block, char *bitmap)
2136 {
2137 int err;
2138 off_t off;
2139 uint64_t blk;
2140 size_t secs, size;
2141
2142 if (!vhd_type_dynamic(ctx))
2143 return -EINVAL;
2144
2145 err = vhd_validate_bat(&ctx->bat);
2146 if (err)
2147 return err;
2148
2149 if (block >= ctx->bat.entries)
2150 return -ERANGE;
2151
2152 if ((unsigned long)bitmap & (VHD_SECTOR_SIZE - 1))
2153 return -EINVAL;
2154
2155 blk = ctx->bat.bat[block];
2156 if (blk == DD_BLK_UNUSED)
2157 return -EINVAL;
2158
2159 off = vhd_sectors_to_bytes(blk);
2160 size = vhd_sectors_to_bytes(ctx->bm_secs);
2161
2162 err = vhd_seek(ctx, off, SEEK_SET);
2163 if (err)
2164 return err;
2165
2166 err = vhd_write(ctx, bitmap, size);
2167 if (err)
2168 return err;
2169
2170 return 0;
2171 }
2172
2173 int
vhd_write_block(vhd_context_t * ctx,uint32_t block,char * data)2174 vhd_write_block(vhd_context_t *ctx, uint32_t block, char *data)
2175 {
2176 int err;
2177 off_t off;
2178 size_t size;
2179 uint64_t blk;
2180
2181 if (!vhd_type_dynamic(ctx))
2182 return -EINVAL;
2183
2184 err = vhd_validate_bat(&ctx->bat);
2185 if (err)
2186 return err;
2187
2188 if (block >= ctx->bat.entries)
2189 return -ERANGE;
2190
2191 if ((unsigned long)data & (VHD_SECTOR_SIZE -1))
2192 return -EINVAL;
2193
2194 blk = ctx->bat.bat[block];
2195 if (blk == DD_BLK_UNUSED)
2196 return -EINVAL;
2197
2198 off = vhd_sectors_to_bytes(blk + ctx->bm_secs);
2199 size = vhd_sectors_to_bytes(ctx->spb);
2200
2201 err = vhd_seek(ctx, off, SEEK_SET);
2202 if (err)
2203 return err;
2204
2205 err = vhd_write(ctx, data, size);
2206 if (err)
2207 return err;
2208
2209 return 0;
2210 }
2211
2212 static inline int
namedup(char ** dup,const char * name)2213 namedup(char **dup, const char *name)
2214 {
2215 *dup = NULL;
2216
2217 if (strnlen(name, MAX_NAME_LEN) >= MAX_NAME_LEN)
2218 return -ENAMETOOLONG;
2219
2220 *dup = strdup(name);
2221 if (*dup == NULL)
2222 return -ENOMEM;
2223
2224 return 0;
2225 }
2226
2227 int
vhd_seek(vhd_context_t * ctx,off_t offset,int whence)2228 vhd_seek(vhd_context_t *ctx, off_t offset, int whence)
2229 {
2230 off_t off;
2231
2232 off = lseek(ctx->fd, offset, whence);
2233 if (off == (off_t)-1) {
2234 VHDLOG("%s: seek(0x%08"PRIx64", %d) failed: %d\n",
2235 ctx->file, offset, whence, -errno);
2236 return -errno;
2237 }
2238
2239 return 0;
2240 }
2241
2242 off_t
vhd_position(vhd_context_t * ctx)2243 vhd_position(vhd_context_t *ctx)
2244 {
2245 return lseek(ctx->fd, 0, SEEK_CUR);
2246 }
2247
2248 int
vhd_read(vhd_context_t * ctx,void * buf,size_t size)2249 vhd_read(vhd_context_t *ctx, void *buf, size_t size)
2250 {
2251 size_t ret;
2252
2253 errno = 0;
2254
2255 ret = read(ctx->fd, buf, size);
2256 if (ret == size)
2257 return 0;
2258
2259 VHDLOG("%s: read of %zu returned %zd, errno: %d\n",
2260 ctx->file, size, ret, -errno);
2261
2262 return (errno ? -errno : -EIO);
2263 }
2264
2265 int
vhd_write(vhd_context_t * ctx,void * buf,size_t size)2266 vhd_write(vhd_context_t *ctx, void *buf, size_t size)
2267 {
2268 size_t ret;
2269
2270 errno = 0;
2271
2272 ret = write(ctx->fd, buf, size);
2273 if (ret == size)
2274 return 0;
2275
2276 VHDLOG("%s: write of %zu returned %zd, errno: %d\n",
2277 ctx->file, size, ret, -errno);
2278
2279 return (errno ? -errno : -EIO);
2280 }
2281
2282 int
vhd_offset(vhd_context_t * ctx,uint32_t sector,uint32_t * offset)2283 vhd_offset(vhd_context_t *ctx, uint32_t sector, uint32_t *offset)
2284 {
2285 int err;
2286 uint32_t block;
2287
2288 if (!vhd_type_dynamic(ctx))
2289 return sector;
2290
2291 err = vhd_get_bat(ctx);
2292 if (err)
2293 return err;
2294
2295 block = sector / ctx->spb;
2296 if (ctx->bat.bat[block] == DD_BLK_UNUSED)
2297 *offset = DD_BLK_UNUSED;
2298 else
2299 *offset = ctx->bat.bat[block] +
2300 ctx->bm_secs + (sector % ctx->spb);
2301
2302 return 0;
2303 }
2304
2305 int
vhd_open_fast(vhd_context_t * ctx)2306 vhd_open_fast(vhd_context_t *ctx)
2307 {
2308 int err;
2309 char *buf;
2310 size_t size;
2311
2312 size = sizeof(vhd_footer_t) + sizeof(vhd_header_t);
2313 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
2314 if (err) {
2315 VHDLOG("failed allocating %s: %d\n", ctx->file, -err);
2316 return -err;
2317 }
2318
2319 err = vhd_read(ctx, buf, size);
2320 if (err) {
2321 VHDLOG("failed reading %s: %d\n", ctx->file, err);
2322 goto out;
2323 }
2324
2325 memcpy(&ctx->footer, buf, sizeof(vhd_footer_t));
2326 vhd_footer_in(&ctx->footer);
2327 err = vhd_validate_footer(&ctx->footer);
2328 if (err)
2329 goto out;
2330
2331 if (vhd_type_dynamic(ctx)) {
2332 if (ctx->footer.data_offset != sizeof(vhd_footer_t))
2333 err = vhd_read_header(ctx, &ctx->header);
2334 else {
2335 memcpy(&ctx->header,
2336 buf + sizeof(vhd_footer_t),
2337 sizeof(vhd_header_t));
2338 vhd_header_in(&ctx->header);
2339 err = vhd_validate_header(&ctx->header);
2340 }
2341
2342 if (err)
2343 goto out;
2344
2345 ctx->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
2346 ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3);
2347 }
2348
2349 out:
2350 free(buf);
2351 return err;
2352 }
2353
2354 int
vhd_open(vhd_context_t * ctx,const char * file,int flags)2355 vhd_open(vhd_context_t *ctx, const char *file, int flags)
2356 {
2357 int err, oflags;
2358
2359 if (flags & VHD_OPEN_STRICT)
2360 vhd_flag_clear(flags, VHD_OPEN_FAST);
2361
2362 memset(ctx, 0, sizeof(vhd_context_t));
2363 ctx->fd = -1;
2364 ctx->oflags = flags;
2365
2366 err = namedup(&ctx->file, file);
2367 if (err)
2368 return err;
2369
2370 oflags = O_DIRECT | O_LARGEFILE;
2371 if (flags & VHD_OPEN_RDONLY)
2372 oflags |= O_RDONLY;
2373 if (flags & VHD_OPEN_RDWR)
2374 oflags |= O_RDWR;
2375
2376 ctx->fd = open(ctx->file, oflags, 0644);
2377 if (ctx->fd == -1) {
2378 err = -errno;
2379 VHDLOG("failed to open %s: %d\n", ctx->file, err);
2380 goto fail;
2381 }
2382
2383 err = vhd_test_file_fixed(ctx->file, &ctx->is_block);
2384 if (err)
2385 goto fail;
2386
2387 if (flags & VHD_OPEN_FAST) {
2388 err = vhd_open_fast(ctx);
2389 if (err)
2390 goto fail;
2391
2392 return 0;
2393 }
2394
2395 err = vhd_read_footer(ctx, &ctx->footer);
2396 if (err)
2397 goto fail;
2398
2399 if (!(flags & VHD_OPEN_IGNORE_DISABLED) && vhd_disabled(ctx)) {
2400 err = -EINVAL;
2401 goto fail;
2402 }
2403
2404 if (vhd_type_dynamic(ctx)) {
2405 err = vhd_read_header(ctx, &ctx->header);
2406 if (err)
2407 goto fail;
2408
2409 ctx->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
2410 ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3);
2411 }
2412
2413 return 0;
2414
2415 fail:
2416 if (ctx->fd != -1)
2417 close(ctx->fd);
2418 free(ctx->file);
2419 memset(ctx, 0, sizeof(vhd_context_t));
2420 return err;
2421 }
2422
2423 void
vhd_close(vhd_context_t * ctx)2424 vhd_close(vhd_context_t *ctx)
2425 {
2426 if (ctx->file)
2427 close(ctx->fd);
2428 free(ctx->file);
2429 free(ctx->bat.bat);
2430 free(ctx->batmap.map);
2431 memset(ctx, 0, sizeof(vhd_context_t));
2432 }
2433
2434 static inline void
vhd_initialize_footer(vhd_context_t * ctx,int type,uint64_t size)2435 vhd_initialize_footer(vhd_context_t *ctx, int type, uint64_t size)
2436 {
2437 memset(&ctx->footer, 0, sizeof(vhd_footer_t));
2438 memcpy(ctx->footer.cookie, HD_COOKIE, sizeof(ctx->footer.cookie));
2439 ctx->footer.features = HD_RESERVED;
2440 ctx->footer.ff_version = HD_FF_VERSION;
2441 ctx->footer.timestamp = vhd_time(time(NULL));
2442 ctx->footer.crtr_ver = VHD_CURRENT_VERSION;
2443 ctx->footer.crtr_os = 0x00000000;
2444 ctx->footer.orig_size = size;
2445 ctx->footer.curr_size = size;
2446 ctx->footer.geometry = vhd_chs(size);
2447 ctx->footer.type = type;
2448 ctx->footer.saved = 0;
2449 ctx->footer.data_offset = 0xFFFFFFFFFFFFFFFF;
2450 strcpy(ctx->footer.crtr_app, "tap");
2451 vhd_uuid_generate(&ctx->footer.uuid);
2452 }
2453
2454 static int
vhd_initialize_header_parent_name(vhd_context_t * ctx,const char * parent_path)2455 vhd_initialize_header_parent_name(vhd_context_t *ctx, const char *parent_path)
2456 {
2457 int err;
2458 iconv_t cd;
2459 size_t ibl, obl;
2460 char *ppath, *dst;
2461 const char *pname;
2462 char *codeset;
2463
2464 err = 0;
2465 pname = NULL;
2466 ppath = NULL;
2467
2468 /*
2469 * MICROSOFT_COMPAT
2470 * big endian unicode here
2471 */
2472 codeset = nl_langinfo(CODESET);
2473 cd = iconv_open(UTF_16BE, codeset);
2474 if (cd == (iconv_t)-1) {
2475 err = -errno;
2476 goto out;
2477 }
2478
2479 ppath = strdup(parent_path);
2480 if (!ppath) {
2481 err = -ENOMEM;
2482 goto out;
2483 }
2484
2485 pname = basename(ppath);
2486 if (!strcmp(pname, "")) {
2487 err = -EINVAL;
2488 goto out;
2489 }
2490
2491 ibl = strlen(pname);
2492 obl = sizeof(ctx->header.prt_name);
2493 dst = ctx->header.prt_name;
2494
2495 memset(dst, 0, obl);
2496
2497 if (iconv(cd,
2498 #ifdef __linux__
2499 (char **)
2500 #endif
2501 &pname, &ibl, &dst, &obl) == (size_t)-1 || ibl)
2502 err = (errno ? -errno : -EINVAL);
2503
2504 out:
2505 iconv_close(cd);
2506 free(ppath);
2507 return err;
2508 }
2509
2510 static off_t
get_file_size(const char * name)2511 get_file_size(const char *name)
2512 {
2513 int fd;
2514 off_t end;
2515
2516 fd = open(name, O_LARGEFILE | O_RDONLY);
2517 if (fd == -1) {
2518 VHDLOG("unable to open '%s': %d\n", name, errno);
2519 return -errno;
2520 }
2521 end = lseek(fd, 0, SEEK_END);
2522 close(fd);
2523 return end;
2524 }
2525
2526 static int
vhd_initialize_header(vhd_context_t * ctx,const char * parent_path,uint64_t size,int raw)2527 vhd_initialize_header(vhd_context_t *ctx, const char *parent_path,
2528 uint64_t size, int raw)
2529 {
2530 int err;
2531 struct stat stats;
2532 vhd_context_t parent;
2533
2534 if (!vhd_type_dynamic(ctx))
2535 return -EINVAL;
2536
2537 memset(&ctx->header, 0, sizeof(vhd_header_t));
2538 memcpy(ctx->header.cookie, DD_COOKIE, sizeof(ctx->header.cookie));
2539 ctx->header.data_offset = (uint64_t)-1;
2540 ctx->header.table_offset = VHD_SECTOR_SIZE * 3; /* 1 ftr + 2 hdr */
2541 ctx->header.hdr_ver = DD_VERSION;
2542 ctx->header.block_size = VHD_BLOCK_SIZE;
2543 ctx->header.prt_ts = 0;
2544 ctx->header.res1 = 0;
2545 ctx->header.max_bat_size = (ctx->footer.curr_size +
2546 VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
2547
2548 ctx->footer.data_offset = VHD_SECTOR_SIZE;
2549
2550 if (ctx->footer.type == HD_TYPE_DYNAMIC)
2551 return 0;
2552
2553 err = stat(parent_path, &stats);
2554 if (err == -1)
2555 return -errno;
2556
2557 if (raw) {
2558 ctx->header.prt_ts = vhd_time(stats.st_mtime);
2559 if (!size)
2560 size = get_file_size(parent_path);
2561 }
2562 else {
2563 err = vhd_open(&parent, parent_path, VHD_OPEN_RDONLY);
2564 if (err)
2565 return err;
2566
2567 ctx->header.prt_ts = vhd_time(stats.st_mtime);
2568 vhd_uuid_copy(&ctx->header.prt_uuid, &parent.footer.uuid);
2569 if (!size)
2570 size = parent.footer.curr_size;
2571 vhd_close(&parent);
2572 }
2573 ctx->footer.orig_size = size;
2574 ctx->footer.curr_size = size;
2575 ctx->footer.geometry = vhd_chs(size);
2576 ctx->header.max_bat_size =
2577 (size + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
2578
2579 return vhd_initialize_header_parent_name(ctx, parent_path);
2580 }
2581
2582 static int
vhd_write_parent_locators(vhd_context_t * ctx,const char * parent)2583 vhd_write_parent_locators(vhd_context_t *ctx, const char *parent)
2584 {
2585 int i, err;
2586 off_t off;
2587 uint32_t code;
2588
2589 code = PLAT_CODE_NONE;
2590
2591 if (ctx->footer.type != HD_TYPE_DIFF)
2592 return -EINVAL;
2593
2594 off = ctx->batmap.header.batmap_offset +
2595 vhd_sectors_to_bytes(ctx->batmap.header.batmap_size);
2596 if (off & (VHD_SECTOR_SIZE - 1))
2597 off = vhd_bytes_padded(off);
2598
2599 for (i = 0; i < 3; i++) {
2600 switch (i) {
2601 case 0:
2602 code = PLAT_CODE_MACX;
2603 break;
2604 case 1:
2605 code = PLAT_CODE_W2KU;
2606 break;
2607 case 2:
2608 code = PLAT_CODE_W2RU;
2609 break;
2610 }
2611
2612 err = vhd_parent_locator_write_at(ctx, parent, off, code,
2613 0, ctx->header.loc + i);
2614 if (err)
2615 return err;
2616
2617 off += vhd_parent_locator_size(ctx->header.loc + i);
2618 }
2619
2620 return 0;
2621 }
2622
2623 int
vhd_change_parent(vhd_context_t * child,char * parent_path,int raw)2624 vhd_change_parent(vhd_context_t *child, char *parent_path, int raw)
2625 {
2626 int i, err;
2627 char *ppath;
2628 struct stat stats;
2629 vhd_context_t parent;
2630
2631 ppath = realpath(parent_path, NULL);
2632 if (!ppath) {
2633 VHDLOG("error resolving parent path %s for %s: %d\n",
2634 parent_path, child->file, errno);
2635 return -errno;
2636 }
2637
2638 err = stat(ppath, &stats);
2639 if (err == -1) {
2640 err = -errno;
2641 goto out;
2642 }
2643
2644 if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
2645 err = -EINVAL;
2646 goto out;
2647 }
2648
2649 if (raw) {
2650 vhd_uuid_clear(&child->header.prt_uuid);
2651 } else {
2652 err = vhd_open(&parent, ppath, VHD_OPEN_RDONLY);
2653 if (err) {
2654 VHDLOG("error opening parent %s for %s: %d\n",
2655 ppath, child->file, err);
2656 goto out;
2657 }
2658 vhd_uuid_copy(&child->header.prt_uuid, &parent.footer.uuid);
2659 vhd_close(&parent);
2660 }
2661
2662 vhd_initialize_header_parent_name(child, ppath);
2663 child->header.prt_ts = vhd_time(stats.st_mtime);
2664
2665 for (i = 0; i < vhd_parent_locator_count(child); i++) {
2666 vhd_parent_locator_t *loc = child->header.loc + i;
2667 size_t max = vhd_parent_locator_size(loc);
2668
2669 switch (loc->code) {
2670 case PLAT_CODE_MACX:
2671 case PLAT_CODE_W2KU:
2672 case PLAT_CODE_W2RU:
2673 break;
2674 default:
2675 continue;
2676 }
2677
2678 err = vhd_parent_locator_write_at(child, ppath,
2679 loc->data_offset,
2680 loc->code, max, loc);
2681 if (err) {
2682 VHDLOG("error writing parent locator %d for %s: %d\n",
2683 i, child->file, err);
2684 goto out;
2685 }
2686 }
2687
2688 TEST_FAIL_AT(FAIL_REPARENT_LOCATOR);
2689
2690 err = vhd_write_header(child, &child->header);
2691 if (err) {
2692 VHDLOG("error writing header for %s: %d\n", child->file, err);
2693 goto out;
2694 }
2695
2696 err = 0;
2697
2698 out:
2699 free(ppath);
2700 return err;
2701 }
2702
2703 static int
vhd_create_batmap(vhd_context_t * ctx)2704 vhd_create_batmap(vhd_context_t *ctx)
2705 {
2706 off_t off;
2707 int err, map_bytes;
2708 vhd_batmap_header_t *header;
2709
2710 if (!vhd_type_dynamic(ctx))
2711 return -EINVAL;
2712
2713 map_bytes = (ctx->header.max_bat_size + 7) >> 3;
2714 header = &ctx->batmap.header;
2715
2716 memset(header, 0, sizeof(vhd_batmap_header_t));
2717 memcpy(header->cookie, VHD_BATMAP_COOKIE, sizeof(header->cookie));
2718
2719 err = vhd_batmap_header_offset(ctx, &off);
2720 if (err)
2721 return err;
2722
2723 header->batmap_offset = off +
2724 vhd_bytes_padded(sizeof(vhd_batmap_header_t));
2725 header->batmap_size = secs_round_up_no_zero(map_bytes);
2726 header->batmap_version = VHD_BATMAP_CURRENT_VERSION;
2727
2728 map_bytes = vhd_sectors_to_bytes(header->batmap_size);
2729
2730 err = posix_memalign((void **)&ctx->batmap.map,
2731 VHD_SECTOR_SIZE, map_bytes);
2732 if (err) {
2733 ctx->batmap.map = NULL;
2734 return -err;
2735 }
2736
2737 memset(ctx->batmap.map, 0, map_bytes);
2738
2739 return vhd_write_batmap(ctx, &ctx->batmap);
2740 }
2741
2742 static int
vhd_create_bat(vhd_context_t * ctx)2743 vhd_create_bat(vhd_context_t *ctx)
2744 {
2745 int i, err;
2746 size_t size;
2747
2748 if (!vhd_type_dynamic(ctx))
2749 return -EINVAL;
2750
2751 size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
2752 err = posix_memalign((void **)&ctx->bat.bat, VHD_SECTOR_SIZE, size);
2753 if (err) {
2754 ctx->bat.bat = NULL;
2755 return err;
2756 }
2757
2758 memset(ctx->bat.bat, 0, size);
2759 for (i = 0; i < ctx->header.max_bat_size; i++)
2760 ctx->bat.bat[i] = DD_BLK_UNUSED;
2761
2762 err = vhd_seek(ctx, ctx->header.table_offset, SEEK_SET);
2763 if (err)
2764 return err;
2765
2766 ctx->bat.entries = ctx->header.max_bat_size;
2767 ctx->bat.spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
2768
2769 return vhd_write_bat(ctx, &ctx->bat);
2770 }
2771
2772 static int
vhd_initialize_fixed_disk(vhd_context_t * ctx)2773 vhd_initialize_fixed_disk(vhd_context_t *ctx)
2774 {
2775 char *buf;
2776 int i, err;
2777
2778 if (ctx->footer.type != HD_TYPE_FIXED)
2779 return -EINVAL;
2780
2781 err = vhd_seek(ctx, 0, SEEK_SET);
2782 if (err)
2783 return err;
2784
2785 buf = mmap(0, VHD_BLOCK_SIZE, PROT_READ,
2786 MAP_SHARED | MAP_ANON, -1, 0);
2787 if (buf == MAP_FAILED)
2788 return -errno;
2789
2790 for (i = 0; i < ctx->footer.curr_size >> VHD_BLOCK_SHIFT; i++) {
2791 err = vhd_write(ctx, buf, VHD_BLOCK_SIZE);
2792 if (err)
2793 goto out;
2794 }
2795
2796 err = 0;
2797
2798 out:
2799 munmap(buf, VHD_BLOCK_SIZE);
2800 return err;
2801 }
2802
2803 int
vhd_get_phys_size(vhd_context_t * ctx,off_t * size)2804 vhd_get_phys_size(vhd_context_t *ctx, off_t *size)
2805 {
2806 int err;
2807
2808 if ((err = vhd_end_of_data(ctx, size)))
2809 return err;
2810 *size += sizeof(vhd_footer_t);
2811 return 0;
2812 }
2813
2814 int
vhd_set_phys_size(vhd_context_t * ctx,off_t size)2815 vhd_set_phys_size(vhd_context_t *ctx, off_t size)
2816 {
2817 off_t phys_size;
2818 int err;
2819
2820 err = vhd_get_phys_size(ctx, &phys_size);
2821 if (err)
2822 return err;
2823 if (size < phys_size) {
2824 // would result in data loss
2825 VHDLOG("ERROR: new size (%"PRIu64") < phys size (%"PRIu64")\n",
2826 size, phys_size);
2827 return -EINVAL;
2828 }
2829 return vhd_write_footer_at(ctx, &ctx->footer,
2830 size - sizeof(vhd_footer_t));
2831 }
2832
2833 static int
__vhd_create(const char * name,const char * parent,uint64_t bytes,int type,vhd_flag_creat_t flags)2834 __vhd_create(const char *name, const char *parent, uint64_t bytes, int type,
2835 vhd_flag_creat_t flags)
2836 {
2837 int err;
2838 off_t off;
2839 vhd_context_t ctx;
2840 vhd_footer_t *footer;
2841 vhd_header_t *header;
2842 uint64_t size, blks;
2843
2844 switch (type) {
2845 case HD_TYPE_DIFF:
2846 if (!parent)
2847 return -EINVAL;
2848 case HD_TYPE_FIXED:
2849 case HD_TYPE_DYNAMIC:
2850 break;
2851 default:
2852 return -EINVAL;
2853 }
2854
2855 if (strnlen(name, VHD_MAX_NAME_LEN - 1) == VHD_MAX_NAME_LEN - 1)
2856 return -ENAMETOOLONG;
2857
2858 memset(&ctx, 0, sizeof(vhd_context_t));
2859 footer = &ctx.footer;
2860 header = &ctx.header;
2861 blks = (bytes + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
2862 size = blks << VHD_BLOCK_SHIFT;
2863
2864 ctx.fd = open(name, O_WRONLY | O_CREAT |
2865 O_TRUNC | O_LARGEFILE | O_DIRECT, 0644);
2866 if (ctx.fd == -1)
2867 return -errno;
2868
2869 ctx.file = strdup(name);
2870 if (!ctx.file) {
2871 err = -ENOMEM;
2872 goto out;
2873 }
2874
2875 err = vhd_test_file_fixed(ctx.file, &ctx.is_block);
2876 if (err)
2877 goto out;
2878
2879 vhd_initialize_footer(&ctx, type, size);
2880
2881 if (type == HD_TYPE_FIXED) {
2882 err = vhd_initialize_fixed_disk(&ctx);
2883 if (err)
2884 goto out;
2885 } else {
2886 int raw = vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW);
2887 err = vhd_initialize_header(&ctx, parent, size, raw);
2888 if (err)
2889 goto out;
2890
2891 err = vhd_write_footer_at(&ctx, &ctx.footer, 0);
2892 if (err)
2893 goto out;
2894
2895 err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
2896 if (err)
2897 goto out;
2898
2899 err = vhd_create_batmap(&ctx);
2900 if (err)
2901 goto out;
2902
2903 err = vhd_create_bat(&ctx);
2904 if (err)
2905 goto out;
2906
2907 if (type == HD_TYPE_DIFF) {
2908 err = vhd_write_parent_locators(&ctx, parent);
2909 if (err)
2910 goto out;
2911 }
2912
2913 /* write header again since it may have changed */
2914 err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
2915 if (err)
2916 goto out;
2917 }
2918
2919 err = vhd_seek(&ctx, 0, SEEK_END);
2920 if (err)
2921 goto out;
2922
2923 off = vhd_position(&ctx);
2924 if (off == (off_t)-1) {
2925 err = -errno;
2926 goto out;
2927 }
2928
2929 if (ctx.is_block)
2930 off -= sizeof(vhd_footer_t);
2931
2932 err = vhd_write_footer_at(&ctx, &ctx.footer, off);
2933 if (err)
2934 goto out;
2935
2936 err = 0;
2937
2938 out:
2939 vhd_close(&ctx);
2940 if (err && !ctx.is_block)
2941 unlink(name);
2942 return err;
2943 }
2944
2945 int
vhd_create(const char * name,uint64_t bytes,int type,vhd_flag_creat_t flags)2946 vhd_create(const char *name, uint64_t bytes, int type, vhd_flag_creat_t flags)
2947 {
2948 return __vhd_create(name, NULL, bytes, type, flags);
2949 }
2950
2951 int
vhd_snapshot(const char * name,uint64_t bytes,const char * parent,vhd_flag_creat_t flags)2952 vhd_snapshot(const char *name, uint64_t bytes, const char *parent,
2953 vhd_flag_creat_t flags)
2954 {
2955 return __vhd_create(name, parent, bytes, HD_TYPE_DIFF, flags);
2956 }
2957
2958 static int
__vhd_io_fixed_read(vhd_context_t * ctx,char * buf,uint64_t sec,uint32_t secs)2959 __vhd_io_fixed_read(vhd_context_t *ctx,
2960 char *buf, uint64_t sec, uint32_t secs)
2961 {
2962 int err;
2963
2964 err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
2965 if (err)
2966 return err;
2967
2968 return vhd_read(ctx, buf, vhd_sectors_to_bytes(secs));
2969 }
2970
2971 static void
__vhd_io_dynamic_copy_data(vhd_context_t * ctx,char * map,int map_off,char * bitmap,int bitmap_off,char * dst,char * src,int secs)2972 __vhd_io_dynamic_copy_data(vhd_context_t *ctx,
2973 char *map, int map_off,
2974 char *bitmap, int bitmap_off,
2975 char *dst, char *src, int secs)
2976 {
2977 int i;
2978
2979 for (i = 0; i < secs; i++) {
2980 if (test_bit(map, map_off + i))
2981 goto next;
2982
2983 if (ctx && !vhd_bitmap_test(ctx, bitmap, bitmap_off + i))
2984 goto next;
2985
2986 memcpy(dst, src, VHD_SECTOR_SIZE);
2987 set_bit(map, map_off + i);
2988
2989 next:
2990 src += VHD_SECTOR_SIZE;
2991 dst += VHD_SECTOR_SIZE;
2992 }
2993 }
2994
2995 static int
__vhd_io_dynamic_read_link(vhd_context_t * ctx,char * map,char * buf,uint64_t sector,uint32_t secs)2996 __vhd_io_dynamic_read_link(vhd_context_t *ctx, char *map,
2997 char *buf, uint64_t sector, uint32_t secs)
2998 {
2999 off_t off;
3000 uint32_t blk, sec;
3001 int err, cnt, map_off;
3002 char *bitmap, *data, *src;
3003
3004 map_off = 0;
3005
3006 do {
3007 blk = sector / ctx->spb;
3008 sec = sector % ctx->spb;
3009 off = ctx->bat.bat[blk];
3010 data = NULL;
3011 bitmap = NULL;
3012
3013 if (off == DD_BLK_UNUSED) {
3014 cnt = MIN(secs, ctx->spb);
3015 goto next;
3016 }
3017
3018 err = vhd_read_bitmap(ctx, blk, &bitmap);
3019 if (err)
3020 return err;
3021
3022 err = vhd_read_block(ctx, blk, &data);
3023 if (err) {
3024 free(bitmap);
3025 return err;
3026 }
3027
3028 cnt = MIN(secs, ctx->spb - sec);
3029 src = data + vhd_sectors_to_bytes(sec);
3030
3031 __vhd_io_dynamic_copy_data(ctx,
3032 map, map_off,
3033 bitmap, sec,
3034 buf, src, cnt);
3035
3036 next:
3037 free(data);
3038 free(bitmap);
3039
3040 secs -= cnt;
3041 sector += cnt;
3042 map_off += cnt;
3043 buf += vhd_sectors_to_bytes(cnt);
3044
3045 } while (secs);
3046
3047 return 0;
3048 }
3049
3050 static int
__raw_read_link(char * filename,char * map,char * buf,uint64_t sec,uint32_t secs)3051 __raw_read_link(char *filename,
3052 char *map, char *buf, uint64_t sec, uint32_t secs)
3053 {
3054 int fd, err;
3055 off_t off;
3056 uint64_t size;
3057 char *data;
3058
3059 err = 0;
3060 errno = 0;
3061 fd = open(filename, O_RDONLY | O_DIRECT | O_LARGEFILE);
3062 if (fd == -1) {
3063 VHDLOG("%s: failed to open: %d\n", filename, -errno);
3064 return -errno;
3065 }
3066
3067 off = lseek(fd, vhd_sectors_to_bytes(sec), SEEK_SET);
3068 if (off == (off_t)-1) {
3069 VHDLOG("%s: seek(0x%08"PRIx64") failed: %d\n",
3070 filename, vhd_sectors_to_bytes(sec), -errno);
3071 err = -errno;
3072 goto close;
3073 }
3074
3075 size = vhd_sectors_to_bytes(secs);
3076 err = posix_memalign((void **)&data, VHD_SECTOR_SIZE, size);
3077 if (err)
3078 goto close;
3079
3080 err = read(fd, data, size);
3081 if (err != size) {
3082 VHDLOG("%s: reading of %"PRIu64" returned %d, errno: %d\n",
3083 filename, size, err, -errno);
3084 free(data);
3085 err = errno ? -errno : -EIO;
3086 goto close;
3087 }
3088 __vhd_io_dynamic_copy_data(NULL, map, 0, NULL, 0, buf, data, secs);
3089 free(data);
3090 err = 0;
3091
3092 close:
3093 close(fd);
3094 return err;
3095 }
3096
3097 static int
__vhd_io_dynamic_read(vhd_context_t * ctx,char * buf,uint64_t sec,uint32_t secs)3098 __vhd_io_dynamic_read(vhd_context_t *ctx,
3099 char *buf, uint64_t sec, uint32_t secs)
3100 {
3101 int err;
3102 uint32_t i, done;
3103 char *map, *next;
3104 vhd_context_t parent, *vhd;
3105
3106 err = vhd_get_bat(ctx);
3107 if (err)
3108 return err;
3109
3110 vhd = ctx;
3111 next = NULL;
3112 map = calloc(1, secs << (VHD_SECTOR_SHIFT - 3));
3113 if (!map)
3114 return -ENOMEM;
3115
3116 memset(buf, 0, vhd_sectors_to_bytes(secs));
3117
3118 for (;;) {
3119 err = __vhd_io_dynamic_read_link(vhd, map, buf, sec, secs);
3120 if (err)
3121 goto close;
3122
3123 for (done = 0, i = 0; i < secs; i++)
3124 if (test_bit(map, i))
3125 done++;
3126
3127 if (done == secs) {
3128 err = 0;
3129 goto close;
3130 }
3131
3132 if (vhd->footer.type == HD_TYPE_DIFF) {
3133 err = vhd_parent_locator_get(vhd, &next);
3134 if (err)
3135 goto close;
3136 if (vhd_parent_raw(vhd)) {
3137 err = __raw_read_link(next, map, buf, sec,
3138 secs);
3139 goto close;
3140 }
3141 } else {
3142 err = 0;
3143 goto close;
3144 }
3145
3146 if (vhd != ctx)
3147 vhd_close(vhd);
3148 vhd = &parent;
3149
3150 err = vhd_open(vhd, next, VHD_OPEN_RDONLY);
3151 if (err)
3152 goto out;
3153
3154 err = vhd_get_bat(vhd);
3155 if (err)
3156 goto close;
3157
3158 free(next);
3159 next = NULL;
3160 }
3161
3162 close:
3163 if (vhd != ctx)
3164 vhd_close(vhd);
3165 out:
3166 free(map);
3167 free(next);
3168 return err;
3169 }
3170
3171 int
vhd_io_read(vhd_context_t * ctx,char * buf,uint64_t sec,uint32_t secs)3172 vhd_io_read(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
3173 {
3174 if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
3175 return -ERANGE;
3176
3177 if (!vhd_type_dynamic(ctx))
3178 return __vhd_io_fixed_read(ctx, buf, sec, secs);
3179
3180 return __vhd_io_dynamic_read(ctx, buf, sec, secs);
3181 }
3182
3183 static int
__vhd_io_fixed_write(vhd_context_t * ctx,char * buf,uint64_t sec,uint32_t secs)3184 __vhd_io_fixed_write(vhd_context_t *ctx,
3185 char *buf, uint64_t sec, uint32_t secs)
3186 {
3187 int err;
3188
3189 err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
3190 if (err)
3191 return err;
3192
3193 return vhd_write(ctx, buf, vhd_sectors_to_bytes(secs));
3194 }
3195
3196 static int
__vhd_io_allocate_block(vhd_context_t * ctx,uint32_t block)3197 __vhd_io_allocate_block(vhd_context_t *ctx, uint32_t block)
3198 {
3199 char *buf;
3200 size_t size;
3201 off_t off, max;
3202 int i, err, gap, spp;
3203
3204 spp = getpagesize() >> VHD_SECTOR_SHIFT;
3205
3206 err = vhd_end_of_data(ctx, &max);
3207 if (err)
3208 return err;
3209
3210 gap = 0;
3211 off = max;
3212 max >>= VHD_SECTOR_SHIFT;
3213
3214 /* data region of segment should begin on page boundary */
3215 if ((max + ctx->bm_secs) % spp) {
3216 gap = (spp - ((max + ctx->bm_secs) % spp));
3217 max += gap;
3218 }
3219
3220 err = vhd_seek(ctx, off, SEEK_SET);
3221 if (err)
3222 return err;
3223
3224 size = vhd_sectors_to_bytes(ctx->spb + ctx->bm_secs + gap);
3225 buf = mmap(0, size, PROT_READ, MAP_SHARED | MAP_ANON, -1, 0);
3226 if (buf == MAP_FAILED)
3227 return -errno;
3228
3229 err = vhd_write(ctx, buf, size);
3230 if (err)
3231 goto out;
3232
3233 ctx->bat.bat[block] = max;
3234 err = vhd_write_bat(ctx, &ctx->bat);
3235 if (err)
3236 goto out;
3237
3238 err = 0;
3239
3240 out:
3241 munmap(buf, size);
3242 return err;
3243 }
3244
3245 static int
__vhd_io_dynamic_write(vhd_context_t * ctx,char * buf,uint64_t sector,uint32_t secs)3246 __vhd_io_dynamic_write(vhd_context_t *ctx,
3247 char *buf, uint64_t sector, uint32_t secs)
3248 {
3249 char *map;
3250 off_t off;
3251 uint32_t blk, sec;
3252 int i, err, cnt, ret;
3253
3254 if (vhd_sectors_to_bytes(sector + secs) > ctx->footer.curr_size)
3255 return -ERANGE;
3256
3257 err = vhd_get_bat(ctx);
3258 if (err)
3259 return err;
3260
3261 if (vhd_has_batmap(ctx)) {
3262 err = vhd_get_batmap(ctx);
3263 if (err)
3264 return err;
3265 }
3266
3267 do {
3268 blk = sector / ctx->spb;
3269 sec = sector % ctx->spb;
3270
3271 off = ctx->bat.bat[blk];
3272 if (off == DD_BLK_UNUSED) {
3273 err = __vhd_io_allocate_block(ctx, blk);
3274 if (err)
3275 return err;
3276
3277 off = ctx->bat.bat[blk];
3278 }
3279
3280 off += ctx->bm_secs + sec;
3281 err = vhd_seek(ctx, vhd_sectors_to_bytes(off), SEEK_SET);
3282 if (err)
3283 return err;
3284
3285 cnt = MIN(secs, ctx->spb - sec);
3286 err = vhd_write(ctx, buf, vhd_sectors_to_bytes(cnt));
3287 if (err)
3288 return err;
3289
3290 if (vhd_has_batmap(ctx) &&
3291 vhd_batmap_test(ctx, &ctx->batmap, blk))
3292 goto next;
3293
3294 err = vhd_read_bitmap(ctx, blk, &map);
3295 if (err)
3296 return err;
3297
3298 for (i = 0; i < cnt; i++)
3299 vhd_bitmap_set(ctx, map, sec + i);
3300
3301 err = vhd_write_bitmap(ctx, blk, map);
3302 if (err)
3303 goto fail;
3304
3305 if (vhd_has_batmap(ctx)) {
3306 for (i = 0; i < ctx->spb; i++)
3307 if (!vhd_bitmap_test(ctx, map, i)) {
3308 free(map);
3309 goto next;
3310 }
3311
3312 vhd_batmap_set(ctx, &ctx->batmap, blk);
3313 err = vhd_write_batmap(ctx, &ctx->batmap);
3314 if (err)
3315 goto fail;
3316 }
3317
3318 free(map);
3319 map = NULL;
3320
3321 next:
3322 secs -= cnt;
3323 sector += cnt;
3324 buf += vhd_sectors_to_bytes(cnt);
3325 } while (secs);
3326
3327 err = 0;
3328
3329 out:
3330 ret = vhd_write_footer(ctx, &ctx->footer);
3331 return (err ? err : ret);
3332
3333 fail:
3334 free(map);
3335 goto out;
3336 }
3337
3338 int
vhd_io_write(vhd_context_t * ctx,char * buf,uint64_t sec,uint32_t secs)3339 vhd_io_write(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
3340 {
3341 if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
3342 return -ERANGE;
3343
3344 if (!vhd_type_dynamic(ctx))
3345 return __vhd_io_fixed_write(ctx, buf, sec, secs);
3346
3347 return __vhd_io_dynamic_write(ctx, buf, sec, secs);
3348 }
3349