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, &copy, &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(&copy, ldst, sizeof(copy));
1125 	memcpy(ldst, lsrc, sizeof(*ldst));
1126 	memcpy(lsrc, &copy, 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