// SPDX-License-Identifier: GPL-3.0-or-later // Copyright (C) 2026 Andrea Mazzoleni #ifndef __UNIX_H #define __UNIX_H #ifdef __linux__ #define HAVE_LINUX_DEVICE 1 /**< In Linux enables special device support. */ #define HAVE_DIRECT_IO 1 /**< Support O_DIRECT in open(). */ #include #endif #define SYSLOG "syslog" #define OS_LVL_CRITICAL LOG_CRIT #define OS_LVL_ERROR LOG_ERR #define OS_LVL_WARNING LOG_WARNING #define OS_LVL_INFO LOG_INFO #define OS_LVL_DEBUG LOG_DEBUG /* implement close_range for glibc 2.33 or earlier */ #if defined(__linux__) && !defined(HAVE_CLOSE_RANGE) #ifndef __NR_close_range #define __NR_close_range 436 #endif #define close_range close_range_impl static inline int close_range_impl(unsigned int first, unsigned int last, unsigned int flags) { return (int)syscall(__NR_close_range, first, last, flags); } #define HAVE_CLOSE_RANGE 1 #endif #ifndef WEXITSTATUS #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif #ifndef WIFEXITED #define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif #define FOPEN_TEXT "" #define FOPEN_BINARY "" #define O_BINARY 0 /**< Not used in Unix. */ #define O_SEQUENTIAL 0 /**< In Unix posix_fadvise() shall be used. */ /** * If nanoseconds are not supported, we report the special STAT_NSEC_INVALID value, * to mark that it's undefined. */ #define STAT_NSEC_INVALID -1 /* Check if we have nanoseconds support */ #if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC #define STAT_NSEC(st) ((int)(st)->st_mtim.tv_nsec) /* Linux */ #elif HAVE_STRUCT_STAT_ST_MTIMENSEC #define STAT_NSEC(st) ((int)(st)->st_mtimensec) /* NetBSD */ #elif HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC #define STAT_NSEC(st) ((int)(st)->st_mtimespec.tv_nsec) /* FreeBSD, Mac OS X */ #else #define STAT_NSEC(st) STAT_NSEC_INVALID #endif #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); #endif #if !HAVE_EACCESS /** * Check effective user's permissions for a file. * Conceptually identical to access(), but uses the effective UID/GID. */ int eaccess(const char* pathname, int mode); #endif /** * Open a file with the O_NOATIME flag to avoid to update the access time. */ int open_noatime(const char* file, int flags); /** * Check if the specified file is hidden. */ int dirent_hidden(struct dirent* dd); /** * Return a description of the file type. */ const char* stat_desc(struct stat* st); /** * Return the alignment requirement for direct IO. */ size_t direct_size(void); /****************************************************************************/ /* thread */ #if HAVE_PTHREAD_H #include #endif #if HAVE_PTHREAD #define HAVE_THREAD 1 typedef pthread_t thread_id_t; typedef pthread_mutex_t thread_mutex_t; typedef pthread_cond_t thread_cond_t; typedef pthread_key_t thread_key_t; typedef pthread_rwlock_t thread_rwlock_t; #endif #endif