summaryrefslogtreecommitdiffstats
path: root/src/bin/pg_verifybackup
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
commit46651ce6fe013220ed397add242004d764fc0153 (patch)
tree6e5299f990f88e60174a1d3ae6e48eedd2688b2b /src/bin/pg_verifybackup
parentInitial commit. (diff)
downloadpostgresql-14-46651ce6fe013220ed397add242004d764fc0153.tar.xz
postgresql-14-46651ce6fe013220ed397add242004d764fc0153.zip
Adding upstream version 14.5.upstream/14.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/bin/pg_verifybackup')
-rw-r--r--src/bin/pg_verifybackup/.gitignore2
-rw-r--r--src/bin/pg_verifybackup/Makefile40
-rw-r--r--src/bin/pg_verifybackup/nls.mk17
-rw-r--r--src/bin/pg_verifybackup/parse_manifest.c748
-rw-r--r--src/bin/pg_verifybackup/parse_manifest.h46
-rw-r--r--src/bin/pg_verifybackup/pg_verifybackup.c924
-rw-r--r--src/bin/pg_verifybackup/po/de.po501
-rw-r--r--src/bin/pg_verifybackup/po/el.po505
-rw-r--r--src/bin/pg_verifybackup/po/es.po505
-rw-r--r--src/bin/pg_verifybackup/po/fr.po508
-rw-r--r--src/bin/pg_verifybackup/po/ja.po503
-rw-r--r--src/bin/pg_verifybackup/po/ko.po467
-rw-r--r--src/bin/pg_verifybackup/po/ru.po514
-rw-r--r--src/bin/pg_verifybackup/po/sv.po503
-rw-r--r--src/bin/pg_verifybackup/po/uk.po488
-rw-r--r--src/bin/pg_verifybackup/po/zh_CN.po494
-rw-r--r--src/bin/pg_verifybackup/t/001_basic.pl36
-rw-r--r--src/bin/pg_verifybackup/t/002_algorithm.pl61
-rw-r--r--src/bin/pg_verifybackup/t/003_corruption.pl292
-rw-r--r--src/bin/pg_verifybackup/t/004_options.pl107
-rw-r--r--src/bin/pg_verifybackup/t/005_bad_manifest.pl211
-rw-r--r--src/bin/pg_verifybackup/t/006_encoding.pl34
-rw-r--r--src/bin/pg_verifybackup/t/007_wal.pl78
23 files changed, 7584 insertions, 0 deletions
diff --git a/src/bin/pg_verifybackup/.gitignore b/src/bin/pg_verifybackup/.gitignore
new file mode 100644
index 0000000..910b227
--- /dev/null
+++ b/src/bin/pg_verifybackup/.gitignore
@@ -0,0 +1,2 @@
+/pg_verifybackup
+/tmp_check/
diff --git a/src/bin/pg_verifybackup/Makefile b/src/bin/pg_verifybackup/Makefile
new file mode 100644
index 0000000..c07643b
--- /dev/null
+++ b/src/bin/pg_verifybackup/Makefile
@@ -0,0 +1,40 @@
+# src/bin/pg_verifybackup/Makefile
+
+PGFILEDESC = "pg_verifybackup - verify a backup against using a backup manifest"
+PGAPPICON = win32
+
+subdir = src/bin/pg_verifybackup
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+# We need libpq only because fe_utils does.
+LDFLAGS_INTERNAL += -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport)
+
+OBJS = \
+ $(WIN32RES) \
+ parse_manifest.o \
+ pg_verifybackup.o
+
+all: pg_verifybackup
+
+pg_verifybackup: $(OBJS) | submake-libpq submake-libpgport submake-libpgfeutils
+ $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
+
+install: all installdirs
+ $(INSTALL_PROGRAM) pg_verifybackup$(X) '$(DESTDIR)$(bindir)/pg_verifybackup$(X)'
+
+installdirs:
+ $(MKDIR_P) '$(DESTDIR)$(bindir)'
+
+uninstall:
+ rm -f '$(DESTDIR)$(bindir)/pg_verifybackup$(X)'
+
+clean distclean maintainer-clean:
+ rm -f pg_verifybackup$(X) $(OBJS)
+ rm -rf tmp_check
+
+check:
+ $(prove_check)
+
+installcheck:
+ $(prove_installcheck)
diff --git a/src/bin/pg_verifybackup/nls.mk b/src/bin/pg_verifybackup/nls.mk
new file mode 100644
index 0000000..81b9635
--- /dev/null
+++ b/src/bin/pg_verifybackup/nls.mk
@@ -0,0 +1,17 @@
+# src/bin/pg_verifybackup/nls.mk
+CATALOG_NAME = pg_verifybackup
+AVAIL_LANGUAGES = de el es fr ja ko ru sv uk zh_CN
+GETTEXT_FILES = $(FRONTEND_COMMON_GETTEXT_FILES) \
+ parse_manifest.c \
+ pg_verifybackup.c \
+ ../../common/fe_memutils.c \
+ ../../common/jsonapi.c
+GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) \
+ json_manifest_parse_failure:2 \
+ error_cb:2 \
+ report_backup_error:2 \
+ report_fatal_error
+GETTEXT_FLAGS = $(FRONTEND_COMMON_GETTEXT_FLAGS) \
+ error_cb:2:c-format \
+ report_backup_error:2:c-format \
+ report_fatal_error:1:c-format
diff --git a/src/bin/pg_verifybackup/parse_manifest.c b/src/bin/pg_verifybackup/parse_manifest.c
new file mode 100644
index 0000000..3b13ae5
--- /dev/null
+++ b/src/bin/pg_verifybackup/parse_manifest.c
@@ -0,0 +1,748 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_manifest.c
+ * Parse a backup manifest in JSON format.
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/bin/pg_verifybackup/parse_manifest.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include "parse_manifest.h"
+#include "common/jsonapi.h"
+
+/*
+ * Semantic states for JSON manifest parsing.
+ */
+typedef enum
+{
+ JM_EXPECT_TOPLEVEL_START,
+ JM_EXPECT_TOPLEVEL_END,
+ JM_EXPECT_TOPLEVEL_FIELD,
+ JM_EXPECT_VERSION_VALUE,
+ JM_EXPECT_FILES_START,
+ JM_EXPECT_FILES_NEXT,
+ JM_EXPECT_THIS_FILE_FIELD,
+ JM_EXPECT_THIS_FILE_VALUE,
+ JM_EXPECT_WAL_RANGES_START,
+ JM_EXPECT_WAL_RANGES_NEXT,
+ JM_EXPECT_THIS_WAL_RANGE_FIELD,
+ JM_EXPECT_THIS_WAL_RANGE_VALUE,
+ JM_EXPECT_MANIFEST_CHECKSUM_VALUE,
+ JM_EXPECT_EOF
+} JsonManifestSemanticState;
+
+/*
+ * Possible fields for one file as described by the manifest.
+ */
+typedef enum
+{
+ JMFF_PATH,
+ JMFF_ENCODED_PATH,
+ JMFF_SIZE,
+ JMFF_LAST_MODIFIED,
+ JMFF_CHECKSUM_ALGORITHM,
+ JMFF_CHECKSUM
+} JsonManifestFileField;
+
+/*
+ * Possible fields for one file as described by the manifest.
+ */
+typedef enum
+{
+ JMWRF_TIMELINE,
+ JMWRF_START_LSN,
+ JMWRF_END_LSN
+} JsonManifestWALRangeField;
+
+/*
+ * Internal state used while decoding the JSON-format backup manifest.
+ */
+typedef struct
+{
+ JsonManifestParseContext *context;
+ JsonManifestSemanticState state;
+
+ /* These fields are used for parsing objects in the list of files. */
+ JsonManifestFileField file_field;
+ char *pathname;
+ char *encoded_pathname;
+ char *size;
+ char *algorithm;
+ pg_checksum_type checksum_algorithm;
+ char *checksum;
+
+ /* These fields are used for parsing objects in the list of WAL ranges. */
+ JsonManifestWALRangeField wal_range_field;
+ char *timeline;
+ char *start_lsn;
+ char *end_lsn;
+
+ /* Miscellaneous other stuff. */
+ bool saw_version_field;
+ char *manifest_checksum;
+} JsonManifestParseState;
+
+static void json_manifest_object_start(void *state);
+static void json_manifest_object_end(void *state);
+static void json_manifest_array_start(void *state);
+static void json_manifest_array_end(void *state);
+static void json_manifest_object_field_start(void *state, char *fname,
+ bool isnull);
+static void json_manifest_scalar(void *state, char *token,
+ JsonTokenType tokentype);
+static void json_manifest_finalize_file(JsonManifestParseState *parse);
+static void json_manifest_finalize_wal_range(JsonManifestParseState *parse);
+static void verify_manifest_checksum(JsonManifestParseState *parse,
+ char *buffer, size_t size);
+static void json_manifest_parse_failure(JsonManifestParseContext *context,
+ char *msg);
+
+static int hexdecode_char(char c);
+static bool hexdecode_string(uint8 *result, char *input, int nbytes);
+static bool parse_xlogrecptr(XLogRecPtr *result, char *input);
+
+/*
+ * Main entrypoint to parse a JSON-format backup manifest.
+ *
+ * Caller should set up the parsing context and then invoke this function.
+ * For each file whose information is extracted from the manifest,
+ * context->perfile_cb is invoked. In case of trouble, context->error_cb is
+ * invoked and is expected not to return.
+ */
+void
+json_parse_manifest(JsonManifestParseContext *context, char *buffer,
+ size_t size)
+{
+ JsonLexContext *lex;
+ JsonParseErrorType json_error;
+ JsonSemAction sem;
+ JsonManifestParseState parse;
+
+ /* Set up our private parsing context. */
+ parse.context = context;
+ parse.state = JM_EXPECT_TOPLEVEL_START;
+ parse.saw_version_field = false;
+
+ /* Create a JSON lexing context. */
+ lex = makeJsonLexContextCstringLen(buffer, size, PG_UTF8, true);
+
+ /* Set up semantic actions. */
+ sem.semstate = &parse;
+ sem.object_start = json_manifest_object_start;
+ sem.object_end = json_manifest_object_end;
+ sem.array_start = json_manifest_array_start;
+ sem.array_end = json_manifest_array_end;
+ sem.object_field_start = json_manifest_object_field_start;
+ sem.object_field_end = NULL;
+ sem.array_element_start = NULL;
+ sem.array_element_end = NULL;
+ sem.scalar = json_manifest_scalar;
+
+ /* Run the actual JSON parser. */
+ json_error = pg_parse_json(lex, &sem);
+ if (json_error != JSON_SUCCESS)
+ json_manifest_parse_failure(context, json_errdetail(json_error, lex));
+ if (parse.state != JM_EXPECT_EOF)
+ json_manifest_parse_failure(context, "manifest ended unexpectedly");
+
+ /* Verify the manifest checksum. */
+ verify_manifest_checksum(&parse, buffer, size);
+}
+
+/*
+ * Invoked at the start of each object in the JSON document.
+ *
+ * The document as a whole is expected to be an object; each file and each
+ * WAL range is also expected to be an object. If we're anywhere else in the
+ * document, it's an error.
+ */
+static void
+json_manifest_object_start(void *state)
+{
+ JsonManifestParseState *parse = state;
+
+ switch (parse->state)
+ {
+ case JM_EXPECT_TOPLEVEL_START:
+ parse->state = JM_EXPECT_TOPLEVEL_FIELD;
+ break;
+ case JM_EXPECT_FILES_NEXT:
+ parse->state = JM_EXPECT_THIS_FILE_FIELD;
+ parse->pathname = NULL;
+ parse->encoded_pathname = NULL;
+ parse->size = NULL;
+ parse->algorithm = NULL;
+ parse->checksum = NULL;
+ break;
+ case JM_EXPECT_WAL_RANGES_NEXT:
+ parse->state = JM_EXPECT_THIS_WAL_RANGE_FIELD;
+ parse->timeline = NULL;
+ parse->start_lsn = NULL;
+ parse->end_lsn = NULL;
+ break;
+ default:
+ json_manifest_parse_failure(parse->context,
+ "unexpected object start");
+ break;
+ }
+}
+
+/*
+ * Invoked at the end of each object in the JSON document.
+ *
+ * The possible cases here are the same as for json_manifest_object_start.
+ * There's nothing special to do at the end of the document, but when we
+ * reach the end of an object representing a particular file or WAL range,
+ * we must call json_manifest_finalize_file() to save the associated details.
+ */
+static void
+json_manifest_object_end(void *state)
+{
+ JsonManifestParseState *parse = state;
+
+ switch (parse->state)
+ {
+ case JM_EXPECT_TOPLEVEL_END:
+ parse->state = JM_EXPECT_EOF;
+ break;
+ case JM_EXPECT_THIS_FILE_FIELD:
+ json_manifest_finalize_file(parse);
+ parse->state = JM_EXPECT_FILES_NEXT;
+ break;
+ case JM_EXPECT_THIS_WAL_RANGE_FIELD:
+ json_manifest_finalize_wal_range(parse);
+ parse->state = JM_EXPECT_WAL_RANGES_NEXT;
+ break;
+ default:
+ json_manifest_parse_failure(parse->context,
+ "unexpected object end");
+ break;
+ }
+}
+
+/*
+ * Invoked at the start of each array in the JSON document.
+ *
+ * Within the toplevel object, the value associated with the "Files" key
+ * should be an array. Similarly for the "WAL-Ranges" key. No other arrays
+ * are expected.
+ */
+static void
+json_manifest_array_start(void *state)
+{
+ JsonManifestParseState *parse = state;
+
+ switch (parse->state)
+ {
+ case JM_EXPECT_FILES_START:
+ parse->state = JM_EXPECT_FILES_NEXT;
+ break;
+ case JM_EXPECT_WAL_RANGES_START:
+ parse->state = JM_EXPECT_WAL_RANGES_NEXT;
+ break;
+ default:
+ json_manifest_parse_failure(parse->context,
+ "unexpected array start");
+ break;
+ }
+}
+
+/*
+ * Invoked at the end of each array in the JSON document.
+ *
+ * The cases here are analogous to those in json_manifest_array_start.
+ */
+static void
+json_manifest_array_end(void *state)
+{
+ JsonManifestParseState *parse = state;
+
+ switch (parse->state)
+ {
+ case JM_EXPECT_FILES_NEXT:
+ case JM_EXPECT_WAL_RANGES_NEXT:
+ parse->state = JM_EXPECT_TOPLEVEL_FIELD;
+ break;
+ default:
+ json_manifest_parse_failure(parse->context,
+ "unexpected array end");
+ break;
+ }
+}
+
+/*
+ * Invoked at the start of each object field in the JSON document.
+ */
+static void
+json_manifest_object_field_start(void *state, char *fname, bool isnull)
+{
+ JsonManifestParseState *parse = state;
+
+ switch (parse->state)
+ {
+ case JM_EXPECT_TOPLEVEL_FIELD:
+
+ /*
+ * Inside toplevel object. The version indicator should always be
+ * the first field.
+ */
+ if (!parse->saw_version_field)
+ {
+ if (strcmp(fname, "PostgreSQL-Backup-Manifest-Version") != 0)
+ json_manifest_parse_failure(parse->context,
+ "expected version indicator");
+ parse->state = JM_EXPECT_VERSION_VALUE;
+ parse->saw_version_field = true;
+ break;
+ }
+
+ /* Is this the list of files? */
+ if (strcmp(fname, "Files") == 0)
+ {
+ parse->state = JM_EXPECT_FILES_START;
+ break;
+ }
+
+ /* Is this the list of WAL ranges? */
+ if (strcmp(fname, "WAL-Ranges") == 0)
+ {
+ parse->state = JM_EXPECT_WAL_RANGES_START;
+ break;
+ }
+
+ /* Is this the manifest checksum? */
+ if (strcmp(fname, "Manifest-Checksum") == 0)
+ {
+ parse->state = JM_EXPECT_MANIFEST_CHECKSUM_VALUE;
+ break;
+ }
+
+ /* It's not a field we recognize. */
+ json_manifest_parse_failure(parse->context,
+ "unrecognized top-level field");
+ break;
+
+ case JM_EXPECT_THIS_FILE_FIELD:
+ /* Inside object for one file; which key have we got? */
+ if (strcmp(fname, "Path") == 0)
+ parse->file_field = JMFF_PATH;
+ else if (strcmp(fname, "Encoded-Path") == 0)
+ parse->file_field = JMFF_ENCODED_PATH;
+ else if (strcmp(fname, "Size") == 0)
+ parse->file_field = JMFF_SIZE;
+ else if (strcmp(fname, "Last-Modified") == 0)
+ parse->file_field = JMFF_LAST_MODIFIED;
+ else if (strcmp(fname, "Checksum-Algorithm") == 0)
+ parse->file_field = JMFF_CHECKSUM_ALGORITHM;
+ else if (strcmp(fname, "Checksum") == 0)
+ parse->file_field = JMFF_CHECKSUM;
+ else
+ json_manifest_parse_failure(parse->context,
+ "unexpected file field");
+ parse->state = JM_EXPECT_THIS_FILE_VALUE;
+ break;
+
+ case JM_EXPECT_THIS_WAL_RANGE_FIELD:
+ /* Inside object for one file; which key have we got? */
+ if (strcmp(fname, "Timeline") == 0)
+ parse->wal_range_field = JMWRF_TIMELINE;
+ else if (strcmp(fname, "Start-LSN") == 0)
+ parse->wal_range_field = JMWRF_START_LSN;
+ else if (strcmp(fname, "End-LSN") == 0)
+ parse->wal_range_field = JMWRF_END_LSN;
+ else
+ json_manifest_parse_failure(parse->context,
+ "unexpected WAL range field");
+ parse->state = JM_EXPECT_THIS_WAL_RANGE_VALUE;
+ break;
+
+ default:
+ json_manifest_parse_failure(parse->context,
+ "unexpected object field");
+ break;
+ }
+}
+
+/*
+ * Invoked at the start of each scalar in the JSON document.
+ *
+ * Object field names don't reach this code; those are handled by
+ * json_manifest_object_field_start. When we're inside of the object for
+ * a particular file or WAL range, that function will have noticed the name
+ * of the field, and we'll get the corresponding value here. When we're in
+ * the toplevel object, the parse state itself tells us which field this is.
+ *
+ * In all cases except for PostgreSQL-Backup-Manifest-Version, which we
+ * can just check on the spot, the goal here is just to save the value in
+ * the parse state for later use. We don't actually do anything until we
+ * reach either the end of the object representing this file, or the end
+ * of the manifest, as the case may be.
+ */
+static void
+json_manifest_scalar(void *state, char *token, JsonTokenType tokentype)
+{
+ JsonManifestParseState *parse = state;
+
+ switch (parse->state)
+ {
+ case JM_EXPECT_VERSION_VALUE:
+ if (strcmp(token, "1") != 0)
+ json_manifest_parse_failure(parse->context,
+ "unexpected manifest version");
+ parse->state = JM_EXPECT_TOPLEVEL_FIELD;
+ break;
+
+ case JM_EXPECT_THIS_FILE_VALUE:
+ switch (parse->file_field)
+ {
+ case JMFF_PATH:
+ parse->pathname = token;
+ break;
+ case JMFF_ENCODED_PATH:
+ parse->encoded_pathname = token;
+ break;
+ case JMFF_SIZE:
+ parse->size = token;
+ break;
+ case JMFF_LAST_MODIFIED:
+ pfree(token); /* unused */
+ break;
+ case JMFF_CHECKSUM_ALGORITHM:
+ parse->algorithm = token;
+ break;
+ case JMFF_CHECKSUM:
+ parse->checksum = token;
+ break;
+ }
+ parse->state = JM_EXPECT_THIS_FILE_FIELD;
+ break;
+
+ case JM_EXPECT_THIS_WAL_RANGE_VALUE:
+ switch (parse->wal_range_field)
+ {
+ case JMWRF_TIMELINE:
+ parse->timeline = token;
+ break;
+ case JMWRF_START_LSN:
+ parse->start_lsn = token;
+ break;
+ case JMWRF_END_LSN:
+ parse->end_lsn = token;
+ break;
+ }
+ parse->state = JM_EXPECT_THIS_WAL_RANGE_FIELD;
+ break;
+
+ case JM_EXPECT_MANIFEST_CHECKSUM_VALUE:
+ parse->state = JM_EXPECT_TOPLEVEL_END;
+ parse->manifest_checksum = token;
+ break;
+
+ default:
+ json_manifest_parse_failure(parse->context, "unexpected scalar");
+ break;
+ }
+}
+
+/*
+ * Do additional parsing and sanity-checking of the details gathered for one
+ * file, and invoke the per-file callback so that the caller gets those
+ * details. This happens for each file when the corresponding JSON object is
+ * completely parsed.
+ */
+static void
+json_manifest_finalize_file(JsonManifestParseState *parse)
+{
+ JsonManifestParseContext *context = parse->context;
+ size_t size;
+ char *ep;
+ int checksum_string_length;
+ pg_checksum_type checksum_type;
+ int checksum_length;
+ uint8 *checksum_payload;
+
+ /* Pathname and size are required. */
+ if (parse->pathname == NULL && parse->encoded_pathname == NULL)
+ json_manifest_parse_failure(parse->context, "missing path name");
+ if (parse->pathname != NULL && parse->encoded_pathname != NULL)
+ json_manifest_parse_failure(parse->context,
+ "both path name and encoded path name");
+ if (parse->size == NULL)
+ json_manifest_parse_failure(parse->context, "missing size");
+ if (parse->algorithm == NULL && parse->checksum != NULL)
+ json_manifest_parse_failure(parse->context,
+ "checksum without algorithm");
+
+ /* Decode encoded pathname, if that's what we have. */
+ if (parse->encoded_pathname != NULL)
+ {
+ int encoded_length = strlen(parse->encoded_pathname);
+ int raw_length = encoded_length / 2;
+
+ parse->pathname = palloc(raw_length + 1);
+ if (encoded_length % 2 != 0 ||
+ !hexdecode_string((uint8 *) parse->pathname,
+ parse->encoded_pathname,
+ raw_length))
+ json_manifest_parse_failure(parse->context,
+ "could not decode file name");
+ parse->pathname[raw_length] = '\0';
+ pfree(parse->encoded_pathname);
+ parse->encoded_pathname = NULL;
+ }
+
+ /* Parse size. */
+ size = strtoul(parse->size, &ep, 10);
+ if (*ep)
+ json_manifest_parse_failure(parse->context,
+ "file size is not an integer");
+
+ /* Parse the checksum algorithm, if it's present. */
+ if (parse->algorithm == NULL)
+ checksum_type = CHECKSUM_TYPE_NONE;
+ else if (!pg_checksum_parse_type(parse->algorithm, &checksum_type))
+ context->error_cb(context, "unrecognized checksum algorithm: \"%s\"",
+ parse->algorithm);
+
+ /* Parse the checksum payload, if it's present. */
+ checksum_string_length = parse->checksum == NULL ? 0
+ : strlen(parse->checksum);
+ if (checksum_string_length == 0)
+ {
+ checksum_length = 0;
+ checksum_payload = NULL;
+ }
+ else
+ {
+ checksum_length = checksum_string_length / 2;
+ checksum_payload = palloc(checksum_length);
+ if (checksum_string_length % 2 != 0 ||
+ !hexdecode_string(checksum_payload, parse->checksum,
+ checksum_length))
+ context->error_cb(context,
+ "invalid checksum for file \"%s\": \"%s\"",
+ parse->pathname, parse->checksum);
+ }
+
+ /* Invoke the callback with the details we've gathered. */
+ context->perfile_cb(context, parse->pathname, size,
+ checksum_type, checksum_length, checksum_payload);
+
+ /* Free memory we no longer need. */
+ if (parse->size != NULL)
+ {
+ pfree(parse->size);
+ parse->size = NULL;
+ }
+ if (parse->algorithm != NULL)
+ {
+ pfree(parse->algorithm);
+ parse->algorithm = NULL;
+ }
+ if (parse->checksum != NULL)
+ {
+ pfree(parse->checksum);
+ parse->checksum = NULL;
+ }
+}
+
+/*
+ * Do additional parsing and sanity-checking of the details gathered for one
+ * WAL range, and invoke the per-WAL-range callback so that the caller gets
+ * those details. This happens for each WAL range when the corresponding JSON
+ * object is completely parsed.
+ */
+static void
+json_manifest_finalize_wal_range(JsonManifestParseState *parse)
+{
+ JsonManifestParseContext *context = parse->context;
+ TimeLineID tli;
+ XLogRecPtr start_lsn,
+ end_lsn;
+ char *ep;
+
+ /* Make sure all fields are present. */
+ if (parse->timeline == NULL)
+ json_manifest_parse_failure(parse->context, "missing timeline");
+ if (parse->start_lsn == NULL)
+ json_manifest_parse_failure(parse->context, "missing start LSN");
+ if (parse->end_lsn == NULL)
+ json_manifest_parse_failure(parse->context, "missing end LSN");
+
+ /* Parse timeline. */
+ tli = strtoul(parse->timeline, &ep, 10);
+ if (*ep)
+ json_manifest_parse_failure(parse->context,
+ "timeline is not an integer");
+ if (!parse_xlogrecptr(&start_lsn, parse->start_lsn))
+ json_manifest_parse_failure(parse->context,
+ "could not parse start LSN");
+ if (!parse_xlogrecptr(&end_lsn, parse->end_lsn))
+ json_manifest_parse_failure(parse->context,
+ "could not parse end LSN");
+
+ /* Invoke the callback with the details we've gathered. */
+ context->perwalrange_cb(context, tli, start_lsn, end_lsn);
+
+ /* Free memory we no longer need. */
+ if (parse->timeline != NULL)
+ {
+ pfree(parse->timeline);
+ parse->timeline = NULL;
+ }
+ if (parse->start_lsn != NULL)
+ {
+ pfree(parse->start_lsn);
+ parse->start_lsn = NULL;
+ }
+ if (parse->end_lsn != NULL)
+ {
+ pfree(parse->end_lsn);
+ parse->end_lsn = NULL;
+ }
+}
+
+/*
+ * Verify that the manifest checksum is correct.
+ *
+ * The last line of the manifest file is excluded from the manifest checksum,
+ * because the last line is expected to contain the checksum that covers
+ * the rest of the file.
+ */
+static void
+verify_manifest_checksum(JsonManifestParseState *parse, char *buffer,
+ size_t size)
+{
+ JsonManifestParseContext *context = parse->context;
+ size_t i;
+ size_t number_of_newlines = 0;
+ size_t ultimate_newline = 0;
+ size_t penultimate_newline = 0;
+ pg_cryptohash_ctx *manifest_ctx;
+ uint8 manifest_checksum_actual[PG_SHA256_DIGEST_LENGTH];
+ uint8 manifest_checksum_expected[PG_SHA256_DIGEST_LENGTH];
+
+ /* Find the last two newlines in the file. */
+ for (i = 0; i < size; ++i)
+ {
+ if (buffer[i] == '\n')
+ {
+ ++number_of_newlines;
+ penultimate_newline = ultimate_newline;
+ ultimate_newline = i;
+ }
+ }
+
+ /*
+ * Make sure that the last newline is right at the end, and that there are
+ * at least two lines total. We need this to be true in order for the
+ * following code, which computes the manifest checksum, to work properly.
+ */
+ if (number_of_newlines < 2)
+ json_manifest_parse_failure(parse->context,
+ "expected at least 2 lines");
+ if (ultimate_newline != size - 1)
+ json_manifest_parse_failure(parse->context,
+ "last line not newline-terminated");
+
+ /* Checksum the rest. */
+ manifest_ctx = pg_cryptohash_create(PG_SHA256);
+ if (manifest_ctx == NULL)
+ context->error_cb(context, "out of memory");
+ if (pg_cryptohash_init(manifest_ctx) < 0)
+ context->error_cb(context, "could not initialize checksum of manifest");
+ if (pg_cryptohash_update(manifest_ctx, (uint8 *) buffer, penultimate_newline + 1) < 0)
+ context->error_cb(context, "could not update checksum of manifest");
+ if (pg_cryptohash_final(manifest_ctx, manifest_checksum_actual,
+ sizeof(manifest_checksum_actual)) < 0)
+ context->error_cb(context, "could not finalize checksum of manifest");
+
+ /* Now verify it. */
+ if (parse->manifest_checksum == NULL)
+ context->error_cb(parse->context, "manifest has no checksum");
+ if (strlen(parse->manifest_checksum) != PG_SHA256_DIGEST_LENGTH * 2 ||
+ !hexdecode_string(manifest_checksum_expected, parse->manifest_checksum,
+ PG_SHA256_DIGEST_LENGTH))
+ context->error_cb(context, "invalid manifest checksum: \"%s\"",
+ parse->manifest_checksum);
+ if (memcmp(manifest_checksum_actual, manifest_checksum_expected,
+ PG_SHA256_DIGEST_LENGTH) != 0)
+ context->error_cb(context, "manifest checksum mismatch");
+ pg_cryptohash_free(manifest_ctx);
+}
+
+/*
+ * Report a parse error.
+ *
+ * This is intended to be used for fairly low-level failures that probably
+ * shouldn't occur unless somebody has deliberately constructed a bad manifest,
+ * or unless the server is generating bad manifests due to some bug. msg should
+ * be a short string giving some hint as to what the problem is.
+ */
+static void
+json_manifest_parse_failure(JsonManifestParseContext *context, char *msg)
+{
+ context->error_cb(context, "could not parse backup manifest: %s", msg);
+}
+
+/*
+ * Convert a character which represents a hexadecimal digit to an integer.
+ *
+ * Returns -1 if the character is not a hexadecimal digit.
+ */
+static int
+hexdecode_char(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+
+ return -1;
+}
+
+/*
+ * Decode a hex string into a byte string, 2 hex chars per byte.
+ *
+ * Returns false if invalid characters are encountered; otherwise true.
+ */
+static bool
+hexdecode_string(uint8 *result, char *input, int nbytes)
+{
+ int i;
+
+ for (i = 0; i < nbytes; ++i)
+ {
+ int n1 = hexdecode_char(input[i * 2]);
+ int n2 = hexdecode_char(input[i * 2 + 1]);
+
+ if (n1 < 0 || n2 < 0)
+ return false;
+ result[i] = n1 * 16 + n2;
+ }
+
+ return true;
+}
+
+/*
+ * Parse an XLogRecPtr expressed using the usual string format.
+ */
+static bool
+parse_xlogrecptr(XLogRecPtr *result, char *input)
+{
+ uint32 hi;
+ uint32 lo;
+
+ if (sscanf(input, "%X/%X", &hi, &lo) != 2)
+ return false;
+ *result = ((uint64) hi) << 32 | lo;
+ return true;
+}
diff --git a/src/bin/pg_verifybackup/parse_manifest.h b/src/bin/pg_verifybackup/parse_manifest.h
new file mode 100644
index 0000000..b0745a0
--- /dev/null
+++ b/src/bin/pg_verifybackup/parse_manifest.h
@@ -0,0 +1,46 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_manifest.h
+ * Parse a backup manifest in JSON format.
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/bin/pg_verifybackup/parse_manifest.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef PARSE_MANIFEST_H
+#define PARSE_MANIFEST_H
+
+#include "access/xlogdefs.h"
+#include "common/checksum_helper.h"
+#include "mb/pg_wchar.h"
+
+struct JsonManifestParseContext;
+typedef struct JsonManifestParseContext JsonManifestParseContext;
+
+typedef void (*json_manifest_perfile_callback) (JsonManifestParseContext *,
+ char *pathname,
+ size_t size, pg_checksum_type checksum_type,
+ int checksum_length, uint8 *checksum_payload);
+typedef void (*json_manifest_perwalrange_callback) (JsonManifestParseContext *,
+ TimeLineID tli,
+ XLogRecPtr start_lsn, XLogRecPtr end_lsn);
+typedef void (*json_manifest_error_callback) (JsonManifestParseContext *,
+ const char *fmt,...) pg_attribute_printf(2, 3)
+ pg_attribute_noreturn();
+
+struct JsonManifestParseContext
+{
+ void *private_data;
+ json_manifest_perfile_callback perfile_cb;
+ json_manifest_perwalrange_callback perwalrange_cb;
+ json_manifest_error_callback error_cb;
+};
+
+extern void json_parse_manifest(JsonManifestParseContext *context,
+ char *buffer, size_t size);
+
+#endif
diff --git a/src/bin/pg_verifybackup/pg_verifybackup.c b/src/bin/pg_verifybackup/pg_verifybackup.c
new file mode 100644
index 0000000..bb93b43
--- /dev/null
+++ b/src/bin/pg_verifybackup/pg_verifybackup.c
@@ -0,0 +1,924 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_verifybackup.c
+ * Verify a backup against a backup manifest.
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/bin/pg_verifybackup/pg_verifybackup.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "common/hashfn.h"
+#include "common/logging.h"
+#include "fe_utils/simple_list.h"
+#include "getopt_long.h"
+#include "parse_manifest.h"
+
+/*
+ * For efficiency, we'd like our hash table containing information about the
+ * manifest to start out with approximately the correct number of entries.
+ * There's no way to know the exact number of entries without reading the whole
+ * file, but we can get an estimate by dividing the file size by the estimated
+ * number of bytes per line.
+ *
+ * This could be off by about a factor of two in either direction, because the
+ * checksum algorithm has a big impact on the line lengths; e.g. a SHA512
+ * checksum is 128 hex bytes, whereas a CRC-32C value is only 8, and there
+ * might be no checksum at all.
+ */
+#define ESTIMATED_BYTES_PER_MANIFEST_LINE 100
+
+/*
+ * How many bytes should we try to read from a file at once?
+ */
+#define READ_CHUNK_SIZE 4096
+
+/*
+ * Each file described by the manifest file is parsed to produce an object
+ * like this.
+ */
+typedef struct manifest_file
+{
+ uint32 status; /* hash status */
+ char *pathname;
+ size_t size;
+ pg_checksum_type checksum_type;
+ int checksum_length;
+ uint8 *checksum_payload;
+ bool matched;
+ bool bad;
+} manifest_file;
+
+/*
+ * Define a hash table which we can use to store information about the files
+ * mentioned in the backup manifest.
+ */
+static uint32 hash_string_pointer(char *s);
+#define SH_PREFIX manifest_files
+#define SH_ELEMENT_TYPE manifest_file
+#define SH_KEY_TYPE char *
+#define SH_KEY pathname
+#define SH_HASH_KEY(tb, key) hash_string_pointer(key)
+#define SH_EQUAL(tb, a, b) (strcmp(a, b) == 0)
+#define SH_SCOPE static inline
+#define SH_RAW_ALLOCATOR pg_malloc0
+#define SH_DECLARE
+#define SH_DEFINE
+#include "lib/simplehash.h"
+
+/*
+ * Each WAL range described by the manifest file is parsed to produce an
+ * object like this.
+ */
+typedef struct manifest_wal_range
+{
+ TimeLineID tli;
+ XLogRecPtr start_lsn;
+ XLogRecPtr end_lsn;
+ struct manifest_wal_range *next;
+ struct manifest_wal_range *prev;
+} manifest_wal_range;
+
+/*
+ * Details we need in callbacks that occur while parsing a backup manifest.
+ */
+typedef struct parser_context
+{
+ manifest_files_hash *ht;
+ manifest_wal_range *first_wal_range;
+ manifest_wal_range *last_wal_range;
+} parser_context;
+
+/*
+ * All of the context information we need while checking a backup manifest.
+ */
+typedef struct verifier_context
+{
+ manifest_files_hash *ht;
+ char *backup_directory;
+ SimpleStringList ignore_list;
+ bool exit_on_error;
+ bool saw_any_error;
+} verifier_context;
+
+static void parse_manifest_file(char *manifest_path,
+ manifest_files_hash **ht_p,
+ manifest_wal_range **first_wal_range_p);
+
+static void record_manifest_details_for_file(JsonManifestParseContext *context,
+ char *pathname, size_t size,
+ pg_checksum_type checksum_type,
+ int checksum_length,
+ uint8 *checksum_payload);
+static void record_manifest_details_for_wal_range(JsonManifestParseContext *context,
+ TimeLineID tli,
+ XLogRecPtr start_lsn,
+ XLogRecPtr end_lsn);
+static void report_manifest_error(JsonManifestParseContext *context,
+ const char *fmt,...)
+ pg_attribute_printf(2, 3) pg_attribute_noreturn();
+
+static void verify_backup_directory(verifier_context *context,
+ char *relpath, char *fullpath);
+static void verify_backup_file(verifier_context *context,
+ char *relpath, char *fullpath);
+static void report_extra_backup_files(verifier_context *context);
+static void verify_backup_checksums(verifier_context *context);
+static void verify_file_checksum(verifier_context *context,
+ manifest_file *m, char *pathname);
+static void parse_required_wal(verifier_context *context,
+ char *pg_waldump_path,
+ char *wal_directory,
+ manifest_wal_range *first_wal_range);
+
+static void report_backup_error(verifier_context *context,
+ const char *pg_restrict fmt,...)
+ pg_attribute_printf(2, 3);
+static void report_fatal_error(const char *pg_restrict fmt,...)
+ pg_attribute_printf(1, 2) pg_attribute_noreturn();
+static bool should_ignore_relpath(verifier_context *context, char *relpath);
+
+static void usage(void);
+
+static const char *progname;
+
+/*
+ * Main entry point.
+ */
+int
+main(int argc, char **argv)
+{
+ static struct option long_options[] = {
+ {"exit-on-error", no_argument, NULL, 'e'},
+ {"ignore", required_argument, NULL, 'i'},
+ {"manifest-path", required_argument, NULL, 'm'},
+ {"no-parse-wal", no_argument, NULL, 'n'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"skip-checksums", no_argument, NULL, 's'},
+ {"wal-directory", required_argument, NULL, 'w'},
+ {NULL, 0, NULL, 0}
+ };
+
+ int c;
+ verifier_context context;
+ manifest_wal_range *first_wal_range;
+ char *manifest_path = NULL;
+ bool no_parse_wal = false;
+ bool quiet = false;
+ bool skip_checksums = false;
+ char *wal_directory = NULL;
+ char *pg_waldump_path = NULL;
+
+ pg_logging_init(argv[0]);
+ set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_verifybackup"));
+ progname = get_progname(argv[0]);
+
+ memset(&context, 0, sizeof(context));
+
+ if (argc > 1)
+ {
+ if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
+ {
+ usage();
+ exit(0);
+ }
+ if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
+ {
+ puts("pg_verifybackup (PostgreSQL) " PG_VERSION);
+ exit(0);
+ }
+ }
+
+ /*
+ * Skip certain files in the toplevel directory.
+ *
+ * Ignore the backup_manifest file, because it's not included in the
+ * backup manifest.
+ *
+ * Ignore the pg_wal directory, because those files are not included in
+ * the backup manifest either, since they are fetched separately from the
+ * backup itself, and verified via a separate mechanism.
+ *
+ * Ignore postgresql.auto.conf, recovery.signal, and standby.signal,
+ * because we expect that those files may sometimes be created or changed
+ * as part of the backup process. For example, pg_basebackup -R will
+ * modify postgresql.auto.conf and create standby.signal.
+ */
+ simple_string_list_append(&context.ignore_list, "backup_manifest");
+ simple_string_list_append(&context.ignore_list, "pg_wal");
+ simple_string_list_append(&context.ignore_list, "postgresql.auto.conf");
+ simple_string_list_append(&context.ignore_list, "recovery.signal");
+ simple_string_list_append(&context.ignore_list, "standby.signal");
+
+ while ((c = getopt_long(argc, argv, "ei:m:nqsw:", long_options, NULL)) != -1)
+ {
+ switch (c)
+ {
+ case 'e':
+ context.exit_on_error = true;
+ break;
+ case 'i':
+ {
+ char *arg = pstrdup(optarg);
+
+ canonicalize_path(arg);
+ simple_string_list_append(&context.ignore_list, arg);
+ break;
+ }
+ case 'm':
+ manifest_path = pstrdup(optarg);
+ canonicalize_path(manifest_path);
+ break;
+ case 'n':
+ no_parse_wal = true;
+ break;
+ case 'q':
+ quiet = true;
+ break;
+ case 's':
+ skip_checksums = true;
+ break;
+ case 'w':
+ wal_directory = pstrdup(optarg);
+ canonicalize_path(wal_directory);
+ break;
+ default:
+ fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
+ progname);
+ exit(1);
+ }
+ }
+
+ /* Get backup directory name */
+ if (optind >= argc)
+ {
+ pg_log_fatal("no backup directory specified");
+ fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
+ progname);
+ exit(1);
+ }
+ context.backup_directory = pstrdup(argv[optind++]);
+ canonicalize_path(context.backup_directory);
+
+ /* Complain if any arguments remain */
+ if (optind < argc)
+ {
+ pg_log_fatal("too many command-line arguments (first is \"%s\")",
+ argv[optind]);
+ fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
+ progname);
+ exit(1);
+ }
+
+ /* Unless --no-parse-wal was specified, we will need pg_waldump. */
+ if (!no_parse_wal)
+ {
+ int ret;
+
+ pg_waldump_path = pg_malloc(MAXPGPATH);
+ ret = find_other_exec(argv[0], "pg_waldump",
+ "pg_waldump (PostgreSQL) " PG_VERSION "\n",
+ pg_waldump_path);
+ if (ret < 0)
+ {
+ char full_path[MAXPGPATH];
+
+ if (find_my_exec(argv[0], full_path) < 0)
+ strlcpy(full_path, progname, sizeof(full_path));
+ if (ret == -1)
+ pg_log_fatal("The program \"%s\" is needed by %s but was not found in the\n"
+ "same directory as \"%s\".\n"
+ "Check your installation.",
+ "pg_waldump", "pg_verifybackup", full_path);
+ else
+ pg_log_fatal("The program \"%s\" was found by \"%s\"\n"
+ "but was not the same version as %s.\n"
+ "Check your installation.",
+ "pg_waldump", full_path, "pg_verifybackup");
+ exit(1);
+ }
+ }
+
+ /* By default, look for the manifest in the backup directory. */
+ if (manifest_path == NULL)
+ manifest_path = psprintf("%s/backup_manifest",
+ context.backup_directory);
+
+ /* By default, look for the WAL in the backup directory, too. */
+ if (wal_directory == NULL)
+ wal_directory = psprintf("%s/pg_wal", context.backup_directory);
+
+ /*
+ * Try to read the manifest. We treat any errors encountered while parsing
+ * the manifest as fatal; there doesn't seem to be much point in trying to
+ * verify the backup directory against a corrupted manifest.
+ */
+ parse_manifest_file(manifest_path, &context.ht, &first_wal_range);
+
+ /*
+ * Now scan the files in the backup directory. At this stage, we verify
+ * that every file on disk is present in the manifest and that the sizes
+ * match. We also set the "matched" flag on every manifest entry that
+ * corresponds to a file on disk.
+ */
+ verify_backup_directory(&context, NULL, context.backup_directory);
+
+ /*
+ * The "matched" flag should now be set on every entry in the hash table.
+ * Any entries for which the bit is not set are files mentioned in the
+ * manifest that don't exist on disk.
+ */
+ report_extra_backup_files(&context);
+
+ /*
+ * Now do the expensive work of verifying file checksums, unless we were
+ * told to skip it.
+ */
+ if (!skip_checksums)
+ verify_backup_checksums(&context);
+
+ /*
+ * Try to parse the required ranges of WAL records, unless we were told
+ * not to do so.
+ */
+ if (!no_parse_wal)
+ parse_required_wal(&context, pg_waldump_path,
+ wal_directory, first_wal_range);
+
+ /*
+ * If everything looks OK, tell the user this, unless we were asked to
+ * work quietly.
+ */
+ if (!context.saw_any_error && !quiet)
+ printf(_("backup successfully verified\n"));
+
+ return context.saw_any_error ? 1 : 0;
+}
+
+/*
+ * Parse a manifest file. Construct a hash table with information about
+ * all the files it mentions, and a linked list of all the WAL ranges it
+ * mentions.
+ */
+static void
+parse_manifest_file(char *manifest_path, manifest_files_hash **ht_p,
+ manifest_wal_range **first_wal_range_p)
+{
+ int fd;
+ struct stat statbuf;
+ off_t estimate;
+ uint32 initial_size;
+ manifest_files_hash *ht;
+ char *buffer;
+ int rc;
+ parser_context private_context;
+ JsonManifestParseContext context;
+
+ /* Open the manifest file. */
+ if ((fd = open(manifest_path, O_RDONLY | PG_BINARY, 0)) < 0)
+ report_fatal_error("could not open file \"%s\": %m", manifest_path);
+
+ /* Figure out how big the manifest is. */
+ if (fstat(fd, &statbuf) != 0)
+ report_fatal_error("could not stat file \"%s\": %m", manifest_path);
+
+ /* Guess how large to make the hash table based on the manifest size. */
+ estimate = statbuf.st_size / ESTIMATED_BYTES_PER_MANIFEST_LINE;
+ initial_size = Min(PG_UINT32_MAX, Max(estimate, 256));
+
+ /* Create the hash table. */
+ ht = manifest_files_create(initial_size, NULL);
+
+ /*
+ * Slurp in the whole file.
+ *
+ * This is not ideal, but there's currently no easy way to get
+ * pg_parse_json() to perform incremental parsing.
+ */
+ buffer = pg_malloc(statbuf.st_size);
+ rc = read(fd, buffer, statbuf.st_size);
+ if (rc != statbuf.st_size)
+ {
+ if (rc < 0)
+ report_fatal_error("could not read file \"%s\": %m",
+ manifest_path);
+ else
+ report_fatal_error("could not read file \"%s\": read %d of %lld",
+ manifest_path, rc, (long long int) statbuf.st_size);
+ }
+
+ /* Close the manifest file. */
+ close(fd);
+
+ /* Parse the manifest. */
+ private_context.ht = ht;
+ private_context.first_wal_range = NULL;
+ private_context.last_wal_range = NULL;
+ context.private_data = &private_context;
+ context.perfile_cb = record_manifest_details_for_file;
+ context.perwalrange_cb = record_manifest_details_for_wal_range;
+ context.error_cb = report_manifest_error;
+ json_parse_manifest(&context, buffer, statbuf.st_size);
+
+ /* Done with the buffer. */
+ pfree(buffer);
+
+ /* Return the file hash table and WAL range list we constructed. */
+ *ht_p = ht;
+ *first_wal_range_p = private_context.first_wal_range;
+}
+
+/*
+ * Report an error while parsing the manifest.
+ *
+ * We consider all such errors to be fatal errors. The manifest parser
+ * expects this function not to return.
+ */
+static void
+report_manifest_error(JsonManifestParseContext *context, const char *fmt,...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ pg_log_generic_v(PG_LOG_FATAL, gettext(fmt), ap);
+ va_end(ap);
+
+ exit(1);
+}
+
+/*
+ * Record details extracted from the backup manifest for one file.
+ */
+static void
+record_manifest_details_for_file(JsonManifestParseContext *context,
+ char *pathname, size_t size,
+ pg_checksum_type checksum_type,
+ int checksum_length, uint8 *checksum_payload)
+{
+ parser_context *pcxt = context->private_data;
+ manifest_files_hash *ht = pcxt->ht;
+ manifest_file *m;
+ bool found;
+
+ /* Make a new entry in the hash table for this file. */
+ m = manifest_files_insert(ht, pathname, &found);
+ if (found)
+ report_fatal_error("duplicate path name in backup manifest: \"%s\"",
+ pathname);
+
+ /* Initialize the entry. */
+ m->size = size;
+ m->checksum_type = checksum_type;
+ m->checksum_length = checksum_length;
+ m->checksum_payload = checksum_payload;
+ m->matched = false;
+ m->bad = false;
+}
+
+/*
+ * Record details extracted from the backup manifest for one WAL range.
+ */
+static void
+record_manifest_details_for_wal_range(JsonManifestParseContext *context,
+ TimeLineID tli,
+ XLogRecPtr start_lsn, XLogRecPtr end_lsn)
+{
+ parser_context *pcxt = context->private_data;
+ manifest_wal_range *range;
+
+ /* Allocate and initialize a struct describing this WAL range. */
+ range = palloc(sizeof(manifest_wal_range));
+ range->tli = tli;
+ range->start_lsn = start_lsn;
+ range->end_lsn = end_lsn;
+ range->prev = pcxt->last_wal_range;
+ range->next = NULL;
+
+ /* Add it to the end of the list. */
+ if (pcxt->first_wal_range == NULL)
+ pcxt->first_wal_range = range;
+ else
+ pcxt->last_wal_range->next = range;
+ pcxt->last_wal_range = range;
+}
+
+/*
+ * Verify one directory.
+ *
+ * 'relpath' is NULL if we are to verify the top-level backup directory,
+ * and otherwise the relative path to the directory that is to be verified.
+ *
+ * 'fullpath' is the backup directory with 'relpath' appended; i.e. the actual
+ * filesystem path at which it can be found.
+ */
+static void
+verify_backup_directory(verifier_context *context, char *relpath,
+ char *fullpath)
+{
+ DIR *dir;
+ struct dirent *dirent;
+
+ dir = opendir(fullpath);
+ if (dir == NULL)
+ {
+ /*
+ * If even the toplevel backup directory cannot be found, treat this
+ * as a fatal error.
+ */
+ if (relpath == NULL)
+ report_fatal_error("could not open directory \"%s\": %m", fullpath);
+
+ /*
+ * Otherwise, treat this as a non-fatal error, but ignore any further
+ * errors related to this path and anything beneath it.
+ */
+ report_backup_error(context,
+ "could not open directory \"%s\": %m", fullpath);
+ simple_string_list_append(&context->ignore_list, relpath);
+
+ return;
+ }
+
+ while (errno = 0, (dirent = readdir(dir)) != NULL)
+ {
+ char *filename = dirent->d_name;
+ char *newfullpath = psprintf("%s/%s", fullpath, filename);
+ char *newrelpath;
+
+ /* Skip "." and ".." */
+ if (filename[0] == '.' && (filename[1] == '\0'
+ || strcmp(filename, "..") == 0))
+ continue;
+
+ if (relpath == NULL)
+ newrelpath = pstrdup(filename);
+ else
+ newrelpath = psprintf("%s/%s", relpath, filename);
+
+ if (!should_ignore_relpath(context, newrelpath))
+ verify_backup_file(context, newrelpath, newfullpath);
+
+ pfree(newfullpath);
+ pfree(newrelpath);
+ }
+
+ if (closedir(dir))
+ {
+ report_backup_error(context,
+ "could not close directory \"%s\": %m", fullpath);
+ return;
+ }
+}
+
+/*
+ * Verify one file (which might actually be a directory or a symlink).
+ *
+ * The arguments to this function have the same meaning as the arguments to
+ * verify_backup_directory.
+ */
+static void
+verify_backup_file(verifier_context *context, char *relpath, char *fullpath)
+{
+ struct stat sb;
+ manifest_file *m;
+
+ if (stat(fullpath, &sb) != 0)
+ {
+ report_backup_error(context,
+ "could not stat file or directory \"%s\": %m",
+ relpath);
+
+ /*
+ * Suppress further errors related to this path name and, if it's a
+ * directory, anything underneath it.
+ */
+ simple_string_list_append(&context->ignore_list, relpath);
+
+ return;
+ }
+
+ /* If it's a directory, just recurse. */
+ if (S_ISDIR(sb.st_mode))
+ {
+ verify_backup_directory(context, relpath, fullpath);
+ return;
+ }
+
+ /* If it's not a directory, it should be a plain file. */
+ if (!S_ISREG(sb.st_mode))
+ {
+ report_backup_error(context,
+ "\"%s\" is not a file or directory",
+ relpath);
+ return;
+ }
+
+ /* Check whether there's an entry in the manifest hash. */
+ m = manifest_files_lookup(context->ht, relpath);
+ if (m == NULL)
+ {
+ report_backup_error(context,
+ "\"%s\" is present on disk but not in the manifest",
+ relpath);
+ return;
+ }
+
+ /* Flag this entry as having been encountered in the filesystem. */
+ m->matched = true;
+
+ /* Check that the size matches. */
+ if (m->size != sb.st_size)
+ {
+ report_backup_error(context,
+ "\"%s\" has size %lld on disk but size %zu in the manifest",
+ relpath, (long long int) sb.st_size, m->size);
+ m->bad = true;
+ }
+
+ /*
+ * We don't verify checksums at this stage. We first finish verifying that
+ * we have the expected set of files with the expected sizes, and only
+ * afterwards verify the checksums. That's because computing checksums may
+ * take a while, and we'd like to report more obvious problems quickly.
+ */
+}
+
+/*
+ * Scan the hash table for entries where the 'matched' flag is not set; report
+ * that such files are present in the manifest but not on disk.
+ */
+static void
+report_extra_backup_files(verifier_context *context)
+{
+ manifest_files_iterator it;
+ manifest_file *m;
+
+ manifest_files_start_iterate(context->ht, &it);
+ while ((m = manifest_files_iterate(context->ht, &it)) != NULL)
+ if (!m->matched && !should_ignore_relpath(context, m->pathname))
+ report_backup_error(context,
+ "\"%s\" is present in the manifest but not on disk",
+ m->pathname);
+}
+
+/*
+ * Verify checksums for hash table entries that are otherwise unproblematic.
+ * If we've already reported some problem related to a hash table entry, or
+ * if it has no checksum, just skip it.
+ */
+static void
+verify_backup_checksums(verifier_context *context)
+{
+ manifest_files_iterator it;
+ manifest_file *m;
+
+ manifest_files_start_iterate(context->ht, &it);
+ while ((m = manifest_files_iterate(context->ht, &it)) != NULL)
+ {
+ if (m->matched && !m->bad && m->checksum_type != CHECKSUM_TYPE_NONE &&
+ !should_ignore_relpath(context, m->pathname))
+ {
+ char *fullpath;
+
+ /* Compute the full pathname to the target file. */
+ fullpath = psprintf("%s/%s", context->backup_directory,
+ m->pathname);
+
+ /* Do the actual checksum verification. */
+ verify_file_checksum(context, m, fullpath);
+
+ /* Avoid leaking memory. */
+ pfree(fullpath);
+ }
+ }
+}
+
+/*
+ * Verify the checksum of a single file.
+ */
+static void
+verify_file_checksum(verifier_context *context, manifest_file *m,
+ char *fullpath)
+{
+ pg_checksum_context checksum_ctx;
+ char *relpath = m->pathname;
+ int fd;
+ int rc;
+ size_t bytes_read = 0;
+ uint8 buffer[READ_CHUNK_SIZE];
+ uint8 checksumbuf[PG_CHECKSUM_MAX_LENGTH];
+ int checksumlen;
+
+ /* Open the target file. */
+ if ((fd = open(fullpath, O_RDONLY | PG_BINARY, 0)) < 0)
+ {
+ report_backup_error(context, "could not open file \"%s\": %m",
+ relpath);
+ return;
+ }
+
+ /* Initialize checksum context. */
+ if (pg_checksum_init(&checksum_ctx, m->checksum_type) < 0)
+ {
+ report_backup_error(context, "could not initialize checksum of file \"%s\"",
+ relpath);
+ close(fd);
+ return;
+ }
+
+ /* Read the file chunk by chunk, updating the checksum as we go. */
+ while ((rc = read(fd, buffer, READ_CHUNK_SIZE)) > 0)
+ {
+ bytes_read += rc;
+ if (pg_checksum_update(&checksum_ctx, buffer, rc) < 0)
+ {
+ report_backup_error(context, "could not update checksum of file \"%s\"",
+ relpath);
+ close(fd);
+ return;
+ }
+
+
+ }
+ if (rc < 0)
+ report_backup_error(context, "could not read file \"%s\": %m",
+ relpath);
+
+ /* Close the file. */
+ if (close(fd) != 0)
+ {
+ report_backup_error(context, "could not close file \"%s\": %m",
+ relpath);
+ return;
+ }
+
+ /* If we didn't manage to read the whole file, bail out now. */
+ if (rc < 0)
+ return;
+
+ /*
+ * Double-check that we read the expected number of bytes from the file.
+ * Normally, a file size mismatch would be caught in verify_backup_file
+ * and this check would never be reached, but this provides additional
+ * safety and clarity in the event of concurrent modifications or
+ * filesystem misbehavior.
+ */
+ if (bytes_read != m->size)
+ {
+ report_backup_error(context,
+ "file \"%s\" should contain %zu bytes, but read %zu bytes",
+ relpath, m->size, bytes_read);
+ return;
+ }
+
+ /* Get the final checksum. */
+ checksumlen = pg_checksum_final(&checksum_ctx, checksumbuf);
+ if (checksumlen < 0)
+ {
+ report_backup_error(context,
+ "could not finalize checksum of file \"%s\"",
+ relpath);
+ return;
+ }
+
+ /* And check it against the manifest. */
+ if (checksumlen != m->checksum_length)
+ report_backup_error(context,
+ "file \"%s\" has checksum of length %d, but expected %d",
+ relpath, m->checksum_length, checksumlen);
+ else if (memcmp(checksumbuf, m->checksum_payload, checksumlen) != 0)
+ report_backup_error(context,
+ "checksum mismatch for file \"%s\"",
+ relpath);
+}
+
+/*
+ * Attempt to parse the WAL files required to restore from backup using
+ * pg_waldump.
+ */
+static void
+parse_required_wal(verifier_context *context, char *pg_waldump_path,
+ char *wal_directory, manifest_wal_range *first_wal_range)
+{
+ manifest_wal_range *this_wal_range = first_wal_range;
+
+ while (this_wal_range != NULL)
+ {
+ char *pg_waldump_cmd;
+
+ pg_waldump_cmd = psprintf("\"%s\" --quiet --path=\"%s\" --timeline=%u --start=%X/%X --end=%X/%X\n",
+ pg_waldump_path, wal_directory, this_wal_range->tli,
+ LSN_FORMAT_ARGS(this_wal_range->start_lsn),
+ LSN_FORMAT_ARGS(this_wal_range->end_lsn));
+ if (system(pg_waldump_cmd) != 0)
+ report_backup_error(context,
+ "WAL parsing failed for timeline %u",
+ this_wal_range->tli);
+
+ this_wal_range = this_wal_range->next;
+ }
+}
+
+/*
+ * Report a problem with the backup.
+ *
+ * Update the context to indicate that we saw an error, and exit if the
+ * context says we should.
+ */
+static void
+report_backup_error(verifier_context *context, const char *pg_restrict fmt,...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ pg_log_generic_v(PG_LOG_ERROR, gettext(fmt), ap);
+ va_end(ap);
+
+ context->saw_any_error = true;
+ if (context->exit_on_error)
+ exit(1);
+}
+
+/*
+ * Report a fatal error and exit
+ */
+static void
+report_fatal_error(const char *pg_restrict fmt,...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ pg_log_generic_v(PG_LOG_FATAL, gettext(fmt), ap);
+ va_end(ap);
+
+ exit(1);
+}
+
+/*
+ * Is the specified relative path, or some prefix of it, listed in the set
+ * of paths to ignore?
+ *
+ * Note that by "prefix" we mean a parent directory; for this purpose,
+ * "aa/bb" is not a prefix of "aa/bbb", but it is a prefix of "aa/bb/cc".
+ */
+static bool
+should_ignore_relpath(verifier_context *context, char *relpath)
+{
+ SimpleStringListCell *cell;
+
+ for (cell = context->ignore_list.head; cell != NULL; cell = cell->next)
+ {
+ char *r = relpath;
+ char *v = cell->val;
+
+ while (*v != '\0' && *r == *v)
+ ++r, ++v;
+
+ if (*v == '\0' && (*r == '\0' || *r == '/'))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Helper function for manifest_files hash table.
+ */
+static uint32
+hash_string_pointer(char *s)
+{
+ unsigned char *ss = (unsigned char *) s;
+
+ return hash_bytes(ss, strlen(s));
+}
+
+/*
+ * Print out usage information and exit.
+ */
+static void
+usage(void)
+{
+ printf(_("%s verifies a backup against the backup manifest.\n\n"), progname);
+ printf(_("Usage:\n %s [OPTION]... BACKUPDIR\n\n"), progname);
+ printf(_("Options:\n"));
+ printf(_(" -e, --exit-on-error exit immediately on error\n"));
+ printf(_(" -i, --ignore=RELATIVE_PATH ignore indicated path\n"));
+ printf(_(" -m, --manifest-path=PATH use specified path for manifest\n"));
+ printf(_(" -n, --no-parse-wal do not try to parse WAL files\n"));
+ printf(_(" -q, --quiet do not print any output, except for errors\n"));
+ printf(_(" -s, --skip-checksums skip checksum verification\n"));
+ printf(_(" -w, --wal-directory=PATH use specified path for WAL files\n"));
+ printf(_(" -V, --version output version information, then exit\n"));
+ printf(_(" -?, --help show this help, then exit\n"));
+ printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
+}
diff --git a/src/bin/pg_verifybackup/po/de.po b/src/bin/pg_verifybackup/po/de.po
new file mode 100644
index 0000000..6cf9d8f
--- /dev/null
+++ b/src/bin/pg_verifybackup/po/de.po
@@ -0,0 +1,501 @@
+# German message translation file for pg_verifybackup
+# Copyright (C) 2020-2021 PostgreSQL Global Development Group
+# This file is distributed under the same license as the pg_verifybackup (PostgreSQL) package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_verifybackup (PostgreSQL) 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-04-17 02:45+0000\n"
+"PO-Revision-Date: 2021-04-17 09:54+0200\n"
+"Last-Translator: Peter Eisentraut <peter@eisentraut.org>\n"
+"Language-Team: German <pgsql-translators@postgresql.org>\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "Fatal: "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "Fehler: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "Warnung: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "Speicher aufgebraucht\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "kann NULL-Zeiger nicht kopieren (interner Fehler)\n"
+
+#: ../../common/jsonapi.c:1066
+#, c-format
+msgid "Escape sequence \"\\%s\" is invalid."
+msgstr "Escape-Sequenz »\\%s« ist nicht gültig."
+
+#: ../../common/jsonapi.c:1069
+#, c-format
+msgid "Character with value 0x%02x must be escaped."
+msgstr "Zeichen mit Wert 0x%02x muss escapt werden."
+
+#: ../../common/jsonapi.c:1072
+#, c-format
+msgid "Expected end of input, but found \"%s\"."
+msgstr "Ende der Eingabe erwartet, aber »%s« gefunden."
+
+#: ../../common/jsonapi.c:1075
+#, c-format
+msgid "Expected array element or \"]\", but found \"%s\"."
+msgstr "Array-Element oder »]« erwartet, aber »%s« gefunden."
+
+#: ../../common/jsonapi.c:1078
+#, c-format
+msgid "Expected \",\" or \"]\", but found \"%s\"."
+msgstr "»,« oder »]« erwartet, aber »%s« gefunden."
+
+#: ../../common/jsonapi.c:1081
+#, c-format
+msgid "Expected \":\", but found \"%s\"."
+msgstr "»:« erwartet, aber »%s« gefunden."
+
+#: ../../common/jsonapi.c:1084
+#, c-format
+msgid "Expected JSON value, but found \"%s\"."
+msgstr "JSON-Wert erwartet, aber »%s« gefunden."
+
+#: ../../common/jsonapi.c:1087
+msgid "The input string ended unexpectedly."
+msgstr "Die Eingabezeichenkette endete unerwartet."
+
+#: ../../common/jsonapi.c:1089
+#, c-format
+msgid "Expected string or \"}\", but found \"%s\"."
+msgstr "Zeichenkette oder »}« erwartet, aber »%s« gefunden."
+
+#: ../../common/jsonapi.c:1092
+#, c-format
+msgid "Expected \",\" or \"}\", but found \"%s\"."
+msgstr "»,« oder »}« erwartet, aber »%s« gefunden."
+
+#: ../../common/jsonapi.c:1095
+#, c-format
+msgid "Expected string, but found \"%s\"."
+msgstr "Zeichenkette erwartet, aber »%s« gefunden."
+
+#: ../../common/jsonapi.c:1098
+#, c-format
+msgid "Token \"%s\" is invalid."
+msgstr "Token »%s« ist ungültig."
+
+#: ../../common/jsonapi.c:1101
+msgid "\\u0000 cannot be converted to text."
+msgstr "\\u0000 kann nicht in »text« umgewandelt werden."
+
+#: ../../common/jsonapi.c:1103
+msgid "\"\\u\" must be followed by four hexadecimal digits."
+msgstr "Nach »\\u« müssen vier Hexadezimalziffern folgen."
+
+#: ../../common/jsonapi.c:1106
+msgid "Unicode escape values cannot be used for code point values above 007F when the encoding is not UTF8."
+msgstr "Unicode-Escape-Werte können nicht für Code-Punkt-Werte über 007F verwendet werden, wenn die Kodierung nicht UTF8 ist."
+
+#: ../../common/jsonapi.c:1108
+msgid "Unicode high surrogate must not follow a high surrogate."
+msgstr "Unicode-High-Surrogate darf nicht auf ein High-Surrogate folgen."
+
+#: ../../common/jsonapi.c:1110
+msgid "Unicode low surrogate must follow a high surrogate."
+msgstr "Unicode-Low-Surrogate muss auf ein High-Surrogate folgen."
+
+#: parse_manifest.c:152
+msgid "manifest ended unexpectedly"
+msgstr "Manifest endete unerwartet"
+
+#: parse_manifest.c:191
+msgid "unexpected object start"
+msgstr "unerwarteter Objektstart"
+
+#: parse_manifest.c:224
+msgid "unexpected object end"
+msgstr "unerwartetes Objektende"
+
+#: parse_manifest.c:251
+msgid "unexpected array start"
+msgstr "unerwarteter Array-Start"
+
+#: parse_manifest.c:274
+msgid "unexpected array end"
+msgstr "unerwartetes Array-Ende"
+
+#: parse_manifest.c:299
+msgid "expected version indicator"
+msgstr "unerwartete Versionskennzeichnung"
+
+#: parse_manifest.c:328
+msgid "unrecognized top-level field"
+msgstr "unbekanntes Feld auf oberster Ebene"
+
+#: parse_manifest.c:347
+msgid "unexpected file field"
+msgstr "unerwartetes Feld für Datei"
+
+#: parse_manifest.c:361
+msgid "unexpected WAL range field"
+msgstr "unerwartetes Feld für WAL-Bereich"
+
+#: parse_manifest.c:367
+msgid "unexpected object field"
+msgstr "unbekanntes Feld für Objekt"
+
+#: parse_manifest.c:397
+msgid "unexpected manifest version"
+msgstr "unerwartete Manifestversion"
+
+#: parse_manifest.c:448
+msgid "unexpected scalar"
+msgstr "unerwarteter Skalar"
+
+#: parse_manifest.c:472
+msgid "missing path name"
+msgstr "fehlender Pfadname"
+
+#: parse_manifest.c:475
+msgid "both path name and encoded path name"
+msgstr "sowohl Pfadname als auch kodierter Pfadname angegeben"
+
+#: parse_manifest.c:477
+msgid "missing size"
+msgstr "Größenangabe fehlt"
+
+#: parse_manifest.c:480
+msgid "checksum without algorithm"
+msgstr "Prüfsumme ohne Algorithmus"
+
+#: parse_manifest.c:494
+msgid "could not decode file name"
+msgstr "konnte Dateinamen nicht dekodieren"
+
+#: parse_manifest.c:504
+msgid "file size is not an integer"
+msgstr "Dateigröße ist keine ganze Zahl"
+
+#: parse_manifest.c:510
+#, c-format
+msgid "unrecognized checksum algorithm: \"%s\""
+msgstr "unbekannter Prüfsummenalgorithmus: »%s«"
+
+#: parse_manifest.c:529
+#, c-format
+msgid "invalid checksum for file \"%s\": \"%s\""
+msgstr "ungültige Prüfsumme für Datei »%s«: »%s«"
+
+#: parse_manifest.c:572
+msgid "missing timeline"
+msgstr "Zeitleiste fehlt"
+
+#: parse_manifest.c:574
+msgid "missing start LSN"
+msgstr "Start-LSN fehlt"
+
+#: parse_manifest.c:576
+msgid "missing end LSN"
+msgstr "End-LSN fehlt"
+
+#: parse_manifest.c:582
+msgid "timeline is not an integer"
+msgstr "Zeitleiste ist keine ganze Zahl"
+
+#: parse_manifest.c:585
+msgid "could not parse start LSN"
+msgstr "konnte Start-LSN nicht parsen"
+
+#: parse_manifest.c:588
+msgid "could not parse end LSN"
+msgstr "konnte End-LSN nicht parsen"
+
+#: parse_manifest.c:649
+msgid "expected at least 2 lines"
+msgstr "mindestens 2 Zeilen erwartet"
+
+#: parse_manifest.c:652
+msgid "last line not newline-terminated"
+msgstr "letzte Zeile nicht durch Newline abgeschlossen"
+
+#: parse_manifest.c:657
+#, c-format
+msgid "out of memory"
+msgstr "Speicher aufgebraucht"
+
+#: parse_manifest.c:659
+#, c-format
+msgid "could not initialize checksum of manifest"
+msgstr "konnte Prüfsumme des Manifests nicht initialisieren"
+
+#: parse_manifest.c:661
+#, c-format
+msgid "could not update checksum of manifest"
+msgstr "konnte Prüfsumme des Manifests nicht aktualisieren"
+
+#: parse_manifest.c:664
+#, c-format
+msgid "could not finalize checksum of manifest"
+msgstr "konnte Prüfsumme des Manifests nicht abschließen"
+
+#: parse_manifest.c:668
+#, c-format
+msgid "manifest has no checksum"
+msgstr "Manifest hat keine Prüfsumme"
+
+#: parse_manifest.c:672
+#, c-format
+msgid "invalid manifest checksum: \"%s\""
+msgstr "ungültige Manifestprüfsumme: »%s«"
+
+#: parse_manifest.c:676
+#, c-format
+msgid "manifest checksum mismatch"
+msgstr "Manifestprüfsumme stimmt nicht überein"
+
+#: parse_manifest.c:691
+#, c-format
+msgid "could not parse backup manifest: %s"
+msgstr "konnte Backup-Manifest nicht parsen: %s"
+
+#: pg_verifybackup.c:255 pg_verifybackup.c:265 pg_verifybackup.c:277
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "Versuchen Sie »%s --help« für weitere Informationen.\n"
+
+#: pg_verifybackup.c:264
+#, c-format
+msgid "no backup directory specified"
+msgstr "kein Backup-Verzeichnis angegeben"
+
+#: pg_verifybackup.c:275
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "zu viele Kommandozeilenargumente (das erste ist »%s«)"
+
+#: pg_verifybackup.c:298
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"Das Programm »%s« wird von %s benötigt, aber wurde nicht im\n"
+"selben Verzeichnis wie »%s« gefunden.\n"
+"Prüfen Sie Ihre Installation."
+
+#: pg_verifybackup.c:303
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"Das Programm »%s« wurde von %s gefunden,\n"
+"aber es hatte nicht die gleiche Version wie %s.\n"
+"Prüfen Sie Ihre Installation."
+
+#: pg_verifybackup.c:361
+#, c-format
+msgid "backup successfully verified\n"
+msgstr "Backup erfolgreich überprüft\n"
+
+#: pg_verifybackup.c:387 pg_verifybackup.c:723
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "konnte Datei »%s« nicht öffnen: %m"
+
+#: pg_verifybackup.c:391
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "konnte »stat« für Datei »%s« nicht ausführen: %m"
+
+#: pg_verifybackup.c:411 pg_verifybackup.c:752
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "konnte Datei »%s« nicht lesen: %m"
+
+#: pg_verifybackup.c:414
+#, c-format
+msgid "could not read file \"%s\": read %d of %lld"
+msgstr "konnte Datei »%s« nicht lesen: %d von %lld gelesen"
+
+#: pg_verifybackup.c:474
+#, c-format
+msgid "duplicate path name in backup manifest: \"%s\""
+msgstr "doppelter Pfadname im Backup-Manifest: »%s«"
+
+#: pg_verifybackup.c:537 pg_verifybackup.c:544
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "konnte Verzeichnis »%s« nicht öffnen: %m"
+
+#: pg_verifybackup.c:576
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "konnte Verzeichnis »%s« nicht schließen: %m"
+
+#: pg_verifybackup.c:596
+#, c-format
+msgid "could not stat file or directory \"%s\": %m"
+msgstr "konnte »stat« für Datei oder Verzeichnis »%s« nicht ausführen: %m"
+
+#: pg_verifybackup.c:619
+#, c-format
+msgid "\"%s\" is not a file or directory"
+msgstr "»%s« ist keine Datei und kein Verzeichnis"
+
+#: pg_verifybackup.c:629
+#, c-format
+msgid "\"%s\" is present on disk but not in the manifest"
+msgstr "»%s« ist auf der Festplatte vorhanden, aber nicht im Manifest"
+
+#: pg_verifybackup.c:641
+#, c-format
+msgid "\"%s\" has size %lld on disk but size %zu in the manifest"
+msgstr "»%s« hat Größe %lld auf Festplatte aber Größe %zu im Manifest"
+
+#: pg_verifybackup.c:668
+#, c-format
+msgid "\"%s\" is present in the manifest but not on disk"
+msgstr "»%s« steht im Manifest, ist aber nicht auf der Festplatte vorhanden"
+
+#: pg_verifybackup.c:731
+#, c-format
+msgid "could not initialize checksum of file \"%s\""
+msgstr "konnte Prüfsumme der Datei »%s« nicht initialisieren"
+
+#: pg_verifybackup.c:743
+#, c-format
+msgid "could not update checksum of file \"%s\""
+msgstr "konnte Prüfsumme der Datei »%s« nicht aktualisieren"
+
+#: pg_verifybackup.c:758
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "konnte Datei »%s« nicht schließen: %m"
+
+#: pg_verifybackup.c:777
+#, c-format
+msgid "file \"%s\" should contain %zu bytes, but read %zu bytes"
+msgstr "Datei »%s« sollte %zu Bytes enthalten, aber %zu Bytes wurden gelesen"
+
+#: pg_verifybackup.c:787
+#, c-format
+msgid "could not finalize checksum of file \"%s\""
+msgstr "konnte Prüfsumme der Datei »%s« nicht abschließen"
+
+#: pg_verifybackup.c:795
+#, c-format
+msgid "file \"%s\" has checksum of length %d, but expected %d"
+msgstr "Datei »%s« hat Prüfsumme mit Länge %d, aber %d wurde erwartet"
+
+#: pg_verifybackup.c:799
+#, c-format
+msgid "checksum mismatch for file \"%s\""
+msgstr "Prüfsumme stimmt nicht überein für Datei »%s«"
+
+#: pg_verifybackup.c:823
+#, c-format
+msgid "WAL parsing failed for timeline %u"
+msgstr "Parsen des WAL fehlgeschlagen für Zeitleiste %u"
+
+#: pg_verifybackup.c:909
+#, c-format
+msgid ""
+"%s verifies a backup against the backup manifest.\n"
+"\n"
+msgstr ""
+"%s überprüft ein Backup anhand eines Backup-Manifests.\n"
+"\n"
+
+#: pg_verifybackup.c:910
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]... BACKUPDIR\n"
+"\n"
+msgstr ""
+"Aufruf:\n"
+" %s [OPTION]... BACKUPVERZ\n"
+"\n"
+
+#: pg_verifybackup.c:911
+#, c-format
+msgid "Options:\n"
+msgstr "Optionen:\n"
+
+#: pg_verifybackup.c:912
+#, c-format
+msgid " -e, --exit-on-error exit immediately on error\n"
+msgstr " -e, --exit-on-error bei Fehler sofort beenden\n"
+
+#: pg_verifybackup.c:913
+#, c-format
+msgid " -i, --ignore=RELATIVE_PATH ignore indicated path\n"
+msgstr " -i, --ignore=REL-PFAD angegebenen Pfad ignorieren\n"
+
+#: pg_verifybackup.c:914
+#, c-format
+msgid " -m, --manifest-path=PATH use specified path for manifest\n"
+msgstr " -m, --manifest-path=PFAD angegebenen Pfad für Manifest verwenden\n"
+
+#: pg_verifybackup.c:915
+#, c-format
+msgid " -n, --no-parse-wal do not try to parse WAL files\n"
+msgstr " -n, --no-parse-wal nicht versuchen WAL-Dateien zu parsen\n"
+
+#: pg_verifybackup.c:916
+#, c-format
+msgid " -q, --quiet do not print any output, except for errors\n"
+msgstr " -q, --quiet keine Ausgabe, außer Fehler\n"
+
+#: pg_verifybackup.c:917
+#, c-format
+msgid " -s, --skip-checksums skip checksum verification\n"
+msgstr " -s, --skip-checksums Überprüfung der Prüfsummen überspringen\n"
+
+#: pg_verifybackup.c:918
+#, c-format
+msgid " -w, --wal-directory=PATH use specified path for WAL files\n"
+msgstr " -w, --wal-directory=PFAD angegebenen Pfad für WAL-Dateien verwenden\n"
+
+#: pg_verifybackup.c:919
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version Versionsinformationen anzeigen, dann beenden\n"
+
+#: pg_verifybackup.c:920
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help diese Hilfe anzeigen, dann beenden\n"
+
+#: pg_verifybackup.c:921
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"Berichten Sie Fehler an <%s>.\n"
+
+#: pg_verifybackup.c:922
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "%s Homepage: <%s>\n"
diff --git a/src/bin/pg_verifybackup/po/el.po b/src/bin/pg_verifybackup/po/el.po
new file mode 100644
index 0000000..1869623
--- /dev/null
+++ b/src/bin/pg_verifybackup/po/el.po
@@ -0,0 +1,505 @@
+# Greek message translation file for pg_verifybackup
+# Copyright (C) 2021 PostgreSQL Global Development Group
+# This file is distributed under the same license as the pg_verifybackup (PostgreSQL) package.
+# Georgios Kokolatos <gkokolatos@pm.me>, 2021.
+#
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_verifybackup (PostgreSQL) 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-07-14 08:14+0000\n"
+"PO-Revision-Date: 2021-06-04 09:16+0200\n"
+"Last-Translator: Georgios Kokolatos <gkokolatos@pm.me>\n"
+"Language-Team: \n"
+"Language: el\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.4.3\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "κρίσιμο: "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "σφάλμα: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "προειδοποίηση: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "έλλειψη μνήμης\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "δεν ήταν δυνατή η αντιγραφή δείκτη null (εσωτερικό σφάλμα)\n"
+
+#: ../../common/jsonapi.c:1066
+#, c-format
+msgid "Escape sequence \"\\%s\" is invalid."
+msgstr "Η ακολουθία διαφυγής «\\%s» δεν είναι έγκυρη."
+
+#: ../../common/jsonapi.c:1069
+#, c-format
+msgid "Character with value 0x%02x must be escaped."
+msgstr "Ο χαρακτήρας με τιμή 0x%02x πρέπει να διαφύγει."
+
+#: ../../common/jsonapi.c:1072
+#, c-format
+msgid "Expected end of input, but found \"%s\"."
+msgstr "Ανέμενε τέλος εισόδου, αλλά βρήκε «%s»."
+
+#: ../../common/jsonapi.c:1075
+#, c-format
+msgid "Expected array element or \"]\", but found \"%s\"."
+msgstr "Ανέμενε στοιχείο συστυχίας ή «]», αλλά βρέθηκε «%s»."
+
+#: ../../common/jsonapi.c:1078
+#, c-format
+msgid "Expected \",\" or \"]\", but found \"%s\"."
+msgstr "Ανέμενε «,» ή «]», αλλά βρήκε «%s»."
+
+#: ../../common/jsonapi.c:1081
+#, c-format
+msgid "Expected \":\", but found \"%s\"."
+msgstr "Ανέμενε «:», αλλά βρήκε «%s»."
+
+#: ../../common/jsonapi.c:1084
+#, c-format
+msgid "Expected JSON value, but found \"%s\"."
+msgstr "Ανέμενε τιμή JSON, αλλά βρήκε «%s»."
+
+#: ../../common/jsonapi.c:1087
+msgid "The input string ended unexpectedly."
+msgstr "Η συμβολοσειρά εισόδου τερματίστηκε αναπάντεχα."
+
+#: ../../common/jsonapi.c:1089
+#, c-format
+msgid "Expected string or \"}\", but found \"%s\"."
+msgstr "Ανέμενε συμβολοσειρά ή «}», αλλά βρήκε «%s»."
+
+#: ../../common/jsonapi.c:1092
+#, c-format
+msgid "Expected \",\" or \"}\", but found \"%s\"."
+msgstr "Ανέμενε «,» ή «}», αλλά βρήκε «%s»."
+
+#: ../../common/jsonapi.c:1095
+#, c-format
+msgid "Expected string, but found \"%s\"."
+msgstr "Ανέμενε συμβολοσειρά, αλλά βρήκε «%s»."
+
+#: ../../common/jsonapi.c:1098
+#, c-format
+msgid "Token \"%s\" is invalid."
+msgstr "Το διακριτικό «%s» δεν είναι έγκυρο."
+
+#: ../../common/jsonapi.c:1101
+msgid "\\u0000 cannot be converted to text."
+msgstr "Δεν είναι δυνατή η μετατροπή του \\u0000 σε κείμενο."
+
+#: ../../common/jsonapi.c:1103
+msgid "\"\\u\" must be followed by four hexadecimal digits."
+msgstr "Το «\\u» πρέπει να ακολουθείται από τέσσερα δεκαεξαδικά ψηφία."
+
+#: ../../common/jsonapi.c:1106
+msgid "Unicode escape values cannot be used for code point values above 007F when the encoding is not UTF8."
+msgstr "Δεν μπορούν να χρησιμοποιηθούν τιμές διαφυγής Unicode για τιμές σημείου κώδικα άνω του 007F όταν η κωδικοποίηση δεν είναι UTF8."
+
+#: ../../common/jsonapi.c:1108
+msgid "Unicode high surrogate must not follow a high surrogate."
+msgstr "Υψηλό διακριτικό Unicode δεν πρέπει να ακολουθεί υψηλό διακριτικό."
+
+#: ../../common/jsonapi.c:1110
+msgid "Unicode low surrogate must follow a high surrogate."
+msgstr "Χαμηλό διακριτικό Unicode πρέπει να ακολουθεί υψηλό διακριτικό."
+
+#: parse_manifest.c:152
+msgid "manifest ended unexpectedly"
+msgstr "η διακήρυξη έληξε απροσδόκητα"
+
+#: parse_manifest.c:191
+msgid "unexpected object start"
+msgstr "μη αναμενόμενη αρχή αντικειμένου"
+
+#: parse_manifest.c:224
+msgid "unexpected object end"
+msgstr "μη αναμενόμενο τέλος αντικειμένου"
+
+#: parse_manifest.c:251
+msgid "unexpected array start"
+msgstr "μη αναμενόμενη αρχή συστοιχίας"
+
+#: parse_manifest.c:274
+msgid "unexpected array end"
+msgstr "μη αναμενόμενο τέλος συστοιχίας"
+
+#: parse_manifest.c:299
+msgid "expected version indicator"
+msgstr "ανέμενε ένδειξη έκδοσης"
+
+#: parse_manifest.c:328
+msgid "unrecognized top-level field"
+msgstr "μη αναγνωρίσιμο πεδίο ανώτατου επιπέδου"
+
+#: parse_manifest.c:347
+msgid "unexpected file field"
+msgstr "μη αναμενόμενο πεδίο αρχείου"
+
+#: parse_manifest.c:361
+msgid "unexpected WAL range field"
+msgstr "μη αναμενόμενο πεδίο περιοχής WAL"
+
+#: parse_manifest.c:367
+msgid "unexpected object field"
+msgstr "μη αναμενόμενο πεδίο αντικειμένου"
+
+#: parse_manifest.c:397
+msgid "unexpected manifest version"
+msgstr "μη αναμενόμενη έκδοση διακήρυξης"
+
+#: parse_manifest.c:448
+msgid "unexpected scalar"
+msgstr "μη αναμενόμενο scalar"
+
+#: parse_manifest.c:472
+msgid "missing path name"
+msgstr "λείπει όνομα διαδρομής"
+
+#: parse_manifest.c:475
+msgid "both path name and encoded path name"
+msgstr "και όνομα διαδρομής και κωδικοποιημένο όνομα διαδρομής"
+
+#: parse_manifest.c:477
+msgid "missing size"
+msgstr "λείπει το μέγεθος"
+
+#: parse_manifest.c:480
+msgid "checksum without algorithm"
+msgstr "άθροισμα ελέγχου χωρίς αλγόριθμο"
+
+#: parse_manifest.c:494
+msgid "could not decode file name"
+msgstr "δεν ήταν δυνατή η αποκωδικοποίηση του ονόματος αρχείου"
+
+#: parse_manifest.c:504
+msgid "file size is not an integer"
+msgstr "το μέγεθος αρχείου δεν είναι ακέραιος"
+
+#: parse_manifest.c:510
+#, c-format
+msgid "unrecognized checksum algorithm: \"%s\""
+msgstr "μη αναγνωρίσιμος αλγόριθμος αθροίσματος ελέγχου: «%s»"
+
+#: parse_manifest.c:529
+#, c-format
+msgid "invalid checksum for file \"%s\": \"%s\""
+msgstr "μη έγκυρο άθροισμα ελέγχου για το αρχείο «%s»: «%s»"
+
+#: parse_manifest.c:572
+msgid "missing timeline"
+msgstr "λείπει η χρονογραμμή"
+
+#: parse_manifest.c:574
+msgid "missing start LSN"
+msgstr "λείπει αρχικό LSN"
+
+#: parse_manifest.c:576
+msgid "missing end LSN"
+msgstr "λείπει τελικό LSN"
+
+#: parse_manifest.c:582
+msgid "timeline is not an integer"
+msgstr "η χρονογραμμή δεν είναι ακέραιος"
+
+#: parse_manifest.c:585
+msgid "could not parse start LSN"
+msgstr "δεν ήταν δυνατή η ανάλυση του αρχικού LSN"
+
+#: parse_manifest.c:588
+msgid "could not parse end LSN"
+msgstr "δεν ήταν δυνατή η ανάλυση του τελικού LSN"
+
+#: parse_manifest.c:649
+msgid "expected at least 2 lines"
+msgstr "αναμένονταν τουλάχιστον 2 γραμμές"
+
+#: parse_manifest.c:652
+msgid "last line not newline-terminated"
+msgstr "η τελευταία γραμμή δεν τερματίστηκε με newline"
+
+#: parse_manifest.c:657
+#, c-format
+msgid "out of memory"
+msgstr "έλλειψη μνήμης"
+
+#: parse_manifest.c:659
+#, c-format
+msgid "could not initialize checksum of manifest"
+msgstr "δεν ήταν δυνατή η αρχικοποίηση του αθροίσματος ελέγχου της διακήρυξης"
+
+#: parse_manifest.c:661
+#, c-format
+msgid "could not update checksum of manifest"
+msgstr "δεν ήταν δυνατή η ενημέρωση του αθροίσματος ελέγχου της διακήρυξης"
+
+#: parse_manifest.c:664
+#, c-format
+msgid "could not finalize checksum of manifest"
+msgstr "δεν ήταν δυνατή η ολοκλήρωση του αθροίσματος ελέγχου της διακήρυξης"
+
+#: parse_manifest.c:668
+#, c-format
+msgid "manifest has no checksum"
+msgstr "η διακήρυξη δεν έχει άθροισμα ελέγχου"
+
+#: parse_manifest.c:672
+#, c-format
+msgid "invalid manifest checksum: \"%s\""
+msgstr "μη έγκυρο άθροισμα ελέγχου διακήρυξης: «%s»"
+
+#: parse_manifest.c:676
+#, c-format
+msgid "manifest checksum mismatch"
+msgstr "αναντιστοιχία ελέγχου αθροίσματος διακήρυξης"
+
+#: parse_manifest.c:691
+#, c-format
+msgid "could not parse backup manifest: %s"
+msgstr "δεν ήταν δυνατή η ανάλυση του αντιγράφου ασφαλείας της διακήρυξης: %s"
+
+#: pg_verifybackup.c:255 pg_verifybackup.c:265 pg_verifybackup.c:277
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "Δοκιμάστε «%s --help» για περισσότερες πληροφορίες.\n"
+
+#: pg_verifybackup.c:264
+#, c-format
+msgid "no backup directory specified"
+msgstr "δεν ορίστηκε κατάλογος αντιγράφου ασφαλείας"
+
+#: pg_verifybackup.c:275
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "πάρα πολλές παράμετροι εισόδου από την γραμμή εντολών (η πρώτη είναι η «%s»)"
+
+#: pg_verifybackup.c:298
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"Το πρόγραμμα «%s» απαιτείται από %s αλλά δεν βρέθηκε στον\n"
+"ίδιο κατάλογο με το «%s».\n"
+"Ελέγξτε την εγκατάστασή σας."
+
+#: pg_verifybackup.c:303
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"Το πρόγραμμα «%s» βρέθηκε από το \"%s\"\n"
+"αλλά δεν ήταν η ίδια εκδοχή με %s.\n"
+"Ελέγξτε την εγκατάστασή σας."
+
+#: pg_verifybackup.c:361
+#, c-format
+msgid "backup successfully verified\n"
+msgstr "το αντίγραφο ασφαλείας επαληθεύτηκε με επιτυχία\n"
+
+#: pg_verifybackup.c:387 pg_verifybackup.c:723
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "δεν ήταν δυνατό το άνοιγμα του αρχείου «%s»: %m"
+
+#: pg_verifybackup.c:391
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "δεν ήταν δυνατή η εκτέλεση stat στο αρχείο «%s»: %m"
+
+#: pg_verifybackup.c:411 pg_verifybackup.c:752
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "δεν ήταν δυνατή η ανάγνωση του αρχείου «%s»: %m"
+
+#: pg_verifybackup.c:414
+#, c-format
+msgid "could not read file \"%s\": read %d of %lld"
+msgstr "δεν ήταν δυνατή η ανάγνωση του αρχείου «%s»: ανέγνωσε %d από %lld"
+
+#: pg_verifybackup.c:474
+#, c-format
+msgid "duplicate path name in backup manifest: \"%s\""
+msgstr "διπλότυπο όνομα διαδρομής στη διακήρυξη αντιγράφου ασφαλείας: «%s»"
+
+#: pg_verifybackup.c:537 pg_verifybackup.c:544
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "δεν ήταν δυνατό το άνοιγμα του καταλόγου «%s»: %m"
+
+#: pg_verifybackup.c:576
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "δεν ήταν δυνατό το κλείσιμο του καταλόγου «%s»: %m"
+
+#: pg_verifybackup.c:596
+#, c-format
+msgid "could not stat file or directory \"%s\": %m"
+msgstr "δεν ήταν δυνατή η εκτέλεση stat στο αρχείο ή κατάλογο «%s»: %m"
+
+#: pg_verifybackup.c:619
+#, c-format
+msgid "\"%s\" is not a file or directory"
+msgstr "«%s» δεν είναι αρχείο ή κατάλογος"
+
+#: pg_verifybackup.c:629
+#, c-format
+msgid "\"%s\" is present on disk but not in the manifest"
+msgstr "«%s» βρίσκεται στο δίσκο, αλλά όχι στη διακήρυξη"
+
+#: pg_verifybackup.c:641
+#, c-format
+msgid "\"%s\" has size %lld on disk but size %zu in the manifest"
+msgstr "«%s» έχει μέγεθος %lld στο δίσκο, αλλά μέγεθος %zu στη διακήρυξη"
+
+#: pg_verifybackup.c:668
+#, c-format
+msgid "\"%s\" is present in the manifest but not on disk"
+msgstr "«%s» βρίσκεται στη διακήρυξη αλλά όχι στο δίσκο"
+
+#: pg_verifybackup.c:731
+#, c-format
+msgid "could not initialize checksum of file \"%s\""
+msgstr "δεν ήταν δυνατή η αρχικοποίηση του αθροίσματος ελέγχου του αρχείου «%s»"
+
+#: pg_verifybackup.c:743
+#, c-format
+msgid "could not update checksum of file \"%s\""
+msgstr "δεν ήταν δυνατή η ενημέρωση αθροίσματος ελέγχου του αρχείου «%s»"
+
+#: pg_verifybackup.c:758
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "δεν ήταν δυνατό το κλείσιμο του αρχείου «%s»: %m"
+
+#: pg_verifybackup.c:777
+#, c-format
+msgid "file \"%s\" should contain %zu bytes, but read %zu bytes"
+msgstr "το αρχείο «%s» έπρεπε να περιέχει %zu bytes, αλλά να αναγνώστηκαν %zu bytes"
+
+#: pg_verifybackup.c:787
+#, c-format
+msgid "could not finalize checksum of file \"%s\""
+msgstr "δεν ήταν δυνατή η ολοκλήρωση του αθροίσματος ελέγχου του αρχείου «%s»"
+
+#: pg_verifybackup.c:795
+#, c-format
+msgid "file \"%s\" has checksum of length %d, but expected %d"
+msgstr "το αρχείο «%s» έχει άθροισμα ελέγχου μήκους %d, αλλά αναμένεται %d"
+
+#: pg_verifybackup.c:799
+#, c-format
+msgid "checksum mismatch for file \"%s\""
+msgstr "αναντιστοιχία αθροίσματος ελέγχου για το αρχείο «%s»"
+
+#: pg_verifybackup.c:823
+#, c-format
+msgid "WAL parsing failed for timeline %u"
+msgstr "απέτυχε η ανάλυση WAL για την χρονογραμμή %u"
+
+#: pg_verifybackup.c:909
+#, c-format
+msgid ""
+"%s verifies a backup against the backup manifest.\n"
+"\n"
+msgstr ""
+"%s επαληθεύει ένα αντίγραφο ασφαλείας έναντι της διακήρυξης αντιγράφων ασφαλείας.\n"
+"\n"
+
+#: pg_verifybackup.c:910
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]... BACKUPDIR\n"
+"\n"
+msgstr ""
+"Χρήση:\n"
+" %s [ΕΠΙΛΟΓΗ]... BACKUPDIR\n"
+"\n"
+
+#: pg_verifybackup.c:911
+#, c-format
+msgid "Options:\n"
+msgstr "Επιλογές:\n"
+
+#: pg_verifybackup.c:912
+#, c-format
+msgid " -e, --exit-on-error exit immediately on error\n"
+msgstr " -e, --exit-on-error να εξέλθει άμεσα σε σφάλμα\n"
+
+#: pg_verifybackup.c:913
+#, c-format
+msgid " -i, --ignore=RELATIVE_PATH ignore indicated path\n"
+msgstr " -i, --ignore=RELATIVE_PATH αγνόησε την υποδεικνυόμενη διαδρομή\n"
+
+#: pg_verifybackup.c:914
+#, c-format
+msgid " -m, --manifest-path=PATH use specified path for manifest\n"
+msgstr " -m, --manifest-path=PATH χρησιμοποίησε την καθορισμένη διαδρομή για την διακήρυξη\n"
+
+#: pg_verifybackup.c:915
+#, c-format
+msgid " -n, --no-parse-wal do not try to parse WAL files\n"
+msgstr " -n, --no-parse-wal μην δοκιμάσεις να αναλύσεις αρχεία WAL\n"
+
+#: pg_verifybackup.c:916
+#, c-format
+msgid " -q, --quiet do not print any output, except for errors\n"
+msgstr " -q, --quiet να μην εκτυπώσεις καμία έξοδο, εκτός από σφάλματα\n"
+
+#: pg_verifybackup.c:917
+#, c-format
+msgid " -s, --skip-checksums skip checksum verification\n"
+msgstr " -s, --skip-checksums παράκαμψε την επαλήθευση αθροισμάτων ελέγχου\n"
+
+#: pg_verifybackup.c:918
+#, c-format
+msgid " -w, --wal-directory=PATH use specified path for WAL files\n"
+msgstr " -w, --wal-directory=PATH χρησιμοποίησε την καθορισμένη διαδρομή για αρχεία WAL\n"
+
+#: pg_verifybackup.c:919
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version εμφάνισε πληροφορίες έκδοσης, στη συνέχεια έξοδος\n"
+
+#: pg_verifybackup.c:920
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help εμφάνισε αυτό το μήνυμα βοήθειας, στη συνέχεια έξοδος\n"
+
+#: pg_verifybackup.c:921
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"Υποβάλετε αναφορές σφάλματων σε <%s>.\n"
+
+#: pg_verifybackup.c:922
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "%s αρχική σελίδα: <%s>\n"
diff --git a/src/bin/pg_verifybackup/po/es.po b/src/bin/pg_verifybackup/po/es.po
new file mode 100644
index 0000000..c234c09
--- /dev/null
+++ b/src/bin/pg_verifybackup/po/es.po
@@ -0,0 +1,505 @@
+# Spanish message translation file for pg_verifybackup
+# Copyright (C) 2020-2021 PostgreSQL Global Development Group
+# This file is distributed under the same license as the pg_verifybackup (PostgreSQL) package.
+# Álvaro Herrera <alvherre@alvh.no-ip.org>, 2020.
+# Carlos Chapi <carloswaldo@babelruins.org>, 2021.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_verifybackup (PostgreSQL) 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-08-07 20:32+0000\n"
+"PO-Revision-Date: 2021-05-24 16:53-0500\n"
+"Last-Translator: Carlos Chapi <carloswaldo@babelruins.org>\n"
+"Language-Team: PgSQL-es-ayuda <pgsql-es-ayuda@lists.postgresql.org>\n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.4.3\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "fatal: "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "error: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "precaución: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "memoria agotada\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "no se puede duplicar un puntero nulo (error interno)\n"
+
+#: ../../common/jsonapi.c:1066
+#, c-format
+msgid "Escape sequence \"\\%s\" is invalid."
+msgstr "La secuencia de escape «%s» no es válida."
+
+#: ../../common/jsonapi.c:1069
+#, c-format
+msgid "Character with value 0x%02x must be escaped."
+msgstr "Los caracteres con valor 0x%02x deben ser escapados."
+
+#: ../../common/jsonapi.c:1072
+#, c-format
+msgid "Expected end of input, but found \"%s\"."
+msgstr "Se esperaba el fin de la entrada, se encontró «%s»."
+
+#: ../../common/jsonapi.c:1075
+#, c-format
+msgid "Expected array element or \"]\", but found \"%s\"."
+msgstr "Se esperaba un elemento de array o «]», se encontró «%s»."
+
+#: ../../common/jsonapi.c:1078
+#, c-format
+msgid "Expected \",\" or \"]\", but found \"%s\"."
+msgstr "Se esperaba «,» o «]», se encontró «%s»."
+
+#: ../../common/jsonapi.c:1081
+#, c-format
+msgid "Expected \":\", but found \"%s\"."
+msgstr "Se esperaba «:», se encontró «%s»."
+
+#: ../../common/jsonapi.c:1084
+#, c-format
+msgid "Expected JSON value, but found \"%s\"."
+msgstr "Se esperaba un valor JSON, se encontró «%s»."
+
+#: ../../common/jsonapi.c:1087
+msgid "The input string ended unexpectedly."
+msgstr "La cadena de entrada terminó inesperadamente."
+
+#: ../../common/jsonapi.c:1089
+#, c-format
+msgid "Expected string or \"}\", but found \"%s\"."
+msgstr "Se esperaba una cadena o «}», se encontró «%s»."
+
+#: ../../common/jsonapi.c:1092
+#, c-format
+msgid "Expected \",\" or \"}\", but found \"%s\"."
+msgstr "Se esperaba «,» o «}», se encontró «%s»."
+
+#: ../../common/jsonapi.c:1095
+#, c-format
+msgid "Expected string, but found \"%s\"."
+msgstr "Se esperaba una cadena, se encontró «%s»."
+
+#: ../../common/jsonapi.c:1098
+#, c-format
+msgid "Token \"%s\" is invalid."
+msgstr "El elemento «%s» no es válido."
+
+#: ../../common/jsonapi.c:1101
+msgid "\\u0000 cannot be converted to text."
+msgstr "\\u0000 no puede ser convertido a text."
+
+#: ../../common/jsonapi.c:1103
+msgid "\"\\u\" must be followed by four hexadecimal digits."
+msgstr "«\\u» debe ser seguido por cuatro dígitos hexadecimales."
+
+#: ../../common/jsonapi.c:1106
+msgid "Unicode escape values cannot be used for code point values above 007F when the encoding is not UTF8."
+msgstr "Los valores de escape Unicode no se pueden utilizar para valores de código superiores a 007F cuando la codificación no es UTF8."
+
+#: ../../common/jsonapi.c:1108
+msgid "Unicode high surrogate must not follow a high surrogate."
+msgstr "Un «high-surrogate» Unicode no puede venir después de un «high-surrogate»."
+
+#: ../../common/jsonapi.c:1110
+msgid "Unicode low surrogate must follow a high surrogate."
+msgstr "Un «low-surrogate» Unicode debe seguir a un «high-surrogate»."
+
+#: parse_manifest.c:152
+msgid "manifest ended unexpectedly"
+msgstr "el manifiesto terminó inesperadamente"
+
+#: parse_manifest.c:191
+msgid "unexpected object start"
+msgstr "inicio de objeto inesperado"
+
+#: parse_manifest.c:224
+msgid "unexpected object end"
+msgstr "fin de objeto inesperado"
+
+#: parse_manifest.c:251
+msgid "unexpected array start"
+msgstr "inicio de array inesperado"
+
+#: parse_manifest.c:274
+msgid "unexpected array end"
+msgstr "fin de array inesperado"
+
+#: parse_manifest.c:299
+msgid "expected version indicator"
+msgstr "se esperaba indicador de versión"
+
+#: parse_manifest.c:328
+msgid "unrecognized top-level field"
+msgstr "campo de nivel superior no reconocido"
+
+#: parse_manifest.c:347
+msgid "unexpected file field"
+msgstr "campo de archivo inesperado"
+
+#: parse_manifest.c:361
+msgid "unexpected WAL range field"
+msgstr "campo de rango de WAL inesperado"
+
+#: parse_manifest.c:367
+msgid "unexpected object field"
+msgstr "campo de objeto inesperado"
+
+#: parse_manifest.c:397
+msgid "unexpected manifest version"
+msgstr "versión de manifiesto inesperada"
+
+#: parse_manifest.c:448
+msgid "unexpected scalar"
+msgstr "escalar inesperado"
+
+#: parse_manifest.c:472
+msgid "missing path name"
+msgstr "ruta de archivo faltante"
+
+#: parse_manifest.c:475
+msgid "both path name and encoded path name"
+msgstr "hay ambos ruta de archivo (path name) y ruta codificada (encoded path name)"
+
+#: parse_manifest.c:477
+msgid "missing size"
+msgstr "tamaño faltante"
+
+#: parse_manifest.c:480
+msgid "checksum without algorithm"
+msgstr "suma de comprobación sin algoritmo"
+
+#: parse_manifest.c:494
+msgid "could not decode file name"
+msgstr "no se pudo decodificar el nombre del archivo"
+
+#: parse_manifest.c:504
+msgid "file size is not an integer"
+msgstr "el tamaño del archivo no es un número entero"
+
+#: parse_manifest.c:510
+#, c-format
+msgid "unrecognized checksum algorithm: \"%s\""
+msgstr "algoritmo de suma de comprobación no reconocido: \"%s\""
+
+#: parse_manifest.c:529
+#, c-format
+msgid "invalid checksum for file \"%s\": \"%s\""
+msgstr "suma de comprobación no válida para el archivo \"%s\": \"%s\""
+
+#: parse_manifest.c:572
+msgid "missing timeline"
+msgstr "falta el timeline"
+
+#: parse_manifest.c:574
+msgid "missing start LSN"
+msgstr "falta el LSN de inicio"
+
+#: parse_manifest.c:576
+msgid "missing end LSN"
+msgstr "falta el LSN de término"
+
+#: parse_manifest.c:582
+msgid "timeline is not an integer"
+msgstr "el timeline no es un número entero"
+
+#: parse_manifest.c:585
+msgid "could not parse start LSN"
+msgstr "no se pudo interpretar el LSN de inicio"
+
+#: parse_manifest.c:588
+msgid "could not parse end LSN"
+msgstr "no se pudo interpretar el LSN de término"
+
+#: parse_manifest.c:649
+msgid "expected at least 2 lines"
+msgstr "esperado al menos 2 líneas"
+
+#: parse_manifest.c:652
+msgid "last line not newline-terminated"
+msgstr "última línea no termina en nueva línea"
+
+#: parse_manifest.c:657
+#, c-format
+msgid "out of memory"
+msgstr "memoria agotada"
+
+#: parse_manifest.c:659
+#, c-format
+msgid "could not initialize checksum of manifest"
+msgstr "no se pudo inicializar la suma de verificación del manifiesto"
+
+#: parse_manifest.c:661
+#, c-format
+msgid "could not update checksum of manifest"
+msgstr "no se pudo actualizar la suma de verificación del manifiesto"
+
+#: parse_manifest.c:664
+#, c-format
+msgid "could not finalize checksum of manifest"
+msgstr "no se pudo finalizar la suma de verificación del manifiesto"
+
+#: parse_manifest.c:668
+#, c-format
+msgid "manifest has no checksum"
+msgstr "el manifiesto no tiene suma de comprobación"
+
+#: parse_manifest.c:672
+#, c-format
+msgid "invalid manifest checksum: \"%s\""
+msgstr "suma de comprobación de manifiesto no válida: \"%s\""
+
+#: parse_manifest.c:676
+#, c-format
+msgid "manifest checksum mismatch"
+msgstr "discordancia en la suma de comprobación del manifiesto"
+
+#: parse_manifest.c:691
+#, c-format
+msgid "could not parse backup manifest: %s"
+msgstr "no se pudo analizar el manifiesto de la copia de seguridad: %s"
+
+#: pg_verifybackup.c:255 pg_verifybackup.c:265 pg_verifybackup.c:277
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "Pruebe «%s --help» para mayor información.\n"
+
+#: pg_verifybackup.c:264
+#, c-format
+msgid "no backup directory specified"
+msgstr "no fue especificado el directorio de respaldo"
+
+#: pg_verifybackup.c:275
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "demasiados argumentos en la línea de órdenes (el primero es «%s»)"
+
+#: pg_verifybackup.c:298
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"%s necesita el programa «%s», pero no pudo encontrarlo en el mismo\n"
+"directorio que «%s».\n"
+"Verifique su instalación."
+
+#: pg_verifybackup.c:303
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"El programa «%s» fue encontrado por «%s»,\n"
+"pero no es de la misma versión que %s.\n"
+"Verifique su instalación."
+
+#: pg_verifybackup.c:362
+#, c-format
+msgid "backup successfully verified\n"
+msgstr "copia de seguridad verificada correctamente\n"
+
+#: pg_verifybackup.c:388 pg_verifybackup.c:724
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "no se pudo abrir el archivo «%s»: %m"
+
+#: pg_verifybackup.c:392
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "no se pudo hacer stat al archivo «%s»: %m"
+
+#: pg_verifybackup.c:412 pg_verifybackup.c:753
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "no se pudo leer el archivo «%s»: %m"
+
+#: pg_verifybackup.c:415
+#, c-format
+msgid "could not read file \"%s\": read %d of %lld"
+msgstr "no se pudo leer el archivo «%s»: leídos %d de %lld"
+
+#: pg_verifybackup.c:475
+#, c-format
+msgid "duplicate path name in backup manifest: \"%s\""
+msgstr "nombre de ruta duplicado en el manifiesto de la copia de seguridad: \"%s\""
+
+#: pg_verifybackup.c:538 pg_verifybackup.c:545
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "no se pudo abrir el directorio «%s»: %m"
+
+#: pg_verifybackup.c:577
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "no se pudo abrir el directorio «%s»: %m"
+
+#: pg_verifybackup.c:597
+#, c-format
+msgid "could not stat file or directory \"%s\": %m"
+msgstr "no se pudo hacer stat al archivo o directorio «%s»: %m"
+
+#: pg_verifybackup.c:620
+#, c-format
+msgid "\"%s\" is not a file or directory"
+msgstr "\"%s\" no es un archivo o directorio"
+
+#: pg_verifybackup.c:630
+#, c-format
+msgid "\"%s\" is present on disk but not in the manifest"
+msgstr "\"%s\" está presente en el disco pero no en el manifiesto"
+
+#: pg_verifybackup.c:642
+#, c-format
+msgid "\"%s\" has size %lld on disk but size %zu in the manifest"
+msgstr "\"%s\" tiene un tamaño %lld en el disco pero un tamaño %zu en el manifiesto"
+
+#: pg_verifybackup.c:669
+#, c-format
+msgid "\"%s\" is present in the manifest but not on disk"
+msgstr "\"%s\" está presente en el manifiesto pero no en el disco"
+
+#: pg_verifybackup.c:732
+#, c-format
+msgid "could not initialize checksum of file \"%s\""
+msgstr "no se pudo inicializar la suma de verificación para el archivo «%s»"
+
+#: pg_verifybackup.c:744
+#, c-format
+msgid "could not update checksum of file \"%s\""
+msgstr "no se pudo actualizar la suma de verificación para el archivo «%s»"
+
+#: pg_verifybackup.c:759
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "no se pudo cerrar el archivo «%s»: %m"
+
+#: pg_verifybackup.c:778
+#, c-format
+msgid "file \"%s\" should contain %zu bytes, but read %zu bytes"
+msgstr "el archivo \"%s\" debe contener %zu bytes, pero se leyeron %zu bytes"
+
+#: pg_verifybackup.c:788
+#, c-format
+msgid "could not finalize checksum of file \"%s\""
+msgstr "no se pudo finalizar la suma de verificación para el archivo «%s»"
+
+#: pg_verifybackup.c:796
+#, c-format
+msgid "file \"%s\" has checksum of length %d, but expected %d"
+msgstr "el archivo \"%s\" tiene una suma de comprobación de longitud %d, pero se esperaba %d"
+
+#: pg_verifybackup.c:800
+#, c-format
+msgid "checksum mismatch for file \"%s\""
+msgstr "no coincide la suma de comprobación para el archivo \"%s\""
+
+#: pg_verifybackup.c:824
+#, c-format
+msgid "WAL parsing failed for timeline %u"
+msgstr "Error al analizar el WAL para el timeline %u"
+
+#: pg_verifybackup.c:910
+#, c-format
+msgid ""
+"%s verifies a backup against the backup manifest.\n"
+"\n"
+msgstr ""
+"%s verifica una copia de seguridad con el fichero de manifiesto de la copia de seguridad.\n"
+"\n"
+
+#: pg_verifybackup.c:911
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]... BACKUPDIR\n"
+"\n"
+msgstr ""
+"Uso:\n"
+" %s [OPCIÓN]... BACKUPDIR\n"
+"\n"
+
+#: pg_verifybackup.c:912
+#, c-format
+msgid "Options:\n"
+msgstr "Opciones:\n"
+
+#: pg_verifybackup.c:913
+#, c-format
+msgid " -e, --exit-on-error exit immediately on error\n"
+msgstr " -e, --exit-on-error salir inmediatamente en caso de error\n"
+
+#: pg_verifybackup.c:914
+#, c-format
+msgid " -i, --ignore=RELATIVE_PATH ignore indicated path\n"
+msgstr " -i, --ignore=RELATIVE_PATH ignorar la ruta indicada\n"
+
+#: pg_verifybackup.c:915
+#, c-format
+msgid " -m, --manifest-path=PATH use specified path for manifest\n"
+msgstr " -m, --manifest-path=PATH usar la ruta especificada para el manifiesto\n"
+
+#: pg_verifybackup.c:916
+#, c-format
+msgid " -n, --no-parse-wal do not try to parse WAL files\n"
+msgstr " -n, --no-parse-wal no intentar analizar archivos WAL\n"
+
+#: pg_verifybackup.c:917
+#, c-format
+msgid " -q, --quiet do not print any output, except for errors\n"
+msgstr " -q, --quiet no escribir ningún mensaje, excepto errores\n"
+
+#: pg_verifybackup.c:918
+#, c-format
+msgid " -s, --skip-checksums skip checksum verification\n"
+msgstr " -s, --skip-checksums omitir la verificación de la suma de comprobación\n"
+
+#: pg_verifybackup.c:919
+#, c-format
+msgid " -w, --wal-directory=PATH use specified path for WAL files\n"
+msgstr " -w, --wal-directory=PATH utilizar la ruta especificada para los archivos WAL\n"
+
+#: pg_verifybackup.c:920
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version mostrar la información de la versión, luego salir\n"
+
+#: pg_verifybackup.c:921
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help muestra esta ayuda, luego salir\n"
+
+#: pg_verifybackup.c:922
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"Reporte errores a <%s>.\n"
+
+#: pg_verifybackup.c:923
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "Sitio web de %s: <%s>\n"
diff --git a/src/bin/pg_verifybackup/po/fr.po b/src/bin/pg_verifybackup/po/fr.po
new file mode 100644
index 0000000..a6c68a9
--- /dev/null
+++ b/src/bin/pg_verifybackup/po/fr.po
@@ -0,0 +1,508 @@
+# LANGUAGE message translation file for pg_verifybackup
+# Copyright (C) 2020 PostgreSQL Global Development Group
+# This file is distributed under the same license as the pg_verifybackup (PostgreSQL) package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_verifybackup (PostgreSQL) 13\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-04-15 01:45+0000\n"
+"PO-Revision-Date: 2021-04-15 08:45+0200\n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.4.2\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "fatal : "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "erreur : "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "attention : "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "mémoire épuisée\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "ne peut pas dupliquer un pointeur nul (erreur interne)\n"
+
+#: ../../common/jsonapi.c:1066
+#, c-format
+msgid "Escape sequence \"\\%s\" is invalid."
+msgstr "La séquence d'échappement « \\%s » est invalide."
+
+#: ../../common/jsonapi.c:1069
+#, c-format
+msgid "Character with value 0x%02x must be escaped."
+msgstr "Le caractère de valeur 0x%02x doit être échappé."
+
+#: ../../common/jsonapi.c:1072
+#, c-format
+msgid "Expected end of input, but found \"%s\"."
+msgstr "Attendait une fin de l'entrée, mais a trouvé « %s »."
+
+#: ../../common/jsonapi.c:1075
+#, c-format
+msgid "Expected array element or \"]\", but found \"%s\"."
+msgstr "Élément de tableau ou « ] » attendu, mais trouvé « %s »."
+
+#: ../../common/jsonapi.c:1078
+#, c-format
+msgid "Expected \",\" or \"]\", but found \"%s\"."
+msgstr "« , » ou « ] » attendu, mais trouvé « %s »."
+
+#: ../../common/jsonapi.c:1081
+#, c-format
+msgid "Expected \":\", but found \"%s\"."
+msgstr "« : » attendu, mais trouvé « %s »."
+
+#: ../../common/jsonapi.c:1084
+#, c-format
+msgid "Expected JSON value, but found \"%s\"."
+msgstr "Valeur JSON attendue, mais « %s » trouvé."
+
+#: ../../common/jsonapi.c:1087
+msgid "The input string ended unexpectedly."
+msgstr "La chaîne en entrée se ferme de manière inattendue."
+
+#: ../../common/jsonapi.c:1089
+#, c-format
+msgid "Expected string or \"}\", but found \"%s\"."
+msgstr "Chaîne ou « } » attendu, mais « %s » trouvé"
+
+#: ../../common/jsonapi.c:1092
+#, c-format
+msgid "Expected \",\" or \"}\", but found \"%s\"."
+msgstr "« , » ou « } » attendu, mais trouvé « %s »."
+
+#: ../../common/jsonapi.c:1095
+#, c-format
+msgid "Expected string, but found \"%s\"."
+msgstr "Chaîne attendue, mais « %s » trouvé."
+
+#: ../../common/jsonapi.c:1098
+#, c-format
+msgid "Token \"%s\" is invalid."
+msgstr "Le jeton « %s » n'est pas valide."
+
+#: ../../common/jsonapi.c:1101
+msgid "\\u0000 cannot be converted to text."
+msgstr "\\u0000 ne peut pas être converti en texte."
+
+#: ../../common/jsonapi.c:1103
+msgid "\"\\u\" must be followed by four hexadecimal digits."
+msgstr "« \\u » doit être suivi par quatre chiffres hexadécimaux."
+
+#: ../../common/jsonapi.c:1106
+msgid "Unicode escape values cannot be used for code point values above 007F when the encoding is not UTF8."
+msgstr "les valeurs d'échappement Unicode ne peuvent pas être utilisées pour des valeurs de point code au-dessus de 007F quand l'encodage n'est pas UTF8."
+
+#: ../../common/jsonapi.c:1108
+msgid "Unicode high surrogate must not follow a high surrogate."
+msgstr "Une substitution unicode haute ne doit pas suivre une substitution haute."
+
+#: ../../common/jsonapi.c:1110
+msgid "Unicode low surrogate must follow a high surrogate."
+msgstr "Une substitution unicode basse ne doit pas suivre une substitution haute."
+
+#: parse_manifest.c:152
+msgid "manifest ended unexpectedly"
+msgstr "le manifeste se termine de façon inattendue"
+
+#: parse_manifest.c:191
+msgid "unexpected object start"
+msgstr "début d'objet inattendu"
+
+#: parse_manifest.c:224
+msgid "unexpected object end"
+msgstr "fin d'objet inattendue"
+
+#: parse_manifest.c:251
+msgid "unexpected array start"
+msgstr "début de tableau inattendu"
+
+#: parse_manifest.c:274
+msgid "unexpected array end"
+msgstr "fin de tableau inattendue"
+
+#: parse_manifest.c:299
+msgid "expected version indicator"
+msgstr "indicateur de version inattendu"
+
+#: parse_manifest.c:328
+msgid "unrecognized top-level field"
+msgstr "champ haut niveau inconnu"
+
+#: parse_manifest.c:347
+msgid "unexpected file field"
+msgstr "champ de fichier inattendu"
+
+#: parse_manifest.c:361
+msgid "unexpected WAL range field"
+msgstr "champ d'intervalle de WAL inattendu"
+
+#: parse_manifest.c:367
+msgid "unexpected object field"
+msgstr "champ d'objet inattendu"
+
+#: parse_manifest.c:397
+msgid "unexpected manifest version"
+msgstr "version du manifeste inattendue"
+
+#: parse_manifest.c:448
+msgid "unexpected scalar"
+msgstr "scalaire inattendu"
+
+#: parse_manifest.c:472
+msgid "missing path name"
+msgstr "nom de chemin manquant"
+
+#: parse_manifest.c:475
+msgid "both path name and encoded path name"
+msgstr "le nom du chemin et le nom du chemin encodé"
+
+#: parse_manifest.c:477
+msgid "missing size"
+msgstr "taille manquante"
+
+#: parse_manifest.c:480
+msgid "checksum without algorithm"
+msgstr "somme de contrôle sans algorithme"
+
+#: parse_manifest.c:494
+msgid "could not decode file name"
+msgstr "n'a pas pu décoder le nom du fichier"
+
+#: parse_manifest.c:504
+msgid "file size is not an integer"
+msgstr "la taille du fichier n'est pas un entier"
+
+#: parse_manifest.c:510
+#, c-format
+msgid "unrecognized checksum algorithm: \"%s\""
+msgstr "algorithme de somme de contrôle inconnu : « %s »"
+
+#: parse_manifest.c:529
+#, c-format
+msgid "invalid checksum for file \"%s\": \"%s\""
+msgstr "somme de contrôle invalide pour le fichier « %s » : « %s »"
+
+#: parse_manifest.c:572
+msgid "missing timeline"
+msgstr "timeline manquante"
+
+#: parse_manifest.c:574
+msgid "missing start LSN"
+msgstr "LSN de début manquante"
+
+#: parse_manifest.c:576
+msgid "missing end LSN"
+msgstr "LSN de fin manquante"
+
+#: parse_manifest.c:582
+msgid "timeline is not an integer"
+msgstr "la timeline n'est pas un entier"
+
+#: parse_manifest.c:585
+msgid "could not parse start LSN"
+msgstr "n'a pas pu analyser le LSN de début"
+
+#: parse_manifest.c:588
+msgid "could not parse end LSN"
+msgstr "n'a pas pu analyser le LSN de fin"
+
+#: parse_manifest.c:649
+msgid "expected at least 2 lines"
+msgstr "attendait au moins deux lignes"
+
+#: parse_manifest.c:652
+msgid "last line not newline-terminated"
+msgstr "dernière ligne non terminée avec un caractère newline"
+
+#: parse_manifest.c:657
+#, c-format
+msgid "out of memory"
+msgstr "mémoire épuisée"
+
+#: parse_manifest.c:659
+#, c-format
+msgid "could not initialize checksum of manifest"
+msgstr "n'a pas pu initialiser la somme de contrôle du manifeste"
+
+#: parse_manifest.c:661
+#, c-format
+msgid "could not update checksum of manifest"
+msgstr "n'a pas pu mettre à jour la somme de contrôle du manifeste"
+
+#: parse_manifest.c:664
+#, c-format
+msgid "could not finalize checksum of manifest"
+msgstr "n'a pas pu finaliser la somme de contrôle du manifeste"
+
+#: parse_manifest.c:668
+#, c-format
+msgid "manifest has no checksum"
+msgstr "le manifeste n'a pas de somme de contrôle"
+
+#: parse_manifest.c:672
+#, c-format
+msgid "invalid manifest checksum: \"%s\""
+msgstr "somme de contrôle du manifeste invalide : « %s »"
+
+#: parse_manifest.c:676
+#, c-format
+msgid "manifest checksum mismatch"
+msgstr "différence de somme de contrôle pour le manifeste"
+
+#: parse_manifest.c:691
+#, c-format
+msgid "could not parse backup manifest: %s"
+msgstr "n'a pas pu analyser le manifeste de sauvegarde : %s"
+
+#: pg_verifybackup.c:255 pg_verifybackup.c:265 pg_verifybackup.c:277
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "Essayez « %s --help » pour plus d'informations.\n"
+
+#: pg_verifybackup.c:264
+#, c-format
+msgid "no backup directory specified"
+msgstr "pas de répertoire de sauvegarde spécifié"
+
+#: pg_verifybackup.c:275
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "trop d'arguments en ligne de commande (le premier étant « %s »)"
+
+#: pg_verifybackup.c:298
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"Le programme « %s » est nécessaire pour %s, mais n'a pas été trouvé\n"
+"dans le même répertoire que « %s ».\n"
+"Vérifiez votre installation."
+
+#: pg_verifybackup.c:303
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"Le programme « %s » a été trouvé par « %s »\n"
+"mais n'est pas de la même version que %s.\n"
+"Vérifiez votre installation."
+
+#: pg_verifybackup.c:361
+#, c-format
+msgid "backup successfully verified\n"
+msgstr "sauvegarde vérifiée avec succès\n"
+
+#: pg_verifybackup.c:387 pg_verifybackup.c:723
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "n'a pas pu ouvrir le fichier « %s » : %m"
+
+#: pg_verifybackup.c:391
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "n'a pas pu tester le fichier « %s » : %m"
+
+#: pg_verifybackup.c:411 pg_verifybackup.c:752
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "n'a pas pu lire le fichier « %s » : %m"
+
+#: pg_verifybackup.c:414
+#, c-format
+msgid "could not read file \"%s\": read %d of %lld"
+msgstr "n'a pas pu lire le fichier « %s » : a lu %d sur %lld"
+
+#: pg_verifybackup.c:474
+#, c-format
+msgid "duplicate path name in backup manifest: \"%s\""
+msgstr "nom de chemin dupliqué dans le manifeste de sauvegarde : « %s »"
+
+#: pg_verifybackup.c:537 pg_verifybackup.c:544
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "n'a pas pu ouvrir le répertoire « %s » : %m"
+
+#: pg_verifybackup.c:576
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "n'a pas pu fermer le répertoire « %s » : %m"
+
+#: pg_verifybackup.c:596
+#, c-format
+msgid "could not stat file or directory \"%s\": %m"
+msgstr ""
+"n'a pas pu récupérer les informations sur le fichier ou répertoire\n"
+"« %s » : %m"
+
+#: pg_verifybackup.c:619
+#, c-format
+msgid "\"%s\" is not a file or directory"
+msgstr "« %s » n'est ni un fichier ni un répertoire"
+
+#: pg_verifybackup.c:629
+#, c-format
+msgid "\"%s\" is present on disk but not in the manifest"
+msgstr "« %s » est présent sur disque mais pas dans le manifeste"
+
+#: pg_verifybackup.c:641
+#, c-format
+msgid "\"%s\" has size %lld on disk but size %zu in the manifest"
+msgstr "« %s » a une taille de %lld sur disque mais de %zu dans le manifeste"
+
+#: pg_verifybackup.c:668
+#, c-format
+msgid "\"%s\" is present in the manifest but not on disk"
+msgstr "« %s » est présent dans le manifeste mais pas sur disque"
+
+#: pg_verifybackup.c:731
+#, c-format
+msgid "could not initialize checksum of file \"%s\""
+msgstr "n'a pas pu initialiser la somme de contrôle du fichier « %s »"
+
+#: pg_verifybackup.c:743
+#, c-format
+msgid "could not update checksum of file \"%s\""
+msgstr "n'a pas pu mettre à jour la somme de contrôle du fichier « %s »"
+
+#: pg_verifybackup.c:758
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "n'a pas pu fermer le fichier « %s » : %m"
+
+#: pg_verifybackup.c:777
+#, c-format
+msgid "file \"%s\" should contain %zu bytes, but read %zu bytes"
+msgstr "le fichier « %s » devrait contenir %zu octets, mais la lecture produit %zu octets"
+
+#: pg_verifybackup.c:787
+#, c-format
+msgid "could not finalize checksum of file \"%s\""
+msgstr "n'a pas pu finaliser la somme de contrôle du fichier « %s »"
+
+#: pg_verifybackup.c:795
+#, c-format
+msgid "file \"%s\" has checksum of length %d, but expected %d"
+msgstr "le fichier « %s » a une somme de contrôle de taille %d, alors que %d était attendu"
+
+#: pg_verifybackup.c:799
+#, c-format
+msgid "checksum mismatch for file \"%s\""
+msgstr "différence de somme de contrôle pour le fichier « %s »"
+
+#: pg_verifybackup.c:823
+#, c-format
+msgid "WAL parsing failed for timeline %u"
+msgstr "analyse du WAL échouée pour la timeline %u"
+
+#: pg_verifybackup.c:909
+#, c-format
+msgid ""
+"%s verifies a backup against the backup manifest.\n"
+"\n"
+msgstr ""
+"%s vérifie une sauvegarde à partir du manifeste de sauvegarde.\n"
+"\n"
+
+#: pg_verifybackup.c:910
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]... BACKUPDIR\n"
+"\n"
+msgstr ""
+"Usage:\n"
+" %s [OPTION]... REP_SAUVEGARDE\n"
+"\n"
+
+#: pg_verifybackup.c:911
+#, c-format
+msgid "Options:\n"
+msgstr "Options :\n"
+
+#: pg_verifybackup.c:912
+#, c-format
+msgid " -e, --exit-on-error exit immediately on error\n"
+msgstr " -e, --exit-on-error quitte immédiatement en cas d'erreur\n"
+
+#: pg_verifybackup.c:913
+#, c-format
+msgid " -i, --ignore=RELATIVE_PATH ignore indicated path\n"
+msgstr " -i, --ignore=CHEMIN_RELATIF ignore le chemin indiqué\n"
+
+#: pg_verifybackup.c:914
+#, c-format
+msgid " -m, --manifest-path=PATH use specified path for manifest\n"
+msgstr " -m, --manifest-path=CHEMIN utilise le chemin spécifié pour le manifeste\n"
+
+#: pg_verifybackup.c:915
+#, c-format
+msgid " -n, --no-parse-wal do not try to parse WAL files\n"
+msgstr " -n, --no-parse-wal n'essaie pas d'analyse les fichiers WAL\n"
+
+#: pg_verifybackup.c:916
+#, c-format
+msgid " -q, --quiet do not print any output, except for errors\n"
+msgstr " -q, --quiet n'affiche aucun message sauf pour les erreurs\n"
+
+#: pg_verifybackup.c:917
+#, c-format
+msgid " -s, --skip-checksums skip checksum verification\n"
+msgstr " -s, --skip-checksums ignore la vérification des sommes de contrôle\n"
+
+#: pg_verifybackup.c:918
+#, c-format
+msgid " -w, --wal-directory=PATH use specified path for WAL files\n"
+msgstr " -w, --wal-directory=CHEMIN utilise le chemin spécifié pour les fichiers WAL\n"
+
+#: pg_verifybackup.c:919
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version affiche la version, puis quitte\n"
+
+#: pg_verifybackup.c:920
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help affiche cette aide, puis quitte\n"
+
+#: pg_verifybackup.c:921
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"Rapporter les bogues à <%s>.\n"
+
+#: pg_verifybackup.c:922
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "Page d'accueil de %s : <%s>\n"
+
+#~ msgid "could not read file \"%s\": read %d of %zu"
+#~ msgstr "n'a pas pu lire le fichier « %s » : a lu %d sur %zu"
diff --git a/src/bin/pg_verifybackup/po/ja.po b/src/bin/pg_verifybackup/po/ja.po
new file mode 100644
index 0000000..97db033
--- /dev/null
+++ b/src/bin/pg_verifybackup/po/ja.po
@@ -0,0 +1,503 @@
+# Japanese message translation file for pg_verifybackup
+# Copyright (C) 2022 PostgreSQL Global Development Group
+# This file is distributed under the same license as the pg_verifybackup (PostgreSQL) package.
+# Haiying Tang <tanghy.fnst@cn.fujitsu.com>, 2021.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_verifybackup (PostgreSQL 14)\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-08-25 17:22+0900\n"
+"PO-Revision-Date: 2021-05-17 16:02+0900\n"
+"Last-Translator: Haiying Tang <tanghy.fnst@cn.fujitsu.com>\n"
+"Language-Team: Japan PostgreSQL Users Group <jpug-doc@ml.postgresql.jp>\n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.8.13\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "致命的エラー: "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "エラー: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "警告: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "メモリ不足\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "nullポインタは複製できません (内部エラー)\n"
+
+#: ../../common/jsonapi.c:1066
+#, c-format
+msgid "Escape sequence \"\\%s\" is invalid."
+msgstr "エスケープシーケンス\"\\%s\"は不正です。"
+
+#: ../../common/jsonapi.c:1069
+#, c-format
+msgid "Character with value 0x%02x must be escaped."
+msgstr "0x%02x値を持つ文字はエスケープしなければなりません"
+
+#: ../../common/jsonapi.c:1072
+#, c-format
+msgid "Expected end of input, but found \"%s\"."
+msgstr "入力の終端を想定していましたが、\"%s\"でした。"
+
+#: ../../common/jsonapi.c:1075
+#, c-format
+msgid "Expected array element or \"]\", but found \"%s\"."
+msgstr "配列要素または\"]\"を想定していましたが、\"%s\"でした。"
+
+#: ../../common/jsonapi.c:1078
+#, c-format
+msgid "Expected \",\" or \"]\", but found \"%s\"."
+msgstr "\",\"または\"]\"を想定していましたが、\"%s\"でした。"
+
+#: ../../common/jsonapi.c:1081
+#, c-format
+msgid "Expected \":\", but found \"%s\"."
+msgstr "\":\"を想定していましたが、\"%s\"でした。"
+
+#: ../../common/jsonapi.c:1084
+#, c-format
+msgid "Expected JSON value, but found \"%s\"."
+msgstr "JSON値を想定していましたが、\"%s\"でした。"
+
+#: ../../common/jsonapi.c:1087
+msgid "The input string ended unexpectedly."
+msgstr "入力文字列が予期せず終了しました。"
+
+#: ../../common/jsonapi.c:1089
+#, c-format
+msgid "Expected string or \"}\", but found \"%s\"."
+msgstr "文字列または\"}\"を想定していましたが、\"%s\"でした。"
+
+#: ../../common/jsonapi.c:1092
+#, c-format
+msgid "Expected \",\" or \"}\", but found \"%s\"."
+msgstr "\",\"または\"}\"を想定していましたが、\"%s\"でした。"
+
+#: ../../common/jsonapi.c:1095
+#, c-format
+msgid "Expected string, but found \"%s\"."
+msgstr "文字列を想定していましたが、\"%s\"でした。"
+
+#: ../../common/jsonapi.c:1098
+#, c-format
+msgid "Token \"%s\" is invalid."
+msgstr "トークン\"%s\"は不正です。"
+
+#: ../../common/jsonapi.c:1101
+msgid "\\u0000 cannot be converted to text."
+msgstr "\\u0000 はテキストに変換できません。"
+
+#: ../../common/jsonapi.c:1103
+msgid "\"\\u\" must be followed by four hexadecimal digits."
+msgstr "\"\\u\"の後には16進数の4桁が続かなければなりません。"
+
+#: ../../common/jsonapi.c:1106
+msgid "Unicode escape values cannot be used for code point values above 007F when the encoding is not UTF8."
+msgstr "エンコーディングがUTF-8ではない場合、コードポイントの値が 007F 以上についてはUnicodeエスケープの値は使用できません。"
+
+#: ../../common/jsonapi.c:1108
+msgid "Unicode high surrogate must not follow a high surrogate."
+msgstr "Unicodeの上位サロゲートは上位サロゲートに続いてはいけません。"
+
+#: ../../common/jsonapi.c:1110
+msgid "Unicode low surrogate must follow a high surrogate."
+msgstr "Unicodeの下位サロゲートは上位サロゲートに続かなければなりません。"
+
+#: parse_manifest.c:152
+msgid "manifest ended unexpectedly"
+msgstr "目録が予期せず終了しました。"
+
+#: parse_manifest.c:191
+msgid "unexpected object start"
+msgstr "予期しないオブジェクトの開始"
+
+#: parse_manifest.c:224
+msgid "unexpected object end"
+msgstr "予期しないオブジェクトの終わり"
+
+#: parse_manifest.c:251
+msgid "unexpected array start"
+msgstr "予期しない配列の開始"
+
+#: parse_manifest.c:274
+msgid "unexpected array end"
+msgstr "予期しない配列の終わり"
+
+#: parse_manifest.c:299
+msgid "expected version indicator"
+msgstr "バージョン指示子を想定していました"
+
+#: parse_manifest.c:328
+msgid "unrecognized top-level field"
+msgstr "認識できないトップレベルフィールド"
+
+#: parse_manifest.c:347
+msgid "unexpected file field"
+msgstr "予期しないファイルフィールド"
+
+#: parse_manifest.c:361
+msgid "unexpected WAL range field"
+msgstr "予期しないWAL範囲フィールド"
+
+#: parse_manifest.c:367
+msgid "unexpected object field"
+msgstr "予期しないオブジェクトフィールド"
+
+#: parse_manifest.c:397
+msgid "unexpected manifest version"
+msgstr "予期しない目録バージョン"
+
+#: parse_manifest.c:448
+msgid "unexpected scalar"
+msgstr "予期しないスカラー"
+
+#: parse_manifest.c:472
+msgid "missing path name"
+msgstr "パス名がありません"
+
+#: parse_manifest.c:475
+msgid "both path name and encoded path name"
+msgstr "パス名とエンコードされたパス名の両方"
+
+#: parse_manifest.c:477
+msgid "missing size"
+msgstr "サイズがありません"
+
+#: parse_manifest.c:480
+msgid "checksum without algorithm"
+msgstr "アルゴリズムなしのチェックサム"
+
+#: parse_manifest.c:494
+msgid "could not decode file name"
+msgstr "ファイル名をデコードできませんでした"
+
+#: parse_manifest.c:504
+msgid "file size is not an integer"
+msgstr "ファイルサイズが整数ではありません"
+
+#: parse_manifest.c:510
+#, c-format
+msgid "unrecognized checksum algorithm: \"%s\""
+msgstr "認識できないチェックサムアルゴリズム: \"%s\""
+
+#: parse_manifest.c:529
+#, c-format
+msgid "invalid checksum for file \"%s\": \"%s\""
+msgstr "\"%s\" ファイルのチェックサムが無効: \"%s\""
+
+#: parse_manifest.c:572
+msgid "missing timeline"
+msgstr "タイムラインがありません"
+
+#: parse_manifest.c:574
+msgid "missing start LSN"
+msgstr "開始LSNがありません"
+
+#: parse_manifest.c:576
+msgid "missing end LSN"
+msgstr "終了LSNがありません"
+
+#: parse_manifest.c:582
+msgid "timeline is not an integer"
+msgstr "タイムラインが整数ではありません"
+
+#: parse_manifest.c:585
+msgid "could not parse start LSN"
+msgstr "開始LSNを解析できませんでした"
+
+#: parse_manifest.c:588
+msgid "could not parse end LSN"
+msgstr "終了LSNを解析できませんでした"
+
+#: parse_manifest.c:649
+msgid "expected at least 2 lines"
+msgstr "少なくとも2行が必要です"
+
+#: parse_manifest.c:652
+msgid "last line not newline-terminated"
+msgstr "最後の行が改行で終わっていません"
+
+#: parse_manifest.c:657
+#, c-format
+msgid "out of memory"
+msgstr "メモリ不足"
+
+#: parse_manifest.c:659
+#, c-format
+msgid "could not initialize checksum of manifest"
+msgstr "目録のチェックサムを初期化に失敗しました"
+
+#: parse_manifest.c:661
+#, c-format
+msgid "could not update checksum of manifest"
+msgstr "目録のチェックサムの更新に失敗しました"
+
+#: parse_manifest.c:664
+#, c-format
+msgid "could not finalize checksum of manifest"
+msgstr "目録のチェックサムの完了に失敗しました"
+
+#: parse_manifest.c:668
+#, c-format
+msgid "manifest has no checksum"
+msgstr "目録にチェックサムがありません"
+
+#: parse_manifest.c:672
+#, c-format
+msgid "invalid manifest checksum: \"%s\""
+msgstr "無効な目録のチェックサム: \"%s\""
+
+#: parse_manifest.c:676
+#, c-format
+msgid "manifest checksum mismatch"
+msgstr "目録のチェックサムが合っていません"
+
+#: parse_manifest.c:691
+#, c-format
+msgid "could not parse backup manifest: %s"
+msgstr "バックアップ目録を解析できませんでした: %s"
+
+#: pg_verifybackup.c:255 pg_verifybackup.c:265 pg_verifybackup.c:277
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "詳細は\"%s --help\"で確認してください。\n"
+
+#: pg_verifybackup.c:264
+#, c-format
+msgid "no backup directory specified"
+msgstr "バックアップディレクトリが指定されていません"
+
+#: pg_verifybackup.c:275
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "コマンドライン引数が多すぎます (先頭は\"%s\")"
+
+#: pg_verifybackup.c:298
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"%2$sにはプログラム\"%1$s\"が必要ですが、\"%3$s\"と同じディレクトリ\n"
+"にはありませんでした。\n"
+"インストールの状態を確認してください。"
+
+#: pg_verifybackup.c:303
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"\"%2$s\"がプログラム\"%1$s\"を見つけましたが、これは%3$sと同じ\n"
+"バージョンではありませんでした。\n"
+"インストールの状態を確認してください。"
+
+#: pg_verifybackup.c:362
+#, c-format
+msgid "backup successfully verified\n"
+msgstr "バックアップが正常に検証されました\n"
+
+#: pg_verifybackup.c:388 pg_verifybackup.c:724
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "ファイル\"%s\"をオープンできませんでした: %m"
+
+#: pg_verifybackup.c:392
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "ファイル\"%s\"のstatに失敗しました: %m"
+
+#: pg_verifybackup.c:412 pg_verifybackup.c:753
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "ファイル\"%s\"の読み取りに失敗しました: %m"
+
+#: pg_verifybackup.c:415
+#, c-format
+msgid "could not read file \"%s\": read %d of %lld"
+msgstr "ファイル\"%1$s\"を読み込めませんでした: %3$lldバイトのうち%2$dバイトを読み込みました"
+
+#: pg_verifybackup.c:475
+#, c-format
+msgid "duplicate path name in backup manifest: \"%s\""
+msgstr "バックアップ目録内にパス名の重複: \"%s\""
+
+#: pg_verifybackup.c:538 pg_verifybackup.c:545
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "ディレクトリ\"%s\"をオープンできませんでした: %m"
+
+#: pg_verifybackup.c:577
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "ディレクトリ\"%s\"をクローズできませんでした: %m"
+
+#: pg_verifybackup.c:597
+#, c-format
+msgid "could not stat file or directory \"%s\": %m"
+msgstr "\"%s\"というファイルまたはディレクトリの情報を取得できませんでした: %m"
+
+#: pg_verifybackup.c:620
+#, c-format
+msgid "\"%s\" is not a file or directory"
+msgstr "\"%s\"はファイルまたはディレクトリではありません"
+
+#: pg_verifybackup.c:630
+#, c-format
+msgid "\"%s\" is present on disk but not in the manifest"
+msgstr "\"%s\"はディスクに存在しますが、目録には存在しません"
+
+#: pg_verifybackup.c:642
+#, c-format
+msgid "\"%s\" has size %lld on disk but size %zu in the manifest"
+msgstr "\"%s\"はディスク上ではサイズが%lldバイトですが、目録では%zuバイトです"
+
+#: pg_verifybackup.c:669
+#, c-format
+msgid "\"%s\" is present in the manifest but not on disk"
+msgstr "\"%s\"目録には存在しますが、ディスクには存在しません"
+
+#: pg_verifybackup.c:732
+#, c-format
+msgid "could not initialize checksum of file \"%s\""
+msgstr "ファイル\"%s\"のチェックサムの初期化に失敗しました"
+
+#: pg_verifybackup.c:744
+#, c-format
+msgid "could not update checksum of file \"%s\""
+msgstr "ファイル\"%s\"のチェックサムの更新に失敗しました"
+
+#: pg_verifybackup.c:759
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "ファイル\"%s\"をクローズできませんでした: %m"
+
+#: pg_verifybackup.c:778
+#, c-format
+msgid "file \"%s\" should contain %zu bytes, but read %zu bytes"
+msgstr "file\"%s\"は%zuバイトを含む必要がありますが、%zuバイトが読み込まれました"
+
+#: pg_verifybackup.c:788
+#, c-format
+msgid "could not finalize checksum of file \"%s\""
+msgstr "ファイル\"%s\"のチェックサムの完了に失敗しました"
+
+#: pg_verifybackup.c:796
+#, c-format
+msgid "file \"%s\" has checksum of length %d, but expected %d"
+msgstr "ファイル\"%s\"のチェックサムの長さは%dですが、予期されるのは%dです"
+
+#: pg_verifybackup.c:800
+#, c-format
+msgid "checksum mismatch for file \"%s\""
+msgstr "ファイル\"%s\"のチェックサムが一致しません"
+
+#: pg_verifybackup.c:824
+#, c-format
+msgid "WAL parsing failed for timeline %u"
+msgstr "タイムライン%uのWAL解析に失敗しました"
+
+#: pg_verifybackup.c:910
+#, c-format
+msgid ""
+"%s verifies a backup against the backup manifest.\n"
+"\n"
+msgstr ""
+"%sはバックアップ目録に対してバックアップを検証します。\n"
+"\n"
+
+#: pg_verifybackup.c:911
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]... BACKUPDIR\n"
+"\n"
+msgstr ""
+"使用方法:\n"
+" %s [オプション]... BACKUPDIR\n"
+"\n"
+
+#: pg_verifybackup.c:912
+#, c-format
+msgid "Options:\n"
+msgstr "オプション:\n"
+
+#: pg_verifybackup.c:913
+#, c-format
+msgid " -e, --exit-on-error exit immediately on error\n"
+msgstr " -e, --exit-on-error エラー時に直ちに終了する\n"
+
+#: pg_verifybackup.c:914
+#, c-format
+msgid " -i, --ignore=RELATIVE_PATH ignore indicated path\n"
+msgstr " -i, --ignore=RELATIVE_PATH 指示されたパスを無視\n"
+
+#: pg_verifybackup.c:915
+#, c-format
+msgid " -m, --manifest-path=PATH use specified path for manifest\n"
+msgstr " -m, --manifest-path=PATH 目録に指定されたパスを使用する\n"
+
+#: pg_verifybackup.c:916
+#, c-format
+msgid " -n, --no-parse-wal do not try to parse WAL files\n"
+msgstr " -n, --no-parse-wal WALファイルをパースしようとしない\n"
+
+#: pg_verifybackup.c:917
+#, c-format
+msgid " -q, --quiet do not print any output, except for errors\n"
+msgstr " -q, --quiet エラー以外何も出力しない\n"
+
+#: pg_verifybackup.c:918
+#, c-format
+msgid " -s, --skip-checksums skip checksum verification\n"
+msgstr " -s, --skip-checksums スキップチェックサム検証\n"
+
+#: pg_verifybackup.c:919
+#, c-format
+msgid " -w, --wal-directory=PATH use specified path for WAL files\n"
+msgstr " -w, --wal-directory=PATH 指定したWALファイルのパスを使用する\n"
+
+#: pg_verifybackup.c:920
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version バージョン情報を表示して終了\n"
+
+#: pg_verifybackup.c:921
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help このヘルプを表示して終了\n"
+
+#: pg_verifybackup.c:922
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"バグは<%s>に報告してください。\n"
+
+#: pg_verifybackup.c:923
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "%s ホームページ: <%s>\n"
diff --git a/src/bin/pg_verifybackup/po/ko.po b/src/bin/pg_verifybackup/po/ko.po
new file mode 100644
index 0000000..06d9ce2
--- /dev/null
+++ b/src/bin/pg_verifybackup/po/ko.po
@@ -0,0 +1,467 @@
+# LANGUAGE message translation file for pg_verifybackup
+# Copyright (C) 2020 PostgreSQL Global Development Group
+# This file is distributed under the same license as the pg_verifybackup (PostgreSQL) package.
+# Ioseph Kim <ioseph@uri.sarang.net>, 2020.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_verifybackup (PostgreSQL) 13\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2020-10-05 20:43+0000\n"
+"PO-Revision-Date: 2020-10-06 14:45+0900\n"
+"Last-Translator: Ioseph Kim <ioseph@uri.sarang.net>\n"
+"Language-Team: PostgreSQL Korea <kr@postgresql.org>\n"
+"Language: ko\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../../../src/common/logging.c:236
+#, c-format
+msgid "fatal: "
+msgstr "심각: "
+
+#: ../../../src/common/logging.c:243
+#, c-format
+msgid "error: "
+msgstr "오류: "
+
+#: ../../../src/common/logging.c:250
+#, c-format
+msgid "warning: "
+msgstr "경고: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "메모리 부족\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "null 포인터를 중복할 수 없음 (내부 오류)\n"
+
+#: ../../common/jsonapi.c:1064
+#, c-format
+msgid "Escape sequence \"\\%s\" is invalid."
+msgstr "잘못된 이스케이프 조합: \"\\%s\""
+
+#: ../../common/jsonapi.c:1067
+#, c-format
+msgid "Character with value 0x%02x must be escaped."
+msgstr "0x%02x 값의 문자는 이스케이프 되어야함."
+
+#: ../../common/jsonapi.c:1070
+#, c-format
+msgid "Expected end of input, but found \"%s\"."
+msgstr "입력 자료의 끝을 기대했는데, \"%s\" 값이 더 있음."
+
+#: ../../common/jsonapi.c:1073
+#, c-format
+msgid "Expected array element or \"]\", but found \"%s\"."
+msgstr "\"]\" 가 필요한데 \"%s\"이(가) 있음"
+
+#: ../../common/jsonapi.c:1076
+#, c-format
+msgid "Expected \",\" or \"]\", but found \"%s\"."
+msgstr "\",\" 또는 \"]\"가 필요한데 \"%s\"이(가) 있음"
+
+#: ../../common/jsonapi.c:1079
+#, c-format
+msgid "Expected \":\", but found \"%s\"."
+msgstr "\":\"가 필요한데 \"%s\"이(가) 있음"
+
+#: ../../common/jsonapi.c:1082
+#, c-format
+msgid "Expected JSON value, but found \"%s\"."
+msgstr "JSON 값을 기대했는데, \"%s\" 값임"
+
+#: ../../common/jsonapi.c:1085
+msgid "The input string ended unexpectedly."
+msgstr "입력 문자열이 예상치 않게 끝났음."
+
+#: ../../common/jsonapi.c:1087
+#, c-format
+msgid "Expected string or \"}\", but found \"%s\"."
+msgstr "\"}\"가 필요한데 \"%s\"이(가) 있음"
+
+#: ../../common/jsonapi.c:1090
+#, c-format
+msgid "Expected \",\" or \"}\", but found \"%s\"."
+msgstr "\",\" 또는 \"}\"가 필요한데 \"%s\"이(가) 있음"
+
+#: ../../common/jsonapi.c:1093
+#, c-format
+msgid "Expected string, but found \"%s\"."
+msgstr "문자열 값을 기대했는데, \"%s\" 값임"
+
+#: ../../common/jsonapi.c:1096
+#, c-format
+msgid "Token \"%s\" is invalid."
+msgstr "잘못된 토큰: \"%s\""
+
+#: ../../common/jsonapi.c:1099
+msgid "\\u0000 cannot be converted to text."
+msgstr "\\u0000 값은 text 형으로 변환할 수 없음."
+
+#: ../../common/jsonapi.c:1101
+msgid "\"\\u\" must be followed by four hexadecimal digits."
+msgstr "\"\\u\" 표기법은 뒤에 4개의 16진수가 와야합니다."
+
+#: ../../common/jsonapi.c:1104
+msgid "Unicode escape values cannot be used for code point values above 007F when the encoding is not UTF8."
+msgstr "인코딩은 UTF8이 아닐 때 유니코드 이스케이프 값은 007F 이상 코드 포인트 값으로 사용할 수 없음."
+
+#: ../../common/jsonapi.c:1106
+msgid "Unicode high surrogate must not follow a high surrogate."
+msgstr "유니코드 상위 surrogate(딸림 코드)는 상위 딸림 코드 뒤에 오면 안됨."
+
+#: ../../common/jsonapi.c:1108
+msgid "Unicode low surrogate must follow a high surrogate."
+msgstr "유니코드 상위 surrogate(딸림 코드) 뒤에는 하위 딸림 코드가 있어야 함."
+
+#: parse_manifest.c:152
+msgid "manifest ended unexpectedly"
+msgstr "메니페스트가 비정상적으로 끝났음"
+
+#: parse_manifest.c:191
+msgid "unexpected object start"
+msgstr "비정상적인 개체 시작"
+
+#: parse_manifest.c:224
+msgid "unexpected object end"
+msgstr "비정상적인 개체 끝"
+
+#: parse_manifest.c:251
+msgid "unexpected array start"
+msgstr "비정상적인 배열 시작"
+
+#: parse_manifest.c:274
+msgid "unexpected array end"
+msgstr "비정상적인 배열 끝"
+
+#: parse_manifest.c:299
+msgid "expected version indicator"
+msgstr "버전 지시자가 있어야 함"
+
+#: parse_manifest.c:328
+msgid "unrecognized top-level field"
+msgstr "최상위 필드를 알 수 없음"
+
+#: parse_manifest.c:347
+msgid "unexpected file field"
+msgstr "예상치 못한 파일 필드"
+
+#: parse_manifest.c:361
+msgid "unexpected WAL range field"
+msgstr "예상치 못한 WAL 범위 필드"
+
+#: parse_manifest.c:367
+msgid "unexpected object field"
+msgstr "예상치 못한 개체 필드"
+
+#: parse_manifest.c:397
+msgid "unexpected manifest version"
+msgstr "예상치 못한 메니페스트 버전"
+
+#: parse_manifest.c:448
+msgid "unexpected scalar"
+msgstr "예상치 못한 스칼라"
+
+#: parse_manifest.c:472
+msgid "missing path name"
+msgstr "패스 이름 빠짐"
+
+#: parse_manifest.c:475
+msgid "both path name and encoded path name"
+msgstr "패스 이름과 인코딩 된 패스 이름이 함께 있음"
+
+#: parse_manifest.c:477
+msgid "missing size"
+msgstr "크기 빠짐"
+
+#: parse_manifest.c:480
+msgid "checksum without algorithm"
+msgstr "알고리즘 없는 체크섬"
+
+#: parse_manifest.c:494
+msgid "could not decode file name"
+msgstr "파일 이름을 디코딩할 수 없음"
+
+#: parse_manifest.c:504
+msgid "file size is not an integer"
+msgstr "파일 크기가 정수가 아님"
+
+#: parse_manifest.c:510
+#, c-format
+msgid "unrecognized checksum algorithm: \"%s\""
+msgstr "알 수 없는 체크섬 알고리즘: \"%s\""
+
+#: parse_manifest.c:529
+#, c-format
+msgid "invalid checksum for file \"%s\": \"%s\""
+msgstr "\"%s\" 파일의 체크섬이 잘못됨: \"%s\""
+
+#: parse_manifest.c:572
+msgid "missing timeline"
+msgstr "타임라인 빠짐"
+
+#: parse_manifest.c:574
+msgid "missing start LSN"
+msgstr "시작 LSN 빠짐"
+
+#: parse_manifest.c:576
+msgid "missing end LSN"
+msgstr "끝 LSN 빠짐"
+
+#: parse_manifest.c:582
+msgid "timeline is not an integer"
+msgstr "타임라인이 정수가 아님"
+
+#: parse_manifest.c:585
+msgid "could not parse start LSN"
+msgstr "시작 LSN 값을 분석할 수 없음"
+
+#: parse_manifest.c:588
+msgid "could not parse end LSN"
+msgstr "끝 LSN 값을 분석할 수 없음"
+
+#: parse_manifest.c:649
+msgid "expected at least 2 lines"
+msgstr "적어도 2줄이 더 있어야 함"
+
+#: parse_manifest.c:652
+msgid "last line not newline-terminated"
+msgstr "마지막 줄에 줄바꿈 문자가 없음"
+
+#: parse_manifest.c:661
+#, c-format
+msgid "manifest has no checksum"
+msgstr "메니페스트에 체크섬 없음"
+
+#: parse_manifest.c:665
+#, c-format
+msgid "invalid manifest checksum: \"%s\""
+msgstr "잘못된 메니페스트 체크섬: \"%s\""
+
+#: parse_manifest.c:669
+#, c-format
+msgid "manifest checksum mismatch"
+msgstr "메니페스트 체크섬 불일치"
+
+#: parse_manifest.c:683
+#, c-format
+msgid "could not parse backup manifest: %s"
+msgstr "백업 메니페스트 구문 분석 실패: %s"
+
+#: pg_verifybackup.c:255 pg_verifybackup.c:265 pg_verifybackup.c:277
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "자제한 사항은 \"%s --help\" 명령으로 살펴보십시오.\n"
+
+#: pg_verifybackup.c:264
+#, c-format
+msgid "no backup directory specified"
+msgstr "백업 디렉터리를 지정하지 않았음"
+
+#: pg_verifybackup.c:275
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "너무 많은 명령행 인자를 지정했습니다. (처음 \"%s\")"
+
+#: pg_verifybackup.c:298
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"\"%s\" 프로그램이 %s 작업에서 필요하지만 \"%s\" 프로그램이\n"
+"있는 디렉터리 내에 없습니다.\n"
+"설치 상태를 확인해 보세요."
+
+#: pg_verifybackup.c:303
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"\"%s\" 프로그램을 \"%s\" 작업을 위해 찾았지만\n"
+"%s 버전과 같지 않습니다.\n"
+"설치 상태를 확인해 보세요."
+
+#: pg_verifybackup.c:361
+#, c-format
+msgid "backup successfully verified\n"
+msgstr "백업 검사 완료\n"
+
+#: pg_verifybackup.c:387 pg_verifybackup.c:723
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "\"%s\" 파일을 열 수 없음: %m"
+
+#: pg_verifybackup.c:391
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "\"%s\" 파일의 상태값을 알 수 없음: %m"
+
+#: pg_verifybackup.c:411 pg_verifybackup.c:738
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "\"%s\" 파일을 읽을 수 없음: %m"
+
+#: pg_verifybackup.c:414
+#, c-format
+msgid "could not read file \"%s\": read %d of %zu"
+msgstr "\"%s\" 파일을 읽을 수 없음: %d 읽음, 전체 %zu"
+
+#: pg_verifybackup.c:474
+#, c-format
+msgid "duplicate path name in backup manifest: \"%s\""
+msgstr "백업 메니페스트 안에 경로 이름이 중복됨: \"%s\""
+
+#: pg_verifybackup.c:537 pg_verifybackup.c:544
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "\"%s\" 디렉터리 열 수 없음: %m"
+
+#: pg_verifybackup.c:576
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "\"%s\" 디렉터리를 닫을 수 없음: %m"
+
+#: pg_verifybackup.c:596
+#, c-format
+msgid "could not stat file or directory \"%s\": %m"
+msgstr "파일 또는 디렉터리 \"%s\"의 상태를 확인할 수 없음: %m"
+
+#: pg_verifybackup.c:619
+#, c-format
+msgid "\"%s\" is not a file or directory"
+msgstr "\"%s\" 이름은 파일이나 디렉터리가 아님"
+
+#: pg_verifybackup.c:629
+#, c-format
+msgid "\"%s\" is present on disk but not in the manifest"
+msgstr "디스크에는 \"%s\" 개체가 있으나, 메니페스트 안에는 없음"
+
+#: pg_verifybackup.c:641
+#, c-format
+msgid "\"%s\" has size %zu on disk but size %zu in the manifest"
+msgstr "\"%s\" 의 디스크 크기는 %zu 이나 메니페스트 안에는 %zu 입니다"
+
+#: pg_verifybackup.c:668
+#, c-format
+msgid "\"%s\" is present in the manifest but not on disk"
+msgstr "메니페스트 안에는 \"%s\" 개체가 있으나 디스크에는 없음"
+
+#: pg_verifybackup.c:744
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "\"%s\" 파일을 닫을 수 없음: %m"
+
+#: pg_verifybackup.c:763
+#, c-format
+msgid "file \"%s\" should contain %zu bytes, but read %zu bytes"
+msgstr "\"%s\" 파일은 %zu 바이트이나 %zu 바이트를 읽음"
+
+#: pg_verifybackup.c:774
+#, c-format
+msgid "file \"%s\" has checksum of length %d, but expected %d"
+msgstr "\"%s\" 파일 체크섬 %d, 예상되는 값: %d"
+
+#: pg_verifybackup.c:778
+#, c-format
+msgid "checksum mismatch for file \"%s\""
+msgstr "\"%s\" 파일의 체크섬이 맞지 않음"
+
+#: pg_verifybackup.c:804
+#, c-format
+msgid "WAL parsing failed for timeline %u"
+msgstr "타임라인 %u번의 WAL 분석 오류"
+
+#: pg_verifybackup.c:890
+#, c-format
+msgid ""
+"%s verifies a backup against the backup manifest.\n"
+"\n"
+msgstr ""
+"%s 프로그램은 백업 메니페스트로 백업을 검사합니다.\n"
+"\n"
+
+#: pg_verifybackup.c:891
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]... BACKUPDIR\n"
+"\n"
+msgstr ""
+"사용법:\n"
+" %s [옵션]... 백업디렉터리\n"
+"\n"
+
+#: pg_verifybackup.c:892
+#, c-format
+msgid "Options:\n"
+msgstr "옵션들:\n"
+
+#: pg_verifybackup.c:893
+#, c-format
+msgid " -e, --exit-on-error exit immediately on error\n"
+msgstr " -e, --exit-on-error 오류가 있으면 작업 중지\n"
+
+#: pg_verifybackup.c:894
+#, c-format
+msgid " -i, --ignore=RELATIVE_PATH ignore indicated path\n"
+msgstr " -i, --ignore=상대경로 지정한 경로 건너뜀\n"
+
+#: pg_verifybackup.c:895
+#, c-format
+msgid " -m, --manifest-path=PATH use specified path for manifest\n"
+msgstr " -m, --manifest-path=경로 메니페스트 파일 경로 지정\n"
+
+#: pg_verifybackup.c:896
+#, c-format
+msgid " -n, --no-parse-wal do not try to parse WAL files\n"
+msgstr " -n, --no-parse-wal WAL 파일 검사 건너뜀\n"
+
+#: pg_verifybackup.c:897
+#, c-format
+msgid " -q, --quiet do not print any output, except for errors\n"
+msgstr " -q, --quiet 오류를 빼고 나머지는 아무 것도 안 보여줌\n"
+
+#: pg_verifybackup.c:898
+#, c-format
+msgid " -s, --skip-checksums skip checksum verification\n"
+msgstr " -s, --skip-checksums 체크섬 검사 건너뜀\n"
+
+#: pg_verifybackup.c:899
+#, c-format
+msgid " -w, --wal-directory=PATH use specified path for WAL files\n"
+msgstr " -w, --wal-directory=경로 WAL 파일이 있는 경로 지정\n"
+
+#: pg_verifybackup.c:900
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version 버전 정보를 보여주고 마침\n"
+
+#: pg_verifybackup.c:901
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help 이 도움말을 보여주고 마침\n"
+
+#: pg_verifybackup.c:902
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"문제점 보고 주소: <%s>\n"
+
+#: pg_verifybackup.c:903
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "%s 홈페이지: <%s>\n"
diff --git a/src/bin/pg_verifybackup/po/ru.po b/src/bin/pg_verifybackup/po/ru.po
new file mode 100644
index 0000000..ea2e96d
--- /dev/null
+++ b/src/bin/pg_verifybackup/po/ru.po
@@ -0,0 +1,514 @@
+# Alexander Lakhin <a.lakhin@postgrespro.ru>, 2020, 2021.
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_verifybackup (PostgreSQL) 13\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-08-14 06:29+0300\n"
+"PO-Revision-Date: 2021-09-17 07:33+0300\n"
+"Last-Translator: Alexander Lakhin <a.lakhin@postgrespro.ru>\n"
+"Language-Team: Russian <pgsql-ru-general@postgresql.org>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Lokalize 19.12.3\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "важно: "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "ошибка: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "предупреждение: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "нехватка памяти\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "попытка дублирования нулевого указателя (внутренняя ошибка)\n"
+
+#: ../../common/jsonapi.c:1066
+#, c-format
+msgid "Escape sequence \"\\%s\" is invalid."
+msgstr "Неверная спецпоследовательность: \"\\%s\"."
+
+#: ../../common/jsonapi.c:1069
+#, c-format
+msgid "Character with value 0x%02x must be escaped."
+msgstr "Символ с кодом 0x%02x необходимо экранировать."
+
+#: ../../common/jsonapi.c:1072
+#, c-format
+msgid "Expected end of input, but found \"%s\"."
+msgstr "Ожидался конец текста, но обнаружено продолжение \"%s\"."
+
+#: ../../common/jsonapi.c:1075
+#, c-format
+msgid "Expected array element or \"]\", but found \"%s\"."
+msgstr "Ожидался элемент массива или \"]\", но обнаружено \"%s\"."
+
+#: ../../common/jsonapi.c:1078
+#, c-format
+msgid "Expected \",\" or \"]\", but found \"%s\"."
+msgstr "Ожидалась \",\" или \"]\", но обнаружено \"%s\"."
+
+#: ../../common/jsonapi.c:1081
+#, c-format
+msgid "Expected \":\", but found \"%s\"."
+msgstr "Ожидалось \":\", но обнаружено \"%s\"."
+
+#: ../../common/jsonapi.c:1084
+#, c-format
+msgid "Expected JSON value, but found \"%s\"."
+msgstr "Ожидалось значение JSON, но обнаружено \"%s\"."
+
+#: ../../common/jsonapi.c:1087
+msgid "The input string ended unexpectedly."
+msgstr "Неожиданный конец входной строки."
+
+#: ../../common/jsonapi.c:1089
+#, c-format
+msgid "Expected string or \"}\", but found \"%s\"."
+msgstr "Ожидалась строка или \"}\", но обнаружено \"%s\"."
+
+#: ../../common/jsonapi.c:1092
+#, c-format
+msgid "Expected \",\" or \"}\", but found \"%s\"."
+msgstr "Ожидалась \",\" или \"}\", но обнаружено \"%s\"."
+
+#: ../../common/jsonapi.c:1095
+#, c-format
+msgid "Expected string, but found \"%s\"."
+msgstr "Ожидалась строка, но обнаружено \"%s\"."
+
+#: ../../common/jsonapi.c:1098
+#, c-format
+msgid "Token \"%s\" is invalid."
+msgstr "Ошибочный элемент текста \"%s\"."
+
+#: ../../common/jsonapi.c:1101
+msgid "\\u0000 cannot be converted to text."
+msgstr "\\u0000 нельзя преобразовать в текст."
+
+#: ../../common/jsonapi.c:1103
+msgid "\"\\u\" must be followed by four hexadecimal digits."
+msgstr "За \"\\u\" должны следовать четыре шестнадцатеричные цифры."
+
+#: ../../common/jsonapi.c:1106
+msgid ""
+"Unicode escape values cannot be used for code point values above 007F when "
+"the encoding is not UTF8."
+msgstr ""
+"Спецкоды Unicode для значений выше 007F можно использовать только с "
+"кодировкой UTF8."
+
+#: ../../common/jsonapi.c:1108
+msgid "Unicode high surrogate must not follow a high surrogate."
+msgstr ""
+"Старшее слово суррогата Unicode не может следовать за другим старшим словом."
+
+#: ../../common/jsonapi.c:1110
+msgid "Unicode low surrogate must follow a high surrogate."
+msgstr "Младшее слово суррогата Unicode должно следовать за старшим словом."
+
+#: parse_manifest.c:152
+msgid "manifest ended unexpectedly"
+msgstr "неожиданный конец манифеста"
+
+#: parse_manifest.c:191
+msgid "unexpected object start"
+msgstr "неожиданное начало объекта"
+
+#: parse_manifest.c:224
+msgid "unexpected object end"
+msgstr "неожиданный конец объекта"
+
+#: parse_manifest.c:251
+msgid "unexpected array start"
+msgstr "неожиданное начало массива"
+
+#: parse_manifest.c:274
+msgid "unexpected array end"
+msgstr "неожиданный конец массива"
+
+#: parse_manifest.c:299
+msgid "expected version indicator"
+msgstr "ожидалось указание версии"
+
+#: parse_manifest.c:328
+msgid "unrecognized top-level field"
+msgstr "нераспознанное поле на верхнем уровне"
+
+#: parse_manifest.c:347
+msgid "unexpected file field"
+msgstr "неизвестное поле для файла"
+
+#: parse_manifest.c:361
+msgid "unexpected WAL range field"
+msgstr "неизвестное поле в указании диапазона WAL"
+
+#: parse_manifest.c:367
+msgid "unexpected object field"
+msgstr "неожиданное поле объекта"
+
+#: parse_manifest.c:397
+msgid "unexpected manifest version"
+msgstr "неожиданная версия манифеста"
+
+#: parse_manifest.c:448
+msgid "unexpected scalar"
+msgstr "неожиданное скалярное значение"
+
+#: parse_manifest.c:472
+msgid "missing path name"
+msgstr "отсутствует указание пути"
+
+#: parse_manifest.c:475
+msgid "both path name and encoded path name"
+msgstr "указание пути задано в обычном виде и в закодированном"
+
+#: parse_manifest.c:477
+msgid "missing size"
+msgstr "отсутствует указание размера"
+
+#: parse_manifest.c:480
+msgid "checksum without algorithm"
+msgstr "не задан алгоритм расчёта контрольной суммы"
+
+#: parse_manifest.c:494
+msgid "could not decode file name"
+msgstr "не удалось декодировать имя файла"
+
+#: parse_manifest.c:504
+msgid "file size is not an integer"
+msgstr "размер файла не является целочисленным"
+
+#: parse_manifest.c:510
+#, c-format
+msgid "unrecognized checksum algorithm: \"%s\""
+msgstr "нераспознанный алгоритм расчёта контрольных сумм: \"%s\""
+
+#: parse_manifest.c:529
+#, c-format
+msgid "invalid checksum for file \"%s\": \"%s\""
+msgstr "неверная контрольная сумма для файла \"%s\": \"%s\""
+
+#: parse_manifest.c:572
+msgid "missing timeline"
+msgstr "отсутствует линия времени"
+
+#: parse_manifest.c:574
+msgid "missing start LSN"
+msgstr "отсутствует начальный LSN"
+
+#: parse_manifest.c:576
+msgid "missing end LSN"
+msgstr "отсутствует конечный LSN"
+
+#: parse_manifest.c:582
+msgid "timeline is not an integer"
+msgstr "линия времени задаётся не целым числом"
+
+#: parse_manifest.c:585
+msgid "could not parse start LSN"
+msgstr "не удалось разобрать начальный LSN"
+
+#: parse_manifest.c:588
+msgid "could not parse end LSN"
+msgstr "не удалось разобрать конечный LSN"
+
+#: parse_manifest.c:649
+msgid "expected at least 2 lines"
+msgstr "ожидалось как минимум 2 строки"
+
+#: parse_manifest.c:652
+msgid "last line not newline-terminated"
+msgstr "последняя строка не оканчивается символом новой строки"
+
+#: parse_manifest.c:657
+#, c-format
+msgid "out of memory"
+msgstr "нехватка памяти"
+
+#: parse_manifest.c:659
+#, c-format
+msgid "could not initialize checksum of manifest"
+msgstr "не удалось подготовить контекст контрольной суммы манифеста"
+
+#: parse_manifest.c:661
+#, c-format
+msgid "could not update checksum of manifest"
+msgstr "не удалось изменить контекст контрольной суммы манифеста"
+
+#: parse_manifest.c:664
+#, c-format
+msgid "could not finalize checksum of manifest"
+msgstr "не удалось завершить расчёт контрольной суммы манифеста"
+
+#: parse_manifest.c:668
+#, c-format
+msgid "manifest has no checksum"
+msgstr "в манифесте нет контрольной суммы"
+
+#: parse_manifest.c:672
+#, c-format
+msgid "invalid manifest checksum: \"%s\""
+msgstr "неверная контрольная сумма в манифесте: \"%s\""
+
+#: parse_manifest.c:676
+#, c-format
+msgid "manifest checksum mismatch"
+msgstr "ошибка контрольной суммы манифеста"
+
+#: parse_manifest.c:691
+#, c-format
+msgid "could not parse backup manifest: %s"
+msgstr "не удалось разобрать манифест копии: %s"
+
+#: pg_verifybackup.c:255 pg_verifybackup.c:265 pg_verifybackup.c:277
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "Для дополнительной информации попробуйте \"%s --help\".\n"
+
+#: pg_verifybackup.c:264
+#, c-format
+msgid "no backup directory specified"
+msgstr "каталог копии не указан"
+
+#: pg_verifybackup.c:275
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "слишком много аргументов командной строки (первый: \"%s\")"
+
+#: pg_verifybackup.c:298
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"Программа \"%s\" нужна для %s, но она не найдена\n"
+"в каталоге \"%s\".\n"
+"Проверьте правильность установки СУБД."
+
+#: pg_verifybackup.c:303
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"Программа \"%s\" найдена программой \"%s\",\n"
+"но её версия отличается от версии %s.\n"
+"Проверьте правильность установки СУБД."
+
+#: pg_verifybackup.c:362
+#, c-format
+msgid "backup successfully verified\n"
+msgstr "копия проверена успешно\n"
+
+#: pg_verifybackup.c:388 pg_verifybackup.c:724
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "не удалось открыть файл \"%s\": %m"
+
+#: pg_verifybackup.c:392
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "не удалось получить информацию о файле \"%s\": %m"
+
+#: pg_verifybackup.c:412 pg_verifybackup.c:753
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "не удалось прочитать файл \"%s\": %m"
+
+#: pg_verifybackup.c:415
+#, c-format
+msgid "could not read file \"%s\": read %d of %lld"
+msgstr "не удалось прочитать файл \"%s\" (прочитано байт: %d из %lld)"
+
+#: pg_verifybackup.c:475
+#, c-format
+msgid "duplicate path name in backup manifest: \"%s\""
+msgstr "дублирующийся путь в манифесте копии: \"%s\""
+
+#: pg_verifybackup.c:538 pg_verifybackup.c:545
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "не удалось открыть каталог \"%s\": %m"
+
+#: pg_verifybackup.c:577
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "не удалось закрыть каталог \"%s\": %m"
+
+#: pg_verifybackup.c:597
+#, c-format
+msgid "could not stat file or directory \"%s\": %m"
+msgstr "не удалось получить информацию о файле или каталоге \"%s\": %m"
+
+#: pg_verifybackup.c:620
+#, c-format
+msgid "\"%s\" is not a file or directory"
+msgstr "\"%s\" не указывает на файл или каталог"
+
+#: pg_verifybackup.c:630
+#, c-format
+msgid "\"%s\" is present on disk but not in the manifest"
+msgstr "файл \"%s\" присутствует на диске, но отсутствует в манифесте"
+
+#: pg_verifybackup.c:642
+#, c-format
+msgid "\"%s\" has size %lld on disk but size %zu in the manifest"
+msgstr ""
+"файл \"%s\" имеет размер на диске: %lld, тогда как размер в манифесте: %zu"
+
+#: pg_verifybackup.c:669
+#, c-format
+msgid "\"%s\" is present in the manifest but not on disk"
+msgstr "файл \"%s\" присутствует в манифесте, но отсутствует на диске"
+
+#: pg_verifybackup.c:732
+#, c-format
+msgid "could not initialize checksum of file \"%s\""
+msgstr "не удалось подготовить контекст контрольной суммы файла \"%s\""
+
+#: pg_verifybackup.c:744
+#, c-format
+msgid "could not update checksum of file \"%s\""
+msgstr "не удалось изменить контекст контрольной суммы файла \"%s\""
+
+#: pg_verifybackup.c:759
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "не удалось закрыть файл \"%s\": %m"
+
+#: pg_verifybackup.c:778
+#, c-format
+msgid "file \"%s\" should contain %zu bytes, but read %zu bytes"
+msgstr "файл \"%s\" должен содержать байт: %zu, но фактически прочитано: %zu"
+
+#: pg_verifybackup.c:788
+#, c-format
+msgid "could not finalize checksum of file \"%s\""
+msgstr "не удалось завершить расчёт контрольной суммы файла \"%s\""
+
+#: pg_verifybackup.c:796
+#, c-format
+msgid "file \"%s\" has checksum of length %d, but expected %d"
+msgstr ""
+"для файла \"%s\" задана контрольная сумма размером %d, но ожидаемый размер: "
+"%d"
+
+#: pg_verifybackup.c:800
+#, c-format
+msgid "checksum mismatch for file \"%s\""
+msgstr "ошибка контрольной суммы для файла \"%s\""
+
+#: pg_verifybackup.c:824
+#, c-format
+msgid "WAL parsing failed for timeline %u"
+msgstr "не удалось разобрать WAL для линии времени %u"
+
+#: pg_verifybackup.c:910
+#, c-format
+msgid ""
+"%s verifies a backup against the backup manifest.\n"
+"\n"
+msgstr ""
+"%s проверяет резервную копию, используя манифест копии.\n"
+"\n"
+
+#: pg_verifybackup.c:911
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]... BACKUPDIR\n"
+"\n"
+msgstr ""
+"Использование:\n"
+" %s [ПАРАМЕТР]... КАТАЛОГ_КОПИИ\n"
+"\n"
+
+#: pg_verifybackup.c:912
+#, c-format
+msgid "Options:\n"
+msgstr "Параметры:\n"
+
+#: pg_verifybackup.c:913
+#, c-format
+msgid " -e, --exit-on-error exit immediately on error\n"
+msgstr " -e, --exit-on-error немедленный выход при ошибке\n"
+
+#: pg_verifybackup.c:914
+#, c-format
+msgid " -i, --ignore=RELATIVE_PATH ignore indicated path\n"
+msgstr ""
+" -i, --ignore=ОТНОСИТЕЛЬНЫЙ_ПУТЬ\n"
+" игнорировать заданный путь\n"
+
+#: pg_verifybackup.c:915
+#, c-format
+msgid " -m, --manifest-path=PATH use specified path for manifest\n"
+msgstr " -m, --manifest-path=ПУТЬ использовать заданный файл манифеста\n"
+
+#: pg_verifybackup.c:916
+#, c-format
+msgid " -n, --no-parse-wal do not try to parse WAL files\n"
+msgstr " -n, --no-parse-wal не пытаться разбирать файлы WAL\n"
+
+#: pg_verifybackup.c:917
+#, c-format
+msgid ""
+" -q, --quiet do not print any output, except for errors\n"
+msgstr ""
+" -q, --quiet не выводить никаких сообщений, кроме ошибок\n"
+
+#: pg_verifybackup.c:918
+#, c-format
+msgid " -s, --skip-checksums skip checksum verification\n"
+msgstr " -s, --skip-checksums пропустить проверку контрольных сумм\n"
+
+#: pg_verifybackup.c:919
+#, c-format
+msgid " -w, --wal-directory=PATH use specified path for WAL files\n"
+msgstr ""
+" -w, --wal-directory=ПУТЬ использовать заданный путь к файлам WAL\n"
+
+#: pg_verifybackup.c:920
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version показать версию и выйти\n"
+
+#: pg_verifybackup.c:921
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help показать эту справку и выйти\n"
+
+#: pg_verifybackup.c:922
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"Об ошибках сообщайте по адресу <%s>.\n"
+
+#: pg_verifybackup.c:923
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "Домашняя страница %s: <%s>\n"
diff --git a/src/bin/pg_verifybackup/po/sv.po b/src/bin/pg_verifybackup/po/sv.po
new file mode 100644
index 0000000..14b42a3
--- /dev/null
+++ b/src/bin/pg_verifybackup/po/sv.po
@@ -0,0 +1,503 @@
+# Swedish message translation file for pg_verifybackup
+# Copyright (C) 2020 PostgreSQL Global Development Group
+# This file is distributed under the same license as the pg_verifybackup (PostgreSQL) package.
+# Dennis Björklund <db@zigo.dhs.org>, 2020, 2021
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PostgreSQL 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-11-07 06:44+0000\n"
+"PO-Revision-Date: 2021-11-07 10:18+0100\n"
+"Last-Translator: Dennis Björklund <db@zigo.dhs.org>\n"
+"Language-Team: Swedish <pgsql-translators@postgresql.org>\n"
+"Language: sv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "fatalt: "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "fel: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "varning: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "slut på minne\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "kan inte duplicera null-pekare (internt fel)\n"
+
+#: ../../common/jsonapi.c:1066
+#, c-format
+msgid "Escape sequence \"\\%s\" is invalid."
+msgstr "Escape-sekvens \"\\%s\" är ogiltig."
+
+#: ../../common/jsonapi.c:1069
+#, c-format
+msgid "Character with value 0x%02x must be escaped."
+msgstr "Tecken med värde 0x%02x måste escape:as."
+
+#: ../../common/jsonapi.c:1072
+#, c-format
+msgid "Expected end of input, but found \"%s\"."
+msgstr "Förväntade slut på indata, men hittade \"%s\"."
+
+#: ../../common/jsonapi.c:1075
+#, c-format
+msgid "Expected array element or \"]\", but found \"%s\"."
+msgstr "Färväntade array-element eller \"]\", men hittade \"%s\"."
+
+#: ../../common/jsonapi.c:1078
+#, c-format
+msgid "Expected \",\" or \"]\", but found \"%s\"."
+msgstr "Förväntade \",\" eller \"]\", men hittade \"%s\"."
+
+#: ../../common/jsonapi.c:1081
+#, c-format
+msgid "Expected \":\", but found \"%s\"."
+msgstr "Förväntade sig \":\" men hittade \"%s\"."
+
+#: ../../common/jsonapi.c:1084
+#, c-format
+msgid "Expected JSON value, but found \"%s\"."
+msgstr "Förväntade JSON-värde, men hittade \"%s\"."
+
+#: ../../common/jsonapi.c:1087
+msgid "The input string ended unexpectedly."
+msgstr "Indatasträngen avslutades oväntat."
+
+#: ../../common/jsonapi.c:1089
+#, c-format
+msgid "Expected string or \"}\", but found \"%s\"."
+msgstr "Färväntade sträng eller \"}\", men hittade \"%s\"."
+
+#: ../../common/jsonapi.c:1092
+#, c-format
+msgid "Expected \",\" or \"}\", but found \"%s\"."
+msgstr "Förväntade sig \",\" eller \"}\" men hittade \"%s\"."
+
+#: ../../common/jsonapi.c:1095
+#, c-format
+msgid "Expected string, but found \"%s\"."
+msgstr "Förväntade sträng, men hittade \"%s\"."
+
+#: ../../common/jsonapi.c:1098
+#, c-format
+msgid "Token \"%s\" is invalid."
+msgstr "Token \"%s\" är ogiltig."
+
+#: ../../common/jsonapi.c:1101
+msgid "\\u0000 cannot be converted to text."
+msgstr "\\u0000 kan inte konverteras till text."
+
+#: ../../common/jsonapi.c:1103
+msgid "\"\\u\" must be followed by four hexadecimal digits."
+msgstr "\"\\u\" måste följas av fyra hexdecimala siffror."
+
+#: ../../common/jsonapi.c:1106
+msgid "Unicode escape values cannot be used for code point values above 007F when the encoding is not UTF8."
+msgstr "Escape-värden för unicode kan inte användas för kodpunkter med värde över 007F när kodningen inte är UTF8."
+
+#: ../../common/jsonapi.c:1108
+msgid "Unicode high surrogate must not follow a high surrogate."
+msgstr "Unicodes övre surrogathalva får inte komma efter en övre surrogathalva."
+
+#: ../../common/jsonapi.c:1110
+msgid "Unicode low surrogate must follow a high surrogate."
+msgstr "Unicodes lägre surrogathalva måste följa en övre surrogathalva."
+
+#: parse_manifest.c:152
+msgid "manifest ended unexpectedly"
+msgstr "manifestet avslutades oväntat"
+
+#: parse_manifest.c:191
+msgid "unexpected object start"
+msgstr "oväntad objektstart"
+
+#: parse_manifest.c:224
+msgid "unexpected object end"
+msgstr "oväntat objektslut"
+
+#: parse_manifest.c:251
+msgid "unexpected array start"
+msgstr "oväntad array-start"
+
+#: parse_manifest.c:274
+msgid "unexpected array end"
+msgstr "oväntat array-slut"
+
+#: parse_manifest.c:299
+msgid "expected version indicator"
+msgstr "förväntade en versionsindikator"
+
+#: parse_manifest.c:328
+msgid "unrecognized top-level field"
+msgstr "okänt toppnivåfält"
+
+#: parse_manifest.c:347
+msgid "unexpected file field"
+msgstr "oväntat filfält"
+
+#: parse_manifest.c:361
+msgid "unexpected WAL range field"
+msgstr "oväntat WAL-intervall-fält"
+
+#: parse_manifest.c:367
+msgid "unexpected object field"
+msgstr "oväntat objektfält"
+
+#: parse_manifest.c:397
+msgid "unexpected manifest version"
+msgstr "oväntad manifestversion"
+
+#: parse_manifest.c:448
+msgid "unexpected scalar"
+msgstr "oväntad skalar"
+
+#: parse_manifest.c:472
+msgid "missing path name"
+msgstr "saknas sökväg"
+
+#: parse_manifest.c:475
+msgid "both path name and encoded path name"
+msgstr "både sökväg och kodad sökväg"
+
+#: parse_manifest.c:477
+msgid "missing size"
+msgstr "saknar storlek"
+
+#: parse_manifest.c:480
+msgid "checksum without algorithm"
+msgstr "kontrollsumma utan algoritm"
+
+#: parse_manifest.c:494
+msgid "could not decode file name"
+msgstr "kunde inte avkoda filnamn"
+
+#: parse_manifest.c:504
+msgid "file size is not an integer"
+msgstr "filstorlek är inte ett haltal"
+
+#: parse_manifest.c:510
+#, c-format
+msgid "unrecognized checksum algorithm: \"%s\""
+msgstr "okänd algoritm för kontrollsumma: \"%s\""
+
+#: parse_manifest.c:529
+#, c-format
+msgid "invalid checksum for file \"%s\": \"%s\""
+msgstr "ogiltig kontrollsumma för fil \"%s\": \"%s\""
+
+#: parse_manifest.c:572
+msgid "missing timeline"
+msgstr "saknar tidslinje"
+
+#: parse_manifest.c:574
+msgid "missing start LSN"
+msgstr "saknar start-LSN"
+
+#: parse_manifest.c:576
+msgid "missing end LSN"
+msgstr "saknar slut-LSN"
+
+#: parse_manifest.c:582
+msgid "timeline is not an integer"
+msgstr "tidslinje är inte ett heltal"
+
+#: parse_manifest.c:585
+msgid "could not parse start LSN"
+msgstr "kunde inte parsa start-LSN"
+
+#: parse_manifest.c:588
+msgid "could not parse end LSN"
+msgstr "kunde inte parsa slut-LSN"
+
+#: parse_manifest.c:649
+msgid "expected at least 2 lines"
+msgstr "förväntade minst två rader"
+
+#: parse_manifest.c:652
+msgid "last line not newline-terminated"
+msgstr "sista raden är inte nyradsterminerad"
+
+#: parse_manifest.c:657
+#, c-format
+msgid "out of memory"
+msgstr "slut på minne"
+
+#: parse_manifest.c:659
+#, c-format
+msgid "could not initialize checksum of manifest"
+msgstr "kunde inte initiera kontrollsumma för backup-manifest"
+
+#: parse_manifest.c:661
+#, c-format
+msgid "could not update checksum of manifest"
+msgstr "kunde inte uppdatera kontrollsumma för backup-manifest"
+
+#: parse_manifest.c:664
+#, c-format
+msgid "could not finalize checksum of manifest"
+msgstr "kunde inte göra klart kontrollsumma för backup-manifest"
+
+#: parse_manifest.c:668
+#, c-format
+msgid "manifest has no checksum"
+msgstr "manifestet har ingen kontrollsumma"
+
+#: parse_manifest.c:672
+#, c-format
+msgid "invalid manifest checksum: \"%s\""
+msgstr "ogiltig kontrollsumma för manifest: \"%s\""
+
+#: parse_manifest.c:676
+#, c-format
+msgid "manifest checksum mismatch"
+msgstr "kontrollsumma för manifest matchar inte"
+
+#: parse_manifest.c:691
+#, c-format
+msgid "could not parse backup manifest: %s"
+msgstr "kunde inte parsa backup-manifest: %s"
+
+#: pg_verifybackup.c:255 pg_verifybackup.c:265 pg_verifybackup.c:277
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "Försök med \"%s --help\" för mer information.\n"
+
+#: pg_verifybackup.c:264
+#, c-format
+msgid "no backup directory specified"
+msgstr "ingen backup-katalog angiven"
+
+#: pg_verifybackup.c:275
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "för många kommandoradsargument (första är \"%s\")"
+
+#: pg_verifybackup.c:298
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"Programmet \"%s\" behövs av %s men hittades inte i samma\n"
+"katalog som \"%s\".\n"
+"Kontrollera din installation."
+
+#: pg_verifybackup.c:303
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"Programmet \"%s\" hittades av \"%s\"\n"
+"men är inte av samma version som %s.\n"
+"Kontrollera din installation."
+
+#: pg_verifybackup.c:362
+#, c-format
+msgid "backup successfully verified\n"
+msgstr "korrekt verifierad backup\n"
+
+#: pg_verifybackup.c:388 pg_verifybackup.c:724
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "kunde inte öppna fil \"%s\": %m"
+
+#: pg_verifybackup.c:392
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "kunde inte göra stat() på fil \"%s\": %m"
+
+#: pg_verifybackup.c:412 pg_verifybackup.c:753
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "kunde inte läsa fil \"%s\": %m"
+
+#: pg_verifybackup.c:415
+#, c-format
+msgid "could not read file \"%s\": read %d of %lld"
+msgstr "kunde inte läsa fil \"%s\": läste %d av %lld"
+
+#: pg_verifybackup.c:475
+#, c-format
+msgid "duplicate path name in backup manifest: \"%s\""
+msgstr "duplicerad sökväg i backup-manifest: \"%s\""
+
+#: pg_verifybackup.c:538 pg_verifybackup.c:545
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "kunde inte öppna katalog \"%s\": %m"
+
+#: pg_verifybackup.c:577
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "kunde inte stänga katalog \"%s\": %m"
+
+#: pg_verifybackup.c:597
+#, c-format
+msgid "could not stat file or directory \"%s\": %m"
+msgstr "kunde inte ta status på fil eller katalog \"%s\": %m"
+
+#: pg_verifybackup.c:620
+#, c-format
+msgid "\"%s\" is not a file or directory"
+msgstr "\"%s\" är inte en fil eller katalog"
+
+#: pg_verifybackup.c:630
+#, c-format
+msgid "\"%s\" is present on disk but not in the manifest"
+msgstr "\"%s\" finns på disk men är inte i manifestet"
+
+#: pg_verifybackup.c:642
+#, c-format
+msgid "\"%s\" has size %lld on disk but size %zu in the manifest"
+msgstr "\"%s\" har storlek %lld på disk men storlek %zu i manifestet"
+
+#: pg_verifybackup.c:669
+#, c-format
+msgid "\"%s\" is present in the manifest but not on disk"
+msgstr "\"%s\" finns i manifestet men inte på disk"
+
+#: pg_verifybackup.c:732
+#, c-format
+msgid "could not initialize checksum of file \"%s\""
+msgstr "kunde inte initiera kontrollsumma för filen \"%s\""
+
+#: pg_verifybackup.c:744
+#, c-format
+msgid "could not update checksum of file \"%s\""
+msgstr "kunde inte uppdatera kontrollsumma för filen \"%s\""
+
+#: pg_verifybackup.c:759
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "kunde inte stänga fil \"%s\": %m"
+
+#: pg_verifybackup.c:778
+#, c-format
+msgid "file \"%s\" should contain %zu bytes, but read %zu bytes"
+msgstr "filen \"%s\" skall innehålla %zu byte men vi läste %zu byte"
+
+#: pg_verifybackup.c:788
+#, c-format
+msgid "could not finalize checksum of file \"%s\""
+msgstr "kunde inte göra klart kontrollsumma för filen \"%s\""
+
+#: pg_verifybackup.c:796
+#, c-format
+msgid "file \"%s\" has checksum of length %d, but expected %d"
+msgstr "filen \"%s\" har kontrollsumma med längd %d men förväntade %d"
+
+#: pg_verifybackup.c:800
+#, c-format
+msgid "checksum mismatch for file \"%s\""
+msgstr "kontrollsumman matchar inte för fil \"%s\""
+
+#: pg_verifybackup.c:824
+#, c-format
+msgid "WAL parsing failed for timeline %u"
+msgstr "WAL-parsning misslyckades för tidslinje %u"
+
+#: pg_verifybackup.c:910
+#, c-format
+msgid ""
+"%s verifies a backup against the backup manifest.\n"
+"\n"
+msgstr ""
+"%s verifierar en backup gentemot backup-manifestet.\n"
+"\n"
+
+#: pg_verifybackup.c:911
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]... BACKUPDIR\n"
+"\n"
+msgstr ""
+"Användning:\n"
+" %s [FLAGGOR]... BACKUPKAT\n"
+"\n"
+
+#: pg_verifybackup.c:912
+#, c-format
+msgid "Options:\n"
+msgstr "Flaggor:\n"
+
+#: pg_verifybackup.c:913
+#, c-format
+msgid " -e, --exit-on-error exit immediately on error\n"
+msgstr " -e, --exit-on-error avsluta direkt vid fel\n"
+
+#: pg_verifybackup.c:914
+#, c-format
+msgid " -i, --ignore=RELATIVE_PATH ignore indicated path\n"
+msgstr " -i, --ignore=RELATIV_SÖKVÄG hoppa över angiven sökväg\n"
+
+#: pg_verifybackup.c:915
+#, c-format
+msgid " -m, --manifest-path=PATH use specified path for manifest\n"
+msgstr " -m, --manifest-path=SÖKVÄG använd denna sökväg till manifestet\n"
+
+#: pg_verifybackup.c:916
+#, c-format
+msgid " -n, --no-parse-wal do not try to parse WAL files\n"
+msgstr " -n, --no-parse-wal försök inte parsa WAL-filer\n"
+
+#: pg_verifybackup.c:917
+#, c-format
+msgid " -q, --quiet do not print any output, except for errors\n"
+msgstr " -q, --quiet skriv inte ut några meddelanden förutom fel\n"
+
+#: pg_verifybackup.c:918
+#, c-format
+msgid " -s, --skip-checksums skip checksum verification\n"
+msgstr " -s, --skip-checksums hoppa över verifiering av kontrollsummor\n"
+
+#: pg_verifybackup.c:919
+#, c-format
+msgid " -w, --wal-directory=PATH use specified path for WAL files\n"
+msgstr " -w, --wal-directory=SÖKVÄG använd denna sökväg till WAL-filer\n"
+
+#: pg_verifybackup.c:920
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version visa versionsinformation, avsluta sedan\n"
+
+#: pg_verifybackup.c:921
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help visa denna hjälp, avsluta sedan\n"
+
+#: pg_verifybackup.c:922
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"Rapportera fel till <%s>.\n"
+
+#: pg_verifybackup.c:923
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "hemsida för %s: <%s>\n"
diff --git a/src/bin/pg_verifybackup/po/uk.po b/src/bin/pg_verifybackup/po/uk.po
new file mode 100644
index 0000000..4fa75e7
--- /dev/null
+++ b/src/bin/pg_verifybackup/po/uk.po
@@ -0,0 +1,488 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: postgresql\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-03-16 09:15+0000\n"
+"PO-Revision-Date: 2022-06-19 10:10\n"
+"Last-Translator: \n"
+"Language-Team: Ukrainian\n"
+"Language: uk_UA\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
+"X-Crowdin-Project: postgresql\n"
+"X-Crowdin-Project-ID: 324573\n"
+"X-Crowdin-Language: uk\n"
+"X-Crowdin-File: /REL_14_STABLE/pg_verifybackup.pot\n"
+"X-Crowdin-File-ID: 756\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "збій: "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "помилка: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "попередження: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "недостатньо пам'яті\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "неможливо дублювати нульовий покажчик (внутрішня помилка)\n"
+
+#: ../../common/jsonapi.c:1066
+#, c-format
+msgid "Escape sequence \"\\%s\" is invalid."
+msgstr "Неприпустима спеціальна послідовність \"\\%s\"."
+
+#: ../../common/jsonapi.c:1069
+#, c-format
+msgid "Character with value 0x%02x must be escaped."
+msgstr "Символ зі значенням 0x%02x повинен бути пропущений."
+
+#: ../../common/jsonapi.c:1072
+#, c-format
+msgid "Expected end of input, but found \"%s\"."
+msgstr "Очікувався кінець введення, але знайдено \"%s\"."
+
+#: ../../common/jsonapi.c:1075
+#, c-format
+msgid "Expected array element or \"]\", but found \"%s\"."
+msgstr "Очікувався елемент масиву або \"]\", але знайдено \"%s\"."
+
+#: ../../common/jsonapi.c:1078
+#, c-format
+msgid "Expected \",\" or \"]\", but found \"%s\"."
+msgstr "Очікувалось \",\" або \"]\", але знайдено \"%s\"."
+
+#: ../../common/jsonapi.c:1081
+#, c-format
+msgid "Expected \":\", but found \"%s\"."
+msgstr "Очікувалось \":\", але знайдено \"%s\"."
+
+#: ../../common/jsonapi.c:1084
+#, c-format
+msgid "Expected JSON value, but found \"%s\"."
+msgstr "Очікувалось значення JSON, але знайдено \"%s\"."
+
+#: ../../common/jsonapi.c:1087
+msgid "The input string ended unexpectedly."
+msgstr "Несподіваний кінець вхідного рядка."
+
+#: ../../common/jsonapi.c:1089
+#, c-format
+msgid "Expected string or \"}\", but found \"%s\"."
+msgstr "Очікувався рядок або \"}\", але знайдено \"%s\"."
+
+#: ../../common/jsonapi.c:1092
+#, c-format
+msgid "Expected \",\" or \"}\", but found \"%s\"."
+msgstr "Очікувалось \",\" або \"}\", але знайдено \"%s\"."
+
+#: ../../common/jsonapi.c:1095
+#, c-format
+msgid "Expected string, but found \"%s\"."
+msgstr "Очікувався рядок, але знайдено \"%s\"."
+
+#: ../../common/jsonapi.c:1098
+#, c-format
+msgid "Token \"%s\" is invalid."
+msgstr "Неприпустимий маркер \"%s\"."
+
+#: ../../common/jsonapi.c:1101
+msgid "\\u0000 cannot be converted to text."
+msgstr "\\u0000 не можна перетворити в текст."
+
+#: ../../common/jsonapi.c:1103
+msgid "\"\\u\" must be followed by four hexadecimal digits."
+msgstr "За \"\\u\" повинні прямувати чотири шістнадцяткових числа."
+
+#: ../../common/jsonapi.c:1106
+msgid "Unicode escape values cannot be used for code point values above 007F when the encoding is not UTF8."
+msgstr "Значення виходу Unicode не можна використовувати для значень кодових точок більше 007F, якщо кодування не UTF8."
+
+#: ../../common/jsonapi.c:1108
+msgid "Unicode high surrogate must not follow a high surrogate."
+msgstr "Старший сурогат Unicode не повинен прямувати за іншим старшим сурогатом."
+
+#: ../../common/jsonapi.c:1110
+msgid "Unicode low surrogate must follow a high surrogate."
+msgstr "Молодший сурогат Unicode не повинен прямувати за іншим молодшим сурогатом."
+
+#: parse_manifest.c:152
+msgid "manifest ended unexpectedly"
+msgstr "маніфест закінчився несподівано"
+
+#: parse_manifest.c:191
+msgid "unexpected object start"
+msgstr "неочікуваний початок об'єкта"
+
+#: parse_manifest.c:224
+msgid "unexpected object end"
+msgstr "неочікуваний кінець об'єкта"
+
+#: parse_manifest.c:251
+msgid "unexpected array start"
+msgstr "неочікуваний початок масиву"
+
+#: parse_manifest.c:274
+msgid "unexpected array end"
+msgstr "неочікуваний кінець масиву"
+
+#: parse_manifest.c:299
+msgid "expected version indicator"
+msgstr "індикатор очікуваної версії"
+
+#: parse_manifest.c:328
+msgid "unrecognized top-level field"
+msgstr "нерозпізнане поле верхнього рівня"
+
+#: parse_manifest.c:347
+msgid "unexpected file field"
+msgstr "неочікуване поле файлу"
+
+#: parse_manifest.c:361
+msgid "unexpected WAL range field"
+msgstr "неочікуване поле діапазону WAL"
+
+#: parse_manifest.c:367
+msgid "unexpected object field"
+msgstr "неочікуване поле об'єкта"
+
+#: parse_manifest.c:397
+msgid "unexpected manifest version"
+msgstr "неочікувана версія маніфесту"
+
+#: parse_manifest.c:448
+msgid "unexpected scalar"
+msgstr "неочікуваний скаляр"
+
+#: parse_manifest.c:472
+msgid "missing path name"
+msgstr "пропущено шлях"
+
+#: parse_manifest.c:475
+msgid "both path name and encoded path name"
+msgstr "і ім'я шляху, і закодований шлях"
+
+#: parse_manifest.c:477
+msgid "missing size"
+msgstr "відсутній розмір"
+
+#: parse_manifest.c:480
+msgid "checksum without algorithm"
+msgstr "контрольна сума без алгоритму"
+
+#: parse_manifest.c:494
+msgid "could not decode file name"
+msgstr "не вдалося декодувати ім'я файлу"
+
+#: parse_manifest.c:504
+msgid "file size is not an integer"
+msgstr "розмір файлу не є цілим числом"
+
+#: parse_manifest.c:510
+#, c-format
+msgid "unrecognized checksum algorithm: \"%s\""
+msgstr "нерозпізнаний алгоритм контрольної суми: \"%s\""
+
+#: parse_manifest.c:529
+#, c-format
+msgid "invalid checksum for file \"%s\": \"%s\""
+msgstr "неприпустима контрольна сума для файлу \"%s\": \"%s\""
+
+#: parse_manifest.c:572
+msgid "missing timeline"
+msgstr "відсутня часова шкала"
+
+#: parse_manifest.c:574
+msgid "missing start LSN"
+msgstr "відсутній LSN початку"
+
+#: parse_manifest.c:576
+msgid "missing end LSN"
+msgstr "відсутній LSN кінця"
+
+#: parse_manifest.c:582
+msgid "timeline is not an integer"
+msgstr "часова лінія не є цілим числом"
+
+#: parse_manifest.c:585
+msgid "could not parse start LSN"
+msgstr "не вдалося проаналізувати початковий LSN"
+
+#: parse_manifest.c:588
+msgid "could not parse end LSN"
+msgstr "не вдалося проаналізувати кінцевий LSN"
+
+#: parse_manifest.c:649
+msgid "expected at least 2 lines"
+msgstr "очікувалося принаймні 2 рядки"
+
+#: parse_manifest.c:652
+msgid "last line not newline-terminated"
+msgstr "останній рядок не завершений новим рядком"
+
+#: parse_manifest.c:657
+#, c-format
+msgid "out of memory"
+msgstr "недостатньо пам'яті"
+
+#: parse_manifest.c:659
+#, c-format
+msgid "could not initialize checksum of manifest"
+msgstr "не вдалося ініціалізувати контрольну суму маніфесту"
+
+#: parse_manifest.c:661
+#, c-format
+msgid "could not update checksum of manifest"
+msgstr "не вдалося оновити контрольну суму маніфесту"
+
+#: parse_manifest.c:664
+#, c-format
+msgid "could not finalize checksum of manifest"
+msgstr "не вдалося остаточно завершити контрольну суму маніфесту"
+
+#: parse_manifest.c:668
+#, c-format
+msgid "manifest has no checksum"
+msgstr "у маніфесті немає контрольної суми"
+
+#: parse_manifest.c:672
+#, c-format
+msgid "invalid manifest checksum: \"%s\""
+msgstr "неприпустима контрольна сума маніфесту: \"%s\""
+
+#: parse_manifest.c:676
+#, c-format
+msgid "manifest checksum mismatch"
+msgstr "невідповідність контрольної суми маніфесту"
+
+#: parse_manifest.c:691
+#, c-format
+msgid "could not parse backup manifest: %s"
+msgstr "не вдалося проаналізувати маніфест резервної копії: %s"
+
+#: pg_verifybackup.c:255 pg_verifybackup.c:265 pg_verifybackup.c:277
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "Спробуйте \"%s --help\" для додаткової інформації.\n"
+
+#: pg_verifybackup.c:264
+#, c-format
+msgid "no backup directory specified"
+msgstr "не вказано папку резервної копії"
+
+#: pg_verifybackup.c:275
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "забагато аргументів у командному рядку (перший \"%s\")"
+
+#: pg_verifybackup.c:298
+#, c-format
+msgid "The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr "Програма \"%s\" потрібна для %s, але не знайдена в тому ж каталозі, що й \"%s\".\n"
+"Перевірте вашу установку."
+
+#: pg_verifybackup.c:303
+#, c-format
+msgid "The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr "Програма \"%s\" була знайдена \"%s\", але не була тієї ж версії, що %s.\n"
+"Перевірте вашу установку."
+
+#: pg_verifybackup.c:362
+#, c-format
+msgid "backup successfully verified\n"
+msgstr "резервну копію успішно перевірено\n"
+
+#: pg_verifybackup.c:388 pg_verifybackup.c:724
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "не можливо відкрити файл \"%s\": %m"
+
+#: pg_verifybackup.c:392
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "не вдалося отримати інформацію від файлу \"%s\": %m"
+
+#: pg_verifybackup.c:412 pg_verifybackup.c:753
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "не вдалося прочитати файл \"%s\": %m"
+
+#: pg_verifybackup.c:415
+#, c-format
+msgid "could not read file \"%s\": read %d of %lld"
+msgstr "не вдалося прочитати файл \"%s\": прочитано %d з %lld"
+
+#: pg_verifybackup.c:475
+#, c-format
+msgid "duplicate path name in backup manifest: \"%s\""
+msgstr "дубльований шлях у маніфесті резервного копіювання: \"%s\""
+
+#: pg_verifybackup.c:538 pg_verifybackup.c:545
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "не вдалося відкрити каталог \"%s\": %m"
+
+#: pg_verifybackup.c:577
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "не вдалося закрити каталог \"%s\": %m"
+
+#: pg_verifybackup.c:597
+#, c-format
+msgid "could not stat file or directory \"%s\": %m"
+msgstr "не вдалося отримати інформацію про файл або каталог \"%s\": %m"
+
+#: pg_verifybackup.c:620
+#, c-format
+msgid "\"%s\" is not a file or directory"
+msgstr "\"%s\" не є файлом або каталогом"
+
+#: pg_verifybackup.c:630
+#, c-format
+msgid "\"%s\" is present on disk but not in the manifest"
+msgstr "\"%s\" присутній на диску, але не у маніфесті"
+
+#: pg_verifybackup.c:642
+#, c-format
+msgid "\"%s\" has size %lld on disk but size %zu in the manifest"
+msgstr "\"%s\" має розмір %lld на диску, але розмір %zu у маніфесті"
+
+#: pg_verifybackup.c:669
+#, c-format
+msgid "\"%s\" is present in the manifest but not on disk"
+msgstr "\"%s\" присутній у маніфесті, але не на диску"
+
+#: pg_verifybackup.c:732
+#, c-format
+msgid "could not initialize checksum of file \"%s\""
+msgstr "не вдалося ініціалізувати контрольну суму файлу \"%s\""
+
+#: pg_verifybackup.c:744
+#, c-format
+msgid "could not update checksum of file \"%s\""
+msgstr "не вдалося оновити контрольну суму файлу \"%s\""
+
+#: pg_verifybackup.c:759
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "неможливо закрити файл \"%s\": %m"
+
+#: pg_verifybackup.c:778
+#, c-format
+msgid "file \"%s\" should contain %zu bytes, but read %zu bytes"
+msgstr "файл \"%s\" мусить містити %zu байтів, але прочитано %zu байтів"
+
+#: pg_verifybackup.c:788
+#, c-format
+msgid "could not finalize checksum of file \"%s\""
+msgstr "не вдалося остаточно завершити контрольну суму файлу \"%s\""
+
+#: pg_verifybackup.c:796
+#, c-format
+msgid "file \"%s\" has checksum of length %d, but expected %d"
+msgstr "файл \"%s\" має контрольну суму довжини %d, але очікувалось %d"
+
+#: pg_verifybackup.c:800
+#, c-format
+msgid "checksum mismatch for file \"%s\""
+msgstr "невідповідність контрольної суми для файлу \"%s\""
+
+#: pg_verifybackup.c:824
+#, c-format
+msgid "WAL parsing failed for timeline %u"
+msgstr "не вдалося проаналізувати WAL для часової шкали %u"
+
+#: pg_verifybackup.c:910
+#, c-format
+msgid "%s verifies a backup against the backup manifest.\n\n"
+msgstr "%s перевіряє резервну копію відповідно до маніфесту резервного копіювання.\n\n"
+
+#: pg_verifybackup.c:911
+#, c-format
+msgid "Usage:\n"
+" %s [OPTION]... BACKUPDIR\n\n"
+msgstr "Використання:\n"
+" %s [OPTION]... КАТАЛОГ_КОПІЮВАННЯ\n\n"
+
+#: pg_verifybackup.c:912
+#, c-format
+msgid "Options:\n"
+msgstr "Параметри:\n"
+
+#: pg_verifybackup.c:913
+#, c-format
+msgid " -e, --exit-on-error exit immediately on error\n"
+msgstr " -e, --exit-on-error вийти при помилці\n"
+
+#: pg_verifybackup.c:914
+#, c-format
+msgid " -i, --ignore=RELATIVE_PATH ignore indicated path\n"
+msgstr " -i, --ignore=RELATIVE_PATH ігнорувати вказаний шлях\n"
+
+#: pg_verifybackup.c:915
+#, c-format
+msgid " -m, --manifest-path=PATH use specified path for manifest\n"
+msgstr " -m, --manifest-path=PATH використовувати вказаний шлях для маніфесту\n"
+
+#: pg_verifybackup.c:916
+#, c-format
+msgid " -n, --no-parse-wal do not try to parse WAL files\n"
+msgstr " -n, --no-parse-wal не намагатися аналізувати файли WAL\n"
+
+#: pg_verifybackup.c:917
+#, c-format
+msgid " -q, --quiet do not print any output, except for errors\n"
+msgstr " -q, --quiet не друкувати жодного виводу, окрім помилок\n"
+
+#: pg_verifybackup.c:918
+#, c-format
+msgid " -s, --skip-checksums skip checksum verification\n"
+msgstr " -s, --skip-checksums не перевіряти контрольні суми\n"
+
+#: pg_verifybackup.c:919
+#, c-format
+msgid " -w, --wal-directory=PATH use specified path for WAL files\n"
+msgstr " -w, --wal-directory=PATH використовувати вказаний шлях для файлів WAL\n"
+
+#: pg_verifybackup.c:920
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version вивести інформацію про версію, потім вийти\n"
+
+#: pg_verifybackup.c:921
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help показати цю довідку, потім вийти\n"
+
+#: pg_verifybackup.c:922
+#, c-format
+msgid "\n"
+"Report bugs to <%s>.\n"
+msgstr "\n"
+"Повідомляти про помилки на <%s>.\n"
+
+#: pg_verifybackup.c:923
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "Домашня сторінка %s: <%s>\n"
+
diff --git a/src/bin/pg_verifybackup/po/zh_CN.po b/src/bin/pg_verifybackup/po/zh_CN.po
new file mode 100644
index 0000000..b7d97c8
--- /dev/null
+++ b/src/bin/pg_verifybackup/po/zh_CN.po
@@ -0,0 +1,494 @@
+# LANGUAGE message translation file for pg_verifybackup
+# Copyright (C) 2020 PostgreSQL Global Development Group
+# This file is distributed under the same license as the pg_verifybackup (PostgreSQL) package.
+# FIRST AUTHOR <zhangjie2@fujitsu.com>, 2020.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_verifybackup (PostgreSQL) 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-08-14 05:45+0000\n"
+"PO-Revision-Date: 2021-08-15 16:00+0800\n"
+"Last-Translator: Jie Zhang <zhangjie2@fujitsu.com>\n"
+"Language-Team: Chinese (Simplified) <zhangjie2@fujitsu.com>\n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "致命的:"
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "错误: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "警告: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "内存不足\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "无法复制空指针 (内部错误)\n"
+
+#: ../../common/jsonapi.c:1066
+#, c-format
+msgid "Escape sequence \"\\%s\" is invalid."
+msgstr "转义序列 \"\\%s\" 无效."
+
+#: ../../common/jsonapi.c:1069
+#, c-format
+msgid "Character with value 0x%02x must be escaped."
+msgstr "值为 0x%02x 的字符必须进行转义处理."
+
+#: ../../common/jsonapi.c:1072
+#, c-format
+msgid "Expected end of input, but found \"%s\"."
+msgstr "期望输入结束,结果发现是\"%s\"."
+
+#: ../../common/jsonapi.c:1075
+#, c-format
+msgid "Expected array element or \"]\", but found \"%s\"."
+msgstr "期望为数组元素或者\"]\",但发现结果是\"%s\"."
+
+#: ../../common/jsonapi.c:1078
+#, c-format
+msgid "Expected \",\" or \"]\", but found \"%s\"."
+msgstr "期望是\",\" 或 \"]\",但发现结果是\"%s\"."
+
+#: ../../common/jsonapi.c:1081
+#, c-format
+msgid "Expected \":\", but found \"%s\"."
+msgstr "期望得到 \":\",但发现结果是\"%s\"."
+
+#: ../../common/jsonapi.c:1084
+#, c-format
+msgid "Expected JSON value, but found \"%s\"."
+msgstr "期望是JSON值, 但结果发现是\"%s\"."
+
+#: ../../common/jsonapi.c:1087
+msgid "The input string ended unexpectedly."
+msgstr "输入字符串意外终止."
+
+#: ../../common/jsonapi.c:1089
+#, c-format
+msgid "Expected string or \"}\", but found \"%s\"."
+msgstr "期望是字符串或\"}\",但发现结果是\"%s\"."
+
+#: ../../common/jsonapi.c:1092
+#, c-format
+msgid "Expected \",\" or \"}\", but found \"%s\"."
+msgstr "期望是 \",\" 或 \"}\",但发现结果是\"%s\"."
+
+#: ../../common/jsonapi.c:1095
+#, c-format
+msgid "Expected string, but found \"%s\"."
+msgstr "期望是字符串, 但发现结果是\"%s\"."
+
+#: ../../common/jsonapi.c:1098
+#, c-format
+msgid "Token \"%s\" is invalid."
+msgstr "令牌 \"%s\" 无效."
+
+#: ../../common/jsonapi.c:1101
+msgid "\\u0000 cannot be converted to text."
+msgstr "\\u0000不能被转换为文本。"
+
+#: ../../common/jsonapi.c:1103
+msgid "\"\\u\" must be followed by four hexadecimal digits."
+msgstr "\"\\u\" 后必须紧跟有效的十六进制数数字"
+
+#: ../../common/jsonapi.c:1106
+msgid "Unicode escape values cannot be used for code point values above 007F when the encoding is not UTF8."
+msgstr "当编码不是UTF8时,大于007F的码位值不能使用Unicode转义值."
+
+#: ../../common/jsonapi.c:1108
+msgid "Unicode high surrogate must not follow a high surrogate."
+msgstr "Unicode 的高位代理项不能紧随另一个高位代理项."
+
+#: ../../common/jsonapi.c:1110
+msgid "Unicode low surrogate must follow a high surrogate."
+msgstr "Unicode 代位代理项必须紧随一个高位代理项."
+
+#: parse_manifest.c:152
+msgid "manifest ended unexpectedly"
+msgstr "清单意外结束"
+
+#: parse_manifest.c:191
+msgid "unexpected object start"
+msgstr "意外的对象开始"
+
+#: parse_manifest.c:224
+msgid "unexpected object end"
+msgstr "意外的对象结束"
+
+#: parse_manifest.c:251
+msgid "unexpected array start"
+msgstr "意外的数组开始"
+
+#: parse_manifest.c:274
+msgid "unexpected array end"
+msgstr "意外的数组结束"
+
+#: parse_manifest.c:299
+msgid "expected version indicator"
+msgstr "预期的版本指示器"
+
+#: parse_manifest.c:328
+msgid "unrecognized top-level field"
+msgstr "无法识别的顶层字段"
+
+#: parse_manifest.c:347
+msgid "unexpected file field"
+msgstr "意外的文件字段"
+
+#: parse_manifest.c:361
+msgid "unexpected WAL range field"
+msgstr "意外的WAL范围字段"
+
+#: parse_manifest.c:367
+msgid "unexpected object field"
+msgstr "意外的对象字段"
+
+#: parse_manifest.c:397
+msgid "unexpected manifest version"
+msgstr "意外的清单版本"
+
+#: parse_manifest.c:448
+msgid "unexpected scalar"
+msgstr "意外的标量"
+
+#: parse_manifest.c:472
+msgid "missing path name"
+msgstr "缺少路径名"
+
+#: parse_manifest.c:475
+msgid "both path name and encoded path name"
+msgstr "路径名和编码路径名"
+
+#: parse_manifest.c:477
+msgid "missing size"
+msgstr "缺少大小"
+
+#: parse_manifest.c:480
+msgid "checksum without algorithm"
+msgstr "校验和没有算法"
+
+#: parse_manifest.c:494
+msgid "could not decode file name"
+msgstr "无法解码文件名"
+
+#: parse_manifest.c:504
+msgid "file size is not an integer"
+msgstr "文件大小不是整数"
+
+#: parse_manifest.c:510
+#, c-format
+msgid "unrecognized checksum algorithm: \"%s\""
+msgstr "无法识别的校验和算法: \"%s\""
+
+#: parse_manifest.c:529
+#, c-format
+msgid "invalid checksum for file \"%s\": \"%s\""
+msgstr "文件\"%s\"的校验和无效: \"%s\""
+
+#: parse_manifest.c:572
+msgid "missing timeline"
+msgstr "缺少时间线"
+
+#: parse_manifest.c:574
+msgid "missing start LSN"
+msgstr "缺少起始LSN"
+
+#: parse_manifest.c:576
+msgid "missing end LSN"
+msgstr "缺少结束LSN"
+
+#: parse_manifest.c:582
+msgid "timeline is not an integer"
+msgstr "时间线不是整数"
+
+#: parse_manifest.c:585
+msgid "could not parse start LSN"
+msgstr "无法解析起始LSN"
+
+#: parse_manifest.c:588
+msgid "could not parse end LSN"
+msgstr "无法解析结束LSN"
+
+#: parse_manifest.c:649
+msgid "expected at least 2 lines"
+msgstr "至少需要2行"
+
+#: parse_manifest.c:652
+msgid "last line not newline-terminated"
+msgstr "最后一行未以换行符结尾"
+
+#: parse_manifest.c:657
+#, c-format
+msgid "out of memory"
+msgstr "内存不足"
+
+#: parse_manifest.c:659
+msgid "could not initialize checksum of manifest"
+msgstr "无法初始化清单的校验和"
+
+#: parse_manifest.c:661
+msgid "could not update checksum of manifest"
+msgstr "无法更新清单的校验和"
+
+#: parse_manifest.c:664
+msgid "could not finalize checksum of manifest"
+msgstr "无法完成清单的校验和"
+
+#: parse_manifest.c:668
+#, c-format
+msgid "manifest has no checksum"
+msgstr "清单没有校验和"
+
+#: parse_manifest.c:672
+#, c-format
+msgid "invalid manifest checksum: \"%s\""
+msgstr "清单校验和无效: \"%s\""
+
+#: parse_manifest.c:676
+#, c-format
+msgid "manifest checksum mismatch"
+msgstr "清单校验和不匹配"
+
+#: parse_manifest.c:691
+#, c-format
+msgid "could not parse backup manifest: %s"
+msgstr "清单校验和不匹配: %s"
+
+#: pg_verifybackup.c:255 pg_verifybackup.c:265 pg_verifybackup.c:277
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "请用 \"%s --help\" 获取更多的信息.\n"
+
+#: pg_verifybackup.c:264
+#, c-format
+msgid "no backup directory specified"
+msgstr "未指定备份目录"
+
+#: pg_verifybackup.c:275
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "命令行参数太多 (第一个是 \"%s\")"
+
+#: pg_verifybackup.c:298
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"%2$s需要程序\"%1$s\"\n"
+"但在与\"%3$s\"相同的目录中找不到该程序.\n"
+"检查您的安装."
+
+#: pg_verifybackup.c:303
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"程序\"%s\"是由\"%s\"找到的\n"
+"但与%s的版本不同.\n"
+"检查您的安装."
+
+#: pg_verifybackup.c:362
+#, c-format
+msgid "backup successfully verified\n"
+msgstr "备份已成功验证\n"
+
+#: pg_verifybackup.c:388 pg_verifybackup.c:724
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "无法打开文件 \"%s\": %m"
+
+#: pg_verifybackup.c:392
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "无法取文件 \"%s\" 的状态: %m"
+
+#: pg_verifybackup.c:412 pg_verifybackup.c:753
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "无法读取文件 \"%s\": %m"
+
+#: pg_verifybackup.c:415
+msgid "could not read file \"%s\": read %d of %lld"
+msgstr "无法读取文件\"%1$s\":读取了%3$lld中的%2$d"
+
+#: pg_verifybackup.c:475
+msgid "duplicate path name in backup manifest: \"%s\""
+msgstr "备份清单中的路径名重复: \"%s\""
+
+#: pg_verifybackup.c:538 pg_verifybackup.c:545
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "无法打开目录 \"%s\": %m"
+
+#: pg_verifybackup.c:577
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "无法关闭目录 \"%s\": %m"
+
+#: pg_verifybackup.c:597
+#, c-format
+msgid "could not stat file or directory \"%s\": %m"
+msgstr "无法统计文件或目录\"%s\": %m"
+
+#: pg_verifybackup.c:620
+#, c-format
+msgid "\"%s\" is not a file or directory"
+msgstr "\"%s\"不是文件或目录"
+
+#: pg_verifybackup.c:630
+#, c-format
+msgid "\"%s\" is present on disk but not in the manifest"
+msgstr "磁盘上有\"%s\",但清单中没有"
+
+#: pg_verifybackup.c:642
+msgid "\"%s\" has size %lld on disk but size %zu in the manifest"
+msgstr "\"%s\"在磁盘上有大小%lld,但在清单中有大小%zu"
+
+#: pg_verifybackup.c:669
+#, c-format
+msgid "\"%s\" is present in the manifest but not on disk"
+msgstr "清单中有\"%s\",但磁盘上没有"
+
+#: pg_verifybackup.c:732
+msgid "could not initialize checksum of file \"%s\""
+msgstr "无法初始化文件\"%s\"的校验和"
+
+#: pg_verifybackup.c:744
+msgid "could not update checksum of file \"%s\""
+msgstr "无法更新文件\"%s\"的校验和"
+
+#: pg_verifybackup.c:759
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "无法关闭文件 \"%s\": %m"
+
+#: pg_verifybackup.c:778
+#, c-format
+msgid "file \"%s\" should contain %zu bytes, but read %zu bytes"
+msgstr "文件\"%s\"应包含%zu到字节,但读取到%zu字节"
+
+#: pg_verifybackup.c:788
+msgid "could not finalize checksum of file \"%s\""
+msgstr "无法完成文件\"%s\"的校验和"
+
+#: pg_verifybackup.c:796
+#, c-format
+msgid "file \"%s\" has checksum of length %d, but expected %d"
+msgstr "文件\"%s\"的校验和长度为%d,但应为%d"
+
+#: pg_verifybackup.c:800
+#, c-format
+msgid "checksum mismatch for file \"%s\""
+msgstr "文件\"%s\"的校验和不匹配"
+
+#: pg_verifybackup.c:824
+#, c-format
+msgid "WAL parsing failed for timeline %u"
+msgstr "时间线%u的WAL解析失败"
+
+#: pg_verifybackup.c:910
+#, c-format
+msgid ""
+"%s verifies a backup against the backup manifest.\n"
+"\n"
+msgstr ""
+"%s 根据备份清单验证备份.\n"
+"\n"
+
+#: pg_verifybackup.c:911
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]... BACKUPDIR\n"
+"\n"
+msgstr ""
+"用法:\n"
+" %s [选项]... BACKUPDIR\n"
+"\n"
+
+#: pg_verifybackup.c:912
+#, c-format
+msgid "Options:\n"
+msgstr "选项:\n"
+
+#: pg_verifybackup.c:913
+#, c-format
+msgid " -e, --exit-on-error exit immediately on error\n"
+msgstr " -e, --exit-on-error 出错时立即退出\n"
+
+#: pg_verifybackup.c:914
+#, c-format
+msgid " -i, --ignore=RELATIVE_PATH ignore indicated path\n"
+msgstr " -i, --ignore=RELATIVE_PATH 忽略指定的路径\n"
+
+#: pg_verifybackup.c:915
+#, c-format
+msgid " -m, --manifest-path=PATH use specified path for manifest\n"
+msgstr " -m, --manifest-path=PATH 使用清单的指定路径\n"
+
+#: pg_verifybackup.c:916
+#, c-format
+msgid " -n, --no-parse-wal do not try to parse WAL files\n"
+msgstr " -n, --no-parse-wal 不试图解析WAL文件\n"
+
+#: pg_verifybackup.c:917
+#, c-format
+msgid " -q, --quiet do not print any output, except for errors\n"
+msgstr " -q, --quiet 不打印任何输出,错误除外\n"
+
+#: pg_verifybackup.c:918
+#, c-format
+msgid " -s, --skip-checksums skip checksum verification\n"
+msgstr " -s, --skip-checksums 跳过校验和验证\n"
+
+#: pg_verifybackup.c:919
+#, c-format
+msgid " -w, --wal-directory=PATH use specified path for WAL files\n"
+msgstr " -w, --wal-directory=PATH 对WAL文件使用指定路径\n"
+
+#: pg_verifybackup.c:920
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version 输出版本信息,然后退出\n"
+
+#: pg_verifybackup.c:921
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help 显示此帮助,然后退出\n"
+
+#: pg_verifybackup.c:922
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"臭虫报告至<%s>.\n"
+
+#: pg_verifybackup.c:923
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "%s 主页: <%s>\n"
+
diff --git a/src/bin/pg_verifybackup/t/001_basic.pl b/src/bin/pg_verifybackup/t/001_basic.pl
new file mode 100644
index 0000000..4ad1c3f
--- /dev/null
+++ b/src/bin/pg_verifybackup/t/001_basic.pl
@@ -0,0 +1,36 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+use strict;
+use warnings;
+use TestLib;
+use Test::More tests => 16;
+
+my $tempdir = TestLib::tempdir;
+
+program_help_ok('pg_verifybackup');
+program_version_ok('pg_verifybackup');
+program_options_handling_ok('pg_verifybackup');
+
+command_fails_like(
+ ['pg_verifybackup'],
+ qr/no backup directory specified/,
+ 'target directory must be specified');
+command_fails_like(
+ [ 'pg_verifybackup', $tempdir ],
+ qr/could not open file.*\/backup_manifest\"/,
+ 'pg_verifybackup requires a manifest');
+command_fails_like(
+ [ 'pg_verifybackup', $tempdir, $tempdir ],
+ qr/too many command-line arguments/,
+ 'multiple target directories not allowed');
+
+# create fake manifest file
+open(my $fh, '>', "$tempdir/backup_manifest") || die "open: $!";
+close($fh);
+
+# but then try to use an alternate, nonexisting manifest
+command_fails_like(
+ [ 'pg_verifybackup', '-m', "$tempdir/not_the_manifest", $tempdir ],
+ qr/could not open file.*\/not_the_manifest\"/,
+ 'pg_verifybackup respects -m flag');
diff --git a/src/bin/pg_verifybackup/t/002_algorithm.pl b/src/bin/pg_verifybackup/t/002_algorithm.pl
new file mode 100644
index 0000000..c2c4c31
--- /dev/null
+++ b/src/bin/pg_verifybackup/t/002_algorithm.pl
@@ -0,0 +1,61 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+# Verify that we can take and verify backups with various checksum types.
+
+use strict;
+use warnings;
+use Cwd;
+use Config;
+use File::Path qw(rmtree);
+use PostgresNode;
+use TestLib;
+use Test::More tests => 19;
+
+my $primary = get_new_node('primary');
+$primary->init(allows_streaming => 1);
+$primary->start;
+
+for my $algorithm (qw(bogus none crc32c sha224 sha256 sha384 sha512))
+{
+ my $backup_path = $primary->backup_dir . '/' . $algorithm;
+ my @backup = (
+ 'pg_basebackup', '-D', $backup_path,
+ '--manifest-checksums', $algorithm, '--no-sync');
+ my @verify = ('pg_verifybackup', '-e', $backup_path);
+
+ # A backup with a bogus algorithm should fail.
+ if ($algorithm eq 'bogus')
+ {
+ $primary->command_fails(\@backup,
+ "backup fails with algorithm \"$algorithm\"");
+ next;
+ }
+
+ # A backup with a valid algorithm should work.
+ $primary->command_ok(\@backup, "backup ok with algorithm \"$algorithm\"");
+
+ # We expect each real checksum algorithm to be mentioned on every line of
+ # the backup manifest file except the first and last; for simplicity, we
+ # just check that it shows up lots of times. When the checksum algorithm
+ # is none, we just check that the manifest exists.
+ if ($algorithm eq 'none')
+ {
+ ok(-f "$backup_path/backup_manifest", "backup manifest exists");
+ }
+ else
+ {
+ my $manifest = slurp_file("$backup_path/backup_manifest");
+ my $count_of_algorithm_in_manifest =
+ (() = $manifest =~ /$algorithm/mig);
+ cmp_ok($count_of_algorithm_in_manifest,
+ '>', 100, "$algorithm is mentioned many times in the manifest");
+ }
+
+ # Make sure that it verifies OK.
+ $primary->command_ok(\@verify,
+ "verify backup with algorithm \"$algorithm\"");
+
+ # Remove backup immediately to save disk space.
+ rmtree($backup_path);
+}
diff --git a/src/bin/pg_verifybackup/t/003_corruption.pl b/src/bin/pg_verifybackup/t/003_corruption.pl
new file mode 100644
index 0000000..867c102
--- /dev/null
+++ b/src/bin/pg_verifybackup/t/003_corruption.pl
@@ -0,0 +1,292 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+# Verify that various forms of corruption are detected by pg_verifybackup.
+
+use strict;
+use warnings;
+use Cwd;
+use Config;
+use File::Path qw(rmtree);
+use PostgresNode;
+use TestLib;
+use Test::More tests => 44;
+
+my $primary = get_new_node('primary');
+$primary->init(allows_streaming => 1);
+$primary->start;
+
+# Include a user-defined tablespace in the hopes of detecting problems in that
+# area.
+my $source_ts_path = TestLib::tempdir_short();
+my $source_ts_prefix = $source_ts_path;
+$source_ts_prefix =~ s!(^[A-Z]:/[^/]*)/.*!$1!;
+
+$primary->safe_psql('postgres', <<EOM);
+CREATE TABLE x1 (a int);
+INSERT INTO x1 VALUES (111);
+CREATE TABLESPACE ts1 LOCATION '$source_ts_path';
+CREATE TABLE x2 (a int) TABLESPACE ts1;
+INSERT INTO x1 VALUES (222);
+EOM
+
+my @scenario = (
+ {
+ 'name' => 'extra_file',
+ 'mutilate' => \&mutilate_extra_file,
+ 'fails_like' =>
+ qr/extra_file.*present on disk but not in the manifest/
+ },
+ {
+ 'name' => 'extra_tablespace_file',
+ 'mutilate' => \&mutilate_extra_tablespace_file,
+ 'fails_like' =>
+ qr/extra_ts_file.*present on disk but not in the manifest/
+ },
+ {
+ 'name' => 'missing_file',
+ 'mutilate' => \&mutilate_missing_file,
+ 'fails_like' =>
+ qr/pg_xact\/0000.*present in the manifest but not on disk/
+ },
+ {
+ 'name' => 'missing_tablespace',
+ 'mutilate' => \&mutilate_missing_tablespace,
+ 'fails_like' =>
+ qr/pg_tblspc.*present in the manifest but not on disk/
+ },
+ {
+ 'name' => 'append_to_file',
+ 'mutilate' => \&mutilate_append_to_file,
+ 'fails_like' => qr/has size \d+ on disk but size \d+ in the manifest/
+ },
+ {
+ 'name' => 'truncate_file',
+ 'mutilate' => \&mutilate_truncate_file,
+ 'fails_like' => qr/has size 0 on disk but size \d+ in the manifest/
+ },
+ {
+ 'name' => 'replace_file',
+ 'mutilate' => \&mutilate_replace_file,
+ 'fails_like' => qr/checksum mismatch for file/
+ },
+ {
+ 'name' => 'bad_manifest',
+ 'mutilate' => \&mutilate_bad_manifest,
+ 'fails_like' => qr/manifest checksum mismatch/
+ },
+ {
+ 'name' => 'open_file_fails',
+ 'mutilate' => \&mutilate_open_file_fails,
+ 'fails_like' => qr/could not open file/,
+ 'skip_on_windows' => 1
+ },
+ {
+ 'name' => 'open_directory_fails',
+ 'mutilate' => \&mutilate_open_directory_fails,
+ 'cleanup' => \&cleanup_open_directory_fails,
+ 'fails_like' => qr/could not open directory/,
+ 'skip_on_windows' => 1
+ },
+ {
+ 'name' => 'search_directory_fails',
+ 'mutilate' => \&mutilate_search_directory_fails,
+ 'cleanup' => \&cleanup_search_directory_fails,
+ 'fails_like' => qr/could not stat file or directory/,
+ 'skip_on_windows' => 1
+ });
+
+for my $scenario (@scenario)
+{
+ my $name = $scenario->{'name'};
+
+ SKIP:
+ {
+ skip "unix-style permissions not supported on Windows", 4
+ if $scenario->{'skip_on_windows'} && $windows_os;
+
+ # Take a backup and check that it verifies OK.
+ my $backup_path = $primary->backup_dir . '/' . $name;
+ my $backup_ts_path = TestLib::tempdir_short();
+ # The tablespace map parameter confuses Msys2, which tries to mangle
+ # it. Tell it not to.
+ # See https://www.msys2.org/wiki/Porting/#filesystem-namespaces
+ local $ENV{MSYS2_ARG_CONV_EXCL} = $source_ts_prefix;
+ $primary->command_ok(
+ [
+ 'pg_basebackup', '-D', $backup_path, '--no-sync',
+ '-T', "${source_ts_path}=${backup_ts_path}"
+ ],
+ "base backup ok");
+ command_ok([ 'pg_verifybackup', $backup_path ],
+ "intact backup verified");
+
+ # Mutilate the backup in some way.
+ $scenario->{'mutilate'}->($backup_path);
+
+ # Now check that the backup no longer verifies.
+ command_fails_like(
+ [ 'pg_verifybackup', $backup_path ],
+ $scenario->{'fails_like'},
+ "corrupt backup fails verification: $name");
+
+ # Run cleanup hook, if provided.
+ $scenario->{'cleanup'}->($backup_path)
+ if exists $scenario->{'cleanup'};
+
+ # Finally, use rmtree to reclaim space.
+ rmtree($backup_path);
+ }
+}
+
+sub create_extra_file
+{
+ my ($backup_path, $relative_path) = @_;
+ my $pathname = "$backup_path/$relative_path";
+ open(my $fh, '>', $pathname) || die "open $pathname: $!";
+ print $fh "This is an extra file.\n";
+ close($fh);
+ return;
+}
+
+# Add a file into the root directory of the backup.
+sub mutilate_extra_file
+{
+ my ($backup_path) = @_;
+ create_extra_file($backup_path, "extra_file");
+ return;
+}
+
+# Add a file inside the user-defined tablespace.
+sub mutilate_extra_tablespace_file
+{
+ my ($backup_path) = @_;
+ my ($tsoid) =
+ grep { $_ ne '.' && $_ ne '..' } slurp_dir("$backup_path/pg_tblspc");
+ my ($catvdir) = grep { $_ ne '.' && $_ ne '..' }
+ slurp_dir("$backup_path/pg_tblspc/$tsoid");
+ my ($tsdboid) = grep { $_ ne '.' && $_ ne '..' }
+ slurp_dir("$backup_path/pg_tblspc/$tsoid/$catvdir");
+ create_extra_file($backup_path,
+ "pg_tblspc/$tsoid/$catvdir/$tsdboid/extra_ts_file");
+ return;
+}
+
+# Remove a file.
+sub mutilate_missing_file
+{
+ my ($backup_path) = @_;
+ my $pathname = "$backup_path/pg_xact/0000";
+ unlink($pathname) || die "$pathname: $!";
+ return;
+}
+
+# Remove the symlink to the user-defined tablespace.
+sub mutilate_missing_tablespace
+{
+ my ($backup_path) = @_;
+ my ($tsoid) =
+ grep { $_ ne '.' && $_ ne '..' } slurp_dir("$backup_path/pg_tblspc");
+ my $pathname = "$backup_path/pg_tblspc/$tsoid";
+ if ($windows_os)
+ {
+ # rmdir works on some windows setups, unlink on others.
+ # Instead of trying to implement precise rules, just try one and then
+ # the other.
+ unless (rmdir($pathname))
+ {
+ my $err = $!;
+ unlink($pathname) || die "$pathname: rmdir: $err, unlink: $!";
+ }
+ }
+ else
+ {
+ unlink($pathname) || die "$pathname: $!";
+ }
+ return;
+}
+
+# Append an additional bytes to a file.
+sub mutilate_append_to_file
+{
+ my ($backup_path) = @_;
+ append_to_file "$backup_path/global/pg_control", 'x';
+ return;
+}
+
+# Truncate a file to zero length.
+sub mutilate_truncate_file
+{
+ my ($backup_path) = @_;
+ my $pathname = "$backup_path/global/pg_control";
+ open(my $fh, '>', $pathname) || die "open $pathname: $!";
+ close($fh);
+ return;
+}
+
+# Replace a file's contents without changing the length of the file. This is
+# not a particularly efficient way to do this, so we pick a file that's
+# expected to be short.
+sub mutilate_replace_file
+{
+ my ($backup_path) = @_;
+ my $pathname = "$backup_path/PG_VERSION";
+ my $contents = slurp_file($pathname);
+ open(my $fh, '>', $pathname) || die "open $pathname: $!";
+ print $fh 'q' x length($contents);
+ close($fh);
+ return;
+}
+
+# Corrupt the backup manifest.
+sub mutilate_bad_manifest
+{
+ my ($backup_path) = @_;
+ append_to_file "$backup_path/backup_manifest", "\n";
+ return;
+}
+
+# Create a file that can't be opened. (This is skipped on Windows.)
+sub mutilate_open_file_fails
+{
+ my ($backup_path) = @_;
+ my $pathname = "$backup_path/PG_VERSION";
+ chmod(0, $pathname) || die "chmod $pathname: $!";
+ return;
+}
+
+# Create a directory that can't be opened. (This is skipped on Windows.)
+sub mutilate_open_directory_fails
+{
+ my ($backup_path) = @_;
+ my $pathname = "$backup_path/pg_subtrans";
+ chmod(0, $pathname) || die "chmod $pathname: $!";
+ return;
+}
+
+# restore permissions on the unreadable directory we created.
+sub cleanup_open_directory_fails
+{
+ my ($backup_path) = @_;
+ my $pathname = "$backup_path/pg_subtrans";
+ chmod(0700, $pathname) || die "chmod $pathname: $!";
+ return;
+}
+
+# Create a directory that can't be searched. (This is skipped on Windows.)
+sub mutilate_search_directory_fails
+{
+ my ($backup_path) = @_;
+ my $pathname = "$backup_path/base";
+ chmod(0400, $pathname) || die "chmod $pathname: $!";
+ return;
+}
+
+# rmtree can't cope with a mode 400 directory, so change back to 700.
+sub cleanup_search_directory_fails
+{
+ my ($backup_path) = @_;
+ my $pathname = "$backup_path/base";
+ chmod(0700, $pathname) || die "chmod $pathname: $!";
+ return;
+}
diff --git a/src/bin/pg_verifybackup/t/004_options.pl b/src/bin/pg_verifybackup/t/004_options.pl
new file mode 100644
index 0000000..3f6e84c
--- /dev/null
+++ b/src/bin/pg_verifybackup/t/004_options.pl
@@ -0,0 +1,107 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+# Verify the behavior of assorted pg_verifybackup options.
+
+use strict;
+use warnings;
+use Cwd;
+use Config;
+use File::Path qw(rmtree);
+use PostgresNode;
+use TestLib;
+use Test::More tests => 25;
+
+# Start up the server and take a backup.
+my $primary = get_new_node('primary');
+$primary->init(allows_streaming => 1);
+$primary->start;
+my $backup_path = $primary->backup_dir . '/test_options';
+$primary->command_ok([ 'pg_basebackup', '-D', $backup_path, '--no-sync' ],
+ "base backup ok");
+
+# Verify that pg_verifybackup -q succeeds and produces no output.
+my $stdout;
+my $stderr;
+my $result = IPC::Run::run [ 'pg_verifybackup', '-q', $backup_path ],
+ '>', \$stdout, '2>', \$stderr;
+ok($result, "-q succeeds: exit code 0");
+is($stdout, '', "-q succeeds: no stdout");
+is($stderr, '', "-q succeeds: no stderr");
+
+# Corrupt the PG_VERSION file.
+my $version_pathname = "$backup_path/PG_VERSION";
+my $version_contents = slurp_file($version_pathname);
+open(my $fh, '>', $version_pathname) || die "open $version_pathname: $!";
+print $fh 'q' x length($version_contents);
+close($fh);
+
+# Verify that pg_verifybackup -q now fails.
+command_fails_like(
+ [ 'pg_verifybackup', '-q', $backup_path ],
+ qr/checksum mismatch for file \"PG_VERSION\"/,
+ '-q checksum mismatch');
+
+# Since we didn't change the length of the file, verification should succeed
+# if we ignore checksums. Check that we get the right message, too.
+command_like(
+ [ 'pg_verifybackup', '-s', $backup_path ],
+ qr/backup successfully verified/,
+ '-s skips checksumming');
+
+# Validation should succeed if we ignore the problem file.
+command_like(
+ [ 'pg_verifybackup', '-i', 'PG_VERSION', $backup_path ],
+ qr/backup successfully verified/,
+ '-i ignores problem file');
+
+# PG_VERSION is already corrupt; let's try also removing all of pg_xact.
+rmtree($backup_path . "/pg_xact");
+
+# We're ignoring the problem with PG_VERSION, but not the problem with
+# pg_xact, so verification should fail here.
+command_fails_like(
+ [ 'pg_verifybackup', '-i', 'PG_VERSION', $backup_path ],
+ qr/pg_xact.*is present in the manifest but not on disk/,
+ '-i does not ignore all problems');
+
+# If we use -i twice, we should be able to ignore all of the problems.
+command_like(
+ [ 'pg_verifybackup', '-i', 'PG_VERSION', '-i', 'pg_xact', $backup_path ],
+ qr/backup successfully verified/,
+ 'multiple -i options work');
+
+# Verify that when -i is not used, both problems are reported.
+$result = IPC::Run::run [ 'pg_verifybackup', $backup_path ],
+ '>', \$stdout, '2>', \$stderr;
+ok(!$result, "multiple problems: fails");
+like(
+ $stderr,
+ qr/pg_xact.*is present in the manifest but not on disk/,
+ "multiple problems: missing files reported");
+like(
+ $stderr,
+ qr/checksum mismatch for file \"PG_VERSION\"/,
+ "multiple problems: checksum mismatch reported");
+
+# Verify that when -e is used, only the problem detected first is reported.
+$result = IPC::Run::run [ 'pg_verifybackup', '-e', $backup_path ],
+ '>', \$stdout, '2>', \$stderr;
+ok(!$result, "-e reports 1 error: fails");
+like(
+ $stderr,
+ qr/pg_xact.*is present in the manifest but not on disk/,
+ "-e reports 1 error: missing files reported");
+unlike(
+ $stderr,
+ qr/checksum mismatch for file \"PG_VERSION\"/,
+ "-e reports 1 error: checksum mismatch not reported");
+
+# Test valid manifest with nonexistent backup directory.
+command_fails_like(
+ [
+ 'pg_verifybackup', '-m',
+ "$backup_path/backup_manifest", "$backup_path/fake"
+ ],
+ qr/could not open directory/,
+ 'nonexistent backup directory');
diff --git a/src/bin/pg_verifybackup/t/005_bad_manifest.pl b/src/bin/pg_verifybackup/t/005_bad_manifest.pl
new file mode 100644
index 0000000..c514282
--- /dev/null
+++ b/src/bin/pg_verifybackup/t/005_bad_manifest.pl
@@ -0,0 +1,211 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+# Test the behavior of pg_verifybackup when the backup manifest has
+# problems.
+
+use strict;
+use warnings;
+use Cwd;
+use Config;
+use PostgresNode;
+use TestLib;
+use Test::More tests => 58;
+
+my $tempdir = TestLib::tempdir;
+
+test_bad_manifest(
+ 'input string ended unexpectedly',
+ qr/could not parse backup manifest: The input string ended unexpectedly/,
+ <<EOM);
+{
+EOM
+
+test_parse_error('unexpected object end', <<EOM);
+{}
+EOM
+
+test_parse_error('unexpected array start', <<EOM);
+[]
+EOM
+
+test_parse_error('expected version indicator', <<EOM);
+{"not-expected": 1}
+EOM
+
+test_parse_error('unexpected manifest version', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": "phooey"}
+EOM
+
+test_parse_error('unexpected scalar', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Files": true}
+EOM
+
+test_parse_error('unrecognized top-level field', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Oops": 1}
+EOM
+
+test_parse_error('unexpected object start', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Files": {}}
+EOM
+
+test_parse_error('missing path name', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Files": [{}]}
+EOM
+
+test_parse_error('both path name and encoded path name', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Files": [
+ {"Path": "x", "Encoded-Path": "1234"}
+]}
+EOM
+
+test_parse_error('unexpected file field', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Files": [
+ {"Oops": 1}
+]}
+EOM
+
+test_parse_error('missing size', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Files": [
+ {"Path": "x"}
+]}
+EOM
+
+test_parse_error('file size is not an integer', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Files": [
+ {"Path": "x", "Size": "Oops"}
+]}
+EOM
+
+test_parse_error('could not decode file name', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Files": [
+ {"Encoded-Path": "123", "Size": 0}
+]}
+EOM
+
+test_fatal_error('duplicate path name in backup manifest', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Files": [
+ {"Path": "x", "Size": 0},
+ {"Path": "x", "Size": 0}
+]}
+EOM
+
+test_parse_error('checksum without algorithm', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Files": [
+ {"Path": "x", "Size": 100, "Checksum": "Oops"}
+]}
+EOM
+
+test_fatal_error('unrecognized checksum algorithm', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Files": [
+ {"Path": "x", "Size": 100, "Checksum-Algorithm": "Oops", "Checksum": "00"}
+]}
+EOM
+
+test_fatal_error('invalid checksum for file', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Files": [
+ {"Path": "x", "Size": 100, "Checksum-Algorithm": "CRC32C", "Checksum": "0"}
+]}
+EOM
+
+test_parse_error('missing start LSN', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "WAL-Ranges": [
+ {"Timeline": 1}
+]}
+EOM
+
+test_parse_error('missing end LSN', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "WAL-Ranges": [
+ {"Timeline": 1, "Start-LSN": "0/0"}
+]}
+EOM
+
+test_parse_error('unexpected WAL range field', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "WAL-Ranges": [
+ {"Oops": 1}
+]}
+EOM
+
+test_parse_error('missing timeline', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "WAL-Ranges": [
+ {}
+]}
+EOM
+
+test_parse_error('unexpected object end', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "WAL-Ranges": [
+ {"Timeline": 1, "Start-LSN": "0/0", "End-LSN": "0/0"}
+]}
+EOM
+
+test_parse_error('timeline is not an integer', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "WAL-Ranges": [
+ {"Timeline": true, "Start-LSN": "0/0", "End-LSN": "0/0"}
+]}
+EOM
+
+test_parse_error('could not parse start LSN', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "WAL-Ranges": [
+ {"Timeline": 1, "Start-LSN": "oops", "End-LSN": "0/0"}
+]}
+EOM
+
+test_parse_error('could not parse end LSN', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "WAL-Ranges": [
+ {"Timeline": 1, "Start-LSN": "0/0", "End-LSN": "oops"}
+]}
+EOM
+
+test_parse_error('expected at least 2 lines', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Files": [], "Manifest-Checksum": null}
+EOM
+
+my $manifest_without_newline = <<EOM;
+{"PostgreSQL-Backup-Manifest-Version": 1,
+ "Files": [],
+ "Manifest-Checksum": null}
+EOM
+chomp($manifest_without_newline);
+test_parse_error('last line not newline-terminated',
+ $manifest_without_newline);
+
+test_fatal_error('invalid manifest checksum', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Files": [],
+ "Manifest-Checksum": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890-"}
+EOM
+
+sub test_parse_error
+{
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+ my ($test_name, $manifest_contents) = @_;
+
+ test_bad_manifest($test_name,
+ qr/could not parse backup manifest: $test_name/,
+ $manifest_contents);
+ return;
+}
+
+sub test_fatal_error
+{
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+ my ($test_name, $manifest_contents) = @_;
+
+ test_bad_manifest($test_name, qr/fatal: $test_name/, $manifest_contents);
+ return;
+}
+
+sub test_bad_manifest
+{
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+ my ($test_name, $regexp, $manifest_contents) = @_;
+
+ open(my $fh, '>', "$tempdir/backup_manifest") || die "open: $!";
+ print $fh $manifest_contents;
+ close($fh);
+
+ command_fails_like([ 'pg_verifybackup', $tempdir ], $regexp, $test_name);
+ return;
+}
diff --git a/src/bin/pg_verifybackup/t/006_encoding.pl b/src/bin/pg_verifybackup/t/006_encoding.pl
new file mode 100644
index 0000000..a821d52
--- /dev/null
+++ b/src/bin/pg_verifybackup/t/006_encoding.pl
@@ -0,0 +1,34 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+# Verify that pg_verifybackup handles hex-encoded filenames correctly.
+
+use strict;
+use warnings;
+use Cwd;
+use Config;
+use PostgresNode;
+use TestLib;
+use Test::More tests => 5;
+
+my $primary = get_new_node('primary');
+$primary->init(allows_streaming => 1);
+$primary->start;
+my $backup_path = $primary->backup_dir . '/test_encoding';
+$primary->command_ok(
+ [
+ 'pg_basebackup', '-D',
+ $backup_path, '--no-sync',
+ '--manifest-force-encode'
+ ],
+ "backup ok with forced hex encoding");
+
+my $manifest = slurp_file("$backup_path/backup_manifest");
+my $count_of_encoded_path_in_manifest = (() = $manifest =~ /Encoded-Path/mig);
+cmp_ok($count_of_encoded_path_in_manifest,
+ '>', 100, "many paths are encoded in the manifest");
+
+command_like(
+ [ 'pg_verifybackup', '-s', $backup_path ],
+ qr/backup successfully verified/,
+ 'backup with forced encoding verified');
diff --git a/src/bin/pg_verifybackup/t/007_wal.pl b/src/bin/pg_verifybackup/t/007_wal.pl
new file mode 100644
index 0000000..baabab3
--- /dev/null
+++ b/src/bin/pg_verifybackup/t/007_wal.pl
@@ -0,0 +1,78 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+# Test pg_verifybackup's WAL verification.
+
+use strict;
+use warnings;
+use Cwd;
+use Config;
+use File::Path qw(rmtree);
+use PostgresNode;
+use TestLib;
+use Test::More tests => 9;
+
+# Start up the server and take a backup.
+my $primary = get_new_node('primary');
+$primary->init(allows_streaming => 1);
+$primary->start;
+my $backup_path = $primary->backup_dir . '/test_wal';
+$primary->command_ok([ 'pg_basebackup', '-D', $backup_path, '--no-sync' ],
+ "base backup ok");
+
+# Rename pg_wal.
+my $original_pg_wal = $backup_path . '/pg_wal';
+my $relocated_pg_wal = $primary->backup_dir . '/relocated_pg_wal';
+rename($original_pg_wal, $relocated_pg_wal) || die "rename pg_wal: $!";
+
+# WAL verification should fail.
+command_fails_like(
+ [ 'pg_verifybackup', $backup_path ],
+ qr/WAL parsing failed for timeline 1/,
+ 'missing pg_wal causes failure');
+
+# Should work if we skip WAL verification.
+command_ok(
+ [ 'pg_verifybackup', '-n', $backup_path ],
+ 'missing pg_wal OK if not verifying WAL');
+
+# Should also work if we specify the correct WAL location.
+command_ok([ 'pg_verifybackup', '-w', $relocated_pg_wal, $backup_path ],
+ '-w can be used to specify WAL directory');
+
+# Move directory back to original location.
+rename($relocated_pg_wal, $original_pg_wal) || die "rename pg_wal back: $!";
+
+# Get a list of files in that directory that look like WAL files.
+my @walfiles = grep { /^[0-9A-F]{24}$/ } slurp_dir($original_pg_wal);
+
+# Replace the contents of one of the files with garbage of equal length.
+my $wal_corruption_target = $original_pg_wal . '/' . $walfiles[0];
+my $wal_size = -s $wal_corruption_target;
+open(my $fh, '>', $wal_corruption_target)
+ || die "open $wal_corruption_target: $!";
+print $fh 'w' x $wal_size;
+close($fh);
+
+# WAL verification should fail.
+command_fails_like(
+ [ 'pg_verifybackup', $backup_path ],
+ qr/WAL parsing failed for timeline 1/,
+ 'corrupt WAL file causes failure');
+
+# Check that WAL-Ranges has correct values with a history file and
+# a timeline > 1. Rather than plugging in a new standby, do a
+# self-promotion of this node.
+$primary->stop;
+$primary->append_conf('standby.signal', '');
+$primary->start;
+$primary->promote;
+$primary->safe_psql('postgres', 'SELECT pg_switch_wal()');
+my $backup_path2 = $primary->backup_dir . '/test_tli';
+# The base backup run below does a checkpoint, that removes the first segment
+# of the current timeline.
+$primary->command_ok([ 'pg_basebackup', '-D', $backup_path2, '--no-sync' ],
+ "base backup 2 ok");
+command_ok(
+ [ 'pg_verifybackup', $backup_path2 ],
+ 'valid base backup with timeline > 1');