1/*-------------------------------------------------------------------------
3 * Load data from a backup manifest into memory.
5 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
6 * Portions Copyright (c) 1994, Regents of the University of California
8 * src/bin/pg_combinebackup/load_manifest.c
10 *-------------------------------------------------------------------------
24 * For efficiency, we'd like our hash table containing information about the
25 * manifest to start out with approximately the correct number of entries.
26 * There's no way to know the exact number of entries without reading the whole
27 * file, but we can get an estimate by dividing the file size by the estimated
28 * number of bytes per line.
30 * This could be off by about a factor of two in either direction, because the
31 * checksum algorithm has a big impact on the line lengths; e.g. a SHA512
32 * checksum is 128 hex bytes, whereas a CRC-32C value is only 8, and there
33 * might be no checksum at all.
35 #define ESTIMATED_BYTES_PER_MANIFEST_LINE 100
38 * size of json chunk to be read in
41 #define READ_CHUNK_SIZE (128 * 1024)
44 * Define a hash table which we can use to store information about the files
45 * mentioned in the backup manifest.
47 #define SH_PREFIX manifest_files
48 #define SH_ELEMENT_TYPE manifest_file
49 #define SH_KEY_TYPE const char *
50 #define SH_KEY pathname
51 #define SH_HASH_KEY(tb, key) hash_string(key)
52 #define SH_EQUAL(tb, a, b) (strcmp(a, b) == 0)
53 #define SH_SCOPE extern
54 #define SH_RAW_ALLOCATOR pg_malloc0
59 int manifest_version);
61 uint64 manifest_system_identifier);
63 const char *pathname,
uint64 size,
66 uint8 *checksum_payload);
76 * Load backup_manifest files from an array of backups and produces an array
77 * of manifest_data objects.
79 * NB: Since load_backup_manifest() can return NULL, the resulting array could
80 * contain NULL entries.
89 for (
i = 0;
i < n_backups; ++
i)
96 * Parse the backup_manifest file in the named backup directory. Construct a
97 * hash table with information about all the files it mentions, and a linked
98 * list of all the WAL ranges it mentions.
100 * If the backup_manifest file simply doesn't exist, logs a warning and returns
101 * NULL. Any other error, or any error parsing the contents of the file, is
112 manifest_files_hash *ht;
119 /* Open the manifest file. */
121 if ((
fd = open(pathname, O_RDONLY |
PG_BINARY, 0)) < 0)
128 pg_fatal(
"could not open file \"%s\": %m", pathname);
131 /* Figure out how big the manifest is. */
133 pg_fatal(
"could not stat file \"%s\": %m", pathname);
135 /* Guess how large to make the hash table based on the manifest size. */
139 /* Create the hash table. */
140 ht = manifest_files_create(initial_size, NULL);
152 * Parse the file, in chunks if necessary.
154 if (statbuf.
st_size <= chunk_size)
161 pg_fatal(
"could not read file \"%s\": %m", pathname);
163 pg_fatal(
"could not read file \"%s\": read %d of %lld",
164 pathname, rc, (
long long int) statbuf.
st_size);
167 /* Close the manifest file. */
170 /* Parse the manifest. */
175 int bytes_left = statbuf.
st_size;
182 while (bytes_left > 0)
184 int bytes_to_read = chunk_size;
187 * Make sure that the last chunk is sufficiently large. (i.e. at
188 * least half the chunk size) so that it will contain fully the
189 * piece at the end with the checksum.
191 if (bytes_left < chunk_size)
192 bytes_to_read = bytes_left;
193 else if (bytes_left < 2 * chunk_size)
194 bytes_to_read = bytes_left / 2;
195 rc =
read(
fd, buffer, bytes_to_read);
196 if (rc != bytes_to_read)
199 pg_fatal(
"could not read file \"%s\": %m", pathname);
201 pg_fatal(
"could not read file \"%s\": read %lld of %lld",
203 (
long long int) (statbuf.
st_size + rc - bytes_left),
204 (
long long int) statbuf.
st_size);
210 /* Release the incremental state memory */
222 * Report an error while parsing the manifest.
224 * We consider all such errors to be fatal errors. The manifest parser
225 * expects this function not to return.
240 * This callback to validate the manifest version number for incremental backup.
244 int manifest_version)
246 /* Incremental backups supported on manifest version 2 or later */
247 if (manifest_version == 1)
248 pg_fatal(
"backup manifest version 1 does not support incremental backup");
252 * Record system identifier extracted from the backup manifest.
256 uint64 manifest_system_identifier)
260 /* Validation will be at the later stage */
261 manifest->system_identifier = manifest_system_identifier;
265 * Record details extracted from the backup manifest for one file.
269 const char *pathname,
uint64 size,
271 int checksum_length,
uint8 *checksum_payload)
277 /* Make a new entry in the hash table for this file. */
278 m = manifest_files_insert(
manifest->files, pathname, &found);
280 pg_fatal(
"duplicate path name in backup manifest: \"%s\"", pathname);
282 /* Initialize the entry. */
290 * Record details extracted from the backup manifest for one WAL range.
300 /* Allocate and initialize a struct describing this WAL range. */
303 range->start_lsn = start_lsn;
304 range->end_lsn = end_lsn;
308 /* Add it to the end of the list. */
309 if (
manifest->first_wal_range == NULL)
#define pg_attribute_printf(f, a)
void * pg_malloc(size_t size)
void * pg_malloc0(size_t size)
#define ESTIMATED_BYTES_PER_MANIFEST_LINE
static pg_noreturn void manifest_data ** load_backup_manifests(int n_backups, char **backup_directories)
manifest_data * load_backup_manifest(char *backup_directory)
static void combinebackup_per_file_cb(JsonManifestParseContext *context, const char *pathname, uint64 size, pg_checksum_type checksum_type, int checksum_length, uint8 *checksum_payload)
static pg_noreturn void report_manifest_error(JsonManifestParseContext *context, const char *fmt,...) pg_attribute_printf(2
static void combinebackup_version_cb(JsonManifestParseContext *context, int manifest_version)
static void combinebackup_per_wal_range_cb(JsonManifestParseContext *context, TimeLineID tli, XLogRecPtr start_lsn, XLogRecPtr end_lsn)
static void combinebackup_system_identifier_cb(JsonManifestParseContext *context, uint64 manifest_system_identifier)
void pg_log_generic_v(enum pg_log_level level, enum pg_log_part part, const char *pg_restrict fmt, va_list ap)
void pfree(void *pointer)
void json_parse_manifest(JsonManifestParseContext *context, const char *buffer, size_t size)
JsonManifestParseIncrementalState * json_parse_manifest_incremental_init(JsonManifestParseContext *context)
void json_parse_manifest_incremental_shutdown(JsonManifestParseIncrementalState *incstate)
void json_parse_manifest_incremental_chunk(JsonManifestParseIncrementalState *incstate, const char *chunk, size_t size, bool is_last)
#define pg_log_warning(...)
static int fd(const char *x, int i)
static struct cvec * range(struct vars *v, chr a, chr b, int cases)
json_manifest_per_wal_range_callback per_wal_range_cb
json_manifest_system_identifier_callback system_identifier_cb
json_manifest_error_callback error_cb
json_manifest_per_file_callback per_file_cb
json_manifest_version_callback version_cb
manifest_files_hash * files
pg_checksum_type checksum_type