| --- /dev/null |
| +++ b/modules/fallocate-posix |
| @@ -0,0 +1,43 @@ |
| +Description: |
| +posix_fallocate function that is glibc compatible. |
| + |
| +Files: |
| +lib/posix_fallocate.c |
| +m4/fcntl_h.m4 |
| +m4/posix_fallocate.m4 |
| + |
| +Depends-on: |
| +errno [test $HAVE_FALLOCATE_POSIX = 0 || test $REPLACE_FALLOCATE_POSIX = 1] |
| +fcntl [test $HAVE_FALLOCATE_POSIX = 0 || test $REPLACE_FALLOCATE_POSIX = 1] |
| +fstat [test $HAVE_FALLOCATE_POSIX = 0 || test $REPLACE_FALLOCATE_POSIX = 1] |
| +ftruncate [test $HAVE_FALLOCATE_POSIX = 0 || test $REPLACE_FALLOCATE_POSIX = 1] |
| +pread [test $HAVE_FALLOCATE_POSIX = 0 || test $REPLACE_FALLOCATE_POSIX = 1] |
| +pwrite [test $HAVE_FALLOCATE_POSIX = 0 || test $REPLACE_FALLOCATE_POSIX = 1] |
| +stdint [test $HAVE_FALLOCATE_POSIX = 0 || test $REPLACE_FALLOCATE_POSIX = 1] |
| +sys_stat [test $HAVE_FALLOCATE_POSIX = 0 || test $REPLACE_FALLOCATE_POSIX = 1] |
| +unistd [test $HAVE_FALLOCATE_POSIX = 0 || test $REPLACE_FALLOCATE_POSIX = 1] |
| +fcntl-h |
| + |
| +configure.ac: |
| +gl_FUNC_POSIX_FALLOCATE |
| +gl_CONDITIONAL([GL_COND_OBJ_POSIX_FALLOCATE], |
| + [test $HAVE_FALLOCATE_POSIX = 0 || test $REPLACE_FALLOCATE_POSIX = 1]) |
| +AM_COND_IF([GL_COND_OBJ_POSIX_FALLOCATE], [ |
| + gl_PREREQ_POSIX_FALLOCATE |
| +]) |
| +gl_MODULE_INDICATOR([fallocate-posix]) |
| +gl_FCNTL_MODULE_INDICATOR([fallocate-posix]) |
| + |
| +Makefile.am: |
| +if GL_COND_OBJ_POSIX_FALLOCATE |
| +lib_SOURCES += posix_fallocate.c |
| +endif |
| + |
| +Include: |
| +<fcntl.h> |
| + |
| +License: |
| +LGPLv2+ |
| + |
| +Maintainer: |
| +all |
| --- /dev/null |
| +++ b/m4/posix_fallocate.m4 |
| @@ -0,0 +1,20 @@ |
| +# posix_fallocate.m4 serial 1 |
| +dnl Copyright (C) 2024 Free Software Foundation, Inc. |
| +dnl This file is free software; the Free Software Foundation |
| +dnl gives unlimited permission to copy and/or distribute it, |
| +dnl with or without modifications, as long as this notice is preserved. |
| + |
| +AC_DEFUN([gl_FUNC_POSIX_FALLOCATE], |
| +[ |
| + AC_REQUIRE([gl_FCNTL_H_DEFAULTS]) |
| + gl_CHECK_FUNCS_ANDROID([posix_fallocate], [[#include <fcntl.h>]]) |
| + if test "$ac_cv_func_posix_fallocate" = no; then |
| + HAVE_FALLOCATE_POSIX=0 |
| + case "$gl_cv_onwards_func_posix_fallocate" in |
| + future*) REPLACE_FALLOCATE_POSIX=1 ;; |
| + esac |
| + fi |
| +]) |
| + |
| +# Prerequisites of lib/posix_fallocate.c. |
| +AC_DEFUN([gl_PREREQ_POSIX_FALLOCATE], [:]) |
| --- a/m4/fcntl_h.m4 |
| +++ b/m4/fcntl_h.m4 |
| @@ -23,7 +23,7 @@ AC_DEFUN_ONCE([gl_FCNTL_H], |
| dnl corresponding gnulib module is not in use, if it is not common |
| dnl enough to be declared everywhere. |
| gl_WARN_ON_USE_PREPARE([[#include <fcntl.h> |
| - ]], [fcntl openat]) |
| + ]], [fcntl openat posix_fallocate]) |
| ]) |
| |
| # gl_FCNTL_MODULE_INDICATOR([modulename]) |
| @@ -50,6 +50,7 @@ AC_DEFUN([gl_FCNTL_H_REQUIRE_DEFAULTS], |
| gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_NONBLOCKING]) |
| gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_OPEN]) |
| gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_OPENAT]) |
| + gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FALLOCATE_POSIX]) |
| dnl Support Microsoft deprecated alias function names by default. |
| gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MDA_CREAT], [1]) |
| gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MDA_OPEN], [1]) |
| @@ -61,10 +62,12 @@ AC_DEFUN([gl_FCNTL_H_REQUIRE_DEFAULTS], |
| AC_DEFUN([gl_FCNTL_H_DEFAULTS], |
| [ |
| dnl Assume proper GNU behavior unless another module says otherwise. |
| - HAVE_FCNTL=1; AC_SUBST([HAVE_FCNTL]) |
| - HAVE_OPENAT=1; AC_SUBST([HAVE_OPENAT]) |
| - REPLACE_CREAT=0; AC_SUBST([REPLACE_CREAT]) |
| - REPLACE_FCNTL=0; AC_SUBST([REPLACE_FCNTL]) |
| - REPLACE_OPEN=0; AC_SUBST([REPLACE_OPEN]) |
| - REPLACE_OPENAT=0; AC_SUBST([REPLACE_OPENAT]) |
| + HAVE_FCNTL=1; AC_SUBST([HAVE_FCNTL]) |
| + HAVE_OPENAT=1; AC_SUBST([HAVE_OPENAT]) |
| + HAVE_FALLOCATE_POSIX=1; AC_SUBST([HAVE_FALLOCATE_POSIX]) |
| + REPLACE_CREAT=0; AC_SUBST([REPLACE_CREAT]) |
| + REPLACE_FCNTL=0; AC_SUBST([REPLACE_FCNTL]) |
| + REPLACE_OPEN=0; AC_SUBST([REPLACE_OPEN]) |
| + REPLACE_OPENAT=0; AC_SUBST([REPLACE_OPENAT]) |
| + REPLACE_FALLOCATE_POSIX=0; AC_SUBST([REPLACE_FALLOCATE_POSIX]) |
| ]) |
| --- a/modules/fcntl-h |
| +++ b/modules/fcntl-h |
| @@ -40,14 +40,17 @@ fcntl.h: fcntl.in.h $(top_builddir)/conf |
| -e 's/@''GNULIB_NONBLOCKING''@/$(GNULIB_NONBLOCKING)/g' \ |
| -e 's/@''GNULIB_OPEN''@/$(GNULIB_OPEN)/g' \ |
| -e 's/@''GNULIB_OPENAT''@/$(GNULIB_OPENAT)/g' \ |
| + -e 's/@''GNULIB_FALLOCATE_POSIX''@/$(GNULIB_FALLOCATE_POSIX)/g' \ |
| -e 's/@''GNULIB_MDA_CREAT''@/$(GNULIB_MDA_CREAT)/g' \ |
| -e 's/@''GNULIB_MDA_OPEN''@/$(GNULIB_MDA_OPEN)/g' \ |
| -e 's|@''HAVE_FCNTL''@|$(HAVE_FCNTL)|g' \ |
| -e 's|@''HAVE_OPENAT''@|$(HAVE_OPENAT)|g' \ |
| + -e 's|@''HAVE_FALLOCATE_POSIX''@|$(HAVE_FALLOCATE_POSIX)|g' \ |
| -e 's|@''REPLACE_CREAT''@|$(REPLACE_CREAT)|g' \ |
| -e 's|@''REPLACE_FCNTL''@|$(REPLACE_FCNTL)|g' \ |
| -e 's|@''REPLACE_OPEN''@|$(REPLACE_OPEN)|g' \ |
| -e 's|@''REPLACE_OPENAT''@|$(REPLACE_OPENAT)|g' \ |
| + -e 's|@''REPLACE_FALLOCATE_POSIX''@|$(REPLACE_FALLOCATE_POSIX)|g' \ |
| -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \ |
| -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \ |
| -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \ |
| --- a/lib/fcntl.in.h |
| +++ b/lib/fcntl.in.h |
| @@ -238,6 +238,33 @@ _GL_WARN_ON_USE (openat, "openat is not |
| # endif |
| #endif |
| |
| +#if @GNULIB_FALLOCATE_POSIX@ |
| +# if @REPLACE_FALLOCATE_POSIX@ |
| +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) |
| +# undef posix_fallocate |
| +# define posix_fallocate rpl_posix_fallocate |
| +# endif |
| +_GL_FUNCDECL_RPL (posix_fallocate, int, |
| + (int fd, off_t offset, off_t len)); |
| +_GL_CXXALIAS_RPL (posix_fallocate, int, |
| + (int fd, off_t offset, off_t len)); |
| +# else |
| +# if !@HAVE_FALLOCATE_POSIX@ |
| +_GL_FUNCDECL_SYS (posix_fallocate, int, |
| + (int fd, off_t offset, off_t len)); |
| +# endif |
| +_GL_CXXALIAS_SYS (posix_fallocate, int, |
| + (int fd, off_t offset, off_t len)); |
| +# endif |
| +_GL_CXXALIASWARN (posix_fallocate); |
| +#elif defined GNULIB_POSIXCHECK |
| +# undef posix_fallocate |
| +# if HAVE_RAW_DECL_POSIX_FALLOCATE |
| +_GL_WARN_ON_USE (posix_fallocate, "posix_fallocate is not portable - " |
| + "use gnulib module fallocate-posix for portability"); |
| +# endif |
| +#endif |
| + |
| |
| /* Fix up the FD_* macros, only known to be missing on mingw. */ |
| |
| --- /dev/null |
| +++ b/lib/posix_fallocate.c |
| @@ -0,0 +1,150 @@ |
| +/* posix_fallocate function that is glibc compatible. |
| + |
| + Copyright (C) 2024 Free Software Foundation, Inc. |
| + |
| + This file is free software: you can redistribute it and/or modify |
| + it under the terms of the GNU Lesser General Public License as |
| + published by the Free Software Foundation; either version 2.1 of the |
| + License, or (at your option) any later version. |
| + |
| + This file is distributed in the hope that it will be useful, |
| + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + GNU Lesser General Public License for more details. |
| + |
| + You should have received a copy of the GNU Lesser General Public License |
| + along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
| + |
| +#include <config.h> |
| + |
| +#include <errno.h> |
| +#include <fcntl.h> |
| +#include <unistd.h> |
| +#include <stdint.h> |
| +#include <sys/fcntl.h> |
| +#include <sys/stat.h> |
| + |
| +#ifdef __APPLE__ |
| +# include <sys/param.h> |
| +# include <sys/mount.h> |
| +#else |
| +# include <sys/statfs.h> |
| +#endif |
| + |
| +/* Reserve storage for the data of the file associated with FD. This |
| + emulation is far from perfect, but the kernel cannot do not much |
| + better for network file systems, either. */ |
| + |
| +int |
| +posix_fallocate (int fd, off_t offset, off_t len) |
| +{ |
| + int ret; |
| + struct stat st; |
| + |
| + if (fd < 0 || offset < 0 || len < 0) |
| + return EINVAL; |
| + |
| + /* Perform overflow check. The outer cast relies on a GCC |
| + extension. */ |
| + if ((off_t) ((uint64_t) offset + (uint64_t) len) < 0) |
| + return EFBIG; |
| + |
| + /* pwrite below will not do the right thing in O_APPEND mode. */ |
| + { |
| + int flags = fcntl (fd, F_GETFL, 0); |
| + if (flags < 0 || (flags & O_APPEND) != 0) |
| + return EBADF; |
| + } |
| + |
| + /* We have to make sure that this is really a regular file. */ |
| + if (fstat (fd, &st) != 0) |
| + return EBADF; |
| + if (S_ISFIFO (st.st_mode)) |
| + return ESPIPE; |
| + if (! S_ISREG (st.st_mode)) |
| + return ENODEV; |
| + |
| + if (len == 0) |
| + { |
| + /* This is racy, but there is no good way to satisfy a |
| + zero-length allocation request. */ |
| + if (st.st_size < offset) |
| + { |
| + ret = ftruncate (fd, offset); |
| + |
| + if (ret != 0) |
| + ret = errno; |
| + return ret; |
| + } |
| + return ret; |
| + } |
| + |
| +#ifdef __APPLE__ |
| + fstore_t sto = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, offset + len, 0}; |
| + /* allocate continuous */ |
| + ret = fcntl (fd, F_PREALLOCATE, &sto); |
| + if (ret < 0) |
| + { |
| + /* allocate non-continuous */ |
| + sto.fst_flags = F_ALLOCATEALL; |
| + ret = fcntl (fd, F_PREALLOCATE, &sto); |
| + if (ret < 0) |
| + { |
| + return ret; |
| + } |
| + } |
| + ret = ftruncate(fd, offset + len); |
| +#else |
| + |
| + /* Minimize data transfer for network file systems, by issuing |
| + single-byte write requests spaced by the file system block size. |
| + (Most local file systems have fallocate support, so this fallback |
| + code is not used there.) */ |
| + |
| + unsigned increment; |
| + { |
| + struct statfs f; |
| + |
| + if (fstatfs (fd, &f) != 0) |
| + return errno; |
| + if (f.f_bsize == 0) |
| + increment = 512; |
| + else if (f.f_bsize < 4096) |
| + increment = f.f_bsize; |
| + else |
| + /* NFS does not propagate the block size of the underlying |
| + storage and may report a much larger value which would still |
| + leave holes after the loop below, so we cap the increment at |
| + 4096. */ |
| + increment = 4096; |
| + } |
| + |
| + /* Write a null byte to every block. This is racy; we currently |
| + lack a better option. Compare-and-swap against a file mapping |
| + might additional local races, but requires interposition of a |
| + signal handler to catch SIGBUS. */ |
| + for (offset += (len - 1) % increment; len > 0; offset += increment) |
| + { |
| + len -= increment; |
| + |
| + if (offset < st.st_size) |
| + { |
| + unsigned char c; |
| + ssize_t rsize = pread (fd, &c, 1, offset); |
| + |
| + if (rsize < 0) |
| + return errno; |
| + /* If there is a non-zero byte, the block must have been |
| + allocated already. */ |
| + else if (rsize == 1 && c != 0) |
| + continue; |
| + } |
| + |
| + if (pwrite (fd, "", 1, offset) != 1) |
| + return errno; |
| + } |
| + |
| +#endif /* __APPLE__ */ |
| + |
| + return ret; |
| +} |
| --- a/MODULES.html.sh |
| +++ b/MODULES.html.sh |
| @@ -2552,6 +2552,7 @@ func_all_modules () |
| func_module execve |
| func_module execvp |
| func_module execvpe |
| + func_module fallocate-posix |
| func_module fchdir |
| func_module fclose |
| func_module fcntl-h |