📄 libvhd.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.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <libgen.h>
#include <iconv.h>
#include <sys/mman.h>
#include "libvhd.h"
#include "relative-path.h"
static int libvhd_dbg = 0;
void
libvhd_set_log_level(int level)
{
if (level)
libvhd_dbg = 1;
}
#define VHDLOG(_f, _a...) \
do { \
if (libvhd_dbg) \
syslog(LOG_INFO, "libvhd::%s: "_f, \
__func__, ##_a); \
} while (0)
#define BIT_MASK 0x80
#ifdef ENABLE_FAILURE_TESTING
const char* ENV_VAR_FAIL[NUM_FAIL_TESTS] = {
"VHD_UTIL_TEST_FAIL_REPARENT_BEGIN",
"VHD_UTIL_TEST_FAIL_REPARENT_LOCATOR",
"VHD_UTIL_TEST_FAIL_REPARENT_END",
"VHD_UTIL_TEST_FAIL_RESIZE_BEGIN",
"VHD_UTIL_TEST_FAIL_RESIZE_DATA_MOVED",
"VHD_UTIL_TEST_FAIL_RESIZE_METADATA_MOVED",
"VHD_UTIL_TEST_FAIL_RESIZE_END"
};
int TEST_FAIL[NUM_FAIL_TESTS];
#endif // ENABLE_FAILURE_TESTING
static inline int
test_bit (volatile char *addr, int nr)
{
return ((addr[nr >> 3] << (nr & 7)) & BIT_MASK) != 0;
}
static inline void
set_bit (volatile char *addr, int nr)
{
addr[nr >> 3] |= (BIT_MASK >> (nr & 7));
}
static inline void
clear_bit (volatile char *addr, int nr)
{
addr[nr >> 3] &= ~(BIT_MASK >> (nr & 7));
}
static inline int
old_test_bit(volatile char *addr, int nr)
{
return (((uint32_t *)addr)[nr >> 5] >> (nr & 31)) & 1;
}
static inline void
old_set_bit(volatile char *addr, int nr)
{
((uint32_t *)addr)[nr >> 5] |= (1 << (nr & 31));
}
static inline void
old_clear_bit(volatile char *addr, int nr)
{
((uint32_t *)addr)[nr >> 5] &= ~(1 << (nr & 31));
}
void
vhd_footer_in(vhd_footer_t *footer)
{
BE32_IN(&footer->features);
BE32_IN(&footer->ff_version);
BE64_IN(&footer->data_offset);
BE32_IN(&footer->timestamp);
BE32_IN(&footer->crtr_ver);
BE32_IN(&footer->crtr_os);
BE64_IN(&footer->orig_size);
BE64_IN(&footer->curr_size);
BE32_IN(&footer->geometry);
BE32_IN(&footer->type);
BE32_IN(&footer->checksum);
}
void
vhd_footer_out(vhd_footer_t *footer)
{
BE32_OUT(&footer->features);
BE32_OUT(&footer->ff_version);
BE64_OUT(&footer->data_offset);
BE32_OUT(&footer->timestamp);
BE32_OUT(&footer->crtr_ver);
BE32_OUT(&footer->crtr_os);
BE64_OUT(&footer->orig_size);
BE64_OUT(&footer->curr_size);
BE32_OUT(&footer->geometry);
BE32_OUT(&footer->type);
BE32_OUT(&footer->checksum);
}
void
vhd_header_in(vhd_header_t *header)
{
int i, n;
BE64_IN(&header->data_offset);
BE64_IN(&header->table_offset);
BE32_IN(&header->hdr_ver);
BE32_IN(&header->max_bat_size);
BE32_IN(&header->block_size);
BE32_IN(&header->checksum);
BE32_IN(&header->prt_ts);
n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
for (i = 0; i < n; i++) {
BE32_IN(&header->loc[i].code);
BE32_IN(&header->loc[i].data_space);
BE32_IN(&header->loc[i].data_len);
BE64_IN(&header->loc[i].data_offset);
}
}
void
vhd_header_out(vhd_header_t *header)
{
int i, n;
BE64_OUT(&header->data_offset);
BE64_OUT(&header->table_offset);
BE32_OUT(&header->hdr_ver);
BE32_OUT(&header->max_bat_size);
BE32_OUT(&header->block_size);
BE32_OUT(&header->checksum);
BE32_OUT(&header->prt_ts);
n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
for (i = 0; i < n; i++) {
BE32_OUT(&header->loc[i].code);
BE32_OUT(&header->loc[i].data_space);
BE32_OUT(&header->loc[i].data_len);
BE64_OUT(&header->loc[i].data_offset);
}
}
void
vhd_batmap_header_in(vhd_batmap_t *batmap)
{
BE64_IN(&batmap->header.batmap_offset);
BE32_IN(&batmap->header.batmap_size);
BE32_IN(&batmap->header.batmap_version);
BE32_IN(&batmap->header.checksum);
}
void
vhd_batmap_header_out(vhd_batmap_t *batmap)
{
BE64_OUT(&batmap->header.batmap_offset);
BE32_OUT(&batmap->header.batmap_size);
BE32_OUT(&batmap->header.batmap_version);
BE32_OUT(&batmap->header.checksum);
}
void
vhd_bat_in(vhd_bat_t *bat)
{
int i;
for (i = 0; i < bat->entries; i++)
BE32_IN(&bat->bat[i]);
}
void
vhd_bat_out(vhd_bat_t *bat)
{
int i;
for (i = 0; i < bat->entries; i++)
BE32_OUT(&bat->bat[i]);
}
uint32_t
vhd_checksum_footer(vhd_footer_t *footer)
{
int i;
unsigned char *blob;
uint32_t checksum, tmp;
checksum = 0;
tmp = footer->checksum;
footer->checksum = 0;
blob = (unsigned char *)footer;
for (i = 0; i < sizeof(vhd_footer_t); i++)
checksum += (uint32_t)blob[i];
footer->checksum = tmp;
return ~checksum;
}
int
vhd_validate_footer(vhd_footer_t *footer)
{
int csize;
uint32_t checksum;
csize = sizeof(footer->cookie);
if (memcmp(footer->cookie, HD_COOKIE, csize) != 0 &&
memcmp(footer->cookie, VHD_POISON_COOKIE, csize) != 0) {
char buf[9];
memcpy(buf, footer->cookie, 8);
buf[8]= '\0';
VHDLOG("invalid footer cookie: %s\n", buf);
return -EINVAL;
}
checksum = vhd_checksum_footer(footer);
if (checksum != footer->checksum) {
/*
* early td-util did not re-calculate
* checksum when marking vhds 'hidden'
*/
if (footer->hidden &&
!strncmp(footer->crtr_app, "tap", 3) &&
(footer->crtr_ver == VHD_VERSION(0, 1) ||
footer->crtr_ver == VHD_VERSION(1, 1))) {
char tmp = footer->hidden;
footer->hidden = 0;
checksum = vhd_checksum_footer(footer);
footer->hidden = tmp;
if (checksum == footer->checksum)
return 0;
}
VHDLOG("invalid footer checksum: "
"footer = 0x%08x, calculated = 0x%08x\n",
footer->checksum, checksum);
return -EINVAL;
}
return 0;
}
uint32_t
vhd_checksum_header(vhd_header_t *header)
{
int i;
unsigned char *blob;
uint32_t checksum, tmp;
checksum = 0;
tmp = header->checksum;
header->checksum = 0;
blob = (unsigned char *)header;
for (i = 0; i < sizeof(vhd_header_t); i++)
checksum += (uint32_t)blob[i];
header->checksum = tmp;
return ~checksum;
}
int
vhd_validate_header(vhd_header_t *header)
{
int i, n;
uint32_t checksum;
if (memcmp(header->cookie, DD_COOKIE, 8) != 0) {
char buf[9];
memcpy(buf, header->cookie, 8);
buf[8] = '\0';
VHDLOG("invalid header cookie: %s\n", buf);
return -EINVAL;
}
if (header->hdr_ver != 0x00010000) {
VHDLOG("invalid header version 0x%08x\n", header->hdr_ver);
return -EINVAL;
}
if (header->data_offset != 0xFFFFFFFFFFFFFFFF) {
VHDLOG("invalid header data_offset 0x%016llx\n",
header->data_offset);
return -EINVAL;
}
n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
for (i = 0; i < n; i++)
if (vhd_validate_platform_code(header->loc[i].code))
return -EINVAL;
checksum = vhd_checksum_header(header);
if (checksum != header->checksum) {
VHDLOG("invalid header checksum: "
"header = 0x%08x, calculated = 0x%08x\n",
header->checksum, checksum);
return -EINVAL;
}
return 0;
}
static inline int
vhd_validate_bat(vhd_bat_t *bat)
{
if (!bat->bat)
return -EINVAL;
return 0;
}
uint32_t
vhd_checksum_batmap(vhd_batmap_t *batmap)
{
int i, n;
char *blob;
uint32_t checksum;
blob = batmap->map;
checksum = 0;
n = vhd_sectors_to_bytes(batmap->header.batmap_size);
for (i = 0; i < n; i++) {
if (batmap->header.batmap_version == VHD_BATMAP_VERSION(1, 1))
checksum += (uint32_t)blob[i];
else
checksum += (uint32_t)(unsigned char)blob[i];
}
return ~checksum;
}
int
vhd_validate_batmap_header(vhd_batmap_t *batmap)
{
if (memcmp(batmap->header.cookie, VHD_BATMAP_COOKIE, 8))
return -EINVAL;
if (batmap->header.batmap_version > VHD_BATMAP_CURRENT_VERSION)
return -EINVAL;
return 0;
}
int
vhd_validate_batmap(vhd_batmap_t *batmap)
{
uint32_t checksum;
if (!batmap->map)
return -EINVAL;
checksum = vhd_checksum_batmap(batmap);
if (checksum != batmap->header.checksum)
return -EINVAL;
return 0;
}
int
vhd_batmap_header_offset(vhd_context_t *ctx, off64_t *_off)
{
off64_t off;
size_t bat;
*_off = 0;
off = ctx->header.table_offset;
bat = ctx->header.max_bat_size * sizeof(uint32_t);
off += vhd_bytes_padded(bat);
*_off = off;
return 0;
}
int
vhd_validate_platform_code(uint32_t code)
{
switch (code) {
case PLAT_CODE_NONE:
case PLAT_CODE_WI2R:
case PLAT_CODE_WI2K:
case PLAT_CODE_W2RU:
case PLAT_CODE_W2KU:
case PLAT_CODE_MAC:
case PLAT_CODE_MACX:
return 0;
default:
VHDLOG("invalid parent locator code %u\n", code);
return -EINVAL;
}
}
int
vhd_parent_locator_count(vhd_context_t *ctx)
{
return (sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t));
}
int
vhd_hidden(vhd_context_t *ctx, int *hidden)
{
int err;
*hidden = 0;
if (vhd_type_dynamic(ctx) && vhd_creator_tapdisk(ctx) &&
(ctx->footer.crtr_ver == VHD_VERSION(0, 1) ||
ctx->footer.crtr_ver == VHD_VERSION(1, 1))) {
vhd_footer_t copy;
err = vhd_read_footer_at(ctx, ©, 0);
if (err) {
VHDLOG("error reading backup footer of %s: %d\n",
ctx->file, err);
return err;
}
*hidden = copy.hidden;
} else
*hidden = ctx->footer.hidden;
return 0;
}
int
vhd_chain_depth(vhd_context_t *ctx, int *depth)
{
char *file;
int err, cnt;
vhd_context_t vhd, *cur;
err = 0;
cnt = 0;
*depth = 0;
file = NULL;
cur = ctx;
for (;;) {
cnt++;
if (cur->footer.type != HD_TYPE_DIFF)
break;
if (vhd_parent_raw(cur)) {
cnt++;
break;
}
err = vhd_parent_locator_get(cur, &file);
if (err) {
file = NULL;
break;
}
if (cur != ctx) {
vhd_close(cur);
cur = NULL;
}
err = vhd_open(&vhd, file, VHD_OPEN_RDONLY);
if (err)
break;
cur = &vhd;
free(file);
file = NULL;
}
free(file);
if (cur && cur != ctx)
vhd_close(cur);
if (!err)
*depth = cnt;
return err;
}
int
vhd_batmap_test(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
{
if (!vhd_has_batmap(ctx) || !batmap->map)
return 0;
if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
return 0;
return test_bit(batmap->map, block);
}
void
vhd_batmap_set(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
{
if (!vhd_has_batmap(ctx) || !batmap->map)
return;
if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
return;
set_bit(batmap->map, block);
}
void
vhd_batmap_clear(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
{
if (!vhd_has_batmap(ctx) || !batmap->map)
return;
if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
return;
clear_bit(batmap->map, block);
}
int
vhd_bitmap_test(vhd_context_t *ctx, char *map, uint32_t block)
{
if (vhd_creator_tapdisk(ctx) &&
ctx->footer.crtr_ver == 0x00000001)
return old_test_bit(map, block);
return test_bit(map, block);
}
void
vhd_bitmap_set(vhd_context_t *ctx, char *map, uint32_t block)
{
if (vhd_creator_tapdisk(ctx) &&
ctx->footer.crtr_ver == 0x00000001)
return old_set_bit(map, block);
return set_bit(map, block);
}
void
vhd_bitmap_clear(vhd_context_t *ctx, char *map, uint32_t block)
{
if (vhd_creator_tapdisk(ctx) &&
ctx->footer.crtr_ver == 0x00000001)
return old_clear_bit(map, block);
return clear_bit(map, block);
}
/*
* returns absolute offset of the first
* byte of the file which is not vhd metadata
*/
int
vhd_end_of_headers(vhd_context_t *ctx, off64_t *end)
{
int err, i, n;
uint32_t bat_bytes;
off64_t eom, bat_end;
vhd_parent_locator_t *loc;
*end = 0;
if (!vhd_type_dynamic(ctx))
return 0;
eom = ctx->footer.data_offset + sizeof(vhd_header_t);
bat_bytes = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
bat_end = ctx->header.table_offset + bat_bytes;
eom = MAX(eom, bat_end);
if (vhd_has_batmap(ctx)) {
off64_t hdr_end, hdr_secs, map_end, map_secs;
err = vhd_get_batmap(ctx);
if (err)
return err;
hdr_secs = secs_round_up_no_zero(sizeof(vhd_batmap_header_t));
err = vhd_batmap_header_offset(ctx, &hdr_end);
if (err)
return err;
hdr_end += vhd_sectors_to_bytes(hdr_secs);
eom = MAX(eom, hdr_end);
map_secs = ctx->batmap.header.batmap_size;
map_end = (ctx->batmap.header.batmap_offset +
vhd_sectors_to_bytes(map_secs));
eom = MAX(eom, map_end);
}
/* parent locators */
n = sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t);
for (i = 0; i < n; i++) {
off64_t loc_end;
loc = &ctx->header.loc[i];
if (loc->code == PLAT_CODE_NONE)
continue;
loc_end = loc->data_offset + vhd_parent_locator_size(loc);
eom = MAX(eom, loc_end);
}
*end = eom;
return 0;
}
int
vhd_end_of_data(vhd_context_t *ctx, off64_t *end)
{
int i, err;
off64_t max;
uint64_t blk;
if (!vhd_type_dynamic(ctx)) {
err = vhd_seek(ctx, 0, SEEK_END);
if (err)
return err;
max = vhd_position(ctx);
if (max == (off64_t)-1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -