1
/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */
8
#include "istream-private.h"
9
#include "istream-zlib.h"
12
#define CHUNK_SIZE (1024*64)
14
#define GZ_HEADER_MIN_SIZE 10
15
#define GZ_TRAILER_SIZE 8
17
#define GZ_MAGIC1 0x1f
18
#define GZ_MAGIC2 0x8b
19
#define GZ_FLAG_FHCRC 0x02
20
#define GZ_FLAG_FEXTRA 0x04
21
#define GZ_FLAG_FNAME 0x08
22
#define GZ_FLAG_FCOMMENT 0x10
25
struct istream_private istream;
28
uoff_t eof_offset, stream_size;
29
size_t prev_size, high_pos;
31
struct stat last_parent_statbuf;
34
unsigned int log_errors:1;
35
unsigned int marked:1;
36
unsigned int header_read:1;
37
unsigned int trailer_read:1;
38
unsigned int zs_closed:1;
41
static void i_stream_zlib_init(struct zlib_istream *zstream);
43
static void i_stream_zlib_close(struct iostream_private *stream)
45
struct zlib_istream *zstream = (struct zlib_istream *)stream;
47
if (!zstream->zs_closed) {
48
(void)inflateEnd(&zstream->zs);
49
zstream->zs_closed = TRUE;
53
static void zlib_read_error(struct zlib_istream *zstream, const char *error)
55
i_error("zlib.read(%s): %s at %"PRIuUOFF_T,
56
i_stream_get_name(&zstream->istream.istream), error,
57
zstream->istream.abs_start_offset +
58
zstream->istream.istream.v_offset);
61
static int i_stream_zlib_read_header(struct istream_private *stream)
63
struct zlib_istream *zstream = (struct zlib_istream *)stream;
64
const unsigned char *data;
66
unsigned int pos, fextra_size;
69
ret = i_stream_read_data(stream->parent, &data, &size,
71
if (size == zstream->prev_size) {
73
if (zstream->log_errors)
74
zlib_read_error(zstream, "missing gz header");
75
stream->istream.stream_errno = EINVAL;
79
zstream->prev_size = size;
81
if (size < GZ_HEADER_MIN_SIZE)
83
pos = GZ_HEADER_MIN_SIZE;
85
if (data[0] != GZ_MAGIC1 || data[1] != GZ_MAGIC2) {
86
/* missing gzip magic header */
87
if (zstream->log_errors) {
88
zlib_read_error(zstream, "wrong magic in header "
91
stream->istream.stream_errno = EINVAL;
94
if ((data[3] & GZ_FLAG_FEXTRA) != 0) {
98
fextra_size = data[pos] + (data[pos+1] << 8);
100
if (pos + fextra_size < size)
104
if ((data[3] & GZ_FLAG_FNAME) != 0) {
108
} while (data[pos++] != '\0');
110
if ((data[3] & GZ_FLAG_FCOMMENT) != 0) {
114
} while (data[pos++] != '\0');
116
if ((data[3] & GZ_FLAG_FHCRC) != 0) {
121
i_stream_skip(stream->parent, pos);
122
zstream->prev_size = 0;
126
static uint32_t data_get_uint32(const unsigned char *data)
128
return data[0] | (data[1] << 8) | (data[2] << 16) |
129
((uint32_t)data[3] << 24);
132
static int i_stream_zlib_read_trailer(struct zlib_istream *zstream)
134
struct istream_private *stream = &zstream->istream;
135
const unsigned char *data;
139
ret = i_stream_read_data(stream->parent, &data, &size,
141
if (size == zstream->prev_size) {
143
if (zstream->log_errors)
144
zlib_read_error(zstream, "missing gz trailer");
145
stream->istream.stream_errno = EINVAL;
149
zstream->prev_size = size;
151
if (size < GZ_TRAILER_SIZE)
154
if (data_get_uint32(data) != zstream->crc32) {
155
if (zstream->log_errors) {
156
zlib_read_error(zstream,
157
"gz trailer has wrong CRC value");
159
stream->istream.stream_errno = EINVAL;
162
i_stream_skip(stream->parent, GZ_TRAILER_SIZE);
163
zstream->prev_size = 0;
164
zstream->trailer_read = TRUE;
168
static ssize_t i_stream_zlib_read(struct istream_private *stream)
170
struct zlib_istream *zstream = (struct zlib_istream *)stream;
171
const unsigned char *data;
173
size_t size, out_size;
176
high_offset = stream->istream.v_offset + (stream->pos - stream->skip);
177
if (zstream->eof_offset == high_offset) {
178
i_assert(zstream->high_pos == 0 ||
179
zstream->high_pos == stream->pos);
180
if (!zstream->trailer_read) {
182
ret = i_stream_zlib_read_trailer(zstream);
183
} while (ret == 0 && stream->istream.blocking);
187
if (!zstream->gz || i_stream_is_eof(stream->parent)) {
188
stream->istream.eof = TRUE;
191
/* gzip file with concatenated content */
192
zstream->eof_offset = (uoff_t)-1;
193
zstream->stream_size = (uoff_t)-1;
194
zstream->header_read = FALSE;
195
zstream->trailer_read = FALSE;
198
(void)inflateEnd(&zstream->zs);
199
i_stream_zlib_init(zstream);
202
if (!zstream->header_read) {
204
ret = i_stream_zlib_read_header(stream);
205
} while (ret == 0 && stream->istream.blocking);
208
zstream->header_read = TRUE;
211
if (stream->pos < zstream->high_pos) {
212
/* we're here because we seeked back within the read buffer. */
213
ret = zstream->high_pos - stream->pos;
214
stream->pos = zstream->high_pos;
215
zstream->high_pos = 0;
216
if (zstream->trailer_read) {
217
high_offset = stream->istream.v_offset +
218
(stream->pos - stream->skip);
219
i_assert(zstream->eof_offset == high_offset);
220
stream->istream.eof = TRUE;
224
zstream->high_pos = 0;
226
if (stream->pos + CHUNK_SIZE > stream->buffer_size) {
227
/* try to keep at least CHUNK_SIZE available */
228
if (!zstream->marked && stream->skip > 0) {
229
/* don't try to keep anything cached if we don't
231
i_stream_compress(stream);
233
if (stream->max_buffer_size == 0 ||
234
stream->buffer_size < stream->max_buffer_size)
235
i_stream_grow_buffer(stream, CHUNK_SIZE);
237
if (stream->pos == stream->buffer_size) {
238
if (stream->skip > 0) {
239
/* lose our buffer cache */
240
i_stream_compress(stream);
243
if (stream->pos == stream->buffer_size)
244
return -2; /* buffer full */
248
if (i_stream_read_data(stream->parent, &data, &size, 0) < 0) {
249
if (stream->parent->stream_errno != 0) {
250
stream->istream.stream_errno =
251
stream->parent->stream_errno;
253
i_assert(stream->parent->eof);
254
if (zstream->log_errors)
255
zlib_read_error(zstream, "unexpected EOF");
256
stream->istream.stream_errno = EPIPE;
262
i_assert(!stream->istream.blocking);
266
zstream->zs.next_in = (void *)data;
267
zstream->zs.avail_in = size;
269
out_size = stream->buffer_size - stream->pos;
270
zstream->zs.next_out = stream->w_buffer + stream->pos;
271
zstream->zs.avail_out = out_size;
272
ret = inflate(&zstream->zs, Z_SYNC_FLUSH);
274
out_size -= zstream->zs.avail_out;
275
zstream->crc32 = crc32_data_more(zstream->crc32,
276
stream->w_buffer + stream->pos,
278
stream->pos += out_size;
280
i_stream_skip(stream->parent, size - zstream->zs.avail_in);
286
if (zstream->log_errors)
287
zlib_read_error(zstream, "can't read file without dict");
288
stream->istream.stream_errno = EINVAL;
291
if (zstream->log_errors)
292
zlib_read_error(zstream, "corrupted data");
293
stream->istream.stream_errno = EINVAL;
296
i_fatal_status(FATAL_OUTOFMEM, "zlib.read(%s): Out of memory",
297
i_stream_get_name(&stream->istream));
299
zstream->eof_offset = stream->istream.v_offset +
300
(stream->pos - stream->skip);
301
zstream->stream_size = zstream->eof_offset;
302
zstream->zs.avail_in = 0;
304
if (!zstream->trailer_read) {
305
/* try to read and verify the trailer, we might not
307
if (i_stream_zlib_read_trailer(zstream) < 0)
312
i_fatal("inflate() failed with %d", ret);
315
/* read more input */
316
return i_stream_zlib_read(stream);
321
static void i_stream_zlib_init(struct zlib_istream *zstream)
325
ret = inflateInit2(&zstream->zs, -15);
330
i_fatal_status(FATAL_OUTOFMEM, "zlib: Out of memory");
331
case Z_VERSION_ERROR:
332
i_fatal("Wrong zlib library version (broken compilation)");
334
i_fatal("zlib: Invalid parameters");
336
i_fatal("inflateInit() failed with %d", ret);
338
zstream->header_read = !zstream->gz;
339
zstream->trailer_read = !zstream->gz;
342
static void i_stream_zlib_reset(struct zlib_istream *zstream)
344
struct istream_private *stream = &zstream->istream;
346
i_stream_seek(stream->parent, stream->parent_start_offset);
347
zstream->eof_offset = (uoff_t)-1;
350
zstream->zs.next_in = NULL;
351
zstream->zs.avail_in = 0;
353
stream->parent_expected_offset = stream->parent_start_offset;
354
stream->skip = stream->pos = 0;
355
stream->istream.v_offset = 0;
356
zstream->high_pos = 0;
357
zstream->prev_size = 0;
359
(void)inflateEnd(&zstream->zs);
360
i_stream_zlib_init(zstream);
364
i_stream_zlib_seek(struct istream_private *stream, uoff_t v_offset, bool mark)
366
struct zlib_istream *zstream = (struct zlib_istream *) stream;
367
uoff_t start_offset = stream->istream.v_offset - stream->skip;
369
if (v_offset < start_offset) {
370
/* have to seek backwards */
371
i_stream_zlib_reset(zstream);
373
} else if (zstream->high_pos != 0) {
374
stream->pos = zstream->high_pos;
375
zstream->high_pos = 0;
378
if (v_offset <= start_offset + stream->pos) {
379
/* seeking backwards within what's already cached */
380
stream->skip = v_offset - start_offset;
381
stream->istream.v_offset = v_offset;
382
zstream->high_pos = stream->pos;
383
stream->pos = stream->skip;
385
/* read and cache forward */
387
size_t avail = stream->pos - stream->skip;
389
if (stream->istream.v_offset + avail >= v_offset) {
390
i_stream_skip(&stream->istream,
392
stream->istream.v_offset);
396
i_stream_skip(&stream->istream, avail);
397
} while (i_stream_read(&stream->istream) >= 0);
399
if (stream->istream.v_offset != v_offset) {
400
/* some failure, we've broken it */
401
if (stream->istream.stream_errno != 0) {
402
i_error("zlib_istream.seek(%s) failed: %s",
403
i_stream_get_name(&stream->istream),
404
strerror(stream->istream.stream_errno));
405
i_stream_close(&stream->istream);
407
/* unexpected EOF. allow it since we may just
408
want to check if there's anything.. */
409
i_assert(stream->istream.eof);
415
zstream->marked = TRUE;
418
static const struct stat *
419
i_stream_zlib_stat(struct istream_private *stream, bool exact)
421
struct zlib_istream *zstream = (struct zlib_istream *) stream;
422
const struct stat *st;
425
st = i_stream_stat(stream->parent, exact);
429
/* when exact=FALSE always return the parent stat's size, even if we
430
know the exact value. this is necessary because otherwise e.g. mbox
431
code can see two different values and think that a compressed mbox
432
file keeps changing. */
436
stream->statbuf = *st;
437
if (zstream->stream_size == (uoff_t)-1) {
438
uoff_t old_offset = stream->istream.v_offset;
441
(void)i_stream_get_data(&stream->istream, &size);
442
i_stream_skip(&stream->istream, size);
443
} while (i_stream_read(&stream->istream) > 0);
445
i_stream_seek(&stream->istream, old_offset);
446
if (zstream->stream_size == (uoff_t)-1)
449
stream->statbuf.st_size = zstream->stream_size;
450
return &stream->statbuf;
453
static void i_stream_zlib_sync(struct istream_private *stream)
455
struct zlib_istream *zstream = (struct zlib_istream *) stream;
456
const struct stat *st;
458
st = i_stream_stat(stream->parent, FALSE);
460
if (memcmp(&zstream->last_parent_statbuf,
461
st, sizeof(*st)) == 0) {
462
/* a compressed file doesn't change unexpectedly,
463
don't clear our caches unnecessarily */
466
zstream->last_parent_statbuf = *st;
468
i_stream_zlib_reset(zstream);
471
static struct istream *
472
i_stream_create_zlib(struct istream *input, bool gz, bool log_errors)
474
struct zlib_istream *zstream;
476
zstream = i_new(struct zlib_istream, 1);
477
zstream->eof_offset = (uoff_t)-1;
478
zstream->stream_size = (uoff_t)-1;
480
zstream->log_errors = log_errors;
482
i_stream_zlib_init(zstream);
484
zstream->istream.iostream.close = i_stream_zlib_close;
485
zstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
486
zstream->istream.read = i_stream_zlib_read;
487
zstream->istream.seek = i_stream_zlib_seek;
488
zstream->istream.stat = i_stream_zlib_stat;
489
zstream->istream.sync = i_stream_zlib_sync;
491
zstream->istream.istream.readable_fd = FALSE;
492
zstream->istream.istream.blocking = input->blocking;
493
zstream->istream.istream.seekable = input->seekable;
495
return i_stream_create(&zstream->istream, input,
496
i_stream_get_fd(input));
499
struct istream *i_stream_create_gz(struct istream *input, bool log_errors)
501
return i_stream_create_zlib(input, TRUE, log_errors);
504
struct istream *i_stream_create_deflate(struct istream *input, bool log_errors)
506
return i_stream_create_zlib(input, FALSE, log_errors);