diff --git a/winsup/cygwin/cygwin.din b/winsup/cygwin/cygwin.din
index d5e8f8c05..9b76ce67a 100644
--- a/winsup/cygwin/cygwin.din
+++ b/winsup/cygwin/cygwin.din
@@ -506,6 +506,7 @@ fabsf NOSIGFE
fabsl NOSIGFE
faccessat SIGFE
facl SIGFE
+fallocate SIGFE
fchdir SIGFE
fchmod SIGFE
fchmodat SIGFE
diff --git a/winsup/cygwin/fhandler/disk_file.cc b/winsup/cygwin/fhandler/disk_file.cc
index 51602f30f..b49b25c71 100644
--- a/winsup/cygwin/fhandler/disk_file.cc
+++ b/winsup/cygwin/fhandler/disk_file.cc
@@ -1153,15 +1153,46 @@ fhandler_disk_file::fallocate (int mode, off_t offset, off_t length)
if (!NT_SUCCESS (status))
return geterrno_from_nt_status (status);
- /* If called through posix_fallocate, silently succeed if
- offset + length is less than the file's actual length. */
- if (mode == 0 && offset + length < fsi.EndOfFile.QuadPart)
- return 0;
+ /* Never change file size if FALLOC_FL_KEEP_SIZE is specified. */
+ if ((mode & FALLOC_FL_KEEP_SIZE)
+ && offset + length > fsi.EndOfFile.QuadPart)
+ {
+ if (offset > fsi.EndOfFile.QuadPart) /* no-op */
+ return 0;
+ length = fsi.EndOfFile.QuadPart - offset;
+ }
+ mode &= ~FALLOC_FL_KEEP_SIZE;
+
+ switch (mode)
+ {
+ case 0:
+ case __FALLOC_FL_TRUNCATE:
+ break;
+ case FALLOC_FL_PUNCH_HOLE: /* TODO */
+ return EOPNOTSUPP;
+ break;
+ case FALLOC_FL_ZERO_RANGE: /* TODO */
+ return EOPNOTSUPP;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ if (mode == 0)
+ {
+ /* If called through posix_fallocate, silently succeed if
+ offset + length is less than the file's actual length. */
+
+ /* TODO: If the file is sparse, POSIX requires to allocate
+ the holes within offset and offset + length. */
+ if (offset + length < fsi.EndOfFile.QuadPart)
+ return 0;
+ }
feofi.EndOfFile.QuadPart = offset + length;
/* Create sparse files only when called through ftruncate, not when
called through posix_fallocate. */
- if ((mode & __FALLOC_FL_TRUNCATE)
+ if (mode == __FALLOC_FL_TRUNCATE
&& !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE)
&& pc.support_sparse ()
&& offset + length >= fsi.EndOfFile.QuadPart + (128 * 1024))
diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h
index 833de646c..c8177c2b1 100644
--- a/winsup/cygwin/include/cygwin/version.h
+++ b/winsup/cygwin/include/cygwin/version.h
@@ -483,12 +483,13 @@ details. */
posix_spawn_file_actions_addfchdir_np.
347: Add c16rtomb, c32rtomb, mbrtoc16, mbrtoc32.
348: Add c8rtomb, mbrtoc.
+ 349: Add fallocate.
Note that we forgot to bump the api for ualarm, strtoll, strtoull,
sigaltstack, sethostname. */
#define CYGWIN_VERSION_API_MAJOR 0
-#define CYGWIN_VERSION_API_MINOR 348
+#define CYGWIN_VERSION_API_MINOR 349
/* There is also a compatibity version number associated with the shared memory
regions. It is incremented when incompatible changes are made to the shared
diff --git a/winsup/cygwin/include/fcntl.h b/winsup/cygwin/include/fcntl.h
index 1ef51e50c..b38dfa50b 100644
--- a/winsup/cygwin/include/fcntl.h
+++ b/winsup/cygwin/include/fcntl.h
@@ -42,12 +42,24 @@ details. */
#define POSIX_FADV_DONTNEED 4
#define POSIX_FADV_NOREUSE 5
-#define __FALLOC_FL_TRUNCATE 0x0001 /* internal */
+#if __GNU_VISIBLE
+#define FALLOC_FL_PUNCH_HOLE 0x0001
+#define FALLOC_FL_ZERO_RANGE 0x0002
+#define FALLOC_FL_UNSHARE_RANGE 0x0004
+#define FALLOC_FL_COLLAPSE_RANGE 0x0008
+#define FALLOC_FL_INSERT_RANGE 0x0010
+#define FALLOC_FL_KEEP_SIZE 0x1000
+/* Internal flags */
+#define __FALLOC_FL_TRUNCATE 0x2000
+#endif
__BEGIN_DECLS
extern int posix_fadvise (int, off_t, off_t, int);
extern int posix_fallocate (int, off_t, off_t);
+#if __GNU_VISIBLE
+extern int fallocate (int, int, off_t, off_t);
+#endif
__END_DECLS
diff --git a/winsup/cygwin/release/3.5.0 b/winsup/cygwin/release/3.5.0
index aee21c972..3b6df951d 100644
--- a/winsup/cygwin/release/3.5.0
+++ b/winsup/cygwin/release/3.5.0
@@ -43,6 +43,8 @@ What's new:
- New API calls: c8rtomb, c16rtomb, c32rtomb, mbrtoc8, mbrtoc16, mbrtoc32.
+- New API call: fallocate (Linux-specific).
+
- Implement OSS-based sound mixer device (/dev/mixer).
What changed:
diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
index b2e6ba16c..0e4c54bcf 100644
--- a/winsup/cygwin/syscalls.cc
+++ b/winsup/cygwin/syscalls.cc
@@ -2987,6 +2987,40 @@ posix_fadvise (int fd, off_t offset, off_t len, int advice)
return res;
}
+extern "C" int
+fallocate (int fd, int mode, off_t offset, off_t len)
+{
+ int res = 0;
+
+ /* First check mask of allowed flags */
+ if (mode & ~(FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE
+ | FALLOC_FL_UNSHARE_RANGE | FALLOC_FL_COLLAPSE_RANGE
+ | FALLOC_FL_INSERT_RANGE | FALLOC_FL_KEEP_SIZE))
+ res = EOPNOTSUPP;
+ /* Either FALLOC_FL_PUNCH_HOLE or FALLOC_FL_ZERO_RANGE, never both */
+ else if ((mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE))
+ == (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE))
+ res = EOPNOTSUPP;
+ /* FALLOC_FL_PUNCH_HOLE must be ORed with FALLOC_FL_KEEP_SIZE */
+ else if ((mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE))
+ == FALLOC_FL_PUNCH_HOLE)
+ res = EOPNOTSUPP;
+ else if (offset < 0 || len == 0)
+ res = EINVAL;
+ else
+ {
+ cygheap_fdget cfd (fd);
+ if (cfd >= 0)
+ res = cfd->fallocate (mode, offset, len);
+ else
+ res = EBADF;
+ if (res == EISDIR)
+ res = ENODEV;
+ }
+ syscall_printf ("%R = posix_fallocate(%d, %D, %D)", res, fd, offset, len);
+ return 0;
+}
+
extern "C" int
posix_fallocate (int fd, off_t offset, off_t len)
{
diff --git a/winsup/doc/new-features.xml b/winsup/doc/new-features.xml
index 2c31a4acc..c5e6cdd7f 100644
--- a/winsup/doc/new-features.xml
+++ b/winsup/doc/new-features.xml
@@ -74,6 +74,10 @@ posix_spawn_file_actions_addfchdir_np.
New API calls: c8rtomb, c16rtomb, c32rtomb, mbrtoc8, mbrtoc16, mbrtoc32.
+
+New API call: fallocate (Linux-specific).
+
+
FIFOs now also work over NFS.