diff --git a/block.c b/block.c index 636aa117c3099efc28dea7144d0d47d01e64ac41..8f718f9f29b1482647e956aa03636af9d1571eb4 100644 --- a/block.c +++ b/block.c @@ -796,6 +796,13 @@ static int bdrv_assign_node_name(BlockDriverState *bs, return -EINVAL; } + /* takes care of avoiding namespaces collisions */ + if (bdrv_find(node_name)) { + error_setg(errp, "node-name=%s is conflicting with a device id", + node_name); + return -EINVAL; + } + /* takes care of avoiding duplicates node names */ if (bdrv_find_node(node_name)) { error_setg(errp, "Duplicate node name"); @@ -977,9 +984,8 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, } QDECREF(options); - bs = bdrv_find(reference); + bs = bdrv_lookup_bs(reference, reference, errp); if (!bs) { - error_setg(errp, "Cannot find block device '%s'", reference); return -ENODEV; } bdrv_ref(bs); @@ -3574,30 +3580,26 @@ BlockDriverState *bdrv_lookup_bs(const char *device, { BlockDriverState *bs = NULL; - if ((!device && !node_name) || (device && node_name)) { - error_setg(errp, "Use either device or node-name but not both"); - return NULL; - } - if (device) { bs = bdrv_find(device); - if (!bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, device); - return NULL; + if (bs) { + return bs; } - - return bs; } - bs = bdrv_find_node(node_name); + if (node_name) { + bs = bdrv_find_node(node_name); - if (!bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, node_name); - return NULL; + if (bs) { + return bs; + } } - return bs; + error_setg(errp, "Cannot find device=%s nor node_name=%s", + device ? device : "", + node_name ? node_name : ""); + return NULL; } BlockDriverState *bdrv_next(BlockDriverState *bs) diff --git a/block/iscsi.c b/block/iscsi.c index 8d0f9667c507f3fe0d5718ee329e1c9096ece572..c97c04060dca61681a8ef7814ecbf62a742d81a5 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -1099,6 +1099,10 @@ fail: /* * We support iscsi url's on the form * iscsi://[%@][:]// + * + * Note: flags are currently not used by iscsi_open. If this function + * is changed such that flags are used, please examine iscsi_reopen_prepare() + * to see if needs to be changed as well. */ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) @@ -1336,11 +1340,13 @@ static int iscsi_refresh_limits(BlockDriverState *bs) return 0; } -/* We have nothing to do for iSCSI reopen, stub just returns - * success */ +/* Since iscsi_open() ignores bdrv_flags, there is nothing to do here in + * prepare. Note that this will not re-establish a connection with an iSCSI + * target - it is effectively a NOP. */ static int iscsi_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue, Error **errp) { + /* NOP */ return 0; } diff --git a/block/mirror.c b/block/mirror.c index 2a4333474e1463dec7517252bdde52de907fcdee..e683959570610401d2c07c98ee51700d15f59f8a 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -633,6 +633,8 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base, { int64_t length, base_length; int orig_base_flags; + int ret; + Error *local_err = NULL; orig_base_flags = bdrv_get_flags(base); @@ -642,19 +644,23 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base, length = bdrv_getlength(bs); if (length < 0) { - error_setg(errp, "Unable to determine length of %s", bs->filename); + error_setg_errno(errp, -length, + "Unable to determine length of %s", bs->filename); goto error_restore_flags; } base_length = bdrv_getlength(base); if (base_length < 0) { - error_setg(errp, "Unable to determine length of %s", base->filename); + error_setg_errno(errp, -base_length, + "Unable to determine length of %s", base->filename); goto error_restore_flags; } if (length > base_length) { - if (bdrv_truncate(base, length) < 0) { - error_setg(errp, "Top image %s is larger than base image %s, and " + ret = bdrv_truncate(base, length); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Top image %s is larger than base image %s, and " "resize of base image failed", bs->filename, base->filename); goto error_restore_flags; @@ -663,9 +669,10 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base, bdrv_ref(base); mirror_start_job(bs, base, speed, 0, 0, - on_error, on_error, cb, opaque, errp, + on_error, on_error, cb, opaque, &local_err, &commit_active_job_driver, false, base); - if (error_is_set(errp)) { + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); goto error_restore_flags; } diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index ad8bf3dcd983f1c297e0b1b076ec10328333b9ee..2fc6320aa18be9fe4195ab21d9e646799e7736a1 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -606,7 +606,8 @@ int qcow2_snapshot_delete(BlockDriverState *bs, s->nb_snapshots--; ret = qcow2_write_snapshots(bs); if (ret < 0) { - error_setg(errp, "Failed to remove snapshot from snapshot list"); + error_setg_errno(errp, -ret, + "Failed to remove snapshot from snapshot list"); return ret; } @@ -624,7 +625,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset, sn.l1_size, -1); if (ret < 0) { - error_setg(errp, "Failed to free the cluster and L1 table"); + error_setg_errno(errp, -ret, "Failed to free the cluster and L1 table"); return ret; } qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t), @@ -633,7 +634,8 @@ int qcow2_snapshot_delete(BlockDriverState *bs, /* must update the copied flag on the current cluster offsets */ ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); if (ret < 0) { - error_setg(errp, "Failed to update snapshot status in disk"); + error_setg_errno(errp, -ret, + "Failed to update snapshot status in disk"); return ret; } diff --git a/block/vmdk.c b/block/vmdk.c index e809e2ef4645c8fca4c83a35b8ab30b9d6494286..ff6f5ee911e77319395cbd11ff1ebaea5e2fe9ed 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1502,7 +1502,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize, if (flat) { ret = bdrv_truncate(bs, filesize); if (ret < 0) { - error_setg(errp, "Could not truncate file"); + error_setg_errno(errp, -ret, "Could not truncate file"); } goto exit; } @@ -1562,7 +1562,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize, ret = bdrv_truncate(bs, le64_to_cpu(header.grain_offset) << 9); if (ret < 0) { - error_setg(errp, "Could not truncate file"); + error_setg_errno(errp, -ret, "Could not truncate file"); goto exit; } @@ -1846,7 +1846,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options, if (desc_offset == 0) { ret = bdrv_truncate(new_bs, desc_len); if (ret < 0) { - error_setg(errp, "Could not truncate file"); + error_setg_errno(errp, -ret, "Could not truncate file"); } } exit: diff --git a/blockdev.c b/blockdev.c index 36ceece9ff8d148adabd24868e0e72c8fa9ec0e1..dfb5ec7529e57ca1a6b15a574aae0535a4d3d74f 100644 --- a/blockdev.c +++ b/blockdev.c @@ -308,7 +308,6 @@ typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType; /* Takes the ownership of bs_opts */ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts, - BlockInterfaceType type, Error **errp) { const char *buf; @@ -437,11 +436,6 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts, on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; if ((buf = qemu_opt_get(opts, "werror")) != NULL) { - if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) { - error_setg(errp, "werror is not supported by this bus type"); - goto early_err; - } - on_write_error = parse_block_error_action(buf, 0, &error); if (error_is_set(&error)) { error_propagate(errp, error); @@ -451,11 +445,6 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts, on_read_error = BLOCKDEV_ON_ERROR_REPORT; if ((buf = qemu_opt_get(opts, "rerror")) != NULL) { - if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) { - error_report("rerror is not supported by this bus type"); - goto early_err; - } - on_read_error = parse_block_error_action(buf, 1, &error); if (error_is_set(&error)) { error_propagate(errp, error); @@ -463,13 +452,18 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts, } } + if (bdrv_find_node(qemu_opts_id(opts))) { + error_setg(errp, "device id=%s is conflicting with a node-name", + qemu_opts_id(opts)); + goto early_err; + } + /* init */ dinfo = g_malloc0(sizeof(*dinfo)); dinfo->id = g_strdup(qemu_opts_id(opts)); dinfo->bdrv = bdrv_new(dinfo->id); dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0; dinfo->bdrv->read_only = ro; - dinfo->type = type; dinfo->refcount = 1; if (serial != NULL) { dinfo->serial = g_strdup(serial); @@ -608,6 +602,14 @@ QemuOptsList qemu_legacy_drive_opts = { .name = "read-only", .type = QEMU_OPT_BOOL, .help = "open drive file as read-only", + },{ + .name = "rerror", + .type = QEMU_OPT_STRING, + .help = "read error action", + },{ + .name = "werror", + .type = QEMU_OPT_STRING, + .help = "write error action", },{ .name = "copy-on-read", .type = QEMU_OPT_BOOL, @@ -629,6 +631,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type) int cyls, heads, secs, translation; int max_devs, bus_id, unit_id, index; const char *devaddr; + const char *werror, *rerror; bool read_only = false; bool copy_on_read; const char *filename; @@ -872,8 +875,29 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type) filename = qemu_opt_get(legacy_opts, "file"); + /* Check werror/rerror compatibility with if=... */ + werror = qemu_opt_get(legacy_opts, "werror"); + if (werror != NULL) { + if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && + type != IF_NONE) { + error_report("werror is not supported by this bus type"); + goto fail; + } + qdict_put(bs_opts, "werror", qstring_from_str(werror)); + } + + rerror = qemu_opt_get(legacy_opts, "rerror"); + if (rerror != NULL) { + if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && + type != IF_NONE) { + error_report("rerror is not supported by this bus type"); + goto fail; + } + qdict_put(bs_opts, "rerror", qstring_from_str(rerror)); + } + /* Actual block device init: Functionality shared with blockdev-add */ - dinfo = blockdev_init(filename, bs_opts, type, &local_err); + dinfo = blockdev_init(filename, bs_opts, &local_err); if (dinfo == NULL) { if (error_is_set(&local_err)) { qerror_report_err(local_err); @@ -893,6 +917,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type) dinfo->secs = secs; dinfo->trans = translation; + dinfo->type = type; dinfo->bus = bus_id; dinfo->unit = unit_id; dinfo->devaddr = devaddr; @@ -1310,8 +1335,6 @@ static void external_snapshot_prepare(BlkTransactionState *common, if (ret != 0) { error_propagate(errp, local_err); } - - QDECREF(options); } static void external_snapshot_commit(BlkTransactionState *common) @@ -2276,7 +2299,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) qdict_flatten(qdict); - blockdev_init(NULL, qdict, IF_NONE, &local_err); + blockdev_init(NULL, qdict, &local_err); if (error_is_set(&local_err)) { error_propagate(errp, local_err); goto fail; diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 0906a1d62b25ba0480d0a36a54ea7e4676272801..a0b90baf6c3a610c83ac3adb116499f2fb27a686 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -26,7 +26,6 @@ #include "sysemu/blockdev.h" #include "sysemu/dma.h" #include "qemu/timer.h" -#include "block/block_int.h" #include "qemu/bitops.h" #include "sdhci.h" diff --git a/tests/qemu-iotests/005 b/tests/qemu-iotests/005 index 9abcb84e4b7e47cc82d8b71ba332907093232c04..ba1236dfbfaaadca4cb39d860c23a69d67b8de34 100755 --- a/tests/qemu-iotests/005 +++ b/tests/qemu-iotests/005 @@ -44,6 +44,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt generic _supported_proto generic _supported_os Linux +_unsupported_imgopts "subformat=twoGbMaxExtentFlat" \ + "subformat=twoGbMaxExtentSparse" # vpc is limited to 127GB, so we can't test it here if [ "$IMGFMT" = "vpc" ]; then diff --git a/tests/qemu-iotests/070 b/tests/qemu-iotests/070 index 41bf1007016e925586e66f1c183ad64b8d46fc88..ce71fa4a225d955e1e1c1b1bf68da4dc41105f4f 100755 --- a/tests/qemu-iotests/070 +++ b/tests/qemu-iotests/070 @@ -56,11 +56,22 @@ _use_sample_img iotest-dirtylog-10G-4M.vhdx.bz2 echo echo "=== Verify open image read-only fails, due to dirty log ===" -$QEMU_IO -r -c "read -pP 0xa5 0 18M" "$TEST_IMG" 2>&1 | grep -o "Permission denied" +$QEMU_IO -r -c "read -pP 0xa5 0 18M" "$TEST_IMG" 2>&1 | _filter_testdir \ + | _filter_qemu_io echo "=== Verify open image replays log ===" $QEMU_IO -c "read -pP 0xa5 0 18M" "$TEST_IMG" | _filter_qemu_io +# extract fresh sample image again +_use_sample_img iotest-dirtylog-10G-4M.vhdx.bz2 + +echo "=== Verify qemu-img check -r all replays log ===" +$QEMU_IMG check -r all "$TEST_IMG" 2>&1 | _filter_testdir | _filter_qemu + +echo "=== Verify open image read-only succeeds after log replay ===" +$QEMU_IO -r -c "read -pP 0xa5 0 18M" "$TEST_IMG" 2>&1 | _filter_testdir \ + | _filter_qemu_io + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/070.out b/tests/qemu-iotests/070.out index 9db8ff2650d3582be496bcb772f2221c6d15bdd9..922d62cb516c416c9d98d6478eeb850e69ccd2d9 100644 --- a/tests/qemu-iotests/070.out +++ b/tests/qemu-iotests/070.out @@ -1,8 +1,21 @@ QA output created by 070 === Verify open image read-only fails, due to dirty log === -Permission denied +qemu-io: can't open device TEST_DIR/iotest-dirtylog-10G-4M.vhdx: VHDX image file 'TEST_DIR/iotest-dirtylog-10G-4M.vhdx' opened read-only, but contains a log that needs to be replayed. To replay the log, execute: + qemu-img check -r all 'TEST_DIR/iotest-dirtylog-10G-4M.vhdx': Operation not permitted + no file open, try 'help open' === Verify open image replays log === read 18874368/18874368 bytes at offset 0 18 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +=== Verify qemu-img check -r all replays log === +The following inconsistencies were found and repaired: + + 0 leaked clusters + 1 corruptions + +Double checking the fixed image now... +No errors were found on the image. +=== Verify open image read-only succeeds after log replay === +read 18874368/18874368 bytes at offset 0 +18 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done