示例#1
0
int storage_file_write(struct storage_msg *msg,
                       const void *r, size_t req_len)
{
    int rc;
    const struct storage_file_write_req *req = r;

    if (req_len < sizeof(*req)) {
        ALOGE("%s: invalid request length (%zd < %zd)\n",
              __func__, req_len, sizeof(*req));
        msg->result = STORAGE_ERR_NOT_VALID;
        goto err_response;
    }

    int fd = lookup_fd(req->handle, true);
    if (write_with_retry(fd, &req->data[0], req_len - sizeof(*req),
                         req->offset) < 0) {
        rc = errno;
        ALOGW("%s: error writing file (fd=%d): %s\n",
              __func__, fd, strerror(errno));
        msg->result = translate_errno(rc);
        goto err_response;
    }

    msg->result = STORAGE_NO_ERROR;

err_response:
    return ipc_respond(msg, NULL, 0);
}
示例#2
0
struct chan *fdtochan(struct fd_table *fdt, int fd, int mode, int chkmnt,
                      int iref)
{
	struct chan *c;

	c = lookup_fd(fdt, fd, iref, FALSE);
	if (!c) {
		/* We lost the info about why there was a problem (we used to track file
		 * group closed too, can add that in later). */
		error(EBADF, ERROR_FIXME);
	}
	if (chkmnt && (c->flag & CMSG)) {
		if (iref)
			cclose(c);
		error(EBADF, ERROR_FIXME);
	}
	if (mode < 0)
		return c;
	if ((mode & c->mode) != mode) {
		if (iref)
			cclose(c);
		error(EBADF,
		      "FD access mode failure: chan mode 0x%x, wanted 0x%x (opened with 0 instead of O_READ?)",
		      c->mode, mode);
	}
	return c;
}
示例#3
0
int storage_file_set_size(struct storage_msg *msg,
                          const void *r, size_t req_len)
{
    const struct storage_file_set_size_req *req = r;

    if (req_len != sizeof(*req)) {
        ALOGE("%s: invalid request length (%zd != %zd)\n",
              __func__, req_len, sizeof(*req));
        msg->result = STORAGE_ERR_NOT_VALID;
        goto err_response;
    }

    int fd = lookup_fd(req->handle, true);
    int rc = TEMP_FAILURE_RETRY(ftruncate(fd, req->size));
    if (rc < 0) {
        rc = errno;
        ALOGE("%s: error truncating file (fd=%d): %s\n",
              __func__, fd, strerror(errno));
        msg->result = translate_errno(rc);
        goto err_response;
    }

    msg->result = STORAGE_NO_ERROR;

err_response:
    return ipc_respond(msg, NULL, 0);
}
示例#4
0
int storage_file_get_size(struct storage_msg *msg,
                          const void *r, size_t req_len)
{
    const struct storage_file_get_size_req *req = r;
    struct storage_file_get_size_resp resp = {0};

    if (req_len != sizeof(*req)) {
        ALOGE("%s: invalid request length (%zd != %zd)\n",
              __func__, req_len, sizeof(*req));
        msg->result = STORAGE_ERR_NOT_VALID;
        goto err_response;
    }

    struct stat stat;
    int fd = lookup_fd(req->handle, false);
    int rc = fstat(fd, &stat);
    if (rc < 0) {
        rc = errno;
        ALOGE("%s: error stat'ing file (fd=%d): %s\n",
              __func__, fd, strerror(errno));
        msg->result = translate_errno(rc);
        goto err_response;
    }

    resp.size = stat.st_size;
    msg->result = STORAGE_NO_ERROR;
    return ipc_respond(msg, &resp, sizeof(resp));

err_response:
    return ipc_respond(msg, NULL, 0);
}
示例#5
0
/*
 * comm_select
 *
 * Called to do the new-style IO, courtesy of squid (like most of this
 * new IO code). This routine handles the stuff we've hidden in
 * comm_setselect and fd_table[] and calls callbacks for IO ready
 * events.
 */
