PostgreSQL Source Code git master
Loading...
Searching...
No Matches
walmethods.c File Reference
#include "postgres_fe.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "common/file_perm.h"
#include "common/file_utils.h"
#include "common/logging.h"
#include "pgtar.h"
#include "walmethods.h"
Include dependency graph for walmethods.c:

Go to the source code of this file.

Data Structures

struct  DirectoryMethodData
 
struct  DirectoryMethodFile
 
struct  TarMethodFile
 
struct  TarMethodData
 

Macros

#define ZLIB_OUT_SIZE   4096
 
#define LZ4_IN_SIZE   4096
 
#define clear_error(wwmethod)    ((wwmethod)->lasterrstring = NULL, (wwmethod)->lasterrno = 0)
 

Typedefs

typedef struct DirectoryMethodData DirectoryMethodData
 
typedef struct DirectoryMethodFile DirectoryMethodFile
 
typedef struct TarMethodFile TarMethodFile
 
typedef struct TarMethodData TarMethodData
 

Functions

static Walfiledir_open_for_write (WalWriteMethod *wwmethod, const char *pathname, const char *temp_suffix, size_t pad_to_size)
 
static int dir_close (Walfile *f, WalCloseMethod method)
 
static bool dir_existsfile (WalWriteMethod *wwmethod, const char *pathname)
 
static ssize_t dir_get_file_size (WalWriteMethod *wwmethod, const char *pathname)
 
static chardir_get_file_name (WalWriteMethod *wwmethod, const char *pathname, const char *temp_suffix)
 
static ssize_t dir_write (Walfile *f, const void *buf, size_t count)
 
static int dir_sync (Walfile *f)
 
static bool dir_finish (WalWriteMethod *wwmethod)
 
static void dir_free (WalWriteMethod *wwmethod)
 
WalWriteMethodCreateWalDirectoryMethod (const char *basedir, pg_compress_algorithm compression_algorithm, int compression_level, bool sync)
 
static Walfiletar_open_for_write (WalWriteMethod *wwmethod, const char *pathname, const char *temp_suffix, size_t pad_to_size)
 
static int tar_close (Walfile *f, WalCloseMethod method)
 
static bool tar_existsfile (WalWriteMethod *wwmethod, const char *pathname)
 
static ssize_t tar_get_file_size (WalWriteMethod *wwmethod, const char *pathname)
 
static chartar_get_file_name (WalWriteMethod *wwmethod, const char *pathname, const char *temp_suffix)
 
static ssize_t tar_write (Walfile *f, const void *buf, size_t count)
 
static int tar_sync (Walfile *f)
 
static bool tar_finish (WalWriteMethod *wwmethod)
 
static void tar_free (WalWriteMethod *wwmethod)
 
static bool tar_write_padding_data (TarMethodFile *f, size_t bytes)
 
WalWriteMethodCreateWalTarMethod (const char *tarbase, pg_compress_algorithm compression_algorithm, int compression_level, bool sync)
 
const charGetLastWalMethodError (WalWriteMethod *wwmethod)
 

Variables

static const WalWriteMethodOps WalDirectoryMethodOps
 
static const WalWriteMethodOps WalTarMethodOps
 

Macro Definition Documentation

◆ clear_error

#define clear_error (   wwmethod)     ((wwmethod)->lasterrstring = NULL, (wwmethod)->lasterrno = 0)

Definition at line 98 of file walmethods.c.

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

◆ LZ4_IN_SIZE

#define LZ4_IN_SIZE   4096

Definition at line 36 of file walmethods.c.

◆ ZLIB_OUT_SIZE

#define ZLIB_OUT_SIZE   4096

Definition at line 33 of file walmethods.c.

Typedef Documentation

◆ DirectoryMethodData

◆ DirectoryMethodFile

◆ TarMethodData

◆ TarMethodFile

Function Documentation

◆ CreateWalDirectoryMethod()

WalWriteMethod * CreateWalDirectoryMethod ( const char basedir,
pg_compress_algorithm  compression_algorithm,
int  compression_level,
bool  sync 
)

Definition at line 640 of file walmethods.c.

643{
644 DirectoryMethodData *wwmethod;
645
646 wwmethod = pg_malloc0(sizeof(DirectoryMethodData));
647 *((const WalWriteMethodOps **) &wwmethod->base.ops) =
650 wwmethod->base.compression_level = compression_level;
651 wwmethod->base.sync = sync;
652 clear_error(&wwmethod->base);
653 wwmethod->basedir = pg_strdup(basedir);
654
655 return &wwmethod->base;
656}

References DirectoryMethodData::base, basedir, DirectoryMethodData::basedir, clear_error, compression_algorithm, WalWriteMethod::compression_algorithm, WalWriteMethod::compression_level, WalWriteMethod::ops, pg_malloc0(), pg_strdup(), WalWriteMethod::sync, and WalDirectoryMethodOps.

Referenced by LogStreamerMain(), and StreamLog().

◆ CreateWalTarMethod()

WalWriteMethod * CreateWalTarMethod ( const char tarbase,
pg_compress_algorithm  compression_algorithm,
int  compression_level,
bool  sync 
)

Definition at line 1355 of file walmethods.c.

