istream-lz4.c revision 7f74811b78f8915e73dffc88bb49009e98b6846d
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen/* Copyright (c) 2013-2016 Dovecot authors, see the included COPYING file */
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen uint32_t chunk_size, chunk_left, max_uncompressed_chunk_size;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainenstatic void i_stream_lz4_close(struct iostream_private *stream,
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen struct lz4_istream *zstream = (struct lz4_istream *)stream;
6dad0888fcec8372f230941c70d8940b8c203b32Stephan Boschstatic void lz4_read_error(struct lz4_istream *zstream, const char *error)
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen io_stream_set_error(&zstream->istream.iostream,
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch i_stream_get_name(&zstream->istream.istream), error,
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch i_stream_get_absolute_offset(&zstream->istream.istream));
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch i_error("%s", zstream->istream.iostream.error);
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainenstatic int i_stream_lz4_read_header(struct lz4_istream *zstream)
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen const unsigned char *data;
6dad0888fcec8372f230941c70d8940b8c203b32Stephan Bosch ret = i_stream_read_bytes(zstream->istream.parent, &data, &size,
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen if (ret == 0 && !zstream->istream.istream.eof)
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen if (ret == 0 || memcmp(hdr->magic, IOSTREAM_LZ4_MAGIC,
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen lz4_read_error(zstream, "wrong magic in header (not lz4 file?)");
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch zstream->istream.istream.stream_errno = EINVAL;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen ((uint32_t)hdr->max_uncompressed_chunk_size[0] << 24) |
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen if (zstream->max_uncompressed_chunk_size > ISTREAM_LZ4_CHUNK_SIZE) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch "lz4 max chunk size too large (%u > %u)",
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch zstream->istream.istream.stream_errno = EINVAL;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen i_stream_skip(zstream->istream.parent, sizeof(*hdr));
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Boschstatic ssize_t i_stream_lz4_read(struct istream_private *stream)
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen struct lz4_istream *zstream = (struct lz4_istream *)stream;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen const unsigned char *data;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if ((ret = i_stream_lz4_read_header(zstream)) <= 0)
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen ret = i_stream_read_bytes(stream->parent, &data, &size,
1175415b88ff168e367c77df23901eada13225b9Stephan Bosch zstream->stream_size = stream->istream.v_offset +
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen zstream->chunk_size > ISTREAM_LZ4_CHUNK_SIZE) {
e8f1e510df3ab051a816715c2056f0d10aee929eStephan Bosch "invalid lz4 chunk size: %u", zstream->chunk_size));
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen i_stream_skip(stream->parent, IOSTREAM_LZ4_CHUNK_PREFIX_LEN);
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch /* read the whole compressed chunk into memory */
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch (ret = i_stream_read_more(zstream->istream.parent, &data, &size)) > 0) {
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch buffer_append(zstream->chunk_buf, data, size);
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen if (ret == -1 && zstream->istream.parent->stream_errno == 0) {
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch lz4_read_error(zstream, "truncated lz4 chunk");
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen /* if we already have max_buffer_size amount of data, fail here */
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen if (stream->pos >= i_stream_get_max_buffer_size(&stream->istream))
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen /* allocate enough space for the old data and the new
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen decompressed chunk. we don't know the original compressed size,
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen so just allocate the max amount of memory. */
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch max_size = stream->pos + zstream->max_uncompressed_chunk_size;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen stream->w_buffer = i_realloc(stream->w_buffer,
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch ret = LZ4_decompress_safe(zstream->chunk_buf->data,
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen i_assert(ret <= (int)zstream->max_uncompressed_chunk_size);
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen lz4_read_error(zstream, "corrupted lz4 chunk");
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainenstatic void i_stream_lz4_reset(struct lz4_istream *zstream)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch struct istream_private *stream = &zstream->istream;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen i_stream_seek(stream->parent, stream->parent_start_offset);
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen zstream->chunk_size = zstream->chunk_left = 0;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen stream->parent_expected_offset = stream->parent_start_offset;
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Boschi_stream_lz4_seek(struct istream_private *stream, uoff_t v_offset, bool mark)
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch struct lz4_istream *zstream = (struct lz4_istream *) stream;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen uoff_t start_offset = stream->istream.v_offset - stream->skip;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen /* have to seek backwards */
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen /* seeking backwards within what's already cached */
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch /* read and cache forward */
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch if (stream->istream.v_offset + avail >= v_offset) {
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch } while ((ret = i_stream_read(&stream->istream)) > 0);
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch /* some failure, we've broken it */
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch /* unexpected EOF. allow it since we may just
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch want to check if there's anything.. */
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Boschi_stream_lz4_stat(struct istream_private *stream, bool exact)
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch struct lz4_istream *zstream = (struct lz4_istream *) stream;
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch if (i_stream_stat(stream->parent, exact, &st) < 0) {
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch stream->istream.stream_errno = stream->parent->stream_errno;
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch /* when exact=FALSE always return the parent stat's size, even if we
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch know the exact value. this is necessary because otherwise e.g. mbox
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch code can see two different values and think that a compressed mbox
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch file keeps changing. */
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch size = i_stream_get_data_size(&stream->istream);
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch } while ((ret = i_stream_read(&stream->istream)) > 0);
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch stream->statbuf.st_size = zstream->stream_size;
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Boschstatic void i_stream_lz4_sync(struct istream_private *stream)
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch struct lz4_istream *zstream = (struct lz4_istream *) stream;
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch if (i_stream_stat(stream->parent, FALSE, &st) < 0) {
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch /* a compressed file doesn't change unexpectedly,
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch don't clear our caches unnecessarily */
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainenstruct istream *i_stream_create_lz4(struct istream *input, bool log_errors)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch zstream->istream.iostream.close = i_stream_lz4_close;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen zstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch zstream->istream.istream.blocking = input->blocking;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch zstream->istream.istream.seekable = input->seekable;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch zstream->chunk_buf = buffer_create_dynamic(default_pool, 1024);