📄 libvhd.c
字号:
sprintf(uri, "file://%s", name);
if (iconv(cd, &urip, &ibl, &uri_utf8p, &obl) == (size_t)-1 ||
ibl || obl) {
err = (errno ? -errno : -EIO);
goto out;
}
ret = malloc(len);
if (!ret) {
err = -ENOMEM;
goto out;
}
memcpy(ret, uri_utf8, len);
*outlen = len;
*out = ret;
out:
free(uri);
free(uri_utf8);
if (cd != (iconv_t)-1)
iconv_close(cd);
return err;
}
static int
vhd_w2u_encode_location(char *name, char **out, int *outlen)
{
iconv_t cd;
int len, err;
size_t ibl, obl;
char *uri, *urip, *uri_utf16, *uri_utf16p, *tmp, *ret;
err = 0;
ret = NULL;
*out = NULL;
*outlen = 0;
cd = (iconv_t) -1;
/*
* MICROSOFT_COMPAT
* relative paths must start with ".\"
*/
if (name[0] != '/') {
tmp = strstr(name, "./");
if (tmp == name)
tmp += strlen("./");
else
tmp = name;
err = asprintf(&uri, ".\\%s", tmp);
} else
err = asprintf(&uri, "%s", name);
if (err == -1)
return -ENOMEM;
tmp = uri;
while (*tmp != '\0') {
if (*tmp == '/')
*tmp = '\\';
tmp++;
}
len = strlen(uri);
ibl = len;
obl = len * 2;
urip = uri;
uri_utf16 = uri_utf16p = malloc(obl);
if (!uri_utf16) {
err = -ENOMEM;
goto out;
}
/*
* MICROSOFT_COMPAT
* little endian unicode here
*/
cd = iconv_open("UTF-16LE", "ASCII");
if (cd == (iconv_t)-1) {
err = -errno;
goto out;
}
if (iconv(cd, &urip, &ibl, &uri_utf16p, &obl) == (size_t)-1 ||
ibl || obl) {
err = (errno ? -errno : -EIO);
goto out;
}
len = len * 2;
ret = malloc(len);
if (!ret) {
err = -ENOMEM;
goto out;
}
memcpy(ret, uri_utf16, len);
*outlen = len;
*out = ret;
err = 0;
out:
free(uri);
free(uri_utf16);
if (cd != (iconv_t)-1)
iconv_close(cd);
return err;
}
static char *
vhd_macx_decode_location(char *in, char *out, int len)
{
iconv_t cd;
char *name;
size_t ibl, obl;
name = out;
ibl = obl = len;
cd = iconv_open("ASCII", "UTF-8");
if (cd == (iconv_t)-1)
return NULL;
if (iconv(cd, &in, &ibl, &out, &obl) == (size_t)-1 || ibl)
return NULL;
iconv_close(cd);
*out = '\0';
if (strstr(name, "file://") != name)
return NULL;
name += strlen("file://");
return strdup(name);
}
static char *
vhd_w2u_decode_location(char *in, char *out, int len, char *utf_type)
{
iconv_t cd;
char *name, *tmp;
size_t ibl, obl;
tmp = name = out;
ibl = obl = len;
cd = iconv_open("ASCII", utf_type);
if (cd == (iconv_t)-1)
return NULL;
if (iconv(cd, &in, &ibl, &out, &obl) == (size_t)-1 || ibl)
return NULL;
iconv_close(cd);
*out = '\0';
/* TODO: spaces */
while (tmp != out) {
if (*tmp == '\\')
*tmp = '/';
tmp++;
}
if (strstr(name, "C:") == name || strstr(name, "c:") == name)
name += strlen("c:");
return strdup(name);
}
int
vhd_header_decode_parent(vhd_context_t *ctx, vhd_header_t *header, char **buf)
{
char *code, out[512];
if (vhd_creator_tapdisk(ctx) &&
ctx->footer.crtr_ver == VHD_VERSION(0, 1))
code = UTF_16;
else
code = UTF_16BE;
*buf = vhd_w2u_decode_location(header->prt_name, out, 512, code);
return (*buf == NULL ? -EINVAL : 0);
}
int
vhd_parent_locator_read(vhd_context_t *ctx,
vhd_parent_locator_t *loc, char **parent)
{
int err, size;
char *raw, *out, *name;
raw = NULL;
out = NULL;
name = NULL;
*parent = NULL;
if (ctx->footer.type != HD_TYPE_DIFF) {
err = -EINVAL;
goto out;
}
switch (loc->code) {
case PLAT_CODE_MACX:
case PLAT_CODE_W2KU:
case PLAT_CODE_W2RU:
break;
default:
err = -EINVAL;
goto out;
}
err = vhd_seek(ctx, loc->data_offset, SEEK_SET);
if (err)
goto out;
size = vhd_parent_locator_size(loc);
if (size <= 0) {
err = -EINVAL;
goto out;
}
err = posix_memalign((void **)&raw, VHD_SECTOR_SIZE, size);
if (err) {
raw = NULL;
err = -err;
goto out;
}
err = vhd_read(ctx, raw, size);
if (err)
goto out;
out = malloc(loc->data_len + 1);
if (!out) {
err = -ENOMEM;
goto out;
}
switch (loc->code) {
case PLAT_CODE_MACX:
name = vhd_macx_decode_location(raw, out, loc->data_len);
break;
case PLAT_CODE_W2KU:
case PLAT_CODE_W2RU:
name = vhd_w2u_decode_location(raw, out,
loc->data_len, UTF_16LE);
break;
}
if (!name) {
err = -EINVAL;
goto out;
}
err = 0;
*parent = name;
out:
free(raw);
free(out);
if (err) {
VHDLOG("%s: error reading parent locator: %d\n",
ctx->file, err);
VHDLOG("%s: locator: code %u, space 0x%x, len 0x%x, "
"off 0x%llx\n", ctx->file, loc->code, loc->data_space,
loc->data_len, loc->data_offset);
}
return err;
}
int
vhd_parent_locator_get(vhd_context_t *ctx, char **parent)
{
int i, n, err;
char *name, *location;
vhd_parent_locator_t *loc;
err = 0;
*parent = NULL;
if (ctx->footer.type != HD_TYPE_DIFF)
return -EINVAL;
n = vhd_parent_locator_count(ctx);
for (i = 0; i < n; i++) {
loc = ctx->header.loc + i;
err = vhd_parent_locator_read(ctx, loc, &name);
if (err)
continue;
err = vhd_find_parent(ctx, name, &location);
if (err)
VHDLOG("%s: couldn't find parent %s (%d)\n",
ctx->file, name, err);
free(name);
if (!err) {
*parent = location;
return 0;
}
}
return err;
}
int
vhd_parent_locator_write_at(vhd_context_t *ctx,
const char *parent, off64_t off, uint32_t code,
size_t max_bytes, vhd_parent_locator_t *loc)
{
struct stat stats;
int err, len, size;
char *absolute_path, *relative_path, *encoded, *block;
memset(loc, 0, sizeof(vhd_parent_locator_t));
if (ctx->footer.type != HD_TYPE_DIFF)
return -EINVAL;
absolute_path = NULL;
relative_path = NULL;
encoded = NULL;
block = NULL;
size = 0;
len = 0;
switch (code) {
case PLAT_CODE_MACX:
case PLAT_CODE_W2KU:
case PLAT_CODE_W2RU:
break;
default:
return -EINVAL;
}
absolute_path = realpath(parent, NULL);
if (!absolute_path) {
err = -errno;
goto out;
}
err = stat(absolute_path, &stats);
if (err) {
err = -errno;
goto out;
}
if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
err = -EINVAL;
goto out;
}
relative_path = relative_path_to(ctx->file, absolute_path, &err);
if (!relative_path || err) {
err = (err ? err : -EINVAL);
goto out;
}
switch (code) {
case PLAT_CODE_MACX:
err = vhd_macx_encode_location(relative_path, &encoded, &len);
break;
case PLAT_CODE_W2KU:
case PLAT_CODE_W2RU:
err = vhd_w2u_encode_location(relative_path, &encoded, &len);
break;
default:
err = -EINVAL;
}
if (err)
goto out;
err = vhd_seek(ctx, off, SEEK_SET);
if (err)
goto out;
size = vhd_bytes_padded(len);
if (max_bytes && size > max_bytes) {
err = -ENAMETOOLONG;
goto out;
}
err = posix_memalign((void **)&block, VHD_SECTOR_SIZE, size);
if (err) {
block = NULL;
err = -err;
goto out;
}
memset(block, 0, size);
memcpy(block, encoded, len);
err = vhd_write(ctx, block, size);
if (err)
goto out;
err = 0;
out:
free(absolute_path);
free(relative_path);
free(encoded);
free(block);
if (!err) {
loc->res = 0;
loc->code = code;
loc->data_len = len;
/*
* write number of bytes ('size') instead of number of sectors
* into loc->data_space to be compatible with MSFT, even though
* this goes against the specs
*/
loc->data_space = size;
loc->data_offset = off;
}
return err;
}
static int
vhd_footer_offset_at_eof(vhd_context_t *ctx, off64_t *off)
{
int err;
if ((err = vhd_seek(ctx, 0, SEEK_END)))
return errno;
*off = vhd_position(ctx) - sizeof(vhd_footer_t);
return 0;
}
int
vhd_read_bitmap(vhd_context_t *ctx, uint32_t block, char **bufp)
{
int err;
char *buf;
size_t size;
off64_t off;
uint64_t blk;
buf = NULL;
*bufp = NULL;
if (!vhd_type_dynamic(ctx))
return -EINVAL;
err = vhd_get_bat(ctx);
if (err)
return err;
if (block >= ctx->bat.entries)
return -ERANGE;
blk = ctx->bat.bat[block];
if (blk == DD_BLK_UNUSED)
return -EINVAL;
off = vhd_sectors_to_bytes(blk);
size = vhd_bytes_padded(ctx->spb >> 3);
err = vhd_seek(ctx, off, SEEK_SET);
if (err)
return err;
err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
if (err)
return -err;
err = vhd_read(ctx, buf, size);
if (err)
goto fail;
*bufp = buf;
return 0;
fail:
free(buf);
return err;
}
int
vhd_read_block(vhd_context_t *ctx, uint32_t block, char **bufp)
{
int err;
char *buf;
size_t size;
uint64_t blk;
off64_t end, off;
buf = NULL;
*bufp = NULL;
if (!vhd_type_dynamic(ctx))
return -EINVAL;
err = vhd_get_bat(ctx);
if (err)
return err;
if (block >= ctx->bat.entries)
return -ERANGE;
blk = ctx->bat.bat[block];
if (blk == DD_BLK_UNUSED)
return -EINVAL;
off = vhd_sectors_to_bytes(blk + ctx->bm_secs);
size = vhd_sectors_to_bytes(ctx->spb);
err = vhd_footer_offset_at_eof(ctx, &end);
if (err)
return err;
err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
if (err) {
err = -err;
goto fail;
}
if (end < off + ctx->header.block_size) {
size = end - off;
memset(buf + size, 0, ctx->header.block_size - size);
}
err = vhd_seek(ctx, off, SEEK_SET);
if (err)
goto fail;
err = vhd_read(ctx, buf, size);
if (err)
goto fail;
*bufp = buf;
return 0;
fail:
free(buf);
return err;
}
int
vhd_write_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off64_t off)
{
int err;
vhd_footer_t *f;
f = NULL;
err = posix_memalign((void **)&f,
VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
if (err) {
f = NULL;
err = -err;
goto out;
}
memcpy(f, footer, sizeof(vhd_footer_t));
f->checksum = vhd_checksum_footer(f);
err = vhd_validate_footer(f);
if (err)
goto out;
err = vhd_seek(ctx, off, SEEK_SET);
if (err)
goto out;
vhd_footer_out(f);
err = vhd_write(ctx, f, sizeof(vhd_footer_t));
out:
if (err)
VHDLOG("%s: failed writing footer at 0x%08"PRIx64": %d\n",
ctx->file, off, err);
free(f);
return err;
}
int
vhd_write_footer(vhd_context_t *ctx, vhd_footer_t *footer)
{
int err;
off64_t off;
if (ctx->is_block)
err = vhd_footer_offset_at_eof(ctx, &off);
else
err = vhd_end_of_data(ctx, &off);
if (err)
return err;
err = vhd_write_footer_at(ctx, footer, off);
if (err)
return err;
if (!vhd_type_dynamic(ctx))
return 0;
return vhd_write_footer_at(ctx, footer, 0);
}
int
vhd_write_header_at(vhd_context_t *ctx, vhd_header_t *header, off64_t off)
{
int err;
vhd_header_t *h;
h = NULL;
if (!vhd_type_dynamic(ctx)) {
err = -EINVAL;
goto out;
}
err = posix_memalign((void **)&h,
VHD_SECTOR_SIZE, sizeof(vhd_header_t));
if (err) {
h = NULL;
err = -err;
goto out;
}
memcpy(h, header, sizeof(vhd_header_t));
h->checksum = vhd_checksum_header(h);
err = vhd_validate_header(h);
if (err)
goto out;
vhd_header_out(h);
err = vhd_seek(ctx, off, SEEK_SET);
if (err)
goto out;
err = vhd_write(ctx, h, sizeof(vhd_header_t));
out:
if (err)
VHDLOG("%s: failed writing header at 0x%08"PRIx64": %d\n",
ctx->file, off, err);
free(h);
return err;
}
int
vhd_write_header(vhd_context_t *ctx, vhd_header_t *header)
{
int err;
off64_t off;
if (!vhd_type_dynamic(ctx))
return -EINVAL;
off = ctx->footer.data_offset;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -