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