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