PostgreSQL Source Code git master
compress_gzip.c
Go to the documentation of this file.
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 * 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 */
36typedef 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 */
45static void DeflateCompressorInit(CompressorState *cs);
46static void DeflateCompressorEnd(ArchiveHandle *AH, CompressorState *cs);
47static void DeflateCompressorCommon(ArchiveHandle *AH, CompressorState *cs,
48 bool flush);
49static void EndCompressorGzip(ArchiveHandle *AH, CompressorState *cs);
50static void WriteDataToArchiveGzip(ArchiveHandle *AH, CompressorState *cs,
51 const void *data, size_t dLen);
52static void ReadDataFromArchiveGzip(ArchiveHandle *AH, CompressorState *cs);
53
54static void
55DeflateCompressorInit(CompressorState *cs)
56{
57 GzipCompressorState *gzipcs;
58 z_streamp zp;
59
60 gzipcs = (GzipCompressorState *) pg_malloc0(sizeof(GzipCompressorState));
61 zp = gzipcs->zp = (z_streamp) pg_malloc(sizeof(z_stream));
62 zp->zalloc = Z_NULL;
63 zp->zfree = Z_NULL;
64 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 gzipcs->outsize = DEFAULT_IO_BUFFER_SIZE;
72 gzipcs->outbuf = pg_malloc(gzipcs->outsize + 1);
73
74 /* -Z 0 uses the "None" compressor -- not zlib with no compression */
76
77 if (deflateInit(zp, cs->compression_spec.level) != Z_OK)
78 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 zp->next_out = gzipcs->outbuf;
82 zp->avail_out = gzipcs->outsize;
83
84 /* Keep track of gzipcs */
85 cs->private_data = gzipcs;
86}
87
88static void
89DeflateCompressorEnd(ArchiveHandle *AH, CompressorState *cs)
90{
91 GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
92 z_streamp zp;
93
94 zp = gzipcs->zp;
95 zp->next_in = NULL;
96 zp->avail_in = 0;
97
98 /* Flush any remaining data from zlib buffer */
99 DeflateCompressorCommon(AH, cs, true);
100
101 if (deflateEnd(zp) != Z_OK)
102 pg_fatal("could not close compression stream: %s", zp->msg);
103
104 pg_free(gzipcs->outbuf);
105 pg_free(gzipcs->zp);
106 pg_free(gzipcs);
107 cs->private_data = NULL;
108}
109
110static void
111DeflateCompressorCommon(ArchiveHandle *AH, CompressorState *cs, bool flush)
112{
113 GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
114 z_streamp zp = gzipcs->zp;
115 void *out = gzipcs->outbuf;
116 int res = Z_OK;
117
118 while (gzipcs->zp->avail_in != 0 || flush)
119 {
120 res = deflate(zp, flush ? Z_FINISH : Z_NO_FLUSH);
121 if (res == Z_STREAM_ERROR)
122 pg_fatal("could not compress data: %s", zp->msg);
123 if ((flush && (zp->avail_out < gzipcs->outsize))
124 || (zp->avail_out == 0)
125 || (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 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 size_t len = gzipcs->outsize - zp->avail_out;
140
141 cs->writeF(AH, out, len);
142 }
143 zp->next_out = out;
144 zp->avail_out = gzipcs->outsize;
145 }
146
147 if (res == Z_STREAM_END)
148 break;
149 }
150}
151
152static void
153EndCompressorGzip(ArchiveHandle *AH, CompressorState *cs)
154{
155 /* If deflation was initialized, finalize it */
156 if (cs->private_data)
157 DeflateCompressorEnd(AH, cs);
158}
159
160static void
161WriteDataToArchiveGzip(ArchiveHandle *AH, CompressorState *cs,
162 const void *data, size_t dLen)
163{
164 GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
165
166 gzipcs->zp->next_in = data;
167 gzipcs->zp->avail_in = dLen;
168 DeflateCompressorCommon(AH, cs, false);
169}
170
171static void
172ReadDataFromArchiveGzip(ArchiveHandle *AH, CompressorState *cs)
173{
174 z_streamp zp;
175 char *out;
176 int res = Z_OK;
177 size_t cnt;
178 char *buf;
179 size_t buflen;
180
181 zp = (z_streamp) pg_malloc(sizeof(z_stream));
182 zp->zalloc = Z_NULL;
183 zp->zfree = Z_NULL;
184 zp->opaque = Z_NULL;
185
186 buflen = DEFAULT_IO_BUFFER_SIZE;
187 buf = pg_malloc(buflen);
188
190
191 if (inflateInit(zp) != Z_OK)
192 pg_fatal("could not initialize compression library: %s",
193 zp->msg);
194
195 /* no minimal chunk size for zlib */
196 while ((cnt = cs->readF(AH, &buf, &buflen)))
197 {
198 zp->next_in = (void *) buf;
199 zp->avail_in = cnt;
200
201 while (zp->avail_in > 0)
202 {
203 zp->next_out = (void *) out;
204 zp->avail_out = DEFAULT_IO_BUFFER_SIZE;
205
206 res = inflate(zp, 0);
207 if (res != Z_OK && res != Z_STREAM_END)
208 pg_fatal("could not uncompress data: %s", zp->msg);
209
210 out[DEFAULT_IO_BUFFER_SIZE - zp->avail_out] = '\0';
211 ahwrite(out, 1, DEFAULT_IO_BUFFER_SIZE - zp->avail_out, AH);
212 }
213 }
214
215 zp->next_in = NULL;
216 zp->avail_in = 0;
217 while (res != Z_STREAM_END)
218 {
219 zp->next_out = (void *) out;
220 zp->avail_out = DEFAULT_IO_BUFFER_SIZE;
221 res = inflate(zp, 0);
222 if (res != Z_OK && res != Z_STREAM_END)
223 pg_fatal("could not uncompress data: %s", zp->msg);
224
225 out[DEFAULT_IO_BUFFER_SIZE - zp->avail_out] = '\0';
226 ahwrite(out, 1, DEFAULT_IO_BUFFER_SIZE - zp->avail_out, AH);
227 }
228
229 if (inflateEnd(zp) != Z_OK)
230 pg_fatal("could not close compression library: %s", zp->msg);
231
232 free(buf);
233 free(out);
234 free(zp);
235}
236
237/* Public routines that support gzip compressed data I/O */
238void
240 const pg_compress_specification compression_spec)
241{
242 cs->readData = ReadDataFromArchiveGzip;
243 cs->writeData = WriteDataToArchiveGzip;
244 cs->end = EndCompressorGzip;
245
246 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 if (cs->writeF)
254 DeflateCompressorInit(cs);
255}
256
257
258/*----------------------
259 * Compress File API
260 *----------------------
261 */
262
263static size_t
264Gzip_read(void *ptr, size_t size, CompressFileHandle *CFH)
265{
266 gzFile gzfp = (gzFile) CFH->private_data;
267 int gzret;
268
269 /* Reading zero bytes must be a no-op */
270 if (size == 0)
271 return 0;
272
273 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 if (gzret <= 0)
281 {
282 int errnum;
283 const char *errmsg;
284
285 if (gzret == 0 && gzeof(gzfp))
286 return 0;
287
288 errmsg = gzerror(gzfp, &errnum);
289
290 pg_fatal("could not read from input file: %s",
291 errnum == Z_ERRNO ? strerror(errno) : errmsg);
292 }
293
294 return (size_t) gzret;
295}
296
297static void
298Gzip_write(const void *ptr, size_t size, CompressFileHandle *CFH)
299{
300 gzFile gzfp = (gzFile) CFH->private_data;
301 int errnum;
302 const char *errmsg;
303
304 if (gzwrite(gzfp, ptr, size) != size)
305 {
306 errmsg = gzerror(gzfp, &errnum);
307 pg_fatal("could not write to file: %s",
308 errnum == Z_ERRNO ? strerror(errno) : errmsg);
309 }
310}
311
312static int
313Gzip_getc(CompressFileHandle *CFH)
314{
315 gzFile gzfp = (gzFile) CFH->private_data;
316 int ret;
317
318 errno = 0;
319 ret = gzgetc(gzfp);
320 if (ret == EOF)
321 {
322 if (!gzeof(gzfp))
323 pg_fatal("could not read from input file: %m");
324 else
325 pg_fatal("could not read from input file: end of file");
326 }
327
328 return ret;
329}
330
331static char *
332Gzip_gets(char *ptr, int size, CompressFileHandle *CFH)
333{
334 gzFile gzfp = (gzFile) CFH->private_data;
335
336 return gzgets(gzfp, ptr, size);
337}
338
339static bool
340Gzip_close(CompressFileHandle *CFH)
341{
342 gzFile gzfp = (gzFile) CFH->private_data;
343
344 CFH->private_data = NULL;
345
346 return gzclose(gzfp) == Z_OK;
347}
348
349static bool
350Gzip_eof(CompressFileHandle *CFH)
351{
352 gzFile gzfp = (gzFile) CFH->private_data;
353
354 return gzeof(gzfp) == 1;
355}
356
357static const char *
358Gzip_get_error(CompressFileHandle *CFH)
359{
360 gzFile gzfp = (gzFile) CFH->private_data;
361 const char *errmsg;
362 int errnum;
363
364 errmsg = gzerror(gzfp, &errnum);
365 if (errnum == Z_ERRNO)
366 errmsg = strerror(errno);
367
368 return errmsg;
369}
370
371static bool
372Gzip_open(const char *path, int fd, const char *mode, CompressFileHandle *CFH)
373{
374 gzFile gzfp;
375 char mode_compression[32];
376
377 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 snprintf(mode_compression, sizeof(mode_compression), "%s%d",
384 }
385 else
386 strcpy(mode_compression, mode);
387
388 if (fd >= 0)
389 gzfp = gzdopen(dup(fd), mode_compression);
390 else
391 gzfp = gzopen(path, mode_compression);
392
393 if (gzfp == NULL)
394 return false;
395
396 CFH->private_data = gzfp;
397
398 return true;
399}
400
401static bool
402Gzip_open_write(const char *path, const char *mode, CompressFileHandle *CFH)
403{
404 char *fname;
405 bool ret;
406 int save_errno;
407
408 fname = psprintf("%s.gz", path);
409 ret = CFH->open_func(fname, -1, mode, CFH);
410
411 save_errno = errno;
412 pg_free(fname);
413 errno = save_errno;
414
415 return ret;
416}
417
418void
420 const pg_compress_specification compression_spec)
421{
422 CFH->open_func = Gzip_open;
423 CFH->open_write_func = Gzip_open_write;
424 CFH->read_func = Gzip_read;
425 CFH->write_func = Gzip_write;
426 CFH->gets_func = Gzip_gets;
427 CFH->getc_func = Gzip_getc;
428 CFH->close_func = Gzip_close;
429 CFH->eof_func = Gzip_eof;
430 CFH->get_error_func = Gzip_get_error;
431
432 CFH->compression_spec = compression_spec;
433
434 CFH->private_data = NULL;
435}
436#else /* HAVE_LIBZ */
437void
439 const pg_compress_specification compression_spec)
440{
441 pg_fatal("this build does not support compression with %s", "gzip");
442}
443
444void
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 */
void InitCompressFileHandleGzip(CompressFileHandle *CFH, const pg_compress_specification compression_spec)
void InitCompressorGzip(CompressorState *cs, const pg_compress_specification compression_spec)
#define DEFAULT_IO_BUFFER_SIZE
Definition: compress_io.h:27
int errmsg(const char *fmt,...)
Definition: elog.c:1080
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
void * pg_malloc0(size_t size)
Definition: fe_memutils.c:53
void pg_free(void *ptr)
Definition: fe_memutils.c:105
Assert(PointerIsAligned(start, uint64))
#define free(a)
Definition: header.h:65
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
void ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle *AH)
#define pg_fatal(...)
static PgChecksumMode mode
Definition: pg_checksums.c:56
const void size_t len
const void * data
while(p+4<=pend)
static char * buf
Definition: pg_test_fsync.c:72
#define strerror
Definition: port.h:252
#define snprintf
Definition: port.h:239
static int fd(const char *x, int i)
Definition: preproc-init.c:105
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
char *(* gets_func)(char *s, int size, CompressFileHandle *CFH)
Definition: compress_io.h:153
bool(* open_write_func)(const char *path, const char *mode, CompressFileHandle *CFH)
Definition: compress_io.h:122
int(* getc_func)(CompressFileHandle *CFH)
Definition: compress_io.h:162
const char *(* get_error_func)(CompressFileHandle *CFH)
Definition: compress_io.h:182
bool(* eof_func)(CompressFileHandle *CFH)
Definition: compress_io.h:169
size_t(* read_func)(void *ptr, size_t size, CompressFileHandle *CFH)
Definition: compress_io.h:132
bool(* open_func)(const char *path, int fd, const char *mode, CompressFileHandle *CFH)
Definition: compress_io.h:111
pg_compress_specification compression_spec
Definition: compress_io.h:187
bool(* close_func)(CompressFileHandle *CFH)
Definition: compress_io.h:176
void(* write_func)(const void *ptr, size_t size, CompressFileHandle *CFH)
Definition: compress_io.h:140
void * private_data
Definition: compress_io.h:87
void(* readData)(ArchiveHandle *AH, CompressorState *cs)
Definition: compress_io.h:56
pg_compress_specification compression_spec
Definition: compress_io.h:82
void(* end)(ArchiveHandle *AH, CompressorState *cs)
Definition: compress_io.h:67
ReadFunc readF
Definition: compress_io.h:72
void(* writeData)(ArchiveHandle *AH, CompressorState *cs, const void *data, size_t dLen)
Definition: compress_io.h:61
WriteFunc writeF
Definition: compress_io.h:77