📄 block-qcow2.c
字号:
if ((cluster_offset + s->cluster_size) == offset) { /* we are lucky: contiguous data */ offset = s->free_byte_offset; update_cluster_refcount(bs, offset >> s->cluster_bits, 1); s->free_byte_offset += size; } else { s->free_byte_offset = offset; goto redo; } } return offset;}static void free_clusters(struct disk_driver *bs, int64_t offset, int64_t size){ update_refcount(bs, offset, size, -1);}static int grow_refcount_table(struct disk_driver *bs, int min_size){ BDRVQcowState *s = bs->private; int new_table_size, new_table_size2, refcount_table_clusters, i, ret; uint64_t *new_table; int64_t table_offset; uint64_t data64; uint32_t data32; int old_table_size; int64_t old_table_offset; if (min_size <= s->refcount_table_size) return 0; /* compute new table size */ refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3); for(;;) { if (refcount_table_clusters == 0) { refcount_table_clusters = 1; } else { refcount_table_clusters = (refcount_table_clusters * 3 + 1) / 2; } new_table_size = refcount_table_clusters << (s->cluster_bits - 3); if (min_size <= new_table_size) break; }#ifdef DEBUG_ALLOC2 printf("grow_refcount_table from %d to %d\n", s->refcount_table_size, new_table_size);#endif new_table_size2 = new_table_size * sizeof(uint64_t); new_table = qemu_mallocz(new_table_size2); if (!new_table) return -ENOMEM; memcpy(new_table, s->refcount_table, s->refcount_table_size * sizeof(uint64_t)); for(i = 0; i < s->refcount_table_size; i++) cpu_to_be64s(&new_table[i]); /* Note: we cannot update the refcount now to avoid recursion */ table_offset = alloc_clusters_noref(bs, new_table_size2); ret = bdrv_pwrite(s->fd, table_offset, new_table, new_table_size2); if (ret != new_table_size2) goto fail; for(i = 0; i < s->refcount_table_size; i++) be64_to_cpus(&new_table[i]); data64 = cpu_to_be64(table_offset); if (bdrv_pwrite(s->fd, offsetof(QCowHeader, refcount_table_offset), &data64, sizeof(data64)) != sizeof(data64)) goto fail; data32 = cpu_to_be32(refcount_table_clusters); if (bdrv_pwrite(s->fd, offsetof(QCowHeader, refcount_table_clusters), &data32, sizeof(data32)) != sizeof(data32)) goto fail; qemu_free(s->refcount_table); old_table_offset = s->refcount_table_offset; old_table_size = s->refcount_table_size; s->refcount_table = new_table; s->refcount_table_size = new_table_size; s->refcount_table_offset = table_offset; update_refcount(bs, table_offset, new_table_size2, 1); free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t)); return 0; fail: free_clusters(bs, table_offset, new_table_size2); qemu_free(new_table); return -EIO;}/* addend must be 1 or -1 *//* XXX: cache several refcount block clusters ? */static int update_cluster_refcount(struct disk_driver *bs, int64_t cluster_index, int addend){ BDRVQcowState *s = bs->private; int64_t offset, refcount_block_offset; int ret, refcount_table_index, block_index, refcount; uint64_t data64; refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT); if (refcount_table_index >= s->refcount_table_size) { if (addend < 0) return -EINVAL; ret = grow_refcount_table(bs, refcount_table_index + 1); if (ret < 0) return ret; } refcount_block_offset = s->refcount_table[refcount_table_index]; if (!refcount_block_offset) { if (addend < 0) return -EINVAL; /* create a new refcount block */ /* Note: we cannot update the refcount now to avoid recursion */ offset = alloc_clusters_noref(bs, s->cluster_size); memset(s->refcount_block_cache, 0, s->cluster_size); ret = bdrv_pwrite(s->fd, offset, s->refcount_block_cache, s->cluster_size); if (ret != s->cluster_size) return -EINVAL; s->refcount_table[refcount_table_index] = offset; data64 = cpu_to_be64(offset); ret = bdrv_pwrite(s->fd, s->refcount_table_offset + refcount_table_index * sizeof(uint64_t), &data64, sizeof(data64)); if (ret != sizeof(data64)) return -EINVAL; refcount_block_offset = offset; s->refcount_block_cache_offset = offset; update_refcount(bs, offset, s->cluster_size, 1); } else { if (refcount_block_offset != s->refcount_block_cache_offset) { if (load_refcount_block(bs, refcount_block_offset) < 0) return -EIO; } } /* we can update the count and save it */ block_index = cluster_index & ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); refcount = be16_to_cpu(s->refcount_block_cache[block_index]); refcount += addend; if (refcount < 0 || refcount > 0xffff) return -EINVAL; if (refcount == 0 && cluster_index < s->free_cluster_index) { s->free_cluster_index = cluster_index; } s->refcount_block_cache[block_index] = cpu_to_be16(refcount); if (bdrv_pwrite(s->fd, refcount_block_offset + (block_index << REFCOUNT_SHIFT), &s->refcount_block_cache[block_index], 2) != 2) return -EIO; return refcount;}static void update_refcount(struct disk_driver *bs, int64_t offset, int64_t length, int addend){ BDRVQcowState *s = bs->private; int64_t start, last, cluster_offset;#ifdef DEBUG_ALLOC2 printf("update_refcount: offset=%lld size=%lld addend=%d\n", offset, length, addend);#endif if (length <= 0) return; start = offset & ~(s->cluster_size - 1); last = (offset + length - 1) & ~(s->cluster_size - 1); for(cluster_offset = start; cluster_offset <= last; cluster_offset += s->cluster_size) { update_cluster_refcount(bs, cluster_offset >> s->cluster_bits, addend); }}#ifdef DEBUG_ALLOCstatic void inc_refcounts(struct disk_driver *bs, uint16_t *refcount_table, int refcount_table_size, int64_t offset, int64_t size){ BDRVQcowState *s = bs->private; int64_t start, last, cluster_offset; int k; if (size <= 0) return; start = offset & ~(s->cluster_size - 1); last = (offset + size - 1) & ~(s->cluster_size - 1); for(cluster_offset = start; cluster_offset <= last; cluster_offset += s->cluster_size) { k = cluster_offset >> s->cluster_bits; if (k < 0 || k >= refcount_table_size) { printf("ERROR: invalid cluster offset=0x%llx\n", cluster_offset); } else { if (++refcount_table[k] == 0) { printf("ERROR: overflow cluster offset=0x%llx\n", cluster_offset); } } }}static int check_refcounts_l1(struct disk_driver *bs, uint16_t *refcount_table, int refcount_table_size, int64_t l1_table_offset, int l1_size, int check_copied){ BDRVQcowState *s = bs->private; uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2; int l2_size, i, j, nb_csectors, refcount; l2_table = NULL; l1_size2 = l1_size * sizeof(uint64_t); inc_refcounts(bs, refcount_table, refcount_table_size, l1_table_offset, l1_size2); l1_table = qemu_malloc(l1_size2); if (!l1_table) goto fail; if (bdrv_pread(s->fd, l1_table_offset, l1_table, l1_size2) != l1_size2) goto fail; for(i = 0;i < l1_size; i++) be64_to_cpus(&l1_table[i]); l2_size = s->l2_size * sizeof(uint64_t); l2_table = qemu_malloc(l2_size); if (!l2_table) goto fail; for(i = 0; i < l1_size; i++) { l2_offset = l1_table[i]; if (l2_offset) { if (check_copied) { refcount = get_refcount(bs, (l2_offset & ~QCOW_OFLAG_COPIED) >> s->cluster_bits); if ((refcount == 1) != ((l2_offset & QCOW_OFLAG_COPIED) != 0)) { printf("ERROR OFLAG_COPIED: l2_offset=%llx refcount=%d\n", l2_offset, refcount); } } l2_offset &= ~QCOW_OFLAG_COPIED; if (bdrv_pread(s->fd, l2_offset, l2_table, l2_size) != l2_size) goto fail; for(j = 0; j < s->l2_size; j++) { offset = be64_to_cpu(l2_table[j]); if (offset != 0) { if (offset & QCOW_OFLAG_COMPRESSED) { if (offset & QCOW_OFLAG_COPIED) { printf("ERROR: cluster %lld: copied flag must never be set for compressed clusters\n", offset >> s->cluster_bits); offset &= ~QCOW_OFLAG_COPIED; } nb_csectors = ((offset >> s->csize_shift) & s->csize_mask) + 1; offset &= s->cluster_offset_mask; inc_refcounts(bs, refcount_table, refcount_table_size, offset & ~511, nb_csectors * 512); } else { if (check_copied) { refcount = get_refcount(bs, (offset & ~QCOW_OFLAG_COPIED) >> s->cluster_bits); if ((refcount == 1) != ((offset & QCOW_OFLAG_COPIED) != 0)) { printf("ERROR OFLAG_COPIED: offset=%llx refcount=%d\n", offset, refcount); } } offset &= ~QCOW_OFLAG_COPIED; inc_refcounts(bs, refcount_table, refcount_table_size, offset, s->cluster_size); } } } inc_refcounts(bs, refcount_table, refcount_table_size, l2_offset, s->cluster_size); } } qemu_free(l1_table); qemu_free(l2_table); return 0; fail: printf("ERROR: I/O error in check_refcounts_l1\n"); qemu_free(l1_table); qemu_free(l2_table); return -EIO;}static void check_refcounts(struct disk_driver *bs){ BDRVQcowState *s = bs->private; int64_t size; int nb_clusters, refcount1, refcount2, i; QCowSnapshot *sn; uint16_t *refcount_table; size = bdrv_getlength(s->fd); nb_clusters = (size + s->cluster_size - 1) >> s->cluster_bits; refcount_table = qemu_mallocz(nb_clusters * sizeof(uint16_t)); /* header */ inc_refcounts(bs, refcount_table, nb_clusters, 0, s->cluster_size); check_refcounts_l1(bs, refcount_table, nb_clusters, s->l1_table_offset, s->l1_size, 1); /* snapshots */ for(i = 0; i < s->nb_snapshots; i++) { sn = s->snapshots + i; check_refcounts_l1(bs, refcount_table, nb_clusters, sn->l1_table_offset, sn->l1_size, 0); } inc_refcounts(bs, refcount_table, nb_clusters, s->snapshots_offset, s->snapshots_size); /* refcount data */ inc_refcounts(bs, refcount_table, nb_clusters, s->refcount_table_offset, s->refcount_table_size * sizeof(uint64_t)); for(i = 0; i < s->refcount_table_size; i++) { int64_t offset; offset = s->refcount_table[i]; if (offset != 0) { inc_refcounts(bs, refcount_table, nb_clusters, offset, s->cluster_size); } } /* compare ref counts */ for(i = 0; i < nb_clusters; i++) { refcount1 = get_refcount(bs, i); refcount2 = refcount_table[i]; if (refcount1 != refcount2) printf("ERROR cluster %d refcount=%d reference=%d\n", i, refcount1, refcount2); } qemu_free(refcount_table);}#endif/** * Wrapper for synchronous read. * This function is called when not using AIO at all (#undef USE_AIO) or * for accessing the backing file. */static int qcow_sync_read(struct disk_driver *dd, uint64_t sector, int nb_sectors, char *buf, td_callback_t cb, int id, void *prv){ int ret = qcow_read(dd, sector, (uint8_t*) buf, nb_sectors); if (cb != NULL) { return cb(dd, (ret < 0) ? ret : 0, sector, nb_sectors, id, prv); } else { return ret; }}#ifndef USE_AIO/** * Wrapper for synchronous write */static int qcow_sync_write(struct disk_driver *dd, uint64_t sector, int nb_sectors, char *buf, td_callback_t cb, int id, void *prv){ int ret = qcow_write(dd, sector, (uint8_t*) buf, nb_sectors); return cb(dd, (ret < 0) ? ret : 0, sector, nb_sectors, id, prv);}#endif#ifndef USE_AIOstatic int qcow_do_callbacks(struct disk_driver *dd, int sid){ return 1;}#elsestatic int qcow_do_callbacks(struct disk_driver *dd, int sid){ int ret, i, nr_events, rsp = 0,*ptr; struct io_event *ep; struct BDRVQcowState *prv = (struct BDRVQcowState*)dd->private; if (sid > MAX_IOFD) return 1; nr_events = tap_aio_get_events(&prv->async.aio_ctx);repeat: for (ep = prv->async.aio_events, i = nr_events; i-- > 0; ep++) { struct iocb *io = ep->obj; struct pending_aio *pio; pio = &prv->async.pending_aio[(long)io->data]; tap_aio_unlock(&prv->async, pio->sector); if (prv->crypt_method) encrypt_sectors(prv, pio->sector, (unsigned char *)pio->buf, (unsigned char *)pio->buf, pio->nb_sectors, 0, &prv->aes_decrypt_key); rsp += pio->cb(dd, ep->res == io->u.c.nbytes ? 0 : 1, pio->sector, pio->nb_sectors, pio->id, pio->private); prv->async.iocb_free[prv->async.iocb_free_count++] = io; } if (nr_events) { nr_events = tap_aio_more_events(&prv->async.aio_ctx); goto repeat; } tap_aio_continue(&prv->async.aio_ctx); return rsp;}#endif /** * @return * 0 if parent id successfully retrieved; * TD_NO_PARENT if no parent exists; * -errno on error */static int qcow_get_parent_id(struct disk_driver *dd, struct disk_id *id){ struct BDRVQcowState* s = (struct BDRVQcowState*) dd->private; if (s->backing_file[0] == '\0') return TD_NO_PARENT; id->name = strdup(s->backing_file); id->drivertype = DISK_TYPE_QCOW2; return 0;}static int qcow_validate_parent(struct disk_driver *child, struct disk_driver *parent, td_flag_t flags){ struct BDRVQcowState *cs = (struct BDRVQcowState*) child->private; struct BDRVQcowState *ps = (struct BDRVQcowState*) parent->private; if (ps->total_sectors != cs->total_sectors) { DPRINTF("qcow_validate_parent(): %#"PRIx64" != %#"PRIx64"\n", ps->total_sectors, cs->total_sectors); return -EINVAL; } return 0;}struct tap_disk tapdisk_qcow2 = { "qcow2", sizeof(BDRVQcowState), qcow_open,#ifdef USE_AIO qcow_queue_read, qcow_queue_write,#else qcow_sync_read, qcow_sync_write,#endif qcow_submit, qcow_close, qcow_do_callbacks, qcow_get_parent_id, qcow_validate_parent};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -