PostgreSQL Source Code  git master
pg_combinebackup.c File Reference
#include "postgres_fe.h"
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include "backup_label.h"
#include "common/blkreftable.h"
#include "common/checksum_helper.h"
#include "common/controldata_utils.h"
#include "common/file_perm.h"
#include "common/file_utils.h"
#include "common/logging.h"
#include "copy_file.h"
#include "fe_utils/option_utils.h"
#include "getopt_long.h"
#include "lib/stringinfo.h"
#include "load_manifest.h"
#include "reconstruct.h"
#include "write_manifest.h"
Include dependency graph for pg_combinebackup.c:

Go to the source code of this file.

Data Structures

struct  cb_cleanup_dir
 
struct  cb_tablespace_mapping
 
struct  cb_options
 
struct  cb_tablespace
 

Macros

#define INCREMENTAL_PREFIX   "INCREMENTAL."
 
#define INCREMENTAL_PREFIX_LENGTH   (sizeof(INCREMENTAL_PREFIX) - 1)
 

Typedefs

typedef struct cb_cleanup_dir cb_cleanup_dir
 
typedef struct cb_tablespace_mapping cb_tablespace_mapping
 
typedef struct cb_options cb_options
 
typedef struct cb_tablespace cb_tablespace
 

Functions

static void add_tablespace_mapping (cb_options *opt, char *arg)
 
static StringInfo check_backup_label_files (int n_backups, char **backup_dirs)
 
static uint64 check_control_files (int n_backups, char **backup_dirs)
 
static void check_input_dir_permissions (char *dir)
 
static void cleanup_directories_atexit (void)
 
static void create_output_directory (char *dirname, cb_options *opt)
 
static void help (const char *progname)
 
static bool parse_oid (char *s, Oid *result)
 
static void process_directory_recursively (Oid tsoid, char *input_directory, char *output_directory, char *relative_path, int n_prior_backups, char **prior_backup_dirs, manifest_data **manifests, manifest_writer *mwriter, cb_options *opt)
 
static int read_pg_version_file (char *directory)
 
static void remember_to_cleanup_directory (char *target_path, bool rmtopdir)
 
static void reset_directory_cleanup_list (void)
 
static cb_tablespacescan_for_existing_tablespaces (char *pathname, cb_options *opt)
 
static void slurp_file (int fd, char *filename, StringInfo buf, int maxlen)
 
int main (int argc, char *argv[])
 

Variables

static cb_cleanup_dircleanup_dir_list = NULL
 

Macro Definition Documentation

◆ INCREMENTAL_PREFIX

#define INCREMENTAL_PREFIX   "INCREMENTAL."

Definition at line 43 of file pg_combinebackup.c.

◆ INCREMENTAL_PREFIX_LENGTH

#define INCREMENTAL_PREFIX_LENGTH   (sizeof(INCREMENTAL_PREFIX) - 1)

Definition at line 44 of file pg_combinebackup.c.

Typedef Documentation

◆ cb_cleanup_dir

◆ cb_options

typedef struct cb_options cb_options

◆ cb_tablespace

typedef struct cb_tablespace cb_tablespace

◆ cb_tablespace_mapping

Function Documentation

◆ add_tablespace_mapping()

static void add_tablespace_mapping ( cb_options opt,
char *  arg 
)
static

Definition at line 436 of file pg_combinebackup.c.

