g10: Add new delete operations that allow more flags.
[gpgme.git] / src / w32-io.c
index e83aa0b..eed8a00 100644 (file)
@@ -1,19 +1,19 @@
 /* w32-io.c - W32 API I/O functions.
    Copyright (C) 2000 Werner Koch (dd9jn)
-   Copyright (C) 2001, 2002, 2003, 2004, 2007 g10 Code GmbH
+   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
 #include <string.h>
 #include <assert.h>
 #include <errno.h>
-#include <signal.h>
 #include <fcntl.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <windows.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
 #include <io.h>
 
 #include "util.h"
+
+#ifdef HAVE_W32CE_SYSTEM
+#include <assuan.h>
+#include <winioctl.h>
+#define GPGCEDEV_IOCTL_UNBLOCK                                        \
+  CTL_CODE (FILE_DEVICE_STREAMS, 2050, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define GPGCEDEV_IOCTL_ASSIGN_RVID                                    \
+  CTL_CODE (FILE_DEVICE_STREAMS, 2051, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#endif
+
 #include "sema.h"
 #include "priv-io.h"
 #include "debug.h"
+#include "sys-util.h"
+
+
+/* FIXME: Optimize.  */
+#define MAX_SLAFD 512
+
+static struct
+{
+  int used;
+
+  /* If this is not INVALID_HANDLE_VALUE, then it's a handle.  */
+  HANDLE handle;
+
+  /* If this is not INVALID_SOCKET, then it's a Windows socket.  */
+  int socket;
+
+  /* If this is not 0, then it's a rendezvous ID for the pipe server.  */
+  int rvid;
+
+  /* DUP_FROM is -1 if this file descriptor was allocated by pipe or
+     socket functions.  Only then should the handle or socket be
+     destroyed when this FD is closed.  This, together with the fact
+     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 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
+     file descriptors shared between GPGME and libassuan, which both
+     want to close something.  Using the same handle for these
+     duplicates works just fine.  */
+  int dup_from;
+} fd_table[MAX_SLAFD];
+DEFINE_STATIC_LOCK (fd_table_lock);
+
+
+/* Returns the FD or -1 on resource limit.  */
+int
+new_fd (void)
+{
+  int idx;
+
+  LOCK (fd_table_lock);
+
+  for (idx = 0; idx < MAX_SLAFD; idx++)
+    if (! fd_table[idx].used)
+      break;
+
+  if (idx == MAX_SLAFD)
+    {
+      gpg_err_set_errno (EIO);
+      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;
+    }
+
+  UNLOCK (fd_table_lock);
+
+  return idx;
+}
+
+
+void
+release_fd (int fd)
+{
+  if (fd < 0 || fd >= MAX_SLAFD)
+    return;
+
+  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);
+}
 
-/* We assume that a HANDLE can be represented by an int which should
-   be true for all i386 systems (HANDLE is defined as void *) and
-   these are the only systems for which Windows is available.  Further
-   we assume that -1 denotes an invalid handle.  */
 
-#define fd_to_handle(a)  ((HANDLE)(a))
 #define handle_to_fd(a)  ((int)(a))
-#define pid_to_handle(a) ((HANDLE)(a))
-#define handle_to_pid(a) ((int)(a))
 
 #define READBUF_SIZE 4096
 #define WRITEBUF_SIZE 4096
 #define PIPEBUF_SIZE  4096
-#define MAX_READERS 40
-#define MAX_WRITERS 40
+#define MAX_READERS 64
+#define MAX_WRITERS 64
 
 static struct
 {
@@ -62,14 +154,15 @@ static struct
   int fd;
   _gpgme_close_notify_handler_t handler;
   void *value;
-} notify_table[256];
+} notify_table[MAX_SLAFD];
 DEFINE_STATIC_LOCK (notify_table_lock);
 
 
 struct reader_context_s
 {
   HANDLE file_hd;
-  HANDLE thread_hd;    
+  int file_sock;
+  HANDLE thread_hd;
   int refcount;
 
   DECLARE_LOCK (mutex);
@@ -79,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];
 };
@@ -103,11 +198,12 @@ DEFINE_STATIC_LOCK (reader_table_lock);
 struct writer_context_s
 {
   HANDLE file_hd;
-  HANDLE thread_hd;    
+  int file_sock;
+  HANDLE thread_hd;
   int refcount;
 
   DECLARE_LOCK (mutex);
-  
+
   int stop_me;
   int error;
   int error_code;
@@ -115,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];
 };
 
@@ -154,6 +250,9 @@ get_desired_thread_priority (void)
 static HANDLE
 set_synchronize (HANDLE hd)
 {
+#ifdef HAVE_W32CE_SYSTEM
+  return hd;
+#else
   HANDLE new_hd;
 
   /* For NT we have to set the sync flag.  It seems that the only way
@@ -165,23 +264,30 @@ set_synchronize (HANDLE hd)
       TRACE1 (DEBUG_SYSIO, "gpgme:set_synchronize", hd,
              "DuplicateHandle failed: ec=%d", (int) GetLastError ());
       /* FIXME: Should translate the error code.  */
-      errno = EIO;
+      gpg_err_set_errno (EIO);
       return INVALID_HANDLE_VALUE;
     }
 
   CloseHandle (hd);
   return new_hd;
