xref: /openbmc/qemu/block/nbd.c (revision 0d73f725)
1019d6b8fSAnthony Liguori /*
2019d6b8fSAnthony Liguori  * QEMU Block driver for  NBD
3019d6b8fSAnthony Liguori  *
4019d6b8fSAnthony Liguori  * Copyright (C) 2008 Bull S.A.S.
5019d6b8fSAnthony Liguori  *     Author: Laurent Vivier <Laurent.Vivier@bull.net>
6019d6b8fSAnthony Liguori  *
7019d6b8fSAnthony Liguori  * Some parts:
8019d6b8fSAnthony Liguori  *    Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws>
9019d6b8fSAnthony Liguori  *
10019d6b8fSAnthony Liguori  * Permission is hereby granted, free of charge, to any person obtaining a copy
11019d6b8fSAnthony Liguori  * of this software and associated documentation files (the "Software"), to deal
12019d6b8fSAnthony Liguori  * in the Software without restriction, including without limitation the rights
13019d6b8fSAnthony Liguori  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14019d6b8fSAnthony Liguori  * copies of the Software, and to permit persons to whom the Software is
15019d6b8fSAnthony Liguori  * furnished to do so, subject to the following conditions:
16019d6b8fSAnthony Liguori  *
17019d6b8fSAnthony Liguori  * The above copyright notice and this permission notice shall be included in
18019d6b8fSAnthony Liguori  * all copies or substantial portions of the Software.
19019d6b8fSAnthony Liguori  *
20019d6b8fSAnthony Liguori  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21019d6b8fSAnthony Liguori  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22019d6b8fSAnthony Liguori  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23019d6b8fSAnthony Liguori  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24019d6b8fSAnthony Liguori  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25019d6b8fSAnthony Liguori  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26019d6b8fSAnthony Liguori  * THE SOFTWARE.
27019d6b8fSAnthony Liguori  */
28019d6b8fSAnthony Liguori 
2980c71a24SPeter Maydell #include "qemu/osdep.h"
302302c1caSMarc-André Lureau #include "block/nbd-client.h"
31da34e65cSMarkus Armbruster #include "qapi/error.h"
321de7afc9SPaolo Bonzini #include "qemu/uri.h"
33737e150eSPaolo Bonzini #include "block/block_int.h"
341de7afc9SPaolo Bonzini #include "qemu/module.h"
352019d68bSMax Reitz #include "qapi/qmp/qdict.h"
36f53a1febSKevin Wolf #include "qapi/qmp/qjson.h"
37f53a1febSKevin Wolf #include "qapi/qmp/qint.h"
382019d68bSMax Reitz #include "qapi/qmp/qstring.h"
39f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
40019d6b8fSAnthony Liguori 
411d45f8b5SLaurent Vivier #define EN_OPTSTR ":exportname="
421d45f8b5SLaurent Vivier 
43019d6b8fSAnthony Liguori typedef struct BDRVNBDState {
442302c1caSMarc-André Lureau     NbdClientSession client;
4503504d05SMax Reitz 
4603504d05SMax Reitz     /* For nbd_refresh_filename() */
4703504d05SMax Reitz     char *path, *host, *port, *export, *tlscredsid;
48019d6b8fSAnthony Liguori } BDRVNBDState;
49019d6b8fSAnthony Liguori 
50f53a1febSKevin Wolf static int nbd_parse_uri(const char *filename, QDict *options)
511d7d2a9dSPaolo Bonzini {
521d7d2a9dSPaolo Bonzini     URI *uri;
531d7d2a9dSPaolo Bonzini     const char *p;
541d7d2a9dSPaolo Bonzini     QueryParams *qp = NULL;
551d7d2a9dSPaolo Bonzini     int ret = 0;
56f53a1febSKevin Wolf     bool is_unix;
571d7d2a9dSPaolo Bonzini 
581d7d2a9dSPaolo Bonzini     uri = uri_parse(filename);
591d7d2a9dSPaolo Bonzini     if (!uri) {
601d7d2a9dSPaolo Bonzini         return -EINVAL;
611d7d2a9dSPaolo Bonzini     }
621d7d2a9dSPaolo Bonzini 
631d7d2a9dSPaolo Bonzini     /* transport */
641d7d2a9dSPaolo Bonzini     if (!strcmp(uri->scheme, "nbd")) {
65f53a1febSKevin Wolf         is_unix = false;
661d7d2a9dSPaolo Bonzini     } else if (!strcmp(uri->scheme, "nbd+tcp")) {
67f53a1febSKevin Wolf         is_unix = false;
681d7d2a9dSPaolo Bonzini     } else if (!strcmp(uri->scheme, "nbd+unix")) {
69f53a1febSKevin Wolf         is_unix = true;
701d7d2a9dSPaolo Bonzini     } else {
711d7d2a9dSPaolo Bonzini         ret = -EINVAL;
721d7d2a9dSPaolo Bonzini         goto out;
731d7d2a9dSPaolo Bonzini     }
741d7d2a9dSPaolo Bonzini 
751d7d2a9dSPaolo Bonzini     p = uri->path ? uri->path : "/";
761d7d2a9dSPaolo Bonzini     p += strspn(p, "/");
771d7d2a9dSPaolo Bonzini     if (p[0]) {
78f53a1febSKevin Wolf         qdict_put(options, "export", qstring_from_str(p));
791d7d2a9dSPaolo Bonzini     }
801d7d2a9dSPaolo Bonzini 
811d7d2a9dSPaolo Bonzini     qp = query_params_parse(uri->query);
82f53a1febSKevin Wolf     if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) {
831d7d2a9dSPaolo Bonzini         ret = -EINVAL;
841d7d2a9dSPaolo Bonzini         goto out;
851d7d2a9dSPaolo Bonzini     }
861d7d2a9dSPaolo Bonzini 
87f53a1febSKevin Wolf     if (is_unix) {
881d7d2a9dSPaolo Bonzini         /* nbd+unix:///export?socket=path */
891d7d2a9dSPaolo Bonzini         if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) {
901d7d2a9dSPaolo Bonzini             ret = -EINVAL;
911d7d2a9dSPaolo Bonzini             goto out;
921d7d2a9dSPaolo Bonzini         }
93f53a1febSKevin Wolf         qdict_put(options, "path", qstring_from_str(qp->p[0].value));
941d7d2a9dSPaolo Bonzini     } else {
9523307908SJán Tomko         QString *host;
96bebbf7faSKevin Wolf         /* nbd[+tcp]://host[:port]/export */
971d7d2a9dSPaolo Bonzini         if (!uri->server) {
981d7d2a9dSPaolo Bonzini             ret = -EINVAL;
991d7d2a9dSPaolo Bonzini             goto out;
1001d7d2a9dSPaolo Bonzini         }
101f17c90beSKevin Wolf 
10223307908SJán Tomko         /* strip braces from literal IPv6 address */
10323307908SJán Tomko         if (uri->server[0] == '[') {
10423307908SJán Tomko             host = qstring_from_substr(uri->server, 1,
10523307908SJán Tomko                                        strlen(uri->server) - 2);
10623307908SJán Tomko         } else {
10723307908SJán Tomko             host = qstring_from_str(uri->server);
10823307908SJán Tomko         }
10923307908SJán Tomko 
11023307908SJán Tomko         qdict_put(options, "host", host);
111bebbf7faSKevin Wolf         if (uri->port) {
112bebbf7faSKevin Wolf             char* port_str = g_strdup_printf("%d", uri->port);
113f53a1febSKevin Wolf             qdict_put(options, "port", qstring_from_str(port_str));
114f53a1febSKevin Wolf             g_free(port_str);
1151d7d2a9dSPaolo Bonzini         }
116bebbf7faSKevin Wolf     }
1171d7d2a9dSPaolo Bonzini 
1181d7d2a9dSPaolo Bonzini out:
1191d7d2a9dSPaolo Bonzini     if (qp) {
1201d7d2a9dSPaolo Bonzini         query_params_free(qp);
1211d7d2a9dSPaolo Bonzini     }
1221d7d2a9dSPaolo Bonzini     uri_free(uri);
1231d7d2a9dSPaolo Bonzini     return ret;
1241d7d2a9dSPaolo Bonzini }
1251d7d2a9dSPaolo Bonzini 
1266963a30dSKevin Wolf static void nbd_parse_filename(const char *filename, QDict *options,
1276963a30dSKevin Wolf                                Error **errp)
128019d6b8fSAnthony Liguori {
1291d45f8b5SLaurent Vivier     char *file;
13033897dc7SNick Thomas     char *export_name;
13133897dc7SNick Thomas     const char *host_spec;
132019d6b8fSAnthony Liguori     const char *unixpath;
133019d6b8fSAnthony Liguori 
134681e7ad0SKevin Wolf     if (qdict_haskey(options, "host")
135681e7ad0SKevin Wolf         || qdict_haskey(options, "port")
136681e7ad0SKevin Wolf         || qdict_haskey(options, "path"))
137681e7ad0SKevin Wolf     {
138681e7ad0SKevin Wolf         error_setg(errp, "host/port/path and a file name may not be specified "
139681e7ad0SKevin Wolf                          "at the same time");
140681e7ad0SKevin Wolf         return;
141681e7ad0SKevin Wolf     }
142681e7ad0SKevin Wolf 
1431d7d2a9dSPaolo Bonzini     if (strstr(filename, "://")) {
1446963a30dSKevin Wolf         int ret = nbd_parse_uri(filename, options);
1456963a30dSKevin Wolf         if (ret < 0) {
1466963a30dSKevin Wolf             error_setg(errp, "No valid URL specified");
1476963a30dSKevin Wolf         }
1486963a30dSKevin Wolf         return;
1491d7d2a9dSPaolo Bonzini     }
1501d7d2a9dSPaolo Bonzini 
1517267c094SAnthony Liguori     file = g_strdup(filename);
1521d45f8b5SLaurent Vivier 
15333897dc7SNick Thomas     export_name = strstr(file, EN_OPTSTR);
15433897dc7SNick Thomas     if (export_name) {
15533897dc7SNick Thomas         if (export_name[strlen(EN_OPTSTR)] == 0) {
1561d45f8b5SLaurent Vivier             goto out;
1571d45f8b5SLaurent Vivier         }
15833897dc7SNick Thomas         export_name[0] = 0; /* truncate 'file' */
15933897dc7SNick Thomas         export_name += strlen(EN_OPTSTR);
160f53a1febSKevin Wolf 
161f53a1febSKevin Wolf         qdict_put(options, "export", qstring_from_str(export_name));
1621d45f8b5SLaurent Vivier     }
1631d45f8b5SLaurent Vivier 
16433897dc7SNick Thomas     /* extract the host_spec - fail if it's not nbd:... */
16533897dc7SNick Thomas     if (!strstart(file, "nbd:", &host_spec)) {
1666963a30dSKevin Wolf         error_setg(errp, "File name string for NBD must start with 'nbd:'");
1671d45f8b5SLaurent Vivier         goto out;
1681d45f8b5SLaurent Vivier     }
169019d6b8fSAnthony Liguori 
170f53a1febSKevin Wolf     if (!*host_spec) {
171f53a1febSKevin Wolf         goto out;
172f53a1febSKevin Wolf     }
173f53a1febSKevin Wolf 
17433897dc7SNick Thomas     /* are we a UNIX or TCP socket? */
17533897dc7SNick Thomas     if (strstart(host_spec, "unix:", &unixpath)) {
176f53a1febSKevin Wolf         qdict_put(options, "path", qstring_from_str(unixpath));
177019d6b8fSAnthony Liguori     } else {
178f53a1febSKevin Wolf         InetSocketAddress *addr = NULL;
179f53a1febSKevin Wolf 
1806963a30dSKevin Wolf         addr = inet_parse(host_spec, errp);
18192de9012SMarkus Armbruster         if (!addr) {
182f17c90beSKevin Wolf             goto out;
183f17c90beSKevin Wolf         }
184f53a1febSKevin Wolf 
185f53a1febSKevin Wolf         qdict_put(options, "host", qstring_from_str(addr->host));
186f53a1febSKevin Wolf         qdict_put(options, "port", qstring_from_str(addr->port));
187f53a1febSKevin Wolf         qapi_free_InetSocketAddress(addr);
1881d45f8b5SLaurent Vivier     }
1891d45f8b5SLaurent Vivier 
1901d45f8b5SLaurent Vivier out:
1917267c094SAnthony Liguori     g_free(file);
19233897dc7SNick Thomas }
193f53a1febSKevin Wolf 
19403504d05SMax Reitz static SocketAddress *nbd_config(BDRVNBDState *s, QemuOpts *opts, Error **errp)
195f53a1febSKevin Wolf {
1967a5ed437SDaniel P. Berrange     SocketAddress *saddr;
197f53a1febSKevin Wolf 
19803504d05SMax Reitz     s->path = g_strdup(qemu_opt_get(opts, "path"));
19903504d05SMax Reitz     s->host = g_strdup(qemu_opt_get(opts, "host"));
20003504d05SMax Reitz 
20103504d05SMax Reitz     if (!s->path == !s->host) {
20203504d05SMax Reitz         if (s->path) {
20377e8b9caSPaolo Bonzini             error_setg(errp, "path and host may not be used at the same time.");
204f53a1febSKevin Wolf         } else {
20577e8b9caSPaolo Bonzini             error_setg(errp, "one of path and host must be specified.");
206a69d9af4SPaolo Bonzini         }
2077a5ed437SDaniel P. Berrange         return NULL;
208019d6b8fSAnthony Liguori     }
209019d6b8fSAnthony Liguori 
2107a5ed437SDaniel P. Berrange     saddr = g_new0(SocketAddress, 1);
211f53a1febSKevin Wolf 
21203504d05SMax Reitz     if (s->path) {
2130399293eSEric Blake         UnixSocketAddress *q_unix;
2146a8f9661SEric Blake         saddr->type = SOCKET_ADDRESS_KIND_UNIX;
21532bafa8fSEric Blake         q_unix = saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
21603504d05SMax Reitz         q_unix->path = g_strdup(s->path);
2177a5ed437SDaniel P. Berrange     } else {
2180399293eSEric Blake         InetSocketAddress *inet;
21903504d05SMax Reitz 
22003504d05SMax Reitz         s->port = g_strdup(qemu_opt_get(opts, "port"));
22103504d05SMax Reitz 
2226a8f9661SEric Blake         saddr->type = SOCKET_ADDRESS_KIND_INET;
22332bafa8fSEric Blake         inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1);
22403504d05SMax Reitz         inet->host = g_strdup(s->host);
22503504d05SMax Reitz         inet->port = g_strdup(s->port);
2267ccc44fdSMax Reitz         if (!inet->port) {
2270399293eSEric Blake             inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
2287a5ed437SDaniel P. Berrange         }
229f53a1febSKevin Wolf     }
230f53a1febSKevin Wolf 
2316a8f9661SEric Blake     s->client.is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
232bebbf7faSKevin Wolf 
23303504d05SMax Reitz     s->export = g_strdup(qemu_opt_get(opts, "export"));
2347a5ed437SDaniel P. Berrange 
2357a5ed437SDaniel P. Berrange     return saddr;
236f53a1febSKevin Wolf }
237f53a1febSKevin Wolf 
238f53a829bSMax Reitz NbdClientSession *nbd_get_client_session(BlockDriverState *bs)
239f53a829bSMax Reitz {
240f53a829bSMax Reitz     BDRVNBDState *s = bs->opaque;
241f53a829bSMax Reitz     return &s->client;
242f53a829bSMax Reitz }
243f53a829bSMax Reitz 
244064097d9SDaniel P. Berrange static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr,
2457a5ed437SDaniel P. Berrange                                                   Error **errp)
24633897dc7SNick Thomas {
247064097d9SDaniel P. Berrange     QIOChannelSocket *sioc;
248064097d9SDaniel P. Berrange     Error *local_err = NULL;
24933897dc7SNick Thomas 
250064097d9SDaniel P. Berrange     sioc = qio_channel_socket_new();
251*0d73f725SDaniel P. Berrange     qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client");
25233897dc7SNick Thomas 
253064097d9SDaniel P. Berrange     qio_channel_socket_connect_sync(sioc,
254064097d9SDaniel P. Berrange                                     saddr,
255064097d9SDaniel P. Berrange                                     &local_err);
256064097d9SDaniel P. Berrange     if (local_err) {
257064097d9SDaniel P. Berrange         error_propagate(errp, local_err);
258064097d9SDaniel P. Berrange         return NULL;
25933897dc7SNick Thomas     }
26033897dc7SNick Thomas 
261064097d9SDaniel P. Berrange     qio_channel_set_delay(QIO_CHANNEL(sioc), false);
2627a5ed437SDaniel P. Berrange 
263064097d9SDaniel P. Berrange     return sioc;
26433897dc7SNick Thomas }
26533897dc7SNick Thomas 
26675822a12SDaniel P. Berrange 
26775822a12SDaniel P. Berrange static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
26875822a12SDaniel P. Berrange {
26975822a12SDaniel P. Berrange     Object *obj;
27075822a12SDaniel P. Berrange     QCryptoTLSCreds *creds;
27175822a12SDaniel P. Berrange 
27275822a12SDaniel P. Berrange     obj = object_resolve_path_component(
27375822a12SDaniel P. Berrange         object_get_objects_root(), id);
27475822a12SDaniel P. Berrange     if (!obj) {
27575822a12SDaniel P. Berrange         error_setg(errp, "No TLS credentials with id '%s'",
27675822a12SDaniel P. Berrange                    id);
27775822a12SDaniel P. Berrange         return NULL;
27875822a12SDaniel P. Berrange     }
27975822a12SDaniel P. Berrange     creds = (QCryptoTLSCreds *)
28075822a12SDaniel P. Berrange         object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
28175822a12SDaniel P. Berrange     if (!creds) {
28275822a12SDaniel P. Berrange         error_setg(errp, "Object with id '%s' is not TLS credentials",
28375822a12SDaniel P. Berrange                    id);
28475822a12SDaniel P. Berrange         return NULL;
28575822a12SDaniel P. Berrange     }
28675822a12SDaniel P. Berrange 
28775822a12SDaniel P. Berrange     if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
28875822a12SDaniel P. Berrange         error_setg(errp,
28975822a12SDaniel P. Berrange                    "Expecting TLS credentials with a client endpoint");
29075822a12SDaniel P. Berrange         return NULL;
29175822a12SDaniel P. Berrange     }
29275822a12SDaniel P. Berrange     object_ref(obj);
29375822a12SDaniel P. Berrange     return creds;
29475822a12SDaniel P. Berrange }
29575822a12SDaniel P. Berrange 
29675822a12SDaniel P. Berrange 
2977ccc44fdSMax Reitz static QemuOptsList nbd_runtime_opts = {
2987ccc44fdSMax Reitz     .name = "nbd",
2997ccc44fdSMax Reitz     .head = QTAILQ_HEAD_INITIALIZER(nbd_runtime_opts.head),
3007ccc44fdSMax Reitz     .desc = {
3017ccc44fdSMax Reitz         {
3027ccc44fdSMax Reitz             .name = "host",
3037ccc44fdSMax Reitz             .type = QEMU_OPT_STRING,
3047ccc44fdSMax Reitz             .help = "TCP host to connect to",
3057ccc44fdSMax Reitz         },
3067ccc44fdSMax Reitz         {
3077ccc44fdSMax Reitz             .name = "port",
3087ccc44fdSMax Reitz             .type = QEMU_OPT_STRING,
3097ccc44fdSMax Reitz             .help = "TCP port to connect to",
3107ccc44fdSMax Reitz         },
3117ccc44fdSMax Reitz         {
3127ccc44fdSMax Reitz             .name = "path",
3137ccc44fdSMax Reitz             .type = QEMU_OPT_STRING,
3147ccc44fdSMax Reitz             .help = "Unix socket path to connect to",
3157ccc44fdSMax Reitz         },
3167ccc44fdSMax Reitz         {
3177ccc44fdSMax Reitz             .name = "export",
3187ccc44fdSMax Reitz             .type = QEMU_OPT_STRING,
3197ccc44fdSMax Reitz             .help = "Name of the NBD export to open",
3207ccc44fdSMax Reitz         },
3217ccc44fdSMax Reitz         {
3227ccc44fdSMax Reitz             .name = "tls-creds",
3237ccc44fdSMax Reitz             .type = QEMU_OPT_STRING,
3247ccc44fdSMax Reitz             .help = "ID of the TLS credentials to use",
3257ccc44fdSMax Reitz         },
3267ccc44fdSMax Reitz     },
3277ccc44fdSMax Reitz };
3287ccc44fdSMax Reitz 
329015a1036SMax Reitz static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
330015a1036SMax Reitz                     Error **errp)
33133897dc7SNick Thomas {
33233897dc7SNick Thomas     BDRVNBDState *s = bs->opaque;
3337ccc44fdSMax Reitz     QemuOpts *opts = NULL;
3347ccc44fdSMax Reitz     Error *local_err = NULL;
33575822a12SDaniel P. Berrange     QIOChannelSocket *sioc = NULL;
3367ccc44fdSMax Reitz     SocketAddress *saddr = NULL;
33775822a12SDaniel P. Berrange     QCryptoTLSCreds *tlscreds = NULL;
33875822a12SDaniel P. Berrange     const char *hostname = NULL;
33975822a12SDaniel P. Berrange     int ret = -EINVAL;
340ae255e52SPaolo Bonzini 
3417ccc44fdSMax Reitz     opts = qemu_opts_create(&nbd_runtime_opts, NULL, 0, &error_abort);
3427ccc44fdSMax Reitz     qemu_opts_absorb_qdict(opts, options, &local_err);
3437ccc44fdSMax Reitz     if (local_err) {
3447ccc44fdSMax Reitz         error_propagate(errp, local_err);
3457ccc44fdSMax Reitz         goto error;
3467ccc44fdSMax Reitz     }
3477ccc44fdSMax Reitz 
34833897dc7SNick Thomas     /* Pop the config into our state object. Exit if invalid. */
34903504d05SMax Reitz     saddr = nbd_config(s, opts, errp);
3507a5ed437SDaniel P. Berrange     if (!saddr) {
35175822a12SDaniel P. Berrange         goto error;
35275822a12SDaniel P. Berrange     }
35375822a12SDaniel P. Berrange 
35403504d05SMax Reitz     s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds"));
35503504d05SMax Reitz     if (s->tlscredsid) {
35603504d05SMax Reitz         tlscreds = nbd_get_tls_creds(s->tlscredsid, errp);
35775822a12SDaniel P. Berrange         if (!tlscreds) {
35875822a12SDaniel P. Berrange             goto error;
35975822a12SDaniel P. Berrange         }
36075822a12SDaniel P. Berrange 
36175822a12SDaniel P. Berrange         if (saddr->type != SOCKET_ADDRESS_KIND_INET) {
36275822a12SDaniel P. Berrange             error_setg(errp, "TLS only supported over IP sockets");
36375822a12SDaniel P. Berrange             goto error;
36475822a12SDaniel P. Berrange         }
36532bafa8fSEric Blake         hostname = saddr->u.inet.data->host;
36633897dc7SNick Thomas     }
36733897dc7SNick Thomas 
36833897dc7SNick Thomas     /* establish TCP connection, return error if it fails
36933897dc7SNick Thomas      * TODO: Configurable retry-until-timeout behaviour.
37033897dc7SNick Thomas      */
371064097d9SDaniel P. Berrange     sioc = nbd_establish_connection(saddr, errp);
372064097d9SDaniel P. Berrange     if (!sioc) {
37375822a12SDaniel P. Berrange         ret = -ECONNREFUSED;
37475822a12SDaniel P. Berrange         goto error;
37533897dc7SNick Thomas     }
37633897dc7SNick Thomas 
3772302c1caSMarc-André Lureau     /* NBD handshake */
37803504d05SMax Reitz     ret = nbd_client_init(bs, sioc, s->export,
37975822a12SDaniel P. Berrange                           tlscreds, hostname, errp);
38075822a12SDaniel P. Berrange  error:
38175822a12SDaniel P. Berrange     if (sioc) {
382064097d9SDaniel P. Berrange         object_unref(OBJECT(sioc));
38375822a12SDaniel P. Berrange     }
38475822a12SDaniel P. Berrange     if (tlscreds) {
38575822a12SDaniel P. Berrange         object_unref(OBJECT(tlscreds));
38675822a12SDaniel P. Berrange     }
38703504d05SMax Reitz     if (ret < 0) {
38803504d05SMax Reitz         g_free(s->path);
38903504d05SMax Reitz         g_free(s->host);
39003504d05SMax Reitz         g_free(s->port);
39103504d05SMax Reitz         g_free(s->export);
39203504d05SMax Reitz         g_free(s->tlscredsid);
39303504d05SMax Reitz     }
39475822a12SDaniel P. Berrange     qapi_free_SocketAddress(saddr);
3957ccc44fdSMax Reitz     qemu_opts_del(opts);
39675822a12SDaniel P. Berrange     return ret;
397ae255e52SPaolo Bonzini }
398d9b09f13SPaolo Bonzini 
3991486d04aSPaolo Bonzini static int nbd_co_flush(BlockDriverState *bs)
4001486d04aSPaolo Bonzini {
401f53a829bSMax Reitz     return nbd_client_co_flush(bs);
4021486d04aSPaolo Bonzini }
4031486d04aSPaolo Bonzini 
404fa21e6faSDenis V. Lunev static void nbd_refresh_limits(BlockDriverState *bs, Error **errp)
405fa21e6faSDenis V. Lunev {
406b9f7855aSEric Blake     bs->bl.max_pdiscard = NBD_MAX_BUFFER_SIZE;
4075def6b80SEric Blake     bs->bl.max_transfer = NBD_MAX_BUFFER_SIZE;
408fa21e6faSDenis V. Lunev }
409fa21e6faSDenis V. Lunev 
410019d6b8fSAnthony Liguori static void nbd_close(BlockDriverState *bs)
411019d6b8fSAnthony Liguori {
41203504d05SMax Reitz     BDRVNBDState *s = bs->opaque;
41303504d05SMax Reitz 
414f53a829bSMax Reitz     nbd_client_close(bs);
41503504d05SMax Reitz 
41603504d05SMax Reitz     g_free(s->path);
41703504d05SMax Reitz     g_free(s->host);
41803504d05SMax Reitz     g_free(s->port);
41903504d05SMax Reitz     g_free(s->export);
42003504d05SMax Reitz     g_free(s->tlscredsid);
421019d6b8fSAnthony Liguori }
422019d6b8fSAnthony Liguori 
423019d6b8fSAnthony Liguori static int64_t nbd_getlength(BlockDriverState *bs)
424019d6b8fSAnthony Liguori {
425019d6b8fSAnthony Liguori     BDRVNBDState *s = bs->opaque;
426019d6b8fSAnthony Liguori 
4272302c1caSMarc-André Lureau     return s->client.size;
428019d6b8fSAnthony Liguori }
429019d6b8fSAnthony Liguori 
43069447cd8SStefan Hajnoczi static void nbd_detach_aio_context(BlockDriverState *bs)
43169447cd8SStefan Hajnoczi {
432f53a829bSMax Reitz     nbd_client_detach_aio_context(bs);
43369447cd8SStefan Hajnoczi }
43469447cd8SStefan Hajnoczi 
43569447cd8SStefan Hajnoczi static void nbd_attach_aio_context(BlockDriverState *bs,
43669447cd8SStefan Hajnoczi                                    AioContext *new_context)
43769447cd8SStefan Hajnoczi {
438f53a829bSMax Reitz     nbd_client_attach_aio_context(bs, new_context);
43969447cd8SStefan Hajnoczi }
44069447cd8SStefan Hajnoczi 
4414cdd01d3SKevin Wolf static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
4422019d68bSMax Reitz {
44303504d05SMax Reitz     BDRVNBDState *s = bs->opaque;
4442019d68bSMax Reitz     QDict *opts = qdict_new();
4452019d68bSMax Reitz 
4462019d68bSMax Reitz     qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd")));
4472019d68bSMax Reitz 
44803504d05SMax Reitz     if (s->path && s->export) {
449ec0de768SMax Reitz         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
45003504d05SMax Reitz                  "nbd+unix:///%s?socket=%s", s->export, s->path);
45103504d05SMax Reitz     } else if (s->path && !s->export) {
452ec0de768SMax Reitz         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
45303504d05SMax Reitz                  "nbd+unix://?socket=%s", s->path);
45403504d05SMax Reitz     } else if (!s->path && s->export && s->port) {
455ec0de768SMax Reitz         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
45603504d05SMax Reitz                  "nbd://%s:%s/%s", s->host, s->port, s->export);
45703504d05SMax Reitz     } else if (!s->path && s->export && !s->port) {
458ec0de768SMax Reitz         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
45903504d05SMax Reitz                  "nbd://%s/%s", s->host, s->export);
46003504d05SMax Reitz     } else if (!s->path && !s->export && s->port) {
461ec0de768SMax Reitz         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
46203504d05SMax Reitz                  "nbd://%s:%s", s->host, s->port);
46303504d05SMax Reitz     } else if (!s->path && !s->export && !s->port) {
464ec0de768SMax Reitz         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
46503504d05SMax Reitz                  "nbd://%s", s->host);
466ec0de768SMax Reitz     }
467ec0de768SMax Reitz 
46803504d05SMax Reitz     if (s->path) {
46903504d05SMax Reitz         qdict_put_obj(opts, "path", QOBJECT(qstring_from_str(s->path)));
47003504d05SMax Reitz     } else if (s->port) {
47103504d05SMax Reitz         qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(s->host)));
47203504d05SMax Reitz         qdict_put_obj(opts, "port", QOBJECT(qstring_from_str(s->port)));
4732019d68bSMax Reitz     } else {
47403504d05SMax Reitz         qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(s->host)));
475ec0de768SMax Reitz     }
47603504d05SMax Reitz     if (s->export) {
47703504d05SMax Reitz         qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(s->export)));
4782019d68bSMax Reitz     }
47903504d05SMax Reitz     if (s->tlscredsid) {
48003504d05SMax Reitz         qdict_put_obj(opts, "tls-creds",
48103504d05SMax Reitz                       QOBJECT(qstring_from_str(s->tlscredsid)));
48275822a12SDaniel P. Berrange     }
4832019d68bSMax Reitz 
4842019d68bSMax Reitz     bs->full_open_options = opts;
4852019d68bSMax Reitz }
4862019d68bSMax Reitz 
487019d6b8fSAnthony Liguori static BlockDriver bdrv_nbd = {
488019d6b8fSAnthony Liguori     .format_name                = "nbd",
4891d7d2a9dSPaolo Bonzini     .protocol_name              = "nbd",
490019d6b8fSAnthony Liguori     .instance_size              = sizeof(BDRVNBDState),
4916963a30dSKevin Wolf     .bdrv_parse_filename        = nbd_parse_filename,
49266f82ceeSKevin Wolf     .bdrv_file_open             = nbd_open,
49370c4fb26SEric Blake     .bdrv_co_preadv             = nbd_client_co_preadv,
49470c4fb26SEric Blake     .bdrv_co_pwritev            = nbd_client_co_pwritev,
495019d6b8fSAnthony Liguori     .bdrv_close                 = nbd_close,
4961486d04aSPaolo Bonzini     .bdrv_co_flush_to_os        = nbd_co_flush,
497447e57c3SEric Blake     .bdrv_co_pdiscard           = nbd_client_co_pdiscard,
498fa21e6faSDenis V. Lunev     .bdrv_refresh_limits        = nbd_refresh_limits,
499019d6b8fSAnthony Liguori     .bdrv_getlength             = nbd_getlength,
50069447cd8SStefan Hajnoczi     .bdrv_detach_aio_context    = nbd_detach_aio_context,
50169447cd8SStefan Hajnoczi     .bdrv_attach_aio_context    = nbd_attach_aio_context,
5022019d68bSMax Reitz     .bdrv_refresh_filename      = nbd_refresh_filename,
5031d7d2a9dSPaolo Bonzini };
5041d7d2a9dSPaolo Bonzini 
5051d7d2a9dSPaolo Bonzini static BlockDriver bdrv_nbd_tcp = {
5061d7d2a9dSPaolo Bonzini     .format_name                = "nbd",
5071d7d2a9dSPaolo Bonzini     .protocol_name              = "nbd+tcp",
5081d7d2a9dSPaolo Bonzini     .instance_size              = sizeof(BDRVNBDState),
5096963a30dSKevin Wolf     .bdrv_parse_filename        = nbd_parse_filename,
5101d7d2a9dSPaolo Bonzini     .bdrv_file_open             = nbd_open,
51170c4fb26SEric Blake     .bdrv_co_preadv             = nbd_client_co_preadv,
51270c4fb26SEric Blake     .bdrv_co_pwritev            = nbd_client_co_pwritev,
5131d7d2a9dSPaolo Bonzini     .bdrv_close                 = nbd_close,
5141d7d2a9dSPaolo Bonzini     .bdrv_co_flush_to_os        = nbd_co_flush,
515447e57c3SEric Blake     .bdrv_co_pdiscard           = nbd_client_co_pdiscard,
516fa21e6faSDenis V. Lunev     .bdrv_refresh_limits        = nbd_refresh_limits,
5171d7d2a9dSPaolo Bonzini     .bdrv_getlength             = nbd_getlength,
51869447cd8SStefan Hajnoczi     .bdrv_detach_aio_context    = nbd_detach_aio_context,
51969447cd8SStefan Hajnoczi     .bdrv_attach_aio_context    = nbd_attach_aio_context,
5202019d68bSMax Reitz     .bdrv_refresh_filename      = nbd_refresh_filename,
5211d7d2a9dSPaolo Bonzini };
5221d7d2a9dSPaolo Bonzini 
5231d7d2a9dSPaolo Bonzini static BlockDriver bdrv_nbd_unix = {
5241d7d2a9dSPaolo Bonzini     .format_name                = "nbd",
5251d7d2a9dSPaolo Bonzini     .protocol_name              = "nbd+unix",
5261d7d2a9dSPaolo Bonzini     .instance_size              = sizeof(BDRVNBDState),
5276963a30dSKevin Wolf     .bdrv_parse_filename        = nbd_parse_filename,
5281d7d2a9dSPaolo Bonzini     .bdrv_file_open             = nbd_open,
52970c4fb26SEric Blake     .bdrv_co_preadv             = nbd_client_co_preadv,
53070c4fb26SEric Blake     .bdrv_co_pwritev            = nbd_client_co_pwritev,
5311d7d2a9dSPaolo Bonzini     .bdrv_close                 = nbd_close,
5321d7d2a9dSPaolo Bonzini     .bdrv_co_flush_to_os        = nbd_co_flush,
533447e57c3SEric Blake     .bdrv_co_pdiscard           = nbd_client_co_pdiscard,
534fa21e6faSDenis V. Lunev     .bdrv_refresh_limits        = nbd_refresh_limits,
5351d7d2a9dSPaolo Bonzini     .bdrv_getlength             = nbd_getlength,
53669447cd8SStefan Hajnoczi     .bdrv_detach_aio_context    = nbd_detach_aio_context,
53769447cd8SStefan Hajnoczi     .bdrv_attach_aio_context    = nbd_attach_aio_context,
5382019d68bSMax Reitz     .bdrv_refresh_filename      = nbd_refresh_filename,
539019d6b8fSAnthony Liguori };
540019d6b8fSAnthony Liguori 
541019d6b8fSAnthony Liguori static void bdrv_nbd_init(void)
542019d6b8fSAnthony Liguori {
543019d6b8fSAnthony Liguori     bdrv_register(&bdrv_nbd);
5441d7d2a9dSPaolo Bonzini     bdrv_register(&bdrv_nbd_tcp);
5451d7d2a9dSPaolo Bonzini     bdrv_register(&bdrv_nbd_unix);
546019d6b8fSAnthony Liguori }
547019d6b8fSAnthony Liguori 
548019d6b8fSAnthony Liguori block_init(bdrv_nbd_init);
549