LCOV - differential code coverage report
Current view: top level - src/bin/pg_verifybackup - parse_manifest.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC GNC CBC EUB ECB
Current: Differential Code Coverage HEAD vs 15 Lines: 94.7 % 318 301 9 6 2 3 112 6 180 12 112
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 14 14 12 1 1 12
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  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 CBC          90 : 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              90 :     parse.context = context;
     129              90 :     parse.state = JM_EXPECT_TOPLEVEL_START;
     130              90 :     parse.saw_version_field = false;
     131                 : 
     132                 :     /* Create a JSON lexing context. */
     133              90 :     lex = makeJsonLexContextCstringLen(buffer, size, PG_UTF8, true);
     134                 : 
     135                 :     /* Set up semantic actions. */
     136              90 :     sem.semstate = &parse;
     137              90 :     sem.object_start = json_manifest_object_start;
     138              90 :     sem.object_end = json_manifest_object_end;
     139              90 :     sem.array_start = json_manifest_array_start;
     140              90 :     sem.array_end = json_manifest_array_end;
     141              90 :     sem.object_field_start = json_manifest_object_field_start;
     142              90 :     sem.object_field_end = NULL;
     143              90 :     sem.array_element_start = NULL;
     144              90 :     sem.array_element_end = NULL;
     145              90 :     sem.scalar = json_manifest_scalar;
     146                 : 
     147                 :     /* Run the actual JSON parser. */
     148              90 :     json_error = pg_parse_json(lex, &sem);
     149              65 :     if (json_error != JSON_SUCCESS)
     150               1 :         json_manifest_parse_failure(context, "parsing failed");
     151              64 :     if (parse.state != JM_EXPECT_EOF)
     152 UBC           0 :         json_manifest_parse_failure(context, "manifest ended unexpectedly");
     153                 : 
     154                 :     /* Verify the manifest checksum. */
     155 CBC          64 :     verify_manifest_checksum(&parse, buffer, size);
     156              60 : }
     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           59171 : json_manifest_object_start(void *state)
     167                 : {
     168           59171 :     JsonManifestParseState *parse = state;
     169                 : 
     170           59171 :     switch (parse->state)
     171                 :     {
     172              89 :         case JM_EXPECT_TOPLEVEL_START:
     173              89 :             parse->state = JM_EXPECT_TOPLEVEL_FIELD;
     174              89 :             break;
     175           59012 :         case JM_EXPECT_FILES_NEXT:
     176           59012 :             parse->state = JM_EXPECT_THIS_FILE_FIELD;
     177           59012 :             parse->pathname = NULL;
     178           59012 :             parse->encoded_pathname = NULL;
     179           59012 :             parse->size = NULL;
     180           59012 :             parse->algorithm = NULL;
     181           59012 :             parse->checksum = NULL;
     182           59012 :             break;
     183              69 :         case JM_EXPECT_WAL_RANGES_NEXT:
     184              69 :             parse->state = JM_EXPECT_THIS_WAL_RANGE_FIELD;
     185              69 :             parse->timeline = NULL;
     186              69 :             parse->start_lsn = NULL;
     187              69 :             parse->end_lsn = NULL;
     188              69 :             break;
     189               1 :         default:
     190               1 :             json_manifest_parse_failure(parse->context,
     191                 :                                         "unexpected object start");
     192 UBC           0 :             break;
     193                 :     }
     194                 : 
     195 GNC       59170 :     return JSON_SUCCESS;
     196                 : }
     197 ECB             : 
     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 GIC       59145 : json_manifest_object_end(void *state)
     208                 : {
     209 CBC       59145 :     JsonManifestParseState *parse = state;
     210                 : 
     211           59145 :     switch (parse->state)
     212                 :     {
     213              64 :         case JM_EXPECT_TOPLEVEL_END:
     214 GIC          64 :             parse->state = JM_EXPECT_EOF;
     215 CBC          64 :             break;
     216           59011 :         case JM_EXPECT_THIS_FILE_FIELD:
     217           59011 :             json_manifest_finalize_file(parse);
     218           59002 :             parse->state = JM_EXPECT_FILES_NEXT;
     219           59002 :             break;
     220              68 :         case JM_EXPECT_THIS_WAL_RANGE_FIELD:
     221              68 :             json_manifest_finalize_wal_range(parse);
     222              62 :             parse->state = JM_EXPECT_WAL_RANGES_NEXT;
     223              62 :             break;
     224               2 :         default:
     225               2 :             json_manifest_parse_failure(parse->context,
     226 ECB             :                                         "unexpected object end");
     227 LBC           0 :             break;
     228                 :     }
     229                 : 
     230 GNC       59128 :     return JSON_SUCCESS;
     231 EUB             : }
     232                 : 
     233                 : /*
     234 ECB             :  * 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 GIC         144 : json_manifest_array_start(void *state)
     242                 : {
     243             144 :     JsonManifestParseState *parse = state;
     244                 : 
     245 CBC         144 :     switch (parse->state)
     246                 :     {
     247              74 :         case JM_EXPECT_FILES_START:
     248 GIC          74 :             parse->state = JM_EXPECT_FILES_NEXT;
     249 CBC          74 :             break;
     250 GIC          69 :         case JM_EXPECT_WAL_RANGES_START:
     251 CBC          69 :             parse->state = JM_EXPECT_WAL_RANGES_NEXT;
     252              69 :             break;
     253               1 :         default:
     254               1 :             json_manifest_parse_failure(parse->context,
     255 ECB             :                                         "unexpected array start");
     256 LBC           0 :             break;
     257 ECB             :     }
     258                 : 
     259 GNC         143 :     return JSON_SUCCESS;
     260 ECB             : }
     261                 : 
     262 EUB             : /*
     263                 :  * Invoked at the end of each array in the JSON document.
     264                 :  *
     265 ECB             :  * The cases here are analogous to those in json_manifest_array_start.
     266                 :  */
     267                 : static JsonParseErrorType
     268 GIC         126 : json_manifest_array_end(void *state)
     269                 : {
     270             126 :     JsonManifestParseState *parse = state;
     271                 : 
     272             126 :     switch (parse->state)
     273                 :     {
     274 CBC         126 :         case JM_EXPECT_FILES_NEXT:
     275                 :         case JM_EXPECT_WAL_RANGES_NEXT:
     276             126 :             parse->state = JM_EXPECT_TOPLEVEL_FIELD;
     277 GIC         126 :             break;
     278 LBC           0 :         default:
     279 UIC           0 :             json_manifest_parse_failure(parse->context,
     280 ECB             :                                         "unexpected array end");
     281 UIC           0 :             break;
     282 ECB             :     }
     283                 : 
     284 GNC         126 :     return JSON_SUCCESS;
     285 ECB             : }
     286 EUB             : 
     287                 : /*
     288                 :  * Invoked at the start of each object field in the JSON document.
     289                 :  */
     290                 : static JsonParseErrorType
     291 GIC      293592 : json_manifest_object_field_start(void *state, char *fname, bool isnull)
     292 ECB             : {
     293 GIC      293592 :     JsonManifestParseState *parse = state;
     294                 : 
     295          293592 :     switch (parse->state)
     296                 :     {
     297             297 :         case JM_EXPECT_TOPLEVEL_FIELD:
     298                 : 
     299 ECB             :             /*
     300                 :              * Inside toplevel object. The version indicator should always be
     301                 :              * the first field.
     302                 :              */
     303 CBC         297 :             if (!parse->saw_version_field)
     304                 :             {
     305              87 :                 if (strcmp(fname, "PostgreSQL-Backup-Manifest-Version") != 0)
     306 GIC           1 :                     json_manifest_parse_failure(parse->context,
     307                 :                                                 "expected version indicator");
     308              86 :                 parse->state = JM_EXPECT_VERSION_VALUE;
     309              86 :                 parse->saw_version_field = true;
     310              86 :                 break;
     311 ECB             :             }
     312                 : 
     313                 :             /* Is this the list of files? */
     314 CBC         210 :             if (strcmp(fname, "Files") == 0)
     315                 :             {
     316              76 :                 parse->state = JM_EXPECT_FILES_START;
     317              76 :                 break;
     318 ECB             :             }
     319                 : 
     320                 :             /* Is this the list of WAL ranges? */
     321 GIC         134 :             if (strcmp(fname, "WAL-Ranges") == 0)
     322 ECB             :             {
     323 GIC          69 :                 parse->state = JM_EXPECT_WAL_RANGES_START;
     324 CBC          69 :                 break;
     325 ECB             :             }
     326                 : 
     327                 :             /* Is this the manifest checksum? */
     328 GIC          65 :             if (strcmp(fname, "Manifest-Checksum") == 0)
     329 ECB             :             {
     330 GIC          64 :                 parse->state = JM_EXPECT_MANIFEST_CHECKSUM_VALUE;
     331 CBC          64 :                 break;
     332 ECB             :             }
     333                 : 
     334                 :             /* It's not a field we recognize. */
     335 GIC           1 :             json_manifest_parse_failure(parse->context,
     336 ECB             :                                         "unrecognized top-level field");
     337 UIC           0 :             break;
     338 ECB             : 
     339 CBC      293096 :         case JM_EXPECT_THIS_FILE_FIELD:
     340                 :             /* Inside object for one file; which key have we got? */
     341 GIC      293096 :             if (strcmp(fname, "Path") == 0)
     342           58043 :                 parse->file_field = JMFF_PATH;
     343 CBC      235053 :             else if (strcmp(fname, "Encoded-Path") == 0)
     344 GIC         968 :                 parse->file_field = JMFF_ENCODED_PATH;
     345 GBC      234085 :             else if (strcmp(fname, "Size") == 0)
     346 GIC       59008 :                 parse->file_field = JMFF_SIZE;
     347 CBC      175077 :             else if (strcmp(fname, "Last-Modified") == 0)
     348 GIC       59001 :                 parse->file_field = JMFF_LAST_MODIFIED;
     349 CBC      116076 :             else if (strcmp(fname, "Checksum-Algorithm") == 0)
     350           58037 :                 parse->file_field = JMFF_CHECKSUM_ALGORITHM;
     351           58039 :             else if (strcmp(fname, "Checksum") == 0)
     352           58038 :                 parse->file_field = JMFF_CHECKSUM;
     353 ECB             :             else
     354 CBC           1 :                 json_manifest_parse_failure(parse->context,
     355 ECB             :                                             "unexpected file field");
     356 CBC      293095 :             parse->state = JM_EXPECT_THIS_FILE_VALUE;
     357          293095 :             break;
     358 ECB             : 
     359 CBC         199 :         case JM_EXPECT_THIS_WAL_RANGE_FIELD:
     360 ECB             :             /* Inside object for one file; which key have we got? */
     361 GIC         199 :             if (strcmp(fname, "Timeline") == 0)
     362 CBC          67 :                 parse->wal_range_field = JMWRF_TIMELINE;
     363 GIC         132 :             else if (strcmp(fname, "Start-LSN") == 0)
     364 CBC          66 :                 parse->wal_range_field = JMWRF_START_LSN;
     365              66 :             else if (strcmp(fname, "End-LSN") == 0)
     366 GIC          65 :                 parse->wal_range_field = JMWRF_END_LSN;
     367 ECB             :             else
     368 GIC           1 :                 json_manifest_parse_failure(parse->context,
     369 ECB             :                                             "unexpected WAL range field");
     370 CBC         198 :             parse->state = JM_EXPECT_THIS_WAL_RANGE_VALUE;
     371             198 :             break;
     372 ECB             : 
     373 LBC           0 :         default:
     374               0 :             json_manifest_parse_failure(parse->context,
     375                 :                                         "unexpected object field");
     376               0 :             break;
     377                 :     }
     378                 : 
     379 GNC      293588 :     return JSON_SUCCESS;
     380 ECB             : }
     381                 : 
     382                 : /*
     383 EUB             :  * 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 ECB             :  * 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 GIC      293444 : json_manifest_scalar(void *state, char *token, JsonTokenType tokentype)
     399                 : {
     400          293444 :     JsonManifestParseState *parse = state;
     401                 : 
     402          293444 :     switch (parse->state)
     403                 :     {
     404              86 :         case JM_EXPECT_VERSION_VALUE:
     405              86 :             if (strcmp(token, "1") != 0)
     406               1 :                 json_manifest_parse_failure(parse->context,
     407                 :                                             "unexpected manifest version");
     408 CBC          85 :             parse->state = JM_EXPECT_TOPLEVEL_FIELD;
     409 GIC          85 :             break;
     410 ECB             : 
     411 GIC      293095 :         case JM_EXPECT_THIS_FILE_VALUE:
     412 CBC      293095 :             switch (parse->file_field)
     413                 :             {
     414           58043 :                 case JMFF_PATH:
     415           58043 :                     parse->pathname = token;
     416           58043 :                     break;
     417 GIC         968 :                 case JMFF_ENCODED_PATH:
     418 CBC         968 :                     parse->encoded_pathname = token;
     419             968 :                     break;
     420 GIC       59008 :                 case JMFF_SIZE:
     421 CBC       59008 :                     parse->size = token;
     422           59008 :                     break;
     423 GIC       59001 :                 case JMFF_LAST_MODIFIED:
     424 CBC       59001 :                     pfree(token);   /* unused */
     425           59001 :                     break;
     426           58037 :                 case JMFF_CHECKSUM_ALGORITHM:
     427           58037 :                     parse->algorithm = token;
     428           58037 :                     break;
     429           58038 :                 case JMFF_CHECKSUM:
     430           58038 :                     parse->checksum = token;
     431           58038 :                     break;
     432 ECB             :             }
     433 CBC      293095 :             parse->state = JM_EXPECT_THIS_FILE_FIELD;
     434          293095 :             break;
     435 ECB             : 
     436 CBC         198 :         case JM_EXPECT_THIS_WAL_RANGE_VALUE:
     437             198 :             switch (parse->wal_range_field)
     438 ECB             :             {
     439 CBC          67 :                 case JMWRF_TIMELINE:
     440              67 :                     parse->timeline = token;
     441              67 :                     break;
     442 GIC          66 :                 case JMWRF_START_LSN:
     443 CBC          66 :                     parse->start_lsn = token;
     444              66 :                     break;
     445 GIC          65 :                 case JMWRF_END_LSN:
     446 CBC          65 :                     parse->end_lsn = token;
     447              65 :                     break;
     448                 :             }
     449             198 :             parse->state = JM_EXPECT_THIS_WAL_RANGE_FIELD;
     450             198 :             break;
     451 ECB             : 
     452 CBC          64 :         case JM_EXPECT_MANIFEST_CHECKSUM_VALUE:
     453              64 :             parse->state = JM_EXPECT_TOPLEVEL_END;
     454              64 :             parse->manifest_checksum = token;
     455              64 :             break;
     456 ECB             : 
     457 CBC           1 :         default:
     458 GIC           1 :             json_manifest_parse_failure(parse->context, "unexpected scalar");
     459 LBC           0 :             break;
     460 ECB             :     }
     461                 : 
     462 GNC      293442 :     return JSON_SUCCESS;
     463                 : }
     464 ECB             : 
     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 EUB             : static void
     472 GIC       59011 : json_manifest_finalize_file(JsonManifestParseState *parse)
     473                 : {
     474 CBC       59011 :     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 GIC       59011 :     if (parse->pathname == NULL && parse->encoded_pathname == NULL)
     484 CBC           1 :         json_manifest_parse_failure(parse->context, "missing path name");
     485 GIC       59010 :     if (parse->pathname != NULL && parse->encoded_pathname != NULL)
     486 CBC           1 :         json_manifest_parse_failure(parse->context,
     487                 :                                     "both path name and encoded path name");
     488 GIC       59009 :     if (parse->size == NULL)
     489               1 :         json_manifest_parse_failure(parse->context, "missing size");
     490           59008 :     if (parse->algorithm == NULL && parse->checksum != NULL)
     491               1 :         json_manifest_parse_failure(parse->context,
     492                 :                                     "checksum without algorithm");
     493                 : 
     494                 :     /* Decode encoded pathname, if that's what we have. */
     495 CBC       59007 :     if (parse->encoded_pathname != NULL)
     496 ECB             :     {
     497 CBC         967 :         int         encoded_length = strlen(parse->encoded_pathname);
     498             967 :         int         raw_length = encoded_length / 2;
     499                 : 
     500             967 :         parse->pathname = palloc(raw_length + 1);
     501             967 :         if (encoded_length % 2 != 0 ||
     502             966 :             !hexdecode_string((uint8 *) parse->pathname,
     503 ECB             :                               parse->encoded_pathname,
     504                 :                               raw_length))
     505 GIC           1 :             json_manifest_parse_failure(parse->context,
     506                 :                                         "could not decode file name");
     507 CBC         966 :         parse->pathname[raw_length] = '\0';
     508 GIC         966 :         pfree(parse->encoded_pathname);
     509 CBC         966 :         parse->encoded_pathname = NULL;
     510 ECB             :     }
     511                 : 
     512                 :     /* Parse size. */
     513 CBC       59006 :     size = strtoul(parse->size, &ep, 10);
     514           59006 :     if (*ep)
     515 GIC           1 :         json_manifest_parse_failure(parse->context,
     516                 :                                     "file size is not an integer");
     517 ECB             : 
     518                 :     /* Parse the checksum algorithm, if it's present. */
     519 CBC       59005 :     if (parse->algorithm == NULL)
     520             968 :         checksum_type = CHECKSUM_TYPE_NONE;
     521           58037 :     else if (!pg_checksum_parse_type(parse->algorithm, &checksum_type))
     522 GIC           1 :         context->error_cb(context, "unrecognized checksum algorithm: \"%s\"",
     523                 :                           parse->algorithm);
     524                 : 
     525 ECB             :     /* Parse the checksum payload, if it's present. */
     526 CBC       59004 :     checksum_string_length = parse->checksum == NULL ? 0
     527           58036 :         : strlen(parse->checksum);
     528 GIC       59004 :     if (checksum_string_length == 0)
     529                 :     {
     530             968 :         checksum_length = 0;
     531 CBC         968 :         checksum_payload = NULL;
     532 ECB             :     }
     533                 :     else
     534                 :     {
     535 GIC       58036 :         checksum_length = checksum_string_length / 2;
     536           58036 :         checksum_payload = palloc(checksum_length);
     537           58036 :         if (checksum_string_length % 2 != 0 ||
     538 CBC       58035 :             !hexdecode_string(checksum_payload, parse->checksum,
     539 ECB             :                               checksum_length))
     540 CBC           1 :             context->error_cb(context,
     541                 :                               "invalid checksum for file \"%s\": \"%s\"",
     542 ECB             :                               parse->pathname, parse->checksum);
     543                 :     }
     544                 : 
     545                 :     /* Invoke the callback with the details we've gathered. */
     546 GIC       59003 :     context->perfile_cb(context, parse->pathname, size,
     547 ECB             :                         checksum_type, checksum_length, checksum_payload);
     548                 : 
     549                 :     /* Free memory we no longer need. */
     550 CBC       59002 :     if (parse->size != NULL)
     551                 :     {
     552           59002 :         pfree(parse->size);
     553 GIC       59002 :         parse->size = NULL;
     554                 :     }
     555           59002 :     if (parse->algorithm != NULL)
     556                 :     {
     557           58035 :         pfree(parse->algorithm);
     558 CBC       58035 :         parse->algorithm = NULL;
     559                 :     }
     560 GIC       59002 :     if (parse->checksum != NULL)
     561                 :     {
     562 CBC       58035 :         pfree(parse->checksum);
     563 GIC       58035 :         parse->checksum = NULL;
     564 ECB             :     }
     565 CBC       59002 : }
     566                 : 
     567 ECB             : /*
     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 CBC          68 : json_manifest_finalize_wal_range(JsonManifestParseState *parse)
     575 ECB             : {
     576 GIC          68 :     JsonManifestParseContext *context = parse->context;
     577 ECB             :     TimeLineID  tli;
     578                 :     XLogRecPtr  start_lsn,
     579                 :                 end_lsn;
     580                 :     char       *ep;
     581                 : 
     582                 :     /* Make sure all fields are present. */
     583 GIC          68 :     if (parse->timeline == NULL)
     584               1 :         json_manifest_parse_failure(parse->context, "missing timeline");
     585              67 :     if (parse->start_lsn == NULL)
     586 CBC           1 :         json_manifest_parse_failure(parse->context, "missing start LSN");
     587 GIC          66 :     if (parse->end_lsn == NULL)
     588 CBC           1 :         json_manifest_parse_failure(parse->context, "missing end LSN");
     589                 : 
     590                 :     /* Parse timeline. */
     591 GIC          65 :     tli = strtoul(parse->timeline, &ep, 10);
     592              65 :     if (*ep)
     593               1 :         json_manifest_parse_failure(parse->context,
     594                 :                                     "timeline is not an integer");
     595 CBC          64 :     if (!parse_xlogrecptr(&start_lsn, parse->start_lsn))
     596               1 :         json_manifest_parse_failure(parse->context,
     597 ECB             :                                     "could not parse start LSN");
     598 CBC          63 :     if (!parse_xlogrecptr(&end_lsn, parse->end_lsn))
     599               1 :         json_manifest_parse_failure(parse->context,
     600 ECB             :                                     "could not parse end LSN");
     601                 : 
     602                 :     /* Invoke the callback with the details we've gathered. */
     603 CBC          62 :     context->perwalrange_cb(context, tli, start_lsn, end_lsn);
     604 ECB             : 
     605                 :     /* Free memory we no longer need. */
     606 GIC          62 :     if (parse->timeline != NULL)
     607 ECB             :     {
     608 CBC          62 :         pfree(parse->timeline);
     609 GIC          62 :         parse->timeline = NULL;
     610 ECB             :     }
     611 CBC          62 :     if (parse->start_lsn != NULL)
     612                 :     {
     613 GIC          62 :         pfree(parse->start_lsn);
     614              62 :         parse->start_lsn = NULL;
     615 ECB             :     }
     616 GIC          62 :     if (parse->end_lsn != NULL)
     617                 :     {
     618 CBC          62 :         pfree(parse->end_lsn);
     619 GIC          62 :         parse->end_lsn = NULL;
     620 ECB             :     }
     621 CBC          62 : }
     622                 : 
     623 ECB             : /*
     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 CBC          64 : verify_manifest_checksum(JsonManifestParseState *parse, char *buffer,
     632                 :                          size_t size)
     633 ECB             : {
     634 GIC          64 :     JsonManifestParseContext *context = parse->context;
     635                 :     size_t      i;
     636              64 :     size_t      number_of_newlines = 0;
     637              64 :     size_t      ultimate_newline = 0;
     638              64 :     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 ECB             :     /* Find the last two newlines in the file. */
     644 GIC     8655763 :     for (i = 0; i < size; ++i)
     645                 :     {
     646 CBC     8655699 :         if (buffer[i] == '\n')
     647                 :         {
     648           59434 :             ++number_of_newlines;
     649           59434 :             penultimate_newline = ultimate_newline;
     650           59434 :             ultimate_newline = i;
     651                 :         }
     652                 :     }
     653                 : 
     654                 :     /*
     655                 :      * Make sure that the last newline is right at the end, and that there are
     656 ECB             :      * 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 GIC          64 :     if (number_of_newlines < 2)
     660 CBC           1 :         json_manifest_parse_failure(parse->context,
     661 ECB             :                                     "expected at least 2 lines");
     662 CBC          63 :     if (ultimate_newline != size - 1)
     663 GIC           1 :         json_manifest_parse_failure(parse->context,
     664                 :                                     "last line not newline-terminated");
     665                 : 
     666                 :     /* Checksum the rest. */
     667              62 :     manifest_ctx = pg_cryptohash_create(PG_SHA256);
     668              62 :     if (manifest_ctx == NULL)
     669 UIC           0 :         context->error_cb(context, "out of memory");
     670 GIC          62 :     if (pg_cryptohash_init(manifest_ctx) < 0)
     671 LBC           0 :         context->error_cb(context, "could not initialize checksum of manifest");
     672 CBC          62 :     if (pg_cryptohash_update(manifest_ctx, (uint8 *) buffer, penultimate_newline + 1) < 0)
     673 UIC           0 :         context->error_cb(context, "could not update checksum of manifest");
     674 CBC          62 :     if (pg_cryptohash_final(manifest_ctx, manifest_checksum_actual,
     675 ECB             :                             sizeof(manifest_checksum_actual)) < 0)
     676 UIC           0 :         context->error_cb(context, "could not finalize checksum of manifest");
     677                 : 
     678                 :     /* Now verify it. */
     679 CBC          62 :     if (parse->manifest_checksum == NULL)
     680 LBC           0 :         context->error_cb(parse->context, "manifest has no checksum");
     681 GBC          62 :     if (strlen(parse->manifest_checksum) != PG_SHA256_DIGEST_LENGTH * 2 ||
     682 CBC          62 :         !hexdecode_string(manifest_checksum_expected, parse->manifest_checksum,
     683 EUB             :                           PG_SHA256_DIGEST_LENGTH))
     684 CBC           1 :         context->error_cb(context, "invalid manifest checksum: \"%s\"",
     685 EUB             :                           parse->manifest_checksum);
     686 CBC          61 :     if (memcmp(manifest_checksum_actual, manifest_checksum_expected,
     687                 :                PG_SHA256_DIGEST_LENGTH) != 0)
     688 GBC           1 :         context->error_cb(context, "manifest checksum mismatch");
     689 GIC          60 :     pg_cryptohash_free(manifest_ctx);
     690              60 : }
     691 ECB             : 
     692 EUB             : /*
     693 ECB             :  * 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 CBC          25 : json_manifest_parse_failure(JsonManifestParseContext *context, char *msg)
     702 ECB             : {
     703 GIC          25 :     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          792792 : hexdecode_char(char c)
     713 ECB             : {
     714 GIC      792792 :     if (c >= '0' && c <= '9')
     715 CBC      528300 :         return c - '0';
     716 GIC      264492 :     if (c >= 'a' && c <= 'f')
     717          264484 :         return c - 'a' + 10;
     718               8 :     if (c >= 'A' && c <= 'F')
     719               6 :         return c - 'A' + 10;
     720                 : 
     721               2 :     return -1;
     722                 : }
     723                 : 
     724 ECB             : /*
     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 CBC       59063 : hexdecode_string(uint8 *result, char *input, int nbytes)
     731 ECB             : {
     732                 :     int         i;
     733                 : 
     734 GIC      455458 :     for (i = 0; i < nbytes; ++i)
     735                 :     {
     736          396396 :         int         n1 = hexdecode_char(input[i * 2]);
     737          396396 :         int         n2 = hexdecode_char(input[i * 2 + 1]);
     738                 : 
     739          396396 :         if (n1 < 0 || n2 < 0)
     740               1 :             return false;
     741          396395 :         result[i] = n1 * 16 + n2;
     742 ECB             :     }
     743                 : 
     744 GIC       59062 :     return true;
     745                 : }
     746 ECB             : 
     747                 : /*
     748                 :  * Parse an XLogRecPtr expressed using the usual string format.
     749                 :  */
     750                 : static bool
     751 CBC         127 : parse_xlogrecptr(XLogRecPtr *result, char *input)
     752 ECB             : {
     753                 :     uint32      hi;
     754                 :     uint32      lo;
     755                 : 
     756 CBC         127 :     if (sscanf(input, "%X/%X", &hi, &lo) != 2)
     757 GIC           2 :         return false;
     758             125 :     *result = ((uint64) hi) << 32 | lo;
     759             125 :     return true;
     760                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a