+#endif
 }
 
 
-static DWORD CALLBACK 
+static DWORD CALLBACK
 reader (void *arg)
 {
   struct reader_context_s *ctx = arg;
   int nbytes;
   DWORD nread;
-  TRACE_BEG1 (DEBUG_SYSIO, "gpgme:reader", ctx->file_hd,
-             "thread=%p", ctx->thread_hd);
+  int sock;
+  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;
+  else
+    sock = 1;
 
   for (;;)
     {
@@ -189,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 ());
@@ -209,38 +315,83 @@ reader (void *arg)
       if (nbytes > READBUF_SIZE - ctx->writepos)
        nbytes = READBUF_SIZE - ctx->writepos;
       UNLOCK (ctx->mutex);
-      
-      TRACE_LOG1 ("reading %d bytes", nbytes);
-      if (!ReadFile (ctx->file_hd,
-                    ctx->buffer + ctx->writepos, nbytes, &nread, NULL))
-       {
-         ctx->error_code = (int) GetLastError ();
-         if (ctx->error_code == ERROR_BROKEN_PIPE)
-           {
-             ctx->eof = 1;
-             TRACE_LOG ("got EOF (broken pipe)");
+
+      TRACE_LOG2 ("%s %d bytes", sock? "receiving":"reading", nbytes);
+
+      if (sock)
+        {
+          int n;
+
+          n = recv (ctx->file_sock, ctx->buffer + ctx->writepos, nbytes, 0);
+          if (n < 0)
+            {
+              ctx->error_code = (int) WSAGetLastError ();
+              if (ctx->error_code == ERROR_BROKEN_PIPE)
+                {
+                  ctx->eof = 1;
+                  TRACE_LOG ("got EOF (broken connection)");
+                }
+              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);
+                }
+              break;
             }
-         else
-           {
-             ctx->error = 1;
-             TRACE_LOG1 ("read error: ec=%d", ctx->error_code);
+          nread = n;
+        }
+      else
+        {
+          if (!ReadFile (ctx->file_hd,
+                         ctx->buffer + ctx->writepos, nbytes, &nread, NULL))
+            {
+              ctx->error_code = (int) GetLastError ();
+             /* NOTE (W32CE): Do not ignore ERROR_BUSY!  Check at
+                least stop_me if that happens.  */
+              if (ctx->error_code == ERROR_BROKEN_PIPE)
+                {
+                  ctx->eof = 1;
+                  TRACE_LOG ("got EOF (broken pipe)");
+                }
+              else
+                {
+                  ctx->error = 1;
+                  TRACE_LOG1 ("read error: ec=%d", ctx->error_code);
+                }
+              break;
             }
+        }
+      LOCK (ctx->mutex);
+      if (ctx->stop_me)
+       {
+         UNLOCK (ctx->mutex);
          break;
         }
       if (!nread)
        {
          ctx->eof = 1;
          TRACE_LOG ("got eof");
-         break;
-        }
-      TRACE_LOG1 ("got %u bytes", nread);
-      
-      LOCK (ctx->mutex);
-      if (ctx->stop_me)
-       {
          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,
@@ -249,16 +400,25 @@ 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 ();
 }
 
 
 static struct reader_context_s *
-create_reader (HANDLE fd)
+create_reader (int fd)
 {
   struct reader_context_s *ctx;
   SECURITY_ATTRIBUTES sec_attr;
@@ -269,7 +429,7 @@ create_reader (HANDLE 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)
     {
@@ -277,22 +437,33 @@ create_reader (HANDLE fd)
       return NULL;
     }
 
-  ctx->file_hd = 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;
+
   ctx->refcount = 1;
   ctx->have_data_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
   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);
@@ -302,7 +473,13 @@ create_reader (HANDLE fd)
   ctx->have_data_ev = set_synchronize (ctx->have_data_ev);
   INIT_LOCK (ctx->mutex);
 
+#ifdef HAVE_W32CE_SYSTEM
+  ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, reader, ctx,
+                                STACK_SIZE_PARAM_IS_A_RESERVATION, &tid);
+#else
   ctx->thread_hd = CreateThread (&sec_attr, 0, reader, ctx, 0, &tid);
+#endif
+
   if (!ctx->thread_hd)
     {
       TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ());
@@ -311,12 +488,12 @@ create_reader (HANDLE 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
@@ -330,6 +507,9 @@ create_reader (HANDLE 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)
 {
@@ -341,28 +521,52 @@ 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);
 
-  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);
+#ifdef HAVE_W32CE_SYSTEM
+  /* Scenario: We never create a full pipe, but already started
+     reading.  Then we need to unblock the reader in the pipe driver
+     to make our reader thread notice that we want it to go away.  */
+
+  if (ctx->file_hd != INVALID_HANDLE_VALUE)
+    {
+      if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
+                       NULL, 0, NULL, 0, NULL, NULL))
+       {
+         TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
+                 "unblock control call failed for thread %p", ctx->thread_hd);
+       }
+    }
+#endif
+
+  /* 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 *
@@ -388,13 +592,10 @@ find_reader (int fd, int start_it)
 
   if (i != reader_table_size)
     {
-      rd = create_reader (fd_to_handle (fd));
-      if (rd)
-       {
-         reader_table[i].fd = fd;
-         reader_table[i].context = rd;
-         reader_table[i].used = 1;
-       }
+      rd = create_reader (fd);
+      reader_table[i].fd = fd;
+      reader_table[i].context = rd;
+      reader_table[i].used = 1;
     }
 
   UNLOCK (reader_table_lock);
@@ -402,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)
 {
@@ -429,11 +610,11 @@ _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)
     {
-      errno = EBADF;
+      gpg_err_set_errno (EBADF);
       return TRACE_SYSRES (-1);
     }
   if (ctx->eof_shortcut)
@@ -449,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);
@@ -461,10 +642,10 @@ _gpgme_io_read (int fd, void *buffer, size_t count)
          TRACE_LOG ("EOF but ctx->eof flag not set");
          return 0;
        }
-      errno = ctx->error_code;
+      gpg_err_set_errno (ctx->error_code);
       return TRACE_SYSRES (-1);
     }
-  
+
   nread = ctx->readpos < ctx->writepos
     ? ctx->writepos - ctx->readpos
     : READBUF_SIZE - ctx->readpos;
@@ -479,7 +660,7 @@ _gpgme_io_read (int fd, void *buffer, size_t count)
          TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
          UNLOCK (ctx->mutex);
          /* FIXME: Should translate the error code.  */
