2 * Copyright (C) 2013-2021 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
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
34 #error "build error: winfile.c should be used on Windows"
47 #include <sys/types.h>
52 #ifdef HAVE_SYS_IOCTL_H
53 #include <sys/ioctl.h>
58 #if defined (__linux__) && !defined (FALLOC_FL_PUNCH_HOLE)
59 #include <linux/falloc.h> /* For FALLOC_FL_*, glibc < 2.18 */
62 #if defined (__linux__) && HAVE_LINUX_FS_H
63 #include <linux/fs.h> /* For BLKZEROOUT */
66 #define NBDKIT_API_VERSION 2
67 #include <nbdkit-plugin.h>
70 #include "isaligned.h"
71 #include "fdatasync.h"
80 static char *filename
= NULL
;
81 static char *directory
= NULL
;
82 static int filedesc
= -1;
84 /* posix_fadvise mode: -1 = don't set it, or POSIX_FADV_*. */
85 static int fadvise_mode
=
86 #if defined (HAVE_POSIX_FADVISE) && defined (POSIX_FADV_NORMAL)
94 static enum { cache_default
, cache_none
} cache_mode
= cache_default
;
96 /* Define EVICT_WRITES if we are going to evict the page cache
97 * (cache=none) after writing. This is only known to work on Linux.
100 #define EVICT_WRITES 1
104 /* Queue writes so they will be evicted from the cache. See
105 * libnbd.git copy/file-ops.c for the rationale behind this.
109 struct write_window
{
115 static pthread_mutex_t window_lock
= PTHREAD_MUTEX_INITIALIZER
;
116 static struct write_window window
[NR_WINDOWS
];
119 evict_writes (int fd
, uint64_t offset
, size_t len
)
121 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&window_lock
);
123 /* Evict the oldest window from the page cache. */
124 if (window
[0].len
> 0) {
125 sync_file_range (window
[0].fd
, window
[0].offset
, window
[0].len
,
126 SYNC_FILE_RANGE_WAIT_BEFORE
|SYNC_FILE_RANGE_WRITE
|
127 SYNC_FILE_RANGE_WAIT_AFTER
);
128 posix_fadvise (window
[0].fd
, window
[0].offset
, window
[0].len
,
129 POSIX_FADV_DONTNEED
);
132 /* Move the Nth window to N-1. */
133 memmove (&window
[0], &window
[1], sizeof window
[0] * (NR_WINDOWS
-1));
135 /* Set up the current window and tell Linux to start writing it out
136 * to disk (asynchronously).
138 sync_file_range (fd
, offset
, len
, SYNC_FILE_RANGE_WRITE
);
139 window
[NR_WINDOWS
-1].fd
= fd
;
140 window
[NR_WINDOWS
-1].offset
= offset
;
141 window
[NR_WINDOWS
-1].len
= len
;
144 /* When we close the handle we must remove any windows which are still
145 * associated. They missed the boat, oh well :-(
148 remove_fd_from_window (int fd
)
150 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&window_lock
);
153 for (i
= 0; i
< NR_WINDOWS
; ++i
)
154 if (window
[i
].len
> 0 && window
[i
].fd
== fd
)
157 #endif /* EVICT_WRITES */
159 /* Any callbacks using lseek must be protected by this lock. */
160 static pthread_mutex_t lseek_lock
= PTHREAD_MUTEX_INITIALIZER
;
162 /* to enable: -D file.zero=1 */
163 NBDKIT_DLL_PUBLIC
int file_debug_zero
;
165 static bool __attribute__((unused
))
168 return err
== ENOTSUP
|| err
== EOPNOTSUPP
;
178 /* Called for each key=value passed on the command line. This plugin
179 * only accepts file=<filename> and dir=<dirname>, where exactly
183 file_config (const char *key
, const char *value
)
185 /* See FILENAMES AND PATHS in nbdkit-plugin(3).
186 * Our use of nbdkit_realpath requires the destination to exist at
187 * startup; use nbdkit_absolute_path instead if we wanted to defer
188 * existence checks to the last possible moment.
190 if (strcmp (key
, "file") == 0) {
191 if (mode
!= mode_none
) goto wrong_mode
;
192 mode
= mode_filename
;
193 assert (filename
== NULL
);
194 filename
= nbdkit_realpath (value
);
198 else if (strcmp (key
, "directory") == 0 ||
199 strcmp (key
, "dir") == 0) {
200 if (mode
!= mode_none
) goto wrong_mode
;
201 mode
= mode_directory
;
202 assert (directory
== NULL
);
203 directory
= nbdkit_realpath (value
);
207 else if (strcmp (key
, "fd") == 0) {
208 if (mode
!= mode_none
) goto wrong_mode
;
210 assert (filedesc
== -1);
211 if (nbdkit_parse_int ("fd", value
, &filedesc
) == -1)
213 if (filedesc
<= STDERR_FILENO
) {
214 nbdkit_error ("file descriptor must be > %d because "
215 "stdin, stdout and stderr are reserved for nbdkit",
220 else if (strcmp (key
, "dirfd") == 0) {
221 if (mode
!= mode_none
) goto wrong_mode
;
223 assert (filedesc
== -1);
224 if (nbdkit_parse_int ("dirfd", value
, &filedesc
) == -1)
226 if (filedesc
<= STDERR_FILENO
) {
227 nbdkit_error ("file descriptor must be > %d because "
228 "stdin, stdout and stderr are reserved for nbdkit",
233 else if (strcmp (key
, "fadvise") == 0) {
234 /* As this is a hint, if the kernel doesn't support the feature
235 * ignore the parameter.
237 if (strcmp (value
, "normal") == 0) {
238 #if defined (HAVE_POSIX_FADVISE) && defined (POSIX_FADV_NORMAL)
239 fadvise_mode
= POSIX_FADV_NORMAL
;
244 else if (strcmp (value
, "random") == 0) {
245 #if defined (HAVE_POSIX_FADVISE) && defined (POSIX_FADV_RANDOM)
246 fadvise_mode
= POSIX_FADV_RANDOM
;
251 else if (strcmp (value
, "sequential") == 0) {
252 #if defined (HAVE_POSIX_FADVISE) && defined (POSIX_FADV_SEQUENTIAL)
253 fadvise_mode
= POSIX_FADV_SEQUENTIAL
;
259 nbdkit_error ("unknown fadvise mode: %s", value
);
263 else if (strcmp (key
, "cache") == 0) {
264 if (strcmp (value
, "default") == 0)
265 cache_mode
= cache_default
;
266 else if (strcmp (value
, "none") == 0)
267 cache_mode
= cache_none
;
269 nbdkit_error ("unknown cache mode: %s", value
);
273 else if (strcmp (key
, "rdelay") == 0 ||
274 strcmp (key
, "wdelay") == 0) {
275 nbdkit_error ("add --filter=delay on the command line");
279 nbdkit_error ("unknown parameter '%s'", key
);
285 nbdkit_error ("%s parameter can only appear once on the command line",
286 "file|dir|fd|dirfd");
290 /* Check that the user passed exactly one parameter. */
292 file_config_complete (void)
299 nbdkit_error ("you must supply [file=]<FILENAME>, "
300 "dir=<DIRNAME> or fd=<FD> "
301 "parameter after the plugin name "
302 "on the command line");
306 assert (filename
!= NULL
);
307 assert (directory
== NULL
);
308 assert (filedesc
== -1);
310 /* Sanity check now, rather than waiting for first client open.
311 * See also comment in .config about use of nbdkit_realpath. Yes,
312 * this is a harmless TOCTTOU race.
314 r
= stat (filename
, &sb
);
315 if (r
== 0 && S_ISDIR (sb
.st_mode
)) {
316 nbdkit_error ("use dir= to serve files within %s", filename
);
319 if (r
== -1 || !(S_ISBLK (sb
.st_mode
) || S_ISREG (sb
.st_mode
))) {
320 nbdkit_error ("file is not regular or block device: %s", filename
);
326 assert (filename
== NULL
);
327 assert (directory
!= NULL
);
328 assert (filedesc
== -1);
330 if (stat (directory
, &sb
) == -1 || !S_ISDIR (sb
.st_mode
)) {
331 nbdkit_error ("expecting a directory: %s", directory
);
337 assert (filename
== NULL
);
338 assert (directory
== NULL
);
339 assert (filedesc
> STDERR_FILENO
);
341 r
= fstat (filedesc
, &sb
);
342 if (r
== -1 || !(S_ISBLK (sb
.st_mode
) || S_ISREG (sb
.st_mode
))) {
343 nbdkit_error ("fd is not regular or block device: %d", filedesc
);
349 assert (filename
== NULL
);
350 assert (directory
== NULL
);
351 assert (filedesc
> STDERR_FILENO
);
353 r
= fstat (filedesc
, &sb
);
354 if (r
== -1 || !(S_ISDIR (sb
.st_mode
))) {
355 nbdkit_error ("dirfd is not a directory: %d", filedesc
);
363 #define file_config_help \
364 "[file=]<FILENAME> The filename to serve.\n" \
365 "dir=<DIRNAME> A directory containing files to serve.\n" \
366 "cache=<MODE> Set use of caching (default, none).\n" \
367 "fadise=<LEVEL> Set fadvise hint (normal, random, sequential).\n" \
369 /* Print some extra information about how the plugin was compiled. */
371 file_dump_plugin (void)
374 printf ("file_blksszget=yes\n");
377 printf ("file_blkzeroout=yes\n");
380 printf ("file_extents=yes\n");
382 #ifdef FALLOC_FL_PUNCH_HOLE
383 printf ("file_falloc_fl_punch_hole=yes\n");
385 #ifdef FALLOC_FL_ZERO_RANGE
386 printf ("file_falloc_fl_zero_range=yes\n");
390 /* Common code for listing exports of a directory. */
392 list_exports_of_directory (struct nbdkit_exports
*exports
, DIR *dir
)
394 struct dirent
*entry
;
397 while ((entry
= readdir (dir
)) != NULL
) {
401 #if HAVE_STRUCT_DIRENT_D_TYPE
402 if (entry
->d_type
== DT_BLK
|| entry
->d_type
== DT_REG
)
404 else if (entry
->d_type
!= DT_LNK
&& entry
->d_type
!= DT_UNKNOWN
)
407 /* TODO: when chasing symlinks, is statx any nicer than fstatat? */
408 if (r
== -1 && fstatat (dirfd (dir
), entry
->d_name
, &sb
, 0) == 0 &&
409 (S_ISREG (sb
.st_mode
) || S_ISBLK (sb
.st_mode
)))
411 if (r
== 1 && nbdkit_add_export (exports
, entry
->d_name
, NULL
) == -1)
417 nbdkit_error ("readdir: %m");
425 file_list_exports (int readonly
, int default_only
,
426 struct nbdkit_exports
*exports
)
428 /* We don't fork, so no need to worry about FD_CLOEXEC on the directory */
435 return nbdkit_add_export (exports
, "", NULL
);
438 dir
= opendir (directory
);
440 nbdkit_error ("opendir: %m");
443 r
= list_exports_of_directory (exports
, dir
);
448 dfd
= dup (filedesc
);
450 nbdkit_error ("dup: %m");
453 dir
= fdopendir (dfd
);
455 nbdkit_error ("fdopendir: %m");
458 r
= list_exports_of_directory (exports
, dir
);
459 closedir (dir
); /* also closes dfd */
466 /* The per-connection handle. */
469 bool is_block_device
;
478 /* Common code for opening a file by name, used by mode_filename and
479 * mode_directory only. If successful, sets h->fd and may adjust
483 open_file_by_name (struct handle
*h
, int readonly
, int dfd
, const char *file
)
487 assert (h
->fd
== -1);
489 flags
= O_CLOEXEC
|O_NOCTTY
;
495 h
->fd
= openat (dfd
, file
, flags
);
496 if (h
->fd
== -1 && !readonly
) {
497 nbdkit_debug ("open O_RDWR failed, falling back to read-only: %s: %m",
499 flags
= (flags
& ~O_ACCMODE
) | O_RDONLY
;
500 h
->fd
= openat (dfd
, file
, flags
);
501 h
->can_write
= false;
504 nbdkit_error ("open: %s: %m", file
);
512 /* Create the per-connection handle. */
514 file_open (int readonly
)
520 h
= malloc (sizeof *h
);
522 nbdkit_error ("malloc: %m");
525 h
->can_write
= !readonly
;
531 if (open_file_by_name (h
, readonly
, -1, file
) == -1) {
537 case mode_directory
: {
540 file
= nbdkit_export_name ();
541 if (strchr (file
, '/')) {
542 nbdkit_error ("exportname cannot contain /");
547 dfd
= open (directory
, O_RDONLY
| O_DIRECTORY
| O_CLOEXEC
);
549 nbdkit_error ("open %s: %m", directory
);
553 if (open_file_by_name (h
, readonly
, dfd
, file
) == -1) {
565 /* This is needed for error messages. */
566 file
= "<file descriptor>";
568 h
->fd
= dup (filedesc
);
570 nbdkit_error ("dup fd=%d: %m", filedesc
);
575 /* If the file descriptor is readonly then we should not advertise
576 * writes as they will fail later.
578 r
= fcntl (h
->fd
, F_GETFL
);
580 nbdkit_error ("fcntl: F_GETFL: %m");
587 h
->can_write
= false;
588 else if (r
== O_WRONLY
)
589 nbdkit_debug ("file descriptor is write-only (ie. not readable): "
590 "NBD protocol does not support this, but continuing "
598 file
= nbdkit_export_name ();
599 if (strchr (file
, '/')) {
600 nbdkit_error ("exportname cannot contain /");
605 /* We don't fork, so no need to worry about FD_CLOEXEC on the directory */
606 dfd
= dup (filedesc
);
608 nbdkit_error ("dup dirfd=%d: %m", filedesc
);
612 if (open_file_by_name (h
, readonly
, dfd
, file
) == -1) {
627 if (fstat (h
->fd
, &statbuf
) == -1) {
628 nbdkit_error ("fstat: %s: %m", file
);
634 if (fadvise_mode
!= -1) {
635 /* This is a hint so we ignore failures. */
636 #ifdef HAVE_POSIX_FADVISE
637 int r
= posix_fadvise (h
->fd
, 0, 0, fadvise_mode
);
639 nbdkit_debug ("posix_fadvise: %s: %m (ignored)", file
);
641 nbdkit_debug ("fadvise is not supported");
645 if (S_ISBLK (statbuf
.st_mode
))
646 h
->is_block_device
= true;
647 else if (S_ISREG (statbuf
.st_mode
))
648 h
->is_block_device
= false;
650 nbdkit_error ("file is not regular or block device: %s", file
);
655 h
->sector_size
= 4096; /* Start with safe guess */
658 if (h
->is_block_device
) {
659 if (ioctl (h
->fd
, BLKSSZGET
, &h
->sector_size
))
660 nbdkit_debug ("cannot get sector size: %s: %m", file
);
664 #ifdef FALLOC_FL_PUNCH_HOLE
665 h
->can_punch_hole
= true;
667 h
->can_punch_hole
= false;
670 #ifdef FALLOC_FL_ZERO_RANGE
671 h
->can_zero_range
= true;
673 h
->can_zero_range
= false;
676 h
->can_fallocate
= true;
677 h
->can_zeroout
= h
->is_block_device
;
682 /* Free up the per-connection handle. */
684 file_close (void *handle
)
686 struct handle
*h
= handle
;
689 remove_fd_from_window (h
->fd
);
695 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
697 /* For block devices, stat->st_size is not the true size. The caller
701 block_device_size (int fd
)
705 size
= lseek (fd
, 0, SEEK_END
);
707 nbdkit_error ("lseek (to find device size): %m");
714 /* Get the file size. */
716 file_get_size (void *handle
)
718 struct handle
*h
= handle
;
720 if (h
->is_block_device
) {
721 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lseek_lock
);
722 return block_device_size (h
->fd
);
727 if (fstat (h
->fd
, &statbuf
) == -1) {
728 nbdkit_error ("fstat: %m");
732 return statbuf
.st_size
;
736 /* Check if file is read-only. */
738 file_can_write (void *handle
)
740 struct handle
*h
= handle
;
745 /* Allow multiple parallel connections from a single client. */
747 file_can_multi_conn (void *handle
)
753 file_can_trim (void *handle
)
755 /* Trim is advisory, but we prefer to advertise it only when we can
756 * actually (attempt to) punch holes. Since not all filesystems
757 * support all fallocate modes, it would be nice if we had a way
758 * from fpathconf() to definitively learn what will work on a given
759 * fd for a more precise answer; oh well. */
760 #ifdef FALLOC_FL_PUNCH_HOLE
768 file_can_fua (void *handle
)
770 return NBDKIT_FUA_NATIVE
;
774 file_can_cache (void *handle
)
776 /* Prefer posix_fadvise(), but letting nbdkit call .pread on our
777 * behalf also tends to work well for the local file system
780 #if HAVE_POSIX_FADVISE
781 return NBDKIT_FUA_NATIVE
;
783 return NBDKIT_FUA_EMULATE
;
787 /* Flush the file to disk. */
789 file_flush (void *handle
, uint32_t flags
)
791 struct handle
*h
= handle
;
793 if (fdatasync (h
->fd
) == -1) {
794 nbdkit_error ("fdatasync: %m");
801 /* Read data from the file. */
803 file_pread (void *handle
, void *buf
, uint32_t count
, uint64_t offset
,
806 struct handle
*h
= handle
;
807 #if defined (HAVE_POSIX_FADVISE) && defined (POSIX_FADV_DONTNEED)
808 uint32_t orig_count
= count
;
809 uint64_t orig_offset
= offset
;
813 ssize_t r
= pread (h
->fd
, buf
, count
, offset
);
815 nbdkit_error ("pread: %m");
819 nbdkit_error ("pread: unexpected end of file");
827 #if defined (HAVE_POSIX_FADVISE) && defined (POSIX_FADV_DONTNEED)
828 /* On Linux this will evict the pages we just read from the page cache. */
829 if (cache_mode
== cache_none
)
830 posix_fadvise (h
->fd
, orig_offset
, orig_count
, POSIX_FADV_DONTNEED
);
836 /* Write data to the file. */
838 file_pwrite (void *handle
, const void *buf
, uint32_t count
, uint64_t offset
,
841 struct handle
*h
= handle
;
844 uint32_t orig_count
= count
;
845 uint64_t orig_offset
= offset
;
849 ssize_t r
= pwrite (h
->fd
, buf
, count
, offset
);
851 nbdkit_error ("pwrite: %m");
859 if ((flags
& NBDKIT_FLAG_FUA
) && file_flush (handle
, 0) == -1)
863 if (cache_mode
== cache_none
)
864 evict_writes (h
->fd
, orig_offset
, orig_count
);
870 #if defined (FALLOC_FL_PUNCH_HOLE) || defined (FALLOC_FL_ZERO_RANGE)
872 do_fallocate (int fd
, int mode_
, off_t offset
, off_t len
)
874 int r
= fallocate (fd
, mode_
, offset
, len
);
875 if (r
== -1 && errno
== ENODEV
) {
876 /* kernel 3.10 fails with ENODEV for block device. Kernel >= 4.9 fails
877 with EOPNOTSUPP in this case. Normalize errno to simplify callers. */
884 /* Write zeroes to the file. */
886 file_zero (void *handle
, uint32_t count
, uint64_t offset
, uint32_t flags
)
888 struct handle
*h
__attribute__((unused
)) = handle
;
890 #ifdef FALLOC_FL_PUNCH_HOLE
891 if (h
->can_punch_hole
&& (flags
& NBDKIT_FLAG_MAY_TRIM
)) {
894 r
= do_fallocate (h
->fd
, FALLOC_FL_PUNCH_HOLE
| FALLOC_FL_KEEP_SIZE
,
898 nbdkit_debug ("h->can_punch_hole && may_trim: "
899 "zero succeeded using fallocate");
903 if (!is_enotsup (errno
)) {
904 nbdkit_error ("zero: %m");
908 h
->can_punch_hole
= false;
912 #ifdef FALLOC_FL_ZERO_RANGE
913 if (h
->can_zero_range
) {
916 r
= do_fallocate (h
->fd
, FALLOC_FL_ZERO_RANGE
, offset
, count
);
919 nbdkit_debug ("h->can_zero-range: "
920 "zero succeeded using fallocate");
924 if (!is_enotsup (errno
)) {
925 nbdkit_error ("zero: %m");
929 h
->can_zero_range
= false;
933 #ifdef FALLOC_FL_PUNCH_HOLE
934 /* If we can punch hole but may not trim, we can combine punching hole and
935 * fallocate to zero a range. This is expected to be more efficient than
936 * writing zeroes manually. */
937 if (h
->can_punch_hole
&& h
->can_fallocate
) {
940 r
= do_fallocate (h
->fd
, FALLOC_FL_PUNCH_HOLE
| FALLOC_FL_KEEP_SIZE
,
943 r
= do_fallocate (h
->fd
, 0, offset
, count
);
946 nbdkit_debug ("h->can_punch_hole && h->can_fallocate: "
947 "zero succeeded using fallocate");
951 if (!is_enotsup (errno
)) {
952 nbdkit_error ("zero: %m");
956 h
->can_fallocate
= false;
958 if (!is_enotsup (errno
)) {
959 nbdkit_error ("zero: %m");
963 h
->can_punch_hole
= false;
969 /* For aligned range and block device, we can use BLKZEROOUT. */
970 if (h
->can_zeroout
&& IS_ALIGNED (offset
| count
, h
->sector_size
)) {
972 uint64_t range
[2] = {offset
, count
};
974 r
= ioctl (h
->fd
, BLKZEROOUT
, &range
);
977 nbdkit_debug ("h->can_zeroout && IS_ALIGNED: "
978 "zero succeeded using BLKZEROOUT");
982 if (errno
!= ENOTTY
) {
983 nbdkit_error ("zero: %m");
987 h
->can_zeroout
= false;
991 /* Trigger a fall back to writing */
993 nbdkit_debug ("zero falling back to writing");
998 __attribute__((unused
))
1001 if ((flags
& NBDKIT_FLAG_FUA
) && file_flush (handle
, 0) == -1)
1006 /* Punch a hole in the file. */
1008 file_trim (void *handle
, uint32_t count
, uint64_t offset
, uint32_t flags
)
1010 #ifdef FALLOC_FL_PUNCH_HOLE
1011 struct handle
*h
= handle
;
1014 if (h
->can_punch_hole
) {
1015 r
= do_fallocate (h
->fd
, FALLOC_FL_PUNCH_HOLE
| FALLOC_FL_KEEP_SIZE
,
1018 /* Trim is advisory; we don't care if it fails for anything other
1019 * than EIO or EPERM. */
1020 if (errno
== EPERM
|| errno
== EIO
) {
1021 nbdkit_error ("fallocate: %m");
1025 if (is_enotsup (EOPNOTSUPP
))
1026 h
->can_punch_hole
= false;
1028 nbdkit_debug ("ignoring failed fallocate during trim: %m");
1033 if ((flags
& NBDKIT_FLAG_FUA
) && file_flush (handle
, 0) == -1)
1043 file_can_extents (void *handle
)
1045 struct handle
*h
= handle
;
1048 /* A simple test to see whether SEEK_HOLE etc is likely to work on
1049 * the current filesystem.
1051 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lseek_lock
);
1052 r
= lseek (h
->fd
, 0, SEEK_HOLE
);
1054 nbdkit_debug ("extents disabled: lseek: SEEK_HOLE: %m");
1061 do_extents (void *handle
, uint32_t count
, uint64_t offset
,
1062 uint32_t flags
, struct nbdkit_extents
*extents
)
1064 struct handle
*h
= handle
;
1065 const bool req_one
= flags
& NBDKIT_FLAG_REQ_ONE
;
1066 uint64_t end
= offset
+ count
;
1071 pos
= lseek (h
->fd
, offset
, SEEK_DATA
);
1073 if (errno
== ENXIO
) {
1074 /* The current man page does not describe this situation well,
1075 * but a proposed change to POSIX adds these words for ENXIO:
1076 * "or the whence argument is SEEK_DATA and the offset falls
1077 * within the final hole of the file."
1082 nbdkit_error ("lseek: SEEK_DATA: %" PRIu64
": %m", offset
);
1087 /* We know there is a hole from offset to pos-1. */
1089 if (nbdkit_add_extent (extents
, offset
, pos
- offset
,
1090 NBDKIT_EXTENT_HOLE
| NBDKIT_EXTENT_ZERO
) == -1)
1100 pos
= lseek (h
->fd
, offset
, SEEK_HOLE
);
1102 nbdkit_error ("lseek: SEEK_HOLE: %" PRIu64
": %m", offset
);
1106 /* We know there is data from offset to pos-1. */
1108 if (nbdkit_add_extent (extents
, offset
, pos
- offset
,
1109 0 /* allocated data */) == -1)
1116 } while (offset
< end
);
1122 file_extents (void *handle
, uint32_t count
, uint64_t offset
,
1123 uint32_t flags
, struct nbdkit_extents
*extents
)
1125 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lseek_lock
);
1126 return do_extents (handle
, count
, offset
, flags
, extents
);
1128 #endif /* SEEK_HOLE */
1130 #if HAVE_POSIX_FADVISE
1133 file_cache (void *handle
, uint32_t count
, uint64_t offset
, uint32_t flags
)
1135 struct handle
*h
= handle
;
1138 /* Cache is advisory, we don't care if this fails */
1139 r
= posix_fadvise (h
->fd
, offset
, count
, POSIX_FADV_WILLNEED
);
1142 nbdkit_error ("posix_fadvise: %m");
1147 #endif /* HAVE_POSIX_FADVISE */
1149 static struct nbdkit_plugin plugin
= {
1151 .longname
= "nbdkit file plugin",
1152 .version
= PACKAGE_VERSION
,
1153 .unload
= file_unload
,
1154 .config
= file_config
,
1155 .config_complete
= file_config_complete
,
1156 .config_help
= file_config_help
,
1157 .magic_config_key
= "file",
1158 .dump_plugin
= file_dump_plugin
,
1159 .list_exports
= file_list_exports
,
1161 .close
= file_close
,
1162 .get_size
= file_get_size
,
1163 .can_write
= file_can_write
,
1164 .can_multi_conn
= file_can_multi_conn
,
1165 .can_trim
= file_can_trim
,
1166 .can_fua
= file_can_fua
,
1167 .can_cache
= file_can_cache
,
1168 .pread
= file_pread
,
1169 .pwrite
= file_pwrite
,
1170 .flush
= file_flush
,
1174 .can_extents
= file_can_extents
,
1175 .extents
= file_extents
,
1177 #if HAVE_POSIX_FADVISE
1178 .cache
= file_cache
,
1180 .errno_is_preserved
= 1,
1183 NBDKIT_REGISTER_PLUGIN(plugin
)