PostgreSQL Source Code  git master
backup_label.c
Go to the documentation of this file.
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
46  TimeLineID *start_tli, XLogRecPtr *start_lsn,
47  TimeLineID *previous_tli, XLogRecPtr *previous_lsn)
48 {
49  int found = 0;
50 
51  *start_tli = 0;
52  *start_lsn = InvalidXLogRecPtr;
53  *previous_tli = 0;
54  *previous_lsn = InvalidXLogRecPtr;
55 
56  while (buf->cursor < buf->len)
57  {
58  char *s = &buf->data[buf->cursor];
59  int eo = get_eol_offset(buf);
60  char *e = &buf->data[eo];
61  char *c;
62 
63  if (line_starts_with(s, e, "START WAL LOCATION: ", &s))
64  {
65  if (!parse_lsn(s, e, start_lsn, &c))
66  pg_fatal("%s: could not parse %s",
67  filename, "START WAL LOCATION");
68  if (c >= e || *c != ' ')
69  pg_fatal("%s: improper terminator for %s",
70  filename, "START WAL LOCATION");
71  found |= 1;
72  }
73  else if (line_starts_with(s, e, "START TIMELINE: ", &s))
74  {
75  if (!parse_tli(s, e, start_tli))
76  pg_fatal("%s: could not parse TLI for %s",
77  filename, "START TIMELINE");
78  if (*start_tli == 0)
79  pg_fatal("%s: invalid TLI", filename);
80  found |= 2;
81  }
82  else if (line_starts_with(s, e, "INCREMENTAL FROM LSN: ", &s))
83  {
84  if (!parse_lsn(s, e, previous_lsn, &c))
85  pg_fatal("%s: could not parse %s",
86  filename, "INCREMENTAL FROM LSN");
87  if (c >= e || *c != '\n')
88  pg_fatal("%s: improper terminator for %s",
89  filename, "INCREMENTAL FROM LSN");
90  found |= 4;
91  }
92  else if (line_starts_with(s, e, "INCREMENTAL FROM TLI: ", &s))
93  {
94  if (!parse_tli(s, e, previous_tli))
95  pg_fatal("%s: could not parse %s",
96  filename, "INCREMENTAL FROM TLI");
97  if (*previous_tli == 0)
98  pg_fatal("%s: invalid TLI", filename);
99  found |= 8;
100  }
101 
102  buf->cursor = eo;
103  }
104 
105  if ((found & 1) == 0)
106  pg_fatal("%s: could not find %s", filename, "START WAL LOCATION");
107  if ((found & 2) == 0)
108  pg_fatal("%s: could not find %s", filename, "START TIMELINE");
109  if ((found & 4) != 0 && (found & 8) == 0)
110  pg_fatal("%s: %s requires %s", filename,
111  "INCREMENTAL FROM LSN", "INCREMENTAL FROM TLI");
112  if ((found & 8) != 0 && (found & 4) == 0)
113  pg_fatal("%s: %s requires %s", filename,
114  "INCREMENTAL FROM TLI", "INCREMENTAL FROM LSN");
115 }
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 write_backup_label(char *output_directory, StringInfo buf,
128  pg_checksum_type checksum_type, manifest_writer *mwriter)
129 {
131  int output_fd;
132  pg_checksum_context checksum_ctx;
133  uint8 checksum_payload[PG_CHECKSUM_MAX_LENGTH];
134  int checksum_length;
135 
136  pg_checksum_init(&checksum_ctx, checksum_type);
137 
138  snprintf(output_filename, MAXPGPATH, "%s/backup_label", output_directory);
139 
140  if ((output_fd = open(output_filename,
141  O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
142  pg_file_create_mode)) < 0)
143  pg_fatal("could not open file \"%s\": %m", output_filename);
144 
145  while (buf->cursor < buf->len)
146  {
147  char *s = &buf->data[buf->cursor];
148  int eo = get_eol_offset(buf);
149  char *e = &buf->data[eo];
150 
151  if (!line_starts_with(s, e, "INCREMENTAL FROM LSN: ", NULL) &&
152  !line_starts_with(s, e, "INCREMENTAL FROM TLI: ", NULL))
153  {
154  ssize_t wb;
155 
156  wb = write(output_fd, s, e - s);
157  if (wb != e - s)
158  {
159  if (wb < 0)
160  pg_fatal("could not write file \"%s\": %m", output_filename);
161  else
162  pg_fatal("could not write file \"%s\": wrote %d of %d",
163  output_filename, (int) wb, (int) (e - s));
164  }
165  if (pg_checksum_update(&checksum_ctx, (uint8 *) s, e - s) < 0)
166  pg_fatal("could not update checksum of file \"%s\"",
168  }
169 
170  buf->cursor = eo;
171  }
172 
173  if (close(output_fd) != 0)
174  pg_fatal("could not close file \"%s\": %m", output_filename);
175 
176  checksum_length = pg_checksum_final(&checksum_ctx, checksum_payload);
177 
178  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  if (stat(output_filename, &sb) < 0)
187  pg_fatal("could not stat file \"%s\": %m", output_filename);
188  add_file_to_manifest(mwriter, "backup_label", sb.st_size,
189  sb.st_mtime, checksum_type,
190  checksum_length, checksum_payload);
191  }
192 }
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
202 {
203  int eo = buf->cursor;
204 
205  while (eo < buf->len)
206  {
207  if (buf->data[eo] == '\n')
208  return eo + 1;
209  ++eo;
210  }
211 
212  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
224 line_starts_with(char *s, char *e, char *match, char **sout)
225 {
226  while (s < e && *match != '\0' && *s == *match)
227  ++s, ++match;
228 
229  if (*match == '\0' && sout != NULL)
230  *sout = s;
231 
232  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 parse_lsn(char *s, char *e, XLogRecPtr *lsn, char **c)
242 {
243  char save = *e;
244  int nchars;
245  bool success;
246  unsigned hi;
247  unsigned lo;
248 
249  *e = '\0';
250  success = (sscanf(s, "%X/%X%n", &hi, &lo, &nchars) == 2);
251  *e = save;
252 
253  if (success)
254  {
255  *lsn = ((XLogRecPtr) hi) << 32 | (XLogRecPtr) lo;
256  *c = s + nchars;
257  }
258 
259  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 parse_tli(char *s, char *e, TimeLineID *tli)
270 {
271  char save = *e;
272  int nchars;
273  bool success;
274 
275  *e = '\0';
276  success = (sscanf(s, "%u%n", tli, &nchars) == 1);
277  *e = save;
278 
279  if (success && s[nchars] != '\n')
280  success = false;
281 
282  return success;
283 }
static int get_eol_offset(StringInfo buf)
Definition: backup_label.c:201
void parse_backup_label(char *filename, StringInfo buf, TimeLineID *start_tli, XLogRecPtr *start_lsn, TimeLineID *previous_tli, XLogRecPtr *previous_lsn)
Definition: backup_label.c:45
static bool parse_lsn(char *s, char *e, XLogRecPtr *lsn, char **c)
Definition: backup_label.c:241
static bool line_starts_with(char *s, char *e, char *match, char **sout)
Definition: backup_label.c:224
void write_backup_label(char *output_directory, StringInfo buf, pg_checksum_type checksum_type, manifest_writer *mwriter)
Definition: backup_label.c:127
static bool parse_tli(char *s, char *e, TimeLineID *tli)
Definition: backup_label.c:269
#define PG_BINARY
Definition: c.h:1276
unsigned char uint8
Definition: c.h:516
int pg_checksum_final(pg_checksum_context *context, uint8 *output)
int pg_checksum_update(pg_checksum_context *context, const uint8 *input, size_t len)
int pg_checksum_init(pg_checksum_context *context, pg_checksum_type type)
#define PG_CHECKSUM_MAX_LENGTH
pg_checksum_type
char * output_filename
Definition: ecpg.c:23
int pg_file_create_mode
Definition: file_perm.c:19
static bool success
Definition: initdb.c:186
#define close(a)
Definition: win32.h:12
#define write(a, b, c)
Definition: win32.h:14
#define pg_fatal(...)
#define MAXPGPATH
const void size_t len
static char * filename
Definition: pg_dumpall.c:119
static char * buf
Definition: pg_test_fsync.c:72
#define snprintf
Definition: port.h:238
char * c
e
Definition: preproc-init.c:82
__time64_t st_mtime
Definition: win32_port.h:275
__int64 st_size
Definition: win32_port.h:273
#define stat
Definition: win32_port.h:284
void add_file_to_manifest(manifest_writer *mwriter, const char *manifest_path, uint64 size, time_t mtime, pg_checksum_type checksum_type, int checksum_length, uint8 *checksum_payload)
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28
uint32 TimeLineID
Definition: xlogdefs.h:59