Block patches:

- Various bug fixes
 - Removal of qemu-img convert's deprecated -s option
 - qemu-io now exits with an error when a command failed
 -----BEGIN PGP SIGNATURE-----
 
 iQEcBAABAgAGBQJbHoXuAAoJEPQH2wBh1c9AyDYIAJ4jp0Uw4Taw9L6qOM/J44Ui
 g6HNujGZoSHejRemiXHfKVozHHbkkNje0UVJJH7lKAPdPy1BtoThAXNfoqSP6mMg
 frsOVMjGJSzPxodmGlFRgnQXNJKWsxrXWJgrGCygwEAlDPnlJxqPpMgHP49H+0QF
 XHWUGPLUebHTdz5LaLOMfkY3e6hFw9EgrlSOsKI3J0ik1kpE2A/+kplAjX54eA7d
 zMyrVQUklYiGYN96QGqWqQgGLMJ1XmCYzs8Rx7fFhdBwZ+6Baana2RbOTfDN/tKr
 HtzDrKwJx3QaawoyBL6WCcoUzXdE868MnqwZ+SriIYna9kPgxB5tFVmdfIbhgCQ=
 =HVjp
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2018-06-11' into staging

Block patches:
- Various bug fixes
- Removal of qemu-img convert's deprecated -s option
- qemu-io now exits with an error when a command failed

# gpg: Signature made Mon 11 Jun 2018 15:23:42 BST
# gpg:                using RSA key F407DB0061D5CF40
# gpg: Good signature from "Max Reitz <mreitz@redhat.com>"
# Primary key fingerprint: 91BE B60A 30DB 3E88 57D1  1829 F407 DB00 61D5 CF40

* remotes/maxreitz/tags/pull-block-2018-06-11: (29 commits)
  iotests: Add case for a corrupted inactive image
  qcow2: Do not mark inactive images corrupt
  block: Make bdrv_is_writable() public
  throttle: Fix crash on reopen
  block/qcow2-bitmap: fix free_bitmap_clusters
  qemu-img: Remove deprecated -s snapshot_id_or_name option
  iotests: Fix 219's timing
  iotests: improve pause_job
  iotests: Test post-backing convert target behavior
  qemu-img: Special post-backing convert handling
  iotests: Add test for rebasing with relative paths
  qemu-img: Resolve relative backing paths in rebase
  iotests: Let 216 make use of qemu-io's exit code
  iotests.py: Add qemu_io_silent
  qemu-io: Exit with error when a command failed
  qemu-io: Let command functions return error code
  qemu-io: Drop command functions' return values
  iotests: Repairing error during snapshot deletion
  qcow2: Repair OFLAG_COPIED when fixing leaks
  iotests: Rework 113
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-06-11 15:31:20 +01:00
commit 2afc4e3df8
41 changed files with 970 additions and 342 deletions

25
block.c
View File

