diff options
Diffstat (limited to 'block/nbd.c')
-rw-r--r-- | block/nbd.c | 234 |
1 files changed, 150 insertions, 84 deletions
diff --git a/block/nbd.c b/block/nbd.c index 6bc06d6..8ef1438 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -32,6 +32,9 @@ #include "qemu/uri.h" #include "block/block_int.h" #include "qemu/module.h" +#include "qapi-visit.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qint.h" @@ -44,7 +47,8 @@ typedef struct BDRVNBDState { NbdClientSession client; /* For nbd_refresh_filename() */ - char *path, *host, *port, *export, *tlscredsid; + SocketAddress *saddr; + char *export, *tlscredsid; } BDRVNBDState; static int nbd_parse_uri(const char *filename, QDict *options) @@ -90,9 +94,13 @@ static int nbd_parse_uri(const char *filename, QDict *options) ret = -EINVAL; goto out; } - qdict_put(options, "path", qstring_from_str(qp->p[0].value)); + qdict_put(options, "server.type", qstring_from_str("unix")); + qdict_put(options, "server.data.path", + qstring_from_str(qp->p[0].value)); } else { QString *host; + char *port_str; + /* nbd[+tcp]://host[:port]/export */ if (!uri->server) { ret = -EINVAL; @@ -107,12 +115,12 @@ static int nbd_parse_uri(const char *filename, QDict *options) host = qstring_from_str(uri->server); } - qdict_put(options, "host", host); - if (uri->port) { - char* port_str = g_strdup_printf("%d", uri->port); - qdict_put(options, "port", qstring_from_str(port_str)); - g_free(port_str); - } + qdict_put(options, "server.type", qstring_from_str("inet")); + qdict_put(options, "server.data.host", host); + + port_str = g_strdup_printf("%d", uri->port ?: NBD_DEFAULT_PORT); + qdict_put(options, "server.data.port", qstring_from_str(port_str)); + g_free(port_str); } out: @@ -123,6 +131,26 @@ out: return ret; } +static bool nbd_has_filename_options_conflict(QDict *options, Error **errp) +{ + const QDictEntry *e; + + for (e = qdict_first(options); e; e = qdict_next(options, e)) { + if (!strcmp(e->key, "host") || + !strcmp(e->key, "port") || + !strcmp(e->key, "path") || + !strcmp(e->key, "export") || + strstart(e->key, "server.", NULL)) + { + error_setg(errp, "Option '%s' cannot be used with a file name", + e->key); + return true; + } + } + + return false; +} + static void nbd_parse_filename(const char *filename, QDict *options, Error **errp) { @@ -131,12 +159,7 @@ static void nbd_parse_filename(const char *filename, QDict *options, const char *host_spec; const char *unixpath; - if (qdict_haskey(options, "host") - || qdict_haskey(options, "port") - || qdict_haskey(options, "path")) - { - error_setg(errp, "host/port/path and a file name may not be specified " - "at the same time"); + if (nbd_has_filename_options_conflict(options, errp)) { return; } @@ -173,7 +196,8 @@ static void nbd_parse_filename(const char *filename, QDict *options, /* are we a UNIX or TCP socket? */ if (strstart(host_spec, "unix:", &unixpath)) { - qdict_put(options, "path", qstring_from_str(unixpath)); + qdict_put(options, "server.type", qstring_from_str("unix")); + qdict_put(options, "server.data.path", qstring_from_str(unixpath)); } else { InetSocketAddress *addr = NULL; @@ -182,8 +206,9 @@ static void nbd_parse_filename(const char *filename, QDict *options, goto out; } - qdict_put(options, "host", qstring_from_str(addr->host)); - qdict_put(options, "port", qstring_from_str(addr->port)); + qdict_put(options, "server.type", qstring_from_str("inet")); + qdict_put(options, "server.data.host", qstring_from_str(addr->host)); + qdict_put(options, "server.data.port", qstring_from_str(addr->port)); qapi_free_InetSocketAddress(addr); } @@ -191,47 +216,81 @@ out: g_free(file); } -static SocketAddress *nbd_config(BDRVNBDState *s, QemuOpts *opts, Error **errp) +static bool nbd_process_legacy_socket_options(QDict *output_options, + QemuOpts *legacy_opts, + Error **errp) { - SocketAddress *saddr; + const char *path = qemu_opt_get(legacy_opts, "path"); + const char *host = qemu_opt_get(legacy_opts, "host"); + const char *port = qemu_opt_get(legacy_opts, "port"); + const QDictEntry *e; - s->path = g_strdup(qemu_opt_get(opts, "path")); - s->host = g_strdup(qemu_opt_get(opts, "host")); + if (!path && !host && !port) { + return true; + } - if (!s->path == !s->host) { - if (s->path) { - error_setg(errp, "path and host may not be used at the same time."); - } else { - error_setg(errp, "one of path and host must be specified."); + for (e = qdict_first(output_options); e; e = qdict_next(output_options, e)) + { + if (strstart(e->key, "server.", NULL)) { + error_setg(errp, "Cannot use 'server' and path/host/port at the " + "same time"); + return false; } - return NULL; } - saddr = g_new0(SocketAddress, 1); + if (path && host) { + error_setg(errp, "path and host may not be used at the same time"); + return false; + } else if (path) { + if (port) { + error_setg(errp, "port may not be used without host"); + return false; + } - if (s->path) { - UnixSocketAddress *q_unix; - saddr->type = SOCKET_ADDRESS_KIND_UNIX; - q_unix = saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1); - q_unix->path = g_strdup(s->path); - } else { - InetSocketAddress *inet; + qdict_put(output_options, "server.type", qstring_from_str("unix")); + qdict_put(output_options, "server.data.path", qstring_from_str(path)); + } else if (host) { + qdict_put(output_options, "server.type", qstring_from_str("inet")); + qdict_put(output_options, "server.data.host", qstring_from_str(host)); + qdict_put(output_options, "server.data.port", + qstring_from_str(port ?: stringify(NBD_DEFAULT_PORT))); + } - s->port = g_strdup(qemu_opt_get(opts, "port")); + return true; +} - saddr->type = SOCKET_ADDRESS_KIND_INET; - inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1); - inet->host = g_strdup(s->host); - inet->port = g_strdup(s->port); - if (!inet->port) { - inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT); - } +static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, Error **errp) +{ + SocketAddress *saddr = NULL; + QDict *addr = NULL; + QObject *crumpled_addr = NULL; + Visitor *iv = NULL; + Error *local_err = NULL; + + qdict_extract_subqdict(options, &addr, "server."); + if (!qdict_size(addr)) { + error_setg(errp, "NBD server address missing"); + goto done; } - s->client.is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; + crumpled_addr = qdict_crumple(addr, errp); + if (!crumpled_addr) { + goto done; + } - s->export = g_strdup(qemu_opt_get(opts, "export")); + iv = qobject_input_visitor_new(crumpled_addr, true); + visit_type_SocketAddress(iv, NULL, &saddr, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto done; + } + + s->client.is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; +done: + QDECREF(addr); + qobject_decref(crumpled_addr); + visit_free(iv); return saddr; } @@ -332,7 +391,6 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, QemuOpts *opts = NULL; Error *local_err = NULL; QIOChannelSocket *sioc = NULL; - SocketAddress *saddr = NULL; QCryptoTLSCreds *tlscreds = NULL; const char *hostname = NULL; int ret = -EINVAL; @@ -344,12 +402,19 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, goto error; } + /* Translate @host, @port, and @path to a SocketAddress */ + if (!nbd_process_legacy_socket_options(options, opts, errp)) { + goto error; + } + /* Pop the config into our state object. Exit if invalid. */ - saddr = nbd_config(s, opts, errp); - if (!saddr) { + s->saddr = nbd_config(s, options, errp); + if (!s->saddr) { goto error; } + s->export = g_strdup(qemu_opt_get(opts, "export")); + s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds")); if (s->tlscredsid) { tlscreds = nbd_get_tls_creds(s->tlscredsid, errp); @@ -357,17 +422,17 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, goto error; } - if (saddr->type != SOCKET_ADDRESS_KIND_INET) { + if (s->saddr->type != SOCKET_ADDRESS_KIND_INET) { error_setg(errp, "TLS only supported over IP sockets"); goto error; } - hostname = saddr->u.inet.data->host; + hostname = s->saddr->u.inet.data->host; } /* establish TCP connection, return error if it fails * TODO: Configurable retry-until-timeout behaviour. */ - sioc = nbd_establish_connection(saddr, errp); + sioc = nbd_establish_connection(s->saddr, errp); if (!sioc) { ret = -ECONNREFUSED; goto error; @@ -384,13 +449,10 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, object_unref(OBJECT(tlscreds)); } if (ret < 0) { - g_free(s->path); - g_free(s->host); - g_free(s->port); + qapi_free_SocketAddress(s->saddr); g_free(s->export); g_free(s->tlscredsid); } - qapi_free_SocketAddress(saddr); qemu_opts_del(opts); return ret; } @@ -412,9 +474,7 @@ static void nbd_close(BlockDriverState *bs) nbd_client_close(bs); - g_free(s->path); - g_free(s->host); - g_free(s->port); + qapi_free_SocketAddress(s->saddr); g_free(s->export); g_free(s->tlscredsid); } @@ -441,45 +501,51 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) { BDRVNBDState *s = bs->opaque; QDict *opts = qdict_new(); + QObject *saddr_qdict; + Visitor *ov; + const char *host = NULL, *port = NULL, *path = NULL; + + if (s->saddr->type == SOCKET_ADDRESS_KIND_INET) { + const InetSocketAddress *inet = s->saddr->u.inet.data; + if (!inet->has_ipv4 && !inet->has_ipv6 && !inet->has_to) { + host = inet->host; + port = inet->port; + } + } else if (s->saddr->type == SOCKET_ADDRESS_KIND_UNIX) { + path = s->saddr->u.q_unix.data->path; + } - qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd"))); + qdict_put(opts, "driver", qstring_from_str("nbd")); - if (s->path && s->export) { + if (path && s->export) { snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd+unix:///%s?socket=%s", s->export, s->path); - } else if (s->path && !s->export) { + "nbd+unix:///%s?socket=%s", s->export, path); + } else if (path && !s->export) { snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd+unix://?socket=%s", s->path); - } else if (!s->path && s->export && s->port) { + "nbd+unix://?socket=%s", path); + } else if (host && s->export) { snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd://%s:%s/%s", s->host, s->port, s->export); - } else if (!s->path && s->export && !s->port) { + "nbd://%s:%s/%s", host, port, s->export); + } else if (host && !s->export) { snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd://%s/%s", s->host, s->export); - } else if (!s->path && !s->export && s->port) { - snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd://%s:%s", s->host, s->port); - } else if (!s->path && !s->export && !s->port) { - snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd://%s", s->host); + "nbd://%s:%s", host, port); } - if (s->path) { - qdict_put_obj(opts, "path", QOBJECT(qstring_from_str(s->path))); - } else if (s->port) { - qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(s->host))); - qdict_put_obj(opts, "port", QOBJECT(qstring_from_str(s->port))); - } else { - qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(s->host))); - } + ov = qobject_output_visitor_new(&saddr_qdict); + visit_type_SocketAddress(ov, NULL, &s->saddr, &error_abort); + visit_complete(ov, &saddr_qdict); + assert(qobject_type(saddr_qdict) == QTYPE_QDICT); + + qdict_put_obj(opts, "server", saddr_qdict); + if (s->export) { - qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(s->export))); + qdict_put(opts, "export", qstring_from_str(s->export)); } if (s->tlscredsid) { - qdict_put_obj(opts, "tls-creds", - QOBJECT(qstring_from_str(s->tlscredsid))); + qdict_put(opts, "tls-creds", qstring_from_str(s->tlscredsid)); } + qdict_flatten(opts); bs->full_open_options = opts; } |