LCOV - code coverage report
Current view: top level - src/bin/pg_dump - compress_gzip.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 140 174 80.5 %
Date: 2025-08-31 01:17:28 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         330 : DeflateCompressorInit(CompressorState *cs)
      47             : {
      48             :     GzipCompressorState *gzipcs;
      49             :     z_streamp   zp;
      50             : 
      51         330 :     gzipcs = (GzipCompressorState *) pg_malloc0(sizeof(GzipCompressorState));
      52         330 :     zp = gzipcs->zp = (z_streamp) pg_malloc(sizeof(z_stream));
      53         330 :     zp->zalloc = Z_NULL;
      54         330 :     zp->zfree = Z_NULL;
      55         330 :     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         330 :     gzipcs->outsize = DEFAULT_IO_BUFFER_SIZE;
      63         330 :     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         330 :     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         330 :     zp->next_out = gzipcs->outbuf;
      73         330 :     zp->avail_out = gzipcs->outsize;
      74             : 
      75             :     /* Keep track of gzipcs */
      76         330 :     cs->private_data = gzipcs;
      77         330 : }
      78             : 
      79             : static void
      80         330 : DeflateCompressorEnd(ArchiveHandle *AH, CompressorState *cs)
      81             : {
      82         330 :     GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
      83             :     z_streamp   zp;
      84             : 
      85         330 :     zp = gzipcs->zp;
      86         330 :     zp->next_in = NULL;
      87         330 :     zp->avail_in = 0;
      88             : 
      89             :     /* Flush any remaining data from zlib buffer */
      90         330 :     DeflateCompressorCommon(AH, cs, true);
      91             : 
      92         330 :     if (deflateEnd(zp) != Z_OK)
      93           0 :         pg_fatal("could not close compression stream: %s", zp->msg);
      94             : 
      95         330 :     pg_free(gzipcs->outbuf);
      96         330 :     pg_free(gzipcs->zp);
      97         330 :     pg_free(gzipcs);
      98         330 :     cs->private_data = NULL;
      99         330 : }
     100             : 
     101             : static void
     102        1198 : DeflateCompressorCommon(ArchiveHandle *AH, CompressorState *cs, bool flush)
     103             : {
     104        1198 :     GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
     105        1198 :     z_streamp   zp = gzipcs->zp;
     106        1198 :     void       *out = gzipcs->outbuf;
     107        1198 :     int         res = Z_OK;
     108             : 
     109        2070 :     while (gzipcs->zp->avail_in != 0 || flush)
     110             :     {
     111        1202 :         res = deflate(zp, flush ? Z_FINISH : Z_NO_FLUSH);
     112        1202 :         if (res == Z_STREAM_ERROR)
     113           0 :             pg_fatal("could not compress data: %s", zp->msg);
     114        1202 :         if ((flush && (zp->avail_out < gzipcs->outsize))
     115         868 :             || (zp->avail_out == 0)
     116         868 :             || (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         334 :             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         334 :                 size_t      len = gzipcs->outsize - zp->avail_out;
     131             : 
     132         334 :                 cs->writeF(AH, out, len);
     133             :             }
     134         334 :             zp->next_out = out;
     135         334 :             zp->avail_out = gzipcs->outsize;
     136             :         }
     137             : 
     138        1202 :         if (res == Z_STREAM_END)
     139         330 :             break;
     140             :     }
     141        1198 : }
     142             : 
     143             : static void
     144         622 : EndCompressorGzip(ArchiveHandle *AH, CompressorState *cs)
     145             : {
     146             :     /* If deflation was initialized, finalize it */
     147         622 :     if (cs->private_data)
     148         330 :         DeflateCompressorEnd(AH, cs);
     149         622 : }
     150             : 
     151             : static void
     152         868 : WriteDataToArchiveGzip(ArchiveHandle *AH, CompressorState *cs,
     153             :                        const void *data, size_t dLen)
     154             : {
     155         868 :     GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
     156             : 
     157         868 :     gzipcs->zp->next_in = data;
     158         868 :     gzipcs->zp->avail_in = dLen;
     159         868 :     DeflateCompressorCommon(AH, cs, false);
     160         868 : }
     161             : 
     162             : static void
     163         292 : ReadDataFromArchiveGzip(ArchiveHandle *AH, CompressorState *cs)
     164             : {
     165             :     z_streamp   zp;
     166             :     char       *out;
     167         292 :     int         res = Z_OK;
     168             :     size_t      cnt;
     169             :     char       *buf;
     170             :     size_t      buflen;
     171             : 
     172         292 :     zp = (z_streamp) pg_malloc(sizeof(z_stream));
     173         292 :     zp->zalloc = Z_NULL;
     174         292 :     zp->zfree = Z_NULL;
     175         292 :     zp->opaque = Z_NULL;
     176             : 
     177         292 :     buflen = DEFAULT_IO_BUFFER_SIZE;
     178         292 :     buf = pg_malloc(buflen);
     179             : 
     180         292 :     out = pg_malloc(DEFAULT_IO_BUFFER_SIZE + 1);
     181             : 
     182         292 :     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         588 :     while ((cnt = cs->readF(AH, &buf, &buflen)))
     188             :     {
     189         296 :         zp->next_in = (void *) buf;
     190         296 :         zp->avail_in = cnt;
     191             : 
     192         604 :         while (zp->avail_in > 0)
     193             :         {
     194         308 :             zp->next_out = (void *) out;
     195         308 :             zp->avail_out = DEFAULT_IO_BUFFER_SIZE;
     196             : 
     197         308 :             res = inflate(zp, 0);
     198         308 :             if (res != Z_OK && res != Z_STREAM_END)
     199           0 :                 pg_fatal("could not uncompress data: %s", zp->msg);
     200             : 
     201         308 :             out[DEFAULT_IO_BUFFER_SIZE - zp->avail_out] = '\0';
     202         308 :             ahwrite(out, 1, DEFAULT_IO_BUFFER_SIZE - zp->avail_out, AH);
     203             :         }
     204             :     }
     205             : 
     206         292 :     zp->next_in = NULL;
     207         292 :     zp->avail_in = 0;
     208         292 :     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         292 :     if (inflateEnd(zp) != Z_OK)
     221           0 :         pg_fatal("could not close compression library: %s", zp->msg);
     222             : 
     223         292 :     free(buf);
     224         292 :     free(out);
     225         292 :     free(zp);
     226         292 : }
     227             : 
     228             : /* Public routines that support gzip compressed data I/O */
     229             : void
     230         622 : InitCompressorGzip(CompressorState *cs,
     231             :                    const pg_compress_specification compression_spec)
     232             : {
     233         622 :     cs->readData = ReadDataFromArchiveGzip;
     234         622 :     cs->writeData = WriteDataToArchiveGzip;
     235         622 :     cs->end = EndCompressorGzip;
     236             : 
     237         622 :     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         622 :     if (cs->writeF)
     245         330 :         DeflateCompressorInit(cs);
     246         622 : }
     247             : 
     248             : 
     249             : /*----------------------
     250             :  * Compress File API
     251             :  *----------------------
     252             :  */
     253             : 
     254             : static size_t
     255         658 : Gzip_read(void *ptr, size_t size, CompressFileHandle *CFH)
     256             : {
     257         658 :     gzFile      gzfp = (gzFile) CFH->private_data;
     258             :     int         gzret;
     259             : 
     260         658 :     gzret = gzread(gzfp, ptr, size);
     261             : 
     262             :     /*
     263             :      * gzread returns zero on EOF as well as some error conditions, and less
     264             :      * than zero on other error conditions, so we need to inspect for EOF on
     265             :      * zero.
     266             :      */
     267         658 :     if (gzret <= 0)
     268             :     {
     269             :         int         errnum;
     270             :         const char *errmsg;
     271             : 
     272         292 :         if (gzret == 0 && gzeof(gzfp))
     273         292 :             return 0;
     274             : 
     275           0 :         errmsg = gzerror(gzfp, &errnum);
     276             : 
     277           0 :         pg_fatal("could not read from input file: %s",
     278             :                  errnum == Z_ERRNO ? strerror(errno) : errmsg);
     279             :     }
     280             : 
     281         366 :     return (size_t) gzret;
     282             : }
     283             : 
     284             : static void
     285       53482 : Gzip_write(const void *ptr, size_t size, CompressFileHandle *CFH)
     286             : {
     287       53482 :     gzFile      gzfp = (gzFile) CFH->private_data;
     288             :     int         errnum;
     289             :     const char *errmsg;
     290             : 
     291       53482 :     if (gzwrite(gzfp, ptr, size) != size)
     292             :     {
     293           0 :         errmsg = gzerror(gzfp, &errnum);
     294           0 :         pg_fatal("could not write to file: %s",
     295             :                  errnum == Z_ERRNO ? strerror(errno) : errmsg);
     296             :     }
     297       53482 : }
     298             : 
     299             : static int
     300           0 : Gzip_getc(CompressFileHandle *CFH)
     301             : {
     302           0 :     gzFile      gzfp = (gzFile) CFH->private_data;
     303             :     int         ret;
     304             : 
     305           0 :     errno = 0;
     306           0 :     ret = gzgetc(gzfp);
     307           0 :     if (ret == EOF)
     308             :     {
     309           0 :         if (!gzeof(gzfp))
     310           0 :             pg_fatal("could not read from input file: %m");
     311             :         else
     312           0 :             pg_fatal("could not read from input file: end of file");
     313             :     }
     314             : 
     315           0 :     return ret;
     316             : }
     317             : 
     318             : static char *
     319           8 : Gzip_gets(char *ptr, int size, CompressFileHandle *CFH)
     320             : {
     321           8 :     gzFile      gzfp = (gzFile) CFH->private_data;
     322             : 
     323           8 :     return gzgets(gzfp, ptr, size);
     324             : }
     325             : 
     326             : static bool
     327         588 : Gzip_close(CompressFileHandle *CFH)
     328             : {
     329         588 :     gzFile      gzfp = (gzFile) CFH->private_data;
     330             : 
     331         588 :     CFH->private_data = NULL;
     332             : 
     333         588 :     return gzclose(gzfp) == Z_OK;
     334             : }
     335             : 
     336             : static bool
     337           4 : Gzip_eof(CompressFileHandle *CFH)
     338             : {
     339           4 :     gzFile      gzfp = (gzFile) CFH->private_data;
     340             : 
     341           4 :     return gzeof(gzfp) == 1;
     342             : }
     343             : 
     344             : static const char *
     345           0 : Gzip_get_error(CompressFileHandle *CFH)
     346             : {
     347           0 :     gzFile      gzfp = (gzFile) CFH->private_data;
     348             :     const char *errmsg;
     349             :     int         errnum;
     350             : 
     351           0 :     errmsg = gzerror(gzfp, &errnum);
     352           0 :     if (errnum == Z_ERRNO)
     353           0 :         errmsg = strerror(errno);
     354             : 
     355           0 :     return errmsg;
     356             : }
     357             : 
     358             : static bool
     359         588 : Gzip_open(const char *path, int fd, const char *mode, CompressFileHandle *CFH)
     360             : {
     361             :     gzFile      gzfp;
     362             :     char        mode_compression[32];
     363             : 
     364         588 :     if (CFH->compression_spec.level != Z_DEFAULT_COMPRESSION)
     365             :     {
     366             :         /*
     367             :          * user has specified a compression level, so tell zlib to use it
     368             :          */
     369         378 :         snprintf(mode_compression, sizeof(mode_compression), "%s%d",
     370             :                  mode, CFH->compression_spec.level);
     371             :     }
     372             :     else
     373         210 :         strcpy(mode_compression, mode);
     374             : 
     375         588 :     if (fd >= 0)
     376           0 :         gzfp = gzdopen(dup(fd), mode_compression);
     377             :     else
     378         588 :         gzfp = gzopen(path, mode_compression);
     379             : 
     380         588 :     if (gzfp == NULL)
     381           0 :         return false;
     382             : 
     383         588 :     CFH->private_data = gzfp;
     384             : 
     385         588 :     return true;
     386             : }
     387             : 
     388             : static bool
     389         290 : Gzip_open_write(const char *path, const char *mode, CompressFileHandle *CFH)
     390             : {
     391             :     char       *fname;
     392             :     bool        ret;
     393             :     int         save_errno;
     394             : 
     395         290 :     fname = psprintf("%s.gz", path);
     396         290 :     ret = CFH->open_func(fname, -1, mode, CFH);
     397             : 
     398         290 :     save_errno = errno;
     399         290 :     pg_free(fname);
     400         290 :     errno = save_errno;
     401             : 
     402         290 :     return ret;
     403             : }
     404             : 
     405             : void
     406         588 : InitCompressFileHandleGzip(CompressFileHandle *CFH,
     407             :                            const pg_compress_specification compression_spec)
     408             : {
     409         588 :     CFH->open_func = Gzip_open;
     410         588 :     CFH->open_write_func = Gzip_open_write;
     411         588 :     CFH->read_func = Gzip_read;
     412         588 :     CFH->write_func = Gzip_write;
     413         588 :     CFH->gets_func = Gzip_gets;
     414         588 :     CFH->getc_func = Gzip_getc;
     415         588 :     CFH->close_func = Gzip_close;
     416         588 :     CFH->eof_func = Gzip_eof;
     417         588 :     CFH->get_error_func = Gzip_get_error;
     418             : 
     419         588 :     CFH->compression_spec = compression_spec;
     420             : 
     421         588 :     CFH->private_data = NULL;
     422         588 : }
     423             : #else                           /* HAVE_LIBZ */
     424             : void
     425             : InitCompressorGzip(CompressorState *cs,
     426             :                    const pg_compress_specification compression_spec)
     427             : {
     428             :     pg_fatal("this build does not support compression with %s", "gzip");
     429             : }
     430             : 
     431             : void
     432             : InitCompressFileHandleGzip(CompressFileHandle *CFH,
     433             :                            const pg_compress_specification compression_spec)
     434             : {
     435             :     pg_fatal("this build does not support compression with %s", "gzip");
     436             : }
     437             : #endif                          /* HAVE_LIBZ */

Generated by: LCOV version 1.16