-         errno = EIO;
+         gpg_err_set_errno (EIO);
          return TRACE_SYSRES (-1);
        }
     }
@@ -489,11 +670,11 @@ _gpgme_io_read (int fd, void *buffer, size_t count)
                  ctx->have_space_ev, (int) GetLastError ());
       UNLOCK (ctx->mutex);
       /* FIXME: Should translate the error code.  */
-      errno = EIO;
+      gpg_err_set_errno (EIO);
       return TRACE_SYSRES (-1);
     }
   UNLOCK (ctx->mutex);
-  
+
   TRACE_LOGBUF (buffer, nread);
   return TRACE_SYSRES (nread);
 }
@@ -502,24 +683,30 @@ _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;
-  TRACE_BEG1 (DEBUG_SYSIO, "gpgme:writer", ctx->file_hd,
-             "thread=%p", ctx->thread_hd);
+  int sock;
+  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;
+  else
+    sock = 1;
 
   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))
@@ -530,27 +717,54 @@ 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_LOG1 ("writing %d bytes", ctx->nbytes);
+
+      TRACE_LOG2 ("%s %d bytes", sock?"sending":"writing", ctx->nbytes);
+
       /* Note that CTX->nbytes is not zero at this point, because
         _gpgme_io_write always writes at least 1 byte before waking
         us up, unless CTX->stop_me is true, which we catch above.  */
-      if (!WriteFile (ctx->file_hd, ctx->buffer,
-                     ctx->nbytes, &nwritten, NULL))
-       {
-         ctx->error_code = (int) GetLastError ();
-         ctx->error = 1;
-         TRACE_LOG1 ("write error: ec=%d", ctx->error_code);
-         break;
-       }
+      if (sock)
+        {
+          /* We need to try send first because a socket handle can't
+             be used with WriteFile.  */
+          int n;
+
+          n = send (ctx->file_sock, ctx->buffer, ctx->nbytes, 0);
+          if (n < 0)
+            {
+              ctx->error_code = (int) WSAGetLastError ();
+              ctx->error = 1;
+              TRACE_LOG1 ("send error: ec=%d", ctx->error_code);
+              break;
+            }
+          nwritten = n;
+        }
+      else
+        {
+          if (!WriteFile (ctx->file_hd, ctx->buffer,
+                          ctx->nbytes, &nwritten, NULL))
+            {
+             if (GetLastError () == ERROR_BUSY)
+               {
+                 /* Probably stop_me is set now.  */
+                  TRACE_LOG ("pipe busy (unblocked?)");
+                 continue;
+                }
+
+              ctx->error_code = (int) GetLastError ();
+              ctx->error = 1;
+              TRACE_LOG1 ("write error: ec=%d", ctx->error_code);
+              break;
+            }
+        }
       TRACE_LOG1 ("wrote %d bytes", (int) nwritten);
-      
+
       LOCK (ctx->mutex);
       ctx->nbytes -= nwritten;
       UNLOCK (ctx->mutex);
@@ -558,14 +772,26 @@ 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 ();
 }
 
 
 static struct writer_context_s *
-create_writer (HANDLE fd)
+create_writer (int fd)
 {
   struct writer_context_s *ctx;
   SECURITY_ATTRIBUTES sec_attr;
@@ -583,23 +809,34 @@ create_writer (HANDLE fd)
       TRACE_SYSERR (errno);
       return NULL;
     }
-  
-  ctx->file_hd = 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;
+
   ctx->refcount = 1;
   ctx->have_data = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
   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);
@@ -609,7 +846,13 @@ create_writer (HANDLE fd)
   ctx->is_empty = set_synchronize (ctx->is_empty);
   INIT_LOCK (ctx->mutex);
 
