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