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 #include <time.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <libgen.h>
34 #include <inttypes.h>
35 #include <sys/stat.h>
36 
37 #include "libvhd.h"
38 #include "vhd-util.h"
39 
40 // allow the VHD timestamp to be at most this many seconds into the future to
41 // account for time skew with NFS servers
42 #define TIMESTAMP_MAX_SLACK 1800
43 
44 static int
vhd_util_check_zeros(void * buf,size_t size)45 vhd_util_check_zeros(void *buf, size_t size)
46 {
47 	int i;
48 	char *p;
49 
50 	p = buf;
51 	for (i = 0; i < size; i++)
52 		if (p[i])
53 			return i;
54 
55 	return 0;
56 }
57 
58 static int
vhd_util_check_footer_opened(vhd_footer_t * footer)59 vhd_util_check_footer_opened(vhd_footer_t *footer)
60 {
61 	int i, n;
62 	uint32_t *buf;
63 
64 	buf = (uint32_t *)footer;
65 	n = sizeof(*footer) / sizeof(uint32_t);
66 
67 	for (i = 0; i < n; i++)
68 		if (buf[i] != 0xc7c7c7c7)
69 			return 0;
70 
71 	return 1;
72 }
73 
74 static char *
vhd_util_check_validate_footer(vhd_footer_t * footer)75 vhd_util_check_validate_footer(vhd_footer_t *footer)
76 {
77 	int size;
78 	uint32_t checksum, now;
79 
80 	size = sizeof(footer->cookie);
81 	if (memcmp(footer->cookie, HD_COOKIE, size))
82 		return "invalid cookie";
83 
84 	checksum = vhd_checksum_footer(footer);
85 	if (checksum != footer->checksum) {
86 		if (footer->hidden &&
87 		    !strncmp(footer->crtr_app, "tap", 3) &&
88 		    (footer->crtr_ver == VHD_VERSION(0, 1) ||
89 		     footer->crtr_ver == VHD_VERSION(1, 1))) {
90 			char tmp = footer->hidden;
91 			footer->hidden = 0;
92 			checksum = vhd_checksum_footer(footer);
93 			footer->hidden = tmp;
94 
95 			if (checksum == footer->checksum)
96 				goto ok;
97 		}
98 
99 		return "invalid checksum";
100 	}
101 
102 ok:
103 	if (!(footer->features & HD_RESERVED))
104 		return "invalid 'reserved' feature";
105 
106 	if (footer->features & ~(HD_TEMPORARY | HD_RESERVED))
107 		return "invalid extra features";
108 
109 	if (footer->ff_version != HD_FF_VERSION)
110 		return "invalid file format version";
111 
112 	if (footer->type != HD_TYPE_DYNAMIC &&
113 	    footer->type != HD_TYPE_DIFF    &&
114 	    footer->data_offset != ~(0ULL))
115 		return "invalid data offset";
116 
117 	now = vhd_time(time(NULL));
118 	if (footer->timestamp > now + TIMESTAMP_MAX_SLACK)
119 		return "creation time in future";
120 
121 	if (!strncmp(footer->crtr_app, "tap", 3) &&
122 	    footer->crtr_ver > VHD_CURRENT_VERSION)
123 		return "unsupported tap creator version";
124 
125 	if (vhd_chs(footer->curr_size) < footer->geometry)
126 		return "geometry too large";
127 
128 	if (footer->type != HD_TYPE_FIXED   &&
129 	    footer->type != HD_TYPE_DYNAMIC &&
130 	    footer->type != HD_TYPE_DIFF)
131 		return "invalid type";
132 
133 	if (footer->saved && footer->saved != 1)
134 		return "invalid 'saved' state";
135 
136 	if (footer->hidden && footer->hidden != 1)
137 		return "invalid 'hidden' state";
138 
139 	if (vhd_util_check_zeros(footer->reserved,
140 				 sizeof(footer->reserved)))
141 		return "invalid 'reserved' bits";
142 
143 	return NULL;
144 }
145 
146 static char *
vhd_util_check_validate_header(int fd,vhd_header_t * header)147 vhd_util_check_validate_header(int fd, vhd_header_t *header)
148 {
149 	off_t eof;
150 	int i, cnt, size;
151 	uint32_t checksum;
152 
153 	size = sizeof(header->cookie);
154 	if (memcmp(header->cookie, DD_COOKIE, size))
155 		return "invalid cookie";
156 
157 	checksum = vhd_checksum_header(header);
158 	if (checksum != header->checksum)
159 		return "invalid checksum";
160 
161 	if (header->hdr_ver != 0x00010000)
162 		return "invalid header version";
163 
164 	if (header->data_offset != ~(0ULL))
165 		return "invalid data offset";
166 
167 	eof = lseek(fd, 0, SEEK_END);
168 	if (eof == (off_t)-1)
169 		return "error finding eof";
170 
171 	if (header->table_offset <= 0  ||
172 	    header->table_offset % 512 ||
173 	    (header->table_offset +
174 	     (header->max_bat_size * sizeof(uint32_t)) >
175 	     eof - sizeof(vhd_footer_t)))
176 		return "invalid table offset";
177 
178 	for (cnt = 0, i = 0; i < sizeof(header->block_size) * 8; i++)
179 		if ((header->block_size >> i) & 1)
180 			cnt++;
181 
182 	if (cnt != 1)
183 		return "invalid block size";
184 
185 	if (header->res1)
186 		return "invalid reserved bits";
187 
188 	if (vhd_util_check_zeros(header->res2, sizeof(header->res2)))
189 		return "invalid reserved bits";
190 
191 	return NULL;
192 }
193 
194 static char *
vhd_util_check_validate_differencing_header(vhd_context_t * vhd)195 vhd_util_check_validate_differencing_header(vhd_context_t *vhd)
196 {
197 	vhd_header_t *header;
198 
199 	header = &vhd->header;
200 
201 	if (vhd->footer.type == HD_TYPE_DIFF) {
202 		char *parent;
203 		uint32_t now;
204 
205 		now = vhd_time(time(NULL));
206 		if (header->prt_ts > now + TIMESTAMP_MAX_SLACK)
207 			return "parent creation time in future";
208 
209 		if (vhd_header_decode_parent(vhd, header, &parent))
210 			return "invalid parent name";
211 
212 		free(parent);
213 	} else {
214 		if (vhd_util_check_zeros(header->prt_name,
215 					 sizeof(header->prt_name)))
216 			return "invalid non-null parent name";
217 
218 		if (vhd_util_check_zeros(header->loc, sizeof(header->loc)))
219 			return "invalid non-null parent locators";
220 
221 		if (!vhd_uuid_is_nil(&header->prt_uuid))
222 			return "invalid non-null parent uuid";
223 
224 		if (header->prt_ts)
225 			return "invalid non-zero parent timestamp";
226 	}
227 
228 	return NULL;
229 }
230 
231 static char *
vhd_util_check_validate_batmap(vhd_context_t * vhd,vhd_batmap_t * batmap)232 vhd_util_check_validate_batmap(vhd_context_t *vhd, vhd_batmap_t *batmap)
233 {
234 	int size;
235 	off_t eof;
236 	uint32_t checksum;
237 
238 	size = sizeof(batmap->header.cookie);
239 	if (memcmp(batmap->header.cookie, VHD_BATMAP_COOKIE, size))
240 		return "invalid cookie";
241 
242 	if (batmap->header.batmap_version > VHD_BATMAP_CURRENT_VERSION)
243 		return "unsupported batmap version";
244 
245 	checksum = vhd_checksum_batmap(batmap);
246 	if (checksum != batmap->header.checksum)
247 		return "invalid checksum";
248 
249 	if (!batmap->header.batmap_size)
250 		return "invalid size zero";
251 
252 	eof = lseek(vhd->fd, 0, SEEK_END);
253 	if (eof == (off_t)-1)
254 		return "error finding eof";
255 
256 	if (!batmap->header.batmap_offset ||
257 	    batmap->header.batmap_offset % 512)
258 		return "invalid batmap offset";
259 
260 	if ((batmap->header.batmap_offset +
261 	     vhd_sectors_to_bytes(batmap->header.batmap_size)) >
262 	    eof - sizeof(vhd_footer_t))
263 		return "invalid batmap size";
264 
265 	return NULL;
266 }
267 
268 static char *
vhd_util_check_validate_parent_locator(vhd_context_t * vhd,vhd_parent_locator_t * loc)269 vhd_util_check_validate_parent_locator(vhd_context_t *vhd,
270 				       vhd_parent_locator_t *loc)
271 {
272 	off_t eof;
273 
274 	if (vhd_validate_platform_code(loc->code))
275 		return "invalid platform code";
276 
277 	if (loc->code == PLAT_CODE_NONE) {
278 		if (vhd_util_check_zeros(loc, sizeof(*loc)))
279 			return "non-zero locator";
280 
281 		return NULL;
282 	}
283 
284 	if (!loc->data_offset)
285 		return "invalid data offset";
286 
287 	if (!loc->data_space)
288 		return "invalid data space";
289 
290 	if (!loc->data_len)
291 		return "invalid data length";
292 
293 	eof = lseek(vhd->fd, 0, SEEK_END);
294 	if (eof == (off_t)-1)
295 		return "error finding eof";
296 
297 	if (loc->data_offset + vhd_parent_locator_size(loc) >
298 	    eof - sizeof(vhd_footer_t))
299 		return "invalid size";
300 
301 	if (loc->res)
302 		return "invalid reserved bits";
303 
304 	return NULL;
305 }
306 
307 static const char *
vhd_util_check_validate_parent(vhd_context_t * vhd,const char * ppath)308 vhd_util_check_validate_parent(vhd_context_t *vhd, const char *ppath)
309 {
310 	const char *msg;
311 	vhd_context_t parent;
312 	uint32_t status;
313 
314 	msg = NULL;
315 
316 	if (vhd_parent_raw(vhd))
317 		return msg;
318 
319 	if (vhd_open(&parent, ppath,
320 				VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED))
321 		return "error opening parent";
322 
323 	if (vhd_uuid_compare(&vhd->header.prt_uuid, &parent.footer.uuid)) {
324 		msg = "invalid parent uuid";
325 		goto out;
326 	}
327 
328 out:
329 	vhd_close(&parent);
330 	return msg;
331 }
332 
333 static int
vhd_util_check_footer(int fd,vhd_footer_t * footer,int ignore)334 vhd_util_check_footer(int fd, vhd_footer_t *footer, int ignore)
335 {
336 	size_t size;
337 	int err, opened;
338 	char *msg, *buf = NULL;
339 	off_t eof, off;
340 	vhd_footer_t primary, backup;
341 
342 	memset(&primary, 0, sizeof(primary));
343 	memset(&backup, 0, sizeof(backup));
344 
345 	err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, sizeof(primary));
346 	if (err) {
347 		printf("error allocating buffer: %d\n", err);
348 		return -err;
349 	}
350 
351 	memset(buf, 0, sizeof(primary));
352 
353 	eof = lseek(fd, 0, SEEK_END);
354 	if (eof == (off_t)-1) {
355 		err = -errno;
356 		printf("error calculating end of file: %d\n", err);
357 		goto out;
358 	}
359 
360 	size = ((eof % 512) ? 511 : 512);
361 	eof  = lseek(fd, eof - size, SEEK_SET);
362 	if (eof == (off_t)-1) {
363 		err = -errno;
364 		printf("error calculating end of file: %d\n", err);
365 		goto out;
366 	}
367 
368 	err = read(fd, buf, 512);
369 	if (err != size) {
370 		err = (errno ? -errno : -EIO);
371 		printf("error reading primary footer: %d\n", err);
372 		goto out;
373 	}
374 
375 	memcpy(&primary, buf, sizeof(primary));
376 	opened = vhd_util_check_footer_opened(&primary);
377 	vhd_footer_in(&primary);
378 
379 	msg = vhd_util_check_validate_footer(&primary);
380 	if (msg) {
381 		if (opened && ignore)
382 			goto check_backup;
383 
384 		err = -EINVAL;
385 		printf("primary footer invalid: %s\n", msg);
386 		goto out;
387 	}
388 
389 	if (primary.type == HD_TYPE_FIXED) {
390 		err = 0;
391 		goto out;
392 	}
393 
394 check_backup:
395 	off = lseek(fd, 0, SEEK_SET);
396 	if (off == (off_t)-1) {
397 		err = -errno;
398 		printf("error seeking to backup footer: %d\n", err);
399 		goto out;
400 	}
401 
402 	size = 512;
403 	memset(buf, 0, sizeof(primary));
404 
405 	err = read(fd, buf, size);
406 	if (err != size) {
407 		err = (errno ? -errno : -EIO);
408 		printf("error reading backup footer: %d\n", err);
409 		goto out;
410 	}
411 
412 	memcpy(&backup, buf, sizeof(backup));
413 	vhd_footer_in(&backup);
414 
415 	msg = vhd_util_check_validate_footer(&backup);
416 	if (msg) {
417 		err = -EINVAL;
418 		printf("backup footer invalid: %s\n", msg);
419 		goto out;
420 	}
421 
422 	if (memcmp(&primary, &backup, sizeof(primary))) {
423 		if (opened && ignore) {
424 			memcpy(&primary, &backup, sizeof(primary));
425 			goto ok;
426 		}
427 
428 		if (backup.hidden &&
429 		    !strncmp(backup.crtr_app, "tap", 3) &&
430 		    (backup.crtr_ver == VHD_VERSION(0, 1) ||
431 		     backup.crtr_ver == VHD_VERSION(1, 1))) {
432 			char cmp, tmp = backup.hidden;
433 			backup.hidden = 0;
434 			cmp = memcmp(&primary, &backup, sizeof(primary));
435 			backup.hidden = tmp;
436 			if (!cmp)
437 				goto ok;
438 		}
439 
440 		err = -EINVAL;
441 		printf("primary and backup footers do not match\n");
442 		goto out;
443 	}
444 
445 ok:
446 	err = 0;
447 	memcpy(footer, &primary, sizeof(primary));
448 
449 out:
450 	free(buf);
451 	return err;
452 }
453 
454 static int
vhd_util_check_header(int fd,vhd_footer_t * footer)455 vhd_util_check_header(int fd, vhd_footer_t *footer)
456 {
457 	int err;
458 	off_t off;
459 	char *msg, *buf;
460 	vhd_header_t header;
461 
462 	err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, sizeof(header));
463 	if (err) {
464 		printf("error allocating header: %d\n", err);
465 		return err;
466 	}
467 
468 	off = footer->data_offset;
469 	off = lseek(fd, off, SEEK_SET);
470 	if (off == (off_t)-1) {
471 		err = -errno;
472 		printf("error seeking to header: %d\n", err);
473 		goto out;
474 	}
475 
476 	err = read(fd, buf, sizeof(header));
477 	if (err != sizeof(header)) {
478 		err = (errno ? -errno : -EIO);
479 		printf("error reading header: %d\n", err);
480 		goto out;
481 	}
482 
483 	memcpy(&header, buf, sizeof(header));
484 	vhd_header_in(&header);
485 
486 	msg = vhd_util_check_validate_header(fd, &header);
487 	if (msg) {
488 		err = -EINVAL;
489 		printf("header is invalid: %s\n", msg);
490 		goto out;
491 	}
492 
493 	err = 0;
494 
495 out:
496 	free(buf);
497 	return err;
498 }
499 
500 static int
vhd_util_check_differencing_header(vhd_context_t * vhd)501 vhd_util_check_differencing_header(vhd_context_t *vhd)
502 {
503 	char *msg;
504 
505 	msg = vhd_util_check_validate_differencing_header(vhd);
506 	if (msg) {
507 		printf("differencing header is invalid: %s\n", msg);
508 		return -EINVAL;
509 	}
510 
511 	return 0;
512 }
513 
514 static int
vhd_util_check_bat(vhd_context_t * vhd)515 vhd_util_check_bat(vhd_context_t *vhd)
516 {
517 	off_t eof, eoh;
518 	int i, j, err, block_size;
519 
520 	err = vhd_seek(vhd, 0, SEEK_END);
521 	if (err) {
522 		printf("error calculating eof: %d\n", err);
523 		return err;
524 	}
525 
526 	eof = vhd_position(vhd);
527 	if (eof == (off_t)-1) {
528 		printf("error calculating eof: %d\n", -errno);
529 		return -errno;
530 	}
531 
532 	/* adjust eof for vhds with short footers */
533 	if (eof % 512) {
534 		if (eof % 512 != 511) {
535 			printf("invalid file size: 0x%"PRIx64"\n", eof);
536 			return -EINVAL;
537 		}
538 
539 		eof++;
540 	}
541 
542 	err = vhd_get_bat(vhd);
543 	if (err) {
544 		printf("error reading bat: %d\n", err);
545 		return err;
546 	}
547 
548 	err = vhd_end_of_headers(vhd, &eoh);
549 	if (err) {
550 		printf("error calculating end of metadata: %d\n", err);
551 		return err;
552 	}
553 
554 	eof  -= sizeof(vhd_footer_t);
555 	eof >>= VHD_SECTOR_SHIFT;
556 	eoh >>= VHD_SECTOR_SHIFT;
557 	block_size = vhd->spb + vhd->bm_secs;
558 
559 	for (i = 0; i < vhd->header.max_bat_size; i++) {
560 		uint32_t off = vhd->bat.bat[i];
561 		if (off == DD_BLK_UNUSED)
562 			continue;
563 
564 		if (off < eoh) {
565 			printf("block %d (offset 0x%x) clobbers headers\n",
566 			       i, off);
567 			return -EINVAL;
568 		}
569 
570 		if (off + block_size > eof) {
571 			printf("block %d (offset 0x%x) clobbers footer\n",
572 			       i, off);
573 			return -EINVAL;
574 		}
575 
576 		for (j = 0; j < vhd->header.max_bat_size; j++) {
577 			uint32_t joff = vhd->bat.bat[j];
578 
579 			if (i == j)
580 				continue;
581 
582 			if (joff == DD_BLK_UNUSED)
583 				continue;
584 
585 			if (off == joff)
586 				err = -EINVAL;
587 
588 			if (off > joff && off < joff + block_size)
589 				err = -EINVAL;
590 
591 			if (off + block_size > joff &&
592 			    off + block_size < joff + block_size)
593 				err = -EINVAL;
594 
595 			if (err) {
596 				printf("block %d (offset 0x%x) clobbers "
597 				       "block %d (offset 0x%x)\n",
598 				       i, off, j, joff);
599 				return err;
600 			}
601 		}
602 	}
603 
604 	return 0;
605 }
606 
607 static int
vhd_util_check_batmap(vhd_context_t * vhd)608 vhd_util_check_batmap(vhd_context_t *vhd)
609 {
610 	char *msg;
611 	int i, err;
612 
613 	err = vhd_get_bat(vhd);
614 	if (err) {
615 		printf("error reading bat: %d\n", err);
616 		return err;
617 	}
618 
619 	err = vhd_get_batmap(vhd);
620 	if (err) {
621 		printf("error reading batmap: %d\n", err);
622 		return err;
623 	}
624 
625 	msg = vhd_util_check_validate_batmap(vhd, &vhd->batmap);
626 	if (msg) {
627 		printf("batmap is invalid: %s\n", msg);
628 		return -EINVAL;
629 	}
630 
631 	for (i = 0; i < vhd->header.max_bat_size; i++) {
632 		if (!vhd_batmap_test(vhd, &vhd->batmap, i))
633 			continue;
634 
635 		if (vhd->bat.bat[i] == DD_BLK_UNUSED) {
636 			printf("batmap shows unallocated block %d full\n", i);
637 			return -EINVAL;
638 		}
639 	}
640 
641 	return 0;
642 }
643 
644 static int
vhd_util_check_parent_locators(vhd_context_t * vhd)645 vhd_util_check_parent_locators(vhd_context_t *vhd)
646 {
647 	int i, n, err;
648 	vhd_parent_locator_t *loc;
649 	char *file, *ppath, *location, *pname;
650 	const char *msg;
651 	int mac, macx, w2ku, w2ru, wi2r, wi2k, found;
652 
653 	mac      = 0;
654 	macx     = 0;
655 	w2ku     = 0;
656 	w2ru     = 0;
657 	wi2r     = 0;
658 	wi2k     = 0;
659 	found    = 0;
660 	pname    = NULL;
661 	ppath    = NULL;
662 	location = NULL;
663 
664 	err = vhd_header_decode_parent(vhd, &vhd->header, &pname);
665 	if (err) {
666 		printf("error decoding parent name: %d\n", err);
667 		return err;
668 	}
669 
670 	n = sizeof(vhd->header.loc) / sizeof(vhd->header.loc[0]);
671 	for (i = 0; i < n; i++) {
672 		ppath    = NULL;
673 		location = NULL;
674 		loc = vhd->header.loc + i;
675 
676 		msg = vhd_util_check_validate_parent_locator(vhd, loc);
677 		if (msg) {
678 			err = -EINVAL;
679 			printf("invalid parent locator %d: %s\n", i, msg);
680 			goto out;
681 		}
682 
683 		if (loc->code == PLAT_CODE_NONE)
684 			continue;
685 
686 		switch (loc->code) {
687 		case PLAT_CODE_MACX:
688 			if (macx++)
689 				goto dup;
690 			break;
691 
692 		case PLAT_CODE_MAC:
693 			if (mac++)
694 				goto dup;
695 			break;
696 
697 		case PLAT_CODE_W2KU:
698 			if (w2ku++)
699 				goto dup;
700 			break;
701 
702 		case PLAT_CODE_W2RU:
703 			if (w2ru++)
704 				goto dup;
705 			break;
706 
707 		case PLAT_CODE_WI2R:
708 			if (wi2r++)
709 				goto dup;
710 			break;
711 
712 		case PLAT_CODE_WI2K:
713 			if (wi2k++)
714 				goto dup;
715 			break;
716 
717 		default:
718 			err = -EINVAL;
719 			printf("invalid  platform code for locator %d\n", i);
720 			goto out;
721 		}
722 
723 		if (loc->code != PLAT_CODE_MACX &&
724 		    loc->code != PLAT_CODE_W2RU &&
725 		    loc->code != PLAT_CODE_W2KU)
726 			continue;
727 
728 		err = vhd_parent_locator_read(vhd, loc, &ppath);
729 		if (err) {
730 			printf("error reading parent locator %d: %d\n", i, err);
731 			goto out;
732 		}
733 
734 		file = basename(ppath);
735 		if (strcmp(pname, file)) {
736 			err = -EINVAL;
737 			printf("parent locator %d name (%s) does not match "
738 			       "header name (%s)\n", i, file, pname);
739 			goto out;
740 		}
741 
742 		err = vhd_find_parent(vhd, ppath, &location);
743 		if (err) {
744 			printf("error resolving %s: %d\n", ppath, err);
745 			goto out;
746 		}
747 
748 		err = access(location, R_OK);
749 		if (err && loc->code == PLAT_CODE_MACX) {
750 			err = -errno;
751 			printf("parent locator %d points to missing file %s "
752 				"(resolved to %s)\n", i, ppath, location);
753 			goto out;
754 		}
755 
756 		msg = vhd_util_check_validate_parent(vhd, location);
757 		if (msg) {
758 			err = -EINVAL;
759 			printf("invalid parent %s: %s\n", location, msg);
760 			goto out;
761 		}
762 
763 		found++;
764 		free(ppath);
765 		free(location);
766 		ppath = NULL;
767 		location = NULL;
768 
769 		continue;
770 
771 	dup:
772 		printf("duplicate platform code in locator %d: 0x%x\n",
773 		       i, loc->code);
774 		err = -EINVAL;
775 		goto out;
776 	}
777 
778 	if (!found) {
779 		err = -EINVAL;
780 		printf("could not find parent %s\n", pname);
781 		goto out;
782 	}
783 
784 	err = 0;
785 
786 out:
787 	free(pname);
788 	free(ppath);
789 	free(location);
790 	return err;
791 }
792 
793 static void
vhd_util_dump_headers(const char * name)794 vhd_util_dump_headers(const char *name)
795 {
796 	char *argv[] = { "read", "-p", "-n", (char *)name };
797 	int argc = sizeof(argv) / sizeof(argv[0]);
798 
799 	printf("%s appears invalid; dumping metadata\n", name);
800 	vhd_util_read(argc, argv);
801 }
802 
803 static int
vhd_util_check_vhd(const char * name,int ignore)804 vhd_util_check_vhd(const char *name, int ignore)
805 {
806 	int fd, err;
807 	vhd_context_t vhd;
808 	struct stat stats;
809 	vhd_footer_t footer;
810 
811 	fd = -1;
812 	memset(&vhd, 0, sizeof(vhd));
813         memset(&footer, 0, sizeof(footer));
814 
815 	err = stat(name, &stats);
816 	if (err == -1) {
817 		printf("cannot stat %s: %d\n", name, errno);
818 		return -errno;
819 	}
820 
821 	if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
822 		printf("%s is not a regular file or block device\n", name);
823 		return -EINVAL;
824 	}
825 
826 	fd = open(name, O_RDONLY | O_DIRECT | O_LARGEFILE);
827 	if (fd == -1) {
828 		printf("error opening %s\n", name);
829 		return -errno;
830 	}
831 
832 	err = vhd_util_check_footer(fd, &footer, ignore);
833 	if (err)
834 		goto out;
835 
836 	if (footer.type != HD_TYPE_DYNAMIC && footer.type != HD_TYPE_DIFF)
837 		goto out;
838 
839 	err = vhd_util_check_header(fd, &footer);
840 	if (err)
841 		goto out;
842 
843 	err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
844 	if (err)
845 		goto out;
846 
847 	err = vhd_util_check_differencing_header(&vhd);
848 	if (err)
849 		goto out;
850 
851 	err = vhd_util_check_bat(&vhd);
852 	if (err)
853 		goto out;
854 
855 	if (vhd_has_batmap(&vhd)) {
856 		err = vhd_util_check_batmap(&vhd);
857 		if (err)
858 			goto out;
859 	}
860 
861 	if (vhd.footer.type == HD_TYPE_DIFF) {
862 		err = vhd_util_check_parent_locators(&vhd);
863 		if (err)
864 			goto out;
865 	}
866 
867 	err = 0;
868 	printf("%s is valid\n", name);
869 
870 out:
871 	if (err)
872 		vhd_util_dump_headers(name);
873 	if (fd != -1)
874 		close(fd);
875 	vhd_close(&vhd);
876 	return err;
877 }
878 
879 static int
vhd_util_check_parents(const char * name,int ignore)880 vhd_util_check_parents(const char *name, int ignore)
881 {
882 	int err;
883 	vhd_context_t vhd;
884 	char *cur, *parent;
885 
886 	cur = (char *)name;
887 
888 	for (;;) {
889 		err = vhd_open(&vhd, cur,
890 				VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
891 		if (err)
892 			goto out;
893 
894 		if (vhd.footer.type != HD_TYPE_DIFF || vhd_parent_raw(&vhd)) {
895 			vhd_close(&vhd);
896 			goto out;
897 		}
898 
899 		err = vhd_parent_locator_get(&vhd, &parent);
900 		vhd_close(&vhd);
901 
902 		if (err) {
903 			printf("error getting parent: %d\n", err);
904 			goto out;
905 		}
906 
907 		if (cur != name)
908 			free(cur);
909 		cur = parent;
910 
911 		err = vhd_util_check_vhd(cur, ignore);
912 		if (err)
913 			goto out;
914 	}
915 
916 out:
917 	if (err)
918 		printf("error checking parents: %d\n", err);
919 	if (cur != name)
920 		free(cur);
921 	return err;
922 }
923 
924 int
vhd_util_check(int argc,char ** argv)925 vhd_util_check(int argc, char **argv)
926 {
927 	char *name;
928 	vhd_context_t vhd;
929 	int c, err, ignore, parents;
930 
931 	if (!argc || !argv) {
932 		err = -EINVAL;
933 		goto usage;
934 	}
935 
936 	ignore  = 0;
937 	parents = 0;
938 	name    = NULL;
939 
940 	optind = 0;
941 	while ((c = getopt(argc, argv, "n:iph")) != -1) {
942 		switch (c) {
943 		case 'n':
944 			name = optarg;
945 			break;
946 		case 'i':
947 			ignore = 1;
948 			break;
949 		case 'p':
950 			parents = 1;
951 			break;
952 		case 'h':
953 			err = 0;
954 			goto usage;
955 		default:
956 			err = -EINVAL;
957 			goto usage;
958 		}
959 	}
960 
961 	if (!name || optind != argc) {
962 		err = -EINVAL;
963 		goto usage;
964 	}
965 
966 	err = vhd_util_check_vhd(name, ignore);
967 	if (err)
968 		goto out;
969 
970 	if (parents)
971 		err = vhd_util_check_parents(name, ignore);
972 
973 out:
974 	return err;
975 
976 usage:
977 	printf("options: -n <file> [-i ignore missing primary footers] "
978 	       "[-p check parents] [-h help]\n");
979 	return err;
980 }
981