OSDN Git Service

Perform whitespace cleanup throughout.
[pf3gnuchains/pf3gnuchains3x.git] / winsup / cygwin / pipe.cc
index 498b172..2498139 100644 (file)
@@ -1,7 +1,7 @@
 /* pipe.cc: pipe for Cygwin.
 
-   Copyright 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-   2005 Red Hat, Inc.
+   Copyright 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+   2008 Hat, Inc.
 
 This file is part of Cygwin.
 
@@ -27,52 +27,243 @@ details. */
 #include "cygthread.h"
 #include "ntdll.h"
 
-static unsigned pipecount;
-static const NO_COPY char pipeid_fmt[] = "stupid_pipe.%u.%u";
-
 fhandler_pipe::fhandler_pipe ()
-  : fhandler_base (), guard (NULL), broken_pipe (false), writepipe_exists(0),
-    orig_pid (0), id (0)
+  : fhandler_base (), popen_pid (0)
+{
+  get_overlapped ()->hEvent = NULL;
+  need_fork_fixup (true);
+}
+
+struct pipesync
+{
+  bool reader;
+  HANDLE ev, non_cygwin_h, ret_handle;
+  pipesync(HANDLE, DWORD);
+  int operator == (int x) const {return !!ev;}
+  static DWORD WINAPI handler (LPVOID *);
+};
+
+inline bool
+getov_result (HANDLE h, DWORD& nbytes, LPOVERLAPPED ov)
+{
+  if (ov && (GetLastError () != ERROR_IO_PENDING
+            || !GetOverlappedResult (h, ov, &nbytes, true)))
+    {
+      __seterrno ();
+      return false;
+    }
+  return true;
+}
+
+static DWORD WINAPI
+pipe_handler (LPVOID in_ps)
+{
+  pipesync ps = *(pipesync *) in_ps;
+  HANDLE in, out;
+  DWORD err = fhandler_pipe::create_selectable (&sec_none_nih, in, out, 0);
+  if (err)
+    {
+      SetLastError (err);
+      system_printf ("couldn't create a shadow pipe for non-cygwin pipe I/O, %E");
+      return 0;
+    }
+  ((pipesync *) in_ps)->ret_handle = ps.reader ? in : out;
+  SetEvent (ps.ev);
+
+  char buf[4096];
+  DWORD read_bytes, write_bytes;
+  HANDLE hread, hwrite, hclose;
+  OVERLAPPED ov, *rov, *wov;
+  memset (&ov, 0, sizeof (ov));
+  ov.hEvent = CreateEvent (&sec_none_nih, true, false, NULL);
+  if (ps.reader)
+    {
+      hread = ps.non_cygwin_h;
+      hclose = hwrite = out;
+      wov = &ov;
+      rov = NULL;
+    }
+  else
+    {
+      hclose = hread = in;
+      hwrite = ps.non_cygwin_h;
+      rov = &ov;
+      wov = NULL;
+    }
+
+  while (1)
+    {
+      ResetEvent (ov.hEvent);
+      BOOL res = ReadFile (hread, buf, 4096, &read_bytes, rov);
+      if (!res && !getov_result (hread, read_bytes, rov))
+       break;
+      if (!read_bytes)
+       break;
+
+      res = WriteFile (hwrite, buf, read_bytes, &write_bytes, wov);
+      if (!res && !getov_result (hwrite, write_bytes, wov))
+       break;
+      if (write_bytes != read_bytes)
+       break;
+    }
+
+  err = GetLastError ();
+  CloseHandle (ov.hEvent);
+  CloseHandle (hclose);
+  CloseHandle (ps.non_cygwin_h);
+  SetLastError (err);
+  return 0;
+}
+
+pipesync::pipesync (HANDLE f, DWORD is_reader):
+  reader (false), ret_handle (NULL)
+{
+  ev = CreateEvent (&sec_none_nih, true, false, NULL);
+  if (!ev)
+    {
+      system_printf ("couldn't create synchronization event for non-cygwin pipe, %E");
+      goto out;
+    }
+  non_cygwin_h = f;
+  reader = !!is_reader;
+  ret_handle = NULL;
+
+  DWORD tid;
+  HANDLE ht = CreateThread (&sec_none_nih, 0, pipe_handler, this, 0, &tid);
+
+  if (!ht)
+    goto out;
+  CloseHandle (ht);
+
+  switch (WaitForSingleObject (ev, INFINITE))
+    {
+    case WAIT_OBJECT_0:
+      break;
+    default:
+      system_printf ("WFSO failed waiting for synchronization event for non-cygwin pipe, %E");
+      break;
+    }
+
+out:
+  if (ev)
+    {
+      CloseHandle (ev);
+      ev = NULL;
+    }
+  return;
+}
+
+#define WINPIPE "\\\\.\\pipe\\"
+void
+fhandler_pipe::init (HANDLE f, DWORD a, mode_t mode)
 {
+  // FIXME: Have to clean this up someday
+  if (!*get_win32_name () && get_name ())
+    {
+      char *hold_normalized_name = (char *) alloca (strlen (get_name ()) + 1);
+      strcpy (hold_normalized_name, get_name ());
+      char *s, *d;
+      for (s = hold_normalized_name, d = (char *) get_win32_name (); *s; s++, d++)
+       if (*s == '/')
+         *d = '\\';
+       else
+         *d = *s;
+      *d = '\0';
+      set_name (hold_normalized_name);
+    }
+
+  bool opened_properly = a & FILE_CREATE_PIPE_INSTANCE;
+  a &= ~FILE_CREATE_PIPE_INSTANCE;
+  if (!opened_properly)
+    {
+      pipesync ps (f, a & GENERIC_READ);
+      f = ps.ret_handle;
+    }
+
+  fhandler_base::init (f, a, mode);
+  if (mode & O_NOINHERIT)
+    close_on_exec (true);
+  setup_overlapped ();
 }
 
