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