Пример #1
0
/* Set the current position from where the next read or write starts
   in the data object with the handle DH to OFFSET, relativ to
   WHENCE.  */
gpgme_off_t
gpgme_data_seek (gpgme_data_t dh, gpgme_off_t offset, int whence)
{
  TRACE_BEG2 (DEBUG_DATA, "gpgme_data_seek", dh,
	      "offset=%lli, whence=%i", offset, whence);

  if (!dh)
    {
      gpg_err_set_errno (EINVAL);
      return TRACE_SYSRES (-1);
    }
  if (!dh->cbs->seek)
    {
      gpg_err_set_errno (ENOSYS);
      return TRACE_SYSRES (-1);
    }

  /* For relative movement, we must take into account the actual
     position of the read counter.  */
  if (whence == SEEK_CUR)
    offset -= dh->pending_len;

  offset = (*dh->cbs->seek) (dh, offset, whence);
  if (offset >= 0)
    dh->pending_len = 0;

  return TRACE_SYSRES (offset);
}
Пример #2
0
int
_gpgme_io_set_nonblocking (int fd)
{
  GIOChannel *chan;
  GIOStatus status;
 
  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);

  chan = find_channel (fd);
  if (!chan)
    {
      errno = EIO;
      return TRACE_SYSRES (-1);
    }

  status = g_io_channel_set_flags (chan,
				   g_io_channel_get_flags (chan) |
				   G_IO_FLAG_NONBLOCK, NULL);

  if (status != G_IO_STATUS_NORMAL)
    {
#if 0
      /* glib 1.9.2 does not implement set_flags and returns an
	 error.  */
      errno = EIO;
      return TRACE_SYSRES (-1);
#else
      TRACE_LOG1 ("g_io_channel_set_flags failed: status=%d (ignored)",
		  status);
#endif
    }

  return TRACE_SYSRES (0);
}
Пример #3
0
int
_gpgme_io_dup (int fd)
{
  int newfd;
  GIOChannel *chan;

  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);

  if (fd < 0 || fd >= MAX_SLAFD || !giochannel_table[fd].used)
    {
      errno = EINVAL;
      return TRACE_SYSRES (-1);
    }

  for (newfd = 0; newfd < MAX_SLAFD; newfd++)
    if (! giochannel_table[newfd].used)
      break;
  if (newfd == MAX_SLAFD)
    {
      errno = EIO;
      return TRACE_SYSRES (-1);
    }
  
  chan = giochannel_table[fd].chan;
  g_io_channel_ref (chan);
  giochannel_table[newfd].used = 1;
  giochannel_table[newfd].chan = chan;
  giochannel_table[newfd].fd = -1;
  giochannel_table[newfd].socket = INVALID_SOCKET;
  giochannel_table[newfd].primary = 0;

  return TRACE_SYSRES (newfd);
}
Пример #4
0
int
_gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
			    void *value)
{
  int i;
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
	      "close_handler=%p/%p", handler, value);

  assert (fd != -1);

  LOCK (notify_table_lock);
  for (i=0; i < DIM (notify_table); i++)
    if (notify_table[i].inuse && notify_table[i].fd == fd)
      break;
  if (i == DIM (notify_table))
    for (i = 0; i < DIM (notify_table); i++)
      if (!notify_table[i].inuse)
	break;
  if (i == DIM (notify_table))
    {
      UNLOCK (notify_table_lock);
      errno = EINVAL;
      return TRACE_SYSRES (-1);
    }
  notify_table[i].fd = fd;
  notify_table[i].handler = handler;
  notify_table[i].value = value;
  notify_table[i].inuse = 1;
  UNLOCK (notify_table_lock);
  return TRACE_SYSRES (0);
}
Пример #5
0
int
_gpgme_io_socket (int domain, int type, int proto)
{
  int res;
  int fd;

  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
	      "type=%i, protp=%i", type, proto);

  res = socket (domain, type, proto);
  if (res == INVALID_SOCKET)
    {
      errno = wsa2errno (WSAGetLastError ());
      return TRACE_SYSRES (-1);
    }

  fd = new_channel_from_socket (res);
  if (fd < 0)
    {
      int saved_errno = errno;
      closesocket (res);
      errno = saved_errno;
      return TRACE_SYSRES (-1);
    }

  TRACE_SUC2 ("fd=%i, socket=0x%x", fd, res);
  
  return fd;
}
Пример #6
0
static ssize_t
__assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer,
		size_t size)
{
  /* Due to the peculiarities of the W32 API we can't use write for a
     network socket and thus we try to use send first and fallback to
     write if send detects that it is not a network socket.  */
  int res;

  TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "__assuan_write", ctx,
	      "fd=0x%x, buffer=%p, size=%i", fd, buffer, size);

#ifdef HAVE_W32CE_SYSTEM
  /* This is a bit of a hack to support stdout over ssh.  Note that
     fread buffers fully while getchar is line buffered.  Weird, but
     that's the way it is.  ASSUAN_STDIN and ASSUAN_STDOUT are
     special handle values that shouldn't occur in the wild.  */
  if (fd == ASSUAN_STDOUT)
    {
      res = fwrite (buffer, 1, size, stdout);
      return TRACE_SYSRES (res);
    }
