示例#1
0
文件: istream.c 项目: rowhit/core-1
bool i_stream_try_alloc(struct istream_private *stream,
			size_t wanted_size, size_t *size_r)
{
	i_assert(wanted_size > 0);

	if (wanted_size > stream->buffer_size - stream->pos) {
		if (stream->skip > 0) {
			/* remove the unused bytes from beginning of buffer */
                        i_stream_compress(stream);
		} else if (stream->buffer_size < i_stream_get_max_buffer_size(&stream->istream)) {
			/* buffer is full - grow it */
			i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE);
		}
	}

	*size_r = stream->buffer_size - stream->pos;
	if (stream->try_alloc_limit > 0 &&
	    *size_r > stream->try_alloc_limit)
		*size_r = stream->try_alloc_limit;
	return *size_r > 0;
}
示例#2
0
static ssize_t i_stream_lzma_read(struct istream_private *stream)
{
	struct lzma_istream *zstream = (struct lzma_istream *)stream;
	const unsigned char *data;
	uoff_t high_offset;
	size_t size, out_size;
	lzma_ret ret;

	high_offset = stream->istream.v_offset + (stream->pos - stream->skip);
	if (zstream->eof_offset == high_offset) {
		i_assert(zstream->high_pos == 0 ||
			 zstream->high_pos == stream->pos);
		stream->istream.eof = TRUE;
		return -1;
	}

	if (stream->pos < zstream->high_pos) {
		/* we're here because we seeked back within the read buffer. */
		ret = zstream->high_pos - stream->pos;
		stream->pos = zstream->high_pos;
		zstream->high_pos = 0;

		if (zstream->eof_offset != (uoff_t)-1) {
			high_offset = stream->istream.v_offset +
				(stream->pos - stream->skip);
			i_assert(zstream->eof_offset == high_offset);
			stream->istream.eof = TRUE;
		}
		return ret;
	}
	zstream->high_pos = 0;

	if (stream->pos + CHUNK_SIZE > stream->buffer_size) {
		/* try to keep at least CHUNK_SIZE available */
		if (!zstream->marked && stream->skip > 0) {
			/* don't try to keep anything cached if we don't
			   have a seek mark. */
			i_stream_compress(stream);
		}
		if (stream->buffer_size < i_stream_get_max_buffer_size(&stream->istream))
			i_stream_grow_buffer(stream, CHUNK_SIZE);

		if (stream->pos == stream->buffer_size) {
			if (stream->skip > 0) {
				/* lose our buffer cache */
				i_stream_compress(stream);
			}

			if (stream->pos == stream->buffer_size)
				return -2; /* buffer full */
		}
	}

	if (i_stream_read_more(stream->parent, &data, &size) < 0) {
		if (stream->parent->stream_errno != 0) {
			stream->istream.stream_errno =
				stream->parent->stream_errno;
		} else {
			i_assert(stream->parent->eof);
			lzma_stream_end(zstream);
			stream->istream.eof = TRUE;
		}
		return -1;
	}
	if (size == 0) {
		/* no more input */
		i_assert(!stream->istream.blocking);
		return 0;
	}

	zstream->strm.next_in = data;
	zstream->strm.avail_in = size;

	out_size = stream->buffer_size - stream->pos;
	zstream->strm.next_out = stream->w_buffer + stream->pos;
	zstream->strm.avail_out = out_size;
	ret = lzma_code(&zstream->strm, LZMA_RUN);

	out_size -= zstream->strm.avail_out;
	stream->pos += out_size;

	i_stream_skip(stream->parent, size - zstream->strm.avail_in);

	switch (ret) {
	case LZMA_OK:
		break;
	case LZMA_DATA_ERROR:
	case LZMA_BUF_ERROR:
		lzma_read_error(zstream, "corrupted data");
		stream->istream.stream_errno = EINVAL;
		return -1;
	case LZMA_FORMAT_ERROR:
		lzma_read_error(zstream, "wrong magic in header (not xz file?)");
		stream->istream.stream_errno = EINVAL;
		return -1;
	case LZMA_OPTIONS_ERROR:
		lzma_read_error(zstream, "Unsupported xz options");
		stream->istream.stream_errno = EINVAL;
		return -1;
	case LZMA_MEM_ERROR:
		i_fatal_status(FATAL_OUTOFMEM, "lzma.read(%s): Out of memory",
			       i_stream_get_name(&stream->istream));
	case LZMA_STREAM_END:
		lzma_stream_end(zstream);
		if (out_size == 0) {
			stream->istream.eof = TRUE;
			return -1;
		}
		break;
	default:
		lzma_read_error(zstream, t_strdup_printf(
			"lzma_code() failed with %d", ret));
		stream->istream.stream_errno = EINVAL;
		return -1;
	}
	if (out_size == 0) {
		/* read more input */
		return i_stream_lzma_read(stream);
	}
	return out_size;
}
示例#3
0
static ssize_t i_stream_zlib_read(struct istream_private *stream)
{
	struct zlib_istream *zstream = (struct zlib_istream *)stream;
	const unsigned char *data;
	uoff_t high_offset;
	size_t size, out_size;
	int ret;

	high_offset = stream->istream.v_offset + (stream->pos - stream->skip);
	if (zstream->eof_offset == high_offset) {
		i_assert(zstream->high_pos == 0 ||
			 zstream->high_pos == stream->pos);
		if (!zstream->trailer_read) {
			do {
				ret = i_stream_zlib_read_trailer(zstream);
			} while (ret == 0 && stream->istream.blocking);
			if (ret <= 0)
				return ret;
		}
		if (!zstream->gz || i_stream_is_eof(stream->parent)) {
			stream->istream.eof = TRUE;
			return -1;
		}
		/* gzip file with concatenated content */
		zstream->eof_offset = (uoff_t)-1;
		zstream->stream_size = (uoff_t)-1;
		zstream->header_read = FALSE;
		zstream->trailer_read = FALSE;
		zstream->crc32 = 0;

		(void)inflateEnd(&zstream->zs);
		i_stream_zlib_init(zstream);
	}

	if (!zstream->header_read) {
		do {
			ret = i_stream_zlib_read_header(stream);
		} while (ret == 0 && stream->istream.blocking);
		if (ret <= 0)
			return ret;
		zstream->header_read = TRUE;
	}

	if (stream->pos < zstream->high_pos) {
		/* we're here because we seeked back within the read buffer. */
		ret = zstream->high_pos - stream->pos;
		stream->pos = zstream->high_pos;
		zstream->high_pos = 0;
		if (zstream->trailer_read) {
			high_offset = stream->istream.v_offset +
				(stream->pos - stream->skip);
			i_assert(zstream->eof_offset == high_offset);
			stream->istream.eof = TRUE;
		}
		return ret;
	}
	zstream->high_pos = 0;

	if (stream->pos + CHUNK_SIZE > stream->buffer_size) {
		/* try to keep at least CHUNK_SIZE available */
		if (!zstream->marked && stream->skip > 0) {
			/* don't try to keep anything cached if we don't
			   have a seek mark. */
			i_stream_compress(stream);
		}
		if (stream->buffer_size < i_stream_get_max_buffer_size(&stream->istream))
			i_stream_grow_buffer(stream, CHUNK_SIZE);

		if (stream->pos == stream->buffer_size) {
			if (stream->skip > 0) {
				/* lose our buffer cache */
				i_stream_compress(stream);
			}

			if (stream->pos == stream->buffer_size)
				return -2; /* buffer full */
		}
	}

	if (i_stream_read_more(stream->parent, &data, &size) < 0) {
		if (stream->parent->stream_errno != 0) {
			stream->istream.stream_errno =
				stream->parent->stream_errno;
		} else {
			i_assert(stream->parent->eof);
			zlib_read_error(zstream, "unexpected EOF");
			stream->istream.stream_errno = EPIPE;
		}
		return -1;
	}
	if (size == 0) {
		/* no more input */
		i_assert(!stream->istream.blocking);
		return 0;
	}

	zstream->zs.next_in = (void *)data;
	zstream->zs.avail_in = size;

	out_size = stream->buffer_size - stream->pos;
	zstream->zs.next_out = stream->w_buffer + stream->pos;
	zstream->zs.avail_out = out_size;
	ret = inflate(&zstream->zs, Z_SYNC_FLUSH);

	out_size -= zstream->zs.avail_out;
	zstream->crc32 = crc32_data_more(zstream->crc32,
					 stream->w_buffer + stream->pos,
					 out_size);
	stream->pos += out_size;

	i_stream_skip(stream->parent, size - zstream->zs.avail_in);

	switch (ret) {
	case Z_OK:
		break;
	case Z_NEED_DICT:
		zlib_read_error(zstream, "can't read file without dict");
		stream->istream.stream_errno = EINVAL;
		return -1;
	case Z_DATA_ERROR:
		zlib_read_error(zstream, "corrupted data");
		stream->istream.stream_errno = EINVAL;
		return -1;
	case Z_MEM_ERROR:
		i_fatal_status(FATAL_OUTOFMEM, "zlib.read(%s): Out of memory",
			       i_stream_get_name(&stream->istream));
	case Z_STREAM_END:
		zstream->eof_offset = stream->istream.v_offset +
			(stream->pos - stream->skip);
		zstream->stream_size = zstream->eof_offset;
		zstream->zs.avail_in = 0;

		if (!zstream->trailer_read) {
			/* try to read and verify the trailer, we might not
			   be called again. */
			if (i_stream_zlib_read_trailer(zstream) < 0)
				return -1;
		}
		break;
	default:
		i_fatal("inflate() failed with %d", ret);
	}
	if (out_size == 0) {
		/* read more input */
		return i_stream_zlib_read(stream);
	}
	return out_size;
}
static ssize_t i_stream_lz4_read(struct istream_private *stream)
{
	struct lz4_istream *zstream = (struct lz4_istream *)stream;
	const unsigned char *data;
	size_t size, max_size;
	int ret;

	if (!zstream->header_read) {
		if ((ret = i_stream_lz4_read_header(zstream)) <= 0)
			return ret;
		zstream->header_read = TRUE;
	}

	if (zstream->chunk_left == 0) {
		ret = i_stream_read_data(stream->parent, &data, &size,
					 IOSTREAM_LZ4_CHUNK_PREFIX_LEN);
		if (ret < 0) {
			stream->istream.stream_errno =
				stream->parent->stream_errno;
			if (stream->istream.stream_errno == 0) {
				stream->istream.eof = TRUE;
				zstream->stream_size = stream->istream.v_offset +
					stream->pos - stream->skip;
			}
			return ret;
		}
		if (ret == 0 && !stream->istream.eof)
			return 0;
		zstream->chunk_size = zstream->chunk_left =
			((uint32_t)data[0] << 24) |
			(data[1] << 16) | (data[2] << 8) | data[3];
		if (zstream->chunk_size == 0 ||
		    zstream->chunk_size > ISTREAM_LZ4_CHUNK_SIZE) {
			lz4_read_error(zstream, t_strdup_printf(
				"invalid lz4 chunk size: %u", zstream->chunk_size));
			stream->istream.stream_errno = EINVAL;
			return -1;
		}
		i_stream_skip(stream->parent, IOSTREAM_LZ4_CHUNK_PREFIX_LEN);
		buffer_set_used_size(zstream->chunk_buf, 0);
	}

	/* read the whole compressed chunk into memory */
	while (zstream->chunk_left > 0 &&
	       (ret = i_stream_read_data(zstream->istream.parent,
					 &data, &size, 0)) > 0) {
		if (size > zstream->chunk_left)
			size = zstream->chunk_left;
		buffer_append(zstream->chunk_buf, data, size);
		i_stream_skip(zstream->istream.parent, size);
		zstream->chunk_left -= size;
	}
	if (zstream->chunk_left > 0) {
		if (ret == -1 && zstream->istream.parent->stream_errno == 0) {
			lz4_read_error(zstream, "truncated lz4 chunk");
			stream->istream.stream_errno = EINVAL;
			return -1;
		}
		zstream->istream.istream.stream_errno =
			zstream->istream.parent->stream_errno;
		return ret;
	}
	/* if we already have max_buffer_size amount of data, fail here */
	i_stream_compress(stream);
	if (stream->pos >= stream->max_buffer_size)
		return -2;
	/* allocate enough space for the old data and the new
	   decompressed chunk. we don't know the original compressed size,
	   so just allocate the max amount of memory. */
	max_size = stream->pos + zstream->max_uncompressed_chunk_size;
	if (stream->buffer_size < max_size) {
		stream->w_buffer = i_realloc(stream->w_buffer,
					     stream->buffer_size, max_size);
		stream->buffer_size = max_size;
		stream->buffer = stream->w_buffer;
	}
	ret = LZ4_decompress_safe(zstream->chunk_buf->data,
				  (void *)(stream->w_buffer + stream->pos),
				  zstream->chunk_buf->used,
				  stream->buffer_size - stream->pos);
	i_assert(ret <= (int)zstream->max_uncompressed_chunk_size);
	if (ret < 0) {
		lz4_read_error(zstream, "corrupted lz4 chunk");
		stream->istream.stream_errno = EINVAL;
		return -1;
	}
	i_assert(ret > 0);
	stream->pos += ret;
	i_assert(stream->pos <= stream->buffer_size);
	return ret;
}
示例#5
0
static ssize_t i_stream_bzlib_read(struct istream_private *stream)
{
	struct bzlib_istream *zstream = (struct bzlib_istream *)stream;
	const unsigned char *data;
	uoff_t high_offset;
	size_t size;
	int ret;

	high_offset = stream->istream.v_offset + (stream->pos - stream->skip);
	if (zstream->eof_offset == high_offset) {
		i_assert(zstream->high_pos == 0 ||
			 zstream->high_pos == stream->pos);
		stream->istream.eof = TRUE;
		return -1;
	}

	if (stream->pos < zstream->high_pos) {
		/* we're here because we seeked back within the read buffer. */
		ret = zstream->high_pos - stream->pos;
		stream->pos = zstream->high_pos;
		zstream->high_pos = 0;

		if (zstream->eof_offset != (uoff_t)-1) {
			high_offset = stream->istream.v_offset +
				(stream->pos - stream->skip);
			i_assert(zstream->eof_offset == high_offset);
			stream->istream.eof = TRUE;
		}
		return ret;
	}
	zstream->high_pos = 0;

	if (stream->pos + CHUNK_SIZE > stream->buffer_size) {
		/* try to keep at least CHUNK_SIZE available */
		if (!zstream->marked && stream->skip > 0) {
			/* don't try to keep anything cached if we don't
			   have a seek mark. */
			i_stream_compress(stream);
		}
		if (stream->max_buffer_size == 0 ||
		    stream->buffer_size < stream->max_buffer_size)
			i_stream_grow_buffer(stream, CHUNK_SIZE);

		if (stream->pos == stream->buffer_size) {
			if (stream->skip > 0) {
				/* lose our buffer cache */
				i_stream_compress(stream);
			}

			if (stream->pos == stream->buffer_size)
				return -2; /* buffer full */
		}
	}

	if (zstream->zs.avail_in == 0) {
		/* need to read more data. try to read a full CHUNK_SIZE */
		i_stream_skip(stream->parent, zstream->prev_size);
		if (i_stream_read_data(stream->parent, &data, &size,
				       CHUNK_SIZE-1) == -1 && size == 0) {
			if (stream->parent->stream_errno != 0) {
				stream->istream.stream_errno =
					stream->parent->stream_errno;
			} else {
				i_assert(stream->parent->eof);
				if (zstream->log_errors) {
					bzlib_read_error(zstream,
							 "unexpected EOF");
				}
				stream->istream.stream_errno = EINVAL;
			}
			return -1;
		}
		zstream->prev_size = size;
		if (size == 0) {
			/* no more input */
			i_assert(!stream->istream.blocking);
			return 0;
		}

		zstream->zs.next_in = (char *)data;
		zstream->zs.avail_in = size;
	}

	size = stream->buffer_size - stream->pos;
	zstream->zs.next_out = (char *)stream->w_buffer + stream->pos;
	zstream->zs.avail_out = size;
	ret = BZ2_bzDecompress(&zstream->zs);

	size -= zstream->zs.avail_out;
	stream->pos += size;

	switch (ret) {
	case BZ_OK:
		break;
	case BZ_PARAM_ERROR:
		i_unreached();
	case BZ_DATA_ERROR:
		if (zstream->log_errors)
			bzlib_read_error(zstream, "corrupted data");
		stream->istream.stream_errno = EINVAL;
		return -1;
	case BZ_DATA_ERROR_MAGIC:
		if (zstream->log_errors) {
			bzlib_read_error(zstream,
				"wrong magic in header (not bz2 file?)");
		}
		stream->istream.stream_errno = EINVAL;
		return -1;
	case BZ_MEM_ERROR:
		i_fatal_status(FATAL_OUTOFMEM, "bzlib.read(%s): Out of memory",
			       i_stream_get_name(&stream->istream));
	case BZ_STREAM_END:
		zstream->eof_offset = stream->istream.v_offset +
			(stream->pos - stream->skip);
		if (size == 0) {
			stream->istream.eof = TRUE;
			return -1;
		}
		break;
	default:
		i_fatal("BZ2_bzDecompress() failed with %d", ret);
	}
	if (size == 0) {
		/* read more input */
		return i_stream_bzlib_read(stream);
	}
	return size;
}
示例#6
0
static ssize_t i_stream_ssl_read_real(struct istream_private *stream)
{
	struct ssl_istream *sstream = (struct ssl_istream *)stream;
	struct ssl_iostream *ssl_io = sstream->ssl_io;
	unsigned char buffer[IO_BLOCK_SIZE];
	size_t max_buffer_size = i_stream_get_max_buffer_size(&stream->istream);
	size_t orig_max_buffer_size = stream->max_buffer_size;
	size_t size;
	ssize_t ret, total_ret;

	if (sstream->seen_eof) {
		stream->istream.eof = TRUE;
		return -1;
	}

	if (stream->pos >= max_buffer_size) {
		i_stream_compress(stream);
		if (stream->pos >= max_buffer_size)
			return -2;
	}

	ret = openssl_iostream_more(ssl_io);
	if (ret <= 0) {
		if (ret < 0) {
			/* handshake failed */
			i_assert(errno != 0);
			io_stream_set_error(&stream->iostream,
					    "%s", ssl_io->last_error);
			stream->istream.stream_errno = errno;
		}
		return ret;
	}

	if (!i_stream_try_alloc(stream, 1, &size))
		i_unreached();
	if (stream->pos + size > max_buffer_size) {
		i_assert(max_buffer_size > stream->pos);
		size = max_buffer_size - stream->pos;
	}

	while ((ret = SSL_read(ssl_io->ssl,
			       stream->w_buffer + stream->pos, size)) <= 0) {
		/* failed to read anything */
		ret = openssl_iostream_handle_error(ssl_io, ret, "SSL_read");
		if (ret <= 0) {
			if (ret == 0)
				return 0;
			if (ssl_io->last_error != NULL) {
				io_stream_set_error(&stream->iostream,
						    "%s", ssl_io->last_error);
			}
			if (errno != EPIPE)
				stream->istream.stream_errno = errno;
			stream->istream.eof = TRUE;
			sstream->seen_eof = TRUE;
			return -1;
		}
		/* we did some BIO I/O, try reading again */
	}
	stream->pos += ret;
	total_ret = ret;

	/* now make sure that we read everything already buffered in OpenSSL
	   into the stream (without reading anything more). this makes I/O loop
	   behave similary for ssl-istream as file-istream. */
	sstream->ssl_io->input_handler = FALSE;
	stream->max_buffer_size = (size_t)-1;
	while ((ret = SSL_read(ssl_io->ssl, buffer, sizeof(buffer))) > 0) {
		memcpy(i_stream_alloc(stream, ret), buffer, ret);
		stream->pos += ret;
		total_ret += ret;
	}
	stream->max_buffer_size = orig_max_buffer_size;
	return total_ret;
}