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