static void datacopier_check_state(libxl__egc *egc, libxl__datacopier_state *dc) { STATE_AO_GC(dc->ao); int rc; if (dc->used && !dc->readbuf) { if (!libxl__ev_fd_isregistered(&dc->towrite)) { rc = libxl__ev_fd_register(gc, &dc->towrite, datacopier_writable, dc->writefd, POLLOUT); if (rc) { LOG(ERROR, "unable to establish write event on %s" " during copy of %s", dc->writewhat, dc->copywhat); datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO); return; } } } else if (!libxl__ev_fd_isregistered(&dc->toread) || dc->bytes_to_read == 0) { /* we have had eof */ datacopier_callback(egc, dc, 0, 0, 0); return; } else { /* nothing buffered, but still reading */ libxl__ev_fd_deregister(gc, &dc->towrite); } }
static void datacopier_abort(libxl__egc *egc, libxl__ao_abortable *abrt, int rc) { libxl__datacopier_state *dc = CONTAINER_OF(abrt, *dc, abrt); STATE_AO_GC(dc->ao); datacopier_callback(egc, dc, rc, -1, 0); }
static void datacopier_writable(libxl__egc *egc, libxl__ev_fd *ev, int fd, short events, short revents) { libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, towrite); STATE_AO_GC(dc->ao); if (datacopier_pollhup_handled(egc, dc, fd, revents, 1)) return; if (revents & ~POLLOUT) { LOG(ERROR, "unexpected poll event 0x%x on fd %d (should be POLLOUT)" " writing %s during copy of %s", revents, fd, dc->writewhat, dc->copywhat); datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO); return; } assert(revents & POLLOUT); for (;;) { libxl__datacopier_buf *buf = LIBXL_TAILQ_FIRST(&dc->bufs); if (!buf) break; if (!buf->used) { LIBXL_TAILQ_REMOVE(&dc->bufs, buf, entry); free(buf); continue; } int r = write(ev->fd, buf->buf, buf->used); if (r < 0) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK) break; assert(errno); LOGE(ERROR, "error writing to %s during copy of %s", dc->writewhat, dc->copywhat); datacopier_callback(egc, dc, ERROR_FAIL, 1, errno); return; } assert(r > 0); assert(r <= buf->used); buf->used -= r; dc->used -= r; assert(dc->used >= 0); memmove(buf->buf, buf->buf+r, buf->used); } datacopier_check_state(egc, dc); }
static void datacopier_readable(libxl__egc *egc, libxl__ev_fd *ev, int fd, short events, short revents) { libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, toread); STATE_AO_GC(dc->ao); if (datacopier_pollhup_handled(egc, dc, fd, revents, 0)) return; if (revents & ~(POLLIN|POLLHUP)) { LOG(ERROR, "unexpected poll event 0x%x on fd %d (expected POLLIN " "and/or POLLHUP) reading %s during copy of %s", revents, fd, dc->readwhat, dc->copywhat); datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO); return; } assert(revents & (POLLIN|POLLHUP)); for (;;) { libxl__datacopier_buf *buf = NULL; int r; if (dc->readbuf) { r = read(ev->fd, dc->readbuf + dc->used, dc->bytes_to_read); } else { while (dc->used >= dc->maxsz) { libxl__datacopier_buf *rm = LIBXL_TAILQ_FIRST(&dc->bufs); dc->used -= rm->used; assert(dc->used >= 0); LIBXL_TAILQ_REMOVE(&dc->bufs, rm, entry); free(rm); } buf = LIBXL_TAILQ_LAST(&dc->bufs, libxl__datacopier_bufs); if (!buf || buf->used >= sizeof(buf->buf)) { buf = libxl__malloc(NOGC, sizeof(*buf)); buf->used = 0; LIBXL_TAILQ_INSERT_TAIL(&dc->bufs, buf, entry); } r = read(ev->fd, buf->buf + buf->used, min_t(size_t, sizeof(buf->buf) - buf->used, (dc->bytes_to_read == -1) ? SIZE_MAX : dc->bytes_to_read)); } if (r < 0) { if (errno == EINTR) continue; assert(errno); if (errno == EWOULDBLOCK) { if (revents & POLLHUP) { LOG(ERROR, "poll reported HUP but fd read gave EWOULDBLOCK" " on %s during copy of %s", dc->readwhat, dc->copywhat); datacopier_callback(egc, dc, ERROR_FAIL, -1, 0); return; } break; } LOGE(ERROR, "error reading %s during copy of %s", dc->readwhat, dc->copywhat); datacopier_callback(egc, dc, ERROR_FAIL, 0, errno); return; } if (r == 0) { if (dc->callback_pollhup) { /* It might be that this "eof" is actually a HUP. If * the caller cares about the difference, * double-check using poll(2). */ struct pollfd hupchk; hupchk.fd = ev->fd; hupchk.events = POLLIN; hupchk.revents = 0; r = poll(&hupchk, 1, 0); if (r < 0) LIBXL__EVENT_DISASTER(egc, "unexpected failure polling fd for datacopier eof hup check", errno, 0); if (datacopier_pollhup_handled(egc, dc, fd, hupchk.revents, 0)) return; } libxl__ev_fd_deregister(gc, &dc->toread); break; } if (dc->log) { int wrote = fwrite(buf->buf + buf->used, 1, r, dc->log); if (wrote != r) { assert(ferror(dc->log)); assert(errno); LOGE(ERROR, "error logging %s", dc->copywhat); datacopier_callback(egc, dc, ERROR_FAIL, 0, errno); return; } } if (!dc->readbuf) { buf->used += r; assert(buf->used <= sizeof(buf->buf)); } dc->used += r; if (dc->bytes_to_read > 0) dc->bytes_to_read -= r; if (dc->bytes_to_read == 0) break; } datacopier_check_state(egc, dc); }
static void datacopier_readable(libxl__egc *egc, libxl__ev_fd *ev, int fd, short events, short revents) { libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, toread); STATE_AO_GC(dc->ao); if (datacopier_pollhup_handled(egc, dc, revents, 0)) return; if (revents & ~POLLIN) { LOG(ERROR, "unexpected poll event 0x%x (should be POLLIN)" " on %s during copy of %s", revents, dc->readwhat, dc->copywhat); datacopier_callback(egc, dc, -1, 0); return; } assert(revents & POLLIN); for (;;) { while (dc->used >= dc->maxsz) { libxl__datacopier_buf *rm = LIBXL_TAILQ_FIRST(&dc->bufs); dc->used -= rm->used; assert(dc->used >= 0); LIBXL_TAILQ_REMOVE(&dc->bufs, rm, entry); free(rm); } libxl__datacopier_buf *buf = LIBXL_TAILQ_LAST(&dc->bufs, libxl__datacopier_bufs); if (!buf || buf->used >= sizeof(buf->buf)) { buf = malloc(sizeof(*buf)); if (!buf) libxl__alloc_failed(CTX, __func__, 1, sizeof(*buf)); buf->used = 0; LIBXL_TAILQ_INSERT_TAIL(&dc->bufs, buf, entry); } int r = read(ev->fd, buf->buf + buf->used, sizeof(buf->buf) - buf->used); if (r < 0) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK) break; LOGE(ERROR, "error reading %s during copy of %s", dc->readwhat, dc->copywhat); datacopier_callback(egc, dc, 0, errno); return; } if (r == 0) { libxl__ev_fd_deregister(gc, &dc->toread); break; } if (dc->log) { int wrote = fwrite(buf->buf + buf->used, 1, r, dc->log); if (wrote != r) { assert(ferror(dc->log)); assert(errno); LOGE(ERROR, "error logging %s", dc->copywhat); datacopier_callback(egc, dc, 0, errno); return; } } buf->used += r; dc->used += r; assert(buf->used <= sizeof(buf->buf)); } datacopier_check_state(egc, dc); }