diff options
author | Samuel Williams <[email protected]> | 2023-05-24 09:15:20 +0900 |
---|---|---|
committer | GitHub <[email protected]> | 2023-05-24 09:15:20 +0900 |
commit | 28056a6d161417bd7b3aed8099f59f4ac164b351 (patch) | |
tree | bf10d11f371f09f73bdc4eebd6bd6f0e360f9f08 /win32/win32.c | |
parent | 9592bc703922933c4196748a0e7221a53ad50b9b (diff) |
Add support for pread/pwrite on windows. (#7827)
Notes
Notes:
Merged-By: ioquatix <[email protected]>
Diffstat (limited to 'win32/win32.c')
-rw-r--r-- | win32/win32.c | 89 |
1 files changed, 68 insertions, 21 deletions
diff --git a/win32/win32.c b/win32/win32.c index 8c13ba7e07..308867bb07 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -7181,21 +7181,43 @@ rb_w32_close(int fd) return 0; } +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + static int -setup_overlapped(OVERLAPPED *ol, int fd, int iswrite) +setup_overlapped(OVERLAPPED *ol, int fd, int iswrite, rb_off_t *_offset) { memset(ol, 0, sizeof(*ol)); - if (!(_osfile(fd) & (FDEV | FPIPE))) { + + // On mode:a, it can write only FILE_END. + // On mode:a+, though it can write only FILE_END, + // it can read from everywhere. + DWORD seek_method = ((_osfile(fd) & FAPPEND) && iswrite) ? FILE_END : FILE_CURRENT; + + if (_offset) { + // Explicit offset was provided (pread/pwrite) - use it: + uint64_t offset = *_offset; + ol->Offset = (uint32_t)(offset & 0xFFFFFFFFLL); + ol->OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 32); + + // Update _offset with the current offset: + LARGE_INTEGER seek_offset = {0}, current_offset = {0}; + if (!SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, ¤t_offset, seek_method)) { + DWORD last_error = GetLastError(); + if (last_error != NO_ERROR) { + errno = map_errno(last_error); + return -1; + } + } + + // As we need to restore the current offset later, we save it here: + *_offset = current_offset.QuadPart; + } + else if (!(_osfile(fd) & (FDEV | FPIPE))) { LONG high = 0; - /* On mode:a, it can write only FILE_END. - * On mode:a+, though it can write only FILE_END, - * it can read from everywhere. - */ - DWORD method = ((_osfile(fd) & FAPPEND) && iswrite) ? FILE_END : FILE_CURRENT; - DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, method); -#ifndef INVALID_SET_FILE_POINTER -#define INVALID_SET_FILE_POINTER ((DWORD)-1) -#endif + DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, seek_method); + if (low == INVALID_SET_FILE_POINTER) { DWORD err = GetLastError(); if (err != NO_ERROR) { @@ -7203,9 +7225,11 @@ setup_overlapped(OVERLAPPED *ol, int fd, int iswrite) return -1; } } + ol->Offset = low; ol->OffsetHigh = high; } + ol->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); if (!ol->hEvent) { errno = map_errno(GetLastError()); @@ -7215,11 +7239,22 @@ setup_overlapped(OVERLAPPED *ol, int fd, int iswrite) } static void -finish_overlapped(OVERLAPPED *ol, int fd, DWORD size) +finish_overlapped(OVERLAPPED *ol, int fd, DWORD size, rb_off_t *_offset) { CloseHandle(ol->hEvent); - if (!(_osfile(fd) & (FDEV | FPIPE))) { + if (_offset) { + // If we were doing a `pread`/`pwrite`, we need to restore the current that was saved in setup_overlapped: + DWORD seek_method = (_osfile(fd) & FAPPEND) ? FILE_END : FILE_BEGIN; + + LARGE_INTEGER seek_offset = {0}; + if (seek_method == FILE_BEGIN) { + seek_offset.QuadPart = *_offset; + } + + SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, NULL, seek_method); + } + else if (!(_osfile(fd) & (FDEV | FPIPE))) { LONG high = ol->OffsetHigh; DWORD low = ol->Offset + size; if (low < ol->Offset) @@ -7231,7 +7266,7 @@ finish_overlapped(OVERLAPPED *ol, int fd, DWORD size) #undef read /* License: Ruby's */ ssize_t -rb_w32_read(int fd, void *buf, size_t size) +rb_w32_read(int fd, void *buf, size_t size, rb_off_t *offset) { SOCKET sock = TO_SOCKET(fd); DWORD read; @@ -7252,7 +7287,7 @@ rb_w32_read(int fd, void *buf, size_t size) return -1; } - if (_osfile(fd) & FTEXT) { + if (!offset && _osfile(fd) & FTEXT) { return _read(fd, buf, size); } @@ -7286,7 +7321,7 @@ rb_w32_read(int fd, void *buf, size_t size) len = size; size -= len; - if (setup_overlapped(&ol, fd, FALSE)) { + if (setup_overlapped(&ol, fd, FALSE, offset)) { rb_acrt_lowio_unlock_fh(fd); return -1; } @@ -7349,7 +7384,7 @@ rb_w32_read(int fd, void *buf, size_t size) errno = map_errno(err); } - finish_overlapped(&ol, fd, read); + finish_overlapped(&ol, fd, read, offset); ret += read; if (read >= len) { @@ -7370,7 +7405,7 @@ rb_w32_read(int fd, void *buf, size_t size) #undef write /* License: Ruby's */ ssize_t -rb_w32_write(int fd, const void *buf, size_t size) +rb_w32_write(int fd, const void *buf, size_t size, rb_off_t *offset) { SOCKET sock = TO_SOCKET(fd); DWORD written; @@ -7388,7 +7423,8 @@ rb_w32_write(int fd, const void *buf, size_t size) return -1; } - if ((_osfile(fd) & FTEXT) && + // If an offset is given, we can't use `_write`. + if (!offset && (_osfile(fd) & FTEXT) && (!(_osfile(fd) & FPIPE) || fd == fileno(stdout) || fd == fileno(stderr))) { ssize_t w = _write(fd, buf, size); if (w == (ssize_t)-1 && errno == EINVAL) { @@ -7410,7 +7446,8 @@ rb_w32_write(int fd, const void *buf, size_t size) size -= len; retry2: - if (setup_overlapped(&ol, fd, TRUE)) { + // Provide the requested offset. + if (setup_overlapped(&ol, fd, TRUE, offset)) { rb_acrt_lowio_unlock_fh(fd); return -1; } @@ -7449,7 +7486,7 @@ rb_w32_write(int fd, const void *buf, size_t size) } } - finish_overlapped(&ol, fd, written); + finish_overlapped(&ol, fd, written, offset); ret += written; if (written == len) { @@ -7473,6 +7510,16 @@ rb_w32_write(int fd, const void *buf, size_t size) return ret; } +ssize_t rb_w32_pread(int descriptor, void *base, size_t size, rb_off_t offset) +{ + return rb_w32_read(descriptor, base, size, &offset); +} + +ssize_t rb_w32_pwrite(int descriptor, const void *base, size_t size, rb_off_t offset) +{ + return rb_w32_write(descriptor, base, size, &offset); +} + /* License: Ruby's */ long rb_w32_write_console(uintptr_t strarg, int fd) |