1 /* Copyright (c) 2008, XenSource Inc. 2 * All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * * Redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer. 8 * * Redistributions in binary form must reproduce the above copyright 9 * notice, this list of conditions and the following disclaimer in the 10 * documentation and/or other materials provided with the distribution. 11 * * Neither the name of XenSource Inc. nor the names of its contributors 12 * may be used to endorse or promote products derived from this software 13 * without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 19 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 24 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <glob.h> 29 #include <errno.h> 30 #include <fcntl.h> 31 #include <stdio.h> 32 #include <string.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <fnmatch.h> 36 #include <libgen.h> /* for basename() */ 37 #include <sys/stat.h> 38 39 #include "list.h" 40 #include "libvhd.h" 41 #include "lvm-util.h" 42 43 #define VHD_SCAN_FAST 0x01 44 #define VHD_SCAN_PRETTY 0x02 45 #define VHD_SCAN_VOLUME 0x04 46 #define VHD_SCAN_NOFAIL 0x08 47 #define VHD_SCAN_VERBOSE 0x10 48 #define VHD_SCAN_PARENTS 0x20 49 50 #define VHD_TYPE_RAW_FILE 0x01 51 #define VHD_TYPE_VHD_FILE 0x02 52 #define VHD_TYPE_RAW_VOLUME 0x04 53 #define VHD_TYPE_VHD_VOLUME 0x08 54 55 static inline int target_volume(uint8_t type)56 target_volume(uint8_t type) 57 { 58 return (type == VHD_TYPE_RAW_VOLUME || type == VHD_TYPE_VHD_VOLUME); 59 } 60 61 static inline int target_vhd(uint8_t type)62 target_vhd(uint8_t type) 63 { 64 return (type == VHD_TYPE_VHD_FILE || type == VHD_TYPE_VHD_VOLUME); 65 } 66 67 struct target { 68 char name[VHD_MAX_NAME_LEN]; 69 char device[VHD_MAX_NAME_LEN]; 70 uint64_t size; 71 uint64_t start; 72 uint64_t end; 73 uint8_t type; 74 }; 75 76 struct iterator { 77 int cur; 78 int cur_size; 79 int max_size; 80 struct target *targets; 81 }; 82 83 struct vhd_image { 84 char *name; 85 char *parent; 86 uint64_t capacity; 87 off_t size; 88 uint8_t hidden; 89 int error; 90 char *message; 91 92 struct target *target; 93 94 struct list_head sibling; 95 struct list_head children; 96 struct vhd_image *parent_image; 97 }; 98 99 struct vhd_scan { 100 int cur; 101 int size; 102 103 int lists_cur; 104 int lists_size; 105 106 struct vhd_image **images; 107 struct vhd_image **lists; 108 }; 109 110 static int flags; 111 static struct vg vg; 112 static struct vhd_scan scan; 113 114 static int vhd_util_scan_pretty_allocate_list(int cnt)115 vhd_util_scan_pretty_allocate_list(int cnt) 116 { 117 int i; 118 struct vhd_image *list; 119 120 memset(&scan, 0, sizeof(scan)); 121 122 scan.lists_cur = 1; 123 scan.lists_size = 10; 124 125 scan.lists = calloc(scan.lists_size, sizeof(struct vhd_image *)); 126 if (!scan.lists) 127 goto fail; 128 129 scan.lists[0] = calloc(cnt, sizeof(struct vhd_image)); 130 if (!scan.lists[0]) 131 goto fail; 132 133 scan.images = calloc(cnt, sizeof(struct vhd_image *)); 134 if (!scan.images) 135 goto fail; 136 137 for (i = 0; i < cnt; i++) 138 scan.images[i] = scan.lists[0] + i; 139 140 scan.cur = 0; 141 scan.size = cnt; 142 143 return 0; 144 145 fail: 146 if (scan.lists) { 147 free(scan.lists[0]); 148 free(scan.lists); 149 } 150 151 free(scan.images); 152 memset(&scan, 0, sizeof(scan)); 153 return -ENOMEM; 154 } 155 156 static void vhd_util_scan_pretty_free_list(void)157 vhd_util_scan_pretty_free_list(void) 158 { 159 int i; 160 161 if (scan.lists) { 162 for (i = 0; i < scan.lists_cur; i++) 163 free(scan.lists[i]); 164 free(scan.lists); 165 } 166 167 free(scan.images); 168 memset(&scan, 0, sizeof(scan)); 169 } 170 171 static int vhd_util_scan_pretty_add_image(struct vhd_image * image)172 vhd_util_scan_pretty_add_image(struct vhd_image *image) 173 { 174 int i; 175 struct vhd_image *img; 176 177 for (i = 0; i < scan.cur; i++) { 178 img = scan.images[i]; 179 if (!strcmp(img->name, image->name)) 180 return 0; 181 } 182 183 if (scan.cur >= scan.size) { 184 struct vhd_image *new, **list; 185 186 if (scan.lists_cur >= scan.lists_size) { 187 list = realloc(scan.lists, scan.lists_size * 2 * 188 sizeof(struct vhd_image *)); 189 if (!list) 190 return -ENOMEM; 191 192 scan.lists_size *= 2; 193 scan.lists = list; 194 } 195 196 new = calloc(scan.size, sizeof(struct vhd_image)); 197 if (!new) 198 return -ENOMEM; 199 200 scan.lists[scan.lists_cur++] = new; 201 scan.size *= 2; 202 203 list = realloc(scan.images, scan.size * 204 sizeof(struct vhd_image *)); 205 if (!list) 206 return -ENOMEM; 207 208 scan.images = list; 209 for (i = 0; i + scan.cur < scan.size; i++) 210 scan.images[i + scan.cur] = new + i; 211 } 212 213 img = scan.images[scan.cur]; 214 INIT_LIST_HEAD(&img->sibling); 215 INIT_LIST_HEAD(&img->children); 216 217 img->capacity = image->capacity; 218 img->size = image->size; 219 img->hidden = image->hidden; 220 img->error = image->error; 221 img->message = image->message; 222 223 img->name = strdup(image->name); 224 if (!img->name) 225 goto fail; 226 227 if (image->parent) { 228 img->parent = strdup(image->parent); 229 if (!img->parent) 230 goto fail; 231 } 232 233 scan.cur++; 234 return 0; 235 236 fail: 237 free(img->name); 238 free(img->parent); 239 memset(img, 0, sizeof(*img)); 240 return -ENOMEM; 241 } 242 243 static int vhd_util_scan_pretty_image_compare(const void * lhs,const void * rhs)244 vhd_util_scan_pretty_image_compare(const void *lhs, const void *rhs) 245 { 246 struct vhd_image *l, *r; 247 248 l = *(struct vhd_image **)lhs; 249 r = *(struct vhd_image **)rhs; 250 251 return strcmp(l->name, r->name); 252 } 253 254 static void vhd_util_scan_print_image_indent(struct vhd_image * image,int tab)255 vhd_util_scan_print_image_indent(struct vhd_image *image, int tab) 256 { 257 char *pad, *name, *pmsg, *parent; 258 259 pad = (tab ? " " : ""); 260 name = image->name; 261 parent = (image->parent ? : "none"); 262 263 if ((flags & VHD_SCAN_PRETTY) && image->parent && !image->parent_image) 264 pmsg = " (not found in scan)"; 265 else 266 pmsg = ""; 267 268 if (!(flags & VHD_SCAN_VERBOSE)) { 269 name = basename(image->name); 270 if (image->parent) 271 parent = basename(image->parent); 272 } 273 274 if (image->error) 275 printf("%*svhd=%s scan-error=%d error-message='%s'\n", 276 tab, pad, image->name, image->error, image->message); 277 else 278 printf("%*svhd=%s capacity=%"PRIu64" size=%"PRIu64" hidden=%u " 279 "parent=%s%s\n", tab, pad, name, image->capacity, 280 image->size, image->hidden, parent, pmsg); 281 } 282 283 static void vhd_util_scan_pretty_print_tree(struct vhd_image * image,int depth)284 vhd_util_scan_pretty_print_tree(struct vhd_image *image, int depth) 285 { 286 struct vhd_image *img, *tmp; 287 288 vhd_util_scan_print_image_indent(image, depth * 3); 289 290 list_for_each_entry_safe(img, tmp, &image->children, sibling) 291 if (!img->hidden) 292 vhd_util_scan_pretty_print_tree(img, depth + 1); 293 294 list_for_each_entry_safe(img, tmp, &image->children, sibling) 295 if (img->hidden) 296 vhd_util_scan_pretty_print_tree(img, depth + 1); 297 298 free(image->name); 299 free(image->parent); 300 301 image->name = NULL; 302 image->parent = NULL; 303 } 304 305 static void vhd_util_scan_pretty_print_images(void)306 vhd_util_scan_pretty_print_images(void) 307 { 308 int i; 309 struct vhd_image *image, **parentp, *parent, *keyp, key; 310 311 qsort(scan.images, scan.cur, sizeof(scan.images[0]), 312 vhd_util_scan_pretty_image_compare); 313 314 for (i = 0; i < scan.cur; i++) { 315 image = scan.images[i]; 316 317 if (!image->parent) { 318 image->parent_image = NULL; 319 continue; 320 } 321 322 memset(&key, 0, sizeof(key)); 323 key.name = image->parent; 324 keyp = &key; 325 326 parentp = bsearch(&keyp, scan.images, scan.cur, 327 sizeof(scan.images[0]), 328 vhd_util_scan_pretty_image_compare); 329 if (!parentp) { 330 image->parent_image = NULL; 331 continue; 332 } 333 334 parent = *parentp; 335 image->parent_image = parent; 336 list_add_tail(&image->sibling, &parent->children); 337 } 338 339 for (i = 0; i < scan.cur; i++) { 340 image = scan.images[i]; 341 342 if (image->parent_image || !image->hidden) 343 continue; 344 345 vhd_util_scan_pretty_print_tree(image, 0); 346 } 347 348 for (i = 0; i < scan.cur; i++) { 349 image = scan.images[i]; 350 351 if (!image->name || image->parent_image) 352 continue; 353 354 vhd_util_scan_pretty_print_tree(image, 0); 355 } 356 357 for (i = 0; i < scan.cur; i++) { 358 image = scan.images[i]; 359 360 if (!image->name) 361 continue; 362 363 vhd_util_scan_pretty_print_tree(image, 0); 364 } 365 } 366 367 static void vhd_util_scan_print_image(struct vhd_image * image)368 vhd_util_scan_print_image(struct vhd_image *image) 369 { 370 int err; 371 372 if (!image->error && (flags & VHD_SCAN_PRETTY)) { 373 err = vhd_util_scan_pretty_add_image(image); 374 if (!err) 375 return; 376 377 if (!image->error) { 378 image->error = err; 379 image->message = "allocating memory"; 380 } 381 } 382 383 vhd_util_scan_print_image_indent(image, 0); 384 } 385 386 static int vhd_util_scan_error(const char * file,int err)387 vhd_util_scan_error(const char *file, int err) 388 { 389 struct vhd_image image; 390 391 memset(&image, 0, sizeof(image)); 392 image.name = (char *)file; 393 image.error = err; 394 image.message = "failure scanning target"; 395 396 vhd_util_scan_print_image(&image); 397 398 /* 399 if (flags & VHD_SCAN_NOFAIL) 400 return 0; 401 */ 402 403 return err; 404 } 405 406 static vhd_parent_locator_t * vhd_util_scan_get_parent_locator(vhd_context_t * vhd)407 vhd_util_scan_get_parent_locator(vhd_context_t *vhd) 408 { 409 int i; 410 vhd_parent_locator_t *loc; 411 412 loc = NULL; 413 414 for (i = 0; i < 8; i++) { 415 if (vhd->header.loc[i].code == PLAT_CODE_MACX) { 416 loc = vhd->header.loc + i; 417 break; 418 } 419 420 if (vhd->header.loc[i].code == PLAT_CODE_W2RU) 421 loc = vhd->header.loc + i; 422 423 if (!loc && vhd->header.loc[i].code != PLAT_CODE_NONE) 424 loc = vhd->header.loc + i; 425 } 426 427 return loc; 428 } 429 430 static inline int copy_name(char * dst,const char * src)431 copy_name(char *dst, const char *src) 432 { 433 if (snprintf(dst, VHD_MAX_NAME_LEN, "%s", src) < VHD_MAX_NAME_LEN) 434 return 0; 435 436 return -ENAMETOOLONG; 437 } 438 439 /* 440 * LVHD stores realpath(parent) in parent locators, so 441 * /dev/<vol-group>/<lv-name> becomes /dev/mapper/<vol--group>-<lv--name> 442 */ 443 static int vhd_util_scan_extract_volume_name(char * dst,const char * src)444 vhd_util_scan_extract_volume_name(char *dst, const char *src) 445 { 446 int err; 447 char copy[VHD_MAX_NAME_LEN], *name, *s, *c; 448 449 name = strrchr(src, '/'); 450 if (!name) 451 name = (char *)src; 452 453 /* convert single dashes to slashes, double dashes to single dashes */ 454 for (c = copy, s = name; *s != '\0'; s++, c++) { 455 if (*s == '-') { 456 if (s[1] != '-') 457 *c = '/'; 458 else { 459 s++; 460 *c = '-'; 461 } 462 } else 463 *c = *s; 464 } 465 466 *c = '\0'; 467 c = strrchr(copy, '/'); 468 if (c == name) { 469 /* unrecognized format */ 470 strcpy(dst, src); 471 return -EINVAL; 472 } 473 474 strcpy(dst, ++c); 475 return 0; 476 } 477 478 static int vhd_util_scan_get_volume_parent(vhd_context_t * vhd,struct vhd_image * image)479 vhd_util_scan_get_volume_parent(vhd_context_t *vhd, struct vhd_image *image) 480 { 481 int err; 482 char name[VHD_MAX_NAME_LEN]; 483 vhd_parent_locator_t *loc, copy; 484 485 if (flags & VHD_SCAN_FAST) { 486 err = vhd_header_decode_parent(vhd, 487 &vhd->header, &image->parent); 488 if (!err) 489 goto found; 490 } 491 492 loc = vhd_util_scan_get_parent_locator(vhd); 493 if (!loc) 494 return -EINVAL; 495 496 copy = *loc; 497 copy.data_offset += image->target->start; 498 err = vhd_parent_locator_read(vhd, ©, &image->parent); 499 if (err) 500 return err; 501 502 found: 503 err = vhd_util_scan_extract_volume_name(name, image->parent); 504 if (!err) 505 return copy_name(image->parent, name); 506 507 return 0; 508 } 509 510 static int vhd_util_scan_get_parent(vhd_context_t * vhd,struct vhd_image * image)511 vhd_util_scan_get_parent(vhd_context_t *vhd, struct vhd_image *image) 512 { 513 int i, err; 514 vhd_parent_locator_t *loc; 515 516 if (!target_vhd(image->target->type)) { 517 image->parent = NULL; 518 return 0; 519 } 520 521 loc = NULL; 522 523 if (target_volume(image->target->type)) 524 return vhd_util_scan_get_volume_parent(vhd, image); 525 526 if (flags & VHD_SCAN_FAST) { 527 err = vhd_header_decode_parent(vhd, 528 &vhd->header, &image->parent); 529 if (!err) 530 return 0; 531 } else { 532 /* 533 * vhd_parent_locator_get checks for the existence of the 534 * parent file. if this call succeeds, all is well; if not, 535 * we'll try to return whatever string we have before failing 536 * outright. 537 */ 538 err = vhd_parent_locator_get(vhd, &image->parent); 539 if (!err) 540 return 0; 541 } 542 543 loc = vhd_util_scan_get_parent_locator(vhd); 544 if (!loc) 545 return -EINVAL; 546 547 return vhd_parent_locator_read(vhd, loc, &image->parent); 548 } 549 550 static int vhd_util_scan_get_hidden(vhd_context_t * vhd,struct vhd_image * image)551 vhd_util_scan_get_hidden(vhd_context_t *vhd, struct vhd_image *image) 552 { 553 int err, hidden; 554 555 err = 0; 556 hidden = 0; 557 558 if (target_vhd(image->target->type)) 559 err = vhd_hidden(vhd, &hidden); 560 else 561 hidden = 1; 562 563 if (err) 564 return err; 565 566 image->hidden = hidden; 567 return 0; 568 } 569 570 static int vhd_util_scan_get_size(vhd_context_t * vhd,struct vhd_image * image)571 vhd_util_scan_get_size(vhd_context_t *vhd, struct vhd_image *image) 572 { 573 image->size = image->target->size; 574 575 if (target_vhd(image->target->type)) 576 image->capacity = vhd->footer.curr_size; 577 else 578 image->capacity = image->size; 579 580 return 0; 581 } 582 583 static int vhd_util_scan_open_file(vhd_context_t * vhd,struct vhd_image * image)584 vhd_util_scan_open_file(vhd_context_t *vhd, struct vhd_image *image) 585 { 586 int err, vhd_flags; 587 588 if (!target_vhd(image->target->type)) 589 return 0; 590 591 vhd_flags = VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED; 592 if (flags & VHD_SCAN_FAST) 593 vhd_flags |= VHD_OPEN_FAST; 594 595 err = vhd_open(vhd, image->name, vhd_flags); 596 if (err) { 597 vhd->file = NULL; 598 image->message = "opening file"; 599 image->error = err; 600 return image->error; 601 } 602 603 return 0; 604 } 605 606 static int vhd_util_scan_read_volume_headers(vhd_context_t * vhd,struct vhd_image * image)607 vhd_util_scan_read_volume_headers(vhd_context_t *vhd, struct vhd_image *image) 608 { 609 int err; 610 char *buf; 611 size_t size; 612 struct target *target; 613 614 buf = NULL; 615 target = image->target; 616 size = sizeof(vhd_footer_t) + sizeof(vhd_header_t); 617 618 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); 619 if (err) { 620 buf = NULL; 621 image->message = "allocating image"; 622 image->error = -err; 623 goto out; 624 } 625 626 err = vhd_seek(vhd, target->start, SEEK_SET); 627 if (err) { 628 image->message = "seeking to headers"; 629 image->error = err; 630 goto out; 631 } 632 633 err = vhd_read(vhd, buf, size); 634 if (err) { 635 image->message = "reading headers"; 636 image->error = err; 637 goto out; 638 } 639 640 memcpy(&vhd->footer, buf, sizeof(vhd_footer_t)); 641 vhd_footer_in(&vhd->footer); 642 err = vhd_validate_footer(&vhd->footer); 643 if (err) { 644 image->message = "invalid footer"; 645 image->error = err; 646 goto out; 647 } 648 649 /* lvhd vhds should always be dynamic */ 650 if (vhd_type_dynamic(vhd)) { 651 if (vhd->footer.data_offset != sizeof(vhd_footer_t)) 652 err = vhd_read_header_at(vhd, &vhd->header, 653 vhd->footer.data_offset + 654 target->start); 655 else { 656 memcpy(&vhd->header, 657 buf + sizeof(vhd_footer_t), 658 sizeof(vhd_header_t)); 659 vhd_header_in(&vhd->header); 660 err = vhd_validate_header(&vhd->header); 661 } 662 663 if (err) { 664 image->message = "reading header"; 665 image->error = err; 666 goto out; 667 } 668 669 vhd->spb = vhd->header.block_size >> VHD_SECTOR_SHIFT; 670 vhd->bm_secs = secs_round_up_no_zero(vhd->spb >> 3); 671 } 672 673 out: 674 free(buf); 675 return image->error; 676 } 677 678 static int vhd_util_scan_open_volume(vhd_context_t * vhd,struct vhd_image * image)679 vhd_util_scan_open_volume(vhd_context_t *vhd, struct vhd_image *image) 680 { 681 int err; 682 struct target *target; 683 684 target = image->target; 685 memset(vhd, 0, sizeof(*vhd)); 686 vhd->oflags = VHD_OPEN_RDONLY | VHD_OPEN_FAST; 687 688 if (target->end - target->start < 4096) { 689 image->message = "device too small"; 690 image->error = -EINVAL; 691 return image->error; 692 } 693 694 vhd->file = strdup(image->name); 695 if (!vhd->file) { 696 image->message = "allocating device"; 697 image->error = -ENOMEM; 698 return image->error; 699 } 700 701 vhd->fd = open(target->device, O_RDONLY | O_DIRECT | O_LARGEFILE); 702 if (vhd->fd == -1) { 703 free(vhd->file); 704 vhd->file = NULL; 705 706 image->message = "opening device"; 707 image->error = -errno; 708 return image->error; 709 } 710 711 if (target_vhd(target->type)) 712 return vhd_util_scan_read_volume_headers(vhd, image); 713 714 return 0; 715 } 716 717 static int vhd_util_scan_open(vhd_context_t * vhd,struct vhd_image * image)718 vhd_util_scan_open(vhd_context_t *vhd, struct vhd_image *image) 719 { 720 struct target *target; 721 722 target = image->target; 723 724 if (target_volume(image->target->type) || !(flags & VHD_SCAN_PRETTY)) 725 image->name = target->name; 726 else { 727 image->name = realpath(target->name, NULL); 728 if (!image->name) { 729 image->name = target->name; 730 image->message = "resolving name"; 731 image->error = -errno; 732 return image->error; 733 } 734 } 735 736 if (target_volume(target->type)) 737 return vhd_util_scan_open_volume(vhd, image); 738 else 739 return vhd_util_scan_open_file(vhd, image); 740 } 741 742 static int vhd_util_scan_init_file_target(struct target * target,const char * file,uint8_t type)743 vhd_util_scan_init_file_target(struct target *target, 744 const char *file, uint8_t type) 745 { 746 int err; 747 struct stat stats; 748 749 err = stat(file, &stats); 750 if (err == -1) 751 return -errno; 752 753 err = copy_name(target->name, file); 754 if (err) 755 return err; 756 757 err = copy_name(target->device, file); 758 if (err) 759 return err; 760 761 target->type = type; 762 target->start = 0; 763 target->size = stats.st_size; 764 target->end = stats.st_size; 765 766 return 0; 767 } 768 769 static int vhd_util_scan_init_volume_target(struct target * target,struct lv * lv,uint8_t type)770 vhd_util_scan_init_volume_target(struct target *target, 771 struct lv *lv, uint8_t type) 772 { 773 int err; 774 775 if (lv->first_segment.type != LVM_SEG_TYPE_LINEAR) 776 return -ENOSYS; 777 778 err = copy_name(target->name, lv->name); 779 if (err) 780 return err; 781 782 err = copy_name(target->device, lv->first_segment.device); 783 if (err) 784 return err; 785 786 target->type = type; 787 target->size = lv->size; 788 target->start = lv->first_segment.pe_start; 789 target->end = target->start + lv->first_segment.pe_size; 790 791 return 0; 792 } 793 794 static int iterator_init(struct iterator * itr,int cnt,struct target * targets)795 iterator_init(struct iterator *itr, int cnt, struct target *targets) 796 { 797 memset(itr, 0, sizeof(*itr)); 798 799 itr->targets = malloc(sizeof(struct target) * cnt); 800 if (!itr->targets) 801 return -ENOMEM; 802 803 memcpy(itr->targets, targets, sizeof(struct target) * cnt); 804 805 itr->cur = 0; 806 itr->cur_size = cnt; 807 itr->max_size = cnt; 808 809 return 0; 810 } 811 812 static struct target * iterator_next(struct iterator * itr)813 iterator_next(struct iterator *itr) 814 { 815 if (itr->cur == itr->cur_size) 816 return NULL; 817 818 return itr->targets + itr->cur++; 819 } 820 821 static int iterator_add_file(struct iterator * itr,struct target * target,const char * parent,uint8_t type)822 iterator_add_file(struct iterator *itr, 823 struct target *target, const char *parent, uint8_t type) 824 { 825 int i; 826 struct target *t; 827 char *lname, *rname; 828 829 for (i = 0; i < itr->cur_size; i++) { 830 t = itr->targets + i; 831 lname = basename((char *)t->name); 832 rname = basename((char *)parent); 833 834 if (!strcmp(lname, rname)) 835 return -EEXIST; 836 } 837 838 return vhd_util_scan_init_file_target(target, parent, type); 839 } 840 841 static int iterator_add_volume(struct iterator * itr,struct target * target,const char * parent,uint8_t type)842 iterator_add_volume(struct iterator *itr, 843 struct target *target, const char *parent, uint8_t type) 844 { 845 int i, err; 846 struct lv *lv; 847 848 lv = NULL; 849 err = -ENOENT; 850 851 for (i = 0; i < itr->cur_size; i++) 852 if (!strcmp(parent, itr->targets[i].name)) 853 return -EEXIST; 854 855 for (i = 0; i < vg.lv_cnt; i++) { 856 err = fnmatch(parent, vg.lvs[i].name, FNM_PATHNAME); 857 if (err != FNM_NOMATCH) { 858 lv = vg.lvs + i; 859 break; 860 } 861 } 862 863 if (err && err != FNM_PATHNAME) 864 return err; 865 866 if (!lv) 867 return -ENOENT; 868 869 return vhd_util_scan_init_volume_target(target, lv, type); 870 } 871 872 static int iterator_add(struct iterator * itr,const char * parent,uint8_t type)873 iterator_add(struct iterator *itr, const char *parent, uint8_t type) 874 { 875 int err; 876 struct target *target; 877 878 if (itr->cur_size == itr->max_size) { 879 struct target *new; 880 881 new = realloc(itr->targets, 882 sizeof(struct target) * 883 itr->max_size * 2); 884 if (!new) 885 return -ENOMEM; 886 887 itr->max_size *= 2; 888 itr->targets = new; 889 } 890 891 target = itr->targets + itr->cur_size; 892 893 if (target_volume(type)) 894 err = iterator_add_volume(itr, target, parent, type); 895 else 896 err = iterator_add_file(itr, target, parent, type); 897 898 if (err) 899 memset(target, 0, sizeof(*target)); 900 else 901 itr->cur_size++; 902 903 return (err == -EEXIST ? 0 : err); 904 } 905 906 static void iterator_free(struct iterator * itr)907 iterator_free(struct iterator *itr) 908 { 909 free(itr->targets); 910 memset(itr, 0, sizeof(*itr)); 911 } 912 913 static void vhd_util_scan_add_parent(struct iterator * itr,vhd_context_t * vhd,struct vhd_image * image)914 vhd_util_scan_add_parent(struct iterator *itr, 915 vhd_context_t *vhd, struct vhd_image *image) 916 { 917 int err; 918 uint8_t type; 919 920 if (vhd_parent_raw(vhd)) 921 type = target_volume(image->target->type) ? 922 VHD_TYPE_RAW_VOLUME : VHD_TYPE_RAW_FILE; 923 else 924 type = target_volume(image->target->type) ? 925 VHD_TYPE_VHD_VOLUME : VHD_TYPE_VHD_FILE; 926 927 err = iterator_add(itr, image->parent, type); 928 if (err) 929 vhd_util_scan_error(image->parent, err); 930 } 931 932 static int vhd_util_scan_targets(int cnt,struct target * targets)933 vhd_util_scan_targets(int cnt, struct target *targets) 934 { 935 int ret, err; 936 vhd_context_t vhd; 937 struct iterator itr; 938 struct target *target; 939 struct vhd_image image; 940 941 ret = 0; 942 err = 0; 943 944 err = iterator_init(&itr, cnt, targets); 945 if (err) 946 return err; 947 948 while ((target = iterator_next(&itr))) { 949 memset(&vhd, 0, sizeof(vhd)); 950 memset(&image, 0, sizeof(image)); 951 952 image.target = target; 953 954 err = vhd_util_scan_open(&vhd, &image); 955 if (err) { 956 ret = -EAGAIN; 957 goto end; 958 } 959 960 err = vhd_util_scan_get_size(&vhd, &image); 961 if (err) { 962 ret = -EAGAIN; 963 image.message = "getting physical size"; 964 image.error = err; 965 goto end; 966 } 967 968 err = vhd_util_scan_get_hidden(&vhd, &image); 969 if (err) { 970 ret = -EAGAIN; 971 image.message = "checking 'hidden' field"; 972 image.error = err; 973 goto end; 974 } 975 976 if (vhd.footer.type == HD_TYPE_DIFF) { 977 err = vhd_util_scan_get_parent(&vhd, &image); 978 if (err) { 979 ret = -EAGAIN; 980 image.message = "getting parent"; 981 image.error = err; 982 goto end; 983 } 984 } 985 986 end: 987 vhd_util_scan_print_image(&image); 988 989 if (flags & VHD_SCAN_PARENTS && image.parent) 990 vhd_util_scan_add_parent(&itr, &vhd, &image); 991 992 if (vhd.file) 993 vhd_close(&vhd); 994 if (image.name != target->name) 995 free(image.name); 996 free(image.parent); 997 998 if (err && !(flags & VHD_SCAN_NOFAIL)) 999 break; 1000 } 1001 1002 iterator_free(&itr); 1003 1004 if (flags & VHD_SCAN_NOFAIL) 1005 return ret; 1006 1007 return err; 1008 } 1009 1010 static int vhd_util_scan_targets_pretty(int cnt,struct target * targets)1011 vhd_util_scan_targets_pretty(int cnt, struct target *targets) 1012 { 1013 int err; 1014 1015 err = vhd_util_scan_pretty_allocate_list(cnt); 1016 if (err) { 1017 printf("scan failed: no memory\n"); 1018 return -ENOMEM; 1019 } 1020 1021 err = vhd_util_scan_targets(cnt, targets); 1022 1023 vhd_util_scan_pretty_print_images(); 1024 vhd_util_scan_pretty_free_list(); 1025 1026 return ((flags & VHD_SCAN_NOFAIL) ? 0 : err); 1027 } 1028 1029 static int vhd_util_scan_find_file_targets(int cnt,char ** names,const char * filter,struct target ** _targets,int * _total)1030 vhd_util_scan_find_file_targets(int cnt, char **names, 1031 const char *filter, 1032 struct target **_targets, int *_total) 1033 { 1034 glob_t g; 1035 struct target *targets; 1036 int i, globs, err, total; 1037 1038 total = cnt; 1039 globs = 0; 1040 *_total = 0; 1041 *_targets = NULL; 1042 1043 memset(&g, 0, sizeof(g)); 1044 1045 if (filter) { 1046 int gflags = ((flags & VHD_SCAN_FAST) ? GLOB_NOSORT : 0); 1047 1048 errno = 0; 1049 err = glob(filter, gflags, vhd_util_scan_error, &g); 1050 1051 switch (err) { 1052 case GLOB_NOSPACE: 1053 err = -ENOMEM; 1054 break; 1055 case GLOB_ABORTED: 1056 err = -EIO; 1057 break; 1058 case GLOB_NOMATCH: 1059 err = -errno; 1060 break; 1061 } 1062 1063 if (err) { 1064 vhd_util_scan_error(filter, err); 1065 return err; 1066 } 1067 1068 globs = g.gl_pathc; 1069 total += globs; 1070 } 1071 1072 targets = calloc(total, sizeof(struct target)); 1073 if (!targets) { 1074 err = -ENOMEM; 1075 goto out; 1076 } 1077 1078 for (i = 0; i < g.gl_pathc; i++) { 1079 err = vhd_util_scan_init_file_target(targets + i, 1080 g.gl_pathv[i], 1081 VHD_TYPE_VHD_FILE); 1082 if (err) { 1083 vhd_util_scan_error(g.gl_pathv[i], err); 1084 if (!(flags & VHD_SCAN_NOFAIL)) 1085 goto out; 1086 } 1087 } 1088 1089 for (i = 0; i + globs < total; i++) { 1090 err = vhd_util_scan_init_file_target(targets + i + globs, 1091 names[i], 1092 VHD_TYPE_VHD_FILE); 1093 if (err) { 1094 vhd_util_scan_error(names[i], err); 1095 if (!(flags & VHD_SCAN_NOFAIL)) 1096 goto out; 1097 } 1098 } 1099 1100 err = 0; 1101 *_total = total; 1102 *_targets = targets; 1103 1104 out: 1105 if (err) 1106 free(targets); 1107 if (filter) 1108 globfree(&g); 1109 1110 return err; 1111 } 1112 1113 static inline void swap_volume(struct lv * lvs,int dst,int src)1114 swap_volume(struct lv *lvs, int dst, int src) 1115 { 1116 struct lv copy, *ldst, *lsrc; 1117 1118 if (dst == src) 1119 return; 1120 1121 lsrc = lvs + src; 1122 ldst = lvs + dst; 1123 1124 memcpy(©, ldst, sizeof(copy)); 1125 memcpy(ldst, lsrc, sizeof(*ldst)); 1126 memcpy(lsrc, ©, sizeof(copy)); 1127 } 1128 1129 static int vhd_util_scan_sort_volumes(struct lv * lvs,int cnt,const char * filter,int * _matches)1130 vhd_util_scan_sort_volumes(struct lv *lvs, int cnt, 1131 const char *filter, int *_matches) 1132 { 1133 struct lv *lv; 1134 int i, err, matches; 1135 1136 matches = 0; 1137 *_matches = 0; 1138 1139 if (!filter) 1140 return 0; 1141 1142 for (i = 0; i < cnt; i++) { 1143 lv = lvs + i; 1144 1145 err = fnmatch(filter, lv->name, FNM_PATHNAME); 1146 if (err) { 1147 if (err != FNM_NOMATCH) { 1148 vhd_util_scan_error(lv->name, err); 1149 if (!(flags & VHD_SCAN_NOFAIL)) 1150 return err; 1151 } 1152 1153 continue; 1154 } 1155 1156 swap_volume(lvs, matches++, i); 1157 } 1158 1159 *_matches = matches; 1160 return 0; 1161 } 1162 1163 static int vhd_util_scan_find_volume_targets(int cnt,char ** names,const char * volume,const char * filter,struct target ** _targets,int * _total)1164 vhd_util_scan_find_volume_targets(int cnt, char **names, 1165 const char *volume, const char *filter, 1166 struct target **_targets, int *_total) 1167 { 1168 struct target *targets; 1169 int i, err, total, matches; 1170 1171 *_total = 0; 1172 *_targets = NULL; 1173 targets = NULL; 1174 1175 err = lvm_scan_vg(volume, &vg); 1176 if (err) 1177 return err; 1178 1179 err = vhd_util_scan_sort_volumes(vg.lvs, vg.lv_cnt, 1180 filter, &matches); 1181 if (err) 1182 goto out; 1183 1184 total = matches; 1185 for (i = 0; i < cnt; i++) { 1186 err = vhd_util_scan_sort_volumes(vg.lvs + total, 1187 vg.lv_cnt - total, 1188 names[i], &matches); 1189 if (err) 1190 goto out; 1191 1192 total += matches; 1193 } 1194 1195 targets = calloc(total, sizeof(struct target)); 1196 if (!targets) { 1197 err = -ENOMEM; 1198 goto out; 1199 } 1200 1201 for (i = 0; i < total; i++) { 1202 err = vhd_util_scan_init_volume_target(targets + i, 1203 vg.lvs + i, 1204 VHD_TYPE_VHD_VOLUME); 1205 if (err) { 1206 vhd_util_scan_error(vg.lvs[i].name, err); 1207 if (!(flags & VHD_SCAN_NOFAIL)) 1208 goto out; 1209 } 1210 } 1211 1212 err = 0; 1213 *_total = total; 1214 *_targets = targets; 1215 1216 out: 1217 if (err) 1218 free(targets); 1219 return err; 1220 } 1221 1222 static int vhd_util_scan_find_targets(int cnt,char ** names,const char * volume,const char * filter,struct target ** targets,int * total)1223 vhd_util_scan_find_targets(int cnt, char **names, 1224 const char *volume, const char *filter, 1225 struct target **targets, int *total) 1226 { 1227 if (flags & VHD_SCAN_VOLUME) 1228 return vhd_util_scan_find_volume_targets(cnt, names, 1229 volume, filter, 1230 targets, total); 1231 return vhd_util_scan_find_file_targets(cnt, names, 1232 filter, targets, total); 1233 } 1234 1235 int vhd_util_scan(int argc,char ** argv)1236 vhd_util_scan(int argc, char **argv) 1237 { 1238 int c, ret, err, cnt; 1239 char *filter, *volume; 1240 struct target *targets; 1241 1242 cnt = 0; 1243 ret = 0; 1244 err = 0; 1245 flags = 0; 1246 filter = NULL; 1247 volume = NULL; 1248 targets = NULL; 1249 1250 optind = 0; 1251 while ((c = getopt(argc, argv, "m:fcl:pavh")) != -1) { 1252 switch (c) { 1253 case 'm': 1254 filter = optarg; 1255 break; 1256 case 'f': 1257 flags |= VHD_SCAN_FAST; 1258 break; 1259 case 'c': 1260 flags |= VHD_SCAN_NOFAIL; 1261 break; 1262 case 'l': 1263 volume = optarg; 1264 flags |= VHD_SCAN_VOLUME; 1265 break; 1266 case 'p': 1267 flags |= VHD_SCAN_PRETTY; 1268 break; 1269 case 'a': 1270 flags |= VHD_SCAN_PARENTS; 1271 break; 1272 case 'v': 1273 flags |= VHD_SCAN_VERBOSE; 1274 break; 1275 case 'h': 1276 goto usage; 1277 default: 1278 err = -EINVAL; 1279 goto usage; 1280 } 1281 } 1282 1283 if (!filter && argc - optind == 0) { 1284 err = -EINVAL; 1285 goto usage; 1286 } 1287 1288 if (flags & VHD_SCAN_PRETTY) 1289 flags &= ~VHD_SCAN_FAST; 1290 1291 err = vhd_util_scan_find_targets(argc - optind, argv + optind, 1292 volume, filter, &targets, &cnt); 1293 if (err) { 1294 printf("scan failed: %d\n", err); 1295 return err; 1296 } 1297 1298 if (!cnt) 1299 return 0; 1300 1301 if (flags & VHD_SCAN_PRETTY) 1302 err = vhd_util_scan_targets_pretty(cnt, targets); 1303 else 1304 err = vhd_util_scan_targets(cnt, targets); 1305 1306 free(targets); 1307 lvm_free_vg(&vg); 1308 1309 return ((flags & VHD_SCAN_NOFAIL) ? 0 : err); 1310 1311 usage: 1312 printf("usage: [OPTIONS] FILES\n" 1313 "options: [-m match filter] [-f fast] [-c continue on failure] " 1314 "[-l LVM volume] [-p pretty print] [-a scan parents] " 1315 "[-v verbose] [-h help]\n"); 1316 return err; 1317 } 1318