1 /*-------------------------------------------------------------------------
2  *
3  * parse_manifest.c
4  *	  Parse a backup manifest in JSON format.
5  *
6  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * src/bin/pg_verifybackup/parse_manifest.c
10  *
11  *-------------------------------------------------------------------------
12  */
13 
14 #include "postgres_fe.h"
15 
16 #include "parse_manifest.h"
17 #include "common/jsonapi.h"
18 
19 /*
20  * Semantic states for JSON manifest parsing.
21  */
22 typedef enum
23 {
24 	JM_EXPECT_TOPLEVEL_START,
25 	JM_EXPECT_TOPLEVEL_END,
26 	JM_EXPECT_TOPLEVEL_FIELD,
27 	JM_EXPECT_VERSION_VALUE,
28 	JM_EXPECT_FILES_START,
29 	JM_EXPECT_FILES_NEXT,
30 	JM_EXPECT_THIS_FILE_FIELD,
31 	JM_EXPECT_THIS_FILE_VALUE,
32 	JM_EXPECT_WAL_RANGES_START,
33 	JM_EXPECT_WAL_RANGES_NEXT,
34 	JM_EXPECT_THIS_WAL_RANGE_FIELD,
35 	JM_EXPECT_THIS_WAL_RANGE_VALUE,
36 	JM_EXPECT_MANIFEST_CHECKSUM_VALUE,
37 	JM_EXPECT_EOF
38 } JsonManifestSemanticState;
39 
40 /*
41  * Possible fields for one file as described by the manifest.
42  */
43 typedef enum
44 {
45 	JMFF_PATH,
46 	JMFF_ENCODED_PATH,
47 	JMFF_SIZE,
48 	JMFF_LAST_MODIFIED,
49 	JMFF_CHECKSUM_ALGORITHM,
50 	JMFF_CHECKSUM
51 } JsonManifestFileField;
52 
53 /*
54  * Possible fields for one file as described by the manifest.
55  */
56 typedef enum
57 {
58 	JMWRF_TIMELINE,
59 	JMWRF_START_LSN,
60 	JMWRF_END_LSN
61 } JsonManifestWALRangeField;
62 
63 /*
64  * Internal state used while decoding the JSON-format backup manifest.
65  */
66 typedef struct
67 {
68 	JsonManifestParseContext *context;
69 	JsonManifestSemanticState state;
70 
71 	/* These fields are used for parsing objects in the list of files. */
72 	JsonManifestFileField file_field;
73 	char	   *pathname;
74 	char	   *encoded_pathname;
75 	char	   *size;
76 	char	   *algorithm;
77 	pg_checksum_type checksum_algorithm;
78 	char	   *checksum;
79 
80 	/* These fields are used for parsing objects in the list of WAL ranges. */
81 	JsonManifestWALRangeField wal_range_field;
82 	char	   *timeline;
83 	char	   *start_lsn;
84 	char	   *end_lsn;
85 
86 	/* Miscellaneous other stuff. */
87 	bool		saw_version_field;
88 	char	   *manifest_checksum;
89 } JsonManifestParseState;
90 
91 static void json_manifest_object_start(void *state);
92 static void json_manifest_object_end(void *state);
93 static void json_manifest_array_start(void *state);
94 static void json_manifest_array_end(void *state);
95 static void json_manifest_object_field_start(void *state, char *fname,
96 											 bool isnull);
97 static void json_manifest_scalar(void *state, char *token,
98 								 JsonTokenType tokentype);
99 static void json_manifest_finalize_file(JsonManifestParseState *parse);
100 static void json_manifest_finalize_wal_range(JsonManifestParseState *parse);
101 static void verify_manifest_checksum(JsonManifestParseState *parse,
102 									 char *buffer, size_t size);
103 static void json_manifest_parse_failure(JsonManifestParseContext *context,
104 										char *msg);
105 
106 static int	hexdecode_char(char c);
107 static bool hexdecode_string(uint8 *result, char *input, int nbytes);
108 static bool parse_xlogrecptr(XLogRecPtr *result, char *input);
109 
110 /*
111  * Main entrypoint to parse a JSON-format backup manifest.
112  *
113  * Caller should set up the parsing context and then invoke this function.
114  * For each file whose information is extracted from the manifest,
115  * context->perfile_cb is invoked.  In case of trouble, context->error_cb is
116  * invoked and is expected not to return.
117  */
118 void
json_parse_manifest(JsonManifestParseContext * context,char * buffer,size_t size)119 json_parse_manifest(JsonManifestParseContext *context, char *buffer,
120 					size_t size)
121 {
122 	JsonLexContext *lex;
123 	JsonParseErrorType json_error;
124 	JsonSemAction sem;
125 	JsonManifestParseState parse;
126 
127 	/* Set up our private parsing context. */
128 	parse.context = context;
129 	parse.state = JM_EXPECT_TOPLEVEL_START;
130 	parse.saw_version_field = false;
131 
132 	/* Create a JSON lexing context. */
133 	lex = makeJsonLexContextCstringLen(buffer, size, PG_UTF8, true);
134 
135 	/* Set up semantic actions. */
136 	sem.semstate = &parse;
137 	sem.object_start = json_manifest_object_start;
138 	sem.object_end = json_manifest_object_end;
139 	sem.array_start = json_manifest_array_start;
140 	sem.array_end = json_manifest_array_end;
141 	sem.object_field_start = json_manifest_object_field_start;
142 	sem.object_field_end = NULL;
143 	sem.array_element_start = NULL;
144 	sem.array_element_end = NULL;
145 	sem.scalar = json_manifest_scalar;
146 
147 	/* Run the actual JSON parser. */
148 	json_error = pg_parse_json(lex, &sem);
149 	if (json_error != JSON_SUCCESS)
150 		json_manifest_parse_failure(context, json_errdetail(json_error, lex));
151 	if (parse.state != JM_EXPECT_EOF)
152 		json_manifest_parse_failure(context, "manifest ended unexpectedly");
153 
154 	/* Verify the manifest checksum. */
155 	verify_manifest_checksum(&parse, buffer, size);
156 }
157 
158 /*
159  * Invoked at the start of each object in the JSON document.
160  *
161  * The document as a whole is expected to be an object; each file and each
162  * WAL range is also expected to be an object. If we're anywhere else in the
163  * document, it's an error.
164  */
165 static void
json_manifest_object_start(void * state)166 json_manifest_object_start(void *state)
167 {
168 	JsonManifestParseState *parse = state;
169 
170 	switch (parse->state)
171 	{
172 		case JM_EXPECT_TOPLEVEL_START:
173 			parse->state = JM_EXPECT_TOPLEVEL_FIELD;
174 			break;
175 		case JM_EXPECT_FILES_NEXT:
176 			parse->state = JM_EXPECT_THIS_FILE_FIELD;
177 			parse->pathname = NULL;
178 			parse->encoded_pathname = NULL;
179 			parse->size = NULL;
180 			parse->algorithm = NULL;
181 			parse->checksum = NULL;
182 			break;
183 		case JM_EXPECT_WAL_RANGES_NEXT:
184 			parse->state = JM_EXPECT_THIS_WAL_RANGE_FIELD;
185 			parse->timeline = NULL;
186 			parse->start_lsn = NULL;
187 			parse->end_lsn = NULL;
188 			break;
189 		default:
190 			json_manifest_parse_failure(parse->context,
191 										"unexpected object start");
192 			break;
193 	}
194 }
195 
196 /*
197  * Invoked at the end of each object in the JSON document.
198  *
199  * The possible cases here are the same as for json_manifest_object_start.
200  * There's nothing special to do at the end of the document, but when we
201  * reach the end of an object representing a particular file or WAL range,
202  * we must call json_manifest_finalize_file() to save the associated details.
203  */
204 static void
json_manifest_object_end(void * state)205 json_manifest_object_end(void *state)
206 {
207 	JsonManifestParseState *parse = state;
208 
209 	switch (parse->state)
210 	{
211 		case JM_EXPECT_TOPLEVEL_END:
212 			parse->state = JM_EXPECT_EOF;
213 			break;
214 		case JM_EXPECT_THIS_FILE_FIELD:
215 			json_manifest_finalize_file(parse);
216 			parse->state = JM_EXPECT_FILES_NEXT;
217 			break;
218 		case JM_EXPECT_THIS_WAL_RANGE_FIELD:
219 			json_manifest_finalize_wal_range(parse);
220 			parse->state = JM_EXPECT_WAL_RANGES_NEXT;
221 			break;
222 		default:
223 			json_manifest_parse_failure(parse->context,
224 										"unexpected object end");
225 			break;
226 	}
227 }
228 
229 /*
230  * Invoked at the start of each array in the JSON document.
231  *
232  * Within the toplevel object, the value associated with the "Files" key
233  * should be an array. Similarly for the "WAL-Ranges" key. No other arrays
234  * are expected.
235  */
236 static void
json_manifest_array_start(void * state)237 json_manifest_array_start(void *state)
238 {
239 	JsonManifestParseState *parse = state;
240 
241 	switch (parse->state)
242 	{
243 		case JM_EXPECT_FILES_START:
244 			parse->state = JM_EXPECT_FILES_NEXT;
245 			break;
246 		case JM_EXPECT_WAL_RANGES_START:
247 			parse->state = JM_EXPECT_WAL_RANGES_NEXT;
248 			break;
249 		default:
250 			json_manifest_parse_failure(parse->context,
251 										"unexpected array start");
252 			break;
253 	}
254 }
255 
256 /*
257  * Invoked at the end of each array in the JSON document.
258  *
259  * The cases here are analogous to those in json_manifest_array_start.
260  */
261 static void
json_manifest_array_end(void * state)262 json_manifest_array_end(void *state)
263 {
264 	JsonManifestParseState *parse = state;
265 
266 	switch (parse->state)
267 	{
268 		case JM_EXPECT_FILES_NEXT:
269 		case JM_EXPECT_WAL_RANGES_NEXT:
270 			parse->state = JM_EXPECT_TOPLEVEL_FIELD;
271 			break;
272 		default:
273 			json_manifest_parse_failure(parse->context,
274 										"unexpected array end");
275 			break;
276 	}
277 }
278 
279 /*
280  * Invoked at the start of each object field in the JSON document.
281  */
282 static void
json_manifest_object_field_start(void * state,char * fname,bool isnull)283 json_manifest_object_field_start(void *state, char *fname, bool isnull)
284 {
285 	JsonManifestParseState *parse = state;
286 
287 	switch (parse->state)
288 	{
289 		case JM_EXPECT_TOPLEVEL_FIELD:
290 
291 			/*
292 			 * Inside toplevel object. The version indicator should always be
293 			 * the first field.
294 			 */
295 			if (!parse->saw_version_field)
296 			{
297 				if (strcmp(fname, "PostgreSQL-Backup-Manifest-Version") != 0)
298 					json_manifest_parse_failure(parse->context,
299 												"expected version indicator");
300 				parse->state = JM_EXPECT_VERSION_VALUE;
301 				parse->saw_version_field = true;
302 				break;
303 			}
304 
305 			/* Is this the list of files? */
306 			if (strcmp(fname, "Files") == 0)
307 			{
308 				parse->state = JM_EXPECT_FILES_START;
309 				break;
310 			}
311 
312 			/* Is this the list of WAL ranges? */
313 			if (strcmp(fname, "WAL-Ranges") == 0)
314 			{
315 				parse->state = JM_EXPECT_WAL_RANGES_START;
316 				break;
317 			}
318 
319 			/* Is this the manifest checksum? */
320 			if (strcmp(fname, "Manifest-Checksum") == 0)
321 			{
322 				parse->state = JM_EXPECT_MANIFEST_CHECKSUM_VALUE;
323 				break;
324 			}
325 
326 			/* It's not a field we recognize. */
327 			json_manifest_parse_failure(parse->context,
328 										"unrecognized top-level field");
329 			break;
330 
331 		case JM_EXPECT_THIS_FILE_FIELD:
332 			/* Inside object for one file; which key have we got? */
333 			if (strcmp(fname, "Path") == 0)
334 				parse->file_field = JMFF_PATH;
335 			else if (strcmp(fname, "Encoded-Path") == 0)
336 				parse->file_field = JMFF_ENCODED_PATH;
337 			else if (strcmp(fname, "Size") == 0)
338 				parse->file_field = JMFF_SIZE;
339 			else if (strcmp(fname, "Last-Modified") == 0)
340 				parse->file_field = JMFF_LAST_MODIFIED;
341 			else if (strcmp(fname, "Checksum-Algorithm") == 0)
342 				parse->file_field = JMFF_CHECKSUM_ALGORITHM;
343 			else if (strcmp(fname, "Checksum") == 0)
344 				parse->file_field = JMFF_CHECKSUM;
345 			else
346 				json_manifest_parse_failure(parse->context,
347 											"unexpected file field");
348 			parse->state = JM_EXPECT_THIS_FILE_VALUE;
349 			break;
350 
351 		case JM_EXPECT_THIS_WAL_RANGE_FIELD:
352 			/* Inside object for one file; which key have we got? */
353 			if (strcmp(fname, "Timeline") == 0)
354 				parse->wal_range_field = JMWRF_TIMELINE;
355 			else if (strcmp(fname, "Start-LSN") == 0)
356 				parse->wal_range_field = JMWRF_START_LSN;
357 			else if (strcmp(fname, "End-LSN") == 0)
358 				parse->wal_range_field = JMWRF_END_LSN;
359 			else
360 				json_manifest_parse_failure(parse->context,
361 											"unexpected WAL range field");
362 			parse->state = JM_EXPECT_THIS_WAL_RANGE_VALUE;
363 			break;
364 
365 		default:
366 			json_manifest_parse_failure(parse->context,
367 										"unexpected object field");
368 			break;
369 	}
370 }
371 
372 /*
373  * Invoked at the start of each scalar in the JSON document.
374  *
375  * Object field names don't reach this code; those are handled by
376  * json_manifest_object_field_start. When we're inside of the object for
377  * a particular file or WAL range, that function will have noticed the name
378  * of the field, and we'll get the corresponding value here. When we're in
379  * the toplevel object, the parse state itself tells us which field this is.
380  *
381  * In all cases except for PostgreSQL-Backup-Manifest-Version, which we
382  * can just check on the spot, the goal here is just to save the value in
383  * the parse state for later use. We don't actually do anything until we
384  * reach either the end of the object representing this file, or the end
385  * of the manifest, as the case may be.
386  */
387 static void
json_manifest_scalar(void * state,char * token,JsonTokenType tokentype)388 json_manifest_scalar(void *state, char *token, JsonTokenType tokentype)
389 {
390 	JsonManifestParseState *parse = state;
391 
392 	switch (parse->state)
393 	{
394 		case JM_EXPECT_VERSION_VALUE:
395 			if (strcmp(token, "1") != 0)
396 				json_manifest_parse_failure(parse->context,
397 											"unexpected manifest version");
398 			parse->state = JM_EXPECT_TOPLEVEL_FIELD;
399 			break;
400 
401 		case JM_EXPECT_THIS_FILE_VALUE:
402 			switch (parse->file_field)
403 			{
404 				case JMFF_PATH:
405 					parse->pathname = token;
406 					break;
407 				case JMFF_ENCODED_PATH:
408 					parse->encoded_pathname = token;
409 					break;
410 				case JMFF_SIZE:
411 					parse->size = token;
412 					break;
413 				case JMFF_LAST_MODIFIED:
414 					pfree(token);	/* unused */
415 					break;
416 				case JMFF_CHECKSUM_ALGORITHM:
417 					parse->algorithm = token;
418 					break;
419 				case JMFF_CHECKSUM:
420 					parse->checksum = token;
421 					break;
422 			}
423 			parse->state = JM_EXPECT_THIS_FILE_FIELD;
424 			break;
425 
426 		case JM_EXPECT_THIS_WAL_RANGE_VALUE:
427 			switch (parse->wal_range_field)
428 			{
429 				case JMWRF_TIMELINE:
430 					parse->timeline = token;
431 					break;
432 				case JMWRF_START_LSN:
433 					parse->start_lsn = token;
434 					break;
435 				case JMWRF_END_LSN:
436 					parse->end_lsn = token;
437 					break;
438 			}
439 			parse->state = JM_EXPECT_THIS_WAL_RANGE_FIELD;
440 			break;
441 
442 		case JM_EXPECT_MANIFEST_CHECKSUM_VALUE:
443 			parse->state = JM_EXPECT_TOPLEVEL_END;
444 			parse->manifest_checksum = token;
445 			break;
446 
447 		default:
448 			json_manifest_parse_failure(parse->context, "unexpected scalar");
449 			break;
450 	}
451 }
452 
453 /*
454  * Do additional parsing and sanity-checking of the details gathered for one
455  * file, and invoke the per-file callback so that the caller gets those
456  * details. This happens for each file when the corresponding JSON object is
457  * completely parsed.
458  */
459 static void
json_manifest_finalize_file(JsonManifestParseState * parse)460 json_manifest_finalize_file(JsonManifestParseState *parse)
461 {
462 	JsonManifestParseContext *context = parse->context;
463 	size_t		size;
464 	char	   *ep;
465 	int			checksum_string_length;
466 	pg_checksum_type checksum_type;
467 	int			checksum_length;
468 	uint8	   *checksum_payload;
469 
470 	/* Pathname and size are required. */
471 	if (parse->pathname == NULL && parse->encoded_pathname == NULL)
472 		json_manifest_parse_failure(parse->context, "missing path name");
473 	if (parse->pathname != NULL && parse->encoded_pathname != NULL)
474 		json_manifest_parse_failure(parse->context,
475 									"both path name and encoded path name");
476 	if (parse->size == NULL)
477 		json_manifest_parse_failure(parse->context, "missing size");
478 	if (parse->algorithm == NULL && parse->checksum != NULL)
479 		json_manifest_parse_failure(parse->context,
480 									"checksum without algorithm");
481 
482 	/* Decode encoded pathname, if that's what we have. */
483 	if (parse->encoded_pathname != NULL)
484 	{
485 		int			encoded_length = strlen(parse->encoded_pathname);
486 		int			raw_length = encoded_length / 2;
487 
488 		parse->pathname = palloc(raw_length + 1);
489 		if (encoded_length % 2 != 0 ||
490 			!hexdecode_string((uint8 *) parse->pathname,
491 							  parse->encoded_pathname,
492 							  raw_length))
493 			json_manifest_parse_failure(parse->context,
494 										"could not decode file name");
495 		parse->pathname[raw_length] = '\0';
496 		pfree(parse->encoded_pathname);
497 		parse->encoded_pathname = NULL;
498 	}
499 
500 	/* Parse size. */
501 	size = strtoul(parse->size, &ep, 10);
502 	if (*ep)
503 		json_manifest_parse_failure(parse->context,
504 									"file size is not an integer");
505 
506 	/* Parse the checksum algorithm, if it's present. */
507 	if (parse->algorithm == NULL)
508 		checksum_type = CHECKSUM_TYPE_NONE;
509 	else if (!pg_checksum_parse_type(parse->algorithm, &checksum_type))
510 		context->error_cb(context, "unrecognized checksum algorithm: \"%s\"",
511 						  parse->algorithm);
512 
513 	/* Parse the checksum payload, if it's present. */
514 	checksum_string_length = parse->checksum == NULL ? 0
515 		: strlen(parse->checksum);
516 	if (checksum_string_length == 0)
517 	{
518 		checksum_length = 0;
519 		checksum_payload = NULL;
520 	}
521 	else
522 	{
523 		checksum_length = checksum_string_length / 2;
524 		checksum_payload = palloc(checksum_length);
525 		if (checksum_string_length % 2 != 0 ||
526 			!hexdecode_string(checksum_payload, parse->checksum,
527 							  checksum_length))
528 			context->error_cb(context,
529 							  "invalid checksum for file \"%s\": \"%s\"",
530 							  parse->pathname, parse->checksum);
531 	}
532 
533 	/* Invoke the callback with the details we've gathered. */
534 	context->perfile_cb(context, parse->pathname, size,
535 						checksum_type, checksum_length, checksum_payload);
536 
537 	/* Free memory we no longer need. */
538 	if (parse->size != NULL)
539 	{
540 		pfree(parse->size);
541 		parse->size = NULL;
542 	}
543 	if (parse->algorithm != NULL)
544 	{
545 		pfree(parse->algorithm);
546 		parse->algorithm = NULL;
547 	}
548 	if (parse->checksum != NULL)
549 	{
550 		pfree(parse->checksum);
551 		parse->checksum = NULL;
552 	}
553 }
554 
555 /*
556  * Do additional parsing and sanity-checking of the details gathered for one
557  * WAL range, and invoke the per-WAL-range callback so that the caller gets
558  * those details. This happens for each WAL range when the corresponding JSON
559  * object is completely parsed.
560  */
561 static void
json_manifest_finalize_wal_range(JsonManifestParseState * parse)562 json_manifest_finalize_wal_range(JsonManifestParseState *parse)
563 {
564 	JsonManifestParseContext *context = parse->context;
565 	TimeLineID	tli;
566 	XLogRecPtr	start_lsn,
567 				end_lsn;
568 	char	   *ep;
569 
570 	/* Make sure all fields are present. */
571 	if (parse->timeline == NULL)
572 		json_manifest_parse_failure(parse->context, "missing timeline");
573 	if (parse->start_lsn == NULL)
574 		json_manifest_parse_failure(parse->context, "missing start LSN");
575 	if (parse->end_lsn == NULL)
576 		json_manifest_parse_failure(parse->context, "missing end LSN");
577 
578 	/* Parse timeline. */
579 	tli = strtoul(parse->timeline, &ep, 10);
580 	if (*ep)
581 		json_manifest_parse_failure(parse->context,
582 									"timeline is not an integer");
583 	if (!parse_xlogrecptr(&start_lsn, parse->start_lsn))
584 		json_manifest_parse_failure(parse->context,
585 									"could not parse start LSN");
586 	if (!parse_xlogrecptr(&end_lsn, parse->end_lsn))
587 		json_manifest_parse_failure(parse->context,
588 									"could not parse end LSN");
589 
590 	/* Invoke the callback with the details we've gathered. */
591 	context->perwalrange_cb(context, tli, start_lsn, end_lsn);
592 
593 	/* Free memory we no longer need. */
594 	if (parse->timeline != NULL)
595 	{
596 		pfree(parse->timeline);
597 		parse->timeline = NULL;
598 	}
599 	if (parse->start_lsn != NULL)
600 	{
601 		pfree(parse->start_lsn);
602 		parse->start_lsn = NULL;
603 	}
604 	if (parse->end_lsn != NULL)
605 	{
606 		pfree(parse->end_lsn);
607 		parse->end_lsn = NULL;
608 	}
609 }
610 
611 /*
612  * Verify that the manifest checksum is correct.
613  *
614  * The last line of the manifest file is excluded from the manifest checksum,
615  * because the last line is expected to contain the checksum that covers
616  * the rest of the file.
617  */
618 static void
verify_manifest_checksum(JsonManifestParseState * parse,char * buffer,size_t size)619 verify_manifest_checksum(JsonManifestParseState *parse, char *buffer,
620 						 size_t size)
621 {
622 	JsonManifestParseContext *context = parse->context;
623 	size_t		i;
624 	size_t		number_of_newlines = 0;
625 	size_t		ultimate_newline = 0;
626 	size_t		penultimate_newline = 0;
627 	pg_sha256_ctx manifest_ctx;
628 	uint8		manifest_checksum_actual[PG_SHA256_DIGEST_LENGTH];
629 	uint8		manifest_checksum_expected[PG_SHA256_DIGEST_LENGTH];
630 
631 	/* Find the last two newlines in the file. */
632 	for (i = 0; i < size; ++i)
633 	{
634 		if (buffer[i] == '\n')
635 		{
636 			++number_of_newlines;
637 			penultimate_newline = ultimate_newline;
638 			ultimate_newline = i;
639 		}
640 	}
641 
642 	/*
643 	 * Make sure that the last newline is right at the end, and that there are
644 	 * at least two lines total. We need this to be true in order for the
645 	 * following code, which computes the manifest checksum, to work properly.
646 	 */
647 	if (number_of_newlines < 2)
648 		json_manifest_parse_failure(parse->context,
649 									"expected at least 2 lines");
650 	if (ultimate_newline != size - 1)
651 		json_manifest_parse_failure(parse->context,
652 									"last line not newline-terminated");
653 
654 	/* Checksum the rest. */
655 	pg_sha256_init(&manifest_ctx);
656 	pg_sha256_update(&manifest_ctx, (uint8 *) buffer, penultimate_newline + 1);
657 	pg_sha256_final(&manifest_ctx, manifest_checksum_actual);
658 
659 	/* Now verify it. */
660 	if (parse->manifest_checksum == NULL)
661 		context->error_cb(parse->context, "manifest has no checksum");
662 	if (strlen(parse->manifest_checksum) != PG_SHA256_DIGEST_LENGTH * 2 ||
663 		!hexdecode_string(manifest_checksum_expected, parse->manifest_checksum,
664 						  PG_SHA256_DIGEST_LENGTH))
665 		context->error_cb(context, "invalid manifest checksum: \"%s\"",
666 						  parse->manifest_checksum);
667 	if (memcmp(manifest_checksum_actual, manifest_checksum_expected,
668 			   PG_SHA256_DIGEST_LENGTH) != 0)
669 		context->error_cb(context, "manifest checksum mismatch");
670 }
671 
672 /*
673  * Report a parse error.
674  *
675  * This is intended to be used for fairly low-level failures that probably
676  * shouldn't occur unless somebody has deliberately constructed a bad manifest,
677  * or unless the server is generating bad manifests due to some bug. msg should
678  * be a short string giving some hint as to what the problem is.
679  */
680 static void
json_manifest_parse_failure(JsonManifestParseContext * context,char * msg)681 json_manifest_parse_failure(JsonManifestParseContext *context, char *msg)
682 {
683 	context->error_cb(context, "could not parse backup manifest: %s", msg);
684 }
685 
686 /*
687  * Convert a character which represents a hexadecimal digit to an integer.
688  *
689  * Returns -1 if the character is not a hexadecimal digit.
690  */
691 static int
hexdecode_char(char c)692 hexdecode_char(char c)
693 {
694 	if (c >= '0' && c <= '9')
695 		return c - '0';
696 	if (c >= 'a' && c <= 'f')
697 		return c - 'a' + 10;
698 	if (c >= 'A' && c <= 'F')
699 		return c - 'A' + 10;
700 
701 	return -1;
702 }
703 
704 /*
705  * Decode a hex string into a byte string, 2 hex chars per byte.
706  *
707  * Returns false if invalid characters are encountered; otherwise true.
708  */
709 static bool
hexdecode_string(uint8 * result,char * input,int nbytes)710 hexdecode_string(uint8 *result, char *input, int nbytes)
711 {
712 	int			i;
713 
714 	for (i = 0; i < nbytes; ++i)
715 	{
716 		int			n1 = hexdecode_char(input[i * 2]);
717 		int			n2 = hexdecode_char(input[i * 2 + 1]);
718 
719 		if (n1 < 0 || n2 < 0)
720 			return false;
721 		result[i] = n1 * 16 + n2;
722 	}
723 
724 	return true;
725 }
726 
727 /*
728  * Parse an XLogRecPtr expressed using the usual string format.
729  */
730 static bool
parse_xlogrecptr(XLogRecPtr * result,char * input)731 parse_xlogrecptr(XLogRecPtr *result, char *input)
732 {
733 	uint32		hi;
734 	uint32		lo;
735 
736 	if (sscanf(input, "%X/%X", &hi, &lo) != 2)
737 		return false;
738 	*result = ((uint64) hi) << 32 | lo;
739 	return true;
740 }
741