LCOV - code coverage report
Current view: top level - src/backend/replication - basebackup.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 403 527 76.5 %
Date: 2019-09-19 16:06:56 Functions: 14 16 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * basebackup.c
       4             :  *    code for taking a base backup and streaming it to a standby
       5             :  *
       6             :  * Portions Copyright (c) 2010-2019, PostgreSQL Global Development Group
       7             :  *
       8             :  * IDENTIFICATION
       9             :  *    src/backend/replication/basebackup.c
      10             :  *
      11             :  *-------------------------------------------------------------------------
      12             :  */
      13             : #include "postgres.h"
      14             : 
      15             : #include <sys/stat.h>
      16             : #include <unistd.h>
      17             : #include <time.h>
      18             : 
      19             : #include "access/xlog_internal.h" /* for pg_start/stop_backup */
      20             : #include "catalog/pg_type.h"
      21             : #include "common/file_perm.h"
      22             : #include "lib/stringinfo.h"
      23             : #include "libpq/libpq.h"
      24             : #include "libpq/pqformat.h"
      25             : #include "miscadmin.h"
      26             : #include "nodes/pg_list.h"
      27             : #include "pgtar.h"
      28             : #include "pgstat.h"
      29             : #include "port.h"
      30             : #include "postmaster/syslogger.h"
      31             : #include "replication/basebackup.h"
      32             : #include "replication/walsender.h"
      33             : #include "replication/walsender_private.h"
      34             : #include "storage/bufpage.h"
      35             : #include "storage/checksum.h"
      36             : #include "storage/dsm_impl.h"
      37             : #include "storage/fd.h"
      38             : #include "storage/ipc.h"
      39             : #include "storage/reinit.h"
      40             : #include "utils/builtins.h"
      41             : #include "utils/ps_status.h"
      42             : #include "utils/relcache.h"
      43             : #include "utils/timestamp.h"
      44             : 
      45             : 
      46             : typedef struct
      47             : {
      48             :     const char *label;
      49             :     bool        progress;
      50             :     bool        fastcheckpoint;
      51             :     bool        nowait;
      52             :     bool        includewal;
      53             :     uint32      maxrate;
      54             :     bool        sendtblspcmapfile;
      55             : } basebackup_options;
      56             : 
      57             : 
      58             : static int64 sendDir(const char *path, int basepathlen, bool sizeonly,
      59             :                      List *tablespaces, bool sendtblspclinks);
      60             : static bool sendFile(const char *readfilename, const char *tarfilename,
      61             :                      struct stat *statbuf, bool missing_ok, Oid dboid);
      62             : static void sendFileWithContent(const char *filename, const char *content);
      63             : static int64 _tarWriteHeader(const char *filename, const char *linktarget,
      64             :                              struct stat *statbuf, bool sizeonly);
      65             : static int64 _tarWriteDir(const char *pathbuf, int basepathlen, struct stat *statbuf,
      66             :                           bool sizeonly);
      67             : static void send_int8_string(StringInfoData *buf, int64 intval);
      68             : static void SendBackupHeader(List *tablespaces);
      69             : static void base_backup_cleanup(int code, Datum arg);
      70             : static void perform_base_backup(basebackup_options *opt);
      71             : static void parse_basebackup_options(List *options, basebackup_options *opt);
      72             : static void SendXlogRecPtrResult(XLogRecPtr ptr, TimeLineID tli);
      73             : static int  compareWalFileNames(const ListCell *a, const ListCell *b);
      74             : static void throttle(size_t increment);
      75             : static bool is_checksummed_file(const char *fullpath, const char *filename);
      76             : 
      77             : /* Was the backup currently in-progress initiated in recovery mode? */
      78             : static bool backup_started_in_recovery = false;
      79             : 
      80             : /* Relative path of temporary statistics directory */
      81             : static char *statrelpath = NULL;
      82             : 
      83             : /*
      84             :  * Size of each block sent into the tar stream for larger files.
      85             :  */
      86             : #define TAR_SEND_SIZE 32768
      87             : 
      88             : /*
      89             :  * How frequently to throttle, as a fraction of the specified rate-second.
      90             :  */
      91             : #define THROTTLING_FREQUENCY    8
      92             : 
      93             : /*
      94             :  * Checks whether we encountered any error in fread().  fread() doesn't give
      95             :  * any clue what has happened, so we check with ferror().  Also, neither
      96             :  * fread() nor ferror() set errno, so we just throw a generic error.
      97             :  */
      98             : #define CHECK_FREAD_ERROR(fp, filename) \
      99             : do { \
     100             :     if (ferror(fp)) \
     101             :         ereport(ERROR, \
     102             :                 (errmsg("could not read from file \"%s\"", filename))); \
     103             : } while (0)
     104             : 
     105             : /* The actual number of bytes, transfer of which may cause sleep. */
     106             : static uint64 throttling_sample;
     107             : 
     108             : /* Amount of data already transferred but not yet throttled.  */
     109             : static int64 throttling_counter;
     110             : 
     111             : /* The minimum time required to transfer throttling_sample bytes. */
     112             : static TimeOffset elapsed_min_unit;
     113             : 
     114             : /* The last check of the transfer rate. */
     115             : static TimestampTz throttled_last;
     116             : 
     117             : /* The starting XLOG position of the base backup. */
     118             : static XLogRecPtr startptr;
     119             : 
     120             : /* Total number of checksum failures during base backup. */
     121             : static long long int total_checksum_failures;
     122             : 
     123             : /* Do not verify checksums. */
     124             : static bool noverify_checksums = false;
     125             : 
     126             : /*
     127             :  * The contents of these directories are removed or recreated during server
     128             :  * start so they are not included in backups.  The directories themselves are
     129             :  * kept and included as empty to preserve access permissions.
     130             :  *
     131             :  * Note: this list should be kept in sync with the filter lists in pg_rewind's
     132             :  * filemap.c.
     133             :  */
     134             : static const char *const excludeDirContents[] =
     135             : {
     136             :     /*
     137             :      * Skip temporary statistics files. PG_STAT_TMP_DIR must be skipped even
     138             :      * when stats_temp_directory is set because PGSS_TEXT_FILE is always
     139             :      * created there.
     140             :      */
     141             :     PG_STAT_TMP_DIR,
     142             : 
     143             :     /*
     144             :      * It is generally not useful to backup the contents of this directory
     145             :      * even if the intention is to restore to another master. See backup.sgml
     146             :      * for a more detailed description.
     147             :      */
     148             :     "pg_replslot",
     149             : 
     150             :     /* Contents removed on startup, see dsm_cleanup_for_mmap(). */
     151             :     PG_DYNSHMEM_DIR,
     152             : 
     153             :     /* Contents removed on startup, see AsyncShmemInit(). */
     154             :     "pg_notify",
     155             : 
     156             :     /*
     157             :      * Old contents are loaded for possible debugging but are not required for
     158             :      * normal operation, see OldSerXidInit().
     159             :      */
     160             :     "pg_serial",
     161             : 
     162             :     /* Contents removed on startup, see DeleteAllExportedSnapshotFiles(). */
     163             :     "pg_snapshots",
     164             : 
     165             :     /* Contents zeroed on startup, see StartupSUBTRANS(). */
     166             :     "pg_subtrans",
     167             : 
     168             :     /* end of list */
     169             :     NULL
     170             : };
     171             : 
     172             : /*
     173             :  * List of files excluded from backups.
     174             :  */
     175             : static const char *const excludeFiles[] =
     176             : {
     177             :     /* Skip auto conf temporary file. */
     178             :     PG_AUTOCONF_FILENAME ".tmp",
     179             : 
     180             :     /* Skip current log file temporary file */
     181             :     LOG_METAINFO_DATAFILE_TMP,
     182             : 
     183             :     /* Skip relation cache because it is rebuilt on startup */
     184             :     RELCACHE_INIT_FILENAME,
     185             : 
     186             :     /*
     187             :      * If there's a backup_label or tablespace_map file, it belongs to a
     188             :      * backup started by the user with pg_start_backup().  It is *not* correct
     189             :      * for this backup.  Our backup_label/tablespace_map is injected into the
     190             :      * tar separately.
     191             :      */
     192             :     BACKUP_LABEL_FILE,
     193             :     TABLESPACE_MAP,
     194             : 
     195             :     "postmaster.pid",
     196             :     "postmaster.opts",
     197             : 
     198             :     /* end of list */
     199             :     NULL
     200             : };
     201             : 
     202             : /*
     203             :  * List of files excluded from checksum validation.
     204             :  *
     205             :  * Note: this list should be kept in sync with what pg_checksums.c
     206             :  * includes.
     207             :  */
     208             : static const char *const noChecksumFiles[] = {
     209             :     "pg_control",
     210             :     "pg_filenode.map",
     211             :     "pg_internal.init",
     212             :     "PG_VERSION",
     213             : #ifdef EXEC_BACKEND
     214             :     "config_exec_params",
     215             :     "config_exec_params.new",
     216             : #endif
     217             :     NULL,
     218             : };
     219             : 
     220             : 
     221             : /*
     222             :  * Called when ERROR or FATAL happens in perform_base_backup() after
     223             :  * we have started the backup - make sure we end it!
     224             :  */
     225             : static void
     226           6 : base_backup_cleanup(int code, Datum arg)
     227             : {
     228           6 :     do_pg_abort_backup();
     229           6 : }
     230             : 
     231             : /*
     232             :  * Actually do a base backup for the specified tablespaces.
     233             :  *
     234             :  * This is split out mainly to avoid complaints about "variable might be
     235             :  * clobbered by longjmp" from stupider versions of gcc.
     236             :  */
     237             : static void
     238          98 : perform_base_backup(basebackup_options *opt)
     239             : {
     240             :     TimeLineID  starttli;
     241             :     XLogRecPtr  endptr;
     242             :     TimeLineID  endtli;
     243             :     StringInfo  labelfile;
     244          98 :     StringInfo  tblspc_map_file = NULL;
     245             :     int         datadirpathlen;
     246          98 :     List       *tablespaces = NIL;
     247             : 
     248          98 :     datadirpathlen = strlen(DataDir);
     249             : 
     250          98 :     backup_started_in_recovery = RecoveryInProgress();
     251             : 
     252          98 :     labelfile = makeStringInfo();
     253          98 :     tblspc_map_file = makeStringInfo();
     254             : 
     255          98 :     total_checksum_failures = 0;
     256             : 
     257         196 :     startptr = do_pg_start_backup(opt->label, opt->fastcheckpoint, &starttli,
     258             :                                   labelfile, &tablespaces,
     259             :                                   tblspc_map_file,
     260         196 :                                   opt->progress, opt->sendtblspcmapfile);
     261             : 
     262             :     /*
     263             :      * Once do_pg_start_backup has been called, ensure that any failure causes
     264             :      * us to abort the backup so we don't "leak" a backup counter. For this
     265             :      * reason, *all* functionality between do_pg_start_backup() and the end of
     266             :      * do_pg_stop_backup() should be inside the error cleanup block!
     267             :      */
     268             : 
     269          98 :     PG_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0);
     270             :     {
     271             :         ListCell   *lc;
     272             :         tablespaceinfo *ti;
     273             : 
     274          98 :         SendXlogRecPtrResult(startptr, starttli);
     275             : 
     276             :         /*
     277             :          * Calculate the relative path of temporary statistics directory in
     278             :          * order to skip the files which are located in that directory later.
     279             :          */
     280          98 :         if (is_absolute_path(pgstat_stat_directory) &&
     281           0 :             strncmp(pgstat_stat_directory, DataDir, datadirpathlen) == 0)
     282           0 :             statrelpath = psprintf("./%s", pgstat_stat_directory + datadirpathlen + 1);
     283          98 :         else if (strncmp(pgstat_stat_directory, "./", 2) != 0)
     284          98 :             statrelpath = psprintf("./%s", pgstat_stat_directory);
     285             :         else
     286           0 :             statrelpath = pgstat_stat_directory;
     287             : 
     288             :         /* Add a node for the base directory at the end */
     289          98 :         ti = palloc0(sizeof(tablespaceinfo));
     290          98 :         ti->size = opt->progress ? sendDir(".", 1, true, tablespaces, true) : -1;
     291          98 :         tablespaces = lappend(tablespaces, ti);
     292             : 
     293             :         /* Send tablespace header */
     294          98 :         SendBackupHeader(tablespaces);
     295             : 
     296             :         /* Setup and activate network throttling, if client requested it */
     297          98 :         if (opt->maxrate > 0)
     298             :         {
     299           0 :             throttling_sample =
     300           0 :                 (int64) opt->maxrate * (int64) 1024 / THROTTLING_FREQUENCY;
     301             : 
     302             :             /*
     303             :              * The minimum amount of time for throttling_sample bytes to be
     304             :              * transferred.
     305             :              */
     306           0 :             elapsed_min_unit = USECS_PER_SEC / THROTTLING_FREQUENCY;
     307             : 
     308             :             /* Enable throttling. */
     309           0 :             throttling_counter = 0;
     310             : 
     311             :             /* The 'real data' starts now (header was ignored). */
     312           0 :             throttled_last = GetCurrentTimestamp();
     313             :         }
     314             :         else
     315             :         {
     316             :             /* Disable throttling. */
     317          98 :             throttling_counter = -1;
     318             :         }
     319             : 
     320             :         /* Send off our tablespaces one by one */
     321         200 :         foreach(lc, tablespaces)
     322             :         {
     323         108 :             tablespaceinfo *ti = (tablespaceinfo *) lfirst(lc);
     324             :             StringInfoData buf;
     325             : 
     326             :             /* Send CopyOutResponse message */
     327         108 :             pq_beginmessage(&buf, 'H');
     328         108 :             pq_sendbyte(&buf, 0);   /* overall format */
     329         108 :             pq_sendint16(&buf, 0);  /* natts */
     330         108 :             pq_endmessage(&buf);
     331             : 
     332         108 :             if (ti->path == NULL)
     333             :             {
     334             :                 struct stat statbuf;
     335             : 
     336             :                 /* In the main tar, include the backup_label first... */
     337          98 :                 sendFileWithContent(BACKUP_LABEL_FILE, labelfile->data);
     338             : 
     339             :                 /*
     340             :                  * Send tablespace_map file if required and then the bulk of
     341             :                  * the files.
     342             :                  */
     343          98 :                 if (tblspc_map_file && opt->sendtblspcmapfile)
     344             :                 {
     345          10 :                     sendFileWithContent(TABLESPACE_MAP, tblspc_map_file->data);
     346          10 :                     sendDir(".", 1, false, tablespaces, false);
     347             :                 }
     348             :                 else
     349          88 :                     sendDir(".", 1, false, tablespaces, true);
     350             : 
     351             :                 /* ... and pg_control after everything else. */
     352          92 :                 if (lstat(XLOG_CONTROL_FILE, &statbuf) != 0)
     353           0 :                     ereport(ERROR,
     354             :                             (errcode_for_file_access(),
     355             :                              errmsg("could not stat file \"%s\": %m",
     356             :                                     XLOG_CONTROL_FILE)));
     357          92 :                 sendFile(XLOG_CONTROL_FILE, XLOG_CONTROL_FILE, &statbuf, false, InvalidOid);
     358             :             }
     359             :             else
     360          10 :                 sendTablespace(ti->path, false);
     361             : 
     362             :             /*
     363             :              * If we're including WAL, and this is the main data directory we
     364             :              * don't terminate the tar stream here. Instead, we will append
     365             :              * the xlog files below and terminate it then. This is safe since
     366             :              * the main data directory is always sent *last*.
     367             :              */
     368         102 :             if (opt->includewal && ti->path == NULL)
     369             :             {
     370             :                 Assert(lnext(tablespaces, lc) == NULL);
     371             :             }
     372             :             else
     373         100 :                 pq_putemptymessage('c');    /* CopyDone */
     374             :         }
     375             : 
     376          92 :         endptr = do_pg_stop_backup(labelfile->data, !opt->nowait, &endtli);
     377             :     }
     378          92 :     PG_END_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0);
     379             : 
     380             : 
     381          92 :     if (opt->includewal)
     382             :     {
     383             :         /*
     384             :          * We've left the last tar file "open", so we can now append the
     385             :          * required WAL files to it.
     386             :          */
     387             :         char        pathbuf[MAXPGPATH];
     388             :         XLogSegNo   segno;
     389             :         XLogSegNo   startsegno;
     390             :         XLogSegNo   endsegno;
     391             :         struct stat statbuf;
     392           2 :         List       *historyFileList = NIL;
     393           2 :         List       *walFileList = NIL;
     394             :         char        firstoff[MAXFNAMELEN];
     395             :         char        lastoff[MAXFNAMELEN];
     396             :         DIR        *dir;
     397             :         struct dirent *de;
     398             :         ListCell   *lc;
     399             :         TimeLineID  tli;
     400             : 
     401             :         /*
     402             :          * I'd rather not worry about timelines here, so scan pg_wal and
     403             :          * include all WAL files in the range between 'startptr' and 'endptr',
     404             :          * regardless of the timeline the file is stamped with. If there are
     405             :          * some spurious WAL files belonging to timelines that don't belong in
     406             :          * this server's history, they will be included too. Normally there
     407             :          * shouldn't be such files, but if there are, there's little harm in
     408             :          * including them.
     409             :          */
     410           2 :         XLByteToSeg(startptr, startsegno, wal_segment_size);
     411           2 :         XLogFileName(firstoff, ThisTimeLineID, startsegno, wal_segment_size);
     412           2 :         XLByteToPrevSeg(endptr, endsegno, wal_segment_size);
     413           2 :         XLogFileName(lastoff, ThisTimeLineID, endsegno, wal_segment_size);
     414             : 
     415           2 :         dir = AllocateDir("pg_wal");
     416          16 :         while ((de = ReadDir(dir, "pg_wal")) != NULL)
     417             :         {
     418             :             /* Does it look like a WAL segment, and is it in the range? */
     419          18 :             if (IsXLogFileName(de->d_name) &&
     420          12 :                 strcmp(de->d_name + 8, firstoff + 8) >= 0 &&
     421           6 :                 strcmp(de->d_name + 8, lastoff + 8) <= 0)
     422             :             {
     423           2 :                 walFileList = lappend(walFileList, pstrdup(de->d_name));
     424             :             }
     425             :             /* Does it look like a timeline history file? */
     426          10 :             else if (IsTLHistoryFileName(de->d_name))
     427             :             {
     428           0 :                 historyFileList = lappend(historyFileList, pstrdup(de->d_name));
     429             :             }
     430             :         }
     431           2 :         FreeDir(dir);
     432             : 
     433             :         /*
     434             :          * Before we go any further, check that none of the WAL segments we
     435             :          * need were removed.
     436             :          */
     437           2 :         CheckXLogRemoved(startsegno, ThisTimeLineID);
     438             : 
     439             :         /*
     440             :          * Sort the WAL filenames.  We want to send the files in order from
     441             :          * oldest to newest, to reduce the chance that a file is recycled
     442             :          * before we get a chance to send it over.
     443             :          */
     444           2 :         list_sort(walFileList, compareWalFileNames);
     445             : 
     446             :         /*
     447             :          * There must be at least one xlog file in the pg_wal directory, since
     448             :          * we are doing backup-including-xlog.
     449             :          */
     450           2 :         if (walFileList == NIL)
     451           0 :             ereport(ERROR,
     452             :                     (errmsg("could not find any WAL files")));
     453             : 
     454             :         /*
     455             :          * Sanity check: the first and last segment should cover startptr and
     456             :          * endptr, with no gaps in between.
     457             :          */
     458           2 :         XLogFromFileName((char *) linitial(walFileList),
     459             :                          &tli, &segno, wal_segment_size);
     460           2 :         if (segno != startsegno)
     461             :         {
     462             :             char        startfname[MAXFNAMELEN];
     463             : 
     464           0 :             XLogFileName(startfname, ThisTimeLineID, startsegno,
     465             :                          wal_segment_size);
     466           0 :             ereport(ERROR,
     467             :                     (errmsg("could not find WAL file \"%s\"", startfname)));
     468             :         }
     469           4 :         foreach(lc, walFileList)
     470             :         {
     471           2 :             char       *walFileName = (char *) lfirst(lc);
     472           2 :             XLogSegNo   currsegno = segno;
     473           2 :             XLogSegNo   nextsegno = segno + 1;
     474             : 
     475           2 :             XLogFromFileName(walFileName, &tli, &segno, wal_segment_size);
     476           2 :             if (!(nextsegno == segno || currsegno == segno))
     477             :             {
     478             :                 char        nextfname[MAXFNAMELEN];
     479             : 
     480           0 :                 XLogFileName(nextfname, ThisTimeLineID, nextsegno,
     481             :                              wal_segment_size);
     482           0 :                 ereport(ERROR,
     483             :                         (errmsg("could not find WAL file \"%s\"", nextfname)));
     484             :             }
     485             :         }
     486           2 :         if (segno != endsegno)
     487             :         {
     488             :             char        endfname[MAXFNAMELEN];
     489             : 
     490           0 :             XLogFileName(endfname, ThisTimeLineID, endsegno, wal_segment_size);
     491           0 :             ereport(ERROR,
     492             :                     (errmsg("could not find WAL file \"%s\"", endfname)));
     493             :         }
     494             : 
     495             :         /* Ok, we have everything we need. Send the WAL files. */
     496           4 :         foreach(lc, walFileList)
     497             :         {
     498           2 :             char       *walFileName = (char *) lfirst(lc);
     499             :             FILE       *fp;
     500             :             char        buf[TAR_SEND_SIZE];
     501             :             size_t      cnt;
     502           2 :             pgoff_t     len = 0;
     503             : 
     504           2 :             snprintf(pathbuf, MAXPGPATH, XLOGDIR "/%s", walFileName);
     505           2 :             XLogFromFileName(walFileName, &tli, &segno, wal_segment_size);
     506             : 
     507           2 :             fp = AllocateFile(pathbuf, "rb");
     508           2 :             if (fp == NULL)
     509             :             {
     510           0 :                 int         save_errno = errno;
     511             : 
     512             :                 /*
     513             :                  * Most likely reason for this is that the file was already
     514             :                  * removed by a checkpoint, so check for that to get a better
     515             :                  * error message.
     516             :                  */
     517           0 :                 CheckXLogRemoved(segno, tli);
     518             : 
     519           0 :                 errno = save_errno;
     520           0 :                 ereport(ERROR,
     521             :                         (errcode_for_file_access(),
     522             :                          errmsg("could not open file \"%s\": %m", pathbuf)));
     523             :             }
     524             : 
     525           2 :             if (fstat(fileno(fp), &statbuf) != 0)
     526           0 :                 ereport(ERROR,
     527             :                         (errcode_for_file_access(),
     528             :                          errmsg("could not stat file \"%s\": %m",
     529             :                                 pathbuf)));
     530           2 :             if (statbuf.st_size != wal_segment_size)
     531             :             {
     532           0 :                 CheckXLogRemoved(segno, tli);
     533           0 :                 ereport(ERROR,
     534             :                         (errcode_for_file_access(),
     535             :                          errmsg("unexpected WAL file size \"%s\"", walFileName)));
     536             :             }
     537             : 
     538             :             /* send the WAL file itself */
     539           2 :             _tarWriteHeader(pathbuf, NULL, &statbuf, false);
     540             : 
     541        1026 :             while ((cnt = fread(buf, 1,
     542        1024 :                                 Min(sizeof(buf), wal_segment_size - len),
     543             :                                 fp)) > 0)
     544             :             {
     545        1024 :                 CheckXLogRemoved(segno, tli);
     546             :                 /* Send the chunk as a CopyData message */
     547        1024 :                 if (pq_putmessage('d', buf, cnt))
     548           0 :                     ereport(ERROR,
     549             :                             (errmsg("base backup could not send data, aborting backup")));
     550             : 
     551        1024 :                 len += cnt;
     552        1024 :                 throttle(cnt);
     553             : 
     554        1024 :                 if (len == wal_segment_size)
     555           2 :                     break;
     556             :             }
     557             : 
     558           2 :             CHECK_FREAD_ERROR(fp, pathbuf);
     559             : 
     560           2 :             if (len != wal_segment_size)
     561             :             {
     562           0 :                 CheckXLogRemoved(segno, tli);
     563           0 :                 ereport(ERROR,
     564             :                         (errcode_for_file_access(),
     565             :                          errmsg("unexpected WAL file size \"%s\"", walFileName)));
     566             :             }
     567             : 
     568             :             /* wal_segment_size is a multiple of 512, so no need for padding */
     569             : 
     570           2 :             FreeFile(fp);
     571             : 
     572             :             /*
     573             :              * Mark file as archived, otherwise files can get archived again
     574             :              * after promotion of a new node. This is in line with
     575             :              * walreceiver.c always doing an XLogArchiveForceDone() after a
     576             :              * complete segment.
     577             :              */
     578           2 :             StatusFilePath(pathbuf, walFileName, ".done");
     579           2 :             sendFileWithContent(pathbuf, "");
     580             :         }
     581             : 
     582             :         /*
     583             :          * Send timeline history files too. Only the latest timeline history
     584             :          * file is required for recovery, and even that only if there happens
     585             :          * to be a timeline switch in the first WAL segment that contains the
     586             :          * checkpoint record, or if we're taking a base backup from a standby
     587             :          * server and the target timeline changes while the backup is taken.
     588             :          * But they are small and highly useful for debugging purposes, so
     589             :          * better include them all, always.
     590             :          */
     591           2 :         foreach(lc, historyFileList)
     592             :         {
     593           0 :             char       *fname = lfirst(lc);
     594             : 
     595           0 :             snprintf(pathbuf, MAXPGPATH, XLOGDIR "/%s", fname);
     596             : 
     597           0 :             if (lstat(pathbuf, &statbuf) != 0)
     598           0 :                 ereport(ERROR,
     599             :                         (errcode_for_file_access(),
     600             :                          errmsg("could not stat file \"%s\": %m", pathbuf)));
     601             : 
     602           0 :             sendFile(pathbuf, pathbuf, &statbuf, false, InvalidOid);
     603             : 
     604             :             /* unconditionally mark file as archived */
     605           0 :             StatusFilePath(pathbuf, fname, ".done");
     606           0 :             sendFileWithContent(pathbuf, "");
     607             :         }
     608             : 
     609             :         /* Send CopyDone message for the last tar file */
     610           2 :         pq_putemptymessage('c');
     611             :     }
     612          92 :     SendXlogRecPtrResult(endptr, endtli);
     613             : 
     614          92 :     if (total_checksum_failures)
     615             :     {
     616           6 :         if (total_checksum_failures > 1)
     617           4 :             ereport(WARNING,
     618             :                     (errmsg("%lld total checksum verification failures", total_checksum_failures)));
     619             : 
     620           6 :         ereport(ERROR,
     621             :                 (errcode(ERRCODE_DATA_CORRUPTED),
     622             :                  errmsg("checksum verification failure during base backup")));
     623             :     }
     624             : 
     625          86 : }
     626             : 
     627             : /*
     628             :  * list_sort comparison function, to compare log/seg portion of WAL segment
     629             :  * filenames, ignoring the timeline portion.
     630             :  */
     631             : static int
     632           0 : compareWalFileNames(const ListCell *a, const ListCell *b)
     633             : {
     634           0 :     char       *fna = (char *) lfirst(a);
     635           0 :     char       *fnb = (char *) lfirst(b);
     636             : 
     637           0 :     return strcmp(fna + 8, fnb + 8);
     638             : }
     639             : 
     640             : /*
     641             :  * Parse the base backup options passed down by the parser
     642             :  */
     643             : static void
     644          98 : parse_basebackup_options(List *options, basebackup_options *opt)
     645             : {
     646             :     ListCell   *lopt;
     647          98 :     bool        o_label = false;
     648          98 :     bool        o_progress = false;
     649          98 :     bool        o_fast = false;
     650          98 :     bool        o_nowait = false;
     651          98 :     bool        o_wal = false;
     652          98 :     bool        o_maxrate = false;
     653          98 :     bool        o_tablespace_map = false;
     654          98 :     bool        o_noverify_checksums = false;
     655             : 
     656          98 :     MemSet(opt, 0, sizeof(*opt));
     657         306 :     foreach(lopt, options)
     658             :     {
     659         208 :         DefElem    *defel = (DefElem *) lfirst(lopt);
     660             : 
     661         208 :         if (strcmp(defel->defname, "label") == 0)
     662             :         {
     663          98 :             if (o_label)
     664           0 :                 ereport(ERROR,
     665             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     666             :                          errmsg("duplicate option \"%s\"", defel->defname)));
     667          98 :             opt->label = strVal(defel->arg);
     668          98 :             o_label = true;
     669             :         }
     670         110 :         else if (strcmp(defel->defname, "progress") == 0)
     671             :         {
     672           0 :             if (o_progress)
     673           0 :                 ereport(ERROR,
     674             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     675             :                          errmsg("duplicate option \"%s\"", defel->defname)));
     676           0 :             opt->progress = true;
     677           0 :             o_progress = true;
     678             :         }
     679         110 :         else if (strcmp(defel->defname, "fast") == 0)
     680             :         {
     681           0 :             if (o_fast)
     682           0 :                 ereport(ERROR,
     683             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     684             :                          errmsg("duplicate option \"%s\"", defel->defname)));
     685           0 :             opt->fastcheckpoint = true;
     686           0 :             o_fast = true;
     687             :         }
     688         110 :         else if (strcmp(defel->defname, "nowait") == 0)
     689             :         {
     690          96 :             if (o_nowait)
     691           0 :                 ereport(ERROR,
     692             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     693             :                          errmsg("duplicate option \"%s\"", defel->defname)));
     694          96 :             opt->nowait = true;
     695          96 :             o_nowait = true;
     696             :         }
     697          14 :         else if (strcmp(defel->defname, "wal") == 0)
     698             :         {
     699           2 :             if (o_wal)
     700           0 :                 ereport(ERROR,
     701             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     702             :                          errmsg("duplicate option \"%s\"", defel->defname)));
     703           2 :             opt->includewal = true;
     704           2 :             o_wal = true;
     705             :         }
     706          12 :         else if (strcmp(defel->defname, "max_rate") == 0)
     707             :         {
     708             :             long        maxrate;
     709             : 
     710           0 :             if (o_maxrate)
     711           0 :                 ereport(ERROR,
     712             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     713             :                          errmsg("duplicate option \"%s\"", defel->defname)));
     714             : 
     715           0 :             maxrate = intVal(defel->arg);
     716           0 :             if (maxrate < MAX_RATE_LOWER || maxrate > MAX_RATE_UPPER)
     717           0 :                 ereport(ERROR,
     718             :                         (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     719             :                          errmsg("%d is outside the valid range for parameter \"%s\" (%d .. %d)",
     720             :                                 (int) maxrate, "MAX_RATE", MAX_RATE_LOWER, MAX_RATE_UPPER)));
     721             : 
     722           0 :             opt->maxrate = (uint32) maxrate;
     723           0 :             o_maxrate = true;
     724             :         }
     725          12 :         else if (strcmp(defel->defname, "tablespace_map") == 0)
     726             :         {
     727          10 :             if (o_tablespace_map)
     728           0 :                 ereport(ERROR,
     729             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     730             :                          errmsg("duplicate option \"%s\"", defel->defname)));
     731          10 :             opt->sendtblspcmapfile = true;
     732          10 :             o_tablespace_map = true;
     733             :         }
     734           2 :         else if (strcmp(defel->defname, "noverify_checksums") == 0)
     735             :         {
     736           2 :             if (o_noverify_checksums)
     737           0 :                 ereport(ERROR,
     738             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     739             :                          errmsg("duplicate option \"%s\"", defel->defname)));
     740           2 :             noverify_checksums = true;
     741           2 :             o_noverify_checksums = true;
     742             :         }
     743             :         else
     744           0 :             elog(ERROR, "option \"%s\" not recognized",
     745             :                  defel->defname);
     746             :     }
     747          98 :     if (opt->label == NULL)
     748           0 :         opt->label = "base backup";
     749          98 : }
     750             : 
     751             : 
     752             : /*
     753             :  * SendBaseBackup() - send a complete base backup.
     754             :  *
     755             :  * The function will put the system into backup mode like pg_start_backup()
     756             :  * does, so that the backup is consistent even though we read directly from
     757             :  * the filesystem, bypassing the buffer cache.
     758             :  */
     759             : void
     760          98 : SendBaseBackup(BaseBackupCmd *cmd)
     761             : {
     762             :     basebackup_options opt;
     763             : 
     764          98 :     parse_basebackup_options(cmd->options, &opt);
     765             : 
     766          98 :     WalSndSetState(WALSNDSTATE_BACKUP);
     767             : 
     768          98 :     if (update_process_title)
     769             :     {
     770             :         char        activitymsg[50];
     771             : 
     772          98 :         snprintf(activitymsg, sizeof(activitymsg), "sending backup \"%s\"",
     773             :                  opt.label);
     774          98 :         set_ps_display(activitymsg, false);
     775             :     }
     776             : 
     777          98 :     perform_base_backup(&opt);
     778          86 : }
     779             : 
     780             : static void
     781           0 : send_int8_string(StringInfoData *buf, int64 intval)
     782             : {
     783             :     char        is[32];
     784             : 
     785           0 :     sprintf(is, INT64_FORMAT, intval);
     786           0 :     pq_sendint32(buf, strlen(is));
     787           0 :     pq_sendbytes(buf, is, strlen(is));
     788           0 : }
     789             : 
     790             : static void
     791          98 : SendBackupHeader(List *tablespaces)
     792             : {
     793             :     StringInfoData buf;
     794             :     ListCell   *lc;
     795             : 
     796             :     /* Construct and send the directory information */
     797          98 :     pq_beginmessage(&buf, 'T'); /* RowDescription */
     798          98 :     pq_sendint16(&buf, 3);      /* 3 fields */
     799             : 
     800             :     /* First field - spcoid */
     801          98 :     pq_sendstring(&buf, "spcoid");
     802          98 :     pq_sendint32(&buf, 0);      /* table oid */
     803          98 :     pq_sendint16(&buf, 0);      /* attnum */
     804          98 :     pq_sendint32(&buf, OIDOID); /* type oid */
     805          98 :     pq_sendint16(&buf, 4);      /* typlen */
     806          98 :     pq_sendint32(&buf, 0);      /* typmod */
     807          98 :     pq_sendint16(&buf, 0);      /* format code */
     808             : 
     809             :     /* Second field - spclocation */
     810          98 :     pq_sendstring(&buf, "spclocation");
     811          98 :     pq_sendint32(&buf, 0);
     812          98 :     pq_sendint16(&buf, 0);
     813          98 :     pq_sendint32(&buf, TEXTOID);
     814          98 :     pq_sendint16(&buf, -1);
     815          98 :     pq_sendint32(&buf, 0);
     816          98 :     pq_sendint16(&buf, 0);
     817             : 
     818             :     /* Third field - size */
     819          98 :     pq_sendstring(&buf, "size");
     820          98 :     pq_sendint32(&buf, 0);
     821          98 :     pq_sendint16(&buf, 0);
     822          98 :     pq_sendint32(&buf, INT8OID);
     823          98 :     pq_sendint16(&buf, 8);
     824          98 :     pq_sendint32(&buf, 0);
     825          98 :     pq_sendint16(&buf, 0);
     826          98 :     pq_endmessage(&buf);
     827             : 
     828         206 :     foreach(lc, tablespaces)
     829             :     {
     830         108 :         tablespaceinfo *ti = lfirst(lc);
     831             : 
     832             :         /* Send one datarow message */
     833         108 :         pq_beginmessage(&buf, 'D');
     834         108 :         pq_sendint16(&buf, 3);  /* number of columns */
     835         108 :         if (ti->path == NULL)
     836             :         {
     837          98 :             pq_sendint32(&buf, -1); /* Length = -1 ==> NULL */
     838          98 :             pq_sendint32(&buf, -1);
     839             :         }
     840             :         else
     841             :         {
     842             :             Size        len;
     843             : 
     844          10 :             len = strlen(ti->oid);
     845          10 :             pq_sendint32(&buf, len);
     846          10 :             pq_sendbytes(&buf, ti->oid, len);
     847             : 
     848          10 :             len = strlen(ti->path);
     849          10 :             pq_sendint32(&buf, len);
     850          10 :             pq_sendbytes(&buf, ti->path, len);
     851             :         }
     852         108 :         if (ti->size >= 0)
     853           0 :             send_int8_string(&buf, ti->size / 1024);
     854             :         else
     855         108 :             pq_sendint32(&buf, -1); /* NULL */
     856             : 
     857         108 :         pq_endmessage(&buf);
     858             :     }
     859             : 
     860             :     /* Send a CommandComplete message */
     861          98 :     pq_puttextmessage('C', "SELECT");
     862          98 : }
     863             : 
     864             : /*
     865             :  * Send a single resultset containing just a single
     866             :  * XLogRecPtr record (in text format)
     867             :  */
     868             : static void
     869         190 : SendXlogRecPtrResult(XLogRecPtr ptr, TimeLineID tli)
     870             : {
     871             :     StringInfoData buf;
     872             :     char        str[MAXFNAMELEN];
     873             :     Size        len;
     874             : 
     875         190 :     pq_beginmessage(&buf, 'T'); /* RowDescription */
     876         190 :     pq_sendint16(&buf, 2);      /* 2 fields */
     877             : 
     878             :     /* Field headers */
     879         190 :     pq_sendstring(&buf, "recptr");
     880         190 :     pq_sendint32(&buf, 0);      /* table oid */
     881         190 :     pq_sendint16(&buf, 0);      /* attnum */
     882         190 :     pq_sendint32(&buf, TEXTOID);    /* type oid */
     883         190 :     pq_sendint16(&buf, -1);
     884         190 :     pq_sendint32(&buf, 0);
     885         190 :     pq_sendint16(&buf, 0);
     886             : 
     887         190 :     pq_sendstring(&buf, "tli");
     888         190 :     pq_sendint32(&buf, 0);      /* table oid */
     889         190 :     pq_sendint16(&buf, 0);      /* attnum */
     890             : 
     891             :     /*
     892             :      * int8 may seem like a surprising data type for this, but in theory int4
     893             :      * would not be wide enough for this, as TimeLineID is unsigned.
     894             :      */
     895         190 :     pq_sendint32(&buf, INT8OID);    /* type oid */
     896         190 :     pq_sendint16(&buf, -1);
     897         190 :     pq_sendint32(&buf, 0);
     898         190 :     pq_sendint16(&buf, 0);
     899         190 :     pq_endmessage(&buf);
     900             : 
     901             :     /* Data row */
     902         190 :     pq_beginmessage(&buf, 'D');
     903         190 :     pq_sendint16(&buf, 2);      /* number of columns */
     904             : 
     905         380 :     len = snprintf(str, sizeof(str),
     906         190 :                    "%X/%X", (uint32) (ptr >> 32), (uint32) ptr);
     907         190 :     pq_sendint32(&buf, len);
     908         190 :     pq_sendbytes(&buf, str, len);
     909             : 
     910         190 :     len = snprintf(str, sizeof(str), "%u", tli);
     911         190 :     pq_sendint32(&buf, len);
     912         190 :     pq_sendbytes(&buf, str, len);
     913             : 
     914         190 :     pq_endmessage(&buf);
     915             : 
     916             :     /* Send a CommandComplete message */
     917         190 :     pq_puttextmessage('C', "SELECT");
     918         190 : }
     919             : 
     920             : /*
     921             :  * Inject a file with given name and content in the output tar stream.
     922             :  */
     923             : static void
     924         110 : sendFileWithContent(const char *filename, const char *content)
     925             : {
     926             :     struct stat statbuf;
     927             :     int         pad,
     928             :                 len;
     929             : 
     930         110 :     len = strlen(content);
     931             : 
     932             :     /*
     933             :      * Construct a stat struct for the backup_label file we're injecting in
     934             :      * the tar.
     935             :      */
     936             :     /* Windows doesn't have the concept of uid and gid */
     937             : #ifdef WIN32
     938             :     statbuf.st_uid = 0;
     939             :     statbuf.st_gid = 0;
     940             : #else
     941         110 :     statbuf.st_uid = geteuid();
     942         110 :     statbuf.st_gid = getegid();
     943             : #endif
     944         110 :     statbuf.st_mtime = time(NULL);
     945         110 :     statbuf.st_mode = pg_file_create_mode;
     946         110 :     statbuf.st_size = len;
     947             : 
     948         110 :     _tarWriteHeader(filename, NULL, &statbuf, false);
     949             :     /* Send the contents as a CopyData message */
     950         110 :     pq_putmessage('d', content, len);
     951             : 
     952             :     /* Pad to 512 byte boundary, per tar format requirements */
     953         110 :     pad = ((len + 511) & ~511) - len;
     954         110 :     if (pad > 0)
     955             :     {
     956             :         char        buf[512];
     957             : 
     958         102 :         MemSet(buf, 0, pad);
     959         102 :         pq_putmessage('d', buf, pad);
     960             :     }
     961         110 : }
     962             : 
     963             : /*
     964             :  * Include the tablespace directory pointed to by 'path' in the output tar
     965             :  * stream.  If 'sizeonly' is true, we just calculate a total length and return
     966             :  * it, without actually sending anything.
     967             :  *
     968             :  * Only used to send auxiliary tablespaces, not PGDATA.
     969             :  */
     970             : int64
     971          10 : sendTablespace(char *path, bool sizeonly)
     972             : {
     973             :     int64       size;
     974             :     char        pathbuf[MAXPGPATH];
     975             :     struct stat statbuf;
     976             : 
     977             :     /*
     978             :      * 'path' points to the tablespace location, but we only want to include
     979             :      * the version directory in it that belongs to us.
     980             :      */
     981          10 :     snprintf(pathbuf, sizeof(pathbuf), "%s/%s", path,
     982             :              TABLESPACE_VERSION_DIRECTORY);
     983             : 
     984             :     /*
     985             :      * Store a directory entry in the tar file so we get the permissions
     986             :      * right.
     987             :      */
     988          10 :     if (lstat(pathbuf, &statbuf) != 0)
     989             :     {
     990           0 :         if (errno != ENOENT)
     991           0 :             ereport(ERROR,
     992             :                     (errcode_for_file_access(),
     993             :                      errmsg("could not stat file or directory \"%s\": %m",
     994             :                             pathbuf)));
     995             : 
     996             :         /* If the tablespace went away while scanning, it's no error. */
     997           0 :         return 0;
     998             :     }
     999             : 
    1000          10 :     size = _tarWriteHeader(TABLESPACE_VERSION_DIRECTORY, NULL, &statbuf,
    1001             :                            sizeonly);
    1002             : 
    1003             :     /* Send all the files in the tablespace version directory */
    1004          10 :     size += sendDir(pathbuf, strlen(path), sizeonly, NIL, true);
    1005             : 
    1006          10 :     return size;
    1007             : }
    1008             : 
    1009             : /*
    1010             :  * Include all files from the given directory in the output tar stream. If
    1011             :  * 'sizeonly' is true, we just calculate a total length and return it, without
    1012             :  * actually sending anything.
    1013             :  *
    1014             :  * Omit any directory in the tablespaces list, to avoid backing up
    1015             :  * tablespaces twice when they were created inside PGDATA.
    1016             :  *
    1017             :  * If sendtblspclinks is true, we need to include symlink
    1018             :  * information in the tar file. If not, we can skip that
    1019             :  * as it will be sent separately in the tablespace_map file.
    1020             :  */
    1021             : static int64
    1022        1648 : sendDir(const char *path, int basepathlen, bool sizeonly, List *tablespaces,
    1023             :         bool sendtblspclinks)
    1024             : {
    1025             :     DIR        *dir;
    1026             :     struct dirent *de;
    1027             :     char        pathbuf[MAXPGPATH * 2];
    1028             :     struct stat statbuf;
    1029        1648 :     int64       size = 0;
    1030             :     const char *lastDir;        /* Split last dir from parent path. */
    1031        1648 :     bool        isDbDir = false;    /* Does this directory contain relations? */
    1032             : 
    1033             :     /*
    1034             :      * Determine if the current path is a database directory that can contain
    1035             :      * relations.
    1036             :      *
    1037             :      * Start by finding the location of the delimiter between the parent path
    1038             :      * and the current path.
    1039             :      */
    1040        1648 :     lastDir = last_dir_separator(path);
    1041             : 
    1042             :     /* Does this path look like a database path (i.e. all digits)? */
    1043        3198 :     if (lastDir != NULL &&
    1044        1550 :         strspn(lastDir + 1, "0123456789") == strlen(lastDir + 1))
    1045             :     {
    1046             :         /* Part of path that contains the parent directory. */
    1047         286 :         int         parentPathLen = lastDir - path;
    1048             : 
    1049             :         /*
    1050             :          * Mark path as a database directory if the parent path is either
    1051             :          * $PGDATA/base or a tablespace version path.
    1052             :          */
    1053         292 :         if (strncmp(path, "./base", parentPathLen) == 0 ||
    1054          12 :             (parentPathLen >= (sizeof(TABLESPACE_VERSION_DIRECTORY) - 1) &&
    1055           6 :              strncmp(lastDir - (sizeof(TABLESPACE_VERSION_DIRECTORY) - 1),
    1056             :                      TABLESPACE_VERSION_DIRECTORY,
    1057             :                      sizeof(TABLESPACE_VERSION_DIRECTORY) - 1) == 0))
    1058         286 :             isDbDir = true;
    1059             :     }
    1060             : 
    1061        1648 :     dir = AllocateDir(path);
    1062      100842 :     while ((de = ReadDir(dir, path)) != NULL)
    1063             :     {
    1064             :         int         excludeIdx;
    1065             :         bool        excludeFound;
    1066             :         ForkNumber  relForkNum; /* Type of fork if file is a relation */
    1067             :         int         relOidChars;    /* Chars in filename that are the rel oid */
    1068             : 
    1069             :         /* Skip special stuff */
    1070       97556 :         if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
    1071        7868 :             continue;
    1072             : 
    1073             :         /* Skip temporary files */
    1074       94280 :         if (strncmp(de->d_name,
    1075             :                     PG_TEMP_FILE_PREFIX,
    1076             :                     strlen(PG_TEMP_FILE_PREFIX)) == 0)
    1077           0 :             continue;
    1078             : 
    1079             :         /*
    1080             :          * Check if the postmaster has signaled us to exit, and abort with an
    1081             :          * error in that case. The error handler further up will call
    1082             :          * do_pg_abort_backup() for us. Also check that if the backup was
    1083             :          * started while still in recovery, the server wasn't promoted.
    1084             :          * do_pg_stop_backup() will check that too, but it's better to stop
    1085             :          * the backup early than continue to the end and fail there.
    1086             :          */
    1087       94280 :         CHECK_FOR_INTERRUPTS();
    1088       94280 :         if (RecoveryInProgress() != backup_started_in_recovery)
    1089           0 :             ereport(ERROR,
    1090             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1091             :                      errmsg("the standby was promoted during online backup"),
    1092             :                      errhint("This means that the backup being taken is corrupt "
    1093             :                              "and should not be used. "
    1094             :                              "Try taking another online backup.")));
    1095             : 
    1096             :         /* Scan for files that should be excluded */
    1097       94280 :         excludeFound = false;
    1098      752788 :         for (excludeIdx = 0; excludeFiles[excludeIdx] != NULL; excludeIdx++)
    1099             :         {
    1100      658906 :             if (strcmp(de->d_name, excludeFiles[excludeIdx]) == 0)
    1101             :             {
    1102         398 :                 elog(DEBUG1, "file \"%s\" excluded from backup", de->d_name);
    1103         398 :                 excludeFound = true;
    1104         398 :                 break;
    1105             :             }
    1106             :         }
    1107             : 
    1108       94280 :         if (excludeFound)
    1109         398 :             continue;
    1110             : 
    1111             :         /* Exclude all forks for unlogged tables except the init fork */
    1112      178644 :         if (isDbDir &&
    1113       84762 :             parse_filename_for_nontemp_relation(de->d_name, &relOidChars,
    1114             :                                                 &relForkNum))
    1115             :         {
    1116             :             /* Never exclude init forks */
    1117       84170 :             if (relForkNum != INIT_FORKNUM)
    1118             :             {
    1119             :                 char        initForkFile[MAXPGPATH];
    1120             :                 char        relOid[OIDCHARS + 1];
    1121             : 
    1122             :                 /*
    1123             :                  * If any other type of fork, check if there is an init fork
    1124             :                  * with the same OID. If so, the file can be excluded.
    1125             :                  */
    1126       84124 :                 memcpy(relOid, de->d_name, relOidChars);
    1127       84124 :                 relOid[relOidChars] = '\0';
    1128       84124 :                 snprintf(initForkFile, sizeof(initForkFile), "%s/%s_init",
    1129             :                          path, relOid);
    1130             : 
    1131       84124 :                 if (lstat(initForkFile, &statbuf) == 0)
    1132             :                 {
    1133          46 :                     elog(DEBUG2,
    1134             :                          "unlogged relation file \"%s\" excluded from backup",
    1135             :                          de->d_name);
    1136             : 
    1137          46 :                     continue;
    1138             :                 }
    1139             :             }
    1140             :         }
    1141             : 
    1142             :         /* Exclude temporary relations */
    1143       93836 :         if (isDbDir && looks_like_temp_rel_name(de->d_name))
    1144             :         {
    1145          32 :             elog(DEBUG2,
    1146             :                  "temporary relation file \"%s\" excluded from backup",
    1147             :                  de->d_name);
    1148             : 
    1149          32 :             continue;
    1150             :         }
    1151             : 
    1152       93804 :         snprintf(pathbuf, sizeof(pathbuf), "%s/%s", path, de->d_name);
    1153             : 
    1154             :         /* Skip pg_control here to back up it last */
    1155       93804 :         if (strcmp(pathbuf, "./global/pg_control") == 0)
    1156          92 :             continue;
    1157             : 
    1158       93712 :         if (lstat(pathbuf, &statbuf) != 0)
    1159             :         {
    1160           0 :             if (errno != ENOENT)
    1161           0 :                 ereport(ERROR,
    1162             :                         (errcode_for_file_access(),
    1163             :                          errmsg("could not stat file or directory \"%s\": %m",
    1164             :                                 pathbuf)));
    1165             : 
    1166             :             /* If the file went away while scanning, it's not an error. */
    1167           0 :             continue;
    1168             :         }
    1169             : 
    1170             :         /* Scan for directories whose contents should be excluded */
    1171       93712 :         excludeFound = false;
    1172      747054 :         for (excludeIdx = 0; excludeDirContents[excludeIdx] != NULL; excludeIdx++)
    1173             :         {
    1174      653998 :             if (strcmp(de->d_name, excludeDirContents[excludeIdx]) == 0)
    1175             :             {
    1176         656 :                 elog(DEBUG1, "contents of directory \"%s\" excluded from backup", de->d_name);
    1177         656 :                 size += _tarWriteDir(pathbuf, basepathlen, &statbuf, sizeonly);
    1178         656 :                 excludeFound = true;
    1179         656 :                 break;
    1180             :             }
    1181             :         }
    1182             : 
    1183       93712 :         if (excludeFound)
    1184         656 :             continue;
    1185             : 
    1186             :         /*
    1187             :          * Exclude contents of directory specified by statrelpath if not set
    1188             :          * to the default (pg_stat_tmp) which is caught in the loop above.
    1189             :          */
    1190       93056 :         if (statrelpath != NULL && strcmp(pathbuf, statrelpath) == 0)
    1191             :         {
    1192           0 :             elog(DEBUG1, "contents of directory \"%s\" excluded from backup", statrelpath);
    1193           0 :             size += _tarWriteDir(pathbuf, basepathlen, &statbuf, sizeonly);
    1194           0 :             continue;
    1195             :         }
    1196             : 
    1197             :         /*
    1198             :          * We can skip pg_wal, the WAL segments need to be fetched from the
    1199             :          * WAL archive anyway. But include it as an empty directory anyway, so
    1200             :          * we get permissions right.
    1201             :          */
    1202       93056 :         if (strcmp(pathbuf, "./pg_wal") == 0)
    1203             :         {
    1204             :             /* If pg_wal is a symlink, write it as a directory anyway */
    1205          92 :             size += _tarWriteDir(pathbuf, basepathlen, &statbuf, sizeonly);
    1206             : 
    1207             :             /*
    1208             :              * Also send archive_status directory (by hackishly reusing
    1209             :              * statbuf from above ...).
    1210             :              */
    1211          92 :             size += _tarWriteHeader("./pg_wal/archive_status", NULL, &statbuf,
    1212             :                                     sizeonly);
    1213             : 
    1214          92 :             continue;           /* don't recurse into pg_wal */
    1215             :         }
    1216             : 
    1217             :         /* Allow symbolic links in pg_tblspc only */
    1218       92968 :         if (strcmp(path, "./pg_tblspc") == 0 &&
    1219             : #ifndef WIN32
    1220           4 :             S_ISLNK(statbuf.st_mode)
    1221             : #else
    1222             :             pgwin32_is_junction(pathbuf)
    1223             : #endif
    1224             :             )
    1225           4 :         {
    1226             : #if defined(HAVE_READLINK) || defined(WIN32)
    1227             :             char        linkpath[MAXPGPATH];
    1228             :             int         rllen;
    1229             : 
    1230           4 :             rllen = readlink(pathbuf, linkpath, sizeof(linkpath));
    1231           4 :             if (rllen < 0)
    1232           0 :                 ereport(ERROR,
    1233             :                         (errcode_for_file_access(),
    1234             :                          errmsg("could not read symbolic link \"%s\": %m",
    1235             :                                 pathbuf)));
    1236           4 :             if (rllen >= sizeof(linkpath))
    1237           0 :                 ereport(ERROR,
    1238             :                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1239             :                          errmsg("symbolic link \"%s\" target is too long",
    1240             :                                 pathbuf)));
    1241           4 :             linkpath[rllen] = '\0';
    1242             : 
    1243           4 :             size += _tarWriteHeader(pathbuf + basepathlen + 1, linkpath,
    1244             :                                     &statbuf, sizeonly);
    1245             : #else
    1246             : 
    1247             :             /*
    1248             :              * If the platform does not have symbolic links, it should not be
    1249             :              * possible to have tablespaces - clearly somebody else created
    1250             :              * them. Warn about it and ignore.
    1251             :              */
    1252             :             ereport(WARNING,
    1253             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1254             :                      errmsg("tablespaces are not supported on this platform")));
    1255             :             continue;
    1256             : #endif                          /* HAVE_READLINK */
    1257             :         }
    1258       92960 :         else if (S_ISDIR(statbuf.st_mode))
    1259             :         {
    1260        1548 :             bool        skip_this_dir = false;
    1261             :             ListCell   *lc;
    1262             : 
    1263             :             /*
    1264             :              * Store a directory entry in the tar file so we can get the
    1265             :              * permissions right.
    1266             :              */
    1267        1548 :             size += _tarWriteHeader(pathbuf + basepathlen + 1, NULL, &statbuf,
    1268             :                                     sizeonly);
    1269             : 
    1270             :             /*
    1271             :              * Call ourselves recursively for a directory, unless it happens
    1272             :              * to be a separate tablespace located within PGDATA.
    1273             :              */
    1274        3238 :             foreach(lc, tablespaces)
    1275             :             {
    1276        1690 :                 tablespaceinfo *ti = (tablespaceinfo *) lfirst(lc);
    1277             : 
    1278             :                 /*
    1279             :                  * ti->rpath is the tablespace relative path within PGDATA, or
    1280             :                  * NULL if the tablespace has been properly located somewhere
    1281             :                  * else.
    1282             :                  *
    1283             :                  * Skip past the leading "./" in pathbuf when comparing.
    1284             :                  */
    1285        1690 :                 if (ti->rpath && strcmp(ti->rpath, pathbuf + 2) == 0)
    1286             :                 {
    1287           0 :                     skip_this_dir = true;
    1288           0 :                     break;
    1289             :                 }
    1290             :             }
    1291             : 
    1292             :             /*
    1293             :              * skip sending directories inside pg_tblspc, if not required.
    1294             :              */
    1295        1548 :             if (strcmp(pathbuf, "./pg_tblspc") == 0 && !sendtblspclinks)
    1296           8 :                 skip_this_dir = true;
    1297             : 
    1298        1548 :             if (!skip_this_dir)
    1299        1540 :                 size += sendDir(pathbuf, basepathlen, sizeonly, tablespaces, sendtblspclinks);
    1300             :         }
    1301       91412 :         else if (S_ISREG(statbuf.st_mode))
    1302             :         {
    1303       91412 :             bool        sent = false;
    1304             : 
    1305       91412 :             if (!sizeonly)
    1306      176096 :                 sent = sendFile(pathbuf, pathbuf + basepathlen + 1, &statbuf,
    1307       84684 :                                 true, isDbDir ? pg_atoi(lastDir + 1, sizeof(Oid), 0) : InvalidOid);
    1308             : 
    1309       91406 :             if (sent || sizeonly)
    1310             :             {
    1311             :                 /* Add size, rounded up to 512byte block */
    1312       91406 :                 size += ((statbuf.st_size + 511) & ~511);
    1313       91406 :                 size += 512;    /* Size of the header of the file */
    1314             :             }
    1315             :         }
    1316             :         else
    1317           0 :             ereport(WARNING,
    1318             :                     (errmsg("skipping special file \"%s\"", pathbuf)));
    1319             :     }
    1320        1638 :     FreeDir(dir);
    1321        1638 :     return size;
    1322             : }
    1323             : 
    1324             : /*
    1325             :  * Check if a file should have its checksum validated.
    1326             :  * We validate checksums on files in regular tablespaces
    1327             :  * (including global and default) only, and in those there
    1328             :  * are some files that are explicitly excluded.
    1329             :  */
    1330             : static bool
    1331       39348 : is_checksummed_file(const char *fullpath, const char *filename)
    1332             : {
    1333             :     const char *const *f;
    1334             : 
    1335             :     /* Check that the file is in a tablespace */
    1336       76162 :     if (strncmp(fullpath, "./global/", 9) == 0 ||
    1337       37336 :         strncmp(fullpath, "./base/", 7) == 0 ||
    1338         522 :         strncmp(fullpath, "/", 1) == 0)
    1339             :     {
    1340             :         /* Compare file against noChecksumFiles skiplist */
    1341      193580 :         for (f = noChecksumFiles; *f; f++)
    1342      155024 :             if (strcmp(*f, filename) == 0)
    1343         280 :                 return false;
    1344             : 
    1345       38556 :         return true;
    1346             :     }
    1347             :     else
    1348         512 :         return false;
    1349             : }
    1350             : 
    1351             : /*****
    1352             :  * Functions for handling tar file format
    1353             :  *
    1354             :  * Copied from pg_dump, but modified to work with libpq for sending
    1355             :  */
    1356             : 
    1357             : 
    1358             : /*
    1359             :  * Given the member, write the TAR header & send the file.
    1360             :  *
    1361             :  * If 'missing_ok' is true, will not throw an error if the file is not found.
    1362             :  *
    1363             :  * If dboid is anything other than InvalidOid then any checksum failures detected
    1364             :  * will get reported to the stats collector.
    1365             :  *
    1366             :  * Returns true if the file was successfully sent, false if 'missing_ok',
    1367             :  * and the file did not exist.
    1368             :  */
    1369             : static bool
    1370       91504 : sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf,
    1371             :          bool missing_ok, Oid dboid)
    1372             : {
    1373             :     FILE       *fp;
    1374       91504 :     BlockNumber blkno = 0;
    1375       91504 :     bool        block_retry = false;
    1376             :     char        buf[TAR_SEND_SIZE];
    1377             :     uint16      checksum;
    1378       91504 :     int         checksum_failures = 0;
    1379             :     off_t       cnt;
    1380             :     int         i;
    1381       91504 :     pgoff_t     len = 0;
    1382             :     char       *page;
    1383             :     size_t      pad;
    1384             :     PageHeader  phdr;
    1385       91504 :     int         segmentno = 0;
    1386             :     char       *segmentpath;
    1387       91504 :     bool        verify_checksum = false;
    1388             : 
    1389       91504 :     fp = AllocateFile(readfilename, "rb");
    1390       91504 :     if (fp == NULL)
    1391             :     {
    1392           0 :         if (errno == ENOENT && missing_ok)
    1393           0 :             return false;
    1394           0 :         ereport(ERROR,
    1395             :                 (errcode_for_file_access(),
    1396             :                  errmsg("could not open file \"%s\": %m", readfilename)));
    1397             :     }
    1398             : 
    1399       91504 :     _tarWriteHeader(tarfilename, NULL, statbuf, false);
    1400             : 
    1401       91502 :     if (!noverify_checksums && DataChecksumsEnabled())
    1402             :     {
    1403             :         char       *filename;
    1404             : 
    1405             :         /*
    1406             :          * Get the filename (excluding path).  As last_dir_separator()
    1407             :          * includes the last directory separator, we chop that off by
    1408             :          * incrementing the pointer.
    1409             :          */
    1410       39348 :         filename = last_dir_separator(readfilename) + 1;
    1411             : 
    1412       39348 :         if (is_checksummed_file(readfilename, filename))
    1413             :         {
    1414       38556 :             verify_checksum = true;
    1415             : 
    1416             :             /*
    1417             :              * Cut off at the segment boundary (".") to get the segment number
    1418             :              * in order to mix it into the checksum.
    1419             :              */
    1420       38556 :             segmentpath = strstr(filename, ".");
    1421       38556 :             if (segmentpath != NULL)
    1422             :             {
    1423           0 :                 segmentno = atoi(segmentpath + 1);
    1424           0 :                 if (segmentno == 0)
    1425           0 :                     ereport(ERROR,
    1426             :                             (errmsg("invalid segment number %d in file \"%s\"",
    1427             :                                     segmentno, filename)));
    1428             :             }
    1429             :         }
    1430             :     }
    1431             : 
    1432      218934 :     while ((cnt = fread(buf, 1, Min(sizeof(buf), statbuf->st_size - len), fp)) > 0)
    1433             :     {
    1434             :         /*
    1435             :          * The checksums are verified at block level, so we iterate over the
    1436             :          * buffer in chunks of BLCKSZ, after making sure that
    1437             :          * TAR_SEND_SIZE/buf is divisible by BLCKSZ and we read a multiple of
    1438             :          * BLCKSZ bytes.
    1439             :          */
    1440             :         Assert(TAR_SEND_SIZE % BLCKSZ == 0);
    1441             : 
    1442      111032 :         if (verify_checksum && (cnt % BLCKSZ != 0))
    1443             :         {
    1444           0 :             ereport(WARNING,
    1445             :                     (errmsg("cannot verify checksum in file \"%s\", block "
    1446             :                             "%d: read buffer size %d and page size %d "
    1447             :                             "differ",
    1448             :                             readfilename, blkno, (int) cnt, BLCKSZ)));
    1449           0 :             verify_checksum = false;
    1450             :         }
    1451             : 
    1452      111032 :         if (verify_checksum)
    1453             :         {
    1454      162724 :             for (i = 0; i < cnt / BLCKSZ; i++)
    1455             :             {
    1456      115804 :                 page = buf + BLCKSZ * i;
    1457             : 
    1458             :                 /*
    1459             :                  * Only check pages which have not been modified since the
    1460             :                  * start of the base backup. Otherwise, they might have been
    1461             :                  * written only halfway and the checksum would not be valid.
    1462             :                  * However, replaying WAL would reinstate the correct page in
    1463             :                  * this case. We also skip completely new pages, since they
    1464             :                  * don't have a checksum yet.
    1465             :                  */
    1466      115804 :                 if (!PageIsNew(page) && PageGetLSN(page) < startptr)
    1467             :                 {
    1468      115804 :                     checksum = pg_checksum_page((char *) page, blkno + segmentno * RELSEG_SIZE);
    1469      115804 :                     phdr = (PageHeader) page;
    1470      115804 :                     if (phdr->pd_checksum != checksum)
    1471             :                     {
    1472             :                         /*
    1473             :                          * Retry the block on the first failure.  It's
    1474             :                          * possible that we read the first 4K page of the
    1475             :                          * block just before postgres updated the entire block
    1476             :                          * so it ends up looking torn to us.  We only need to
    1477             :                          * retry once because the LSN should be updated to
    1478             :                          * something we can ignore on the next pass.  If the
    1479             :                          * error happens again then it is a true validation
    1480             :                          * failure.
    1481             :                          */
    1482          56 :                         if (block_retry == false)
    1483             :                         {
    1484             :                             /* Reread the failed block */
    1485          28 :                             if (fseek(fp, -(cnt - BLCKSZ * i), SEEK_CUR) == -1)
    1486             :                             {
    1487           0 :                                 ereport(ERROR,
    1488             :                                         (errcode_for_file_access(),
    1489             :                                          errmsg("could not fseek in file \"%s\": %m",
    1490             :                                                 readfilename)));
    1491             :                             }
    1492             : 
    1493          28 :                             if (fread(buf + BLCKSZ * i, 1, BLCKSZ, fp) != BLCKSZ)
    1494             :                             {
    1495             :                                 /*
    1496             :                                  * If we hit end-of-file, a concurrent
    1497             :                                  * truncation must have occurred, so break out
    1498             :                                  * of this loop just as if the initial fread()
    1499             :                                  * returned 0. We'll drop through to the same
    1500             :                                  * code that handles that case. (We must fix
    1501             :                                  * up cnt first, though.)
    1502             :                                  */
    1503           0 :                                 if (feof(fp))
    1504             :                                 {
    1505           0 :                                     cnt = BLCKSZ * i;
    1506           0 :                                     break;
    1507             :                                 }
    1508             : 
    1509           0 :                                 ereport(ERROR,
    1510             :                                         (errcode_for_file_access(),
    1511             :                                          errmsg("could not reread block %d of file \"%s\": %m",
    1512             :                                                 blkno, readfilename)));
    1513             :                             }
    1514             : 
    1515          28 :                             if (fseek(fp, cnt - BLCKSZ * i - BLCKSZ, SEEK_CUR) == -1)
    1516             :                             {
    1517           0 :                                 ereport(ERROR,
    1518             :                                         (errcode_for_file_access(),
    1519             :                                          errmsg("could not fseek in file \"%s\": %m",
    1520             :                                                 readfilename)));
    1521             :                             }
    1522             : 
    1523             :                             /* Set flag so we know a retry was attempted */
    1524          28 :                             block_retry = true;
    1525             : 
    1526             :                             /* Reset loop to validate the block again */
    1527          28 :                             i--;
    1528          28 :                             continue;
    1529             :                         }
    1530             : 
    1531          28 :                         checksum_failures++;
    1532             : 
    1533          28 :                         if (checksum_failures <= 5)
    1534          24 :                             ereport(WARNING,
    1535             :                                     (errmsg("checksum verification failed in "
    1536             :                                             "file \"%s\", block %d: calculated "
    1537             :                                             "%X but expected %X",
    1538             :                                             readfilename, blkno, checksum,
    1539             :                                             phdr->pd_checksum)));
    1540          28 :                         if (checksum_failures == 5)
    1541           4 :                             ereport(WARNING,
    1542             :                                     (errmsg("further checksum verification "
    1543             :                                             "failures in file \"%s\" will not "
    1544             :                                             "be reported", readfilename)));
    1545             :                     }
    1546             :                 }
    1547      115776 :                 block_retry = false;
    1548      115776 :                 blkno++;
    1549             :             }
    1550             :         }
    1551             : 
    1552             :         /* Send the chunk as a CopyData message */
    1553      111032 :         if (pq_putmessage('d', buf, cnt))
    1554           4 :             ereport(ERROR,
    1555             :                     (errmsg("base backup could not send data, aborting backup")));
    1556             : 
    1557      111028 :         len += cnt;
    1558      111028 :         throttle(cnt);
    1559             : 
    1560      111028 :         if (feof(fp) || len >= statbuf->st_size)
    1561             :         {
    1562             :             /*
    1563             :              * Reached end of file. The file could be longer, if it was
    1564             :              * extended while we were sending it, but for a base backup we can
    1565             :              * ignore such extended data. It will be restored from WAL.
    1566             :              */
    1567             :             break;
    1568             :         }
    1569             :     }
    1570             : 
    1571       91498 :     CHECK_FREAD_ERROR(fp, readfilename);
    1572             : 
    1573             :     /* If the file was truncated while we were sending it, pad it with zeros */
    1574       91498 :     if (len < statbuf->st_size)
    1575             :     {
    1576           0 :         MemSet(buf, 0, sizeof(buf));
    1577           0 :         while (len < statbuf->st_size)
    1578             :         {
    1579           0 :             cnt = Min(sizeof(buf), statbuf->st_size - len);
    1580           0 :             pq_putmessage('d', buf, cnt);
    1581           0 :             len += cnt;
    1582           0 :             throttle(cnt);
    1583             :         }
    1584             :     }
    1585             : 
    1586             :     /*
    1587             :      * Pad to 512 byte boundary, per tar format requirements. (This small
    1588             :      * piece of data is probably not worth throttling.)
    1589             :      */
    1590       91498 :     pad = ((len + 511) & ~511) - len;
    1591       91498 :     if (pad > 0)
    1592             :     {
    1593         950 :         MemSet(buf, 0, pad);
    1594         950 :         pq_putmessage('d', buf, pad);
    1595             :     }
    1596             : 
    1597       91498 :     FreeFile(fp);
    1598             : 
    1599       91498 :     if (checksum_failures > 1)
    1600             :     {
    1601           4 :         ereport(WARNING,
    1602             :                 (errmsg("file \"%s\" has a total of %d checksum verification "
    1603             :                         "failures", readfilename, checksum_failures)));
    1604             : 
    1605           4 :         pgstat_report_checksum_failures_in_db(dboid, checksum_failures);
    1606             :     }
    1607             : 
    1608       91498 :     total_checksum_failures += checksum_failures;
    1609             : 
    1610       91498 :     return true;
    1611             : }
    1612             : 
    1613             : 
    1614             : static int64
    1615       94018 : _tarWriteHeader(const char *filename, const char *linktarget,
    1616             :                 struct stat *statbuf, bool sizeonly)
    1617             : {
    1618             :     char        h[512];
    1619             :     enum tarError rc;
    1620             : 
    1621       94018 :     if (!sizeonly)
    1622             :     {
    1623       94018 :         rc = tarCreateHeader(h, filename, linktarget, statbuf->st_size,
    1624             :                              statbuf->st_mode, statbuf->st_uid, statbuf->st_gid,
    1625             :                              statbuf->st_mtime);
    1626             : 
    1627       94018 :         switch (rc)
    1628             :         {
    1629             :             case TAR_OK:
    1630       94016 :                 break;
    1631             :             case TAR_NAME_TOO_LONG:
    1632           2 :                 ereport(ERROR,
    1633             :                         (errmsg("file name too long for tar format: \"%s\"",
    1634             :                                 filename)));
    1635             :                 break;
    1636             :             case TAR_SYMLINK_TOO_LONG:
    1637           0 :                 ereport(ERROR,
    1638             :                         (errmsg("symbolic link target too long for tar format: "
    1639             :                                 "file name \"%s\", target \"%s\"",
    1640             :                                 filename, linktarget)));
    1641             :                 break;
    1642             :             default:
    1643           0 :                 elog(ERROR, "unrecognized tar error: %d", rc);
    1644             :         }
    1645             : 
    1646       94016 :         pq_putmessage('d', h, sizeof(h));
    1647             :     }
    1648             : 
    1649       94016 :     return sizeof(h);
    1650             : }
    1651             : 
    1652             : /*
    1653             :  * Write tar header for a directory.  If the entry in statbuf is a link then
    1654             :  * write it as a directory anyway.
    1655             :  */
    1656             : static int64
    1657         748 : _tarWriteDir(const char *pathbuf, int basepathlen, struct stat *statbuf,
    1658             :              bool sizeonly)
    1659             : {
    1660             :     /* If symlink, write it as a directory anyway */
    1661             : #ifndef WIN32
    1662         748 :     if (S_ISLNK(statbuf->st_mode))
    1663             : #else
    1664             :     if (pgwin32_is_junction(pathbuf))
    1665             : #endif
    1666          44 :         statbuf->st_mode = S_IFDIR | pg_dir_create_mode;
    1667             : 
    1668         748 :     return _tarWriteHeader(pathbuf + basepathlen + 1, NULL, statbuf, sizeonly);
    1669             : }
    1670             : 
    1671             : /*
    1672             :  * Increment the network transfer counter by the given number of bytes,
    1673             :  * and sleep if necessary to comply with the requested network transfer
    1674             :  * rate.
    1675             :  */
    1676             : static void
    1677      112052 : throttle(size_t increment)
    1678             : {
    1679             :     TimeOffset  elapsed_min;
    1680             : 
    1681      112052 :     if (throttling_counter < 0)
    1682      112052 :         return;
    1683             : 
    1684           0 :     throttling_counter += increment;
    1685           0 :     if (throttling_counter < throttling_sample)
    1686           0 :         return;
    1687             : 
    1688             :     /* How much time should have elapsed at minimum? */
    1689           0 :     elapsed_min = elapsed_min_unit *
    1690           0 :         (throttling_counter / throttling_sample);
    1691             : 
    1692             :     /*
    1693             :      * Since the latch could be set repeatedly because of concurrently WAL
    1694             :      * activity, sleep in a loop to ensure enough time has passed.
    1695             :      */
    1696             :     for (;;)
    1697           0 :     {
    1698             :         TimeOffset  elapsed,
    1699             :                     sleep;
    1700             :         int         wait_result;
    1701             : 
    1702             :         /* Time elapsed since the last measurement (and possible wake up). */
    1703           0 :         elapsed = GetCurrentTimestamp() - throttled_last;
    1704             : 
    1705             :         /* sleep if the transfer is faster than it should be */
    1706           0 :         sleep = elapsed_min - elapsed;
    1707           0 :         if (sleep <= 0)
    1708           0 :             break;
    1709             : 
    1710           0 :         ResetLatch(MyLatch);
    1711             : 
    1712             :         /* We're eating a potentially set latch, so check for interrupts */
    1713           0 :         CHECK_FOR_INTERRUPTS();
    1714             : 
    1715             :         /*
    1716             :          * (TAR_SEND_SIZE / throttling_sample * elapsed_min_unit) should be
    1717             :          * the maximum time to sleep. Thus the cast to long is safe.
    1718             :          */
    1719           0 :         wait_result = WaitLatch(MyLatch,
    1720             :                                 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
    1721           0 :                                 (long) (sleep / 1000),
    1722             :                                 WAIT_EVENT_BASE_BACKUP_THROTTLE);
    1723             : 
    1724           0 :         if (wait_result & WL_LATCH_SET)
    1725           0 :             CHECK_FOR_INTERRUPTS();
    1726             : 
    1727             :         /* Done waiting? */
    1728           0 :         if (wait_result & WL_TIMEOUT)
    1729           0 :             break;
    1730             :     }
    1731             : 
    1732             :     /*
    1733             :      * As we work with integers, only whole multiple of throttling_sample was
    1734             :      * processed. The rest will be done during the next call of this function.
    1735             :      */
    1736           0 :     throttling_counter %= throttling_sample;
    1737             : 
    1738             :     /*
    1739             :      * Time interval for the remaining amount and possible next increments
    1740             :      * starts now.
    1741             :      */
    1742           0 :     throttled_last = GetCurrentTimestamp();
    1743             : }

Generated by: LCOV version 1.13