blob: 747a390c99299aef71ba58ecf40ed8851c9949fd [file] [log] [blame]
/**
* This file has no copyright assigned and is placed in the Public Domain.
* This file is part of the mingw-w64 runtime package.
* No warranty is given; refer to the file DISCLAIMER.PD within this package.
*/
#include <io.h>
#include <errno.h>
#include <limits.h>
#include <windows.h>
/* 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)
{
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 = EBADF;
return -1;
}
/* 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. */
_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 };
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;
__int64 __attribute__ ((alias ("_lseeki64"))) __cdecl lseek64(int, __int64, int);
extern __int64 (__cdecl * __attribute__((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(_lseeki64))))) __MINGW_IMP_SYMBOL(lseek64))(int, __int64, int);