+#ifdef HAVE_W32CE_SYSTEM
+  ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, writer, ctx,
+                                STACK_SIZE_PARAM_IS_A_RESERVATION, &tid);
+#else
   ctx->thread_hd = CreateThread (&sec_attr, 0, writer, ctx, 0, &tid );
+#endif
+
   if (!ctx->thread_hd)
     {
       TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ());
@@ -618,12 +861,12 @@ create_writer (HANDLE 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
@@ -636,6 +879,7 @@ create_writer (HANDLE fd)
   return ctx;
 }
 
+
 static void
 destroy_writer (struct writer_context_s *ctx)
 {
@@ -647,25 +891,29 @@ 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);
-  
-  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);
+
+  /* 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
+     writer in the pipe driver to make our writer thread notice that
+     we want it to go away.  */
+
+  if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
+                       NULL, 0, NULL, 0, NULL, NULL))
+    {
+      TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
+             "unblock control call failed for thread %p", ctx->thread_hd);
+    }
+#endif
+
+  /* After setting this event CTX is void.  */
+  SetEvent (ctx->close_ev);
 }
 
 
@@ -694,13 +942,10 @@ find_writer (int fd, int start_it)
 
   if (i != writer_table_size)
     {
-      wt = create_writer (fd_to_handle (fd));
-      if (wt)
-       {
-         writer_table[i].fd = fd;
-         writer_table[i].context = wt; 
-         writer_table[i].used = 1;
-       }
+      wt = create_writer (fd);
+      writer_table[i].fd = fd;
+      writer_table[i].context = wt;
+      writer_table[i].used = 1;
     }
 
   UNLOCK (writer_table_lock);
@@ -708,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)
 {
@@ -739,7 +964,7 @@ _gpgme_io_write (int fd, const void *buffer, size_t count)
   if (count == 0)
     return TRACE_SYSRES (0);
 
-  ctx = find_writer (fd, 0);
+  ctx = find_writer (fd, 1);
   if (!ctx)
     return TRACE_SYSRES (-1);
 
@@ -754,7 +979,7 @@ _gpgme_io_write (int fd, const void *buffer, size_t count)
          TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
          UNLOCK (ctx->mutex);
          /* FIXME: Should translate the error code.  */
-         errno = EIO;
+         gpg_err_set_errno (EIO);
          return TRACE_SYSRES (-1);
        }
       UNLOCK (ctx->mutex);
@@ -768,13 +993,13 @@ _gpgme_io_write (int fd, const void *buffer, size_t count)
     {
       UNLOCK (ctx->mutex);
       if (ctx->error_code == ERROR_NO_DATA)
-        errno = EPIPE;
+        gpg_err_set_errno (EPIPE);
       else
-        errno = EIO;
+        gpg_err_set_errno (EIO);
       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);
 
@@ -790,7 +1015,7 @@ _gpgme_io_write (int fd, const void *buffer, size_t count)
       TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
       UNLOCK (ctx->mutex);
       /* FIXME: Should translate the error code.  */
-      errno = EIO;
+      gpg_err_set_errno (EIO);
       return TRACE_SYSRES (-1);
     }
   if (!SetEvent (ctx->have_data))
@@ -798,7 +1023,7 @@ _gpgme_io_write (int fd, const void *buffer, size_t count)
       TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
       UNLOCK (ctx->mutex);
       /* FIXME: Should translate the error code.  */
-      errno = EIO;
+      gpg_err_set_errno (EIO);
       return TRACE_SYSRES (-1);
     }
   UNLOCK (ctx->mutex);
@@ -810,29 +1035,74 @@ _gpgme_io_write (int fd, const void *buffer, size_t count)
 int
 _gpgme_io_pipe (int filedes[2], int inherit_idx)
 {
+  int rfd;
+  int wfd;
+#ifdef HAVE_W32CE_SYSTEM
+  HANDLE hd;
+  int rvid;
+#else
   HANDLE rh;
   HANDLE wh;
   SECURITY_ATTRIBUTES sec_attr;
+#endif
+
   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
              "inherit_idx=%i (GPGME uses it for %s)",
              inherit_idx, inherit_idx ? "reading" : "writing");
 
+  rfd = new_fd ();
+  if (rfd == -1)
+    return TRACE_SYSRES (-1);
+  wfd = new_fd ();
+  if (wfd == -1)
+    {
+      release_fd (rfd);
+      return TRACE_SYSRES (-1);
+    }
+
+#ifdef HAVE_W32CE_SYSTEM
+  hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx);
+  if (hd == INVALID_HANDLE_VALUE)
+    {
+      TRACE_LOG1 ("_assuan_w32ce_prepare_pipe failed: ec=%d",
+                 (int) GetLastError ());
+      release_fd (rfd);
+      release_fd (wfd);
+      /* FIXME: Should translate the error code.  */
+      gpg_err_set_errno (EIO);
+      return TRACE_SYSRES (-1);
+    }
+
+  if (inherit_idx == 0)
+    {
+      fd_table[rfd].rvid = rvid;
+      fd_table[wfd].handle = hd;
+    }
+  else
+    {
+      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 ());
+      release_fd (rfd);
+      release_fd (wfd);
       /* FIXME: Should translate the error code.  */
-      errno = EIO;
+      gpg_err_set_errno (EIO);
       return TRACE_SYSRES (-1);
     }
 
   /* Make one end inheritable.  */
   if (inherit_idx == 0)
     {
-      struct writer_context_s *ctx;
       HANDLE hd;
       if (!DuplicateHandle (GetCurrentProcess(), rh,
                            GetCurrentProcess(), &hd, 0,
@@ -840,30 +1110,19 @@ _gpgme_io_pipe (int filedes[2], int inherit_idx)
        {
          TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
                      (int) GetLastError ());
+         release_fd (rfd);
+         release_fd (wfd);
          CloseHandle (rh);
          CloseHandle (wh);
          /* FIXME: Should translate the error code.  */
-         errno = EIO;
+         gpg_err_set_errno (EIO);
          return TRACE_SYSRES (-1);
         }
       CloseHandle (rh);
       rh = hd;
-
-      ctx = find_writer (handle_to_fd (wh), 0);
-      assert (ctx == NULL);
-      ctx = find_writer (handle_to_fd (wh), 1);
-      if (!ctx)
-       {
-         CloseHandle (rh);
-         CloseHandle (wh);
-         /* FIXME: Should translate the error code.  */
-         errno = EIO;
-         return TRACE_SYSRES (-1);
-       }
     }
   else if (inherit_idx == 1)
     {
-      struct reader_context_s *ctx;
       HANDLE hd;
       if (!DuplicateHandle( GetCurrentProcess(), wh,
                            GetCurrentProcess(), &hd, 0,
@@ -871,31 +1130,26 @@ _gpgme_io_pipe (int filedes[2], int inherit_idx)
        {
          TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
                      (int) GetLastError ());
+         release_fd (rfd);
+         release_fd (wfd);
          CloseHandle (rh);
          CloseHandle (wh);
          /* FIXME: Should translate the error code.  */
-         errno = EIO;
+         gpg_err_set_errno (EIO);
          return TRACE_SYSRES (-1);
         }
       CloseHandle (wh);
       wh = hd;
-
-      ctx = find_reader (handle_to_fd (rh), 0);
-      assert (ctx == NULL);
-      ctx = find_reader (handle_to_fd (rh), 1);
-      if (!ctx)
-       {
-         CloseHandle (rh);
-         CloseHandle (wh);
-         /* FIXME: Should translate the error code.  */
-         errno = EIO;
-         return TRACE_SYSRES (-1);
-       }
     }
-  
-  filedes[0] = handle_to_fd (rh);
-  filedes[1] = handle_to_fd (wh);
-  return TRACE_SUC2 ("read=%p, write=%p", rh, wh);
+  fd_table[rfd].handle = rh;
+  fd_table[wfd].handle = wh;
+#endif
+
+  filedes[0] = rfd;
+  filedes[1] = wfd;
+  return TRACE_SUC6 ("read=0x%x (%p/0x%x), write=0x%x (%p/0x%x)",
+                    rfd, fd_table[rfd].handle, fd_table[rfd].rvid,
+                    wfd, fd_table[wfd].handle, fd_table[wfd].rvid);
 }
 
 
@@ -905,16 +1159,50 @@ _gpgme_io_close (int fd)
   int i;
   _gpgme_close_notify_handler_t handler = NULL;
   void *value = NULL;
+
   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
 
   if (fd == -1)
     {
-      errno = EBADF;
+      gpg_err_set_errno (EBADF);
       return TRACE_SYSRES (-1);
     }
+  if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+    {
+      gpg_err_set_errno (EBADF);
+      return TRACE_SYSRES (-1);
+    }
+
+  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);
 
-  kill_reader (fd);
-  kill_writer (fd);
   LOCK (notify_table_lock);
   for (i = 0; i < DIM (notify_table); i++)
     {
@@ -932,14 +1220,33 @@ _gpgme_io_close (int fd)
   if (handler)
     handler (fd, value);
 
-  if (!CloseHandle (fd_to_handle (fd)))
-    { 
-      TRACE_LOG1 ("CloseHandle failed: ec=%d", (int) GetLastError ());
-      /* FIXME: Should translate the error code.  */
-      errno = EIO;
-      return TRACE_SYSRES (-1);
+  if (fd_table[fd].dup_from == -1)
+    {
+      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);
+             return TRACE_SYSRES (-1);
+           }
+       }
+      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);
+             return TRACE_SYSRES (-1);
+           }
+       }
+      /* Nothing to do for RVIDs.  */
     }
 
+  release_fd (fd);
+
   return TRACE_SYSRES (0);
 }
 
@@ -965,7 +1272,7 @@ _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
   if (i == DIM (notify_table))
     {
       UNLOCK (notify_table_lock);
-      errno = EINVAL;
+      gpg_err_set_errno (EINVAL);
       return TRACE_SYSRES (-1);
     }
   notify_table[i].fd = fd;
@@ -985,6 +1292,91 @@ _gpgme_io_set_nonblocking (int fd)
 }
 
 
