diff --git a/cmdline/stream.c b/cmdline/stream.c index 59c9df55..83116599 100644 --- a/cmdline/stream.c +++ b/cmdline/stream.c @@ -49,7 +49,7 @@ STREAM* sopen_read(const char* file, int flags) #if HAVE_POSIX_FADVISE if (s->flags & STREAM_FLAGS_SEQUENTIAL) { /* advise sequential access */ - ret = posix_fadvise(s->handle[0].f, 0, 0, POSIX_FADV_SEQUENTIAL); + ret = posix_fadvise_wrapper(s->handle[0].f, 0, 0, POSIX_FADV_SEQUENTIAL); if (ret == ENOSYS) { log_error(errno, "WARNING! fadvise() is not supported in this platform. Performance may not be optimal!\n"); /* call is not supported, like in armhf, see posix_fadvise manpage */ @@ -137,7 +137,7 @@ int sopen_multi_file(STREAM* s, unsigned i, const char* file) #if HAVE_POSIX_FADVISE if (s->flags & STREAM_FLAGS_SEQUENTIAL) { /* advise sequential access */ - ret = posix_fadvise(f, 0, 0, POSIX_FADV_SEQUENTIAL); + ret = posix_fadvise_wrapper(f, 0, 0, POSIX_FADV_SEQUENTIAL); if (ret == ENOSYS) { /* call is not supported, like in armhf, see posix_fadvise manpage */ ret = 0; diff --git a/cmdline/support.c b/cmdline/support.c index dc75f2de..33ad516a 100644 --- a/cmdline/support.c +++ b/cmdline/support.c @@ -1357,7 +1357,7 @@ int advise_open(struct advise_struct* advise, int f) /* advise noreuse access, this avoids to pollute the page cache */ /* supported from Linux Kernel 6.3 with this commit: https://github.com/torvalds/linux/commit/17e810229cb3068b692fa078bd9b3a6527e0866a */ - ret = posix_fadvise(f, 0, 0, POSIX_FADV_NOREUSE); + ret = posix_fadvise_wrapper(f, 0, 0, POSIX_FADV_NOREUSE); if (ret == ENOSYS) { /* call is not supported */ ret = 0; @@ -1370,7 +1370,7 @@ int advise_open(struct advise_struct* advise, int f) } /* advise sequential access, this doubles the read-ahead window size */ - ret = posix_fadvise(f, 0, 0, POSIX_FADV_SEQUENTIAL); + ret = posix_fadvise_wrapper(f, 0, 0, POSIX_FADV_SEQUENTIAL); if (ret == ENOSYS) { /* call is not supported, like in armhf, see posix_fadvise manpage */ ret = 0; @@ -1550,8 +1550,11 @@ int advise_write(struct advise_struct* advise, int f, data_off_t offset, data_of } /* flush the data from the cache */ - ret = posix_fadvise(f, discard_offset, discard_size, POSIX_FADV_DONTNEED); - /* for POSIX_FADV_DONTNEED we don't allow failure with ENOSYS */ + ret = posix_fadvise_wrapper(f, discard_offset, discard_size, POSIX_FADV_DONTNEED); + if (ret == ENOSYS) { + /* call is not supported */ + ret = 0; + } if (ret != 0) { /* LCOV_EXCL_START */ errno = ret; /* posix_fadvise return the error code */ @@ -1578,8 +1581,11 @@ int advise_read(struct advise_struct* advise, int f, data_off_t offset, data_off int ret; /* flush the data from the cache */ - ret = posix_fadvise(f, offset, size, POSIX_FADV_DONTNEED); - /* for POSIX_FADV_DONTNEED we don't allow failure with ENOSYS */ + ret = posix_fadvise_wrapper(f, offset, size, POSIX_FADV_DONTNEED); + if (ret == ENOSYS) { + /* call is not supported */ + ret = 0; + } if (ret != 0) { /* LCOV_EXCL_START */ errno = ret; /* posix_fadvise return the error code */ diff --git a/cmdline/unix.c b/cmdline/unix.c index d6920381..ba17720d 100644 --- a/cmdline/unix.c +++ b/cmdline/unix.c @@ -14,6 +14,63 @@ int exit_success = 0; int exit_failure = 1; int exit_sync_needed = 2; +#if HAVE_POSIX_FADVISE +/** + * Wrapper around posix_fadvise() that handles ENOENT by converting it to + * ENOSYS. + * + * Why can posix_fadvise() return ENOENT? + * + * According to POSIX and the Linux kernel's standard fadvise64 implementation + * (mm/fadvise.c), posix_fadvise() should only return EBADF (bad file + * descriptor), ESPIPE (fd refers to a pipe or FIFO), or EINVAL (invalid + * advice value or arguments). ENOENT is never generated by the kernel's + * generic path. + * + * However, the VFS layer allows individual filesystems to provide their own + * .fadvise() method, which is called instead of (or in addition to) the + * generic implementation. Certain filesystem drivers can return ENOENT in + * this method for reasons that are completely unrelated to the file not + * existing (the file is clearly open, we have a valid fd): + * + * - NFS: A stale NFS inode can be revalidated against the server while + * fadvise is being processed. If the server-side file has been removed or + * renamed, the revalidation discovers the inode is gone and returns + * -ENOENT, even though the client still holds an open file descriptor. + * + * - FUSE-based filesystems (mergerfs, sshfs, rclone, etc.): The FUSE daemon + * receives the fadvise request and may forward it to an underlying backend. + * If that backend cannot find the file (e.g., the backing path has changed, + * the FUSE daemon is partially initialised, or the daemon translates the + * request and the target layer returns an error), the daemon may return + * ENOENT to the kernel. + * + * - Overlayfs (used by Docker, Podman, LXC): The overlayfs .fadvise() + * implementation delegates to the lower or upper layer. If the lookup + * through the overlay stack fails to find the real inode (e.g., the upper + * directory entry is absent and the lower copy is in a weird state), the + * delegation can return ENOENT. + * + * - Other network or virtual filesystems: Any out-of-tree or in-tree + * filesystem that implements .fadvise() and performs a path or inode + * lookup as part of processing the hint can propagate ENOENT if that + * internal lookup fails. + * + * Because posix_fadvise() is always an advisory hint, it never affects + * correctness, only performance, it is safe to treat unexpected errors, + * including ENOENT, as "not supported" (ENOSYS) and continue. + */ +int posix_fadvise_wrapper(int fd, off_t offset, off_t len, int advice) +{ + int ret = posix_fadvise(fd, offset, len, advice); + + if (ret == ENOENT) + return ENOSYS; + + return ret; +} +#endif + int open_noatime(const char* file, int flags) { #ifdef O_NOATIME @@ -2054,7 +2111,11 @@ static int devup(dev_t device, const char* name) #if HAVE_POSIX_FADVISE /* clear cache */ - ret = posix_fadvise(f, offset, 4096, POSIX_FADV_DONTNEED); + ret = posix_fadvise_wrapper(f, offset, 4096, POSIX_FADV_DONTNEED); + if (ret == ENOSYS) { + /* call is not supported */ + ret = 0; + } if (ret != 0) { /* LCOV_EXCL_START */ close(f); diff --git a/cmdline/unix.h b/cmdline/unix.h index 6d8d3d0c..caadde30 100644 --- a/cmdline/unix.h +++ b/cmdline/unix.h @@ -12,6 +12,11 @@ #define O_BINARY 0 /**< Not used in Unix. */ #define O_SEQUENTIAL 0 /**< In Unix posix_fadvise() shall be used. */ + +#if HAVE_POSIX_FADVISE +int posix_fadvise_wrapper(int fd, off_t offset, off_t len, int advice); +#endif + /** * If nanoseconds are not supported, we report the special STAT_NSEC_INVALID value, * to mark that it's undefined.