@ -1620,13 +1620,24 @@ static int bdrv_reopen_get_flags(BlockReopenQueue *q, BlockDriverState *bs)
/* Returns whether the image file can be written to after the reopen queue @q /* Returns whether the image file can be written to after the reopen queue @q
* has been successfully applied, or right now if @q is NULL. */ * has been successfully applied, or right now if @q is NULL. */
static bool bdrv_is_writable(BlockDriverState *bs, BlockReopenQueue *q) static bool bdrv_is_writable_after_reopen(BlockDriverState *bs,
BlockReopenQueue *q)
{ {
int flags = bdrv_reopen_get_flags(q, bs); int flags = bdrv_reopen_get_flags(q, bs);
return (flags & (BDRV_O_RDWR | BDRV_O_INACTIVE)) == BDRV_O_RDWR; return (flags & (BDRV_O_RDWR | BDRV_O_INACTIVE)) == BDRV_O_RDWR;
} }
/*
* Return whether the BDS can be written to. This is not necessarily
* the same as !bdrv_is_read_only(bs), as inactivated images may not
* be written to but do not count as read-only images.
*/
bool bdrv_is_writable(BlockDriverState *bs)
{
return bdrv_is_writable_after_reopen(bs, NULL);
}
static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
BdrvChild *c, const BdrvChildRole *role, BdrvChild *c, const BdrvChildRole *role,
BlockReopenQueue *reopen_queue, BlockReopenQueue *reopen_queue,
@ -1664,7 +1675,7 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
/* Write permissions never work with read-only images */ /* Write permissions never work with read-only images */
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) && if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
!bdrv_is_writable(bs, q)) !bdrv_is_writable_after_reopen(bs, q))
{ {
error_setg(errp, "Block node is read-only"); error_setg(errp, "Block node is read-only");
return -EPERM; return -EPERM;
@ -1956,7 +1967,7 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
&perm, &shared); &perm, &shared);
/* Format drivers may touch metadata even if the guest doesn't write */ /* Format drivers may touch metadata even if the guest doesn't write */
if (bdrv_is_writable(bs, reopen_queue)) { if (bdrv_is_writable_after_reopen(bs, reopen_queue)) {
perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE; perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
} }
@ -4996,15 +5007,19 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs,
} }
int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb, void *cb_opaque) BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
Error **errp)
{ {
if (!bs->drv) { if (!bs->drv) {
error_setg(errp, "Node is ejected");
return -ENOMEDIUM; return -ENOMEDIUM;
} }
if (!bs->drv->bdrv_amend_options) { if (!bs->drv->bdrv_amend_options) {
error_setg(errp, "Block driver '%s' does not support option amendment",
bs->drv->format_name);
return -ENOTSUP; return -ENOTSUP;
} }
return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque); return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp);
} }
/* This function will be called by the bdrv_recurse_is_first_non_filter method /* This function will be called by the bdrv_recurse_is_first_non_filter method

View File

@ -643,7 +643,7 @@ typedef enum {
* file; if @unlock == true, also unlock the unneeded bytes. * file; if @unlock == true, also unlock the unneeded bytes.
* @shared_perm_lock_bits is the mask of all permissions that are NOT shared. * @shared_perm_lock_bits is the mask of all permissions that are NOT shared.
*/ */
static int raw_apply_lock_bytes(BDRVRawState *s, static int raw_apply_lock_bytes(int fd,
uint64_t perm_lock_bits, uint64_t perm_lock_bits,
uint64_t shared_perm_lock_bits, uint64_t shared_perm_lock_bits,
bool unlock, Error **errp) bool unlock, Error **errp)
@ -654,13 +654,13 @@ static int raw_apply_lock_bytes(BDRVRawState *s,
PERM_FOREACH(i) { PERM_FOREACH(i) {
int off = RAW_LOCK_PERM_BASE + i; int off = RAW_LOCK_PERM_BASE + i;
if (perm_lock_bits & (1ULL << i)) { if (perm_lock_bits & (1ULL << i)) {
ret = qemu_lock_fd(s->lock_fd, off, 1, false); ret = qemu_lock_fd(fd, off, 1, false);
if (ret) { if (ret) {
error_setg(errp, "Failed to lock byte %d", off); error_setg(errp, "Failed to lock byte %d", off);
return ret; return ret;
} }
} else if (unlock) { } else if (unlock) {
ret = qemu_unlock_fd(s->lock_fd, off, 1); ret = qemu_unlock_fd(fd, off, 1);
if (ret) { if (ret) {
error_setg(errp, "Failed to unlock byte %d", off); error_setg(errp, "Failed to unlock byte %d", off);
return ret; return ret;
@ -670,13 +670,13 @@ static int raw_apply_lock_bytes(BDRVRawState *s,
PERM_FOREACH(i) { PERM_FOREACH(i) {
int off = RAW_LOCK_SHARED_BASE + i; int off = RAW_LOCK_SHARED_BASE + i;
if (shared_perm_lock_bits & (1ULL << i)) { if (shared_perm_lock_bits & (1ULL << i)) {
ret = qemu_lock_fd(s->lock_fd, off, 1, false); ret = qemu_lock_fd(fd, off, 1, false);
if (ret) { if (ret) {
error_setg(errp, "Failed to lock byte %d", off); error_setg(errp, "Failed to lock byte %d", off);
return ret; return ret;
} }
} else if (unlock) { } else if (unlock) {
ret = qemu_unlock_fd(s->lock_fd, off, 1); ret = qemu_unlock_fd(fd, off, 1);
if (ret) { if (ret) {
error_setg(errp, "Failed to unlock byte %d", off); error_setg(errp, "Failed to unlock byte %d", off);
return ret; return ret;
@ -687,8 +687,7 @@ static int raw_apply_lock_bytes(BDRVRawState *s,
} }
/* Check "unshared" bytes implied by @perm and ~@shared_perm in the file. */ /* Check "unshared" bytes implied by @perm and ~@shared_perm in the file. */
static int raw_check_lock_bytes(BDRVRawState *s, static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm,
uint64_t perm, uint64_t shared_perm,
Error **errp) Error **errp)
{ {
int ret; int ret;
@ -698,7 +697,7 @@ static int raw_check_lock_bytes(BDRVRawState *s,
int off = RAW_LOCK_SHARED_BASE + i; int off = RAW_LOCK_SHARED_BASE + i;
uint64_t p = 1ULL << i; uint64_t p = 1ULL << i;
if (perm & p) { if (perm & p) {
ret = qemu_lock_fd_test(s->lock_fd, off, 1, true); ret = qemu_lock_fd_test(fd, off, 1, true);
if (ret) { if (ret) {
char *perm_name = bdrv_perm_names(p); char *perm_name = bdrv_perm_names(p);
error_setg(errp, error_setg(errp,
@ -715,7 +714,7 @@ static int raw_check_lock_bytes(BDRVRawState *s,
int off = RAW_LOCK_PERM_BASE + i; int off = RAW_LOCK_PERM_BASE + i;
uint64_t p = 1ULL << i; uint64_t p = 1ULL << i;
if (!(shared_perm & p)) { if (!(shared_perm & p)) {
ret = qemu_lock_fd_test(s->lock_fd, off, 1, true); ret = qemu_lock_fd_test(fd, off, 1, true);
if (ret) { if (ret) {
char *perm_name = bdrv_perm_names(p); char *perm_name = bdrv_perm_names(p);
error_setg(errp, error_setg(errp,
@ -752,11 +751,11 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
switch (op) { switch (op) {
case RAW_PL_PREPARE: case RAW_PL_PREPARE:
ret = raw_apply_lock_bytes(s, s->perm | new_perm, ret = raw_apply_lock_bytes(s->lock_fd, s->perm | new_perm,
~s->shared_perm | ~new_shared, ~s->shared_perm | ~new_shared,
false, errp); false, errp);
if (!ret) { if (!ret) {
ret = raw_check_lock_bytes(s, new_perm, new_shared, errp); ret = raw_check_lock_bytes(s->lock_fd, new_perm, new_shared, errp);
if (!ret) { if (!ret) {
return 0; return 0;
} }
@ -764,7 +763,8 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
op = RAW_PL_ABORT; op = RAW_PL_ABORT;
/* fall through to unlock bytes. */ /* fall through to unlock bytes. */
case RAW_PL_ABORT: case RAW_PL_ABORT:
raw_apply_lock_bytes(s, s->perm, ~s->shared_perm, true, &local_err); raw_apply_lock_bytes(s->lock_fd, s->perm, ~s->shared_perm,
true, &local_err);
if (local_err) { if (local_err) {
/* Theoretically the above call only unlocks bytes and it cannot /* Theoretically the above call only unlocks bytes and it cannot
* fail. Something weird happened, report it. * fail. Something weird happened, report it.
@ -773,7 +773,8 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
} }
break; break;
case RAW_PL_COMMIT: case RAW_PL_COMMIT:
raw_apply_lock_bytes(s, new_perm, ~new_shared, true, &local_err); raw_apply_lock_bytes(s->lock_fd, new_perm, ~new_shared,
true, &local_err);
if (local_err) { if (local_err) {
/* Theoretically the above call only unlocks bytes and it cannot /* Theoretically the above call only unlocks bytes and it cannot
* fail. Something weird happened, report it. * fail. Something weird happened, report it.
@ -2075,6 +2076,7 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
{ {
BlockdevCreateOptionsFile *file_opts; BlockdevCreateOptionsFile *file_opts;
int fd; int fd;
int perm, shared;
int result = 0; int result = 0;
/* Validate options and set default values */ /* Validate options and set default values */
@ -2089,14 +2091,44 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
} }
/* Create file */ /* Create file */
fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_BINARY, 0644);
0644);
if (fd < 0) { if (fd < 0) {
result = -errno; result = -errno;
error_setg_errno(errp, -result, "Could not create file"); error_setg_errno(errp, -result, "Could not create file");
goto out; goto out;
} }
/* Take permissions: We want to discard everything, so we need
* BLK_PERM_WRITE; and truncation to the desired size requires
* BLK_PERM_RESIZE.
* On the other hand, we cannot share the RESIZE permission
* because we promise that after this function, the file has the
* size given in the options. If someone else were to resize it
* concurrently, we could not guarantee that.
* Note that after this function, we can no longer guarantee that
* the file is not touched by a third party, so it may be resized
* then. */
perm = BLK_PERM_WRITE | BLK_PERM_RESIZE;
shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE;
/* Step one: Take locks */
result = raw_apply_lock_bytes(fd, perm, shared, false, errp);
if (result < 0) {
goto out_close;
}
/* Step two: Check that nobody else has taken conflicting locks */
result = raw_check_lock_bytes(fd, perm, shared, errp);
if (result < 0) {
goto out_close;
}
/* Clear the file by truncating it to 0 */
result = raw_regular_truncate(fd, 0, PREALLOC_MODE_OFF, errp);
if (result < 0) {
goto out_close;
}
if (file_opts->nocow) { if (file_opts->nocow) {
#ifdef __linux__ #ifdef __linux__
/* Set NOCOW flag to solve performance issue on fs like btrfs. /* Set NOCOW flag to solve performance issue on fs like btrfs.
@ -2112,6 +2144,8 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
#endif #endif
} }
/* Resize and potentially preallocate the file to the desired
* final size */
result = raw_regular_truncate(fd, file_opts->size, file_opts->preallocation, result = raw_regular_truncate(fd, file_opts->size, file_opts->preallocation,
errp); errp);
if (result < 0) { if (result < 0) {

View File

@ -254,7 +254,6 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb)
ret = bitmap_table_load(bs, tb, &bitmap_table); ret = bitmap_table_load(bs, tb, &bitmap_table);
if (ret < 0) { if (ret < 0) {
assert(bitmap_table == NULL);
return ret; return ret;
} }

View File

@ -1799,6 +1799,19 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
int ret; int ret;
uint64_t refcount; uint64_t refcount;
int i, j; int i, j;
bool repair;
if (fix & BDRV_FIX_ERRORS) {
/* Always repair */
repair = true;
} else if (fix & BDRV_FIX_LEAKS) {
/* Repair only if that seems safe: This function is always
* called after the refcounts have been fixed, so the refcount
* is accurate if that repair was successful */
repair = !res->check_errors && !res->corruptions && !res->leaks;
} else {
repair = false;
}
for (i = 0; i < s->l1_size; i++) { for (i = 0; i < s->l1_size; i++) {
uint64_t l1_entry = s->l1_table[i]; uint64_t l1_entry = s->l1_table[i];
@ -1818,10 +1831,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) {
fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d "
"l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
fix & BDRV_FIX_ERRORS ? "Repairing" : repair ? "Repairing" : "ERROR", i, l1_entry, refcount);
"ERROR", if (repair) {
i, l1_entry, refcount);
if (fix & BDRV_FIX_ERRORS) {
s->l1_table[i] = refcount == 1 s->l1_table[i] = refcount == 1
? l1_entry | QCOW_OFLAG_COPIED ? l1_entry | QCOW_OFLAG_COPIED
: l1_entry & ~QCOW_OFLAG_COPIED; : l1_entry & ~QCOW_OFLAG_COPIED;
@ -1862,10 +1873,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) {
fprintf(stderr, "%s OFLAG_COPIED data cluster: " fprintf(stderr, "%s OFLAG_COPIED data cluster: "
"l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
fix & BDRV_FIX_ERRORS ? "Repairing" : repair ? "Repairing" : "ERROR", l2_entry, refcount);
"ERROR", if (repair) {
l2_entry, refcount);
if (fix & BDRV_FIX_ERRORS) {
l2_table[j] = cpu_to_be64(refcount == 1 l2_table[j] = cpu_to_be64(refcount == 1
? l2_entry | QCOW_OFLAG_COPIED ? l2_entry | QCOW_OFLAG_COPIED
: l2_entry & ~QCOW_OFLAG_COPIED); : l2_entry & ~QCOW_OFLAG_COPIED);

View File

@ -4215,22 +4215,21 @@ static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
* have to be removed. * have to be removed.
*/ */
static int qcow2_downgrade(BlockDriverState *bs, int target_version, static int qcow2_downgrade(BlockDriverState *bs, int target_version,
BlockDriverAmendStatusCB *status_cb, void *cb_opaque) BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
Error **errp)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
int current_version = s->qcow_version; int current_version = s->qcow_version;
int ret; int ret;
if (target_version == current_version) { /* This is qcow2_downgrade(), not qcow2_upgrade() */
return 0; assert(target_version < current_version);
} else if (target_version > current_version) {
return -EINVAL; /* There are no other versions (now) that you can downgrade to */
} else if (target_version != 2) { assert(target_version == 2);
return -EINVAL;
}
if (s->refcount_order != 4) { if (s->refcount_order != 4) {
error_report("compat=0.10 requires refcount_bits=16"); error_setg(errp, "compat=0.10 requires refcount_bits=16");
return -ENOTSUP; return -ENOTSUP;
} }
@ -4238,6 +4237,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
ret = qcow2_mark_clean(bs); ret = qcow2_mark_clean(bs);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to make the image clean");
return ret; return ret;
} }
} }
@ -4247,6 +4247,8 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
* best thing to do anyway */ * best thing to do anyway */
if (s->incompatible_features) { if (s->incompatible_features) {
error_setg(errp, "Cannot downgrade an image with incompatible features "
"%#" PRIx64 " set", s->incompatible_features);
return -ENOTSUP; return -ENOTSUP;
} }
@ -4260,6 +4262,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
ret = qcow2_expand_zero_clusters(bs, status_cb, cb_opaque); ret = qcow2_expand_zero_clusters(bs, status_cb, cb_opaque);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to turn zero into data clusters");
return ret; return ret;
} }
@ -4267,6 +4270,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
ret = qcow2_update_header(bs); ret = qcow2_update_header(bs);
if (ret < 0) { if (ret < 0) {
s->qcow_version = current_version; s->qcow_version = current_version;
error_setg_errno(errp, -ret, "Failed to update the image header");
return ret; return ret;
} }
return 0; return 0;
@ -4344,7 +4348,8 @@ static void qcow2_amend_helper_cb(BlockDriverState *bs,
static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb, BlockDriverAmendStatusCB *status_cb,
void *cb_opaque) void *cb_opaque,
Error **errp)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
int old_version = s->qcow_version, new_version = old_version; int old_version = s->qcow_version, new_version = old_version;
@ -4356,7 +4361,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
bool encrypt; bool encrypt;
int encformat; int encformat;
int refcount_bits = s->refcount_bits; int refcount_bits = s->refcount_bits;
Error *local_err = NULL;
int ret; int ret;
QemuOptDesc *desc = opts->list->desc; QemuOptDesc *desc = opts->list->desc;
Qcow2AmendHelperCBInfo helper_cb_info; Qcow2AmendHelperCBInfo helper_cb_info;
@ -4377,11 +4381,11 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
} else if (!strcmp(compat, "1.1")) { } else if (!strcmp(compat, "1.1")) {
new_version = 3; new_version = 3;
} else { } else {
error_report("Unknown compatibility level %s", compat); error_setg(errp, "Unknown compatibility level %s", compat);
return -EINVAL; return -EINVAL;
} }
} else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) { } else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) {
error_report("Cannot change preallocation mode"); error_setg(errp, "Cannot change preallocation mode");
return -ENOTSUP; return -ENOTSUP;
} else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) { } else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) {
new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0); new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0);
@ -4394,7 +4398,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
!!s->crypto); !!s->crypto);
if (encrypt != !!s->crypto) { if (encrypt != !!s->crypto) {
error_report("Changing the encryption flag is not supported"); error_setg(errp,
"Changing the encryption flag is not supported");
return -ENOTSUP; return -ENOTSUP;
} }
} else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT_FORMAT)) { } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT_FORMAT)) {
@ -4402,17 +4407,19 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
qemu_opt_get(opts, BLOCK_OPT_ENCRYPT_FORMAT)); qemu_opt_get(opts, BLOCK_OPT_ENCRYPT_FORMAT));
if (encformat != s->crypt_method_header) { if (encformat != s->crypt_method_header) {
error_report("Changing the encryption format is not supported"); error_setg(errp,
"Changing the encryption format is not supported");
return -ENOTSUP; return -ENOTSUP;
} }
} else if (g_str_has_prefix(desc->name, "encrypt.")) { } else if (g_str_has_prefix(desc->name, "encrypt.")) {
error_report("Changing the encryption parameters is not supported"); error_setg(errp,
"Changing the encryption parameters is not supported");
return -ENOTSUP; return -ENOTSUP;
} else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) { } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
cluster_size); cluster_size);
if (cluster_size != s->cluster_size) { if (cluster_size != s->cluster_size) {
error_report("Changing the cluster size is not supported"); error_setg(errp, "Changing the cluster size is not supported");
return -ENOTSUP; return -ENOTSUP;
} }
} else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) { } else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
@ -4425,8 +4432,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
if (refcount_bits <= 0 || refcount_bits > 64 || if (refcount_bits <= 0 || refcount_bits > 64 ||
!is_power_of_2(refcount_bits)) !is_power_of_2(refcount_bits))
{ {
error_report("Refcount width must be a power of two and may " error_setg(errp, "Refcount width must be a power of two and "
"not exceed 64 bits"); "may not exceed 64 bits");
return -EINVAL; return -EINVAL;
} }
} else { } else {
@ -4451,6 +4458,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
ret = qcow2_update_header(bs); ret = qcow2_update_header(bs);
if (ret < 0) { if (ret < 0) {
s->qcow_version = old_version; s->qcow_version = old_version;
error_setg_errno(errp, -ret, "Failed to update the image header");
return ret; return ret;
} }
} }
@ -4459,18 +4467,17 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
int refcount_order = ctz32(refcount_bits); int refcount_order = ctz32(refcount_bits);
if (new_version < 3 && refcount_bits != 16) { if (new_version < 3 && refcount_bits != 16) {
error_report("Different refcount widths than 16 bits require " error_setg(errp, "Refcount widths other than 16 bits require "
"compatibility level 1.1 or above (use compat=1.1 or " "compatibility level 1.1 or above (use compat=1.1 or "
"greater)"); "greater)");
return -EINVAL; return -EINVAL;
} }
helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER; helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER;
ret = qcow2_change_refcount_order(bs, refcount_order, ret = qcow2_change_refcount_order(bs, refcount_order,
&qcow2_amend_helper_cb, &qcow2_amend_helper_cb,
&helper_cb_info, &local_err); &helper_cb_info, errp);
if (ret < 0) { if (ret < 0) {
error_report_err(local_err);
return ret; return ret;
} }
} }
@ -4480,6 +4487,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
backing_file ?: s->image_backing_file, backing_file ?: s->image_backing_file,
backing_format ?: s->image_backing_format); backing_format ?: s->image_backing_format);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to change the backing file");
return ret; return ret;
} }
} }
@ -4487,14 +4495,16 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
if (s->use_lazy_refcounts != lazy_refcounts) { if (s->use_lazy_refcounts != lazy_refcounts) {
if (lazy_refcounts) { if (lazy_refcounts) {
if (new_version < 3) { if (new_version < 3) {
error_report("Lazy refcounts only supported with compatibility " error_setg(errp, "Lazy refcounts only supported with "
"level 1.1 and above (use compat=1.1 or greater)"); "compatibility level 1.1 and above (use compat=1.1 "
"or greater)");
return -EINVAL; return -EINVAL;
} }
s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
ret = qcow2_update_header(bs); ret = qcow2_update_header(bs);
if (ret < 0) { if (ret < 0) {
s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS; s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS;
error_setg_errno(errp, -ret, "Failed to update the image header");
return ret; return ret;
} }
s->use_lazy_refcounts = true; s->use_lazy_refcounts = true;
@ -4502,6 +4512,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
/* make image clean first */ /* make image clean first */
ret = qcow2_mark_clean(bs); ret = qcow2_mark_clean(bs);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to make the image clean");
return ret; return ret;
} }
/* now disallow lazy refcounts */ /* now disallow lazy refcounts */
@ -4509,6 +4520,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
ret = qcow2_update_header(bs); ret = qcow2_update_header(bs);
if (ret < 0) { if (ret < 0) {
s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
error_setg_errno(errp, -ret, "Failed to update the image header");
return ret; return ret;
} }
s->use_lazy_refcounts = false; s->use_lazy_refcounts = false;
@ -4517,17 +4529,15 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
if (new_size) { if (new_size) {
BlockBackend *blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL); BlockBackend *blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL);
ret = blk_insert_bs(blk, bs, &local_err); ret = blk_insert_bs(blk, bs, errp);
if (ret < 0) { if (ret < 0) {
error_report_err(local_err);
blk_unref(blk); blk_unref(blk);
return ret; return ret;
} }
ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, &local_err); ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, errp);
blk_unref(blk); blk_unref(blk);
if (ret < 0) { if (ret < 0) {
error_report_err(local_err);
return ret; return ret;
} }
} }
@ -4536,7 +4546,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
if (new_version < old_version) { if (new_version < old_version) {
helper_cb_info.current_operation = QCOW2_DOWNGRADING; helper_cb_info.current_operation = QCOW2_DOWNGRADING;
ret = qcow2_downgrade(bs, new_version, &qcow2_amend_helper_cb, ret = qcow2_downgrade(bs, new_version, &qcow2_amend_helper_cb,
&helper_cb_info); &helper_cb_info, errp);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
@ -4559,7 +4569,7 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
char *message; char *message;
va_list ap; va_list ap;
fatal = fatal && !bs->read_only; fatal = fatal && bdrv_is_writable(bs);
if (s->signaled_corruption && if (s->signaled_corruption &&
(!fatal || (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT))) (!fatal || (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT)))

View File

@ -36,9 +36,12 @@ static QemuOptsList throttle_opts = {
}, },
}; };
static int throttle_configure_tgm(BlockDriverState *bs, /*
ThrottleGroupMember *tgm, * If this function succeeds then the throttle group name is stored in
QDict *options, Error **errp) * @group and must be freed by the caller.
* If there's an error then @group remains unmodified.
*/
static int throttle_parse_options(QDict *options, char **group, Error **errp)
{ {
int ret; int ret;
const char *group_name; const char *group_name;
@ -63,8 +66,7 @@ static int throttle_configure_tgm(BlockDriverState *bs,
goto fin; goto fin;
} }
/* Register membership to group with name group_name */ *group = g_strdup(group_name);
throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs));
ret = 0; ret = 0;
fin: fin:
qemu_opts_del(opts); qemu_opts_del(opts);
@ -75,6 +77,8 @@ static int throttle_open(BlockDriverState *bs, QDict *options,
int flags, Error **errp) int flags, Error **errp)
{ {
ThrottleGroupMember *tgm = bs->opaque; ThrottleGroupMember *tgm = bs->opaque;
char *group;
int ret;
bs->file = bdrv_open_child(NULL, options, "file", bs, bs->file = bdrv_open_child(NULL, options, "file", bs,
&child_file, false, errp); &child_file, false, errp);
@ -86,7 +90,14 @@ static int throttle_open(BlockDriverState *bs, QDict *options,
bs->supported_zero_flags = bs->file->bs->supported_zero_flags | bs->supported_zero_flags = bs->file->bs->supported_zero_flags |
BDRV_REQ_WRITE_UNCHANGED; BDRV_REQ_WRITE_UNCHANGED;
return throttle_configure_tgm(bs, tgm, options, errp); ret = throttle_parse_options(options, &group, errp);
if (ret == 0) {
/* Register membership to group with name group_name */
throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs));
g_free(group);
}
return ret;
} }
static void throttle_close(BlockDriverState *bs) static void throttle_close(BlockDriverState *bs)
@ -162,35 +173,36 @@ static void throttle_attach_aio_context(BlockDriverState *bs,
static int throttle_reopen_prepare(BDRVReopenState *reopen_state, static int throttle_reopen_prepare(BDRVReopenState *reopen_state,
BlockReopenQueue *queue, Error **errp) BlockReopenQueue *queue, Error **errp)
{ {
ThrottleGroupMember *tgm; int ret;
char *group = NULL;
assert(reopen_state != NULL); assert(reopen_state != NULL);
assert(reopen_state->bs != NULL); assert(reopen_state->bs != NULL);
reopen_state->opaque = g_new0(ThrottleGroupMember, 1); ret = throttle_parse_options(reopen_state->options, &group, errp);
tgm = reopen_state->opaque; reopen_state->opaque = group;
return ret;
return throttle_configure_tgm(reopen_state->bs, tgm, reopen_state->options,
errp);
} }
static void throttle_reopen_commit(BDRVReopenState *reopen_state) static void throttle_reopen_commit(BDRVReopenState *reopen_state)
{ {
ThrottleGroupMember *old_tgm = reopen_state->bs->opaque; BlockDriverState *bs = reopen_state->bs;
ThrottleGroupMember *new_tgm = reopen_state->opaque; ThrottleGroupMember *tgm = bs->opaque;
char *group = reopen_state->opaque;
throttle_group_unregister_tgm(old_tgm); assert(group);
g_free(old_tgm);
reopen_state->bs->opaque = new_tgm; if (strcmp(group, throttle_group_get_name(tgm))) {
throttle_group_unregister_tgm(tgm);
throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs));
}
g_free(reopen_state->opaque);
reopen_state->opaque = NULL; reopen_state->opaque = NULL;
} }
static void throttle_reopen_abort(BDRVReopenState *reopen_state) static void throttle_reopen_abort(BDRVReopenState *reopen_state)
{ {
ThrottleGroupMember *tgm = reopen_state->opaque; g_free(reopen_state->opaque);
throttle_group_unregister_tgm(tgm);
g_free(tgm);
reopen_state->opaque = NULL; reopen_state->opaque = NULL;
} }

View File

@ -343,7 +343,8 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix);
typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset, typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset,
int64_t total_work_size, void *opaque); int64_t total_work_size, void *opaque);
int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts, int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb, void *cb_opaque); BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
Error **errp);
/* external snapshots */ /* external snapshots */
bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
@ -407,6 +408,7 @@ bool bdrv_is_read_only(BlockDriverState *bs);
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
bool ignore_allow_rdw, Error **errp); bool ignore_allow_rdw, Error **errp);
int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp);
bool bdrv_is_writable(BlockDriverState *bs);
bool bdrv_is_sg(BlockDriverState *bs); bool bdrv_is_sg(BlockDriverState *bs);
bool bdrv_is_inserted(BlockDriverState *bs); bool bdrv_is_inserted(BlockDriverState *bs);
void bdrv_lock_medium(BlockDriverState *bs, bool locked); void bdrv_lock_medium(BlockDriverState *bs, bool locked);

View File

@ -353,7 +353,8 @@ struct BlockDriver {
int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts, int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb, BlockDriverAmendStatusCB *status_cb,
void *cb_opaque); void *cb_opaque,
Error **errp);
void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event); void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event);