+#ifdef HAVE_W32CE_SYSTEM
+static char *
+build_commandline (char **argv, int fd0, int fd0_isnull,
+                  int fd1, int fd1_isnull,
+                  int fd2, int fd2_isnull)
+{
+  int i, n;
+  const char *s;
+  char *buf, *p;
+  char fdbuf[3*30];
+
+  p = fdbuf;
+  *p = 0;
+
+  if (fd0 != -1)
+    {
+      if (fd0_isnull)
+        strcpy (p, "-&S0=null ");
+      else
+       snprintf (p, 25, "-&S0=%d ", fd_table[fd0].rvid);
+      p += strlen (p);
+    }
+  if (fd1 != -1)
+    {
+      if (fd1_isnull)
+        strcpy (p, "-&S1=null ");
+      else
+       snprintf (p, 25, "-&S1=%d ", fd_table[fd1].rvid);
+      p += strlen (p);
+    }
+  if (fd2 != -1)
+    {
+      if (fd2_isnull)
+        strcpy (p, "-&S2=null ");
+      else
+        snprintf (p, 25, "-&S2=%d ", fd_table[fd2].rvid);
+      p += strlen (p);
+    }
+  strcpy (p, "-&S2=null ");
+  p += strlen (p);
+
+  n = strlen (fdbuf);
+  for (i=0; (s = argv[i]); i++)
+    {
+      if (!i)
+        continue; /* Ignore argv[0].  */
+      n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting) */
+      for (; *s; s++)
+        if (*s == '\"')
+          n++;  /* Need to double inner quotes.  */
+    }
+  n++;
+  buf = p = malloc (n);
+  if (! buf)
+    return NULL;
+
+  p = stpcpy (p, fdbuf);
+  for (i = 0; argv[i]; i++)
+    {
+      if (!i)
+        continue; /* Ignore argv[0].  */
+      if (i > 1)
+        p = stpcpy (p, " ");
+
+      if (! *argv[i]) /* Empty string. */
+        p = stpcpy (p, "\"\"");
+      else if (strpbrk (argv[i], " \t\n\v\f\""))
+        {
+          p = stpcpy (p, "\"");
+          for (s = argv[i]; *s; s++)
+            {
+              *p++ = *s;
+              if (*s == '\"')
+                *p++ = *s;
+            }
+          *p++ = '\"';
+          *p = 0;
+        }
+      else
+        p = stpcpy (p, argv[i]);
+    }
+
+  return buf;
+}
+#else
 static char *
 build_commandline (char **argv)
 {
@@ -992,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
@@ -1038,13 +1430,15 @@ build_commandline (char **argv)
 
   return buf;
 }
+#endif
 
 
 int
 _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
-                struct spawn_fd_item_s *fd_list, pid_t *r_pid)
+                struct spawn_fd_item_s *fd_list,
+                void (*atfork) (void *opaque, int reserved),
+                void *atforkvalue, pid_t *r_pid)
 {
-  SECURITY_ATTRIBUTES sec_attr;
   PROCESS_INFORMATION pi =
     {
       NULL,      /* returns process handle */
@@ -1052,16 +1446,132 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
       0,         /* returns pid */
       0          /* returns tid */
     };
-  STARTUPINFO si;
-  int cr_flags = (CREATE_DEFAULT_ERROR_MODE
-                  | GetPriorityClass (GetCurrentProcess ()));
   int i;
+
+#ifdef HAVE_W32CE_SYSTEM
+  int fd_in = -1;
+  int fd_out = -1;
+  int fd_err = -1;
+  int fd_in_isnull = 1;
+  int fd_out_isnull = 1;
+  int fd_err_isnull = 1;
+  char *cmdline;
+  HANDLE hd = INVALID_HANDLE_VALUE;
+
+  TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
+             "path=%s", path);
+  i = 0;
+  while (argv[i])
+    {
+      TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
+      i++;
+    }
+
+  for (i = 0; fd_list[i].fd != -1; i++)
+    {
+      int fd = fd_list[i].fd;
+
+      TRACE_LOG3 ("fd_list[%2i] = fd %i, dup_to %i", i, fd, fd_list[i].dup_to);
+      if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+       {
+         TRACE_LOG1 ("invalid fd 0x%x", fd);
+         gpg_err_set_errno (EBADF);
+         return TRACE_SYSRES (-1);
+       }
+      if (fd_table[fd].rvid == 0)
+       {
+         TRACE_LOG1 ("fd 0x%x not inheritable (not an RVID)", fd);
+         gpg_err_set_errno (EBADF);
+         return TRACE_SYSRES (-1);
+       }
+
+      if (fd_list[i].dup_to == 0)
+       {
+         fd_in = fd_list[i].fd;
+         fd_in_isnull = 0;
+       }
+      else if (fd_list[i].dup_to == 1)
+       {
+         fd_out = fd_list[i].fd;
+         fd_out_isnull = 0;
+       }
+      else if (fd_list[i].dup_to == 2)
+       {
+         fd_err = fd_list[i].fd;
+         fd_err_isnull = 0;
+       }
+    }
+
+  cmdline = build_commandline (argv, fd_in, fd_in_isnull,
+                              fd_out, fd_out_isnull, fd_err, fd_err_isnull);
+  if (!cmdline)
+    {
+      TRACE_LOG1 ("build_commandline failed: %s", strerror (errno));
+      return TRACE_SYSRES (-1);
+    }
+
+  if (!CreateProcessA (path,                /* Program to start.  */
+                      cmdline,             /* Command line arguments.  */
+                      NULL,                 /* (not supported)  */
+                      NULL,                 /* (not supported)  */
+                      FALSE,                /* (not supported)  */
+                      (CREATE_SUSPENDED),   /* Creation flags.  */
+                      NULL,                 /* (not supported)  */
+                      NULL,                 /* (not supported)  */
+                      NULL,                 /* (not supported) */
+                      &pi                   /* Returns process information.*/
+                      ))
+    {
+      TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
+      free (cmdline);
+      gpg_err_set_errno (EIO);
+      return TRACE_SYSRES (-1);
+    }
+
+  /* Create arbitrary pipe descriptor to send in ASSIGN_RVID
+     commands.  Errors are ignored.  We don't need read or write access,
+     as ASSIGN_RVID works without any permissions, yay!  */
+  hd = CreateFile (L"GPG1:", 0, 0,
+                  NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+  if (hd == INVALID_HANDLE_VALUE)
+    {
+      TRACE_LOG1 ("CreateFile failed (ignored): ec=%d",
+                 (int) GetLastError ());
+    }
+
+  /* Insert the inherited handles.  */
+  for (i = 0; fd_list[i].fd != -1; i++)
+    {
+      /* Return the child name of this handle.  */
+      fd_list[i].peer_name = fd_table[fd_list[i].fd].rvid;
+
+      if (hd != INVALID_HANDLE_VALUE)
+       {
+         DWORD data[2];
+         data[0] = (DWORD) fd_table[fd_list[i].fd].rvid;
+         data[1] = pi.dwProcessId;
+         if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_ASSIGN_RVID,
+                               data, sizeof (data), NULL, 0, NULL, NULL))
+           {
+             TRACE_LOG3 ("ASSIGN_RVID(%i, %i) failed (ignored): %i",
+                         data[0], data[1], (int) GetLastError ());
+           }
+       }
+    }
+  if (hd != INVALID_HANDLE_VALUE)
+    CloseHandle (hd);
+
+#else
+  SECURITY_ATTRIBUTES sec_attr;
+  STARTUPINFOA si;
+  int cr_flags = CREATE_DEFAULT_ERROR_MODE;
   char **args;
   char *arg_string;
   /* FIXME.  */
   int debug_me = 0;
   int tmp_fd;
   char *tmp_name;
