posix/fd: fix the win32 pwrite emulation

We had assumed that WriteFile/OVERLAPPED operations didn't modify the
file cursor. This was wrong. We emulate the POSIX behaviour using a few
explicit seek calls.

This introduces an incorrect transient state, but isn't a huge problem
for our use cases.
This commit is contained in:
Danny Robson 2019-07-01 15:41:04 +10:00
parent 0db1d54a93
commit d0950a97fa

View File

@ -180,14 +180,26 @@ fd::pwrite (const void *buf, size_t count, size_t offset)
::pwrite (native (), buf, count, offset)
);
#else
DWORD written;
OVERLAPPED overlapped;
memset (&overlapped, 0, sizeof (overlapped));
overlapped.Offset = offset & 0xffffffff;
overlapped.OffsetHigh = offset >> 32u;
// Ideally we'd to use something like WriteFile with an OVERLAPPED
// structure applied to the handle extracted with _get_osfhandle (m_fd).
//
// But this modifies the file cursor, and if we need to seek anyway then
// we may as well just emulate the entire with a query and two seeks.
//
// NOTE: It is thus possible to observe the fd with an 'incorrect' file
// pointer. Do not rely on POSIX behaviour across multiple threads.
if (!WriteFile (reinterpret_cast<HANDLE> (_get_osfhandle (m_fd)), buf, count, &written, &overlapped))
error::throw_code (EINVAL);
auto const initial = lseek (0, SEEK_CUR);
ssize_t written = -1;
try {
(void)lseek (offset, SEEK_SET);
written = write (buf, count);
} catch (...) {
(void)lseek (initial, SEEK_SET);
throw;
}
(void)lseek (initial, SEEK_SET);
return written;
#endif
}