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

Generated by: LCOV version 1.14