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

cb_cleanup_dircleanup_dir_list = NULL
 

Macro Definition Documentation

◆ INCREMENTAL_PREFIX

#define INCREMENTAL_PREFIX   "INCREMENTAL."

Definition at line 35 of file pg_combinebackup.c.

◆ INCREMENTAL_PREFIX_LENGTH

#define INCREMENTAL_PREFIX_LENGTH   (sizeof(INCREMENTAL_PREFIX) - 1)

Definition at line 36 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 424 of file pg_combinebackup.c.

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

490 {
492  StringInfo lastbuf = buf;
493  int i;
494  TimeLineID check_tli = 0;
495  XLogRecPtr check_lsn = InvalidXLogRecPtr;
496 
497  /* Try to read each backup_label file in turn, last to first. */
498  for (i = n_backups - 1; i >= 0; --i)
499  {
500  char pathbuf[MAXPGPATH];
501  int fd;
502  TimeLineID start_tli;
503  TimeLineID previous_tli;
504  XLogRecPtr start_lsn;
505  XLogRecPtr previous_lsn;
506 
507  /* Open the backup_label file. */
508  snprintf(pathbuf, MAXPGPATH, "%s/backup_label", backup_dirs[i]);
509  pg_log_debug("reading \"%s\"", pathbuf);
510  if ((fd = open(pathbuf, O_RDONLY, 0)) < 0)
511  pg_fatal("could not open file \"%s\": %m", pathbuf);
512 
513  /*
514  * Slurp the whole file into memory.
515  *
516  * The exact size limit that we impose here doesn't really matter --
517  * most of what's supposed to be in the file is fixed size and quite
518  * short. However, the length of the backup_label is limited (at least
519  * by some parts of the code) to MAXPGPATH, so include that value in
520  * the maximum length that we tolerate.
521  */
522  slurp_file(fd, pathbuf, buf, 10000 + MAXPGPATH);
523 
524  /* Close the file. */
525  if (close(fd) != 0)
526  pg_fatal("could not close \"%s\": %m", pathbuf);
527 
528  /* Parse the file contents. */
529  parse_backup_label(pathbuf, buf, &start_tli, &start_lsn,
530  &previous_tli, &previous_lsn);
531 
532  /*
533  * Sanity checks.
534  *
535  * XXX. It's actually not required that start_lsn == check_lsn. It
536  * would be OK if start_lsn > check_lsn provided that start_lsn is
537  * less than or equal to the relevant switchpoint. But at the moment
538  * we don't have that information.
539  */
540  if (i > 0 && previous_tli == 0)
541  pg_fatal("backup at \"%s\" is a full backup, but only the first backup should be a full backup",
542  backup_dirs[i]);
543  if (i == 0 && previous_tli != 0)
544  pg_fatal("backup at \"%s\" is an incremental backup, but the first backup should be a full backup",
545  backup_dirs[i]);
546  if (i < n_backups - 1 && start_tli != check_tli)
547  pg_fatal("backup at \"%s\" starts on timeline %u, but expected %u",
548  backup_dirs[i], start_tli, check_tli);
549  if (i < n_backups - 1 && start_lsn != check_lsn)
550  pg_fatal("backup at \"%s\" starts at LSN %X/%X, but expected %X/%X",
551  backup_dirs[i],
552  LSN_FORMAT_ARGS(start_lsn),
553  LSN_FORMAT_ARGS(check_lsn));
554  check_tli = previous_tli;
555  check_lsn = previous_lsn;
556 
557  /*
558  * The last backup label in the chain needs to be saved for later use,
559  * while the others are only needed within this loop.
560  */
561  if (lastbuf == buf)
562  buf = makeStringInfo();
563  else
565  }
566 
567  /* Free memory that we don't need any more. */
568  if (lastbuf != buf)
570 
571  /*
572  * Return the data from the first backup_info that we read (which is the
573  * backup_label from the last directory specified on the command line).
574  */
575  return lastbuf;
576 }
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 582 of file pg_combinebackup.c.

583 {
584  int i;
585  uint64 system_identifier = 0; /* placate compiler */
586 
587  /* Try to read each control file in turn, last to first. */
588  for (i = n_backups - 1; i >= 0; --i)
589  {
590  ControlFileData *control_file;
591  bool crc_ok;
592  char *controlpath;
593 
594  controlpath = psprintf("%s/%s", backup_dirs[i], "global/pg_control");
595  pg_log_debug("reading \"%s\"", controlpath);
596  control_file = get_controlfile_by_exact_path(controlpath, &crc_ok);
597 
598  /* Control file contents not meaningful if CRC is bad. */
599  if (!crc_ok)
600  pg_fatal("%s: CRC is incorrect", controlpath);
601 
602  /* Can't interpret control file if not current version. */
603  if (control_file->pg_control_version != PG_CONTROL_VERSION)
604  pg_fatal("%s: unexpected control file version",
605  controlpath);
606 
607  /* System identifiers should all match. */
608  if (i == n_backups - 1)
609  system_identifier = control_file->system_identifier;
610  else if (system_identifier != control_file->system_identifier)
611  pg_fatal("%s: expected system identifier %llu, but found %llu",
612  controlpath, (unsigned long long) system_identifier,
613  (unsigned long long) control_file->system_identifier);
614 
615  /* Release memory. */
616  pfree(control_file);
617  pfree(controlpath);
618  }
619 
620  /*
621  * If debug output is enabled, make a note of the system identifier that
622  * we found in all of the relevant control files.
623  */
624  pg_log_debug("system identifier is %llu",
625  (unsigned long long) system_identifier);
626 
627  return system_identifier;
628 }
ControlFileData * get_controlfile_by_exact_path(const char *ControlFilePath, bool *crc_ok_p)
void pfree(void *pointer)
Definition: mcxt.c:1520
#define PG_CONTROL_VERSION
Definition: pg_control.h:25
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
uint32 pg_control_version
Definition: pg_control.h:124
uint64 system_identifier
Definition: pg_control.h:109

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

Referenced by main().

◆ check_input_dir_permissions()

static void check_input_dir_permissions ( char *  dir)
static

Definition at line 637 of file pg_combinebackup.c.

638 {
639  struct stat st;
640 
641  if (stat(dir, &st) != 0)
642  pg_fatal("could not stat \"%s\": %m", dir);
643 
644  SetDataDirectoryCreatePerm(st.st_mode);
645 }
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 651 of file pg_combinebackup.c.

652 {
653  while (cleanup_dir_list != NULL)
654  {
656 
657  if (dir->rmtopdir)
658  {
659  pg_log_info("removing output directory \"%s\"", dir->target_path);
660  if (!rmtree(dir->target_path, dir->rmtopdir))
661  pg_log_error("failed to remove output directory");
662  }
663  else
664  {
665  pg_log_info("removing contents of output directory \"%s\"",
666  dir->target_path);
667  if (!rmtree(dir->target_path, dir->rmtopdir))
668  pg_log_error("failed to remove contents of output directory");
669  }
670 
672  pfree(dir);
673  }
674 }
#define pg_log_error(...)
Definition: logging.h:106
#define pg_log_info(...)
Definition: logging.h:124
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 684 of file pg_combinebackup.c.

685 {
686  switch (pg_check_dir(dirname))
687  {
688  case 0:
689  if (opt->dry_run)
690  {
691  pg_log_debug("would create directory \"%s\"", dirname);
692  return;
693  }
694  pg_log_debug("creating directory \"%s\"", dirname);
695  if (pg_mkdir_p(dirname, pg_dir_create_mode) == -1)
696  pg_fatal("could not create directory \"%s\": %m", dirname);
697  remember_to_cleanup_directory(dirname, true);
698  break;
699 
700  case 1:
701  pg_log_debug("using existing directory \"%s\"", dirname);
702  remember_to_cleanup_directory(dirname, false);
703  break;
704 
705  case 2:
706  case 3:
707  case 4:
708  pg_fatal("directory \"%s\" exists but is not empty", dirname);
709 
710  case -1:
711  pg_fatal("could not access directory \"%s\": %m", dirname);
712  }
713 }
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 723 of file pg_combinebackup.c.

724 {
725  printf(_("%s reconstructs full backups from incrementals.\n\n"), progname);
726  printf(_("Usage:\n"));
727  printf(_(" %s [OPTION]... DIRECTORY...\n"), progname);
728  printf(_("\nOptions:\n"));
729  printf(_(" -d, --debug generate lots of debugging output\n"));
730  printf(_(" -n, --dry-run don't actually do anything\n"));
731  printf(_(" -N, --no-sync do not wait for changes to be written safely to disk\n"));
732  printf(_(" -o, --output output directory\n"));
733  printf(_(" -T, --tablespace-mapping=OLDDIR=NEWDIR\n"
734  " relocate tablespace in OLDDIR to NEWDIR\n"));
735  printf(_(" --manifest-checksums=SHA{224,256,384,512}|CRC32C|NONE\n"
736  " use algorithm for manifest checksums\n"));
737  printf(_(" --no-manifest suppress generation of backup manifest\n"));
738  printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
739  printf(_(" --clone clone (reflink) instead of copying files\n"));
740  printf(_(" --copy-file-range copy using copy_file_range() syscall\n"));
741  printf(_(" -?, --help show this help, then exit\n"));
742 
743  printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
744  printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
745 }
#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 122 of file pg_combinebackup.c.

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

754 {
755  Oid oid;
756  char *ep;
757 
758  errno = 0;
759  oid = strtoul(s, &ep, 10);
760  if (errno != 0 || *ep != '\0' || oid < 1 || oid > PG_UINT32_MAX)
761  return false;
762 
763  *result = oid;
764  return true;
765 }
#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 787 of file pg_combinebackup.c.

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

1119 {
1120  char filename[MAXPGPATH];
1122  int fd;
1123  int version;
1124  char *ep;
1125 
1126  /* Construct pathname. */
1127  snprintf(filename, MAXPGPATH, "%s/PG_VERSION", directory);
1128 
1129  /* Open file. */
1130  if ((fd = open(filename, O_RDONLY, 0)) < 0)
1131  pg_fatal("could not open file \"%s\": %m", filename);
1132 
1133  /* Read into memory. Length limit of 128 should be more than generous. */
1134  initStringInfo(&buf);
1135  slurp_file(fd, filename, &buf, 128);
1136 
1137  /* Close the file. */
1138  if (close(fd) != 0)
1139  pg_fatal("could not close \"%s\": %m", filename);
1140 
1141  /* Convert to integer. */
1142  errno = 0;
1143  version = strtoul(buf.data, &ep, 10);
1144  if (errno != 0 || *ep != '\n')
1145  {
1146  /*
1147  * Incremental backup is not relevant to very old server versions that
1148  * used multi-part version number (e.g. 9.6, or 8.4). So if we see
1149  * what looks like the beginning of such a version number, just bail
1150  * out.
1151  */
1152  if (version < 10 && *ep == '.')
1153  pg_fatal("%s: server version too old\n", filename);
1154  pg_fatal("%s: could not parse version number\n", filename);
1155  }
1156 
1157  /* Debugging output. */
1158  pg_log_debug("read server version %d from \"%s\"", version, filename);
1159 
1160  /* Release memory and return result. */
1161  pfree(buf.data);
1162  return version * 10000;
1163 }
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 1169 of file pg_combinebackup.c.

1170 {
1171  cb_cleanup_dir *dir = pg_malloc(sizeof(cb_cleanup_dir));
1172 
1173  dir->target_path = target_path;
1174  dir->rmtopdir = rmtopdir;
1175  dir->next = cleanup_dir_list;
1176  cleanup_dir_list = dir;
1177 }

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 1190 of file pg_combinebackup.c.

1191 {
1192  while (cleanup_dir_list != NULL)
1193  {
1195 
1197  pfree(dir);
1198  }
1199 }

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 1209 of file pg_combinebackup.c.

