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/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
  56typedef 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
  70typedef 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
  80static 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;
 147out:
 148    if (qp) {
 149        query_params_free(qp);
 150    }
 151    if (uri) {
 152        uri_free(uri);
 153    }
 154    return ret;
 155}
 156
 157static 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
 181static 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
 191static void nfs_process_read(void *arg);
 192static void nfs_process_write(void *arg);
 193
 194/* Called with QemuMutex held.  */
 195static 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
 209static 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
 219static 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
 229static 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
 238static 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.  */
 247static void
 248nfs_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
 268static 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
 302static 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
 351static 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
 373static 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
 382static 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
 391static 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
 414static void nfs_file_close(BlockDriverState *bs)
 415{
 416    NFSClient *client = bs->opaque;
 417    nfs_client_close(client);
 418}
 419
 420static 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
 557fail:
 558    nfs_client_close(client);
 559out:
 560    g_free(file);
 561    return ret;
 562}
 563
 564static 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
 591static 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);
 604fail:
 605    qapi_free_BlockdevOptionsNfs(opts);
 606    return ret;
 607}
 608
 609static 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
 630static 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
 643static 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
 660out:
 661    g_free(client);
 662    return ret;
 663}
 664
 665static 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;
 701out:
 702    qobject_unref(options);
 703    qapi_free_BlockdevCreateOptions(create_options);
 704    return ret;
 705}
 706
 707static 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.  */
 715static void
 716nfs_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
 733static 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
 758static int coroutine_fn
 759nfs_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.  */
 783static 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
 817static 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
 839static 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
 854static 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
 862static const char *nfs_strong_runtime_opts[] = {
 863    "path",
 864    "user",
 865    "group",
 866    "server.",
 867
 868    NULL
 869};
 870
 871static 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
 908static void nfs_block_init(void)
 909{
 910    bdrv_register(&bdrv_nfs);
 911}
 912
 913block_init(nfs_block_init);
 914