Age Owner Branch data 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 *
6853 bruce@momjian.us 58 :CBC 22 : z_alloc(void *priv, unsigned n_items, unsigned item_len)
59 : : {
1297 michael@paquier.xyz 60 : 22 : return palloc(n_items * item_len);
61 : : }
62 : :
63 : : static void
6853 bruce@momjian.us 64 : 22 : z_free(void *priv, void *addr)
65 : : {
1297 michael@paquier.xyz 66 : 22 : pfree(addr);
6853 bruce@momjian.us 67 : 22 : }
68 : :
69 : : static int
5421 70 : 3 : compress_init(PushFilter *next, void *init_arg, void **priv_p)
71 : : {
72 : : int res;
73 : : struct ZipStat *st;
6853 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)
6853 bruce@momjian.us 78 :UBC 0 : return PXE_PGP_UNSUPPORTED_COMPR;
79 : :
80 : : /*
81 : : * init
82 : : */
1297 michael@paquier.xyz 83 :CBC 3 : st = palloc0(sizeof(*st));
6853 bruce@momjian.us 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 : : {
1297 michael@paquier.xyz 95 :UBC 0 : pfree(st);
6853 bruce@momjian.us 96 : 0 : return PXE_PGP_COMPRESSION_ERROR;
97 : : }
6853 bruce@momjian.us 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
5421 107 : 4 : compress_process(PushFilter *next, void *priv, const uint8 *data, int len)
108 : : {
109 : : int res,
110 : : n_out;
6853 111 : 4 : struct ZipStat *st = priv;
112 : :
113 : : /*
114 : : * process data
115 : : */
251 peter@eisentraut.org 116 :GNC 4 : st->stream.next_in = data;
1361 tgl@sss.pgh.pa.us 117 :CBC 4 : st->stream.avail_in = len;
118 [ + + ]: 12 : while (st->stream.avail_in > 0)
119 : : {
6853 bruce@momjian.us 120 : 4 : st->stream.next_out = st->buf;
121 : 4 : st->stream.avail_out = st->buf_len;
1361 tgl@sss.pgh.pa.us 122 : 4 : res = deflate(&st->stream, Z_NO_FLUSH);
6853 bruce@momjian.us 123 [ - + ]: 4 : if (res != Z_OK)
6853 bruce@momjian.us 124 :UBC 0 : return PXE_PGP_COMPRESSION_ERROR;
125 : :
6853 bruce@momjian.us 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)
6853 bruce@momjian.us 131 :UBC 0 : return res;
132 : : }
133 : : }
134 : :
6853 bruce@momjian.us 135 :CBC 4 : return 0;
136 : : }
137 : :
138 : : static int
5421 139 : 3 : compress_flush(PushFilter *next, void *priv)
140 : : {
141 : : int res,
142 : : zres,
143 : : n_out;
6853 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)
6853 bruce@momjian.us 154 :UBC 0 : return PXE_PGP_COMPRESSION_ERROR;
155 : :
6853 bruce@momjian.us 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)
6853 bruce@momjian.us 161 :UBC 0 : return res;
162 : : }
6853 bruce@momjian.us 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);
3650 175 : 3 : px_memset(st, 0, sizeof(*st));
1297 michael@paquier.xyz 176 : 3 : pfree(st);
6853 bruce@momjian.us 177 : 3 : }
178 : :
179 : : static const PushFilterOps
180 : : compress_filter = {
181 : : compress_init, compress_process, compress_flush, compress_free
182 : : };
183 : :
184 : : int
5421 185 : 3 : pgp_compress_filter(PushFilter **res, PGP_Context *ctx, PushFilter *dst)
186 : : {
6853 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
5421 204 : 4 : decompress_init(void **priv_p, void *arg, PullFilter *src)
205 : : {
6853 206 : 4 : PGP_Context *ctx = arg;
207 : : struct DecomprData *dec;
208 : : int res;
209 : :
210 [ + + ]: 4 : if (ctx->compress_algo != PGP_COMPR_ZLIB
6756 211 [ - + ]: 3 : && ctx->compress_algo != PGP_COMPR_ZIP)
6853 bruce@momjian.us 212 :UBC 0 : return PXE_PGP_UNSUPPORTED_COMPR;
213 : :
1297 michael@paquier.xyz 214 :CBC 4 : dec = palloc0(sizeof(*dec));
6853 bruce@momjian.us 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 : : {
1297 michael@paquier.xyz 227 :UBC 0 : pfree(dec);
6853 bruce@momjian.us 228 : 0 : px_debug("decompress_init: inflateInit error");
229 : 0 : return PXE_PGP_COMPRESSION_ERROR;
230 : : }
231 : :
6853 bruce@momjian.us 232 :CBC 4 : return 0;
233 : : }
234 : :
235 : : static int
5421 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;
6853 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 : :
1361 michael@paquier.xyz 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)
1361 michael@paquier.xyz 263 :UBC 0 : return res;
1361 michael@paquier.xyz 264 :CBC 8 : dec->stream.next_in = tmp;
265 : 8 : dec->stream.avail_in = res;
266 : : }
267 : :
6853 bruce@momjian.us 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 : : {
6853 bruce@momjian.us 281 :UBC 0 : px_debug("decompress_read: inflate error: %d", res);
282 : 0 : return PXE_PGP_CORRUPT_DATA;
283 : : }
284 : :
6853 bruce@momjian.us 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 : : */
1357 michael@paquier.xyz 298 : 4 : res = pullf_read(src, 1, &tmp);
299 : :
300 [ - + ]: 4 : if (res < 0)
1357 michael@paquier.xyz 301 :UBC 0 : return res;
1357 michael@paquier.xyz 302 [ - + ]:CBC 4 : else if (res > 0)
303 : : {
1357 michael@paquier.xyz 304 :UBC 0 : px_debug("decompress_read: extra bytes after end of stream");
305 : 0 : return PXE_PGP_CORRUPT_DATA;
306 : : }
6853 bruce@momjian.us 307 :CBC 4 : dec->eof = 1;
308 : : }
309 : 8 : goto restart;
310 : : }
311 : :
312 : : static void
6756 313 : 4 : decompress_free(void *priv)
314 : : {
6853 315 : 4 : struct DecomprData *dec = priv;
316 : :
317 : 4 : inflateEnd(&dec->stream);
3650 318 : 4 : px_memset(dec, 0, sizeof(*dec));
1297 michael@paquier.xyz 319 : 4 : pfree(dec);
6853 bruce@momjian.us 320 : 4 : }
321 : :
322 : : static const PullFilterOps
323 : : decompress_filter = {
324 : : decompress_init, decompress_read, decompress_free
325 : : };
326 : :
327 : : int
5421 328 : 4 : pgp_decompress_filter(PullFilter **res, PGP_Context *ctx, PullFilter *src)
329 : : {
6853 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
|