Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pg_resetwal.c
4 : * A utility to "zero out" the xlog when it's corrupt beyond recovery.
5 : * Can also rebuild pg_control if needed.
6 : *
7 : * The theory of operation is fairly simple:
8 : * 1. Read the existing pg_control (which will include the last
9 : * checkpoint record).
10 : * 2. If pg_control is corrupt, attempt to intuit reasonable values,
11 : * by scanning the old xlog if necessary.
12 : * 3. Modify pg_control to reflect a "shutdown" state with a checkpoint
13 : * record at the start of xlog.
14 : * 4. Flush the existing xlog files and write a new segment with
15 : * just a checkpoint record in it. The new segment is positioned
16 : * just past the end of the old xlog, so that existing LSNs in
17 : * data pages will appear to be "in the past".
18 : * This is all pretty straightforward except for the intuition part of
19 : * step 2 ...
20 : *
21 : *
22 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
23 : * Portions Copyright (c) 1994, Regents of the University of California
24 : *
25 : * src/bin/pg_resetwal/pg_resetwal.c
26 : *
27 : *-------------------------------------------------------------------------
28 : */
29 :
30 : /*
31 : * We have to use postgres.h not postgres_fe.h here, because there's so much
32 : * backend-only stuff in the XLOG include files we need. But we need a
33 : * frontend-ish environment otherwise. Hence this ugly hack.
34 : */
35 : #define FRONTEND 1
36 :
37 : #include "postgres.h"
38 :
39 : #include <dirent.h>
40 : #include <fcntl.h>
41 : #include <sys/stat.h>
42 : #include <sys/time.h>
43 : #include <time.h>
44 : #include <unistd.h>
45 :
46 : #include "access/heaptoast.h"
47 : #include "access/multixact.h"
48 : #include "access/transam.h"
49 : #include "access/xlog.h"
50 : #include "access/xlog_internal.h"
51 : #include "common/controldata_utils.h"
52 : #include "common/fe_memutils.h"
53 : #include "common/file_perm.h"
54 : #include "common/logging.h"
55 : #include "common/restricted_token.h"
56 : #include "common/string.h"
57 : #include "fe_utils/option_utils.h"
58 : #include "fe_utils/version.h"
59 : #include "getopt_long.h"
60 : #include "pg_getopt.h"
61 : #include "storage/large_object.h"
62 :
63 : static ControlFileData ControlFile; /* pg_control values */
64 : static XLogSegNo newXlogSegNo; /* new XLOG segment # */
65 : static bool guessed = false; /* T if we had to guess at any values */
66 : static const char *progname;
67 :
68 : /*
69 : * New values given on the command-line
70 : */
71 : static bool next_xid_epoch_given = false;
72 : static uint32 next_xid_epoch_val;
73 :
74 : static bool oldest_xid_given = false;
75 : static TransactionId oldest_xid_val;
76 :
77 : static bool next_xid_given = false;
78 : static TransactionId next_xid_val;
79 :
80 : static bool commit_ts_xids_given = false;
81 : static TransactionId oldest_commit_ts_xid_val;
82 : static TransactionId newest_commit_ts_xid_val;
83 :
84 : static bool next_oid_given = false;
85 : static Oid next_oid_val;
86 :
87 : static bool mxids_given = false;
88 : static MultiXactId next_mxid_val;
89 : static MultiXactId oldest_mxid_val = 0;
90 :
91 : static bool next_mxoff_given = false;
92 : static MultiXactOffset next_mxoff_val;
93 :
94 : static bool wal_segsize_given = false;
95 : static int wal_segsize_val;
96 :
97 : static bool char_signedness_given = false;
98 : static bool char_signedness_val;
99 :
100 :
101 : static TimeLineID minXlogTli = 0;
102 : static XLogSegNo minXlogSegNo = 0;
103 : static int WalSegSz;
104 :
105 : static void CheckDataVersion(void);
106 : static bool read_controlfile(void);
107 : static void GuessControlValues(void);
108 : static void PrintControlValues(bool guessed);
109 : static void PrintNewControlValues(void);
110 : static void RewriteControlFile(void);
111 : static void FindEndOfXLOG(void);
112 : static void KillExistingXLOG(void);
113 : static void KillExistingArchiveStatus(void);
114 : static void KillExistingWALSummaries(void);
115 : static void WriteEmptyXLOG(void);
116 : static void usage(void);
117 : static uint32 strtouint32_strict(const char *restrict s, char **restrict endptr, int base);
118 : static uint64 strtouint64_strict(const char *restrict s, char **restrict endptr, int base);
119 :
120 :
121 : int
122 386 : main(int argc, char *argv[])
123 : {
124 : static struct option long_options[] = {
125 : {"commit-timestamp-ids", required_argument, NULL, 'c'},
126 : {"pgdata", required_argument, NULL, 'D'},
127 : {"epoch", required_argument, NULL, 'e'},
128 : {"force", no_argument, NULL, 'f'},
129 : {"next-wal-file", required_argument, NULL, 'l'},
130 : {"multixact-ids", required_argument, NULL, 'm'},
131 : {"dry-run", no_argument, NULL, 'n'},
132 : {"next-oid", required_argument, NULL, 'o'},
133 : {"multixact-offset", required_argument, NULL, 'O'},
134 : {"oldest-transaction-id", required_argument, NULL, 'u'},
135 : {"next-transaction-id", required_argument, NULL, 'x'},
136 : {"wal-segsize", required_argument, NULL, 1},
137 : {"char-signedness", required_argument, NULL, 2},
138 : {NULL, 0, NULL, 0}
139 : };
140 :
141 : int c;
142 386 : bool force = false;
143 386 : bool noupdate = false;
144 : char *endptr;
145 : char *endptr2;
146 386 : char *DataDir = NULL;
147 386 : char *log_fname = NULL;
148 : int fd;
149 :
150 386 : pg_logging_init(argv[0]);
151 386 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_resetwal"));
152 386 : progname = get_progname(argv[0]);
153 :
154 386 : if (argc > 1)
155 : {
156 384 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
157 : {
158 2 : usage();
159 2 : exit(0);
160 : }
161 382 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
162 : {
163 94 : puts("pg_resetwal (PostgreSQL) " PG_VERSION);
164 94 : exit(0);
165 : }
166 : }
167 :
168 :
169 646 : while ((c = getopt_long(argc, argv, "c:D:e:fl:m:no:O:u:x:", long_options, NULL)) != -1)
170 : {
171 408 : switch (c)
172 : {
173 8 : case 'D':
174 8 : DataDir = optarg;
175 8 : break;
176 :
177 78 : case 'f':
178 78 : force = true;
179 78 : break;
180 :
181 82 : case 'n':
182 82 : noupdate = true;
183 82 : break;
184 :
185 28 : case 'e':
186 28 : errno = 0;
187 28 : next_xid_epoch_val = strtouint32_strict(optarg, &endptr, 0);
188 28 : if (endptr == optarg || *endptr != '\0' || errno != 0)
189 : {
190 : /*------
191 : translator: the second %s is a command line argument (-e, etc) */
192 4 : pg_log_error("invalid argument for option %s", "-e");
193 4 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
194 4 : exit(1);
195 : }
196 24 : next_xid_epoch_given = true;
197 24 : break;
198 :
199 26 : case 'u':
200 26 : errno = 0;
201 26 : oldest_xid_val = strtouint32_strict(optarg, &endptr, 0);
202 26 : if (endptr == optarg || *endptr != '\0' || errno != 0)
203 : {
204 2 : pg_log_error("invalid argument for option %s", "-u");
205 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
206 2 : exit(1);
207 : }
208 24 : if (!TransactionIdIsNormal(oldest_xid_val))
209 2 : pg_fatal("oldest transaction ID (-u) must be greater than or equal to %u", FirstNormalTransactionId);
210 22 : oldest_xid_given = true;
211 22 : break;
212 :
213 32 : case 'x':
214 32 : errno = 0;
215 32 : next_xid_val = strtouint32_strict(optarg, &endptr, 0);
216 32 : if (endptr == optarg || *endptr != '\0' || errno != 0)
217 : {
218 8 : pg_log_error("invalid argument for option %s", "-x");
219 8 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
220 8 : exit(1);
221 : }
222 24 : if (!TransactionIdIsNormal(next_xid_val))
223 2 : pg_fatal("transaction ID (-x) must be greater than or equal to %u", FirstNormalTransactionId);
224 22 : next_xid_given = true;
225 22 : break;
226 :
227 30 : case 'c':
228 30 : errno = 0;
229 30 : oldest_commit_ts_xid_val = strtouint32_strict(optarg, &endptr, 0);
230 30 : if (endptr == optarg || *endptr != ',' || errno != 0)
231 : {
232 2 : pg_log_error("invalid argument for option %s", "-c");
233 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
234 2 : exit(1);
235 : }
236 28 : newest_commit_ts_xid_val = strtoul(endptr + 1, &endptr2, 0);
237 28 : if (endptr2 == endptr + 1 || *endptr2 != '\0' || errno != 0)
238 : {
239 2 : pg_log_error("invalid argument for option %s", "-c");
240 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
241 2 : exit(1);
242 : }
243 :
244 26 : if (oldest_commit_ts_xid_val < FirstNormalTransactionId &&
245 2 : oldest_commit_ts_xid_val != InvalidTransactionId)
246 2 : pg_fatal("transaction ID (-c) must be either %u or greater than or equal to %u", InvalidTransactionId, FirstNormalTransactionId);
247 :
248 24 : if (newest_commit_ts_xid_val < FirstNormalTransactionId &&
249 6 : newest_commit_ts_xid_val != InvalidTransactionId)
250 2 : pg_fatal("transaction ID (-c) must be either %u or greater than or equal to %u", InvalidTransactionId, FirstNormalTransactionId);
251 22 : commit_ts_xids_given = true;
252 22 : break;
253 :
254 26 : case 'o':
255 26 : errno = 0;
256 26 : next_oid_val = strtouint32_strict(optarg, &endptr, 0);
257 26 : if (endptr == optarg || *endptr != '\0' || errno != 0)
258 : {
259 2 : pg_log_error("invalid argument for option %s", "-o");
260 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
261 2 : exit(1);
262 : }
263 24 : if (next_oid_val == 0)
264 2 : pg_fatal("OID (-o) must not be 0");
265 22 : next_oid_given = true;
266 22 : break;
267 :
268 32 : case 'm':
269 32 : errno = 0;
270 32 : next_mxid_val = strtouint32_strict(optarg, &endptr, 0);
271 32 : if (endptr == optarg || *endptr != ',' || errno != 0)
272 : {
273 2 : pg_log_error("invalid argument for option %s", "-m");
274 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
275 2 : exit(1);
276 : }
277 :
278 30 : oldest_mxid_val = strtouint32_strict(endptr + 1, &endptr2, 0);
279 30 : if (endptr2 == endptr + 1 || *endptr2 != '\0' || errno != 0)
280 : {
281 2 : pg_log_error("invalid argument for option %s", "-m");
282 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
283 2 : exit(1);
284 : }
285 :
286 : /*
287 : * XXX It'd be nice to have more sanity checks here, e.g. so
288 : * that oldest is not wrapped around w.r.t. nextMulti.
289 : */
290 28 : if (next_mxid_val == 0)
291 2 : pg_fatal("next multitransaction ID (-m) must not be 0");
292 26 : if (oldest_mxid_val == 0)
293 2 : pg_fatal("oldest multitransaction ID (-m) must not be 0");
294 24 : mxids_given = true;
295 24 : break;
296 :
297 26 : case 'O':
298 26 : errno = 0;
299 26 : next_mxoff_val = strtouint64_strict(optarg, &endptr, 0);
300 26 : if (endptr == optarg || *endptr != '\0' || errno != 0)
301 : {
302 4 : pg_log_error("invalid argument for option %s", "-O");
303 4 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
304 4 : exit(1);
305 : }
306 22 : next_mxoff_given = true;
307 22 : break;
308 :
309 24 : case 'l':
310 24 : if (strspn(optarg, "01234567890ABCDEFabcdef") != XLOG_FNAME_LEN)
311 : {
312 2 : pg_log_error("invalid argument for option %s", "-l");
313 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
314 2 : exit(1);
315 : }
316 :
317 : /*
318 : * XLogFromFileName requires wal segment size which is not yet
319 : * set. Hence wal details are set later on.
320 : */
321 22 : log_fname = pg_strdup(optarg);
322 22 : break;
323 :
324 8 : case 1:
325 : {
326 : int wal_segsize_mb;
327 :
328 8 : if (!option_parse_int(optarg, "--wal-segsize", 1, 1024, &wal_segsize_mb))
329 2 : exit(1);
330 6 : wal_segsize_val = wal_segsize_mb * 1024 * 1024;
331 6 : if (!IsValidWalSegSize(wal_segsize_val))
332 2 : pg_fatal("argument of %s must be a power of two between 1 and 1024", "--wal-segsize");
333 4 : wal_segsize_given = true;
334 4 : break;
335 : }
336 :
337 6 : case 2:
338 : {
339 6 : errno = 0;
340 :
341 6 : if (pg_strcasecmp(optarg, "signed") == 0)
342 0 : char_signedness_val = true;
343 6 : else if (pg_strcasecmp(optarg, "unsigned") == 0)
344 4 : char_signedness_val = false;
345 : else
346 : {
347 2 : pg_log_error("invalid argument for option %s", "--char-signedness");
348 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
349 2 : exit(1);
350 : }
351 4 : char_signedness_given = true;
352 4 : break;
353 : }
354 :
355 2 : default:
356 : /* getopt_long already emitted a complaint */
357 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
358 2 : exit(1);
359 : }
360 : }
361 :
362 238 : if (DataDir == NULL && optind < argc)
363 228 : DataDir = argv[optind++];
364 :
365 : /* Complain if any arguments remain */
366 238 : if (optind < argc)
367 : {
368 2 : pg_log_error("too many command-line arguments (first is \"%s\")",
369 : argv[optind]);
370 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
371 2 : exit(1);
372 : }
373 :
374 236 : if (DataDir == NULL)
375 : {
376 2 : pg_log_error("no data directory specified");
377 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
378 2 : exit(1);
379 : }
380 :
381 : /*
382 : * Don't allow pg_resetwal to be run as root, to avoid overwriting the
383 : * ownership of files in the data directory. We need only check for root
384 : * -- any other user won't have sufficient permissions to modify files in
385 : * the data directory.
386 : */
387 : #ifndef WIN32
388 234 : if (geteuid() == 0)
389 : {
390 0 : pg_log_error("cannot be executed by \"root\"");
391 0 : pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
392 : progname);
393 0 : exit(1);
394 : }
395 : #endif
396 :
397 234 : get_restricted_token();
398 :
399 : /* Set mask based on PGDATA permissions */
400 234 : if (!GetDataDirectoryCreatePerm(DataDir))
401 2 : pg_fatal("could not read permissions of directory \"%s\": %m",
402 : DataDir);
403 :
404 232 : umask(pg_mode_mask);
405 :
406 232 : if (chdir(DataDir) < 0)
407 0 : pg_fatal("could not change directory to \"%s\": %m",
408 : DataDir);
409 :
410 : /* Check that data directory matches our server version */
411 232 : CheckDataVersion();
412 :
413 : /*
414 : * Check for a postmaster lock file --- if there is one, refuse to
415 : * proceed, on grounds we might be interfering with a live installation.
416 : */
417 232 : if ((fd = open("postmaster.pid", O_RDONLY, 0)) < 0)
418 : {
419 230 : if (errno != ENOENT)
420 0 : pg_fatal("could not open file \"%s\" for reading: %m",
421 : "postmaster.pid");
422 : }
423 : else
424 : {
425 2 : pg_log_error("lock file \"%s\" exists", "postmaster.pid");
426 2 : pg_log_error_hint("Is a server running? If not, delete the lock file and try again.");
427 2 : exit(1);
428 : }
429 :
430 : /*
431 : * Attempt to read the existing pg_control file
432 : */
433 230 : if (!read_controlfile())
434 8 : GuessControlValues();
435 :
436 : /*
437 : * If no new WAL segment size was specified, use the control file value.
438 : */
439 230 : if (wal_segsize_given)
440 4 : WalSegSz = wal_segsize_val;
441 : else
442 226 : WalSegSz = ControlFile.xlog_seg_size;
443 :
444 230 : if (log_fname != NULL)
445 22 : XLogFromFileName(log_fname, &minXlogTli, &minXlogSegNo, WalSegSz);
446 :
447 : /*
448 : * Also look at existing segment files to set up newXlogSegNo
449 : */
450 230 : FindEndOfXLOG();
451 :
452 : /*
453 : * If we're not going to proceed with the reset, print the current control
454 : * file parameters.
455 : */
456 230 : if ((guessed && !force) || noupdate)
457 84 : PrintControlValues(guessed);
458 :
459 : /*
460 : * Adjust fields if required by switches. (Do this now so that printout,
461 : * if any, includes these values.)
462 : */
463 230 : if (next_xid_epoch_given)
464 : ControlFile.checkPointCopy.nextXid =
465 24 : FullTransactionIdFromEpochAndXid(next_xid_epoch_val,
466 24 : XidFromFullTransactionId(ControlFile.checkPointCopy.nextXid));
467 :
468 230 : if (oldest_xid_given)
469 : {
470 22 : ControlFile.checkPointCopy.oldestXid = oldest_xid_val;
471 22 : ControlFile.checkPointCopy.oldestXidDB = InvalidOid;
472 : }
473 :
474 230 : if (next_xid_given)
475 : ControlFile.checkPointCopy.nextXid =
476 22 : FullTransactionIdFromEpochAndXid(EpochFromFullTransactionId(ControlFile.checkPointCopy.nextXid),
477 : next_xid_val);
478 :
479 230 : if (commit_ts_xids_given)
480 : {
481 22 : ControlFile.checkPointCopy.oldestCommitTsXid = oldest_commit_ts_xid_val;
482 22 : ControlFile.checkPointCopy.newestCommitTsXid = newest_commit_ts_xid_val;
483 : }
484 :
485 230 : if (next_oid_given)
486 22 : ControlFile.checkPointCopy.nextOid = next_oid_val;
487 :
488 230 : if (mxids_given)
489 : {
490 24 : ControlFile.checkPointCopy.nextMulti = next_mxid_val;
491 :
492 24 : ControlFile.checkPointCopy.oldestMulti = oldest_mxid_val;
493 24 : if (ControlFile.checkPointCopy.oldestMulti < FirstMultiXactId)
494 0 : ControlFile.checkPointCopy.oldestMulti += FirstMultiXactId;
495 24 : ControlFile.checkPointCopy.oldestMultiDB = InvalidOid;
496 : }
497 :
498 230 : if (next_mxoff_given)
499 22 : ControlFile.checkPointCopy.nextMultiOffset = next_mxoff_val;
500 :
501 230 : if (minXlogTli > ControlFile.checkPointCopy.ThisTimeLineID)
502 : {
503 0 : ControlFile.checkPointCopy.ThisTimeLineID = minXlogTli;
504 0 : ControlFile.checkPointCopy.PrevTimeLineID = minXlogTli;
505 : }
506 :
507 230 : if (wal_segsize_given)
508 4 : ControlFile.xlog_seg_size = WalSegSz;
509 :
510 230 : if (char_signedness_given)
511 4 : ControlFile.default_char_signedness = char_signedness_val;
512 :
513 230 : if (minXlogSegNo > newXlogSegNo)
514 6 : newXlogSegNo = minXlogSegNo;
515 :
516 230 : if (noupdate)
517 : {
518 82 : PrintNewControlValues();
519 82 : exit(0);
520 : }
521 :
522 : /*
523 : * If we had to guess anything, and -f was not given, just print the
524 : * guessed values and exit.
525 : */
526 148 : if (guessed && !force)
527 : {
528 2 : PrintNewControlValues();
529 2 : pg_log_error("not proceeding because control file values were guessed");
530 2 : pg_log_error_hint("If these values seem acceptable, use -f to force reset.");
531 2 : exit(1);
532 : }
533 :
534 : /*
535 : * Don't reset from a dirty pg_control without -f, either.
536 : */
537 146 : if (ControlFile.state != DB_SHUTDOWNED && !force)
538 : {
539 2 : pg_log_error("database server was not shut down cleanly");
540 2 : pg_log_error_detail("Resetting the write-ahead log might cause data to be lost.");
541 2 : pg_log_error_hint("If you want to proceed anyway, use -f to force reset.");
542 2 : exit(1);
543 : }
544 :
545 : /*
546 : * Else, do the dirty deed.
547 : */
548 144 : RewriteControlFile();
549 144 : KillExistingXLOG();
550 144 : KillExistingArchiveStatus();
551 144 : KillExistingWALSummaries();
552 144 : WriteEmptyXLOG();
553 :
554 144 : printf(_("Write-ahead log reset\n"));
555 144 : return 0;
556 : }
557 :
558 :
559 : /*
560 : * Look at the version string stored in PG_VERSION and decide if this utility
561 : * can be run safely or not.
562 : *
563 : * We don't want to inject pg_control and WAL files that are for a different
564 : * major version; that can't do anything good. Note that we don't treat
565 : * mismatching version info in pg_control as a reason to bail out, because
566 : * recovering from a corrupted pg_control is one of the main reasons for this
567 : * program to exist at all. However, PG_VERSION is unlikely to get corrupted,
568 : * and if it were it would be easy to fix by hand. So let's make this check
569 : * to prevent simple user errors.
570 : */
571 : static void
572 232 : CheckDataVersion(void)
573 : {
574 : char *version_str;
575 232 : uint32 version = get_pg_version(".", &version_str);
576 :
577 232 : if (GET_PG_MAJORVERSION_NUM(version) != PG_MAJORVERSION_NUM)
578 : {
579 0 : pg_log_error("data directory is of wrong version");
580 0 : pg_log_error_detail("File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
581 : "PG_VERSION",
582 : version_str,
583 : PG_MAJORVERSION);
584 0 : exit(1);
585 : }
586 232 : }
587 :
588 :
589 : /*
590 : * Try to read the existing pg_control file.
591 : *
592 : * This routine is also responsible for updating old pg_control versions
593 : * to the current format. (Currently we don't do anything of the sort.)
594 : */
595 : static bool
596 230 : read_controlfile(void)
597 : {
598 : int fd;
599 : int len;
600 : char *buffer;
601 : pg_crc32c crc;
602 :
603 230 : if ((fd = open(XLOG_CONTROL_FILE, O_RDONLY | PG_BINARY, 0)) < 0)
604 : {
605 : /*
606 : * If pg_control is not there at all, or we can't read it, the odds
607 : * are we've been handed a bad DataDir path, so give up. User can do
608 : * "touch pg_control" to force us to proceed.
609 : */
610 0 : pg_log_error("could not open file \"%s\" for reading: %m",
611 : XLOG_CONTROL_FILE);
612 0 : if (errno == ENOENT)
613 0 : pg_log_error_hint("If you are sure the data directory path is correct, execute\n"
614 : " touch %s\n"
615 : "and try again.",
616 : XLOG_CONTROL_FILE);
617 0 : exit(1);
618 : }
619 :
620 : /* Use malloc to ensure we have a maxaligned buffer */
621 230 : buffer = (char *) pg_malloc(PG_CONTROL_FILE_SIZE);
622 :
623 230 : len = read(fd, buffer, PG_CONTROL_FILE_SIZE);
624 230 : if (len < 0)
625 0 : pg_fatal("could not read file \"%s\": %m", XLOG_CONTROL_FILE);
626 230 : close(fd);
627 :
628 230 : if (len >= sizeof(ControlFileData) &&
629 230 : ((ControlFileData *) buffer)->pg_control_version == PG_CONTROL_VERSION)
630 : {
631 : /* Check the CRC. */
632 228 : INIT_CRC32C(crc);
633 228 : COMP_CRC32C(crc,
634 : buffer,
635 : offsetof(ControlFileData, crc));
636 228 : FIN_CRC32C(crc);
637 :
638 228 : if (!EQ_CRC32C(crc, ((ControlFileData *) buffer)->crc))
639 : {
640 : /* We will use the data but treat it as guessed. */
641 6 : pg_log_warning("pg_control exists but has invalid CRC; proceed with caution");
642 6 : guessed = true;
643 : }
644 :
645 228 : memcpy(&ControlFile, buffer, sizeof(ControlFile));
646 :
647 : /* return false if WAL segment size is not valid */
648 228 : if (!IsValidWalSegSize(ControlFile.xlog_seg_size))
649 : {
650 6 : pg_log_warning(ngettext("pg_control specifies invalid WAL segment size (%d byte); proceed with caution",
651 : "pg_control specifies invalid WAL segment size (%d bytes); proceed with caution",
652 : ControlFile.xlog_seg_size),
653 : ControlFile.xlog_seg_size);
654 6 : return false;
655 : }
656 :
657 222 : return true;
658 : }
659 :
660 : /* Looks like it's a mess. */
661 2 : pg_log_warning("pg_control exists but is broken or wrong version; ignoring it");
662 2 : return false;
663 : }
664 :
665 :
666 : /*
667 : * Guess at pg_control values when we can't read the old ones.
668 : */
669 : static void
670 8 : GuessControlValues(void)
671 : {
672 : uint64 sysidentifier;
673 : struct timeval tv;
674 :
675 : /*
676 : * Set up a completely default set of pg_control values.
677 : */
678 8 : guessed = true;
679 8 : memset(&ControlFile, 0, sizeof(ControlFile));
680 :
681 8 : ControlFile.pg_control_version = PG_CONTROL_VERSION;
682 8 : ControlFile.catalog_version_no = CATALOG_VERSION_NO;
683 :
684 : /*
685 : * Create a new unique installation identifier, since we can no longer use
686 : * any old XLOG records. See notes in xlog.c about the algorithm.
687 : */
688 8 : gettimeofday(&tv, NULL);
689 8 : sysidentifier = ((uint64) tv.tv_sec) << 32;
690 8 : sysidentifier |= ((uint64) tv.tv_usec) << 12;
691 8 : sysidentifier |= getpid() & 0xFFF;
692 :
693 8 : ControlFile.system_identifier = sysidentifier;
694 :
695 8 : ControlFile.checkPointCopy.redo = SizeOfXLogLongPHD;
696 8 : ControlFile.checkPointCopy.ThisTimeLineID = 1;
697 8 : ControlFile.checkPointCopy.PrevTimeLineID = 1;
698 8 : ControlFile.checkPointCopy.fullPageWrites = false;
699 : ControlFile.checkPointCopy.nextXid =
700 8 : FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId);
701 8 : ControlFile.checkPointCopy.nextOid = FirstGenbkiObjectId;
702 8 : ControlFile.checkPointCopy.nextMulti = FirstMultiXactId;
703 8 : ControlFile.checkPointCopy.nextMultiOffset = 0;
704 8 : ControlFile.checkPointCopy.oldestXid = FirstNormalTransactionId;
705 8 : ControlFile.checkPointCopy.oldestXidDB = InvalidOid;
706 8 : ControlFile.checkPointCopy.oldestMulti = FirstMultiXactId;
707 8 : ControlFile.checkPointCopy.oldestMultiDB = InvalidOid;
708 8 : ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
709 8 : ControlFile.checkPointCopy.oldestActiveXid = InvalidTransactionId;
710 :
711 8 : ControlFile.state = DB_SHUTDOWNED;
712 8 : ControlFile.time = (pg_time_t) time(NULL);
713 8 : ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
714 8 : ControlFile.unloggedLSN = FirstNormalUnloggedLSN;
715 :
716 : /* minRecoveryPoint, backupStartPoint and backupEndPoint can be left zero */
717 :
718 8 : ControlFile.wal_level = WAL_LEVEL_MINIMAL;
719 8 : ControlFile.wal_log_hints = false;
720 8 : ControlFile.track_commit_timestamp = false;
721 8 : ControlFile.MaxConnections = 100;
722 8 : ControlFile.max_wal_senders = 10;
723 8 : ControlFile.max_worker_processes = 8;
724 8 : ControlFile.max_prepared_xacts = 0;
725 8 : ControlFile.max_locks_per_xact = 64;
726 :
727 8 : ControlFile.maxAlign = MAXIMUM_ALIGNOF;
728 8 : ControlFile.floatFormat = FLOATFORMAT_VALUE;
729 8 : ControlFile.blcksz = BLCKSZ;
730 8 : ControlFile.relseg_size = RELSEG_SIZE;
731 8 : ControlFile.slru_pages_per_segment = SLRU_PAGES_PER_SEGMENT;
732 8 : ControlFile.xlog_blcksz = XLOG_BLCKSZ;
733 8 : ControlFile.xlog_seg_size = DEFAULT_XLOG_SEG_SIZE;
734 8 : ControlFile.nameDataLen = NAMEDATALEN;
735 8 : ControlFile.indexMaxKeys = INDEX_MAX_KEYS;
736 8 : ControlFile.toast_max_chunk_size = TOAST_MAX_CHUNK_SIZE;
737 8 : ControlFile.loblksize = LOBLKSIZE;
738 8 : ControlFile.float8ByVal = true; /* vestigial */
739 :
740 : /*
741 : * XXX eventually, should try to grovel through old XLOG to develop more
742 : * accurate values for TimeLineID, nextXID, etc.
743 : */
744 8 : }
745 :
746 :
747 : /*
748 : * Print the guessed pg_control values when we had to guess.
749 : *
750 : * NB: this display should be just those fields that will not be
751 : * reset by RewriteControlFile().
752 : */
753 : static void
754 84 : PrintControlValues(bool guessed)
755 : {
756 84 : if (guessed)
757 6 : printf(_("Guessed pg_control values:\n\n"));
758 : else
759 78 : printf(_("Current pg_control values:\n\n"));
760 :
761 84 : printf(_("pg_control version number: %u\n"),
762 : ControlFile.pg_control_version);
763 84 : printf(_("Catalog version number: %u\n"),
764 : ControlFile.catalog_version_no);
765 84 : printf(_("Database system identifier: %" PRIu64 "\n"),
766 : ControlFile.system_identifier);
767 84 : printf(_("Latest checkpoint's TimeLineID: %u\n"),
768 : ControlFile.checkPointCopy.ThisTimeLineID);
769 84 : printf(_("Latest checkpoint's full_page_writes: %s\n"),
770 : ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
771 84 : printf(_("Latest checkpoint's NextXID: %u:%u\n"),
772 : EpochFromFullTransactionId(ControlFile.checkPointCopy.nextXid),
773 : XidFromFullTransactionId(ControlFile.checkPointCopy.nextXid));
774 84 : printf(_("Latest checkpoint's NextOID: %u\n"),
775 : ControlFile.checkPointCopy.nextOid);
776 84 : printf(_("Latest checkpoint's NextMultiXactId: %u\n"),
777 : ControlFile.checkPointCopy.nextMulti);
778 84 : printf(_("Latest checkpoint's NextMultiOffset: %" PRIu64 "\n"),
779 : ControlFile.checkPointCopy.nextMultiOffset);
780 84 : printf(_("Latest checkpoint's oldestXID: %u\n"),
781 : ControlFile.checkPointCopy.oldestXid);
782 84 : printf(_("Latest checkpoint's oldestXID's DB: %u\n"),
783 : ControlFile.checkPointCopy.oldestXidDB);
784 84 : printf(_("Latest checkpoint's oldestActiveXID: %u\n"),
785 : ControlFile.checkPointCopy.oldestActiveXid);
786 84 : printf(_("Latest checkpoint's oldestMultiXid: %u\n"),
787 : ControlFile.checkPointCopy.oldestMulti);
788 84 : printf(_("Latest checkpoint's oldestMulti's DB: %u\n"),
789 : ControlFile.checkPointCopy.oldestMultiDB);
790 84 : printf(_("Latest checkpoint's oldestCommitTsXid:%u\n"),
791 : ControlFile.checkPointCopy.oldestCommitTsXid);
792 84 : printf(_("Latest checkpoint's newestCommitTsXid:%u\n"),
793 : ControlFile.checkPointCopy.newestCommitTsXid);
794 84 : printf(_("Maximum data alignment: %u\n"),
795 : ControlFile.maxAlign);
796 : /* we don't print floatFormat since can't say much useful about it */
797 84 : printf(_("Database block size: %u\n"),
798 : ControlFile.blcksz);
799 84 : printf(_("Blocks per segment of large relation: %u\n"),
800 : ControlFile.relseg_size);
801 84 : printf(_("Pages per SLRU segment: %u\n"),
802 : ControlFile.slru_pages_per_segment);
803 84 : printf(_("WAL block size: %u\n"),
804 : ControlFile.xlog_blcksz);
805 84 : printf(_("Bytes per WAL segment: %u\n"),
806 : ControlFile.xlog_seg_size);
807 84 : printf(_("Maximum length of identifiers: %u\n"),
808 : ControlFile.nameDataLen);
809 84 : printf(_("Maximum columns in an index: %u\n"),
810 : ControlFile.indexMaxKeys);
811 84 : printf(_("Maximum size of a TOAST chunk: %u\n"),
812 : ControlFile.toast_max_chunk_size);
813 84 : printf(_("Size of a large-object chunk: %u\n"),
814 : ControlFile.loblksize);
815 : /* This is no longer configurable, but users may still expect to see it: */
816 84 : printf(_("Date/time type storage: %s\n"),
817 : _("64-bit integers"));
818 84 : printf(_("Float8 argument passing: %s\n"),
819 : (ControlFile.float8ByVal ? _("by value") : _("by reference")));
820 84 : printf(_("Data page checksum version: %u\n"),
821 : ControlFile.data_checksum_version);
822 84 : printf(_("Default char data signedness: %s\n"),
823 : (ControlFile.default_char_signedness ? _("signed") : _("unsigned")));
824 84 : }
825 :
826 :
827 : /*
828 : * Print the values to be changed.
829 : */
830 : static void
831 84 : PrintNewControlValues(void)
832 : {
833 : char fname[MAXFNAMELEN];
834 :
835 : /* This will be always printed in order to keep format same. */
836 84 : printf(_("\n\nValues to be changed:\n\n"));
837 :
838 84 : XLogFileName(fname, ControlFile.checkPointCopy.ThisTimeLineID,
839 : newXlogSegNo, WalSegSz);
840 84 : printf(_("First log segment after reset: %s\n"), fname);
841 :
842 84 : if (mxids_given)
843 : {
844 2 : printf(_("NextMultiXactId: %u\n"),
845 : ControlFile.checkPointCopy.nextMulti);
846 2 : printf(_("OldestMultiXid: %u\n"),
847 : ControlFile.checkPointCopy.oldestMulti);
848 2 : printf(_("OldestMulti's DB: %u\n"),
849 : ControlFile.checkPointCopy.oldestMultiDB);
850 : }
851 :
852 84 : if (next_mxoff_given)
853 : {
854 2 : printf(_("NextMultiOffset: %" PRIu64 "\n"),
855 : ControlFile.checkPointCopy.nextMultiOffset);
856 : }
857 :
858 84 : if (next_oid_given)
859 : {
860 2 : printf(_("NextOID: %u\n"),
861 : ControlFile.checkPointCopy.nextOid);
862 : }
863 :
864 84 : if (next_xid_given)
865 : {
866 2 : printf(_("NextXID: %u\n"),
867 : XidFromFullTransactionId(ControlFile.checkPointCopy.nextXid));
868 : }
869 :
870 84 : if (oldest_xid_given)
871 : {
872 2 : printf(_("OldestXID: %u\n"),
873 : ControlFile.checkPointCopy.oldestXid);
874 2 : printf(_("OldestXID's DB: %u\n"),
875 : ControlFile.checkPointCopy.oldestXidDB);
876 : }
877 :
878 84 : if (next_xid_epoch_given)
879 : {
880 2 : printf(_("NextXID epoch: %u\n"),
881 : EpochFromFullTransactionId(ControlFile.checkPointCopy.nextXid));
882 : }
883 :
884 84 : if (commit_ts_xids_given)
885 : {
886 2 : printf(_("oldestCommitTsXid: %u\n"),
887 : ControlFile.checkPointCopy.oldestCommitTsXid);
888 2 : printf(_("newestCommitTsXid: %u\n"),
889 : ControlFile.checkPointCopy.newestCommitTsXid);
890 : }
891 :
892 84 : if (wal_segsize_given)
893 : {
894 2 : printf(_("Bytes per WAL segment: %u\n"),
895 : ControlFile.xlog_seg_size);
896 : }
897 84 : }
898 :
899 :
900 : /*
901 : * Write out the new pg_control file.
902 : */
903 : static void
904 144 : RewriteControlFile(void)
905 : {
906 : /*
907 : * Adjust fields as needed to force an empty XLOG starting at
908 : * newXlogSegNo.
909 : */
910 144 : XLogSegNoOffsetToRecPtr(newXlogSegNo, SizeOfXLogLongPHD, WalSegSz,
911 : ControlFile.checkPointCopy.redo);
912 144 : ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
913 :
914 144 : ControlFile.state = DB_SHUTDOWNED;
915 144 : ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
916 144 : ControlFile.minRecoveryPoint = 0;
917 144 : ControlFile.minRecoveryPointTLI = 0;
918 144 : ControlFile.backupStartPoint = 0;
919 144 : ControlFile.backupEndPoint = 0;
920 144 : ControlFile.backupEndRequired = false;
921 :
922 : /*
923 : * Force the defaults for max_* settings. The values don't really matter
924 : * as long as wal_level='minimal'; the postmaster will reset these fields
925 : * anyway at startup.
926 : */
927 144 : ControlFile.wal_level = WAL_LEVEL_MINIMAL;
928 144 : ControlFile.wal_log_hints = false;
929 144 : ControlFile.track_commit_timestamp = false;
930 144 : ControlFile.MaxConnections = 100;
931 144 : ControlFile.max_wal_senders = 10;
932 144 : ControlFile.max_worker_processes = 8;
933 144 : ControlFile.max_prepared_xacts = 0;
934 144 : ControlFile.max_locks_per_xact = 64;
935 :
936 : /* The control file gets flushed here. */
937 144 : update_controlfile(".", &ControlFile, true);
938 144 : }
939 :
940 :
941 : /*
942 : * Scan existing XLOG files and determine the highest existing WAL address
943 : *
944 : * On entry, ControlFile.checkPointCopy.redo and ControlFile.xlog_seg_size
945 : * are assumed valid (note that we allow the old xlog seg size to differ
946 : * from what we're using). On exit, newXlogSegNo is set to suitable
947 : * value for the beginning of replacement WAL (in our seg size).
948 : */
949 : static void
950 230 : FindEndOfXLOG(void)
951 : {
952 : DIR *xldir;
953 : struct dirent *xlde;
954 : uint64 xlogbytepos;
955 :
956 : /*
957 : * Initialize the max() computation using the last checkpoint address from
958 : * old pg_control. Note that for the moment we are working with segment
959 : * numbering according to the old xlog seg size.
960 : */
961 230 : XLByteToSeg(ControlFile.checkPointCopy.redo, newXlogSegNo,
962 : ControlFile.xlog_seg_size);
963 :
964 : /*
965 : * Scan the pg_wal directory to find existing WAL segment files. We assume
966 : * any present have been used; in most scenarios this should be
967 : * conservative, because of xlog.c's attempts to pre-create files.
968 : */
969 230 : xldir = opendir(XLOGDIR);
970 230 : if (xldir == NULL)
971 0 : pg_fatal("could not open directory \"%s\": %m", XLOGDIR);
972 :
973 1724 : while (errno = 0, (xlde = readdir(xldir)) != NULL)
974 : {
975 2416 : if (IsXLogFileName(xlde->d_name) ||
976 922 : IsPartialXLogFileName(xlde->d_name))
977 : {
978 : TimeLineID tli;
979 : XLogSegNo segno;
980 :
981 : /* Use the segment size from the control file */
982 572 : XLogFromFileName(xlde->d_name, &tli, &segno,
983 572 : ControlFile.xlog_seg_size);
984 :
985 : /*
986 : * Note: we take the max of all files found, regardless of their
987 : * timelines. Another possibility would be to ignore files of
988 : * timelines other than the target TLI, but this seems safer.
989 : * Better too large a result than too small...
990 : */
991 572 : if (segno > newXlogSegNo)
992 86 : newXlogSegNo = segno;
993 : }
994 : }
995 :
996 230 : if (errno)
997 0 : pg_fatal("could not read directory \"%s\": %m", XLOGDIR);
998 :
999 230 : if (closedir(xldir))
1000 0 : pg_fatal("could not close directory \"%s\": %m", XLOGDIR);
1001 :
1002 : /*
1003 : * Finally, convert to new xlog seg size, and advance by one to ensure we
1004 : * are in virgin territory.
1005 : */
1006 230 : xlogbytepos = newXlogSegNo * ControlFile.xlog_seg_size;
1007 230 : newXlogSegNo = (xlogbytepos + ControlFile.xlog_seg_size - 1) / WalSegSz;
1008 230 : newXlogSegNo++;
1009 230 : }
1010 :
1011 :
1012 : /*
1013 : * Remove existing XLOG files
1014 : */
1015 : static void
1016 144 : KillExistingXLOG(void)
1017 : {
1018 : DIR *xldir;
1019 : struct dirent *xlde;
1020 : char path[MAXPGPATH + sizeof(XLOGDIR)];
1021 :
1022 144 : xldir = opendir(XLOGDIR);
1023 144 : if (xldir == NULL)
1024 0 : pg_fatal("could not open directory \"%s\": %m", XLOGDIR);
1025 :
1026 910 : while (errno = 0, (xlde = readdir(xldir)) != NULL)
1027 : {
1028 1344 : if (IsXLogFileName(xlde->d_name) ||
1029 578 : IsPartialXLogFileName(xlde->d_name))
1030 : {
1031 188 : snprintf(path, sizeof(path), "%s/%s", XLOGDIR, xlde->d_name);
1032 188 : if (unlink(path) < 0)
1033 0 : pg_fatal("could not delete file \"%s\": %m", path);
1034 : }
1035 : }
1036 :
1037 144 : if (errno)
1038 0 : pg_fatal("could not read directory \"%s\": %m", XLOGDIR);
1039 :
1040 144 : if (closedir(xldir))
1041 0 : pg_fatal("could not close directory \"%s\": %m", XLOGDIR);
1042 144 : }
1043 :
1044 :
1045 : /*
1046 : * Remove existing archive status files
1047 : */
1048 : static void
1049 144 : KillExistingArchiveStatus(void)
1050 : {
1051 : #define ARCHSTATDIR XLOGDIR "/archive_status"
1052 :
1053 : DIR *xldir;
1054 : struct dirent *xlde;
1055 : char path[MAXPGPATH + sizeof(ARCHSTATDIR)];
1056 :
1057 144 : xldir = opendir(ARCHSTATDIR);
1058 144 : if (xldir == NULL)
1059 0 : pg_fatal("could not open directory \"%s\": %m", ARCHSTATDIR);
1060 :
1061 432 : while (errno = 0, (xlde = readdir(xldir)) != NULL)
1062 : {
1063 288 : if (strspn(xlde->d_name, "0123456789ABCDEF") == XLOG_FNAME_LEN &&
1064 0 : (strcmp(xlde->d_name + XLOG_FNAME_LEN, ".ready") == 0 ||
1065 0 : strcmp(xlde->d_name + XLOG_FNAME_LEN, ".done") == 0 ||
1066 0 : strcmp(xlde->d_name + XLOG_FNAME_LEN, ".partial.ready") == 0 ||
1067 0 : strcmp(xlde->d_name + XLOG_FNAME_LEN, ".partial.done") == 0))
1068 : {
1069 0 : snprintf(path, sizeof(path), "%s/%s", ARCHSTATDIR, xlde->d_name);
1070 0 : if (unlink(path) < 0)
1071 0 : pg_fatal("could not delete file \"%s\": %m", path);
1072 : }
1073 : }
1074 :
1075 144 : if (errno)
1076 0 : pg_fatal("could not read directory \"%s\": %m", ARCHSTATDIR);
1077 :
1078 144 : if (closedir(xldir))
1079 0 : pg_fatal("could not close directory \"%s\": %m", ARCHSTATDIR);
1080 144 : }
1081 :
1082 : /*
1083 : * Remove existing WAL summary files
1084 : */
1085 : static void
1086 144 : KillExistingWALSummaries(void)
1087 : {
1088 : #define WALSUMMARYDIR XLOGDIR "/summaries"
1089 : #define WALSUMMARY_NHEXCHARS 40
1090 :
1091 : DIR *xldir;
1092 : struct dirent *xlde;
1093 : char path[MAXPGPATH + sizeof(WALSUMMARYDIR)];
1094 :
1095 144 : xldir = opendir(WALSUMMARYDIR);
1096 144 : if (xldir == NULL)
1097 0 : pg_fatal("could not open directory \"%s\": %m", WALSUMMARYDIR);
1098 :
1099 432 : while (errno = 0, (xlde = readdir(xldir)) != NULL)
1100 : {
1101 288 : if (strspn(xlde->d_name, "0123456789ABCDEF") == WALSUMMARY_NHEXCHARS &&
1102 0 : strcmp(xlde->d_name + WALSUMMARY_NHEXCHARS, ".summary") == 0)
1103 : {
1104 0 : snprintf(path, sizeof(path), "%s/%s", WALSUMMARYDIR, xlde->d_name);
1105 0 : if (unlink(path) < 0)
1106 0 : pg_fatal("could not delete file \"%s\": %m", path);
1107 : }
1108 : }
1109 :
1110 144 : if (errno)
1111 0 : pg_fatal("could not read directory \"%s\": %m", WALSUMMARYDIR);
1112 :
1113 144 : if (closedir(xldir))
1114 0 : pg_fatal("could not close directory \"%s\": %m", ARCHSTATDIR);
1115 144 : }
1116 :
1117 : /*
1118 : * Write an empty XLOG file, containing only the checkpoint record
1119 : * already set up in ControlFile.
1120 : */
1121 : static void
1122 144 : WriteEmptyXLOG(void)
1123 : {
1124 : PGAlignedXLogBlock buffer;
1125 : XLogPageHeader page;
1126 : XLogLongPageHeader longpage;
1127 : XLogRecord *record;
1128 : pg_crc32c crc;
1129 : char path[MAXPGPATH];
1130 : int fd;
1131 : int nbytes;
1132 : char *recptr;
1133 :
1134 144 : memset(buffer.data, 0, XLOG_BLCKSZ);
1135 :
1136 : /* Set up the XLOG page header */
1137 144 : page = (XLogPageHeader) buffer.data;
1138 144 : page->xlp_magic = XLOG_PAGE_MAGIC;
1139 144 : page->xlp_info = XLP_LONG_HEADER;
1140 144 : page->xlp_tli = ControlFile.checkPointCopy.ThisTimeLineID;
1141 144 : page->xlp_pageaddr = ControlFile.checkPointCopy.redo - SizeOfXLogLongPHD;
1142 144 : longpage = (XLogLongPageHeader) page;
1143 144 : longpage->xlp_sysid = ControlFile.system_identifier;
1144 144 : longpage->xlp_seg_size = WalSegSz;
1145 144 : longpage->xlp_xlog_blcksz = XLOG_BLCKSZ;
1146 :
1147 : /* Insert the initial checkpoint record */
1148 144 : recptr = (char *) page + SizeOfXLogLongPHD;
1149 144 : record = (XLogRecord *) recptr;
1150 144 : record->xl_prev = 0;
1151 144 : record->xl_xid = InvalidTransactionId;
1152 144 : record->xl_tot_len = SizeOfXLogRecord + SizeOfXLogRecordDataHeaderShort + sizeof(CheckPoint);
1153 144 : record->xl_info = XLOG_CHECKPOINT_SHUTDOWN;
1154 144 : record->xl_rmid = RM_XLOG_ID;
1155 :
1156 144 : recptr += SizeOfXLogRecord;
1157 144 : *(recptr++) = (char) XLR_BLOCK_ID_DATA_SHORT;
1158 144 : *(recptr++) = sizeof(CheckPoint);
1159 144 : memcpy(recptr, &ControlFile.checkPointCopy,
1160 : sizeof(CheckPoint));
1161 :
1162 144 : INIT_CRC32C(crc);
1163 144 : COMP_CRC32C(crc, ((char *) record) + SizeOfXLogRecord, record->xl_tot_len - SizeOfXLogRecord);
1164 144 : COMP_CRC32C(crc, (char *) record, offsetof(XLogRecord, xl_crc));
1165 144 : FIN_CRC32C(crc);
1166 144 : record->xl_crc = crc;
1167 :
1168 : /* Write the first page */
1169 144 : XLogFilePath(path, ControlFile.checkPointCopy.ThisTimeLineID,
1170 : newXlogSegNo, WalSegSz);
1171 :
1172 144 : unlink(path);
1173 :
1174 144 : fd = open(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
1175 : pg_file_create_mode);
1176 144 : if (fd < 0)
1177 0 : pg_fatal("could not open file \"%s\": %m", path);
1178 :
1179 144 : errno = 0;
1180 144 : if (write(fd, buffer.data, XLOG_BLCKSZ) != XLOG_BLCKSZ)
1181 : {
1182 : /* if write didn't set errno, assume problem is no disk space */
1183 0 : if (errno == 0)
1184 0 : errno = ENOSPC;
1185 0 : pg_fatal("could not write file \"%s\": %m", path);
1186 : }
1187 :
1188 : /* Fill the rest of the file with zeroes */
1189 144 : memset(buffer.data, 0, XLOG_BLCKSZ);
1190 264192 : for (nbytes = XLOG_BLCKSZ; nbytes < WalSegSz; nbytes += XLOG_BLCKSZ)
1191 : {
1192 264048 : errno = 0;
1193 264048 : if (write(fd, buffer.data, XLOG_BLCKSZ) != XLOG_BLCKSZ)
1194 : {
1195 0 : if (errno == 0)
1196 0 : errno = ENOSPC;
1197 0 : pg_fatal("could not write file \"%s\": %m", path);
1198 : }
1199 : }
1200 :
1201 144 : if (fsync(fd) != 0)
1202 0 : pg_fatal("fsync error: %m");
1203 :
1204 144 : close(fd);
1205 144 : }
1206 :
1207 :
1208 : static void
1209 2 : usage(void)
1210 : {
1211 2 : printf(_("%s resets the PostgreSQL write-ahead log.\n\n"), progname);
1212 2 : printf(_("Usage:\n"));
1213 2 : printf(_(" %s [OPTION]... DATADIR\n"), progname);
1214 :
1215 2 : printf(_("\nOptions:\n"));
1216 2 : printf(_(" [-D, --pgdata=]DATADIR data directory\n"));
1217 2 : printf(_(" -f, --force force update to be done even after unclean shutdown or\n"
1218 : " if pg_control values had to be guessed\n"));
1219 2 : printf(_(" -n, --dry-run no update, just show what would be done\n"));
1220 2 : printf(_(" -V, --version output version information, then exit\n"));
1221 2 : printf(_(" -?, --help show this help, then exit\n"));
1222 :
1223 2 : printf(_("\nOptions to override control file values:\n"));
1224 2 : printf(_(" -c, --commit-timestamp-ids=XID,XID\n"
1225 : " set oldest and newest transactions bearing\n"
1226 : " commit timestamp (zero means no change)\n"));
1227 2 : printf(_(" -e, --epoch=XIDEPOCH set next transaction ID epoch\n"));
1228 2 : printf(_(" -l, --next-wal-file=WALFILE set minimum starting location for new WAL\n"));
1229 2 : printf(_(" -m, --multixact-ids=MXID,MXID set next and oldest multitransaction ID\n"));
1230 2 : printf(_(" -o, --next-oid=OID set next OID\n"));
1231 2 : printf(_(" -O, --multixact-offset=OFFSET set next multitransaction offset\n"));
1232 2 : printf(_(" -u, --oldest-transaction-id=XID set oldest transaction ID\n"));
1233 2 : printf(_(" -x, --next-transaction-id=XID set next transaction ID\n"));
1234 2 : printf(_(" --char-signedness=OPTION set char signedness to \"signed\" or \"unsigned\"\n"));
1235 2 : printf(_(" --wal-segsize=SIZE size of WAL segments, in megabytes\n"));
1236 :
1237 2 : printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1238 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1239 2 : }
1240 :
1241 : /*
1242 : * strtouint32_strict -- like strtoul(), but returns uint32 and doesn't accept
1243 : * negative values
1244 : */
1245 : static uint32
1246 204 : strtouint32_strict(const char *restrict s, char **restrict endptr, int base)
1247 : {
1248 : unsigned long val;
1249 : bool is_neg;
1250 :
1251 : /* skip leading whitespace */
1252 204 : while (isspace((unsigned char) *s))
1253 0 : s++;
1254 :
1255 : /*
1256 : * Is it negative? We still call strtoul() if it was, to set 'endptr'.
1257 : * (The current callers don't care though.)
1258 : */
1259 204 : is_neg = (*s == '-');
1260 :
1261 204 : val = strtoul(s, endptr, base);
1262 :
1263 : /* reject if it was negative */
1264 204 : if (errno == 0 && is_neg)
1265 : {
1266 6 : errno = ERANGE;
1267 6 : val = 0;
1268 : }
1269 :
1270 : /*
1271 : * reject values larger than UINT32_MAX on platforms where long is 64 bits
1272 : * wide.
1273 : */
1274 204 : if (errno == 0 && val != (uint32) val)
1275 : {
1276 2 : errno = ERANGE;
1277 2 : val = UINT32_MAX;
1278 : }
1279 :
1280 204 : return (uint32) val;
1281 : }
1282 :
1283 : /*
1284 : * strtouint64_strict -- like strtou64(), but doesn't accept negative values
1285 : */
1286 : static uint64
1287 26 : strtouint64_strict(const char *restrict s, char **restrict endptr, int base)
1288 : {
1289 : uint64 val;
1290 : bool is_neg;
1291 :
1292 : /* skip leading whitespace */
1293 26 : while (isspace((unsigned char) *s))
1294 0 : s++;
1295 :
1296 : /*
1297 : * Is it negative? We still call strtou64() if it was, to set 'endptr'.
1298 : * (The current callers don't care though.)
1299 : */
1300 26 : is_neg = (*s == '-');
1301 :
1302 26 : val = strtou64(s, endptr, base);
1303 :
1304 : /* reject if it was negative */
1305 26 : if (errno == 0 && is_neg)
1306 : {
1307 2 : errno = ERANGE;
1308 2 : val = 0;
1309 : }
1310 :
1311 26 : return val;
1312 : }
|