1
/* Copyright (c) 2008, XenSource Inc.
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.
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.
36
#include <libgen.h> /* for basename() */
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
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
56
target_volume(uint8_t type)
58
return (type == VHD_TYPE_RAW_VOLUME || type == VHD_TYPE_VHD_VOLUME);
62
target_vhd(uint8_t type)
64
return (type == VHD_TYPE_VHD_FILE || type == VHD_TYPE_VHD_VOLUME);
68
char name[VHD_MAX_NAME_LEN];
69
char device[VHD_MAX_NAME_LEN];
80
struct target *targets;
92
struct target *target;
94
struct list_head sibling;
95
struct list_head children;
96
struct vhd_image *parent_image;
106
struct vhd_image **images;
107
struct vhd_image **lists;
112
static struct vhd_scan scan;
115
vhd_util_scan_pretty_allocate_list(int cnt)
118
struct vhd_image *list;
120
memset(&scan, 0, sizeof(scan));
123
scan.lists_size = 10;
125
scan.lists = calloc(scan.lists_size, sizeof(struct vhd_image *));
129
scan.lists[0] = calloc(cnt, sizeof(struct vhd_image));
133
scan.images = calloc(cnt, sizeof(struct vhd_image *));
137
for (i = 0; i < cnt; i++)
138
scan.images[i] = scan.lists[0] + i;
152
memset(&scan, 0, sizeof(scan));
157
vhd_util_scan_pretty_free_list(void)
162
for (i = 0; i < scan.lists_cur; i++)
168
memset(&scan, 0, sizeof(scan));
172
vhd_util_scan_pretty_add_image(struct vhd_image *image)
175
struct vhd_image *img;
177
for (i = 0; i < scan.cur; i++) {
178
img = scan.images[i];
179
if (!strcmp(img->name, image->name))
183
if (scan.cur >= scan.size) {
184
struct vhd_image *new, **list;
186
if (scan.lists_cur >= scan.lists_size) {
187
list = realloc(scan.lists, scan.lists_size * 2 *
188
sizeof(struct vhd_image *));
192
scan.lists_size *= 2;
196
new = calloc(scan.size, sizeof(struct vhd_image));
200
scan.lists[scan.lists_cur++] = new;
203
list = realloc(scan.images, scan.size *
204
sizeof(struct vhd_image *));
209
for (i = 0; i + scan.cur < scan.size; i++)
210
scan.images[i + scan.cur] = new + i;
213
img = scan.images[scan.cur];
214
INIT_LIST_HEAD(&img->sibling);
215
INIT_LIST_HEAD(&img->children);
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;
223
img->name = strdup(image->name);
228
img->parent = strdup(image->parent);
239
memset(img, 0, sizeof(*img));
244
vhd_util_scan_pretty_image_compare(const void *lhs, const void *rhs)
246
struct vhd_image *l, *r;
248
l = *(struct vhd_image **)lhs;
249
r = *(struct vhd_image **)rhs;
251
return strcmp(l->name, r->name);
255
vhd_util_scan_print_image_indent(struct vhd_image *image, int tab)
257
char *pad, *name, *pmsg, *parent;
259
pad = (tab ? " " : "");
261
parent = (image->parent ? : "none");
263
if ((flags & VHD_SCAN_PRETTY) && image->parent && !image->parent_image)
264
pmsg = " (not found in scan)";
268
if (!(flags & VHD_SCAN_VERBOSE)) {
269
name = basename(image->name);
271
parent = basename(image->parent);
275
printf("%*svhd=%s scan-error=%d error-message='%s'\n",
276
tab, pad, image->name, image->error, image->message);
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);
284
vhd_util_scan_pretty_print_tree(struct vhd_image *image, int depth)
286
struct vhd_image *img, *tmp;
288
vhd_util_scan_print_image_indent(image, depth * 3);
290
list_for_each_entry_safe(img, tmp, &image->children, sibling)
292
vhd_util_scan_pretty_print_tree(img, depth + 1);
294
list_for_each_entry_safe(img, tmp, &image->children, sibling)
296
vhd_util_scan_pretty_print_tree(img, depth + 1);
302
image->parent = NULL;
306
vhd_util_scan_pretty_print_images(void)
309
struct vhd_image *image, **parentp, *parent, *keyp, key;
311
qsort(scan.images, scan.cur, sizeof(scan.images[0]),
312
vhd_util_scan_pretty_image_compare);
314
for (i = 0; i < scan.cur; i++) {
315
image = scan.images[i];
317
if (!image->parent) {
318
image->parent_image = NULL;
322
memset(&key, 0, sizeof(key));
323
key.name = image->parent;
326
parentp = bsearch(&keyp, scan.images, scan.cur,
327
sizeof(scan.images[0]),
328
vhd_util_scan_pretty_image_compare);
330
image->parent_image = NULL;
335
image->parent_image = parent;
336
list_add_tail(&image->sibling, &parent->children);
339
for (i = 0; i < scan.cur; i++) {
340
image = scan.images[i];
342
if (image->parent_image || !image->hidden)
345
vhd_util_scan_pretty_print_tree(image, 0);
348
for (i = 0; i < scan.cur; i++) {
349
image = scan.images[i];
351
if (!image->name || image->parent_image)
354
vhd_util_scan_pretty_print_tree(image, 0);
357
for (i = 0; i < scan.cur; i++) {
358
image = scan.images[i];
363
vhd_util_scan_pretty_print_tree(image, 0);
368
vhd_util_scan_print_image(struct vhd_image *image)
372
if (!image->error && (flags & VHD_SCAN_PRETTY)) {
373
err = vhd_util_scan_pretty_add_image(image);
379
image->message = "allocating memory";
383
vhd_util_scan_print_image_indent(image, 0);
387
vhd_util_scan_error(const char *file, int err)
389
struct vhd_image image;
391
memset(&image, 0, sizeof(image));
392
image.name = (char *)file;
394
image.message = "failure scanning target";
396
vhd_util_scan_print_image(&image);
399
if (flags & VHD_SCAN_NOFAIL)
406
static vhd_parent_locator_t *
407
vhd_util_scan_get_parent_locator(vhd_context_t *vhd)
410
vhd_parent_locator_t *loc;
414
for (i = 0; i < 8; i++) {
415
if (vhd->header.loc[i].code == PLAT_CODE_MACX) {
416
loc = vhd->header.loc + i;
420
if (vhd->header.loc[i].code == PLAT_CODE_W2RU)
421
loc = vhd->header.loc + i;
423
if (!loc && vhd->header.loc[i].code != PLAT_CODE_NONE)
424
loc = vhd->header.loc + i;
431
copy_name(char *dst, const char *src)
433
if (snprintf(dst, VHD_MAX_NAME_LEN, "%s", src) < VHD_MAX_NAME_LEN)
436
return -ENAMETOOLONG;
440
* LVHD stores realpath(parent) in parent locators, so
441
* /dev/<vol-group>/<lv-name> becomes /dev/mapper/<vol--group>-<lv--name>
444
vhd_util_scan_extract_volume_name(char *dst, const char *src)
447
char copy[VHD_MAX_NAME_LEN], *name, *s, *c;
449
name = strrchr(src, '/');
453
/* convert single dashes to slashes, double dashes to single dashes */
454
for (c = copy, s = name; *s != '\0'; s++, c++) {
467
c = strrchr(copy, '/');
469
/* unrecognized format */
479
vhd_util_scan_get_volume_parent(vhd_context_t *vhd, struct vhd_image *image)
482
char name[VHD_MAX_NAME_LEN];
483
vhd_parent_locator_t *loc, copy;
485
if (flags & VHD_SCAN_FAST) {
486
err = vhd_header_decode_parent(vhd,
487
&vhd->header, &image->parent);
492
loc = vhd_util_scan_get_parent_locator(vhd);
497
copy.data_offset += image->target->start;
498
err = vhd_parent_locator_read(vhd, ©, &image->parent);
503
err = vhd_util_scan_extract_volume_name(name, image->parent);
505
return copy_name(image->parent, name);
511
vhd_util_scan_get_parent(vhd_context_t *vhd, struct vhd_image *image)
514
vhd_parent_locator_t *loc;
516
if (!target_vhd(image->target->type)) {
517
image->parent = NULL;
523
if (target_volume(image->target->type))
524
return vhd_util_scan_get_volume_parent(vhd, image);
526
if (flags & VHD_SCAN_FAST) {
527
err = vhd_header_decode_parent(vhd,
528
&vhd->header, &image->parent);
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
538
err = vhd_parent_locator_get(vhd, &image->parent);
543
loc = vhd_util_scan_get_parent_locator(vhd);
547
return vhd_parent_locator_read(vhd, loc, &image->parent);
551
vhd_util_scan_get_hidden(vhd_context_t *vhd, struct vhd_image *image)
558
if (target_vhd(image->target->type))
559
err = vhd_hidden(vhd, &hidden);
566
image->hidden = hidden;
571
vhd_util_scan_get_size(vhd_context_t *vhd, struct vhd_image *image)
573
image->size = image->target->size;
575
if (target_vhd(image->target->type))
576
image->capacity = vhd->footer.curr_size;
578
image->capacity = image->size;
584
vhd_util_scan_open_file(vhd_context_t *vhd, struct vhd_image *image)
588
if (!target_vhd(image->target->type))
591
vhd_flags = VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED;
592
if (flags & VHD_SCAN_FAST)
593
vhd_flags |= VHD_OPEN_FAST;
595
err = vhd_open(vhd, image->name, vhd_flags);
598
image->message = "opening file";
607
vhd_util_scan_read_volume_headers(vhd_context_t *vhd, struct vhd_image *image)
612
struct target *target;
615
target = image->target;
616
size = sizeof(vhd_footer_t) + sizeof(vhd_header_t);
618
err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
621
image->message = "allocating image";
626
err = vhd_seek(vhd, target->start, SEEK_SET);
628
image->message = "seeking to headers";
633
err = vhd_read(vhd, buf, size);
635
image->message = "reading headers";
640
memcpy(&vhd->footer, buf, sizeof(vhd_footer_t));
641
vhd_footer_in(&vhd->footer);
642
err = vhd_validate_footer(&vhd->footer);
644
image->message = "invalid footer";
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 +
657
buf + sizeof(vhd_footer_t),
658
sizeof(vhd_header_t));
659
vhd_header_in(&vhd->header);
660
err = vhd_validate_header(&vhd->header);
664
image->message = "reading header";
669
vhd->spb = vhd->header.block_size >> VHD_SECTOR_SHIFT;
670
vhd->bm_secs = secs_round_up_no_zero(vhd->spb >> 3);
679
vhd_util_scan_open_volume(vhd_context_t *vhd, struct vhd_image *image)
682
struct target *target;
684
target = image->target;
685
memset(vhd, 0, sizeof(*vhd));
686
vhd->oflags = VHD_OPEN_RDONLY | VHD_OPEN_FAST;
688
if (target->end - target->start < 4096) {
689
image->message = "device too small";
690
image->error = -EINVAL;
694
vhd->file = strdup(image->name);
696
image->message = "allocating device";
697
image->error = -ENOMEM;
701
vhd->fd = open(target->device, O_RDONLY | O_DIRECT | O_LARGEFILE);
706
image->message = "opening device";
707
image->error = -errno;
711
if (target_vhd(target->type))
712
return vhd_util_scan_read_volume_headers(vhd, image);
718
vhd_util_scan_open(vhd_context_t *vhd, struct vhd_image *image)
720
struct target *target;
722
target = image->target;
724
if (target_volume(image->target->type) || !(flags & VHD_SCAN_PRETTY))
725
image->name = target->name;
727
image->name = realpath(target->name, NULL);
729
image->name = target->name;
730
image->message = "resolving name";
731
image->error = -errno;
736
if (target_volume(target->type))
737
return vhd_util_scan_open_volume(vhd, image);
739
return vhd_util_scan_open_file(vhd, image);
743
vhd_util_scan_init_file_target(struct target *target,
744
const char *file, uint8_t type)
749
err = stat(file, &stats);
753
err = copy_name(target->name, file);
757
err = copy_name(target->device, file);
763
target->size = stats.st_size;
764
target->end = stats.st_size;
770
vhd_util_scan_init_volume_target(struct target *target,
771
struct lv *lv, uint8_t type)
775
if (lv->first_segment.type != LVM_SEG_TYPE_LINEAR)
778
err = copy_name(target->name, lv->name);
782
err = copy_name(target->device, lv->first_segment.device);
787
target->size = lv->size;
788
target->start = lv->first_segment.pe_start;
789
target->end = target->start + lv->first_segment.pe_size;
795
iterator_init(struct iterator *itr, int cnt, struct target *targets)
797
memset(itr, 0, sizeof(*itr));
799
itr->targets = malloc(sizeof(struct target) * cnt);
803
memcpy(itr->targets, targets, sizeof(struct target) * cnt);
812
static struct target *
813
iterator_next(struct iterator *itr)
815
if (itr->cur == itr->cur_size)
818
return itr->targets + itr->cur++;
822
iterator_add_file(struct iterator *itr,
823
struct target *target, const char *parent, uint8_t type)
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);
834
if (!strcmp(lname, rname))
838
return vhd_util_scan_init_file_target(target, parent, type);
842
iterator_add_volume(struct iterator *itr,
843
struct target *target, const char *parent, uint8_t type)
851
for (i = 0; i < itr->cur_size; i++)
852
if (!strcmp(parent, itr->targets[i].name))
855
for (i = 0; i < vg.lv_cnt; i++) {
856
err = fnmatch(parent, vg.lvs[i].name, FNM_PATHNAME);
857
if (err != FNM_NOMATCH) {
863
if (err && err != FNM_PATHNAME)
869
return vhd_util_scan_init_volume_target(target, lv, type);
873
iterator_add(struct iterator *itr, const char *parent, uint8_t type)
876
struct target *target;
878
if (itr->cur_size == itr->max_size) {
881
new = realloc(itr->targets,
882
sizeof(struct target) *
891
target = itr->targets + itr->cur_size;
893
if (target_volume(type))
894
err = iterator_add_volume(itr, target, parent, type);
896
err = iterator_add_file(itr, target, parent, type);
899
memset(target, 0, sizeof(*target));
903
return (err == -EEXIST ? 0 : err);
907
iterator_free(struct iterator *itr)
910
memset(itr, 0, sizeof(*itr));
914
vhd_util_scan_add_parent(struct iterator *itr,
915
vhd_context_t *vhd, struct vhd_image *image)
920
if (vhd_parent_raw(vhd))
921
type = target_volume(image->target->type) ?
922
VHD_TYPE_RAW_VOLUME : VHD_TYPE_RAW_FILE;
924
type = target_volume(image->target->type) ?
925
VHD_TYPE_VHD_VOLUME : VHD_TYPE_VHD_FILE;
927
err = iterator_add(itr, image->parent, type);
929
vhd_util_scan_error(image->parent, err);
933
vhd_util_scan_targets(int cnt, struct target *targets)
938
struct target *target;
939
struct vhd_image image;
944
err = iterator_init(&itr, cnt, targets);
948
while ((target = iterator_next(&itr))) {
949
memset(&vhd, 0, sizeof(vhd));
950
memset(&image, 0, sizeof(image));
952
image.target = target;
954
err = vhd_util_scan_open(&vhd, &image);
960
err = vhd_util_scan_get_size(&vhd, &image);
963
image.message = "getting physical size";
968
err = vhd_util_scan_get_hidden(&vhd, &image);
971
image.message = "checking 'hidden' field";
976
if (vhd.footer.type == HD_TYPE_DIFF) {
977
err = vhd_util_scan_get_parent(&vhd, &image);
980
image.message = "getting parent";
987
vhd_util_scan_print_image(&image);
989
if (flags & VHD_SCAN_PARENTS && image.parent)
990
vhd_util_scan_add_parent(&itr, &vhd, &image);
994
if (image.name != target->name)
998
if (err && !(flags & VHD_SCAN_NOFAIL))
1002
iterator_free(&itr);
1004
if (flags & VHD_SCAN_NOFAIL)
1011
vhd_util_scan_targets_pretty(int cnt, struct target *targets)
1015
err = vhd_util_scan_pretty_allocate_list(cnt);
1017
printf("scan failed: no memory\n");
1021
err = vhd_util_scan_targets(cnt, targets);
1023
vhd_util_scan_pretty_print_images();
1024
vhd_util_scan_pretty_free_list();
1026
return ((flags & VHD_SCAN_NOFAIL) ? 0 : err);
1030
vhd_util_scan_find_file_targets(int cnt, char **names,
1032
struct target **_targets, int *_total)
1035
struct target *targets;
1036
int i, globs, err, total;
1043
memset(&g, 0, sizeof(g));
1046
int gflags = ((flags & VHD_SCAN_FAST) ? GLOB_NOSORT : 0);
1049
err = glob(filter, gflags, vhd_util_scan_error, &g);
1064
vhd_util_scan_error(filter, err);
1072
targets = calloc(total, sizeof(struct target));
1078
for (i = 0; i < g.gl_pathc; i++) {
1079
err = vhd_util_scan_init_file_target(targets + i,
1083
vhd_util_scan_error(g.gl_pathv[i], err);
1084
if (!(flags & VHD_SCAN_NOFAIL))
1089
for (i = 0; i + globs < total; i++) {
1090
err = vhd_util_scan_init_file_target(targets + i + globs,
1094
vhd_util_scan_error(names[i], err);
1095
if (!(flags & VHD_SCAN_NOFAIL))
1102
*_targets = targets;
1114
swap_volume(struct lv *lvs, int dst, int src)
1116
struct lv copy, *ldst, *lsrc;
1124
memcpy(©, ldst, sizeof(copy));
1125
memcpy(ldst, lsrc, sizeof(*ldst));
1126
memcpy(lsrc, ©, sizeof(copy));
1130
vhd_util_scan_sort_volumes(struct lv *lvs, int cnt,
1131
const char *filter, int *_matches)
1134
int i, err, matches;
1142
for (i = 0; i < cnt; i++) {
1145
err = fnmatch(filter, lv->name, FNM_PATHNAME);
1147
if (err != FNM_NOMATCH) {
1148
vhd_util_scan_error(lv->name, err);
1149
if (!(flags & VHD_SCAN_NOFAIL))
1156
swap_volume(lvs, matches++, i);
1159
*_matches = matches;
1164
vhd_util_scan_find_volume_targets(int cnt, char **names,
1165
const char *volume, const char *filter,
1166
struct target **_targets, int *_total)
1168
struct target *targets;
1169
int i, err, total, matches;
1175
err = lvm_scan_vg(volume, &vg);
1179
err = vhd_util_scan_sort_volumes(vg.lvs, vg.lv_cnt,
1185
for (i = 0; i < cnt; i++) {
1186
err = vhd_util_scan_sort_volumes(vg.lvs + total,
1188
names[i], &matches);
1195
targets = calloc(total, sizeof(struct target));
1201
for (i = 0; i < total; i++) {
1202
err = vhd_util_scan_init_volume_target(targets + i,
1204
VHD_TYPE_VHD_VOLUME);
1206
vhd_util_scan_error(vg.lvs[i].name, err);
1207
if (!(flags & VHD_SCAN_NOFAIL))
1214
*_targets = targets;
1223
vhd_util_scan_find_targets(int cnt, char **names,
1224
const char *volume, const char *filter,
1225
struct target **targets, int *total)
1227
if (flags & VHD_SCAN_VOLUME)
1228
return vhd_util_scan_find_volume_targets(cnt, names,
1231
return vhd_util_scan_find_file_targets(cnt, names,
1232
filter, targets, total);
1236
vhd_util_scan(int argc, char **argv)
1238
int c, ret, err, cnt;
1239
char *filter, *volume;
1240
struct target *targets;
1251
while ((c = getopt(argc, argv, "m:fcl:pavh")) != -1) {
1257
flags |= VHD_SCAN_FAST;
1260
flags |= VHD_SCAN_NOFAIL;
1264
flags |= VHD_SCAN_VOLUME;
1267
flags |= VHD_SCAN_PRETTY;
1270
flags |= VHD_SCAN_PARENTS;
1273
flags |= VHD_SCAN_VERBOSE;
1283
if (!filter && argc - optind == 0) {
1288
if (flags & VHD_SCAN_PRETTY)
1289
flags &= ~VHD_SCAN_FAST;
1291
err = vhd_util_scan_find_targets(argc - optind, argv + optind,
1292
volume, filter, &targets, &cnt);
1294
printf("scan failed: %d\n", err);
1301
if (flags & VHD_SCAN_PRETTY)
1302
err = vhd_util_scan_targets_pretty(cnt, targets);
1304
err = vhd_util_scan_targets(cnt, targets);
1309
return ((flags & VHD_SCAN_NOFAIL) ? 0 : err);
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");