1358{
1359 TarMethodData *wwmethod;
1360 const char *suffix = (compression_algorithm == PG_COMPRESSION_GZIP) ?
1361 ".tar.gz" : ".tar";
1362
1363 wwmethod = pg_malloc0(sizeof(TarMethodData));
1364 *((const WalWriteMethodOps **) &wwmethod->base.ops) =
1367 wwmethod->base.compression_level = compression_level;
1368 wwmethod->base.sync = sync;
1369 clear_error(&wwmethod->base);
1370
1371 wwmethod->tarfilename = pg_malloc0(strlen(tarbase) + strlen(suffix) + 1);
1372 sprintf(wwmethod->tarfilename, "%s%s", tarbase, suffix);
1373 wwmethod->fd = -1;
1374#ifdef HAVE_LIBZ
1376 wwmethod->zlibOut = (char *) pg_malloc(ZLIB_OUT_SIZE + 1);
1377#endif
1378
1379 return &wwmethod->base;
1380}

References TarMethodData::base, clear_error, compression_algorithm, WalWriteMethod::compression_algorithm, WalWriteMethod::compression_level, fb(), TarMethodData::fd, WalWriteMethod::ops, PG_COMPRESSION_GZIP, pg_malloc(), pg_malloc0(), sprintf, WalWriteMethod::sync, TarMethodData::tarfilename, WalTarMethodOps, and ZLIB_OUT_SIZE.

Referenced by LogStreamerMain().

◆ dir_close()

static int dir_close ( Walfile f,
WalCloseMethod  method 
)
static

Definition at line 385 of file walmethods.c.

386{
387 int r;
390 char tmppath[MAXPGPATH];
391 char tmppath2[MAXPGPATH];
392
393 Assert(f != NULL);
395
396#ifdef HAVE_LIBZ
398 {
399 errno = 0; /* in case gzclose() doesn't set it */
400 r = gzclose(df->gzfp);
401 }
402 else
403#endif
404#ifdef USE_LZ4
406 {
407 size_t compressed;
408
410 df->lz4buf, df->lz4bufsize,
411 NULL);
412
414 {
416 return -1;
417 }
418
419 errno = 0;
420 if (write(df->fd, df->lz4buf, compressed) != compressed)
421 {
422 /* If write didn't set errno, assume problem is no disk space */
424 return -1;
425 }
426
427 r = close(df->fd);
428 }
429 else
430#endif
431 r = close(df->fd);
432
433 if (r == 0)
434 {
435 /* Build path to the current version of the file */
436 if (method == CLOSE_NORMAL && df->temp_suffix)
437 {
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 filename = dir_get_file_name(f->wwmethod, df->base.pathname,
446 df->temp_suffix);
447 snprintf(tmppath, sizeof(tmppath), "%s/%s",
448 dir_data->basedir, filename);
450
451 /* permanent name, so no need for the prefix */
452 filename2 = dir_get_file_name(f->wwmethod, df->base.pathname, NULL);
453 snprintf(tmppath2, sizeof(tmppath2), "%s/%s",
454 dir_data->basedir, filename2);
456 if (f->wwmethod->sync)
458 else
459 {
460 if (rename(tmppath, tmppath2) != 0)
461 {
462 pg_log_error("could not rename file \"%s\" to \"%s\": %m",
464 r = -1;
465 }
466 }
467 }
468 else if (method == CLOSE_UNLINK)
469 {
470 char *filename;
471
472 /* Unlink the file once it's closed */
473 filename = dir_get_file_name(f->wwmethod, df->base.pathname,
474 df->temp_suffix);
475 snprintf(tmppath, sizeof(tmppath), "%s/%s",
476 dir_data->basedir, filename);
478 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 if (f->wwmethod->sync)
488 {
489 r = fsync_fname(df->fullpath, false);
490 if (r == 0)
491 r = fsync_parent_path(df->fullpath);
492 }
493 }
494 }
495
496 if (r != 0)
498
499#ifdef USE_LZ4
500 pg_free(df->lz4buf);
501 /* supports free on NULL */
503#endif
504
505 pg_free(df->base.pathname);
506 pg_free(df->fullpath);
507 pg_free(df->temp_suffix);
508 pg_free(df);
509
510 return r;
511}

References Assert, clear_error, close, CLOSE_NORMAL, CLOSE_UNLINK, WalWriteMethod::compression_algorithm, dir_get_file_name(), durable_rename(), fb(), filename, fsync_fname(), fsync_parent_path(), WalWriteMethod::lasterrno, WalWriteMethod::lasterrstring, MAXPGPATH, PG_COMPRESSION_GZIP, PG_COMPRESSION_LZ4, pg_free(), pg_log_error, snprintf, WalWriteMethod::sync, write, and Walfile::wwmethod.

◆ dir_existsfile()

static bool dir_existsfile ( WalWriteMethod wwmethod,
const char pathname 
)
static

Definition at line 584 of file walmethods.c.

585{
587 char tmppath[MAXPGPATH];
588 int fd;
589
590 clear_error(wwmethod);
591
592 snprintf(tmppath, sizeof(tmppath), "%s/%s",
593 dir_data->basedir, pathname);
594
596 if (fd < 0)
597
598 /*
599 * Skip setting dir_data->lasterrno here because we are only checking
600 * for existence.
601 */
602 return false;
603 close(fd);
604 return true;
605}

References clear_error, close, fb(), fd(), MAXPGPATH, PG_BINARY, and snprintf.

◆ dir_finish()

