xref: /qemu/block/nfs.c (revision 977c736f)
16542aa9cSPeter Lieven /*
26542aa9cSPeter Lieven  * QEMU Block driver for native access to files on NFS shares
36542aa9cSPeter Lieven  *
438f8d5e0SPeter Lieven  * Copyright (c) 2014-2016 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 
276542aa9cSPeter Lieven #include <poll.h>
286542aa9cSPeter Lieven #include "qemu-common.h"
296542aa9cSPeter Lieven #include "qemu/config-file.h"
306542aa9cSPeter Lieven #include "qemu/error-report.h"
31d165b8cbSStefan Hajnoczi #include "qapi/error.h"
326542aa9cSPeter Lieven #include "block/block_int.h"
336542aa9cSPeter Lieven #include "trace.h"
346542aa9cSPeter Lieven #include "qemu/iov.h"
356542aa9cSPeter Lieven #include "qemu/uri.h"
360d94b746SStefan Hajnoczi #include "qemu/cutils.h"
376542aa9cSPeter Lieven #include "sysemu/sysemu.h"
3894d6a7a7SAshijeet Acharya #include "qapi/qmp/qdict.h"
3994d6a7a7SAshijeet Acharya #include "qapi/qmp/qstring.h"
4094d6a7a7SAshijeet Acharya #include "qapi-visit.h"
4194d6a7a7SAshijeet Acharya #include "qapi/qobject-input-visitor.h"
4294d6a7a7SAshijeet Acharya #include "qapi/qobject-output-visitor.h"
436542aa9cSPeter Lieven #include <nfsc/libnfs.h>
446542aa9cSPeter Lieven 
4594d6a7a7SAshijeet Acharya 
4629c838cdSPeter Lieven #define QEMU_NFS_MAX_READAHEAD_SIZE 1048576
47d99b26c4SPeter Lieven #define QEMU_NFS_MAX_PAGECACHE_SIZE (8388608 / NFS_BLKSIZE)
487725b8bfSPeter Lieven #define QEMU_NFS_MAX_DEBUG_LEVEL 2
4929c838cdSPeter Lieven 
506542aa9cSPeter Lieven typedef struct NFSClient {
516542aa9cSPeter Lieven     struct nfs_context *context;
526542aa9cSPeter Lieven     struct nfsfh *fh;
536542aa9cSPeter Lieven     int events;
546542aa9cSPeter Lieven     bool has_zero_init;
55471799d1SStefan Hajnoczi     AioContext *aio_context;
5637d1e4d9SPaolo Bonzini     QemuMutex mutex;
5718a8056eSPeter Lieven     blkcnt_t st_blocks;
5838f8d5e0SPeter Lieven     bool cache_used;
5994d6a7a7SAshijeet Acharya     NFSServer *server;
6094d6a7a7SAshijeet Acharya     char *path;
6194d6a7a7SAshijeet Acharya     int64_t uid, gid, tcp_syncnt, readahead, pagecache, debug;
626542aa9cSPeter Lieven } NFSClient;
636542aa9cSPeter Lieven 
646542aa9cSPeter Lieven typedef struct NFSRPC {
65d746427aSPaolo Bonzini     BlockDriverState *bs;
666542aa9cSPeter Lieven     int ret;
676542aa9cSPeter Lieven     int complete;
686542aa9cSPeter Lieven     QEMUIOVector *iov;
696542aa9cSPeter Lieven     struct stat *st;
706542aa9cSPeter Lieven     Coroutine *co;
71471799d1SStefan Hajnoczi     NFSClient *client;
726542aa9cSPeter Lieven } NFSRPC;
736542aa9cSPeter Lieven 
7494d6a7a7SAshijeet Acharya static int nfs_parse_uri(const char *filename, QDict *options, Error **errp)
7594d6a7a7SAshijeet Acharya {
7694d6a7a7SAshijeet Acharya     URI *uri = NULL;
7794d6a7a7SAshijeet Acharya     QueryParams *qp = NULL;
7894d6a7a7SAshijeet Acharya     int ret = -EINVAL, i;
7994d6a7a7SAshijeet Acharya 
8094d6a7a7SAshijeet Acharya     uri = uri_parse(filename);
8194d6a7a7SAshijeet Acharya     if (!uri) {
8294d6a7a7SAshijeet Acharya         error_setg(errp, "Invalid URI specified");
8394d6a7a7SAshijeet Acharya         goto out;
8494d6a7a7SAshijeet Acharya     }
85f69165a8SMax Reitz     if (g_strcmp0(uri->scheme, "nfs") != 0) {
8694d6a7a7SAshijeet Acharya         error_setg(errp, "URI scheme must be 'nfs'");
8794d6a7a7SAshijeet Acharya         goto out;
8894d6a7a7SAshijeet Acharya     }
8994d6a7a7SAshijeet Acharya 
9094d6a7a7SAshijeet Acharya     if (!uri->server) {
9194d6a7a7SAshijeet Acharya         error_setg(errp, "missing hostname in URI");
9294d6a7a7SAshijeet Acharya         goto out;
9394d6a7a7SAshijeet Acharya     }
9494d6a7a7SAshijeet Acharya 
9594d6a7a7SAshijeet Acharya     if (!uri->path) {
9694d6a7a7SAshijeet Acharya         error_setg(errp, "missing file path in URI");
9794d6a7a7SAshijeet Acharya         goto out;
9894d6a7a7SAshijeet Acharya     }
9994d6a7a7SAshijeet Acharya 
10094d6a7a7SAshijeet Acharya     qp = query_params_parse(uri->query);
10194d6a7a7SAshijeet Acharya     if (!qp) {
10294d6a7a7SAshijeet Acharya         error_setg(errp, "could not parse query parameters");
10394d6a7a7SAshijeet Acharya         goto out;
10494d6a7a7SAshijeet Acharya     }
10594d6a7a7SAshijeet Acharya 
10646f5ac20SEric Blake     qdict_put_str(options, "server.host", uri->server);
10746f5ac20SEric Blake     qdict_put_str(options, "server.type", "inet");
10846f5ac20SEric Blake     qdict_put_str(options, "path", uri->path);
10994d6a7a7SAshijeet Acharya 
11094d6a7a7SAshijeet Acharya     for (i = 0; i < qp->n; i++) {
1118d20abe8SPeter Lieven         unsigned long long val;
11294d6a7a7SAshijeet Acharya         if (!qp->p[i].value) {
11394d6a7a7SAshijeet Acharya             error_setg(errp, "Value for NFS parameter expected: %s",
11494d6a7a7SAshijeet Acharya                        qp->p[i].name);
11594d6a7a7SAshijeet Acharya             goto out;
11694d6a7a7SAshijeet Acharya         }
1178d20abe8SPeter Lieven         if (parse_uint_full(qp->p[i].value, &val, 0)) {
11894d6a7a7SAshijeet Acharya             error_setg(errp, "Illegal value for NFS parameter: %s",
11994d6a7a7SAshijeet Acharya                        qp->p[i].name);
12094d6a7a7SAshijeet Acharya             goto out;
12194d6a7a7SAshijeet Acharya         }
12294d6a7a7SAshijeet Acharya         if (!strcmp(qp->p[i].name, "uid")) {
12346f5ac20SEric Blake             qdict_put_str(options, "user", qp->p[i].value);
12494d6a7a7SAshijeet Acharya         } else if (!strcmp(qp->p[i].name, "gid")) {
12546f5ac20SEric Blake             qdict_put_str(options, "group", qp->p[i].value);
12694d6a7a7SAshijeet Acharya         } else if (!strcmp(qp->p[i].name, "tcp-syncnt")) {
12746f5ac20SEric Blake             qdict_put_str(options, "tcp-syn-count", qp->p[i].value);
12894d6a7a7SAshijeet Acharya         } else if (!strcmp(qp->p[i].name, "readahead")) {
12946f5ac20SEric Blake             qdict_put_str(options, "readahead-size", qp->p[i].value);
13094d6a7a7SAshijeet Acharya         } else if (!strcmp(qp->p[i].name, "pagecache")) {
13146f5ac20SEric Blake             qdict_put_str(options, "page-cache-size", qp->p[i].value);
13294d6a7a7SAshijeet Acharya         } else if (!strcmp(qp->p[i].name, "debug")) {
13346f5ac20SEric Blake             qdict_put_str(options, "debug", qp->p[i].value);
13494d6a7a7SAshijeet Acharya         } else {
13594d6a7a7SAshijeet Acharya             error_setg(errp, "Unknown NFS parameter name: %s",
13694d6a7a7SAshijeet Acharya                        qp->p[i].name);
13794d6a7a7SAshijeet Acharya             goto out;
13894d6a7a7SAshijeet Acharya         }
13994d6a7a7SAshijeet Acharya     }
14094d6a7a7SAshijeet Acharya     ret = 0;
14194d6a7a7SAshijeet Acharya out:
14294d6a7a7SAshijeet Acharya     if (qp) {
14394d6a7a7SAshijeet Acharya         query_params_free(qp);
14494d6a7a7SAshijeet Acharya     }
14594d6a7a7SAshijeet Acharya     if (uri) {
14694d6a7a7SAshijeet Acharya         uri_free(uri);
14794d6a7a7SAshijeet Acharya     }
14894d6a7a7SAshijeet Acharya     return ret;
14994d6a7a7SAshijeet Acharya }
15094d6a7a7SAshijeet Acharya 
15194d6a7a7SAshijeet Acharya static bool nfs_has_filename_options_conflict(QDict *options, Error **errp)
15294d6a7a7SAshijeet Acharya {
15394d6a7a7SAshijeet Acharya     const QDictEntry *qe;
15494d6a7a7SAshijeet Acharya 
15594d6a7a7SAshijeet Acharya     for (qe = qdict_first(options); qe; qe = qdict_next(options, qe)) {
15694d6a7a7SAshijeet Acharya         if (!strcmp(qe->key, "host") ||
15794d6a7a7SAshijeet Acharya             !strcmp(qe->key, "path") ||
15894d6a7a7SAshijeet Acharya             !strcmp(qe->key, "user") ||
15994d6a7a7SAshijeet Acharya             !strcmp(qe->key, "group") ||
16094d6a7a7SAshijeet Acharya             !strcmp(qe->key, "tcp-syn-count") ||
16194d6a7a7SAshijeet Acharya             !strcmp(qe->key, "readahead-size") ||
16294d6a7a7SAshijeet Acharya             !strcmp(qe->key, "page-cache-size") ||
1637103d916SPrasanna Kumar Kalever             !strcmp(qe->key, "debug") ||
16494d6a7a7SAshijeet Acharya             strstart(qe->key, "server.", NULL))
16594d6a7a7SAshijeet Acharya         {
16694d6a7a7SAshijeet Acharya             error_setg(errp, "Option %s cannot be used with a filename",
16794d6a7a7SAshijeet Acharya                        qe->key);
16894d6a7a7SAshijeet Acharya             return true;
16994d6a7a7SAshijeet Acharya         }
17094d6a7a7SAshijeet Acharya     }
17194d6a7a7SAshijeet Acharya 
17294d6a7a7SAshijeet Acharya     return false;
17394d6a7a7SAshijeet Acharya }
17494d6a7a7SAshijeet Acharya 
17594d6a7a7SAshijeet Acharya static void nfs_parse_filename(const char *filename, QDict *options,
17694d6a7a7SAshijeet Acharya                                Error **errp)
17794d6a7a7SAshijeet Acharya {
17894d6a7a7SAshijeet Acharya     if (nfs_has_filename_options_conflict(options, errp)) {
17994d6a7a7SAshijeet Acharya         return;
18094d6a7a7SAshijeet Acharya     }
18194d6a7a7SAshijeet Acharya 
18294d6a7a7SAshijeet Acharya     nfs_parse_uri(filename, options, errp);
18394d6a7a7SAshijeet Acharya }
18494d6a7a7SAshijeet Acharya 
1856542aa9cSPeter Lieven static void nfs_process_read(void *arg);
1866542aa9cSPeter Lieven static void nfs_process_write(void *arg);
1876542aa9cSPeter Lieven 
18837d1e4d9SPaolo Bonzini /* Called with QemuMutex held.  */
1896542aa9cSPeter Lieven static void nfs_set_events(NFSClient *client)
1906542aa9cSPeter Lieven {
1916542aa9cSPeter Lieven     int ev = nfs_which_events(client->context);
1926542aa9cSPeter Lieven     if (ev != client->events) {
193dca21ef2SFam Zheng         aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
194dca21ef2SFam Zheng                            false,
1956542aa9cSPeter Lieven                            (ev & POLLIN) ? nfs_process_read : NULL,
196f6a51c84SStefan Hajnoczi                            (ev & POLLOUT) ? nfs_process_write : NULL,
197f6a51c84SStefan Hajnoczi                            NULL, client);
1986542aa9cSPeter Lieven 
1996542aa9cSPeter Lieven     }
2006542aa9cSPeter Lieven     client->events = ev;
2016542aa9cSPeter Lieven }
2026542aa9cSPeter Lieven 
2036542aa9cSPeter Lieven static void nfs_process_read(void *arg)
2046542aa9cSPeter Lieven {
2056542aa9cSPeter Lieven     NFSClient *client = arg;
2069d456654SPaolo Bonzini 
20737d1e4d9SPaolo Bonzini     qemu_mutex_lock(&client->mutex);
2086542aa9cSPeter Lieven     nfs_service(client->context, POLLIN);
2096542aa9cSPeter Lieven     nfs_set_events(client);
21037d1e4d9SPaolo Bonzini     qemu_mutex_unlock(&client->mutex);
2116542aa9cSPeter Lieven }
2126542aa9cSPeter Lieven 
2136542aa9cSPeter Lieven static void nfs_process_write(void *arg)
2146542aa9cSPeter Lieven {
2156542aa9cSPeter Lieven     NFSClient *client = arg;
2169d456654SPaolo Bonzini 
21737d1e4d9SPaolo Bonzini     qemu_mutex_lock(&client->mutex);
2186542aa9cSPeter Lieven     nfs_service(client->context, POLLOUT);
2196542aa9cSPeter Lieven     nfs_set_events(client);
22037d1e4d9SPaolo Bonzini     qemu_mutex_unlock(&client->mutex);
2216542aa9cSPeter Lieven }
2226542aa9cSPeter Lieven 
223d746427aSPaolo Bonzini static void nfs_co_init_task(BlockDriverState *bs, NFSRPC *task)
2246542aa9cSPeter Lieven {
2256542aa9cSPeter Lieven     *task = (NFSRPC) {
2266542aa9cSPeter Lieven         .co             = qemu_coroutine_self(),
227d746427aSPaolo Bonzini         .bs             = bs,
228d746427aSPaolo Bonzini         .client         = bs->opaque,
2296542aa9cSPeter Lieven     };
2306542aa9cSPeter Lieven }
2316542aa9cSPeter Lieven 
2326542aa9cSPeter Lieven static void nfs_co_generic_bh_cb(void *opaque)
2336542aa9cSPeter Lieven {
2346542aa9cSPeter Lieven     NFSRPC *task = opaque;
2351919631eSPaolo Bonzini 
236a2c0fe2fSPeter Lieven     task->complete = 1;
2371919631eSPaolo Bonzini     aio_co_wake(task->co);
2386542aa9cSPeter Lieven }
2396542aa9cSPeter Lieven 
24037d1e4d9SPaolo Bonzini /* Called (via nfs_service) with QemuMutex held.  */
2416542aa9cSPeter Lieven static void
2426542aa9cSPeter Lieven nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
2436542aa9cSPeter Lieven                   void *private_data)
2446542aa9cSPeter Lieven {
2456542aa9cSPeter Lieven     NFSRPC *task = private_data;
2466542aa9cSPeter Lieven     task->ret = ret;
247d746427aSPaolo Bonzini     assert(!task->st);
2486542aa9cSPeter Lieven     if (task->ret > 0 && task->iov) {
2496542aa9cSPeter Lieven         if (task->ret <= task->iov->size) {
2506542aa9cSPeter Lieven             qemu_iovec_from_buf(task->iov, 0, data, task->ret);
2516542aa9cSPeter Lieven         } else {
2526542aa9cSPeter Lieven             task->ret = -EIO;
2536542aa9cSPeter Lieven         }
2546542aa9cSPeter Lieven     }
25520fccb18SPeter Lieven     if (task->ret < 0) {
25620fccb18SPeter Lieven         error_report("NFS Error: %s", nfs_get_error(nfs));
25720fccb18SPeter Lieven     }
258fffb6e12SPaolo Bonzini     aio_bh_schedule_oneshot(task->client->aio_context,
259471799d1SStefan Hajnoczi                             nfs_co_generic_bh_cb, task);
2606542aa9cSPeter Lieven }
2616542aa9cSPeter Lieven 
26269785a22SPeter Lieven static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, uint64_t offset,
26369785a22SPeter Lieven                                       uint64_t bytes, QEMUIOVector *iov,
26469785a22SPeter Lieven                                       int flags)
2656542aa9cSPeter Lieven {
2666542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
2676542aa9cSPeter Lieven     NFSRPC task;
2686542aa9cSPeter Lieven 
269d746427aSPaolo Bonzini     nfs_co_init_task(bs, &task);
2706542aa9cSPeter Lieven     task.iov = iov;
2716542aa9cSPeter Lieven 
27237d1e4d9SPaolo Bonzini     qemu_mutex_lock(&client->mutex);
2736542aa9cSPeter Lieven     if (nfs_pread_async(client->context, client->fh,
27469785a22SPeter Lieven                         offset, bytes, nfs_co_generic_cb, &task) != 0) {
27537d1e4d9SPaolo Bonzini         qemu_mutex_unlock(&client->mutex);
2766542aa9cSPeter Lieven         return -ENOMEM;
2776542aa9cSPeter Lieven     }
2786542aa9cSPeter Lieven 
2796542aa9cSPeter Lieven     nfs_set_events(client);
28037d1e4d9SPaolo Bonzini     qemu_mutex_unlock(&client->mutex);
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 
29769785a22SPeter Lieven static int coroutine_fn nfs_co_pwritev(BlockDriverState *bs, uint64_t offset,
29869785a22SPeter Lieven                                        uint64_t bytes, QEMUIOVector *iov,
29969785a22SPeter Lieven                                        int 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 
31937d1e4d9SPaolo Bonzini     qemu_mutex_lock(&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) {
32337d1e4d9SPaolo Bonzini         qemu_mutex_unlock(&client->mutex);
324ef503a84SPeter Lieven         if (my_buffer) {
3256542aa9cSPeter Lieven             g_free(buf);
326ef503a84SPeter Lieven         }
3276542aa9cSPeter Lieven         return -ENOMEM;
3286542aa9cSPeter Lieven     }
3296542aa9cSPeter Lieven 
3306542aa9cSPeter Lieven     nfs_set_events(client);
33137d1e4d9SPaolo Bonzini     qemu_mutex_unlock(&client->mutex);
332aa92d6c4SPaolo Bonzini     while (!task.complete) {
3336542aa9cSPeter Lieven         qemu_coroutine_yield();
3346542aa9cSPeter Lieven     }
3356542aa9cSPeter Lieven 
336ef503a84SPeter Lieven     if (my_buffer) {
3376542aa9cSPeter Lieven         g_free(buf);
338ef503a84SPeter Lieven     }
3396542aa9cSPeter Lieven 
34069785a22SPeter Lieven     if (task.ret != bytes) {
3416542aa9cSPeter Lieven         return task.ret < 0 ? task.ret : -EIO;
3426542aa9cSPeter Lieven     }
3436542aa9cSPeter Lieven 
3446542aa9cSPeter Lieven     return 0;
3456542aa9cSPeter Lieven }
3466542aa9cSPeter Lieven 
3476542aa9cSPeter Lieven static int coroutine_fn nfs_co_flush(BlockDriverState *bs)
3486542aa9cSPeter Lieven {
3496542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
3506542aa9cSPeter Lieven     NFSRPC task;
3516542aa9cSPeter Lieven 
352d746427aSPaolo Bonzini     nfs_co_init_task(bs, &task);
3536542aa9cSPeter Lieven 
35437d1e4d9SPaolo Bonzini     qemu_mutex_lock(&client->mutex);
3556542aa9cSPeter Lieven     if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb,
3566542aa9cSPeter Lieven                         &task) != 0) {
35737d1e4d9SPaolo Bonzini         qemu_mutex_unlock(&client->mutex);
3586542aa9cSPeter Lieven         return -ENOMEM;
3596542aa9cSPeter Lieven     }
3606542aa9cSPeter Lieven 
3616542aa9cSPeter Lieven     nfs_set_events(client);
36237d1e4d9SPaolo Bonzini     qemu_mutex_unlock(&client->mutex);
363aa92d6c4SPaolo Bonzini     while (!task.complete) {
3646542aa9cSPeter Lieven         qemu_coroutine_yield();
3656542aa9cSPeter Lieven     }
3666542aa9cSPeter Lieven 
3676542aa9cSPeter Lieven     return task.ret;
3686542aa9cSPeter Lieven }
3696542aa9cSPeter Lieven 
3706542aa9cSPeter Lieven static QemuOptsList runtime_opts = {
3716542aa9cSPeter Lieven     .name = "nfs",
3726542aa9cSPeter Lieven     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
3736542aa9cSPeter Lieven     .desc = {
3746542aa9cSPeter Lieven         {
37594d6a7a7SAshijeet Acharya             .name = "path",
3766542aa9cSPeter Lieven             .type = QEMU_OPT_STRING,
37794d6a7a7SAshijeet Acharya             .help = "Path of the image on the host",
37894d6a7a7SAshijeet Acharya         },
37994d6a7a7SAshijeet Acharya         {
380f67409a5SPeter Lieven             .name = "user",
38194d6a7a7SAshijeet Acharya             .type = QEMU_OPT_NUMBER,
38294d6a7a7SAshijeet Acharya             .help = "UID value to use when talking to the server",
38394d6a7a7SAshijeet Acharya         },
38494d6a7a7SAshijeet Acharya         {
385f67409a5SPeter Lieven             .name = "group",
38694d6a7a7SAshijeet Acharya             .type = QEMU_OPT_NUMBER,
38794d6a7a7SAshijeet Acharya             .help = "GID value to use when talking to the server",
38894d6a7a7SAshijeet Acharya         },
38994d6a7a7SAshijeet Acharya         {
390f67409a5SPeter Lieven             .name = "tcp-syn-count",
39194d6a7a7SAshijeet Acharya             .type = QEMU_OPT_NUMBER,
39294d6a7a7SAshijeet Acharya             .help = "Number of SYNs to send during the session establish",
39394d6a7a7SAshijeet Acharya         },
39494d6a7a7SAshijeet Acharya         {
395f67409a5SPeter Lieven             .name = "readahead-size",
39694d6a7a7SAshijeet Acharya             .type = QEMU_OPT_NUMBER,
39794d6a7a7SAshijeet Acharya             .help = "Set the readahead size in bytes",
39894d6a7a7SAshijeet Acharya         },
39994d6a7a7SAshijeet Acharya         {
400f67409a5SPeter Lieven             .name = "page-cache-size",
40194d6a7a7SAshijeet Acharya             .type = QEMU_OPT_NUMBER,
40294d6a7a7SAshijeet Acharya             .help = "Set the pagecache size in bytes",
40394d6a7a7SAshijeet Acharya         },
40494d6a7a7SAshijeet Acharya         {
40594d6a7a7SAshijeet Acharya             .name = "debug",
40694d6a7a7SAshijeet Acharya             .type = QEMU_OPT_NUMBER,
40794d6a7a7SAshijeet Acharya             .help = "Set the NFS debug level (max 2)",
4086542aa9cSPeter Lieven         },
4096542aa9cSPeter Lieven         { /* end of list */ }
4106542aa9cSPeter Lieven     },
4116542aa9cSPeter Lieven };
4126542aa9cSPeter Lieven 
413471799d1SStefan Hajnoczi static void nfs_detach_aio_context(BlockDriverState *bs)
414471799d1SStefan Hajnoczi {
415471799d1SStefan Hajnoczi     NFSClient *client = bs->opaque;
416471799d1SStefan Hajnoczi 
417dca21ef2SFam Zheng     aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
418f6a51c84SStefan Hajnoczi                        false, NULL, NULL, NULL, NULL);
419471799d1SStefan Hajnoczi     client->events = 0;
420471799d1SStefan Hajnoczi }
421471799d1SStefan Hajnoczi 
422471799d1SStefan Hajnoczi static void nfs_attach_aio_context(BlockDriverState *bs,
423471799d1SStefan Hajnoczi                                    AioContext *new_context)
424471799d1SStefan Hajnoczi {
425471799d1SStefan Hajnoczi     NFSClient *client = bs->opaque;
426471799d1SStefan Hajnoczi 
427471799d1SStefan Hajnoczi     client->aio_context = new_context;
428471799d1SStefan Hajnoczi     nfs_set_events(client);
429471799d1SStefan Hajnoczi }
430471799d1SStefan Hajnoczi 
4316542aa9cSPeter Lieven static void nfs_client_close(NFSClient *client)
4326542aa9cSPeter Lieven {
4336542aa9cSPeter Lieven     if (client->context) {
4346542aa9cSPeter Lieven         if (client->fh) {
4356542aa9cSPeter Lieven             nfs_close(client->context, client->fh);
436113fe792SJeff Cody             client->fh = NULL;
4376542aa9cSPeter Lieven         }
438dca21ef2SFam Zheng         aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
439f6a51c84SStefan Hajnoczi                            false, NULL, NULL, NULL, NULL);
4406542aa9cSPeter Lieven         nfs_destroy_context(client->context);
441113fe792SJeff Cody         client->context = NULL;
4426542aa9cSPeter Lieven     }
443113fe792SJeff Cody     g_free(client->path);
444113fe792SJeff Cody     qemu_mutex_destroy(&client->mutex);
445113fe792SJeff Cody     qapi_free_NFSServer(client->server);
446113fe792SJeff Cody     client->server = NULL;
4476542aa9cSPeter Lieven }
4486542aa9cSPeter Lieven 
4496542aa9cSPeter Lieven static void nfs_file_close(BlockDriverState *bs)
4506542aa9cSPeter Lieven {
4516542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
4526542aa9cSPeter Lieven     nfs_client_close(client);
4536542aa9cSPeter Lieven }
4546542aa9cSPeter Lieven 
45594d6a7a7SAshijeet Acharya static NFSServer *nfs_config(QDict *options, Error **errp)
45694d6a7a7SAshijeet Acharya {
45794d6a7a7SAshijeet Acharya     NFSServer *server = NULL;
45894d6a7a7SAshijeet Acharya     QDict *addr = NULL;
45994d6a7a7SAshijeet Acharya     QObject *crumpled_addr = NULL;
46094d6a7a7SAshijeet Acharya     Visitor *iv = NULL;
46194d6a7a7SAshijeet Acharya     Error *local_error = NULL;
46294d6a7a7SAshijeet Acharya 
46394d6a7a7SAshijeet Acharya     qdict_extract_subqdict(options, &addr, "server.");
46494d6a7a7SAshijeet Acharya     if (!qdict_size(addr)) {
46594d6a7a7SAshijeet Acharya         error_setg(errp, "NFS server address missing");
46694d6a7a7SAshijeet Acharya         goto out;
46794d6a7a7SAshijeet Acharya     }
46894d6a7a7SAshijeet Acharya 
46994d6a7a7SAshijeet Acharya     crumpled_addr = qdict_crumple(addr, errp);
47094d6a7a7SAshijeet Acharya     if (!crumpled_addr) {
47194d6a7a7SAshijeet Acharya         goto out;
47294d6a7a7SAshijeet Acharya     }
47394d6a7a7SAshijeet Acharya 
474129c7d1cSMarkus Armbruster     /*
475129c7d1cSMarkus Armbruster      * Caution: this works only because all scalar members of
476129c7d1cSMarkus Armbruster      * NFSServer are QString in @crumpled_addr.  The visitor expects
477129c7d1cSMarkus Armbruster      * @crumpled_addr to be typed according to the QAPI schema.  It
478129c7d1cSMarkus Armbruster      * is when @options come from -blockdev or blockdev_add.  But when
479129c7d1cSMarkus Armbruster      * they come from -drive, they're all QString.
480129c7d1cSMarkus Armbruster      */
481048abb7bSMarkus Armbruster     iv = qobject_input_visitor_new(crumpled_addr);
48294d6a7a7SAshijeet Acharya     visit_type_NFSServer(iv, NULL, &server, &local_error);
48394d6a7a7SAshijeet Acharya     if (local_error) {
48494d6a7a7SAshijeet Acharya         error_propagate(errp, local_error);
48594d6a7a7SAshijeet Acharya         goto out;
48694d6a7a7SAshijeet Acharya     }
48794d6a7a7SAshijeet Acharya 
48894d6a7a7SAshijeet Acharya out:
48994d6a7a7SAshijeet Acharya     QDECREF(addr);
49094d6a7a7SAshijeet Acharya     qobject_decref(crumpled_addr);
49194d6a7a7SAshijeet Acharya     visit_free(iv);
49294d6a7a7SAshijeet Acharya     return server;
49394d6a7a7SAshijeet Acharya }
49494d6a7a7SAshijeet Acharya 
49594d6a7a7SAshijeet Acharya 
49694d6a7a7SAshijeet Acharya static int64_t nfs_client_open(NFSClient *client, QDict *options,
497cb8d4bf6SFam Zheng                                int flags, int open_flags, Error **errp)
4986542aa9cSPeter Lieven {
49994d6a7a7SAshijeet Acharya     int ret = -EINVAL;
50094d6a7a7SAshijeet Acharya     QemuOpts *opts = NULL;
50194d6a7a7SAshijeet Acharya     Error *local_err = NULL;
5026542aa9cSPeter Lieven     struct stat st;
5036542aa9cSPeter Lieven     char *file = NULL, *strp = NULL;
5046542aa9cSPeter Lieven 
505113fe792SJeff Cody     qemu_mutex_init(&client->mutex);
50694d6a7a7SAshijeet Acharya     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
50794d6a7a7SAshijeet Acharya     qemu_opts_absorb_qdict(opts, options, &local_err);
50894d6a7a7SAshijeet Acharya     if (local_err) {
50994d6a7a7SAshijeet Acharya         error_propagate(errp, local_err);
51094d6a7a7SAshijeet Acharya         ret = -EINVAL;
5116542aa9cSPeter Lieven         goto fail;
5126542aa9cSPeter Lieven     }
51394d6a7a7SAshijeet Acharya 
51494d6a7a7SAshijeet Acharya     client->path = g_strdup(qemu_opt_get(opts, "path"));
51594d6a7a7SAshijeet Acharya     if (!client->path) {
51694d6a7a7SAshijeet Acharya         ret = -EINVAL;
51794d6a7a7SAshijeet Acharya         error_setg(errp, "No path was specified");
5185f4d5e1aSMax Reitz         goto fail;
5195f4d5e1aSMax Reitz     }
52094d6a7a7SAshijeet Acharya 
52194d6a7a7SAshijeet Acharya     strp = strrchr(client->path, '/');
5226542aa9cSPeter Lieven     if (strp == NULL) {
5236542aa9cSPeter Lieven         error_setg(errp, "Invalid URL specified");
5246542aa9cSPeter Lieven         goto fail;
5256542aa9cSPeter Lieven     }
5266542aa9cSPeter Lieven     file = g_strdup(strp);
5276542aa9cSPeter Lieven     *strp = 0;
5286542aa9cSPeter Lieven 
52994d6a7a7SAshijeet Acharya     /* Pop the config into our state object, Exit if invalid */
53094d6a7a7SAshijeet Acharya     client->server = nfs_config(options, errp);
53194d6a7a7SAshijeet Acharya     if (!client->server) {
53294d6a7a7SAshijeet Acharya         ret = -EINVAL;
53394d6a7a7SAshijeet Acharya         goto fail;
53494d6a7a7SAshijeet Acharya     }
53594d6a7a7SAshijeet Acharya 
5366542aa9cSPeter Lieven     client->context = nfs_init_context();
5376542aa9cSPeter Lieven     if (client->context == NULL) {
5386542aa9cSPeter Lieven         error_setg(errp, "Failed to init NFS context");
5396542aa9cSPeter Lieven         goto fail;
5406542aa9cSPeter Lieven     }
5416542aa9cSPeter Lieven 
542f67409a5SPeter Lieven     if (qemu_opt_get(opts, "user")) {
543f67409a5SPeter Lieven         client->uid = qemu_opt_get_number(opts, "user", 0);
54494d6a7a7SAshijeet Acharya         nfs_set_uid(client->context, client->uid);
5456542aa9cSPeter Lieven     }
54694d6a7a7SAshijeet Acharya 
547f67409a5SPeter Lieven     if (qemu_opt_get(opts, "group")) {
548f67409a5SPeter Lieven         client->gid = qemu_opt_get_number(opts, "group", 0);
54994d6a7a7SAshijeet Acharya         nfs_set_gid(client->context, client->gid);
5507c24384bSPeter Lieven     }
55194d6a7a7SAshijeet Acharya 
552f67409a5SPeter Lieven     if (qemu_opt_get(opts, "tcp-syn-count")) {
553f67409a5SPeter Lieven         client->tcp_syncnt = qemu_opt_get_number(opts, "tcp-syn-count", 0);
55494d6a7a7SAshijeet Acharya         nfs_set_tcp_syncnt(client->context, client->tcp_syncnt);
55594d6a7a7SAshijeet Acharya     }
55694d6a7a7SAshijeet Acharya 
557f42ca3caSPeter Lieven #ifdef LIBNFS_FEATURE_READAHEAD
558f67409a5SPeter Lieven     if (qemu_opt_get(opts, "readahead-size")) {
55938f8d5e0SPeter Lieven         if (open_flags & BDRV_O_NOCACHE) {
56038f8d5e0SPeter Lieven             error_setg(errp, "Cannot enable NFS readahead "
56138f8d5e0SPeter Lieven                              "if cache.direct = on");
56238f8d5e0SPeter Lieven             goto fail;
56338f8d5e0SPeter Lieven         }
564f67409a5SPeter Lieven         client->readahead = qemu_opt_get_number(opts, "readahead-size", 0);
56594d6a7a7SAshijeet Acharya         if (client->readahead > QEMU_NFS_MAX_READAHEAD_SIZE) {
5663dc6f869SAlistair Francis             warn_report("Truncating NFS readahead size to %d",
5673dc6f869SAlistair Francis                         QEMU_NFS_MAX_READAHEAD_SIZE);
56894d6a7a7SAshijeet Acharya             client->readahead = QEMU_NFS_MAX_READAHEAD_SIZE;
56929c838cdSPeter Lieven         }
57094d6a7a7SAshijeet Acharya         nfs_set_readahead(client->context, client->readahead);
571d99b26c4SPeter Lieven #ifdef LIBNFS_FEATURE_PAGECACHE
572d99b26c4SPeter Lieven         nfs_set_pagecache_ttl(client->context, 0);
573d99b26c4SPeter Lieven #endif
574d99b26c4SPeter Lieven         client->cache_used = true;
57594d6a7a7SAshijeet Acharya     }
576d99b26c4SPeter Lieven #endif
57794d6a7a7SAshijeet Acharya 
578d99b26c4SPeter Lieven #ifdef LIBNFS_FEATURE_PAGECACHE
579f67409a5SPeter Lieven     if (qemu_opt_get(opts, "page-cache-size")) {
580d99b26c4SPeter Lieven         if (open_flags & BDRV_O_NOCACHE) {
581d99b26c4SPeter Lieven             error_setg(errp, "Cannot enable NFS pagecache "
582d99b26c4SPeter Lieven                              "if cache.direct = on");
583d99b26c4SPeter Lieven             goto fail;
584d99b26c4SPeter Lieven         }
585f67409a5SPeter Lieven         client->pagecache = qemu_opt_get_number(opts, "page-cache-size", 0);
58694d6a7a7SAshijeet Acharya         if (client->pagecache > QEMU_NFS_MAX_PAGECACHE_SIZE) {
5873dc6f869SAlistair Francis             warn_report("Truncating NFS pagecache size to %d pages",
5883dc6f869SAlistair Francis                         QEMU_NFS_MAX_PAGECACHE_SIZE);
58994d6a7a7SAshijeet Acharya             client->pagecache = QEMU_NFS_MAX_PAGECACHE_SIZE;
590d99b26c4SPeter Lieven         }
59194d6a7a7SAshijeet Acharya         nfs_set_pagecache(client->context, client->pagecache);
592d99b26c4SPeter Lieven         nfs_set_pagecache_ttl(client->context, 0);
59338f8d5e0SPeter Lieven         client->cache_used = true;
59494d6a7a7SAshijeet Acharya     }
595f42ca3caSPeter Lieven #endif
59694d6a7a7SAshijeet Acharya 
5977725b8bfSPeter Lieven #ifdef LIBNFS_FEATURE_DEBUG
59894d6a7a7SAshijeet Acharya     if (qemu_opt_get(opts, "debug")) {
59994d6a7a7SAshijeet Acharya         client->debug = qemu_opt_get_number(opts, "debug", 0);
6007725b8bfSPeter Lieven         /* limit the maximum debug level to avoid potential flooding
6017725b8bfSPeter Lieven          * of our log files. */
60294d6a7a7SAshijeet Acharya         if (client->debug > QEMU_NFS_MAX_DEBUG_LEVEL) {
6033dc6f869SAlistair Francis             warn_report("Limiting NFS debug level to %d",
6043dc6f869SAlistair Francis                         QEMU_NFS_MAX_DEBUG_LEVEL);
60594d6a7a7SAshijeet Acharya             client->debug = QEMU_NFS_MAX_DEBUG_LEVEL;
6067725b8bfSPeter Lieven         }
60794d6a7a7SAshijeet Acharya         nfs_set_debug(client->context, client->debug);
60894d6a7a7SAshijeet Acharya     }
6097725b8bfSPeter Lieven #endif
6106542aa9cSPeter Lieven 
61194d6a7a7SAshijeet Acharya     ret = nfs_mount(client->context, client->server->host, client->path);
6126542aa9cSPeter Lieven     if (ret < 0) {
6136542aa9cSPeter Lieven         error_setg(errp, "Failed to mount nfs share: %s",
6146542aa9cSPeter Lieven                    nfs_get_error(client->context));
6156542aa9cSPeter Lieven         goto fail;
6166542aa9cSPeter Lieven     }
6176542aa9cSPeter Lieven 
6186542aa9cSPeter Lieven     if (flags & O_CREAT) {
6196542aa9cSPeter Lieven         ret = nfs_creat(client->context, file, 0600, &client->fh);
6206542aa9cSPeter Lieven         if (ret < 0) {
6216542aa9cSPeter Lieven             error_setg(errp, "Failed to create file: %s",
6226542aa9cSPeter Lieven                        nfs_get_error(client->context));
6236542aa9cSPeter Lieven             goto fail;
6246542aa9cSPeter Lieven         }
6256542aa9cSPeter Lieven     } else {
6266542aa9cSPeter Lieven         ret = nfs_open(client->context, file, flags, &client->fh);
6276542aa9cSPeter Lieven         if (ret < 0) {
6286542aa9cSPeter Lieven             error_setg(errp, "Failed to open file : %s",
6296542aa9cSPeter Lieven                        nfs_get_error(client->context));
6306542aa9cSPeter Lieven             goto fail;
6316542aa9cSPeter Lieven         }
6326542aa9cSPeter Lieven     }
6336542aa9cSPeter Lieven 
6346542aa9cSPeter Lieven     ret = nfs_fstat(client->context, client->fh, &st);
6356542aa9cSPeter Lieven     if (ret < 0) {
6366542aa9cSPeter Lieven         error_setg(errp, "Failed to fstat file: %s",
6376542aa9cSPeter Lieven                    nfs_get_error(client->context));
6386542aa9cSPeter Lieven         goto fail;
6396542aa9cSPeter Lieven     }
6406542aa9cSPeter Lieven 
6416542aa9cSPeter Lieven     ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE);
64218a8056eSPeter Lieven     client->st_blocks = st.st_blocks;
6436542aa9cSPeter Lieven     client->has_zero_init = S_ISREG(st.st_mode);
64494d6a7a7SAshijeet Acharya     *strp = '/';
6456542aa9cSPeter Lieven     goto out;
64694d6a7a7SAshijeet Acharya 
6476542aa9cSPeter Lieven fail:
6486542aa9cSPeter Lieven     nfs_client_close(client);
6496542aa9cSPeter Lieven out:
65094d6a7a7SAshijeet Acharya     qemu_opts_del(opts);
6516542aa9cSPeter Lieven     g_free(file);
6526542aa9cSPeter Lieven     return ret;
6536542aa9cSPeter Lieven }
6546542aa9cSPeter Lieven 
6556542aa9cSPeter Lieven static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
6566542aa9cSPeter Lieven                          Error **errp) {
6576542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
6586542aa9cSPeter Lieven     int64_t ret;
6596542aa9cSPeter Lieven 
660471799d1SStefan Hajnoczi     client->aio_context = bdrv_get_aio_context(bs);
661471799d1SStefan Hajnoczi 
66294d6a7a7SAshijeet Acharya     ret = nfs_client_open(client, options,
6636542aa9cSPeter Lieven                           (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
664cb8d4bf6SFam Zheng                           bs->open_flags, errp);
6656542aa9cSPeter Lieven     if (ret < 0) {
66694d6a7a7SAshijeet Acharya         return ret;
6676542aa9cSPeter Lieven     }
668113fe792SJeff Cody 
6696542aa9cSPeter Lieven     bs->total_sectors = ret;
670810f4f86SFam Zheng     ret = 0;
671810f4f86SFam Zheng     return ret;
6726542aa9cSPeter Lieven }
6736542aa9cSPeter Lieven 
674fd752801SMax Reitz static QemuOptsList nfs_create_opts = {
675fd752801SMax Reitz     .name = "nfs-create-opts",
676fd752801SMax Reitz     .head = QTAILQ_HEAD_INITIALIZER(nfs_create_opts.head),
677fd752801SMax Reitz     .desc = {
678fd752801SMax Reitz         {
679fd752801SMax Reitz             .name = BLOCK_OPT_SIZE,
680fd752801SMax Reitz             .type = QEMU_OPT_SIZE,
681fd752801SMax Reitz             .help = "Virtual disk size"
682fd752801SMax Reitz         },
683fd752801SMax Reitz         { /* end of list */ }
684fd752801SMax Reitz     }
685fd752801SMax Reitz };
686fd752801SMax Reitz 
68798c10b81SChunyan Liu static int nfs_file_create(const char *url, QemuOpts *opts, Error **errp)
6886542aa9cSPeter Lieven {
6896542aa9cSPeter Lieven     int ret = 0;
6906542aa9cSPeter Lieven     int64_t total_size = 0;
6915839e53bSMarkus Armbruster     NFSClient *client = g_new0(NFSClient, 1);
69294d6a7a7SAshijeet Acharya     QDict *options = NULL;
6936542aa9cSPeter Lieven 
694471799d1SStefan Hajnoczi     client->aio_context = qemu_get_aio_context();
695471799d1SStefan Hajnoczi 
6966542aa9cSPeter Lieven     /* Read out options */
697c2eb918eSHu Tao     total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
698c2eb918eSHu Tao                           BDRV_SECTOR_SIZE);
6996542aa9cSPeter Lieven 
70094d6a7a7SAshijeet Acharya     options = qdict_new();
70194d6a7a7SAshijeet Acharya     ret = nfs_parse_uri(url, options, errp);
70294d6a7a7SAshijeet Acharya     if (ret < 0) {
70394d6a7a7SAshijeet Acharya         goto out;
70494d6a7a7SAshijeet Acharya     }
70594d6a7a7SAshijeet Acharya 
706cb8d4bf6SFam Zheng     ret = nfs_client_open(client, options, O_CREAT, 0, errp);
7076542aa9cSPeter Lieven     if (ret < 0) {
7086542aa9cSPeter Lieven         goto out;
7096542aa9cSPeter Lieven     }
7106542aa9cSPeter Lieven     ret = nfs_ftruncate(client->context, client->fh, total_size);
7116542aa9cSPeter Lieven     nfs_client_close(client);
7126542aa9cSPeter Lieven out:
71307555ba6SKevin Wolf     QDECREF(options);
7146542aa9cSPeter Lieven     g_free(client);
7156542aa9cSPeter Lieven     return ret;
7166542aa9cSPeter Lieven }
7176542aa9cSPeter Lieven 
7186542aa9cSPeter Lieven static int nfs_has_zero_init(BlockDriverState *bs)
7196542aa9cSPeter Lieven {
7206542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
7216542aa9cSPeter Lieven     return client->has_zero_init;
7226542aa9cSPeter Lieven }
7236542aa9cSPeter Lieven 
72437d1e4d9SPaolo Bonzini /* Called (via nfs_service) with QemuMutex held.  */
725d746427aSPaolo Bonzini static void
726d746427aSPaolo Bonzini nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data,
727d746427aSPaolo Bonzini                                void *private_data)
728d746427aSPaolo Bonzini {
729d746427aSPaolo Bonzini     NFSRPC *task = private_data;
730d746427aSPaolo Bonzini     task->ret = ret;
731d746427aSPaolo Bonzini     if (task->ret == 0) {
732d746427aSPaolo Bonzini         memcpy(task->st, data, sizeof(struct stat));
733d746427aSPaolo Bonzini     }
734d746427aSPaolo Bonzini     if (task->ret < 0) {
735d746427aSPaolo Bonzini         error_report("NFS Error: %s", nfs_get_error(nfs));
736d746427aSPaolo Bonzini     }
737e2a6ae7fSPaolo Bonzini 
738e2a6ae7fSPaolo Bonzini     /* Set task->complete before reading bs->wakeup.  */
739e2a6ae7fSPaolo Bonzini     atomic_mb_set(&task->complete, 1);
740c9d1a561SPaolo Bonzini     bdrv_wakeup(task->bs);
741d746427aSPaolo Bonzini }
742d746427aSPaolo Bonzini 
7436542aa9cSPeter Lieven static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
7446542aa9cSPeter Lieven {
7456542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
7466542aa9cSPeter Lieven     NFSRPC task = {0};
7476542aa9cSPeter Lieven     struct stat st;
7486542aa9cSPeter Lieven 
74918a8056eSPeter Lieven     if (bdrv_is_read_only(bs) &&
75018a8056eSPeter Lieven         !(bs->open_flags & BDRV_O_NOCACHE)) {
75118a8056eSPeter Lieven         return client->st_blocks * 512;
75218a8056eSPeter Lieven     }
75318a8056eSPeter Lieven 
754d746427aSPaolo Bonzini     task.bs = bs;
7556542aa9cSPeter Lieven     task.st = &st;
756d746427aSPaolo Bonzini     if (nfs_fstat_async(client->context, client->fh, nfs_get_allocated_file_size_cb,
7576542aa9cSPeter Lieven                         &task) != 0) {
7586542aa9cSPeter Lieven         return -ENOMEM;
7596542aa9cSPeter Lieven     }
7606542aa9cSPeter Lieven 
7616542aa9cSPeter Lieven     nfs_set_events(client);
762d746427aSPaolo Bonzini     BDRV_POLL_WHILE(bs, !task.complete);
7636542aa9cSPeter Lieven 
764055c6f91SPeter Lieven     return (task.ret < 0 ? task.ret : st.st_blocks * 512);
7656542aa9cSPeter Lieven }
7666542aa9cSPeter Lieven 
7678243ccb7SMax Reitz static int nfs_file_truncate(BlockDriverState *bs, int64_t offset,
7688243ccb7SMax Reitz                              PreallocMode prealloc, Error **errp)
7696542aa9cSPeter Lieven {
7706542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
771f59adb32SMax Reitz     int ret;
772f59adb32SMax Reitz 
7738243ccb7SMax Reitz     if (prealloc != PREALLOC_MODE_OFF) {
7748243ccb7SMax Reitz         error_setg(errp, "Unsupported preallocation mode '%s'",
775*977c736fSMarkus Armbruster                    PreallocMode_str(prealloc));
7768243ccb7SMax Reitz         return -ENOTSUP;
7778243ccb7SMax Reitz     }
7788243ccb7SMax Reitz 
779f59adb32SMax Reitz     ret = nfs_ftruncate(client->context, client->fh, offset);
780f59adb32SMax Reitz     if (ret < 0) {
781f59adb32SMax Reitz         error_setg_errno(errp, -ret, "Failed to truncate file");
782f59adb32SMax Reitz         return ret;
783f59adb32SMax Reitz     }
784f59adb32SMax Reitz 
785f59adb32SMax Reitz     return 0;
7866542aa9cSPeter Lieven }
7876542aa9cSPeter Lieven 
78818a8056eSPeter Lieven /* Note that this will not re-establish a connection with the NFS server
78918a8056eSPeter Lieven  * - it is effectively a NOP.  */
79018a8056eSPeter Lieven static int nfs_reopen_prepare(BDRVReopenState *state,
79118a8056eSPeter Lieven                               BlockReopenQueue *queue, Error **errp)
79218a8056eSPeter Lieven {
79318a8056eSPeter Lieven     NFSClient *client = state->bs->opaque;
79418a8056eSPeter Lieven     struct stat st;
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         }
81618a8056eSPeter Lieven         client->st_blocks = st.st_blocks;
81718a8056eSPeter Lieven     }
81818a8056eSPeter Lieven 
81918a8056eSPeter Lieven     return 0;
82018a8056eSPeter Lieven }
82118a8056eSPeter Lieven 
82294d6a7a7SAshijeet Acharya static void nfs_refresh_filename(BlockDriverState *bs, QDict *options)
82394d6a7a7SAshijeet Acharya {
82494d6a7a7SAshijeet Acharya     NFSClient *client = bs->opaque;
82594d6a7a7SAshijeet Acharya     QDict *opts = qdict_new();
82694d6a7a7SAshijeet Acharya     QObject *server_qdict;
82794d6a7a7SAshijeet Acharya     Visitor *ov;
82894d6a7a7SAshijeet Acharya 
82946f5ac20SEric Blake     qdict_put_str(opts, "driver", "nfs");
83094d6a7a7SAshijeet Acharya 
83194d6a7a7SAshijeet Acharya     if (client->uid && !client->gid) {
83294d6a7a7SAshijeet Acharya         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
83394d6a7a7SAshijeet Acharya                  "nfs://%s%s?uid=%" PRId64, client->server->host, client->path,
83494d6a7a7SAshijeet Acharya                  client->uid);
83594d6a7a7SAshijeet Acharya     } else if (!client->uid && client->gid) {
83694d6a7a7SAshijeet Acharya         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
83794d6a7a7SAshijeet Acharya                  "nfs://%s%s?gid=%" PRId64, client->server->host, client->path,
83894d6a7a7SAshijeet Acharya                  client->gid);
83994d6a7a7SAshijeet Acharya     } else if (client->uid && client->gid) {
84094d6a7a7SAshijeet Acharya         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
84194d6a7a7SAshijeet Acharya                  "nfs://%s%s?uid=%" PRId64 "&gid=%" PRId64,
84294d6a7a7SAshijeet Acharya                  client->server->host, client->path, client->uid, client->gid);
84394d6a7a7SAshijeet Acharya     } else {
84494d6a7a7SAshijeet Acharya         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
84594d6a7a7SAshijeet Acharya                  "nfs://%s%s", client->server->host, client->path);
84694d6a7a7SAshijeet Acharya     }
84794d6a7a7SAshijeet Acharya 
84894d6a7a7SAshijeet Acharya     ov = qobject_output_visitor_new(&server_qdict);
84994d6a7a7SAshijeet Acharya     visit_type_NFSServer(ov, NULL, &client->server, &error_abort);
85094d6a7a7SAshijeet Acharya     visit_complete(ov, &server_qdict);
85194d6a7a7SAshijeet Acharya     qdict_put_obj(opts, "server", server_qdict);
85246f5ac20SEric Blake     qdict_put_str(opts, "path", client->path);
85394d6a7a7SAshijeet Acharya 
85494d6a7a7SAshijeet Acharya     if (client->uid) {
85546f5ac20SEric Blake         qdict_put_int(opts, "user", client->uid);
85694d6a7a7SAshijeet Acharya     }
85794d6a7a7SAshijeet Acharya     if (client->gid) {
85846f5ac20SEric Blake         qdict_put_int(opts, "group", client->gid);
85994d6a7a7SAshijeet Acharya     }
86094d6a7a7SAshijeet Acharya     if (client->tcp_syncnt) {
86146f5ac20SEric Blake         qdict_put_int(opts, "tcp-syn-cnt", client->tcp_syncnt);
86294d6a7a7SAshijeet Acharya     }
86394d6a7a7SAshijeet Acharya     if (client->readahead) {
86446f5ac20SEric Blake         qdict_put_int(opts, "readahead-size", client->readahead);
86594d6a7a7SAshijeet Acharya     }
86694d6a7a7SAshijeet Acharya     if (client->pagecache) {
86746f5ac20SEric Blake         qdict_put_int(opts, "page-cache-size", client->pagecache);
86894d6a7a7SAshijeet Acharya     }
86994d6a7a7SAshijeet Acharya     if (client->debug) {
87046f5ac20SEric Blake         qdict_put_int(opts, "debug", client->debug);
87194d6a7a7SAshijeet Acharya     }
87294d6a7a7SAshijeet Acharya 
87394d6a7a7SAshijeet Acharya     visit_free(ov);
87494d6a7a7SAshijeet Acharya     qdict_flatten(opts);
87594d6a7a7SAshijeet Acharya     bs->full_open_options = opts;
87694d6a7a7SAshijeet Acharya }
87794d6a7a7SAshijeet Acharya 
878d99b26c4SPeter Lieven #ifdef LIBNFS_FEATURE_PAGECACHE
879d99b26c4SPeter Lieven static void nfs_invalidate_cache(BlockDriverState *bs,
880d99b26c4SPeter Lieven                                  Error **errp)
881d99b26c4SPeter Lieven {
882d99b26c4SPeter Lieven     NFSClient *client = bs->opaque;
883d99b26c4SPeter Lieven     nfs_pagecache_invalidate(client->context, client->fh);
884d99b26c4SPeter Lieven }
885d99b26c4SPeter Lieven #endif
886d99b26c4SPeter Lieven 
8876542aa9cSPeter Lieven static BlockDriver bdrv_nfs = {
8886542aa9cSPeter Lieven     .format_name                    = "nfs",
8896542aa9cSPeter Lieven     .protocol_name                  = "nfs",
8906542aa9cSPeter Lieven 
8916542aa9cSPeter Lieven     .instance_size                  = sizeof(NFSClient),
89294d6a7a7SAshijeet Acharya     .bdrv_parse_filename            = nfs_parse_filename,
893fd752801SMax Reitz     .create_opts                    = &nfs_create_opts,
894fd752801SMax Reitz 
8956542aa9cSPeter Lieven     .bdrv_has_zero_init             = nfs_has_zero_init,
8966542aa9cSPeter Lieven     .bdrv_get_allocated_file_size   = nfs_get_allocated_file_size,
8976542aa9cSPeter Lieven     .bdrv_truncate                  = nfs_file_truncate,
8986542aa9cSPeter Lieven 
8996542aa9cSPeter Lieven     .bdrv_file_open                 = nfs_file_open,
9006542aa9cSPeter Lieven     .bdrv_close                     = nfs_file_close,
901c282e1fdSChunyan Liu     .bdrv_create                    = nfs_file_create,
90218a8056eSPeter Lieven     .bdrv_reopen_prepare            = nfs_reopen_prepare,
9036542aa9cSPeter Lieven 
90469785a22SPeter Lieven     .bdrv_co_preadv                 = nfs_co_preadv,
90569785a22SPeter Lieven     .bdrv_co_pwritev                = nfs_co_pwritev,
9066542aa9cSPeter Lieven     .bdrv_co_flush_to_disk          = nfs_co_flush,
907471799d1SStefan Hajnoczi 
908471799d1SStefan Hajnoczi     .bdrv_detach_aio_context        = nfs_detach_aio_context,
909471799d1SStefan Hajnoczi     .bdrv_attach_aio_context        = nfs_attach_aio_context,
91094d6a7a7SAshijeet Acharya     .bdrv_refresh_filename          = nfs_refresh_filename,
911d99b26c4SPeter Lieven 
912d99b26c4SPeter Lieven #ifdef LIBNFS_FEATURE_PAGECACHE
913d99b26c4SPeter Lieven     .bdrv_invalidate_cache          = nfs_invalidate_cache,
914d99b26c4SPeter Lieven #endif
9156542aa9cSPeter Lieven };
9166542aa9cSPeter Lieven 
9176542aa9cSPeter Lieven static void nfs_block_init(void)
9186542aa9cSPeter Lieven {
9196542aa9cSPeter Lieven     bdrv_register(&bdrv_nfs);
9206542aa9cSPeter Lieven }
9216542aa9cSPeter Lieven 
9226542aa9cSPeter Lieven block_init(nfs_block_init);
923