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, &copy, 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