Example #1
0
/* skip up to length bytes in a chunkqueue, return number of bytes skipped */
goffset li_chunkqueue_skip(liChunkQueue *cq, goffset length) {
	liChunk *c;
	goffset bytes = 0;
	goffset we_have;

	while ( (NULL != (c = li_chunkqueue_first_chunk(cq))) && (0 == (we_have = li_chunk_length(c)) || length > 0) ) {
		if (we_have <= length) {
			/* skip (delete) complete chunk */
			if (c->type == STRING_CHUNK) cqlimit_update(cq, - (goffset)c->data.str->len);
			else if (c->type == MEM_CHUNK) cqlimit_update(cq, - (goffset)c->mem->len);
			else if (c->type == BUFFER_CHUNK) cqlimit_update(cq, - (goffset)c->data.buffer.length);
			chunk_free(cq, c);
			bytes += we_have;
			length -= we_have;
		} else { /* skip first part of a chunk */
			c->offset += length;
			bytes += length;
			length = 0;
		}
	}

	cq->bytes_out += bytes;
	cq->length -= bytes;
	return bytes;
}
Example #2
0
/* steal the first chunk from in and append it to out, return number of bytes stolen */
goffset li_chunkqueue_steal_chunk(liChunkQueue *out, liChunkQueue *in) {
	liChunk *c;
	goffset length;
	GList *l = g_queue_pop_head_link(&in->queue);
	if (!l) return 0;
	g_queue_push_tail_link(&out->queue, l);

	c = (liChunk*) l->data;
	length = li_chunk_length(c);
	in->bytes_out += length;
	in->length -= length;
	out->bytes_in += length;
	out->length += length;
	if (in->limit != out->limit) {
		if (c->type == STRING_CHUNK) {
			cqlimit_update(out, c->data.str->len);
			cqlimit_update(in, - (goffset)c->data.str->len);
		} else if (c->type == MEM_CHUNK) {
			cqlimit_update(out, c->mem->len);
			cqlimit_update(in, - (goffset)c->mem->len);
		} else if (c->type == BUFFER_CHUNK) {
			cqlimit_update(out, c->data.buffer.length);
			cqlimit_update(in, - (goffset)c->data.buffer.length);
		}
	}
	return length;
}
Example #3
0
/* first chunk must be a STRING_CHUNK ! */
liNetworkStatus li_network_backend_writev(int fd, liChunkQueue *cq, goffset *write_max, GError **err) {
	off_t we_have;
	ssize_t r;
	gboolean did_write_something = FALSE;
	liChunkIter ci;
	liChunk *c;
	liNetworkStatus res = LI_NETWORK_STATUS_FATAL_ERROR;

	GArray *chunks = g_array_sized_new(FALSE, TRUE, sizeof(struct iovec), UIO_MAXIOV);

	if (0 == cq->length) goto cleanup; /* FATAL ERROR */

	do {
		ci = li_chunkqueue_iter(cq);

		if (STRING_CHUNK != (c = li_chunkiter_chunk(ci))->type && MEM_CHUNK != c->type && BUFFER_CHUNK != c->type) {
			res = did_write_something ? LI_NETWORK_STATUS_SUCCESS : LI_NETWORK_STATUS_FATAL_ERROR;
			goto cleanup;
		}

		we_have = 0;
		do {
			guint i = chunks->len;
			off_t len = li_chunk_length(c);
			struct iovec *v;
			g_array_set_size(chunks, i + 1);
			v = &g_array_index(chunks, struct iovec, i);
			if (c->type == STRING_CHUNK) {
				v->iov_base = c->data.str->str + c->offset;
			} else if (c->type == MEM_CHUNK) {
				v->iov_base = c->mem->data + c->offset;
			} else { /* if (c->type == BUFFER_CHUNK) */
				v->iov_base = c->data.buffer.buffer->addr + c->data.buffer.offset + c->offset;
			}
			if (len > *write_max - we_have) len = *write_max - we_have;
			v->iov_len = len;
			we_have += len;
		} while (we_have < *write_max &&
		         li_chunkiter_next(&ci) &&
		         (STRING_CHUNK == (c = li_chunkiter_chunk(ci))->type || MEM_CHUNK == c->type || BUFFER_CHUNK == c->type) &&
		         chunks->len < UIO_MAXIOV);

		while (-1 == (r = writev(fd, (struct iovec*) chunks->data, chunks->len))) {
			switch (errno) {
			case EAGAIN:
#if EWOULDBLOCK != EAGAIN
			case EWOULDBLOCK:
#endif
				res = LI_NETWORK_STATUS_WAIT_FOR_EVENT;
				goto cleanup;
			case ECONNRESET:
			case EPIPE:
			case ETIMEDOUT:
				res = LI_NETWORK_STATUS_CONNECTION_CLOSE;
				goto cleanup;
			case EINTR:
				break; /* try again */
			default:
				g_set_error(err, LI_NETWORK_ERROR, 0, "li_network_backend_writev: oops, write to fd=%d failed: %s", fd, g_strerror(errno));
				goto cleanup;
			}
		}
		if (0 == r) {
			res = LI_NETWORK_STATUS_WAIT_FOR_EVENT;
			goto cleanup;
		}
		li_chunkqueue_skip(cq, r);
		*write_max -= r;

		if (r != we_have) {
			res = LI_NETWORK_STATUS_WAIT_FOR_EVENT;
			goto cleanup;
		}

		if (0 == cq->length) {
			res = LI_NETWORK_STATUS_SUCCESS;
			goto cleanup;
		}

		did_write_something = TRUE;
		g_array_set_size(chunks, 0);
	} while (*write_max > 0);

	res = LI_NETWORK_STATUS_SUCCESS;

cleanup:
	g_array_free(chunks, TRUE);
	return res;
}
static void bod_handle_data(bod_state *state) {
	liChunkQueue *out = state->stream.out;
	liChunkQueue *in;

	if (out->is_closed) {
		li_stream_disconnect(&state->stream);
		bod_close(state);
		return;
	}

	in = (state->stream.source != NULL) ? state->stream.source->out : NULL;
	if (NULL == in) goto out;

	if (NULL == state->vr) {
		li_chunkqueue_steal_all(out, in);
		goto out;
	}

	while (in->length > 0) {
		liChunk *c = li_chunkqueue_first_chunk(in);
		liChunkIter ci;
		off_t length, data_len;
		char *data = NULL;
		GError *err;

		assert(UNUSED_CHUNK != c->type);
		switch (c->type) {
		case UNUSED_CHUNK:
			/* shouldn't happen anyway, but stealing it is ok here too */
		case FILE_CHUNK:
			if (state->split_on_file_chunks) {
				bod_close(state);
			} else {
				bod_flush(state);
			}
			li_chunkqueue_steal_chunk(out, in);
			break;
		case STRING_CHUNK:
		case MEM_CHUNK:
		case BUFFER_CHUNK:
			if (!bod_open(state)) return;

			length = li_chunk_length(c);
			ci = li_chunkqueue_iter(in);

			err = NULL;
			if (LI_HANDLER_GO_ON != li_chunkiter_read(ci, 0, length, &data, &data_len, &err)) {
				if (NULL != err) {
					VR_ERROR(state->vr, "%s", err->message);
					g_error_free(err);
				}
				bod_error(state);
				return;
			}

			while ( data_len > 0 ) {
				ssize_t r;

				r = pwrite(state->tempfile->fd, data, data_len, state->write_pos);

				if (r < 0) {
					switch (errno) {
					case EINTR: continue;
					default: break;
					}

					VR_ERROR(state->vr, "pwrite failed: %s", g_strerror(errno));
					bod_stop(state); /* write failures are not critical */
					return;
				}

				data += r;
				data_len -= r;
				state->write_pos += r;
			}

			li_chunkqueue_skip(in, length);

			break;
		}
	}

	bod_autoflush(state);

out:
	if (NULL == in || in->is_closed) {
		out->is_closed = TRUE;
		bod_close(state); /* close/flush ignores out->is_closed */
		li_stream_notify(&state->stream); /* if no flush happened we still notify */
	}
}
Example #5
0
liHandlerResult li_filter_buffer_on_disk(liVRequest *vr, liChunkQueue *out, liChunkQueue *in, bod_state *state) {
	UNUSED(vr);

	if (out->is_closed) {
		in->is_closed = TRUE;
		li_chunkqueue_skip_all(in);
		bod_close(state);
		return LI_HANDLER_GO_ON;
	}

	while (in->length > 0) {
		liChunk *c = li_chunkqueue_first_chunk(in);
		liChunkIter ci;
		off_t length, data_len;
		char *data = NULL;
		GError *err;

		switch (c->type) {
		case UNUSED_CHUNK: return LI_HANDLER_ERROR;
		case FILE_CHUNK:
			bod_flush(out, state);
			if (state->split_on_file_chunks) {
				bod_close(state);
			}
			li_chunkqueue_steal_chunk(out, in);
			break;
		case STRING_CHUNK:
		case MEM_CHUNK:
		case BUFFER_CHUNK:
			if (!bod_open(vr, state)) return LI_HANDLER_ERROR;

			length = li_chunk_length(c);
			ci = li_chunkqueue_iter(in);

			err = NULL;
			if (LI_HANDLER_GO_ON != li_chunkiter_read(ci, 0, length, &data, &data_len, &err)) {
				if (NULL != err) {
					VR_ERROR(vr, "%s", err->message);
					g_error_free(err);
				}
				return LI_HANDLER_ERROR;
			}

			while ( data_len > 0 ) {
				ssize_t r;

				r = pwrite(state->tempfile->fd, data, data_len, state->write_pos);

				if (r < 0) {
					switch (errno) {
					case EINTR: continue;
					default: break;
					}

					VR_ERROR(vr, "pwrite failed: %s", g_strerror(errno));
					return LI_HANDLER_ERROR;
				}

				data += r;
				data_len -= r;
				state->write_pos += r;
			}

			li_chunkqueue_skip(in, length);

			break;
		}
	}

	bod_autoflush(out, state);

	if (in->is_closed) {
		bod_flush(out, state);
		out->is_closed = TRUE;
		bod_close(state);
		return LI_HANDLER_GO_ON;
	}
	return LI_HANDLER_GO_ON;
}
Example #6
0
/* get the data from a chunk; easy in case of a STRING_CHUNK,
 * but needs to do io in case of FILE_CHUNK; the data is _not_ marked as "done"
 * may return HANDLER_GO_ON, HANDLER_ERROR
 */
liHandlerResult li_chunkiter_read(liVRequest *vr, liChunkIter iter, off_t start, off_t length, char **data_start, off_t *data_len) {
	liChunk *c = li_chunkiter_chunk(iter);
	off_t we_have, our_start;
	liHandlerResult res = LI_HANDLER_GO_ON;

	if (!c) return LI_HANDLER_ERROR;
	if (!data_start || !data_len) return LI_HANDLER_ERROR;

	we_have = li_chunk_length(c) - start;
	if (length > we_have) length = we_have;
	if (length <= 0) return LI_HANDLER_ERROR;

	switch (c->type) {
	case UNUSED_CHUNK: return LI_HANDLER_ERROR;
	case STRING_CHUNK:
		*data_start = c->data.str->str + c->offset + start;
		*data_len = length;
		break;
	case MEM_CHUNK:
		*data_start = (char*) c->mem->data + c->offset + start;
		*data_len = length;
		break;
	case FILE_CHUNK:
		if (LI_HANDLER_GO_ON != (res = li_chunkfile_open(vr, c->data.file.file))) return res;

		if (length > MAX_MMAP_CHUNK) length = MAX_MMAP_CHUNK;

		if (!c->mem) {
			c->mem = g_byte_array_sized_new(length);
		} else {
			g_byte_array_set_size(c->mem, length);
		}

		our_start = start + c->offset + c->data.file.start;

read_chunk:
		if (-1 == (we_have = pread(c->data.file.file->fd, c->mem->data, length, our_start))) {
			if (EINTR == errno) goto read_chunk;
			VR_ERROR(vr, "pread failed for '%s' (fd = %i): %s",
				GSTR_SAFE_STR(c->data.file.file->name), c->data.file.file->fd,
				g_strerror(errno));
			g_byte_array_free(c->mem, TRUE);
			c->mem = NULL;
			return LI_HANDLER_ERROR;
		} else if (we_have != length) {
			/* may return less than requested bytes due to signals */
			/* CON_TRACE(srv, "read return unexpected number of bytes"); */
			if (we_have == 0) {
				VR_ERROR(vr, "pread returned 0 bytes for '%s' (fd = %i): unexpected end of file?",
					GSTR_SAFE_STR(c->data.file.file->name), c->data.file.file->fd);
				g_byte_array_free(c->mem, TRUE);
				c->mem = NULL;
				return LI_HANDLER_ERROR;
			}
			length = we_have;
			g_byte_array_set_size(c->mem, length);
		}
		*data_start = (char*) c->mem->data;
		*data_len = length;
		break;
	case BUFFER_CHUNK:
		*data_start = (char*) c->data.buffer.buffer->addr + c->data.buffer.offset + c->offset + start;
		*data_len = length;
		break;
	}
	return LI_HANDLER_GO_ON;
}
Example #7
0
/* steal up to length bytes from in and put them into out, return number of bytes stolen */
goffset li_chunkqueue_steal_len(liChunkQueue *out, liChunkQueue *in, goffset length) {
	liChunk *c, *cnew;
	GList* l;
	goffset bytes = 0, meminbytes = 0, memoutbytes = 0;
	goffset we_have;

	while ( (NULL != (c = li_chunkqueue_first_chunk(in))) && length > 0 ) {
		we_have = li_chunk_length(c);
		if (!we_have) { /* remove empty chunks */
			if (c->type == STRING_CHUNK) meminbytes -= c->data.str->len;
			else if (c->type == MEM_CHUNK) meminbytes -= c->mem->len;
			else if (c->type == BUFFER_CHUNK) meminbytes -= c->data.buffer.length;
			chunk_free(in, c);
			continue;
		}
		if (we_have <= length) { /* move complete chunk */
			l = g_queue_pop_head_link(&in->queue);
			g_queue_push_tail_link(&out->queue, l);
			bytes += we_have;
			if (c->type == STRING_CHUNK) {
				meminbytes -= c->data.str->len;
				memoutbytes += c->data.str->len;
			} else if (c->type == MEM_CHUNK) {
				meminbytes -= c->mem->len;
				memoutbytes += c->mem->len;
			} else if (c->type == BUFFER_CHUNK) {
				meminbytes -= c->data.buffer.length;
				memoutbytes += c->data.buffer.length;
			}
			length -= we_have;
		} else { /* copy first part of a chunk */
			cnew = chunk_new();
			switch (c->type) {
			case UNUSED_CHUNK: /* impossible, has length 0 */
				/* remove "empty" chunks */
				chunk_free(in, c);
				chunk_free(NULL, cnew);
				continue;
			case STRING_CHUNK: /* change type to MEM_CHUNK, as we copy it anyway */
				cnew->type = MEM_CHUNK;
				cnew->mem = g_byte_array_sized_new(length);
				g_byte_array_append(cnew->mem, (guint8*) c->data.str->str + c->offset, length);
				memoutbytes += length;
				break;
			case MEM_CHUNK:
				cnew->type = MEM_CHUNK;
				cnew->mem = g_byte_array_sized_new(length);
				g_byte_array_append(cnew->mem, (guint8*) c->mem->data + c->offset, length);
				memoutbytes += length;
				break;
			case FILE_CHUNK:
				cnew->type = FILE_CHUNK;
				li_chunkfile_acquire(c->data.file.file);
				cnew->data.file.file = c->data.file.file;
				cnew->data.file.start = c->data.file.start + c->offset;
				cnew->data.file.length = length;
				break;
			case BUFFER_CHUNK:
				cnew->type = BUFFER_CHUNK;
				li_buffer_acquire(c->data.buffer.buffer);
				cnew->data.buffer.buffer = c->data.buffer.buffer;
				cnew->data.buffer.offset = c->data.buffer.offset + c->offset;
				cnew->data.buffer.length = length;
				memoutbytes += length;
				break;
			}
			c->offset += length;
			bytes += length;
			length = 0;
			g_queue_push_tail_link(&out->queue, &cnew->cq_link);
		}
	}

	in->bytes_out += bytes;
	in->length -= bytes;
	out->bytes_in += bytes;
	out->length += bytes;
	cqlimit_update(out, memoutbytes);
	cqlimit_update(in, meminbytes);

	return bytes;
}
Example #8
0
/* same as li_chunkiter_read, but tries mmap() first and falls back to pread();
 * as accessing mmap()-ed areas may result in SIGBUS, you have to handle that signal somehow.
 */
liHandlerResult li_chunkiter_read_mmap(liVRequest *vr, liChunkIter iter, off_t start, off_t length, char **data_start, off_t *data_len) {
	liChunk *c = li_chunkiter_chunk(iter);
	off_t we_want, we_have, our_start, our_offset;
	liHandlerResult res = LI_HANDLER_GO_ON;
	int mmap_errno = 0;

	if (!c) return LI_HANDLER_ERROR;
	if (!data_start || !data_len) return LI_HANDLER_ERROR;

	we_have = li_chunk_length(c) - start;
	if (length > we_have) length = we_have;
	if (length <= 0) return LI_HANDLER_ERROR;

	switch (c->type) {
	case UNUSED_CHUNK: return LI_HANDLER_ERROR;
	case STRING_CHUNK:
		*data_start = c->data.str->str + c->offset + start;
		*data_len = length;
		break;
	case MEM_CHUNK:
		*data_start = (char*) c->mem->data + c->offset + start;
		*data_len = length;
		break;
	case FILE_CHUNK:
		if (LI_HANDLER_GO_ON != (res = li_chunkfile_open(vr, c->data.file.file))) return res;

		if (length > MAX_MMAP_CHUNK) length = MAX_MMAP_CHUNK;

		if ( !(c->data.file.mmap.data != MAP_FAILED || c->mem) /* no data present */
			|| !( /* or in the wrong range */
				(start + c->offset + c->data.file.start >= c->data.file.mmap.offset)
				&& (start + c->offset + c->data.file.start + length <= c->data.file.mmap.offset + (ssize_t) c->data.file.mmap.length)) ) {
			/* then find new range */
			our_start = start + c->offset + c->data.file.start;  /* "start" maps to this offset in the file */
			our_offset = our_start % MMAP_CHUNK_ALIGN;      /* offset for "start" in new mmap block */
			our_start -= our_offset;                 /* file offset for mmap */

			we_want = length + MAX_MMAP_CHUNK;
			if (we_want > we_have) we_want = we_have;
			we_want += our_offset;

			if (MAP_FAILED != c->data.file.mmap.data) {
				munmap(c->data.file.mmap.data, c->data.file.mmap.length);
				c->data.file.mmap.data = MAP_FAILED;
			}
			c->data.file.mmap.offset = our_start;
			c->data.file.mmap.length = we_want;
			if (!c->mem) { /* mmap did not fail till now */
				c->data.file.mmap.data = mmap(0, we_want, PROT_READ, MAP_SHARED, c->data.file.file->fd, our_start);
				mmap_errno = errno;
			}
			if (MAP_FAILED == c->data.file.mmap.data) {
				/* fallback to pread(...) */
				if (!c->mem) {
					c->mem = g_byte_array_sized_new(we_want);
				} else {
					g_byte_array_set_size(c->mem, we_want);
				}
read_chunk:
				if (-1 == (we_have = pread(c->data.file.file->fd, c->mem->data, we_want, our_start))) {
					if (EINTR == errno) goto read_chunk;
					/* prefer the error of the first syscall */
					if (0 != mmap_errno) {
						VR_ERROR(vr, "mmap failed for '%s' (fd = %i): %s",
							GSTR_SAFE_STR(c->data.file.file->name), c->data.file.file->fd,
							g_strerror(mmap_errno));
					} else {
						VR_ERROR(vr, "pread failed for '%s' (fd = %i): %s",
							GSTR_SAFE_STR(c->data.file.file->name), c->data.file.file->fd,
							g_strerror(errno));
					}
					g_byte_array_free(c->mem, TRUE);
					c->mem = NULL;
					return LI_HANDLER_ERROR;
				} else if (we_have != we_want) {
					/* may return less than requested bytes due to signals */
					/* CON_TRACE(srv, "read return unexpected number of bytes"); */
					we_want = we_have;
					if (length > we_have) length = we_have;
					c->data.file.mmap.length = we_want;
					g_byte_array_set_size(c->mem, we_want);
				}
			} else {
#ifdef HAVE_MADVISE
				/* don't advise files < 64Kb */
				if (c->data.file.mmap.length > (64*1024) &&
					0 != madvise(c->data.file.mmap.data, c->data.file.mmap.length, MADV_WILLNEED)) {
					VR_ERROR(vr, "madvise failed for '%s' (fd = %i): %s",
						GSTR_SAFE_STR(c->data.file.file->name), c->data.file.file->fd,
						g_strerror(errno));
				}
#endif
			}
		}
		*data_start = (c->mem ? (char*) c->mem->data : c->data.file.mmap.data) + start + c->offset + c->data.file.start - c->data.file.mmap.offset;
		*data_len = length;
		break;
	case BUFFER_CHUNK:
		*data_start = (char*) c->data.buffer.buffer->addr + c->data.buffer.offset + c->offset + start;
		*data_len = length;
		break;
	}
	return LI_HANDLER_GO_ON;
}