LCOV - code coverage report
Current view: top level - src/common - parse_manifest.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 416 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 19 0
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 0.0 % 200 0

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * parse_manifest.c
       4                 :             :  *        Parse a backup manifest in JSON format.
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :             :  *
       9                 :             :  * src/common/parse_manifest.c
      10                 :             :  *
      11                 :             :  *-------------------------------------------------------------------------
      12                 :             :  */
      13                 :             : 
      14                 :             : #include "postgres_fe.h"
      15                 :             : 
      16                 :             : #include "common/jsonapi.h"
      17                 :             : #include "common/parse_manifest.h"
      18                 :             : 
      19                 :             : /*
      20                 :             :  * Semantic states for JSON manifest parsing.
      21                 :             :  */
      22                 :             : typedef enum
      23                 :             : {
      24                 :             :         JM_EXPECT_TOPLEVEL_START,
      25                 :             :         JM_EXPECT_TOPLEVEL_END,
      26                 :             :         JM_EXPECT_TOPLEVEL_FIELD,
      27                 :             :         JM_EXPECT_VERSION_VALUE,
      28                 :             :         JM_EXPECT_SYSTEM_IDENTIFIER_VALUE,
      29                 :             :         JM_EXPECT_FILES_START,
      30                 :             :         JM_EXPECT_FILES_NEXT,
      31                 :             :         JM_EXPECT_THIS_FILE_FIELD,
      32                 :             :         JM_EXPECT_THIS_FILE_VALUE,
      33                 :             :         JM_EXPECT_WAL_RANGES_START,
      34                 :             :         JM_EXPECT_WAL_RANGES_NEXT,
      35                 :             :         JM_EXPECT_THIS_WAL_RANGE_FIELD,
      36                 :             :         JM_EXPECT_THIS_WAL_RANGE_VALUE,
      37                 :             :         JM_EXPECT_MANIFEST_CHECKSUM_VALUE,
      38                 :             :         JM_EXPECT_EOF,
      39                 :             : } JsonManifestSemanticState;
      40                 :             : 
      41                 :             : /*
      42                 :             :  * Possible fields for one file as described by the manifest.
      43                 :             :  */
      44                 :             : typedef enum
      45                 :             : {
      46                 :             :         JMFF_PATH,
      47                 :             :         JMFF_ENCODED_PATH,
      48                 :             :         JMFF_SIZE,
      49                 :             :         JMFF_LAST_MODIFIED,
      50                 :             :         JMFF_CHECKSUM_ALGORITHM,
      51                 :             :         JMFF_CHECKSUM,
      52                 :             : } JsonManifestFileField;
      53                 :             : 
      54                 :             : /*
      55                 :             :  * Possible fields for one file as described by the manifest.
      56                 :             :  */
      57                 :             : typedef enum
      58                 :             : {
      59                 :             :         JMWRF_TIMELINE,
      60                 :             :         JMWRF_START_LSN,
      61                 :             :         JMWRF_END_LSN,
      62                 :             : } JsonManifestWALRangeField;
      63                 :             : 
      64                 :             : /*
      65                 :             :  * Internal state used while decoding the JSON-format backup manifest.
      66                 :             :  */
      67                 :             : typedef struct
      68                 :             : {
      69                 :             :         JsonManifestParseContext *context;
      70                 :             :         JsonManifestSemanticState state;
      71                 :             : 
      72                 :             :         /* These fields are used for parsing objects in the list of files. */
      73                 :             :         JsonManifestFileField file_field;
      74                 :             :         char       *pathname;
      75                 :             :         char       *encoded_pathname;
      76                 :             :         char       *size;
      77                 :             :         char       *algorithm;
      78                 :             :         pg_checksum_type checksum_algorithm;
      79                 :             :         char       *checksum;
      80                 :             : 
      81                 :             :         /* These fields are used for parsing objects in the list of WAL ranges. */
      82                 :             :         JsonManifestWALRangeField wal_range_field;
      83                 :             :         char       *timeline;
      84                 :             :         char       *start_lsn;
      85                 :             :         char       *end_lsn;
      86                 :             : 
      87                 :             :         /* Miscellaneous other stuff. */
      88                 :             :         bool            saw_version_field;
      89                 :             :         char       *manifest_version;
      90                 :             :         char       *manifest_system_identifier;
      91                 :             :         char       *manifest_checksum;
      92                 :             : } JsonManifestParseState;
      93                 :             : 
      94                 :             : /* typedef appears in parse_manifest.h */
      95                 :             : struct JsonManifestParseIncrementalState
      96                 :             : {
      97                 :             :         JsonLexContext lex;
      98                 :             :         JsonSemAction sem;
      99                 :             :         pg_cryptohash_ctx *manifest_ctx;
     100                 :             : };
     101                 :             : 
     102                 :             : static JsonParseErrorType json_manifest_object_start(void *state);
     103                 :             : static JsonParseErrorType json_manifest_object_end(void *state);
     104                 :             : static JsonParseErrorType json_manifest_array_start(void *state);
     105                 :             : static JsonParseErrorType json_manifest_array_end(void *state);
     106                 :             : static JsonParseErrorType json_manifest_object_field_start(void *state, char *fname,
     107                 :             :                                                                                                                    bool isnull);
     108                 :             : static JsonParseErrorType json_manifest_scalar(void *state, char *token,
     109                 :             :                                                                                            JsonTokenType tokentype);
     110                 :             : static void json_manifest_finalize_version(JsonManifestParseState *parse);
     111                 :             : static void json_manifest_finalize_system_identifier(JsonManifestParseState *parse);
     112                 :             : static void json_manifest_finalize_file(JsonManifestParseState *parse);
     113                 :             : static void json_manifest_finalize_wal_range(JsonManifestParseState *parse);
     114                 :             : static void verify_manifest_checksum(JsonManifestParseState *parse,
     115                 :             :                                                                          const char *buffer, size_t size,
     116                 :             :                                                                          pg_cryptohash_ctx *incr_ctx);
     117                 :             : pg_noreturn static void json_manifest_parse_failure(JsonManifestParseContext *context,
     118                 :             :                                                                                                         char *msg);
     119                 :             : 
     120                 :             : static int      hexdecode_char(char c);
     121                 :             : static bool hexdecode_string(uint8 *result, char *input, int nbytes);
     122                 :             : static bool parse_xlogrecptr(XLogRecPtr *result, char *input);
     123                 :             : 
     124                 :             : /*
     125                 :             :  * Set up for incremental parsing of the manifest.
     126                 :             :  */
     127                 :             : 
     128                 :             : JsonManifestParseIncrementalState *
     129                 :           0 : json_parse_manifest_incremental_init(JsonManifestParseContext *context)
     130                 :             : {
     131                 :           0 :         JsonManifestParseIncrementalState *incstate;
     132                 :           0 :         JsonManifestParseState *parse;
     133                 :           0 :         pg_cryptohash_ctx *manifest_ctx;
     134                 :             : 
     135                 :           0 :         incstate = palloc_object(JsonManifestParseIncrementalState);
     136                 :           0 :         parse = palloc_object(JsonManifestParseState);
     137                 :             : 
     138                 :           0 :         parse->context = context;
     139                 :           0 :         parse->state = JM_EXPECT_TOPLEVEL_START;
     140                 :           0 :         parse->saw_version_field = false;
     141                 :             : 
     142                 :           0 :         makeJsonLexContextIncremental(&(incstate->lex), PG_UTF8, true);
     143                 :             : 
     144                 :           0 :         incstate->sem.semstate = parse;
     145                 :           0 :         incstate->sem.object_start = json_manifest_object_start;
     146                 :           0 :         incstate->sem.object_end = json_manifest_object_end;
     147                 :           0 :         incstate->sem.array_start = json_manifest_array_start;
     148                 :           0 :         incstate->sem.array_end = json_manifest_array_end;
     149                 :           0 :         incstate->sem.object_field_start = json_manifest_object_field_start;
     150                 :           0 :         incstate->sem.object_field_end = NULL;
     151                 :           0 :         incstate->sem.array_element_start = NULL;
     152                 :           0 :         incstate->sem.array_element_end = NULL;
     153                 :           0 :         incstate->sem.scalar = json_manifest_scalar;
     154                 :             : 
     155                 :           0 :         manifest_ctx = pg_cryptohash_create(PG_SHA256);
     156         [ #  # ]:           0 :         if (manifest_ctx == NULL)
     157                 :           0 :                 context->error_cb(context, "out of memory");
     158         [ #  # ]:           0 :         if (pg_cryptohash_init(manifest_ctx) < 0)
     159                 :           0 :                 context->error_cb(context, "could not initialize checksum of manifest");
     160                 :           0 :         incstate->manifest_ctx = manifest_ctx;
     161                 :             : 
     162                 :           0 :         return incstate;
     163                 :           0 : }
     164                 :             : 
     165                 :             : /*
     166                 :             :  * Free an incremental state object and its contents.
     167                 :             :  */
     168                 :             : void
     169                 :           0 : json_parse_manifest_incremental_shutdown(JsonManifestParseIncrementalState *incstate)
     170                 :             : {
     171                 :           0 :         pfree(incstate->sem.semstate);
     172                 :           0 :         freeJsonLexContext(&(incstate->lex));
     173                 :             :         /* incstate->manifest_ctx has already been freed */
     174                 :           0 :         pfree(incstate);
     175                 :           0 : }
     176                 :             : 
     177                 :             : /*
     178                 :             :  * parse the manifest in pieces.
     179                 :             :  *
     180                 :             :  * The caller must ensure that the final piece contains the final lines
     181                 :             :  * with the complete checksum.
     182                 :             :  */
     183                 :             : 
     184                 :             : void
     185                 :           0 : json_parse_manifest_incremental_chunk(JsonManifestParseIncrementalState *incstate,
     186                 :             :                                                                           const char *chunk, size_t size, bool is_last)
     187                 :             : {
     188                 :           0 :         JsonParseErrorType res,
     189                 :             :                                 expected;
     190                 :           0 :         JsonManifestParseState *parse = incstate->sem.semstate;
     191                 :           0 :         JsonManifestParseContext *context = parse->context;
     192                 :             : 
     193                 :           0 :         res = pg_parse_json_incremental(&(incstate->lex), &(incstate->sem),
     194                 :           0 :                                                                         chunk, size, is_last);
     195                 :             : 
     196                 :           0 :         expected = is_last ? JSON_SUCCESS : JSON_INCOMPLETE;
     197                 :             : 
     198         [ #  # ]:           0 :         if (res != expected)
     199                 :           0 :                 json_manifest_parse_failure(context,
     200                 :           0 :                                                                         json_errdetail(res, &(incstate->lex)));
     201                 :             : 
     202   [ #  #  #  # ]:           0 :         if (is_last && parse->state != JM_EXPECT_EOF)
     203                 :           0 :                 json_manifest_parse_failure(context, "manifest ended unexpectedly");
     204                 :             : 
     205         [ #  # ]:           0 :         if (!is_last)
     206                 :             :         {
     207                 :           0 :                 if (pg_cryptohash_update(incstate->manifest_ctx,
     208   [ #  #  #  # ]:           0 :                                                                  (const uint8 *) chunk, size) < 0)
     209                 :           0 :                         context->error_cb(context, "could not update checksum of manifest");
     210                 :           0 :         }
     211                 :             :         else
     212                 :             :         {
     213                 :           0 :                 verify_manifest_checksum(parse, chunk, size, incstate->manifest_ctx);
     214                 :             :         }
     215                 :           0 : }
     216                 :             : 
     217                 :             : 
     218                 :             : /*
     219                 :             :  * Main entrypoint to parse a JSON-format backup manifest.
     220                 :             :  *
     221                 :             :  * Caller should set up the parsing context and then invoke this function.
     222                 :             :  * For each file whose information is extracted from the manifest,
     223                 :             :  * context->per_file_cb is invoked.  In case of trouble, context->error_cb is
     224                 :             :  * invoked and is expected not to return.
     225                 :             :  */
     226                 :             : void
     227                 :           0 : json_parse_manifest(JsonManifestParseContext *context, const char *buffer,
     228                 :             :                                         size_t size)
     229                 :             : {
     230                 :           0 :         JsonLexContext *lex;
     231                 :           0 :         JsonParseErrorType json_error;
     232                 :           0 :         JsonSemAction sem;
     233                 :           0 :         JsonManifestParseState parse;
     234                 :             : 
     235                 :             :         /* Set up our private parsing context. */
     236                 :           0 :         parse.context = context;
     237                 :           0 :         parse.state = JM_EXPECT_TOPLEVEL_START;
     238                 :           0 :         parse.saw_version_field = false;
     239                 :             : 
     240                 :             :         /* Create a JSON lexing context. */
     241                 :           0 :         lex = makeJsonLexContextCstringLen(NULL, buffer, size, PG_UTF8, true);
     242                 :             : 
     243                 :             :         /* Set up semantic actions. */
     244                 :           0 :         sem.semstate = &parse;
     245                 :           0 :         sem.object_start = json_manifest_object_start;
     246                 :           0 :         sem.object_end = json_manifest_object_end;
     247                 :           0 :         sem.array_start = json_manifest_array_start;
     248                 :           0 :         sem.array_end = json_manifest_array_end;
     249                 :           0 :         sem.object_field_start = json_manifest_object_field_start;
     250                 :           0 :         sem.object_field_end = NULL;
     251                 :           0 :         sem.array_element_start = NULL;
     252                 :           0 :         sem.array_element_end = NULL;
     253                 :           0 :         sem.scalar = json_manifest_scalar;
     254                 :             : 
     255                 :             :         /* Run the actual JSON parser. */
     256                 :           0 :         json_error = pg_parse_json(lex, &sem);
     257         [ #  # ]:           0 :         if (json_error != JSON_SUCCESS)
     258                 :           0 :                 json_manifest_parse_failure(context, json_errdetail(json_error, lex));
     259         [ #  # ]:           0 :         if (parse.state != JM_EXPECT_EOF)
     260                 :           0 :                 json_manifest_parse_failure(context, "manifest ended unexpectedly");
     261                 :             : 
     262                 :             :         /* Verify the manifest checksum. */
     263                 :           0 :         verify_manifest_checksum(&parse, buffer, size, NULL);
     264                 :             : 
     265                 :           0 :         freeJsonLexContext(lex);
     266                 :           0 : }
     267                 :             : 
     268                 :             : /*
     269                 :             :  * Invoked at the start of each object in the JSON document.
     270                 :             :  *
     271                 :             :  * The document as a whole is expected to be an object; each file and each
     272                 :             :  * WAL range is also expected to be an object. If we're anywhere else in the
     273                 :             :  * document, it's an error.
     274                 :             :  */
     275                 :             : static JsonParseErrorType
     276                 :           0 : json_manifest_object_start(void *state)
     277                 :             : {
     278                 :           0 :         JsonManifestParseState *parse = state;
     279                 :             : 
     280   [ #  #  #  # ]:           0 :         switch (parse->state)
     281                 :             :         {
     282                 :             :                 case JM_EXPECT_TOPLEVEL_START:
     283                 :           0 :                         parse->state = JM_EXPECT_TOPLEVEL_FIELD;
     284                 :           0 :                         break;
     285                 :             :                 case JM_EXPECT_FILES_NEXT:
     286                 :           0 :                         parse->state = JM_EXPECT_THIS_FILE_FIELD;
     287                 :           0 :                         parse->pathname = NULL;
     288                 :           0 :                         parse->encoded_pathname = NULL;
     289                 :           0 :                         parse->size = NULL;
     290                 :           0 :                         parse->algorithm = NULL;
     291                 :           0 :                         parse->checksum = NULL;
     292                 :           0 :                         break;
     293                 :             :                 case JM_EXPECT_WAL_RANGES_NEXT:
     294                 :           0 :                         parse->state = JM_EXPECT_THIS_WAL_RANGE_FIELD;
     295                 :           0 :                         parse->timeline = NULL;
     296                 :           0 :                         parse->start_lsn = NULL;
     297                 :           0 :                         parse->end_lsn = NULL;
     298                 :           0 :                         break;
     299                 :             :                 default:
     300                 :           0 :                         json_manifest_parse_failure(parse->context,
     301                 :             :                                                                                 "unexpected object start");
     302                 :             :                         break;
     303                 :             :         }
     304                 :             : 
     305                 :           0 :         return JSON_SUCCESS;
     306                 :           0 : }
     307                 :             : 
     308                 :             : /*
     309                 :             :  * Invoked at the end of each object in the JSON document.
     310                 :             :  *
     311                 :             :  * The possible cases here are the same as for json_manifest_object_start.
     312                 :             :  * There's nothing special to do at the end of the document, but when we
     313                 :             :  * reach the end of an object representing a particular file or WAL range,
     314                 :             :  * we must call json_manifest_finalize_file() to save the associated details.
     315                 :             :  */
     316                 :             : static JsonParseErrorType
     317                 :           0 : json_manifest_object_end(void *state)
     318                 :             : {
     319                 :           0 :         JsonManifestParseState *parse = state;
     320                 :             : 
     321   [ #  #  #  # ]:           0 :         switch (parse->state)
     322                 :             :         {
     323                 :             :                 case JM_EXPECT_TOPLEVEL_END:
     324                 :           0 :                         parse->state = JM_EXPECT_EOF;
     325                 :           0 :                         break;
     326                 :             :                 case JM_EXPECT_THIS_FILE_FIELD:
     327                 :           0 :                         json_manifest_finalize_file(parse);
     328                 :           0 :                         parse->state = JM_EXPECT_FILES_NEXT;
     329                 :           0 :                         break;
     330                 :             :                 case JM_EXPECT_THIS_WAL_RANGE_FIELD:
     331                 :           0 :                         json_manifest_finalize_wal_range(parse);
     332                 :           0 :                         parse->state = JM_EXPECT_WAL_RANGES_NEXT;
     333                 :           0 :                         break;
     334                 :             :                 default:
     335                 :           0 :                         json_manifest_parse_failure(parse->context,
     336                 :             :                                                                                 "unexpected object end");
     337                 :             :                         break;
     338                 :             :         }
     339                 :             : 
     340                 :           0 :         return JSON_SUCCESS;
     341                 :           0 : }
     342                 :             : 
     343                 :             : /*
     344                 :             :  * Invoked at the start of each array in the JSON document.
     345                 :             :  *
     346                 :             :  * Within the toplevel object, the value associated with the "Files" key
     347                 :             :  * should be an array. Similarly for the "WAL-Ranges" key. No other arrays
     348                 :             :  * are expected.
     349                 :             :  */
     350                 :             : static JsonParseErrorType
     351                 :           0 : json_manifest_array_start(void *state)
     352                 :             : {
     353                 :           0 :         JsonManifestParseState *parse = state;
     354                 :             : 
     355      [ #  #  # ]:           0 :         switch (parse->state)
     356                 :             :         {
     357                 :             :                 case JM_EXPECT_FILES_START:
     358                 :           0 :                         parse->state = JM_EXPECT_FILES_NEXT;
     359                 :           0 :                         break;
     360                 :             :                 case JM_EXPECT_WAL_RANGES_START:
     361                 :           0 :                         parse->state = JM_EXPECT_WAL_RANGES_NEXT;
     362                 :           0 :                         break;
     363                 :             :                 default:
     364                 :           0 :                         json_manifest_parse_failure(parse->context,
     365                 :             :                                                                                 "unexpected array start");
     366                 :             :                         break;
     367                 :             :         }
     368                 :             : 
     369                 :           0 :         return JSON_SUCCESS;
     370                 :           0 : }
     371                 :             : 
     372                 :             : /*
     373                 :             :  * Invoked at the end of each array in the JSON document.
     374                 :             :  *
     375                 :             :  * The cases here are analogous to those in json_manifest_array_start.
     376                 :             :  */
     377                 :             : static JsonParseErrorType
     378                 :           0 : json_manifest_array_end(void *state)
     379                 :             : {
     380                 :           0 :         JsonManifestParseState *parse = state;
     381                 :             : 
     382         [ #  # ]:           0 :         switch (parse->state)
     383                 :             :         {
     384                 :             :                 case JM_EXPECT_FILES_NEXT:
     385                 :             :                 case JM_EXPECT_WAL_RANGES_NEXT:
     386                 :           0 :                         parse->state = JM_EXPECT_TOPLEVEL_FIELD;
     387                 :           0 :                         break;
     388                 :             :                 default:
     389                 :           0 :                         json_manifest_parse_failure(parse->context,
     390                 :             :                                                                                 "unexpected array end");
     391                 :             :                         break;
     392                 :             :         }
     393                 :             : 
     394                 :           0 :         return JSON_SUCCESS;
     395                 :           0 : }
     396                 :             : 
     397                 :             : /*
     398                 :             :  * Invoked at the start of each object field in the JSON document.
     399                 :             :  */
     400                 :             : static JsonParseErrorType
     401                 :           0 : json_manifest_object_field_start(void *state, char *fname, bool isnull)
     402                 :             : {
     403                 :           0 :         JsonManifestParseState *parse = state;
     404                 :             : 
     405   [ #  #  #  # ]:           0 :         switch (parse->state)
     406                 :             :         {
     407                 :             :                 case JM_EXPECT_TOPLEVEL_FIELD:
     408                 :             : 
     409                 :             :                         /*
     410                 :             :                          * Inside toplevel object. The version indicator should always be
     411                 :             :                          * the first field.
     412                 :             :                          */
     413         [ #  # ]:           0 :                         if (!parse->saw_version_field)
     414                 :             :                         {
     415         [ #  # ]:           0 :                                 if (strcmp(fname, "PostgreSQL-Backup-Manifest-Version") != 0)
     416                 :           0 :                                         json_manifest_parse_failure(parse->context,
     417                 :             :                                                                                                 "expected version indicator");
     418                 :           0 :                                 parse->state = JM_EXPECT_VERSION_VALUE;
     419                 :           0 :                                 parse->saw_version_field = true;
     420                 :           0 :                                 break;
     421                 :             :                         }
     422                 :             : 
     423                 :             :                         /* Is this the system identifier? */
     424         [ #  # ]:           0 :                         if (strcmp(fname, "System-Identifier") == 0)
     425                 :             :                         {
     426                 :           0 :                                 parse->state = JM_EXPECT_SYSTEM_IDENTIFIER_VALUE;
     427                 :           0 :                                 break;
     428                 :             :                         }
     429                 :             : 
     430                 :             :                         /* Is this the list of files? */
     431         [ #  # ]:           0 :                         if (strcmp(fname, "Files") == 0)
     432                 :             :                         {
     433                 :           0 :                                 parse->state = JM_EXPECT_FILES_START;
     434                 :           0 :                                 break;
     435                 :             :                         }
     436                 :             : 
     437                 :             :                         /* Is this the list of WAL ranges? */
     438         [ #  # ]:           0 :                         if (strcmp(fname, "WAL-Ranges") == 0)
     439                 :             :                         {
     440                 :           0 :                                 parse->state = JM_EXPECT_WAL_RANGES_START;
     441                 :           0 :                                 break;
     442                 :             :                         }
     443                 :             : 
     444                 :             :                         /* Is this the manifest checksum? */
     445         [ #  # ]:           0 :                         if (strcmp(fname, "Manifest-Checksum") == 0)
     446                 :             :                         {
     447                 :           0 :                                 parse->state = JM_EXPECT_MANIFEST_CHECKSUM_VALUE;
     448                 :           0 :                                 break;
     449                 :             :                         }
     450                 :             : 
     451                 :             :                         /* It's not a field we recognize. */
     452                 :           0 :                         json_manifest_parse_failure(parse->context,
     453                 :             :                                                                                 "unrecognized top-level field");
     454                 :             :                         break;
     455                 :             : 
     456                 :             :                 case JM_EXPECT_THIS_FILE_FIELD:
     457                 :             :                         /* Inside object for one file; which key have we got? */
     458         [ #  # ]:           0 :                         if (strcmp(fname, "Path") == 0)
     459                 :           0 :                                 parse->file_field = JMFF_PATH;
     460         [ #  # ]:           0 :                         else if (strcmp(fname, "Encoded-Path") == 0)
     461                 :           0 :                                 parse->file_field = JMFF_ENCODED_PATH;
     462         [ #  # ]:           0 :                         else if (strcmp(fname, "Size") == 0)
     463                 :           0 :                                 parse->file_field = JMFF_SIZE;
     464         [ #  # ]:           0 :                         else if (strcmp(fname, "Last-Modified") == 0)
     465                 :           0 :                                 parse->file_field = JMFF_LAST_MODIFIED;
     466         [ #  # ]:           0 :                         else if (strcmp(fname, "Checksum-Algorithm") == 0)
     467                 :           0 :                                 parse->file_field = JMFF_CHECKSUM_ALGORITHM;
     468         [ #  # ]:           0 :                         else if (strcmp(fname, "Checksum") == 0)
     469                 :           0 :                                 parse->file_field = JMFF_CHECKSUM;
     470                 :             :                         else
     471                 :           0 :                                 json_manifest_parse_failure(parse->context,
     472                 :             :                                                                                         "unexpected file field");
     473                 :           0 :                         parse->state = JM_EXPECT_THIS_FILE_VALUE;
     474                 :           0 :                         break;
     475                 :             : 
     476                 :             :                 case JM_EXPECT_THIS_WAL_RANGE_FIELD:
     477                 :             :                         /* Inside object for one file; which key have we got? */
     478         [ #  # ]:           0 :                         if (strcmp(fname, "Timeline") == 0)
     479                 :           0 :                                 parse->wal_range_field = JMWRF_TIMELINE;
     480         [ #  # ]:           0 :                         else if (strcmp(fname, "Start-LSN") == 0)
     481                 :           0 :                                 parse->wal_range_field = JMWRF_START_LSN;
     482         [ #  # ]:           0 :                         else if (strcmp(fname, "End-LSN") == 0)
     483                 :           0 :                                 parse->wal_range_field = JMWRF_END_LSN;
     484                 :             :                         else
     485                 :           0 :                                 json_manifest_parse_failure(parse->context,
     486                 :             :                                                                                         "unexpected WAL range field");
     487                 :           0 :                         parse->state = JM_EXPECT_THIS_WAL_RANGE_VALUE;
     488                 :           0 :                         break;
     489                 :             : 
     490                 :             :                 default:
     491                 :           0 :                         json_manifest_parse_failure(parse->context,
     492                 :             :                                                                                 "unexpected object field");
     493                 :             :                         break;
     494                 :             :         }
     495                 :             : 
     496                 :           0 :         pfree(fname);
     497                 :             : 
     498                 :           0 :         return JSON_SUCCESS;
     499                 :           0 : }
     500                 :             : 
     501                 :             : /*
     502                 :             :  * Invoked at the start of each scalar in the JSON document.
     503                 :             :  *
     504                 :             :  * Object field names don't reach this code; those are handled by
     505                 :             :  * json_manifest_object_field_start. When we're inside of the object for
     506                 :             :  * a particular file or WAL range, that function will have noticed the name
     507                 :             :  * of the field, and we'll get the corresponding value here. When we're in
     508                 :             :  * the toplevel object, the parse state itself tells us which field this is.
     509                 :             :  *
     510                 :             :  * In all cases except for PostgreSQL-Backup-Manifest-Version, which we
     511                 :             :  * can just check on the spot, the goal here is just to save the value in
     512                 :             :  * the parse state for later use. We don't actually do anything until we
     513                 :             :  * reach either the end of the object representing this file, or the end
     514                 :             :  * of the manifest, as the case may be.
     515                 :             :  */
     516                 :             : static JsonParseErrorType
     517                 :           0 : json_manifest_scalar(void *state, char *token, JsonTokenType tokentype)
     518                 :             : {
     519                 :           0 :         JsonManifestParseState *parse = state;
     520                 :             : 
     521   [ #  #  #  #  :           0 :         switch (parse->state)
                   #  # ]
     522                 :             :         {
     523                 :             :                 case JM_EXPECT_VERSION_VALUE:
     524                 :           0 :                         parse->manifest_version = token;
     525                 :           0 :                         json_manifest_finalize_version(parse);
     526                 :           0 :                         parse->state = JM_EXPECT_TOPLEVEL_FIELD;
     527                 :           0 :                         break;
     528                 :             : 
     529                 :             :                 case JM_EXPECT_SYSTEM_IDENTIFIER_VALUE:
     530                 :           0 :                         parse->manifest_system_identifier = token;
     531                 :           0 :                         json_manifest_finalize_system_identifier(parse);
     532                 :           0 :                         parse->state = JM_EXPECT_TOPLEVEL_FIELD;
     533                 :           0 :                         break;
     534                 :             : 
     535                 :             :                 case JM_EXPECT_THIS_FILE_VALUE:
     536   [ #  #  #  #  :           0 :                         switch (parse->file_field)
                #  #  # ]
     537                 :             :                         {
     538                 :             :                                 case JMFF_PATH:
     539                 :           0 :                                         parse->pathname = token;
     540                 :           0 :                                         break;
     541                 :             :                                 case JMFF_ENCODED_PATH:
     542                 :           0 :                                         parse->encoded_pathname = token;
     543                 :           0 :                                         break;
     544                 :             :                                 case JMFF_SIZE:
     545                 :           0 :                                         parse->size = token;
     546                 :           0 :                                         break;
     547                 :             :                                 case JMFF_LAST_MODIFIED:
     548                 :           0 :                                         pfree(token);   /* unused */
     549                 :           0 :                                         break;
     550                 :             :                                 case JMFF_CHECKSUM_ALGORITHM:
     551                 :           0 :                                         parse->algorithm = token;
     552                 :           0 :                                         break;
     553                 :             :                                 case JMFF_CHECKSUM:
     554                 :           0 :                                         parse->checksum = token;
     555                 :           0 :                                         break;
     556                 :             :                         }
     557                 :           0 :                         parse->state = JM_EXPECT_THIS_FILE_FIELD;
     558                 :           0 :                         break;
     559                 :             : 
     560                 :             :                 case JM_EXPECT_THIS_WAL_RANGE_VALUE:
     561   [ #  #  #  # ]:           0 :                         switch (parse->wal_range_field)
     562                 :             :                         {
     563                 :             :                                 case JMWRF_TIMELINE:
     564                 :           0 :                                         parse->timeline = token;
     565                 :           0 :                                         break;
     566                 :             :                                 case JMWRF_START_LSN:
     567                 :           0 :                                         parse->start_lsn = token;
     568                 :           0 :                                         break;
     569                 :             :                                 case JMWRF_END_LSN:
     570                 :           0 :                                         parse->end_lsn = token;
     571                 :           0 :                                         break;
     572                 :             :                         }
     573                 :           0 :                         parse->state = JM_EXPECT_THIS_WAL_RANGE_FIELD;
     574                 :           0 :                         break;
     575                 :             : 
     576                 :             :                 case JM_EXPECT_MANIFEST_CHECKSUM_VALUE:
     577                 :           0 :                         parse->state = JM_EXPECT_TOPLEVEL_END;
     578                 :           0 :                         parse->manifest_checksum = token;
     579                 :           0 :                         break;
     580                 :             : 
     581                 :             :                 default:
     582                 :           0 :                         json_manifest_parse_failure(parse->context, "unexpected scalar");
     583                 :             :                         break;
     584                 :             :         }
     585                 :             : 
     586                 :           0 :         return JSON_SUCCESS;
     587                 :           0 : }
     588                 :             : 
     589                 :             : /*
     590                 :             :  * Do additional parsing and sanity-checking of the manifest version, and invoke
     591                 :             :  * the callback so that the caller can gets that detail and take actions
     592                 :             :  * accordingly.  This happens for each manifest when the corresponding JSON
     593                 :             :  * object is completely parsed.
     594                 :             :  */
     595                 :             : static void
     596                 :           0 : json_manifest_finalize_version(JsonManifestParseState *parse)
     597                 :             : {
     598                 :           0 :         JsonManifestParseContext *context = parse->context;
     599                 :           0 :         int                     version;
     600                 :           0 :         char       *ep;
     601                 :             : 
     602         [ #  # ]:           0 :         Assert(parse->saw_version_field);
     603                 :             : 
     604                 :             :         /* Parse version. */
     605                 :           0 :         version = strtoi64(parse->manifest_version, &ep, 10);
     606         [ #  # ]:           0 :         if (*ep)
     607                 :           0 :                 json_manifest_parse_failure(parse->context,
     608                 :             :                                                                         "manifest version not an integer");
     609                 :             : 
     610   [ #  #  #  # ]:           0 :         if (version != 1 && version != 2)
     611                 :           0 :                 json_manifest_parse_failure(parse->context,
     612                 :             :                                                                         "unexpected manifest version");
     613                 :             : 
     614                 :             :         /* Invoke the callback for version */
     615                 :           0 :         context->version_cb(context, version);
     616                 :           0 : }
     617                 :             : 
     618                 :             : /*
     619                 :             :  * Do additional parsing and sanity-checking of the system identifier, and
     620                 :             :  * invoke the callback so that the caller can gets that detail and take actions
     621                 :             :  * accordingly.
     622                 :             :  */
     623                 :             : static void
     624                 :           0 : json_manifest_finalize_system_identifier(JsonManifestParseState *parse)
     625                 :             : {
     626                 :           0 :         JsonManifestParseContext *context = parse->context;
     627                 :           0 :         uint64          system_identifier;
     628                 :           0 :         char       *ep;
     629                 :             : 
     630         [ #  # ]:           0 :         Assert(parse->manifest_system_identifier != NULL);
     631                 :             : 
     632                 :             :         /* Parse system identifier. */
     633                 :           0 :         system_identifier = strtou64(parse->manifest_system_identifier, &ep, 10);
     634         [ #  # ]:           0 :         if (*ep)
     635                 :           0 :                 json_manifest_parse_failure(parse->context,
     636                 :             :                                                                         "system identifier in manifest not an integer");
     637                 :             : 
     638                 :             :         /* Invoke the callback for system identifier */
     639                 :           0 :         context->system_identifier_cb(context, system_identifier);
     640                 :           0 : }
     641                 :             : 
     642                 :             : /*
     643                 :             :  * Do additional parsing and sanity-checking of the details gathered for one
     644                 :             :  * file, and invoke the per-file callback so that the caller gets those
     645                 :             :  * details. This happens for each file when the corresponding JSON object is
     646                 :             :  * completely parsed.
     647                 :             :  */
     648                 :             : static void
     649                 :           0 : json_manifest_finalize_file(JsonManifestParseState *parse)
     650                 :             : {
     651                 :           0 :         JsonManifestParseContext *context = parse->context;
     652                 :           0 :         uint64          size;
     653                 :           0 :         char       *ep;
     654                 :           0 :         int                     checksum_string_length;
     655                 :           0 :         pg_checksum_type checksum_type;
     656                 :           0 :         int                     checksum_length;
     657                 :           0 :         uint8      *checksum_payload;
     658                 :             : 
     659                 :             :         /* Pathname and size are required. */
     660   [ #  #  #  # ]:           0 :         if (parse->pathname == NULL && parse->encoded_pathname == NULL)
     661                 :           0 :                 json_manifest_parse_failure(parse->context, "missing path name");
     662   [ #  #  #  # ]:           0 :         if (parse->pathname != NULL && parse->encoded_pathname != NULL)
     663                 :           0 :                 json_manifest_parse_failure(parse->context,
     664                 :             :                                                                         "both path name and encoded path name");
     665         [ #  # ]:           0 :         if (parse->size == NULL)
     666                 :           0 :                 json_manifest_parse_failure(parse->context, "missing size");
     667   [ #  #  #  # ]:           0 :         if (parse->algorithm == NULL && parse->checksum != NULL)
     668                 :           0 :                 json_manifest_parse_failure(parse->context,
     669                 :             :                                                                         "checksum without algorithm");
     670                 :             : 
     671                 :             :         /* Decode encoded pathname, if that's what we have. */
     672         [ #  # ]:           0 :         if (parse->encoded_pathname != NULL)
     673                 :             :         {
     674                 :           0 :                 int                     encoded_length = strlen(parse->encoded_pathname);
     675                 :           0 :                 int                     raw_length = encoded_length / 2;
     676                 :             : 
     677                 :           0 :                 parse->pathname = palloc(raw_length + 1);
     678         [ #  # ]:           0 :                 if (encoded_length % 2 != 0 ||
     679                 :           0 :                         !hexdecode_string((uint8 *) parse->pathname,
     680                 :           0 :                                                           parse->encoded_pathname,
     681                 :           0 :                                                           raw_length))
     682                 :           0 :                         json_manifest_parse_failure(parse->context,
     683                 :             :                                                                                 "could not decode file name");
     684                 :           0 :                 parse->pathname[raw_length] = '\0';
     685                 :           0 :                 pfree(parse->encoded_pathname);
     686                 :           0 :                 parse->encoded_pathname = NULL;
     687                 :           0 :         }
     688                 :             : 
     689                 :             :         /* Parse size. */
     690                 :           0 :         size = strtou64(parse->size, &ep, 10);
     691         [ #  # ]:           0 :         if (*ep)
     692                 :           0 :                 json_manifest_parse_failure(parse->context,
     693                 :             :                                                                         "file size is not an integer");
     694                 :             : 
     695                 :             :         /* Parse the checksum algorithm, if it's present. */
     696         [ #  # ]:           0 :         if (parse->algorithm == NULL)
     697                 :           0 :                 checksum_type = CHECKSUM_TYPE_NONE;
     698         [ #  # ]:           0 :         else if (!pg_checksum_parse_type(parse->algorithm, &checksum_type))
     699                 :           0 :                 context->error_cb(context, "unrecognized checksum algorithm: \"%s\"",
     700                 :           0 :                                                   parse->algorithm);
     701                 :             : 
     702                 :             :         /* Parse the checksum payload, if it's present. */
     703         [ #  # ]:           0 :         checksum_string_length = parse->checksum == NULL ? 0
     704                 :           0 :                 : strlen(parse->checksum);
     705         [ #  # ]:           0 :         if (checksum_string_length == 0)
     706                 :             :         {
     707                 :           0 :                 checksum_length = 0;
     708                 :           0 :                 checksum_payload = NULL;
     709                 :           0 :         }
     710                 :             :         else
     711                 :             :         {
     712                 :           0 :                 checksum_length = checksum_string_length / 2;
     713                 :           0 :                 checksum_payload = palloc(checksum_length);
     714   [ #  #  #  # ]:           0 :                 if (checksum_string_length % 2 != 0 ||
     715                 :           0 :                         !hexdecode_string(checksum_payload, parse->checksum,
     716                 :           0 :                                                           checksum_length))
     717                 :           0 :                         context->error_cb(context,
     718                 :             :                                                           "invalid checksum for file \"%s\": \"%s\"",
     719                 :           0 :                                                           parse->pathname, parse->checksum);
     720                 :             :         }
     721                 :             : 
     722                 :             :         /* Invoke the callback with the details we've gathered. */
     723                 :           0 :         context->per_file_cb(context, parse->pathname, size,
     724                 :           0 :                                                  checksum_type, checksum_length, checksum_payload);
     725                 :             : 
     726                 :             :         /* Free memory we no longer need. */
     727         [ #  # ]:           0 :         if (parse->size != NULL)
     728                 :             :         {
     729                 :           0 :                 pfree(parse->size);
     730                 :           0 :                 parse->size = NULL;
     731                 :           0 :         }
     732         [ #  # ]:           0 :         if (parse->algorithm != NULL)
     733                 :             :         {
     734                 :           0 :                 pfree(parse->algorithm);
     735                 :           0 :                 parse->algorithm = NULL;
     736                 :           0 :         }
     737         [ #  # ]:           0 :         if (parse->checksum != NULL)
     738                 :             :         {
     739                 :           0 :                 pfree(parse->checksum);
     740                 :           0 :                 parse->checksum = NULL;
     741                 :           0 :         }
     742                 :           0 : }
     743                 :             : 
     744                 :             : /*
     745                 :             :  * Do additional parsing and sanity-checking of the details gathered for one
     746                 :             :  * WAL range, and invoke the per-WAL-range callback so that the caller gets
     747                 :             :  * those details. This happens for each WAL range when the corresponding JSON
     748                 :             :  * object is completely parsed.
     749                 :             :  */
     750                 :             : static void
     751                 :           0 : json_manifest_finalize_wal_range(JsonManifestParseState *parse)
     752                 :             : {
     753                 :           0 :         JsonManifestParseContext *context = parse->context;
     754                 :           0 :         TimeLineID      tli;
     755                 :           0 :         XLogRecPtr      start_lsn,
     756                 :             :                                 end_lsn;
     757                 :           0 :         char       *ep;
     758                 :             : 
     759                 :             :         /* Make sure all fields are present. */
     760         [ #  # ]:           0 :         if (parse->timeline == NULL)
     761                 :           0 :                 json_manifest_parse_failure(parse->context, "missing timeline");
     762         [ #  # ]:           0 :         if (parse->start_lsn == NULL)
     763                 :           0 :                 json_manifest_parse_failure(parse->context, "missing start LSN");
     764         [ #  # ]:           0 :         if (parse->end_lsn == NULL)
     765                 :           0 :                 json_manifest_parse_failure(parse->context, "missing end LSN");
     766                 :             : 
     767                 :             :         /* Parse timeline. */
     768                 :           0 :         tli = strtoul(parse->timeline, &ep, 10);
     769         [ #  # ]:           0 :         if (*ep)
     770                 :           0 :                 json_manifest_parse_failure(parse->context,
     771                 :             :                                                                         "timeline is not an integer");
     772         [ #  # ]:           0 :         if (!parse_xlogrecptr(&start_lsn, parse->start_lsn))
     773                 :           0 :                 json_manifest_parse_failure(parse->context,
     774                 :             :                                                                         "could not parse start LSN");
     775         [ #  # ]:           0 :         if (!parse_xlogrecptr(&end_lsn, parse->end_lsn))
     776                 :           0 :                 json_manifest_parse_failure(parse->context,
     777                 :             :                                                                         "could not parse end LSN");
     778                 :             : 
     779                 :             :         /* Invoke the callback with the details we've gathered. */
     780                 :           0 :         context->per_wal_range_cb(context, tli, start_lsn, end_lsn);
     781                 :             : 
     782                 :             :         /* Free memory we no longer need. */
     783         [ #  # ]:           0 :         if (parse->timeline != NULL)
     784                 :             :         {
     785                 :           0 :                 pfree(parse->timeline);
     786                 :           0 :                 parse->timeline = NULL;
     787                 :           0 :         }
     788         [ #  # ]:           0 :         if (parse->start_lsn != NULL)
     789                 :             :         {
     790                 :           0 :                 pfree(parse->start_lsn);
     791                 :           0 :                 parse->start_lsn = NULL;
     792                 :           0 :         }
     793         [ #  # ]:           0 :         if (parse->end_lsn != NULL)
     794                 :             :         {
     795                 :           0 :                 pfree(parse->end_lsn);
     796                 :           0 :                 parse->end_lsn = NULL;
     797                 :           0 :         }
     798                 :           0 : }
     799                 :             : 
     800                 :             : /*
     801                 :             :  * Verify that the manifest checksum is correct.
     802                 :             :  *
     803                 :             :  * The last line of the manifest file is excluded from the manifest checksum,
     804                 :             :  * because the last line is expected to contain the checksum that covers
     805                 :             :  * the rest of the file.
     806                 :             :  *
     807                 :             :  * For an incremental parse, this will just be called on the last chunk of the
     808                 :             :  * manifest, and the cryptohash context passed in. For a non-incremental
     809                 :             :  * parse incr_ctx will be NULL.
     810                 :             :  */
     811                 :             : static void
     812                 :           0 : verify_manifest_checksum(JsonManifestParseState *parse, const char *buffer,
     813                 :             :                                                  size_t size, pg_cryptohash_ctx *incr_ctx)
     814                 :             : {
     815                 :           0 :         JsonManifestParseContext *context = parse->context;
     816                 :           0 :         size_t          i;
     817                 :           0 :         size_t          number_of_newlines = 0;
     818                 :           0 :         size_t          ultimate_newline = 0;
     819                 :           0 :         size_t          penultimate_newline = 0;
     820                 :           0 :         pg_cryptohash_ctx *manifest_ctx;
     821                 :           0 :         uint8           manifest_checksum_actual[PG_SHA256_DIGEST_LENGTH];
     822                 :           0 :         uint8           manifest_checksum_expected[PG_SHA256_DIGEST_LENGTH];
     823                 :             : 
     824                 :             :         /* Find the last two newlines in the file. */
     825         [ #  # ]:           0 :         for (i = 0; i < size; ++i)
     826                 :             :         {
     827         [ #  # ]:           0 :                 if (buffer[i] == '\n')
     828                 :             :                 {
     829                 :           0 :                         ++number_of_newlines;
     830                 :           0 :                         penultimate_newline = ultimate_newline;
     831                 :           0 :                         ultimate_newline = i;
     832                 :           0 :                 }
     833                 :           0 :         }
     834                 :             : 
     835                 :             :         /*
     836                 :             :          * Make sure that the last newline is right at the end, and that there are
     837                 :             :          * at least two lines total. We need this to be true in order for the
     838                 :             :          * following code, which computes the manifest checksum, to work properly.
     839                 :             :          */
     840         [ #  # ]:           0 :         if (number_of_newlines < 2)
     841                 :           0 :                 json_manifest_parse_failure(parse->context,
     842                 :             :                                                                         "expected at least 2 lines");
     843         [ #  # ]:           0 :         if (ultimate_newline != size - 1)
     844                 :           0 :                 json_manifest_parse_failure(parse->context,
     845                 :             :                                                                         "last line not newline-terminated");
     846                 :             : 
     847                 :             :         /* Checksum the rest. */
     848         [ #  # ]:           0 :         if (incr_ctx == NULL)
     849                 :             :         {
     850                 :           0 :                 manifest_ctx = pg_cryptohash_create(PG_SHA256);
     851         [ #  # ]:           0 :                 if (manifest_ctx == NULL)
     852                 :           0 :                         context->error_cb(context, "out of memory");
     853         [ #  # ]:           0 :                 if (pg_cryptohash_init(manifest_ctx) < 0)
     854                 :           0 :                         context->error_cb(context, "could not initialize checksum of manifest");
     855                 :           0 :         }
     856                 :             :         else
     857                 :             :         {
     858                 :           0 :                 manifest_ctx = incr_ctx;
     859                 :             :         }
     860         [ #  # ]:           0 :         if (pg_cryptohash_update(manifest_ctx, (const uint8 *) buffer, penultimate_newline + 1) < 0)
     861                 :           0 :                 context->error_cb(context, "could not update checksum of manifest");
     862                 :           0 :         if (pg_cryptohash_final(manifest_ctx, manifest_checksum_actual,
     863         [ #  # ]:           0 :                                                         sizeof(manifest_checksum_actual)) < 0)
     864                 :           0 :                 context->error_cb(context, "could not finalize checksum of manifest");
     865                 :             : 
     866                 :             :         /* Now verify it. */
     867         [ #  # ]:           0 :         if (parse->manifest_checksum == NULL)
     868                 :           0 :                 context->error_cb(parse->context, "manifest has no checksum");
     869   [ #  #  #  # ]:           0 :         if (strlen(parse->manifest_checksum) != PG_SHA256_DIGEST_LENGTH * 2 ||
     870                 :           0 :                 !hexdecode_string(manifest_checksum_expected, parse->manifest_checksum,
     871                 :             :                                                   PG_SHA256_DIGEST_LENGTH))
     872                 :           0 :                 context->error_cb(context, "invalid manifest checksum: \"%s\"",
     873                 :           0 :                                                   parse->manifest_checksum);
     874                 :           0 :         if (memcmp(manifest_checksum_actual, manifest_checksum_expected,
     875         [ #  # ]:           0 :                            PG_SHA256_DIGEST_LENGTH) != 0)
     876                 :           0 :                 context->error_cb(context, "manifest checksum mismatch");
     877                 :           0 :         pg_cryptohash_free(manifest_ctx);
     878                 :           0 : }
     879                 :             : 
     880                 :             : /*
     881                 :             :  * Report a parse error.
     882                 :             :  *
     883                 :             :  * This is intended to be used for fairly low-level failures that probably
     884                 :             :  * shouldn't occur unless somebody has deliberately constructed a bad manifest,
     885                 :             :  * or unless the server is generating bad manifests due to some bug. msg should
     886                 :             :  * be a short string giving some hint as to what the problem is.
     887                 :             :  */
     888                 :             : static void
     889                 :           0 : json_manifest_parse_failure(JsonManifestParseContext *context, char *msg)
     890                 :             : {
     891                 :           0 :         context->error_cb(context, "could not parse backup manifest: %s", msg);
     892                 :           0 :         pg_unreachable();
     893                 :             : }
     894                 :             : 
     895                 :             : /*
     896                 :             :  * Convert a character which represents a hexadecimal digit to an integer.
     897                 :             :  *
     898                 :             :  * Returns -1 if the character is not a hexadecimal digit.
     899                 :             :  */
     900                 :             : static int
     901                 :           0 : hexdecode_char(char c)
     902                 :             : {
     903   [ #  #  #  # ]:           0 :         if (c >= '0' && c <= '9')
     904                 :           0 :                 return c - '0';
     905   [ #  #  #  # ]:           0 :         if (c >= 'a' && c <= 'f')
     906                 :           0 :                 return c - 'a' + 10;
     907   [ #  #  #  # ]:           0 :         if (c >= 'A' && c <= 'F')
     908                 :           0 :                 return c - 'A' + 10;
     909                 :             : 
     910                 :           0 :         return -1;
     911                 :           0 : }
     912                 :             : 
     913                 :             : /*
     914                 :             :  * Decode a hex string into a byte string, 2 hex chars per byte.
     915                 :             :  *
     916                 :             :  * Returns false if invalid characters are encountered; otherwise true.
     917                 :             :  */
     918                 :             : static bool
     919                 :           0 : hexdecode_string(uint8 *result, char *input, int nbytes)
     920                 :             : {
     921                 :           0 :         int                     i;
     922                 :             : 
     923         [ #  # ]:           0 :         for (i = 0; i < nbytes; ++i)
     924                 :             :         {
     925                 :           0 :                 int                     n1 = hexdecode_char(input[i * 2]);
     926                 :           0 :                 int                     n2 = hexdecode_char(input[i * 2 + 1]);
     927                 :             : 
     928   [ #  #  #  # ]:           0 :                 if (n1 < 0 || n2 < 0)
     929                 :           0 :                         return false;
     930                 :           0 :                 result[i] = n1 * 16 + n2;
     931         [ #  # ]:           0 :         }
     932                 :             : 
     933                 :           0 :         return true;
     934                 :           0 : }
     935                 :             : 
     936                 :             : /*
     937                 :             :  * Parse an XLogRecPtr expressed using the usual string format.
     938                 :             :  */
     939                 :             : static bool
     940                 :           0 : parse_xlogrecptr(XLogRecPtr *result, char *input)
     941                 :             : {
     942                 :           0 :         uint32          hi;
     943                 :           0 :         uint32          lo;
     944                 :             : 
     945         [ #  # ]:           0 :         if (sscanf(input, "%X/%08X", &hi, &lo) != 2)
     946                 :           0 :                 return false;
     947                 :           0 :         *result = ((uint64) hi) << 32 | lo;
     948                 :           0 :         return true;
     949                 :           0 : }
        

Generated by: LCOV version 2.3.2-1