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