qemu/block/nbd.c
<<
>>
Prefs
   1/*
   2 * QEMU Block driver for  NBD
   3 *
   4 * Copyright (C) 2008 Bull S.A.S.
   5 *     Author: Laurent Vivier <Laurent.Vivier@bull.net>
   6 *
   7 * Some parts:
   8 *    Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws>
   9 *
  10 * Permission is hereby granted, free of charge, to any person obtaining a copy
  11 * of this software and associated documentation files (the "Software"), to deal
  12 * in the Software without restriction, including without limitation the rights
  13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  14 * copies of the Software, and to permit persons to whom the Software is
  15 * furnished to do so, subject to the following conditions:
  16 *
  17 * The above copyright notice and this permission notice shall be included in
  18 * all copies or substantial portions of the Software.
  19 *
  20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  26 * THE SOFTWARE.
  27 */
  28
  29#include "qemu/osdep.h"
  30#include "block/nbd-client.h"
  31#include "qapi/error.h"
  32#include "qemu/uri.h"
  33#include "block/block_int.h"
  34#include "qemu/module.h"
  35#include "qapi/qmp/qdict.h"
  36#include "qapi/qmp/qjson.h"
  37#include "qapi/qmp/qint.h"
  38#include "qapi/qmp/qstring.h"
  39#include "qemu/cutils.h"
  40
  41#define EN_OPTSTR ":exportname="
  42
  43typedef struct BDRVNBDState {
  44    NbdClientSession client;
  45} BDRVNBDState;
  46
  47static int nbd_parse_uri(const char *filename, QDict *options)
  48{
  49    URI *uri;
  50    const char *p;
  51    QueryParams *qp = NULL;
  52    int ret = 0;
  53    bool is_unix;
  54
  55    uri = uri_parse(filename);
  56    if (!uri) {
  57        return -EINVAL;
  58    }
  59
  60    /* transport */
  61    if (!strcmp(uri->scheme, "nbd")) {
  62        is_unix = false;
  63    } else if (!strcmp(uri->scheme, "nbd+tcp")) {
  64        is_unix = false;
  65    } else if (!strcmp(uri->scheme, "nbd+unix")) {
  66        is_unix = true;
  67    } else {
  68        ret = -EINVAL;
  69        goto out;
  70    }
  71
  72    p = uri->path ? uri->path : "/";
  73    p += strspn(p, "/");
  74    if (p[0]) {
  75        qdict_put(options, "export", qstring_from_str(p));
  76    }
  77
  78    qp = query_params_parse(uri->query);
  79    if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) {
  80        ret = -EINVAL;
  81        goto out;
  82    }
  83
  84    if (is_unix) {
  85        /* nbd+unix:///export?socket=path */
  86        if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) {
  87            ret = -EINVAL;
  88            goto out;
  89        }
  90        qdict_put(options, "path", qstring_from_str(qp->p[0].value));
  91    } else {
  92        QString *host;
  93        /* nbd[+tcp]://host[:port]/export */
  94        if (!uri->server) {
  95            ret = -EINVAL;
  96            goto out;
  97        }
  98
  99        /* strip braces from literal IPv6 address */
 100        if (uri->server[0] == '[') {
 101            host = qstring_from_substr(uri->server, 1,
 102                                       strlen(uri->server) - 2);
 103        } else {
 104            host = qstring_from_str(uri->server);
 105        }
 106
 107        qdict_put(options, "host", host);
 108        if (uri->port) {
 109            char* port_str = g_strdup_printf("%d", uri->port);
 110            qdict_put(options, "port", qstring_from_str(port_str));
 111            g_free(port_str);
 112        }
 113    }
 114
 115out:
 116    if (qp) {
 117        query_params_free(qp);
 118    }
 119    uri_free(uri);
 120    return ret;
 121}
 122
 123static void nbd_parse_filename(const char *filename, QDict *options,
 124                               Error **errp)
 125{
 126    char *file;
 127    char *export_name;
 128    const char *host_spec;
 129    const char *unixpath;
 130
 131    if (qdict_haskey(options, "host")
 132        || qdict_haskey(options, "port")
 133        || qdict_haskey(options, "path"))
 134    {
 135        error_setg(errp, "host/port/path and a file name may not be specified "
 136                         "at the same time");
 137        return;
 138    }
 139
 140    if (strstr(filename, "://")) {
 141        int ret = nbd_parse_uri(filename, options);
 142        if (ret < 0) {
 143            error_setg(errp, "No valid URL specified");
 144        }
 145        return;
 146    }
 147
 148    file = g_strdup(filename);
 149
 150    export_name = strstr(file, EN_OPTSTR);
 151    if (export_name) {
 152        if (export_name[strlen(EN_OPTSTR)] == 0) {
 153            goto out;
 154        }
 155        export_name[0] = 0; /* truncate 'file' */
 156        export_name += strlen(EN_OPTSTR);
 157
 158        qdict_put(options, "export", qstring_from_str(export_name));
 159    }
 160
 161    /* extract the host_spec - fail if it's not nbd:... */
 162    if (!strstart(file, "nbd:", &host_spec)) {
 163        error_setg(errp, "File name string for NBD must start with 'nbd:'");
 164        goto out;
 165    }
 166
 167    if (!*host_spec) {
 168        goto out;
 169    }
 170
 171    /* are we a UNIX or TCP socket? */
 172    if (strstart(host_spec, "unix:", &unixpath)) {
 173        qdict_put(options, "path", qstring_from_str(unixpath));
 174    } else {
 175        InetSocketAddress *addr = NULL;
 176
 177        addr = inet_parse(host_spec, errp);
 178        if (!addr) {
 179            goto out;
 180        }
 181
 182        qdict_put(options, "host", qstring_from_str(addr->host));
 183        qdict_put(options, "port", qstring_from_str(addr->port));
 184        qapi_free_InetSocketAddress(addr);
 185    }
 186
 187out:
 188    g_free(file);
 189}
 190
 191static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, char **export,
 192                                 Error **errp)
 193{
 194    SocketAddress *saddr;
 195
 196    if (qdict_haskey(options, "path") == qdict_haskey(options, "host")) {
 197        if (qdict_haskey(options, "path")) {
 198            error_setg(errp, "path and host may not be used at the same time.");
 199        } else {
 200            error_setg(errp, "one of path and host must be specified.");
 201        }
 202        return NULL;
 203    }
 204
 205    saddr = g_new0(SocketAddress, 1);
 206
 207    if (qdict_haskey(options, "path")) {
 208        UnixSocketAddress *q_unix;
 209        saddr->type = SOCKET_ADDRESS_KIND_UNIX;
 210        q_unix = saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
 211        q_unix->path = g_strdup(qdict_get_str(options, "path"));
 212        qdict_del(options, "path");
 213    } else {
 214        InetSocketAddress *inet;
 215        saddr->type = SOCKET_ADDRESS_KIND_INET;
 216        inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1);
 217        inet->host = g_strdup(qdict_get_str(options, "host"));
 218        if (!qdict_get_try_str(options, "port")) {
 219            inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
 220        } else {
 221            inet->port = g_strdup(qdict_get_str(options, "port"));
 222        }
 223        qdict_del(options, "host");
 224        qdict_del(options, "port");
 225    }
 226
 227    s->client.is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
 228
 229    *export = g_strdup(qdict_get_try_str(options, "export"));
 230    if (*export) {
 231        qdict_del(options, "export");
 232    }
 233
 234    return saddr;
 235}
 236
 237NbdClientSession *nbd_get_client_session(BlockDriverState *bs)
 238{
 239    BDRVNBDState *s = bs->opaque;
 240    return &s->client;
 241}
 242
 243static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr,
 244                                                  Error **errp)
 245{
 246    QIOChannelSocket *sioc;
 247    Error *local_err = NULL;
 248
 249    sioc = qio_channel_socket_new();
 250
 251    qio_channel_socket_connect_sync(sioc,
 252                                    saddr,
 253                                    &local_err);
 254    if (local_err) {
 255        error_propagate(errp, local_err);
 256        return NULL;
 257    }
 258
 259    qio_channel_set_delay(QIO_CHANNEL(sioc), false);
 260
 261    return sioc;
 262}
 263
 264
 265static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
 266{
 267    Object *obj;
 268    QCryptoTLSCreds *creds;
 269
 270    obj = object_resolve_path_component(
 271        object_get_objects_root(), id);
 272    if (!obj) {
 273        error_setg(errp, "No TLS credentials with id '%s'",
 274                   id);
 275        return NULL;
 276    }
 277    creds = (QCryptoTLSCreds *)
 278        object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
 279    if (!creds) {
 280        error_setg(errp, "Object with id '%s' is not TLS credentials",
 281                   id);
 282        return NULL;
 283    }
 284
 285    if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
 286        error_setg(errp,
 287                   "Expecting TLS credentials with a client endpoint");
 288        return NULL;
 289    }
 290    object_ref(obj);
 291    return creds;
 292}
 293
 294
 295static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
 296                    Error **errp)
 297{
 298    BDRVNBDState *s = bs->opaque;
 299    char *export = NULL;
 300    QIOChannelSocket *sioc = NULL;
 301    SocketAddress *saddr;
 302    const char *tlscredsid;
 303    QCryptoTLSCreds *tlscreds = NULL;
 304    const char *hostname = NULL;
 305    int ret = -EINVAL;
 306
 307    /* Pop the config into our state object. Exit if invalid. */
 308    saddr = nbd_config(s, options, &export, errp);
 309    if (!saddr) {
 310        goto error;
 311    }
 312
 313    tlscredsid = g_strdup(qdict_get_try_str(options, "tls-creds"));
 314    if (tlscredsid) {
 315        qdict_del(options, "tls-creds");
 316        tlscreds = nbd_get_tls_creds(tlscredsid, errp);
 317        if (!tlscreds) {
 318            goto error;
 319        }
 320
 321        if (saddr->type != SOCKET_ADDRESS_KIND_INET) {
 322            error_setg(errp, "TLS only supported over IP sockets");
 323            goto error;
 324        }
 325        hostname = saddr->u.inet.data->host;
 326    }
 327
 328    /* establish TCP connection, return error if it fails
 329     * TODO: Configurable retry-until-timeout behaviour.
 330     */
 331    sioc = nbd_establish_connection(saddr, errp);
 332    if (!sioc) {
 333        ret = -ECONNREFUSED;
 334        goto error;
 335    }
 336
 337    /* NBD handshake */
 338    ret = nbd_client_init(bs, sioc, export,
 339                          tlscreds, hostname, errp);
 340 error:
 341    if (sioc) {
 342        object_unref(OBJECT(sioc));
 343    }
 344    if (tlscreds) {
 345        object_unref(OBJECT(tlscreds));
 346    }
 347    qapi_free_SocketAddress(saddr);
 348    g_free(export);
 349    return ret;
 350}
 351
 352static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num,
 353                        int nb_sectors, QEMUIOVector *qiov)
 354{
 355    return nbd_client_co_readv(bs, sector_num, nb_sectors, qiov);
 356}
 357
 358static int nbd_co_writev_flags(BlockDriverState *bs, int64_t sector_num,
 359                               int nb_sectors, QEMUIOVector *qiov, int flags)
 360{
 361    int ret;
 362
 363    ret = nbd_client_co_writev(bs, sector_num, nb_sectors, qiov, &flags);
 364    if (ret < 0) {
 365        return ret;
 366    }
 367
 368    /* The flag wasn't sent to the server, so we need to emulate it with an
 369     * explicit flush */
 370    if (flags & BDRV_REQ_FUA) {
 371        ret = nbd_client_co_flush(bs);
 372    }
 373
 374    return ret;
 375}
 376
 377static int nbd_co_writev(BlockDriverState *bs, int64_t sector_num,
 378                         int nb_sectors, QEMUIOVector *qiov)
 379{
 380    return nbd_co_writev_flags(bs, sector_num, nb_sectors, qiov, 0);
 381}
 382
 383static int nbd_co_flush(BlockDriverState *bs)
 384{
 385    return nbd_client_co_flush(bs);
 386}
 387
 388static void nbd_refresh_limits(BlockDriverState *bs, Error **errp)
 389{
 390    bs->bl.max_discard = UINT32_MAX >> BDRV_SECTOR_BITS;
 391    bs->bl.max_transfer_length = UINT32_MAX >> BDRV_SECTOR_BITS;
 392}
 393
 394static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num,
 395                          int nb_sectors)
 396{
 397    return nbd_client_co_discard(bs, sector_num, nb_sectors);
 398}
 399
 400static void nbd_close(BlockDriverState *bs)
 401{
 402    nbd_client_close(bs);
 403}
 404
 405static int64_t nbd_getlength(BlockDriverState *bs)
 406{
 407    BDRVNBDState *s = bs->opaque;
 408
 409    return s->client.size;
 410}
 411
 412static void nbd_detach_aio_context(BlockDriverState *bs)
 413{
 414    nbd_client_detach_aio_context(bs);
 415}
 416
 417static void nbd_attach_aio_context(BlockDriverState *bs,
 418                                   AioContext *new_context)
 419{
 420    nbd_client_attach_aio_context(bs, new_context);
 421}
 422
 423static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
 424{
 425    QDict *opts = qdict_new();
 426    const char *path   = qdict_get_try_str(options, "path");
 427    const char *host   = qdict_get_try_str(options, "host");
 428    const char *port   = qdict_get_try_str(options, "port");
 429    const char *export = qdict_get_try_str(options, "export");
 430    const char *tlscreds = qdict_get_try_str(options, "tls-creds");
 431
 432    qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd")));
 433
 434    if (path && export) {
 435        snprintf(bs->exact_filename, sizeof(bs->exact_filename),
 436                 "nbd+unix:///%s?socket=%s", export, path);
 437    } else if (path && !export) {
 438        snprintf(bs->exact_filename, sizeof(bs->exact_filename),
 439                 "nbd+unix://?socket=%s", path);
 440    } else if (!path && export && port) {
 441        snprintf(bs->exact_filename, sizeof(bs->exact_filename),
 442                 "nbd://%s:%s/%s", host, port, export);
 443    } else if (!path && export && !port) {
 444        snprintf(bs->exact_filename, sizeof(bs->exact_filename),
 445                 "nbd://%s/%s", host, export);
 446    } else if (!path && !export && port) {
 447        snprintf(bs->exact_filename, sizeof(bs->exact_filename),
 448                 "nbd://%s:%s", host, port);
 449    } else if (!path && !export && !port) {
 450        snprintf(bs->exact_filename, sizeof(bs->exact_filename),
 451                 "nbd://%s", host);
 452    }
 453
 454    if (path) {
 455        qdict_put_obj(opts, "path", QOBJECT(qstring_from_str(path)));
 456    } else if (port) {
 457        qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(host)));
 458        qdict_put_obj(opts, "port", QOBJECT(qstring_from_str(port)));
 459    } else {
 460        qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(host)));
 461    }
 462    if (export) {
 463        qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(export)));
 464    }
 465    if (tlscreds) {
 466        qdict_put_obj(opts, "tls-creds", QOBJECT(qstring_from_str(tlscreds)));
 467    }
 468
 469    bs->full_open_options = opts;
 470}
 471
 472static BlockDriver bdrv_nbd = {
 473    .format_name                = "nbd",
 474    .protocol_name              = "nbd",
 475    .instance_size              = sizeof(BDRVNBDState),
 476    .bdrv_parse_filename        = nbd_parse_filename,
 477    .bdrv_file_open             = nbd_open,
 478    .bdrv_co_readv              = nbd_co_readv,
 479    .bdrv_co_writev             = nbd_co_writev,
 480    .bdrv_co_writev_flags       = nbd_co_writev_flags,
 481    .supported_write_flags      = BDRV_REQ_FUA,
 482    .bdrv_close                 = nbd_close,
 483    .bdrv_co_flush_to_os        = nbd_co_flush,
 484    .bdrv_co_discard            = nbd_co_discard,
 485    .bdrv_refresh_limits        = nbd_refresh_limits,
 486    .bdrv_getlength             = nbd_getlength,
 487    .bdrv_detach_aio_context    = nbd_detach_aio_context,
 488    .bdrv_attach_aio_context    = nbd_attach_aio_context,
 489    .bdrv_refresh_filename      = nbd_refresh_filename,
 490};
 491
 492static BlockDriver bdrv_nbd_tcp = {
 493    .format_name                = "nbd",
 494    .protocol_name              = "nbd+tcp",
 495    .instance_size              = sizeof(BDRVNBDState),
 496    .bdrv_parse_filename        = nbd_parse_filename,
 497    .bdrv_file_open             = nbd_open,
 498    .bdrv_co_readv              = nbd_co_readv,
 499    .bdrv_co_writev             = nbd_co_writev,
 500    .bdrv_co_writev_flags       = nbd_co_writev_flags,
 501    .supported_write_flags      = BDRV_REQ_FUA,
 502    .bdrv_close                 = nbd_close,
 503    .bdrv_co_flush_to_os        = nbd_co_flush,
 504    .bdrv_co_discard            = nbd_co_discard,
 505    .bdrv_refresh_limits        = nbd_refresh_limits,
 506    .bdrv_getlength             = nbd_getlength,
 507    .bdrv_detach_aio_context    = nbd_detach_aio_context,
 508    .bdrv_attach_aio_context    = nbd_attach_aio_context,
 509    .bdrv_refresh_filename      = nbd_refresh_filename,
 510};
 511
 512static BlockDriver bdrv_nbd_unix = {
 513    .format_name                = "nbd",
 514    .protocol_name              = "nbd+unix",
 515    .instance_size              = sizeof(BDRVNBDState),
 516    .bdrv_parse_filename        = nbd_parse_filename,
 517    .bdrv_file_open             = nbd_open,
 518    .bdrv_co_readv              = nbd_co_readv,
 519    .bdrv_co_writev             = nbd_co_writev,
 520    .bdrv_co_writev_flags       = nbd_co_writev_flags,
 521    .supported_write_flags      = BDRV_REQ_FUA,
 522    .bdrv_close                 = nbd_close,
 523    .bdrv_co_flush_to_os        = nbd_co_flush,
 524    .bdrv_co_discard            = nbd_co_discard,
 525    .bdrv_refresh_limits        = nbd_refresh_limits,
 526    .bdrv_getlength             = nbd_getlength,
 527    .bdrv_detach_aio_context    = nbd_detach_aio_context,
 528    .bdrv_attach_aio_context    = nbd_attach_aio_context,
 529    .bdrv_refresh_filename      = nbd_refresh_filename,
 530};
 531
 532static void bdrv_nbd_init(void)
 533{
 534    bdrv_register(&bdrv_nbd);
 535    bdrv_register(&bdrv_nbd_tcp);
 536    bdrv_register(&bdrv_nbd_unix);
 537}
 538
 539block_init(bdrv_nbd_init);
 540