Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * walmethods.c - implementations of different ways to write received wal
4 : *
5 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
6 : *
7 : * IDENTIFICATION
8 : * src/bin/pg_basebackup/walmethods.c
9 : *-------------------------------------------------------------------------
10 : */
11 :
12 : #include "postgres_fe.h"
13 :
14 : #include <sys/stat.h>
15 : #include <time.h>
16 : #include <unistd.h>
17 :
18 : #ifdef USE_LZ4
19 : #include <lz4frame.h>
20 : #endif
21 : #ifdef HAVE_LIBZ
22 : #include <zlib.h>
23 : #endif
24 :
25 : #include "common/file_perm.h"
26 : #include "common/file_utils.h"
27 : #include "common/logging.h"
28 : #include "pgtar.h"
29 : #include "receivelog.h"
30 : #include "streamutil.h"
31 :
32 : /* Size of zlib buffer for .tar.gz */
33 : #define ZLIB_OUT_SIZE 4096
34 :
35 : /* Size of LZ4 input chunk for .lz4 */
36 : #define LZ4_IN_SIZE 4096
37 :
38 : /*-------------------------------------------------------------------------
39 : * WalDirectoryMethod - write wal to a directory looking like pg_wal
40 : *-------------------------------------------------------------------------
41 : */
42 :
43 : static Walfile *dir_open_for_write(WalWriteMethod *wwmethod,
44 : const char *pathname,
45 : const char *temp_suffix,
46 : size_t pad_to_size);
47 : static int dir_close(Walfile *f, WalCloseMethod method);
48 : static bool dir_existsfile(WalWriteMethod *wwmethod, const char *pathname);
49 : static ssize_t dir_get_file_size(WalWriteMethod *wwmethod,
50 : const char *pathname);
51 : static char *dir_get_file_name(WalWriteMethod *wwmethod,
52 : const char *pathname, const char *temp_suffix);
53 : static ssize_t dir_write(Walfile *f, const void *buf, size_t count);
54 : static int dir_sync(Walfile *f);
55 : static bool dir_finish(WalWriteMethod *wwmethod);
56 : static void dir_free(WalWriteMethod *wwmethod);
57 :
58 : const WalWriteMethodOps WalDirectoryMethodOps = {
59 : .open_for_write = dir_open_for_write,
60 : .close = dir_close,
61 : .existsfile = dir_existsfile,
62 : .get_file_size = dir_get_file_size,
63 : .get_file_name = dir_get_file_name,
64 : .write = dir_write,
65 : .sync = dir_sync,
66 : .finish = dir_finish,
67 : .free = dir_free
68 : };
69 :
70 : /*
71 : * Global static data for this method
72 : */
73 : typedef struct DirectoryMethodData
74 : {
75 : WalWriteMethod base;
76 : char *basedir;
77 : } DirectoryMethodData;
78 :
79 : /*
80 : * Local file handle
81 : */
82 : typedef struct DirectoryMethodFile
83 : {
84 : Walfile base;
85 : int fd;
86 : char *fullpath;
87 : char *temp_suffix;
88 : #ifdef HAVE_LIBZ
89 : gzFile gzfp;
90 : #endif
91 : #ifdef USE_LZ4
92 : LZ4F_compressionContext_t ctx;
93 : size_t lz4bufsize;
94 : void *lz4buf;
95 : #endif
96 : } DirectoryMethodFile;
97 :
98 : #define clear_error(wwmethod) \
99 : ((wwmethod)->lasterrstring = NULL, (wwmethod)->lasterrno = 0)
100 :
101 : static char *
102 632 : dir_get_file_name(WalWriteMethod *wwmethod,
103 : const char *pathname, const char *temp_suffix)
104 : {
105 632 : char *filename = pg_malloc0(MAXPGPATH * sizeof(char));
106 :
107 1264 : snprintf(filename, MAXPGPATH, "%s%s%s",
108 : pathname,
109 632 : wwmethod->compression_algorithm == PG_COMPRESSION_GZIP ? ".gz" :
110 616 : wwmethod->compression_algorithm == PG_COMPRESSION_LZ4 ? ".lz4" : "",
111 : temp_suffix ? temp_suffix : "");
112 :
113 632 : return filename;
114 : }
115 :
116 : static Walfile *
117 212 : dir_open_for_write(WalWriteMethod *wwmethod, const char *pathname,
118 : const char *temp_suffix, size_t pad_to_size)
119 : {
120 212 : DirectoryMethodData *dir_data = (DirectoryMethodData *) wwmethod;
121 : char tmppath[MAXPGPATH];
122 : char *filename;
123 : int fd;
124 : DirectoryMethodFile *f;
125 : #ifdef HAVE_LIBZ
126 212 : gzFile gzfp = NULL;
127 : #endif
128 : #ifdef USE_LZ4
129 212 : LZ4F_compressionContext_t ctx = NULL;
130 212 : size_t lz4bufsize = 0;
131 212 : void *lz4buf = NULL;
132 : #endif
133 :
134 212 : clear_error(wwmethod);
135 :
136 212 : filename = dir_get_file_name(wwmethod, pathname, temp_suffix);
137 212 : snprintf(tmppath, sizeof(tmppath), "%s/%s",
138 : dir_data->basedir, filename);
139 212 : pg_free(filename);
140 :
141 : /*
142 : * Open a file for non-compressed as well as compressed files. Tracking
143 : * the file descriptor is important for dir_sync() method as gzflush()
144 : * does not do any system calls to fsync() to make changes permanent on
145 : * disk.
146 : */
147 212 : fd = open(tmppath, O_WRONLY | O_CREAT | PG_BINARY, pg_file_create_mode);
148 212 : if (fd < 0)
149 : {
150 0 : wwmethod->lasterrno = errno;
151 0 : return NULL;
152 : }
153 :
154 : #ifdef HAVE_LIBZ
155 212 : if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
156 : {
157 4 : gzfp = gzdopen(fd, "wb");
158 4 : if (gzfp == NULL)
159 : {
160 0 : wwmethod->lasterrno = errno;
161 0 : close(fd);
162 0 : return NULL;
163 : }
164 :
165 4 : if (gzsetparams(gzfp, wwmethod->compression_level,
166 : Z_DEFAULT_STRATEGY) != Z_OK)
167 : {
168 0 : wwmethod->lasterrno = errno;
169 0 : gzclose(gzfp);
170 0 : return NULL;
171 : }
172 : }
173 : #endif
174 : #ifdef USE_LZ4
175 212 : if (wwmethod->compression_algorithm == PG_COMPRESSION_LZ4)
176 : {
177 : size_t ctx_out;
178 : size_t header_size;
179 : LZ4F_preferences_t prefs;
180 :
181 4 : ctx_out = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
182 4 : if (LZ4F_isError(ctx_out))
183 : {
184 0 : wwmethod->lasterrstring = LZ4F_getErrorName(ctx_out);
185 0 : close(fd);
186 0 : return NULL;
187 : }
188 :
189 4 : lz4bufsize = LZ4F_compressBound(LZ4_IN_SIZE, NULL);
190 4 : lz4buf = pg_malloc0(lz4bufsize);
191 :
192 : /* assign the compression level, default is 0 */
193 4 : memset(&prefs, 0, sizeof(prefs));
194 4 : prefs.compressionLevel = wwmethod->compression_level;
195 :
196 : /* add the header */
197 4 : header_size = LZ4F_compressBegin(ctx, lz4buf, lz4bufsize, &prefs);
198 4 : if (LZ4F_isError(header_size))
199 : {
200 0 : wwmethod->lasterrstring = LZ4F_getErrorName(header_size);
201 0 : (void) LZ4F_freeCompressionContext(ctx);
202 0 : pg_free(lz4buf);
203 0 : close(fd);
204 0 : return NULL;
205 : }
206 :
207 4 : errno = 0;
208 4 : if (write(fd, lz4buf, header_size) != header_size)
209 : {
210 : /* If write didn't set errno, assume problem is no disk space */
211 0 : wwmethod->lasterrno = errno ? errno : ENOSPC;
212 0 : (void) LZ4F_freeCompressionContext(ctx);
213 0 : pg_free(lz4buf);
214 0 : close(fd);
215 0 : return NULL;
216 : }
217 : }
218 : #endif
219 :
220 : /* Do pre-padding on non-compressed files */
221 212 : if (pad_to_size && wwmethod->compression_algorithm == PG_COMPRESSION_NONE)
222 : {
223 : ssize_t rc;
224 :
225 186 : rc = pg_pwrite_zeros(fd, pad_to_size, 0);
226 :
227 186 : if (rc < 0)
228 : {
229 0 : wwmethod->lasterrno = errno;
230 0 : close(fd);
231 0 : return NULL;
232 : }
233 :
234 : /*
235 : * pg_pwrite() (called via pg_pwrite_zeros()) may have moved the file
236 : * position, so reset it (see win32pwrite.c).
237 : */
238 186 : if (lseek(fd, 0, SEEK_SET) != 0)
239 : {
240 0 : wwmethod->lasterrno = errno;
241 0 : close(fd);
242 0 : return NULL;
243 : }
244 : }
245 :
246 : /*
247 : * fsync WAL file and containing directory, to ensure the file is
248 : * persistently created and zeroed (if padded). That's particularly
249 : * important when using synchronous mode, where the file is modified and
250 : * fsynced in-place, without a directory fsync.
251 : */
252 212 : if (wwmethod->sync)
253 : {
254 28 : if (fsync_fname(tmppath, false) != 0 ||
255 14 : fsync_parent_path(tmppath) != 0)
256 : {
257 0 : wwmethod->lasterrno = errno;
258 : #ifdef HAVE_LIBZ
259 0 : if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
260 0 : gzclose(gzfp);
261 : else
262 : #endif
263 : #ifdef USE_LZ4
264 0 : if (wwmethod->compression_algorithm == PG_COMPRESSION_LZ4)
265 : {
266 0 : (void) LZ4F_compressEnd(ctx, lz4buf, lz4bufsize, NULL);
267 0 : (void) LZ4F_freeCompressionContext(ctx);
268 0 : pg_free(lz4buf);
269 0 : close(fd);
270 : }
271 : else
272 : #endif
273 0 : close(fd);
274 0 : return NULL;
275 : }
276 : }
277 :
278 212 : f = pg_malloc0(sizeof(DirectoryMethodFile));
279 : #ifdef HAVE_LIBZ
280 212 : if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
281 4 : f->gzfp = gzfp;
282 : #endif
283 : #ifdef USE_LZ4
284 212 : if (wwmethod->compression_algorithm == PG_COMPRESSION_LZ4)
285 : {
286 4 : f->ctx = ctx;
287 4 : f->lz4buf = lz4buf;
288 4 : f->lz4bufsize = lz4bufsize;
289 : }
290 : #endif
291 :
292 212 : f->base.wwmethod = wwmethod;
293 212 : f->base.currpos = 0;
294 212 : f->base.pathname = pg_strdup(pathname);
295 212 : f->fd = fd;
296 212 : f->fullpath = pg_strdup(tmppath);
297 212 : if (temp_suffix)
298 28 : f->temp_suffix = pg_strdup(temp_suffix);
299 :
300 212 : return &f->base;
301 : }
302 :
303 : static ssize_t
304 3556 : dir_write(Walfile *f, const void *buf, size_t count)
305 : {
306 : ssize_t r;
307 3556 : DirectoryMethodFile *df = (DirectoryMethodFile *) f;
308 :
309 : Assert(f != NULL);
310 3556 : clear_error(f->wwmethod);
311 :
312 : #ifdef HAVE_LIBZ
313 3556 : if (f->wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
314 : {
315 18 : errno = 0;
316 18 : r = (ssize_t) gzwrite(df->gzfp, buf, count);
317 18 : if (r != count)
318 : {
319 : /* If write didn't set errno, assume problem is no disk space */
320 0 : f->wwmethod->lasterrno = errno ? errno : ENOSPC;
321 : }
322 : }
323 : else
324 : #endif
325 : #ifdef USE_LZ4
326 3538 : if (f->wwmethod->compression_algorithm == PG_COMPRESSION_LZ4)
327 : {
328 : size_t chunk;
329 : size_t remaining;
330 18 : const void *inbuf = buf;
331 :
332 18 : remaining = count;
333 532 : while (remaining > 0)
334 : {
335 : size_t compressed;
336 :
337 514 : if (remaining > LZ4_IN_SIZE)
338 496 : chunk = LZ4_IN_SIZE;
339 : else
340 18 : chunk = remaining;
341 :
342 514 : remaining -= chunk;
343 514 : compressed = LZ4F_compressUpdate(df->ctx,
344 : df->lz4buf, df->lz4bufsize,
345 : inbuf, chunk,
346 : NULL);
347 :
348 514 : if (LZ4F_isError(compressed))
349 : {
350 0 : f->wwmethod->lasterrstring = LZ4F_getErrorName(compressed);
351 0 : return -1;
352 : }
353 :
354 514 : errno = 0;
355 514 : if (write(df->fd, df->lz4buf, compressed) != compressed)
356 : {
357 : /* If write didn't set errno, assume problem is no disk space */
358 0 : f->wwmethod->lasterrno = errno ? errno : ENOSPC;
359 0 : return -1;
360 : }
361 :
362 514 : inbuf = ((char *) inbuf) + chunk;
363 : }
364 :
365 : /* Our caller keeps track of the uncompressed size. */
366 18 : r = (ssize_t) count;
367 : }
368 : else
369 : #endif
370 : {
371 3520 : errno = 0;
372 3520 : r = write(df->fd, buf, count);
373 3520 : if (r != count)
374 : {
375 : /* If write didn't set errno, assume problem is no disk space */
376 0 : f->wwmethod->lasterrno = errno ? errno : ENOSPC;
377 : }
378 : }
379 3556 : if (r > 0)
380 3556 : df->base.currpos += r;
381 3556 : return r;
382 : }
383 :
384 : static int
385 212 : dir_close(Walfile *f, WalCloseMethod method)
386 : {
387 : int r;
388 212 : DirectoryMethodFile *df = (DirectoryMethodFile *) f;
389 212 : DirectoryMethodData *dir_data = (DirectoryMethodData *) f->wwmethod;
390 : char tmppath[MAXPGPATH];
391 : char tmppath2[MAXPGPATH];
392 :
393 : Assert(f != NULL);
394 212 : clear_error(f->wwmethod);
395 :
396 : #ifdef HAVE_LIBZ
397 212 : if (f->wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
398 : {
399 4 : errno = 0; /* in case gzclose() doesn't set it */
400 4 : r = gzclose(df->gzfp);
401 : }
402 : else
403 : #endif
404 : #ifdef USE_LZ4
405 208 : if (f->wwmethod->compression_algorithm == PG_COMPRESSION_LZ4)
406 : {
407 : size_t compressed;
408 :
409 4 : compressed = LZ4F_compressEnd(df->ctx,
410 : df->lz4buf, df->lz4bufsize,
411 : NULL);
412 :
413 4 : if (LZ4F_isError(compressed))
414 : {
415 0 : f->wwmethod->lasterrstring = LZ4F_getErrorName(compressed);
416 0 : return -1;
417 : }
418 :
419 4 : errno = 0;
420 4 : if (write(df->fd, df->lz4buf, compressed) != compressed)
421 : {
422 : /* If write didn't set errno, assume problem is no disk space */
423 0 : f->wwmethod->lasterrno = errno ? errno : ENOSPC;
424 0 : return -1;
425 : }
426 :
427 4 : r = close(df->fd);
428 : }
429 : else
430 : #endif
431 204 : r = close(df->fd);
432 :
433 212 : if (r == 0)
434 : {
435 : /* Build path to the current version of the file */
436 212 : if (method == CLOSE_NORMAL && df->temp_suffix)
437 16 : {
438 : char *filename;
439 : char *filename2;
440 :
441 : /*
442 : * If we have a temp prefix, normal operation is to rename the
443 : * file.
444 : */
445 16 : filename = dir_get_file_name(f->wwmethod, df->base.pathname,
446 16 : df->temp_suffix);
447 16 : snprintf(tmppath, sizeof(tmppath), "%s/%s",
448 : dir_data->basedir, filename);
449 16 : pg_free(filename);
450 :
451 : /* permanent name, so no need for the prefix */
452 16 : filename2 = dir_get_file_name(f->wwmethod, df->base.pathname, NULL);
453 16 : snprintf(tmppath2, sizeof(tmppath2), "%s/%s",
454 : dir_data->basedir, filename2);
455 16 : pg_free(filename2);
456 16 : if (f->wwmethod->sync)
457 6 : r = durable_rename(tmppath, tmppath2);
458 : else
459 : {
460 10 : if (rename(tmppath, tmppath2) != 0)
461 : {
462 0 : pg_log_error("could not rename file \"%s\" to \"%s\": %m",
463 : tmppath, tmppath2);
464 0 : r = -1;
465 : }
466 : }
467 : }
468 196 : else if (method == CLOSE_UNLINK)
469 : {
470 : char *filename;
471 :
472 : /* Unlink the file once it's closed */
473 0 : filename = dir_get_file_name(f->wwmethod, df->base.pathname,
474 0 : df->temp_suffix);
475 0 : snprintf(tmppath, sizeof(tmppath), "%s/%s",
476 : dir_data->basedir, filename);
477 0 : pg_free(filename);
478 0 : r = unlink(tmppath);
479 : }
480 : else
481 : {
482 : /*
483 : * Else either CLOSE_NORMAL and no temp suffix, or
484 : * CLOSE_NO_RENAME. In this case, fsync the file and containing
485 : * directory if sync mode is requested.
486 : */
487 196 : if (f->wwmethod->sync)
488 : {
489 8 : r = fsync_fname(df->fullpath, false);
490 8 : if (r == 0)
491 8 : r = fsync_parent_path(df->fullpath);
492 : }
493 : }
494 : }
495 :
496 212 : if (r != 0)
497 0 : f->wwmethod->lasterrno = errno;
498 :
499 : #ifdef USE_LZ4
500 212 : pg_free(df->lz4buf);
501 : /* supports free on NULL */
502 212 : LZ4F_freeCompressionContext(df->ctx);
503 : #endif
504 :
505 212 : pg_free(df->base.pathname);
506 212 : pg_free(df->fullpath);
507 212 : pg_free(df->temp_suffix);
508 212 : pg_free(df);
509 :
510 212 : return r;
511 : }
512 :
513 : static int
514 0 : dir_sync(Walfile *f)
515 : {
516 : int r;
517 :
518 : Assert(f != NULL);
519 0 : clear_error(f->wwmethod);
520 :
521 0 : if (!f->wwmethod->sync)
522 0 : return 0;
523 :
524 : #ifdef HAVE_LIBZ
525 0 : if (f->wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
526 : {
527 0 : if (gzflush(((DirectoryMethodFile *) f)->gzfp, Z_SYNC_FLUSH) != Z_OK)
528 : {
529 0 : f->wwmethod->lasterrno = errno;
530 0 : return -1;
531 : }
532 : }
533 : #endif
534 : #ifdef USE_LZ4
535 0 : if (f->wwmethod->compression_algorithm == PG_COMPRESSION_LZ4)
536 : {
537 0 : DirectoryMethodFile *df = (DirectoryMethodFile *) f;
538 : size_t compressed;
539 :
540 : /* Flush any internal buffers */
541 0 : compressed = LZ4F_flush(df->ctx, df->lz4buf, df->lz4bufsize, NULL);
542 0 : if (LZ4F_isError(compressed))
543 : {
544 0 : f->wwmethod->lasterrstring = LZ4F_getErrorName(compressed);
545 0 : return -1;
546 : }
547 :
548 0 : errno = 0;
549 0 : if (write(df->fd, df->lz4buf, compressed) != compressed)
550 : {
551 : /* If write didn't set errno, assume problem is no disk space */
552 0 : f->wwmethod->lasterrno = errno ? errno : ENOSPC;
553 0 : return -1;
554 : }
555 : }
556 : #endif
557 :
558 0 : r = fsync(((DirectoryMethodFile *) f)->fd);
559 0 : if (r < 0)
560 0 : f->wwmethod->lasterrno = errno;
561 0 : return r;
562 : }
563 :
564 : static ssize_t
565 0 : dir_get_file_size(WalWriteMethod *wwmethod, const char *pathname)
566 : {
567 0 : DirectoryMethodData *dir_data = (DirectoryMethodData *) wwmethod;
568 : struct stat statbuf;
569 : char tmppath[MAXPGPATH];
570 :
571 0 : snprintf(tmppath, sizeof(tmppath), "%s/%s",
572 : dir_data->basedir, pathname);
573 :
574 0 : if (stat(tmppath, &statbuf) != 0)
575 : {
576 0 : wwmethod->lasterrno = errno;
577 0 : return -1;
578 : }
579 :
580 0 : return statbuf.st_size;
581 : }
582 :
583 : static bool
584 190 : dir_existsfile(WalWriteMethod *wwmethod, const char *pathname)
585 : {
586 190 : DirectoryMethodData *dir_data = (DirectoryMethodData *) wwmethod;
587 : char tmppath[MAXPGPATH];
588 : int fd;
589 :
590 190 : clear_error(wwmethod);
591 :
592 190 : snprintf(tmppath, sizeof(tmppath), "%s/%s",
593 : dir_data->basedir, pathname);
594 :
595 190 : fd = open(tmppath, O_RDONLY | PG_BINARY, 0);
596 190 : if (fd < 0)
597 190 : return false;
598 0 : close(fd);
599 0 : return true;
600 : }
601 :
602 : static bool
603 180 : dir_finish(WalWriteMethod *wwmethod)
604 : {
605 180 : clear_error(wwmethod);
606 :
607 180 : if (wwmethod->sync)
608 : {
609 8 : DirectoryMethodData *dir_data = (DirectoryMethodData *) wwmethod;
610 :
611 : /*
612 : * Files are fsynced when they are closed, but we need to fsync the
613 : * directory entry here as well.
614 : */
615 8 : if (fsync_fname(dir_data->basedir, true) != 0)
616 : {
617 0 : wwmethod->lasterrno = errno;
618 0 : return false;
619 : }
620 : }
621 180 : return true;
622 : }
623 :
624 : static void
625 180 : dir_free(WalWriteMethod *wwmethod)
626 : {
627 180 : DirectoryMethodData *dir_data = (DirectoryMethodData *) wwmethod;
628 :
629 180 : pg_free(dir_data->basedir);
630 180 : pg_free(wwmethod);
631 180 : }
632 :
633 :
634 : WalWriteMethod *
635 186 : CreateWalDirectoryMethod(const char *basedir,
636 : pg_compress_algorithm compression_algorithm,
637 : int compression_level, bool sync)
638 : {
639 : DirectoryMethodData *wwmethod;
640 :
641 186 : wwmethod = pg_malloc0(sizeof(DirectoryMethodData));
642 186 : *((const WalWriteMethodOps **) &wwmethod->base.ops) =
643 : &WalDirectoryMethodOps;
644 186 : wwmethod->base.compression_algorithm = compression_algorithm;
645 186 : wwmethod->base.compression_level = compression_level;
646 186 : wwmethod->base.sync = sync;
647 186 : clear_error(&wwmethod->base);
648 186 : wwmethod->basedir = pg_strdup(basedir);
649 :
650 186 : return &wwmethod->base;
651 : }
652 :
653 :
654 : /*-------------------------------------------------------------------------
655 : * WalTarMethod - write wal to a tar file containing pg_wal contents
656 : *-------------------------------------------------------------------------
657 : */
658 :
659 : static Walfile *tar_open_for_write(WalWriteMethod *wwmethod,
660 : const char *pathname,
661 : const char *temp_suffix,
662 : size_t pad_to_size);
663 : static int tar_close(Walfile *f, WalCloseMethod method);
664 : static bool tar_existsfile(WalWriteMethod *wwmethod, const char *pathname);
665 : static ssize_t tar_get_file_size(WalWriteMethod *wwmethod,
666 : const char *pathname);
667 : static char *tar_get_file_name(WalWriteMethod *wwmethod,
668 : const char *pathname, const char *temp_suffix);
669 : static ssize_t tar_write(Walfile *f, const void *buf, size_t count);
670 : static int tar_sync(Walfile *f);
671 : static bool tar_finish(WalWriteMethod *wwmethod);
672 : static void tar_free(WalWriteMethod *wwmethod);
673 :
674 : const WalWriteMethodOps WalTarMethodOps = {
675 : .open_for_write = tar_open_for_write,
676 : .close = tar_close,
677 : .existsfile = tar_existsfile,
678 : .get_file_size = tar_get_file_size,
679 : .get_file_name = tar_get_file_name,
680 : .write = tar_write,
681 : .sync = tar_sync,
682 : .finish = tar_finish,
683 : .free = tar_free
684 : };
685 :
686 : typedef struct TarMethodFile
687 : {
688 : Walfile base;
689 : off_t ofs_start; /* Where does the *header* for this file start */
690 : char header[TAR_BLOCK_SIZE];
691 : size_t pad_to_size;
692 : } TarMethodFile;
693 :
694 : typedef struct TarMethodData
695 : {
696 : WalWriteMethod base;
697 : char *tarfilename;
698 : int fd;
699 : TarMethodFile *currentfile;
700 : #ifdef HAVE_LIBZ
701 : z_streamp zp;
702 : void *zlibOut;
703 : #endif
704 : } TarMethodData;
705 :
706 : #ifdef HAVE_LIBZ
707 : static bool
708 11814 : tar_write_compressed_data(TarMethodData *tar_data, void *buf, size_t count,
709 : bool flush)
710 : {
711 11814 : tar_data->zp->next_in = buf;
712 11814 : tar_data->zp->avail_in = count;
713 :
714 23656 : while (tar_data->zp->avail_in || flush)
715 : {
716 : int r;
717 :
718 11866 : r = deflate(tar_data->zp, flush ? Z_FINISH : Z_NO_FLUSH);
719 11866 : if (r == Z_STREAM_ERROR)
720 : {
721 0 : tar_data->base.lasterrstring = _("could not compress data");
722 0 : return false;
723 : }
724 :
725 11866 : if (tar_data->zp->avail_out < ZLIB_OUT_SIZE)
726 : {
727 124 : size_t len = ZLIB_OUT_SIZE - tar_data->zp->avail_out;
728 :
729 124 : errno = 0;
730 124 : if (write(tar_data->fd, tar_data->zlibOut, len) != len)
731 : {
732 : /* If write didn't set errno, assume problem is no disk space */
733 0 : tar_data->base.lasterrno = errno ? errno : ENOSPC;
734 0 : return false;
735 : }
736 :
737 124 : tar_data->zp->next_out = tar_data->zlibOut;
738 124 : tar_data->zp->avail_out = ZLIB_OUT_SIZE;
739 : }
740 :
741 11866 : if (r == Z_STREAM_END)
742 24 : break;
743 : }
744 :
745 11814 : if (flush)
746 : {
747 : /* Reset the stream for writing */
748 24 : if (deflateReset(tar_data->zp) != Z_OK)
749 : {
750 0 : tar_data->base.lasterrstring = _("could not reset compression stream");
751 0 : return false;
752 : }
753 : }
754 :
755 11814 : return true;
756 : }
757 : #endif
758 :
759 : static ssize_t
760 29014 : tar_write(Walfile *f, const void *buf, size_t count)
761 : {
762 29014 : TarMethodData *tar_data = (TarMethodData *) f->wwmethod;
763 : ssize_t r;
764 :
765 : Assert(f != NULL);
766 29014 : clear_error(f->wwmethod);
767 :
768 : /* Tarfile will always be positioned at the end */
769 29014 : if (f->wwmethod->compression_algorithm == PG_COMPRESSION_NONE)
770 : {
771 17230 : errno = 0;
772 17230 : r = write(tar_data->fd, buf, count);
773 17230 : if (r != count)
774 : {
775 : /* If write didn't set errno, assume problem is no disk space */
776 0 : f->wwmethod->lasterrno = errno ? errno : ENOSPC;
777 0 : return -1;
778 : }
779 17230 : f->currpos += r;
780 17230 : return r;
781 : }
782 : #ifdef HAVE_LIBZ
783 11784 : else if (f->wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
784 : {
785 11784 : if (!tar_write_compressed_data(tar_data, unconstify(void *, buf),
786 : count, false))
787 0 : return -1;
788 11784 : f->currpos += count;
789 11784 : return count;
790 : }
791 : #endif
792 : else
793 : {
794 : /* Can't happen - compression enabled with no method set */
795 0 : f->wwmethod->lasterrno = ENOSYS;
796 0 : return -1;
797 : }
798 : }
799 :
800 : static bool
801 14 : tar_write_padding_data(TarMethodFile *f, size_t bytes)
802 : {
803 : PGAlignedXLogBlock zerobuf;
804 14 : size_t bytesleft = bytes;
805 :
806 14 : memset(zerobuf.data, 0, XLOG_BLCKSZ);
807 28142 : while (bytesleft)
808 : {
809 28128 : size_t bytestowrite = Min(bytesleft, XLOG_BLCKSZ);
810 28128 : ssize_t r = tar_write(&f->base, zerobuf.data, bytestowrite);
811 :
812 28128 : if (r < 0)
813 0 : return false;
814 28128 : bytesleft -= r;
815 : }
816 :
817 14 : return true;
818 : }
819 :
820 : static char *
821 46 : tar_get_file_name(WalWriteMethod *wwmethod, const char *pathname,
822 : const char *temp_suffix)
823 : {
824 46 : char *filename = pg_malloc0(MAXPGPATH * sizeof(char));
825 :
826 46 : snprintf(filename, MAXPGPATH, "%s%s",
827 : pathname, temp_suffix ? temp_suffix : "");
828 :
829 46 : return filename;
830 : }
831 :
832 : static Walfile *
833 18 : tar_open_for_write(WalWriteMethod *wwmethod, const char *pathname,
834 : const char *temp_suffix, size_t pad_to_size)
835 : {
836 18 : TarMethodData *tar_data = (TarMethodData *) wwmethod;
837 : char *tmppath;
838 :
839 18 : clear_error(wwmethod);
840 :
841 18 : if (tar_data->fd < 0)
842 : {
843 : /*
844 : * We open the tar file only when we first try to write to it.
845 : */
846 14 : tar_data->fd = open(tar_data->tarfilename,
847 : O_WRONLY | O_CREAT | PG_BINARY,
848 : pg_file_create_mode);
849 14 : if (tar_data->fd < 0)
850 : {
851 0 : wwmethod->lasterrno = errno;
852 0 : return NULL;
853 : }
854 :
855 : #ifdef HAVE_LIBZ
856 14 : if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
857 : {
858 6 : tar_data->zp = (z_streamp) pg_malloc(sizeof(z_stream));
859 6 : tar_data->zp->zalloc = Z_NULL;
860 6 : tar_data->zp->zfree = Z_NULL;
861 6 : tar_data->zp->opaque = Z_NULL;
862 6 : tar_data->zp->next_out = tar_data->zlibOut;
863 6 : tar_data->zp->avail_out = ZLIB_OUT_SIZE;
864 :
865 : /*
866 : * Initialize deflation library. Adding the magic value 16 to the
867 : * default 15 for the windowBits parameter makes the output be
868 : * gzip instead of zlib.
869 : */
870 6 : if (deflateInit2(tar_data->zp, wwmethod->compression_level,
871 : Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK)
872 : {
873 0 : pg_free(tar_data->zp);
874 0 : tar_data->zp = NULL;
875 0 : wwmethod->lasterrstring =
876 0 : _("could not initialize compression library");
877 0 : return NULL;
878 : }
879 : }
880 : #endif
881 :
882 : /* There's no tar header itself, the file starts with regular files */
883 : }
884 :
885 18 : if (tar_data->currentfile != NULL)
886 : {
887 0 : wwmethod->lasterrstring =
888 0 : _("implementation error: tar files can't have more than one open file");
889 0 : return NULL;
890 : }
891 :
892 18 : tar_data->currentfile = pg_malloc0(sizeof(TarMethodFile));
893 18 : tar_data->currentfile->base.wwmethod = wwmethod;
894 :
895 18 : tmppath = tar_get_file_name(wwmethod, pathname, temp_suffix);
896 :
897 : /* Create a header with size set to 0 - we will fill out the size on close */
898 18 : if (tarCreateHeader(tar_data->currentfile->header, tmppath, NULL, 0, S_IRUSR | S_IWUSR, 0, 0, time(NULL)) != TAR_OK)
899 : {
900 0 : pg_free(tar_data->currentfile);
901 0 : pg_free(tmppath);
902 0 : tar_data->currentfile = NULL;
903 0 : wwmethod->lasterrstring = _("could not create tar header");
904 0 : return NULL;
905 : }
906 :
907 18 : pg_free(tmppath);
908 :
909 : #ifdef HAVE_LIBZ
910 18 : if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
911 : {
912 : /* Flush existing data */
913 6 : if (!tar_write_compressed_data(tar_data, NULL, 0, true))
914 0 : return NULL;
915 :
916 : /* Turn off compression for header */
917 6 : if (deflateParams(tar_data->zp, 0, Z_DEFAULT_STRATEGY) != Z_OK)
918 : {
919 0 : wwmethod->lasterrstring =
920 0 : _("could not change compression parameters");
921 0 : return NULL;
922 : }
923 : }
924 : #endif
925 :
926 18 : tar_data->currentfile->ofs_start = lseek(tar_data->fd, 0, SEEK_CUR);
927 18 : if (tar_data->currentfile->ofs_start == -1)
928 : {
929 0 : wwmethod->lasterrno = errno;
930 0 : pg_free(tar_data->currentfile);
931 0 : tar_data->currentfile = NULL;
932 0 : return NULL;
933 : }
934 18 : tar_data->currentfile->base.currpos = 0;
935 :
936 18 : if (wwmethod->compression_algorithm == PG_COMPRESSION_NONE)
937 : {
938 12 : errno = 0;
939 12 : if (write(tar_data->fd, tar_data->currentfile->header,
940 : TAR_BLOCK_SIZE) != TAR_BLOCK_SIZE)
941 : {
942 : /* If write didn't set errno, assume problem is no disk space */
943 0 : wwmethod->lasterrno = errno ? errno : ENOSPC;
944 0 : pg_free(tar_data->currentfile);
945 0 : tar_data->currentfile = NULL;
946 0 : return NULL;
947 : }
948 : }
949 : #ifdef HAVE_LIBZ
950 6 : else if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
951 : {
952 : /* Write header through the zlib APIs but with no compression */
953 6 : if (!tar_write_compressed_data(tar_data, tar_data->currentfile->header,
954 : TAR_BLOCK_SIZE, true))
955 0 : return NULL;
956 :
957 : /* Re-enable compression for the rest of the file */
958 6 : if (deflateParams(tar_data->zp, wwmethod->compression_level,
959 : Z_DEFAULT_STRATEGY) != Z_OK)
960 : {
961 0 : wwmethod->lasterrstring = _("could not change compression parameters");
962 0 : return NULL;
963 : }
964 : }
965 : #endif
966 : else
967 : {
968 : /* not reachable */
969 : Assert(false);
970 : }
971 :
972 18 : tar_data->currentfile->base.pathname = pg_strdup(pathname);
973 :
974 : /*
975 : * Uncompressed files are padded on creation, but for compression we can't
976 : * do that
977 : */
978 18 : if (pad_to_size)
979 : {
980 14 : tar_data->currentfile->pad_to_size = pad_to_size;
981 14 : if (wwmethod->compression_algorithm == PG_COMPRESSION_NONE)
982 : {
983 : /* Uncompressed, so pad now */
984 8 : if (!tar_write_padding_data(tar_data->currentfile, pad_to_size))
985 0 : return NULL;
986 : /* Seek back to start */
987 16 : if (lseek(tar_data->fd,
988 8 : tar_data->currentfile->ofs_start + TAR_BLOCK_SIZE,
989 8 : SEEK_SET) != tar_data->currentfile->ofs_start + TAR_BLOCK_SIZE)
990 : {
991 0 : wwmethod->lasterrno = errno;
992 0 : return NULL;
993 : }
994 :
995 8 : tar_data->currentfile->base.currpos = 0;
996 : }
997 : }
998 :
999 18 : return &tar_data->currentfile->base;
1000 : }
1001 :
1002 : static ssize_t
1003 0 : tar_get_file_size(WalWriteMethod *wwmethod, const char *pathname)
1004 : {
1005 0 : clear_error(wwmethod);
1006 :
1007 : /* Currently not used, so not supported */
1008 0 : wwmethod->lasterrno = ENOSYS;
1009 0 : return -1;
1010 : }
1011 :
1012 : static int
1013 18 : tar_sync(Walfile *f)
1014 : {
1015 18 : TarMethodData *tar_data = (TarMethodData *) f->wwmethod;
1016 : int r;
1017 :
1018 : Assert(f != NULL);
1019 18 : clear_error(f->wwmethod);
1020 :
1021 18 : if (!f->wwmethod->sync)
1022 18 : return 0;
1023 :
1024 : /*
1025 : * Always sync the whole tarfile, because that's all we can do. This makes
1026 : * no sense on compressed files, so just ignore those.
1027 : */
1028 0 : if (f->wwmethod->compression_algorithm != PG_COMPRESSION_NONE)
1029 0 : return 0;
1030 :
1031 0 : r = fsync(tar_data->fd);
1032 0 : if (r < 0)
1033 0 : f->wwmethod->lasterrno = errno;
1034 0 : return r;
1035 : }
1036 :
1037 : static int
1038 18 : tar_close(Walfile *f, WalCloseMethod method)
1039 : {
1040 : ssize_t filesize;
1041 : int padding;
1042 18 : TarMethodData *tar_data = (TarMethodData *) f->wwmethod;
1043 18 : TarMethodFile *tf = (TarMethodFile *) f;
1044 :
1045 : Assert(f != NULL);
1046 18 : clear_error(f->wwmethod);
1047 :
1048 18 : if (method == CLOSE_UNLINK)
1049 : {
1050 0 : if (f->wwmethod->compression_algorithm != PG_COMPRESSION_NONE)
1051 : {
1052 0 : f->wwmethod->lasterrstring = _("unlink not supported with compression");
1053 0 : return -1;
1054 : }
1055 :
1056 : /*
1057 : * Unlink the file that we just wrote to the tar. We do this by
1058 : * truncating it to the start of the header. This is safe as we only
1059 : * allow writing of the very last file.
1060 : */
1061 0 : if (ftruncate(tar_data->fd, tf->ofs_start) != 0)
1062 : {
1063 0 : f->wwmethod->lasterrno = errno;
1064 0 : return -1;
1065 : }
1066 :
1067 0 : pg_free(tf->base.pathname);
1068 0 : pg_free(tf);
1069 0 : tar_data->currentfile = NULL;
1070 :
1071 0 : return 0;
1072 : }
1073 :
1074 : /*
1075 : * Pad the file itself with zeroes if necessary. Note that this is
1076 : * different from the tar format padding -- this is the padding we asked
1077 : * for when the file was opened.
1078 : */
1079 18 : if (tf->pad_to_size)
1080 : {
1081 14 : if (f->wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
1082 : {
1083 : /*
1084 : * A compressed tarfile is padded on close since we cannot know
1085 : * the size of the compressed output until the end.
1086 : */
1087 6 : size_t sizeleft = tf->pad_to_size - tf->base.currpos;
1088 :
1089 6 : if (sizeleft)
1090 : {
1091 6 : if (!tar_write_padding_data(tf, sizeleft))
1092 0 : return -1;
1093 : }
1094 : }
1095 : else
1096 : {
1097 : /*
1098 : * An uncompressed tarfile was padded on creation, so just adjust
1099 : * the current position as if we seeked to the end.
1100 : */
1101 8 : tf->base.currpos = tf->pad_to_size;
1102 : }
1103 : }
1104 :
1105 : /*
1106 : * Get the size of the file, and pad out to a multiple of the tar block
1107 : * size.
1108 : */
1109 18 : filesize = f->currpos;
1110 18 : padding = tarPaddingBytesRequired(filesize);
1111 18 : if (padding)
1112 : {
1113 0 : char zerobuf[TAR_BLOCK_SIZE] = {0};
1114 :
1115 0 : if (tar_write(f, zerobuf, padding) != padding)
1116 0 : return -1;
1117 : }
1118 :
1119 :
1120 : #ifdef HAVE_LIBZ
1121 18 : if (f->wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
1122 : {
1123 : /* Flush the current buffer */
1124 6 : if (!tar_write_compressed_data(tar_data, NULL, 0, true))
1125 0 : return -1;
1126 : }
1127 : #endif
1128 :
1129 : /*
1130 : * Now go back and update the header with the correct filesize and
1131 : * possibly also renaming the file. We overwrite the entire current header
1132 : * when done, including the checksum.
1133 : */
1134 18 : print_tar_number(&(tf->header[124]), 12, filesize);
1135 :
1136 18 : if (method == CLOSE_NORMAL)
1137 :
1138 : /*
1139 : * We overwrite it with what it was before if we have no tempname,
1140 : * since we're going to write the buffer anyway.
1141 : */
1142 18 : strlcpy(&(tf->header[0]), tf->base.pathname, 100);
1143 :
1144 18 : print_tar_number(&(tf->header[148]), 8, tarChecksum(((TarMethodFile *) f)->header));
1145 18 : if (lseek(tar_data->fd, tf->ofs_start, SEEK_SET) != ((TarMethodFile *) f)->ofs_start)
1146 : {
1147 0 : f->wwmethod->lasterrno = errno;
1148 0 : return -1;
1149 : }
1150 18 : if (f->wwmethod->compression_algorithm == PG_COMPRESSION_NONE)
1151 : {
1152 12 : errno = 0;
1153 12 : if (write(tar_data->fd, tf->header, TAR_BLOCK_SIZE) != TAR_BLOCK_SIZE)
1154 : {
1155 : /* If write didn't set errno, assume problem is no disk space */
1156 0 : f->wwmethod->lasterrno = errno ? errno : ENOSPC;
1157 0 : return -1;
1158 : }
1159 : }
1160 : #ifdef HAVE_LIBZ
1161 6 : else if (f->wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
1162 : {
1163 : /* Turn off compression */
1164 6 : if (deflateParams(tar_data->zp, 0, Z_DEFAULT_STRATEGY) != Z_OK)
1165 : {
1166 0 : f->wwmethod->lasterrstring = _("could not change compression parameters");
1167 0 : return -1;
1168 : }
1169 :
1170 : /* Overwrite the header, assuming the size will be the same */
1171 6 : if (!tar_write_compressed_data(tar_data, tar_data->currentfile->header,
1172 : TAR_BLOCK_SIZE, true))
1173 0 : return -1;
1174 :
1175 : /* Turn compression back on */
1176 6 : if (deflateParams(tar_data->zp, f->wwmethod->compression_level,
1177 : Z_DEFAULT_STRATEGY) != Z_OK)
1178 : {
1179 0 : f->wwmethod->lasterrstring = _("could not change compression parameters");
1180 0 : return -1;
1181 : }
1182 : }
1183 : #endif
1184 : else
1185 : {
1186 : /* not reachable */
1187 : Assert(false);
1188 : }
1189 :
1190 : /* Move file pointer back down to end, so we can write the next file */
1191 18 : if (lseek(tar_data->fd, 0, SEEK_END) < 0)
1192 : {
1193 0 : f->wwmethod->lasterrno = errno;
1194 0 : return -1;
1195 : }
1196 :
1197 : /* Always fsync on close, so the padding gets fsynced */
1198 18 : if (tar_sync(f) < 0)
1199 : {
1200 : /* XXX this seems pretty bogus; why is only this case fatal? */
1201 0 : pg_fatal("could not fsync file \"%s\": %s",
1202 : tf->base.pathname, GetLastWalMethodError(f->wwmethod));
1203 : }
1204 :
1205 : /* Clean up and done */
1206 18 : pg_free(tf->base.pathname);
1207 18 : pg_free(tf);
1208 18 : tar_data->currentfile = NULL;
1209 :
1210 18 : return 0;
1211 : }
1212 :
1213 : static bool
1214 8 : tar_existsfile(WalWriteMethod *wwmethod, const char *pathname)
1215 : {
1216 8 : clear_error(wwmethod);
1217 : /* We only deal with new tarfiles, so nothing externally created exists */
1218 8 : return false;
1219 : }
1220 :
1221 : static bool
1222 14 : tar_finish(WalWriteMethod *wwmethod)
1223 : {
1224 14 : TarMethodData *tar_data = (TarMethodData *) wwmethod;
1225 14 : char zerobuf[1024] = {0};
1226 :
1227 14 : clear_error(wwmethod);
1228 :
1229 14 : if (tar_data->currentfile)
1230 : {
1231 0 : if (tar_close(&tar_data->currentfile->base, CLOSE_NORMAL) != 0)
1232 0 : return false;
1233 : }
1234 :
1235 : /* A tarfile always ends with two empty blocks */
1236 14 : if (wwmethod->compression_algorithm == PG_COMPRESSION_NONE)
1237 : {
1238 8 : errno = 0;
1239 8 : if (write(tar_data->fd, zerobuf, sizeof(zerobuf)) != sizeof(zerobuf))
1240 : {
1241 : /* If write didn't set errno, assume problem is no disk space */
1242 0 : wwmethod->lasterrno = errno ? errno : ENOSPC;
1243 0 : return false;
1244 : }
1245 : }
1246 : #ifdef HAVE_LIBZ
1247 6 : else if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
1248 : {
1249 6 : if (!tar_write_compressed_data(tar_data, zerobuf, sizeof(zerobuf),
1250 : false))
1251 0 : return false;
1252 :
1253 : /* Also flush all data to make sure the gzip stream is finished */
1254 6 : tar_data->zp->next_in = NULL;
1255 6 : tar_data->zp->avail_in = 0;
1256 : while (true)
1257 0 : {
1258 : int r;
1259 :
1260 6 : r = deflate(tar_data->zp, Z_FINISH);
1261 :
1262 6 : if (r == Z_STREAM_ERROR)
1263 : {
1264 0 : wwmethod->lasterrstring = _("could not compress data");
1265 0 : return false;
1266 : }
1267 6 : if (tar_data->zp->avail_out < ZLIB_OUT_SIZE)
1268 : {
1269 6 : size_t len = ZLIB_OUT_SIZE - tar_data->zp->avail_out;
1270 :
1271 6 : errno = 0;
1272 6 : if (write(tar_data->fd, tar_data->zlibOut, len) != len)
1273 : {
1274 : /*
1275 : * If write didn't set errno, assume problem is no disk
1276 : * space.
1277 : */
1278 0 : wwmethod->lasterrno = errno ? errno : ENOSPC;
1279 0 : return false;
1280 : }
1281 : }
1282 6 : if (r == Z_STREAM_END)
1283 6 : break;
1284 : }
1285 :
1286 6 : if (deflateEnd(tar_data->zp) != Z_OK)
1287 : {
1288 0 : wwmethod->lasterrstring = _("could not close compression stream");
1289 0 : return false;
1290 : }
1291 : }
1292 : #endif
1293 : else
1294 : {
1295 : /* not reachable */
1296 : Assert(false);
1297 : }
1298 :
1299 : /* sync the empty blocks as well, since they're after the last file */
1300 14 : if (wwmethod->sync)
1301 : {
1302 0 : if (fsync(tar_data->fd) != 0)
1303 : {
1304 0 : wwmethod->lasterrno = errno;
1305 0 : return false;
1306 : }
1307 : }
1308 :
1309 14 : if (close(tar_data->fd) != 0)
1310 : {
1311 0 : wwmethod->lasterrno = errno;
1312 0 : return false;
1313 : }
1314 :
1315 14 : tar_data->fd = -1;
1316 :
1317 14 : if (wwmethod->sync)
1318 : {
1319 0 : if (fsync_fname(tar_data->tarfilename, false) != 0 ||
1320 0 : fsync_parent_path(tar_data->tarfilename) != 0)
1321 : {
1322 0 : wwmethod->lasterrno = errno;
1323 0 : return false;
1324 : }
1325 : }
1326 :
1327 14 : return true;
1328 : }
1329 :
1330 : static void
1331 14 : tar_free(WalWriteMethod *wwmethod)
1332 : {
1333 14 : TarMethodData *tar_data = (TarMethodData *) wwmethod;
1334 :
1335 14 : pg_free(tar_data->tarfilename);
1336 : #ifdef HAVE_LIBZ
1337 14 : if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
1338 6 : pg_free(tar_data->zlibOut);
1339 : #endif
1340 14 : pg_free(wwmethod);
1341 14 : }
1342 :
1343 : /*
1344 : * The argument compression_algorithm is currently ignored. It is in place for
1345 : * symmetry with CreateWalDirectoryMethod which uses it for distinguishing
1346 : * between the different compression methods. CreateWalTarMethod and its family
1347 : * of functions handle only zlib compression.
1348 : */
1349 : WalWriteMethod *
1350 14 : CreateWalTarMethod(const char *tarbase,
1351 : pg_compress_algorithm compression_algorithm,
1352 : int compression_level, bool sync)
1353 : {
1354 : TarMethodData *wwmethod;
1355 14 : const char *suffix = (compression_algorithm == PG_COMPRESSION_GZIP) ?
1356 14 : ".tar.gz" : ".tar";
1357 :
1358 14 : wwmethod = pg_malloc0(sizeof(TarMethodData));
1359 14 : *((const WalWriteMethodOps **) &wwmethod->base.ops) =
1360 : &WalTarMethodOps;
1361 14 : wwmethod->base.compression_algorithm = compression_algorithm;
1362 14 : wwmethod->base.compression_level = compression_level;
1363 14 : wwmethod->base.sync = sync;
1364 14 : clear_error(&wwmethod->base);
1365 :
1366 14 : wwmethod->tarfilename = pg_malloc0(strlen(tarbase) + strlen(suffix) + 1);
1367 14 : sprintf(wwmethod->tarfilename, "%s%s", tarbase, suffix);
1368 14 : wwmethod->fd = -1;
1369 : #ifdef HAVE_LIBZ
1370 14 : if (compression_algorithm == PG_COMPRESSION_GZIP)
1371 6 : wwmethod->zlibOut = (char *) pg_malloc(ZLIB_OUT_SIZE + 1);
1372 : #endif
1373 :
1374 14 : return &wwmethod->base;
1375 : }
1376 :
1377 : const char *
1378 0 : GetLastWalMethodError(WalWriteMethod *wwmethod)
1379 : {
1380 0 : if (wwmethod->lasterrstring)
1381 0 : return wwmethod->lasterrstring;
1382 0 : return strerror(wwmethod->lasterrno);
1383 : }
|