cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

nfs.c (26139B)


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