LCOV - code coverage report
Current view: top level - src/bin/pg_basebackup - walmethods.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 646 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 23 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * walmethods.c - implementations of different ways to write received wal
       4              :  *
       5              :  * Portions Copyright (c) 1996-2026, 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 <fcntl.h>
      15              : #include <sys/stat.h>
      16              : #include <time.h>
      17              : #include <unistd.h>
      18              : 
      19              : #ifdef USE_LZ4
      20              : #include <lz4frame.h>
      21              : #endif
      22              : #ifdef HAVE_LIBZ
      23              : #include <zlib.h>
      24              : #endif
      25              : 
      26              : #include "common/file_perm.h"
      27              : #include "common/file_utils.h"
      28              : #include "common/logging.h"
      29              : #include "pgtar.h"
      30              : #include "walmethods.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              : static 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            0 : dir_get_file_name(WalWriteMethod *wwmethod,
     103              :                                   const char *pathname, const char *temp_suffix)
     104              : {
     105            0 :         char       *filename = pg_malloc0(MAXPGPATH * sizeof(char));
     106              : 
     107            0 :         snprintf(filename, MAXPGPATH, "%s%s%s",
     108            0 :                          pathname,
     109            0 :                          wwmethod->compression_algorithm == PG_COMPRESSION_GZIP ? ".gz" :
     110            0 :                          wwmethod->compression_algorithm == PG_COMPRESSION_LZ4 ? ".lz4" : "",
     111            0 :                          temp_suffix ? temp_suffix : "");
     112              : 
     113            0 :         return filename;
     114            0 : }
     115              : 
     116              : static Walfile *
     117            0 : dir_open_for_write(WalWriteMethod *wwmethod, const char *pathname,
     118              :                                    const char *temp_suffix, size_t pad_to_size)
     119              : {
     120            0 :         DirectoryMethodData *dir_data = (DirectoryMethodData *) wwmethod;
     121            0 :         char            tmppath[MAXPGPATH];
     122            0 :         char       *filename;
     123            0 :         int                     fd;
     124            0 :         DirectoryMethodFile *f;
     125              : #ifdef HAVE_LIBZ
     126            0 :         gzFile          gzfp = NULL;
     127              : #endif
     128              : #ifdef USE_LZ4
     129            0 :         LZ4F_compressionContext_t ctx = NULL;
     130            0 :         size_t          lz4bufsize = 0;
     131            0 :         void       *lz4buf = NULL;
     132              : #endif
     133              : 
     134            0 :         clear_error(wwmethod);
     135              : 
     136            0 :         filename = dir_get_file_name(wwmethod, pathname, temp_suffix);
     137            0 :         snprintf(tmppath, sizeof(tmppath), "%s/%s",
     138            0 :                          dir_data->basedir, filename);
     139            0 :         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            0 :         fd = open(tmppath, O_WRONLY | O_CREAT | PG_BINARY, pg_file_create_mode);
     148            0 :         if (fd < 0)
     149              :         {
     150            0 :                 wwmethod->lasterrno = errno;
     151            0 :                 return NULL;
     152              :         }
     153              : 
     154              : #ifdef HAVE_LIBZ
     155            0 :         if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
     156              :         {
     157            0 :                 gzfp = gzdopen(fd, "wb");
     158            0 :                 if (gzfp == NULL)
     159              :                 {
     160            0 :                         wwmethod->lasterrno = errno;
     161            0 :                         close(fd);
     162            0 :                         return NULL;
     163              :                 }
     164              : 
     165            0 :                 if (gzsetparams(gzfp, wwmethod->compression_level,
     166            0 :                                                 Z_DEFAULT_STRATEGY) != Z_OK)
     167              :                 {
     168            0 :                         wwmethod->lasterrno = errno;
     169            0 :                         gzclose(gzfp);
     170            0 :                         return NULL;
     171              :                 }
     172            0 :         }
     173              : #endif
     174              : #ifdef USE_LZ4
     175            0 :         if (wwmethod->compression_algorithm == PG_COMPRESSION_LZ4)
     176              :         {
     177            0 :                 size_t          ctx_out;
     178            0 :                 size_t          header_size;
     179            0 :                 LZ4F_preferences_t prefs;
     180              : 
     181            0 :                 ctx_out = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
     182            0 :                 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            0 :                 lz4bufsize = LZ4F_compressBound(LZ4_IN_SIZE, NULL);
     190            0 :                 lz4buf = pg_malloc0(lz4bufsize);
     191              : 
     192              :                 /* assign the compression level, default is 0 */
     193            0 :                 memset(&prefs, 0, sizeof(prefs));
     194            0 :                 prefs.compressionLevel = wwmethod->compression_level;
     195              : 
     196              :                 /* add the header */
     197            0 :                 header_size = LZ4F_compressBegin(ctx, lz4buf, lz4bufsize, &prefs);
     198            0 :                 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            0 :                 errno = 0;
     208            0 :                 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            0 :         }
     218              : #endif
     219              : 
     220              :         /* Do pre-padding on non-compressed files */
     221            0 :         if (pad_to_size && wwmethod->compression_algorithm == PG_COMPRESSION_NONE)
     222              :         {
     223            0 :                 ssize_t         rc;
     224              : 
     225            0 :                 rc = pg_pwrite_zeros(fd, pad_to_size, 0);
     226              : 
     227            0 :                 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            0 :                 if (lseek(fd, 0, SEEK_SET) != 0)
     239              :                 {
     240            0 :                         wwmethod->lasterrno = errno;
     241            0 :                         close(fd);
     242            0 :                         return NULL;
     243              :                 }
     244            0 :         }
     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            0 :         if (wwmethod->sync)
     253              :         {
     254            0 :                 if (fsync_fname(tmppath, false) != 0 ||
     255            0 :                         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            0 :                         }
     271              :                         else
     272              : #endif
     273            0 :                                 close(fd);
     274            0 :                         return NULL;
     275              :                 }
     276            0 :         }
     277              : 
     278            0 :         f = pg_malloc0(sizeof(DirectoryMethodFile));
     279              : #ifdef HAVE_LIBZ
     280            0 :         if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
     281            0 :                 f->gzfp = gzfp;
     282              : #endif
     283              : #ifdef USE_LZ4
     284            0 :         if (wwmethod->compression_algorithm == PG_COMPRESSION_LZ4)
     285              :         {
     286            0 :                 f->ctx = ctx;
     287            0 :                 f->lz4buf = lz4buf;
     288            0 :                 f->lz4bufsize = lz4bufsize;
     289            0 :         }
     290              : #endif
     291              : 
     292            0 :         f->base.wwmethod = wwmethod;
     293            0 :         f->base.currpos = 0;
     294            0 :         f->base.pathname = pg_strdup(pathname);
     295            0 :         f->fd = fd;
     296            0 :         f->fullpath = pg_strdup(tmppath);
     297            0 :         if (temp_suffix)
     298            0 :                 f->temp_suffix = pg_strdup(temp_suffix);
     299              : 
     300            0 :         return &f->base;
     301            0 : }
     302              : 
     303              : static ssize_t
     304            0 : dir_write(Walfile *f, const void *buf, size_t count)
     305              : {
     306            0 :         ssize_t         r;
     307            0 :         DirectoryMethodFile *df = (DirectoryMethodFile *) f;
     308              : 
     309            0 :         Assert(f != NULL);
     310            0 :         clear_error(f->wwmethod);
     311              : 
     312              : #ifdef HAVE_LIBZ
     313            0 :         if (f->wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
     314              :         {
     315            0 :                 errno = 0;
     316            0 :                 r = (ssize_t) gzwrite(df->gzfp, buf, count);
     317            0 :                 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            0 :                 }
     322            0 :         }
     323              :         else
     324              : #endif
     325              : #ifdef USE_LZ4
     326            0 :         if (f->wwmethod->compression_algorithm == PG_COMPRESSION_LZ4)
     327              :         {
     328            0 :                 size_t          chunk;
     329            0 :                 size_t          remaining;
     330            0 :                 const void *inbuf = buf;
     331              : 
     332            0 :                 remaining = count;
     333            0 :                 while (remaining > 0)
     334              :                 {
     335            0 :                         size_t          compressed;
     336              : 
     337            0 :                         if (remaining > LZ4_IN_SIZE)
     338            0 :                                 chunk = LZ4_IN_SIZE;
     339              :                         else
     340            0 :                                 chunk = remaining;
     341              : 
     342            0 :                         remaining -= chunk;
     343            0 :                         compressed = LZ4F_compressUpdate(df->ctx,
     344            0 :                                                                                          df->lz4buf, df->lz4bufsize,
     345            0 :                                                                                          inbuf, chunk,
     346              :                                                                                          NULL);
     347              : 
     348            0 :                         if (LZ4F_isError(compressed))
     349              :                         {
     350            0 :                                 f->wwmethod->lasterrstring = LZ4F_getErrorName(compressed);
     351            0 :                                 return -1;
     352              :                         }
     353              : 
     354            0 :                         errno = 0;
     355            0 :                         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            0 :                         inbuf = ((char *) inbuf) + chunk;
     363            0 :                 }
     364              : 
     365              :                 /* Our caller keeps track of the uncompressed size. */
     366            0 :                 r = (ssize_t) count;
     367            0 :         }
     368              :         else
     369              : #endif
     370              :         {
     371            0 :                 errno = 0;
     372            0 :                 r = write(df->fd, buf, count);
     373            0 :                 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            0 :                 }
     378              :         }
     379            0 :         if (r > 0)
     380            0 :                 df->base.currpos += r;
     381            0 :         return r;
     382            0 : }
     383              : 
     384              : static int
     385            0 : dir_close(Walfile *f, WalCloseMethod method)
     386              : {
     387            0 :         int                     r;
     388            0 :         DirectoryMethodFile *df = (DirectoryMethodFile *) f;
     389            0 :         DirectoryMethodData *dir_data = (DirectoryMethodData *) f->wwmethod;
     390            0 :         char            tmppath[MAXPGPATH];
     391            0 :         char            tmppath2[MAXPGPATH];
     392              : 
     393            0 :         Assert(f != NULL);
     394            0 :         clear_error(f->wwmethod);
     395              : 
     396              : #ifdef HAVE_LIBZ
     397            0 :         if (f->wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
     398              :         {
     399            0 :                 errno = 0;                              /* in case gzclose() doesn't set it */
     400            0 :                 r = gzclose(df->gzfp);
     401            0 :         }
     402              :         else
     403              : #endif
     404              : #ifdef USE_LZ4
     405            0 :         if (f->wwmethod->compression_algorithm == PG_COMPRESSION_LZ4)
     406              :         {
     407            0 :                 size_t          compressed;
     408              : 
     409            0 :                 compressed = LZ4F_compressEnd(df->ctx,
     410            0 :                                                                           df->lz4buf, df->lz4bufsize,
     411              :                                                                           NULL);
     412              : 
     413            0 :                 if (LZ4F_isError(compressed))
     414              :                 {
     415            0 :                         f->wwmethod->lasterrstring = LZ4F_getErrorName(compressed);
     416            0 :                         return -1;
     417              :                 }
     418              : 
     419            0 :                 errno = 0;
     420            0 :                 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            0 :                 r = close(df->fd);
     428            0 :         }
     429              :         else
     430              : #endif
     431            0 :                 r = close(df->fd);
     432              : 
     433            0 :         if (r == 0)
     434              :         {
     435              :                 /* Build path to the current version of the file */
     436            0 :                 if (method == CLOSE_NORMAL && df->temp_suffix)
     437              :                 {
     438            0 :                         char       *filename;
     439            0 :                         char       *filename2;
     440              : 
     441              :                         /*
     442              :                          * If we have a temp prefix, normal operation is to rename the
     443              :                          * file.
     444              :                          */
     445            0 :                         filename = dir_get_file_name(f->wwmethod, df->base.pathname,
     446            0 :                                                                                  df->temp_suffix);
     447            0 :                         snprintf(tmppath, sizeof(tmppath), "%s/%s",
     448            0 :                                          dir_data->basedir, filename);
     449            0 :                         pg_free(filename);
     450              : 
     451              :                         /* permanent name, so no need for the prefix */
     452            0 :                         filename2 = dir_get_file_name(f->wwmethod, df->base.pathname, NULL);
     453            0 :                         snprintf(tmppath2, sizeof(tmppath2), "%s/%s",
     454            0 :                                          dir_data->basedir, filename2);
     455            0 :                         pg_free(filename2);
     456            0 :                         if (f->wwmethod->sync)
     457            0 :                                 r = durable_rename(tmppath, tmppath2);
     458              :                         else
     459              :                         {
     460            0 :                                 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            0 :                                 }
     466              :                         }
     467            0 :                 }
     468            0 :                 else if (method == CLOSE_UNLINK)
     469              :                 {
     470            0 :                         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            0 :                                          dir_data->basedir, filename);
     477            0 :                         pg_free(filename);
     478            0 :                         r = unlink(tmppath);
     479            0 :                 }
     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            0 :                         if (f->wwmethod->sync)
     488              :                         {
     489            0 :                                 r = fsync_fname(df->fullpath, false);
     490            0 :                                 if (r == 0)
     491            0 :                                         r = fsync_parent_path(df->fullpath);
     492            0 :                         }
     493              :                 }
     494            0 :         }
     495              : 
     496            0 :         if (r != 0)
     497            0 :                 f->wwmethod->lasterrno = errno;
     498              : 
     499              : #ifdef USE_LZ4
     500            0 :         pg_free(df->lz4buf);
     501              :         /* supports free on NULL */
     502            0 :         LZ4F_freeCompressionContext(df->ctx);
     503              : #endif
     504              : 
     505            0 :         pg_free(df->base.pathname);
     506            0 :         pg_free(df->fullpath);
     507            0 :         pg_free(df->temp_suffix);
     508            0 :         pg_free(df);
     509              : 
     510            0 :         return r;
     511            0 : }
     512              : 
     513              : static int
     514            0 : dir_sync(Walfile *f)
     515              : {
     516            0 :         int                     r;
     517              : 
     518            0 :         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            0 :         }
     533              : #endif
     534              : #ifdef USE_LZ4
     535            0 :         if (f->wwmethod->compression_algorithm == PG_COMPRESSION_LZ4)
     536              :         {
     537            0 :                 DirectoryMethodFile *df = (DirectoryMethodFile *) f;
     538            0 :                 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            0 :         }
     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            0 : }
     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            0 :         struct stat statbuf;
     569            0 :         char            tmppath[MAXPGPATH];
     570              : 
     571            0 :         snprintf(tmppath, sizeof(tmppath), "%s/%s",
     572            0 :                          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            0 : }
     582              : 
     583              : static bool
     584            0 : dir_existsfile(WalWriteMethod *wwmethod, const char *pathname)
     585              : {
     586            0 :         DirectoryMethodData *dir_data = (DirectoryMethodData *) wwmethod;
     587            0 :         char            tmppath[MAXPGPATH];
     588            0 :         int                     fd;
     589              : 
     590            0 :         clear_error(wwmethod);
     591              : 
     592            0 :         snprintf(tmppath, sizeof(tmppath), "%s/%s",
     593            0 :                          dir_data->basedir, pathname);
     594              : 
     595            0 :         fd = open(tmppath, O_RDONLY | PG_BINARY, 0);
     596            0 :         if (fd < 0)
     597              : 
     598              :                 /*
     599              :                  * Skip setting dir_data->lasterrno here because we are only checking
     600              :                  * for existence.
     601              :                  */
     602            0 :                 return false;
     603            0 :         close(fd);
     604            0 :         return true;
     605            0 : }
     606              : 
     607              : static bool
     608            0 : dir_finish(WalWriteMethod *wwmethod)
     609              : {
     610            0 :         clear_error(wwmethod);
     611              : 
     612            0 :         if (wwmethod->sync)
     613              :         {
     614            0 :                 DirectoryMethodData *dir_data = (DirectoryMethodData *) wwmethod;
     615              : 
     616              :                 /*
     617              :                  * Files are fsynced when they are closed, but we need to fsync the
     618              :                  * directory entry here as well.
     619              :                  */
     620            0 :                 if (fsync_fname(dir_data->basedir, true) != 0)
     621              :                 {
     622            0 :                         wwmethod->lasterrno = errno;
     623            0 :                         return false;
     624              :                 }
     625            0 :         }
     626            0 :         return true;
     627            0 : }
     628              : 
     629              : static void
     630            0 : dir_free(WalWriteMethod *wwmethod)
     631              : {
     632            0 :         DirectoryMethodData *dir_data = (DirectoryMethodData *) wwmethod;
     633              : 
     634            0 :         pg_free(dir_data->basedir);
     635            0 :         pg_free(wwmethod);
     636            0 : }
     637              : 
     638              : 
     639              : WalWriteMethod *
     640            0 : CreateWalDirectoryMethod(const char *basedir,
     641              :                                                  pg_compress_algorithm compression_algorithm,
     642              :                                                  int compression_level, bool sync)
     643              : {
     644            0 :         DirectoryMethodData *wwmethod;
     645              : 
     646            0 :         wwmethod = pg_malloc0(sizeof(DirectoryMethodData));
     647            0 :         *((const WalWriteMethodOps **) &wwmethod->base.ops) =
     648              :                 &WalDirectoryMethodOps;
     649            0 :         wwmethod->base.compression_algorithm = compression_algorithm;
     650            0 :         wwmethod->base.compression_level = compression_level;
     651            0 :         wwmethod->base.sync = sync;
     652            0 :         clear_error(&wwmethod->base);
     653            0 :         wwmethod->basedir = pg_strdup(basedir);
     654              : 
     655            0 :         return &wwmethod->base;
     656            0 : }
     657              : 
     658              : 
     659              : /*-------------------------------------------------------------------------
     660              :  * WalTarMethod - write wal to a tar file containing pg_wal contents
     661              :  *-------------------------------------------------------------------------
     662              :  */
     663              : 
     664              : static Walfile *tar_open_for_write(WalWriteMethod *wwmethod,
     665              :                                                                    const char *pathname,
     666              :                                                                    const char *temp_suffix,
     667              :                                                                    size_t pad_to_size);
     668              : static int      tar_close(Walfile *f, WalCloseMethod method);
     669              : static bool tar_existsfile(WalWriteMethod *wwmethod, const char *pathname);
     670              : static ssize_t tar_get_file_size(WalWriteMethod *wwmethod,
     671              :                                                                  const char *pathname);
     672              : static char *tar_get_file_name(WalWriteMethod *wwmethod,
     673              :                                                            const char *pathname, const char *temp_suffix);
     674              : static ssize_t tar_write(Walfile *f, const void *buf, size_t count);
     675              : static int      tar_sync(Walfile *f);
     676              : static bool tar_finish(WalWriteMethod *wwmethod);
     677              : static void tar_free(WalWriteMethod *wwmethod);
     678              : 
     679              : static const WalWriteMethodOps WalTarMethodOps = {
     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              : };
     690              : 
     691              : typedef struct TarMethodFile
     692              : {
     693              :         Walfile         base;
     694              :         pgoff_t         ofs_start;              /* Where does the *header* for this file start */
     695              :         char            header[TAR_BLOCK_SIZE];
     696              :         size_t          pad_to_size;
     697              : } TarMethodFile;
     698              : 
     699              : typedef struct TarMethodData
     700              : {
     701              :         WalWriteMethod base;
     702              :         char       *tarfilename;
     703              :         int                     fd;
     704              :         TarMethodFile *currentfile;
     705              : #ifdef HAVE_LIBZ
     706              :         z_streamp       zp;
     707              :         void       *zlibOut;
     708              : #endif
     709              : } TarMethodData;
     710              : 
     711              : #ifdef HAVE_LIBZ
     712              : static bool
     713            0 : tar_write_compressed_data(TarMethodData *tar_data, const void *buf, size_t count,
     714              :                                                   bool flush)
     715              : {
     716            0 :         tar_data->zp->next_in = buf;
     717            0 :         tar_data->zp->avail_in = count;
     718              : 
     719            0 :         while (tar_data->zp->avail_in || flush)
     720              :         {
     721            0 :                 int                     r;
     722              : 
     723            0 :                 r = deflate(tar_data->zp, flush ? Z_FINISH : Z_NO_FLUSH);
     724            0 :                 if (r == Z_STREAM_ERROR)
     725              :                 {
     726            0 :                         tar_data->base.lasterrstring = _("could not compress data");
     727            0 :                         return false;
     728              :                 }
     729              : 
     730            0 :                 if (tar_data->zp->avail_out < ZLIB_OUT_SIZE)
     731              :                 {
     732            0 :                         size_t          len = ZLIB_OUT_SIZE - tar_data->zp->avail_out;
     733              : 
     734            0 :                         errno = 0;
     735            0 :                         if (write(tar_data->fd, tar_data->zlibOut, len) != len)
     736              :                         {
     737              :                                 /* If write didn't set errno, assume problem is no disk space */
     738            0 :                                 tar_data->base.lasterrno = errno ? errno : ENOSPC;
     739            0 :                                 return false;
     740              :                         }
     741              : 
     742            0 :                         tar_data->zp->next_out = tar_data->zlibOut;
     743            0 :                         tar_data->zp->avail_out = ZLIB_OUT_SIZE;
     744            0 :                 }
     745              : 
     746            0 :                 if (r == Z_STREAM_END)
     747            0 :                         break;
     748            0 :         }
     749              : 
     750            0 :         if (flush)
     751              :         {
     752              :                 /* Reset the stream for writing */
     753            0 :                 if (deflateReset(tar_data->zp) != Z_OK)
     754              :                 {
     755            0 :                         tar_data->base.lasterrstring = _("could not reset compression stream");
     756            0 :                         return false;
     757              :                 }
     758            0 :         }
     759              : 
     760            0 :         return true;
     761            0 : }
     762              : #endif
     763              : 
     764              : static ssize_t
     765            0 : tar_write(Walfile *f, const void *buf, size_t count)
     766              : {
     767            0 :         TarMethodData *tar_data = (TarMethodData *) f->wwmethod;
     768            0 :         ssize_t         r;
     769              : 
     770            0 :         Assert(f != NULL);
     771            0 :         clear_error(f->wwmethod);
     772              : 
     773              :         /* Tarfile will always be positioned at the end */
     774            0 :         if (f->wwmethod->compression_algorithm == PG_COMPRESSION_NONE)
     775              :         {
     776            0 :                 errno = 0;
     777            0 :                 r = write(tar_data->fd, buf, count);
     778            0 :                 if (r != count)
     779              :                 {
     780              :                         /* If write didn't set errno, assume problem is no disk space */
     781            0 :                         f->wwmethod->lasterrno = errno ? errno : ENOSPC;
     782            0 :                         return -1;
     783              :                 }
     784            0 :                 f->currpos += r;
     785            0 :                 return r;
     786              :         }
     787              : #ifdef HAVE_LIBZ
     788            0 :         else if (f->wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
     789              :         {
     790            0 :                 if (!tar_write_compressed_data(tar_data, buf, count, false))
     791            0 :                         return -1;
     792            0 :                 f->currpos += count;
     793            0 :                 return count;
     794              :         }
     795              : #endif
     796              :         else
     797              :         {
     798              :                 /* Can't happen - compression enabled with no method set */
     799            0 :                 f->wwmethod->lasterrno = ENOSYS;
     800            0 :                 return -1;
     801              :         }
     802            0 : }
     803              : 
     804              : static bool
     805            0 : tar_write_padding_data(TarMethodFile *f, size_t bytes)
     806              : {
     807            0 :         PGAlignedXLogBlock zerobuf;
     808            0 :         size_t          bytesleft = bytes;
     809              : 
     810            0 :         memset(zerobuf.data, 0, XLOG_BLCKSZ);
     811            0 :         while (bytesleft)
     812              :         {
     813            0 :                 size_t          bytestowrite = Min(bytesleft, XLOG_BLCKSZ);
     814            0 :                 ssize_t         r = tar_write(&f->base, zerobuf.data, bytestowrite);
     815              : 
     816            0 :                 if (r < 0)
     817            0 :                         return false;
     818            0 :                 bytesleft -= r;
     819            0 :         }
     820              : 
     821            0 :         return true;
     822            0 : }
     823              : 
     824              : static char *
     825            0 : tar_get_file_name(WalWriteMethod *wwmethod, const char *pathname,
     826              :                                   const char *temp_suffix)
     827              : {
     828            0 :         char       *filename = pg_malloc0(MAXPGPATH * sizeof(char));
     829              : 
     830            0 :         snprintf(filename, MAXPGPATH, "%s%s",
     831            0 :                          pathname, temp_suffix ? temp_suffix : "");
     832              : 
     833            0 :         return filename;
     834            0 : }
     835              : 
     836              : static Walfile *
     837            0 : tar_open_for_write(WalWriteMethod *wwmethod, const char *pathname,
     838              :                                    const char *temp_suffix, size_t pad_to_size)
     839              : {
     840            0 :         TarMethodData *tar_data = (TarMethodData *) wwmethod;
     841            0 :         char       *tmppath;
     842              : 
     843            0 :         clear_error(wwmethod);
     844              : 
     845            0 :         if (tar_data->fd < 0)
     846              :         {
     847              :                 /*
     848              :                  * We open the tar file only when we first try to write to it.
     849              :                  */
     850            0 :                 tar_data->fd = open(tar_data->tarfilename,
     851              :                                                         O_WRONLY | O_CREAT | PG_BINARY,
     852            0 :                                                         pg_file_create_mode);
     853            0 :                 if (tar_data->fd < 0)
     854              :                 {
     855            0 :                         wwmethod->lasterrno = errno;
     856            0 :                         return NULL;
     857              :                 }
     858              : 
     859              : #ifdef HAVE_LIBZ
     860            0 :                 if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
     861              :                 {
     862            0 :                         tar_data->zp = (z_streamp) pg_malloc(sizeof(z_stream));
     863            0 :                         tar_data->zp->zalloc = Z_NULL;
     864            0 :                         tar_data->zp->zfree = Z_NULL;
     865            0 :                         tar_data->zp->opaque = Z_NULL;
     866            0 :                         tar_data->zp->next_out = tar_data->zlibOut;
     867            0 :                         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            0 :                         if (deflateInit2(tar_data->zp, wwmethod->compression_level,
     875            0 :                                                          Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK)
     876              :                         {
     877            0 :                                 pg_free(tar_data->zp);
     878            0 :                                 tar_data->zp = NULL;
     879            0 :                                 wwmethod->lasterrstring =
     880            0 :                                         _("could not initialize compression library");
     881            0 :                                 return NULL;
     882              :                         }
     883            0 :                 }
     884              : #endif
     885              : 
     886              :                 /* There's no tar header itself, the file starts with regular files */
     887            0 :         }
     888              : 
     889            0 :         if (tar_data->currentfile != NULL)
     890              :         {
     891            0 :                 wwmethod->lasterrstring =
     892            0 :                         _("implementation error: tar files can't have more than one open file");
     893            0 :                 return NULL;
     894              :         }
     895              : 
     896            0 :         tar_data->currentfile = pg_malloc0(sizeof(TarMethodFile));
     897            0 :         tar_data->currentfile->base.wwmethod = wwmethod;
     898              : 
     899            0 :         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            0 :         if (tarCreateHeader(tar_data->currentfile->header, tmppath, NULL, 0, S_IRUSR | S_IWUSR, 0, 0, time(NULL)) != TAR_OK)
     903              :         {
     904            0 :                 pg_free(tar_data->currentfile);
     905            0 :                 pg_free(tmppath);
     906            0 :                 tar_data->currentfile = NULL;
     907            0 :                 wwmethod->lasterrstring = _("could not create tar header");
     908            0 :                 return NULL;
     909              :         }
     910              : 
     911            0 :         pg_free(tmppath);
     912              : 
     913              : #ifdef HAVE_LIBZ
     914            0 :         if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
     915              :         {
     916              :                 /* Flush existing data */
     917            0 :                 if (!tar_write_compressed_data(tar_data, NULL, 0, true))
     918            0 :                         return NULL;
     919              : 
     920              :                 /* Turn off compression for header */
     921            0 :                 if (deflateParams(tar_data->zp, 0, Z_DEFAULT_STRATEGY) != Z_OK)
     922              :                 {
     923            0 :                         wwmethod->lasterrstring =
     924            0 :                                 _("could not change compression parameters");
     925            0 :                         return NULL;
     926              :                 }
     927            0 :         }
     928              : #endif
     929              : 
     930            0 :         tar_data->currentfile->ofs_start = lseek(tar_data->fd, 0, SEEK_CUR);
     931            0 :         if (tar_data->currentfile->ofs_start == -1)
     932              :         {
     933            0 :                 wwmethod->lasterrno = errno;
     934            0 :                 pg_free(tar_data->currentfile);
     935            0 :                 tar_data->currentfile = NULL;
     936            0 :                 return NULL;
     937              :         }
     938            0 :         tar_data->currentfile->base.currpos = 0;
     939              : 
     940            0 :         if (wwmethod->compression_algorithm == PG_COMPRESSION_NONE)
     941              :         {
     942            0 :                 errno = 0;
     943            0 :                 if (write(tar_data->fd, tar_data->currentfile->header,
     944            0 :                                   TAR_BLOCK_SIZE) != TAR_BLOCK_SIZE)
     945              :                 {
     946              :                         /* If write didn't set errno, assume problem is no disk space */
     947            0 :                         wwmethod->lasterrno = errno ? errno : ENOSPC;
     948            0 :                         pg_free(tar_data->currentfile);
     949            0 :                         tar_data->currentfile = NULL;
     950            0 :                         return NULL;
     951              :                 }
     952            0 :         }
     953              : #ifdef HAVE_LIBZ
     954            0 :         else if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
     955              :         {
     956              :                 /* Write header through the zlib APIs but with no compression */
     957            0 :                 if (!tar_write_compressed_data(tar_data, tar_data->currentfile->header,
     958              :                                                                            TAR_BLOCK_SIZE, true))
     959            0 :                         return NULL;
     960              : 
     961              :                 /* Re-enable compression for the rest of the file */
     962            0 :                 if (deflateParams(tar_data->zp, wwmethod->compression_level,
     963            0 :                                                   Z_DEFAULT_STRATEGY) != Z_OK)
     964              :                 {
     965            0 :                         wwmethod->lasterrstring = _("could not change compression parameters");
     966            0 :                         return NULL;
     967              :                 }
     968            0 :         }
     969              : #endif
     970              :         else
     971              :         {
     972              :                 /* not reachable */
     973            0 :                 Assert(false);
     974              :         }
     975              : 
     976            0 :         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            0 :         if (pad_to_size)
     983              :         {
     984            0 :                 tar_data->currentfile->pad_to_size = pad_to_size;
     985            0 :                 if (wwmethod->compression_algorithm == PG_COMPRESSION_NONE)
     986              :                 {
     987              :                         /* Uncompressed, so pad now */
     988            0 :                         if (!tar_write_padding_data(tar_data->currentfile, pad_to_size))
     989            0 :                                 return NULL;
     990              :                         /* Seek back to start */
     991            0 :                         if (lseek(tar_data->fd,
     992            0 :                                           tar_data->currentfile->ofs_start + TAR_BLOCK_SIZE,
     993            0 :                                           SEEK_SET) != tar_data->currentfile->ofs_start + TAR_BLOCK_SIZE)
     994              :                         {
     995            0 :                                 wwmethod->lasterrno = errno;
     996            0 :                                 return NULL;
     997              :                         }
     998              : 
     999            0 :                         tar_data->currentfile->base.currpos = 0;
    1000            0 :                 }
    1001            0 :         }
    1002              : 
    1003            0 :         return &tar_data->currentfile->base;
    1004            0 : }
    1005              : 
    1006              : static ssize_t
    1007            0 : tar_get_file_size(WalWriteMethod *wwmethod, const char *pathname)
    1008              : {
    1009            0 :         clear_error(wwmethod);
    1010              : 
    1011              :         /* Currently not used, so not supported */
    1012            0 :         wwmethod->lasterrno = ENOSYS;
    1013            0 :         return -1;
    1014              : }
    1015              : 
    1016              : static int
    1017            0 : tar_sync(Walfile *f)
    1018              : {
    1019            0 :         TarMethodData *tar_data = (TarMethodData *) f->wwmethod;
    1020            0 :         int                     r;
    1021              : 
    1022            0 :         Assert(f != NULL);
    1023            0 :         clear_error(f->wwmethod);
    1024              : 
    1025            0 :         if (!f->wwmethod->sync)
    1026            0 :                 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              :          */
    1032            0 :         if (f->wwmethod->compression_algorithm != PG_COMPRESSION_NONE)
    1033            0 :                 return 0;
    1034              : 
    1035            0 :         r = fsync(tar_data->fd);
    1036            0 :         if (r < 0)
    1037            0 :                 f->wwmethod->lasterrno = errno;
    1038            0 :         return r;
    1039            0 : }
    1040              : 
    1041              : static int
    1042            0 : tar_close(Walfile *f, WalCloseMethod method)
    1043              : {
    1044            0 :         ssize_t         filesize;
    1045            0 :         int                     padding;
    1046            0 :         TarMethodData *tar_data = (TarMethodData *) f->wwmethod;
    1047            0 :         TarMethodFile *tf = (TarMethodFile *) f;
    1048              : 
    1049            0 :         Assert(f != NULL);
    1050            0 :         clear_error(f->wwmethod);
    1051              : 
    1052            0 :         if (method == CLOSE_UNLINK)
    1053              :         {
    1054            0 :                 if (f->wwmethod->compression_algorithm != PG_COMPRESSION_NONE)
    1055              :                 {
    1056            0 :                         f->wwmethod->lasterrstring = _("unlink not supported with compression");
    1057            0 :                         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            0 :                 if (ftruncate(tar_data->fd, tf->ofs_start) != 0)
    1066              :                 {
    1067            0 :                         f->wwmethod->lasterrno = errno;
    1068            0 :                         return -1;
    1069              :                 }
    1070              : 
    1071            0 :                 pg_free(tf->base.pathname);
    1072            0 :                 pg_free(tf);
    1073            0 :                 tar_data->currentfile = NULL;
    1074              : 
    1075            0 :                 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            0 :         if (tf->pad_to_size)
    1084              :         {
    1085            0 :                 if (f->wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
    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            0 :                         size_t          sizeleft = tf->pad_to_size - tf->base.currpos;
    1092              : 
    1093            0 :                         if (sizeleft)
    1094              :                         {
    1095            0 :                                 if (!tar_write_padding_data(tf, sizeleft))
    1096            0 :                                         return -1;
    1097            0 :                         }
    1098            0 :                 }
    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            0 :                         tf->base.currpos = tf->pad_to_size;
    1106              :                 }
    1107            0 :         }
    1108              : 
    1109              :         /*
    1110              :          * Get the size of the file, and pad out to a multiple of the tar block
    1111              :          * size.
    1112              :          */
    1113            0 :         filesize = f->currpos;
    1114            0 :         padding = tarPaddingBytesRequired(filesize);
    1115            0 :         if (padding)
    1116              :         {
    1117            0 :                 char            zerobuf[TAR_BLOCK_SIZE] = {0};
    1118              : 
    1119            0 :                 if (tar_write(f, zerobuf, padding) != padding)
    1120            0 :                         return -1;
    1121            0 :         }
    1122              : 
    1123              : 
    1124              : #ifdef HAVE_LIBZ
    1125            0 :         if (f->wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
    1126              :         {
    1127              :                 /* Flush the current buffer */
    1128            0 :                 if (!tar_write_compressed_data(tar_data, NULL, 0, true))
    1129            0 :                         return -1;
    1130            0 :         }
    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              :          */
    1138            0 :         print_tar_number(&(tf->header[TAR_OFFSET_SIZE]), 12, filesize);
    1139              : 
    1140            0 :         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            0 :                 strlcpy(&(tf->header[TAR_OFFSET_NAME]), tf->base.pathname, 100);
    1147              : 
    1148            0 :         print_tar_number(&(tf->header[TAR_OFFSET_CHECKSUM]), 8,
    1149            0 :                                          tarChecksum(((TarMethodFile *) f)->header));
    1150            0 :         if (lseek(tar_data->fd, tf->ofs_start, SEEK_SET) != ((TarMethodFile *) f)->ofs_start)
    1151              :         {
    1152            0 :                 f->wwmethod->lasterrno = errno;
    1153            0 :                 return -1;
    1154              :         }
    1155            0 :         if (f->wwmethod->compression_algorithm == PG_COMPRESSION_NONE)
    1156              :         {
    1157            0 :                 errno = 0;
    1158            0 :                 if (write(tar_data->fd, tf->header, TAR_BLOCK_SIZE) != TAR_BLOCK_SIZE)
    1159              :                 {
    1160              :                         /* If write didn't set errno, assume problem is no disk space */
    1161            0 :                         f->wwmethod->lasterrno = errno ? errno : ENOSPC;
    1162            0 :                         return -1;
    1163              :                 }
    1164            0 :         }
    1165              : #ifdef HAVE_LIBZ
    1166            0 :         else if (f->wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
    1167              :         {
    1168              :                 /* Turn off compression */
    1169            0 :                 if (deflateParams(tar_data->zp, 0, Z_DEFAULT_STRATEGY) != Z_OK)
    1170              :                 {
    1171            0 :                         f->wwmethod->lasterrstring = _("could not change compression parameters");
    1172            0 :                         return -1;
    1173              :                 }
    1174              : 
    1175              :                 /* Overwrite the header, assuming the size will be the same */
    1176            0 :                 if (!tar_write_compressed_data(tar_data, tar_data->currentfile->header,
    1177              :                                                                            TAR_BLOCK_SIZE, true))
    1178            0 :                         return -1;
    1179              : 
    1180              :                 /* Turn compression back on */
    1181            0 :                 if (deflateParams(tar_data->zp, f->wwmethod->compression_level,
    1182            0 :                                                   Z_DEFAULT_STRATEGY) != Z_OK)
    1183              :                 {
    1184            0 :                         f->wwmethod->lasterrstring = _("could not change compression parameters");
    1185            0 :                         return -1;
    1186              :                 }
    1187            0 :         }
    1188              : #endif
    1189              :         else
    1190              :         {
    1191              :                 /* not reachable */
    1192            0 :                 Assert(false);
    1193              :         }
    1194              : 
    1195              :         /* Move file pointer back down to end, so we can write the next file */
    1196            0 :         if (lseek(tar_data->fd, 0, SEEK_END) < 0)
    1197              :         {
    1198            0 :                 f->wwmethod->lasterrno = errno;
    1199            0 :                 return -1;
    1200              :         }
    1201              : 
    1202              :         /* Always fsync on close, so the padding gets fsynced */
    1203            0 :         if (tar_sync(f) < 0)
    1204              :         {
    1205              :                 /* XXX this seems pretty bogus; why is only this case fatal? */
    1206            0 :                 pg_fatal("could not fsync file \"%s\": %s",
    1207              :                                  tf->base.pathname, GetLastWalMethodError(f->wwmethod));
    1208            0 :         }
    1209              : 
    1210              :         /* Clean up and done */
    1211            0 :         pg_free(tf->base.pathname);
    1212            0 :         pg_free(tf);
    1213            0 :         tar_data->currentfile = NULL;
    1214              : 
    1215            0 :         return 0;
    1216            0 : }
    1217              : 
    1218              : static bool
    1219            0 : tar_existsfile(WalWriteMethod *wwmethod, const char *pathname)
    1220              : {
    1221            0 :         clear_error(wwmethod);
    1222              :         /* We only deal with new tarfiles, so nothing externally created exists */
    1223            0 :         return false;
    1224              : }
    1225              : 
    1226              : static bool
    1227            0 : tar_finish(WalWriteMethod *wwmethod)
    1228              : {
    1229            0 :         TarMethodData *tar_data = (TarMethodData *) wwmethod;
    1230            0 :         char            zerobuf[1024] = {0};
    1231              : 
    1232            0 :         clear_error(wwmethod);
    1233              : 
    1234            0 :         if (tar_data->currentfile)
    1235              :         {
    1236            0 :                 if (tar_close(&tar_data->currentfile->base, CLOSE_NORMAL) != 0)
    1237            0 :                         return false;
    1238            0 :         }
    1239              : 
    1240              :         /* A tarfile always ends with two empty blocks */
    1241            0 :         if (wwmethod->compression_algorithm == PG_COMPRESSION_NONE)
    1242              :         {
    1243            0 :                 errno = 0;
    1244            0 :                 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            0 :                         wwmethod->lasterrno = errno ? errno : ENOSPC;
    1248            0 :                         return false;
    1249              :                 }
    1250            0 :         }
    1251              : #ifdef HAVE_LIBZ
    1252            0 :         else if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
    1253              :         {
    1254            0 :                 if (!tar_write_compressed_data(tar_data, zerobuf, sizeof(zerobuf),
    1255              :                                                                            false))
    1256            0 :                         return false;
    1257              : 
    1258              :                 /* Also flush all data to make sure the gzip stream is finished */
    1259            0 :                 tar_data->zp->next_in = NULL;
    1260            0 :                 tar_data->zp->avail_in = 0;
    1261            0 :                 while (true)
    1262              :                 {
    1263            0 :                         int                     r;
    1264              : 
    1265            0 :                         r = deflate(tar_data->zp, Z_FINISH);
    1266              : 
    1267            0 :                         if (r == Z_STREAM_ERROR)
    1268              :                         {
    1269            0 :                                 wwmethod->lasterrstring = _("could not compress data");
    1270            0 :                                 return false;
    1271              :                         }
    1272            0 :                         if (tar_data->zp->avail_out < ZLIB_OUT_SIZE)
    1273              :                         {
    1274            0 :                                 size_t          len = ZLIB_OUT_SIZE - tar_data->zp->avail_out;
    1275              : 
    1276            0 :                                 errno = 0;
    1277            0 :                                 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            0 :                                         wwmethod->lasterrno = errno ? errno : ENOSPC;
    1284            0 :                                         return false;
    1285              :                                 }
    1286            0 :                         }
    1287            0 :                         if (r == Z_STREAM_END)
    1288            0 :                                 break;
    1289            0 :                 }
    1290              : 
    1291            0 :                 if (deflateEnd(tar_data->zp) != Z_OK)
    1292              :                 {
    1293            0 :                         wwmethod->lasterrstring = _("could not close compression stream");
    1294            0 :                         return false;
    1295              :                 }
    1296            0 :         }
    1297              : #endif
    1298              :         else
    1299              :         {
    1300              :                 /* not reachable */
    1301            0 :                 Assert(false);
    1302              :         }
    1303              : 
    1304              :         /* sync the empty blocks as well, since they're after the last file */
    1305            0 :         if (wwmethod->sync)
    1306              :         {
    1307            0 :                 if (fsync(tar_data->fd) != 0)
    1308              :                 {
    1309            0 :                         wwmethod->lasterrno = errno;
    1310            0 :                         return false;
    1311              :                 }
    1312            0 :         }
    1313              : 
    1314            0 :         if (close(tar_data->fd) != 0)
    1315              :         {
    1316            0 :                 wwmethod->lasterrno = errno;
    1317            0 :                 return false;
    1318              :         }
    1319              : 
    1320            0 :         tar_data->fd = -1;
    1321              : 
    1322            0 :         if (wwmethod->sync)
    1323              :         {
    1324            0 :                 if (fsync_fname(tar_data->tarfilename, false) != 0 ||
    1325            0 :                         fsync_parent_path(tar_data->tarfilename) != 0)
    1326              :                 {
    1327            0 :                         wwmethod->lasterrno = errno;
    1328            0 :                         return false;
    1329              :                 }
    1330            0 :         }
    1331              : 
    1332            0 :         return true;
    1333            0 : }
    1334              : 
    1335              : static void
    1336            0 : tar_free(WalWriteMethod *wwmethod)
    1337              : {
    1338            0 :         TarMethodData *tar_data = (TarMethodData *) wwmethod;
    1339              : 
    1340            0 :         pg_free(tar_data->tarfilename);
    1341              : #ifdef HAVE_LIBZ
    1342            0 :         if (wwmethod->compression_algorithm == PG_COMPRESSION_GZIP)
    1343            0 :                 pg_free(tar_data->zlibOut);
    1344              : #endif
    1345            0 :         pg_free(wwmethod);
    1346            0 : }
    1347              : 
    1348              : /*
    1349              :  * The argument compression_algorithm is currently ignored. It is in place for
    1350              :  * symmetry with CreateWalDirectoryMethod which uses it for distinguishing
    1351              :  * between the different compression methods. CreateWalTarMethod and its family
    1352              :  * of functions handle only zlib compression.
    1353              :  */
    1354              : WalWriteMethod *
    1355            0 : CreateWalTarMethod(const char *tarbase,
    1356              :                                    pg_compress_algorithm compression_algorithm,
    1357              :                                    int compression_level, bool sync)
    1358              : {
    1359            0 :         TarMethodData *wwmethod;
    1360            0 :         const char *suffix = (compression_algorithm == PG_COMPRESSION_GZIP) ?
    1361              :                 ".tar.gz" : ".tar";
    1362              : 
    1363            0 :         wwmethod = pg_malloc0(sizeof(TarMethodData));
    1364            0 :         *((const WalWriteMethodOps **) &wwmethod->base.ops) =
    1365              :                 &WalTarMethodOps;
    1366            0 :         wwmethod->base.compression_algorithm = compression_algorithm;
    1367            0 :         wwmethod->base.compression_level = compression_level;
    1368            0 :         wwmethod->base.sync = sync;
    1369            0 :         clear_error(&wwmethod->base);
    1370              : 
    1371            0 :         wwmethod->tarfilename = pg_malloc0(strlen(tarbase) + strlen(suffix) + 1);
    1372            0 :         sprintf(wwmethod->tarfilename, "%s%s", tarbase, suffix);
    1373            0 :         wwmethod->fd = -1;
    1374              : #ifdef HAVE_LIBZ
    1375            0 :         if (compression_algorithm == PG_COMPRESSION_GZIP)
    1376            0 :                 wwmethod->zlibOut = (char *) pg_malloc(ZLIB_OUT_SIZE + 1);
    1377              : #endif
    1378              : 
    1379            0 :         return &wwmethod->base;
    1380            0 : }
    1381              : 
    1382              : const char *
    1383            0 : GetLastWalMethodError(WalWriteMethod *wwmethod)
    1384              : {
    1385            0 :         if (wwmethod->lasterrstring)
    1386            0 :                 return wwmethod->lasterrstring;
    1387            0 :         return strerror(wwmethod->lasterrno);
    1388            0 : }
        

Generated by: LCOV version 2.3.2-1