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. */
+ LARGE_INTEGER li = { .QuadPart = offset_overflowed ? offset : 0 };
+ DWORD method = offset_overflowed ? whence : FILE_CURRENT;
+ li.LowPart = SetFilePointer(handle, li.LowPart, &li.HighPart, method);
+ if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* At this stage, the file position was already changed by SetFilePointer().
+ * For any file position change it is needed to clear EOF flag on fd, because
+ * otherwise _read() would not work. The easiest way to clear EOF flag is to
+ * call _lseek() function which at success path clears the EOF flag. If the
+ * offset did not overflow then the _lseek() was already called. So when
offset
+ * overflowed call it with 0/SEEK_CUR which does nothing and clears EOF
flag. */
+ if (offset_overflowed)
+ {
+ (void)_lseek(fd, 0, SEEK_CUR);
+ }
+
+ return li.QuadPart;
}
__int64 (__cdecl *__MINGW_IMP_SYMBOL(_lseeki64))(int, __int64, int) =
_lseeki64;
--
2.20.1
_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public