istream-lzma.c revision ad31916e2ad230d0e553203a5461bf7a8dc0d816
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2010-2016 Dovecot authors, see the included COPYING file */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include "lib.h"
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen#ifdef HAVE_LZMA
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include "istream-private.h"
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include "istream-zlib.h"
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include <lzma.h>
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#define CHUNK_SIZE (1024*64)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
2cd2518bab14292a67cf8a490b58ab9ef89879daTimo Sirainen#define LZMA_MEMORY_LIMIT (1024*1024*80)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstruct lzma_istream {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen struct istream_private istream;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
6ec925f52d04ec8700e47cb005bd7ddc65ac5614Timo Sirainen lzma_stream strm;
c60178b2610a9b193ff72aa18448398ef72529a1Timo Sirainen uoff_t eof_offset, stream_size;
4d4d6d4745682790c20d759ba93dbea46b812c5dTimo Sirainen size_t high_pos;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen struct stat last_parent_statbuf;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen bool log_errors:1;
cf2e6953d03a1c22f272ec19432fc03c136ac1bbTimo Sirainen bool marked:1;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen bool strm_closed:1;
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen};
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic void i_stream_lzma_close(struct iostream_private *stream,
5323bec1c24184863a13bc14a2dc9487093eea3dTimo Sirainen bool close_parent)
5323bec1c24184863a13bc14a2dc9487093eea3dTimo Sirainen{
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen struct lzma_istream *zstream = (struct lzma_istream *)stream;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
617e13833c798435e2be425b99c27ecaad1b8393Timo Sirainen if (!zstream->strm_closed) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen lzma_end(&zstream->strm);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->strm_closed = TRUE;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (close_parent)
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen i_stream_close(zstream->istream.parent);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen}
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic void lzma_read_error(struct lzma_istream *zstream, const char *error)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen io_stream_set_error(&zstream->istream.iostream,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen "lzma.read(%s): %s at %"PRIuUOFF_T,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_stream_get_name(&zstream->istream.istream), error,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_stream_get_absolute_offset(&zstream->istream.istream));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (zstream->log_errors)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_error("%s", zstream->istream.iostream.error);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen}
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic void lzma_stream_end(struct lzma_istream *zstream)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->eof_offset = zstream->istream.istream.v_offset +
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen (zstream->istream.pos - zstream->istream.skip);
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen zstream->stream_size = zstream->eof_offset;
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen}
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainenstatic ssize_t i_stream_lzma_read(struct istream_private *stream)
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen{
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen struct lzma_istream *zstream = (struct lzma_istream *)stream;
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen const unsigned char *data;
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen uoff_t high_offset;
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen size_t size, out_size;
230ef558135f16a66b86cbe3762524eaa9ae9d81Timo Sirainen lzma_ret ret;
230ef558135f16a66b86cbe3762524eaa9ae9d81Timo Sirainen
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen high_offset = stream->istream.v_offset + (stream->pos - stream->skip);
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen if (zstream->eof_offset == high_offset) {
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen i_assert(zstream->high_pos == 0 ||
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen zstream->high_pos == stream->pos);
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen stream->istream.eof = TRUE;
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen return -1;
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen }
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen if (stream->pos < zstream->high_pos) {
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen /* we're here because we seeked back within the read buffer. */
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen ret = zstream->high_pos - stream->pos;
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen stream->pos = zstream->high_pos;
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen zstream->high_pos = 0;
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen if (zstream->eof_offset != (uoff_t)-1) {
617e13833c798435e2be425b99c27ecaad1b8393Timo Sirainen high_offset = stream->istream.v_offset +
617e13833c798435e2be425b99c27ecaad1b8393Timo Sirainen (stream->pos - stream->skip);
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen i_assert(zstream->eof_offset == high_offset);
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen stream->istream.eof = TRUE;
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen }
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen return ret;
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen }
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen zstream->high_pos = 0;
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen if (stream->pos + CHUNK_SIZE > stream->buffer_size) {
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen /* try to keep at least CHUNK_SIZE available */
617e13833c798435e2be425b99c27ecaad1b8393Timo Sirainen if (!zstream->marked && stream->skip > 0) {
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen /* don't try to keep anything cached if we don't
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen have a seek mark. */
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen i_stream_compress(stream);
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen }
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen if (stream->buffer_size < i_stream_get_max_buffer_size(&stream->istream))
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen i_stream_grow_buffer(stream, CHUNK_SIZE);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (stream->pos == stream->buffer_size) {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (stream->skip > 0) {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen /* lose our buffer cache */
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_stream_compress(stream);
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen }
efc5c69c572e83db7bf7eab5d4698c0ab0d3d886Timo Sirainen
efc5c69c572e83db7bf7eab5d4698c0ab0d3d886Timo Sirainen if (stream->pos == stream->buffer_size)
efc5c69c572e83db7bf7eab5d4698c0ab0d3d886Timo Sirainen return -2; /* buffer full */
efc5c69c572e83db7bf7eab5d4698c0ab0d3d886Timo Sirainen }
efc5c69c572e83db7bf7eab5d4698c0ab0d3d886Timo Sirainen }
efc5c69c572e83db7bf7eab5d4698c0ab0d3d886Timo Sirainen
efc5c69c572e83db7bf7eab5d4698c0ab0d3d886Timo Sirainen if (i_stream_read_more(stream->parent, &data, &size) < 0) {
efc5c69c572e83db7bf7eab5d4698c0ab0d3d886Timo Sirainen if (stream->parent->stream_errno != 0) {
dc07b75b7ea83ff5f447970a20419032725271a7Timo Sirainen stream->istream.stream_errno =
efc5c69c572e83db7bf7eab5d4698c0ab0d3d886Timo Sirainen stream->parent->stream_errno;
efc5c69c572e83db7bf7eab5d4698c0ab0d3d886Timo Sirainen } else {
efc5c69c572e83db7bf7eab5d4698c0ab0d3d886Timo Sirainen i_assert(stream->parent->eof);
efc5c69c572e83db7bf7eab5d4698c0ab0d3d886Timo Sirainen lzma_stream_end(zstream);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen stream->istream.eof = TRUE;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return -1;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (size == 0) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* no more input */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_assert(!stream->istream.blocking);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return 0;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->strm.next_in = data;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->strm.avail_in = size;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen out_size = stream->buffer_size - stream->pos;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->strm.next_out = stream->w_buffer + stream->pos;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->strm.avail_out = out_size;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen ret = lzma_code(&zstream->strm, LZMA_RUN);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen out_size -= zstream->strm.avail_out;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen stream->pos += out_size;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
601b455f4d5e780044b9e4fac5f687c1b07ae145Timo Sirainen i_stream_skip(stream->parent, size - zstream->strm.avail_in);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen switch (ret) {
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen case LZMA_OK:
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen break;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen case LZMA_DATA_ERROR:
fb7ac3e31c92627efe076318319976ac1c27ae2aTimo Sirainen case LZMA_BUF_ERROR:
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen lzma_read_error(zstream, "corrupted data");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen stream->istream.stream_errno = EINVAL;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return -1;
72cc352b25ad401b923436c6ed0f1f3adaffa737Timo Sirainen case LZMA_FORMAT_ERROR:
e1f866daa1bd1a5cd7516f3b19c6f197bcf6cc8aTimo Sirainen lzma_read_error(zstream, "wrong magic in header (not xz file?)");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen stream->istream.stream_errno = EINVAL;
614529ee060755c0b282102b70daf56bcd64222dTimo Sirainen return -1;
61618d4c58080570f689614fec204ae14e90cef2Timo Sirainen case LZMA_OPTIONS_ERROR:
6ec925f52d04ec8700e47cb005bd7ddc65ac5614Timo Sirainen lzma_read_error(zstream, "Unsupported xz options");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen stream->istream.stream_errno = EIO;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return -1;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen case LZMA_MEM_ERROR:
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_fatal_status(FATAL_OUTOFMEM, "lzma.read(%s): Out of memory",
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_stream_get_name(&stream->istream));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen case LZMA_STREAM_END:
e1f866daa1bd1a5cd7516f3b19c6f197bcf6cc8aTimo Sirainen lzma_stream_end(zstream);
e1f866daa1bd1a5cd7516f3b19c6f197bcf6cc8aTimo Sirainen if (out_size == 0) {
e1f866daa1bd1a5cd7516f3b19c6f197bcf6cc8aTimo Sirainen stream->istream.eof = TRUE;
e1f866daa1bd1a5cd7516f3b19c6f197bcf6cc8aTimo Sirainen return -1;
e1f866daa1bd1a5cd7516f3b19c6f197bcf6cc8aTimo Sirainen }
e1f866daa1bd1a5cd7516f3b19c6f197bcf6cc8aTimo Sirainen break;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen default:
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen lzma_read_error(zstream, t_strdup_printf(
72cc352b25ad401b923436c6ed0f1f3adaffa737Timo Sirainen "lzma_code() failed with %d", ret));
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen stream->istream.stream_errno = EIO;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen return -1;
72cc352b25ad401b923436c6ed0f1f3adaffa737Timo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (out_size == 0) {
72cc352b25ad401b923436c6ed0f1f3adaffa737Timo Sirainen /* read more input */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return i_stream_lzma_read(stream);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return out_size;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen}
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
bad5fa318c6c1384ab83bd72d53ce06593274c18Timo Sirainenstatic void i_stream_lzma_init(struct lzma_istream *zstream)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen lzma_ret ret;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen ret = lzma_stream_decoder(&zstream->strm, LZMA_MEMORY_LIMIT,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen LZMA_CONCATENATED);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen switch (ret) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen case LZMA_OK:
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen break;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen case LZMA_MEM_ERROR:
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_fatal_status(FATAL_OUTOFMEM, "lzma: Out of memory");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen default:
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_fatal("lzma_stream_decoder() failed with ret=%d", ret);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen}
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic void i_stream_lzma_reset(struct lzma_istream *zstream)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen struct istream_private *stream = &zstream->istream;
f5e1d3d6b34ec152aa1ff15c7bd3d3552e9227eaTimo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_stream_seek(stream->parent, stream->parent_start_offset);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->eof_offset = (uoff_t)-1;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->strm.next_in = NULL;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->strm.avail_in = 0;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
bad5fa318c6c1384ab83bd72d53ce06593274c18Timo Sirainen stream->parent_expected_offset = stream->parent_start_offset;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen stream->skip = stream->pos = 0;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen stream->istream.v_offset = 0;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->high_pos = 0;
3a0f9aa9504497e4e47f32df54fbf47fdc7423b6Timo Sirainen
3a0f9aa9504497e4e47f32df54fbf47fdc7423b6Timo Sirainen lzma_end(&zstream->strm);
788f275469ad9ed530e440d6690d0e4381a323b2Timo Sirainen i_stream_lzma_init(zstream);
788f275469ad9ed530e440d6690d0e4381a323b2Timo Sirainen}
788f275469ad9ed530e440d6690d0e4381a323b2Timo Sirainen
788f275469ad9ed530e440d6690d0e4381a323b2Timo Sirainenstatic void
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Siraineni_stream_lzma_seek(struct istream_private *stream, uoff_t v_offset, bool mark)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen struct lzma_istream *zstream = (struct lzma_istream *) stream;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen uoff_t start_offset = stream->istream.v_offset - stream->skip;
43d7e7ce608f5451e4907b5f5c48c00beb265802Timo Sirainen
43d7e7ce608f5451e4907b5f5c48c00beb265802Timo Sirainen if (v_offset < start_offset) {
43d7e7ce608f5451e4907b5f5c48c00beb265802Timo Sirainen /* have to seek backwards */
43d7e7ce608f5451e4907b5f5c48c00beb265802Timo Sirainen i_stream_lzma_reset(zstream);
43d7e7ce608f5451e4907b5f5c48c00beb265802Timo Sirainen start_offset = 0;
43d7e7ce608f5451e4907b5f5c48c00beb265802Timo Sirainen } else if (zstream->high_pos != 0) {
43d7e7ce608f5451e4907b5f5c48c00beb265802Timo Sirainen stream->pos = zstream->high_pos;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->high_pos = 0;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (v_offset <= start_offset + stream->pos) {
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen /* seeking backwards within what's already cached */
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen stream->skip = v_offset - start_offset;
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen stream->istream.v_offset = v_offset;
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen zstream->high_pos = stream->pos;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen stream->pos = stream->skip;
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen } else {
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen /* read and cache forward */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen do {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen size_t avail = stream->pos - stream->skip;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (stream->istream.v_offset + avail >= v_offset) {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_stream_skip(&stream->istream,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen v_offset -
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen stream->istream.v_offset);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen break;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen i_stream_skip(&stream->istream, avail);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen } while (i_stream_read(&stream->istream) >= 0);
601b455f4d5e780044b9e4fac5f687c1b07ae145Timo Sirainen
601b455f4d5e780044b9e4fac5f687c1b07ae145Timo Sirainen if (stream->istream.v_offset != v_offset) {
cdc8485491045d82bb98405d4b995f277d12838eTimo Sirainen /* some failure, we've broken it */
cdc8485491045d82bb98405d4b995f277d12838eTimo Sirainen if (stream->istream.stream_errno != 0) {
b2ed2b25c4c457ec1c99ebe5e9bd66a2e2f89cfdTimo Sirainen i_error("lzma_istream.seek(%s) failed: %s",
b2ed2b25c4c457ec1c99ebe5e9bd66a2e2f89cfdTimo Sirainen i_stream_get_name(&stream->istream),
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen strerror(stream->istream.stream_errno));
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen i_stream_close(&stream->istream);
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen } else {
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen /* unexpected EOF. allow it since we may just
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen want to check if there's anything.. */
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen i_assert(stream->istream.eof);
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen }
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen }
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen }
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen if (mark)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->marked = TRUE;
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen}
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic int
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Siraineni_stream_lzma_stat(struct istream_private *stream, bool exact)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen struct lzma_istream *zstream = (struct lzma_istream *) stream;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen const struct stat *st;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen size_t size;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (i_stream_stat(stream->parent, exact, &st) < 0) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen stream->istream.stream_errno = stream->parent->stream_errno;
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen return -1;
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen stream->statbuf = *st;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* when exact=FALSE always return the parent stat's size, even if we
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen know the exact value. this is necessary because otherwise e.g. mbox
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen code can see two different values and think that a compressed mbox
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen file keeps changing. */
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen if (!exact)
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen return 0;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
3a0f9aa9504497e4e47f32df54fbf47fdc7423b6Timo Sirainen if (zstream->stream_size == (uoff_t)-1) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen uoff_t old_offset = stream->istream.v_offset;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen do {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen size = i_stream_get_data_size(&stream->istream);
bad5fa318c6c1384ab83bd72d53ce06593274c18Timo Sirainen i_stream_skip(&stream->istream, size);
bad5fa318c6c1384ab83bd72d53ce06593274c18Timo Sirainen } while (i_stream_read(&stream->istream) > 0);
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen i_stream_seek(&stream->istream, old_offset);
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen if (zstream->stream_size == (uoff_t)-1)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return -1;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen stream->statbuf.st_size = zstream->stream_size;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return 0;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen}
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
bad5fa318c6c1384ab83bd72d53ce06593274c18Timo Sirainenstatic void i_stream_lzma_sync(struct istream_private *stream)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
2cd2518bab14292a67cf8a490b58ab9ef89879daTimo Sirainen struct lzma_istream *zstream = (struct lzma_istream *) stream;
bad5fa318c6c1384ab83bd72d53ce06593274c18Timo Sirainen const struct stat *st;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (i_stream_stat(stream->parent, FALSE, &st) < 0) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (memcmp(&zstream->last_parent_statbuf,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen st, sizeof(*st)) == 0) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* a compressed file doesn't change unexpectedly,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen don't clear our caches unnecessarily */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
d8552f9f65e5ff64be5de9faf9a8171799a0bbecTimo Sirainen zstream->last_parent_statbuf = *st;
d8552f9f65e5ff64be5de9faf9a8171799a0bbecTimo Sirainen }
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen i_stream_lzma_reset(zstream);
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen}
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstruct istream *i_stream_create_lzma(struct istream *input, bool log_errors)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen struct lzma_istream *zstream;
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen zstream = i_new(struct lzma_istream, 1);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen zstream->eof_offset = (uoff_t)-1;
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen zstream->stream_size = (uoff_t)-1;
bad5fa318c6c1384ab83bd72d53ce06593274c18Timo Sirainen zstream->log_errors = log_errors;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_stream_lzma_init(zstream);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->istream.iostream.close = i_stream_lzma_close;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->istream.read = i_stream_lzma_read;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->istream.seek = i_stream_lzma_seek;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->istream.stat = i_stream_lzma_stat;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->istream.sync = i_stream_lzma_sync;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->istream.istream.readable_fd = FALSE;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->istream.istream.blocking = input->blocking;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zstream->istream.istream.seekable = input->seekable;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return i_stream_create(&zstream->istream, input,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_stream_get_fd(input));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen}
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#endif
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen