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_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, uint64_t offset,
 266                                      uint64_t bytes, QEMUIOVector *iov,
 267                                      int 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, uint64_t offset,
 300                                       uint64_t bytes, QEMUIOVector *iov,
 301                                       int 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);
 911