Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #ifdef HAVE_CONFIG_H
32 : : #include "config.h"
33 : : #endif
34 : :
35 : : #include <time.h>
36 : : #include <stdio.h>
37 : : #include <errno.h>
38 : : #include <fcntl.h>
39 : : #include <stdlib.h>
40 : : #include <unistd.h>
41 : : #include <libgen.h>
42 : : #include <inttypes.h>
43 : : #include <sys/stat.h>
44 : :
45 : : #include "list.h"
46 : : #include "libvhd.h"
47 : : #include "vhd-util.h"
48 : :
49 : : // allow the VHD timestamp to be at most this many seconds into the future to
50 : : // account for time skew with NFS servers
51 : : #define TIMESTAMP_MAX_SLACK 1800
52 : :
53 : : struct vhd_util_check_options {
54 : : char ignore_footer;
55 : : char ignore_parent_uuid;
56 : : char ignore_timestamps;
57 : : char check_data;
58 : : char no_check_bat;
59 : : char collect_stats;
60 : : };
61 : :
62 : : struct vhd_util_check_stats {
63 : : char *name;
64 : : char *bitmap;
65 : : uint64_t secs_total;
66 : : uint64_t secs_allocated;
67 : : uint64_t secs_written;
68 : : struct list_head next;
69 : : };
70 : :
71 : : struct vhd_util_check_ctx {
72 : : struct vhd_util_check_options opts;
73 : : struct list_head stats;
74 : : int primary_footer_missing;
75 : : };
76 : :
77 : : #define ctx_cur_stats(ctx) \
78 : : list_entry((ctx)->stats.next, struct vhd_util_check_stats, next)
79 : :
80 : : static inline int
81 : : test_bit_u64(volatile char *addr, uint64_t nr)
82 : : {
83 : 0 : return ((addr[nr >> 3] << (nr & 7)) & 0x80) != 0;
84 : : }
85 : :
86 : : static inline void
87 : : set_bit_u64(volatile char *addr, uint64_t nr)
88 : : {
89 : 0 : addr[nr >> 3] |= (0x80 >> (nr & 7));
90 : : }
91 : :
92 : : static void
93 : : vhd_util_check_stats_init(struct vhd_util_check_ctx *ctx)
94 : : {
95 : 0 : memset(&ctx->stats, 0, sizeof(ctx->stats));
96 : 0 : INIT_LIST_HEAD(&ctx->stats);
97 : : }
98 : :
99 : : static void
100 : 0 : vhd_util_check_stats_free_one(struct vhd_util_check_stats *stats)
101 : : {
102 [ # # ]: 0 : if (stats) {
103 : 0 : free(stats->name);
104 : 0 : free(stats->bitmap);
105 : 0 : free(stats);
106 : : }
107 : 0 : }
108 : :
109 : : static int
110 : 0 : vhd_util_check_stats_alloc_one(struct vhd_util_check_ctx *ctx,
111 : : vhd_context_t *vhd)
112 : : {
113 : : int size;
114 : : struct vhd_util_check_stats *stats;
115 : :
116 : 0 : stats = calloc(1, sizeof(*stats));
117 [ # # ]: 0 : if (!stats)
118 : : goto fail;
119 : :
120 : 0 : stats->name = strdup(vhd->file);
121 [ # # ]: 0 : if (!stats->name)
122 : : goto fail;
123 : :
124 : 0 : stats->secs_total = (uint64_t)vhd->spb * vhd->header.max_bat_size;
125 : 0 : size = (stats->secs_total + 7) >> 3;
126 : 0 : stats->bitmap = calloc(1, size);
127 [ # # ]: 0 : if (!stats->bitmap)
128 : : goto fail;
129 : :
130 : 0 : INIT_LIST_HEAD(&stats->next);
131 : 0 : list_add(&stats->next, &ctx->stats);
132 : :
133 : 0 : return 0;
134 : :
135 : : fail:
136 : 0 : vhd_util_check_stats_free_one(stats);
137 : 0 : printf("failed to allocate stats for %s\n", vhd->file);
138 : 0 : return -ENOMEM;
139 : : }
140 : :
141 : : static void
142 : 0 : vhd_util_check_stats_free(struct vhd_util_check_ctx *ctx)
143 : : {
144 : : struct vhd_util_check_stats *stats, *tmp;
145 : :
146 [ # # ]: 0 : list_for_each_entry_safe(stats, tmp, &ctx->stats, next) {
147 : 0 : list_del_init(&stats->next);
148 : 0 : vhd_util_check_stats_free_one(stats);
149 : : }
150 : 0 : }
151 : :
152 : : static inline float
153 : : pct(uint64_t num, uint64_t den)
154 : : {
155 [ # # ][ # # ]: 0 : return (!den ? 0.0 : (((float)num / (float)den)) * 100.0);
[ # # ][ # # ]
156 : : }
157 : :
158 : : static inline char *
159 : 0 : name(const char *path)
160 : : {
161 : 0 : char *p = strrchr(path, '/');
162 [ # # ][ # # ]: 0 : if (p && (p - path) == strlen(path))
163 : 0 : p = strrchr(--p, '/');
164 [ # # ]: 0 : return (char *)(p ? ++p : path);
165 : : }
166 : :
167 : : static void
168 : 0 : vhd_util_check_stats_print(struct vhd_util_check_ctx *ctx)
169 : : {
170 : : char *bitmap;
171 : : uint64_t secs;
172 : : struct vhd_util_check_stats *head, *cur, *prev;
173 : :
174 [ # # ]: 0 : if (list_empty(&ctx->stats))
175 : : return;
176 : :
177 : 0 : head = list_entry(ctx->stats.next, struct vhd_util_check_stats, next);
178 : 0 : printf("%s: secs allocated: 0x%"PRIx64" secs written: 0x%"PRIx64" (%.2f%%)\n",
179 : 0 : name(head->name), head->secs_allocated, head->secs_written,
180 : 0 : pct(head->secs_written, head->secs_allocated));
181 : :
182 [ # # ]: 0 : if (list_is_last(&head->next, &ctx->stats))
183 : : return;
184 : :
185 : 0 : secs = head->secs_total;
186 : :
187 : 0 : bitmap = malloc((secs + 7) >> 3);
188 [ # # ]: 0 : if (!bitmap) {
189 : : printf("failed to allocate bitmap\n");
190 : : return;
191 : : }
192 : 0 : memcpy(bitmap, head->bitmap, ((secs + 7) >> 3));
193 : :
194 : 0 : cur = prev = head;
195 [ # # ]: 0 : while (!list_is_last(&cur->next, &ctx->stats)) {
196 : 0 : uint64_t i, up = 0, uc = 0;
197 : :
198 : 0 : cur = list_entry(cur->next.next,
199 : : struct vhd_util_check_stats, next);
200 : :
201 [ # # ]: 0 : for (i = 0; i < secs; i++) {
202 [ # # ]: 0 : if (test_bit_u64(cur->bitmap, i)) {
203 [ # # ]: 0 : if (!test_bit_u64(prev->bitmap, i))
204 : 0 : up++; /* sector is unique wrt parent */
205 : :
206 [ # # ]: 0 : if (!test_bit_u64(bitmap, i))
207 : 0 : uc++; /* sector is unique wrt chain */
208 : :
209 : : set_bit_u64(bitmap, i);
210 : : }
211 : : }
212 : :
213 : 0 : printf("%s: secs allocated: 0x%"PRIx64" secs written: 0x%"PRIx64
214 : : " (%.2f%%) secs not in parent: 0x%"PRIx64" (%.2f%%)"
215 : : " secs not in ancestors: 0x%"PRIx64" (%.2f%%)\n",
216 : 0 : name(cur->name), cur->secs_allocated, cur->secs_written,
217 : 0 : pct(cur->secs_written, cur->secs_allocated),
218 : 0 : up, pct(up, cur->secs_written),
219 : 0 : uc, pct(uc, cur->secs_written));
220 : :
221 : 0 : prev = cur;
222 : : }
223 : :
224 : 0 : free(bitmap);
225 : : }
226 : :
227 : : static int
228 : : vhd_util_check_zeros(void *buf, size_t size)
229 : : {
230 : : int i;
231 : : char *p;
232 : :
233 : 0 : p = buf;
234 [ # # ][ # # ]: 0 : for (i = 0; i < size; i++)
[ # # ][ # # ]
[ # # ][ # # ]
235 [ # # ][ # # ]: 0 : if (p[i])
[ # # ][ # # ]
[ # # ][ # # ]
236 : : return i;
237 : :
238 : : return 0;
239 : : }
240 : :
241 : : static char *
242 : 0 : vhd_util_check_validate_footer(struct vhd_util_check_ctx *ctx,
243 : : vhd_footer_t *footer)
244 : : {
245 : : int size;
246 : : uint32_t checksum;
247 : :
248 : 0 : size = sizeof(footer->cookie);
249 [ # # ]: 0 : if (memcmp(footer->cookie, HD_COOKIE, size))
250 : : return "invalid cookie";
251 : :
252 : 0 : checksum = vhd_checksum_footer(footer);
253 [ # # ]: 0 : if (checksum != footer->checksum) {
254 [ # # ][ # # ]: 0 : if (footer->hidden &&
255 [ # # ]: 0 : !strncmp(footer->crtr_app, "tap", 3) &&
256 : 0 : (footer->crtr_ver == VHD_VERSION(0, 1) ||
257 : : footer->crtr_ver == VHD_VERSION(1, 1))) {
258 : 0 : char tmp = footer->hidden;
259 : 0 : footer->hidden = 0;
260 : 0 : checksum = vhd_checksum_footer(footer);
261 : 0 : footer->hidden = tmp;
262 : :
263 [ # # ]: 0 : if (checksum == footer->checksum)
264 : : goto ok;
265 : : }
266 : :
267 : : return "invalid checksum";
268 : : }
269 : :
270 : : ok:
271 [ # # ]: 0 : if (!(footer->features & HD_RESERVED))
272 : : return "invalid 'reserved' feature";
273 : :
274 [ # # ]: 0 : if (footer->features & ~(HD_TEMPORARY | HD_RESERVED))
275 : : return "invalid extra features";
276 : :
277 [ # # ]: 0 : if (footer->ff_version != HD_FF_VERSION)
278 : : return "invalid file format version";
279 : :
280 [ # # ]: 0 : if (footer->type != HD_TYPE_DYNAMIC &&
281 [ # # ]: 0 : footer->type != HD_TYPE_DIFF &&
282 : 0 : footer->data_offset != ~(0ULL))
283 : : return "invalid data offset";
284 : :
285 [ # # ]: 0 : if (!ctx->opts.ignore_timestamps) {
286 : 0 : uint32_t now = vhd_time(time(NULL));
287 [ # # ]: 0 : if (footer->timestamp > now + TIMESTAMP_MAX_SLACK)
288 : : return "creation time in future";
289 : : }
290 : :
291 [ # # ][ # # ]: 0 : if (!strncmp(footer->crtr_app, "tap", 3) &&
292 : 0 : footer->crtr_ver > VHD_CURRENT_VERSION)
293 : : return "unsupported tap creator version";
294 : :
295 [ # # ]: 0 : if (vhd_chs(footer->curr_size) < footer->geometry)
296 : : return "geometry too large";
297 : :
298 [ # # ]: 0 : if (footer->type != HD_TYPE_FIXED &&
299 : 0 : footer->type != HD_TYPE_DYNAMIC &&
300 : : footer->type != HD_TYPE_DIFF)
301 : : return "invalid type";
302 : :
303 [ # # ]: 0 : if (footer->saved && footer->saved != 1)
304 : : return "invalid 'saved' state";
305 : :
306 [ # # ]: 0 : if (footer->hidden && footer->hidden != 1)
307 : : return "invalid 'hidden' state";
308 : :
309 [ # # ]: 0 : if (vhd_util_check_zeros(footer->reserved,
310 : : sizeof(footer->reserved)))
311 : : return "invalid 'reserved' bits";
312 : :
313 [ # # ]: 0 : if (uuid_is_null(footer->uuid))
314 : : return "invalid (NULL) uuid";
315 : :
316 : 0 : return NULL;
317 : : }
318 : :
319 : : static char *
320 : 0 : vhd_util_check_validate_header(int fd, vhd_header_t *header)
321 : : {
322 : : off64_t eof;
323 : : int i, cnt, size;
324 : : uint32_t checksum;
325 : :
326 : 0 : size = sizeof(header->cookie);
327 [ # # ]: 0 : if (memcmp(header->cookie, DD_COOKIE, size))
328 : : return "invalid cookie";
329 : :
330 : 0 : checksum = vhd_checksum_header(header);
331 [ # # ]: 0 : if (checksum != header->checksum)
332 : : return "invalid checksum";
333 : :
334 [ # # ]: 0 : if (header->hdr_ver != 0x00010000)
335 : : return "invalid header version";
336 : :
337 [ # # ]: 0 : if (header->data_offset != ~(0ULL))
338 : : return "invalid data offset";
339 : :
340 : 0 : eof = lseek64(fd, 0, SEEK_END);
341 [ # # ]: 0 : if (eof == (off64_t)-1)
342 : : return "error finding eof";
343 : :
344 [ # # ][ # # ]: 0 : if (header->table_offset <= 0 ||
345 [ # # ]: 0 : header->table_offset % 512 ||
346 : 0 : (header->table_offset +
347 : 0 : (header->max_bat_size * sizeof(uint32_t)) >
348 : 0 : eof - sizeof(vhd_footer_t)))
349 : : return "invalid table offset";
350 : :
351 [ # # ]: 0 : for (cnt = 0, i = 0; i < sizeof(header->block_size) * 8; i++)
352 [ # # ]: 0 : if ((header->block_size >> i) & 1)
353 : 0 : cnt++;
354 : :
355 [ # # ]: 0 : if (cnt != 1)
356 : : return "invalid block size";
357 : :
358 [ # # ]: 0 : if (header->res1)
359 : : return "invalid reserved bits";
360 : :
361 [ # # ]: 0 : if (vhd_util_check_zeros(header->res2, sizeof(header->res2)))
362 : : return "invalid reserved bits";
363 : :
364 : 0 : return NULL;
365 : : }
366 : :
367 : : static char *
368 : 0 : vhd_util_check_validate_differencing_header(struct vhd_util_check_ctx *ctx,
369 : : vhd_context_t *vhd)
370 : : {
371 : : vhd_header_t *header;
372 : :
373 : 0 : header = &vhd->header;
374 : :
375 [ # # ]: 0 : if (vhd->footer.type == HD_TYPE_DIFF) {
376 : : char *parent;
377 : :
378 [ # # ]: 0 : if (!ctx->opts.ignore_timestamps) {
379 : 0 : uint32_t now = vhd_time(time(NULL));
380 [ # # ]: 0 : if (header->prt_ts > now + TIMESTAMP_MAX_SLACK)
381 : 0 : return "parent creation time in future";
382 : : }
383 : :
384 [ # # ]: 0 : if (vhd_header_decode_parent(vhd, header, &parent))
385 : : return "invalid parent name";
386 : :
387 : 0 : free(parent);
388 : : } else {
389 [ # # ]: 0 : if (vhd_util_check_zeros(header->prt_name,
390 : : sizeof(header->prt_name)))
391 : : return "invalid non-null parent name";
392 : :
393 [ # # ]: 0 : if (vhd_util_check_zeros(header->loc, sizeof(header->loc)))
394 : : return "invalid non-null parent locators";
395 : :
396 [ # # ]: 0 : if (!uuid_is_null(header->prt_uuid))
397 : : return "invalid non-null parent uuid";
398 : :
399 [ # # ]: 0 : if (header->prt_ts)
400 : : return "invalid non-zero parent timestamp";
401 : : }
402 : :
403 : : return NULL;
404 : : }
405 : :
406 : : static char *
407 : 0 : vhd_util_check_validate_batmap(vhd_context_t *vhd, vhd_batmap_t *batmap)
408 : : {
409 : : int size;
410 : : off64_t eof;
411 : : uint32_t checksum;
412 : :
413 : 0 : size = sizeof(batmap->header.cookie);
414 [ # # ]: 0 : if (memcmp(batmap->header.cookie, VHD_BATMAP_COOKIE, size))
415 : : return "invalid cookie";
416 : :
417 [ # # ]: 0 : if (batmap->header.batmap_version > VHD_BATMAP_CURRENT_VERSION)
418 : : return "unsupported batmap version";
419 : :
420 : 0 : checksum = vhd_checksum_batmap(vhd, batmap);
421 [ # # ]: 0 : if (checksum != batmap->header.checksum)
422 : : return "invalid checksum";
423 : :
424 [ # # ]: 0 : if (!batmap->header.batmap_size)
425 : : return "invalid size zero";
426 : :
427 [ # # ]: 0 : if (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3) <
428 : 0 : vhd->header.max_bat_size)
429 : : return "batmap-BAT size mismatch";
430 : :
431 : 0 : eof = lseek64(vhd->fd, 0, SEEK_END);
432 [ # # ]: 0 : if (eof == (off64_t)-1)
433 : : return "error finding eof";
434 : :
435 [ # # ][ # # ]: 0 : if (!batmap->header.batmap_offset ||
436 : 0 : batmap->header.batmap_offset % 512)
437 : : return "invalid batmap offset";
438 : :
439 [ # # ]: 0 : if ((batmap->header.batmap_offset +
440 : 0 : vhd_sectors_to_bytes(batmap->header.batmap_size)) >
441 : 0 : eof - sizeof(vhd_footer_t))
442 : : return "invalid batmap size";
443 : :
444 : 0 : return NULL;
445 : : }
446 : :
447 : : static char *
448 : 0 : vhd_util_check_validate_parent_locator(vhd_context_t *vhd,
449 : 0 : vhd_parent_locator_t *loc)
450 : : {
451 : : off64_t eof;
452 : :
453 [ # # ]: 0 : if (vhd_validate_platform_code(loc->code))
454 : : return "invalid platform code";
455 : :
456 [ # # ]: 0 : if (loc->code == PLAT_CODE_NONE) {
457 [ # # ]: 0 : if (vhd_util_check_zeros(loc, sizeof(*loc)))
458 : : return "non-zero locator";
459 : :
460 : 0 : return NULL;
461 : : }
462 : :
463 [ # # ]: 0 : if (!loc->data_offset)
464 : : return "invalid data offset";
465 : :
466 [ # # ]: 0 : if (!loc->data_space)
467 : : return "invalid data space";
468 : :
469 [ # # ]: 0 : if (!loc->data_len)
470 : : return "invalid data length";
471 : :
472 : 0 : eof = lseek64(vhd->fd, 0, SEEK_END);
473 [ # # ]: 0 : if (eof == (off64_t)-1)
474 : : return "error finding eof";
475 : :
476 [ # # ]: 0 : if (loc->data_offset + vhd_parent_locator_size(loc) >
477 : 0 : eof - sizeof(vhd_footer_t))
478 : : return "invalid size";
479 : :
480 [ # # ]: 0 : if (loc->res)
481 : : return "invalid reserved bits";
482 : :
483 : 0 : return NULL;
484 : : }
485 : :
486 : : static char *
487 : 0 : vhd_util_check_validate_parent(struct vhd_util_check_ctx *ctx,
488 : : vhd_context_t *vhd, const char *ppath)
489 : : {
490 : : char *msg;
491 : : vhd_context_t parent;
492 : :
493 : 0 : msg = NULL;
494 : :
495 [ # # ]: 0 : if (vhd_parent_raw(vhd))
496 : 0 : return msg;
497 : :
498 [ # # ]: 0 : if (ctx->opts.ignore_parent_uuid)
499 : : return msg;
500 : :
501 [ # # ]: 0 : if (vhd_open(&parent, ppath,
502 : : VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED))
503 : : return "error opening parent";
504 : :
505 [ # # ]: 0 : if (uuid_compare(vhd->header.prt_uuid, parent.footer.uuid)) {
506 : 0 : msg = "invalid parent uuid";
507 : 0 : goto out;
508 : : }
509 : :
510 : : out:
511 : 0 : vhd_close(&parent);
512 : : return msg;
513 : : }
514 : :
515 : : static int
516 : 0 : vhd_util_check_footer(struct vhd_util_check_ctx *ctx,
517 : : int fd, vhd_footer_t *footer)
518 : : {
519 : : int err;
520 : : size_t size;
521 : : char *msg;
522 : : void *buf;
523 : : off64_t eof, off;
524 : : vhd_footer_t primary, backup;
525 : :
526 : : memset(&primary, 0, sizeof(primary));
527 : : memset(&backup, 0, sizeof(backup));
528 : :
529 : 0 : err = posix_memalign(&buf, VHD_SECTOR_SIZE, sizeof(primary));
530 [ # # ]: 0 : if (err) {
531 : : printf("error allocating buffer: %d\n", err);
532 : 0 : return -err;
533 : : }
534 : :
535 : 0 : memset(buf, 0, sizeof(primary));
536 : :
537 : 0 : eof = lseek64(fd, 0, SEEK_END);
538 [ # # ]: 0 : if (eof == (off64_t)-1) {
539 : 0 : err = -errno;
540 : : printf("error calculating end of file: %d\n", err);
541 : : goto out;
542 : : }
543 : :
544 [ # # ]: 0 : size = ((eof % 512) ? 511 : 512);
545 : 0 : eof = lseek64(fd, eof - size, SEEK_SET);
546 [ # # ]: 0 : if (eof == (off64_t)-1) {
547 : 0 : err = -errno;
548 : : printf("error calculating end of file: %d\n", err);
549 : : goto out;
550 : : }
551 : :
552 : 0 : err = read(fd, buf, 512);
553 [ # # ]: 0 : if (err != size) {
554 [ # # ]: 0 : err = (errno ? -errno : -EIO);
555 : : printf("error reading primary footer: %d\n", err);
556 : : goto out;
557 : : }
558 : :
559 : 0 : memcpy(&primary, buf, sizeof(primary));
560 : 0 : vhd_footer_in(&primary);
561 : :
562 : 0 : msg = vhd_util_check_validate_footer(ctx, &primary);
563 [ # # ]: 0 : if (msg) {
564 : 0 : ctx->primary_footer_missing = 1;
565 : :
566 [ # # ]: 0 : if (ctx->opts.ignore_footer)
567 : : goto check_backup;
568 : :
569 : 0 : err = -EINVAL;
570 : : printf("primary footer invalid: %s\n", msg);
571 : : goto out;
572 : : }
573 : :
574 [ # # ]: 0 : if (primary.type == HD_TYPE_FIXED) {
575 : : err = 0;
576 : : goto out;
577 : : }
578 : :
579 : : check_backup:
580 : 0 : off = lseek64(fd, 0, SEEK_SET);
581 [ # # ]: 0 : if (off == (off64_t)-1) {
582 : 0 : err = -errno;
583 : : printf("error seeking to backup footer: %d\n", err);
584 : : goto out;
585 : : }
586 : :
587 : 0 : size = 512;
588 : 0 : memset(buf, 0, sizeof(primary));
589 : :
590 : 0 : err = read(fd, buf, size);
591 [ # # ]: 0 : if (err != size) {
592 [ # # ]: 0 : err = (errno ? -errno : -EIO);
593 : : printf("error reading backup footer: %d\n", err);
594 : : goto out;
595 : : }
596 : :
597 : 0 : memcpy(&backup, buf, sizeof(backup));
598 : 0 : vhd_footer_in(&backup);
599 : :
600 : 0 : msg = vhd_util_check_validate_footer(ctx, &backup);
601 [ # # ]: 0 : if (msg) {
602 : 0 : err = -EINVAL;
603 : : printf("backup footer invalid: %s\n", msg);
604 : : goto out;
605 : : }
606 : :
607 [ # # ]: 0 : if (memcmp(&primary, &backup, sizeof(primary))) {
608 [ # # ]: 0 : if (ctx->opts.ignore_footer) {
609 : : memcpy(&primary, &backup, sizeof(primary));
610 : : goto ok;
611 : : }
612 : :
613 [ # # ][ # # ]: 0 : if (backup.hidden &&
614 [ # # ]: 0 : !strncmp(backup.crtr_app, "tap", 3) &&
615 : 0 : (backup.crtr_ver == VHD_VERSION(0, 1) ||
616 : : backup.crtr_ver == VHD_VERSION(1, 1))) {
617 : : int cmp;
618 : 0 : char tmp = backup.hidden;
619 : 0 : backup.hidden = 0;
620 : 0 : cmp = memcmp(&primary, &backup, sizeof(primary));
621 : 0 : backup.hidden = tmp;
622 [ # # ]: 0 : if (!cmp)
623 : : goto ok;
624 : : }
625 : :
626 : 0 : err = -EINVAL;
627 : : printf("primary and backup footers do not match\n");
628 : : goto out;
629 : : }
630 : :
631 : : ok:
632 : 0 : err = 0;
633 : : memcpy(footer, &primary, sizeof(primary));
634 : :
635 : : out:
636 : 0 : free(buf);
637 : 0 : return err;
638 : : }
639 : :
640 : : static int
641 : 0 : vhd_util_check_header(int fd, vhd_footer_t *footer)
642 : : {
643 : : int err;
644 : : off64_t off;
645 : : char *msg;
646 : : void *buf;
647 : : vhd_header_t header;
648 : :
649 : 0 : err = posix_memalign(&buf, VHD_SECTOR_SIZE, sizeof(header));
650 [ # # ]: 0 : if (err) {
651 : : printf("error allocating header: %d\n", err);
652 : 0 : return err;
653 : : }
654 : :
655 : 0 : off = footer->data_offset;
656 : 0 : off = lseek64(fd, off, SEEK_SET);
657 [ # # ]: 0 : if (off == (off64_t)-1) {
658 : 0 : err = -errno;
659 : : printf("error seeking to header: %d\n", err);
660 : : goto out;
661 : : }
662 : :
663 : 0 : err = read(fd, buf, sizeof(header));
664 [ # # ]: 0 : if (err != sizeof(header)) {
665 [ # # ]: 0 : err = (errno ? -errno : -EIO);
666 : : printf("error reading header: %d\n", err);
667 : : goto out;
668 : : }
669 : :
670 : 0 : memcpy(&header, buf, sizeof(header));
671 : 0 : vhd_header_in(&header);
672 : :
673 : 0 : msg = vhd_util_check_validate_header(fd, &header);
674 [ # # ]: 0 : if (msg) {
675 : 0 : err = -EINVAL;
676 : : printf("header is invalid: %s\n", msg);
677 : : goto out;
678 : : }
679 : :
680 : : err = 0;
681 : :
682 : : out:
683 : 0 : free(buf);
684 : 0 : return err;
685 : : }
686 : :
687 : : static int
688 : 0 : vhd_util_check_differencing_header(struct vhd_util_check_ctx *ctx,
689 : : vhd_context_t *vhd)
690 : : {
691 : : char *msg;
692 : :
693 : 0 : msg = vhd_util_check_validate_differencing_header(ctx, vhd);
694 [ # # ]: 0 : if (msg) {
695 : : printf("differencing header is invalid: %s\n", msg);
696 : 0 : return -EINVAL;
697 : : }
698 : :
699 : : return 0;
700 : : }
701 : :
702 : : static int
703 : 0 : vhd_util_check_bitmap(struct vhd_util_check_ctx *ctx,
704 : : vhd_context_t *vhd, uint32_t block)
705 : : {
706 : : int err, i;
707 : : uint64_t sector;
708 : : char *bitmap, *data;
709 : :
710 : 0 : data = NULL;
711 : 0 : bitmap = NULL;
712 : 0 : sector = (uint64_t)block * vhd->spb;
713 : :
714 : 0 : err = vhd_read_bitmap(vhd, block, &bitmap);
715 [ # # ]: 0 : if (err) {
716 : : printf("error reading bitmap 0x%x\n", block);
717 : : goto out;
718 : : }
719 : :
720 [ # # ]: 0 : if (ctx->opts.check_data) {
721 : 0 : err = vhd_read_block(vhd, block, &data);
722 [ # # ]: 0 : if (err) {
723 : : printf("error reading data block 0x%x\n", block);
724 : : goto out;
725 : : }
726 : : }
727 : :
728 [ # # ]: 0 : for (i = 0; i < vhd->spb; i++) {
729 [ # # # # ]: 0 : if (ctx->opts.collect_stats &&
730 : 0 : vhd_bitmap_test(vhd, bitmap, i)) {
731 : 0 : ctx_cur_stats(ctx)->secs_written++;
732 : 0 : set_bit_u64(ctx_cur_stats(ctx)->bitmap, sector + i);
733 : : }
734 : :
735 [ # # ]: 0 : if (ctx->opts.check_data) {
736 : 0 : char *buf = data + (i << VHD_SECTOR_SHIFT);
737 : 0 : int set = vhd_util_check_zeros(buf, VHD_SECTOR_SIZE);
738 : 0 : int map = vhd_bitmap_test(vhd, bitmap, i);
739 : :
740 [ # # ]: 0 : if (set && !map) {
741 : : printf("sector 0x%x of block 0x%x has data "
742 : : "where bitmap is clear\n", i, block);
743 : : err = -EINVAL;
744 : : }
745 : : }
746 : : }
747 : :
748 : : out:
749 : 0 : free(data);
750 : 0 : free(bitmap);
751 : 0 : return err;
752 : : }
753 : :
754 : : static int
755 : 0 : vhd_util_check_bat(struct vhd_util_check_ctx *ctx, vhd_context_t *vhd)
756 : : {
757 : : off64_t eof, eoh;
758 : : uint64_t vhd_blks;
759 : : int i, j, err, block_size;
760 : :
761 [ # # ]: 0 : if (ctx->opts.collect_stats) {
762 : 0 : err = vhd_util_check_stats_alloc_one(ctx, vhd);
763 [ # # ]: 0 : if (err)
764 : 0 : return err;
765 : : }
766 : :
767 : 0 : err = vhd_seek(vhd, 0, SEEK_END);
768 [ # # ]: 0 : if (err) {
769 : : printf("error calculating eof: %d\n", err);
770 : : return err;
771 : : }
772 : :
773 : 0 : eof = vhd_position(vhd);
774 [ # # ]: 0 : if (eof == (off64_t)-1) {
775 : 0 : printf("error calculating eof: %d\n", -errno);
776 : 0 : return -errno;
777 : : }
778 : :
779 : : /* adjust eof for vhds with short footers */
780 [ # # ]: 0 : if (eof % 512) {
781 [ # # ]: 0 : if (eof % 512 != 511) {
782 : : printf("invalid file size: 0x%"PRIx64"\n", eof);
783 : : return -EINVAL;
784 : : }
785 : :
786 : 0 : eof++;
787 : : }
788 : :
789 : 0 : err = vhd_get_bat(vhd);
790 [ # # ]: 0 : if (err) {
791 : : printf("error reading bat: %d\n", err);
792 : : return err;
793 : : }
794 : :
795 : 0 : err = vhd_end_of_headers(vhd, &eoh);
796 [ # # ]: 0 : if (err) {
797 : : printf("error calculating end of metadata: %d\n", err);
798 : : return err;
799 : : }
800 : :
801 : 0 : eof -= sizeof(vhd_footer_t);
802 : 0 : eof >>= VHD_SECTOR_SHIFT;
803 : 0 : eoh >>= VHD_SECTOR_SHIFT;
804 : 0 : block_size = vhd->spb + vhd->bm_secs;
805 : :
806 : 0 : vhd_blks = vhd->footer.curr_size >> VHD_BLOCK_SHIFT;
807 [ # # ]: 0 : if (vhd_blks > vhd->header.max_bat_size) {
808 : 0 : printf("VHD size (%"PRIu64" blocks) exceeds BAT size (%u)\n",
809 : : vhd_blks, vhd->header.max_bat_size);
810 : : return -EINVAL;
811 : : }
812 : :
813 [ # # ]: 0 : for (i = 0; i < vhd_blks; i++) {
814 : 0 : uint32_t off = vhd->bat.bat[i];
815 [ # # ]: 0 : if (off == DD_BLK_UNUSED)
816 : 0 : continue;
817 : :
818 [ # # ]: 0 : if (off < eoh) {
819 : : printf("block %d (offset 0x%x) clobbers headers\n",
820 : : i, off);
821 : : return -EINVAL;
822 : : }
823 : :
824 [ # # ]: 0 : if (off + block_size > eof) {
825 [ # # ][ # # ]: 0 : if (!(ctx->primary_footer_missing &&
[ # # ]
826 : 0 : ctx->opts.ignore_footer &&
827 : 0 : off + block_size == eof + 1)) {
828 : : printf("block %d (offset 0x%x) clobbers "
829 : : "footer\n", i, off);
830 : : return -EINVAL;
831 : : }
832 : : }
833 : :
834 [ # # ]: 0 : if (ctx->opts.no_check_bat)
835 : 0 : continue;
836 : :
837 [ # # ]: 0 : for (j = 0; j < vhd_blks; j++) {
838 : 0 : uint32_t joff = vhd->bat.bat[j];
839 : :
840 [ # # ]: 0 : if (i == j)
841 : 0 : continue;
842 : :
843 [ # # ]: 0 : if (joff == DD_BLK_UNUSED)
844 : 0 : continue;
845 : :
846 [ # # ]: 0 : if (off == joff)
847 : 0 : err = -EINVAL;
848 : :
849 [ # # ][ # # ]: 0 : if (off > joff && off < joff + block_size)
850 : 0 : err = -EINVAL;
851 : :
852 [ # # ][ # # ]: 0 : if (off + block_size > joff &&
853 : 0 : off + block_size < joff + block_size)
854 : 0 : err = -EINVAL;
855 : :
856 [ # # ]: 0 : if (err) {
857 : : printf("block %d (offset 0x%x) clobbers "
858 : : "block %d (offset 0x%x)\n",
859 : : i, off, j, joff);
860 : : return err;
861 : : }
862 : : }
863 : :
864 [ # # ]: 0 : if (ctx->opts.check_data || ctx->opts.collect_stats) {
865 [ # # ]: 0 : if (ctx->opts.collect_stats)
866 : 0 : ctx_cur_stats(ctx)->secs_allocated += vhd->spb;
867 : :
868 : 0 : err = vhd_util_check_bitmap(ctx, vhd, i);
869 [ # # ]: 0 : if (err)
870 : : return err;
871 : : }
872 : : }
873 : :
874 : : return 0;
875 : : }
876 : :
877 : : static int
878 : 0 : vhd_util_check_batmap(vhd_context_t *vhd)
879 : : {
880 : : char *msg;
881 : : int i, err;
882 : :
883 : 0 : err = vhd_get_bat(vhd);
884 [ # # ]: 0 : if (err) {
885 : : printf("error reading bat: %d\n", err);
886 : 0 : return err;
887 : : }
888 : :
889 : 0 : err = vhd_get_batmap(vhd);
890 [ # # ]: 0 : if (err) {
891 : : printf("error reading batmap: %d\n", err);
892 : 0 : return err;
893 : : }
894 : :
895 : 0 : msg = vhd_util_check_validate_batmap(vhd, &vhd->batmap);
896 [ # # ]: 0 : if (msg) {
897 : : printf("batmap is invalid: %s\n", msg);
898 : 0 : return -EINVAL;
899 : : }
900 : :
901 [ # # ]: 0 : for (i = 0; i < vhd->footer.curr_size >> VHD_BLOCK_SHIFT; i++) {
902 [ # # ]: 0 : if (!vhd_batmap_test(vhd, &vhd->batmap, i))
903 : 0 : continue;
904 : :
905 [ # # ]: 0 : if (vhd->bat.bat[i] == DD_BLK_UNUSED) {
906 : : printf("batmap shows unallocated block %d full\n", i);
907 : 0 : return -EINVAL;
908 : : }
909 : : }
910 : :
911 : : return 0;
912 : : }
913 : :
914 : : static int
915 : 0 : vhd_util_check_parent_locators(struct vhd_util_check_ctx *ctx,
916 : : vhd_context_t *vhd)
917 : : {
918 : : int i, n, err;
919 : : vhd_parent_locator_t *loc;
920 : : char *msg, *file, *ppath, *location, *pname;
921 : : int mac, macx, w2ku, w2ru, wi2r, wi2k, found;
922 : :
923 : 0 : mac = 0;
924 : 0 : macx = 0;
925 : 0 : w2ku = 0;
926 : 0 : w2ru = 0;
927 : 0 : wi2r = 0;
928 : 0 : wi2k = 0;
929 : 0 : found = 0;
930 : 0 : pname = NULL;
931 : 0 : ppath = NULL;
932 : 0 : location = NULL;
933 : :
934 : 0 : err = vhd_header_decode_parent(vhd, &vhd->header, &pname);
935 [ # # ]: 0 : if (err) {
936 : : printf("error decoding parent name: %d\n", err);
937 : 0 : return err;
938 : : }
939 : :
940 : : n = sizeof(vhd->header.loc) / sizeof(vhd->header.loc[0]);
941 [ # # ]: 0 : for (i = 0; i < n; i++) {
942 : 0 : ppath = NULL;
943 : 0 : location = NULL;
944 : 0 : loc = vhd->header.loc + i;
945 : :
946 : 0 : msg = vhd_util_check_validate_parent_locator(vhd, loc);
947 [ # # ]: 0 : if (msg) {
948 : 0 : err = -EINVAL;
949 : : printf("invalid parent locator %d: %s\n", i, msg);
950 : : goto out;
951 : : }
952 : :
953 [ # # ]: 0 : if (loc->code == PLAT_CODE_NONE)
954 : 0 : continue;
955 : :
956 [ # # # # : 0 : switch (loc->code) {
# # # ]
957 : : case PLAT_CODE_MACX:
958 [ # # ]: 0 : if (macx++)
959 : : goto dup;
960 : : break;
961 : :
962 : : case PLAT_CODE_MAC:
963 [ # # ]: 0 : if (mac++)
964 : : goto dup;
965 : : break;
966 : :
967 : : case PLAT_CODE_W2KU:
968 [ # # ]: 0 : if (w2ku++)
969 : : goto dup;
970 : : break;
971 : :
972 : : case PLAT_CODE_W2RU:
973 [ # # ]: 0 : if (w2ru++)
974 : : goto dup;
975 : : break;
976 : :
977 : : case PLAT_CODE_WI2R:
978 [ # # ]: 0 : if (wi2r++)
979 : : goto dup;
980 : : break;
981 : :
982 : : case PLAT_CODE_WI2K:
983 [ # # ]: 0 : if (wi2k++)
984 : : goto dup;
985 : : break;
986 : :
987 : : default:
988 : 0 : err = -EINVAL;
989 : : printf("invalid platform code for locator %d\n", i);
990 : : goto out;
991 : : }
992 : :
993 [ # # ]: 0 : if (loc->code != PLAT_CODE_MACX &&
994 [ # # ]: 0 : loc->code != PLAT_CODE_W2RU &&
995 : : loc->code != PLAT_CODE_W2KU)
996 : 0 : continue;
997 : :
998 : 0 : err = vhd_parent_locator_read(vhd, loc, &ppath);
999 [ # # ]: 0 : if (err) {
1000 : : printf("error reading parent locator %d: %d\n", i, err);
1001 : : goto out;
1002 : : }
1003 : :
1004 : 0 : file = basename(ppath);
1005 [ # # ]: 0 : if (strcmp(pname, file)) {
1006 : 0 : err = -EINVAL;
1007 : 0 : printf("parent locator %d name (%s) does not match "
1008 : : "header name (%s)\n", i, file, pname);
1009 : : goto out;
1010 : : }
1011 : :
1012 : 0 : err = vhd_find_parent(vhd, ppath, &location);
1013 [ # # ]: 0 : if (err) {
1014 : 0 : printf("error resolving %s: %d\n", ppath, err);
1015 : : goto out;
1016 : : }
1017 : :
1018 : 0 : err = access(location, R_OK);
1019 [ # # ][ # # ]: 0 : if (err && loc->code == PLAT_CODE_MACX) {
1020 : 0 : err = -errno;
1021 : 0 : printf("parent locator %d points to missing file %s "
1022 : : "(resolved to %s)\n", i, ppath, location);
1023 : : goto out;
1024 : : }
1025 : :
1026 : 0 : msg = vhd_util_check_validate_parent(ctx, vhd, location);
1027 [ # # ]: 0 : if (msg) {
1028 : 0 : err = -EINVAL;
1029 : 0 : printf("invalid parent %s: %s\n", location, msg);
1030 : : goto out;
1031 : : }
1032 : :
1033 : 0 : found++;
1034 : 0 : free(ppath);
1035 : 0 : free(location);
1036 : 0 : ppath = NULL;
1037 : 0 : location = NULL;
1038 : :
1039 : 0 : continue;
1040 : :
1041 : : dup:
1042 : 0 : printf("duplicate platform code in locator %d: 0x%x\n",
1043 : : i, loc->code);
1044 : : err = -EINVAL;
1045 : : goto out;
1046 : : }
1047 : :
1048 [ # # ]: 0 : if (!found) {
1049 : 0 : err = -EINVAL;
1050 : 0 : printf("could not find parent %s\n", pname);
1051 : : goto out;
1052 : : }
1053 : :
1054 : : err = 0;
1055 : :
1056 : : out:
1057 : 0 : free(pname);
1058 : 0 : free(ppath);
1059 : 0 : free(location);
1060 : 0 : return err;
1061 : : }
1062 : :
1063 : : static void
1064 : 0 : vhd_util_dump_headers(const char *name)
1065 : : {
1066 : 0 : char *argv[] = { "read", "-p", "-n", (char *)name };
1067 : 0 : int argc = sizeof(argv) / sizeof(argv[0]);
1068 : :
1069 : : printf("%s appears invalid; dumping metadata\n", name);
1070 : 0 : vhd_util_read(argc, argv);
1071 : 0 : }
1072 : :
1073 : : static int
1074 : 0 : vhd_util_check_vhd(struct vhd_util_check_ctx *ctx, const char *name)
1075 : : {
1076 : : int fd, err;
1077 : : vhd_context_t vhd;
1078 : : struct stat stats;
1079 : : vhd_footer_t footer;
1080 : :
1081 : : memset(&vhd, 0, sizeof(vhd));
1082 : : memset(&footer, 0, sizeof(footer));
1083 : :
1084 : 0 : err = stat(name, &stats);
1085 [ # # ]: 0 : if (err == -1) {
1086 : 0 : printf("cannot stat %s: %d\n", name, errno);
1087 : 0 : return -errno;
1088 : : }
1089 : :
1090 [ # # ]: 0 : if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
1091 : : printf("%s is not a regular file or block device\n", name);
1092 : : return -EINVAL;
1093 : : }
1094 : :
1095 : 0 : fd = open_optional_odirect(name, O_RDONLY | O_DIRECT | O_LARGEFILE);
1096 [ # # ]: 0 : if (fd == -1) {
1097 : : printf("error opening %s\n", name);
1098 : 0 : return -errno;
1099 : : }
1100 : :
1101 : 0 : err = vhd_util_check_footer(ctx, fd, &footer);
1102 [ # # ]: 0 : if (err)
1103 : : goto out;
1104 : :
1105 [ # # ]: 0 : if (footer.type != HD_TYPE_DYNAMIC && footer.type != HD_TYPE_DIFF)
1106 : : goto out;
1107 : :
1108 : 0 : err = vhd_util_check_header(fd, &footer);
1109 [ # # ]: 0 : if (err)
1110 : : goto out;
1111 : :
1112 : 0 : err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
1113 [ # # ]: 0 : if (err)
1114 : : goto out;
1115 : :
1116 : 0 : err = vhd_util_check_differencing_header(ctx, &vhd);
1117 [ # # ]: 0 : if (err)
1118 : : goto out;
1119 : :
1120 : 0 : err = vhd_util_check_bat(ctx, &vhd);
1121 [ # # ]: 0 : if (err)
1122 : : goto out;
1123 : :
1124 [ # # ]: 0 : if (vhd_has_batmap(&vhd)) {
1125 : 0 : err = vhd_util_check_batmap(&vhd);
1126 [ # # ]: 0 : if (err)
1127 : : goto out;
1128 : : }
1129 : :
1130 [ # # ]: 0 : if (vhd.footer.type == HD_TYPE_DIFF) {
1131 : 0 : err = vhd_util_check_parent_locators(ctx, &vhd);
1132 [ # # ]: 0 : if (err)
1133 : : goto out;
1134 : : }
1135 : :
1136 : 0 : err = 0;
1137 : :
1138 [ # # ]: 0 : if (!ctx->opts.collect_stats)
1139 : : printf("%s is valid\n", name);
1140 : :
1141 : : out:
1142 [ # # ]: 0 : if (err)
1143 : 0 : vhd_util_dump_headers(name);
1144 [ # # ]: 0 : if (fd != -1)
1145 : 0 : close(fd);
1146 : 0 : vhd_close(&vhd);
1147 : : return err;
1148 : : }
1149 : :
1150 : : static int
1151 : 0 : vhd_util_check_parents(struct vhd_util_check_ctx *ctx, const char *name)
1152 : : {
1153 : : int err;
1154 : : vhd_context_t vhd;
1155 : : char *cur, *parent;
1156 : :
1157 : 0 : cur = (char *)name;
1158 : :
1159 : : for (;;) {
1160 : 0 : err = vhd_open(&vhd, cur,
1161 : : VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
1162 [ # # ]: 0 : if (err)
1163 : : goto out;
1164 : :
1165 [ # # # # ]: 0 : if (vhd.footer.type != HD_TYPE_DIFF || vhd_parent_raw(&vhd)) {
1166 : 0 : vhd_close(&vhd);
1167 : : goto out;
1168 : : }
1169 : :
1170 : 0 : err = vhd_parent_locator_get(&vhd, &parent);
1171 : 0 : vhd_close(&vhd);
1172 : :
1173 [ # # ]: 0 : if (err) {
1174 : : printf("error getting parent: %d\n", err);
1175 : : goto out;
1176 : : }
1177 : :
1178 [ # # ]: 0 : if (cur != name)
1179 : 0 : free(cur);
1180 : 0 : cur = parent;
1181 : :
1182 : 0 : err = vhd_util_check_vhd(ctx, cur);
1183 [ # # ]: 0 : if (err)
1184 : : goto out;
1185 : : }
1186 : :
1187 : : out:
1188 [ # # ]: 0 : if (err)
1189 : : printf("error checking parents: %d\n", err);
1190 [ # # ]: 0 : if (cur != name)
1191 : 0 : free(cur);
1192 : 0 : return err;
1193 : : }
1194 : :
1195 : : int
1196 : 0 : vhd_util_check(int argc, char **argv)
1197 : : {
1198 : : char *name;
1199 : : int c, err, parents;
1200 : : struct vhd_util_check_ctx ctx;
1201 : :
1202 [ # # ]: 0 : if (!argc || !argv) {
1203 : : err = -EINVAL;
1204 : : goto usage;
1205 : : }
1206 : :
1207 : 0 : name = NULL;
1208 : 0 : parents = 0;
1209 : : memset(&ctx, 0, sizeof(ctx));
1210 : : vhd_util_check_stats_init(&ctx);
1211 : :
1212 : 0 : optind = 0;
1213 [ # # ]: 0 : while ((c = getopt(argc, argv, "n:iItpbBsh")) != -1) {
1214 [ # # # # : 0 : switch (c) {
# # # # #
# ]
1215 : : case 'n':
1216 : 0 : name = optarg;
1217 : 0 : break;
1218 : : case 'i':
1219 : 0 : ctx.opts.ignore_footer = 1;
1220 : 0 : break;
1221 : : case 'I':
1222 : 0 : ctx.opts.ignore_parent_uuid = 1;
1223 : 0 : break;
1224 : : case 't':
1225 : 0 : ctx.opts.ignore_timestamps = 1;
1226 : 0 : break;
1227 : : case 'p':
1228 : : parents = 1;
1229 : : break;
1230 : : case 'b':
1231 : 0 : ctx.opts.check_data = 1;
1232 : 0 : break;
1233 : : case 'B':
1234 : 0 : ctx.opts.no_check_bat = 1;
1235 : 0 : break;
1236 : : case 's':
1237 : 0 : ctx.opts.collect_stats = 1;
1238 : 0 : break;
1239 : : case 'h':
1240 : : err = 0;
1241 : : goto usage;
1242 : : default:
1243 : 0 : err = -EINVAL;
1244 : 0 : goto usage;
1245 : : }
1246 : : }
1247 : :
1248 [ # # ][ # # ]: 0 : if (!name || optind != argc) {
1249 : : err = -EINVAL;
1250 : : goto usage;
1251 : : }
1252 : :
1253 [ # # ][ # # ]: 0 : if ((ctx.opts.collect_stats || ctx.opts.check_data) &&
1254 : 0 : ctx.opts.no_check_bat) {
1255 : : err = -EINVAL;
1256 : : goto usage;
1257 : : }
1258 : :
1259 : 0 : err = vhd_util_check_vhd(&ctx, name);
1260 [ # # ]: 0 : if (err)
1261 : : goto out;
1262 : :
1263 [ # # ]: 0 : if (parents)
1264 : 0 : err = vhd_util_check_parents(&ctx, name);
1265 : :
1266 [ # # ]: 0 : if (ctx.opts.collect_stats)
1267 : 0 : vhd_util_check_stats_print(&ctx);
1268 : :
1269 : 0 : vhd_util_check_stats_free(&ctx);
1270 : :
1271 : : out:
1272 : 0 : return err;
1273 : :
1274 : : usage:
1275 : : printf("options: -n <file> [-i ignore missing primary footers] "
1276 : : "[-I ignore parent uuids] [-t ignore timestamps] "
1277 : : "[-B do not check BAT for overlapping (precludes -s, -b)] "
1278 : : "[-p check parents] [-b check bitmaps] [-s stats] [-h help]\n");
1279 : : return err;
1280 : : }
|