+  const char *spawnhelper;
 
   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
              "path=%s", path);
@@ -1087,19 +1597,19 @@ _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)
     {
       close (tmp_fd);
-      DeleteFile (tmp_name);
+      DeleteFileA (tmp_name);
       return TRACE_SYSRES (-1);
     }
 
@@ -1111,9 +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; 
-  cr_flags |= DETACHED_PROCESS;
-  if (!CreateProcessA (_gpgme_get_w32spawn_path (),
+  cr_flags |= CREATE_SUSPENDED;
+  if ((flags & IOSPAWN_FLAG_DETACHED))
+    cr_flags |= DETACHED_PROCESS;
+  cr_flags |= GetPriorityClass (GetCurrentProcess ());
+  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 */
@@ -1124,13 +1662,14 @@ _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);
-      DeleteFile (tmp_name);
+      DeleteFileA (tmp_name);
 
       /* FIXME: Should translate the error code.  */
-      errno = EIO;
+      gpg_err_set_errno (EIO);
       return TRACE_SYSRES (-1);
     }
 
@@ -1142,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 ());
@@ -1157,16 +1701,16 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
          CloseHandle (pi.hProcess);
 
          close (tmp_fd);
-         DeleteFile (tmp_name);
+         DeleteFileA (tmp_name);
 
          /* FIXME: Should translate the error code.  */
-         errno = EIO;
+         gpg_err_set_errno (EIO);
          return TRACE_SYSRES (-1);
         }
       /* 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.  */
   {
@@ -1188,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,
@@ -1210,19 +1754,21 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
   close (tmp_fd);
   /* The temporary file is deleted by the gpgme-w32spawn process
      (hopefully).  */
+#endif
+
 
   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 ());
@@ -1269,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;
@@ -1283,8 +1831,8 @@ _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,0);
-             
+             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);
@@ -1295,7 +1843,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
                      TRACE_END (dbg_help, "oops ]");
                      TRACE_LOG ("Too many objects for WFMO!");
                      /* FIXME: Should translate the error code.  */
-                     errno = EIO;
+                     gpg_err_set_errno (EIO);
                      return TRACE_SYSRES (-1);
                     }
                  waitidx[nwait] = i;
@@ -1306,8 +1854,8 @@ _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,0);
-              
+             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);
@@ -1318,7 +1866,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
                      TRACE_END (dbg_help, "oops ]");
                      TRACE_LOG ("Too many objects for WFMO!");
                      /* FIXME: Should translate the error code.  */
-                     errno = EIO;
+                     gpg_err_set_errno (EIO);
                      return TRACE_SYSRES (-1);
                     }
                  waitidx[nwait] = i;