437 {
439  char *dst;
440  char *dst_ptr;
441  char *arg_ptr;
442 
443  /*
444  * Basically, we just want to copy everything before the equals sign to
445  * tsmap->old_dir and everything afterwards to tsmap->new_dir, but if
446  * there's more or less than one equals sign, that's an error, and if
447  * there's an equals sign preceded by a backslash, don't treat it as a
448  * field separator but instead copy a literal equals sign.
449  */
450  dst_ptr = dst = tsmap->old_dir;
451  for (arg_ptr = arg; *arg_ptr != '\0'; arg_ptr++)
452  {
453  if (dst_ptr - dst >= MAXPGPATH)
454  pg_fatal("directory name too long");
455 
456  if (*arg_ptr == '\\' && *(arg_ptr + 1) == '=')
457  ; /* skip backslash escaping = */
458  else if (*arg_ptr == '=' && (arg_ptr == arg || *(arg_ptr - 1) != '\\'))
459  {
460  if (tsmap->new_dir[0] != '\0')
461  pg_fatal("multiple \"=\" signs in tablespace mapping");
462  else
463  dst = dst_ptr = tsmap->new_dir;
464  }
465  else
466  *dst_ptr++ = *arg_ptr;
467  }
468  if (!tsmap->old_dir[0] || !tsmap->new_dir[0])
469  pg_fatal("invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"", arg);
470 
471  /*
472  * All tablespaces are created with absolute directories, so specifying a
473  * non-absolute path here would never match, possibly confusing users.
474  *
475  * In contrast to pg_basebackup, both the old and new directories are on
476  * the local machine, so the local machine's definition of an absolute
477  * path is the only relevant one.
478  */
479  if (!is_absolute_path(tsmap->old_dir))
480  pg_fatal("old directory is not an absolute path in tablespace mapping: %s",
481  tsmap->old_dir);
482 
483  if (!is_absolute_path(tsmap->new_dir))
484  pg_fatal("old directory is not an absolute path in tablespace mapping: %s",
485  tsmap->new_dir);
486 
487  /* Canonicalize paths to avoid spurious failures when comparing. */
488  canonicalize_path(tsmap->old_dir);
489  canonicalize_path(tsmap->new_dir);
490 
491  /* Add it to the list. */
492  tsmap->next = opt->tsmappings;
493  opt->tsmappings = tsmap;
494 }
void * pg_malloc0(size_t size)
Definition: fe_memutils.c:53
void * arg
#define pg_fatal(...)
#define MAXPGPATH
#define is_absolute_path(filename)
Definition: port.h:103
void canonicalize_path(char *path)
Definition: path.c:264
cb_tablespace_mapping * tsmappings
struct cb_tablespace_mapping * next
char new_dir[MAXPGPATH]
char old_dir[MAXPGPATH]

References arg, canonicalize_path(), is_absolute_path, MAXPGPATH, cb_tablespace_mapping::new_dir, cb_tablespace_mapping::next, cb_tablespace_mapping::old_dir, pg_fatal, pg_malloc0(), and cb_options::tsmappings.

Referenced by main().

◆ check_backup_label_files()

static StringInfo check_backup_label_files ( int  n_backups,
char **  backup_dirs 
)
static

Definition at line 501 of file pg_combinebackup.c.

502 {
504  StringInfo lastbuf = buf;
505  int i;
506  TimeLineID check_tli = 0;
507  XLogRecPtr check_lsn = InvalidXLogRecPtr;
508 
509  /* Try to read each backup_label file in turn, last to first. */
510  for (i = n_backups - 1; i >= 0; --i)
511  {
512  char pathbuf[MAXPGPATH];
513  int fd;
514  TimeLineID start_tli;
515  TimeLineID previous_tli;
516  XLogRecPtr start_lsn;
517  XLogRecPtr previous_lsn;
518 
519  /* Open the backup_label file. */
520  snprintf(pathbuf, MAXPGPATH, "%s/backup_label", backup_dirs[i]);
521  pg_log_debug("reading \"%s\"", pathbuf);
522  if ((fd = open(pathbuf, O_RDONLY, 0)) < 0)
523  pg_fatal("could not open file \"%s\": %m", pathbuf);
524 
525  /*
526  * Slurp the whole file into memory.
527  *
528  * The exact size limit that we impose here doesn't really matter --
529  * most of what's supposed to be in the file is fixed size and quite
530  * short. However, the length of the backup_label is limited (at least
531  * by some parts of the code) to MAXPGPATH, so include that value in
532  * the maximum length that we tolerate.
533  */
534  slurp_file(fd, pathbuf, buf, 10000 + MAXPGPATH);
535 
536  /* Close the file. */
537  if (close(fd) != 0)
538  pg_fatal("could not close file \"%s\": %m", pathbuf);
539 
540  /* Parse the file contents. */
541  parse_backup_label(pathbuf, buf, &start_tli, &start_lsn,
542  &previous_tli, &previous_lsn);
543 
544  /*
545  * Sanity checks.
546  *
547  * XXX. It's actually not required that start_lsn == check_lsn. It
548  * would be OK if start_lsn > check_lsn provided that start_lsn is
549  * less than or equal to the relevant switchpoint. But at the moment
550  * we don't have that information.
551  */
552  if (i > 0 && previous_tli == 0)
553  pg_fatal("backup at \"%s\" is a full backup, but only the first backup should be a full backup",
554  backup_dirs[i]);
555  if (i == 0 && previous_tli != 0)
556  pg_fatal("backup at \"%s\" is an incremental backup, but the first backup should be a full backup",
557  backup_dirs[i]);
558  if (i < n_backups - 1 && start_tli != check_tli)
559  pg_fatal("backup at \"%s\" starts on timeline %u, but expected %u",
560  backup_dirs[i], start_tli, check_tli);
561  if (i < n_backups - 1 && start_lsn != check_lsn)
562  pg_fatal("backup at \"%s\" starts at LSN %X/%X, but expected %X/%X",
563  backup_dirs[i],
564  LSN_FORMAT_ARGS(start_lsn),
565  LSN_FORMAT_ARGS(check_lsn));
566  check_tli = previous_tli;
567  check_lsn = previous_lsn;
568 
569  /*
570  * The last backup label in the chain needs to be saved for later use,
571  * while the others are only needed within this loop.
572  */
573  if (lastbuf == buf)
574  buf = makeStringInfo();
575  else
577  }
578 
579  /* Free memory that we don't need any more. */
580  if (lastbuf != buf)
582 
583  /*
584  * Return the data from the first backup_info that we read (which is the
585  * backup_label from the last directory specified on the command line).
586  */
587  return lastbuf;
588 }
void parse_backup_label(char *filename, StringInfo buf, TimeLineID *start_tli, XLogRecPtr *start_lsn, TimeLineID *previous_tli, XLogRecPtr *previous_lsn)
Definition: backup_label.c:45
#define close(a)
Definition: win32.h:12
int i
Definition: isn.c:73
#define pg_log_debug(...)
Definition: logging.h:133
static void slurp_file(int fd, char *filename, StringInfo buf, int maxlen)
static char * buf
Definition: pg_test_fsync.c:73
#define snprintf
Definition: port.h:238
static int fd(const char *x, int i)
Definition: preproc-init.c:105
void destroyStringInfo(StringInfo str)
Definition: stringinfo.c:361
StringInfo makeStringInfo(void)
Definition: stringinfo.c:41
void resetStringInfo(StringInfo str)
Definition: stringinfo.c:78
#define LSN_FORMAT_ARGS(lsn)
Definition: xlogdefs.h:43
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28
uint32 TimeLineID
Definition: xlogdefs.h:59

References buf, close, destroyStringInfo(), fd(), i, InvalidXLogRecPtr, LSN_FORMAT_ARGS, makeStringInfo(), MAXPGPATH, parse_backup_label(), pg_fatal, pg_log_debug, resetStringInfo(), slurp_file(), and snprintf.

Referenced by main().

◆ check_control_files()

static uint64 check_control_files ( int  n_backups,
char **  backup_dirs 
)
static

Definition at line 594 of file pg_combinebackup.c.

595 {
596  int i;
597  uint64 system_identifier = 0; /* placate compiler */
598  uint32 data_checksum_version = 0; /* placate compiler */
599  bool data_checksum_mismatch = false;
600 
601  /* Try to read each control file in turn, last to first. */
602  for (i = n_backups - 1; i >= 0; --i)
603  {
604  ControlFileData *control_file;
605  bool crc_ok;
606  char *controlpath;
607 
608  controlpath = psprintf("%s/%s", backup_dirs[i], "global/pg_control");
609  pg_log_debug("reading \"%s\"", controlpath);
610  control_file = get_controlfile_by_exact_path(controlpath, &crc_ok);
611 
612  /* Control file contents not meaningful if CRC is bad. */
613  if (!crc_ok)
614  pg_fatal("%s: CRC is incorrect", controlpath);
615 
616  /* Can't interpret control file if not current version. */
617  if (control_file->pg_control_version != PG_CONTROL_VERSION)
618  pg_fatal("%s: unexpected control file version",
619  controlpath);
620 
621  /* System identifiers should all match. */
622  if (i == n_backups - 1)
623  system_identifier = control_file->system_identifier;
624  else if (system_identifier != control_file->system_identifier)
625  pg_fatal("%s: expected system identifier %llu, but found %llu",
626  controlpath, (unsigned long long) system_identifier,
627  (unsigned long long) control_file->system_identifier);
628 
629  /*
630  * Detect checksum mismatches, but only if the last backup in the
631  * chain has checksums enabled.
632  */
633  if (i == n_backups - 1)
634  data_checksum_version = control_file->data_checksum_version;
635  else if (data_checksum_version != 0 &&
636  data_checksum_version != control_file->data_checksum_version)
637  data_checksum_mismatch = true;
638 
639  /* Release memory. */
640  pfree(control_file);
641  pfree(controlpath);
642  }
643 
644  /*
645  * If debug output is enabled, make a note of the system identifier that
646  * we found in all of the relevant control files.
647  */
648  pg_log_debug("system identifier is %llu",
649  (unsigned long long) system_identifier);
650 
651  /*
652  * Warn the user if not all backups are in the same state with regards to
653  * checksums.
654  */
655  if (data_checksum_mismatch)
656  {
657  pg_log_warning("only some backups have checksums enabled");
658  pg_log_warning_hint("disable, and optionally reenable, checksums on the output directory to avoid failures");
659  }
660 
661  return system_identifier;
662 }
unsigned int uint32
Definition: c.h:506
ControlFileData * get_controlfile_by_exact_path(const char *ControlFilePath, bool *crc_ok_p)
#define pg_log_warning_hint(...)
Definition: logging.h:121
void pfree(void *pointer)
Definition: mcxt.c:1521
#define PG_CONTROL_VERSION
Definition: pg_control.h:25
#define pg_log_warning(...)
Definition: pgfnames.c:24
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
uint32 pg_control_version
Definition: pg_control.h:125
uint32 data_checksum_version
Definition: pg_control.h:222
uint64 system_identifier
Definition: pg_control.h:110

References ControlFileData::data_checksum_version, get_controlfile_by_exact_path(), i, pfree(), PG_CONTROL_VERSION, ControlFileData::pg_control_version, pg_fatal, pg_log_debug, pg_log_warning, pg_log_warning_hint, psprintf(), and ControlFileData::system_identifier.

Referenced by main().

◆ check_input_dir_permissions()

static void check_input_dir_permissions ( char *  dir)
static

Definition at line 671 of file pg_combinebackup.c.

672 {
673  struct stat st;
674 
675  if (stat(dir, &st) != 0)
676  pg_fatal("could not stat file \"%s\": %m", dir);
677 
678  SetDataDirectoryCreatePerm(st.st_mode);
679 }
void SetDataDirectoryCreatePerm(int dataDirMode)
Definition: file_perm.c:34
#define stat
Definition: win32_port.h:284

References pg_fatal, SetDataDirectoryCreatePerm(), stat::st_mode, and stat.

Referenced by main().

◆ cleanup_directories_atexit()

static void cleanup_directories_atexit ( void  )
static

Definition at line 685 of file pg_combinebackup.c.

686 {
687  while (cleanup_dir_list != NULL)
688  {
690 
691  if (dir->rmtopdir)
692  {
693  pg_log_info("removing output directory \"%s\"", dir->target_path);
694  if (!rmtree(dir->target_path, dir->rmtopdir))
695  pg_log_error("failed to remove output directory");
696  }
697  else
698  {
699  pg_log_info("removing contents of output directory \"%s\"",
700  dir->target_path);
701  if (!rmtree(dir->target_path, dir->rmtopdir))
702  pg_log_error("failed to remove contents of output directory");
703  }
704 
706  pfree(dir);
707  }
708 }
#define pg_log_error(...)
Definition: logging.h:106
#define pg_log_info(...)
Definition: logging.h:124
static cb_cleanup_dir * cleanup_dir_list
bool rmtree(const char *path, bool rmtopdir)
Definition: rmtree.c:50
struct cb_cleanup_dir * next

References cleanup_dir_list, cb_cleanup_dir::next, pfree(), pg_log_error, pg_log_info, cb_cleanup_dir::rmtopdir, rmtree(), and cb_cleanup_dir::target_path.

Referenced by main().

◆ create_output_directory()

static void create_output_directory ( char *  dirname,
cb_options opt 
)
static

Definition at line 718 of file pg_combinebackup.c.

719 {
720  switch (pg_check_dir(dirname))
721  {
722  case 0:
723  if (opt->dry_run)
724  {
725  pg_log_debug("would create directory \"%s\"", dirname);
726  return;
727  }
728  pg_log_debug("creating directory \"%s\"", dirname);
729  if (pg_mkdir_p(dirname, pg_dir_create_mode) == -1)
730  pg_fatal("could not create directory \"%s\": %m", dirname);
731  remember_to_cleanup_directory(dirname, true);
732  break;
733 
734  case 1:
735  pg_log_debug("using existing directory \"%s\"", dirname);
736  remember_to_cleanup_directory(dirname, false);
737  break;
738 
739  case 2:
740  case 3:
741  case 4:
742  pg_fatal("directory \"%s\" exists but is not empty", dirname);
743 
744  case -1:
745  pg_fatal("could not access directory \"%s\": %m", dirname);
746  }
747 }
int pg_dir_create_mode
Definition: file_perm.c:18
static void remember_to_cleanup_directory(char *target_path, bool rmtopdir)
int pg_mkdir_p(char *path, int omode)
Definition: pgmkdirp.c:57
int pg_check_dir(const char *dir)
Definition: pgcheckdir.c:33

References cb_options::dry_run, pg_check_dir(), pg_dir_create_mode, pg_fatal, pg_log_debug, pg_mkdir_p(), and remember_to_cleanup_directory().

Referenced by main().

◆ help()

static void help ( const char *  progname)
static

Definition at line 757 of file pg_combinebackup.c.

758 {
759  printf(_("%s reconstructs full backups from incrementals.\n\n"), progname);
760  printf(_("Usage:\n"));
761  printf(_(" %s [OPTION]... DIRECTORY...\n"), progname);
762  printf(_("\nOptions:\n"));
763  printf(_(" -d, --debug generate lots of debugging output\n"));
764  printf(_(" -n, --dry-run do not actually do anything\n"));
765  printf(_(" -N, --no-sync do not wait for changes to be written safely to disk\n"));
766  printf(_(" -o, --output=DIRECTORY output directory\n"));
767  printf(_(" -T, --tablespace-mapping=OLDDIR=NEWDIR\n"
768  " relocate tablespace in OLDDIR to NEWDIR\n"));
769  printf(_(" --clone clone (reflink) instead of copying files\n"));
770  printf(_(" --copy copy files (default)\n"));
771  printf(_(" --copy-file-range copy using copy_file_range() syscall\n"));
772  printf(_(" --manifest-checksums=SHA{224,256,384,512}|CRC32C|NONE\n"
773  " use algorithm for manifest checksums\n"));
774  printf(_(" --no-manifest suppress generation of backup manifest\n"));
775  printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
776  printf(_(" -V, --version output version information, then exit\n"));
777  printf(_(" -?, --help show this help, then exit\n"));
778 
779  printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
780  printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
781 }
#define _(x)
Definition: elog.c:90
const char * progname
Definition: main.c:44
#define printf(...)
Definition: port.h:244

References _, printf, and progname.

Referenced by main().

◆ main()

int main ( int  argc,
char *  argv[] 
)

Definition at line 130 of file pg_combinebackup.c.

131 {
132  static struct option long_options[] = {
133  {"debug", no_argument, NULL, 'd'},
134  {"dry-run", no_argument, NULL, 'n'},
135  {"no-sync", no_argument, NULL, 'N'},
136  {"output", required_argument, NULL, 'o'},
137  {"tablespace-mapping", required_argument, NULL, 'T'},
138  {"manifest-checksums", required_argument, NULL, 1},
139  {"no-manifest", no_argument, NULL, 2},
140  {"sync-method", required_argument, NULL, 3},
141  {"clone", no_argument, NULL, 4},
142  {"copy", no_argument, NULL, 5},
143  {"copy-file-range", no_argument, NULL, 6},
144  {NULL, 0, NULL, 0}
145  };
146 
147  const char *progname;
148  char *last_input_dir;
149  int i;
150  int optindex;
151  int c;
152  int n_backups;
153  int n_prior_backups;
154  int version;
155  uint64 system_identifier;
156  char **prior_backup_dirs;
157  cb_options opt;
158  cb_tablespace *tablespaces;
159  cb_tablespace *ts;
160  StringInfo last_backup_label;
161  manifest_data **manifests;
162  manifest_writer *mwriter;
163 
164  pg_logging_init(argv[0]);
165  progname = get_progname(argv[0]);
166  set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_combinebackup"));
167  handle_help_version_opts(argc, argv, progname, help);
168 
169  memset(&opt, 0, sizeof(opt));
173 
174  /* process command-line options */
175  while ((c = getopt_long(argc, argv, "dnNo:T:",
176  long_options, &optindex)) != -1)
177  {
178  switch (c)
179  {
180  case 'd':
181  opt.debug = true;
183  break;
184  case 'n':
185  opt.dry_run = true;
186  break;
187  case 'N':
188  opt.no_sync = true;
189  break;
190  case 'o':
191  opt.output = optarg;
192  break;
193  case 'T':
195  break;
196  case 1:
198  &opt.manifest_checksums))
199  pg_fatal("unrecognized checksum algorithm: \"%s\"",
200  optarg);
201  break;
202  case 2:
203  opt.no_manifest = true;
204  break;
205  case 3:
207  exit(1);
208  break;
209  case 4:
211  break;
212  case 5:
214  break;
215  case 6:
217  break;
218  default:
219  /* getopt_long already emitted a complaint */
220  pg_log_error_hint("Try \"%s --help\" for more information.", progname);
221  exit(1);
222  }
223  }
224 
225  if (optind >= argc)
226  {
227  pg_log_error("no input directories specified");
228  pg_log_error_hint("Try \"%s --help\" for more information.", progname);
229  exit(1);
230  }
231 
232  if (opt.output == NULL)
233  pg_fatal("no output directory specified");
234 
235  /* If no manifest is needed, no checksums are needed, either. */
236  if (opt.no_manifest)
238 
239  /* Check that the platform supports the requested copy method. */
240  if (opt.copy_method == COPY_METHOD_CLONE)
241  {
242 #if (defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)) || \
243  (defined(__linux__) && defined(FICLONE))
244 
245  if (opt.dry_run)
246  pg_log_debug("would use cloning to copy files");
247  else
248  pg_log_debug("will use cloning to copy files");
249 
250 #else
251  pg_fatal("file cloning not supported on this platform");
252 #endif
253  }
255  {
256 #if defined(HAVE_COPY_FILE_RANGE)
257 
258  if (opt.dry_run)
259  pg_log_debug("would use copy_file_range to copy blocks");
260  else
261  pg_log_debug("will use copy_file_range to copy blocks");
262 
263 #else
264  pg_fatal("copy_file_range not supported on this platform");
265 #endif
266  }
267 
268  /* Read the server version from the final backup. */
269  version = read_pg_version_file(argv[argc - 1]);
270 
271  /* Sanity-check control files. */
272  n_backups = argc - optind;
273  system_identifier = check_control_files(n_backups, argv + optind);
274 
275  /* Sanity-check backup_label files, and get the contents of the last one. */
276  last_backup_label = check_backup_label_files(n_backups, argv + optind);
277 
278  /*
279  * We'll need the pathnames to the prior backups. By "prior" we mean all
280  * but the last one listed on the command line.
281  */
282  n_prior_backups = argc - optind - 1;
283  prior_backup_dirs = argv + optind;
284 
285  /* Load backup manifests. */
286  manifests = load_backup_manifests(n_backups, prior_backup_dirs);
287 
288  /*
289  * Validate the manifest system identifier against the backup system
290  * identifier.
291  */
292  for (i = 0; i < n_backups; i++)
293  {
294  if (manifests[i] &&
295  manifests[i]->system_identifier != system_identifier)
296  {
297  char *controlpath;
298 
299  controlpath = psprintf("%s/%s", prior_backup_dirs[i], "global/pg_control");
300 
301  pg_fatal("%s: manifest system identifier is %llu, but control file has %llu",
302  controlpath,
303  (unsigned long long) manifests[i]->system_identifier,
304  (unsigned long long) system_identifier);
305  }
306  }
307 
308  /* Figure out which tablespaces are going to be included in the output. */
309  last_input_dir = argv[argc - 1];
310  check_input_dir_permissions(last_input_dir);
311  tablespaces = scan_for_existing_tablespaces(last_input_dir, &opt);
312 
313  /*
314  * Create output directories.
315  *
316  * We create one output directory for the main data directory plus one for
317  * each non-in-place tablespace. create_output_directory() will arrange
318  * for those directories to be cleaned up on failure. In-place tablespaces
319  * aren't handled at this stage because they're located beneath the main
320  * output directory, and thus the cleanup of that directory will get rid
321  * of them. Plus, the pg_tblspc directory that needs to contain them
322  * doesn't exist yet.
323  */
325  create_output_directory(opt.output, &opt);
326  for (ts = tablespaces; ts != NULL; ts = ts->next)
327  if (!ts->in_place)
328  create_output_directory(ts->new_dir, &opt);
329 
330  /* If we need to write a backup_manifest, prepare to do so. */
331  if (!opt.dry_run && !opt.no_manifest)
332  {
333  mwriter = create_manifest_writer(opt.output, system_identifier);
334 
335  /*
336  * Verify that we have a backup manifest for the final backup; else we
337  * won't have the WAL ranges for the resulting manifest.
338  */
339  if (manifests[n_prior_backups] == NULL)
340  pg_fatal("can't generate a manifest because no manifest is available for the final input backup");
341  }
342  else
343  mwriter = NULL;
344 
345  /* Write backup label into output directory. */
346  if (opt.dry_run)
347  pg_log_debug("would generate \"%s/backup_label\"", opt.output);
348  else
349  {
350  pg_log_debug("generating \"%s/backup_label\"", opt.output);
351  last_backup_label->cursor = 0;
352  write_backup_label(opt.output, last_backup_label,
353  opt.manifest_checksums, mwriter);
354  }
355 
356  /* Process everything that's not part of a user-defined tablespace. */
357  pg_log_debug("processing backup directory \"%s\"", last_input_dir);
358  process_directory_recursively(InvalidOid, last_input_dir, opt.output,
359  NULL, n_prior_backups, prior_backup_dirs,
360  manifests, mwriter, &opt);
361 
362  /* Process user-defined tablespaces. */
363  for (ts = tablespaces; ts != NULL; ts = ts->next)
364  {
365  pg_log_debug("processing tablespace directory \"%s\"", ts->old_dir);
366 
367  /*
368  * If it's a normal tablespace, we need to set up a symbolic link from
369  * pg_tblspc/${OID} to the target directory; if it's an in-place
370  * tablespace, we need to create a directory at pg_tblspc/${OID}.
371  */
372  if (!ts->in_place)
373  {
374  char linkpath[MAXPGPATH];
375 
376  snprintf(linkpath, MAXPGPATH, "%s/pg_tblspc/%u", opt.output,
377  ts->oid);
378 
379  if (opt.dry_run)
380  pg_log_debug("would create symbolic link from \"%s\" to \"%s\"",
381  linkpath, ts->new_dir);
382  else
383  {
384  pg_log_debug("creating symbolic link from \"%s\" to \"%s\"",
385  linkpath, ts->new_dir);
386  if (symlink(ts->new_dir, linkpath) != 0)
387  pg_fatal("could not create symbolic link from \"%s\" to \"%s\": %m",
388  linkpath, ts->new_dir);
389  }
390  }
391  else
392  {
393  if (opt.dry_run)
394  pg_log_debug("would create directory \"%s\"", ts->new_dir);
395  else
396  {
397  pg_log_debug("creating directory \"%s\"", ts->new_dir);
398  if (pg_mkdir_p(ts->new_dir, pg_dir_create_mode) == -1)
399  pg_fatal("could not create directory \"%s\": %m",
400  ts->new_dir);
401  }
402  }
403 
404  /* OK, now handle the directory contents. */
406  NULL, n_prior_backups, prior_backup_dirs,
407  manifests, mwriter, &opt);
408  }
409 
410  /* Finalize the backup_manifest, if we're generating one. */
411  if (mwriter != NULL)
412  finalize_manifest(mwriter,
413  manifests[n_prior_backups]->first_wal_range);
414 
415  /* fsync that output directory unless we've been told not to do so */
416  if (!opt.no_sync)
417  {
418  if (opt.dry_run)
419  pg_log_debug("would recursively fsync \"%s\"", opt.output);
420  else
421  {
422  pg_log_debug("recursively fsyncing \"%s\"", opt.output);
423  sync_pgdata(opt.output, version * 10000, opt.sync_method);
424  }
425  }
426 
427  /* It's a success, so don't remove the output directories. */
429  exit(0);
430 }
void write_backup_label(char *output_directory, StringInfo buf, pg_checksum_type checksum_type, manifest_writer *mwriter)
Definition: backup_label.c:127
#define PG_TEXTDOMAIN(domain)
Definition: c.h:1214
bool pg_checksum_parse_type(char *name, pg_checksum_type *type)
@ CHECKSUM_TYPE_NONE
@ CHECKSUM_TYPE_CRC32C
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition: exec.c:448
@ COPY_METHOD_CLONE
Definition: copy_file.h:23
@ COPY_METHOD_COPY
Definition: copy_file.h:24
@ COPY_METHOD_COPY_FILE_RANGE
Definition: copy_file.h:25
@ DATA_DIR_SYNC_METHOD_FSYNC
Definition: file_utils.h:29
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition: getopt_long.c:60
#define no_argument
Definition: getopt_long.h:24
#define required_argument
Definition: getopt_long.h:25
exit(1)
manifest_data ** load_backup_manifests(int n_backups, char **backup_directories)
Definition: load_manifest.c:83
void pg_logging_increase_verbosity(void)
Definition: logging.c:184
void pg_logging_init(const char *argv0)
Definition: logging.c:83
#define pg_log_error_hint(...)
Definition: logging.h:112
void handle_help_version_opts(int argc, char *argv[], const char *fixed_progname, help_handler hlp)
Definition: option_utils.c:24
bool parse_sync_method(const char *optarg, DataDirSyncMethod *sync_method)
Definition: option_utils.c:90
static cb_tablespace * scan_for_existing_tablespaces(char *pathname, cb_options *opt)
static void help(const char *progname)
static void process_directory_recursively(Oid tsoid, char *input_directory, char *output_directory, char *relative_path, int n_prior_backups, char **prior_backup_dirs, manifest_data **manifests, manifest_writer *mwriter, cb_options *opt)
static void create_output_directory(char *dirname, cb_options *opt)
static void check_input_dir_permissions(char *dir)
static uint64 check_control_files(int n_backups, char **backup_dirs)
static void cleanup_directories_atexit(void)
static StringInfo check_backup_label_files(int n_backups, char **backup_dirs)
static void add_tablespace_mapping(cb_options *opt, char *arg)
static int read_pg_version_file(char *directory)
static void reset_directory_cleanup_list(void)
PGDLLIMPORT int optind
Definition: getopt.c:50
PGDLLIMPORT char * optarg
Definition: getopt.c:52
const char * get_progname(const char *argv0)
Definition: path.c:574
#define InvalidOid
Definition: postgres_ext.h:36
char * c
DataDirSyncMethod sync_method
CopyMethod copy_method
pg_checksum_type manifest_checksums
char new_dir[MAXPGPATH]
struct cb_tablespace * next
char old_dir[MAXPGPATH]
#define symlink(oldpath, newpath)
Definition: win32_port.h:235
manifest_writer * create_manifest_writer(char *directory, uint64 system_identifier)
void finalize_manifest(manifest_writer *mwriter, manifest_wal_range *first_wal_range)

