Age Owner TLA Line data Source code
1 : /*
2 : * pgp-compress.c
3 : * ZIP and ZLIB compression via zlib.
4 : *
5 : * Copyright (c) 2005 Marko Kreen
6 : * All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : * 1. Redistributions of source code must retain the above copyright
12 : * notice, this list of conditions and the following disclaimer.
13 : * 2. Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in the
15 : * documentation and/or other materials provided with the distribution.
16 : *
17 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 : * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 : * SUCH DAMAGE.
28 : *
29 : * contrib/pgcrypto/pgp-compress.c
30 : */
31 :
32 : #include "postgres.h"
33 :
34 : #include "pgp.h"
35 : #include "px.h"
36 :
37 : /*
38 : * Compressed pkt writer
39 : */
40 :
41 : #ifdef HAVE_LIBZ
42 :
43 : #include <zlib.h>
44 :
45 : #define ZIP_OUT_BUF 8192
46 : #define ZIP_IN_BLOCK 8192
47 :
48 : struct ZipStat
49 : {
50 : uint8 type;
51 : int buf_len;
52 : int hdr_done;
53 : z_stream stream;
54 : uint8 buf[ZIP_OUT_BUF];
55 : };
56 :
57 : static void *
6482 bruce 58 CBC 22 : z_alloc(void *priv, unsigned n_items, unsigned item_len)
59 : {
926 michael 60 22 : return palloc(n_items * item_len);
61 : }
62 :
63 : static void
6482 bruce 64 22 : z_free(void *priv, void *addr)
65 : {
926 michael 66 22 : pfree(addr);
6482 bruce 67 22 : }
68 :
69 : static int
5050 70 3 : compress_init(PushFilter *next, void *init_arg, void **priv_p)
71 : {
72 : int res;
73 : struct ZipStat *st;
6482 74 3 : PGP_Context *ctx = init_arg;
75 3 : uint8 type = ctx->compress_algo;
76 :
77 3 : if (type != PGP_COMPR_ZLIB && type != PGP_COMPR_ZIP)
6482 bruce 78 UBC 0 : return PXE_PGP_UNSUPPORTED_COMPR;
79 :
80 : /*
81 : * init
82 : */
926 michael 83 CBC 3 : st = palloc0(sizeof(*st));
6482 bruce 84 3 : st->buf_len = ZIP_OUT_BUF;
85 3 : st->stream.zalloc = z_alloc;
86 3 : st->stream.zfree = z_free;
87 :
88 3 : if (type == PGP_COMPR_ZIP)
89 2 : res = deflateInit2(&st->stream, ctx->compress_level,
90 : Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
91 : else
92 1 : res = deflateInit(&st->stream, ctx->compress_level);
93 3 : if (res != Z_OK)
94 : {
926 michael 95 UBC 0 : pfree(st);
6482 bruce 96 0 : return PXE_PGP_COMPRESSION_ERROR;
97 : }
6482 bruce 98 CBC 3 : *priv_p = st;
99 :
100 3 : return ZIP_IN_BLOCK;
101 : }
102 :
103 : /* writes compressed data packet */
104 :
105 : /* can handle zero-len incoming data, but shouldn't */
106 : static int
5050 107 4 : compress_process(PushFilter *next, void *priv, const uint8 *data, int len)
108 : {
109 : int res,
110 : n_out;
6482 111 4 : struct ZipStat *st = priv;
112 :
113 : /*
114 : * process data
115 : */
990 tgl 116 4 : st->stream.next_in = unconstify(uint8 *, data);
117 4 : st->stream.avail_in = len;
118 12 : while (st->stream.avail_in > 0)
119 : {
6482 bruce 120 4 : st->stream.next_out = st->buf;
121 4 : st->stream.avail_out = st->buf_len;
990 tgl 122 4 : res = deflate(&st->stream, Z_NO_FLUSH);
6482 bruce 123 4 : if (res != Z_OK)
6482 bruce 124 UBC 0 : return PXE_PGP_COMPRESSION_ERROR;
125 :
6482 bruce 126 CBC 4 : n_out = st->buf_len - st->stream.avail_out;
127 4 : if (n_out > 0)
128 : {
129 1 : res = pushf_write(next, st->buf, n_out);
130 1 : if (res < 0)
6482 bruce 131 UBC 0 : return res;
132 : }
133 : }
134 :
6482 bruce 135 CBC 4 : return 0;
136 : }
137 :
138 : static int
5050 139 3 : compress_flush(PushFilter *next, void *priv)
140 : {
141 : int res,
142 : zres,
143 : n_out;
6482 144 3 : struct ZipStat *st = priv;
145 :
146 3 : st->stream.next_in = NULL;
147 3 : st->stream.avail_in = 0;
148 : while (1)
149 : {
150 4 : st->stream.next_out = st->buf;
151 4 : st->stream.avail_out = st->buf_len;
152 4 : zres = deflate(&st->stream, Z_FINISH);
153 4 : if (zres != Z_STREAM_END && zres != Z_OK)
6482 bruce 154 UBC 0 : return PXE_PGP_COMPRESSION_ERROR;
155 :
6482 bruce 156 CBC 4 : n_out = st->buf_len - st->stream.avail_out;
157 4 : if (n_out > 0)
158 : {
159 4 : res = pushf_write(next, st->buf, n_out);
160 4 : if (res < 0)
6482 bruce 161 UBC 0 : return res;
162 : }
6482 bruce 163 CBC 4 : if (zres == Z_STREAM_END)
164 3 : break;
165 : }
166 3 : return 0;
167 : }
168 :
169 : static void
170 3 : compress_free(void *priv)
171 : {
172 3 : struct ZipStat *st = priv;
173 :
174 3 : deflateEnd(&st->stream);
3279 175 3 : px_memset(st, 0, sizeof(*st));
926 michael 176 3 : pfree(st);
6482 bruce 177 3 : }
178 :
179 : static const PushFilterOps
180 : compress_filter = {
181 : compress_init, compress_process, compress_flush, compress_free
182 : };
183 :
184 : int
5050 185 3 : pgp_compress_filter(PushFilter **res, PGP_Context *ctx, PushFilter *dst)
186 : {
6482 187 3 : return pushf_create(res, &compress_filter, ctx, dst);
188 : }
189 :
190 : /*
191 : * Decompress
192 : */
193 : struct DecomprData
194 : {
195 : int buf_len; /* = ZIP_OUT_BUF */
196 : int buf_data; /* available data */
197 : uint8 *pos;
198 : z_stream stream;
199 : int eof;
200 : uint8 buf[ZIP_OUT_BUF];
201 : };
202 :
203 : static int
5050 204 4 : decompress_init(void **priv_p, void *arg, PullFilter *src)
205 : {
6482 206 4 : PGP_Context *ctx = arg;
207 : struct DecomprData *dec;
208 : int res;
209 :
210 4 : if (ctx->compress_algo != PGP_COMPR_ZLIB
6385 211 3 : && ctx->compress_algo != PGP_COMPR_ZIP)
6482 bruce 212 UBC 0 : return PXE_PGP_UNSUPPORTED_COMPR;
213 :
926 michael 214 CBC 4 : dec = palloc0(sizeof(*dec));
6482 bruce 215 4 : dec->buf_len = ZIP_OUT_BUF;
216 4 : *priv_p = dec;
217 :
218 4 : dec->stream.zalloc = z_alloc;
219 4 : dec->stream.zfree = z_free;
220 :
221 4 : if (ctx->compress_algo == PGP_COMPR_ZIP)
222 3 : res = inflateInit2(&dec->stream, -15);
223 : else
224 1 : res = inflateInit(&dec->stream);
225 4 : if (res != Z_OK)
226 : {
926 michael 227 UBC 0 : pfree(dec);
6482 bruce 228 0 : px_debug("decompress_init: inflateInit error");
229 0 : return PXE_PGP_COMPRESSION_ERROR;
230 : }
231 :
6482 bruce 232 CBC 4 : return 0;
233 : }
234 :
235 : static int
5050 236 36 : decompress_read(void *priv, PullFilter *src, int len,
237 : uint8 **data_p, uint8 *buf, int buflen)
238 : {
239 : int res;
240 : int flush;
6482 241 36 : struct DecomprData *dec = priv;
242 :
243 44 : restart:
244 44 : if (dec->buf_data > 0)
245 : {
246 32 : if (len > dec->buf_data)
247 4 : len = dec->buf_data;
248 32 : *data_p = dec->pos;
249 32 : dec->pos += len;
250 32 : dec->buf_data -= len;
251 32 : return len;
252 : }
253 :
254 12 : if (dec->eof)
255 4 : return 0;
256 :
990 michael 257 8 : if (dec->stream.avail_in == 0)
258 : {
259 : uint8 *tmp;
260 :
261 8 : res = pullf_read(src, 8192, &tmp);
262 8 : if (res < 0)
990 michael 263 UBC 0 : return res;
990 michael 264 CBC 8 : dec->stream.next_in = tmp;
265 8 : dec->stream.avail_in = res;
266 : }
267 :
6482 bruce 268 8 : dec->stream.next_out = dec->buf;
269 8 : dec->stream.avail_out = dec->buf_len;
270 8 : dec->pos = dec->buf;
271 :
272 : /*
273 : * Z_SYNC_FLUSH is tell zlib to output as much as possible. It should do
274 : * it anyway (Z_NO_FLUSH), but seems to reserve the right not to. So lets
275 : * follow the API.
276 : */
277 8 : flush = dec->stream.avail_in ? Z_SYNC_FLUSH : Z_FINISH;
278 8 : res = inflate(&dec->stream, flush);
279 8 : if (res != Z_OK && res != Z_STREAM_END)
280 : {
6482 bruce 281 UBC 0 : px_debug("decompress_read: inflate error: %d", res);
282 0 : return PXE_PGP_CORRUPT_DATA;
283 : }
284 :
6482 bruce 285 CBC 8 : dec->buf_data = dec->buf_len - dec->stream.avail_out;
286 8 : if (res == Z_STREAM_END)
287 : {
288 : uint8 *tmp;
289 :
290 : /*
291 : * A stream must be terminated by a normal packet. If the last stream
292 : * packet in the source stream is a full packet, a normal empty packet
293 : * must follow. Since the underlying packet reader doesn't know that
294 : * the compressed stream has been ended, we need to consume the
295 : * terminating packet here. This read does not harm even if the
296 : * stream has already ended.
297 : */
986 michael 298 4 : res = pullf_read(src, 1, &tmp);
299 :
300 4 : if (res < 0)
986 michael 301 UBC 0 : return res;
986 michael 302 CBC 4 : else if (res > 0)
303 : {
986 michael 304 UBC 0 : px_debug("decompress_read: extra bytes after end of stream");
305 0 : return PXE_PGP_CORRUPT_DATA;
306 : }
6482 bruce 307 CBC 4 : dec->eof = 1;
308 : }
309 8 : goto restart;
310 : }
311 :
312 : static void
6385 313 4 : decompress_free(void *priv)
314 : {
6482 315 4 : struct DecomprData *dec = priv;
316 :
317 4 : inflateEnd(&dec->stream);
3279 318 4 : px_memset(dec, 0, sizeof(*dec));
926 michael 319 4 : pfree(dec);
6482 bruce 320 4 : }
321 :
322 : static const PullFilterOps
323 : decompress_filter = {
324 : decompress_init, decompress_read, decompress_free
325 : };
326 :
327 : int
5050 328 4 : pgp_decompress_filter(PullFilter **res, PGP_Context *ctx, PullFilter *src)
329 : {
6482 330 4 : return pullf_create(res, &decompress_filter, ctx, src);
331 : }
332 : #else /* !HAVE_LIBZ */
333 :
334 : int
335 : pgp_compress_filter(PushFilter **res, PGP_Context *ctx, PushFilter *dst)
336 : {
337 : return PXE_PGP_UNSUPPORTED_COMPR;
338 : }
339 :
340 : int
341 : pgp_decompress_filter(PullFilter **res, PGP_Context *ctx, PullFilter *src)
342 : {
343 : return PXE_PGP_UNSUPPORTED_COMPR;
344 : }
345 :
346 : #endif
|