ia64/xen-unstable

view tools/blktap2/vhd/lib/vhd-util-check.c @ 19647:1c627434605e

blktap2: a completely rewritten blktap implementation

Benefits to blktap2 over the old version of blktap:

* Isolation from xenstore - Blktap devices are now created directly on
the linux dom0 command line, rather than being spawned in response
to XenStore events. This is handy for debugging, makes blktap
generally easier to work with, and is a step toward a generic
user-level block device implementation that is not Xen-specific.

* Improved tapdisk infrastructure: simpler request forwarding, new
request scheduler, request merging, more efficient use of AIO.

* Improved tapdisk error handling and memory management. No
allocations on the block data path, IO retry logic to protect
guests
transient block device failures. This has been tested and is known
to work on weird environments such as NFS soft mounts.

* Pause and snapshot of live virtual disks (see xmsnap script).

* VHD support. The VHD code in this release has been rigorously
tested, and represents a very mature implementation of the VHD
image
format.

* No more duplication of mechanism with blkback. The blktap kernel
module has changed dramatically from the original blktap. Blkback
is now always used to talk to Xen guests, blktap just presents a
Linux gendisk that blkback can export. This is done while
preserving the zero-copy data path from domU to physical device.

These patches deprecate the old blktap code, which can hopefully be
removed from the tree completely at some point in the future.