+extern "C" int sscanf (const char *, const char *, ...);
+
 int
 fhandler_pipe::open (int flags, mode_t mode)
 {
-  const char *path = get_name ();
-  debug_printf ("path: %s", path);
-  if (!strncmp (get_name (), "/proc/", 6))
+  HANDLE proc, pipe_hdl, nio_hdl = NULL;
+  fhandler_pipe *fh = NULL;
+  size_t size;
+  int pid, rwflags = (flags & O_ACCMODE);
+  bool inh;
+
+  sscanf (get_name (), "/proc/%d/fd/pipe:[%d]", &pid, (int *) &pipe_hdl);
+  if (pid == myself->pid)
     {
-      char *c;
-      HANDLE hdl;
-      int pid = strtol (path += 6, &c, 10);
-      if (!pid || !c || *c != '/')
-        goto out;
-      path = c;
-      if (strncmp (path, "/fd/pipe:[", 10))
-        goto out;
-      path += 10;
-      hdl = (HANDLE) atoi (path);
-      if (pid == myself->pid)
-        {
-         cygheap_fdenum cfd;
-         while (cfd.next () >= 0)
+      cygheap_fdenum cfd (true);
+      while (cfd.next () >= 0)
+       {
+         if (cfd->get_handle () != pipe_hdl)
+           continue;
+         if ((rwflags == O_RDONLY && !(cfd->get_access () & GENERIC_READ))
+             || (rwflags == O_WRONLY && !(cfd->get_access () & GENERIC_WRITE)))
            {
-             if (cfd->get_handle () == hdl)
-               {
-                 if (!cfd->dup (this))
-                   return 1;
-                 return 0;
-               }
+             set_errno (EACCES);
+             return 0;
            }
+         if (!cfd->dup (this))
+           return 1;
+         return 0;
        }
-      else
-        {
-         /* TODO: Open pipes of different process.  Is that possible? */
-       }
+      set_errno (ENOENT);
+      return 0;
+    }
+
+  pinfo p (pid);
+  if (!p)
+    {
+      set_errno (ESRCH);
+      return 0;
+    }
+  if (!(proc = OpenProcess (PROCESS_DUP_HANDLE, false, p->dwProcessId)))
+    {
+      __seterrno ();
+      return 0;
     }
+  if (!(fh = p->pipe_fhandler (pipe_hdl, size)) || !size)
+    {
+      set_errno (ENOENT);
+      goto out;
+    }
+  /* Too bad, but Windows only allows the same access mode when dup'ing
+     the pipe. */
+  if ((rwflags == O_RDONLY && !(fh->get_access () & GENERIC_READ))
+      || (rwflags == O_WRONLY && !(fh->get_access () & GENERIC_WRITE)))
+    {
+      set_errno (EACCES);
+      goto out;
+    }
+  inh = !(flags & O_NOINHERIT);
+  if (!DuplicateHandle (proc, pipe_hdl, hMainProc, &nio_hdl,
+                       0, inh, DUPLICATE_SAME_ACCESS))
+    {
+      __seterrno ();
+      goto out;
+    }
+  init (nio_hdl, fh->get_access (), mode & O_TEXT ?: O_BINARY);
+  if (flags & O_NOINHERIT)
+    close_on_exec (true);
+  uninterruptible_io (fh->uninterruptible_io ());
+  cfree (fh);
+  CloseHandle (proc);
+  return 1;
 out:
-  set_errno (ENXIO);
+  if (nio_hdl)
+    CloseHandle (nio_hdl);
+  if (fh)
+    free (fh);
+  if (proc)
+    CloseHandle (proc);
   return 0;
 }
 
@@ -84,179 +275,51 @@ fhandler_pipe::lseek (_off64_t offset, int whence)
   return -1;
 }
 
