LCOV - differential code coverage report
Current view: top level - src/bin/pg_combinebackup - backup_label.c (source / functions) Coverage Total Hit UNC GNC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 79.2 % 101 80 21 80
Current Date: 2024-04-14 14:21:10 Functions: 100.0 % 6 6 6
Baseline: 16@8cea358b128 Branches: 68.3 % 82 56 26 56
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (60,120] days: 79.2 % 101 80 21 80
Function coverage date bins:
(60,120] days: 100.0 % 6 6 6
Branch coverage date bins:
(60,120] days: 68.3 % 82 56 26 56

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * Read and manipulate backup label files
                                  4                 :                :  *
                                  5                 :                :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
                                  6                 :                :  * Portions Copyright (c) 1994, Regents of the University of California
                                  7                 :                :  *
                                  8                 :                :  * src/bin/pg_combinebackup/backup_label.c
                                  9                 :                :  *
                                 10                 :                :  *-------------------------------------------------------------------------
                                 11                 :                :  */
                                 12                 :                : #include "postgres_fe.h"
                                 13                 :                : 
                                 14                 :                : #include <unistd.h>
                                 15                 :                : 
                                 16                 :                : #include "access/xlogdefs.h"
                                 17                 :                : #include "backup_label.h"
                                 18                 :                : #include "common/file_perm.h"
                                 19                 :                : #include "common/logging.h"
                                 20                 :                : #include "write_manifest.h"
                                 21                 :                : 
                                 22                 :                : static int  get_eol_offset(StringInfo buf);
                                 23                 :                : static bool line_starts_with(char *s, char *e, char *match, char **sout);
                                 24                 :                : static bool parse_lsn(char *s, char *e, XLogRecPtr *lsn, char **c);
                                 25                 :                : static bool parse_tli(char *s, char *e, TimeLineID *tli);
                                 26                 :                : 
                                 27                 :                : /*
                                 28                 :                :  * Parse a backup label file, starting at buf->cursor.
                                 29                 :                :  *
                                 30                 :                :  * We expect to find a START WAL LOCATION line, followed by a LSN, followed
                                 31                 :                :  * by a space; the resulting LSN is stored into *start_lsn.
                                 32                 :                :  *
                                 33                 :                :  * We expect to find a START TIMELINE line, followed by a TLI, followed by
                                 34                 :                :  * a newline; the resulting TLI is stored into *start_tli.
                                 35                 :                :  *
                                 36                 :                :  * We expect to find either both INCREMENTAL FROM LSN and INCREMENTAL FROM TLI
                                 37                 :                :  * or neither. If these are found, they should be followed by an LSN or TLI
                                 38                 :                :  * respectively and then by a newline, and the values will be stored into
                                 39                 :                :  * *previous_lsn and *previous_tli, respectively.
                                 40                 :                :  *
                                 41                 :                :  * Other lines in the provided backup_label data are ignored. filename is used
                                 42                 :                :  * for error reporting; errors are fatal.
                                 43                 :                :  */
                                 44                 :                : void
  116 rhaas@postgresql.org       45                 :GNC          31 : parse_backup_label(char *filename, StringInfo buf,
                                 46                 :                :                    TimeLineID *start_tli, XLogRecPtr *start_lsn,
                                 47                 :                :                    TimeLineID *previous_tli, XLogRecPtr *previous_lsn)
                                 48                 :                : {
                                 49                 :             31 :     int         found = 0;
                                 50                 :                : 
                                 51                 :             31 :     *start_tli = 0;
                                 52                 :             31 :     *start_lsn = InvalidXLogRecPtr;
                                 53                 :             31 :     *previous_tli = 0;
                                 54                 :             31 :     *previous_lsn = InvalidXLogRecPtr;
                                 55                 :                : 
                                 56         [ +  + ]:            282 :     while (buf->cursor < buf->len)
                                 57                 :                :     {
                                 58                 :            251 :         char       *s = &buf->data[buf->cursor];
                                 59                 :            251 :         int         eo = get_eol_offset(buf);
                                 60                 :            251 :         char       *e = &buf->data[eo];
                                 61                 :                :         char       *c;
                                 62                 :                : 
                                 63         [ +  + ]:            251 :         if (line_starts_with(s, e, "START WAL LOCATION: ", &s))
                                 64                 :                :         {
                                 65         [ -  + ]:             31 :             if (!parse_lsn(s, e, start_lsn, &c))
  116 rhaas@postgresql.org       66                 :UNC           0 :                 pg_fatal("%s: could not parse %s",
                                 67                 :                :                          filename, "START WAL LOCATION");
  116 rhaas@postgresql.org       68   [ +  -  -  + ]:GNC          31 :             if (c >= e || *c != ' ')
  116 rhaas@postgresql.org       69                 :UNC           0 :                 pg_fatal("%s: improper terminator for %s",
                                 70                 :                :                          filename, "START WAL LOCATION");
  116 rhaas@postgresql.org       71                 :GNC          31 :             found |= 1;
                                 72                 :                :         }
                                 73         [ +  + ]:            220 :         else if (line_starts_with(s, e, "START TIMELINE: ", &s))
                                 74                 :                :         {
                                 75         [ -  + ]:             31 :             if (!parse_tli(s, e, start_tli))
  116 rhaas@postgresql.org       76                 :UNC           0 :                 pg_fatal("%s: could not parse TLI for %s",
                                 77                 :                :                          filename, "START TIMELINE");
  116 rhaas@postgresql.org       78         [ -  + ]:GNC          31 :             if (*start_tli == 0)
  116 rhaas@postgresql.org       79                 :UNC           0 :                 pg_fatal("%s: invalid TLI", filename);
  116 rhaas@postgresql.org       80                 :GNC          31 :             found |= 2;
                                 81                 :                :         }
                                 82         [ +  + ]:            189 :         else if (line_starts_with(s, e, "INCREMENTAL FROM LSN: ", &s))
                                 83                 :                :         {
                                 84         [ -  + ]:             17 :             if (!parse_lsn(s, e, previous_lsn, &c))
  116 rhaas@postgresql.org       85                 :UNC           0 :                 pg_fatal("%s: could not parse %s",
                                 86                 :                :                          filename, "INCREMENTAL FROM LSN");
  116 rhaas@postgresql.org       87   [ +  -  -  + ]:GNC          17 :             if (c >= e || *c != '\n')
  116 rhaas@postgresql.org       88                 :UNC           0 :                 pg_fatal("%s: improper terminator for %s",
                                 89                 :                :                          filename, "INCREMENTAL FROM LSN");
  116 rhaas@postgresql.org       90                 :GNC          17 :             found |= 4;
                                 91                 :                :         }
                                 92         [ +  + ]:            172 :         else if (line_starts_with(s, e, "INCREMENTAL FROM TLI: ", &s))
                                 93                 :                :         {
                                 94         [ -  + ]:             17 :             if (!parse_tli(s, e, previous_tli))
  116 rhaas@postgresql.org       95                 :UNC           0 :                 pg_fatal("%s: could not parse %s",
                                 96                 :                :                          filename, "INCREMENTAL FROM TLI");
  116 rhaas@postgresql.org       97         [ -  + ]:GNC          17 :             if (*previous_tli == 0)
  116 rhaas@postgresql.org       98                 :UNC           0 :                 pg_fatal("%s: invalid TLI", filename);
  116 rhaas@postgresql.org       99                 :GNC          17 :             found |= 8;
                                100                 :                :         }
                                101                 :                : 
                                102                 :            251 :         buf->cursor = eo;
                                103                 :                :     }
                                104                 :                : 
                                105         [ -  + ]:             31 :     if ((found & 1) == 0)
  116 rhaas@postgresql.org      106                 :UNC           0 :         pg_fatal("%s: could not find %s", filename, "START WAL LOCATION");
  116 rhaas@postgresql.org      107         [ -  + ]:GNC          31 :     if ((found & 2) == 0)
  116 rhaas@postgresql.org      108                 :UNC           0 :         pg_fatal("%s: could not find %s", filename, "START TIMELINE");
  116 rhaas@postgresql.org      109   [ +  +  -  + ]:GNC          31 :     if ((found & 4) != 0 && (found & 8) == 0)
  116 rhaas@postgresql.org      110                 :UNC           0 :         pg_fatal("%s: %s requires %s", filename,
                                111                 :                :                  "INCREMENTAL FROM LSN", "INCREMENTAL FROM TLI");
  116 rhaas@postgresql.org      112   [ +  +  -  + ]:GNC          31 :     if ((found & 8) != 0 && (found & 4) == 0)
  116 rhaas@postgresql.org      113                 :UNC           0 :         pg_fatal("%s: %s requires %s", filename,
                                114                 :                :                  "INCREMENTAL FROM TLI", "INCREMENTAL FROM LSN");
  116 rhaas@postgresql.org      115                 :GNC          31 : }
                                116                 :                : 
                                117                 :                : /*
                                118                 :                :  * Write a backup label file to the output directory.
                                119                 :                :  *
                                120                 :                :  * This will be identical to the provided backup_label file, except that the
                                121                 :                :  * INCREMENTAL FROM LSN and INCREMENTAL FROM TLI lines will be omitted.
                                122                 :                :  *
                                123                 :                :  * The new file will be checksummed using the specified algorithm. If
                                124                 :                :  * mwriter != NULL, it will be added to the manifest.
                                125                 :                :  */
                                126                 :                : void
                                127                 :             10 : write_backup_label(char *output_directory, StringInfo buf,
                                128                 :                :                    pg_checksum_type checksum_type, manifest_writer *mwriter)
                                129                 :                : {
                                130                 :                :     char        output_filename[MAXPGPATH];
                                131                 :                :     int         output_fd;
                                132                 :                :     pg_checksum_context checksum_ctx;
                                133                 :                :     uint8       checksum_payload[PG_CHECKSUM_MAX_LENGTH];
                                134                 :                :     int         checksum_length;
                                135                 :                : 
                                136                 :             10 :     pg_checksum_init(&checksum_ctx, checksum_type);
                                137                 :                : 
                                138                 :             10 :     snprintf(output_filename, MAXPGPATH, "%s/backup_label", output_directory);
                                139                 :                : 
                                140         [ -  + ]:             10 :     if ((output_fd = open(output_filename,
                                141                 :                :                           O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
                                142                 :                :                           pg_file_create_mode)) < 0)
  116 rhaas@postgresql.org      143                 :UNC           0 :         pg_fatal("could not open file \"%s\": %m", output_filename);
                                144                 :                : 
  116 rhaas@postgresql.org      145         [ +  + ]:GNC          94 :     while (buf->cursor < buf->len)
                                146                 :                :     {
                                147                 :             84 :         char       *s = &buf->data[buf->cursor];
                                148                 :             84 :         int         eo = get_eol_offset(buf);
                                149                 :             84 :         char       *e = &buf->data[eo];
                                150                 :                : 
                                151         [ +  + ]:             84 :         if (!line_starts_with(s, e, "INCREMENTAL FROM LSN: ", NULL) &&
                                152         [ +  + ]:             77 :             !line_starts_with(s, e, "INCREMENTAL FROM TLI: ", NULL))
                                153                 :                :         {
                                154                 :                :             ssize_t     wb;
                                155                 :                : 
                                156                 :             70 :             wb = write(output_fd, s, e - s);
                                157         [ -  + ]:             70 :             if (wb != e - s)
                                158                 :                :             {
  116 rhaas@postgresql.org      159         [ #  # ]:UNC           0 :                 if (wb < 0)
                                160                 :              0 :                     pg_fatal("could not write file \"%s\": %m", output_filename);
                                161                 :                :                 else
                                162                 :              0 :                     pg_fatal("could not write file \"%s\": wrote only %d of %d bytes",
                                163                 :                :                              output_filename, (int) wb, (int) (e - s));
                                164                 :                :             }
  116 rhaas@postgresql.org      165         [ -  + ]:GNC          70 :             if (pg_checksum_update(&checksum_ctx, (uint8 *) s, e - s) < 0)
  116 rhaas@postgresql.org      166                 :UNC           0 :                 pg_fatal("could not update checksum of file \"%s\"",
                                167                 :                :                          output_filename);
                                168                 :                :         }
                                169                 :                : 
  116 rhaas@postgresql.org      170                 :GNC          84 :         buf->cursor = eo;
                                171                 :                :     }
                                172                 :                : 
                                173         [ -  + ]:             10 :     if (close(output_fd) != 0)
  116 rhaas@postgresql.org      174                 :UNC           0 :         pg_fatal("could not close \"%s\": %m", output_filename);
                                175                 :                : 
  116 rhaas@postgresql.org      176                 :GNC          10 :     checksum_length = pg_checksum_final(&checksum_ctx, checksum_payload);
                                177                 :                : 
                                178         [ +  + ]:             10 :     if (mwriter != NULL)
                                179                 :                :     {
                                180                 :                :         struct stat sb;
                                181                 :                : 
                                182                 :                :         /*
                                183                 :                :          * We could track the length ourselves, but must stat() to get the
                                184                 :                :          * mtime.
                                185                 :                :          */
                                186         [ -  + ]:              9 :         if (stat(output_filename, &sb) < 0)
  116 rhaas@postgresql.org      187                 :UNC           0 :             pg_fatal("could not stat file \"%s\": %m", output_filename);
  116 rhaas@postgresql.org      188                 :GNC           9 :         add_file_to_manifest(mwriter, "backup_label", sb.st_size,
                                189                 :                :                              sb.st_mtime, checksum_type,
                                190                 :                :                              checksum_length, checksum_payload);
                                191                 :                :     }
                                192                 :             10 : }
                                193                 :                : 
                                194                 :                : /*
                                195                 :                :  * Return the offset at which the next line in the buffer starts, or there
                                196                 :                :  * is none, the offset at which the buffer ends.
                                197                 :                :  *
                                198                 :                :  * The search begins at buf->cursor.
                                199                 :                :  */
                                200                 :                : static int
                                201                 :            335 : get_eol_offset(StringInfo buf)
                                202                 :                : {
                                203                 :            335 :     int         eo = buf->cursor;
                                204                 :                : 
                                205         [ +  - ]:          10569 :     while (eo < buf->len)
                                206                 :                :     {
                                207         [ +  + ]:          10569 :         if (buf->data[eo] == '\n')
                                208                 :            335 :             return eo + 1;
                                209                 :          10234 :         ++eo;
                                210                 :                :     }
                                211                 :                : 
  116 rhaas@postgresql.org      212                 :UNC           0 :     return eo;
                                213                 :                : }
                                214                 :                : 
                                215                 :                : /*
                                216                 :                :  * Test whether the line that runs from s to e (inclusive of *s, but not
                                217                 :                :  * inclusive of *e) starts with the match string provided, and return true
                                218                 :                :  * or false according to whether or not this is the case.
                                219                 :                :  *
                                220                 :                :  * If the function returns true and if *sout != NULL, stores a pointer to the
                                221                 :                :  * byte following the match into *sout.
                                222                 :                :  */
                                223                 :                : static bool
  116 rhaas@postgresql.org      224                 :GNC         993 : line_starts_with(char *s, char *e, char *match, char **sout)
                                225                 :                : {
                                226   [ +  -  +  +  :           4255 :     while (s < e && *match != '\0' && *s == *match)
                                              +  + ]
                                227                 :           3262 :         ++s, ++match;
                                228                 :                : 
                                229   [ +  +  +  + ]:            993 :     if (*match == '\0' && sout != NULL)
                                230                 :             96 :         *sout = s;
                                231                 :                : 
                                232                 :            993 :     return (*match == '\0');
                                233                 :                : }
                                234                 :                : 
                                235                 :                : /*
                                236                 :                :  * Parse an LSN starting at s and not stopping at or before e. The return value
                                237                 :                :  * is true on success and otherwise false. On success, stores the result into
                                238                 :                :  * *lsn and sets *c to the first character that is not part of the LSN.
                                239                 :                :  */
                                240                 :                : static bool
                                241                 :             48 : parse_lsn(char *s, char *e, XLogRecPtr *lsn, char **c)
                                242                 :                : {
                                243                 :             48 :     char        save = *e;
                                244                 :                :     int         nchars;
                                245                 :                :     bool        success;
                                246                 :                :     unsigned    hi;
                                247                 :                :     unsigned    lo;
                                248                 :                : 
                                249                 :             48 :     *e = '\0';
                                250                 :             48 :     success = (sscanf(s, "%X/%X%n", &hi, &lo, &nchars) == 2);
                                251                 :             48 :     *e = save;
                                252                 :                : 
                                253         [ +  - ]:             48 :     if (success)
                                254                 :                :     {
                                255                 :             48 :         *lsn = ((XLogRecPtr) hi) << 32 | (XLogRecPtr) lo;
                                256                 :             48 :         *c = s + nchars;
                                257                 :                :     }
                                258                 :                : 
                                259                 :             48 :     return success;
                                260                 :                : }
                                261                 :                : 
                                262                 :                : /*
                                263                 :                :  * Parse a TLI starting at s and stopping at or before e. The return value is
                                264                 :                :  * true on success and otherwise false. On success, stores the result into
                                265                 :                :  * *tli. If the first character that is not part of the TLI is anything other
                                266                 :                :  * than a newline, that is deemed a failure.
                                267                 :                :  */
                                268                 :                : static bool
                                269                 :             48 : parse_tli(char *s, char *e, TimeLineID *tli)
                                270                 :                : {
                                271                 :             48 :     char        save = *e;
                                272                 :                :     int         nchars;
                                273                 :                :     bool        success;
                                274                 :                : 
                                275                 :             48 :     *e = '\0';
                                276                 :             48 :     success = (sscanf(s, "%u%n", tli, &nchars) == 1);
                                277                 :             48 :     *e = save;
                                278                 :                : 
                                279   [ +  -  -  + ]:             48 :     if (success && s[nchars] != '\n')
  116 rhaas@postgresql.org      280                 :UNC           0 :         success = false;
                                281                 :                : 
  116 rhaas@postgresql.org      282                 :GNC          48 :     return success;
                                283                 :                : }
        

Generated by: LCOV version 2.1-beta2-3-g6141622