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