#endif

  res = send ((int)fd, buffer, size, 0);
  if (res == -1 && WSAGetLastError () == WSAENOTSOCK)
    {
      DWORD nwrite;

      TRACE_LOG ("send call failed - trying WriteFile");
      res = WriteFile (fd, buffer, size, &nwrite, NULL);
      if (! res)
        {
          TRACE_LOG1 ("WriteFile failed: rc=%d", (int)GetLastError ());
          switch (GetLastError ())
            {
            case ERROR_BROKEN_PIPE: 
            case ERROR_NO_DATA:
	      gpg_err_set_errno (EPIPE);
	      break;

            case ERROR_PIPE_NOT_CONNECTED:
            case ERROR_BUSY:
              gpg_err_set_errno (EAGAIN);
              break;
	      
            default:
	      gpg_err_set_errno (EIO);
	      break;
            }
          res = -1;
        }
      else
        res = (int) nwrite;
    }
  else if (res == -1)
    TRACE_LOG1 ("send call failed: rc=%d", (int)GetLastError ());
  return TRACE_SYSRES (res);
}
Пример #7
0
static ssize_t
old_user_read (gpgme_data_t dh, void *buffer, size_t size)
{
  gpgme_error_t err;
  size_t amt;
  TRACE_BEG2 (DEBUG_DATA, "gpgme:old_user_read", dh,
	      "buffer=%p, size=%u", buffer, size);

  err = (*dh->data.old_user.cb) (dh->data.old_user.handle,
				 buffer, size, &amt);
  if (err)
    return TRACE_SYSRES (gpgme_error_to_errno (err));
  return TRACE_SYSRES (amt);
}
Пример #8
0
int
_gpgme_io_read (int fd, void *buffer, size_t count)
{
  int saved_errno = 0;
  gsize nread;
  GIOChannel *chan;
  GIOStatus status;
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
	      "buffer=%p, count=%u", buffer, count);

  chan = find_channel (fd);
  if (!chan)
    {
      TRACE_LOG ("no channel registered");
      errno = EINVAL;
      return TRACE_SYSRES (-1);
    }
  TRACE_LOG1 ("channel %p", chan);

  {
    GError *err = NULL;
    status = g_io_channel_read_chars (chan, (gchar *) buffer,
				      count, &nread, &err);
    if (err)
      {
	TRACE_LOG2 ("status %i, err %s", status, err->message);
	g_error_free (err);
      }
  }

  if (status == G_IO_STATUS_EOF)
    nread = 0;
  else if (status == G_IO_STATUS_AGAIN)
    {
      nread = -1;
      saved_errno = EAGAIN;
    }
  else if (status != G_IO_STATUS_NORMAL)
    {
      TRACE_LOG1 ("status %d", status);
      nread = -1;
      saved_errno = EIO;
    }
  
  if (nread != 0 && nread != -1)
    TRACE_LOGBUF (buffer, nread);

  errno = saved_errno;
  return TRACE_SYSRES (nread);
}
Пример #9
0
static off_t
old_user_seek (gpgme_data_t dh, off_t offset, int whence)
{
  gpgme_error_t err;
  TRACE_BEG2 (DEBUG_DATA, "gpgme:old_user_seek", dh,
	      "offset=%llu, whence=%i", offset, whence);

  if (whence != SEEK_SET || offset)
    {
      errno = EINVAL;
      return TRACE_SYSRES (-1);
    }
  err = (*dh->data.old_user.cb) (dh->data.old_user.handle, NULL, 0, NULL);
  if (err)
    return TRACE_SYSRES (gpgme_error_to_errno (err));
  return TRACE_SYSRES (0);
}
Пример #10
0
int
_gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
			    void *value)
{
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
	      "close_handler=%p/%p", handler, value);

  assert (fd != -1);

  if (fd < 0 || fd >= (int) DIM (notify_table))
    {
      errno = EINVAL;
      return TRACE_SYSRES (-1);
    }
  notify_table[fd].handler = handler;
  notify_table[fd].value = value;
  return TRACE_SYSRES (0);
}
Пример #11
0
int
_gpgme_io_close (int fd)
{
  int i;
  _gpgme_close_notify_handler_t handler = NULL;
  void *value = NULL;
  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);

  if (fd == -1)
    {
      errno = EBADF;
      return TRACE_SYSRES (-1);
    }

  kill_reader (fd);
  kill_writer (fd);
  LOCK (notify_table_lock);
  for (i = 0; i < DIM (notify_table); i++)
    {
      if (notify_table[i].inuse && notify_table[i].fd == fd)
	{
	  handler = notify_table[i].handler;
	  value   = notify_table[i].value;
	  notify_table[i].handler = NULL;
	  notify_table[i].value = NULL;
	  notify_table[i].inuse = 0;
	  break;
	}
    }
  UNLOCK (notify_table_lock);
  if (handler)
    handler (fd, value);

  if (!CloseHandle (fd_to_handle (fd)))
    { 
      TRACE_LOG1 ("CloseHandle failed: ec=%d", (int) GetLastError ());
      /* FIXME: Should translate the error code.  */
      errno = EIO;
      return TRACE_SYSRES (-1);
    }

  return TRACE_SYSRES (0);
}
Пример #12
0
/* This function provides access to the internal write function.  It
   is to be used by user callbacks to return data to gpgme.  See
   gpgme_passphrase_cb_t and gpgme_edit_cb_t.  */
gpgme_ssize_t
gpgme_io_write (int fd, const void *buffer, size_t count)
{
  int ret;
  TRACE_BEG2 (DEBUG_GLOBAL, "gpgme_io_write", fd,
	      "buffer=%p, count=%u", buffer, count);

  ret = _gpgme_io_write (fd, buffer, count);

  return TRACE_SYSRES (ret);
}
Пример #13
0
/* Read up to SIZE bytes into buffer BUFFER from the data object with
   the handle DH.  Return the number of characters read, 0 on EOF and
   -1 on error.  If an error occurs, errno is set.  */
