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:
		
						commit
						b64842dee4
					
				
							
								
								
									
										184
									
								
								block.c
									
									
									
									
									
								
							
							
						
						
									
										184
									
								
								block.c
									
									
									
									
									
								
							@ -1403,7 +1403,8 @@ static int bdrv_fill_options(QDict **options, const char *filename,
 | 
				
			|||||||
 * or bdrv_abort_perm_update().
 | 
					 * or bdrv_abort_perm_update().
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int bdrv_check_perm(BlockDriverState *bs, uint64_t cumulative_perms,
 | 
					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;
 | 
					    BlockDriver *drv = bs->drv;
 | 
				
			||||||
    BdrvChild *c;
 | 
					    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,
 | 
					        drv->bdrv_child_perm(bs, c, c->role,
 | 
				
			||||||
                             cumulative_perms, cumulative_shared_perms,
 | 
					                             cumulative_perms, cumulative_shared_perms,
 | 
				
			||||||
                             &cur_perm, &cur_shared);
 | 
					                             &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) {
 | 
					        if (ret < 0) {
 | 
				
			||||||
            return ret;
 | 
					            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
 | 
					 * 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,
 | 
					 * @new_used_perm/@new_shared_perm as its permissions. If @ignore_children is
 | 
				
			||||||
 * this old reference is ignored in the calculations; this allows checking
 | 
					 * set, the BdrvChild objects in this list are ignored in the calculations;
 | 
				
			||||||
 * permission updates for an existing reference.
 | 
					 * this allows checking permission updates for an existing reference.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Needs to be followed by a call to either bdrv_set_perm() or
 | 
					 * Needs to be followed by a call to either bdrv_set_perm() or
 | 
				
			||||||
 * bdrv_abort_perm_update(). */
 | 
					 * bdrv_abort_perm_update(). */
 | 
				
			||||||
static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
 | 
					static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
 | 
				
			||||||
                                  uint64_t new_shared_perm,
 | 
					                                  uint64_t new_shared_perm,
 | 
				
			||||||
                                  BdrvChild *ignore_child, Error **errp)
 | 
					                                  GSList *ignore_children, Error **errp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    BdrvChild *c;
 | 
					    BdrvChild *c;
 | 
				
			||||||
    uint64_t cumulative_perms = new_used_perm;
 | 
					    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);
 | 
					    assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    QLIST_FOREACH(c, &bs->parents, next_parent) {
 | 
					    QLIST_FOREACH(c, &bs->parents, next_parent) {
 | 
				
			||||||
        if (c == ignore_child) {
 | 
					        if (g_slist_find(ignore_children, c)) {
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1607,15 +1609,22 @@ static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
 | 
				
			|||||||
        cumulative_shared_perms &= c->shared_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
 | 
					/* Needs to be followed by a call to either bdrv_child_set_perm() or
 | 
				
			||||||
 * bdrv_child_abort_perm_update(). */
 | 
					 * bdrv_child_abort_perm_update(). */
 | 
				
			||||||
int bdrv_child_check_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
 | 
					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)
 | 
					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;
 | 
					    int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ret = bdrv_child_check_perm(c, perm, shared, errp);
 | 
					    ret = bdrv_child_check_perm(c, perm, shared, NULL, errp);
 | 
				
			||||||
    if (ret < 0) {
 | 
					    if (ret < 0) {
 | 
				
			||||||
        bdrv_child_abort_perm_update(c);
 | 
					        bdrv_child_abort_perm_update(c);
 | 
				
			||||||
        return ret;
 | 
					        return ret;
 | 
				
			||||||
@ -1718,11 +1727,10 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
 | 
				
			|||||||
    *nshared = shared;
 | 
					    *nshared = shared;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
 | 
					static void bdrv_replace_child_noperm(BdrvChild *child,
 | 
				
			||||||
                               bool check_new_perm)
 | 
					                                      BlockDriverState *new_bs)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    BlockDriverState *old_bs = child->bs;
 | 
					    BlockDriverState *old_bs = child->bs;
 | 
				
			||||||
    uint64_t perm, shared_perm;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (old_bs) {
 | 
					    if (old_bs) {
 | 
				
			||||||
        if (old_bs->quiesce_counter && child->role->drained_end) {
 | 
					        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);
 | 
					            child->role->detach(child);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        QLIST_REMOVE(child, next_parent);
 | 
					        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;
 | 
					    child->bs = new_bs;
 | 
				
			||||||
@ -1749,18 +1750,38 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
 | 
				
			|||||||
            child->role->drained_begin(child);
 | 
					            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) {
 | 
					        if (child->role->attach) {
 | 
				
			||||||
            child->role->attach(child);
 | 
					            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,
 | 
					BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
 | 
				
			||||||
                                  const char *child_name,
 | 
					                                  const char *child_name,
 | 
				
			||||||
                                  const BdrvChildRole *child_role,
 | 
					                                  const BdrvChildRole *child_role,
 | 
				
			||||||
@ -2891,35 +2912,82 @@ void bdrv_close_all(void)
 | 
				
			|||||||
    assert(QTAILQ_EMPTY(&all_bdrv_states));
 | 
					    assert(QTAILQ_EMPTY(&all_bdrv_states));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void change_parent_backing_link(BlockDriverState *from,
 | 
					static bool should_update_child(BdrvChild *c, BlockDriverState *to)
 | 
				
			||||||
                                       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) {
 | 
					    QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
 | 
				
			||||||
        if (c->role->stay_at_node) {
 | 
					        if (!should_update_child(c, to)) {
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (c->role == &child_backing) {
 | 
					        list = g_slist_prepend(list, c);
 | 
				
			||||||
            /* If @from is a backing file of @to, ignore the child to avoid
 | 
					        perm |= c->perm;
 | 
				
			||||||
             * creating a loop. We only want to change the pointer of other
 | 
					        shared &= c->shared_perm;
 | 
				
			||||||
             * parents. */
 | 
					    }
 | 
				
			||||||
            QLIST_FOREACH(to_c, &to->children, next) {
 | 
					
 | 
				
			||||||
                if (to_c == c) {
 | 
					    /* Check whether the required permissions can be granted on @to, ignoring
 | 
				
			||||||
                    break;
 | 
					     * all BdrvChild in @list so that they can't block themselves. */
 | 
				
			||||||
                }
 | 
					    ret = bdrv_check_update_perm(to, perm, shared, list, errp);
 | 
				
			||||||
            }
 | 
					    if (ret < 0) {
 | 
				
			||||||
            if (to_c) {
 | 
					        bdrv_abort_perm_update(to);
 | 
				
			||||||
                continue;
 | 
					        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);
 | 
					        bdrv_ref(to);
 | 
				
			||||||
        /* FIXME Are we sure that bdrv_replace_child() can't run into
 | 
					        bdrv_replace_child_noperm(c, to);
 | 
				
			||||||
         * &error_abort because of permissions? */
 | 
					 | 
				
			||||||
        bdrv_replace_child(c, to, true);
 | 
					 | 
				
			||||||
        bdrv_unref(from);
 | 
					        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;
 | 
					    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);
 | 
					    bdrv_set_backing_hd(bs_new, bs_top, &local_err);
 | 
				
			||||||
    if (local_err) {
 | 
					    if (local_err) {
 | 
				
			||||||
        error_propagate(errp, local_err);
 | 
					        error_propagate(errp, local_err);
 | 
				
			||||||
        goto out;
 | 
					        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
 | 
					    /* bs_new is now referenced by its new parents, we don't need the
 | 
				
			||||||
     * additional reference any more. */
 | 
					     * additional reference any more. */
 | 
				
			||||||
@ -2960,18 +3030,6 @@ out:
 | 
				
			|||||||
    bdrv_unref(bs_new);
 | 
					    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)
 | 
					static void bdrv_delete(BlockDriverState *bs)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    assert(!bs->job);
 | 
					    assert(!bs->job);
 | 
				
			||||||
 | 
				
			|||||||
@ -213,7 +213,12 @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    blk->root = bdrv_root_attach_child(bs, "root", &child_root,
 | 
					    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;
 | 
					    return blk;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -316,8 +316,20 @@ void commit_start(const char *job_id, BlockDriverState *bs,
 | 
				
			|||||||
        goto fail;
 | 
					        goto fail;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bdrv_set_backing_hd(commit_top_bs, top, &error_abort);
 | 
					    bdrv_set_backing_hd(commit_top_bs, top, &local_err);
 | 
				
			||||||
    bdrv_set_backing_hd(overlay_bs, commit_top_bs, &error_abort);
 | 
					    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;
 | 
					    s->commit_top_bs = commit_top_bs;
 | 
				
			||||||
    bdrv_unref(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() */
 | 
					    /* Required permissions are already taken with block_job_add_bdrv() */
 | 
				
			||||||
    s->top = blk_new(0, BLK_PERM_ALL);
 | 
					    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) {
 | 
					    if (ret < 0) {
 | 
				
			||||||
        goto fail;
 | 
					        goto fail;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										127
									
								
								block/gluster.c
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								block/gluster.c
									
									
									
									
									
								
							@ -12,6 +12,7 @@
 | 
				
			|||||||
#include "block/block_int.h"
 | 
					#include "block/block_int.h"
 | 
				
			||||||
#include "qapi/error.h"
 | 
					#include "qapi/error.h"
 | 
				
			||||||
#include "qapi/qmp/qerror.h"
 | 
					#include "qapi/qmp/qerror.h"
 | 
				
			||||||
 | 
					#include "qapi/util.h"
 | 
				
			||||||
#include "qemu/uri.h"
 | 
					#include "qemu/uri.h"
 | 
				
			||||||
#include "qemu/error-report.h"
 | 
					#include "qemu/error-report.h"
 | 
				
			||||||
#include "qemu/cutils.h"
 | 
					#include "qemu/cutils.h"
 | 
				
			||||||
@ -151,7 +152,7 @@ static QemuOptsList runtime_type_opts = {
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            .name = GLUSTER_OPT_TYPE,
 | 
					            .name = GLUSTER_OPT_TYPE,
 | 
				
			||||||
            .type = QEMU_OPT_STRING,
 | 
					            .type = QEMU_OPT_STRING,
 | 
				
			||||||
            .help = "tcp|unix",
 | 
					            .help = "inet|unix",
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        { /* end of list */ }
 | 
					        { /* end of list */ }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -170,14 +171,14 @@ static QemuOptsList runtime_unix_opts = {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static QemuOptsList runtime_tcp_opts = {
 | 
					static QemuOptsList runtime_inet_opts = {
 | 
				
			||||||
    .name = "gluster_tcp",
 | 
					    .name = "gluster_inet",
 | 
				
			||||||
    .head = QTAILQ_HEAD_INITIALIZER(runtime_tcp_opts.head),
 | 
					    .head = QTAILQ_HEAD_INITIALIZER(runtime_inet_opts.head),
 | 
				
			||||||
    .desc = {
 | 
					    .desc = {
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            .name = GLUSTER_OPT_TYPE,
 | 
					            .name = GLUSTER_OPT_TYPE,
 | 
				
			||||||
            .type = QEMU_OPT_STRING,
 | 
					            .type = QEMU_OPT_STRING,
 | 
				
			||||||
            .help = "tcp|unix",
 | 
					            .help = "inet|unix",
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            .name = GLUSTER_OPT_HOST,
 | 
					            .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,
 | 
					static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf,
 | 
				
			||||||
                                  const char *filename)
 | 
					                                  const char *filename)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    GlusterServer *gsconf;
 | 
					    SocketAddressFlat *gsconf;
 | 
				
			||||||
    URI *uri;
 | 
					    URI *uri;
 | 
				
			||||||
    QueryParams *qp = NULL;
 | 
					    QueryParams *qp = NULL;
 | 
				
			||||||
    bool is_unix = false;
 | 
					    bool is_unix = false;
 | 
				
			||||||
@ -331,19 +332,19 @@ static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf,
 | 
				
			|||||||
        return -EINVAL;
 | 
					        return -EINVAL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    gconf->server = g_new0(GlusterServerList, 1);
 | 
					    gconf->server = g_new0(SocketAddressFlatList, 1);
 | 
				
			||||||
    gconf->server->value = gsconf = g_new0(GlusterServer, 1);
 | 
					    gconf->server->value = gsconf = g_new0(SocketAddressFlat, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* transport */
 | 
					    /* transport */
 | 
				
			||||||
    if (!uri->scheme || !strcmp(uri->scheme, "gluster")) {
 | 
					    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")) {
 | 
					    } 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")) {
 | 
					    } else if (!strcmp(uri->scheme, "gluster+unix")) {
 | 
				
			||||||
        gsconf->type = GLUSTER_TRANSPORT_UNIX;
 | 
					        gsconf->type = SOCKET_ADDRESS_FLAT_TYPE_UNIX;
 | 
				
			||||||
        is_unix = true;
 | 
					        is_unix = true;
 | 
				
			||||||
    } else if (!strcmp(uri->scheme, "gluster+rdma")) {
 | 
					    } 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 "
 | 
					        error_report("Warning: rdma feature is not supported, falling "
 | 
				
			||||||
                     "back to tcp");
 | 
					                     "back to tcp");
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
@ -373,11 +374,11 @@ static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf,
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        gsconf->u.q_unix.path = g_strdup(qp->p[0].value);
 | 
					        gsconf->u.q_unix.path = g_strdup(qp->p[0].value);
 | 
				
			||||||
    } else {
 | 
					    } 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) {
 | 
					        if (uri->port) {
 | 
				
			||||||
            gsconf->u.tcp.port = g_strdup_printf("%d", uri->port);
 | 
					            gsconf->u.inet.port = g_strdup_printf("%d", uri->port);
 | 
				
			||||||
        } else {
 | 
					        } 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;
 | 
					    struct glfs *glfs;
 | 
				
			||||||
    int ret;
 | 
					    int ret;
 | 
				
			||||||
    int old_errno;
 | 
					    int old_errno;
 | 
				
			||||||
    GlusterServerList *server;
 | 
					    SocketAddressFlatList *server;
 | 
				
			||||||
    unsigned long long port;
 | 
					    unsigned long long port;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    glfs = glfs_find_preopened(gconf->volume);
 | 
					    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);
 | 
					    glfs_set_preopened(gconf->volume, glfs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (server = gconf->server; server; server = server->next) {
 | 
					    for (server = gconf->server; server; server = server->next) {
 | 
				
			||||||
        if (server->value->type  == GLUSTER_TRANSPORT_UNIX) {
 | 
					        if (server->value->type  == SOCKET_ADDRESS_FLAT_TYPE_UNIX) {
 | 
				
			||||||
            ret = glfs_set_volfile_server(glfs,
 | 
					            ret = glfs_set_volfile_server(glfs, "unix",
 | 
				
			||||||
                                   GlusterTransport_lookup[server->value->type],
 | 
					 | 
				
			||||||
                                   server->value->u.q_unix.path, 0);
 | 
					                                   server->value->u.q_unix.path, 0);
 | 
				
			||||||
        } else {
 | 
					        } 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) {
 | 
					                port > 65535) {
 | 
				
			||||||
                error_setg(errp, "'%s' is not a valid port number",
 | 
					                error_setg(errp, "'%s' is not a valid port number",
 | 
				
			||||||
                           server->value->u.tcp.port);
 | 
					                           server->value->u.inet.port);
 | 
				
			||||||
                errno = EINVAL;
 | 
					                errno = EINVAL;
 | 
				
			||||||
                goto out;
 | 
					                goto out;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ret = glfs_set_volfile_server(glfs,
 | 
					            ret = glfs_set_volfile_server(glfs, "tcp",
 | 
				
			||||||
                                   GlusterTransport_lookup[server->value->type],
 | 
					                                   server->value->u.inet.host,
 | 
				
			||||||
                                   server->value->u.tcp.host,
 | 
					 | 
				
			||||||
                                   (int)port);
 | 
					                                   (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"
 | 
					        error_setg(errp, "Gluster connection for volume %s, path %s failed"
 | 
				
			||||||
                         " to connect", gconf->volume, gconf->path);
 | 
					                         " to connect", gconf->volume, gconf->path);
 | 
				
			||||||
        for (server = gconf->server; server; server = server->next) {
 | 
					        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 ",
 | 
					                error_append_hint(errp, "hint: failed on socket %s ",
 | 
				
			||||||
                                  server->value->u.q_unix.path);
 | 
					                                  server->value->u.q_unix.path);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                error_append_hint(errp, "hint: failed on host %s and port %s ",
 | 
					                error_append_hint(errp, "hint: failed on host %s and port %s ",
 | 
				
			||||||
                                  server->value->u.tcp.host,
 | 
					                                  server->value->u.inet.host,
 | 
				
			||||||
                                  server->value->u.tcp.port);
 | 
					                                  server->value->u.inet.port);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -474,23 +473,6 @@ out:
 | 
				
			|||||||
    return NULL;
 | 
					    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.
 | 
					 * Convert the json formatted command line into qapi.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
@ -498,8 +480,8 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
 | 
				
			|||||||
                                  QDict *options, Error **errp)
 | 
					                                  QDict *options, Error **errp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    QemuOpts *opts;
 | 
					    QemuOpts *opts;
 | 
				
			||||||
    GlusterServer *gsconf;
 | 
					    SocketAddressFlat *gsconf = NULL;
 | 
				
			||||||
    GlusterServerList *curr = NULL;
 | 
					    SocketAddressFlatList *curr = NULL;
 | 
				
			||||||
    QDict *backing_options = NULL;
 | 
					    QDict *backing_options = NULL;
 | 
				
			||||||
    Error *local_err = NULL;
 | 
					    Error *local_err = NULL;
 | 
				
			||||||
    char *str = NULL;
 | 
					    char *str = NULL;
 | 
				
			||||||
@ -547,25 +529,31 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ptr = qemu_opt_get(opts, GLUSTER_OPT_TYPE);
 | 
					        ptr = qemu_opt_get(opts, GLUSTER_OPT_TYPE);
 | 
				
			||||||
        gsconf = g_new0(GlusterServer, 1);
 | 
					 | 
				
			||||||
        gsconf->type = qapi_enum_parse(ptr);
 | 
					 | 
				
			||||||
        if (!ptr) {
 | 
					        if (!ptr) {
 | 
				
			||||||
            error_setg(&local_err, QERR_MISSING_PARAMETER, GLUSTER_OPT_TYPE);
 | 
					            error_setg(&local_err, QERR_MISSING_PARAMETER, GLUSTER_OPT_TYPE);
 | 
				
			||||||
            error_append_hint(&local_err, GERR_INDEX_HINT, i);
 | 
					            error_append_hint(&local_err, GERR_INDEX_HINT, i);
 | 
				
			||||||
            goto out;
 | 
					            goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (gsconf->type == GLUSTER_TRANSPORT__MAX) {
 | 
					        gsconf = g_new0(SocketAddressFlat, 1);
 | 
				
			||||||
            error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE,
 | 
					        if (!strcmp(ptr, "tcp")) {
 | 
				
			||||||
                       GLUSTER_OPT_TYPE, "tcp or unix");
 | 
					            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);
 | 
					            error_append_hint(&local_err, GERR_INDEX_HINT, i);
 | 
				
			||||||
            goto out;
 | 
					            goto out;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        qemu_opts_del(opts);
 | 
					        qemu_opts_del(opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (gsconf->type == GLUSTER_TRANSPORT_TCP) {
 | 
					        if (gsconf->type == SOCKET_ADDRESS_FLAT_TYPE_INET) {
 | 
				
			||||||
            /* create opts info from runtime_tcp_opts list */
 | 
					            /* create opts info from runtime_inet_opts list */
 | 
				
			||||||
            opts = qemu_opts_create(&runtime_tcp_opts, NULL, 0, &error_abort);
 | 
					            opts = qemu_opts_create(&runtime_inet_opts, NULL, 0, &error_abort);
 | 
				
			||||||
            qemu_opts_absorb_qdict(opts, backing_options, &local_err);
 | 
					            qemu_opts_absorb_qdict(opts, backing_options, &local_err);
 | 
				
			||||||
            if (local_err) {
 | 
					            if (local_err) {
 | 
				
			||||||
                goto out;
 | 
					                goto out;
 | 
				
			||||||
@ -578,7 +566,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
 | 
				
			|||||||
                error_append_hint(&local_err, GERR_INDEX_HINT, i);
 | 
					                error_append_hint(&local_err, GERR_INDEX_HINT, i);
 | 
				
			||||||
                goto out;
 | 
					                goto out;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            gsconf->u.tcp.host = g_strdup(ptr);
 | 
					            gsconf->u.inet.host = g_strdup(ptr);
 | 
				
			||||||
            ptr = qemu_opt_get(opts, GLUSTER_OPT_PORT);
 | 
					            ptr = qemu_opt_get(opts, GLUSTER_OPT_PORT);
 | 
				
			||||||
            if (!ptr) {
 | 
					            if (!ptr) {
 | 
				
			||||||
                error_setg(&local_err, QERR_MISSING_PARAMETER,
 | 
					                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);
 | 
					                error_append_hint(&local_err, GERR_INDEX_HINT, i);
 | 
				
			||||||
                goto out;
 | 
					                goto out;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            gsconf->u.tcp.port = g_strdup(ptr);
 | 
					            gsconf->u.inet.port = g_strdup(ptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            /* defend for unsupported fields in InetSocketAddress,
 | 
					            /* defend for unsupported fields in InetSocketAddress,
 | 
				
			||||||
             * i.e. @ipv4, @ipv6  and @to
 | 
					             * i.e. @ipv4, @ipv6  and @to
 | 
				
			||||||
             */
 | 
					             */
 | 
				
			||||||
            ptr = qemu_opt_get(opts, GLUSTER_OPT_TO);
 | 
					            ptr = qemu_opt_get(opts, GLUSTER_OPT_TO);
 | 
				
			||||||
            if (ptr) {
 | 
					            if (ptr) {
 | 
				
			||||||
                gsconf->u.tcp.has_to = true;
 | 
					                gsconf->u.inet.has_to = true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ptr = qemu_opt_get(opts, GLUSTER_OPT_IPV4);
 | 
					            ptr = qemu_opt_get(opts, GLUSTER_OPT_IPV4);
 | 
				
			||||||
            if (ptr) {
 | 
					            if (ptr) {
 | 
				
			||||||
                gsconf->u.tcp.has_ipv4 = true;
 | 
					                gsconf->u.inet.has_ipv4 = true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ptr = qemu_opt_get(opts, GLUSTER_OPT_IPV6);
 | 
					            ptr = qemu_opt_get(opts, GLUSTER_OPT_IPV6);
 | 
				
			||||||
            if (ptr) {
 | 
					            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");
 | 
					                error_setg(&local_err, "Parameter 'to' not supported");
 | 
				
			||||||
                goto out;
 | 
					                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");
 | 
					                error_setg(&local_err, "Parameters 'ipv4/ipv6' not supported");
 | 
				
			||||||
                goto out;
 | 
					                goto out;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -632,16 +620,18 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (gconf->server == NULL) {
 | 
					        if (gconf->server == NULL) {
 | 
				
			||||||
            gconf->server = g_new0(GlusterServerList, 1);
 | 
					            gconf->server = g_new0(SocketAddressFlatList, 1);
 | 
				
			||||||
            gconf->server->value = gsconf;
 | 
					            gconf->server->value = gsconf;
 | 
				
			||||||
            curr = gconf->server;
 | 
					            curr = gconf->server;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            curr->next = g_new0(GlusterServerList, 1);
 | 
					            curr->next = g_new0(SocketAddressFlatList, 1);
 | 
				
			||||||
            curr->next->value = gsconf;
 | 
					            curr->next->value = gsconf;
 | 
				
			||||||
            curr = curr->next;
 | 
					            curr = curr->next;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        gsconf = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        qdict_del(backing_options, str);
 | 
					        QDECREF(backing_options);
 | 
				
			||||||
 | 
					        backing_options = NULL;
 | 
				
			||||||
        g_free(str);
 | 
					        g_free(str);
 | 
				
			||||||
        str = NULL;
 | 
					        str = NULL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -650,11 +640,10 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
    error_propagate(errp, local_err);
 | 
					    error_propagate(errp, local_err);
 | 
				
			||||||
 | 
					    qapi_free_SocketAddressFlat(gsconf);
 | 
				
			||||||
    qemu_opts_del(opts);
 | 
					    qemu_opts_del(opts);
 | 
				
			||||||
    if (str) {
 | 
					    g_free(str);
 | 
				
			||||||
        qdict_del(backing_options, str);
 | 
					    QDECREF(backing_options);
 | 
				
			||||||
        g_free(str);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    errno = EINVAL;
 | 
					    errno = EINVAL;
 | 
				
			||||||
    return -errno;
 | 
					    return -errno;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -683,7 +672,7 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
 | 
				
			|||||||
                             "file.volume=testvol,file.path=/path/a.qcow2"
 | 
					                             "file.volume=testvol,file.path=/path/a.qcow2"
 | 
				
			||||||
                             "[,file.debug=9]"
 | 
					                             "[,file.debug=9]"
 | 
				
			||||||
                             "[,file.logfile=/path/filename.log],"
 | 
					                             "[,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.host=1.2.3.4,"
 | 
				
			||||||
                             "file.server.0.port=24007,"
 | 
					                             "file.server.0.port=24007,"
 | 
				
			||||||
                             "file.server.1.transport=unix,"
 | 
					                             "file.server.1.transport=unix,"
 | 
				
			||||||
 | 
				
			|||||||
@ -509,6 +509,13 @@ static void mirror_exit(BlockJob *job, void *opaque)
 | 
				
			|||||||
     * block_job_completed(). */
 | 
					     * block_job_completed(). */
 | 
				
			||||||
    bdrv_ref(src);
 | 
					    bdrv_ref(src);
 | 
				
			||||||
    bdrv_ref(mirror_top_bs);
 | 
					    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
 | 
					    /* We don't access the source any more. Dropping any WRITE/RESIZE is
 | 
				
			||||||
     * required before it could become a backing file of target_bs. */
 | 
					     * 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
 | 
					        /* 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. */
 | 
					         * drain potential other users of the BDS before changing the graph. */
 | 
				
			||||||
        bdrv_drained_begin(target_bs);
 | 
					        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);
 | 
					        bdrv_drained_end(target_bs);
 | 
				
			||||||
 | 
					        if (local_err) {
 | 
				
			||||||
 | 
					            error_report_err(local_err);
 | 
				
			||||||
 | 
					            data->ret = -EPERM;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (s->to_replace) {
 | 
					    if (s->to_replace) {
 | 
				
			||||||
        bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
 | 
					        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);
 | 
					        aio_context_release(replace_aio_context);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    g_free(s->replaces);
 | 
					    g_free(s->replaces);
 | 
				
			||||||
    blk_unref(s->target);
 | 
					    bdrv_unref(target_bs);
 | 
				
			||||||
    s->target = NULL;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Remove the mirror filter driver from the graph. Before this, get rid of
 | 
					    /* 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
 | 
					     * 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);
 | 
					    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
 | 
					    /* 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
 | 
					     * bdrv_replace_node() calls), so switch the BB back so the cleanup does
 | 
				
			||||||
     * cleanup does the right thing. We don't need any permissions any more
 | 
					     * the right thing. We don't need any permissions any more now. */
 | 
				
			||||||
     * now. */
 | 
					 | 
				
			||||||
    blk_remove_bs(job->blk);
 | 
					    blk_remove_bs(job->blk);
 | 
				
			||||||
    blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
 | 
					    blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
 | 
				
			||||||
    blk_insert_bs(job->blk, mirror_top_bs, &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);
 | 
					    s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp);
 | 
				
			||||||
    if (!s->dirty_bitmap) {
 | 
					    if (!s->dirty_bitmap) {
 | 
				
			||||||
        g_free(s->replaces);
 | 
					        goto fail;
 | 
				
			||||||
        blk_unref(s->target);
 | 
					 | 
				
			||||||
        block_job_unref(&s->common);
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Required permissions are already taken with blk_new() */
 | 
					    /* Required permissions are already taken with blk_new() */
 | 
				
			||||||
@ -1228,7 +1236,8 @@ fail:
 | 
				
			|||||||
        block_job_unref(&s->common);
 | 
					        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,
 | 
					void mirror_start(const char *job_id, BlockDriverState *bs,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										457
									
								
								block/sheepdog.c
									
									
									
									
									
								
							
							
						
						
									
										457
									
								
								block/sheepdog.c
									
									
									
									
									
								
							@ -14,6 +14,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "qemu/osdep.h"
 | 
					#include "qemu/osdep.h"
 | 
				
			||||||
#include "qapi/error.h"
 | 
					#include "qapi/error.h"
 | 
				
			||||||
 | 
					#include "qapi/qmp/qdict.h"
 | 
				
			||||||
 | 
					#include "qapi/qmp/qint.h"
 | 
				
			||||||
#include "qemu/uri.h"
 | 
					#include "qemu/uri.h"
 | 
				
			||||||
#include "qemu/error-report.h"
 | 
					#include "qemu/error-report.h"
 | 
				
			||||||
#include "qemu/sockets.h"
 | 
					#include "qemu/sockets.h"
 | 
				
			||||||
@ -374,8 +376,7 @@ struct BDRVSheepdogState {
 | 
				
			|||||||
    uint32_t cache_flags;
 | 
					    uint32_t cache_flags;
 | 
				
			||||||
    bool discard_supported;
 | 
					    bool discard_supported;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    char *host_spec;
 | 
					    SocketAddress *addr;
 | 
				
			||||||
    bool is_unix;
 | 
					 | 
				
			||||||
    int fd;
 | 
					    int fd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    CoMutex lock;
 | 
					    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);
 | 
					    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 */
 | 
					/* Return -EIO in case of error, file descriptor on success */
 | 
				
			||||||
static int connect_to_sdog(BDRVSheepdogState *s, Error **errp)
 | 
					static int connect_to_sdog(BDRVSheepdogState *s, Error **errp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int fd;
 | 
					    int fd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (s->is_unix) {
 | 
					    fd = socket_connect(s->addr, errp, NULL, NULL);
 | 
				
			||||||
        fd = unix_connect(s->host_spec, errp);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        fd = inet_connect(s->host_spec, errp);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (fd >= 0) {
 | 
					    if (s->addr->type == SOCKET_ADDRESS_KIND_INET && fd >= 0) {
 | 
				
			||||||
            int ret = socket_set_nodelay(fd);
 | 
					        int ret = socket_set_nodelay(fd);
 | 
				
			||||||
            if (ret < 0) {
 | 
					        if (ret < 0) {
 | 
				
			||||||
                error_report("%s", strerror(errno));
 | 
					            error_report("%s", strerror(errno));
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -820,8 +836,7 @@ static void coroutine_fn aio_read_response(void *opaque)
 | 
				
			|||||||
    case AIOCB_DISCARD_OBJ:
 | 
					    case AIOCB_DISCARD_OBJ:
 | 
				
			||||||
        switch (rsp.result) {
 | 
					        switch (rsp.result) {
 | 
				
			||||||
        case SD_RES_INVALID_PARMS:
 | 
					        case SD_RES_INVALID_PARMS:
 | 
				
			||||||
            error_report("sheep(%s) doesn't support discard command",
 | 
					            error_report("server doesn't support discard command");
 | 
				
			||||||
                         s->host_spec);
 | 
					 | 
				
			||||||
            rsp.result = SD_RES_SUCCESS;
 | 
					            rsp.result = SD_RES_SUCCESS;
 | 
				
			||||||
            s->discard_supported = false;
 | 
					            s->discard_supported = false;
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
@ -914,71 +929,155 @@ static int get_sheep_fd(BDRVSheepdogState *s, Error **errp)
 | 
				
			|||||||
    return fd;
 | 
					    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;
 | 
					    unsigned long ul;
 | 
				
			||||||
    QueryParams *qp = NULL;
 | 
					    int ret;
 | 
				
			||||||
    int ret = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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) {
 | 
					    if (!uri) {
 | 
				
			||||||
        return -EINVAL;
 | 
					        error_setg(&err, "invalid URI");
 | 
				
			||||||
 | 
					        goto out;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* transport */
 | 
					    /* transport */
 | 
				
			||||||
    if (!strcmp(uri->scheme, "sheepdog")) {
 | 
					    if (!strcmp(uri->scheme, "sheepdog")) {
 | 
				
			||||||
        s->is_unix = false;
 | 
					        is_unix = false;
 | 
				
			||||||
    } else if (!strcmp(uri->scheme, "sheepdog+tcp")) {
 | 
					    } else if (!strcmp(uri->scheme, "sheepdog+tcp")) {
 | 
				
			||||||
        s->is_unix = false;
 | 
					        is_unix = false;
 | 
				
			||||||
    } else if (!strcmp(uri->scheme, "sheepdog+unix")) {
 | 
					    } else if (!strcmp(uri->scheme, "sheepdog+unix")) {
 | 
				
			||||||
        s->is_unix = true;
 | 
					        is_unix = true;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        ret = -EINVAL;
 | 
					        error_setg(&err, "URI scheme must be 'sheepdog', 'sheepdog+tcp',"
 | 
				
			||||||
 | 
					                   " or 'sheepdog+unix'");
 | 
				
			||||||
        goto out;
 | 
					        goto out;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (uri->path == NULL || !strcmp(uri->path, "/")) {
 | 
					    if (uri->path == NULL || !strcmp(uri->path, "/")) {
 | 
				
			||||||
        ret = -EINVAL;
 | 
					        error_setg(&err, "missing file path in URI");
 | 
				
			||||||
        goto out;
 | 
					        goto out;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    pstrcpy(vdi, SD_MAX_VDI_LEN, uri->path + 1);
 | 
					    if (g_strlcpy(cfg->vdi, uri->path + 1, SD_MAX_VDI_LEN)
 | 
				
			||||||
 | 
					        >= SD_MAX_VDI_LEN) {
 | 
				
			||||||
    qp = query_params_parse(uri->query);
 | 
					        error_setg(&err, "VDI name is too long");
 | 
				
			||||||
    if (qp->n > 1 || (s->is_unix && !qp->n) || (!s->is_unix && qp->n)) {
 | 
					 | 
				
			||||||
        ret = -EINVAL;
 | 
					 | 
				
			||||||
        goto out;
 | 
					        goto out;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (s->is_unix) {
 | 
					    cfg->qp = qp = query_params_parse(uri->query);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (is_unix) {
 | 
				
			||||||
        /* sheepdog+unix:///vdiname?socket=path */
 | 
					        /* sheepdog+unix:///vdiname?socket=path */
 | 
				
			||||||
        if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) {
 | 
					        if (uri->server || uri->port) {
 | 
				
			||||||
            ret = -EINVAL;
 | 
					            error_setg(&err, "URI scheme %s doesn't accept a server address",
 | 
				
			||||||
 | 
					                       uri->scheme);
 | 
				
			||||||
            goto out;
 | 
					            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 {
 | 
					    } else {
 | 
				
			||||||
        /* sheepdog[+tcp]://[host:port]/vdiname */
 | 
					        /* sheepdog[+tcp]://[host:port]/vdiname */
 | 
				
			||||||
        s->host_spec = g_strdup_printf("%s:%d", uri->server ?: SD_DEFAULT_ADDR,
 | 
					        if (qp->n) {
 | 
				
			||||||
                                       uri->port ?: SD_DEFAULT_PORT);
 | 
					            error_setg(&err, "unexpected query parameters");
 | 
				
			||||||
 | 
					            goto out;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        cfg->host = uri->server;
 | 
				
			||||||
 | 
					        cfg->port = uri->port;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* snapshot tag */
 | 
					    /* snapshot tag */
 | 
				
			||||||
    if (uri->fragment) {
 | 
					    if (uri->fragment) {
 | 
				
			||||||
        *snapid = strtoul(uri->fragment, NULL, 10);
 | 
					        if (!sd_parse_snapid_or_tag(uri->fragment,
 | 
				
			||||||
        if (*snapid == 0) {
 | 
					                                    &cfg->snap_id, cfg->tag)) {
 | 
				
			||||||
            pstrcpy(tag, SD_MAX_VDI_TAG_LEN, uri->fragment);
 | 
					            error_setg(&err, "'%s' is not a valid snapshot ID",
 | 
				
			||||||
 | 
					                       uri->fragment);
 | 
				
			||||||
 | 
					            goto out;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        *snapid = CURRENT_VDI_ID; /* search current vdi */
 | 
					        cfg->snap_id = CURRENT_VDI_ID; /* search current vdi */
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
    if (qp) {
 | 
					    if (err) {
 | 
				
			||||||
        query_params_free(qp);
 | 
					        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
 | 
					 * You can run VMs outside the Sheepdog cluster by specifying
 | 
				
			||||||
 * `hostname' and `port' (experimental).
 | 
					 * `hostname' and `port' (experimental).
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int parse_vdiname(BDRVSheepdogState *s, const char *filename,
 | 
					static void parse_vdiname(SheepdogConfig *cfg, const char *filename,
 | 
				
			||||||
                         char *vdi, uint32_t *snapid, char *tag)
 | 
					                          Error **errp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    Error *err = NULL;
 | 
				
			||||||
    char *p, *q, *uri;
 | 
					    char *p, *q, *uri;
 | 
				
			||||||
    const char *host_spec, *vdi_spec;
 | 
					    const char *host_spec, *vdi_spec;
 | 
				
			||||||
    int nr_sep, ret;
 | 
					    int nr_sep;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    strstart(filename, "sheepdog:", &filename);
 | 
					    strstart(filename, "sheepdog:", &filename);
 | 
				
			||||||
    p = q = g_strdup(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);
 | 
					    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(q);
 | 
				
			||||||
    g_free(uri);
 | 
					    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,
 | 
					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);
 | 
					                       co_read_response, NULL, NULL, s);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* TODO Convert to fine grained options */
 | 
					 | 
				
			||||||
static QemuOptsList runtime_opts = {
 | 
					static QemuOptsList runtime_opts = {
 | 
				
			||||||
    .name = "sheepdog",
 | 
					    .name = "sheepdog",
 | 
				
			||||||
    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
 | 
					    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
 | 
				
			||||||
    .desc = {
 | 
					    .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,
 | 
					            .type = QEMU_OPT_STRING,
 | 
				
			||||||
            .help = "URL to the sheepdog image",
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        { /* end of list */ }
 | 
					        { /* end of list */ }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -1377,12 +1543,11 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
 | 
				
			|||||||
    int ret, fd;
 | 
					    int ret, fd;
 | 
				
			||||||
    uint32_t vid = 0;
 | 
					    uint32_t vid = 0;
 | 
				
			||||||
    BDRVSheepdogState *s = bs->opaque;
 | 
					    BDRVSheepdogState *s = bs->opaque;
 | 
				
			||||||
    char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN];
 | 
					    const char *host, *port, *path, *vdi, *snap_id_str, *tag;
 | 
				
			||||||
    uint32_t snapid;
 | 
					    uint64_t snap_id;
 | 
				
			||||||
    char *buf = NULL;
 | 
					    char *buf = NULL;
 | 
				
			||||||
    QemuOpts *opts;
 | 
					    QemuOpts *opts;
 | 
				
			||||||
    Error *local_err = NULL;
 | 
					    Error *local_err = NULL;
 | 
				
			||||||
    const char *filename;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    s->bs = bs;
 | 
					    s->bs = bs;
 | 
				
			||||||
    s->aio_context = bdrv_get_aio_context(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) {
 | 
					    if (local_err) {
 | 
				
			||||||
        error_propagate(errp, local_err);
 | 
					        error_propagate(errp, local_err);
 | 
				
			||||||
        ret = -EINVAL;
 | 
					        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->inflight_aio_head);
 | 
				
			||||||
    QLIST_INIT(&s->failed_aio_head);
 | 
					    QLIST_INIT(&s->failed_aio_head);
 | 
				
			||||||
    QLIST_INIT(&s->inflight_aiocb_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);
 | 
					    s->fd = get_sheep_fd(s, errp);
 | 
				
			||||||
    if (s->fd < 0) {
 | 
					    if (s->fd < 0) {
 | 
				
			||||||
        ret = s->fd;
 | 
					        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) {
 | 
					    if (ret) {
 | 
				
			||||||
        goto out;
 | 
					        goto err;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /*
 | 
					    /*
 | 
				
			||||||
@ -1435,7 +1631,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    s->discard_supported = true;
 | 
					    s->discard_supported = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (snapid || tag[0] != '\0') {
 | 
					    if (snap_id || tag[0]) {
 | 
				
			||||||
        DPRINTF("%" PRIx32 " snapshot inode was open.\n", vid);
 | 
					        DPRINTF("%" PRIx32 " snapshot inode was open.\n", vid);
 | 
				
			||||||
        s->is_snapshot = true;
 | 
					        s->is_snapshot = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -1443,7 +1639,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
 | 
				
			|||||||
    fd = connect_to_sdog(s, errp);
 | 
					    fd = connect_to_sdog(s, errp);
 | 
				
			||||||
    if (fd < 0) {
 | 
					    if (fd < 0) {
 | 
				
			||||||
        ret = fd;
 | 
					        ret = fd;
 | 
				
			||||||
        goto out;
 | 
					        goto err;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    buf = g_malloc(SD_INODE_SIZE);
 | 
					    buf = g_malloc(SD_INODE_SIZE);
 | 
				
			||||||
@ -1454,7 +1650,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (ret) {
 | 
					    if (ret) {
 | 
				
			||||||
        error_setg(errp, "Can't read snapshot inode");
 | 
					        error_setg(errp, "Can't read snapshot inode");
 | 
				
			||||||
        goto out;
 | 
					        goto err;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    memcpy(&s->inode, buf, sizeof(s->inode));
 | 
					    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);
 | 
					    qemu_opts_del(opts);
 | 
				
			||||||
    g_free(buf);
 | 
					    g_free(buf);
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
out:
 | 
					
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
    aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd,
 | 
					    aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd,
 | 
				
			||||||
                       false, NULL, NULL, NULL, NULL);
 | 
					                       false, NULL, NULL, NULL, NULL);
 | 
				
			||||||
    if (s->fd >= 0) {
 | 
					    closesocket(s->fd);
 | 
				
			||||||
        closesocket(s->fd);
 | 
					err_no_fd:
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    qemu_opts_del(opts);
 | 
					    qemu_opts_del(opts);
 | 
				
			||||||
    g_free(buf);
 | 
					    g_free(buf);
 | 
				
			||||||
    return ret;
 | 
					    return ret;
 | 
				
			||||||
@ -1685,6 +1881,7 @@ static int parse_redundancy(BDRVSheepdogState *s, const char *opt)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    copy = strtol(n1, NULL, 10);
 | 
					    copy = strtol(n1, NULL, 10);
 | 
				
			||||||
 | 
					    /* FIXME fix error checking by switching to qemu_strtol() */
 | 
				
			||||||
    if (copy > SD_MAX_COPIES || copy < 1) {
 | 
					    if (copy > SD_MAX_COPIES || copy < 1) {
 | 
				
			||||||
        return -EINVAL;
 | 
					        return -EINVAL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -1699,6 +1896,7 @@ static int parse_redundancy(BDRVSheepdogState *s, const char *opt)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    parity = strtol(n2, NULL, 10);
 | 
					    parity = strtol(n2, NULL, 10);
 | 
				
			||||||
 | 
					    /* FIXME fix error checking by switching to qemu_strtol() */
 | 
				
			||||||
    if (parity >= SD_EC_MAX_STRIP || parity < 1) {
 | 
					    if (parity >= SD_EC_MAX_STRIP || parity < 1) {
 | 
				
			||||||
        return -EINVAL;
 | 
					        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,
 | 
					static int sd_create(const char *filename, QemuOpts *opts,
 | 
				
			||||||
                     Error **errp)
 | 
					                     Error **errp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    Error *err = NULL;
 | 
				
			||||||
    int ret = 0;
 | 
					    int ret = 0;
 | 
				
			||||||
    uint32_t vid = 0;
 | 
					    uint32_t vid = 0;
 | 
				
			||||||
    char *backing_file = NULL;
 | 
					    char *backing_file = NULL;
 | 
				
			||||||
    char *buf = NULL;
 | 
					    char *buf = NULL;
 | 
				
			||||||
    BDRVSheepdogState *s;
 | 
					    BDRVSheepdogState *s;
 | 
				
			||||||
    char tag[SD_MAX_VDI_TAG_LEN];
 | 
					    SheepdogConfig cfg;
 | 
				
			||||||
    uint32_t snapid;
 | 
					 | 
				
			||||||
    uint64_t max_vdi_size;
 | 
					    uint64_t max_vdi_size;
 | 
				
			||||||
    bool prealloc = false;
 | 
					    bool prealloc = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    s = g_new0(BDRVSheepdogState, 1);
 | 
					    s = g_new0(BDRVSheepdogState, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    memset(tag, 0, sizeof(tag));
 | 
					 | 
				
			||||||
    if (strstr(filename, "://")) {
 | 
					    if (strstr(filename, "://")) {
 | 
				
			||||||
        ret = sd_parse_uri(s, filename, s->name, &snapid, tag);
 | 
					        sd_parse_uri(&cfg, filename, &err);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        ret = parse_vdiname(s, filename, s->name, &snapid, tag);
 | 
					        parse_vdiname(&cfg, filename, &err);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (ret < 0) {
 | 
					    if (err) {
 | 
				
			||||||
        error_setg(errp, "Can't parse filename");
 | 
					        error_propagate(errp, err);
 | 
				
			||||||
        goto out;
 | 
					        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),
 | 
					    s->inode.vdi_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
 | 
				
			||||||
                                 BDRV_SECTOR_SIZE);
 | 
					                                 BDRV_SECTOR_SIZE);
 | 
				
			||||||
    backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
 | 
					    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) {
 | 
					    if (s->inode.block_size_shift == 0) {
 | 
				
			||||||
        SheepdogVdiReq hdr;
 | 
					        SheepdogVdiReq hdr;
 | 
				
			||||||
        SheepdogClusterRsp *rsp = (SheepdogClusterRsp *)&hdr;
 | 
					        SheepdogClusterRsp *rsp = (SheepdogClusterRsp *)&hdr;
 | 
				
			||||||
        Error *local_err = NULL;
 | 
					 | 
				
			||||||
        int fd;
 | 
					        int fd;
 | 
				
			||||||
        unsigned int wlen = 0, rlen = 0;
 | 
					        unsigned int wlen = 0, rlen = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fd = connect_to_sdog(s, &local_err);
 | 
					        fd = connect_to_sdog(s, errp);
 | 
				
			||||||
        if (fd < 0) {
 | 
					        if (fd < 0) {
 | 
				
			||||||
            error_report_err(local_err);
 | 
					            ret = fd;
 | 
				
			||||||
            ret = -EIO;
 | 
					 | 
				
			||||||
            goto out;
 | 
					            goto out;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1922,7 +2123,7 @@ static void sd_close(BlockDriverState *bs)
 | 
				
			|||||||
    aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd,
 | 
					    aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd,
 | 
				
			||||||
                       false, NULL, NULL, NULL, NULL);
 | 
					                       false, NULL, NULL, NULL, NULL);
 | 
				
			||||||
    closesocket(s->fd);
 | 
					    closesocket(s->fd);
 | 
				
			||||||
    g_free(s->host_spec);
 | 
					    qapi_free_SocketAddress(s->addr);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int64_t sd_getlength(BlockDriverState *bs)
 | 
					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;
 | 
					    BDRVSheepdogState *old_s;
 | 
				
			||||||
    char tag[SD_MAX_VDI_TAG_LEN];
 | 
					    char tag[SD_MAX_VDI_TAG_LEN];
 | 
				
			||||||
    uint32_t snapid = 0;
 | 
					    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);
 | 
					    old_s = g_new(BDRVSheepdogState, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    memcpy(old_s, s, sizeof(BDRVSheepdogState));
 | 
					    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);
 | 
					    ret = reload_inode(s, snapid, tag);
 | 
				
			||||||
    if (ret) {
 | 
					    if (ret) {
 | 
				
			||||||
        goto out;
 | 
					        goto out;
 | 
				
			||||||
@ -2405,18 +2603,15 @@ out:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#define NR_BATCHED_DISCARD 128
 | 
					#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;
 | 
					    int fd, i = 0, nr_objs = 0;
 | 
				
			||||||
    Error *local_err = NULL;
 | 
					    int ret;
 | 
				
			||||||
    int ret = 0;
 | 
					 | 
				
			||||||
    bool result = true;
 | 
					 | 
				
			||||||
    SheepdogInode *inode = &s->inode;
 | 
					    SheepdogInode *inode = &s->inode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fd = connect_to_sdog(s, &local_err);
 | 
					    fd = connect_to_sdog(s, errp);
 | 
				
			||||||
    if (fd < 0) {
 | 
					    if (fd < 0) {
 | 
				
			||||||
        error_report_err(local_err);
 | 
					        return fd;
 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    nr_objs = count_data_objs(inode);
 | 
					    nr_objs = count_data_objs(inode);
 | 
				
			||||||
@ -2446,15 +2641,15 @@ static bool remove_objects(BDRVSheepdogState *s)
 | 
				
			|||||||
                                    data_vdi_id[start_idx]),
 | 
					                                    data_vdi_id[start_idx]),
 | 
				
			||||||
                           false, s->cache_flags);
 | 
					                           false, s->cache_flags);
 | 
				
			||||||
        if (ret < 0) {
 | 
					        if (ret < 0) {
 | 
				
			||||||
            error_report("failed to discard snapshot inode.");
 | 
					            error_setg(errp, "Failed to discard snapshot inode");
 | 
				
			||||||
            result = false;
 | 
					 | 
				
			||||||
            goto out;
 | 
					            goto out;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ret = 0;
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
    closesocket(fd);
 | 
					    closesocket(fd);
 | 
				
			||||||
    return result;
 | 
					    return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int sd_snapshot_delete(BlockDriverState *bs,
 | 
					static int sd_snapshot_delete(BlockDriverState *bs,
 | 
				
			||||||
@ -2462,9 +2657,12 @@ static int sd_snapshot_delete(BlockDriverState *bs,
 | 
				
			|||||||
                              const char *name,
 | 
					                              const char *name,
 | 
				
			||||||
                              Error **errp)
 | 
					                              Error **errp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * FIXME should delete the snapshot matching both @snapshot_id and
 | 
				
			||||||
 | 
					     * @name, but @name not used here
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    unsigned long snap_id = 0;
 | 
					    unsigned long snap_id = 0;
 | 
				
			||||||
    char snap_tag[SD_MAX_VDI_TAG_LEN];
 | 
					    char snap_tag[SD_MAX_VDI_TAG_LEN];
 | 
				
			||||||
    Error *local_err = NULL;
 | 
					 | 
				
			||||||
    int fd, ret;
 | 
					    int fd, ret;
 | 
				
			||||||
    char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN];
 | 
					    char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN];
 | 
				
			||||||
    BDRVSheepdogState *s = bs->opaque;
 | 
					    BDRVSheepdogState *s = bs->opaque;
 | 
				
			||||||
@ -2477,15 +2675,22 @@ static int sd_snapshot_delete(BlockDriverState *bs,
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
    SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
 | 
					    SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!remove_objects(s)) {
 | 
					    ret = remove_objects(s, errp);
 | 
				
			||||||
        return -1;
 | 
					    if (ret) {
 | 
				
			||||||
 | 
					        return ret;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    memset(buf, 0, sizeof(buf));
 | 
					    memset(buf, 0, sizeof(buf));
 | 
				
			||||||
    memset(snap_tag, 0, sizeof(snap_tag));
 | 
					    memset(snap_tag, 0, sizeof(snap_tag));
 | 
				
			||||||
    pstrcpy(buf, SD_MAX_VDI_LEN, s->name);
 | 
					    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);
 | 
					    ret = qemu_strtoul(snapshot_id, NULL, 10, &snap_id);
 | 
				
			||||||
    if (ret || snap_id > UINT32_MAX) {
 | 
					    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",
 | 
					        error_setg(errp, "Invalid snapshot ID: %s",
 | 
				
			||||||
                         snapshot_id ? snapshot_id : "<null>");
 | 
					                         snapshot_id ? snapshot_id : "<null>");
 | 
				
			||||||
        return -EINVAL;
 | 
					        return -EINVAL;
 | 
				
			||||||
@ -2494,40 +2699,42 @@ static int sd_snapshot_delete(BlockDriverState *bs,
 | 
				
			|||||||
    if (snap_id) {
 | 
					    if (snap_id) {
 | 
				
			||||||
        hdr.snapid = (uint32_t) snap_id;
 | 
					        hdr.snapid = (uint32_t) snap_id;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
 | 
					        /* FIXME I suspect we should use @name here */
 | 
				
			||||||
 | 
					        /* FIXME don't truncate silently */
 | 
				
			||||||
        pstrcpy(snap_tag, sizeof(snap_tag), snapshot_id);
 | 
					        pstrcpy(snap_tag, sizeof(snap_tag), snapshot_id);
 | 
				
			||||||
        pstrcpy(buf + SD_MAX_VDI_LEN, SD_MAX_VDI_TAG_LEN, snap_tag);
 | 
					        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,
 | 
					    ret = find_vdi_name(s, s->name, snap_id, snap_tag, &vid, true, errp);
 | 
				
			||||||
                        &local_err);
 | 
					 | 
				
			||||||
    if (ret) {
 | 
					    if (ret) {
 | 
				
			||||||
        return ret;
 | 
					        return ret;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fd = connect_to_sdog(s, &local_err);
 | 
					    fd = connect_to_sdog(s, errp);
 | 
				
			||||||
    if (fd < 0) {
 | 
					    if (fd < 0) {
 | 
				
			||||||
        error_report_err(local_err);
 | 
					        return fd;
 | 
				
			||||||
        return -1;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ret = do_req(fd, s->bs, (SheepdogReq *)&hdr,
 | 
					    ret = do_req(fd, s->bs, (SheepdogReq *)&hdr,
 | 
				
			||||||
                 buf, &wlen, &rlen);
 | 
					                 buf, &wlen, &rlen);
 | 
				
			||||||
    closesocket(fd);
 | 
					    closesocket(fd);
 | 
				
			||||||
    if (ret) {
 | 
					    if (ret) {
 | 
				
			||||||
 | 
					        error_setg_errno(errp, -ret, "Couldn't send request to server");
 | 
				
			||||||
        return ret;
 | 
					        return ret;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (rsp->result) {
 | 
					    switch (rsp->result) {
 | 
				
			||||||
    case SD_RES_NO_VDI:
 | 
					    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:
 | 
					    case SD_RES_SUCCESS:
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
        error_report("%s, %s", sd_strerror(rsp->result), s->name);
 | 
					        error_setg(errp, "%s", sd_strerror(rsp->result));
 | 
				
			||||||
        return -1;
 | 
					        return -EIO;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return ret;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
 | 
					static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
 | 
				
			||||||
@ -2832,7 +3039,7 @@ static BlockDriver bdrv_sheepdog = {
 | 
				
			|||||||
    .format_name    = "sheepdog",
 | 
					    .format_name    = "sheepdog",
 | 
				
			||||||
    .protocol_name  = "sheepdog",
 | 
					    .protocol_name  = "sheepdog",
 | 
				
			||||||
    .instance_size  = sizeof(BDRVSheepdogState),
 | 
					    .instance_size  = sizeof(BDRVSheepdogState),
 | 
				
			||||||
    .bdrv_needs_filename = true,
 | 
					    .bdrv_parse_filename    = sd_parse_filename,
 | 
				
			||||||
    .bdrv_file_open = sd_open,
 | 
					    .bdrv_file_open = sd_open,
 | 
				
			||||||
    .bdrv_reopen_prepare    = sd_reopen_prepare,
 | 
					    .bdrv_reopen_prepare    = sd_reopen_prepare,
 | 
				
			||||||
    .bdrv_reopen_commit     = sd_reopen_commit,
 | 
					    .bdrv_reopen_commit     = sd_reopen_commit,
 | 
				
			||||||
@ -2868,7 +3075,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
 | 
				
			|||||||
    .format_name    = "sheepdog",
 | 
					    .format_name    = "sheepdog",
 | 
				
			||||||
    .protocol_name  = "sheepdog+tcp",
 | 
					    .protocol_name  = "sheepdog+tcp",
 | 
				
			||||||
    .instance_size  = sizeof(BDRVSheepdogState),
 | 
					    .instance_size  = sizeof(BDRVSheepdogState),
 | 
				
			||||||
    .bdrv_needs_filename = true,
 | 
					    .bdrv_parse_filename    = sd_parse_filename,
 | 
				
			||||||
    .bdrv_file_open = sd_open,
 | 
					    .bdrv_file_open = sd_open,
 | 
				
			||||||
    .bdrv_reopen_prepare    = sd_reopen_prepare,
 | 
					    .bdrv_reopen_prepare    = sd_reopen_prepare,
 | 
				
			||||||
    .bdrv_reopen_commit     = sd_reopen_commit,
 | 
					    .bdrv_reopen_commit     = sd_reopen_commit,
 | 
				
			||||||
@ -2904,7 +3111,7 @@ static BlockDriver bdrv_sheepdog_unix = {
 | 
				
			|||||||
    .format_name    = "sheepdog",
 | 
					    .format_name    = "sheepdog",
 | 
				
			||||||
    .protocol_name  = "sheepdog+unix",
 | 
					    .protocol_name  = "sheepdog+unix",
 | 
				
			||||||
    .instance_size  = sizeof(BDRVSheepdogState),
 | 
					    .instance_size  = sizeof(BDRVSheepdogState),
 | 
				
			||||||
    .bdrv_needs_filename = true,
 | 
					    .bdrv_parse_filename    = sd_parse_filename,
 | 
				
			||||||
    .bdrv_file_open = sd_open,
 | 
					    .bdrv_file_open = sd_open,
 | 
				
			||||||
    .bdrv_reopen_prepare    = sd_reopen_prepare,
 | 
					    .bdrv_reopen_prepare    = sd_reopen_prepare,
 | 
				
			||||||
    .bdrv_reopen_commit     = sd_reopen_commit,
 | 
					    .bdrv_reopen_commit     = sd_reopen_commit,
 | 
				
			||||||
 | 
				
			|||||||
@ -1614,6 +1614,7 @@ typedef struct ExternalSnapshotState {
 | 
				
			|||||||
    BlockDriverState *old_bs;
 | 
					    BlockDriverState *old_bs;
 | 
				
			||||||
    BlockDriverState *new_bs;
 | 
					    BlockDriverState *new_bs;
 | 
				
			||||||
    AioContext *aio_context;
 | 
					    AioContext *aio_context;
 | 
				
			||||||
 | 
					    bool overlay_appended;
 | 
				
			||||||
} ExternalSnapshotState;
 | 
					} ExternalSnapshotState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void external_snapshot_prepare(BlkActionState *common,
 | 
					static void external_snapshot_prepare(BlkActionState *common,
 | 
				
			||||||
@ -1780,6 +1781,7 @@ static void external_snapshot_prepare(BlkActionState *common,
 | 
				
			|||||||
        error_propagate(errp, local_err);
 | 
					        error_propagate(errp, local_err);
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    state->overlay_appended = true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void external_snapshot_commit(BlkActionState *common)
 | 
					static void external_snapshot_commit(BlkActionState *common)
 | 
				
			||||||
@ -1803,8 +1805,8 @@ static void external_snapshot_abort(BlkActionState *common)
 | 
				
			|||||||
    ExternalSnapshotState *state =
 | 
					    ExternalSnapshotState *state =
 | 
				
			||||||
                             DO_UPCAST(ExternalSnapshotState, common, common);
 | 
					                             DO_UPCAST(ExternalSnapshotState, common, common);
 | 
				
			||||||
    if (state->new_bs) {
 | 
					    if (state->new_bs) {
 | 
				
			||||||
        if (state->new_bs->backing) {
 | 
					        if (state->overlay_appended) {
 | 
				
			||||||
            bdrv_replace_in_backing_chain(state->new_bs, state->old_bs);
 | 
					            bdrv_replace_node(state->new_bs, state->old_bs, &error_abort);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -238,8 +238,8 @@ int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp);
 | 
				
			|||||||
BlockDriverState *bdrv_new(void);
 | 
					BlockDriverState *bdrv_new(void);
 | 
				
			||||||
void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
 | 
					void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
 | 
				
			||||||
                 Error **errp);
 | 
					                 Error **errp);
 | 
				
			||||||
void bdrv_replace_in_backing_chain(BlockDriverState *old,
 | 
					void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
 | 
				
			||||||
                                   BlockDriverState *new);
 | 
					                       Error **errp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough);
 | 
					int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough);
 | 
				
			||||||
int bdrv_parse_discard_flags(const char *mode, int *flags);
 | 
					int bdrv_parse_discard_flags(const char *mode, int *flags);
 | 
				
			||||||
 | 
				
			|||||||
@ -441,8 +441,8 @@ typedef struct BdrvAioNotifier {
 | 
				
			|||||||
} BdrvAioNotifier;
 | 
					} BdrvAioNotifier;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct BdrvChildRole {
 | 
					struct BdrvChildRole {
 | 
				
			||||||
    /* If true, bdrv_replace_in_backing_chain() doesn't change the node this
 | 
					    /* If true, bdrv_replace_node() doesn't change the node this BdrvChild
 | 
				
			||||||
     * BdrvChild points to. */
 | 
					     * points to. */
 | 
				
			||||||
    bool stay_at_node;
 | 
					    bool stay_at_node;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void (*inherit_options)(int *child_flags, QDict *child_options,
 | 
					    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);
 | 
					void bdrv_root_unref_child(BdrvChild *child);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int bdrv_child_check_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
 | 
					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_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
 | 
				
			||||||
void bdrv_child_abort_perm_update(BdrvChild *c);
 | 
					void bdrv_child_abort_perm_update(BdrvChild *c);
 | 
				
			||||||
int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
 | 
					int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
 | 
				
			||||||
 | 
				
			|||||||
@ -4100,6 +4100,44 @@
 | 
				
			|||||||
    'vsock': 'VsockSocketAddress',
 | 
					    'vsock': 'VsockSocketAddress',
 | 
				
			||||||
    'fd': 'String' } }
 | 
					    '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:
 | 
					# @getfd:
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
				
			|||||||
@ -2124,6 +2124,7 @@
 | 
				
			|||||||
# @ssh: Since 2.8
 | 
					# @ssh: Since 2.8
 | 
				
			||||||
# @iscsi: Since 2.9
 | 
					# @iscsi: Since 2.9
 | 
				
			||||||
# @rbd: Since 2.9
 | 
					# @rbd: Since 2.9
 | 
				
			||||||
 | 
					# @sheepdog: Since 2.9
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Since: 2.0
 | 
					# Since: 2.0
 | 
				
			||||||
##
 | 
					##
 | 
				
			||||||
@ -2132,8 +2133,8 @@
 | 
				
			|||||||
            'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom',
 | 
					            'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom',
 | 
				
			||||||
            'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
 | 
					            'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
 | 
				
			||||||
            'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed',
 | 
					            'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed',
 | 
				
			||||||
            'quorum', 'raw', 'rbd', 'replication', 'ssh', 'vdi', 'vhdx', 'vmdk',
 | 
					            'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
 | 
				
			||||||
            'vpc', 'vvfat' ] }
 | 
					            'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
##
 | 
					##
 | 
				
			||||||
# @BlockdevOptionsFile:
 | 
					# @BlockdevOptionsFile:
 | 
				
			||||||
@ -2545,50 +2546,6 @@
 | 
				
			|||||||
            '*rewrite-corrupted': 'bool',
 | 
					            '*rewrite-corrupted': 'bool',
 | 
				
			||||||
            '*read-pattern': 'QuorumReadPattern' } }
 | 
					            '*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:
 | 
					# @BlockdevOptionsGluster:
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
@ -2610,7 +2567,7 @@
 | 
				
			|||||||
{ 'struct': 'BlockdevOptionsGluster',
 | 
					{ 'struct': 'BlockdevOptionsGluster',
 | 
				
			||||||
  'data': { 'volume': 'str',
 | 
					  'data': { 'volume': 'str',
 | 
				
			||||||
            'path': 'str',
 | 
					            'path': 'str',
 | 
				
			||||||
            'server': ['GlusterServer'],
 | 
					            'server': ['SocketAddressFlat'],
 | 
				
			||||||
            '*debug': 'int',
 | 
					            '*debug': 'int',
 | 
				
			||||||
            '*logfile': 'str' } }
 | 
					            '*logfile': 'str' } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -2735,6 +2692,26 @@
 | 
				
			|||||||
            '*auth-supported': ['RbdAuthMethod'],
 | 
					            '*auth-supported': ['RbdAuthMethod'],
 | 
				
			||||||
            '*password-secret': 'str' } }
 | 
					            '*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:
 | 
					# @ReplicationMode:
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
@ -2935,7 +2912,7 @@
 | 
				
			|||||||
      'raw':        'BlockdevOptionsRaw',
 | 
					      'raw':        'BlockdevOptionsRaw',
 | 
				
			||||||
      'rbd':        'BlockdevOptionsRbd',
 | 
					      'rbd':        'BlockdevOptionsRbd',
 | 
				
			||||||
      'replication':'BlockdevOptionsReplication',
 | 
					      'replication':'BlockdevOptionsReplication',
 | 
				
			||||||
# TODO sheepdog: Wait for structured options
 | 
					      'sheepdog':   'BlockdevOptionsSheepdog',
 | 
				
			||||||
      'ssh':        'BlockdevOptionsSsh',
 | 
					      'ssh':        'BlockdevOptionsSsh',
 | 
				
			||||||
      'vdi':        'BlockdevOptionsGenericFormat',
 | 
					      'vdi':        'BlockdevOptionsGenericFormat',
 | 
				
			||||||
      'vhdx':       'BlockdevOptionsGenericFormat',
 | 
					      'vhdx':       'BlockdevOptionsGenericFormat',
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user