Signed-off-by: Jake Wires <jake.wires@citrix.com>
Signed-off-by: Dutch Meyer <dmeyer@cs.ubc.ca>
author Keir Fraser <keir.fraser@citrix.com>
date Tue May 26 11:52:31 2009 +0100 (2009-05-26)
parents
children b7f73a7f3078
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 off64_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 = lseek64(fd, 0, SEEK_END);
168 if (eof == (off64_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 off64_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 = lseek64(vhd->fd, 0, SEEK_END);
253 if (eof == (off64_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 off64_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 = lseek64(vhd->fd, 0, SEEK_END);
294 if (eof == (off64_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 char *
308 vhd_util_check_validate_parent(vhd_context_t *vhd, const char *ppath)
309 {
310 char *msg;
311 vhd_context_t parent;
313 msg = NULL;
315 if (vhd_parent_raw(vhd))
316 return msg;
318 if (vhd_open(&parent, ppath,
319 VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED))
320 return "error opening parent";
322 if (uuid_compare(vhd->header.prt_uuid, parent.footer.uuid)) {
323 msg = "invalid parent uuid";
324 goto out;
325 }
327 out:
328 vhd_close(&parent);
329 return msg;
330 }
332 static int
333 vhd_util_check_footer(int fd, vhd_footer_t *footer, int ignore)
334 {
335 size_t size;
336 int err, opened;
337 char *msg, *buf;
338 off64_t eof, off;
339 vhd_footer_t primary, backup;
341 memset(&primary, 0, sizeof(primary));
342 memset(&backup, 0, sizeof(backup));
344 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, sizeof(primary));
345 if (err) {
346 printf("error allocating buffer: %d\n", err);
347 return -err;
348 }
350 memset(buf, 0, sizeof(primary));
352 eof = lseek64(fd, 0, SEEK_END);
353 if (eof == (off64_t)-1) {
354 err = -errno;
355 printf("error calculating end of file: %d\n", err);
356 goto out;
357 }
359 size = ((eof % 512) ? 511 : 512);
360 eof = lseek64(fd, eof - size, SEEK_SET);
361 if (eof == (off64_t)-1) {
362 err = -errno;
363 printf("error calculating end of file: %d\n", err);
364 goto out;
365 }
367 err = read(fd, buf, 512);
368 if (err != size) {
369 err = (errno ? -errno : -EIO);
370 printf("error reading primary footer: %d\n", err);
371 goto out;
372 }
374 memcpy(&primary, buf, sizeof(primary));
375 opened = vhd_util_check_footer_opened(&primary);
376 vhd_footer_in(&primary);
378 msg = vhd_util_check_validate_footer(&primary);
379 if (msg) {
380 if (opened && ignore)
381 goto check_backup;
383 err = -EINVAL;
384 printf("primary footer invalid: %s\n", msg);
385 goto out;
386 }
388 if (primary.type == HD_TYPE_FIXED) {
389 err = 0;
390 goto out;
391 }
393 check_backup:
394 off = lseek64(fd, 0, SEEK_SET);
395 if (off == (off64_t)-1) {
396 err = -errno;
397 printf("error seeking to backup footer: %d\n", err);
398 goto out;
399 }
401 size = 512;
402 memset(buf, 0, sizeof(primary));
404 err = read(fd, buf, size);
405 if (err != size) {
406 err = (errno ? -errno : -EIO);
407 printf("error reading backup footer: %d\n", err);
408 goto out;
409 }
411 memcpy(&backup, buf, sizeof(backup));
412 vhd_footer_in(&backup);
414 msg = vhd_util_check_validate_footer(&backup);
415 if (msg) {
416 err = -EINVAL;
417 printf("backup footer invalid: %s\n", msg);
418 goto out;
419 }
421 if (memcmp(&primary, &backup, sizeof(primary))) {
422 if (opened && ignore) {
423 memcpy(&primary, &backup, sizeof(primary));
424 goto ok;
425 }
427 if (backup.hidden &&
428 !strncmp(backup.crtr_app, "tap", 3) &&
429 (backup.crtr_ver == VHD_VERSION(0, 1) ||
430 backup.crtr_ver == VHD_VERSION(1, 1))) {
431 char cmp, tmp = backup.hidden;
432 backup.hidden = 0;
433 cmp = memcmp(&primary, &backup, sizeof(primary));
434 backup.hidden = tmp;
435 if (!cmp)
436 goto ok;
437 }
439 err = -EINVAL;
440 printf("primary and backup footers do not match\n");
441 goto out;
442 }
444 ok:
445 err = 0;
446 memcpy(footer, &primary, sizeof(primary));
448 out:
449 free(buf);
450 return err;
451 }
453 static int
454 vhd_util_check_header(int fd, vhd_footer_t *footer)
455 {
456 int err;
457 off64_t off;
458 char *msg, *buf;
459 vhd_header_t header;
461 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, sizeof(header));
462 if (err) {
463 printf("error allocating header: %d\n", err);
464 return err;
465 }
467 off = footer->data_offset;
468 off = lseek64(fd, off, SEEK_SET);
469 if (off == (off64_t)-1) {
470 err = -errno;
471 printf("error seeking to header: %d\n", err);
472 goto out;
473 }
475 err = read(fd, buf, sizeof(header));
476 if (err != sizeof(header)) {
477 err = (errno ? -errno : -EIO);
478 printf("error reading header: %d\n", err);
479 goto out;
480 }
482 memcpy(&header, buf, sizeof(header));
483 vhd_header_in(&header);
485 msg = vhd_util_check_validate_header(fd, &header);
486 if (msg) {
487 err = -EINVAL;
488 printf("header is invalid: %s\n", msg);
489 goto out;
490 }
492 err = 0;
494 out:
495 free(buf);
496 return err;
497 }
499 static int
500 vhd_util_check_differencing_header(vhd_context_t *vhd)
501 {
502 char *msg;
504 msg = vhd_util_check_validate_differencing_header(vhd);
505 if (msg) {
506 printf("differencing header is invalid: %s\n", msg);
507 return -EINVAL;
508 }
510 return 0;
511 }
513 static int
514 vhd_util_check_bat(vhd_context_t *vhd)
515 {
516 off64_t eof, eoh;
517 int i, j, err, block_size;
519 err = vhd_seek(vhd, 0, SEEK_END);
520 if (err) {
521 printf("error calculating eof: %d\n", err);
522 return err;
523 }
525 eof = vhd_position(vhd);
526 if (eof == (off64_t)-1) {
527 printf("error calculating eof: %d\n", -errno);
528 return -errno;
529 }
531 /* adjust eof for vhds with short footers */
532 if (eof % 512) {
533 if (eof % 512 != 511) {
534 printf("invalid file size: 0x%"PRIx64"\n", eof);
535 return -EINVAL;
536 }
538 eof++;
539 }
541 err = vhd_get_bat(vhd);
542 if (err) {
543 printf("error reading bat: %d\n", err);
544 return err;
545 }
547 err = vhd_end_of_headers(vhd, &eoh);
548 if (err) {
549 printf("error calculating end of metadata: %d\n", err);
550 return err;
551 }
553 eof -= sizeof(vhd_footer_t);
554 eof >>= VHD_SECTOR_SHIFT;
555 eoh >>= VHD_SECTOR_SHIFT;
556 block_size = vhd->spb + vhd->bm_secs;
558 for (i = 0; i < vhd->header.max_bat_size; i++) {
559 uint32_t off = vhd->bat.bat[i];
560 if (off == DD_BLK_UNUSED)
561 continue;
563 if (off < eoh) {
564 printf("block %d (offset 0x%x) clobbers headers\n",
565 i, off);
566 return -EINVAL;
567 }
569 if (off + block_size > eof) {
570 printf("block %d (offset 0x%x) clobbers footer\n",
571 i, off);
572 return -EINVAL;
573 }
575 for (j = 0; j < vhd->header.max_bat_size; j++) {
576 uint32_t joff = vhd->bat.bat[j];
578 if (i == j)
579 continue;
581 if (joff == DD_BLK_UNUSED)
582 continue;
584 if (off == joff)
585 err = -EINVAL;
587 if (off > joff && off < joff + block_size)
588 err = -EINVAL;
590 if (off + block_size > joff &&
591 off + block_size < joff + block_size)
592 err = -EINVAL;
594 if (err) {
595 printf("block %d (offset 0x%x) clobbers "
596 "block %d (offset 0x%x)\n",
597 i, off, j, joff);
598 return err;
599 }
600 }
601 }
603 return 0;
604 }
606 static int
607 vhd_util_check_batmap(vhd_context_t *vhd)
608 {
609 char *msg;
610 int i, err;
612 err = vhd_get_bat(vhd);
613 if (err) {
614 printf("error reading bat: %d\n", err);
615 return err;
616 }
618 err = vhd_get_batmap(vhd);
619 if (err) {
620 printf("error reading batmap: %d\n", err);
621 return err;
622 }
624 msg = vhd_util_check_validate_batmap(vhd, &vhd->batmap);
625 if (msg) {
626 printf("batmap is invalid: %s\n", msg);
627 return -EINVAL;
628 }
630 for (i = 0; i < vhd->header.max_bat_size; i++) {
631 if (!vhd_batmap_test(vhd, &vhd->batmap, i))
632 continue;
634 if (vhd->bat.bat[i] == DD_BLK_UNUSED) {
635 printf("batmap shows unallocated block %d full\n", i);
636 return -EINVAL;
637 }
638 }
640 return 0;
641 }
643 static int
644 vhd_util_check_parent_locators(vhd_context_t *vhd)
645 {
646 int i, n, err;
647 vhd_parent_locator_t *loc;
648 char *msg, *file, *ppath, *location, *pname;
649 int mac, macx, w2ku, w2ru, wi2r, wi2k, found;
651 mac = 0;
652 macx = 0;
653 w2ku = 0;
654 w2ru = 0;
655 wi2r = 0;
656 wi2k = 0;
657 found = 0;
658 pname = NULL;
659 ppath = NULL;
660 location = NULL;
662 err = vhd_header_decode_parent(vhd, &vhd->header, &pname);
663 if (err) {
664 printf("error decoding parent name: %d\n", err);
665 return err;
666 }
668 n = sizeof(vhd->header.loc) / sizeof(vhd->header.loc[0]);
669 for (i = 0; i < n; i++) {
670 ppath = NULL;
671 location = NULL;
672 loc = vhd->header.loc + i;
674 msg = vhd_util_check_validate_parent_locator(vhd, loc);
675 if (msg) {
676 err = -EINVAL;
677 printf("invalid parent locator %d: %s\n", i, msg);
678 goto out;
679 }
681 if (loc->code == PLAT_CODE_NONE)
682 continue;
684 switch (loc->code) {
685 case PLAT_CODE_MACX:
686 if (macx++)
687 goto dup;
688 break;
690 case PLAT_CODE_MAC:
691 if (mac++)
692 goto dup;
693 break;
695 case PLAT_CODE_W2KU:
696 if (w2ku++)
697 goto dup;
698 break;
700 case PLAT_CODE_W2RU:
701 if (w2ru++)
702 goto dup;
703 break;
705 case PLAT_CODE_WI2R:
706 if (wi2r++)
707 goto dup;
708 break;
710 case PLAT_CODE_WI2K:
711 if (wi2k++)
712 goto dup;
713 break;
715 default:
716 err = -EINVAL;
717 printf("invalid platform code for locator %d\n", i);
718 goto out;
719 }
721 if (loc->code != PLAT_CODE_MACX &&
722 loc->code != PLAT_CODE_W2RU &&
723 loc->code != PLAT_CODE_W2KU)
724 continue;
726 err = vhd_parent_locator_read(vhd, loc, &ppath);
727 if (err) {
728 printf("error reading parent locator %d: %d\n", i, err);
729 goto out;
730 }
732 file = basename(ppath);
733 if (strcmp(pname, file)) {
734 err = -EINVAL;
735 printf("parent locator %d name (%s) does not match "
736 "header name (%s)\n", i, file, pname);
737 goto out;
738 }
740 err = vhd_find_parent(vhd, ppath, &location);
741 if (err) {
742 printf("error resolving %s: %d\n", ppath, err);
743 goto out;
744 }
746 err = access(location, R_OK);
747 if (err && loc->code == PLAT_CODE_MACX) {
748 err = -errno;
749 printf("parent locator %d points to missing file %s "
750 "(resolved to %s)\n", i, ppath, location);
751 goto out;
752 }
754 msg = vhd_util_check_validate_parent(vhd, location);
755 if (msg) {
756 err = -EINVAL;
757 printf("invalid parent %s: %s\n", location, msg);
758 goto out;
759 }
761 found++;
762 free(ppath);
763 free(location);
764 ppath = NULL;
765 location = NULL;
767 continue;
769 dup:
770 printf("duplicate platform code in locator %d: 0x%x\n",
771 i, loc->code);
772 err = -EINVAL;
773 goto out;
774 }
776 if (!found) {
777 err = -EINVAL;
778 printf("could not find parent %s\n", pname);
779 goto out;
780 }
782 err = 0;
784 out:
785 free(pname);
786 free(ppath);
787 free(location);
788 return err;
789 }
791 static void
792 vhd_util_dump_headers(const char *name)
793 {
794 char *argv[] = { "read", "-p", "-n", (char *)name };
795 int argc = sizeof(argv) / sizeof(argv[0]);
797 printf("%s appears invalid; dumping metadata\n", name);
798 vhd_util_read(argc, argv);
799 }
801 static int
802 vhd_util_check_vhd(const char *name, int ignore)
803 {
804 int fd, err;
805 vhd_context_t vhd;
806 struct stat stats;
807 vhd_footer_t footer;
809 fd = -1;
810 memset(&vhd, 0, sizeof(vhd));
812 err = stat(name, &stats);
813 if (err == -1) {
814 printf("cannot stat %s: %d\n", name, errno);
815 return -errno;
816 }
818 if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
819 printf("%s is not a regular file or block device\n", name);
820 return -EINVAL;
821 }
823 fd = open(name, O_RDONLY | O_DIRECT | O_LARGEFILE);
824 if (fd == -1) {
825 printf("error opening %s\n", name);
826 return -errno;
827 }
829 err = vhd_util_check_footer(fd, &footer, ignore);
830 if (err)
831 goto out;
833 if (footer.type != HD_TYPE_DYNAMIC && footer.type != HD_TYPE_DIFF)
834 goto out;
836 err = vhd_util_check_header(fd, &footer);
837 if (err)
838 goto out;
840 err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
841 if (err)
842 goto out;
844 err = vhd_util_check_differencing_header(&vhd);
845 if (err)
846 goto out;
848 err = vhd_util_check_bat(&vhd);
849 if (err)
850 goto out;
852 if (vhd_has_batmap(&vhd)) {
853 err = vhd_util_check_batmap(&vhd);
854 if (err)
855 goto out;
856 }
858 if (vhd.footer.type == HD_TYPE_DIFF) {
859 err = vhd_util_check_parent_locators(&vhd);
860 if (err)
861 goto out;
862 }
864 err = 0;
865 printf("%s is valid\n", name);
867 out:
868 if (err)
869 vhd_util_dump_headers(name);
870 if (fd != -1)
871 close(fd);
872 vhd_close(&vhd);
873 return err;
874 }
876 static int
877 vhd_util_check_parents(const char *name, int ignore)
878 {
879 int err;
880 vhd_context_t vhd;
881 char *cur, *parent;
883 cur = (char *)name;
885 for (;;) {
886 err = vhd_open(&vhd, cur,
887 VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
888 if (err)
889 goto out;
891 if (vhd.footer.type != HD_TYPE_DIFF || vhd_parent_raw(&vhd)) {
892 vhd_close(&vhd);
893 goto out;
894 }
896 err = vhd_parent_locator_get(&vhd, &parent);
897 vhd_close(&vhd);
899 if (err) {
900 printf("error getting parent: %d\n", err);
901 goto out;
902 }
904 if (cur != name)
905 free(cur);
906 cur = parent;
908 err = vhd_util_check_vhd(cur, ignore);
909 if (err)
910 goto out;
911 }
913 out:
914 if (err)
915 printf("error checking parents: %d\n", err);
916 if (cur != name)
917 free(cur);
918 return err;
919 }
921 int
922 vhd_util_check(int argc, char **argv)
923 {
924 char *name;
925 vhd_context_t vhd;
926 int c, err, ignore, parents;
928 if (!argc || !argv) {
929 err = -EINVAL;
930 goto usage;
931 }
933 ignore = 0;
934 parents = 0;
935 name = NULL;
937 optind = 0;
938 while ((c = getopt(argc, argv, "n:iph")) != -1) {
939 switch (c) {
940 case 'n':
941 name = optarg;
942 break;
943 case 'i':
944 ignore = 1;
945 break;
946 case 'p':
947 parents = 1;
948 break;
949 case 'h':
950 err = 0;
951 goto usage;
952 default:
953 err = -EINVAL;
954 goto usage;
955 }
956 }
958 if (!name || optind != argc) {
959 err = -EINVAL;
960 goto usage;
961 }
963 err = vhd_util_check_vhd(name, ignore);
964 if (err)
965 goto out;
967 if (parents)
968 err = vhd_util_check_parents(name, ignore);
970 out:
971 return err;
973 usage:
974 printf("options: -n <file> [-i ignore missing primary footers] "
975 "[-p check parents] [-h help]\n");
976 return err;
977 }