Statistics
| Branch: | Revision:

root / vhd / lib / vhd-util-scan.c @ abdb293f

History | View | Annotate | Download (27.6 kB)

1
/*
2
 * Copyright (c) 2007, XenSource Inc.
3
 * Copyright (c) 2010, Citrix Systems, Inc.
4
 *
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions are met:
9
 *     * Redistributions of source code must retain the above copyright
10
 *       notice, this list of conditions and the following disclaimer.
11
 *     * Redistributions in binary form must reproduce the above copyright
12
 *       notice, this list of conditions and the following disclaimer in the
13
 *       documentation and/or other materials provided with the distribution.
14
 *     * Neither the name of XenSource Inc. nor the names of its contributors
15
 *       may be used to endorse or promote products derived from this software
16
 *       without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 */
30

    
31
#ifdef HAVE_CONFIG_H
32
#include "config.h"
33
#endif
34

    
35
#include <glob.h>
36
#include <errno.h>
37
#include <fcntl.h>
38
#include <stdio.h>
39
#include <string.h>
40
#include <stdlib.h>
41
#include <unistd.h>
42
#include <fnmatch.h>
43
#include <limits.h>
44
#include <libgen.h>
45
#include <syslog.h>
46
#include <sys/stat.h>
47
#include <sys/types.h>
48

    
49
#include "list.h"
50
#include "libvhd.h"
51
#include "lvm-util.h"
52

    
53
#define VHD_SCAN_FAST        0x01
54
#define VHD_SCAN_PRETTY      0x02
55
#define VHD_SCAN_VOLUME      0x04
56
#define VHD_SCAN_NOFAIL      0x08
57
#define VHD_SCAN_VERBOSE     0x10
58
#define VHD_SCAN_PARENTS     0x20
59
#define VHD_SCAN_MARKERS     0x40
60

    
61
#define VHD_TYPE_RAW_FILE    0x01
62
#define VHD_TYPE_VHD_FILE    0x02
63
#define VHD_TYPE_RAW_VOLUME  0x04
64
#define VHD_TYPE_VHD_VOLUME  0x08
65

    
66
#define EPRINTF(_f, _a...)                                        \
67
        do {                                                        \
68
                syslog(LOG_INFO, "%s: " _f, __func__, ##_a);        \
69
        } while (0)
70

    
71
static inline int
72
target_volume(uint8_t type)
73
{
74
        return (type == VHD_TYPE_RAW_VOLUME || type == VHD_TYPE_VHD_VOLUME);
75
}
76

    
77
static inline int
78
target_vhd(uint8_t type)
79
{
80
        return (type == VHD_TYPE_VHD_FILE || type == VHD_TYPE_VHD_VOLUME);
81
}
82

    
83
struct target {
84
        char                 name[VHD_MAX_NAME_LEN];
85
        char                 device[VHD_MAX_NAME_LEN];
86
        uint64_t             size;
87
        uint64_t             start;
88
        uint64_t             end;
89
        uint8_t              type;
90
};
91

    
92
struct iterator {
93
        int                  cur;
94
        int                  cur_size;
95
        int                  max_size;
96
        struct target       *targets;
97
};
98

    
99
struct vhd_image {
100
        char                *name;
101
        char                *parent;
102
        uint64_t             capacity;
103
        off64_t              size;
104
        uint8_t              hidden;
105
        char                 marker;
106
        int                  error;
107
        char                *message;
108

    
109
        struct target       *target;
110

    
111
        struct list_head     sibling;
112
        struct list_head     children;
113
        struct vhd_image    *parent_image;
114
};
115

    
116
struct vhd_scan {
117
        int                  cur;
118
        int                  size;
119

    
120
        int                  lists_cur;
121
        int                  lists_size;
122

    
123
        struct vhd_image   **images;
124
        struct vhd_image   **lists;
125
};
126

    
127
static int flags;
128
static struct vg vg;
129
static struct vhd_scan scan;
130

    
131
static int
132
vhd_util_scan_pretty_allocate_list(int cnt)
133
{
134
        int i;
135

    
136
        memset(&scan, 0, sizeof(scan));
137

    
138
        scan.lists_cur  = 1;
139
        scan.lists_size = 10;
140

    
141
        scan.lists = calloc(scan.lists_size, sizeof(struct vhd_image *));
142
        if (!scan.lists)
143
                goto fail;
144

    
145
        scan.lists[0] = calloc(cnt, sizeof(struct vhd_image));
146
        if (!scan.lists[0])
147
                goto fail;
148

    
149
        scan.images = calloc(cnt, sizeof(struct vhd_image *));
150
        if (!scan.images)
151
                goto fail;
152

    
153
        for (i = 0; i < cnt; i++)
154
                scan.images[i] = scan.lists[0] + i;
155

    
156
        scan.cur  = 0;
157
        scan.size = cnt;
158

    
159
        return 0;
160

    
161
fail:
162
        if (scan.lists) {
163
                free(scan.lists[0]);
164
                free(scan.lists);
165
        }
166

    
167
        free(scan.images);
168
        memset(&scan, 0, sizeof(scan));
169
        return -ENOMEM;
170
}
171

    
172
static void
173
vhd_util_scan_pretty_free_list(void)
174
{
175
        int i;
176

    
177
        if (scan.lists) {
178
                for (i = 0; i < scan.lists_cur; i++)
179
                        free(scan.lists[i]);
180
                free(scan.lists);
181
        }
182

    
183
        free(scan.images);
184
        memset(&scan, 0, sizeof(scan));
185
}
186

    
187
static int
188
vhd_util_scan_pretty_add_image(struct vhd_image *image)
189
{
190
        int i;
191
        struct vhd_image *img;
192

    
193
        for (i = 0; i < scan.cur; i++) {
194
                img = scan.images[i];
195
                if (!strcmp(img->name, image->name))
196
                        return 0;
197
        }
198

    
199
        if (scan.cur >= scan.size) {
200
                struct vhd_image *new, **list;
201

    
202
                if (scan.lists_cur >= scan.lists_size) {
203
                        list = realloc(scan.lists, scan.lists_size * 2 *
204
                                       sizeof(struct vhd_image *));
205
                        if (!list)
206
                                return -ENOMEM;
207

    
208
                        scan.lists_size *= 2;
209
                        scan.lists       = list;
210
                }
211

    
212
                new = calloc(scan.size, sizeof(struct vhd_image));
213
                if (!new)
214
                        return -ENOMEM;
215

    
216
                scan.lists[scan.lists_cur++] = new;
217
                scan.size *= 2;
218

    
219
                list = realloc(scan.images, scan.size *
220
                               sizeof(struct vhd_image *));
221
                if (!list)
222
                        return -ENOMEM;
223

    
224
                scan.images = list;
225
                for (i = 0; i + scan.cur < scan.size; i++)
226
                        scan.images[i + scan.cur] = new + i;
227
        }
228

    
229
        img = scan.images[scan.cur];
230
        INIT_LIST_HEAD(&img->sibling);
231
        INIT_LIST_HEAD(&img->children);
232

    
233
        img->capacity = image->capacity;
234
        img->size     = image->size;
235
        img->hidden   = image->hidden;
236
        img->marker   = image->marker;
237
        img->error    = image->error;
238
        img->message  = image->message;
239

    
240
        img->name = strdup(image->name);
241
        if (!img->name)
242
                goto fail;
243

    
244
        if (image->parent) {
245
                img->parent = strdup(image->parent);
246
                if (!img->parent)
247
                        goto fail;
248
        }
249

    
250
        scan.cur++;
251
        return 0;
252

    
253
fail:
254
        free(img->name);
255
        free(img->parent);
256
        memset(img, 0, sizeof(*img));
257
        return -ENOMEM;
258
}
259

    
260
static int
261
vhd_util_scan_pretty_image_compare(const void *lhs, const void *rhs)
262
{
263
        struct vhd_image *l, *r;
264

    
265
        l = *(struct vhd_image **)lhs;
266
        r = *(struct vhd_image **)rhs;
267

    
268
        return strcmp(l->name, r->name);
269
}
270

    
271
static void
272
vhd_util_scan_print_image_indent(struct vhd_image *image, int tab)
273
{
274
        char *pad, *name, *pmsg, *parent;
275

    
276
        pad    = (tab ? " " : "");
277
        name   = image->name;
278
        parent = (image->parent ? : "none");
279

    
280
        if ((flags & VHD_SCAN_PRETTY) && image->parent && !image->parent_image)
281
                pmsg = " (not found in scan)";
282
        else
283
                pmsg = "";
284

    
285
        if (!(flags & VHD_SCAN_VERBOSE)) {
286
                name = basename(image->name);
287
                if (image->parent)
288
                        parent = basename(image->parent);
289
        }
290

    
291
        if (image->error)
292
                printf("%*svhd=%s scan-error=%d error-message='%s'\n",
293
                       tab, pad, image->name, image->error, image->message);
294
        else if (!(flags & VHD_SCAN_MARKERS))
295
                printf("%*svhd=%s capacity=%"PRIu64" size=%"PRIu64" hidden=%u "
296
                       "parent=%s%s\n", tab, pad, name, image->capacity,
297
                       image->size, image->hidden, parent, pmsg);
298
        else
299
                printf("%*svhd=%s capacity=%"PRIu64" size=%"PRIu64" hidden=%u "
300
                       "marker=%u parent=%s%s\n", tab, pad, name,
301
                       image->capacity, image->size, image->hidden,
302
                       (uint8_t)image->marker, parent, pmsg);
303
}
304

    
305
static void
306
vhd_util_scan_pretty_print_tree(struct vhd_image *image, int depth)
307
{
308
        struct vhd_image *img, *tmp;
309

    
310
        vhd_util_scan_print_image_indent(image, depth * 3);
311

    
312
        list_for_each_entry_safe(img, tmp, &image->children, sibling)
313
                if (!img->hidden)
314
                        vhd_util_scan_pretty_print_tree(img, depth + 1);
315

    
316
        list_for_each_entry_safe(img, tmp, &image->children, sibling)
317
                if (img->hidden)
318
                        vhd_util_scan_pretty_print_tree(img, depth + 1);
319

    
320
        free(image->name);
321
        free(image->parent);
322

    
323
        image->name   = NULL;
324
        image->parent = NULL;
325
}
326

    
327
static void
328
vhd_util_scan_pretty_print_images(void)
329
{
330
        int i;
331
        struct vhd_image *image, **parentp, *parent, *keyp, key;
332

    
333
        qsort(scan.images, scan.cur, sizeof(scan.images[0]),
334
              vhd_util_scan_pretty_image_compare);
335

    
336
        for (i = 0; i < scan.cur; i++) {
337
                image = scan.images[i];
338

    
339
                if (!image->parent) {
340
                        image->parent_image = NULL;
341
                        continue;
342
                }
343

    
344
                memset(&key, 0, sizeof(key));
345
                key.name = image->parent;
346
                keyp     = &key;
347

    
348
                parentp  = bsearch(&keyp, scan.images, scan.cur,
349
                                   sizeof(scan.images[0]),
350
                                   vhd_util_scan_pretty_image_compare);
351
                if (!parentp) {
352
                        image->parent_image = NULL;
353
                        continue;
354
                }
355

    
356
                parent = *parentp;
357
                image->parent_image = parent;
358
                list_add_tail(&image->sibling, &parent->children);
359
        }
360

    
361
        for (i = 0; i < scan.cur; i++) {
362
                image = scan.images[i];
363

    
364
                if (image->parent_image || !image->hidden)
365
                        continue;
366

    
367
                vhd_util_scan_pretty_print_tree(image, 0);
368
        }
369

    
370
        for (i = 0; i < scan.cur; i++) {
371
                image = scan.images[i];
372

    
373
                if (!image->name || image->parent_image)
374
                        continue;
375

    
376
                vhd_util_scan_pretty_print_tree(image, 0);
377
        }
378

    
379
        for (i = 0; i < scan.cur; i++) {
380
                image = scan.images[i];
381

    
382
                if (!image->name)
383
                        continue;
384

    
385
                vhd_util_scan_pretty_print_tree(image, 0);
386
        }
387
}
388

    
389
static void
390
vhd_util_scan_print_image(struct vhd_image *image)
391
{
392
        int err;
393

    
394
        if (!image->error && (flags & VHD_SCAN_PRETTY)) {
395
                err = vhd_util_scan_pretty_add_image(image);
396
                if (!err)
397
                        return;
398

    
399
                if (!image->error) {
400
                        image->error   = err;
401
                        image->message = "allocating memory";
402
                }
403
        }
404

    
405
        vhd_util_scan_print_image_indent(image, 0);
406
}
407

    
408
static int
409
vhd_util_scan_error(const char *file, int err)
410
{
411
        struct vhd_image image;
412

    
413
        memset(&image, 0, sizeof(image));
414
        image.name    = (char *)file;
415
        image.error   = err;
416
        image.message = "failure scanning target";
417

    
418
        vhd_util_scan_print_image(&image);
419

    
420
        /*
421
        if (flags & VHD_SCAN_NOFAIL)
422
                return 0;
423
        */
424

    
425
        return err;
426
}
427

    
428
static vhd_parent_locator_t *
429
vhd_util_scan_get_parent_locator(vhd_context_t *vhd)
430
{
431
        int i;
432
        vhd_parent_locator_t *loc;
433

    
434
        loc = NULL;
435

    
436
        for (i = 0; i < 8; i++) {
437
                if (vhd->header.loc[i].code == PLAT_CODE_MACX) {
438
                        loc = vhd->header.loc + i;
439
                        break;
440
                }
441

    
442
                if (vhd->header.loc[i].code == PLAT_CODE_W2RU)
443
                        loc = vhd->header.loc + i;
444

    
445
                if (!loc && vhd->header.loc[i].code != PLAT_CODE_NONE)
446
                        loc = vhd->header.loc + i;
447
        }
448

    
449
        return loc;
450
}
451

    
452
static inline int
453
copy_name(char *dst, const char *src)
454
{
455
        if (snprintf(dst, VHD_MAX_NAME_LEN, "%s", src) < VHD_MAX_NAME_LEN)
456
                return 0;
457

    
458
        return -ENAMETOOLONG;
459
}
460

    
461
/*
462
 * LVHD stores realpath(parent) in parent locators, so
463
 * /dev/<vol-group>/<lv-name> becomes /dev/mapper/<vol--group>-<lv--name>
464
 */
465
static int
466
vhd_util_scan_extract_volume_name(char *dst, const char *src)
467
{
468
        char copy[VHD_MAX_NAME_LEN], *name, *s, *c;
469

    
470
        name = strrchr(src, '/');
471
        if (!name)
472
                name = (char *)src;
473

    
474
        /* convert single dashes to slashes, double dashes to single dashes */
475
        for (c = copy, s = name; *s != '\0'; s++, c++) {
476
                if (*s == '-') {
477
                        if (s[1] != '-')
478
                                *c = '/';
479
                        else {
480
                                s++;
481
                                *c = '-';
482
                        }
483
                } else
484
                        *c = *s;
485
        }
486

    
487
        *c = '\0';
488
        c = strrchr(copy, '/');
489
        if (c == name) {
490
                /* unrecognized format */
491
                strcpy(dst, src);
492
                return -EINVAL;
493
        }
494

    
495
        strcpy(dst, ++c);
496
        return 0;
497
}
498

    
499
static int
500
vhd_util_scan_get_volume_parent(vhd_context_t *vhd, struct vhd_image *image)
501
{
502
        int err;
503
        char name[VHD_MAX_NAME_LEN];
504
        vhd_parent_locator_t *loc, copy;
505

    
506
        if (flags & VHD_SCAN_FAST) {
507
                err = vhd_header_decode_parent(vhd,
508
                                               &vhd->header, &image->parent);
509
                if (!err)
510
                        goto found;
511
        }
512

    
513
        loc = vhd_util_scan_get_parent_locator(vhd);
514
        if (!loc)
515
                return -EINVAL;
516

    
517
        copy = *loc;
518
        copy.data_offset += image->target->start;
519
        err = vhd_parent_locator_read(vhd, &copy, &image->parent);
520
        if (err)
521
                return err;
522

    
523
found:
524
        err = vhd_util_scan_extract_volume_name(name, image->parent);
525
        if (!err)
526
                return copy_name(image->parent, name);
527

    
528
        return 0;
529
}
530

    
531
static int
532
vhd_util_scan_get_parent(vhd_context_t *vhd, struct vhd_image *image)
533
{
534
        int err;
535
        vhd_parent_locator_t *loc;
536

    
537
        if (!target_vhd(image->target->type)) {
538
                image->parent = NULL;
539
                return 0;
540
        }
541

    
542
        loc = NULL;
543

    
544
        if (target_volume(image->target->type))
545
                return vhd_util_scan_get_volume_parent(vhd, image);
546

    
547
        if (flags & VHD_SCAN_FAST) {
548
                err = vhd_header_decode_parent(vhd,
549
                                               &vhd->header, &image->parent);
550
                if (!err)
551
                        return 0;
552
        } else {
553
                /*
554
                 * vhd_parent_locator_get checks for the existence of the 
555
                 * parent file. if this call succeeds, all is well; if not,
556
                 * we'll try to return whatever string we have before failing
557
                 * outright.
558
                 */
559
                err = vhd_parent_locator_get(vhd, &image->parent);
560
                if (!err)
561
                        return 0;
562
        }
563

    
564
        loc = vhd_util_scan_get_parent_locator(vhd);
565
        if (!loc)
566
                return -EINVAL;
567

    
568
        return vhd_parent_locator_read(vhd, loc, &image->parent);
569
}
570

    
571
static int
572
vhd_util_scan_get_hidden(vhd_context_t *vhd, struct vhd_image *image)
573
{
574
        int err, hidden;
575

    
576
        err    = 0;
577
        hidden = 0;
578

    
579
        if (target_vhd(image->target->type))
580
                err = vhd_hidden(vhd, &hidden);
581
        else
582
                hidden = 1;
583

    
584
        if (err)
585
                return err;
586

    
587
        image->hidden = hidden;
588
        return 0;
589
}
590

    
591
static int
592
vhd_util_scan_get_marker(vhd_context_t *vhd, struct vhd_image *image)
593
{
594
        int err;
595
        char marker;
596

    
597
        err    = 0;
598
        marker = 0;
599

    
600
        if (target_vhd(image->target->type) && vhd_has_batmap(vhd))
601
                err = vhd_marker(vhd, &marker);
602

    
603
        image->marker = marker;
604
        return err;
605
}
606

    
607
static int
608
vhd_util_scan_get_size(vhd_context_t *vhd, struct vhd_image *image)
609
{
610
        image->size = image->target->size;
611

    
612
        if (target_vhd(image->target->type))
613
                image->capacity = vhd->footer.curr_size;
614
        else
615
                image->capacity = image->size;
616

    
617
        return 0;
618
}
619

    
620
static int
621
vhd_util_scan_open_file(vhd_context_t *vhd, struct vhd_image *image)
622
{
623
        int err, vhd_flags;
624

    
625
        if (!target_vhd(image->target->type))
626
                return 0;
627

    
628
        vhd_flags = VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED;
629
        if (flags & VHD_SCAN_FAST)
630
                vhd_flags |= VHD_OPEN_FAST;
631

    
632
        err = vhd_open(vhd, image->name, vhd_flags);
633
        if (err) {
634
                vhd->file      = NULL;
635
                image->message = "opening file";
636
                image->error   = err;
637
                return image->error;
638
        }
639

    
640
        return 0;
641
}
642

    
643
static int
644
vhd_util_scan_read_volume_headers(vhd_context_t *vhd, struct vhd_image *image)
645
{
646
        int err;
647
        void *buf;
648
        size_t size;
649
        struct target *target;
650

    
651
        buf    = NULL;
652
        target = image->target;
653
        size   = sizeof(vhd_footer_t) + sizeof(vhd_header_t);
654

    
655
        err = posix_memalign(&buf, VHD_SECTOR_SIZE, size);
656
        if (err) {
657
                buf            = NULL;
658
                image->message = "allocating image";
659
                image->error   = -err;
660
                goto out;
661
        }
662

    
663
        err = vhd_seek(vhd, target->start, SEEK_SET);
664
        if (err) {
665
                image->message = "seeking to headers";
666
                image->error   = err;
667
                goto out;
668
        }
669

    
670
        err = vhd_read(vhd, buf, size);
671
        if (err) {
672
                image->message = "reading headers";
673
                image->error   = err;
674
                goto out;
675
        }
676

    
677
        memcpy(&vhd->footer, buf, sizeof(vhd_footer_t));
678
        vhd_footer_in(&vhd->footer);
679
        err = vhd_validate_footer(&vhd->footer);
680
        if (err) {
681
                image->message = "invalid footer";
682
                image->error   = err;
683
                goto out;
684
        }
685

    
686
        /* lvhd vhds should always be dynamic */
687
        if (vhd_type_dynamic(vhd)) {
688
                if (vhd->footer.data_offset != sizeof(vhd_footer_t))
689
                        err = vhd_read_header_at(vhd, &vhd->header,
690
                                                 vhd->footer.data_offset +
691
                                                 target->start);
692
                else {
693
                        memcpy(&vhd->header,
694
                               buf + sizeof(vhd_footer_t),
695
                               sizeof(vhd_header_t));
696
                        vhd_header_in(&vhd->header);
697
                        err = vhd_validate_header(&vhd->header);
698
                }
699

    
700
                if (err) {
701
                        image->message = "reading header";
702
                        image->error   = err;
703
                        goto out;
704
                }
705

    
706
                vhd->spb = vhd->header.block_size >> VHD_SECTOR_SHIFT;
707
                vhd->bm_secs = secs_round_up_no_zero(vhd->spb >> 3);
708
        }
709

    
710
out:
711
        free(buf);
712
        return image->error;
713
}
714

    
715
static int
716
vhd_util_scan_open_volume(vhd_context_t *vhd, struct vhd_image *image)
717
{
718
        struct target *target;
719

    
720
        target = image->target;
721
        memset(vhd, 0, sizeof(*vhd));
722
        vhd->oflags = VHD_OPEN_RDONLY | VHD_OPEN_FAST;
723

    
724
        if (target->end - target->start < 4096) {
725
                image->message = "device too small";
726
                image->error   = -EINVAL;
727
                return image->error;
728
        }
729

    
730
        vhd->file = strdup(image->name);
731
        if (!vhd->file) {
732
                image->message = "allocating device";
733
                image->error   = -ENOMEM;
734
                return image->error;
735
        }
736

    
737
        vhd->fd = open(target->device, O_RDONLY | O_DIRECT | O_LARGEFILE);
738
        if (vhd->fd == -1) {
739
                free(vhd->file);
740
                vhd->file = NULL;
741

    
742
                image->message = "opening device";
743
                image->error   = -errno;
744
                return image->error;
745
        }
746

    
747
        if (target_vhd(target->type))
748
                return vhd_util_scan_read_volume_headers(vhd, image);
749

    
750
        return 0;
751
}
752

    
753
static int
754
vhd_util_scan_open(vhd_context_t *vhd, struct vhd_image *image)
755
{
756
        struct target *target;
757

    
758
        target = image->target;
759

    
760
        if (target_volume(image->target->type) || !(flags & VHD_SCAN_PRETTY))
761
                image->name = target->name;
762
        else {
763
                char __image_name[PATH_MAX];
764

    
765
                image->name = realpath(target->name, __image_name);
766
                if (image->name)
767
                        image->name = strdup(__image_name);
768
                if (!image->name) {
769
                        image->name    = target->name;
770
                        image->message = "resolving name";
771
                        image->error   = -errno;
772
                        return image->error;
773
                }
774
        }
775

    
776
        if (target_volume(target->type))
777
                return vhd_util_scan_open_volume(vhd, image);
778
        else
779
                return vhd_util_scan_open_file(vhd, image);
780
}
781

    
782
static int
783
vhd_util_scan_init_file_target(struct target *target,
784
                               const char *file, uint8_t type)
785
{
786
        int err;
787
        struct stat stats;
788

    
789
        err = stat(file, &stats);
790
        if (err == -1)
791
                return -errno;
792

    
793
        err = copy_name(target->name, file);
794
        if (err)
795
                return err;
796

    
797
        err = copy_name(target->device, file);
798
        if (err)
799
                return err;
800

    
801
        target->type  = type;
802
        target->start = 0;
803
        target->size  = stats.st_size;
804
        target->end   = stats.st_size;
805

    
806
        return 0;
807
}
808

    
809
static int
810
vhd_util_scan_init_volume_target(struct target *target,
811
                                 struct lv *lv, uint8_t type)
812
{
813
        int err;
814

    
815
        if (lv->first_segment.type != LVM_SEG_TYPE_LINEAR)
816
                return -ENOSYS;
817

    
818
        err = copy_name(target->name, lv->name);
819
        if (err) {
820
                EPRINTF("copy target name failed: '%s'\n", lv->name);
821
                return err;
822
        }
823

    
824
        err = copy_name(target->device, lv->first_segment.device);
825
        if (err) {
826
                EPRINTF("copy target device failed: '%s'\n",
827
                                lv->first_segment.device);
828
                return err;
829
        }
830

    
831
        target->type  = type;
832
        target->size  = lv->size;
833
        target->start = lv->first_segment.pe_start;
834
        target->end   = target->start + lv->first_segment.pe_size;
835

    
836
        return 0;
837
}
838

    
839
static int
840
iterator_init(struct iterator *itr, int cnt, struct target *targets)
841
{
842
        memset(itr, 0, sizeof(*itr));
843

    
844
        itr->targets = malloc(sizeof(struct target) * cnt);
845
        if (!itr->targets)
846
                return -ENOMEM;
847

    
848
        memcpy(itr->targets, targets, sizeof(struct target) * cnt);
849

    
850
        itr->cur      = 0;
851
        itr->cur_size = cnt;
852
        itr->max_size = cnt;
853

    
854
        return 0;
855
}
856

    
857
static struct target *
858
iterator_next(struct iterator *itr)
859
{
860
        if (itr->cur == itr->cur_size)
861
                return NULL;
862

    
863
        return itr->targets + itr->cur++;
864
}
865

    
866
static int
867
iterator_add_file(struct iterator *itr,
868
                  struct target *target, const char *parent, uint8_t type)
869
{
870
        int i;
871
        struct target *t;
872
        char *lname, *rname;
873

    
874
        for (i = 0; i < itr->cur_size; i++) {
875
                t = itr->targets + i;
876
                lname = basename((char *)t->name);
877
                rname = basename((char *)parent);
878

    
879
                if (!strcmp(lname, rname))
880
                        return -EEXIST;
881
        }
882

    
883
        return vhd_util_scan_init_file_target(target, parent, type);
884
}
885

    
886
static int
887
iterator_add_volume(struct iterator *itr,
888
                    struct target *target, const char *parent, uint8_t type)
889
{
890
        int i, err;
891
        struct lv *lv;
892

    
893
        lv  = NULL;
894
        err = -ENOENT;
895

    
896
        for (i = 0; i < itr->cur_size; i++)
897
                if (!strcmp(parent, itr->targets[i].name))
898
                        return -EEXIST;
899

    
900
        for (i = 0; i < vg.lv_cnt; i++) {
901
                err = fnmatch(parent, vg.lvs[i].name, FNM_PATHNAME);
902
                if (err != FNM_NOMATCH) {
903
                        lv = vg.lvs + i;
904
                        break;
905
                }
906
        }
907

    
908
        if (err && err != FNM_PATHNAME)
909
                return err;
910

    
911
        if (!lv)
912
                return -ENOENT;
913

    
914
        return vhd_util_scan_init_volume_target(target, lv, type);
915
}
916

    
917
static int
918
iterator_add(struct iterator *itr, const char *parent, uint8_t type)
919
{
920
        int err;
921
        struct target *target;
922

    
923
        if (itr->cur_size == itr->max_size) {
924
                struct target *new;
925

    
926
                new = realloc(itr->targets,
927
                              sizeof(struct target) *
928
                              itr->max_size * 2);
929
                if (!new)
930
                        return -ENOMEM;
931

    
932
                itr->max_size *= 2;
933
                itr->targets   = new;
934
        }
935

    
936
        target = itr->targets + itr->cur_size;
937

    
938
        if (target_volume(type))
939
                err = iterator_add_volume(itr, target, parent, type);
940
        else
941
                err = iterator_add_file(itr, target, parent, type);
942

    
943
        if (err)
944
                memset(target, 0, sizeof(*target));
945
        else
946
                itr->cur_size++;
947

    
948
        return (err == -EEXIST ? 0 : err);
949
}
950

    
951
static void
952
iterator_free(struct iterator *itr)
953
{
954
        free(itr->targets);
955
        memset(itr, 0, sizeof(*itr));
956
}
957

    
958
static void
959
vhd_util_scan_add_parent(struct iterator *itr,
960
                         vhd_context_t *vhd, struct vhd_image *image)
961
{
962
        int err;
963
        uint8_t type;
964

    
965
        if (vhd_parent_raw(vhd))
966
                type = target_volume(image->target->type) ? 
967
                        VHD_TYPE_RAW_VOLUME : VHD_TYPE_RAW_FILE;
968
        else
969
                type = target_volume(image->target->type) ? 
970
                        VHD_TYPE_VHD_VOLUME : VHD_TYPE_VHD_FILE;
971

    
972
        err = iterator_add(itr, image->parent, type);
973
        if (err)
974
                vhd_util_scan_error(image->parent, err);
975
}
976

    
977
static int
978
vhd_util_scan_targets(int cnt, struct target *targets)
979
{
980
        int ret, err;
981
        vhd_context_t vhd;
982
        struct iterator itr;
983
        struct target *target;
984
        struct vhd_image image;
985

    
986
        ret = 0;
987
        err = 0;
988

    
989
        err = iterator_init(&itr, cnt, targets);
990
        if (err)
991
                return err;
992

    
993
        while ((target = iterator_next(&itr))) {
994
                memset(&vhd, 0, sizeof(vhd));
995
                memset(&image, 0, sizeof(image));
996

    
997
                image.target = target;
998

    
999
                err = vhd_util_scan_open(&vhd, &image);
1000
                if (err) {
1001
                        ret = -EAGAIN;
1002
                        goto end;
1003
                }
1004

    
1005
                err = vhd_util_scan_get_size(&vhd, &image);
1006
                if (err) {
1007
                        ret           = -EAGAIN;
1008
                        image.message = "getting physical size";
1009
                        image.error   = err;
1010
                        goto end;
1011
                }
1012

    
1013
                err = vhd_util_scan_get_hidden(&vhd, &image);
1014
                if (err) {
1015
                        ret           = -EAGAIN;
1016
                        image.message = "checking 'hidden' field";
1017
                        image.error   = err;
1018
                        goto end;
1019
                }
1020

    
1021
                if (flags & VHD_SCAN_MARKERS) {
1022
                        err = vhd_util_scan_get_marker(&vhd, &image);
1023
                        if (err) {
1024
                                ret           = -EAGAIN;
1025
                                image.message = "checking marker";
1026
                                image.error   = err;
1027
                                goto end;
1028
                        }
1029
                }
1030

    
1031
                if (vhd.footer.type == HD_TYPE_DIFF) {
1032
                        err = vhd_util_scan_get_parent(&vhd, &image);
1033
                        if (err) {
1034
                                ret           = -EAGAIN;
1035
                                image.message = "getting parent";
1036
                                image.error   = err;
1037
                                goto end;
1038
                        }
1039
                }
1040

    
1041
        end:
1042
                vhd_util_scan_print_image(&image);
1043

    
1044
                if (flags & VHD_SCAN_PARENTS && image.parent)
1045
                        vhd_util_scan_add_parent(&itr, &vhd, &image);
1046

    
1047
                if (vhd.file)
1048
                        vhd_close(&vhd);
1049
                if (image.name != target->name)
1050
                        free(image.name);
1051
                free(image.parent);
1052

    
1053
                if (err && !(flags & VHD_SCAN_NOFAIL))
1054
                        break;
1055
        }
1056

    
1057
        iterator_free(&itr);
1058

    
1059
        if (flags & VHD_SCAN_NOFAIL)
1060
                return ret;
1061

    
1062
        return err;
1063
}
1064

    
1065
static int
1066
vhd_util_scan_targets_pretty(int cnt, struct target *targets)
1067
{
1068
        int err;
1069

    
1070
        err = vhd_util_scan_pretty_allocate_list(cnt);
1071
        if (err) {
1072
                printf("scan failed: no memory\n");
1073
                return -ENOMEM;
1074
        }
1075

    
1076
        err = vhd_util_scan_targets(cnt, targets);
1077

    
1078
        vhd_util_scan_pretty_print_images();
1079
        vhd_util_scan_pretty_free_list();
1080

    
1081
        return ((flags & VHD_SCAN_NOFAIL) ? 0 : err);
1082
}
1083

    
1084
static int
1085
vhd_util_scan_find_file_targets(int cnt, char **names,
1086
                                const char *filter,
1087
                                struct target **_targets, int *_total)
1088
{
1089
        glob_t g;
1090
        struct target *targets;
1091
        int i, globs, err, total;
1092

    
1093
        total     = cnt;
1094
        globs     = 0;
1095
        *_total   = 0;
1096
        *_targets = NULL;
1097
        
1098
        memset(&g, 0, sizeof(g));
1099

    
1100
        if (filter) {
1101
                int gflags = ((flags & VHD_SCAN_FAST) ? GLOB_NOSORT : 0);
1102

    
1103
                errno = 0;
1104
                err   = glob(filter, gflags, vhd_util_scan_error, &g);
1105

    
1106
                switch (err) {
1107
                case GLOB_NOSPACE:
1108
                        err = -ENOMEM;
1109
                        break;
1110
                case GLOB_ABORTED:
1111
                        err = -EIO;
1112
                        break;
1113
                case GLOB_NOMATCH:
1114
                        err = -errno;
1115
                        break;
1116
                }
1117

    
1118
                if (err) {
1119
                        vhd_util_scan_error(filter, err);
1120
                        return err;
1121
                }
1122

    
1123
                globs  = g.gl_pathc;
1124
                total += globs;
1125
        }
1126

    
1127
        targets = calloc(total, sizeof(struct target));
1128
        if (!targets) {
1129
                err = -ENOMEM;
1130
                goto out;
1131
        }
1132

    
1133
        for (i = 0; i < g.gl_pathc; i++) {
1134
                err = vhd_util_scan_init_file_target(targets + i,
1135
                                                     g.gl_pathv[i],
1136
                                                     VHD_TYPE_VHD_FILE);
1137
                if (err) {
1138
                        vhd_util_scan_error(g.gl_pathv[i], err);
1139
                        if (!(flags & VHD_SCAN_NOFAIL))
1140
                                goto out;
1141
                }
1142
        }
1143

    
1144
        for (i = 0; i + globs < total; i++) {
1145
                err = vhd_util_scan_init_file_target(targets + i + globs,
1146
                                                     names[i],
1147
                                                     VHD_TYPE_VHD_FILE);
1148
                if (err) {
1149
                        vhd_util_scan_error(names[i], err);
1150
                        if (!(flags & VHD_SCAN_NOFAIL))
1151
                                goto out;
1152
                }
1153
        }
1154

    
1155
        err       = 0;
1156
        *_total   = total;
1157
        *_targets = targets;
1158

    
1159
out:
1160
        if (err)
1161
                free(targets);
1162
        if (filter)
1163
                globfree(&g);
1164

    
1165
        return err;
1166
}
1167

    
1168
static inline void
1169
swap_volume(struct lv *lvs, int dst, int src)
1170
{
1171
        struct lv copy, *ldst, *lsrc;
1172

    
1173
        if (dst == src)
1174
                return;
1175

    
1176
        lsrc = lvs + src;
1177
        ldst = lvs + dst;
1178

    
1179
        memcpy(&copy, ldst, sizeof(copy));
1180
        memcpy(ldst, lsrc, sizeof(*ldst));
1181
        memcpy(lsrc, &copy, sizeof(copy));
1182
}
1183

    
1184
static int
1185
vhd_util_scan_sort_volumes(struct lv *lvs, int cnt,
1186
                           const char *filter, int *_matches)
1187
{
1188
        struct lv *lv;
1189
        int i, err, matches;
1190

    
1191
        matches   = 0;
1192
        *_matches = 0;
1193

    
1194
        if (!filter)
1195
                return 0;
1196

    
1197
        for (i = 0; i < cnt; i++) {
1198
                lv  = lvs + i;
1199

    
1200
                err = fnmatch(filter, lv->name, FNM_PATHNAME);
1201
                if (err) {
1202
                        if (err != FNM_NOMATCH) {
1203
                                EPRINTF("fnmatch failed: '%s', '%s'\n", 
1204
                                                filter, lv->name);
1205
                                vhd_util_scan_error(lv->name, err);
1206
                                if (!(flags & VHD_SCAN_NOFAIL))
1207
                                        return err;
1208
                        }
1209

    
1210
                        continue;
1211
                }
1212

    
1213
                swap_volume(lvs, matches++, i);
1214
        }
1215

    
1216
        *_matches = matches;
1217
        return 0;
1218
}
1219

    
1220
static int
1221
vhd_util_scan_find_volume_targets(int cnt, char **names,
1222
                                  const char *volume, const char *filter,
1223
                                  struct target **_targets, int *_total)
1224
{
1225
        struct target *targets;
1226
        int i, err, total, matches;
1227

    
1228
        *_total   = 0;
1229
        *_targets = NULL;
1230
        targets   = NULL;
1231

    
1232
        err = lvm_scan_vg(volume, &vg);
1233
        if (err)
1234
                return err;
1235

    
1236
        err = vhd_util_scan_sort_volumes(vg.lvs, vg.lv_cnt,
1237
                                         filter, &matches);
1238
        if (err)
1239
                goto out;
1240

    
1241
        total = matches;
1242
        for (i = 0; i < cnt; i++) {
1243
                err = vhd_util_scan_sort_volumes(vg.lvs + total,
1244
                                                 vg.lv_cnt - total,
1245
                                                 names[i], &matches);
1246
                if (err)
1247
                        goto out;
1248

    
1249
                total += matches;
1250
        }
1251

    
1252
        targets = calloc(total, sizeof(struct target));
1253
        if (!targets) {
1254
                err = -ENOMEM;
1255
                goto out;
1256
        }
1257

    
1258
        for (i = 0; i < total; i++) {
1259
                err = vhd_util_scan_init_volume_target(targets + i,
1260
                                                       vg.lvs + i,
1261
                                                       VHD_TYPE_VHD_VOLUME);
1262
                if (err) {
1263
                        vhd_util_scan_error(vg.lvs[i].name, err);
1264
                        if (!(flags & VHD_SCAN_NOFAIL))
1265
                                goto out;
1266
                }
1267
        }
1268

    
1269
        err       = 0;
1270
        *_total   = total;
1271
        *_targets = targets;
1272

    
1273
out:
1274
        if (err)
1275
                free(targets);
1276
        return err;
1277
}
1278

    
1279
static int
1280
vhd_util_scan_find_targets(int cnt, char **names,
1281
                           const char *volume, const char *filter,
1282
                           struct target **targets, int *total)
1283
{
1284
        if (flags & VHD_SCAN_VOLUME)
1285
                return vhd_util_scan_find_volume_targets(cnt, names,
1286
                                                         volume, filter,
1287
                                                         targets, total);
1288
        return vhd_util_scan_find_file_targets(cnt, names,
1289
                                               filter, targets, total);
1290
}
1291

    
1292
int
1293
vhd_util_scan(int argc, char **argv)
1294
{
1295
        int c, err, cnt;
1296
        char *filter, *volume;
1297
        struct target *targets;
1298

    
1299
        cnt     = 0;
1300
        err     = 0;
1301
        flags   = 0;
1302
        filter  = NULL;
1303
        volume  = NULL;
1304
        targets = NULL;
1305

    
1306
        optind = 0;
1307
        while ((c = getopt(argc, argv, "m:fcl:pavMh")) != -1) {
1308
                switch (c) {
1309
                case 'm':
1310
                        filter = optarg;
1311
                        break;
1312
                case 'f':
1313
                        flags |= VHD_SCAN_FAST;
1314
                        break;
1315
                case 'c':
1316
                        flags |= VHD_SCAN_NOFAIL;
1317
                        break;
1318
                case 'l':
1319
                        volume = optarg;
1320
                        flags |= VHD_SCAN_VOLUME;
1321
                        break;
1322
                case 'p':
1323
                        flags |= VHD_SCAN_PRETTY;
1324
                        break;
1325
                case 'a':
1326
                        flags |= VHD_SCAN_PARENTS;
1327
                        break;
1328
                case 'v':
1329
                        flags |= VHD_SCAN_VERBOSE;
1330
                        break;
1331
                case 'M':
1332
                        flags |= VHD_SCAN_MARKERS;
1333
                        break;
1334
                case 'h':
1335
                        goto usage;
1336
                default:
1337
                        err = -EINVAL;
1338
                        goto usage;
1339
                }
1340
        }
1341

    
1342
        if (!filter && argc - optind == 0) {
1343
                err = -EINVAL;
1344
                goto usage;
1345
        }
1346

    
1347
        if (flags & VHD_SCAN_PRETTY)
1348
                flags &= ~VHD_SCAN_FAST;
1349

    
1350
        err = vhd_util_scan_find_targets(argc - optind, argv + optind,
1351
                                         volume, filter, &targets, &cnt);
1352
        if (err) {
1353
                printf("scan failed: %d\n", err);
1354
                return err;
1355
        }
1356

    
1357
        if (!cnt)
1358
                return 0;
1359

    
1360
        if (flags & VHD_SCAN_PRETTY)
1361
                err = vhd_util_scan_targets_pretty(cnt, targets);
1362
        else
1363
                err = vhd_util_scan_targets(cnt, targets);
1364

    
1365
        free(targets);
1366
        lvm_free_vg(&vg);
1367

    
1368
        return ((flags & VHD_SCAN_NOFAIL) ? 0 : err);
1369

    
1370
usage:
1371
        printf("usage: [OPTIONS] FILES\n"
1372
               "options: [-m match filter] [-f fast] [-c continue on failure] "
1373
               "[-l LVM volume] [-p pretty print] [-a scan parents] "
1374
               "[-v verbose] [-h help] [-M show markers]\n");
1375
        return err;
1376
}