1210 {
1211  char pg_tblspc[MAXPGPATH];
1212  DIR *dir;
1213  struct dirent *de;
1214  cb_tablespace *tslist = NULL;
1215 
1216  snprintf(pg_tblspc, MAXPGPATH, "%s/pg_tblspc", pathname);
1217  pg_log_debug("scanning \"%s\"", pg_tblspc);
1218 
1219  if ((dir = opendir(pg_tblspc)) == NULL)
1220  pg_fatal("could not open directory \"%s\": %m", pg_tblspc);
1221 
1222  while (errno = 0, (de = readdir(dir)) != NULL)
1223  {
1224  Oid oid;
1225  char tblspcdir[MAXPGPATH];
1226  char link_target[MAXPGPATH];
1227  int link_length;
1228  cb_tablespace *ts;
1229  cb_tablespace *otherts;
1230  PGFileType type;
1231 
1232  /* Silently ignore "." and ".." entries. */
1233  if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
1234  continue;
1235 
1236  /* Construct full pathname. */
1237  snprintf(tblspcdir, MAXPGPATH, "%s/%s", pg_tblspc, de->d_name);
1238 
1239  /* Ignore any file name that doesn't look like a proper OID. */
1240  if (!parse_oid(de->d_name, &oid))
1241  {
1242  pg_log_debug("skipping \"%s\" because the filename is not a legal tablespace OID",
1243  tblspcdir);
1244  continue;
1245  }
1246 
1247  /* Only symbolic links and directories are tablespaces. */
1248  type = get_dirent_type(tblspcdir, de, false, PG_LOG_ERROR);
1249  if (type == PGFILETYPE_ERROR)
1250  exit(1);
1251  if (type != PGFILETYPE_LNK && type != PGFILETYPE_DIR)
1252  {
1253  pg_log_debug("skipping \"%s\" because it is neither a symbolic link nor a directory",
1254  tblspcdir);
1255  continue;
1256  }
1257 
1258  /* Create a new tablespace object. */
1259  ts = pg_malloc0(sizeof(cb_tablespace));
1260  ts->oid = oid;
1261 
1262  /*
1263  * If it's a link, it's not an in-place tablespace. Otherwise, it must
1264  * be a directory, and thus an in-place tablespace.
1265  */
1266  if (type == PGFILETYPE_LNK)
1267  {
1268  cb_tablespace_mapping *tsmap;
1269 
1270  /* Read the link target. */
1271  link_length = readlink(tblspcdir, link_target, sizeof(link_target));
1272  if (link_length < 0)
1273  pg_fatal("could not read symbolic link \"%s\": %m",
1274  tblspcdir);
1275  if (link_length >= sizeof(link_target))
1276  pg_fatal("symbolic link \"%s\" is too long", tblspcdir);
1277  link_target[link_length] = '\0';
1278  if (!is_absolute_path(link_target))
1279  pg_fatal("symbolic link \"%s\" is relative", tblspcdir);
1280 
1281  /* Canonicalize the link target. */
1282  canonicalize_path(link_target);
1283 
1284  /*
1285  * Find the corresponding tablespace mapping and copy the relevant
1286  * details into the new tablespace entry.
1287  */
1288  for (tsmap = opt->tsmappings; tsmap != NULL; tsmap = tsmap->next)
1289  {
1290  if (strcmp(tsmap->old_dir, link_target) == 0)
1291  {
1292  strlcpy(ts->old_dir, tsmap->old_dir, MAXPGPATH);
1293  strlcpy(ts->new_dir, tsmap->new_dir, MAXPGPATH);
1294  ts->in_place = false;
1295  break;
1296  }
1297  }
1298 
1299  /* Every non-in-place tablespace must be mapped. */
1300  if (tsmap == NULL)
1301  pg_fatal("tablespace at \"%s\" has no tablespace mapping",
1302  link_target);
1303  }
1304  else
1305  {
1306  /*
1307  * For an in-place tablespace, there's no separate directory, so
1308  * we just record the paths within the data directories.
1309  */
1310  snprintf(ts->old_dir, MAXPGPATH, "%s/%s", pg_tblspc, de->d_name);
1311  snprintf(ts->new_dir, MAXPGPATH, "%s/pg_tblspc/%s", opt->output,
1312  de->d_name);
1313  ts->in_place = true;
1314  }
1315 
1316  /* Tablespaces should not share a directory. */
1317  for (otherts = tslist; otherts != NULL; otherts = otherts->next)
1318  if (strcmp(ts->new_dir, otherts->new_dir) == 0)
1319  pg_fatal("tablespaces with OIDs %u and %u both point at \"%s\"",
1320  otherts->oid, oid, ts->new_dir);
1321 
1322  /* Add this tablespace to the list. */
1323  ts->next = tslist;
1324  tslist = ts;
1325  }
1326 
1327  if (closedir(dir) != 0)
1328  pg_fatal("could not close directory \"%s\": %m", pg_tblspc);
1329 
1330  return tslist;
1331 }
#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 1340 of file pg_combinebackup.c.

1341 {
1342  struct stat st;
1343  ssize_t rb;
1344 
1345  /* Check file size, and complain if it's too large. */
1346  if (fstat(fd, &st) != 0)
1347  pg_fatal("could not stat \"%s\": %m", filename);
1348  if (st.st_size > maxlen)
1349  pg_fatal("file \"%s\" is too large", filename);
1350 
1351  /* Make sure we have enough space. */
1352  enlargeStringInfo(buf, st.st_size);
1353 
1354  /* Read the data. */
1355  rb = read(fd, &buf->data[buf->len], st.st_size);
1356 
1357  /*
1358  * We don't expect any concurrent changes, so we should read exactly the
1359  * expected number of bytes.
1360  */
1361  if (rb != st.st_size)
1362  {
1363  if (rb < 0)
1364  pg_fatal("could not read file \"%s\": %m", filename);
1365  else
1366  pg_fatal("could not read file \"%s\": read only %zd of %lld bytes",
1367  filename, rb, (long long int) st.st_size);
1368  }
1369 
1370  /* Adjust buffer length for new data and restore trailing-\0 invariant */
1371  buf->len += rb;
1372  buf->data[buf->len] = '\0';
1373 }
#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