LCOV - code coverage report
Current view: top level - src/bin/pg_dump - compress_gzip.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 213 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 17 0
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            0 : DeflateCompressorInit(CompressorState *cs)
      56              : {
      57            0 :         GzipCompressorState *gzipcs;
      58            0 :         z_streamp       zp;
      59              : 
      60            0 :         gzipcs = (GzipCompressorState *) pg_malloc0(sizeof(GzipCompressorState));
      61            0 :         zp = gzipcs->zp = (z_streamp) pg_malloc(sizeof(z_stream));
      62            0 :         zp->zalloc = Z_NULL;
      63            0 :         zp->zfree = Z_NULL;
      64            0 :         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            0 :         gzipcs->outsize = DEFAULT_IO_BUFFER_SIZE;
      72            0 :         gzipcs->outbuf = pg_malloc(gzipcs->outsize + 1);
      73              : 
      74              :         /* -Z 0 uses the "None" compressor -- not zlib with no compression */
      75            0 :         Assert(cs->compression_spec.level != 0);
      76              : 
      77            0 :         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            0 :         zp->next_out = gzipcs->outbuf;
      82            0 :         zp->avail_out = gzipcs->outsize;
      83              : 
      84              :         /* Keep track of gzipcs */
      85            0 :         cs->private_data = gzipcs;
      86            0 : }
      87              : 
      88              : static void
      89            0 : DeflateCompressorEnd(ArchiveHandle *AH, CompressorState *cs)
      90              : {
      91            0 :         GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
      92            0 :         z_streamp       zp;
      93              : 
      94            0 :         zp = gzipcs->zp;
      95            0 :         zp->next_in = NULL;
      96            0 :         zp->avail_in = 0;
      97              : 
      98              :         /* Flush any remaining data from zlib buffer */
      99            0 :         DeflateCompressorCommon(AH, cs, true);
     100              : 
     101            0 :         if (deflateEnd(zp) != Z_OK)
     102            0 :                 pg_fatal("could not close compression stream: %s", zp->msg);
     103              : 
     104            0 :         pg_free(gzipcs->outbuf);
     105            0 :         pg_free(gzipcs->zp);
     106            0 :         pg_free(gzipcs);
     107            0 :         cs->private_data = NULL;
     108            0 : }
     109              : 
     110              : static void
     111            0 : DeflateCompressorCommon(ArchiveHandle *AH, CompressorState *cs, bool flush)
     112              : {
     113            0 :         GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
     114            0 :         z_streamp       zp = gzipcs->zp;
     115            0 :         void       *out = gzipcs->outbuf;
     116            0 :         int                     res = Z_OK;
     117              : 
     118            0 :         while (gzipcs->zp->avail_in != 0 || flush)
     119              :         {
     120            0 :                 res = deflate(zp, flush ? Z_FINISH : Z_NO_FLUSH);
     121            0 :                 if (res == Z_STREAM_ERROR)
     122            0 :                         pg_fatal("could not compress data: %s", zp->msg);
     123            0 :                 if ((flush && (zp->avail_out < gzipcs->outsize))
     124            0 :                         || (zp->avail_out == 0)
     125            0 :                         || (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            0 :                         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            0 :                                 size_t          len = gzipcs->outsize - zp->avail_out;
     140              : 
     141            0 :                                 cs->writeF(AH, out, len);
     142            0 :                         }
     143            0 :                         zp->next_out = out;
     144            0 :                         zp->avail_out = gzipcs->outsize;
     145            0 :                 }
     146              : 
     147            0 :                 if (res == Z_STREAM_END)
     148            0 :                         break;
     149              :         }
     150            0 : }
     151              : 
     152              : static void
     153            0 : EndCompressorGzip(ArchiveHandle *AH, CompressorState *cs)
     154              : {
     155              :         /* If deflation was initialized, finalize it */
     156            0 :         if (cs->private_data)
     157            0 :                 DeflateCompressorEnd(AH, cs);
     158            0 : }
     159              : 
     160              : static void
     161            0 : WriteDataToArchiveGzip(ArchiveHandle *AH, CompressorState *cs,
     162              :                                            const void *data, size_t dLen)
     163              : {
     164            0 :         GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
     165              : 
     166            0 :         gzipcs->zp->next_in = data;
     167            0 :         gzipcs->zp->avail_in = dLen;
     168            0 :         DeflateCompressorCommon(AH, cs, false);
     169            0 : }
     170              : 
     171              : static void
     172            0 : ReadDataFromArchiveGzip(ArchiveHandle *AH, CompressorState *cs)
     173              : {
     174            0 :         z_streamp       zp;
     175            0 :         char       *out;
     176            0 :         int                     res = Z_OK;
     177            0 :         size_t          cnt;
     178            0 :         char       *buf;
     179            0 :         size_t          buflen;
     180              : 
     181            0 :         zp = (z_streamp) pg_malloc(sizeof(z_stream));
     182            0 :         zp->zalloc = Z_NULL;
     183            0 :         zp->zfree = Z_NULL;
     184            0 :         zp->opaque = Z_NULL;
     185              : 
     186            0 :         buflen = DEFAULT_IO_BUFFER_SIZE;
     187            0 :         buf = pg_malloc(buflen);
     188              : 
     189            0 :         out = pg_malloc(DEFAULT_IO_BUFFER_SIZE + 1);
     190              : 
     191            0 :         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            0 :         while ((cnt = cs->readF(AH, &buf, &buflen)))
     197              :         {
     198            0 :                 zp->next_in = (void *) buf;
     199            0 :                 zp->avail_in = cnt;
     200              : 
     201            0 :                 while (zp->avail_in > 0)
     202              :                 {
     203            0 :                         zp->next_out = (void *) out;
     204            0 :                         zp->avail_out = DEFAULT_IO_BUFFER_SIZE;
     205              : 
     206            0 :                         res = inflate(zp, 0);
     207            0 :                         if (res != Z_OK && res != Z_STREAM_END)
     208            0 :                                 pg_fatal("could not uncompress data: %s", zp->msg);
     209              : 
     210            0 :                         out[DEFAULT_IO_BUFFER_SIZE - zp->avail_out] = '\0';
     211            0 :                         ahwrite(out, 1, DEFAULT_IO_BUFFER_SIZE - zp->avail_out, AH);
     212              :                 }
     213              :         }
     214              : 
     215            0 :         zp->next_in = NULL;
     216            0 :         zp->avail_in = 0;
     217            0 :         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            0 :         if (inflateEnd(zp) != Z_OK)
     230            0 :                 pg_fatal("could not close compression library: %s", zp->msg);
     231              : 
     232            0 :         free(buf);
     233            0 :         free(out);
     234            0 :         free(zp);
     235            0 : }
     236              : 
     237              : /* Public routines that support gzip compressed data I/O */
     238              : void
     239            0 : InitCompressorGzip(CompressorState *cs,
     240              :                                    const pg_compress_specification compression_spec)
     241              : {
     242            0 :         cs->readData = ReadDataFromArchiveGzip;
     243            0 :         cs->writeData = WriteDataToArchiveGzip;
     244            0 :         cs->end = EndCompressorGzip;
     245              : 
     246            0 :         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            0 :         if (cs->writeF)
     254            0 :                 DeflateCompressorInit(cs);
     255            0 : }
     256              : 
     257              : 
     258              : /*----------------------
     259              :  * Compress File API
     260              :  *----------------------
     261              :  */
     262              : 
     263              : static size_t
     264            0 : Gzip_read(void *ptr, size_t size, CompressFileHandle *CFH)
     265              : {
     266            0 :         gzFile          gzfp = (gzFile) CFH->private_data;
     267            0 :         int                     gzret;
     268              : 
     269              :         /* Reading zero bytes must be a no-op */
     270            0 :         if (size == 0)
     271            0 :                 return 0;
     272              : 
     273            0 :         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            0 :         if (gzret <= 0)
     281              :         {
     282            0 :                 int                     errnum;
     283            0 :                 const char *errmsg;
     284              : 
     285            0 :                 if (gzret == 0 && gzeof(gzfp))
     286            0 :                         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            0 :         }
     293              : 
     294            0 :         return (size_t) gzret;
     295            0 : }
     296              : 
     297              : static void
     298            0 : Gzip_write(const void *ptr, size_t size, CompressFileHandle *CFH)
     299              : {
     300            0 :         gzFile          gzfp = (gzFile) CFH->private_data;
     301            0 :         int                     errnum;
     302            0 :         const char *errmsg;
     303              : 
     304            0 :         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            0 :         }
     310            0 : }
     311              : 
     312              : static int
     313            0 : Gzip_getc(CompressFileHandle *CFH)
     314              : {
     315            0 :         gzFile          gzfp = (gzFile) CFH->private_data;
     316            0 :         int                     ret;
     317              : 
     318            0 :         errno = 0;
     319            0 :         ret = gzgetc(gzfp);
     320            0 :         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            0 :         }
     327              : 
     328            0 :         return ret;
     329            0 : }
     330              : 
     331              : static char *
     332            0 : Gzip_gets(char *ptr, int size, CompressFileHandle *CFH)
     333              : {
     334            0 :         gzFile          gzfp = (gzFile) CFH->private_data;
     335              : 
     336            0 :         return gzgets(gzfp, ptr, size);
     337            0 : }
     338              : 
     339              : static bool
     340            0 : Gzip_close(CompressFileHandle *CFH)
     341              : {
     342            0 :         gzFile          gzfp = (gzFile) CFH->private_data;
     343              : 
     344            0 :         CFH->private_data = NULL;
     345              : 
     346            0 :         return gzclose(gzfp) == Z_OK;
     347            0 : }
     348              : 
     349              : static bool
     350            0 : Gzip_eof(CompressFileHandle *CFH)
     351              : {
     352            0 :         gzFile          gzfp = (gzFile) CFH->private_data;
     353              : 
     354            0 :         return gzeof(gzfp) == 1;
     355            0 : }
     356              : 
     357              : static const char *
     358            0 : Gzip_get_error(CompressFileHandle *CFH)
     359              : {
     360            0 :         gzFile          gzfp = (gzFile) CFH->private_data;
     361            0 :         const char *errmsg;
     362            0 :         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            0 : }
     370              : 
     371              : static bool
     372            0 : Gzip_open(const char *path, int fd, const char *mode, CompressFileHandle *CFH)
     373              : {
     374            0 :         gzFile          gzfp;
     375            0 :         char            mode_compression[32];
     376              : 
     377            0 :         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            0 :                 snprintf(mode_compression, sizeof(mode_compression), "%s%d",
     383            0 :                                  mode, CFH->compression_spec.level);
     384            0 :         }
     385              :         else
     386            0 :                 strcpy(mode_compression, mode);
     387              : 
     388            0 :         if (fd >= 0)
     389            0 :                 gzfp = gzdopen(dup(fd), mode_compression);
     390              :         else
     391            0 :                 gzfp = gzopen(path, mode_compression);
     392              : 
     393            0 :         if (gzfp == NULL)
     394            0 :                 return false;
     395              : 
     396            0 :         CFH->private_data = gzfp;
     397              : 
     398            0 :         return true;
     399            0 : }
     400              : 
     401              : static bool
     402            0 : Gzip_open_write(const char *path, const char *mode, CompressFileHandle *CFH)
     403              : {
     404            0 :         char       *fname;
     405            0 :         bool            ret;
     406            0 :         int                     save_errno;
     407              : 
     408            0 :         fname = psprintf("%s.gz", path);
     409            0 :         ret = CFH->open_func(fname, -1, mode, CFH);
     410              : 
     411            0 :         save_errno = errno;
     412            0 :         pg_free(fname);
     413            0 :         errno = save_errno;
     414              : 
     415            0 :         return ret;
     416            0 : }
     417              : 
     418              : void
     419            0 : InitCompressFileHandleGzip(CompressFileHandle *CFH,
     420              :                                                    const pg_compress_specification compression_spec)
     421              : {
     422            0 :         CFH->open_func = Gzip_open;
     423            0 :         CFH->open_write_func = Gzip_open_write;
     424            0 :         CFH->read_func = Gzip_read;
     425            0 :         CFH->write_func = Gzip_write;
     426            0 :         CFH->gets_func = Gzip_gets;
     427            0 :         CFH->getc_func = Gzip_getc;
     428            0 :         CFH->close_func = Gzip_close;
     429            0 :         CFH->eof_func = Gzip_eof;
     430            0 :         CFH->get_error_func = Gzip_get_error;
     431              : 
     432            0 :         CFH->compression_spec = compression_spec;
     433              : 
     434            0 :         CFH->private_data = NULL;
     435            0 : }
     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.3.2-1