TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pg_checksums.c
4 : * Checks, enables or disables page level checksums for an offline
5 : * cluster
6 : *
7 : * Copyright (c) 2010-2023, PostgreSQL Global Development Group
8 : *
9 : * IDENTIFICATION
10 : * src/bin/pg_checksums/pg_checksums.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 :
15 : #include "postgres_fe.h"
16 :
17 : #include <dirent.h>
18 : #include <limits.h>
19 : #include <time.h>
20 : #include <sys/stat.h>
21 : #include <unistd.h>
22 :
23 : #include "access/xlog_internal.h"
24 : #include "common/controldata_utils.h"
25 : #include "common/file_perm.h"
26 : #include "common/file_utils.h"
27 : #include "common/logging.h"
28 : #include "fe_utils/option_utils.h"
29 : #include "getopt_long.h"
30 : #include "pg_getopt.h"
31 : #include "storage/bufpage.h"
32 : #include "storage/checksum.h"
33 : #include "storage/checksum_impl.h"
34 :
35 :
36 : static int64 files_scanned = 0;
37 : static int64 files_written = 0;
38 : static int64 blocks_scanned = 0;
39 : static int64 blocks_written = 0;
40 : static int64 badblocks = 0;
41 : static ControlFileData *ControlFile;
42 :
43 : static char *only_filenode = NULL;
44 : static bool do_sync = true;
45 : static bool verbose = false;
46 : static bool showprogress = false;
47 :
48 : typedef enum
49 : {
50 : PG_MODE_CHECK,
51 : PG_MODE_DISABLE,
52 : PG_MODE_ENABLE
53 : } PgChecksumMode;
54 :
55 : /*
56 : * Filename components.
57 : *
58 : * XXX: fd.h is not declared here as frontend side code is not able to
59 : * interact with the backend-side definitions for the various fsync
60 : * wrappers.
61 : */
62 : #define PG_TEMP_FILES_DIR "pgsql_tmp"
63 : #define PG_TEMP_FILE_PREFIX "pgsql_tmp"
64 :
65 : static PgChecksumMode mode = PG_MODE_CHECK;
66 :
67 : static const char *progname;
68 :
69 : /*
70 : * Progress status information.
71 : */
72 : int64 total_size = 0;
73 : int64 current_size = 0;
74 : static pg_time_t last_progress_report = 0;
75 :
76 : static void
77 CBC 1 : usage(void)
78 : {
79 1 : printf(_("%s enables, disables, or verifies data checksums in a PostgreSQL database cluster.\n\n"), progname);
80 1 : printf(_("Usage:\n"));
81 1 : printf(_(" %s [OPTION]... [DATADIR]\n"), progname);
82 1 : printf(_("\nOptions:\n"));
83 1 : printf(_(" [-D, --pgdata=]DATADIR data directory\n"));
84 1 : printf(_(" -c, --check check data checksums (default)\n"));
85 1 : printf(_(" -d, --disable disable data checksums\n"));
86 1 : printf(_(" -e, --enable enable data checksums\n"));
87 1 : printf(_(" -f, --filenode=FILENODE check only relation with specified filenode\n"));
88 1 : printf(_(" -N, --no-sync do not wait for changes to be written safely to disk\n"));
89 1 : printf(_(" -P, --progress show progress information\n"));
90 1 : printf(_(" -v, --verbose output verbose messages\n"));
91 1 : printf(_(" -V, --version output version information, then exit\n"));
92 1 : printf(_(" -?, --help show this help, then exit\n"));
93 1 : printf(_("\nIf no data directory (DATADIR) is specified, "
94 : "the environment variable PGDATA\nis used.\n\n"));
95 1 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
96 1 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
97 1 : }
98 :
99 : /*
100 : * Definition of one element part of an exclusion list, used for files
101 : * to exclude from checksum validation. "name" is the name of the file
102 : * or path to check for exclusion. If "match_prefix" is true, any items
103 : * matching the name as prefix are excluded.
104 : */
105 : struct exclude_list_item
106 : {
107 : const char *name;
108 : bool match_prefix;
109 : };
110 :
111 : /*
112 : * List of files excluded from checksum validation.
113 : *
114 : * Note: this list should be kept in sync with what basebackup.c includes.
115 : */
116 : static const struct exclude_list_item skip[] = {
117 : {"pg_control", false},
118 : {"pg_filenode.map", false},
119 : {"pg_internal.init", true},
120 : {"PG_VERSION", false},
121 : #ifdef EXEC_BACKEND
122 : {"config_exec_params", true},
123 : #endif
124 : {NULL, false}
125 : };
126 :
127 : /*
128 : * Report current progress status. Parts borrowed from
129 : * src/bin/pg_basebackup/pg_basebackup.c.
130 : */
131 : static void
132 UBC 0 : progress_report(bool finished)
133 : {
134 : int percent;
135 : pg_time_t now;
136 :
137 0 : Assert(showprogress);
138 :
139 0 : now = time(NULL);
140 0 : if (now == last_progress_report && !finished)
141 0 : return; /* Max once per second */
142 :
143 : /* Save current time */
144 0 : last_progress_report = now;
145 :
146 : /* Adjust total size if current_size is larger */
147 0 : if (current_size > total_size)
148 0 : total_size = current_size;
149 :
150 : /* Calculate current percentage of size done */
151 0 : percent = total_size ? (int) ((current_size) * 100 / total_size) : 0;
152 :
153 0 : fprintf(stderr, _("%lld/%lld MB (%d%%) computed"),
154 0 : (long long) (current_size / (1024 * 1024)),
155 0 : (long long) (total_size / (1024 * 1024)),
156 : percent);
157 :
158 : /*
159 : * Stay on the same line if reporting to a terminal and we're not done
160 : * yet.
161 : */
162 0 : fputc((!finished && isatty(fileno(stderr))) ? '\r' : '\n', stderr);
163 : }
164 :
165 : static bool
166 CBC 12858 : skipfile(const char *fn)
167 : {
168 : int excludeIdx;
169 :
170 63932 : for (excludeIdx = 0; skip[excludeIdx].name != NULL; excludeIdx++)
171 : {
172 51224 : int cmplen = strlen(skip[excludeIdx].name);
173 :
174 51224 : if (!skip[excludeIdx].match_prefix)
175 38442 : cmplen++;
176 51224 : if (strncmp(skip[excludeIdx].name, fn, cmplen) == 0)
177 150 : return true;
178 : }
179 :
180 12708 : return false;
181 : }
182 :
183 : static void
184 8884 : scan_file(const char *fn, int segmentno)
185 : {
186 : PGIOAlignedBlock buf;
187 8884 : PageHeader header = (PageHeader) buf.data;
188 : int f;
189 : BlockNumber blockno;
190 : int flags;
191 8884 : int64 blocks_written_in_file = 0;
192 :
193 8884 : Assert(mode == PG_MODE_ENABLE ||
194 : mode == PG_MODE_CHECK);
195 :
196 8884 : flags = (mode == PG_MODE_ENABLE) ? O_RDWR : O_RDONLY;
197 8884 : f = open(fn, PG_BINARY | flags, 0);
198 :
199 8884 : if (f < 0)
200 UBC 0 : pg_fatal("could not open file \"%s\": %m", fn);
201 :
202 CBC 8884 : files_scanned++;
203 :
204 8884 : for (blockno = 0;; blockno++)
205 26402 : {
206 : uint16 csum;
207 35286 : int r = read(f, buf.data, BLCKSZ);
208 :
209 35286 : if (r == 0)
210 8876 : break;
211 26410 : if (r != BLCKSZ)
212 : {
213 8 : if (r < 0)
214 UBC 0 : pg_fatal("could not read block %u in file \"%s\": %m",
215 : blockno, fn);
216 : else
217 CBC 8 : pg_fatal("could not read block %u in file \"%s\": read %d of %d",
218 : blockno, fn, r, BLCKSZ);
219 : }
220 26402 : blocks_scanned++;
221 :
222 : /*
223 : * Since the file size is counted as total_size for progress status
224 : * information, the sizes of all pages including new ones in the file
225 : * should be counted as current_size. Otherwise the progress reporting
226 : * calculated using those counters may not reach 100%.
227 : */
228 26402 : current_size += r;
229 :
230 : /* New pages have no checksum yet */
231 GNC 26402 : if (PageIsNew(buf.data))
232 UBC 0 : continue;
233 :
234 CBC 26402 : csum = pg_checksum_page(buf.data, blockno + segmentno * RELSEG_SIZE);
235 26402 : if (mode == PG_MODE_CHECK)
236 : {
237 20654 : if (csum != header->pd_checksum)
238 : {
239 4 : if (ControlFile->data_checksum_version == PG_DATA_CHECKSUM_VERSION)
240 4 : pg_log_error("checksum verification failed in file \"%s\", block %u: calculated checksum %X but block contains %X",
241 : fn, blockno, csum, header->pd_checksum);
242 4 : badblocks++;
243 : }
244 : }
245 5748 : else if (mode == PG_MODE_ENABLE)
246 : {
247 : int w;
248 :
249 : /*
250 : * Do not rewrite if the checksum is already set to the expected
251 : * value.
252 : */
253 5748 : if (header->pd_checksum == csum)
254 2874 : continue;
255 :
256 2874 : blocks_written_in_file++;
257 :
258 : /* Set checksum in page header */
259 2874 : header->pd_checksum = csum;
260 :
261 : /* Seek back to beginning of block */
262 2874 : if (lseek(f, -BLCKSZ, SEEK_CUR) < 0)
263 UBC 0 : pg_fatal("seek failed for block %u in file \"%s\": %m", blockno, fn);
264 :
265 : /* Write block with checksum */
266 CBC 2874 : w = write(f, buf.data, BLCKSZ);
267 2874 : if (w != BLCKSZ)
268 : {
269 UBC 0 : if (w < 0)
270 0 : pg_fatal("could not write block %u in file \"%s\": %m",
271 : blockno, fn);
272 : else
273 0 : pg_fatal("could not write block %u in file \"%s\": wrote %d of %d",
274 : blockno, fn, w, BLCKSZ);
275 : }
276 : }
277 :
278 CBC 23528 : if (showprogress)
279 UBC 0 : progress_report(false);
280 : }
281 :
282 CBC 8876 : if (verbose)
283 : {
284 UBC 0 : if (mode == PG_MODE_CHECK)
285 0 : pg_log_info("checksums verified in file \"%s\"", fn);
286 0 : if (mode == PG_MODE_ENABLE)
287 0 : pg_log_info("checksums enabled in file \"%s\"", fn);
288 : }
289 :
290 : /* Update write counters if any write activity has happened */
291 CBC 8876 : if (blocks_written_in_file > 0)
292 : {
293 780 : files_written++;
294 780 : blocks_written += blocks_written_in_file;
295 : }
296 :
297 8876 : close(f);
298 8876 : }
299 :
300 : /*
301 : * Scan the given directory for items which can be checksummed and
302 : * operate on each one of them. If "sizeonly" is true, the size of
303 : * all the items which have checksums is computed and returned back
304 : * to the caller without operating on the files. This is used to compile
305 : * the total size of the data directory for progress reports.
306 : */
307 : static int64
308 96 : scan_directory(const char *basedir, const char *subdir, bool sizeonly)
309 : {
310 96 : int64 dirsize = 0;
311 : char path[MAXPGPATH];
312 : DIR *dir;
313 : struct dirent *de;
314 :
315 96 : snprintf(path, sizeof(path), "%s/%s", basedir, subdir);
316 96 : dir = opendir(path);
317 96 : if (!dir)
318 UBC 0 : pg_fatal("could not open directory \"%s\": %m", path);
319 CBC 13223 : while ((de = readdir(dir)) != NULL)
320 : {
321 : char fn[MAXPGPATH];
322 : struct stat st;
323 :
324 13135 : if (strcmp(de->d_name, ".") == 0 ||
325 13039 : strcmp(de->d_name, "..") == 0)
326 4202 : continue;
327 :
328 : /* Skip temporary files */
329 12943 : if (strncmp(de->d_name,
330 : PG_TEMP_FILE_PREFIX,
331 : strlen(PG_TEMP_FILE_PREFIX)) == 0)
332 36 : continue;
333 :
334 : /* Skip temporary folders */
335 12907 : if (strncmp(de->d_name,
336 : PG_TEMP_FILES_DIR,
337 : strlen(PG_TEMP_FILES_DIR)) == 0)
338 UBC 0 : continue;
339 :
340 CBC 12907 : snprintf(fn, sizeof(fn), "%s/%s", path, de->d_name);
341 12907 : if (lstat(fn, &st) < 0)
342 UBC 0 : pg_fatal("could not stat file \"%s\": %m", fn);
343 CBC 12907 : if (S_ISREG(st.st_mode))
344 : {
345 : char fnonly[MAXPGPATH];
346 : char *forkpath,
347 : *segmentpath;
348 12858 : int segmentno = 0;
349 :
350 12858 : if (skipfile(de->d_name))
351 3974 : continue;
352 :
353 : /*
354 : * Cut off at the segment boundary (".") to get the segment number
355 : * in order to mix it into the checksum. Then also cut off at the
356 : * fork boundary, to get the filenode the file belongs to for
357 : * filtering.
358 : */
359 12708 : strlcpy(fnonly, de->d_name, sizeof(fnonly));
360 12708 : segmentpath = strchr(fnonly, '.');
361 12708 : if (segmentpath != NULL)
362 : {
363 72 : *segmentpath++ = '\0';
364 72 : segmentno = atoi(segmentpath);
365 72 : if (segmentno == 0)
366 UBC 0 : pg_fatal("invalid segment number %d in file name \"%s\"",
367 : segmentno, fn);
368 : }
369 :
370 CBC 12708 : forkpath = strchr(fnonly, '_');
371 12708 : if (forkpath != NULL)
372 3145 : *forkpath++ = '\0';
373 :
374 12708 : if (only_filenode && strcmp(only_filenode, fnonly) != 0)
375 : /* filenode not to be included */
376 3824 : continue;
377 :
378 8884 : dirsize += st.st_size;
379 :
380 : /*
381 : * No need to work on the file when calculating only the size of
382 : * the items in the data folder.
383 : */
384 8884 : if (!sizeonly)
385 8884 : scan_file(fn, segmentno);
386 : }
387 GIC 49 : else if (S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))
388 : {
389 : /*
390 : * If going through the entries of pg_tblspc, we assume to operate
391 ECB : * on tablespace locations where only TABLESPACE_VERSION_DIRECTORY
392 : * is valid, resolving the linked locations and dive into them
393 : * directly.
394 : */
395 GIC 49 : if (strncmp("pg_tblspc", subdir, strlen("pg_tblspc")) == 0)
396 : {
397 : char tblspc_path[MAXPGPATH];
398 : struct stat tblspc_st;
399 :
400 : /*
401 : * Resolve tablespace location path and check whether
402 : * TABLESPACE_VERSION_DIRECTORY exists. Not finding a valid
403 ECB : * location is unexpected, since there should be no orphaned
404 : * links and no links pointing to something else than a
405 : * directory.
406 : */
407 GBC 5 : snprintf(tblspc_path, sizeof(tblspc_path), "%s/%s/%s",
408 GIC 5 : path, de->d_name, TABLESPACE_VERSION_DIRECTORY);
409 :
410 5 : if (lstat(tblspc_path, &tblspc_st) < 0)
411 UIC 0 : pg_fatal("could not stat file \"%s\": %m",
412 : tblspc_path);
413 :
414 ECB : /*
415 : * Move backwards once as the scan needs to happen for the
416 : * contents of TABLESPACE_VERSION_DIRECTORY.
417 : */
418 CBC 5 : snprintf(tblspc_path, sizeof(tblspc_path), "%s/%s",
419 GIC 5 : path, de->d_name);
420 :
421 : /* Looks like a valid tablespace location */
422 5 : dirsize += scan_directory(tblspc_path,
423 : TABLESPACE_VERSION_DIRECTORY,
424 ECB : sizeonly);
425 : }
426 : else
427 : {
428 CBC 44 : dirsize += scan_directory(path, de->d_name, sizeonly);
429 ECB : }
430 : }
431 : }
432 GIC 88 : closedir(dir);
433 CBC 88 : return dirsize;
434 : }
435 :
436 : int
437 GIC 31 : main(int argc, char *argv[])
438 : {
439 : static struct option long_options[] = {
440 : {"check", no_argument, NULL, 'c'},
441 : {"pgdata", required_argument, NULL, 'D'},
442 : {"disable", no_argument, NULL, 'd'},
443 : {"enable", no_argument, NULL, 'e'},
444 : {"filenode", required_argument, NULL, 'f'},
445 : {"no-sync", no_argument, NULL, 'N'},
446 : {"progress", no_argument, NULL, 'P'},
447 ECB : {"verbose", no_argument, NULL, 'v'},
448 : {NULL, 0, NULL, 0}
449 : };
450 :
451 GIC 31 : char *DataDir = NULL;
452 ECB : int c;
453 : int option_index;
454 : bool crc_ok;
455 :
456 CBC 31 : pg_logging_init(argv[0]);
457 GIC 31 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_checksums"));
458 CBC 31 : progname = get_progname(argv[0]);
459 :
460 31 : if (argc > 1)
461 ECB : {
462 GIC 31 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
463 ECB : {
464 GIC 1 : usage();
465 CBC 1 : exit(0);
466 ECB : }
467 GIC 30 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
468 : {
469 1 : puts("pg_checksums (PostgreSQL) " PG_VERSION);
470 CBC 1 : exit(0);
471 : }
472 ECB : }
473 :
474 GNC 93 : while ((c = getopt_long(argc, argv, "cdD:ef:NPv", long_options, &option_index)) != -1)
475 ECB : {
476 CBC 65 : switch (c)
477 ECB : {
478 CBC 19 : case 'c':
479 19 : mode = PG_MODE_CHECK;
480 19 : break;
481 3 : case 'd':
482 3 : mode = PG_MODE_DISABLE;
483 3 : break;
484 GNC 28 : case 'D':
485 28 : DataDir = optarg;
486 28 : break;
487 CBC 4 : case 'e':
488 4 : mode = PG_MODE_ENABLE;
489 4 : break;
490 6 : case 'f':
491 GIC 6 : if (!option_parse_int(optarg, "-f/--filenode", 0,
492 : INT_MAX,
493 EUB : NULL))
494 LBC 0 : exit(1);
495 CBC 6 : only_filenode = pstrdup(optarg);
496 6 : break;
497 4 : case 'N':
498 4 : do_sync = false;
499 GBC 4 : break;
500 UIC 0 : case 'P':
501 LBC 0 : showprogress = true;
502 0 : break;
503 UNC 0 : case 'v':
504 0 : verbose = true;
505 0 : break;
506 GIC 1 : default:
507 : /* getopt_long already emitted a complaint */
508 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
509 CBC 1 : exit(1);
510 : }
511 EUB : }
512 :
513 GIC 28 : if (DataDir == NULL)
514 EUB : {
515 UIC 0 : if (optind < argc)
516 0 : DataDir = argv[optind++];
517 EUB : else
518 UIC 0 : DataDir = getenv("PGDATA");
519 EUB :
520 : /* If no DataDir was specified, and none could be found, error out */
521 UBC 0 : if (DataDir == NULL)
522 : {
523 UIC 0 : pg_log_error("no data directory specified");
524 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
525 0 : exit(1);
526 ECB : }
527 : }
528 EUB :
529 : /* Complain if any arguments remain */
530 GBC 28 : if (optind < argc)
531 EUB : {
532 UIC 0 : pg_log_error("too many command-line arguments (first is \"%s\")",
533 : argv[optind]);
534 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
535 LBC 0 : exit(1);
536 : }
537 ECB :
538 : /* filenode checking only works in --check mode */
539 CBC 28 : if (mode != PG_MODE_CHECK && only_filenode)
540 : {
541 GIC 2 : pg_log_error("option -f/--filenode can only be used with --check");
542 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
543 CBC 2 : exit(1);
544 ECB : }
545 EUB :
546 : /* Read the control file and check compatibility */
547 CBC 26 : ControlFile = get_controlfile(DataDir, &crc_ok);
548 GBC 26 : if (!crc_ok)
549 UIC 0 : pg_fatal("pg_control CRC value is incorrect");
550 ECB :
551 GIC 26 : if (ControlFile->pg_control_version != PG_CONTROL_VERSION)
552 UBC 0 : pg_fatal("cluster is not compatible with this version of pg_checksums");
553 EUB :
554 GIC 26 : if (ControlFile->blcksz != BLCKSZ)
555 EUB : {
556 UIC 0 : pg_log_error("database cluster is not compatible");
557 0 : pg_log_error_detail("The database cluster was initialized with block size %u, but pg_checksums was compiled with block size %u.",
558 : ControlFile->blcksz, BLCKSZ);
559 0 : exit(1);
560 : }
561 :
562 : /*
563 ECB : * Check if cluster is running. A clean shutdown is required to avoid
564 : * random checksum failures caused by torn pages. Note that this doesn't
565 : * guard against someone starting the cluster concurrently.
566 : */
567 CBC 26 : if (ControlFile->state != DB_SHUTDOWNED &&
568 1 : ControlFile->state != DB_SHUTDOWNED_IN_RECOVERY)
569 1 : pg_fatal("cluster must be shut down");
570 :
571 25 : if (ControlFile->data_checksum_version == 0 &&
572 4 : mode == PG_MODE_CHECK)
573 1 : pg_fatal("data checksums are not enabled in cluster");
574 :
575 24 : if (ControlFile->data_checksum_version == 0 &&
576 3 : mode == PG_MODE_DISABLE)
577 1 : pg_fatal("data checksums are already disabled in cluster");
578 :
579 GIC 23 : if (ControlFile->data_checksum_version > 0 &&
580 CBC 21 : mode == PG_MODE_ENABLE)
581 GIC 1 : pg_fatal("data checksums are already enabled in cluster");
582 :
583 : /* Operate on all files if checking or enabling checksums */
584 22 : if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE)
585 : {
586 : /*
587 ECB : * If progress status information is requested, we need to scan the
588 : * directory tree twice: once to know how much total data needs to be
589 EUB : * processed and once to do the real work.
590 : */
591 GBC 21 : if (showprogress)
592 : {
593 UIC 0 : total_size = scan_directory(DataDir, "global", true);
594 LBC 0 : total_size += scan_directory(DataDir, "base", true);
595 0 : total_size += scan_directory(DataDir, "pg_tblspc", true);
596 ECB : }
597 :
598 CBC 21 : (void) scan_directory(DataDir, "global", false);
599 GBC 13 : (void) scan_directory(DataDir, "base", false);
600 GIC 13 : (void) scan_directory(DataDir, "pg_tblspc", false);
601 ECB :
602 CBC 13 : if (showprogress)
603 LBC 0 : progress_report(true);
604 ECB :
605 GIC 13 : printf(_("Checksum operation completed\n"));
606 CBC 13 : printf(_("Files scanned: %lld\n"), (long long) files_scanned);
607 13 : printf(_("Blocks scanned: %lld\n"), (long long) blocks_scanned);
608 GIC 13 : if (mode == PG_MODE_CHECK)
609 ECB : {
610 CBC 11 : printf(_("Bad checksums: %lld\n"), (long long) badblocks);
611 GIC 11 : printf(_("Data checksum version: %u\n"), ControlFile->data_checksum_version);
612 ECB :
613 GIC 11 : if (badblocks > 0)
614 CBC 4 : exit(1);
615 ECB : }
616 GIC 2 : else if (mode == PG_MODE_ENABLE)
617 : {
618 2 : printf(_("Files written: %lld\n"), (long long) files_written);
619 2 : printf(_("Blocks written: %lld\n"), (long long) blocks_written);
620 : }
621 : }
622 :
623 : /*
624 ECB : * Finally make the data durable on disk if enabling or disabling
625 : * checksums. Flush first the data directory for safety, and then update
626 : * the control file to keep the switch consistent.
627 : */
628 GIC 10 : if (mode == PG_MODE_ENABLE || mode == PG_MODE_DISABLE)
629 ECB : {
630 GIC 3 : ControlFile->data_checksum_version =
631 CBC 3 : (mode == PG_MODE_ENABLE) ? PG_DATA_CHECKSUM_VERSION : 0;
632 ECB :
633 GIC 3 : if (do_sync)
634 : {
635 CBC 1 : pg_log_info("syncing data directory");
636 1 : fsync_pgdata(DataDir, PG_VERSION_NUM);
637 : }
638 ECB :
639 GBC 3 : pg_log_info("updating control file");
640 CBC 3 : update_controlfile(DataDir, ControlFile, do_sync);
641 ECB :
642 GIC 3 : if (verbose)
643 LBC 0 : printf(_("Data checksum version: %u\n"), ControlFile->data_checksum_version);
644 GIC 3 : if (mode == PG_MODE_ENABLE)
645 2 : printf(_("Checksums enabled in cluster\n"));
646 ECB : else
647 GIC 1 : printf(_("Checksums disabled in cluster\n"));
648 : }
649 :
650 10 : return 0;
651 : }
|