Block layer fixes for 2.9.0-rc0

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJYvsrGAAoJEH8JsnLIjy/W5N4QAJAx81OUMvn4b+owj+a5U8T+
 uu6kOhIkRkeNtNFGxolQqRq1vIuTNfCwPsaFR5E6Qv7MyNKK50korFfI9MHOwbg5
 VNiRfsw88wI4u5PHw1cHEcpdJPJx5D7gLz0tdM8dYSjdZPIe5myO3C7bMwElEz4t
 1PJdpNZueiW/HAw+10IVxn1E2oCEC+6LWPOZRtVmwiItZgJHbZXJ3izNCFaNAFmF
 Xb1YMG4gZKxH0DiMKbBAE+Np+8h6dKQw/guVV6aoOfRoLc5cEkbB1VHW50PQO8w9
 Z0Hb++xTAT7KugxrVQlImh8HGZ0ICxj4zILw8owLmXeB9DeJ1MKQhsoWkirQ65T9
 OnYqDwPtWqAvO738ygGIYGXhm5ltyUfXKdwfQDvSgJqQP4lJcz4RMs7ZgiuaPLZY
 nr/CTR6867Bv2jIoEXpx2zGpYiWnMjCbA6uorHIVlEOPPFuvOqa5ClU1BwT6bojO
 QnWWu99SXH27fioin/1TFiL1KQqCXXmEznFLXp2CNALkR+GwMO/kUSKbQeYDFy0E
 7KlfLac0tKJC4qzsXyeDgtvETVUJAn2Pz04y97RF3ESsI/JPI4KaSa3ARrOmEa3g
 kayR/C+UmsH8phn6HyqgR3miv0oRuVQuhTVX04z7aH11a2Bgca3nHjjcjag8E+I4
 GNOaGdmQkG5P4GCM5xW8
 =0adR
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block layer fixes for 2.9.0-rc0

# gpg: Signature made Tue 07 Mar 2017 14:59:18 GMT
# gpg:                using RSA key 0x7F09B272C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* remotes/kevin/tags/for-upstream: (27 commits)
  commit: Don't use error_abort in commit_start
  block: Don't use error_abort in blk_new_open
  sheepdog: Support blockdev-add
  qapi-schema: Rename SocketAddressFlat's variant tcp to inet
  qapi-schema: Rename GlusterServer to SocketAddressFlat
  gluster: Plug memory leaks in qemu_gluster_parse_json()
  gluster: Don't duplicate qapi-util.c's qapi_enum_parse()
  gluster: Drop assumptions on SocketTransport names
  sheepdog: Implement bdrv_parse_filename()
  sheepdog: Use SocketAddress and socket_connect()
  sheepdog: Report errors in pseudo-filename more usefully
  sheepdog: Don't truncate long VDI name in _open(), _create()
  sheepdog: Fix snapshot ID parsing in _open(), _create, _goto()
  sheepdog: Mark sd_snapshot_delete() lossage FIXME
  sheepdog: Fix error handling sd_create()
  sheepdog: Fix error handling in sd_snapshot_delete()
  sheepdog: Defuse time bomb in sd_open() error handling
  block: Fix error handling in bdrv_replace_in_backing_chain()
  block: Handle permission errors in change_parent_backing_link()
  block: Ignore multiple children in bdrv_check_update_perm()
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2017-03-08 09:47:52 +00:00
commit b64842dee4
11 changed files with 626 additions and 329 deletions

184
block.c
View File

