?? libvhd.c
字號:
}
}
TEST_FAIL_AT(FAIL_REPARENT_LOCATOR);
err = vhd_write_header(child, &child->header);
if (err) {
VHDLOG("error writing header for %s: %d\n", child->file, err);
goto out;
}
err = 0;
out:
free(ppath);
return err;
}
static int
vhd_create_batmap(vhd_context_t *ctx)
{
off64_t off;
int err, map_bytes;
vhd_batmap_header_t *header;
if (!vhd_type_dynamic(ctx))
return -EINVAL;
map_bytes = (ctx->header.max_bat_size + 7) >> 3;
header = &ctx->batmap.header;
memset(header, 0, sizeof(vhd_batmap_header_t));
memcpy(header->cookie, VHD_BATMAP_COOKIE, sizeof(header->cookie));
err = vhd_batmap_header_offset(ctx, &off);
if (err)
return err;
header->batmap_offset = off +
vhd_bytes_padded(sizeof(vhd_batmap_header_t));
header->batmap_size = secs_round_up_no_zero(map_bytes);
header->batmap_version = VHD_BATMAP_CURRENT_VERSION;
map_bytes = vhd_sectors_to_bytes(header->batmap_size);
err = posix_memalign((void **)&ctx->batmap.map,
VHD_SECTOR_SIZE, map_bytes);
if (err) {
ctx->batmap.map = NULL;
return -err;
}
memset(ctx->batmap.map, 0, map_bytes);
return vhd_write_batmap(ctx, &ctx->batmap);
}
static int
vhd_create_bat(vhd_context_t *ctx)
{
int i, err;
size_t size;
if (!vhd_type_dynamic(ctx))
return -EINVAL;
size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
err = posix_memalign((void **)&ctx->bat.bat, VHD_SECTOR_SIZE, size);
if (err) {
ctx->bat.bat = NULL;
return err;
}
memset(ctx->bat.bat, 0, size);
for (i = 0; i < ctx->header.max_bat_size; i++)
ctx->bat.bat[i] = DD_BLK_UNUSED;
err = vhd_seek(ctx, ctx->header.table_offset, SEEK_SET);
if (err)
return err;
ctx->bat.entries = ctx->header.max_bat_size;
ctx->bat.spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
return vhd_write_bat(ctx, &ctx->bat);
}
static int
vhd_initialize_fixed_disk(vhd_context_t *ctx)
{
char *buf;
int i, err;
if (ctx->footer.type != HD_TYPE_FIXED)
return -EINVAL;
err = vhd_seek(ctx, 0, SEEK_SET);
if (err)
return err;
buf = mmap(0, VHD_BLOCK_SIZE, PROT_READ,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (buf == MAP_FAILED)
return -errno;
for (i = 0; i < ctx->footer.curr_size >> VHD_BLOCK_SHIFT; i++) {
err = vhd_write(ctx, buf, VHD_BLOCK_SIZE);
if (err)
goto out;
}
err = 0;
out:
munmap(buf, VHD_BLOCK_SIZE);
return err;
}
int
vhd_get_phys_size(vhd_context_t *ctx, off64_t *size)
{
int err;
if ((err = vhd_end_of_data(ctx, size)))
return err;
*size += sizeof(vhd_footer_t);
return 0;
}
int
vhd_set_phys_size(vhd_context_t *ctx, off64_t size)
{
off64_t phys_size;
int err;
err = vhd_get_phys_size(ctx, &phys_size);
if (err)
return err;
if (size < phys_size) {
// would result in data loss
VHDLOG("ERROR: new size (%"PRIu64") < phys size (%"PRIu64")\n",
size, phys_size);
return -EINVAL;
}
return vhd_write_footer_at(ctx, &ctx->footer,
size - sizeof(vhd_footer_t));
}
static int
__vhd_create(const char *name, const char *parent, uint64_t bytes, int type,
vhd_flag_creat_t flags)
{
int err;
off64_t off;
vhd_context_t ctx;
vhd_footer_t *footer;
vhd_header_t *header;
uint64_t size, blks;
switch (type) {
case HD_TYPE_DIFF:
if (!parent)
return -EINVAL;
case HD_TYPE_FIXED:
case HD_TYPE_DYNAMIC:
break;
default:
return -EINVAL;
}
if (strnlen(name, VHD_MAX_NAME_LEN - 1) == VHD_MAX_NAME_LEN - 1)
return -ENAMETOOLONG;
memset(&ctx, 0, sizeof(vhd_context_t));
footer = &ctx.footer;
header = &ctx.header;
blks = (bytes + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
size = blks << VHD_BLOCK_SHIFT;
ctx.fd = open(name, O_WRONLY | O_CREAT |
O_TRUNC | O_LARGEFILE | O_DIRECT, 0644);
if (ctx.fd == -1)
return -errno;
ctx.file = strdup(name);
if (!ctx.file) {
err = -ENOMEM;
goto out;
}
err = vhd_test_file_fixed(ctx.file, &ctx.is_block);
if (err)
goto out;
vhd_initialize_footer(&ctx, type, size);
if (type == HD_TYPE_FIXED) {
err = vhd_initialize_fixed_disk(&ctx);
if (err)
goto out;
} else {
int raw = vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW);
err = vhd_initialize_header(&ctx, parent, size, raw);
if (err)
goto out;
err = vhd_write_footer_at(&ctx, &ctx.footer, 0);
if (err)
goto out;
err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
if (err)
goto out;
err = vhd_create_batmap(&ctx);
if (err)
goto out;
err = vhd_create_bat(&ctx);
if (err)
goto out;
if (type == HD_TYPE_DIFF) {
err = vhd_write_parent_locators(&ctx, parent);
if (err)
goto out;
}
/* write header again since it may have changed */
err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
if (err)
goto out;
}
err = vhd_seek(&ctx, 0, SEEK_END);
if (err)
goto out;
off = vhd_position(&ctx);
if (off == (off64_t)-1) {
err = -errno;
goto out;
}
if (ctx.is_block)
off -= sizeof(vhd_footer_t);
err = vhd_write_footer_at(&ctx, &ctx.footer, off);
if (err)
goto out;
err = 0;
out:
vhd_close(&ctx);
if (err && !ctx.is_block)
unlink(name);
return err;
}
int
vhd_create(const char *name, uint64_t bytes, int type, vhd_flag_creat_t flags)
{
return __vhd_create(name, NULL, bytes, type, flags);
}
int
vhd_snapshot(const char *name, uint64_t bytes, const char *parent,
vhd_flag_creat_t flags)
{
return __vhd_create(name, parent, bytes, HD_TYPE_DIFF, flags);
}
static int
__vhd_io_fixed_read(vhd_context_t *ctx,
char *buf, uint64_t sec, uint32_t secs)
{
int err;
err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
if (err)
return err;
return vhd_read(ctx, buf, vhd_sectors_to_bytes(secs));
}
static void
__vhd_io_dynamic_copy_data(vhd_context_t *ctx,
char *map, int map_off,
char *bitmap, int bitmap_off,
char *dst, char *src, int secs)
{
int i;
for (i = 0; i < secs; i++) {
if (test_bit(map, map_off + i))
goto next;
if (ctx && !vhd_bitmap_test(ctx, bitmap, bitmap_off + i))
goto next;
memcpy(dst, src, VHD_SECTOR_SIZE);
set_bit(map, map_off + i);
next:
src += VHD_SECTOR_SIZE;
dst += VHD_SECTOR_SIZE;
}
}
static int
__vhd_io_dynamic_read_link(vhd_context_t *ctx, char *map,
char *buf, uint64_t sector, uint32_t secs)
{
off64_t off;
uint32_t blk, sec;
int err, cnt, map_off;
char *bitmap, *data, *src;
map_off = 0;
do {
blk = sector / ctx->spb;
sec = sector % ctx->spb;
off = ctx->bat.bat[blk];
data = NULL;
bitmap = NULL;
if (off == DD_BLK_UNUSED) {
cnt = MIN(secs, ctx->spb);
goto next;
}
err = vhd_read_bitmap(ctx, blk, &bitmap);
if (err)
return err;
err = vhd_read_block(ctx, blk, &data);
if (err) {
free(bitmap);
return err;
}
cnt = MIN(secs, ctx->spb - sec);
src = data + vhd_sectors_to_bytes(sec);
__vhd_io_dynamic_copy_data(ctx,
map, map_off,
bitmap, sec,
buf, src, cnt);
next:
free(data);
free(bitmap);
secs -= cnt;
sector += cnt;
map_off += cnt;
buf += vhd_sectors_to_bytes(cnt);
} while (secs);
return 0;
}
static int
__raw_read_link(char *filename,
char *map, char *buf, uint64_t sec, uint32_t secs)
{
int fd, err;
off64_t off;
uint64_t size;
char *data;
err = 0;
errno = 0;
fd = open(filename, O_RDONLY | O_DIRECT | O_LARGEFILE);
if (fd == -1) {
VHDLOG("%s: failed to open: %d\n", filename, -errno);
return -errno;
}
off = lseek64(fd, vhd_sectors_to_bytes(sec), SEEK_SET);
if (off == (off64_t)-1) {
VHDLOG("%s: seek(0x%08"PRIx64") failed: %d\n",
filename, vhd_sectors_to_bytes(sec), -errno);
err = -errno;
goto close;
}
size = vhd_sectors_to_bytes(secs);
err = posix_memalign((void **)&data, VHD_SECTOR_SIZE, size);
if (err)
goto close;
err = read(fd, data, size);
if (err != size) {
VHDLOG("%s: reading of %"PRIu64" returned %d, errno: %d\n",
filename, size, err, -errno);
free(data);
err = errno ? -errno : -EIO;
goto close;
}
__vhd_io_dynamic_copy_data(NULL, map, 0, NULL, 0, buf, data, secs);
free(data);
err = 0;
close:
close(fd);
return err;
}
static int
__vhd_io_dynamic_read(vhd_context_t *ctx,
char *buf, uint64_t sec, uint32_t secs)
{
int err;
uint32_t i, done;
char *map, *next;
vhd_context_t parent, *vhd;
err = vhd_get_bat(ctx);
if (err)
return err;
vhd = ctx;
next = NULL;
map = calloc(1, secs << (VHD_SECTOR_SHIFT - 3));
if (!map)
return -ENOMEM;
memset(buf, 0, vhd_sectors_to_bytes(secs));
for (;;) {
err = __vhd_io_dynamic_read_link(vhd, map, buf, sec, secs);
if (err)
goto close;
for (done = 0, i = 0; i < secs; i++)
if (test_bit(map, i))
done++;
if (done == secs) {
err = 0;
goto close;
}
if (vhd->footer.type == HD_TYPE_DIFF) {
err = vhd_parent_locator_get(vhd, &next);
if (err)
goto close;
if (vhd_parent_raw(vhd)) {
err = __raw_read_link(next, map, buf, sec,
secs);
goto close;
}
} else {
err = 0;
goto close;
}
if (vhd != ctx)
vhd_close(vhd);
vhd = &parent;
err = vhd_open(vhd, next, VHD_OPEN_RDONLY);
if (err)
goto out;
err = vhd_get_bat(vhd);
if (err)
goto close;
free(next);
next = NULL;
}
close:
if (vhd != ctx)
vhd_close(vhd);
out:
free(map);
free(next);
return err;
}
int
vhd_io_read(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
{
if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
return -ERANGE;
if (!vhd_type_dynamic(ctx))
return __vhd_io_fixed_read(ctx, buf, sec, secs);
return __vhd_io_dynamic_read(ctx, buf, sec, secs);
}
static int
__vhd_io_fixed_write(vhd_context_t *ctx,
char *buf, uint64_t sec, uint32_t secs)
{
int err;
err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
if (err)
return err;
return vhd_write(ctx, buf, vhd_sectors_to_bytes(secs));
}
static int
__vhd_io_allocate_block(vhd_context_t *ctx, uint32_t block)
{
char *buf;
size_t size;
off64_t off, max;
int i, err, gap, spp;
spp = getpagesize() >> VHD_SECTOR_SHIFT;
err = vhd_end_of_data(ctx, &max);
if (err)
return err;
gap = 0;
off = max;
max >>= VHD_SECTOR_SHIFT;
/* data region of segment should begin on page boundary */
if ((max + ctx->bm_secs) % spp) {
gap = (spp - ((max + ctx->bm_secs) % spp));
max += gap;
}
err = vhd_seek(ctx, off, SEEK_SET);
if (err)
return err;
size = vhd_sectors_to_bytes(ctx->spb + ctx->bm_secs + gap);
buf = mmap(0, size, PROT_READ, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (buf == MAP_FAILED)
return -errno;
err = vhd_write(ctx, buf, size);
if (err)
goto out;
ctx->bat.bat[block] = max;
err = vhd_write_bat(ctx, &ctx->bat);
if (err)
goto out;
err = 0;
out:
munmap(buf, size);
return err;
}
static int
__vhd_io_dynamic_write(vhd_context_t *ctx,
char *buf, uint64_t sector, uint32_t secs)
{
char *map;
off64_t off;
uint32_t blk, sec;
int i, err, cnt, ret;
if (vhd_sectors_to_bytes(sector + secs) > ctx->footer.curr_size)
return -ERANGE;
err = vhd_get_bat(ctx);
if (err)
return err;
if (vhd_has_batmap(ctx)) {
err = vhd_get_batmap(ctx);
if (err)
return err;
}
do {
blk = sector / ctx->spb;
sec = sector % ctx->spb;
off = ctx->bat.bat[blk];
if (off == DD_BLK_UNUSED) {
err = __vhd_io_allocate_block(ctx, blk);
if (err)
return err;
off = ctx->bat.bat[blk];
}
off += ctx->bm_secs + sec;
err = vhd_seek(ctx, vhd_sectors_to_bytes(off), SEEK_SET);
if (err)
return err;
cnt = MIN(secs, ctx->spb - sec);
err = vhd_write(ctx, buf, vhd_sectors_to_bytes(cnt));
if (err)
return err;
if (vhd_has_batmap(ctx) &&
vhd_batmap_test(ctx, &ctx->batmap, blk))
goto next;
err = vhd_read_bitmap(ctx, blk, &map);
if (err)
return err;
for (i = 0; i < cnt; i++)
vhd_bitmap_set(ctx, map, sec + i);
err = vhd_write_bitmap(ctx, blk, map);
if (err)
goto fail;
if (vhd_has_batmap(ctx)) {
for (i = 0; i < ctx->spb; i++)
if (!vhd_bitmap_test(ctx, map, i)) {
free(map);
goto next;
}
vhd_batmap_set(ctx, &ctx->batmap, blk);
err = vhd_write_batmap(ctx, &ctx->batmap);
if (err)
goto fail;
}
free(map);
map = NULL;
next:
secs -= cnt;
sector += cnt;
buf += vhd_sectors_to_bytes(cnt);
} while (secs);
err = 0;
out:
ret = vhd_write_footer(ctx, &ctx->footer);
return (err ? err : ret);
fail:
free(map);
goto out;
}
int
vhd_io_write(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
{
if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
return -ERANGE;
if (!vhd_type_dynamic(ctx))
return __vhd_io_fixed_write(ctx, buf, sec, secs);
return __vhd_io_dynamic_write(ctx, buf, sec, secs);
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -