Coverage Report

Created: 2026-08-14 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/common/parse_manifest.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * parse_manifest.c
4
 *    Parse a backup manifest in JSON format.
5
 *
6
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 * src/common/parse_manifest.c
10
 *
11
 *-------------------------------------------------------------------------
12
 */
13
14
#include "postgres_fe.h"
15
16
#include "common/jsonapi.h"
17
#include "common/parse_manifest.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_SYSTEM_IDENTIFIER_VALUE,
29
  JM_EXPECT_FILES_START,
30
  JM_EXPECT_FILES_NEXT,
31
  JM_EXPECT_THIS_FILE_FIELD,
32
  JM_EXPECT_THIS_FILE_VALUE,
33
  JM_EXPECT_WAL_RANGES_START,
34
  JM_EXPECT_WAL_RANGES_NEXT,
35
  JM_EXPECT_THIS_WAL_RANGE_FIELD,
36
  JM_EXPECT_THIS_WAL_RANGE_VALUE,
37
  JM_EXPECT_MANIFEST_CHECKSUM_VALUE,
38
  JM_EXPECT_EOF,
39
} JsonManifestSemanticState;
40
41
/*
42
 * Possible fields for one file as described by the manifest.
43
 */
44
typedef enum
45
{
46
  JMFF_PATH,
47
  JMFF_ENCODED_PATH,
48
  JMFF_SIZE,
49
  JMFF_LAST_MODIFIED,
50
  JMFF_CHECKSUM_ALGORITHM,
51
  JMFF_CHECKSUM,
52
} JsonManifestFileField;
53
54
/*
55
 * Possible fields for one file as described by the manifest.
56
 */
57
typedef enum
58
{
59
  JMWRF_TIMELINE,
60
  JMWRF_START_LSN,
61
  JMWRF_END_LSN,
62
} JsonManifestWALRangeField;
63
64
/*
65
 * Internal state used while decoding the JSON-format backup manifest.
66
 */
67
typedef struct
68
{
69
  JsonManifestParseContext *context;
70
  JsonManifestSemanticState state;
71
72
  /* These fields are used for parsing objects in the list of files. */
73
  JsonManifestFileField file_field;
74
  char     *pathname;
75
  char     *encoded_pathname;
76
  char     *size;
77
  char     *algorithm;
78
  pg_checksum_type checksum_algorithm;
79
  char     *checksum;
80
81
  /* These fields are used for parsing objects in the list of WAL ranges. */
82
  JsonManifestWALRangeField wal_range_field;
83
  char     *timeline;
84
  char     *start_lsn;
85
  char     *end_lsn;
86
87
  /* Miscellaneous other stuff. */
88
  bool    saw_version_field;
89
  char     *manifest_version;
90
  char     *manifest_system_identifier;
91
  char     *manifest_checksum;
92
} JsonManifestParseState;
93
94
/* typedef appears in parse_manifest.h */
95
struct JsonManifestParseIncrementalState
96
{
97
  JsonLexContext lex;
98
  JsonSemAction sem;
99
  pg_cryptohash_ctx *manifest_ctx;
100
};
101
102
static JsonParseErrorType json_manifest_object_start(void *state);
103
static JsonParseErrorType json_manifest_object_end(void *state);
104
static JsonParseErrorType json_manifest_array_start(void *state);
105
static JsonParseErrorType json_manifest_array_end(void *state);
106
static JsonParseErrorType json_manifest_object_field_start(void *state, char *fname,
107
                               bool isnull);
108
static JsonParseErrorType json_manifest_scalar(void *state, char *token,
109
                         JsonTokenType tokentype);
110
static void json_manifest_finalize_version(JsonManifestParseState *parse);
111
static void json_manifest_finalize_system_identifier(JsonManifestParseState *parse);
112
static void json_manifest_finalize_file(JsonManifestParseState *parse);
113
static void json_manifest_finalize_wal_range(JsonManifestParseState *parse);
114
static void verify_manifest_checksum(JsonManifestParseState *parse,
115
                   const char *buffer, size_t size,
116
                   pg_cryptohash_ctx *incr_ctx);
117
pg_noreturn static void json_manifest_parse_failure(JsonManifestParseContext *context,
118
                          char *msg);
119
120
static int  hexdecode_char(char c);
121
static bool hexdecode_string(uint8 *result, char *input, int nbytes);
122
static bool parse_xlogrecptr(XLogRecPtr *result, char *input);
123
124
/*
125
 * Set up for incremental parsing of the manifest.
126
 */
127
128
JsonManifestParseIncrementalState *
129
json_parse_manifest_incremental_init(JsonManifestParseContext *context)
130
0
{
131
0
  JsonManifestParseIncrementalState *incstate;
132
0
  JsonManifestParseState *parse;
133
0
  pg_cryptohash_ctx *manifest_ctx;
134
135
0
  incstate = palloc_object(JsonManifestParseIncrementalState);
136
0
  parse = palloc_object(JsonManifestParseState);
137
138
0
  parse->context = context;
139
0
  parse->state = JM_EXPECT_TOPLEVEL_START;
140
0
  parse->saw_version_field = false;
141
142
0
  makeJsonLexContextIncremental(&(incstate->lex), PG_UTF8, true);
143
144
0
  incstate->sem.semstate = parse;
145
0
  incstate->sem.object_start = json_manifest_object_start;
146
0
  incstate->sem.object_end = json_manifest_object_end;
147
0
  incstate->sem.array_start = json_manifest_array_start;
148
0
  incstate->sem.array_end = json_manifest_array_end;
149
0
  incstate->sem.object_field_start = json_manifest_object_field_start;
150
0
  incstate->sem.object_field_end = NULL;
151
0
  incstate->sem.array_element_start = NULL;
152
0
  incstate->sem.array_element_end = NULL;
153
0
  incstate->sem.scalar = json_manifest_scalar;
154
155
0
  manifest_ctx = pg_cryptohash_create(PG_SHA256);
156
0
  if (manifest_ctx == NULL)
157
0
    context->error_cb(context, "out of memory");
158
0
  if (pg_cryptohash_init(manifest_ctx) < 0)
159
0
    context->error_cb(context, "could not initialize checksum of manifest");
160
0
  incstate->manifest_ctx = manifest_ctx;
161
162
0
  return incstate;
163
0
}
164
165
/*
166
 * Free an incremental state object and its contents.
167
 */
168
void
169
json_parse_manifest_incremental_shutdown(JsonManifestParseIncrementalState *incstate)
170
0
{
171
0
  pfree(incstate->sem.semstate);
172
0
  freeJsonLexContext(&(incstate->lex));
173
  /* incstate->manifest_ctx has already been freed */
174
0
  pfree(incstate);
175
0
}
176
177
/*
178
 * parse the manifest in pieces.
179
 *
180
 * The caller must ensure that the final piece contains the final lines
181
 * with the complete checksum.
182
 */
183
184
void
185
json_parse_manifest_incremental_chunk(JsonManifestParseIncrementalState *incstate,
186
                    const char *chunk, size_t size, bool is_last)
187
0
{
188
0
  JsonParseErrorType res,
189
0
        expected;
190
0
  JsonManifestParseState *parse = incstate->sem.semstate;
191
0
  JsonManifestParseContext *context = parse->context;
192
193
0
  res = pg_parse_json_incremental(&(incstate->lex), &(incstate->sem),
194
0
                  chunk, size, is_last);
195
196
0
  expected = is_last ? JSON_SUCCESS : JSON_INCOMPLETE;
197
198
0
  if (res != expected)
199
0
    json_manifest_parse_failure(context,
200
0
                  json_errdetail(res, &(incstate->lex)));
201
202
0
  if (is_last && parse->state != JM_EXPECT_EOF)
203
0
    json_manifest_parse_failure(context, "manifest ended unexpectedly");
204
205
0
  if (!is_last)
206
0
  {
207
0
    if (pg_cryptohash_update(incstate->manifest_ctx,
208
0
                 (const uint8 *) chunk, size) < 0)
209
0
      context->error_cb(context, "could not update checksum of manifest");
210
0
  }
211
0
  else
212
0
  {
213
0
    verify_manifest_checksum(parse, chunk, size, incstate->manifest_ctx);
214
0
  }
215
0
}
216
217
218
/*
219
 * Main entrypoint to parse a JSON-format backup manifest.
220
 *
221
 * Caller should set up the parsing context and then invoke this function.
222
 * For each file whose information is extracted from the manifest,
223
 * context->per_file_cb is invoked.  In case of trouble, context->error_cb is
224
 * invoked and is expected not to return.
225
 */
226
void
227
json_parse_manifest(JsonManifestParseContext *context, const char *buffer,
228
          size_t size)
229
0
{
230
0
  JsonLexContext *lex;
231
0
  JsonParseErrorType json_error;
232
0
  JsonSemAction sem;
233
0
  JsonManifestParseState parse;
234
235
  /* Set up our private parsing context. */
236
0
  parse.context = context;
237
0
  parse.state = JM_EXPECT_TOPLEVEL_START;
238
0
  parse.saw_version_field = false;
239
240
  /* Create a JSON lexing context. */
241
0
  lex = makeJsonLexContextCstringLen(NULL, buffer, size, PG_UTF8, true);
242
243
  /* Set up semantic actions. */
244
0
  sem.semstate = &parse;
245
0
  sem.object_start = json_manifest_object_start;
246
0
  sem.object_end = json_manifest_object_end;
247
0
  sem.array_start = json_manifest_array_start;
248
0
  sem.array_end = json_manifest_array_end;
249
0
  sem.object_field_start = json_manifest_object_field_start;
250
0
  sem.object_field_end = NULL;
251
0
  sem.array_element_start = NULL;
252
0
  sem.array_element_end = NULL;
253
0
  sem.scalar = json_manifest_scalar;
254
255
  /* Run the actual JSON parser. */
256
0
  json_error = pg_parse_json(lex, &sem);
257
0
  if (json_error != JSON_SUCCESS)
258
0
    json_manifest_parse_failure(context, json_errdetail(json_error, lex));
259
0
  if (parse.state != JM_EXPECT_EOF)
260
0
    json_manifest_parse_failure(context, "manifest ended unexpectedly");
261
262
  /* Verify the manifest checksum. */
263
0
  verify_manifest_checksum(&parse, buffer, size, NULL);
264
265
0
  freeJsonLexContext(lex);
266
0
}
267
268
/*
269
 * Invoked at the start of each object in the JSON document.
270
 *
271
 * The document as a whole is expected to be an object; each file and each
272
 * WAL range is also expected to be an object. If we're anywhere else in the
273
 * document, it's an error.
274
 */
275
static JsonParseErrorType
276
json_manifest_object_start(void *state)
277
0
{
278
0
  JsonManifestParseState *parse = state;
279
280
0
  switch (parse->state)
281
0
  {
282
0
    case JM_EXPECT_TOPLEVEL_START:
283
0
      parse->state = JM_EXPECT_TOPLEVEL_FIELD;
284
0
      break;
285
0
    case JM_EXPECT_FILES_NEXT:
286
0
      parse->state = JM_EXPECT_THIS_FILE_FIELD;
287
0
      parse->pathname = NULL;
288
0
      parse->encoded_pathname = NULL;
289
0
      parse->size = NULL;
290
0
      parse->algorithm = NULL;
291
0
      parse->checksum = NULL;
292
0
      break;
293
0
    case JM_EXPECT_WAL_RANGES_NEXT:
294
0
      parse->state = JM_EXPECT_THIS_WAL_RANGE_FIELD;
295
0
      parse->timeline = NULL;
296
0
      parse->start_lsn = NULL;
297
0
      parse->end_lsn = NULL;
298
0
      break;
299
0
    default:
300
0
      json_manifest_parse_failure(parse->context,
301
0
                    "unexpected object start");
302
0
      break;
303
0
  }
304
305
0
  return JSON_SUCCESS;
306
0
}
307
308
/*
309
 * Invoked at the end of each object in the JSON document.
310
 *
311
 * The possible cases here are the same as for json_manifest_object_start.
312
 * There's nothing special to do at the end of the document, but when we
313
 * reach the end of an object representing a particular file or WAL range,
314
 * we must call json_manifest_finalize_file() to save the associated details.
315
 */
316
static JsonParseErrorType
317
json_manifest_object_end(void *state)
318
0
{
319
0
  JsonManifestParseState *parse = state;
320
321
0
  switch (parse->state)
322
0
  {
323
0
    case JM_EXPECT_TOPLEVEL_END:
324
0
      parse->state = JM_EXPECT_EOF;
325
0
      break;
326
0
    case JM_EXPECT_THIS_FILE_FIELD:
327
0
      json_manifest_finalize_file(parse);
328
0
      parse->state = JM_EXPECT_FILES_NEXT;
329
0
      break;
330
0
    case JM_EXPECT_THIS_WAL_RANGE_FIELD:
331
0
      json_manifest_finalize_wal_range(parse);
332
0
      parse->state = JM_EXPECT_WAL_RANGES_NEXT;
333
0
      break;
334
0
    default:
335
0
      json_manifest_parse_failure(parse->context,
336
0
                    "unexpected object end");
337
0
      break;
338
0
  }
339
340
0
  return JSON_SUCCESS;
341
0
}
342
343
/*
344
 * Invoked at the start of each array in the JSON document.
345
 *
346
 * Within the toplevel object, the value associated with the "Files" key
347
 * should be an array. Similarly for the "WAL-Ranges" key. No other arrays
348
 * are expected.
349
 */
350
static JsonParseErrorType
351
json_manifest_array_start(void *state)
352
0
{
353
0
  JsonManifestParseState *parse = state;
354
355
0
  switch (parse->state)
356
0
  {
357
0
    case JM_EXPECT_FILES_START:
358
0
      parse->state = JM_EXPECT_FILES_NEXT;
359
0
      break;
360
0
    case JM_EXPECT_WAL_RANGES_START:
361
0
      parse->state = JM_EXPECT_WAL_RANGES_NEXT;
362
0
      break;
363
0
    default:
364
0
      json_manifest_parse_failure(parse->context,
365
0
                    "unexpected array start");
366
0
      break;
367
0
  }
368
369
0
  return JSON_SUCCESS;
370
0
}
371
372
/*
373
 * Invoked at the end of each array in the JSON document.
374
 *
375
 * The cases here are analogous to those in json_manifest_array_start.
376
 */
377
static JsonParseErrorType
378
json_manifest_array_end(void *state)
379
0
{
380
0
  JsonManifestParseState *parse = state;
381
382
0
  switch (parse->state)
383
0
  {
384
0
    case JM_EXPECT_FILES_NEXT:
385
0
    case JM_EXPECT_WAL_RANGES_NEXT:
386
0
      parse->state = JM_EXPECT_TOPLEVEL_FIELD;
387
0
      break;
388
0
    default:
389
0
      json_manifest_parse_failure(parse->context,
390
0
                    "unexpected array end");
391
0
      break;
392
0
  }
393
394
0
  return JSON_SUCCESS;
395
0
}
396
397
/*
398
 * Invoked at the start of each object field in the JSON document.
399
 */
400
static JsonParseErrorType
401
json_manifest_object_field_start(void *state, char *fname, bool isnull)
402
0
{
403
0
  JsonManifestParseState *parse = state;
404
405
0
  switch (parse->state)
406
0
  {
407
0
    case JM_EXPECT_TOPLEVEL_FIELD:
408
409
      /*
410
       * Inside toplevel object. The version indicator should always be
411
       * the first field.
412
       */
413
0
      if (!parse->saw_version_field)
414
0
      {
415
0
        if (strcmp(fname, "PostgreSQL-Backup-Manifest-Version") != 0)
416
0
          json_manifest_parse_failure(parse->context,
417
0
                        "expected version indicator");
418
0
        parse->state = JM_EXPECT_VERSION_VALUE;
419
0
        parse->saw_version_field = true;
420
0
        break;
421
0
      }
422
423
      /* Is this the system identifier? */
424
0
      if (strcmp(fname, "System-Identifier") == 0)
425
0
      {
426
0
        parse->state = JM_EXPECT_SYSTEM_IDENTIFIER_VALUE;
427
0
        break;
428
0
      }
429
430
      /* Is this the list of files? */
431
0
      if (strcmp(fname, "Files") == 0)
432
0
      {
433
0
        parse->state = JM_EXPECT_FILES_START;
434
0
        break;
435
0
      }
436
437
      /* Is this the list of WAL ranges? */
438
0
      if (strcmp(fname, "WAL-Ranges") == 0)
439
0
      {
440
0
        parse->state = JM_EXPECT_WAL_RANGES_START;
441
0
        break;
442
0
      }
443
444
      /* Is this the manifest checksum? */
445
0
      if (strcmp(fname, "Manifest-Checksum") == 0)
446
0
      {
447
0
        parse->state = JM_EXPECT_MANIFEST_CHECKSUM_VALUE;
448
0
        break;
449
0
      }
450
451
      /* It's not a field we recognize. */
452
0
      json_manifest_parse_failure(parse->context,
453
0
                    "unrecognized top-level field");
454
0
      break;
455
456
0
    case JM_EXPECT_THIS_FILE_FIELD:
457
      /* Inside object for one file; which key have we got? */
458
0
      if (strcmp(fname, "Path") == 0)
459
0
        parse->file_field = JMFF_PATH;
460
0
      else if (strcmp(fname, "Encoded-Path") == 0)
461
0
        parse->file_field = JMFF_ENCODED_PATH;
462
0
      else if (strcmp(fname, "Size") == 0)
463
0
        parse->file_field = JMFF_SIZE;
464
0
      else if (strcmp(fname, "Last-Modified") == 0)
465
0
        parse->file_field = JMFF_LAST_MODIFIED;
466
0
      else if (strcmp(fname, "Checksum-Algorithm") == 0)
467
0
        parse->file_field = JMFF_CHECKSUM_ALGORITHM;
468
0
      else if (strcmp(fname, "Checksum") == 0)
469
0
        parse->file_field = JMFF_CHECKSUM;
470
0
      else
471
0
        json_manifest_parse_failure(parse->context,
472
0
                      "unexpected file field");
473
0
      parse->state = JM_EXPECT_THIS_FILE_VALUE;
474
0
      break;
475
476
0
    case JM_EXPECT_THIS_WAL_RANGE_FIELD:
477
      /* Inside object for one file; which key have we got? */
478
0
      if (strcmp(fname, "Timeline") == 0)
479
0
        parse->wal_range_field = JMWRF_TIMELINE;
480
0
      else if (strcmp(fname, "Start-LSN") == 0)
481
0
        parse->wal_range_field = JMWRF_START_LSN;
482
0
      else if (strcmp(fname, "End-LSN") == 0)
483
0
        parse->wal_range_field = JMWRF_END_LSN;
484
0
      else
485
0
        json_manifest_parse_failure(parse->context,
486
0
                      "unexpected WAL range field");
487
0
      parse->state = JM_EXPECT_THIS_WAL_RANGE_VALUE;
488
0
      break;
489
490
0
    default:
491
0
      json_manifest_parse_failure(parse->context,
492
0
                    "unexpected object field");
493
0
      break;
494
0
  }
495
496
0
  pfree(fname);
497
498
0
  return JSON_SUCCESS;
499
0
}
500
501
/*
502
 * Invoked at the start of each scalar in the JSON document.
503
 *
504
 * Object field names don't reach this code; those are handled by
505
 * json_manifest_object_field_start. When we're inside of the object for
506
 * a particular file or WAL range, that function will have noticed the name
507
 * of the field, and we'll get the corresponding value here. When we're in
508
 * the toplevel object, the parse state itself tells us which field this is.
509
 *
510
 * In all cases except for PostgreSQL-Backup-Manifest-Version, which we
511
 * can just check on the spot, the goal here is just to save the value in
512
 * the parse state for later use. We don't actually do anything until we
513
 * reach either the end of the object representing this file, or the end
514
 * of the manifest, as the case may be.
515
 */
516
static JsonParseErrorType
517
json_manifest_scalar(void *state, char *token, JsonTokenType tokentype)
518
0
{
519
0
  JsonManifestParseState *parse = state;
520
521
0
  switch (parse->state)
522
0
  {
523
0
    case JM_EXPECT_VERSION_VALUE:
524
0
      parse->manifest_version = token;
525
0
      json_manifest_finalize_version(parse);
526
0
      parse->state = JM_EXPECT_TOPLEVEL_FIELD;
527
0
      break;
528
529
0
    case JM_EXPECT_SYSTEM_IDENTIFIER_VALUE:
530
0
      parse->manifest_system_identifier = token;
531
0
      json_manifest_finalize_system_identifier(parse);
532
0
      parse->state = JM_EXPECT_TOPLEVEL_FIELD;
533
0
      break;
534
535
0
    case JM_EXPECT_THIS_FILE_VALUE:
536
0
      switch (parse->file_field)
537
0
      {
538
0
        case JMFF_PATH:
539
0
          parse->pathname = token;
540
0
          break;
541
0
        case JMFF_ENCODED_PATH:
542
0
          parse->encoded_pathname = token;
543
0
          break;
544
0
        case JMFF_SIZE:
545
0
          parse->size = token;
546
0
          break;
547
0
        case JMFF_LAST_MODIFIED:
548
0
          pfree(token); /* unused */
549
0
          break;
550
0
        case JMFF_CHECKSUM_ALGORITHM:
551
0
          parse->algorithm = token;
552
0
          break;
553
0
        case JMFF_CHECKSUM:
554
0
          parse->checksum = token;
555
0
          break;
556
0
      }
557
0
      parse->state = JM_EXPECT_THIS_FILE_FIELD;
558
0
      break;
559
560
0
    case JM_EXPECT_THIS_WAL_RANGE_VALUE:
561
0
      switch (parse->wal_range_field)
562
0
      {
563
0
        case JMWRF_TIMELINE:
564
0
          parse->timeline = token;
565
0
          break;
566
0
        case JMWRF_START_LSN:
567
0
          parse->start_lsn = token;
568
0
          break;
569
0
        case JMWRF_END_LSN:
570
0
          parse->end_lsn = token;
571
0
          break;
572
0
      }
573
0
      parse->state = JM_EXPECT_THIS_WAL_RANGE_FIELD;
574
0
      break;
575
576
0
    case JM_EXPECT_MANIFEST_CHECKSUM_VALUE:
577
0
      parse->state = JM_EXPECT_TOPLEVEL_END;
578
0
      parse->manifest_checksum = token;
579
0
      break;
580
581
0
    default:
582
0
      json_manifest_parse_failure(parse->context, "unexpected scalar");
583
0
      break;
584
0
  }
585
586
0
  return JSON_SUCCESS;
587
0
}
588
589
/*
590
 * Do additional parsing and sanity-checking of the manifest version, and invoke
591
 * the callback so that the caller can gets that detail and take actions
592
 * accordingly.  This happens for each manifest when the corresponding JSON
593
 * object is completely parsed.
594
 */
595
static void
596
json_manifest_finalize_version(JsonManifestParseState *parse)
597
0
{
598
0
  JsonManifestParseContext *context = parse->context;
599
0
  int     version;
600
0
  char     *ep;
601
602
0
  Assert(parse->saw_version_field);
603
604
  /* Parse version. */
605
0
  version = strtoi64(parse->manifest_version, &ep, 10);
606
0
  if (*ep)
607
0
    json_manifest_parse_failure(parse->context,
608
0
                  "manifest version not an integer");
609
610
0
  if (version != 1 && version != 2)
611
0
    json_manifest_parse_failure(parse->context,
612
0
                  "unexpected manifest version");
613
614
  /* Invoke the callback for version */
615
0
  context->version_cb(context, version);
616
0
}
617
618
/*
619
 * Do additional parsing and sanity-checking of the system identifier, and
620
 * invoke the callback so that the caller can gets that detail and take actions
621
 * accordingly.
622
 */
623
static void
624
json_manifest_finalize_system_identifier(JsonManifestParseState *parse)
625
0
{
626
0
  JsonManifestParseContext *context = parse->context;
627
0
  uint64    system_identifier;
628
0
  char     *ep;
629
630
0
  Assert(parse->manifest_system_identifier != NULL);
631
632
  /* Parse system identifier. */
633
0
  system_identifier = strtou64(parse->manifest_system_identifier, &ep, 10);
634
0
  if (ep == parse->manifest_system_identifier || *ep)
635
0
    json_manifest_parse_failure(parse->context,
636
0
                  "system identifier in manifest not an integer");
637
638
  /* Invoke the callback for system identifier */
639
0
  context->system_identifier_cb(context, system_identifier);
640
0
}
641
642
/*
643
 * Do additional parsing and sanity-checking of the details gathered for one
644
 * file, and invoke the per-file callback so that the caller gets those
645
 * details. This happens for each file when the corresponding JSON object is
646
 * completely parsed.
647
 */
648
static void
649
json_manifest_finalize_file(JsonManifestParseState *parse)
650
0
{
651
0
  JsonManifestParseContext *context = parse->context;
652
0
  uint64    size;
653
0
  char     *ep;
654
0
  int     checksum_string_length;
655
0
  pg_checksum_type checksum_type;
656
0
  int     checksum_length;
657
0
  uint8    *checksum_payload;
658
659
  /* Pathname and size are required. */
660
0
  if (parse->pathname == NULL && parse->encoded_pathname == NULL)
661
0
    json_manifest_parse_failure(parse->context, "missing path name");
662
0
  if (parse->pathname != NULL && parse->encoded_pathname != NULL)
663
0
    json_manifest_parse_failure(parse->context,
664
0
                  "both path name and encoded path name");
665
0
  if (parse->size == NULL)
666
0
    json_manifest_parse_failure(parse->context, "missing size");
667
0
  if (parse->algorithm == NULL && parse->checksum != NULL)
668
0
    json_manifest_parse_failure(parse->context,
669
0
                  "checksum without algorithm");
670
671
  /* Decode encoded pathname, if that's what we have. */
672
0
  if (parse->encoded_pathname != NULL)
673
0
  {
674
0
    int     encoded_length = strlen(parse->encoded_pathname);
675
0
    int     raw_length = encoded_length / 2;
676
677
0
    parse->pathname = palloc(raw_length + 1);
678
0
    if (encoded_length % 2 != 0 ||
679
0
      !hexdecode_string((uint8 *) parse->pathname,
680
0
                parse->encoded_pathname,
681
0
                raw_length))
682
0
      json_manifest_parse_failure(parse->context,
683
0
                    "could not decode file name");
684
0
    parse->pathname[raw_length] = '\0';
685
0
    pfree(parse->encoded_pathname);
686
0
    parse->encoded_pathname = NULL;
687
0
  }
688
689
  /* Parse size. */
690
0
  size = strtou64(parse->size, &ep, 10);
691
0
  if (ep == parse->size || *ep)
692
0
    json_manifest_parse_failure(parse->context,
693
0
                  "file size is not an integer");
694
695
  /* Parse the checksum algorithm, if it's present. */
696
0
  if (parse->algorithm == NULL)
697
0
    checksum_type = CHECKSUM_TYPE_NONE;
698
0
  else if (!pg_checksum_parse_type(parse->algorithm, &checksum_type))
699
0
    context->error_cb(context, "unrecognized checksum algorithm: \"%s\"",
700
0
              parse->algorithm);
701
702
  /* Parse the checksum payload, if it's present. */
703
0
  checksum_string_length = parse->checksum == NULL ? 0
704
0
    : strlen(parse->checksum);
705
0
  if (checksum_string_length == 0)
706
0
  {
707
0
    checksum_length = 0;
708
0
    checksum_payload = NULL;
709
0
  }
710
0
  else
711
0
  {
712
0
    checksum_length = checksum_string_length / 2;
713
0
    checksum_payload = palloc(checksum_length);
714
0
    if (checksum_string_length % 2 != 0 ||
715
0
      !hexdecode_string(checksum_payload, parse->checksum,
716
0
                checksum_length))
717
0
      context->error_cb(context,
718
0
                "invalid checksum for file \"%s\": \"%s\"",
719
0
                parse->pathname, parse->checksum);
720
0
  }
721
722
  /* Invoke the callback with the details we've gathered. */
723
0
  context->per_file_cb(context, parse->pathname, size,
724
0
             checksum_type, checksum_length, checksum_payload);
725
726
  /* Free memory we no longer need. */
727
0
  if (parse->size != NULL)
728
0
  {
729
0
    pfree(parse->size);
730
0
    parse->size = NULL;
731
0
  }
732
0
  if (parse->algorithm != NULL)
733
0
  {
734
0
    pfree(parse->algorithm);
735
0
    parse->algorithm = NULL;
736
0
  }
737
0
  if (parse->checksum != NULL)
738
0
  {
739
0
    pfree(parse->checksum);
740
0
    parse->checksum = NULL;
741
0
  }
742
0
}
743
744
/*
745
 * Do additional parsing and sanity-checking of the details gathered for one
746
 * WAL range, and invoke the per-WAL-range callback so that the caller gets
747
 * those details. This happens for each WAL range when the corresponding JSON
748
 * object is completely parsed.
749
 */
