[svn] assuan - r399 - trunk/src

svn author marcus cvs at cvs.gnupg.org
Wed Nov 17 03:08:14 CET 2010


Author: marcus
Date: 2010-11-17 03:08:13 +0100 (Wed, 17 Nov 2010)
New Revision: 399

Modified:
   trunk/src/ChangeLog
   trunk/src/gpgcedev.c
Log:
2010-11-17  Marcus Brinkmann  <mb at g10code.com>

	* gpgcedev.c (struct pipeimpl_s): Add member monitor_rvid.
	(struct monitor_s, monitor_t): New types.
	(monitor_table, monitor_table_size): New static variables.
	(pipeimpl_new): Initialize monitor_rvid.
	(allocate_monitor): New function.
	(make_pipe): Also try the monitor_table if the opnctx_table fails.
	(monitor): Renamed to ...
	(monitor_main): ... this.  Remove from monitor_table when done.
	(assign_rvid): Add to monitor table when creating monitor thread.


Modified: trunk/src/ChangeLog
===================================================================
--- trunk/src/ChangeLog	2010-11-15 14:52:45 UTC (rev 398)
+++ trunk/src/ChangeLog	2010-11-17 02:08:13 UTC (rev 399)
@@ -1,3 +1,15 @@
+2010-11-17  Marcus Brinkmann  <mb at g10code.com>
+
+	* gpgcedev.c (struct pipeimpl_s): Add member monitor_rvid.
+	(struct monitor_s, monitor_t): New types.
+	(monitor_table, monitor_table_size): New static variables.
+	(pipeimpl_new): Initialize monitor_rvid.
+	(allocate_monitor): New function.
+	(make_pipe): Also try the monitor_table if the opnctx_table fails.
+	(monitor): Renamed to ...
+	(monitor_main): ... this.  Remove from monitor_table when done.
+	(assign_rvid): Add to monitor table when creating monitor thread.
+
 2010-11-15  Werner Koch  <wk at g10code.com>
 
 	* gpgcedev.c (GPG_Init): Read debug settings.

Modified: trunk/src/gpgcedev.c
===================================================================
--- trunk/src/gpgcedev.c	2010-11-15 14:52:45 UTC (rev 398)
+++ trunk/src/gpgcedev.c	2010-11-17 02:08:13 UTC (rev 399)
@@ -103,6 +103,7 @@
   /* For the monitor thread started by ASSIGN_RVID.  */
   HANDLE monitor_proc;
   int monitor_access;
+  LONG monitor_rvid;
 };
 typedef struct pipeimpl_s *pipeimpl_t;
 
@@ -150,10 +151,21 @@
 #define OPNCTX_FROM_IDX(idx) (&opnctx_table[(idx) - 1])
 #define OPNCTX_VALID_IDX_P(idx) ((idx) > 0 && (idx) <= opnctx_table_size)
 
-/* A criticial section object used to protect the OPNCTX_TABLE.  */
+typedef struct monitor_s *monitor_t;
+struct monitor_s
+{
+  int inuse;        /* True if this object has valid data.  */
+  pipeimpl_t pipeimpl;
+};
+static monitor_t monitor_table;
+static size_t monitor_table_size;
+
+/* A criticial section object used to protect the OPNCTX_TABLE and
+   MONITOR_TABLE.  */
 static CRITICAL_SECTION opnctx_table_cs;
 
 
+
 /* A global object to control the logging.  */
 struct {
   CRITICAL_SECTION lock; /* Lock for this structure.  */
@@ -246,6 +258,7 @@
   pimpl->data_available = CreateEvent (NULL, FALSE, FALSE, NULL);
   pimpl->monitor_proc = INVALID_HANDLE_VALUE;
   pimpl->monitor_access = 0;
+  pimpl->monitor_rvid = 0;
   return pimpl;
 }
 
@@ -429,6 +442,45 @@
 }
 
 
