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