void
comm_select(void)
{
  int num, i;
  static struct kevent ke[KE_LENGTH];
  struct timespec poll_time;
  void (*hdl)(fde_t *, void *);
  fde_t *F;

  /*
   * remember we are doing NANOseconds here, not micro/milli. God knows
   * why jlemon used a timespec, but hey, he wrote the interface, not I
   *   -- Adrian
   */
  poll_time.tv_sec = 0;
  poll_time.tv_nsec = SELECT_DELAY * 1000000;
  num = kevent(kqfd.fd, kq_fdlist, kqoff, ke, KE_LENGTH, &poll_time);
  kqoff = 0;

  set_time();

  if (num < 0)
  {
#ifdef HAVE_USLEEP
    usleep(50000);  /* avoid 99% CPU in comm_select */
#endif
    return;
  }

  for (i = 0; i < num; i++)
  {
    F = lookup_fd(ke[i].ident);
    if (F == NULL || !F->flags.open || (ke[i].flags & EV_ERROR))
      continue;

    if (ke[i].filter == EVFILT_READ)
    {
      if ((hdl = F->read_handler) != NULL)
      {
        F->read_handler = NULL;
        hdl(F, F->read_data);
        if (!F->flags.open)
          continue;
      }
    }

    if (ke[i].filter == EVFILT_WRITE)
    {
      if ((hdl = F->write_handler) != NULL)
      {
        F->write_handler = NULL;
        hdl(F, F->write_data);
        if (!F->flags.open)
          continue;
      }
    }

    comm_setselect(F, 0, NULL, NULL, 0);
  }
}
示例#6
0
/* Returns the file descriptor associated with the given handle.
   Terminates the process if HANDLE is not associated with an
   open directory. */
static struct file_descriptor *
lookup_dir_fd (int handle) 
{
  struct file_descriptor *fd = lookup_fd (handle);
  if (fd->dir == NULL)
    thread_exit ();
  return fd;
}
示例#7
0
/*
 * comm_select
 *
 * Called to do the new-style IO, courtesy of of squid (like most of this
 * new IO code). This routine handles the stuff we've hidden in
 * comm_setselect and fd_table[] and calls callbacks for IO ready
 * events.
 */
void
comm_select(void)
{
  int num, ci;
  void (*hdl)(fde_t *, void *);
  fde_t *F;

  num = poll(pollfds, pollnum, SELECT_DELAY);

  set_time();

  if (num < 0)
  {
#ifdef HAVE_USLEEP
    usleep(50000);  /* avoid 99% CPU in comm_select */
#endif
    return;
  }

  for (ci = 0; ci < pollnum && num > 0; ci++)
  {
    int revents = pollfds[ci].revents;

    if (revents == 0)
      continue;
    num--;

    F = lookup_fd(pollfds[ci].fd);
    assert(F);
    if (!F->flags.open)
      continue;

    if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR))
    {
      if ((hdl = F->read_handler) != NULL)
      {
        F->read_handler = NULL;
        hdl(F, F->read_data);
        if (!F->flags.open)
          continue;
      }
    }

    if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR))
    {
      if ((hdl = F->write_handler) != NULL)
      {
        F->write_handler = NULL;
        hdl(F, F->write_data);
        if (!F->flags.open)
          continue;
      }
    }

    comm_setselect(F, 0, NULL, NULL, 0);
  }
}
示例#8
0
/*
 * comm_select
 *
 * Called to do the new-style IO, courtesy of squid (like most of this
 * new IO code). This routine handles the stuff we've hidden in
 * comm_setselect and fd_table[] and calls callbacks for IO ready
 * events.
 */