References add_tablespace_mapping(), check_backup_label_files(), check_control_files(), check_input_dir_permissions(), CHECKSUM_TYPE_CRC32C, CHECKSUM_TYPE_NONE, cleanup_directories_atexit(), cb_options::copy_method, COPY_METHOD_CLONE, COPY_METHOD_COPY, COPY_METHOD_COPY_FILE_RANGE, create_manifest_writer(), create_output_directory(), StringInfoData::cursor, DATA_DIR_SYNC_METHOD_FSYNC, cb_options::debug, cb_options::dry_run, exit(), finalize_manifest(), get_progname(), getopt_long(), handle_help_version_opts(), help(), i, cb_tablespace::in_place, InvalidOid, load_backup_manifests(), cb_options::manifest_checksums, MAXPGPATH, cb_tablespace::new_dir, cb_tablespace::next, no_argument, cb_options::no_manifest, cb_options::no_sync, cb_tablespace::oid, cb_tablespace::old_dir, optarg, optind, cb_options::output, parse_sync_method(), pg_checksum_parse_type(), pg_dir_create_mode, pg_fatal, pg_log_debug, pg_log_error, pg_log_error_hint, pg_logging_increase_verbosity(), pg_logging_init(), pg_mkdir_p(), PG_TEXTDOMAIN, process_directory_recursively(), progname, psprintf(), read_pg_version_file(), required_argument, reset_directory_cleanup_list(), scan_for_existing_tablespaces(), set_pglocale_pgservice(), snprintf, symlink, cb_options::sync_method, and write_backup_label().

