tools/gnulib: add fallocate-posix module
authorMichael Pratt <mcpratt@pm.me>
Mon, 10 Jun 2024 08:04:41 +0000 (04:04 -0400)
committerRobert Marko <robimarko@gmail.com>
Wed, 19 Jun 2024 09:22:13 +0000 (11:22 +0200)
Add a module to gnulib to support posix_fallocate()
for macOS and other systems that are missing it.

Apple-specific code is sourced from Mozilla,
and the rest from glibc, both licensed under LGPL.

Signed-off-by: Michael Pratt <mcpratt@pm.me>
Link: https://github.com/openwrt/openwrt/pull/15690
Signed-off-by: Robert Marko <robimarko@gmail.com>
tools/gnulib/patches/320-modules-fallocate-posix.patch [new file with mode: 0644]

diff --git a/tools/gnulib/patches/320-modules-fallocate-posix.patch b/tools/gnulib/patches/320-modules-fallocate-posix.patch
new file mode 100644 (file)
index 0000000..e30a717
--- /dev/null
@@ -0,0 +1,326 @@
+--- /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