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 *
58 44 : z_alloc(void *priv, unsigned n_items, unsigned item_len)
59 : {
60 44 : return palloc(n_items * item_len);
61 : }
62 :
63 : static void
64 44 : z_free(void *priv, void *addr)
65 : {
66 44 : pfree(addr);
67 44 : }
68 :
69 : static int
70 6 : compress_init(PushFilter *next, void *init_arg, void **priv_p)
71 : {
72 : int res;
73 : struct ZipStat *st;
74 6 : PGP_Context *ctx = init_arg;
75 6 : uint8 type = ctx->compress_algo;
76 :
77 6 : if (type != PGP_COMPR_ZLIB && type != PGP_COMPR_ZIP)
78 0 : return PXE_PGP_UNSUPPORTED_COMPR;
79 :
80 : /*
81 : * init
82 : */
83 6 : st = palloc0(sizeof(*st));
84 6 : st->buf_len = ZIP_OUT_BUF;
85 6 : st->stream.zalloc = z_alloc;
86 6 : st->stream.zfree = z_free;
87 :
88 6 : if (type == PGP_COMPR_ZIP)
89 4 : res = deflateInit2(&st->stream, ctx->compress_level,
90 : Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
91 : else
92 2 : res = deflateInit(&st->stream, ctx->compress_level);
93 6 : if (res != Z_OK)
94 : {
95 0 : pfree(st);
96 0 : return PXE_PGP_COMPRESSION_ERROR;
97 : }
98 6 : *priv_p = st;
99 :
100 6 : 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
107 8 : compress_process(PushFilter *next, void *priv, const uint8 *data, int len)
108 : {
109 : int res,
110 : n_out;
111 8 : struct ZipStat *st = priv;
112 :
113 : /*
114 : * process data
115 : */
116 8 : st->stream.next_in = data;
117 8 : st->stream.avail_in = len;
118 16 : while (st->stream.avail_in > 0)
119 : {
120 8 : st->stream.next_out = st->buf;
121 8 : st->stream.avail_out = st->buf_len;
122 8 : res = deflate(&st->stream, Z_NO_FLUSH);
123 8 : if (res != Z_OK)
124 0 : return PXE_PGP_COMPRESSION_ERROR;
125 :
126 8 : n_out = st->buf_len - st->stream.avail_out;
127 8 : if (n_out > 0)
128 : {
129 2 : res = pushf_write(next, st->buf, n_out);
130 2 : if (res < 0)
131 0 : return res;
132 : }
133 : }
134 :
135 8 : return 0;
136 : }
137 :
138 : static int
139 6 : compress_flush(PushFilter *next, void *priv)
140 : {
141 : int res,
142 : zres,
143 : n_out;
144 6 : struct ZipStat *st = priv;
145 :
146 6 : st->stream.next_in = NULL;
147 6 : st->stream.avail_in = 0;
148 : while (1)
149 : {
150 8 : st->stream.next_out = st->buf;
151 8 : st->stream.avail_out = st->buf_len;
152 8 : zres = deflate(&st->stream, Z_FINISH);
153 8 : if (zres != Z_STREAM_END && zres != Z_OK)
154 0 : return PXE_PGP_COMPRESSION_ERROR;
155 :
156 8 : n_out = st->buf_len - st->stream.avail_out;
157 8 : if (n_out > 0)
158 : {
159 8 : res = pushf_write(next, st->buf, n_out);
160 8 : if (res < 0)
161 0 : return res;
162 : }
163 8 : if (zres == Z_STREAM_END)
164 6 : break;
165 : }
166 6 : return 0;
167 : }
168 :
169 : static void
170 6 : compress_free(void *priv)
171 : {
172 6 : struct ZipStat *st = priv;
173 :
174 6 : deflateEnd(&st->stream);
175 6 : px_memset(st, 0, sizeof(*st));
176 6 : pfree(st);
177 6 : }
178 :
179 : static const PushFilterOps
180 : compress_filter = {
181 : compress_init, compress_process, compress_flush, compress_free
182 : };
183 :
184 : int
185 6 : pgp_compress_filter(PushFilter **res, PGP_Context *ctx, PushFilter *dst)
186 : {
187 6 : 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
204 8 : decompress_init(void **priv_p, void *arg, PullFilter *src)
205 : {
206 8 : PGP_Context *ctx = arg;
207 : struct DecomprData *dec;
208 : int res;
209 :
210 8 : if (ctx->compress_algo != PGP_COMPR_ZLIB
211 6 : && ctx->compress_algo != PGP_COMPR_ZIP)
212 0 : return PXE_PGP_UNSUPPORTED_COMPR;
213 :
214 8 : dec = palloc0(sizeof(*dec));
215 8 : dec->buf_len = ZIP_OUT_BUF;
216 8 : *priv_p = dec;
217 :
218 8 : dec->stream.zalloc = z_alloc;
219 8 : dec->stream.zfree = z_free;
220 :
221 8 : if (ctx->compress_algo == PGP_COMPR_ZIP)
222 6 : res = inflateInit2(&dec->stream, -15);
223 : else
224 2 : res = inflateInit(&dec->stream);
225 8 : if (res != Z_OK)
226 : {
227 0 : pfree(dec);
228 0 : px_debug("decompress_init: inflateInit error");
229 0 : return PXE_PGP_COMPRESSION_ERROR;
230 : }
231 :
232 8 : return 0;
233 : }
234 :
235 : static int
236 72 : decompress_read(void *priv, PullFilter *src, int len,
237 : uint8 **data_p, uint8 *buf, int buflen)
238 : {
239 : int res;
240 : int flush;
241 72 : struct DecomprData *dec = priv;
242 :
243 88 : restart:
244 88 : if (dec->buf_data > 0)
245 : {
246 64 : if (len > dec->buf_data)
247 8 : len = dec->buf_data;
248 64 : *data_p = dec->pos;
249 64 : dec->pos += len;
250 64 : dec->buf_data -= len;
251 64 : return len;
252 : }
253 :
254 24 : if (dec->eof)
255 8 : return 0;
256 :
257 16 : if (dec->stream.avail_in == 0)
258 : {
259 : uint8 *tmp;
260 :
261 16 : res = pullf_read(src, 8192, &tmp);
262 16 : if (res < 0)
263 0 : return res;
264 16 : dec->stream.next_in = tmp;
265 16 : dec->stream.avail_in = res;
266 : }
267 :
268 16 : dec->stream.next_out = dec->buf;
269 16 : dec->stream.avail_out = dec->buf_len;
270 16 : 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 16 : flush = dec->stream.avail_in ? Z_SYNC_FLUSH : Z_FINISH;
278 16 : res = inflate(&dec->stream, flush);
279 16 : if (res != Z_OK && res != Z_STREAM_END)
280 : {
281 0 : px_debug("decompress_read: inflate error: %d", res);
282 0 : return PXE_PGP_CORRUPT_DATA;
283 : }
284 :
285 16 : dec->buf_data = dec->buf_len - dec->stream.avail_out;
286 16 : 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 : */
298 8 : res = pullf_read(src, 1, &tmp);
299 :
300 8 : if (res < 0)
301 0 : return res;
302 8 : else if (res > 0)
303 : {
304 0 : px_debug("decompress_read: extra bytes after end of stream");
305 0 : return PXE_PGP_CORRUPT_DATA;
306 : }
307 8 : dec->eof = 1;
308 : }
309 16 : goto restart;
310 : }
311 :
312 : static void
313 8 : decompress_free(void *priv)
314 : {
315 8 : struct DecomprData *dec = priv;
316 :
317 8 : inflateEnd(&dec->stream);
318 8 : px_memset(dec, 0, sizeof(*dec));
319 8 : pfree(dec);
320 8 : }
321 :
322 : static const PullFilterOps
323 : decompress_filter = {
324 : decompress_init, decompress_read, decompress_free
325 : };
326 :
327 : int
328 8 : pgp_decompress_filter(PullFilter **res, PGP_Context *ctx, PullFilter *src)
329 : {
330 8 : 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
|