void
comm_select(void)
{
  struct timeval to;
  int num, fd;
  fde_t *F;
  PF *hdl;

  /* Copy over the read/write sets so we don't have to rebuild em */
  memcpy(&tmpreadfds, &select_readfds, sizeof(fd_set));
  memcpy(&tmpwritefds, &select_writefds, sizeof(fd_set));

  to.tv_sec = 0;
  to.tv_usec = SELECT_DELAY * 1000;
  num = select(highest_fd + 1, &tmpreadfds, &tmpwritefds, NULL, &to);

  set_time();

  if (num < 0)
  {
#ifdef HAVE_USLEEP
    usleep(50000);
#endif
    return;
  }

  for (fd = 0; fd <= highest_fd && num > 0; fd++)
    if (FD_ISSET(fd, &tmpreadfds) || FD_ISSET(fd, &tmpwritefds))
    {
      num--;

      F = lookup_fd(fd);
      if (F == NULL || !F->flags.open)
        continue;

      if (FD_ISSET(fd, &tmpreadfds))
        if ((hdl = F->read_handler) != NULL)
        {
          F->read_handler = NULL;
          hdl(F, F->read_data);
          if (!F->flags.open)
            continue;
        }

      if (FD_ISSET(fd, &tmpwritefds))
        if ((hdl = F->write_handler) != NULL)
        {
          F->write_handler = NULL;
          hdl(F, F->write_data);
          if (!F->flags.open)
            continue;
        }

      comm_setselect(F, 0, NULL, NULL, 0);
    }
}
示例#9
0
/* Close system call. */
static int
sys_close (int handle) 
{
  struct file_descriptor *fd = lookup_fd (handle);
  file_close (fd->file);
  dir_close (fd->dir);
  list_remove (&fd->elem);
  free (fd);
  return 0;
}
示例#10
0
/*
 * comm_select
 *
 * Called to do the new-style IO, courtesy of squid (like most of this
 * new IO code). This routine handles the stuff we've hidden in
 * comm_setselect and fd_table[] and calls callbacks for IO ready
 * events.
 */
void
comm_select(void)
{
  int num, i;
  struct pollfd pollfds[128];
  struct dvpoll dopoll;
  void (*hdl)(fde_t *, void *);
  fde_t *F;

  dopoll.dp_timeout = SELECT_DELAY;
  dopoll.dp_nfds = 128;
  dopoll.dp_fds = &pollfds[0];
  num = ioctl(dpfd.fd, DP_POLL, &dopoll);

  set_time();

  if (num < 0)
  {
#ifdef HAVE_USLEEP
    usleep(50000);  /* avoid 99% CPU in comm_select */
#endif
    return;
  }

  for (i = 0; i < num; i++)
  {
    F = lookup_fd(dopoll.dp_fds[i].fd);
    if (F == NULL || !F->flags.open)
      continue;

    if ((dopoll.dp_fds[i].revents & POLLIN))
    {
      if ((hdl = F->read_handler) != NULL)
      {
        F->read_handler = NULL;
        hdl(F, F->read_data);
        if (!F->flags.open)
          continue;
      }
    }

    if ((dopoll.dp_fds[i].revents & POLLOUT))
    {
      if ((hdl = F->write_handler) != NULL)
      {
        F->write_handler = NULL;
        hdl(F, F->write_data);
        if (!F->flags.open)
          continue;
      }
    }

    comm_setselect(F, 0, NULL, NULL, 0);
  }
}
示例#11
0
/* Close system call. */
static int
sys_close (int handle) 
{
  struct file_descriptor *fd = lookup_fd (handle);
  lock_acquire (&fs_lock);
  file_close (fd->file);
  lock_release (&fs_lock);
  list_remove (&fd->elem);
  free (fd);
  return 0;
}
示例#12
0
/* Filesize system call. */
static int
sys_filesize (int handle) 
{
  struct file_descriptor *fd = lookup_fd (handle);
  int size;
 
  lock_acquire (&fs_lock);
  size = file_length (fd->file);
  lock_release (&fs_lock);
 
  return size;
}
示例#13
0
/* Seek system call. */
static int
sys_seek (int handle, unsigned position) 
{
  struct file_descriptor *fd = lookup_fd (handle);
   
  lock_acquire (&fs_lock);
  if ((off_t) position >= 0)
    file_seek (fd->file, position);
  lock_release (&fs_lock);

  return 0;
}
示例#14
0
/* Tell system call. */
static int
sys_tell (int handle) 
{
  struct file_descriptor *fd = lookup_fd (handle);
  unsigned position;
   
  lock_acquire (&fs_lock);
  position = file_tell (fd->file);
  lock_release (&fs_lock);

  return position;
}
示例#15
0
/* Write system call. */
static int
sys_write (int handle, void *usrc_, unsigned size) 
{
  uint8_t *usrc = usrc_;
  struct file_descriptor *fd = NULL;
  int bytes_written = 0;

  /* Lookup up file descriptor. */
  if (handle != STDOUT_FILENO)
    fd = lookup_fd (handle);

  while (size > 0) 
    {
      /* How much bytes to write to this page? */
      size_t page_left = PGSIZE - pg_ofs (usrc);
      size_t write_amt = size < page_left ? size : page_left;
      off_t retval;

      /* Write from page into file. */
      if (!page_lock (usrc, false)) 
        thread_exit ();
      lock_acquire (&fs_lock);
      if (handle == STDOUT_FILENO)
        {
          putbuf ((char *) usrc, write_amt);
          retval = write_amt;
        }
      else
        retval = file_write (fd->file, usrc, write_amt);
      lock_release (&fs_lock);
      page_unlock (usrc);

      /* Handle return value. */
      if (retval < 0) 
        {
          if (bytes_written == 0)
            bytes_written = -1;
          break;
        }
      bytes_written += retval;

      /* If it was a short write we're done. */
      if (retval != (off_t) write_amt)
        break;

      /* Advance. */
      usrc += retval;
      size -= retval;
    }
 
  return bytes_written;
}
示例#16
0
/*
 * comm_select
 *
 * Called to do the new-style IO, courtesy of of squid (like most of this
 * new IO code). This routine handles the stuff we've hidden in
 * comm_setselect and fd_table[] and calls callbacks for IO ready
 * events.
 */
void
comm_select(void)
{
  int num, ci, revents;
  PF *hdl;
  fde_t *F;

  /* XXX kill that +1 later ! -- adrian */
  num = poll(pollfds, pollmax + 1, SELECT_DELAY);

  set_time();

  if (num < 0)
  {
#ifdef HAVE_USLEEP
    usleep(50000);  /* avoid 99% CPU in comm_select */
#endif
    return;
  }

  for (ci = 0; ci <= pollmax && num > 0; ci++)
  {
    if ((revents = pollfds[ci].revents) == 0 || pollfds[ci].fd == -1)
      continue;
    num--;

    F = lookup_fd(pollfds[ci].fd);
    if (F == NULL || !F->flags.open)
      continue;

    if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR))
      if ((hdl = F->read_handler) != NULL)
      {
        F->read_handler = NULL;
        hdl(F, F->read_data);
	if (!F->flags.open)
	  continue;
      }

    if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR))
      if ((hdl = F->write_handler) != NULL)
      {
        F->write_handler = NULL;
        hdl(F, F->write_data);
	if (!F->flags.open)
	  continue;
      }

    comm_setselect(F, 0, NULL, NULL, 0);
  }
}
示例#17
0
/*
 * comm_select()
 *
 * Called to do the new-style IO, courtesy of of squid (like most of this
 * new IO code). This routine handles the stuff we've hidden in
 * comm_setselect and fd_table[] and calls callbacks for IO ready
 * events.
 */
void
comm_select(void)
{
  struct epoll_event ep_fdlist[128];
  int num, i;
  void (*hdl)(fde_t *, void *);
  fde_t *F;

  num = epoll_wait(efd.fd, ep_fdlist, 128, SELECT_DELAY);

  set_time();

  if (num < 0)
  {
    const struct timespec req = { .tv_sec = 0, .tv_nsec = 50000000 };
    nanosleep(&req, NULL);  /* Avoid 99% CPU in comm_select */
    return;
  }

  for (i = 0; i < num; i++)
  {
    F = lookup_fd(ep_fdlist[i].data.fd);
    if (F == NULL || !F->flags.open)
      continue;

    if ((ep_fdlist[i].events & (EPOLLIN | EPOLLHUP | EPOLLERR)))
    {
      if ((hdl = F->read_handler))
      {
        F->read_handler = NULL;
        hdl(F, F->read_data);
        if (!F->flags.open)
          continue;
      }
    }

    if ((ep_fdlist[i].events & (EPOLLOUT | EPOLLHUP | EPOLLERR)))
    {
      if ((hdl = F->write_handler))
      {
        F->write_handler = NULL;
        hdl(F, F->write_data);
        if (!F->flags.open)
          continue;
      }
    }

    comm_setselect(F, 0, NULL, NULL, 0);
  }
}
示例#18
0
/*
 * comm_select()
 *
 * Called to do the new-style IO, courtesy of of squid (like most of this
 * new IO code). This routine handles the stuff we've hidden in
 * comm_setselect and fd_table[] and calls callbacks for IO ready
 * events.
 */
