~ubuntu-branches/ubuntu/utopic/dovecot/utopic-proposed

« back to all changes in this revision

Viewing changes to src/plugins/zlib/istream-bzlib.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-01-08 09:35:49 UTC
  • mfrom: (4.1.35 sid)
  • Revision ID: package-import@ubuntu.com-20140108093549-i72o93pux8p0dlaf
Tags: 1:2.2.9-1ubuntu1
* Merge from Debian unstable, remaining changes:
  + Add mail-stack-delivery package:
    - Update d/rules
    - d/control: convert existing dovecot-postfix package to a dummy
      package and add new mail-stack-delivery package.
    - Update maintainer scripts.
    - Rename d/dovecot-postfix.* to debian/mail-stack-delivery.*
    - d/mail-stack-delivery.preinst: Move previously installed backups and
      config files to a new package namespace.
    - d/mail-stack-delivery.prerm: Added to handle downgrades.
  + Use Snakeoil SSL certificates by default:
    - d/control: Depend on ssl-cert.
    - d/dovecot-core.postinst: Relax grep for SSL_* a bit.
  + Add autopkgtest to debian/tests/*.
  + Add ufw integration:
    - d/dovecot-core.ufw.profile: new ufw profile.
    - d/rules: install profile in dovecot-core.
    - d/control: dovecot-core - suggest ufw.
  + d/dovecot-core.dirs: Added usr/share/doc/dovecot-core
  + Add apport hook:
    - d/rules, d/source_dovecot.py
  + Add upstart job:
    - d/rules, d/dovecot-core.dovecot.upstart, d/control,
      d/dovecot-core.dirs, dovecot-imapd.{postrm, postinst, prerm},
      d/dovecot-pop3d.{postinst, postrm, prerm}.
      d/mail-stack-deliver.postinst: Convert init script to upstart.
  + Use the autotools-dev dh addon to update config.guess/config.sub for
    arm64.
* Dropped changes, included in Debian:
  - Update Dovecot name to reflect distribution in login greeting.
  - Update Drac plugin for >= 2.0.0 support.
* d/control: Drop dovecot-postfix package as its no longer required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */
2
 
 
3
 
#include "lib.h"
4
 
 
5
 
#ifdef HAVE_BZLIB
6
 
 
7
 
#include "istream-private.h"
8
 
#include "istream-zlib.h"
9
 
#include <bzlib.h>
10
 
 
11
 
#define CHUNK_SIZE (1024*64)
12
 
 
13
 
struct bzlib_istream {
14
 
        struct istream_private istream;
15
 
 
16
 
        bz_stream zs;
17
 
        uoff_t eof_offset, stream_size;
18
 
        size_t prev_size, high_pos;
19
 
        struct stat last_parent_statbuf;
20
 
 
21
 
        unsigned int log_errors:1;
22
 
        unsigned int marked:1;
23
 
        unsigned int zs_closed:1;
24
 
};
25
 
 
26
 
static void i_stream_bzlib_close(struct iostream_private *stream)
27
 
{
28
 
        struct bzlib_istream *zstream = (struct bzlib_istream *)stream;
29
 
 
30
 
        if (!zstream->zs_closed) {
31
 
                (void)BZ2_bzDecompressEnd(&zstream->zs);
32
 
                zstream->zs_closed = TRUE;
33
 
        }
34
 
}
35
 
 
36
 
static void bzlib_read_error(struct bzlib_istream *zstream, const char *error)
37
 
{
38
 
        i_error("bzlib.read(%s): %s at %"PRIuUOFF_T,
39
 
                i_stream_get_name(&zstream->istream.istream), error,
40
 
                zstream->istream.abs_start_offset +
41
 
                zstream->istream.istream.v_offset);
42
 
}
43
 
 
44
 
static ssize_t i_stream_bzlib_read(struct istream_private *stream)
45
 
