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); }