-void
-fhandler_pipe::set_close_on_exec (bool val)
+int
+fhandler_pipe::fadvise (_off64_t offset, _off64_t length, int advice)
 {
-  fhandler_base::set_close_on_exec (val);
-  if (guard)
-    set_no_inheritance (guard, val);
-  if (writepipe_exists)
-    set_no_inheritance (writepipe_exists, val);
+  set_errno (ESPIPE);
+  return -1;
 }
 
-char *fhandler_pipe::get_proc_fd_name (char *buf)
+int
+fhandler_pipe::ftruncate (_off64_t length, bool allow_truncate)
 {
-  __small_sprintf (buf, "pipe:[%d]", get_handle ());
-  return buf;
+  set_errno (allow_truncate ? EINVAL : ESPIPE);
+  return -1;
 }
 
-struct pipeargs
-{
-  fhandler_base *fh;
-  void *ptr;
-  size_t *len;
-};
-
-static DWORD WINAPI
-read_pipe (void *arg)
+char *
+fhandler_pipe::get_proc_fd_name (char *buf)
 {
-  pipeargs *pi = (pipeargs *) arg;
-  pi->fh->fhandler_base::read (pi->ptr, *pi->len);
-  return 0;
+  __small_sprintf (buf, "pipe:[%d]", get_handle ());
+  return buf;
 }
 
 void __stdcall
 fhandler_pipe::read (void *in_ptr, size_t& in_len)
 {
-  if (broken_pipe)
-    in_len = 0;
-  else
-    {
-      pipeargs pi = {dynamic_cast<fhandler_base *>(this), in_ptr, &in_len};
-      ResetEvent (read_state);
-      cygthread *th = new cygthread (read_pipe, &pi, "read_pipe");
-      if (th->detach (read_state) && !in_len)
-       in_len = (size_t) -1;   /* received a signal */
-    }
-  (void) ReleaseMutex (guard);
-  return;
+  return read_overlapped (in_ptr, in_len);
 }
 
 int
-fhandler_pipe::close ()
+fhandler_pipe::write (const void *ptr, size_t len)
 {
-  if (guard)
-    CloseHandle (guard);
-  if (writepipe_exists)
-    CloseHandle (writepipe_exists);
-#ifndef NEWVFORK
-  if (read_state)
-#else
-  // FIXME is this vfork_cleanup test right?  Is it responsible for some of
-  // the strange pipe behavior that has been reported in the cygwin mailing
-  // list?
-  if (read_state && !cygheap->fdtab.in_vfork_cleanup ())
-#endif
-    ForceCloseHandle (read_state);
-  if (get_handle ())
-    {
-      CloseHandle (get_handle ());
-      set_io_handle (NULL);
-    }
-  return 0;
-}
-
-bool
-fhandler_pipe::hit_eof ()
-{
-  char buf[80];
-  HANDLE ev;
-  if (broken_pipe)
-    return 1;
-  if (!orig_pid)
-    return false;
-  __small_sprintf (buf, pipeid_fmt, orig_pid, id);
-  if ((ev = OpenEvent (EVENT_ALL_ACCESS, FALSE, buf)))
-    CloseHandle (ev);
-  debug_printf ("%s %p", buf, ev);
-  return ev == NULL;
-}
-
-void
-fhandler_pipe::fixup_after_exec ()
-{
-  if (read_state)
-    {
-      read_state = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
-      ProtectHandle (read_state);
-    }
-}
-
-void
-fhandler_pipe::fixup_after_fork (HANDLE parent)
-{
-  fhandler_base::fixup_after_fork (parent);
-  if (guard)
-    fork_fixup (parent, guard, "guard");
-  if (writepipe_exists)
-    fork_fixup (parent, writepipe_exists, "guard");
-  fixup_after_exec ();
+  return write_overlapped (ptr, len);
 }
 
 int
 fhandler_pipe::dup (fhandler_base *child)
 {
-  int res = -1;
   fhandler_pipe *ftp = (fhandler_pipe *) child;
-  ftp->guard = ftp->writepipe_exists = ftp->read_state = NULL;
+  ftp->set_popen_pid (0);
 
-  if (get_handle ())
-    {
-      res = fhandler_base::dup (child);
-      if (res)
-       goto err;
-    }
-
-  /* FIXME: This leaks handles in the failing condition */
-  if (guard == NULL)
-    ftp->guard = NULL;
-  else if (!DuplicateHandle (hMainProc, guard, hMainProc, &ftp->guard, 0, 1,
-                            DUPLICATE_SAME_ACCESS))
-    {
-      debug_printf ("couldn't duplicate guard %p, %E", guard);
-      goto err;
-    }
-
-  if (writepipe_exists == NULL)
-    ftp->writepipe_exists = NULL;
-  else if (!DuplicateHandle (hMainProc, writepipe_exists, hMainProc,
-                            &ftp->writepipe_exists, 0, 1,
-                            DUPLICATE_SAME_ACCESS))
-    {
-      debug_printf ("couldn't duplicate writepipe_exists %p, %E", writepipe_exists);
-      goto err;
-    }
-
-  if (read_state == NULL)
-    ftp->read_state = NULL;
-  else if (!DuplicateHandle (hMainProc, read_state, hMainProc,
-                            &ftp->read_state, 0, 0,
-                            DUPLICATE_SAME_ACCESS))
-    {
-      debug_printf ("couldn't duplicate read_state %p, %E", read_state);
-      goto err;
-    }
-
-  res = 0;
-  goto out;
-
-err:
-  if (ftp->guard)
-    CloseHandle (ftp->guard);
-  if (ftp->writepipe_exists)
-    CloseHandle (ftp->writepipe_exists);
-  if (ftp->read_state)
-    CloseHandle (ftp->read_state);
-  goto leave;
-
-out:
-  ftp->id = id;
-  ftp->orig_pid = orig_pid;
-  VerifyHandle (ftp->guard);
-  VerifyHandle (ftp->writepipe_exists);
-  VerifyHandle (ftp->read_state);
+  int res;
+  if (get_handle () && fhandler_base::dup (child))
+    res = -1;
+  else
+    res = 0;
 
-leave:
   debug_printf ("res %d", res);
   return res;
 }
@@ -266,18 +329,14 @@ leave:
    FILE_READ_ATTRIBUTES access, on later versions of win32 where
    this is supported.  This access is needed by NtQueryInformationFile,
    which is used to implement select and nonblocking writes.
-   Note that the return value is either NO_ERROR or GetLastError,
+   Note that the return value is either 0 or GetLastError,
    unlike CreatePipe, which returns a bool for success or failure.  */
-static int
-create_selectable_pipe (PHANDLE read_pipe_ptr,
-                       PHANDLE write_pipe_ptr,
-                       LPSECURITY_ATTRIBUTES sa_ptr,
-                       DWORD psize)
+int
+fhandler_pipe::create_selectable (LPSECURITY_ATTRIBUTES sa_ptr, HANDLE& r,
+                                 HANDLE& w, DWORD psize)
 {
   /* Default to error. */
-  *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
-
-  HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
+  r = w = INVALID_HANDLE_VALUE;
 
   /* Ensure that there is enough pipe buffer space for atomic writes.  */
   if (psize < PIPE_BUF)
@@ -290,9 +349,9 @@ create_selectable_pipe (PHANDLE read_pipe_ptr,
      to be as robust as possible.  */
   while (1)
     {
-      static volatile LONG pipe_unique_id;
+      static volatile ULONG pipe_unique_id;
 
-      __small_sprintf (pipename, "\\\\.\\pipe\\cygwin-%d-%ld", myself->pid,
+      __small_sprintf (pipename, "\\\\.\\pipe\\cygwin-%p-%p", myself->pid,
                       InterlockedIncrement ((LONG *) &pipe_unique_id));
 
       debug_printf ("CreateNamedPipe: name %s, size %lu", pipename, psize);
@@ -306,125 +365,83 @@ create_selectable_pipe (PHANDLE read_pipe_ptr,
         the pipe was not created earlier by some other process, even if
         the pid has been reused.  We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
         because that is only available for Win2k SP2 and WinXP.  */
-      SetLastError (0);
-      read_pipe = CreateNamedPipe (pipename,
-                                  PIPE_ACCESS_INBOUND,
-                                  PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
-                                  1,       /* max instances */
-                                  psize,   /* output buffer size */
-                                  psize,   /* input buffer size */
-                                  NMPWAIT_USE_DEFAULT_WAIT,
-                                  sa_ptr);
+      r = CreateNamedPipe (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
+                          PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, psize,
+                          psize, NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
 
-      DWORD err = GetLastError ();
       /* Win 95 seems to return NULL instead of INVALID_HANDLE_VALUE */
-      if ((read_pipe || !err) && read_pipe != INVALID_HANDLE_VALUE)
+      if (r && r != INVALID_HANDLE_VALUE)
        {
-         debug_printf ("pipe read handle %p", read_pipe);
+         debug_printf ("pipe read handle %p", r);
          break;
        }
 
+      DWORD err = GetLastError ();
       switch (err)
        {
        case ERROR_PIPE_BUSY:
          /* The pipe is already open with compatible parameters.
             Pick a new name and retry.  */
          debug_printf ("pipe busy, retrying");
-         continue;
+         break;
        case ERROR_ACCESS_DENIED:
          /* The pipe is already open with incompatible parameters.
             Pick a new name and retry.  */
          debug_printf ("pipe access denied, retrying");
-         continue;
-       case ERROR_CALL_NOT_IMPLEMENTED:
-         /* We are on an older Win9x platform without named pipes.
-            Return an anonymous pipe as the best approximation.  */
-         debug_printf ("CreateNamedPipe not implemented, resorting to "
-                       "CreatePipe size %lu", psize);
-         if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
-           {
-             debug_printf ("pipe read handle %p", *read_pipe_ptr);
-             debug_printf ("pipe write handle %p", *write_pipe_ptr);
-             return NO_ERROR;
-           }
-         err = GetLastError ();
-         debug_printf ("CreatePipe failed, %E");
-         return err;
+         break;
        default:
-         debug_printf ("CreateNamedPipe failed, %E");
-         return err;
+         {
+           err = GetLastError ();
+           debug_printf ("CreatePipe failed, %E");
+           return err;
+         }
        }
-      /* NOTREACHED */
     }
 
   debug_printf ("CreateFile: name %s", pipename);
 
   /* Open the named pipe for writing.
      Be sure to permit FILE_READ_ATTRIBUTES access.  */
-  write_pipe = CreateFile (pipename,
-                          GENERIC_WRITE | FILE_READ_ATTRIBUTES,
-                          0,       /* share mode */
-                          sa_ptr,
-                          OPEN_EXISTING,
-                          0,       /* flags and attributes */
-                          0);      /* handle to template file */
-
-  if (write_pipe == INVALID_HANDLE_VALUE)
+  w = CreateFile (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, sa_ptr,
+                 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+
+  if (!w || w == INVALID_HANDLE_VALUE)
     {
       /* Failure. */
       DWORD err = GetLastError ();
       debug_printf ("CreateFile failed, %E");
-      CloseHandle (read_pipe);
+      CloseHandle (r);
       return err;
     }
 
-  debug_printf ("pipe write handle %p", write_pipe);
+  debug_printf ("pipe write handle %p", w);
 
   /* Success. */
-  *read_pipe_ptr = read_pipe;
-  *write_pipe_ptr = write_pipe;
-  return NO_ERROR;
+  return 0;
 }
 
 int
-fhandler_pipe::create (fhandler_pipe *fhs[2], unsigned psize, int mode, bool fifo)
+fhandler_pipe::create (fhandler_pipe *fhs[2], unsigned psize, int mode)
 {
   HANDLE r, w;
   SECURITY_ATTRIBUTES *sa = (mode & O_NOINHERIT) ?  &sec_none_nih : &sec_none;
-  int res = -1;
-  int ret;
+  int res;
 
-  if ((ret = create_selectable_pipe (&r, &w, sa, psize)) != NO_ERROR)
-    __seterrno_from_win_error (ret);
+  int ret = create_selectable (sa, r, w, psize);
+  if (ret)
+    {
+      __seterrno_from_win_error (ret);
+      res = -1;
+    }
   else
     {
       fhs[0] = (fhandler_pipe *) build_fh_dev (*piper_dev);
       fhs[1] = (fhandler_pipe *) build_fh_dev (*pipew_dev);
 
-      int binmode = mode & O_TEXT ?: O_BINARY;
-      fhs[0]->init (r, GENERIC_READ, binmode);
-      fhs[1]->init (w, GENERIC_WRITE, binmode);
-      if (mode & O_NOINHERIT)
-       {
-        fhs[0]->close_on_exec (true);
-        fhs[1]->close_on_exec (true);
-       }
-
-      fhs[0]->read_state = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
-      fhs[0]->need_fork_fixup (true);
-      ProtectHandle1 (fhs[0]->read_state, read_state);
-
+      mode |= mode & O_TEXT ?: O_BINARY;
+      fhs[0]->init (r, FILE_CREATE_PIPE_INSTANCE | GENERIC_READ, mode);
+      fhs[1]->init (w, FILE_CREATE_PIPE_INSTANCE | GENERIC_WRITE, mode);
       res = 0;
-      fhs[0]->create_guard (sa);
-      if (wincap.has_unreliable_pipes ())
-       {
-         char buf[80];
-         int count = pipecount++;      /* FIXME: Should this be InterlockedIncrement? */
-         __small_sprintf (buf, pipeid_fmt, myself->pid, count);
-         fhs[1]->writepipe_exists = CreateEvent (sa, TRUE, FALSE, buf);
-         fhs[0]->orig_pid = myself->pid;
-         fhs[0]->id = count;
-       }
     }
 
   syscall_printf ("%d = pipe ([%p, %p], %d, %p)", res, fhs[0], fhs[1], psize, mode);
@@ -458,7 +475,14 @@ fhandler_pipe::ioctl (unsigned int cmd, void *p)
   return 0;
 }
 
-#define DEFAULT_PIPEBUFSIZE (4 * PIPE_BUF)
+int __stdcall
+fhandler_pipe::fstatvfs (struct statvfs *sfs)
+{
+  set_errno (EBADF);
+  return -1;
+}
+
+#define DEFAULT_PIPEBUFSIZE (16 * PIPE_BUF)
 
 extern "C" int
 pipe (int filedes[2])