LCOV - code coverage report
Current view: top level - src/backend/backup - basebackup.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 83.6 % 665 556
Test Date: 2026-03-19 10:16:14 Functions: 93.3 % 15 14
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-2026, PostgreSQL Global Development Group
       7              :  *
       8              :  * IDENTIFICATION
       9              :  *    src/backend/backup/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"
      20              : #include "access/xlogbackup.h"
      21              : #include "backup/backup_manifest.h"
      22              : #include "backup/basebackup.h"
      23              : #include "backup/basebackup_incremental.h"
      24              : #include "backup/basebackup_sink.h"
      25              : #include "backup/basebackup_target.h"
      26              : #include "catalog/pg_tablespace_d.h"
      27              : #include "commands/defrem.h"
      28              : #include "common/compression.h"
      29              : #include "common/file_perm.h"
      30              : #include "common/file_utils.h"
      31              : #include "lib/stringinfo.h"
      32              : #include "miscadmin.h"
      33              : #include "nodes/pg_list.h"
      34              : #include "pgstat.h"
      35              : #include "pgtar.h"
      36              : #include "postmaster/syslogger.h"
      37              : #include "postmaster/walsummarizer.h"
      38              : #include "replication/slot.h"
      39              : #include "replication/walsender.h"
      40              : #include "replication/walsender_private.h"
      41              : #include "storage/bufpage.h"
      42              : #include "storage/checksum.h"
      43              : #include "storage/dsm_impl.h"
      44              : #include "storage/ipc.h"
      45              : #include "storage/reinit.h"
      46              : #include "utils/builtins.h"
      47              : #include "utils/guc.h"
      48              : #include "utils/ps_status.h"
      49              : #include "utils/relcache.h"
      50              : #include "utils/resowner.h"
      51              : #include "utils/wait_event.h"
      52              : 
      53              : /*
      54              :  * How much data do we want to send in one CopyData message? Note that
      55              :  * this may also result in reading the underlying files in chunks of this
      56              :  * size.
      57              :  *
      58              :  * NB: The buffer size is required to be a multiple of the system block
      59              :  * size, so use that value instead if it's bigger than our preference.
      60              :  */
      61              : #define SINK_BUFFER_LENGTH          Max(32768, BLCKSZ)
      62              : 
      63              : typedef struct
      64              : {
      65              :     const char *label;
      66              :     bool        progress;
      67              :     bool        fastcheckpoint;
      68              :     bool        nowait;
      69              :     bool        includewal;
      70              :     bool        incremental;
      71              :     uint32      maxrate;
      72              :     bool        sendtblspcmapfile;
      73              :     bool        send_to_client;
      74              :     bool        use_copytblspc;
      75              :     BaseBackupTargetHandle *target_handle;
      76              :     backup_manifest_option manifest;
      77              :     pg_compress_algorithm compression;
      78              :     pg_compress_specification compression_specification;
      79              :     pg_checksum_type manifest_checksum_type;
      80              : } basebackup_options;
      81              : 
      82              : #define TAR_NUM_TERMINATION_BLOCKS 2
      83              : 
      84              : StaticAssertDecl(TAR_NUM_TERMINATION_BLOCKS * TAR_BLOCK_SIZE <= BLCKSZ,
      85              :                  "BLCKSZ too small for " CppAsString2(TAR_NUM_TERMINATION_BLOCKS) " tar termination blocks");
      86              : 
      87              : static int64 sendTablespace(bbsink *sink, char *path, Oid spcoid, bool sizeonly,
      88              :                             struct backup_manifest_info *manifest,
      89              :                             IncrementalBackupInfo *ib);
      90              : static int64 sendDir(bbsink *sink, const char *path, int basepathlen, bool sizeonly,
      91              :                      List *tablespaces, bool sendtblspclinks,
      92              :                      backup_manifest_info *manifest, Oid spcoid,
      93              :                      IncrementalBackupInfo *ib);
      94              : static bool sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
      95              :                      struct stat *statbuf, bool missing_ok,
      96              :                      Oid dboid, Oid spcoid, RelFileNumber relfilenumber,
      97              :                      unsigned segno,
      98              :                      backup_manifest_info *manifest,
      99              :                      unsigned num_incremental_blocks,
     100              :                      BlockNumber *incremental_blocks,
     101              :                      unsigned truncation_block_length);
     102              : static off_t read_file_data_into_buffer(bbsink *sink,
     103              :                                         const char *readfilename, int fd,
     104              :                                         off_t offset, size_t length,
     105              :                                         BlockNumber blkno,
     106              :                                         bool verify_checksum,
     107              :                                         int *checksum_failures);
     108              : static void push_to_sink(bbsink *sink, pg_checksum_context *checksum_ctx,
     109              :                          size_t *bytes_done, void *data, size_t length);
     110              : static bool verify_page_checksum(Page page, XLogRecPtr start_lsn,
     111              :                                  BlockNumber blkno,
     112              :                                  uint16 *expected_checksum);
     113              : static void sendFileWithContent(bbsink *sink, const char *filename,
     114              :                                 const char *content, int len,
     115              :                                 backup_manifest_info *manifest);
     116              : static int64 _tarWriteHeader(bbsink *sink, const char *filename,
     117              :                              const char *linktarget, struct stat *statbuf,
     118              :                              bool sizeonly);
     119              : static void _tarWritePadding(bbsink *sink, int len);
     120              : static void convert_link_to_directory(const char *pathbuf, struct stat *statbuf);
     121              : static void perform_base_backup(basebackup_options *opt, bbsink *sink,
     122              :                                 IncrementalBackupInfo *ib);
     123              : static void parse_basebackup_options(List *options, basebackup_options *opt);
     124              : static int  compareWalFileNames(const ListCell *a, const ListCell *b);
     125              : static ssize_t basebackup_read_file(int fd, char *buf, size_t nbytes, off_t offset,
     126              :                                     const char *filename, bool partial_read_ok);
     127              : 
     128              : /* Was the backup currently in-progress initiated in recovery mode? */
     129              : static bool backup_started_in_recovery = false;
     130              : 
     131              : /* Total number of checksum failures during base backup. */
     132              : static long long int total_checksum_failures;
     133              : 
     134              : /* Do not verify checksums. */
     135              : static bool noverify_checksums = false;
     136              : 
     137              : /*
     138              :  * Definition of one element part of an exclusion list, used for paths part
     139              :  * of checksum validation or base backups.  "name" is the name of the file
     140              :  * or path to check for exclusion.  If "match_prefix" is true, any items
     141              :  * matching the name as prefix are excluded.
     142              :  */
     143              : struct exclude_list_item
     144              : {
     145              :     const char *name;
     146              :     bool        match_prefix;
     147              : };
     148              : 
     149              : /*
     150              :  * The contents of these directories are removed or recreated during server
     151              :  * start so they are not included in backups.  The directories themselves are
     152              :  * kept and included as empty to preserve access permissions.
     153              :  *
     154              :  * Note: this list should be kept in sync with the filter lists in pg_rewind's
     155              :  * filemap.c.
     156              :  */
     157              : static const char *const excludeDirContents[] =
     158              : {
     159              :     /*
     160              :      * Skip temporary statistics files. PG_STAT_TMP_DIR must be skipped
     161              :      * because extensions like pg_stat_statements store data there.
     162              :      */
     163              :     PG_STAT_TMP_DIR,
     164              : 
     165              :     /*
     166              :      * It is generally not useful to backup the contents of this directory
     167              :      * even if the intention is to restore to another primary. See backup.sgml
     168              :      * for a more detailed description.
     169              :      */
     170              :     PG_REPLSLOT_DIR,
     171              : 
     172              :     /* Contents removed on startup, see dsm_cleanup_for_mmap(). */
     173              :     PG_DYNSHMEM_DIR,
     174              : 
     175              :     /* Contents removed on startup, see AsyncShmemInit(). */
     176              :     "pg_notify",
     177              : 
     178              :     /*
     179              :      * Old contents are loaded for possible debugging but are not required for
     180              :      * normal operation, see SerialInit().
     181              :      */
     182              :     "pg_serial",
     183              : 
     184              :     /* Contents removed on startup, see DeleteAllExportedSnapshotFiles(). */
     185              :     "pg_snapshots",
     186              : 
     187              :     /* Contents zeroed on startup, see StartupSUBTRANS(). */
     188              :     "pg_subtrans",
     189              : 
     190              :     /* end of list */
     191              :     NULL
     192              : };
     193              : 
     194              : /*
     195              :  * List of files excluded from backups.
     196              :  */
     197              : static const struct exclude_list_item excludeFiles[] =
     198              : {
     199              :     /* Skip auto conf temporary file. */
     200              :     {PG_AUTOCONF_FILENAME ".tmp", false},
     201              : 
     202              :     /* Skip current log file temporary file */
     203              :     {LOG_METAINFO_DATAFILE_TMP, false},
     204              : 
     205              :     /*
     206              :      * Skip relation cache because it is rebuilt on startup.  This includes
     207              :      * temporary files.
     208              :      */
     209              :     {RELCACHE_INIT_FILENAME, true},
     210              : 
     211              :     /*
     212              :      * backup_label and tablespace_map should not exist in a running cluster
     213              :      * capable of doing an online backup, but exclude them just in case.
     214              :      */
     215              :     {BACKUP_LABEL_FILE, false},
     216              :     {TABLESPACE_MAP, false},
     217              : 
     218              :     /*
     219              :      * If there's a backup_manifest, it belongs to a backup that was used to
     220              :      * start this server. It is *not* correct for this backup. Our
     221              :      * backup_manifest is injected into the backup separately if users want
     222              :      * it.
     223              :      */
     224              :     {"backup_manifest", false},
     225              : 
     226              :     {"postmaster.pid", false},
     227              :     {"postmaster.opts", false},
     228              : 
     229              :     /* end of list */
     230              :     {NULL, false}
     231              : };
     232              : 
     233              : /*
     234              :  * Actually do a base backup for the specified tablespaces.
     235              :  *
     236              :  * This is split out mainly to avoid complaints about "variable might be
     237              :  * clobbered by longjmp" from stupider versions of gcc.
     238              :  */
     239              : static void
     240          169 : perform_base_backup(basebackup_options *opt, bbsink *sink,
     241              :                     IncrementalBackupInfo *ib)
     242              : {
     243              :     bbsink_state state;
     244              :     XLogRecPtr  endptr;
     245              :     TimeLineID  endtli;
     246              :     backup_manifest_info manifest;
     247              :     BackupState *backup_state;
     248              :     StringInfoData tablespace_map;
     249              : 
     250              :     /* Initial backup state, insofar as we know it now. */
     251          169 :     state.tablespaces = NIL;
     252          169 :     state.tablespace_num = 0;
     253          169 :     state.bytes_done = 0;
     254          169 :     state.bytes_total = 0;
     255          169 :     state.bytes_total_is_valid = false;
     256              : 
     257              :     /* we're going to use a BufFile, so we need a ResourceOwner */
     258              :     Assert(AuxProcessResourceOwner != NULL);
     259              :     Assert(CurrentResourceOwner == AuxProcessResourceOwner ||
     260              :            CurrentResourceOwner == NULL);
     261          169 :     CurrentResourceOwner = AuxProcessResourceOwner;
     262              : 
     263          169 :     backup_started_in_recovery = RecoveryInProgress();
     264              : 
     265          169 :     InitializeBackupManifest(&manifest, opt->manifest,
     266              :                              opt->manifest_checksum_type);
     267              : 
     268          169 :     total_checksum_failures = 0;
     269              : 
     270              :     /* Allocate backup related variables. */
     271          169 :     backup_state = palloc0_object(BackupState);
     272          169 :     initStringInfo(&tablespace_map);
     273              : 
     274          169 :     basebackup_progress_wait_checkpoint();
     275          169 :     do_pg_backup_start(opt->label, opt->fastcheckpoint, &state.tablespaces,
     276              :                        backup_state, &tablespace_map);
     277              : 
     278          169 :     state.startptr = backup_state->startpoint;
     279          169 :     state.starttli = backup_state->starttli;
     280              : 
     281              :     /*
     282              :      * Once do_pg_backup_start has been called, ensure that any failure causes
     283              :      * us to abort the backup so we don't "leak" a backup counter. For this
     284              :      * reason, *all* functionality between do_pg_backup_start() and the end of
     285              :      * do_pg_backup_stop() should be inside the error cleanup block!
     286              :      */
     287              : 
     288          169 :     PG_ENSURE_ERROR_CLEANUP(do_pg_abort_backup, BoolGetDatum(false));
     289              :     {
     290              :         ListCell   *lc;
     291              :         tablespaceinfo *newti;
     292              : 
     293              :         /* If this is an incremental backup, execute preparatory steps. */
     294          169 :         if (ib != NULL)
     295           11 :             PrepareForIncrementalBackup(ib, backup_state);
     296              : 
     297              :         /* Add a node for the base directory at the end */
     298          169 :         newti = palloc0_object(tablespaceinfo);
     299          169 :         newti->size = -1;
     300          169 :         state.tablespaces = lappend(state.tablespaces, newti);
     301              : 
     302              :         /*
     303              :          * Calculate the total backup size by summing up the size of each
     304              :          * tablespace
     305              :          */
     306          169 :         if (opt->progress)
     307              :         {
     308          169 :             basebackup_progress_estimate_backup_size();
     309              : 
     310          375 :             foreach(lc, state.tablespaces)
     311              :             {
     312          206 :                 tablespaceinfo *tmp = (tablespaceinfo *) lfirst(lc);
     313              : 
     314          206 :                 if (tmp->path == NULL)
     315          169 :                     tmp->size = sendDir(sink, ".", 1, true, state.tablespaces,
     316              :                                         true, NULL, InvalidOid, NULL);
     317              :                 else
     318           37 :                     tmp->size = sendTablespace(sink, tmp->path, tmp->oid, true,
     319              :                                                NULL, NULL);
     320          206 :                 state.bytes_total += tmp->size;
     321              :             }
     322          169 :             state.bytes_total_is_valid = true;
     323              :         }
     324              : 
     325              :         /* notify basebackup sink about start of backup */
     326          169 :         bbsink_begin_backup(sink, &state, SINK_BUFFER_LENGTH);
     327              : 
     328              :         /* Send off our tablespaces one by one */
     329          370 :         foreach(lc, state.tablespaces)
     330              :         {
     331          206 :             tablespaceinfo *ti = (tablespaceinfo *) lfirst(lc);
     332              : 
     333          206 :             if (ti->path == NULL)
     334              :             {
     335              :                 struct stat statbuf;
     336          169 :                 bool        sendtblspclinks = true;
     337              :                 char       *backup_label;
     338              : 
     339          169 :                 bbsink_begin_archive(sink, "base.tar");
     340              : 
     341              :                 /* In the main tar, include the backup_label first... */
     342          169 :                 backup_label = build_backup_content(backup_state, false);
     343          169 :                 sendFileWithContent(sink, BACKUP_LABEL_FILE,
     344              :                                     backup_label, -1, &manifest);
     345          169 :                 pfree(backup_label);
     346              : 
     347              :                 /* Then the tablespace_map file, if required... */
     348          169 :                 if (opt->sendtblspcmapfile)
     349              :                 {
     350           27 :                     sendFileWithContent(sink, TABLESPACE_MAP,
     351           27 :                                         tablespace_map.data, -1, &manifest);
     352           27 :                     sendtblspclinks = false;
     353              :                 }
     354              : 
     355              :                 /* Then the bulk of the files... */
     356          169 :                 sendDir(sink, ".", 1, false, state.tablespaces,
     357              :                         sendtblspclinks, &manifest, InvalidOid, ib);
     358              : 
     359              :                 /* ... and pg_control after everything else. */
     360          164 :                 if (lstat(XLOG_CONTROL_FILE, &statbuf) != 0)
     361            0 :                     ereport(ERROR,
     362              :                             (errcode_for_file_access(),
     363              :                              errmsg("could not stat file \"%s\": %m",
     364              :                                     XLOG_CONTROL_FILE)));
     365          164 :                 sendFile(sink, XLOG_CONTROL_FILE, XLOG_CONTROL_FILE, &statbuf,
     366              :                          false, InvalidOid, InvalidOid,
     367              :                          InvalidRelFileNumber, 0, &manifest, 0, NULL, 0);
     368              :             }
     369              :             else
     370              :             {
     371           37 :                 char       *archive_name = psprintf("%u.tar", ti->oid);
     372              : 
     373           37 :                 bbsink_begin_archive(sink, archive_name);
     374              : 
     375           37 :                 sendTablespace(sink, ti->path, ti->oid, false, &manifest, ib);
     376              :             }
     377              : 
     378              :             /*
     379              :              * If we're including WAL, and this is the main data directory we
     380              :              * don't treat this as the end of the tablespace. Instead, we will
     381              :              * include the xlog files below and stop afterwards. This is safe
     382              :              * since the main data directory is always sent *last*.
     383              :              */
     384          201 :             if (opt->includewal && ti->path == NULL)
     385              :             {
     386              :                 Assert(lnext(state.tablespaces, lc) == NULL);
     387              :             }
     388              :             else
     389              :             {
     390              :                 /* Properly terminate the tarfile. */
     391          186 :                 memset(sink->bbs_buffer, 0, TAR_NUM_TERMINATION_BLOCKS * TAR_BLOCK_SIZE);
     392          186 :                 bbsink_archive_contents(sink, TAR_NUM_TERMINATION_BLOCKS * TAR_BLOCK_SIZE);
     393              : 
     394              :                 /* OK, that's the end of the archive. */
     395          186 :                 bbsink_end_archive(sink);
     396              :             }
     397              :         }
     398              : 
     399          164 :         basebackup_progress_wait_wal_archive(&state);
     400          164 :         do_pg_backup_stop(backup_state, !opt->nowait);
     401              : 
     402          164 :         endptr = backup_state->stoppoint;
     403          164 :         endtli = backup_state->stoptli;
     404              : 
     405              :         /* Deallocate backup-related variables. */
     406          164 :         pfree(tablespace_map.data);
     407          164 :         pfree(backup_state);
     408              :     }
     409          165 :     PG_END_ENSURE_ERROR_CLEANUP(do_pg_abort_backup, BoolGetDatum(false));
     410              : 
     411              : 
     412          164 :     if (opt->includewal)
     413              :     {
     414              :         /*
     415              :          * We've left the last tar file "open", so we can now append the
     416              :          * required WAL files to it.
     417              :          */
     418              :         char        pathbuf[MAXPGPATH];
     419              :         XLogSegNo   segno;
     420              :         XLogSegNo   startsegno;
     421              :         XLogSegNo   endsegno;
     422              :         struct stat statbuf;
     423           15 :         List       *historyFileList = NIL;
     424           15 :         List       *walFileList = NIL;
     425              :         char        firstoff[MAXFNAMELEN];
     426              :         char        lastoff[MAXFNAMELEN];
     427              :         DIR        *dir;
     428              :         struct dirent *de;
     429              :         ListCell   *lc;
     430              :         TimeLineID  tli;
     431              : 
     432           15 :         basebackup_progress_transfer_wal();
     433              : 
     434              :         /*
     435              :          * I'd rather not worry about timelines here, so scan pg_wal and
     436              :          * include all WAL files in the range between 'startptr' and 'endptr',
     437              :          * regardless of the timeline the file is stamped with. If there are
     438              :          * some spurious WAL files belonging to timelines that don't belong in
     439              :          * this server's history, they will be included too. Normally there
     440              :          * shouldn't be such files, but if there are, there's little harm in
     441              :          * including them.
     442              :          */
     443           15 :         XLByteToSeg(state.startptr, startsegno, wal_segment_size);
     444           15 :         XLogFileName(firstoff, state.starttli, startsegno, wal_segment_size);
     445           15 :         XLByteToPrevSeg(endptr, endsegno, wal_segment_size);
     446           15 :         XLogFileName(lastoff, endtli, endsegno, wal_segment_size);
     447              : 
     448           15 :         dir = AllocateDir("pg_wal");
     449          108 :         while ((de = ReadDir(dir, "pg_wal")) != NULL)
     450              :         {
     451              :             /* Does it look like a WAL segment, and is it in the range? */
     452           93 :             if (IsXLogFileName(de->d_name) &&
     453           33 :                 strcmp(de->d_name + 8, firstoff + 8) >= 0 &&
     454           33 :                 strcmp(de->d_name + 8, lastoff + 8) <= 0)
     455              :             {
     456           15 :                 walFileList = lappend(walFileList, pstrdup(de->d_name));
     457              :             }
     458              :             /* Does it look like a timeline history file? */
     459           78 :             else if (IsTLHistoryFileName(de->d_name))
     460              :             {
     461            0 :                 historyFileList = lappend(historyFileList, pstrdup(de->d_name));
     462              :             }
     463              :         }
     464           15 :         FreeDir(dir);
     465              : 
     466              :         /*
     467              :          * Before we go any further, check that none of the WAL segments we
     468              :          * need were removed.
     469              :          */
     470           15 :         CheckXLogRemoved(startsegno, state.starttli);
     471              : 
     472              :         /*
     473              :          * Sort the WAL filenames.  We want to send the files in order from
     474              :          * oldest to newest, to reduce the chance that a file is recycled
     475              :          * before we get a chance to send it over.
     476              :          */
     477           15 :         list_sort(walFileList, compareWalFileNames);
     478              : 
     479              :         /*
     480              :          * There must be at least one xlog file in the pg_wal directory, since
     481              :          * we are doing backup-including-xlog.
     482              :          */
     483           15 :         if (walFileList == NIL)
     484            0 :             ereport(ERROR,
     485              :                     (errmsg("could not find any WAL files")));
     486              : 
     487              :         /*
     488              :          * Sanity check: the first and last segment should cover startptr and
     489              :          * endptr, with no gaps in between.
     490              :          */
     491           15 :         XLogFromFileName((char *) linitial(walFileList),
     492              :                          &tli, &segno, wal_segment_size);
     493           15 :         if (segno != startsegno)
     494              :         {
     495              :             char        startfname[MAXFNAMELEN];
     496              : 
     497            0 :             XLogFileName(startfname, state.starttli, startsegno,
     498              :                          wal_segment_size);
     499            0 :             ereport(ERROR,
     500              :                     (errmsg("could not find WAL file \"%s\"", startfname)));
     501              :         }
     502           30 :         foreach(lc, walFileList)
     503              :         {
     504           15 :             char       *walFileName = (char *) lfirst(lc);
     505           15 :             XLogSegNo   currsegno = segno;
     506           15 :             XLogSegNo   nextsegno = segno + 1;
     507              : 
     508           15 :             XLogFromFileName(walFileName, &tli, &segno, wal_segment_size);
     509           15 :             if (!(nextsegno == segno || currsegno == segno))
     510              :             {
     511              :                 char        nextfname[MAXFNAMELEN];
     512              : 
     513            0 :                 XLogFileName(nextfname, tli, nextsegno, wal_segment_size);
     514            0 :                 ereport(ERROR,
     515              :                         (errmsg("could not find WAL file \"%s\"", nextfname)));
     516              :             }
     517              :         }
     518           15 :         if (segno != endsegno)
     519              :         {
     520              :             char        endfname[MAXFNAMELEN];
     521              : 
     522            0 :             XLogFileName(endfname, endtli, endsegno, wal_segment_size);
     523            0 :             ereport(ERROR,
     524              :                     (errmsg("could not find WAL file \"%s\"", endfname)));
     525              :         }
     526              : 
     527              :         /* Ok, we have everything we need. Send the WAL files. */
     528           30 :         foreach(lc, walFileList)
     529              :         {
     530           15 :             char       *walFileName = (char *) lfirst(lc);
     531              :             int         fd;
     532              :             ssize_t     cnt;
     533           15 :             pgoff_t     len = 0;
     534              : 
     535           15 :             snprintf(pathbuf, MAXPGPATH, XLOGDIR "/%s", walFileName);
     536           15 :             XLogFromFileName(walFileName, &tli, &segno, wal_segment_size);
     537              : 
     538           15 :             fd = OpenTransientFile(pathbuf, O_RDONLY | PG_BINARY);
     539           15 :             if (fd < 0)
     540              :             {
     541            0 :                 int         save_errno = errno;
     542              : 
     543              :                 /*
     544              :                  * Most likely reason for this is that the file was already
     545              :                  * removed by a checkpoint, so check for that to get a better
     546              :                  * error message.
     547              :                  */
     548            0 :                 CheckXLogRemoved(segno, tli);
     549              : 
     550            0 :                 errno = save_errno;
     551            0 :                 ereport(ERROR,
     552              :                         (errcode_for_file_access(),
     553              :                          errmsg("could not open file \"%s\": %m", pathbuf)));
     554              :             }
     555              : 
     556           15 :             if (fstat(fd, &statbuf) != 0)
     557            0 :                 ereport(ERROR,
     558              :                         (errcode_for_file_access(),
     559              :                          errmsg("could not stat file \"%s\": %m",
     560              :                                 pathbuf)));
     561           15 :             if (statbuf.st_size != wal_segment_size)
     562              :             {
     563            0 :                 CheckXLogRemoved(segno, tli);
     564            0 :                 ereport(ERROR,
     565              :                         (errcode_for_file_access(),
     566              :                          errmsg("unexpected WAL file size \"%s\"", walFileName)));
     567              :             }
     568              : 
     569              :             /* send the WAL file itself */
     570           15 :             _tarWriteHeader(sink, pathbuf, NULL, &statbuf, false);
     571              : 
     572           15 :             while ((cnt = basebackup_read_file(fd, sink->bbs_buffer,
     573         7680 :                                                Min(sink->bbs_buffer_length,
     574              :                                                    wal_segment_size - len),
     575         7680 :                                                len, pathbuf, true)) > 0)
     576              :             {
     577         7680 :                 CheckXLogRemoved(segno, tli);
     578         7680 :                 bbsink_archive_contents(sink, cnt);
     579              : 
     580         7680 :                 len += cnt;
     581              : 
     582         7680 :                 if (len == wal_segment_size)
     583           15 :                     break;
     584              :             }
     585              : 
     586           15 :             if (len != wal_segment_size)
     587              :             {
     588            0 :                 CheckXLogRemoved(segno, tli);
     589            0 :                 ereport(ERROR,
     590              :                         (errcode_for_file_access(),
     591              :                          errmsg("unexpected WAL file size \"%s\"", walFileName)));
     592              :             }
     593              : 
     594              :             /*
     595              :              * wal_segment_size is a multiple of TAR_BLOCK_SIZE, so no need
     596              :              * for padding.
     597              :              */
     598              :             Assert(wal_segment_size % TAR_BLOCK_SIZE == 0);
     599              : 
     600           15 :             CloseTransientFile(fd);
     601              : 
     602              :             /*
     603              :              * Mark file as archived, otherwise files can get archived again
     604              :              * after promotion of a new node. This is in line with
     605              :              * walreceiver.c always doing an XLogArchiveForceDone() after a
     606              :              * complete segment.
     607              :              */
     608           15 :             StatusFilePath(pathbuf, walFileName, ".done");
     609           15 :             sendFileWithContent(sink, pathbuf, "", -1, &manifest);
     610              :         }
     611              : 
     612              :         /*
     613              :          * Send timeline history files too. Only the latest timeline history
     614              :          * file is required for recovery, and even that only if there happens
     615              :          * to be a timeline switch in the first WAL segment that contains the
     616              :          * checkpoint record, or if we're taking a base backup from a standby
     617              :          * server and the target timeline changes while the backup is taken.
     618              :          * But they are small and highly useful for debugging purposes, so
     619              :          * better include them all, always.
     620              :          */
     621           15 :         foreach(lc, historyFileList)
     622              :         {
     623            0 :             char       *fname = lfirst(lc);
     624              : 
     625            0 :             snprintf(pathbuf, MAXPGPATH, XLOGDIR "/%s", fname);
     626              : 
     627            0 :             if (lstat(pathbuf, &statbuf) != 0)
     628            0 :                 ereport(ERROR,
     629              :                         (errcode_for_file_access(),
     630              :                          errmsg("could not stat file \"%s\": %m", pathbuf)));
     631              : 
     632            0 :             sendFile(sink, pathbuf, pathbuf, &statbuf, false,
     633              :                      InvalidOid, InvalidOid, InvalidRelFileNumber, 0,
     634              :                      &manifest, 0, NULL, 0);
     635              : 
     636              :             /* unconditionally mark file as archived */
     637            0 :             StatusFilePath(pathbuf, fname, ".done");
     638            0 :             sendFileWithContent(sink, pathbuf, "", -1, &manifest);
     639              :         }
     640              : 
     641              :         /* Properly terminate the tar file. */
     642           15 :         memset(sink->bbs_buffer, 0, TAR_NUM_TERMINATION_BLOCKS * TAR_BLOCK_SIZE);
     643           15 :         bbsink_archive_contents(sink, TAR_NUM_TERMINATION_BLOCKS * TAR_BLOCK_SIZE);
     644              : 
     645              :         /* OK, that's the end of the archive. */
     646           15 :         bbsink_end_archive(sink);
     647              :     }
     648              : 
     649          164 :     AddWALInfoToBackupManifest(&manifest, state.startptr, state.starttli,
     650              :                                endptr, endtli);
     651              : 
     652          164 :     SendBackupManifest(&manifest, sink);
     653              : 
     654          164 :     bbsink_end_backup(sink, endptr, endtli);
     655              : 
     656          164 :     if (total_checksum_failures)
     657              :     {
     658            3 :         if (total_checksum_failures > 1)
     659            2 :             ereport(WARNING,
     660              :                     (errmsg_plural("%lld total checksum verification failure",
     661              :                                    "%lld total checksum verification failures",
     662              :                                    total_checksum_failures,
     663              :                                    total_checksum_failures)));
     664              : 
     665            3 :         ereport(ERROR,
     666              :                 (errcode(ERRCODE_DATA_CORRUPTED),
     667              :                  errmsg("checksum verification failure during base backup")));
     668              :     }
     669              : 
     670              :     /*
     671              :      * Make sure to free the manifest before the resource owners as manifests
     672              :      * use cryptohash contexts that may depend on resource owners (like
     673              :      * OpenSSL).
     674              :      */
     675          161 :     FreeBackupManifest(&manifest);
     676              : 
     677              :     /* clean up the resource owner we created */
     678          161 :     ReleaseAuxProcessResources(true);
     679              : 
     680          161 :     basebackup_progress_done();
     681          161 : }
     682              : 
     683              : /*
     684              :  * list_sort comparison function, to compare log/seg portion of WAL segment
     685              :  * filenames, ignoring the timeline portion.
     686              :  */
     687              : static int
     688            0 : compareWalFileNames(const ListCell *a, const ListCell *b)
     689              : {
     690            0 :     char       *fna = (char *) lfirst(a);
     691            0 :     char       *fnb = (char *) lfirst(b);
     692              : 
     693            0 :     return strcmp(fna + 8, fnb + 8);
     694              : }
     695              : 
     696              : /*
     697              :  * Parse the base backup options passed down by the parser
     698              :  */
     699              : static void
     700          186 : parse_basebackup_options(List *options, basebackup_options *opt)
     701              : {
     702              :     ListCell   *lopt;
     703          186 :     bool        o_label = false;
     704          186 :     bool        o_progress = false;
     705          186 :     bool        o_checkpoint = false;
     706          186 :     bool        o_nowait = false;
     707          186 :     bool        o_wal = false;
     708          186 :     bool        o_incremental = false;
     709          186 :     bool        o_maxrate = false;
     710          186 :     bool        o_tablespace_map = false;
     711          186 :     bool        o_noverify_checksums = false;
     712          186 :     bool        o_manifest = false;
     713          186 :     bool        o_manifest_checksums = false;
     714          186 :     bool        o_target = false;
     715          186 :     bool        o_target_detail = false;
     716          186 :     char       *target_str = NULL;
     717          186 :     char       *target_detail_str = NULL;
     718          186 :     bool        o_compression = false;
     719          186 :     bool        o_compression_detail = false;
     720          186 :     char       *compression_detail_str = NULL;
     721              : 
     722         2046 :     MemSet(opt, 0, sizeof(*opt));
     723          186 :     opt->manifest = MANIFEST_OPTION_NO;
     724          186 :     opt->manifest_checksum_type = CHECKSUM_TYPE_CRC32C;
     725          186 :     opt->compression = PG_COMPRESSION_NONE;
     726          186 :     opt->compression_specification.algorithm = PG_COMPRESSION_NONE;
     727              : 
     728         1402 :     foreach(lopt, options)
     729              :     {
     730         1219 :         DefElem    *defel = (DefElem *) lfirst(lopt);
     731              : 
     732         1219 :         if (strcmp(defel->defname, "label") == 0)
     733              :         {
     734          186 :             if (o_label)
     735            0 :                 ereport(ERROR,
     736              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     737              :                          errmsg("duplicate option \"%s\"", defel->defname)));
     738          186 :             opt->label = defGetString(defel);
     739          186 :             o_label = true;
     740              :         }
     741         1033 :         else if (strcmp(defel->defname, "progress") == 0)
     742              :         {
     743          186 :             if (o_progress)
     744            0 :                 ereport(ERROR,
     745              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     746              :                          errmsg("duplicate option \"%s\"", defel->defname)));
     747          186 :             opt->progress = defGetBoolean(defel);
     748          186 :             o_progress = true;
     749              :         }
     750          847 :         else if (strcmp(defel->defname, "checkpoint") == 0)
     751              :         {
     752          176 :             char       *optval = defGetString(defel);
     753              : 
     754          176 :             if (o_checkpoint)
     755            0 :                 ereport(ERROR,
     756              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     757              :                          errmsg("duplicate option \"%s\"", defel->defname)));
     758          176 :             if (pg_strcasecmp(optval, "fast") == 0)
     759          176 :                 opt->fastcheckpoint = true;
     760            0 :             else if (pg_strcasecmp(optval, "spread") == 0)
     761            0 :                 opt->fastcheckpoint = false;
     762              :             else
     763            0 :                 ereport(ERROR,
     764              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     765              :                          errmsg("unrecognized checkpoint type: \"%s\"",
     766              :                                 optval)));
     767          176 :             o_checkpoint = true;
     768              :         }
     769          671 :         else if (strcmp(defel->defname, "wait") == 0)
     770              :         {
     771          177 :             if (o_nowait)
     772            0 :                 ereport(ERROR,
     773              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     774              :                          errmsg("duplicate option \"%s\"", defel->defname)));
     775          177 :             opt->nowait = !defGetBoolean(defel);
     776          177 :             o_nowait = true;
     777              :         }
     778          494 :         else if (strcmp(defel->defname, "wal") == 0)
     779              :         {
     780           19 :             if (o_wal)
     781            0 :                 ereport(ERROR,
     782              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     783              :                          errmsg("duplicate option \"%s\"", defel->defname)));
     784           19 :             opt->includewal = defGetBoolean(defel);
     785           19 :             o_wal = true;
     786              :         }
     787          475 :         else if (strcmp(defel->defname, "incremental") == 0)
     788              :         {
     789           11 :             if (o_incremental)
     790            0 :                 ereport(ERROR,
     791              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     792              :                          errmsg("duplicate option \"%s\"", defel->defname)));
     793           11 :             opt->incremental = defGetBoolean(defel);
     794           11 :             if (opt->incremental && !summarize_wal)
     795            0 :                 ereport(ERROR,
     796              :                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     797              :                          errmsg("incremental backups cannot be taken unless WAL summarization is enabled")));
     798           11 :             o_incremental = true;
     799              :         }
     800          464 :         else if (strcmp(defel->defname, "max_rate") == 0)
     801              :         {
     802              :             int64       maxrate;
     803              : 
     804            1 :             if (o_maxrate)
     805            0 :                 ereport(ERROR,
     806              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     807              :                          errmsg("duplicate option \"%s\"", defel->defname)));
     808              : 
     809            1 :             maxrate = defGetInt64(defel);
     810            1 :             if (maxrate < MAX_RATE_LOWER || maxrate > MAX_RATE_UPPER)
     811            0 :                 ereport(ERROR,
     812              :                         (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     813              :                          errmsg("%" PRId64 " is outside the valid range for parameter \"%s\" (%d .. %d)",
     814              :                                 maxrate, "MAX_RATE", MAX_RATE_LOWER, MAX_RATE_UPPER)));
     815              : 
     816            1 :             opt->maxrate = (uint32) maxrate;
     817            1 :             o_maxrate = true;
     818              :         }
     819          463 :         else if (strcmp(defel->defname, "tablespace_map") == 0)
     820              :         {
     821           33 :             if (o_tablespace_map)
     822            0 :                 ereport(ERROR,
     823              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     824              :                          errmsg("duplicate option \"%s\"", defel->defname)));
     825           33 :             opt->sendtblspcmapfile = defGetBoolean(defel);
     826           33 :             o_tablespace_map = true;
     827              :         }
     828          430 :         else if (strcmp(defel->defname, "verify_checksums") == 0)
     829              :         {
     830            1 :             if (o_noverify_checksums)
     831            0 :                 ereport(ERROR,
     832              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     833              :                          errmsg("duplicate option \"%s\"", defel->defname)));
     834            1 :             noverify_checksums = !defGetBoolean(defel);
     835            1 :             o_noverify_checksums = true;
     836              :         }
     837          429 :         else if (strcmp(defel->defname, "manifest") == 0)
     838              :         {
     839          185 :             char       *optval = defGetString(defel);
     840              :             bool        manifest_bool;
     841              : 
     842          185 :             if (o_manifest)
     843            0 :                 ereport(ERROR,
     844              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     845              :                          errmsg("duplicate option \"%s\"", defel->defname)));
     846          185 :             if (parse_bool(optval, &manifest_bool))
     847              :             {
     848          184 :                 if (manifest_bool)
     849          184 :                     opt->manifest = MANIFEST_OPTION_YES;
     850              :                 else
     851            0 :                     opt->manifest = MANIFEST_OPTION_NO;
     852              :             }
     853            1 :             else if (pg_strcasecmp(optval, "force-encode") == 0)
     854            1 :                 opt->manifest = MANIFEST_OPTION_FORCE_ENCODE;
     855              :             else
     856            0 :                 ereport(ERROR,
     857              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     858              :                          errmsg("unrecognized manifest option: \"%s\"",
     859              :                                 optval)));
     860          185 :             o_manifest = true;
     861              :         }
     862          244 :         else if (strcmp(defel->defname, "manifest_checksums") == 0)
     863              :         {
     864           14 :             char       *optval = defGetString(defel);
     865              : 
     866           14 :             if (o_manifest_checksums)
     867            0 :                 ereport(ERROR,
     868              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     869              :                          errmsg("duplicate option \"%s\"", defel->defname)));
     870           14 :             if (!pg_checksum_parse_type(optval,
     871              :                                         &opt->manifest_checksum_type))
     872            2 :                 ereport(ERROR,
     873              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     874              :                          errmsg("unrecognized checksum algorithm: \"%s\"",
     875              :                                 optval)));
     876           12 :             o_manifest_checksums = true;
     877              :         }
     878          230 :         else if (strcmp(defel->defname, "target") == 0)
     879              :         {
     880          184 :             if (o_target)
     881            0 :                 ereport(ERROR,
     882              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     883              :                          errmsg("duplicate option \"%s\"", defel->defname)));
     884          184 :             target_str = defGetString(defel);
     885          184 :             o_target = true;
     886              :         }
     887           46 :         else if (strcmp(defel->defname, "target_detail") == 0)
     888              :         {
     889            8 :             char       *optval = defGetString(defel);
     890              : 
     891            8 :             if (o_target_detail)
     892            0 :                 ereport(ERROR,
     893              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     894              :                          errmsg("duplicate option \"%s\"", defel->defname)));
     895            8 :             target_detail_str = optval;
     896            8 :             o_target_detail = true;
     897              :         }
     898           38 :         else if (strcmp(defel->defname, "compression") == 0)
     899              :         {
     900           26 :             char       *optval = defGetString(defel);
     901              : 
     902           26 :             if (o_compression)
     903            0 :                 ereport(ERROR,
     904              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     905              :                          errmsg("duplicate option \"%s\"", defel->defname)));
     906           26 :             if (!parse_compress_algorithm(optval, &opt->compression))
     907            1 :                 ereport(ERROR,
     908              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     909              :                          errmsg("unrecognized compression algorithm: \"%s\"",
     910              :                                 optval)));
     911           25 :             o_compression = true;
     912              :         }
     913           12 :         else if (strcmp(defel->defname, "compression_detail") == 0)
     914              :         {
     915           12 :             if (o_compression_detail)
     916            0 :                 ereport(ERROR,
     917              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     918              :                          errmsg("duplicate option \"%s\"", defel->defname)));
     919           12 :             compression_detail_str = defGetString(defel);
     920           12 :             o_compression_detail = true;
     921              :         }
     922              :         else
     923            0 :             ereport(ERROR,
     924              :                     (errcode(ERRCODE_SYNTAX_ERROR),
     925              :                      errmsg("unrecognized base backup option: \"%s\"",
     926              :                             defel->defname)));
     927              :     }
     928              : 
     929          183 :     if (opt->label == NULL)
     930            0 :         opt->label = "base backup";
     931          183 :     if (opt->manifest == MANIFEST_OPTION_NO)
     932              :     {
     933            1 :         if (o_manifest_checksums)
     934            0 :             ereport(ERROR,
     935              :                     (errcode(ERRCODE_SYNTAX_ERROR),
     936              :                      errmsg("manifest checksums require a backup manifest")));
     937            1 :         opt->manifest_checksum_type = CHECKSUM_TYPE_NONE;
     938              :     }
     939              : 
     940          183 :     if (target_str == NULL)
     941              :     {
     942            0 :         if (target_detail_str != NULL)
     943            0 :             ereport(ERROR,
     944              :                     (errcode(ERRCODE_SYNTAX_ERROR),
     945              :                      errmsg("target detail cannot be used without target")));
     946            0 :         opt->use_copytblspc = true;
     947            0 :         opt->send_to_client = true;
     948              :     }
     949          183 :     else if (strcmp(target_str, "client") == 0)
     950              :     {
     951          169 :         if (target_detail_str != NULL)
     952            0 :             ereport(ERROR,
     953              :                     (errcode(ERRCODE_SYNTAX_ERROR),
     954              :                      errmsg("target \"%s\" does not accept a target detail",
     955              :                             target_str)));
     956          169 :         opt->send_to_client = true;
     957              :     }
     958              :     else
     959           12 :         opt->target_handle =
     960           14 :             BaseBackupGetTargetHandle(target_str, target_detail_str);
     961              : 
     962          181 :     if (o_compression_detail && !o_compression)
     963            0 :         ereport(ERROR,
     964              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     965              :                  errmsg("compression detail cannot be specified unless compression is enabled")));
     966              : 
     967          181 :     if (o_compression)
     968              :     {
     969              :         char       *error_detail;
     970              : 
     971           23 :         parse_compress_specification(opt->compression, compression_detail_str,
     972              :                                      &opt->compression_specification);
     973              :         error_detail =
     974           23 :             validate_compress_specification(&opt->compression_specification);
     975           23 :         if (error_detail != NULL)
     976            9 :             ereport(ERROR,
     977              :                     errcode(ERRCODE_SYNTAX_ERROR),
     978              :                     errmsg("invalid compression specification: %s",
     979              :                            error_detail));
     980              :     }
     981          172 : }
     982              : 
     983              : 
     984              : /*
     985              :  * SendBaseBackup() - send a complete base backup.
     986              :  *
     987              :  * The function will put the system into backup mode like pg_backup_start()
     988              :  * does, so that the backup is consistent even though we read directly from
     989              :  * the filesystem, bypassing the buffer cache.
     990              :  */
     991              : void
     992          187 : SendBaseBackup(BaseBackupCmd *cmd, IncrementalBackupInfo *ib)
     993              : {
     994              :     basebackup_options opt;
     995              :     bbsink     *sink;
     996          187 :     SessionBackupState status = get_backup_status();
     997              : 
     998          187 :     if (status == SESSION_BACKUP_RUNNING)
     999            1 :         ereport(ERROR,
    1000              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1001              :                  errmsg("a backup is already in progress in this session")));
    1002              : 
    1003          186 :     parse_basebackup_options(cmd->options, &opt);
    1004              : 
    1005          172 :     WalSndSetState(WALSNDSTATE_BACKUP);
    1006              : 
    1007          172 :     if (update_process_title)
    1008              :     {
    1009              :         char        activitymsg[50];
    1010              : 
    1011          172 :         snprintf(activitymsg, sizeof(activitymsg), "sending backup \"%s\"",
    1012              :                  opt.label);
    1013          172 :         set_ps_display(activitymsg);
    1014              :     }
    1015              : 
    1016              :     /*
    1017              :      * If we're asked to perform an incremental backup and the user has not
    1018              :      * supplied a manifest, that's an ERROR.
    1019              :      *
    1020              :      * If we're asked to perform a full backup and the user did supply a
    1021              :      * manifest, just ignore it.
    1022              :      */
    1023          172 :     if (!opt.incremental)
    1024          161 :         ib = NULL;
    1025           11 :     else if (ib == NULL)
    1026            0 :         ereport(ERROR,
    1027              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1028              :                  errmsg("must UPLOAD_MANIFEST before performing an incremental BASE_BACKUP")));
    1029              : 
    1030              :     /*
    1031              :      * If the target is specifically 'client' then set up to stream the backup
    1032              :      * to the client; otherwise, it's being sent someplace else and should not
    1033              :      * be sent to the client. BaseBackupGetSink has the job of setting up a
    1034              :      * sink to send the backup data wherever it needs to go.
    1035              :      */
    1036          172 :     sink = bbsink_copystream_new(opt.send_to_client);
    1037          172 :     if (opt.target_handle != NULL)
    1038           12 :         sink = BaseBackupGetSink(opt.target_handle, sink);
    1039              : 
    1040              :     /* Set up network throttling, if client requested it */
    1041          169 :     if (opt.maxrate > 0)
    1042            1 :         sink = bbsink_throttle_new(sink, opt.maxrate);
    1043              : 
    1044              :     /* Set up server-side compression, if client requested it */
    1045          169 :     if (opt.compression == PG_COMPRESSION_GZIP)
    1046            2 :         sink = bbsink_gzip_new(sink, &opt.compression_specification);
    1047          167 :     else if (opt.compression == PG_COMPRESSION_LZ4)
    1048            3 :         sink = bbsink_lz4_new(sink, &opt.compression_specification);
    1049          164 :     else if (opt.compression == PG_COMPRESSION_ZSTD)
    1050            0 :         sink = bbsink_zstd_new(sink, &opt.compression_specification);
    1051              : 
    1052              :     /* Set up progress reporting. */
    1053          169 :     sink = bbsink_progress_new(sink, opt.progress, opt.incremental);
    1054              : 
    1055              :     /*
    1056              :      * Perform the base backup, but make sure we clean up the bbsink even if
    1057              :      * an error occurs.
    1058              :      */
    1059          169 :     PG_TRY();
    1060              :     {
    1061          169 :         perform_base_backup(&opt, sink, ib);
    1062              :     }
    1063            4 :     PG_FINALLY();
    1064              :     {
    1065          165 :         bbsink_cleanup(sink);
    1066              :     }
    1067          165 :     PG_END_TRY();
    1068          161 : }
    1069              : 
    1070              : /*
    1071              :  * Inject a file with given name and content in the output tar stream.
    1072              :  *
    1073              :  * "len" can optionally be set to an arbitrary length of data sent.  If set
    1074              :  * to -1, the content sent is treated as a string with strlen() as length.
    1075              :  */
    1076              : static void
    1077          211 : sendFileWithContent(bbsink *sink, const char *filename, const char *content,
    1078              :                     int len, backup_manifest_info *manifest)
    1079              : {
    1080              :     struct stat statbuf;
    1081          211 :     int         bytes_done = 0;
    1082              :     pg_checksum_context checksum_ctx;
    1083              : 
    1084          211 :     if (pg_checksum_init(&checksum_ctx, manifest->checksum_type) < 0)
    1085            0 :         elog(ERROR, "could not initialize checksum of file \"%s\"",
    1086              :              filename);
    1087              : 
    1088          211 :     if (len < 0)
    1089          211 :         len = strlen(content);
    1090              : 
    1091              :     /*
    1092              :      * Construct a stat struct for the file we're injecting in the tar.
    1093              :      */
    1094              : 
    1095              :     /* Windows doesn't have the concept of uid and gid */
    1096              : #ifdef WIN32
    1097              :     statbuf.st_uid = 0;
    1098              :     statbuf.st_gid = 0;
    1099              : #else
    1100          211 :     statbuf.st_uid = geteuid();
    1101          211 :     statbuf.st_gid = getegid();
    1102              : #endif
    1103          211 :     statbuf.st_mtime = time(NULL);
    1104          211 :     statbuf.st_mode = pg_file_create_mode;
    1105          211 :     statbuf.st_size = len;
    1106              : 
    1107          211 :     _tarWriteHeader(sink, filename, NULL, &statbuf, false);
    1108              : 
    1109          211 :     if (pg_checksum_update(&checksum_ctx, (const uint8 *) content, len) < 0)
    1110            0 :         elog(ERROR, "could not update checksum of file \"%s\"",
    1111              :              filename);
    1112              : 
    1113          386 :     while (bytes_done < len)
    1114              :     {
    1115          175 :         size_t      remaining = len - bytes_done;
    1116          175 :         size_t      nbytes = Min(sink->bbs_buffer_length, remaining);
    1117              : 
    1118          175 :         memcpy(sink->bbs_buffer, content, nbytes);
    1119          175 :         bbsink_archive_contents(sink, nbytes);
    1120          175 :         bytes_done += nbytes;
    1121          175 :         content += nbytes;
    1122              :     }
    1123              : 
    1124          211 :     _tarWritePadding(sink, len);
    1125              : 
    1126          211 :     AddFileToBackupManifest(manifest, InvalidOid, filename, len,
    1127          211 :                             (pg_time_t) statbuf.st_mtime, &checksum_ctx);
    1128          211 : }
    1129              : 
    1130              : /*
    1131              :  * Include the tablespace directory pointed to by 'path' in the output tar
    1132              :  * stream.  If 'sizeonly' is true, we just calculate a total length and return
    1133              :  * it, without actually sending anything.
    1134              :  *
    1135              :  * Only used to send auxiliary tablespaces, not PGDATA.
    1136              :  */
    1137              : static int64
    1138           74 : sendTablespace(bbsink *sink, char *path, Oid spcoid, bool sizeonly,
    1139              :                backup_manifest_info *manifest, IncrementalBackupInfo *ib)
    1140              : {
    1141              :     int64       size;
    1142              :     char        pathbuf[MAXPGPATH];
    1143              :     struct stat statbuf;
    1144              : 
    1145              :     /*
    1146              :      * 'path' points to the tablespace location, but we only want to include
    1147              :      * the version directory in it that belongs to us.
    1148              :      */
    1149           74 :     snprintf(pathbuf, sizeof(pathbuf), "%s/%s", path,
    1150              :              TABLESPACE_VERSION_DIRECTORY);
    1151              : 
    1152              :     /*
    1153              :      * Store a directory entry in the tar file so we get the permissions
    1154              :      * right.
    1155              :      */
    1156           74 :     if (lstat(pathbuf, &statbuf) != 0)
    1157              :     {
    1158            0 :         if (errno != ENOENT)
    1159            0 :             ereport(ERROR,
    1160              :                     (errcode_for_file_access(),
    1161              :                      errmsg("could not stat file or directory \"%s\": %m",
    1162              :                             pathbuf)));
    1163              : 
    1164              :         /* If the tablespace went away while scanning, it's no error. */
    1165            0 :         return 0;
    1166              :     }
    1167              : 
    1168           74 :     size = _tarWriteHeader(sink, TABLESPACE_VERSION_DIRECTORY, NULL, &statbuf,
    1169              :                            sizeonly);
    1170              : 
    1171              :     /* Send all the files in the tablespace version directory */
    1172           74 :     size += sendDir(sink, pathbuf, strlen(path), sizeonly, NIL, true, manifest,
    1173              :                     spcoid, ib);
    1174              : 
    1175           74 :     return size;
    1176              : }
    1177              : 
    1178              : /*
    1179              :  * Include all files from the given directory in the output tar stream. If
    1180              :  * 'sizeonly' is true, we just calculate a total length and return it, without
    1181              :  * actually sending anything.
    1182              :  *
    1183              :  * Omit any directory in the tablespaces list, to avoid backing up
    1184              :  * tablespaces twice when they were created inside PGDATA.
    1185              :  *
    1186              :  * If sendtblspclinks is true, we need to include symlink
    1187              :  * information in the tar file. If not, we can skip that
    1188              :  * as it will be sent separately in the tablespace_map file.
    1189              :  */
    1190              : static int64
    1191         5855 : sendDir(bbsink *sink, const char *path, int basepathlen, bool sizeonly,
    1192              :         List *tablespaces, bool sendtblspclinks, backup_manifest_info *manifest,
    1193              :         Oid spcoid, IncrementalBackupInfo *ib)
    1194              : {
    1195              :     DIR        *dir;
    1196              :     struct dirent *de;
    1197              :     char        pathbuf[MAXPGPATH * 2];
    1198              :     struct stat statbuf;
    1199         5855 :     int64       size = 0;
    1200              :     const char *lastDir;        /* Split last dir from parent path. */
    1201         5855 :     bool        isRelationDir = false;  /* Does directory contain relations? */
    1202         5855 :     bool        isGlobalDir = false;
    1203         5855 :     Oid         dboid = InvalidOid;
    1204         5855 :     BlockNumber *relative_block_numbers = NULL;
    1205              : 
    1206              :     /*
    1207              :      * Since this array is relatively large, avoid putting it on the stack.
    1208              :      * But we don't need it at all if this is not an incremental backup.
    1209              :      */
    1210         5855 :     if (ib != NULL)
    1211          191 :         relative_block_numbers = palloc_array(BlockNumber, RELSEG_SIZE);
    1212              : 
    1213              :     /*
    1214              :      * Determine if the current path is a database directory that can contain
    1215              :      * relations.
    1216              :      *
    1217              :      * Start by finding the location of the delimiter between the parent path
    1218              :      * and the current path.
    1219              :      */
    1220         5855 :     lastDir = last_dir_separator(path);
    1221              : 
    1222              :     /* Does this path look like a database path (i.e. all digits)? */
    1223         5855 :     if (lastDir != NULL &&
    1224         5517 :         strspn(lastDir + 1, "0123456789") == strlen(lastDir + 1))
    1225         1085 :     {
    1226              :         /* Part of path that contains the parent directory. */
    1227         1085 :         int         parentPathLen = lastDir - path;
    1228              : 
    1229              :         /*
    1230              :          * Mark path as a database directory if the parent path is either
    1231              :          * $PGDATA/base or a tablespace version path.
    1232              :          */
    1233         1085 :         if (strncmp(path, "./base", parentPathLen) == 0 ||
    1234           52 :             (parentPathLen >= (sizeof(TABLESPACE_VERSION_DIRECTORY) - 1) &&
    1235           52 :              strncmp(lastDir - (sizeof(TABLESPACE_VERSION_DIRECTORY) - 1),
    1236              :                      TABLESPACE_VERSION_DIRECTORY,
    1237              :                      sizeof(TABLESPACE_VERSION_DIRECTORY) - 1) == 0))
    1238              :         {
    1239         1085 :             isRelationDir = true;
    1240         1085 :             dboid = atooid(lastDir + 1);
    1241              :         }
    1242              :     }
    1243         4770 :     else if (strcmp(path, "./global") == 0)
    1244              :     {
    1245          336 :         isRelationDir = true;
    1246          336 :         isGlobalDir = true;
    1247              :     }
    1248              : 
    1249         5855 :     dir = AllocateDir(path);
    1250       382258 :     while ((de = ReadDir(dir, path)) != NULL)
    1251              :     {
    1252              :         int         excludeIdx;
    1253              :         bool        excludeFound;
    1254       376411 :         RelFileNumber relfilenumber = InvalidRelFileNumber;
    1255       376411 :         ForkNumber  relForkNum = InvalidForkNumber;
    1256       376411 :         unsigned    segno = 0;
    1257       376411 :         bool        isRelationFile = false;
    1258              : 
    1259              :         /* Skip special stuff */
    1260       376411 :         if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
    1261        16542 :             continue;
    1262              : 
    1263              :         /* Skip temporary files */
    1264       364715 :         if (strncmp(de->d_name,
    1265              :                     PG_TEMP_FILE_PREFIX,
    1266              :                     strlen(PG_TEMP_FILE_PREFIX)) == 0)
    1267          333 :             continue;
    1268              : 
    1269              :         /* Skip macOS system files */
    1270       364382 :         if (strcmp(de->d_name, ".DS_Store") == 0)
    1271           70 :             continue;
    1272              : 
    1273              :         /*
    1274              :          * Check if the postmaster has signaled us to exit, and abort with an
    1275              :          * error in that case. The error handler further up will call
    1276              :          * do_pg_abort_backup() for us. Also check that if the backup was
    1277              :          * started while still in recovery, the server wasn't promoted.
    1278              :          * do_pg_backup_stop() will check that too, but it's better to stop
    1279              :          * the backup early than continue to the end and fail there.
    1280              :          */
    1281       364312 :         CHECK_FOR_INTERRUPTS();
    1282       364308 :         if (RecoveryInProgress() != backup_started_in_recovery)
    1283            0 :             ereport(ERROR,
    1284              :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1285              :                      errmsg("the standby was promoted during online backup"),
    1286              :                      errhint("This means that the backup being taken is corrupt "
    1287              :                              "and should not be used. "
    1288              :                              "Try taking another online backup.")));
    1289              : 
    1290              :         /* Scan for files that should be excluded */
    1291       364308 :         excludeFound = false;
    1292      3273676 :         for (excludeIdx = 0; excludeFiles[excludeIdx].name != NULL; excludeIdx++)
    1293              :         {
    1294      2910698 :             int         cmplen = strlen(excludeFiles[excludeIdx].name);
    1295              : 
    1296      2910698 :             if (!excludeFiles[excludeIdx].match_prefix)
    1297      2546520 :                 cmplen++;
    1298      2910698 :             if (strncmp(de->d_name, excludeFiles[excludeIdx].name, cmplen) == 0)
    1299              :             {
    1300         1330 :                 elog(DEBUG1, "file \"%s\" excluded from backup", de->d_name);
    1301         1330 :                 excludeFound = true;
    1302         1330 :                 break;
    1303              :             }
    1304              :         }
    1305              : 
    1306       364308 :         if (excludeFound)
    1307         1330 :             continue;
    1308              : 
    1309              :         /*
    1310              :          * If there could be non-temporary relation files in this directory,
    1311              :          * try to parse the filename.
    1312              :          */
    1313       362978 :         if (isRelationDir)
    1314              :             isRelationFile =
    1315       351248 :                 parse_filename_for_nontemp_relation(de->d_name,
    1316              :                                                     &relfilenumber,
    1317              :                                                     &relForkNum, &segno);
    1318              : 
    1319              :         /* Exclude all forks for unlogged tables except the init fork */
    1320       362978 :         if (isRelationFile && relForkNum != INIT_FORKNUM)
    1321              :         {
    1322              :             char        initForkFile[MAXPGPATH];
    1323              : 
    1324              :             /*
    1325              :              * If any other type of fork, check if there is an init fork with
    1326              :              * the same RelFileNumber. If so, the file can be excluded.
    1327              :              */
    1328       348405 :             snprintf(initForkFile, sizeof(initForkFile), "%s/%u_init",
    1329              :                      path, relfilenumber);
    1330              : 
    1331       348405 :             if (lstat(initForkFile, &statbuf) == 0)
    1332              :             {
    1333           69 :                 elog(DEBUG2,
    1334              :                      "unlogged relation file \"%s\" excluded from backup",
    1335              :                      de->d_name);
    1336              : 
    1337           69 :                 continue;
    1338              :             }
    1339              :         }
    1340              : 
    1341              :         /* Exclude temporary relations */
    1342       362909 :         if (OidIsValid(dboid) && looks_like_temp_rel_name(de->d_name))
    1343              :         {
    1344           36 :             elog(DEBUG2,
    1345              :                  "temporary relation file \"%s\" excluded from backup",
    1346              :                  de->d_name);
    1347              : 
    1348           36 :             continue;
    1349              :         }
    1350              : 
    1351       362873 :         snprintf(pathbuf, sizeof(pathbuf), "%s/%s", path, de->d_name);
    1352              : 
    1353              :         /* Skip pg_control here to back up it last */
    1354       362873 :         if (strcmp(pathbuf, "./" XLOG_CONTROL_FILE) == 0)
    1355          334 :             continue;
    1356              : 
    1357       362539 :         if (lstat(pathbuf, &statbuf) != 0)
    1358              :         {
    1359            0 :             if (errno != ENOENT)
    1360            0 :                 ereport(ERROR,
    1361              :                         (errcode_for_file_access(),
    1362              :                          errmsg("could not stat file or directory \"%s\": %m",
    1363              :                                 pathbuf)));
    1364              : 
    1365              :             /* If the file went away while scanning, it's not an error. */
    1366            0 :             continue;
    1367              :         }
    1368              : 
    1369              :         /* Scan for directories whose contents should be excluded */
    1370       362539 :         excludeFound = false;
    1371      2890933 :         for (excludeIdx = 0; excludeDirContents[excludeIdx] != NULL; excludeIdx++)
    1372              :         {
    1373      2530735 :             if (strcmp(de->d_name, excludeDirContents[excludeIdx]) == 0)
    1374              :             {
    1375         2341 :                 elog(DEBUG1, "contents of directory \"%s\" excluded from backup", de->d_name);
    1376         2341 :                 convert_link_to_directory(pathbuf, &statbuf);
    1377         2341 :                 size += _tarWriteHeader(sink, pathbuf + basepathlen + 1, NULL,
    1378              :                                         &statbuf, sizeonly);
    1379         2341 :                 excludeFound = true;
    1380         2341 :                 break;
    1381              :             }
    1382              :         }
    1383              : 
    1384       362539 :         if (excludeFound)
    1385         2341 :             continue;
    1386              : 
    1387              :         /*
    1388              :          * We can skip pg_wal, the WAL segments need to be fetched from the
    1389              :          * WAL archive anyway. But include it as an empty directory anyway, so
    1390              :          * we get permissions right.
    1391              :          */
    1392       360198 :         if (strcmp(pathbuf, "./pg_wal") == 0)
    1393              :         {
    1394              :             /* If pg_wal is a symlink, write it as a directory anyway */
    1395          333 :             convert_link_to_directory(pathbuf, &statbuf);
    1396          333 :             size += _tarWriteHeader(sink, pathbuf + basepathlen + 1, NULL,
    1397              :                                     &statbuf, sizeonly);
    1398              : 
    1399              :             /*
    1400              :              * Also send archive_status and summaries directories (by
    1401              :              * hackishly reusing statbuf from above ...).
    1402              :              */
    1403          333 :             size += _tarWriteHeader(sink, "./pg_wal/archive_status", NULL,
    1404              :                                     &statbuf, sizeonly);
    1405          333 :             size += _tarWriteHeader(sink, "./pg_wal/summaries", NULL,
    1406              :                                     &statbuf, sizeonly);
    1407              : 
    1408          333 :             continue;           /* don't recurse into pg_wal */
    1409              :         }
    1410              : 
    1411              :         /* Allow symbolic links in pg_tblspc only */
    1412       359865 :         if (strcmp(path, "./pg_tblspc") == 0 && S_ISLNK(statbuf.st_mode))
    1413           39 :         {
    1414              :             char        linkpath[MAXPGPATH];
    1415              :             int         rllen;
    1416              : 
    1417           39 :             rllen = readlink(pathbuf, linkpath, sizeof(linkpath));
    1418           39 :             if (rllen < 0)
    1419            0 :                 ereport(ERROR,
    1420              :                         (errcode_for_file_access(),
    1421              :                          errmsg("could not read symbolic link \"%s\": %m",
    1422              :                                 pathbuf)));
    1423           39 :             if (rllen >= sizeof(linkpath))
    1424            0 :                 ereport(ERROR,
    1425              :                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1426              :                          errmsg("symbolic link \"%s\" target is too long",
    1427              :                                 pathbuf)));
    1428           39 :             linkpath[rllen] = '\0';
    1429              : 
    1430           39 :             size += _tarWriteHeader(sink, pathbuf + basepathlen + 1, linkpath,
    1431              :                                     &statbuf, sizeonly);
    1432              :         }
    1433       359826 :         else if (S_ISDIR(statbuf.st_mode))
    1434              :         {
    1435         5497 :             bool        skip_this_dir = false;
    1436              :             ListCell   *lc;
    1437              : 
    1438              :             /*
    1439              :              * Store a directory entry in the tar file so we can get the
    1440              :              * permissions right.
    1441              :              */
    1442         5497 :             size += _tarWriteHeader(sink, pathbuf + basepathlen + 1, NULL, &statbuf,
    1443              :                                     sizeonly);
    1444              : 
    1445              :             /*
    1446              :              * Call ourselves recursively for a directory, unless it happens
    1447              :              * to be a separate tablespace located within PGDATA.
    1448              :              */
    1449        12136 :             foreach(lc, tablespaces)
    1450              :             {
    1451         6667 :                 tablespaceinfo *ti = (tablespaceinfo *) lfirst(lc);
    1452              : 
    1453              :                 /*
    1454              :                  * ti->rpath is the tablespace relative path within PGDATA, or
    1455              :                  * NULL if the tablespace has been properly located somewhere
    1456              :                  * else.
    1457              :                  *
    1458              :                  * Skip past the leading "./" in pathbuf when comparing.
    1459              :                  */
    1460         6667 :                 if (ti->rpath && strcmp(ti->rpath, pathbuf + 2) == 0)
    1461              :                 {
    1462           28 :                     skip_this_dir = true;
    1463           28 :                     break;
    1464              :                 }
    1465              :             }
    1466              : 
    1467              :             /*
    1468              :              * skip sending directories inside pg_tblspc, if not required.
    1469              :              */
    1470         5497 :             if (strcmp(pathbuf, "./pg_tblspc") == 0 && !sendtblspclinks)
    1471           26 :                 skip_this_dir = true;
    1472              : 
    1473         5497 :             if (!skip_this_dir)
    1474         5443 :                 size += sendDir(sink, pathbuf, basepathlen, sizeonly, tablespaces,
    1475              :                                 sendtblspclinks, manifest, spcoid, ib);
    1476              :         }
    1477       354329 :         else if (S_ISREG(statbuf.st_mode))
    1478              :         {
    1479       354329 :             bool        sent = false;
    1480       354329 :             unsigned    num_blocks_required = 0;
    1481       354329 :             unsigned    truncation_block_length = 0;
    1482              :             char        tarfilenamebuf[MAXPGPATH * 2];
    1483       354329 :             char       *tarfilename = pathbuf + basepathlen + 1;
    1484       354329 :             FileBackupMethod method = BACK_UP_FILE_FULLY;
    1485              : 
    1486       354329 :             if (ib != NULL && isRelationFile)
    1487              :             {
    1488              :                 Oid         relspcoid;
    1489              :                 char       *lookup_path;
    1490              : 
    1491        11795 :                 if (OidIsValid(spcoid))
    1492              :                 {
    1493            9 :                     relspcoid = spcoid;
    1494            9 :                     lookup_path = psprintf("%s/%u/%s", PG_TBLSPC_DIR, spcoid,
    1495              :                                            tarfilename);
    1496              :                 }
    1497              :                 else
    1498              :                 {
    1499        11786 :                     if (isGlobalDir)
    1500          616 :                         relspcoid = GLOBALTABLESPACE_OID;
    1501              :                     else
    1502        11170 :                         relspcoid = DEFAULTTABLESPACE_OID;
    1503        11786 :                     lookup_path = pstrdup(tarfilename);
    1504              :                 }
    1505              : 
    1506        11795 :                 method = GetFileBackupMethod(ib, lookup_path, dboid, relspcoid,
    1507              :                                              relfilenumber, relForkNum,
    1508        11795 :                                              segno, statbuf.st_size,
    1509              :                                              &num_blocks_required,
    1510              :                                              relative_block_numbers,
    1511              :                                              &truncation_block_length);
    1512        11795 :                 if (method == BACK_UP_FILE_INCREMENTALLY)
    1513              :                 {
    1514         7756 :                     statbuf.st_size =
    1515         7756 :                         GetIncrementalFileSize(num_blocks_required);
    1516         7756 :                     snprintf(tarfilenamebuf, sizeof(tarfilenamebuf),
    1517              :                              "%s/INCREMENTAL.%s",
    1518         7756 :                              path + basepathlen + 1,
    1519         7756 :                              de->d_name);
    1520         7756 :                     tarfilename = tarfilenamebuf;
    1521              :                 }
    1522              : 
    1523        11795 :                 pfree(lookup_path);
    1524              :             }
    1525              : 
    1526       354329 :             if (!sizeonly)
    1527       174651 :                 sent = sendFile(sink, pathbuf, tarfilename, &statbuf,
    1528              :                                 true, dboid, spcoid,
    1529              :                                 relfilenumber, segno, manifest,
    1530              :                                 num_blocks_required,
    1531              :                                 method == BACK_UP_FILE_INCREMENTALLY ? relative_block_numbers : NULL,
    1532              :                                 truncation_block_length);
    1533              : 
    1534       354328 :             if (sent || sizeonly)
    1535              :             {
    1536              :                 /* Add size. */
    1537       354328 :                 size += statbuf.st_size;
    1538              : 
    1539              :                 /* Pad to a multiple of the tar block size. */
    1540       354328 :                 size += tarPaddingBytesRequired(statbuf.st_size);
    1541              : 
    1542              :                 /* Size of the header for the file. */
    1543       354328 :                 size += TAR_BLOCK_SIZE;
    1544              :             }
    1545              :         }
    1546              :         else
    1547            0 :             ereport(WARNING,
    1548              :                     (errmsg("skipping special file \"%s\"", pathbuf)));
    1549              :     }
    1550              : 
    1551         5847 :     if (relative_block_numbers != NULL)
    1552          191 :         pfree(relative_block_numbers);
    1553              : 
    1554         5847 :     FreeDir(dir);
    1555         5847 :     return size;
    1556              : }
    1557              : 
    1558              : /*
    1559              :  * Given the member, write the TAR header & send the file.
    1560              :  *
    1561              :  * If 'missing_ok' is true, will not throw an error if the file is not found.
    1562              :  *
    1563              :  * If dboid is anything other than InvalidOid then any checksum failures
    1564              :  * detected will get reported to the cumulative stats system.
    1565              :  *
    1566              :  * If the file is to be sent incrementally, then num_incremental_blocks
    1567              :  * should be the number of blocks to be sent, and incremental_blocks
    1568              :  * an array of block numbers relative to the start of the current segment.
    1569              :  * If the whole file is to be sent, then incremental_blocks should be NULL,
    1570              :  * and num_incremental_blocks can have any value, as it will be ignored.
    1571              :  *
    1572              :  * Returns true if the file was successfully sent, false if 'missing_ok',
    1573              :  * and the file did not exist.
    1574              :  */
    1575              : static bool
    1576       174815 : sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
    1577              :          struct stat *statbuf, bool missing_ok, Oid dboid, Oid spcoid,
    1578              :          RelFileNumber relfilenumber, unsigned segno,
    1579              :          backup_manifest_info *manifest, unsigned num_incremental_blocks,
    1580              :          BlockNumber *incremental_blocks, unsigned truncation_block_length)
    1581              : {
    1582              :     int         fd;
    1583       174815 :     BlockNumber blkno = 0;
    1584       174815 :     int         checksum_failures = 0;
    1585              :     off_t       cnt;
    1586       174815 :     pgoff_t     bytes_done = 0;
    1587       174815 :     bool        verify_checksum = false;
    1588              :     pg_checksum_context checksum_ctx;
    1589       174815 :     int         ibindex = 0;
    1590              : 
    1591       174815 :     if (pg_checksum_init(&checksum_ctx, manifest->checksum_type) < 0)
    1592            0 :         elog(ERROR, "could not initialize checksum of file \"%s\"",
    1593              :              readfilename);
    1594              : 
    1595       174815 :     fd = OpenTransientFile(readfilename, O_RDONLY | PG_BINARY);
    1596       174815 :     if (fd < 0)
    1597              :     {
    1598            0 :         if (errno == ENOENT && missing_ok)
    1599            0 :             return false;
    1600            0 :         ereport(ERROR,
    1601              :                 (errcode_for_file_access(),
    1602              :                  errmsg("could not open file \"%s\": %m", readfilename)));
    1603              :     }
    1604              : 
    1605       174815 :     _tarWriteHeader(sink, tarfilename, NULL, statbuf, false);
    1606              : 
    1607              :     /*
    1608              :      * Checksums are verified in multiples of BLCKSZ, so the buffer length
    1609              :      * should be a multiple of the block size as well.
    1610              :      */
    1611              :     Assert((sink->bbs_buffer_length % BLCKSZ) == 0);
    1612              : 
    1613              :     /*
    1614              :      * If we weren't told not to verify checksums, and if checksums are
    1615              :      * enabled for this cluster, and if this is a relation file, then verify
    1616              :      * the checksum.
    1617              :      */
    1618       174814 :     if (!noverify_checksums && DataChecksumsEnabled() &&
    1619              :         RelFileNumberIsValid(relfilenumber))
    1620       170702 :         verify_checksum = true;
    1621              : 
    1622              :     /*
    1623              :      * If we're sending an incremental file, write the file header.
    1624              :      */
    1625       174814 :     if (incremental_blocks != NULL)
    1626              :     {
    1627         7756 :         unsigned    magic = INCREMENTAL_MAGIC;
    1628         7756 :         size_t      header_bytes_done = 0;
    1629              :         char        padding[BLCKSZ];
    1630              :         size_t      paddinglen;
    1631              : 
    1632              :         /* Emit header data. */
    1633         7756 :         push_to_sink(sink, &checksum_ctx, &header_bytes_done,
    1634              :                      &magic, sizeof(magic));
    1635         7756 :         push_to_sink(sink, &checksum_ctx, &header_bytes_done,
    1636              :                      &num_incremental_blocks, sizeof(num_incremental_blocks));
    1637         7756 :         push_to_sink(sink, &checksum_ctx, &header_bytes_done,
    1638              :                      &truncation_block_length, sizeof(truncation_block_length));
    1639         7756 :         push_to_sink(sink, &checksum_ctx, &header_bytes_done,
    1640              :                      incremental_blocks,
    1641              :                      sizeof(BlockNumber) * num_incremental_blocks);
    1642              : 
    1643              :         /*
    1644              :          * Add padding to align header to a multiple of BLCKSZ, but only if
    1645              :          * the incremental file has some blocks, and the alignment is actually
    1646              :          * needed (i.e. header is not already a multiple of BLCKSZ). If there
    1647              :          * are no blocks we don't want to make the file unnecessarily large,
    1648              :          * as that might make some filesystem optimizations impossible.
    1649              :          */
    1650         7756 :         if ((num_incremental_blocks > 0) && (header_bytes_done % BLCKSZ != 0))
    1651              :         {
    1652           26 :             paddinglen = (BLCKSZ - (header_bytes_done % BLCKSZ));
    1653              : 
    1654           26 :             memset(padding, 0, paddinglen);
    1655           26 :             bytes_done += paddinglen;
    1656              : 
    1657           26 :             push_to_sink(sink, &checksum_ctx, &header_bytes_done,
    1658              :                          padding, paddinglen);
    1659              :         }
    1660              : 
    1661              :         /* Flush out any data still in the buffer so it's again empty. */
    1662         7756 :         if (header_bytes_done > 0)
    1663              :         {
    1664         7756 :             bbsink_archive_contents(sink, header_bytes_done);
    1665         7756 :             if (pg_checksum_update(&checksum_ctx,
    1666         7756 :                                    (uint8 *) sink->bbs_buffer,
    1667              :                                    header_bytes_done) < 0)
    1668            0 :                 elog(ERROR, "could not update checksum of base backup");
    1669              :         }
    1670              : 
    1671              :         /* Update our notion of file position. */
    1672         7756 :         bytes_done += sizeof(magic);
    1673         7756 :         bytes_done += sizeof(num_incremental_blocks);
    1674         7756 :         bytes_done += sizeof(truncation_block_length);
    1675         7756 :         bytes_done += sizeof(BlockNumber) * num_incremental_blocks;
    1676              :     }
    1677              : 
    1678              :     /*
    1679              :      * Loop until we read the amount of data the caller told us to expect. The
    1680              :      * file could be longer, if it was extended while we were sending it, but
    1681              :      * for a base backup we can ignore such extended data. It will be restored
    1682              :      * from WAL.
    1683              :      */
    1684              :     while (1)
    1685              :     {
    1686              :         /*
    1687              :          * Determine whether we've read all the data that we need, and if not,
    1688              :          * read some more.
    1689              :          */
    1690       372011 :         if (incremental_blocks == NULL)
    1691              :         {
    1692       364218 :             size_t      remaining = statbuf->st_size - bytes_done;
    1693              : 
    1694              :             /*
    1695              :              * If we've read the required number of bytes, then it's time to
    1696              :              * stop.
    1697              :              */
    1698       364218 :             if (bytes_done >= statbuf->st_size)
    1699       167058 :                 break;
    1700              : 
    1701              :             /*
    1702              :              * Read as many bytes as will fit in the buffer, or however many
    1703              :              * are left to read, whichever is less.
    1704              :              */
    1705       197160 :             cnt = read_file_data_into_buffer(sink, readfilename, fd,
    1706              :                                              bytes_done, remaining,
    1707       197160 :                                              blkno + segno * RELSEG_SIZE,
    1708              :                                              verify_checksum,
    1709              :                                              &checksum_failures);
    1710              :         }
    1711              :         else
    1712              :         {
    1713              :             BlockNumber relative_blkno;
    1714              : 
    1715              :             /*
    1716              :              * If we've read all the blocks, then it's time to stop.
    1717              :              */
    1718         7793 :             if (ibindex >= num_incremental_blocks)
    1719         7756 :                 break;
    1720              : 
    1721              :             /*
    1722              :              * Read just one block, whichever one is the next that we're
    1723              :              * supposed to include.
    1724              :              */
    1725           37 :             relative_blkno = incremental_blocks[ibindex++];
    1726           37 :             cnt = read_file_data_into_buffer(sink, readfilename, fd,
    1727           37 :                                              relative_blkno * BLCKSZ,
    1728              :                                              BLCKSZ,
    1729           37 :                                              relative_blkno + segno * RELSEG_SIZE,
    1730              :                                              verify_checksum,
    1731              :                                              &checksum_failures);
    1732              : 
    1733              :             /*
    1734              :              * If we get a partial read, that must mean that the relation is
    1735              :              * being truncated. Ultimately, it should be truncated to a
    1736              :              * multiple of BLCKSZ, since this path should only be reached for
    1737              :              * relation files, but we might transiently observe an
    1738              :              * intermediate value.
    1739              :              *
    1740              :              * It should be fine to treat this just as if the entire block had
    1741              :              * been truncated away - i.e. fill this and all later blocks with
    1742              :              * zeroes. WAL replay will fix things up.
    1743              :              */
    1744           37 :             if (cnt < BLCKSZ)
    1745            0 :                 break;
    1746              :         }
    1747              : 
    1748              :         /*
    1749              :          * If the amount of data we were able to read was not a multiple of
    1750              :          * BLCKSZ, we cannot verify checksums, which are block-level.
    1751              :          */
    1752       197197 :         if (verify_checksum && (cnt % BLCKSZ != 0))
    1753              :         {
    1754            0 :             ereport(WARNING,
    1755              :                     (errmsg("could not verify checksum in file \"%s\", block "
    1756              :                             "%u: read buffer size %d and page size %d "
    1757              :                             "differ",
    1758              :                             readfilename, blkno, (int) cnt, BLCKSZ)));
    1759            0 :             verify_checksum = false;
    1760              :         }
    1761              : 
    1762              :         /*
    1763              :          * If we hit end-of-file, a concurrent truncation must have occurred.
    1764              :          * That's not an error condition, because WAL replay will fix things
    1765              :          * up.
    1766              :          */
    1767       197197 :         if (cnt == 0)
    1768            0 :             break;
    1769              : 
    1770              :         /* Update block number and # of bytes done for next loop iteration. */
    1771       197197 :         blkno += cnt / BLCKSZ;
    1772       197197 :         bytes_done += cnt;
    1773              : 
    1774              :         /*
    1775              :          * Make sure incremental files with block data are properly aligned
    1776              :          * (header is a multiple of BLCKSZ, blocks are BLCKSZ too).
    1777              :          */
    1778              :         Assert(!((incremental_blocks != NULL && num_incremental_blocks > 0) &&
    1779              :                  (bytes_done % BLCKSZ != 0)));
    1780              : 
    1781              :         /* Archive the data we just read. */
    1782       197197 :         bbsink_archive_contents(sink, cnt);
    1783              : 
    1784              :         /* Also feed it to the checksum machinery. */
    1785       197197 :         if (pg_checksum_update(&checksum_ctx,
    1786       197197 :                                (uint8 *) sink->bbs_buffer, cnt) < 0)
    1787            0 :             elog(ERROR, "could not update checksum of base backup");
    1788              :     }
    1789              : 
    1790              :     /* If the file was truncated while we were sending it, pad it with zeros */
    1791       174814 :     while (bytes_done < statbuf->st_size)
    1792              :     {
    1793            0 :         size_t      remaining = statbuf->st_size - bytes_done;
    1794            0 :         size_t      nbytes = Min(sink->bbs_buffer_length, remaining);
    1795              : 
    1796            0 :         MemSet(sink->bbs_buffer, 0, nbytes);
    1797            0 :         if (pg_checksum_update(&checksum_ctx,
    1798            0 :                                (uint8 *) sink->bbs_buffer,
    1799              :                                nbytes) < 0)
    1800            0 :             elog(ERROR, "could not update checksum of base backup");
    1801            0 :         bbsink_archive_contents(sink, nbytes);
    1802            0 :         bytes_done += nbytes;
    1803              :     }
    1804              : 
    1805              :     /*
    1806              :      * Pad to a block boundary, per tar format requirements. (This small piece
    1807              :      * of data is probably not worth throttling, and is not checksummed
    1808              :      * because it's not actually part of the file.)
    1809              :      */
    1810       174814 :     _tarWritePadding(sink, bytes_done);
    1811              : 
    1812       174814 :     CloseTransientFile(fd);
    1813              : 
    1814       174814 :     if (checksum_failures > 1)
    1815              :     {
    1816            2 :         ereport(WARNING,
    1817              :                 (errmsg_plural("file \"%s\" has a total of %d checksum verification failure",
    1818              :                                "file \"%s\" has a total of %d checksum verification failures",
    1819              :                                checksum_failures,
    1820              :                                readfilename, checksum_failures)));
    1821              : 
    1822            2 :         pgstat_prepare_report_checksum_failure(dboid);
    1823            2 :         pgstat_report_checksum_failures_in_db(dboid, checksum_failures);
    1824              :     }
    1825              : 
    1826       174814 :     total_checksum_failures += checksum_failures;
    1827              : 
    1828       174814 :     AddFileToBackupManifest(manifest, spcoid, tarfilename, statbuf->st_size,
    1829       174814 :                             (pg_time_t) statbuf->st_mtime, &checksum_ctx);
    1830              : 
    1831       174814 :     return true;
    1832              : }
    1833              : 
    1834              : /*
    1835              :  * Read some more data from the file into the bbsink's buffer, verifying
    1836              :  * checksums as required.
    1837              :  *
    1838              :  * 'offset' is the file offset from which we should begin to read, and
    1839              :  * 'length' is the amount of data that should be read. The actual amount
    1840              :  * of data read will be less than the requested amount if the bbsink's
    1841              :  * buffer isn't big enough to hold it all, or if the underlying file has
    1842              :  * been truncated. The return value is the number of bytes actually read.
    1843              :  *
    1844              :  * 'blkno' is the block number of the first page in the bbsink's buffer
    1845              :  * relative to the start of the relation.
    1846              :  *
    1847              :  * 'verify_checksum' indicates whether we should try to verify checksums
    1848              :  * for the blocks we read. If we do this, we'll update *checksum_failures
    1849              :  * and issue warnings as appropriate.
    1850              :  */
    1851              : static off_t
    1852       197197 : read_file_data_into_buffer(bbsink *sink, const char *readfilename, int fd,
    1853              :                            off_t offset, size_t length, BlockNumber blkno,
    1854              :                            bool verify_checksum, int *checksum_failures)
    1855              : {
    1856              :     off_t       cnt;
    1857              :     int         i;
    1858              :     char       *page;
    1859              : 
    1860              :     /* Try to read some more data. */
    1861       197197 :     cnt = basebackup_read_file(fd, sink->bbs_buffer,
    1862       197197 :                                Min(sink->bbs_buffer_length, length),
    1863              :                                offset, readfilename, true);
    1864              : 
    1865              :     /* Can't verify checksums if read length is not a multiple of BLCKSZ. */
    1866       197197 :     if (!verify_checksum || (cnt % BLCKSZ) != 0)
    1867         4643 :         return cnt;
    1868              : 
    1869              :     /* Verify checksum for each block. */
    1870       664255 :     for (i = 0; i < cnt / BLCKSZ; i++)
    1871              :     {
    1872              :         int         reread_cnt;
    1873              :         uint16      expected_checksum;
    1874              : 
    1875       471701 :         page = sink->bbs_buffer + BLCKSZ * i;
    1876              : 
    1877              :         /* If the page is OK, go on to the next one. */
    1878       471701 :         if (verify_page_checksum(page, sink->bbs_state->startptr, blkno + i,
    1879              :                                  &expected_checksum))
    1880       471687 :             continue;
    1881              : 
    1882              :         /*
    1883              :          * Retry the block on the first failure.  It's possible that we read
    1884              :          * the first 4K page of the block just before postgres updated the
    1885              :          * entire block so it ends up looking torn to us. If, before we retry
    1886              :          * the read, the concurrent write of the block finishes, the page LSN
    1887              :          * will be updated and we'll realize that we should ignore this block.
    1888              :          *
    1889              :          * There's no guarantee that this will actually happen, though: the
    1890              :          * torn write could take an arbitrarily long time to complete.
    1891              :          * Retrying multiple times wouldn't fix this problem, either, though
    1892              :          * it would reduce the chances of it happening in practice. The only
    1893              :          * real fix here seems to be to have some kind of interlock that
    1894              :          * allows us to wait until we can be certain that no write to the
    1895              :          * block is in progress. Since we don't have any such thing right now,
    1896              :          * we just do this and hope for the best.
    1897              :          */
    1898           14 :         reread_cnt =
    1899           14 :             basebackup_read_file(fd, sink->bbs_buffer + BLCKSZ * i,
    1900           14 :                                  BLCKSZ, offset + BLCKSZ * i,
    1901              :                                  readfilename, false);
    1902           14 :         if (reread_cnt == 0)
    1903              :         {
    1904              :             /*
    1905              :              * If we hit end-of-file, a concurrent truncation must have
    1906              :              * occurred, so reduce cnt to reflect only the blocks already
    1907              :              * processed and break out of this loop.
    1908              :              */
    1909            0 :             cnt = BLCKSZ * i;
    1910            0 :             break;
    1911              :         }
    1912              : 
    1913              :         /* If the page now looks OK, go on to the next one. */
    1914           14 :         if (verify_page_checksum(page, sink->bbs_state->startptr, blkno + i,
    1915              :                                  &expected_checksum))
    1916            0 :             continue;
    1917              : 
    1918              :         /* Handle checksum failure. */
    1919           14 :         (*checksum_failures)++;
    1920           14 :         if (*checksum_failures <= 5)
    1921           12 :             ereport(WARNING,
    1922              :                     (errmsg("checksum verification failed in "
    1923              :                             "file \"%s\", block %u: calculated "
    1924              :                             "%X but expected %X",
    1925              :                             readfilename, blkno + i, expected_checksum,
    1926              :                             ((PageHeader) page)->pd_checksum)));
    1927           14 :         if (*checksum_failures == 5)
    1928            2 :             ereport(WARNING,
    1929              :                     (errmsg("further checksum verification "
    1930              :                             "failures in file \"%s\" will not "
    1931              :                             "be reported", readfilename)));
    1932              :     }
    1933              : 
    1934       192554 :     return cnt;
    1935              : }
    1936              : 
    1937              : /*
    1938              :  * Push data into a bbsink.
    1939              :  *
    1940              :  * It's better, when possible, to read data directly into the bbsink's buffer,
    1941              :  * rather than using this function to copy it into the buffer; this function is
    1942              :  * for cases where that approach is not practical.
    1943              :  *
    1944              :  * bytes_done should point to a count of the number of bytes that are
    1945              :  * currently used in the bbsink's buffer. Upon return, the bytes identified by
    1946              :  * data and length will have been copied into the bbsink's buffer, flushing
    1947              :  * as required, and *bytes_done will have been updated accordingly. If the
    1948              :  * buffer was flushed, the previous contents will also have been fed to
    1949              :  * checksum_ctx.
    1950              :  *
    1951              :  * Note that after one or more calls to this function it is the caller's
    1952              :  * responsibility to perform any required final flush.
    1953              :  */
    1954              : static void
    1955        31050 : push_to_sink(bbsink *sink, pg_checksum_context *checksum_ctx,
    1956              :              size_t *bytes_done, void *data, size_t length)
    1957              : {
    1958        31050 :     while (length > 0)
    1959              :     {
    1960              :         size_t      bytes_to_copy;
    1961              : 
    1962              :         /*
    1963              :          * We use < here rather than <= so that if the data exactly fills the
    1964              :          * remaining buffer space, we trigger a flush now.
    1965              :          */
    1966        23320 :         if (length < sink->bbs_buffer_length - *bytes_done)
    1967              :         {
    1968              :             /* Append remaining data to buffer. */
    1969        23320 :             memcpy(sink->bbs_buffer + *bytes_done, data, length);
    1970        23320 :             *bytes_done += length;
    1971        23320 :             return;
    1972              :         }
    1973              : 
    1974              :         /* Copy until buffer is full and flush it. */
    1975            0 :         bytes_to_copy = sink->bbs_buffer_length - *bytes_done;
    1976            0 :         memcpy(sink->bbs_buffer + *bytes_done, data, bytes_to_copy);
    1977            0 :         data = ((char *) data) + bytes_to_copy;
    1978            0 :         length -= bytes_to_copy;
    1979            0 :         bbsink_archive_contents(sink, sink->bbs_buffer_length);
    1980            0 :         if (pg_checksum_update(checksum_ctx, (uint8 *) sink->bbs_buffer,
    1981              :                                sink->bbs_buffer_length) < 0)
    1982            0 :             elog(ERROR, "could not update checksum");
    1983            0 :         *bytes_done = 0;
    1984              :     }
    1985              : }
    1986              : 
    1987              : /*
    1988              :  * Try to verify the checksum for the provided page, if it seems appropriate
    1989              :  * to do so.
    1990              :  *
    1991              :  * Returns true if verification succeeds or if we decide not to check it,
    1992              :  * and false if verification fails. When return false, it also sets
    1993              :  * *expected_checksum to the computed value.
    1994              :  */
    1995              : static bool
    1996       471715 : verify_page_checksum(Page page, XLogRecPtr start_lsn, BlockNumber blkno,
    1997              :                      uint16 *expected_checksum)
    1998              : {
    1999              :     PageHeader  phdr;
    2000              :     uint16      checksum;
    2001              : 
    2002              :     /*
    2003              :      * Only check pages which have not been modified since the start of the
    2004              :      * base backup. Otherwise, they might have been written only halfway and
    2005              :      * the checksum would not be valid.  However, replaying WAL would
    2006              :      * reinstate the correct page in this case. We also skip completely new
    2007              :      * pages, since they don't have a checksum yet.
    2008              :      */
    2009       471715 :     if (PageIsNew(page) || PageGetLSN(page) >= start_lsn)
    2010         1050 :         return true;
    2011              : 
    2012              :     /* Perform the actual checksum calculation. */
    2013       470665 :     checksum = pg_checksum_page(page, blkno);
    2014              : 
    2015              :     /* See whether it matches the value from the page. */
    2016       470665 :     phdr = (PageHeader) page;
    2017       470665 :     if (phdr->pd_checksum == checksum)
    2018       470637 :         return true;
    2019           28 :     *expected_checksum = checksum;
    2020           28 :     return false;
    2021              : }
    2022              : 
    2023              : static int64
    2024       183991 : _tarWriteHeader(bbsink *sink, const char *filename, const char *linktarget,
    2025              :                 struct stat *statbuf, bool sizeonly)
    2026              : {
    2027              :     enum tarError rc;
    2028              : 
    2029       183991 :     if (!sizeonly)
    2030              :     {
    2031              :         /*
    2032              :          * As of this writing, the smallest supported block size is 1kB, which
    2033              :          * is twice TAR_BLOCK_SIZE. Since the buffer size is required to be a
    2034              :          * multiple of BLCKSZ, it should be safe to assume that the buffer is
    2035              :          * large enough to fit an entire tar block. We double-check by means
    2036              :          * of these assertions.
    2037              :          */
    2038              :         StaticAssertDecl(TAR_BLOCK_SIZE <= BLCKSZ,
    2039              :                          "BLCKSZ too small for tar block");
    2040              :         Assert(sink->bbs_buffer_length >= TAR_BLOCK_SIZE);
    2041              : 
    2042       179476 :         rc = tarCreateHeader(sink->bbs_buffer, filename, linktarget,
    2043              :                              statbuf->st_size, statbuf->st_mode,
    2044              :                              statbuf->st_uid, statbuf->st_gid,
    2045              :                              statbuf->st_mtime);
    2046              : 
    2047       179476 :         switch (rc)
    2048              :         {
    2049       179475 :             case TAR_OK:
    2050       179475 :                 break;
    2051            1 :             case TAR_NAME_TOO_LONG:
    2052            1 :                 ereport(ERROR,
    2053              :                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    2054              :                          errmsg("file name too long for tar format: \"%s\"",
    2055              :                                 filename)));
    2056              :                 break;
    2057            0 :             case TAR_SYMLINK_TOO_LONG:
    2058            0 :                 ereport(ERROR,
    2059              :                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    2060              :                          errmsg("symbolic link target too long for tar format: "
    2061              :                                 "file name \"%s\", target \"%s\"",
    2062              :                                 filename, linktarget)));
    2063              :                 break;
    2064            0 :             default:
    2065            0 :                 elog(ERROR, "unrecognized tar error: %d", rc);
    2066              :         }
    2067              : 
    2068       179475 :         bbsink_archive_contents(sink, TAR_BLOCK_SIZE);
    2069              :     }
    2070              : 
    2071       183990 :     return TAR_BLOCK_SIZE;
    2072              : }
    2073              : 
    2074              : /*
    2075              :  * Pad with zero bytes out to a multiple of TAR_BLOCK_SIZE.
    2076              :  */
    2077              : static void
    2078       175025 : _tarWritePadding(bbsink *sink, int len)
    2079              : {
    2080       175025 :     int         pad = tarPaddingBytesRequired(len);
    2081              : 
    2082              :     /*
    2083              :      * As in _tarWriteHeader, it should be safe to assume that the buffer is
    2084              :      * large enough that we don't need to do this in multiple chunks.
    2085              :      */
    2086              :     Assert(sink->bbs_buffer_length >= TAR_BLOCK_SIZE);
    2087              :     Assert(pad <= TAR_BLOCK_SIZE);
    2088              : 
    2089       175025 :     if (pad > 0)
    2090              :     {
    2091        30448 :         MemSet(sink->bbs_buffer, 0, pad);
    2092        10320 :         bbsink_archive_contents(sink, pad);
    2093              :     }
    2094       175025 : }
    2095              : 
    2096              : /*
    2097              :  * If the entry in statbuf is a link, then adjust statbuf to make it look like a
    2098              :  * directory, so that it will be written that way.
    2099              :  */
    2100              : static void
    2101         2674 : convert_link_to_directory(const char *pathbuf, struct stat *statbuf)
    2102              : {
    2103              :     /* If symlink, write it as a directory anyway */
    2104         2674 :     if (S_ISLNK(statbuf->st_mode))
    2105           66 :         statbuf->st_mode = S_IFDIR | pg_dir_create_mode;
    2106         2674 : }
    2107              : 
    2108              : /*
    2109              :  * Read some data from a file, setting a wait event and reporting any error
    2110              :  * encountered.
    2111              :  *
    2112              :  * If partial_read_ok is false, also report an error if the number of bytes
    2113              :  * read is not equal to the number of bytes requested.
    2114              :  *
    2115              :  * Returns the number of bytes read.
    2116              :  */
    2117              : static ssize_t
    2118       204891 : basebackup_read_file(int fd, char *buf, size_t nbytes, off_t offset,
    2119              :                      const char *filename, bool partial_read_ok)
    2120              : {
    2121              :     ssize_t     rc;
    2122              : 
    2123       204891 :     pgstat_report_wait_start(WAIT_EVENT_BASEBACKUP_READ);
    2124       204891 :     rc = pg_pread(fd, buf, nbytes, offset);
    2125       204891 :     pgstat_report_wait_end();
    2126              : 
    2127       204891 :     if (rc < 0)
    2128            0 :         ereport(ERROR,
    2129              :                 (errcode_for_file_access(),
    2130              :                  errmsg("could not read file \"%s\": %m", filename)));
    2131       204891 :     if (!partial_read_ok && rc > 0 && rc != nbytes)
    2132            0 :         ereport(ERROR,
    2133              :                 (errcode_for_file_access(),
    2134              :                  errmsg("could not read file \"%s\": read %zd of %zu",
    2135              :                         filename, rc, nbytes)));
    2136              : 
    2137       204891 :     return rc;
    2138              : }
        

Generated by: LCOV version 2.0-1