X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=block-qcow2.c;h=60d87a4b8d4db8de0d1e1c4007dae029b2d25927;hb=2fb5a561c661f8a17a4f738a5a89addb86186e03;hp=8a5b621debdebbbedfd7a61746481e41d7270298;hpb=1987530fe0fbe00970d572cd2eda5a149aac3333;p=qemu diff --git a/block-qcow2.c b/block-qcow2.c index 8a5b621..60d87a4 100644 --- a/block-qcow2.c +++ b/block-qcow2.c @@ -45,6 +45,7 @@ //#define DEBUG_ALLOC //#define DEBUG_ALLOC2 +//#define DEBUG_EXT #define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb) #define QCOW_VERSION 2 @@ -77,6 +78,15 @@ typedef struct QCowHeader { uint64_t snapshots_offset; } QCowHeader; + +typedef struct { + uint32_t magic; + uint32_t len; +} QCowExtension; +#define QCOW_EXT_MAGIC_END 0 +#define QCOW_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA + + typedef struct __attribute__((packed)) QCowSnapshotHeader { /* header is 8 byte aligned */ uint64_t l1_table_offset; @@ -143,10 +153,6 @@ typedef struct BDRVQcowState { uint32_t crypt_method_header; AES_KEY aes_encrypt_key; AES_KEY aes_decrypt_key; - - int64_t highest_alloc; /* highest cluester allocated (in clusters) */ - int64_t nc_free; /* num of free clusters below highest_alloc */ - uint64_t snapshots_offset; int snapshots_size; int nb_snapshots; @@ -174,8 +180,6 @@ static void free_clusters(BlockDriverState *bs, #ifdef DEBUG_ALLOC static void check_refcounts(BlockDriverState *bs); #endif -static void scan_refcount(BlockDriverState *bs, int64_t *high, int64_t *free); - static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) { @@ -189,11 +193,84 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } + +/* + * read qcow2 extension and fill bs + * start reading from start_offset + * finish reading upon magic of value 0 or when end_offset reached + * unknown magic is skipped (future extension this version knows nothing about) + * return 0 upon success, non-0 otherwise + */ +static int qcow_read_extensions(BlockDriverState *bs, uint64_t start_offset, + uint64_t end_offset) +{ + BDRVQcowState *s = bs->opaque; + QCowExtension ext; + uint64_t offset; + +#ifdef DEBUG_EXT + printf("qcow_read_extensions: start=%ld end=%ld\n", start_offset, end_offset); +#endif + offset = start_offset; + while (offset < end_offset) { + +#ifdef DEBUG_EXT + /* Sanity check */ + if (offset > s->cluster_size) + printf("qcow_handle_extension: suspicious offset %lu\n", offset); + + printf("attemting to read extended header in offset %lu\n", offset); +#endif + + if (bdrv_pread(s->hd, offset, &ext, sizeof(ext)) != sizeof(ext)) { + fprintf(stderr, "qcow_handle_extension: ERROR: pread fail from offset %llu\n", + (unsigned long long)offset); + return 1; + } + be32_to_cpus(&ext.magic); + be32_to_cpus(&ext.len); + offset += sizeof(ext); +#ifdef DEBUG_EXT + printf("ext.magic = 0x%x\n", ext.magic); +#endif + switch (ext.magic) { + case QCOW_EXT_MAGIC_END: + return 0; + + case QCOW_EXT_MAGIC_BACKING_FORMAT: + if (ext.len >= sizeof(bs->backing_format)) { + fprintf(stderr, "ERROR: ext_backing_format: len=%u too large" + " (>=%zu)\n", + ext.len, sizeof(bs->backing_format)); + return 2; + } + if (bdrv_pread(s->hd, offset , bs->backing_format, + ext.len) != ext.len) + return 3; + bs->backing_format[ext.len] = '\0'; +#ifdef DEBUG_EXT + printf("Qcow2: Got format extension %s\n", bs->backing_format); +#endif + offset += ((ext.len + 7) & ~7); + break; + + default: + /* unknown magic -- just skip it */ + offset += ((ext.len + 7) & ~7); + break; + } + } + + return 0; +} + + static int qcow_open(BlockDriverState *bs, const char *filename, int flags) { BDRVQcowState *s = bs->opaque; int len, i, shift, ret; QCowHeader header; + uint64_t ext_end; /* Performance is terrible right now with cache=writethrough due mainly * to reference count updates. If the user does not explicitly specify @@ -259,8 +336,6 @@ static int qcow_open(BlockDriverState *bs, const char *filename, int flags) goto fail; s->l1_table_offset = header.l1_table_offset; s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t)); - if (!s->l1_table) - goto fail; if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) != s->l1_size * sizeof(uint64_t)) goto fail; @@ -269,22 +344,22 @@ static int qcow_open(BlockDriverState *bs, const char *filename, int flags) } /* alloc L2 cache */ s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); - if (!s->l2_cache) - goto fail; s->cluster_cache = qemu_malloc(s->cluster_size); - if (!s->cluster_cache) - goto fail; /* one more sector for decompressed data alignment */ s->cluster_data = qemu_malloc(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size + 512); - if (!s->cluster_data) - goto fail; s->cluster_cache_offset = -1; if (refcount_init(bs) < 0) goto fail; - scan_refcount(bs, &s->highest_alloc, &s->nc_free); + /* read qcow2 extensions */ + if (header.backing_file_offset) + ext_end = header.backing_file_offset; + else + ext_end = s->cluster_size; + if (qcow_read_extensions(bs, sizeof(header), ext_end)) + goto fail; /* read the backing file name */ if (header.backing_file_offset != 0) { @@ -459,8 +534,6 @@ static int grow_l1_table(BlockDriverState *bs, int min_size) new_l1_size2 = sizeof(uint64_t) * new_l1_size; new_l1_table = qemu_mallocz(new_l1_size2); - if (!new_l1_table) - return -ENOMEM; memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t)); /* write new table (align to cluster) */ @@ -893,8 +966,7 @@ static int alloc_cluster_link_l2(BlockDriverState *bs, uint64_t cluster_offset, if (m->nb_clusters == 0) return 0; - if (!(old_cluster = qemu_malloc(m->nb_clusters * sizeof(uint64_t)))) - return -ENOMEM; + old_cluster = qemu_malloc(m->nb_clusters * sizeof(uint64_t)); /* copy content of unmodified sectors */ start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9; @@ -1393,10 +1465,6 @@ static void qcow_aio_write_cb(void *opaque, int ret) if (!acb->cluster_data) { acb->cluster_data = qemu_mallocz(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); - if (!acb->cluster_data) { - ret = -ENOMEM; - goto fail; - } } encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->buf, acb->n, 1, &s->aes_encrypt_key); @@ -1477,13 +1545,18 @@ static void create_refcount_update(QCowCreateState *s, } } -static int qcow_create(const char *filename, int64_t total_size, - const char *backing_file, int flags) +static int qcow_create2(const char *filename, int64_t total_size, + const char *backing_file, const char *backing_format, + int flags) { + int fd, header_size, backing_filename_len, l1_size, i, shift, l2_bits; + int backing_format_len = 0; QCowHeader header; uint64_t tmp, offset; QCowCreateState s1, *s = &s1; + QCowExtension ext_bf = {0, 0}; + memset(s, 0, sizeof(*s)); @@ -1497,6 +1570,12 @@ static int qcow_create(const char *filename, int64_t total_size, header_size = sizeof(header); backing_filename_len = 0; if (backing_file) { + if (backing_format) { + ext_bf.magic = QCOW_EXT_MAGIC_BACKING_FORMAT; + backing_format_len = strlen(backing_format); + ext_bf.len = (backing_format_len + 7) & ~7; + header_size += ((sizeof(ext_bf) + ext_bf.len + 7) & ~7); + } header.backing_file_offset = cpu_to_be64(header_size); backing_filename_len = strlen(backing_file); header.backing_file_size = cpu_to_be32(backing_filename_len); @@ -1521,11 +1600,7 @@ static int qcow_create(const char *filename, int64_t total_size, offset += align_offset(l1_size * sizeof(uint64_t), s->cluster_size); s->refcount_table = qemu_mallocz(s->cluster_size); - if (!s->refcount_table) - goto fail; s->refcount_block = qemu_mallocz(s->cluster_size); - if (!s->refcount_block) - goto fail; s->refcount_table_offset = offset; header.refcount_table_offset = cpu_to_be64(offset); @@ -1545,6 +1620,19 @@ static int qcow_create(const char *filename, int64_t total_size, /* write all the data */ write(fd, &header, sizeof(header)); if (backing_file) { + if (backing_format_len) { + char zero[16]; + int d = ext_bf.len - backing_format_len; + + memset(zero, 0, sizeof(zero)); + cpu_to_be32s(&ext_bf.magic); + cpu_to_be32s(&ext_bf.len); + write(fd, &ext_bf, sizeof(ext_bf)); + write(fd, backing_format, backing_format_len); + if (d>0) { + write(fd, zero, d); + } + } write(fd, backing_file, backing_filename_len); } lseek(fd, s->l1_table_offset, SEEK_SET); @@ -1562,11 +1650,12 @@ static int qcow_create(const char *filename, int64_t total_size, qemu_free(s->refcount_block); close(fd); return 0; - fail: - qemu_free(s->refcount_table); - qemu_free(s->refcount_block); - close(fd); - return -ENOMEM; +} + +static int qcow_create(const char *filename, int64_t total_size, + const char *backing_file, int flags) +{ + return qcow_create2(filename, total_size, backing_file, NULL, flags); } static int qcow_make_empty(BlockDriverState *bs) @@ -1613,8 +1702,6 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, return -EINVAL; out_buf = qemu_malloc(s->cluster_size + (s->cluster_size / 1000) + 128); - if (!out_buf) - return -ENOMEM; /* best compression, small window, no zlib header */ memset(&strm, 0, sizeof(strm)); @@ -1672,8 +1759,6 @@ static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) bdi->cluster_size = s->cluster_size; bdi->vm_state_offset = (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); - bdi->highest_alloc = s->highest_alloc << s->cluster_bits; - bdi->num_free_bytes = s->nc_free << s->cluster_bits; return 0; } @@ -1699,8 +1784,6 @@ static int update_snapshot_refcount(BlockDriverState *bs, l1_allocated = 0; if (l1_table_offset != s->l1_table_offset) { l1_table = qemu_malloc(l1_size2); - if (!l1_table) - goto fail; l1_allocated = 1; if (bdrv_pread(s->hd, l1_table_offset, l1_table, l1_size2) != l1_size2) @@ -1715,8 +1798,6 @@ static int update_snapshot_refcount(BlockDriverState *bs, l2_size = s->l2_size * sizeof(uint64_t); l2_table = qemu_malloc(l2_size); - if (!l2_table) - goto fail; l1_modified = 0; for(i = 0; i < l1_size; i++) { l2_offset = l1_table[i]; @@ -1827,8 +1908,6 @@ static int qcow_read_snapshots(BlockDriverState *bs) offset = s->snapshots_offset; s->snapshots = qemu_mallocz(s->nb_snapshots * sizeof(QCowSnapshot)); - if (!s->snapshots) - goto fail; for(i = 0; i < s->nb_snapshots; i++) { offset = align_offset(offset, 8); if (bdrv_pread(s->hd, offset, &h, sizeof(h)) != sizeof(h)) @@ -1849,16 +1928,12 @@ static int qcow_read_snapshots(BlockDriverState *bs) offset += extra_data_size; sn->id_str = qemu_malloc(id_str_size + 1); - if (!sn->id_str) - goto fail; if (bdrv_pread(s->hd, offset, sn->id_str, id_str_size) != id_str_size) goto fail; offset += id_str_size; sn->id_str[id_str_size] = '\0'; sn->name = qemu_malloc(name_size + 1); - if (!sn->name) - goto fail; if (bdrv_pread(s->hd, offset, sn->name, name_size) != name_size) goto fail; offset += name_size; @@ -2024,8 +2099,6 @@ static int qcow_snapshot_create(BlockDriverState *bs, sn->l1_size = s->l1_size; l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t)); - if (!l1_table) - goto fail; for(i = 0; i < s->l1_size; i++) { l1_table[i] = cpu_to_be64(s->l1_table[i]); } @@ -2037,8 +2110,6 @@ static int qcow_snapshot_create(BlockDriverState *bs, l1_table = NULL; snapshots1 = qemu_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot)); - if (!snapshots1) - goto fail; if (s->snapshots) { memcpy(snapshots1, s->snapshots, s->nb_snapshots * sizeof(QCowSnapshot)); qemu_free(s->snapshots); @@ -2145,8 +2216,6 @@ static int qcow_snapshot_list(BlockDriverState *bs, int i; sn_tab = qemu_mallocz(s->nb_snapshots * sizeof(QEMUSnapshotInfo)); - if (!sn_tab) - goto fail; for(i = 0; i < s->nb_snapshots; i++) { sn_info = sn_tab + i; sn = s->snapshots + i; @@ -2161,10 +2230,6 @@ static int qcow_snapshot_list(BlockDriverState *bs, } *psn_tab = sn_tab; return s->nb_snapshots; - fail: - qemu_free(sn_tab); - *psn_tab = NULL; - return -ENOMEM; } /*********************************************************/ @@ -2176,12 +2241,8 @@ static int refcount_init(BlockDriverState *bs) int ret, refcount_table_size2, i; s->refcount_block_cache = qemu_malloc(s->cluster_size); - if (!s->refcount_block_cache) - goto fail; refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t); s->refcount_table = qemu_malloc(refcount_table_size2); - if (!s->refcount_table) - goto fail; if (s->refcount_table_size > 0) { ret = bdrv_pread(s->hd, s->refcount_table_offset, s->refcount_table, refcount_table_size2); @@ -2216,39 +2277,6 @@ static int load_refcount_block(BlockDriverState *bs, return 0; } -static void scan_refcount(BlockDriverState *bs, int64_t *high, int64_t *free) -{ - BDRVQcowState *s = bs->opaque; - int64_t refcnt_index, cluster_index, cluster_end, h = 0, f = 0; - int64_t tail = 0; /* do not count last consecutive free entries */ - - for (refcnt_index=0; refcnt_index < s->refcount_table_size; refcnt_index++){ - if (s->refcount_table[refcnt_index] == 0) { - f += 1 << (s->cluster_bits - REFCOUNT_SHIFT); - tail += 1 << (s->cluster_bits - REFCOUNT_SHIFT); - continue; - } - cluster_index = refcnt_index << (s->cluster_bits - REFCOUNT_SHIFT); - cluster_end = (refcnt_index + 1) << (s->cluster_bits - REFCOUNT_SHIFT); - for ( ; cluster_index < cluster_end; cluster_index++) { - if (get_refcount(bs, cluster_index) == 0) { - f++; - tail++; - } - else { - h = cluster_index; - tail = 0; - } - } - } - - f -= tail; - if (free) - *free = f; - if (high) - *high = (h+1); -} - static int get_refcount(BlockDriverState *bs, int64_t cluster_index) { BDRVQcowState *s = bs->opaque; @@ -2289,12 +2317,6 @@ retry: size, (s->free_cluster_index - nb_clusters) << s->cluster_bits); #endif - - if (s->highest_alloc < s->free_cluster_index) { - s->nc_free += (s->free_cluster_index - s->highest_alloc); - s->highest_alloc = s->free_cluster_index; - } - return (s->free_cluster_index - nb_clusters) << s->cluster_bits; } @@ -2384,8 +2406,6 @@ static int grow_refcount_table(BlockDriverState *bs, int min_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++) @@ -2470,12 +2490,6 @@ static int update_cluster_refcount(BlockDriverState *bs, block_index = cluster_index & ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); refcount = be16_to_cpu(s->refcount_block_cache[block_index]); - - if (refcount == 1 && addend == -1) - s->nc_free += 1; - else if (refcount == 0 && addend == 1) - s->nc_free -= 1; - refcount += addend; if (refcount < 0 || refcount > 0xffff) return -EINVAL; @@ -2556,8 +2570,6 @@ static int check_refcounts_l1(BlockDriverState *bs, l1_table_offset, l1_size2); l1_table = qemu_malloc(l1_size2); - if (!l1_table) - goto fail; if (bdrv_pread(s->hd, l1_table_offset, l1_table, l1_size2) != l1_size2) goto fail; @@ -2566,8 +2578,6 @@ static int check_refcounts_l1(BlockDriverState *bs, 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) { @@ -2702,28 +2712,28 @@ static void dump_refcounts(BlockDriverState *bs) #endif BlockDriver bdrv_qcow2 = { - "qcow2", - sizeof(BDRVQcowState), - qcow_probe, - qcow_open, - NULL, - NULL, - qcow_close, - qcow_create, - qcow_flush, - qcow_is_allocated, - qcow_set_key, - qcow_make_empty, - - .bdrv_aio_read = qcow_aio_read, - .bdrv_aio_write = qcow_aio_write, - .bdrv_aio_cancel = qcow_aio_cancel, - .aiocb_size = sizeof(QCowAIOCB), + .format_name = "qcow2", + .instance_size = sizeof(BDRVQcowState), + .bdrv_probe = qcow_probe, + .bdrv_open = qcow_open, + .bdrv_close = qcow_close, + .bdrv_create = qcow_create, + .bdrv_flush = qcow_flush, + .bdrv_is_allocated = qcow_is_allocated, + .bdrv_set_key = qcow_set_key, + .bdrv_make_empty = qcow_make_empty, + + .bdrv_aio_read = qcow_aio_read, + .bdrv_aio_write = qcow_aio_write, + .bdrv_aio_cancel = qcow_aio_cancel, + .aiocb_size = sizeof(QCowAIOCB), .bdrv_write_compressed = qcow_write_compressed, .bdrv_snapshot_create = qcow_snapshot_create, - .bdrv_snapshot_goto = qcow_snapshot_goto, + .bdrv_snapshot_goto = qcow_snapshot_goto, .bdrv_snapshot_delete = qcow_snapshot_delete, - .bdrv_snapshot_list = qcow_snapshot_list, - .bdrv_get_info = qcow_get_info, + .bdrv_snapshot_list = qcow_snapshot_list, + .bdrv_get_info = qcow_get_info, + + .bdrv_create2 = qcow_create2, };