+/* Return a new monitor handle and mark it as used.  Returns NULL and
+   sets LastError on memory failure etc.  opnctx_table_cs must be
+   locked on entry and is locked on exit.  Note that the returned
+   pointer is only valid as long as opnctx_table_cs stays locked, as
+   it is not stable under table reallocation.  */
+static monitor_t
+allocate_monitor (void)
+{
+  monitor_t monitor = NULL;
+  int idx;
+
+  for (idx = 0; idx < monitor_table_size; idx++)
+    if (! monitor_table[idx].inuse)
+      break;
+  if (idx == monitor_table_size)
+    {
+      /* We need to increase the size of the table.  The approach we
+         take is straightforward to minimize the risk of bugs.  */
+      monitor_t newtbl;
+      size_t newsize = monitor_table_size + 16;
+
+      newtbl = calloc (newsize, sizeof *newtbl);
+      if (!newtbl)
+        goto leave;
+      memcpy (newtbl, monitor_table, monitor_table_size * sizeof (*newtbl));
+      free (monitor_table);
+      monitor_table = newtbl;
+      idx = monitor_table_size;
+      monitor_table_size = newsize;
+    }
+  monitor = &monitor_table[idx];
+  monitor->inuse = 1;
+  monitor->pipeimpl = 0;
+
+ leave:
+  return monitor;
+}
+
+
 static pipeimpl_t
 assert_pipeimpl (opnctx_t ctx)
 {
@@ -1036,62 +1088,117 @@
       return FALSE;
     }
 
+  /* GnuPG and other programs don't use the safe ASSIGN_RVID call
+     because they guarantee that the context exists during the whole
+     time the child process runs.  GPGME is more asynchronous and
+     relies on ASSIGN_RVID monitors.  So, first check for open
+     contexts, then check for monitors.  */
+
   for (idx = 0; idx < opnctx_table_size; idx++)
     if (opnctx_table[idx].inuse && opnctx_table[idx].rvid == rvid)
       {
         peerctx = &opnctx_table[idx];
         break;
       }
-  if (! peerctx)
+  if (peerctx)
     {
-      log_debug ("  make_pipe (ctx=%i): error: not found\n", ctx_arg);
-      SetLastError (ERROR_NOT_FOUND);
-      return FALSE;
-    }
+      /* This is the GnuPG etc case, where the context is still open.
+	 It may also cover the GPGME case if GPGME is still using its
+	 own end of the pipe at the time of this call.  */
+      if (peerctx == ctx)
+	{
+	  log_debug ("  make_pipe (ctx=%i): error: target and source identical\n",
+		     ctx_arg);
+	  SetLastError (ERROR_INVALID_TARGET_HANDLE);
+	  return FALSE;
+	}
 
-  if (peerctx == ctx)
-    {
-      log_debug ("  make_pipe (ctx=%i): error: target and source identical\n",
-		 ctx_arg);
-      SetLastError (ERROR_INVALID_TARGET_HANDLE);
-      return FALSE;
+      if ((ctx->access_code & GENERIC_READ))
+	{
+	  /* Check that the peer is a write end.  */
+	  if (!(peerctx->access_code & GENERIC_WRITE))
+	    {
+	      log_debug ("  make_pipe (ctx=%i): error: peer is not writer\n",
+			 ctx_arg);
+	      SetLastError (ERROR_INVALID_ACCESS);
+	      return FALSE;
+	    }
+	}
+      else if ((ctx->access_code & GENERIC_WRITE))
+	{
+	  /* Check that the peer is a read end.  */
+	  if (!(peerctx->access_code & GENERIC_READ))
+	    {
+	      log_debug ("  make_pipe (ctx=%i): error: peer is not reader\n",
+			 ctx_arg);
+	      SetLastError (ERROR_INVALID_ACCESS);
+	      return FALSE;
+	    }
+	}
+      else
+	{
+	  log_debug ("  make_pipe (ctx=%i): error: invalid access\n", ctx_arg);
+	  SetLastError (ERROR_INVALID_ACCESS);
+	  return FALSE;
+	}
+      
+      /* Make sure the peer has a pipe implementation to be shared.  If
+	 not yet, create one.  */
+      pimpl = assert_pipeimpl (peerctx);
+      if (! pimpl)
+	return FALSE;
     }
