LCOV - code coverage report
Current view: top level - src/bin/pg_dump - compress_gzip.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 81.3 % 182 148
Test Date: 2026-04-07 14:16:30 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          196 : DeflateCompressorInit(CompressorState *cs)
      56              : {
      57              :     GzipCompressorState *gzipcs;
      58              :     z_streamp   zp;
      59              : 
      60          196 :     gzipcs = pg_malloc0_object(GzipCompressorState);
      61          196 :     zp = gzipcs->zp = pg_malloc_object(z_stream);
      62          196 :     zp->zalloc = Z_NULL;
      63          196 :     zp->zfree = Z_NULL;
      64          196 :     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          196 :     gzipcs->outsize = DEFAULT_IO_BUFFER_SIZE;
      72          196 :     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          196 :     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          196 :     zp->next_out = gzipcs->outbuf;
      82          196 :     zp->avail_out = gzipcs->outsize;
      83              : 
      84              :     /* Keep track of gzipcs */
      85          196 :     cs->private_data = gzipcs;
      86          196 : }
      87              : 
      88              : static void
      89          196 : DeflateCompressorEnd(ArchiveHandle *AH, CompressorState *cs)
      90              : {
      91          196 :     GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
      92              :     z_streamp   zp;
      93              : 
      94          196 :     zp = gzipcs->zp;
      95          196 :     zp->next_in = NULL;
      96          196 :     zp->avail_in = 0;
      97              : 
      98              :     /* Flush any remaining data from zlib buffer */
      99          196 :     DeflateCompressorCommon(AH, cs, true);
     100              : 
     101          196 :     if (deflateEnd(zp) != Z_OK)
     102            0 :         pg_fatal("could not close compression stream: %s", zp->msg);
     103              : 
     104          196 :     pg_free(gzipcs->outbuf);
     105          196 :     pg_free(gzipcs->zp);
     106          196 :     pg_free(gzipcs);
     107          196 :     cs->private_data = NULL;
     108          196 : }
     109              : 
     110              : static void
     111         1147 : DeflateCompressorCommon(ArchiveHandle *AH, CompressorState *cs, bool flush)
     112              : {
     113         1147 :     GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
     114         1147 :     z_streamp   zp = gzipcs->zp;
     115         1147 :     void       *out = gzipcs->outbuf;
     116         1147 :     int         res = Z_OK;
     117              : 
     118         2098 :     while (gzipcs->zp->avail_in != 0 || flush)
     119              :     {
     120         1147 :         res = deflate(zp, flush ? Z_FINISH : Z_NO_FLUSH);
     121         1147 :         if (res == Z_STREAM_ERROR)
     122            0 :             pg_fatal("could not compress data: %s", zp->msg);
     123         1147 :         if ((flush && (zp->avail_out < gzipcs->outsize))
     124          951 :             || (zp->avail_out == 0)
     125          951 :             || (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          196 :             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          196 :                 size_t      len = gzipcs->outsize - zp->avail_out;
     140              : 
     141          196 :                 cs->writeF(AH, out, len);
     142              :             }
     143          196 :             zp->next_out = out;
     144          196 :             zp->avail_out = gzipcs->outsize;
     145              :         }
     146              : 
     147         1147 :         if (res == Z_STREAM_END)
     148          196 :             break;
     149              :     }
     150         1147 : }
     151              : 
     152              : static void
     153          395 : EndCompressorGzip(ArchiveHandle *AH, CompressorState *cs)
     154              : {
     155              :     /* If deflation was initialized, finalize it */
     156          395 :     if (cs->private_data)
     157          196 :         DeflateCompressorEnd(AH, cs);
     158          395 : }
     159              : 
     160              : static void
     161          951 : WriteDataToArchiveGzip(ArchiveHandle *AH, CompressorState *cs,
     162              :                        const void *data, size_t dLen)
     163              : {
     164          951 :     GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
     165              : 
     166          951 :     gzipcs->zp->next_in = data;
     167          951 :     gzipcs->zp->avail_in = dLen;
     168          951 :     DeflateCompressorCommon(AH, cs, false);
     169          951 : }
     170              : 
     171              : static void
     172          199 : ReadDataFromArchiveGzip(ArchiveHandle *AH, CompressorState *cs)
     173              : {
     174              :     z_streamp   zp;
     175              :     char       *out;
     176          199 :     int         res = Z_OK;
     177              :     size_t      cnt;
     178              :     char       *buf;
     179              :     size_t      buflen;
     180              : 
     181          199 :     zp = pg_malloc_object(z_stream);
     182          199 :     zp->zalloc = Z_NULL;
     183          199 :     zp->zfree = Z_NULL;
     184          199 :     zp->opaque = Z_NULL;
     185              : 
     186          199 :     buflen = DEFAULT_IO_BUFFER_SIZE;
     187          199 :     buf = pg_malloc(buflen);
     188              : 
     189          199 :     out = pg_malloc(DEFAULT_IO_BUFFER_SIZE + 1);
     190              : 
     191          199 :     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          398 :     while ((cnt = cs->readF(AH, &buf, &buflen)))
     197              :     {
     198          199 :         zp->next_in = (void *) buf;
     199          199 :         zp->avail_in = cnt;
     200              : 
     201          400 :         while (zp->avail_in > 0)
     202              :         {
     203          201 :             zp->next_out = (void *) out;
     204          201 :             zp->avail_out = DEFAULT_IO_BUFFER_SIZE;
     205              : 
     206          201 :             res = inflate(zp, 0);
     207          201 :             if (res != Z_OK && res != Z_STREAM_END)
     208            0 :                 pg_fatal("could not uncompress data: %s", zp->msg);
     209              : 
     210          201 :             out[DEFAULT_IO_BUFFER_SIZE - zp->avail_out] = '\0';
     211          201 :             ahwrite(out, 1, DEFAULT_IO_BUFFER_SIZE - zp->avail_out, AH);
     212              :         }
     213              :     }
     214              : 
     215          199 :     zp->next_in = NULL;
     216          199 :     zp->avail_in = 0;
     217          199 :     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          199 :     if (inflateEnd(zp) != Z_OK)
     230            0 :         pg_fatal("could not close compression library: %s", zp->msg);
     231              : 
     232          199 :     free(buf);
     233          199 :     free(out);
     234          199 :     free(zp);
     235          199 : }
     236              : 
     237              : /* Public routines that support gzip compressed data I/O */
     238              : void
     239          395 : InitCompressorGzip(CompressorState *cs,
     240              :                    const pg_compress_specification compression_spec)
     241              : {
     242          395 :     cs->readData = ReadDataFromArchiveGzip;
     243          395 :     cs->writeData = WriteDataToArchiveGzip;
     244          395 :     cs->end = EndCompressorGzip;
     245              : 
     246          395 :     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          395 :     if (cs->writeF)
     254          196 :         DeflateCompressorInit(cs);
     255          395 : }
     256              : 
     257              : 
     258              : /*----------------------
     259              :  * Compress File API
     260              :  *----------------------
     261              :  */
     262              : 
     263              : static size_t
     264          593 : Gzip_read(void *ptr, size_t size, CompressFileHandle *CFH)
     265              : {
     266          593 :     gzFile      gzfp = (gzFile) CFH->private_data;
     267              :     int         gzret;
     268              : 
     269              :     /* Reading zero bytes must be a no-op */
     270          593 :     if (size == 0)
     271           16 :         return 0;
     272              : 
     273          577 :     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          577 :     if (gzret <= 0)
     281              :     {
     282              :         int         errnum;
     283              :         const char *errmsg;
     284              : 
     285          217 :         if (gzret == 0 && gzeof(gzfp))
     286          217 :             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          360 :     return (size_t) gzret;
     295              : }
     296              : 
     297              : static void
     298        25128 : Gzip_write(const void *ptr, size_t size, CompressFileHandle *CFH)
     299              : {
     300        25128 :     gzFile      gzfp = (gzFile) CFH->private_data;
     301              :     int         errnum;
     302              :     const char *errmsg;
     303              : 
     304        25128 :     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        25128 : }
     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          439 : Gzip_close(CompressFileHandle *CFH)
     341              : {
     342          439 :     gzFile      gzfp = (gzFile) CFH->private_data;
     343              : 
     344          439 :     CFH->private_data = NULL;
     345              : 
     346          439 :     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          439 : Gzip_open(const char *path, int fd, const char *mode, CompressFileHandle *CFH)
     373              : {
     374              :     gzFile      gzfp;
     375              :     char        mode_compression[32];
     376              : 
     377          439 :     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          223 :         snprintf(mode_compression, sizeof(mode_compression), "%s%d",
     383              :                  mode, CFH->compression_spec.level);
     384              :     }
     385              :     else
     386          216 :         strcpy(mode_compression, mode);
     387              : 
     388          439 :     if (fd >= 0)
     389              :     {
     390            0 :         int         dup_fd = dup(fd);
     391              : 
     392            0 :         if (dup_fd < 0)
     393            0 :             return false;
     394            0 :         gzfp = gzdopen(dup_fd, mode_compression);
     395            0 :         if (gzfp == NULL)
     396              :         {
     397            0 :             close(dup_fd);
     398            0 :             return false;
     399              :         }
     400              :     }
     401              :     else
     402              :     {
     403          439 :         gzfp = gzopen(path, mode_compression);
     404          439 :         if (gzfp == NULL)
     405            0 :             return false;
     406              :     }
     407              : 
     408          439 :     CFH->private_data = gzfp;
     409              : 
     410          439 :     return true;
     411              : }
     412              : 
     413              : static bool
     414          219 : Gzip_open_write(const char *path, const char *mode, CompressFileHandle *CFH)
     415              : {
     416              :     char       *fname;
     417              :     bool        ret;
     418              :     int         save_errno;
     419              : 
     420          219 :     fname = psprintf("%s.gz", path);
     421          219 :     ret = CFH->open_func(fname, -1, mode, CFH);
     422              : 
     423          219 :     save_errno = errno;
     424          219 :     pg_free(fname);
     425          219 :     errno = save_errno;
     426              : 
     427          219 :     return ret;
     428              : }
     429              : 
     430              : void
     431          439 : InitCompressFileHandleGzip(CompressFileHandle *CFH,
     432              :                            const pg_compress_specification compression_spec)
     433              : {
     434          439 :     CFH->open_func = Gzip_open;
     435          439 :     CFH->open_write_func = Gzip_open_write;
     436          439 :     CFH->read_func = Gzip_read;
     437          439 :     CFH->write_func = Gzip_write;
     438          439 :     CFH->gets_func = Gzip_gets;
     439          439 :     CFH->getc_func = Gzip_getc;
     440          439 :     CFH->close_func = Gzip_close;
     441          439 :     CFH->eof_func = Gzip_eof;
     442          439 :     CFH->get_error_func = Gzip_get_error;
     443              : 
     444          439 :     CFH->compression_spec = compression_spec;
     445              : 
     446          439 :     CFH->private_data = NULL;
     447          439 : }
     448              : #else                           /* HAVE_LIBZ */
     449              : void
     450              : InitCompressorGzip(CompressorState *cs,
     451              :                    const pg_compress_specification compression_spec)
     452              : {
     453              :     pg_fatal("this build does not support compression with %s", "gzip");
     454              : }
     455              : 
     456              : void
     457              : InitCompressFileHandleGzip(CompressFileHandle *CFH,
     458              :                            const pg_compress_specification compression_spec)
     459              : {
     460              :     pg_fatal("this build does not support compression with %s", "gzip");
     461              : }
     462              : #endif                          /* HAVE_LIBZ */
        

Generated by: LCOV version 2.0-1