X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=block.c;h=382ea71f4b8c28550d4a798377fd1e6c4bae04ae;hb=60aad298cb6de52f2716b2e82e1353ea9de95fd6;hp=5df17de5a48ea9a5faa2b6028ae7b9304da8f621;hpb=d663640c04f2aab810915c556390211d75457704;p=qemu.git diff --git a/block.c b/block.c index 5df17de5a..382ea71f4 100644 --- a/block.c +++ b/block.c @@ -394,18 +394,26 @@ typedef struct CreateCo { char *filename; QEMUOptionParameter *options; int ret; + Error *err; } CreateCo; static void coroutine_fn bdrv_create_co_entry(void *opaque) { + Error *local_err = NULL; + int ret; + CreateCo *cco = opaque; assert(cco->drv); - cco->ret = cco->drv->bdrv_create(cco->filename, cco->options); + ret = cco->drv->bdrv_create(cco->filename, cco->options, &local_err); + if (error_is_set(&local_err)) { + error_propagate(&cco->err, local_err); + } + cco->ret = ret; } int bdrv_create(BlockDriver *drv, const char* filename, - QEMUOptionParameter *options) + QEMUOptionParameter *options, Error **errp) { int ret; @@ -415,9 +423,11 @@ int bdrv_create(BlockDriver *drv, const char* filename, .filename = g_strdup(filename), .options = options, .ret = NOT_DONE, + .err = NULL, }; if (!drv->bdrv_create) { + error_setg(errp, "Driver '%s' does not support image creation", drv->format_name); ret = -ENOTSUP; goto out; } @@ -434,22 +444,37 @@ int bdrv_create(BlockDriver *drv, const char* filename, } ret = cco.ret; + if (ret < 0) { + if (error_is_set(&cco.err)) { + error_propagate(errp, cco.err); + } else { + error_setg_errno(errp, -ret, "Could not create image"); + } + } out: g_free(cco.filename); return ret; } -int bdrv_create_file(const char* filename, QEMUOptionParameter *options) +int bdrv_create_file(const char* filename, QEMUOptionParameter *options, + Error **errp) { BlockDriver *drv; + Error *local_err = NULL; + int ret; drv = bdrv_find_protocol(filename, true); if (drv == NULL) { + error_setg(errp, "Could not find protocol for file '%s'", filename); return -ENOENT; } - return bdrv_create(drv, filename, options); + ret = bdrv_create(drv, filename, options, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + } + return ret; } /* @@ -552,7 +577,7 @@ BlockDriver *bdrv_find_protocol(const char *filename, } static int find_image_format(BlockDriverState *bs, const char *filename, - BlockDriver **pdrv) + BlockDriver **pdrv, Error **errp) { int score, score_max; BlockDriver *drv1, *drv; @@ -563,6 +588,7 @@ static int find_image_format(BlockDriverState *bs, const char *filename, if (bs->sg || !bdrv_is_inserted(bs) || bdrv_getlength(bs) == 0) { drv = bdrv_find_format("raw"); if (!drv) { + error_setg(errp, "Could not find raw image format"); ret = -ENOENT; } *pdrv = drv; @@ -571,6 +597,8 @@ static int find_image_format(BlockDriverState *bs, const char *filename, ret = bdrv_pread(bs, 0, buf, sizeof(buf)); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read image for determining its " + "format"); *pdrv = NULL; return ret; } @@ -587,6 +615,8 @@ static int find_image_format(BlockDriverState *bs, const char *filename, } } if (!drv) { + error_setg(errp, "Could not determine image format: No compatible " + "driver found"); ret = -ENOENT; } *pdrv = drv; @@ -610,7 +640,7 @@ static int refresh_total_sectors(BlockDriverState *bs, int64_t hint) if (length < 0) { return length; } - hint = length >> BDRV_SECTOR_BITS; + hint = DIV_ROUND_UP(length, BDRV_SECTOR_SIZE); } bs->total_sectors = hint; @@ -706,10 +736,11 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags) * Removes all processed options from *options. */ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, - QDict *options, int flags, BlockDriver *drv) + QDict *options, int flags, BlockDriver *drv, Error **errp) { int ret, open_flags; const char *filename; + Error *local_err = NULL; assert(drv != NULL); assert(bs->file == NULL); @@ -738,12 +769,22 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, bs->read_only = !(open_flags & BDRV_O_RDWR); if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { + error_setg(errp, + !bs->read_only && bdrv_is_whitelisted(drv, true) + ? "Driver '%s' can only be used for read-only devices" + : "Driver '%s' is not whitelisted", + drv->format_name); return -ENOTSUP; } assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */ - if (!bs->read_only && (flags & BDRV_O_COPY_ON_READ)) { - bdrv_enable_copy_on_read(bs); + if (flags & BDRV_O_COPY_ON_READ) { + if (!bs->read_only) { + bdrv_enable_copy_on_read(bs); + } else { + error_setg(errp, "Can't use copy-on-read on read-only device"); + return -EINVAL; + } } if (filename != NULL) { @@ -760,33 +801,40 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, /* Open the image, either directly or using a protocol */ if (drv->bdrv_file_open) { assert(file == NULL); - assert(drv->bdrv_parse_filename || filename != NULL); - ret = drv->bdrv_file_open(bs, options, open_flags); + assert(!drv->bdrv_needs_filename || filename != NULL); + ret = drv->bdrv_file_open(bs, options, open_flags, &local_err); } else { if (file == NULL) { - qerror_report(ERROR_CLASS_GENERIC_ERROR, "Can't use '%s' as a " - "block driver for the protocol level", - drv->format_name); + error_setg(errp, "Can't use '%s' as a block driver for the " + "protocol level", drv->format_name); ret = -EINVAL; goto free_and_fail; } bs->file = file; - ret = drv->bdrv_open(bs, options, open_flags); + ret = drv->bdrv_open(bs, options, open_flags, &local_err); } if (ret < 0) { + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + } else if (bs->filename[0]) { + error_setg_errno(errp, -ret, "Could not open '%s'", bs->filename); + } else { + error_setg_errno(errp, -ret, "Could not open image"); + } goto free_and_fail; } ret = refresh_total_sectors(bs, bs->total_sectors); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not refresh total sector count"); goto free_and_fail; } #ifndef _WIN32 if (bs->is_temporary) { - assert(filename != NULL); - unlink(filename); + assert(bs->filename[0] != '\0'); + unlink(bs->filename); } #endif return 0; @@ -808,12 +856,13 @@ free_and_fail: * dictionary, it needs to use QINCREF() before calling bdrv_file_open. */ int bdrv_file_open(BlockDriverState **pbs, const char *filename, - QDict *options, int flags) + QDict *options, int flags, Error **errp) { BlockDriverState *bs; BlockDriver *drv; const char *drvname; bool allow_protocol_prefix = false; + Error *local_err = NULL; int ret; /* NULL means an empty set of options */ @@ -832,8 +881,8 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, qdict_put(options, "filename", qstring_from_str(filename)); allow_protocol_prefix = true; } else { - qerror_report(ERROR_CLASS_GENERIC_ERROR, "Can't specify 'file' and " - "'filename' options at the same time"); + error_setg(errp, "Can't specify 'file' and 'filename' options at the " + "same time"); ret = -EINVAL; goto fail; } @@ -841,54 +890,54 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, /* Find the right block driver */ drvname = qdict_get_try_str(options, "driver"); if (drvname) { - drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR)); + drv = bdrv_find_format(drvname); + if (!drv) { + error_setg(errp, "Unknown driver '%s'", drvname); + } qdict_del(options, "driver"); } else if (filename) { drv = bdrv_find_protocol(filename, allow_protocol_prefix); if (!drv) { - qerror_report(ERROR_CLASS_GENERIC_ERROR, "Unknown protocol"); + error_setg(errp, "Unknown protocol"); } } else { - qerror_report(ERROR_CLASS_GENERIC_ERROR, - "Must specify either driver or file"); + error_setg(errp, "Must specify either driver or file"); drv = NULL; } if (!drv) { + /* errp has been set already */ ret = -ENOENT; goto fail; } /* Parse the filename and open it */ if (drv->bdrv_parse_filename && filename) { - Error *local_err = NULL; drv->bdrv_parse_filename(filename, options, &local_err); if (error_is_set(&local_err)) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); ret = -EINVAL; goto fail; } qdict_del(options, "filename"); - } else if (!drv->bdrv_parse_filename && !filename) { - qerror_report(ERROR_CLASS_GENERIC_ERROR, - "The '%s' block driver requires a file name", - drv->format_name); + } else if (drv->bdrv_needs_filename && !filename) { + error_setg(errp, "The '%s' block driver requires a file name", + drv->format_name); ret = -EINVAL; goto fail; } - ret = bdrv_open_common(bs, NULL, options, flags, drv); + ret = bdrv_open_common(bs, NULL, options, flags, drv, &local_err); if (ret < 0) { + error_propagate(errp, local_err); goto fail; } /* Check if any unknown options were used */ if (qdict_size(options) != 0) { const QDictEntry *entry = qdict_first(options); - qerror_report(ERROR_CLASS_GENERIC_ERROR, "Block protocol '%s' doesn't " - "support the option '%s'", - drv->format_name, entry->key); + error_setg(errp, "Block protocol '%s' doesn't support the option '%s'", + drv->format_name, entry->key); ret = -EINVAL; goto fail; } @@ -915,11 +964,12 @@ fail: * function (even on failure), so if the caller intends to reuse the dictionary, * it needs to use QINCREF() before calling bdrv_file_open. */ -int bdrv_open_backing_file(BlockDriverState *bs, QDict *options) +int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) { char backing_filename[PATH_MAX]; int back_flags, ret; BlockDriver *back_drv = NULL; + Error *local_err = NULL; if (bs->backing_hd != NULL) { QDECREF(options); @@ -937,50 +987,38 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options) } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { QDECREF(options); return 0; + } else { + bdrv_get_full_backing_filename(bs, backing_filename, + sizeof(backing_filename)); } bs->backing_hd = bdrv_new(""); - bdrv_get_full_backing_filename(bs, backing_filename, - sizeof(backing_filename)); if (bs->backing_format[0] != '\0') { back_drv = bdrv_find_format(bs->backing_format); } /* backing files always opened read-only */ - back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT); + back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | + BDRV_O_COPY_ON_READ); ret = bdrv_open(bs->backing_hd, *backing_filename ? backing_filename : NULL, options, - back_flags, back_drv); + back_flags, back_drv, &local_err); if (ret < 0) { bdrv_unref(bs->backing_hd); bs->backing_hd = NULL; bs->open_flags |= BDRV_O_NO_BACKING; + error_setg(errp, "Could not open backing file: %s", + error_get_pretty(local_err)); + error_free(local_err); return ret; } + pstrcpy(bs->backing_file, sizeof(bs->backing_file), + bs->backing_hd->file->filename); return 0; } -static void extract_subqdict(QDict *src, QDict **dst, const char *start) -{ - const QDictEntry *entry, *next; - const char *p; - - *dst = qdict_new(); - entry = qdict_first(src); - - while (entry != NULL) { - next = qdict_next(src, entry); - if (strstart(entry->key, start, &p)) { - qobject_incref(entry->value); - qdict_put_obj(*dst, p, entry->value); - qdict_del(src, entry->key); - } - entry = next; - } -} - /* * Opens a disk image (raw, qcow2, vmdk, ...) * @@ -990,7 +1028,7 @@ static void extract_subqdict(QDict *src, QDict **dst, const char *start) * dictionary, it needs to use QINCREF() before calling bdrv_open. */ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, - int flags, BlockDriver *drv) + int flags, BlockDriver *drv, Error **errp) { int ret; /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */ @@ -998,6 +1036,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, BlockDriverState *file = NULL; QDict *file_options = NULL; const char *drvname; + Error *local_err = NULL; /* NULL means an empty set of options */ if (options == NULL) { @@ -1016,7 +1055,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, char backing_filename[PATH_MAX]; if (qdict_size(options) != 0) { - error_report("Can't use snapshot=on with driver-specific options"); + error_setg(errp, "Can't use snapshot=on with driver-specific options"); ret = -EINVAL; goto fail; } @@ -1027,7 +1066,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, /* if there is a backing file, use it */ bs1 = bdrv_new(""); - ret = bdrv_open(bs1, filename, NULL, 0, drv); + ret = bdrv_open(bs1, filename, NULL, 0, drv, &local_err); if (ret < 0) { bdrv_unref(bs1); goto fail; @@ -1038,6 +1077,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, ret = get_tmp_filename(tmp_filename, sizeof(tmp_filename)); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not get temporary filename"); goto fail; } @@ -1047,6 +1087,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, "%s", filename); } else if (!realpath(filename, backing_filename)) { ret = -errno; + error_setg_errno(errp, errno, "Could not resolve path '%s'", filename); goto fail; } @@ -1062,9 +1103,14 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, drv->format_name); } - ret = bdrv_create(bdrv_qcow2, tmp_filename, create_options); + ret = bdrv_create(bdrv_qcow2, tmp_filename, create_options, &local_err); free_option_parameters(create_options); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not create temporary overlay " + "'%s': %s", tmp_filename, + error_get_pretty(local_err)); + error_free(local_err); + local_err = NULL; goto fail; } @@ -1078,10 +1124,10 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, flags |= BDRV_O_ALLOW_RDWR; } - extract_subqdict(options, &file_options, "file."); + qdict_extract_subqdict(options, &file_options, "file."); ret = bdrv_file_open(&file, filename, file_options, - bdrv_open_flags(bs, flags | BDRV_O_UNMAP)); + bdrv_open_flags(bs, flags | BDRV_O_UNMAP), &local_err); if (ret < 0) { goto fail; } @@ -1089,12 +1135,17 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, /* Find the right image format driver */ drvname = qdict_get_try_str(options, "driver"); if (drvname) { - drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR)); + drv = bdrv_find_format(drvname); qdict_del(options, "driver"); + if (!drv) { + error_setg(errp, "Invalid driver: '%s'", drvname); + ret = -EINVAL; + goto unlink_and_fail; + } } if (!drv) { - ret = find_image_format(file, filename, &drv); + ret = find_image_format(file, filename, &drv, &local_err); } if (!drv) { @@ -1102,7 +1153,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, } /* Open the image */ - ret = bdrv_open_common(bs, file, options, flags, drv); + ret = bdrv_open_common(bs, file, options, flags, drv, &local_err); if (ret < 0) { goto unlink_and_fail; } @@ -1116,8 +1167,8 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, if ((flags & BDRV_O_NO_BACKING) == 0) { QDict *backing_options; - extract_subqdict(options, &backing_options, "backing."); - ret = bdrv_open_backing_file(bs, backing_options); + qdict_extract_subqdict(options, &backing_options, "backing."); + ret = bdrv_open_backing_file(bs, backing_options, &local_err); if (ret < 0) { goto close_and_fail; } @@ -1126,9 +1177,9 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, /* Check if any unknown options were used */ if (qdict_size(options) != 0) { const QDictEntry *entry = qdict_first(options); - qerror_report(ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by " - "device '%s' doesn't support the option '%s'", - drv->format_name, bs->device_name, entry->key); + error_setg(errp, "Block format '%s' used by device '%s' doesn't " + "support the option '%s'", drv->format_name, bs->device_name, + entry->key); ret = -EINVAL; goto close_and_fail; @@ -1152,11 +1203,17 @@ fail: QDECREF(bs->options); QDECREF(options); bs->options = NULL; + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + } return ret; close_and_fail: bdrv_close(bs); QDECREF(options); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + } return ret; } @@ -2613,7 +2670,7 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs, goto out; } - total_sectors = len >> BDRV_SECTOR_BITS; + total_sectors = DIV_ROUND_UP(len, BDRV_SECTOR_SIZE); max_nb_sectors = MAX(0, total_sectors - sector_num); if (max_nb_sectors > 0) { ret = drv->bdrv_co_readv(bs, sector_num, @@ -2819,9 +2876,10 @@ int64_t bdrv_getlength(BlockDriverState *bs) if (!drv) return -ENOMEDIUM; - if (bdrv_dev_has_removable_media(bs)) { - if (drv->bdrv_getlength) { - return drv->bdrv_getlength(bs); + if (drv->has_variable_length) { + int ret = refresh_total_sectors(bs, bs->total_sectors); + if (ret < 0) { + return ret; } } return bs->total_sectors * BDRV_SECTOR_SIZE; @@ -3031,6 +3089,11 @@ int bdrv_has_zero_init(BlockDriverState *bs) { assert(bs->drv); + /* If BS is a copy on write image, it is initialized to + the contents of the base image, which may not be zeroes. */ + if (bs->backing_hd) { + return 0; + } if (bs->drv->bdrv_has_zero_init) { return bs->drv->bdrv_has_zero_init(bs); } @@ -3039,15 +3102,15 @@ int bdrv_has_zero_init(BlockDriverState *bs) return 0; } -typedef struct BdrvCoIsAllocatedData { +typedef struct BdrvCoGetBlockStatusData { BlockDriverState *bs; BlockDriverState *base; int64_t sector_num; int nb_sectors; int *pnum; - int ret; + int64_t ret; bool done; -} BdrvCoIsAllocatedData; +} BdrvCoGetBlockStatusData; /* * Returns true iff the specified sector is present in the disk image. Drivers @@ -3064,12 +3127,13 @@ typedef struct BdrvCoIsAllocatedData { * 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes * beyond the end of the disk image it will be clamped. */ -static int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, - int64_t sector_num, - int nb_sectors, int *pnum) +static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, int *pnum) { int64_t length; int64_t n; + int64_t ret, ret2; length = bdrv_getlength(bs); if (length < 0) { @@ -3086,35 +3150,76 @@ static int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, nb_sectors = n; } - if (!bs->drv->bdrv_co_is_allocated) { + if (!bs->drv->bdrv_co_get_block_status) { *pnum = nb_sectors; - return 1; + ret = BDRV_BLOCK_DATA; + if (bs->drv->protocol_name) { + ret |= BDRV_BLOCK_OFFSET_VALID | (sector_num * BDRV_SECTOR_SIZE); + } + return ret; + } + + ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum); + if (ret < 0) { + *pnum = 0; + return ret; + } + + if (ret & BDRV_BLOCK_RAW) { + assert(ret & BDRV_BLOCK_OFFSET_VALID); + return bdrv_get_block_status(bs->file, ret >> BDRV_SECTOR_BITS, + *pnum, pnum); + } + + if (!(ret & BDRV_BLOCK_DATA)) { + if (bdrv_has_zero_init(bs)) { + ret |= BDRV_BLOCK_ZERO; + } else if (bs->backing_hd) { + BlockDriverState *bs2 = bs->backing_hd; + int64_t length2 = bdrv_getlength(bs2); + if (length2 >= 0 && sector_num >= (length2 >> BDRV_SECTOR_BITS)) { + ret |= BDRV_BLOCK_ZERO; + } + } + } + + if (bs->file && + (ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO) && + (ret & BDRV_BLOCK_OFFSET_VALID)) { + ret2 = bdrv_co_get_block_status(bs->file, ret >> BDRV_SECTOR_BITS, + *pnum, pnum); + if (ret2 >= 0) { + /* Ignore errors. This is just providing extra information, it + * is useful but not necessary. + */ + ret |= (ret2 & BDRV_BLOCK_ZERO); + } } - return bs->drv->bdrv_co_is_allocated(bs, sector_num, nb_sectors, pnum); + return ret; } -/* Coroutine wrapper for bdrv_is_allocated() */ -static void coroutine_fn bdrv_is_allocated_co_entry(void *opaque) +/* Coroutine wrapper for bdrv_get_block_status() */ +static void coroutine_fn bdrv_get_block_status_co_entry(void *opaque) { - BdrvCoIsAllocatedData *data = opaque; + BdrvCoGetBlockStatusData *data = opaque; BlockDriverState *bs = data->bs; - data->ret = bdrv_co_is_allocated(bs, data->sector_num, data->nb_sectors, - data->pnum); + data->ret = bdrv_co_get_block_status(bs, data->sector_num, data->nb_sectors, + data->pnum); data->done = true; } /* - * Synchronous wrapper around bdrv_co_is_allocated(). + * Synchronous wrapper around bdrv_co_get_block_status(). * - * See bdrv_co_is_allocated() for details. + * See bdrv_co_get_block_status() for details. */ -int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, - int *pnum) +int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum) { Coroutine *co; - BdrvCoIsAllocatedData data = { + BdrvCoGetBlockStatusData data = { .bs = bs, .sector_num = sector_num, .nb_sectors = nb_sectors, @@ -3124,9 +3229,9 @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, if (qemu_in_coroutine()) { /* Fast-path if already in coroutine context */ - bdrv_is_allocated_co_entry(&data); + bdrv_get_block_status_co_entry(&data); } else { - co = qemu_coroutine_create(bdrv_is_allocated_co_entry); + co = qemu_coroutine_create(bdrv_get_block_status_co_entry); qemu_coroutine_enter(co, &data); while (!data.done) { qemu_aio_wait(); @@ -3135,6 +3240,18 @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, return data.ret; } +int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum) +{ + int64_t ret = bdrv_get_block_status(bs, sector_num, nb_sectors, pnum); + if (ret < 0) { + return ret; + } + return + (ret & BDRV_BLOCK_DATA) || + ((ret & BDRV_BLOCK_ZERO) && !bdrv_has_zero_init(bs)); +} + /* * Given an image chain: ... -> [BASE] -> [INTER1] -> [INTER2] -> [TOP] * @@ -3229,6 +3346,15 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return drv->bdrv_get_info(bs, bdi); } +ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + if (drv && drv->bdrv_get_specific_info) { + return drv->bdrv_get_specific_info(bs); + } + return NULL; +} + int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, int64_t pos, int size) { @@ -4381,6 +4507,7 @@ void bdrv_img_create(const char *filename, const char *fmt, BlockDriverState *bs = NULL; BlockDriver *drv, *proto_drv; BlockDriver *backing_drv = NULL; + Error *local_err = NULL; int ret = 0; /* Find driver and parse its options */ @@ -4467,10 +4594,13 @@ void bdrv_img_create(const char *filename, const char *fmt, bs = bdrv_new(""); ret = bdrv_open(bs, backing_file->value.s, NULL, back_flags, - backing_drv); + backing_drv, &local_err); if (ret < 0) { - error_setg_errno(errp, -ret, "Could not open '%s'", - backing_file->value.s); + error_setg_errno(errp, -ret, "Could not open '%s': %s", + backing_file->value.s, + error_get_pretty(local_err)); + error_free(local_err); + local_err = NULL; goto out; } bdrv_get_geometry(bs, &size); @@ -4489,22 +4619,19 @@ void bdrv_img_create(const char *filename, const char *fmt, print_option_parameters(param); puts(""); } - ret = bdrv_create(drv, filename, param); - if (ret < 0) { - if (ret == -ENOTSUP) { - error_setg(errp,"Formatting or formatting option not supported for " - "file format '%s'", fmt); - } else if (ret == -EFBIG) { - const char *cluster_size_hint = ""; - if (get_option_parameter(create_options, BLOCK_OPT_CLUSTER_SIZE)) { - cluster_size_hint = " (try using a larger cluster size)"; - } - error_setg(errp, "The image size is too large for file format '%s'%s", - fmt, cluster_size_hint); - } else { - error_setg(errp, "%s: error while creating %s: %s", filename, fmt, - strerror(-ret)); + ret = bdrv_create(drv, filename, param, &local_err); + if (ret == -EFBIG) { + /* This is generally a better message than whatever the driver would + * deliver (especially because of the cluster_size_hint), since that + * is most probably not much different from "image too large". */ + const char *cluster_size_hint = ""; + if (get_option_parameter(create_options, BLOCK_OPT_CLUSTER_SIZE)) { + cluster_size_hint = " (try using a larger cluster size)"; } + error_setg(errp, "The image size is too large for file format '%s'" + "%s", fmt, cluster_size_hint); + error_free(local_err); + local_err = NULL; } out: @@ -4514,6 +4641,9 @@ out: if (bs) { bdrv_unref(bs); } + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + } } AioContext *bdrv_get_aio_context(BlockDriverState *bs) @@ -4527,3 +4657,30 @@ void bdrv_add_before_write_notifier(BlockDriverState *bs, { notifier_with_return_list_add(&bs->before_write_notifiers, notifier); } + +int bdrv_amend_options(BlockDriverState *bs, QEMUOptionParameter *options) +{ + if (bs->drv->bdrv_amend_options == NULL) { + return -ENOTSUP; + } + return bs->drv->bdrv_amend_options(bs, options); +} + +ExtSnapshotPerm bdrv_check_ext_snapshot(BlockDriverState *bs) +{ + if (bs->drv->bdrv_check_ext_snapshot) { + return bs->drv->bdrv_check_ext_snapshot(bs); + } + + if (bs->file && bs->file->drv && bs->file->drv->bdrv_check_ext_snapshot) { + return bs->file->drv->bdrv_check_ext_snapshot(bs); + } + + /* external snapshots are allowed by default */ + return EXT_SNAPSHOT_ALLOWED; +} + +ExtSnapshotPerm bdrv_check_ext_snapshot_forbidden(BlockDriverState *bs) +{ + return EXT_SNAPSHOT_FORBIDDEN; +}