-
-  if ((ctx->access_code & GENERIC_READ))
-    {
-      /* Check that the peer is a write end.  */
-      if (!(peerctx->access_code & GENERIC_WRITE))
-        {
-          SetLastError (ERROR_INVALID_ACCESS);
-	  log_debug ("  make_pipe (ctx=%i): error: peer is not writer\n",
-		     ctx_arg);
-          return FALSE;
-        }
-    }
-  else if ((ctx->access_code & GENERIC_WRITE))
-    {
-      /* Check that the peer is a read end.  */
-      if (!(peerctx->access_code & GENERIC_READ))
-        {
-          SetLastError (ERROR_INVALID_ACCESS);
-	  log_debug ("  make_pipe (ctx=%i): error: peer is not reader\n",
-		     ctx_arg);
-          return FALSE;
-        }
-    }
   else
     {
-      SetLastError (ERROR_INVALID_ACCESS);
-      log_debug ("  make_pipe (ctx=%i): error: invalid access\n", ctx_arg);
-      return FALSE;
+      /* This is the case where ASSIGN_RVID was called to create a
+	 monitor, and the pipe is already closed at the parent side.
+	 For example GPGME verify detached plain text, where GPG calls
+	 MAKE_PIPE very late.  */
+      
+      for (idx = 0; idx < monitor_table_size; idx++)
+	if (monitor_table[idx].inuse
+	    && monitor_table[idx].pipeimpl->monitor_rvid == rvid)
+	  {
+	    pimpl = monitor_table[idx].pipeimpl;
+	    break;
+	  }
+      if (idx == monitor_table_size)
+	{
+	  log_debug ("  make_pipe (ctx=%i): error: not found\n", ctx_arg);
+	  SetLastError (ERROR_NOT_FOUND);
+	  return FALSE;
+	}
+
+      if (ctx->access_code & GENERIC_READ)
+	{
+	  /* Check that the peer is a write end.  */
+	  if (!(pimpl->monitor_access & GENERIC_READ))
+	    {
+	      log_debug ("  make_pipe (ctx=%i): error: monitor is not reader\n",
+			 ctx_arg);
+	      SetLastError (ERROR_INVALID_ACCESS);
+	      return FALSE;
+	    }
+	}
+      else if ((ctx->access_code & GENERIC_WRITE))
+	{
+	  /* Check that the peer is a read end.  */
+	  if (!(pimpl->monitor_access & GENERIC_WRITE))
+	    {
+	      log_debug ("  make_pipe (ctx=%i): error: monitor is not writer\n",
+			 ctx_arg);
+	      SetLastError (ERROR_INVALID_ACCESS);
+	      return FALSE;
+	    }
+	}
+      else
+	{
+	  log_debug ("  make_pipe (ctx=%i): error: invalid access\n", ctx_arg);
+	  SetLastError (ERROR_INVALID_ACCESS);
+	  return FALSE;
+	}
     }
 
-  /* Make sure the peer has a pipe implementation to be shared.  If
-     not yet, create one.  */
-  pimpl = assert_pipeimpl (peerctx);
-  if (! pimpl)
-    return FALSE;
-
   EnterCriticalSection (&pimpl->critsect);
   pimpl->refcnt++;
   if (pimpl->monitor_proc != INVALID_HANDLE_VALUE)
@@ -1110,8 +1217,16 @@
 
   ctx->pipeimpl = pimpl;
 
