Blame common/tio.c

Packit 6bd9ab
/*
Packit 6bd9ab
   tio.c - timed io functions
Packit 6bd9ab
   This file is part of the nss-pam-ldapd library.
Packit 6bd9ab
Packit 6bd9ab
   Copyright (C) 2007-2014 Arthur de Jong
Packit 6bd9ab
Packit 6bd9ab
   This library is free software; you can redistribute it and/or
Packit 6bd9ab
   modify it under the terms of the GNU Lesser General Public
Packit 6bd9ab
   License as published by the Free Software Foundation; either
Packit 6bd9ab
   version 2.1 of the License, or (at your option) any later version.
Packit 6bd9ab
Packit 6bd9ab
   This library is distributed in the hope that it will be useful,
Packit 6bd9ab
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6bd9ab
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6bd9ab
   Lesser General Public License for more details.
Packit 6bd9ab
Packit 6bd9ab
   You should have received a copy of the GNU Lesser General Public
Packit 6bd9ab
   License along with this library; if not, write to the Free Software
Packit 6bd9ab
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit 6bd9ab
   02110-1301 USA
Packit 6bd9ab
*/
Packit 6bd9ab
Packit 6bd9ab
#include "config.h"
Packit 6bd9ab
Packit 6bd9ab
#ifdef HAVE_STDINT_H
Packit 6bd9ab
#include <stdint.h>
Packit 6bd9ab
#endif /* HAVE_STDINT_H */
Packit 6bd9ab
#include <stdlib.h>
Packit 6bd9ab
#include <unistd.h>
Packit 6bd9ab
#include <sys/time.h>
Packit 6bd9ab
#include <sys/types.h>
Packit 6bd9ab
#include <sys/socket.h>
Packit 6bd9ab
#include <errno.h>
Packit 6bd9ab
#include <string.h>
Packit 6bd9ab
#include <signal.h>
Packit 6bd9ab
#include <stdio.h>
Packit 6bd9ab
#include <limits.h>
Packit 6bd9ab
#include <poll.h>
Packit 6bd9ab
#include <time.h>
Packit 6bd9ab
Packit 6bd9ab
#include "tio.h"
Packit 6bd9ab
Packit 6bd9ab
/* for platforms that don't have ETIME use ETIMEDOUT */
Packit 6bd9ab
#ifndef ETIME
Packit 6bd9ab
#define ETIME ETIMEDOUT
Packit 6bd9ab
#endif /* ETIME */
Packit 6bd9ab
Packit 6bd9ab
/* structure that holds a buffer
Packit 6bd9ab
   the buffer contains the data that is between the application and the
Packit 6bd9ab
   file descriptor that is used for efficient transfer
Packit 6bd9ab
   the buffer is built up as follows:
Packit 6bd9ab
   |.....********......|
Packit 6bd9ab
         ^start        ^size
Packit 6bd9ab
         ^--len--^           */
Packit 6bd9ab
struct tio_buffer {
Packit 6bd9ab
  uint8_t *buffer;
Packit 6bd9ab
  size_t size;      /* the size of the buffer */
Packit 6bd9ab
  size_t maxsize;   /* the maximum size of the buffer */
Packit 6bd9ab
  size_t start;     /* the start of the data (before start is unused) */
Packit 6bd9ab
  size_t len;       /* size of the data (from the start) */
Packit 6bd9ab
};
Packit 6bd9ab
Packit 6bd9ab
/* structure that holds all the state for files */
Packit 6bd9ab
struct tio_fileinfo {
Packit 6bd9ab
  int fd;
Packit 6bd9ab
  struct tio_buffer readbuffer;
Packit 6bd9ab
  struct tio_buffer writebuffer;
Packit 6bd9ab
  int readtimeout;
Packit 6bd9ab
  int writetimeout;
Packit 6bd9ab
  int read_resettable; /* whether the tio_reset() function can be called */
Packit 6bd9ab
#ifdef DEBUG_TIO_STATS
Packit 6bd9ab
  /* this is used to collect statistics on the use of the streams
Packit 6bd9ab
     and can be used to tune the buffer sizes */
Packit 6bd9ab
  size_t byteswritten;
Packit 6bd9ab
  size_t bytesread;
Packit 6bd9ab
#endif /* DEBUG_TIO_STATS */
Packit 6bd9ab
};
Packit 6bd9ab
Packit 6bd9ab
/* some older versions of Solaris don't provide CLOCK_MONOTONIC but do have
Packit 6bd9ab
   a CLOCK_HIGHRES that has the same properties we need */
