|
Packit |
fc043f |
/* w32-estream.c - es_poll support on W32.
|
|
Packit |
fc043f |
* Copyright (C) 2000 Werner Koch (dd9jn)
|
|
Packit |
fc043f |
* Copyright (C) 2001, 2002, 2003, 2004, 2007, 2010, 2016 g10 Code GmbH
|
|
Packit |
fc043f |
*
|
|
Packit |
fc043f |
* This file is part of libgpg-error.
|
|
Packit |
fc043f |
*
|
|
Packit |
fc043f |
* libgpg-error is free software; you can redistribute it and/or
|
|
Packit |
fc043f |
* modify it under the terms of the GNU Lesser General Public License
|
|
Packit |
fc043f |
* as published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
fc043f |
* the License, or (at your option) any later version.
|
|
Packit |
fc043f |
*
|
|
Packit |
fc043f |
* libgpg-error is distributed in the hope that it will be useful, but
|
|
Packit |
fc043f |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
fc043f |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
fc043f |
* Lesser General Public License for more details.
|
|
Packit |
fc043f |
*
|
|
Packit |
fc043f |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
fc043f |
* License along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
Packit |
fc043f |
*/
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/*
|
|
Packit |
fc043f |
* This file is based on GPGME's w32-io.c started in 2001.
|
|
Packit |
fc043f |
*/
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
#ifdef HAVE_CONFIG_H
|
|
Packit |
fc043f |
#include <config.h>
|
|
Packit |
fc043f |
#endif
|
|
Packit |
fc043f |
#include <stdio.h>
|
|
Packit |
fc043f |
#include <stdlib.h>
|
|
Packit |
fc043f |
#include <string.h>
|
|
Packit |
fc043f |
#include <assert.h>
|
|
Packit |
fc043f |
#include <errno.h>
|
|
Packit |
fc043f |
#include <fcntl.h>
|
|
Packit |
fc043f |
#ifdef HAVE_SYS_TIME_H
|
|
Packit |
fc043f |
# include <sys/time.h>
|
|
Packit |
fc043f |
#endif
|
|
Packit |
fc043f |
#ifdef HAVE_SYS_TYPES_H
|
|
Packit |
fc043f |
# include <sys/types.h>
|
|
Packit |
fc043f |
#endif
|
|
Packit |
fc043f |
#include <io.h>
|
|
Packit |
fc043f |
#include <windows.h>
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* Enable tracing. The value is the module name to be printed. */
|
|
Packit |
fc043f |
/*#define ENABLE_TRACING "estream" */
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
#include "gpgrt-int.h"
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/*
|
|
Packit |
fc043f |
* In order to support es_poll on Windows, we create a proxy shim that
|
|
Packit |
fc043f |
* we use as the estream I/O functions. This shim creates reader and
|
|
Packit |
fc043f |
* writer threads that use the original I/O functions.
|
|
Packit |
fc043f |
*/
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* Calculate array dimension. */
|
|
Packit |
fc043f |
#ifndef DIM
|
|
Packit |
fc043f |
#define DIM(array) (sizeof (array) / sizeof (*array))
|
|
Packit |
fc043f |
#endif
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
#define READBUF_SIZE 4096
|
|
Packit |
fc043f |
#define WRITEBUF_SIZE 4096
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
typedef struct estream_cookie_w32_pollable *estream_cookie_w32_pollable_t;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
struct reader_context_s
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
estream_cookie_w32_pollable_t pcookie;
|
|
Packit |
fc043f |
HANDLE thread_hd;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
CRITICAL_SECTION mutex;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
int stop_me;
|
|
Packit |
fc043f |
int eof;
|
|
Packit |
fc043f |
int eof_shortcut;
|
|
Packit |
fc043f |
int error;
|
|
Packit |
fc043f |
int error_code;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* This is manually reset. */
|
|
Packit |
fc043f |
HANDLE have_data_ev;
|
|
Packit |
fc043f |
/* This is automatically reset. */
|
|
Packit |
fc043f |
HANDLE have_space_ev;
|
|
Packit |
fc043f |
/* This is manually reset but actually only triggered once. */
|
|
Packit |
fc043f |
HANDLE close_ev;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
size_t readpos, writepos;
|
|
Packit |
fc043f |
char buffer[READBUF_SIZE];
|
|
Packit |
fc043f |
};
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
struct writer_context_s
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
estream_cookie_w32_pollable_t pcookie;
|
|
Packit |
fc043f |
HANDLE thread_hd;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
CRITICAL_SECTION mutex;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
int stop_me;
|
|
Packit |
fc043f |
int error;
|
|
Packit |
fc043f |
int error_code;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* This is manually reset. */
|
|
Packit |
fc043f |
HANDLE have_data;
|
|
Packit |
fc043f |
HANDLE is_empty;
|
|
Packit |
fc043f |
HANDLE close_ev;
|
|
Packit |
fc043f |
size_t nbytes;
|
|
Packit |
fc043f |
char buffer[WRITEBUF_SIZE];
|
|
Packit |
fc043f |
};
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* Cookie for pollable objects. */
|
|
Packit |
fc043f |
struct estream_cookie_w32_pollable
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
unsigned int modeflags;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
struct cookie_io_functions_s next_functions;
|
|
Packit |
fc043f |
void *next_cookie;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
struct reader_context_s *reader;
|
|
Packit |
fc043f |
struct writer_context_s *writer;
|
|
Packit |
fc043f |
};
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
static HANDLE
|
|
Packit |
fc043f |
set_synchronize (HANDLE hd)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
#ifdef HAVE_W32CE_SYSTEM
|
|
Packit |
fc043f |
return hd;
|
|
Packit |
fc043f |
#else
|
|
Packit |
fc043f |
HANDLE new_hd;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* For NT we have to set the sync flag. It seems that the only way
|
|
Packit |
fc043f |
to do it is by duplicating the handle. Tsss... */
|
|
Packit |
fc043f |
if (!DuplicateHandle (GetCurrentProcess (), hd,
|
|
Packit |
fc043f |
GetCurrentProcess (), &new_hd,
|
|
Packit |
fc043f |
EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, 0))
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace_errno (1, ("DuplicateHandle failed: ec=%d", (int)GetLastError ()));
|
|
Packit |
fc043f |
/* FIXME: Should translate the error code. */
|
|
Packit |
fc043f |
_gpg_err_set_errno (EIO);
|
|
Packit |
fc043f |
return INVALID_HANDLE_VALUE;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
CloseHandle (hd);
|
|
Packit |
fc043f |
return new_hd;
|
|
Packit |
fc043f |
#endif
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
static DWORD CALLBACK
|
|
Packit |
fc043f |
reader (void *arg)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
struct reader_context_s *ctx = arg;
|
|
Packit |
fc043f |
int nbytes;
|
|
Packit |
fc043f |
ssize_t nread;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
trace (("%p: reader starting", ctx));
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
for (;;)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
EnterCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
/* Leave a 1 byte gap so that we can see whether it is empty or
|
|
Packit |
fc043f |
full. */
|
|
Packit |
fc043f |
while ((ctx->writepos + 1) % READBUF_SIZE == ctx->readpos)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
/* Wait for space. */
|
|
Packit |
fc043f |
if (!ResetEvent (ctx->have_space_ev))
|
|
Packit |
fc043f |
trace (("%p: ResetEvent failed: ec=%d", ctx, (int)GetLastError()));
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
trace (("%p: waiting for space", ctx));
|
|
Packit |
fc043f |
WaitForSingleObject (ctx->have_space_ev, INFINITE);
|
|
Packit |
fc043f |
trace (("%p: got space", ctx));
|
|
Packit |
fc043f |
EnterCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
assert (((ctx->writepos + 1) % READBUF_SIZE != ctx->readpos));
|
|
Packit |
fc043f |
if (ctx->stop_me)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
break;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
nbytes = (ctx->readpos + READBUF_SIZE
|
|
Packit |
fc043f |
- ctx->writepos - 1) % READBUF_SIZE;
|
|
Packit |
fc043f |
assert (nbytes);
|
|
Packit |
fc043f |
if (nbytes > READBUF_SIZE - ctx->writepos)
|
|
Packit |
fc043f |
nbytes = READBUF_SIZE - ctx->writepos;
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
trace (("%p: reading up to %d bytes", ctx, nbytes));
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
nread = ctx->pcookie->next_functions.public.func_read
|
|
Packit |
fc043f |
(ctx->pcookie->next_cookie, ctx->buffer + ctx->writepos, nbytes);
|
|
Packit |
fc043f |
trace (("%p: got %d bytes", ctx, nread));
|
|
Packit |
fc043f |
if (nread < 0)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
ctx->error_code = (int) errno;
|
|
Packit |
fc043f |
/* NOTE (W32CE): Do not ignore ERROR_BUSY! Check at
|
|
Packit |
fc043f |
least stop_me if that happens. */
|
|
Packit |
fc043f |
if (ctx->error_code == ERROR_BROKEN_PIPE)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
ctx->eof = 1;
|
|
Packit |
fc043f |
trace (("%p: got EOF (broken pipe)", ctx));
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
else
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
ctx->error = 1;
|
|
Packit |
fc043f |
trace (("%p: read error: ec=%d", ctx, ctx->error_code));
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
break;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
EnterCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
if (ctx->stop_me)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
break;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
if (!nread)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
ctx->eof = 1;
|
|
Packit |
fc043f |
trace (("%p: got eof", ctx));
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
break;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE;
|
|
Packit |
fc043f |
if (!SetEvent (ctx->have_data_ev))
|
|
Packit |
fc043f |
trace (("%p: SetEvent (%p) failed: ec=%d",
|
|
Packit |
fc043f |
ctx, ctx->have_data_ev, (int)GetLastError ()));
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
/* Indicate that we have an error or EOF. */
|
|
Packit |
fc043f |
if (!SetEvent (ctx->have_data_ev))
|
|
Packit |
fc043f |
trace (("%p: SetEvent (%p) failed: ec=%d",
|
|
Packit |
fc043f |
ctx, ctx->have_data_ev, (int)GetLastError ()));
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
trace (("%p: waiting for close", ctx));
|
|
Packit |
fc043f |
WaitForSingleObject (ctx->close_ev, INFINITE);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
CloseHandle (ctx->close_ev);
|
|
Packit |
fc043f |
CloseHandle (ctx->have_data_ev);
|
|
Packit |
fc043f |
CloseHandle (ctx->have_space_ev);
|
|
Packit |
fc043f |
CloseHandle (ctx->thread_hd);
|
|
Packit |
fc043f |
DeleteCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
free (ctx); /* Standard free! See comment in create_reader. */
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
return 0;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
static struct reader_context_s *
|
|
Packit |
fc043f |
create_reader (estream_cookie_w32_pollable_t pcookie)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
struct reader_context_s *ctx;
|
|
Packit |
fc043f |
SECURITY_ATTRIBUTES sec_attr;
|
|
Packit |
fc043f |
DWORD tid;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
memset (&sec_attr, 0, sizeof sec_attr);
|
|
Packit |
fc043f |
sec_attr.nLength = sizeof sec_attr;
|
|
Packit |
fc043f |
sec_attr.bInheritHandle = FALSE;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* The CTX must be allocated in standard system memory so that we
|
|
Packit |
fc043f |
* won't use any custom allocation handler which may use our lock
|
|
Packit |
fc043f |
* primitives for its implementation. The problem here is that the
|
|
Packit |
fc043f |
* syscall clamp mechanism (e.g. nPth) would be called recursively:
|
|
Packit |
fc043f |
* 1. For example by the caller of _gpgrt_w32_poll and 2. by
|
|
Packit |
fc043f |
* gpgrt_lock_lock on behalf of the the custom allocation and free
|
|
Packit |
fc043f |
* functions. */
|
|
Packit |
fc043f |
ctx = calloc (1, sizeof *ctx);
|
|
Packit |
fc043f |
if (!ctx)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
return NULL;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
ctx->pcookie = pcookie;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
ctx->have_data_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
|
|
Packit |
fc043f |
if (ctx->have_data_ev)
|
|
Packit |
fc043f |
ctx->have_space_ev = CreateEvent (&sec_attr, FALSE, TRUE, NULL);
|
|
Packit |
fc043f |
if (ctx->have_space_ev)
|
|
Packit |
fc043f |
ctx->close_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
|
|
Packit |
fc043f |
if (!ctx->have_data_ev || !ctx->have_space_ev || !ctx->close_ev)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("%p: CreateEvent failed: ec=%d", ctx, (int)GetLastError ()));
|
|
Packit |
fc043f |
if (ctx->have_data_ev)
|
|
Packit |
fc043f |
CloseHandle (ctx->have_data_ev);
|
|
Packit |
fc043f |
if (ctx->have_space_ev)
|
|
Packit |
fc043f |
CloseHandle (ctx->have_space_ev);
|
|
Packit |
fc043f |
if (ctx->close_ev)
|
|
Packit |
fc043f |
CloseHandle (ctx->close_ev);
|
|
Packit |
fc043f |
_gpgrt_free (ctx);
|
|
Packit |
fc043f |
return NULL;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
ctx->have_data_ev = set_synchronize (ctx->have_data_ev);
|
|
Packit |
fc043f |
InitializeCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
#ifdef HAVE_W32CE_SYSTEM
|
|
Packit |
fc043f |
ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, reader, ctx,
|
|
Packit |
fc043f |
STACK_SIZE_PARAM_IS_A_RESERVATION, &tid;;
|
|
Packit |
fc043f |
#else
|
|
Packit |
fc043f |
ctx->thread_hd = CreateThread (&sec_attr, 0, reader, ctx, 0, &tid;;
|
|
Packit |
fc043f |
#endif
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (!ctx->thread_hd)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("%p: CreateThread failed: ec=%d", ctx, (int)GetLastError ()));
|
|
Packit |
fc043f |
DeleteCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
if (ctx->have_data_ev)
|
|
Packit |
fc043f |
CloseHandle (ctx->have_data_ev);
|
|
Packit |
fc043f |
if (ctx->have_space_ev)
|
|
Packit |
fc043f |
CloseHandle (ctx->have_space_ev);
|
|
Packit |
fc043f |
if (ctx->close_ev)
|
|
Packit |
fc043f |
CloseHandle (ctx->close_ev);
|
|
Packit |
fc043f |
_gpgrt_free (ctx);
|
|
Packit |
fc043f |
return NULL;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
else
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
#if 0
|
|
Packit |
fc043f |
/* We set the priority of the thread higher because we know that
|
|
Packit |
fc043f |
it only runs for a short time. This greatly helps to
|
|
Packit |
fc043f |
increase the performance of the I/O. */
|
|
Packit |
fc043f |
SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
|
|
Packit |
fc043f |
#endif
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
return ctx;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* Prepare destruction of the reader thread for CTX. Returns 0 if a
|
|
Packit |
fc043f |
call to this function is sufficient and destroy_reader_finish shall
|
|
Packit |
fc043f |
not be called. */
|
|
Packit |
fc043f |
static void
|
|
Packit |
fc043f |
destroy_reader (struct reader_context_s *ctx)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
EnterCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
ctx->stop_me = 1;
|
|
Packit |
fc043f |
if (ctx->have_space_ev)
|
|
Packit |
fc043f |
SetEvent (ctx->have_space_ev);
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
#ifdef HAVE_W32CE_SYSTEM
|
|
Packit |
fc043f |
/* Scenario: We never create a full pipe, but already started
|
|
Packit |
fc043f |
reading. Then we need to unblock the reader in the pipe driver
|
|
Packit |
fc043f |
to make our reader thread notice that we want it to go away. */
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (ctx->file_hd != INVALID_HANDLE_VALUE)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
|
|
Packit |
fc043f |
NULL, 0, NULL, 0, NULL, NULL))
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("%p: unblock control call failed: ec=%d",
|
|
Packit |
fc043f |
ctx, (int)GetLastError ()));
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
#endif
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* XXX is it feasible to unblock the thread? */
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* After setting this event CTX is void. */
|
|
Packit |
fc043f |
SetEvent (ctx->close_ev);
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/*
|
|
Packit |
fc043f |
* Read function for pollable objects.
|
|
Packit |
fc043f |
*/
|
|
Packit |
fc043f |
static gpgrt_ssize_t
|
|
Packit |
fc043f |
func_w32_pollable_read (void *cookie, void *buffer, size_t count)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
estream_cookie_w32_pollable_t pcookie = cookie;
|
|
Packit |
fc043f |
gpgrt_ssize_t nread;
|
|
Packit |
fc043f |
struct reader_context_s *ctx;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
trace (("%p: enter buffer=%p count=%u", cookie, buffer, count));
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* FIXME: implement pending check if COUNT==0 */
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
ctx = pcookie->reader;
|
|
Packit |
fc043f |
if (ctx == NULL)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
pcookie->reader = ctx = create_reader (pcookie);
|
|
Packit |
fc043f |
if (!ctx)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
_gpg_err_set_errno (EBADF);
|
|
Packit |
fc043f |
nread = -1;
|
|
Packit |
fc043f |
goto leave;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
trace (("%p: new reader %p", cookie, pcookie->reader));
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (ctx->eof_shortcut)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
nread = 0;
|
|
Packit |
fc043f |
goto leave;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
EnterCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
trace (("%p: readpos: %d, writepos %d", cookie, ctx->readpos, ctx->writepos));
|
|
Packit |
fc043f |
if (ctx->readpos == ctx->writepos && !ctx->error)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
/* No data available. */
|
|
Packit |
fc043f |
int eof = ctx->eof;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (pcookie->modeflags & O_NONBLOCK && ! eof)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
_gpg_err_set_errno (EAGAIN);
|
|
Packit |
fc043f |
nread = -1;
|
|
Packit |
fc043f |
goto leave;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
trace (("%p: waiting for data", cookie));
|
|
Packit |
fc043f |
WaitForSingleObject (ctx->have_data_ev, INFINITE);
|
|
Packit |
fc043f |
trace (("%p: data available", cookie));
|
|
Packit |
fc043f |
EnterCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (ctx->readpos == ctx->writepos || ctx->error)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
ctx->eof_shortcut = 1;
|
|
Packit |
fc043f |
if (ctx->eof)
|
|
Packit |
fc043f |
return 0;
|
|
Packit |
fc043f |
if (!ctx->error)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("%p: EOF but ctx->eof flag not set", cookie));
|
|
Packit |
fc043f |
nread = 0;
|
|
Packit |
fc043f |
goto leave;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
_gpg_err_set_errno (ctx->error_code);
|
|
Packit |
fc043f |
return -1;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
nread = ctx->readpos < ctx->writepos
|
|
Packit |
fc043f |
? ctx->writepos - ctx->readpos
|
|
Packit |
fc043f |
: READBUF_SIZE - ctx->readpos;
|
|
Packit |
fc043f |
if (nread > count)
|
|
Packit |
fc043f |
nread = count;
|
|
Packit |
fc043f |
memcpy (buffer, ctx->buffer + ctx->readpos, nread);
|
|
Packit |
fc043f |
ctx->readpos = (ctx->readpos + nread) % READBUF_SIZE;
|
|
Packit |
fc043f |
if (ctx->readpos == ctx->writepos && !ctx->eof)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
if (!ResetEvent (ctx->have_data_ev))
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("%p: ResetEvent failed: ec=%d",
|
|
Packit |
fc043f |
cookie, (int)GetLastError ()));
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
/* FIXME: Should translate the error code. */
|
|
Packit |
fc043f |
_gpg_err_set_errno (EIO);
|
|
Packit |
fc043f |
nread = -1;
|
|
Packit |
fc043f |
goto leave;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
if (!SetEvent (ctx->have_space_ev))
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("%p: SetEvent (%p) failed: ec=%d",
|
|
Packit |
fc043f |
cookie, ctx->have_space_ev, (int)GetLastError ()));
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
/* FIXME: Should translate the error code. */
|
|
Packit |
fc043f |
_gpg_err_set_errno (EIO);
|
|
Packit |
fc043f |
nread = -1;
|
|
Packit |
fc043f |
goto leave;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
leave:
|
|
Packit |
fc043f |
trace_errno (nread==-1,("%p: leave nread=%d", cookie, (int)nread));
|
|
Packit |
fc043f |
return nread;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* The writer does use a simple buffering strategy so that we are
|
|
Packit |
fc043f |
informed about write errors as soon as possible (i. e. with the the
|
|
Packit |
fc043f |
next call to the write function. */
|
|
Packit |
fc043f |
static DWORD CALLBACK
|
|
Packit |
fc043f |
writer (void *arg)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
struct writer_context_s *ctx = arg;
|
|
Packit |
fc043f |
ssize_t nwritten;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
trace (("%p: writer starting", ctx));
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
for (;;)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
EnterCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
if (ctx->stop_me && !ctx->nbytes)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
break;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
if (!ctx->nbytes)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
if (!SetEvent (ctx->is_empty))
|
|
Packit |
fc043f |
trace (("%p: SetEvent failed: ec=%d", ctx, (int)GetLastError ()));
|
|
Packit |
fc043f |
if (!ResetEvent (ctx->have_data))
|
|
Packit |
fc043f |
trace (("%p: ResetEvent failed: ec=%d", ctx, (int)GetLastError ()));
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
trace (("%p: idle", ctx));
|
|
Packit |
fc043f |
WaitForSingleObject (ctx->have_data, INFINITE);
|
|
Packit |
fc043f |
trace (("%p: got data to write", ctx));
|
|
Packit |
fc043f |
EnterCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
if (ctx->stop_me && !ctx->nbytes)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
break;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
trace (("%p: writing up to %d bytes", ctx, ctx->nbytes));
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
nwritten = ctx->pcookie->next_functions.public.func_write
|
|
Packit |
fc043f |
(ctx->pcookie->next_cookie, ctx->buffer, ctx->nbytes);
|
|
Packit |
fc043f |
trace (("%p: wrote %d bytes", ctx, nwritten));
|
|
Packit |
fc043f |
if (nwritten < 1)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
/* XXX */
|
|
Packit |
fc043f |
if (errno == ERROR_BUSY)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
/* Probably stop_me is set now. */
|
|
Packit |
fc043f |
trace (("%p: pipe busy (unblocked?)", ctx));
|
|
Packit |
fc043f |
continue;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
ctx->error_code = errno;
|
|
Packit |
fc043f |
ctx->error = 1;
|
|
Packit |
fc043f |
trace (("%p: write error: ec=%d", ctx, ctx->error_code));
|
|
Packit |
fc043f |
break;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
EnterCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
ctx->nbytes -= nwritten;
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
/* Indicate that we have an error. */
|
|
Packit |
fc043f |
if (!SetEvent (ctx->is_empty))
|
|
Packit |
fc043f |
trace (("%p: SetEvent failed: ec=%d", ctx, (int)GetLastError ()));
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
trace (("%p: waiting for close", ctx));
|
|
Packit |
fc043f |
WaitForSingleObject (ctx->close_ev, INFINITE);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (ctx->nbytes)
|
|
Packit |
fc043f |
trace (("%p: still %d bytes in buffer at close time", ctx, ctx->nbytes));
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
CloseHandle (ctx->close_ev);
|
|
Packit |
fc043f |
CloseHandle (ctx->have_data);
|
|
Packit |
fc043f |
CloseHandle (ctx->is_empty);
|
|
Packit |
fc043f |
CloseHandle (ctx->thread_hd);
|
|
Packit |
fc043f |
DeleteCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
trace (("%p: writer is destroyed", ctx));
|
|
Packit |
fc043f |
free (ctx); /* Standard free! See comment in create_writer. */
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
return 0;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
static struct writer_context_s *
|
|
Packit |
fc043f |
create_writer (estream_cookie_w32_pollable_t pcookie)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
struct writer_context_s *ctx;
|
|
Packit |
fc043f |
SECURITY_ATTRIBUTES sec_attr;
|
|
Packit |
fc043f |
DWORD tid;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
memset (&sec_attr, 0, sizeof sec_attr);
|
|
Packit |
fc043f |
sec_attr.nLength = sizeof sec_attr;
|
|
Packit |
fc043f |
sec_attr.bInheritHandle = FALSE;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* See comment at create_reader. */
|
|
Packit |
fc043f |
ctx = calloc (1, sizeof *ctx);
|
|
Packit |
fc043f |
if (!ctx)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
return NULL;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
ctx->pcookie = pcookie;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
ctx->have_data = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
|
|
Packit |
fc043f |
if (ctx->have_data)
|
|
Packit |
fc043f |
ctx->is_empty = CreateEvent (&sec_attr, TRUE, TRUE, NULL);
|
|
Packit |
fc043f |
if (ctx->is_empty)
|
|
Packit |
fc043f |
ctx->close_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
|
|
Packit |
fc043f |
if (!ctx->have_data || !ctx->is_empty || !ctx->close_ev)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("%p: CreateEvent failed: ec=%d", ctx, (int)GetLastError ()));
|
|
Packit |
fc043f |
if (ctx->have_data)
|
|
Packit |
fc043f |
CloseHandle (ctx->have_data);
|
|
Packit |
fc043f |
if (ctx->is_empty)
|
|
Packit |
fc043f |
CloseHandle (ctx->is_empty);
|
|
Packit |
fc043f |
if (ctx->close_ev)
|
|
Packit |
fc043f |
CloseHandle (ctx->close_ev);
|
|
Packit |
fc043f |
_gpgrt_free (ctx);
|
|
Packit |
fc043f |
return NULL;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
ctx->is_empty = set_synchronize (ctx->is_empty);
|
|
Packit |
fc043f |
InitializeCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
#ifdef HAVE_W32CE_SYSTEM
|
|
Packit |
fc043f |
ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, writer, ctx,
|
|
Packit |
fc043f |
STACK_SIZE_PARAM_IS_A_RESERVATION, &tid;;
|
|
Packit |
fc043f |
#else
|
|
Packit |
fc043f |
ctx->thread_hd = CreateThread (&sec_attr, 0, writer, ctx, 0, &tid );
|
|
Packit |
fc043f |
#endif
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (!ctx->thread_hd)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("%p: CreateThread failed: ec=%d", ctx, (int)GetLastError ()));
|
|
Packit |
fc043f |
DeleteCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
if (ctx->have_data)
|
|
Packit |
fc043f |
CloseHandle (ctx->have_data);
|
|
Packit |
fc043f |
if (ctx->is_empty)
|
|
Packit |
fc043f |
CloseHandle (ctx->is_empty);
|
|
Packit |
fc043f |
if (ctx->close_ev)
|
|
Packit |
fc043f |
CloseHandle (ctx->close_ev);
|
|
Packit |
fc043f |
_gpgrt_free (ctx);
|
|
Packit |
fc043f |
return NULL;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
else
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
#if 0
|
|
Packit |
fc043f |
/* We set the priority of the thread higher because we know
|
|
Packit |
fc043f |
that it only runs for a short time. This greatly helps to
|
|
Packit |
fc043f |
increase the performance of the I/O. */
|
|
Packit |
fc043f |
SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
|
|
Packit |
fc043f |
#endif
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
return ctx;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
static void
|
|
Packit |
fc043f |
destroy_writer (struct writer_context_s *ctx)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("%p: enter pollable_destroy_writer", ctx));
|
|
Packit |
fc043f |
EnterCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
trace (("%p: setting stopme", ctx));
|
|
Packit |
fc043f |
ctx->stop_me = 1;
|
|
Packit |
fc043f |
if (ctx->have_data)
|
|
Packit |
fc043f |
SetEvent (ctx->have_data);
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
trace (("%p: waiting for empty", ctx));
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* Give the writer a chance to flush the buffer. */
|
|
Packit |
fc043f |
WaitForSingleObject (ctx->is_empty, INFINITE);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
#ifdef HAVE_W32CE_SYSTEM
|
|
Packit |
fc043f |
/* Scenario: We never create a full pipe, but already started
|
|
Packit |
fc043f |
writing more than the pipe buffer. Then we need to unblock the
|
|
Packit |
fc043f |
writer in the pipe driver to make our writer thread notice that
|
|
Packit |
fc043f |
we want it to go away. */
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
|
|
Packit |
fc043f |
NULL, 0, NULL, 0, NULL, NULL))
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("%p: unblock control call failed: ec=%d",
|
|
Packit |
fc043f |
ctx, (int)GetLastError ()));
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
#endif
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* After setting this event CTX is void. */
|
|
Packit |
fc043f |
trace (("%p: set close_ev", ctx));
|
|
Packit |
fc043f |
SetEvent (ctx->close_ev);
|
|
Packit |
fc043f |
trace (("%p: leave pollable_destroy_writer", ctx));
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/*
|
|
Packit |
fc043f |
* Write function for pollable objects.
|
|
Packit |
fc043f |
*/
|
|
Packit |
fc043f |
static gpgrt_ssize_t
|
|
Packit |
fc043f |
func_w32_pollable_write (void *cookie, const void *buffer, size_t count)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
estream_cookie_w32_pollable_t pcookie = cookie;
|
|
Packit |
fc043f |
struct writer_context_s *ctx = pcookie->writer;
|
|
Packit |
fc043f |
int nwritten;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
trace (("%p: enter buffer: %p count: %d", cookie, buffer, count));
|
|
Packit |
fc043f |
if (count == 0)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
nwritten = 0;
|
|
Packit |
fc043f |
goto leave;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (ctx == NULL)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
pcookie->writer = ctx = create_writer (pcookie);
|
|
Packit |
fc043f |
if (!ctx)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
nwritten = -1;
|
|
Packit |
fc043f |
goto leave;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
trace (("%p: new writer %p", cookie, pcookie->writer));
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
EnterCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
trace (("%p: buffer: %p, count: %d, nbytes: %d",
|
|
Packit |
fc043f |
cookie, buffer, count, ctx->nbytes));
|
|
Packit |
fc043f |
if (!ctx->error && ctx->nbytes)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
/* Bytes are pending for send. */
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* Reset the is_empty event. Better safe than sorry. */
|
|
Packit |
fc043f |
if (!ResetEvent (ctx->is_empty))
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("%p: ResetEvent failed: ec=%d",
|
|
Packit |
fc043f |
cookie, (int)GetLastError ()));
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
/* FIXME: Should translate the error code. */
|
|
Packit |
fc043f |
_gpg_err_set_errno (EIO);
|
|
Packit |
fc043f |
nwritten = -1;
|
|
Packit |
fc043f |
goto leave;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (pcookie->modeflags & O_NONBLOCK)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("%p: would block", cookie));
|
|
Packit |
fc043f |
_gpg_err_set_errno (EAGAIN);
|
|
Packit |
fc043f |
nwritten = -1;
|
|
Packit |
fc043f |
goto leave;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
trace (("%p: waiting for empty buffer", cookie));
|
|
Packit |
fc043f |
WaitForSingleObject (ctx->is_empty, INFINITE);
|
|
Packit |
fc043f |
trace (("%p: buffer is empty", cookie));
|
|
Packit |
fc043f |
EnterCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (ctx->error)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
if (ctx->error_code == ERROR_NO_DATA)
|
|
Packit |
fc043f |
_gpg_err_set_errno (EPIPE);
|
|
Packit |
fc043f |
else
|
|
Packit |
fc043f |
_gpg_err_set_errno (EIO);
|
|
Packit |
fc043f |
nwritten = -1;
|
|
Packit |
fc043f |
goto leave;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* If no error occurred, the number of bytes in the buffer must be
|
|
Packit |
fc043f |
zero. */
|
|
Packit |
fc043f |
assert (!ctx->nbytes);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (count > WRITEBUF_SIZE)
|
|
Packit |
fc043f |
count = WRITEBUF_SIZE;
|
|
Packit |
fc043f |
memcpy (ctx->buffer, buffer, count);
|
|
Packit |
fc043f |
ctx->nbytes = count;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* We have to reset the is_empty event early, because it is also
|
|
Packit |
fc043f |
used by the select() implementation to probe the channel. */
|
|
Packit |
fc043f |
if (!ResetEvent (ctx->is_empty))
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("%p: ResetEvent failed: ec=%d", cookie, (int)GetLastError ()));
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
/* FIXME: Should translate the error code. */
|
|
Packit |
fc043f |
_gpg_err_set_errno (EIO);
|
|
Packit |
fc043f |
nwritten = -1;
|
|
Packit |
fc043f |
goto leave;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
if (!SetEvent (ctx->have_data))
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("%p: SetEvent failed: ec=%d", cookie, (int)GetLastError ()));
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
/* FIXME: Should translate the error code. */
|
|
Packit |
fc043f |
_gpg_err_set_errno (EIO);
|
|
Packit |
fc043f |
nwritten = -1;
|
|
Packit |
fc043f |
goto leave;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
LeaveCriticalSection (&ctx->mutex);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
nwritten = count;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
leave:
|
|
Packit |
fc043f |
trace_errno (nwritten==-1,("%p: leave nwritten=%d", cookie, nwritten));
|
|
Packit |
fc043f |
return nwritten;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/* This is the core of _gpgrt_poll. The caller needs to make sure that
|
|
Packit |
fc043f |
* the syscall clamp has been engaged. */
|
|
Packit |
fc043f |
int
|
|
Packit |
fc043f |
_gpgrt_w32_poll (gpgrt_poll_t *fds, size_t nfds, int timeout)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
|
|
Packit |
fc043f |
int waitidx[MAXIMUM_WAIT_OBJECTS];
|
|
Packit |
fc043f |
#ifdef ENABLE_TRACING
|
|
Packit |
fc043f |
char waitinfo[MAXIMUM_WAIT_OBJECTS];
|
|
Packit |
fc043f |
#endif
|
|
Packit |
fc043f |
unsigned int code;
|
|
Packit |
fc043f |
int nwait;
|
|
Packit |
fc043f |
int i;
|
|
Packit |
fc043f |
int any;
|
|
Packit |
fc043f |
int count;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
#if 0
|
|
Packit |
fc043f |
restart:
|
|
Packit |
fc043f |
#endif
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
any = 0;
|
|
Packit |
fc043f |
nwait = 0;
|
|
Packit |
fc043f |
count = 0;
|
|
Packit |
fc043f |
for (i = 0; i < nfds; i++)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
struct estream_cookie_w32_pollable *pcookie;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (fds[i].ignore)
|
|
Packit |
fc043f |
continue;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (fds[i].stream->intern->kind != BACKEND_W32_POLLABLE)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
/* This stream does not support polling. */
|
|
Packit |
fc043f |
fds[i].got_err = 1;
|
|
Packit |
fc043f |
continue;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
pcookie = fds[i].stream->intern->cookie;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (fds[i].want_read || fds[i].want_write)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
/* XXX: What if one wants read and write, is that supported? */
|
|
Packit |
fc043f |
if (fds[i].want_read)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
struct reader_context_s *ctx = pcookie->reader;
|
|
Packit |
fc043f |
if (ctx == NULL)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
pcookie->reader = ctx = create_reader (pcookie);
|
|
Packit |
fc043f |
if (!ctx)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
/* FIXME: Is the error code appropriate? */
|
|
Packit |
fc043f |
_gpg_err_set_errno (EBADF);
|
|
Packit |
fc043f |
return -1;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
trace (("%p: new reader %p", pcookie, pcookie->reader));
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
trace (("%p: using reader %p", pcookie, pcookie->reader));
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (nwait >= DIM (waitbuf))
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("oops: too many objects for WFMO"));
|
|
Packit |
fc043f |
/* FIXME: Should translate the error code. */
|
|
Packit |
fc043f |
_gpg_err_set_errno (EIO);
|
|
Packit |
fc043f |
return -1;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
waitidx[nwait] = i;
|
|
Packit |
fc043f |
#ifdef ENABLE_TRACING
|
|
Packit |
fc043f |
waitinfo[nwait] = 'r';
|
|
Packit |
fc043f |
#endif /*ENABLE_TRACING*/
|
|
Packit |
fc043f |
waitbuf[nwait++] = ctx->have_data_ev;
|
|
Packit |
fc043f |
any = 1;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
else if (fds[i].want_write)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
struct writer_context_s *ctx = pcookie->writer;
|
|
Packit |
fc043f |
if (ctx == NULL)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
pcookie->writer = ctx = create_writer (pcookie);
|
|
Packit |
fc043f |
if (!ctx)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("oops: create writer failed"));
|
|
Packit |
fc043f |
/* FIXME: Is the error code appropriate? */
|
|
Packit |
fc043f |
_gpg_err_set_errno (EBADF);
|
|
Packit |
fc043f |
return -1;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
trace (("%p: new writer %p", pcookie, pcookie->writer));
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
trace (("%p: using writer %p", pcookie, pcookie->writer));
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (nwait >= DIM (waitbuf))
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("oops: Too many objects for WFMO"));
|
|
Packit |
fc043f |
/* FIXME: Should translate the error code. */
|
|
Packit |
fc043f |
_gpg_err_set_errno (EIO);
|
|
Packit |
fc043f |
return -1;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
waitidx[nwait] = i;
|
|
Packit |
fc043f |
#ifdef ENABLE_TRACING
|
|
Packit |
fc043f |
waitinfo[nwait] = 'w';
|
|
Packit |
fc043f |
#endif /*ENABLE_TRACING*/
|
|
Packit |
fc043f |
waitbuf[nwait++] = ctx->is_empty;
|
|
Packit |
fc043f |
any = 1;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
#ifdef ENABLE_TRACING
|
|
Packit |
fc043f |
trace_start (("poll on [ "));
|
|
Packit |
fc043f |
for (i = 0; i < nwait; i++)
|
|
Packit |
fc043f |
trace_append (("%d/%c ", waitidx[i], waitinfo[i]));
|
|
Packit |
fc043f |
trace_finish (("]"));
|
|
Packit |
fc043f |
#endif /*ENABLE_TRACING*/
|
|
Packit |
fc043f |
if (!any)
|
|
Packit |
fc043f |
return 0;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
code = WaitForMultipleObjects (nwait, waitbuf, 0,
|
|
Packit |
fc043f |
timeout == -1 ? INFINITE : timeout);
|
|
Packit |
fc043f |
if (code < WAIT_OBJECT_0 + nwait)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
/* This WFMO is a really silly function: It does return either
|
|
Packit |
fc043f |
the index of the signaled object or if 2 objects have been
|
|
Packit |
fc043f |
signalled at the same time, the index of the object with the
|
|
Packit |
fc043f |
lowest object is returned - so and how do we find out how
|
|
Packit |
fc043f |
many objects have been signaled???. The only solution I can
|
|
Packit |
fc043f |
imagine is to test each object starting with the returned
|
|
Packit |
fc043f |
index individually - how dull. */
|
|
Packit |
fc043f |
any = 0;
|
|
Packit |
fc043f |
for (i = code - WAIT_OBJECT_0; i < nwait; i++)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
if (WaitForSingleObject (waitbuf[i], 0) == WAIT_OBJECT_0)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
assert (waitidx[i] >=0 && waitidx[i] < nfds);
|
|
Packit |
fc043f |
/* XXX: What if one wants read and write, is that
|
|
Packit |
fc043f |
supported? */
|
|
Packit |
fc043f |
if (fds[waitidx[i]].want_read)
|
|
Packit |
fc043f |
fds[waitidx[i]].got_read = 1;
|
|
Packit |
fc043f |
else if (fds[waitidx[i]].want_write)
|
|
Packit |
fc043f |
fds[waitidx[i]].got_write = 1;
|
|
Packit |
fc043f |
any = 1;
|
|
Packit |
fc043f |
count++;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
if (!any)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("no signaled objects found after WFMO"));
|
|
Packit |
fc043f |
count = -1;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
else if (code == WAIT_TIMEOUT)
|
|
Packit |
fc043f |
trace (("WFMO timed out"));
|
|
Packit |
fc043f |
else if (code == WAIT_FAILED)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("WFMO failed: ec=%d", (int)GetLastError ()));
|
|
Packit |
fc043f |
#if 0
|
|
Packit |
fc043f |
if (GetLastError () == ERROR_INVALID_HANDLE)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
int k;
|
|
Packit |
fc043f |
int j = handle_to_fd (waitbuf[i]);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
trace (("WFMO invalid handle %d removed", j));
|
|
Packit |
fc043f |
for (k = 0 ; k < nfds; k++)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
if (fds[k].fd == j)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
fds[k].want_read = fds[k].want_write = 0;
|
|
Packit |
fc043f |
goto restart;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
trace ((" oops, or not???"));
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
#endif
|
|
Packit |
fc043f |
count = -1;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
else
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace (("WFMO returned %u", code));
|
|
Packit |
fc043f |
count = -1;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (count > 0)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
trace_start (("poll OK [ "));
|
|
Packit |
fc043f |
for (i = 0; i < nfds; i++)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
if (fds[i].ignore)
|
|
Packit |
fc043f |
continue;
|
|
Packit |
fc043f |
if (fds[i].got_read || fds[i].got_write)
|
|
Packit |
fc043f |
trace_append (("%c%d ", fds[i].want_read ? 'r' : 'w', i));
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
trace_finish (("]"));
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (count < 0)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
/* FIXME: Should determine a proper error code. */
|
|
Packit |
fc043f |
_gpg_err_set_errno (EIO);
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
return count;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/*
|
|
Packit |
fc043f |
* Implementation of pollable I/O on Windows.
|
|
Packit |
fc043f |
*/
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/*
|
|
Packit |
fc043f |
* Constructor for pollable objects.
|
|
Packit |
fc043f |
*/
|
|
Packit |
fc043f |
int
|
|
Packit |
fc043f |
_gpgrt_w32_pollable_create (void *_GPGRT__RESTRICT *_GPGRT__RESTRICT cookie,
|
|
Packit |
fc043f |
unsigned int modeflags,
|
|
Packit |
fc043f |
struct cookie_io_functions_s next_functions,
|
|
Packit |
fc043f |
void *next_cookie)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
estream_cookie_w32_pollable_t pcookie;
|
|
Packit |
fc043f |
int err;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
pcookie = _gpgrt_malloc (sizeof *pcookie);
|
|
Packit |
fc043f |
if (!pcookie)
|
|
Packit |
fc043f |
err = -1;
|
|
Packit |
fc043f |
else
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
pcookie->modeflags = modeflags;
|
|
Packit |
fc043f |
pcookie->next_functions = next_functions;
|
|
Packit |
fc043f |
pcookie->next_cookie = next_cookie;
|
|
Packit |
fc043f |
pcookie->reader = NULL;
|
|
Packit |
fc043f |
pcookie->writer = NULL;
|
|
Packit |
fc043f |
*cookie = pcookie;
|
|
Packit |
fc043f |
err = 0;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
trace_errno (err,("cookie=%p", *cookie));
|
|
Packit |
fc043f |
return err;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/*
|
|
Packit |
fc043f |
* Seek function for pollable objects.
|
|
Packit |
fc043f |
*/
|
|
Packit |
fc043f |
static int
|
|
Packit |
fc043f |
func_w32_pollable_seek (void *cookie, gpgrt_off_t *offset, int whence)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
estream_cookie_w32_pollable_t pcookie = cookie;
|
|
Packit |
fc043f |
(void) pcookie;
|
|
Packit |
fc043f |
(void) offset;
|
|
Packit |
fc043f |
(void) whence;
|
|
Packit |
fc043f |
/* XXX */
|
|
Packit |
fc043f |
_gpg_err_set_errno (EOPNOTSUPP);
|
|
Packit |
fc043f |
return -1;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/*
|
|
Packit |
fc043f |
* The IOCTL function for pollable objects.
|
|
Packit |
fc043f |
*/
|
|
Packit |
fc043f |
static int
|
|
Packit |
fc043f |
func_w32_pollable_ioctl (void *cookie, int cmd, void *ptr, size_t *len)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
estream_cookie_w32_pollable_t pcookie = cookie;
|
|
Packit |
fc043f |
cookie_ioctl_function_t func_ioctl = pcookie->next_functions.func_ioctl;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (cmd == COOKIE_IOCTL_NONBLOCK)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
if (ptr)
|
|
Packit |
fc043f |
pcookie->modeflags |= O_NONBLOCK;
|
|
Packit |
fc043f |
else
|
|
Packit |
fc043f |
pcookie->modeflags &= ~O_NONBLOCK;
|
|
Packit |
fc043f |
return 0;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (func_ioctl)
|
|
Packit |
fc043f |
return func_ioctl (pcookie->next_cookie, cmd, ptr, len);
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
_gpg_err_set_errno (EOPNOTSUPP);
|
|
Packit |
fc043f |
return -1;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/*
|
|
Packit |
fc043f |
* The destroy function for pollable objects.
|
|
Packit |
fc043f |
*/
|
|
Packit |
fc043f |
static int
|
|
Packit |
fc043f |
func_w32_pollable_destroy (void *cookie)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
estream_cookie_w32_pollable_t pcookie = cookie;
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
if (cookie)
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
if (pcookie->reader)
|
|
Packit |
fc043f |
destroy_reader (pcookie->reader);
|
|
Packit |
fc043f |
if (pcookie->writer)
|
|
Packit |
fc043f |
destroy_writer (pcookie->writer);
|
|
Packit |
fc043f |
pcookie->next_functions.public.func_close (pcookie->next_cookie);
|
|
Packit |
fc043f |
_gpgrt_free (pcookie);
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
return 0;
|
|
Packit |
fc043f |
}
|
|
Packit |
fc043f |
|
|
Packit |
fc043f |
/*
|
|
Packit |
fc043f |
* Access object for the pollable functions.
|
|
Packit |
fc043f |
*/
|
|
Packit |
fc043f |
struct cookie_io_functions_s _gpgrt_functions_w32_pollable =
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
{
|
|
Packit |
fc043f |
func_w32_pollable_read,
|
|
Packit |
fc043f |
func_w32_pollable_write,
|
|
Packit |
fc043f |
func_w32_pollable_seek,
|
|
Packit |
fc043f |
func_w32_pollable_destroy,
|
|
Packit |
fc043f |
},
|
|
Packit |
fc043f |
func_w32_pollable_ioctl,
|
|
Packit |
fc043f |
};
|