750
static void
751
json_manifest_finalize_wal_range(JsonManifestParseState *parse)
752
0
{
753
0
  JsonManifestParseContext *context = parse->context;
754
0
  TimeLineID  tli;
755
0
  XLogRecPtr  start_lsn,
756
0
        end_lsn;
757
0
  char     *ep;
758
759
  /* Make sure all fields are present. */
760
0
  if (parse->timeline == NULL)
761
0
    json_manifest_parse_failure(parse->context, "missing timeline");
762
0
  if (parse->start_lsn == NULL)
763
0
    json_manifest_parse_failure(parse->context, "missing start LSN");
764
0
  if (parse->end_lsn == NULL)
765
0
    json_manifest_parse_failure(parse->context, "missing end LSN");
766
767
  /* Parse timeline. */
768
0
  tli = strtoul(parse->timeline, &ep, 10);
769
0
  if (ep == parse->timeline || *ep)
770
0
    json_manifest_parse_failure(parse->context,
771
0
                  "timeline is not an integer");
772
0
  if (!parse_xlogrecptr(&start_lsn, parse->start_lsn))
773
0
    json_manifest_parse_failure(parse->context,
774
0
                  "could not parse start LSN");
775
0
  if (!parse_xlogrecptr(&end_lsn, parse->end_lsn))
776
0
    json_manifest_parse_failure(parse->context,
777
0
                  "could not parse end LSN");
778
779
  /* Invoke the callback with the details we've gathered. */
780
0
  context->per_wal_range_cb(context, tli, start_lsn, end_lsn);
781
782
  /* Free memory we no longer need. */
783
0
  if (parse->timeline != NULL)
784
0
  {
785
0
    pfree(parse->timeline);
786
0
    parse->timeline = NULL;
787
0
  }
788
0
  if (parse->start_lsn != NULL)
789
0
  {
790
0
    pfree(parse->start_lsn);
791
0
    parse->start_lsn = NULL;
792
0
  }
793
0
  if (parse->end_lsn != NULL)
794
0
  {
795
0
    pfree(parse->end_lsn);
796
0
    parse->end_lsn = NULL;
797
0
  }
798
0
}
799
800
/*
801
 * Verify that the manifest checksum is correct.
802
 *
803
 * The last line of the manifest file is excluded from the manifest checksum,
804
 * because the last line is expected to contain the checksum that covers
805
 * the rest of the file.
806
 *
807
 * For an incremental parse, this will just be called on the last chunk of the
808
 * manifest, and the cryptohash context passed in. For a non-incremental
809
 * parse incr_ctx will be NULL.
810
 */
811
static void
812
verify_manifest_checksum(JsonManifestParseState *parse, const char *buffer,
813
             size_t size, pg_cryptohash_ctx *incr_ctx)
