16542aa9cSPeter Lieven /*
26542aa9cSPeter Lieven * QEMU Block driver for native access to files on NFS shares
36542aa9cSPeter Lieven *
4f1a7ff77SPeter Lieven * Copyright (c) 2014-2017 Peter Lieven <pl@kamp.de>
56542aa9cSPeter Lieven *
66542aa9cSPeter Lieven * Permission is hereby granted, free of charge, to any person obtaining a copy
76542aa9cSPeter Lieven * of this software and associated documentation files (the "Software"), to deal
86542aa9cSPeter Lieven * in the Software without restriction, including without limitation the rights
96542aa9cSPeter Lieven * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
106542aa9cSPeter Lieven * copies of the Software, and to permit persons to whom the Software is
116542aa9cSPeter Lieven * furnished to do so, subject to the following conditions:
126542aa9cSPeter Lieven *
136542aa9cSPeter Lieven * The above copyright notice and this permission notice shall be included in
146542aa9cSPeter Lieven * all copies or substantial portions of the Software.
156542aa9cSPeter Lieven *
166542aa9cSPeter Lieven * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
176542aa9cSPeter Lieven * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
186542aa9cSPeter Lieven * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
196542aa9cSPeter Lieven * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
206542aa9cSPeter Lieven * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
216542aa9cSPeter Lieven * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
226542aa9cSPeter Lieven * THE SOFTWARE.
236542aa9cSPeter Lieven */
246542aa9cSPeter Lieven
2580c71a24SPeter Maydell #include "qemu/osdep.h"
266542aa9cSPeter Lieven
27c63b0201SYonggang Luo #if !defined(_WIN32)
286542aa9cSPeter Lieven #include <poll.h>
29c63b0201SYonggang Luo #endif
306542aa9cSPeter Lieven #include "qemu/config-file.h"
316542aa9cSPeter Lieven #include "qemu/error-report.h"
32d165b8cbSStefan Hajnoczi #include "qapi/error.h"
33e2c1c34fSMarkus Armbruster #include "block/block-io.h"
346542aa9cSPeter Lieven #include "block/block_int.h"
35609f45eaSMax Reitz #include "block/qdict.h"
366542aa9cSPeter Lieven #include "trace.h"
376542aa9cSPeter Lieven #include "qemu/iov.h"
38db725815SMarkus Armbruster #include "qemu/main-loop.h"
390b8fa32fSMarkus Armbruster #include "qemu/module.h"
40922a01a0SMarkus Armbruster #include "qemu/option.h"
416542aa9cSPeter Lieven #include "qemu/uri.h"
420d94b746SStefan Hajnoczi #include "qemu/cutils.h"
43e4ec5ad4SPavel Dovgalyuk #include "sysemu/replay.h"
449af23989SMarkus Armbruster #include "qapi/qapi-visit-block-core.h"
4594d6a7a7SAshijeet Acharya #include "qapi/qmp/qdict.h"
4694d6a7a7SAshijeet Acharya #include "qapi/qmp/qstring.h"
4794d6a7a7SAshijeet Acharya #include "qapi/qobject-input-visitor.h"
4894d6a7a7SAshijeet Acharya #include "qapi/qobject-output-visitor.h"
496542aa9cSPeter Lieven #include <nfsc/libnfs.h>
506542aa9cSPeter Lieven
5194d6a7a7SAshijeet Acharya
5229c838cdSPeter Lieven #define QEMU_NFS_MAX_READAHEAD_SIZE 1048576
53d99b26c4SPeter Lieven #define QEMU_NFS_MAX_PAGECACHE_SIZE (8388608 / NFS_BLKSIZE)
547725b8bfSPeter Lieven #define QEMU_NFS_MAX_DEBUG_LEVEL 2
5529c838cdSPeter Lieven
566542aa9cSPeter Lieven typedef struct NFSClient {
576542aa9cSPeter Lieven struct nfs_context *context;
586542aa9cSPeter Lieven struct nfsfh *fh;
596542aa9cSPeter Lieven int events;
606542aa9cSPeter Lieven bool has_zero_init;
61471799d1SStefan Hajnoczi AioContext *aio_context;
6237d1e4d9SPaolo Bonzini QemuMutex mutex;
63c63b0201SYonggang Luo uint64_t st_blocks;
6438f8d5e0SPeter Lieven bool cache_used;
6594d6a7a7SAshijeet Acharya NFSServer *server;
6694d6a7a7SAshijeet Acharya char *path;
6794d6a7a7SAshijeet Acharya int64_t uid, gid, tcp_syncnt, readahead, pagecache, debug;
686542aa9cSPeter Lieven } NFSClient;
696542aa9cSPeter Lieven
706542aa9cSPeter Lieven typedef struct NFSRPC {
71d746427aSPaolo Bonzini BlockDriverState *bs;
726542aa9cSPeter Lieven int ret;
736542aa9cSPeter Lieven int complete;
746542aa9cSPeter Lieven QEMUIOVector *iov;
756542aa9cSPeter Lieven struct stat *st;
766542aa9cSPeter Lieven Coroutine *co;
77471799d1SStefan Hajnoczi NFSClient *client;
786542aa9cSPeter Lieven } NFSRPC;
796542aa9cSPeter Lieven
nfs_parse_uri(const char * filename,QDict * options,Error ** errp)8094d6a7a7SAshijeet Acharya static int nfs_parse_uri(const char *filename, QDict *options, Error **errp)
8194d6a7a7SAshijeet Acharya {
8294d6a7a7SAshijeet Acharya URI *uri = NULL;
8394d6a7a7SAshijeet Acharya QueryParams *qp = NULL;
8494d6a7a7SAshijeet Acharya int ret = -EINVAL, i;
8594d6a7a7SAshijeet Acharya
8694d6a7a7SAshijeet Acharya uri = uri_parse(filename);
8794d6a7a7SAshijeet Acharya if (!uri) {
8894d6a7a7SAshijeet Acharya error_setg(errp, "Invalid URI specified");
8994d6a7a7SAshijeet Acharya goto out;
9094d6a7a7SAshijeet Acharya }
91f69165a8SMax Reitz if (g_strcmp0(uri->scheme, "nfs") != 0) {
9294d6a7a7SAshijeet Acharya error_setg(errp, "URI scheme must be 'nfs'");
9394d6a7a7SAshijeet Acharya goto out;
9494d6a7a7SAshijeet Acharya }
9594d6a7a7SAshijeet Acharya
9694d6a7a7SAshijeet Acharya if (!uri->server) {
9794d6a7a7SAshijeet Acharya error_setg(errp, "missing hostname in URI");
9894d6a7a7SAshijeet Acharya goto out;
9994d6a7a7SAshijeet Acharya }
10094d6a7a7SAshijeet Acharya
10194d6a7a7SAshijeet Acharya if (!uri->path) {
10294d6a7a7SAshijeet Acharya error_setg(errp, "missing file path in URI");
10394d6a7a7SAshijeet Acharya goto out;
10494d6a7a7SAshijeet Acharya }
10594d6a7a7SAshijeet Acharya
10694d6a7a7SAshijeet Acharya qp = query_params_parse(uri->query);
10794d6a7a7SAshijeet Acharya if (!qp) {
10894d6a7a7SAshijeet Acharya error_setg(errp, "could not parse query parameters");
10994d6a7a7SAshijeet Acharya goto out;
11094d6a7a7SAshijeet Acharya }
11194d6a7a7SAshijeet Acharya
11246f5ac20SEric Blake qdict_put_str(options, "server.host", uri->server);
11346f5ac20SEric Blake qdict_put_str(options, "server.type", "inet");
11446f5ac20SEric Blake qdict_put_str(options, "path", uri->path);
11594d6a7a7SAshijeet Acharya
11694d6a7a7SAshijeet Acharya for (i = 0; i < qp->n; i++) {
117bd1386ccSEric Blake uint64_t val;
11894d6a7a7SAshijeet Acharya if (!qp->p[i].value) {
11994d6a7a7SAshijeet Acharya error_setg(errp, "Value for NFS parameter expected: %s",
12094d6a7a7SAshijeet Acharya qp->p[i].name);
12194d6a7a7SAshijeet Acharya goto out;
12294d6a7a7SAshijeet Acharya }
123bd1386ccSEric Blake if (parse_uint_full(qp->p[i].value, 0, &val)) {
12494d6a7a7SAshijeet Acharya error_setg(errp, "Illegal value for NFS parameter: %s",
12594d6a7a7SAshijeet Acharya qp->p[i].name);
12694d6a7a7SAshijeet Acharya goto out;
12794d6a7a7SAshijeet Acharya }
12894d6a7a7SAshijeet Acharya if (!strcmp(qp->p[i].name, "uid")) {
12946f5ac20SEric Blake qdict_put_str(options, "user", qp->p[i].value);
13094d6a7a7SAshijeet Acharya } else if (!strcmp(qp->p[i].name, "gid")) {
13146f5ac20SEric Blake qdict_put_str(options, "group", qp->p[i].value);
13294d6a7a7SAshijeet Acharya } else if (!strcmp(qp->p[i].name, "tcp-syncnt")) {
13346f5ac20SEric Blake qdict_put_str(options, "tcp-syn-count", qp->p[i].value);
13494d6a7a7SAshijeet Acharya } else if (!strcmp(qp->p[i].name, "readahead")) {
13546f5ac20SEric Blake qdict_put_str(options, "readahead-size", qp->p[i].value);
13694d6a7a7SAshijeet Acharya } else if (!strcmp(qp->p[i].name, "pagecache")) {
13746f5ac20SEric Blake qdict_put_str(options, "page-cache-size", qp->p[i].value);
13894d6a7a7SAshijeet Acharya } else if (!strcmp(qp->p[i].name, "debug")) {
13946f5ac20SEric Blake qdict_put_str(options, "debug", qp->p[i].value);
14094d6a7a7SAshijeet Acharya } else {
14194d6a7a7SAshijeet Acharya error_setg(errp, "Unknown NFS parameter name: %s",
14294d6a7a7SAshijeet Acharya qp->p[i].name);
14394d6a7a7SAshijeet Acharya goto out;
14494d6a7a7SAshijeet Acharya }
14594d6a7a7SAshijeet Acharya }
14694d6a7a7SAshijeet Acharya ret = 0;
14794d6a7a7SAshijeet Acharya out:
14894d6a7a7SAshijeet Acharya if (qp) {
14994d6a7a7SAshijeet Acharya query_params_free(qp);
15094d6a7a7SAshijeet Acharya }
15194d6a7a7SAshijeet Acharya uri_free(uri);
15294d6a7a7SAshijeet Acharya return ret;
15394d6a7a7SAshijeet Acharya }
15494d6a7a7SAshijeet Acharya
nfs_has_filename_options_conflict(QDict * options,Error ** errp)15594d6a7a7SAshijeet Acharya static bool nfs_has_filename_options_conflict(QDict *options, Error **errp)
15694d6a7a7SAshijeet Acharya {
15794d6a7a7SAshijeet Acharya const QDictEntry *qe;
15894d6a7a7SAshijeet Acharya
15994d6a7a7SAshijeet Acharya for (qe = qdict_first(options); qe; qe = qdict_next(options, qe)) {
16094d6a7a7SAshijeet Acharya if (!strcmp(qe->key, "host") ||
16194d6a7a7SAshijeet Acharya !strcmp(qe->key, "path") ||
16294d6a7a7SAshijeet Acharya !strcmp(qe->key, "user") ||
16394d6a7a7SAshijeet Acharya !strcmp(qe->key, "group") ||
16494d6a7a7SAshijeet Acharya !strcmp(qe->key, "tcp-syn-count") ||
16594d6a7a7SAshijeet Acharya !strcmp(qe->key, "readahead-size") ||
16694d6a7a7SAshijeet Acharya !strcmp(qe->key, "page-cache-size") ||
1677103d916SPrasanna Kumar Kalever !strcmp(qe->key, "debug") ||
16894d6a7a7SAshijeet Acharya strstart(qe->key, "server.", NULL))
16994d6a7a7SAshijeet Acharya {
17094d6a7a7SAshijeet Acharya error_setg(errp, "Option %s cannot be used with a filename",
17194d6a7a7SAshijeet Acharya qe->key);
17294d6a7a7SAshijeet Acharya return true;
17394d6a7a7SAshijeet Acharya }
17494d6a7a7SAshijeet Acharya }
17594d6a7a7SAshijeet Acharya
17694d6a7a7SAshijeet Acharya return false;
17794d6a7a7SAshijeet Acharya }
17894d6a7a7SAshijeet Acharya
nfs_parse_filename(const char * filename,QDict * options,Error ** errp)17994d6a7a7SAshijeet Acharya static void nfs_parse_filename(const char *filename, QDict *options,
18094d6a7a7SAshijeet Acharya Error **errp)
18194d6a7a7SAshijeet Acharya {
18294d6a7a7SAshijeet Acharya if (nfs_has_filename_options_conflict(options, errp)) {
18394d6a7a7SAshijeet Acharya return;
18494d6a7a7SAshijeet Acharya }
18594d6a7a7SAshijeet Acharya
18694d6a7a7SAshijeet Acharya nfs_parse_uri(filename, options, errp);
18794d6a7a7SAshijeet Acharya }
18894d6a7a7SAshijeet Acharya
1896542aa9cSPeter Lieven static void nfs_process_read(void *arg);
1906542aa9cSPeter Lieven static void nfs_process_write(void *arg);
1916542aa9cSPeter Lieven
19237d1e4d9SPaolo Bonzini /* Called with QemuMutex held. */
nfs_set_events(NFSClient * client)1936542aa9cSPeter Lieven static void nfs_set_events(NFSClient *client)
1946542aa9cSPeter Lieven {
1956542aa9cSPeter Lieven int ev = nfs_which_events(client->context);
1966542aa9cSPeter Lieven if (ev != client->events) {
197dca21ef2SFam Zheng aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
1986542aa9cSPeter Lieven (ev & POLLIN) ? nfs_process_read : NULL,
199f6a51c84SStefan Hajnoczi (ev & POLLOUT) ? nfs_process_write : NULL,
200826cc324SStefan Hajnoczi NULL, NULL, client);
2016542aa9cSPeter Lieven
2026542aa9cSPeter Lieven }
2036542aa9cSPeter Lieven client->events = ev;
2046542aa9cSPeter Lieven }
2056542aa9cSPeter Lieven
nfs_process_read(void * arg)2066542aa9cSPeter Lieven static void nfs_process_read(void *arg)
2076542aa9cSPeter Lieven {
2086542aa9cSPeter Lieven NFSClient *client = arg;
2099d456654SPaolo Bonzini
21037d1e4d9SPaolo Bonzini qemu_mutex_lock(&client->mutex);
2116542aa9cSPeter Lieven nfs_service(client->context, POLLIN);
2126542aa9cSPeter Lieven nfs_set_events(client);
21337d1e4d9SPaolo Bonzini qemu_mutex_unlock(&client->mutex);
2146542aa9cSPeter Lieven }
2156542aa9cSPeter Lieven
nfs_process_write(void * arg)2166542aa9cSPeter Lieven static void nfs_process_write(void *arg)
2176542aa9cSPeter Lieven {
2186542aa9cSPeter Lieven NFSClient *client = arg;
2199d456654SPaolo Bonzini
22037d1e4d9SPaolo Bonzini qemu_mutex_lock(&client->mutex);
2216542aa9cSPeter Lieven nfs_service(client->context, POLLOUT);
2226542aa9cSPeter Lieven nfs_set_events(client);
22337d1e4d9SPaolo Bonzini qemu_mutex_unlock(&client->mutex);
2246542aa9cSPeter Lieven }
2256542aa9cSPeter Lieven
nfs_co_init_task(BlockDriverState * bs,NFSRPC * task)226ee15ee36SPaolo Bonzini static void coroutine_fn nfs_co_init_task(BlockDriverState *bs, NFSRPC *task)
2276542aa9cSPeter Lieven {
2286542aa9cSPeter Lieven *task = (NFSRPC) {
2296542aa9cSPeter Lieven .co = qemu_coroutine_self(),
230d746427aSPaolo Bonzini .bs = bs,
231d746427aSPaolo Bonzini .client = bs->opaque,
2326542aa9cSPeter Lieven };
2336542aa9cSPeter Lieven }
2346542aa9cSPeter Lieven
nfs_co_generic_bh_cb(void * opaque)2356542aa9cSPeter Lieven static void nfs_co_generic_bh_cb(void *opaque)
2366542aa9cSPeter Lieven {
2376542aa9cSPeter Lieven NFSRPC *task = opaque;
2381919631eSPaolo Bonzini
239a2c0fe2fSPeter Lieven task->complete = 1;
2401919631eSPaolo Bonzini aio_co_wake(task->co);
2416542aa9cSPeter Lieven }
2426542aa9cSPeter Lieven
24337d1e4d9SPaolo Bonzini /* Called (via nfs_service) with QemuMutex held. */
2446542aa9cSPeter Lieven static void
nfs_co_generic_cb(int ret,struct nfs_context * nfs,void * data,void * private_data)2456542aa9cSPeter Lieven nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
2466542aa9cSPeter Lieven void *private_data)
2476542aa9cSPeter Lieven {
2486542aa9cSPeter Lieven NFSRPC *task = private_data;
2496542aa9cSPeter Lieven task->ret = ret;
250d746427aSPaolo Bonzini assert(!task->st);
2516542aa9cSPeter Lieven if (task->ret > 0 && task->iov) {
2526542aa9cSPeter Lieven if (task->ret <= task->iov->size) {
2536542aa9cSPeter Lieven qemu_iovec_from_buf(task->iov, 0, data, task->ret);
2546542aa9cSPeter Lieven } else {
2556542aa9cSPeter Lieven task->ret = -EIO;
2566542aa9cSPeter Lieven }
2576542aa9cSPeter Lieven }
25820fccb18SPeter Lieven if (task->ret < 0) {
25920fccb18SPeter Lieven error_report("NFS Error: %s", nfs_get_error(nfs));
26020fccb18SPeter Lieven }
261e4ec5ad4SPavel Dovgalyuk replay_bh_schedule_oneshot_event(task->client->aio_context,
262471799d1SStefan Hajnoczi nfs_co_generic_bh_cb, task);
2636542aa9cSPeter Lieven }
2646542aa9cSPeter Lieven
nfs_co_preadv(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * iov,BdrvRequestFlags flags)265f7ef38ddSVladimir Sementsov-Ogievskiy static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, int64_t offset,
266f7ef38ddSVladimir Sementsov-Ogievskiy int64_t bytes, QEMUIOVector *iov,
267f7ef38ddSVladimir Sementsov-Ogievskiy BdrvRequestFlags flags)
2686542aa9cSPeter Lieven {
2696542aa9cSPeter Lieven NFSClient *client = bs->opaque;
2706542aa9cSPeter Lieven NFSRPC task;
2716542aa9cSPeter Lieven
272d746427aSPaolo Bonzini nfs_co_init_task(bs, &task);
2736542aa9cSPeter Lieven task.iov = iov;
2746542aa9cSPeter Lieven
2756e8a355dSDaniel Brodsky WITH_QEMU_LOCK_GUARD(&client->mutex) {
2766542aa9cSPeter Lieven if (nfs_pread_async(client->context, client->fh,
27769785a22SPeter Lieven offset, bytes, nfs_co_generic_cb, &task) != 0) {
2786542aa9cSPeter Lieven return -ENOMEM;
2796542aa9cSPeter Lieven }
2806542aa9cSPeter Lieven
2816542aa9cSPeter Lieven nfs_set_events(client);
2826e8a355dSDaniel Brodsky }
283aa92d6c4SPaolo Bonzini while (!task.complete) {
2846542aa9cSPeter Lieven qemu_coroutine_yield();
2856542aa9cSPeter Lieven }
2866542aa9cSPeter Lieven
2876542aa9cSPeter Lieven if (task.ret < 0) {
2886542aa9cSPeter Lieven return task.ret;
2896542aa9cSPeter Lieven }
2906542aa9cSPeter Lieven
2916542aa9cSPeter Lieven /* zero pad short reads */
2926542aa9cSPeter Lieven if (task.ret < iov->size) {
2936542aa9cSPeter Lieven qemu_iovec_memset(iov, task.ret, 0, iov->size - task.ret);
2946542aa9cSPeter Lieven }
2956542aa9cSPeter Lieven
2966542aa9cSPeter Lieven return 0;
2976542aa9cSPeter Lieven }
2986542aa9cSPeter Lieven
nfs_co_pwritev(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * iov,BdrvRequestFlags flags)299e75abedaSVladimir Sementsov-Ogievskiy static int coroutine_fn nfs_co_pwritev(BlockDriverState *bs, int64_t offset,
300e75abedaSVladimir Sementsov-Ogievskiy int64_t bytes, QEMUIOVector *iov,
301e75abedaSVladimir Sementsov-Ogievskiy BdrvRequestFlags flags)
3026542aa9cSPeter Lieven {
3036542aa9cSPeter Lieven NFSClient *client = bs->opaque;
3046542aa9cSPeter Lieven NFSRPC task;
3056542aa9cSPeter Lieven char *buf = NULL;
306ef503a84SPeter Lieven bool my_buffer = false;
3076542aa9cSPeter Lieven
308d746427aSPaolo Bonzini nfs_co_init_task(bs, &task);
3096542aa9cSPeter Lieven
310ef503a84SPeter Lieven if (iov->niov != 1) {
31169785a22SPeter Lieven buf = g_try_malloc(bytes);
31269785a22SPeter Lieven if (bytes && buf == NULL) {
3132347dd7bSKevin Wolf return -ENOMEM;
3142347dd7bSKevin Wolf }
31569785a22SPeter Lieven qemu_iovec_to_buf(iov, 0, buf, bytes);
316ef503a84SPeter Lieven my_buffer = true;
317ef503a84SPeter Lieven } else {
318ef503a84SPeter Lieven buf = iov->iov[0].iov_base;
319ef503a84SPeter Lieven }
3206542aa9cSPeter Lieven
3216e8a355dSDaniel Brodsky WITH_QEMU_LOCK_GUARD(&client->mutex) {
3226542aa9cSPeter Lieven if (nfs_pwrite_async(client->context, client->fh,
32369785a22SPeter Lieven offset, bytes, buf,
32469785a22SPeter Lieven nfs_co_generic_cb, &task) != 0) {
325ef503a84SPeter Lieven if (my_buffer) {
3266542aa9cSPeter Lieven g_free(buf);
327ef503a84SPeter Lieven }
3286542aa9cSPeter Lieven return -ENOMEM;
3296542aa9cSPeter Lieven }
3306542aa9cSPeter Lieven
3316542aa9cSPeter Lieven nfs_set_events(client);
3326e8a355dSDaniel Brodsky }
333aa92d6c4SPaolo Bonzini while (!task.complete) {
3346542aa9cSPeter Lieven qemu_coroutine_yield();
3356542aa9cSPeter Lieven }
3366542aa9cSPeter Lieven
337ef503a84SPeter Lieven if (my_buffer) {
3386542aa9cSPeter Lieven g_free(buf);
339ef503a84SPeter Lieven }
3406542aa9cSPeter Lieven
34169785a22SPeter Lieven if (task.ret != bytes) {
3426542aa9cSPeter Lieven return task.ret < 0 ? task.ret : -EIO;
3436542aa9cSPeter Lieven }
3446542aa9cSPeter Lieven
3456542aa9cSPeter Lieven return 0;
3466542aa9cSPeter Lieven }
3476542aa9cSPeter Lieven
nfs_co_flush(BlockDriverState * bs)3486542aa9cSPeter Lieven static int coroutine_fn nfs_co_flush(BlockDriverState *bs)
3496542aa9cSPeter Lieven {
3506542aa9cSPeter Lieven NFSClient *client = bs->opaque;
3516542aa9cSPeter Lieven NFSRPC task;
3526542aa9cSPeter Lieven
353d746427aSPaolo Bonzini nfs_co_init_task(bs, &task);
3546542aa9cSPeter Lieven
3556e8a355dSDaniel Brodsky WITH_QEMU_LOCK_GUARD(&client->mutex) {
3566542aa9cSPeter Lieven if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb,
3576542aa9cSPeter Lieven &task) != 0) {
3586542aa9cSPeter Lieven return -ENOMEM;
3596542aa9cSPeter Lieven }
3606542aa9cSPeter Lieven
3616542aa9cSPeter Lieven nfs_set_events(client);
3626e8a355dSDaniel Brodsky }
363aa92d6c4SPaolo Bonzini while (!task.complete) {
3646542aa9cSPeter Lieven qemu_coroutine_yield();
3656542aa9cSPeter Lieven }
3666542aa9cSPeter Lieven
3676542aa9cSPeter Lieven return task.ret;
3686542aa9cSPeter Lieven }
3696542aa9cSPeter Lieven
nfs_detach_aio_context(BlockDriverState * bs)370471799d1SStefan Hajnoczi static void nfs_detach_aio_context(BlockDriverState *bs)
371471799d1SStefan Hajnoczi {
372471799d1SStefan Hajnoczi NFSClient *client = bs->opaque;
373471799d1SStefan Hajnoczi
374dca21ef2SFam Zheng aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
37560f782b6SStefan Hajnoczi NULL, NULL, NULL, NULL, NULL);
376471799d1SStefan Hajnoczi client->events = 0;
377471799d1SStefan Hajnoczi }
378471799d1SStefan Hajnoczi
nfs_attach_aio_context(BlockDriverState * bs,AioContext * new_context)379471799d1SStefan Hajnoczi static void nfs_attach_aio_context(BlockDriverState *bs,
380471799d1SStefan Hajnoczi AioContext *new_context)
381471799d1SStefan Hajnoczi {
382471799d1SStefan Hajnoczi NFSClient *client = bs->opaque;
383471799d1SStefan Hajnoczi
384471799d1SStefan Hajnoczi client->aio_context = new_context;
385471799d1SStefan Hajnoczi nfs_set_events(client);
386471799d1SStefan Hajnoczi }
387471799d1SStefan Hajnoczi
nfs_client_close(NFSClient * client)3886542aa9cSPeter Lieven static void nfs_client_close(NFSClient *client)
3896542aa9cSPeter Lieven {
3906542aa9cSPeter Lieven if (client->context) {
391601dc655SPeter Lieven qemu_mutex_lock(&client->mutex);
392601dc655SPeter Lieven aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
39360f782b6SStefan Hajnoczi NULL, NULL, NULL, NULL, NULL);
394601dc655SPeter Lieven qemu_mutex_unlock(&client->mutex);
3956542aa9cSPeter Lieven if (client->fh) {
3966542aa9cSPeter Lieven nfs_close(client->context, client->fh);
397113fe792SJeff Cody client->fh = NULL;
3986542aa9cSPeter Lieven }
399d2c6becbSPeter Lieven #ifdef LIBNFS_FEATURE_UMOUNT
400d2c6becbSPeter Lieven nfs_umount(client->context);
401d2c6becbSPeter Lieven #endif
4026542aa9cSPeter Lieven nfs_destroy_context(client->context);
403113fe792SJeff Cody client->context = NULL;
4046542aa9cSPeter Lieven }
405113fe792SJeff Cody g_free(client->path);
406113fe792SJeff Cody qemu_mutex_destroy(&client->mutex);
407113fe792SJeff Cody qapi_free_NFSServer(client->server);
408113fe792SJeff Cody client->server = NULL;
4096542aa9cSPeter Lieven }
4106542aa9cSPeter Lieven
nfs_file_close(BlockDriverState * bs)4116542aa9cSPeter Lieven static void nfs_file_close(BlockDriverState *bs)
4126542aa9cSPeter Lieven {
4136542aa9cSPeter Lieven NFSClient *client = bs->opaque;
4146542aa9cSPeter Lieven nfs_client_close(client);
4156542aa9cSPeter Lieven }
4166542aa9cSPeter Lieven
nfs_client_open(NFSClient * client,BlockdevOptionsNfs * opts,int flags,int open_flags,Error ** errp)417c22a0345SKevin Wolf static int64_t nfs_client_open(NFSClient *client, BlockdevOptionsNfs *opts,
418cb8d4bf6SFam Zheng int flags, int open_flags, Error **errp)
4196542aa9cSPeter Lieven {
420f1a7ff77SPeter Lieven int64_t ret = -EINVAL;
421ebdebe47SBin Meng #ifdef _WIN32
422ebdebe47SBin Meng struct __stat64 st;
423ebdebe47SBin Meng #else
4246542aa9cSPeter Lieven struct stat st;
425ebdebe47SBin Meng #endif
4266542aa9cSPeter Lieven char *file = NULL, *strp = NULL;
4276542aa9cSPeter Lieven
428113fe792SJeff Cody qemu_mutex_init(&client->mutex);
42994d6a7a7SAshijeet Acharya
430c22a0345SKevin Wolf client->path = g_strdup(opts->path);
43194d6a7a7SAshijeet Acharya
43294d6a7a7SAshijeet Acharya strp = strrchr(client->path, '/');
4336542aa9cSPeter Lieven if (strp == NULL) {
4346542aa9cSPeter Lieven error_setg(errp, "Invalid URL specified");
4356542aa9cSPeter Lieven goto fail;
4366542aa9cSPeter Lieven }
4376542aa9cSPeter Lieven file = g_strdup(strp);
4386542aa9cSPeter Lieven *strp = 0;
4396542aa9cSPeter Lieven
440c22a0345SKevin Wolf /* Steal the NFSServer object from opts; set the original pointer to NULL
441c22a0345SKevin Wolf * to avoid use after free and double free. */
442c22a0345SKevin Wolf client->server = opts->server;
443c22a0345SKevin Wolf opts->server = NULL;
44494d6a7a7SAshijeet Acharya
4456542aa9cSPeter Lieven client->context = nfs_init_context();
4466542aa9cSPeter Lieven if (client->context == NULL) {
4476542aa9cSPeter Lieven error_setg(errp, "Failed to init NFS context");
4486542aa9cSPeter Lieven goto fail;
4496542aa9cSPeter Lieven }
4506542aa9cSPeter Lieven
451c22a0345SKevin Wolf if (opts->has_user) {
452c22a0345SKevin Wolf client->uid = opts->user;
45394d6a7a7SAshijeet Acharya nfs_set_uid(client->context, client->uid);
4546542aa9cSPeter Lieven }
45594d6a7a7SAshijeet Acharya
456c22a0345SKevin Wolf if (opts->has_group) {
457c22a0345SKevin Wolf client->gid = opts->group;
45894d6a7a7SAshijeet Acharya nfs_set_gid(client->context, client->gid);
4597c24384bSPeter Lieven }
46094d6a7a7SAshijeet Acharya
461c22a0345SKevin Wolf if (opts->has_tcp_syn_count) {
462c22a0345SKevin Wolf client->tcp_syncnt = opts->tcp_syn_count;
46394d6a7a7SAshijeet Acharya nfs_set_tcp_syncnt(client->context, client->tcp_syncnt);
46494d6a7a7SAshijeet Acharya }
46594d6a7a7SAshijeet Acharya
466f42ca3caSPeter Lieven #ifdef LIBNFS_FEATURE_READAHEAD
467c22a0345SKevin Wolf if (opts->has_readahead_size) {
46838f8d5e0SPeter Lieven if (open_flags & BDRV_O_NOCACHE) {
46938f8d5e0SPeter Lieven error_setg(errp, "Cannot enable NFS readahead "
47038f8d5e0SPeter Lieven "if cache.direct = on");
47138f8d5e0SPeter Lieven goto fail;
47238f8d5e0SPeter Lieven }
473c22a0345SKevin Wolf client->readahead = opts->readahead_size;
47494d6a7a7SAshijeet Acharya if (client->readahead > QEMU_NFS_MAX_READAHEAD_SIZE) {
4753dc6f869SAlistair Francis warn_report("Truncating NFS readahead size to %d",
4763dc6f869SAlistair Francis QEMU_NFS_MAX_READAHEAD_SIZE);
47794d6a7a7SAshijeet Acharya client->readahead = QEMU_NFS_MAX_READAHEAD_SIZE;
47829c838cdSPeter Lieven }
47994d6a7a7SAshijeet Acharya nfs_set_readahead(client->context, client->readahead);
480d99b26c4SPeter Lieven #ifdef LIBNFS_FEATURE_PAGECACHE
481d99b26c4SPeter Lieven nfs_set_pagecache_ttl(client->context, 0);
482d99b26c4SPeter Lieven #endif
483d99b26c4SPeter Lieven client->cache_used = true;
48494d6a7a7SAshijeet Acharya }
485d99b26c4SPeter Lieven #endif
48694d6a7a7SAshijeet Acharya
487d99b26c4SPeter Lieven #ifdef LIBNFS_FEATURE_PAGECACHE
488c22a0345SKevin Wolf if (opts->has_page_cache_size) {
489d99b26c4SPeter Lieven if (open_flags & BDRV_O_NOCACHE) {
490d99b26c4SPeter Lieven error_setg(errp, "Cannot enable NFS pagecache "
491d99b26c4SPeter Lieven "if cache.direct = on");
492d99b26c4SPeter Lieven goto fail;
493d99b26c4SPeter Lieven }
494c22a0345SKevin Wolf client->pagecache = opts->page_cache_size;
49594d6a7a7SAshijeet Acharya if (client->pagecache > QEMU_NFS_MAX_PAGECACHE_SIZE) {
4963dc6f869SAlistair Francis warn_report("Truncating NFS pagecache size to %d pages",
4973dc6f869SAlistair Francis QEMU_NFS_MAX_PAGECACHE_SIZE);
49894d6a7a7SAshijeet Acharya client->pagecache = QEMU_NFS_MAX_PAGECACHE_SIZE;
499d99b26c4SPeter Lieven }
50094d6a7a7SAshijeet Acharya nfs_set_pagecache(client->context, client->pagecache);
501d99b26c4SPeter Lieven nfs_set_pagecache_ttl(client->context, 0);
50238f8d5e0SPeter Lieven client->cache_used = true;
50394d6a7a7SAshijeet Acharya }
504f42ca3caSPeter Lieven #endif
50594d6a7a7SAshijeet Acharya
5067725b8bfSPeter Lieven #ifdef LIBNFS_FEATURE_DEBUG
507c22a0345SKevin Wolf if (opts->has_debug) {
508c22a0345SKevin Wolf client->debug = opts->debug;
5097725b8bfSPeter Lieven /* limit the maximum debug level to avoid potential flooding
5107725b8bfSPeter Lieven * of our log files. */
51194d6a7a7SAshijeet Acharya if (client->debug > QEMU_NFS_MAX_DEBUG_LEVEL) {
5123dc6f869SAlistair Francis warn_report("Limiting NFS debug level to %d",
5133dc6f869SAlistair Francis QEMU_NFS_MAX_DEBUG_LEVEL);
51494d6a7a7SAshijeet Acharya client->debug = QEMU_NFS_MAX_DEBUG_LEVEL;
5157725b8bfSPeter Lieven }
51694d6a7a7SAshijeet Acharya nfs_set_debug(client->context, client->debug);
51794d6a7a7SAshijeet Acharya }
5187725b8bfSPeter Lieven #endif
5196542aa9cSPeter Lieven
52094d6a7a7SAshijeet Acharya ret = nfs_mount(client->context, client->server->host, client->path);
5216542aa9cSPeter Lieven if (ret < 0) {
5226542aa9cSPeter Lieven error_setg(errp, "Failed to mount nfs share: %s",
5236542aa9cSPeter Lieven nfs_get_error(client->context));
5246542aa9cSPeter Lieven goto fail;
5256542aa9cSPeter Lieven }
5266542aa9cSPeter Lieven
5276542aa9cSPeter Lieven if (flags & O_CREAT) {
5286542aa9cSPeter Lieven ret = nfs_creat(client->context, file, 0600, &client->fh);
5296542aa9cSPeter Lieven if (ret < 0) {
5306542aa9cSPeter Lieven error_setg(errp, "Failed to create file: %s",
5316542aa9cSPeter Lieven nfs_get_error(client->context));
5326542aa9cSPeter Lieven goto fail;
5336542aa9cSPeter Lieven }
5346542aa9cSPeter Lieven } else {
5356542aa9cSPeter Lieven ret = nfs_open(client->context, file, flags, &client->fh);
5366542aa9cSPeter Lieven if (ret < 0) {
5376542aa9cSPeter Lieven error_setg(errp, "Failed to open file : %s",
5386542aa9cSPeter Lieven nfs_get_error(client->context));
5396542aa9cSPeter Lieven goto fail;
5406542aa9cSPeter Lieven }
5416542aa9cSPeter Lieven }
5426542aa9cSPeter Lieven
5436542aa9cSPeter Lieven ret = nfs_fstat(client->context, client->fh, &st);
5446542aa9cSPeter Lieven if (ret < 0) {
5456542aa9cSPeter Lieven error_setg(errp, "Failed to fstat file: %s",
5466542aa9cSPeter Lieven nfs_get_error(client->context));
5476542aa9cSPeter Lieven goto fail;
5486542aa9cSPeter Lieven }
5496542aa9cSPeter Lieven
5506542aa9cSPeter Lieven ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE);
551c63b0201SYonggang Luo #if !defined(_WIN32)
55218a8056eSPeter Lieven client->st_blocks = st.st_blocks;
553c63b0201SYonggang Luo #endif
5546542aa9cSPeter Lieven client->has_zero_init = S_ISREG(st.st_mode);
55594d6a7a7SAshijeet Acharya *strp = '/';
5566542aa9cSPeter Lieven goto out;
55794d6a7a7SAshijeet Acharya
5586542aa9cSPeter Lieven fail:
5596542aa9cSPeter Lieven nfs_client_close(client);
5606542aa9cSPeter Lieven out:
5616542aa9cSPeter Lieven g_free(file);
5626542aa9cSPeter Lieven return ret;
5636542aa9cSPeter Lieven }
5646542aa9cSPeter Lieven
nfs_options_qdict_to_qapi(QDict * options,Error ** errp)565a1a42af4SKevin Wolf static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
566a1a42af4SKevin Wolf Error **errp)
567c22a0345SKevin Wolf {
568c22a0345SKevin Wolf BlockdevOptionsNfs *opts = NULL;
569c22a0345SKevin Wolf Visitor *v;
570c82be42cSKevin Wolf const QDictEntry *e;
571c22a0345SKevin Wolf
572af91062eSMarkus Armbruster v = qobject_input_visitor_new_flat_confused(options, errp);
573af91062eSMarkus Armbruster if (!v) {
574a1a42af4SKevin Wolf return NULL;
575c22a0345SKevin Wolf }
576c22a0345SKevin Wolf
577b11a093cSMarkus Armbruster visit_type_BlockdevOptionsNfs(v, NULL, &opts, errp);
578c22a0345SKevin Wolf visit_free(v);
579b11a093cSMarkus Armbruster if (!opts) {
580a1a42af4SKevin Wolf return NULL;
581a1a42af4SKevin Wolf }
582a1a42af4SKevin Wolf
583c82be42cSKevin Wolf /* Remove the processed options from the QDict (the visitor processes
584c82be42cSKevin Wolf * _all_ options in the QDict) */
585c82be42cSKevin Wolf while ((e = qdict_first(options))) {
586c82be42cSKevin Wolf qdict_del(options, e->key);
587c82be42cSKevin Wolf }
588c82be42cSKevin Wolf
589a1a42af4SKevin Wolf return opts;
590a1a42af4SKevin Wolf }
591a1a42af4SKevin Wolf
nfs_client_open_qdict(NFSClient * client,QDict * options,int flags,int open_flags,Error ** errp)592a1a42af4SKevin Wolf static int64_t nfs_client_open_qdict(NFSClient *client, QDict *options,
593a1a42af4SKevin Wolf int flags, int open_flags, Error **errp)
594a1a42af4SKevin Wolf {
595a1a42af4SKevin Wolf BlockdevOptionsNfs *opts;
596182454dcSPeter Lieven int64_t ret;
597a1a42af4SKevin Wolf
598a1a42af4SKevin Wolf opts = nfs_options_qdict_to_qapi(options, errp);
599a1a42af4SKevin Wolf if (opts == NULL) {
600c22a0345SKevin Wolf ret = -EINVAL;
601c22a0345SKevin Wolf goto fail;
602c22a0345SKevin Wolf }
603c22a0345SKevin Wolf
604c22a0345SKevin Wolf ret = nfs_client_open(client, opts, flags, open_flags, errp);
605c22a0345SKevin Wolf fail:
606c22a0345SKevin Wolf qapi_free_BlockdevOptionsNfs(opts);
607c22a0345SKevin Wolf return ret;
608c22a0345SKevin Wolf }
609c22a0345SKevin Wolf
nfs_file_open(BlockDriverState * bs,QDict * options,int flags,Error ** errp)6106542aa9cSPeter Lieven static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
6116542aa9cSPeter Lieven Error **errp) {
6126542aa9cSPeter Lieven NFSClient *client = bs->opaque;
6136542aa9cSPeter Lieven int64_t ret;
6146542aa9cSPeter Lieven
615471799d1SStefan Hajnoczi client->aio_context = bdrv_get_aio_context(bs);
616471799d1SStefan Hajnoczi
617c22a0345SKevin Wolf ret = nfs_client_open_qdict(client, options,
6186542aa9cSPeter Lieven (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
619cb8d4bf6SFam Zheng bs->open_flags, errp);
6206542aa9cSPeter Lieven if (ret < 0) {
62194d6a7a7SAshijeet Acharya return ret;
6226542aa9cSPeter Lieven }
623113fe792SJeff Cody
6246542aa9cSPeter Lieven bs->total_sectors = ret;
6258f23aaf5SEric Blake if (client->has_zero_init) {
6268f23aaf5SEric Blake bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE;
6278f23aaf5SEric Blake }
628b3ac2b94SSimran Singhal return 0;
6296542aa9cSPeter Lieven }
6306542aa9cSPeter Lieven
631fd752801SMax Reitz static QemuOptsList nfs_create_opts = {
632fd752801SMax Reitz .name = "nfs-create-opts",
633fd752801SMax Reitz .head = QTAILQ_HEAD_INITIALIZER(nfs_create_opts.head),
634fd752801SMax Reitz .desc = {
635fd752801SMax Reitz {
636fd752801SMax Reitz .name = BLOCK_OPT_SIZE,
637fd752801SMax Reitz .type = QEMU_OPT_SIZE,
638fd752801SMax Reitz .help = "Virtual disk size"
639fd752801SMax Reitz },
640fd752801SMax Reitz { /* end of list */ }
641fd752801SMax Reitz }
642fd752801SMax Reitz };
643fd752801SMax Reitz
nfs_file_co_create(BlockdevCreateOptions * options,Error ** errp)644a1a42af4SKevin Wolf static int nfs_file_co_create(BlockdevCreateOptions *options, Error **errp)
6456542aa9cSPeter Lieven {
646a1a42af4SKevin Wolf BlockdevCreateOptionsNfs *opts = &options->u.nfs;
6475839e53bSMarkus Armbruster NFSClient *client = g_new0(NFSClient, 1);
648a1a42af4SKevin Wolf int ret;
649a1a42af4SKevin Wolf
650a1a42af4SKevin Wolf assert(options->driver == BLOCKDEV_DRIVER_NFS);
6516542aa9cSPeter Lieven
652471799d1SStefan Hajnoczi client->aio_context = qemu_get_aio_context();
653471799d1SStefan Hajnoczi
654a1a42af4SKevin Wolf ret = nfs_client_open(client, opts->location, O_CREAT, 0, errp);
655a1a42af4SKevin Wolf if (ret < 0) {
656a1a42af4SKevin Wolf goto out;
657a1a42af4SKevin Wolf }
658a1a42af4SKevin Wolf ret = nfs_ftruncate(client->context, client->fh, opts->size);
659a1a42af4SKevin Wolf nfs_client_close(client);
660a1a42af4SKevin Wolf
661a1a42af4SKevin Wolf out:
662a1a42af4SKevin Wolf g_free(client);
663a1a42af4SKevin Wolf return ret;
664a1a42af4SKevin Wolf }
665a1a42af4SKevin Wolf
nfs_file_co_create_opts(BlockDriver * drv,const char * url,QemuOpts * opts,Error ** errp)666b92902dfSMaxim Levitsky static int coroutine_fn nfs_file_co_create_opts(BlockDriver *drv,
667b92902dfSMaxim Levitsky const char *url,
668b92902dfSMaxim Levitsky QemuOpts *opts,
669a1a42af4SKevin Wolf Error **errp)
670a1a42af4SKevin Wolf {
671a1a42af4SKevin Wolf BlockdevCreateOptions *create_options;
672a1a42af4SKevin Wolf BlockdevCreateOptionsNfs *nfs_opts;
673a1a42af4SKevin Wolf QDict *options;
674a1a42af4SKevin Wolf int ret;
675a1a42af4SKevin Wolf
676a1a42af4SKevin Wolf create_options = g_new0(BlockdevCreateOptions, 1);
677a1a42af4SKevin Wolf create_options->driver = BLOCKDEV_DRIVER_NFS;
678a1a42af4SKevin Wolf nfs_opts = &create_options->u.nfs;
679a1a42af4SKevin Wolf
6806542aa9cSPeter Lieven /* Read out options */
681a1a42af4SKevin Wolf nfs_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
682c2eb918eSHu Tao BDRV_SECTOR_SIZE);
6836542aa9cSPeter Lieven
68494d6a7a7SAshijeet Acharya options = qdict_new();
68594d6a7a7SAshijeet Acharya ret = nfs_parse_uri(url, options, errp);
68694d6a7a7SAshijeet Acharya if (ret < 0) {
68794d6a7a7SAshijeet Acharya goto out;
68894d6a7a7SAshijeet Acharya }
68994d6a7a7SAshijeet Acharya
690a1a42af4SKevin Wolf nfs_opts->location = nfs_options_qdict_to_qapi(options, errp);
691a1a42af4SKevin Wolf if (nfs_opts->location == NULL) {
692a1a42af4SKevin Wolf ret = -EINVAL;
693a1a42af4SKevin Wolf goto out;
694a1a42af4SKevin Wolf }
695a1a42af4SKevin Wolf
696a1a42af4SKevin Wolf ret = nfs_file_co_create(create_options, errp);
6976542aa9cSPeter Lieven if (ret < 0) {
6986542aa9cSPeter Lieven goto out;
6996542aa9cSPeter Lieven }
700a1a42af4SKevin Wolf
701a1a42af4SKevin Wolf ret = 0;
7026542aa9cSPeter Lieven out:
703cb3e7f08SMarc-André Lureau qobject_unref(options);
704a1a42af4SKevin Wolf qapi_free_BlockdevCreateOptions(create_options);
7056542aa9cSPeter Lieven return ret;
7066542aa9cSPeter Lieven }
7076542aa9cSPeter Lieven
nfs_has_zero_init(BlockDriverState * bs)7086542aa9cSPeter Lieven static int nfs_has_zero_init(BlockDriverState *bs)
7096542aa9cSPeter Lieven {
7106542aa9cSPeter Lieven NFSClient *client = bs->opaque;
7116542aa9cSPeter Lieven return client->has_zero_init;
7126542aa9cSPeter Lieven }
7136542aa9cSPeter Lieven
714c63b0201SYonggang Luo #if !defined(_WIN32)
71537d1e4d9SPaolo Bonzini /* Called (via nfs_service) with QemuMutex held. */
716d746427aSPaolo Bonzini static void
nfs_get_allocated_file_size_cb(int ret,struct nfs_context * nfs,void * data,void * private_data)717d746427aSPaolo Bonzini nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data,
718d746427aSPaolo Bonzini void *private_data)
719d746427aSPaolo Bonzini {
720d746427aSPaolo Bonzini NFSRPC *task = private_data;
721d746427aSPaolo Bonzini task->ret = ret;
722d746427aSPaolo Bonzini if (task->ret == 0) {
723d746427aSPaolo Bonzini memcpy(task->st, data, sizeof(struct stat));
724d746427aSPaolo Bonzini }
725d746427aSPaolo Bonzini if (task->ret < 0) {
726d746427aSPaolo Bonzini error_report("NFS Error: %s", nfs_get_error(nfs));
727d746427aSPaolo Bonzini }
7283fe64abcSPaolo Bonzini replay_bh_schedule_oneshot_event(task->client->aio_context,
7293fe64abcSPaolo Bonzini nfs_co_generic_bh_cb, task);
730d746427aSPaolo Bonzini }
731d746427aSPaolo Bonzini
nfs_co_get_allocated_file_size(BlockDriverState * bs)73282618d7bSEmanuele Giuseppe Esposito static int64_t coroutine_fn nfs_co_get_allocated_file_size(BlockDriverState *bs)
7336542aa9cSPeter Lieven {
7346542aa9cSPeter Lieven NFSClient *client = bs->opaque;
7356542aa9cSPeter Lieven NFSRPC task = {0};
7366542aa9cSPeter Lieven struct stat st;
7376542aa9cSPeter Lieven
73818a8056eSPeter Lieven if (bdrv_is_read_only(bs) &&
73918a8056eSPeter Lieven !(bs->open_flags & BDRV_O_NOCACHE)) {
74018a8056eSPeter Lieven return client->st_blocks * 512;
74118a8056eSPeter Lieven }
74218a8056eSPeter Lieven
7433fe64abcSPaolo Bonzini nfs_co_init_task(bs, &task);
7446542aa9cSPeter Lieven task.st = &st;
7453fe64abcSPaolo Bonzini WITH_QEMU_LOCK_GUARD(&client->mutex) {
746d746427aSPaolo Bonzini if (nfs_fstat_async(client->context, client->fh, nfs_get_allocated_file_size_cb,
7476542aa9cSPeter Lieven &task) != 0) {
7486542aa9cSPeter Lieven return -ENOMEM;
7496542aa9cSPeter Lieven }
7506542aa9cSPeter Lieven
7516542aa9cSPeter Lieven nfs_set_events(client);
7523fe64abcSPaolo Bonzini }
7533fe64abcSPaolo Bonzini while (!task.complete) {
7543fe64abcSPaolo Bonzini qemu_coroutine_yield();
7553fe64abcSPaolo Bonzini }
7566542aa9cSPeter Lieven
757055c6f91SPeter Lieven return (task.ret < 0 ? task.ret : st.st_blocks * 512);
7586542aa9cSPeter Lieven }
759c63b0201SYonggang Luo #endif
7606542aa9cSPeter Lieven
761061ca8a3SKevin Wolf static int coroutine_fn
nfs_file_co_truncate(BlockDriverState * bs,int64_t offset,bool exact,PreallocMode prealloc,BdrvRequestFlags flags,Error ** errp)762c80d8b06SMax Reitz nfs_file_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
76392b92799SKevin Wolf PreallocMode prealloc, BdrvRequestFlags flags,
76492b92799SKevin Wolf Error **errp)
7656542aa9cSPeter Lieven {
7666542aa9cSPeter Lieven NFSClient *client = bs->opaque;
767f59adb32SMax Reitz int ret;
768f59adb32SMax Reitz
7698243ccb7SMax Reitz if (prealloc != PREALLOC_MODE_OFF) {
7708243ccb7SMax Reitz error_setg(errp, "Unsupported preallocation mode '%s'",
771977c736fSMarkus Armbruster PreallocMode_str(prealloc));
7728243ccb7SMax Reitz return -ENOTSUP;
7738243ccb7SMax Reitz }
7748243ccb7SMax Reitz
775f59adb32SMax Reitz ret = nfs_ftruncate(client->context, client->fh, offset);
776f59adb32SMax Reitz if (ret < 0) {
777f59adb32SMax Reitz error_setg_errno(errp, -ret, "Failed to truncate file");
778f59adb32SMax Reitz return ret;
779f59adb32SMax Reitz }
780f59adb32SMax Reitz
781f59adb32SMax Reitz return 0;
7826542aa9cSPeter Lieven }
7836542aa9cSPeter Lieven
78418a8056eSPeter Lieven /* Note that this will not re-establish a connection with the NFS server
78518a8056eSPeter Lieven * - it is effectively a NOP. */
nfs_reopen_prepare(BDRVReopenState * state,BlockReopenQueue * queue,Error ** errp)78618a8056eSPeter Lieven static int nfs_reopen_prepare(BDRVReopenState *state,
78718a8056eSPeter Lieven BlockReopenQueue *queue, Error **errp)
78818a8056eSPeter Lieven {
78918a8056eSPeter Lieven NFSClient *client = state->bs->opaque;
790ebdebe47SBin Meng #ifdef _WIN32
791ebdebe47SBin Meng struct __stat64 st;
792ebdebe47SBin Meng #else
79318a8056eSPeter Lieven struct stat st;
794ebdebe47SBin Meng #endif
79518a8056eSPeter Lieven int ret = 0;
79618a8056eSPeter Lieven
79718a8056eSPeter Lieven if (state->flags & BDRV_O_RDWR && bdrv_is_read_only(state->bs)) {
79818a8056eSPeter Lieven error_setg(errp, "Cannot open a read-only mount as read-write");
79918a8056eSPeter Lieven return -EACCES;
80018a8056eSPeter Lieven }
80118a8056eSPeter Lieven
80238f8d5e0SPeter Lieven if ((state->flags & BDRV_O_NOCACHE) && client->cache_used) {
803d99b26c4SPeter Lieven error_setg(errp, "Cannot disable cache if libnfs readahead or"
804d99b26c4SPeter Lieven " pagecache is enabled");
80538f8d5e0SPeter Lieven return -EINVAL;
80638f8d5e0SPeter Lieven }
80738f8d5e0SPeter Lieven
80818a8056eSPeter Lieven /* Update cache for read-only reopens */
80918a8056eSPeter Lieven if (!(state->flags & BDRV_O_RDWR)) {
81018a8056eSPeter Lieven ret = nfs_fstat(client->context, client->fh, &st);
81118a8056eSPeter Lieven if (ret < 0) {
81218a8056eSPeter Lieven error_setg(errp, "Failed to fstat file: %s",
81318a8056eSPeter Lieven nfs_get_error(client->context));
81418a8056eSPeter Lieven return ret;
81518a8056eSPeter Lieven }
816c63b0201SYonggang Luo #if !defined(_WIN32)
81718a8056eSPeter Lieven client->st_blocks = st.st_blocks;
818c63b0201SYonggang Luo #endif
81918a8056eSPeter Lieven }
82018a8056eSPeter Lieven
82118a8056eSPeter Lieven return 0;
82218a8056eSPeter Lieven }
82318a8056eSPeter Lieven
nfs_refresh_filename(BlockDriverState * bs)824998b3a1eSMax Reitz static void nfs_refresh_filename(BlockDriverState *bs)
82594d6a7a7SAshijeet Acharya {
82694d6a7a7SAshijeet Acharya NFSClient *client = bs->opaque;
82794d6a7a7SAshijeet Acharya
82894d6a7a7SAshijeet Acharya if (client->uid && !client->gid) {
82994d6a7a7SAshijeet Acharya snprintf(bs->exact_filename, sizeof(bs->exact_filename),
83094d6a7a7SAshijeet Acharya "nfs://%s%s?uid=%" PRId64, client->server->host, client->path,
83194d6a7a7SAshijeet Acharya client->uid);
83294d6a7a7SAshijeet Acharya } else if (!client->uid && client->gid) {
83394d6a7a7SAshijeet Acharya snprintf(bs->exact_filename, sizeof(bs->exact_filename),
83494d6a7a7SAshijeet Acharya "nfs://%s%s?gid=%" PRId64, client->server->host, client->path,
83594d6a7a7SAshijeet Acharya client->gid);
83694d6a7a7SAshijeet Acharya } else if (client->uid && client->gid) {
83794d6a7a7SAshijeet Acharya snprintf(bs->exact_filename, sizeof(bs->exact_filename),
83894d6a7a7SAshijeet Acharya "nfs://%s%s?uid=%" PRId64 "&gid=%" PRId64,
83994d6a7a7SAshijeet Acharya client->server->host, client->path, client->uid, client->gid);
84094d6a7a7SAshijeet Acharya } else {
84194d6a7a7SAshijeet Acharya snprintf(bs->exact_filename, sizeof(bs->exact_filename),
84294d6a7a7SAshijeet Acharya "nfs://%s%s", client->server->host, client->path);
84394d6a7a7SAshijeet Acharya }
84494d6a7a7SAshijeet Acharya }
84594d6a7a7SAshijeet Acharya
nfs_dirname(BlockDriverState * bs,Error ** errp)846b7cfc7d5SKevin Wolf static char * GRAPH_RDLOCK nfs_dirname(BlockDriverState *bs, Error **errp)
8470dcbc54aSMax Reitz {
8480dcbc54aSMax Reitz NFSClient *client = bs->opaque;
8490dcbc54aSMax Reitz
8500dcbc54aSMax Reitz if (client->uid || client->gid) {
8510dcbc54aSMax Reitz bdrv_refresh_filename(bs);
8520dcbc54aSMax Reitz error_setg(errp, "Cannot generate a base directory for NFS node '%s'",
8530dcbc54aSMax Reitz bs->filename);
8540dcbc54aSMax Reitz return NULL;
8550dcbc54aSMax Reitz }
8560dcbc54aSMax Reitz
8570dcbc54aSMax Reitz return g_strdup_printf("nfs://%s%s/", client->server->host, client->path);
8580dcbc54aSMax Reitz }
8590dcbc54aSMax Reitz
860d99b26c4SPeter Lieven #ifdef LIBNFS_FEATURE_PAGECACHE
nfs_co_invalidate_cache(BlockDriverState * bs,Error ** errp)8612b148f39SPaolo Bonzini static void coroutine_fn nfs_co_invalidate_cache(BlockDriverState *bs,
862d99b26c4SPeter Lieven Error **errp)
863d99b26c4SPeter Lieven {
864d99b26c4SPeter Lieven NFSClient *client = bs->opaque;
865d99b26c4SPeter Lieven nfs_pagecache_invalidate(client->context, client->fh);
866d99b26c4SPeter Lieven }
867d99b26c4SPeter Lieven #endif
868d99b26c4SPeter Lieven
8692654267cSMax Reitz static const char *nfs_strong_runtime_opts[] = {
8702654267cSMax Reitz "path",
8712654267cSMax Reitz "user",
8722654267cSMax Reitz "group",
8732654267cSMax Reitz "server.",
8742654267cSMax Reitz
8752654267cSMax Reitz NULL
8762654267cSMax Reitz };
8772654267cSMax Reitz
8786542aa9cSPeter Lieven static BlockDriver bdrv_nfs = {
8796542aa9cSPeter Lieven .format_name = "nfs",
8806542aa9cSPeter Lieven .protocol_name = "nfs",
8816542aa9cSPeter Lieven
8826542aa9cSPeter Lieven .instance_size = sizeof(NFSClient),
88394d6a7a7SAshijeet Acharya .bdrv_parse_filename = nfs_parse_filename,
884fd752801SMax Reitz .create_opts = &nfs_create_opts,
885fd752801SMax Reitz
8866542aa9cSPeter Lieven .bdrv_has_zero_init = nfs_has_zero_init,
887c63b0201SYonggang Luo /* libnfs does not provide the allocated filesize of a file on win32. */
888c63b0201SYonggang Luo #if !defined(_WIN32)
88982618d7bSEmanuele Giuseppe Esposito .bdrv_co_get_allocated_file_size = nfs_co_get_allocated_file_size,
890c63b0201SYonggang Luo #endif
891061ca8a3SKevin Wolf .bdrv_co_truncate = nfs_file_co_truncate,
8926542aa9cSPeter Lieven
8936542aa9cSPeter Lieven .bdrv_file_open = nfs_file_open,
8946542aa9cSPeter Lieven .bdrv_close = nfs_file_close,
895a1a42af4SKevin Wolf .bdrv_co_create = nfs_file_co_create,
896efc75e2aSStefan Hajnoczi .bdrv_co_create_opts = nfs_file_co_create_opts,
89718a8056eSPeter Lieven .bdrv_reopen_prepare = nfs_reopen_prepare,
8986542aa9cSPeter Lieven
89969785a22SPeter Lieven .bdrv_co_preadv = nfs_co_preadv,
90069785a22SPeter Lieven .bdrv_co_pwritev = nfs_co_pwritev,
9016542aa9cSPeter Lieven .bdrv_co_flush_to_disk = nfs_co_flush,
902471799d1SStefan Hajnoczi
903471799d1SStefan Hajnoczi .bdrv_detach_aio_context = nfs_detach_aio_context,
904471799d1SStefan Hajnoczi .bdrv_attach_aio_context = nfs_attach_aio_context,
90594d6a7a7SAshijeet Acharya .bdrv_refresh_filename = nfs_refresh_filename,
9060dcbc54aSMax Reitz .bdrv_dirname = nfs_dirname,
907d99b26c4SPeter Lieven
9082654267cSMax Reitz .strong_runtime_opts = nfs_strong_runtime_opts,
9092654267cSMax Reitz
910d99b26c4SPeter Lieven #ifdef LIBNFS_FEATURE_PAGECACHE
9112b148f39SPaolo Bonzini .bdrv_co_invalidate_cache = nfs_co_invalidate_cache,
912d99b26c4SPeter Lieven #endif
9136542aa9cSPeter Lieven };
9146542aa9cSPeter Lieven
nfs_block_init(void)9156542aa9cSPeter Lieven static void nfs_block_init(void)
9166542aa9cSPeter Lieven {
9176542aa9cSPeter Lieven bdrv_register(&bdrv_nfs);
9186542aa9cSPeter Lieven }
9196542aa9cSPeter Lieven
9206542aa9cSPeter Lieven block_init(nfs_block_init);
921