◆ parse_oid()

static bool parse_oid ( char *  s,
Oid result 
)
static

Definition at line 789 of file pg_combinebackup.c.

790 {
791  Oid oid;
792  char *ep;
793 
794  errno = 0;
795  oid = strtoul(s, &ep, 10);
796  if (errno != 0 || *ep != '\0' || oid < 1 || oid > PG_UINT32_MAX)
797  return false;
798 
799  *result = oid;
800  return true;
801 }
#define PG_UINT32_MAX
Definition: c.h:590
unsigned int Oid
Definition: postgres_ext.h:31

References PG_UINT32_MAX.

Referenced by process_directory_recursively(), and scan_for_existing_tablespaces().

◆ process_directory_recursively()

static void process_directory_recursively ( Oid  tsoid,
char *  input_directory,
char *  output_directory,
char *  relative_path,
int  n_prior_backups,
char **  prior_backup_dirs,
manifest_data **  manifests,
manifest_writer mwriter,
cb_options opt 
)
static

Definition at line 823 of file pg_combinebackup.c.

832 {
833  char ifulldir[MAXPGPATH];
834  char ofulldir[MAXPGPATH];
835  char manifest_prefix[MAXPGPATH];
836  DIR *dir;
837  struct dirent *de;
838  bool is_pg_tblspc = false;
839  bool is_pg_wal = false;
840  bool is_incremental_dir = false;
841  manifest_data *latest_manifest = manifests[n_prior_backups];
842  pg_checksum_type checksum_type;
843 
844  /*
845  * Classify this directory.
846  *
847  * We set is_pg_tblspc only for the toplevel pg_tblspc directory, because
848  * the symlinks in that specific directory require special handling.
849  *
850  * We set is_pg_wal for the toplevel WAL directory and all of its
851  * subdirectories, because those files are not included in the backup
852  * manifest and hence need special treatement. (Since incremental backup
853  * does not exist in pre-v10 versions, we don't have to worry about the
854  * old pg_xlog naming.)
855  *
856  * We set is_incremental_dir for directories that can contain incremental
857  * files requiring reconstruction. If such files occur outside these
858  * directories, we want to just copy them straight to the output
859  * directory. This is to protect against a user creating a file with a
860  * strange name like INCREMENTAL.config and then complaining that
861  * incremental backups don't work properly. The test here is a bit tricky:
862  * incremental files occur in subdirectories of base, in pg_global itself,
863  * and in subdirectories of pg_tblspc only if in-place tablespaces are
864  * used.
865  */
866  if (OidIsValid(tsoid))
867  is_incremental_dir = true;
868  else if (relative_path != NULL)
869  {
870  is_pg_tblspc = strcmp(relative_path, "pg_tblspc") == 0;
871  is_pg_wal = (strcmp(relative_path, "pg_wal") == 0 ||
872  strncmp(relative_path, "pg_wal/", 7) == 0);
873  is_incremental_dir = strncmp(relative_path, "base/", 5) == 0 ||
874  strcmp(relative_path, "global") == 0 ||
875  strncmp(relative_path, "pg_tblspc/", 10) == 0;
876  }
877 
878  /*
879  * If we're under pg_wal, then we don't need checksums, because these
880  * files aren't included in the backup manifest. Otherwise use whatever
881  * type of checksum is configured.
882  */
883  if (!is_pg_wal)
884  checksum_type = opt->manifest_checksums;
885  else
886  checksum_type = CHECKSUM_TYPE_NONE;
887 
888  /*
889  * Append the relative path to the input and output directories, and
890  * figure out the appropriate prefix to add to files in this directory
891  * when looking them up in a backup manifest.
892  */
893  if (relative_path == NULL)
894  {
895  strlcpy(ifulldir, input_directory, MAXPGPATH);
896  strlcpy(ofulldir, output_directory, MAXPGPATH);
897  if (OidIsValid(tsoid))
898  snprintf(manifest_prefix, MAXPGPATH, "pg_tblspc/%u/", tsoid);
899  else
900  manifest_prefix[0] = '\0';
901  }
902  else
903  {
904  snprintf(ifulldir, MAXPGPATH, "%s/%s", input_directory,
905  relative_path);
906  snprintf(ofulldir, MAXPGPATH, "%s/%s", output_directory,
907  relative_path);
908  if (OidIsValid(tsoid))
909  snprintf(manifest_prefix, MAXPGPATH, "pg_tblspc/%u/%s/",
910  tsoid, relative_path);
911  else
912  snprintf(manifest_prefix, MAXPGPATH, "%s/", relative_path);
913  }
914 
915  /*
916  * Toplevel output directories have already been created by the time this
917  * function is called, but any subdirectories are our responsibility.
918  */
919  if (relative_path != NULL)
920  {
921  if (opt->dry_run)
922  pg_log_debug("would create directory \"%s\"", ofulldir);
923  else
924  {
925  pg_log_debug("creating directory \"%s\"", ofulldir);
926  if (mkdir(ofulldir, pg_dir_create_mode) == -1)
927  pg_fatal("could not create directory \"%s\": %m", ofulldir);
928  }
929  }
930 
931  /* It's time to scan the directory. */
932  if ((dir = opendir(ifulldir)) == NULL)
933  pg_fatal("could not open directory \"%s\": %m", ifulldir);
934  while (errno = 0, (de = readdir(dir)) != NULL)
935  {
937  char ifullpath[MAXPGPATH];
938  char ofullpath[MAXPGPATH];
939  char manifest_path[MAXPGPATH];
940  Oid oid = InvalidOid;
941  int checksum_length = 0;
942  uint8 *checksum_payload = NULL;
943  pg_checksum_context checksum_ctx;
944 
945  /* Ignore "." and ".." entries. */
946  if (strcmp(de->d_name, ".") == 0 ||
947  strcmp(de->d_name, "..") == 0)
948  continue;
949 
950  /* Construct input path. */
951  snprintf(ifullpath, MAXPGPATH, "%s/%s", ifulldir, de->d_name);
952 
953  /* Figure out what kind of directory entry this is. */
954  type = get_dirent_type(ifullpath, de, false, PG_LOG_ERROR);
955  if (type == PGFILETYPE_ERROR)
956  exit(1);
957 
958  /*
959  * If we're processing pg_tblspc, then check whether the filename
960  * looks like it could be a tablespace OID. If so, and if the
961  * directory entry is a symbolic link or a directory, skip it.
962  *
963  * Our goal here is to ignore anything that would have been considered
964  * by scan_for_existing_tablespaces to be a tablespace.
965  */
966  if (is_pg_tblspc && parse_oid(de->d_name, &oid) &&
968  continue;
969 
970  /* If it's a directory, recurse. */
971  if (type == PGFILETYPE_DIR)
972  {
973  char new_relative_path[MAXPGPATH];
974 
975  /* Append new pathname component to relative path. */
976  if (relative_path == NULL)
977  strlcpy(new_relative_path, de->d_name, MAXPGPATH);
978  else
979  snprintf(new_relative_path, MAXPGPATH, "%s/%s", relative_path,
980  de->d_name);
981 
982  /* And recurse. */
984  input_directory, output_directory,
985  new_relative_path,
986  n_prior_backups, prior_backup_dirs,
987  manifests, mwriter, opt);
988  continue;
989  }
990 
991  /* Skip anything that's not a regular file. */
992  if (type != PGFILETYPE_REG)
993  {
994  if (type == PGFILETYPE_LNK)
995  pg_log_warning("skipping symbolic link \"%s\"", ifullpath);
996  else
997  pg_log_warning("skipping special file \"%s\"", ifullpath);
998  continue;
999  }
1000 
1001  /*
1002  * Skip the backup_label and backup_manifest files; they require
1003  * special handling and are handled elsewhere.
1004  */
1005  if (relative_path == NULL &&
1006  (strcmp(de->d_name, "backup_label") == 0 ||
1007  strcmp(de->d_name, "backup_manifest") == 0))
1008  continue;
1009 
1010  /*
1011  * If it's an incremental file, hand it off to the reconstruction
1012  * code, which will figure out what to do.
1013  */
1014  if (is_incremental_dir &&
1015  strncmp(de->d_name, INCREMENTAL_PREFIX,
1017  {
1018  /* Output path should not include "INCREMENTAL." prefix. */
1019  snprintf(ofullpath, MAXPGPATH, "%s/%s", ofulldir,
1021 
1022 
1023  /* Manifest path likewise omits incremental prefix. */
1024  snprintf(manifest_path, MAXPGPATH, "%s%s", manifest_prefix,
1026 
1027  /* Reconstruction logic will do the rest. */
1028  reconstruct_from_incremental_file(ifullpath, ofullpath,
1029  manifest_prefix,
1031  n_prior_backups,
1032  prior_backup_dirs,
1033  manifests,
1034  manifest_path,
1035  checksum_type,
1036  &checksum_length,
1037  &checksum_payload,
1038  opt->copy_method,
1039  opt->debug,
1040  opt->dry_run);
1041  }
1042  else
1043  {
1044  /* Construct the path that the backup_manifest will use. */
1045  snprintf(manifest_path, MAXPGPATH, "%s%s", manifest_prefix,
1046  de->d_name);
1047 
1048  /*
1049  * It's not an incremental file, so we need to copy the entire
1050  * file to the output directory.
1051  *
1052  * If a checksum of the required type already exists in the
1053  * backup_manifest for the final input directory, we can save some
1054  * work by reusing that checksum instead of computing a new one.
1055  */
1056  if (checksum_type != CHECKSUM_TYPE_NONE &&
1057  latest_manifest != NULL)
1058  {
1059  manifest_file *mfile;
1060 
1061  mfile = manifest_files_lookup(latest_manifest->files,
1062  manifest_path);
1063  if (mfile == NULL)
1064  {
1065  char *bmpath;
1066 
1067  /*
1068  * The directory is out of sync with the backup_manifest,
1069  * so emit a warning.
1070  */
1071  bmpath = psprintf("%s/%s", input_directory,
1072  "backup_manifest");
1073  pg_log_warning("\"%s\" contains no entry for \"%s\"",
1074  bmpath, manifest_path);
1075  pfree(bmpath);
1076  }
1077  else if (mfile->checksum_type == checksum_type)
1078  {
1079  checksum_length = mfile->checksum_length;
1080  checksum_payload = mfile->checksum_payload;
1081  }
1082  }
1083 
1084  /*
1085  * If we're reusing a checksum, then we don't need copy_file() to
1086  * compute one for us, but otherwise, it needs to compute whatever
1087  * type of checksum we need.
1088  */
1089  if (checksum_length != 0)
1090  pg_checksum_init(&checksum_ctx, CHECKSUM_TYPE_NONE);
1091  else
1092  pg_checksum_init(&checksum_ctx, checksum_type);
1093 
1094  /* Actually copy the file. */
1095  snprintf(ofullpath, MAXPGPATH, "%s/%s", ofulldir, de->d_name);
1096  copy_file(ifullpath, ofullpath, &checksum_ctx,
1097  opt->copy_method, opt->dry_run);
1098 
1099  /*
1100  * If copy_file() performed a checksum calculation for us, then
1101  * save the results (except in dry-run mode, when there's no
1102  * point).
1103  */
1104  if (checksum_ctx.type != CHECKSUM_TYPE_NONE && !opt->dry_run)
1105  {
1106  checksum_payload = pg_malloc(PG_CHECKSUM_MAX_LENGTH);
1107  checksum_length = pg_checksum_final(&checksum_ctx,
1108  checksum_payload);
1109  }
1110  }
1111 
1112  /* Generate manifest entry, if needed. */
1113  if (mwriter != NULL)
1114  {
1115  struct stat sb;
1116 
1117  /*
1118  * In order to generate a manifest entry, we need the file size
1119  * and mtime. We have no way to know the correct mtime except to
1120  * stat() the file, so just do that and get the size as well.
1121  *
1122  * If we didn't need the mtime here, we could try to obtain the
1123  * file size from the reconstruction or file copy process above,
1124  * although that is actually not convenient in all cases. If we
1125  * write the file ourselves then clearly we can keep a count of
1126  * bytes, but if we use something like CopyFile() then it's
1127  * trickier. Since we have to stat() anyway to get the mtime,
1128  * there's no point in worrying about it.
1129  */
1130  if (stat(ofullpath, &sb) < 0)
1131  pg_fatal("could not stat file \"%s\": %m", ofullpath);
1132 
1133  /* OK, now do the work. */
1134  add_file_to_manifest(mwriter, manifest_path,
1135  sb.st_size, sb.st_mtime,
1136  checksum_type, checksum_length,
1137  checksum_payload);
1138  }
1139 
1140  /* Avoid leaking memory. */
1141  if (checksum_payload != NULL)
1142  pfree(checksum_payload);
1143  }
1144 
1145  closedir(dir);
1146 }
unsigned char uint8
Definition: c.h:504
#define OidIsValid(objectId)
Definition: c.h:775
int pg_checksum_final(pg_checksum_context *context, uint8 *output)
int pg_checksum_init(pg_checksum_context *context, pg_checksum_type type)
#define PG_CHECKSUM_MAX_LENGTH
pg_checksum_type
void copy_file(const char *fromfile, const char *tofile)
Definition: copydir.c:117
int closedir(DIR *)
Definition: dirent.c:127
struct dirent * readdir(DIR *)
Definition: dirent.c:78
DIR * opendir(const char *)
Definition: dirent.c:33
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
PGFileType get_dirent_type(const char *path, const struct dirent *de, bool look_through_symlinks, int elevel)
Definition: file_utils.c:525
PGFileType
Definition: file_utils.h:19
@ PGFILETYPE_LNK
Definition: file_utils.h:24
@ PGFILETYPE_DIR
Definition: file_utils.h:23
@ PGFILETYPE_REG
Definition: file_utils.h:22
@ PGFILETYPE_ERROR
Definition: file_utils.h:20
@ PG_LOG_ERROR
Definition: logging.h:43
#define INCREMENTAL_PREFIX_LENGTH
#define INCREMENTAL_PREFIX
static bool parse_oid(char *s, Oid *result)
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
void reconstruct_from_incremental_file(char *input_filename, char *output_filename, char *relative_path, char *bare_file_name, int n_prior_backups, char **prior_backup_dirs, manifest_data **manifests, char *manifest_path, pg_checksum_type checksum_type, int *checksum_length, uint8 **checksum_payload, CopyMethod copy_method, bool debug, bool dry_run)
Definition: reconstruct.c:87
Definition: dirent.c:26
Definition: dirent.h:10
char d_name[MAX_PATH]
Definition: dirent.h:15
manifest_files_hash * files
Definition: load_manifest.h:59
uint8 * checksum_payload
Definition: load_manifest.h:29
pg_checksum_type checksum_type
Definition: load_manifest.h:27
pg_checksum_type type
const char * type
#define mkdir(a, b)
Definition: win32_port.h:80
void add_file_to_manifest(manifest_writer *mwriter, const char *manifest_path, size_t size, time_t mtime, pg_checksum_type checksum_type, int checksum_length, uint8 *checksum_payload)

References add_file_to_manifest(), manifest_file::checksum_length, manifest_file::checksum_payload, manifest_file::checksum_type, CHECKSUM_TYPE_NONE, closedir(), copy_file(), cb_options::copy_method, dirent::d_name, cb_options::debug, cb_options::dry_run, exit(), manifest_data::files, get_dirent_type(), INCREMENTAL_PREFIX, INCREMENTAL_PREFIX_LENGTH, InvalidOid, cb_options::manifest_checksums, MAXPGPATH, mkdir, OidIsValid, opendir(), parse_oid(), pfree(), pg_checksum_final(), pg_checksum_init(), PG_CHECKSUM_MAX_LENGTH, pg_dir_create_mode, pg_fatal, pg_log_debug, PG_LOG_ERROR, pg_log_warning, pg_malloc(), PGFILETYPE_DIR, PGFILETYPE_ERROR, PGFILETYPE_LNK, PGFILETYPE_REG, psprintf(), readdir(), reconstruct_from_incremental_file(), snprintf, stat::st_mtime, stat::st_size, stat, strlcpy(), type, and pg_checksum_context::type.

Referenced by main().

◆ read_pg_version_file()

static int read_pg_version_file ( char *  directory)
static

Definition at line 1154 of file pg_combinebackup.c.

1155 {
1156  char filename[MAXPGPATH];
1158  int fd;
1159  int version;
1160  char *ep;
1161 
1162  /* Construct pathname. */
1163  snprintf(filename, MAXPGPATH, "%s/PG_VERSION", directory);
1164 
1165  /* Open file. */
1166  if ((fd = open(filename, O_RDONLY, 0)) < 0)
1167  pg_fatal("could not open file \"%s\": %m", filename);
1168 
1169  /* Read into memory. Length limit of 128 should be more than generous. */
1170  initStringInfo(&buf);
1171  slurp_file(fd, filename, &buf, 128);
1172 
1173  /* Close the file. */
1174  if (close(fd) != 0)
1175  pg_fatal("could not close file \"%s\": %m", filename);
1176 
1177  /* Convert to integer. */
1178  errno = 0;
1179  version = strtoul(buf.data, &ep, 10);
1180  if (errno != 0 || *ep != '\n')
1181  {
1182  /*
1183  * Incremental backup is not relevant to very old server versions that
1184  * used multi-part version number (e.g. 9.6, or 8.4). So if we see
1185  * what looks like the beginning of such a version number, just bail
1186  * out.
1187  */
1188  if (version < 10 && *ep == '.')
1189  pg_fatal("%s: server version too old", filename);
1190  pg_fatal("%s: could not parse version number", filename);
1191  }
1192 
1193  /* Debugging output. */
1194  pg_log_debug("read server version %d from file \"%s\"", version, filename);
1195 
1196  /* Release memory and return result. */
1197  pfree(buf.data);
1198  return version * 10000;
1199 }
static char * filename
Definition: pg_dumpall.c:119
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
static const char * directory
Definition: zic.c:634

References buf, close, directory, fd(), filename, initStringInfo(), MAXPGPATH, pfree(), pg_fatal, pg_log_debug, slurp_file(), and snprintf.

Referenced by main().

◆ remember_to_cleanup_directory()

static void remember_to_cleanup_directory ( char *  target_path,
bool  rmtopdir 
)
static

Definition at line 1205 of file pg_combinebackup.c.

1206 {
1207  cb_cleanup_dir *dir = pg_malloc(sizeof(cb_cleanup_dir));
1208 
1209  dir->target_path = target_path;
1210  dir->rmtopdir = rmtopdir;
1211  dir->next = cleanup_dir_list;
1212  cleanup_dir_list = dir;
1213 }

References cleanup_dir_list, cb_cleanup_dir::next, pg_malloc(), cb_cleanup_dir::rmtopdir, and cb_cleanup_dir::target_path.

Referenced by create_output_directory().

◆ reset_directory_cleanup_list()

static void reset_directory_cleanup_list ( void  )
static

Definition at line 1226 of file pg_combinebackup.c.

1227 {
1228  while (cleanup_dir_list != NULL)
1229  {
1231 
1233  pfree(dir);
1234  }
1235 }

References cleanup_dir_list, cb_cleanup_dir::next, and pfree().

Referenced by main().

◆ scan_for_existing_tablespaces()

static cb_tablespace * scan_for_existing_tablespaces ( char *  pathname,
cb_options opt 
)
static

Definition at line 1245 of file pg_combinebackup.c.

1246 {
1247  char pg_tblspc[MAXPGPATH];
1248  DIR *dir;
1249  struct dirent *de;
1250  cb_tablespace *tslist = NULL;
1251 
1252  snprintf(pg_tblspc, MAXPGPATH, "%s/pg_tblspc", pathname);
1253  pg_log_debug("scanning \"%s\"", pg_tblspc);
1254 
1255  if ((dir = opendir(pg_tblspc)) == NULL)
1256  pg_fatal("could not open directory \"%s\": %m", pg_tblspc);
1257 
1258  while (errno = 0, (de = readdir(dir)) != NULL)
1259  {
1260  Oid oid;
1261  char tblspcdir[MAXPGPATH];
1262  char link_target[MAXPGPATH];
1263  int link_length;
1264  cb_tablespace *ts;
1265  cb_tablespace *otherts;
1266  PGFileType type;
1267 
1268  /* Silently ignore "." and ".." entries. */
1269  if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
1270  continue;
1271 
1272  /* Construct full pathname. */
1273  snprintf(tblspcdir, MAXPGPATH, "%s/%s", pg_tblspc, de->d_name);
1274 
1275  /* Ignore any file name that doesn't look like a proper OID. */
1276  if (!parse_oid(de->d_name, &oid))
1277  {
1278  pg_log_debug("skipping \"%s\" because the filename is not a legal tablespace OID",
1279  tblspcdir);
1280  continue;
1281  }
1282 
1283  /* Only symbolic links and directories are tablespaces. */
1284  type = get_dirent_type(tblspcdir, de, false, PG_LOG_ERROR);
1285  if (type == PGFILETYPE_ERROR)
1286  exit(1);
1287  if (type != PGFILETYPE_LNK && type != PGFILETYPE_DIR)
1288  {
1289  pg_log_debug("skipping \"%s\" because it is neither a symbolic link nor a directory",
1290  tblspcdir);
1291  continue;
1292  }
1293 
1294  /* Create a new tablespace object. */
1295  ts = pg_malloc0(sizeof(cb_tablespace));
1296  ts->oid = oid;
1297 
1298  /*
1299  * If it's a link, it's not an in-place tablespace. Otherwise, it must
1300  * be a directory, and thus an in-place tablespace.
1301  */
1302  if (type == PGFILETYPE_LNK)
1303  {
1304  cb_tablespace_mapping *tsmap;
1305 
1306  /* Read the link target. */
1307  link_length = readlink(tblspcdir, link_target, sizeof(link_target));
1308  if (link_length < 0)
1309  pg_fatal("could not read symbolic link \"%s\": %m",
1310  tblspcdir);
1311  if (link_length >= sizeof(link_target))
1312  pg_fatal("target of symbolic link \"%s\" is too long", tblspcdir);
1313  link_target[link_length] = '\0';
1314  if (!is_absolute_path(link_target))
1315  pg_fatal("target of symbolic link \"%s\" is relative", tblspcdir);
1316 
1317  /* Canonicalize the link target. */
1318  canonicalize_path(link_target);
1319 
1320  /*
1321  * Find the corresponding tablespace mapping and copy the relevant
1322  * details into the new tablespace entry.
1323  */
1324  for (tsmap = opt->tsmappings; tsmap != NULL; tsmap = tsmap->next)
1325  {
1326  if (strcmp(tsmap->old_dir, link_target) == 0)
1327  {
1328  strlcpy(ts->old_dir, tsmap->old_dir, MAXPGPATH);
1329  strlcpy(ts->new_dir, tsmap->new_dir, MAXPGPATH);
1330  ts->in_place = false;
1331  break;
1332  }
1333  }
1334 
1335  /* Every non-in-place tablespace must be mapped. */
1336  if (tsmap == NULL)
1337  pg_fatal("tablespace at \"%s\" has no tablespace mapping",
1338  link_target);
1339  }
1340  else
1341  {
1342  /*
1343  * For an in-place tablespace, there's no separate directory, so
1344  * we just record the paths within the data directories.
1345  */
1346  snprintf(ts->old_dir, MAXPGPATH, "%s/%s", pg_tblspc, de->d_name);
1347  snprintf(ts->new_dir, MAXPGPATH, "%s/pg_tblspc/%s", opt->output,
1348  de->d_name);
1349  ts->in_place = true;
1350  }
1351 
1352  /* Tablespaces should not share a directory. */
1353  for (otherts = tslist; otherts != NULL; otherts = otherts->next)
1354  if (strcmp(ts->new_dir, otherts->new_dir) == 0)
1355  pg_fatal("tablespaces with OIDs %u and %u both point at directory \"%s\"",
1356  otherts->oid, oid, ts->new_dir);
1357 
1358  /* Add this tablespace to the list. */
1359  ts->next = tslist;
1360  tslist = ts;
1361  }
1362 
1363  if (closedir(dir) != 0)
1364  pg_fatal("could not close directory \"%s\": %m", pg_tblspc);
1365 
1366  return tslist;
1367 }
#define readlink(path, buf, size)
Definition: win32_port.h:236

References canonicalize_path(), closedir(), dirent::d_name, exit(), get_dirent_type(), cb_tablespace::in_place, is_absolute_path, MAXPGPATH, cb_tablespace_mapping::new_dir, cb_tablespace::new_dir, cb_tablespace_mapping::next, cb_tablespace::next, cb_tablespace::oid, cb_tablespace_mapping::old_dir, cb_tablespace::old_dir, opendir(), cb_options::output, parse_oid(), pg_fatal, pg_log_debug, PG_LOG_ERROR, pg_malloc0(), PGFILETYPE_DIR, PGFILETYPE_ERROR, PGFILETYPE_LNK, readdir(), readlink, snprintf, strlcpy(), cb_options::tsmappings, and type.

Referenced by main().

◆ slurp_file()

static void slurp_file ( int  fd,
char *  filename,
StringInfo  buf,
int  maxlen 
)
static

Definition at line 1376 of file pg_combinebackup.c.

1377 {
1378  struct stat st;
1379  ssize_t rb;
1380 
1381  /* Check file size, and complain if it's too large. */
1382  if (fstat(fd, &st) != 0)
1383  pg_fatal("could not stat file \"%s\": %m", filename);
1384  if (st.st_size > maxlen)
1385  pg_fatal("file \"%s\" is too large", filename);
1386 
1387  /* Make sure we have enough space. */
1388  enlargeStringInfo(buf, st.st_size);
1389 
1390  /* Read the data. */
1391  rb = read(fd, &buf->data[buf->len], st.st_size);
1392 
1393  /*
1394  * We don't expect any concurrent changes, so we should read exactly the
1395  * expected number of bytes.
1396  */
1397  if (rb != st.st_size)
1398  {
1399  if (rb < 0)
1400  pg_fatal("could not read file \"%s\": %m", filename);
1401  else
1402  pg_fatal("could not read file \"%s\": read %zd of %lld",
1403  filename, rb, (long long int) st.st_size);
1404  }
1405 
1406  /* Adjust buffer length for new data and restore trailing-\0 invariant */
1407  buf->len += rb;
1408  buf->data[buf->len] = '\0';
1409 }
#define read(a, b, c)
Definition: win32.h:13
void enlargeStringInfo(StringInfo str, int needed)
Definition: stringinfo.c:289
#define fstat
Definition: win32_port.h:283

References buf, enlargeStringInfo(), fd(), filename, fstat, pg_fatal, read, and stat::st_size.

Referenced by check_backup_label_files(), and read_pg_version_file().

Variable Documentation

◆ cleanup_dir_list

cb_cleanup_dir* cleanup_dir_list = NULL
static