1 /* 2 * QEMU Block driver for native access to files on NFS shares 3 * 4 * Copyright (c) 2014-2017 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 #if !defined(_WIN32) 28 #include <poll.h> 29 #endif 30 #include "qemu/config-file.h" 31 #include "qemu/error-report.h" 32 #include "qapi/error.h" 33 #include "block/block_int.h" 34 #include "block/qdict.h" 35 #include "trace.h" 36 #include "qemu/iov.h" 37 #include "qemu/main-loop.h" 38 #include "qemu/module.h" 39 #include "qemu/option.h" 40 #include "qemu/uri.h" 41 #include "qemu/cutils.h" 42 #include "sysemu/sysemu.h" 43 #include "sysemu/replay.h" 44 #include "qapi/qapi-visit-block-core.h" 45 #include "qapi/qmp/qdict.h" 46 #include "qapi/qmp/qstring.h" 47 #include "qapi/qobject-input-visitor.h" 48 #include "qapi/qobject-output-visitor.h" 49 #include <nfsc/libnfs.h> 50 51 52 #define QEMU_NFS_MAX_READAHEAD_SIZE 1048576 53 #define QEMU_NFS_MAX_PAGECACHE_SIZE (8388608 / NFS_BLKSIZE) 54 #define QEMU_NFS_MAX_DEBUG_LEVEL 2 55 56 typedef struct NFSClient { 57 struct nfs_context *context; 58 struct nfsfh *fh; 59 int events; 60 bool has_zero_init; 61 AioContext *aio_context; 62 QemuMutex mutex; 63 uint64_t st_blocks; 64 bool cache_used; 65 NFSServer *server; 66 char *path; 67 int64_t uid, gid, tcp_syncnt, readahead, pagecache, debug; 68 } NFSClient; 69 70 typedef struct NFSRPC { 71 BlockDriverState *bs; 72 int ret; 73 int complete; 74 QEMUIOVector *iov; 75 struct stat *st; 76 Coroutine *co; 77 NFSClient *client; 78 } NFSRPC; 79 80 static int nfs_parse_uri(const char *filename, QDict *options, Error **errp) 81 { 82 URI *uri = NULL; 83 QueryParams *qp = NULL; 84 int ret = -EINVAL, i; 85 86 uri = uri_parse(filename); 87 if (!uri) { 88 error_setg(errp, "Invalid URI specified"); 89 goto out; 90 } 91 if (g_strcmp0(uri->scheme, "nfs") != 0) { 92 error_setg(errp, "URI scheme must be 'nfs'"); 93 goto out; 94 } 95 96 if (!uri->server) { 97 error_setg(errp, "missing hostname in URI"); 98 goto out; 99 } 100 101 if (!uri->path) { 102 error_setg(errp, "missing file path in URI"); 103 goto out; 104 } 105 106 qp = query_params_parse(uri->query); 107 if (!qp) { 108 error_setg(errp, "could not parse query parameters"); 109 goto out; 110 } 111 112 qdict_put_str(options, "server.host", uri->server); 113 qdict_put_str(options, "server.type", "inet"); 114 qdict_put_str(options, "path", uri->path); 115 116 for (i = 0; i < qp->n; i++) { 117 unsigned long long val; 118 if (!qp->p[i].value) { 119 error_setg(errp, "Value for NFS parameter expected: %s", 120 qp->p[i].name); 121 goto out; 122 } 123 if (parse_uint_full(qp->p[i].value, &val, 0)) { 124 error_setg(errp, "Illegal value for NFS parameter: %s", 125 qp->p[i].name); 126 goto out; 127 } 128 if (!strcmp(qp->p[i].name, "uid")) { 129 qdict_put_str(options, "user", qp->p[i].value); 130 } else if (!strcmp(qp->p[i].name, "gid")) { 131 qdict_put_str(options, "group", qp->p[i].value); 132 } else if (!strcmp(qp->p[i].name, "tcp-syncnt")) { 133 qdict_put_str(options, "tcp-syn-count", qp->p[i].value); 134 } else if (!strcmp(qp->p[i].name, "readahead")) { 135 qdict_put_str(options, "readahead-size", qp->p[i].value); 136 } else if (!strcmp(qp->p[i].name, "pagecache")) { 137 qdict_put_str(options, "page-cache-size", qp->p[i].value); 138 } else if (!strcmp(qp->p[i].name, "debug")) { 139 qdict_put_str(options, "debug", qp->p[i].value); 140 } else { 141 error_setg(errp, "Unknown NFS parameter name: %s", 142 qp->p[i].name); 143 goto out; 144 } 145 } 146 ret = 0; 147 out: 148 if (qp) { 149 query_params_free(qp); 150 } 151 if (uri) { 152 uri_free(uri); 153 } 154 return ret; 155 } 156 157 static bool nfs_has_filename_options_conflict(QDict *options, Error **errp) 158 { 159 const QDictEntry *qe; 160 161 for (qe = qdict_first(options); qe; qe = qdict_next(options, qe)) { 162 if (!strcmp(qe->key, "host") || 163 !strcmp(qe->key, "path") || 164 !strcmp(qe->key, "user") || 165 !strcmp(qe->key, "group") || 166 !strcmp(qe->key, "tcp-syn-count") || 167 !strcmp(qe->key, "readahead-size") || 168 !strcmp(qe->key, "page-cache-size") || 169 !strcmp(qe->key, "debug") || 170 strstart(qe->key, "server.", NULL)) 171 { 172 error_setg(errp, "Option %s cannot be used with a filename", 173 qe->key); 174 return true; 175 } 176 } 177 178 return false; 179 } 180 181 static void nfs_parse_filename(const char *filename, QDict *options, 182 Error **errp) 183 { 184 if (nfs_has_filename_options_conflict(options, errp)) { 185 return; 186 } 187 188 nfs_parse_uri(filename, options, errp); 189 } 190 191 static void nfs_process_read(void *arg); 192 static void nfs_process_write(void *arg); 193 194 /* Called with QemuMutex held. */ 195 static void nfs_set_events(NFSClient *client) 196 { 197 int ev = nfs_which_events(client->context); 198 if (ev != client->events) { 199 aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), 200 false, 201 (ev & POLLIN) ? nfs_process_read : NULL, 202 (ev & POLLOUT) ? nfs_process_write : NULL, 203 NULL, client); 204 205 } 206 client->events = ev; 207 } 208 209 static void nfs_process_read(void *arg) 210 { 211 NFSClient *client = arg; 212 213 qemu_mutex_lock(&client->mutex); 214 nfs_service(client->context, POLLIN); 215 nfs_set_events(client); 216 qemu_mutex_unlock(&client->mutex); 217 } 218 219 static void nfs_process_write(void *arg) 220 { 221 NFSClient *client = arg; 222 223 qemu_mutex_lock(&client->mutex); 224 nfs_service(client->context, POLLOUT); 225 nfs_set_events(client); 226 qemu_mutex_unlock(&client->mutex); 227 } 228 229 static void nfs_co_init_task(BlockDriverState *bs, NFSRPC *task) 230 { 231 *task = (NFSRPC) { 232 .co = qemu_coroutine_self(), 233 .bs = bs, 234 .client = bs->opaque, 235 }; 236 } 237 238 static void nfs_co_generic_bh_cb(void *opaque) 239 { 240 NFSRPC *task = opaque; 241 242 task->complete = 1; 243 aio_co_wake(task->co); 244 } 245 246 /* Called (via nfs_service) with QemuMutex held. */ 247 static void 248 nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, 249 void *private_data) 250 { 251 NFSRPC *task = private_data; 252 task->ret = ret; 253 assert(!task->st); 254 if (task->ret > 0 && task->iov) { 255 if (task->ret <= task->iov->size) { 256 qemu_iovec_from_buf(task->iov, 0, data, task->ret); 257 } else { 258 task->ret = -EIO; 259 } 260 } 261 if (task->ret < 0) { 262 error_report("NFS Error: %s", nfs_get_error(nfs)); 263 } 264 replay_bh_schedule_oneshot_event(task->client->aio_context, 265 nfs_co_generic_bh_cb, task); 266 } 267 268 static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, uint64_t offset, 269 uint64_t bytes, QEMUIOVector *iov, 270 int flags) 271 { 272 NFSClient *client = bs->opaque; 273 NFSRPC task; 274 275 nfs_co_init_task(bs, &task); 276 task.iov = iov; 277 278 WITH_QEMU_LOCK_GUARD(&client->mutex) { 279 if (nfs_pread_async(client->context, client->fh, 280 offset, bytes, nfs_co_generic_cb, &task) != 0) { 281 return -ENOMEM; 282 } 283 284 nfs_set_events(client); 285 } 286 while (!task.complete) { 287 qemu_coroutine_yield(); 288 } 289 290 if (task.ret < 0) { 291 return task.ret; 292 } 293 294 /* zero pad short reads */ 295 if (task.ret < iov->size) { 296 qemu_iovec_memset(iov, task.ret, 0, iov->size - task.ret); 297 } 298 299 return 0; 300 } 301 302 static int coroutine_fn nfs_co_pwritev(BlockDriverState *bs, uint64_t offset, 303 uint64_t bytes, QEMUIOVector *iov, 304 int flags) 305 { 306 NFSClient *client = bs->opaque; 307 NFSRPC task; 308 char *buf = NULL; 309 bool my_buffer = false; 310 311 nfs_co_init_task(bs, &task); 312 313 if (iov->niov != 1) { 314 buf = g_try_malloc(bytes); 315 if (bytes && buf == NULL) { 316 return -ENOMEM; 317 } 318 qemu_iovec_to_buf(iov, 0, buf, bytes); 319 my_buffer = true; 320 } else { 321 buf = iov->iov[0].iov_base; 322 } 323 324 WITH_QEMU_LOCK_GUARD(&client->mutex) { 325 if (nfs_pwrite_async(client->context, client->fh, 326 offset, bytes, buf, 327 nfs_co_generic_cb, &task) != 0) { 328 if (my_buffer) { 329 g_free(buf); 330 } 331 return -ENOMEM; 332 } 333 334 nfs_set_events(client); 335 } 336 while (!task.complete) { 337 qemu_coroutine_yield(); 338 } 339 340 if (my_buffer) { 341 g_free(buf); 342 } 343 344 if (task.ret != bytes) { 345 return task.ret < 0 ? task.ret : -EIO; 346 } 347 348 return 0; 349 } 350 351 static int coroutine_fn nfs_co_flush(BlockDriverState *bs) 352 { 353 NFSClient *client = bs->opaque; 354 NFSRPC task; 355 356 nfs_co_init_task(bs, &task); 357 358 WITH_QEMU_LOCK_GUARD(&client->mutex) { 359 if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb, 360 &task) != 0) { 361 return -ENOMEM; 362 } 363 364 nfs_set_events(client); 365 } 366 while (!task.complete) { 367 qemu_coroutine_yield(); 368 } 369 370 return task.ret; 371 } 372 373 static void nfs_detach_aio_context(BlockDriverState *bs) 374 { 375 NFSClient *client = bs->opaque; 376 377 aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), 378 false, NULL, NULL, NULL, NULL); 379 client->events = 0; 380 } 381 382 static void nfs_attach_aio_context(BlockDriverState *bs, 383 AioContext *new_context) 384 { 385 NFSClient *client = bs->opaque; 386 387 client->aio_context = new_context; 388 nfs_set_events(client); 389 } 390 391 static void nfs_client_close(NFSClient *client) 392 { 393 if (client->context) { 394 qemu_mutex_lock(&client->mutex); 395 aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), 396 false, NULL, NULL, NULL, NULL); 397 qemu_mutex_unlock(&client->mutex); 398 if (client->fh) { 399 nfs_close(client->context, client->fh); 400 client->fh = NULL; 401 } 402 #ifdef LIBNFS_FEATURE_UMOUNT 403 nfs_umount(client->context); 404 #endif 405 nfs_destroy_context(client->context); 406 client->context = NULL; 407 } 408 g_free(client->path); 409 qemu_mutex_destroy(&client->mutex); 410 qapi_free_NFSServer(client->server); 411 client->server = NULL; 412 } 413 414 static void nfs_file_close(BlockDriverState *bs) 415 { 416 NFSClient *client = bs->opaque; 417 nfs_client_close(client); 418 } 419 420 static int64_t nfs_client_open(NFSClient *client, BlockdevOptionsNfs *opts, 421 int flags, int open_flags, Error **errp) 422 { 423 int64_t ret = -EINVAL; 424 struct stat st; 425 char *file = NULL, *strp = NULL; 426 427 qemu_mutex_init(&client->mutex); 428 429 client->path = g_strdup(opts->path); 430 431 strp = strrchr(client->path, '/'); 432 if (strp == NULL) { 433 error_setg(errp, "Invalid URL specified"); 434 goto fail; 435 } 436 file = g_strdup(strp); 437 *strp = 0; 438 439 /* Steal the NFSServer object from opts; set the original pointer to NULL 440 * to avoid use after free and double free. */ 441 client->server = opts->server; 442 opts->server = NULL; 443 444 client->context = nfs_init_context(); 445 if (client->context == NULL) { 446 error_setg(errp, "Failed to init NFS context"); 447 goto fail; 448 } 449 450 if (opts->has_user) { 451 client->uid = opts->user; 452 nfs_set_uid(client->context, client->uid); 453 } 454 455 if (opts->has_group) { 456 client->gid = opts->group; 457 nfs_set_gid(client->context, client->gid); 458 } 459 460 if (opts->has_tcp_syn_count) { 461 client->tcp_syncnt = opts->tcp_syn_count; 462 nfs_set_tcp_syncnt(client->context, client->tcp_syncnt); 463 } 464 465 #ifdef LIBNFS_FEATURE_READAHEAD 466 if (opts->has_readahead_size) { 467 if (open_flags & BDRV_O_NOCACHE) { 468 error_setg(errp, "Cannot enable NFS readahead " 469 "if cache.direct = on"); 470 goto fail; 471 } 472 client->readahead = opts->readahead_size; 473 if (client->readahead > QEMU_NFS_MAX_READAHEAD_SIZE) { 474 warn_report("Truncating NFS readahead size to %d", 475 QEMU_NFS_MAX_READAHEAD_SIZE); 476 client->readahead = QEMU_NFS_MAX_READAHEAD_SIZE; 477 } 478 nfs_set_readahead(client->context, client->readahead); 479 #ifdef LIBNFS_FEATURE_PAGECACHE 480 nfs_set_pagecache_ttl(client->context, 0); 481 #endif 482 client->cache_used = true; 483 } 484 #endif 485 486 #ifdef LIBNFS_FEATURE_PAGECACHE 487 if (opts->has_page_cache_size) { 488 if (open_flags & BDRV_O_NOCACHE) { 489 error_setg(errp, "Cannot enable NFS pagecache " 490 "if cache.direct = on"); 491 goto fail; 492 } 493 client->pagecache = opts->page_cache_size; 494 if (client->pagecache > QEMU_NFS_MAX_PAGECACHE_SIZE) { 495 warn_report("Truncating NFS pagecache size to %d pages", 496 QEMU_NFS_MAX_PAGECACHE_SIZE); 497 client->pagecache = QEMU_NFS_MAX_PAGECACHE_SIZE; 498 } 499 nfs_set_pagecache(client->context, client->pagecache); 500 nfs_set_pagecache_ttl(client->context, 0); 501 client->cache_used = true; 502 } 503 #endif 504 505 #ifdef LIBNFS_FEATURE_DEBUG 506 if (opts->has_debug) { 507 client->debug = opts->debug; 508 /* limit the maximum debug level to avoid potential flooding 509 * of our log files. */ 510 if (client->debug > QEMU_NFS_MAX_DEBUG_LEVEL) { 511 warn_report("Limiting NFS debug level to %d", 512 QEMU_NFS_MAX_DEBUG_LEVEL); 513 client->debug = QEMU_NFS_MAX_DEBUG_LEVEL; 514 } 515 nfs_set_debug(client->context, client->debug); 516 } 517 #endif 518 519 ret = nfs_mount(client->context, client->server->host, client->path); 520 if (ret < 0) { 521 error_setg(errp, "Failed to mount nfs share: %s", 522 nfs_get_error(client->context)); 523 goto fail; 524 } 525 526 if (flags & O_CREAT) { 527 ret = nfs_creat(client->context, file, 0600, &client->fh); 528 if (ret < 0) { 529 error_setg(errp, "Failed to create file: %s", 530 nfs_get_error(client->context)); 531 goto fail; 532 } 533 } else { 534 ret = nfs_open(client->context, file, flags, &client->fh); 535 if (ret < 0) { 536 error_setg(errp, "Failed to open file : %s", 537 nfs_get_error(client->context)); 538 goto fail; 539 } 540 } 541 542 ret = nfs_fstat(client->context, client->fh, &st); 543 if (ret < 0) { 544 error_setg(errp, "Failed to fstat file: %s", 545 nfs_get_error(client->context)); 546 goto fail; 547 } 548 549 ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE); 550 #if !defined(_WIN32) 551 client->st_blocks = st.st_blocks; 552 #endif 553 client->has_zero_init = S_ISREG(st.st_mode); 554 *strp = '/'; 555 goto out; 556 557 fail: 558 nfs_client_close(client); 559 out: 560 g_free(file); 561 return ret; 562 } 563 564 static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, 565 Error **errp) 566 { 567 BlockdevOptionsNfs *opts = NULL; 568 Visitor *v; 569 const QDictEntry *e; 570 571 v = qobject_input_visitor_new_flat_confused(options, errp); 572 if (!v) { 573 return NULL; 574 } 575 576 visit_type_BlockdevOptionsNfs(v, NULL, &opts, errp); 577 visit_free(v); 578 if (!opts) { 579 return NULL; 580 } 581 582 /* Remove the processed options from the QDict (the visitor processes 583 * _all_ options in the QDict) */ 584 while ((e = qdict_first(options))) { 585 qdict_del(options, e->key); 586 } 587 588 return opts; 589 } 590 591 static int64_t nfs_client_open_qdict(NFSClient *client, QDict *options, 592 int flags, int open_flags, Error **errp) 593 { 594 BlockdevOptionsNfs *opts; 595 int64_t ret; 596 597 opts = nfs_options_qdict_to_qapi(options, errp); 598 if (opts == NULL) { 599 ret = -EINVAL; 600 goto fail; 601 } 602 603 ret = nfs_client_open(client, opts, flags, open_flags, errp); 604 fail: 605 qapi_free_BlockdevOptionsNfs(opts); 606 return ret; 607 } 608 609 static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags, 610 Error **errp) { 611 NFSClient *client = bs->opaque; 612 int64_t ret; 613 614 client->aio_context = bdrv_get_aio_context(bs); 615 616 ret = nfs_client_open_qdict(client, options, 617 (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY, 618 bs->open_flags, errp); 619 if (ret < 0) { 620 return ret; 621 } 622 623 bs->total_sectors = ret; 624 if (client->has_zero_init) { 625 bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; 626 } 627 return 0; 628 } 629 630 static QemuOptsList nfs_create_opts = { 631 .name = "nfs-create-opts", 632 .head = QTAILQ_HEAD_INITIALIZER(nfs_create_opts.head), 633 .desc = { 634 { 635 .name = BLOCK_OPT_SIZE, 636 .type = QEMU_OPT_SIZE, 637 .help = "Virtual disk size" 638 }, 639 { /* end of list */ } 640 } 641 }; 642 643 static int nfs_file_co_create(BlockdevCreateOptions *options, Error **errp) 644 { 645 BlockdevCreateOptionsNfs *opts = &options->u.nfs; 646 NFSClient *client = g_new0(NFSClient, 1); 647 int ret; 648 649 assert(options->driver == BLOCKDEV_DRIVER_NFS); 650 651 client->aio_context = qemu_get_aio_context(); 652 653 ret = nfs_client_open(client, opts->location, O_CREAT, 0, errp); 654 if (ret < 0) { 655 goto out; 656 } 657 ret = nfs_ftruncate(client->context, client->fh, opts->size); 658 nfs_client_close(client); 659 660 out: 661 g_free(client); 662 return ret; 663 } 664 665 static int coroutine_fn nfs_file_co_create_opts(BlockDriver *drv, 666 const char *url, 667 QemuOpts *opts, 668 Error **errp) 669 { 670 BlockdevCreateOptions *create_options; 671 BlockdevCreateOptionsNfs *nfs_opts; 672 QDict *options; 673 int ret; 674 675 create_options = g_new0(BlockdevCreateOptions, 1); 676 create_options->driver = BLOCKDEV_DRIVER_NFS; 677 nfs_opts = &create_options->u.nfs; 678 679 /* Read out options */ 680 nfs_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), 681 BDRV_SECTOR_SIZE); 682 683 options = qdict_new(); 684 ret = nfs_parse_uri(url, options, errp); 685 if (ret < 0) { 686 goto out; 687 } 688 689 nfs_opts->location = nfs_options_qdict_to_qapi(options, errp); 690 if (nfs_opts->location == NULL) { 691 ret = -EINVAL; 692 goto out; 693 } 694 695 ret = nfs_file_co_create(create_options, errp); 696 if (ret < 0) { 697 goto out; 698 } 699 700 ret = 0; 701 out: 702 qobject_unref(options); 703 qapi_free_BlockdevCreateOptions(create_options); 704 return ret; 705 } 706 707 static int nfs_has_zero_init(BlockDriverState *bs) 708 { 709 NFSClient *client = bs->opaque; 710 return client->has_zero_init; 711 } 712 713 #if !defined(_WIN32) 714 /* Called (via nfs_service) with QemuMutex held. */ 715 static void 716 nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data, 717 void *private_data) 718 { 719 NFSRPC *task = private_data; 720 task->ret = ret; 721 if (task->ret == 0) { 722 memcpy(task->st, data, sizeof(struct stat)); 723 } 724 if (task->ret < 0) { 725 error_report("NFS Error: %s", nfs_get_error(nfs)); 726 } 727 728 /* Set task->complete before reading bs->wakeup. */ 729 qatomic_mb_set(&task->complete, 1); 730 bdrv_wakeup(task->bs); 731 } 732 733 static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) 734 { 735 NFSClient *client = bs->opaque; 736 NFSRPC task = {0}; 737 struct stat st; 738 739 if (bdrv_is_read_only(bs) && 740 !(bs->open_flags & BDRV_O_NOCACHE)) { 741 return client->st_blocks * 512; 742 } 743 744 task.bs = bs; 745 task.st = &st; 746 if (nfs_fstat_async(client->context, client->fh, nfs_get_allocated_file_size_cb, 747 &task) != 0) { 748 return -ENOMEM; 749 } 750 751 nfs_set_events(client); 752 BDRV_POLL_WHILE(bs, !task.complete); 753 754 return (task.ret < 0 ? task.ret : st.st_blocks * 512); 755 } 756 #endif 757 758 static int coroutine_fn 759 nfs_file_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, 760 PreallocMode prealloc, BdrvRequestFlags flags, 761 Error **errp) 762 { 763 NFSClient *client = bs->opaque; 764 int ret; 765 766 if (prealloc != PREALLOC_MODE_OFF) { 767 error_setg(errp, "Unsupported preallocation mode '%s'", 768 PreallocMode_str(prealloc)); 769 return -ENOTSUP; 770 } 771 772 ret = nfs_ftruncate(client->context, client->fh, offset); 773 if (ret < 0) { 774 error_setg_errno(errp, -ret, "Failed to truncate file"); 775 return ret; 776 } 777 778 return 0; 779 } 780 781 /* Note that this will not re-establish a connection with the NFS server 782 * - it is effectively a NOP. */ 783 static int nfs_reopen_prepare(BDRVReopenState *state, 784 BlockReopenQueue *queue, Error **errp) 785 { 786 NFSClient *client = state->bs->opaque; 787 struct stat st; 788 int ret = 0; 789 790 if (state->flags & BDRV_O_RDWR && bdrv_is_read_only(state->bs)) { 791 error_setg(errp, "Cannot open a read-only mount as read-write"); 792 return -EACCES; 793 } 794 795 if ((state->flags & BDRV_O_NOCACHE) && client->cache_used) { 796 error_setg(errp, "Cannot disable cache if libnfs readahead or" 797 " pagecache is enabled"); 798 return -EINVAL; 799 } 800 801 /* Update cache for read-only reopens */ 802 if (!(state->flags & BDRV_O_RDWR)) { 803 ret = nfs_fstat(client->context, client->fh, &st); 804 if (ret < 0) { 805 error_setg(errp, "Failed to fstat file: %s", 806 nfs_get_error(client->context)); 807 return ret; 808 } 809 #if !defined(_WIN32) 810 client->st_blocks = st.st_blocks; 811 #endif 812 } 813 814 return 0; 815 } 816 817 static void nfs_refresh_filename(BlockDriverState *bs) 818 { 819 NFSClient *client = bs->opaque; 820 821 if (client->uid && !client->gid) { 822 snprintf(bs->exact_filename, sizeof(bs->exact_filename), 823 "nfs://%s%s?uid=%" PRId64, client->server->host, client->path, 824 client->uid); 825 } else if (!client->uid && client->gid) { 826 snprintf(bs->exact_filename, sizeof(bs->exact_filename), 827 "nfs://%s%s?gid=%" PRId64, client->server->host, client->path, 828 client->gid); 829 } else if (client->uid && client->gid) { 830 snprintf(bs->exact_filename, sizeof(bs->exact_filename), 831 "nfs://%s%s?uid=%" PRId64 "&gid=%" PRId64, 832 client->server->host, client->path, client->uid, client->gid); 833 } else { 834 snprintf(bs->exact_filename, sizeof(bs->exact_filename), 835 "nfs://%s%s", client->server->host, client->path); 836 } 837 } 838 839 static char *nfs_dirname(BlockDriverState *bs, Error **errp) 840 { 841 NFSClient *client = bs->opaque; 842 843 if (client->uid || client->gid) { 844 bdrv_refresh_filename(bs); 845 error_setg(errp, "Cannot generate a base directory for NFS node '%s'", 846 bs->filename); 847 return NULL; 848 } 849 850 return g_strdup_printf("nfs://%s%s/", client->server->host, client->path); 851 } 852 853 #ifdef LIBNFS_FEATURE_PAGECACHE 854 static void coroutine_fn nfs_co_invalidate_cache(BlockDriverState *bs, 855 Error **errp) 856 { 857 NFSClient *client = bs->opaque; 858 nfs_pagecache_invalidate(client->context, client->fh); 859 } 860 #endif 861 862 static const char *nfs_strong_runtime_opts[] = { 863 "path", 864 "user", 865 "group", 866 "server.", 867 868 NULL 869 }; 870 871 static BlockDriver bdrv_nfs = { 872 .format_name = "nfs", 873 .protocol_name = "nfs", 874 875 .instance_size = sizeof(NFSClient), 876 .bdrv_parse_filename = nfs_parse_filename, 877 .create_opts = &nfs_create_opts, 878 879 .bdrv_has_zero_init = nfs_has_zero_init, 880 /* libnfs does not provide the allocated filesize of a file on win32. */ 881 #if !defined(_WIN32) 882 .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, 883 #endif 884 .bdrv_co_truncate = nfs_file_co_truncate, 885 886 .bdrv_file_open = nfs_file_open, 887 .bdrv_close = nfs_file_close, 888 .bdrv_co_create = nfs_file_co_create, 889 .bdrv_co_create_opts = nfs_file_co_create_opts, 890 .bdrv_reopen_prepare = nfs_reopen_prepare, 891 892 .bdrv_co_preadv = nfs_co_preadv, 893 .bdrv_co_pwritev = nfs_co_pwritev, 894 .bdrv_co_flush_to_disk = nfs_co_flush, 895 896 .bdrv_detach_aio_context = nfs_detach_aio_context, 897 .bdrv_attach_aio_context = nfs_attach_aio_context, 898 .bdrv_refresh_filename = nfs_refresh_filename, 899 .bdrv_dirname = nfs_dirname, 900 901 .strong_runtime_opts = nfs_strong_runtime_opts, 902 903 #ifdef LIBNFS_FEATURE_PAGECACHE 904 .bdrv_co_invalidate_cache = nfs_co_invalidate_cache, 905 #endif 906 }; 907 908 static void nfs_block_init(void) 909 { 910 bdrv_register(&bdrv_nfs); 911 } 912 913 block_init(nfs_block_init); 914