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