bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen#include "lib.h"
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen#ifdef HAVE_LZMA
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen#include "istream-private.h"
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen#include "istream-zlib.h"
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen#include <lzma.h>
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen#define CHUNK_SIZE (1024*64)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen#define LZMA_MEMORY_LIMIT (1024*1024*80)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainenstruct lzma_istream {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen struct istream_private istream;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen lzma_stream strm;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen uoff_t eof_offset, stream_size;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen size_t high_pos;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen struct stat last_parent_statbuf;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool log_errors:1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool marked:1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool strm_closed:1;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen};
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainenstatic void i_stream_lzma_close(struct iostream_private *stream,
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen bool close_parent)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen{
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen struct lzma_istream *zstream = (struct lzma_istream *)stream;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (!zstream->strm_closed) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen lzma_end(&zstream->strm);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->strm_closed = TRUE;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (close_parent)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_stream_close(zstream->istream.parent);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen}
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainenstatic void lzma_read_error(struct lzma_istream *zstream, const char *error)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen{
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen io_stream_set_error(&zstream->istream.iostream,
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen "lzma.read(%s): %s at %"PRIuUOFF_T,
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_stream_get_name(&zstream->istream.istream), error,
d28179fd78550a58be44dcb1e3e830ab7d33172dTimo Sirainen i_stream_get_absolute_offset(&zstream->istream.istream));
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (zstream->log_errors)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_error("%s", zstream->istream.iostream.error);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen}
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
62c05ba0290a0bf9c2b0ab4ce117f9a736907c21Timo Sirainenstatic void lzma_stream_end(struct lzma_istream *zstream)
62c05ba0290a0bf9c2b0ab4ce117f9a736907c21Timo Sirainen{
62c05ba0290a0bf9c2b0ab4ce117f9a736907c21Timo Sirainen zstream->eof_offset = zstream->istream.istream.v_offset +
62c05ba0290a0bf9c2b0ab4ce117f9a736907c21Timo Sirainen (zstream->istream.pos - zstream->istream.skip);
62c05ba0290a0bf9c2b0ab4ce117f9a736907c21Timo Sirainen zstream->stream_size = zstream->eof_offset;
62c05ba0290a0bf9c2b0ab4ce117f9a736907c21Timo Sirainen}
62c05ba0290a0bf9c2b0ab4ce117f9a736907c21Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainenstatic ssize_t i_stream_lzma_read(struct istream_private *stream)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen{
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen struct lzma_istream *zstream = (struct lzma_istream *)stream;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen const unsigned char *data;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen uoff_t high_offset;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen size_t size, out_size;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen lzma_ret ret;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen high_offset = stream->istream.v_offset + (stream->pos - stream->skip);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (zstream->eof_offset == high_offset) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_assert(zstream->high_pos == 0 ||
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->high_pos == stream->pos);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->istream.eof = TRUE;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return -1;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (stream->pos < zstream->high_pos) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen /* we're here because we seeked back within the read buffer. */
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen ret = zstream->high_pos - stream->pos;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->pos = zstream->high_pos;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->high_pos = 0;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (zstream->eof_offset != (uoff_t)-1) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen high_offset = stream->istream.v_offset +
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen (stream->pos - stream->skip);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_assert(zstream->eof_offset == high_offset);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->istream.eof = TRUE;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return ret;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->high_pos = 0;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
ef4661aec4031e24944175ab914dc60942a2b68aTimo Sirainen if (!zstream->marked) {
ef4661aec4031e24944175ab914dc60942a2b68aTimo Sirainen if (!i_stream_try_alloc(stream, CHUNK_SIZE, &out_size))
ef4661aec4031e24944175ab914dc60942a2b68aTimo Sirainen return -2; /* buffer full */
ef4661aec4031e24944175ab914dc60942a2b68aTimo Sirainen } else {
ef4661aec4031e24944175ab914dc60942a2b68aTimo Sirainen /* try to avoid compressing, so we can quickly seek backwards */
ef4661aec4031e24944175ab914dc60942a2b68aTimo Sirainen if (!i_stream_try_alloc_avoid_compress(stream, CHUNK_SIZE, &out_size))
ef4661aec4031e24944175ab914dc60942a2b68aTimo Sirainen return -2; /* buffer full */
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
3858a7a5da361c35f1e6e50c8e3214dc0cf379d6Phil Carmody if (i_stream_read_more(stream->parent, &data, &size) < 0) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (stream->parent->stream_errno != 0) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->istream.stream_errno =
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->parent->stream_errno;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen } else {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_assert(stream->parent->eof);
62c05ba0290a0bf9c2b0ab4ce117f9a736907c21Timo Sirainen lzma_stream_end(zstream);
62c05ba0290a0bf9c2b0ab4ce117f9a736907c21Timo Sirainen stream->istream.eof = TRUE;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return -1;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (size == 0) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen /* no more input */
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_assert(!stream->istream.blocking);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return 0;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->strm.next_in = data;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->strm.avail_in = size;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->strm.next_out = stream->w_buffer + stream->pos;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->strm.avail_out = out_size;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen ret = lzma_code(&zstream->strm, LZMA_RUN);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen out_size -= zstream->strm.avail_out;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->pos += out_size;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_stream_skip(stream->parent, size - zstream->strm.avail_in);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen switch (ret) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen case LZMA_OK:
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen break;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen case LZMA_DATA_ERROR:
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen case LZMA_BUF_ERROR:
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen lzma_read_error(zstream, "corrupted data");
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->istream.stream_errno = EINVAL;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return -1;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen case LZMA_FORMAT_ERROR:
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen lzma_read_error(zstream, "wrong magic in header (not xz file?)");
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->istream.stream_errno = EINVAL;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return -1;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen case LZMA_OPTIONS_ERROR:
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen lzma_read_error(zstream, "Unsupported xz options");
ad31916e2ad230d0e553203a5461bf7a8dc0d816Timo Sirainen stream->istream.stream_errno = EIO;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return -1;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen case LZMA_MEM_ERROR:
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_fatal_status(FATAL_OUTOFMEM, "lzma.read(%s): Out of memory",
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_stream_get_name(&stream->istream));
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen case LZMA_STREAM_END:
62c05ba0290a0bf9c2b0ab4ce117f9a736907c21Timo Sirainen lzma_stream_end(zstream);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (out_size == 0) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->istream.eof = TRUE;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return -1;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen break;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen default:
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen lzma_read_error(zstream, t_strdup_printf(
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen "lzma_code() failed with %d", ret));
ad31916e2ad230d0e553203a5461bf7a8dc0d816Timo Sirainen stream->istream.stream_errno = EIO;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return -1;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (out_size == 0) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen /* read more input */
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return i_stream_lzma_read(stream);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return out_size;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen}
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainenstatic void i_stream_lzma_init(struct lzma_istream *zstream)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen{
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen lzma_ret ret;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen ret = lzma_stream_decoder(&zstream->strm, LZMA_MEMORY_LIMIT,
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen LZMA_CONCATENATED);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen switch (ret) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen case LZMA_OK:
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen break;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen case LZMA_MEM_ERROR:
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_fatal_status(FATAL_OUTOFMEM, "lzma: Out of memory");
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen default:
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_fatal("lzma_stream_decoder() failed with ret=%d", ret);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen}
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainenstatic void i_stream_lzma_reset(struct lzma_istream *zstream)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen{
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen struct istream_private *stream = &zstream->istream;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_stream_seek(stream->parent, stream->parent_start_offset);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->eof_offset = (uoff_t)-1;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->strm.next_in = NULL;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->strm.avail_in = 0;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->parent_expected_offset = stream->parent_start_offset;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->skip = stream->pos = 0;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->istream.v_offset = 0;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->high_pos = 0;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen lzma_end(&zstream->strm);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_stream_lzma_init(zstream);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen}
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainenstatic void
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Siraineni_stream_lzma_seek(struct istream_private *stream, uoff_t v_offset, bool mark)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen{
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen struct lzma_istream *zstream = (struct lzma_istream *) stream;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen uoff_t start_offset = stream->istream.v_offset - stream->skip;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (v_offset < start_offset) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen /* have to seek backwards */
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_stream_lzma_reset(zstream);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen start_offset = 0;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen } else if (zstream->high_pos != 0) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->pos = zstream->high_pos;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->high_pos = 0;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (v_offset <= start_offset + stream->pos) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen /* seeking backwards within what's already cached */
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->skip = v_offset - start_offset;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->istream.v_offset = v_offset;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->high_pos = stream->pos;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->pos = stream->skip;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen } else {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen /* read and cache forward */
9dadffcd2545eab4b251e83b60cee78ceb1e8362Anton Yuzhaninov ssize_t ret;
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen do {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen size_t avail = stream->pos - stream->skip;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (stream->istream.v_offset + avail >= v_offset) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_stream_skip(&stream->istream,
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen v_offset -
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->istream.v_offset);
9dadffcd2545eab4b251e83b60cee78ceb1e8362Anton Yuzhaninov ret = -1;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen break;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_stream_skip(&stream->istream, avail);
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen } while ((ret = i_stream_read(&stream->istream)) > 0);
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen i_assert(ret == -1);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (stream->istream.v_offset != v_offset) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen /* some failure, we've broken it */
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (stream->istream.stream_errno != 0) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_error("lzma_istream.seek(%s) failed: %s",
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_stream_get_name(&stream->istream),
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen strerror(stream->istream.stream_errno));
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_stream_close(&stream->istream);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen } else {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen /* unexpected EOF. allow it since we may just
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen want to check if there's anything.. */
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_assert(stream->istream.eof);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (mark)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->marked = TRUE;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen}
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainenstatic int
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Siraineni_stream_lzma_stat(struct istream_private *stream, bool exact)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen{
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen struct lzma_istream *zstream = (struct lzma_istream *) stream;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen const struct stat *st;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen size_t size;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen if (i_stream_stat(stream->parent, exact, &st) < 0) {
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen stream->istream.stream_errno = stream->parent->stream_errno;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return -1;
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->statbuf = *st;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen /* when exact=FALSE always return the parent stat's size, even if we
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen know the exact value. this is necessary because otherwise e.g. mbox
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen code can see two different values and think that a compressed mbox
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen file keeps changing. */
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (!exact)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return 0;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (zstream->stream_size == (uoff_t)-1) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen uoff_t old_offset = stream->istream.v_offset;
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen ssize_t ret;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen do {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen size = i_stream_get_data_size(&stream->istream);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_stream_skip(&stream->istream, size);
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen } while ((ret = i_stream_read(&stream->istream)) > 0);
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen i_assert(ret == -1);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_stream_seek(&stream->istream, old_offset);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (zstream->stream_size == (uoff_t)-1)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return -1;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen stream->statbuf.st_size = zstream->stream_size;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return 0;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen}
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainenstatic void i_stream_lzma_sync(struct istream_private *stream)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen{
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen struct lzma_istream *zstream = (struct lzma_istream *) stream;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen const struct stat *st;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (i_stream_stat(stream->parent, FALSE, &st) < 0) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen if (memcmp(&zstream->last_parent_statbuf,
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen st, sizeof(*st)) == 0) {
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen /* a compressed file doesn't change unexpectedly,
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen don't clear our caches unnecessarily */
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->last_parent_statbuf = *st;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen }
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_stream_lzma_reset(zstream);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen}
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainenstruct istream *i_stream_create_lzma(struct istream *input, bool log_errors)
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen{
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen struct lzma_istream *zstream;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream = i_new(struct lzma_istream, 1);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->eof_offset = (uoff_t)-1;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->stream_size = (uoff_t)-1;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->log_errors = log_errors;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen i_stream_lzma_init(zstream);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->istream.iostream.close = i_stream_lzma_close;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->istream.read = i_stream_lzma_read;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->istream.seek = i_stream_lzma_seek;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->istream.stat = i_stream_lzma_stat;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->istream.sync = i_stream_lzma_sync;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->istream.istream.readable_fd = FALSE;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->istream.istream.blocking = input->blocking;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen zstream->istream.istream.seekable = input->seekable;
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen return i_stream_create(&zstream->istream, input,
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen i_stream_get_fd(input), 0);
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen}
12983e9d3b4ebdfb1e14f197c153304b3af44b59Timo Sirainen#endif