📄 vhd-util-scan.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 "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;
off64_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);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -