ia64/xen-unstable

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

blktap2: portability fixes for NetBSD

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

Signed-off-by: Christoph Egger <Christoph.Egger@amd.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Jun 23 17:24:14 2009 +0100 (2009-06-23)
parents 1c627434605e
children f1fec38c8228
rev   line source
keir@19647 1 /* Copyright (c) 2008, XenSource Inc.
keir@19647 2 * All rights reserved.
keir@19647 3 *
keir@19647 4 * Redistribution and use in source and binary forms, with or without
keir@19647 5 * modification, are permitted provided that the following conditions are met:
keir@19647 6 * * Redistributions of source code must retain the above copyright
keir@19647 7 * notice, this list of conditions and the following disclaimer.
keir@19647 8 * * Redistributions in binary form must reproduce the above copyright
keir@19647 9 * notice, this list of conditions and the following disclaimer in the
keir@19647 10 * documentation and/or other materials provided with the distribution.
keir@19647 11 * * Neither the name of XenSource Inc. nor the names of its contributors
keir@19647 12 * may be used to endorse or promote products derived from this software
keir@19647 13 * without specific prior written permission.
keir@19647 14 *
keir@19647 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
keir@19647 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
keir@19647 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
keir@19647 18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
keir@19647 19 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
keir@19647 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
keir@19647 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
keir@19647 22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
keir@19647 23 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
keir@19647 24 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
keir@19647 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
keir@19647 26 */
keir@19647 27 #include <time.h>
keir@19647 28 #include <stdio.h>
keir@19647 29 #include <errno.h>
keir@19647 30 #include <fcntl.h>
keir@19647 31 #include <stdlib.h>
keir@19647 32 #include <unistd.h>
keir@19647 33 #include <libgen.h>
keir@19647 34 #include <inttypes.h>
keir@19647 35 #include <sys/stat.h>
keir@19647 36
keir@19647 37 #include "libvhd.h"
keir@19647 38 #include "vhd-util.h"
keir@19647 39
keir@19647 40 // allow the VHD timestamp to be at most this many seconds into the future to
keir@19647 41 // account for time skew with NFS servers
keir@19647 42 #define TIMESTAMP_MAX_SLACK 1800
keir@19647 43
keir@19647 44 static int
keir@19647 45 vhd_util_check_zeros(void *buf, size_t size)
keir@19647 46 {
keir@19647 47 int i;
keir@19647 48 char *p;
keir@19647 49
keir@19647 50 p = buf;
keir@19647 51 for (i = 0; i < size; i++)
keir@19647 52 if (p[i])
keir@19647 53 return i;
keir@19647 54
keir@19647 55 return 0;
keir@19647 56 }
keir@19647 57
keir@19647 58 static int
keir@19647 59 vhd_util_check_footer_opened(vhd_footer_t *footer)
keir@19647 60 {
keir@19647 61 int i, n;
keir@19647 62 uint32_t *buf;
keir@19647 63
keir@19647 64 buf = (uint32_t *)footer;
keir@19647 65 n = sizeof(*footer) / sizeof(uint32_t);
keir@19647 66
keir@19647 67 for (i = 0; i < n; i++)
keir@19647 68 if (buf[i] != 0xc7c7c7c7)
keir@19647 69 return 0;
keir@19647 70
keir@19647 71 return 1;
keir@19647 72 }
keir@19647 73
keir@19647 74 static char *
keir@19647 75 vhd_util_check_validate_footer(vhd_footer_t *footer)
keir@19647 76 {
keir@19647 77 int size;
keir@19647 78 uint32_t checksum, now;
keir@19647 79
keir@19647 80 size = sizeof(footer->cookie);
keir@19647 81 if (memcmp(footer->cookie, HD_COOKIE, size))
keir@19647 82 return "invalid cookie";
keir@19647 83
keir@19647 84 checksum = vhd_checksum_footer(footer);
keir@19647 85 if (checksum != footer->checksum) {
keir@19647 86 if (footer->hidden &&
keir@19647 87 !strncmp(footer->crtr_app, "tap", 3) &&
keir@19647 88 (footer->crtr_ver == VHD_VERSION(0, 1) ||
keir@19647 89 footer->crtr_ver == VHD_VERSION(1, 1))) {
keir@19647 90 char tmp = footer->hidden;
keir@19647 91 footer->hidden = 0;
keir@19647 92 checksum = vhd_checksum_footer(footer);
keir@19647 93 footer->hidden = tmp;
keir@19647 94
keir@19647 95 if (checksum == footer->checksum)
keir@19647 96 goto ok;
keir@19647 97 }
keir@19647 98
keir@19647 99 return "invalid checksum";
keir@19647 100 }
keir@19647 101
keir@19647 102 ok:
keir@19647 103 if (!(footer->features & HD_RESERVED))
keir@19647 104 return "invalid 'reserved' feature";
keir@19647 105
keir@19647 106 if (footer->features & ~(HD_TEMPORARY | HD_RESERVED))
keir@19647 107 return "invalid extra features";
keir@19647 108
keir@19647 109 if (footer->ff_version != HD_FF_VERSION)
keir@19647 110 return "invalid file format version";
keir@19647 111
keir@19647 112 if (footer->type != HD_TYPE_DYNAMIC &&
keir@19647 113 footer->type != HD_TYPE_DIFF &&
keir@19647 114 footer->data_offset != ~(0ULL))
keir@19647 115 return "invalid data offset";
keir@19647 116
keir@19647 117 now = vhd_time(time(NULL));
keir@19647 118 if (footer->timestamp > now + TIMESTAMP_MAX_SLACK)
keir@19647 119 return "creation time in future";
keir@19647 120
keir@19647 121 if (!strncmp(footer->crtr_app, "tap", 3) &&
keir@19647 122 footer->crtr_ver > VHD_CURRENT_VERSION)
keir@19647 123 return "unsupported tap creator version";
keir@19647 124
keir@19647 125 if (vhd_chs(footer->curr_size) < footer->geometry)
keir@19647 126 return "geometry too large";
keir@19647 127
keir@19647 128 if (footer->type != HD_TYPE_FIXED &&
keir@19647 129 footer->type != HD_TYPE_DYNAMIC &&
keir@19647 130 footer->type != HD_TYPE_DIFF)
keir@19647 131 return "invalid type";
keir@19647 132
keir@19647 133 if (footer->saved && footer->saved != 1)
keir@19647 134 return "invalid 'saved' state";
keir@19647 135
keir@19647 136 if (footer->hidden && footer->hidden != 1)
keir@19647 137 return "invalid 'hidden' state";
keir@19647 138
keir@19647 139 if (vhd_util_check_zeros(footer->reserved,
keir@19647 140 sizeof(footer->reserved)))
keir@19647 141 return "invalid 'reserved' bits";
keir@19647 142
keir@19647 143 return NULL;
keir@19647 144 }
keir@19647 145
keir@19647 146 static char *
keir@19647 147 vhd_util_check_validate_header(int fd, vhd_header_t *header)
keir@19647 148 {
keir@19817 149 off_t eof;
keir@19647 150 int i, cnt, size;
keir@19647 151 uint32_t checksum;
keir@19647 152
keir@19647 153 size = sizeof(header->cookie);
keir@19647 154 if (memcmp(header->cookie, DD_COOKIE, size))
keir@19647 155 return "invalid cookie";
keir@19647 156
keir@19647 157 checksum = vhd_checksum_header(header);
keir@19647 158 if (checksum != header->checksum)
keir@19647 159 return "invalid checksum";
keir@19647 160
keir@19647 161 if (header->hdr_ver != 0x00010000)
keir@19647 162 return "invalid header version";
keir@19647 163
keir@19647 164 if (header->data_offset != ~(0ULL))
keir@19647 165 return "invalid data offset";
keir@19647 166
keir@19817 167 eof = lseek(fd, 0, SEEK_END);
keir@19817 168 if (eof == (off_t)-1)
keir@19647 169 return "error finding eof";
keir@19647 170
keir@19647 171 if (header->table_offset <= 0 ||
keir@19647 172 header->table_offset % 512 ||
keir@19647 173 (header->table_offset +
keir@19647 174 (header->max_bat_size * sizeof(uint32_t)) >
keir@19647 175 eof - sizeof(vhd_footer_t)))
keir@19647 176 return "invalid table offset";
keir@19647 177
keir@19647 178 for (cnt = 0, i = 0; i < sizeof(header->block_size) * 8; i++)
keir@19647 179 if ((header->block_size >> i) & 1)
keir@19647 180 cnt++;
keir@19647 181
keir@19647 182 if (cnt != 1)
keir@19647 183 return "invalid block size";
keir@19647 184
keir@19647 185 if (header->res1)
keir@19647 186 return "invalid reserved bits";
keir@19647 187
keir@19647 188 if (vhd_util_check_zeros(header->res2, sizeof(header->res2)))
keir@19647 189 return "invalid reserved bits";
keir@19647 190
keir@19647 191 return NULL;
keir@19647 192 }
keir@19647 193
keir@19647 194 static char *
keir@19647 195 vhd_util_check_validate_differencing_header(vhd_context_t *vhd)
keir@19647 196 {
keir@19647 197 vhd_header_t *header;
keir@19647 198
keir@19647 199 header = &vhd->header;
keir@19647 200
keir@19647 201 if (vhd->footer.type == HD_TYPE_DIFF) {
keir@19647 202 char *parent;
keir@19647 203 uint32_t now;
keir@19647 204
keir@19647 205 now = vhd_time(time(NULL));
keir@19647 206 if (header->prt_ts > now + TIMESTAMP_MAX_SLACK)
keir@19647 207 return "parent creation time in future";
keir@19647 208
keir@19647 209 if (vhd_header_decode_parent(vhd, header, &parent))
keir@19647 210 return "invalid parent name";
keir@19647 211
keir@19647 212 free(parent);
keir@19647 213 } else {
keir@19647 214 if (vhd_util_check_zeros(header->prt_name,
keir@19647 215 sizeof(header->prt_name)))
keir@19647 216 return "invalid non-null parent name";
keir@19647 217
keir@19647 218 if (vhd_util_check_zeros(header->loc, sizeof(header->loc)))
keir@19647 219 return "invalid non-null parent locators";
keir@19647 220
keir@19647 221 if (!uuid_is_null(header->prt_uuid))
keir@19647 222 return "invalid non-null parent uuid";
keir@19647 223
keir@19647 224 if (header->prt_ts)
keir@19647 225 return "invalid non-zero parent timestamp";
keir@19647 226 }
keir@19647 227
keir@19647 228 return NULL;
keir@19647 229 }
keir@19647 230
keir@19647 231 static char *
keir@19647 232 vhd_util_check_validate_batmap(vhd_context_t *vhd, vhd_batmap_t *batmap)
keir@19647 233 {
keir@19647 234 int size;
keir@19817 235 off_t eof;
keir@19647 236 uint32_t checksum;
keir@19647 237
keir@19647 238 size = sizeof(batmap->header.cookie);
keir@19647 239 if (memcmp(batmap->header.cookie, VHD_BATMAP_COOKIE, size))
keir@19647 240 return "invalid cookie";
keir@19647 241
keir@19647 242 if (batmap->header.batmap_version > VHD_BATMAP_CURRENT_VERSION)
keir@19647 243 return "unsupported batmap version";
keir@19647 244
keir@19647 245 checksum = vhd_checksum_batmap(batmap);
keir@19647 246 if (checksum != batmap->header.checksum)
keir@19647 247 return "invalid checksum";
keir@19647 248
keir@19647 249 if (!batmap->header.batmap_size)
keir@19647 250 return "invalid size zero";
keir@19647 251
keir@19817 252 eof = lseek(vhd->fd, 0, SEEK_END);
keir@19817 253 if (eof == (off_t)-1)
keir@19647 254 return "error finding eof";
keir@19647 255
keir@19647 256 if (!batmap->header.batmap_offset ||
keir@19647 257 batmap->header.batmap_offset % 512)
keir@19647 258 return "invalid batmap offset";
keir@19647 259
keir@19647 260 if ((batmap->header.batmap_offset +
keir@19647 261 vhd_sectors_to_bytes(batmap->header.batmap_size)) >
keir@19647 262 eof - sizeof(vhd_footer_t))
keir@19647 263 return "invalid batmap size";
keir@19647 264
keir@19647 265 return NULL;
keir@19647 266 }
keir@19647 267
keir@19647 268 static char *
keir@19647 269 vhd_util_check_validate_parent_locator(vhd_context_t *vhd,
keir@19647 270 vhd_parent_locator_t *loc)
keir@19647 271 {
keir@19817 272 off_t eof;
keir@19647 273
keir@19647 274 if (vhd_validate_platform_code(loc->code))
keir@19647 275 return "invalid platform code";
keir@19647 276
keir@19647 277 if (loc->code == PLAT_CODE_NONE) {
keir@19647 278 if (vhd_util_check_zeros(loc, sizeof(*loc)))
keir@19647 279 return "non-zero locator";
keir@19647 280
keir@19647 281 return NULL;
keir@19647 282 }
keir@19647 283
keir@19647 284 if (!loc->data_offset)
keir@19647 285 return "invalid data offset";
keir@19647 286
keir@19647 287 if (!loc->data_space)
keir@19647 288 return "invalid data space";
keir@19647 289
keir@19647 290 if (!loc->data_len)
keir@19647 291 return "invalid data length";
keir@19647 292
keir@19817 293 eof = lseek(vhd->fd, 0, SEEK_END);
keir@19817 294 if (eof == (off_t)-1)
keir@19647 295 return "error finding eof";
keir@19647 296
keir@19647 297 if (loc->data_offset + vhd_parent_locator_size(loc) >
keir@19647 298 eof - sizeof(vhd_footer_t))
keir@19647 299 return "invalid size";
keir@19647 300
keir@19647 301 if (loc->res)
keir@19647 302 return "invalid reserved bits";
keir@19647 303
keir@19647 304 return NULL;
keir@19647 305 }
keir@19647 306
keir@19817 307 static const char *
keir@19647 308 vhd_util_check_validate_parent(vhd_context_t *vhd, const char *ppath)
keir@19647 309 {
keir@19817 310 const char *msg;
keir@19647 311 vhd_context_t parent;
keir@19817 312 uint32_t status;
keir@19647 313
keir@19647 314 msg = NULL;
keir@19647 315
keir@19647 316 if (vhd_parent_raw(vhd))
keir@19647 317 return msg;
keir@19647 318
keir@19647 319 if (vhd_open(&parent, ppath,
keir@19647 320 VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED))
keir@19647 321 return "error opening parent";
keir@19647 322
keir@19647 323 if (uuid_compare(vhd->header.prt_uuid, parent.footer.uuid)) {
keir@19647 324 msg = "invalid parent uuid";
keir@19647 325 goto out;
keir@19647 326 }
keir@19647 327
keir@19647 328 out:
keir@19647 329 vhd_close(&parent);
keir@19647 330 return msg;
keir@19647 331 }
keir@19647 332
keir@19647 333 static int
keir@19647 334 vhd_util_check_footer(int fd, vhd_footer_t *footer, int ignore)
keir@19647 335 {
keir@19647 336 size_t size;
keir@19647 337 int err, opened;
keir@19647 338 char *msg, *buf;
keir@19817 339 off_t eof, off;
keir@19647 340 vhd_footer_t primary, backup;
keir@19647 341
keir@19647 342 memset(&primary, 0, sizeof(primary));
keir@19647 343 memset(&backup, 0, sizeof(backup));
keir@19647 344
keir@19647 345 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, sizeof(primary));
keir@19647 346 if (err) {
keir@19647 347 printf("error allocating buffer: %d\n", err);
keir@19647 348 return -err;
keir@19647 349 }
keir@19647 350
keir@19647 351 memset(buf, 0, sizeof(primary));
keir@19647 352
keir@19817 353 eof = lseek(fd, 0, SEEK_END);
keir@19817 354 if (eof == (off_t)-1) {
keir@19647 355 err = -errno;
keir@19647 356 printf("error calculating end of file: %d\n", err);
keir@19647 357 goto out;
keir@19647 358 }
keir@19647 359
keir@19647 360 size = ((eof % 512) ? 511 : 512);
keir@19817 361 eof = lseek(fd, eof - size, SEEK_SET);
keir@19817 362 if (eof == (off_t)-1) {
keir@19647 363 err = -errno;
keir@19647 364 printf("error calculating end of file: %d\n", err);
keir@19647 365 goto out;
keir@19647 366 }
keir@19647 367
keir@19647 368 err = read(fd, buf, 512);
keir@19647 369 if (err != size) {
keir@19647 370 err = (errno ? -errno : -EIO);
keir@19647 371 printf("error reading primary footer: %d\n", err);
keir@19647 372 goto out;
keir@19647 373 }
keir@19647 374
keir@19647 375 memcpy(&primary, buf, sizeof(primary));
keir@19647 376 opened = vhd_util_check_footer_opened(&primary);
keir@19647 377 vhd_footer_in(&primary);
keir@19647 378
keir@19647 379 msg = vhd_util_check_validate_footer(&primary);
keir@19647 380 if (msg) {
keir@19647 381 if (opened && ignore)
keir@19647 382 goto check_backup;
keir@19647 383
keir@19647 384 err = -EINVAL;
keir@19647 385 printf("primary footer invalid: %s\n", msg);
keir@19647 386 goto out;
keir@19647 387 }
keir@19647 388
keir@19647 389 if (primary.type == HD_TYPE_FIXED) {
keir@19647 390 err = 0;
keir@19647 391 goto out;
keir@19647 392 }
keir@19647 393
keir@19647 394 check_backup:
keir@19817 395 off = lseek(fd, 0, SEEK_SET);
keir@19817 396 if (off == (off_t)-1) {
keir@19647 397 err = -errno;
keir@19647 398 printf("error seeking to backup footer: %d\n", err);
keir@19647 399 goto out;
keir@19647 400 }
keir@19647 401
keir@19647 402 size = 512;
keir@19647 403 memset(buf, 0, sizeof(primary));
keir@19647 404
keir@19647 405 err = read(fd, buf, size);
keir@19647 406 if (err != size) {
keir@19647 407 err = (errno ? -errno : -EIO);
keir@19647 408 printf("error reading backup footer: %d\n", err);
keir@19647 409 goto out;
keir@19647 410 }
keir@19647 411
keir@19647 412 memcpy(&backup, buf, sizeof(backup));
keir@19647 413 vhd_footer_in(&backup);
keir@19647 414
keir@19647 415 msg = vhd_util_check_validate_footer(&backup);
keir@19647 416 if (msg) {
keir@19647 417 err = -EINVAL;
keir@19647 418 printf("backup footer invalid: %s\n", msg);
keir@19647 419 goto out;
keir@19647 420 }
keir@19647 421
keir@19647 422 if (memcmp(&primary, &backup, sizeof(primary))) {
keir@19647 423 if (opened && ignore) {
keir@19647 424 memcpy(&primary, &backup, sizeof(primary));
keir@19647 425 goto ok;
keir@19647 426 }
keir@19647 427
keir@19647 428 if (backup.hidden &&
keir@19647 429 !strncmp(backup.crtr_app, "tap", 3) &&
keir@19647 430 (backup.crtr_ver == VHD_VERSION(0, 1) ||
keir@19647 431 backup.crtr_ver == VHD_VERSION(1, 1))) {
keir@19647 432 char cmp, tmp = backup.hidden;
keir@19647 433 backup.hidden = 0;
keir@19647 434 cmp = memcmp(&primary, &backup, sizeof(primary));
keir@19647 435 backup.hidden = tmp;
keir@19647 436 if (!cmp)
keir@19647 437 goto ok;
keir@19647 438 }
keir@19647 439
keir@19647 440 err = -EINVAL;
keir@19647 441 printf("primary and backup footers do not match\n");
keir@19647 442 goto out;
keir@19647 443 }
keir@19647 444
keir@19647 445 ok:
keir@19647 446 err = 0;
keir@19647 447 memcpy(footer, &primary, sizeof(primary));
keir@19647 448
keir@19647 449 out:
keir@19647 450 free(buf);
keir@19647 451 return err;
keir@19647 452 }
keir@19647 453
keir@19647 454 static int
keir@19647 455 vhd_util_check_header(int fd, vhd_footer_t *footer)
keir@19647 456 {
keir@19647 457 int err;
keir@19817 458 off_t off;
keir@19647 459 char *msg, *buf;
keir@19647 460 vhd_header_t header;
keir@19647 461
keir@19647 462 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, sizeof(header));
keir@19647 463 if (err) {
keir@19647 464 printf("error allocating header: %d\n", err);
keir@19647 465 return err;
keir@19647 466 }
keir@19647 467
keir@19647 468 off = footer->data_offset;
keir@19817 469 off = lseek(fd, off, SEEK_SET);
keir@19817 470 if (off == (off_t)-1) {
keir@19647 471 err = -errno;
keir@19647 472 printf("error seeking to header: %d\n", err);
keir@19647 473 goto out;
keir@19647 474 }
keir@19647 475
keir@19647 476 err = read(fd, buf, sizeof(header));
keir@19647 477 if (err != sizeof(header)) {
keir@19647 478 err = (errno ? -errno : -EIO);
keir@19647 479 printf("error reading header: %d\n", err);
keir@19647 480 goto out;
keir@19647 481 }
keir@19647 482
keir@19647 483 memcpy(&header, buf, sizeof(header));
keir@19647 484 vhd_header_in(&header);
keir@19647 485
keir@19647 486 msg = vhd_util_check_validate_header(fd, &header);
keir@19647 487 if (msg) {
keir@19647 488 err = -EINVAL;
keir@19647 489 printf("header is invalid: %s\n", msg);
keir@19647 490 goto out;
keir@19647 491 }
keir@19647 492
keir@19647 493 err = 0;
keir@19647 494
keir@19647 495 out:
keir@19647 496 free(buf);
keir@19647 497 return err;
keir@19647 498 }
keir@19647 499
keir@19647 500 static int
keir@19647 501 vhd_util_check_differencing_header(vhd_context_t *vhd)
keir@19647 502 {
keir@19647 503 char *msg;
keir@19647 504
keir@19647 505 msg = vhd_util_check_validate_differencing_header(vhd);
keir@19647 506 if (msg) {
keir@19647 507 printf("differencing header is invalid: %s\n", msg);
keir@19647 508 return -EINVAL;
keir@19647 509 }
keir@19647 510
keir@19647 511 return 0;
keir@19647 512 }
keir@19647 513
keir@19647 514 static int
keir@19647 515 vhd_util_check_bat(vhd_context_t *vhd)
keir@19647 516 {
keir@19817 517 off_t eof, eoh;
keir@19647 518 int i, j, err, block_size;
keir@19647 519
keir@19647 520 err = vhd_seek(vhd, 0, SEEK_END);
keir@19647 521 if (err) {
keir@19647 522 printf("error calculating eof: %d\n", err);
keir@19647 523 return err;
keir@19647 524 }
keir@19647 525
keir@19647 526 eof = vhd_position(vhd);
keir@19817 527 if (eof == (off_t)-1) {
keir@19647 528 printf("error calculating eof: %d\n", -errno);
keir@19647 529 return -errno;
keir@19647 530 }
keir@19647 531
keir@19647 532 /* adjust eof for vhds with short footers */
keir@19647 533 if (eof % 512) {
keir@19647 534 if (eof % 512 != 511) {
keir@19647 535 printf("invalid file size: 0x%"PRIx64"\n", eof);
keir@19647 536 return -EINVAL;
keir@19647 537 }
keir@19647 538
keir@19647 539 eof++;
keir@19647 540 }
keir@19647 541
keir@19647 542 err = vhd_get_bat(vhd);
keir@19647 543 if (err) {
keir@19647 544 printf("error reading bat: %d\n", err);
keir@19647 545 return err;
keir@19647 546 }
keir@19647 547
keir@19647 548 err = vhd_end_of_headers(vhd, &eoh);
keir@19647 549 if (err) {
keir@19647 550 printf("error calculating end of metadata: %d\n", err);
keir@19647 551 return err;
keir@19647 552 }
keir@19647 553
keir@19647 554 eof -= sizeof(vhd_footer_t);
keir@19647 555 eof >>= VHD_SECTOR_SHIFT;
keir@19647 556 eoh >>= VHD_SECTOR_SHIFT;
keir@19647 557 block_size = vhd->spb + vhd->bm_secs;
keir@19647 558
keir@19647 559 for (i = 0; i < vhd->header.max_bat_size; i++) {
keir@19647 560 uint32_t off = vhd->bat.bat[i];
keir@19647 561 if (off == DD_BLK_UNUSED)
keir@19647 562 continue;
keir@19647 563
keir@19647 564 if (off < eoh) {
keir@19647 565 printf("block %d (offset 0x%x) clobbers headers\n",
keir@19647 566 i, off);
keir@19647 567 return -EINVAL;
keir@19647 568 }
keir@19647 569
keir@19647 570 if (off + block_size > eof) {
keir@19647 571 printf("block %d (offset 0x%x) clobbers footer\n",
keir@19647 572 i, off);
keir@19647 573 return -EINVAL;
keir@19647 574 }
keir@19647 575
keir@19647 576 for (j = 0; j < vhd->header.max_bat_size; j++) {
keir@19647 577 uint32_t joff = vhd->bat.bat[j];
keir@19647 578
keir@19647 579 if (i == j)
keir@19647 580 continue;
keir@19647 581
keir@19647 582 if (joff == DD_BLK_UNUSED)
keir@19647 583 continue;
keir@19647 584
keir@19647 585 if (off == joff)
keir@19647 586 err = -EINVAL;
keir@19647 587
keir@19647 588 if (off > joff && off < joff + block_size)
keir@19647 589 err = -EINVAL;
keir@19647 590
keir@19647 591 if (off + block_size > joff &&
keir@19647 592 off + block_size < joff + block_size)
keir@19647 593 err = -EINVAL;
keir@19647 594
keir@19647 595 if (err) {
keir@19647 596 printf("block %d (offset 0x%x) clobbers "
keir@19647 597 "block %d (offset 0x%x)\n",
keir@19647 598 i, off, j, joff);
keir@19647 599 return err;
keir@19647 600 }
keir@19647 601 }
keir@19647 602 }
keir@19647 603
keir@19647 604 return 0;
keir@19647 605 }
keir@19647 606
keir@19647 607 static int
keir@19647 608 vhd_util_check_batmap(vhd_context_t *vhd)
keir@19647 609 {
keir@19647 610 char *msg;
keir@19647 611 int i, err;
keir@19647 612
keir@19647 613 err = vhd_get_bat(vhd);
keir@19647 614 if (err) {
keir@19647 615 printf("error reading bat: %d\n", err);
keir@19647 616 return err;
keir@19647 617 }
keir@19647 618
keir@19647 619 err = vhd_get_batmap(vhd);
keir@19647 620 if (err) {
keir@19647 621 printf("error reading batmap: %d\n", err);
keir@19647 622 return err;
keir@19647 623 }
keir@19647 624
keir@19647 625 msg = vhd_util_check_validate_batmap(vhd, &vhd->batmap);
keir@19647 626 if (msg) {
keir@19647 627 printf("batmap is invalid: %s\n", msg);
keir@19647 628 return -EINVAL;
keir@19647 629 }
keir@19647 630
keir@19647 631 for (i = 0; i < vhd->header.max_bat_size; i++) {
keir@19647 632 if (!vhd_batmap_test(vhd, &vhd->batmap, i))
keir@19647 633 continue;
keir@19647 634
keir@19647 635 if (vhd->bat.bat[i] == DD_BLK_UNUSED) {
keir@19647 636 printf("batmap shows unallocated block %d full\n", i);
keir@19647 637 return -EINVAL;
keir@19647 638 }
keir@19647 639 }
keir@19647 640
keir@19647 641 return 0;
keir@19647 642 }
keir@19647 643
keir@19647 644 static int
keir@19647 645 vhd_util_check_parent_locators(vhd_context_t *vhd)
keir@19647 646 {
keir@19647 647 int i, n, err;
keir@19647 648 vhd_parent_locator_t *loc;
keir@19817 649 char *file, *ppath, *location, *pname;
keir@19817 650 const char *msg;
keir@19647 651 int mac, macx, w2ku, w2ru, wi2r, wi2k, found;
keir@19647 652
keir@19647 653 mac = 0;
keir@19647 654 macx = 0;
keir@19647 655 w2ku = 0;
keir@19647 656 w2ru = 0;
keir@19647 657 wi2r = 0;
keir@19647 658 wi2k = 0;
keir@19647 659 found = 0;
keir@19647 660 pname = NULL;
keir@19647 661 ppath = NULL;
keir@19647 662 location = NULL;
keir@19647 663
keir@19647 664 err = vhd_header_decode_parent(vhd, &vhd->header, &pname);
keir@19647 665 if (err) {
keir@19647 666 printf("error decoding parent name: %d\n", err);
keir@19647 667 return err;
keir@19647 668 }
keir@19647 669
keir@19647 670 n = sizeof(vhd->header.loc) / sizeof(vhd->header.loc[0]);
keir@19647 671 for (i = 0; i < n; i++) {
keir@19647 672 ppath = NULL;
keir@19647 673 location = NULL;
keir@19647 674 loc = vhd->header.loc + i;
keir@19647 675
keir@19647 676 msg = vhd_util_check_validate_parent_locator(vhd, loc);
keir@19647 677 if (msg) {
keir@19647 678 err = -EINVAL;
keir@19647 679 printf("invalid parent locator %d: %s\n", i, msg);
keir@19647 680 goto out;
keir@19647 681 }
keir@19647 682
keir@19647 683 if (loc->code == PLAT_CODE_NONE)
keir@19647 684 continue;
keir@19647 685
keir@19647 686 switch (loc->code) {
keir@19647 687 case PLAT_CODE_MACX:
keir@19647 688 if (macx++)
keir@19647 689 goto dup;
keir@19647 690 break;
keir@19647 691
keir@19647 692 case PLAT_CODE_MAC:
keir@19647 693 if (mac++)
keir@19647 694 goto dup;
keir@19647 695 break;
keir@19647 696
keir@19647 697 case PLAT_CODE_W2KU:
keir@19647 698 if (w2ku++)
keir@19647 699 goto dup;
keir@19647 700 break;
keir@19647 701
keir@19647 702 case PLAT_CODE_W2RU:
keir@19647 703 if (w2ru++)
keir@19647 704 goto dup;
keir@19647 705 break;
keir@19647 706
keir@19647 707 case PLAT_CODE_WI2R:
keir@19647 708 if (wi2r++)
keir@19647 709 goto dup;
keir@19647 710 break;
keir@19647 711
keir@19647 712 case PLAT_CODE_WI2K:
keir@19647 713 if (wi2k++)
keir@19647 714 goto dup;
keir@19647 715 break;
keir@19647 716
keir@19647 717 default:
keir@19647 718 err = -EINVAL;
keir@19647 719 printf("invalid platform code for locator %d\n", i);
keir@19647 720 goto out;
keir@19647 721 }
keir@19647 722
keir@19647 723 if (loc->code != PLAT_CODE_MACX &&
keir@19647 724 loc->code != PLAT_CODE_W2RU &&
keir@19647 725 loc->code != PLAT_CODE_W2KU)
keir@19647 726 continue;
keir@19647 727
keir@19647 728 err = vhd_parent_locator_read(vhd, loc, &ppath);
keir@19647 729 if (err) {
keir@19647 730 printf("error reading parent locator %d: %d\n", i, err);
keir@19647 731 goto out;
keir@19647 732 }
keir@19647 733
keir@19647 734 file = basename(ppath);
keir@19647 735 if (strcmp(pname, file)) {
keir@19647 736 err = -EINVAL;
keir@19647 737 printf("parent locator %d name (%s) does not match "
keir@19647 738 "header name (%s)\n", i, file, pname);
keir@19647 739 goto out;
keir@19647 740 }
keir@19647 741
keir@19647 742 err = vhd_find_parent(vhd, ppath, &location);
keir@19647 743 if (err) {
keir@19647 744 printf("error resolving %s: %d\n", ppath, err);
keir@19647 745 goto out;
keir@19647 746 }
keir@19647 747
keir@19647 748 err = access(location, R_OK);
keir@19647 749 if (err && loc->code == PLAT_CODE_MACX) {
keir@19647 750 err = -errno;
keir@19647 751 printf("parent locator %d points to missing file %s "
keir@19647 752 "(resolved to %s)\n", i, ppath, location);
keir@19647 753 goto out;
keir@19647 754 }
keir@19647 755
keir@19647 756 msg = vhd_util_check_validate_parent(vhd, location);
keir@19647 757 if (msg) {
keir@19647 758 err = -EINVAL;
keir@19647 759 printf("invalid parent %s: %s\n", location, msg);
keir@19647 760 goto out;
keir@19647 761 }
keir@19647 762
keir@19647 763 found++;
keir@19647 764 free(ppath);
keir@19647 765 free(location);
keir@19647 766 ppath = NULL;
keir@19647 767 location = NULL;
keir@19647 768
keir@19647 769 continue;
keir@19647 770
keir@19647 771 dup:
keir@19647 772 printf("duplicate platform code in locator %d: 0x%x\n",
keir@19647 773 i, loc->code);
keir@19647 774 err = -EINVAL;
keir@19647 775 goto out;
keir@19647 776 }
keir@19647 777
keir@19647 778 if (!found) {
keir@19647 779 err = -EINVAL;
keir@19647 780 printf("could not find parent %s\n", pname);
keir@19647 781 goto out;
keir@19647 782 }
keir@19647 783
keir@19647 784 err = 0;
keir@19647 785
keir@19647 786 out:
keir@19647 787 free(pname);
keir@19647 788 free(ppath);
keir@19647 789 free(location);
keir@19647 790 return err;
keir@19647 791 }
keir@19647 792
keir@19647 793 static void
keir@19647 794 vhd_util_dump_headers(const char *name)
keir@19647 795 {
keir@19647 796 char *argv[] = { "read", "-p", "-n", (char *)name };
keir@19647 797 int argc = sizeof(argv) / sizeof(argv[0]);
keir@19647 798
keir@19647 799 printf("%s appears invalid; dumping metadata\n", name);
keir@19647 800 vhd_util_read(argc, argv);
keir@19647 801 }
keir@19647 802
keir@19647 803 static int
keir@19647 804 vhd_util_check_vhd(const char *name, int ignore)
keir@19647 805 {
keir@19647 806 int fd, err;
keir@19647 807 vhd_context_t vhd;
keir@19647 808 struct stat stats;
keir@19647 809 vhd_footer_t footer;
keir@19647 810
keir@19647 811 fd = -1;
keir@19647 812 memset(&vhd, 0, sizeof(vhd));
keir@19647 813
keir@19647 814 err = stat(name, &stats);
keir@19647 815 if (err == -1) {
keir@19647 816 printf("cannot stat %s: %d\n", name, errno);
keir@19647 817 return -errno;
keir@19647 818 }
keir@19647 819
keir@19647 820 if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
keir@19647 821 printf("%s is not a regular file or block device\n", name);
keir@19647 822 return -EINVAL;
keir@19647 823 }
keir@19647 824
keir@19647 825 fd = open(name, O_RDONLY | O_DIRECT | O_LARGEFILE);
keir@19647 826 if (fd == -1) {
keir@19647 827 printf("error opening %s\n", name);
keir@19647 828 return -errno;
keir@19647 829 }
keir@19647 830
keir@19647 831 err = vhd_util_check_footer(fd, &footer, ignore);
keir@19647 832 if (err)
keir@19647 833 goto out;
keir@19647 834
keir@19647 835 if (footer.type != HD_TYPE_DYNAMIC && footer.type != HD_TYPE_DIFF)
keir@19647 836 goto out;
keir@19647 837
keir@19647 838 err = vhd_util_check_header(fd, &footer);
keir@19647 839 if (err)
keir@19647 840 goto out;
keir@19647 841
keir@19647 842 err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
keir@19647 843 if (err)
keir@19647 844 goto out;
keir@19647 845
keir@19647 846 err = vhd_util_check_differencing_header(&vhd);
keir@19647 847 if (err)
keir@19647 848 goto out;
keir@19647 849
keir@19647 850 err = vhd_util_check_bat(&vhd);
keir@19647 851 if (err)
keir@19647 852 goto out;
keir@19647 853
keir@19647 854 if (vhd_has_batmap(&vhd)) {
keir@19647 855 err = vhd_util_check_batmap(&vhd);
keir@19647 856 if (err)
keir@19647 857 goto out;
keir@19647 858 }
keir@19647 859
keir@19647 860 if (vhd.footer.type == HD_TYPE_DIFF) {
keir@19647 861 err = vhd_util_check_parent_locators(&vhd);
keir@19647 862 if (err)
keir@19647 863 goto out;
keir@19647 864 }
keir@19647 865
keir@19647 866 err = 0;
keir@19647 867 printf("%s is valid\n", name);
keir@19647 868
keir@19647 869 out:
keir@19647 870 if (err)
keir@19647 871 vhd_util_dump_headers(name);
keir@19647 872 if (fd != -1)
keir@19647 873 close(fd);
keir@19647 874 vhd_close(&vhd);
keir@19647 875 return err;
keir@19647 876 }
keir@19647 877
keir@19647 878 static int
keir@19647 879 vhd_util_check_parents(const char *name, int ignore)
keir@19647 880 {
keir@19647 881 int err;
keir@19647 882 vhd_context_t vhd;
keir@19647 883 char *cur, *parent;
keir@19647 884
keir@19647 885 cur = (char *)name;
keir@19647 886
keir@19647 887 for (;;) {
keir@19647 888 err = vhd_open(&vhd, cur,
keir@19647 889 VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
keir@19647 890 if (err)
keir@19647 891 goto out;
keir@19647 892
keir@19647 893 if (vhd.footer.type != HD_TYPE_DIFF || vhd_parent_raw(&vhd)) {
keir@19647 894 vhd_close(&vhd);
keir@19647 895 goto out;
keir@19647 896 }
keir@19647 897
keir@19647 898 err = vhd_parent_locator_get(&vhd, &parent);
keir@19647 899 vhd_close(&vhd);
keir@19647 900
keir@19647 901 if (err) {
keir@19647 902 printf("error getting parent: %d\n", err);
keir@19647 903 goto out;
keir@19647 904 }
keir@19647 905
keir@19647 906 if (cur != name)
keir@19647 907 free(cur);
keir@19647 908 cur = parent;
keir@19647 909
keir@19647 910 err = vhd_util_check_vhd(cur, ignore);
keir@19647 911 if (err)
keir@19647 912 goto out;
keir@19647 913 }
keir@19647 914
keir@19647 915 out:
keir@19647 916 if (err)
keir@19647 917 printf("error checking parents: %d\n", err);
keir@19647 918 if (cur != name)
keir@19647 919 free(cur);
keir@19647 920 return err;
keir@19647 921 }
keir@19647 922
keir@19647 923 int
keir@19647 924 vhd_util_check(int argc, char **argv)
keir@19647 925 {
keir@19647 926 char *name;
keir@19647 927 vhd_context_t vhd;
keir@19647 928 int c, err, ignore, parents;
keir@19647 929
keir@19647 930 if (!argc || !argv) {
keir@19647 931 err = -EINVAL;
keir@19647 932 goto usage;
keir@19647 933 }
keir@19647 934
keir@19647 935 ignore = 0;
keir@19647 936 parents = 0;
keir@19647 937 name = NULL;
keir@19647 938
keir@19647 939 optind = 0;
keir@19647 940 while ((c = getopt(argc, argv, "n:iph")) != -1) {
keir@19647 941 switch (c) {
keir@19647 942 case 'n':
keir@19647 943 name = optarg;
keir@19647 944 break;
keir@19647 945 case 'i':
keir@19647 946 ignore = 1;
keir@19647 947 break;
keir@19647 948 case 'p':
keir@19647 949 parents = 1;
keir@19647 950 break;
keir@19647 951 case 'h':
keir@19647 952 err = 0;
keir@19647 953 goto usage;
keir@19647 954 default:
keir@19647 955 err = -EINVAL;
keir@19647 956 goto usage;
keir@19647 957 }
keir@19647 958 }
keir@19647 959
keir@19647 960 if (!name || optind != argc) {
keir@19647 961 err = -EINVAL;
keir@19647 962 goto usage;
keir@19647 963 }
keir@19647 964
keir@19647 965 err = vhd_util_check_vhd(name, ignore);
keir@19647 966 if (err)
keir@19647 967 goto out;
keir@19647 968
keir@19647 969 if (parents)
keir@19647 970 err = vhd_util_check_parents(name, ignore);
keir@19647 971
keir@19647 972 out:
keir@19647 973 return err;
keir@19647 974
keir@19647 975 usage:
keir@19647 976 printf("options: -n <file> [-i ignore missing primary footers] "
keir@19647 977 "[-p check parents] [-h help]\n");
keir@19647 978 return err;
keir@19647 979 }