@ -1403,7 +1403,8 @@ static int bdrv_fill_options(QDict **options, const char *filename,
* or bdrv_abort_perm_update().
*/
static int bdrv_check_perm(BlockDriverState *bs, uint64_t cumulative_perms,
uint64_t cumulative_shared_perms, Error **errp)
uint64_t cumulative_shared_perms,
GSList *ignore_children, Error **errp)
{
BlockDriver *drv = bs->drv;
BdrvChild *c;
@ -1439,7 +1440,8 @@ static int bdrv_check_perm(BlockDriverState *bs, uint64_t cumulative_perms,
drv->bdrv_child_perm(bs, c, c->role,
cumulative_perms, cumulative_shared_perms,
&cur_perm, &cur_shared);
ret = bdrv_child_check_perm(c, cur_perm, cur_shared, errp);
ret = bdrv_child_check_perm(c, cur_perm, cur_shared, ignore_children,
errp);
if (ret < 0) {
return ret;
}
@ -1559,15 +1561,15 @@ static char *bdrv_perm_names(uint64_t perm)
/*
* Checks whether a new reference to @bs can be added if the new user requires
* @new_used_perm/@new_shared_perm as its permissions. If @ignore_child is set,
* this old reference is ignored in the calculations; this allows checking
* permission updates for an existing reference.
* @new_used_perm/@new_shared_perm as its permissions. If @ignore_children is
* set, the BdrvChild objects in this list are ignored in the calculations;
* this allows checking permission updates for an existing reference.
*
* Needs to be followed by a call to either bdrv_set_perm() or
* bdrv_abort_perm_update(). */
static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
uint64_t new_shared_perm,
BdrvChild *ignore_child, Error **errp)
GSList *ignore_children, Error **errp)
{
BdrvChild *c;
uint64_t cumulative_perms = new_used_perm;
@ -1577,7 +1579,7 @@ static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED);
QLIST_FOREACH(c, &bs->parents, next_parent) {
if (c == ignore_child) {
if (g_slist_find(ignore_children, c)) {
continue;
}
@ -1607,15 +1609,22 @@ static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
cumulative_shared_perms &= c->shared_perm;
}
return bdrv_check_perm(bs, cumulative_perms, cumulative_shared_perms, errp);
return bdrv_check_perm(bs, cumulative_perms, cumulative_shared_perms,
ignore_children, errp);
}
/* Needs to be followed by a call to either bdrv_child_set_perm() or
* bdrv_child_abort_perm_update(). */
int bdrv_child_check_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
Error **errp)
GSList *ignore_children, Error **errp)
{
return bdrv_check_update_perm(c->bs, perm, shared, c, errp);
int ret;
ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c);
ret = bdrv_check_update_perm(c->bs, perm, shared, ignore_children, errp);
g_slist_free(ignore_children);
return ret;
}
void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared)
@ -1640,7 +1649,7 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
{
int ret;
ret = bdrv_child_check_perm(c, perm, shared, errp);
ret = bdrv_child_check_perm(c, perm, shared, NULL, errp);
if (ret < 0) {
bdrv_child_abort_perm_update(c);
return ret;
@ -1718,11 +1727,10 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
*nshared = shared;
}
static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
bool check_new_perm)
static void bdrv_replace_child_noperm(BdrvChild *child,
BlockDriverState *new_bs)
{
BlockDriverState *old_bs = child->bs;
uint64_t perm, shared_perm;
if (old_bs) {
if (old_bs->quiesce_counter && child->role->drained_end) {
@ -1732,13 +1740,6 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
child->role->detach(child);
}
QLIST_REMOVE(child, next_parent);
/* Update permissions for old node. This is guaranteed to succeed
* because we're just taking a parent away, so we're loosening
* restrictions. */
bdrv_get_cumulative_perm(old_bs, &perm, &shared_perm);
bdrv_check_perm(old_bs, perm, shared_perm, &error_abort);
bdrv_set_perm(old_bs, perm, shared_perm);
}
child->bs = new_bs;
@ -1749,18 +1750,38 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
child->role->drained_begin(child);
}
bdrv_get_cumulative_perm(new_bs, &perm, &shared_perm);
if (check_new_perm) {
bdrv_check_perm(new_bs, perm, shared_perm, &error_abort);
}
bdrv_set_perm(new_bs, perm, shared_perm);
if (child->role->attach) {
child->role->attach(child);
}
}
}
static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
bool check_new_perm)
{
BlockDriverState *old_bs = child->bs;
uint64_t perm, shared_perm;
if (old_bs) {
/* Update permissions for old node. This is guaranteed to succeed
* because we're just taking a parent away, so we're loosening
* restrictions. */
bdrv_get_cumulative_perm(old_bs, &perm, &shared_perm);
bdrv_check_perm(old_bs, perm, shared_perm, NULL, &error_abort);
bdrv_set_perm(old_bs, perm, shared_perm);
}
bdrv_replace_child_noperm(child, new_bs);
if (new_bs) {
bdrv_get_cumulative_perm(new_bs, &perm, &shared_perm);
if (check_new_perm) {
bdrv_check_perm(new_bs, perm, shared_perm, NULL, &error_abort);
}
bdrv_set_perm(new_bs, perm, shared_perm);
}
}
BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
const char *child_name,
const BdrvChildRole *child_role,
@ -2891,35 +2912,82 @@ void bdrv_close_all(void)
assert(QTAILQ_EMPTY(&all_bdrv_states));
}
static void change_parent_backing_link(BlockDriverState *from,
BlockDriverState *to)
static bool should_update_child(BdrvChild *c, BlockDriverState *to)
{
BdrvChild *c, *next, *to_c;
BdrvChild *to_c;
if (c->role->stay_at_node) {
return false;
}
if (c->role == &child_backing) {
/* If @from is a backing file of @to, ignore the child to avoid
* creating a loop. We only want to change the pointer of other
* parents. */
QLIST_FOREACH(to_c, &to->children, next) {
if (to_c == c) {
break;
}
}
if (to_c) {
return false;
}
}
return true;
}
void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
Error **errp)
{
BdrvChild *c, *next;
GSList *list = NULL, *p;
uint64_t old_perm, old_shared;
uint64_t perm = 0, shared = BLK_PERM_ALL;
int ret;
assert(!atomic_read(&from->in_flight));
assert(!atomic_read(&to->in_flight));
/* Make sure that @from doesn't go away until we have successfully attached
* all of its parents to @to. */
bdrv_ref(from);
/* Put all parents into @list and calculate their cumulative permissions */
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
if (c->role->stay_at_node) {
if (!should_update_child(c, to)) {
continue;
}
if (c->role == &child_backing) {
/* If @from is a backing file of @to, ignore the child to avoid
* creating a loop. We only want to change the pointer of other
* parents. */
QLIST_FOREACH(to_c, &to->children, next) {
if (to_c == c) {
break;
}
}
if (to_c) {
continue;
}
}
list = g_slist_prepend(list, c);
perm |= c->perm;
shared &= c->shared_perm;
}
/* Check whether the required permissions can be granted on @to, ignoring
* all BdrvChild in @list so that they can't block themselves. */
ret = bdrv_check_update_perm(to, perm, shared, list, errp);
if (ret < 0) {
bdrv_abort_perm_update(to);
goto out;
}
/* Now actually perform the change. We performed the permission check for
* all elements of @list at once, so set the permissions all at once at the
* very end. */
for (p = list; p != NULL; p = p->next) {
c = p->data;
bdrv_ref(to);
/* FIXME Are we sure that bdrv_replace_child() can't run into
* &error_abort because of permissions? */
bdrv_replace_child(c, to, true);
bdrv_replace_child_noperm(c, to);
bdrv_unref(from);
}
bdrv_get_cumulative_perm(to, &old_perm, &old_shared);
bdrv_set_perm(to, old_perm | perm, old_shared | shared);
out:
g_slist_free(list);
bdrv_unref(from);
}
/*
@ -2943,16 +3011,18 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
{
Error *local_err = NULL;
assert(!atomic_read(&bs_top->in_flight));
assert(!atomic_read(&bs_new->in_flight));
bdrv_set_backing_hd(bs_new, bs_top, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto out;
}
change_parent_backing_link(bs_top, bs_new);
bdrv_replace_node(bs_top, bs_new, &local_err);
if (local_err) {
error_propagate(errp, local_err);
bdrv_set_backing_hd(bs_new, NULL, &error_abort);
goto out;
}
/* bs_new is now referenced by its new parents, we don't need the
* additional reference any more. */
@ -2960,18 +3030,6 @@ out:
bdrv_unref(bs_new);
}
void bdrv_replace_in_backing_chain(BlockDriverState *old, BlockDriverState *new)
{
assert(!bdrv_requests_pending(old));
assert(!bdrv_requests_pending(new));
bdrv_ref(old);
change_parent_backing_link(old, new);
bdrv_unref(old);
}
static void bdrv_delete(BlockDriverState *bs)
{
assert(!bs->job);

View File

@ -213,7 +213,12 @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
}
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
perm, BLK_PERM_ALL, blk, &error_abort);
perm, BLK_PERM_ALL, blk, errp);
if (!blk->root) {
bdrv_unref(bs);
blk_unref(blk);
return NULL;
}
return blk;
}

View File

@ -316,8 +316,20 @@ void commit_start(const char *job_id, BlockDriverState *bs,
goto fail;
}
bdrv_set_backing_hd(commit_top_bs, top, &error_abort);
bdrv_set_backing_hd(overlay_bs, commit_top_bs, &error_abort);
bdrv_set_backing_hd(commit_top_bs, top, &local_err);
if (local_err) {
bdrv_unref(commit_top_bs);
commit_top_bs = NULL;
error_propagate(errp, local_err);
goto fail;
}
bdrv_set_backing_hd(overlay_bs, commit_top_bs, &local_err);
if (local_err) {
bdrv_unref(commit_top_bs);
commit_top_bs = NULL;
error_propagate(errp, local_err);
goto fail;
}
s->commit_top_bs = commit_top_bs;
bdrv_unref(commit_top_bs);
@ -364,7 +376,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
/* Required permissions are already taken with block_job_add_bdrv() */
s->top = blk_new(0, BLK_PERM_ALL);
blk_insert_bs(s->top, top, errp);
ret = blk_insert_bs(s->top, top, errp);
if (ret < 0) {
goto fail;
}

View File

