plugins: Wire up rust plugin support for NBD_INFO_INIT_STATE
[nbdkit/ericb.git] / plugins / file / file.c
blobca7e2d9e76cfb3467e600a9bc406daeddb628bc0
1 /* nbdkit
2 * Copyright (C) 2013-2020 Red Hat Inc.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
33 #include <config.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdbool.h>
38 #include <string.h>
39 #include <inttypes.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/ioctl.h>
45 #include <errno.h>
47 #include <pthread.h>
49 #if defined (__linux__) && !defined (FALLOC_FL_PUNCH_HOLE)
50 #include <linux/falloc.h> /* For FALLOC_FL_*, glibc < 2.18 */
51 #endif
53 #if defined (__linux__)
54 #include <linux/fs.h> /* For BLKZEROOUT */
55 #endif
57 #define NBDKIT_API_VERSION 2
59 #include <nbdkit-plugin.h>
61 #include "cleanup.h"
62 #include "isaligned.h"
64 #ifndef HAVE_FDATASYNC
65 #define fdatasync fsync
66 #endif
68 static char *filename = NULL;
70 /* Any callbacks using lseek must be protected by this lock. */
71 static pthread_mutex_t lseek_lock = PTHREAD_MUTEX_INITIALIZER;
73 /* to enable: -D file.zero=1 */
74 int file_debug_zero;
76 static bool
77 is_enotsup (int err)
79 return err == ENOTSUP || err == EOPNOTSUPP;
82 static void
83 file_unload (void)
85 free (filename);
88 /* Called for each key=value passed on the command line. This plugin
89 * only accepts file=<filename>, which is required.
91 static int
92 file_config (const char *key, const char *value)
94 if (strcmp (key, "file") == 0) {
95 /* See FILENAMES AND PATHS in nbdkit-plugin(3). */
96 free (filename);
97 filename = nbdkit_realpath (value);
98 if (!filename)
99 return -1;
101 else if (strcmp (key, "rdelay") == 0 ||
102 strcmp (key, "wdelay") == 0) {
103 nbdkit_error ("add --filter=delay on the command line");
104 return -1;
106 else {
107 nbdkit_error ("unknown parameter '%s'", key);
108 return -1;
111 return 0;
114 /* Check the user did pass a file=<FILENAME> parameter. */
115 static int
116 file_config_complete (void)
118 if (filename == NULL) {
119 nbdkit_error ("you must supply the file=<FILENAME> parameter "
120 "after the plugin name on the command line");
121 return -1;
124 return 0;
127 #define file_config_help \
128 "file=<FILENAME> (required) The filename to serve." \
130 /* Print some extra information about how the plugin was compiled. */
131 static void
132 file_dump_plugin (void)
134 #ifdef BLKSSZGET
135 printf ("file_blksszget=yes\n");
136 #endif
137 #ifdef BLKZEROOUT
138 printf ("file_blkzeroout=yes\n");
139 #endif
140 #ifdef FALLOC_FL_PUNCH_HOLE
141 printf ("file_falloc_fl_punch_hole=yes\n");
142 #endif
143 #ifdef FALLOC_FL_ZERO_RANGE
144 printf ("file_falloc_fl_zero_range=yes\n");
145 #endif
148 /* The per-connection handle. */
149 struct handle {
150 int fd;
151 bool is_block_device;
152 int sector_size;
153 bool can_punch_hole;
154 bool can_zero_range;
155 bool can_fallocate;
156 bool can_zeroout;
157 bool can_extents;
158 bool init_sparse;
159 bool init_zero;
162 /* Create the per-connection handle. */
163 static void *
164 file_open (int readonly)
166 struct handle *h;
167 struct stat statbuf;
168 int flags;
170 h = malloc (sizeof *h);
171 if (h == NULL) {
172 nbdkit_error ("malloc: %m");
173 return NULL;
176 flags = O_CLOEXEC|O_NOCTTY;
177 if (readonly)
178 flags |= O_RDONLY;
179 else
180 flags |= O_RDWR;
182 h->fd = open (filename, flags);
183 if (h->fd == -1) {
184 nbdkit_error ("open: %s: %m", filename);
185 free (h);
186 return NULL;
189 if (fstat (h->fd, &statbuf) == -1) {
190 nbdkit_error ("fstat: %s: %m", filename);
191 free (h);
192 return NULL;
195 h->is_block_device = S_ISBLK (statbuf.st_mode);
196 h->sector_size = 4096; /* Start with safe guess */
198 #ifdef BLKSSZGET
199 if (h->is_block_device) {
200 if (ioctl (h->fd, BLKSSZGET, &h->sector_size))
201 nbdkit_debug ("cannot get sector size: %s: %m", filename);
203 #endif
205 #ifdef FALLOC_FL_PUNCH_HOLE
206 h->can_punch_hole = true;
207 #else
208 h->can_punch_hole = false;
209 #endif
211 #ifdef FALLOC_FL_ZERO_RANGE
212 h->can_zero_range = true;
213 #else
214 h->can_zero_range = false;
215 #endif
217 h->can_fallocate = true;
218 h->can_zeroout = h->is_block_device;
220 h->can_extents = false;
221 h->init_sparse = false;
222 h->init_zero = false;
223 #ifdef SEEK_HOLE
224 if (!h->is_block_device) {
225 off_t r;
227 /* A simple test to see whether SEEK_DATA/SEEK_HOLE are likely to work on
228 * the current filesystem, and to see if the image is sparse or zero.
230 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lseek_lock);
231 r = lseek (h->fd, 0, SEEK_DATA);
232 if (r == -1) {
233 if (errno == ENXIO) {
234 nbdkit_debug ("extents enabled, entire image is hole");
235 h->can_extents = true;
236 h->init_sparse = true;
237 h->init_zero = true;
238 } else {
239 nbdkit_debug ("extents disabled: lseek(SEEK_DATA): %m");
242 else {
243 h->can_extents = true;
244 if (r > 0) {
245 nbdkit_debug ("extents enabled, image includes hole before data");
246 h->init_sparse = true;
248 else {
249 r = lseek (h->fd, 0, SEEK_HOLE);
250 if (r == -1) {
251 nbdkit_debug ("extents disabled: lseek(SEEK_HOLE): %m");
252 h->can_extents = false;
254 else if (r == statbuf.st_size) {
255 nbdkit_debug ("extents enabled, image currently all data");
257 else {
258 nbdkit_debug ("extents enabled, image includes data before hole");
259 h->init_sparse = true;
264 #endif
266 return h;
269 /* Free up the per-connection handle. */
270 static void
271 file_close (void *handle)
273 struct handle *h = handle;
275 close (h->fd);
276 free (h);
279 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
281 /* For block devices, stat->st_size is not the true size. The caller
282 * grabs the lseek_lock.
284 static int64_t
285 block_device_size (int fd)
287 off_t size;
289 size = lseek (fd, 0, SEEK_END);
290 if (size == -1) {
291 nbdkit_error ("lseek (to find device size): %m");
292 return -1;
295 return size;
298 /* Get the file size. */
299 static int64_t
300 file_get_size (void *handle)
302 struct handle *h = handle;
304 if (h->is_block_device) {
305 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lseek_lock);
306 return block_device_size (h->fd);
307 } else {
308 /* Regular file. */
309 struct stat statbuf;
311 if (fstat (h->fd, &statbuf) == -1) {
312 nbdkit_error ("fstat: %m");
313 return -1;
316 return statbuf.st_size;
320 /* Allow multiple parallel connections from a single client. */
321 static int
322 file_can_multi_conn (void *handle)
324 return 1;
327 static int
328 file_can_trim (void *handle)
330 /* Trim is advisory, but we prefer to advertise it only when we can
331 * actually (attempt to) punch holes. Since not all filesystems
332 * support all fallocate modes, it would be nice if we had a way
333 * from fpathconf() to definitively learn what will work on a given
334 * fd for a more precise answer; oh well. */
335 #ifdef FALLOC_FL_PUNCH_HOLE
336 return 1;
337 #else
338 return 0;
339 #endif
342 static int
343 file_can_fua (void *handle)
345 return NBDKIT_FUA_NATIVE;
348 static int
349 file_can_cache (void *handle)
351 /* Prefer posix_fadvise(), but letting nbdkit call .pread on our
352 * behalf also tends to work well for the local file system
353 * cache.
355 #if HAVE_POSIX_FADVISE
356 return NBDKIT_FUA_NATIVE;
357 #else
358 return NBDKIT_FUA_EMULATE;
359 #endif
362 /* Flush the file to disk. */
363 static int
364 file_flush (void *handle, uint32_t flags)
366 struct handle *h = handle;
368 if (fdatasync (h->fd) == -1) {
369 nbdkit_error ("fdatasync: %m");
370 return -1;
373 return 0;
376 /* Read data from the file. */
377 static int
378 file_pread (void *handle, void *buf, uint32_t count, uint64_t offset,
379 uint32_t flags)
381 struct handle *h = handle;
383 while (count > 0) {
384 ssize_t r = pread (h->fd, buf, count, offset);
385 if (r == -1) {
386 nbdkit_error ("pread: %m");
387 return -1;
389 if (r == 0) {
390 nbdkit_error ("pread: unexpected end of file");
391 return -1;
393 buf += r;
394 count -= r;
395 offset += r;
398 return 0;
401 /* Write data to the file. */
402 static int
403 file_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset,
404 uint32_t flags)
406 struct handle *h = handle;
408 while (count > 0) {
409 ssize_t r = pwrite (h->fd, buf, count, offset);
410 if (r == -1) {
411 nbdkit_error ("pwrite: %m");
412 return -1;
414 buf += r;
415 count -= r;
416 offset += r;
419 if ((flags & NBDKIT_FLAG_FUA) && file_flush (handle, 0) == -1)
420 return -1;
422 return 0;
425 #if defined (FALLOC_FL_PUNCH_HOLE) || defined (FALLOC_FL_ZERO_RANGE)
426 static int
427 do_fallocate (int fd, int mode, off_t offset, off_t len)
429 int r = fallocate (fd, mode, offset, len);
430 if (r == -1 && errno == ENODEV) {
431 /* kernel 3.10 fails with ENODEV for block device. Kernel >= 4.9 fails
432 with EOPNOTSUPP in this case. Normalize errno to simplify callers. */
433 errno = EOPNOTSUPP;
435 return r;
437 #endif
439 /* Write zeroes to the file. */
440 static int
441 file_zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
443 struct handle *h = handle;
444 int r;
446 #ifdef FALLOC_FL_PUNCH_HOLE
447 if (h->can_punch_hole && (flags & NBDKIT_FLAG_MAY_TRIM)) {
448 r = do_fallocate (h->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
449 offset, count);
450 if (r == 0) {
451 if (file_debug_zero)
452 nbdkit_debug ("h->can_punch_hole && may_trim: "
453 "zero succeeded using fallocate");
454 goto out;
457 if (!is_enotsup (errno)) {
458 nbdkit_error ("zero: %m");
459 return -1;
462 h->can_punch_hole = false;
464 #endif
466 #ifdef FALLOC_FL_ZERO_RANGE
467 if (h->can_zero_range) {
468 r = do_fallocate (h->fd, FALLOC_FL_ZERO_RANGE, offset, count);
469 if (r == 0) {
470 if (file_debug_zero)
471 nbdkit_debug ("h->can_zero-range: "
472 "zero succeeded using fallocate");
473 goto out;
476 if (!is_enotsup (errno)) {
477 nbdkit_error ("zero: %m");
478 return -1;
481 h->can_zero_range = false;
483 #endif
485 #ifdef FALLOC_FL_PUNCH_HOLE
486 /* If we can punch hole but may not trim, we can combine punching hole and
487 * fallocate to zero a range. This is expected to be more efficient than
488 * writing zeroes manually. */
489 if (h->can_punch_hole && h->can_fallocate) {
490 r = do_fallocate (h->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
491 offset, count);
492 if (r == 0) {
493 r = do_fallocate (h->fd, 0, offset, count);
494 if (r == 0) {
495 if (file_debug_zero)
496 nbdkit_debug ("h->can_punch_hole && h->can_fallocate: "
497 "zero succeeded using fallocate");
498 goto out;
501 if (!is_enotsup (errno)) {
502 nbdkit_error ("zero: %m");
503 return -1;
506 h->can_fallocate = false;
507 } else {
508 if (!is_enotsup (errno)) {
509 nbdkit_error ("zero: %m");
510 return -1;
513 h->can_punch_hole = false;
516 #endif
518 #ifdef BLKZEROOUT
519 /* For aligned range and block device, we can use BLKZEROOUT. */
520 if (h->can_zeroout && IS_ALIGNED (offset | count, h->sector_size)) {
521 uint64_t range[2] = {offset, count};
523 r = ioctl (h->fd, BLKZEROOUT, &range);
524 if (r == 0) {
525 if (file_debug_zero)
526 nbdkit_debug ("h->can_zeroout && IS_ALIGNED: "
527 "zero succeeded using BLKZEROOUT");
528 goto out;
531 if (errno != ENOTTY) {
532 nbdkit_error ("zero: %m");
533 return -1;
536 h->can_zeroout = false;
538 #endif
540 /* Trigger a fall back to writing */
541 if (file_debug_zero)
542 nbdkit_debug ("zero falling back to writing");
543 errno = EOPNOTSUPP;
544 return -1;
546 out:
547 if ((flags & NBDKIT_FLAG_FUA) && file_flush (handle, 0) == -1)
548 return -1;
549 return 0;
552 /* Punch a hole in the file. */
553 static int
554 file_trim (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
556 #ifdef FALLOC_FL_PUNCH_HOLE
557 struct handle *h = handle;
558 int r;
560 if (h->can_punch_hole) {
561 r = do_fallocate (h->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
562 offset, count);
563 if (r == -1) {
564 /* Trim is advisory; we don't care if it fails for anything other
565 * than EIO or EPERM. */
566 if (errno == EPERM || errno == EIO) {
567 nbdkit_error ("fallocate: %m");
568 return -1;
571 if (is_enotsup (EOPNOTSUPP))
572 h->can_punch_hole = false;
574 nbdkit_debug ("ignoring failed fallocate during trim: %m");
577 #endif
579 if ((flags & NBDKIT_FLAG_FUA) && file_flush (handle, 0) == -1)
580 return -1;
582 return 0;
585 #ifdef SEEK_HOLE
586 /* Extents. */
588 static int
589 file_can_extents (void *handle)
591 struct handle *h = handle;
593 return h->can_extents;
596 static int
597 do_extents (void *handle, uint32_t count, uint64_t offset,
598 uint32_t flags, struct nbdkit_extents *extents)
600 struct handle *h = handle;
601 const bool req_one = flags & NBDKIT_FLAG_REQ_ONE;
602 uint64_t end = offset + count;
604 do {
605 off_t pos;
607 pos = lseek (h->fd, offset, SEEK_DATA);
608 if (pos == -1) {
609 if (errno == ENXIO) {
610 /* The current man page does not describe this situation well,
611 * but a proposed change to POSIX adds these words for ENXIO:
612 * "or the whence argument is SEEK_DATA and the offset falls
613 * within the final hole of the file."
615 pos = end;
617 else {
618 nbdkit_error ("lseek: SEEK_DATA: %" PRIu64 ": %m", offset);
619 return -1;
623 /* We know there is a hole from offset to pos-1. */
624 if (pos > offset) {
625 if (nbdkit_add_extent (extents, offset, pos - offset,
626 NBDKIT_EXTENT_HOLE | NBDKIT_EXTENT_ZERO) == -1)
627 return -1;
628 if (req_one)
629 break;
632 offset = pos;
633 if (offset >= end)
634 break;
636 pos = lseek (h->fd, offset, SEEK_HOLE);
637 if (pos == -1) {
638 nbdkit_error ("lseek: SEEK_HOLE: %" PRIu64 ": %m", offset);
639 return -1;
642 /* We know there is data from offset to pos-1. */
643 if (pos > offset) {
644 if (nbdkit_add_extent (extents, offset, pos - offset,
645 0 /* allocated data */) == -1)
646 return -1;
647 if (req_one)
648 break;
651 offset = pos;
652 } while (offset < end);
654 return 0;
657 static int
658 file_extents (void *handle, uint32_t count, uint64_t offset,
659 uint32_t flags, struct nbdkit_extents *extents)
661 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lseek_lock);
662 return do_extents (handle, count, offset, flags, extents);
665 /* Initial state. */
666 static int
667 file_init_sparse (void *handle)
669 struct handle *h = handle;
671 return h->init_sparse;
674 static int
675 file_init_zero (void *handle)
677 struct handle *h = handle;
679 return h->init_zero;
681 #endif /* SEEK_HOLE */
683 #if HAVE_POSIX_FADVISE
684 /* Caching. */
685 static int
686 file_cache (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
688 struct handle *h = handle;
689 int r;
691 /* Cache is advisory, we don't care if this fails */
692 r = posix_fadvise (h->fd, offset, count, POSIX_FADV_WILLNEED);
693 if (r) {
694 errno = r;
695 nbdkit_error ("posix_fadvise: %m");
696 return -1;
698 return 0;
700 #endif /* HAVE_POSIX_FADVISE */
702 static struct nbdkit_plugin plugin = {
703 .name = "file",
704 .longname = "nbdkit file plugin",
705 .version = PACKAGE_VERSION,
706 .unload = file_unload,
707 .config = file_config,
708 .config_complete = file_config_complete,
709 .config_help = file_config_help,
710 .magic_config_key = "file",
711 .dump_plugin = file_dump_plugin,
712 .open = file_open,
713 .close = file_close,
714 .get_size = file_get_size,
715 .can_multi_conn = file_can_multi_conn,
716 .can_trim = file_can_trim,
717 .can_fua = file_can_fua,
718 .can_cache = file_can_cache,
719 .pread = file_pread,
720 .pwrite = file_pwrite,
721 .flush = file_flush,
722 .trim = file_trim,
723 .zero = file_zero,
724 #ifdef SEEK_HOLE
725 .can_extents = file_can_extents,
726 .extents = file_extents,
727 .init_sparse = file_init_sparse,
728 .init_zero = file_init_zero,
729 #endif
730 #if HAVE_POSIX_FADVISE
731 .cache = file_cache,
732 #endif
733 .errno_is_preserved = 1,
736 NBDKIT_REGISTER_PLUGIN(plugin)