LCOV - code coverage report
Current view: top level - contrib/pgcrypto - pgp-compress.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 108 125 86.4 %
Date: 2024-04-26 20:11:02 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.14