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