mirror of https://github.com/xemu-project/xemu.git
Block pull request
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJThdcTAAoJEJykq7OBq3PIH4UH/iKRrso84PjPirsYfQsga+2i Nx3FG2AnmCU5jYn0XjXBlq+K56BpAxuimhscPdtCpP2FeBEeFb53XfxyfUH8ybVs dACgd4MulFC9UUX06CnaSh011nG7tGELHrnAVOSqMKVSOc+GDv9yQhtN8r8bfqKx kt0ZWsZZ6iawbXgnd0L1mS22XANrGy1DlSWzUPL6WgS7ksJ0gg6EalEWfU4YXv+u k2xjVfEcHhmvlS+eS1xnI0RrJ9LAWmi0nS5BV7yRiy8+U+V30iADDKObWNIom+jm SYH4oT+asIYucl9CpXCa+5AA0tTt52oeBM28TYgJwQx8hEVLN6Td+N4q7RewKsM= =kKnv -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/stefanha/tags/block-pull-request' into staging Block pull request # gpg: Signature made Wed 28 May 2014 13:31:15 BST using RSA key ID 81AB73C8 # gpg: Can't check signature: public key not found * remotes/stefanha/tags/block-pull-request: (33 commits) block/sheepdog: Don't use qerror_report() block/sheepdog: Fix silent sd_open(), sd_create() failures block/sheepdog: Propagate errors to open and create methods block/sheepdog: Propagate errors through find_vdi_name() block/sheepdog: Propagate errors through do_sd_create() block/sheepdog: Propagate errors through sd_prealloc() block/sheepdog: Propagate errors through get_sheep_fd() block/sheepdog: Propagate errors through connect_to_sdog() block/vvfat: Propagate errors through init_directories() block/vvfat: Propagate errors through enable_write_target() block/ssh: Propagate errors to open and create methods block/ssh: Propagate errors through connect_to_ssh() block/ssh: Propagate errors through authenticate() block/ssh: Propagate errors through check_host_key() block/ssh: Drop superfluous libssh2_session_last_errno() calls block/rbd: Propagate errors to open and create methods qemu-nbd: Don't use qerror_report() blockdev: Don't use qerror_report() in do_drive_del() blockdev: Don't use qerror_report_err() in drive_init() docs: Define refcount_bits value ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
3ee933c9d4
|
@ -59,6 +59,7 @@ typedef struct BlkMigDevState {
|
|||
unsigned long *aio_bitmap;
|
||||
int64_t completed_sectors;
|
||||
BdrvDirtyBitmap *dirty_bitmap;
|
||||
Error *blocker;
|
||||
} BlkMigDevState;
|
||||
|
||||
typedef struct BlkMigBlock {
|
||||
|
@ -361,7 +362,8 @@ static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
|
|||
bmds->completed_sectors = 0;
|
||||
bmds->shared_base = block_mig_state.shared_base;
|
||||
alloc_aio_bitmap(bmds);
|
||||
bdrv_set_in_use(bs, 1);
|
||||
error_setg(&bmds->blocker, "block device is in use by migration");
|
||||
bdrv_op_block_all(bs, bmds->blocker);
|
||||
bdrv_ref(bs);
|
||||
|
||||
block_mig_state.total_sector_sum += sectors;
|
||||
|
@ -599,7 +601,8 @@ static void blk_mig_cleanup(void)
|
|||
blk_mig_lock();
|
||||
while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) {
|
||||
QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry);
|
||||
bdrv_set_in_use(bmds->bs, 0);
|
||||
bdrv_op_unblock_all(bmds->bs, bmds->blocker);
|
||||
error_free(bmds->blocker);
|
||||
bdrv_unref(bmds->bs);
|
||||
g_free(bmds->aio_bitmap);
|
||||
g_free(bmds);
|
||||
|
|
152
block.c
152
block.c
|
@ -335,6 +335,7 @@ void bdrv_register(BlockDriver *bdrv)
|
|||
BlockDriverState *bdrv_new(const char *device_name, Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
int i;
|
||||
|
||||
if (bdrv_find(device_name)) {
|
||||
error_setg(errp, "Device with id '%s' already exists",
|
||||
|
@ -353,6 +354,9 @@ BlockDriverState *bdrv_new(const char *device_name, Error **errp)
|
|||
if (device_name[0] != '\0') {
|
||||
QTAILQ_INSERT_TAIL(&bdrv_states, bs, device_list);
|
||||
}
|
||||
for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) {
|
||||
QLIST_INIT(&bs->op_blockers[i]);
|
||||
}
|
||||
bdrv_iostatus_disable(bs);
|
||||
notifier_list_init(&bs->close_notifiers);
|
||||
notifier_with_return_list_init(&bs->before_write_notifiers);
|
||||
|
@ -1090,6 +1094,37 @@ fail:
|
|||
return ret;
|
||||
}
|
||||
|
||||
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
|
||||
{
|
||||
|
||||
if (bs->backing_hd) {
|
||||
assert(bs->backing_blocker);
|
||||
bdrv_op_unblock_all(bs->backing_hd, bs->backing_blocker);
|
||||
} else if (backing_hd) {
|
||||
error_setg(&bs->backing_blocker,
|
||||
"device is used as backing hd of '%s'",
|
||||
bs->device_name);
|
||||
}
|
||||
|
||||
bs->backing_hd = backing_hd;
|
||||
if (!backing_hd) {
|
||||
error_free(bs->backing_blocker);
|
||||
bs->backing_blocker = NULL;
|
||||
goto out;
|
||||
}
|
||||
bs->open_flags &= ~BDRV_O_NO_BACKING;
|
||||
pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename);
|
||||
pstrcpy(bs->backing_format, sizeof(bs->backing_format),
|
||||
backing_hd->drv ? backing_hd->drv->format_name : "");
|
||||
|
||||
bdrv_op_block_all(bs->backing_hd, bs->backing_blocker);
|
||||
/* Otherwise we won't be able to commit due to check in bdrv_commit */
|
||||
bdrv_op_unblock(bs->backing_hd, BLOCK_OP_TYPE_COMMIT,
|
||||
bs->backing_blocker);
|
||||
out:
|
||||
bdrv_refresh_limits(bs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Opens the backing file for a BlockDriverState if not yet open
|
||||
*
|
||||
|
@ -1103,6 +1138,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
|
|||
char *backing_filename = g_malloc0(PATH_MAX);
|
||||
int ret = 0;
|
||||
BlockDriver *back_drv = NULL;
|
||||
BlockDriverState *backing_hd;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (bs->backing_hd != NULL) {
|
||||
|
@ -1125,30 +1161,26 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
|
|||
bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX);
|
||||
}
|
||||
|
||||
backing_hd = bdrv_new("", errp);
|
||||
|
||||
if (bs->backing_format[0] != '\0') {
|
||||
back_drv = bdrv_find_format(bs->backing_format);
|
||||
}
|
||||
|
||||
assert(bs->backing_hd == NULL);
|
||||
ret = bdrv_open(&bs->backing_hd,
|
||||
ret = bdrv_open(&backing_hd,
|
||||
*backing_filename ? backing_filename : NULL, NULL, options,
|
||||
bdrv_backing_flags(bs->open_flags), back_drv, &local_err);
|
||||
if (ret < 0) {
|
||||
bs->backing_hd = NULL;
|
||||
bdrv_unref(backing_hd);
|
||||
backing_hd = NULL;
|
||||
bs->open_flags |= BDRV_O_NO_BACKING;
|
||||
error_setg(errp, "Could not open backing file: %s",
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
goto free_exit;
|
||||
}
|
||||
|
||||
if (bs->backing_hd->file) {
|
||||
pstrcpy(bs->backing_file, sizeof(bs->backing_file),
|
||||
bs->backing_hd->file->filename);
|
||||
}
|
||||
|
||||
/* Recalculate the BlockLimits with the backing file */
|
||||
bdrv_refresh_limits(bs);
|
||||
bdrv_set_backing_hd(bs, backing_hd);
|
||||
|
||||
free_exit:
|
||||
g_free(backing_filename);
|
||||
|
@ -1784,8 +1816,9 @@ void bdrv_close(BlockDriverState *bs)
|
|||
|
||||
if (bs->drv) {
|
||||
if (bs->backing_hd) {
|
||||
bdrv_unref(bs->backing_hd);
|
||||
bs->backing_hd = NULL;
|
||||
BlockDriverState *backing_hd = bs->backing_hd;
|
||||
bdrv_set_backing_hd(bs, NULL);
|
||||
bdrv_unref(backing_hd);
|
||||
}
|
||||
bs->drv->bdrv_close(bs);
|
||||
g_free(bs->opaque);
|
||||
|
@ -1945,13 +1978,14 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
|
|||
bs_dest->refcnt = bs_src->refcnt;
|
||||
|
||||
/* job */
|
||||
bs_dest->in_use = bs_src->in_use;
|
||||
bs_dest->job = bs_src->job;
|
||||
|
||||
/* keep the same entry in bdrv_states */
|
||||
pstrcpy(bs_dest->device_name, sizeof(bs_dest->device_name),
|
||||
bs_src->device_name);
|
||||
bs_dest->device_list = bs_src->device_list;
|
||||
memcpy(bs_dest->op_blockers, bs_src->op_blockers,
|
||||
sizeof(bs_dest->op_blockers));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1986,7 +2020,6 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
|
|||
assert(QLIST_EMPTY(&bs_new->dirty_bitmaps));
|
||||
assert(bs_new->job == NULL);
|
||||
assert(bs_new->dev == NULL);
|
||||
assert(bs_new->in_use == 0);
|
||||
assert(bs_new->io_limits_enabled == false);
|
||||
assert(!throttle_have_timer(&bs_new->throttle_state));
|
||||
|
||||
|
@ -2005,7 +2038,6 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
|
|||
/* Check a few fields that should remain attached to the device */
|
||||
assert(bs_new->dev == NULL);
|
||||
assert(bs_new->job == NULL);
|
||||
assert(bs_new->in_use == 0);
|
||||
assert(bs_new->io_limits_enabled == false);
|
||||
assert(!throttle_have_timer(&bs_new->throttle_state));
|
||||
|
||||
|
@ -2038,19 +2070,14 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
|
|||
|
||||
/* The contents of 'tmp' will become bs_top, as we are
|
||||
* swapping bs_new and bs_top contents. */
|
||||
bs_top->backing_hd = bs_new;
|
||||
bs_top->open_flags &= ~BDRV_O_NO_BACKING;
|
||||
pstrcpy(bs_top->backing_file, sizeof(bs_top->backing_file),
|
||||
bs_new->filename);
|
||||
pstrcpy(bs_top->backing_format, sizeof(bs_top->backing_format),
|
||||
bs_new->drv ? bs_new->drv->format_name : "");
|
||||
bdrv_set_backing_hd(bs_top, bs_new);
|
||||
}
|
||||
|
||||
static void bdrv_delete(BlockDriverState *bs)
|
||||
{
|
||||
assert(!bs->dev);
|
||||
assert(!bs->job);
|
||||
assert(!bs->in_use);
|
||||
assert(bdrv_op_blocker_is_empty(bs));
|
||||
assert(!bs->refcnt);
|
||||
assert(QLIST_EMPTY(&bs->dirty_bitmaps));
|
||||
|
||||
|
@ -2232,7 +2259,8 @@ int bdrv_commit(BlockDriverState *bs)
|
|||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (bdrv_in_use(bs) || bdrv_in_use(bs->backing_hd)) {
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT, NULL) ||
|
||||
bdrv_op_is_blocked(bs->backing_hd, BLOCK_OP_TYPE_COMMIT, NULL)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
@ -2636,13 +2664,11 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
|
|||
if (ret) {
|
||||
goto exit;
|
||||
}
|
||||
new_top_bs->backing_hd = base_bs;
|
||||
|
||||
bdrv_refresh_limits(new_top_bs);
|
||||
bdrv_set_backing_hd(new_top_bs, base_bs);
|
||||
|
||||
QSIMPLEQ_FOREACH_SAFE(intermediate_state, &states_to_delete, entry, next) {
|
||||
/* so that bdrv_close() does not recursively close the chain */
|
||||
intermediate_state->bs->backing_hd = NULL;
|
||||
bdrv_set_backing_hd(intermediate_state->bs, NULL);
|
||||
bdrv_unref(intermediate_state->bs);
|
||||
}
|
||||
ret = 0;
|
||||
|
@ -3494,8 +3520,9 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset)
|
|||
return -ENOTSUP;
|
||||
if (bs->read_only)
|
||||
return -EACCES;
|
||||
if (bdrv_in_use(bs))
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, NULL)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
ret = drv->bdrv_truncate(bs, offset);
|
||||
if (ret == 0) {
|
||||
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
|
||||
|
@ -5325,15 +5352,74 @@ void bdrv_unref(BlockDriverState *bs)
|
|||
}
|
||||
}
|
||||
|
||||
void bdrv_set_in_use(BlockDriverState *bs, int in_use)
|
||||
struct BdrvOpBlocker {
|
||||
Error *reason;
|
||||
QLIST_ENTRY(BdrvOpBlocker) list;
|
||||
};
|
||||
|
||||
bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp)
|
||||
{
|
||||
assert(bs->in_use != in_use);
|
||||
bs->in_use = in_use;
|
||||
BdrvOpBlocker *blocker;
|
||||
assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX);
|
||||
if (!QLIST_EMPTY(&bs->op_blockers[op])) {
|
||||
blocker = QLIST_FIRST(&bs->op_blockers[op]);
|
||||
if (errp) {
|
||||
error_setg(errp, "Device '%s' is busy: %s",
|
||||
bs->device_name, error_get_pretty(blocker->reason));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int bdrv_in_use(BlockDriverState *bs)
|
||||
void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason)
|
||||
{
|
||||
return bs->in_use;
|
||||
BdrvOpBlocker *blocker;
|
||||
assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX);
|
||||
|
||||
blocker = g_malloc0(sizeof(BdrvOpBlocker));
|
||||
blocker->reason = reason;
|
||||
QLIST_INSERT_HEAD(&bs->op_blockers[op], blocker, list);
|
||||
}
|
||||
|
||||
void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, Error *reason)
|
||||
{
|
||||
BdrvOpBlocker *blocker, *next;
|
||||
assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX);
|
||||
QLIST_FOREACH_SAFE(blocker, &bs->op_blockers[op], list, next) {
|
||||
if (blocker->reason == reason) {
|
||||
QLIST_REMOVE(blocker, list);
|
||||
g_free(blocker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_op_block_all(BlockDriverState *bs, Error *reason)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) {
|
||||
bdrv_op_block(bs, i, reason);
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) {
|
||||
bdrv_op_unblock(bs, i, reason);
|
||||
}
|
||||
}
|
||||
|
||||
bool bdrv_op_blocker_is_empty(BlockDriverState *bs)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) {
|
||||
if (!QLIST_EMPTY(&bs->op_blockers[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void bdrv_iostatus_enable(BlockDriverState *bs)
|
||||
|
|
|
@ -498,7 +498,7 @@ immediate_exit:
|
|||
/* drop the bs loop chain formed by the swap: break the loop then
|
||||
* trigger the unref from the top one */
|
||||
BlockDriverState *p = s->base->backing_hd;
|
||||
s->base->backing_hd = NULL;
|
||||
bdrv_set_backing_hd(s->base, NULL);
|
||||
bdrv_unref(p);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -379,7 +379,8 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
|
|||
BLKDBG_EVENT(bs->file, BLKDBG_COW_READ);
|
||||
|
||||
if (!bs->drv) {
|
||||
return -ENOMEDIUM;
|
||||
ret = -ENOMEDIUM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Call .bdrv_co_readv() directly instead of using the public block-layer
|
||||
|
|
71
block/rbd.c
71
block/rbd.c
|
@ -105,7 +105,7 @@ typedef struct BDRVRBDState {
|
|||
static int qemu_rbd_next_tok(char *dst, int dst_len,
|
||||
char *src, char delim,
|
||||
const char *name,
|
||||
char **p)
|
||||
char **p, Error **errp)
|
||||
{
|
||||
int l;
|
||||
char *end;
|
||||
|
@ -128,10 +128,10 @@ static int qemu_rbd_next_tok(char *dst, int dst_len,
|
|||
}
|
||||
l = strlen(src);
|
||||
if (l >= dst_len) {
|
||||
error_report("%s too long", name);
|
||||
error_setg(errp, "%s too long", name);
|
||||
return -EINVAL;
|
||||
} else if (l == 0) {
|
||||
error_report("%s too short", name);
|
||||
error_setg(errp, "%s too short", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -157,13 +157,15 @@ static int qemu_rbd_parsename(const char *filename,
|
|||
char *pool, int pool_len,
|
||||
char *snap, int snap_len,
|
||||
char *name, int name_len,
|
||||
char *conf, int conf_len)
|
||||
char *conf, int conf_len,
|
||||
Error **errp)
|
||||
{
|
||||
const char *start;
|
||||
char *p, *buf;
|
||||
int ret;
|
||||
|
||||
if (!strstart(filename, "rbd:", &start)) {
|
||||
error_setg(errp, "File name must start with 'rbd:'");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -172,7 +174,8 @@ static int qemu_rbd_parsename(const char *filename,
|
|||
*snap = '\0';
|
||||
*conf = '\0';
|
||||
|
||||
ret = qemu_rbd_next_tok(pool, pool_len, p, '/', "pool name", &p);
|
||||
ret = qemu_rbd_next_tok(pool, pool_len, p,
|
||||
'/', "pool name", &p, errp);
|
||||
if (ret < 0 || !p) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
|
@ -180,21 +183,25 @@ static int qemu_rbd_parsename(const char *filename,
|
|||
qemu_rbd_unescape(pool);
|
||||
|
||||
if (strchr(p, '@')) {
|
||||
ret = qemu_rbd_next_tok(name, name_len, p, '@', "object name", &p);
|
||||
ret = qemu_rbd_next_tok(name, name_len, p,
|
||||
'@', "object name", &p, errp);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
ret = qemu_rbd_next_tok(snap, snap_len, p, ':', "snap name", &p);
|
||||
ret = qemu_rbd_next_tok(snap, snap_len, p,
|
||||
':', "snap name", &p, errp);
|
||||
qemu_rbd_unescape(snap);
|
||||
} else {
|
||||
ret = qemu_rbd_next_tok(name, name_len, p, ':', "object name", &p);
|
||||
ret = qemu_rbd_next_tok(name, name_len, p,
|
||||
':', "object name", &p, errp);
|
||||
}
|
||||
qemu_rbd_unescape(name);
|
||||
if (ret < 0 || !p) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = qemu_rbd_next_tok(conf, conf_len, p, '\0', "configuration", &p);
|
||||
ret = qemu_rbd_next_tok(conf, conf_len, p,
|
||||
'\0', "configuration", &p, errp);
|
||||
|
||||
done:
|
||||
g_free(buf);
|
||||
|
@ -229,7 +236,7 @@ static char *qemu_rbd_parse_clientname(const char *conf, char *clientname)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int qemu_rbd_set_conf(rados_t cluster, const char *conf)
|
||||
static int qemu_rbd_set_conf(rados_t cluster, const char *conf, Error **errp)
|
||||
{
|
||||
char *p, *buf;
|
||||
char name[RBD_MAX_CONF_NAME_SIZE];
|
||||
|
@ -241,20 +248,20 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf)
|
|||
|
||||
while (p) {
|
||||
ret = qemu_rbd_next_tok(name, sizeof(name), p,
|
||||
'=', "conf option name", &p);
|
||||
'=', "conf option name", &p, errp);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
qemu_rbd_unescape(name);
|
||||
|
||||
if (!p) {
|
||||
error_report("conf option %s has no value", name);
|
||||
error_setg(errp, "conf option %s has no value", name);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = qemu_rbd_next_tok(value, sizeof(value), p,
|
||||
':', "conf option value", &p);
|
||||
':', "conf option value", &p, errp);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
|
@ -263,7 +270,7 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf)
|
|||
if (strcmp(name, "conf") == 0) {
|
||||
ret = rados_conf_read_file(cluster, value);
|
||||
if (ret < 0) {
|
||||
error_report("error reading conf file %s", value);
|
||||
error_setg(errp, "error reading conf file %s", value);
|
||||
break;
|
||||
}
|
||||
} else if (strcmp(name, "id") == 0) {
|
||||
|
@ -271,7 +278,7 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf)
|
|||
} else {
|
||||
ret = rados_conf_set(cluster, name, value);
|
||||
if (ret < 0) {
|
||||
error_report("invalid conf option %s", name);
|
||||
error_setg(errp, "invalid conf option %s", name);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
@ -285,6 +292,7 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf)
|
|||
static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
int64_t bytes = 0;
|
||||
int64_t objsize;
|
||||
int obj_order = 0;
|
||||
|
@ -301,7 +309,8 @@ static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options,
|
|||
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
|
||||
snap_buf, sizeof(snap_buf),
|
||||
name, sizeof(name),
|
||||
conf, sizeof(conf)) < 0) {
|
||||
conf, sizeof(conf), &local_err) < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -313,11 +322,11 @@ static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options,
|
|||
if (options->value.n) {
|
||||
objsize = options->value.n;
|
||||
if ((objsize - 1) & objsize) { /* not a power of 2? */
|
||||
error_report("obj size needs to be power of 2");
|
||||
error_setg(errp, "obj size needs to be power of 2");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (objsize < 4096) {
|
||||
error_report("obj size too small");
|
||||
error_setg(errp, "obj size too small");
|
||||
return -EINVAL;
|
||||
}
|
||||
obj_order = ffs(objsize) - 1;
|
||||
|
@ -328,7 +337,7 @@ static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options,
|
|||
|
||||
clientname = qemu_rbd_parse_clientname(conf, clientname_buf);
|
||||
if (rados_create(&cluster, clientname) < 0) {
|
||||
error_report("error initializing");
|
||||
error_setg(errp, "error initializing");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -338,20 +347,20 @@ static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options,
|
|||
}
|
||||
|
||||
if (conf[0] != '\0' &&
|
||||
qemu_rbd_set_conf(cluster, conf) < 0) {
|
||||
error_report("error setting config options");
|
||||
qemu_rbd_set_conf(cluster, conf, &local_err) < 0) {
|
||||
rados_shutdown(cluster);
|
||||
error_propagate(errp, local_err);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rados_connect(cluster) < 0) {
|
||||
error_report("error connecting");
|
||||
error_setg(errp, "error connecting");
|
||||
rados_shutdown(cluster);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rados_ioctx_create(cluster, pool, &io_ctx) < 0) {
|
||||
error_report("error opening pool %s", pool);
|
||||
error_setg(errp, "error opening pool %s", pool);
|
||||
rados_shutdown(cluster);
|
||||
return -EIO;
|
||||
}
|
||||
|
@ -441,8 +450,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
error_propagate(errp, local_err);
|
||||
qemu_opts_del(opts);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -452,7 +460,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
|
||||
snap_buf, sizeof(snap_buf),
|
||||
s->name, sizeof(s->name),
|
||||
conf, sizeof(conf)) < 0) {
|
||||
conf, sizeof(conf), errp) < 0) {
|
||||
r = -EINVAL;
|
||||
goto failed_opts;
|
||||
}
|
||||
|
@ -460,7 +468,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
clientname = qemu_rbd_parse_clientname(conf, clientname_buf);
|
||||
r = rados_create(&s->cluster, clientname);
|
||||
if (r < 0) {
|
||||
error_report("error initializing");
|
||||
error_setg(&local_err, "error initializing");
|
||||
goto failed_opts;
|
||||
}
|
||||
|
||||
|
@ -488,28 +496,27 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
}
|
||||
|
||||
if (conf[0] != '\0') {
|
||||
r = qemu_rbd_set_conf(s->cluster, conf);
|
||||
r = qemu_rbd_set_conf(s->cluster, conf, errp);
|
||||
if (r < 0) {
|
||||
error_report("error setting config options");
|
||||
goto failed_shutdown;
|
||||
}
|
||||
}
|
||||
|
||||
r = rados_connect(s->cluster);
|
||||
if (r < 0) {
|
||||
error_report("error connecting");
|
||||
error_setg(&local_err, "error connecting");
|
||||
goto failed_shutdown;
|
||||
}
|
||||
|
||||
r = rados_ioctx_create(s->cluster, pool, &s->io_ctx);
|
||||
if (r < 0) {
|
||||
error_report("error opening pool %s", pool);
|
||||
error_setg(&local_err, "error opening pool %s", pool);
|
||||
goto failed_shutdown;
|
||||
}
|
||||
|
||||
r = rbd_open(s->io_ctx, s->name, &s->image, s->snap);
|
||||
if (r < 0) {
|
||||
error_report("error reading header from %s", s->name);
|
||||
error_setg(&local_err, "error reading header from %s", s->name);
|
||||
goto failed_open;
|
||||
}
|
||||
|
||||
|
|
149
block/sheepdog.c
149
block/sheepdog.c
|
@ -526,17 +526,16 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
|
|||
return acb;
|
||||
}
|
||||
|
||||
static int connect_to_sdog(BDRVSheepdogState *s)
|
||||
static int connect_to_sdog(BDRVSheepdogState *s, Error **errp)
|
||||
{
|
||||
int fd;
|
||||
Error *err = NULL;
|
||||
|
||||
if (s->is_unix) {
|
||||
fd = unix_connect(s->host_spec, &err);
|
||||
fd = unix_connect(s->host_spec, errp);
|
||||
} else {
|
||||
fd = inet_connect(s->host_spec, &err);
|
||||
fd = inet_connect(s->host_spec, errp);
|
||||
|
||||
if (err == NULL) {
|
||||
if (fd >= 0) {
|
||||
int ret = socket_set_nodelay(fd);
|
||||
if (ret < 0) {
|
||||
error_report("%s", strerror(errno));
|
||||
|
@ -544,10 +543,7 @@ static int connect_to_sdog(BDRVSheepdogState *s)
|
|||
}
|
||||
}
|
||||
|
||||
if (err != NULL) {
|
||||
qerror_report_err(err);
|
||||
error_free(err);
|
||||
} else {
|
||||
if (fd >= 0) {
|
||||
qemu_set_nonblock(fd);
|
||||
}
|
||||
|
||||
|
@ -672,7 +668,7 @@ static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
|
|||
enum AIOCBState aiocb_type);
|
||||
static void coroutine_fn resend_aioreq(BDRVSheepdogState *s, AIOReq *aio_req);
|
||||
static int reload_inode(BDRVSheepdogState *s, uint32_t snapid, const char *tag);
|
||||
static int get_sheep_fd(BDRVSheepdogState *s);
|
||||
static int get_sheep_fd(BDRVSheepdogState *s, Error **errp);
|
||||
static void co_write_request(void *opaque);
|
||||
|
||||
static AIOReq *find_pending_req(BDRVSheepdogState *s, uint64_t oid)
|
||||
|
@ -709,6 +705,7 @@ static void coroutine_fn send_pending_req(BDRVSheepdogState *s, uint64_t oid)
|
|||
|
||||
static coroutine_fn void reconnect_to_sdog(void *opaque)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
BDRVSheepdogState *s = opaque;
|
||||
AIOReq *aio_req, *next;
|
||||
|
||||
|
@ -723,9 +720,11 @@ static coroutine_fn void reconnect_to_sdog(void *opaque)
|
|||
|
||||
/* Try to reconnect the sheepdog server every one second. */
|
||||
while (s->fd < 0) {
|
||||
s->fd = get_sheep_fd(s);
|
||||
s->fd = get_sheep_fd(s, &local_err);
|
||||
if (s->fd < 0) {
|
||||
DPRINTF("Wait for connection to be established\n");
|
||||
error_report("%s", error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
co_aio_sleep_ns(bdrv_get_aio_context(s->bs), QEMU_CLOCK_REALTIME,
|
||||
1000000000ULL);
|
||||
}
|
||||
|
@ -914,11 +913,11 @@ static void co_write_request(void *opaque)
|
|||
* We cannot use this descriptor for other operations because
|
||||
* the block driver may be on waiting response from the server.
|
||||
*/
|
||||
static int get_sheep_fd(BDRVSheepdogState *s)
|
||||
static int get_sheep_fd(BDRVSheepdogState *s, Error **errp)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = connect_to_sdog(s);
|
||||
fd = connect_to_sdog(s, errp);
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
}
|
||||
|
@ -1061,7 +1060,7 @@ static int parse_vdiname(BDRVSheepdogState *s, const char *filename,
|
|||
|
||||
static int find_vdi_name(BDRVSheepdogState *s, const char *filename,
|
||||
uint32_t snapid, const char *tag, uint32_t *vid,
|
||||
bool lock)
|
||||
bool lock, Error **errp)
|
||||
{
|
||||
int ret, fd;
|
||||
SheepdogVdiReq hdr;
|
||||
|
@ -1069,7 +1068,7 @@ static int find_vdi_name(BDRVSheepdogState *s, const char *filename,
|
|||
unsigned int wlen, rlen = 0;
|
||||
char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN];
|
||||
|
||||
fd = connect_to_sdog(s);
|
||||
fd = connect_to_sdog(s, errp);
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
}
|
||||
|
@ -1095,12 +1094,13 @@ static int find_vdi_name(BDRVSheepdogState *s, const char *filename,
|
|||
|
||||
ret = do_req(fd, (SheepdogReq *)&hdr, buf, &wlen, &rlen);
|
||||
if (ret) {
|
||||
error_setg_errno(errp, -ret, "cannot get vdi info");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rsp->result != SD_RES_SUCCESS) {
|
||||
error_report("cannot get vdi info, %s, %s %" PRIu32 " %s",
|
||||
sd_strerror(rsp->result), filename, snapid, tag);
|
||||
error_setg(errp, "cannot get vdi info, %s, %s %" PRIu32 " %s",
|
||||
sd_strerror(rsp->result), filename, snapid, tag);
|
||||
if (rsp->result == SD_RES_NO_VDI) {
|
||||
ret = -ENOENT;
|
||||
} else {
|
||||
|
@ -1263,19 +1263,24 @@ static int write_object(int fd, char *buf, uint64_t oid, uint8_t copies,
|
|||
/* update inode with the latest state */
|
||||
static int reload_inode(BDRVSheepdogState *s, uint32_t snapid, const char *tag)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
SheepdogInode *inode;
|
||||
int ret = 0, fd;
|
||||
uint32_t vid = 0;
|
||||
|
||||
fd = connect_to_sdog(s);
|
||||
fd = connect_to_sdog(s, &local_err);
|
||||
if (fd < 0) {
|
||||
error_report("%s", error_get_pretty(local_err));;
|
||||
error_free(local_err);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
inode = g_malloc(sizeof(s->inode));
|
||||
|
||||
ret = find_vdi_name(s, s->name, snapid, tag, &vid, false);
|
||||
ret = find_vdi_name(s, s->name, snapid, tag, &vid, false, &local_err);
|
||||
if (ret) {
|
||||
error_report("%s", error_get_pretty(local_err));;
|
||||
error_free(local_err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -1386,8 +1391,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1408,15 +1412,16 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
ret = parse_vdiname(s, filename, vdi, &snapid, tag);
|
||||
}
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Can't parse filename");
|
||||
goto out;
|
||||
}
|
||||
s->fd = get_sheep_fd(s);
|
||||
s->fd = get_sheep_fd(s, errp);
|
||||
if (s->fd < 0) {
|
||||
ret = s->fd;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = find_vdi_name(s, vdi, snapid, tag, &vid, true);
|
||||
ret = find_vdi_name(s, vdi, snapid, tag, &vid, true, errp);
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
|
@ -1436,7 +1441,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
s->is_snapshot = true;
|
||||
}
|
||||
|
||||
fd = connect_to_sdog(s);
|
||||
fd = connect_to_sdog(s, errp);
|
||||
if (fd < 0) {
|
||||
ret = fd;
|
||||
goto out;
|
||||
|
@ -1449,6 +1454,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
closesocket(fd);
|
||||
|
||||
if (ret) {
|
||||
error_setg(errp, "Can't read snapshot inode");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -1472,7 +1478,8 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot)
|
||||
static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot,
|
||||
Error **errp)
|
||||
{
|
||||
SheepdogVdiReq hdr;
|
||||
SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
|
||||
|
@ -1480,7 +1487,7 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot)
|
|||
unsigned int wlen, rlen = 0;
|
||||
char buf[SD_MAX_VDI_LEN];
|
||||
|
||||
fd = connect_to_sdog(s);
|
||||
fd = connect_to_sdog(s, errp);
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
}
|
||||
|
@ -1510,11 +1517,12 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot)
|
|||
closesocket(fd);
|
||||
|
||||
if (ret) {
|
||||
error_setg_errno(errp, -ret, "create failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rsp->result != SD_RES_SUCCESS) {
|
||||
error_report("%s, %s", sd_strerror(rsp->result), s->inode.name);
|
||||
error_setg(errp, "%s, %s", sd_strerror(rsp->result), s->inode.name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -1525,21 +1533,18 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sd_prealloc(const char *filename)
|
||||
static int sd_prealloc(const char *filename, Error **errp)
|
||||
{
|
||||
BlockDriverState *bs = NULL;
|
||||
uint32_t idx, max_idx;
|
||||
int64_t vdi_size;
|
||||
void *buf = g_malloc0(SD_DATA_OBJ_SIZE);
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
NULL, &local_err);
|
||||
NULL, errp);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
goto out;
|
||||
goto out_with_err_set;
|
||||
}
|
||||
|
||||
vdi_size = bdrv_getlength(bs);
|
||||
|
@ -1563,7 +1568,12 @@ static int sd_prealloc(const char *filename)
|
|||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Can't pre-allocate");
|
||||
}
|
||||
out_with_err_set:
|
||||
if (bs) {
|
||||
bdrv_unref(bs);
|
||||
}
|
||||
|
@ -1636,7 +1646,6 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
|
|||
char tag[SD_MAX_VDI_TAG_LEN];
|
||||
uint32_t snapid;
|
||||
bool prealloc = false;
|
||||
Error *local_err = NULL;
|
||||
|
||||
s = g_malloc0(sizeof(BDRVSheepdogState));
|
||||
|
||||
|
@ -1647,6 +1656,7 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
|
|||
ret = parse_vdiname(s, filename, s->name, &snapid, tag);
|
||||
}
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Can't parse filename");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -1661,8 +1671,8 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
|
|||
} else if (!strcmp(options->value.s, "full")) {
|
||||
prealloc = true;
|
||||
} else {
|
||||
error_report("Invalid preallocation mode: '%s'",
|
||||
options->value.s);
|
||||
error_setg(errp, "Invalid preallocation mode: '%s'",
|
||||
options->value.s);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1670,6 +1680,8 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
|
|||
if (options->value.s) {
|
||||
ret = parse_redundancy(s, options->value.s);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Invalid redundancy mode: '%s'",
|
||||
options->value.s);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
@ -1678,7 +1690,7 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
|
|||
}
|
||||
|
||||
if (s->inode.vdi_size > SD_MAX_VDI_SIZE) {
|
||||
error_report("too big image size");
|
||||
error_setg(errp, "too big image size");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1691,24 +1703,22 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
|
|||
/* Currently, only Sheepdog backing image is supported. */
|
||||
drv = bdrv_find_protocol(backing_file, true);
|
||||
if (!drv || strcmp(drv->protocol_name, "sheepdog") != 0) {
|
||||
error_report("backing_file must be a sheepdog image");
|
||||
error_setg(errp, "backing_file must be a sheepdog image");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, NULL,
|
||||
&local_err);
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
base = bs->opaque;
|
||||
|
||||
if (!is_snapshot(&base->inode)) {
|
||||
error_report("cannot clone from a non snapshot vdi");
|
||||
error_setg(errp, "cannot clone from a non snapshot vdi");
|
||||
bdrv_unref(bs);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
|
@ -1717,12 +1727,14 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
|
|||
bdrv_unref(bs);
|
||||
}
|
||||
|
||||
ret = do_sd_create(s, &vid, 0);
|
||||
if (!prealloc || ret) {
|
||||
ret = do_sd_create(s, &vid, 0, errp);
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = sd_prealloc(filename);
|
||||
if (prealloc) {
|
||||
ret = sd_prealloc(filename, errp);
|
||||
}
|
||||
out:
|
||||
g_free(s);
|
||||
return ret;
|
||||
|
@ -1730,6 +1742,7 @@ out:
|
|||
|
||||
static void sd_close(BlockDriverState *bs)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
SheepdogVdiReq hdr;
|
||||
SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
|
||||
|
@ -1738,8 +1751,10 @@ static void sd_close(BlockDriverState *bs)
|
|||
|
||||
DPRINTF("%s\n", s->name);
|
||||
|
||||
fd = connect_to_sdog(s);
|
||||
fd = connect_to_sdog(s, &local_err);
|
||||
if (fd < 0) {
|
||||
error_report("%s", error_get_pretty(local_err));;
|
||||
error_free(local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1774,6 +1789,7 @@ static int64_t sd_getlength(BlockDriverState *bs)
|
|||
|
||||
static int sd_truncate(BlockDriverState *bs, int64_t offset)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
int ret, fd;
|
||||
unsigned int datalen;
|
||||
|
@ -1786,8 +1802,10 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
fd = connect_to_sdog(s);
|
||||
fd = connect_to_sdog(s, &local_err);
|
||||
if (fd < 0) {
|
||||
error_report("%s", error_get_pretty(local_err));;
|
||||
error_free(local_err);
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
@ -1846,6 +1864,7 @@ static void coroutine_fn sd_write_done(SheepdogAIOCB *acb)
|
|||
/* Delete current working VDI on the snapshot chain */
|
||||
static bool sd_delete(BDRVSheepdogState *s)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
unsigned int wlen = SD_MAX_VDI_LEN, rlen = 0;
|
||||
SheepdogVdiReq hdr = {
|
||||
.opcode = SD_OP_DEL_VDI,
|
||||
|
@ -1856,8 +1875,10 @@ static bool sd_delete(BDRVSheepdogState *s)
|
|||
SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
|
||||
int fd, ret;
|
||||
|
||||
fd = connect_to_sdog(s);
|
||||
fd = connect_to_sdog(s, &local_err);
|
||||
if (fd < 0) {
|
||||
error_report("%s", error_get_pretty(local_err));;
|
||||
error_free(local_err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1885,6 +1906,7 @@ static bool sd_delete(BDRVSheepdogState *s)
|
|||
*/
|
||||
static int sd_create_branch(BDRVSheepdogState *s)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
int ret, fd;
|
||||
uint32_t vid;
|
||||
char *buf;
|
||||
|
@ -1900,15 +1922,19 @@ static int sd_create_branch(BDRVSheepdogState *s)
|
|||
* false bail out.
|
||||
*/
|
||||
deleted = sd_delete(s);
|
||||
ret = do_sd_create(s, &vid, !deleted);
|
||||
ret = do_sd_create(s, &vid, !deleted, &local_err);
|
||||
if (ret) {
|
||||
error_report("%s", error_get_pretty(local_err));;
|
||||
error_free(local_err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
DPRINTF("%" PRIx32 " is created.\n", vid);
|
||||
|
||||
fd = connect_to_sdog(s);
|
||||
fd = connect_to_sdog(s, &local_err);
|
||||
if (fd < 0) {
|
||||
error_report("%s", error_get_pretty(local_err));;
|
||||
error_free(local_err);
|
||||
ret = fd;
|
||||
goto out;
|
||||
}
|
||||
|
@ -2122,6 +2148,7 @@ static int coroutine_fn sd_co_flush_to_disk(BlockDriverState *bs)
|
|||
|
||||
static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
int ret, fd;
|
||||
uint32_t new_vid;
|
||||
|
@ -2151,8 +2178,10 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
|||
datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id);
|
||||
|
||||
/* refresh inode. */
|
||||
fd = connect_to_sdog(s);
|
||||
fd = connect_to_sdog(s, &local_err);
|
||||
if (fd < 0) {
|
||||
error_report("%s", error_get_pretty(local_err));;
|
||||
error_free(local_err);
|
||||
ret = fd;
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -2164,8 +2193,10 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = do_sd_create(s, &new_vid, 1);
|
||||
ret = do_sd_create(s, &new_vid, 1, &local_err);
|
||||
if (ret < 0) {
|
||||
error_report("%s", error_get_pretty(local_err));;
|
||||
error_free(local_err);
|
||||
error_report("failed to create inode for snapshot. %s",
|
||||
strerror(errno));
|
||||
goto cleanup;
|
||||
|
@ -2249,6 +2280,7 @@ static int sd_snapshot_delete(BlockDriverState *bs,
|
|||
|
||||
static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
SheepdogReq req;
|
||||
int fd, nr = 1024, ret, max = BITS_TO_LONGS(SD_NR_VDIS) * sizeof(long);
|
||||
|
@ -2263,8 +2295,10 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
|||
|
||||
vdi_inuse = g_malloc(max);
|
||||
|
||||
fd = connect_to_sdog(s);
|
||||
fd = connect_to_sdog(s, &local_err);
|
||||
if (fd < 0) {
|
||||
error_report("%s", error_get_pretty(local_err));;
|
||||
error_free(local_err);
|
||||
ret = fd;
|
||||
goto out;
|
||||
}
|
||||
|
@ -2290,8 +2324,10 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
|||
hval = fnv_64a_buf(s->name, strlen(s->name), FNV1A_64_INIT);
|
||||
start_nr = hval & (SD_NR_VDIS - 1);
|
||||
|
||||
fd = connect_to_sdog(s);
|
||||
fd = connect_to_sdog(s, &local_err);
|
||||
if (fd < 0) {
|
||||
error_report("%s", error_get_pretty(local_err));;
|
||||
error_free(local_err);
|
||||
ret = fd;
|
||||
goto out;
|
||||
}
|
||||
|
@ -2341,6 +2377,7 @@ out:
|
|||
static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data,
|
||||
int64_t pos, int size, int load)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
bool create;
|
||||
int fd, ret = 0, remaining = size;
|
||||
unsigned int data_len;
|
||||
|
@ -2349,8 +2386,10 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data,
|
|||
uint32_t vdi_index;
|
||||
uint32_t vdi_id = load ? s->inode.parent_vdi_id : s->inode.vdi_id;
|
||||
|
||||
fd = connect_to_sdog(s);
|
||||
fd = connect_to_sdog(s, &local_err);
|
||||
if (fd < 0) {
|
||||
error_report("%s", error_get_pretty(local_err));;
|
||||
error_free(local_err);
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
|
151
block/ssh.c
151
block/ssh.c
|
@ -106,30 +106,59 @@ static void ssh_state_free(BDRVSSHState *s)
|
|||
}
|
||||
}
|
||||
|
||||
/* Wrappers around error_report which make sure to dump as much
|
||||
* information from libssh2 as possible.
|
||||
*/
|
||||
static void GCC_FMT_ATTR(2, 3)
|
||||
session_error_report(BDRVSSHState *s, const char *fs, ...)
|
||||
static void GCC_FMT_ATTR(3, 4)
|
||||
session_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *msg;
|
||||
|
||||
va_start(args, fs);
|
||||
error_vprintf(fs, args);
|
||||
msg = g_strdup_vprintf(fs, args);
|
||||
va_end(args);
|
||||
|
||||
if ((s)->session) {
|
||||
if (s->session) {
|
||||
char *ssh_err;
|
||||
int ssh_err_code;
|
||||
|
||||
libssh2_session_last_error((s)->session, &ssh_err, NULL, 0);
|
||||
/* This is not an errno. See <libssh2.h>. */
|
||||
ssh_err_code = libssh2_session_last_errno((s)->session);
|
||||
|
||||
error_printf(": %s (libssh2 error code: %d)", ssh_err, ssh_err_code);
|
||||
ssh_err_code = libssh2_session_last_error(s->session,
|
||||
&ssh_err, NULL, 0);
|
||||
error_setg(errp, "%s: %s (libssh2 error code: %d)",
|
||||
msg, ssh_err, ssh_err_code);
|
||||
} else {
|
||||
error_setg(errp, "%s", msg);
|
||||
}
|
||||
g_free(msg);
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR(3, 4)
|
||||
sftp_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *msg;
|
||||
|
||||
va_start(args, fs);
|
||||
msg = g_strdup_vprintf(fs, args);
|
||||
va_end(args);
|
||||
error_printf("\n");
|
||||
|
||||
if (s->sftp) {
|
||||
char *ssh_err;
|
||||
int ssh_err_code;
|
||||
unsigned long sftp_err_code;
|
||||
|
||||
/* This is not an errno. See <libssh2.h>. */
|
||||
ssh_err_code = libssh2_session_last_error(s->session,
|
||||
&ssh_err, NULL, 0);
|
||||
/* See <libssh2_sftp.h>. */
|
||||
sftp_err_code = libssh2_sftp_last_error((s)->sftp);
|
||||
|
||||
error_setg(errp,
|
||||
"%s: %s (libssh2 error code: %d, sftp error code: %lu)",
|
||||
msg, ssh_err, ssh_err_code, sftp_err_code);
|
||||
} else {
|
||||
error_setg(errp, "%s", msg);
|
||||
}
|
||||
g_free(msg);
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR(2, 3)
|
||||
|
@ -145,9 +174,9 @@ sftp_error_report(BDRVSSHState *s, const char *fs, ...)
|
|||
int ssh_err_code;
|
||||
unsigned long sftp_err_code;
|
||||
|
||||
libssh2_session_last_error((s)->session, &ssh_err, NULL, 0);
|
||||
/* This is not an errno. See <libssh2.h>. */
|
||||
ssh_err_code = libssh2_session_last_errno((s)->session);
|
||||
ssh_err_code = libssh2_session_last_error(s->session,
|
||||
&ssh_err, NULL, 0);
|
||||
/* See <libssh2_sftp.h>. */
|
||||
sftp_err_code = libssh2_sftp_last_error((s)->sftp);
|
||||
|
||||
|
@ -243,7 +272,7 @@ static void ssh_parse_filename(const char *filename, QDict *options,
|
|||
}
|
||||
|
||||
static int check_host_key_knownhosts(BDRVSSHState *s,
|
||||
const char *host, int port)
|
||||
const char *host, int port, Error **errp)
|
||||
{
|
||||
const char *home;
|
||||
char *knh_file = NULL;
|
||||
|
@ -257,14 +286,15 @@ static int check_host_key_knownhosts(BDRVSSHState *s,
|
|||
hostkey = libssh2_session_hostkey(s->session, &len, &type);
|
||||
if (!hostkey) {
|
||||
ret = -EINVAL;
|
||||
session_error_report(s, "failed to read remote host key");
|
||||
session_error_setg(errp, s, "failed to read remote host key");
|
||||
goto out;
|
||||
}
|
||||
|
||||
knh = libssh2_knownhost_init(s->session);
|
||||
if (!knh) {
|
||||
ret = -EINVAL;
|
||||
session_error_report(s, "failed to initialize known hosts support");
|
||||
session_error_setg(errp, s,
|
||||
"failed to initialize known hosts support");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -289,21 +319,23 @@ static int check_host_key_knownhosts(BDRVSSHState *s,
|
|||
break;
|
||||
case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
|
||||
ret = -EINVAL;
|
||||
session_error_report(s, "host key does not match the one in known_hosts (found key %s)",
|
||||
found->key);
|
||||
session_error_setg(errp, s,
|
||||
"host key does not match the one in known_hosts"
|
||||
" (found key %s)", found->key);
|
||||
goto out;
|
||||
case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
|
||||
ret = -EINVAL;
|
||||
session_error_report(s, "no host key was found in known_hosts");
|
||||
session_error_setg(errp, s, "no host key was found in known_hosts");
|
||||
goto out;
|
||||
case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
|
||||
ret = -EINVAL;
|
||||
session_error_report(s, "failure matching the host key with known_hosts");
|
||||
session_error_setg(errp, s,
|
||||
"failure matching the host key with known_hosts");
|
||||
goto out;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
session_error_report(s, "unknown error matching the host key with known_hosts (%d)",
|
||||
r);
|
||||
session_error_setg(errp, s, "unknown error matching the host key"
|
||||
" with known_hosts (%d)", r);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -358,20 +390,20 @@ static int compare_fingerprint(const unsigned char *fingerprint, size_t len,
|
|||
|
||||
static int
|
||||
check_host_key_hash(BDRVSSHState *s, const char *hash,
|
||||
int hash_type, size_t fingerprint_len)
|
||||
int hash_type, size_t fingerprint_len, Error **errp)
|
||||
{
|
||||
const char *fingerprint;
|
||||
|
||||
fingerprint = libssh2_hostkey_hash(s->session, hash_type);
|
||||
if (!fingerprint) {
|
||||
session_error_report(s, "failed to read remote host key");
|
||||
session_error_setg(errp, s, "failed to read remote host key");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if(compare_fingerprint((unsigned char *) fingerprint, fingerprint_len,
|
||||
hash) != 0) {
|
||||
error_report("remote host key does not match host_key_check '%s'",
|
||||
hash);
|
||||
error_setg(errp, "remote host key does not match host_key_check '%s'",
|
||||
hash);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
|
@ -379,7 +411,7 @@ check_host_key_hash(BDRVSSHState *s, const char *hash,
|
|||
}
|
||||
|
||||
static int check_host_key(BDRVSSHState *s, const char *host, int port,
|
||||
const char *host_key_check)
|
||||
const char *host_key_check, Error **errp)
|
||||
{
|
||||
/* host_key_check=no */
|
||||
if (strcmp(host_key_check, "no") == 0) {
|
||||
|
@ -389,25 +421,25 @@ static int check_host_key(BDRVSSHState *s, const char *host, int port,
|
|||
/* host_key_check=md5:xx:yy:zz:... */
|
||||
if (strncmp(host_key_check, "md5:", 4) == 0) {
|
||||
return check_host_key_hash(s, &host_key_check[4],
|
||||
LIBSSH2_HOSTKEY_HASH_MD5, 16);
|
||||
LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
|
||||
}
|
||||
|
||||
/* host_key_check=sha1:xx:yy:zz:... */
|
||||
if (strncmp(host_key_check, "sha1:", 5) == 0) {
|
||||
return check_host_key_hash(s, &host_key_check[5],
|
||||
LIBSSH2_HOSTKEY_HASH_SHA1, 20);
|
||||
LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
|
||||
}
|
||||
|
||||
/* host_key_check=yes */
|
||||
if (strcmp(host_key_check, "yes") == 0) {
|
||||
return check_host_key_knownhosts(s, host, port);
|
||||
return check_host_key_knownhosts(s, host, port, errp);
|
||||
}
|
||||
|
||||
error_report("unknown host_key_check setting (%s)", host_key_check);
|
||||
error_setg(errp, "unknown host_key_check setting (%s)", host_key_check);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int authenticate(BDRVSSHState *s, const char *user)
|
||||
static int authenticate(BDRVSSHState *s, const char *user, Error **errp)
|
||||
{
|
||||
int r, ret;
|
||||
const char *userauthlist;
|
||||
|
@ -418,7 +450,8 @@ static int authenticate(BDRVSSHState *s, const char *user)
|
|||
userauthlist = libssh2_userauth_list(s->session, user, strlen(user));
|
||||
if (strstr(userauthlist, "publickey") == NULL) {
|
||||
ret = -EPERM;
|
||||
error_report("remote server does not support \"publickey\" authentication");
|
||||
error_setg(errp,
|
||||
"remote server does not support \"publickey\" authentication");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -426,17 +459,18 @@ static int authenticate(BDRVSSHState *s, const char *user)
|
|||
agent = libssh2_agent_init(s->session);
|
||||
if (!agent) {
|
||||
ret = -EINVAL;
|
||||
session_error_report(s, "failed to initialize ssh-agent support");
|
||||
session_error_setg(errp, s, "failed to initialize ssh-agent support");
|
||||
goto out;
|
||||
}
|
||||
if (libssh2_agent_connect(agent)) {
|
||||
ret = -ECONNREFUSED;
|
||||
session_error_report(s, "failed to connect to ssh-agent");
|
||||
session_error_setg(errp, s, "failed to connect to ssh-agent");
|
||||
goto out;
|
||||
}
|
||||
if (libssh2_agent_list_identities(agent)) {
|
||||
ret = -EINVAL;
|
||||
session_error_report(s, "failed requesting identities from ssh-agent");
|
||||
session_error_setg(errp, s,
|
||||
"failed requesting identities from ssh-agent");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -447,7 +481,8 @@ static int authenticate(BDRVSSHState *s, const char *user)
|
|||
}
|
||||
if (r < 0) {
|
||||
ret = -EINVAL;
|
||||
session_error_report(s, "failed to obtain identity from ssh-agent");
|
||||
session_error_setg(errp, s,
|
||||
"failed to obtain identity from ssh-agent");
|
||||
goto out;
|
||||
}
|
||||
r = libssh2_agent_userauth(agent, user, identity);
|
||||
|
@ -461,8 +496,8 @@ static int authenticate(BDRVSSHState *s, const char *user)
|
|||
}
|
||||
|
||||
ret = -EPERM;
|
||||
error_report("failed to authenticate using publickey authentication "
|
||||
"and the identities held by your ssh-agent");
|
||||
error_setg(errp, "failed to authenticate using publickey authentication "
|
||||
"and the identities held by your ssh-agent");
|
||||
|
||||
out:
|
||||
if (agent != NULL) {
|
||||
|
@ -476,10 +511,9 @@ static int authenticate(BDRVSSHState *s, const char *user)
|
|||
}
|
||||
|
||||
static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||
int ssh_flags, int creat_mode)
|
||||
int ssh_flags, int creat_mode, Error **errp)
|
||||
{
|
||||
int r, ret;
|
||||
Error *err = NULL;
|
||||
const char *host, *user, *path, *host_key_check;
|
||||
int port;
|
||||
|
||||
|
@ -498,6 +532,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
|||
} else {
|
||||
user = g_get_user_name();
|
||||
if (!user) {
|
||||
error_setg_errno(errp, errno, "Can't get user name");
|
||||
ret = -errno;
|
||||
goto err;
|
||||
}
|
||||
|
@ -514,11 +549,9 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
|||
s->hostport = g_strdup_printf("%s:%d", host, port);
|
||||
|
||||
/* Open the socket and connect. */
|
||||
s->sock = inet_connect(s->hostport, &err);
|
||||
if (err != NULL) {
|
||||
s->sock = inet_connect(s->hostport, errp);
|
||||
if (s->sock < 0) {
|
||||
ret = -errno;
|
||||
qerror_report_err(err);
|
||||
error_free(err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
@ -526,7 +559,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
|||
s->session = libssh2_session_init();
|
||||
if (!s->session) {
|
||||
ret = -EINVAL;
|
||||
session_error_report(s, "failed to initialize libssh2 session");
|
||||
session_error_setg(errp, s, "failed to initialize libssh2 session");
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
@ -537,18 +570,18 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
|||
r = libssh2_session_handshake(s->session, s->sock);
|
||||
if (r != 0) {
|
||||
ret = -EINVAL;
|
||||
session_error_report(s, "failed to establish SSH session");
|
||||
session_error_setg(errp, s, "failed to establish SSH session");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Check the remote host's key against known_hosts. */
|
||||
ret = check_host_key(s, host, port, host_key_check);
|
||||
ret = check_host_key(s, host, port, host_key_check, errp);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Authenticate. */
|
||||
ret = authenticate(s, user);
|
||||
ret = authenticate(s, user, errp);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
@ -556,7 +589,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
|||
/* Start SFTP. */
|
||||
s->sftp = libssh2_sftp_init(s->session);
|
||||
if (!s->sftp) {
|
||||
session_error_report(s, "failed to initialize sftp handle");
|
||||
session_error_setg(errp, s, "failed to initialize sftp handle");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
@ -566,14 +599,14 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
|||
path, ssh_flags, creat_mode);
|
||||
s->sftp_handle = libssh2_sftp_open(s->sftp, path, ssh_flags, creat_mode);
|
||||
if (!s->sftp_handle) {
|
||||
session_error_report(s, "failed to open remote file '%s'", path);
|
||||
session_error_setg(errp, s, "failed to open remote file '%s'", path);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs);
|
||||
if (r < 0) {
|
||||
sftp_error_report(s, "failed to read file attributes");
|
||||
sftp_error_setg(errp, s, "failed to read file attributes");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -623,7 +656,7 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
|
|||
}
|
||||
|
||||
/* Start up SSH. */
|
||||
ret = connect_to_ssh(s, options, ssh_flags, 0);
|
||||
ret = connect_to_ssh(s, options, ssh_flags, 0, errp);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
@ -655,7 +688,6 @@ static int ssh_create(const char *filename, QEMUOptionParameter *options,
|
|||
Error **errp)
|
||||
{
|
||||
int r, ret;
|
||||
Error *local_err = NULL;
|
||||
int64_t total_size = 0;
|
||||
QDict *uri_options = NULL;
|
||||
BDRVSSHState s;
|
||||
|
@ -674,17 +706,16 @@ static int ssh_create(const char *filename, QEMUOptionParameter *options,
|
|||
DPRINTF("total_size=%" PRIi64, total_size);
|
||||
|
||||
uri_options = qdict_new();
|
||||
r = parse_uri(filename, uri_options, &local_err);
|
||||
r = parse_uri(filename, uri_options, errp);
|
||||
if (r < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
ret = r;
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = connect_to_ssh(&s, uri_options,
|
||||
LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
|
||||
LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, 0644);
|
||||
LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
|
||||
0644, errp);
|
||||
if (r < 0) {
|
||||
ret = r;
|
||||
goto out;
|
||||
|
@ -694,7 +725,7 @@ static int ssh_create(const char *filename, QEMUOptionParameter *options,
|
|||
libssh2_sftp_seek64(s.sftp_handle, total_size-1);
|
||||
r2 = libssh2_sftp_write(s.sftp_handle, c, 1);
|
||||
if (r2 < 0) {
|
||||
sftp_error_report(&s, "truncate failed");
|
||||
sftp_error_setg(errp, &s, "truncate failed");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ static void close_unused_images(BlockDriverState *top, BlockDriverState *base,
|
|||
/* Must assign before bdrv_delete() to prevent traversing dangling pointer
|
||||
* while we delete backing image instances.
|
||||
*/
|
||||
top->backing_hd = base;
|
||||
bdrv_set_backing_hd(top, base);
|
||||
|
||||
while (intermediate) {
|
||||
BlockDriverState *unused;
|
||||
|
@ -72,7 +72,7 @@ static void close_unused_images(BlockDriverState *top, BlockDriverState *base,
|
|||
|
||||
unused = intermediate;
|
||||
intermediate = intermediate->backing_hd;
|
||||
unused->backing_hd = NULL;
|
||||
bdrv_set_backing_hd(unused, NULL);
|
||||
bdrv_unref(unused);
|
||||
}
|
||||
|
||||
|
|
|
@ -831,7 +831,8 @@ static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
|
|||
}
|
||||
|
||||
static int init_directories(BDRVVVFATState* s,
|
||||
const char *dirname, int heads, int secs)
|
||||
const char *dirname, int heads, int secs,
|
||||
Error **errp)
|
||||
{
|
||||
bootsector_t* bootsector;
|
||||
mapping_t* mapping;
|
||||
|
@ -892,8 +893,8 @@ static int init_directories(BDRVVVFATState* s,
|
|||
if (mapping->mode & MODE_DIRECTORY) {
|
||||
mapping->begin = cluster;
|
||||
if(read_directory(s, i)) {
|
||||
fprintf(stderr, "Could not read directory %s\n",
|
||||
mapping->path);
|
||||
error_setg(errp, "Could not read directory %s",
|
||||
mapping->path);
|
||||
return -1;
|
||||
}
|
||||
mapping = array_get(&(s->mapping), i);
|
||||
|
@ -919,9 +920,10 @@ static int init_directories(BDRVVVFATState* s,
|
|||
cluster = mapping->end;
|
||||
|
||||
if(cluster > s->cluster_count) {
|
||||
fprintf(stderr,"Directory does not fit in FAT%d (capacity %.2f MB)\n",
|
||||
s->fat_type, s->sector_count / 2000.0);
|
||||
return -EINVAL;
|
||||
error_setg(errp,
|
||||
"Directory does not fit in FAT%d (capacity %.2f MB)",
|
||||
s->fat_type, s->sector_count / 2000.0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* fix fat for entry */
|
||||
|
@ -979,7 +981,7 @@ static int init_directories(BDRVVVFATState* s,
|
|||
static BDRVVVFATState *vvv = NULL;
|
||||
#endif
|
||||
|
||||
static int enable_write_target(BDRVVVFATState *s);
|
||||
static int enable_write_target(BDRVVVFATState *s, Error **errp);
|
||||
static int is_consistent(BDRVVVFATState *s);
|
||||
|
||||
static void vvfat_rebind(BlockDriverState *bs)
|
||||
|
@ -1160,7 +1162,7 @@ DLOG(if (stderr == NULL) {
|
|||
s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1);
|
||||
|
||||
if (qemu_opt_get_bool(opts, "rw", false)) {
|
||||
ret = enable_write_target(s);
|
||||
ret = enable_write_target(s, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -1169,7 +1171,7 @@ DLOG(if (stderr == NULL) {
|
|||
|
||||
bs->total_sectors = cyls * heads * secs;
|
||||
|
||||
if (init_directories(s, dirname, heads, secs)) {
|
||||
if (init_directories(s, dirname, heads, secs, errp)) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
@ -2904,11 +2906,10 @@ static BlockDriver vvfat_write_target = {
|
|||
.bdrv_close = write_target_close,
|
||||
};
|
||||
|
||||
static int enable_write_target(BDRVVVFATState *s)
|
||||
static int enable_write_target(BDRVVVFATState *s, Error **errp)
|
||||
{
|
||||
BlockDriver *bdrv_qcow;
|
||||
QEMUOptionParameter *options;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
int size = sector2cluster(s, s->sector_count);
|
||||
s->used_clusters = calloc(size, 1);
|
||||
|
@ -2918,6 +2919,7 @@ static int enable_write_target(BDRVVVFATState *s)
|
|||
s->qcow_filename = g_malloc(1024);
|
||||
ret = get_tmp_filename(s->qcow_filename, 1024);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "can't create temporary file");
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
@ -2926,20 +2928,16 @@ static int enable_write_target(BDRVVVFATState *s)
|
|||
set_option_parameter_int(options, BLOCK_OPT_SIZE, s->sector_count * 512);
|
||||
set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:");
|
||||
|
||||
ret = bdrv_create(bdrv_qcow, s->qcow_filename, options, &local_err);
|
||||
ret = bdrv_create(bdrv_qcow, s->qcow_filename, options, errp);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
s->qcow = NULL;
|
||||
ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow,
|
||||
&local_err);
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH,
|
||||
bdrv_qcow, errp);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
@ -2947,7 +2945,7 @@ static int enable_write_target(BDRVVVFATState *s)
|
|||
unlink(s->qcow_filename);
|
||||
#endif
|
||||
|
||||
s->bs->backing_hd = bdrv_new("", &error_abort);
|
||||
bdrv_set_backing_hd(s->bs, bdrv_new("", &error_abort));
|
||||
s->bs->backing_hd->drv = &vvfat_write_target;
|
||||
s->bs->backing_hd->opaque = g_malloc(sizeof(void*));
|
||||
*(void**)s->bs->backing_hd->opaque = s;
|
||||
|
|
34
blockdev.c
34
blockdev.c
|
@ -34,7 +34,6 @@
|
|||
#include "hw/block/block.h"
|
||||
#include "block/blockjob.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qapi/qmp/types.h"
|
||||
|
@ -730,7 +729,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||
&error_abort);
|
||||
qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err);
|
||||
if (local_err) {
|
||||
qerror_report_err(local_err);
|
||||
error_report("%s", error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
@ -942,7 +941,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||
dinfo = blockdev_init(filename, bs_opts, &local_err);
|
||||
if (dinfo == NULL) {
|
||||
if (local_err) {
|
||||
qerror_report_err(local_err);
|
||||
error_report("%s", error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
}
|
||||
goto fail;
|
||||
|
@ -1334,8 +1333,8 @@ static void external_snapshot_prepare(BlkTransactionState *common,
|
|||
return;
|
||||
}
|
||||
|
||||
if (bdrv_in_use(state->old_bs)) {
|
||||
error_set(errp, QERR_DEVICE_IN_USE, device);
|
||||
if (bdrv_op_is_blocked(state->old_bs,
|
||||
BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1557,8 +1556,7 @@ exit:
|
|||
|
||||
static void eject_device(BlockDriverState *bs, int force, Error **errp)
|
||||
{
|
||||
if (bdrv_in_use(bs)) {
|
||||
error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) {
|
||||
return;
|
||||
}
|
||||
if (!bdrv_dev_has_removable_media(bs)) {
|
||||
|
@ -1760,14 +1758,16 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
|||
{
|
||||
const char *id = qdict_get_str(qdict, "id");
|
||||
BlockDriverState *bs;
|
||||
Error *local_err = NULL;
|
||||
|
||||
bs = bdrv_find(id);
|
||||
if (!bs) {
|
||||
qerror_report(QERR_DEVICE_NOT_FOUND, id);
|
||||
error_report("Device '%s' not found", id);
|
||||
return -1;
|
||||
}
|
||||
if (bdrv_in_use(bs)) {
|
||||
qerror_report(QERR_DEVICE_IN_USE, id);
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) {
|
||||
error_report("%s", error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1888,6 +1888,10 @@ void qmp_block_stream(const char *device, bool has_base,
|
|||
return;
|
||||
}
|
||||
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_STREAM, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (base) {
|
||||
base_bs = bdrv_find_backing_image(bs, base);
|
||||
if (base_bs == NULL) {
|
||||
|
@ -1932,6 +1936,10 @@ void qmp_block_commit(const char *device,
|
|||
return;
|
||||
}
|
||||
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* default top_bs is the active layer */
|
||||
top_bs = bs;
|
||||
|
||||
|
@ -2023,8 +2031,7 @@ void qmp_drive_backup(const char *device, const char *target,
|
|||
}
|
||||
}
|
||||
|
||||
if (bdrv_in_use(bs)) {
|
||||
error_set(errp, QERR_DEVICE_IN_USE, device);
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2157,8 +2164,7 @@ void qmp_drive_mirror(const char *device, const char *target,
|
|||
}
|
||||
}
|
||||
|
||||
if (bdrv_in_use(bs)) {
|
||||
error_set(errp, QERR_DEVICE_IN_USE, device);
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
14
blockjob.c
14
blockjob.c
|
@ -41,14 +41,16 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
|||
{
|
||||
BlockJob *job;
|
||||
|
||||
if (bs->job || bdrv_in_use(bs)) {
|
||||
if (bs->job) {
|
||||
error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
|
||||
return NULL;
|
||||
}
|
||||
bdrv_ref(bs);
|
||||
bdrv_set_in_use(bs, 1);
|
||||
|
||||
job = g_malloc0(driver->instance_size);
|
||||
error_setg(&job->blocker, "block device is in use by block job: %s",
|
||||
BlockJobType_lookup[driver->job_type]);
|
||||
bdrv_op_block_all(bs, job->blocker);
|
||||
|
||||
job->driver = driver;
|
||||
job->bs = bs;
|
||||
job->cb = cb;
|
||||
|
@ -63,8 +65,9 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
|||
block_job_set_speed(job, speed, &local_err);
|
||||
if (local_err) {
|
||||
bs->job = NULL;
|
||||
bdrv_op_unblock_all(bs, job->blocker);
|
||||
error_free(job->blocker);
|
||||
g_free(job);
|
||||
bdrv_set_in_use(bs, 0);
|
||||
error_propagate(errp, local_err);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -79,8 +82,9 @@ void block_job_completed(BlockJob *job, int ret)
|
|||
assert(bs->job == job);
|
||||
job->cb(job->opaque, ret);
|
||||
bs->job = NULL;
|
||||
bdrv_op_unblock_all(bs, job->blocker);
|
||||
error_free(job->blocker);
|
||||
g_free(job);
|
||||
bdrv_set_in_use(bs, 0);
|
||||
}
|
||||
|
||||
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
|
|
|
@ -107,8 +107,9 @@ in the description of a field.
|
|||
|
||||
96 - 99: refcount_order
|
||||
Describes the width of a reference count block entry (width
|
||||
in bits = 1 << refcount_order). For version 2 images, the
|
||||
order is always assumed to be 4 (i.e. the width is 16 bits).
|
||||
in bits: refcount_bits = 1 << refcount_order). For version 2
|
||||
images, the order is always assumed to be 4
|
||||
(i.e. refcount_bits = 16).
|
||||
|
||||
100 - 103: header_length
|
||||
Length of the header structure in bytes. For version 2
|
||||
|
|
|
@ -70,6 +70,9 @@ struct VirtIOBlockDataPlane {
|
|||
queue */
|
||||
|
||||
unsigned int num_reqs;
|
||||
|
||||
/* Operation blocker on BDS */
|
||||
Error *blocker;
|
||||
};
|
||||
|
||||
/* Raise an interrupt to signal guest, if necessary */
|
||||
|
@ -350,6 +353,7 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
|
|||
{
|
||||
VirtIOBlockDataPlane *s;
|
||||
int fd;
|
||||
Error *local_err = NULL;
|
||||
|
||||
*dataplane = NULL;
|
||||
|
||||
|
@ -372,9 +376,10 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
|
|||
/* If dataplane is (re-)enabled while the guest is running there could be
|
||||
* block jobs that can conflict.
|
||||
*/
|
||||
if (bdrv_in_use(blk->conf.bs)) {
|
||||
error_setg(errp,
|
||||
"cannot start dataplane thread while device is in use");
|
||||
if (bdrv_op_is_blocked(blk->conf.bs, BLOCK_OP_TYPE_DATAPLANE, &local_err)) {
|
||||
error_report("cannot start dataplane thread: %s",
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -406,8 +411,8 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
|
|||
}
|
||||
s->ctx = iothread_get_aio_context(s->iothread);
|
||||
|
||||
/* Prevent block operations that conflict with data plane thread */
|
||||
bdrv_set_in_use(blk->conf.bs, 1);
|
||||
error_setg(&s->blocker, "block device is in use by data plane");
|
||||
bdrv_op_block_all(blk->conf.bs, s->blocker);
|
||||
|
||||
*dataplane = s;
|
||||
}
|
||||
|
@ -420,7 +425,8 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
|
|||
}
|
||||
|
||||
virtio_blk_data_plane_stop(s);
|
||||
bdrv_set_in_use(s->blk->conf.bs, 0);
|
||||
bdrv_op_unblock_all(s->blk->conf.bs, s->blocker);
|
||||
error_free(s->blocker);
|
||||
object_unref(OBJECT(s->iothread));
|
||||
g_free(s);
|
||||
}
|
||||
|
|
|
@ -162,6 +162,25 @@ typedef struct BDRVReopenState {
|
|||
void *opaque;
|
||||
} BDRVReopenState;
|
||||
|
||||
/*
|
||||
* Block operation types
|
||||
*/
|
||||
typedef enum BlockOpType {
|
||||
BLOCK_OP_TYPE_BACKUP_SOURCE,
|
||||
BLOCK_OP_TYPE_BACKUP_TARGET,
|
||||
BLOCK_OP_TYPE_CHANGE,
|
||||
BLOCK_OP_TYPE_COMMIT,
|
||||
BLOCK_OP_TYPE_DATAPLANE,
|
||||
BLOCK_OP_TYPE_DRIVE_DEL,
|
||||
BLOCK_OP_TYPE_EJECT,
|
||||
BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
|
||||
BLOCK_OP_TYPE_INTERNAL_SNAPSHOT,
|
||||
BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE,
|
||||
BLOCK_OP_TYPE_MIRROR,
|
||||
BLOCK_OP_TYPE_RESIZE,
|
||||
BLOCK_OP_TYPE_STREAM,
|
||||
BLOCK_OP_TYPE_MAX,
|
||||
} BlockOpType;
|
||||
|
||||
void bdrv_iostatus_enable(BlockDriverState *bs);
|
||||
void bdrv_iostatus_reset(BlockDriverState *bs);
|
||||
|
@ -197,6 +216,7 @@ int bdrv_parse_discard_flags(const char *mode, int *flags);
|
|||
int bdrv_open_image(BlockDriverState **pbs, const char *filename,
|
||||
QDict *options, const char *bdref_key, int flags,
|
||||
bool allow_none, Error **errp);
|
||||
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd);
|
||||
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp);
|
||||
void bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp);
|
||||
int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
|
@ -453,8 +473,13 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs);
|
|||
|
||||
void bdrv_ref(BlockDriverState *bs);
|
||||
void bdrv_unref(BlockDriverState *bs);
|
||||
void bdrv_set_in_use(BlockDriverState *bs, int in_use);
|
||||
int bdrv_in_use(BlockDriverState *bs);
|
||||
|
||||
bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp);
|
||||
void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason);
|
||||
void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, Error *reason);
|
||||
void bdrv_op_block_all(BlockDriverState *bs, Error *reason);
|
||||
void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason);
|
||||
bool bdrv_op_blocker_is_empty(BlockDriverState *bs);
|
||||
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
int raw_get_aio_fd(BlockDriverState *bs);
|
||||
|
|
|
@ -270,6 +270,8 @@ typedef struct BlockLimits {
|
|||
size_t opt_mem_alignment;
|
||||
} BlockLimits;
|
||||
|
||||
typedef struct BdrvOpBlocker BdrvOpBlocker;
|
||||
|
||||
/*
|
||||
* Note: the function bdrv_append() copies and swaps contents of
|
||||
* BlockDriverStates, so if you add new fields to this struct, please
|
||||
|
@ -356,15 +358,20 @@ struct BlockDriverState {
|
|||
QTAILQ_ENTRY(BlockDriverState) device_list;
|
||||
QLIST_HEAD(, BdrvDirtyBitmap) dirty_bitmaps;
|
||||
int refcnt;
|
||||
int in_use; /* users other than guest access, eg. block migration */
|
||||
|
||||
QLIST_HEAD(, BdrvTrackedRequest) tracked_requests;
|
||||
|
||||
/* operation blockers */
|
||||
QLIST_HEAD(, BdrvOpBlocker) op_blockers[BLOCK_OP_TYPE_MAX];
|
||||
|
||||
/* long-running background operation */
|
||||
BlockJob *job;
|
||||
|
||||
QDict *options;
|
||||
BlockdevDetectZeroesOptions detect_zeroes;
|
||||
|
||||
/* The error object in use for blocking operations on backing_hd */
|
||||
Error *backing_blocker;
|
||||
};
|
||||
|
||||
int get_tmp_filename(char *filename, int size);
|
||||
|
|
|
@ -106,6 +106,9 @@ struct BlockJob {
|
|||
/** The completion function that will be called when the job completes. */
|
||||
BlockDriverCompletionFunc *cb;
|
||||
|
||||
/** Block other operations when block job is running */
|
||||
Error *blocker;
|
||||
|
||||
/** The opaque value that is passed to the completion function. */
|
||||
void *opaque;
|
||||
};
|
||||
|
|
|
@ -223,7 +223,7 @@ static int tcp_socket_incoming(const char *address, uint16_t port)
|
|||
int fd = inet_listen(address_and_port, NULL, 0, SOCK_STREAM, 0, &local_err);
|
||||
|
||||
if (local_err != NULL) {
|
||||
qerror_report_err(local_err);
|
||||
error_report("%s", error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
}
|
||||
return fd;
|
||||
|
@ -235,7 +235,7 @@ static int unix_socket_incoming(const char *path)
|
|||
int fd = unix_listen(path, NULL, 0, &local_err);
|
||||
|
||||
if (local_err != NULL) {
|
||||
qerror_report_err(local_err);
|
||||
error_report("%s", error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
}
|
||||
return fd;
|
||||
|
@ -247,7 +247,7 @@ static int unix_socket_outgoing(const char *path)
|
|||
int fd = unix_connect(path, &local_err);
|
||||
|
||||
if (local_err != NULL) {
|
||||
qerror_report_err(local_err);
|
||||
error_report("%s", error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
}
|
||||
return fd;
|
||||
|
|
|
@ -60,6 +60,8 @@ check-unit-y += tests/test-qdev-global-props$(EXESUF)
|
|||
check-unit-y += tests/check-qom-interface$(EXESUF)
|
||||
gcov-files-check-qom-interface-y = qom/object.c
|
||||
check-unit-$(CONFIG_POSIX) += tests/test-vmstate$(EXESUF)
|
||||
check-unit-y += tests/test-qemu-opts$(EXESUF)
|
||||
gcov-files-test-qemu-opts-y = qom/test-qemu-opts.c
|
||||
|
||||
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
|
||||
|
||||
|
@ -319,6 +321,7 @@ tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o
|
|||
tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o
|
||||
tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o
|
||||
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
|
||||
tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o libqemuutil.a libqemustub.a
|
||||
|
||||
# QTest rules
|
||||
|
||||
|
|
|
@ -107,8 +107,7 @@ echo
|
|||
echo "=== Testing qemu-img info output ==="
|
||||
echo
|
||||
|
||||
$QEMU_IMG info "json:{\"driver\":\"qcow2\",\"file.filename\":\"$TEST_IMG\"}" \
|
||||
| _filter_testdir | _filter_imgfmt
|
||||
TEST_IMG="json:{\"driver\":\"qcow2\",\"file.filename\":\"$TEST_IMG\"}" _img_info
|
||||
|
||||
|
||||
echo
|
||||
|
|
|
@ -31,11 +31,7 @@ read failed: Input/output error
|
|||
image: TEST_DIR/t.IMGFMT
|
||||
file format: IMGFMT
|
||||
virtual size: 64M (67108864 bytes)
|
||||
disk size: 324K
|
||||
cluster_size: 65536
|
||||
Format specific information:
|
||||
compat: 1.1
|
||||
lazy refcounts: false
|
||||
|
||||
=== Testing option merging ===
|
||||
|
||||
|
|
|
@ -47,6 +47,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
|||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
_default_cache_mode "none"
|
||||
_supported_cache_modes "writethrough" "none" "writeback"
|
||||
|
||||
size=1G
|
||||
|
||||
|
@ -59,13 +61,13 @@ echo === Starting QEMU VM1 ===
|
|||
echo
|
||||
|
||||
qemu_comm_method="monitor"
|
||||
_launch_qemu -drive file="${TEST_IMG}",cache=none,id=disk
|
||||
_launch_qemu -drive file="${TEST_IMG}",cache=${CACHEMODE},id=disk
|
||||
h1=$QEMU_HANDLE
|
||||
|
||||
echo
|
||||
echo === Starting QEMU VM2 ===
|
||||
echo
|
||||
_launch_qemu -drive file="${TEST_IMG}",cache=none,id=disk \
|
||||
_launch_qemu -drive file="${TEST_IMG}",cache=${CACHEMODE},id=disk \
|
||||
-incoming "exec: cat '${MIG_FIFO}'"
|
||||
h2=$QEMU_HANDLE
|
||||
|
||||
|
|
|
@ -0,0 +1,441 @@
|
|||
/*
|
||||
* QemuOpts unit-tests.
|
||||
*
|
||||
* Copyright (C) 2014 Leandro Dorileo <l@dorileo.org>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qemu/config-file.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
|
||||
static QemuOptsList opts_list_01 = {
|
||||
.name = "opts_list_01",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(opts_list_01.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "str1",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},{
|
||||
.name = "str2",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},{
|
||||
.name = "str3",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},{
|
||||
.name = "number1",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static QemuOptsList opts_list_02 = {
|
||||
.name = "opts_list_02",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(opts_list_02.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "str1",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},{
|
||||
.name = "bool1",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
},{
|
||||
.name = "str2",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},{
|
||||
.name = "size1",
|
||||
.type = QEMU_OPT_SIZE,
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
QemuOptsList opts_list_03 = {
|
||||
.name = "opts_list_03",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(opts_list_03.head),
|
||||
.desc = {
|
||||
/* no elements => accept any params */
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static void register_opts(void)
|
||||
{
|
||||
qemu_add_opts(&opts_list_01);
|
||||
qemu_add_opts(&opts_list_02);
|
||||
qemu_add_opts(&opts_list_03);
|
||||
}
|
||||
|
||||
static void test_find_unknown_opts(void)
|
||||
{
|
||||
QemuOptsList *list;
|
||||
Error *err = NULL;
|
||||
|
||||
/* should not return anything, we don't have an "unknown" option */
|
||||
list = qemu_find_opts_err("unknown", &err);
|
||||
g_assert(list == NULL);
|
||||
g_assert(err);
|
||||
error_free(err);
|
||||
}
|
||||
|
||||
static void test_qemu_find_opts(void)
|
||||
{
|
||||
QemuOptsList *list;
|
||||
|
||||
/* we have an "opts_list_01" option, should return it */
|
||||
list = qemu_find_opts("opts_list_01");
|
||||
g_assert(list != NULL);
|
||||
g_assert_cmpstr(list->name, ==, "opts_list_01");
|
||||
}
|
||||
|
||||
static void test_qemu_opts_create(void)
|
||||
{
|
||||
QemuOptsList *list;
|
||||
QemuOpts *opts;
|
||||
|
||||
list = qemu_find_opts("opts_list_01");
|
||||
g_assert(list != NULL);
|
||||
g_assert(QTAILQ_EMPTY(&list->head));
|
||||
g_assert_cmpstr(list->name, ==, "opts_list_01");
|
||||
|
||||
/* should not find anything at this point */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts == NULL);
|
||||
|
||||
/* create the opts */
|
||||
opts = qemu_opts_create(list, NULL, 0, &error_abort);
|
||||
g_assert(opts != NULL);
|
||||
g_assert(!QTAILQ_EMPTY(&list->head));
|
||||
|
||||
/* now we've create the opts, must find it */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts != NULL);
|
||||
|
||||
qemu_opts_del(opts);
|
||||
|
||||
/* should not find anything at this point */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts == NULL);
|
||||
}
|
||||
|
||||
static void test_qemu_opt_get(void)
|
||||
{
|
||||
QemuOptsList *list;
|
||||
QemuOpts *opts;
|
||||
const char *opt = NULL;
|
||||
|
||||
list = qemu_find_opts("opts_list_01");
|
||||
g_assert(list != NULL);
|
||||
g_assert(QTAILQ_EMPTY(&list->head));
|
||||
g_assert_cmpstr(list->name, ==, "opts_list_01");
|
||||
|
||||
/* should not find anything at this point */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts == NULL);
|
||||
|
||||
/* create the opts */
|
||||
opts = qemu_opts_create(list, NULL, 0, &error_abort);
|
||||
g_assert(opts != NULL);
|
||||
g_assert(!QTAILQ_EMPTY(&list->head));
|
||||
|
||||
/* haven't set anything to str2 yet */
|
||||
opt = qemu_opt_get(opts, "str2");
|
||||
g_assert(opt == NULL);
|
||||
|
||||
qemu_opt_set(opts, "str2", "value");
|
||||
|
||||
/* now we have set str2, should know about it */
|
||||
opt = qemu_opt_get(opts, "str2");
|
||||
g_assert_cmpstr(opt, ==, "value");
|
||||
|
||||
qemu_opt_set(opts, "str2", "value2");
|
||||
|
||||
/* having reset the value, the returned should be the reset one */
|
||||
opt = qemu_opt_get(opts, "str2");
|
||||
g_assert_cmpstr(opt, ==, "value2");
|
||||
|
||||
qemu_opts_del(opts);
|
||||
|
||||
/* should not find anything at this point */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts == NULL);
|
||||
}
|
||||
|
||||
static void test_qemu_opt_get_bool(void)
|
||||
{
|
||||
QemuOptsList *list;
|
||||
QemuOpts *opts;
|
||||
bool opt;
|
||||
int ret;
|
||||
|
||||
list = qemu_find_opts("opts_list_02");
|
||||
g_assert(list != NULL);
|
||||
g_assert(QTAILQ_EMPTY(&list->head));
|
||||
g_assert_cmpstr(list->name, ==, "opts_list_02");
|
||||
|
||||
/* should not find anything at this point */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts == NULL);
|
||||
|
||||
/* create the opts */
|
||||
opts = qemu_opts_create(list, NULL, 0, &error_abort);
|
||||
g_assert(opts != NULL);
|
||||
g_assert(!QTAILQ_EMPTY(&list->head));
|
||||
|
||||
/* haven't set anything to bool1 yet, so defval should be returned */
|
||||
opt = qemu_opt_get_bool(opts, "bool1", false);
|
||||
g_assert(opt == false);
|
||||
|
||||
ret = qemu_opt_set_bool(opts, "bool1", true);
|
||||
g_assert(ret == 0);
|
||||
|
||||
/* now we have set bool1, should know about it */
|
||||
opt = qemu_opt_get_bool(opts, "bool1", false);
|
||||
g_assert(opt == true);
|
||||
|
||||
/* having reset the value, opt should be the reset one not defval */
|
||||
ret = qemu_opt_set_bool(opts, "bool1", false);
|
||||
g_assert(ret == 0);
|
||||
|
||||
opt = qemu_opt_get_bool(opts, "bool1", true);
|
||||
g_assert(opt == false);
|
||||
|
||||
qemu_opts_del(opts);
|
||||
|
||||
/* should not find anything at this point */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts == NULL);
|
||||
}
|
||||
|
||||
static void test_qemu_opt_get_number(void)
|
||||
{
|
||||
QemuOptsList *list;
|
||||
QemuOpts *opts;
|
||||
uint64_t opt;
|
||||
int ret;
|
||||
|
||||
list = qemu_find_opts("opts_list_01");
|
||||
g_assert(list != NULL);
|
||||
g_assert(QTAILQ_EMPTY(&list->head));
|
||||
g_assert_cmpstr(list->name, ==, "opts_list_01");
|
||||
|
||||
/* should not find anything at this point */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts == NULL);
|
||||
|
||||
/* create the opts */
|
||||
opts = qemu_opts_create(list, NULL, 0, &error_abort);
|
||||
g_assert(opts != NULL);
|
||||
g_assert(!QTAILQ_EMPTY(&list->head));
|
||||
|
||||
/* haven't set anything to number1 yet, so defval should be returned */
|
||||
opt = qemu_opt_get_number(opts, "number1", 5);
|
||||
g_assert(opt == 5);
|
||||
|
||||
ret = qemu_opt_set_number(opts, "number1", 10);
|
||||
g_assert(ret == 0);
|
||||
|
||||
/* now we have set number1, should know about it */
|
||||
opt = qemu_opt_get_number(opts, "number1", 5);
|
||||
g_assert(opt == 10);
|
||||
|
||||
/* having reset it, the returned should be the reset one not defval */
|
||||
ret = qemu_opt_set_number(opts, "number1", 15);
|
||||
g_assert(ret == 0);
|
||||
|
||||
opt = qemu_opt_get_number(opts, "number1", 5);
|
||||
g_assert(opt == 15);
|
||||
|
||||
qemu_opts_del(opts);
|
||||
|
||||
/* should not find anything at this point */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts == NULL);
|
||||
}
|
||||
|
||||
static void test_qemu_opt_get_size(void)
|
||||
{
|
||||
QemuOptsList *list;
|
||||
QemuOpts *opts;
|
||||
uint64_t opt;
|
||||
QDict *dict;
|
||||
|
||||
list = qemu_find_opts("opts_list_02");
|
||||
g_assert(list != NULL);
|
||||
g_assert(QTAILQ_EMPTY(&list->head));
|
||||
g_assert_cmpstr(list->name, ==, "opts_list_02");
|
||||
|
||||
/* should not find anything at this point */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts == NULL);
|
||||
|
||||
/* create the opts */
|
||||
opts = qemu_opts_create(list, NULL, 0, &error_abort);
|
||||
g_assert(opts != NULL);
|
||||
g_assert(!QTAILQ_EMPTY(&list->head));
|
||||
|
||||
/* haven't set anything to size1 yet, so defval should be returned */
|
||||
opt = qemu_opt_get_size(opts, "size1", 5);
|
||||
g_assert(opt == 5);
|
||||
|
||||
dict = qdict_new();
|
||||
g_assert(dict != NULL);
|
||||
|
||||
qdict_put(dict, "size1", qstring_from_str("10"));
|
||||
|
||||
qemu_opts_absorb_qdict(opts, dict, &error_abort);
|
||||
g_assert(error_abort == NULL);
|
||||
|
||||
/* now we have set size1, should know about it */
|
||||
opt = qemu_opt_get_size(opts, "size1", 5);
|
||||
g_assert(opt == 10);
|
||||
|
||||
/* reset value */
|
||||
qdict_put(dict, "size1", qstring_from_str("15"));
|
||||
|
||||
qemu_opts_absorb_qdict(opts, dict, &error_abort);
|
||||
g_assert(error_abort == NULL);
|
||||
|
||||
/* test the reset value */
|
||||
opt = qemu_opt_get_size(opts, "size1", 5);
|
||||
g_assert(opt == 15);
|
||||
|
||||
qdict_del(dict, "size1");
|
||||
g_free(dict);
|
||||
|
||||
qemu_opts_del(opts);
|
||||
|
||||
/* should not find anything at this point */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts == NULL);
|
||||
}
|
||||
|
||||
static void test_qemu_opt_unset(void)
|
||||
{
|
||||
QemuOpts *opts;
|
||||
const char *value;
|
||||
int ret;
|
||||
|
||||
/* dynamically initialized (parsed) opts */
|
||||
opts = qemu_opts_parse(&opts_list_03, "key=value", 0);
|
||||
g_assert(opts != NULL);
|
||||
|
||||
/* check default/parsed value */
|
||||
value = qemu_opt_get(opts, "key");
|
||||
g_assert_cmpstr(value, ==, "value");
|
||||
|
||||
/* reset it to value2 */
|
||||
qemu_opt_set(opts, "key", "value2");
|
||||
|
||||
value = qemu_opt_get(opts, "key");
|
||||
g_assert_cmpstr(value, ==, "value2");
|
||||
|
||||
/* unset, valid only for "accept any" */
|
||||
ret = qemu_opt_unset(opts, "key");
|
||||
g_assert(ret == 0);
|
||||
|
||||
/* after reset the value should be the parsed/default one */
|
||||
value = qemu_opt_get(opts, "key");
|
||||
g_assert_cmpstr(value, ==, "value");
|
||||
|
||||
qemu_opts_del(opts);
|
||||
}
|
||||
|
||||
static void test_qemu_opts_reset(void)
|
||||
{
|
||||
QemuOptsList *list;
|
||||
QemuOpts *opts;
|
||||
uint64_t opt;
|
||||
int ret;
|
||||
|
||||
list = qemu_find_opts("opts_list_01");
|
||||
g_assert(list != NULL);
|
||||
g_assert(QTAILQ_EMPTY(&list->head));
|
||||
g_assert_cmpstr(list->name, ==, "opts_list_01");
|
||||
|
||||
/* should not find anything at this point */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts == NULL);
|
||||
|
||||
/* create the opts */
|
||||
opts = qemu_opts_create(list, NULL, 0, &error_abort);
|
||||
g_assert(opts != NULL);
|
||||
g_assert(!QTAILQ_EMPTY(&list->head));
|
||||
|
||||
/* haven't set anything to number1 yet, so defval should be returned */
|
||||
opt = qemu_opt_get_number(opts, "number1", 5);
|
||||
g_assert(opt == 5);
|
||||
|
||||
ret = qemu_opt_set_number(opts, "number1", 10);
|
||||
g_assert(ret == 0);
|
||||
|
||||
/* now we have set number1, should know about it */
|
||||
opt = qemu_opt_get_number(opts, "number1", 5);
|
||||
g_assert(opt == 10);
|
||||
|
||||
qemu_opts_reset(list);
|
||||
|
||||
/* should not find anything at this point */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts == NULL);
|
||||
}
|
||||
|
||||
static void test_qemu_opts_set(void)
|
||||
{
|
||||
QemuOptsList *list;
|
||||
QemuOpts *opts;
|
||||
int ret;
|
||||
const char *opt;
|
||||
|
||||
list = qemu_find_opts("opts_list_01");
|
||||
g_assert(list != NULL);
|
||||
g_assert(QTAILQ_EMPTY(&list->head));
|
||||
g_assert_cmpstr(list->name, ==, "opts_list_01");
|
||||
|
||||
/* should not find anything at this point */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts == NULL);
|
||||
|
||||
/* implicitly create opts and set str3 value */
|
||||
ret = qemu_opts_set(list, NULL, "str3", "value");
|
||||
g_assert(ret == 0);
|
||||
g_assert(!QTAILQ_EMPTY(&list->head));
|
||||
|
||||
/* get the just created opts */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts != NULL);
|
||||
|
||||
/* check the str3 value */
|
||||
opt = qemu_opt_get(opts, "str3");
|
||||
g_assert_cmpstr(opt, ==, "value");
|
||||
|
||||
qemu_opts_del(opts);
|
||||
|
||||
/* should not find anything at this point */
|
||||
opts = qemu_opts_find(list, NULL);
|
||||
g_assert(opts == NULL);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
register_opts();
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/qemu-opts/find_unknown_opts", test_find_unknown_opts);
|
||||
g_test_add_func("/qemu-opts/find_opts", test_qemu_find_opts);
|
||||
g_test_add_func("/qemu-opts/opts_create", test_qemu_opts_create);
|
||||
g_test_add_func("/qemu-opts/opt_get", test_qemu_opt_get);
|
||||
g_test_add_func("/qemu-opts/opt_get_bool", test_qemu_opt_get_bool);
|
||||
g_test_add_func("/qemu-opts/opt_get_number", test_qemu_opt_get_number);
|
||||
g_test_add_func("/qemu-opts/opt_get_size", test_qemu_opt_get_size);
|
||||
g_test_add_func("/qemu-opts/opt_unset", test_qemu_opt_unset);
|
||||
g_test_add_func("/qemu-opts/opts_reset", test_qemu_opts_reset);
|
||||
g_test_add_func("/qemu-opts/opts_set", test_qemu_opts_set);
|
||||
g_test_run();
|
||||
return 0;
|
||||
}
|
|
@ -180,7 +180,7 @@ static void test_cancel(void)
|
|||
|
||||
/* Canceling the others will be a blocking operation. */
|
||||
for (i = 0; i < 100; i++) {
|
||||
if (data[i].n != 3) {
|
||||
if (data[i].aiocb && data[i].n != 3) {
|
||||
bdrv_aio_cancel(data[i].aiocb);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -224,6 +224,7 @@ static void thread_pool_cancel(BlockDriverAIOCB *acb)
|
|||
pool->pending_cancellations--;
|
||||
}
|
||||
qemu_mutex_unlock(&pool->lock);
|
||||
event_notifier_ready(&pool->notifier);
|
||||
}
|
||||
|
||||
static const AIOCBInfo thread_pool_aiocb_info = {
|
||||
|
|
Loading…
Reference in New Issue