View File

@ -22,7 +22,12 @@
#define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */ #define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */
/* Implement a qemu-io command.
* Operate on @blk using @argc/@argv as the command's arguments, and
* return 0 on success or negative errno on failure.
*/
typedef int (*cfunc_t)(BlockBackend *blk, int argc, char **argv); typedef int (*cfunc_t)(BlockBackend *blk, int argc, char **argv);
typedef void (*helpfunc_t)(void); typedef void (*helpfunc_t)(void);
typedef struct cmdinfo { typedef struct cmdinfo {
@ -41,10 +46,10 @@ typedef struct cmdinfo {
extern bool qemuio_misalign; extern bool qemuio_misalign;
bool qemuio_command(BlockBackend *blk, const char *cmd); int qemuio_command(BlockBackend *blk, const char *cmd);
void qemuio_add_command(const cmdinfo_t *ci); void qemuio_add_command(const cmdinfo_t *ci);
int qemuio_command_usage(const cmdinfo_t *ci); void qemuio_command_usage(const cmdinfo_t *ci);
void qemuio_complete_command(const char *input, void qemuio_complete_command(const char *input,
void (*fn)(const char *cmd, void *opaque), void (*fn)(const char *cmd, void *opaque),
void *opaque); void *opaque);

View File

@ -2927,13 +2927,6 @@ Option @option{-virtioconsole} has been replaced by
The @code{-clock} option is ignored since QEMU version 1.7.0. There is no The @code{-clock} option is ignored since QEMU version 1.7.0. There is no
replacement since it is not needed anymore. replacement since it is not needed anymore.
@section qemu-img command line arguments
@subsection convert -s (since 2.0.0)
The ``convert -s snapshot_id_or_name'' argument is obsoleted
by the ``convert -l snapshot_param'' argument instead.
@section QEMU Machine Protocol (QMP) commands @section QEMU Machine Protocol (QMP) commands
@subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0) @subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0)

View File

@ -44,9 +44,9 @@ STEXI
ETEXI ETEXI
DEF("convert", img_convert, DEF("convert", img_convert,
"convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename")
STEXI STEXI
@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} @item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename}
ETEXI ETEXI
DEF("create", img_create, DEF("create", img_create,

View File

@ -148,8 +148,6 @@ static void QEMU_NORETURN help(void)
" 'snapshot_param' is param used for internal snapshot, format\n" " 'snapshot_param' is param used for internal snapshot, format\n"
" is 'snapshot.id=[ID],snapshot.name=[NAME]', or\n" " is 'snapshot.id=[ID],snapshot.name=[NAME]', or\n"
" '[ID_OR_NAME]'\n" " '[ID_OR_NAME]'\n"
" 'snapshot_id_or_name' is deprecated, use 'snapshot_param'\n"
" instead\n"
" '-c' indicates that target image must be compressed (qcow format only)\n" " '-c' indicates that target image must be compressed (qcow format only)\n"
" '-u' allows unsafe backing chains. For rebasing, it is assumed that old and\n" " '-u' allows unsafe backing chains. For rebasing, it is assumed that old and\n"
" new backing file match exactly. The image doesn't need a working\n" " new backing file match exactly. The image doesn't need a working\n"
@ -249,6 +247,11 @@ static int print_block_option_help(const char *filename, const char *fmt)
return 1; return 1;
} }
if (!drv->create_opts) {
error_report("Format driver '%s' does not support image creation", fmt);
return 1;
}
create_opts = qemu_opts_append(create_opts, drv->create_opts); create_opts = qemu_opts_append(create_opts, drv->create_opts);
if (filename) { if (filename) {
proto_drv = bdrv_find_protocol(filename, true, &local_err); proto_drv = bdrv_find_protocol(filename, true, &local_err);
@ -257,9 +260,15 @@ static int print_block_option_help(const char *filename, const char *fmt)
qemu_opts_free(create_opts); qemu_opts_free(create_opts);
return 1; return 1;
} }
if (!proto_drv->create_opts) {
error_report("Protocal driver '%s' does not support image creation",
proto_drv->format_name);
return 1;
}
create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); create_opts = qemu_opts_append(create_opts, proto_drv->create_opts);
} }
printf("Supported options:\n");
qemu_opts_print_help(create_opts); qemu_opts_print_help(create_opts);
qemu_opts_free(create_opts); qemu_opts_free(create_opts);
return 0; return 0;
@ -1545,7 +1554,9 @@ typedef struct ImgConvertState {
BlockBackend *target; BlockBackend *target;
bool has_zero_init; bool has_zero_init;
bool compressed; bool compressed;
bool unallocated_blocks_are_zero;
bool target_has_backing; bool target_has_backing;
int64_t target_backing_sectors; /* negative if unknown */
bool wr_in_order; bool wr_in_order;
bool copy_range; bool copy_range;
int min_sparse; int min_sparse;
@ -1575,12 +1586,23 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num)
{ {
int64_t src_cur_offset; int64_t src_cur_offset;
int ret, n, src_cur; int ret, n, src_cur;
bool post_backing_zero = false;
convert_select_part(s, sector_num, &src_cur, &src_cur_offset); convert_select_part(s, sector_num, &src_cur, &src_cur_offset);
assert(s->total_sectors > sector_num); assert(s->total_sectors > sector_num);
n = MIN(s->total_sectors - sector_num, BDRV_REQUEST_MAX_SECTORS); n = MIN(s->total_sectors - sector_num, BDRV_REQUEST_MAX_SECTORS);
if (s->target_backing_sectors >= 0) {
if (sector_num >= s->target_backing_sectors) {
post_backing_zero = s->unallocated_blocks_are_zero;
} else if (sector_num + n > s->target_backing_sectors) {
/* Split requests around target_backing_sectors (because
* starting from there, zeros are handled differently) */
n = s->target_backing_sectors - sector_num;
}
}
if (s->sector_next_status <= sector_num) { if (s->sector_next_status <= sector_num) {
int64_t count = n * BDRV_SECTOR_SIZE; int64_t count = n * BDRV_SECTOR_SIZE;
@ -1602,7 +1624,7 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num)
n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE); n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE);
if (ret & BDRV_BLOCK_ZERO) { if (ret & BDRV_BLOCK_ZERO) {
s->status = BLK_ZERO; s->status = post_backing_zero ? BLK_BACKING_FILE : BLK_ZERO;
} else if (ret & BDRV_BLOCK_DATA) { } else if (ret & BDRV_BLOCK_DATA) {
s->status = BLK_DATA; s->status = BLK_DATA;
} else { } else {
@ -1994,7 +2016,7 @@ static int img_convert(int argc, char **argv)
{"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS}, {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
c = getopt_long(argc, argv, ":hf:O:B:co:s:l:S:pt:T:qnm:WU", c = getopt_long(argc, argv, ":hf:O:B:co:l:S:pt:T:qnm:WU",
long_options, NULL); long_options, NULL);
if (c == -1) { if (c == -1) {
break; break;
@ -2035,9 +2057,6 @@ static int img_convert(int argc, char **argv)
g_free(old_options); g_free(old_options);
} }
break; break;
case 's':
snapshot_name = optarg;
break;
case 'l': case 'l':
if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) {
sn_opts = qemu_opts_parse_noisily(&internal_snapshot_opts, sn_opts = qemu_opts_parse_noisily(&internal_snapshot_opts,
@ -2368,6 +2387,16 @@ static int img_convert(int argc, char **argv)
} }
} }
if (s.target_has_backing) {
/* Errors are treated as "backing length unknown" (which means
* s.target_backing_sectors has to be negative, which it will
* be automatically). The backing file length is used only
* for optimizations, so such a case is not fatal. */
s.target_backing_sectors = bdrv_nb_sectors(out_bs->backing->bs);
} else {
s.target_backing_sectors = -1;
}
ret = bdrv_get_info(out_bs, &bdi); ret = bdrv_get_info(out_bs, &bdi);
if (ret < 0) { if (ret < 0) {
if (s.compressed) { if (s.compressed) {
@ -2377,6 +2406,7 @@ static int img_convert(int argc, char **argv)
} else { } else {
s.compressed = s.compressed || bdi.needs_compressed_writes; s.compressed = s.compressed || bdi.needs_compressed_writes;
s.cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE; s.cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE;
s.unallocated_blocks_are_zero = bdi.unallocated_blocks_are_zero;
} }
ret = convert_do_copy(&s); ret = convert_do_copy(&s);
@ -3240,6 +3270,9 @@ static int img_rebase(int argc, char **argv)
} }
if (out_baseimg[0]) { if (out_baseimg[0]) {
const char *overlay_filename;
char *out_real_path;
options = qdict_new(); options = qdict_new();
if (out_basefmt) { if (out_basefmt) {
qdict_put_str(options, "driver", out_basefmt); qdict_put_str(options, "driver", out_basefmt);
@ -3248,8 +3281,26 @@ static int img_rebase(int argc, char **argv)
qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true);
} }
blk_new_backing = blk_new_open(out_baseimg, NULL, overlay_filename = bs->exact_filename[0] ? bs->exact_filename
: bs->filename;
out_real_path = g_malloc(PATH_MAX);
bdrv_get_full_backing_filename_from_filename(overlay_filename,
out_baseimg,
out_real_path,
PATH_MAX,
&local_err);
if (local_err) {
error_reportf_err(local_err,
"Could not resolve backing filename: ");
ret = -1;
g_free(out_real_path);
goto out;
}
blk_new_backing = blk_new_open(out_real_path, NULL,
options, src_flags, &local_err); options, src_flags, &local_err);
g_free(out_real_path);
if (!blk_new_backing) { if (!blk_new_backing) {
error_reportf_err(local_err, error_reportf_err(local_err,
"Could not open new backing file '%s': ", "Could not open new backing file '%s': ",
@ -3657,6 +3708,32 @@ static void amend_status_cb(BlockDriverState *bs,
qemu_progress_print(100.f * offset / total_work_size, 0); qemu_progress_print(100.f * offset / total_work_size, 0);
} }
static int print_amend_option_help(const char *format)
{
BlockDriver *drv;
/* Find driver and parse its options */
drv = bdrv_find_format(format);
if (!drv) {
error_report("Unknown file format '%s'", format);
return 1;
}
if (!drv->bdrv_amend_options) {
error_report("Format driver '%s' does not support option amendment",
format);
return 1;
}
/* Every driver supporting amendment must have create_opts */
assert(drv->create_opts);
printf("Creation options for '%s':\n", format);
qemu_opts_print_help(drv->create_opts);
printf("\nNote that not all of these options may be amendable.\n");
return 0;
}
static int img_amend(int argc, char **argv) static int img_amend(int argc, char **argv)
{ {
Error *err = NULL; Error *err = NULL;
@ -3756,7 +3833,7 @@ static int img_amend(int argc, char **argv)
if (fmt && has_help_option(options)) { if (fmt && has_help_option(options)) {
/* If a format is explicitly specified (and possibly no filename is /* If a format is explicitly specified (and possibly no filename is
* given), print option help here */ * given), print option help here */
ret = print_block_option_help(filename, fmt); ret = print_amend_option_help(fmt);
goto out; goto out;
} }
@ -3785,17 +3862,20 @@ static int img_amend(int argc, char **argv)
if (has_help_option(options)) { if (has_help_option(options)) {
/* If the format was auto-detected, print option help here */ /* If the format was auto-detected, print option help here */
ret = print_block_option_help(filename, fmt); ret = print_amend_option_help(fmt);
goto out; goto out;
} }
if (!bs->drv->create_opts) { if (!bs->drv->bdrv_amend_options) {
error_report("Format driver '%s' does not support any options to amend", error_report("Format driver '%s' does not support option amendment",
fmt); fmt);
ret = -1; ret = -1;
goto out; goto out;
} }
/* Every driver supporting amendment must have create_opts */
assert(bs->drv->create_opts);
create_opts = qemu_opts_append(create_opts, bs->drv->create_opts); create_opts = qemu_opts_append(create_opts, bs->drv->create_opts);
opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); opts = qemu_opts_create(create_opts, NULL, 0, &error_abort);
qemu_opts_do_parse(opts, options, NULL, &err); qemu_opts_do_parse(opts, options, NULL, &err);
@ -3807,10 +3887,10 @@ static int img_amend(int argc, char **argv)
/* In case the driver does not call amend_status_cb() */ /* In case the driver does not call amend_status_cb() */
qemu_progress_print(0.f, 0); qemu_progress_print(0.f, 0);
ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL); ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL, &err);
qemu_progress_print(100.f, 0); qemu_progress_print(100.f, 0);
if (ret < 0) { if (ret < 0) {
error_report("Error while amending options: %s", strerror(-ret)); error_report_err(err);
goto out; goto out;
} }

View File

@ -61,9 +61,6 @@ by the used format or see the format descriptions below for details.
is param used for internal snapshot, format is is param used for internal snapshot, format is
'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]' 'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]'
@item snapshot_id_or_name
is deprecated, use snapshot_param instead
@end table @end table
@table @option @table @option
@ -322,9 +319,9 @@ Error on reading data
@end table @end table
@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} @item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename}
Convert the disk image @var{filename} or a snapshot @var{snapshot_param}(@var{snapshot_id_or_name} is deprecated) Convert the disk image @var{filename} or a snapshot @var{snapshot_param}
to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c} to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c}
option) or use any format specific options like encryption (@code{-o} option). option) or use any format specific options like encryption (@code{-o} option).

View File

