ia64/xen-unstable

view tools/blktap2/vhd/lib/vhd-util-check.c @ 19817:b7f73a7f3078

blktap2: portability fixes for NetBSD

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

Signed-off-by: Christoph Egger <Christoph.Egger@amd.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Jun 23 17:24:14 2009 +0100 (2009-06-23)
parents 1c627434605e
children f1fec38c8228
line source
1 /* Copyright (c) 2008, XenSource Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 * * Neither the name of XenSource Inc. nor the names of its contributors
12 * may be used to endorse or promote products derived from this software
13 * without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
19 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 #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>
37 #include "libvhd.h"
38 #include "vhd-util.h"
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
44 static int
45 vhd_util_check_zeros(void *buf, size_t size)
46 {
47 int i;
48 char *p;
50 p = buf;
51 for (i = 0; i < size; i++)
52 if (p[i])
53 return i;
55 return 0;
56 }
58 static int
59 vhd_util_check_footer_opened(vhd_footer_t *footer)
60 {
61 int i, n;
62 uint32_t *buf;
64 buf = (uint32_t *)footer;
65 n = sizeof(*footer) / sizeof(uint32_t);
67 for (i = 0; i < n; i++)
68 if (buf[i] != 0xc7c7c7c7)
69 return 0;
71 return 1;
72 }
74 static char *
75 vhd_util_check_validate_footer(vhd_footer_t *footer)
76 {
77 int size;
78 uint32_t checksum, now;
80 size = sizeof(footer->cookie);
81 if (memcmp(footer->cookie, HD_COOKIE, size))
82 return "invalid cookie";
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;
95 if (checksum == footer->checksum)
96 goto ok;
97 }
99 return "invalid checksum";
100 }
102 ok:
103 if (!(footer->features & HD_RESERVED))
104 return "invalid 'reserved' feature";
106 if (footer->features & ~(HD_TEMPORARY | HD_RESERVED))
107 return "invalid extra features";
109 if (footer->ff_version != HD_FF_VERSION)
110 return "invalid file format version";
112 if (footer->type != HD_TYPE_DYNAMIC &&
113 footer->type != HD_TYPE_DIFF &&
114 footer->data_offset != ~(0ULL))
115 return "invalid data offset";
117 now = vhd_time(time(NULL));
118 if (footer->timestamp > now + TIMESTAMP_MAX_SLACK)
119 return "creation time in future";
121 if (!strncmp(footer->crtr_app, "tap", 3) &&
122 footer->crtr_ver > VHD_CURRENT_VERSION)
123 return "unsupported tap creator version";
125 if (vhd_chs(footer->curr_size) < footer->geometry)
126 return "geometry too large";
128 if (footer->type != HD_TYPE_FIXED &&
129 footer->type != HD_TYPE_DYNAMIC &&
130 footer->type != HD_TYPE_DIFF)
131 return "invalid type";
133 if (footer->saved && footer->saved != 1)
134 return "invalid 'saved' state";
136 if (footer->hidden && footer->hidden != 1)
137 return "invalid 'hidden' state";
139 if (vhd_util_check_zeros(footer->reserved,
140 sizeof(footer->reserved)))
141 return "invalid 'reserved' bits";
143 return NULL;
144 }
146 static char *
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;
153 size = sizeof(header->cookie);
154 if (memcmp(header->cookie, DD_COOKIE, size))
155 return "invalid cookie";
157 checksum = vhd_checksum_header(header);
158 if (checksum != header->checksum)
159 return "invalid checksum";
161 if (header->hdr_ver != 0x00010000)
162 return "invalid header version";
164 if (header->data_offset != ~(0ULL))
165 return "invalid data offset";
167 eof = lseek(fd, 0, SEEK_END);
168 if (eof == (off_t)-1)
169 return "error finding eof";
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";
178 for (cnt = 0, i = 0; i < sizeof(header->block_size) * 8; i++)
179 if ((header->block_size >> i) & 1)
180 cnt++;
182 if (cnt != 1)
183 return "invalid block size";
185 if (header->res1)
186 return "invalid reserved bits";
188 if (vhd_util_check_zeros(header->res2, sizeof(header->res2)))
189 return "invalid reserved bits";
191 return NULL;
192 }
194 static char *
195 vhd_util_check_validate_differencing_header(vhd_context_t *vhd)
196 {
197 vhd_header_t *header;
199 header = &vhd->header;
201 if (vhd->footer.type == HD_TYPE_DIFF) {
202 char *parent;
203 uint32_t now;
205 now = vhd_time(time(NULL));
206 if (header->prt_ts > now + TIMESTAMP_MAX_SLACK)
207 return "parent creation time in future";
209 if (vhd_header_decode_parent(vhd, header, &parent))
210 return "invalid parent name";
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";
218 if (vhd_util_check_zeros(header->loc, sizeof(header->loc)))
219 return "invalid non-null parent locators";
221 if (!uuid_is_null(header->prt_uuid))
222 return "invalid non-null parent uuid";
224 if (header->prt_ts)
225 return "invalid non-zero parent timestamp";
226 }
228 return NULL;
229 }
231 static char *
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;
238 size = sizeof(batmap->header.cookie);
239 if (memcmp(batmap->header.cookie, VHD_BATMAP_COOKIE, size))
240 return "invalid cookie";
242 if (batmap->header.batmap_version > VHD_BATMAP_CURRENT_VERSION)
243 return "unsupported batmap version";
245 checksum = vhd_checksum_batmap(batmap);
246 if (checksum != batmap->header.checksum)
247 return "invalid checksum";
249 if (!batmap->header.batmap_size)
250 return "invalid size zero";
252 eof = lseek(vhd->fd, 0, SEEK_END);
253 if (eof == (off_t)-1)
254 return "error finding eof";
256 if (!batmap->header.batmap_offset ||
257 batmap->header.batmap_offset % 512)
258 return "invalid batmap offset";
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";
265 return NULL;
266 }
268 static char *
269 vhd_util_check_validate_parent_locator(vhd_context_t *vhd,
270 vhd_parent_locator_t *loc)
271 {
272 off_t eof;
274 if (vhd_validate_platform_code(loc->code))
275 return "invalid platform code";
277 if (loc->code == PLAT_CODE_NONE) {
278 if (vhd_util_check_zeros(loc, sizeof(*loc)))
279 return "non-zero locator";
281 return NULL;
282 }
284 if (!loc->data_offset)
285 return "invalid data offset";
287 if (!loc->data_space)
288 return "invalid data space";
290 if (!loc->data_len)
291 return "invalid data length";
293 eof = lseek(vhd->fd, 0, SEEK_END);
294 if (eof == (off_t)-1)
295 return "error finding eof";
297 if (loc->data_offset + vhd_parent_locator_size(loc) >
298 eof - sizeof(vhd_footer_t))
299 return "invalid size";
301 if (loc->res)
302 return "invalid reserved bits";
304 return NULL;
305 }
307 static const char *
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;
314 msg = NULL;
316 if (vhd_parent_raw(vhd))
317 return msg;
319 if (vhd_open(&parent, ppath,
320 VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED))
321 return "error opening parent";
323 if (uuid_compare(vhd->header.prt_uuid, parent.footer.uuid)) {
324 msg = "invalid parent uuid";
325 goto out;
326 }
328 out:
329 vhd_close(&parent);
330 return msg;
331 }
333 static int
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;
339 off_t eof, off;
340 vhd_footer_t primary, backup;
342 memset(&primary, 0, sizeof(primary));
343 memset(&backup, 0, sizeof(backup));
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 }
351 memset(buf, 0, sizeof(primary));
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 }
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 }
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 }
375 memcpy(&primary, buf, sizeof(primary));
376 opened = vhd_util_check_footer_opened(&primary);
377 vhd_footer_in(&primary);
379 msg = vhd_util_check_validate_footer(&primary);
380 if (msg) {
381 if (opened && ignore)
382 goto check_backup;
384 err = -EINVAL;
385 printf("primary footer invalid: %s\n", msg);
386 goto out;
387 }
389 if (primary.type == HD_TYPE_FIXED) {
390 err = 0;
391 goto out;
392 }
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 }
402 size = 512;
403 memset(buf, 0, sizeof(primary));
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 }
412 memcpy(&backup, buf, sizeof(backup));
413 vhd_footer_in(&backup);
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 }
422 if (memcmp(&primary, &backup, sizeof(primary))) {
423 if (opened && ignore) {
424 memcpy(&primary, &backup, sizeof(primary));
425 goto ok;
426 }
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 }
440 err = -EINVAL;
441 printf("primary and backup footers do not match\n");
442 goto out;
443 }
445 ok:
446 err = 0;
447 memcpy(footer, &primary, sizeof(primary));
449 out:
450 free(buf);
451 return err;
452 }
454 static int
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;
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 }
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 }
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 }
483 memcpy(&header, buf, sizeof(header));
484 vhd_header_in(&header);
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 }
493 err = 0;
495 out:
496 free(buf);
497 return err;
498 }
500 static int
501 vhd_util_check_differencing_header(vhd_context_t *vhd)
502 {
503 char *msg;
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 }
511 return 0;
512 }
514 static int
515 vhd_util_check_bat(vhd_context_t *vhd)
516 {
517 off_t eof, eoh;
518 int i, j, err, block_size;
520 err = vhd_seek(vhd, 0, SEEK_END);
521 if (err) {
522 printf("error calculating eof: %d\n", err);
523 return err;
524 }
526 eof = vhd_position(vhd);
527 if (eof == (off_t)-1) {
528 printf("error calculating eof: %d\n", -errno);
529 return -errno;
530 }
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 }
539 eof++;
540 }
542 err = vhd_get_bat(vhd);
543 if (err) {
544 printf("error reading bat: %d\n", err);
545 return err;
546 }
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 }
554 eof -= sizeof(vhd_footer_t);
555 eof >>= VHD_SECTOR_SHIFT;
556 eoh >>= VHD_SECTOR_SHIFT;
557 block_size = vhd->spb + vhd->bm_secs;
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;
564 if (off < eoh) {
565 printf("block %d (offset 0x%x) clobbers headers\n",
566 i, off);
567 return -EINVAL;
568 }
570 if (off + block_size > eof) {
571 printf("block %d (offset 0x%x) clobbers footer\n",
572 i, off);
573 return -EINVAL;
574 }
576 for (j = 0; j < vhd->header.max_bat_size; j++) {
577 uint32_t joff = vhd->bat.bat[j];
579 if (i == j)
580 continue;
582 if (joff == DD_BLK_UNUSED)
583 continue;
585 if (off == joff)
586 err = -EINVAL;
588 if (off > joff && off < joff + block_size)
589 err = -EINVAL;
591 if (off + block_size > joff &&
592 off + block_size < joff + block_size)
593 err = -EINVAL;
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 }
604 return 0;
605 }
607 static int
608 vhd_util_check_batmap(vhd_context_t *vhd)
609 {
610 char *msg;
611 int i, err;
613 err = vhd_get_bat(vhd);
614 if (err) {
615 printf("error reading bat: %d\n", err);
616 return err;
617 }
619 err = vhd_get_batmap(vhd);
620 if (err) {
621 printf("error reading batmap: %d\n", err);
622 return err;
623 }
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 }
631 for (i = 0; i < vhd->header.max_bat_size; i++) {
632 if (!vhd_batmap_test(vhd, &vhd->batmap, i))
633 continue;
635 if (vhd->bat.bat[i] == DD_BLK_UNUSED) {
636 printf("batmap shows unallocated block %d full\n", i);
637 return -EINVAL;
638 }
639 }
641 return 0;
642 }
644 static int
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;
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;
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 }
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;
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 }
683 if (loc->code == PLAT_CODE_NONE)
684 continue;
686 switch (loc->code) {
687 case PLAT_CODE_MACX:
688 if (macx++)
689 goto dup;
690 break;
692 case PLAT_CODE_MAC:
693 if (mac++)
694 goto dup;
695 break;
697 case PLAT_CODE_W2KU:
698 if (w2ku++)
699 goto dup;
700 break;
702 case PLAT_CODE_W2RU:
703 if (w2ru++)
704 goto dup;
705 break;
707 case PLAT_CODE_WI2R:
708 if (wi2r++)
709 goto dup;
710 break;
712 case PLAT_CODE_WI2K:
713 if (wi2k++)
714 goto dup;
715 break;
717 default:
718 err = -EINVAL;
719 printf("invalid platform code for locator %d\n", i);
720 goto out;
721 }
723 if (loc->code != PLAT_CODE_MACX &&
724 loc->code != PLAT_CODE_W2RU &&
725 loc->code != PLAT_CODE_W2KU)
726 continue;
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 }
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 }
742 err = vhd_find_parent(vhd, ppath, &location);
743 if (err) {
744 printf("error resolving %s: %d\n", ppath, err);
745 goto out;
746 }
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 }
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 }
763 found++;
764 free(ppath);
765 free(location);
766 ppath = NULL;
767 location = NULL;
769 continue;
771 dup:
772 printf("duplicate platform code in locator %d: 0x%x\n",
773 i, loc->code);
774 err = -EINVAL;
775 goto out;
776 }
778 if (!found) {
779 err = -EINVAL;
780 printf("could not find parent %s\n", pname);
781 goto out;
782 }
784 err = 0;
786 out:
787 free(pname);
788 free(ppath);
789 free(location);
790 return err;
791 }
793 static void
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]);
799 printf("%s appears invalid; dumping metadata\n", name);
800 vhd_util_read(argc, argv);
801 }
803 static int
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;
811 fd = -1;
812 memset(&vhd, 0, sizeof(vhd));
814 err = stat(name, &stats);
815 if (err == -1) {
816 printf("cannot stat %s: %d\n", name, errno);
817 return -errno;
818 }
820 if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
821 printf("%s is not a regular file or block device\n", name);
822 return -EINVAL;
823 }
825 fd = open(name, O_RDONLY | O_DIRECT | O_LARGEFILE);
826 if (fd == -1) {
827 printf("error opening %s\n", name);
828 return -errno;
829 }
831 err = vhd_util_check_footer(fd, &footer, ignore);
832 if (err)
833 goto out;
835 if (footer.type != HD_TYPE_DYNAMIC && footer.type != HD_TYPE_DIFF)
836 goto out;
838 err = vhd_util_check_header(fd, &footer);
839 if (err)
840 goto out;
842 err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
843 if (err)
844 goto out;
846 err = vhd_util_check_differencing_header(&vhd);
847 if (err)
848 goto out;
850 err = vhd_util_check_bat(&vhd);
851 if (err)
852 goto out;
854 if (vhd_has_batmap(&vhd)) {
855 err = vhd_util_check_batmap(&vhd);
856 if (err)
857 goto out;
858 }
860 if (vhd.footer.type == HD_TYPE_DIFF) {
861 err = vhd_util_check_parent_locators(&vhd);
862 if (err)
863 goto out;
864 }
866 err = 0;
867 printf("%s is valid\n", name);
869 out:
870 if (err)
871 vhd_util_dump_headers(name);
872 if (fd != -1)
873 close(fd);
874 vhd_close(&vhd);
875 return err;
876 }
878 static int
879 vhd_util_check_parents(const char *name, int ignore)
880 {
881 int err;
882 vhd_context_t vhd;
883 char *cur, *parent;
885 cur = (char *)name;
887 for (;;) {
888 err = vhd_open(&vhd, cur,
889 VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
890 if (err)
891 goto out;
893 if (vhd.footer.type != HD_TYPE_DIFF || vhd_parent_raw(&vhd)) {
894 vhd_close(&vhd);
895 goto out;
896 }
898 err = vhd_parent_locator_get(&vhd, &parent);
899 vhd_close(&vhd);
901 if (err) {
902 printf("error getting parent: %d\n", err);
903 goto out;
904 }
906 if (cur != name)
907 free(cur);
908 cur = parent;
910 err = vhd_util_check_vhd(cur, ignore);
911 if (err)
912 goto out;
913 }
915 out:
916 if (err)
917 printf("error checking parents: %d\n", err);
918 if (cur != name)
919 free(cur);
920 return err;
921 }
923 int
924 vhd_util_check(int argc, char **argv)
925 {
926 char *name;
927 vhd_context_t vhd;
928 int c, err, ignore, parents;
930 if (!argc || !argv) {
931 err = -EINVAL;
932 goto usage;
933 }
935 ignore = 0;
936 parents = 0;
937 name = NULL;
939 optind = 0;
940 while ((c = getopt(argc, argv, "n:iph")) != -1) {
941 switch (c) {
942 case 'n':
943 name = optarg;
944 break;
945 case 'i':
946 ignore = 1;
947 break;
948 case 'p':
949 parents = 1;
950 break;
951 case 'h':
952 err = 0;
953 goto usage;
954 default:
955 err = -EINVAL;
956 goto usage;
957 }
958 }
960 if (!name || optind != argc) {
961 err = -EINVAL;
962 goto usage;
963 }
965 err = vhd_util_check_vhd(name, ignore);
966 if (err)
967 goto out;
969 if (parents)
970 err = vhd_util_check_parents(name, ignore);
972 out:
973 return err;
975 usage:
976 printf("options: -n <file> [-i ignore missing primary footers] "
977 "[-p check parents] [-h help]\n");
978 return err;
979 }