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  uint32 data_checksum_version = 0; /* placate compiler */
587  bool data_checksum_mismatch = false;
588 
589  /* Try to read each control file in turn, last to first. */
590  for (i = n_backups - 1; i >= 0; --i)
591  {
592  ControlFileData *control_file;
593  bool crc_ok;
594  char *controlpath;
595 
596  controlpath = psprintf("%s/%s", backup_dirs[i], "global/pg_control");
597  pg_log_debug("reading \"%s\"", controlpath);
598  control_file = get_controlfile_by_exact_path(controlpath, &crc_ok);
599 
600  /* Control file contents not meaningful if CRC is bad. */
601  if (!crc_ok)
602  pg_fatal("%s: CRC is incorrect", controlpath);
603 
604  /* Can't interpret control file if not current version. */
605  if (control_file->pg_control_version != PG_CONTROL_VERSION)
606  pg_fatal("%s: unexpected control file version",
607  controlpath);
608 
609  /* System identifiers should all match. */
610  if (i == n_backups - 1)
611  system_identifier = control_file->system_identifier;
612  else if (system_identifier != control_file->system_identifier)
613  pg_fatal("%s: expected system identifier %llu, but found %llu",
614  controlpath, (unsigned long long) system_identifier,
615  (unsigned long long) control_file->system_identifier);
616 
617  /*
618  * Detect checksum mismatches, but only if the last backup in the
619  * chain has checksums enabled.
620  */
621  if (i == n_backups - 1)
622  data_checksum_version = control_file->data_checksum_version;
623  else if (data_checksum_version != 0 &&
624  data_checksum_version != control_file->data_checksum_version)
625  data_checksum_mismatch = true;
626 
627  /* Release memory. */
628  pfree(control_file);
629  pfree(controlpath);
630  }
631 
632  /*
633  * If debug output is enabled, make a note of the system identifier that
634  * we found in all of the relevant control files.
635  */
636  pg_log_debug("system identifier is %llu",
637  (unsigned long long) system_identifier);
638 
639  /*
640  * Warn the user if not all backups are in the same state with regards to
641  * checksums.
642  */
643  if (data_checksum_mismatch)
644  {
645  pg_log_warning("only some backups have checksums enabled");
646  pg_log_warning_hint("disable, and optionally reenable, checksums on the output directory to avoid failures");
647  }
648 
649  return system_identifier;
650 }
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:1520
#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:124
uint32 data_checksum_version
Definition: pg_control.h:221
uint64 system_identifier
Definition: pg_control.h:109

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

660 {
661  struct stat st;
662 
663  if (stat(dir, &st) != 0)
664  pg_fatal("could not stat \"%s\": %m", dir);
665 
666  SetDataDirectoryCreatePerm(st.st_mode);
667 }
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 673 of file pg_combinebackup.c.

674 {
675  while (cleanup_dir_list != NULL)
676  {
678 
679  if (dir->rmtopdir)
680  {
681  pg_log_info("removing output directory \"%s\"", dir->target_path);
682  if (!rmtree(dir->target_path, dir->rmtopdir))
683  pg_log_error("failed to remove output directory");
684  }
685  else
686  {
687  pg_log_info("removing contents of output directory \"%s\"",
688  dir->target_path);
689  if (!rmtree(dir->target_path, dir->rmtopdir))
690  pg_log_error("failed to remove contents of output directory");
691  }
692 
694  pfree(dir);
695  }
696 }
#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 706 of file pg_combinebackup.c.

707 {
708  switch (pg_check_dir(dirname))
709  {
710  case 0:
711  if (opt->dry_run)
712  {
713  pg_log_debug("would create directory \"%s\"", dirname);
714  return;
715  }
716  pg_log_debug("creating directory \"%s\"", dirname);
717  if (pg_mkdir_p(dirname, pg_dir_create_mode) == -1)
718  pg_fatal("could not create directory \"%s\": %m", dirname);
719  remember_to_cleanup_directory(dirname, true);
720  break;
721 
722  case 1:
723  pg_log_debug("using existing directory \"%s\"", dirname);
724  remember_to_cleanup_directory(dirname, false);
725  break;
726 
727  case 2:
728  case 3:
729  case 4:
730  pg_fatal("directory \"%s\" exists but is not empty", dirname);
731 
732  case -1:
733  pg_fatal("could not access directory \"%s\": %m", dirname);
734  }
735 }
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 745 of file pg_combinebackup.c.

