diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
commit | f7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch) | |
tree | a3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/lib-compression/istream-decompress.c | |
parent | Initial commit. (diff) | |
download | dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.zip |
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib-compression/istream-decompress.c')
-rw-r--r-- | src/lib-compression/istream-decompress.c | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/src/lib-compression/istream-decompress.c b/src/lib-compression/istream-decompress.c new file mode 100644 index 0000000..2021a01 --- /dev/null +++ b/src/lib-compression/istream-decompress.c @@ -0,0 +1,258 @@ +/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream-private.h" +#include "compression.h" + +struct decompress_istream { + struct istream_private istream; + struct istream *compressed_input; + struct istream *decompressed_input; + enum istream_decompress_flags flags; +}; + +static void copy_compressed_input_error(struct decompress_istream *zstream) +{ + struct istream_private *stream = &zstream->istream; + + stream->istream.stream_errno = zstream->compressed_input->stream_errno; + stream->istream.eof = zstream->compressed_input->eof; + if (zstream->compressed_input->stream_errno != 0) { + io_stream_set_error(&stream->iostream, "%s", + i_stream_get_error(&zstream->compressed_input->real_stream->istream)); + } +} + +static void copy_decompressed_input_error(struct decompress_istream *zstream) +{ + struct istream_private *stream = &zstream->istream; + + stream->istream.stream_errno = zstream->decompressed_input->stream_errno; + stream->istream.eof = zstream->decompressed_input->eof; + if (zstream->decompressed_input->stream_errno != 0) { + io_stream_set_error(&stream->iostream, "%s", + i_stream_get_error(&zstream->decompressed_input->real_stream->istream)); + } +} + +static void +i_stream_decompress_close(struct iostream_private *_stream, bool close_parent) +{ + struct istream_private *stream = + container_of(_stream, struct istream_private, iostream); + struct decompress_istream *zstream = + container_of(stream, struct decompress_istream, istream); + + if (zstream->decompressed_input != NULL) + i_stream_close(zstream->decompressed_input); + if (close_parent) + i_stream_close(zstream->compressed_input); +} + +static void +i_stream_decompress_destroy(struct iostream_private *_stream) +{ + struct istream_private *stream = + container_of(_stream, struct istream_private, iostream); + struct decompress_istream *zstream = + container_of(stream, struct decompress_istream, istream); + + i_stream_unref(&zstream->decompressed_input); + i_stream_unref(&zstream->compressed_input); +} + +static int +i_stream_decompress_not_compressed(struct decompress_istream *zstream) +{ + if ((zstream->flags & ISTREAM_DECOMPRESS_FLAG_TRY) == 0) { + zstream->istream.istream.stream_errno = EINVAL; + io_stream_set_error(&zstream->istream.iostream, + "Stream isn't compressed"); + return -1; + } else { + zstream->decompressed_input = zstream->compressed_input; + i_stream_ref(zstream->decompressed_input); + return 1; + } +} + +static int i_stream_decompress_detect(struct decompress_istream *zstream) +{ + const struct compression_handler *handler; + ssize_t ret; + + ret = i_stream_read(zstream->compressed_input); + handler = compression_detect_handler(zstream->compressed_input); + if (handler == NULL) { + switch (ret) { + case -1: + if (zstream->compressed_input->stream_errno != 0) { + copy_compressed_input_error(zstream); + return -1; + } + /* fall through */ + case -2: + /* we've read a full buffer or we reached EOF - + the stream isn't compressed */ + return i_stream_decompress_not_compressed(zstream); + case 0: + return 0; + default: + if (!zstream->istream.istream.blocking) + return 0; + return i_stream_decompress_detect(zstream); + } + } + if (handler->create_istream == NULL) { + zstream->istream.istream.stream_errno = EINVAL; + io_stream_set_error(&zstream->istream.iostream, + "Compression handler %s not supported", handler->name); + return -1; + } + + zstream->decompressed_input = + handler->create_istream(zstream->compressed_input); + return 1; +} + +static ssize_t i_stream_decompress_read(struct istream_private *stream) +{ + struct decompress_istream *zstream = + container_of(stream, struct decompress_istream, istream); + ssize_t ret; + size_t pos; + + if (zstream->decompressed_input == NULL) { + if ((ret = i_stream_decompress_detect(zstream)) <= 0) + return ret; + } + + i_stream_seek(zstream->decompressed_input, stream->istream.v_offset); + stream->pos -= stream->skip; + stream->skip = 0; + + stream->buffer = i_stream_get_data(zstream->decompressed_input, &pos); + if (pos > stream->pos) + ret = 0; + else do { + ret = i_stream_read_memarea(zstream->decompressed_input); + copy_decompressed_input_error(zstream); + stream->buffer = i_stream_get_data(zstream->decompressed_input, + &pos); + } while (pos <= stream->pos && ret > 0); + if (ret == -2) + return -2; + + if (pos <= stream->pos) + ret = ret == 0 ? 0 : -1; + else + ret = (ssize_t)(pos - stream->pos); + stream->pos = pos; + i_assert(ret != -1 || stream->istream.eof || + stream->istream.stream_errno != 0); + return ret; +} + +static void i_stream_decompress_reset(struct istream_private *stream) +{ + stream->skip = stream->pos = 0; + stream->istream.v_offset = 0; + stream->istream.eof = FALSE; +} + +static void +i_stream_decompress_seek(struct istream_private *stream, + uoff_t v_offset, bool mark) +{ + struct decompress_istream *zstream = + container_of(stream, struct decompress_istream, istream); + + if (zstream->decompressed_input == NULL) { + if (!i_stream_nonseekable_try_seek(stream, v_offset)) + i_panic("seeking backwards before detecting compression format"); + } else { + i_stream_decompress_reset(stream); + stream->istream.v_offset = v_offset; + if (mark) + i_stream_seek_mark(zstream->decompressed_input, v_offset); + else + i_stream_seek(zstream->decompressed_input, v_offset); + copy_decompressed_input_error(zstream); + } +} + +static void i_stream_decompress_sync(struct istream_private *stream) +{ + struct decompress_istream *zstream = + container_of(stream, struct decompress_istream, istream); + + i_stream_decompress_reset(stream); + if (zstream->decompressed_input != NULL) + i_stream_sync(zstream->decompressed_input); +} + +static int i_stream_decompress_stat(struct istream_private *stream, bool exact) +{ + struct decompress_istream *zstream = + container_of(stream, struct decompress_istream, istream); + const struct stat *st; + + if (!exact) { + if (i_stream_stat(zstream->compressed_input, exact, &st) < 0) { + copy_compressed_input_error(zstream); + return -1; + } + stream->statbuf = *st; + return 0; + } + if (zstream->decompressed_input == NULL) { + (void)i_stream_read(&stream->istream); + if (zstream->decompressed_input == NULL) { + if (stream->istream.stream_errno == 0) { + zstream->istream.istream.stream_errno = EINVAL; + io_stream_set_error(&zstream->istream.iostream, + "Stream compression couldn't be detected during stat"); + } + return -1; + } + } + + if (i_stream_stat(zstream->decompressed_input, exact, &st) < 0) { + copy_decompressed_input_error(zstream); + return -1; + } + i_stream_decompress_reset(stream); + stream->statbuf = *st; + return 0; +} + +struct istream * +i_stream_create_decompress(struct istream *input, + enum istream_decompress_flags flags) +{ + struct decompress_istream *zstream; + + zstream = i_new(struct decompress_istream, 1); + zstream->compressed_input = input; + zstream->flags = flags; + i_stream_ref(input); + + zstream->istream.iostream.close = i_stream_decompress_close; + zstream->istream.iostream.destroy = i_stream_decompress_destroy; + zstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + zstream->istream.read = i_stream_decompress_read; + zstream->istream.seek = i_stream_decompress_seek; + zstream->istream.sync = i_stream_decompress_sync; + zstream->istream.stat = i_stream_decompress_stat; + + zstream->istream.istream.readable_fd = FALSE; + zstream->istream.istream.blocking = input->blocking; + zstream->istream.istream.seekable = input->seekable; + + struct istream *ret = i_stream_create(&zstream->istream, NULL, + i_stream_get_fd(input), 0); + /* input isn't used as our parent istream, so need to copy the stream + name to preserve it. */ + i_stream_set_name(ret, i_stream_get_name(input)); + return ret; +} |