static int do_directtcp_accept( XferElementGlue *self, int *socketp) { int sock; g_assert(*socketp != -1); if ((sock = interruptible_accept(*socketp, NULL, NULL, prolong_accept, self)) == -1) { /* if the accept was interrupted due to a cancellation, then do not * add a further error message */ if (errno == 0 && XFER_ELEMENT(self)->cancelled) return -1; xfer_cancel_with_error(XFER_ELEMENT(self), _("Error accepting incoming connection: %s"), strerror(errno)); wait_until_xfer_cancelled(XFER_ELEMENT(self)->xfer); return -1; } /* close the listening socket now, for good measure */ close(*socketp); *socketp = -1; g_debug("do_directtcp_accept: %d", sock); return sock; }
static void use_device_impl( XferSourceRecovery *xdtself, Device *device) { XferSourceRecovery *self = XFER_SOURCE_RECOVERY(xdtself); g_assert(self->paused); /* short-circuit if nothing is changing */ if (self->device == device) return; if (self->device) g_object_unref(self->device); self->device = NULL; /* if we already have a connection, then make this device use it */ if (self->conn) { if (!device_use_connection(device, self->conn)) { /* queue up an error for later, and set device_bad. * start_part will see this and fail silently */ self->device_bad = TRUE; xfer_cancel_with_error(XFER_ELEMENT(self), _("Cannot continue onto new volume: %s"), device_error_or_status(device)); return; } } self->device = device; g_object_ref(device); }
static void pull_and_write(XferElementGlue *self) { XferElement *elt = XFER_ELEMENT(self); int fd = get_write_fd(self); XMsg *msg; size_t written; self->write_fdp = NULL; while (!elt->cancelled) { size_t len; char *buf; /* get a buffer from upstream */ buf = xfer_element_pull_buffer(elt->upstream, &len); if (!buf) break; /* write it */ if (!elt->downstream->drain_mode) { written = full_write(fd, buf, len); if (written < len) { if (elt->downstream->must_drain) { g_debug("Error writing to fd %d: %s", fd, strerror(errno)); } else if (elt->downstream->ignore_broken_pipe && errno == EPIPE) { } else { if (!elt->cancelled) { xfer_cancel_with_error(elt, _("Error writing to fd %d: %s"), fd, strerror(errno)); xfer_cancel(elt->xfer); wait_until_xfer_cancelled(elt->xfer); } amfree(buf); break; } elt->downstream->drain_mode = TRUE; } } crc32_add((uint8_t *)buf, len, &elt->crc); amfree(buf); } if (elt->cancelled && elt->expect_eof) xfer_element_drain_buffers(elt->upstream); g_debug("sending XMSG_CRC message %p", elt->downstream); g_debug("pull_and_write CRC: %08x size %lld", crc32_finish(&elt->crc), (long long)elt->crc.size); msg = xmsg_new(elt->downstream, XMSG_CRC, 0); msg->crc = crc32_finish(&elt->crc); msg->size = elt->crc.size; xfer_queue_message(elt->xfer, msg); /* close the fd we've been writing, as an EOF signal to downstream, and * set it to -1 to avoid accidental re-use */ close_write_fd(self); }
static void read_and_push( XferElementGlue *self) { XferElement *elt = XFER_ELEMENT(self); int fd = get_read_fd(self); XMsg *msg; crc32_init(&elt->crc); while (!elt->cancelled) { char *buf = g_malloc(GLUE_BUFFER_SIZE); gsize len; int read_error; /* read a buffer from upstream */ len = read_fully(fd, buf, GLUE_BUFFER_SIZE, &read_error); if (len < GLUE_BUFFER_SIZE) { if (read_error) { if (!elt->cancelled) { xfer_cancel_with_error(elt, _("Error reading from fd %d: %s"), fd, strerror(read_error)); g_debug("element-glue: error reading from fd %d: %s", fd, strerror(read_error)); wait_until_xfer_cancelled(elt->xfer); } amfree(buf); break; } else if (len == 0) { /* we only count a zero-length read as EOF */ amfree(buf); break; } } crc32_add((uint8_t *)buf, len, &elt->crc); xfer_element_push_buffer(elt->downstream, buf, len); } if (elt->cancelled && elt->expect_eof) xfer_element_drain_fd(fd); /* send an EOF indication downstream */ xfer_element_push_buffer(elt->downstream, NULL, 0); /* close the read fd, since it's at EOF */ close_read_fd(self); g_debug("sending XMSG_CRC message"); g_debug("read_and_push CRC: %08x size %lld", crc32_finish(&elt->crc), (long long)elt->crc.size); msg = xmsg_new(elt->upstream, XMSG_CRC, 0); msg->crc = crc32_finish(&elt->crc); msg->size = elt->crc.size; xfer_queue_message(elt->xfer, msg); }
static gboolean do_block( XferDestDevice *self, guint size, gpointer data) { XferElement *elt = XFER_ELEMENT(self); if (device_write_block(self->device, size, data) != WRITE_SUCCEED) { xfer_cancel_with_error(elt, "%s: %s", self->device->device_name, device_error_or_status(self->device)); wait_until_xfer_cancelled(elt->xfer); return FALSE; } /* check for LEOM */ if (self->cancel_at_leom && self->device->is_eom) { xfer_cancel_with_error(elt, "%s: LEOM detected", self->device->device_name); wait_until_xfer_cancelled(elt->xfer); return FALSE; } return TRUE; }
static gpointer directtcp_listen_thread( gpointer data) { XferSourceRecovery *self = XFER_SOURCE_RECOVERY(data); XferElement *elt = XFER_ELEMENT(self); int result; DBG(1, "(this is directtcp_listen_thread)"); /* we need to make an outgoing connection to downstream; we do this while * holding the start_part_mutex, so that a part doesn't get started until * we're finished with the device */ g_mutex_lock(self->start_part_mutex); if (elt->cancelled) { g_mutex_unlock(self->start_part_mutex); goto send_done; } g_assert(self->device != NULL); /* have a device */ g_assert(elt->downstream->input_listen_addrs != NULL); /* downstream listening */ DBG(2, "making DirectTCP connection on device %s", self->device->device_name); result = device_connect(self->device, FALSE, elt->downstream->input_listen_addrs, &self->conn, &elt->cancelled, self->start_part_mutex, self->abort_cond); if (result == 1 && !elt->cancelled) { xfer_cancel_with_error(elt, _("error making DirectTCP connection: %s"), device_error_or_status(self->device)); g_mutex_unlock(self->start_part_mutex); wait_until_xfer_cancelled(elt->xfer); goto send_done; } else if (result == 2 || elt->cancelled) { g_mutex_unlock(self->start_part_mutex); wait_until_xfer_cancelled(elt->xfer); goto send_done; } DBG(2, "DirectTCP connect succeeded"); return directtcp_common_thread(self); send_done: xfer_queue_message(elt->xfer, xmsg_new(elt, XMSG_DONE, 0)); return NULL; }
static void push_buffer_impl( XferElement *elt, gpointer buf, size_t len) { XferDestNull *self = (XferDestNull *)elt; if (buf) { crc32_add(buf, len, &elt->crc); } else { XMsg *msg = xmsg_new((XferElement *)self, XMSG_CRC, 0); msg->crc = crc32_finish(&elt->crc); msg->size = elt->crc.size; xfer_queue_message(XFER_ELEMENT(self)->xfer, msg); return; } if (self->do_verify && !elt->cancelled) { if (!simpleprng_verify_buffer(&self->prng, buf, len)) { xfer_cancel_with_error(elt, "verification of incoming bytestream failed; see stderr for details"), wait_until_xfer_cancelled(elt->xfer); amfree(buf); return; } } self->byte_position += len; if (!self->sent_info) { /* send a superfluous message (this is a testing XferElement, * after all) */ XMsg *msg = xmsg_new((XferElement *)self, XMSG_INFO, 0); msg->message = g_strdup("Is this thing on?"); xfer_queue_message(XFER_ELEMENT(self)->xfer, msg); self->sent_info = TRUE; } amfree(buf); }
static void push_buffer_impl( XferElement *elt, gpointer buf, size_t len) { XferDestBuffer *self = (XferDestBuffer *)elt; if (!buf) return; /* make sure this isn't too much data */ if (self->max_size && self->len + len > self->max_size) { xfer_cancel_with_error(elt, _("illegal attempt to transfer more than %zd bytes"), self->max_size); wait_until_xfer_cancelled(elt->xfer); amfree(buf); return; } /* expand the buffer if necessary */ if (self->len + len > self->allocated) { gsize new_size = self->allocated * 2; if (new_size < self->len+len) new_size = self->len+len; if (self->max_size && new_size > self->max_size) new_size = self->max_size; self->buf = g_realloc(self->buf, new_size); self->allocated = new_size; } g_memmove(((guint8 *)self->buf)+self->len, buf, len); self->len += len; amfree(buf); }
static gboolean setup_impl( XferElement *elt) { XferSourceRecovery *self = XFER_SOURCE_RECOVERY(elt); if (elt->output_mech == XFER_MECH_DIRECTTCP_CONNECT) { g_assert(self->device != NULL); DBG(2, "listening for DirectTCP connection on device %s", self->device->device_name); if (!device_listen(self->device, FALSE, &elt->output_listen_addrs)) { xfer_cancel_with_error(elt, _("error listening for DirectTCP connection: %s"), device_error_or_status(self->device)); return FALSE; } self->listen_ok = TRUE; } else { /* no output_listen_addrs for either XFER_MECH_DIRECTTCP_LISTEN or * XFER_MECH_PULL_BUFFER */ elt->output_listen_addrs = NULL; } return TRUE; }
static gboolean do_directtcp_listen( XferElement *elt, int *sockp, DirectTCPAddr **addrsp) { int sock; sockaddr_union data_addr; DirectTCPAddr *addrs; socklen_t len; struct addrinfo *res; struct addrinfo *res_addr; sockaddr_union *addr = NULL; if (resolve_hostname("localhost", 0, &res, NULL) != 0) { xfer_cancel_with_error(elt, "resolve_hostname(): %s", strerror(errno)); return FALSE; } for (res_addr = res; res_addr != NULL; res_addr = res_addr->ai_next) { if (res_addr->ai_family == AF_INET) { addr = (sockaddr_union *)res_addr->ai_addr; break; } } if (!addr) { addr = (sockaddr_union *)res->ai_addr; } sock = *sockp = socket(SU_GET_FAMILY(addr), SOCK_STREAM, 0); if (sock < 0) { xfer_cancel_with_error(elt, "socket(): %s", strerror(errno)); freeaddrinfo(res); return FALSE; } len = SS_LEN(addr); if (bind(sock, (struct sockaddr *)addr, len) != 0) { xfer_cancel_with_error(elt, "bind(): %s", strerror(errno)); freeaddrinfo(res); close(sock); *sockp = -1; return FALSE; } if (listen(sock, 1) < 0) { xfer_cancel_with_error(elt, "listen(): %s", strerror(errno)); freeaddrinfo(res); close(sock); *sockp = -1; return FALSE; } /* TODO: which addresses should this display? all ifaces? localhost? */ len = sizeof(data_addr); if (getsockname(sock, (struct sockaddr *)&data_addr, &len) < 0) error("getsockname(): %s", strerror(errno)); addrs = g_new0(DirectTCPAddr, 2); copy_sockaddr(&addrs[0], &data_addr); *addrsp = addrs; freeaddrinfo(res); return TRUE; }
static gboolean setup_impl( XferElement *elt) { XferElementGlue *self = (XferElementGlue *)elt; gboolean need_ring = FALSE; gboolean need_listen_input = FALSE; gboolean need_listen_output = FALSE; g_assert(elt->input_mech != XFER_MECH_NONE); g_assert(elt->output_mech != XFER_MECH_NONE); g_assert(elt->input_mech != elt->output_mech); self->read_fdp = NULL; self->write_fdp = NULL; self->on_push = PUSH_INVALID; self->on_pull = PULL_INVALID; self->need_thread = FALSE; switch (mech_pair(elt->input_mech, elt->output_mech)) { case mech_pair(XFER_MECH_READFD, XFER_MECH_WRITEFD): /* thread will read from one fd and write to the other */ self->read_fdp = &neighboring_element_fd; self->write_fdp = &neighboring_element_fd; self->need_thread = TRUE; break; case mech_pair(XFER_MECH_READFD, XFER_MECH_PUSH_BUFFER): /* thread will read from one fd and call push_buffer downstream */ self->read_fdp = &neighboring_element_fd; self->need_thread = TRUE; break; case mech_pair(XFER_MECH_READFD, XFER_MECH_PULL_BUFFER): self->read_fdp = &neighboring_element_fd; self->on_pull = PULL_FROM_FD; break; case mech_pair(XFER_MECH_READFD, XFER_MECH_DIRECTTCP_LISTEN): /* thread will connect for output, then read from fd and write to the * socket. */ self->read_fdp = &neighboring_element_fd; self->need_thread = TRUE; break; case mech_pair(XFER_MECH_READFD, XFER_MECH_DIRECTTCP_CONNECT): /* thread will accept output conn, then read from upstream and write to socket */ self->read_fdp = &neighboring_element_fd; self->need_thread = TRUE; need_listen_output = TRUE; break; case mech_pair(XFER_MECH_WRITEFD, XFER_MECH_READFD): make_pipe(self); g_assert(xfer_element_swap_input_fd(elt, self->pipe[1]) == -1); self->pipe[1] = -1; /* upstream will close this for us */ g_assert(xfer_element_swap_output_fd(elt, self->pipe[0]) == -1); self->pipe[0] = -1; /* downstream will close this for us */ break; case mech_pair(XFER_MECH_WRITEFD, XFER_MECH_PUSH_BUFFER): /* thread will read from pipe and call downstream's push_buffer */ make_pipe(self); g_assert(xfer_element_swap_input_fd(elt, self->pipe[1]) == -1); self->pipe[1] = -1; /* upstream will close this for us */ self->read_fdp = &self->pipe[0]; self->need_thread = TRUE; break; case mech_pair(XFER_MECH_WRITEFD, XFER_MECH_PULL_BUFFER): make_pipe(self); g_assert(xfer_element_swap_input_fd(elt, self->pipe[1]) == -1); self->pipe[1] = -1; /* upstream will close this for us */ self->on_pull = PULL_FROM_FD; self->read_fdp = &self->pipe[0]; break; case mech_pair(XFER_MECH_WRITEFD, XFER_MECH_DIRECTTCP_LISTEN): /* thread will connect for output, then read from pipe and write to socket */ make_pipe(self); g_assert(xfer_element_swap_input_fd(elt, self->pipe[1]) == -1); self->pipe[1] = -1; /* upstream will close this for us */ self->read_fdp = &self->pipe[0]; self->need_thread = TRUE; break; case mech_pair(XFER_MECH_WRITEFD, XFER_MECH_DIRECTTCP_CONNECT): /* thread will accept output conn, then read from pipe and write to socket */ make_pipe(self); g_assert(xfer_element_swap_input_fd(elt, self->pipe[1]) == -1); self->pipe[1] = -1; /* upstream will close this for us */ self->read_fdp = &self->pipe[0]; self->need_thread = TRUE; need_listen_output = TRUE; break; case mech_pair(XFER_MECH_PUSH_BUFFER, XFER_MECH_READFD): make_pipe(self); g_assert(xfer_element_swap_output_fd(elt, self->pipe[0]) == -1); self->pipe[0] = -1; /* downstream will close this for us */ self->on_push = PUSH_TO_FD; self->write_fdp = &self->pipe[1]; break; case mech_pair(XFER_MECH_PUSH_BUFFER, XFER_MECH_WRITEFD): self->on_push = PUSH_TO_FD; self->write_fdp = &neighboring_element_fd; break; case mech_pair(XFER_MECH_PUSH_BUFFER, XFER_MECH_PULL_BUFFER): self->on_push = PUSH_TO_RING_BUFFER; self->on_pull = PULL_FROM_RING_BUFFER; need_ring = TRUE; break; case mech_pair(XFER_MECH_PUSH_BUFFER, XFER_MECH_DIRECTTCP_LISTEN): /* push will connect for output first */ self->on_push = PUSH_TO_FD | PUSH_CONNECT_FIRST; break; case mech_pair(XFER_MECH_PUSH_BUFFER, XFER_MECH_DIRECTTCP_CONNECT): /* push will accept for output first */ self->on_push = PUSH_TO_FD | PUSH_ACCEPT_FIRST; need_listen_output = TRUE; break; case mech_pair(XFER_MECH_PULL_BUFFER, XFER_MECH_READFD): /* thread will pull from upstream and write to pipe */ make_pipe(self); g_assert(xfer_element_swap_output_fd(elt, self->pipe[0]) == -1); self->pipe[0] = -1; /* downstream will close this for us */ self->write_fdp = &self->pipe[1]; self->need_thread = TRUE; break; case mech_pair(XFER_MECH_PULL_BUFFER, XFER_MECH_WRITEFD): /* thread will pull from upstream and write to downstream */ self->write_fdp = &neighboring_element_fd; self->need_thread = TRUE; break; case mech_pair(XFER_MECH_PULL_BUFFER, XFER_MECH_PUSH_BUFFER): /* thread will pull from upstream and push to downstream */ self->need_thread = TRUE; break; case mech_pair(XFER_MECH_PULL_BUFFER, XFER_MECH_DIRECTTCP_LISTEN): /* thread will connect for output, then pull from upstream and write to socket */ self->need_thread = TRUE; break; case mech_pair(XFER_MECH_PULL_BUFFER, XFER_MECH_DIRECTTCP_CONNECT): /* thread will accept for output, then pull from upstream and write to socket */ self->need_thread = TRUE; need_listen_output = TRUE; break; case mech_pair(XFER_MECH_DIRECTTCP_LISTEN, XFER_MECH_READFD): /* thread will accept for input, then read from socket and write to pipe */ make_pipe(self); g_assert(xfer_element_swap_output_fd(elt, self->pipe[0]) == -1); self->pipe[0] = -1; /* downstream will close this for us */ self->write_fdp = &self->pipe[1]; self->need_thread = TRUE; need_listen_input = TRUE; break; case mech_pair(XFER_MECH_DIRECTTCP_LISTEN, XFER_MECH_WRITEFD): /* thread will accept for input, then read from socket and write to downstream */ self->write_fdp = &neighboring_element_fd; self->need_thread = TRUE; need_listen_input = TRUE; break; case mech_pair(XFER_MECH_DIRECTTCP_LISTEN, XFER_MECH_PUSH_BUFFER): /* thread will accept for input, then read from socket and push downstream */ self->need_thread = TRUE; need_listen_input = TRUE; break; case mech_pair(XFER_MECH_DIRECTTCP_LISTEN, XFER_MECH_PULL_BUFFER): /* first pull will accept for input, then read from socket */ self->on_pull = PULL_FROM_FD | PULL_ACCEPT_FIRST; need_listen_input = TRUE; break; case mech_pair(XFER_MECH_DIRECTTCP_LISTEN, XFER_MECH_DIRECTTCP_CONNECT): /* thread will accept on both sides, then copy from socket to socket */ self->need_thread = TRUE; need_listen_input = TRUE; need_listen_output = TRUE; break; case mech_pair(XFER_MECH_DIRECTTCP_CONNECT, XFER_MECH_READFD): /* thread will connect for input, then read from socket and write to pipe */ make_pipe(self); g_assert(xfer_element_swap_output_fd(elt, self->pipe[0]) == -1); self->pipe[0] = -1; /* downstream will close this for us */ self->write_fdp = &self->pipe[1]; self->need_thread = TRUE; break; case mech_pair(XFER_MECH_DIRECTTCP_CONNECT, XFER_MECH_WRITEFD): /* thread will connect for input, then read from socket and write to downstream */ self->write_fdp = &neighboring_element_fd; self->need_thread = TRUE; break; case mech_pair(XFER_MECH_DIRECTTCP_CONNECT, XFER_MECH_PUSH_BUFFER): /* thread will connect for input, then read from socket and push downstream */ self->need_thread = TRUE; break; case mech_pair(XFER_MECH_DIRECTTCP_CONNECT, XFER_MECH_PULL_BUFFER): /* first pull will connect for input, then read from socket */ self->on_pull = PULL_FROM_FD | PULL_CONNECT_FIRST; break; case mech_pair(XFER_MECH_DIRECTTCP_CONNECT, XFER_MECH_DIRECTTCP_LISTEN): /* thread will connect on both sides, then copy from socket to socket */ self->on_pull = PULL_FROM_FD | PULL_ACCEPT_FIRST; self->need_thread = TRUE; break; default: g_assert_not_reached(); break; } /* set up ring if desired */ if (need_ring) { self->ring = g_try_malloc(sizeof(*self->ring) * GLUE_RING_BUFFER_SIZE); if (self->ring == NULL) { xfer_cancel_with_error(elt, "Can't allocate memory for ring"); return FALSE; } self->ring_used_sem = amsemaphore_new_with_value(0); self->ring_free_sem = amsemaphore_new_with_value(GLUE_RING_BUFFER_SIZE); } if (need_listen_input) { if (!do_directtcp_listen(elt, &self->input_listen_socket, &elt->input_listen_addrs)) return FALSE; } if (need_listen_output) { if (!do_directtcp_listen(elt, &self->output_listen_socket, &elt->output_listen_addrs)) return FALSE; } return TRUE; }
static void read_and_write(XferElementGlue *self) { XferElement *elt = XFER_ELEMENT(self); /* dynamically allocate a buffer, in case this thread has * a limited amount of stack allocated */ char *buf = g_malloc(GLUE_BUFFER_SIZE); int rfd = get_read_fd(self); int wfd = get_write_fd(self); XMsg *msg; crc32_init(&elt->crc); g_debug("read_and_write: read from %d, write to %d", rfd, wfd); while (!elt->cancelled) { size_t len; /* read from upstream */ len = read_fully(rfd, buf, GLUE_BUFFER_SIZE, NULL); if (len < GLUE_BUFFER_SIZE) { if (errno) { if (!elt->cancelled) { xfer_cancel_with_error(elt, _("Error reading from fd %d: %s"), rfd, strerror(errno)); wait_until_xfer_cancelled(elt->xfer); } break; } else if (len == 0) { /* we only count a zero-length read as EOF */ break; } } /* write the buffer fully */ if (!elt->downstream->drain_mode && full_write(wfd, buf, len) < len) { if (elt->downstream->must_drain) { g_debug("Could not write to fd %d: %s", wfd, strerror(errno)); } else if (elt->downstream->ignore_broken_pipe && errno == EPIPE) { } else { if (!elt->cancelled) { xfer_cancel_with_error(elt, _("Could not write to fd %d: %s"), wfd, strerror(errno)); wait_until_xfer_cancelled(elt->xfer); } break; } } crc32_add((uint8_t *)buf, len, &elt->crc); } if (elt->cancelled && elt->expect_eof) xfer_element_drain_fd(rfd); /* close the read fd. If it's not at EOF, then upstream will get EPIPE, which will hopefully * kill it and complete the cancellation */ close_read_fd(self); /* close the fd we've been writing, as an EOF signal to downstream */ close_write_fd(self); g_debug("read_and_write upstream CRC: %08x size %lld", crc32_finish(&elt->crc), (long long)elt->crc.size); g_debug("sending XMSG_CRC message"); msg = xmsg_new(elt->upstream, XMSG_CRC, 0); msg->crc = crc32_finish(&elt->crc); msg->size = elt->crc.size; xfer_queue_message(elt->xfer, msg); g_debug("read_and_write downstream CRC: %08x size %lld", crc32_finish(&elt->crc), (long long)elt->crc.size); g_debug("sending XMSG_CRC message"); msg = xmsg_new(elt->downstream, XMSG_CRC, 0); msg->crc = crc32_finish(&elt->crc); msg->size = elt->crc.size; xfer_queue_message(elt->xfer, msg); amfree(buf); }
static gpointer pull_buffer_impl( XferElement *elt, size_t *size) { XferSourceRecovery *self = XFER_SOURCE_RECOVERY(elt); gpointer buf = NULL; int result; int devsize; XMsg *msg; g_assert(elt->output_mech == XFER_MECH_PULL_BUFFER); g_mutex_lock(self->start_part_mutex); if (elt->size == 0) { if (elt->offset == 0 && elt->orig_size == 0) { self->paused = TRUE; } else { DBG(2, "xfer-source-recovery sending XMSG_CRC message"); DBG(2, "xfer-source-recovery CRC: %08x size %lld", crc32_finish(&elt->crc), (long long)elt->crc.size); msg = xmsg_new(XFER_ELEMENT(self), XMSG_CRC, 0); msg->crc = crc32_finish(&elt->crc); msg->size = elt->crc.size; xfer_queue_message(elt->xfer, msg); /* the device has signalled EOF (really end-of-part), so clean up instance * variables and report the EOP to the caller in the form of an xmsg */ DBG(2, "pull_buffer hit EOF; sending XMSG_SEGMENT_DONE"); msg = xmsg_new(XFER_ELEMENT(self), XMSG_SEGMENT_DONE, 0); msg->size = self->part_size; if (self->part_timer) { msg->duration = g_timer_elapsed(self->part_timer, NULL); g_timer_destroy(self->part_timer); self->part_timer = NULL; } msg->partnum = 0; msg->fileno = self->device->file; msg->successful = TRUE; msg->eof = FALSE; self->paused = TRUE; device_clear_bytes_read(self->device); self->bytes_read += self->part_size; self->part_size = 0; self->block_size = 0; /* don't queue the XMSG_PART_DONE until we've adjusted all of our * instance variables appropriately */ xfer_queue_message(elt->xfer, msg); if (self->device->is_eof) { DBG(2, "pull_buffer hit EOF; sending XMSG_PART_DONE"); msg = xmsg_new(XFER_ELEMENT(self), XMSG_PART_DONE, 0); msg->size = self->part_size; if (self->part_timer) { msg->duration = g_timer_elapsed(self->part_timer, NULL); g_timer_destroy(self->part_timer); self->part_timer = NULL; } msg->partnum = 0; msg->fileno = self->device->file; msg->successful = TRUE; msg->eof = FALSE; xfer_queue_message(elt->xfer, msg); } } } while (1) { /* make sure we have a device */ while (self->paused && !elt->cancelled) g_cond_wait(self->start_part_cond, self->start_part_mutex); /* indicate EOF on an cancel or when there are no more parts */ if (elt->cancelled) { goto error; } if (self->done) goto error; /* start the timer if this is the first pull_buffer of this part */ if (!self->part_timer) { DBG(2, "first pull_buffer of new part"); self->part_timer = g_timer_new(); } if (elt->size == 0) { result = -1; } else { /* loop until we read a full block, in case the blocks are larger * than expected */ if (self->block_size == 0) self->block_size = (size_t)self->device->block_size; do { int max_block; buf = g_malloc(self->block_size); if (buf == NULL) { xfer_cancel_with_error(elt, _("%s: cannot allocate memory"), self->device->device_name); g_mutex_unlock(self->start_part_mutex); wait_until_xfer_cancelled(elt->xfer); goto error_unlocked; } devsize = (int)self->block_size; if (elt->size < 0) max_block = -1; else max_block = (elt->size+self->block_size-1)/self->block_size; result = device_read_block(self->device, buf, &devsize, max_block); *size = devsize; if (result == 0) { g_assert(*size > self->block_size); self->block_size = devsize; amfree(buf); } } while (result == 0); if (result > 0 && (elt->offset || (elt->size > 0 && (long long unsigned)elt->size < *size))) { gpointer buf1 = g_malloc(self->block_size); if ((long long unsigned)elt->offset > *size) { g_debug("offset > *size"); } else if ((long long unsigned)elt->offset == *size) { g_debug("offset == *size"); } *size -= elt->offset; if (elt->size > 0 && (size_t)elt->size < *size) *size = elt->size; memmove(buf1, buf + elt->offset, *size); elt->offset = 0; g_free(buf); buf = buf1; } if (result > 0) elt->size -= *size; } /* if this block was successful, return it */ if (result > 0) { self->part_size += *size; break; } if (result < 0) { amfree(buf); /* if we're not at EOF, it's an error */ if (!self->device->is_eof && elt->size != 0) { xfer_cancel_with_error(elt, _("error reading from %s: %s"), self->device->device_name, device_error_or_status(self->device)); g_mutex_unlock(self->start_part_mutex); wait_until_xfer_cancelled(elt->xfer); goto error_unlocked; } DBG(2, "xfer-source-recovery sending XMSG_CRC message"); DBG(2, "xfer-source-recovery CRC: %08x size %lld", crc32_finish(&elt->crc), (long long)elt->crc.size); msg = xmsg_new(XFER_ELEMENT(self), XMSG_CRC, 0); msg->crc = crc32_finish(&elt->crc); msg->size = elt->crc.size; xfer_queue_message(elt->xfer, msg); /* the device has signalled EOF (really end-of-part), so clean up instance * variables and report the EOP to the caller in the form of an xmsg */ DBG(2, "pull_buffer hit EOF; sending XMSG_PART_DONE"); msg = xmsg_new(XFER_ELEMENT(self), XMSG_PART_DONE, 0); msg->size = self->part_size; msg->duration = g_timer_elapsed(self->part_timer, NULL); msg->partnum = 0; msg->fileno = self->device->file; msg->successful = TRUE; msg->eof = FALSE; self->paused = TRUE; self->bytes_read += self->part_size; device_clear_bytes_read(self->device); self->part_size = 0; self->block_size = 0; if (self->part_timer) { g_timer_destroy(self->part_timer); self->part_timer = NULL; } /* don't queue the XMSG_PART_DONE until we've adjusted all of our * instance variables appropriately */ xfer_queue_message(elt->xfer, msg); if (elt->size == 0) { g_mutex_unlock(self->start_part_mutex); return NULL; } } } g_mutex_unlock(self->start_part_mutex); if (buf) { crc32_add(buf, *size, &elt->crc); } return buf; error: g_mutex_unlock(self->start_part_mutex); error_unlocked: *size = 0; return NULL; }
static int do_directtcp_connect( XferElementGlue *self, DirectTCPAddr *addrs) { XferElement *elt = XFER_ELEMENT(self); sockaddr_union addr; int sock; #ifdef WORKING_IPV6 char strsockaddr[INET6_ADDRSTRLEN + 20]; #else char strsockaddr[INET_ADDRSTRLEN + 20]; #endif if (!addrs) { g_debug("element-glue got no directtcp addresses to connect to!"); if (!elt->cancelled) { xfer_cancel_with_error(elt, "%s got no directtcp addresses to connect to", xfer_element_repr(elt)); } goto cancel_wait; } /* set up the sockaddr -- IPv4 only */ copy_sockaddr(&addr, addrs); str_sockaddr_r(&addr, strsockaddr, sizeof(strsockaddr)); if (strncmp(strsockaddr,"255.255.255.255:", 16) == 0) { char buffer[32770]; char *s; int size; char *data_host; int data_port; g_debug("do_directtcp_connect making indirect data connection to %s", strsockaddr); data_port = SU_GET_PORT(&addr); sock = stream_client(NULL, "localhost", data_port, STREAM_BUFSIZE, 0, NULL, 0); if (sock < 0) { xfer_cancel_with_error(elt, "stream_client(): %s", strerror(errno)); goto cancel_wait; } size = full_read(sock, buffer, 32768); if (size < 0 ) { xfer_cancel_with_error(elt, "failed to read from indirecttcp: %s", strerror(errno)); goto cancel_wait; } close(sock); buffer[size++] = ' '; buffer[size] = '\0'; if ((s = strchr(buffer, ':')) == NULL) { xfer_cancel_with_error(elt, "Failed to parse indirect data stream: %s", buffer); goto cancel_wait; } *s++ = '\0'; data_host = buffer; data_port = atoi(s); str_to_sockaddr(data_host, &addr); SU_SET_PORT(&addr, data_port); str_sockaddr_r(&addr, strsockaddr, sizeof(strsockaddr)); } sock = socket(SU_GET_FAMILY(&addr), SOCK_STREAM, 0); g_debug("do_directtcp_connect making data connection to %s", strsockaddr); if (sock < 0) { xfer_cancel_with_error(elt, "socket(): %s", strerror(errno)); goto cancel_wait; } if (connect(sock, (struct sockaddr *)&addr, SS_LEN(&addr)) < 0) { xfer_cancel_with_error(elt, "connect(): %s", strerror(errno)); close(sock); goto cancel_wait; } g_debug("do_directtcp_connect: connected to %s, fd %d", strsockaddr, sock); return sock; cancel_wait: wait_until_xfer_cancelled(elt->xfer); return -1; }
static gpointer pull_buffer_impl( XferElement *elt, size_t *size) { XferSourceDevice *self = (XferSourceDevice *)elt; gpointer buf = NULL; int result; int devsize; int max_block; /* indicate EOF on an cancel */ if (elt->cancelled) { *size = 0; return NULL; } /* get the device block size */ if (self->block_size == 0) { self->block_size = self->device->block_size; } do { buf = g_try_malloc(self->block_size); if (buf == NULL) { xfer_cancel_with_error(elt, _("%s: cannot allocate memory"), self->device->device_name); wait_until_xfer_cancelled(elt->xfer); return NULL; } devsize = (int)self->block_size; if (elt->size < 0) max_block = -1; else max_block = (elt->size+self->block_size-1)/self->block_size; result = device_read_block(self->device, buf, &devsize, max_block); *size = devsize; /* if the buffer was too small, loop around again */ if (result == 0) { g_assert(*size > self->block_size); self->block_size = devsize; amfree(buf); } } while (result == 0); if (result < 0) { amfree(buf); /* if we're not at EOF, it's an error */ if (!self->device->is_eof) { xfer_cancel_with_error(elt, _("error reading from %s: %s"), self->device->device_name, device_error_or_status(self->device)); wait_until_xfer_cancelled(elt->xfer); } *size = 0; return NULL; } return buf; }
static void push_buffer_impl( XferElement *elt, gpointer buf, size_t len) { XferDestDevice *self = XFER_DEST_DEVICE(elt); gpointer to_free = buf; /* Handle EOF */ if (!buf) { /* write out the partial buffer, if there's anything in it */ if (self->partial_length) { if (!do_block(self, self->block_size, self->partial)) { return; } self->partial_length = 0; } device_finish_file(self->device); return; } /* set up the block buffer, now that we can depend on having a blocksize * from the device */ if (!self->partial) { self->partial = g_try_malloc(self->device->block_size); if (self->partial == NULL) { xfer_cancel_with_error(elt, "%s: Cannot allocate memory", self->device->device_name); wait_until_xfer_cancelled(elt->xfer); return; } self->block_size = self->device->block_size; self->partial_length = 0; } /* if we already have data in the buffer, add the new data to it */ if (self->partial_length != 0) { gsize to_copy = min(self->block_size - self->partial_length, len); memmove((char *)self->partial + self->partial_length, buf, to_copy); buf = (gpointer)(to_copy + (char *)buf); len -= to_copy; self->partial_length += to_copy; } /* and if the buffer is now full, write the block */ if (self->partial_length == self->block_size) { if (!do_block(self, self->block_size, self->partial)) { g_free(to_free); return; } self->partial_length = 0; } /* write any whole blocks directly from the push buffer */ while (len >= self->block_size) { if (!do_block(self, self->block_size, buf)) { g_free(to_free); return; } buf = (gpointer)(self->block_size + (char *)buf); len -= self->block_size; } /* and finally store any leftover data in the partial buffer */ if (len) { memmove(self->partial, buf, len); self->partial_length = len; } g_free(to_free); }
static gpointer pull_buffer_impl( XferElement *elt, size_t *size) { XferElementGlue *self = XFER_ELEMENT_GLUE(elt); /* accept first, if required */ if (self->on_pull & PULL_ACCEPT_FIRST) { /* don't accept the next time around */ self->on_pull &= ~PULL_ACCEPT_FIRST; if (elt->cancelled) { *size = 0; return NULL; } if ((self->input_data_socket = do_directtcp_accept(self, &self->input_listen_socket)) == -1) { /* do_directtcp_accept already signalled an error; xfer * is cancelled */ *size = 0; return NULL; } /* read from this new socket */ self->read_fdp = &self->input_data_socket; } else if (self->on_pull & PULL_CONNECT_FIRST) { /* or connect first, if required */ /* don't connect the next time around */ self->on_pull &= ~PULL_CONNECT_FIRST; if (elt->cancelled) { *size = 0; return NULL; } if ((self->input_data_socket = do_directtcp_connect(self, elt->upstream->output_listen_addrs)) == -1) { /* do_directtcp_connect already signalled an error; xfer * is cancelled */ *size = 0; return NULL; } /* read from this new socket */ self->read_fdp = &self->input_data_socket; } switch (self->on_pull) { case PULL_FROM_RING_BUFFER: { gpointer buf; if (elt->cancelled) { /* the finalize method will empty the ring buffer */ *size = 0; return NULL; } /* make sure there's at least one element available */ amsemaphore_down(self->ring_used_sem); /* get it */ buf = self->ring[self->ring_tail].buf; *size = self->ring[self->ring_tail].size; self->ring_tail = (self->ring_tail + 1) % GLUE_RING_BUFFER_SIZE; /* and mark this element as free to be overwritten */ amsemaphore_up(self->ring_free_sem); return buf; } case PULL_FROM_FD: { int fd = get_read_fd(self); char *buf; ssize_t len; /* if the fd is already closed, it's possible upstream bailed out * so quickly that we didn't even get a look at the fd */ if (elt->cancelled || fd == -1) { if (fd != -1) { if (elt->expect_eof) xfer_element_drain_fd(fd); close_read_fd(self); } *size = 0; return NULL; } buf = g_malloc(GLUE_BUFFER_SIZE); /* read from upstream */ len = read_fully(fd, buf, GLUE_BUFFER_SIZE, NULL); if (len < GLUE_BUFFER_SIZE) { if (errno) { if (!elt->cancelled) { xfer_cancel_with_error(elt, _("Error reading from fd %d: %s"), fd, strerror(errno)); wait_until_xfer_cancelled(elt->xfer); } /* return an EOF */ amfree(buf); len = 0; /* and finish off the upstream */ if (elt->expect_eof) { xfer_element_drain_fd(fd); } close_read_fd(self); } else if (len == 0) { /* EOF */ g_free(buf); buf = NULL; *size = 0; /* signal EOF to downstream */ close_read_fd(self); } } *size = (size_t)len; return buf; } default: case PULL_INVALID: g_assert_not_reached(); return NULL; } }
static void push_buffer_impl( XferElement *elt, gpointer buf, size_t len) { XferElementGlue *self = (XferElementGlue *)elt; XMsg *msg; /* accept first, if required */ if (self->on_push & PUSH_ACCEPT_FIRST) { /* don't accept the next time around */ self->on_push &= ~PUSH_ACCEPT_FIRST; if (elt->cancelled) { return; } if ((self->output_data_socket = do_directtcp_accept(self, &self->output_listen_socket)) == -1) { /* do_directtcp_accept already signalled an error; xfer * is cancelled */ return; } /* write to this new socket */ self->write_fdp = &self->output_data_socket; } /* or connect first, if required */ if (self->on_push & PUSH_CONNECT_FIRST) { /* don't accept the next time around */ self->on_push &= ~PUSH_CONNECT_FIRST; if (elt->cancelled) { return; } if ((self->output_data_socket = do_directtcp_connect(self, elt->downstream->input_listen_addrs)) == -1) { /* do_directtcp_connect already signalled an error; xfer * is cancelled */ return; } /* read from this new socket */ self->write_fdp = &self->output_data_socket; } switch (self->on_push) { case PUSH_TO_RING_BUFFER: /* just drop packets if the transfer has been cancelled */ if (elt->cancelled) { amfree(buf); return; } /* make sure there's at least one element free */ amsemaphore_down(self->ring_free_sem); /* set it */ self->ring[self->ring_head].buf = buf; self->ring[self->ring_head].size = len; self->ring_head = (self->ring_head + 1) % GLUE_RING_BUFFER_SIZE; /* and mark this element as available for reading */ amsemaphore_up(self->ring_used_sem); return; case PUSH_TO_FD: { int fd = get_write_fd(self); /* if the fd is already closed, it's possible upstream bailed out * so quickly that we didn't even get a look at the fd. In this * case we can assume the xfer has been cancelled and just discard * the data. */ if (fd == -1) return; if (elt->cancelled) { if (!elt->expect_eof || !buf) { close_write_fd(self); /* hack to ensure we won't close the fd again, if we get another push */ elt->expect_eof = TRUE; } amfree(buf); return; } /* write the full buffer to the fd, or close on EOF */ if (buf) { if (!elt->downstream->drain_mode && full_write(fd, buf, len) < len) { if (elt->downstream->must_drain) { g_debug("Error writing to fd %d: %s", fd, strerror(errno)); } else if (elt->downstream->ignore_broken_pipe && errno == EPIPE) { } else { if (!elt->cancelled) { xfer_cancel_with_error(elt, _("Error writing to fd %d: %s"), fd, strerror(errno)); wait_until_xfer_cancelled(elt->xfer); } /* nothing special to do to handle a cancellation */ } elt->downstream->drain_mode = TRUE; } crc32_add((uint8_t *)buf, len, &elt->crc); amfree(buf); } else { g_debug("sending XMSG_CRC message"); g_debug("push_to_fd CRC: %08x", crc32_finish(&elt->crc)); msg = xmsg_new(elt->downstream, XMSG_CRC, 0); msg->crc = crc32_finish(&elt->crc); msg->size = elt->crc.size; xfer_queue_message(elt->xfer, msg); close_write_fd(self); } return; } default: case PUSH_INVALID: g_assert_not_reached(); break; } }
/* common code for both directtcp_listen_thread and directtcp_connect_thread; * this is called after self->conn is filled in and carries out the data * transfer over that connection. NOTE: start_part_mutex is HELD when this * function begins */ static gpointer directtcp_common_thread( XferSourceRecovery *self) { XferElement *elt = XFER_ELEMENT(self); char *errmsg = NULL; int result; /* send XMSG_READY to indicate it's OK to call start_part now */ DBG(2, "directtcp_common_thread sending XMSG_READY"); xfer_queue_message(elt->xfer, xmsg_new(elt, XMSG_READY, 0)); /* now we sit around waiting for signals to write a part */ while (1) { guint64 actual_size; XMsg *msg; while (self->paused && !elt->cancelled) { DBG(9, "directtcp_common_thread waiting to be un-paused"); g_cond_wait(self->start_part_cond, self->start_part_mutex); } DBG(9, "directtcp_common_thread done waiting"); if (elt->cancelled) { g_mutex_unlock(self->start_part_mutex); goto close_conn_and_send_done; } /* if the device is NULL, we're done */ if (!self->device) break; /* read the part */ self->part_timer = g_timer_new(); while (1) { DBG(2, "directtcp_common_thread reading part from %s", self->device->device_name); result = device_read_to_connection(self->device, G_MAXUINT64, &actual_size, &elt->cancelled, self->start_part_mutex, self->abort_cond); if (result == 1 && !elt->cancelled) { xfer_cancel_with_error(elt, _("error reading from device: %s"), device_error_or_status(self->device)); g_mutex_unlock(self->start_part_mutex); goto close_conn_and_send_done; } else if (result == 2 || elt->cancelled) { g_mutex_unlock(self->start_part_mutex); goto close_conn_and_send_done; } /* break on EOF; otherwise do another read_to_connection */ if (self->device->is_eof) { break; } } DBG(2, "done reading part; sending XMSG_PART_DONE"); /* the device has signalled EOF (really end-of-part), so clean up instance * variables and report the EOP to the caller in the form of an xmsg */ msg = xmsg_new(XFER_ELEMENT(self), XMSG_PART_DONE, 0); msg->size = actual_size; msg->duration = g_timer_elapsed(self->part_timer, NULL); msg->partnum = 0; msg->fileno = self->device->file; msg->successful = TRUE; msg->eof = FALSE; self->paused = TRUE; g_object_unref(self->device); self->device = NULL; self->part_size = 0; self->block_size = 0; g_timer_destroy(self->part_timer); self->part_timer = NULL; xfer_queue_message(elt->xfer, msg); } g_mutex_unlock(self->start_part_mutex); close_conn_and_send_done: if (self->conn) { errmsg = directtcp_connection_close(self->conn); g_object_unref(self->conn); self->conn = NULL; if (errmsg) { xfer_cancel_with_error(elt, _("error closing DirectTCP connection: %s"), errmsg); wait_until_xfer_cancelled(elt->xfer); } } xfer_queue_message(elt->xfer, xmsg_new(elt, XMSG_DONE, 0)); return NULL; }