mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
1. Some files use stat, mkfifo, mkdir etc. without including sys/stat.h 2. Some programs link against libpthread without a -lpthread compile option. The compile used to work if this library happened to be used by one of the other libraries that was being linked against, but Fedora 13 has stopped allowing this. From: M A Young <m.a.young@durham.ac.uk> Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
1318 lines
26 KiB
C
1318 lines
26 KiB
C
/* Copyright (c) 2008, XenSource Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of XenSource Inc. nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <glob.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fnmatch.h>
|
|
#include <libgen.h> /* for basename() */
|
|
#include <sys/stat.h>
|
|
|
|
#include "list.h"
|
|
#include "libvhd.h"
|
|
#include "lvm-util.h"
|
|
|
|
#define VHD_SCAN_FAST 0x01
|
|
#define VHD_SCAN_PRETTY 0x02
|
|
#define VHD_SCAN_VOLUME 0x04
|
|
#define VHD_SCAN_NOFAIL 0x08
|
|
#define VHD_SCAN_VERBOSE 0x10
|
|
#define VHD_SCAN_PARENTS 0x20
|
|
|
|
#define VHD_TYPE_RAW_FILE 0x01
|
|
#define VHD_TYPE_VHD_FILE 0x02
|
|
#define VHD_TYPE_RAW_VOLUME 0x04
|
|
#define VHD_TYPE_VHD_VOLUME 0x08
|
|
|
|
static inline int
|
|
target_volume(uint8_t type)
|
|
{
|
|
return (type == VHD_TYPE_RAW_VOLUME || type == VHD_TYPE_VHD_VOLUME);
|
|
}
|
|
|
|
static inline int
|
|
target_vhd(uint8_t type)
|
|
{
|
|
return (type == VHD_TYPE_VHD_FILE || type == VHD_TYPE_VHD_VOLUME);
|
|
}
|
|
|
|
struct target {
|
|
char name[VHD_MAX_NAME_LEN];
|
|
char device[VHD_MAX_NAME_LEN];
|
|
uint64_t size;
|
|
uint64_t start;
|
|
uint64_t end;
|
|
uint8_t type;
|
|
};
|
|
|
|
struct iterator {
|
|
int cur;
|
|
int cur_size;
|
|
int max_size;
|
|
struct target *targets;
|
|
};
|
|
|
|
struct vhd_image {
|
|
char *name;
|
|
char *parent;
|
|
uint64_t capacity;
|
|
off_t size;
|
|
uint8_t hidden;
|
|
int error;
|
|
char *message;
|
|
|
|
struct target *target;
|
|
|
|
struct list_head sibling;
|
|
struct list_head children;
|
|
struct vhd_image *parent_image;
|
|
};
|
|
|
|
struct vhd_scan {
|
|
int cur;
|
|
int size;
|
|
|
|
int lists_cur;
|
|
int lists_size;
|
|
|
|
struct vhd_image **images;
|
|
struct vhd_image **lists;
|
|
};
|
|
|
|
static int flags;
|
|
static struct vg vg;
|
|
static struct vhd_scan scan;
|
|
|
|
static int
|
|
vhd_util_scan_pretty_allocate_list(int cnt)
|
|
{
|
|
int i;
|
|
struct vhd_image *list;
|
|
|
|
memset(&scan, 0, sizeof(scan));
|
|
|
|
scan.lists_cur = 1;
|
|
scan.lists_size = 10;
|
|
|
|
scan.lists = calloc(scan.lists_size, sizeof(struct vhd_image *));
|
|
if (!scan.lists)
|
|
goto fail;
|
|
|
|
scan.lists[0] = calloc(cnt, sizeof(struct vhd_image));
|
|
if (!scan.lists[0])
|
|
goto fail;
|
|
|
|
scan.images = calloc(cnt, sizeof(struct vhd_image *));
|
|
if (!scan.images)
|
|
goto fail;
|
|
|
|
for (i = 0; i < cnt; i++)
|
|
scan.images[i] = scan.lists[0] + i;
|
|
|
|
scan.cur = 0;
|
|
scan.size = cnt;
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
if (scan.lists) {
|
|
free(scan.lists[0]);
|
|
free(scan.lists);
|
|
}
|
|
|
|
free(scan.images);
|
|
memset(&scan, 0, sizeof(scan));
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static void
|
|
vhd_util_scan_pretty_free_list(void)
|
|
{
|
|
int i;
|
|
|
|
if (scan.lists) {
|
|
for (i = 0; i < scan.lists_cur; i++)
|
|
free(scan.lists[i]);
|
|
free(scan.lists);
|
|
}
|
|
|
|
free(scan.images);
|
|
memset(&scan, 0, sizeof(scan));
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_pretty_add_image(struct vhd_image *image)
|
|
{
|
|
int i;
|
|
struct vhd_image *img;
|
|
|
|
for (i = 0; i < scan.cur; i++) {
|
|
img = scan.images[i];
|
|
if (!strcmp(img->name, image->name))
|
|
return 0;
|
|
}
|
|
|
|
if (scan.cur >= scan.size) {
|
|
struct vhd_image *new, **list;
|
|
|
|
if (scan.lists_cur >= scan.lists_size) {
|
|
list = realloc(scan.lists, scan.lists_size * 2 *
|
|
sizeof(struct vhd_image *));
|
|
if (!list)
|
|
return -ENOMEM;
|
|
|
|
scan.lists_size *= 2;
|
|
scan.lists = list;
|
|
}
|
|
|
|
new = calloc(scan.size, sizeof(struct vhd_image));
|
|
if (!new)
|
|
return -ENOMEM;
|
|
|
|
scan.lists[scan.lists_cur++] = new;
|
|
scan.size *= 2;
|
|
|
|
list = realloc(scan.images, scan.size *
|
|
sizeof(struct vhd_image *));
|
|
if (!list)
|
|
return -ENOMEM;
|
|
|
|
scan.images = list;
|
|
for (i = 0; i + scan.cur < scan.size; i++)
|
|
scan.images[i + scan.cur] = new + i;
|
|
}
|
|
|
|
img = scan.images[scan.cur];
|
|
INIT_LIST_HEAD(&img->sibling);
|
|
INIT_LIST_HEAD(&img->children);
|
|
|
|
img->capacity = image->capacity;
|
|
img->size = image->size;
|
|
img->hidden = image->hidden;
|
|
img->error = image->error;
|
|
img->message = image->message;
|
|
|
|
img->name = strdup(image->name);
|
|
if (!img->name)
|
|
goto fail;
|
|
|
|
if (image->parent) {
|
|
img->parent = strdup(image->parent);
|
|
if (!img->parent)
|
|
goto fail;
|
|
}
|
|
|
|
scan.cur++;
|
|
return 0;
|
|
|
|
fail:
|
|
free(img->name);
|
|
free(img->parent);
|
|
memset(img, 0, sizeof(*img));
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_pretty_image_compare(const void *lhs, const void *rhs)
|
|
{
|
|
struct vhd_image *l, *r;
|
|
|
|
l = *(struct vhd_image **)lhs;
|
|
r = *(struct vhd_image **)rhs;
|
|
|
|
return strcmp(l->name, r->name);
|
|
}
|
|
|
|
static void
|
|
vhd_util_scan_print_image_indent(struct vhd_image *image, int tab)
|
|
{
|
|
char *pad, *name, *pmsg, *parent;
|
|
|
|
pad = (tab ? " " : "");
|
|
name = image->name;
|
|
parent = (image->parent ? : "none");
|
|
|
|
if ((flags & VHD_SCAN_PRETTY) && image->parent && !image->parent_image)
|
|
pmsg = " (not found in scan)";
|
|
else
|
|
pmsg = "";
|
|
|
|
if (!(flags & VHD_SCAN_VERBOSE)) {
|
|
name = basename(image->name);
|
|
if (image->parent)
|
|
parent = basename(image->parent);
|
|
}
|
|
|
|
if (image->error)
|
|
printf("%*svhd=%s scan-error=%d error-message='%s'\n",
|
|
tab, pad, image->name, image->error, image->message);
|
|
else
|
|
printf("%*svhd=%s capacity=%"PRIu64" size=%"PRIu64" hidden=%u "
|
|
"parent=%s%s\n", tab, pad, name, image->capacity,
|
|
image->size, image->hidden, parent, pmsg);
|
|
}
|
|
|
|
static void
|
|
vhd_util_scan_pretty_print_tree(struct vhd_image *image, int depth)
|
|
{
|
|
struct vhd_image *img, *tmp;
|
|
|
|
vhd_util_scan_print_image_indent(image, depth * 3);
|
|
|
|
list_for_each_entry_safe(img, tmp, &image->children, sibling)
|
|
if (!img->hidden)
|
|
vhd_util_scan_pretty_print_tree(img, depth + 1);
|
|
|
|
list_for_each_entry_safe(img, tmp, &image->children, sibling)
|
|
if (img->hidden)
|
|
vhd_util_scan_pretty_print_tree(img, depth + 1);
|
|
|
|
free(image->name);
|
|
free(image->parent);
|
|
|
|
image->name = NULL;
|
|
image->parent = NULL;
|
|
}
|
|
|
|
static void
|
|
vhd_util_scan_pretty_print_images(void)
|
|
{
|
|
int i;
|
|
struct vhd_image *image, **parentp, *parent, *keyp, key;
|
|
|
|
qsort(scan.images, scan.cur, sizeof(scan.images[0]),
|
|
vhd_util_scan_pretty_image_compare);
|
|
|
|
for (i = 0; i < scan.cur; i++) {
|
|
image = scan.images[i];
|
|
|
|
if (!image->parent) {
|
|
image->parent_image = NULL;
|
|
continue;
|
|
}
|
|
|
|
memset(&key, 0, sizeof(key));
|
|
key.name = image->parent;
|
|
keyp = &key;
|
|
|
|
parentp = bsearch(&keyp, scan.images, scan.cur,
|
|
sizeof(scan.images[0]),
|
|
vhd_util_scan_pretty_image_compare);
|
|
if (!parentp) {
|
|
image->parent_image = NULL;
|
|
continue;
|
|
}
|
|
|
|
parent = *parentp;
|
|
image->parent_image = parent;
|
|
list_add_tail(&image->sibling, &parent->children);
|
|
}
|
|
|
|
for (i = 0; i < scan.cur; i++) {
|
|
image = scan.images[i];
|
|
|
|
if (image->parent_image || !image->hidden)
|
|
continue;
|
|
|
|
vhd_util_scan_pretty_print_tree(image, 0);
|
|
}
|
|
|
|
for (i = 0; i < scan.cur; i++) {
|
|
image = scan.images[i];
|
|
|
|
if (!image->name || image->parent_image)
|
|
continue;
|
|
|
|
vhd_util_scan_pretty_print_tree(image, 0);
|
|
}
|
|
|
|
for (i = 0; i < scan.cur; i++) {
|
|
image = scan.images[i];
|
|
|
|
if (!image->name)
|
|
continue;
|
|
|
|
vhd_util_scan_pretty_print_tree(image, 0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
vhd_util_scan_print_image(struct vhd_image *image)
|
|
{
|
|
int err;
|
|
|
|
if (!image->error && (flags & VHD_SCAN_PRETTY)) {
|
|
err = vhd_util_scan_pretty_add_image(image);
|
|
if (!err)
|
|
return;
|
|
|
|
if (!image->error) {
|
|
image->error = err;
|
|
image->message = "allocating memory";
|
|
}
|
|
}
|
|
|
|
vhd_util_scan_print_image_indent(image, 0);
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_error(const char *file, int err)
|
|
{
|
|
struct vhd_image image;
|
|
|
|
memset(&image, 0, sizeof(image));
|
|
image.name = (char *)file;
|
|
image.error = err;
|
|
image.message = "failure scanning target";
|
|
|
|
vhd_util_scan_print_image(&image);
|
|
|
|
/*
|
|
if (flags & VHD_SCAN_NOFAIL)
|
|
return 0;
|
|
*/
|
|
|
|
return err;
|
|
}
|
|
|
|
static vhd_parent_locator_t *
|
|
vhd_util_scan_get_parent_locator(vhd_context_t *vhd)
|
|
{
|
|
int i;
|
|
vhd_parent_locator_t *loc;
|
|
|
|
loc = NULL;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
if (vhd->header.loc[i].code == PLAT_CODE_MACX) {
|
|
loc = vhd->header.loc + i;
|
|
break;
|
|
}
|
|
|
|
if (vhd->header.loc[i].code == PLAT_CODE_W2RU)
|
|
loc = vhd->header.loc + i;
|
|
|
|
if (!loc && vhd->header.loc[i].code != PLAT_CODE_NONE)
|
|
loc = vhd->header.loc + i;
|
|
}
|
|
|
|
return loc;
|
|
}
|
|
|
|
static inline int
|
|
copy_name(char *dst, const char *src)
|
|
{
|
|
if (snprintf(dst, VHD_MAX_NAME_LEN, "%s", src) < VHD_MAX_NAME_LEN)
|
|
return 0;
|
|
|
|
return -ENAMETOOLONG;
|
|
}
|
|
|
|
/*
|
|
* LVHD stores realpath(parent) in parent locators, so
|
|
* /dev/<vol-group>/<lv-name> becomes /dev/mapper/<vol--group>-<lv--name>
|
|
*/
|
|
static int
|
|
vhd_util_scan_extract_volume_name(char *dst, const char *src)
|
|
{
|
|
int err;
|
|
char copy[VHD_MAX_NAME_LEN], *name, *s, *c;
|
|
|
|
name = strrchr(src, '/');
|
|
if (!name)
|
|
name = (char *)src;
|
|
|
|
/* convert single dashes to slashes, double dashes to single dashes */
|
|
for (c = copy, s = name; *s != '\0'; s++, c++) {
|
|
if (*s == '-') {
|
|
if (s[1] != '-')
|
|
*c = '/';
|
|
else {
|
|
s++;
|
|
*c = '-';
|
|
}
|
|
} else
|
|
*c = *s;
|
|
}
|
|
|
|
*c = '\0';
|
|
c = strrchr(copy, '/');
|
|
if (c == name) {
|
|
/* unrecognized format */
|
|
strcpy(dst, src);
|
|
return -EINVAL;
|
|
}
|
|
|
|
strcpy(dst, ++c);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_get_volume_parent(vhd_context_t *vhd, struct vhd_image *image)
|
|
{
|
|
int err;
|
|
char name[VHD_MAX_NAME_LEN];
|
|
vhd_parent_locator_t *loc, copy;
|
|
|
|
if (flags & VHD_SCAN_FAST) {
|
|
err = vhd_header_decode_parent(vhd,
|
|
&vhd->header, &image->parent);
|
|
if (!err)
|
|
goto found;
|
|
}
|
|
|
|
loc = vhd_util_scan_get_parent_locator(vhd);
|
|
if (!loc)
|
|
return -EINVAL;
|
|
|
|
copy = *loc;
|
|
copy.data_offset += image->target->start;
|
|
err = vhd_parent_locator_read(vhd, ©, &image->parent);
|
|
if (err)
|
|
return err;
|
|
|
|
found:
|
|
err = vhd_util_scan_extract_volume_name(name, image->parent);
|
|
if (!err)
|
|
return copy_name(image->parent, name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_get_parent(vhd_context_t *vhd, struct vhd_image *image)
|
|
{
|
|
int i, err;
|
|
vhd_parent_locator_t *loc;
|
|
|
|
if (!target_vhd(image->target->type)) {
|
|
image->parent = NULL;
|
|
return 0;
|
|
}
|
|
|
|
loc = NULL;
|
|
|
|
if (target_volume(image->target->type))
|
|
return vhd_util_scan_get_volume_parent(vhd, image);
|
|
|
|
if (flags & VHD_SCAN_FAST) {
|
|
err = vhd_header_decode_parent(vhd,
|
|
&vhd->header, &image->parent);
|
|
if (!err)
|
|
return 0;
|
|
} else {
|
|
/*
|
|
* vhd_parent_locator_get checks for the existence of the
|
|
* parent file. if this call succeeds, all is well; if not,
|
|
* we'll try to return whatever string we have before failing
|
|
* outright.
|
|
*/
|
|
err = vhd_parent_locator_get(vhd, &image->parent);
|
|
if (!err)
|
|
return 0;
|
|
}
|
|
|
|
loc = vhd_util_scan_get_parent_locator(vhd);
|
|
if (!loc)
|
|
return -EINVAL;
|
|
|
|
return vhd_parent_locator_read(vhd, loc, &image->parent);
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_get_hidden(vhd_context_t *vhd, struct vhd_image *image)
|
|
{
|
|
int err, hidden;
|
|
|
|
err = 0;
|
|
hidden = 0;
|
|
|
|
if (target_vhd(image->target->type))
|
|
err = vhd_hidden(vhd, &hidden);
|
|
else
|
|
hidden = 1;
|
|
|
|
if (err)
|
|
return err;
|
|
|
|
image->hidden = hidden;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_get_size(vhd_context_t *vhd, struct vhd_image *image)
|
|
{
|
|
image->size = image->target->size;
|
|
|
|
if (target_vhd(image->target->type))
|
|
image->capacity = vhd->footer.curr_size;
|
|
else
|
|
image->capacity = image->size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_open_file(vhd_context_t *vhd, struct vhd_image *image)
|
|
{
|
|
int err, vhd_flags;
|
|
|
|
if (!target_vhd(image->target->type))
|
|
return 0;
|
|
|
|
vhd_flags = VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED;
|
|
if (flags & VHD_SCAN_FAST)
|
|
vhd_flags |= VHD_OPEN_FAST;
|
|
|
|
err = vhd_open(vhd, image->name, vhd_flags);
|
|
if (err) {
|
|
vhd->file = NULL;
|
|
image->message = "opening file";
|
|
image->error = err;
|
|
return image->error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_read_volume_headers(vhd_context_t *vhd, struct vhd_image *image)
|
|
{
|
|
int err;
|
|
char *buf;
|
|
size_t size;
|
|
struct target *target;
|
|
|
|
buf = NULL;
|
|
target = image->target;
|
|
size = sizeof(vhd_footer_t) + sizeof(vhd_header_t);
|
|
|
|
err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
|
|
if (err) {
|
|
buf = NULL;
|
|
image->message = "allocating image";
|
|
image->error = -err;
|
|
goto out;
|
|
}
|
|
|
|
err = vhd_seek(vhd, target->start, SEEK_SET);
|
|
if (err) {
|
|
image->message = "seeking to headers";
|
|
image->error = err;
|
|
goto out;
|
|
}
|
|
|
|
err = vhd_read(vhd, buf, size);
|
|
if (err) {
|
|
image->message = "reading headers";
|
|
image->error = err;
|
|
goto out;
|
|
}
|
|
|
|
memcpy(&vhd->footer, buf, sizeof(vhd_footer_t));
|
|
vhd_footer_in(&vhd->footer);
|
|
err = vhd_validate_footer(&vhd->footer);
|
|
if (err) {
|
|
image->message = "invalid footer";
|
|
image->error = err;
|
|
goto out;
|
|
}
|
|
|
|
/* lvhd vhds should always be dynamic */
|
|
if (vhd_type_dynamic(vhd)) {
|
|
if (vhd->footer.data_offset != sizeof(vhd_footer_t))
|
|
err = vhd_read_header_at(vhd, &vhd->header,
|
|
vhd->footer.data_offset +
|
|
target->start);
|
|
else {
|
|
memcpy(&vhd->header,
|
|
buf + sizeof(vhd_footer_t),
|
|
sizeof(vhd_header_t));
|
|
vhd_header_in(&vhd->header);
|
|
err = vhd_validate_header(&vhd->header);
|
|
}
|
|
|
|
if (err) {
|
|
image->message = "reading header";
|
|
image->error = err;
|
|
goto out;
|
|
}
|
|
|
|
vhd->spb = vhd->header.block_size >> VHD_SECTOR_SHIFT;
|
|
vhd->bm_secs = secs_round_up_no_zero(vhd->spb >> 3);
|
|
}
|
|
|
|
out:
|
|
free(buf);
|
|
return image->error;
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_open_volume(vhd_context_t *vhd, struct vhd_image *image)
|
|
{
|
|
int err;
|
|
struct target *target;
|
|
|
|
target = image->target;
|
|
memset(vhd, 0, sizeof(*vhd));
|
|
vhd->oflags = VHD_OPEN_RDONLY | VHD_OPEN_FAST;
|
|
|
|
if (target->end - target->start < 4096) {
|
|
image->message = "device too small";
|
|
image->error = -EINVAL;
|
|
return image->error;
|
|
}
|
|
|
|
vhd->file = strdup(image->name);
|
|
if (!vhd->file) {
|
|
image->message = "allocating device";
|
|
image->error = -ENOMEM;
|
|
return image->error;
|
|
}
|
|
|
|
vhd->fd = open(target->device, O_RDONLY | O_DIRECT | O_LARGEFILE);
|
|
if (vhd->fd == -1) {
|
|
free(vhd->file);
|
|
vhd->file = NULL;
|
|
|
|
image->message = "opening device";
|
|
image->error = -errno;
|
|
return image->error;
|
|
}
|
|
|
|
if (target_vhd(target->type))
|
|
return vhd_util_scan_read_volume_headers(vhd, image);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_open(vhd_context_t *vhd, struct vhd_image *image)
|
|
{
|
|
struct target *target;
|
|
|
|
target = image->target;
|
|
|
|
if (target_volume(image->target->type) || !(flags & VHD_SCAN_PRETTY))
|
|
image->name = target->name;
|
|
else {
|
|
image->name = realpath(target->name, NULL);
|
|
if (!image->name) {
|
|
image->name = target->name;
|
|
image->message = "resolving name";
|
|
image->error = -errno;
|
|
return image->error;
|
|
}
|
|
}
|
|
|
|
if (target_volume(target->type))
|
|
return vhd_util_scan_open_volume(vhd, image);
|
|
else
|
|
return vhd_util_scan_open_file(vhd, image);
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_init_file_target(struct target *target,
|
|
const char *file, uint8_t type)
|
|
{
|
|
int err;
|
|
struct stat stats;
|
|
|
|
err = stat(file, &stats);
|
|
if (err == -1)
|
|
return -errno;
|
|
|
|
err = copy_name(target->name, file);
|
|
if (err)
|
|
return err;
|
|
|
|
err = copy_name(target->device, file);
|
|
if (err)
|
|
return err;
|
|
|
|
target->type = type;
|
|
target->start = 0;
|
|
target->size = stats.st_size;
|
|
target->end = stats.st_size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_init_volume_target(struct target *target,
|
|
struct lv *lv, uint8_t type)
|
|
{
|
|
int err;
|
|
|
|
if (lv->first_segment.type != LVM_SEG_TYPE_LINEAR)
|
|
return -ENOSYS;
|
|
|
|
err = copy_name(target->name, lv->name);
|
|
if (err)
|
|
return err;
|
|
|
|
err = copy_name(target->device, lv->first_segment.device);
|
|
if (err)
|
|
return err;
|
|
|
|
target->type = type;
|
|
target->size = lv->size;
|
|
target->start = lv->first_segment.pe_start;
|
|
target->end = target->start + lv->first_segment.pe_size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iterator_init(struct iterator *itr, int cnt, struct target *targets)
|
|
{
|
|
memset(itr, 0, sizeof(*itr));
|
|
|
|
itr->targets = malloc(sizeof(struct target) * cnt);
|
|
if (!itr->targets)
|
|
return -ENOMEM;
|
|
|
|
memcpy(itr->targets, targets, sizeof(struct target) * cnt);
|
|
|
|
itr->cur = 0;
|
|
itr->cur_size = cnt;
|
|
itr->max_size = cnt;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct target *
|
|
iterator_next(struct iterator *itr)
|
|
{
|
|
if (itr->cur == itr->cur_size)
|
|
return NULL;
|
|
|
|
return itr->targets + itr->cur++;
|
|
}
|
|
|
|
static int
|
|
iterator_add_file(struct iterator *itr,
|
|
struct target *target, const char *parent, uint8_t type)
|
|
{
|
|
int i;
|
|
struct target *t;
|
|
char *lname, *rname;
|
|
|
|
for (i = 0; i < itr->cur_size; i++) {
|
|
t = itr->targets + i;
|
|
lname = basename((char *)t->name);
|
|
rname = basename((char *)parent);
|
|
|
|
if (!strcmp(lname, rname))
|
|
return -EEXIST;
|
|
}
|
|
|
|
return vhd_util_scan_init_file_target(target, parent, type);
|
|
}
|
|
|
|
static int
|
|
iterator_add_volume(struct iterator *itr,
|
|
struct target *target, const char *parent, uint8_t type)
|
|
{
|
|
int i, err;
|
|
struct lv *lv;
|
|
|
|
lv = NULL;
|
|
err = -ENOENT;
|
|
|
|
for (i = 0; i < itr->cur_size; i++)
|
|
if (!strcmp(parent, itr->targets[i].name))
|
|
return -EEXIST;
|
|
|
|
for (i = 0; i < vg.lv_cnt; i++) {
|
|
err = fnmatch(parent, vg.lvs[i].name, FNM_PATHNAME);
|
|
if (err != FNM_NOMATCH) {
|
|
lv = vg.lvs + i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (err && err != FNM_PATHNAME)
|
|
return err;
|
|
|
|
if (!lv)
|
|
return -ENOENT;
|
|
|
|
return vhd_util_scan_init_volume_target(target, lv, type);
|
|
}
|
|
|
|
static int
|
|
iterator_add(struct iterator *itr, const char *parent, uint8_t type)
|
|
{
|
|
int err;
|
|
struct target *target;
|
|
|
|
if (itr->cur_size == itr->max_size) {
|
|
struct target *new;
|
|
|
|
new = realloc(itr->targets,
|
|
sizeof(struct target) *
|
|
itr->max_size * 2);
|
|
if (!new)
|
|
return -ENOMEM;
|
|
|
|
itr->max_size *= 2;
|
|
itr->targets = new;
|
|
}
|
|
|
|
target = itr->targets + itr->cur_size;
|
|
|
|
if (target_volume(type))
|
|
err = iterator_add_volume(itr, target, parent, type);
|
|
else
|
|
err = iterator_add_file(itr, target, parent, type);
|
|
|
|
if (err)
|
|
memset(target, 0, sizeof(*target));
|
|
else
|
|
itr->cur_size++;
|
|
|
|
return (err == -EEXIST ? 0 : err);
|
|
}
|
|
|
|
static void
|
|
iterator_free(struct iterator *itr)
|
|
{
|
|
free(itr->targets);
|
|
memset(itr, 0, sizeof(*itr));
|
|
}
|
|
|
|
static void
|
|
vhd_util_scan_add_parent(struct iterator *itr,
|
|
vhd_context_t *vhd, struct vhd_image *image)
|
|
{
|
|
int err;
|
|
uint8_t type;
|
|
|
|
if (vhd_parent_raw(vhd))
|
|
type = target_volume(image->target->type) ?
|
|
VHD_TYPE_RAW_VOLUME : VHD_TYPE_RAW_FILE;
|
|
else
|
|
type = target_volume(image->target->type) ?
|
|
VHD_TYPE_VHD_VOLUME : VHD_TYPE_VHD_FILE;
|
|
|
|
err = iterator_add(itr, image->parent, type);
|
|
if (err)
|
|
vhd_util_scan_error(image->parent, err);
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_targets(int cnt, struct target *targets)
|
|
{
|
|
int ret, err;
|
|
vhd_context_t vhd;
|
|
struct iterator itr;
|
|
struct target *target;
|
|
struct vhd_image image;
|
|
|
|
ret = 0;
|
|
err = 0;
|
|
|
|
err = iterator_init(&itr, cnt, targets);
|
|
if (err)
|
|
return err;
|
|
|
|
while ((target = iterator_next(&itr))) {
|
|
memset(&vhd, 0, sizeof(vhd));
|
|
memset(&image, 0, sizeof(image));
|
|
|
|
image.target = target;
|
|
|
|
err = vhd_util_scan_open(&vhd, &image);
|
|
if (err) {
|
|
ret = -EAGAIN;
|
|
goto end;
|
|
}
|
|
|
|
err = vhd_util_scan_get_size(&vhd, &image);
|
|
if (err) {
|
|
ret = -EAGAIN;
|
|
image.message = "getting physical size";
|
|
image.error = err;
|
|
goto end;
|
|
}
|
|
|
|
err = vhd_util_scan_get_hidden(&vhd, &image);
|
|
if (err) {
|
|
ret = -EAGAIN;
|
|
image.message = "checking 'hidden' field";
|
|
image.error = err;
|
|
goto end;
|
|
}
|
|
|
|
if (vhd.footer.type == HD_TYPE_DIFF) {
|
|
err = vhd_util_scan_get_parent(&vhd, &image);
|
|
if (err) {
|
|
ret = -EAGAIN;
|
|
image.message = "getting parent";
|
|
image.error = err;
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
end:
|
|
vhd_util_scan_print_image(&image);
|
|
|
|
if (flags & VHD_SCAN_PARENTS && image.parent)
|
|
vhd_util_scan_add_parent(&itr, &vhd, &image);
|
|
|
|
if (vhd.file)
|
|
vhd_close(&vhd);
|
|
if (image.name != target->name)
|
|
free(image.name);
|
|
free(image.parent);
|
|
|
|
if (err && !(flags & VHD_SCAN_NOFAIL))
|
|
break;
|
|
}
|
|
|
|
iterator_free(&itr);
|
|
|
|
if (flags & VHD_SCAN_NOFAIL)
|
|
return ret;
|
|
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_targets_pretty(int cnt, struct target *targets)
|
|
{
|
|
int err;
|
|
|
|
err = vhd_util_scan_pretty_allocate_list(cnt);
|
|
if (err) {
|
|
printf("scan failed: no memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
err = vhd_util_scan_targets(cnt, targets);
|
|
|
|
vhd_util_scan_pretty_print_images();
|
|
vhd_util_scan_pretty_free_list();
|
|
|
|
return ((flags & VHD_SCAN_NOFAIL) ? 0 : err);
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_find_file_targets(int cnt, char **names,
|
|
const char *filter,
|
|
struct target **_targets, int *_total)
|
|
{
|
|
glob_t g;
|
|
struct target *targets;
|
|
int i, globs, err, total;
|
|
|
|
total = cnt;
|
|
globs = 0;
|
|
*_total = 0;
|
|
*_targets = NULL;
|
|
|
|
memset(&g, 0, sizeof(g));
|
|
|
|
if (filter) {
|
|
int gflags = ((flags & VHD_SCAN_FAST) ? GLOB_NOSORT : 0);
|
|
|
|
errno = 0;
|
|
err = glob(filter, gflags, vhd_util_scan_error, &g);
|
|
|
|
switch (err) {
|
|
case GLOB_NOSPACE:
|
|
err = -ENOMEM;
|
|
break;
|
|
case GLOB_ABORTED:
|
|
err = -EIO;
|
|
break;
|
|
case GLOB_NOMATCH:
|
|
err = -errno;
|
|
break;
|
|
}
|
|
|
|
if (err) {
|
|
vhd_util_scan_error(filter, err);
|
|
return err;
|
|
}
|
|
|
|
globs = g.gl_pathc;
|
|
total += globs;
|
|
}
|
|
|
|
targets = calloc(total, sizeof(struct target));
|
|
if (!targets) {
|
|
err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < g.gl_pathc; i++) {
|
|
err = vhd_util_scan_init_file_target(targets + i,
|
|
g.gl_pathv[i],
|
|
VHD_TYPE_VHD_FILE);
|
|
if (err) {
|
|
vhd_util_scan_error(g.gl_pathv[i], err);
|
|
if (!(flags & VHD_SCAN_NOFAIL))
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i + globs < total; i++) {
|
|
err = vhd_util_scan_init_file_target(targets + i + globs,
|
|
names[i],
|
|
VHD_TYPE_VHD_FILE);
|
|
if (err) {
|
|
vhd_util_scan_error(names[i], err);
|
|
if (!(flags & VHD_SCAN_NOFAIL))
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
err = 0;
|
|
*_total = total;
|
|
*_targets = targets;
|
|
|
|
out:
|
|
if (err)
|
|
free(targets);
|
|
if (filter)
|
|
globfree(&g);
|
|
|
|
return err;
|
|
}
|
|
|
|
static inline void
|
|
swap_volume(struct lv *lvs, int dst, int src)
|
|
{
|
|
struct lv copy, *ldst, *lsrc;
|
|
|
|
if (dst == src)
|
|
return;
|
|
|
|
lsrc = lvs + src;
|
|
ldst = lvs + dst;
|
|
|
|
memcpy(©, ldst, sizeof(copy));
|
|
memcpy(ldst, lsrc, sizeof(*ldst));
|
|
memcpy(lsrc, ©, sizeof(copy));
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_sort_volumes(struct lv *lvs, int cnt,
|
|
const char *filter, int *_matches)
|
|
{
|
|
struct lv *lv;
|
|
int i, err, matches;
|
|
|
|
matches = 0;
|
|
*_matches = 0;
|
|
|
|
if (!filter)
|
|
return 0;
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
lv = lvs + i;
|
|
|
|
err = fnmatch(filter, lv->name, FNM_PATHNAME);
|
|
if (err) {
|
|
if (err != FNM_NOMATCH) {
|
|
vhd_util_scan_error(lv->name, err);
|
|
if (!(flags & VHD_SCAN_NOFAIL))
|
|
return err;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
swap_volume(lvs, matches++, i);
|
|
}
|
|
|
|
*_matches = matches;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_find_volume_targets(int cnt, char **names,
|
|
const char *volume, const char *filter,
|
|
struct target **_targets, int *_total)
|
|
{
|
|
struct target *targets;
|
|
int i, err, total, matches;
|
|
|
|
*_total = 0;
|
|
*_targets = NULL;
|
|
targets = NULL;
|
|
|
|
err = lvm_scan_vg(volume, &vg);
|
|
if (err)
|
|
return err;
|
|
|
|
err = vhd_util_scan_sort_volumes(vg.lvs, vg.lv_cnt,
|
|
filter, &matches);
|
|
if (err)
|
|
goto out;
|
|
|
|
total = matches;
|
|
for (i = 0; i < cnt; i++) {
|
|
err = vhd_util_scan_sort_volumes(vg.lvs + total,
|
|
vg.lv_cnt - total,
|
|
names[i], &matches);
|
|
if (err)
|
|
goto out;
|
|
|
|
total += matches;
|
|
}
|
|
|
|
targets = calloc(total, sizeof(struct target));
|
|
if (!targets) {
|
|
err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < total; i++) {
|
|
err = vhd_util_scan_init_volume_target(targets + i,
|
|
vg.lvs + i,
|
|
VHD_TYPE_VHD_VOLUME);
|
|
if (err) {
|
|
vhd_util_scan_error(vg.lvs[i].name, err);
|
|
if (!(flags & VHD_SCAN_NOFAIL))
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
err = 0;
|
|
*_total = total;
|
|
*_targets = targets;
|
|
|
|
out:
|
|
if (err)
|
|
free(targets);
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
vhd_util_scan_find_targets(int cnt, char **names,
|
|
const char *volume, const char *filter,
|
|
struct target **targets, int *total)
|
|
{
|
|
if (flags & VHD_SCAN_VOLUME)
|
|
return vhd_util_scan_find_volume_targets(cnt, names,
|
|
volume, filter,
|
|
targets, total);
|
|
return vhd_util_scan_find_file_targets(cnt, names,
|
|
filter, targets, total);
|
|
}
|
|
|
|
int
|
|
vhd_util_scan(int argc, char **argv)
|
|
{
|
|
int c, ret, err, cnt;
|
|
char *filter, *volume;
|
|
struct target *targets;
|
|
|
|
cnt = 0;
|
|
ret = 0;
|
|
err = 0;
|
|
flags = 0;
|
|
filter = NULL;
|
|
volume = NULL;
|
|
targets = NULL;
|
|
|
|
optind = 0;
|
|
while ((c = getopt(argc, argv, "m:fcl:pavh")) != -1) {
|
|
switch (c) {
|
|
case 'm':
|
|
filter = optarg;
|
|
break;
|
|
case 'f':
|
|
flags |= VHD_SCAN_FAST;
|
|
break;
|
|
case 'c':
|
|
flags |= VHD_SCAN_NOFAIL;
|
|
break;
|
|
case 'l':
|
|
volume = optarg;
|
|
flags |= VHD_SCAN_VOLUME;
|
|
break;
|
|
case 'p':
|
|
flags |= VHD_SCAN_PRETTY;
|
|
break;
|
|
case 'a':
|
|
flags |= VHD_SCAN_PARENTS;
|
|
break;
|
|
case 'v':
|
|
flags |= VHD_SCAN_VERBOSE;
|
|
break;
|
|
case 'h':
|
|
goto usage;
|
|
default:
|
|
err = -EINVAL;
|
|
goto usage;
|
|
}
|
|
}
|
|
|
|
if (!filter && argc - optind == 0) {
|
|
err = -EINVAL;
|
|
goto usage;
|
|
}
|
|
|
|
if (flags & VHD_SCAN_PRETTY)
|
|
flags &= ~VHD_SCAN_FAST;
|
|
|
|
err = vhd_util_scan_find_targets(argc - optind, argv + optind,
|
|
volume, filter, &targets, &cnt);
|
|
if (err) {
|
|
printf("scan failed: %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
if (!cnt)
|
|
return 0;
|
|
|
|
if (flags & VHD_SCAN_PRETTY)
|
|
err = vhd_util_scan_targets_pretty(cnt, targets);
|
|
else
|
|
err = vhd_util_scan_targets(cnt, targets);
|
|
|
|
free(targets);
|
|
lvm_free_vg(&vg);
|
|
|
|
return ((flags & VHD_SCAN_NOFAIL) ? 0 : err);
|
|
|
|
usage:
|
|
printf("usage: [OPTIONS] FILES\n"
|
|
"options: [-m match filter] [-f fast] [-c continue on failure] "
|
|
"[-l LVM volume] [-p pretty print] [-a scan parents] "
|
|
"[-v verbose] [-h help]\n");
|
|
return err;
|
|
}
|