xref: /qemu/block/nfs.c (revision d2c6becb)
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 
276542aa9cSPeter Lieven #include <poll.h>
286542aa9cSPeter Lieven #include "qemu/config-file.h"
296542aa9cSPeter Lieven #include "qemu/error-report.h"
30d165b8cbSStefan Hajnoczi #include "qapi/error.h"
316542aa9cSPeter Lieven #include "block/block_int.h"
32609f45eaSMax Reitz #include "block/qdict.h"
336542aa9cSPeter Lieven #include "trace.h"
346542aa9cSPeter Lieven #include "qemu/iov.h"
35db725815SMarkus Armbruster #include "qemu/main-loop.h"
360b8fa32fSMarkus Armbruster #include "qemu/module.h"
37922a01a0SMarkus Armbruster #include "qemu/option.h"
386542aa9cSPeter Lieven #include "qemu/uri.h"
390d94b746SStefan Hajnoczi #include "qemu/cutils.h"
409af23989SMarkus Armbruster #include "qapi/qapi-visit-block-core.h"
4194d6a7a7SAshijeet Acharya #include "qapi/qmp/qdict.h"
4294d6a7a7SAshijeet Acharya #include "qapi/qmp/qstring.h"
4394d6a7a7SAshijeet Acharya #include "qapi/qobject-input-visitor.h"
4494d6a7a7SAshijeet Acharya #include "qapi/qobject-output-visitor.h"
456542aa9cSPeter Lieven #include <nfsc/libnfs.h>
466542aa9cSPeter Lieven 
4794d6a7a7SAshijeet Acharya 
4829c838cdSPeter Lieven #define QEMU_NFS_MAX_READAHEAD_SIZE 1048576
49d99b26c4SPeter Lieven #define QEMU_NFS_MAX_PAGECACHE_SIZE (8388608 / NFS_BLKSIZE)
507725b8bfSPeter Lieven #define QEMU_NFS_MAX_DEBUG_LEVEL 2
5129c838cdSPeter Lieven 
526542aa9cSPeter Lieven typedef struct NFSClient {
536542aa9cSPeter Lieven     struct nfs_context *context;
546542aa9cSPeter Lieven     struct nfsfh *fh;
556542aa9cSPeter Lieven     int events;
566542aa9cSPeter Lieven     bool has_zero_init;
57471799d1SStefan Hajnoczi     AioContext *aio_context;
5837d1e4d9SPaolo Bonzini     QemuMutex mutex;
5918a8056eSPeter Lieven     blkcnt_t st_blocks;
6038f8d5e0SPeter Lieven     bool cache_used;
6194d6a7a7SAshijeet Acharya     NFSServer *server;
6294d6a7a7SAshijeet Acharya     char *path;
6394d6a7a7SAshijeet Acharya     int64_t uid, gid, tcp_syncnt, readahead, pagecache, debug;
646542aa9cSPeter Lieven } NFSClient;
656542aa9cSPeter Lieven 
666542aa9cSPeter Lieven typedef struct NFSRPC {
67d746427aSPaolo Bonzini     BlockDriverState *bs;
686542aa9cSPeter Lieven     int ret;
696542aa9cSPeter Lieven     int complete;
706542aa9cSPeter Lieven     QEMUIOVector *iov;
716542aa9cSPeter Lieven     struct stat *st;
726542aa9cSPeter Lieven     Coroutine *co;
73471799d1SStefan Hajnoczi     NFSClient *client;
746542aa9cSPeter Lieven } NFSRPC;
756542aa9cSPeter Lieven 
7694d6a7a7SAshijeet Acharya static int nfs_parse_uri(const char *filename, QDict *options, Error **errp)
7794d6a7a7SAshijeet Acharya {
7894d6a7a7SAshijeet Acharya     URI *uri = NULL;
7994d6a7a7SAshijeet Acharya     QueryParams *qp = NULL;
8094d6a7a7SAshijeet Acharya     int ret = -EINVAL, i;
8194d6a7a7SAshijeet Acharya 
8294d6a7a7SAshijeet Acharya     uri = uri_parse(filename);
8394d6a7a7SAshijeet Acharya     if (!uri) {
8494d6a7a7SAshijeet Acharya         error_setg(errp, "Invalid URI specified");
8594d6a7a7SAshijeet Acharya         goto out;
8694d6a7a7SAshijeet Acharya     }
87f69165a8SMax Reitz     if (g_strcmp0(uri->scheme, "nfs") != 0) {
8894d6a7a7SAshijeet Acharya         error_setg(errp, "URI scheme must be 'nfs'");
8994d6a7a7SAshijeet Acharya         goto out;
9094d6a7a7SAshijeet Acharya     }
9194d6a7a7SAshijeet Acharya 
9294d6a7a7SAshijeet Acharya     if (!uri->server) {
9394d6a7a7SAshijeet Acharya         error_setg(errp, "missing hostname in URI");
9494d6a7a7SAshijeet Acharya         goto out;
9594d6a7a7SAshijeet Acharya     }
9694d6a7a7SAshijeet Acharya 
9794d6a7a7SAshijeet Acharya     if (!uri->path) {
9894d6a7a7SAshijeet Acharya         error_setg(errp, "missing file path in URI");
9994d6a7a7SAshijeet Acharya         goto out;
10094d6a7a7SAshijeet Acharya     }
10194d6a7a7SAshijeet Acharya 
10294d6a7a7SAshijeet Acharya     qp = query_params_parse(uri->query);
10394d6a7a7SAshijeet Acharya     if (!qp) {
10494d6a7a7SAshijeet Acharya         error_setg(errp, "could not parse query parameters");
10594d6a7a7SAshijeet Acharya         goto out;
10694d6a7a7SAshijeet Acharya     }
10794d6a7a7SAshijeet Acharya 
10846f5ac20SEric Blake     qdict_put_str(options, "server.host", uri->server);
10946f5ac20SEric Blake     qdict_put_str(options, "server.type", "inet");
11046f5ac20SEric Blake     qdict_put_str(options, "path", uri->path);
11194d6a7a7SAshijeet Acharya 
11294d6a7a7SAshijeet Acharya     for (i = 0; i < qp->n; i++) {
1138d20abe8SPeter Lieven         unsigned long long val;
11494d6a7a7SAshijeet Acharya         if (!qp->p[i].value) {
11594d6a7a7SAshijeet Acharya             error_setg(errp, "Value for NFS parameter expected: %s",
11694d6a7a7SAshijeet Acharya                        qp->p[i].name);
11794d6a7a7SAshijeet Acharya             goto out;
11894d6a7a7SAshijeet Acharya         }
1198d20abe8SPeter Lieven         if (parse_uint_full(qp->p[i].value, &val, 0)) {
12094d6a7a7SAshijeet Acharya             error_setg(errp, "Illegal value for NFS parameter: %s",
12194d6a7a7SAshijeet Acharya                        qp->p[i].name);
12294d6a7a7SAshijeet Acharya             goto out;
12394d6a7a7SAshijeet Acharya         }
12494d6a7a7SAshijeet Acharya         if (!strcmp(qp->p[i].name, "uid")) {
12546f5ac20SEric Blake             qdict_put_str(options, "user", qp->p[i].value);
12694d6a7a7SAshijeet Acharya         } else if (!strcmp(qp->p[i].name, "gid")) {
12746f5ac20SEric Blake             qdict_put_str(options, "group", qp->p[i].value);
12894d6a7a7SAshijeet Acharya         } else if (!strcmp(qp->p[i].name, "tcp-syncnt")) {
12946f5ac20SEric Blake             qdict_put_str(options, "tcp-syn-count", qp->p[i].value);
13094d6a7a7SAshijeet Acharya         } else if (!strcmp(qp->p[i].name, "readahead")) {
13146f5ac20SEric Blake             qdict_put_str(options, "readahead-size", qp->p[i].value);
13294d6a7a7SAshijeet Acharya         } else if (!strcmp(qp->p[i].name, "pagecache")) {
13346f5ac20SEric Blake             qdict_put_str(options, "page-cache-size", qp->p[i].value);
13494d6a7a7SAshijeet Acharya         } else if (!strcmp(qp->p[i].name, "debug")) {
13546f5ac20SEric Blake             qdict_put_str(options, "debug", qp->p[i].value);
13694d6a7a7SAshijeet Acharya         } else {
13794d6a7a7SAshijeet Acharya             error_setg(errp, "Unknown NFS parameter name: %s",
13894d6a7a7SAshijeet Acharya                        qp->p[i].name);
13994d6a7a7SAshijeet Acharya             goto out;
14094d6a7a7SAshijeet Acharya         }
14194d6a7a7SAshijeet Acharya     }
14294d6a7a7SAshijeet Acharya     ret = 0;
14394d6a7a7SAshijeet Acharya out:
14494d6a7a7SAshijeet Acharya     if (qp) {
14594d6a7a7SAshijeet Acharya         query_params_free(qp);
14694d6a7a7SAshijeet Acharya     }
14794d6a7a7SAshijeet Acharya     if (uri) {
14894d6a7a7SAshijeet Acharya         uri_free(uri);
14994d6a7a7SAshijeet Acharya     }
15094d6a7a7SAshijeet Acharya     return ret;
15194d6a7a7SAshijeet Acharya }
15294d6a7a7SAshijeet Acharya 
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 
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.  */
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),
196dca21ef2SFam Zheng                            false,
1976542aa9cSPeter Lieven                            (ev & POLLIN) ? nfs_process_read : NULL,
198f6a51c84SStefan Hajnoczi                            (ev & POLLOUT) ? nfs_process_write : NULL,
199f6a51c84SStefan Hajnoczi                            NULL, client);
2006542aa9cSPeter Lieven 
2016542aa9cSPeter Lieven     }
2026542aa9cSPeter Lieven     client->events = ev;
2036542aa9cSPeter Lieven }
2046542aa9cSPeter Lieven 
2056542aa9cSPeter Lieven static void nfs_process_read(void *arg)
2066542aa9cSPeter Lieven {
2076542aa9cSPeter Lieven     NFSClient *client = arg;
2089d456654SPaolo Bonzini 
20937d1e4d9SPaolo Bonzini     qemu_mutex_lock(&client->mutex);
2106542aa9cSPeter Lieven     nfs_service(client->context, POLLIN);
2116542aa9cSPeter Lieven     nfs_set_events(client);
21237d1e4d9SPaolo Bonzini     qemu_mutex_unlock(&client->mutex);
2136542aa9cSPeter Lieven }
2146542aa9cSPeter Lieven 
2156542aa9cSPeter Lieven static void nfs_process_write(void *arg)
2166542aa9cSPeter Lieven {
2176542aa9cSPeter Lieven     NFSClient *client = arg;
2189d456654SPaolo Bonzini 
21937d1e4d9SPaolo Bonzini     qemu_mutex_lock(&client->mutex);
2206542aa9cSPeter Lieven     nfs_service(client->context, POLLOUT);
2216542aa9cSPeter Lieven     nfs_set_events(client);
22237d1e4d9SPaolo Bonzini     qemu_mutex_unlock(&client->mutex);
2236542aa9cSPeter Lieven }
2246542aa9cSPeter Lieven 
225d746427aSPaolo Bonzini static void nfs_co_init_task(BlockDriverState *bs, NFSRPC *task)
2266542aa9cSPeter Lieven {
2276542aa9cSPeter Lieven     *task = (NFSRPC) {
2286542aa9cSPeter Lieven         .co             = qemu_coroutine_self(),
229d746427aSPaolo Bonzini         .bs             = bs,
230d746427aSPaolo Bonzini         .client         = bs->opaque,
2316542aa9cSPeter Lieven     };
2326542aa9cSPeter Lieven }
2336542aa9cSPeter Lieven 
2346542aa9cSPeter Lieven static void nfs_co_generic_bh_cb(void *opaque)
2356542aa9cSPeter Lieven {
2366542aa9cSPeter Lieven     NFSRPC *task = opaque;
2371919631eSPaolo Bonzini 
238a2c0fe2fSPeter Lieven     task->complete = 1;
2391919631eSPaolo Bonzini     aio_co_wake(task->co);
2406542aa9cSPeter Lieven }
2416542aa9cSPeter Lieven 
24237d1e4d9SPaolo Bonzini /* Called (via nfs_service) with QemuMutex held.  */
2436542aa9cSPeter Lieven static void
2446542aa9cSPeter Lieven nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
2456542aa9cSPeter Lieven                   void *private_data)
2466542aa9cSPeter Lieven {
2476542aa9cSPeter Lieven     NFSRPC *task = private_data;
2486542aa9cSPeter Lieven     task->ret = ret;
249d746427aSPaolo Bonzini     assert(!task->st);
2506542aa9cSPeter Lieven     if (task->ret > 0 && task->iov) {
2516542aa9cSPeter Lieven         if (task->ret <= task->iov->size) {
2526542aa9cSPeter Lieven             qemu_iovec_from_buf(task->iov, 0, data, task->ret);
2536542aa9cSPeter Lieven         } else {
2546542aa9cSPeter Lieven             task->ret = -EIO;
2556542aa9cSPeter Lieven         }
2566542aa9cSPeter Lieven     }
25720fccb18SPeter Lieven     if (task->ret < 0) {
25820fccb18SPeter Lieven         error_report("NFS Error: %s", nfs_get_error(nfs));
25920fccb18SPeter Lieven     }
260fffb6e12SPaolo Bonzini     aio_bh_schedule_oneshot(task->client->aio_context,
261471799d1SStefan Hajnoczi                             nfs_co_generic_bh_cb, task);
2626542aa9cSPeter Lieven }
2636542aa9cSPeter Lieven 
26469785a22SPeter Lieven static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, uint64_t offset,
26569785a22SPeter Lieven                                       uint64_t bytes, QEMUIOVector *iov,
26669785a22SPeter Lieven                                       int flags)
2676542aa9cSPeter Lieven {
2686542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
2696542aa9cSPeter Lieven     NFSRPC task;
2706542aa9cSPeter Lieven 
271d746427aSPaolo Bonzini     nfs_co_init_task(bs, &task);
2726542aa9cSPeter Lieven     task.iov = iov;
2736542aa9cSPeter Lieven 
27437d1e4d9SPaolo Bonzini     qemu_mutex_lock(&client->mutex);
2756542aa9cSPeter Lieven     if (nfs_pread_async(client->context, client->fh,
27669785a22SPeter Lieven                         offset, bytes, nfs_co_generic_cb, &task) != 0) {
27737d1e4d9SPaolo Bonzini         qemu_mutex_unlock(&client->mutex);
2786542aa9cSPeter Lieven         return -ENOMEM;
2796542aa9cSPeter Lieven     }
2806542aa9cSPeter Lieven 
2816542aa9cSPeter Lieven     nfs_set_events(client);
28237d1e4d9SPaolo Bonzini     qemu_mutex_unlock(&client->mutex);
283aa92d6c4SPaolo Bonzini     while (!task.complete) {
2846542aa9cSPeter Lieven         qemu_coroutine_yield();
2856542aa9cSPeter Lieven     }
2866542aa9cSPeter Lieven 
2876542aa9cSPeter Lieven     if (task.ret < 0) {
2886542aa9cSPeter Lieven         return task.ret;
2896542aa9cSPeter Lieven     }
2906542aa9cSPeter Lieven 
2916542aa9cSPeter Lieven     /* zero pad short reads */
2926542aa9cSPeter Lieven     if (task.ret < iov->size) {
2936542aa9cSPeter Lieven         qemu_iovec_memset(iov, task.ret, 0, iov->size - task.ret);
2946542aa9cSPeter Lieven     }
2956542aa9cSPeter Lieven 
2966542aa9cSPeter Lieven     return 0;
2976542aa9cSPeter Lieven }
2986542aa9cSPeter Lieven 
29969785a22SPeter Lieven static int coroutine_fn nfs_co_pwritev(BlockDriverState *bs, uint64_t offset,
30069785a22SPeter Lieven                                        uint64_t bytes, QEMUIOVector *iov,
30169785a22SPeter Lieven                                        int flags)
3026542aa9cSPeter Lieven {
3036542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
3046542aa9cSPeter Lieven     NFSRPC task;
3056542aa9cSPeter Lieven     char *buf = NULL;
306ef503a84SPeter Lieven     bool my_buffer = false;
3076542aa9cSPeter Lieven 
308d746427aSPaolo Bonzini     nfs_co_init_task(bs, &task);
3096542aa9cSPeter Lieven 
310ef503a84SPeter Lieven     if (iov->niov != 1) {
31169785a22SPeter Lieven         buf = g_try_malloc(bytes);
31269785a22SPeter Lieven         if (bytes && buf == NULL) {
3132347dd7bSKevin Wolf             return -ENOMEM;
3142347dd7bSKevin Wolf         }
31569785a22SPeter Lieven         qemu_iovec_to_buf(iov, 0, buf, bytes);
316ef503a84SPeter Lieven         my_buffer = true;
317ef503a84SPeter Lieven     } else {
318ef503a84SPeter Lieven         buf = iov->iov[0].iov_base;
319ef503a84SPeter Lieven     }
3206542aa9cSPeter Lieven 
32137d1e4d9SPaolo Bonzini     qemu_mutex_lock(&client->mutex);
3226542aa9cSPeter Lieven     if (nfs_pwrite_async(client->context, client->fh,
32369785a22SPeter Lieven                          offset, bytes, buf,
32469785a22SPeter Lieven                          nfs_co_generic_cb, &task) != 0) {
32537d1e4d9SPaolo Bonzini         qemu_mutex_unlock(&client->mutex);
326ef503a84SPeter Lieven         if (my_buffer) {
3276542aa9cSPeter Lieven             g_free(buf);
328ef503a84SPeter Lieven         }
3296542aa9cSPeter Lieven         return -ENOMEM;
3306542aa9cSPeter Lieven     }
3316542aa9cSPeter Lieven 
3326542aa9cSPeter Lieven     nfs_set_events(client);
33337d1e4d9SPaolo Bonzini     qemu_mutex_unlock(&client->mutex);
334aa92d6c4SPaolo Bonzini     while (!task.complete) {
3356542aa9cSPeter Lieven         qemu_coroutine_yield();
3366542aa9cSPeter Lieven     }
3376542aa9cSPeter Lieven 
338ef503a84SPeter Lieven     if (my_buffer) {
3396542aa9cSPeter Lieven         g_free(buf);
340ef503a84SPeter Lieven     }
3416542aa9cSPeter Lieven 
34269785a22SPeter Lieven     if (task.ret != bytes) {
3436542aa9cSPeter Lieven         return task.ret < 0 ? task.ret : -EIO;
3446542aa9cSPeter Lieven     }
3456542aa9cSPeter Lieven 
3466542aa9cSPeter Lieven     return 0;
3476542aa9cSPeter Lieven }
3486542aa9cSPeter Lieven 
3496542aa9cSPeter Lieven static int coroutine_fn nfs_co_flush(BlockDriverState *bs)
3506542aa9cSPeter Lieven {
3516542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
3526542aa9cSPeter Lieven     NFSRPC task;
3536542aa9cSPeter Lieven 
354d746427aSPaolo Bonzini     nfs_co_init_task(bs, &task);
3556542aa9cSPeter Lieven 
35637d1e4d9SPaolo Bonzini     qemu_mutex_lock(&client->mutex);
3576542aa9cSPeter Lieven     if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb,
3586542aa9cSPeter Lieven                         &task) != 0) {
35937d1e4d9SPaolo Bonzini         qemu_mutex_unlock(&client->mutex);
3606542aa9cSPeter Lieven         return -ENOMEM;
3616542aa9cSPeter Lieven     }
3626542aa9cSPeter Lieven 
3636542aa9cSPeter Lieven     nfs_set_events(client);
36437d1e4d9SPaolo Bonzini     qemu_mutex_unlock(&client->mutex);
365aa92d6c4SPaolo Bonzini     while (!task.complete) {
3666542aa9cSPeter Lieven         qemu_coroutine_yield();
3676542aa9cSPeter Lieven     }
3686542aa9cSPeter Lieven 
3696542aa9cSPeter Lieven     return task.ret;
3706542aa9cSPeter Lieven }
3716542aa9cSPeter Lieven 
372471799d1SStefan Hajnoczi static void nfs_detach_aio_context(BlockDriverState *bs)
373471799d1SStefan Hajnoczi {
374471799d1SStefan Hajnoczi     NFSClient *client = bs->opaque;
375471799d1SStefan Hajnoczi 
376dca21ef2SFam Zheng     aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
377f6a51c84SStefan Hajnoczi                        false, NULL, NULL, NULL, NULL);
378471799d1SStefan Hajnoczi     client->events = 0;
379471799d1SStefan Hajnoczi }
380471799d1SStefan Hajnoczi 
381471799d1SStefan Hajnoczi static void nfs_attach_aio_context(BlockDriverState *bs,
382471799d1SStefan Hajnoczi                                    AioContext *new_context)
383471799d1SStefan Hajnoczi {
384471799d1SStefan Hajnoczi     NFSClient *client = bs->opaque;
385471799d1SStefan Hajnoczi 
386471799d1SStefan Hajnoczi     client->aio_context = new_context;
387471799d1SStefan Hajnoczi     nfs_set_events(client);
388471799d1SStefan Hajnoczi }
389471799d1SStefan Hajnoczi 
3906542aa9cSPeter Lieven static void nfs_client_close(NFSClient *client)
3916542aa9cSPeter Lieven {
3926542aa9cSPeter Lieven     if (client->context) {
393601dc655SPeter Lieven         qemu_mutex_lock(&client->mutex);
394601dc655SPeter Lieven         aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
395601dc655SPeter Lieven                            false, NULL, NULL, NULL, NULL);
396601dc655SPeter Lieven         qemu_mutex_unlock(&client->mutex);
3976542aa9cSPeter Lieven         if (client->fh) {
3986542aa9cSPeter Lieven             nfs_close(client->context, client->fh);
399113fe792SJeff Cody             client->fh = NULL;
4006542aa9cSPeter Lieven         }
401*d2c6becbSPeter Lieven #ifdef LIBNFS_FEATURE_UMOUNT
402*d2c6becbSPeter Lieven         nfs_umount(client->context);
403*d2c6becbSPeter Lieven #endif
4046542aa9cSPeter Lieven         nfs_destroy_context(client->context);
405113fe792SJeff Cody         client->context = NULL;
4066542aa9cSPeter Lieven     }
407113fe792SJeff Cody     g_free(client->path);
408113fe792SJeff Cody     qemu_mutex_destroy(&client->mutex);
409113fe792SJeff Cody     qapi_free_NFSServer(client->server);
410113fe792SJeff Cody     client->server = NULL;
4116542aa9cSPeter Lieven }
4126542aa9cSPeter Lieven 
4136542aa9cSPeter Lieven static void nfs_file_close(BlockDriverState *bs)
4146542aa9cSPeter Lieven {
4156542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
4166542aa9cSPeter Lieven     nfs_client_close(client);
4176542aa9cSPeter Lieven }
4186542aa9cSPeter Lieven 
419c22a0345SKevin Wolf static int64_t nfs_client_open(NFSClient *client, BlockdevOptionsNfs *opts,
420cb8d4bf6SFam Zheng                                int flags, int open_flags, Error **errp)
4216542aa9cSPeter Lieven {
422f1a7ff77SPeter Lieven     int64_t ret = -EINVAL;
4236542aa9cSPeter Lieven     struct stat st;
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);
54918a8056eSPeter Lieven     client->st_blocks = st.st_blocks;
5506542aa9cSPeter Lieven     client->has_zero_init = S_ISREG(st.st_mode);
55194d6a7a7SAshijeet Acharya     *strp = '/';
5526542aa9cSPeter Lieven     goto out;
55394d6a7a7SAshijeet Acharya 
5546542aa9cSPeter Lieven fail:
5556542aa9cSPeter Lieven     nfs_client_close(client);
5566542aa9cSPeter Lieven out:
5576542aa9cSPeter Lieven     g_free(file);
5586542aa9cSPeter Lieven     return ret;
5596542aa9cSPeter Lieven }
5606542aa9cSPeter Lieven 
561a1a42af4SKevin Wolf static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
562a1a42af4SKevin Wolf                                                      Error **errp)
563c22a0345SKevin Wolf {
564c22a0345SKevin Wolf     BlockdevOptionsNfs *opts = NULL;
565c22a0345SKevin Wolf     Visitor *v;
566c82be42cSKevin Wolf     const QDictEntry *e;
567c22a0345SKevin Wolf     Error *local_err = NULL;
568c22a0345SKevin Wolf 
569af91062eSMarkus Armbruster     v = qobject_input_visitor_new_flat_confused(options, errp);
570af91062eSMarkus Armbruster     if (!v) {
571a1a42af4SKevin Wolf         return NULL;
572c22a0345SKevin Wolf     }
573c22a0345SKevin Wolf 
574c22a0345SKevin Wolf     visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err);
575c22a0345SKevin Wolf     visit_free(v);
576c22a0345SKevin Wolf 
577c22a0345SKevin Wolf     if (local_err) {
57854b7af43SKevin Wolf         error_propagate(errp, local_err);
579a1a42af4SKevin Wolf         return NULL;
580a1a42af4SKevin Wolf     }
581a1a42af4SKevin Wolf 
582c82be42cSKevin Wolf     /* Remove the processed options from the QDict (the visitor processes
583c82be42cSKevin Wolf      * _all_ options in the QDict) */
584c82be42cSKevin Wolf     while ((e = qdict_first(options))) {
585c82be42cSKevin Wolf         qdict_del(options, e->key);
586c82be42cSKevin Wolf     }
587c82be42cSKevin Wolf 
588a1a42af4SKevin Wolf     return opts;
589a1a42af4SKevin Wolf }
590a1a42af4SKevin Wolf 
591a1a42af4SKevin Wolf static int64_t nfs_client_open_qdict(NFSClient *client, QDict *options,
592a1a42af4SKevin Wolf                                      int flags, int open_flags, Error **errp)
593a1a42af4SKevin Wolf {
594a1a42af4SKevin Wolf     BlockdevOptionsNfs *opts;
595a1a42af4SKevin Wolf     int ret;
596a1a42af4SKevin Wolf 
597a1a42af4SKevin Wolf     opts = nfs_options_qdict_to_qapi(options, errp);
598a1a42af4SKevin Wolf     if (opts == NULL) {
599c22a0345SKevin Wolf         ret = -EINVAL;
600c22a0345SKevin Wolf         goto fail;
601c22a0345SKevin Wolf     }
602c22a0345SKevin Wolf 
603c22a0345SKevin Wolf     ret = nfs_client_open(client, opts, flags, open_flags, errp);
604c22a0345SKevin Wolf fail:
605c22a0345SKevin Wolf     qapi_free_BlockdevOptionsNfs(opts);
606c22a0345SKevin Wolf     return ret;
607c22a0345SKevin Wolf }
608c22a0345SKevin Wolf 
6096542aa9cSPeter Lieven static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
6106542aa9cSPeter Lieven                          Error **errp) {
6116542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
6126542aa9cSPeter Lieven     int64_t ret;
6136542aa9cSPeter Lieven 
614471799d1SStefan Hajnoczi     client->aio_context = bdrv_get_aio_context(bs);
615471799d1SStefan Hajnoczi 
616c22a0345SKevin Wolf     ret = nfs_client_open_qdict(client, options,
6176542aa9cSPeter Lieven                                 (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
618cb8d4bf6SFam Zheng                                 bs->open_flags, errp);
6196542aa9cSPeter Lieven     if (ret < 0) {
62094d6a7a7SAshijeet Acharya         return ret;
6216542aa9cSPeter Lieven     }
622113fe792SJeff Cody 
6236542aa9cSPeter Lieven     bs->total_sectors = ret;
624810f4f86SFam Zheng     ret = 0;
625810f4f86SFam Zheng     return ret;
6266542aa9cSPeter Lieven }
6276542aa9cSPeter Lieven 
628fd752801SMax Reitz static QemuOptsList nfs_create_opts = {
629fd752801SMax Reitz     .name = "nfs-create-opts",
630fd752801SMax Reitz     .head = QTAILQ_HEAD_INITIALIZER(nfs_create_opts.head),
631fd752801SMax Reitz     .desc = {
632fd752801SMax Reitz         {
633fd752801SMax Reitz             .name = BLOCK_OPT_SIZE,
634fd752801SMax Reitz             .type = QEMU_OPT_SIZE,
635fd752801SMax Reitz             .help = "Virtual disk size"
636fd752801SMax Reitz         },
637fd752801SMax Reitz         { /* end of list */ }
638fd752801SMax Reitz     }
639fd752801SMax Reitz };
640fd752801SMax Reitz 
641a1a42af4SKevin Wolf static int nfs_file_co_create(BlockdevCreateOptions *options, Error **errp)
6426542aa9cSPeter Lieven {
643a1a42af4SKevin Wolf     BlockdevCreateOptionsNfs *opts = &options->u.nfs;
6445839e53bSMarkus Armbruster     NFSClient *client = g_new0(NFSClient, 1);
645a1a42af4SKevin Wolf     int ret;
646a1a42af4SKevin Wolf 
647a1a42af4SKevin Wolf     assert(options->driver == BLOCKDEV_DRIVER_NFS);
6486542aa9cSPeter Lieven 
649471799d1SStefan Hajnoczi     client->aio_context = qemu_get_aio_context();
650471799d1SStefan Hajnoczi 
651a1a42af4SKevin Wolf     ret = nfs_client_open(client, opts->location, O_CREAT, 0, errp);
652a1a42af4SKevin Wolf     if (ret < 0) {
653a1a42af4SKevin Wolf         goto out;
654a1a42af4SKevin Wolf     }
655a1a42af4SKevin Wolf     ret = nfs_ftruncate(client->context, client->fh, opts->size);
656a1a42af4SKevin Wolf     nfs_client_close(client);
657a1a42af4SKevin Wolf 
658a1a42af4SKevin Wolf out:
659a1a42af4SKevin Wolf     g_free(client);
660a1a42af4SKevin Wolf     return ret;
661a1a42af4SKevin Wolf }
662a1a42af4SKevin Wolf 
663a1a42af4SKevin Wolf static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
664a1a42af4SKevin Wolf                                                 Error **errp)
665a1a42af4SKevin Wolf {
666a1a42af4SKevin Wolf     BlockdevCreateOptions *create_options;
667a1a42af4SKevin Wolf     BlockdevCreateOptionsNfs *nfs_opts;
668a1a42af4SKevin Wolf     QDict *options;
669a1a42af4SKevin Wolf     int ret;
670a1a42af4SKevin Wolf 
671a1a42af4SKevin Wolf     create_options = g_new0(BlockdevCreateOptions, 1);
672a1a42af4SKevin Wolf     create_options->driver = BLOCKDEV_DRIVER_NFS;
673a1a42af4SKevin Wolf     nfs_opts = &create_options->u.nfs;
674a1a42af4SKevin Wolf 
6756542aa9cSPeter Lieven     /* Read out options */
676a1a42af4SKevin Wolf     nfs_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
677c2eb918eSHu Tao                               BDRV_SECTOR_SIZE);
6786542aa9cSPeter Lieven 
67994d6a7a7SAshijeet Acharya     options = qdict_new();
68094d6a7a7SAshijeet Acharya     ret = nfs_parse_uri(url, options, errp);
68194d6a7a7SAshijeet Acharya     if (ret < 0) {
68294d6a7a7SAshijeet Acharya         goto out;
68394d6a7a7SAshijeet Acharya     }
68494d6a7a7SAshijeet Acharya 
685a1a42af4SKevin Wolf     nfs_opts->location = nfs_options_qdict_to_qapi(options, errp);
686a1a42af4SKevin Wolf     if (nfs_opts->location == NULL) {
687a1a42af4SKevin Wolf         ret = -EINVAL;
688a1a42af4SKevin Wolf         goto out;
689a1a42af4SKevin Wolf     }
690a1a42af4SKevin Wolf 
691a1a42af4SKevin Wolf     ret = nfs_file_co_create(create_options, errp);
6926542aa9cSPeter Lieven     if (ret < 0) {
6936542aa9cSPeter Lieven         goto out;
6946542aa9cSPeter Lieven     }
695a1a42af4SKevin Wolf 
696a1a42af4SKevin Wolf     ret = 0;
6976542aa9cSPeter Lieven out:
698cb3e7f08SMarc-André Lureau     qobject_unref(options);
699a1a42af4SKevin Wolf     qapi_free_BlockdevCreateOptions(create_options);
7006542aa9cSPeter Lieven     return ret;
7016542aa9cSPeter Lieven }
7026542aa9cSPeter Lieven 
7036542aa9cSPeter Lieven static int nfs_has_zero_init(BlockDriverState *bs)
7046542aa9cSPeter Lieven {
7056542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
7066542aa9cSPeter Lieven     return client->has_zero_init;
7076542aa9cSPeter Lieven }
7086542aa9cSPeter Lieven 
70937d1e4d9SPaolo Bonzini /* Called (via nfs_service) with QemuMutex held.  */
710d746427aSPaolo Bonzini static void
711d746427aSPaolo Bonzini nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data,
712d746427aSPaolo Bonzini                                void *private_data)
713d746427aSPaolo Bonzini {
714d746427aSPaolo Bonzini     NFSRPC *task = private_data;
715d746427aSPaolo Bonzini     task->ret = ret;
716d746427aSPaolo Bonzini     if (task->ret == 0) {
717d746427aSPaolo Bonzini         memcpy(task->st, data, sizeof(struct stat));
718d746427aSPaolo Bonzini     }
719d746427aSPaolo Bonzini     if (task->ret < 0) {
720d746427aSPaolo Bonzini         error_report("NFS Error: %s", nfs_get_error(nfs));
721d746427aSPaolo Bonzini     }
722e2a6ae7fSPaolo Bonzini 
723e2a6ae7fSPaolo Bonzini     /* Set task->complete before reading bs->wakeup.  */
724e2a6ae7fSPaolo Bonzini     atomic_mb_set(&task->complete, 1);
725c9d1a561SPaolo Bonzini     bdrv_wakeup(task->bs);
726d746427aSPaolo Bonzini }
727d746427aSPaolo Bonzini 
7286542aa9cSPeter Lieven static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
7296542aa9cSPeter Lieven {
7306542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
7316542aa9cSPeter Lieven     NFSRPC task = {0};
7326542aa9cSPeter Lieven     struct stat st;
7336542aa9cSPeter Lieven 
73418a8056eSPeter Lieven     if (bdrv_is_read_only(bs) &&
73518a8056eSPeter Lieven         !(bs->open_flags & BDRV_O_NOCACHE)) {
73618a8056eSPeter Lieven         return client->st_blocks * 512;
73718a8056eSPeter Lieven     }
73818a8056eSPeter Lieven 
739d746427aSPaolo Bonzini     task.bs = bs;
7406542aa9cSPeter Lieven     task.st = &st;
741d746427aSPaolo Bonzini     if (nfs_fstat_async(client->context, client->fh, nfs_get_allocated_file_size_cb,
7426542aa9cSPeter Lieven                         &task) != 0) {
7436542aa9cSPeter Lieven         return -ENOMEM;
7446542aa9cSPeter Lieven     }
7456542aa9cSPeter Lieven 
7466542aa9cSPeter Lieven     nfs_set_events(client);
747d746427aSPaolo Bonzini     BDRV_POLL_WHILE(bs, !task.complete);
7486542aa9cSPeter Lieven 
749055c6f91SPeter Lieven     return (task.ret < 0 ? task.ret : st.st_blocks * 512);
7506542aa9cSPeter Lieven }
7516542aa9cSPeter Lieven 
752061ca8a3SKevin Wolf static int coroutine_fn
753061ca8a3SKevin Wolf nfs_file_co_truncate(BlockDriverState *bs, int64_t offset,
7548243ccb7SMax Reitz                      PreallocMode prealloc, Error **errp)
7556542aa9cSPeter Lieven {
7566542aa9cSPeter Lieven     NFSClient *client = bs->opaque;
757f59adb32SMax Reitz     int ret;
758f59adb32SMax Reitz 
7598243ccb7SMax Reitz     if (prealloc != PREALLOC_MODE_OFF) {
7608243ccb7SMax Reitz         error_setg(errp, "Unsupported preallocation mode '%s'",
761977c736fSMarkus Armbruster                    PreallocMode_str(prealloc));
7628243ccb7SMax Reitz         return -ENOTSUP;
7638243ccb7SMax Reitz     }
7648243ccb7SMax Reitz 
765f59adb32SMax Reitz     ret = nfs_ftruncate(client->context, client->fh, offset);
766f59adb32SMax Reitz     if (ret < 0) {
767f59adb32SMax Reitz         error_setg_errno(errp, -ret, "Failed to truncate file");
768f59adb32SMax Reitz         return ret;
769f59adb32SMax Reitz     }
770f59adb32SMax Reitz 
771f59adb32SMax Reitz     return 0;
7726542aa9cSPeter Lieven }
7736542aa9cSPeter Lieven 
77418a8056eSPeter Lieven /* Note that this will not re-establish a connection with the NFS server
77518a8056eSPeter Lieven  * - it is effectively a NOP.  */
77618a8056eSPeter Lieven static int nfs_reopen_prepare(BDRVReopenState *state,
77718a8056eSPeter Lieven                               BlockReopenQueue *queue, Error **errp)
77818a8056eSPeter Lieven {
77918a8056eSPeter Lieven     NFSClient *client = state->bs->opaque;
78018a8056eSPeter Lieven     struct stat st;
78118a8056eSPeter Lieven     int ret = 0;
78218a8056eSPeter Lieven 
78318a8056eSPeter Lieven     if (state->flags & BDRV_O_RDWR && bdrv_is_read_only(state->bs)) {
78418a8056eSPeter Lieven         error_setg(errp, "Cannot open a read-only mount as read-write");
78518a8056eSPeter Lieven         return -EACCES;
78618a8056eSPeter Lieven     }
78718a8056eSPeter Lieven 
78838f8d5e0SPeter Lieven     if ((state->flags & BDRV_O_NOCACHE) && client->cache_used) {
789d99b26c4SPeter Lieven         error_setg(errp, "Cannot disable cache if libnfs readahead or"
790d99b26c4SPeter Lieven                          " pagecache is enabled");
79138f8d5e0SPeter Lieven         return -EINVAL;
79238f8d5e0SPeter Lieven     }
79338f8d5e0SPeter Lieven 
79418a8056eSPeter Lieven     /* Update cache for read-only reopens */
79518a8056eSPeter Lieven     if (!(state->flags & BDRV_O_RDWR)) {
79618a8056eSPeter Lieven         ret = nfs_fstat(client->context, client->fh, &st);
79718a8056eSPeter Lieven         if (ret < 0) {
79818a8056eSPeter Lieven             error_setg(errp, "Failed to fstat file: %s",
79918a8056eSPeter Lieven                        nfs_get_error(client->context));
80018a8056eSPeter Lieven             return ret;
80118a8056eSPeter Lieven         }
80218a8056eSPeter Lieven         client->st_blocks = st.st_blocks;
80318a8056eSPeter Lieven     }
80418a8056eSPeter Lieven 
80518a8056eSPeter Lieven     return 0;
80618a8056eSPeter Lieven }
80718a8056eSPeter Lieven 
808998b3a1eSMax Reitz static void nfs_refresh_filename(BlockDriverState *bs)
80994d6a7a7SAshijeet Acharya {
81094d6a7a7SAshijeet Acharya     NFSClient *client = bs->opaque;
81194d6a7a7SAshijeet Acharya 
81294d6a7a7SAshijeet Acharya     if (client->uid && !client->gid) {
81394d6a7a7SAshijeet Acharya         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
81494d6a7a7SAshijeet Acharya                  "nfs://%s%s?uid=%" PRId64, client->server->host, client->path,
81594d6a7a7SAshijeet Acharya                  client->uid);
81694d6a7a7SAshijeet Acharya     } else if (!client->uid && client->gid) {
81794d6a7a7SAshijeet Acharya         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
81894d6a7a7SAshijeet Acharya                  "nfs://%s%s?gid=%" PRId64, client->server->host, client->path,
81994d6a7a7SAshijeet Acharya                  client->gid);
82094d6a7a7SAshijeet Acharya     } else if (client->uid && client->gid) {
82194d6a7a7SAshijeet Acharya         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
82294d6a7a7SAshijeet Acharya                  "nfs://%s%s?uid=%" PRId64 "&gid=%" PRId64,
82394d6a7a7SAshijeet Acharya                  client->server->host, client->path, client->uid, client->gid);
82494d6a7a7SAshijeet Acharya     } else {
82594d6a7a7SAshijeet Acharya         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
82694d6a7a7SAshijeet Acharya                  "nfs://%s%s", client->server->host, client->path);
82794d6a7a7SAshijeet Acharya     }
82894d6a7a7SAshijeet Acharya }
82994d6a7a7SAshijeet Acharya 
8300dcbc54aSMax Reitz static char *nfs_dirname(BlockDriverState *bs, Error **errp)
8310dcbc54aSMax Reitz {
8320dcbc54aSMax Reitz     NFSClient *client = bs->opaque;
8330dcbc54aSMax Reitz 
8340dcbc54aSMax Reitz     if (client->uid || client->gid) {
8350dcbc54aSMax Reitz         bdrv_refresh_filename(bs);
8360dcbc54aSMax Reitz         error_setg(errp, "Cannot generate a base directory for NFS node '%s'",
8370dcbc54aSMax Reitz                    bs->filename);
8380dcbc54aSMax Reitz         return NULL;
8390dcbc54aSMax Reitz     }
8400dcbc54aSMax Reitz 
8410dcbc54aSMax Reitz     return g_strdup_printf("nfs://%s%s/", client->server->host, client->path);
8420dcbc54aSMax Reitz }
8430dcbc54aSMax Reitz 
844d99b26c4SPeter Lieven #ifdef LIBNFS_FEATURE_PAGECACHE
8452b148f39SPaolo Bonzini static void coroutine_fn nfs_co_invalidate_cache(BlockDriverState *bs,
846d99b26c4SPeter Lieven                                                  Error **errp)
847d99b26c4SPeter Lieven {
848d99b26c4SPeter Lieven     NFSClient *client = bs->opaque;
849d99b26c4SPeter Lieven     nfs_pagecache_invalidate(client->context, client->fh);
850d99b26c4SPeter Lieven }
851d99b26c4SPeter Lieven #endif
852d99b26c4SPeter Lieven 
8532654267cSMax Reitz static const char *nfs_strong_runtime_opts[] = {
8542654267cSMax Reitz     "path",
8552654267cSMax Reitz     "user",
8562654267cSMax Reitz     "group",
8572654267cSMax Reitz     "server.",
8582654267cSMax Reitz 
8592654267cSMax Reitz     NULL
8602654267cSMax Reitz };
8612654267cSMax Reitz 
8626542aa9cSPeter Lieven static BlockDriver bdrv_nfs = {
8636542aa9cSPeter Lieven     .format_name                    = "nfs",
8646542aa9cSPeter Lieven     .protocol_name                  = "nfs",
8656542aa9cSPeter Lieven 
8666542aa9cSPeter Lieven     .instance_size                  = sizeof(NFSClient),
86794d6a7a7SAshijeet Acharya     .bdrv_parse_filename            = nfs_parse_filename,
868fd752801SMax Reitz     .create_opts                    = &nfs_create_opts,
869fd752801SMax Reitz 
8706542aa9cSPeter Lieven     .bdrv_has_zero_init             = nfs_has_zero_init,
8711dcaf527SMax Reitz     .bdrv_has_zero_init_truncate    = nfs_has_zero_init,
8726542aa9cSPeter Lieven     .bdrv_get_allocated_file_size   = nfs_get_allocated_file_size,
873061ca8a3SKevin Wolf     .bdrv_co_truncate               = nfs_file_co_truncate,
8746542aa9cSPeter Lieven 
8756542aa9cSPeter Lieven     .bdrv_file_open                 = nfs_file_open,
8766542aa9cSPeter Lieven     .bdrv_close                     = nfs_file_close,
877a1a42af4SKevin Wolf     .bdrv_co_create                 = nfs_file_co_create,
878efc75e2aSStefan Hajnoczi     .bdrv_co_create_opts            = nfs_file_co_create_opts,
87918a8056eSPeter Lieven     .bdrv_reopen_prepare            = nfs_reopen_prepare,
8806542aa9cSPeter Lieven 
88169785a22SPeter Lieven     .bdrv_co_preadv                 = nfs_co_preadv,
88269785a22SPeter Lieven     .bdrv_co_pwritev                = nfs_co_pwritev,
8836542aa9cSPeter Lieven     .bdrv_co_flush_to_disk          = nfs_co_flush,
884471799d1SStefan Hajnoczi 
885471799d1SStefan Hajnoczi     .bdrv_detach_aio_context        = nfs_detach_aio_context,
886471799d1SStefan Hajnoczi     .bdrv_attach_aio_context        = nfs_attach_aio_context,
88794d6a7a7SAshijeet Acharya     .bdrv_refresh_filename          = nfs_refresh_filename,
8880dcbc54aSMax Reitz     .bdrv_dirname                   = nfs_dirname,
889d99b26c4SPeter Lieven 
8902654267cSMax Reitz     .strong_runtime_opts            = nfs_strong_runtime_opts,
8912654267cSMax Reitz 
892d99b26c4SPeter Lieven #ifdef LIBNFS_FEATURE_PAGECACHE
8932b148f39SPaolo Bonzini     .bdrv_co_invalidate_cache       = nfs_co_invalidate_cache,
894d99b26c4SPeter Lieven #endif
8956542aa9cSPeter Lieven };
8966542aa9cSPeter Lieven 
8976542aa9cSPeter Lieven static void nfs_block_init(void)
8986542aa9cSPeter Lieven {
8996542aa9cSPeter Lieven     bdrv_register(&bdrv_nfs);
9006542aa9cSPeter Lieven }
9016542aa9cSPeter Lieven 
9026542aa9cSPeter Lieven block_init(nfs_block_init);
903