--- /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: + + +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 ]]) + 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 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 . */ + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +# include +# include +#else +# include +#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