Esempio n. 1
0
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;
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
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);
}
Esempio n. 4
0
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);
}
Esempio n. 5
0
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;
}
Esempio n. 6
0
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);
}
Esempio n. 7
0
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);
}
Esempio n. 8
0
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);
}
Esempio n. 9
0
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);
}
Esempio n. 10
0
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;
}
Esempio n. 11
0
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;
    }
}
Esempio n. 12
0
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;
    }
}
Esempio n. 13
0
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;
}
Esempio n. 14
0
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;
}
Esempio n. 15
0
/* 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;
}