1
/* Copyright (c) 2010-2013 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,
46
struct zlib_istream *zstream = (struct zlib_istream *)stream;
48
if (!zstream->zs_closed) {
49
(void)inflateEnd(&zstream->zs);
50
zstream->zs_closed = TRUE;
53
i_stream_close(zstream->istream.parent);
56
static void zlib_read_error(struct zlib_istream *zstream, const char *error)
58
i_error("zlib.read(%s): %s at %"PRIuUOFF_T,
59
i_stream_get_name(&zstream->istream.istream), error,
60
zstream->istream.abs_start_offset +
61
zstream->istream.istream.v_offset);
64
static int i_stream_zlib_read_header(struct istream_private *stream)
66
struct zlib_istream *zstream = (struct zlib_istream *)stream;
67
const unsigned char *data;
69
unsigned int pos, fextra_size;
72
ret = i_stream_read_data(stream->parent, &data, &size,
74
if (size == zstream->prev_size) {
76
if (zstream->log_errors)
77
zlib_read_error(zstream, "missing gz header");
78
stream->istream.stream_errno = EINVAL;
82
zstream->prev_size = size;
84
if (size < GZ_HEADER_MIN_SIZE)
86
pos = GZ_HEADER_MIN_SIZE;
88
if (data[0] != GZ_MAGIC1 || data[1] != GZ_MAGIC2) {
89
/* missing gzip magic header */
90
if (zstream->log_errors) {
91
zlib_read_error(zstream, "wrong magic in header "
94
stream->istream.stream_errno = EINVAL;
97
if ((data[3] & GZ_FLAG_FEXTRA) != 0) {
101
fextra_size = data[pos] + (data[pos+1] << 8);
103
if (pos + fextra_size < size)
107
if ((data[3] & GZ_FLAG_FNAME) != 0) {
111
} while (data[pos++] != '\0');
113
if ((data[3] & GZ_FLAG_FCOMMENT) != 0) {
117
} while (data[pos++] != '\0');
119
if ((data[3] & GZ_FLAG_FHCRC) != 0) {
124
i_stream_skip(stream->parent, pos);
125
zstream->prev_size = 0;
129
static uint32_t data_get_uint32(const unsigned char *data)
131
return data[0] | (data[1] << 8) | (data[2] << 16) |
132
((uint32_t)data[3] << 24);
135
static int i_stream_zlib_read_trailer(struct zlib_istream *zstream)
137
struct istream_private *stream = &zstream->istream;
138
const unsigned char *data;
142
ret = i_stream_read_data(stream->parent, &data, &size,
144
if (size == zstream->prev_size) {
146
if (zstream->log_errors)
147
zlib_read_error(zstream, "missing gz trailer");
148
stream->istream.stream_errno = EINVAL;
152
zstream->prev_size = size;
154
if (size < GZ_TRAILER_SIZE)
157
if (data_get_uint32(data) != zstream->crc32) {
158
if (zstream->log_errors) {
159
zlib_read_error(zstream,
160
"gz trailer has wrong CRC value");
162
stream->istream.stream_errno = EINVAL;
165
i_stream_skip(stream->parent, GZ_TRAILER_SIZE);
166
zstream->prev_size = 0;
167
zstream->trailer_read = TRUE;
171
static ssize_t i_stream_zlib_read(struct istream_private *stream)
173
struct zlib_istream *zstream = (struct zlib_istream *)stream;
174
const unsigned char *data;
176
size_t size, out_size;
179
high_offset = stream->istream.v_offset + (stream->pos - stream->skip);
180
if (zstream->eof_offset == high_offset) {
181
i_assert(zstream->high_pos == 0 ||
182
zstream->high_pos == stream->pos);
183
if (!zstream->trailer_read) {
185
ret = i_stream_zlib_read_trailer(zstream);
186
} while (ret == 0 && stream->istream.blocking);
190
if (!zstream->gz || i_stream_is_eof(stream->parent)) {
191
stream->istream.eof = TRUE;
194
/* gzip file with concatenated content */
195
zstream->eof_offset = (uoff_t)-1;
196
zstream->stream_size = (uoff_t)-1;
197
zstream->header_read = FALSE;
198
zstream->trailer_read = FALSE;
201
(void)inflateEnd(&zstream->zs);
202
i_stream_zlib_init(zstream);
205
if (!zstream->header_read) {
207
ret = i_stream_zlib_read_header(stream);
208
} while (ret == 0 && stream->istream.blocking);
211
zstream->header_read = TRUE;
214
if (stream->pos < zstream->high_pos) {
215
/* we're here because we seeked back within the read buffer. */
216
ret = zstream->high_pos - stream->pos;
217
stream->pos = zstream->high_pos;
218
zstream->high_pos = 0;
219
if (zstream->trailer_read) {
220
high_offset = stream->istream.v_offset +
221
(stream->pos - stream->skip);
222
i_assert(zstream->eof_offset == high_offset);
223
stream->istream.eof = TRUE;
227
zstream->high_pos = 0;
229
if (stream->pos + CHUNK_SIZE > stream->buffer_size) {
230
/* try to keep at least CHUNK_SIZE available */
231
if (!zstream->marked && stream->skip > 0) {
232
/* don't try to keep anything cached if we don't
234
i_stream_compress(stream);
236
if (stream->max_buffer_size == 0 ||
237
stream->buffer_size < stream->max_buffer_size)
238
i_stream_grow_buffer(stream, CHUNK_SIZE);
240
if (stream->pos == stream->buffer_size) {
241
if (stream->skip > 0) {
242
/* lose our buffer cache */
243
i_stream_compress(stream);
246
if (stream->pos == stream->buffer_size)
247
return -2; /* buffer full */
251
if (i_stream_read_data(stream->parent, &data, &size, 0) < 0) {
252
if (stream->parent->stream_errno != 0) {
253
stream->istream.stream_errno =
254
stream->parent->stream_errno;
256
i_assert(stream->parent->eof);
257
if (zstream->log_errors)
258
zlib_read_error(zstream, "unexpected EOF");
259
stream->istream.stream_errno = EPIPE;
265
i_assert(!stream->istream.blocking);
269
zstream->zs.next_in = (void *)data;
270
zstream->zs.avail_in = size;
272
out_size = stream->buffer_size - stream->pos;
273
zstream->zs.next_out = stream->w_buffer + stream->pos;
274
zstream->zs.avail_out = out_size;
275
ret = inflate(&zstream->zs, Z_SYNC_FLUSH);
277
out_size -= zstream->zs.avail_out;
278
zstream->crc32 = crc32_data_more(zstream->crc32,
279
stream->w_buffer + stream->pos,
281
stream->pos += out_size;
283
i_stream_skip(stream->parent, size - zstream->zs.avail_in);
289
if (zstream->log_errors)
290
zlib_read_error(zstream, "can't read file without dict");
291
stream->istream.stream_errno = EINVAL;
294
if (zstream->log_errors)
295
zlib_read_error(zstream, "corrupted data");
296
stream->istream.stream_errno = EINVAL;
299
i_fatal_status(FATAL_OUTOFMEM, "zlib.read(%s): Out of memory",
300
i_stream_get_name(&stream->istream));
302
zstream->eof_offset = stream->istream.v_offset +
303
(stream->pos - stream->skip);
304
zstream->stream_size = zstream->eof_offset;
305
zstream->zs.avail_in = 0;
307
if (!zstream->trailer_read) {
308
/* try to read and verify the trailer, we might not
310
if (i_stream_zlib_read_trailer(zstream) < 0)
315
i_fatal("inflate() failed with %d", ret);
318
/* read more input */
319
return i_stream_zlib_read(stream);
324
static void i_stream_zlib_init(struct zlib_istream *zstream)
328
ret = inflateInit2(&zstream->zs, -15);
333
i_fatal_status(FATAL_OUTOFMEM, "zlib: Out of memory");
334
case Z_VERSION_ERROR:
335
i_fatal("Wrong zlib library version (broken compilation)");
337
i_fatal("zlib: Invalid parameters");
339
i_fatal("inflateInit() failed with %d", ret);
341
zstream->header_read = !zstream->gz;
342
zstream->trailer_read = !zstream->gz;
345
static void i_stream_zlib_reset(struct zlib_istream *zstream)
347
struct istream_private *stream = &zstream->istream;
349
i_stream_seek(stream->parent, stream->parent_start_offset);
350
zstream->eof_offset = (uoff_t)-1;
353
zstream->zs.next_in = NULL;
354
zstream->zs.avail_in = 0;
356
stream->parent_expected_offset = stream->parent_start_offset;
357
stream->skip = stream->pos = 0;
358
stream->istream.v_offset = 0;
359
zstream->high_pos = 0;
360
zstream->prev_size = 0;
362
(void)inflateEnd(&zstream->zs);
363
i_stream_zlib_init(zstream);
367
i_stream_zlib_seek(struct istream_private *stream, uoff_t v_offset, bool mark)
369
struct zlib_istream *zstream = (struct zlib_istream *) stream;
370
uoff_t start_offset = stream->istream.v_offset - stream->skip;
372
if (v_offset < start_offset) {
373
/* have to seek backwards */
374
i_stream_zlib_reset(zstream);
376
} else if (zstream->high_pos != 0) {
377
stream->pos = zstream->high_pos;
378
zstream->high_pos = 0;
381
if (v_offset <= start_offset + stream->pos) {
382
/* seeking backwards within what's already cached */
383
stream->skip = v_offset - start_offset;
384
stream->istream.v_offset = v_offset;
385
zstream->high_pos = stream->pos;
386
stream->pos = stream->skip;
388
/* read and cache forward */
390
size_t avail = stream->pos - stream->skip;
392
if (stream->istream.v_offset + avail >= v_offset) {
393
i_stream_skip(&stream->istream,
395
stream->istream.v_offset);
399
i_stream_skip(&stream->istream, avail);
400
} while (i_stream_read(&stream->istream) >= 0);
402
if (stream->istream.v_offset != v_offset) {
403
/* some failure, we've broken it */
404
if (stream->istream.stream_errno != 0) {
405
i_error("zlib_istream.seek(%s) failed: %s",
406
i_stream_get_name(&stream->istream),
407
strerror(stream->istream.stream_errno));
408
i_stream_close(&stream->istream);
410
/* unexpected EOF. allow it since we may just
411
want to check if there's anything.. */
412
i_assert(stream->istream.eof);
418
zstream->marked = TRUE;
422
i_stream_zlib_stat(struct istream_private *stream, bool exact)
424
struct zlib_istream *zstream = (struct zlib_istream *) stream;
425
const struct stat *st;
428
if (i_stream_stat(stream->parent, exact, &st) < 0)
430
stream->statbuf = *st;
432
/* when exact=FALSE always return the parent stat's size, even if we
433
know the exact value. this is necessary because otherwise e.g. mbox
434
code can see two different values and think that a compressed mbox
435
file keeps changing. */
439
if (zstream->stream_size == (uoff_t)-1) {
440
uoff_t old_offset = stream->istream.v_offset;
443
size = i_stream_get_data_size(&stream->istream);
444
i_stream_skip(&stream->istream, size);
445
} while (i_stream_read(&stream->istream) > 0);
447
i_stream_seek(&stream->istream, old_offset);
448
if (zstream->stream_size == (uoff_t)-1)
451
stream->statbuf.st_size = zstream->stream_size;
455
static void i_stream_zlib_sync(struct istream_private *stream)
457
struct zlib_istream *zstream = (struct zlib_istream *) stream;
458
const struct stat *st;
460
if (i_stream_stat(stream->parent, FALSE, &st) < 0) {
461
if (memcmp(&zstream->last_parent_statbuf,
462
st, sizeof(*st)) == 0) {
463
/* a compressed file doesn't change unexpectedly,
464
don't clear our caches unnecessarily */
467
zstream->last_parent_statbuf = *st;
469
i_stream_zlib_reset(zstream);
472
static struct istream *
473
i_stream_create_zlib(struct istream *input, bool gz, bool log_errors)
475
struct zlib_istream *zstream;
477
zstream = i_new(struct zlib_istream, 1);
478
zstream->eof_offset = (uoff_t)-1;
479
zstream->stream_size = (uoff_t)-1;
481
zstream->log_errors = log_errors;
483
i_stream_zlib_init(zstream);
485
zstream->istream.iostream.close = i_stream_zlib_close;
486
zstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
487
zstream->istream.read = i_stream_zlib_read;
488
zstream->istream.seek = i_stream_zlib_seek;
489
zstream->istream.stat = i_stream_zlib_stat;
490
zstream->istream.sync = i_stream_zlib_sync;
492
zstream->istream.istream.readable_fd = FALSE;
493
zstream->istream.istream.blocking = input->blocking;
494
zstream->istream.istream.seekable = input->seekable;
496
return i_stream_create(&zstream->istream, input,
497
i_stream_get_fd(input));
500
struct istream *i_stream_create_gz(struct istream *input, bool log_errors)
502
return i_stream_create_zlib(input, TRUE, log_errors);
505
struct istream *i_stream_create_deflate(struct istream *input, bool log_errors)
507
return i_stream_create_zlib(input, FALSE, log_errors);