diff options
-rw-r--r-- | src/ChangeLog | 29 | ||||
-rw-r--r-- | src/gpgcedev.c | 619 |
2 files changed, 373 insertions, 275 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 3409bc8..fd2a293 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,32 @@ +2010-06-07 Marcus Brinkmann <[email protected]> + + * gpgcedev.c: This rewrite does away with troublesome race + conditions (close vs everything else, for example) by simplifying + the locking model. It also handles EOF, EPIPE, but still assumes + that there is always only ever one reader and writer. Also, no + need to treat ERROR_PIPE_NOT_CONNECTED and ERROR_BUSY as EAGAIN + anymore. + (struct pipeimpl_s, pipeimpl_t): New types. + (PIPE_FLAG_NO_READER, PIPE_FLAG, NO_WRITER): New macros. + (struct opnctx_s): Remove everything that's now in struct + pipeimpl_s. Remove also assoc and locked. Add pipeimpl field. + (pipeimpl_new, pipeimpl_unref, allocate_opnctx, verify_opnctx, + access_opnctx): New functions. + (get_new_opnctx, find_and_lock_opnctx, validate_and_lock_opnctx, + unlock_opnctx): Removed. + (GPG_Init, GPG_Deinit): Improve debugging output. + (GPG_Open): Improve debugging output, use allocate_opnctx instead + of get_new_opnctx. + (GPG_Close): Improve debugging output. Rewrite to use reference + counting. Also check if reader or writer is closed and set flags + for triggering EOF or EPIPE. + (GPG_Read): Improve debugging output. Rewrite using pipeimpl. + Check for EOF. + (GPG_Write): Improve debugging output. Rewrite using pipeimpl. + Check for EPIPE. + (make_pipe): Rewrite using pipeimpl. + (GPG_IOControl): Improve debugging output. + 2010-04-22 Werner Koch <[email protected]> * assuan-listen.c (assuan_accept): Show the PID with the default diff --git a/src/gpgcedev.c b/src/gpgcedev.c index a295dd6..357c488 100644 --- a/src/gpgcedev.c +++ b/src/gpgcedev.c @@ -54,6 +54,25 @@ #define GPGCEDEV_IOCTL_MAKE_PIPE \ CTL_CODE (FILE_DEVICE_STREAMS, 2049, METHOD_BUFFERED, FILE_ANY_ACCESS) +struct pipeimpl_s +{ + CRITICAL_SECTION critsect; /* Lock for all members. */ + + int refcnt; + char *buffer; + size_t buffer_size; + size_t buffer_len; /* The valid length of the bufer. */ + size_t buffer_pos; /* The actual read position. */ + +#define PIPE_FLAG_NO_READER 1 +#define PIPE_FLAG_NO_WRITER 2 + int flags; + + HANDLE space_available; /* Set if space is available. */ + HANDLE data_available; /* Set if data is available. */ +}; +typedef struct pipeimpl_s *pipeimpl_t; + /* An object to store information pertaining to an open-context. */ struct opnctx_s; @@ -61,27 +80,12 @@ typedef struct opnctx_s *opnctx_t; struct opnctx_s { int inuse; /* True if this object has valid data. */ - opnctx_t assoc; /* This context has been associated with this - other context; i.e. a pipe has been - established. */ - int is_write; /* True if this is the write end of the pipe. */ LONG rvid; /* The unique rendezvous identifier. */ DWORD access_code;/* Value from OpenFile. */ DWORD share_mode; /* Value from OpenFile. */ - CRITICAL_SECTION critsect; /* Lock for all operations. */ - int locked; /* True if we are in a critical section. */ - - /* The malloced buffer and its size. We use a buffer for each - handle which allows us eventually implement a system to - distribute data to several handles. Not sure whether this is - really needed but as a side effect it makes the code easier. */ - char *buffer; - size_t buffer_size; - size_t buffer_len; /* The valid length of the bufer. */ - size_t buffer_pos; /* The actual read or write position. */ - HANDLE space_available; /* Set if space is available. */ - HANDLE data_available; /* Set if data is available. */ + /* The state shared by all users. */ + pipeimpl_t pipeimpl; }; /* A malloced table of open-context and the number of allocated slots. */ @@ -136,18 +140,78 @@ create_rendezvous_id (void) +pipeimpl_t +pipeimpl_new (void) +{ + pipeimpl_t pimpl; + + pimpl = malloc (sizeof (*pimpl)); + if (!pimpl) + return NULL; + + InitializeCriticalSection (&pimpl->critsect); + pimpl->refcnt = 1; + pimpl->buffer_size = 512; + pimpl->buffer = malloc (pimpl->buffer_size); + pimpl->buffer_len = 0; + pimpl->buffer_pos = 0; + pimpl->flags = 0; + pimpl->space_available = CreateEvent (NULL, FALSE, FALSE, NULL); + pimpl->data_available = CreateEvent (NULL, FALSE, FALSE, NULL); + + return pimpl; +} + + +/* PIMPL must be locked. It is unlocked at exit. */ +void +pipeimpl_unref (pipeimpl_t pimpl) +{ + int release = 0; + + log_debug ("pipeimpl_unref (%p): dereference\n", pimpl); + + if (--pimpl->refcnt == 0) + release = 1; + LeaveCriticalSection (&pimpl->critsect); + + if (! release) + return; + + log_debug ("pipeimpl_unref (%p): release\n", pimpl); + + DeleteCriticalSection (&pimpl->critsect); + if (pimpl->buffer) + { + free (pimpl->buffer); + pimpl->buffer = NULL; + pimpl->buffer_size = 0; + } + if (pimpl->space_available != INVALID_HANDLE_VALUE) + { + CloseHandle (pimpl->space_available); + pimpl->space_available = INVALID_HANDLE_VALUE; + } + if (pimpl->data_available != INVALID_HANDLE_VALUE) + { + CloseHandle (pimpl->data_available); + pimpl->data_available = INVALID_HANDLE_VALUE; + } +} + + + /* Return a new opnctx handle and mark it as used. Returns NULL and - sets LastError on memory failure etc. On success the context is - locked. */ + sets LastError on memory failure etc. opnctx_table_cs must be + locked on entry and is locked on exit. */ static opnctx_t -get_new_opnctx (void) +allocate_opnctx (void) { opnctx_t opnctx = NULL; int idx; - EnterCriticalSection (&opnctx_table_cs); - for (idx=0; idx < opnctx_table_size; idx++) - if (!opnctx_table[idx].inuse) + for (idx = 0; idx < opnctx_table_size; idx++) + if (! opnctx_table[idx].inuse) break; if (idx == opnctx_table_size) { @@ -159,129 +223,91 @@ get_new_opnctx (void) newtbl = calloc (newsize, sizeof *newtbl); if (!newtbl) goto leave; - for (idx=0; idx < opnctx_table_size; idx++) - newtbl[idx] = opnctx_table[idx]; + memcpy (newtbl, opnctx_table, opnctx_table_size * sizeof (*newtbl)); free (opnctx_table); opnctx_table = newtbl; idx = opnctx_table_size; opnctx_table_size = newsize; } - opnctx = opnctx_table + idx; - opnctx->assoc = NULL; - opnctx->rvid = create_rendezvous_id (); - opnctx->is_write = 0; + opnctx = &opnctx_table[idx]; + opnctx->inuse = 1; + opnctx->rvid = 0; opnctx->access_code = 0; opnctx->share_mode = 0; - InitializeCriticalSection (&opnctx->critsect); - opnctx->locked = 0; - opnctx->buffer_size = 512; - opnctx->buffer = malloc (opnctx->buffer_size); - if (!opnctx->buffer) - { - opnctx = NULL; - goto leave; - } - opnctx->buffer_len = 0; - opnctx->buffer_pos = 0; - opnctx->data_available = INVALID_HANDLE_VALUE; - opnctx->space_available = INVALID_HANDLE_VALUE; + opnctx->pipeimpl = 0; - opnctx->inuse = 1; - EnterCriticalSection (&opnctx->critsect); - opnctx->locked = 1; - leave: - LeaveCriticalSection (&opnctx_table_cs); - if (opnctx) - log_debug ("get_new_opnctx -> %p (rvid=%ld)\n", opnctx, opnctx->rvid); - else - log_debug ("get_new_opnctx -> failed\n"); return opnctx; } -/* Find the OPNCTX object with the rendezvous id RVID. */ -static opnctx_t -find_and_lock_opnctx (LONG rvid) +/* Verify context CTX, returns NULL if not valid and the pointer to + the context if valid. opnctx_table_cs must be locked on entry and + is locked on exit. */ +opnctx_t +verify_opnctx (opnctx_t ctx) { - opnctx_t result = NULL; - int idx; - - EnterCriticalSection (&opnctx_table_cs); - for (idx=0; idx < opnctx_table_size; idx++) - if (opnctx_table[idx].inuse && opnctx_table[idx].rvid == rvid) - { - result = opnctx_table + idx; - break; - } - LeaveCriticalSection (&opnctx_table_cs); - if (!result) + int idx = ctx - opnctx_table; + if (idx < 0 || idx >= opnctx_table_size) { SetLastError (ERROR_INVALID_HANDLE); - log_debug ("find_opnctx -> invalid rendezvous id\n"); - } - else if (TryEnterCriticalSection (&result->critsect)) - { - result->locked++; - log_debug ("find_opnctx -> %p (rvid=%ld)\n", result, result->rvid); + return NULL; } - else + if (! opnctx_table[idx].inuse) { - SetLastError (ERROR_BUSY); - result = NULL; - log_debug ("find_opnctx -> busy\n"); + SetLastError (ERROR_INVALID_HANDLE); + return NULL; } - return result; + return &opnctx_table[idx]; } -/* Check that OPNCTX is valid. Returns TRUE if it is valid or FALSE - if it is a bad or closed contect. In the latter case SetLastError - is called. In the former case a lock is taken and unlock_opnctx - needs to be called. If WAIT is false the fucntion only tries to - acquire a lock. */ -static BOOL -validate_and_lock_opnctx (opnctx_t opnctx, int wait) +/* Verify access CODE for context CTX, returning a reference to the + locked pipe implementation. opnctx_table_cs must be locked on + entry and is locked on exit. */ +pipeimpl_t +access_opnctx (opnctx_t ctx, DWORD code) { - BOOL result = FALSE; int idx; EnterCriticalSection (&opnctx_table_cs); - for (idx=0; idx < opnctx_table_size; idx++) - if (opnctx_table[idx].inuse && (opnctx_table + idx) == opnctx) - { - result = TRUE; - break; - } - LeaveCriticalSection (&opnctx_table_cs); - - if (!result) - SetLastError (ERROR_INVALID_HANDLE); - else if (wait) + idx = ctx - opnctx_table; + if (idx < 0 || idx >= opnctx_table_size || ! opnctx_table[idx].inuse) { - EnterCriticalSection (&opnctx->critsect); - opnctx->locked++; + SetLastError (ERROR_INVALID_HANDLE); + LeaveCriticalSection (&opnctx_table_cs); + return NULL; } - else if (TryEnterCriticalSection (&opnctx->critsect)) - opnctx->locked++; - else + ctx = &opnctx_table[idx]; + + if (!(ctx->access_code & code)) { - SetLastError (ERROR_BUSY); - result = FALSE; + SetLastError (ERROR_INVALID_ACCESS); + LeaveCriticalSection (&opnctx_table_cs); + return NULL; } - return result; -} - -static void -unlock_opnctx (opnctx_t opnctx) -{ - opnctx->locked--; - LeaveCriticalSection (&opnctx->critsect); + if (! ctx->pipeimpl) + { + ctx->pipeimpl = pipeimpl_new (); + if (! ctx->pipeimpl) + { + log_debug (" access_opnctx (ctx=0x%p): error: can't create pipe\n", + ctx); + LeaveCriticalSection (&opnctx_table_cs); + return NULL; + } + log_debug (" access_opnctx (ctx=0x%p): created pipe 0x%p\n", + ctx, ctx->pipeimpl); + } + + EnterCriticalSection (&ctx->pipeimpl->critsect); + ctx->pipeimpl->refcnt++; + LeaveCriticalSection (&opnctx_table_cs); + return ctx->pipeimpl; } - static char * wchar_to_utf8 (const wchar_t *string) @@ -314,7 +340,7 @@ GPG_Init (LPCTSTR active_key, DWORD bus_context) (void)bus_context; tmpbuf = wchar_to_utf8 (active_key); - log_debug ("GPG_Init (%s)\n", tmpbuf); + log_debug ("GPG_Init (devctx=0x%p, %s)\n", DEVCTX_VALUE, tmpbuf); free (tmpbuf); /* We don't need any global data. However, we need to return @@ -328,7 +354,7 @@ GPG_Init (LPCTSTR active_key, DWORD bus_context) BOOL GPG_Deinit (DWORD devctx) { - log_debug ("GPG_Deinit (%p)\n", (void*)devctx); + log_debug ("GPG_Deinit (devctx=0x%p)\n", (void*)devctx); if (devctx != DEVCTX_VALUE) { SetLastError (ERROR_INVALID_PARAMETER); @@ -349,20 +375,32 @@ GPG_Open (DWORD devctx, DWORD access_code, DWORD share_mode) { opnctx_t opnctx; - log_debug ("GPG_Open(devctx=%p)\n", (void*)devctx); + log_debug ("GPG_Open (devctx=%p)\n", (void*)devctx); if (devctx != DEVCTX_VALUE) { + log_debug ("GPG_Open (devctx=%p): error: devctx mismatch (expected 0x%p)\n", + (void*) devctx); SetLastError (ERROR_INVALID_PARAMETER); return 0; /* Error. */ } - opnctx = get_new_opnctx (); + EnterCriticalSection (&opnctx_table_cs); + opnctx = allocate_opnctx (); if (!opnctx) - return 0; + { + log_debug ("GPG_Open (devctx=%p): error: could not allocate context\n", + (void*) devctx); + goto leave; + } + opnctx->access_code = access_code; opnctx->share_mode = share_mode; - unlock_opnctx (opnctx); + log_debug ("GPG_Open (devctx=%p): success: created context 0x%p\n", + (void*) devctx, opnctx); + + leave: + LeaveCriticalSection (&opnctx_table_cs); return (DWORD)opnctx; } @@ -373,50 +411,45 @@ GPG_Close (DWORD opnctx_arg) { opnctx_t opnctx = (opnctx_t)opnctx_arg; BOOL result = FALSE; - int idx; - log_debug ("GPG_Close(%p)\n", (void*)opnctx); + log_debug ("GPG_Close (ctx=0x%p)\n", (void*)opnctx); EnterCriticalSection (&opnctx_table_cs); - for (idx=0; idx < opnctx_table_size; idx++) - if (opnctx_table[idx].inuse && (opnctx_table + idx) == opnctx) - { - if (opnctx->assoc) - { - opnctx->assoc->assoc = NULL; - opnctx->assoc = NULL; - } - if (opnctx->locked) - { - /* FIXME: Check earlier or use close only in locked state - or use PReClose. */ - log_debug ("GPG_Close while still locked\n"); - } - DeleteCriticalSection (&opnctx->critsect); - if (opnctx->buffer) - { - free (opnctx->buffer); - opnctx->buffer = NULL; - opnctx->buffer_size = 0; - } - if (opnctx->space_available != INVALID_HANDLE_VALUE) - { - CloseHandle (opnctx->space_available); - opnctx->space_available = INVALID_HANDLE_VALUE; - } - if (opnctx->data_available != INVALID_HANDLE_VALUE) - { - CloseHandle (opnctx->data_available); - opnctx->data_available = INVALID_HANDLE_VALUE; - } - opnctx->inuse = 0; - result = TRUE; - break; - } - LeaveCriticalSection (&opnctx_table_cs); + opnctx = verify_opnctx (opnctx); + if (!opnctx) + { + log_debug ("GPG_Close (ctx=0x%p): could not find context\n", (void*)opnctx); + goto leave; + } - if (!result) - SetLastError (ERROR_INVALID_HANDLE); + if (opnctx->pipeimpl) + { + pipeimpl_t pimpl = opnctx->pipeimpl; + EnterCriticalSection (&pimpl->critsect); + /* This needs to be adjusted if there can be multiple + reader/writers. */ + if (opnctx->access_code & GENERIC_READ) + { + pimpl->flags |= PIPE_FLAG_NO_READER; + SetEvent (pimpl->space_available); + } + else if (opnctx->access_code & GENERIC_WRITE) + { + pimpl->flags |= PIPE_FLAG_NO_WRITER; + SetEvent (pimpl->data_available); + } + pipeimpl_unref (pimpl); + opnctx->pipeimpl = 0; + } + opnctx->access_code = 0; + opnctx->share_mode = 0; + opnctx->rvid = 0; + opnctx->inuse = 0; + result = TRUE; + log_debug ("GPG_Close (ctx=0x%p): success\n", (void*)opnctx); + + leave: + LeaveCriticalSection (&opnctx_table_cs); return result; } @@ -425,72 +458,67 @@ GPG_Close (DWORD opnctx_arg) DWORD GPG_Read (DWORD opnctx_arg, void *buffer, DWORD count) { - opnctx_t rctx = (opnctx_t)opnctx_arg; - opnctx_t wctx; - int result = -1; + opnctx_t ctx = (opnctx_t)opnctx_arg; + pipeimpl_t pimpl; const char *src; char *dst; + int result = -1; - log_debug ("GPG_Read(%p, count=%d)\n", (void*)rctx, count); - - /* We use the write end's buffer, thus there is no need to wait for - our (read end) lock. */ - if (!validate_and_lock_opnctx (rctx, LOCK_TRY)) - return -1; /* Error. */ + log_debug ("GPG_Read (ctx=0x%p, buffer=0x%p, count=%d)\n", + (void*)ctx, buffer, count); - if (rctx->is_write) + pimpl = access_opnctx (ctx, GENERIC_READ); + if (!pimpl) { - SetLastError (ERROR_INVALID_ACCESS); - log_debug ("GPG_Read(%p) -> invalid access\n", (void*)rctx); - goto leave; - } - if (!rctx->assoc) - { - SetLastError (ERROR_PIPE_NOT_CONNECTED); - goto leave; + log_debug ("GPG_Read (ctx=0x%p): error: could not access context\n", + (void*)ctx); + return -1; } - /* Read from the corresponding write buffer. */ retry: - wctx = rctx->assoc; - if (!validate_and_lock_opnctx (wctx, LOCK_WAIT)) - goto leave; - - if (wctx->buffer_pos == wctx->buffer_len) + if (pimpl->buffer_pos == pimpl->buffer_len) { - unlock_opnctx (wctx); - log_debug ("%s:%d: WFSO(data_available)\n", __func__, __LINE__); - WaitForSingleObject (wctx->data_available, INFINITE); - log_debug ("%s:%d: WFSO ... woke up\n", __func__, __LINE__); + HANDLE data_available = pimpl->data_available; + + /* Check for end of file. */ + if (pimpl->flags & PIPE_FLAG_NO_WRITER) + { + log_debug ("GPG_Read (ctx=0x%p): success: EOF\n", (void*)ctx); + result = 0; + goto leave; + } + + LeaveCriticalSection (&pimpl->critsect); + log_debug ("GPG_Read (ctx=0x%p): waiting: data_available\n", (void*)ctx); + WaitForSingleObject (data_available, INFINITE); + log_debug ("GPG_Read (ctx=0x%p): resuming: data_available\n", (void*)ctx); + EnterCriticalSection (&pimpl->critsect); goto retry; } dst = buffer; - src = wctx->buffer + wctx->buffer_pos; - while (count > 0 && wctx->buffer_pos < wctx->buffer_len) + src = pimpl->buffer + pimpl->buffer_pos; + while (count > 0 && pimpl->buffer_pos < pimpl->buffer_len) { *dst++ = *src++; count--; - wctx->buffer_pos++; + pimpl->buffer_pos++; } result = (dst - (char*)buffer); - if (wctx->buffer_pos == wctx->buffer_len) - wctx->buffer_pos = wctx->buffer_len = 0; + if (pimpl->buffer_pos == pimpl->buffer_len) + pimpl->buffer_pos = pimpl->buffer_len = 0; /* Now there should be some space available. Signal the write end. Even if COUNT was passed as NULL and no space is available, signaling must be done. */ - if (!SetEvent (wctx->space_available)) - { - log_debug ("%s:%d: SetEvent(space_available) failed: rc=%d\n", - __func__, __LINE__, (int)GetLastError ()); - unlock_opnctx (wctx); - goto leave; - } - unlock_opnctx (wctx); + if (!SetEvent (pimpl->space_available)) + log_debug ("GPG_Read (ctx=0x%p): warning: SetEvent(space_available) failed: rc=%d\n", + (void*) ctx, (int)GetLastError ()); + + log_debug ("GPG_Read (ctx=0x%p): success: result=%d\n", (void*)ctx, result); leave: - unlock_opnctx (rctx); + pipeimpl_unref (pimpl); return result; } @@ -499,64 +527,72 @@ GPG_Read (DWORD opnctx_arg, void *buffer, DWORD count) DWORD GPG_Write (DWORD opnctx_arg, const void *buffer, DWORD count) { - opnctx_t wctx = (opnctx_t)opnctx_arg; + opnctx_t ctx = (opnctx_t)opnctx_arg; + pipeimpl_t pimpl; int result = -1; const char *src; char *dst; size_t nwritten = 0; - log_debug ("GPG_Write(%p, count=%d)\n", (void*)wctx, count); - retry: - if (!validate_and_lock_opnctx (wctx, LOCK_WAIT)) - return -1; /* Error. */ + log_debug ("GPG_Write (ctx=0x%p, buffer=0x%p, count=%d)\n", (void*)ctx, + buffer, count); - if (!wctx->is_write) + pimpl = access_opnctx (ctx, GENERIC_WRITE); + if (!pimpl) { - SetLastError (ERROR_INVALID_ACCESS); - log_debug ("GPG_Write(%p) -> invalid access\n", (void*)wctx); - goto leave; + log_debug ("GPG_Write (ctx=0x%p): error: could not access context\n", + (void*)ctx); + return -1; } - if (!wctx->assoc) + + if (!count) { - SetLastError (ERROR_PIPE_NOT_CONNECTED); + log_debug ("GPG_Write (ctx=0x%p): success\n", (void*)ctx); + result = 0; goto leave; } - if (!count) + + retry: + /* Check for broken pipe. */ + if (pimpl->flags & PIPE_FLAG_NO_READER) { - result = 0; + log_debug ("GPG_Write (ctx=0x%p): error: broken pipe\n", (void*)ctx); + SetLastError (ERROR_BROKEN_PIPE); goto leave; } /* Write to our buffer. */ - if (wctx->buffer_len == wctx->buffer_size) + if (pimpl->buffer_len == pimpl->buffer_size) { /* Buffer is full. */ - unlock_opnctx (wctx); - log_debug ("%s:%d: WFSO(space_available)\n", __func__, __LINE__); - WaitForSingleObject (wctx->space_available, INFINITE); - log_debug ("%s:%d: WFSO ... woke up\n", __func__, __LINE__); + HANDLE space_available = pimpl->space_available; + LeaveCriticalSection (&pimpl->critsect); + log_debug ("GPG_Write (ctx=0x%p): waiting: space_available\n", (void*)ctx); + WaitForSingleObject (space_available, INFINITE); + log_debug ("GPG_Write (ctx=0x%p): resuming: space_available\n", (void*)ctx); + EnterCriticalSection (&pimpl->critsect); goto retry; } src = buffer; - dst = wctx->buffer + wctx->buffer_len; - while (count > 0 && wctx->buffer_len < wctx->buffer_size) + dst = pimpl->buffer + pimpl->buffer_len; + while (count > 0 && pimpl->buffer_len < pimpl->buffer_size) { *dst++ = *src++; count--; - wctx->buffer_len++; + pimpl->buffer_len++; nwritten++; } - if (!SetEvent (wctx->data_available)) - { - log_debug ("%s:%d: SetEvent(data_available) failed: rc=%d\n", - __func__, __LINE__, (int)GetLastError ()); - goto leave; - } result = nwritten; + if (!SetEvent (pimpl->data_available)) + log_debug ("GPG_Write (ctx=0x%p): warning: SetEvent(data_available) failed: rc=%d\n", + (void*) ctx, (int)GetLastError ()); + + log_debug ("GPG_Write (ctx=0x%p): success: result=%d\n", (void*)ctx, result); + leave: - unlock_opnctx (wctx); + pipeimpl_unref (pimpl); return result; } @@ -571,34 +607,40 @@ GPG_Seek (DWORD opnctx, long amount, WORD type) +/* opnctx_table_s is locked on entering and on exit. */ static BOOL make_pipe (opnctx_t ctx, LONG rvid) { - BOOL result = FALSE; opnctx_t peerctx = NULL; + int idx; - log_debug (" make_pipe(%p, rvid=%ld)\n", ctx, rvid); - if (ctx->assoc) + log_debug (" make_pipe (ctx=0x%p, rvid=%08lx)\n", ctx, rvid); + + if (ctx->pipeimpl) { + log_debug (" make_pipe (ctx=0x%p): error: already assigned\n", ctx); SetLastError (ERROR_ALREADY_ASSIGNED); - goto leave; + return FALSE; } - peerctx = find_and_lock_opnctx (rvid); - if (!peerctx) + 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) { + log_debug (" make_pipe (ctx=0x%p): error: not found\n", ctx); SetLastError (ERROR_NOT_FOUND); - goto leave; + return FALSE; } + if (peerctx == ctx) { + log_debug (" make_pipe (ctx=0x%p): error: target and source identical\n", ctx); SetLastError (ERROR_INVALID_TARGET_HANDLE); - goto leave; - } - if (peerctx->assoc) - { - SetLastError (ERROR_ALREADY_ASSIGNED); - goto leave; + return FALSE; } if ((ctx->access_code & GENERIC_READ)) @@ -607,17 +649,9 @@ make_pipe (opnctx_t ctx, LONG rvid) if (!(peerctx->access_code & GENERIC_WRITE)) { SetLastError (ERROR_INVALID_ACCESS); - log_debug (" make_pipe(%p) write end -> invalid access\n", ctx); - goto leave; + log_debug (" make_pipe (ctx=0x%p): error: peer is not writer\n", ctx); + return FALSE; } - peerctx->space_available = CreateEvent (NULL, FALSE, FALSE, NULL); - peerctx->data_available = CreateEvent (NULL, FALSE, FALSE, NULL); - - ctx->assoc = peerctx; - peerctx->assoc = ctx; - ctx->is_write = 0; - peerctx->is_write = 1; - result = TRUE; } else if ((ctx->access_code & GENERIC_WRITE)) { @@ -625,29 +659,39 @@ make_pipe (opnctx_t ctx, LONG rvid) if (!(peerctx->access_code & GENERIC_READ)) { SetLastError (ERROR_INVALID_ACCESS); - log_debug (" make_pipe(%p) read_end -> invalid access\n", ctx); - goto leave; + log_debug (" make_pipe (ctx=0x%p): error: peer is not reader\n", ctx); + return FALSE; } - ctx->space_available = CreateEvent (NULL, FALSE, FALSE, NULL); - ctx->data_available = CreateEvent (NULL, FALSE, FALSE, NULL); - - ctx->assoc = peerctx; - peerctx->assoc = ctx; - ctx->is_write = 1; - peerctx->is_write = 0; - result = TRUE; } else { SetLastError (ERROR_INVALID_ACCESS); - log_debug (" make_pipe(%p) no_access -> invalid access\n", ctx); - goto leave; + log_debug (" make_pipe (ctx=0x%p): error: invalid access\n", ctx); + return FALSE; } - leave: - if (peerctx) - unlock_opnctx (peerctx); - return result; + /* Make sure the peer has a pipe implementation to be shared. If + not yet, create one. */ + if (! peerctx->pipeimpl) + { + peerctx->pipeimpl = pipeimpl_new (); + if (! peerctx->pipeimpl) + { + log_debug (" make_pipe (ctx=0x%p): error: can't create pipe\n", + ctx); + return FALSE; + } + log_debug (" make_pipe (ctx=0x%p): created pipe 0x%p\n", + ctx, peerctx->pipeimpl); + } + EnterCriticalSection (&peerctx->pipeimpl->critsect); + peerctx->pipeimpl->refcnt++; + ctx->pipeimpl = peerctx->pipeimpl; + LeaveCriticalSection (&peerctx->pipeimpl->critsect); + log_debug (" make_pipe (ctx=0x%p): success: combined with peer ctx=0x%p (pipe 0x%p)\n", + ctx, peerctx, peerctx->pipeimpl); + + return TRUE; } @@ -659,19 +703,34 @@ GPG_IOControl (DWORD opnctx_arg, DWORD code, void *inbuf, DWORD inbuflen, BOOL result = FALSE; LONG rvid; - log_debug ("GPG_IOControl(%p, %d)\n", (void*)opnctx, code); - if (!validate_and_lock_opnctx (opnctx, LOCK_TRY)) - return FALSE; + log_debug ("GPG_IOControl (ctx=0x%p, %08x)\n", (void*)opnctx, code); + + EnterCriticalSection (&opnctx_table_cs); + opnctx = verify_opnctx (opnctx); + if (!opnctx) + { + log_debug ("GPG_IOControl (ctx=0x%p): error: could not access context\n", + (void*)opnctx); + goto leave; + } switch (code) { case GPGCEDEV_IOCTL_GET_RVID: - if (!opnctx || inbuf || inbuflen - || !outbuf || outbuflen < sizeof (LONG)) + log_debug ("GPG_IOControl (ctx=0x%p): code: GET_RVID\n", (void*)opnctx); + if (inbuf || inbuflen || !outbuf || outbuflen < sizeof (LONG)) { + log_debug ("GPG_IOControl (ctx=0x%p): error: invalid parameter\n", + (void*)opnctx); SetLastError (ERROR_INVALID_PARAMETER); goto leave; } + + if (! opnctx->rvid) + opnctx->rvid = create_rendezvous_id (); + log_debug ("GPG_IOControl (ctx=0x%p): returning rvid: %08lx\n", + (void*)opnctx, opnctx->rvid); + memcpy (outbuf, &opnctx->rvid, sizeof (LONG)); if (actualoutlen) *actualoutlen = sizeof (LONG); @@ -679,28 +738,38 @@ GPG_IOControl (DWORD opnctx_arg, DWORD code, void *inbuf, DWORD inbuflen, break; case GPGCEDEV_IOCTL_MAKE_PIPE: - if (!opnctx || !inbuf || inbuflen < sizeof (LONG) - || outbuf || outbuflen || actualoutlen ) + log_debug ("GPG_IOControl (ctx=0x%p): code: MAKE_PIPE\n", (void*)opnctx); + if (!inbuf || inbuflen < sizeof (LONG) + || outbuf || outbuflen || actualoutlen) { + log_debug ("GPG_IOControl (ctx=0x%p): error: invalid parameter\n", + (void*)opnctx); SetLastError (ERROR_INVALID_PARAMETER); goto leave; } memcpy (&rvid, inbuf, sizeof (LONG)); + log_debug ("GPG_IOControl (ctx=0x%p): requesting to finish pipe for rvid: %08lx\n", + (void*)opnctx, rvid); if (make_pipe (opnctx, rvid)) result = TRUE; break; case IOCTL_PSL_NOTIFY: + log_debug ("GPG_IOControl (ctx=0x%p): code: NOTIFY\n", (void*)opnctx); /* Unexpected process termination. */ break; default: + log_debug ("GPG_IOControl (ctx=0x%p): code: (unknown)\n", (void*)opnctx); SetLastError (ERROR_INVALID_PARAMETER); break; } + log_debug ("GPG_IOControl (ctx=0x%p): success: result=%d\n", (void*)opnctx, + result); + leave: - unlock_opnctx (opnctx); + LeaveCriticalSection (&opnctx_table_cs); return result; } |