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" 300d8c41daSMichael S. Tsirkin #include "nbd-client.h" 31609f45eaSMax Reitz #include "block/qdict.h" 32da34e65cSMarkus Armbruster #include "qapi/error.h" 331de7afc9SPaolo Bonzini #include "qemu/uri.h" 34737e150eSPaolo Bonzini #include "block/block_int.h" 351de7afc9SPaolo Bonzini #include "qemu/module.h" 36922a01a0SMarkus Armbruster #include "qemu/option.h" 379af23989SMarkus Armbruster #include "qapi/qapi-visit-sockets.h" 38491d6c7cSMax Reitz #include "qapi/qobject-input-visitor.h" 39491d6c7cSMax Reitz #include "qapi/qobject-output-visitor.h" 402019d68bSMax Reitz #include "qapi/qmp/qdict.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() */ 5062cf396bSMarkus Armbruster 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 */ 68f69165a8SMax Reitz if (!g_strcmp0(uri->scheme, "nbd")) { 69f53a1febSKevin Wolf is_unix = false; 70f69165a8SMax Reitz } else if (!g_strcmp0(uri->scheme, "nbd+tcp")) { 71f53a1febSKevin Wolf is_unix = false; 72f69165a8SMax Reitz } else if (!g_strcmp0(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]) { 8246f5ac20SEric Blake qdict_put_str(options, "export", 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 } 9746f5ac20SEric Blake qdict_put_str(options, "server.type", "unix"); 9846f5ac20SEric Blake qdict_put_str(options, "server.path", qp->p[0].value); 991d7d2a9dSPaolo Bonzini } else { 10023307908SJán Tomko QString *host; 101f84d431bSMax Reitz char *port_str; 102f84d431bSMax Reitz 103bebbf7faSKevin Wolf /* nbd[+tcp]://host[:port]/export */ 1041d7d2a9dSPaolo Bonzini if (!uri->server) { 1051d7d2a9dSPaolo Bonzini ret = -EINVAL; 1061d7d2a9dSPaolo Bonzini goto out; 1071d7d2a9dSPaolo Bonzini } 108f17c90beSKevin Wolf 10923307908SJán Tomko /* strip braces from literal IPv6 address */ 11023307908SJán Tomko if (uri->server[0] == '[') { 11123307908SJán Tomko host = qstring_from_substr(uri->server, 1, 112ba891d68SMarkus Armbruster strlen(uri->server) - 1); 11323307908SJán Tomko } else { 11423307908SJán Tomko host = qstring_from_str(uri->server); 11523307908SJán Tomko } 11623307908SJán Tomko 11746f5ac20SEric Blake qdict_put_str(options, "server.type", "inet"); 1189445673eSMarkus Armbruster qdict_put(options, "server.host", host); 119f84d431bSMax Reitz 120f84d431bSMax Reitz port_str = g_strdup_printf("%d", uri->port ?: NBD_DEFAULT_PORT); 12146f5ac20SEric Blake qdict_put_str(options, "server.port", port_str); 122f53a1febSKevin Wolf g_free(port_str); 1231d7d2a9dSPaolo Bonzini } 1241d7d2a9dSPaolo Bonzini 1251d7d2a9dSPaolo Bonzini out: 1261d7d2a9dSPaolo Bonzini if (qp) { 1271d7d2a9dSPaolo Bonzini query_params_free(qp); 1281d7d2a9dSPaolo Bonzini } 1291d7d2a9dSPaolo Bonzini uri_free(uri); 1301d7d2a9dSPaolo Bonzini return ret; 1311d7d2a9dSPaolo Bonzini } 1321d7d2a9dSPaolo Bonzini 13348c38e0bSMax Reitz static bool nbd_has_filename_options_conflict(QDict *options, Error **errp) 13448c38e0bSMax Reitz { 13548c38e0bSMax Reitz const QDictEntry *e; 13648c38e0bSMax Reitz 13748c38e0bSMax Reitz for (e = qdict_first(options); e; e = qdict_next(options, e)) { 13848c38e0bSMax Reitz if (!strcmp(e->key, "host") || 13948c38e0bSMax Reitz !strcmp(e->key, "port") || 14048c38e0bSMax Reitz !strcmp(e->key, "path") || 141491d6c7cSMax Reitz !strcmp(e->key, "export") || 142491d6c7cSMax Reitz strstart(e->key, "server.", NULL)) 14348c38e0bSMax Reitz { 14448c38e0bSMax Reitz error_setg(errp, "Option '%s' cannot be used with a file name", 14548c38e0bSMax Reitz e->key); 14648c38e0bSMax Reitz return true; 14748c38e0bSMax Reitz } 14848c38e0bSMax Reitz } 14948c38e0bSMax Reitz 15048c38e0bSMax Reitz return false; 15148c38e0bSMax Reitz } 15248c38e0bSMax Reitz 1536963a30dSKevin Wolf static void nbd_parse_filename(const char *filename, QDict *options, 1546963a30dSKevin Wolf Error **errp) 155019d6b8fSAnthony Liguori { 1561d45f8b5SLaurent Vivier char *file; 15733897dc7SNick Thomas char *export_name; 15833897dc7SNick Thomas const char *host_spec; 159019d6b8fSAnthony Liguori const char *unixpath; 160019d6b8fSAnthony Liguori 16148c38e0bSMax Reitz if (nbd_has_filename_options_conflict(options, errp)) { 162681e7ad0SKevin Wolf return; 163681e7ad0SKevin Wolf } 164681e7ad0SKevin Wolf 1651d7d2a9dSPaolo Bonzini if (strstr(filename, "://")) { 1666963a30dSKevin Wolf int ret = nbd_parse_uri(filename, options); 1676963a30dSKevin Wolf if (ret < 0) { 1686963a30dSKevin Wolf error_setg(errp, "No valid URL specified"); 1696963a30dSKevin Wolf } 1706963a30dSKevin Wolf return; 1711d7d2a9dSPaolo Bonzini } 1721d7d2a9dSPaolo Bonzini 1737267c094SAnthony Liguori file = g_strdup(filename); 1741d45f8b5SLaurent Vivier 17533897dc7SNick Thomas export_name = strstr(file, EN_OPTSTR); 17633897dc7SNick Thomas if (export_name) { 17733897dc7SNick Thomas if (export_name[strlen(EN_OPTSTR)] == 0) { 1781d45f8b5SLaurent Vivier goto out; 1791d45f8b5SLaurent Vivier } 18033897dc7SNick Thomas export_name[0] = 0; /* truncate 'file' */ 18133897dc7SNick Thomas export_name += strlen(EN_OPTSTR); 182f53a1febSKevin Wolf 18346f5ac20SEric Blake qdict_put_str(options, "export", export_name); 1841d45f8b5SLaurent Vivier } 1851d45f8b5SLaurent Vivier 18633897dc7SNick Thomas /* extract the host_spec - fail if it's not nbd:... */ 18733897dc7SNick Thomas if (!strstart(file, "nbd:", &host_spec)) { 1886963a30dSKevin Wolf error_setg(errp, "File name string for NBD must start with 'nbd:'"); 1891d45f8b5SLaurent Vivier goto out; 1901d45f8b5SLaurent Vivier } 191019d6b8fSAnthony Liguori 192f53a1febSKevin Wolf if (!*host_spec) { 193f53a1febSKevin Wolf goto out; 194f53a1febSKevin Wolf } 195f53a1febSKevin Wolf 19633897dc7SNick Thomas /* are we a UNIX or TCP socket? */ 19733897dc7SNick Thomas if (strstart(host_spec, "unix:", &unixpath)) { 19846f5ac20SEric Blake qdict_put_str(options, "server.type", "unix"); 19946f5ac20SEric Blake qdict_put_str(options, "server.path", unixpath); 200019d6b8fSAnthony Liguori } else { 2010785bd7aSMarkus Armbruster InetSocketAddress *addr = g_new(InetSocketAddress, 1); 202f53a1febSKevin Wolf 2030785bd7aSMarkus Armbruster if (inet_parse(addr, host_spec, errp)) { 2040785bd7aSMarkus Armbruster goto out_inet; 205f17c90beSKevin Wolf } 206f53a1febSKevin Wolf 20746f5ac20SEric Blake qdict_put_str(options, "server.type", "inet"); 20846f5ac20SEric Blake qdict_put_str(options, "server.host", addr->host); 20946f5ac20SEric Blake qdict_put_str(options, "server.port", addr->port); 2100785bd7aSMarkus Armbruster out_inet: 211f53a1febSKevin Wolf qapi_free_InetSocketAddress(addr); 2121d45f8b5SLaurent Vivier } 2131d45f8b5SLaurent Vivier 2141d45f8b5SLaurent Vivier out: 2157267c094SAnthony Liguori g_free(file); 21633897dc7SNick Thomas } 217f53a1febSKevin Wolf 218491d6c7cSMax Reitz static bool nbd_process_legacy_socket_options(QDict *output_options, 219491d6c7cSMax Reitz QemuOpts *legacy_opts, 220491d6c7cSMax Reitz Error **errp) 221f53a1febSKevin Wolf { 222491d6c7cSMax Reitz const char *path = qemu_opt_get(legacy_opts, "path"); 223491d6c7cSMax Reitz const char *host = qemu_opt_get(legacy_opts, "host"); 224491d6c7cSMax Reitz const char *port = qemu_opt_get(legacy_opts, "port"); 225491d6c7cSMax Reitz const QDictEntry *e; 226f53a1febSKevin Wolf 227491d6c7cSMax Reitz if (!path && !host && !port) { 228491d6c7cSMax Reitz return true; 229491d6c7cSMax Reitz } 23003504d05SMax Reitz 231491d6c7cSMax Reitz for (e = qdict_first(output_options); e; e = qdict_next(output_options, e)) 232491d6c7cSMax Reitz { 233491d6c7cSMax Reitz if (strstart(e->key, "server.", NULL)) { 234491d6c7cSMax Reitz error_setg(errp, "Cannot use 'server' and path/host/port at the " 235491d6c7cSMax Reitz "same time"); 236491d6c7cSMax Reitz return false; 237491d6c7cSMax Reitz } 238491d6c7cSMax Reitz } 239491d6c7cSMax Reitz 240491d6c7cSMax Reitz if (path && host) { 24182d73014SMax Reitz error_setg(errp, "path and host may not be used at the same time"); 242491d6c7cSMax Reitz return false; 243491d6c7cSMax Reitz } else if (path) { 244491d6c7cSMax Reitz if (port) { 245442045cbSMax Reitz error_setg(errp, "port may not be used without host"); 246491d6c7cSMax Reitz return false; 247442045cbSMax Reitz } 248019d6b8fSAnthony Liguori 24946f5ac20SEric Blake qdict_put_str(output_options, "server.type", "unix"); 25046f5ac20SEric Blake qdict_put_str(output_options, "server.path", path); 251491d6c7cSMax Reitz } else if (host) { 25246f5ac20SEric Blake qdict_put_str(output_options, "server.type", "inet"); 25346f5ac20SEric Blake qdict_put_str(output_options, "server.host", host); 25446f5ac20SEric Blake qdict_put_str(output_options, "server.port", 25546f5ac20SEric Blake port ?: stringify(NBD_DEFAULT_PORT)); 2567a5ed437SDaniel P. Berrange } 257491d6c7cSMax Reitz 258491d6c7cSMax Reitz return true; 259491d6c7cSMax Reitz } 260491d6c7cSMax Reitz 26162cf396bSMarkus Armbruster static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, 2629445673eSMarkus Armbruster Error **errp) 263491d6c7cSMax Reitz { 26462cf396bSMarkus Armbruster SocketAddress *saddr = NULL; 265491d6c7cSMax Reitz QDict *addr = NULL; 266491d6c7cSMax Reitz Visitor *iv = NULL; 267491d6c7cSMax Reitz Error *local_err = NULL; 268491d6c7cSMax Reitz 269491d6c7cSMax Reitz qdict_extract_subqdict(options, &addr, "server."); 270491d6c7cSMax Reitz if (!qdict_size(addr)) { 271491d6c7cSMax Reitz error_setg(errp, "NBD server address missing"); 272491d6c7cSMax Reitz goto done; 273491d6c7cSMax Reitz } 274491d6c7cSMax Reitz 275af91062eSMarkus Armbruster iv = qobject_input_visitor_new_flat_confused(addr, errp); 276af91062eSMarkus Armbruster if (!iv) { 277491d6c7cSMax Reitz goto done; 278491d6c7cSMax Reitz } 279491d6c7cSMax Reitz 28062cf396bSMarkus Armbruster visit_type_SocketAddress(iv, NULL, &saddr, &local_err); 281491d6c7cSMax Reitz if (local_err) { 282491d6c7cSMax Reitz error_propagate(errp, local_err); 283491d6c7cSMax Reitz goto done; 284f53a1febSKevin Wolf } 285f53a1febSKevin Wolf 286491d6c7cSMax Reitz done: 287cb3e7f08SMarc-André Lureau qobject_unref(addr); 288491d6c7cSMax Reitz visit_free(iv); 2897a5ed437SDaniel P. Berrange return saddr; 290f53a1febSKevin Wolf } 291f53a1febSKevin Wolf 29210676b81SEric Blake NBDClientSession *nbd_get_client_session(BlockDriverState *bs) 293f53a829bSMax Reitz { 294f53a829bSMax Reitz BDRVNBDState *s = bs->opaque; 295f53a829bSMax Reitz return &s->client; 296f53a829bSMax Reitz } 297f53a829bSMax Reitz 29875822a12SDaniel P. Berrange static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) 29975822a12SDaniel P. Berrange { 30075822a12SDaniel P. Berrange Object *obj; 30175822a12SDaniel P. Berrange QCryptoTLSCreds *creds; 30275822a12SDaniel P. Berrange 30375822a12SDaniel P. Berrange obj = object_resolve_path_component( 30475822a12SDaniel P. Berrange object_get_objects_root(), id); 30575822a12SDaniel P. Berrange if (!obj) { 30675822a12SDaniel P. Berrange error_setg(errp, "No TLS credentials with id '%s'", 30775822a12SDaniel P. Berrange id); 30875822a12SDaniel P. Berrange return NULL; 30975822a12SDaniel P. Berrange } 31075822a12SDaniel P. Berrange creds = (QCryptoTLSCreds *) 31175822a12SDaniel P. Berrange object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS); 31275822a12SDaniel P. Berrange if (!creds) { 31375822a12SDaniel P. Berrange error_setg(errp, "Object with id '%s' is not TLS credentials", 31475822a12SDaniel P. Berrange id); 31575822a12SDaniel P. Berrange return NULL; 31675822a12SDaniel P. Berrange } 31775822a12SDaniel P. Berrange 31875822a12SDaniel P. Berrange if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { 31975822a12SDaniel P. Berrange error_setg(errp, 32075822a12SDaniel P. Berrange "Expecting TLS credentials with a client endpoint"); 32175822a12SDaniel P. Berrange return NULL; 32275822a12SDaniel P. Berrange } 32375822a12SDaniel P. Berrange object_ref(obj); 32475822a12SDaniel P. Berrange return creds; 32575822a12SDaniel P. Berrange } 32675822a12SDaniel P. Berrange 32775822a12SDaniel P. Berrange 3287ccc44fdSMax Reitz static QemuOptsList nbd_runtime_opts = { 3297ccc44fdSMax Reitz .name = "nbd", 3307ccc44fdSMax Reitz .head = QTAILQ_HEAD_INITIALIZER(nbd_runtime_opts.head), 3317ccc44fdSMax Reitz .desc = { 3327ccc44fdSMax Reitz { 3337ccc44fdSMax Reitz .name = "host", 3347ccc44fdSMax Reitz .type = QEMU_OPT_STRING, 3357ccc44fdSMax Reitz .help = "TCP host to connect to", 3367ccc44fdSMax Reitz }, 3377ccc44fdSMax Reitz { 3387ccc44fdSMax Reitz .name = "port", 3397ccc44fdSMax Reitz .type = QEMU_OPT_STRING, 3407ccc44fdSMax Reitz .help = "TCP port to connect to", 3417ccc44fdSMax Reitz }, 3427ccc44fdSMax Reitz { 3437ccc44fdSMax Reitz .name = "path", 3447ccc44fdSMax Reitz .type = QEMU_OPT_STRING, 3457ccc44fdSMax Reitz .help = "Unix socket path to connect to", 3467ccc44fdSMax Reitz }, 3477ccc44fdSMax Reitz { 3487ccc44fdSMax Reitz .name = "export", 3497ccc44fdSMax Reitz .type = QEMU_OPT_STRING, 3507ccc44fdSMax Reitz .help = "Name of the NBD export to open", 3517ccc44fdSMax Reitz }, 3527ccc44fdSMax Reitz { 3537ccc44fdSMax Reitz .name = "tls-creds", 3547ccc44fdSMax Reitz .type = QEMU_OPT_STRING, 3557ccc44fdSMax Reitz .help = "ID of the TLS credentials to use", 3567ccc44fdSMax Reitz }, 357216ee365SEric Blake { 358216ee365SEric Blake .name = "x-dirty-bitmap", 359216ee365SEric Blake .type = QEMU_OPT_STRING, 360216ee365SEric Blake .help = "experimental: expose named dirty bitmap in place of " 361216ee365SEric Blake "block status", 362216ee365SEric Blake }, 363c4365735SMurilo Opsfelder Araujo { /* end of list */ } 3647ccc44fdSMax Reitz }, 3657ccc44fdSMax Reitz }; 3667ccc44fdSMax Reitz 367015a1036SMax Reitz static int nbd_open(BlockDriverState *bs, QDict *options, int flags, 368015a1036SMax Reitz Error **errp) 36933897dc7SNick Thomas { 37033897dc7SNick Thomas BDRVNBDState *s = bs->opaque; 3717ccc44fdSMax Reitz QemuOpts *opts = NULL; 3727ccc44fdSMax Reitz Error *local_err = NULL; 37375822a12SDaniel P. Berrange QCryptoTLSCreds *tlscreds = NULL; 37475822a12SDaniel P. Berrange const char *hostname = NULL; 37575822a12SDaniel P. Berrange int ret = -EINVAL; 376ae255e52SPaolo Bonzini 3777ccc44fdSMax Reitz opts = qemu_opts_create(&nbd_runtime_opts, NULL, 0, &error_abort); 3787ccc44fdSMax Reitz qemu_opts_absorb_qdict(opts, options, &local_err); 3797ccc44fdSMax Reitz if (local_err) { 3807ccc44fdSMax Reitz error_propagate(errp, local_err); 3817ccc44fdSMax Reitz goto error; 3827ccc44fdSMax Reitz } 3837ccc44fdSMax Reitz 38462cf396bSMarkus Armbruster /* Translate @host, @port, and @path to a SocketAddress */ 385491d6c7cSMax Reitz if (!nbd_process_legacy_socket_options(options, opts, errp)) { 38675822a12SDaniel P. Berrange goto error; 38775822a12SDaniel P. Berrange } 38875822a12SDaniel P. Berrange 389491d6c7cSMax Reitz /* Pop the config into our state object. Exit if invalid. */ 390491d6c7cSMax Reitz s->saddr = nbd_config(s, options, errp); 391491d6c7cSMax Reitz if (!s->saddr) { 392491d6c7cSMax Reitz goto error; 393491d6c7cSMax Reitz } 394491d6c7cSMax Reitz 395491d6c7cSMax Reitz s->export = g_strdup(qemu_opt_get(opts, "export")); 396491d6c7cSMax Reitz 39703504d05SMax Reitz s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds")); 39803504d05SMax Reitz if (s->tlscredsid) { 39903504d05SMax Reitz tlscreds = nbd_get_tls_creds(s->tlscredsid, errp); 40075822a12SDaniel P. Berrange if (!tlscreds) { 40175822a12SDaniel P. Berrange goto error; 40275822a12SDaniel P. Berrange } 40375822a12SDaniel P. Berrange 404ca0b64e5SMarkus Armbruster /* TODO SOCKET_ADDRESS_KIND_FD where fd has AF_INET or AF_INET6 */ 40562cf396bSMarkus Armbruster if (s->saddr->type != SOCKET_ADDRESS_TYPE_INET) { 40675822a12SDaniel P. Berrange error_setg(errp, "TLS only supported over IP sockets"); 40775822a12SDaniel P. Berrange goto error; 40875822a12SDaniel P. Berrange } 4099445673eSMarkus Armbruster hostname = s->saddr->u.inet.host; 41033897dc7SNick Thomas } 41133897dc7SNick Thomas 4122302c1caSMarc-André Lureau /* NBD handshake */ 413d42f78e9SVladimir Sementsov-Ogievskiy ret = nbd_client_init(bs, s->saddr, s->export, tlscreds, hostname, 414216ee365SEric Blake qemu_opt_get(opts, "x-dirty-bitmap"), errp); 415d42f78e9SVladimir Sementsov-Ogievskiy 41675822a12SDaniel P. Berrange error: 41775822a12SDaniel P. Berrange if (tlscreds) { 41875822a12SDaniel P. Berrange object_unref(OBJECT(tlscreds)); 41975822a12SDaniel P. Berrange } 42003504d05SMax Reitz if (ret < 0) { 42162cf396bSMarkus Armbruster qapi_free_SocketAddress(s->saddr); 42203504d05SMax Reitz g_free(s->export); 42303504d05SMax Reitz g_free(s->tlscredsid); 42403504d05SMax Reitz } 4257ccc44fdSMax Reitz qemu_opts_del(opts); 42675822a12SDaniel P. Berrange return ret; 427ae255e52SPaolo Bonzini } 428d9b09f13SPaolo Bonzini 4291486d04aSPaolo Bonzini static int nbd_co_flush(BlockDriverState *bs) 4301486d04aSPaolo Bonzini { 431f53a829bSMax Reitz return nbd_client_co_flush(bs); 4321486d04aSPaolo Bonzini } 4331486d04aSPaolo Bonzini 434fa21e6faSDenis V. Lunev static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) 435fa21e6faSDenis V. Lunev { 436081dd1feSEric Blake NBDClientSession *s = nbd_get_client_session(bs); 437fd8d372dSEric Blake uint32_t min = s->info.min_block; 438081dd1feSEric Blake uint32_t max = MIN_NON_ZERO(NBD_MAX_BUFFER_SIZE, s->info.max_block); 439081dd1feSEric Blake 440fd8d372dSEric Blake bs->bl.request_alignment = min ? min : BDRV_SECTOR_SIZE; 441081dd1feSEric Blake bs->bl.max_pdiscard = max; 442081dd1feSEric Blake bs->bl.max_pwrite_zeroes = max; 443081dd1feSEric Blake bs->bl.max_transfer = max; 444081dd1feSEric Blake 445081dd1feSEric Blake if (s->info.opt_block && 446081dd1feSEric Blake s->info.opt_block > bs->bl.opt_transfer) { 447081dd1feSEric Blake bs->bl.opt_transfer = s->info.opt_block; 448081dd1feSEric Blake } 449fa21e6faSDenis V. Lunev } 450fa21e6faSDenis V. Lunev 451019d6b8fSAnthony Liguori static void nbd_close(BlockDriverState *bs) 452019d6b8fSAnthony Liguori { 45303504d05SMax Reitz BDRVNBDState *s = bs->opaque; 45403504d05SMax Reitz 455f53a829bSMax Reitz nbd_client_close(bs); 45603504d05SMax Reitz 45762cf396bSMarkus Armbruster qapi_free_SocketAddress(s->saddr); 45803504d05SMax Reitz g_free(s->export); 45903504d05SMax Reitz g_free(s->tlscredsid); 460019d6b8fSAnthony Liguori } 461019d6b8fSAnthony Liguori 462019d6b8fSAnthony Liguori static int64_t nbd_getlength(BlockDriverState *bs) 463019d6b8fSAnthony Liguori { 464019d6b8fSAnthony Liguori BDRVNBDState *s = bs->opaque; 465019d6b8fSAnthony Liguori 466004a89fcSEric Blake return s->client.info.size; 467019d6b8fSAnthony Liguori } 468019d6b8fSAnthony Liguori 46969447cd8SStefan Hajnoczi static void nbd_detach_aio_context(BlockDriverState *bs) 47069447cd8SStefan Hajnoczi { 471f53a829bSMax Reitz nbd_client_detach_aio_context(bs); 47269447cd8SStefan Hajnoczi } 47369447cd8SStefan Hajnoczi 47469447cd8SStefan Hajnoczi static void nbd_attach_aio_context(BlockDriverState *bs, 47569447cd8SStefan Hajnoczi AioContext *new_context) 47669447cd8SStefan Hajnoczi { 477f53a829bSMax Reitz nbd_client_attach_aio_context(bs, new_context); 47869447cd8SStefan Hajnoczi } 47969447cd8SStefan Hajnoczi 4804cdd01d3SKevin Wolf static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) 4812019d68bSMax Reitz { 48203504d05SMax Reitz BDRVNBDState *s = bs->opaque; 4832019d68bSMax Reitz QDict *opts = qdict_new(); 484491d6c7cSMax Reitz QObject *saddr_qdict; 485491d6c7cSMax Reitz Visitor *ov; 486491d6c7cSMax Reitz const char *host = NULL, *port = NULL, *path = NULL; 487491d6c7cSMax Reitz 48862cf396bSMarkus Armbruster if (s->saddr->type == SOCKET_ADDRESS_TYPE_INET) { 4899445673eSMarkus Armbruster const InetSocketAddress *inet = &s->saddr->u.inet; 490491d6c7cSMax Reitz if (!inet->has_ipv4 && !inet->has_ipv6 && !inet->has_to) { 491491d6c7cSMax Reitz host = inet->host; 492491d6c7cSMax Reitz port = inet->port; 493491d6c7cSMax Reitz } 49462cf396bSMarkus Armbruster } else if (s->saddr->type == SOCKET_ADDRESS_TYPE_UNIX) { 4959445673eSMarkus Armbruster path = s->saddr->u.q_unix.path; 4969445673eSMarkus Armbruster } /* else can't represent as pseudo-filename */ 4972019d68bSMax Reitz 49846f5ac20SEric Blake qdict_put_str(opts, "driver", "nbd"); 4992019d68bSMax Reitz 500491d6c7cSMax Reitz if (path && s->export) { 501ec0de768SMax Reitz snprintf(bs->exact_filename, sizeof(bs->exact_filename), 502491d6c7cSMax Reitz "nbd+unix:///%s?socket=%s", s->export, path); 503491d6c7cSMax Reitz } else if (path && !s->export) { 504ec0de768SMax Reitz snprintf(bs->exact_filename, sizeof(bs->exact_filename), 505491d6c7cSMax Reitz "nbd+unix://?socket=%s", path); 506491d6c7cSMax Reitz } else if (host && s->export) { 507ec0de768SMax Reitz snprintf(bs->exact_filename, sizeof(bs->exact_filename), 508491d6c7cSMax Reitz "nbd://%s:%s/%s", host, port, s->export); 509491d6c7cSMax Reitz } else if (host && !s->export) { 510ec0de768SMax Reitz snprintf(bs->exact_filename, sizeof(bs->exact_filename), 511491d6c7cSMax Reitz "nbd://%s:%s", host, port); 512ec0de768SMax Reitz } 513ec0de768SMax Reitz 514491d6c7cSMax Reitz ov = qobject_output_visitor_new(&saddr_qdict); 51562cf396bSMarkus Armbruster visit_type_SocketAddress(ov, NULL, &s->saddr, &error_abort); 516491d6c7cSMax Reitz visit_complete(ov, &saddr_qdict); 517a1d4e38aSAshijeet Acharya visit_free(ov); 518491d6c7cSMax Reitz qdict_put_obj(opts, "server", saddr_qdict); 519491d6c7cSMax Reitz 52003504d05SMax Reitz if (s->export) { 52146f5ac20SEric Blake qdict_put_str(opts, "export", s->export); 5222019d68bSMax Reitz } 52303504d05SMax Reitz if (s->tlscredsid) { 52446f5ac20SEric Blake qdict_put_str(opts, "tls-creds", s->tlscredsid); 52575822a12SDaniel P. Berrange } 5262019d68bSMax Reitz 527491d6c7cSMax Reitz qdict_flatten(opts); 5282019d68bSMax Reitz bs->full_open_options = opts; 5292019d68bSMax Reitz } 5302019d68bSMax Reitz 5318a6239c0SMax Reitz static char *nbd_dirname(BlockDriverState *bs, Error **errp) 5328a6239c0SMax Reitz { 5338a6239c0SMax Reitz /* The generic bdrv_dirname() implementation is able to work out some 5348a6239c0SMax Reitz * directory name for NBD nodes, but that would be wrong. So far there is no 5358a6239c0SMax Reitz * specification for how "export paths" would work, so NBD does not have 5368a6239c0SMax Reitz * directory names. */ 5378a6239c0SMax Reitz error_setg(errp, "Cannot generate a base directory for NBD nodes"); 5388a6239c0SMax Reitz return NULL; 5398a6239c0SMax Reitz } 5408a6239c0SMax Reitz 541*2654267cSMax Reitz static const char *const nbd_strong_runtime_opts[] = { 542*2654267cSMax Reitz "path", 543*2654267cSMax Reitz "host", 544*2654267cSMax Reitz "port", 545*2654267cSMax Reitz "export", 546*2654267cSMax Reitz "tls-creds", 547*2654267cSMax Reitz "server.", 548*2654267cSMax Reitz 549*2654267cSMax Reitz NULL 550*2654267cSMax Reitz }; 551*2654267cSMax 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, 56978a33ab5SVladimir Sementsov-Ogievskiy .bdrv_co_block_status = nbd_client_co_block_status, 5708a6239c0SMax Reitz .bdrv_dirname = nbd_dirname, 571*2654267cSMax Reitz .strong_runtime_opts = nbd_strong_runtime_opts, 5721d7d2a9dSPaolo Bonzini }; 5731d7d2a9dSPaolo Bonzini 5741d7d2a9dSPaolo Bonzini static BlockDriver bdrv_nbd_tcp = { 5751d7d2a9dSPaolo Bonzini .format_name = "nbd", 5761d7d2a9dSPaolo Bonzini .protocol_name = "nbd+tcp", 5771d7d2a9dSPaolo Bonzini .instance_size = sizeof(BDRVNBDState), 5786963a30dSKevin Wolf .bdrv_parse_filename = nbd_parse_filename, 5791d7d2a9dSPaolo Bonzini .bdrv_file_open = nbd_open, 58070c4fb26SEric Blake .bdrv_co_preadv = nbd_client_co_preadv, 58170c4fb26SEric Blake .bdrv_co_pwritev = nbd_client_co_pwritev, 582fa778fffSEric Blake .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, 5831d7d2a9dSPaolo Bonzini .bdrv_close = nbd_close, 5841d7d2a9dSPaolo Bonzini .bdrv_co_flush_to_os = nbd_co_flush, 585447e57c3SEric Blake .bdrv_co_pdiscard = nbd_client_co_pdiscard, 586fa21e6faSDenis V. Lunev .bdrv_refresh_limits = nbd_refresh_limits, 5871d7d2a9dSPaolo Bonzini .bdrv_getlength = nbd_getlength, 58869447cd8SStefan Hajnoczi .bdrv_detach_aio_context = nbd_detach_aio_context, 58969447cd8SStefan Hajnoczi .bdrv_attach_aio_context = nbd_attach_aio_context, 5902019d68bSMax Reitz .bdrv_refresh_filename = nbd_refresh_filename, 59178a33ab5SVladimir Sementsov-Ogievskiy .bdrv_co_block_status = nbd_client_co_block_status, 5928a6239c0SMax Reitz .bdrv_dirname = nbd_dirname, 593*2654267cSMax Reitz .strong_runtime_opts = nbd_strong_runtime_opts, 5941d7d2a9dSPaolo Bonzini }; 5951d7d2a9dSPaolo Bonzini 5961d7d2a9dSPaolo Bonzini static BlockDriver bdrv_nbd_unix = { 5971d7d2a9dSPaolo Bonzini .format_name = "nbd", 5981d7d2a9dSPaolo Bonzini .protocol_name = "nbd+unix", 5991d7d2a9dSPaolo Bonzini .instance_size = sizeof(BDRVNBDState), 6006963a30dSKevin Wolf .bdrv_parse_filename = nbd_parse_filename, 6011d7d2a9dSPaolo Bonzini .bdrv_file_open = nbd_open, 60270c4fb26SEric Blake .bdrv_co_preadv = nbd_client_co_preadv, 60370c4fb26SEric Blake .bdrv_co_pwritev = nbd_client_co_pwritev, 604fa778fffSEric Blake .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, 6051d7d2a9dSPaolo Bonzini .bdrv_close = nbd_close, 6061d7d2a9dSPaolo Bonzini .bdrv_co_flush_to_os = nbd_co_flush, 607447e57c3SEric Blake .bdrv_co_pdiscard = nbd_client_co_pdiscard, 608fa21e6faSDenis V. Lunev .bdrv_refresh_limits = nbd_refresh_limits, 6091d7d2a9dSPaolo Bonzini .bdrv_getlength = nbd_getlength, 61069447cd8SStefan Hajnoczi .bdrv_detach_aio_context = nbd_detach_aio_context, 61169447cd8SStefan Hajnoczi .bdrv_attach_aio_context = nbd_attach_aio_context, 6122019d68bSMax Reitz .bdrv_refresh_filename = nbd_refresh_filename, 61378a33ab5SVladimir Sementsov-Ogievskiy .bdrv_co_block_status = nbd_client_co_block_status, 6148a6239c0SMax Reitz .bdrv_dirname = nbd_dirname, 615*2654267cSMax Reitz .strong_runtime_opts = nbd_strong_runtime_opts, 616019d6b8fSAnthony Liguori }; 617019d6b8fSAnthony Liguori 618019d6b8fSAnthony Liguori static void bdrv_nbd_init(void) 619019d6b8fSAnthony Liguori { 620019d6b8fSAnthony Liguori bdrv_register(&bdrv_nbd); 6211d7d2a9dSPaolo Bonzini bdrv_register(&bdrv_nbd_tcp); 6221d7d2a9dSPaolo Bonzini bdrv_register(&bdrv_nbd_unix); 623019d6b8fSAnthony Liguori } 624019d6b8fSAnthony Liguori 625019d6b8fSAnthony Liguori block_init(bdrv_nbd_init); 626