On Wed, 19 Nov 2025, Pali Rohár wrote:

On Wednesday 19 November 2025 15:32:29 Martin Storsjö wrote:
On Sat, 15 Nov 2025, Pali Rohár wrote:

Currently the _lseeki64 emulation supports only 32-bit offsets.

When the offset does not fit into the 32-bit integer (input parameter or
return value) then use WinAPI SetFilePointer() function which supports
64-bit offsets and is available in every Windows version.

With this change mingw-w64 should fully support for all CRT builds the
_lseeki64 function and also other mingw-w64 functions which depends on it:
_telli64, lseek64, ftruncate64, truncate64. And if _FILE_OFFSET_BITS == 64:
lseek, ftruncate, truncate.
---
mingw-w64-crt/stdio/_lseeki64.c | 51 ++++++++++++++++++++++++++++++---
1 file changed, 47 insertions(+), 4 deletions(-)

diff --git a/mingw-w64-crt/stdio/_lseeki64.c b/mingw-w64-crt/stdio/_lseeki64.c
index 6ead4a98e182..948320c1acf9 100644
--- a/mingw-w64-crt/stdio/_lseeki64.c
+++ b/mingw-w64-crt/stdio/_lseeki64.c
@@ -7,17 +7,60 @@
#include <io.h>
#include <errno.h>
#include <limits.h>
+#include <windows.h>

-/* Define 64-bit _lseeki64() function via 32-bit _lseek() function */
+/* Define 64-bit _lseeki64() function via 32-bit CRT _lseek() function and 
64-bit WinAPI SetFilePointer() function */
__int64 __cdecl _lseeki64(int fd, __int64 offset, int whence)
{
-  if (offset < LONG_MIN || offset > LONG_MAX)
+  const BOOL offset_overflowed = offset < LONG_MIN || offset > LONG_MAX;
+
+  if (!offset_overflowed)
+  {
+    /* _lseek() takes only 32-bit offset value but can seek to file position
+     * which does not fit into 32-bit integer via SEEK_CUR / SEEK_END.
+     * Just the return value of new offset is truncated to 32 bits.
+     * _lseek() does not signal truncation, so check here just for offset
+     * overflow for SEEK_SET. For all other cases call SetFilePointer()
+     * to retrieve new 64-bit file offset. */
+    errno = 0;
+    long ret = _lseek(fd, offset, whence);
+    if (whence == SEEK_SET || (ret == -1 && errno))
+      return ret;
+  }
+
+  HANDLE handle = (HANDLE)_get_osfhandle(fd);
+  if (handle == INVALID_HANDLE_VALUE)
  {
-    errno = EOVERFLOW;
+    errno = EBADF;
    return -1;
  }

-  return _lseek(fd, offset, whence);
+  /* If offset did not overflow then _lseek() was called and it already
+   * set new file position, so just read that new file position.
+   * If offset overflowed then _lseek() was not called at all,
+   * so move in the file and set new position.
+   * Note that FILE_* method and SEEK_* whence constants are same. */

Small extra wish: Could we add something to make this even clearer? Like
static_assert(SEEK_SET == FILE_BEGIN) or so? That would also make it clearer
what the other parameters (FILE_BEGIN, FILE_END) to this function even are,
even if we can pass the value through.

Other than that, this patch seems fine.

// Martin

That is a good idea. Maybe something like this could be amended?

diff --git a/mingw-w64-crt/stdio/_lseeki64.c b/mingw-w64-crt/stdio/_lseeki64.c
index 948320c1acf9..747a390c9929 100644
--- a/mingw-w64-crt/stdio/_lseeki64.c
+++ b/mingw-w64-crt/stdio/_lseeki64.c
@@ -40,6 +40,9 @@ __int64 __cdecl _lseeki64(int fd, __int64 offset, int whence)
   * If offset overflowed then _lseek() was not called at all,
   * so move in the file and set new position.
   * Note that FILE_* method and SEEK_* whence constants are same. */
+  _Static_assert(FILE_BEGIN == SEEK_SET, "FILE_BEGIN == SEEK_SET");
+  _Static_assert(FILE_CURRENT == SEEK_CUR, "FILE_CURRENT == SEEK_CUR");
+  _Static_assert(FILE_END == SEEK_END, "FILE_END == SEEK_END");
  LARGE_INTEGER li = { .QuadPart = offset_overflowed ? offset : 0 };

Ok, I amended that into my local copy of the branch, and pushed it for another CI round - I can merge it when that's done.

// Martin

_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to