1 /* 2 * QEMU Block driver for native access to files on NFS shares 3 * 4 * Copyright (c) 2014-2016 Peter Lieven <pl@kamp.de> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25 #include "qemu/osdep.h" 26 27 #include <poll.h> 28 #include "qemu-common.h" 29 #include "qemu/config-file.h" 30 #include "qemu/error-report.h" 31 #include "qapi/error.h" 32 #include "block/block_int.h" 33 #include "trace.h" 34 #include "qemu/iov.h" 35 #include "qemu/uri.h" 36 #include "qemu/cutils.h" 37 #include "sysemu/sysemu.h" 38 #include <nfsc/libnfs.h> 39 40 #define QEMU_NFS_MAX_READAHEAD_SIZE 1048576 41 #define QEMU_NFS_MAX_PAGECACHE_SIZE (8388608 / NFS_BLKSIZE) 42 #define QEMU_NFS_MAX_DEBUG_LEVEL 2 43 44 typedef struct NFSClient { 45 struct nfs_context *context; 46 struct nfsfh *fh; 47 int events; 48 bool has_zero_init; 49 AioContext *aio_context; 50 blkcnt_t st_blocks; 51 bool cache_used; 52 } NFSClient; 53 54 typedef struct NFSRPC { 55 int ret; 56 int complete; 57 QEMUIOVector *iov; 58 struct stat *st; 59 Coroutine *co; 60 QEMUBH *bh; 61 NFSClient *client; 62 } NFSRPC; 63 64 static void nfs_process_read(void *arg); 65 static void nfs_process_write(void *arg); 66 67 static void nfs_set_events(NFSClient *client) 68 { 69 int ev = nfs_which_events(client->context); 70 if (ev != client->events) { 71 aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), 72 false, 73 (ev & POLLIN) ? nfs_process_read : NULL, 74 (ev & POLLOUT) ? nfs_process_write : NULL, client); 75 76 } 77 client->events = ev; 78 } 79 80 static void nfs_process_read(void *arg) 81 { 82 NFSClient *client = arg; 83 nfs_service(client->context, POLLIN); 84 nfs_set_events(client); 85 } 86 87 static void nfs_process_write(void *arg) 88 { 89 NFSClient *client = arg; 90 nfs_service(client->context, POLLOUT); 91 nfs_set_events(client); 92 } 93 94 static void nfs_co_init_task(NFSClient *client, NFSRPC *task) 95 { 96 *task = (NFSRPC) { 97 .co = qemu_coroutine_self(), 98 .client = client, 99 }; 100 } 101 102 static void nfs_co_generic_bh_cb(void *opaque) 103 { 104 NFSRPC *task = opaque; 105 task->complete = 1; 106 qemu_bh_delete(task->bh); 107 qemu_coroutine_enter(task->co); 108 } 109 110 static void 111 nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, 112 void *private_data) 113 { 114 NFSRPC *task = private_data; 115 task->ret = ret; 116 if (task->ret > 0 && task->iov) { 117 if (task->ret <= task->iov->size) { 118 qemu_iovec_from_buf(task->iov, 0, data, task->ret); 119 } else { 120 task->ret = -EIO; 121 } 122 } 123 if (task->ret == 0 && task->st) { 124 memcpy(task->st, data, sizeof(struct stat)); 125 } 126 if (task->ret < 0) { 127 error_report("NFS Error: %s", nfs_get_error(nfs)); 128 } 129 if (task->co) { 130 task->bh = aio_bh_new(task->client->aio_context, 131 nfs_co_generic_bh_cb, task); 132 qemu_bh_schedule(task->bh); 133 } else { 134 task->complete = 1; 135 } 136 } 137 138 static int coroutine_fn nfs_co_readv(BlockDriverState *bs, 139 int64_t sector_num, int nb_sectors, 140 QEMUIOVector *iov) 141 { 142 NFSClient *client = bs->opaque; 143 NFSRPC task; 144 145 nfs_co_init_task(client, &task); 146 task.iov = iov; 147 148 if (nfs_pread_async(client->context, client->fh, 149 sector_num * BDRV_SECTOR_SIZE, 150 nb_sectors * BDRV_SECTOR_SIZE, 151 nfs_co_generic_cb, &task) != 0) { 152 return -ENOMEM; 153 } 154 155 while (!task.complete) { 156 nfs_set_events(client); 157 qemu_coroutine_yield(); 158 } 159 160 if (task.ret < 0) { 161 return task.ret; 162 } 163 164 /* zero pad short reads */ 165 if (task.ret < iov->size) { 166 qemu_iovec_memset(iov, task.ret, 0, iov->size - task.ret); 167 } 168 169 return 0; 170 } 171 172 static int coroutine_fn nfs_co_writev(BlockDriverState *bs, 173 int64_t sector_num, int nb_sectors, 174 QEMUIOVector *iov) 175 { 176 NFSClient *client = bs->opaque; 177 NFSRPC task; 178 char *buf = NULL; 179 180 nfs_co_init_task(client, &task); 181 182 buf = g_try_malloc(nb_sectors * BDRV_SECTOR_SIZE); 183 if (nb_sectors && buf == NULL) { 184 return -ENOMEM; 185 } 186 187 qemu_iovec_to_buf(iov, 0, buf, nb_sectors * BDRV_SECTOR_SIZE); 188 189 if (nfs_pwrite_async(client->context, client->fh, 190 sector_num * BDRV_SECTOR_SIZE, 191 nb_sectors * BDRV_SECTOR_SIZE, 192 buf, nfs_co_generic_cb, &task) != 0) { 193 g_free(buf); 194 return -ENOMEM; 195 } 196 197 while (!task.complete) { 198 nfs_set_events(client); 199 qemu_coroutine_yield(); 200 } 201 202 g_free(buf); 203 204 if (task.ret != nb_sectors * BDRV_SECTOR_SIZE) { 205 return task.ret < 0 ? task.ret : -EIO; 206 } 207 208 return 0; 209 } 210 211 static int coroutine_fn nfs_co_flush(BlockDriverState *bs) 212 { 213 NFSClient *client = bs->opaque; 214 NFSRPC task; 215 216 nfs_co_init_task(client, &task); 217 218 if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb, 219 &task) != 0) { 220 return -ENOMEM; 221 } 222 223 while (!task.complete) { 224 nfs_set_events(client); 225 qemu_coroutine_yield(); 226 } 227 228 return task.ret; 229 } 230 231 /* TODO Convert to fine grained options */ 232 static QemuOptsList runtime_opts = { 233 .name = "nfs", 234 .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), 235 .desc = { 236 { 237 .name = "filename", 238 .type = QEMU_OPT_STRING, 239 .help = "URL to the NFS file", 240 }, 241 { /* end of list */ } 242 }, 243 }; 244 245 static void nfs_detach_aio_context(BlockDriverState *bs) 246 { 247 NFSClient *client = bs->opaque; 248 249 aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), 250 false, NULL, NULL, NULL); 251 client->events = 0; 252 } 253 254 static void nfs_attach_aio_context(BlockDriverState *bs, 255 AioContext *new_context) 256 { 257 NFSClient *client = bs->opaque; 258 259 client->aio_context = new_context; 260 nfs_set_events(client); 261 } 262 263 static void nfs_client_close(NFSClient *client) 264 { 265 if (client->context) { 266 if (client->fh) { 267 nfs_close(client->context, client->fh); 268 } 269 aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), 270 false, NULL, NULL, NULL); 271 nfs_destroy_context(client->context); 272 } 273 memset(client, 0, sizeof(NFSClient)); 274 } 275 276 static void nfs_file_close(BlockDriverState *bs) 277 { 278 NFSClient *client = bs->opaque; 279 nfs_client_close(client); 280 } 281 282 static int64_t nfs_client_open(NFSClient *client, const char *filename, 283 int flags, Error **errp, int open_flags) 284 { 285 int ret = -EINVAL, i; 286 struct stat st; 287 URI *uri; 288 QueryParams *qp = NULL; 289 char *file = NULL, *strp = NULL; 290 291 uri = uri_parse(filename); 292 if (!uri) { 293 error_setg(errp, "Invalid URL specified"); 294 goto fail; 295 } 296 if (!uri->server) { 297 error_setg(errp, "Invalid URL specified"); 298 goto fail; 299 } 300 strp = strrchr(uri->path, '/'); 301 if (strp == NULL) { 302 error_setg(errp, "Invalid URL specified"); 303 goto fail; 304 } 305 file = g_strdup(strp); 306 *strp = 0; 307 308 client->context = nfs_init_context(); 309 if (client->context == NULL) { 310 error_setg(errp, "Failed to init NFS context"); 311 goto fail; 312 } 313 314 qp = query_params_parse(uri->query); 315 for (i = 0; i < qp->n; i++) { 316 unsigned long long val; 317 if (!qp->p[i].value) { 318 error_setg(errp, "Value for NFS parameter expected: %s", 319 qp->p[i].name); 320 goto fail; 321 } 322 if (parse_uint_full(qp->p[i].value, &val, 0)) { 323 error_setg(errp, "Illegal value for NFS parameter: %s", 324 qp->p[i].name); 325 goto fail; 326 } 327 if (!strcmp(qp->p[i].name, "uid")) { 328 nfs_set_uid(client->context, val); 329 } else if (!strcmp(qp->p[i].name, "gid")) { 330 nfs_set_gid(client->context, val); 331 } else if (!strcmp(qp->p[i].name, "tcp-syncnt")) { 332 nfs_set_tcp_syncnt(client->context, val); 333 #ifdef LIBNFS_FEATURE_READAHEAD 334 } else if (!strcmp(qp->p[i].name, "readahead")) { 335 if (open_flags & BDRV_O_NOCACHE) { 336 error_setg(errp, "Cannot enable NFS readahead " 337 "if cache.direct = on"); 338 goto fail; 339 } 340 if (val > QEMU_NFS_MAX_READAHEAD_SIZE) { 341 error_report("NFS Warning: Truncating NFS readahead" 342 " size to %d", QEMU_NFS_MAX_READAHEAD_SIZE); 343 val = QEMU_NFS_MAX_READAHEAD_SIZE; 344 } 345 nfs_set_readahead(client->context, val); 346 #ifdef LIBNFS_FEATURE_PAGECACHE 347 nfs_set_pagecache_ttl(client->context, 0); 348 #endif 349 client->cache_used = true; 350 #endif 351 #ifdef LIBNFS_FEATURE_PAGECACHE 352 nfs_set_pagecache_ttl(client->context, 0); 353 } else if (!strcmp(qp->p[i].name, "pagecache")) { 354 if (open_flags & BDRV_O_NOCACHE) { 355 error_setg(errp, "Cannot enable NFS pagecache " 356 "if cache.direct = on"); 357 goto fail; 358 } 359 if (val > QEMU_NFS_MAX_PAGECACHE_SIZE) { 360 error_report("NFS Warning: Truncating NFS pagecache" 361 " size to %d pages", QEMU_NFS_MAX_PAGECACHE_SIZE); 362 val = QEMU_NFS_MAX_PAGECACHE_SIZE; 363 } 364 nfs_set_pagecache(client->context, val); 365 nfs_set_pagecache_ttl(client->context, 0); 366 client->cache_used = true; 367 #endif 368 #ifdef LIBNFS_FEATURE_DEBUG 369 } else if (!strcmp(qp->p[i].name, "debug")) { 370 /* limit the maximum debug level to avoid potential flooding 371 * of our log files. */ 372 if (val > QEMU_NFS_MAX_DEBUG_LEVEL) { 373 error_report("NFS Warning: Limiting NFS debug level" 374 " to %d", QEMU_NFS_MAX_DEBUG_LEVEL); 375 val = QEMU_NFS_MAX_DEBUG_LEVEL; 376 } 377 nfs_set_debug(client->context, val); 378 #endif 379 } else { 380 error_setg(errp, "Unknown NFS parameter name: %s", 381 qp->p[i].name); 382 goto fail; 383 } 384 } 385 386 ret = nfs_mount(client->context, uri->server, uri->path); 387 if (ret < 0) { 388 error_setg(errp, "Failed to mount nfs share: %s", 389 nfs_get_error(client->context)); 390 goto fail; 391 } 392 393 if (flags & O_CREAT) { 394 ret = nfs_creat(client->context, file, 0600, &client->fh); 395 if (ret < 0) { 396 error_setg(errp, "Failed to create file: %s", 397 nfs_get_error(client->context)); 398 goto fail; 399 } 400 } else { 401 ret = nfs_open(client->context, file, flags, &client->fh); 402 if (ret < 0) { 403 error_setg(errp, "Failed to open file : %s", 404 nfs_get_error(client->context)); 405 goto fail; 406 } 407 } 408 409 ret = nfs_fstat(client->context, client->fh, &st); 410 if (ret < 0) { 411 error_setg(errp, "Failed to fstat file: %s", 412 nfs_get_error(client->context)); 413 goto fail; 414 } 415 416 ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE); 417 client->st_blocks = st.st_blocks; 418 client->has_zero_init = S_ISREG(st.st_mode); 419 goto out; 420 fail: 421 nfs_client_close(client); 422 out: 423 if (qp) { 424 query_params_free(qp); 425 } 426 uri_free(uri); 427 g_free(file); 428 return ret; 429 } 430 431 static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags, 432 Error **errp) { 433 NFSClient *client = bs->opaque; 434 int64_t ret; 435 QemuOpts *opts; 436 Error *local_err = NULL; 437 438 client->aio_context = bdrv_get_aio_context(bs); 439 440 opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); 441 qemu_opts_absorb_qdict(opts, options, &local_err); 442 if (local_err) { 443 error_propagate(errp, local_err); 444 ret = -EINVAL; 445 goto out; 446 } 447 ret = nfs_client_open(client, qemu_opt_get(opts, "filename"), 448 (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY, 449 errp, bs->open_flags); 450 if (ret < 0) { 451 goto out; 452 } 453 bs->total_sectors = ret; 454 ret = 0; 455 out: 456 qemu_opts_del(opts); 457 return ret; 458 } 459 460 static QemuOptsList nfs_create_opts = { 461 .name = "nfs-create-opts", 462 .head = QTAILQ_HEAD_INITIALIZER(nfs_create_opts.head), 463 .desc = { 464 { 465 .name = BLOCK_OPT_SIZE, 466 .type = QEMU_OPT_SIZE, 467 .help = "Virtual disk size" 468 }, 469 { /* end of list */ } 470 } 471 }; 472 473 static int nfs_file_create(const char *url, QemuOpts *opts, Error **errp) 474 { 475 int ret = 0; 476 int64_t total_size = 0; 477 NFSClient *client = g_new0(NFSClient, 1); 478 479 client->aio_context = qemu_get_aio_context(); 480 481 /* Read out options */ 482 total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), 483 BDRV_SECTOR_SIZE); 484 485 ret = nfs_client_open(client, url, O_CREAT, errp, 0); 486 if (ret < 0) { 487 goto out; 488 } 489 ret = nfs_ftruncate(client->context, client->fh, total_size); 490 nfs_client_close(client); 491 out: 492 g_free(client); 493 return ret; 494 } 495 496 static int nfs_has_zero_init(BlockDriverState *bs) 497 { 498 NFSClient *client = bs->opaque; 499 return client->has_zero_init; 500 } 501 502 static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) 503 { 504 NFSClient *client = bs->opaque; 505 NFSRPC task = {0}; 506 struct stat st; 507 508 if (bdrv_is_read_only(bs) && 509 !(bs->open_flags & BDRV_O_NOCACHE)) { 510 return client->st_blocks * 512; 511 } 512 513 task.st = &st; 514 if (nfs_fstat_async(client->context, client->fh, nfs_co_generic_cb, 515 &task) != 0) { 516 return -ENOMEM; 517 } 518 519 while (!task.complete) { 520 nfs_set_events(client); 521 aio_poll(client->aio_context, true); 522 } 523 524 return (task.ret < 0 ? task.ret : st.st_blocks * 512); 525 } 526 527 static int nfs_file_truncate(BlockDriverState *bs, int64_t offset) 528 { 529 NFSClient *client = bs->opaque; 530 return nfs_ftruncate(client->context, client->fh, offset); 531 } 532 533 /* Note that this will not re-establish a connection with the NFS server 534 * - it is effectively a NOP. */ 535 static int nfs_reopen_prepare(BDRVReopenState *state, 536 BlockReopenQueue *queue, Error **errp) 537 { 538 NFSClient *client = state->bs->opaque; 539 struct stat st; 540 int ret = 0; 541 542 if (state->flags & BDRV_O_RDWR && bdrv_is_read_only(state->bs)) { 543 error_setg(errp, "Cannot open a read-only mount as read-write"); 544 return -EACCES; 545 } 546 547 if ((state->flags & BDRV_O_NOCACHE) && client->cache_used) { 548 error_setg(errp, "Cannot disable cache if libnfs readahead or" 549 " pagecache is enabled"); 550 return -EINVAL; 551 } 552 553 /* Update cache for read-only reopens */ 554 if (!(state->flags & BDRV_O_RDWR)) { 555 ret = nfs_fstat(client->context, client->fh, &st); 556 if (ret < 0) { 557 error_setg(errp, "Failed to fstat file: %s", 558 nfs_get_error(client->context)); 559 return ret; 560 } 561 client->st_blocks = st.st_blocks; 562 } 563 564 return 0; 565 } 566 567 #ifdef LIBNFS_FEATURE_PAGECACHE 568 static void nfs_invalidate_cache(BlockDriverState *bs, 569 Error **errp) 570 { 571 NFSClient *client = bs->opaque; 572 nfs_pagecache_invalidate(client->context, client->fh); 573 } 574 #endif 575 576 static BlockDriver bdrv_nfs = { 577 .format_name = "nfs", 578 .protocol_name = "nfs", 579 580 .instance_size = sizeof(NFSClient), 581 .bdrv_needs_filename = true, 582 .create_opts = &nfs_create_opts, 583 584 .bdrv_has_zero_init = nfs_has_zero_init, 585 .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, 586 .bdrv_truncate = nfs_file_truncate, 587 588 .bdrv_file_open = nfs_file_open, 589 .bdrv_close = nfs_file_close, 590 .bdrv_create = nfs_file_create, 591 .bdrv_reopen_prepare = nfs_reopen_prepare, 592 593 .bdrv_co_readv = nfs_co_readv, 594 .bdrv_co_writev = nfs_co_writev, 595 .bdrv_co_flush_to_disk = nfs_co_flush, 596 597 .bdrv_detach_aio_context = nfs_detach_aio_context, 598 .bdrv_attach_aio_context = nfs_attach_aio_context, 599 600 #ifdef LIBNFS_FEATURE_PAGECACHE 601 .bdrv_invalidate_cache = nfs_invalidate_cache, 602 #endif 603 }; 604 605 static void nfs_block_init(void) 606 { 607 bdrv_register(&bdrv_nfs); 608 } 609 610 block_init(nfs_block_init); 611