LCOV - code coverage report
Current view: top level - src/bin/pg_checksums - pg_checksums.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 309 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 6 0
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.3.2-1