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

Generated by: LCOV version 1.14