LCOV - differential code coverage report
Current view: top level - src/bin/pg_basebackup - walmethods.c (source / functions) Coverage Total Hit UNC UBC GNC CBC DCB
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 62.4 % 529 330 199 6 324 5
Current Date: 2024-04-14 14:21:10 Functions: 82.6 % 23 19 4 4 15 1
Baseline: 16@8cea358b128 Branches: 52.6 % 308 162 1 145 1 161
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (240..) days: 62.4 % 529 330 199 6 324
Function coverage date bins:
(240..) days: 82.6 % 23 19 4 4 15
Branch coverage date bins:
(240..) days: 52.6 % 308 162 1 145 1 161

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

Generated by: LCOV version 2.1-beta2-3-g6141622