/* w32-estream.c - es_poll support on W32. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2007, 2010, 2016 g10 Code GmbH * * This file is part of libgpg-error. * * libgpg-error 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. * * libgpg-error 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, see . */ /* * This file is based on GPGME's w32-io.c started in 2001. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #include #include #ifndef EOPNOTSUPP # define EOPNOTSUPP ENOSYS #endif /* Enable tracing. The value is the module name to be printed. */ /*#define ENABLE_TRACING "estream" */ #include "gpgrt-int.h" /* * In order to support es_poll on Windows, we create a proxy shim that * we use as the estream I/O functions. This shim creates reader and * writer threads that use the original I/O functions. */ /* Calculate array dimension. */ #ifndef DIM #define DIM(array) (sizeof (array) / sizeof (*array)) #endif #define READBUF_SIZE 8192 #define WRITEBUF_SIZE 8192 typedef struct estream_cookie_w32_pollable *estream_cookie_w32_pollable_t; struct reader_context_s { estream_cookie_w32_pollable_t pcookie; HANDLE thread_hd; CRITICAL_SECTION mutex; int stop_me; int eof; int eof_shortcut; int error; int error_code; /* This is manually reset. */ HANDLE have_data_ev; /* This is automatically reset. */ HANDLE have_space_ev; /* This is manually reset but actually only triggered once. */ HANDLE close_ev; size_t readpos, writepos; char buffer[READBUF_SIZE]; }; struct writer_context_s { estream_cookie_w32_pollable_t pcookie; HANDLE thread_hd; CRITICAL_SECTION mutex; int stop_me; int error; int error_code; /* This is manually reset. */ HANDLE have_data; HANDLE is_empty; HANDLE close_ev; size_t nbytes; char buffer[WRITEBUF_SIZE]; }; /* Cookie for pollable objects. */ struct estream_cookie_w32_pollable { unsigned int modeflags; struct cookie_io_functions_s next_functions; void *next_cookie; struct reader_context_s *reader; struct writer_context_s *writer; }; static DWORD CALLBACK reader (void *arg) { struct reader_context_s *ctx = arg; int nbytes; ssize_t nread; trace (("%p: reader starting", ctx)); for (;;) { EnterCriticalSection (&ctx->mutex); /* Leave a 1 byte gap so that we can see whether it is empty or full. */ while ((ctx->writepos + 1) % READBUF_SIZE == ctx->readpos) { /* Wait for space. */ if (!ResetEvent (ctx->have_space_ev)) trace (("%p: ResetEvent failed: ec=%d", ctx, (int)GetLastError())); LeaveCriticalSection (&ctx->mutex); trace (("%p: waiting for space", ctx)); WaitForSingleObject (ctx->have_space_ev, INFINITE); trace (("%p: got space", ctx)); EnterCriticalSection (&ctx->mutex); } gpgrt_assert (((ctx->writepos + 1) % READBUF_SIZE != ctx->readpos)); if (ctx->stop_me) { LeaveCriticalSection (&ctx->mutex); break; } nbytes = (ctx->readpos + READBUF_SIZE - ctx->writepos - 1) % READBUF_SIZE; gpgrt_assert (nbytes); if (nbytes > READBUF_SIZE - ctx->writepos) nbytes = READBUF_SIZE - ctx->writepos; LeaveCriticalSection (&ctx->mutex); trace (("%p: reading up to %d bytes", ctx, nbytes)); nread = ctx->pcookie->next_functions.public.func_read (ctx->pcookie->next_cookie, ctx->buffer + ctx->writepos, nbytes); trace (("%p: got %d bytes", ctx, nread)); if (nread < 0) { ctx->error_code = (int) errno; if (ctx->error_code == ERROR_BROKEN_PIPE) { ctx->eof = 1; trace (("%p: got EOF (broken pipe)", ctx)); } else { ctx->error = 1; trace (("%p: read error: ec=%d", ctx, ctx->error_code)); } break; } EnterCriticalSection (&ctx->mutex); if (ctx->stop_me) { LeaveCriticalSection (&ctx->mutex); break; } if (!nread) { ctx->eof = 1; trace (("%p: got eof", ctx)); LeaveCriticalSection (&ctx->mutex); break; } ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE; if (!SetEvent (ctx->have_data_ev)) trace (("%p: SetEvent (%p) failed: ec=%d", ctx, ctx->have_data_ev, (int)GetLastError ())); LeaveCriticalSection (&ctx->mutex); } /* Indicate that we have an error or EOF. */ if (!SetEvent (ctx->have_data_ev)) trace (("%p: SetEvent (%p) failed: ec=%d", ctx, ctx->have_data_ev, (int)GetLastError ())); trace (("%p: waiting for close", ctx)); WaitForSingleObject (ctx->close_ev, INFINITE); CloseHandle (ctx->close_ev); CloseHandle (ctx->have_data_ev); CloseHandle (ctx->have_space_ev); CloseHandle (ctx->thread_hd); DeleteCriticalSection (&ctx->mutex); free (ctx); /* Standard free! See comment in create_reader. */ return 0; } static struct reader_context_s * create_reader (estream_cookie_w32_pollable_t pcookie) { struct reader_context_s *ctx; SECURITY_ATTRIBUTES sec_attr; DWORD tid; memset (&sec_attr, 0, sizeof sec_attr); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; /* The CTX must be allocated in standard system memory so that we * won't use any custom allocation handler which may use our lock * primitives for its implementation. The problem here is that the * syscall clamp mechanism (e.g. nPth) would be called recursively: * 1. For example by the caller of _gpgrt_w32_poll and 2. by * gpgrt_lock_lock on behalf of the the custom allocation and free * functions. */ ctx = calloc (1, sizeof *ctx); if (!ctx) { return NULL; } ctx->pcookie = pcookie; 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->close_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL); if (!ctx->have_data_ev || !ctx->have_space_ev || !ctx->close_ev) { trace (("%p: CreateEvent failed: ec=%d", ctx, (int)GetLastError ())); if (ctx->have_data_ev) CloseHandle (ctx->have_data_ev); if (ctx->have_space_ev) CloseHandle (ctx->have_space_ev); if (ctx->close_ev) CloseHandle (ctx->close_ev); _gpgrt_free (ctx); return NULL; } InitializeCriticalSection (&ctx->mutex); ctx->thread_hd = CreateThread (&sec_attr, 0, reader, ctx, 0, &tid); if (!ctx->thread_hd) { trace (("%p: CreateThread failed: ec=%d", ctx, (int)GetLastError ())); DeleteCriticalSection (&ctx->mutex); if (ctx->have_data_ev) CloseHandle (ctx->have_data_ev); if (ctx->have_space_ev) CloseHandle (ctx->have_space_ev); if (ctx->close_ev) CloseHandle (ctx->close_ev); _gpgrt_free (ctx); return NULL; } else { #if 0 /* We set the priority of the thread higher because we know that it only runs for a short time. This greatly helps to increase the performance of the I/O. */ SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ()); #endif } return ctx; } /* 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) { EnterCriticalSection (&ctx->mutex); ctx->stop_me = 1; if (ctx->have_space_ev) SetEvent (ctx->have_space_ev); LeaveCriticalSection (&ctx->mutex); /* XXX is it feasible to unblock the thread? */ /* After setting this event CTX is void. */ SetEvent (ctx->close_ev); } /* * Read function for pollable objects. */ static gpgrt_ssize_t func_w32_pollable_read (void *cookie, void *buffer, size_t count) { estream_cookie_w32_pollable_t pcookie = cookie; gpgrt_ssize_t nread; struct reader_context_s *ctx; trace (("%p: enter buffer=%p count=%u", cookie, buffer, count)); /* FIXME: implement pending check if COUNT==0 */ ctx = pcookie->reader; if (ctx == NULL) { pcookie->reader = ctx = create_reader (pcookie); if (!ctx) { _gpg_err_set_errno (EBADF); nread = -1; goto leave; } trace (("%p: new reader %p", cookie, pcookie->reader)); } if (ctx->eof_shortcut) { nread = 0; goto leave; } EnterCriticalSection (&ctx->mutex); trace (("%p: readpos: %d, writepos %d", cookie, ctx->readpos, ctx->writepos)); if (ctx->readpos == ctx->writepos && !ctx->error) { /* No data available. */ int eof = ctx->eof; LeaveCriticalSection (&ctx->mutex); if (pcookie->modeflags & O_NONBLOCK && ! eof) { _gpg_err_set_errno (EAGAIN); nread = -1; goto leave; } trace (("%p: waiting for data", cookie)); WaitForSingleObject (ctx->have_data_ev, INFINITE); trace (("%p: data available", cookie)); EnterCriticalSection (&ctx->mutex); } if (ctx->readpos == ctx->writepos || ctx->error) { LeaveCriticalSection (&ctx->mutex); ctx->eof_shortcut = 1; if (ctx->eof) return 0; if (!ctx->error) { trace (("%p: EOF but ctx->eof flag not set", cookie)); nread = 0; goto leave; } _gpg_err_set_errno (ctx->error_code); return -1; } nread = ctx->readpos < ctx->writepos ? ctx->writepos - ctx->readpos : READBUF_SIZE - ctx->readpos; if (nread > count) nread = count; memcpy (buffer, ctx->buffer + ctx->readpos, nread); ctx->readpos = (ctx->readpos + nread) % READBUF_SIZE; if (ctx->readpos == ctx->writepos && !ctx->eof) { if (!ResetEvent (ctx->have_data_ev)) { trace (("%p: ResetEvent failed: ec=%d", cookie, (int)GetLastError ())); LeaveCriticalSection (&ctx->mutex); /* FIXME: Should translate the error code. */ _gpg_err_set_errno (EIO); nread = -1; goto leave; } } if (!SetEvent (ctx->have_space_ev)) { trace (("%p: SetEvent (%p) failed: ec=%d", cookie, ctx->have_space_ev, (int)GetLastError ())); LeaveCriticalSection (&ctx->mutex); /* FIXME: Should translate the error code. */ _gpg_err_set_errno (EIO); nread = -1; goto leave; } LeaveCriticalSection (&ctx->mutex); leave: trace_errno (nread==-1,("%p: leave nread=%d", cookie, (int)nread)); return nread; } /* 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 writer (void *arg) { struct writer_context_s *ctx = arg; ssize_t nwritten; trace (("%p: writer starting", ctx)); for (;;) { EnterCriticalSection (&ctx->mutex); if (ctx->stop_me && !ctx->nbytes) { LeaveCriticalSection (&ctx->mutex); break; } if (!ctx->nbytes) { if (!SetEvent (ctx->is_empty)) trace (("%p: SetEvent failed: ec=%d", ctx, (int)GetLastError ())); if (!ResetEvent (ctx->have_data)) trace (("%p: ResetEvent failed: ec=%d", ctx, (int)GetLastError ())); LeaveCriticalSection (&ctx->mutex); trace (("%p: idle", ctx)); WaitForSingleObject (ctx->have_data, INFINITE); trace (("%p: got data to write", ctx)); EnterCriticalSection (&ctx->mutex); } if (ctx->stop_me && !ctx->nbytes) { LeaveCriticalSection (&ctx->mutex); break; } LeaveCriticalSection (&ctx->mutex); trace (("%p: writing up to %d bytes", ctx, ctx->nbytes)); nwritten = ctx->pcookie->next_functions.public.func_write (ctx->pcookie->next_cookie, ctx->buffer, ctx->nbytes); trace (("%p: wrote %d bytes", ctx, nwritten)); if (nwritten < 1) { /* XXX */ if (errno == ERROR_BUSY) { /* Probably stop_me is set now. */ trace (("%p: pipe busy (unblocked?)", ctx)); continue; } ctx->error_code = errno; ctx->error = 1; trace (("%p: write error: ec=%d", ctx, ctx->error_code)); break; } EnterCriticalSection (&ctx->mutex); ctx->nbytes -= nwritten; LeaveCriticalSection (&ctx->mutex); } /* Indicate that we have an error. */ if (!SetEvent (ctx->is_empty)) trace (("%p: SetEvent failed: ec=%d", ctx, (int)GetLastError ())); trace (("%p: waiting for close", ctx)); WaitForSingleObject (ctx->close_ev, INFINITE); if (ctx->nbytes) trace (("%p: still %d bytes in buffer at close time", ctx, ctx->nbytes)); CloseHandle (ctx->close_ev); CloseHandle (ctx->have_data); CloseHandle (ctx->is_empty); CloseHandle (ctx->thread_hd); DeleteCriticalSection (&ctx->mutex); trace (("%p: writer is destroyed", ctx)); free (ctx); /* Standard free! See comment in create_writer. */ return 0; } static struct writer_context_s * create_writer (estream_cookie_w32_pollable_t pcookie) { struct writer_context_s *ctx; SECURITY_ATTRIBUTES sec_attr; DWORD tid; memset (&sec_attr, 0, sizeof sec_attr); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; /* See comment at create_reader. */ ctx = calloc (1, sizeof *ctx); if (!ctx) { return NULL; } ctx->pcookie = pcookie; 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->close_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL); if (!ctx->have_data || !ctx->is_empty || !ctx->close_ev) { trace (("%p: CreateEvent failed: ec=%d", ctx, (int)GetLastError ())); if (ctx->have_data) CloseHandle (ctx->have_data); if (ctx->is_empty) CloseHandle (ctx->is_empty); if (ctx->close_ev) CloseHandle (ctx->close_ev); _gpgrt_free (ctx); return NULL; } InitializeCriticalSection (&ctx->mutex); ctx->thread_hd = CreateThread (&sec_attr, 0, writer, ctx, 0, &tid ); if (!ctx->thread_hd) { trace (("%p: CreateThread failed: ec=%d", ctx, (int)GetLastError ())); DeleteCriticalSection (&ctx->mutex); if (ctx->have_data) CloseHandle (ctx->have_data); if (ctx->is_empty) CloseHandle (ctx->is_empty); if (ctx->close_ev) CloseHandle (ctx->close_ev); _gpgrt_free (ctx); return NULL; } else { #if 0 /* We set the priority of the thread higher because we know that it only runs for a short time. This greatly helps to increase the performance of the I/O. */ SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ()); #endif } return ctx; } static void destroy_writer (struct writer_context_s *ctx) { trace (("%p: enter pollable_destroy_writer", ctx)); EnterCriticalSection (&ctx->mutex); trace (("%p: setting stopme", ctx)); ctx->stop_me = 1; if (ctx->have_data) SetEvent (ctx->have_data); LeaveCriticalSection (&ctx->mutex); trace (("%p: waiting for empty", ctx)); /* Give the writer a chance to flush the buffer. */ WaitForSingleObject (ctx->is_empty, INFINITE); /* After setting this event CTX is void. */ trace (("%p: set close_ev", ctx)); SetEvent (ctx->close_ev); trace (("%p: leave pollable_destroy_writer", ctx)); } /* * Write function for pollable objects. */ static gpgrt_ssize_t func_w32_pollable_write (void *cookie, const void *buffer, size_t count) { estream_cookie_w32_pollable_t pcookie = cookie; struct writer_context_s *ctx = pcookie->writer; int nwritten; trace (("%p: enter buffer: %p count: %d", cookie, buffer, count)); if (count == 0) { nwritten = 0; goto leave; } if (ctx == NULL) { pcookie->writer = ctx = create_writer (pcookie); if (!ctx) { nwritten = -1; goto leave; } trace (("%p: new writer %p", cookie, pcookie->writer)); } EnterCriticalSection (&ctx->mutex); trace (("%p: buffer: %p, count: %d, nbytes: %d", cookie, buffer, count, ctx->nbytes)); if (!ctx->error && ctx->nbytes) { /* Bytes are pending for send. */ /* Reset the is_empty event. Better safe than sorry. */ if (!ResetEvent (ctx->is_empty)) { trace (("%p: ResetEvent failed: ec=%d", cookie, (int)GetLastError ())); LeaveCriticalSection (&ctx->mutex); /* FIXME: Should translate the error code. */ _gpg_err_set_errno (EIO); nwritten = -1; goto leave; } LeaveCriticalSection (&ctx->mutex); if (pcookie->modeflags & O_NONBLOCK) { trace (("%p: would block", cookie)); _gpg_err_set_errno (EAGAIN); nwritten = -1; goto leave; } trace (("%p: waiting for empty buffer", cookie)); WaitForSingleObject (ctx->is_empty, INFINITE); trace (("%p: buffer is empty", cookie)); EnterCriticalSection (&ctx->mutex); } if (ctx->error) { LeaveCriticalSection (&ctx->mutex); if (ctx->error_code == ERROR_NO_DATA) _gpg_err_set_errno (EPIPE); else _gpg_err_set_errno (EIO); nwritten = -1; goto leave; } /* If no error occurred, the number of bytes in the buffer must be zero. */ gpgrt_assert (!ctx->nbytes); if (count > WRITEBUF_SIZE) count = WRITEBUF_SIZE; memcpy (ctx->buffer, buffer, count); ctx->nbytes = count; /* We have to reset the is_empty event early, because it is also used by the select() implementation to probe the channel. */ if (!ResetEvent (ctx->is_empty)) { trace (("%p: ResetEvent failed: ec=%d", cookie, (int)GetLastError ())); LeaveCriticalSection (&ctx->mutex); /* FIXME: Should translate the error code. */ _gpg_err_set_errno (EIO); nwritten = -1; goto leave; } if (!SetEvent (ctx->have_data)) { trace (("%p: SetEvent failed: ec=%d", cookie, (int)GetLastError ())); LeaveCriticalSection (&ctx->mutex); /* FIXME: Should translate the error code. */ _gpg_err_set_errno (EIO); nwritten = -1; goto leave; } LeaveCriticalSection (&ctx->mutex); nwritten = count; leave: trace_errno (nwritten==-1,("%p: leave nwritten=%d", cookie, nwritten)); return nwritten; } /* This is the core of _gpgrt_poll. The caller needs to make sure that * the syscall clamp has been engaged. */ int _gpgrt_w32_poll (gpgrt_poll_t *fds, size_t nfds, int timeout) { HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS]; int waitidx[MAXIMUM_WAIT_OBJECTS]; #ifdef ENABLE_TRACING char waitinfo[MAXIMUM_WAIT_OBJECTS]; #endif unsigned int code; int nwait; int i; int any; int count; #if 0 restart: #endif any = 0; nwait = 0; count = 0; for (i = 0; i < nfds; i++) { struct estream_cookie_w32_pollable *pcookie; if (fds[i].ignore) continue; if (fds[i].stream->intern->kind != BACKEND_W32_POLLABLE) { /* This stream does not support polling. */ fds[i].got_err = 1; continue; } pcookie = fds[i].stream->intern->cookie; if (fds[i].want_read || fds[i].want_write) { /* XXX: What if one wants read and write, is that supported? */ if (fds[i].want_read) { struct reader_context_s *ctx = pcookie->reader; if (ctx == NULL) { pcookie->reader = ctx = create_reader (pcookie); if (!ctx) { /* FIXME: Is the error code appropriate? */ _gpg_err_set_errno (EBADF); return -1; } trace (("%p: new reader %p", pcookie, pcookie->reader)); } trace (("%p: using reader %p", pcookie, pcookie->reader)); if (nwait >= DIM (waitbuf)) { trace (("oops: too many objects for WFMO")); /* FIXME: Should translate the error code. */ _gpg_err_set_errno (EIO); return -1; } waitidx[nwait] = i; #ifdef ENABLE_TRACING waitinfo[nwait] = 'r'; #endif /*ENABLE_TRACING*/ waitbuf[nwait++] = ctx->have_data_ev; any = 1; } else if (fds[i].want_write) { struct writer_context_s *ctx = pcookie->writer; if (ctx == NULL) { pcookie->writer = ctx = create_writer (pcookie); if (!ctx) { trace (("oops: create writer failed")); /* FIXME: Is the error code appropriate? */ _gpg_err_set_errno (EBADF); return -1; } trace (("%p: new writer %p", pcookie, pcookie->writer)); } trace (("%p: using writer %p", pcookie, pcookie->writer)); if (nwait >= DIM (waitbuf)) { trace (("oops: Too many objects for WFMO")); /* FIXME: Should translate the error code. */ _gpg_err_set_errno (EIO); return -1; } waitidx[nwait] = i; #ifdef ENABLE_TRACING waitinfo[nwait] = 'w'; #endif /*ENABLE_TRACING*/ waitbuf[nwait++] = ctx->is_empty; any = 1; } } } #ifdef ENABLE_TRACING trace_start (("poll on [ ")); for (i = 0; i < nwait; i++) trace_append (("%d/%c ", waitidx[i], waitinfo[i])); trace_finish (("]")); #endif /*ENABLE_TRACING*/ if (!any) { /* WFMO needs at least one object, thus we use use sleep here. * INFINITE wait does not make any sense in this case, so we * error out. */ if (timeout == -1) { _gpg_err_set_errno (EINVAL); return -1; } if (timeout) Sleep (timeout); code = WAIT_TIMEOUT; } else code = WaitForMultipleObjects (nwait, waitbuf, 0, timeout == -1 ? INFINITE : timeout); if (code < WAIT_OBJECT_0 + nwait) { /* This WFMO is a really silly function: It does return either the index of the signaled object or if 2 objects have been signalled at the same time, the index of the object with the lowest object is returned - so and how do we find out how many objects have been signaled???. The only solution I can imagine is to test each object starting with the returned index individually - how dull. */ any = 0; for (i = code - WAIT_OBJECT_0; i < nwait; i++) { if (WaitForSingleObject (waitbuf[i], 0) == WAIT_OBJECT_0) { gpgrt_assert (waitidx[i] >=0 && waitidx[i] < nfds); /* XXX: What if one wants read and write, is that supported? */ if (fds[waitidx[i]].want_read) fds[waitidx[i]].got_read = 1; else if (fds[waitidx[i]].want_write) fds[waitidx[i]].got_write = 1; any = 1; count++; } } if (!any) { trace (("no signaled objects found after WFMO")); count = -1; } } else if (code == WAIT_TIMEOUT) trace (("WFMO timed out")); else if (code == WAIT_FAILED) { trace (("WFMO failed: ec=%d", (int)GetLastError ())); #if 0 if (GetLastError () == ERROR_INVALID_HANDLE) { int k; int j = handle_to_fd (waitbuf[i]); trace (("WFMO invalid handle %d removed", j)); for (k = 0 ; k < nfds; k++) { if (fds[k].fd == j) { fds[k].want_read = fds[k].want_write = 0; goto restart; } } trace ((" oops, or not???")); } #endif count = -1; } else { trace (("WFMO returned %u", code)); count = -1; } if (count > 0) { trace_start (("poll OK [ ")); for (i = 0; i < nfds; i++) { if (fds[i].ignore) continue; if (fds[i].got_read || fds[i].got_write) trace_append (("%c%d ", fds[i].want_read ? 'r' : 'w', i)); } trace_finish (("]")); } if (count < 0) { /* FIXME: Should determine a proper error code. */ _gpg_err_set_errno (EIO); } return count; } /* * Implementation of pollable I/O on Windows. */ /* * Constructor for pollable objects. */ int _gpgrt_w32_pollable_create (void *_GPGRT__RESTRICT *_GPGRT__RESTRICT cookie, unsigned int modeflags, struct cookie_io_functions_s next_functions, void *next_cookie) { estream_cookie_w32_pollable_t pcookie; int err; pcookie = _gpgrt_malloc (sizeof *pcookie); if (!pcookie) err = -1; else { pcookie->modeflags = modeflags; pcookie->next_functions = next_functions; pcookie->next_cookie = next_cookie; pcookie->reader = NULL; pcookie->writer = NULL; *cookie = pcookie; err = 0; } trace_errno (err,("cookie=%p", *cookie)); return err; } /* * Seek function for pollable objects. */ static int func_w32_pollable_seek (void *cookie, gpgrt_off_t *offset, int whence) { estream_cookie_w32_pollable_t pcookie = cookie; (void) pcookie; (void) offset; (void) whence; /* XXX */ _gpg_err_set_errno (EOPNOTSUPP); return -1; } /* * The IOCTL function for pollable objects. */ static int func_w32_pollable_ioctl (void *cookie, int cmd, void *ptr, size_t *len) { estream_cookie_w32_pollable_t pcookie = cookie; cookie_ioctl_function_t func_ioctl = pcookie->next_functions.func_ioctl; if (cmd == COOKIE_IOCTL_NONBLOCK) { if (ptr) pcookie->modeflags |= O_NONBLOCK; else pcookie->modeflags &= ~O_NONBLOCK; return 0; } if (func_ioctl) return func_ioctl (pcookie->next_cookie, cmd, ptr, len); _gpg_err_set_errno (EOPNOTSUPP); return -1; } /* * The destroy function for pollable objects. */ static int func_w32_pollable_destroy (void *cookie) { estream_cookie_w32_pollable_t pcookie = cookie; if (cookie) { if (pcookie->reader) destroy_reader (pcookie->reader); if (pcookie->writer) destroy_writer (pcookie->writer); pcookie->next_functions.public.func_close (pcookie->next_cookie); _gpgrt_free (pcookie); } return 0; } /* * Access object for the pollable functions. */ struct cookie_io_functions_s _gpgrt_functions_w32_pollable = { { func_w32_pollable_read, func_w32_pollable_write, func_w32_pollable_seek, func_w32_pollable_destroy, }, func_w32_pollable_ioctl, };