@ -48,10 +48,9 @@ void qemuio_add_command(const cmdinfo_t *ci)
qsort(cmdtab, ncmds, sizeof(*cmdtab), compare_cmdname); qsort(cmdtab, ncmds, sizeof(*cmdtab), compare_cmdname);
} }
int qemuio_command_usage(const cmdinfo_t *ci) void qemuio_command_usage(const cmdinfo_t *ci)
{ {
printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline); printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline);
return 0;
} }
static int init_check_command(BlockBackend *blk, const cmdinfo_t *ct) static int init_check_command(BlockBackend *blk, const cmdinfo_t *ct)
@ -72,7 +71,7 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc,
char *cmd = argv[0]; char *cmd = argv[0];
if (!init_check_command(blk, ct)) { if (!init_check_command(blk, ct)) {
return 0; return -EINVAL;
} }
if (argc - 1 < ct->argmin || (ct->argmax != -1 && argc - 1 > ct->argmax)) { if (argc - 1 < ct->argmin || (ct->argmax != -1 && argc - 1 > ct->argmax)) {
@ -89,7 +88,7 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc,
"bad argument count %d to %s, expected between %d and %d arguments\n", "bad argument count %d to %s, expected between %d and %d arguments\n",
argc-1, cmd, ct->argmin, ct->argmax); argc-1, cmd, ct->argmin, ct->argmax);
} }
return 0; return -EINVAL;
} }
/* Request additional permissions if necessary for this command. The caller /* Request additional permissions if necessary for this command. The caller
@ -109,7 +108,7 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc,
ret = blk_set_perm(blk, new_perm, orig_shared_perm, &local_err); ret = blk_set_perm(blk, new_perm, orig_shared_perm, &local_err);
if (ret < 0) { if (ret < 0) {
error_report_err(local_err); error_report_err(local_err);
return 0; return ret;
} }
} }
} }
@ -652,7 +651,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
struct timeval t1, t2; struct timeval t1, t2;
bool Cflag = false, qflag = false, vflag = false; bool Cflag = false, qflag = false, vflag = false;
bool Pflag = false, sflag = false, lflag = false, bflag = false; bool Pflag = false, sflag = false, lflag = false, bflag = false;
int c, cnt; int c, cnt, ret;
char *buf; char *buf;
int64_t offset; int64_t offset;
int64_t count; int64_t count;
@ -674,7 +673,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
pattern_count = cvtnum(optarg); pattern_count = cvtnum(optarg);
if (pattern_count < 0) { if (pattern_count < 0) {
print_cvtnum_err(pattern_count, optarg); print_cvtnum_err(pattern_count, optarg);
return 0; return pattern_count;
} }
break; break;
case 'p': case 'p':
@ -684,7 +683,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
Pflag = true; Pflag = true;
pattern = parse_pattern(optarg); pattern = parse_pattern(optarg);
if (pattern < 0) { if (pattern < 0) {
return 0; return -EINVAL;
} }
break; break;
case 'q': case 'q':
@ -695,40 +694,43 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
pattern_offset = cvtnum(optarg); pattern_offset = cvtnum(optarg);
if (pattern_offset < 0) { if (pattern_offset < 0) {
print_cvtnum_err(pattern_offset, optarg); print_cvtnum_err(pattern_offset, optarg);
return 0; return pattern_offset;
} }
break; break;
case 'v': case 'v':
vflag = true; vflag = true;
break; break;
default: default:
return qemuio_command_usage(&read_cmd); qemuio_command_usage(&read_cmd);
return -EINVAL;
} }
} }
if (optind != argc - 2) { if (optind != argc - 2) {
return qemuio_command_usage(&read_cmd); qemuio_command_usage(&read_cmd);
return -EINVAL;
} }
offset = cvtnum(argv[optind]); offset = cvtnum(argv[optind]);
if (offset < 0) { if (offset < 0) {
print_cvtnum_err(offset, argv[optind]); print_cvtnum_err(offset, argv[optind]);
return 0; return offset;
} }
optind++; optind++;
count = cvtnum(argv[optind]); count = cvtnum(argv[optind]);
if (count < 0) { if (count < 0) {
print_cvtnum_err(count, argv[optind]); print_cvtnum_err(count, argv[optind]);
return 0; return count;
} else if (count > BDRV_REQUEST_MAX_BYTES) { } else if (count > BDRV_REQUEST_MAX_BYTES) {
printf("length cannot exceed %" PRIu64 ", given %s\n", printf("length cannot exceed %" PRIu64 ", given %s\n",
(uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]); (uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]);
return 0; return -EINVAL;
} }
if (!Pflag && (lflag || sflag)) { if (!Pflag && (lflag || sflag)) {
return qemuio_command_usage(&read_cmd); qemuio_command_usage(&read_cmd);
return -EINVAL;
} }
if (!lflag) { if (!lflag) {
@ -737,19 +739,19 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
if ((pattern_count < 0) || (pattern_count + pattern_offset > count)) { if ((pattern_count < 0) || (pattern_count + pattern_offset > count)) {
printf("pattern verification range exceeds end of read data\n"); printf("pattern verification range exceeds end of read data\n");
return 0; return -EINVAL;
} }
if (bflag) { if (bflag) {
if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) { if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) {
printf("%" PRId64 " is not a sector-aligned value for 'offset'\n", printf("%" PRId64 " is not a sector-aligned value for 'offset'\n",
offset); offset);
return 0; return -EINVAL;
} }
if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) { if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) {
printf("%"PRId64" is not a sector-aligned value for 'count'\n", printf("%"PRId64" is not a sector-aligned value for 'count'\n",
count); count);
return 0; return -EINVAL;
} }
} }
@ -757,16 +759,19 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
gettimeofday(&t1, NULL); gettimeofday(&t1, NULL);
if (bflag) { if (bflag) {
cnt = do_load_vmstate(blk, buf, offset, count, &total); ret = do_load_vmstate(blk, buf, offset, count, &total);
} else { } else {
cnt = do_pread(blk, buf, offset, count, &total); ret = do_pread(blk, buf, offset, count, &total);
} }
gettimeofday(&t2, NULL); gettimeofday(&t2, NULL);
if (cnt < 0) { if (ret < 0) {
printf("read failed: %s\n", strerror(-cnt)); printf("read failed: %s\n", strerror(-ret));
goto out; goto out;
} }
cnt = ret;
ret = 0;
if (Pflag) { if (Pflag) {
void *cmp_buf = g_malloc(pattern_count); void *cmp_buf = g_malloc(pattern_count);
@ -775,6 +780,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
printf("Pattern verification failed at offset %" printf("Pattern verification failed at offset %"
PRId64 ", %"PRId64" bytes\n", PRId64 ", %"PRId64" bytes\n",
offset + pattern_offset, pattern_count); offset + pattern_offset, pattern_count);
ret = -EINVAL;
} }
g_free(cmp_buf); g_free(cmp_buf);
} }
@ -793,8 +799,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
out: out:
qemu_io_free(buf); qemu_io_free(buf);
return ret;
return 0;
} }
static void readv_help(void) static void readv_help(void)
@ -832,7 +837,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv)
{ {
struct timeval t1, t2; struct timeval t1, t2;
bool Cflag = false, qflag = false, vflag = false; bool Cflag = false, qflag = false, vflag = false;
int c, cnt; int c, cnt, ret;
char *buf; char *buf;
int64_t offset; int64_t offset;
/* Some compilers get confused and warn if this is not initialized. */ /* Some compilers get confused and warn if this is not initialized. */
@ -851,7 +856,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv)
Pflag = true; Pflag = true;
pattern = parse_pattern(optarg); pattern = parse_pattern(optarg);
if (pattern < 0) { if (pattern < 0) {
return 0; return -EINVAL;
} }
break; break;
case 'q': case 'q':
@ -861,36 +866,41 @@ static int readv_f(BlockBackend *blk, int argc, char **argv)
vflag = true; vflag = true;
break; break;
default: default:
return qemuio_command_usage(&readv_cmd); qemuio_command_usage(&readv_cmd);
return -EINVAL;
} }
} }
if (optind > argc - 2) { if (optind > argc - 2) {
return qemuio_command_usage(&readv_cmd); qemuio_command_usage(&readv_cmd);
return -EINVAL;
} }
offset = cvtnum(argv[optind]); offset = cvtnum(argv[optind]);
if (offset < 0) { if (offset < 0) {
print_cvtnum_err(offset, argv[optind]); print_cvtnum_err(offset, argv[optind]);
return 0; return offset;
} }
optind++; optind++;
nr_iov = argc - optind; nr_iov = argc - optind;
buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, 0xab); buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, 0xab);
if (buf == NULL) { if (buf == NULL) {
return 0; return -EINVAL;
} }
gettimeofday(&t1, NULL); gettimeofday(&t1, NULL);
cnt = do_aio_readv(blk, &qiov, offset, &total); ret = do_aio_readv(blk, &qiov, offset, &total);
gettimeofday(&t2, NULL); gettimeofday(&t2, NULL);
if (cnt < 0) { if (ret < 0) {
printf("readv failed: %s\n", strerror(-cnt)); printf("readv failed: %s\n", strerror(-ret));
goto out; goto out;
} }
cnt = ret;
ret = 0;
if (Pflag) { if (Pflag) {
void *cmp_buf = g_malloc(qiov.size); void *cmp_buf = g_malloc(qiov.size);
@ -898,6 +908,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv)
if (memcmp(buf, cmp_buf, qiov.size)) { if (memcmp(buf, cmp_buf, qiov.size)) {
printf("Pattern verification failed at offset %" printf("Pattern verification failed at offset %"
PRId64 ", %zd bytes\n", offset, qiov.size); PRId64 ", %zd bytes\n", offset, qiov.size);
ret = -EINVAL;
} }
g_free(cmp_buf); g_free(cmp_buf);
} }
@ -917,7 +928,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv)
out: out:
qemu_iovec_destroy(&qiov); qemu_iovec_destroy(&qiov);
qemu_io_free(buf); qemu_io_free(buf);
return 0; return ret;
} }
static void write_help(void) static void write_help(void)
@ -963,7 +974,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv)
bool Cflag = false, qflag = false, bflag = false; bool Cflag = false, qflag = false, bflag = false;
bool Pflag = false, zflag = false, cflag = false; bool Pflag = false, zflag = false, cflag = false;
int flags = 0; int flags = 0;
int c, cnt; int c, cnt, ret;
char *buf = NULL; char *buf = NULL;
int64_t offset; int64_t offset;
int64_t count; int64_t count;
@ -992,7 +1003,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv)
Pflag = true; Pflag = true;
pattern = parse_pattern(optarg); pattern = parse_pattern(optarg);
if (pattern < 0) { if (pattern < 0) {
return 0; return -EINVAL;
} }
break; break;
case 'q': case 'q':
@ -1005,62 +1016,64 @@ static int write_f(BlockBackend *blk, int argc, char **argv)
zflag = true; zflag = true;
break; break;
default: default:
return qemuio_command_usage(&write_cmd); qemuio_command_usage(&write_cmd);
return -EINVAL;
} }
} }
if (optind != argc - 2) { if (optind != argc - 2) {
return qemuio_command_usage(&write_cmd); qemuio_command_usage(&write_cmd);
return -EINVAL;
} }
if (bflag && zflag) { if (bflag && zflag) {
printf("-b and -z cannot be specified at the same time\n"); printf("-b and -z cannot be specified at the same time\n");
return 0; return -EINVAL;
} }
if ((flags & BDRV_REQ_FUA) && (bflag || cflag)) { if ((flags & BDRV_REQ_FUA) && (bflag || cflag)) {
printf("-f and -b or -c cannot be specified at the same time\n"); printf("-f and -b or -c cannot be specified at the same time\n");
return 0; return -EINVAL;
} }
if ((flags & BDRV_REQ_MAY_UNMAP) && !zflag) { if ((flags & BDRV_REQ_MAY_UNMAP) && !zflag) {
printf("-u requires -z to be specified\n"); printf("-u requires -z to be specified\n");
return 0; return -EINVAL;
} }
if (zflag && Pflag) { if (zflag && Pflag) {
printf("-z and -P cannot be specified at the same time\n"); printf("-z and -P cannot be specified at the same time\n");
return 0; return -EINVAL;
} }
offset = cvtnum(argv[optind]); offset = cvtnum(argv[optind]);
if (offset < 0) { if (offset < 0) {
print_cvtnum_err(offset, argv[optind]); print_cvtnum_err(offset, argv[optind]);
return 0; return offset;
} }
optind++; optind++;
count = cvtnum(argv[optind]); count = cvtnum(argv[optind]);
if (count < 0) { if (count < 0) {
print_cvtnum_err(count, argv[optind]); print_cvtnum_err(count, argv[optind]);
return 0; return count;
} else if (count > BDRV_REQUEST_MAX_BYTES) { } else if (count > BDRV_REQUEST_MAX_BYTES) {
printf("length cannot exceed %" PRIu64 ", given %s\n", printf("length cannot exceed %" PRIu64 ", given %s\n",
(uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]); (uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]);
return 0; return -EINVAL;
} }
if (bflag || cflag) { if (bflag || cflag) {
if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) { if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) {
printf("%" PRId64 " is not a sector-aligned value for 'offset'\n", printf("%" PRId64 " is not a sector-aligned value for 'offset'\n",
offset); offset);
return 0; return -EINVAL;
} }
if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) { if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) {
printf("%"PRId64" is not a sector-aligned value for 'count'\n", printf("%"PRId64" is not a sector-aligned value for 'count'\n",
count); count);
return 0; return -EINVAL;
} }
} }
@ -1070,20 +1083,23 @@ static int write_f(BlockBackend *blk, int argc, char **argv)
gettimeofday(&t1, NULL); gettimeofday(&t1, NULL);
if (bflag) { if (bflag) {
cnt = do_save_vmstate(blk, buf, offset, count, &total); ret = do_save_vmstate(blk, buf, offset, count, &total);
} else if (zflag) { } else if (zflag) {
cnt = do_co_pwrite_zeroes(blk, offset, count, flags, &total); ret = do_co_pwrite_zeroes(blk, offset, count, flags, &total);
} else if (cflag) { } else if (cflag) {
cnt = do_write_compressed(blk, buf, offset, count, &total); ret = do_write_compressed(blk, buf, offset, count, &total);
} else { } else {
cnt = do_pwrite(blk, buf, offset, count, flags, &total); ret = do_pwrite(blk, buf, offset, count, flags, &total);
} }
gettimeofday(&t2, NULL); gettimeofday(&t2, NULL);
if (cnt < 0) { if (ret < 0) {
printf("write failed: %s\n", strerror(-cnt)); printf("write failed: %s\n", strerror(-ret));
goto out; goto out;
} }
cnt = ret;
ret = 0;
if (qflag) { if (qflag) {
goto out; goto out;
@ -1097,8 +1113,7 @@ out:
if (!zflag) { if (!zflag) {
qemu_io_free(buf); qemu_io_free(buf);
} }
return ret;
return 0;
} }
static void static void
@ -1138,7 +1153,7 @@ static int writev_f(BlockBackend *blk, int argc, char **argv)
struct timeval t1, t2; struct timeval t1, t2;
bool Cflag = false, qflag = false; bool Cflag = false, qflag = false;
int flags = 0; int flags = 0;
int c, cnt; int c, cnt, ret;
char *buf; char *buf;
int64_t offset; int64_t offset;
/* Some compilers get confused and warn if this is not initialized. */ /* Some compilers get confused and warn if this is not initialized. */
@ -1161,39 +1176,44 @@ static int writev_f(BlockBackend *blk, int argc, char **argv)
case 'P': case 'P':
pattern = parse_pattern(optarg); pattern = parse_pattern(optarg);
if (pattern < 0) { if (pattern < 0) {
return 0; return -EINVAL;
} }
break; break;
default: default:
return qemuio_command_usage(&writev_cmd); qemuio_command_usage(&writev_cmd);
return -EINVAL;
} }
} }
if (optind > argc - 2) { if (optind > argc - 2) {
return qemuio_command_usage(&writev_cmd); qemuio_command_usage(&writev_cmd);
return -EINVAL;
} }
offset = cvtnum(argv[optind]); offset = cvtnum(argv[optind]);
if (offset < 0) { if (offset < 0) {
print_cvtnum_err(offset, argv[optind]); print_cvtnum_err(offset, argv[optind]);
return 0; return offset;
} }
optind++; optind++;
nr_iov = argc - optind; nr_iov = argc - optind;
buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, pattern); buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, pattern);
if (buf == NULL) { if (buf == NULL) {
return 0; return -EINVAL;
} }
gettimeofday(&t1, NULL); gettimeofday(&t1, NULL);
cnt = do_aio_writev(blk, &qiov, offset, flags, &total); ret = do_aio_writev(blk, &qiov, offset, flags, &total);
gettimeofday(&t2, NULL); gettimeofday(&t2, NULL);
if (cnt < 0) { if (ret < 0) {
printf("writev failed: %s\n", strerror(-cnt)); printf("writev failed: %s\n", strerror(-ret));
goto out; goto out;
} }
cnt = ret;
ret = 0;
if (qflag) { if (qflag) {
goto out; goto out;
@ -1205,7 +1225,7 @@ static int writev_f(BlockBackend *blk, int argc, char **argv)
out: out:
qemu_iovec_destroy(&qiov); qemu_iovec_destroy(&qiov);
qemu_io_free(buf); qemu_io_free(buf);
return 0; return ret;
} }
struct aio_ctx { struct aio_ctx {
@ -1312,6 +1332,9 @@ static void aio_read_help(void)
" standard output stream (with -v option) for subsequent inspection.\n" " standard output stream (with -v option) for subsequent inspection.\n"
" The read is performed asynchronously and the aio_flush command must be\n" " The read is performed asynchronously and the aio_flush command must be\n"
" used to ensure all outstanding aio requests have been completed.\n" " used to ensure all outstanding aio requests have been completed.\n"
" Note that due to its asynchronous nature, this command will be\n"
" considered successful once the request is submitted, independently\n"
" of potential I/O errors or pattern mismatches.\n"
" -C, -- report statistics in a machine parsable format\n" " -C, -- report statistics in a machine parsable format\n"
" -P, -- use a pattern to verify read data\n" " -P, -- use a pattern to verify read data\n"
" -i, -- treat request as invalid, for exercising stats\n" " -i, -- treat request as invalid, for exercising stats\n"
@ -1348,7 +1371,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv)
ctx->pattern = parse_pattern(optarg); ctx->pattern = parse_pattern(optarg);
if (ctx->pattern < 0) { if (ctx->pattern < 0) {
g_free(ctx); g_free(ctx);
return 0; return -EINVAL;
} }
break; break;
case 'i': case 'i':
@ -1364,20 +1387,23 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv)
break; break;
default: default:
g_free(ctx); g_free(ctx);
return qemuio_command_usage(&aio_read_cmd); qemuio_command_usage(&aio_read_cmd);
return -EINVAL;
} }
} }
if (optind > argc - 2) { if (optind > argc - 2) {
g_free(ctx); g_free(ctx);
return qemuio_command_usage(&aio_read_cmd); qemuio_command_usage(&aio_read_cmd);
return -EINVAL;
} }
ctx->offset = cvtnum(argv[optind]); ctx->offset = cvtnum(argv[optind]);
if (ctx->offset < 0) { if (ctx->offset < 0) {
print_cvtnum_err(ctx->offset, argv[optind]); int ret = ctx->offset;
print_cvtnum_err(ret, argv[optind]);
g_free(ctx); g_free(ctx);
return 0; return ret;
} }
optind++; optind++;
@ -1386,7 +1412,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv)
if (ctx->buf == NULL) { if (ctx->buf == NULL) {
block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ); block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ);
g_free(ctx); g_free(ctx);
return 0; return -EINVAL;
} }
gettimeofday(&ctx->t1, NULL); gettimeofday(&ctx->t1, NULL);
@ -1410,6 +1436,9 @@ static void aio_write_help(void)
" filled with a set pattern (0xcdcdcdcd).\n" " filled with a set pattern (0xcdcdcdcd).\n"
" The write is performed asynchronously and the aio_flush command must be\n" " The write is performed asynchronously and the aio_flush command must be\n"
" used to ensure all outstanding aio requests have been completed.\n" " used to ensure all outstanding aio requests have been completed.\n"
" Note that due to its asynchronous nature, this command will be\n"
" considered successful once the request is submitted, independently\n"
" of potential I/O errors or pattern mismatches.\n"
" -P, -- use different pattern to fill file\n" " -P, -- use different pattern to fill file\n"
" -C, -- report statistics in a machine parsable format\n" " -C, -- report statistics in a machine parsable format\n"
" -f, -- use Force Unit Access semantics\n" " -f, -- use Force Unit Access semantics\n"
@ -1459,7 +1488,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv)
pattern = parse_pattern(optarg); pattern = parse_pattern(optarg);
if (pattern < 0) { if (pattern < 0) {
g_free(ctx); g_free(ctx);
return 0; return -EINVAL;
} }
break; break;
case 'i': case 'i':
@ -1472,38 +1501,41 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv)
break; break;
default: default:
g_free(ctx); g_free(ctx);
return qemuio_command_usage(&aio_write_cmd); qemuio_command_usage(&aio_write_cmd);
return -EINVAL;
} }
} }
if (optind > argc - 2) { if (optind > argc - 2) {
g_free(ctx); g_free(ctx);
return qemuio_command_usage(&aio_write_cmd); qemuio_command_usage(&aio_write_cmd);
return -EINVAL;
} }
if (ctx->zflag && optind != argc - 2) { if (ctx->zflag && optind != argc - 2) {
printf("-z supports only a single length parameter\n"); printf("-z supports only a single length parameter\n");
g_free(ctx); g_free(ctx);
return 0; return -EINVAL;
} }
if ((flags & BDRV_REQ_MAY_UNMAP) && !ctx->zflag) { if ((flags & BDRV_REQ_MAY_UNMAP) && !ctx->zflag) {
printf("-u requires -z to be specified\n"); printf("-u requires -z to be specified\n");
g_free(ctx); g_free(ctx);
return 0; return -EINVAL;
} }
if (ctx->zflag && ctx->Pflag) { if (ctx->zflag && ctx->Pflag) {
printf("-z and -P cannot be specified at the same time\n"); printf("-z and -P cannot be specified at the same time\n");
g_free(ctx); g_free(ctx);
return 0; return -EINVAL;
} }
ctx->offset = cvtnum(argv[optind]); ctx->offset = cvtnum(argv[optind]);
if (ctx->offset < 0) { if (ctx->offset < 0) {
print_cvtnum_err(ctx->offset, argv[optind]); int ret = ctx->offset;
print_cvtnum_err(ret, argv[optind]);
g_free(ctx); g_free(ctx);
return 0; return ret;
} }
optind++; optind++;
@ -1512,7 +1544,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv)
if (count < 0) { if (count < 0) {
print_cvtnum_err(count, argv[optind]); print_cvtnum_err(count, argv[optind]);
g_free(ctx); g_free(ctx);
return 0; return count;
} }
ctx->qiov.size = count; ctx->qiov.size = count;
@ -1525,7 +1557,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv)
if (ctx->buf == NULL) { if (ctx->buf == NULL) {
block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE); block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE);
g_free(ctx); g_free(ctx);
return 0; return -EINVAL;
} }
gettimeofday(&ctx->t1, NULL); gettimeofday(&ctx->t1, NULL);
@ -1535,6 +1567,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv)
blk_aio_pwritev(blk, ctx->offset, &ctx->qiov, flags, aio_write_done, blk_aio_pwritev(blk, ctx->offset, &ctx->qiov, flags, aio_write_done,
ctx); ctx);
} }
return 0; return 0;
} }
@ -1555,8 +1588,7 @@ static const cmdinfo_t aio_flush_cmd = {
static int flush_f(BlockBackend *blk, int argc, char **argv) static int flush_f(BlockBackend *blk, int argc, char **argv)
{ {
blk_flush(blk); return blk_flush(blk);
return 0;
} }
static const cmdinfo_t flush_cmd = { static const cmdinfo_t flush_cmd = {
@ -1575,13 +1607,13 @@ static int truncate_f(BlockBackend *blk, int argc, char **argv)
offset = cvtnum(argv[1]); offset = cvtnum(argv[1]);
if (offset < 0) { if (offset < 0) {
print_cvtnum_err(offset, argv[1]); print_cvtnum_err(offset, argv[1]);
return 0; return offset;
} }
ret = blk_truncate(blk, offset, PREALLOC_MODE_OFF, &local_err); ret = blk_truncate(blk, offset, PREALLOC_MODE_OFF, &local_err);
if (ret < 0) { if (ret < 0) {
error_report_err(local_err); error_report_err(local_err);
return 0; return ret;
} }
return 0; return 0;
@ -1606,7 +1638,7 @@ static int length_f(BlockBackend *blk, int argc, char **argv)
size = blk_getlength(blk); size = blk_getlength(blk);
if (size < 0) { if (size < 0) {
printf("getlength: %s\n", strerror(-size)); printf("getlength: %s\n", strerror(-size));
return 0; return size;
} }
cvtstr(size, s1, sizeof(s1)); cvtstr(size, s1, sizeof(s1));
@ -1640,7 +1672,7 @@ static int info_f(BlockBackend *blk, int argc, char **argv)
ret = bdrv_get_info(bs, &bdi); ret = bdrv_get_info(bs, &bdi);
if (ret) { if (ret) {
return 0; return ret;
} }
cvtstr(bdi.cluster_size, s1, sizeof(s1)); cvtstr(bdi.cluster_size, s1, sizeof(s1));
@ -1713,30 +1745,32 @@ static int discard_f(BlockBackend *blk, int argc, char **argv)
qflag = true; qflag = true;
break; break;
default: default:
return qemuio_command_usage(&discard_cmd); qemuio_command_usage(&discard_cmd);
return -EINVAL;
} }
} }
if (optind != argc - 2) { if (optind != argc - 2) {
return qemuio_command_usage(&discard_cmd); qemuio_command_usage(&discard_cmd);
return -EINVAL;
} }
offset = cvtnum(argv[optind]); offset = cvtnum(argv[optind]);
if (offset < 0) { if (offset < 0) {
print_cvtnum_err(offset, argv[optind]); print_cvtnum_err(offset, argv[optind]);
return 0; return offset;
} }
optind++; optind++;
bytes = cvtnum(argv[optind]); bytes = cvtnum(argv[optind]);
if (bytes < 0) { if (bytes < 0) {
print_cvtnum_err(bytes, argv[optind]); print_cvtnum_err(bytes, argv[optind]);
return 0; return bytes;
} else if (bytes >> BDRV_SECTOR_BITS > BDRV_REQUEST_MAX_SECTORS) { } else if (bytes >> BDRV_SECTOR_BITS > BDRV_REQUEST_MAX_SECTORS) {
printf("length cannot exceed %"PRIu64", given %s\n", printf("length cannot exceed %"PRIu64", given %s\n",
(uint64_t)BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS, (uint64_t)BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS,
argv[optind]); argv[optind]);
return 0; return -EINVAL;
} }
gettimeofday(&t1, NULL); gettimeofday(&t1, NULL);
@ -1745,7 +1779,7 @@ static int discard_f(BlockBackend *blk, int argc, char **argv)
if (ret < 0) { if (ret < 0) {
printf("discard failed: %s\n", strerror(-ret)); printf("discard failed: %s\n", strerror(-ret));
goto out; return ret;
} }
/* Finally, report back -- -C gives a parsable format */ /* Finally, report back -- -C gives a parsable format */
@ -1754,7 +1788,6 @@ static int discard_f(BlockBackend *blk, int argc, char **argv)
print_report("discard", &t2, offset, bytes, bytes, 1, Cflag); print_report("discard", &t2, offset, bytes, bytes, 1, Cflag);
} }
out:
return 0; return 0;
} }
@ -1769,14 +1802,14 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv)
start = offset = cvtnum(argv[1]); start = offset = cvtnum(argv[1]);
if (offset < 0) { if (offset < 0) {
print_cvtnum_err(offset, argv[1]); print_cvtnum_err(offset, argv[1]);
return 0; return offset;
} }
if (argc == 3) { if (argc == 3) {
count = cvtnum(argv[2]); count = cvtnum(argv[2]);
if (count < 0) { if (count < 0) {
print_cvtnum_err(count, argv[2]); print_cvtnum_err(count, argv[2]);
return 0; return count;
} }
} else { } else {
count = BDRV_SECTOR_SIZE; count = BDRV_SECTOR_SIZE;
@ -1788,7 +1821,7 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv)
ret = bdrv_is_allocated(bs, offset, remaining, &num); ret = bdrv_is_allocated(bs, offset, remaining, &num);
if (ret < 0) { if (ret < 0) {
printf("is_allocated failed: %s\n", strerror(-ret)); printf("is_allocated failed: %s\n", strerror(-ret));
return 0; return ret;
} }
offset += num; offset += num;
remaining -= num; remaining -= num;
@ -1863,17 +1896,17 @@ static int map_f(BlockBackend *blk, int argc, char **argv)
bytes = blk_getlength(blk); bytes = blk_getlength(blk);
if (bytes < 0) { if (bytes < 0) {
error_report("Failed to query image length: %s", strerror(-bytes)); error_report("Failed to query image length: %s", strerror(-bytes));
return 0; return bytes;
} }
while (bytes) { while (bytes) {
ret = map_is_allocated(blk_bs(blk), offset, bytes, &num); ret = map_is_allocated(blk_bs(blk), offset, bytes, &num);
if (ret < 0) { if (ret < 0) {
error_report("Failed to get allocation status: %s", strerror(-ret)); error_report("Failed to get allocation status: %s", strerror(-ret));
return 0; return ret;
} else if (!num) { } else if (!num) {
error_report("Unexpected end of image"); error_report("Unexpected end of image");
return 0; return -EIO;
} }
retstr = ret ? " allocated" : "not allocated"; retstr = ret ? " allocated" : "not allocated";
@ -1954,19 +1987,19 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
case 'c': case 'c':
if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) { if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) {
error_report("Invalid cache option: %s", optarg); error_report("Invalid cache option: %s", optarg);
return 0; return -EINVAL;
} }
break; break;
case 'o': case 'o':
if (!qemu_opts_parse_noisily(&reopen_opts, optarg, 0)) { if (!qemu_opts_parse_noisily(&reopen_opts, optarg, 0)) {
qemu_opts_reset(&reopen_opts); qemu_opts_reset(&reopen_opts);
return 0; return -EINVAL;
} }
break; break;
case 'r': case 'r':
if (has_rw_option) { if (has_rw_option) {
error_report("Only one -r/-w option may be given"); error_report("Only one -r/-w option may be given");
return 0; return -EINVAL;
} }
flags &= ~BDRV_O_RDWR; flags &= ~BDRV_O_RDWR;
has_rw_option = true; has_rw_option = true;
@ -1974,20 +2007,22 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
case 'w': case 'w':
if (has_rw_option) { if (has_rw_option) {
error_report("Only one -r/-w option may be given"); error_report("Only one -r/-w option may be given");
return 0; return -EINVAL;
} }
flags |= BDRV_O_RDWR; flags |= BDRV_O_RDWR;
has_rw_option = true; has_rw_option = true;
break; break;
default: default:
qemu_opts_reset(&reopen_opts); qemu_opts_reset(&reopen_opts);
return qemuio_command_usage(&reopen_cmd); qemuio_command_usage(&reopen_cmd);
return -EINVAL;
} }
} }
if (optind != argc) { if (optind != argc) {
qemu_opts_reset(&reopen_opts); qemu_opts_reset(&reopen_opts);
return qemuio_command_usage(&reopen_cmd); qemuio_command_usage(&reopen_cmd);
return -EINVAL;
} }
if (writethrough != blk_enable_write_cache(blk) && if (writethrough != blk_enable_write_cache(blk) &&
@ -1995,7 +2030,7 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
{ {
error_report("Cannot change cache.writeback: Device attached"); error_report("Cannot change cache.writeback: Device attached");
qemu_opts_reset(&reopen_opts); qemu_opts_reset(&reopen_opts);
return 0; return -EBUSY;
} }
if (!(flags & BDRV_O_RDWR)) { if (!(flags & BDRV_O_RDWR)) {
@ -2021,10 +2056,10 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
if (local_err) { if (local_err) {
error_report_err(local_err); error_report_err(local_err);
} else { return -EINVAL;
blk_set_enable_write_cache(blk, !writethrough);
} }
blk_set_enable_write_cache(blk, !writethrough);
return 0; return 0;
} }
@ -2035,6 +2070,7 @@ static int break_f(BlockBackend *blk, int argc, char **argv)
ret = bdrv_debug_breakpoint(blk_bs(blk), argv[1], argv[2]); ret = bdrv_debug_breakpoint(blk_bs(blk), argv[1], argv[2]);
if (ret < 0) { if (ret < 0) {
printf("Could not set breakpoint: %s\n", strerror(-ret)); printf("Could not set breakpoint: %s\n", strerror(-ret));
return ret;
} }
return 0; return 0;
@ -2047,6 +2083,7 @@ static int remove_break_f(BlockBackend *blk, int argc, char **argv)
ret = bdrv_debug_remove_breakpoint(blk_bs(blk), argv[1]); ret = bdrv_debug_remove_breakpoint(blk_bs(blk), argv[1]);
if (ret < 0) { if (ret < 0) {
printf("Could not remove breakpoint %s: %s\n", argv[1], strerror(-ret)); printf("Could not remove breakpoint %s: %s\n", argv[1], strerror(-ret));
return ret;
} }
return 0; return 0;
@ -2078,6 +2115,7 @@ static int resume_f(BlockBackend *blk, int argc, char **argv)
ret = bdrv_debug_resume(blk_bs(blk), argv[1]); ret = bdrv_debug_resume(blk_bs(blk), argv[1]);
if (ret < 0) { if (ret < 0) {
printf("Could not resume request: %s\n", strerror(-ret)); printf("Could not resume request: %s\n", strerror(-ret));
return ret;
} }
return 0; return 0;
@ -2097,7 +2135,6 @@ static int wait_break_f(BlockBackend *blk, int argc, char **argv)
while (!bdrv_debug_is_suspended(blk_bs(blk), argv[1])) { while (!bdrv_debug_is_suspended(blk_bs(blk), argv[1])) {
aio_poll(blk_get_aio_context(blk), true); aio_poll(blk_get_aio_context(blk), true);
} }
return 0; return 0;
} }
@ -2154,11 +2191,11 @@ static int sigraise_f(BlockBackend *blk, int argc, char **argv)
int64_t sig = cvtnum(argv[1]); int64_t sig = cvtnum(argv[1]);
if (sig < 0) { if (sig < 0) {
print_cvtnum_err(sig, argv[1]); print_cvtnum_err(sig, argv[1]);
return 0; return sig;
} else if (sig > NSIG) { } else if (sig > NSIG) {
printf("signal argument '%s' is too large to be a valid signal\n", printf("signal argument '%s' is too large to be a valid signal\n",
argv[1]); argv[1]);
return 0; return -EINVAL;
} }
/* Using raise() to kill this process does not necessarily flush all open /* Using raise() to kill this process does not necessarily flush all open
@ -2168,6 +2205,7 @@ static int sigraise_f(BlockBackend *blk, int argc, char **argv)
fflush(stderr); fflush(stderr);
raise(sig); raise(sig);
return 0; return 0;
} }
@ -2187,7 +2225,7 @@ static int sleep_f(BlockBackend *blk, int argc, char **argv)
ms = strtol(argv[1], &endptr, 0); ms = strtol(argv[1], &endptr, 0);
if (ms < 0 || *endptr != '\0') { if (ms < 0 || *endptr != '\0') {
printf("%s is not a valid number\n", argv[1]); printf("%s is not a valid number\n", argv[1]);
return 0; return -EINVAL;
} }
timer = timer_new_ns(QEMU_CLOCK_HOST, sleep_cb, &expired); timer = timer_new_ns(QEMU_CLOCK_HOST, sleep_cb, &expired);
@ -2198,7 +2236,6 @@ static int sleep_f(BlockBackend *blk, int argc, char **argv)
} }
timer_free(timer); timer_free(timer);
return 0; return 0;
} }
@ -2258,7 +2295,7 @@ static int help_f(BlockBackend *blk, int argc, char **argv)
ct = find_command(argv[1]); ct = find_command(argv[1]);
if (ct == NULL) { if (ct == NULL) {
printf("command %s not found\n", argv[1]); printf("command %s not found\n", argv[1]);
return 0; return -EINVAL;
} }
help_onecmd(argv[1], ct); help_onecmd(argv[1], ct);
@ -2276,14 +2313,14 @@ static const cmdinfo_t help_cmd = {
.oneline = "help for one or all commands", .oneline = "help for one or all commands",
}; };
bool qemuio_command(BlockBackend *blk, const char *cmd) int qemuio_command(BlockBackend *blk, const char *cmd)
{ {
AioContext *ctx; AioContext *ctx;
char *input; char *input;
const cmdinfo_t *ct; const cmdinfo_t *ct;
char **v; char **v;
int c; int c;
bool done = false; int ret = 0;
input = g_strdup(cmd); input = g_strdup(cmd);
v = breakline(input, &c); v = breakline(input, &c);
@ -2292,16 +2329,17 @@ bool qemuio_command(BlockBackend *blk, const char *cmd)
if (ct) { if (ct) {
ctx = blk ? blk_get_aio_context(blk) : qemu_get_aio_context(); ctx = blk ? blk_get_aio_context(blk) : qemu_get_aio_context();
aio_context_acquire(ctx); aio_context_acquire(ctx);
done = command(blk, ct, c, v); ret = command(blk, ct, c, v);
aio_context_release(ctx); aio_context_release(ctx);
} else { } else {
fprintf(stderr, "command \"%s\" not found\n", v[0]); fprintf(stderr, "command \"%s\" not found\n", v[0]);
ret = -EINVAL;
} }
} }
g_free(input); g_free(input);
g_free(v); g_free(v);
return done; return ret;
} }
static void __attribute((constructor)) init_qemuio_commands(void) static void __attribute((constructor)) init_qemuio_commands(void)

View File

@ -37,6 +37,7 @@
static char *progname; static char *progname;
static BlockBackend *qemuio_blk; static BlockBackend *qemuio_blk;
static bool quit_qemu_io;
/* qemu-io commands passed using -c */ /* qemu-io commands passed using -c */
static int ncmdline; static int ncmdline;
@ -166,6 +167,7 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
int readonly = 0; int readonly = 0;
bool writethrough = true; bool writethrough = true;
int c; int c;
int ret;
QemuOpts *qopts; QemuOpts *qopts;
QDict *opts; QDict *opts;
bool force_share = false; bool force_share = false;
@ -192,25 +194,25 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) { if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) {
error_report("Invalid cache option: %s", optarg); error_report("Invalid cache option: %s", optarg);
qemu_opts_reset(&empty_opts); qemu_opts_reset(&empty_opts);
return 0; return -EINVAL;
} }
break; break;
case 'd': case 'd':
if (bdrv_parse_discard_flags(optarg, &flags) < 0) { if (bdrv_parse_discard_flags(optarg, &flags) < 0) {
error_report("Invalid discard option: %s", optarg); error_report("Invalid discard option: %s", optarg);
qemu_opts_reset(&empty_opts); qemu_opts_reset(&empty_opts);
return 0; return -EINVAL;
} }
break; break;
case 'o': case 'o':
if (imageOpts) { if (imageOpts) {
printf("--image-opts and 'open -o' are mutually exclusive\n"); printf("--image-opts and 'open -o' are mutually exclusive\n");
qemu_opts_reset(&empty_opts); qemu_opts_reset(&empty_opts);
return 0; return -EINVAL;
} }
if (!qemu_opts_parse_noisily(&empty_opts, optarg, false)) { if (!qemu_opts_parse_noisily(&empty_opts, optarg, false)) {
qemu_opts_reset(&empty_opts); qemu_opts_reset(&empty_opts);
return 0; return -EINVAL;
} }
break; break;
case 'U': case 'U':
@ -218,7 +220,8 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
break; break;
default: default:
qemu_opts_reset(&empty_opts); qemu_opts_reset(&empty_opts);
return qemuio_command_usage(&open_cmd); qemuio_command_usage(&open_cmd);
return -EINVAL;
} }
} }
@ -229,7 +232,7 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
if (imageOpts && (optind == argc - 1)) { if (imageOpts && (optind == argc - 1)) {
if (!qemu_opts_parse_noisily(&empty_opts, argv[optind], false)) { if (!qemu_opts_parse_noisily(&empty_opts, argv[optind], false)) {
qemu_opts_reset(&empty_opts); qemu_opts_reset(&empty_opts);
return 0; return -EINVAL;
} }
optind++; optind++;
} }
@ -239,19 +242,26 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
qemu_opts_reset(&empty_opts); qemu_opts_reset(&empty_opts);
if (optind == argc - 1) { if (optind == argc - 1) {
openfile(argv[optind], flags, writethrough, force_share, opts); ret = openfile(argv[optind], flags, writethrough, force_share, opts);
} else if (optind == argc) { } else if (optind == argc) {
openfile(NULL, flags, writethrough, force_share, opts); ret = openfile(NULL, flags, writethrough, force_share, opts);
} else { } else {
qobject_unref(opts); qobject_unref(opts);
qemuio_command_usage(&open_cmd); qemuio_command_usage(&open_cmd);
return -EINVAL;
} }
if (ret) {
return -EINVAL;
}
return 0; return 0;
} }
static int quit_f(BlockBackend *blk, int argc, char **argv) static int quit_f(BlockBackend *blk, int argc, char **argv)
{ {
return 1; quit_qemu_io = true;
return 0;
} }
static const cmdinfo_t quit_cmd = { static const cmdinfo_t quit_cmd = {
@ -390,20 +400,24 @@ static void prep_fetchline(void *opaque)
*fetchable= 1; *fetchable= 1;
} }
static void command_loop(void) static int command_loop(void)
{ {
int i, done = 0, fetchable = 0, prompted = 0; int i, fetchable = 0, prompted = 0;
int ret, last_error = 0;
char *input; char *input;
for (i = 0; !done && i < ncmdline; i++) { for (i = 0; !quit_qemu_io && i < ncmdline; i++) {
done = qemuio_command(qemuio_blk, cmdline[i]); ret = qemuio_command(qemuio_blk, cmdline[i]);
if (ret < 0) {
last_error = ret;
}
} }
if (cmdline) { if (cmdline) {
g_free(cmdline); g_free(cmdline);
return; return last_error;
} }
while (!done) { while (!quit_qemu_io) {
if (!prompted) { if (!prompted) {
printf("%s", get_prompt()); printf("%s", get_prompt());
fflush(stdout); fflush(stdout);
@ -421,13 +435,19 @@ static void command_loop(void)
if (input == NULL) { if (input == NULL) {
break; break;
} }
done = qemuio_command(qemuio_blk, input); ret = qemuio_command(qemuio_blk, input);
g_free(input); g_free(input);
if (ret < 0) {
last_error = ret;
}
prompted = 0; prompted = 0;
fetchable = 0; fetchable = 0;
} }
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
return last_error;
} }
static void add_user_command(char *optarg) static void add_user_command(char *optarg)
@ -492,6 +512,7 @@ int main(int argc, char **argv)
int c; int c;
int opt_index = 0; int opt_index = 0;
int flags = BDRV_O_UNMAP; int flags = BDRV_O_UNMAP;
int ret;
bool writethrough = true; bool writethrough = true;
Error *local_error = NULL; Error *local_error = NULL;
QDict *opts = NULL; QDict *opts = NULL;
@ -653,7 +674,7 @@ int main(int argc, char **argv)
} }
} }
} }
command_loop(); ret = command_loop();
/* /*
* Make sure all outstanding requests complete before the program exits. * Make sure all outstanding requests complete before the program exits.
@ -662,5 +683,10 @@ int main(int argc, char **argv)
blk_unref(qemuio_blk); blk_unref(qemuio_blk);
g_free(readline_state); g_free(readline_state);
return 0;
if (ret < 0) {
return 1;
} else {
return 0;
}
} }

View File

@ -29,9 +29,14 @@ status=1 # failure is the default!
_cleanup() _cleanup()
{ {
_cleanup_test_img _cleanup_test_img
rm -f "$TEST_DIR/t.$IMGFMT.base_old" rm -f "$TEST_DIR/t.$IMGFMT.base_old"
rm -f "$TEST_DIR/t.$IMGFMT.base_new" rm -f "$TEST_DIR/t.$IMGFMT.base_new"
rm -f "$TEST_DIR/subdir/t.$IMGFMT"
rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_old"
rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_new"
rmdir "$TEST_DIR/subdir" 2> /dev/null
} }
trap "_cleanup; exit \$status" 0 1 2 3 15 trap "_cleanup; exit \$status" 0 1 2 3 15
@ -123,6 +128,77 @@ io_pattern readv $((13 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x00
io_pattern readv $((14 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x11 io_pattern readv $((14 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x11
io_pattern readv $((15 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x00 io_pattern readv $((15 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x00
echo
echo "=== Test rebase in a subdirectory of the working directory ==="
echo
# Clean up the old images beforehand so they do not interfere with
# this test
_cleanup
mkdir "$TEST_DIR/subdir"
# Relative to the overlay
BASE_OLD_OREL="t.$IMGFMT.base_old"
BASE_NEW_OREL="t.$IMGFMT.base_new"
# Relative to $TEST_DIR (which is going to be our working directory)
OVERLAY_WREL="subdir/t.$IMGFMT"
BASE_OLD="$TEST_DIR/subdir/$BASE_OLD_OREL"
BASE_NEW="$TEST_DIR/subdir/$BASE_NEW_OREL"
OVERLAY="$TEST_DIR/$OVERLAY_WREL"
# Test done here:
#
# Backing (old): 11 11 -- 11
# Backing (new): -- 22 22 11
# Overlay: -- -- -- --
#
# Rebasing works, we have verified that above. Here, we just want to
# see that rebasing is done for the correct target backing file.
TEST_IMG=$BASE_OLD _make_test_img 1M
TEST_IMG=$BASE_NEW _make_test_img 1M
TEST_IMG=$OVERLAY _make_test_img -b "$BASE_OLD_OREL" 1M
echo
$QEMU_IO "$BASE_OLD" \
-c "write -P 0x11 $((0 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \
-c "write -P 0x11 $((3 * CLUSTER_SIZE)) $((1 * CLUSTER_SIZE))" \
| _filter_qemu_io
$QEMU_IO "$BASE_NEW" \
-c "write -P 0x22 $((1 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \
-c "write -P 0x11 $((3 * CLUSTER_SIZE)) $((1 * CLUSTER_SIZE))" \
| _filter_qemu_io
echo
pushd "$TEST_DIR" >/dev/null
$QEMU_IMG rebase -f "$IMGFMT" -b "$BASE_NEW_OREL" "$OVERLAY_WREL"
popd >/dev/null
# Verify the backing path is correct
TEST_IMG=$OVERLAY _img_info | grep '^backing file'
echo
# Verify the data is correct
$QEMU_IO "$OVERLAY" \
-c "read -P 0x11 $((0 * CLUSTER_SIZE)) $CLUSTER_SIZE" \
-c "read -P 0x11 $((1 * CLUSTER_SIZE)) $CLUSTER_SIZE" \
-c "read -P 0x00 $((2 * CLUSTER_SIZE)) $CLUSTER_SIZE" \
-c "read -P 0x11 $((3 * CLUSTER_SIZE)) $CLUSTER_SIZE" \
| _filter_qemu_io
echo
# Verify that cluster #3 is not allocated (because it is the same in
# $BASE_OLD and $BASE_NEW)
$QEMU_IMG map "$OVERLAY" | _filter_qemu_img_map
# success, all done # success, all done
echo "*** done" echo "*** done"

View File

@ -141,4 +141,34 @@ read 65536/65536 bytes at offset 917504
=== IO: pattern 0x00 === IO: pattern 0x00
read 65536/65536 bytes at offset 983040 read 65536/65536 bytes at offset 983040
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Test rebase in a subdirectory of the working directory ===
Formatting 'TEST_DIR/subdir/t.IMGFMT.base_old', fmt=IMGFMT size=1048576
Formatting 'TEST_DIR/subdir/t.IMGFMT.base_new', fmt=IMGFMT size=1048576
Formatting 'TEST_DIR/subdir/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=t.IMGFMT.base_old
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 196608
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 65536
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 196608
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
backing file: t.IMGFMT.base_new (actual path: TEST_DIR/subdir/t.IMGFMT.base_new)
read 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 65536
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 131072
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 196608
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Offset Length File
0 0x30000 TEST_DIR/subdir/t.IMGFMT
0x30000 0x10000 TEST_DIR/subdir/t.IMGFMT.base_new
*** done *** done

View File

@ -92,7 +92,7 @@ _make_test_img 64M
{ $QEMU_IMG snapshot -c foo $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir { $QEMU_IMG snapshot -c foo $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
poke_file "$TEST_IMG" "$offset_size" "\x00\x00\x00\x00\x00\x00\x02\x00" poke_file "$TEST_IMG" "$offset_size" "\x00\x00\x00\x00\x00\x00\x02\x00"
poke_file "$TEST_IMG" "$offset_l1_size" "\x00\x00\x00\x01" poke_file "$TEST_IMG" "$offset_l1_size" "\x00\x00\x00\x01"
{ $QEMU_IMG convert -s foo $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_qemu_io | _filter_testdir { $QEMU_IMG convert -l foo $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_qemu_io | _filter_testdir
# success, all done # success, all done

View File

@ -440,6 +440,36 @@ echo "{'execute': 'qmp_capabilities'}
-drive if=none,node-name=drive,file="$TEST_IMG",driver=qcow2 \ -drive if=none,node-name=drive,file="$TEST_IMG",driver=qcow2 \
| _filter_qmp | _filter_qemu_io | _filter_qmp | _filter_qemu_io
echo
echo "=== Testing incoming inactive corrupted image ==="
echo
_make_test_img 64M
# Create an unaligned L1 entry, so qemu will signal a corruption when
# reading from the covered area
poke_file "$TEST_IMG" "$l1_offset" "\x00\x00\x00\x00\x2a\x2a\x2a\x2a"
# Inactive images are effectively read-only images, so this should be a
# non-fatal corruption (which does not modify the image)
echo "{'execute': 'qmp_capabilities'}
{'execute': 'human-monitor-command',
'arguments': {'command-line': 'qemu-io drive \"read 0 512\"'}}
{'execute': 'quit'}" \
| $QEMU -qmp stdio -nographic -nodefaults \
-blockdev "{'node-name': 'drive',
'driver': 'qcow2',
'file': {
'driver': 'file',
'filename': '$TEST_IMG'
}}" \
-incoming exec:'cat /dev/null' \
2>&1 \
| _filter_qmp | _filter_qemu_io
echo
# Image should not have been marked corrupt
_img_info --format-specific | grep 'corrupt:'
# success, all done # success, all done
echo "*** done" echo "*** done"
rm -f $seq.full rm -f $seq.full

View File

@ -129,7 +129,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 65536/65536 bytes at offset 0 wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qcow2: Marking image as corrupt: L2 table offset 0x42a00 unaligned (L1 index: 0); further corruption events will be suppressed qcow2: Marking image as corrupt: L2 table offset 0x42a00 unaligned (L1 index: 0); further corruption events will be suppressed
qemu-img: Error while amending options: Input/output error qemu-img: Failed to turn zero into data clusters: Input/output error
=== Testing unaligned L2 entry === === Testing unaligned L2 entry ===
@ -145,7 +145,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 65536/65536 bytes at offset 0 wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qcow2: Marking image as corrupt: Cluster allocation offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed qcow2: Marking image as corrupt: Cluster allocation offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed
qemu-img: Error while amending options: Input/output error qemu-img: Failed to turn zero into data clusters: Input/output error
=== Testing unaligned reftable entry === === Testing unaligned reftable entry ===
@ -420,4 +420,18 @@ write failed: Input/output error
{"return": ""} {"return": ""}
{"return": {}} {"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
=== Testing incoming inactive corrupted image ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
QMP_VERSION
{"return": {}}
qcow2: Image is corrupt: L2 table offset 0x2a2a2a00 unaligned (L1 index: 0); further non-fatal corruption events will be suppressed
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}}
read failed: Input/output error
{"return": ""}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
corrupt: false
*** done *** done

View File

@ -358,18 +358,12 @@ No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
qemu-img: Error while amending options: Invalid argument
qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
qemu-img: Error while amending options: Invalid argument
qemu-img: Unknown compatibility level 0.42 qemu-img: Unknown compatibility level 0.42
qemu-img: Error while amending options: Invalid argument
qemu-img: Invalid parameter 'foo' qemu-img: Invalid parameter 'foo'
qemu-img: Changing the cluster size is not supported qemu-img: Changing the cluster size is not supported
qemu-img: Error while amending options: Operation not supported
qemu-img: Changing the encryption flag is not supported qemu-img: Changing the encryption flag is not supported
qemu-img: Error while amending options: Operation not supported
qemu-img: Cannot change preallocation mode qemu-img: Cannot change preallocation mode
qemu-img: Error while amending options: Operation not supported
=== Testing correct handling of unset value === === Testing correct handling of unset value ===
@ -377,7 +371,6 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
Should work: Should work:
Should not work: Should not work:
qemu-img: Changing the cluster size is not supported qemu-img: Changing the cluster size is not supported
qemu-img: Error while amending options: Operation not supported
=== Testing zero expansion on inactive clusters === === Testing zero expansion on inactive clusters ===

View File

@ -176,7 +176,7 @@ _make_test_img 64M
{ $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir { $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir { $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir
poke_file "$TEST_IMG" "$offset_snap1_l1_offset" "\x00\x00\x00\x00\x00\x40\x02\x00" poke_file "$TEST_IMG" "$offset_snap1_l1_offset" "\x00\x00\x00\x00\x00\x40\x02\x00"
{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir { $QEMU_IMG convert -l test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir
{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir { $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir
{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \ { $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \
-c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir
@ -190,7 +190,7 @@ _make_test_img 64M
{ $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir { $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir { $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir
poke_file "$TEST_IMG" "$offset_snap1_l1_size" "\x10\x00\x00\x00" poke_file "$TEST_IMG" "$offset_snap1_l1_size" "\x10\x00\x00\x00"
{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir { $QEMU_IMG convert -l test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir
{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir { $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir
{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \ { $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \
-c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir

View File

@ -65,7 +65,7 @@ wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qemu-img: Failed to load snapshot: Snapshot L1 table offset invalid qemu-img: Failed to load snapshot: Snapshot L1 table offset invalid
qemu-img: Snapshot L1 table offset invalid qemu-img: Snapshot L1 table offset invalid
qemu-img: Error while amending options: Invalid argument qemu-img: Failed to turn zero into data clusters: Invalid argument
Failed to flush the refcount block cache: Invalid argument Failed to flush the refcount block cache: Invalid argument
write failed: Invalid argument write failed: Invalid argument
qemu-img: Snapshot L1 table offset invalid qemu-img: Snapshot L1 table offset invalid
@ -88,7 +88,7 @@ wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qemu-img: Failed to load snapshot: Snapshot L1 table too large qemu-img: Failed to load snapshot: Snapshot L1 table too large
qemu-img: Snapshot L1 table too large qemu-img: Snapshot L1 table too large
qemu-img: Error while amending options: File too large qemu-img: Failed to turn zero into data clusters: File too large
Failed to flush the refcount block cache: File too large Failed to flush the refcount block cache: File too large
write failed: File too large write failed: File too large
qemu-img: Snapshot L1 table too large qemu-img: Snapshot L1 table too large

View File

@ -97,6 +97,9 @@ run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_
run_qemu_img create -f $IMGFMT -o help run_qemu_img create -f $IMGFMT -o help
run_qemu_img create -o help run_qemu_img create -o help
# Try help option for a format that does not support creation
run_qemu_img create -f bochs -o help
echo echo
echo === convert: Options specified more than once === echo === convert: Options specified more than once ===
@ -151,6 +154,9 @@ run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST
run_qemu_img convert -O $IMGFMT -o help run_qemu_img convert -O $IMGFMT -o help
run_qemu_img convert -o help run_qemu_img convert -o help
# Try help option for a format that does not support creation
run_qemu_img convert -O bochs -o help
echo echo
echo === amend: Options specified more than once === echo === amend: Options specified more than once ===
@ -201,6 +207,9 @@ run_qemu_img amend -f $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_I
run_qemu_img amend -f $IMGFMT -o help run_qemu_img amend -f $IMGFMT -o help
run_qemu_img convert -o help run_qemu_img convert -o help
# Try help option for a format that does not support amendment
run_qemu_img amend -f bochs -o help
# success, all done # success, all done
echo "*** done" echo "*** done"
rm -f $seq.full rm -f $seq.full

View File

@ -249,6 +249,9 @@ Testing: create -o help
Supported options: Supported options:
size Virtual disk size size Virtual disk size
Testing: create -f bochs -o help
qemu-img: Format driver 'bochs' does not support image creation
=== convert: Options specified more than once === === convert: Options specified more than once ===
Testing: create -f qcow2 TEST_DIR/t.qcow2 128M Testing: create -f qcow2 TEST_DIR/t.qcow2 128M
@ -502,6 +505,9 @@ Testing: convert -o help
Supported options: Supported options:
size Virtual disk size size Virtual disk size
Testing: convert -O bochs -o help
qemu-img: Format driver 'bochs' does not support image creation
=== amend: Options specified more than once === === amend: Options specified more than once ===
Testing: amend -f foo -f qcow2 -o lazy_refcounts=on TEST_DIR/t.qcow2 Testing: amend -f foo -f qcow2 -o lazy_refcounts=on TEST_DIR/t.qcow2
@ -546,7 +552,7 @@ cluster_size: 65536
=== amend: help for -o === === amend: help for -o ===
Testing: amend -f qcow2 -o help TEST_DIR/t.qcow2 Testing: amend -f qcow2 -o help TEST_DIR/t.qcow2
Supported options: Creation options for 'qcow2':
size Virtual disk size size Virtual disk size
compat Compatibility level (0.10 or 1.1) compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image backing_file File name of a base image
@ -564,10 +570,11 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full) preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2 Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2
Supported options: Creation options for 'qcow2':
size Virtual disk size size Virtual disk size
compat Compatibility level (0.10 or 1.1) compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image backing_file File name of a base image
@ -585,10 +592,11 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full) preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2
Supported options: Creation options for 'qcow2':
size Virtual disk size size Virtual disk size
compat Compatibility level (0.10 or 1.1) compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image backing_file File name of a base image
@ -606,10 +614,11 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full) preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2
Supported options: Creation options for 'qcow2':
size Virtual disk size size Virtual disk size
compat Compatibility level (0.10 or 1.1) compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image backing_file File name of a base image
@ -627,10 +636,11 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full) preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2
Supported options: Creation options for 'qcow2':
size Virtual disk size size Virtual disk size
compat Compatibility level (0.10 or 1.1) compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image backing_file File name of a base image
@ -648,10 +658,11 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full) preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2
Supported options: Creation options for 'qcow2':
size Virtual disk size size Virtual disk size
compat Compatibility level (0.10 or 1.1) compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image backing_file File name of a base image
@ -669,10 +680,11 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full) preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2
Supported options: Creation options for 'qcow2':
size Virtual disk size size Virtual disk size
compat Compatibility level (0.10 or 1.1) compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image backing_file File name of a base image
@ -690,10 +702,11 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full) preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2
Supported options: Creation options for 'qcow2':
size Virtual disk size size Virtual disk size
compat Compatibility level (0.10 or 1.1) compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image backing_file File name of a base image
@ -711,7 +724,8 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full) preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2
@ -731,7 +745,7 @@ Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,, -o help TEST_DIR/
qemu-img: Invalid option list: ,, qemu-img: Invalid option list: ,,
Testing: amend -f qcow2 -o help Testing: amend -f qcow2 -o help
Supported options: Creation options for 'qcow2':
size Virtual disk size size Virtual disk size
compat Compatibility level (0.10 or 1.1) compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image backing_file File name of a base image
@ -750,7 +764,12 @@ preallocation Preallocation mode (allowed values: off, metadata, falloc, full
lazy_refcounts Postpone refcount updates lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits refcount_bits Width of a reference count entry in bits
Note that not all of these options may be amendable.
Testing: convert -o help Testing: convert -o help
Supported options: Supported options:
size Virtual disk size size Virtual disk size
Testing: amend -f bochs -o help
qemu-img: Format driver 'bochs' does not support option amendment
*** done *** done

View File

@ -99,13 +99,11 @@ refcount bits: 64
=== Amend to compat=0.10 === === Amend to compat=0.10 ===
qemu-img: compat=0.10 requires refcount_bits=16 qemu-img: compat=0.10 requires refcount_bits=16
qemu-img: Error while amending options: Operation not supported
refcount bits: 64 refcount bits: 64
No errors were found on the image. No errors were found on the image.
refcount bits: 16 refcount bits: 16
refcount bits: 16 refcount bits: 16
qemu-img: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) qemu-img: Refcount widths other than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater)
qemu-img: Error while amending options: Invalid argument
refcount bits: 16 refcount bits: 16
=== Amend with snapshot === === Amend with snapshot ===
@ -113,7 +111,6 @@ refcount bits: 16
wrote 16777216/16777216 bytes at offset 0 wrote 16777216/16777216 bytes at offset 0
16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qemu-img: Cannot decrease refcount entry width to 1 bits: Cluster at offset 0x50000 has a refcount of 2 qemu-img: Cannot decrease refcount entry width to 1 bits: Cluster at offset 0x50000 has a refcount of 2
qemu-img: Error while amending options: Invalid argument
No errors were found on the image. No errors were found on the image.
refcount bits: 16 refcount bits: 16
No errors were found on the image. No errors were found on the image.

View File

@ -38,16 +38,17 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.rc . ./common.rc
. ./common.filter . ./common.filter
# We can only test one format here because we need its sample file # Some of these test cases use bochs, but others do use raw, so this
_supported_fmt bochs # is only half a lie.
_supported_proto nbd _supported_fmt raw
_supported_proto file
_supported_os Linux _supported_os Linux
echo echo
echo '=== Unsupported image creation in qemu-img create ===' echo '=== Unsupported image creation in qemu-img create ==='
echo echo
$QEMU_IMG create -f $IMGFMT nbd://example.com 2>&1 64M | _filter_imgfmt $QEMU_IMG create -f bochs nbd://example.com 2>&1 64M
echo echo
echo '=== Unsupported image creation in qemu-img convert ===' echo '=== Unsupported image creation in qemu-img convert ==='
@ -56,17 +57,15 @@ echo
# We could use any input image format here, but this is a bochs test, so just # We could use any input image format here, but this is a bochs test, so just
# use the bochs image # use the bochs image
_use_sample_img empty.bochs.bz2 _use_sample_img empty.bochs.bz2
$QEMU_IMG convert -f $IMGFMT -O $IMGFMT "$TEST_IMG" nbd://example.com 2>&1 \ $QEMU_IMG convert -f bochs -O bochs "$TEST_IMG" nbd://example.com
| _filter_imgfmt
echo echo
echo '=== Unsupported format in qemu-img amend ===' echo '=== Unsupported format in qemu-img amend ==='
echo echo
# The protocol does not matter here TEST_IMG="$TEST_DIR/t.$IMGFMT"
_use_sample_img empty.bochs.bz2 _make_test_img 1M
$QEMU_IMG amend -f $IMGFMT -o foo=bar "$TEST_IMG" 2>&1 | _filter_imgfmt $QEMU_IMG amend -f $IMGFMT -o size=2M "$TEST_IMG" 2>&1 | _filter_imgfmt
# success, all done # success, all done
echo echo

View File

@ -2,14 +2,15 @@ QA output created by 113
=== Unsupported image creation in qemu-img create === === Unsupported image creation in qemu-img create ===
qemu-img: nbd://example.com: Format driver 'IMGFMT' does not support image creation qemu-img: nbd://example.com: Format driver 'bochs' does not support image creation
=== Unsupported image creation in qemu-img convert === === Unsupported image creation in qemu-img convert ===
qemu-img: Format driver 'IMGFMT' does not support image creation qemu-img: Format driver 'bochs' does not support image creation
=== Unsupported format in qemu-img amend === === Unsupported format in qemu-img amend ===
qemu-img: Format driver 'IMGFMT' does not support any options to amend Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
qemu-img: Format driver 'IMGFMT' does not support option amendment
*** done *** done

View File

@ -76,6 +76,48 @@ $QEMU_IMG convert -O $IMGFMT -c -B "$TEST_IMG".base "$TEST_IMG" "$TEST_IMG".orig
$QEMU_IO -c "read -P 0 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir $QEMU_IO -c "read -P 0 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "=== Converting to an overlay larger than its backing file ==="
echo
TEST_IMG="$TEST_IMG".base _make_test_img 256M
# Needs to be at least how much an L2 table covers
# (64 kB/entry * 64 kB / 8 B/entry = 512 MB)
# That way, qcow2 will yield at least two status request responses.
# With just a single response, it would always say "Allocated in the
# backing file", so the optimization qemu-img convert tries to do is
# done automatically. Once it has to be queried twice, however (and
# one of the queries is completely after the end of the backing file),
# the block layer will automatically add a ZERO flag that qemu-img
# convert used to follow up with a zero write to the target.
# We do not want such a zero write, however, because we are past the
# end of the backing file on the target as well, so we do not need to
# write anything there.
_make_test_img -b "$TEST_IMG".base 768M
# Use compat=0.10 as the output so there is no zero cluster support
$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -o compat=0.10 \
"$TEST_IMG" "$TEST_IMG".orig
# See that nothing has been allocated past 64M
$QEMU_IMG map "$TEST_IMG".orig | _filter_qemu_img_map
echo
# Just before the end of the backing file
$QEMU_IO -c 'write -P 0x11 255M 1M' "$TEST_IMG".base 2>&1 | _filter_qemu_io
# Somewhere in the second L2 table
$QEMU_IO -c 'write -P 0x22 600M 1M' "$TEST_IMG" 2>&1 | _filter_qemu_io
$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -o compat=0.10 \
"$TEST_IMG" "$TEST_IMG".orig
$QEMU_IMG map "$TEST_IMG".orig | _filter_qemu_img_map
$QEMU_IO -c 'read -P 0x11 255M 1M' \
-c 'read -P 0x22 600M 1M' \
"$TEST_IMG".orig \
| _filter_qemu_io
echo echo
echo "=== Concatenate multiple source images ===" echo "=== Concatenate multiple source images ==="
echo echo

View File

@ -28,6 +28,24 @@ read 3145728/3145728 bytes at offset 0
read 3145728/3145728 bytes at offset 0 read 3145728/3145728 bytes at offset 0
3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Converting to an overlay larger than its backing file ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=268435456
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=805306368 backing_file=TEST_DIR/t.IMGFMT.base
Offset Length File
wrote 1048576/1048576 bytes at offset 267386880
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 1048576/1048576 bytes at offset 629145600
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Offset Length File
0xff00000 0x100000 TEST_DIR/t.IMGFMT.base
0x25800000 0x100000 TEST_DIR/t.IMGFMT.orig
read 1048576/1048576 bytes at offset 267386880
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 1048576/1048576 bytes at offset 629145600
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Concatenate multiple source images === === Concatenate multiple source images ===
Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=4194304 Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=4194304

View File

@ -137,6 +137,24 @@ for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do
_run_cmd $QEMU_IMG dd $L if="${TEST_IMG}" of="${TEST_IMG}.convert" bs=512 count=1 _run_cmd $QEMU_IMG dd $L if="${TEST_IMG}" of="${TEST_IMG}.convert" bs=512 count=1
_run_cmd $QEMU_IMG bench $L -c 1 "${TEST_IMG}" _run_cmd $QEMU_IMG bench $L -c 1 "${TEST_IMG}"
_run_cmd $QEMU_IMG bench $L -w -c 1 "${TEST_IMG}" _run_cmd $QEMU_IMG bench $L -w -c 1 "${TEST_IMG}"
# qemu-img create does not support -U
if [ -z "$L" ]; then
_run_cmd $QEMU_IMG create -f $IMGFMT "${TEST_IMG}" \
-b ${TEST_IMG}.base
# Read the file format. It used to be the case that
# file-posix simply truncated the file, but the qcow2
# driver then failed to format it because it was unable
# to acquire the necessary WRITE permission. However, the
# truncation was already wrong, and the whole process
# resulted in the file being completely empty and thus its
# format would be detected to be raw.
# So we read it here to see that creation either completed
# successfully (thus the format is qcow2) or it aborted
# before the file was changed at all (thus the format stays
# qcow2).
_img_info -U | grep 'file format'
fi
done done
_send_qemu_cmd $h "{ 'execute': 'quit', }" "" _send_qemu_cmd $h "{ 'execute': 'quit', }" ""
echo echo

View File

@ -92,6 +92,11 @@ _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
Is another process using the image? Is another process using the image?
_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock
Is another process using the image?
file format: IMGFMT
== Running utility commands -U == == Running utility commands -U ==
_qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2 _qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2
@ -209,6 +214,11 @@ _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
Is another process using the image? Is another process using the image?
_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock
Is another process using the image?
file format: IMGFMT
== Running utility commands -U == == Running utility commands -U ==
_qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2 _qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2
@ -309,6 +319,9 @@ _qemu_img_wrapper bench -c 1 TEST_DIR/t.qcow2
_qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2 _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2
_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
file format: IMGFMT
== Running utility commands -U == == Running utility commands -U ==
_qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2 _qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2

View File

@ -20,7 +20,7 @@
# Creator/Owner: Max Reitz <mreitz@redhat.com> # Creator/Owner: Max Reitz <mreitz@redhat.com>
import iotests import iotests
from iotests import log, qemu_img_pipe, qemu_io, filter_qemu_io from iotests import log, qemu_img, qemu_io_silent
# Need backing file support # Need backing file support
iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk']) iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk'])
@ -50,14 +50,13 @@ with iotests.FilePath('base.img') as base_img_path, \
log('--- Setting up images ---') log('--- Setting up images ---')
log('') log('')
qemu_img_pipe('create', '-f', iotests.imgfmt, base_img_path, '64M') assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0
assert qemu_io_silent(base_img_path, '-c', 'write -P 1 0M 1M') == 0
assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path,
top_img_path) == 0
assert qemu_io_silent(top_img_path, '-c', 'write -P 2 1M 1M') == 0
log(filter_qemu_io(qemu_io(base_img_path, '-c', 'write -P 1 0M 1M'))) log('Done')
qemu_img_pipe('create', '-f', iotests.imgfmt, '-b', base_img_path,
top_img_path)
log(filter_qemu_io(qemu_io(top_img_path, '-c', 'write -P 2 1M 1M')))
log('') log('')
log('--- Doing COR ---') log('--- Doing COR ---')
@ -110,6 +109,8 @@ with iotests.FilePath('base.img') as base_img_path, \
log('--- Checking COR result ---') log('--- Checking COR result ---')
log('') log('')
log(filter_qemu_io(qemu_io(base_img_path, '-c', 'discard 0 64M'))) assert qemu_io_silent(base_img_path, '-c', 'discard 0 64M') == 0
log(filter_qemu_io(qemu_io(top_img_path, '-c', 'read -P 1 0M 1M'))) assert qemu_io_silent(top_img_path, '-c', 'read -P 1 0M 1M') == 0
log(filter_qemu_io(qemu_io(top_img_path, '-c', 'read -P 2 1M 1M'))) assert qemu_io_silent(top_img_path, '-c', 'read -P 2 1M 1M') == 0
log('Done')

View File

@ -3,12 +3,7 @@
--- Setting up images --- --- Setting up images ---
wrote 1048576/1048576 bytes at offset 0 Done
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 1048576/1048576 bytes at offset 1048576
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
--- Doing COR --- --- Doing COR ---
@ -17,12 +12,4 @@ wrote 1048576/1048576 bytes at offset 1048576
--- Checking COR result --- --- Checking COR result ---
discard 67108864/67108864 bytes at offset 0 Done
64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 1048576/1048576 bytes at offset 0
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 1048576/1048576 bytes at offset 1048576
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)

90
tests/qemu-iotests/217 Executable file
View File

@ -0,0 +1,90 @@
#!/bin/bash
#
# I/O errors when working with internal qcow2 snapshots, and repairing
# the result
#
# Copyright (C) 2018 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
seq=$(basename $0)
echo "QA output created by $seq"
status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
rm -f "$TEST_DIR/blkdebug.conf"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
# This test is specific to qcow2
_supported_fmt qcow2
_supported_proto file
_supported_os Linux
# This test needs clusters with at least a refcount of 2 so that
# OFLAG_COPIED is not set. refcount_bits=1 is therefore unsupported.
_unsupported_imgopts 'refcount_bits=1[^0-9]'
echo
echo '=== Simulating an I/O error during snapshot deletion ==='
echo
_make_test_img 64M
$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io
# Create the snapshot
$QEMU_IMG snapshot -c foo "$TEST_IMG"
# Verify the snapshot is there
echo
_img_info | grep 'Snapshot list'
echo '(Snapshot filtered)'
echo
# Try to delete the snapshot (with an error happening when freeing the
# then leaked clusters)
cat > "$TEST_DIR/blkdebug.conf" <<EOF
[inject-error]
event = "cluster_free"
errno = "5"
EOF
$QEMU_IMG snapshot -d foo "blkdebug:$TEST_DIR/blkdebug.conf:$TEST_IMG"
# Verify the snapshot is gone
echo
_img_info | grep 'Snapshot list'
# Should only show leaks
echo '--- Checking test image ---'
_check_test_img
echo
# As there are only leaks, this should be able to fully repair the
# image
echo '--- Repairing test image ---'
_check_test_img -r leaks
# success, all done
echo '*** done'
rm -f $seq.full
status=0

View File

@ -0,0 +1,42 @@
QA output created by 217
=== Simulating an I/O error during snapshot deletion ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Snapshot list:
(Snapshot filtered)
qcow2_free_clusters failed: Input/output error
qemu-img: Could not delete snapshot 'foo': Failed to free the cluster and L1 table: Input/output error
--- Checking test image ---
Leaked cluster 4 refcount=2 reference=1
Leaked cluster 5 refcount=2 reference=1
Leaked cluster 6 refcount=1 reference=0
Leaked cluster 7 refcount=1 reference=0
4 leaked clusters were found on the image.
This means waste of disk space, but no harm to data.
--- Repairing test image ---
Leaked cluster 4 refcount=2 reference=1
Leaked cluster 5 refcount=2 reference=1
Leaked cluster 6 refcount=1 reference=0
Leaked cluster 7 refcount=1 reference=0
Repairing cluster 4 refcount=2 reference=1
Repairing cluster 5 refcount=2 reference=1
Repairing cluster 6 refcount=1 reference=0
Repairing cluster 7 refcount=1 reference=0
Repairing OFLAG_COPIED L2 cluster: l1_index=0 l1_entry=40000 refcount=1
Repairing OFLAG_COPIED data cluster: l2_entry=50000 refcount=1
The following inconsistencies were found and repaired:
4 leaked clusters
2 corruptions
Double checking the fixed image now...
No errors were found on the image.
*** done

View File

@ -42,11 +42,24 @@ def test_pause_resume(vm):
iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'})) iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'}))
pause_wait(vm, 'job0') pause_wait(vm, 'job0')
iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
iotests.log(vm.qmp('query-jobs')) result = vm.qmp('query-jobs')
iotests.log(result)
old_progress = result['return'][0]['current-progress']
total_progress = result['return'][0]['total-progress']
iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'})) iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'}))
iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
iotests.log(vm.qmp('query-jobs')) if old_progress < total_progress:
# Wait for the job to advance
while result['return'][0]['current-progress'] == old_progress:
result = vm.qmp('query-jobs')
iotests.log(result)
else:
# Already reached the end, so the job cannot advance
# any further; therefore, the query-jobs result can be
# logged immediately
iotests.log(vm.qmp('query-jobs'))
def test_job_lifecycle(vm, job, job_args, has_ready=False): def test_job_lifecycle(vm, job, job_args, has_ready=False):
iotests.log('') iotests.log('')
@ -58,12 +71,13 @@ def test_job_lifecycle(vm, job, job_args, has_ready=False):
iotests.log(vm.qmp(job, job_id='job0', **job_args)) iotests.log(vm.qmp(job, job_id='job0', **job_args))
# Depending on the storage, the first request may or may not have completed # Depending on the storage, the first request may or may not have completed
# yet, so filter out the progress. Later query-job calls don't need the # yet (and the total progress may not have been fully determined yet), so
# filtering because the progress is made deterministic by the block job # filter out the progress. Later query-job calls don't need the filtering
# speed # because the progress is made deterministic by the block job speed
result = vm.qmp('query-jobs') result = vm.qmp('query-jobs')
for j in result['return']: for j in result['return']:
del j['current-progress'] j['current-progress'] = 'FILTERED'
j['total-progress'] = 'FILTERED'
iotests.log(result) iotests.log(result)
# undefined -> created -> running # undefined -> created -> running

View File

@ -3,7 +3,7 @@ Launching VM...
Starting block job: drive-mirror (auto-finalize: True; auto-dismiss: True) Starting block job: drive-mirror (auto-finalize: True; auto-dismiss: True)
{u'return': {}} {u'return': {}}
{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} {u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'mirror'}]}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
@ -93,7 +93,7 @@ Waiting for PENDING state...
Starting block job: drive-backup (auto-finalize: True; auto-dismiss: True) Starting block job: drive-backup (auto-finalize: True; auto-dismiss: True)
{u'return': {}} {u'return': {}}
{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} {u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
@ -144,7 +144,7 @@ Waiting for PENDING state...
Starting block job: drive-backup (auto-finalize: True; auto-dismiss: False) Starting block job: drive-backup (auto-finalize: True; auto-dismiss: False)
{u'return': {}} {u'return': {}}
{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} {u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
@ -203,7 +203,7 @@ Waiting for PENDING state...
Starting block job: drive-backup (auto-finalize: False; auto-dismiss: True) Starting block job: drive-backup (auto-finalize: False; auto-dismiss: True)
{u'return': {}} {u'return': {}}
{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} {u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
@ -262,7 +262,7 @@ Waiting for PENDING state...
Starting block job: drive-backup (auto-finalize: False; auto-dismiss: False) Starting block job: drive-backup (auto-finalize: False; auto-dismiss: False)
{u'return': {}} {u'return': {}}
{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} {u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}

View File

@ -215,5 +215,6 @@
214 rw auto 214 rw auto
215 rw auto quick 215 rw auto quick
216 rw auto quick 216 rw auto quick
217 rw auto quick
218 rw auto quick 218 rw auto quick
219 rw auto 219 rw auto

View File

@ -133,6 +133,15 @@ def qemu_io(*args):
sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args))) sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args)))
return subp.communicate()[0] return subp.communicate()[0]
def qemu_io_silent(*args):
'''Run qemu-io and return the exit code, suppressing stdout'''
args = qemu_io_args + list(args)
exitcode = subprocess.call(args, stdout=open('/dev/null', 'w'))
if exitcode < 0:
sys.stderr.write('qemu-io received signal %i: %s\n' %
(-exitcode, ' '.join(args)))
return exitcode
class QemuIoInteractive: class QemuIoInteractive:
def __init__(self, *args): def __init__(self, *args):
@ -581,9 +590,14 @@ class QMPTestCase(unittest.TestCase):
with Timeout(1, "Timeout waiting for job to pause"): with Timeout(1, "Timeout waiting for job to pause"):
while True: while True:
result = self.vm.qmp('query-block-jobs') result = self.vm.qmp('query-block-jobs')
found = False
for job in result['return']: for job in result['return']:
if job['device'] == job_id and job['paused'] == True and job['busy'] == False: if job['device'] == job_id:
return job found = True
if job['paused'] == True and job['busy'] == False:
return job
break
assert found
def pause_job(self, job_id='job0', wait=True): def pause_job(self, job_id='job0', wait=True):
result = self.vm.qmp('block-job-pause', device=job_id) result = self.vm.qmp('block-job-pause', device=job_id)

View File

@ -220,7 +220,6 @@ void qemu_opts_print_help(QemuOptsList *list)
assert(list); assert(list);
desc = list->desc; desc = list->desc;
printf("Supported options:\n");
while (desc && desc->name) { while (desc && desc->name) {
printf("%-16s %s\n", desc->name, printf("%-16s %s\n", desc->name,
desc->help ? desc->help : "No description available"); desc->help ? desc->help : "No description available");