Statistics
| Branch: | Revision:

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
}