void
comm_select(void)
{
  struct epoll_event ep_fdlist[128];
  int num, i;
  PF *hdl;
  fde_t *F;

  num = epoll_wait(efd.fd, ep_fdlist, 128, SELECT_DELAY);

  set_time();

  if (num < 0)
  {
#ifdef HAVE_USLEEP
    usleep(50000);  /* avoid 99% CPU in comm_select */
#endif
    return;
  }

  for (i = 0; i < num; i++)
  {
    F = lookup_fd(ep_fdlist[i].data.fd);
    if (F == NULL || !F->flags.open)
      continue;

    if ((ep_fdlist[i].events & (EPOLLIN | EPOLLHUP | EPOLLERR)))
      if ((hdl = F->read_handler) != NULL)
      {
        F->read_handler = NULL;
        hdl(F, F->read_data);
	if (!F->flags.open)
	  continue;
      }

    if ((ep_fdlist[i].events & (EPOLLOUT | EPOLLHUP | EPOLLERR)))
      if ((hdl = F->write_handler) != NULL)
      {
        F->write_handler = NULL;
        hdl(F, F->write_data);
	if (!F->flags.open)
	  continue;
      }

    comm_setselect(F, 0, NULL, NULL, 0);
  }
}
示例#19
0
int storage_file_read(struct storage_msg *msg,
                      const void *r, size_t req_len)
{
    int rc;
    const struct storage_file_read_req *req = r;

    if (req_len != sizeof(*req)) {
        ALOGE("%s: invalid request length (%zd != %zd)\n",
              __func__, req_len, sizeof(*req));
        msg->result = STORAGE_ERR_NOT_VALID;
        goto err_response;
    }

    if (req->size > MAX_READ_SIZE) {
        ALOGW("%s: request is too large (%u > %d) - refusing\n",
              __func__, req->size, MAX_READ_SIZE);
        msg->result = STORAGE_ERR_NOT_VALID;
        goto err_response;
    }

    int fd = lookup_fd(req->handle, false);
    ssize_t read_res = read_with_retry(fd, read_rsp.hdr.data, req->size,
                                       (off_t)req->offset);
    if (read_res < 0) {
        rc = errno;
        ALOGW("%s: error reading file (fd=%d): %s\n",
              __func__, fd, strerror(errno));
        msg->result = translate_errno(rc);
        goto err_response;
    }

    msg->result = STORAGE_NO_ERROR;
    return ipc_respond(msg, &read_rsp, read_res + sizeof(read_rsp.hdr));

err_response:
    return ipc_respond(msg, NULL, 0);
}
示例#20
0
/*
 * Handler for Win32 messages.
 */
static LRESULT CALLBACK
hybrid_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  switch (uMsg)
  {
    case WM_SOCKET:
    {
      fde_t *F = lookup_fd((int) wParam);
      PF *hdl;

      if (F != NULL && F->flags.open)
        switch (WSAGETSELECTEVENT(lParam))
	{
	  case FD_ACCEPT:
	  case FD_CLOSE:
	  case FD_READ:
	    if ((hdl = F->read_handler) != NULL)
	    {
	      F->read_handler = NULL;
              hdl(F, F->read_data);
	      if (F->flags.open)
	        comm_setselect(F, 0, NULL, NULL, 0);
	    }
	    break;

          case FD_CONNECT:
          case FD_WRITE:
	    if ((hdl = F->write_handler) != NULL)
	    {
	      F->write_handler = NULL;
	      hdl(F, F->write_data);
	      if (F->flags.open)
	        comm_setselect(F, 0, NULL, NULL, 0);
	    }
        }

      return 0;
    }

    case WM_DNS:
    {
      dlink_node *ptr;

      DLINK_FOREACH(ptr, dns_queries.head)
        if (((struct DNSQuery *) ptr->data)->handle == wParam)
	{
	  struct DNSQuery *query = ptr->data;
	  struct DNSReply *reply = NULL;

          dlinkDelete(&query->node, &dns_queries);

          if (WSAGETASYNCERROR(lParam) == 0)
	  {
	    struct hostent *h = (struct hostent *) &query->reply;
	    static struct DNSReply _reply;
	    reply = &_reply;

            reply->h_name = h->h_name;
	    reply->addr.ss.ss_family = h->h_addrtype;

	    switch (h->h_addrtype)
	    {
	      case AF_INET:
	        memcpy(&((struct sockaddr_in *) &reply->addr)->sin_addr,
		       h->h_addr_list[0], h->h_length);
		break;

#ifdef IPV6
              case AF_INET6:
	        memcpy(&((struct sockaddr_in6 *) &reply->addr)->sin6_addr,
		       h->h_addr_list[0], h->h_length);
		break;
#endif

              default:  /* shouldn't happen */
	        reply = NULL;
	    }
	  }

          query->callback(query->ptr, reply);
	  return 0;
	}

      return 0;
    }

    case WM_REHASH:
      dorehash = 1;
      return 0;

    case WM_REMOTD:
      doremotd = 1;
      return 0;

    case WM_DESTROY:
      PostQuitMessage(0);
      return 0;

    default:
      return DefWindowProc(hwnd, uMsg, wParam, lParam);
  }
}
示例#21
0
/*
 * comm_setselect
 *
 * This is a needed exported function which will be called to register
 * and deregister interest in a pending IO state for a given FD.
 */