gpgme_ssize_t
gpgme_data_read (gpgme_data_t dh, void *buffer, size_t size)
{
  gpgme_ssize_t res;
  TRACE_BEG2 (DEBUG_DATA, "gpgme_data_read", dh,
	      "buffer=%p, size=%u", buffer, size);

  if (!dh)
    {
      gpg_err_set_errno (EINVAL);
      return TRACE_SYSRES (-1);
    }
  if (!dh->cbs->read)
    {
      gpg_err_set_errno (ENOSYS);
      return TRACE_SYSRES (-1);
    }
  do
    res = (*dh->cbs->read) (dh, buffer, size);
  while (res < 0 && errno == EINTR);

  return TRACE_SYSRES (res);
}
Пример #14
0
int
_gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
{
  int res;

  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
	      "addr=%p, addrlen=%i", addr, addrlen);

  res = connect (fd, addr, addrlen);
  if (!res)
    {
      errno = wsa2errno (WSAGetLastError ());
      return TRACE_SYSRES (-1);
    }

  return TRACE_SUC ();
}
Пример #15
0
int
_gpgme_io_close (int fd)
{
  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);

  if (fd < 0 || fd >= MAX_SLAFD)
    {
      errno = EBADF;
      return TRACE_SYSRES (-1);
    }

  assert (giochannel_table[fd].used);

  /* First call the notify handler.  */
  if (notify_table[fd].handler)
    {
      notify_table[fd].handler (fd, notify_table[fd].value);
      notify_table[fd].handler = NULL;
      notify_table[fd].value = NULL;
    }

  /* Then do the close.  */
  if (giochannel_table[fd].chan)
    {
      if (giochannel_table[fd].primary)
	g_io_channel_shutdown (giochannel_table[fd].chan, 1, NULL);
      
      g_io_channel_unref (giochannel_table[fd].chan);
    }
  else
    {
      /* Dummy entry, just close.  */
      assert (giochannel_table[fd].fd != -1);
      _close (giochannel_table[fd].fd);
    }
	
  giochannel_table[fd].used = 0;
  giochannel_table[fd].fd = -1;
  giochannel_table[fd].socket = INVALID_SOCKET;
  giochannel_table[fd].chan = NULL;
  giochannel_table[fd].primary = 0;

  TRACE_SUC ();
  return 0;
}
Пример #16
0
/* This function provides access to the internal write function.  It
   is to be used by user callbacks to return data to gpgme.  See
   gpgme_passphrase_cb_t and gpgme_edit_cb_t.  Note that this is a
   variant of gpgme_io_write which guarantees that all COUNT bytes are
   written or an error is return.  Returns: 0 on success or -1 on
   error and the sets errno. */
int
gpgme_io_writen (int fd, const void *buffer_arg, size_t count)
{
  const char *buffer = buffer_arg;
  int ret = 0;
  TRACE_BEG2 (DEBUG_GLOBAL, "gpgme_io_writen", fd,
	      "buffer=%p, count=%u", buffer, count);
  while (count)
    {
      ret = _gpgme_io_write (fd, buffer, count);
      if (ret < 0)
        break;
      buffer += ret;
      count -= ret;
      ret = 0;
    }
  return TRACE_SYSRES (ret);
}
Пример #17
0
int
_gpgme_io_write (int fd, const void *buffer, size_t count)
{
  int saved_errno = 0;
  gsize nwritten;
  GIOChannel *chan;
  GIOStatus status;
  GError *err = NULL;

  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
	      "buffer=%p, count=%u", buffer, count);
  TRACE_LOGBUF (buffer, count);

  chan = find_channel (fd);
  if (!chan)
    {
      TRACE_LOG ("fd %d: no channel registered");
      errno = EINVAL;
      return -1;
    }

  status = g_io_channel_write_chars (chan, (gchar *) buffer, count,
				     &nwritten, &err);
  if (err)
    {
      TRACE_LOG1 ("write error: %s", err->message);
      g_error_free (err);
    }

  if (status == G_IO_STATUS_AGAIN)
    {
      nwritten = -1;
      saved_errno = EAGAIN;
    }
  else if (status != G_IO_STATUS_NORMAL)
    {
      nwritten = -1;
      saved_errno = EIO;
    }
  errno = saved_errno;

  return TRACE_SYSRES (nwritten);
}
Пример #18
0
int
_gpgme_io_socket (int domain, int type, int proto)
{
  int res;

  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
	      "type=%i, protp=%i", type, proto);

  res = socket (domain, type, proto);
  if (res == INVALID_SOCKET)
    {
      errno = wsa2errno (WSAGetLastError ());
      return TRACE_SYSRES (-1);
    }

  TRACE_SUC1 ("socket=0x%x", res);
  
  return res;
}
Пример #19
0
int
_gpgme_io_pipe (int filedes[2], int inherit_idx)
{
  int fds[2];

  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
	      "inherit_idx=%i (GPGME uses it for %s)",
	      inherit_idx, inherit_idx ? "reading" : "writing");

