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