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

Generated by: LCOV version 2.0-1