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

Generated by: LCOV version 1.14