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 43 typedef struct BDRVNBDState { 44 NbdClientSession client; 45 } BDRVNBDState; 46 47 static 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 115 out: 116 if (qp) { 117 query_params_free(qp); 118 } 119 uri_free(uri); 120 return ret; 121 } 122 123 static 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 187 out: 188 g_free(file); 189 } 190 191 static 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 237 NbdClientSession *nbd_get_client_session(BlockDriverState *bs) 238 { 239 BDRVNBDState *s = bs->opaque; 240 return &s->client; 241 } 242 243 static 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 265 static 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 295 static 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 352 static 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 358 static int nbd_co_flush(BlockDriverState *bs) 359 { 360 return nbd_client_co_flush(bs); 361 } 362 363 static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) 364 { 365 bs->bl.max_pdiscard = NBD_MAX_BUFFER_SIZE; 366 bs->bl.max_transfer = NBD_MAX_BUFFER_SIZE; 367 } 368 369 static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num, 370 int nb_sectors) 371 { 372 return nbd_client_co_discard(bs, sector_num, nb_sectors); 373 } 374 375 static void nbd_close(BlockDriverState *bs) 376 { 377 nbd_client_close(bs); 378 } 379 380 static int64_t nbd_getlength(BlockDriverState *bs) 381 { 382 BDRVNBDState *s = bs->opaque; 383 384 return s->client.size; 385 } 386 387 static void nbd_detach_aio_context(BlockDriverState *bs) 388 { 389 nbd_client_detach_aio_context(bs); 390 } 391 392 static void nbd_attach_aio_context(BlockDriverState *bs, 393 AioContext *new_context) 394 { 395 nbd_client_attach_aio_context(bs, new_context); 396 } 397 398 static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) 399 { 400 QDict *opts = qdict_new(); 401 const char *path = qdict_get_try_str(options, "path"); 402 const char *host = qdict_get_try_str(options, "host"); 403 const char *port = qdict_get_try_str(options, "port"); 404 const char *export = qdict_get_try_str(options, "export"); 405 const char *tlscreds = qdict_get_try_str(options, "tls-creds"); 406 407 qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd"))); 408 409 if (path && export) { 410 snprintf(bs->exact_filename, sizeof(bs->exact_filename), 411 "nbd+unix:///%s?socket=%s", export, path); 412 } else if (path && !export) { 413 snprintf(bs->exact_filename, sizeof(bs->exact_filename), 414 "nbd+unix://?socket=%s", path); 415 } else if (!path && export && port) { 416 snprintf(bs->exact_filename, sizeof(bs->exact_filename), 417 "nbd://%s:%s/%s", host, port, export); 418 } else if (!path && export && !port) { 419 snprintf(bs->exact_filename, sizeof(bs->exact_filename), 420 "nbd://%s/%s", host, export); 421 } else if (!path && !export && port) { 422 snprintf(bs->exact_filename, sizeof(bs->exact_filename), 423 "nbd://%s:%s", host, port); 424 } else if (!path && !export && !port) { 425 snprintf(bs->exact_filename, sizeof(bs->exact_filename), 426 "nbd://%s", host); 427 } 428 429 if (path) { 430 qdict_put_obj(opts, "path", QOBJECT(qstring_from_str(path))); 431 } else if (port) { 432 qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(host))); 433 qdict_put_obj(opts, "port", QOBJECT(qstring_from_str(port))); 434 } else { 435 qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(host))); 436 } 437 if (export) { 438 qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(export))); 439 } 440 if (tlscreds) { 441 qdict_put_obj(opts, "tls-creds", QOBJECT(qstring_from_str(tlscreds))); 442 } 443 444 bs->full_open_options = opts; 445 } 446 447 static BlockDriver bdrv_nbd = { 448 .format_name = "nbd", 449 .protocol_name = "nbd", 450 .instance_size = sizeof(BDRVNBDState), 451 .bdrv_parse_filename = nbd_parse_filename, 452 .bdrv_file_open = nbd_open, 453 .bdrv_co_readv = nbd_co_readv, 454 .bdrv_co_writev_flags = nbd_client_co_writev, 455 .bdrv_close = nbd_close, 456 .bdrv_co_flush_to_os = nbd_co_flush, 457 .bdrv_co_discard = nbd_co_discard, 458 .bdrv_refresh_limits = nbd_refresh_limits, 459 .bdrv_getlength = nbd_getlength, 460 .bdrv_detach_aio_context = nbd_detach_aio_context, 461 .bdrv_attach_aio_context = nbd_attach_aio_context, 462 .bdrv_refresh_filename = nbd_refresh_filename, 463 }; 464 465 static BlockDriver bdrv_nbd_tcp = { 466 .format_name = "nbd", 467 .protocol_name = "nbd+tcp", 468 .instance_size = sizeof(BDRVNBDState), 469 .bdrv_parse_filename = nbd_parse_filename, 470 .bdrv_file_open = nbd_open, 471 .bdrv_co_readv = nbd_co_readv, 472 .bdrv_co_writev_flags = nbd_client_co_writev, 473 .bdrv_close = nbd_close, 474 .bdrv_co_flush_to_os = nbd_co_flush, 475 .bdrv_co_discard = nbd_co_discard, 476 .bdrv_refresh_limits = nbd_refresh_limits, 477 .bdrv_getlength = nbd_getlength, 478 .bdrv_detach_aio_context = nbd_detach_aio_context, 479 .bdrv_attach_aio_context = nbd_attach_aio_context, 480 .bdrv_refresh_filename = nbd_refresh_filename, 481 }; 482 483 static BlockDriver bdrv_nbd_unix = { 484 .format_name = "nbd", 485 .protocol_name = "nbd+unix", 486 .instance_size = sizeof(BDRVNBDState), 487 .bdrv_parse_filename = nbd_parse_filename, 488 .bdrv_file_open = nbd_open, 489 .bdrv_co_readv = nbd_co_readv, 490 .bdrv_co_writev_flags = nbd_client_co_writev, 491 .bdrv_close = nbd_close, 492 .bdrv_co_flush_to_os = nbd_co_flush, 493 .bdrv_co_discard = nbd_co_discard, 494 .bdrv_refresh_limits = nbd_refresh_limits, 495 .bdrv_getlength = nbd_getlength, 496 .bdrv_detach_aio_context = nbd_detach_aio_context, 497 .bdrv_attach_aio_context = nbd_attach_aio_context, 498 .bdrv_refresh_filename = nbd_refresh_filename, 499 }; 500 501 static void bdrv_nbd_init(void) 502 { 503 bdrv_register(&bdrv_nbd); 504 bdrv_register(&bdrv_nbd_tcp); 505 bdrv_register(&bdrv_nbd_unix); 506 } 507 508 block_init(bdrv_nbd_init); 509