xref: /openbmc/qemu/block/nfs.c (revision 3665dd6bb9043bef181c91e2dce9e1efff47ed51)
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"
410d94b746SStefan Hajnoczi #include "qemu/cutils.h"
42e4ec5ad4SPavel Dovgalyuk #include "sysemu/replay.h"
439af23989SMarkus Armbruster #include "qapi/qapi-visit-block-core.h"
4494d6a7a7SAshijeet Acharya #include "qapi/qmp/qdict.h"
4594d6a7a7SAshijeet Acharya #include "qapi/qmp/qstring.h"
4694d6a7a7SAshijeet Acharya #include "qapi/qobject-input-visitor.h"
4794d6a7a7SAshijeet Acharya #include "qapi/qobject-output-visitor.h"
486542aa9cSPeter Lieven #include <nfsc/libnfs.h>
496542aa9cSPeter Lieven 
5094d6a7a7SAshijeet Acharya 
5129c838cdSPeter Lieven #define QEMU_NFS_MAX_READAHEAD_SIZE 1048576
52d99b26c4SPeter Lieven #define QEMU_NFS_MAX_PAGECACHE_SIZE (8388608 / NFS_BLKSIZE)
537725b8bfSPeter Lieven #define QEMU_NFS_MAX_DEBUG_LEVEL 2
5429c838cdSPeter Lieven 
556542aa9cSPeter Lieven typedef struct NFSClient {
566542aa9cSPeter Lieven     struct nfs_context *context;
576542aa9cSPeter Lieven     struct nfsfh *fh;
586542aa9cSPeter Lieven     int events;
596542aa9cSPeter Lieven     bool has_zero_init;
60471799d1SStefan Hajnoczi     AioContext *aio_context;
6137d1e4d9SPaolo Bonzini     QemuMutex mutex;
62c63b0201SYonggang Luo     uint64_t st_blocks;
6338f8d5e0SPeter Lieven     bool cache_used;
6494d6a7a7SAshijeet Acharya     NFSServer *server;
6594d6a7a7SAshijeet Acharya     char *path;
6694d6a7a7SAshijeet Acharya     int64_t uid, gid, tcp_syncnt, readahead, pagecache, debug;
676542aa9cSPeter Lieven } NFSClient;
686542aa9cSPeter Lieven 
696542aa9cSPeter Lieven typedef struct NFSRPC {
70d746427aSPaolo Bonzini     BlockDriverState *bs;
716542aa9cSPeter Lieven     int ret;
726542aa9cSPeter Lieven     int complete;
736542aa9cSPeter Lieven     QEMUIOVector *iov;
746542aa9cSPeter Lieven     struct stat *st;
756542aa9cSPeter Lieven     Coroutine *co;
76471799d1SStefan Hajnoczi     NFSClient *client;
776542aa9cSPeter Lieven } NFSRPC;
786542aa9cSPeter Lieven 
nfs_parse_uri(const char * filename,QDict * options,Error ** errp)7994d6a7a7SAshijeet Acharya static int nfs_parse_uri(const char *filename, QDict *options, Error **errp)
8094d6a7a7SAshijeet Acharya {
81f8b74fc5SThomas Huth     g_autoptr(GUri) uri = g_uri_parse(filename, G_URI_FLAGS_NONE, NULL);
82f8b74fc5SThomas Huth     GUriParamsIter qp;
83f8b74fc5SThomas Huth     const char *uri_server, *uri_path, *uri_query;
84f8b74fc5SThomas Huth     char *qp_name, *qp_value;
85f8b74fc5SThomas Huth     GError *gerror = NULL;
8694d6a7a7SAshijeet Acharya 
8794d6a7a7SAshijeet Acharya     if (!uri) {
8894d6a7a7SAshijeet Acharya         error_setg(errp, "Invalid URI specified");
89f8b74fc5SThomas Huth         return -EINVAL;
9094d6a7a7SAshijeet Acharya     }
91f8b74fc5SThomas Huth     if (!g_str_equal(g_uri_get_scheme(uri), "nfs")) {
9294d6a7a7SAshijeet Acharya         error_setg(errp, "URI scheme must be 'nfs'");
93f8b74fc5SThomas Huth         return -EINVAL;
9494d6a7a7SAshijeet Acharya     }
9594d6a7a7SAshijeet Acharya 
96f8b74fc5SThomas Huth     uri_server = g_uri_get_host(uri);
97f8b74fc5SThomas Huth     if (!uri_server || !uri_server[0]) {
9894d6a7a7SAshijeet Acharya         error_setg(errp, "missing hostname in URI");
99f8b74fc5SThomas Huth         return -EINVAL;
10094d6a7a7SAshijeet Acharya     }
10194d6a7a7SAshijeet Acharya 
102f8b74fc5SThomas Huth     uri_path = g_uri_get_path(uri);
103f8b74fc5SThomas Huth     if (!uri_path || !uri_path[0]) {
10494d6a7a7SAshijeet Acharya         error_setg(errp, "missing file path in URI");
105f8b74fc5SThomas Huth         return -EINVAL;
10694d6a7a7SAshijeet Acharya     }
10794d6a7a7SAshijeet Acharya 
108f8b74fc5SThomas Huth     qdict_put_str(options, "server.host", uri_server);
10946f5ac20SEric Blake     qdict_put_str(options, "server.type", "inet");
110f8b74fc5SThomas Huth     qdict_put_str(options, "path", uri_path);
11194d6a7a7SAshijeet Acharya 
112f8b74fc5SThomas Huth     uri_query = g_uri_get_query(uri);
113f8b74fc5SThomas Huth     if (uri_query) {
114f8b74fc5SThomas Huth         g_uri_params_iter_init(&qp, uri_query, -1, "&", G_URI_PARAMS_NONE);
115f8b74fc5SThomas Huth         while (g_uri_params_iter_next(&qp, &qp_name, &qp_value, &gerror)) {
116bd1386ccSEric Blake             uint64_t val;
117f8b74fc5SThomas Huth             if (!qp_name || gerror) {
118f8b74fc5SThomas Huth                 error_setg(errp, "Failed to parse NFS parameter");
119f8b74fc5SThomas Huth                 return -EINVAL;
120f8b74fc5SThomas Huth             }
121f8b74fc5SThomas Huth             if (!qp_value) {
12294d6a7a7SAshijeet Acharya                 error_setg(errp, "Value for NFS parameter expected: %s",
123f8b74fc5SThomas Huth                            qp_name);
124f8b74fc5SThomas Huth                 return -EINVAL;
12594d6a7a7SAshijeet Acharya             }
126f8b74fc5SThomas Huth             if (parse_uint_full(qp_value, 0, &val)) {
127f8b74fc5SThomas Huth                 error_setg(errp, "Invalid value for NFS parameter: %s",
128f8b74fc5SThomas Huth                            qp_name);
129f8b74fc5SThomas Huth                 return -EINVAL;
13094d6a7a7SAshijeet Acharya             }
131f8b74fc5SThomas Huth             if (g_str_equal(qp_name, "uid")) {
132f8b74fc5SThomas Huth                 qdict_put_str(options, "user", qp_value);
133f8b74fc5SThomas Huth             } else if (g_str_equal(qp_name, "gid")) {
134f8b74fc5SThomas Huth                 qdict_put_str(options, "group", qp_value);
135f8b74fc5SThomas Huth             } else if (g_str_equal(qp_name, "tcp-syncnt")) {
136f8b74fc5SThomas Huth                 qdict_put_str(options, "tcp-syn-count", qp_value);
137f8b74fc5SThomas Huth             } else if (g_str_equal(qp_name, "readahead")) {
138f8b74fc5SThomas Huth                 qdict_put_str(options, "readahead-size", qp_value);
139f8b74fc5SThomas Huth             } else if (g_str_equal(qp_name, "pagecache")) {
140f8b74fc5SThomas Huth                 qdict_put_str(options, "page-cache-size", qp_value);
141f8b74fc5SThomas Huth             } else if (g_str_equal(qp_name, "debug")) {
142f8b74fc5SThomas Huth                 qdict_put_str(options, "debug", qp_value);
14394d6a7a7SAshijeet Acharya             } else {
144f8b74fc5SThomas Huth                 error_setg(errp, "Unknown NFS parameter name: %s", qp_name);
145f8b74fc5SThomas Huth                 return -EINVAL;
14694d6a7a7SAshijeet Acharya             }
14794d6a7a7SAshijeet Acharya         }
14894d6a7a7SAshijeet Acharya     }
149f8b74fc5SThomas Huth 
150f8b74fc5SThomas Huth     return 0;
15194d6a7a7SAshijeet Acharya }
15294d6a7a7SAshijeet Acharya 
nfs_has_filename_options_conflict(QDict * options,Error ** errp)15394d6a7a7SAshijeet Acharya static bool nfs_has_filename_options_conflict(QDict *options, Error **errp)
15494d6a7a7SAshijeet Acharya {
15594d6a7a7SAshijeet Acharya     const QDictEntry *qe;
15694d6a7a7SAshijeet Acharya 
15794d6a7a7SAshijeet Acharya     for (qe = qdict_first(options); qe; qe = qdict_next(options, qe)) {
15894d6a7a7SAshijeet Acharya         if (!strcmp(qe->key, "host") ||
15994d6a7a7SAshijeet Acharya             !strcmp(qe->key, "path") ||
16094d6a7a7SAshijeet Acharya             !strcmp(qe->key, "user") ||
16194d6a7a7SAshijeet Acharya             !strcmp(qe->key, "group") ||
16294d6a7a7SAshijeet Acharya             !strcmp(qe->key, "tcp-syn-count") ||
16394d6a7a7SAshijeet Acharya             !strcmp(qe->key, "readahead-size") ||
16494d6a7a7SAshijeet Acharya             !strcmp(qe->key, "page-cache-size") ||
1657103d916SPrasanna Kumar Kalever             !strcmp(qe->key, "debug") ||
16694d6a7a7SAshijeet Acharya             strstart(qe->key, "server.", NULL))
16794d6a7a7SAshijeet Acharya         {
16894d6a7a7SAshijeet Acharya             error_setg(errp, "Option %s cannot be used with a filename",
16994d6a7a7SAshijeet Acharya                        qe->key);
17094d6a7a7SAshijeet Acharya             return true;
17194d6a7a7SAshijeet Acharya         }
17294d6a7a7SAshijeet Acharya     }
17394d6a7a7SAshijeet Acharya 
17494d6a7a7SAshijeet Acharya     return false;
17594d6a7a7SAshijeet Acharya }
17694d6a7a7SAshijeet Acharya 
nfs_parse_filename(const char * filename,QDict * options,Error ** errp)17794d6a7a7SAshijeet Acharya static void nfs_parse_filename(const char *filename, QDict *options,
17894d6a7a7SAshijeet Acharya                                Error **errp)
17994d6a7a7SAshijeet Acharya {
18094d6a7a7SAshijeet Acharya     if (nfs_has_filename_options_conflict(options, errp)) {
18194d6a7a7SAshijeet Acharya         return;
18294d6a7a7SAshijeet Acharya     }
18394d6a7a7SAshijeet Acharya 
18494d6a7a7SAshijeet Acharya     nfs_parse_uri(filename, options, errp);
18594d6a7a7SAshijeet Acharya }
18694d6a7a7SAshijeet Acharya 
1876542aa9cSPeter Lieven static void nfs_process_read(void *arg);
1886542aa9cSPeter Lieven static void nfs_process_write(void *arg);
1896542aa9cSPeter Lieven 
19037d1e4d9SPaolo Bonzini /* Called with QemuMutex held.  */
nfs_set_events(NFSClient * client)1916542aa9cSPeter Lieven static void nfs_set_events(NFSClient *client)
1926542aa9cSPeter Lieven {
1936542aa9cSPeter Lieven     int ev = nfs_which_events(client->context);
1946542aa9cSPeter Lieven     if (ev != client->events) {
195dca21ef2SFam Zheng         aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
1966542aa9cSPeter Lieven                            (ev & POLLIN) ? nfs_process_read : NULL,
197f6a51c84SStefan Hajnoczi                            (ev & POLLOUT) ? nfs_process_write : NULL,
198826cc324SStefan Hajnoczi                            NULL, NULL, client);
1996542aa9cSPeter Lieven 
2006542aa9cSPeter Lieven     }
2016542aa9cSPeter Lieven     client->events = ev;
2026542aa9cSPeter Lieven }
2036542aa9cSPeter Lieven 
nfs_process_read(void * arg)2046542aa9cSPeter Lieven static void nfs_process_read(void *arg)
2056542aa9cSPeter Lieven {
2066542aa9cSPeter Lieven     NFSClient *client = arg;
2079d456654SPaolo Bonzini 
20837d1e4d9SPaolo Bonzini     qemu_mutex_lock(&client->mutex);
2096542aa9cSPeter Lieven     nfs_service(client->context, POLLIN);
2106542aa9cSPeter Lieven     nfs_set_events(client);
21137d1e4d9SPaolo Bonzini     qemu_mutex_unlock(&client->mutex);
2126542aa9cSPeter Lieven }
2136542aa9cSPeter Lieven 
nfs_process_write(void * arg)2146542aa9cSPeter Lieven static void nfs_process_write(void *arg)
2156542aa9cSPeter Lieven {
2166542aa9cSPeter Lieven     NFSClient *client = arg;
2179d456654SPaolo Bonzini 
21837d1e4d9SPaolo Bonzini     qemu_mutex_lock(&client->mutex);
2196542aa9cSPeter Lieven     nfs_service(client->context, POLLOUT);
2206542aa9cSPeter Lieven     nfs_set_events(client);
22137d1e4d9SPaolo Bonzini     qemu_mutex_unlock(&client->mutex);
2226542aa9cSPeter Lieven }
2236542aa9cSPeter Lieven 
nfs_co_init_task(BlockDriverState * bs,NFSRPC * task)224ee15ee36SPaolo Bonzini static void coroutine_fn nfs_co_init_task(BlockDriverState *bs, NFSRPC *task)
2256542aa9cSPeter Lieven {
2266542aa9cSPeter Lieven     *task = (NFSRPC) {
2276542aa9cSPeter Lieven         .co             = qemu_coroutine_self(),
228d746427aSPaolo Bonzini         .bs             = bs,
229d746427aSPaolo Bonzini         .client         = bs->opaque,
2306542aa9cSPeter Lieven     };
2316542aa9cSPeter Lieven }
2326542aa9cSPeter Lieven 
nfs_co_generic_bh_cb(void * opaque)2336542aa9cSPeter Lieven static void nfs_co_generic_bh_cb(void *opaque)
2346542aa9cSPeter Lieven {
2356542aa9cSPeter Lieven     NFSRPC *task = opaque;
2361919631eSPaolo Bonzini 
237a2c0fe2fSPeter Lieven     task->complete = 1;
2381919631eSPaolo Bonzini     aio_co_wake(task->co);
2396542aa9cSPeter Lieven }
2406542aa9cSPeter Lieven 
24137d1e4d9SPaolo Bonzini /* Called (via nfs_service) with QemuMutex held.  */
2426542aa9cSPeter Lieven static void
nfs_co_generic_cb(int ret,struct nfs_context * nfs,void * data,void * private_data)2436542aa9cSPeter Lieven nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
2446542aa9cSPeter Lieven                   void *private_data)
2456542aa9cSPeter Lieven {
2466542aa9cSPeter Lieven     NFSRPC *task = private_data;
2476542aa9cSPeter Lieven     task->ret = ret;
248d746427aSPaolo Bonzini     assert(!task->st);
2496542aa9cSPeter Lieven     if (task->ret > 0 && task->iov) {
2506542aa9cSPeter Lieven         if (task->ret <= task->iov->size) {
2516542aa9cSPeter Lieven             qemu_iovec_from_buf(task->iov, 0, data, task->ret);
2526542aa9cSPeter Lieven         } else {
2536542aa9cSPeter Lieven             task->ret = -EIO;
2546542aa9cSPeter Lieven         }
2556542aa9cSPeter Lieven     }
25620fccb18SPeter Lieven     if (task->ret < 0) {
25720fccb18SPeter Lieven         error_report("NFS Error: %s", nfs_get_error(nfs));
25820fccb18SPeter Lieven     }
259e4ec5ad4SPavel Dovgalyuk     replay_bh_schedule_oneshot_event(task->client->aio_context,
260471799d1SStefan Hajnoczi                                      nfs_co_generic_bh_cb, task);
2616542aa9cSPeter Lieven }
2626542aa9cSPeter Lieven 
nfs_co_preadv(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * iov,BdrvRequestFlags flags)263f7ef38ddSVladimir Sementsov-Ogievskiy static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, int64_t offset,
264f7ef38ddSVladimir Sementsov-Ogievskiy                                       int64_t bytes, QEMUIOVector *iov,
265f7ef38ddSVladimir Sementsov-Ogievskiy                                       BdrvRequestFlags flags)
2666542aa9cSPeter Lieven {
2676542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
2686542aa9cSPeter Lieven     NFSRPC task;
2696542aa9cSPeter Lieven 
270d746427aSPaolo Bonzini     nfs_co_init_task(bs, &task);
2716542aa9cSPeter Lieven     task.iov = iov;
2726542aa9cSPeter Lieven 
2736e8a355dSDaniel Brodsky     WITH_QEMU_LOCK_GUARD(&client->mutex) {
2746542aa9cSPeter Lieven         if (nfs_pread_async(client->context, client->fh,
27569785a22SPeter Lieven                             offset, bytes, nfs_co_generic_cb, &task) != 0) {
2766542aa9cSPeter Lieven             return -ENOMEM;
2776542aa9cSPeter Lieven         }
2786542aa9cSPeter Lieven 
2796542aa9cSPeter Lieven         nfs_set_events(client);
2806e8a355dSDaniel Brodsky     }
281aa92d6c4SPaolo Bonzini     while (!task.complete) {
2826542aa9cSPeter Lieven         qemu_coroutine_yield();
2836542aa9cSPeter Lieven     }
2846542aa9cSPeter Lieven 
2856542aa9cSPeter Lieven     if (task.ret < 0) {
2866542aa9cSPeter Lieven         return task.ret;
2876542aa9cSPeter Lieven     }
2886542aa9cSPeter Lieven 
2896542aa9cSPeter Lieven     /* zero pad short reads */
2906542aa9cSPeter Lieven     if (task.ret < iov->size) {
2916542aa9cSPeter Lieven         qemu_iovec_memset(iov, task.ret, 0, iov->size - task.ret);
2926542aa9cSPeter Lieven     }
2936542aa9cSPeter Lieven 
2946542aa9cSPeter Lieven     return 0;
2956542aa9cSPeter Lieven }
2966542aa9cSPeter Lieven 
nfs_co_pwritev(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * iov,BdrvRequestFlags flags)297e75abedaSVladimir Sementsov-Ogievskiy static int coroutine_fn nfs_co_pwritev(BlockDriverState *bs, int64_t offset,
298e75abedaSVladimir Sementsov-Ogievskiy                                        int64_t bytes, QEMUIOVector *iov,
299e75abedaSVladimir Sementsov-Ogievskiy                                        BdrvRequestFlags flags)
3006542aa9cSPeter Lieven {
3016542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
3026542aa9cSPeter Lieven     NFSRPC task;
3036542aa9cSPeter Lieven     char *buf = NULL;
304ef503a84SPeter Lieven     bool my_buffer = false;
3056542aa9cSPeter Lieven 
306d746427aSPaolo Bonzini     nfs_co_init_task(bs, &task);
3076542aa9cSPeter Lieven 
308ef503a84SPeter Lieven     if (iov->niov != 1) {
30969785a22SPeter Lieven         buf = g_try_malloc(bytes);
31069785a22SPeter Lieven         if (bytes && buf == NULL) {
3112347dd7bSKevin Wolf             return -ENOMEM;
3122347dd7bSKevin Wolf         }
31369785a22SPeter Lieven         qemu_iovec_to_buf(iov, 0, buf, bytes);
314ef503a84SPeter Lieven         my_buffer = true;
315ef503a84SPeter Lieven     } else {
316ef503a84SPeter Lieven         buf = iov->iov[0].iov_base;
317ef503a84SPeter Lieven     }
3186542aa9cSPeter Lieven 
3196e8a355dSDaniel Brodsky     WITH_QEMU_LOCK_GUARD(&client->mutex) {
3206542aa9cSPeter Lieven         if (nfs_pwrite_async(client->context, client->fh,
32169785a22SPeter Lieven                              offset, bytes, buf,
32269785a22SPeter Lieven                              nfs_co_generic_cb, &task) != 0) {
323ef503a84SPeter Lieven             if (my_buffer) {
3246542aa9cSPeter Lieven                 g_free(buf);
325ef503a84SPeter Lieven             }
3266542aa9cSPeter Lieven             return -ENOMEM;
3276542aa9cSPeter Lieven         }
3286542aa9cSPeter Lieven 
3296542aa9cSPeter Lieven         nfs_set_events(client);
3306e8a355dSDaniel Brodsky     }
331aa92d6c4SPaolo Bonzini     while (!task.complete) {
3326542aa9cSPeter Lieven         qemu_coroutine_yield();
3336542aa9cSPeter Lieven     }
3346542aa9cSPeter Lieven 
335ef503a84SPeter Lieven     if (my_buffer) {
3366542aa9cSPeter Lieven         g_free(buf);
337ef503a84SPeter Lieven     }
3386542aa9cSPeter Lieven 
33969785a22SPeter Lieven     if (task.ret != bytes) {
3406542aa9cSPeter Lieven         return task.ret < 0 ? task.ret : -EIO;
3416542aa9cSPeter Lieven     }
3426542aa9cSPeter Lieven 
3436542aa9cSPeter Lieven     return 0;
3446542aa9cSPeter Lieven }
3456542aa9cSPeter Lieven 
nfs_co_flush(BlockDriverState * bs)3466542aa9cSPeter Lieven static int coroutine_fn nfs_co_flush(BlockDriverState *bs)
3476542aa9cSPeter Lieven {
3486542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
3496542aa9cSPeter Lieven     NFSRPC task;
3506542aa9cSPeter Lieven 
351d746427aSPaolo Bonzini     nfs_co_init_task(bs, &task);
3526542aa9cSPeter Lieven 
3536e8a355dSDaniel Brodsky     WITH_QEMU_LOCK_GUARD(&client->mutex) {
3546542aa9cSPeter Lieven         if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb,
3556542aa9cSPeter Lieven                             &task) != 0) {
3566542aa9cSPeter Lieven             return -ENOMEM;
3576542aa9cSPeter Lieven         }
3586542aa9cSPeter Lieven 
3596542aa9cSPeter Lieven         nfs_set_events(client);
3606e8a355dSDaniel Brodsky     }
361aa92d6c4SPaolo Bonzini     while (!task.complete) {
3626542aa9cSPeter Lieven         qemu_coroutine_yield();
3636542aa9cSPeter Lieven     }
3646542aa9cSPeter Lieven 
3656542aa9cSPeter Lieven     return task.ret;
3666542aa9cSPeter Lieven }
3676542aa9cSPeter Lieven 
nfs_detach_aio_context(BlockDriverState * bs)368471799d1SStefan Hajnoczi static void nfs_detach_aio_context(BlockDriverState *bs)
369471799d1SStefan Hajnoczi {
370471799d1SStefan Hajnoczi     NFSClient *client = bs->opaque;
371471799d1SStefan Hajnoczi 
372dca21ef2SFam Zheng     aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
37360f782b6SStefan Hajnoczi                        NULL, NULL, NULL, NULL, NULL);
374471799d1SStefan Hajnoczi     client->events = 0;
375471799d1SStefan Hajnoczi }
376471799d1SStefan Hajnoczi 
nfs_attach_aio_context(BlockDriverState * bs,AioContext * new_context)377471799d1SStefan Hajnoczi static void nfs_attach_aio_context(BlockDriverState *bs,
378471799d1SStefan Hajnoczi                                    AioContext *new_context)
379471799d1SStefan Hajnoczi {
380471799d1SStefan Hajnoczi     NFSClient *client = bs->opaque;
381471799d1SStefan Hajnoczi 
382471799d1SStefan Hajnoczi     client->aio_context = new_context;
383471799d1SStefan Hajnoczi     nfs_set_events(client);
384471799d1SStefan Hajnoczi }
385471799d1SStefan Hajnoczi 
nfs_client_close(NFSClient * client)3866542aa9cSPeter Lieven static void nfs_client_close(NFSClient *client)
3876542aa9cSPeter Lieven {
3886542aa9cSPeter Lieven     if (client->context) {
389601dc655SPeter Lieven         qemu_mutex_lock(&client->mutex);
390601dc655SPeter Lieven         aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
39160f782b6SStefan Hajnoczi                            NULL, NULL, NULL, NULL, NULL);
392601dc655SPeter Lieven         qemu_mutex_unlock(&client->mutex);
3936542aa9cSPeter Lieven         if (client->fh) {
3946542aa9cSPeter Lieven             nfs_close(client->context, client->fh);
395113fe792SJeff Cody             client->fh = NULL;
3966542aa9cSPeter Lieven         }
397d2c6becbSPeter Lieven #ifdef LIBNFS_FEATURE_UMOUNT
398d2c6becbSPeter Lieven         nfs_umount(client->context);
399d2c6becbSPeter Lieven #endif
4006542aa9cSPeter Lieven         nfs_destroy_context(client->context);
401113fe792SJeff Cody         client->context = NULL;
4026542aa9cSPeter Lieven     }
403113fe792SJeff Cody     g_free(client->path);
404113fe792SJeff Cody     qemu_mutex_destroy(&client->mutex);
405113fe792SJeff Cody     qapi_free_NFSServer(client->server);
406113fe792SJeff Cody     client->server = NULL;
4076542aa9cSPeter Lieven }
4086542aa9cSPeter Lieven 
nfs_file_close(BlockDriverState * bs)4096542aa9cSPeter Lieven static void nfs_file_close(BlockDriverState *bs)
4106542aa9cSPeter Lieven {
4116542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
4126542aa9cSPeter Lieven     nfs_client_close(client);
4136542aa9cSPeter Lieven }
4146542aa9cSPeter Lieven 
nfs_client_open(NFSClient * client,BlockdevOptionsNfs * opts,int flags,int open_flags,Error ** errp)415c22a0345SKevin Wolf static int64_t nfs_client_open(NFSClient *client, BlockdevOptionsNfs *opts,
416cb8d4bf6SFam Zheng                                int flags, int open_flags, Error **errp)
4176542aa9cSPeter Lieven {
418f1a7ff77SPeter Lieven     int64_t ret = -EINVAL;
419ebdebe47SBin Meng #ifdef _WIN32
420ebdebe47SBin Meng     struct __stat64 st;
421ebdebe47SBin Meng #else
4226542aa9cSPeter Lieven     struct stat st;
423ebdebe47SBin Meng #endif
4246542aa9cSPeter Lieven     char *file = NULL, *strp = NULL;
4256542aa9cSPeter Lieven 
426113fe792SJeff Cody     qemu_mutex_init(&client->mutex);
42794d6a7a7SAshijeet Acharya 
428c22a0345SKevin Wolf     client->path = g_strdup(opts->path);
42994d6a7a7SAshijeet Acharya 
43094d6a7a7SAshijeet Acharya     strp = strrchr(client->path, '/');
4316542aa9cSPeter Lieven     if (strp == NULL) {
4326542aa9cSPeter Lieven         error_setg(errp, "Invalid URL specified");
4336542aa9cSPeter Lieven         goto fail;
4346542aa9cSPeter Lieven     }
4356542aa9cSPeter Lieven     file = g_strdup(strp);
4366542aa9cSPeter Lieven     *strp = 0;
4376542aa9cSPeter Lieven 
438c22a0345SKevin Wolf     /* Steal the NFSServer object from opts; set the original pointer to NULL
439c22a0345SKevin Wolf      * to avoid use after free and double free. */
440c22a0345SKevin Wolf     client->server = opts->server;
441c22a0345SKevin Wolf     opts->server = NULL;
44294d6a7a7SAshijeet Acharya 
4436542aa9cSPeter Lieven     client->context = nfs_init_context();
4446542aa9cSPeter Lieven     if (client->context == NULL) {
4456542aa9cSPeter Lieven         error_setg(errp, "Failed to init NFS context");
4466542aa9cSPeter Lieven         goto fail;
4476542aa9cSPeter Lieven     }
4486542aa9cSPeter Lieven 
449c22a0345SKevin Wolf     if (opts->has_user) {
450c22a0345SKevin Wolf         client->uid = opts->user;
45194d6a7a7SAshijeet Acharya         nfs_set_uid(client->context, client->uid);
4526542aa9cSPeter Lieven     }
45394d6a7a7SAshijeet Acharya 
454c22a0345SKevin Wolf     if (opts->has_group) {
455c22a0345SKevin Wolf         client->gid = opts->group;
45694d6a7a7SAshijeet Acharya         nfs_set_gid(client->context, client->gid);
4577c24384bSPeter Lieven     }
45894d6a7a7SAshijeet Acharya 
459c22a0345SKevin Wolf     if (opts->has_tcp_syn_count) {
460c22a0345SKevin Wolf         client->tcp_syncnt = opts->tcp_syn_count;
46194d6a7a7SAshijeet Acharya         nfs_set_tcp_syncnt(client->context, client->tcp_syncnt);
46294d6a7a7SAshijeet Acharya     }
46394d6a7a7SAshijeet Acharya 
464f42ca3caSPeter Lieven #ifdef LIBNFS_FEATURE_READAHEAD
465c22a0345SKevin Wolf     if (opts->has_readahead_size) {
46638f8d5e0SPeter Lieven         if (open_flags & BDRV_O_NOCACHE) {
46738f8d5e0SPeter Lieven             error_setg(errp, "Cannot enable NFS readahead "
46838f8d5e0SPeter Lieven                              "if cache.direct = on");
46938f8d5e0SPeter Lieven             goto fail;
47038f8d5e0SPeter Lieven         }
471c22a0345SKevin Wolf         client->readahead = opts->readahead_size;
47294d6a7a7SAshijeet Acharya         if (client->readahead > QEMU_NFS_MAX_READAHEAD_SIZE) {
4733dc6f869SAlistair Francis             warn_report("Truncating NFS readahead size to %d",
4743dc6f869SAlistair Francis                         QEMU_NFS_MAX_READAHEAD_SIZE);
47594d6a7a7SAshijeet Acharya             client->readahead = QEMU_NFS_MAX_READAHEAD_SIZE;
47629c838cdSPeter Lieven         }
47794d6a7a7SAshijeet Acharya         nfs_set_readahead(client->context, client->readahead);
478d99b26c4SPeter Lieven #ifdef LIBNFS_FEATURE_PAGECACHE
479d99b26c4SPeter Lieven         nfs_set_pagecache_ttl(client->context, 0);
480d99b26c4SPeter Lieven #endif
481d99b26c4SPeter Lieven         client->cache_used = true;
48294d6a7a7SAshijeet Acharya     }
483d99b26c4SPeter Lieven #endif
48494d6a7a7SAshijeet Acharya 
485d99b26c4SPeter Lieven #ifdef LIBNFS_FEATURE_PAGECACHE
486c22a0345SKevin Wolf     if (opts->has_page_cache_size) {
487d99b26c4SPeter Lieven         if (open_flags & BDRV_O_NOCACHE) {
488d99b26c4SPeter Lieven             error_setg(errp, "Cannot enable NFS pagecache "
489d99b26c4SPeter Lieven                              "if cache.direct = on");
490d99b26c4SPeter Lieven             goto fail;
491d99b26c4SPeter Lieven         }
492c22a0345SKevin Wolf         client->pagecache = opts->page_cache_size;
49394d6a7a7SAshijeet Acharya         if (client->pagecache > QEMU_NFS_MAX_PAGECACHE_SIZE) {
4943dc6f869SAlistair Francis             warn_report("Truncating NFS pagecache size to %d pages",
4953dc6f869SAlistair Francis                         QEMU_NFS_MAX_PAGECACHE_SIZE);
49694d6a7a7SAshijeet Acharya             client->pagecache = QEMU_NFS_MAX_PAGECACHE_SIZE;
497d99b26c4SPeter Lieven         }
49894d6a7a7SAshijeet Acharya         nfs_set_pagecache(client->context, client->pagecache);
499d99b26c4SPeter Lieven         nfs_set_pagecache_ttl(client->context, 0);
50038f8d5e0SPeter Lieven         client->cache_used = true;
50194d6a7a7SAshijeet Acharya     }
502f42ca3caSPeter Lieven #endif
50394d6a7a7SAshijeet Acharya 
5047725b8bfSPeter Lieven #ifdef LIBNFS_FEATURE_DEBUG
505c22a0345SKevin Wolf     if (opts->has_debug) {
506c22a0345SKevin Wolf         client->debug = opts->debug;
5077725b8bfSPeter Lieven         /* limit the maximum debug level to avoid potential flooding
5087725b8bfSPeter Lieven          * of our log files. */
50994d6a7a7SAshijeet Acharya         if (client->debug > QEMU_NFS_MAX_DEBUG_LEVEL) {
5103dc6f869SAlistair Francis             warn_report("Limiting NFS debug level to %d",
5113dc6f869SAlistair Francis                         QEMU_NFS_MAX_DEBUG_LEVEL);
51294d6a7a7SAshijeet Acharya             client->debug = QEMU_NFS_MAX_DEBUG_LEVEL;
5137725b8bfSPeter Lieven         }
51494d6a7a7SAshijeet Acharya         nfs_set_debug(client->context, client->debug);
51594d6a7a7SAshijeet Acharya     }
5167725b8bfSPeter Lieven #endif
5176542aa9cSPeter Lieven 
51894d6a7a7SAshijeet Acharya     ret = nfs_mount(client->context, client->server->host, client->path);
5196542aa9cSPeter Lieven     if (ret < 0) {
5206542aa9cSPeter Lieven         error_setg(errp, "Failed to mount nfs share: %s",
5216542aa9cSPeter Lieven                    nfs_get_error(client->context));
5226542aa9cSPeter Lieven         goto fail;
5236542aa9cSPeter Lieven     }
5246542aa9cSPeter Lieven 
5256542aa9cSPeter Lieven     if (flags & O_CREAT) {
5266542aa9cSPeter Lieven         ret = nfs_creat(client->context, file, 0600, &client->fh);
5276542aa9cSPeter Lieven         if (ret < 0) {
5286542aa9cSPeter Lieven             error_setg(errp, "Failed to create file: %s",
5296542aa9cSPeter Lieven                        nfs_get_error(client->context));
5306542aa9cSPeter Lieven             goto fail;
5316542aa9cSPeter Lieven         }
5326542aa9cSPeter Lieven     } else {
5336542aa9cSPeter Lieven         ret = nfs_open(client->context, file, flags, &client->fh);
5346542aa9cSPeter Lieven         if (ret < 0) {
5356542aa9cSPeter Lieven             error_setg(errp, "Failed to open file : %s",
5366542aa9cSPeter Lieven                        nfs_get_error(client->context));
5376542aa9cSPeter Lieven             goto fail;
5386542aa9cSPeter Lieven         }
5396542aa9cSPeter Lieven     }
5406542aa9cSPeter Lieven 
5416542aa9cSPeter Lieven     ret = nfs_fstat(client->context, client->fh, &st);
5426542aa9cSPeter Lieven     if (ret < 0) {
5436542aa9cSPeter Lieven         error_setg(errp, "Failed to fstat file: %s",
5446542aa9cSPeter Lieven                    nfs_get_error(client->context));
5456542aa9cSPeter Lieven         goto fail;
5466542aa9cSPeter Lieven     }
5476542aa9cSPeter Lieven 
5486542aa9cSPeter Lieven     ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE);
549c63b0201SYonggang Luo #if !defined(_WIN32)
55018a8056eSPeter Lieven     client->st_blocks = st.st_blocks;
551c63b0201SYonggang Luo #endif
5526542aa9cSPeter Lieven     client->has_zero_init = S_ISREG(st.st_mode);
55394d6a7a7SAshijeet Acharya     *strp = '/';
5546542aa9cSPeter Lieven     goto out;
55594d6a7a7SAshijeet Acharya 
5566542aa9cSPeter Lieven fail:
5576542aa9cSPeter Lieven     nfs_client_close(client);
5586542aa9cSPeter Lieven out:
5596542aa9cSPeter Lieven     g_free(file);
5606542aa9cSPeter Lieven     return ret;
5616542aa9cSPeter Lieven }
5626542aa9cSPeter Lieven 
nfs_options_qdict_to_qapi(QDict * options,Error ** errp)563a1a42af4SKevin Wolf static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
564a1a42af4SKevin Wolf                                                      Error **errp)
565c22a0345SKevin Wolf {
566c22a0345SKevin Wolf     BlockdevOptionsNfs *opts = NULL;
567c22a0345SKevin Wolf     Visitor *v;
568c82be42cSKevin Wolf     const QDictEntry *e;
569c22a0345SKevin Wolf 
570af91062eSMarkus Armbruster     v = qobject_input_visitor_new_flat_confused(options, errp);
571af91062eSMarkus Armbruster     if (!v) {
572a1a42af4SKevin Wolf         return NULL;
573c22a0345SKevin Wolf     }
574c22a0345SKevin Wolf 
575b11a093cSMarkus Armbruster     visit_type_BlockdevOptionsNfs(v, NULL, &opts, errp);
576c22a0345SKevin Wolf     visit_free(v);
577b11a093cSMarkus Armbruster     if (!opts) {
578a1a42af4SKevin Wolf         return NULL;
579a1a42af4SKevin Wolf     }
580a1a42af4SKevin Wolf 
581c82be42cSKevin Wolf     /* Remove the processed options from the QDict (the visitor processes
582c82be42cSKevin Wolf      * _all_ options in the QDict) */
583c82be42cSKevin Wolf     while ((e = qdict_first(options))) {
584c82be42cSKevin Wolf         qdict_del(options, e->key);
585c82be42cSKevin Wolf     }
586c82be42cSKevin Wolf 
587a1a42af4SKevin Wolf     return opts;
588a1a42af4SKevin Wolf }
589a1a42af4SKevin Wolf 
nfs_client_open_qdict(NFSClient * client,QDict * options,int flags,int open_flags,Error ** errp)590a1a42af4SKevin Wolf static int64_t nfs_client_open_qdict(NFSClient *client, QDict *options,
591a1a42af4SKevin Wolf                                      int flags, int open_flags, Error **errp)
592a1a42af4SKevin Wolf {
593a1a42af4SKevin Wolf     BlockdevOptionsNfs *opts;
594182454dcSPeter Lieven     int64_t ret;
595a1a42af4SKevin Wolf 
596a1a42af4SKevin Wolf     opts = nfs_options_qdict_to_qapi(options, errp);
597a1a42af4SKevin Wolf     if (opts == NULL) {
598c22a0345SKevin Wolf         ret = -EINVAL;
599c22a0345SKevin Wolf         goto fail;
600c22a0345SKevin Wolf     }
601c22a0345SKevin Wolf 
602c22a0345SKevin Wolf     ret = nfs_client_open(client, opts, flags, open_flags, errp);
603c22a0345SKevin Wolf fail:
604c22a0345SKevin Wolf     qapi_free_BlockdevOptionsNfs(opts);
605c22a0345SKevin Wolf     return ret;
606c22a0345SKevin Wolf }
607c22a0345SKevin Wolf 
nfs_file_open(BlockDriverState * bs,QDict * options,int flags,Error ** errp)6086542aa9cSPeter Lieven static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
6096542aa9cSPeter Lieven                          Error **errp) {
6106542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
6116542aa9cSPeter Lieven     int64_t ret;
6126542aa9cSPeter Lieven 
613471799d1SStefan Hajnoczi     client->aio_context = bdrv_get_aio_context(bs);
614471799d1SStefan Hajnoczi 
615c22a0345SKevin Wolf     ret = nfs_client_open_qdict(client, options,
6166542aa9cSPeter Lieven                                 (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
617cb8d4bf6SFam Zheng                                 bs->open_flags, errp);
6186542aa9cSPeter Lieven     if (ret < 0) {
61994d6a7a7SAshijeet Acharya         return ret;
6206542aa9cSPeter Lieven     }
621113fe792SJeff Cody 
6226542aa9cSPeter Lieven     bs->total_sectors = ret;
6238f23aaf5SEric Blake     if (client->has_zero_init) {
6248f23aaf5SEric Blake         bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE;
6258f23aaf5SEric Blake     }
626b3ac2b94SSimran Singhal     return 0;
6276542aa9cSPeter Lieven }
6286542aa9cSPeter Lieven 
629fd752801SMax Reitz static QemuOptsList nfs_create_opts = {
630fd752801SMax Reitz     .name = "nfs-create-opts",
631fd752801SMax Reitz     .head = QTAILQ_HEAD_INITIALIZER(nfs_create_opts.head),
632fd752801SMax Reitz     .desc = {
633fd752801SMax Reitz         {
634fd752801SMax Reitz             .name = BLOCK_OPT_SIZE,
635fd752801SMax Reitz             .type = QEMU_OPT_SIZE,
636fd752801SMax Reitz             .help = "Virtual disk size"
637fd752801SMax Reitz         },
638fd752801SMax Reitz         { /* end of list */ }
639fd752801SMax Reitz     }
640fd752801SMax Reitz };
641fd752801SMax Reitz 
nfs_file_co_create(BlockdevCreateOptions * options,Error ** errp)642a1a42af4SKevin Wolf static int nfs_file_co_create(BlockdevCreateOptions *options, Error **errp)
6436542aa9cSPeter Lieven {
644a1a42af4SKevin Wolf     BlockdevCreateOptionsNfs *opts = &options->u.nfs;
6455839e53bSMarkus Armbruster     NFSClient *client = g_new0(NFSClient, 1);
646a1a42af4SKevin Wolf     int ret;
647a1a42af4SKevin Wolf 
648a1a42af4SKevin Wolf     assert(options->driver == BLOCKDEV_DRIVER_NFS);
6496542aa9cSPeter Lieven 
650471799d1SStefan Hajnoczi     client->aio_context = qemu_get_aio_context();
651471799d1SStefan Hajnoczi 
652a1a42af4SKevin Wolf     ret = nfs_client_open(client, opts->location, O_CREAT, 0, errp);
653a1a42af4SKevin Wolf     if (ret < 0) {
654a1a42af4SKevin Wolf         goto out;
655a1a42af4SKevin Wolf     }
656a1a42af4SKevin Wolf     ret = nfs_ftruncate(client->context, client->fh, opts->size);
657a1a42af4SKevin Wolf     nfs_client_close(client);
658a1a42af4SKevin Wolf 
659a1a42af4SKevin Wolf out:
660a1a42af4SKevin Wolf     g_free(client);
661a1a42af4SKevin Wolf     return ret;
662a1a42af4SKevin Wolf }
663a1a42af4SKevin Wolf 
nfs_file_co_create_opts(BlockDriver * drv,const char * url,QemuOpts * opts,Error ** errp)664b92902dfSMaxim Levitsky static int coroutine_fn nfs_file_co_create_opts(BlockDriver *drv,
665b92902dfSMaxim Levitsky                                                 const char *url,
666b92902dfSMaxim Levitsky                                                 QemuOpts *opts,
667a1a42af4SKevin Wolf                                                 Error **errp)
668a1a42af4SKevin Wolf {
669a1a42af4SKevin Wolf     BlockdevCreateOptions *create_options;
670a1a42af4SKevin Wolf     BlockdevCreateOptionsNfs *nfs_opts;
671a1a42af4SKevin Wolf     QDict *options;
672a1a42af4SKevin Wolf     int ret;
673a1a42af4SKevin Wolf 
674a1a42af4SKevin Wolf     create_options = g_new0(BlockdevCreateOptions, 1);
675a1a42af4SKevin Wolf     create_options->driver = BLOCKDEV_DRIVER_NFS;
676a1a42af4SKevin Wolf     nfs_opts = &create_options->u.nfs;
677a1a42af4SKevin Wolf 
6786542aa9cSPeter Lieven     /* Read out options */
679a1a42af4SKevin Wolf     nfs_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
680c2eb918eSHu Tao                               BDRV_SECTOR_SIZE);
6816542aa9cSPeter Lieven 
68294d6a7a7SAshijeet Acharya     options = qdict_new();
68394d6a7a7SAshijeet Acharya     ret = nfs_parse_uri(url, options, errp);
68494d6a7a7SAshijeet Acharya     if (ret < 0) {
68594d6a7a7SAshijeet Acharya         goto out;
68694d6a7a7SAshijeet Acharya     }
68794d6a7a7SAshijeet Acharya 
688a1a42af4SKevin Wolf     nfs_opts->location = nfs_options_qdict_to_qapi(options, errp);
689a1a42af4SKevin Wolf     if (nfs_opts->location == NULL) {
690a1a42af4SKevin Wolf         ret = -EINVAL;
691a1a42af4SKevin Wolf         goto out;
692a1a42af4SKevin Wolf     }
693a1a42af4SKevin Wolf 
694a1a42af4SKevin Wolf     ret = nfs_file_co_create(create_options, errp);
6956542aa9cSPeter Lieven     if (ret < 0) {
6966542aa9cSPeter Lieven         goto out;
6976542aa9cSPeter Lieven     }
698a1a42af4SKevin Wolf 
699a1a42af4SKevin Wolf     ret = 0;
7006542aa9cSPeter Lieven out:
701cb3e7f08SMarc-André Lureau     qobject_unref(options);
702a1a42af4SKevin Wolf     qapi_free_BlockdevCreateOptions(create_options);
7036542aa9cSPeter Lieven     return ret;
7046542aa9cSPeter Lieven }
7056542aa9cSPeter Lieven 
nfs_has_zero_init(BlockDriverState * bs)7066542aa9cSPeter Lieven static int nfs_has_zero_init(BlockDriverState *bs)
7076542aa9cSPeter Lieven {
7086542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
7096542aa9cSPeter Lieven     return client->has_zero_init;
7106542aa9cSPeter Lieven }
7116542aa9cSPeter Lieven 
712c63b0201SYonggang Luo #if !defined(_WIN32)
71337d1e4d9SPaolo Bonzini /* Called (via nfs_service) with QemuMutex held.  */
714d746427aSPaolo Bonzini static void
nfs_get_allocated_file_size_cb(int ret,struct nfs_context * nfs,void * data,void * private_data)715d746427aSPaolo Bonzini nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data,
716d746427aSPaolo Bonzini                                void *private_data)
717d746427aSPaolo Bonzini {
718d746427aSPaolo Bonzini     NFSRPC *task = private_data;
719d746427aSPaolo Bonzini     task->ret = ret;
720d746427aSPaolo Bonzini     if (task->ret == 0) {
721d746427aSPaolo Bonzini         memcpy(task->st, data, sizeof(struct stat));
722d746427aSPaolo Bonzini     }
723d746427aSPaolo Bonzini     if (task->ret < 0) {
724d746427aSPaolo Bonzini         error_report("NFS Error: %s", nfs_get_error(nfs));
725d746427aSPaolo Bonzini     }
7263fe64abcSPaolo Bonzini     replay_bh_schedule_oneshot_event(task->client->aio_context,
7273fe64abcSPaolo Bonzini                                      nfs_co_generic_bh_cb, task);
728d746427aSPaolo Bonzini }
729d746427aSPaolo Bonzini 
nfs_co_get_allocated_file_size(BlockDriverState * bs)73082618d7bSEmanuele Giuseppe Esposito static int64_t coroutine_fn nfs_co_get_allocated_file_size(BlockDriverState *bs)
7316542aa9cSPeter Lieven {
7326542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
7336542aa9cSPeter Lieven     NFSRPC task = {0};
7346542aa9cSPeter Lieven     struct stat st;
7356542aa9cSPeter Lieven 
73618a8056eSPeter Lieven     if (bdrv_is_read_only(bs) &&
73718a8056eSPeter Lieven         !(bs->open_flags & BDRV_O_NOCACHE)) {
73818a8056eSPeter Lieven         return client->st_blocks * 512;
73918a8056eSPeter Lieven     }
74018a8056eSPeter Lieven 
7413fe64abcSPaolo Bonzini     nfs_co_init_task(bs, &task);
7426542aa9cSPeter Lieven     task.st = &st;
7433fe64abcSPaolo Bonzini     WITH_QEMU_LOCK_GUARD(&client->mutex) {
744d746427aSPaolo Bonzini         if (nfs_fstat_async(client->context, client->fh, nfs_get_allocated_file_size_cb,
7456542aa9cSPeter Lieven                             &task) != 0) {
7466542aa9cSPeter Lieven             return -ENOMEM;
7476542aa9cSPeter Lieven         }
7486542aa9cSPeter Lieven 
7496542aa9cSPeter Lieven         nfs_set_events(client);
7503fe64abcSPaolo Bonzini     }
7513fe64abcSPaolo Bonzini     while (!task.complete) {
7523fe64abcSPaolo Bonzini         qemu_coroutine_yield();
7533fe64abcSPaolo Bonzini     }
7546542aa9cSPeter Lieven 
755055c6f91SPeter Lieven     return (task.ret < 0 ? task.ret : st.st_blocks * 512);
7566542aa9cSPeter Lieven }
757c63b0201SYonggang Luo #endif
7586542aa9cSPeter Lieven 
759061ca8a3SKevin Wolf static int coroutine_fn
nfs_file_co_truncate(BlockDriverState * bs,int64_t offset,bool exact,PreallocMode prealloc,BdrvRequestFlags flags,Error ** errp)760c80d8b06SMax Reitz nfs_file_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
76192b92799SKevin Wolf                      PreallocMode prealloc, BdrvRequestFlags flags,
76292b92799SKevin Wolf                      Error **errp)
7636542aa9cSPeter Lieven {
7646542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
765f59adb32SMax Reitz     int ret;
766f59adb32SMax Reitz 
7678243ccb7SMax Reitz     if (prealloc != PREALLOC_MODE_OFF) {
7688243ccb7SMax Reitz         error_setg(errp, "Unsupported preallocation mode '%s'",
769977c736fSMarkus Armbruster                    PreallocMode_str(prealloc));
7708243ccb7SMax Reitz         return -ENOTSUP;
7718243ccb7SMax Reitz     }
7728243ccb7SMax Reitz 
773f59adb32SMax Reitz     ret = nfs_ftruncate(client->context, client->fh, offset);
774f59adb32SMax Reitz     if (ret < 0) {
775f59adb32SMax Reitz         error_setg_errno(errp, -ret, "Failed to truncate file");
776f59adb32SMax Reitz         return ret;
777f59adb32SMax Reitz     }
778f59adb32SMax Reitz 
779f59adb32SMax Reitz     return 0;
7806542aa9cSPeter Lieven }
7816542aa9cSPeter Lieven 
78218a8056eSPeter Lieven /* Note that this will not re-establish a connection with the NFS server
78318a8056eSPeter Lieven  * - it is effectively a NOP.  */
nfs_reopen_prepare(BDRVReopenState * state,BlockReopenQueue * queue,Error ** errp)78418a8056eSPeter Lieven static int nfs_reopen_prepare(BDRVReopenState *state,
78518a8056eSPeter Lieven                               BlockReopenQueue *queue, Error **errp)
78618a8056eSPeter Lieven {
78718a8056eSPeter Lieven     NFSClient *client = state->bs->opaque;
788ebdebe47SBin Meng #ifdef _WIN32
789ebdebe47SBin Meng     struct __stat64 st;
790ebdebe47SBin Meng #else
79118a8056eSPeter Lieven     struct stat st;
792ebdebe47SBin Meng #endif
79318a8056eSPeter Lieven     int ret = 0;
79418a8056eSPeter Lieven 
79518a8056eSPeter Lieven     if (state->flags & BDRV_O_RDWR && bdrv_is_read_only(state->bs)) {
79618a8056eSPeter Lieven         error_setg(errp, "Cannot open a read-only mount as read-write");
79718a8056eSPeter Lieven         return -EACCES;
79818a8056eSPeter Lieven     }
79918a8056eSPeter Lieven 
80038f8d5e0SPeter Lieven     if ((state->flags & BDRV_O_NOCACHE) && client->cache_used) {
801d99b26c4SPeter Lieven         error_setg(errp, "Cannot disable cache if libnfs readahead or"
802d99b26c4SPeter Lieven                          " pagecache is enabled");
80338f8d5e0SPeter Lieven         return -EINVAL;
80438f8d5e0SPeter Lieven     }
80538f8d5e0SPeter Lieven 
80618a8056eSPeter Lieven     /* Update cache for read-only reopens */
80718a8056eSPeter Lieven     if (!(state->flags & BDRV_O_RDWR)) {
80818a8056eSPeter Lieven         ret = nfs_fstat(client->context, client->fh, &st);
80918a8056eSPeter Lieven         if (ret < 0) {
81018a8056eSPeter Lieven             error_setg(errp, "Failed to fstat file: %s",
81118a8056eSPeter Lieven                        nfs_get_error(client->context));
81218a8056eSPeter Lieven             return ret;
81318a8056eSPeter Lieven         }
814c63b0201SYonggang Luo #if !defined(_WIN32)
81518a8056eSPeter Lieven         client->st_blocks = st.st_blocks;
816c63b0201SYonggang Luo #endif
81718a8056eSPeter Lieven     }
81818a8056eSPeter Lieven 
81918a8056eSPeter Lieven     return 0;
82018a8056eSPeter Lieven }
82118a8056eSPeter Lieven 
nfs_refresh_filename(BlockDriverState * bs)822998b3a1eSMax Reitz static void nfs_refresh_filename(BlockDriverState *bs)
82394d6a7a7SAshijeet Acharya {
82494d6a7a7SAshijeet Acharya     NFSClient *client = bs->opaque;
82594d6a7a7SAshijeet Acharya 
82694d6a7a7SAshijeet Acharya     if (client->uid && !client->gid) {
82794d6a7a7SAshijeet Acharya         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
82894d6a7a7SAshijeet Acharya                  "nfs://%s%s?uid=%" PRId64, client->server->host, client->path,
82994d6a7a7SAshijeet Acharya                  client->uid);
83094d6a7a7SAshijeet Acharya     } else if (!client->uid && client->gid) {
83194d6a7a7SAshijeet Acharya         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
83294d6a7a7SAshijeet Acharya                  "nfs://%s%s?gid=%" PRId64, client->server->host, client->path,
83394d6a7a7SAshijeet Acharya                  client->gid);
83494d6a7a7SAshijeet Acharya     } else if (client->uid && client->gid) {
83594d6a7a7SAshijeet Acharya         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
83694d6a7a7SAshijeet Acharya                  "nfs://%s%s?uid=%" PRId64 "&gid=%" PRId64,
83794d6a7a7SAshijeet Acharya                  client->server->host, client->path, client->uid, client->gid);
83894d6a7a7SAshijeet Acharya     } else {
83994d6a7a7SAshijeet Acharya         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
84094d6a7a7SAshijeet Acharya                  "nfs://%s%s", client->server->host, client->path);
84194d6a7a7SAshijeet Acharya     }
84294d6a7a7SAshijeet Acharya }
84394d6a7a7SAshijeet Acharya 
nfs_dirname(BlockDriverState * bs,Error ** errp)844b7cfc7d5SKevin Wolf static char * GRAPH_RDLOCK nfs_dirname(BlockDriverState *bs, Error **errp)
8450dcbc54aSMax Reitz {
8460dcbc54aSMax Reitz     NFSClient *client = bs->opaque;
8470dcbc54aSMax Reitz 
8480dcbc54aSMax Reitz     if (client->uid || client->gid) {
8490dcbc54aSMax Reitz         bdrv_refresh_filename(bs);
8500dcbc54aSMax Reitz         error_setg(errp, "Cannot generate a base directory for NFS node '%s'",
8510dcbc54aSMax Reitz                    bs->filename);
8520dcbc54aSMax Reitz         return NULL;
8530dcbc54aSMax Reitz     }
8540dcbc54aSMax Reitz 
8550dcbc54aSMax Reitz     return g_strdup_printf("nfs://%s%s/", client->server->host, client->path);
8560dcbc54aSMax Reitz }
8570dcbc54aSMax Reitz 
858d99b26c4SPeter Lieven #ifdef LIBNFS_FEATURE_PAGECACHE
nfs_co_invalidate_cache(BlockDriverState * bs,Error ** errp)8592b148f39SPaolo Bonzini static void coroutine_fn nfs_co_invalidate_cache(BlockDriverState *bs,
860d99b26c4SPeter Lieven                                                  Error **errp)
861d99b26c4SPeter Lieven {
862d99b26c4SPeter Lieven     NFSClient *client = bs->opaque;
863d99b26c4SPeter Lieven     nfs_pagecache_invalidate(client->context, client->fh);
864d99b26c4SPeter Lieven }
865d99b26c4SPeter Lieven #endif
866d99b26c4SPeter Lieven 
8672654267cSMax Reitz static const char *nfs_strong_runtime_opts[] = {
8682654267cSMax Reitz     "path",
8692654267cSMax Reitz     "user",
8702654267cSMax Reitz     "group",
8712654267cSMax Reitz     "server.",
8722654267cSMax Reitz 
8732654267cSMax Reitz     NULL
8742654267cSMax Reitz };
8752654267cSMax Reitz 
8766542aa9cSPeter Lieven static BlockDriver bdrv_nfs = {
8776542aa9cSPeter Lieven     .format_name                    = "nfs",
8786542aa9cSPeter Lieven     .protocol_name                  = "nfs",
8796542aa9cSPeter Lieven 
8806542aa9cSPeter Lieven     .instance_size                  = sizeof(NFSClient),
88194d6a7a7SAshijeet Acharya     .bdrv_parse_filename            = nfs_parse_filename,
882fd752801SMax Reitz     .create_opts                    = &nfs_create_opts,
883fd752801SMax Reitz 
8846542aa9cSPeter Lieven     .bdrv_has_zero_init             = nfs_has_zero_init,
885c63b0201SYonggang Luo /* libnfs does not provide the allocated filesize of a file on win32. */
886c63b0201SYonggang Luo #if !defined(_WIN32)
88782618d7bSEmanuele Giuseppe Esposito     .bdrv_co_get_allocated_file_size = nfs_co_get_allocated_file_size,
888c63b0201SYonggang Luo #endif
889061ca8a3SKevin Wolf     .bdrv_co_truncate               = nfs_file_co_truncate,
8906542aa9cSPeter Lieven 
891*44b424dcSPaolo Bonzini     .bdrv_open                      = nfs_file_open,
8926542aa9cSPeter Lieven     .bdrv_close                     = nfs_file_close,
893a1a42af4SKevin Wolf     .bdrv_co_create                 = nfs_file_co_create,
894efc75e2aSStefan Hajnoczi     .bdrv_co_create_opts            = nfs_file_co_create_opts,
89518a8056eSPeter Lieven     .bdrv_reopen_prepare            = nfs_reopen_prepare,
8966542aa9cSPeter Lieven 
89769785a22SPeter Lieven     .bdrv_co_preadv                 = nfs_co_preadv,
89869785a22SPeter Lieven     .bdrv_co_pwritev                = nfs_co_pwritev,
8996542aa9cSPeter Lieven     .bdrv_co_flush_to_disk          = nfs_co_flush,
900471799d1SStefan Hajnoczi 
901471799d1SStefan Hajnoczi     .bdrv_detach_aio_context        = nfs_detach_aio_context,
902471799d1SStefan Hajnoczi     .bdrv_attach_aio_context        = nfs_attach_aio_context,
90394d6a7a7SAshijeet Acharya     .bdrv_refresh_filename          = nfs_refresh_filename,
9040dcbc54aSMax Reitz     .bdrv_dirname                   = nfs_dirname,
905d99b26c4SPeter Lieven 
9062654267cSMax Reitz     .strong_runtime_opts            = nfs_strong_runtime_opts,
9072654267cSMax Reitz 
908d99b26c4SPeter Lieven #ifdef LIBNFS_FEATURE_PAGECACHE
9092b148f39SPaolo Bonzini     .bdrv_co_invalidate_cache       = nfs_co_invalidate_cache,
910d99b26c4SPeter Lieven #endif
9116542aa9cSPeter Lieven };
9126542aa9cSPeter Lieven 
nfs_block_init(void)9136542aa9cSPeter Lieven static void nfs_block_init(void)
9146542aa9cSPeter Lieven {
9156542aa9cSPeter Lieven     bdrv_register(&bdrv_nfs);
9166542aa9cSPeter Lieven }
9176542aa9cSPeter Lieven 
9186542aa9cSPeter Lieven block_init(nfs_block_init);
919