void
comm_setselect(fde_t *F, unsigned int type, void (*handler)(fde_t *, void *),
               void *client_data, time_t timeout)
{
  int new_events;

  if ((type & COMM_SELECT_READ))
  {
    F->read_handler = handler;
    F->read_data = client_data;
  }

  if ((type & COMM_SELECT_WRITE))
  {
    F->write_handler = handler;
    F->write_data = client_data;
  }

  new_events = (F->read_handler ? POLLRDNORM : 0) |
               (F->write_handler ? POLLWRNORM : 0);

  if (timeout != 0)
  {
    F->timeout = CurrentTime + (timeout / 1000);
    F->timeout_handler = handler;
    F->timeout_data = client_data;
  }

  if (new_events != F->evcache)
  {
    if (new_events == 0)
    {
      if (F->comm_index != pollnum - 1)
      {
        fde_t *other = lookup_fd(pollfds[pollnum - 1].fd);

        pollfds[F->comm_index].fd = pollfds[pollnum - 1].fd;
        pollfds[F->comm_index].events = pollfds[pollnum - 1].events;
        pollfds[F->comm_index].revents = pollfds[pollnum - 1].revents;

        assert(other);
        other->comm_index = F->comm_index;
      }

      F->comm_index = -1;
      --pollnum;
    }
    else
    {
      if (F->evcache == 0)
      {
        if (pollnum >= pollfds_size)
        {
          pollfds_size *= 2;
          pollfds = MyRealloc(pollfds, sizeof(struct pollfd) * pollfds_size);
        }

        F->comm_index = pollnum++;
        pollfds[F->comm_index].fd = F->fd;
      }

      pollfds[F->comm_index].events = new_events;
      pollfds[F->comm_index].revents = 0;
    }

    F->evcache = new_events;
  }
}
示例#22
0
/* Read system call. */
static int
sys_read (int handle, void *udst_, unsigned size) 
{
  uint8_t *udst = udst_;
  struct file_descriptor *fd;
  int bytes_read = 0;

  fd = lookup_fd (handle);
  while (size > 0) 
    {
      /* How much to read into this page? */
      size_t page_left = PGSIZE - pg_ofs (udst);
      size_t read_amt = size < page_left ? size : page_left;
      off_t retval;

      /* Read from file into page. */
      if (handle != STDIN_FILENO) 
        {
          if (!page_lock (udst, true)) 
            thread_exit (); 
          lock_acquire (&fs_lock);
          retval = file_read (fd->file, udst, read_amt);
          lock_release (&fs_lock);
          page_unlock (udst);
        }
      else 
        {
          size_t i;
          
          for (i = 0; i < read_amt; i++) 
            {
              char c = input_getc ();
              if (!page_lock (udst, true)) 
                thread_exit ();
              udst[i] = c;
              page_unlock (udst);
            }
          bytes_read = read_amt;
        }
      
      /* Check success. */
      if (retval < 0)
        {
          if (bytes_read == 0)
            bytes_read = -1; 
          break;
        }
      bytes_read += retval; 
      if (retval != (off_t) read_amt) 
        {
          /* Short read, so we're done. */
          break; 
        }

      /* Advance. */
      udst += retval;
      size -= retval;
    }
   
  return bytes_read;
}