814
0
{
815
0
  JsonManifestParseContext *context = parse->context;
816
0
  size_t    i;
817
0
  size_t    number_of_newlines = 0;
818
0
  size_t    ultimate_newline = 0;
819
0
  size_t    penultimate_newline = 0;
820
0
  pg_cryptohash_ctx *manifest_ctx;
821
0
  uint8   manifest_checksum_actual[PG_SHA256_DIGEST_LENGTH];
822
0
  uint8   manifest_checksum_expected[PG_SHA256_DIGEST_LENGTH];
823
824
  /* Find the last two newlines in the file. */
825
0
  for (i = 0; i < size; ++i)
826
0
  {
827
0
    if (buffer[i] == '\n')
828
0
    {
829
0
      ++number_of_newlines;
830
0
      penultimate_newline = ultimate_newline;
831
0
      ultimate_newline = i;
832
0
    }
833
0
  }
834
835
  /*
836
   * Make sure that the last newline is right at the end, and that there are
837
   * at least two lines total. We need this to be true in order for the
838
   * following code, which computes the manifest checksum, to work properly.
839
   */
840
0
  if (number_of_newlines < 2)
841
0
    json_manifest_parse_failure(parse->context,
842
0
                  "expected at least 2 lines");
843
0
  if (ultimate_newline != size - 1)
844
0
    json_manifest_parse_failure(parse->context,
845
0
                  "last line not newline-terminated");
846
847
  /* Checksum the rest. */
848
0
  if (incr_ctx == NULL)
849
0
  {
850
0
    manifest_ctx = pg_cryptohash_create(PG_SHA256);
851
0
    if (manifest_ctx == NULL)
852
0
      context->error_cb(context, "out of memory");
853
0
    if (pg_cryptohash_init(manifest_ctx) < 0)
854
0
      context->error_cb(context, "could not initialize checksum of manifest");
855
0
  }
856
0
  else
857
0
  {
858
0
    manifest_ctx = incr_ctx;
859
0
  }
860
0
  if (pg_cryptohash_update(manifest_ctx, (const uint8 *) buffer, penultimate_newline + 1) < 0)
861
0
    context->error_cb(context, "could not update checksum of manifest");
862
0
  if (pg_cryptohash_final(manifest_ctx, manifest_checksum_actual,
863
0
              sizeof(manifest_checksum_actual)) < 0)
864
0
    context->error_cb(context, "could not finalize checksum of manifest");
865
866
  /* Now verify it. */
867
0
  if (parse->manifest_checksum == NULL)
868
0
    context->error_cb(parse->context, "manifest has no checksum");
869
0
  if (strlen(parse->manifest_checksum) != PG_SHA256_DIGEST_LENGTH * 2 ||
870
0
    !hexdecode_string(manifest_checksum_expected, parse->manifest_checksum,
871
0
              PG_SHA256_DIGEST_LENGTH))
872
0
    context->error_cb(context, "invalid manifest checksum: \"%s\"",
873
0
              parse->manifest_checksum);
874
0
  if (memcmp(manifest_checksum_actual, manifest_checksum_expected,
875
0
         PG_SHA256_DIGEST_LENGTH) != 0)
876
0
    context->error_cb(context, "manifest checksum mismatch");
877
0
  pg_cryptohash_free(manifest_ctx);
878
0
}
879
880
/*
881
 * Report a parse error.
882
 *
883
 * This is intended to be used for fairly low-level failures that probably
884
 * shouldn't occur unless somebody has deliberately constructed a bad manifest,
885
 * or unless the server is generating bad manifests due to some bug. msg should
886
 * be a short string giving some hint as to what the problem is.
887
 */
888
static void
889
json_manifest_parse_failure(JsonManifestParseContext *context, char *msg)
890
0
{
891
0
  context->error_cb(context, "could not parse backup manifest: %s", msg);
892
0
  pg_unreachable();
893
0
}
894
895
/*
896
 * Convert a character which represents a hexadecimal digit to an integer.
897
 *
898
 * Returns -1 if the character is not a hexadecimal digit.
899
 */
900
static int
901
hexdecode_char(char c)
902
0
{
903
0
  if (c >= '0' && c <= '9')
904
0
    return c - '0';
905
0
  if (c >= 'a' && c <= 'f')
906
0
    return c - 'a' + 10;
907
0
  if (c >= 'A' && c <= 'F')
908
0
    return c - 'A' + 10;
909
910
0
  return -1;
911
0
}
912
913
/*
914
 * Decode a hex string into a byte string, 2 hex chars per byte.
915
 *
916
 * Returns false if invalid characters are encountered; otherwise true.
917
 */
918
static bool
919
hexdecode_string(uint8 *result, char *input, int nbytes)
920
0
{
921
0
  int     i;
922
923
0
  for (i = 0; i < nbytes; ++i)
924
0
  {
925
0
    int     n1 = hexdecode_char(input[i * 2]);
926
0
    int     n2 = hexdecode_char(input[i * 2 + 1]);
927
928
0
    if (n1 < 0 || n2 < 0)
929
0
      return false;
930
0
    result[i] = n1 * 16 + n2;
931
0
  }
932
933
0
  return true;
934
0
}
935
936
/*
937
 * Parse an XLogRecPtr expressed using the usual string format.
938
 */
939
static bool
940
parse_xlogrecptr(XLogRecPtr *result, char *input)
941
0
{
942
0
  uint32    hi;
943
0
  uint32    lo;
944
945
0
  if (sscanf(input, "%X/%08X", &hi, &lo) != 2)
946
0
    return false;
947
0
  *result = ((uint64) hi) << 32 | lo;
948
  return true;
949
0
}