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" 35491d6c7cSMax Reitz #include "qapi-visit.h" 36491d6c7cSMax Reitz #include "qapi/qobject-input-visitor.h" 37491d6c7cSMax Reitz #include "qapi/qobject-output-visitor.h" 382019d68bSMax Reitz #include "qapi/qmp/qdict.h" 39f53a1febSKevin Wolf #include "qapi/qmp/qjson.h" 40f53a1febSKevin Wolf #include "qapi/qmp/qint.h" 412019d68bSMax Reitz #include "qapi/qmp/qstring.h" 42f348b6d1SVeronia Bahaa #include "qemu/cutils.h" 43019d6b8fSAnthony Liguori 441d45f8b5SLaurent Vivier #define EN_OPTSTR ":exportname=" 451d45f8b5SLaurent Vivier 46019d6b8fSAnthony Liguori typedef struct BDRVNBDState { 4710676b81SEric Blake NBDClientSession client; 4803504d05SMax Reitz 4903504d05SMax Reitz /* For nbd_refresh_filename() */ 50491d6c7cSMax Reitz SocketAddress *saddr; 51491d6c7cSMax Reitz char *export, *tlscredsid; 52019d6b8fSAnthony Liguori } BDRVNBDState; 53019d6b8fSAnthony Liguori 54f53a1febSKevin Wolf static int nbd_parse_uri(const char *filename, QDict *options) 551d7d2a9dSPaolo Bonzini { 561d7d2a9dSPaolo Bonzini URI *uri; 571d7d2a9dSPaolo Bonzini const char *p; 581d7d2a9dSPaolo Bonzini QueryParams *qp = NULL; 591d7d2a9dSPaolo Bonzini int ret = 0; 60f53a1febSKevin Wolf bool is_unix; 611d7d2a9dSPaolo Bonzini 621d7d2a9dSPaolo Bonzini uri = uri_parse(filename); 631d7d2a9dSPaolo Bonzini if (!uri) { 641d7d2a9dSPaolo Bonzini return -EINVAL; 651d7d2a9dSPaolo Bonzini } 661d7d2a9dSPaolo Bonzini 671d7d2a9dSPaolo Bonzini /* transport */ 681d7d2a9dSPaolo Bonzini if (!strcmp(uri->scheme, "nbd")) { 69f53a1febSKevin Wolf is_unix = false; 701d7d2a9dSPaolo Bonzini } else if (!strcmp(uri->scheme, "nbd+tcp")) { 71f53a1febSKevin Wolf is_unix = false; 721d7d2a9dSPaolo Bonzini } else if (!strcmp(uri->scheme, "nbd+unix")) { 73f53a1febSKevin Wolf is_unix = true; 741d7d2a9dSPaolo Bonzini } else { 751d7d2a9dSPaolo Bonzini ret = -EINVAL; 761d7d2a9dSPaolo Bonzini goto out; 771d7d2a9dSPaolo Bonzini } 781d7d2a9dSPaolo Bonzini 791d7d2a9dSPaolo Bonzini p = uri->path ? uri->path : "/"; 801d7d2a9dSPaolo Bonzini p += strspn(p, "/"); 811d7d2a9dSPaolo Bonzini if (p[0]) { 82f53a1febSKevin Wolf qdict_put(options, "export", qstring_from_str(p)); 831d7d2a9dSPaolo Bonzini } 841d7d2a9dSPaolo Bonzini 851d7d2a9dSPaolo Bonzini qp = query_params_parse(uri->query); 86f53a1febSKevin Wolf if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) { 871d7d2a9dSPaolo Bonzini ret = -EINVAL; 881d7d2a9dSPaolo Bonzini goto out; 891d7d2a9dSPaolo Bonzini } 901d7d2a9dSPaolo Bonzini 91f53a1febSKevin Wolf if (is_unix) { 921d7d2a9dSPaolo Bonzini /* nbd+unix:///export?socket=path */ 931d7d2a9dSPaolo Bonzini if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) { 941d7d2a9dSPaolo Bonzini ret = -EINVAL; 951d7d2a9dSPaolo Bonzini goto out; 961d7d2a9dSPaolo Bonzini } 97f84d431bSMax Reitz qdict_put(options, "server.type", qstring_from_str("unix")); 98f84d431bSMax Reitz qdict_put(options, "server.data.path", 99f84d431bSMax Reitz qstring_from_str(qp->p[0].value)); 1001d7d2a9dSPaolo Bonzini } else { 10123307908SJán Tomko QString *host; 102f84d431bSMax Reitz char *port_str; 103f84d431bSMax Reitz 104bebbf7faSKevin Wolf /* nbd[+tcp]://host[:port]/export */ 1051d7d2a9dSPaolo Bonzini if (!uri->server) { 1061d7d2a9dSPaolo Bonzini ret = -EINVAL; 1071d7d2a9dSPaolo Bonzini goto out; 1081d7d2a9dSPaolo Bonzini } 109f17c90beSKevin Wolf 11023307908SJán Tomko /* strip braces from literal IPv6 address */ 11123307908SJán Tomko if (uri->server[0] == '[') { 11223307908SJán Tomko host = qstring_from_substr(uri->server, 1, 11323307908SJán Tomko strlen(uri->server) - 2); 11423307908SJán Tomko } else { 11523307908SJán Tomko host = qstring_from_str(uri->server); 11623307908SJán Tomko } 11723307908SJán Tomko 118f84d431bSMax Reitz qdict_put(options, "server.type", qstring_from_str("inet")); 119f84d431bSMax Reitz qdict_put(options, "server.data.host", host); 120f84d431bSMax Reitz 121f84d431bSMax Reitz port_str = g_strdup_printf("%d", uri->port ?: NBD_DEFAULT_PORT); 122f84d431bSMax Reitz qdict_put(options, "server.data.port", qstring_from_str(port_str)); 123f53a1febSKevin Wolf g_free(port_str); 1241d7d2a9dSPaolo Bonzini } 1251d7d2a9dSPaolo Bonzini 1261d7d2a9dSPaolo Bonzini out: 1271d7d2a9dSPaolo Bonzini if (qp) { 1281d7d2a9dSPaolo Bonzini query_params_free(qp); 1291d7d2a9dSPaolo Bonzini } 1301d7d2a9dSPaolo Bonzini uri_free(uri); 1311d7d2a9dSPaolo Bonzini return ret; 1321d7d2a9dSPaolo Bonzini } 1331d7d2a9dSPaolo Bonzini 13448c38e0bSMax Reitz static bool nbd_has_filename_options_conflict(QDict *options, Error **errp) 13548c38e0bSMax Reitz { 13648c38e0bSMax Reitz const QDictEntry *e; 13748c38e0bSMax Reitz 13848c38e0bSMax Reitz for (e = qdict_first(options); e; e = qdict_next(options, e)) { 13948c38e0bSMax Reitz if (!strcmp(e->key, "host") || 14048c38e0bSMax Reitz !strcmp(e->key, "port") || 14148c38e0bSMax Reitz !strcmp(e->key, "path") || 142491d6c7cSMax Reitz !strcmp(e->key, "export") || 143491d6c7cSMax Reitz strstart(e->key, "server.", NULL)) 14448c38e0bSMax Reitz { 14548c38e0bSMax Reitz error_setg(errp, "Option '%s' cannot be used with a file name", 14648c38e0bSMax Reitz e->key); 14748c38e0bSMax Reitz return true; 14848c38e0bSMax Reitz } 14948c38e0bSMax Reitz } 15048c38e0bSMax Reitz 15148c38e0bSMax Reitz return false; 15248c38e0bSMax Reitz } 15348c38e0bSMax Reitz 1546963a30dSKevin Wolf static void nbd_parse_filename(const char *filename, QDict *options, 1556963a30dSKevin Wolf Error **errp) 156019d6b8fSAnthony Liguori { 1571d45f8b5SLaurent Vivier char *file; 15833897dc7SNick Thomas char *export_name; 15933897dc7SNick Thomas const char *host_spec; 160019d6b8fSAnthony Liguori const char *unixpath; 161019d6b8fSAnthony Liguori 16248c38e0bSMax Reitz if (nbd_has_filename_options_conflict(options, errp)) { 163681e7ad0SKevin Wolf return; 164681e7ad0SKevin Wolf } 165681e7ad0SKevin Wolf 1661d7d2a9dSPaolo Bonzini if (strstr(filename, "://")) { 1676963a30dSKevin Wolf int ret = nbd_parse_uri(filename, options); 1686963a30dSKevin Wolf if (ret < 0) { 1696963a30dSKevin Wolf error_setg(errp, "No valid URL specified"); 1706963a30dSKevin Wolf } 1716963a30dSKevin Wolf return; 1721d7d2a9dSPaolo Bonzini } 1731d7d2a9dSPaolo Bonzini 1747267c094SAnthony Liguori file = g_strdup(filename); 1751d45f8b5SLaurent Vivier 17633897dc7SNick Thomas export_name = strstr(file, EN_OPTSTR); 17733897dc7SNick Thomas if (export_name) { 17833897dc7SNick Thomas if (export_name[strlen(EN_OPTSTR)] == 0) { 1791d45f8b5SLaurent Vivier goto out; 1801d45f8b5SLaurent Vivier } 18133897dc7SNick Thomas export_name[0] = 0; /* truncate 'file' */ 18233897dc7SNick Thomas export_name += strlen(EN_OPTSTR); 183f53a1febSKevin Wolf 184f53a1febSKevin Wolf qdict_put(options, "export", qstring_from_str(export_name)); 1851d45f8b5SLaurent Vivier } 1861d45f8b5SLaurent Vivier 18733897dc7SNick Thomas /* extract the host_spec - fail if it's not nbd:... */ 18833897dc7SNick Thomas if (!strstart(file, "nbd:", &host_spec)) { 1896963a30dSKevin Wolf error_setg(errp, "File name string for NBD must start with 'nbd:'"); 1901d45f8b5SLaurent Vivier goto out; 1911d45f8b5SLaurent Vivier } 192019d6b8fSAnthony Liguori 193f53a1febSKevin Wolf if (!*host_spec) { 194f53a1febSKevin Wolf goto out; 195f53a1febSKevin Wolf } 196f53a1febSKevin Wolf 19733897dc7SNick Thomas /* are we a UNIX or TCP socket? */ 19833897dc7SNick Thomas if (strstart(host_spec, "unix:", &unixpath)) { 199f84d431bSMax Reitz qdict_put(options, "server.type", qstring_from_str("unix")); 200f84d431bSMax Reitz qdict_put(options, "server.data.path", qstring_from_str(unixpath)); 201019d6b8fSAnthony Liguori } else { 202f53a1febSKevin Wolf InetSocketAddress *addr = NULL; 203f53a1febSKevin Wolf 2046963a30dSKevin Wolf addr = inet_parse(host_spec, errp); 20592de9012SMarkus Armbruster if (!addr) { 206f17c90beSKevin Wolf goto out; 207f17c90beSKevin Wolf } 208f53a1febSKevin Wolf 209f84d431bSMax Reitz qdict_put(options, "server.type", qstring_from_str("inet")); 210f84d431bSMax Reitz qdict_put(options, "server.data.host", qstring_from_str(addr->host)); 211f84d431bSMax Reitz qdict_put(options, "server.data.port", qstring_from_str(addr->port)); 212f53a1febSKevin Wolf qapi_free_InetSocketAddress(addr); 2131d45f8b5SLaurent Vivier } 2141d45f8b5SLaurent Vivier 2151d45f8b5SLaurent Vivier out: 2167267c094SAnthony Liguori g_free(file); 21733897dc7SNick Thomas } 218f53a1febSKevin Wolf 219491d6c7cSMax Reitz static bool nbd_process_legacy_socket_options(QDict *output_options, 220491d6c7cSMax Reitz QemuOpts *legacy_opts, 221491d6c7cSMax Reitz Error **errp) 222f53a1febSKevin Wolf { 223491d6c7cSMax Reitz const char *path = qemu_opt_get(legacy_opts, "path"); 224491d6c7cSMax Reitz const char *host = qemu_opt_get(legacy_opts, "host"); 225491d6c7cSMax Reitz const char *port = qemu_opt_get(legacy_opts, "port"); 226491d6c7cSMax Reitz const QDictEntry *e; 227f53a1febSKevin Wolf 228491d6c7cSMax Reitz if (!path && !host && !port) { 229491d6c7cSMax Reitz return true; 230491d6c7cSMax Reitz } 23103504d05SMax Reitz 232491d6c7cSMax Reitz for (e = qdict_first(output_options); e; e = qdict_next(output_options, e)) 233491d6c7cSMax Reitz { 234491d6c7cSMax Reitz if (strstart(e->key, "server.", NULL)) { 235491d6c7cSMax Reitz error_setg(errp, "Cannot use 'server' and path/host/port at the " 236491d6c7cSMax Reitz "same time"); 237491d6c7cSMax Reitz return false; 238491d6c7cSMax Reitz } 239491d6c7cSMax Reitz } 240491d6c7cSMax Reitz 241491d6c7cSMax Reitz if (path && host) { 24282d73014SMax Reitz error_setg(errp, "path and host may not be used at the same time"); 243491d6c7cSMax Reitz return false; 244491d6c7cSMax Reitz } else if (path) { 245491d6c7cSMax Reitz if (port) { 246442045cbSMax Reitz error_setg(errp, "port may not be used without host"); 247491d6c7cSMax Reitz return false; 248442045cbSMax Reitz } 249019d6b8fSAnthony Liguori 250491d6c7cSMax Reitz qdict_put(output_options, "server.type", qstring_from_str("unix")); 251491d6c7cSMax Reitz qdict_put(output_options, "server.data.path", qstring_from_str(path)); 252491d6c7cSMax Reitz } else if (host) { 253491d6c7cSMax Reitz qdict_put(output_options, "server.type", qstring_from_str("inet")); 254491d6c7cSMax Reitz qdict_put(output_options, "server.data.host", qstring_from_str(host)); 255491d6c7cSMax Reitz qdict_put(output_options, "server.data.port", 256491d6c7cSMax Reitz qstring_from_str(port ?: stringify(NBD_DEFAULT_PORT))); 2577a5ed437SDaniel P. Berrange } 258491d6c7cSMax Reitz 259491d6c7cSMax Reitz return true; 260491d6c7cSMax Reitz } 261491d6c7cSMax Reitz 262491d6c7cSMax Reitz static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, Error **errp) 263491d6c7cSMax Reitz { 264491d6c7cSMax Reitz SocketAddress *saddr = NULL; 265491d6c7cSMax Reitz QDict *addr = NULL; 266491d6c7cSMax Reitz QObject *crumpled_addr = NULL; 267491d6c7cSMax Reitz Visitor *iv = NULL; 268491d6c7cSMax Reitz Error *local_err = NULL; 269491d6c7cSMax Reitz 270491d6c7cSMax Reitz qdict_extract_subqdict(options, &addr, "server."); 271491d6c7cSMax Reitz if (!qdict_size(addr)) { 272491d6c7cSMax Reitz error_setg(errp, "NBD server address missing"); 273491d6c7cSMax Reitz goto done; 274491d6c7cSMax Reitz } 275491d6c7cSMax Reitz 276491d6c7cSMax Reitz crumpled_addr = qdict_crumple(addr, errp); 277491d6c7cSMax Reitz if (!crumpled_addr) { 278491d6c7cSMax Reitz goto done; 279491d6c7cSMax Reitz } 280491d6c7cSMax Reitz 281048abb7bSMarkus Armbruster iv = qobject_input_visitor_new(crumpled_addr); 282491d6c7cSMax Reitz visit_type_SocketAddress(iv, NULL, &saddr, &local_err); 283491d6c7cSMax Reitz if (local_err) { 284491d6c7cSMax Reitz error_propagate(errp, local_err); 285491d6c7cSMax Reitz goto done; 286f53a1febSKevin Wolf } 287f53a1febSKevin Wolf 288491d6c7cSMax Reitz done: 289491d6c7cSMax Reitz QDECREF(addr); 290491d6c7cSMax Reitz qobject_decref(crumpled_addr); 291491d6c7cSMax Reitz visit_free(iv); 2927a5ed437SDaniel P. Berrange return saddr; 293f53a1febSKevin Wolf } 294f53a1febSKevin Wolf 29510676b81SEric Blake NBDClientSession *nbd_get_client_session(BlockDriverState *bs) 296f53a829bSMax Reitz { 297f53a829bSMax Reitz BDRVNBDState *s = bs->opaque; 298f53a829bSMax Reitz return &s->client; 299f53a829bSMax Reitz } 300f53a829bSMax Reitz 301064097d9SDaniel P. Berrange static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, 3027a5ed437SDaniel P. Berrange Error **errp) 30333897dc7SNick Thomas { 304064097d9SDaniel P. Berrange QIOChannelSocket *sioc; 305064097d9SDaniel P. Berrange Error *local_err = NULL; 30633897dc7SNick Thomas 307064097d9SDaniel P. Berrange sioc = qio_channel_socket_new(); 3080d73f725SDaniel P. Berrange qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client"); 30933897dc7SNick Thomas 310064097d9SDaniel P. Berrange qio_channel_socket_connect_sync(sioc, 311064097d9SDaniel P. Berrange saddr, 312064097d9SDaniel P. Berrange &local_err); 313064097d9SDaniel P. Berrange if (local_err) { 314064097d9SDaniel P. Berrange error_propagate(errp, local_err); 315064097d9SDaniel P. Berrange return NULL; 31633897dc7SNick Thomas } 31733897dc7SNick Thomas 318064097d9SDaniel P. Berrange qio_channel_set_delay(QIO_CHANNEL(sioc), false); 3197a5ed437SDaniel P. Berrange 320064097d9SDaniel P. Berrange return sioc; 32133897dc7SNick Thomas } 32233897dc7SNick Thomas 32375822a12SDaniel P. Berrange 32475822a12SDaniel P. Berrange static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) 32575822a12SDaniel P. Berrange { 32675822a12SDaniel P. Berrange Object *obj; 32775822a12SDaniel P. Berrange QCryptoTLSCreds *creds; 32875822a12SDaniel P. Berrange 32975822a12SDaniel P. Berrange obj = object_resolve_path_component( 33075822a12SDaniel P. Berrange object_get_objects_root(), id); 33175822a12SDaniel P. Berrange if (!obj) { 33275822a12SDaniel P. Berrange error_setg(errp, "No TLS credentials with id '%s'", 33375822a12SDaniel P. Berrange id); 33475822a12SDaniel P. Berrange return NULL; 33575822a12SDaniel P. Berrange } 33675822a12SDaniel P. Berrange creds = (QCryptoTLSCreds *) 33775822a12SDaniel P. Berrange object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS); 33875822a12SDaniel P. Berrange if (!creds) { 33975822a12SDaniel P. Berrange error_setg(errp, "Object with id '%s' is not TLS credentials", 34075822a12SDaniel P. Berrange id); 34175822a12SDaniel P. Berrange return NULL; 34275822a12SDaniel P. Berrange } 34375822a12SDaniel P. Berrange 34475822a12SDaniel P. Berrange if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { 34575822a12SDaniel P. Berrange error_setg(errp, 34675822a12SDaniel P. Berrange "Expecting TLS credentials with a client endpoint"); 34775822a12SDaniel P. Berrange return NULL; 34875822a12SDaniel P. Berrange } 34975822a12SDaniel P. Berrange object_ref(obj); 35075822a12SDaniel P. Berrange return creds; 35175822a12SDaniel P. Berrange } 35275822a12SDaniel P. Berrange 35375822a12SDaniel P. Berrange 3547ccc44fdSMax Reitz static QemuOptsList nbd_runtime_opts = { 3557ccc44fdSMax Reitz .name = "nbd", 3567ccc44fdSMax Reitz .head = QTAILQ_HEAD_INITIALIZER(nbd_runtime_opts.head), 3577ccc44fdSMax Reitz .desc = { 3587ccc44fdSMax Reitz { 3597ccc44fdSMax Reitz .name = "host", 3607ccc44fdSMax Reitz .type = QEMU_OPT_STRING, 3617ccc44fdSMax Reitz .help = "TCP host to connect to", 3627ccc44fdSMax Reitz }, 3637ccc44fdSMax Reitz { 3647ccc44fdSMax Reitz .name = "port", 3657ccc44fdSMax Reitz .type = QEMU_OPT_STRING, 3667ccc44fdSMax Reitz .help = "TCP port to connect to", 3677ccc44fdSMax Reitz }, 3687ccc44fdSMax Reitz { 3697ccc44fdSMax Reitz .name = "path", 3707ccc44fdSMax Reitz .type = QEMU_OPT_STRING, 3717ccc44fdSMax Reitz .help = "Unix socket path to connect to", 3727ccc44fdSMax Reitz }, 3737ccc44fdSMax Reitz { 3747ccc44fdSMax Reitz .name = "export", 3757ccc44fdSMax Reitz .type = QEMU_OPT_STRING, 3767ccc44fdSMax Reitz .help = "Name of the NBD export to open", 3777ccc44fdSMax Reitz }, 3787ccc44fdSMax Reitz { 3797ccc44fdSMax Reitz .name = "tls-creds", 3807ccc44fdSMax Reitz .type = QEMU_OPT_STRING, 3817ccc44fdSMax Reitz .help = "ID of the TLS credentials to use", 3827ccc44fdSMax Reitz }, 3837ccc44fdSMax Reitz }, 3847ccc44fdSMax Reitz }; 3857ccc44fdSMax Reitz 386015a1036SMax Reitz static int nbd_open(BlockDriverState *bs, QDict *options, int flags, 387015a1036SMax Reitz Error **errp) 38833897dc7SNick Thomas { 38933897dc7SNick Thomas BDRVNBDState *s = bs->opaque; 3907ccc44fdSMax Reitz QemuOpts *opts = NULL; 3917ccc44fdSMax Reitz Error *local_err = NULL; 39275822a12SDaniel P. Berrange QIOChannelSocket *sioc = NULL; 39375822a12SDaniel P. Berrange QCryptoTLSCreds *tlscreds = NULL; 39475822a12SDaniel P. Berrange const char *hostname = NULL; 39575822a12SDaniel P. Berrange int ret = -EINVAL; 396ae255e52SPaolo Bonzini 3977ccc44fdSMax Reitz opts = qemu_opts_create(&nbd_runtime_opts, NULL, 0, &error_abort); 3987ccc44fdSMax Reitz qemu_opts_absorb_qdict(opts, options, &local_err); 3997ccc44fdSMax Reitz if (local_err) { 4007ccc44fdSMax Reitz error_propagate(errp, local_err); 4017ccc44fdSMax Reitz goto error; 4027ccc44fdSMax Reitz } 4037ccc44fdSMax Reitz 404491d6c7cSMax Reitz /* Translate @host, @port, and @path to a SocketAddress */ 405491d6c7cSMax Reitz if (!nbd_process_legacy_socket_options(options, opts, errp)) { 40675822a12SDaniel P. Berrange goto error; 40775822a12SDaniel P. Berrange } 40875822a12SDaniel P. Berrange 409491d6c7cSMax Reitz /* Pop the config into our state object. Exit if invalid. */ 410491d6c7cSMax Reitz s->saddr = nbd_config(s, options, errp); 411491d6c7cSMax Reitz if (!s->saddr) { 412491d6c7cSMax Reitz goto error; 413491d6c7cSMax Reitz } 414491d6c7cSMax Reitz 415491d6c7cSMax Reitz s->export = g_strdup(qemu_opt_get(opts, "export")); 416491d6c7cSMax Reitz 41703504d05SMax Reitz s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds")); 41803504d05SMax Reitz if (s->tlscredsid) { 41903504d05SMax Reitz tlscreds = nbd_get_tls_creds(s->tlscredsid, errp); 42075822a12SDaniel P. Berrange if (!tlscreds) { 42175822a12SDaniel P. Berrange goto error; 42275822a12SDaniel P. Berrange } 42375822a12SDaniel P. Berrange 424*ca0b64e5SMarkus Armbruster /* TODO SOCKET_ADDRESS_KIND_FD where fd has AF_INET or AF_INET6 */ 425491d6c7cSMax Reitz if (s->saddr->type != SOCKET_ADDRESS_KIND_INET) { 42675822a12SDaniel P. Berrange error_setg(errp, "TLS only supported over IP sockets"); 42775822a12SDaniel P. Berrange goto error; 42875822a12SDaniel P. Berrange } 429491d6c7cSMax Reitz hostname = s->saddr->u.inet.data->host; 43033897dc7SNick Thomas } 43133897dc7SNick Thomas 43233897dc7SNick Thomas /* establish TCP connection, return error if it fails 43333897dc7SNick Thomas * TODO: Configurable retry-until-timeout behaviour. 43433897dc7SNick Thomas */ 435491d6c7cSMax Reitz sioc = nbd_establish_connection(s->saddr, errp); 436064097d9SDaniel P. Berrange if (!sioc) { 43775822a12SDaniel P. Berrange ret = -ECONNREFUSED; 43875822a12SDaniel P. Berrange goto error; 43933897dc7SNick Thomas } 44033897dc7SNick Thomas 4412302c1caSMarc-André Lureau /* NBD handshake */ 44203504d05SMax Reitz ret = nbd_client_init(bs, sioc, s->export, 44375822a12SDaniel P. Berrange tlscreds, hostname, errp); 44475822a12SDaniel P. Berrange error: 44575822a12SDaniel P. Berrange if (sioc) { 446064097d9SDaniel P. Berrange object_unref(OBJECT(sioc)); 44775822a12SDaniel P. Berrange } 44875822a12SDaniel P. Berrange if (tlscreds) { 44975822a12SDaniel P. Berrange object_unref(OBJECT(tlscreds)); 45075822a12SDaniel P. Berrange } 45103504d05SMax Reitz if (ret < 0) { 452491d6c7cSMax Reitz qapi_free_SocketAddress(s->saddr); 45303504d05SMax Reitz g_free(s->export); 45403504d05SMax Reitz g_free(s->tlscredsid); 45503504d05SMax Reitz } 4567ccc44fdSMax Reitz qemu_opts_del(opts); 45775822a12SDaniel P. Berrange return ret; 458ae255e52SPaolo Bonzini } 459d9b09f13SPaolo Bonzini 4601486d04aSPaolo Bonzini static int nbd_co_flush(BlockDriverState *bs) 4611486d04aSPaolo Bonzini { 462f53a829bSMax Reitz return nbd_client_co_flush(bs); 4631486d04aSPaolo Bonzini } 4641486d04aSPaolo Bonzini 465fa21e6faSDenis V. Lunev static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) 466fa21e6faSDenis V. Lunev { 467b9f7855aSEric Blake bs->bl.max_pdiscard = NBD_MAX_BUFFER_SIZE; 468fa778fffSEric Blake bs->bl.max_pwrite_zeroes = NBD_MAX_BUFFER_SIZE; 4695def6b80SEric Blake bs->bl.max_transfer = NBD_MAX_BUFFER_SIZE; 470fa21e6faSDenis V. Lunev } 471fa21e6faSDenis V. Lunev 472019d6b8fSAnthony Liguori static void nbd_close(BlockDriverState *bs) 473019d6b8fSAnthony Liguori { 47403504d05SMax Reitz BDRVNBDState *s = bs->opaque; 47503504d05SMax Reitz 476f53a829bSMax Reitz nbd_client_close(bs); 47703504d05SMax Reitz 478491d6c7cSMax Reitz qapi_free_SocketAddress(s->saddr); 47903504d05SMax Reitz g_free(s->export); 48003504d05SMax Reitz g_free(s->tlscredsid); 481019d6b8fSAnthony Liguori } 482019d6b8fSAnthony Liguori 483019d6b8fSAnthony Liguori static int64_t nbd_getlength(BlockDriverState *bs) 484019d6b8fSAnthony Liguori { 485019d6b8fSAnthony Liguori BDRVNBDState *s = bs->opaque; 486019d6b8fSAnthony Liguori 4872302c1caSMarc-André Lureau return s->client.size; 488019d6b8fSAnthony Liguori } 489019d6b8fSAnthony Liguori 49069447cd8SStefan Hajnoczi static void nbd_detach_aio_context(BlockDriverState *bs) 49169447cd8SStefan Hajnoczi { 492f53a829bSMax Reitz nbd_client_detach_aio_context(bs); 49369447cd8SStefan Hajnoczi } 49469447cd8SStefan Hajnoczi 49569447cd8SStefan Hajnoczi static void nbd_attach_aio_context(BlockDriverState *bs, 49669447cd8SStefan Hajnoczi AioContext *new_context) 49769447cd8SStefan Hajnoczi { 498f53a829bSMax Reitz nbd_client_attach_aio_context(bs, new_context); 49969447cd8SStefan Hajnoczi } 50069447cd8SStefan Hajnoczi 5014cdd01d3SKevin Wolf static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) 5022019d68bSMax Reitz { 50303504d05SMax Reitz BDRVNBDState *s = bs->opaque; 5042019d68bSMax Reitz QDict *opts = qdict_new(); 505491d6c7cSMax Reitz QObject *saddr_qdict; 506491d6c7cSMax Reitz Visitor *ov; 507491d6c7cSMax Reitz const char *host = NULL, *port = NULL, *path = NULL; 508491d6c7cSMax Reitz 509491d6c7cSMax Reitz if (s->saddr->type == SOCKET_ADDRESS_KIND_INET) { 510491d6c7cSMax Reitz const InetSocketAddress *inet = s->saddr->u.inet.data; 511491d6c7cSMax Reitz if (!inet->has_ipv4 && !inet->has_ipv6 && !inet->has_to) { 512491d6c7cSMax Reitz host = inet->host; 513491d6c7cSMax Reitz port = inet->port; 514491d6c7cSMax Reitz } 515491d6c7cSMax Reitz } else if (s->saddr->type == SOCKET_ADDRESS_KIND_UNIX) { 516491d6c7cSMax Reitz path = s->saddr->u.q_unix.data->path; 517491d6c7cSMax Reitz } 5182019d68bSMax Reitz 519fcfcd8ffSMax Reitz qdict_put(opts, "driver", qstring_from_str("nbd")); 5202019d68bSMax Reitz 521491d6c7cSMax Reitz if (path && s->export) { 522ec0de768SMax Reitz snprintf(bs->exact_filename, sizeof(bs->exact_filename), 523491d6c7cSMax Reitz "nbd+unix:///%s?socket=%s", s->export, path); 524491d6c7cSMax Reitz } else if (path && !s->export) { 525ec0de768SMax Reitz snprintf(bs->exact_filename, sizeof(bs->exact_filename), 526491d6c7cSMax Reitz "nbd+unix://?socket=%s", path); 527491d6c7cSMax Reitz } else if (host && s->export) { 528ec0de768SMax Reitz snprintf(bs->exact_filename, sizeof(bs->exact_filename), 529491d6c7cSMax Reitz "nbd://%s:%s/%s", host, port, s->export); 530491d6c7cSMax Reitz } else if (host && !s->export) { 531ec0de768SMax Reitz snprintf(bs->exact_filename, sizeof(bs->exact_filename), 532491d6c7cSMax Reitz "nbd://%s:%s", host, port); 533ec0de768SMax Reitz } 534ec0de768SMax Reitz 535491d6c7cSMax Reitz ov = qobject_output_visitor_new(&saddr_qdict); 536491d6c7cSMax Reitz visit_type_SocketAddress(ov, NULL, &s->saddr, &error_abort); 537491d6c7cSMax Reitz visit_complete(ov, &saddr_qdict); 538a1d4e38aSAshijeet Acharya visit_free(ov); 539491d6c7cSMax Reitz qdict_put_obj(opts, "server", saddr_qdict); 540491d6c7cSMax Reitz 54103504d05SMax Reitz if (s->export) { 542fcfcd8ffSMax Reitz qdict_put(opts, "export", qstring_from_str(s->export)); 5432019d68bSMax Reitz } 54403504d05SMax Reitz if (s->tlscredsid) { 545fcfcd8ffSMax Reitz qdict_put(opts, "tls-creds", qstring_from_str(s->tlscredsid)); 54675822a12SDaniel P. Berrange } 5472019d68bSMax Reitz 548491d6c7cSMax Reitz qdict_flatten(opts); 5492019d68bSMax Reitz bs->full_open_options = opts; 5502019d68bSMax Reitz } 5512019d68bSMax Reitz 552019d6b8fSAnthony Liguori static BlockDriver bdrv_nbd = { 553019d6b8fSAnthony Liguori .format_name = "nbd", 5541d7d2a9dSPaolo Bonzini .protocol_name = "nbd", 555019d6b8fSAnthony Liguori .instance_size = sizeof(BDRVNBDState), 5566963a30dSKevin Wolf .bdrv_parse_filename = nbd_parse_filename, 55766f82ceeSKevin Wolf .bdrv_file_open = nbd_open, 55870c4fb26SEric Blake .bdrv_co_preadv = nbd_client_co_preadv, 55970c4fb26SEric Blake .bdrv_co_pwritev = nbd_client_co_pwritev, 560fa778fffSEric Blake .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, 561019d6b8fSAnthony Liguori .bdrv_close = nbd_close, 5621486d04aSPaolo Bonzini .bdrv_co_flush_to_os = nbd_co_flush, 563447e57c3SEric Blake .bdrv_co_pdiscard = nbd_client_co_pdiscard, 564fa21e6faSDenis V. Lunev .bdrv_refresh_limits = nbd_refresh_limits, 565019d6b8fSAnthony Liguori .bdrv_getlength = nbd_getlength, 56669447cd8SStefan Hajnoczi .bdrv_detach_aio_context = nbd_detach_aio_context, 56769447cd8SStefan Hajnoczi .bdrv_attach_aio_context = nbd_attach_aio_context, 5682019d68bSMax Reitz .bdrv_refresh_filename = nbd_refresh_filename, 5691d7d2a9dSPaolo Bonzini }; 5701d7d2a9dSPaolo Bonzini 5711d7d2a9dSPaolo Bonzini static BlockDriver bdrv_nbd_tcp = { 5721d7d2a9dSPaolo Bonzini .format_name = "nbd", 5731d7d2a9dSPaolo Bonzini .protocol_name = "nbd+tcp", 5741d7d2a9dSPaolo Bonzini .instance_size = sizeof(BDRVNBDState), 5756963a30dSKevin Wolf .bdrv_parse_filename = nbd_parse_filename, 5761d7d2a9dSPaolo Bonzini .bdrv_file_open = nbd_open, 57770c4fb26SEric Blake .bdrv_co_preadv = nbd_client_co_preadv, 57870c4fb26SEric Blake .bdrv_co_pwritev = nbd_client_co_pwritev, 579fa778fffSEric Blake .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, 5801d7d2a9dSPaolo Bonzini .bdrv_close = nbd_close, 5811d7d2a9dSPaolo Bonzini .bdrv_co_flush_to_os = nbd_co_flush, 582447e57c3SEric Blake .bdrv_co_pdiscard = nbd_client_co_pdiscard, 583fa21e6faSDenis V. Lunev .bdrv_refresh_limits = nbd_refresh_limits, 5841d7d2a9dSPaolo Bonzini .bdrv_getlength = nbd_getlength, 58569447cd8SStefan Hajnoczi .bdrv_detach_aio_context = nbd_detach_aio_context, 58669447cd8SStefan Hajnoczi .bdrv_attach_aio_context = nbd_attach_aio_context, 5872019d68bSMax Reitz .bdrv_refresh_filename = nbd_refresh_filename, 5881d7d2a9dSPaolo Bonzini }; 5891d7d2a9dSPaolo Bonzini 5901d7d2a9dSPaolo Bonzini static BlockDriver bdrv_nbd_unix = { 5911d7d2a9dSPaolo Bonzini .format_name = "nbd", 5921d7d2a9dSPaolo Bonzini .protocol_name = "nbd+unix", 5931d7d2a9dSPaolo Bonzini .instance_size = sizeof(BDRVNBDState), 5946963a30dSKevin Wolf .bdrv_parse_filename = nbd_parse_filename, 5951d7d2a9dSPaolo Bonzini .bdrv_file_open = nbd_open, 59670c4fb26SEric Blake .bdrv_co_preadv = nbd_client_co_preadv, 59770c4fb26SEric Blake .bdrv_co_pwritev = nbd_client_co_pwritev, 598fa778fffSEric Blake .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, 5991d7d2a9dSPaolo Bonzini .bdrv_close = nbd_close, 6001d7d2a9dSPaolo Bonzini .bdrv_co_flush_to_os = nbd_co_flush, 601447e57c3SEric Blake .bdrv_co_pdiscard = nbd_client_co_pdiscard, 602fa21e6faSDenis V. Lunev .bdrv_refresh_limits = nbd_refresh_limits, 6031d7d2a9dSPaolo Bonzini .bdrv_getlength = nbd_getlength, 60469447cd8SStefan Hajnoczi .bdrv_detach_aio_context = nbd_detach_aio_context, 60569447cd8SStefan Hajnoczi .bdrv_attach_aio_context = nbd_attach_aio_context, 6062019d68bSMax Reitz .bdrv_refresh_filename = nbd_refresh_filename, 607019d6b8fSAnthony Liguori }; 608019d6b8fSAnthony Liguori 609019d6b8fSAnthony Liguori static void bdrv_nbd_init(void) 610019d6b8fSAnthony Liguori { 611019d6b8fSAnthony Liguori bdrv_register(&bdrv_nbd); 6121d7d2a9dSPaolo Bonzini bdrv_register(&bdrv_nbd_tcp); 6131d7d2a9dSPaolo Bonzini bdrv_register(&bdrv_nbd_unix); 614019d6b8fSAnthony Liguori } 615019d6b8fSAnthony Liguori 616019d6b8fSAnthony Liguori block_init(bdrv_nbd_init); 617