Packit 6bd9ab
#ifndef CLOCK_MONOTONIC
Packit 6bd9ab
#ifdef CLOCK_HIGHRES
Packit 6bd9ab
#define CLOCK_MONOTONIC CLOCK_HIGHRES
Packit 6bd9ab
#endif /* CLOCK_HIGHRES */
Packit 6bd9ab
#endif /* not CLOCK_MONOTONIC */
Packit 6bd9ab
Packit 6bd9ab
/* update the timeout to the value that is remaining before the deadline
Packit 6bd9ab
   returns the number of milliseconds before the deadline (or a negative
Packit 6bd9ab
   value of the deadline has expired) */
Packit 6bd9ab
static inline int tio_time_remaining(struct timespec *deadline, int timeout)
Packit 6bd9ab
{
Packit 6bd9ab
  struct timespec tv;
Packit 6bd9ab
  /* if this is the first call, set the deadline and return the full time */
Packit 6bd9ab
  if ((deadline->tv_sec == 0) && (deadline->tv_nsec == 0))
Packit 6bd9ab
  {
Packit 6bd9ab
    if (clock_gettime(CLOCK_MONOTONIC, deadline) == 0)
Packit 6bd9ab
    {
Packit 6bd9ab
      deadline->tv_sec += timeout / 1000;
Packit 6bd9ab
      deadline->tv_nsec += (timeout % 1000) * 1000000;
Packit 6bd9ab
    }
Packit 6bd9ab
    return timeout;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* get the current time (fall back to full time on error) */
Packit 6bd9ab
  if (clock_gettime(CLOCK_MONOTONIC, &tv))
Packit 6bd9ab
    return timeout;
Packit 6bd9ab
  /* calculate time remaining in milliseconds */
Packit 6bd9ab
  return (deadline->tv_sec - tv.tv_sec) * 1000 +
Packit 6bd9ab
         (deadline->tv_nsec - tv.tv_nsec) / 1000000;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* open a new TFILE based on the file descriptor */
Packit 6bd9ab
TFILE *tio_fdopen(int fd, int readtimeout, int writetimeout,
Packit 6bd9ab
                  size_t initreadsize, size_t maxreadsize,
Packit 6bd9ab
                  size_t initwritesize, size_t maxwritesize)
Packit 6bd9ab
{
Packit 6bd9ab
  struct tio_fileinfo *fp;
Packit 6bd9ab
  fp = (struct tio_fileinfo *)malloc(sizeof(struct tio_fileinfo));
Packit 6bd9ab
  if (fp == NULL)
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  fp->fd = fd;
Packit 6bd9ab
  /* initialize read buffer */
Packit 6bd9ab
  fp->readbuffer.buffer = (uint8_t *)malloc(initreadsize);
Packit 6bd9ab
  if (fp->readbuffer.buffer == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    free(fp);
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  fp->readbuffer.size = initreadsize;
Packit 6bd9ab
  fp->readbuffer.maxsize = maxreadsize;
Packit 6bd9ab
  fp->readbuffer.start = 0;
Packit 6bd9ab
  fp->readbuffer.len = 0;
Packit 6bd9ab
  /* initialize write buffer */
Packit 6bd9ab
  fp->writebuffer.buffer = (uint8_t *)malloc(initwritesize);
Packit 6bd9ab
  if (fp->writebuffer.buffer == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    free(fp->readbuffer.buffer);
Packit 6bd9ab
    free(fp);
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  fp->writebuffer.size = initwritesize;
Packit 6bd9ab
  fp->writebuffer.maxsize = maxwritesize;
Packit 6bd9ab
  fp->writebuffer.start = 0;
Packit 6bd9ab
  fp->writebuffer.len = 0;
Packit 6bd9ab
  /* initialize other attributes */
Packit 6bd9ab
  fp->readtimeout = readtimeout;
Packit 6bd9ab
  fp->writetimeout = writetimeout;
Packit 6bd9ab
  fp->read_resettable = 0;
Packit 6bd9ab
#ifdef DEBUG_TIO_STATS
Packit 6bd9ab
  fp->byteswritten = 0;
Packit 6bd9ab
  fp->bytesread = 0;
Packit 6bd9ab
#endif /* DEBUG_TIO_STATS */
Packit 6bd9ab
  return fp;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* wait for any activity on the specified file descriptor using
Packit 6bd9ab
   the specified deadline */
Packit 6bd9ab
static int tio_wait(int fd, short events, int timeout,
Packit 6bd9ab
                    struct timespec *deadline)
Packit 6bd9ab
{
Packit 6bd9ab
  int t;
Packit 6bd9ab
  struct pollfd fds[1];
Packit 6bd9ab
  int rv;
Packit 6bd9ab
  while (1)
Packit 6bd9ab
  {
Packit 6bd9ab
    fds[0].fd = fd;
Packit 6bd9ab
    fds[0].events = events;
Packit 6bd9ab
    /* figure out the time we need to wait */
Packit 6bd9ab
    if ((t = tio_time_remaining(deadline, timeout)) < 0)
Packit 6bd9ab
    {
Packit 6bd9ab
      errno = ETIME;
Packit 6bd9ab
      return -1;
Packit 6bd9ab
    }
Packit 6bd9ab
    /* sanitiy check for moving clock */
Packit 6bd9ab
    if (t > timeout)
Packit 6bd9ab
      t = timeout;
Packit 6bd9ab
    /* wait for activity */
Packit 6bd9ab
    rv = poll(fds, 1, t);
Packit 6bd9ab
    if (rv > 0)
Packit 6bd9ab
      return 0; /* we have activity */
Packit 6bd9ab
    else if (rv == 0)
Packit 6bd9ab
    {
Packit 6bd9ab
      /* no file descriptors were available within the specified time */
Packit 6bd9ab
      errno = ETIME;
Packit 6bd9ab
      return -1;
Packit 6bd9ab
    }
Packit 6bd9ab
    else if ((errno != EINTR) && (errno != EAGAIN))
Packit 6bd9ab
      /* some error ocurred */
Packit 6bd9ab
      return -1;
Packit 6bd9ab
    /* we just try again on EINTR or EAGAIN */
Packit 6bd9ab
  }
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* do a read on the file descriptor, returning the data in the buffer
Packit 6bd9ab
   if no data was read in the specified time an error is returned */
Packit 6bd9ab
int tio_read(TFILE *fp, void *buf, size_t count)
Packit 6bd9ab
{
Packit 6bd9ab
  struct timespec deadline = {0, 0};
Packit 6bd9ab
  int rv;
Packit 6bd9ab
  uint8_t *tmp;
Packit 6bd9ab
  size_t newsz;
Packit 6bd9ab
  size_t len;
Packit 6bd9ab
  /* have a more convenient storage type for the buffer */
Packit 6bd9ab
  uint8_t *ptr = (uint8_t *)buf;
Packit 6bd9ab
  /* loop until we have returned all the needed data */
Packit 6bd9ab
  while (1)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* check if we have enough data in the buffer */
Packit 6bd9ab
    if (fp->readbuffer.len >= count)
Packit 6bd9ab
    {
Packit 6bd9ab
      if (count > 0)
Packit 6bd9ab
      {
Packit 6bd9ab
        if (ptr != NULL)
Packit 6bd9ab
          memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start, count);
Packit 6bd9ab
        /* adjust buffer position */
Packit 6bd9ab
        fp->readbuffer.start += count;
Packit 6bd9ab
        fp->readbuffer.len -= count;
Packit 6bd9ab
      }
Packit 6bd9ab
      return 0;
Packit 6bd9ab
    }
Packit 6bd9ab
    /* empty what we have and continue from there */
Packit 6bd9ab
    if (fp->readbuffer.len > 0)
Packit 6bd9ab
    {
Packit 6bd9ab
      if (ptr != NULL)
Packit 6bd9ab
      {
Packit 6bd9ab
        memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start,
Packit 6bd9ab
               fp->readbuffer.len);
Packit 6bd9ab
        ptr += fp->readbuffer.len;
Packit 6bd9ab
      }
Packit 6bd9ab
      count -= fp->readbuffer.len;
Packit 6bd9ab
      fp->readbuffer.start += fp->readbuffer.len;
Packit 6bd9ab
      fp->readbuffer.len = 0;
Packit 6bd9ab
    }
Packit 6bd9ab
    /* after this point until the read fp->readbuffer.len is 0 */
Packit 6bd9ab
    if (!fp->read_resettable)
Packit 6bd9ab
    {
Packit 6bd9ab
      /* the stream is not resettable, re-use the buffer */
Packit 6bd9ab
      fp->readbuffer.start = 0;
Packit 6bd9ab
    }
Packit 6bd9ab
    else if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
Packit 6bd9ab
    {
Packit 6bd9ab
      /* buffer is running empty, try to grow buffer */
Packit 6bd9ab
      if (fp->readbuffer.size < fp->readbuffer.maxsize)
Packit 6bd9ab
      {
Packit 6bd9ab
        newsz = fp->readbuffer.size * 2;
Packit 6bd9ab
        if (newsz > fp->readbuffer.maxsize)
Packit 6bd9ab
          newsz = fp->readbuffer.maxsize;
Packit 6bd9ab
        tmp = realloc(fp->readbuffer.buffer, newsz);
Packit 6bd9ab
        if (tmp != NULL)
Packit 6bd9ab
        {
Packit 6bd9ab
          fp->readbuffer.buffer = tmp;
Packit 6bd9ab
          fp->readbuffer.size = newsz;
Packit 6bd9ab
        }
Packit 6bd9ab
      }
Packit 6bd9ab
      /* if buffer still does not contain enough room, clear resettable */
Packit 6bd9ab
      if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
Packit 6bd9ab
      {
Packit 6bd9ab
        fp->readbuffer.start = 0;
Packit 6bd9ab
        fp->read_resettable = 0;
Packit 6bd9ab
      }
Packit 6bd9ab
    }
Packit 6bd9ab
    /* wait until we have input */
Packit 6bd9ab
    if (tio_wait(fp->fd, POLLIN, fp->readtimeout, &deadline))
Packit 6bd9ab
      return -1;
Packit 6bd9ab
    /* read the input in the buffer */
Packit 6bd9ab
    len = fp->readbuffer.size - fp->readbuffer.start;
Packit 6bd9ab
#ifdef SSIZE_MAX
Packit 6bd9ab
    if (len > SSIZE_MAX)
Packit 6bd9ab
      len = SSIZE_MAX;
Packit 6bd9ab
#endif /* SSIZE_MAX */
Packit 6bd9ab
    rv = read(fp->fd, fp->readbuffer.buffer + fp->readbuffer.start, len);
Packit 6bd9ab
    /* check for errors */
Packit 6bd9ab
    if (rv == 0)
Packit 6bd9ab
    {
Packit 6bd9ab
      errno = ECONNRESET;
Packit 6bd9ab
      return -1;
Packit 6bd9ab
    }
Packit 6bd9ab
    else if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))
Packit 6bd9ab
      return -1;        /* something went wrong with the read */
Packit 6bd9ab
    else if (rv > 0)
Packit 6bd9ab
      fp->readbuffer.len = rv;  /* skip the read part in the buffer */
Packit 6bd9ab
#ifdef DEBUG_TIO_STATS
Packit 6bd9ab
    fp->bytesread += rv;
Packit 6bd9ab
#endif /* DEBUG_TIO_STATS */
Packit 6bd9ab
  }
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* Read and discard the specified number of bytes from the stream. */
Packit 6bd9ab
int tio_skip(TFILE *fp, size_t count)
Packit 6bd9ab
{
Packit 6bd9ab
  return tio_read(fp, NULL, count);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* Read all available data from the stream and empty the read buffer. */
Packit 6bd9ab
int tio_skipall(TFILE *fp, int timeout)
Packit 6bd9ab
{
Packit 6bd9ab
  struct timespec deadline = {0, 0};
Packit 6bd9ab
  int rv;
Packit 6bd9ab
  size_t len;
Packit 6bd9ab
  /* clear the read buffer */
Packit 6bd9ab
  fp->readbuffer.start = 0;
Packit 6bd9ab
  fp->readbuffer.len = 0;
Packit 6bd9ab
  fp->read_resettable = 0;
Packit 6bd9ab
  /* read until we can't read no more */
Packit 6bd9ab
  len = fp->readbuffer.size;
Packit 6bd9ab
#ifdef SSIZE_MAX
Packit 6bd9ab
  if (len > SSIZE_MAX)
Packit 6bd9ab
    len = SSIZE_MAX;
Packit 6bd9ab
#endif /* SSIZE_MAX */
Packit 6bd9ab
  while (1)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* wait until we have input */
Packit 6bd9ab
    if (tio_wait(fp->fd, POLLIN, timeout, &deadline))
Packit 6bd9ab
      return -1;
Packit 6bd9ab
    /* read data from the stream */
Packit 6bd9ab
    rv = read(fp->fd, fp->readbuffer.buffer, len);
Packit 6bd9ab
    if (rv == 0)
Packit 6bd9ab
      return 0; /* end-of-file */
Packit 6bd9ab
    if ((rv < 0) && (errno == EWOULDBLOCK))
Packit 6bd9ab
      return 0; /* we've ready everything we can without blocking */
Packit 6bd9ab
    if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))
Packit 6bd9ab
      return -1; /* something went wrong with the read */
Packit 6bd9ab
  }
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* the caller has assured us that we can write to the file descriptor
Packit 6bd9ab
   and we give it a shot */
Packit 6bd9ab
static int tio_writebuf(TFILE *fp)
Packit 6bd9ab
{
Packit 6bd9ab
  int rv;
Packit 6bd9ab
  /* write the buffer */
Packit 6bd9ab
#ifdef MSG_NOSIGNAL
Packit 6bd9ab
  rv = send(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start,
Packit 6bd9ab
            fp->writebuffer.len, MSG_NOSIGNAL);
Packit 6bd9ab
#else /* not MSG_NOSIGNAL */
Packit 6bd9ab
  /* on platforms that cannot use send() with masked signals, we change the
Packit 6bd9ab
     signal mask and change it back after the write (note that there is a
Packit 6bd9ab
     race condition here) */
Packit 6bd9ab
  struct sigaction act, oldact;
Packit 6bd9ab
  /* set up sigaction */
Packit 6bd9ab
  memset(&act, 0, sizeof(struct sigaction));
Packit 6bd9ab
  act.sa_sigaction = NULL;
Packit 6bd9ab
  act.sa_handler = SIG_IGN;
Packit 6bd9ab
  sigemptyset(&act.sa_mask);
Packit 6bd9ab
  act.sa_flags = SA_RESTART;
Packit 6bd9ab
  /* ignore SIGPIPE */
Packit 6bd9ab
  if (sigaction(SIGPIPE, &act, &oldact) != 0)
Packit 6bd9ab
    return -1; /* error setting signal handler */
Packit 6bd9ab
  /* write the buffer */
Packit 6bd9ab
  rv = write(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start,
Packit 6bd9ab
             fp->writebuffer.len);
Packit 6bd9ab
  /* restore the old handler for SIGPIPE */
Packit 6bd9ab
  if (sigaction(SIGPIPE, &oldact, NULL) != 0)
Packit 6bd9ab
    return -1; /* error restoring signal handler */
Packit 6bd9ab
#endif
Packit 6bd9ab
  /* check for errors */
Packit 6bd9ab
  if ((rv == 0) || ((rv < 0) && (errno != EINTR) && (errno != EAGAIN)))
Packit 6bd9ab
    return -1; /* something went wrong with the write */
Packit 6bd9ab
  /* skip the written part in the buffer */
Packit 6bd9ab
  if (rv > 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    fp->writebuffer.start += rv;
Packit 6bd9ab
    fp->writebuffer.len -= rv;
Packit 6bd9ab
#ifdef DEBUG_TIO_STATS
Packit 6bd9ab
    fp->byteswritten += rv;
Packit 6bd9ab
#endif /* DEBUG_TIO_STATS */
Packit 6bd9ab
    /* reset start if len is 0 */
Packit 6bd9ab
    if (fp->writebuffer.len == 0)
Packit 6bd9ab
      fp->writebuffer.start = 0;
Packit 6bd9ab
    /* move contents of the buffer to the front if it will save enough room */
Packit 6bd9ab
    if (fp->writebuffer.start >= (fp->writebuffer.size / 4))
Packit 6bd9ab
    {
Packit 6bd9ab
      memmove(fp->writebuffer.buffer,
Packit 6bd9ab
              fp->writebuffer.buffer + fp->writebuffer.start,
Packit 6bd9ab
              fp->writebuffer.len);
Packit 6bd9ab
      fp->writebuffer.start = 0;
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
  return 0;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* write all the data in the buffer to the stream */
Packit 6bd9ab
int tio_flush(TFILE *fp)
Packit 6bd9ab
{
Packit 6bd9ab
  struct timespec deadline = {0, 0};
Packit 6bd9ab
  /* loop until we have written our buffer */
Packit 6bd9ab
  while (fp->writebuffer.len > 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* wait until we can write */
Packit 6bd9ab
    if (tio_wait(fp->fd, POLLOUT, fp->writetimeout, &deadline))
Packit 6bd9ab
      return -1;
Packit 6bd9ab
    /* write one block */
Packit 6bd9ab
    if (tio_writebuf(fp))
Packit 6bd9ab
      return -1;
Packit 6bd9ab
  }
Packit 6bd9ab
  return 0;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* try a single write of data in the buffer if the file descriptor
Packit 6bd9ab
   will accept data */
Packit 6bd9ab
static int tio_flush_nonblock(TFILE *fp)
Packit 6bd9ab
{
Packit 6bd9ab
  struct pollfd fds[1];
Packit 6bd9ab
  int rv;
Packit 6bd9ab
  /* see if we can write without blocking */
Packit 6bd9ab
  fds[0].fd = fp->fd;
Packit 6bd9ab
  fds[0].events = POLLOUT;
Packit 6bd9ab
  rv = poll(fds, 1, 0);
Packit 6bd9ab
  /* check if any file descriptors were ready (timeout) or we were
Packit 6bd9ab
     interrupted */
Packit 6bd9ab
  if ((rv == 0) || ((rv < 0) && ((errno == EINTR) || (errno == EAGAIN))))
Packit 6bd9ab
    return 0;
Packit 6bd9ab
  /* any other errors? */
Packit 6bd9ab
  if (rv < 0)
Packit 6bd9ab
    return -1;
Packit 6bd9ab
  /* so file descriptor will accept writes */
Packit 6bd9ab
  return tio_writebuf(fp);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
int tio_write(TFILE *fp, const void *buf, size_t count)
Packit 6bd9ab
{
Packit 6bd9ab
  size_t fr;
Packit 6bd9ab
  uint8_t *tmp;
Packit 6bd9ab
  size_t newsz;
Packit 6bd9ab
  const uint8_t *ptr = (const uint8_t *)buf;
Packit 6bd9ab
  /* keep filling the buffer until we have bufferred everything */
Packit 6bd9ab
  while (count > 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* figure out free size in buffer */
Packit 6bd9ab
    fr = fp->writebuffer.size - (fp->writebuffer.start + fp->writebuffer.len);
Packit 6bd9ab
    if (count <= fr)
Packit 6bd9ab
    {
Packit 6bd9ab
      /* the data fits in the buffer */
Packit 6bd9ab
      memcpy(fp->writebuffer.buffer + fp->writebuffer.start +
Packit 6bd9ab
             fp->writebuffer.len, ptr, count);
Packit 6bd9ab
      fp->writebuffer.len += count;
Packit 6bd9ab
      return 0;
Packit 6bd9ab
    }
Packit 6bd9ab
    else if (fr > 0)
Packit 6bd9ab
    {
Packit 6bd9ab
      /* fill the buffer with data that will fit */
Packit 6bd9ab
      memcpy(fp->writebuffer.buffer + fp->writebuffer.start +
Packit 6bd9ab
             fp->writebuffer.len, ptr, fr);
Packit 6bd9ab
      fp->writebuffer.len += fr;
Packit 6bd9ab
      ptr += fr;
Packit 6bd9ab
      count -= fr;
Packit 6bd9ab
    }
Packit 6bd9ab
    /* try to flush some of the data that is in the buffer */
Packit 6bd9ab
    if (tio_flush_nonblock(fp))
Packit 6bd9ab
      return -1;
Packit 6bd9ab
    /* if we have room now, try again */
Packit 6bd9ab
    if (fp->writebuffer.size > (fp->writebuffer.start + fp->writebuffer.len))
Packit 6bd9ab
      continue;
Packit 6bd9ab
    /* try to grow the buffer */
Packit 6bd9ab
    if (fp->writebuffer.size < fp->writebuffer.maxsize)
Packit 6bd9ab
    {
Packit 6bd9ab
      newsz = fp->writebuffer.size * 2;
Packit 6bd9ab
      if (newsz > fp->writebuffer.maxsize)
Packit 6bd9ab
        newsz = fp->writebuffer.maxsize;
Packit 6bd9ab
      tmp = realloc(fp->writebuffer.buffer, newsz);
Packit 6bd9ab
      if (tmp != NULL)
Packit 6bd9ab
      {
Packit 6bd9ab
        fp->writebuffer.buffer = tmp;
Packit 6bd9ab
        fp->writebuffer.size = newsz;
Packit 6bd9ab
        continue; /* try again */
Packit 6bd9ab
      }
Packit 6bd9ab
    }
Packit 6bd9ab
    /* write the buffer to the stream */
Packit 6bd9ab
    if (tio_flush(fp))
Packit 6bd9ab
      return -1;
Packit 6bd9ab
  }
Packit 6bd9ab
  return 0;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
int tio_close(TFILE *fp)
Packit 6bd9ab
{
Packit 6bd9ab
  int retv;
Packit 6bd9ab
  /* write any buffered data */
Packit 6bd9ab
  retv = tio_flush(fp);
Packit 6bd9ab
#ifdef DEBUG_TIO_STATS
Packit 6bd9ab
  /* dump statistics to stderr */
Packit 6bd9ab
  fprintf(stderr, "DEBUG_TIO_STATS READ=%lu WRITTEN=%lu\n",
Packit 6bd9ab
          (unsigned long)fp->bytesread, (unsigned long)fp->byteswritten);
Packit 6bd9ab
#endif /* DEBUG_TIO_STATS */
Packit 6bd9ab
  /* close file descriptor */
Packit 6bd9ab
  if (close(fp->fd))
Packit 6bd9ab
    retv = -1;
Packit 6bd9ab
  /* free any allocated buffers */
Packit 6bd9ab
  memset(fp->readbuffer.buffer, 0, fp->readbuffer.size);
Packit 6bd9ab
  memset(fp->writebuffer.buffer, 0, fp->writebuffer.size);
Packit 6bd9ab
  free(fp->readbuffer.buffer);
Packit 6bd9ab
  free(fp->writebuffer.buffer);
Packit 6bd9ab
  /* free the tio struct itself */
Packit 6bd9ab
  free(fp);
Packit 6bd9ab
  /* return the result of the earlier operations */
Packit 6bd9ab
  return retv;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
void tio_mark(TFILE *fp)
Packit 6bd9ab
{
Packit 6bd9ab
  /* move any data in the buffer to the start of the buffer */
Packit 6bd9ab
  if ((fp->readbuffer.start > 0) && (fp->readbuffer.len > 0))
Packit 6bd9ab
  {
Packit 6bd9ab
    memmove(fp->readbuffer.buffer,
Packit 6bd9ab
            fp->readbuffer.buffer + fp->readbuffer.start, fp->readbuffer.len);
Packit 6bd9ab
    fp->readbuffer.start = 0;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* mark the stream as resettable */
Packit 6bd9ab
  fp->read_resettable = 1;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
int tio_reset(TFILE *fp)
Packit 6bd9ab
{
Packit 6bd9ab
  /* check if the stream is (still) resettable */
Packit 6bd9ab
  if (!fp->read_resettable)
Packit 6bd9ab
    return -1;
Packit 6bd9ab
  /* reset the buffer */
Packit 6bd9ab
  fp->readbuffer.len += fp->readbuffer.start;
Packit 6bd9ab
  fp->readbuffer.start = 0;
Packit 6bd9ab
  return 0;
Packit 6bd9ab
}