📄 block-vhd.c
字号:
allocated = 0;
full = 0;
for (i = 0; i < s->bat.bat.entries; i++) {
if (bat_entry(s, i) != DD_BLK_UNUSED)
allocated++;
if (test_batmap(s, i))
full++;
}
DPRINTF("%s version: %s 0x%08x, b: %u, a: %u, f: %u, n: %llu\n",
s->vhd.file, buf, s->vhd.footer.crtr_ver, s->bat.bat.entries,
allocated, full, s->next_db);
}
static int
__vhd_open(td_driver_t *driver, const char *name, vhd_flag_t flags)
{
int i, o_flags, err;
struct vhd_state *s;
DBG(TLOG_INFO, "vhd_open: %s\n", name);
if (test_vhd_flag(flags, VHD_FLAG_OPEN_STRICT))
libvhd_set_log_level(1);
s = (struct vhd_state *)driver->data;
memset(s, 0, sizeof(struct vhd_state));
s->flags = flags;
s->driver = driver;
err = vhd_initialize(s);
if (err)
return err;
o_flags = ((test_vhd_flag(flags, VHD_FLAG_OPEN_RDONLY)) ?
VHD_OPEN_RDONLY : VHD_OPEN_RDWR);
err = vhd_open(&s->vhd, name, o_flags);
if (err) {
libvhd_set_log_level(1);
err = vhd_open(&s->vhd, name, o_flags);
if (err) {
EPRINTF("Unable to open [%s] (%d)!\n", name, err);
return err;
}
}
err = vhd_check_version(s);
if (err)
goto fail;
s->spb = s->spp = 1;
if (vhd_type_dynamic(&s->vhd)) {
err = vhd_initialize_dynamic_disk(s);
if (err)
goto fail;
}
vhd_log_open(s);
SPB = s->spb;
s->vreq_free_count = VHD_REQS_DATA;
for (i = 0; i < VHD_REQS_DATA; i++)
s->vreq_free[i] = s->vreq_list + i;
driver->info.size = s->vhd.footer.curr_size >> VHD_SECTOR_SHIFT;
driver->info.sector_size = VHD_SECTOR_SIZE;
driver->info.info = 0;
DBG(TLOG_INFO, "vhd_open: done (sz:%llu, sct:%lu, inf:%u)\n",
driver->info.size, driver->info.sector_size, driver->info.info);
if (test_vhd_flag(flags, VHD_FLAG_OPEN_STRICT) &&
!test_vhd_flag(flags, VHD_FLAG_OPEN_RDONLY)) {
err = vhd_kill_footer(s);
if (err) {
DPRINTF("ERROR killing footer: %d\n", err);
goto fail;
}
s->writes++;
}
return 0;
fail:
vhd_free_bat(s);
vhd_free_bitmap_cache(s);
vhd_close(&s->vhd);
vhd_free(s);
return err;
}
static int
_vhd_open(td_driver_t *driver, const char *name, td_flag_t flags)
{
vhd_flag_t vhd_flags = 0;
if (flags & TD_OPEN_RDONLY)
vhd_flags |= VHD_FLAG_OPEN_RDONLY;
if (flags & TD_OPEN_QUIET)
vhd_flags |= VHD_FLAG_OPEN_QUIET;
if (flags & TD_OPEN_STRICT)
vhd_flags |= VHD_FLAG_OPEN_STRICT;
if (flags & TD_OPEN_QUERY)
vhd_flags |= (VHD_FLAG_OPEN_QUERY |
VHD_FLAG_OPEN_QUIET |
VHD_FLAG_OPEN_RDONLY |
VHD_FLAG_OPEN_NO_CACHE);
/* pre-allocate for all but NFS and LVM storage */
if (driver->storage != TAPDISK_STORAGE_TYPE_NFS &&
driver->storage != TAPDISK_STORAGE_TYPE_LVM)
vhd_flags |= VHD_FLAG_OPEN_PREALLOCATE;
return __vhd_open(driver, name, vhd_flags);
}
static void
vhd_log_close(struct vhd_state *s)
{
uint32_t i, allocated, full;
if (test_vhd_flag(s->flags, VHD_FLAG_OPEN_QUIET))
return;
allocated = 0;
full = 0;
for (i = 0; i < s->bat.bat.entries; i++) {
if (bat_entry(s, i) != DD_BLK_UNUSED)
allocated++;
if (test_batmap(s, i))
full++;
}
DPRINTF("%s: b: %u, a: %u, f: %u, n: %llu\n",
s->vhd.file, s->bat.bat.entries, allocated, full, s->next_db);
}
static int
_vhd_close(td_driver_t *driver)
{
int err;
struct vhd_state *s;
struct vhd_bitmap *bm;
DBG(TLOG_WARN, "vhd_close\n");
s = (struct vhd_state *)driver->data;
/* don't write footer if tapdisk is read-only */
if (test_vhd_flag(s->flags, VHD_FLAG_OPEN_RDONLY))
goto free;
/*
* write footer if:
* - we killed it on open (opened with strict)
* - we've written data since opening
*/
if (test_vhd_flag(s->flags, VHD_FLAG_OPEN_STRICT) || s->writes) {
memcpy(&s->vhd.bat, &s->bat.bat, sizeof(vhd_bat_t));
err = vhd_write_footer(&s->vhd, &s->vhd.footer);
memset(&s->vhd.bat, 0, sizeof(vhd_bat_t));
if (err)
EPRINTF("writing %s footer: %d\n", s->vhd.file, err);
if (!vhd_has_batmap(&s->vhd))
goto free;
err = vhd_write_batmap(&s->vhd, &s->bat.batmap);
if (err)
EPRINTF("writing %s batmap: %d\n", s->vhd.file, err);
}
free:
vhd_log_close(s);
vhd_free_bat(s);
vhd_free_bitmap_cache(s);
vhd_close(&s->vhd);
vhd_free(s);
memset(s, 0, sizeof(struct vhd_state));
return 0;
}
int
vhd_validate_parent(td_driver_t *child_driver,
td_driver_t *parent_driver, td_flag_t flags)
{
struct stat stats;
struct vhd_state *child = (struct vhd_state *)child_driver->data;
struct vhd_state *parent;
if (parent_driver->type != DISK_TYPE_VHD) {
if (child_driver->type != DISK_TYPE_VHD)
return -EINVAL;
if (child->vhd.footer.type != HD_TYPE_DIFF)
return -EINVAL;
if (!vhd_parent_raw(&child->vhd))
return -EINVAL;
return 0;
}
parent = (struct vhd_state *)parent_driver->data;
/*
* This check removed because of cases like:
* - parent VHD marked as 'hidden'
* - parent VHD modified during coalesce
*/
/*
if (stat(parent->vhd.file, &stats)) {
DPRINTF("ERROR stating parent file %s\n", parent->vhd.file);
return -errno;
}
if (child->hdr.prt_ts != vhd_time(stats.st_mtime)) {
DPRINTF("ERROR: parent file has been modified since "
"snapshot. Child image no longer valid.\n");
return -EINVAL;
}
*/
if (uuid_compare(child->vhd.header.prt_uuid, parent->vhd.footer.uuid)) {
DPRINTF("ERROR: %s: %s, %s: parent uuid has changed since "
"snapshot. Child image no longer valid.\n",
__func__, child->vhd.file, parent->vhd.file);
return -EINVAL;
}
/* TODO: compare sizes */
return 0;
}
int
vhd_get_parent_id(td_driver_t *driver, td_disk_id_t *id)
{
int err;
char *parent;
struct vhd_state *s;
DBG(TLOG_DBG, "\n");
memset(id, 0, sizeof(td_disk_id_t));
s = (struct vhd_state *)driver->data;
if (s->vhd.footer.type != HD_TYPE_DIFF)
return TD_NO_PARENT;
err = vhd_parent_locator_get(&s->vhd, &parent);
if (err)
return err;
id->name = parent;
id->drivertype = DISK_TYPE_VHD;
if (vhd_parent_raw(&s->vhd)) {
DPRINTF("VHD: parent is raw\n");
id->drivertype = DISK_TYPE_AIO;
}
return 0;
}
static inline void
clear_req_list(struct vhd_req_list *list)
{
list->head = list->tail = NULL;
}
static inline void
add_to_tail(struct vhd_req_list *list, struct vhd_request *e)
{
if (!list->head)
list->head = list->tail = e;
else
list->tail = list->tail->next = e;
}
static inline int
remove_from_req_list(struct vhd_req_list *list, struct vhd_request *e)
{
struct vhd_request *i = list->head;
if (list->head == e) {
if (list->tail == e)
clear_req_list(list);
else
list->head = list->head->next;
return 0;
}
while (i->next) {
if (i->next == e) {
if (list->tail == e) {
i->next = NULL;
list->tail = i;
} else
i->next = i->next->next;
return 0;
}
i = i->next;
}
return -EINVAL;
}
static inline void
init_vhd_request(struct vhd_state *s, struct vhd_request *req)
{
memset(req, 0, sizeof(struct vhd_request));
req->state = s;
}
static inline void
init_tx(struct vhd_transaction *tx)
{
memset(tx, 0, sizeof(struct vhd_transaction));
}
static inline void
add_to_transaction(struct vhd_transaction *tx, struct vhd_request *r)
{
ASSERT(!tx->closed);
r->tx = tx;
tx->started++;
add_to_tail(&tx->requests, r);
set_vhd_flag(tx->status, VHD_FLAG_TX_LIVE);
DBG(TLOG_DBG, "blk: 0x%04"PRIx64", lsec: 0x%08"PRIx64", tx: %p, "
"started: %d, finished: %d, status: %u\n",
r->treq.sec / SPB, r->treq.sec, tx,
tx->started, tx->finished, tx->status);
}
static inline int
transaction_completed(struct vhd_transaction *tx)
{
return (tx->started == tx->finished);
}
static inline void
init_bat(struct vhd_state *s)
{
s->bat.req.tx = NULL;
s->bat.req.next = NULL;
s->bat.req.error = 0;
s->bat.pbw_blk = 0;
s->bat.pbw_offset = 0;
s->bat.status = 0;
}
static inline void
lock_bat(struct vhd_state *s)
{
set_vhd_flag(s->bat.status, VHD_FLAG_BAT_LOCKED);
}
static inline void
unlock_bat(struct vhd_state *s)
{
clear_vhd_flag(s->bat.status, VHD_FLAG_BAT_LOCKED);
}
static inline int
bat_locked(struct vhd_state *s)
{
return test_vhd_flag(s->bat.status, VHD_FLAG_BAT_LOCKED);
}
static inline void
init_vhd_bitmap(struct vhd_state *s, struct vhd_bitmap *bm)
{
bm->blk = 0;
bm->seqno = 0;
bm->status = 0;
init_tx(&bm->tx);
clear_req_list(&bm->queue);
clear_req_list(&bm->waiting);
memset(bm->map, 0, vhd_sectors_to_bytes(s->bm_secs));
memset(bm->shadow, 0, vhd_sectors_to_bytes(s->bm_secs));
init_vhd_request(s, &bm->req);
}
static inline struct vhd_bitmap *
get_bitmap(struct vhd_state *s, uint32_t block)
{
int i;
struct vhd_bitmap *bm;
for (i = 0; i < VHD_CACHE_SIZE; i++) {
bm = s->bitmap[i];
if (bm && bm->blk == block)
return bm;
}
return NULL;
}
static inline void
lock_bitmap(struct vhd_bitmap *bm)
{
set_vhd_flag(bm->status, VHD_FLAG_BM_LOCKED);
}
static inline void
unlock_bitmap(struct vhd_bitmap *bm)
{
clear_vhd_flag(bm->status, VHD_FLAG_BM_LOCKED);
}
static inline int
bitmap_locked(struct vhd_bitmap *bm)
{
return test_vhd_flag(bm->status, VHD_FLAG_BM_LOCKED);
}
static inline int
bitmap_valid(struct vhd_bitmap *bm)
{
return !test_vhd_flag(bm->status, VHD_FLAG_BM_READ_PENDING);
}
static inline int
bitmap_in_use(struct vhd_bitmap *bm)
{
return (test_vhd_flag(bm->status, VHD_FLAG_BM_READ_PENDING) ||
test_vhd_flag(bm->status, VHD_FLAG_BM_WRITE_PENDING) ||
test_vhd_flag(bm->tx.status, VHD_FLAG_TX_UPDATE_BAT) ||
bm->waiting.head || bm->tx.requests.head || bm->queue.head);
}
static inline int
bitmap_full(struct vhd_state *s, struct vhd_bitmap *bm)
{
int i, n;
n = s->spb >> 3;
for (i = 0; i < n; i++)
if (bm->map[i] != (char)0xFF)
return 0;
DBG(TLOG_DBG, "bitmap 0x%04x full\n", bm->blk);
return 1;
}
static struct vhd_bitmap *
remove_lru_bitmap(struct vhd_state *s)
{
int i, idx = 0;
u64 seq = s->bm_lru;
struct vhd_bitmap *bm, *lru = NULL;
for (i = 0; i < VHD_CACHE_SIZE; i++) {
bm = s->bitmap[i];
if (bm && bm->seqno < seq && !bitmap_locked(bm)) {
idx = i;
lru = bm;
seq = lru->seqno;
}
}
if (lru) {
s->bitmap[idx] = NULL;
ASSERT(!bitmap_in_use(lru));
}
return lru;
}
static int
alloc_vhd_bitmap(struct vhd_state *s, struct vhd_bitmap **bitmap, uint32_t blk)
{
struct vhd_bitmap *bm;
*bitmap = NULL;
if (s->bm_free_count > 0) {
bm = s->bitmap_free[--s->bm_free_count];
} else {
bm = remove_lru_bitmap(s);
if (!bm)
return -EBUSY;
}
init_vhd_bitmap(s, bm);
bm->blk = blk;
*bitmap = bm;
return 0;
}
static inline uint64_t
__bitmap_lru_seqno(struct vhd_state *s)
{
int i;
struct vhd_bitmap *bm;
if (s->bm_lru == 0xffffffff) {
s->bm_lru = 0;
for (i = 0; i < VHD_CACHE_SIZE; i++) {
bm = s->bitmap[i];
if (bm) {
bm->seqno >>= 1;
if (bm->seqno > s->bm_lru)
s->bm_lru = bm->seqno;
}
}
}
return ++s->bm_lru;
}
static inline void
touch_bitmap(struct vhd_state *s, struct vhd_bitmap *bm)
{
bm->seqno = __bitmap_lru_seqno(s);
}
static inline void
install_bitmap(struct vhd_state *s, struct vhd_bitmap *bm)
{
int i;
for (i = 0; i < VHD_CACHE_SIZE; i++) {
if (!s->bitmap[i]) {
touch_bitmap(s, bm);
s->bitmap[i] = bm;
return;
}
}
ASSERT(0);
}
static inline void
free_vhd_bitmap(struct vhd_state *s, struct vhd_bitmap *bm)
{
int i;
for (i = 0; i < VHD_CACHE_SIZE; i++)
if (s->bitmap[i] == bm)
break;
ASSERT(!bitmap_locked(bm));
ASSERT(!bitmap_in_use(bm));
ASSERT(i < VHD_CACHE_SIZE);
s->bitmap[i] = NULL;
s->bitmap_free[s->bm_free_count++] = bm;
}
static int
read_bitmap_cache(struct vhd_state *s, uint64_t sector, uint8_t op)
{
u32 blk, sec;
struct vhd_bitmap *bm;
/* in fixed disks, every block is present */
if (s->vhd.footer.type == HD_TYPE_FIXED)
return VHD_BM_BIT_SET;
blk = sector / s->spb;
sec = sector % s->spb;
if (blk > s->vhd.header.max_bat_size) {
DPRINTF("ERROR: sec %"PRIu64" out of range, op = %d\n",
sector, op);
return -EINVAL;
}
if (bat_entry(s, blk) == DD_BLK_UNUSED) {
if (op == VHD_OP_DATA_WRITE &&
s->bat.pbw_blk != blk && bat_locked(s))
return VHD_BM_BAT_LOCKED;
return VHD_BM_BAT_CLEAR;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -