g10: Add new delete operations that allow more flags.
[gpgme.git] / src / w32-io.c
index 168177e..eed8a00 100644 (file)
@@ -3,17 +3,17 @@
    Copyright (C) 2001, 2002, 2003, 2004, 2007, 2010 g10 Code GmbH
 
    This file is part of GPGME.
+
    GPGME is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation; either version 2.1 of
    the License, or (at your option) any later version.
-   
+
    GPGME is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
-   
+
    You should have received a copy of the GNU Lesser General Public
    License along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
@@ -50,6 +50,7 @@
 #include "sema.h"
 #include "priv-io.h"
 #include "debug.h"
+#include "sys-util.h"
 
 
 /* FIXME: Optimize.  */
@@ -74,7 +75,7 @@ static struct
      that dup'ed file descriptors are closed before the file
      descriptors from which they are dup'ed are closed, ensures that
      the handle or socket is always valid, and shared among all file
-     descriptors refering to the same underlying object.
+     descriptors referring to the same underlying object.
 
      The logic behind this is that there is only one reason for us to
      dup file descriptors anyway: to allow simpler book-keeping of
@@ -82,7 +83,8 @@ static struct
      want to close something.  Using the same handle for these
      duplicates works just fine.  */
   int dup_from;
-} fd_table[MAX_SLAFD];  
+} fd_table[MAX_SLAFD];
+DEFINE_STATIC_LOCK (fd_table_lock);
 
 
 /* Returns the FD or -1 on resource limit.  */
@@ -91,6 +93,8 @@ new_fd (void)
 {
   int idx;
 
+  LOCK (fd_table_lock);
+
   for (idx = 0; idx < MAX_SLAFD; idx++)
     if (! fd_table[idx].used)
       break;
@@ -98,14 +102,18 @@ new_fd (void)
   if (idx == MAX_SLAFD)
     {
       gpg_err_set_errno (EIO);
-      return -1;
+      idx = -1;
+    }
+  else
+    {
+      fd_table[idx].used = 1;
+      fd_table[idx].handle = INVALID_HANDLE_VALUE;
+      fd_table[idx].socket = INVALID_SOCKET;
+      fd_table[idx].rvid = 0;
+      fd_table[idx].dup_from = -1;
     }
 
-  fd_table[idx].used = 1;
-  fd_table[idx].handle = INVALID_HANDLE_VALUE;
-  fd_table[idx].socket = INVALID_SOCKET;
-  fd_table[idx].rvid = 0;
-  fd_table[idx].dup_from = -1;
+  UNLOCK (fd_table_lock);
 
   return idx;
 }
@@ -114,20 +122,24 @@ new_fd (void)
 void
 release_fd (int fd)
 {
-  if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+  if (fd < 0 || fd >= MAX_SLAFD)
     return;
 
-  fd_table[fd].used = 0;
-  fd_table[fd].handle = INVALID_HANDLE_VALUE;
-  fd_table[fd].socket = INVALID_SOCKET;
-  fd_table[fd].rvid = 0;
-  fd_table[fd].dup_from = -1;
+  LOCK (fd_table_lock);
+
+  if (fd_table[fd].used)
+    {
+      fd_table[fd].used = 0;
+      fd_table[fd].handle = INVALID_HANDLE_VALUE;
+      fd_table[fd].socket = INVALID_SOCKET;
+      fd_table[fd].rvid = 0;
+      fd_table[fd].dup_from = -1;
+    }
+
+  UNLOCK (fd_table_lock);
 }
 
 
-#define pid_to_handle(a) ((HANDLE)(a))
-#define handle_to_pid(a) ((int)(a))
-#define fd_to_handle(a)  ((HANDLE)(a))
 #define handle_to_fd(a)  ((int)(a))
 
 #define READBUF_SIZE 4096
@@ -150,7 +162,7 @@ struct reader_context_s
 {
   HANDLE file_hd;
   int file_sock;
-  HANDLE thread_hd;    
+  HANDLE thread_hd;
   int refcount;
 
   DECLARE_LOCK (mutex);
@@ -160,12 +172,14 @@ struct reader_context_s
   int eof_shortcut;
   int error;
   int error_code;
-  
+
   /* This is manually reset.  */
   HANDLE have_data_ev;
   /* This is automatically reset.  */
   HANDLE have_space_ev;
-  HANDLE stopped;
+  /* This is manually reset but actually only triggered once.  */
+  HANDLE close_ev;
+
   size_t readpos, writepos;
   char buffer[READBUF_SIZE];
 };
@@ -185,11 +199,11 @@ struct writer_context_s
 {
   HANDLE file_hd;
   int file_sock;
-  HANDLE thread_hd;    
+  HANDLE thread_hd;
   int refcount;
 
   DECLARE_LOCK (mutex);
-  
+
   int stop_me;
   int error;
   int error_code;
@@ -197,8 +211,8 @@ struct writer_context_s
   /* This is manually reset.  */
   HANDLE have_data;
   HANDLE is_empty;
-  HANDLE stopped;
-  size_t nbytes; 
+  HANDLE close_ev;
+  size_t nbytes;
   char buffer[WRITEBUF_SIZE];
 };
 
@@ -260,49 +274,15 @@ set_synchronize (HANDLE hd)
 }
 
 
-/* Return 1 if HD refers to a socket, 0 if it does not refer to a
-   socket, and -1 for unknown (autodetect).  */
-static int
-is_socket (HANDLE hd)
-{
-#ifdef HAVE_W32CE_SYSTEM
-  return -1;
-#else
-  /* We need to figure out whether we are working on a socket or on a
-     handle.  A trivial way would be to check for the return code of
-     recv and see if it is WSAENOTSOCK.  However the recv may block
-     after the server process died and thus the destroy_reader will
-     hang.  Another option is to use getsockopt to test whether it is
-     a socket.  The bug here is that once a socket with a certain
-     values has been opened, closed and later a CreatePipe returned
-     the same value (i.e. handle), getsockopt still believes it is a
-     socket.  What we do now is to use a combination of GetFileType
-     and GetNamedPipeInfo.  The specs say that the latter may be used
-     on anonymous pipes as well.  Note that there are claims that
-     since winsocket version 2 ReadFile may be used on a socket but
-     only if it is supported by the service provider.  Tests on a
-     stock XP using a local TCP socket show that it does not work.  */
-  DWORD dummyflags, dummyoutsize, dummyinsize, dummyinst;
-
-  if (GetFileType (hd) == FILE_TYPE_PIPE
-      && !GetNamedPipeInfo (hd, &dummyflags, &dummyoutsize,
-                            &dummyinsize, &dummyinst))
-    return 1; /* Function failed; thus we assume it is a socket.  */
-  else
-    return 0; /* Success; this is not a socket.  */
-#endif
-}
-
-
-static DWORD CALLBACK 
+static DWORD CALLBACK
 reader (void *arg)
 {
   struct reader_context_s *ctx = arg;
   int nbytes;
   DWORD nread;
   int sock;
-  TRACE_BEG1 (DEBUG_SYSIO, "gpgme:reader", ctx->file_hd,
-             "thread=%p", ctx->thread_hd);
+  TRACE_BEG2 (DEBUG_SYSIO, "gpgme:reader", ctx->file_hd,
+             "file_sock=%d, thread=%p", ctx->file_sock, ctx->thread_hd);
 
   if (ctx->file_hd != INVALID_HANDLE_VALUE)
     sock = 0;
@@ -315,7 +295,7 @@ reader (void *arg)
       /* Leave a 1 byte gap so that we can see whether it is empty or
         full.  */
       if ((ctx->writepos + 1) % READBUF_SIZE == ctx->readpos)
-       { 
+       {
          /* Wait for space.  */
          if (!ResetEvent (ctx->have_space_ev))
            TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
@@ -335,7 +315,7 @@ reader (void *arg)
       if (nbytes > READBUF_SIZE - ctx->writepos)
        nbytes = READBUF_SIZE - ctx->writepos;
       UNLOCK (ctx->mutex);
-      
+
       TRACE_LOG2 ("%s %d bytes", sock? "receiving":"reading", nbytes);
 
       if (sock)
@@ -353,6 +333,21 @@ reader (void *arg)
                 }
               else
                 {
+                  /* Check whether the shutdown triggered the error -
+                     no need to to print a warning in this case.  */
+                  if ( ctx->error_code == WSAECONNABORTED
+                       || ctx->error_code == WSAECONNRESET)
+                    {
+                      LOCK (ctx->mutex);
+                      if (ctx->stop_me)
+                        {
+                          UNLOCK (ctx->mutex);
+                          TRACE_LOG ("got shutdown");
+                          break;
+                        }
+                      UNLOCK (ctx->mutex);
+                    }
+
                   ctx->error = 1;
                   TRACE_LOG1 ("recv error: ec=%d", ctx->error_code);
                 }
@@ -394,8 +389,9 @@ reader (void *arg)
          UNLOCK (ctx->mutex);
          break;
         }
+
       TRACE_LOG1 ("got %u bytes", nread);
-      
+
       ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE;
       if (!SetEvent (ctx->have_data_ev))
        TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
@@ -404,10 +400,19 @@ reader (void *arg)
     }
   /* Indicate that we have an error or EOF.  */
   if (!SetEvent (ctx->have_data_ev))
-       TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
-                   (int) GetLastError ());
-  SetEvent (ctx->stopped);
-  
+    TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
+                (int) GetLastError ());
+
+  TRACE_LOG ("waiting for close");
+  WaitForSingleObject (ctx->close_ev, INFINITE);
+
+  CloseHandle (ctx->close_ev);
+  CloseHandle (ctx->have_data_ev);
+  CloseHandle (ctx->have_space_ev);
+  CloseHandle (ctx->thread_hd);
+  DESTROY_LOCK (ctx->mutex);
+  free (ctx);
+
   return TRACE_SUC ();
 }
 
@@ -424,7 +429,7 @@ create_reader (int fd)
   memset (&sec_attr, 0, sizeof sec_attr);
   sec_attr.nLength = sizeof sec_attr;
   sec_attr.bInheritHandle = FALSE;
-  
+
   ctx = calloc (1, sizeof *ctx);
   if (!ctx)
     {
@@ -435,8 +440,12 @@ create_reader (int fd)
   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
     {
       TRACE_SYSERR (EIO);
+      free (ctx);
       return NULL;
     }
+  TRACE_LOG4 ("fd=%d -> handle=%p socket=%d dupfrom=%d",
+              fd, fd_table[fd].handle, fd_table[fd].socket,
+              fd_table[fd].dup_from);
   ctx->file_hd = fd_table[fd].handle;
   ctx->file_sock = fd_table[fd].socket;
 
@@ -445,16 +454,16 @@ create_reader (int fd)
   if (ctx->have_data_ev)
     ctx->have_space_ev = CreateEvent (&sec_attr, FALSE, TRUE, NULL);
   if (ctx->have_space_ev)
-    ctx->stopped = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
-  if (!ctx->have_data_ev || !ctx->have_space_ev || !ctx->stopped)
+    ctx->close_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
+  if (!ctx->have_data_ev || !ctx->have_space_ev || !ctx->close_ev)
     {
       TRACE_LOG1 ("CreateEvent failed: ec=%d", (int) GetLastError ());
       if (ctx->have_data_ev)
        CloseHandle (ctx->have_data_ev);
       if (ctx->have_space_ev)
        CloseHandle (ctx->have_space_ev);
-      if (ctx->stopped)
-       CloseHandle (ctx->stopped);
+      if (ctx->close_ev)
+       CloseHandle (ctx->close_ev);
       free (ctx);
       /* FIXME: Translate the error code.  */
       TRACE_SYSERR (EIO);
@@ -479,12 +488,12 @@ create_reader (int fd)
        CloseHandle (ctx->have_data_ev);
       if (ctx->have_space_ev)
        CloseHandle (ctx->have_space_ev);
-      if (ctx->stopped)
-       CloseHandle (ctx->stopped);
+      if (ctx->close_ev)
+       CloseHandle (ctx->close_ev);
       free (ctx);
       TRACE_SYSERR (EIO);
       return NULL;
-    }    
+    }
   else
     {
       /* We set the priority of the thread higher because we know that
@@ -498,6 +507,9 @@ create_reader (int fd)
 }
 
 
+/* Prepare destruction of the reader thread for CTX.  Returns 0 if a
+   call to this function is sufficient and destroy_reader_finish shall
+   not be called.  */
 static void
 destroy_reader (struct reader_context_s *ctx)
 {
@@ -509,7 +521,7 @@ destroy_reader (struct reader_context_s *ctx)
       return;
     }
   ctx->stop_me = 1;
-  if (ctx->have_space_ev) 
+  if (ctx->have_space_ev)
     SetEvent (ctx->have_space_ev);
   UNLOCK (ctx->mutex);
 
@@ -529,24 +541,32 @@ destroy_reader (struct reader_context_s *ctx)
     }
 #endif
 
-  TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
-         "waiting for termination of thread %p", ctx->thread_hd);
-  WaitForSingleObject (ctx->stopped, INFINITE);
-  TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
-         "thread %p has terminated", ctx->thread_hd);
-    
-  if (ctx->stopped)
-    CloseHandle (ctx->stopped);
-  if (ctx->have_data_ev)
-    CloseHandle (ctx->have_data_ev);
-  if (ctx->have_space_ev)
-    CloseHandle (ctx->have_space_ev);
-  CloseHandle (ctx->thread_hd);
-  DESTROY_LOCK (ctx->mutex);
-  free (ctx);
+  /* The reader thread is usually blocking in recv or ReadFile.  If
+     the peer does not send an EOF or breaks the pipe the WFSO might
+     get stuck waiting for the termination of the reader thread.  This
+     happens quite often with sockets, thus we definitely need to get
+     out of the recv.  A shutdown does this nicely.  For handles
+     (i.e. pipes) it would also be nice to cancel the operation, but
+     such a feature is only available since Vista.  Thus we need to
+     dlopen that syscall.  */
+  if (ctx->file_hd != INVALID_HANDLE_VALUE)
+    {
+      /* Fixme: Call CancelSynchronousIo (handle_of_thread).  */
+    }
+  else if (ctx->file_sock != INVALID_SOCKET)
+    {
+      if (shutdown (ctx->file_sock, 2))
+        TRACE2 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
+                "shutdown socket %d failed: %s",
+                ctx->file_sock, (int) WSAGetLastError ());
+    }
+
+  /* After setting this event CTX is void. */
+  SetEvent (ctx->close_ev);
 }
 
 
+
 /* Find a reader context or create a new one.  Note that the reader
    context will last until a _gpgme_io_close.  */
 static struct reader_context_s *
@@ -583,26 +603,6 @@ find_reader (int fd, int start_it)
 }
 
 
-static void
-kill_reader (int fd)
-{
-  int i;
-
-  LOCK (reader_table_lock);
-  for (i = 0; i < reader_table_size; i++)
-    {
-      if (reader_table[i].used && reader_table[i].fd == fd)
-       {
-         destroy_reader (reader_table[i].context);
-         reader_table[i].context = NULL;
-         reader_table[i].used = 0;
-         break;
-       }
-    }
-  UNLOCK (reader_table_lock);
-}
-
-
 int
 _gpgme_io_read (int fd, void *buffer, size_t count)
 {
@@ -610,7 +610,7 @@ _gpgme_io_read (int fd, void *buffer, size_t count)
   struct reader_context_s *ctx;
   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
              "buffer=%p, count=%u", buffer, count);
-  
+
   ctx = find_reader (fd, 1);
   if (!ctx)
     {
@@ -630,7 +630,7 @@ _gpgme_io_read (int fd, void *buffer, size_t count)
       TRACE_LOG1 ("data from thread %p available", ctx->thread_hd);
       LOCK (ctx->mutex);
     }
-  
+
   if (ctx->readpos == ctx->writepos || ctx->error)
     {
       UNLOCK (ctx->mutex);
@@ -645,7 +645,7 @@ _gpgme_io_read (int fd, void *buffer, size_t count)
       gpg_err_set_errno (ctx->error_code);
       return TRACE_SYSRES (-1);
     }
-  
+
   nread = ctx->readpos < ctx->writepos
     ? ctx->writepos - ctx->readpos
     : READBUF_SIZE - ctx->readpos;
@@ -674,7 +674,7 @@ _gpgme_io_read (int fd, void *buffer, size_t count)
       return TRACE_SYSRES (-1);
     }
   UNLOCK (ctx->mutex);
-  
+
   TRACE_LOGBUF (buffer, nread);
   return TRACE_SYSRES (nread);
 }
@@ -683,14 +683,14 @@ _gpgme_io_read (int fd, void *buffer, size_t count)
 /* The writer does use a simple buffering strategy so that we are
    informed about write errors as soon as possible (i. e. with the the
    next call to the write function.  */
-static DWORD CALLBACK 
+static DWORD CALLBACK
 writer (void *arg)
 {
   struct writer_context_s *ctx = arg;
   DWORD nwritten;
   int sock;
-  TRACE_BEG1 (DEBUG_SYSIO, "gpgme:writer", ctx->file_hd,
-             "thread=%p", ctx->thread_hd);
+  TRACE_BEG2 (DEBUG_SYSIO, "gpgme:writer", ctx->file_hd,
+             "file_sock=%d, thread=%p", ctx->file_sock, ctx->thread_hd);
 
   if (ctx->file_hd != INVALID_HANDLE_VALUE)
     sock = 0;
@@ -700,13 +700,13 @@ writer (void *arg)
   for (;;)
     {
       LOCK (ctx->mutex);
-      if (ctx->stop_me)
+      if (ctx->stop_me && !ctx->nbytes)
        {
          UNLOCK (ctx->mutex);
          break;
         }
       if (!ctx->nbytes)
-       { 
+       {
          if (!SetEvent (ctx->is_empty))
            TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
          if (!ResetEvent (ctx->have_data))
@@ -717,13 +717,13 @@ writer (void *arg)
          TRACE_LOG ("got data to send");
          LOCK (ctx->mutex);
                }
-      if (ctx->stop_me)
+      if (ctx->stop_me && !ctx->nbytes)
        {
          UNLOCK (ctx->mutex);
          break;
         }
       UNLOCK (ctx->mutex);
-      
+
       TRACE_LOG2 ("%s %d bytes", sock?"sending":"writing", ctx->nbytes);
 
       /* Note that CTX->nbytes is not zero at this point, because
@@ -764,7 +764,7 @@ writer (void *arg)
             }
         }
       TRACE_LOG1 ("wrote %d bytes", (int) nwritten);
-      
+
       LOCK (ctx->mutex);
       ctx->nbytes -= nwritten;
       UNLOCK (ctx->mutex);
@@ -772,7 +772,19 @@ writer (void *arg)
   /* Indicate that we have an error.  */
   if (!SetEvent (ctx->is_empty))
     TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
-  SetEvent (ctx->stopped);
+
+  TRACE_LOG ("waiting for close");
+  WaitForSingleObject (ctx->close_ev, INFINITE);
+
+  if (ctx->nbytes)
+    TRACE_LOG1 ("still %d bytes in buffer at close time", ctx->nbytes);
+
+  CloseHandle (ctx->close_ev);
+  CloseHandle (ctx->have_data);
+  CloseHandle (ctx->is_empty);
+  CloseHandle (ctx->thread_hd);
+  DESTROY_LOCK (ctx->mutex);
+  free (ctx);
 
   return TRACE_SUC ();
 }
@@ -797,12 +809,16 @@ create_writer (int fd)
       TRACE_SYSERR (errno);
       return NULL;
     }
-  
+
   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
     {
       TRACE_SYSERR (EIO);
+      free (ctx);
       return NULL;
     }
+  TRACE_LOG4 ("fd=%d -> handle=%p socket=%d dupfrom=%d",
+              fd, fd_table[fd].handle, fd_table[fd].socket,
+              fd_table[fd].dup_from);
   ctx->file_hd = fd_table[fd].handle;
   ctx->file_sock = fd_table[fd].socket;
 
@@ -811,16 +827,16 @@ create_writer (int fd)
   if (ctx->have_data)
     ctx->is_empty  = CreateEvent (&sec_attr, TRUE, TRUE, NULL);
   if (ctx->is_empty)
-    ctx->stopped = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
-  if (!ctx->have_data || !ctx->is_empty || !ctx->stopped)
+    ctx->close_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
+  if (!ctx->have_data || !ctx->is_empty || !ctx->close_ev)
     {
       TRACE_LOG1 ("CreateEvent failed: ec=%d", (int) GetLastError ());
       if (ctx->have_data)
        CloseHandle (ctx->have_data);
       if (ctx->is_empty)
        CloseHandle (ctx->is_empty);
-      if (ctx->stopped)
-       CloseHandle (ctx->stopped);
+      if (ctx->close_ev)
+       CloseHandle (ctx->close_ev);
       free (ctx);
       /* FIXME: Translate the error code.  */
       TRACE_SYSERR (EIO);
@@ -845,12 +861,12 @@ create_writer (int fd)
        CloseHandle (ctx->have_data);
       if (ctx->is_empty)
        CloseHandle (ctx->is_empty);
-      if (ctx->stopped)
-       CloseHandle (ctx->stopped);
+      if (ctx->close_ev)
+       CloseHandle (ctx->close_ev);
       free (ctx);
       TRACE_SYSERR (EIO);
       return NULL;
-    }    
+    }
   else
     {
       /* We set the priority of the thread higher because we know
@@ -863,6 +879,7 @@ create_writer (int fd)
   return ctx;
 }
 
+
 static void
 destroy_writer (struct writer_context_s *ctx)
 {
@@ -874,10 +891,13 @@ destroy_writer (struct writer_context_s *ctx)
       return;
     }
   ctx->stop_me = 1;
-  if (ctx->have_data) 
+  if (ctx->have_data)
     SetEvent (ctx->have_data);
   UNLOCK (ctx->mutex);
 
+  /* Give the writer a chance to flush the buffer.  */
+  WaitForSingleObject (ctx->is_empty, INFINITE);
+
 #ifdef HAVE_W32CE_SYSTEM
   /* Scenario: We never create a full pipe, but already started
      writing more than the pipe buffer.  Then we need to unblock the
@@ -891,22 +911,9 @@ destroy_writer (struct writer_context_s *ctx)
              "unblock control call failed for thread %p", ctx->thread_hd);
     }
 #endif
-  
-  TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
-         "waiting for termination of thread %p", ctx->thread_hd);
-  WaitForSingleObject (ctx->stopped, INFINITE);
-  TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
-         "thread %p has terminated", ctx->thread_hd);
-  
-  if (ctx->stopped)
-    CloseHandle (ctx->stopped);
-  if (ctx->have_data)
-    CloseHandle (ctx->have_data);
-  if (ctx->is_empty)
-    CloseHandle (ctx->is_empty);
-  CloseHandle (ctx->thread_hd);
-  DESTROY_LOCK (ctx->mutex);
-  free (ctx);
+
+  /* After setting this event CTX is void.  */
+  SetEvent (ctx->close_ev);
 }
 
 
@@ -937,7 +944,7 @@ find_writer (int fd, int start_it)
     {
       wt = create_writer (fd);
       writer_table[i].fd = fd;
-      writer_table[i].context = wt; 
+      writer_table[i].context = wt;
       writer_table[i].used = 1;
     }
 
@@ -946,26 +953,6 @@ find_writer (int fd, int start_it)
 }
 
 
-static void
-kill_writer (int fd)
-{
-  int i;
-
-  LOCK (writer_table_lock);
-  for (i = 0; i < writer_table_size; i++)
-    {
-      if (writer_table[i].used && writer_table[i].fd == fd)
-       {
-         destroy_writer (writer_table[i].context);
-         writer_table[i].context = NULL;
-         writer_table[i].used = 0;
-         break;
-       }
-    }
-  UNLOCK (writer_table_lock);
-}
-
-
 int
 _gpgme_io_write (int fd, const void *buffer, size_t count)
 {
@@ -1012,7 +999,7 @@ _gpgme_io_write (int fd, const void *buffer, size_t count)
       return TRACE_SYSRES (-1);
     }
 
-  /* If no error occured, the number of bytes in the buffer must be
+  /* If no error occurred, the number of bytes in the buffer must be
      zero.  */
   assert (!ctx->nbytes);
 
@@ -1095,14 +1082,14 @@ _gpgme_io_pipe (int filedes[2], int inherit_idx)
     {
       fd_table[rfd].handle = hd;
       fd_table[wfd].rvid = rvid;
-    }  
+    }
 
 #else
 
   memset (&sec_attr, 0, sizeof (sec_attr));
   sec_attr.nLength = sizeof (sec_attr);
   sec_attr.bInheritHandle = FALSE;
-  
+
   if (!CreatePipe (&rh, &wh, &sec_attr, PIPEBUF_SIZE))
     {
       TRACE_LOG1 ("CreatePipe failed: ec=%d", (int) GetLastError ());
@@ -1186,8 +1173,36 @@ _gpgme_io_close (int fd)
       return TRACE_SYSRES (-1);
     }
 
-  kill_reader (fd);
-  kill_writer (fd);
+  TRACE_LOG4 ("fd=%d -> handle=%p socket=%d dupfrom=%d",
+              fd, fd_table[fd].handle, fd_table[fd].socket,
+              fd_table[fd].dup_from);
+
+  LOCK (reader_table_lock);
+  for (i = 0; i < reader_table_size; i++)
+    {
+      if (reader_table[i].used && reader_table[i].fd == fd)
+       {
+         destroy_reader (reader_table[i].context);
+         reader_table[i].context = NULL;
+         reader_table[i].used = 0;
+         break;
+       }
+    }
+  UNLOCK (reader_table_lock);
+
+  LOCK (writer_table_lock);
+  for (i = 0; i < writer_table_size; i++)
+    {
+      if (writer_table[i].used && writer_table[i].fd == fd)
+       {
+         destroy_writer (writer_table[i].context);
+         writer_table[i].context = NULL;
+         writer_table[i].used = 0;
+         break;
+       }
+    }
+  UNLOCK (writer_table_lock);
+
   LOCK (notify_table_lock);
   for (i = 0; i < DIM (notify_table); i++)
     {
@@ -1210,7 +1225,7 @@ _gpgme_io_close (int fd)
       if (fd_table[fd].handle != INVALID_HANDLE_VALUE)
        {
          if (!CloseHandle (fd_table[fd].handle))
-           { 
+           {
              TRACE_LOG1 ("CloseHandle failed: ec=%d", (int) GetLastError ());
              /* FIXME: Should translate the error code.  */
              gpg_err_set_errno (EIO);
@@ -1220,7 +1235,7 @@ _gpgme_io_close (int fd)
       else if (fd_table[fd].socket != INVALID_SOCKET)
        {
          if (closesocket (fd_table[fd].socket))
-           { 
+           {
              TRACE_LOG1 ("closesocket failed: ec=%d", (int) WSAGetLastError ());
              /* FIXME: Should translate the error code.  */
              gpg_err_set_errno (EIO);
@@ -1231,7 +1246,7 @@ _gpgme_io_close (int fd)
     }
 
   release_fd (fd);
-      
+
   return TRACE_SYSRES (0);
 }
 
@@ -1290,7 +1305,7 @@ build_commandline (char **argv, int fd0, int fd0_isnull,
 
   p = fdbuf;
   *p = 0;
-  
+
   if (fd0 != -1)
     {
       if (fd0_isnull)
@@ -1317,7 +1332,7 @@ build_commandline (char **argv, int fd0, int fd0_isnull,
     }
   strcpy (p, "-&S2=null ");
   p += strlen (p);
-  
+
   n = strlen (fdbuf);
   for (i=0; (s = argv[i]); i++)
     {
@@ -1334,7 +1349,7 @@ build_commandline (char **argv, int fd0, int fd0_isnull,
     return NULL;
 
   p = stpcpy (p, fdbuf);
-  for (i = 0; argv[i]; i++) 
+  for (i = 0; argv[i]; i++)
     {
       if (!i)
         continue; /* Ignore argv[0].  */
@@ -1359,7 +1374,7 @@ build_commandline (char **argv, int fd0, int fd0_isnull,
         p = stpcpy (p, argv[i]);
     }
 
-  return buf;  
+  return buf;
 }
 #else
 static char *
@@ -1369,7 +1384,7 @@ build_commandline (char **argv)
   int n = 0;
   char *buf;
   char *p;
-  
+
   /* We have to quote some things because under Windows the program
      parses the commandline and does some unquoting.  We enclose the
      whole argument in double-quotes, and escape literal double-quotes
@@ -1469,7 +1484,7 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
          gpg_err_set_errno (EBADF);
          return TRACE_SYSRES (-1);
        }
-      
+
       if (fd_list[i].dup_to == 0)
        {
          fd_in = fd_list[i].fd;
@@ -1556,6 +1571,7 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
   int debug_me = 0;
   int tmp_fd;
   char *tmp_name;
+  const char *spawnhelper;
 
   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
              "path=%s", path);
@@ -1581,13 +1597,13 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
   args = calloc (2 + i + 1, sizeof (*args));
   args[0] = (char *) _gpgme_get_w32spawn_path ();
   args[1] = tmp_name;
-  args[2] = path;
+  args[2] = (char *)path;
   memcpy (&args[3], &argv[1], i * sizeof (*args));
 
   memset (&sec_attr, 0, sizeof sec_attr);
   sec_attr.nLength = sizeof sec_attr;
   sec_attr.bInheritHandle = FALSE;
+
   arg_string = build_commandline (args);
   free (args);
   if (!arg_string)
@@ -1605,12 +1621,37 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
   si.hStdOutput = INVALID_HANDLE_VALUE;
   si.hStdError = INVALID_HANDLE_VALUE;
 
-  cr_flags |= CREATE_SUSPENDED; 
-#ifndef HAVE_W32CE_SYSTEM
-  cr_flags |= DETACHED_PROCESS;
+  cr_flags |= CREATE_SUSPENDED;
+  if ((flags & IOSPAWN_FLAG_DETACHED))
+    cr_flags |= DETACHED_PROCESS;
   cr_flags |= GetPriorityClass (GetCurrentProcess ());
-#endif
-  if (!CreateProcessA (_gpgme_get_w32spawn_path (),
+  spawnhelper = _gpgme_get_w32spawn_path ();
+  if (!spawnhelper)
+    {
+      /* This is a common mistake for new users of gpgme not to include
+         gpgme-w32spawn.exe with their binary. So we want to make
+         this transparent to developers. If users have somehow messed
+         up their installation this should also be properly communicated
+         as otherwise calls to gnupg will result in unsupported protocol
+         errors that do not explain a lot. */
+      char *msg;
+      gpgrt_asprintf (&msg, "gpgme-w32spawn.exe was not found in the "
+                            "detected installation directory of GpgME"
+                            "\n\t\"%s\"\n\n"
+                            "Crypto operations will not work.\n\n"
+                            "If you see this it indicates a problem "
+                            "with your installation.\n"
+                            "Please report the problem to your "
+                            "distributor of GpgME.\n\n"
+                            "Developer's Note: The install dir can be "
+                            "manually set with: gpgme_set_global_flag",
+                            _gpgme_get_inst_dir ());
+      MessageBoxA (NULL, msg, "GpgME not installed correctly", MB_OK);
+      gpgrt_free (msg);
+      gpg_err_set_errno (EIO);
+      return TRACE_SYSRES (-1);
+    }
+  if (!CreateProcessA (spawnhelper,
                       arg_string,
                       &sec_attr,     /* process security attributes */
                       &sec_attr,     /* thread security attributes */
@@ -1621,7 +1662,8 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
                       &si,           /* startup information */
                       &pi))          /* returns process information */
     {
-      TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
+      int lasterr = (int)GetLastError ();
+      TRACE_LOG1 ("CreateProcess failed: ec=%d", lasterr);
       free (arg_string);
       close (tmp_fd);
       DeleteFileA (tmp_name);
@@ -1639,10 +1681,15 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
   /* Insert the inherited handles.  */
   for (i = 0; fd_list[i].fd != -1; i++)
     {
-      HANDLE hd;
+      int fd = fd_list[i].fd;
+      HANDLE ohd = INVALID_HANDLE_VALUE;
+      HANDLE hd = INVALID_HANDLE_VALUE;
 
       /* Make it inheritable for the wrapper process.  */
-      if (!DuplicateHandle (GetCurrentProcess(), fd_to_handle (fd_list[i].fd),
+      if (fd >= 0 && fd < MAX_SLAFD && fd_table[fd].used)
+       ohd = fd_table[fd].handle;
+
+      if (!DuplicateHandle (GetCurrentProcess(), ohd,
                            pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
        {
          TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
@@ -1663,7 +1710,7 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
       /* Return the child name of this handle.  */
       fd_list[i].peer_name = handle_to_fd (hd);
     }
-  
+
   /* Write the handle translation information to the temporary
      file.  */
   {
@@ -1685,7 +1732,7 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
       {
        /* Strip the newline.  */
        len = strlen (line) - 1;
-       
+
        /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx.  */
        snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d  \n",
                  fd_list[i].fd, fd_list[i].dup_to,
@@ -1712,16 +1759,16 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
 
   TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
              "dwProcessID=%d, dwThreadId=%d",
-             pi.hProcess, pi.hThread, 
+             pi.hProcess, pi.hThread,
              (int) pi.dwProcessId, (int) pi.dwThreadId);
-  
+
   if (r_pid)
     *r_pid = (pid_t)pi.dwProcessId;
 
-  
+
   if (ResumeThread (pi.hThread) < 0)
     TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
-  
+
   if (!CloseHandle (pi.hThread))
     TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
                (int) GetLastError ());
@@ -1768,7 +1815,9 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
              "nfds=%u, nonblock=%u", nfds, nonblock);
 
+#if 0
  restart:
+#endif
   TRACE_SEQ (dbg_help, "select on [ ");
   any = 0;
   nwait = 0;
@@ -1783,7 +1832,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
          if (fds[i].for_read)
            {
              struct reader_context_s *ctx = find_reader (fds[i].fd,1);
-             
+
              if (!ctx)
                TRACE_LOG1 ("error: no reader for FD 0x%x (ignored)",
                            fds[i].fd);
@@ -1806,7 +1855,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
          else if (fds[i].for_write)
            {
              struct writer_context_s *ctx = find_writer (fds[i].fd,1);
-              
+
              if (!ctx)
                TRACE_LOG1 ("error: no writer for FD 0x%x (ignored)",
                            fds[i].fd);
@@ -1829,7 +1878,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
         }
     }
   TRACE_END (dbg_help, "]");
-  if (!any) 
+  if (!any)
     return TRACE_SYSRES (0);
 
   code = WaitForMultipleObjects (nwait, waitbuf, 0, nonblock ? 0 : 1000);
@@ -1869,7 +1918,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
        {
          int k;
          int j = handle_to_fd (waitbuf[i]);
-          
+
          TRACE_LOG1 ("WFMO invalid handle %d removed", j);
          for (k = 0 ; k < nfds; k++)
            {
@@ -1890,7 +1939,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
       TRACE_LOG1 ("WFMO returned %d", code);
       count = -1;
     }
-  
+
   if (count > 0)
     {
       TRACE_SEQ (dbg_help, "select OK [ ");
@@ -1910,7 +1959,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
       /* FIXME: Should determine a proper error code.  */
       gpg_err_set_errno (EIO);
     }
-  
+
   return TRACE_SYSRES (count);
 }
 
@@ -1960,7 +2009,7 @@ _gpgme_io_dup (int fd)
   newfd = new_fd();
   if (newfd == -1)
     return TRACE_SYSRES (-1);
-  
+
   fd_table[newfd].handle = fd_table[fd].handle;
   fd_table[newfd].socket = fd_table[fd].socket;
   fd_table[newfd].rvid = fd_table[fd].rvid;
@@ -2057,7 +2106,7 @@ _gpgme_io_socket (int domain, int type, int proto)
   fd = new_fd();
   if (fd == -1)
     return TRACE_SYSRES (-1);
-      
+
   res = socket (domain, type, proto);
   if (res == INVALID_SOCKET)
     {
@@ -2068,8 +2117,8 @@ _gpgme_io_socket (int domain, int type, int proto)
   fd_table[fd].socket = res;
 
   TRACE_SUC2 ("socket=0x%x (0x%x)", fd, fd_table[fd].socket);
-  
-  return res;
+
+  return fd;
 }
 
 
@@ -2086,7 +2135,7 @@ _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
       gpg_err_set_errno (EBADF);
       return TRACE_SYSRES (-1);
     }
-    
+
   res = connect (fd_table[fd].socket, addr, addrlen);
   if (res)
     {