LCOV - code coverage report
Current view: top level - src/common - parse_manifest.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 95.6 % 383 366
Test Date: 2026-04-07 14:16:30 Functions: 100.0 % 19 19
Legend: Lines:     hit not hit

            Line data    Source code
       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          122 : json_parse_manifest_incremental_init(JsonManifestParseContext *context)
     130              : {
     131              :     JsonManifestParseIncrementalState *incstate;
     132              :     JsonManifestParseState *parse;
     133              :     pg_cryptohash_ctx *manifest_ctx;
     134              : 
     135          122 :     incstate = palloc_object(JsonManifestParseIncrementalState);
     136          122 :     parse = palloc_object(JsonManifestParseState);
     137              : 
     138          122 :     parse->context = context;
     139          122 :     parse->state = JM_EXPECT_TOPLEVEL_START;
     140          122 :     parse->saw_version_field = false;
     141              : 
     142          122 :     makeJsonLexContextIncremental(&(incstate->lex), PG_UTF8, true);
     143              : 
     144          122 :     incstate->sem.semstate = parse;
     145          122 :     incstate->sem.object_start = json_manifest_object_start;
     146          122 :     incstate->sem.object_end = json_manifest_object_end;
     147          122 :     incstate->sem.array_start = json_manifest_array_start;
     148          122 :     incstate->sem.array_end = json_manifest_array_end;
     149          122 :     incstate->sem.object_field_start = json_manifest_object_field_start;
     150          122 :     incstate->sem.object_field_end = NULL;
     151          122 :     incstate->sem.array_element_start = NULL;
     152          122 :     incstate->sem.array_element_end = NULL;
     153          122 :     incstate->sem.scalar = json_manifest_scalar;
     154              : 
     155          122 :     manifest_ctx = pg_cryptohash_create(PG_SHA256);
     156          122 :     if (manifest_ctx == NULL)
     157            0 :         context->error_cb(context, "out of memory");
     158          122 :     if (pg_cryptohash_init(manifest_ctx) < 0)
     159            0 :         context->error_cb(context, "could not initialize checksum of manifest");
     160          122 :     incstate->manifest_ctx = manifest_ctx;
     161              : 
     162          122 :     return incstate;
     163              : }
     164              : 
     165              : /*
     166              :  * Free an incremental state object and its contents.
     167              :  */
     168              : void
     169          119 : json_parse_manifest_incremental_shutdown(JsonManifestParseIncrementalState *incstate)
     170              : {
     171          119 :     pfree(incstate->sem.semstate);
     172          119 :     freeJsonLexContext(&(incstate->lex));
     173              :     /* incstate->manifest_ctx has already been freed */
     174          119 :     pfree(incstate);
     175          119 : }
     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          245 : json_parse_manifest_incremental_chunk(JsonManifestParseIncrementalState *incstate,
     186              :                                       const char *chunk, size_t size, bool is_last)
     187              : {
     188              :     JsonParseErrorType res,
     189              :                 expected;
     190          245 :     JsonManifestParseState *parse = incstate->sem.semstate;
     191          245 :     JsonManifestParseContext *context = parse->context;
     192              : 
     193          245 :     res = pg_parse_json_incremental(&(incstate->lex), &(incstate->sem),
     194              :                                     chunk, size, is_last);
     195              : 
     196          244 :     expected = is_last ? JSON_SUCCESS : JSON_INCOMPLETE;
     197              : 
     198          244 :     if (res != expected)
     199            0 :         json_manifest_parse_failure(context,
     200              :                                     json_errdetail(res, &(incstate->lex)));
     201              : 
     202          244 :     if (is_last && parse->state != JM_EXPECT_EOF)
     203            0 :         json_manifest_parse_failure(context, "manifest ended unexpectedly");
     204              : 
     205          244 :     if (!is_last)
     206              :     {
     207          123 :         if (pg_cryptohash_update(incstate->manifest_ctx,
     208              :                                  (const uint8 *) chunk, size) < 0)
     209            0 :             context->error_cb(context, "could not update checksum of manifest");
     210              :     }
     211              :     else
     212              :     {
     213          121 :         verify_manifest_checksum(parse, chunk, size, incstate->manifest_ctx);
     214              :     }
     215          242 : }
     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           33 : json_parse_manifest(JsonManifestParseContext *context, const char *buffer,
     228              :                     size_t size)
     229              : {
     230              :     JsonLexContext *lex;
     231              :     JsonParseErrorType json_error;
     232              :     JsonSemAction sem;
     233              :     JsonManifestParseState parse;
     234              : 
     235              :     /* Set up our private parsing context. */
     236           33 :     parse.context = context;
     237           33 :     parse.state = JM_EXPECT_TOPLEVEL_START;
     238           33 :     parse.saw_version_field = false;
     239              : 
     240              :     /* Create a JSON lexing context. */
     241           33 :     lex = makeJsonLexContextCstringLen(NULL, buffer, size, PG_UTF8, true);
     242              : 
     243              :     /* Set up semantic actions. */
     244           33 :     sem.semstate = &parse;
     245           33 :     sem.object_start = json_manifest_object_start;
     246           33 :     sem.object_end = json_manifest_object_end;
     247           33 :     sem.array_start = json_manifest_array_start;
     248           33 :     sem.array_end = json_manifest_array_end;
     249           33 :     sem.object_field_start = json_manifest_object_field_start;
     250           33 :     sem.object_field_end = NULL;
     251           33 :     sem.array_element_start = NULL;
     252           33 :     sem.array_element_end = NULL;
     253           33 :     sem.scalar = json_manifest_scalar;
     254              : 
     255              :     /* Run the actual JSON parser. */
     256           33 :     json_error = pg_parse_json(lex, &sem);
     257            7 :     if (json_error != JSON_SUCCESS)
     258            1 :         json_manifest_parse_failure(context, json_errdetail(json_error, lex));
     259            6 :     if (parse.state != JM_EXPECT_EOF)
     260            0 :         json_manifest_parse_failure(context, "manifest ended unexpectedly");
     261              : 
     262              :     /* Verify the manifest checksum. */
     263            6 :     verify_manifest_checksum(&parse, buffer, size, NULL);
     264              : 
     265            3 :     freeJsonLexContext(lex);
     266            3 : }
     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       130091 : json_manifest_object_start(void *state)
     277              : {
     278       130091 :     JsonManifestParseState *parse = state;
     279              : 
     280       130091 :     switch (parse->state)
     281              :     {
     282          154 :         case JM_EXPECT_TOPLEVEL_START:
     283          154 :             parse->state = JM_EXPECT_TOPLEVEL_FIELD;
     284          154 :             break;
     285       129804 :         case JM_EXPECT_FILES_NEXT:
     286       129804 :             parse->state = JM_EXPECT_THIS_FILE_FIELD;
     287       129804 :             parse->pathname = NULL;
     288       129804 :             parse->encoded_pathname = NULL;
     289       129804 :             parse->size = NULL;
     290       129804 :             parse->algorithm = NULL;
     291       129804 :             parse->checksum = NULL;
     292       129804 :             break;
     293          132 :         case JM_EXPECT_WAL_RANGES_NEXT:
     294          132 :             parse->state = JM_EXPECT_THIS_WAL_RANGE_FIELD;
     295          132 :             parse->timeline = NULL;
     296          132 :             parse->start_lsn = NULL;
     297          132 :             parse->end_lsn = NULL;
     298          132 :             break;
     299            1 :         default:
     300            1 :             json_manifest_parse_failure(parse->context,
     301              :                                         "unexpected object start");
     302              :             break;
     303              :     }
     304              : 
     305       130090 :     return JSON_SUCCESS;
     306              : }
     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       130063 : json_manifest_object_end(void *state)
     318              : {
     319       130063 :     JsonManifestParseState *parse = state;
     320              : 
     321       130063 :     switch (parse->state)
     322              :     {
     323          127 :         case JM_EXPECT_TOPLEVEL_END:
     324          127 :             parse->state = JM_EXPECT_EOF;
     325          127 :             break;
     326       129803 :         case JM_EXPECT_THIS_FILE_FIELD:
     327       129803 :             json_manifest_finalize_file(parse);
     328       129794 :             parse->state = JM_EXPECT_FILES_NEXT;
     329       129794 :             break;
     330          131 :         case JM_EXPECT_THIS_WAL_RANGE_FIELD:
     331          131 :             json_manifest_finalize_wal_range(parse);
     332          125 :             parse->state = JM_EXPECT_WAL_RANGES_NEXT;
     333          125 :             break;
     334            2 :         default:
     335            2 :             json_manifest_parse_failure(parse->context,
     336              :                                         "unexpected object end");
     337              :             break;
     338              :     }
     339              : 
     340       130046 :     return JSON_SUCCESS;
     341              : }
     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          270 : json_manifest_array_start(void *state)
     352              : {
     353          270 :     JsonManifestParseState *parse = state;
     354              : 
     355          270 :     switch (parse->state)
     356              :     {
     357          137 :         case JM_EXPECT_FILES_START:
     358          137 :             parse->state = JM_EXPECT_FILES_NEXT;
     359          137 :             break;
     360          132 :         case JM_EXPECT_WAL_RANGES_START:
     361          132 :             parse->state = JM_EXPECT_WAL_RANGES_NEXT;
     362          132 :             break;
     363            1 :         default:
     364            1 :             json_manifest_parse_failure(parse->context,
     365              :                                         "unexpected array start");
     366              :             break;
     367              :     }
     368              : 
     369          269 :     return JSON_SUCCESS;
     370              : }
     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          252 : json_manifest_array_end(void *state)
     379              : {
     380          252 :     JsonManifestParseState *parse = state;
     381              : 
     382          252 :     switch (parse->state)
     383              :     {
     384          252 :         case JM_EXPECT_FILES_NEXT:
     385              :         case JM_EXPECT_WAL_RANGES_NEXT:
     386          252 :             parse->state = JM_EXPECT_TOPLEVEL_FIELD;
     387          252 :             break;
     388            0 :         default:
     389            0 :             json_manifest_parse_failure(parse->context,
     390              :                                         "unexpected array end");
     391              :             break;
     392              :     }
     393              : 
     394          252 :     return JSON_SUCCESS;
     395              : }
     396              : 
     397              : /*
     398              :  * Invoked at the start of each object field in the JSON document.
     399              :  */
     400              : static JsonParseErrorType
     401       643870 : json_manifest_object_field_start(void *state, char *fname, bool isnull)
     402              : {
     403       643870 :     JsonManifestParseState *parse = state;
     404              : 
     405       643870 :     switch (parse->state)
     406              :     {
     407          676 :         case JM_EXPECT_TOPLEVEL_FIELD:
     408              : 
     409              :             /*
     410              :              * Inside toplevel object. The version indicator should always be
     411              :              * the first field.
     412              :              */
     413          676 :             if (!parse->saw_version_field)
     414              :             {
     415          152 :                 if (strcmp(fname, "PostgreSQL-Backup-Manifest-Version") != 0)
     416            1 :                     json_manifest_parse_failure(parse->context,
     417              :                                                 "expected version indicator");
     418          151 :                 parse->state = JM_EXPECT_VERSION_VALUE;
     419          151 :                 parse->saw_version_field = true;
     420          151 :                 break;
     421              :             }
     422              : 
     423              :             /* Is this the system identifier? */
     424          524 :             if (strcmp(fname, "System-Identifier") == 0)
     425              :             {
     426          125 :                 parse->state = JM_EXPECT_SYSTEM_IDENTIFIER_VALUE;
     427          125 :                 break;
     428              :             }
     429              : 
     430              :             /* Is this the list of files? */
     431          399 :             if (strcmp(fname, "Files") == 0)
     432              :             {
     433          139 :                 parse->state = JM_EXPECT_FILES_START;
     434          139 :                 break;
     435              :             }
     436              : 
     437              :             /* Is this the list of WAL ranges? */
     438          260 :             if (strcmp(fname, "WAL-Ranges") == 0)
     439              :             {
     440          132 :                 parse->state = JM_EXPECT_WAL_RANGES_START;
     441          132 :                 break;
     442              :             }
     443              : 
     444              :             /* Is this the manifest checksum? */
     445          128 :             if (strcmp(fname, "Manifest-Checksum") == 0)
     446              :             {
     447          127 :                 parse->state = JM_EXPECT_MANIFEST_CHECKSUM_VALUE;
     448          127 :                 break;
     449              :             }
     450              : 
     451              :             /* It's not a field we recognize. */
     452            1 :             json_manifest_parse_failure(parse->context,
     453              :                                         "unrecognized top-level field");
     454              :             break;
     455              : 
     456       642806 :         case JM_EXPECT_THIS_FILE_FIELD:
     457              :             /* Inside object for one file; which key have we got? */
     458       642806 :             if (strcmp(fname, "Path") == 0)
     459       128772 :                 parse->file_field = JMFF_PATH;
     460       514034 :             else if (strcmp(fname, "Encoded-Path") == 0)
     461         1031 :                 parse->file_field = JMFF_ENCODED_PATH;
     462       513003 :             else if (strcmp(fname, "Size") == 0)
     463       129800 :                 parse->file_field = JMFF_SIZE;
     464       383203 :             else if (strcmp(fname, "Last-Modified") == 0)
     465       129793 :                 parse->file_field = JMFF_LAST_MODIFIED;
     466       253410 :             else if (strcmp(fname, "Checksum-Algorithm") == 0)
     467       126704 :                 parse->file_field = JMFF_CHECKSUM_ALGORITHM;
     468       126706 :             else if (strcmp(fname, "Checksum") == 0)
     469       126705 :                 parse->file_field = JMFF_CHECKSUM;
     470              :             else
     471            1 :                 json_manifest_parse_failure(parse->context,
     472              :                                             "unexpected file field");
     473       642805 :             parse->state = JM_EXPECT_THIS_FILE_VALUE;
     474       642805 :             break;
     475              : 
     476          388 :         case JM_EXPECT_THIS_WAL_RANGE_FIELD:
     477              :             /* Inside object for one file; which key have we got? */
     478          388 :             if (strcmp(fname, "Timeline") == 0)
     479          130 :                 parse->wal_range_field = JMWRF_TIMELINE;
     480          258 :             else if (strcmp(fname, "Start-LSN") == 0)
     481          129 :                 parse->wal_range_field = JMWRF_START_LSN;
     482          129 :             else if (strcmp(fname, "End-LSN") == 0)
     483          128 :                 parse->wal_range_field = JMWRF_END_LSN;
     484              :             else
     485            1 :                 json_manifest_parse_failure(parse->context,
     486              :                                             "unexpected WAL range field");
     487          387 :             parse->state = JM_EXPECT_THIS_WAL_RANGE_VALUE;
     488          387 :             break;
     489              : 
     490            0 :         default:
     491            0 :             json_manifest_parse_failure(parse->context,
     492              :                                         "unexpected object field");
     493              :             break;
     494              :     }
     495              : 
     496       643866 :     pfree(fname);
     497              : 
     498       643866 :     return JSON_SUCCESS;
     499              : }
     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       643596 : json_manifest_scalar(void *state, char *token, JsonTokenType tokentype)
     518              : {
     519       643596 :     JsonManifestParseState *parse = state;
     520              : 
     521       643596 :     switch (parse->state)
     522              :     {
     523          151 :         case JM_EXPECT_VERSION_VALUE:
     524          151 :             parse->manifest_version = token;
     525          151 :             json_manifest_finalize_version(parse);
     526          149 :             parse->state = JM_EXPECT_TOPLEVEL_FIELD;
     527          149 :             break;
     528              : 
     529          125 :         case JM_EXPECT_SYSTEM_IDENTIFIER_VALUE:
     530          125 :             parse->manifest_system_identifier = token;
     531          125 :             json_manifest_finalize_system_identifier(parse);
     532          124 :             parse->state = JM_EXPECT_TOPLEVEL_FIELD;
     533          124 :             break;
     534              : 
     535       642805 :         case JM_EXPECT_THIS_FILE_VALUE:
     536       642805 :             switch (parse->file_field)
     537              :             {
     538       128772 :                 case JMFF_PATH:
     539       128772 :                     parse->pathname = token;
     540       128772 :                     break;
     541         1031 :                 case JMFF_ENCODED_PATH:
     542         1031 :                     parse->encoded_pathname = token;
     543         1031 :                     break;
     544       129800 :                 case JMFF_SIZE:
     545       129800 :                     parse->size = token;
     546       129800 :                     break;
     547       129793 :                 case JMFF_LAST_MODIFIED:
     548       129793 :                     pfree(token);   /* unused */
     549       129793 :                     break;
     550       126704 :                 case JMFF_CHECKSUM_ALGORITHM:
     551       126704 :                     parse->algorithm = token;
     552       126704 :                     break;
     553       126705 :                 case JMFF_CHECKSUM:
     554       126705 :                     parse->checksum = token;
     555       126705 :                     break;
     556              :             }
     557       642805 :             parse->state = JM_EXPECT_THIS_FILE_FIELD;
     558       642805 :             break;
     559              : 
     560          387 :         case JM_EXPECT_THIS_WAL_RANGE_VALUE:
     561          387 :             switch (parse->wal_range_field)
     562              :             {
     563          130 :                 case JMWRF_TIMELINE:
     564          130 :                     parse->timeline = token;
     565          130 :                     break;
     566          129 :                 case JMWRF_START_LSN:
     567          129 :                     parse->start_lsn = token;
     568          129 :                     break;
     569          128 :                 case JMWRF_END_LSN:
     570          128 :                     parse->end_lsn = token;
     571          128 :                     break;
     572              :             }
     573          387 :             parse->state = JM_EXPECT_THIS_WAL_RANGE_FIELD;
     574          387 :             break;
     575              : 
     576          127 :         case JM_EXPECT_MANIFEST_CHECKSUM_VALUE:
     577          127 :             parse->state = JM_EXPECT_TOPLEVEL_END;
     578          127 :             parse->manifest_checksum = token;
     579          127 :             break;
     580              : 
     581            1 :         default:
     582            1 :             json_manifest_parse_failure(parse->context, "unexpected scalar");
     583              :             break;
     584              :     }
     585              : 
     586       643592 :     return JSON_SUCCESS;
     587              : }
     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          151 : json_manifest_finalize_version(JsonManifestParseState *parse)
     597              : {
     598          151 :     JsonManifestParseContext *context = parse->context;
     599              :     int         version;
     600              :     char       *ep;
     601              : 
     602              :     Assert(parse->saw_version_field);
     603              : 
     604              :     /* Parse version. */
     605          151 :     version = strtoi64(parse->manifest_version, &ep, 10);
     606          151 :     if (*ep)
     607            1 :         json_manifest_parse_failure(parse->context,
     608              :                                     "manifest version not an integer");
     609              : 
     610          150 :     if (version != 1 && version != 2)
     611            1 :         json_manifest_parse_failure(parse->context,
     612              :                                     "unexpected manifest version");
     613              : 
     614              :     /* Invoke the callback for version */
     615          149 :     context->version_cb(context, version);
     616          149 : }
     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          125 : json_manifest_finalize_system_identifier(JsonManifestParseState *parse)
     625              : {
     626          125 :     JsonManifestParseContext *context = parse->context;
     627              :     uint64      system_identifier;
     628              :     char       *ep;
     629              : 
     630              :     Assert(parse->manifest_system_identifier != NULL);
     631              : 
     632              :     /* Parse system identifier. */
     633          125 :     system_identifier = strtou64(parse->manifest_system_identifier, &ep, 10);
     634          125 :     if (*ep)
     635            0 :         json_manifest_parse_failure(parse->context,
     636              :                                     "system identifier in manifest not an integer");
     637              : 
     638              :     /* Invoke the callback for system identifier */
     639          125 :     context->system_identifier_cb(context, system_identifier);
     640          124 : }
     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       129803 : json_manifest_finalize_file(JsonManifestParseState *parse)
     650              : {
     651       129803 :     JsonManifestParseContext *context = parse->context;
     652              :     uint64      size;
     653              :     char       *ep;
     654              :     int         checksum_string_length;
     655              :     pg_checksum_type checksum_type;
     656              :     int         checksum_length;
     657              :     uint8      *checksum_payload;
     658              : 
     659              :     /* Pathname and size are required. */
     660       129803 :     if (parse->pathname == NULL && parse->encoded_pathname == NULL)
     661            1 :         json_manifest_parse_failure(parse->context, "missing path name");
     662       129802 :     if (parse->pathname != NULL && parse->encoded_pathname != NULL)
     663            1 :         json_manifest_parse_failure(parse->context,
     664              :                                     "both path name and encoded path name");
     665       129801 :     if (parse->size == NULL)
     666            1 :         json_manifest_parse_failure(parse->context, "missing size");
     667       129800 :     if (parse->algorithm == NULL && parse->checksum != NULL)
     668            1 :         json_manifest_parse_failure(parse->context,
     669              :                                     "checksum without algorithm");
     670              : 
     671              :     /* Decode encoded pathname, if that's what we have. */
     672       129799 :     if (parse->encoded_pathname != NULL)
     673              :     {
     674         1030 :         int         encoded_length = strlen(parse->encoded_pathname);
     675         1030 :         int         raw_length = encoded_length / 2;
     676              : 
     677         1030 :         parse->pathname = palloc(raw_length + 1);
     678         1030 :         if (encoded_length % 2 != 0 ||
     679         1029 :             !hexdecode_string((uint8 *) parse->pathname,
     680              :                               parse->encoded_pathname,
     681              :                               raw_length))
     682            1 :             json_manifest_parse_failure(parse->context,
     683              :                                         "could not decode file name");
     684         1029 :         parse->pathname[raw_length] = '\0';
     685         1029 :         pfree(parse->encoded_pathname);
     686         1029 :         parse->encoded_pathname = NULL;
     687              :     }
     688              : 
     689              :     /* Parse size. */
     690       129798 :     size = strtou64(parse->size, &ep, 10);
     691       129798 :     if (*ep)
     692            1 :         json_manifest_parse_failure(parse->context,
     693              :                                     "file size is not an integer");
     694              : 
     695              :     /* Parse the checksum algorithm, if it's present. */
     696       129797 :     if (parse->algorithm == NULL)
     697         3093 :         checksum_type = CHECKSUM_TYPE_NONE;
     698       126704 :     else if (!pg_checksum_parse_type(parse->algorithm, &checksum_type))
     699            1 :         context->error_cb(context, "unrecognized checksum algorithm: \"%s\"",
     700              :                           parse->algorithm);
     701              : 
     702              :     /* Parse the checksum payload, if it's present. */
     703       129796 :     checksum_string_length = parse->checksum == NULL ? 0
     704       126703 :         : strlen(parse->checksum);
     705       129796 :     if (checksum_string_length == 0)
     706              :     {
     707         3093 :         checksum_length = 0;
     708         3093 :         checksum_payload = NULL;
     709              :     }
     710              :     else
     711              :     {
     712       126703 :         checksum_length = checksum_string_length / 2;
     713       126703 :         checksum_payload = palloc(checksum_length);
     714       126703 :         if (checksum_string_length % 2 != 0 ||
     715       126702 :             !hexdecode_string(checksum_payload, parse->checksum,
     716              :                               checksum_length))
     717            1 :             context->error_cb(context,
     718              :                               "invalid checksum for file \"%s\": \"%s\"",
     719              :                               parse->pathname, parse->checksum);
     720              :     }
     721              : 
     722              :     /* Invoke the callback with the details we've gathered. */
     723       129795 :     context->per_file_cb(context, parse->pathname, size,
     724              :                          checksum_type, checksum_length, checksum_payload);
     725              : 
     726              :     /* Free memory we no longer need. */
     727       129794 :     if (parse->size != NULL)
     728              :     {
     729       129794 :         pfree(parse->size);
     730       129794 :         parse->size = NULL;
     731              :     }
     732       129794 :     if (parse->algorithm != NULL)
     733              :     {
     734       126702 :         pfree(parse->algorithm);
     735       126702 :         parse->algorithm = NULL;
     736              :     }
     737       129794 :     if (parse->checksum != NULL)
     738              :     {
     739       126702 :         pfree(parse->checksum);
     740       126702 :         parse->checksum = NULL;
     741              :     }
     742       129794 : }
     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          131 : json_manifest_finalize_wal_range(JsonManifestParseState *parse)
     752              : {
     753          131 :     JsonManifestParseContext *context = parse->context;
     754              :     TimeLineID  tli;
     755              :     XLogRecPtr  start_lsn,
     756              :                 end_lsn;
     757              :     char       *ep;
     758              : 
     759              :     /* Make sure all fields are present. */
     760          131 :     if (parse->timeline == NULL)
     761            1 :         json_manifest_parse_failure(parse->context, "missing timeline");
     762          130 :     if (parse->start_lsn == NULL)
     763            1 :         json_manifest_parse_failure(parse->context, "missing start LSN");
     764          129 :     if (parse->end_lsn == NULL)
     765            1 :         json_manifest_parse_failure(parse->context, "missing end LSN");
     766              : 
     767              :     /* Parse timeline. */
     768          128 :     tli = strtoul(parse->timeline, &ep, 10);
     769          128 :     if (*ep)
     770            1 :         json_manifest_parse_failure(parse->context,
     771              :                                     "timeline is not an integer");
     772          127 :     if (!parse_xlogrecptr(&start_lsn, parse->start_lsn))
     773            1 :         json_manifest_parse_failure(parse->context,
     774              :                                     "could not parse start LSN");
     775          126 :     if (!parse_xlogrecptr(&end_lsn, parse->end_lsn))
     776            1 :         json_manifest_parse_failure(parse->context,
     777              :                                     "could not parse end LSN");
     778              : 
     779              :     /* Invoke the callback with the details we've gathered. */
     780          125 :     context->per_wal_range_cb(context, tli, start_lsn, end_lsn);
     781              : 
     782              :     /* Free memory we no longer need. */
     783          125 :     if (parse->timeline != NULL)
     784              :     {
     785          125 :         pfree(parse->timeline);
     786          125 :         parse->timeline = NULL;
     787              :     }
     788          125 :     if (parse->start_lsn != NULL)
     789              :     {
     790          125 :         pfree(parse->start_lsn);
     791          125 :         parse->start_lsn = NULL;
     792              :     }
     793          125 :     if (parse->end_lsn != NULL)
     794              :     {
     795          125 :         pfree(parse->end_lsn);
     796          125 :         parse->end_lsn = NULL;
     797              :     }
     798          125 : }
     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          127 : verify_manifest_checksum(JsonManifestParseState *parse, const char *buffer,
     813              :                          size_t size, pg_cryptohash_ctx *incr_ctx)
     814              : {
     815          127 :     JsonManifestParseContext *context = parse->context;
     816              :     size_t      i;
     817          127 :     size_t      number_of_newlines = 0;
     818          127 :     size_t      ultimate_newline = 0;
     819          127 :     size_t      penultimate_newline = 0;
     820              :     pg_cryptohash_ctx *manifest_ctx;
     821              :     uint8       manifest_checksum_actual[PG_SHA256_DIGEST_LENGTH];
     822              :     uint8       manifest_checksum_expected[PG_SHA256_DIGEST_LENGTH];
     823              : 
     824              :     /* Find the last two newlines in the file. */
     825      8935185 :     for (i = 0; i < size; ++i)
     826              :     {
     827      8935058 :         if (buffer[i] == '\n')
     828              :         {
     829        62232 :             ++number_of_newlines;
     830        62232 :             penultimate_newline = ultimate_newline;
     831        62232 :             ultimate_newline = i;
     832              :         }
     833              :     }
     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          127 :     if (number_of_newlines < 2)
     841            1 :         json_manifest_parse_failure(parse->context,
     842              :                                     "expected at least 2 lines");
     843          126 :     if (ultimate_newline != size - 1)
     844            1 :         json_manifest_parse_failure(parse->context,
     845              :                                     "last line not newline-terminated");
     846              : 
     847              :     /* Checksum the rest. */
     848          125 :     if (incr_ctx == NULL)
     849              :     {
     850            4 :         manifest_ctx = pg_cryptohash_create(PG_SHA256);
     851            4 :         if (manifest_ctx == NULL)
     852            0 :             context->error_cb(context, "out of memory");
     853            4 :         if (pg_cryptohash_init(manifest_ctx) < 0)
     854            0 :             context->error_cb(context, "could not initialize checksum of manifest");
     855              :     }
     856              :     else
     857              :     {
     858          121 :         manifest_ctx = incr_ctx;
     859              :     }
     860          125 :     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          125 :     if (pg_cryptohash_final(manifest_ctx, manifest_checksum_actual,
     863              :                             sizeof(manifest_checksum_actual)) < 0)
     864            0 :         context->error_cb(context, "could not finalize checksum of manifest");
     865              : 
     866              :     /* Now verify it. */
     867          125 :     if (parse->manifest_checksum == NULL)
     868            0 :         context->error_cb(parse->context, "manifest has no checksum");
     869          125 :     if (strlen(parse->manifest_checksum) != PG_SHA256_DIGEST_LENGTH * 2 ||
     870          125 :         !hexdecode_string(manifest_checksum_expected, parse->manifest_checksum,
     871              :                           PG_SHA256_DIGEST_LENGTH))
     872            1 :         context->error_cb(context, "invalid manifest checksum: \"%s\"",
     873              :                           parse->manifest_checksum);
     874          124 :     if (memcmp(manifest_checksum_actual, manifest_checksum_expected,
     875              :                PG_SHA256_DIGEST_LENGTH) != 0)
     876            2 :         context->error_cb(context, "manifest checksum mismatch");
     877          122 :     pg_cryptohash_free(manifest_ctx);
     878          122 : }
     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           26 : json_manifest_parse_failure(JsonManifestParseContext *context, char *msg)
     890              : {
     891           26 :     context->error_cb(context, "could not parse backup manifest: %s", msg);
     892            0 :     pg_unreachable();
     893              : }
     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      1737980 : hexdecode_char(char c)
     902              : {
     903      1737980 :     if (c >= '0' && c <= '9')
     904      1144152 :         return c - '0';
     905       593828 :     if (c >= 'a' && c <= 'f')
     906       593820 :         return c - 'a' + 10;
     907            8 :     if (c >= 'A' && c <= 'F')
     908            6 :         return c - 'A' + 10;
     909              : 
     910            2 :     return -1;
     911              : }
     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       127856 : hexdecode_string(uint8 *result, char *input, int nbytes)
     920              : {
     921              :     int         i;
     922              : 
     923       996845 :     for (i = 0; i < nbytes; ++i)
     924              :     {
     925       868990 :         int         n1 = hexdecode_char(input[i * 2]);
     926       868990 :         int         n2 = hexdecode_char(input[i * 2 + 1]);
     927              : 
     928       868990 :         if (n1 < 0 || n2 < 0)
     929            1 :             return false;
     930       868989 :         result[i] = n1 * 16 + n2;
     931              :     }
     932              : 
     933       127855 :     return true;
     934              : }
     935              : 
     936              : /*
     937              :  * Parse an XLogRecPtr expressed using the usual string format.
     938              :  */
     939              : static bool
     940          253 : parse_xlogrecptr(XLogRecPtr *result, char *input)
     941              : {
     942              :     uint32      hi;
     943              :     uint32      lo;
     944              : 
     945          253 :     if (sscanf(input, "%X/%08X", &hi, &lo) != 2)
     946            2 :         return false;
     947          251 :     *result = ((uint64) hi) << 32 | lo;
     948          251 :     return true;
     949              : }
        

Generated by: LCOV version 2.0-1