bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include "lib.h"
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#ifdef HAVE_LZ4
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include "buffer.h"
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include "istream-private.h"
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include "istream-zlib.h"
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include "iostream-lz4.h"
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include <lz4.h>
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstruct lz4_istream {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct istream_private istream;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen uoff_t stream_size;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct stat last_parent_statbuf;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen buffer_t *chunk_buf;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen uint32_t chunk_size, chunk_left, max_uncompressed_chunk_size;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool log_errors:1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool marked:1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool header_read:1;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen};
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic void i_stream_lz4_close(struct iostream_private *stream,
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen bool close_parent)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen{
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct lz4_istream *zstream = (struct lz4_istream *)stream;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
6307d76096764e66bddc63d4a3e5a1aa19cc528fJosef 'Jeff' Sipek buffer_free(&zstream->chunk_buf);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (close_parent)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_stream_close(zstream->istream.parent);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen}
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic void lz4_read_error(struct lz4_istream *zstream, const char *error)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen{
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen io_stream_set_error(&zstream->istream.iostream,
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen "lz4.read(%s): %s at %"PRIuUOFF_T,
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_stream_get_name(&zstream->istream.istream), error,
d28179fd78550a58be44dcb1e3e830ab7d33172dTimo Sirainen i_stream_get_absolute_offset(&zstream->istream.istream));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (zstream->log_errors)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_error("%s", zstream->istream.iostream.error);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen}
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic int i_stream_lz4_read_header(struct lz4_istream *zstream)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen{
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen const struct iostream_lz4_header *hdr;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen const unsigned char *data;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen size_t size;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen int ret;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
573424407a2d3c1453638a643583a7cf10c129e1Phil Carmody ret = i_stream_read_bytes(zstream->istream.parent, &data, &size,
573424407a2d3c1453638a643583a7cf10c129e1Phil Carmody sizeof(*hdr));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (ret < 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->istream.istream.stream_errno =
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->istream.parent->stream_errno;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return ret;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (ret == 0 && !zstream->istream.istream.eof)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return 0;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen hdr = (const void *)data;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (ret == 0 || memcmp(hdr->magic, IOSTREAM_LZ4_MAGIC,
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen IOSTREAM_LZ4_MAGIC_LEN) != 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen lz4_read_error(zstream, "wrong magic in header (not lz4 file?)");
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->istream.istream.stream_errno = EINVAL;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return -1;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->max_uncompressed_chunk_size =
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_read_error(zstream, t_strdup_printf(
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen "lz4 max chunk size too large (%u > %u)",
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->max_uncompressed_chunk_size,
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen ISTREAM_LZ4_CHUNK_SIZE));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->istream.istream.stream_errno = EINVAL;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return -1;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_stream_skip(zstream->istream.parent, sizeof(*hdr));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return 1;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen}
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic ssize_t i_stream_lz4_read(struct istream_private *stream)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen{
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct lz4_istream *zstream = (struct lz4_istream *)stream;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen const unsigned char *data;
e05039894bb8fa68d6e5bc2c36fd870463bcd09fTimo Sirainen size_t size;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen int ret;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (!zstream->header_read) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if ((ret = i_stream_lz4_read_header(zstream)) <= 0)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return ret;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->header_read = TRUE;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (zstream->chunk_left == 0) {
d868a04630bd7bfe9c1543a7c3f68703b3e276e4Timo Sirainen ret = i_stream_read_bytes(stream->parent, &data, &size,
d868a04630bd7bfe9c1543a7c3f68703b3e276e4Timo Sirainen IOSTREAM_LZ4_CHUNK_PREFIX_LEN);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (ret < 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->istream.stream_errno =
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->parent->stream_errno;
131b0d222ad89b2c5d2b03b865b45cae9e290d68Timo Sirainen if (stream->istream.stream_errno == 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->istream.eof = TRUE;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->stream_size = stream->istream.v_offset +
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->pos - stream->skip;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return ret;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (ret == 0 && !stream->istream.eof)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return 0;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->chunk_size = zstream->chunk_left =
b9e830a81455faf3c0dadfc9dbf0c7dc8aca955cJosef 'Jeff' Sipek be32_to_cpu_unaligned(data);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (zstream->chunk_size == 0 ||
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->chunk_size > ISTREAM_LZ4_CHUNK_SIZE) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen lz4_read_error(zstream, t_strdup_printf(
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen "invalid lz4 chunk size: %u", zstream->chunk_size));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->istream.stream_errno = EINVAL;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return -1;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_stream_skip(stream->parent, IOSTREAM_LZ4_CHUNK_PREFIX_LEN);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen buffer_set_used_size(zstream->chunk_buf, 0);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* read the whole compressed chunk into memory */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen while (zstream->chunk_left > 0 &&
9184983183ae28fb543695c54c85bc5396c07e42Phil Carmody (ret = i_stream_read_more(zstream->istream.parent, &data, &size)) > 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (size > zstream->chunk_left)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen size = zstream->chunk_left;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen buffer_append(zstream->chunk_buf, data, size);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_stream_skip(zstream->istream.parent, size);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->chunk_left -= size;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (zstream->chunk_left > 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (ret == -1 && zstream->istream.parent->stream_errno == 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen lz4_read_error(zstream, "truncated lz4 chunk");
b7cabbb8fd1d662008faf20d2ec93e4be51f1e96Timo Sirainen stream->istream.stream_errno = EPIPE;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return -1;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->istream.istream.stream_errno =
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->istream.parent->stream_errno;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return ret;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
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 return -2;
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 zstream->chunk_buf->used,
e05039894bb8fa68d6e5bc2c36fd870463bcd09fTimo Sirainen zstream->max_uncompressed_chunk_size);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_assert(ret <= (int)zstream->max_uncompressed_chunk_size);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (ret < 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen lz4_read_error(zstream, "corrupted lz4 chunk");
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->istream.stream_errno = EINVAL;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return -1;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_assert(ret > 0);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->pos += ret;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_assert(stream->pos <= stream->buffer_size);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return ret;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen}
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic void i_stream_lz4_reset(struct lz4_istream *zstream)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen{
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct istream_private *stream = &zstream->istream;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_stream_seek(stream->parent, stream->parent_start_offset);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->header_read = FALSE;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->chunk_size = zstream->chunk_left = 0;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->parent_expected_offset = stream->parent_start_offset;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->skip = stream->pos = 0;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->istream.v_offset = 0;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen}
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic void
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Siraineni_stream_lz4_seek(struct istream_private *stream, uoff_t v_offset, bool mark)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen{
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct lz4_istream *zstream = (struct lz4_istream *) stream;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen uoff_t start_offset = stream->istream.v_offset - stream->skip;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (v_offset < start_offset) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* have to seek backwards */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_stream_lz4_reset(zstream);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen start_offset = 0;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (v_offset <= start_offset + stream->pos) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* seeking backwards within what's already cached */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->skip = v_offset - start_offset;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->istream.v_offset = v_offset;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->pos = stream->skip;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen } else {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* read and cache forward */
9dadffcd2545eab4b251e83b60cee78ceb1e8362Anton Yuzhaninov ssize_t ret;
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen do {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen size_t avail = stream->pos - stream->skip;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (stream->istream.v_offset + avail >= v_offset) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_stream_skip(&stream->istream,
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen v_offset -
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->istream.v_offset);
9dadffcd2545eab4b251e83b60cee78ceb1e8362Anton Yuzhaninov ret = -1;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen break;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_stream_skip(&stream->istream, avail);
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen } while ((ret = i_stream_read(&stream->istream)) > 0);
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen i_assert(ret == -1);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (stream->istream.v_offset != v_offset) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* some failure, we've broken it */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (stream->istream.stream_errno != 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_error("lz4_istream.seek(%s) failed: %s",
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_stream_get_name(&stream->istream),
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen strerror(stream->istream.stream_errno));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_stream_close(&stream->istream);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen } else {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* unexpected EOF. allow it since we may just
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen want to check if there's anything.. */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_assert(stream->istream.eof);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (mark)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->marked = TRUE;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen}
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic int
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Siraineni_stream_lz4_stat(struct istream_private *stream, bool exact)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen{
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct lz4_istream *zstream = (struct lz4_istream *) stream;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen const struct stat *st;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen size_t size;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen if (i_stream_stat(stream->parent, exact, &st) < 0) {
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen stream->istream.stream_errno = stream->parent->stream_errno;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return -1;
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->statbuf = *st;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
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 if (!exact)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return 0;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (zstream->stream_size == (uoff_t)-1) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen uoff_t old_offset = stream->istream.v_offset;
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen ssize_t ret;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen do {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen size = i_stream_get_data_size(&stream->istream);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_stream_skip(&stream->istream, size);
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen } while ((ret = i_stream_read(&stream->istream)) > 0);
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen i_assert(ret == -1);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_stream_seek(&stream->istream, old_offset);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (zstream->stream_size == (uoff_t)-1)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return -1;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen stream->statbuf.st_size = zstream->stream_size;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return 0;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen}
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic void i_stream_lz4_sync(struct istream_private *stream)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen{
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct lz4_istream *zstream = (struct lz4_istream *) stream;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen const struct stat *st;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (i_stream_stat(stream->parent, FALSE, &st) < 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (memcmp(&zstream->last_parent_statbuf,
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen st, sizeof(*st)) == 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* a compressed file doesn't change unexpectedly,
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen don't clear our caches unnecessarily */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->last_parent_statbuf = *st;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_stream_lz4_reset(zstream);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen}
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstruct istream *i_stream_create_lz4(struct istream *input, bool log_errors)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen{
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct lz4_istream *zstream;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream = i_new(struct lz4_istream, 1);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->stream_size = (uoff_t)-1;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->log_errors = log_errors;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
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.read = i_stream_lz4_read;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->istream.seek = i_stream_lz4_seek;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->istream.stat = i_stream_lz4_stat;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->istream.sync = i_stream_lz4_sync;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen zstream->istream.istream.readable_fd = FALSE;
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);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return i_stream_create(&zstream->istream, input,
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen i_stream_get_fd(input), 0);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen}
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#endif