-  log_debug ("  make_pipe (ctx=%i): success: combined with peer ctx=%i "
-	     "(pipe 0x%p)\n", ctx_arg, OPNCTX_TO_IDX (peerctx), pimpl);
+  if (peerctx)
+    {
+      log_debug ("  make_pipe (ctx=%i): success: combined with peer ctx=%i "
+		 "(pipe 0x%p)\n", ctx_arg, OPNCTX_TO_IDX (peerctx), pimpl);
+    }
+  else
+    {
+      log_debug ("  make_pipe (ctx=%i): success: combined with "
+		 "pipe 0x%p\n", ctx_arg, OPNCTX_TO_IDX (peerctx), pimpl);
+    }
 
   return TRUE;
 }
@@ -1143,10 +1258,11 @@
 
 
 static DWORD CALLBACK 
-monitor (void *arg)
+monitor_main (void *arg)
 {
   pipeimpl_t pimpl = (pipeimpl_t) arg;
   HANDLE handles[2];
+  int idx;
 
   log_debug ("starting monitor (pimpl=0x%p)\n", pimpl);
   
@@ -1198,9 +1314,28 @@
     }
 
   log_debug ("ending monitor (pimpl=0x%p)\n", pimpl);
+
+  /* Remove the monitor from the monitor table.  */
+  LeaveCriticalSection (&pimpl->critsect);
+  EnterCriticalSection (&opnctx_table_cs);
+  for (idx = 0; idx < monitor_table_size; idx++)
+    if (monitor_table[idx].inuse
+	&& monitor_table[idx].pipeimpl == pimpl)
+      {
+	monitor_table[idx].pipeimpl = NULL;
+	monitor_table[idx].inuse = 0;
+	break;
+      }
+  if (idx == monitor_table_size)
+    log_debug ("can not find monitor in table (pimpl=0x%p)\n", pimpl);
+  LeaveCriticalSection (&opnctx_table_cs);
+  EnterCriticalSection (&pimpl->critsect);
+
+  /* Now do the rest of the cleanup.  */
   CloseHandle (pimpl->monitor_proc);
   pimpl->monitor_proc = INVALID_HANDLE_VALUE;
   pimpl->monitor_access = 0;
+  pimpl->monitor_rvid = 0;
   pimpl->flags &= ~PIPE_FLAG_HALT_MONITOR;
   pipeimpl_unref (pimpl);
 
@@ -1218,6 +1353,7 @@
   HANDLE monitor_hnd;
   HANDLE proc;
   pipeimpl_t pimpl;
+  monitor_t monitor;
 
   log_debug ("  assign_rvid (ctx=%i, rvid=%08lx, pid=%08lx)\n",
 	     ctx_arg, rvid, pid);
@@ -1261,6 +1397,16 @@
       return FALSE;
     }
 
+  monitor = allocate_monitor ();
+  if (!monitor)
+    {
+      log_debug ("  assign_rvid (ctx=%i): error: could not allocate monitor\n",
+		 ctx_arg);
+      CloseHandle (proc);
+      return FALSE;
+    }
+  monitor->pipeimpl = pimpl;
+
   EnterCriticalSection (&pimpl->critsect);
   pimpl->refcnt++;
 
@@ -1271,14 +1417,18 @@
     pimpl->monitor_access = GENERIC_WRITE;
   else
     pimpl->monitor_access = GENERIC_READ;
+  pimpl->monitor_rvid = rvid;
 
-  monitor_hnd = CreateThread (NULL, 0, monitor, pimpl, 0, NULL);
+  monitor_hnd = CreateThread (NULL, 0, monitor_main, pimpl, 0, NULL);
   if (monitor_hnd == INVALID_HANDLE_VALUE)
     {
       pimpl->monitor_access = 0;
       pimpl->monitor_proc = INVALID_HANDLE_VALUE;
       LeaveCriticalSection (&pimpl->critsect);
 
+      monitor->pipeimpl = NULL;
+      monitor->inuse = 0;
+
       CloseHandle (proc);
       log_debug ("  assign_rvid (ctx=%i): error: can not create monitor\n",
 		 ctx_arg);





More information about the Gnupg-commits mailing list