static bool dir_finish ( WalWriteMethod wwmethod)
static

Definition at line 608 of file walmethods.c.

609{
610 clear_error(wwmethod);
611
612 if (wwmethod->sync)
613 {
615
616 /*
617 * Files are fsynced when they are closed, but we need to fsync the
618 * directory entry here as well.
619 */
620 if (fsync_fname(dir_data->basedir, true) != 0)
621 {
622 wwmethod->lasterrno = errno;
623 return false;
624 }
625 }
626 return true;
627}

References clear_error, fb(), fsync_fname(), WalWriteMethod::lasterrno, and WalWriteMethod::sync.

◆ dir_free()

static void dir_free ( WalWriteMethod wwmethod)
static

Definition at line 630 of file walmethods.c.

631{
633
634 pg_free(dir_data->basedir);
635 pg_free(wwmethod);
636}

References fb(), and pg_free().

◆ dir_get_file_name()

static char * dir_get_file_name ( WalWriteMethod wwmethod,
const char pathname,
const char temp_suffix 
)
static

Definition at line 102 of file walmethods.c.

104{
105 char *filename = pg_malloc0(MAXPGPATH * sizeof(char));
106
107 snprintf(filename, MAXPGPATH, "%s%s%s",
108 pathname,
109 wwmethod->compression_algorithm == PG_COMPRESSION_GZIP ? ".gz" :
110 wwmethod->compression_algorithm == PG_COMPRESSION_LZ4 ? ".lz4" : "",
111 temp_suffix ? temp_suffix : "");
112
113 return filename;
114}

References WalWriteMethod::compression_algorithm, filename, MAXPGPATH, PG_COMPRESSION_GZIP, PG_COMPRESSION_LZ4, pg_malloc0(), and snprintf.

Referenced by dir_close(), and dir_open_for_write().

◆ dir_get_file_size()

static ssize_t dir_get_file_size ( WalWriteMethod wwmethod,
const char pathname 
)
static

Definition at line 565 of file walmethods.c.

566{
568 struct stat statbuf;
569 char tmppath[MAXPGPATH];
570
571 snprintf(tmppath, sizeof(tmppath), "%s/%s",
572 dir_data->basedir, pathname);
573
574 if (stat(tmppath, &statbuf) != 0)
575 {
576 wwmethod->lasterrno = errno;
577 return -1;
578 }
579
580 return statbuf.st_size;
581}

References fb(), WalWriteMethod::lasterrno, MAXPGPATH, snprintf, and stat.

◆ dir_open_for_write()

static Walfile * dir_open_for_write ( WalWriteMethod wwmethod,
const char pathname,
const char temp_suffix,
size_t  pad_to_size 
)
static

Definition at line 117 of file walmethods.c.