#define PIPEBUF_SIZE  4096
  if (_pipe (fds, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1)
    return TRACE_SYSRES (-1);

  /* Make one end inheritable. */
  if (inherit_idx == 0)
    {
      int new_read;

      new_read = _dup (fds[0]);
      _close (fds[0]);
      fds[0] = new_read;

      if (new_read < 0)
	{
	  _close (fds[1]);
	  return TRACE_SYSRES (-1);
	}
    }
  else if (inherit_idx == 1)
    {
      int new_write;

      new_write = _dup (fds[1]);
      _close (fds[1]);
      fds[1] = new_write;

      if (new_write < 0)
	{
	  _close (fds[0]);
	  return TRACE_SYSRES (-1);
	}
    }

  /* For _gpgme_io_close.  */
  filedes[inherit_idx] = new_dummy_channel_from_fd (fds[inherit_idx]);
  if (filedes[inherit_idx] < 0)
    {
      int saved_errno = errno;
      
      _close (fds[0]);
      _close (fds[1]);
      errno = saved_errno;
      return TRACE_SYSRES (-1);
    }

  /* Now we have a pipe with the correct end inheritable.  The other end
     should have a giochannel.  */
  filedes[1 - inherit_idx] = new_channel_from_fd (fds[1 - inherit_idx]);
  if (filedes[1 - inherit_idx] < 0)
    {
      int saved_errno = errno;
      
      _gpgme_io_close (fds[inherit_idx]);
      _close (fds[1 - inherit_idx]);
      errno = saved_errno;
      return TRACE_SYSRES (-1);
    }
  
  return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p",
		     filedes[0],
		     (HANDLE) _get_osfhandle (giochannel_table[filedes[0]].fd),
		     filedes[1],
		     (HANDLE) _get_osfhandle (giochannel_table[filedes[1]].fd),
		     giochannel_table[1 - inherit_idx].chan);
}
Пример #20
0
int
_gpgme_io_pipe (int filedes[2], int inherit_idx)
{
  HANDLE rh;
  HANDLE wh;
  SECURITY_ATTRIBUTES sec_attr;
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
	      "inherit_idx=%i (GPGME uses it for %s)",
	      inherit_idx, inherit_idx ? "reading" : "writing");

  memset (&sec_attr, 0, sizeof (sec_attr));
  sec_attr.nLength = sizeof (sec_attr);
  sec_attr.bInheritHandle = FALSE;
  
  if (!CreatePipe (&rh, &wh, &sec_attr, PIPEBUF_SIZE))
    {
      TRACE_LOG1 ("CreatePipe failed: ec=%d", (int) GetLastError ());
      /* FIXME: Should translate the error code.  */
      errno = EIO;
      return TRACE_SYSRES (-1);
    }

  /* Make one end inheritable.  */
  if (inherit_idx == 0)
    {
      struct writer_context_s *ctx;
      HANDLE hd;
      if (!DuplicateHandle (GetCurrentProcess(), rh,
			    GetCurrentProcess(), &hd, 0,
			    TRUE, DUPLICATE_SAME_ACCESS))
	{
	  TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
		      (int) GetLastError ());
	  CloseHandle (rh);
	  CloseHandle (wh);
	  /* FIXME: Should translate the error code.  */
	  errno = EIO;
	  return TRACE_SYSRES (-1);
        }
      CloseHandle (rh);
      rh = hd;

      ctx = find_writer (handle_to_fd (wh), 0);
      assert (ctx == NULL);
      ctx = find_writer (handle_to_fd (wh), 1);
      if (!ctx)
	{
	  CloseHandle (rh);
	  CloseHandle (wh);
	  /* FIXME: Should translate the error code.  */
	  errno = EIO;
	  return TRACE_SYSRES (-1);
	}
    }
  else if (inherit_idx == 1)
    {
      struct reader_context_s *ctx;
      HANDLE hd;
      if (!DuplicateHandle( GetCurrentProcess(), wh,
			    GetCurrentProcess(), &hd, 0,
			    TRUE, DUPLICATE_SAME_ACCESS))
	{
	  TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
		      (int) GetLastError ());
	  CloseHandle (rh);
	  CloseHandle (wh);
	  /* FIXME: Should translate the error code.  */
	  errno = EIO;
	  return TRACE_SYSRES (-1);
        }
      CloseHandle (wh);
      wh = hd;

      ctx = find_reader (handle_to_fd (rh), 0);
      assert (ctx == NULL);
      ctx = find_reader (handle_to_fd (rh), 1);
      if (!ctx)
	{
	  CloseHandle (rh);
	  CloseHandle (wh);
	  /* FIXME: Should translate the error code.  */
	  errno = EIO;
	  return TRACE_SYSRES (-1);
	}
    }
  
  filedes[0] = handle_to_fd (rh);
  filedes[1] = handle_to_fd (wh);
  return TRACE_SUC2 ("read=%p, write=%p", rh, wh);
}
Пример #21
0
int
_gpgme_io_dup (int fd)
{
  HANDLE handle = fd_to_handle (fd);
  HANDLE new_handle = fd_to_handle (fd);
  int i;
  struct reader_context_s *rd_ctx;
  struct writer_context_s *wt_ctx;

  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);

  if (!DuplicateHandle (GetCurrentProcess(), handle,
			GetCurrentProcess(), &new_handle,
			0, FALSE, DUPLICATE_SAME_ACCESS))
    {
      TRACE_LOG1 ("DuplicateHandle failed: ec=%d\n", (int) GetLastError ());
      /* FIXME: Translate error code.  */
      errno = EIO;
      return TRACE_SYSRES (-1);
    }

  rd_ctx = find_reader (fd, 0);
  if (rd_ctx)
    {
      /* No need for locking, as the only races are against the reader
	 thread itself, which doesn't touch refcount.  */
      rd_ctx->refcount++;

      LOCK (reader_table_lock);
      for (i = 0; i < reader_table_size; i++)
	if (!reader_table[i].used)
	  break;
      /* FIXME.  */
      assert (i != reader_table_size);
      reader_table[i].fd = handle_to_fd (new_handle);
      reader_table[i].context = rd_ctx;
      reader_table[i].used = 1;
      UNLOCK (reader_table_lock);
    }

  wt_ctx = find_writer (fd, 0);
  if (wt_ctx)
    {
      /* No need for locking, as the only races are against the writer
	 thread itself, which doesn't touch refcount.  */
      wt_ctx->refcount++;

      LOCK (writer_table_lock);
      for (i = 0; i < writer_table_size; i++)
	if (!writer_table[i].used)
	  break;
      /* FIXME.  */
      assert (i != writer_table_size);
      writer_table[i].fd = handle_to_fd (new_handle);
      writer_table[i].context = wt_ctx;
      writer_table[i].used = 1;
      UNLOCK (writer_table_lock);
    }

  return TRACE_SYSRES (handle_to_fd (new_handle));
}
Пример #22
0
int
_gpgme_io_read (int fd, void *buffer, size_t count)
{
  int nread;
  struct reader_context_s *ctx;
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
	      "buffer=%p, count=%u", buffer, count);
  
  ctx = find_reader (fd, 1);
  if (!ctx)
    {
      errno = EBADF;
      return TRACE_SYSRES (-1);
    }
  if (ctx->eof_shortcut)
    return TRACE_SYSRES (0);

  LOCK (ctx->mutex);
  if (ctx->readpos == ctx->writepos && !ctx->error)
    {
      /* No data available.  */
      UNLOCK (ctx->mutex);
      TRACE_LOG1 ("waiting for data from thread %p", ctx->thread_hd);
      WaitForSingleObject (ctx->have_data_ev, INFINITE);
      TRACE_LOG1 ("data from thread %p available", ctx->thread_hd);
      LOCK (ctx->mutex);
    }
  
  if (ctx->readpos == ctx->writepos || ctx->error)
    {
      UNLOCK (ctx->mutex);
      ctx->eof_shortcut = 1;
      if (ctx->eof)
	return TRACE_SYSRES (0);
      if (!ctx->error)
	{
	  TRACE_LOG ("EOF but ctx->eof flag not set");
	  return 0;
	}
      errno = ctx->error_code;
      return TRACE_SYSRES (-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_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
	  UNLOCK (ctx->mutex);
	  /* FIXME: Should translate the error code.  */
	  errno = EIO;
	  return TRACE_SYSRES (-1);
	}
    }
  if (!SetEvent (ctx->have_space_ev))
    {
      TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d",
		  ctx->have_space_ev, (int) GetLastError ());
      UNLOCK (ctx->mutex);
      /* FIXME: Should translate the error code.  */
      errno = EIO;
      return TRACE_SYSRES (-1);
    }
  UNLOCK (ctx->mutex);
  
  TRACE_LOGBUF (buffer, nread);
  return TRACE_SYSRES (nread);
}
Пример #23
0
int
_gpgme_io_spawn (const char *path, char * const argv[], unsigned int flags,
		 struct spawn_fd_item_s *fd_list,
		 void (*atfork) (void *opaque, int reserved),
		 void *atforkvalue, pid_t *r_pid)
{
  SECURITY_ATTRIBUTES sec_attr;
  PROCESS_INFORMATION pi =
    {
      NULL,      /* returns process handle */
      0,         /* returns primary thread handle */
      0,         /* returns pid */
      0          /* returns tid */
    };
  STARTUPINFO si;
  int cr_flags = (CREATE_DEFAULT_ERROR_MODE
                  | GetPriorityClass (GetCurrentProcess ()));
  int i;
  char **args;
  char *arg_string;
  /* FIXME.  */
  int debug_me = 0;
  int tmp_fd;
  char *tmp_name;

  TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
	      "path=%s", path);
  i = 0;
  while (argv[i])
    {
      TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
      i++;
    }
  
  /* We do not inherit any handles by default, and just insert those
     handles we want the child to have afterwards.  But some handle
     values occur on the command line, and we need to move
     stdin/out/err to the right location.  So we use a wrapper program
     which gets the information from a temporary file.  */
  if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
    {
      TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
      return TRACE_SYSRES (-1);
    }
  TRACE_LOG1 ("tmp_name = %s", tmp_name);

  args = calloc (2 + i + 1, sizeof (*args));
  args[0] = (char *) _gpgme_get_w32spawn_path ();
  args[1] = tmp_name;
  args[2] = path;
  memcpy (&args[3], &argv[1], i * sizeof (*args));

  memset (&sec_attr, 0, sizeof sec_attr);
  sec_attr.nLength = sizeof sec_attr;
  sec_attr.bInheritHandle = FALSE;
  
  arg_string = build_commandline (args);
  free (args);
  if (!arg_string)
    {
      close (tmp_fd);
      DeleteFile (tmp_name);
      return TRACE_SYSRES (-1);
    }

  memset (&si, 0, sizeof si);
  si.cb = sizeof (si);
  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
  si.hStdInput = INVALID_HANDLE_VALUE;
  si.hStdOutput = INVALID_HANDLE_VALUE;
  si.hStdError = INVALID_HANDLE_VALUE;

  cr_flags |= CREATE_SUSPENDED;
  cr_flags |= DETACHED_PROCESS;
  if (!CreateProcessA (_gpgme_get_w32spawn_path (),
		       arg_string,
		       &sec_attr,     /* process security attributes */
		       &sec_attr,     /* thread security attributes */
		       FALSE,         /* inherit handles */
		       cr_flags,      /* creation flags */
		       NULL,          /* environment */
		       NULL,          /* use current drive/directory */
		       &si,           /* startup information */
		       &pi))          /* returns process information */
    {
      TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
      free (arg_string);
      close (tmp_fd);
      DeleteFile (tmp_name);

      /* FIXME: Should translate the error code.  */
      errno = EIO;
      return TRACE_SYSRES (-1);
    }

  free (arg_string);
  
  if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
    _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);

  /* Insert the inherited handles.  */
  for (i = 0; fd_list[i].fd != -1; i++)
    {
      HANDLE hd;

      /* Make it inheritable for the wrapper process.  */
      if (!DuplicateHandle (GetCurrentProcess(),
			    _get_osfhandle (giochannel_table[fd_list[i].fd].fd),
			    pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
	{
	  TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
	  TerminateProcess (pi.hProcess, 0);
	  /* Just in case TerminateProcess didn't work, let the
	     process fail on its own.  */
	  ResumeThread (pi.hThread);
	  CloseHandle (pi.hThread);
	  CloseHandle (pi.hProcess);

	  close (tmp_fd);
	  DeleteFile (tmp_name);

	  /* FIXME: Should translate the error code.  */
	  errno = EIO;
	  return TRACE_SYSRES (-1);
        }
      /* Return the child name of this handle.  */
      fd_list[i].peer_name = (int) hd;
    }

  /* Write the handle translation information to the temporary
     file.  */
  {
    /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
       notation: "0xFEDCBA9876543210" with an extra white space after
       every quadruplet.  10*(19*4 + 1) - 1 = 769.  This plans ahead
       for a time when a HANDLE is 64 bit.  */
#define BUFFER_MAX 800
    char line[BUFFER_MAX + 1];
    int res;
    int written;
    size_t len;

    if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
      strcpy (line, "~1 \n");
    else
      strcpy (line, "\n");
    for (i = 0; fd_list[i].fd != -1; i++)
      {
	/* Strip the newline.  */
	len = strlen (line) - 1;
	
	/* Format is: Local name, stdin/stdout/stderr, peer name, argv idx.  */
	snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d  \n",
		  fd_list[i].fd, fd_list[i].dup_to,
		  fd_list[i].peer_name, fd_list[i].arg_loc);
	/* Rather safe than sorry.  */
	line[BUFFER_MAX - 1] = '\n';
	line[BUFFER_MAX] = '\0';
      }
    len = strlen (line);
    written = 0;
    do
      {
	res = write (tmp_fd, &line[written], len - written);
	if (res > 0)
	  written += res;
      }
    while (res > 0 || (res < 0 && errno == EAGAIN));
  }
  close (tmp_fd);
  /* The temporary file is deleted by the gpgme-w32spawn process
     (hopefully).  */
    
  TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
	      "dwProcessID=%d, dwThreadId=%d",
	      pi.hProcess, pi.hThread, 
	      (int) pi.dwProcessId, (int) pi.dwThreadId);
  
  if (r_pid)
    *r_pid = (pid_t)pi.dwProcessId;

  if (ResumeThread (pi.hThread) < 0)
    TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
  
  if (!CloseHandle (pi.hThread))
    TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
		(int) GetLastError ());

  TRACE_LOG1 ("process=%p", pi.hProcess);

  /* We don't need to wait for the process.  */
  if (!CloseHandle (pi.hProcess))
    TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
		(int) GetLastError ());

  if (! (flags & IOSPAWN_FLAG_NOCLOSE))
    {
      for (i = 0; fd_list[i].fd != -1; i++)
	_gpgme_io_close (fd_list[i].fd);
    }

  for (i = 0; fd_list[i].fd != -1; i++)
    if (fd_list[i].dup_to == -1)
      TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
		  fd_list[i].peer_name);
    else
      TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
		  fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
		  ((fd_list[i].dup_to == 1) ? "out" : "err"));

  return TRACE_SYSRES (0);
}
Пример #24
0
int
_gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
{
  GIOChannel *chan; 
  int sockfd;
  int res;
  GIOFlags flags;
  GIOStatus status;
  GError *err = NULL;

  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
	      "addr=%p, addrlen=%i", addr, addrlen);

  chan = find_channel (fd);
  if (! chan)
    {
      errno = EINVAL;
      return TRACE_SYSRES (-1);
    }

  flags = g_io_channel_get_flags (chan);
  if (flags & G_IO_FLAG_NONBLOCK)
    {
      status = g_io_channel_set_flags (chan, flags & ~G_IO_FLAG_NONBLOCK, &err);
      if (err)
	{
	  TRACE_LOG1 ("setting flags error: %s", err->message);
	  g_error_free (err);
	  err = NULL;
	}
      if (status != G_IO_STATUS_NORMAL)
	{
	  errno = EIO;
	  return TRACE_SYSRES (-1);
	}
    }

  sockfd = giochannel_table[fd].socket;
  if (sockfd == INVALID_SOCKET)
    {
      errno = EINVAL;
      return TRACE_SYSRES (-1);
    }

  TRACE_LOG1 ("connect sockfd=0x%x", sockfd);
  res = connect (sockfd, addr, addrlen);

  /* FIXME: Error ignored here.  */
  if (! (flags & G_IO_FLAG_NONBLOCK))
    g_io_channel_set_flags (chan, flags, NULL);

  if (res)
    {
      TRACE_LOG2 ("connect failed: %i %i", res, WSAGetLastError ());

      errno = wsa2errno (WSAGetLastError ());
      return TRACE_SYSRES (-1);
    }

  return TRACE_SUC ();
}
Пример #25
0
/* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
   nothing to select, > 0 = number of signaled fds.  */
