X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/58369e22cf971448411bfbc8c894b2addebe2111..cfc87e00:/block/vpc.c diff --git a/block/vpc.c b/block/vpc.c index 076a7ce399..10e6519d78 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -28,11 +28,9 @@ #include "block/block_int.h" #include "sysemu/block-backend.h" #include "qemu/module.h" -#include "migration/migration.h" +#include "migration/blocker.h" #include "qemu/bswap.h" -#if defined(CONFIG_UUID) -#include -#endif +#include "qemu/uuid.h" /**************************************************************/ @@ -89,7 +87,7 @@ typedef struct vhd_footer { uint32_t checksum; /* UUID used to identify a parent hard disk (backing file) */ - uint8_t uuid[16]; + QemuUUID uuid; uint8_t in_saved_state; } QEMU_PACKED VHDFooter; @@ -222,6 +220,12 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, int disk_type = VHD_DYNAMIC; int ret; + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + opts = qemu_opts_create(&vpc_runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); if (local_err) { @@ -237,7 +241,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_pread(bs->file->bs, 0, s->footer_buf, HEADER_SIZE); + ret = bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE); if (ret < 0) { error_setg(errp, "Unable to read VHD header"); goto fail; @@ -257,7 +261,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, } /* If a fixed disk, the footer is found only at the end of the file */ - ret = bdrv_pread(bs->file->bs, offset-HEADER_SIZE, s->footer_buf, + ret = bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, HEADER_SIZE); if (ret < 0) { goto fail; @@ -328,7 +332,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, } if (disk_type == VHD_DYNAMIC) { - ret = bdrv_pread(bs->file->bs, be64_to_cpu(footer->data_offset), buf, + ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE); if (ret < 0) { error_setg(errp, "Error reading dynamic VHD header"); @@ -385,7 +389,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); - ret = bdrv_pread(bs->file->bs, s->bat_offset, s->pagetable, + ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, pagetable_size); if (ret < 0) { error_setg(errp, "Error reading pagetable"); @@ -424,13 +428,18 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, #endif } - qemu_co_mutex_init(&s->lock); - /* Disable migration when VHD images are used */ error_setg(&s->migration_blocker, "The vpc format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - migrate_add_blocker(s->migration_blocker); + ret = migrate_add_blocker(s->migration_blocker, &local_err); + if (local_err) { + error_propagate(errp, local_err); + error_free(s->migration_blocker); + goto fail; + } + + qemu_co_mutex_init(&s->lock); return 0; @@ -451,17 +460,23 @@ static int vpc_reopen_prepare(BDRVReopenState *state, /* * Returns the absolute byte offset of the given sector in the image file. * If the sector is not allocated, -1 is returned instead. + * If an error occurred trying to write an updated block bitmap back to + * the file, -2 is returned, and the error value is written to *err. + * This can only happen for a write operation. * * The parameter write must be 1 if the offset will be used for a write * operation (the block bitmaps is updated then), 0 otherwise. + * If write is true then err must not be NULL. */ static inline int64_t get_image_offset(BlockDriverState *bs, uint64_t offset, - bool write) + bool write, int *err) { BDRVVPCState *s = bs->opaque; uint64_t bitmap_offset, block_offset; uint32_t pagetable_index, offset_in_block; + assert(!(write && err == NULL)); + pagetable_index = offset / s->block_size; offset_in_block = offset % s->block_size; @@ -478,21 +493,20 @@ static inline int64_t get_image_offset(BlockDriverState *bs, uint64_t offset, correctness. */ if (write && (s->last_bitmap_offset != bitmap_offset)) { uint8_t bitmap[s->bitmap_size]; + int r; s->last_bitmap_offset = bitmap_offset; memset(bitmap, 0xff, s->bitmap_size); - bdrv_pwrite_sync(bs->file->bs, bitmap_offset, bitmap, s->bitmap_size); + r = bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size); + if (r < 0) { + *err = r; + return -2; + } } return block_offset; } -static inline int64_t get_sector_offset(BlockDriverState *bs, - int64_t sector_num, bool write) -{ - return get_image_offset(bs, sector_num * BDRV_SECTOR_SIZE, write); -} - /* * Writes the footer to the end of the image file. This is needed when the * file grows as it overwrites the old footer @@ -505,7 +519,7 @@ static int rewrite_footer(BlockDriverState* bs) BDRVVPCState *s = bs->opaque; int64_t offset = s->free_data_block_offset; - ret = bdrv_pwrite_sync(bs->file->bs, offset, s->footer_buf, HEADER_SIZE); + ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, HEADER_SIZE); if (ret < 0) return ret; @@ -539,7 +553,7 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t offset) /* Initialize the block's bitmap */ memset(bitmap, 0xff, s->bitmap_size); - ret = bdrv_pwrite_sync(bs->file->bs, s->free_data_block_offset, bitmap, + ret = bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap, s->bitmap_size); if (ret < 0) { return ret; @@ -554,11 +568,11 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t offset) /* Write BAT entry to disk */ bat_offset = s->bat_offset + (4 * index); bat_value = cpu_to_be32(s->pagetable[index]); - ret = bdrv_pwrite_sync(bs->file->bs, bat_offset, &bat_value, 4); + ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4); if (ret < 0) goto fail; - return get_image_offset(bs, offset, false); + return get_image_offset(bs, offset, false, NULL); fail: s->free_data_block_offset -= (s->block_size + s->bitmap_size); @@ -591,14 +605,14 @@ vpc_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector local_qiov; if (be32_to_cpu(footer->type) == VHD_FIXED) { - return bdrv_co_preadv(bs->file->bs, offset, bytes, qiov, 0); + return bdrv_co_preadv(bs->file, offset, bytes, qiov, 0); } qemu_co_mutex_lock(&s->lock); qemu_iovec_init(&local_qiov, qiov->niov); while (bytes > 0) { - image_offset = get_image_offset(bs, offset, false); + image_offset = get_image_offset(bs, offset, false, NULL); n_bytes = MIN(bytes, s->block_size - (offset % s->block_size)); if (image_offset == -1) { @@ -607,7 +621,7 @@ vpc_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, qemu_iovec_reset(&local_qiov); qemu_iovec_concat(&local_qiov, qiov, bytes_done, n_bytes); - ret = bdrv_co_preadv(bs->file->bs, image_offset, n_bytes, + ret = bdrv_co_preadv(bs->file, image_offset, n_bytes, &local_qiov, 0); if (ret < 0) { goto fail; @@ -640,14 +654,18 @@ vpc_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector local_qiov; if (be32_to_cpu(footer->type) == VHD_FIXED) { - return bdrv_co_pwritev(bs->file->bs, offset, bytes, qiov, 0); + return bdrv_co_pwritev(bs->file, offset, bytes, qiov, 0); } qemu_co_mutex_lock(&s->lock); qemu_iovec_init(&local_qiov, qiov->niov); while (bytes > 0) { - image_offset = get_image_offset(bs, offset, true); + image_offset = get_image_offset(bs, offset, true, &ret); + if (image_offset == -2) { + /* Failed to write block bitmap: can't proceed with write */ + goto fail; + } n_bytes = MIN(bytes, s->block_size - (offset % s->block_size)); if (image_offset == -1) { @@ -661,7 +679,7 @@ vpc_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, qemu_iovec_reset(&local_qiov); qemu_iovec_concat(&local_qiov, qiov, bytes_done, n_bytes); - ret = bdrv_co_pwritev(bs->file->bs, image_offset, n_bytes, + ret = bdrv_co_pwritev(bs->file, image_offset, n_bytes, &local_qiov, 0); if (ret < 0) { goto fail; @@ -687,19 +705,23 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs, VHDFooter *footer = (VHDFooter*) s->footer_buf; int64_t start, offset; bool allocated; + int64_t ret; int n; if (be32_to_cpu(footer->type) == VHD_FIXED) { *pnum = nb_sectors; *file = bs->file->bs; - return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA | + return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | (sector_num << BDRV_SECTOR_BITS); } - offset = get_sector_offset(bs, sector_num, 0); + qemu_co_mutex_lock(&s->lock); + + offset = get_image_offset(bs, sector_num << BDRV_SECTOR_BITS, false, NULL); start = offset; allocated = (offset != -1); *pnum = 0; + ret = 0; do { /* All sectors in a block are contiguous (without using the bitmap) */ @@ -714,15 +736,18 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs, * sectors since there is always a bitmap in between. */ if (allocated) { *file = bs->file->bs; - return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; + ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; + break; } if (nb_sectors == 0) { break; } - offset = get_sector_offset(bs, sector_num, 0); + offset = get_image_offset(bs, sector_num << BDRV_SECTOR_BITS, false, + NULL); } while (offset == -1); - return 0; + qemu_co_mutex_unlock(&s->lock); + return ret; } /* @@ -842,20 +867,21 @@ static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf, } static int create_fixed_disk(BlockBackend *blk, uint8_t *buf, - int64_t total_size) + int64_t total_size, Error **errp) { int ret; /* Add footer to total size */ total_size += HEADER_SIZE; - ret = blk_truncate(blk, total_size); + ret = blk_truncate(blk, total_size, PREALLOC_MODE_OFF, errp); if (ret < 0) { return ret; } ret = blk_pwrite(blk, total_size - HEADER_SIZE, buf, HEADER_SIZE, 0); if (ret < 0) { + error_setg_errno(errp, -ret, "Unable to write VHD header"); return ret; } @@ -906,7 +932,8 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) } blk = blk_new_open(filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, + &local_err); if (blk == NULL) { error_propagate(errp, local_err); ret = -EIO; @@ -980,19 +1007,17 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) footer->type = cpu_to_be32(disk_type); -#if defined(CONFIG_UUID) - uuid_generate(footer->uuid); -#endif + qemu_uuid_generate(&footer->uuid); footer->checksum = cpu_to_be32(vpc_checksum(buf, HEADER_SIZE)); if (disk_type == VHD_DYNAMIC) { ret = create_dynamic_disk(blk, buf, total_sectors); + if (ret < 0) { + error_setg(errp, "Unable to create or write VHD header"); + } } else { - ret = create_fixed_disk(blk, buf, total_size); - } - if (ret < 0) { - error_setg(errp, "Unable to create or write VHD header"); + ret = create_fixed_disk(blk, buf, total_size, errp); } out: @@ -1060,6 +1085,7 @@ static BlockDriver bdrv_vpc = { .bdrv_open = vpc_open, .bdrv_close = vpc_close, .bdrv_reopen_prepare = vpc_reopen_prepare, + .bdrv_child_perm = bdrv_format_default_perms, .bdrv_create = vpc_create, .bdrv_co_preadv = vpc_co_preadv,