@@ -1330,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);
@@ -1365,11 +1913,12 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
   else if (code == WAIT_FAILED)
     {
       int le = (int) GetLastError ();
+#if 0
       if (le == ERROR_INVALID_HANDLE)
        {
          int k;
          int j = handle_to_fd (waitbuf[i]);
-          
+
          TRACE_LOG1 ("WFMO invalid handle %d removed", j);
          for (k = 0 ; k < nfds; k++)
            {
@@ -1381,6 +1930,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
             }
          TRACE_LOG (" oops, or not???");
         }
+#endif
       TRACE_LOG1 ("WFMO failed: %d", le);
       count = -1;
     }
@@ -1389,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 [ ");
@@ -1407,9 +1957,9 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
   if (count < 0)
     {
       /* FIXME: Should determine a proper error code.  */
-      errno = EIO;
+      gpg_err_set_errno (EIO);
     }
-  
+
   return TRACE_SYSRES (count);
 }
 
@@ -1427,6 +1977,15 @@ _gpgme_io_subsystem_init (void)
 int
 _gpgme_io_fd2str (char *buf, int buflen, int fd)
 {
+#ifdef HAVE_W32CE_SYSTEM
+  /* FIXME: For now. See above.  */
+  if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used
+      || fd_table[fd].rvid == 0)
+    fd = -1;
+  else
+    fd = fd_table[fd].rvid;
+#endif
+
   return snprintf (buf, buflen, "%d", fd);
 }
 
@@ -1434,25 +1993,29 @@ _gpgme_io_fd2str (char *buf, int buflen, int fd)
 int
 _gpgme_io_dup (int fd)
 {
-  HANDLE handle = fd_to_handle (fd);
-  HANDLE new_handle = fd_to_handle (fd);
-  int i;
+  int newfd;
   struct reader_context_s *rd_ctx;
   struct writer_context_s *wt_ctx;
+  int i;
 
   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);
 
-  if (!DuplicateHandle (GetCurrentProcess(), handle,
-                       GetCurrentProcess(), &new_handle,
-                       0, FALSE, DUPLICATE_SAME_ACCESS))
+  if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
     {
-      TRACE_LOG1 ("DuplicateHandle failed: ec=%d\n", (int) GetLastError ());
-      /* FIXME: Translate error code.  */
-      errno = EIO;
+      gpg_err_set_errno (EINVAL);
       return TRACE_SYSRES (-1);
     }
 
-  rd_ctx = find_reader (fd, 0);
+  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;
+  fd_table[newfd].dup_from = fd;
+
+  rd_ctx = find_reader (fd, 1);
   if (rd_ctx)
     {
       /* No need for locking, as the only races are against the reader
@@ -1465,13 +2028,13 @@ _gpgme_io_dup (int fd)
          break;
       /* FIXME.  */
       assert (i != reader_table_size);
-      reader_table[i].fd = handle_to_fd (new_handle);
+      reader_table[i].fd = newfd;
       reader_table[i].context = rd_ctx;
       reader_table[i].used = 1;
       UNLOCK (reader_table_lock);
     }
 
-  wt_ctx = find_writer (fd, 0);
+  wt_ctx = find_writer (fd, 1);
   if (wt_ctx)
     {
       /* No need for locking, as the only races are against the writer
@@ -1484,13 +2047,13 @@ _gpgme_io_dup (int fd)
          break;
       /* FIXME.  */
       assert (i != writer_table_size);
-      writer_table[i].fd = handle_to_fd (new_handle);
+      writer_table[i].fd = newfd;
       writer_table[i].context = wt_ctx;
       writer_table[i].used = 1;
       UNLOCK (writer_table_lock);
     }
 
-  return TRACE_SYSRES (handle_to_fd (new_handle));
+  return TRACE_SYSRES (newfd);
 }
 
 \f
@@ -1535,20 +2098,27 @@ int
 _gpgme_io_socket (int domain, int type, int proto)
 {
   int res;
+  int fd;
 
   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
              "type=%i, protp=%i", type, proto);
 
+  fd = new_fd();
+  if (fd == -1)
+    return TRACE_SYSRES (-1);
+
   res = socket (domain, type, proto);
   if (res == INVALID_SOCKET)
     {
-      errno = wsa2errno (WSAGetLastError ());
+      release_fd (fd);
+      gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
       return TRACE_SYSRES (-1);
     }
+  fd_table[fd].socket = res;
+
+  TRACE_SUC2 ("socket=0x%x (0x%x)", fd, fd_table[fd].socket);
 
-  TRACE_SUC1 ("socket=0x%x", res);
-  
-  return res;
+  return fd;
 }
 
 
@@ -1560,10 +2130,16 @@ _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
              "addr=%p, addrlen=%i", addr, addrlen);
 
-  res = connect (fd, addr, addrlen);
-  if (!res)
+  if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+    {
+      gpg_err_set_errno (EBADF);
+      return TRACE_SYSRES (-1);
+    }
+
+  res = connect (fd_table[fd].socket, addr, addrlen);
+  if (res)
     {
-      errno = wsa2errno (WSAGetLastError ());
+      gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
       return TRACE_SYSRES (-1);
     }