PostgreSQL Source Code  git master
write_manifest.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * Write a new backup manifest.
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/write_manifest.c
9  *
10  *-------------------------------------------------------------------------
11  */
12 
13 #include "postgres_fe.h"
14 
15 #include <fcntl.h>
16 #include <time.h>
17 #include <unistd.h>
18 
19 #include "common/checksum_helper.h"
20 #include "common/file_perm.h"
21 #include "common/logging.h"
22 #include "lib/stringinfo.h"
23 #include "load_manifest.h"
24 #include "mb/pg_wchar.h"
25 #include "write_manifest.h"
26 
28 {
30  int fd;
32  bool first_file;
35 };
36 
37 static void escape_json(StringInfo buf, const char *str);
38 static void flush_manifest(manifest_writer *mwriter);
39 static size_t hex_encode(const uint8 *src, size_t len, char *dst);
40 
41 /*
42  * Create a new backup manifest writer.
43  *
44  * The backup manifest will be written into a file named backup_manifest
45  * in the specified directory.
46  */
48 create_manifest_writer(char *directory, uint64 system_identifier)
49 {
50  manifest_writer *mwriter = pg_malloc(sizeof(manifest_writer));
51 
52  snprintf(mwriter->pathname, MAXPGPATH, "%s/backup_manifest", directory);
53  mwriter->fd = -1;
54  initStringInfo(&mwriter->buf);
55  mwriter->first_file = true;
56  mwriter->still_checksumming = true;
58 
59  appendStringInfo(&mwriter->buf,
60  "{ \"PostgreSQL-Backup-Manifest-Version\": 2,\n"
61  "\"System-Identifier\": " UINT64_FORMAT ",\n"
62  "\"Files\": [",
63  system_identifier);
64 
65  return mwriter;
66 }
67 
68 /*
69  * Add an entry for a file to a backup manifest.
70  *
71  * This is very similar to the backend's AddFileToBackupManifest, but
72  * various adjustments are required due to frontend/backend differences
73  * and other details.
74  */
75 void
76 add_file_to_manifest(manifest_writer *mwriter, const char *manifest_path,
77  uint64 size, time_t mtime,
78  pg_checksum_type checksum_type,
79  int checksum_length,
80  uint8 *checksum_payload)
81 {
82  int pathlen = strlen(manifest_path);
83 
84  if (mwriter->first_file)
85  {
86  appendStringInfoChar(&mwriter->buf, '\n');
87  mwriter->first_file = false;
88  }
89  else
90  appendStringInfoString(&mwriter->buf, ",\n");
91 
92  if (pg_encoding_verifymbstr(PG_UTF8, manifest_path, pathlen) == pathlen)
93  {
94  appendStringInfoString(&mwriter->buf, "{ \"Path\": ");
95  escape_json(&mwriter->buf, manifest_path);
96  appendStringInfoString(&mwriter->buf, ", ");
97  }
98  else
99  {
100  appendStringInfoString(&mwriter->buf, "{ \"Encoded-Path\": \"");
101  enlargeStringInfo(&mwriter->buf, 2 * pathlen);
102  mwriter->buf.len += hex_encode((const uint8 *) manifest_path, pathlen,
103  &mwriter->buf.data[mwriter->buf.len]);
104  appendStringInfoString(&mwriter->buf, "\", ");
105  }
106 
107  appendStringInfo(&mwriter->buf, "\"Size\": %llu, ",
108  (unsigned long long) size);
109 
110  appendStringInfoString(&mwriter->buf, "\"Last-Modified\": \"");
111  enlargeStringInfo(&mwriter->buf, 128);
112  mwriter->buf.len += strftime(&mwriter->buf.data[mwriter->buf.len], 128,
113  "%Y-%m-%d %H:%M:%S %Z",
114  gmtime(&mtime));
115  appendStringInfoChar(&mwriter->buf, '"');
116 
117  if (mwriter->buf.len > 128 * 1024)
118  flush_manifest(mwriter);
119 
120  if (checksum_length > 0)
121  {
122  appendStringInfo(&mwriter->buf,
123  ", \"Checksum-Algorithm\": \"%s\", \"Checksum\": \"",
124  pg_checksum_type_name(checksum_type));
125 
126  enlargeStringInfo(&mwriter->buf, 2 * checksum_length);
127  mwriter->buf.len += hex_encode(checksum_payload, checksum_length,
128  &mwriter->buf.data[mwriter->buf.len]);
129 
130  appendStringInfoChar(&mwriter->buf, '"');
131  }
132 
133  appendStringInfoString(&mwriter->buf, " }");
134 
135  if (mwriter->buf.len > 128 * 1024)
136  flush_manifest(mwriter);
137 }
138 
139 /*
140  * Finalize the backup_manifest.
141  */
142 void
144  manifest_wal_range *first_wal_range)
145 {
146  uint8 checksumbuf[PG_SHA256_DIGEST_LENGTH];
147  int len;
148  manifest_wal_range *wal_range;
149 
150  /* Terminate the list of files. */
151  appendStringInfoString(&mwriter->buf, "\n],\n");
152 
153  /* Start a list of LSN ranges. */
154  appendStringInfoString(&mwriter->buf, "\"WAL-Ranges\": [\n");
155 
156  for (wal_range = first_wal_range; wal_range != NULL;
157  wal_range = wal_range->next)
158  appendStringInfo(&mwriter->buf,
159  "%s{ \"Timeline\": %u, \"Start-LSN\": \"%X/%X\", \"End-LSN\": \"%X/%X\" }",
160  wal_range == first_wal_range ? "" : ",\n",
161  wal_range->tli,
162  LSN_FORMAT_ARGS(wal_range->start_lsn),
163  LSN_FORMAT_ARGS(wal_range->end_lsn));
164 
165  /* Terminate the list of WAL ranges. */
166  appendStringInfoString(&mwriter->buf, "\n],\n");
167 
168  /* Flush accumulated data and update checksum calculation. */
169  flush_manifest(mwriter);
170 
171  /* Checksum only includes data up to this point. */
172  mwriter->still_checksumming = false;
173 
174  /* Compute and insert manifest checksum. */
175  appendStringInfoString(&mwriter->buf, "\"Manifest-Checksum\": \"");
177  len = pg_checksum_final(&mwriter->manifest_ctx, checksumbuf);
179  mwriter->buf.len +=
180  hex_encode(checksumbuf, len, &mwriter->buf.data[mwriter->buf.len]);
181  appendStringInfoString(&mwriter->buf, "\"}\n");
182 
183  /* Flush the last manifest checksum itself. */
184  flush_manifest(mwriter);
185 
186  /* Close the file. */
187  if (close(mwriter->fd) != 0)
188  pg_fatal("could not close file \"%s\": %m", mwriter->pathname);
189  mwriter->fd = -1;
190 }
191 
192 /*
193  * Produce a JSON string literal, properly escaping characters in the text.
194  */
195 static void
197 {
198  const char *p;
199 
201  for (p = str; *p; p++)
202  {
203  switch (*p)
204  {
205  case '\b':
206  appendStringInfoString(buf, "\\b");
207  break;
208  case '\f':
209  appendStringInfoString(buf, "\\f");
210  break;
211  case '\n':
212  appendStringInfoString(buf, "\\n");
213  break;
214  case '\r':
215  appendStringInfoString(buf, "\\r");
216  break;
217  case '\t':
218  appendStringInfoString(buf, "\\t");
219  break;
220  case '"':
221  appendStringInfoString(buf, "\\\"");
222  break;
223  case '\\':
224  appendStringInfoString(buf, "\\\\");
225  break;
226  default:
227  if ((unsigned char) *p < ' ')
228  appendStringInfo(buf, "\\u%04x", (int) *p);
229  else
231  break;
232  }
233  }
235 }
236 
237 /*
238  * Flush whatever portion of the backup manifest we have generated and
239  * buffered in memory out to a file on disk.
240  *
241  * The first call to this function will create the file. After that, we
242  * keep it open and just append more data.
243  */
244 static void
246 {
247  if (mwriter->fd == -1 &&
248  (mwriter->fd = open(mwriter->pathname,
249  O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
250  pg_file_create_mode)) < 0)
251  pg_fatal("could not open file \"%s\": %m", mwriter->pathname);
252 
253  if (mwriter->buf.len > 0)
254  {
255  ssize_t wb;
256 
257  wb = write(mwriter->fd, mwriter->buf.data, mwriter->buf.len);
258  if (wb != mwriter->buf.len)
259  {
260  if (wb < 0)
261  pg_fatal("could not write file \"%s\": %m", mwriter->pathname);
262  else
263  pg_fatal("could not write file \"%s\": wrote %d of %d",
264  mwriter->pathname, (int) wb, mwriter->buf.len);
265  }
266 
267  if (mwriter->still_checksumming &&
269  (uint8 *) mwriter->buf.data,
270  mwriter->buf.len) < 0)
271  pg_fatal("could not update checksum of file \"%s\"",
272  mwriter->pathname);
273  resetStringInfo(&mwriter->buf);
274  }
275 }
276 
277 /*
278  * Encode bytes using two hexadecimal digits for each one.
279  */
280 static size_t
281 hex_encode(const uint8 *src, size_t len, char *dst)
282 {
283  const uint8 *end = src + len;
284 
285  while (src < end)
286  {
287  unsigned n1 = (*src >> 4) & 0xF;
288  unsigned n2 = *src & 0xF;
289 
290  *dst++ = n1 < 10 ? '0' + n1 : 'a' + n1 - 10;
291  *dst++ = n2 < 10 ? '0' + n2 : 'a' + n2 - 10;
292  ++src;
293  }
294 
295  return len * 2;
296 }
#define Assert(condition)
Definition: c.h:837
#define PG_BINARY
Definition: c.h:1252
#define UINT64_FORMAT
Definition: c.h:526
unsigned char uint8
Definition: c.h:490
char * pg_checksum_type_name(pg_checksum_type type)
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)
pg_checksum_type
@ CHECKSUM_TYPE_SHA256
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
int pg_file_create_mode
Definition: file_perm.c:19
const char * str
#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 * buf
Definition: pg_test_fsync.c:72
@ PG_UTF8
Definition: pg_wchar.h:232
#define snprintf
Definition: port.h:238
#define PG_SHA256_DIGEST_LENGTH
Definition: sha2.h:23
#define PG_SHA256_DIGEST_STRING_LENGTH
Definition: sha2.h:24
static pg_noinline void Size size
Definition: slab.c:607
void resetStringInfo(StringInfo str)
Definition: stringinfo.c:75
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:94
void enlargeStringInfo(StringInfo str, int needed)
Definition: stringinfo.c:286
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:179
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:191
void initStringInfo(StringInfo str)
Definition: stringinfo.c:56
#define appendStringInfoCharMacro(str, ch)
Definition: stringinfo.h:204
XLogRecPtr end_lsn
Definition: load_manifest.h:48
struct manifest_wal_range * next
Definition: load_manifest.h:49
XLogRecPtr start_lsn
Definition: load_manifest.h:47
char pathname[MAXPGPATH]
StringInfoData buf
pg_checksum_context manifest_ctx
int pg_encoding_verifymbstr(int encoding, const char *mbstr, int len)
Definition: wchar.c:2116
static void flush_manifest(manifest_writer *mwriter)
manifest_writer * create_manifest_writer(char *directory, uint64 system_identifier)
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)
static size_t hex_encode(const uint8 *src, size_t len, char *dst)
void finalize_manifest(manifest_writer *mwriter, manifest_wal_range *first_wal_range)
static void escape_json(StringInfo buf, const char *str)
#define LSN_FORMAT_ARGS(lsn)
Definition: xlogdefs.h:43
static const char * directory
Definition: zic.c:634