int
_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
{
  int npollfds;
  GPollFD *pollfds;
  int *pollfds_map; 
  int i;
  int j;
  int any;
  int n;
  int count;
  /* Use a 1s timeout.  */
  int timeout = 1000;
  void *dbg_help = NULL;
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
	      "nfds=%u, nonblock=%u", nfds, nonblock);

  if (nonblock)
    timeout = 0;

  pollfds = calloc (nfds, sizeof *pollfds);
  if (!pollfds)
    return -1;
  pollfds_map = calloc (nfds, sizeof *pollfds_map);
  if (!pollfds_map)
    {
      free (pollfds);
      return -1;
    }
  npollfds = 0;

  TRACE_SEQ (dbg_help, "select on [ ");
  any = 0;
  for (i = 0; i < nfds; i++)
    {
      GIOChannel *chan = NULL;

      if (fds[i].fd == -1) 
	continue;

      if ((fds[i].for_read || fds[i].for_write)
          && !(chan = find_channel (fds[i].fd)))
        {
          TRACE_ADD1 (dbg_help, "[BAD0x%x ", fds[i].fd);
          TRACE_END (dbg_help, "]"); 
          assert (!"see log file");
        }
      else if (fds[i].for_read )
	{
          assert(chan);
          g_io_channel_win32_make_pollfd (chan, G_IO_IN, pollfds + npollfds);
          pollfds_map[npollfds] = i;
	  TRACE_ADD2 (dbg_help, "r0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
          npollfds++;
	  any = 1;
        }
      else if (fds[i].for_write)
	{
          assert(chan);
          g_io_channel_win32_make_pollfd (chan, G_IO_OUT, pollfds + npollfds);
          pollfds_map[npollfds] = i;
	  TRACE_ADD2 (dbg_help, "w0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
          npollfds++;
	  any = 1;
        }
      fds[i].signaled = 0;
    }
  TRACE_END (dbg_help, "]"); 
  if (!any)
    {
      count = 0;
      goto leave;
    }


  count = g_io_channel_win32_poll (pollfds, npollfds, timeout);
  if (count < 0)
    {
      int saved_errno = errno;
      errno = saved_errno;
      goto leave;
    }

  TRACE_SEQ (dbg_help, "select OK [ ");
  if (TRACE_ENABLED (dbg_help))
    {
      for (i = 0; i < npollfds; i++)
	{
	  if ((pollfds[i].revents & G_IO_IN))
	    TRACE_ADD1 (dbg_help, "r0x%x ", fds[pollfds_map[i]].fd);
          if ((pollfds[i].revents & G_IO_OUT))
            TRACE_ADD1 (dbg_help, "w0x%x ", fds[pollfds_map[i]].fd);
        }
      TRACE_END (dbg_help, "]");
    }
    
  /* COUNT is used to stop the lop as soon as possible.  */
  for (n = count, i = 0; i < npollfds && n; i++)
    {
      j = pollfds_map[i];
      assert (j >= 0 && j < nfds);
      if (fds[j].fd == -1)
	;
      else if (fds[j].for_read)
	{
	  if ((pollfds[i].revents & G_IO_IN))
	    {
	      fds[j].signaled = 1;
	      n--;
            }
        }
      else if (fds[j].for_write)
	{
	  if ((pollfds[i].revents & G_IO_OUT))
	    {
	      fds[j].signaled = 1;
	      n--;
            }
        }
    }

leave:
  free (pollfds);
  free (pollfds_map);
  return TRACE_SYSRES (count);
}
Пример #26
0
/* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
   nothing to select, > 0 = number of signaled fds.  */
int
_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
{
  HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
  int waitidx[MAXIMUM_WAIT_OBJECTS];
  int code;
  int nwait;
  int i;
  int any;
  int count;
  void *dbg_help;
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
	      "nfds=%u, nonblock=%u", nfds, nonblock);

 restart:
  TRACE_SEQ (dbg_help, "select on [ ");
  any = 0;
  nwait = 0;
  count = 0;
  for (i=0; i < nfds; i++)
    {
      if (fds[i].fd == -1)
	continue;
      fds[i].signaled = 0;
      if (fds[i].for_read || fds[i].for_write)
	{
	  if (fds[i].for_read)
	    {
	      struct reader_context_s *ctx = find_reader (fds[i].fd,0);
	      
	      if (!ctx)
		TRACE_LOG1 ("error: no reader for FD 0x%x (ignored)",
			    fds[i].fd);
	      else
		{
		  if (nwait >= DIM (waitbuf))
		    {
		      TRACE_END (dbg_help, "oops ]");
		      TRACE_LOG ("Too many objects for WFMO!");
		      /* FIXME: Should translate the error code.  */
		      errno = EIO;
		      return TRACE_SYSRES (-1);
                    }
		  waitidx[nwait] = i;
		  waitbuf[nwait++] = ctx->have_data_ev;
                }
	      TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
	      any = 1;
            }
	  else if (fds[i].for_write)
	    {
	      struct writer_context_s *ctx = find_writer (fds[i].fd,0);
              
	      if (!ctx)
		TRACE_LOG1 ("error: no writer for FD 0x%x (ignored)",
			    fds[i].fd);
	      else
		{
		  if (nwait >= DIM (waitbuf))
		    {
		      TRACE_END (dbg_help, "oops ]");
		      TRACE_LOG ("Too many objects for WFMO!");
		      /* FIXME: Should translate the error code.  */
		      errno = EIO;
		      return TRACE_SYSRES (-1);
                    }
		  waitidx[nwait] = i;
		  waitbuf[nwait++] = ctx->is_empty;
                }
	      TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
	      any = 1;
            }
        }
    }
  TRACE_END (dbg_help, "]");
  if (!any) 
    return TRACE_SYSRES (0);

  code = WaitForMultipleObjects (nwait, waitbuf, 0, nonblock ? 0 : 1000);
  if (code >= WAIT_OBJECT_0 && 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)
	    {
	      assert (waitidx[i] >=0 && waitidx[i] < nfds);
	      fds[waitidx[i]].signaled = 1;
	      any = 1;
	      count++;
	    }
	}
      if (!any)
	{
	  TRACE_LOG ("no signaled objects found after WFMO");
	  count = -1;
	}
    }
  else if (code == WAIT_TIMEOUT)
    TRACE_LOG ("WFMO timed out");
  else if (code == WAIT_FAILED)
    {
      int le = (int) GetLastError ();
      if (le == ERROR_INVALID_HANDLE)
	{
	  int k;
	  int j = handle_to_fd (waitbuf[i]);
          
	  TRACE_LOG1 ("WFMO invalid handle %d removed", j);
	  for (k = 0 ; k < nfds; k++)
	    {
	      if (fds[k].fd == j)
		{
		  fds[k].for_read = fds[k].for_write = 0;
		  goto restart;
                }
            }
	  TRACE_LOG (" oops, or not???");
        }
      TRACE_LOG1 ("WFMO failed: %d", le);
      count = -1;
    }
  else
    {
      TRACE_LOG1 ("WFMO returned %d", code);
      count = -1;
    }
  
  if (count > 0)
    {
      TRACE_SEQ (dbg_help, "select OK [ ");
      for (i = 0; i < nfds; i++)
	{
	  if (fds[i].fd == -1)
	    continue;
	  if ((fds[i].for_read || fds[i].for_write) && fds[i].signaled)
	    TRACE_ADD2 (dbg_help, "%c0x%x ",
			fds[i].for_read ? 'r' : 'w', fds[i].fd);
        }
      TRACE_END (dbg_help, "]");
    }

  if (count < 0)
    {
      /* FIXME: Should determine a proper error code.  */
      errno = EIO;
    }
  
  return TRACE_SYSRES (count);
}
Пример #27
0
static ssize_t
__assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size)
{
  /* Due to the peculiarities of the W32 API we can't use read for a
     network socket and thus we try to use recv first and fallback to
     read if recv detects that it is not a network socket.  */
  int res;

  TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "__assuan_read", ctx,
	      "fd=0x%x, buffer=%p, size=%i", fd, buffer, size);

