LCOV - code coverage report
Current view: top level - src/bin/pg_combinebackup - backup_label.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 131 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 6 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * Read and manipulate backup label files
       4              :  *
       5              :  * Portions Copyright (c) 1996-2026, 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
      45            0 : parse_backup_label(char *filename, StringInfo buf,
      46              :                                    TimeLineID *start_tli, XLogRecPtr *start_lsn,
      47              :                                    TimeLineID *previous_tli, XLogRecPtr *previous_lsn)
      48              : {
      49            0 :         int                     found = 0;
      50              : 
      51            0 :         *start_tli = 0;
      52            0 :         *start_lsn = InvalidXLogRecPtr;
      53            0 :         *previous_tli = 0;
      54            0 :         *previous_lsn = InvalidXLogRecPtr;
      55              : 
      56            0 :         while (buf->cursor < buf->len)
      57              :         {
      58            0 :                 char       *s = &buf->data[buf->cursor];
      59            0 :                 int                     eo = get_eol_offset(buf);
      60            0 :                 char       *e = &buf->data[eo];
      61            0 :                 char       *c;
      62              : 
      63            0 :                 if (line_starts_with(s, e, "START WAL LOCATION: ", &s))
      64              :                 {
      65            0 :                         if (!parse_lsn(s, e, start_lsn, &c))
      66            0 :                                 pg_fatal("%s: could not parse %s",
      67              :                                                  filename, "START WAL LOCATION");
      68            0 :                         if (c >= e || *c != ' ')
      69            0 :                                 pg_fatal("%s: improper terminator for %s",
      70              :                                                  filename, "START WAL LOCATION");
      71            0 :                         found |= 1;
      72            0 :                 }
      73            0 :                 else if (line_starts_with(s, e, "START TIMELINE: ", &s))
      74              :                 {
      75            0 :                         if (!parse_tli(s, e, start_tli))
      76            0 :                                 pg_fatal("%s: could not parse TLI for %s",
      77              :                                                  filename, "START TIMELINE");
      78            0 :                         if (*start_tli == 0)
      79            0 :                                 pg_fatal("%s: invalid TLI", filename);
      80            0 :                         found |= 2;
      81            0 :                 }
      82            0 :                 else if (line_starts_with(s, e, "INCREMENTAL FROM LSN: ", &s))
      83              :                 {
      84            0 :                         if (!parse_lsn(s, e, previous_lsn, &c))
      85            0 :                                 pg_fatal("%s: could not parse %s",
      86              :                                                  filename, "INCREMENTAL FROM LSN");
      87            0 :                         if (c >= e || *c != '\n')
      88            0 :                                 pg_fatal("%s: improper terminator for %s",
      89              :                                                  filename, "INCREMENTAL FROM LSN");
      90            0 :                         found |= 4;
      91            0 :                 }
      92            0 :                 else if (line_starts_with(s, e, "INCREMENTAL FROM TLI: ", &s))
      93              :                 {
      94            0 :                         if (!parse_tli(s, e, previous_tli))
      95            0 :                                 pg_fatal("%s: could not parse %s",
      96              :                                                  filename, "INCREMENTAL FROM TLI");
      97            0 :                         if (*previous_tli == 0)
      98            0 :                                 pg_fatal("%s: invalid TLI", filename);
      99            0 :                         found |= 8;
     100            0 :                 }
     101              : 
     102            0 :                 buf->cursor = eo;
     103            0 :         }
     104              : 
     105            0 :         if ((found & 1) == 0)
     106            0 :                 pg_fatal("%s: could not find %s", filename, "START WAL LOCATION");
     107            0 :         if ((found & 2) == 0)
     108            0 :                 pg_fatal("%s: could not find %s", filename, "START TIMELINE");
     109            0 :         if ((found & 4) != 0 && (found & 8) == 0)
     110            0 :                 pg_fatal("%s: %s requires %s", filename,
     111              :                                  "INCREMENTAL FROM LSN", "INCREMENTAL FROM TLI");
     112            0 :         if ((found & 8) != 0 && (found & 4) == 0)
     113            0 :                 pg_fatal("%s: %s requires %s", filename,
     114              :                                  "INCREMENTAL FROM TLI", "INCREMENTAL FROM LSN");
     115            0 : }
     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            0 : write_backup_label(char *output_directory, StringInfo buf,
     128              :                                    pg_checksum_type checksum_type, manifest_writer *mwriter)
     129              : {
     130            0 :         char            output_filename[MAXPGPATH];
     131            0 :         int                     output_fd;
     132            0 :         pg_checksum_context checksum_ctx;
     133            0 :         uint8           checksum_payload[PG_CHECKSUM_MAX_LENGTH];
     134            0 :         int                     checksum_length;
     135              : 
     136            0 :         pg_checksum_init(&checksum_ctx, checksum_type);
     137              : 
     138            0 :         snprintf(output_filename, MAXPGPATH, "%s/backup_label", output_directory);
     139              : 
     140            0 :         if ((output_fd = open(output_filename,
     141              :                                                   O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
     142            0 :                                                   pg_file_create_mode)) < 0)
     143            0 :                 pg_fatal("could not open file \"%s\": %m", output_filename);
     144              : 
     145            0 :         while (buf->cursor < buf->len)
     146              :         {
     147            0 :                 char       *s = &buf->data[buf->cursor];
     148            0 :                 int                     eo = get_eol_offset(buf);
     149            0 :                 char       *e = &buf->data[eo];
     150              : 
     151            0 :                 if (!line_starts_with(s, e, "INCREMENTAL FROM LSN: ", NULL) &&
     152            0 :                         !line_starts_with(s, e, "INCREMENTAL FROM TLI: ", NULL))
     153              :                 {
     154            0 :                         ssize_t         wb;
     155              : 
     156            0 :                         wb = write(output_fd, s, e - s);
     157            0 :                         if (wb != e - s)
     158              :                         {
     159            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 %d of %d",
     163              :                                                          output_filename, (int) wb, (int) (e - s));
     164            0 :                         }
     165            0 :                         if (pg_checksum_update(&checksum_ctx, (uint8 *) s, e - s) < 0)
     166            0 :                                 pg_fatal("could not update checksum of file \"%s\"",
     167              :                                                  output_filename);
     168            0 :                 }
     169              : 
     170            0 :                 buf->cursor = eo;
     171            0 :         }
     172              : 
     173            0 :         if (close(output_fd) != 0)
     174            0 :                 pg_fatal("could not close file \"%s\": %m", output_filename);
     175              : 
     176            0 :         checksum_length = pg_checksum_final(&checksum_ctx, checksum_payload);
     177              : 
     178            0 :         if (mwriter != NULL)
     179              :         {
     180            0 :                 struct stat sb;
     181              : 
     182              :                 /*
     183              :                  * We could track the length ourselves, but must stat() to get the
     184              :                  * mtime.
     185              :                  */
     186            0 :                 if (stat(output_filename, &sb) < 0)
     187            0 :                         pg_fatal("could not stat file \"%s\": %m", output_filename);
     188            0 :                 add_file_to_manifest(mwriter, "backup_label", sb.st_size,
     189            0 :                                                          sb.st_mtime, checksum_type,
     190            0 :                                                          checksum_length, checksum_payload);
     191            0 :         }
     192            0 : }
     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            0 : get_eol_offset(StringInfo buf)
     202              : {
     203            0 :         int                     eo = buf->cursor;
     204              : 
     205            0 :         while (eo < buf->len)
     206              :         {
     207            0 :                 if (buf->data[eo] == '\n')
     208            0 :                         return eo + 1;
     209            0 :                 ++eo;
     210              :         }
     211              : 
     212            0 :         return eo;
     213            0 : }
     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
     224            0 : line_starts_with(char *s, char *e, char *match, char **sout)
     225              : {
     226            0 :         while (s < e && *match != '\0' && *s == *match)
     227            0 :                 ++s, ++match;
     228              : 
     229            0 :         if (*match == '\0' && sout != NULL)
     230            0 :                 *sout = s;
     231              : 
     232            0 :         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            0 : parse_lsn(char *s, char *e, XLogRecPtr *lsn, char **c)
     242              : {
     243            0 :         char            save = *e;
     244            0 :         int                     nchars;
     245            0 :         bool            success;
     246            0 :         unsigned        hi;
     247            0 :         unsigned        lo;
     248              : 
     249            0 :         *e = '\0';
     250            0 :         success = (sscanf(s, "%X/%08X%n", &hi, &lo, &nchars) == 2);
     251            0 :         *e = save;
     252              : 
     253            0 :         if (success)
     254              :         {
     255            0 :                 *lsn = ((XLogRecPtr) hi) << 32 | (XLogRecPtr) lo;
     256            0 :                 *c = s + nchars;
     257            0 :         }
     258              : 
     259            0 :         return success;
     260            0 : }
     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            0 : parse_tli(char *s, char *e, TimeLineID *tli)
     270              : {
     271            0 :         char            save = *e;
     272            0 :         int                     nchars;
     273            0 :         bool            success;
     274              : 
     275            0 :         *e = '\0';
     276            0 :         success = (sscanf(s, "%u%n", tli, &nchars) == 1);
     277            0 :         *e = save;
     278              : 
     279            0 :         if (success && s[nchars] != '\n')
     280            0 :                 success = false;
     281              : 
     282            0 :         return success;
     283            0 : }
        

Generated by: LCOV version 2.3.2-1