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