@ -12,6 +12,7 @@
#include "block/block_int.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qapi/util.h"
#include "qemu/uri.h"
#include "qemu/error-report.h"
#include "qemu/cutils.h"
@ -151,7 +152,7 @@ static QemuOptsList runtime_type_opts = {
{
.name = GLUSTER_OPT_TYPE,
.type = QEMU_OPT_STRING,
.help = "tcp|unix",
.help = "inet|unix",
},
{ /* end of list */ }
},
@ -170,14 +171,14 @@ static QemuOptsList runtime_unix_opts = {
},
};
static QemuOptsList runtime_tcp_opts = {
.name = "gluster_tcp",
.head = QTAILQ_HEAD_INITIALIZER(runtime_tcp_opts.head),
static QemuOptsList runtime_inet_opts = {
.name = "gluster_inet",
.head = QTAILQ_HEAD_INITIALIZER(runtime_inet_opts.head),
.desc = {
{
.name = GLUSTER_OPT_TYPE,
.type = QEMU_OPT_STRING,
.help = "tcp|unix",
.help = "inet|unix",
},
{
.name = GLUSTER_OPT_HOST,
@ -320,7 +321,7 @@ static int parse_volume_options(BlockdevOptionsGluster *gconf, char *path)
static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf,
const char *filename)
{
GlusterServer *gsconf;
SocketAddressFlat *gsconf;
URI *uri;
QueryParams *qp = NULL;
bool is_unix = false;
@ -331,19 +332,19 @@ static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf,
return -EINVAL;
}
gconf->server = g_new0(GlusterServerList, 1);
gconf->server->value = gsconf = g_new0(GlusterServer, 1);
gconf->server = g_new0(SocketAddressFlatList, 1);
gconf->server->value = gsconf = g_new0(SocketAddressFlat, 1);
/* transport */
if (!uri->scheme || !strcmp(uri->scheme, "gluster")) {
gsconf->type = GLUSTER_TRANSPORT_TCP;
gsconf->type = SOCKET_ADDRESS_FLAT_TYPE_INET;
} else if (!strcmp(uri->scheme, "gluster+tcp")) {
gsconf->type = GLUSTER_TRANSPORT_TCP;
gsconf->type = SOCKET_ADDRESS_FLAT_TYPE_INET;
} else if (!strcmp(uri->scheme, "gluster+unix")) {
gsconf->type = GLUSTER_TRANSPORT_UNIX;
gsconf->type = SOCKET_ADDRESS_FLAT_TYPE_UNIX;
is_unix = true;
} else if (!strcmp(uri->scheme, "gluster+rdma")) {
gsconf->type = GLUSTER_TRANSPORT_TCP;
gsconf->type = SOCKET_ADDRESS_FLAT_TYPE_INET;
error_report("Warning: rdma feature is not supported, falling "
"back to tcp");
} else {
@ -373,11 +374,11 @@ static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf,
}
gsconf->u.q_unix.path = g_strdup(qp->p[0].value);
} else {
gsconf->u.tcp.host = g_strdup(uri->server ? uri->server : "localhost");
gsconf->u.inet.host = g_strdup(uri->server ? uri->server : "localhost");
if (uri->port) {
gsconf->u.tcp.port = g_strdup_printf("%d", uri->port);
gsconf->u.inet.port = g_strdup_printf("%d", uri->port);
} else {
gsconf->u.tcp.port = g_strdup_printf("%d", GLUSTER_DEFAULT_PORT);
gsconf->u.inet.port = g_strdup_printf("%d", GLUSTER_DEFAULT_PORT);
}
}
@ -395,7 +396,7 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
struct glfs *glfs;
int ret;
int old_errno;
GlusterServerList *server;
SocketAddressFlatList *server;
unsigned long long port;
glfs = glfs_find_preopened(gconf->volume);
@ -411,21 +412,19 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
glfs_set_preopened(gconf->volume, glfs);
for (server = gconf->server; server; server = server->next) {
if (server->value->type == GLUSTER_TRANSPORT_UNIX) {
ret = glfs_set_volfile_server(glfs,
GlusterTransport_lookup[server->value->type],
if (server->value->type == SOCKET_ADDRESS_FLAT_TYPE_UNIX) {
ret = glfs_set_volfile_server(glfs, "unix",
server->value->u.q_unix.path, 0);
} else {
if (parse_uint_full(server->value->u.tcp.port, &port, 10) < 0 ||
if (parse_uint_full(server->value->u.inet.port, &port, 10) < 0 ||
port > 65535) {
error_setg(errp, "'%s' is not a valid port number",
server->value->u.tcp.port);
server->value->u.inet.port);
errno = EINVAL;
goto out;
}
ret = glfs_set_volfile_server(glfs,
GlusterTransport_lookup[server->value->type],
server->value->u.tcp.host,
ret = glfs_set_volfile_server(glfs, "tcp",
server->value->u.inet.host,
(int)port);
}
@ -444,13 +443,13 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
error_setg(errp, "Gluster connection for volume %s, path %s failed"
" to connect", gconf->volume, gconf->path);
for (server = gconf->server; server; server = server->next) {
if (server->value->type == GLUSTER_TRANSPORT_UNIX) {
if (server->value->type == SOCKET_ADDRESS_FLAT_TYPE_UNIX) {
error_append_hint(errp, "hint: failed on socket %s ",
server->value->u.q_unix.path);
} else {
error_append_hint(errp, "hint: failed on host %s and port %s ",
server->value->u.tcp.host,
server->value->u.tcp.port);
server->value->u.inet.host,
server->value->u.inet.port);
}
}
@ -474,23 +473,6 @@ out:
return NULL;
}
static int qapi_enum_parse(const char *opt)
{
int i;
if (!opt) {
return GLUSTER_TRANSPORT__MAX;
}
for (i = 0; i < GLUSTER_TRANSPORT__MAX; i++) {
if (!strcmp(opt, GlusterTransport_lookup[i])) {
return i;
}
}
return i;
}
/*
* Convert the json formatted command line into qapi.
*/
@ -498,8 +480,8 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
QDict *options, Error **errp)
{
QemuOpts *opts;
GlusterServer *gsconf;
GlusterServerList *curr = NULL;
SocketAddressFlat *gsconf = NULL;
SocketAddressFlatList *curr = NULL;
QDict *backing_options = NULL;
Error *local_err = NULL;
char *str = NULL;
@ -547,25 +529,31 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
}
ptr = qemu_opt_get(opts, GLUSTER_OPT_TYPE);
gsconf = g_new0(GlusterServer, 1);
gsconf->type = qapi_enum_parse(ptr);
if (!ptr) {
error_setg(&local_err, QERR_MISSING_PARAMETER, GLUSTER_OPT_TYPE);
error_append_hint(&local_err, GERR_INDEX_HINT, i);
goto out;
}
if (gsconf->type == GLUSTER_TRANSPORT__MAX) {
error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE,
GLUSTER_OPT_TYPE, "tcp or unix");
gsconf = g_new0(SocketAddressFlat, 1);
if (!strcmp(ptr, "tcp")) {
ptr = "inet"; /* accept legacy "tcp" */
}
gsconf->type = qapi_enum_parse(SocketAddressFlatType_lookup, ptr,
SOCKET_ADDRESS_FLAT_TYPE__MAX, -1,
&local_err);
if (local_err) {
error_append_hint(&local_err,
"Parameter '%s' may be 'inet' or 'unix'\n",
GLUSTER_OPT_TYPE);
error_append_hint(&local_err, GERR_INDEX_HINT, i);
goto out;
}
qemu_opts_del(opts);
if (gsconf->type == GLUSTER_TRANSPORT_TCP) {
/* create opts info from runtime_tcp_opts list */
opts = qemu_opts_create(&runtime_tcp_opts, NULL, 0, &error_abort);
if (gsconf->type == SOCKET_ADDRESS_FLAT_TYPE_INET) {
/* create opts info from runtime_inet_opts list */
opts = qemu_opts_create(&runtime_inet_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, backing_options, &local_err);
if (local_err) {
goto out;
@ -578,7 +566,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
error_append_hint(&local_err, GERR_INDEX_HINT, i);
goto out;
}
gsconf->u.tcp.host = g_strdup(ptr);
gsconf->u.inet.host = g_strdup(ptr);
ptr = qemu_opt_get(opts, GLUSTER_OPT_PORT);
if (!ptr) {
error_setg(&local_err, QERR_MISSING_PARAMETER,
@ -586,28 +574,28 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
error_append_hint(&local_err, GERR_INDEX_HINT, i);
goto out;
}
gsconf->u.tcp.port = g_strdup(ptr);
gsconf->u.inet.port = g_strdup(ptr);
/* defend for unsupported fields in InetSocketAddress,
* i.e. @ipv4, @ipv6 and @to
*/
ptr = qemu_opt_get(opts, GLUSTER_OPT_TO);
if (ptr) {
gsconf->u.tcp.has_to = true;
gsconf->u.inet.has_to = true;
}
ptr = qemu_opt_get(opts, GLUSTER_OPT_IPV4);
if (ptr) {
gsconf->u.tcp.has_ipv4 = true;
gsconf->u.inet.has_ipv4 = true;
}
ptr = qemu_opt_get(opts, GLUSTER_OPT_IPV6);
if (ptr) {
gsconf->u.tcp.has_ipv6 = true;
gsconf->u.inet.has_ipv6 = true;
}
if (gsconf->u.tcp.has_to) {
if (gsconf->u.inet.has_to) {
error_setg(&local_err, "Parameter 'to' not supported");
goto out;
}
if (gsconf->u.tcp.has_ipv4 || gsconf->u.tcp.has_ipv6) {
if (gsconf->u.inet.has_ipv4 || gsconf->u.inet.has_ipv6) {
error_setg(&local_err, "Parameters 'ipv4/ipv6' not supported");
goto out;
}
@ -632,16 +620,18 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
}
if (gconf->server == NULL) {
gconf->server = g_new0(GlusterServerList, 1);
gconf->server = g_new0(SocketAddressFlatList, 1);
gconf->server->value = gsconf;
curr = gconf->server;
} else {
curr->next = g_new0(GlusterServerList, 1);
curr->next = g_new0(SocketAddressFlatList, 1);
curr->next->value = gsconf;
curr = curr->next;
}
gsconf = NULL;
qdict_del(backing_options, str);
QDECREF(backing_options);
backing_options = NULL;
g_free(str);
str = NULL;
}
@ -650,11 +640,10 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
out:
error_propagate(errp, local_err);
qapi_free_SocketAddressFlat(gsconf);
qemu_opts_del(opts);
if (str) {
qdict_del(backing_options, str);
g_free(str);
}
g_free(str);
QDECREF(backing_options);
errno = EINVAL;
return -errno;
}
@ -683,7 +672,7 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
"file.volume=testvol,file.path=/path/a.qcow2"
"[,file.debug=9]"
"[,file.logfile=/path/filename.log],"
"file.server.0.type=tcp,"
"file.server.0.type=inet,"
"file.server.0.host=1.2.3.4,"
"file.server.0.port=24007,"
"file.server.1.transport=unix,"

View File

@ -509,6 +509,13 @@ static void mirror_exit(BlockJob *job, void *opaque)
* block_job_completed(). */
bdrv_ref(src);
bdrv_ref(mirror_top_bs);
bdrv_ref(target_bs);
/* Remove target parent that still uses BLK_PERM_WRITE/RESIZE before
* inserting target_bs at s->to_replace, where we might not be able to get
* these permissions. */
blk_unref(s->target);
s->target = NULL;
/* We don't access the source any more. Dropping any WRITE/RESIZE is
* required before it could become a backing file of target_bs. */
@ -543,8 +550,12 @@ static void mirror_exit(BlockJob *job, void *opaque)
/* The mirror job has no requests in flight any more, but we need to
* drain potential other users of the BDS before changing the graph. */
bdrv_drained_begin(target_bs);
bdrv_replace_in_backing_chain(to_replace, target_bs);
bdrv_replace_node(to_replace, target_bs, &local_err);
bdrv_drained_end(target_bs);
if (local_err) {
error_report_err(local_err);
data->ret = -EPERM;
}
}
if (s->to_replace) {
bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
@ -555,19 +566,19 @@ static void mirror_exit(BlockJob *job, void *opaque)
aio_context_release(replace_aio_context);
}
g_free(s->replaces);
blk_unref(s->target);
s->target = NULL;
bdrv_unref(target_bs);
/* Remove the mirror filter driver from the graph. Before this, get rid of
* the blockers on the intermediate nodes so that the resulting state is
* valid. */
* valid. Also give up permissions on mirror_top_bs->backing, which might
* block the removal. */
block_job_remove_all_bdrv(job);
bdrv_replace_in_backing_chain(mirror_top_bs, backing_bs(mirror_top_bs));
bdrv_child_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL);
bdrv_replace_node(mirror_top_bs, backing_bs(mirror_top_bs), &error_abort);
/* We just changed the BDS the job BB refers to (with either or both of the
* bdrv_replace_in_backing_chain() calls), so switch the BB back so the
* cleanup does the right thing. We don't need any permissions any more
* now. */
* bdrv_replace_node() calls), so switch the BB back so the cleanup does
* the right thing. We don't need any permissions any more now. */
blk_remove_bs(job->blk);
blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
blk_insert_bs(job->blk, mirror_top_bs, &error_abort);
@ -1189,10 +1200,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp);
if (!s->dirty_bitmap) {
g_free(s->replaces);
blk_unref(s->target);
block_job_unref(&s->common);
return;
goto fail;
}
/* Required permissions are already taken with blk_new() */
@ -1228,7 +1236,8 @@ fail:
block_job_unref(&s->common);
}
bdrv_replace_in_backing_chain(mirror_top_bs, backing_bs(mirror_top_bs));
bdrv_child_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL);
bdrv_replace_node(mirror_top_bs, backing_bs(mirror_top_bs), &error_abort);
}
void mirror_start(const char *job_id, BlockDriverState *bs,

View File

@ -14,6 +14,8 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qint.h"
#include "qemu/uri.h"
#include "qemu/error-report.h"
#include "qemu/sockets.h"
@ -374,8 +376,7 @@ struct BDRVSheepdogState {
uint32_t cache_flags;
bool discard_supported;
char *host_spec;
bool is_unix;
SocketAddress *addr;
int fd;
CoMutex lock;
@ -527,21 +528,36 @@ static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s,
QLIST_INSERT_HEAD(&s->inflight_aiocb_head, acb, aiocb_siblings);
}
static SocketAddress *sd_socket_address(const char *path,
const char *host, const char *port)
{
SocketAddress *addr = g_new0(SocketAddress, 1);
if (path) {
addr->type = SOCKET_ADDRESS_KIND_UNIX;
addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
addr->u.q_unix.data->path = g_strdup(path);
} else {
addr->type = SOCKET_ADDRESS_KIND_INET;
addr->u.inet.data = g_new0(InetSocketAddress, 1);
addr->u.inet.data->host = g_strdup(host ?: SD_DEFAULT_ADDR);
addr->u.inet.data->port = g_strdup(port ?: stringify(SD_DEFAULT_PORT));
}
return addr;
}
/* Return -EIO in case of error, file descriptor on success */
static int connect_to_sdog(BDRVSheepdogState *s, Error **errp)
{
int fd;
if (s->is_unix) {
fd = unix_connect(s->host_spec, errp);
} else {
fd = inet_connect(s->host_spec, errp);
fd = socket_connect(s->addr, errp, NULL, NULL);
if (fd >= 0) {
int ret = socket_set_nodelay(fd);
if (ret < 0) {
error_report("%s", strerror(errno));
}
if (s->addr->type == SOCKET_ADDRESS_KIND_INET && fd >= 0) {
int ret = socket_set_nodelay(fd);
if (ret < 0) {
error_report("%s", strerror(errno));
}
}
@ -820,8 +836,7 @@ static void coroutine_fn aio_read_response(void *opaque)
case AIOCB_DISCARD_OBJ:
switch (rsp.result) {
case SD_RES_INVALID_PARMS:
error_report("sheep(%s) doesn't support discard command",
s->host_spec);
error_report("server doesn't support discard command");
rsp.result = SD_RES_SUCCESS;
s->discard_supported = false;
break;
@ -914,71 +929,155 @@ static int get_sheep_fd(BDRVSheepdogState *s, Error **errp)
return fd;
}
static int sd_parse_uri(BDRVSheepdogState *s, const char *filename,
char *vdi, uint32_t *snapid, char *tag)
/*
* Parse numeric snapshot ID in @str
* If @str can't be parsed as number, return false.
* Else, if the number is zero or too large, set *@snapid to zero and
* return true.
* Else, set *@snapid to the number and return true.
*/
static bool sd_parse_snapid(const char *str, uint32_t *snapid)
{
URI *uri;
QueryParams *qp = NULL;
int ret = 0;
unsigned long ul;
int ret;
uri = uri_parse(filename);
ret = qemu_strtoul(str, NULL, 10, &ul);
if (ret == -ERANGE) {
ul = ret = 0;
}
if (ret) {
return false;
}
if (ul > UINT32_MAX) {
ul = 0;
}
*snapid = ul;
return true;
}
static bool sd_parse_snapid_or_tag(const char *str,
uint32_t *snapid, char tag[])
{
if (!sd_parse_snapid(str, snapid)) {
*snapid = 0;
if (g_strlcpy(tag, str, SD_MAX_VDI_TAG_LEN) >= SD_MAX_VDI_TAG_LEN) {
return false;
}
} else if (!*snapid) {
return false;
} else {
tag[0] = 0;
}
return true;
}
typedef struct {
const char *path; /* non-null iff transport is tcp */
const char *host; /* valid when transport is tcp */
int port; /* valid when transport is tcp */
char vdi[SD_MAX_VDI_LEN];
char tag[SD_MAX_VDI_TAG_LEN];
uint32_t snap_id;
/* Remainder is only for sd_config_done() */
URI *uri;
QueryParams *qp;
} SheepdogConfig;
static void sd_config_done(SheepdogConfig *cfg)
{
if (cfg->qp) {
query_params_free(cfg->qp);
}
uri_free(cfg->uri);
}
static void sd_parse_uri(SheepdogConfig *cfg, const char *filename,
Error **errp)
{
Error *err = NULL;
QueryParams *qp = NULL;
bool is_unix;
URI *uri;
memset(cfg, 0, sizeof(*cfg));
cfg->uri = uri = uri_parse(filename);
if (!uri) {
return -EINVAL;
error_setg(&err, "invalid URI");
goto out;
}
/* transport */
if (!strcmp(uri->scheme, "sheepdog")) {
s->is_unix = false;
is_unix = false;
} else if (!strcmp(uri->scheme, "sheepdog+tcp")) {
s->is_unix = false;
is_unix = false;
} else if (!strcmp(uri->scheme, "sheepdog+unix")) {
s->is_unix = true;
is_unix = true;
} else {
ret = -EINVAL;
error_setg(&err, "URI scheme must be 'sheepdog', 'sheepdog+tcp',"
" or 'sheepdog+unix'");
goto out;
}
if (uri->path == NULL || !strcmp(uri->path, "/")) {
ret = -EINVAL;
error_setg(&err, "missing file path in URI");
goto out;
}
pstrcpy(vdi, SD_MAX_VDI_LEN, uri->path + 1);
qp = query_params_parse(uri->query);
if (qp->n > 1 || (s->is_unix && !qp->n) || (!s->is_unix && qp->n)) {
ret = -EINVAL;
if (g_strlcpy(cfg->vdi, uri->path + 1, SD_MAX_VDI_LEN)
>= SD_MAX_VDI_LEN) {
error_setg(&err, "VDI name is too long");
goto out;
}
if (s->is_unix) {
cfg->qp = qp = query_params_parse(uri->query);
if (is_unix) {
/* sheepdog+unix:///vdiname?socket=path */
if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) {
ret = -EINVAL;
if (uri->server || uri->port) {
error_setg(&err, "URI scheme %s doesn't accept a server address",
uri->scheme);
goto out;
}
s->host_spec = g_strdup(qp->p[0].value);
if (!qp->n) {
error_setg(&err,
"URI scheme %s requires query parameter 'socket'",
uri->scheme);
goto out;
}
if (qp->n != 1 || strcmp(qp->p[0].name, "socket")) {
error_setg(&err, "unexpected query parameters");
goto out;
}
cfg->path = qp->p[0].value;
} else {
/* sheepdog[+tcp]://[host:port]/vdiname */
s->host_spec = g_strdup_printf("%s:%d", uri->server ?: SD_DEFAULT_ADDR,
uri->port ?: SD_DEFAULT_PORT);
if (qp->n) {
error_setg(&err, "unexpected query parameters");
goto out;
}
cfg->host = uri->server;
cfg->port = uri->port;
}
/* snapshot tag */
if (uri->fragment) {
*snapid = strtoul(uri->fragment, NULL, 10);
if (*snapid == 0) {
pstrcpy(tag, SD_MAX_VDI_TAG_LEN, uri->fragment);
if (!sd_parse_snapid_or_tag(uri->fragment,
&cfg->snap_id, cfg->tag)) {
error_setg(&err, "'%s' is not a valid snapshot ID",
uri->fragment);
goto out;
}
} else {
*snapid = CURRENT_VDI_ID; /* search current vdi */
cfg->snap_id = CURRENT_VDI_ID; /* search current vdi */
}
out:
if (qp) {
query_params_free(qp);
if (err) {
error_propagate(errp, err);
sd_config_done(cfg);
}
uri_free(uri);
return ret;
}
/*
@ -998,12 +1097,13 @@ out:
* You can run VMs outside the Sheepdog cluster by specifying
* `hostname' and `port' (experimental).
*/
static int parse_vdiname(BDRVSheepdogState *s, const char *filename,
char *vdi, uint32_t *snapid, char *tag)
static void parse_vdiname(SheepdogConfig *cfg, const char *filename,
Error **errp)
{
Error *err = NULL;
char *p, *q, *uri;
const char *host_spec, *vdi_spec;
int nr_sep, ret;
int nr_sep;
strstart(filename, "sheepdog:", &filename);
p = q = g_strdup(filename);
@ -1038,12 +1138,60 @@ static int parse_vdiname(BDRVSheepdogState *s, const char *filename,
uri = g_strdup_printf("sheepdog://%s/%s", host_spec, vdi_spec);
ret = sd_parse_uri(s, uri, vdi, snapid, tag);
/*
* FIXME We to escape URI meta-characters, e.g. "x?y=z"
* produces "sheepdog://x?y=z". Because of that ...
*/
sd_parse_uri(cfg, uri, &err);
if (err) {
/*
* ... this can fail, but the error message is misleading.
* Replace it by the traditional useless one until the
* escaping is fixed.
*/
error_free(err);
error_setg(errp, "Can't parse filename");
}
g_free(q);
g_free(uri);
}
return ret;
static void sd_parse_filename(const char *filename, QDict *options,
Error **errp)
{
Error *err = NULL;
SheepdogConfig cfg;
char buf[32];
if (strstr(filename, "://")) {
sd_parse_uri(&cfg, filename, &err);
} else {
parse_vdiname(&cfg, filename, &err);
}
if (err) {
error_propagate(errp, err);
return;
}
if (cfg.host) {
qdict_set_default_str(options, "host", cfg.host);
}
if (cfg.port) {
snprintf(buf, sizeof(buf), "%d", cfg.port);
qdict_set_default_str(options, "port", buf);
}
if (cfg.path) {
qdict_set_default_str(options, "path", cfg.path);
}
qdict_set_default_str(options, "vdi", cfg.vdi);
qdict_set_default_str(options, "tag", cfg.tag);
if (cfg.snap_id) {
snprintf(buf, sizeof(buf), "%d", cfg.snap_id);
qdict_set_default_str(options, "snap-id", buf);
}
sd_config_done(&cfg);
}
static int find_vdi_name(BDRVSheepdogState *s, const char *filename,
@ -1357,15 +1505,33 @@ static void sd_attach_aio_context(BlockDriverState *bs,
co_read_response, NULL, NULL, s);
}
/* TODO Convert to fine grained options */
static QemuOptsList runtime_opts = {
.name = "sheepdog",
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
.desc = {
{
.name = "filename",
.name = "host",
.type = QEMU_OPT_STRING,
},
{
.name = "port",
.type = QEMU_OPT_STRING,
},
{
.name = "path",
.type = QEMU_OPT_STRING,
},
{
.name = "vdi",
.type = QEMU_OPT_STRING,
},
{
.name = "snap-id",
.type = QEMU_OPT_NUMBER,
},
{
.name = "tag",
.type = QEMU_OPT_STRING,
.help = "URL to the sheepdog image",
},
{ /* end of list */ }
},
@ -1377,12 +1543,11 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
int ret, fd;
uint32_t vid = 0;
BDRVSheepdogState *s = bs->opaque;
char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN];
uint32_t snapid;
const char *host, *port, *path, *vdi, *snap_id_str, *tag;
uint64_t snap_id;
char *buf = NULL;
QemuOpts *opts;
Error *local_err = NULL;
const char *filename;
s->bs = bs;
s->aio_context = bdrv_get_aio_context(bs);
@ -1392,37 +1557,68 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
if (local_err) {
error_propagate(errp, local_err);
ret = -EINVAL;
goto out;
goto err_no_fd;
}
filename = qemu_opt_get(opts, "filename");
host = qemu_opt_get(opts, "host");
port = qemu_opt_get(opts, "port");
path = qemu_opt_get(opts, "path");
vdi = qemu_opt_get(opts, "vdi");
snap_id_str = qemu_opt_get(opts, "snap-id");
snap_id = qemu_opt_get_number(opts, "snap-id", CURRENT_VDI_ID);
tag = qemu_opt_get(opts, "tag");
if ((host || port) && path) {
error_setg(errp, "can't use 'path' together with 'host' or 'port'");
ret = -EINVAL;
goto err_no_fd;
}
if (!vdi) {
error_setg(errp, "parameter 'vdi' is missing");
ret = -EINVAL;
goto err_no_fd;
}
if (strlen(vdi) >= SD_MAX_VDI_LEN) {
error_setg(errp, "value of parameter 'vdi' is too long");
ret = -EINVAL;
goto err_no_fd;
}
if (snap_id > UINT32_MAX) {
snap_id = 0;
}
if (snap_id_str && !snap_id) {
error_setg(errp, "'snap-id=%s' is not a valid snapshot ID",
snap_id_str);
ret = -EINVAL;
goto err_no_fd;
}
if (!tag) {
tag = "";
}
if (tag && strlen(tag) >= SD_MAX_VDI_TAG_LEN) {
error_setg(errp, "value of parameter 'tag' is too long");
ret = -EINVAL;
goto err_no_fd;
}
s->addr = sd_socket_address(path, host, port);
QLIST_INIT(&s->inflight_aio_head);
QLIST_INIT(&s->failed_aio_head);
QLIST_INIT(&s->inflight_aiocb_head);
s->fd = -1;
memset(vdi, 0, sizeof(vdi));
memset(tag, 0, sizeof(tag));
if (strstr(filename, "://")) {
ret = sd_parse_uri(s, filename, vdi, &snapid, tag);
} else {
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, errp);
if (s->fd < 0) {
ret = s->fd;
goto out;
goto err_no_fd;
}
ret = find_vdi_name(s, vdi, snapid, tag, &vid, true, errp);
ret = find_vdi_name(s, vdi, (uint32_t)snap_id, tag, &vid, true, errp);
if (ret) {
goto out;
goto err;
}
/*
@ -1435,7 +1631,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
}
s->discard_supported = true;
if (snapid || tag[0] != '\0') {
if (snap_id || tag[0]) {
DPRINTF("%" PRIx32 " snapshot inode was open.\n", vid);
s->is_snapshot = true;
}
@ -1443,7 +1639,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
fd = connect_to_sdog(s, errp);
if (fd < 0) {
ret = fd;
goto out;
goto err;
}
buf = g_malloc(SD_INODE_SIZE);
@ -1454,7 +1650,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
if (ret) {
error_setg(errp, "Can't read snapshot inode");
goto out;
goto err;
}
memcpy(&s->inode, buf, sizeof(s->inode));
@ -1466,12 +1662,12 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
qemu_opts_del(opts);
g_free(buf);
return 0;
out:
err:
aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd,
false, NULL, NULL, NULL, NULL);
if (s->fd >= 0) {
closesocket(s->fd);
}
closesocket(s->fd);
err_no_fd:
qemu_opts_del(opts);
g_free(buf);
return ret;
@ -1685,6 +1881,7 @@ static int parse_redundancy(BDRVSheepdogState *s, const char *opt)
}
copy = strtol(n1, NULL, 10);
/* FIXME fix error checking by switching to qemu_strtol() */
if (copy > SD_MAX_COPIES || copy < 1) {
return -EINVAL;
}
@ -1699,6 +1896,7 @@ static int parse_redundancy(BDRVSheepdogState *s, const char *opt)
}
parity = strtol(n2, NULL, 10);
/* FIXME fix error checking by switching to qemu_strtol() */
if (parity >= SD_EC_MAX_STRIP || parity < 1) {
return -EINVAL;
}
@ -1737,29 +1935,34 @@ static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt)
static int sd_create(const char *filename, QemuOpts *opts,
Error **errp)
{
Error *err = NULL;
int ret = 0;
uint32_t vid = 0;
char *backing_file = NULL;
char *buf = NULL;
BDRVSheepdogState *s;
char tag[SD_MAX_VDI_TAG_LEN];
uint32_t snapid;
SheepdogConfig cfg;
uint64_t max_vdi_size;
bool prealloc = false;
s = g_new0(BDRVSheepdogState, 1);
memset(tag, 0, sizeof(tag));
if (strstr(filename, "://")) {
ret = sd_parse_uri(s, filename, s->name, &snapid, tag);
sd_parse_uri(&cfg, filename, &err);
} else {
ret = parse_vdiname(s, filename, s->name, &snapid, tag);
parse_vdiname(&cfg, filename, &err);
}
if (ret < 0) {
error_setg(errp, "Can't parse filename");
if (err) {
error_propagate(errp, err);
goto out;
}
buf = cfg.port ? g_strdup_printf("%d", cfg.port) : NULL;
s->addr = sd_socket_address(cfg.path, cfg.host, buf);
g_free(buf);
strcpy(s->name, cfg.vdi);
sd_config_done(&cfg);
s->inode.vdi_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
BDRV_SECTOR_SIZE);
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
@ -1829,14 +2032,12 @@ static int sd_create(const char *filename, QemuOpts *opts,
if (s->inode.block_size_shift == 0) {
SheepdogVdiReq hdr;
SheepdogClusterRsp *rsp = (SheepdogClusterRsp *)&hdr;
Error *local_err = NULL;
int fd;
unsigned int wlen = 0, rlen = 0;
fd = connect_to_sdog(s, &local_err);
fd = connect_to_sdog(s, errp);
if (fd < 0) {
error_report_err(local_err);
ret = -EIO;
ret = fd;
goto out;
}
@ -1922,7 +2123,7 @@ static void sd_close(BlockDriverState *bs)
aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd,
false, NULL, NULL, NULL, NULL);
closesocket(s->fd);
g_free(s->host_spec);
qapi_free_SocketAddress(s->addr);
}
static int64_t sd_getlength(BlockDriverState *bs)
@ -2367,19 +2568,16 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
BDRVSheepdogState *old_s;
char tag[SD_MAX_VDI_TAG_LEN];
uint32_t snapid = 0;
int ret = 0;
int ret;
if (!sd_parse_snapid_or_tag(snapshot_id, &snapid, tag)) {
return -EINVAL;
}
old_s = g_new(BDRVSheepdogState, 1);
memcpy(old_s, s, sizeof(BDRVSheepdogState));
snapid = strtoul(snapshot_id, NULL, 10);
if (snapid) {
tag[0] = 0;
} else {
pstrcpy(tag, sizeof(tag), snapshot_id);
}
ret = reload_inode(s, snapid, tag);
if (ret) {
goto out;
@ -2405,18 +2603,15 @@ out:
#define NR_BATCHED_DISCARD 128
static bool remove_objects(BDRVSheepdogState *s)
static int remove_objects(BDRVSheepdogState *s, Error **errp)
{
int fd, i = 0, nr_objs = 0;
Error *local_err = NULL;
int ret = 0;
bool result = true;
int ret;
SheepdogInode *inode = &s->inode;
fd = connect_to_sdog(s, &local_err);
fd = connect_to_sdog(s, errp);
if (fd < 0) {
error_report_err(local_err);
return false;
return fd;
}
nr_objs = count_data_objs(inode);
@ -2446,15 +2641,15 @@ static bool remove_objects(BDRVSheepdogState *s)
data_vdi_id[start_idx]),
false, s->cache_flags);
if (ret < 0) {
error_report("failed to discard snapshot inode.");
result = false;
error_setg(errp, "Failed to discard snapshot inode");
goto out;
}
}
ret = 0;
out:
closesocket(fd);
return result;
return ret;
}
static int sd_snapshot_delete(BlockDriverState *bs,
@ -2462,9 +2657,12 @@ static int sd_snapshot_delete(BlockDriverState *bs,
const char *name,
Error **errp)
{
/*
* FIXME should delete the snapshot matching both @snapshot_id and
* @name, but @name not used here
*/
unsigned long snap_id = 0;
char snap_tag[SD_MAX_VDI_TAG_LEN];
Error *local_err = NULL;
int fd, ret;
char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN];
BDRVSheepdogState *s = bs->opaque;
@ -2477,15 +2675,22 @@ static int sd_snapshot_delete(BlockDriverState *bs,
};
SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
if (!remove_objects(s)) {
return -1;
ret = remove_objects(s, errp);
if (ret) {
return ret;
}
memset(buf, 0, sizeof(buf));
memset(snap_tag, 0, sizeof(snap_tag));
pstrcpy(buf, SD_MAX_VDI_LEN, s->name);
/* TODO Use sd_parse_snapid() once this mess is cleaned up */
ret = qemu_strtoul(snapshot_id, NULL, 10, &snap_id);
if (ret || snap_id > UINT32_MAX) {
/*
* FIXME Since qemu_strtoul() returns -EINVAL when
* @snapshot_id is null, @snapshot_id is mandatory. Correct
* would be to require at least one of @snapshot_id and @name.
*/
error_setg(errp, "Invalid snapshot ID: %s",
snapshot_id ? snapshot_id : "<null>");
return -EINVAL;
@ -2494,40 +2699,42 @@ static int sd_snapshot_delete(BlockDriverState *bs,
if (snap_id) {
hdr.snapid = (uint32_t) snap_id;
} else {
/* FIXME I suspect we should use @name here */
/* FIXME don't truncate silently */
pstrcpy(snap_tag, sizeof(snap_tag), snapshot_id);
pstrcpy(buf + SD_MAX_VDI_LEN, SD_MAX_VDI_TAG_LEN, snap_tag);
}
ret = find_vdi_name(s, s->name, snap_id, snap_tag, &vid, true,
&local_err);
ret = find_vdi_name(s, s->name, snap_id, snap_tag, &vid, true, errp);
if (ret) {
return ret;
}
fd = connect_to_sdog(s, &local_err);
fd = connect_to_sdog(s, errp);
if (fd < 0) {
error_report_err(local_err);
return -1;
return fd;
}
ret = do_req(fd, s->bs, (SheepdogReq *)&hdr,
buf, &wlen, &rlen);
closesocket(fd);
if (ret) {
error_setg_errno(errp, -ret, "Couldn't send request to server");
return ret;
}
switch (rsp->result) {
case SD_RES_NO_VDI:
error_report("%s was already deleted", s->name);
error_setg(errp, "Can't find the snapshot");
return -ENOENT;
case SD_RES_SUCCESS:
break;
default:
error_report("%s, %s", sd_strerror(rsp->result), s->name);
return -1;
error_setg(errp, "%s", sd_strerror(rsp->result));
return -EIO;
}
return ret;
return 0;
}
static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
@ -2832,7 +3039,7 @@ static BlockDriver bdrv_sheepdog = {
.format_name = "sheepdog",
.protocol_name = "sheepdog",
.instance_size = sizeof(BDRVSheepdogState),
.bdrv_needs_filename = true,
.bdrv_parse_filename = sd_parse_filename,
.bdrv_file_open = sd_open,
.bdrv_reopen_prepare = sd_reopen_prepare,
.bdrv_reopen_commit = sd_reopen_commit,
@ -2868,7 +3075,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
.format_name = "sheepdog",
.protocol_name = "sheepdog+tcp",
.instance_size = sizeof(BDRVSheepdogState),
.bdrv_needs_filename = true,
.bdrv_parse_filename = sd_parse_filename,
.bdrv_file_open = sd_open,
.bdrv_reopen_prepare = sd_reopen_prepare,
.bdrv_reopen_commit = sd_reopen_commit,
@ -2904,7 +3111,7 @@ static BlockDriver bdrv_sheepdog_unix = {
.format_name = "sheepdog",
.protocol_name = "sheepdog+unix",
.instance_size = sizeof(BDRVSheepdogState),
.bdrv_needs_filename = true,
.bdrv_parse_filename = sd_parse_filename,
.bdrv_file_open = sd_open,
.bdrv_reopen_prepare = sd_reopen_prepare,
.bdrv_reopen_commit = sd_reopen_commit,

View File

@ -1614,6 +1614,7 @@ typedef struct ExternalSnapshotState {
BlockDriverState *old_bs;
BlockDriverState *new_bs;
AioContext *aio_context;
bool overlay_appended;
} ExternalSnapshotState;
static void external_snapshot_prepare(BlkActionState *common,
@ -1780,6 +1781,7 @@ static void external_snapshot_prepare(BlkActionState *common,
error_propagate(errp, local_err);
return;
}
state->overlay_appended = true;
}
static void external_snapshot_commit(BlkActionState *common)
@ -1803,8 +1805,8 @@ static void external_snapshot_abort(BlkActionState *common)
ExternalSnapshotState *state =
DO_UPCAST(ExternalSnapshotState, common, common);
if (state->new_bs) {
if (state->new_bs->backing) {
bdrv_replace_in_backing_chain(state->new_bs, state->old_bs);
if (state->overlay_appended) {
bdrv_replace_node(state->new_bs, state->old_bs, &error_abort);
}
}
}

View File

@ -238,8 +238,8 @@ int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp);
BlockDriverState *bdrv_new(void);
void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
Error **errp);
void bdrv_replace_in_backing_chain(BlockDriverState *old,
BlockDriverState *new);
void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
Error **errp);
int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough);
int bdrv_parse_discard_flags(const char *mode, int *flags);

View File

@ -441,8 +441,8 @@ typedef struct BdrvAioNotifier {
} BdrvAioNotifier;
struct BdrvChildRole {
/* If true, bdrv_replace_in_backing_chain() doesn't change the node this
* BdrvChild points to. */
/* If true, bdrv_replace_node() doesn't change the node this BdrvChild
* points to. */
bool stay_at_node;
void (*inherit_options)(int *child_flags, QDict *child_options,
@ -890,7 +890,7 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
void bdrv_root_unref_child(BdrvChild *child);
int bdrv_child_check_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
Error **errp);
GSList *ignore_children, Error **errp);
void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
void bdrv_child_abort_perm_update(BdrvChild *c);
int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,

View File

@ -4100,6 +4100,44 @@
'vsock': 'VsockSocketAddress',
'fd': 'String' } }
##
# @SocketAddressFlatType:
#
# Available SocketAddressFlat types
#
# @inet: Internet address
#
# @unix: Unix domain socket
#
# Since: 2.9
##
{ 'enum': 'SocketAddressFlatType',
'data': [ 'unix', 'inet' ] }
##
# @SocketAddressFlat:
#
# Captures the address of a socket
#
# @type: Transport type
#
# This is similar to SocketAddress, only distinction:
#
# 1. SocketAddressFlat is a flat union, SocketAddress is a simple union.
# A flat union is nicer than simple because it avoids nesting
# (i.e. more {}) on the wire.
#
# 2. SocketAddressFlat supports only types 'unix' and 'inet', because
# that's what its current users need.
#
# Since: 2.9
##
{ 'union': 'SocketAddressFlat',
'base': { 'type': 'SocketAddressFlatType' },
'discriminator': 'type',
'data': { 'unix': 'UnixSocketAddress',
'inet': 'InetSocketAddress' } }
##
# @getfd:
#

View File

@ -2124,6 +2124,7 @@
# @ssh: Since 2.8
# @iscsi: Since 2.9
# @rbd: Since 2.9
# @sheepdog: Since 2.9
#
# Since: 2.0
##
@ -2132,8 +2133,8 @@
'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom',
'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed',
'quorum', 'raw', 'rbd', 'replication', 'ssh', 'vdi', 'vhdx', 'vmdk',
'vpc', 'vvfat' ] }
'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
##
# @BlockdevOptionsFile:
@ -2545,50 +2546,6 @@
'*rewrite-corrupted': 'bool',
'*read-pattern': 'QuorumReadPattern' } }
##
# @GlusterTransport:
#
# An enumeration of Gluster transport types
#
# @tcp: TCP - Transmission Control Protocol
#
# @unix: UNIX - Unix domain socket
#
# Since: 2.7
##
{ 'enum': 'GlusterTransport',
'data': [ 'unix', 'tcp' ] }
##
# @GlusterServer:
#
# Captures the address of a socket
#
# Details for connecting to a gluster server
#
# @type: Transport type used for gluster connection
#
# This is similar to SocketAddress, only distinction:
#
# 1. GlusterServer is a flat union, SocketAddress is a simple union.
# A flat union is nicer than simple because it avoids nesting
# (i.e. more {}) on the wire.
#
# 2. GlusterServer lacks case 'fd', since gluster doesn't let you
# pass in a file descriptor.
#
# GlusterServer is actually not Gluster-specific, its a
# compatibility evolved into an alternate for SocketAddress.
#
# Since: 2.7
##
{ 'union': 'GlusterServer',
'base': { 'type': 'GlusterTransport' },
'discriminator': 'type',
'data': { 'unix': 'UnixSocketAddress',
'tcp': 'InetSocketAddress' } }
##
# @BlockdevOptionsGluster:
#
@ -2610,7 +2567,7 @@
{ 'struct': 'BlockdevOptionsGluster',
'data': { 'volume': 'str',
'path': 'str',
'server': ['GlusterServer'],
'server': ['SocketAddressFlat'],
'*debug': 'int',
'*logfile': 'str' } }
@ -2735,6 +2692,26 @@
'*auth-supported': ['RbdAuthMethod'],
'*password-secret': 'str' } }
##
# @BlockdevOptionsSheepdog:
#
# Driver specific block device options for sheepdog
#
# @vdi: Virtual disk image name
# @addr: The Sheepdog server to connect to
# @snap-id: Snapshot ID
# @tag: Snapshot tag name
#
# Only one of @snap-id and @tag may be present.
#
# Since: 2.9
##
{ 'struct': 'BlockdevOptionsSheepdog',
'data': { 'addr': 'SocketAddressFlat',
'vdi': 'str',
'*snap-id': 'uint32',
'*tag': 'str' } }
##
# @ReplicationMode:
#
@ -2935,7 +2912,7 @@
'raw': 'BlockdevOptionsRaw',
'rbd': 'BlockdevOptionsRbd',
'replication':'BlockdevOptionsReplication',
# TODO sheepdog: Wait for structured options
'sheepdog': 'BlockdevOptionsSheepdog',
'ssh': 'BlockdevOptionsSsh',
'vdi': 'BlockdevOptionsGenericFormat',
'vhdx': 'BlockdevOptionsGenericFormat',