Age Owner 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
1682 tgl 77 CBC 1 : usage(void)
78 : {
1378 peter 79 1 : printf(_("%s enables, disables, or verifies data checksums in a PostgreSQL database cluster.\n\n"), progname);
1830 magnus 80 1 : printf(_("Usage:\n"));
1690 peter_e 81 1 : printf(_(" %s [OPTION]... [DATADIR]\n"), progname);
1830 magnus 82 1 : printf(_("\nOptions:\n"));
1410 michael 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"));
1830 magnus 93 1 : printf(_("\nIf no data directory (DATADIR) is specified, "
94 : "the environment variable PGDATA\nis used.\n\n"));
1136 peter 95 1 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
96 1 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1830 magnus 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
965 heikki.linnakangas 132 UBC 0 : progress_report(bool finished)
133 : {
134 : int percent;
135 : pg_time_t now;
136 :
1468 michael 137 0 : Assert(showprogress);
138 :
139 0 : now = time(NULL);
965 heikki.linnakangas 140 0 : if (now == last_progress_report && !finished)
1468 michael 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 :
384 tgl 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 : */
964 heikki.linnakangas 162 0 : fputc((!finished && isatty(fileno(stderr))) ? '\r' : '\n', stderr);
163 : }
164 :
165 : static bool
1591 michael 166 CBC 12858 : skipfile(const char *fn)
167 : {
168 : int excludeIdx;
169 :
1140 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)
1591 177 150 : return true;
178 : }
179 :
180 12708 : return false;
181 : }
182 :
183 : static void
480 peter 184 8884 : scan_file(const char *fn, int segmentno)
185 : {
186 : PGIOAlignedBlock buf;
1681 tgl 187 8884 : PageHeader header = (PageHeader) buf.data;
188 : int f;
189 : BlockNumber blockno;
190 : int flags;
648 michael 191 8884 : int64 blocks_written_in_file = 0;
192 :
1478 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 :
1830 magnus 199 8884 : if (f < 0)
366 tgl 200 UBC 0 : pg_fatal("could not open file \"%s\": %m", fn);
201 :
648 michael 202 CBC 8884 : files_scanned++;
203 :
1830 magnus 204 8884 : for (blockno = 0;; blockno++)
205 26402 : {
206 : uint16 csum;
1681 tgl 207 35286 : int r = read(f, buf.data, BLCKSZ);
208 :
1830 magnus 209 35286 : if (r == 0)
210 8876 : break;
211 26410 : if (r != BLCKSZ)
212 : {
1314 peter 213 8 : if (r < 0)
366 tgl 214 UBC 0 : pg_fatal("could not read block %u in file \"%s\": %m",
215 : blockno, fn);
216 : else
366 tgl 217 CBC 8 : pg_fatal("could not read block %u in file \"%s\": read %d of %d",
218 : blockno, fn, r, BLCKSZ);
219 : }
648 michael 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 : */
736 fujii 228 26402 : current_size += r;
229 :
230 : /* New pages have no checksum yet */
272 peter 231 GNC 26402 : if (PageIsNew(buf.data))
1820 magnus 232 UBC 0 : continue;
233 :
1681 tgl 234 CBC 26402 : csum = pg_checksum_page(buf.data, blockno + segmentno * RELSEG_SIZE);
1478 michael 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)
1469 peter 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);
1478 michael 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 : */
648 253 5748 : if (header->pd_checksum == csum)
254 2874 : continue;
255 :
256 2874 : blocks_written_in_file++;
257 :
258 : /* Set checksum in page header */
1478 259 2874 : header->pd_checksum = csum;
260 :
261 : /* Seek back to beginning of block */
262 2874 : if (lseek(f, -BLCKSZ, SEEK_CUR) < 0)
366 tgl 263 UBC 0 : pg_fatal("seek failed for block %u in file \"%s\": %m", blockno, fn);
264 :
265 : /* Write block with checksum */
1314 peter 266 CBC 2874 : w = write(f, buf.data, BLCKSZ);
267 2874 : if (w != BLCKSZ)
268 : {
1314 peter 269 UBC 0 : if (w < 0)
366 tgl 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 :
1468 michael 278 CBC 23528 : if (showprogress)
1468 michael 279 UBC 0 : progress_report(false);
280 : }
281 :
1683 alvherre 282 CBC 8876 : if (verbose)
283 : {
1478 michael 284 UBC 0 : if (mode == PG_MODE_CHECK)
1469 peter 285 0 : pg_log_info("checksums verified in file \"%s\"", fn);
1478 michael 286 0 : if (mode == PG_MODE_ENABLE)
1469 peter 287 0 : pg_log_info("checksums enabled in file \"%s\"", fn);
288 : }
289 :
290 : /* Update write counters if any write activity has happened */
648 michael 291 CBC 8876 : if (blocks_written_in_file > 0)
292 : {
293 780 : files_written++;
294 780 : blocks_written += blocks_written_in_file;
295 : }
296 :
1830 magnus 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
1468 michael 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 :
1829 peter_e 315 96 : snprintf(path, sizeof(path), "%s/%s", basedir, subdir);
1830 magnus 316 96 : dir = opendir(path);
317 96 : if (!dir)
366 tgl 318 UBC 0 : pg_fatal("could not open directory \"%s\": %m", path);
1830 magnus 319 CBC 13223 : while ((de = readdir(dir)) != NULL)
320 : {
321 : char fn[MAXPGPATH];
322 : struct stat st;
323 :
1591 michael 324 13135 : if (strcmp(de->d_name, ".") == 0 ||
325 13039 : strcmp(de->d_name, "..") == 0)
1830 magnus 326 4202 : continue;
327 :
328 : /* Skip temporary files */
1591 michael 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)
1487 michael 338 UBC 0 : continue;
339 :
1829 peter_e 340 CBC 12907 : snprintf(fn, sizeof(fn), "%s/%s", path, de->d_name);
1830 magnus 341 12907 : if (lstat(fn, &st) < 0)
366 tgl 342 UBC 0 : pg_fatal("could not stat file \"%s\": %m", fn);
1830 magnus 343 CBC 12907 : if (S_ISREG(st.st_mode))
344 : {
345 : char fnonly[MAXPGPATH];
346 : char *forkpath,
347 : *segmentpath;
480 peter 348 12858 : int segmentno = 0;
349 :
1591 michael 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 : */
1682 tgl 359 12708 : strlcpy(fnonly, de->d_name, sizeof(fnonly));
360 12708 : segmentpath = strchr(fnonly, '.');
1830 magnus 361 12708 : if (segmentpath != NULL)
362 : {
363 72 : *segmentpath++ = '\0';
364 72 : segmentno = atoi(segmentpath);
365 72 : if (segmentno == 0)
366 tgl 366 UBC 0 : pg_fatal("invalid segment number %d in file name \"%s\"",
367 : segmentno, fn);
368 : }
369 :
1682 tgl 370 CBC 12708 : forkpath = strchr(fnonly, '_');
1830 magnus 371 12708 : if (forkpath != NULL)
372 3145 : *forkpath++ = '\0';
373 :
1410 michael 374 12708 : if (only_filenode && strcmp(only_filenode, fnonly) != 0)
375 : /* filenode not to be included */
1830 magnus 376 3824 : continue;
377 :
1468 michael 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 : }
1830 magnus 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
1137 michael 391 ECB : * on tablespace locations where only TABLESPACE_VERSION_DIRECTORY
392 : * is valid, resolving the linked locations and dive into them
393 : * directly.
394 : */
1137 michael 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
1137 michael 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 : */
1137 michael 407 GBC 5 : snprintf(tblspc_path, sizeof(tblspc_path), "%s/%s/%s",
1137 michael 408 GIC 5 : path, de->d_name, TABLESPACE_VERSION_DIRECTORY);
409 :
410 5 : if (lstat(tblspc_path, &tblspc_st) < 0)
366 tgl 411 UIC 0 : pg_fatal("could not stat file \"%s\": %m",
412 : tblspc_path);
413 :
1137 michael 414 ECB : /*
415 : * Move backwards once as the scan needs to happen for the
416 : * contents of TABLESPACE_VERSION_DIRECTORY.
417 : */
1137 michael 418 CBC 5 : snprintf(tblspc_path, sizeof(tblspc_path), "%s/%s",
1137 michael 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,
1137 michael 424 ECB : sizeonly);
425 : }
426 : else
427 : {
1137 michael 428 CBC 44 : dirsize += scan_directory(path, de->d_name, sizeonly);
1137 michael 429 ECB : }
430 : }
431 : }
1830 magnus 432 GIC 88 : closedir(dir);
1468 michael 433 CBC 88 : return dirsize;
434 : }
435 :
436 : int
1830 magnus 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'},
1683 alvherre 447 ECB : {"verbose", no_argument, NULL, 'v'},
448 : {NULL, 0, NULL, 0}
449 : };
450 :
1830 magnus 451 GIC 31 : char *DataDir = NULL;
1830 magnus 452 ECB : int c;
1754 453 : int option_index;
1830 454 : bool crc_ok;
455 :
1469 peter 456 CBC 31 : pg_logging_init(argv[0]);
1488 michael 457 GIC 31 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_checksums"));
1830 magnus 458 CBC 31 : progname = get_progname(argv[0]);
459 :
460 31 : if (argc > 1)
1830 magnus 461 ECB : {
1830 magnus 462 GIC 31 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
1830 magnus 463 ECB : {
1830 magnus 464 GIC 1 : usage();
1830 magnus 465 CBC 1 : exit(0);
1830 magnus 466 ECB : }
1830 magnus 467 GIC 30 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
468 : {
1488 michael 469 1 : puts("pg_checksums (PostgreSQL) " PG_VERSION);
1830 magnus 470 CBC 1 : exit(0);
471 : }
1830 magnus 472 ECB : }
473 :
118 peter 474 GNC 93 : while ((c = getopt_long(argc, argv, "cdD:ef:NPv", long_options, &option_index)) != -1)
1830 magnus 475 ECB : {
1830 magnus 476 CBC 65 : switch (c)
1830 magnus 477 ECB : {
1478 michael 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;
118 peter 484 GNC 28 : case 'D':
485 28 : DataDir = optarg;
486 28 : break;
1478 michael 487 CBC 4 : case 'e':
488 4 : mode = PG_MODE_ENABLE;
489 4 : break;
1410 490 6 : case 'f':
193 rhaas 491 GIC 6 : if (!option_parse_int(optarg, "-f/--filenode", 0,
492 : INT_MAX,
193 rhaas 493 EUB : NULL))
1410 michael 494 LBC 0 : exit(1);
1410 michael 495 CBC 6 : only_filenode = pstrdup(optarg);
496 6 : break;
1478 497 4 : case 'N':
498 4 : do_sync = false;
1478 michael 499 GBC 4 : break;
1468 michael 500 UIC 0 : case 'P':
1468 michael 501 LBC 0 : showprogress = true;
502 0 : break;
118 peter 503 UNC 0 : case 'v':
504 0 : verbose = true;
505 0 : break;
1830 magnus 506 GIC 1 : default:
507 : /* getopt_long already emitted a complaint */
366 tgl 508 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
1830 magnus 509 CBC 1 : exit(1);
510 : }
1830 magnus 511 EUB : }
512 :
1830 magnus 513 GIC 28 : if (DataDir == NULL)
1830 magnus 514 EUB : {
1830 magnus 515 UIC 0 : if (optind < argc)
516 0 : DataDir = argv[optind++];
1830 magnus 517 EUB : else
1830 magnus 518 UIC 0 : DataDir = getenv("PGDATA");
1830 magnus 519 EUB :
520 : /* If no DataDir was specified, and none could be found, error out */
1830 magnus 521 UBC 0 : if (DataDir == NULL)
522 : {
1469 peter 523 UIC 0 : pg_log_error("no data directory specified");
366 tgl 524 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
1830 magnus 525 0 : exit(1);
1830 magnus 526 ECB : }
527 : }
1830 magnus 528 EUB :
529 : /* Complain if any arguments remain */
1830 magnus 530 GBC 28 : if (optind < argc)
1830 magnus 531 EUB : {
1469 peter 532 UIC 0 : pg_log_error("too many command-line arguments (first is \"%s\")",
533 : argv[optind]);
366 tgl 534 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
1830 magnus 535 LBC 0 : exit(1);
536 : }
1830 magnus 537 ECB :
1410 michael 538 : /* filenode checking only works in --check mode */
1410 michael 539 CBC 28 : if (mode != PG_MODE_CHECK && only_filenode)
540 : {
1370 peter 541 GIC 2 : pg_log_error("option -f/--filenode can only be used with --check");
366 tgl 542 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
1478 michael 543 CBC 2 : exit(1);
1478 michael 544 ECB : }
1478 michael 545 EUB :
546 : /* Read the control file and check compatibility */
1469 peter 547 CBC 26 : ControlFile = get_controlfile(DataDir, &crc_ok);
1830 magnus 548 GBC 26 : if (!crc_ok)
366 tgl 549 UIC 0 : pg_fatal("pg_control CRC value is incorrect");
1830 magnus 550 ECB :
1488 michael 551 GIC 26 : if (ControlFile->pg_control_version != PG_CONTROL_VERSION)
366 tgl 552 UBC 0 : pg_fatal("cluster is not compatible with this version of pg_checksums");
1488 michael 553 EUB :
1483 michael 554 GIC 26 : if (ControlFile->blcksz != BLCKSZ)
1483 michael 555 EUB : {
1469 peter 556 UIC 0 : pg_log_error("database cluster is not compatible");
366 tgl 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);
1483 michael 559 0 : exit(1);
560 : }
561 :
562 : /*
1402 michael 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 : */
1830 magnus 567 CBC 26 : if (ControlFile->state != DB_SHUTDOWNED &&
568 1 : ControlFile->state != DB_SHUTDOWNED_IN_RECOVERY)
366 tgl 569 1 : pg_fatal("cluster must be shut down");
570 :
1478 michael 571 25 : if (ControlFile->data_checksum_version == 0 &&
572 4 : mode == PG_MODE_CHECK)
366 tgl 573 1 : pg_fatal("data checksums are not enabled in cluster");
574 :
1478 michael 575 24 : if (ControlFile->data_checksum_version == 0 &&
576 3 : mode == PG_MODE_DISABLE)
366 tgl 577 1 : pg_fatal("data checksums are already disabled in cluster");
578 :
1478 michael 579 GIC 23 : if (ControlFile->data_checksum_version > 0 &&
1478 michael 580 CBC 21 : mode == PG_MODE_ENABLE)
366 tgl 581 GIC 1 : pg_fatal("data checksums are already enabled in cluster");
582 :
583 : /* Operate on all files if checking or enabling checksums */
1478 michael 584 22 : if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE)
585 : {
586 : /*
1468 michael 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
1468 michael 589 EUB : * processed and once to do the real work.
590 : */
1468 michael 591 GBC 21 : if (showprogress)
592 : {
1468 michael 593 UIC 0 : total_size = scan_directory(DataDir, "global", true);
1468 michael 594 LBC 0 : total_size += scan_directory(DataDir, "base", true);
595 0 : total_size += scan_directory(DataDir, "pg_tblspc", true);
1468 michael 596 ECB : }
597 :
1468 michael 598 CBC 21 : (void) scan_directory(DataDir, "global", false);
1468 michael 599 GBC 13 : (void) scan_directory(DataDir, "base", false);
1468 michael 600 GIC 13 : (void) scan_directory(DataDir, "pg_tblspc", false);
1468 michael 601 ECB :
1468 michael 602 CBC 13 : if (showprogress)
1468 michael 603 LBC 0 : progress_report(true);
1478 michael 604 ECB :
1478 michael 605 GIC 13 : printf(_("Checksum operation completed\n"));
384 tgl 606 CBC 13 : printf(_("Files scanned: %lld\n"), (long long) files_scanned);
607 13 : printf(_("Blocks scanned: %lld\n"), (long long) blocks_scanned);
1478 michael 608 GIC 13 : if (mode == PG_MODE_CHECK)
1478 michael 609 ECB : {
384 tgl 610 CBC 11 : printf(_("Bad checksums: %lld\n"), (long long) badblocks);
859 bruce 611 GIC 11 : printf(_("Data checksum version: %u\n"), ControlFile->data_checksum_version);
1478 michael 612 ECB :
1478 michael 613 GIC 11 : if (badblocks > 0)
1478 michael 614 CBC 4 : exit(1);
1478 michael 615 ECB : }
648 michael 616 GIC 2 : else if (mode == PG_MODE_ENABLE)
617 : {
384 tgl 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 : /*
1478 michael 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 : */
1478 michael 628 GIC 10 : if (mode == PG_MODE_ENABLE || mode == PG_MODE_DISABLE)
1478 michael 629 ECB : {
1478 michael 630 GIC 3 : ControlFile->data_checksum_version =
1478 michael 631 CBC 3 : (mode == PG_MODE_ENABLE) ? PG_DATA_CHECKSUM_VERSION : 0;
1830 magnus 632 ECB :
1478 michael 633 GIC 3 : if (do_sync)
634 : {
1469 peter 635 CBC 1 : pg_log_info("syncing data directory");
636 1 : fsync_pgdata(DataDir, PG_VERSION_NUM);
637 : }
1830 magnus 638 ECB :
1469 peter 639 GBC 3 : pg_log_info("updating control file");
1469 peter 640 CBC 3 : update_controlfile(DataDir, ControlFile, do_sync);
1830 magnus 641 ECB :
1478 michael 642 GIC 3 : if (verbose)
859 bruce 643 LBC 0 : printf(_("Data checksum version: %u\n"), ControlFile->data_checksum_version);
1478 michael 644 GIC 3 : if (mode == PG_MODE_ENABLE)
645 2 : printf(_("Checksums enabled in cluster\n"));
1478 michael 646 ECB : else
1478 michael 647 GIC 1 : printf(_("Checksums disabled in cluster\n"));
648 : }
649 :
1830 magnus 650 10 : return 0;
651 : }
|