https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=4b25687ea3ee2fc6c79e2df999a2fa86fcd39e67

commit 4b25687ea3ee2fc6c79e2df999a2fa86fcd39e67
Author: Ken Brown <[email protected]>
Date:   Sat May 25 13:03:08 2019 -0400

    Cygwin: fhandler_pipe: add raw_read and raw_write

Diff:
---
 winsup/cygwin/fhandler.h       |   2 +
 winsup/cygwin/fhandler_pipe.cc | 200 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 202 insertions(+)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index c612a3684..12618db58 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1257,6 +1257,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 65ef70c25..61ab29187 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,198 @@ 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;
+  bool keep_looping = false;
+  size_t orig_len = len;
+
+  if (!len)
+    return;
+
+  /* Create a wait event if we're in blocking mode. */
+  if (!is_nonblocking () && !(evt = CreateEvent (NULL, false, false, NULL)))
+    {
+      __seterrno ();
+      len = (size_t) -1;
+      return;
+    }
+
+  do
+    {
+      len = orig_len;
+      keep_looping = false;
+      if (evt)
+       ResetEvent (evt);
+      status = NtReadFile (get_handle (), evt, NULL, NULL, &io, ptr,
+                          len, NULL, NULL);
+      if (evt && status == STATUS_PENDING)
+       {
+         waitret = cygwait (evt);
+         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 (isclosed ())  /* A signal handler might have closed the fd. */
+       {
+         if (waitret == WAIT_OBJECT_0)
+           set_errno (EBADF);
+         else
+           __seterrno ();
+         len = (size_t) -1;
+       }
+      else if (NT_SUCCESS (status))
+       {
+         len = io.Information;
+         if (len == 0)
+           keep_looping = true;
+       }
+      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;
+             if (len == 0)
+               keep_looping = true;
+             break;
+           case STATUS_PIPE_LISTENING:
+           case STATUS_PIPE_EMPTY:
+             if (is_nonblocking ())
+               {
+                 set_errno (EAGAIN);
+                 len = (size_t) -1;
+                 break;
+               }
+             fallthrough;
+           default:
+             __seterrno_from_nt_status (status);
+             len = (size_t) -1;
+             break;
+           }
+       }
+    } while (keep_looping);
+  if (evt)
+    CloseHandle (evt);
+  if (status == STATUS_THREAD_SIGNALED)
+    {
+      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;
+  ULONG chunk;
+  NTSTATUS status = STATUS_SUCCESS;
+  IO_STATUS_BLOCK io;
+  HANDLE evt = NULL;
+
+  if (!len)
+    return 0;
+
+  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)))
+    {
+      __seterrno ();
+      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;
+      ULONG len1;
+      DWORD waitret = WAIT_OBJECT_0;
+
+      if (left > chunk)
+       len1 = chunk;
+      else
+       len1 = (ULONG) left;
+      nbytes_now = 0;
+      status = NtWriteFile (get_handle (), evt, NULL, NULL, &io,
+                           (PVOID) ptr, len1, NULL, NULL);
+      if (evt && status == STATUS_PENDING)
+       {
+         waitret = cygwait (evt);
+         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 (isclosed ())  /* A signal handler might have closed the fd. */
+       {
+         if (waitret == WAIT_OBJECT_0)
+           set_errno (EBADF);
+         else
+           __seterrno ();
+         len = (size_t) -1;
+       }
+      else 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 && ret < 0)
+    set_errno (EINTR);
+  else if (status == STATUS_THREAD_CANCELED)
+    pthread::static_cancel_self ();
+  return ret;
+}
+
 int
 fhandler_pipe::dup (fhandler_base *child, int flags)
 {

Reply via email to