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

Generated by: LCOV version 1.14