On Sat, Jun 27, 2026 at 5:53 PM Cedric Blancher via Cygwin
<[email protected]> wrote:
[CC:'ing Takashi Yano <[email protected]> to have a look]
> The code works for me, but we need this in Cygwin, and not an addon.

I know, I know...

I've attached a prototype patch as
"cygwin370_fcntl_allocsp_freesp20260630_002.diff.txt" ...
... AFAIK the only issue is whether it is OK to call
|fstat()|/|ftruncate()|/|fallocate()|/|lseek()| from within the
wrapper, or whether there is a better way... help, please...

----

Nye,
Roland
-- 
  __ .  . __
 (o.\ \/ /.o) [email protected]
  \__\/\/__/  MPEG specialist, C&&JAVA&&Sun&&Unix programmer
  /O /==\ O\  TEL +49 641 3992797
 (;O/ \/ \O;)
diff --git a/newlib/libc/include/sys/_default_fcntl.h 
b/newlib/libc/include/sys/_default_fcntl.h
index b3222d452..c8f0bb1f7 100644
--- a/newlib/libc/include/sys/_default_fcntl.h
+++ b/newlib/libc/include/sys/_default_fcntl.h
@@ -155,6 +155,17 @@ extern "C" {
 #define        F_OFD_SETLKW    17      /* Set or Clear OFD lock (Blocking) */
 #endif
 
+/*
+ * { Solaris, FreeBSD, SUPER/UX, ... }-style file space allocation/freeing.
+ *
+ * |F_ALLOCSP|/|F_FREESP| take |struct flock *|.
+ *
+ * |l_len| == 0 means "to EOF".  Negative |l_len| follows the usual flock
+ * range convention and names [l_start + l_len, l_start].
+ */
+#define        F_ALLOCSP       18
+#define        F_FREESP        19
+
 /* fcntl(2) flags (l_type field of flock structure) */
 #define        F_RDLCK         1       /* read lock */
 #define        F_WRLCK         2       /* write lock */
diff --git a/winsup/cygwin/fcntl.cc b/winsup/cygwin/fcntl.cc
index 2bc52edb0..bf6d47542 100644
--- a/winsup/cygwin/fcntl.cc
+++ b/winsup/cygwin/fcntl.cc
@@ -7,6 +7,8 @@ Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 details. */
 
 #include "winsup.h"
+#include <limits.h>
+#include <sys/stat.h>
 #include <unistd.h>
 #include "cygerrno.h"
 #include "security.h"
@@ -16,6 +18,195 @@ details. */
 #include "cygheap.h"
 #include "cygtls.h"
 
+/*
+ * In the Cygwin DLL build, these prototypes are not always visible from
+ * <unistd.h> in this translation unit, even though the symbols are part of
+ * the public Cygwin API.  Declare them explicitly for use by the fcntl()
+ * space-management helpers below.
+ */
+extern "C" off_t lseek (int, off_t, int);
+extern "C" int ftruncate (int, off_t);
+
+struct fcntl_space_range
+{
+  off_t start;
+  off_t len;
+  bool to_eof;
+};
+
+static inline bool
+off_add_overflow (off_t a, off_t b, off_t *r)
+{
+  return __builtin_add_overflow (a, b, r);
+}
+
+static int
+fcntl_space_resolve_range (int fd, short whence, off_t offset, off_t flen,
+    fcntl_space_range *range)
+{
+  struct stat st;
+  off_t base;
+  off_t start;
+
+  switch (whence)
+    {
+    case SEEK_SET:
+      base = 0;
+      break;
+    case SEEK_CUR:
+      base = lseek (fd, 0, SEEK_CUR);
+      if (base == (off_t) -1)
+       return -1;
+      break;
+    case SEEK_END:
+      if (fstat (fd, &st) < 0)
+       return -1;
+      base = st.st_size;
+      break;
+    default:
+      set_errno (EINVAL);
+      return -1;
+    }
+
+  if (off_add_overflow (base, offset, &start))
+    {
+      set_errno (EOVERFLOW);
+      return -1;
+    }
+
+  /*
+     Follow normal struct flock byte-range conventions:
+
+       l_len > 0:  [start, start + l_len)
+       l_len == 0: [start, EOF)
+       l_len < 0:  [start + l_len, start)
+  */
+  if (flen < 0)
+    {
+      if (flen == (off_t) LONG_MIN)
+       {
+         set_errno (EOVERFLOW);
+         return -1;
+       }
+
+      if (off_add_overflow (start, flen, &start))
+       {
+         set_errno (EOVERFLOW);
+         return -1;
+       }
+
+      flen = -flen;
+      range->to_eof = false;
+    }
+  else if (flen == 0)
+    {
+      range->to_eof = true;
+    }
+  else
+    {
+      range->to_eof = false;
+    }
+
+  if (start < 0)
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+
+  range->start = start;
+  range->len = flen;
+  return 0;
+}
+
+static int
+fcntl_allocsp_range (int fd, const fcntl_space_range *range)
+{
+  struct stat st;
+  off_t len = range->len;
+
+  /*
+     For |F_ALLOCSP| with |l_len == 0|, allocate from start to current EOF.
+     If start is at or beyond EOF, the range is empty and this is a no-op.
+  */
+  if (range->to_eof)
+    {
+      if (fstat (fd, &st) < 0)
+       return -1;
+
+      if (range->start >= st.st_size)
+       return 0;
+
+      len = st.st_size - range->start;
+    }
+
+  if (len == 0)
+    return 0;
+
+  return fallocate (fd, 0, range->start, len);
+}
+
+static int
+fcntl_freesp_range (int fd, const fcntl_space_range *range)
+{
+  struct stat st;
+  off_t end;
+
+  if (fstat (fd, &st) < 0)
+    return -1;
+
+  if (range->start >= st.st_size)
+    return 0;
+
+  /*
+     |F_FREESP| to EOF shrinks the file. Hole punching keeps the
+     logical file size unchanged, so use ftruncate for tail ranges.
+  */
+  if (range->to_eof)
+    return ftruncate (fd, range->start);
+
+  if (range->len == 0)
+    return 0;
+
+  if (off_add_overflow (range->start, range->len, &end))
+    {
+      set_errno (EOVERFLOW);
+      return -1;
+    }
+
+  if (end >= st.st_size)
+    return ftruncate (fd, range->start);
+
+  return fallocate (fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+                   range->start, range->len);
+}
+
+static int
+fcntl_space_common (int fd, int cmd, short whence, off_t start, off_t len)
+{
+  fcntl_space_range range;
+
+  if (fcntl_space_resolve_range (fd, whence, start, len, &range) < 0)
+    return -1;
+
+  switch (cmd)
+    {
+    case F_ALLOCSP:
+      return fcntl_allocsp_range (fd, &range);
+    case F_FREESP:
+      return fcntl_freesp_range (fd, &range);
+    default:
+      set_errno (EINVAL);
+      return -1;
+    }
+}
+
+static int
+fcntl_space_flock (int fd, int cmd, const struct flock *fl)
+{
+  return fcntl_space_common (fd, cmd, fl->l_whence,
+    fl->l_start, fl->l_len);
+}
+
 extern "C" int
 fcntl (int fd, int cmd, ...)
 {
@@ -68,6 +259,10 @@ fcntl (int fd, int cmd, ...)
              res = -1;
            }
          break;
+       case F_ALLOCSP:
+       case F_FREESP:
+         res = fcntl_space_flock (fd, cmd, (const struct flock *) arg);
+         break;
        default:
          res = cfd->fcntl (cmd, arg);
          break;
-- 
Problem reports:      https://cygwin.com/problems.html
FAQ:                  https://cygwin.com/faq/
Documentation:        https://cygwin.com/docs.html
Unsubscribe info:     https://cygwin.com/ml/#unsubscribe-simple

Reply via email to