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