istream-lzma.c revision 9dadffcd2545eab4b251e83b60cee78ceb1e8362
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan#include "lib.h"
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher#ifdef HAVE_LZMA
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek#include "istream-private.h"
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek#include "istream-zlib.h"
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan#include <lzma.h>
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher#define CHUNK_SIZE (1024*64)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
0172959f117b545c8a6b1893f5f56818d82dd624Jakub Hrozek#define LZMA_MEMORY_LIMIT (1024*1024*80)
0172959f117b545c8a6b1893f5f56818d82dd624Jakub Hrozek
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagherstruct lzma_istream {
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher struct istream_private istream;
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan lzma_stream strm;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan uoff_t eof_offset, stream_size;
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher size_t high_pos;
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher struct stat last_parent_statbuf;
7a14e8f66c0e932fe2954d792614a3b61d444bd1Jakub Hrozek
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan bool log_errors:1;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher bool marked:1;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher bool strm_closed:1;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher};
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozek
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozekstatic void i_stream_lzma_close(struct iostream_private *stream,
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozek bool close_parent)
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozek{
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozek struct lzma_istream *zstream = (struct lzma_istream *)stream;
2ea6196484055397cc4bc011c5960f790431fa9dStephen Gallagher
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (!zstream->strm_closed) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan lzma_end(&zstream->strm);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan zstream->strm_closed = TRUE;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher }
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (close_parent)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_stream_close(zstream->istream.parent);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan}
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagherstatic void lzma_read_error(struct lzma_istream *zstream, const char *error)
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher{
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher io_stream_set_error(&zstream->istream.iostream,
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher "lzma.read(%s): %s at %"PRIuUOFF_T,
2ea6196484055397cc4bc011c5960f790431fa9dStephen Gallagher i_stream_get_name(&zstream->istream.istream), error,
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozek i_stream_get_absolute_offset(&zstream->istream.istream));
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (zstream->log_errors)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_error("%s", zstream->istream.iostream.error);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan}
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanstatic void lzma_stream_end(struct lzma_istream *zstream)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan{
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan zstream->eof_offset = zstream->istream.istream.v_offset +
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan (zstream->istream.pos - zstream->istream.skip);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher zstream->stream_size = zstream->eof_offset;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan}
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagherstatic ssize_t i_stream_lzma_read(struct istream_private *stream)
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher{
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher struct lzma_istream *zstream = (struct lzma_istream *)stream;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan const unsigned char *data;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher uoff_t high_offset;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher size_t size, out_size;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher lzma_ret ret;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher high_offset = stream->istream.v_offset + (stream->pos - stream->skip);
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher if (zstream->eof_offset == high_offset) {
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher i_assert(zstream->high_pos == 0 ||
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozek zstream->high_pos == stream->pos);
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozek stream->istream.eof = TRUE;
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozek return -1;
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozek }
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozek
2ea6196484055397cc4bc011c5960f790431fa9dStephen Gallagher if (stream->pos < zstream->high_pos) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* we're here because we seeked back within the read buffer. */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan ret = zstream->high_pos - stream->pos;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan stream->pos = zstream->high_pos;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher zstream->high_pos = 0;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (zstream->eof_offset != (uoff_t)-1) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan high_offset = stream->istream.v_offset +
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan (stream->pos - stream->skip);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_assert(zstream->eof_offset == high_offset);
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan stream->istream.eof = TRUE;
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan }
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan return ret;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher }
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher zstream->high_pos = 0;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher if (stream->pos + CHUNK_SIZE > stream->buffer_size) {
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozek /* try to keep at least CHUNK_SIZE available */
ea929f1b022fc2cb77dec89b0e12accef983ec85Jakub Hrozek if (!zstream->marked && stream->skip > 0) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* don't try to keep anything cached if we don't
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan have a seek mark. */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_stream_compress(stream);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher }
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (stream->buffer_size < i_stream_get_max_buffer_size(&stream->istream))
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_stream_grow_buffer(stream, CHUNK_SIZE);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher if (stream->pos == stream->buffer_size) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (stream->skip > 0) {
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher /* lose our buffer cache */
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher i_stream_compress(stream);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan }
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (stream->pos == stream->buffer_size)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan return -2; /* buffer full */
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher }
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher }
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (i_stream_read_more(stream->parent, &data, &size) < 0) {
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher if (stream->parent->stream_errno != 0) {
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher stream->istream.stream_errno =
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan stream->parent->stream_errno;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan } else {
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher i_assert(stream->parent->eof);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan lzma_stream_end(zstream);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan stream->istream.eof = TRUE;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher }
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher return -1;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan }
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher if (size == 0) {
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher /* no more input */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_assert(!stream->istream.blocking);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher return 0;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan }
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher zstream->strm.next_in = data;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher zstream->strm.avail_in = size;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher out_size = stream->buffer_size - stream->pos;
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher zstream->strm.next_out = stream->w_buffer + stream->pos;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan zstream->strm.avail_out = out_size;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher ret = lzma_code(&zstream->strm, LZMA_RUN);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan out_size -= zstream->strm.avail_out;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan stream->pos += out_size;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher i_stream_skip(stream->parent, size - zstream->strm.avail_in);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozek switch (ret) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan case LZMA_OK:
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan break;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan case LZMA_DATA_ERROR:
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher case LZMA_BUF_ERROR:
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher lzma_read_error(zstream, "corrupted data");
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozek stream->istream.stream_errno = EINVAL;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan return -1;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan case LZMA_FORMAT_ERROR:
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan lzma_read_error(zstream, "wrong magic in header (not xz file?)");
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher stream->istream.stream_errno = EINVAL;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher return -1;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek case LZMA_OPTIONS_ERROR:
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan lzma_read_error(zstream, "Unsupported xz options");
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan stream->istream.stream_errno = EIO;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan return -1;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher case LZMA_MEM_ERROR:
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_fatal_status(FATAL_OUTOFMEM, "lzma.read(%s): Out of memory",
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_stream_get_name(&stream->istream));
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan case LZMA_STREAM_END:
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan lzma_stream_end(zstream);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher if (out_size == 0) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan stream->istream.eof = TRUE;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan return -1;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan }
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan break;
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher default:
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher lzma_read_error(zstream, t_strdup_printf(
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan "lzma_code() failed with %d", ret));
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan stream->istream.stream_errno = EIO;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan return -1;
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher }
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher if (out_size == 0) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* read more input */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan return i_stream_lzma_read(stream);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher }
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan return out_size;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan}
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanstatic void i_stream_lzma_init(struct lzma_istream *zstream)
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher{
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher lzma_ret ret;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan ret = lzma_stream_decoder(&zstream->strm, LZMA_MEMORY_LIMIT,
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan LZMA_CONCATENATED);
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan switch (ret) {
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher case LZMA_OK:
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher break;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan case LZMA_MEM_ERROR:
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher i_fatal_status(FATAL_OUTOFMEM, "lzma: Out of memory");
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan default:
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_fatal("lzma_stream_decoder() failed with ret=%d", ret);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan }
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan}
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivanstatic void i_stream_lzma_reset(struct lzma_istream *zstream)
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan{
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan struct istream_private *stream = &zstream->istream;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_stream_seek(stream->parent, stream->parent_start_offset);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan zstream->eof_offset = (uoff_t)-1;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan zstream->strm.next_in = NULL;
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher zstream->strm.avail_in = 0;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher stream->parent_expected_offset = stream->parent_start_offset;
7797e361155f7ce937085fd98e360469d7baf1b6Jakub Hrozek stream->skip = stream->pos = 0;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan stream->istream.v_offset = 0;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher zstream->high_pos = 0;
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan lzma_end(&zstream->strm);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_stream_lzma_init(zstream);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan}
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagherstatic void
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagheri_stream_lzma_seek(struct istream_private *stream, uoff_t v_offset, bool mark)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan{
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher struct lzma_istream *zstream = (struct lzma_istream *) stream;
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher uoff_t start_offset = stream->istream.v_offset - stream->skip;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (v_offset < start_offset) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* have to seek backwards */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_stream_lzma_reset(zstream);
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan start_offset = 0;
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan } else if (zstream->high_pos != 0) {
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan stream->pos = zstream->high_pos;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan zstream->high_pos = 0;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher }
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (v_offset <= start_offset + stream->pos) {
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan /* seeking backwards within what's already cached */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan stream->skip = v_offset - start_offset;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher stream->istream.v_offset = v_offset;
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher zstream->high_pos = stream->pos;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan stream->pos = stream->skip;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan } else {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* read and cache forward */
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher ssize_t ret;
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan do {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan size_t avail = stream->pos - stream->skip;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher if (stream->istream.v_offset + avail >= v_offset) {
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher i_stream_skip(&stream->istream,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan v_offset -
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan stream->istream.v_offset);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan ret = -1;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher break;
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher }
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_stream_skip(&stream->istream, avail);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan } while ((ret = i_stream_read(&stream->istream)) > 0);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_assert(ret == -1);
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan if (stream->istream.v_offset != v_offset) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* some failure, we've broken it */
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher if (stream->istream.stream_errno != 0) {
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher i_error("lzma_istream.seek(%s) failed: %s",
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_stream_get_name(&stream->istream),
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan strerror(stream->istream.stream_errno));
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_stream_close(&stream->istream);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher } else {
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher /* unexpected EOF. allow it since we may just
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan want to check if there's anything.. */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_assert(stream->istream.eof);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan }
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan }
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan }
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher if (mark)
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher zstream->marked = TRUE;
056302a92862fda16351d7192600746746f38e5dStephen Gallagher}
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagherstatic int
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagheri_stream_lzma_stat(struct istream_private *stream, bool exact)
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher{
056302a92862fda16351d7192600746746f38e5dStephen Gallagher struct lzma_istream *zstream = (struct lzma_istream *) stream;
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher const struct stat *st;
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher size_t size;
7797e361155f7ce937085fd98e360469d7baf1b6Jakub Hrozek
7797e361155f7ce937085fd98e360469d7baf1b6Jakub Hrozek if (i_stream_stat(stream->parent, exact, &st) < 0) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan stream->istream.stream_errno = stream->parent->stream_errno;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher return -1;
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek }
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan stream->statbuf = *st;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* when exact=FALSE always return the parent stat's size, even if we
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher know the exact value. this is necessary because otherwise e.g. mbox
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek code can see two different values and think that a compressed mbox
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan file keeps changing. */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (!exact)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan return 0;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher if (zstream->stream_size == (uoff_t)-1) {
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher uoff_t old_offset = stream->istream.v_offset;
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher ssize_t ret;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher do {
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek size = i_stream_get_data_size(&stream->istream);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_stream_skip(&stream->istream, size);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan } while ((ret = i_stream_read(&stream->istream)) > 0);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_assert(ret == -1);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher i_stream_seek(&stream->istream, old_offset);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (zstream->stream_size == (uoff_t)-1)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan return -1;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan }
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher stream->statbuf.st_size = zstream->stream_size;
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher return 0;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan}
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanstatic void i_stream_lzma_sync(struct istream_private *stream)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan{
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek struct lzma_istream *zstream = (struct lzma_istream *) stream;
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek const struct stat *st;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan if (i_stream_stat(stream->parent, FALSE, &st) < 0) {
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan if (memcmp(&zstream->last_parent_statbuf,
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan st, sizeof(*st)) == 0) {
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher /* a compressed file doesn't change unexpectedly,
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek don't clear our caches unnecessarily */
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek return;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan }
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher zstream->last_parent_statbuf = *st;
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek }
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_stream_lzma_reset(zstream);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan}
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagherstruct istream *i_stream_create_lzma(struct istream *input, bool log_errors)
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek{
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan struct lzma_istream *zstream;
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher zstream = i_new(struct lzma_istream, 1);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan zstream->eof_offset = (uoff_t)-1;
7797e361155f7ce937085fd98e360469d7baf1b6Jakub Hrozek zstream->stream_size = (uoff_t)-1;
7797e361155f7ce937085fd98e360469d7baf1b6Jakub Hrozek zstream->log_errors = log_errors;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher i_stream_lzma_init(zstream);
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan zstream->istream.iostream.close = i_stream_lzma_close;
524ceecc11f3d458eb3c1cf1489c3ff6ccb22226Jakub Hrozek zstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
524ceecc11f3d458eb3c1cf1489c3ff6ccb22226Jakub Hrozek zstream->istream.read = i_stream_lzma_read;
524ceecc11f3d458eb3c1cf1489c3ff6ccb22226Jakub Hrozek zstream->istream.seek = i_stream_lzma_seek;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan zstream->istream.stat = i_stream_lzma_stat;
e5c33e0bd03a2deb8e5011deeb3ae93f960910eeJakub Hrozek zstream->istream.sync = i_stream_lzma_sync;
e5c33e0bd03a2deb8e5011deeb3ae93f960910eeJakub Hrozek
e5c33e0bd03a2deb8e5011deeb3ae93f960910eeJakub Hrozek zstream->istream.istream.readable_fd = FALSE;
e5c33e0bd03a2deb8e5011deeb3ae93f960910eeJakub Hrozek zstream->istream.istream.blocking = input->blocking;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan zstream->istream.istream.seekable = input->seekable;
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek return i_stream_create(&zstream->istream, input,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_stream_get_fd(input));
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan}
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan#endif
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek