--- winsup/cygwin/fhandler.h | 2 + winsup/cygwin/fhandler_pipe.cc | 164 +++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+)
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index bdfe4a272..b9cc7b471 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -1206,6 +1206,8 @@ public: char *get_proc_fd_name (char *buf); int open (int flags, mode_t mode = 0); int dup (fhandler_base *child, int); + void __reg3 raw_read (void *ptr, size_t& len); + ssize_t __reg3 raw_write (const void *ptr, size_t len); int ioctl (unsigned int cmd, void *); int __reg2 fstat (struct stat *buf); int __reg2 fstatvfs (struct statvfs *buf); diff --git a/winsup/cygwin/fhandler_pipe.cc b/winsup/cygwin/fhandler_pipe.cc index 2ea69f8ed..4765adc8d 100644 --- a/winsup/cygwin/fhandler_pipe.cc +++ b/winsup/cygwin/fhandler_pipe.cc @@ -20,6 +20,14 @@ details. */ #include "pinfo.h" #include "shared_info.h" +/* This is only to be used for writing. When reading, +STATUS_PIPE_EMPTY simply means there's no data to be read. */ +#define STATUS_PIPE_IS_CLOSED(status) \ + ({ NTSTATUS _s = (status); \ + _s == STATUS_PIPE_CLOSING \ + || _s == STATUS_PIPE_BROKEN \ + || _s == STATUS_PIPE_EMPTY; }) + fhandler_pipe::fhandler_pipe () : fhandler_base (), popen_pid (0) { @@ -184,6 +192,162 @@ fhandler_pipe::get_proc_fd_name (char *buf) return buf; } +void __reg3 +fhandler_pipe::raw_read (void *ptr, size_t& len) +{ + NTSTATUS status; + IO_STATUS_BLOCK io; + HANDLE evt = NULL; + DWORD waitret = WAIT_OBJECT_0; + + /* Create a wait event if we're in blocking mode. */ + if (!is_nonblocking () && !(evt = CreateEvent (NULL, false, false, NULL))) + { + len = (size_t) -1; + __seterrno (); + return; + } + + status = NtReadFile (get_handle (), evt, NULL, NULL, &io, ptr, len, + NULL, NULL); + if (evt && status == STATUS_PENDING) + { + waitret = cygwait (evt, cw_infinite, cw_cancel | cw_sig_eintr); + if (waitret == WAIT_OBJECT_0) + status = io.Status; + } + if (waitret == WAIT_CANCELED) + status = STATUS_THREAD_CANCELED; + else if (waitret == WAIT_SIGNALED) + status = STATUS_THREAD_SIGNALED; + else if (NT_SUCCESS (status)) + len = io.Information; + else + { + /* Some errors are not really errors. Detect such cases here. */ + switch (status) + { + case STATUS_END_OF_FILE: + case STATUS_PIPE_BROKEN: + /* This is really EOF. */ + len = 0; + break; + case STATUS_MORE_ENTRIES: + case STATUS_BUFFER_OVERFLOW: + /* `io.Information' is supposedly valid. */ + len = io.Information; + break; + case STATUS_PIPE_LISTENING: + case STATUS_PIPE_EMPTY: + if (is_nonblocking ()) + { + set_errno (EAGAIN); + len = (size_t) -1; + break; + } + /* Fall through. */ + default: + __seterrno_from_nt_status (status); + len = (size_t) -1; + break; + } + } + if (evt) + CloseHandle (evt); + if (status == STATUS_THREAD_SIGNALED && !_my_tls.call_signal_handler ()) + { + set_errno (EINTR); + len = (size_t) -1; + } + else if (status == STATUS_THREAD_CANCELED) + pthread::static_cancel_self (); +} + +ssize_t __reg3 +fhandler_pipe::raw_write (const void *ptr, size_t len) +{ + ssize_t ret = -1; + size_t nbytes = 0, chunk; + NTSTATUS status = STATUS_SUCCESS; + IO_STATUS_BLOCK io; + HANDLE evt = NULL; + + if (len <= max_atomic_write) + chunk = len; + else if (is_nonblocking ()) + chunk = len = max_atomic_write; + else + chunk = max_atomic_write; + + /* Create a wait event if the pipe is in blocking mode. */ + if (!is_nonblocking () && !(evt = CreateEvent (NULL, false, false, NULL))) + return -1; + + /* Write in chunks, accumulating a total. If there's an error, just + return the accumulated total unless the first write fails, in + which case return -1. */ + while (nbytes < len) + { + ULONG_PTR nbytes_now = 0; + size_t left = len - nbytes; + size_t len1; + if (left > chunk) + len1 = chunk; + else + len1 = left; + nbytes_now = 0; + status = NtWriteFile (get_handle (), evt, NULL, NULL, &io, + (PVOID) ptr, len1, NULL, NULL); + if (evt && status == STATUS_PENDING) + { + DWORD waitret = cygwait (evt, cw_infinite, cw_cancel | cw_sig_eintr); + switch (waitret) + { + case WAIT_OBJECT_0: + status = io.Status; + break; + case WAIT_SIGNALED: + status = STATUS_THREAD_SIGNALED; + break; + case WAIT_CANCELED: + status = STATUS_THREAD_CANCELED; + break; + default: + break; + } + } + if (NT_SUCCESS (status)) + { + nbytes_now = io.Information; + /* NtWriteFile returns success with # of bytes written == 0 + if writing on a non-blocking pipe fails because the pipe + buffer doesn't have sufficient space. */ + if (nbytes_now == 0) + set_errno (EAGAIN); + ptr = ((char *) ptr) + chunk; + nbytes += nbytes_now; + } + else if (STATUS_PIPE_IS_CLOSED (status)) + { + set_errno (EPIPE); + raise (SIGPIPE); + } + else + __seterrno_from_nt_status (status); + if (nbytes_now == 0) + len = 0; /* Terminate loop. */ + if (nbytes > 0) + ret = nbytes; + } + if (evt) + CloseHandle (evt); + if (status == STATUS_THREAD_SIGNALED && !_my_tls.call_signal_handler ()) + set_errno (EINTR); + else if (status == STATUS_THREAD_CANCELED) + pthread::static_cancel_self (); + return ret; +} + int fhandler_pipe::dup (fhandler_base *child, int flags) { -- 2.17.0