PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
pg_combinebackup.c File Reference
#include "postgres_fe.h"
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include "backup_label.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 "common/relpath.h"
#include "copy_file.h"
#include "fe_utils/option_utils.h"
#include "getopt_long.h"
#include "lib/stringinfo.h"
#include "load_manifest.h"
#include "reconstruct.h"
#include "write_manifest.h"
Include dependency graph for pg_combinebackup.c:

Go to the source code of this file.

Data Structures

struct  cb_cleanup_dir
 
struct  cb_tablespace_mapping
 
struct  cb_options
 
struct  cb_tablespace
 

Macros

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

Typedefs

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

Functions

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

Variables

static cb_cleanup_dircleanup_dir_list = NULL
 

Macro Definition Documentation

◆ INCREMENTAL_PREFIX

#define INCREMENTAL_PREFIX   "INCREMENTAL."

Definition at line 43 of file pg_combinebackup.c.

◆ INCREMENTAL_PREFIX_LENGTH

#define INCREMENTAL_PREFIX_LENGTH   (sizeof(INCREMENTAL_PREFIX) - 1)

Definition at line 44 of file pg_combinebackup.c.

Typedef Documentation

◆ cb_cleanup_dir

◆ cb_options

typedef struct cb_options cb_options

◆ cb_tablespace

typedef struct cb_tablespace cb_tablespace

◆ cb_tablespace_mapping

Function Documentation

◆ add_tablespace_mapping()

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

Definition at line 436 of file pg_combinebackup.c.

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

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

Referenced by main().

◆ check_backup_label_files()

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

Definition at line 501 of file pg_combinebackup.c.

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

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

Referenced by main().

◆ check_control_files()

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

Definition at line 594 of file pg_combinebackup.c.

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

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

Referenced by main().

◆ check_input_dir_permissions()

static void check_input_dir_permissions ( char *  dir)
static

Definition at line 671 of file pg_combinebackup.c.

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

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

Referenced by main().

◆ cleanup_directories_atexit()

static void cleanup_directories_atexit ( void  )
static

Definition at line 685 of file pg_combinebackup.c.

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

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

Referenced by main().

◆ create_output_directory()

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

Definition at line 718 of file pg_combinebackup.c.

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

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

Referenced by main().

◆ help()

static void help ( const char *  progname)
static

Definition at line 757 of file pg_combinebackup.c.

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

References _, printf, and progname.

Referenced by main().

◆ main()

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

Definition at line 130 of file pg_combinebackup.c.

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

◆ parse_oid()

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

Definition at line 789 of file pg_combinebackup.c.

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

References PG_UINT32_MAX.

Referenced by process_directory_recursively(), and scan_for_existing_tablespaces().

◆ process_directory_recursively()

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

Definition at line 823 of file pg_combinebackup.c.

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

Referenced by main(), and process_directory_recursively().

◆ read_pg_version_file()

static int read_pg_version_file ( char *  directory)
static

Definition at line 1154 of file pg_combinebackup.c.

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

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

Referenced by main().

◆ remember_to_cleanup_directory()

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

Definition at line 1205 of file pg_combinebackup.c.

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

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

Referenced by create_output_directory().

◆ reset_directory_cleanup_list()

static void reset_directory_cleanup_list ( void  )
static

Definition at line 1226 of file pg_combinebackup.c.

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

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

Referenced by main().

◆ scan_for_existing_tablespaces()

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

Definition at line 1245 of file pg_combinebackup.c.

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

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

Referenced by main().

◆ slurp_file()

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

Definition at line 1376 of file pg_combinebackup.c.

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

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

Referenced by check_backup_label_files(), and read_pg_version_file().

Variable Documentation

◆ cleanup_dir_list

cb_cleanup_dir* cleanup_dir_list = NULL
static