{
46
 
        struct bzlib_istream *zstream = (struct bzlib_istream *)stream;
47
 
        const unsigned char *data;
48
 
        uoff_t high_offset;
49
 
        size_t size;
50
 
        int ret;
51
 
 
52
 
        high_offset = stream->istream.v_offset + (stream->pos - stream->skip);
53
 
        if (zstream->eof_offset == high_offset) {
54
 
                i_assert(zstream->high_pos == 0 ||
55
 
                         zstream->high_pos == stream->pos);
56
 
                stream->istream.eof = TRUE;
57
 
                return -1;
58
 
        }
59
 
 
60
 
        if (stream->pos < zstream->high_pos) {
61
 
                /* we're here because we seeked back within the read buffer. */
62
 
                ret = zstream->high_pos - stream->pos;
63
 
                stream->pos = zstream->high_pos;
64
 
                zstream->high_pos = 0;
65
 
 
66
 
                if (zstream->eof_offset != (uoff_t)-1) {
67
 
                        high_offset = stream->istream.v_offset +
68
 
                                (stream->pos - stream->skip);
69
 
                        i_assert(zstream->eof_offset == high_offset);
70
 
                        stream->istream.eof = TRUE;
71
 
                }
72
 
                return ret;
73
 
        }
74
 
        zstream->high_pos = 0;
75
 
 
76
 
        if (stream->pos + CHUNK_SIZE > stream->buffer_size) {
77
 
                /* try to keep at least CHUNK_SIZE available */
78
 
                if (!zstream->marked && stream->skip > 0) {
79
 
                        /* don't try to keep anything cached if we don't
80
 
                           have a seek mark. */
81
 
                        i_stream_compress(stream);
82
 
                }
83
 
                if (stream->max_buffer_size == 0 ||
84
 
                    stream->buffer_size < stream->max_buffer_size)
85
 
                        i_stream_grow_buffer(stream, CHUNK_SIZE);
86
 
 
87
 
                if (stream->pos == stream->buffer_size) {
88
 
                        if (stream->skip > 0) {
89
 
                                /* lose our buffer cache */
90
 
                                i_stream_compress(stream);
91
 
                        }
92
 
 
93
 
                        if (stream->pos == stream->buffer_size)
94
 
                                return -2; /* buffer full */
95
 
                }
96
 
        }
97
 
 
98
 
        if (zstream->zs.avail_in == 0) {
99
 
                /* need to read more data. try to read a full CHUNK_SIZE */
100
 
                i_stream_skip(stream->parent, zstream->prev_size);
101
 
                if (i_stream_read_data(stream->parent, &data, &size,
102
 
                                       CHUNK_SIZE-1) == -1 && size == 0) {
103
 
                        if (stream->parent->stream_errno != 0) {
104
 
                                stream->istream.stream_errno =
105
 
                                        stream->parent->stream_errno;
106
 
                        } else {
107
 
                                i_assert(stream->parent->eof);
108
 
                                if (zstream->log_errors) {
109
 
                                        bzlib_read_error(zstream,
110
 
                                                         "unexpected EOF");
111
 
                                }
112
 
                                stream->istream.stream_errno = EINVAL;
113
 
                        }
114
 
                        return -1;
115
 
                }
116
 
                zstream->prev_size = size;
117
 
                if (size == 0) {
118
 
                        /* no more input */
119
 
                        i_assert(!stream->istream.blocking);
120
 
                        return 0;
121
 
                }
122
 
 
123
 
                zstream->zs.next_in = (char *)data;
124
 
                zstream->zs.avail_in = size;
125
 
        }
126
 
 
127
 
        size = stream->buffer_size - stream->pos;
128
 
        zstream->zs.next_out = (char *)stream->w_buffer + stream->pos;
129
 
        zstream->zs.avail_out = size;
130
 
        ret = BZ2_bzDecompress(&zstream->zs);
131
 
 
132
 
        size -= zstream->zs.avail_out;
133
 
        stream->pos += size;
134
 
 
135
 
        switch (ret) {
136
 
        case BZ_OK:
137
 
                break;
138
 
        case BZ_PARAM_ERROR:
139
 
                i_unreached();
140
 
        case BZ_DATA_ERROR:
141
 
                if (zstream->log_errors)
142
 
                        bzlib_read_error(zstream, "corrupted data");
143
 
                stream->istream.stream_errno = EINVAL;
144
 
                return -1;
145
 
        case BZ_DATA_ERROR_MAGIC:
146
 
                if (zstream->log_errors) {
147
 
                        bzlib_read_error(zstream,
148
 
                                "wrong magic in header (not bz2 file?)");
149
 
                }
150
 
                stream->istream.stream_errno = EINVAL;
151
 
                return -1;
152
 
        case BZ_MEM_ERROR:
153
 
                i_fatal_status(FATAL_OUTOFMEM, "bzlib.read(%s): Out of memory",
154
 
                               i_stream_get_name(&stream->istream));
155
 
        case BZ_STREAM_END:
156
 
                zstream->eof_offset = stream->istream.v_offset +
157
 
                        (stream->pos - stream->skip);
158
 
                zstream->stream_size = zstream->eof_offset;
159
 
                if (size == 0) {
160
 
                        stream->istream.eof = TRUE;
161
 
                        return -1;
162
 
                }
163
 
                break;
164
 
        default:
165
 
                i_fatal("BZ2_bzDecompress() failed with %d", ret);
166
 
        }
167
 
        if (size == 0) {
168
 
                /* read more input */
169
 
                return i_stream_bzlib_read(stream);
170
 
        }
171
 
        return size;
172
 
}
173
 
 
174
 
static void i_stream_bzlib_init(struct bzlib_istream *zstream)
175
 
{
176
 
        int ret;
177
 
 
178
 
        ret = BZ2_bzDecompressInit(&zstream->zs, 0, 0);
179
 
        switch (ret) {
180
 
        case BZ_OK:
181
 
                break;
182
 
        case BZ_MEM_ERROR:
183
 
                i_fatal_status(FATAL_OUTOFMEM, "bzlib: Out of memory");
184
 
        case BZ_CONFIG_ERROR:
185
 
                i_fatal("Wrong bzlib library version (broken compilation)");
186
 
        case BZ_PARAM_ERROR:
187
 
                i_fatal("bzlib: Invalid parameters");
188
 
        default:
189
 
                i_fatal("BZ2_bzDecompressInit() failed with %d", ret);
190
 
        }
191
 
}
192
 
 
193
 
static void i_stream_bzlib_reset(struct bzlib_istream *zstream)
194
 
{
195
 
        struct istream_private *stream = &zstream->istream;
196
 
 
197
 
        i_stream_seek(stream->parent, stream->parent_start_offset);
198
 
        zstream->eof_offset = (uoff_t)-1;
199
 
        zstream->zs.next_in = NULL;
200
 
        zstream->zs.avail_in = 0;
201
 
 
202
 
        stream->parent_expected_offset = stream->parent_start_offset;
203
 
        stream->skip = stream->pos = 0;
204
 
        stream->istream.v_offset = 0;
205
 
        zstream->high_pos = 0;
206
 
        zstream->prev_size = 0;
207
 
 
208
 
        (void)BZ2_bzDecompressEnd(&zstream->zs);
209
 
        i_stream_bzlib_init(zstream);
210
 
}
211
 
 
212
 
static void
213
 
i_stream_bzlib_seek(struct istream_private *stream, uoff_t v_offset, bool mark)
214
 
{
215
 
        struct bzlib_istream *zstream = (struct bzlib_istream *) stream;
216
 
        uoff_t start_offset = stream->istream.v_offset - stream->skip;
217
 
 
218
 
        if (v_offset < start_offset) {
219
 
                /* have to seek backwards */
220
 
                i_stream_bzlib_reset(zstream);
221
 
                start_offset = 0;
222
 
        } else if (zstream->high_pos != 0) {
223
 
                stream->pos = zstream->high_pos;
224
 
                zstream->high_pos = 0;
225
 
        }
226
 
 
227
 
        if (v_offset <= start_offset + stream->pos) {
228
 
                /* seeking backwards within what's already cached */
229
 
                stream->skip = v_offset - start_offset;
230
 
                stream->istream.v_offset = v_offset;
231
 
                zstream->high_pos = stream->pos;
232
 
                stream->pos = stream->skip;
233
 
        } else {
234
 
                /* read and cache forward */
235
 
                do {
236
 
                        size_t avail = stream->pos - stream->skip;
237
 
 
238
 
                        if (stream->istream.v_offset + avail >= v_offset) {
239
 
                                i_stream_skip(&stream->istream,
240
 
                                              v_offset -
241
 
                                              stream->istream.v_offset);
242
 
                                break;
243
 
                        }
244
 
 
245
 
                        i_stream_skip(&stream->istream, avail);
246
 
                } while (i_stream_read(&stream->istream) >= 0);
247
 
 
248
 
                if (stream->istream.v_offset != v_offset) {
249
 
                        /* some failure, we've broken it */
250
 
                        if (stream->istream.stream_errno != 0) {
251
 
                                i_error("bzlib_istream.seek(%s) failed: %s",
252
 
                                        i_stream_get_name(&stream->istream),
253
 
                                        strerror(stream->istream.stream_errno));
254
 
                                i_stream_close(&stream->istream);
255
 
                        } else {
256
 
                                /* unexpected EOF. allow it since we may just
257
 
                                   want to check if there's anything.. */
258
 
                                i_assert(stream->istream.eof);
259
 
                        }
260
 
                }
261
 
        }
262
 
 
263
 
        if (mark)
264
 
                zstream->marked = TRUE;
265
 
}
266
 
 
267
 
static const struct stat *
268
 
i_stream_bzlib_stat(struct istream_private *stream, bool exact)
269
 
{
270
 
        struct bzlib_istream *zstream = (struct bzlib_istream *) stream;
271
 
        const struct stat *st;
272
 
        size_t size;
273
 
 
274
 
        st = i_stream_stat(stream->parent, exact);
275
 
        if (st == NULL)
276
 
                return NULL;
277
 
 
278
 
        /* when exact=FALSE always return the parent stat's size, even if we
279
 
           know the exact value. this is necessary because otherwise e.g. mbox
280
 
           code can see two different values and think that a compressed mbox
281
 
           file keeps changing. */
282
 
        if (!exact)
283
 
                return st;
284
 
 
285
 
        stream->statbuf = *st;
286
 
        if (zstream->stream_size == (uoff_t)-1) {
287
 
                uoff_t old_offset = stream->istream.v_offset;
288
 
 
289
 
                do {
290
 
                        (void)i_stream_get_data(&stream->istream, &size);
291
 
                        i_stream_skip(&stream->istream, size);
292
 
                } while (i_stream_read(&stream->istream) > 0);
293
 
 
294
 
                i_stream_seek(&stream->istream, old_offset);
295
 
                if (zstream->stream_size == (uoff_t)-1)
296
 
                        return NULL;
297
 
        }
298
 
        stream->statbuf.st_size = zstream->stream_size;
299
 
        return &stream->statbuf;
300
 
}
301
 
 
302
 
static void i_stream_bzlib_sync(struct istream_private *stream)
303
 
{
304
 
        struct bzlib_istream *zstream = (struct bzlib_istream *) stream;
305
 
        const struct stat *st;
306
 
 
307
 
        st = i_stream_stat(stream->parent, FALSE);
308
 
        if (st != NULL) {
309
 
                if (memcmp(&zstream->last_parent_statbuf,
310
 
                           st, sizeof(*st)) == 0) {
311
 
                        /* a compressed file doesn't change unexpectedly,
312
 
                           don't clear our caches unnecessarily */
313
 
                        return;
314
 
                }
315
 
                zstream->last_parent_statbuf = *st;
316
 
        }
317
 
        i_stream_bzlib_reset(zstream);
318
 
}
319
 
 
320
 
struct istream *i_stream_create_bz2(struct istream *input, bool log_errors)
321
 
{
322
 
        struct bzlib_istream *zstream;
323
 
 
324
 
        zstream = i_new(struct bzlib_istream, 1);
325
 
        zstream->eof_offset = (uoff_t)-1;
326
 
        zstream->stream_size = (uoff_t)-1;
327
 
        zstream->log_errors = log_errors;
328
 
 
329
 
        i_stream_bzlib_init(zstream);
330
 
 
331
 
        zstream->istream.iostream.close = i_stream_bzlib_close;
332
 
        zstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
333
 
        zstream->istream.read = i_stream_bzlib_read;
334
 
        zstream->istream.seek = i_stream_bzlib_seek;
335
 
        zstream->istream.stat = i_stream_bzlib_stat;
336
 
        zstream->istream.sync = i_stream_bzlib_sync;
337
 
 
338
 
        zstream->istream.istream.readable_fd = FALSE;
339
 
        zstream->istream.istream.blocking = input->blocking;
340
 
        zstream->istream.istream.seekable = input->seekable;
341
 
 
342
 
        return i_stream_create(&zstream->istream, input,
343
 
                               i_stream_get_fd(input));
344
 
}
345
 
#endif