1 /*
2 * QEMU Block driver for native access to files on NFS shares
3 *
4 * Copyright (c) 2014-2017 Peter Lieven <pl@kamp.de>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25 #include "qemu/osdep.h"
26
27 #if !defined(_WIN32)
28 #include <poll.h>
29 #endif
30 #include "qemu/config-file.h"
31 #include "qemu/error-report.h"
32 #include "qapi/error.h"
33 #include "block/block-io.h"
34 #include "block/block_int.h"
35 #include "block/qdict.h"
36 #include "trace.h"
37 #include "qemu/iov.h"
38 #include "qemu/main-loop.h"
39 #include "qemu/module.h"
40 #include "qemu/option.h"
41 #include "qemu/cutils.h"
42 #include "system/replay.h"
43 #include "qapi/qapi-visit-block-core.h"
44 #include "qobject/qdict.h"
45 #include "qobject/qstring.h"
46 #include "qapi/qobject-input-visitor.h"
47 #include "qapi/qobject-output-visitor.h"
48 #include <nfsc/libnfs.h>
49
50
51 #define QEMU_NFS_MAX_READAHEAD_SIZE 1048576
52 #define QEMU_NFS_MAX_PAGECACHE_SIZE (8388608 / NFS_BLKSIZE)
53 #define QEMU_NFS_MAX_DEBUG_LEVEL 2
54
55 typedef struct NFSClient {
56 struct nfs_context *context;
57 struct nfsfh *fh;
58 int events;
59 bool has_zero_init;
60 AioContext *aio_context;
61 QemuMutex mutex;
62 uint64_t st_blocks;
63 bool cache_used;
64 NFSServer *server;
65 char *path;
66 int64_t uid, gid, tcp_syncnt, readahead, pagecache, debug;
67 } NFSClient;
68
69 typedef struct NFSRPC {
70 BlockDriverState *bs;
71 int ret;
72 QEMUIOVector *iov;
73 struct stat *st;
74 Coroutine *co;
75 NFSClient *client;
76 } NFSRPC;
77
nfs_parse_uri(const char * filename,QDict * options,Error ** errp)78 static int nfs_parse_uri(const char *filename, QDict *options, Error **errp)
79 {
80 g_autoptr(GUri) uri = g_uri_parse(filename, G_URI_FLAGS_NONE, NULL);
81 GUriParamsIter qp;
82 const char *uri_server, *uri_path, *uri_query;
83 char *qp_name, *qp_value;
84 GError *gerror = NULL;
85
86 if (!uri) {
87 error_setg(errp, "Invalid URI specified");
88 return -EINVAL;
89 }
90 if (!g_str_equal(g_uri_get_scheme(uri), "nfs")) {
91 error_setg(errp, "URI scheme must be 'nfs'");
92 return -EINVAL;
93 }
94
95 uri_server = g_uri_get_host(uri);
96 if (!uri_server || !uri_server[0]) {
97 error_setg(errp, "missing hostname in URI");
98 return -EINVAL;
99 }
100
101 uri_path = g_uri_get_path(uri);
102 if (!uri_path || !uri_path[0]) {
103 error_setg(errp, "missing file path in URI");
104 return -EINVAL;
105 }
106
107 qdict_put_str(options, "server.host", uri_server);
108 qdict_put_str(options, "server.type", "inet");
109 qdict_put_str(options, "path", uri_path);
110
111 uri_query = g_uri_get_query(uri);
112 if (uri_query) {
113 g_uri_params_iter_init(&qp, uri_query, -1, "&", G_URI_PARAMS_NONE);
114 while (g_uri_params_iter_next(&qp, &qp_name, &qp_value, &gerror)) {
115 uint64_t val;
116 if (!qp_name || gerror) {
117 error_setg(errp, "Failed to parse NFS parameter");
118 return -EINVAL;
119 }
120 if (!qp_value) {
121 error_setg(errp, "Value for NFS parameter expected: %s",
122 qp_name);
123 return -EINVAL;
124 }
125 if (parse_uint_full(qp_value, 0, &val)) {
126 error_setg(errp, "Invalid value for NFS parameter: %s",
127 qp_name);
128 return -EINVAL;
129 }
130 if (g_str_equal(qp_name, "uid")) {
131 qdict_put_str(options, "user", qp_value);
132 } else if (g_str_equal(qp_name, "gid")) {
133 qdict_put_str(options, "group", qp_value);
134 } else if (g_str_equal(qp_name, "tcp-syncnt")) {
135 qdict_put_str(options, "tcp-syn-count", qp_value);
136 } else if (g_str_equal(qp_name, "readahead")) {
137 qdict_put_str(options, "readahead-size", qp_value);
138 } else if (g_str_equal(qp_name, "pagecache")) {
139 qdict_put_str(options, "page-cache-size", qp_value);
140 } else if (g_str_equal(qp_name, "debug")) {
141 qdict_put_str(options, "debug", qp_value);
142 } else {
143 error_setg(errp, "Unknown NFS parameter name: %s", qp_name);
144 return -EINVAL;
145 }
146 }
147 }
148
149 return 0;
150 }
151
nfs_has_filename_options_conflict(QDict * options,Error ** errp)152 static bool nfs_has_filename_options_conflict(QDict *options, Error **errp)
153 {
154 const QDictEntry *qe;
155
156 for (qe = qdict_first(options); qe; qe = qdict_next(options, qe)) {
157 if (!strcmp(qe->key, "host") ||
158 !strcmp(qe->key, "path") ||
159 !strcmp(qe->key, "user") ||
160 !strcmp(qe->key, "group") ||
161 !strcmp(qe->key, "tcp-syn-count") ||
162 !strcmp(qe->key, "readahead-size") ||
163 !strcmp(qe->key, "page-cache-size") ||
164 !strcmp(qe->key, "debug") ||
165 strstart(qe->key, "server.", NULL))
166 {
167 error_setg(errp, "Option %s cannot be used with a filename",
168 qe->key);
169 return true;
170 }
171 }
172
173 return false;
174 }
175
nfs_parse_filename(const char * filename,QDict * options,Error ** errp)176 static void nfs_parse_filename(const char *filename, QDict *options,
177 Error **errp)
178 {
179 if (nfs_has_filename_options_conflict(options, errp)) {
180 return;
181 }
182
183 nfs_parse_uri(filename, options, errp);
184 }
185
186 static void nfs_process_read(void *arg);
187 static void nfs_process_write(void *arg);
188
189 /* Called with QemuMutex held. */
nfs_set_events(NFSClient * client)190 static void nfs_set_events(NFSClient *client)
191 {
192 int ev = nfs_which_events(client->context);
193 if (ev != client->events) {
194 aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
195 (ev & POLLIN) ? nfs_process_read : NULL,
196 (ev & POLLOUT) ? nfs_process_write : NULL,
197 NULL, NULL, client);
198
199 }
200 client->events = ev;
201 }
202
nfs_process_read(void * arg)203 static void nfs_process_read(void *arg)
204 {
205 NFSClient *client = arg;
206
207 qemu_mutex_lock(&client->mutex);
208 nfs_service(client->context, POLLIN);
209 nfs_set_events(client);
210 qemu_mutex_unlock(&client->mutex);
211 }
212
nfs_process_write(void * arg)213 static void nfs_process_write(void *arg)
214 {
215 NFSClient *client = arg;
216
217 qemu_mutex_lock(&client->mutex);
218 nfs_service(client->context, POLLOUT);
219 nfs_set_events(client);
220 qemu_mutex_unlock(&client->mutex);
221 }
222
nfs_co_init_task(BlockDriverState * bs,NFSRPC * task)223 static void coroutine_fn nfs_co_init_task(BlockDriverState *bs, NFSRPC *task)
224 {
225 *task = (NFSRPC) {
226 .co = qemu_coroutine_self(),
227 .bs = bs,
228 .client = bs->opaque,
229 };
230 }
231
232 /* Called (via nfs_service) with QemuMutex held. */
233 static void
nfs_co_generic_cb(int ret,struct nfs_context * nfs,void * data,void * private_data)234 nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
235 void *private_data)
236 {
237 NFSRPC *task = private_data;
238 task->ret = ret;
239 assert(!task->st);
240 if (task->ret > 0 && task->iov) {
241 if (task->ret <= task->iov->size) {
242 qemu_iovec_from_buf(task->iov, 0, data, task->ret);
243 } else {
244 task->ret = -EIO;
245 }
246 }
247 if (task->ret < 0) {
248 error_report("NFS Error: %s", nfs_get_error(nfs));
249 }
250
251 /*
252 * Safe to call: nfs_service(), which called us, is only run from the FD
253 * handlers, never from the request coroutine. The request coroutine in
254 * turn will yield unconditionally.
255 * No need to release the lock, even if we directly enter the coroutine, as
256 * the lock is never re-taken after yielding. (Note: If we do enter the
257 * coroutine, @task will probably be dangling once aio_co_wake() returns.)
258 */
259 aio_co_wake(task->co);
260 }
261
nfs_co_preadv(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * iov,BdrvRequestFlags flags)262 static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, int64_t offset,
263 int64_t bytes, QEMUIOVector *iov,
264 BdrvRequestFlags flags)
265 {
266 NFSClient *client = bs->opaque;
267 NFSRPC task;
268
269 nfs_co_init_task(bs, &task);
270 task.iov = iov;
271
272 WITH_QEMU_LOCK_GUARD(&client->mutex) {
273 if (nfs_pread_async(client->context, client->fh,
274 offset, bytes, nfs_co_generic_cb, &task) != 0) {
275 return -ENOMEM;
276 }
277
278 nfs_set_events(client);
279 }
280 qemu_coroutine_yield();
281
282 if (task.ret < 0) {
283 return task.ret;
284 }
285
286 /* zero pad short reads */
287 if (task.ret < iov->size) {
288 qemu_iovec_memset(iov, task.ret, 0, iov->size - task.ret);
289 }
290
291 return 0;
292 }
293
nfs_co_pwritev(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * iov,BdrvRequestFlags flags)294 static int coroutine_fn nfs_co_pwritev(BlockDriverState *bs, int64_t offset,
295 int64_t bytes, QEMUIOVector *iov,
296 BdrvRequestFlags flags)
297 {
298 NFSClient *client = bs->opaque;
299 NFSRPC task;
300 char *buf = NULL;
301 bool my_buffer = false;
302
303 nfs_co_init_task(bs, &task);
304
305 if (iov->niov != 1) {
306 buf = g_try_malloc(bytes);
307 if (bytes && buf == NULL) {
308 return -ENOMEM;
309 }
310 qemu_iovec_to_buf(iov, 0, buf, bytes);
311 my_buffer = true;
312 } else {
313 buf = iov->iov[0].iov_base;
314 }
315
316 WITH_QEMU_LOCK_GUARD(&client->mutex) {
317 if (nfs_pwrite_async(client->context, client->fh,
318 offset, bytes, buf,
319 nfs_co_generic_cb, &task) != 0) {
320 if (my_buffer) {
321 g_free(buf);
322 }
323 return -ENOMEM;
324 }
325
326 nfs_set_events(client);
327 }
328 qemu_coroutine_yield();
329
330 if (my_buffer) {
331 g_free(buf);
332 }
333
334 if (task.ret != bytes) {
335 return task.ret < 0 ? task.ret : -EIO;
336 }
337
338 return 0;
339 }
340
nfs_co_flush(BlockDriverState * bs)341 static int coroutine_fn nfs_co_flush(BlockDriverState *bs)
342 {
343 NFSClient *client = bs->opaque;
344 NFSRPC task;
345
346 nfs_co_init_task(bs, &task);
347
348 WITH_QEMU_LOCK_GUARD(&client->mutex) {
349 if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb,
350 &task) != 0) {
351 return -ENOMEM;
352 }
353
354 nfs_set_events(client);
355 }
356 qemu_coroutine_yield();
357
358 return task.ret;
359 }
360
nfs_detach_aio_context(BlockDriverState * bs)361 static void nfs_detach_aio_context(BlockDriverState *bs)
362 {
363 NFSClient *client = bs->opaque;
364
365 aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
366 NULL, NULL, NULL, NULL, NULL);
367 client->events = 0;
368 }
369
nfs_attach_aio_context(BlockDriverState * bs,AioContext * new_context)370 static void nfs_attach_aio_context(BlockDriverState *bs,
371 AioContext *new_context)
372 {
373 NFSClient *client = bs->opaque;
374
375 client->aio_context = new_context;
376 nfs_set_events(client);
377 }
378
nfs_client_close(NFSClient * client)379 static void nfs_client_close(NFSClient *client)
380 {
381 if (client->context) {
382 qemu_mutex_lock(&client->mutex);
383 aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
384 NULL, NULL, NULL, NULL, NULL);
385 qemu_mutex_unlock(&client->mutex);
386 if (client->fh) {
387 nfs_close(client->context, client->fh);
388 client->fh = NULL;
389 }
390 #ifdef LIBNFS_FEATURE_UMOUNT
391 nfs_umount(client->context);
392 #endif
393 nfs_destroy_context(client->context);
394 client->context = NULL;
395 }
396 g_free(client->path);
397 qemu_mutex_destroy(&client->mutex);
398 qapi_free_NFSServer(client->server);
399 client->server = NULL;
400 }
401
nfs_file_close(BlockDriverState * bs)402 static void nfs_file_close(BlockDriverState *bs)
403 {
404 NFSClient *client = bs->opaque;
405 nfs_client_close(client);
406 }
407
nfs_client_open(NFSClient * client,BlockdevOptionsNfs * opts,int flags,int open_flags,Error ** errp)408 static int64_t nfs_client_open(NFSClient *client, BlockdevOptionsNfs *opts,
409 int flags, int open_flags, Error **errp)
410 {
411 int64_t ret = -EINVAL;
412 #ifdef _WIN32
413 struct __stat64 st;
414 #else
415 struct stat st;
416 #endif
417 char *file = NULL, *strp = NULL;
418
419 qemu_mutex_init(&client->mutex);
420
421 client->path = g_strdup(opts->path);
422
423 strp = strrchr(client->path, '/');
424 if (strp == NULL) {
425 error_setg(errp, "Invalid URL specified");
426 goto fail;
427 }
428 file = g_strdup(strp);
429 *strp = 0;
430
431 /* Steal the NFSServer object from opts; set the original pointer to NULL
432 * to avoid use after free and double free. */
433 client->server = opts->server;
434 opts->server = NULL;
435
436 client->context = nfs_init_context();
437 if (client->context == NULL) {
438 error_setg(errp, "Failed to init NFS context");
439 goto fail;
440 }
441
442 if (opts->has_user) {
443 client->uid = opts->user;
444 nfs_set_uid(client->context, client->uid);
445 }
446
447 if (opts->has_group) {
448 client->gid = opts->group;
449 nfs_set_gid(client->context, client->gid);
450 }
451
452 if (opts->has_tcp_syn_count) {
453 client->tcp_syncnt = opts->tcp_syn_count;
454 nfs_set_tcp_syncnt(client->context, client->tcp_syncnt);
455 }
456
457 #ifdef LIBNFS_FEATURE_READAHEAD
458 if (opts->has_readahead_size) {
459 if (open_flags & BDRV_O_NOCACHE) {
460 error_setg(errp, "Cannot enable NFS readahead "
461 "if cache.direct = on");
462 goto fail;
463 }
464 client->readahead = opts->readahead_size;
465 if (client->readahead > QEMU_NFS_MAX_READAHEAD_SIZE) {
466 warn_report("Truncating NFS readahead size to %d",
467 QEMU_NFS_MAX_READAHEAD_SIZE);
468 client->readahead = QEMU_NFS_MAX_READAHEAD_SIZE;
469 }
470 nfs_set_readahead(client->context, client->readahead);
471 #ifdef LIBNFS_FEATURE_PAGECACHE
472 nfs_set_pagecache_ttl(client->context, 0);
473 #endif
474 client->cache_used = true;
475 }
476 #endif
477
478 #ifdef LIBNFS_FEATURE_PAGECACHE
479 if (opts->has_page_cache_size) {
480 if (open_flags & BDRV_O_NOCACHE) {
481 error_setg(errp, "Cannot enable NFS pagecache "
482 "if cache.direct = on");
483 goto fail;
484 }
485 client->pagecache = opts->page_cache_size;
486 if (client->pagecache > QEMU_NFS_MAX_PAGECACHE_SIZE) {
487 warn_report("Truncating NFS pagecache size to %d pages",
488 QEMU_NFS_MAX_PAGECACHE_SIZE);
489 client->pagecache = QEMU_NFS_MAX_PAGECACHE_SIZE;
490 }
491 nfs_set_pagecache(client->context, client->pagecache);
492 nfs_set_pagecache_ttl(client->context, 0);
493 client->cache_used = true;
494 }
495 #endif
496
497 #ifdef LIBNFS_FEATURE_DEBUG
498 if (opts->has_debug) {
499 client->debug = opts->debug;
500 /* limit the maximum debug level to avoid potential flooding
501 * of our log files. */
502 if (client->debug > QEMU_NFS_MAX_DEBUG_LEVEL) {
503 warn_report("Limiting NFS debug level to %d",
504 QEMU_NFS_MAX_DEBUG_LEVEL);
505 client->debug = QEMU_NFS_MAX_DEBUG_LEVEL;
506 }
507 nfs_set_debug(client->context, client->debug);
508 }
509 #endif
510
511 ret = nfs_mount(client->context, client->server->host, client->path);
512 if (ret < 0) {
513 error_setg(errp, "Failed to mount nfs share: %s",
514 nfs_get_error(client->context));
515 goto fail;
516 }
517
518 if (flags & O_CREAT) {
519 ret = nfs_creat(client->context, file, 0600, &client->fh);
520 if (ret < 0) {
521 error_setg(errp, "Failed to create file: %s",
522 nfs_get_error(client->context));
523 goto fail;
524 }
525 } else {
526 ret = nfs_open(client->context, file, flags, &client->fh);
527 if (ret < 0) {
528 error_setg(errp, "Failed to open file : %s",
529 nfs_get_error(client->context));
530 goto fail;
531 }
532 }
533
534 ret = nfs_fstat(client->context, client->fh, &st);
535 if (ret < 0) {
536 error_setg(errp, "Failed to fstat file: %s",
537 nfs_get_error(client->context));
538 goto fail;
539 }
540
541 ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE);
542 #if !defined(_WIN32)
543 client->st_blocks = st.st_blocks;
544 #endif
545 client->has_zero_init = S_ISREG(st.st_mode);
546 *strp = '/';
547 goto out;
548
549 fail:
550 nfs_client_close(client);
551 out:
552 g_free(file);
553 return ret;
554 }
555
nfs_options_qdict_to_qapi(QDict * options,Error ** errp)556 static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
557 Error **errp)
558 {
559 BlockdevOptionsNfs *opts = NULL;
560 Visitor *v;
561 const QDictEntry *e;
562
563 v = qobject_input_visitor_new_flat_confused(options, errp);
564 if (!v) {
565 return NULL;
566 }
567
568 visit_type_BlockdevOptionsNfs(v, NULL, &opts, errp);
569 visit_free(v);
570 if (!opts) {
571 return NULL;
572 }
573
574 /* Remove the processed options from the QDict (the visitor processes
575 * _all_ options in the QDict) */
576 while ((e = qdict_first(options))) {
577 qdict_del(options, e->key);
578 }
579
580 return opts;
581 }
582
nfs_client_open_qdict(NFSClient * client,QDict * options,int flags,int open_flags,Error ** errp)583 static int64_t nfs_client_open_qdict(NFSClient *client, QDict *options,
584 int flags, int open_flags, Error **errp)
585 {
586 BlockdevOptionsNfs *opts;
587 int64_t ret;
588
589 opts = nfs_options_qdict_to_qapi(options, errp);
590 if (opts == NULL) {
591 ret = -EINVAL;
592 goto fail;
593 }
594
595 ret = nfs_client_open(client, opts, flags, open_flags, errp);
596 fail:
597 qapi_free_BlockdevOptionsNfs(opts);
598 return ret;
599 }
600
nfs_file_open(BlockDriverState * bs,QDict * options,int flags,Error ** errp)601 static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
602 Error **errp) {
603 NFSClient *client = bs->opaque;
604 int64_t ret;
605
606 client->aio_context = bdrv_get_aio_context(bs);
607
608 ret = nfs_client_open_qdict(client, options,
609 (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
610 bs->open_flags, errp);
611 if (ret < 0) {
612 return ret;
613 }
614
615 bs->total_sectors = ret;
616 if (client->has_zero_init) {
617 bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE;
618 }
619 return 0;
620 }
621
622 static QemuOptsList nfs_create_opts = {
623 .name = "nfs-create-opts",
624 .head = QTAILQ_HEAD_INITIALIZER(nfs_create_opts.head),
625 .desc = {
626 {
627 .name = BLOCK_OPT_SIZE,
628 .type = QEMU_OPT_SIZE,
629 .help = "Virtual disk size"
630 },
631 { /* end of list */ }
632 }
633 };
634
nfs_file_co_create(BlockdevCreateOptions * options,Error ** errp)635 static int nfs_file_co_create(BlockdevCreateOptions *options, Error **errp)
636 {
637 BlockdevCreateOptionsNfs *opts = &options->u.nfs;
638 NFSClient *client = g_new0(NFSClient, 1);
639 int ret;
640
641 assert(options->driver == BLOCKDEV_DRIVER_NFS);
642
643 client->aio_context = qemu_get_aio_context();
644
645 ret = nfs_client_open(client, opts->location, O_CREAT, 0, errp);
646 if (ret < 0) {
647 goto out;
648 }
649 ret = nfs_ftruncate(client->context, client->fh, opts->size);
650 nfs_client_close(client);
651
652 out:
653 g_free(client);
654 return ret;
655 }
656
nfs_file_co_create_opts(BlockDriver * drv,const char * url,QemuOpts * opts,Error ** errp)657 static int coroutine_fn nfs_file_co_create_opts(BlockDriver *drv,
658 const char *url,
659 QemuOpts *opts,
660 Error **errp)
661 {
662 BlockdevCreateOptions *create_options;
663 BlockdevCreateOptionsNfs *nfs_opts;
664 QDict *options;
665 int ret;
666
667 create_options = g_new0(BlockdevCreateOptions, 1);
668 create_options->driver = BLOCKDEV_DRIVER_NFS;
669 nfs_opts = &create_options->u.nfs;
670
671 /* Read out options */
672 nfs_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
673 BDRV_SECTOR_SIZE);
674
675 options = qdict_new();
676 ret = nfs_parse_uri(url, options, errp);
677 if (ret < 0) {
678 goto out;
679 }
680
681 nfs_opts->location = nfs_options_qdict_to_qapi(options, errp);
682 if (nfs_opts->location == NULL) {
683 ret = -EINVAL;
684 goto out;
685 }
686
687 ret = nfs_file_co_create(create_options, errp);
688 if (ret < 0) {
689 goto out;
690 }
691
692 ret = 0;
693 out:
694 qobject_unref(options);
695 qapi_free_BlockdevCreateOptions(create_options);
696 return ret;
697 }
698
nfs_has_zero_init(BlockDriverState * bs)699 static int nfs_has_zero_init(BlockDriverState *bs)
700 {
701 NFSClient *client = bs->opaque;
702 return client->has_zero_init;
703 }
704
705 #if !defined(_WIN32)
706 /* Called (via nfs_service) with QemuMutex held. */
707 static void
nfs_get_allocated_file_size_cb(int ret,struct nfs_context * nfs,void * data,void * private_data)708 nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data,
709 void *private_data)
710 {
711 NFSRPC *task = private_data;
712 task->ret = ret;
713 if (task->ret == 0) {
714 memcpy(task->st, data, sizeof(struct stat));
715 }
716 if (task->ret < 0) {
717 error_report("NFS Error: %s", nfs_get_error(nfs));
718 }
719 /* Safe to call, see nfs_co_generic_cb() */
720 aio_co_wake(task->co);
721 }
722
nfs_co_get_allocated_file_size(BlockDriverState * bs)723 static int64_t coroutine_fn nfs_co_get_allocated_file_size(BlockDriverState *bs)
724 {
725 NFSClient *client = bs->opaque;
726 NFSRPC task = {0};
727 struct stat st;
728
729 if (bdrv_is_read_only(bs) &&
730 !(bs->open_flags & BDRV_O_NOCACHE)) {
731 return client->st_blocks * 512;
732 }
733
734 nfs_co_init_task(bs, &task);
735 task.st = &st;
736 WITH_QEMU_LOCK_GUARD(&client->mutex) {
737 if (nfs_fstat_async(client->context, client->fh, nfs_get_allocated_file_size_cb,
738 &task) != 0) {
739 return -ENOMEM;
740 }
741
742 nfs_set_events(client);
743 }
744 qemu_coroutine_yield();
745
746 return (task.ret < 0 ? task.ret : st.st_blocks * 512);
747 }
748 #endif
749
750 static int coroutine_fn
nfs_file_co_truncate(BlockDriverState * bs,int64_t offset,bool exact,PreallocMode prealloc,BdrvRequestFlags flags,Error ** errp)751 nfs_file_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
752 PreallocMode prealloc, BdrvRequestFlags flags,
753 Error **errp)
754 {
755 NFSClient *client = bs->opaque;
756 int ret;
757
758 if (prealloc != PREALLOC_MODE_OFF) {
759 error_setg(errp, "Unsupported preallocation mode '%s'",
760 PreallocMode_str(prealloc));
761 return -ENOTSUP;
762 }
763
764 ret = nfs_ftruncate(client->context, client->fh, offset);
765 if (ret < 0) {
766 error_setg_errno(errp, -ret, "Failed to truncate file");
767 return ret;
768 }
769
770 return 0;
771 }
772
773 /* Note that this will not re-establish a connection with the NFS server
774 * - it is effectively a NOP. */
nfs_reopen_prepare(BDRVReopenState * state,BlockReopenQueue * queue,Error ** errp)775 static int nfs_reopen_prepare(BDRVReopenState *state,
776 BlockReopenQueue *queue, Error **errp)
777 {
778 NFSClient *client = state->bs->opaque;
779 #ifdef _WIN32
780 struct __stat64 st;
781 #else
782 struct stat st;
783 #endif
784 int ret = 0;
785
786 if (state->flags & BDRV_O_RDWR && bdrv_is_read_only(state->bs)) {
787 error_setg(errp, "Cannot open a read-only mount as read-write");
788 return -EACCES;
789 }
790
791 if ((state->flags & BDRV_O_NOCACHE) && client->cache_used) {
792 error_setg(errp, "Cannot disable cache if libnfs readahead or"
793 " pagecache is enabled");
794 return -EINVAL;
795 }
796
797 /* Update cache for read-only reopens */
798 if (!(state->flags & BDRV_O_RDWR)) {
799 ret = nfs_fstat(client->context, client->fh, &st);
800 if (ret < 0) {
801 error_setg(errp, "Failed to fstat file: %s",
802 nfs_get_error(client->context));
803 return ret;
804 }
805 #if !defined(_WIN32)
806 client->st_blocks = st.st_blocks;
807 #endif
808 }
809
810 return 0;
811 }
812
nfs_refresh_filename(BlockDriverState * bs)813 static void nfs_refresh_filename(BlockDriverState *bs)
814 {
815 NFSClient *client = bs->opaque;
816
817 if (client->uid && !client->gid) {
818 snprintf(bs->exact_filename, sizeof(bs->exact_filename),
819 "nfs://%s%s?uid=%" PRId64, client->server->host, client->path,
820 client->uid);
821 } else if (!client->uid && client->gid) {
822 snprintf(bs->exact_filename, sizeof(bs->exact_filename),
823 "nfs://%s%s?gid=%" PRId64, client->server->host, client->path,
824 client->gid);
825 } else if (client->uid && client->gid) {
826 snprintf(bs->exact_filename, sizeof(bs->exact_filename),
827 "nfs://%s%s?uid=%" PRId64 "&gid=%" PRId64,
828 client->server->host, client->path, client->uid, client->gid);
829 } else {
830 snprintf(bs->exact_filename, sizeof(bs->exact_filename),
831 "nfs://%s%s", client->server->host, client->path);
832 }
833 }
834
nfs_dirname(BlockDriverState * bs,Error ** errp)835 static char * GRAPH_RDLOCK nfs_dirname(BlockDriverState *bs, Error **errp)
836 {
837 NFSClient *client = bs->opaque;
838
839 if (client->uid || client->gid) {
840 bdrv_refresh_filename(bs);
841 error_setg(errp, "Cannot generate a base directory for NFS node '%s'",
842 bs->filename);
843 return NULL;
844 }
845
846 return g_strdup_printf("nfs://%s%s/", client->server->host, client->path);
847 }
848
849 #ifdef LIBNFS_FEATURE_PAGECACHE
nfs_co_invalidate_cache(BlockDriverState * bs,Error ** errp)850 static void coroutine_fn nfs_co_invalidate_cache(BlockDriverState *bs,
851 Error **errp)
852 {
853 NFSClient *client = bs->opaque;
854 nfs_pagecache_invalidate(client->context, client->fh);
855 }
856 #endif
857
858 static const char *nfs_strong_runtime_opts[] = {
859 "path",
860 "user",
861 "group",
862 "server.",
863
864 NULL
865 };
866
867 static BlockDriver bdrv_nfs = {
868 .format_name = "nfs",
869 .protocol_name = "nfs",
870
871 .instance_size = sizeof(NFSClient),
872 .bdrv_parse_filename = nfs_parse_filename,
873 .create_opts = &nfs_create_opts,
874
875 .bdrv_has_zero_init = nfs_has_zero_init,
876 /* libnfs does not provide the allocated filesize of a file on win32. */
877 #if !defined(_WIN32)
878 .bdrv_co_get_allocated_file_size = nfs_co_get_allocated_file_size,
879 #endif
880 .bdrv_co_truncate = nfs_file_co_truncate,
881
882 .bdrv_open = nfs_file_open,
883 .bdrv_close = nfs_file_close,
884 .bdrv_co_create = nfs_file_co_create,
885 .bdrv_co_create_opts = nfs_file_co_create_opts,
886 .bdrv_reopen_prepare = nfs_reopen_prepare,
887
888 .bdrv_co_preadv = nfs_co_preadv,
889 .bdrv_co_pwritev = nfs_co_pwritev,
890 .bdrv_co_flush_to_disk = nfs_co_flush,
891
892 .bdrv_detach_aio_context = nfs_detach_aio_context,
893 .bdrv_attach_aio_context = nfs_attach_aio_context,
894 .bdrv_refresh_filename = nfs_refresh_filename,
895 .bdrv_dirname = nfs_dirname,
896
897 .strong_runtime_opts = nfs_strong_runtime_opts,
898
899 #ifdef LIBNFS_FEATURE_PAGECACHE
900 .bdrv_co_invalidate_cache = nfs_co_invalidate_cache,
901 #endif
902 };
903
nfs_block_init(void)904 static void nfs_block_init(void)
905 {
906 bdrv_register(&bdrv_nfs);
907 }
908
909 block_init(nfs_block_init);
910