746 {
747  printf(_("%s reconstructs full backups from incrementals.\n\n"), progname);
748  printf(_("Usage:\n"));
749  printf(_(" %s [OPTION]... DIRECTORY...\n"), progname);
750  printf(_("\nOptions:\n"));
751  printf(_(" -d, --debug generate lots of debugging output\n"));
752  printf(_(" -n, --dry-run do not actually do anything\n"));
753  printf(_(" -N, --no-sync do not wait for changes to be written safely to disk\n"));
754  printf(_(" -o, --output output directory\n"));
755  printf(_(" -T, --tablespace-mapping=OLDDIR=NEWDIR\n"
756  " relocate tablespace in OLDDIR to NEWDIR\n"));
757  printf(_(" --clone clone (reflink) instead of copying files\n"));
758  printf(_(" --copy-file-range copy using copy_file_range() syscall\n"));
759  printf(_(" --manifest-checksums=SHA{224,256,384,512}|CRC32C|NONE\n"
760  " use algorithm for manifest checksums\n"));
761  printf(_(" --no-manifest suppress generation of backup manifest\n"));
762  printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
763  printf(_(" -V, --version output version information, then exit\n"));
764  printf(_(" -?, --help show this help, then exit\n"));
765 
766  printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
767  printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
768 }
#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", required_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, "dnNo: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 776 of file pg_combinebackup.c.

777 {
778  Oid oid;
779  char *ep;
780 
781  errno = 0;
782  oid = strtoul(s, &ep, 10);
783  if (errno != 0 || *ep != '\0' || oid < 1 || oid > PG_UINT32_MAX)
784  return false;
785 
786  *result = oid;
787  return true;
788 }
#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 810 of file pg_combinebackup.c.

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

1142 {
1143  char filename[MAXPGPATH];
1145  int fd;
1146  int version;
1147  char *ep;
1148 
1149  /* Construct pathname. */
1150  snprintf(filename, MAXPGPATH, "%s/PG_VERSION", directory);
1151 
1152  /* Open file. */
1153  if ((fd = open(filename, O_RDONLY, 0)) < 0)
1154  pg_fatal("could not open file \"%s\": %m", filename);
1155 
1156  /* Read into memory. Length limit of 128 should be more than generous. */
1157  initStringInfo(&buf);
1158  slurp_file(fd, filename, &buf, 128);
1159 
1160  /* Close the file. */
1161  if (close(fd) != 0)
1162  pg_fatal("could not close \"%s\": %m", filename);
1163 
1164  /* Convert to integer. */
1165  errno = 0;
1166  version = strtoul(buf.data, &ep, 10);
1167  if (errno != 0 || *ep != '\n')
1168  {
1169  /*
1170  * Incremental backup is not relevant to very old server versions that
1171  * used multi-part version number (e.g. 9.6, or 8.4). So if we see
1172  * what looks like the beginning of such a version number, just bail
1173  * out.
1174  */
1175  if (version < 10 && *ep == '.')
1176  pg_fatal("%s: server version too old\n", filename);
1177  pg_fatal("%s: could not parse version number\n", filename);
1178  }
1179 
1180  /* Debugging output. */
1181  pg_log_debug("read server version %d from \"%s\"", version, filename);
1182 
1183  /* Release memory and return result. */
1184  pfree(buf.data);
1185  return version * 10000;
1186 }
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 1192 of file pg_combinebackup.c.

1193 {
1194  cb_cleanup_dir *dir = pg_malloc(sizeof(cb_cleanup_dir));
1195 
1196  dir->target_path = target_path;
1197  dir->rmtopdir = rmtopdir;
1198  dir->next = cleanup_dir_list;
1199  cleanup_dir_list = dir;
1200 }

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

1214 {
1215  while (cleanup_dir_list != NULL)
1216  {
1218 
1220  pfree(dir);
1221  }
1222 }

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

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

1364 {
1365  struct stat st;
1366  ssize_t rb;
1367 
1368  /* Check file size, and complain if it's too large. */
1369  if (fstat(fd, &st) != 0)
1370  pg_fatal("could not stat \"%s\": %m", filename);
1371  if (st.st_size > maxlen)
1372  pg_fatal("file \"%s\" is too large", filename);
1373 
1374  /* Make sure we have enough space. */
1375  enlargeStringInfo(buf, st.st_size);
1376 
1377  /* Read the data. */
1378  rb = read(fd, &buf->data[buf->len], st.st_size);
1379 
1380  /*
1381  * We don't expect any concurrent changes, so we should read exactly the
1382  * expected number of bytes.
1383  */
1384  if (rb != st.st_size)
1385  {
1386  if (rb < 0)
1387  pg_fatal("could not read file \"%s\": %m", filename);
1388  else
1389  pg_fatal("could not read file \"%s\": read only %zd of %lld bytes",
1390  filename, rb, (long long int) st.st_size);
1391  }
1392 
1393  /* Adjust buffer length for new data and restore trailing-\0 invariant */
1394  buf->len += rb;
1395  buf->data[buf->len] = '\0';
1396 }
#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