119{
121 char tmppath[MAXPGPATH];
122 char *filename;
123 int fd;
125#ifdef HAVE_LIBZ
126 gzFile gzfp = NULL;
127#endif
128#ifdef USE_LZ4
130 size_t lz4bufsize = 0;
131 void *lz4buf = NULL;
132#endif
133
134 clear_error(wwmethod);
135
136 filename = dir_get_file_name(wwmethod, pathname, temp_suffix);
137 snprintf(tmppath, sizeof(tmppath), "%s/%s",
138 dir_data->basedir, 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 */
148 if (fd < 0)
149 {
150 wwmethod->lasterrno = errno;
151 return NULL;
152 }
153
154#ifdef HAVE_LIBZ
156 {
157 gzfp = gzdopen(fd, "wb");
158 if (gzfp == NULL)
159 {
160 wwmethod->lasterrno = errno;
161 close(fd);
162 return NULL;
163 }
164
165 if (gzsetparams(gzfp, wwmethod->compression_level,
167 {
168 wwmethod->lasterrno = errno;
169 gzclose(gzfp);
170 return NULL;
171 }
172 }
173#endif
174#ifdef USE_LZ4
176 {
177 size_t ctx_out;
178 size_t header_size;
180
183 {
185 close(fd);
186 return NULL;
187 }
188
191
192 /* assign the compression level, default is 0 */
193 memset(&prefs, 0, sizeof(prefs));
194 prefs.compressionLevel = wwmethod->compression_level;
195
196 /* add the header */
199 {
203 close(fd);
204 return NULL;
205 }
206
207 errno = 0;
209 {
210 /* If write didn't set errno, assume problem is no disk space */
211 wwmethod->lasterrno = errno ? errno : ENOSPC;
214 close(fd);
215 return NULL;
216 }
217 }
218#endif
219
220 /* Do pre-padding on non-compressed files */
221 if (pad_to_size && wwmethod->compression_algorithm == PG_COMPRESSION_NONE)
222 {
223 ssize_t rc;
224
225 rc = pg_pwrite_zeros(fd, pad_to_size, 0);
226
227 if (rc < 0)
228 {
229 wwmethod->lasterrno = errno;
230 close(fd);
231 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 if (lseek(fd, 0, SEEK_SET) != 0)
239 {
240 wwmethod->lasterrno = errno;
241 close(fd);
242 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 if (wwmethod->sync)
253 {
254 if (fsync_fname(tmppath, false) != 0 ||
256 {
257 wwmethod->lasterrno = errno;
258#ifdef HAVE_LIBZ
260 gzclose(gzfp);
261 else
262#endif
263#ifdef USE_LZ4
265 {
269 close(fd);
270 }
271 else
272#endif
273 close(fd);
274 return NULL;
275 }
276 }
277
278 f = pg_malloc0(sizeof(DirectoryMethodFile));
279#ifdef HAVE_LIBZ
281 f->gzfp = gzfp;
282#endif
283#ifdef USE_LZ4
285 {
286 f->ctx = ctx;
287 f->lz4buf = lz4buf;
288 f->lz4bufsize = lz4bufsize;
289 }
290#endif
291
292 f->base.wwmethod = wwmethod;
293 f->base.currpos = 0;
294 f->base.pathname = pg_strdup(pathname);
295 f->fd = fd;
297 if (temp_suffix)
298 f->temp_suffix = pg_strdup(temp_suffix);
299
300 return &f->base;
301}

References DirectoryMethodFile::base, clear_error, close, WalWriteMethod::compression_algorithm, WalWriteMethod::compression_level, Walfile::currpos, dir_get_file_name(), fb(), DirectoryMethodFile::fd, fd(), filename, fsync_fname(), fsync_parent_path(), DirectoryMethodFile::fullpath, WalWriteMethod::lasterrno, WalWriteMethod::lasterrstring, LZ4_IN_SIZE, MAXPGPATH, Walfile::pathname, PG_BINARY, PG_COMPRESSION_GZIP, PG_COMPRESSION_LZ4, PG_COMPRESSION_NONE, pg_file_create_mode, pg_free(), pg_malloc0(), pg_pwrite_zeros(), pg_strdup(), snprintf, WalWriteMethod::sync, DirectoryMethodFile::temp_suffix, write, and Walfile::wwmethod.

◆ dir_sync()

static int dir_sync ( Walfile f)
static

Definition at line 514 of file walmethods.c.

515{
516 int r;
517
518 Assert(f != NULL);
520
521 if (!f->wwmethod->sync)
522 return 0;
523
524#ifdef HAVE_LIBZ
526 {
528 {
529 f->wwmethod->lasterrno = errno;
530 return -1;
531 }
532 }
533#endif
534#ifdef USE_LZ4
536 {
538 size_t compressed;
539
540 /* Flush any internal buffers */
541 compressed = LZ4F_flush(df->ctx, df->lz4buf, df->lz4bufsize, NULL);
543 {
545 return -1;
546 }
547
548 errno = 0;
549 if (write(df->fd, df->lz4buf, compressed) != compressed)
550 {
551 /* If write didn't set errno, assume problem is no disk space */
553 return -1;
554 }
555 }
556#endif
557
558 r = fsync(((DirectoryMethodFile *) f)->fd);
559 if (r < 0)
561 return r;
562}

References Assert, clear_error, WalWriteMethod::compression_algorithm, fb(), fd(), fsync, WalWriteMethod::lasterrno, WalWriteMethod::lasterrstring, PG_COMPRESSION_GZIP, PG_COMPRESSION_LZ4, WalWriteMethod::sync, write, and Walfile::wwmethod.

◆ dir_write()

static ssize_t dir_write ( Walfile f,
const void buf,
size_t  count 
)
static

Definition at line 304 of file walmethods.c.

305{
306 ssize_t r;
308
309 Assert(f != NULL);
311
312#ifdef HAVE_LIBZ
314 {
315 errno = 0;
316 r = (ssize_t) gzwrite(df->gzfp, buf, count);
317 if (r != count)
318 {
319 /* If write didn't set errno, assume problem is no disk space */
321 }
322 }
323 else
324#endif
325#ifdef USE_LZ4
327 {
328 size_t chunk;
329 size_t remaining;
330 const void *inbuf = buf;
331
332 remaining = count;
333 while (remaining > 0)
334 {
335 size_t compressed;
336
339 else
341
342 remaining -= chunk;
344 df->lz4buf, df->lz4bufsize,
345 inbuf, chunk,
346 NULL);
347
349 {
351 return -1;
352 }
353
354 errno = 0;
355 if (write(df->fd, df->lz4buf, compressed) != compressed)
356 {
357 /* If write didn't set errno, assume problem is no disk space */
359 return -1;
360 }
361
362 inbuf = ((const char *) inbuf) + chunk;
363 }
364
365 /* Our caller keeps track of the uncompressed size. */
366 r = (ssize_t) count;
367 }
368 else
369#endif
370 {
371 errno = 0;
372 r = write(df->fd, buf, count);
373 if (r != count)
374 {
375 /* If write didn't set errno, assume problem is no disk space */
377 }
378 }
379 if (r > 0)
380 df->base.currpos += r;
381 return r;
382}

References Assert, buf, clear_error, WalWriteMethod::compression_algorithm, fb(), WalWriteMethod::lasterrno, WalWriteMethod::lasterrstring, LZ4_IN_SIZE, PG_COMPRESSION_GZIP, PG_COMPRESSION_LZ4, remaining, write, and Walfile::wwmethod.

◆ GetLastWalMethodError()

const char * GetLastWalMethodError ( WalWriteMethod wwmethod)

◆ tar_close()

static int tar_close ( Walfile f,
WalCloseMethod  method 
)
static

Definition at line 1042 of file walmethods.c.

1043{
1045 int padding;
1047 TarMethodFile *tf = (TarMethodFile *) f;
1048
1049 Assert(f != NULL);
1051
1052 if (method == CLOSE_UNLINK)
1053 {
1055 {
1056 f->wwmethod->lasterrstring = _("unlink not supported with compression");
1057 return -1;
1058 }
1059
1060 /*
1061 * Unlink the file that we just wrote to the tar. We do this by
1062 * truncating it to the start of the header. This is safe as we only
1063 * allow writing of the very last file.
1064 */
1065 if (ftruncate(tar_data->fd, tf->ofs_start) != 0)
1066 {
1067 f->wwmethod->lasterrno = errno;
1068 return -1;
1069 }
1070
1071 pg_free(tf->base.pathname);
1072 pg_free(tf);
1073 tar_data->currentfile = NULL;
1074
1075 return 0;
1076 }
1077
1078 /*
1079 * Pad the file itself with zeroes if necessary. Note that this is
1080 * different from the tar format padding -- this is the padding we asked
1081 * for when the file was opened.
1082 */
1083 if (tf->pad_to_size)
1084 {
1086 {
1087 /*
1088 * A compressed tarfile is padded on close since we cannot know
1089 * the size of the compressed output until the end.
1090 */
1091 size_t sizeleft = tf->pad_to_size - tf->base.currpos;
1092
1093 if (sizeleft)
1094 {
1096 return -1;
1097 }
1098 }
1099 else
1100 {
1101 /*
1102 * An uncompressed tarfile was padded on creation, so just adjust
1103 * the current position as if we seeked to the end.
1104 */
1105 tf->base.currpos = tf->pad_to_size;
1106 }
1107 }
1108
1109 /*
1110 * Get the size of the file, and pad out to a multiple of the tar block
1111 * size.
1112 */
1113 filesize = f->currpos;
1115 if (padding)
1116 {
1117 char zerobuf[TAR_BLOCK_SIZE] = {0};
1118
1119 if (tar_write(f, zerobuf, padding) != padding)
1120 return -1;
1121 }
1122
1123
1124#ifdef HAVE_LIBZ
1126 {
1127 /* Flush the current buffer */
1128 if (!tar_write_compressed_data(tar_data, NULL, 0, true))
1129 return -1;
1130 }
1131#endif
1132
1133 /*
1134 * Now go back and update the header with the correct filesize and
1135 * possibly also renaming the file. We overwrite the entire current header
1136 * when done, including the checksum.
1137 */
1139
1140 if (method == CLOSE_NORMAL)
1141
1142 /*
1143 * We overwrite it with what it was before if we have no tempname,
1144 * since we're going to write the buffer anyway.
1145 */
1146 strlcpy(&(tf->header[TAR_OFFSET_NAME]), tf->base.pathname, 100);
1147
1149 tarChecksum(((TarMethodFile *) f)->header));
1150 if (lseek(tar_data->fd, tf->ofs_start, SEEK_SET) != ((TarMethodFile *) f)->ofs_start)
1151 {
1152 f->wwmethod->lasterrno = errno;
1153 return -1;
1154 }
1155 if (f->wwmethod->compression_algorithm == PG_COMPRESSION_NONE)
1156 {
1157 errno = 0;
1159 {
1160 /* If write didn't set errno, assume problem is no disk space */
1161 f->wwmethod->lasterrno = errno ? errno : ENOSPC;
1162 return -1;
1163 }
1164 }
1165#ifdef HAVE_LIBZ
1166 else if (f->wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
1167 {
1168 /* Turn off compression */
1170 {
1171 f->wwmethod->lasterrstring = _("could not change compression parameters");
1172 return -1;
1173 }
1174
1175 /* Overwrite the header, assuming the size will be the same */
1176 if (!tar_write_compressed_data(tar_data, tar_data->currentfile->header,
1177 TAR_BLOCK_SIZE, true))
1178 return -1;
1179
1180 /* Turn compression back on */
1181 if (deflateParams(tar_data->zp, f->wwmethod->compression_level,
1183 {
1184 f->wwmethod->lasterrstring = _("could not change compression parameters");
1185 return -1;
1186 }
1187 }
1188#endif
1189 else
1190 {
1191 /* not reachable */
1192 Assert(false);
1193 }
1194
1195 /* Move file pointer back down to end, so we can write the next file */
1196 if (lseek(tar_data->fd, 0, SEEK_END) < 0)
1197 {
1198 f->wwmethod->lasterrno = errno;
1199 return -1;
1200 }
1201
1202 /* Always fsync on close, so the padding gets fsynced */
1203 if (tar_sync(f) < 0)
1204 {
1205 /* XXX this seems pretty bogus; why is only this case fatal? */
1206 pg_fatal("could not fsync file \"%s\": %s",
1207 tf->base.pathname, GetLastWalMethodError(f->wwmethod));
1208 }
1209
1210 /* Clean up and done */
1211 pg_free(tf->base.pathname);
1212 pg_free(tf);
1213 tar_data->currentfile = NULL;
1214
1215 return 0;
1216}

References _, Assert, TarMethodFile::base, clear_error, CLOSE_NORMAL, CLOSE_UNLINK, WalWriteMethod::compression_algorithm, WalWriteMethod::compression_level, Walfile::currpos, fb(), GetLastWalMethodError(), TarMethodFile::header, WalWriteMethod::lasterrno, WalWriteMethod::lasterrstring, TarMethodFile::ofs_start, TarMethodFile::pad_to_size, Walfile::pathname, PG_COMPRESSION_GZIP, PG_COMPRESSION_NONE, pg_fatal, pg_free(), print_tar_number(), strlcpy(), TAR_BLOCK_SIZE, TAR_OFFSET_CHECKSUM, TAR_OFFSET_NAME, TAR_OFFSET_SIZE, tar_sync(), tar_write(), tar_write_padding_data(), tarChecksum(), tarPaddingBytesRequired(), write, and Walfile::wwmethod.

Referenced by tar_finish().

◆ tar_existsfile()

static bool tar_existsfile ( WalWriteMethod wwmethod,
const char pathname 
)
static

Definition at line 1219 of file walmethods.c.

1220{
1221 clear_error(wwmethod);
1222 /* We only deal with new tarfiles, so nothing externally created exists */
1223 return false;
1224}

References clear_error.

◆ tar_finish()

static bool tar_finish ( WalWriteMethod wwmethod)
static

Definition at line 1227 of file walmethods.c.

1228{
1229 TarMethodData *tar_data = (TarMethodData *) wwmethod;
1230 char zerobuf[1024] = {0};
1231
1232 clear_error(wwmethod);
1233
1234 if (tar_data->currentfile)
1235 {
1236 if (tar_close(&tar_data->currentfile->base, CLOSE_NORMAL) != 0)
1237 return false;
1238 }
1239
1240 /* A tarfile always ends with two empty blocks */
1242 {
1243 errno = 0;
1244 if (write(tar_data->fd, zerobuf, sizeof(zerobuf)) != sizeof(zerobuf))
1245 {
1246 /* If write didn't set errno, assume problem is no disk space */
1247 wwmethod->lasterrno = errno ? errno : ENOSPC;
1248 return false;
1249 }
1250 }
1251#ifdef HAVE_LIBZ
1252 else if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
1253 {
1255 false))
1256 return false;
1257
1258 /* Also flush all data to make sure the gzip stream is finished */
1259 tar_data->zp->next_in = NULL;
1260 tar_data->zp->avail_in = 0;
1261 while (true)
1262 {
1263 int r;
1264
1265 r = deflate(tar_data->zp, Z_FINISH);
1266
1267 if (r == Z_STREAM_ERROR)
1268 {
1269 wwmethod->lasterrstring = _("could not compress data");
1270 return false;
1271 }
1272 if (tar_data->zp->avail_out < ZLIB_OUT_SIZE)
1273 {
1274 size_t len = ZLIB_OUT_SIZE - tar_data->zp->avail_out;
1275
1276 errno = 0;
1277 if (write(tar_data->fd, tar_data->zlibOut, len) != len)
1278 {
1279 /*
1280 * If write didn't set errno, assume problem is no disk
1281 * space.
1282 */
1283 wwmethod->lasterrno = errno ? errno : ENOSPC;
1284 return false;
1285 }
1286 }
1287 if (r == Z_STREAM_END)
1288 break;
1289 }
1290
1291 if (deflateEnd(tar_data->zp) != Z_OK)
1292 {
1293 wwmethod->lasterrstring = _("could not close compression stream");
1294 return false;
1295 }
1296 }
1297#endif
1298 else
1299 {
1300 /* not reachable */
1301 Assert(false);
1302 }
1303
1304 /* sync the empty blocks as well, since they're after the last file */
1305 if (wwmethod->sync)
1306 {
1307 if (fsync(tar_data->fd) != 0)
1308 {
1309 wwmethod->lasterrno = errno;
1310 return false;
1311 }
1312 }
1313
1314 if (close(tar_data->fd) != 0)
1315 {
1316 wwmethod->lasterrno = errno;
1317 return false;
1318 }
1319
1320 tar_data->fd = -1;
1321
1322 if (wwmethod->sync)
1323 {
1324 if (fsync_fname(tar_data->tarfilename, false) != 0 ||
1325 fsync_parent_path(tar_data->tarfilename) != 0)
1326 {
1327 wwmethod->lasterrno = errno;
1328 return false;
1329 }
1330 }
1331
1332 return true;
1333}

References _, Assert, clear_error, close, CLOSE_NORMAL, WalWriteMethod::compression_algorithm, fb(), fsync, fsync_fname(), fsync_parent_path(), WalWriteMethod::lasterrno, WalWriteMethod::lasterrstring, len, PG_COMPRESSION_GZIP, PG_COMPRESSION_NONE, WalWriteMethod::sync, tar_close(), write, and ZLIB_OUT_SIZE.

◆ tar_free()

static void tar_free ( WalWriteMethod wwmethod)
static

Definition at line 1336 of file walmethods.c.

1337{
1338 TarMethodData *tar_data = (TarMethodData *) wwmethod;
1339
1340 pg_free(tar_data->tarfilename);
1341#ifdef HAVE_LIBZ
1343 pg_free(tar_data->zlibOut);
1344#endif
1345 pg_free(wwmethod);
1346}

References WalWriteMethod::compression_algorithm, fb(), PG_COMPRESSION_GZIP, and pg_free().

◆ tar_get_file_name()

static char * tar_get_file_name ( WalWriteMethod wwmethod,
const char pathname,
const char temp_suffix 
)
static

Definition at line 825 of file walmethods.c.

827{
828 char *filename = pg_malloc0(MAXPGPATH * sizeof(char));
829
830 snprintf(filename, MAXPGPATH, "%s%s",
831 pathname, temp_suffix ? temp_suffix : "");
832
833 return filename;
834}

References filename, MAXPGPATH, pg_malloc0(), and snprintf.

Referenced by tar_open_for_write().

◆ tar_get_file_size()

static ssize_t tar_get_file_size ( WalWriteMethod wwmethod,
const char pathname 
)
static

Definition at line 1007 of file walmethods.c.

1008{
1009 clear_error(wwmethod);
1010
1011 /* Currently not used, so not supported */
1012 wwmethod->lasterrno = ENOSYS;
1013 return -1;
1014}

References clear_error, fb(), and WalWriteMethod::lasterrno.

◆ tar_open_for_write()

static Walfile * tar_open_for_write ( WalWriteMethod wwmethod,
const char pathname,
const char temp_suffix,
size_t  pad_to_size 
)
static

Definition at line 837 of file walmethods.c.

839{
840 TarMethodData *tar_data = (TarMethodData *) wwmethod;
841 char *tmppath;
842
843 clear_error(wwmethod);
844
845 if (tar_data->fd < 0)
846 {
847 /*
848 * We open the tar file only when we first try to write to it.
849 */
850 tar_data->fd = open(tar_data->tarfilename,
853 if (tar_data->fd < 0)
854 {
855 wwmethod->lasterrno = errno;
856 return NULL;
857 }
858
859#ifdef HAVE_LIBZ
861 {
862 tar_data->zp = (z_streamp) pg_malloc(sizeof(z_stream));
863 tar_data->zp->zalloc = Z_NULL;
864 tar_data->zp->zfree = Z_NULL;
865 tar_data->zp->opaque = Z_NULL;
866 tar_data->zp->next_out = tar_data->zlibOut;
867 tar_data->zp->avail_out = ZLIB_OUT_SIZE;
868
869 /*
870 * Initialize deflation library. Adding the magic value 16 to the
871 * default 15 for the windowBits parameter makes the output be
872 * gzip instead of zlib.
873 */
874 if (deflateInit2(tar_data->zp, wwmethod->compression_level,
875 Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK)
876 {
877 pg_free(tar_data->zp);
878 tar_data->zp = NULL;
879 wwmethod->lasterrstring =
880 _("could not initialize compression library");
881 return NULL;
882 }
883 }
884#endif
885
886 /* There's no tar header itself, the file starts with regular files */
887 }
888
889 if (tar_data->currentfile != NULL)
890 {
891 wwmethod->lasterrstring =
892 _("implementation error: tar files can't have more than one open file");
893 return NULL;
894 }
895
896 tar_data->currentfile = pg_malloc0(sizeof(TarMethodFile));
897 tar_data->currentfile->base.wwmethod = wwmethod;
898
899 tmppath = tar_get_file_name(wwmethod, pathname, temp_suffix);
900
901 /* Create a header with size set to 0 - we will fill out the size on close */
902 if (tarCreateHeader(tar_data->currentfile->header, tmppath, NULL, 0, S_IRUSR | S_IWUSR, 0, 0, time(NULL)) != TAR_OK)
903 {
904 pg_free(tar_data->currentfile);
906 tar_data->currentfile = NULL;
907 wwmethod->lasterrstring = _("could not create tar header");
908 return NULL;
909 }
910
912
913#ifdef HAVE_LIBZ
915 {
916 /* Flush existing data */
918 return NULL;
919
920 /* Turn off compression for header */
922 {
923 wwmethod->lasterrstring =
924 _("could not change compression parameters");
925 return NULL;
926 }
927 }
928#endif
929
930 tar_data->currentfile->ofs_start = lseek(tar_data->fd, 0, SEEK_CUR);
931 if (tar_data->currentfile->ofs_start == -1)
932 {
933 wwmethod->lasterrno = errno;
934 pg_free(tar_data->currentfile);
935 tar_data->currentfile = NULL;
936 return NULL;
937 }
938 tar_data->currentfile->base.currpos = 0;
939
941 {
942 errno = 0;
943 if (write(tar_data->fd, tar_data->currentfile->header,
945 {
946 /* If write didn't set errno, assume problem is no disk space */
947 wwmethod->lasterrno = errno ? errno : ENOSPC;
948 pg_free(tar_data->currentfile);
949 tar_data->currentfile = NULL;
950 return NULL;
951 }
952 }
953#ifdef HAVE_LIBZ
954 else if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
955 {
956 /* Write header through the zlib APIs but with no compression */
957 if (!tar_write_compressed_data(tar_data, tar_data->currentfile->header,
958 TAR_BLOCK_SIZE, true))
959 return NULL;
960
961 /* Re-enable compression for the rest of the file */
962 if (deflateParams(tar_data->zp, wwmethod->compression_level,
964 {
965 wwmethod->lasterrstring = _("could not change compression parameters");
966 return NULL;
967 }
968 }
969#endif
970 else
971 {
972 /* not reachable */
973 Assert(false);
974 }
975
976 tar_data->currentfile->base.pathname = pg_strdup(pathname);
977
978 /*
979 * Uncompressed files are padded on creation, but for compression we can't
980 * do that
981 */
982 if (pad_to_size)
983 {
984 tar_data->currentfile->pad_to_size = pad_to_size;
986 {
987 /* Uncompressed, so pad now */
988 if (!tar_write_padding_data(tar_data->currentfile, pad_to_size))
989 return NULL;
990 /* Seek back to start */
991 if (lseek(tar_data->fd,
992 tar_data->currentfile->ofs_start + TAR_BLOCK_SIZE,
993 SEEK_SET) != tar_data->currentfile->ofs_start + TAR_BLOCK_SIZE)
994 {
995 wwmethod->lasterrno = errno;
996 return NULL;
997 }
998
999 tar_data->currentfile->base.currpos = 0;
1000 }
1001 }
1002
1003 return &tar_data->currentfile->base;
1004}

References _, Assert, clear_error, WalWriteMethod::compression_algorithm, WalWriteMethod::compression_level, fb(), WalWriteMethod::lasterrno, WalWriteMethod::lasterrstring, PG_BINARY, PG_COMPRESSION_GZIP, PG_COMPRESSION_NONE, pg_file_create_mode, pg_free(), pg_malloc(), pg_malloc0(), pg_strdup(), S_IRUSR, S_IWUSR, TAR_BLOCK_SIZE, tar_get_file_name(), TAR_OK, tar_write_padding_data(), tarCreateHeader(), write, and ZLIB_OUT_SIZE.

◆ tar_sync()

static int tar_sync ( Walfile f)
static

Definition at line 1017 of file walmethods.c.

1018{
1020 int r;
1021
1022 Assert(f != NULL);
1024
1025 if (!f->wwmethod->sync)
1026 return 0;
1027
1028 /*
1029 * Always sync the whole tarfile, because that's all we can do. This makes
1030 * no sense on compressed files, so just ignore those.
1031 */
1033 return 0;
1034
1035 r = fsync(tar_data->fd);
1036 if (r < 0)
1037 f->wwmethod->lasterrno = errno;
1038 return r;
1039}

References Assert, clear_error, WalWriteMethod::compression_algorithm, fb(), fsync, WalWriteMethod::lasterrno, PG_COMPRESSION_NONE, WalWriteMethod::sync, and Walfile::wwmethod.

Referenced by tar_close().

◆ tar_write()

static ssize_t tar_write ( Walfile f,
const void buf,
size_t  count 
)
static

Definition at line 765 of file walmethods.c.

766{
768 ssize_t r;
769
770 Assert(f != NULL);
772
773 /* Tarfile will always be positioned at the end */
775 {
776 errno = 0;
777 r = write(tar_data->fd, buf, count);
778 if (r != count)
779 {
780 /* If write didn't set errno, assume problem is no disk space */
782 return -1;
783 }
784 f->currpos += r;
785 return r;
786 }
787#ifdef HAVE_LIBZ
789 {
790 if (!tar_write_compressed_data(tar_data, buf, count, false))
791 return -1;
792 f->currpos += count;
793 return count;
794 }
795#endif
796 else
797 {
798 /* Can't happen - compression enabled with no method set */
800 return -1;
801 }
802}

References Assert, buf, clear_error, WalWriteMethod::compression_algorithm, Walfile::currpos, fb(), WalWriteMethod::lasterrno, PG_COMPRESSION_GZIP, PG_COMPRESSION_NONE, write, and Walfile::wwmethod.

Referenced by tar_close(), and tar_write_padding_data().

◆ tar_write_padding_data()

static bool tar_write_padding_data ( TarMethodFile f,
size_t  bytes 
)
static

Definition at line 805 of file walmethods.c.

806{
808 size_t bytesleft = bytes;
809
810 memset(zerobuf.data, 0, XLOG_BLCKSZ);
811 while (bytesleft)
812 {
814 ssize_t r = tar_write(&f->base, zerobuf.data, bytestowrite);
815
816 if (r < 0)
817 return false;
818 bytesleft -= r;
819 }
820
821 return true;
822}

References TarMethodFile::base, fb(), Min, and tar_write().

Referenced by tar_close(), and tar_open_for_write().

Variable Documentation

◆ WalDirectoryMethodOps

const WalWriteMethodOps WalDirectoryMethodOps
static
Initial value:
= {
.open_for_write = dir_open_for_write,
.close = dir_close,
.existsfile = dir_existsfile,
.get_file_size = dir_get_file_size,
.get_file_name = dir_get_file_name,
.write = dir_write,
.sync = dir_sync,
.finish = dir_finish,
.free = dir_free
}

Definition at line 58 of file walmethods.c.

58 {
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};

Referenced by CreateWalDirectoryMethod().

◆ WalTarMethodOps

const WalWriteMethodOps WalTarMethodOps
static
Initial value:
= {
.open_for_write = tar_open_for_write,
.close = tar_close,
.existsfile = tar_existsfile,
.get_file_size = tar_get_file_size,
.get_file_name = tar_get_file_name,
.write = tar_write,
.sync = tar_sync,
.finish = tar_finish,
.free = tar_free
}

Definition at line 679 of file walmethods.c.

679 {
680 .open_for_write = tar_open_for_write,
681 .close = tar_close,
682 .existsfile = tar_existsfile,
683 .get_file_size = tar_get_file_size,
684 .get_file_name = tar_get_file_name,
685 .write = tar_write,
686 .sync = tar_sync,
687 .finish = tar_finish,
688 .free = tar_free
689};

Referenced by CreateWalTarMethod().