xref: /qemu/block/nfs.c (revision b7cfc7d5)
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