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-03-12 06:14:44 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          121 : json_parse_manifest_incremental_init(JsonManifestParseContext *context)
     130              : {
     131              :     JsonManifestParseIncrementalState *incstate;
     132              :     JsonManifestParseState *parse;
     133              :     pg_cryptohash_ctx *manifest_ctx;
     134              : 
     135          121 :     incstate = palloc_object(JsonManifestParseIncrementalState);
     136          121 :     parse = palloc_object(JsonManifestParseState);
     137              : 
     138          121 :     parse->context = context;
     139          121 :     parse->state = JM_EXPECT_TOPLEVEL_START;
     140          121 :     parse->saw_version_field = false;
     141              : 
     142          121 :     makeJsonLexContextIncremental(&(incstate->lex), PG_UTF8, true);
     143              : 
     144          121 :     incstate->sem.semstate = parse;
     145          121 :     incstate->sem.object_start = json_manifest_object_start;
     146          121 :     incstate->sem.object_end = json_manifest_object_end;
     147          121 :     incstate->sem.array_start = json_manifest_array_start;
     148          121 :     incstate->sem.array_end = json_manifest_array_end;
     149          121 :     incstate->sem.object_field_start = json_manifest_object_field_start;
     150          121 :     incstate->sem.object_field_end = NULL;
     151          121 :     incstate->sem.array_element_start = NULL;
     152          121 :     incstate->sem.array_element_end = NULL;
     153          121 :     incstate->sem.scalar = json_manifest_scalar;
     154              : 
     155          121 :     manifest_ctx = pg_cryptohash_create(PG_SHA256);
     156          121 :     if (manifest_ctx == NULL)
     157            0 :         context->error_cb(context, "out of memory");
     158          121 :     if (pg_cryptohash_init(manifest_ctx) < 0)
     159            0 :         context->error_cb(context, "could not initialize checksum of manifest");
     160          121 :     incstate->manifest_ctx = manifest_ctx;
     161              : 
     162          121 :     return incstate;
     163              : }
     164              : 
     165              : /*
     166              :  * Free an incremental state object and its contents.
     167              :  */
     168              : void
     169          118 : json_parse_manifest_incremental_shutdown(JsonManifestParseIncrementalState *incstate)
     170              : {
     171          118 :     pfree(incstate->sem.semstate);
     172          118 :     freeJsonLexContext(&(incstate->lex));
     173              :     /* incstate->manifest_ctx has already been freed */
     174          118 :     pfree(incstate);
     175          118 : }
     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          241 : json_parse_manifest_incremental_chunk(JsonManifestParseIncrementalState *incstate,
     186              :                                       const char *chunk, size_t size, bool is_last)
     187              : {
     188              :     JsonParseErrorType res,
     189              :                 expected;
     190          241 :     JsonManifestParseState *parse = incstate->sem.semstate;
     191          241 :     JsonManifestParseContext *context = parse->context;
     192              : 
     193          241 :     res = pg_parse_json_incremental(&(incstate->lex), &(incstate->sem),
     194              :                                     chunk, size, is_last);
     195              : 
     196          240 :     expected = is_last ? JSON_SUCCESS : JSON_INCOMPLETE;
     197              : 
     198          240 :     if (res != expected)
     199            0 :         json_manifest_parse_failure(context,
     200              :                                     json_errdetail(res, &(incstate->lex)));
     201              : 
     202          240 :     if (is_last && parse->state != JM_EXPECT_EOF)
     203            0 :         json_manifest_parse_failure(context, "manifest ended unexpectedly");
     204              : 
     205          240 :     if (!is_last)
     206              :     {
     207          120 :         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          120 :         verify_manifest_checksum(parse, chunk, size, incstate->manifest_ctx);
     214              :     }
     215          238 : }
     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       121436 : json_manifest_object_start(void *state)
     277              : {
     278       121436 :     JsonManifestParseState *parse = state;
     279              : 
     280       121436 :     switch (parse->state)
     281              :     {
     282          153 :         case JM_EXPECT_TOPLEVEL_START:
     283          153 :             parse->state = JM_EXPECT_TOPLEVEL_FIELD;
     284          153 :             break;
     285       121151 :         case JM_EXPECT_FILES_NEXT:
     286       121151 :             parse->state = JM_EXPECT_THIS_FILE_FIELD;
     287       121151 :             parse->pathname = NULL;
     288       121151 :             parse->encoded_pathname = NULL;
     289       121151 :             parse->size = NULL;
     290       121151 :             parse->algorithm = NULL;
     291       121151 :             parse->checksum = NULL;
     292       121151 :             break;
     293          131 :         case JM_EXPECT_WAL_RANGES_NEXT:
     294          131 :             parse->state = JM_EXPECT_THIS_WAL_RANGE_FIELD;
     295          131 :             parse->timeline = NULL;
     296          131 :             parse->start_lsn = NULL;
     297          131 :             parse->end_lsn = NULL;
     298          131 :             break;
     299            1 :         default:
     300            1 :             json_manifest_parse_failure(parse->context,
     301              :                                         "unexpected object start");
     302              :             break;
     303              :     }
     304              : 
     305       121435 :     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       121408 : json_manifest_object_end(void *state)
     318              : {
     319       121408 :     JsonManifestParseState *parse = state;
     320              : 
     321       121408 :     switch (parse->state)
     322              :     {
     323          126 :         case JM_EXPECT_TOPLEVEL_END:
     324          126 :             parse->state = JM_EXPECT_EOF;
     325          126 :             break;
     326       121150 :         case JM_EXPECT_THIS_FILE_FIELD:
     327       121150 :             json_manifest_finalize_file(parse);
     328       121141 :             parse->state = JM_EXPECT_FILES_NEXT;
     329       121141 :             break;
     330          130 :         case JM_EXPECT_THIS_WAL_RANGE_FIELD:
     331          130 :             json_manifest_finalize_wal_range(parse);
     332          124 :             parse->state = JM_EXPECT_WAL_RANGES_NEXT;
     333          124 :             break;
     334            2 :         default:
     335            2 :             json_manifest_parse_failure(parse->context,
     336              :                                         "unexpected object end");
     337              :             break;
     338              :     }
     339              : 
     340       121391 :     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          268 : json_manifest_array_start(void *state)
     352              : {
     353          268 :     JsonManifestParseState *parse = state;
     354              : 
     355          268 :     switch (parse->state)
     356              :     {
     357          136 :         case JM_EXPECT_FILES_START:
     358          136 :             parse->state = JM_EXPECT_FILES_NEXT;
     359          136 :             break;
     360          131 :         case JM_EXPECT_WAL_RANGES_START:
     361          131 :             parse->state = JM_EXPECT_WAL_RANGES_NEXT;
     362          131 :             break;
     363            1 :         default:
     364            1 :             json_manifest_parse_failure(parse->context,
     365              :                                         "unexpected array start");
     366              :             break;
     367              :     }
     368              : 
     369          267 :     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          250 : json_manifest_array_end(void *state)
     379              : {
     380          250 :     JsonManifestParseState *parse = state;
     381              : 
     382          250 :     switch (parse->state)
     383              :     {
     384          250 :         case JM_EXPECT_FILES_NEXT:
     385              :         case JM_EXPECT_WAL_RANGES_NEXT:
     386          250 :             parse->state = JM_EXPECT_TOPLEVEL_FIELD;
     387          250 :             break;
     388            0 :         default:
     389            0 :             json_manifest_parse_failure(parse->context,
     390              :                                         "unexpected array end");
     391              :             break;
     392              :     }
     393              : 
     394          250 :     return JSON_SUCCESS;
     395              : }
     396              : 
     397              : /*
     398              :  * Invoked at the start of each object field in the JSON document.
     399              :  */
     400              : static JsonParseErrorType
     401       600963 : json_manifest_object_field_start(void *state, char *fname, bool isnull)
     402              : {
     403       600963 :     JsonManifestParseState *parse = state;
     404              : 
     405       600963 :     switch (parse->state)
     406              :     {
     407          671 :         case JM_EXPECT_TOPLEVEL_FIELD:
     408              : 
     409              :             /*
     410              :              * Inside toplevel object. The version indicator should always be
     411              :              * the first field.
     412              :              */
     413          671 :             if (!parse->saw_version_field)
     414              :             {
     415          151 :                 if (strcmp(fname, "PostgreSQL-Backup-Manifest-Version") != 0)
     416            1 :                     json_manifest_parse_failure(parse->context,
     417              :                                                 "expected version indicator");
     418          150 :                 parse->state = JM_EXPECT_VERSION_VALUE;
     419          150 :                 parse->saw_version_field = true;
     420          150 :                 break;
     421              :             }
     422              : 
     423              :             /* Is this the system identifier? */
     424          520 :             if (strcmp(fname, "System-Identifier") == 0)
     425              :             {
     426          124 :                 parse->state = JM_EXPECT_SYSTEM_IDENTIFIER_VALUE;
     427          124 :                 break;
     428              :             }
     429              : 
     430              :             /* Is this the list of files? */
     431          396 :             if (strcmp(fname, "Files") == 0)
     432              :             {
     433          138 :                 parse->state = JM_EXPECT_FILES_START;
     434          138 :                 break;
     435              :             }
     436              : 
     437              :             /* Is this the list of WAL ranges? */
     438          258 :             if (strcmp(fname, "WAL-Ranges") == 0)
     439              :             {
     440          131 :                 parse->state = JM_EXPECT_WAL_RANGES_START;
     441          131 :                 break;
     442              :             }
     443              : 
     444              :             /* Is this the manifest checksum? */
     445          127 :             if (strcmp(fname, "Manifest-Checksum") == 0)
     446              :             {
     447          126 :                 parse->state = JM_EXPECT_MANIFEST_CHECKSUM_VALUE;
     448          126 :                 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       599907 :         case JM_EXPECT_THIS_FILE_FIELD:
     457              :             /* Inside object for one file; which key have we got? */
     458       599907 :             if (strcmp(fname, "Path") == 0)
     459       120180 :                 parse->file_field = JMFF_PATH;
     460       479727 :             else if (strcmp(fname, "Encoded-Path") == 0)
     461          970 :                 parse->file_field = JMFF_ENCODED_PATH;
     462       478757 :             else if (strcmp(fname, "Size") == 0)
     463       121147 :                 parse->file_field = JMFF_SIZE;
     464       357610 :             else if (strcmp(fname, "Last-Modified") == 0)
     465       121140 :                 parse->file_field = JMFF_LAST_MODIFIED;
     466       236470 :             else if (strcmp(fname, "Checksum-Algorithm") == 0)
     467       118234 :                 parse->file_field = JMFF_CHECKSUM_ALGORITHM;
     468       118236 :             else if (strcmp(fname, "Checksum") == 0)
     469       118235 :                 parse->file_field = JMFF_CHECKSUM;
     470              :             else
     471            1 :                 json_manifest_parse_failure(parse->context,
     472              :                                             "unexpected file field");
     473       599906 :             parse->state = JM_EXPECT_THIS_FILE_VALUE;
     474       599906 :             break;
     475              : 
     476          385 :         case JM_EXPECT_THIS_WAL_RANGE_FIELD:
     477              :             /* Inside object for one file; which key have we got? */
     478          385 :             if (strcmp(fname, "Timeline") == 0)
     479          129 :                 parse->wal_range_field = JMWRF_TIMELINE;
     480          256 :             else if (strcmp(fname, "Start-LSN") == 0)
     481          128 :                 parse->wal_range_field = JMWRF_START_LSN;
     482          128 :             else if (strcmp(fname, "End-LSN") == 0)
     483          127 :                 parse->wal_range_field = JMWRF_END_LSN;
     484              :             else
     485            1 :                 json_manifest_parse_failure(parse->context,
     486              :                                             "unexpected WAL range field");
     487          384 :             parse->state = JM_EXPECT_THIS_WAL_RANGE_VALUE;
     488          384 :             break;
     489              : 
     490            0 :         default:
     491            0 :             json_manifest_parse_failure(parse->context,
     492              :                                         "unexpected object field");
     493              :             break;
     494              :     }
     495              : 
     496       600959 :     pfree(fname);
     497              : 
     498       600959 :     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       600691 : json_manifest_scalar(void *state, char *token, JsonTokenType tokentype)
     518              : {
     519       600691 :     JsonManifestParseState *parse = state;
     520              : 
     521       600691 :     switch (parse->state)
     522              :     {
     523          150 :         case JM_EXPECT_VERSION_VALUE:
     524          150 :             parse->manifest_version = token;
     525          150 :             json_manifest_finalize_version(parse);
     526          148 :             parse->state = JM_EXPECT_TOPLEVEL_FIELD;
     527          148 :             break;
     528              : 
     529          124 :         case JM_EXPECT_SYSTEM_IDENTIFIER_VALUE:
     530          124 :             parse->manifest_system_identifier = token;
     531          124 :             json_manifest_finalize_system_identifier(parse);
     532          123 :             parse->state = JM_EXPECT_TOPLEVEL_FIELD;
     533          123 :             break;
     534              : 
     535       599906 :         case JM_EXPECT_THIS_FILE_VALUE:
     536       599906 :             switch (parse->file_field)
     537              :             {
     538       120180 :                 case JMFF_PATH:
     539       120180 :                     parse->pathname = token;
     540       120180 :                     break;
     541          970 :                 case JMFF_ENCODED_PATH:
     542          970 :                     parse->encoded_pathname = token;
     543          970 :                     break;
     544       121147 :                 case JMFF_SIZE:
     545       121147 :                     parse->size = token;
     546       121147 :                     break;
     547       121140 :                 case JMFF_LAST_MODIFIED:
     548       121140 :                     pfree(token);   /* unused */
     549       121140 :                     break;
     550       118234 :                 case JMFF_CHECKSUM_ALGORITHM:
     551       118234 :                     parse->algorithm = token;
     552       118234 :                     break;
     553       118235 :                 case JMFF_CHECKSUM:
     554       118235 :                     parse->checksum = token;
     555       118235 :                     break;
     556              :             }
     557       599906 :             parse->state = JM_EXPECT_THIS_FILE_FIELD;
     558       599906 :             break;
     559              : 
     560          384 :         case JM_EXPECT_THIS_WAL_RANGE_VALUE:
     561          384 :             switch (parse->wal_range_field)
     562              :             {
     563          129 :                 case JMWRF_TIMELINE:
     564          129 :                     parse->timeline = token;
     565          129 :                     break;
     566          128 :                 case JMWRF_START_LSN:
     567          128 :                     parse->start_lsn = token;
     568          128 :                     break;
     569          127 :                 case JMWRF_END_LSN:
     570          127 :                     parse->end_lsn = token;
     571          127 :                     break;
     572              :             }
     573          384 :             parse->state = JM_EXPECT_THIS_WAL_RANGE_FIELD;
     574          384 :             break;
     575              : 
     576          126 :         case JM_EXPECT_MANIFEST_CHECKSUM_VALUE:
     577          126 :             parse->state = JM_EXPECT_TOPLEVEL_END;
     578          126 :             parse->manifest_checksum = token;
     579          126 :             break;
     580              : 
     581            1 :         default:
     582            1 :             json_manifest_parse_failure(parse->context, "unexpected scalar");
     583              :             break;
     584              :     }
     585              : 
     586       600687 :     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          150 : json_manifest_finalize_version(JsonManifestParseState *parse)
     597              : {
     598          150 :     JsonManifestParseContext *context = parse->context;
     599              :     int         version;
     600              :     char       *ep;
     601              : 
     602              :     Assert(parse->saw_version_field);
     603              : 
     604              :     /* Parse version. */
     605          150 :     version = strtoi64(parse->manifest_version, &ep, 10);
     606          150 :     if (*ep)
     607            1 :         json_manifest_parse_failure(parse->context,
     608              :                                     "manifest version not an integer");
     609              : 
     610          149 :     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          148 :     context->version_cb(context, version);
     616          148 : }
     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          124 : json_manifest_finalize_system_identifier(JsonManifestParseState *parse)
     625              : {
     626          124 :     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          124 :     system_identifier = strtou64(parse->manifest_system_identifier, &ep, 10);
     634          124 :     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          124 :     context->system_identifier_cb(context, system_identifier);
     640          123 : }
     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       121150 : json_manifest_finalize_file(JsonManifestParseState *parse)
     650              : {
     651       121150 :     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       121150 :     if (parse->pathname == NULL && parse->encoded_pathname == NULL)
     661            1 :         json_manifest_parse_failure(parse->context, "missing path name");
     662       121149 :     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       121148 :     if (parse->size == NULL)
     666            1 :         json_manifest_parse_failure(parse->context, "missing size");
     667       121147 :     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       121146 :     if (parse->encoded_pathname != NULL)
     673              :     {
     674          969 :         int         encoded_length = strlen(parse->encoded_pathname);
     675          969 :         int         raw_length = encoded_length / 2;
     676              : 
     677          969 :         parse->pathname = palloc(raw_length + 1);
     678          969 :         if (encoded_length % 2 != 0 ||
     679          968 :             !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          968 :         parse->pathname[raw_length] = '\0';
     685          968 :         pfree(parse->encoded_pathname);
     686          968 :         parse->encoded_pathname = NULL;
     687              :     }
     688              : 
     689              :     /* Parse size. */
     690       121145 :     size = strtou64(parse->size, &ep, 10);
     691       121145 :     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       121144 :     if (parse->algorithm == NULL)
     697         2910 :         checksum_type = CHECKSUM_TYPE_NONE;
     698       118234 :     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       121143 :     checksum_string_length = parse->checksum == NULL ? 0
     704       118233 :         : strlen(parse->checksum);
     705       121143 :     if (checksum_string_length == 0)
     706              :     {
     707         2910 :         checksum_length = 0;
     708         2910 :         checksum_payload = NULL;
     709              :     }
     710              :     else
     711              :     {
     712       118233 :         checksum_length = checksum_string_length / 2;
     713       118233 :         checksum_payload = palloc(checksum_length);
     714       118233 :         if (checksum_string_length % 2 != 0 ||
     715       118232 :             !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       121142 :     context->per_file_cb(context, parse->pathname, size,
     724              :                          checksum_type, checksum_length, checksum_payload);
     725              : 
     726              :     /* Free memory we no longer need. */
     727       121141 :     if (parse->size != NULL)
     728              :     {
     729       121141 :         pfree(parse->size);
     730       121141 :         parse->size = NULL;
     731              :     }
     732       121141 :     if (parse->algorithm != NULL)
     733              :     {
     734       118232 :         pfree(parse->algorithm);
     735       118232 :         parse->algorithm = NULL;
     736              :     }
     737       121141 :     if (parse->checksum != NULL)
     738              :     {
     739       118232 :         pfree(parse->checksum);
     740       118232 :         parse->checksum = NULL;
     741              :     }
     742       121141 : }
     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          130 : json_manifest_finalize_wal_range(JsonManifestParseState *parse)
     752              : {
     753          130 :     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          130 :     if (parse->timeline == NULL)
     761            1 :         json_manifest_parse_failure(parse->context, "missing timeline");
     762          129 :     if (parse->start_lsn == NULL)
     763            1 :         json_manifest_parse_failure(parse->context, "missing start LSN");
     764          128 :     if (parse->end_lsn == NULL)
     765            1 :         json_manifest_parse_failure(parse->context, "missing end LSN");
     766              : 
     767              :     /* Parse timeline. */
     768          127 :     tli = strtoul(parse->timeline, &ep, 10);
     769          127 :     if (*ep)
     770            1 :         json_manifest_parse_failure(parse->context,
     771              :                                     "timeline is not an integer");
     772          126 :     if (!parse_xlogrecptr(&start_lsn, parse->start_lsn))
     773            1 :         json_manifest_parse_failure(parse->context,
     774              :                                     "could not parse start LSN");
     775          125 :     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          124 :     context->per_wal_range_cb(context, tli, start_lsn, end_lsn);
     781              : 
     782              :     /* Free memory we no longer need. */
     783          124 :     if (parse->timeline != NULL)
     784              :     {
     785          124 :         pfree(parse->timeline);
     786          124 :         parse->timeline = NULL;
     787              :     }
     788          124 :     if (parse->start_lsn != NULL)
     789              :     {
     790          124 :         pfree(parse->start_lsn);
     791          124 :         parse->start_lsn = NULL;
     792              :     }
     793          124 :     if (parse->end_lsn != NULL)
     794              :     {
     795          124 :         pfree(parse->end_lsn);
     796          124 :         parse->end_lsn = NULL;
     797              :     }
     798          124 : }
     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          126 : verify_manifest_checksum(JsonManifestParseState *parse, const char *buffer,
     813              :                          size_t size, pg_cryptohash_ctx *incr_ctx)
     814              : {
     815          126 :     JsonManifestParseContext *context = parse->context;
     816              :     size_t      i;
     817          126 :     size_t      number_of_newlines = 0;
     818          126 :     size_t      ultimate_newline = 0;
     819          126 :     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      8377432 :     for (i = 0; i < size; ++i)
     826              :     {
     827      8377306 :         if (buffer[i] == '\n')
     828              :         {
     829        57917 :             ++number_of_newlines;
     830        57917 :             penultimate_newline = ultimate_newline;
     831        57917 :             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          126 :     if (number_of_newlines < 2)
     841            1 :         json_manifest_parse_failure(parse->context,
     842              :                                     "expected at least 2 lines");
     843          125 :     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          124 :     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          120 :         manifest_ctx = incr_ctx;
     859              :     }
     860          124 :     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          124 :     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          124 :     if (parse->manifest_checksum == NULL)
     868            0 :         context->error_cb(parse->context, "manifest has no checksum");
     869          124 :     if (strlen(parse->manifest_checksum) != PG_SHA256_DIGEST_LENGTH * 2 ||
     870          124 :         !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          123 :     if (memcmp(manifest_checksum_actual, manifest_checksum_expected,
     875              :                PG_SHA256_DIGEST_LENGTH) != 0)
     876            2 :         context->error_cb(context, "manifest checksum mismatch");
     877          121 :     pg_cryptohash_free(manifest_ctx);
     878          121 : }
     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      1627818 : hexdecode_char(char c)
     902              : {
     903      1627818 :     if (c >= '0' && c <= '9')
     904      1071422 :         return c - '0';
     905       556396 :     if (c >= 'a' && c <= 'f')
     906       556388 :         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       119324 : hexdecode_string(uint8 *result, char *input, int nbytes)
     920              : {
     921              :     int         i;
     922              : 
     923       933232 :     for (i = 0; i < nbytes; ++i)
     924              :     {
     925       813909 :         int         n1 = hexdecode_char(input[i * 2]);
     926       813909 :         int         n2 = hexdecode_char(input[i * 2 + 1]);
     927              : 
     928       813909 :         if (n1 < 0 || n2 < 0)
     929            1 :             return false;
     930       813908 :         result[i] = n1 * 16 + n2;
     931              :     }
     932              : 
     933       119323 :     return true;
     934              : }
     935              : 
     936              : /*
     937              :  * Parse an XLogRecPtr expressed using the usual string format.
     938              :  */
     939              : static bool
     940          251 : parse_xlogrecptr(XLogRecPtr *result, char *input)
     941              : {
     942              :     uint32      hi;
     943              :     uint32      lo;
     944              : 
     945          251 :     if (sscanf(input, "%X/%08X", &hi, &lo) != 2)
     946            2 :         return false;
     947          249 :     *result = ((uint64) hi) << 32 | lo;
     948          249 :     return true;
     949              : }
        

Generated by: LCOV version 2.0-1