42#define ESTIMATED_BYTES_PER_MANIFEST_LINE 100
47#define READ_CHUNK_SIZE (128 * 1024)
61 int manifest_version);
63 uint64 manifest_system_identifier);
65 const char *pathname,
uint64 size,
68 uint8 *checksum_payload);
84 uint64 manifest_system_identifier);
95 char *pg_waldump_path,
103static
void usage(
void);
120 static struct option long_options[] = {
135 char *manifest_path = NULL;
136 bool no_parse_wal =
false;
138 char *wal_directory = NULL;
139 char *pg_waldump_path = NULL;
146 memset(&context, 0,
sizeof(context));
150 if (strcmp(argv[1],
"--help") == 0 || strcmp(argv[1],
"-?") == 0)
155 if (strcmp(argv[1],
"--version") == 0 || strcmp(argv[1],
"-V") == 0)
157 puts(
"pg_verifybackup (PostgreSQL) " PG_VERSION);
183 while ((
c =
getopt_long(argc, argv,
"eF:i:m:nPqsw:", long_options, NULL)) != -1)
188 context.exit_on_error =
true;
203 if (strcmp(
optarg,
"p") == 0 || strcmp(
optarg,
"plain") == 0)
204 context.format =
'p';
205 else if (strcmp(
optarg,
"t") == 0 || strcmp(
optarg,
"tar") == 0)
206 context.format =
't';
208 pg_fatal(
"invalid backup format \"%s\", must be \"plain\" or \"tar\"",
221 context.skip_checksums =
true;
247 pg_log_error(
"too many command-line arguments (first is \"%s\")",
255 pg_fatal(
"cannot specify both %s and %s",
256 "-P/--progress",
"-q/--quiet");
265 "pg_waldump (PostgreSQL) " PG_VERSION
"\n",
275 pg_fatal(
"program \"%s\" is needed by %s but was not found in the same directory as \"%s\"",
276 "pg_waldump",
"pg_verifybackup", full_path);
278 pg_fatal(
"program \"%s\" was found by \"%s\" but was not the same version as %s",
279 "pg_waldump", full_path,
"pg_verifybackup");
284 if (manifest_path == NULL)
285 manifest_path =
psprintf(
"%s/backup_manifest",
286 context.backup_directory);
289 if (wal_directory == NULL)
290 wal_directory =
psprintf(
"%s/pg_wal", context.backup_directory);
302 dir =
opendir(context.backup_directory);
305 context.backup_directory);
313 if (context.format ==
'\0')
318 path =
psprintf(
"%s/%s", context.backup_directory,
"PG_VERSION");
319 if (
stat(path, &sb) == 0)
320 context.format =
'p';
321 else if (errno != ENOENT)
329 context.format =
't';
338 if (!no_parse_wal && context.format ==
't')
341 pg_log_error_hint(
"You must use -n or --no-parse-wal when verifying a tar-format backup.");
349 if (context.format ==
'p')
368 if (context.format ==
'p' && !context.skip_checksums)
382 if (!context.saw_any_error && !quiet)
383 printf(
_(
"backup successfully verified\n"));
385 return context.saw_any_error ? 1 : 0;
398 manifest_files_hash *ht;
407 if ((
fd = open(manifest_path, O_RDONLY |
PG_BINARY, 0)) < 0)
419 ht = manifest_files_create(initial_size, NULL);
433 if (statbuf.
st_size <= chunk_size)
440 pg_fatal(
"could not read file \"%s\": %m", manifest_path);
442 pg_fatal(
"could not read file \"%s\": read %d of %lld",
443 manifest_path, rc, (
long long int) statbuf.
st_size);
454 int bytes_left = statbuf.
st_size;
461 while (bytes_left > 0)
463 int bytes_to_read = chunk_size;
470 if (bytes_left < chunk_size)
471 bytes_to_read = bytes_left;
472 else if (bytes_left < 2 * chunk_size)
473 bytes_to_read = bytes_left / 2;
474 rc =
read(
fd, buffer, bytes_to_read);
475 if (rc != bytes_to_read)
478 pg_fatal(
"could not read file \"%s\": %m", manifest_path);
480 pg_fatal(
"could not read file \"%s\": read %lld of %lld",
482 (
long long int) (statbuf.
st_size + rc - bytes_left),
483 (
long long int) statbuf.
st_size);
525 int manifest_version)
530 manifest->version = manifest_version;
538 uint64 manifest_system_identifier)
543 manifest->system_identifier = manifest_system_identifier;
551 const char *pathname,
uint64 size,
553 int checksum_length,
uint8 *checksum_payload)
556 manifest_files_hash *ht =
manifest->files;
561 m = manifest_files_insert(ht, pathname, &found);
589 range->start_lsn = start_lsn;
590 range->end_lsn = end_lsn;
595 if (
manifest->first_wal_range == NULL)
617 char *fullpath,
DIR *dir)
622 if (dir == NULL && ((dir =
opendir(fullpath)) == NULL))
625 "could not open directory \"%s\": %m", fullpath);
657 "could not close directory \"%s\": %m", fullpath);
675 if (
stat(fullpath, &sb) != 0)
678 "could not stat file or directory \"%s\": %m",
701 "\"%s\" is not a file or directory",
711 "\"%s\" is present on disk but not in the manifest",
723 "\"%s\" has size %llu on disk but size %llu in the manifest",
725 (
unsigned long long) m->size);
774 report_fatal_error(
"%s: manifest system identifier is %" PRIu64
", but control file has %" PRIu64,
776 manifest_system_identifier,
827 "could not close directory \"%s\": %m",
833 for (cell = tarfiles.
head; cell != NULL; cell = cell->
next)
894 if (
stat(fullpath, &sb) != 0)
897 "could not stat file or directory \"%s\": %m",
906 "\"%s\" is not a plain file",
920 if (strncmp(
"base",
relpath, 4) == 0)
922 else if (strncmp(
"pg_wal",
relpath, 6) == 0)
933 if (suffix == NULL || num <= 0 || num >
OID_MAX)
936 "file \"%s\" is not expected in a tar format backup",
940 tblspc_oid = (
Oid) num;
944 if (strcmp(suffix,
".tar") == 0)
946 else if (strcmp(suffix,
".tgz") == 0)
948 else if (strcmp(suffix,
".tar.gz") == 0)
950 else if (strcmp(suffix,
".tar.lz4") == 0)
952 else if (strcmp(suffix,
".tar.zst") == 0)
957 "file \"%s\" is not expected in a tar format backup",
966 if (strncmp(
"pg_wal",
relpath, 6) == 0)
1003 if ((
fd = open(fullpath, O_RDONLY |
PG_BINARY, 0)) < 0)
1042 manifest_files_iterator it;
1045 manifest_files_start_iterate(
manifest->files, &it);
1046 while ((m = manifest_files_iterate(
manifest->files, &it)) != NULL)
1049 "\"%s\" is present in the manifest but not on disk",
1062 manifest_files_iterator it;
1070 manifest_files_start_iterate(
manifest->files, &it);
1071 while ((m = manifest_files_iterate(
manifest->files, &it)) != NULL)
1100 char *fullpath,
uint8 *buffer)
1111 if ((
fd = open(fullpath, O_RDONLY |
PG_BINARY, 0)) < 0)
1166 if (bytes_read != m->
size)
1169 "file \"%s\" should contain %" PRIu64
" bytes, but read %" PRIu64,
1176 if (checksumlen < 0)
1179 "could not finalize checksum of file \"%s\"",
1187 "file \"%s\" has checksum of length %d, but expected %d",
1191 "checksum mismatch for file \"%s\"",
1201 char *wal_directory)
1206 while (this_wal_range != NULL)
1208 char *pg_waldump_cmd;
1210 pg_waldump_cmd =
psprintf(
"\"%s\" --quiet --path=\"%s\" --timeline=%u --start=%X/%X --end=%X/%X\n",
1211 pg_waldump_path, wal_directory, this_wal_range->
tli,
1215 if (system(pg_waldump_cmd) != 0)
1217 "WAL parsing failed for timeline %u",
1218 this_wal_range->
tli);
1220 this_wal_range = this_wal_range->
next;
1274 char *v = cell->
val;
1276 while (*v !=
'\0' && *r == *v)
1279 if (*v ==
'\0' && (*r ==
'\0' || *r ==
'/'))
1331 int percent_size = 0;
1332 char totalsize_str[32];
1333 char donesize_str[32];
1351 _(
"%*s/%s kB (%d%%) verified"),
1352 (
int) strlen(totalsize_str),
1353 donesize_str, totalsize_str, percent_size);
1359 fputc((!finished && isatty(fileno(stderr))) ?
'\r' :
'\n', stderr);
1368 printf(
_(
"%s verifies a backup against the backup manifest.\n\n"),
progname);
1371 printf(
_(
" -e, --exit-on-error exit immediately on error\n"));
1372 printf(
_(
" -F, --format=p|t backup format (plain, tar)\n"));
1373 printf(
_(
" -i, --ignore=RELATIVE_PATH ignore indicated path\n"));
1374 printf(
_(
" -m, --manifest-path=PATH use specified path for manifest\n"));
1375 printf(
_(
" -n, --no-parse-wal do not try to parse WAL files\n"));
1376 printf(
_(
" -P, --progress show progress information\n"));
1377 printf(
_(
" -q, --quiet do not print any output, except for errors\n"));
1378 printf(
_(
" -s, --skip-checksums skip checksum verification\n"));
1379 printf(
_(
" -w, --wal-directory=PATH use specified path for WAL files\n"));
1380 printf(
_(
" -V, --version output version information, then exit\n"));
1381 printf(
_(
" -?, --help show this help, then exit\n"));
1382 printf(
_(
"\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1383 printf(
_(
"%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
static void astreamer_free(astreamer *streamer)
static void astreamer_content(astreamer *streamer, astreamer_member *member, const char *data, int len, astreamer_archive_context context)
static void astreamer_finalize(astreamer *streamer)
astreamer * astreamer_gzip_decompressor_new(astreamer *next)
astreamer * astreamer_lz4_decompressor_new(astreamer *next)
astreamer * astreamer_tar_parser_new(astreamer *next)
astreamer * astreamer_verify_content_new(astreamer *next, verifier_context *context, char *archive_name, Oid tblspc_oid)
astreamer * astreamer_zstd_decompressor_new(astreamer *next)
Datum now(PG_FUNCTION_ARGS)
#define PG_TEXTDOMAIN(domain)
#define pg_attribute_printf(f, a)
int pg_checksum_final(pg_checksum_context *context, uint8 *output)
int pg_checksum_update(pg_checksum_context *context, const uint8 *input, size_t len)
int pg_checksum_init(pg_checksum_context *context, pg_checksum_type type)
#define PG_CHECKSUM_MAX_LENGTH
int find_my_exec(const char *argv0, char *retpath)
void set_pglocale_pgservice(const char *argv0, const char *app)
int find_other_exec(const char *argv0, const char *target, const char *versionstr, char *retpath)
ControlFileData * get_controlfile_by_exact_path(const char *ControlFilePath, bool *crc_ok_p)
#define fprintf(file, fmt, msg)
struct dirent * readdir(DIR *)
DIR * opendir(const char *)
void * pg_malloc(size_t size)
void * pg_malloc0(size_t size)
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
#define required_argument
Assert(PointerIsAligned(start, uint64))
void pg_logging_init(const char *argv0)
void pg_log_generic_v(enum pg_log_level level, enum pg_log_part part, const char *pg_restrict fmt, va_list ap)
#define pg_log_error(...)
#define pg_log_error_hint(...)
#define pg_log_debug(...)
char * pstrdup(const char *in)
void pfree(void *pointer)
void json_parse_manifest(JsonManifestParseContext *context, const char *buffer, size_t size)
JsonManifestParseIncrementalState * json_parse_manifest_incremental_init(JsonManifestParseContext *context)
void json_parse_manifest_incremental_shutdown(JsonManifestParseIncrementalState *incstate)
void json_parse_manifest_incremental_chunk(JsonManifestParseIncrementalState *incstate, const char *chunk, size_t size, bool is_last)
static pg_time_t last_progress_report
#define PG_CONTROL_VERSION
PGDLLIMPORT char * optarg
static void verify_backup_checksums(verifier_context *context)
static manifest_data * parse_manifest_file(char *manifest_path)
static void verify_tar_file(verifier_context *context, char *relpath, char *fullpath, astreamer *streamer)
void report_fatal_error(const char *pg_restrict fmt,...)
static void verifybackup_version_cb(JsonManifestParseContext *context, int manifest_version)
static void verifybackup_system_identifier(JsonManifestParseContext *context, uint64 manifest_system_identifier)
bool should_ignore_relpath(verifier_context *context, const char *relpath)
int main(int argc, char **argv)
static void verifybackup_per_wal_range_cb(JsonManifestParseContext *context, TimeLineID tli, XLogRecPtr start_lsn, XLogRecPtr end_lsn)
static void verify_plain_backup_directory(verifier_context *context, char *relpath, char *fullpath, DIR *dir)
static void report_extra_backup_files(verifier_context *context)
#define ESTIMATED_BYTES_PER_MANIFEST_LINE
static void progress_report(bool finished)
static astreamer * create_archive_verifier(verifier_context *context, char *archive_name, Oid tblspc_oid, pg_compress_algorithm compress_algo)
static bool show_progress
static pg_noreturn void report_manifest_error(JsonManifestParseContext *context, const char *fmt,...) pg_attribute_printf(2
static void parse_required_wal(verifier_context *context, char *pg_waldump_path, char *wal_directory)
void report_backup_error(verifier_context *context, const char *pg_restrict fmt,...)
static void precheck_tar_backup_file(verifier_context *context, char *relpath, char *fullpath, SimplePtrList *tarfiles)
static const char * progname
static pg_noreturn void static void verify_tar_backup(verifier_context *context, DIR *dir)
static void verify_plain_backup_file(verifier_context *context, char *relpath, char *fullpath)
static void verify_file_checksum(verifier_context *context, manifest_file *m, char *fullpath, uint8 *buffer)
static void verify_control_file(const char *controlpath, uint64 manifest_system_identifier)
static void verifybackup_per_file_cb(JsonManifestParseContext *context, const char *pathname, uint64 size, pg_checksum_type checksum_type, int checksum_length, uint8 *checksum_payload)
#define should_verify_checksum(m)
void canonicalize_path(char *path)
const char * get_progname(const char *argv0)
size_t strlcpy(char *dst, const char *src, size_t siz)
static int fd(const char *x, int i)
char * psprintf(const char *fmt,...)
static struct cvec * range(struct vars *v, chr a, chr b, int cases)
#define relpath(rlocator, forknum)
void simple_ptr_list_destroy(SimplePtrList *list)
void simple_string_list_append(SimpleStringList *list, const char *val)
void simple_ptr_list_append(SimplePtrList *list, void *ptr)
uint32 pg_control_version
json_manifest_per_wal_range_callback per_wal_range_cb
json_manifest_system_identifier_callback system_identifier_cb
json_manifest_error_callback error_cb
json_manifest_per_file_callback per_file_cb
json_manifest_version_callback version_cb
struct SimplePtrListCell * next
char val[FLEXIBLE_ARRAY_MEMBER]
struct SimpleStringListCell * next
SimpleStringListCell * head
manifest_files_hash * files
pg_checksum_type checksum_type
struct manifest_wal_range * next
pg_compress_algorithm compress_algorithm
SimpleStringList ignore_list
#define XLOG_CONTROL_FILE
#define LSN_FORMAT_ARGS(lsn)