LCOV - code coverage report
Current view: top level - src/bin/pg_dump - compress_gzip.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18beta1 Lines: 139 171 81.3 %
Date: 2025-05-17 05:15:19 Functions: 15 17 88.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * compress_gzip.c
       4             :  *   Routines for archivers to read or write a gzip compressed data stream.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *     src/bin/pg_dump/compress_gzip.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : #include "postgres_fe.h"
      15             : #include <unistd.h>
      16             : 
      17             : #include "compress_gzip.h"
      18             : #include "pg_backup_utils.h"
      19             : 
      20             : #ifdef HAVE_LIBZ
      21             : #include <zlib.h>
      22             : 
      23             : /*----------------------
      24             :  * Compressor API
      25             :  *----------------------
      26             :  */
      27             : typedef struct GzipCompressorState
      28             : {
      29             :     z_streamp   zp;
      30             : 
      31             :     void       *outbuf;
      32             :     size_t      outsize;
      33             : } GzipCompressorState;
      34             : 
      35             : /* Private routines that support gzip compressed data I/O */
      36             : static void DeflateCompressorInit(CompressorState *cs);
      37             : static void DeflateCompressorEnd(ArchiveHandle *AH, CompressorState *cs);
      38             : static void DeflateCompressorCommon(ArchiveHandle *AH, CompressorState *cs,
      39             :                                     bool flush);
      40             : static void EndCompressorGzip(ArchiveHandle *AH, CompressorState *cs);
      41             : static void WriteDataToArchiveGzip(ArchiveHandle *AH, CompressorState *cs,
      42             :                                    const void *data, size_t dLen);
      43             : static void ReadDataFromArchiveGzip(ArchiveHandle *AH, CompressorState *cs);
      44             : 
      45             : static void
      46         212 : DeflateCompressorInit(CompressorState *cs)
      47             : {
      48             :     GzipCompressorState *gzipcs;
      49             :     z_streamp   zp;
      50             : 
      51         212 :     gzipcs = (GzipCompressorState *) pg_malloc0(sizeof(GzipCompressorState));
      52         212 :     zp = gzipcs->zp = (z_streamp) pg_malloc(sizeof(z_stream));
      53         212 :     zp->zalloc = Z_NULL;
      54         212 :     zp->zfree = Z_NULL;
      55         212 :     zp->opaque = Z_NULL;
      56             : 
      57             :     /*
      58             :      * outsize is the buffer size we tell zlib it can output to.  We actually
      59             :      * allocate one extra byte because some routines want to append a trailing
      60             :      * zero byte to the zlib output.
      61             :      */
      62         212 :     gzipcs->outsize = DEFAULT_IO_BUFFER_SIZE;
      63         212 :     gzipcs->outbuf = pg_malloc(gzipcs->outsize + 1);
      64             : 
      65             :     /* -Z 0 uses the "None" compressor -- not zlib with no compression */
      66             :     Assert(cs->compression_spec.level != 0);
      67             : 
      68         212 :     if (deflateInit(zp, cs->compression_spec.level) != Z_OK)
      69           0 :         pg_fatal("could not initialize compression library: %s", zp->msg);
      70             : 
      71             :     /* Just be paranoid - maybe End is called after Start, with no Write */
      72         212 :     zp->next_out = gzipcs->outbuf;
      73         212 :     zp->avail_out = gzipcs->outsize;
      74             : 
      75             :     /* Keep track of gzipcs */
      76         212 :     cs->private_data = gzipcs;
      77         212 : }
      78             : 
      79             : static void
      80         212 : DeflateCompressorEnd(ArchiveHandle *AH, CompressorState *cs)
      81             : {
      82         212 :     GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
      83             :     z_streamp   zp;
      84             : 
      85         212 :     zp = gzipcs->zp;
      86         212 :     zp->next_in = NULL;
      87         212 :     zp->avail_in = 0;
      88             : 
      89             :     /* Flush any remaining data from zlib buffer */
      90         212 :     DeflateCompressorCommon(AH, cs, true);
      91             : 
      92         212 :     if (deflateEnd(zp) != Z_OK)
      93           0 :         pg_fatal("could not close compression stream: %s", zp->msg);
      94             : 
      95         212 :     pg_free(gzipcs->outbuf);
      96         212 :     pg_free(gzipcs->zp);
      97         212 :     pg_free(gzipcs);
      98         212 :     cs->private_data = NULL;
      99         212 : }
     100             : 
     101             : static void
     102        1054 : DeflateCompressorCommon(ArchiveHandle *AH, CompressorState *cs, bool flush)
     103             : {
     104        1054 :     GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
     105        1054 :     z_streamp   zp = gzipcs->zp;
     106        1054 :     void       *out = gzipcs->outbuf;
     107        1054 :     int         res = Z_OK;
     108             : 
     109        1900 :     while (gzipcs->zp->avail_in != 0 || flush)
     110             :     {
     111        1058 :         res = deflate(zp, flush ? Z_FINISH : Z_NO_FLUSH);
     112        1058 :         if (res == Z_STREAM_ERROR)
     113           0 :             pg_fatal("could not compress data: %s", zp->msg);
     114        1058 :         if ((flush && (zp->avail_out < gzipcs->outsize))
     115         842 :             || (zp->avail_out == 0)
     116         842 :             || (zp->avail_in != 0)
     117             :             )
     118             :         {
     119             :             /*
     120             :              * Extra paranoia: avoid zero-length chunks, since a zero length
     121             :              * chunk is the EOF marker in the custom format. This should never
     122             :              * happen but ...
     123             :              */
     124         216 :             if (zp->avail_out < gzipcs->outsize)
     125             :             {
     126             :                 /*
     127             :                  * Any write function should do its own error checking but to
     128             :                  * make sure we do a check here as well ...
     129             :                  */
     130         216 :                 size_t      len = gzipcs->outsize - zp->avail_out;
     131             : 
     132         216 :                 cs->writeF(AH, out, len);
     133             :             }
     134         216 :             zp->next_out = out;
     135         216 :             zp->avail_out = gzipcs->outsize;
     136             :         }
     137             : 
     138        1058 :         if (res == Z_STREAM_END)
     139         212 :             break;
     140             :     }
     141        1054 : }
     142             : 
     143             : static void
     144         410 : EndCompressorGzip(ArchiveHandle *AH, CompressorState *cs)
     145             : {
     146             :     /* If deflation was initialized, finalize it */
     147         410 :     if (cs->private_data)
     148         212 :         DeflateCompressorEnd(AH, cs);
     149         410 : }
     150             : 
     151             : static void
     152         842 : WriteDataToArchiveGzip(ArchiveHandle *AH, CompressorState *cs,
     153             :                        const void *data, size_t dLen)
     154             : {
     155         842 :     GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
     156             : 
     157         842 :     gzipcs->zp->next_in = data;
     158         842 :     gzipcs->zp->avail_in = dLen;
     159         842 :     DeflateCompressorCommon(AH, cs, false);
     160         842 : }
     161             : 
     162             : static void
     163         198 : ReadDataFromArchiveGzip(ArchiveHandle *AH, CompressorState *cs)
     164             : {
     165             :     z_streamp   zp;
     166             :     char       *out;
     167         198 :     int         res = Z_OK;
     168             :     size_t      cnt;
     169             :     char       *buf;
     170             :     size_t      buflen;
     171             : 
     172         198 :     zp = (z_streamp) pg_malloc(sizeof(z_stream));
     173         198 :     zp->zalloc = Z_NULL;
     174         198 :     zp->zfree = Z_NULL;
     175         198 :     zp->opaque = Z_NULL;
     176             : 
     177         198 :     buflen = DEFAULT_IO_BUFFER_SIZE;
     178         198 :     buf = pg_malloc(buflen);
     179             : 
     180         198 :     out = pg_malloc(DEFAULT_IO_BUFFER_SIZE + 1);
     181             : 
     182         198 :     if (inflateInit(zp) != Z_OK)
     183           0 :         pg_fatal("could not initialize compression library: %s",
     184             :                  zp->msg);
     185             : 
     186             :     /* no minimal chunk size for zlib */
     187         400 :     while ((cnt = cs->readF(AH, &buf, &buflen)))
     188             :     {
     189         202 :         zp->next_in = (void *) buf;
     190         202 :         zp->avail_in = cnt;
     191             : 
     192         416 :         while (zp->avail_in > 0)
     193             :         {
     194         214 :             zp->next_out = (void *) out;
     195         214 :             zp->avail_out = DEFAULT_IO_BUFFER_SIZE;
     196             : 
     197         214 :             res = inflate(zp, 0);
     198         214 :             if (res != Z_OK && res != Z_STREAM_END)
     199           0 :                 pg_fatal("could not uncompress data: %s", zp->msg);
     200             : 
     201         214 :             out[DEFAULT_IO_BUFFER_SIZE - zp->avail_out] = '\0';
     202         214 :             ahwrite(out, 1, DEFAULT_IO_BUFFER_SIZE - zp->avail_out, AH);
     203             :         }
     204             :     }
     205             : 
     206         198 :     zp->next_in = NULL;
     207         198 :     zp->avail_in = 0;
     208         198 :     while (res != Z_STREAM_END)
     209             :     {
     210           0 :         zp->next_out = (void *) out;
     211           0 :         zp->avail_out = DEFAULT_IO_BUFFER_SIZE;
     212           0 :         res = inflate(zp, 0);
     213           0 :         if (res != Z_OK && res != Z_STREAM_END)
     214           0 :             pg_fatal("could not uncompress data: %s", zp->msg);
     215             : 
     216           0 :         out[DEFAULT_IO_BUFFER_SIZE - zp->avail_out] = '\0';
     217           0 :         ahwrite(out, 1, DEFAULT_IO_BUFFER_SIZE - zp->avail_out, AH);
     218             :     }
     219             : 
     220         198 :     if (inflateEnd(zp) != Z_OK)
     221           0 :         pg_fatal("could not close compression library: %s", zp->msg);
     222             : 
     223         198 :     free(buf);
     224         198 :     free(out);
     225         198 :     free(zp);
     226         198 : }
     227             : 
     228             : /* Public routines that support gzip compressed data I/O */
     229             : void
     230         410 : InitCompressorGzip(CompressorState *cs,
     231             :                    const pg_compress_specification compression_spec)
     232             : {
     233         410 :     cs->readData = ReadDataFromArchiveGzip;
     234         410 :     cs->writeData = WriteDataToArchiveGzip;
     235         410 :     cs->end = EndCompressorGzip;
     236             : 
     237         410 :     cs->compression_spec = compression_spec;
     238             : 
     239             :     /*
     240             :      * If the caller has defined a write function, prepare the necessary
     241             :      * state.  Note that if the data is empty, End may be called immediately
     242             :      * after Init, without ever calling Write.
     243             :      */
     244         410 :     if (cs->writeF)
     245         212 :         DeflateCompressorInit(cs);
     246         410 : }
     247             : 
     248             : 
     249             : /*----------------------
     250             :  * Compress File API
     251             :  *----------------------
     252             :  */
     253             : 
     254             : static bool
     255        9272 : Gzip_read(void *ptr, size_t size, size_t *rsize, CompressFileHandle *CFH)
     256             : {
     257        9272 :     gzFile      gzfp = (gzFile) CFH->private_data;
     258             :     int         gzret;
     259             : 
     260        9272 :     gzret = gzread(gzfp, ptr, size);
     261        9272 :     if (gzret <= 0 && !gzeof(gzfp))
     262             :     {
     263             :         int         errnum;
     264           0 :         const char *errmsg = gzerror(gzfp, &errnum);
     265             : 
     266           0 :         pg_fatal("could not read from input file: %s",
     267             :                  errnum == Z_ERRNO ? strerror(errno) : errmsg);
     268             :     }
     269             : 
     270        9272 :     if (rsize)
     271        9272 :         *rsize = (size_t) gzret;
     272             : 
     273        9272 :     return true;
     274             : }
     275             : 
     276             : static bool
     277      892328 : Gzip_write(const void *ptr, size_t size, CompressFileHandle *CFH)
     278             : {
     279      892328 :     gzFile      gzfp = (gzFile) CFH->private_data;
     280             : 
     281      892328 :     return gzwrite(gzfp, ptr, size) > 0;
     282             : }
     283             : 
     284             : static int
     285           0 : Gzip_getc(CompressFileHandle *CFH)
     286             : {
     287           0 :     gzFile      gzfp = (gzFile) CFH->private_data;
     288             :     int         ret;
     289             : 
     290           0 :     errno = 0;
     291           0 :     ret = gzgetc(gzfp);
     292           0 :     if (ret == EOF)
     293             :     {
     294           0 :         if (!gzeof(gzfp))
     295           0 :             pg_fatal("could not read from input file: %m");
     296             :         else
     297           0 :             pg_fatal("could not read from input file: end of file");
     298             :     }
     299             : 
     300           0 :     return ret;
     301             : }
     302             : 
     303             : static char *
     304           8 : Gzip_gets(char *ptr, int size, CompressFileHandle *CFH)
     305             : {
     306           8 :     gzFile      gzfp = (gzFile) CFH->private_data;
     307             : 
     308           8 :     return gzgets(gzfp, ptr, size);
     309             : }
     310             : 
     311             : static bool
     312        3376 : Gzip_close(CompressFileHandle *CFH)
     313             : {
     314        3376 :     gzFile      gzfp = (gzFile) CFH->private_data;
     315             : 
     316        3376 :     CFH->private_data = NULL;
     317             : 
     318        3376 :     return gzclose(gzfp) == Z_OK;
     319             : }
     320             : 
     321             : static bool
     322           4 : Gzip_eof(CompressFileHandle *CFH)
     323             : {
     324           4 :     gzFile      gzfp = (gzFile) CFH->private_data;
     325             : 
     326           4 :     return gzeof(gzfp) == 1;
     327             : }
     328             : 
     329             : static const char *
     330           0 : Gzip_get_error(CompressFileHandle *CFH)
     331             : {
     332           0 :     gzFile      gzfp = (gzFile) CFH->private_data;
     333             :     const char *errmsg;
     334             :     int         errnum;
     335             : 
     336           0 :     errmsg = gzerror(gzfp, &errnum);
     337           0 :     if (errnum == Z_ERRNO)
     338           0 :         errmsg = strerror(errno);
     339             : 
     340           0 :     return errmsg;
     341             : }
     342             : 
     343             : static bool
     344        3376 : Gzip_open(const char *path, int fd, const char *mode, CompressFileHandle *CFH)
     345             : {
     346             :     gzFile      gzfp;
     347             :     char        mode_compression[32];
     348             : 
     349        3376 :     if (CFH->compression_spec.level != Z_DEFAULT_COMPRESSION)
     350             :     {
     351             :         /*
     352             :          * user has specified a compression level, so tell zlib to use it
     353             :          */
     354        1766 :         snprintf(mode_compression, sizeof(mode_compression), "%s%d",
     355             :                  mode, CFH->compression_spec.level);
     356             :     }
     357             :     else
     358        1610 :         strcpy(mode_compression, mode);
     359             : 
     360        3376 :     if (fd >= 0)
     361           0 :         gzfp = gzdopen(dup(fd), mode_compression);
     362             :     else
     363        3376 :         gzfp = gzopen(path, mode_compression);
     364             : 
     365        3376 :     if (gzfp == NULL)
     366           0 :         return false;
     367             : 
     368        3376 :     CFH->private_data = gzfp;
     369             : 
     370        3376 :     return true;
     371             : }
     372             : 
     373             : static bool
     374        1686 : Gzip_open_write(const char *path, const char *mode, CompressFileHandle *CFH)
     375             : {
     376             :     char       *fname;
     377             :     bool        ret;
     378             :     int         save_errno;
     379             : 
     380        1686 :     fname = psprintf("%s.gz", path);
     381        1686 :     ret = CFH->open_func(fname, -1, mode, CFH);
     382             : 
     383        1686 :     save_errno = errno;
     384        1686 :     pg_free(fname);
     385        1686 :     errno = save_errno;
     386             : 
     387        1686 :     return ret;
     388             : }
     389             : 
     390             : void
     391        3376 : InitCompressFileHandleGzip(CompressFileHandle *CFH,
     392             :                            const pg_compress_specification compression_spec)
     393             : {
     394        3376 :     CFH->open_func = Gzip_open;
     395        3376 :     CFH->open_write_func = Gzip_open_write;
     396        3376 :     CFH->read_func = Gzip_read;
     397        3376 :     CFH->write_func = Gzip_write;
     398        3376 :     CFH->gets_func = Gzip_gets;
     399        3376 :     CFH->getc_func = Gzip_getc;
     400        3376 :     CFH->close_func = Gzip_close;
     401        3376 :     CFH->eof_func = Gzip_eof;
     402        3376 :     CFH->get_error_func = Gzip_get_error;
     403             : 
     404        3376 :     CFH->compression_spec = compression_spec;
     405             : 
     406        3376 :     CFH->private_data = NULL;
     407        3376 : }
     408             : #else                           /* HAVE_LIBZ */
     409             : void
     410             : InitCompressorGzip(CompressorState *cs,
     411             :                    const pg_compress_specification compression_spec)
     412             : {
     413             :     pg_fatal("this build does not support compression with %s", "gzip");
     414             : }
     415             : 
     416             : void
     417             : InitCompressFileHandleGzip(CompressFileHandle *CFH,
     418             :                            const pg_compress_specification compression_spec)
     419             : {
     420             :     pg_fatal("this build does not support compression with %s", "gzip");
     421             : }
     422             : #endif                          /* HAVE_LIBZ */

Generated by: LCOV version 1.14