Branch data 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 : 274 : DeflateCompressorInit(CompressorState *cs)
56 : : {
57 : : GzipCompressorState *gzipcs;
58 : : z_streamp zp;
59 : :
60 : 274 : gzipcs = pg_malloc0_object(GzipCompressorState);
61 : 274 : zp = gzipcs->zp = pg_malloc_object(z_stream);
62 : 274 : zp->zalloc = Z_NULL;
63 : 274 : zp->zfree = Z_NULL;
64 : 274 : 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 : 274 : gzipcs->outsize = DEFAULT_IO_BUFFER_SIZE;
72 : 274 : 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 [ - + ]: 274 : 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 : 274 : zp->next_out = gzipcs->outbuf;
82 : 274 : zp->avail_out = gzipcs->outsize;
83 : :
84 : : /* Keep track of gzipcs */
85 : 274 : cs->private_data = gzipcs;
86 : 274 : }
87 : :
88 : : static void
89 : 274 : DeflateCompressorEnd(ArchiveHandle *AH, CompressorState *cs)
90 : : {
91 : 274 : GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
92 : : z_streamp zp;
93 : :
94 : 274 : zp = gzipcs->zp;
95 : 274 : zp->next_in = NULL;
96 : 274 : zp->avail_in = 0;
97 : :
98 : : /* Flush any remaining data from zlib buffer */
99 : 274 : DeflateCompressorCommon(AH, cs, true);
100 : :
101 [ - + ]: 274 : if (deflateEnd(zp) != Z_OK)
102 : 0 : pg_fatal("could not close compression stream: %s", zp->msg);
103 : :
104 : 274 : pg_free(gzipcs->outbuf);
105 : 274 : pg_free(gzipcs->zp);
106 : 274 : pg_free(gzipcs);
107 : 274 : cs->private_data = NULL;
108 : 274 : }
109 : :
110 : : static void
111 : 1710 : DeflateCompressorCommon(ArchiveHandle *AH, CompressorState *cs, bool flush)
112 : : {
113 : 1710 : GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
114 : 1710 : z_streamp zp = gzipcs->zp;
115 : 1710 : void *out = gzipcs->outbuf;
116 : 1710 : int res = Z_OK;
117 : :
118 [ + + + + ]: 3146 : while (gzipcs->zp->avail_in != 0 || flush)
119 : : {
120 [ + + ]: 1710 : res = deflate(zp, flush ? Z_FINISH : Z_NO_FLUSH);
121 [ - + ]: 1710 : if (res == Z_STREAM_ERROR)
122 : 0 : pg_fatal("could not compress data: %s", zp->msg);
123 [ + + - + ]: 1710 : if ((flush && (zp->avail_out < gzipcs->outsize))
124 [ + - ]: 1436 : || (zp->avail_out == 0)
125 [ - + ]: 1436 : || (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 [ + - ]: 274 : 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 : 274 : size_t len = gzipcs->outsize - zp->avail_out;
140 : :
141 : 274 : cs->writeF(AH, out, len);
142 : : }
143 : 274 : zp->next_out = out;
144 : 274 : zp->avail_out = gzipcs->outsize;
145 : : }
146 : :
147 [ + + ]: 1710 : if (res == Z_STREAM_END)
148 : 274 : break;
149 : : }
150 : 1710 : }
151 : :
152 : : static void
153 : 447 : EndCompressorGzip(ArchiveHandle *AH, CompressorState *cs)
154 : : {
155 : : /* If deflation was initialized, finalize it */
156 [ + + ]: 447 : if (cs->private_data)
157 : 274 : DeflateCompressorEnd(AH, cs);
158 : 447 : }
159 : :
160 : : static void
161 : 1436 : WriteDataToArchiveGzip(ArchiveHandle *AH, CompressorState *cs,
162 : : const void *data, size_t dLen)
163 : : {
164 : 1436 : GzipCompressorState *gzipcs = (GzipCompressorState *) cs->private_data;
165 : :
166 : 1436 : gzipcs->zp->next_in = data;
167 : 1436 : gzipcs->zp->avail_in = dLen;
168 : 1436 : DeflateCompressorCommon(AH, cs, false);
169 : 1436 : }
170 : :
171 : : static void
172 : 173 : ReadDataFromArchiveGzip(ArchiveHandle *AH, CompressorState *cs)
173 : : {
174 : : z_streamp zp;
175 : : char *out;
176 : 173 : int res = Z_OK;
177 : : size_t cnt;
178 : : char *buf;
179 : : size_t buflen;
180 : :
181 : 173 : zp = pg_malloc_object(z_stream);
182 : 173 : zp->zalloc = Z_NULL;
183 : 173 : zp->zfree = Z_NULL;
184 : 173 : zp->opaque = Z_NULL;
185 : :
186 : 173 : buflen = DEFAULT_IO_BUFFER_SIZE;
187 : 173 : buf = pg_malloc(buflen);
188 : :
189 : 173 : out = pg_malloc(DEFAULT_IO_BUFFER_SIZE + 1);
190 : :
191 [ - + ]: 173 : 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 [ + + ]: 346 : while ((cnt = cs->readF(AH, &buf, &buflen)))
197 : : {
198 : 173 : zp->next_in = (void *) buf;
199 : 173 : zp->avail_in = cnt;
200 : :
201 [ + + ]: 348 : while (zp->avail_in > 0)
202 : : {
203 : 175 : zp->next_out = (void *) out;
204 : 175 : zp->avail_out = DEFAULT_IO_BUFFER_SIZE;
205 : :
206 : 175 : res = inflate(zp, 0);
207 [ + + - + ]: 175 : if (res != Z_OK && res != Z_STREAM_END)
208 [ # # ]: 0 : pg_fatal("could not uncompress data: %s",
209 : : zp->msg ? zp->msg : "unknown error");
210 : :
211 : 175 : out[DEFAULT_IO_BUFFER_SIZE - zp->avail_out] = '\0';
212 : 175 : ahwrite(out, 1, DEFAULT_IO_BUFFER_SIZE - zp->avail_out, AH);
213 : : }
214 : : }
215 : :
216 : 173 : zp->next_in = NULL;
217 : 173 : zp->avail_in = 0;
218 [ - + ]: 173 : while (res != Z_STREAM_END)
219 : : {
220 : 0 : zp->next_out = (void *) out;
221 : 0 : zp->avail_out = DEFAULT_IO_BUFFER_SIZE;
222 : 0 : res = inflate(zp, 0);
223 [ # # # # ]: 0 : if (res != Z_OK && res != Z_STREAM_END)
224 [ # # ]: 0 : pg_fatal("could not uncompress data: %s",
225 : : zp->msg ? zp->msg : "unknown error");
226 : :
227 : 0 : out[DEFAULT_IO_BUFFER_SIZE - zp->avail_out] = '\0';
228 : 0 : ahwrite(out, 1, DEFAULT_IO_BUFFER_SIZE - zp->avail_out, AH);
229 : : }
230 : :
231 [ - + ]: 173 : if (inflateEnd(zp) != Z_OK)
232 : 0 : pg_fatal("could not close compression library: %s", zp->msg);
233 : :
234 : 173 : pg_free(buf);
235 : 173 : pg_free(out);
236 : 173 : pg_free(zp);
237 : 173 : }
238 : :
239 : : /* Public routines that support gzip compressed data I/O */
240 : : void
241 : 447 : InitCompressorGzip(CompressorState *cs,
242 : : const pg_compress_specification compression_spec)
243 : : {
244 : 447 : cs->readData = ReadDataFromArchiveGzip;
245 : 447 : cs->writeData = WriteDataToArchiveGzip;
246 : 447 : cs->end = EndCompressorGzip;
247 : :
248 : 447 : cs->compression_spec = compression_spec;
249 : :
250 : : /*
251 : : * If the caller has defined a write function, prepare the necessary
252 : : * state. Note that if the data is empty, End may be called immediately
253 : : * after Init, without ever calling Write.
254 : : */
255 [ + + ]: 447 : if (cs->writeF)
256 : 274 : DeflateCompressorInit(cs);
257 : 447 : }
258 : :
259 : :
260 : : /*----------------------
261 : : * Compress File API
262 : : *----------------------
263 : : */
264 : :
265 : : static size_t
266 : 389 : Gzip_read(void *ptr, size_t size, CompressFileHandle *CFH)
267 : : {
268 : 389 : gzFile gzfp = (gzFile) CFH->private_data;
269 : : int gzret;
270 : :
271 : : /* Reading zero bytes must be a no-op */
272 [ + + ]: 389 : if (size == 0)
273 : 16 : return 0;
274 : :
275 : 373 : gzret = gzread(gzfp, ptr, size);
276 : :
277 : : /*
278 : : * gzread returns zero on EOF as well as some error conditions, and less
279 : : * than zero on other error conditions, so we need to inspect for EOF on
280 : : * zero.
281 : : */
282 [ + + ]: 373 : if (gzret <= 0)
283 : : {
284 : : int errnum;
285 : : const char *errmsg;
286 : :
287 [ + - + - ]: 115 : if (gzret == 0 && gzeof(gzfp))
288 : 115 : return 0;
289 : :
290 : 0 : errmsg = gzerror(gzfp, &errnum);
291 : :
292 [ # # ]: 0 : pg_fatal("could not read from input file: %s",
293 : : errnum == Z_ERRNO ? strerror(errno) : errmsg);
294 : : }
295 : :
296 : 258 : return (size_t) gzret;
297 : : }
298 : :
299 : : static void
300 : 24663 : Gzip_write(const void *ptr, size_t size, CompressFileHandle *CFH)
301 : : {
302 : 24663 : gzFile gzfp = (gzFile) CFH->private_data;
303 : : int errnum;
304 : : const char *errmsg;
305 : :
306 [ - + ]: 24663 : if (gzwrite(gzfp, ptr, size) != size)
307 : : {
308 : 0 : errmsg = gzerror(gzfp, &errnum);
309 [ # # ]: 0 : pg_fatal("could not write to file: %s",
310 : : errnum == Z_ERRNO ? strerror(errno) : errmsg);
311 : : }
312 : 24663 : }
313 : :
314 : : static int
315 : 1572 : Gzip_getc(CompressFileHandle *CFH)
316 : : {
317 : 1572 : gzFile gzfp = (gzFile) CFH->private_data;
318 : : int ret;
319 : :
320 : 1572 : errno = 0;
321 : 1572 : ret = gzgetc(gzfp);
322 [ - + ]: 1572 : if (ret == EOF)
323 : : {
324 [ # # ]: 0 : if (!gzeof(gzfp))
325 : 0 : pg_fatal("could not read from input file: %m");
326 : : else
327 : 0 : pg_fatal("could not read from input file: end of file");
328 : : }
329 : :
330 : 1572 : return ret;
331 : : }
332 : :
333 : : static char *
334 : 2 : Gzip_gets(char *ptr, int size, CompressFileHandle *CFH)
335 : : {
336 : 2 : gzFile gzfp = (gzFile) CFH->private_data;
337 : :
338 : 2 : return gzgets(gzfp, ptr, size);
339 : : }
340 : :
341 : : static bool
342 : 232 : Gzip_close(CompressFileHandle *CFH)
343 : : {
344 : 232 : gzFile gzfp = (gzFile) CFH->private_data;
345 : :
346 : 232 : CFH->private_data = NULL;
347 : :
348 : 232 : return gzclose(gzfp) == Z_OK;
349 : : }
350 : :
351 : : static bool
352 : 1 : Gzip_eof(CompressFileHandle *CFH)
353 : : {
354 : 1 : gzFile gzfp = (gzFile) CFH->private_data;
355 : :
356 : 1 : return gzeof(gzfp) == 1;
357 : : }
358 : :
359 : : static const char *
360 : 0 : Gzip_get_error(CompressFileHandle *CFH)
361 : : {
362 : 0 : gzFile gzfp = (gzFile) CFH->private_data;
363 : : const char *errmsg;
364 : : int errnum;
365 : :
366 : 0 : errmsg = gzerror(gzfp, &errnum);
367 [ # # ]: 0 : if (errnum == Z_ERRNO)
368 : 0 : errmsg = strerror(errno);
369 : :
370 : 0 : return errmsg;
371 : : }
372 : :
373 : : static bool
374 : 232 : Gzip_open(const char *path, int fd, const char *mode, CompressFileHandle *CFH)
375 : : {
376 : : gzFile gzfp;
377 : : char mode_compression[32];
378 : :
379 [ + + ]: 232 : if (CFH->compression_spec.level != Z_DEFAULT_COMPRESSION)
380 : : {
381 : : /*
382 : : * user has specified a compression level, so tell zlib to use it
383 : : */
384 : 121 : snprintf(mode_compression, sizeof(mode_compression), "%s%d",
385 : : mode, CFH->compression_spec.level);
386 : : }
387 : : else
388 : 111 : strcpy(mode_compression, mode);
389 : :
390 [ - + ]: 232 : if (fd >= 0)
391 : : {
392 : 0 : int dup_fd = dup(fd);
393 : :
394 [ # # ]: 0 : if (dup_fd < 0)
395 : 0 : return false;
396 : 0 : gzfp = gzdopen(dup_fd, mode_compression);
397 [ # # ]: 0 : if (gzfp == NULL)
398 : : {
399 : 0 : close(dup_fd);
400 : 0 : return false;
401 : : }
402 : : }
403 : : else
404 : : {
405 : 232 : gzfp = gzopen(path, mode_compression);
406 [ - + ]: 232 : if (gzfp == NULL)
407 : 0 : return false;
408 : : }
409 : :
410 : 232 : CFH->private_data = gzfp;
411 : :
412 : 232 : return true;
413 : : }
414 : :
415 : : static bool
416 : 114 : Gzip_open_write(const char *path, const char *mode, CompressFileHandle *CFH)
417 : : {
418 : : char *fname;
419 : : bool ret;
420 : : int save_errno;
421 : :
422 : 114 : fname = psprintf("%s.gz", path);
423 : 114 : ret = CFH->open_func(fname, -1, mode, CFH);
424 : :
425 : 114 : save_errno = errno;
426 : 114 : pfree(fname);
427 : 114 : errno = save_errno;
428 : :
429 : 114 : return ret;
430 : : }
431 : :
432 : : void
433 : 232 : InitCompressFileHandleGzip(CompressFileHandle *CFH,
434 : : const pg_compress_specification compression_spec)
435 : : {
436 : 232 : CFH->open_func = Gzip_open;
437 : 232 : CFH->open_write_func = Gzip_open_write;
438 : 232 : CFH->read_func = Gzip_read;
439 : 232 : CFH->write_func = Gzip_write;
440 : 232 : CFH->gets_func = Gzip_gets;
441 : 232 : CFH->getc_func = Gzip_getc;
442 : 232 : CFH->close_func = Gzip_close;
443 : 232 : CFH->eof_func = Gzip_eof;
444 : 232 : CFH->get_error_func = Gzip_get_error;
445 : :
446 : 232 : CFH->compression_spec = compression_spec;
447 : :
448 : 232 : CFH->private_data = NULL;
449 : 232 : }
450 : : #else /* HAVE_LIBZ */
451 : : void
452 : : InitCompressorGzip(CompressorState *cs,
453 : : const pg_compress_specification compression_spec)
454 : : {
455 : : pg_fatal("this build does not support compression with %s", "gzip");
456 : : }
457 : :
458 : : void
459 : : InitCompressFileHandleGzip(CompressFileHandle *CFH,
460 : : const pg_compress_specification compression_spec)
461 : : {
462 : : pg_fatal("this build does not support compression with %s", "gzip");
463 : : }
464 : : #endif /* HAVE_LIBZ */
|