#ifdef HAVE_W32CE_SYSTEM
  /* This is a bit of a hack to support stdin over ssh.  Note that
     fread buffers fully while getchar is line buffered.  Weird, but
     that's the way it is.  ASSUAN_STDIN and ASSUAN_STDOUT are
     special handle values that shouldn't occur in the wild.  */
  if (fd == ASSUAN_STDIN)
    {
      int i = 0;
      int chr;
      while (i < size)
	{
	  chr = getchar();
	  if (chr == EOF)
	    break;
	  ((char*)buffer)[i++] = (char) chr;
	  if (chr == '\n')
	    break;
	}
      return TRACE_SYSRES (i);
    }
#endif

  res = recv (HANDLE2SOCKET (fd), buffer, size, 0);
  if (res == -1)
    {
      TRACE_LOG1 ("recv failed: rc=%d", (int)WSAGetLastError ());
      switch (WSAGetLastError ())
        {
        case WSAENOTSOCK:
          {
            DWORD nread = 0;
            
            res = ReadFile (fd, buffer, size, &nread, NULL);
            if (! res)
              {
                TRACE_LOG1 ("ReadFile failed: rc=%d", (int)GetLastError ());
                switch (GetLastError ())
                  {
                  case ERROR_BROKEN_PIPE:
		    gpg_err_set_errno (EPIPE);
		    break;

                  case ERROR_PIPE_NOT_CONNECTED:
                  case ERROR_BUSY:
		    gpg_err_set_errno (EAGAIN);
		    break;

                  default:
		    gpg_err_set_errno (EIO); 
                  }
                res = -1;
              }
            else
              res = (int) nread;
          }
          break;
          
        case WSAEWOULDBLOCK:
	  gpg_err_set_errno (EAGAIN);
	  break;

        case ERROR_BROKEN_PIPE:
	  gpg_err_set_errno (EPIPE);
	  break;

        default:
	  gpg_err_set_errno (EIO);
	  break;
        }
    }
  return TRACE_SYSRES (res);
}
Пример #28
0
int
_gpgme_io_write (int fd, const void *buffer, size_t count)
{
  struct writer_context_s *ctx;
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
	      "buffer=%p, count=%u", buffer, count);
  TRACE_LOGBUF (buffer, count);

  if (count == 0)
    return TRACE_SYSRES (0);

  ctx = find_writer (fd, 0);
  if (!ctx)
    return TRACE_SYSRES (-1);

  LOCK (ctx->mutex);
  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_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
	  UNLOCK (ctx->mutex);
	  /* FIXME: Should translate the error code.  */
	  errno = EIO;
	  return TRACE_SYSRES (-1);
	}
      UNLOCK (ctx->mutex);
      TRACE_LOG1 ("waiting for empty buffer in thread %p", ctx->thread_hd);
      WaitForSingleObject (ctx->is_empty, INFINITE);
      TRACE_LOG1 ("thread %p buffer is empty", ctx->thread_hd);
      LOCK (ctx->mutex);
    }

  if (ctx->error)
    {
      UNLOCK (ctx->mutex);
      if (ctx->error_code == ERROR_NO_DATA)
        errno = EPIPE;
      else
        errno = EIO;
      return TRACE_SYSRES (-1);
    }

  /* If no error occured, the number of bytes in the buffer must be
     zero.  */
  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_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
      UNLOCK (ctx->mutex);
      /* FIXME: Should translate the error code.  */
      errno = EIO;
      return TRACE_SYSRES (-1);
    }
  if (!SetEvent (ctx->have_data))
    {
      TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
      UNLOCK (ctx->mutex);
      /* FIXME: Should translate the error code.  */
      errno = EIO;
      return TRACE_SYSRES (-1);
    }
  UNLOCK (ctx->mutex);

  return TRACE_SYSRES ((int) count);
}