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 292302c1caSMarc-André Lureau #include "block/nbd-client.h" 301de7afc9SPaolo Bonzini #include "qemu/uri.h" 31737e150eSPaolo Bonzini #include "block/block_int.h" 321de7afc9SPaolo Bonzini #include "qemu/module.h" 331de7afc9SPaolo Bonzini #include "qemu/sockets.h" 342019d68bSMax Reitz #include "qapi/qmp/qdict.h" 35f53a1febSKevin Wolf #include "qapi/qmp/qjson.h" 36f53a1febSKevin Wolf #include "qapi/qmp/qint.h" 372019d68bSMax Reitz #include "qapi/qmp/qstring.h" 38019d6b8fSAnthony Liguori 39019d6b8fSAnthony Liguori #include <sys/types.h> 40019d6b8fSAnthony Liguori #include <unistd.h> 41019d6b8fSAnthony Liguori 421d45f8b5SLaurent Vivier #define EN_OPTSTR ":exportname=" 431d45f8b5SLaurent Vivier 44019d6b8fSAnthony Liguori typedef struct BDRVNBDState { 452302c1caSMarc-André Lureau NbdClientSession client; 46f53a1febSKevin Wolf QemuOpts *socket_opts; 47019d6b8fSAnthony Liguori } BDRVNBDState; 48019d6b8fSAnthony Liguori 49f53a1febSKevin Wolf static int nbd_parse_uri(const char *filename, QDict *options) 501d7d2a9dSPaolo Bonzini { 511d7d2a9dSPaolo Bonzini URI *uri; 521d7d2a9dSPaolo Bonzini const char *p; 531d7d2a9dSPaolo Bonzini QueryParams *qp = NULL; 541d7d2a9dSPaolo Bonzini int ret = 0; 55f53a1febSKevin Wolf bool is_unix; 561d7d2a9dSPaolo Bonzini 571d7d2a9dSPaolo Bonzini uri = uri_parse(filename); 581d7d2a9dSPaolo Bonzini if (!uri) { 591d7d2a9dSPaolo Bonzini return -EINVAL; 601d7d2a9dSPaolo Bonzini } 611d7d2a9dSPaolo Bonzini 621d7d2a9dSPaolo Bonzini /* transport */ 631d7d2a9dSPaolo Bonzini if (!strcmp(uri->scheme, "nbd")) { 64f53a1febSKevin Wolf is_unix = false; 651d7d2a9dSPaolo Bonzini } else if (!strcmp(uri->scheme, "nbd+tcp")) { 66f53a1febSKevin Wolf is_unix = false; 671d7d2a9dSPaolo Bonzini } else if (!strcmp(uri->scheme, "nbd+unix")) { 68f53a1febSKevin Wolf is_unix = true; 691d7d2a9dSPaolo Bonzini } else { 701d7d2a9dSPaolo Bonzini ret = -EINVAL; 711d7d2a9dSPaolo Bonzini goto out; 721d7d2a9dSPaolo Bonzini } 731d7d2a9dSPaolo Bonzini 741d7d2a9dSPaolo Bonzini p = uri->path ? uri->path : "/"; 751d7d2a9dSPaolo Bonzini p += strspn(p, "/"); 761d7d2a9dSPaolo Bonzini if (p[0]) { 77f53a1febSKevin Wolf qdict_put(options, "export", qstring_from_str(p)); 781d7d2a9dSPaolo Bonzini } 791d7d2a9dSPaolo Bonzini 801d7d2a9dSPaolo Bonzini qp = query_params_parse(uri->query); 81f53a1febSKevin Wolf if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) { 821d7d2a9dSPaolo Bonzini ret = -EINVAL; 831d7d2a9dSPaolo Bonzini goto out; 841d7d2a9dSPaolo Bonzini } 851d7d2a9dSPaolo Bonzini 86f53a1febSKevin Wolf if (is_unix) { 871d7d2a9dSPaolo Bonzini /* nbd+unix:///export?socket=path */ 881d7d2a9dSPaolo Bonzini if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) { 891d7d2a9dSPaolo Bonzini ret = -EINVAL; 901d7d2a9dSPaolo Bonzini goto out; 911d7d2a9dSPaolo Bonzini } 92f53a1febSKevin Wolf qdict_put(options, "path", qstring_from_str(qp->p[0].value)); 931d7d2a9dSPaolo Bonzini } else { 9423307908SJán Tomko QString *host; 95bebbf7faSKevin Wolf /* nbd[+tcp]://host[:port]/export */ 961d7d2a9dSPaolo Bonzini if (!uri->server) { 971d7d2a9dSPaolo Bonzini ret = -EINVAL; 981d7d2a9dSPaolo Bonzini goto out; 991d7d2a9dSPaolo Bonzini } 100f17c90beSKevin Wolf 10123307908SJán Tomko /* strip braces from literal IPv6 address */ 10223307908SJán Tomko if (uri->server[0] == '[') { 10323307908SJán Tomko host = qstring_from_substr(uri->server, 1, 10423307908SJán Tomko strlen(uri->server) - 2); 10523307908SJán Tomko } else { 10623307908SJán Tomko host = qstring_from_str(uri->server); 10723307908SJán Tomko } 10823307908SJán Tomko 10923307908SJán Tomko qdict_put(options, "host", host); 110bebbf7faSKevin Wolf if (uri->port) { 111bebbf7faSKevin Wolf char* port_str = g_strdup_printf("%d", uri->port); 112f53a1febSKevin Wolf qdict_put(options, "port", qstring_from_str(port_str)); 113f53a1febSKevin Wolf g_free(port_str); 1141d7d2a9dSPaolo Bonzini } 115bebbf7faSKevin Wolf } 1161d7d2a9dSPaolo Bonzini 1171d7d2a9dSPaolo Bonzini out: 1181d7d2a9dSPaolo Bonzini if (qp) { 1191d7d2a9dSPaolo Bonzini query_params_free(qp); 1201d7d2a9dSPaolo Bonzini } 1211d7d2a9dSPaolo Bonzini uri_free(uri); 1221d7d2a9dSPaolo Bonzini return ret; 1231d7d2a9dSPaolo Bonzini } 1241d7d2a9dSPaolo Bonzini 1256963a30dSKevin Wolf static void nbd_parse_filename(const char *filename, QDict *options, 1266963a30dSKevin Wolf Error **errp) 127019d6b8fSAnthony Liguori { 1281d45f8b5SLaurent Vivier char *file; 12933897dc7SNick Thomas char *export_name; 13033897dc7SNick Thomas const char *host_spec; 131019d6b8fSAnthony Liguori const char *unixpath; 132019d6b8fSAnthony Liguori 133681e7ad0SKevin Wolf if (qdict_haskey(options, "host") 134681e7ad0SKevin Wolf || qdict_haskey(options, "port") 135681e7ad0SKevin Wolf || qdict_haskey(options, "path")) 136681e7ad0SKevin Wolf { 137681e7ad0SKevin Wolf error_setg(errp, "host/port/path and a file name may not be specified " 138681e7ad0SKevin Wolf "at the same time"); 139681e7ad0SKevin Wolf return; 140681e7ad0SKevin Wolf } 141681e7ad0SKevin Wolf 1421d7d2a9dSPaolo Bonzini if (strstr(filename, "://")) { 1436963a30dSKevin Wolf int ret = nbd_parse_uri(filename, options); 1446963a30dSKevin Wolf if (ret < 0) { 1456963a30dSKevin Wolf error_setg(errp, "No valid URL specified"); 1466963a30dSKevin Wolf } 1476963a30dSKevin Wolf return; 1481d7d2a9dSPaolo Bonzini } 1491d7d2a9dSPaolo Bonzini 1507267c094SAnthony Liguori file = g_strdup(filename); 1511d45f8b5SLaurent Vivier 15233897dc7SNick Thomas export_name = strstr(file, EN_OPTSTR); 15333897dc7SNick Thomas if (export_name) { 15433897dc7SNick Thomas if (export_name[strlen(EN_OPTSTR)] == 0) { 1551d45f8b5SLaurent Vivier goto out; 1561d45f8b5SLaurent Vivier } 15733897dc7SNick Thomas export_name[0] = 0; /* truncate 'file' */ 15833897dc7SNick Thomas export_name += strlen(EN_OPTSTR); 159f53a1febSKevin Wolf 160f53a1febSKevin Wolf qdict_put(options, "export", qstring_from_str(export_name)); 1611d45f8b5SLaurent Vivier } 1621d45f8b5SLaurent Vivier 16333897dc7SNick Thomas /* extract the host_spec - fail if it's not nbd:... */ 16433897dc7SNick Thomas if (!strstart(file, "nbd:", &host_spec)) { 1656963a30dSKevin Wolf error_setg(errp, "File name string for NBD must start with 'nbd:'"); 1661d45f8b5SLaurent Vivier goto out; 1671d45f8b5SLaurent Vivier } 168019d6b8fSAnthony Liguori 169f53a1febSKevin Wolf if (!*host_spec) { 170f53a1febSKevin Wolf goto out; 171f53a1febSKevin Wolf } 172f53a1febSKevin Wolf 17333897dc7SNick Thomas /* are we a UNIX or TCP socket? */ 17433897dc7SNick Thomas if (strstart(host_spec, "unix:", &unixpath)) { 175f53a1febSKevin Wolf qdict_put(options, "path", qstring_from_str(unixpath)); 176019d6b8fSAnthony Liguori } else { 177f53a1febSKevin Wolf InetSocketAddress *addr = NULL; 178f53a1febSKevin Wolf 1796963a30dSKevin Wolf addr = inet_parse(host_spec, errp); 18092de9012SMarkus Armbruster if (!addr) { 181f17c90beSKevin Wolf goto out; 182f17c90beSKevin Wolf } 183f53a1febSKevin Wolf 184f53a1febSKevin Wolf qdict_put(options, "host", qstring_from_str(addr->host)); 185f53a1febSKevin Wolf qdict_put(options, "port", qstring_from_str(addr->port)); 186f53a1febSKevin Wolf qapi_free_InetSocketAddress(addr); 1871d45f8b5SLaurent Vivier } 1881d45f8b5SLaurent Vivier 1891d45f8b5SLaurent Vivier out: 1907267c094SAnthony Liguori g_free(file); 19133897dc7SNick Thomas } 192f53a1febSKevin Wolf 19377e8b9caSPaolo Bonzini static void nbd_config(BDRVNBDState *s, QDict *options, char **export, 19477e8b9caSPaolo Bonzini Error **errp) 195f53a1febSKevin Wolf { 196f53a1febSKevin Wolf Error *local_err = NULL; 197f53a1febSKevin Wolf 198a69d9af4SPaolo Bonzini if (qdict_haskey(options, "path") == qdict_haskey(options, "host")) { 199f53a1febSKevin Wolf if (qdict_haskey(options, "path")) { 20077e8b9caSPaolo Bonzini error_setg(errp, "path and host may not be used at the same time."); 201f53a1febSKevin Wolf } else { 20277e8b9caSPaolo Bonzini error_setg(errp, "one of path and host must be specified."); 203a69d9af4SPaolo Bonzini } 20477e8b9caSPaolo Bonzini return; 205019d6b8fSAnthony Liguori } 206019d6b8fSAnthony Liguori 207a69d9af4SPaolo Bonzini s->client.is_unix = qdict_haskey(options, "path"); 20887ea75d5SPeter Crosthwaite s->socket_opts = qemu_opts_create(&socket_optslist, NULL, 0, 20987ea75d5SPeter Crosthwaite &error_abort); 210f53a1febSKevin Wolf 211f53a1febSKevin Wolf qemu_opts_absorb_qdict(s->socket_opts, options, &local_err); 21284d18f06SMarkus Armbruster if (local_err) { 21377e8b9caSPaolo Bonzini error_propagate(errp, local_err); 21477e8b9caSPaolo Bonzini return; 215f53a1febSKevin Wolf } 216f53a1febSKevin Wolf 217bebbf7faSKevin Wolf if (!qemu_opt_get(s->socket_opts, "port")) { 218bebbf7faSKevin Wolf qemu_opt_set_number(s->socket_opts, "port", NBD_DEFAULT_PORT); 219bebbf7faSKevin Wolf } 220bebbf7faSKevin Wolf 221e2bc625fSMarc-André Lureau *export = g_strdup(qdict_get_try_str(options, "export")); 222e2bc625fSMarc-André Lureau if (*export) { 223f53a1febSKevin Wolf qdict_del(options, "export"); 224f53a1febSKevin Wolf } 225f53a1febSKevin Wolf } 226f53a1febSKevin Wolf 22777e8b9caSPaolo Bonzini static int nbd_establish_connection(BlockDriverState *bs, Error **errp) 22833897dc7SNick Thomas { 22933897dc7SNick Thomas BDRVNBDState *s = bs->opaque; 23033897dc7SNick Thomas int sock; 23133897dc7SNick Thomas 2322302c1caSMarc-André Lureau if (s->client.is_unix) { 23377e8b9caSPaolo Bonzini sock = unix_connect_opts(s->socket_opts, errp, NULL, NULL); 23433897dc7SNick Thomas } else { 23577e8b9caSPaolo Bonzini sock = inet_connect_opts(s->socket_opts, errp, NULL, NULL); 23697ebbab0SStefan Hajnoczi if (sock >= 0) { 23797ebbab0SStefan Hajnoczi socket_set_nodelay(sock); 23897ebbab0SStefan Hajnoczi } 23933897dc7SNick Thomas } 24033897dc7SNick Thomas 24133897dc7SNick Thomas /* Failed to establish connection */ 242fc19f8a0SPaolo Bonzini if (sock < 0) { 24333897dc7SNick Thomas logout("Failed to establish connection to NBD server\n"); 24433897dc7SNick Thomas return -errno; 24533897dc7SNick Thomas } 24633897dc7SNick Thomas 2472302c1caSMarc-André Lureau return sock; 24833897dc7SNick Thomas } 24933897dc7SNick Thomas 250015a1036SMax Reitz static int nbd_open(BlockDriverState *bs, QDict *options, int flags, 251015a1036SMax Reitz Error **errp) 25233897dc7SNick Thomas { 25333897dc7SNick Thomas BDRVNBDState *s = bs->opaque; 254e2bc625fSMarc-André Lureau char *export = NULL; 2552302c1caSMarc-André Lureau int result, sock; 25677e8b9caSPaolo Bonzini Error *local_err = NULL; 257ae255e52SPaolo Bonzini 25833897dc7SNick Thomas /* Pop the config into our state object. Exit if invalid. */ 25977e8b9caSPaolo Bonzini nbd_config(s, options, &export, &local_err); 26077e8b9caSPaolo Bonzini if (local_err) { 26177e8b9caSPaolo Bonzini error_propagate(errp, local_err); 26277e8b9caSPaolo Bonzini return -EINVAL; 26333897dc7SNick Thomas } 26433897dc7SNick Thomas 26533897dc7SNick Thomas /* establish TCP connection, return error if it fails 26633897dc7SNick Thomas * TODO: Configurable retry-until-timeout behaviour. 26733897dc7SNick Thomas */ 26877e8b9caSPaolo Bonzini sock = nbd_establish_connection(bs, errp); 2692302c1caSMarc-André Lureau if (sock < 0) { 2702302c1caSMarc-André Lureau return sock; 27133897dc7SNick Thomas } 27233897dc7SNick Thomas 2732302c1caSMarc-André Lureau /* NBD handshake */ 2741ce52846SMax Reitz result = nbd_client_session_init(&s->client, bs, sock, export, errp); 275e2bc625fSMarc-André Lureau g_free(export); 276e2bc625fSMarc-André Lureau return result; 277ae255e52SPaolo Bonzini } 278d9b09f13SPaolo Bonzini 279d9b09f13SPaolo Bonzini static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num, 280d9b09f13SPaolo Bonzini int nb_sectors, QEMUIOVector *qiov) 281d9b09f13SPaolo Bonzini { 2822302c1caSMarc-André Lureau BDRVNBDState *s = bs->opaque; 2832302c1caSMarc-André Lureau 2842302c1caSMarc-André Lureau return nbd_client_session_co_readv(&s->client, sector_num, 2852302c1caSMarc-André Lureau nb_sectors, qiov); 286d9b09f13SPaolo Bonzini } 287d9b09f13SPaolo Bonzini 288d9b09f13SPaolo Bonzini static int nbd_co_writev(BlockDriverState *bs, int64_t sector_num, 289d9b09f13SPaolo Bonzini int nb_sectors, QEMUIOVector *qiov) 290d9b09f13SPaolo Bonzini { 2912302c1caSMarc-André Lureau BDRVNBDState *s = bs->opaque; 2922302c1caSMarc-André Lureau 2932302c1caSMarc-André Lureau return nbd_client_session_co_writev(&s->client, sector_num, 2942302c1caSMarc-André Lureau nb_sectors, qiov); 295d9b09f13SPaolo Bonzini } 296d9b09f13SPaolo Bonzini 2971486d04aSPaolo Bonzini static int nbd_co_flush(BlockDriverState *bs) 2981486d04aSPaolo Bonzini { 2991486d04aSPaolo Bonzini BDRVNBDState *s = bs->opaque; 3001486d04aSPaolo Bonzini 3012302c1caSMarc-André Lureau return nbd_client_session_co_flush(&s->client); 3021486d04aSPaolo Bonzini } 3031486d04aSPaolo Bonzini 304*fa21e6faSDenis V. Lunev static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) 305*fa21e6faSDenis V. Lunev { 306*fa21e6faSDenis V. Lunev bs->bl.max_discard = UINT32_MAX >> BDRV_SECTOR_BITS; 307*fa21e6faSDenis V. Lunev bs->bl.max_transfer_length = UINT32_MAX >> BDRV_SECTOR_BITS; 308*fa21e6faSDenis V. Lunev } 309*fa21e6faSDenis V. Lunev 3107a706633SPaolo Bonzini static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num, 3117a706633SPaolo Bonzini int nb_sectors) 3127a706633SPaolo Bonzini { 3137a706633SPaolo Bonzini BDRVNBDState *s = bs->opaque; 3147a706633SPaolo Bonzini 3152302c1caSMarc-André Lureau return nbd_client_session_co_discard(&s->client, sector_num, 3162302c1caSMarc-André Lureau nb_sectors); 3177a706633SPaolo Bonzini } 3187a706633SPaolo Bonzini 319019d6b8fSAnthony Liguori static void nbd_close(BlockDriverState *bs) 320019d6b8fSAnthony Liguori { 321d2d979c6SNick Thomas BDRVNBDState *s = bs->opaque; 322d2d979c6SNick Thomas 3232302c1caSMarc-André Lureau qemu_opts_del(s->socket_opts); 3242302c1caSMarc-André Lureau nbd_client_session_close(&s->client); 325019d6b8fSAnthony Liguori } 326019d6b8fSAnthony Liguori 327019d6b8fSAnthony Liguori static int64_t nbd_getlength(BlockDriverState *bs) 328019d6b8fSAnthony Liguori { 329019d6b8fSAnthony Liguori BDRVNBDState *s = bs->opaque; 330019d6b8fSAnthony Liguori 3312302c1caSMarc-André Lureau return s->client.size; 332019d6b8fSAnthony Liguori } 333019d6b8fSAnthony Liguori 33469447cd8SStefan Hajnoczi static void nbd_detach_aio_context(BlockDriverState *bs) 33569447cd8SStefan Hajnoczi { 33669447cd8SStefan Hajnoczi BDRVNBDState *s = bs->opaque; 33769447cd8SStefan Hajnoczi 33869447cd8SStefan Hajnoczi nbd_client_session_detach_aio_context(&s->client); 33969447cd8SStefan Hajnoczi } 34069447cd8SStefan Hajnoczi 34169447cd8SStefan Hajnoczi static void nbd_attach_aio_context(BlockDriverState *bs, 34269447cd8SStefan Hajnoczi AioContext *new_context) 34369447cd8SStefan Hajnoczi { 34469447cd8SStefan Hajnoczi BDRVNBDState *s = bs->opaque; 34569447cd8SStefan Hajnoczi 34669447cd8SStefan Hajnoczi nbd_client_session_attach_aio_context(&s->client, new_context); 34769447cd8SStefan Hajnoczi } 34869447cd8SStefan Hajnoczi 3492019d68bSMax Reitz static void nbd_refresh_filename(BlockDriverState *bs) 3502019d68bSMax Reitz { 3512019d68bSMax Reitz QDict *opts = qdict_new(); 352ec0de768SMax Reitz const char *path = qdict_get_try_str(bs->options, "path"); 353ec0de768SMax Reitz const char *host = qdict_get_try_str(bs->options, "host"); 354ec0de768SMax Reitz const char *port = qdict_get_try_str(bs->options, "port"); 355ec0de768SMax Reitz const char *export = qdict_get_try_str(bs->options, "export"); 3562019d68bSMax Reitz 3572019d68bSMax Reitz qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd"))); 3582019d68bSMax Reitz 359ec0de768SMax Reitz if (path && export) { 360ec0de768SMax Reitz snprintf(bs->exact_filename, sizeof(bs->exact_filename), 361ec0de768SMax Reitz "nbd+unix:///%s?socket=%s", export, path); 362ec0de768SMax Reitz } else if (path && !export) { 363ec0de768SMax Reitz snprintf(bs->exact_filename, sizeof(bs->exact_filename), 364ec0de768SMax Reitz "nbd+unix://?socket=%s", path); 365ec0de768SMax Reitz } else if (!path && export && port) { 366ec0de768SMax Reitz snprintf(bs->exact_filename, sizeof(bs->exact_filename), 367ec0de768SMax Reitz "nbd://%s:%s/%s", host, port, export); 368ec0de768SMax Reitz } else if (!path && export && !port) { 369ec0de768SMax Reitz snprintf(bs->exact_filename, sizeof(bs->exact_filename), 370ec0de768SMax Reitz "nbd://%s/%s", host, export); 371ec0de768SMax Reitz } else if (!path && !export && port) { 372ec0de768SMax Reitz snprintf(bs->exact_filename, sizeof(bs->exact_filename), 373ec0de768SMax Reitz "nbd://%s:%s", host, port); 374ec0de768SMax Reitz } else if (!path && !export && !port) { 375ec0de768SMax Reitz snprintf(bs->exact_filename, sizeof(bs->exact_filename), 376ec0de768SMax Reitz "nbd://%s", host); 377ec0de768SMax Reitz } 378ec0de768SMax Reitz 3792019d68bSMax Reitz if (path) { 3802019d68bSMax Reitz qdict_put_obj(opts, "path", QOBJECT(qstring_from_str(path))); 381ec0de768SMax Reitz } else if (port) { 3822019d68bSMax Reitz qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(host))); 3832019d68bSMax Reitz qdict_put_obj(opts, "port", QOBJECT(qstring_from_str(port))); 3842019d68bSMax Reitz } else { 3852019d68bSMax Reitz qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(host))); 386ec0de768SMax Reitz } 387ec0de768SMax Reitz if (export) { 388ec0de768SMax Reitz qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(export))); 3892019d68bSMax Reitz } 3902019d68bSMax Reitz 3912019d68bSMax Reitz bs->full_open_options = opts; 3922019d68bSMax Reitz } 3932019d68bSMax Reitz 394019d6b8fSAnthony Liguori static BlockDriver bdrv_nbd = { 395019d6b8fSAnthony Liguori .format_name = "nbd", 3961d7d2a9dSPaolo Bonzini .protocol_name = "nbd", 397019d6b8fSAnthony Liguori .instance_size = sizeof(BDRVNBDState), 3986963a30dSKevin Wolf .bdrv_parse_filename = nbd_parse_filename, 39966f82ceeSKevin Wolf .bdrv_file_open = nbd_open, 400ae255e52SPaolo Bonzini .bdrv_co_readv = nbd_co_readv, 401ae255e52SPaolo Bonzini .bdrv_co_writev = nbd_co_writev, 402019d6b8fSAnthony Liguori .bdrv_close = nbd_close, 4031486d04aSPaolo Bonzini .bdrv_co_flush_to_os = nbd_co_flush, 4047a706633SPaolo Bonzini .bdrv_co_discard = nbd_co_discard, 405*fa21e6faSDenis V. Lunev .bdrv_refresh_limits = nbd_refresh_limits, 406019d6b8fSAnthony Liguori .bdrv_getlength = nbd_getlength, 40769447cd8SStefan Hajnoczi .bdrv_detach_aio_context = nbd_detach_aio_context, 40869447cd8SStefan Hajnoczi .bdrv_attach_aio_context = nbd_attach_aio_context, 4092019d68bSMax Reitz .bdrv_refresh_filename = nbd_refresh_filename, 4101d7d2a9dSPaolo Bonzini }; 4111d7d2a9dSPaolo Bonzini 4121d7d2a9dSPaolo Bonzini static BlockDriver bdrv_nbd_tcp = { 4131d7d2a9dSPaolo Bonzini .format_name = "nbd", 4141d7d2a9dSPaolo Bonzini .protocol_name = "nbd+tcp", 4151d7d2a9dSPaolo Bonzini .instance_size = sizeof(BDRVNBDState), 4166963a30dSKevin Wolf .bdrv_parse_filename = nbd_parse_filename, 4171d7d2a9dSPaolo Bonzini .bdrv_file_open = nbd_open, 4181d7d2a9dSPaolo Bonzini .bdrv_co_readv = nbd_co_readv, 4191d7d2a9dSPaolo Bonzini .bdrv_co_writev = nbd_co_writev, 4201d7d2a9dSPaolo Bonzini .bdrv_close = nbd_close, 4211d7d2a9dSPaolo Bonzini .bdrv_co_flush_to_os = nbd_co_flush, 4221d7d2a9dSPaolo Bonzini .bdrv_co_discard = nbd_co_discard, 423*fa21e6faSDenis V. Lunev .bdrv_refresh_limits = nbd_refresh_limits, 4241d7d2a9dSPaolo Bonzini .bdrv_getlength = nbd_getlength, 42569447cd8SStefan Hajnoczi .bdrv_detach_aio_context = nbd_detach_aio_context, 42669447cd8SStefan Hajnoczi .bdrv_attach_aio_context = nbd_attach_aio_context, 4272019d68bSMax Reitz .bdrv_refresh_filename = nbd_refresh_filename, 4281d7d2a9dSPaolo Bonzini }; 4291d7d2a9dSPaolo Bonzini 4301d7d2a9dSPaolo Bonzini static BlockDriver bdrv_nbd_unix = { 4311d7d2a9dSPaolo Bonzini .format_name = "nbd", 4321d7d2a9dSPaolo Bonzini .protocol_name = "nbd+unix", 4331d7d2a9dSPaolo Bonzini .instance_size = sizeof(BDRVNBDState), 4346963a30dSKevin Wolf .bdrv_parse_filename = nbd_parse_filename, 4351d7d2a9dSPaolo Bonzini .bdrv_file_open = nbd_open, 4361d7d2a9dSPaolo Bonzini .bdrv_co_readv = nbd_co_readv, 4371d7d2a9dSPaolo Bonzini .bdrv_co_writev = nbd_co_writev, 4381d7d2a9dSPaolo Bonzini .bdrv_close = nbd_close, 4391d7d2a9dSPaolo Bonzini .bdrv_co_flush_to_os = nbd_co_flush, 4401d7d2a9dSPaolo Bonzini .bdrv_co_discard = nbd_co_discard, 441*fa21e6faSDenis V. Lunev .bdrv_refresh_limits = nbd_refresh_limits, 4421d7d2a9dSPaolo Bonzini .bdrv_getlength = nbd_getlength, 44369447cd8SStefan Hajnoczi .bdrv_detach_aio_context = nbd_detach_aio_context, 44469447cd8SStefan Hajnoczi .bdrv_attach_aio_context = nbd_attach_aio_context, 4452019d68bSMax Reitz .bdrv_refresh_filename = nbd_refresh_filename, 446019d6b8fSAnthony Liguori }; 447019d6b8fSAnthony Liguori 448019d6b8fSAnthony Liguori static void bdrv_nbd_init(void) 449019d6b8fSAnthony Liguori { 450019d6b8fSAnthony Liguori bdrv_register(&bdrv_nbd); 4511d7d2a9dSPaolo Bonzini bdrv_register(&bdrv_nbd_tcp); 4521d7d2a9dSPaolo Bonzini bdrv_register(&bdrv_nbd_unix); 453019d6b8fSAnthony Liguori } 454019d6b8fSAnthony Liguori 455019d6b8fSAnthony Liguori block_init(bdrv_nbd_init); 456