LCOV - code coverage report
Current view: top level - src/bin/pg_checksums - pg_checksums.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 77.2 % 263 203
Test Date: 2026-02-17 17:20:33 Functions: 83.3 % 6 5
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            1 : usage(void)
      69              : {
      70            1 :     printf(_("%s enables, disables, or verifies data checksums in a PostgreSQL database cluster.\n\n"), progname);
      71            1 :     printf(_("Usage:\n"));
      72            1 :     printf(_("  %s [OPTION]... [DATADIR]\n"), progname);
      73            1 :     printf(_("\nOptions:\n"));
      74            1 :     printf(_(" [-D, --pgdata=]DATADIR    data directory\n"));
      75            1 :     printf(_("  -c, --check              check data checksums (default)\n"));
      76            1 :     printf(_("  -d, --disable            disable data checksums\n"));
      77            1 :     printf(_("  -e, --enable             enable data checksums\n"));
      78            1 :     printf(_("  -f, --filenode=FILENODE  check only relation with specified filenode\n"));
      79            1 :     printf(_("  -N, --no-sync            do not wait for changes to be written safely to disk\n"));
      80            1 :     printf(_("  -P, --progress           show progress information\n"));
      81            1 :     printf(_("      --sync-method=METHOD set method for syncing files to disk\n"));
      82            1 :     printf(_("  -v, --verbose            output verbose messages\n"));
      83            1 :     printf(_("  -V, --version            output version information, then exit\n"));
      84            1 :     printf(_("  -?, --help               show this help, then exit\n"));
      85            1 :     printf(_("\nIf no data directory (DATADIR) is specified, "
      86              :              "the environment variable PGDATA\nis used.\n\n"));
      87            1 :     printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
      88            1 :     printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
      89            1 : }
      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              :     int         percent;
     127              :     pg_time_t   now;
     128              : 
     129              :     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              :             (current_size / (1024 * 1024)),
     147              :             (total_size / (1024 * 1024)),
     148              :             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              : }
     156              : 
     157              : static bool
     158        12850 : skipfile(const char *fn)
     159              : {
     160              :     int         excludeIdx;
     161              : 
     162        63911 :     for (excludeIdx = 0; skip[excludeIdx].name != NULL; excludeIdx++)
     163              :     {
     164        51207 :         int         cmplen = strlen(skip[excludeIdx].name);
     165              : 
     166        51207 :         if (!skip[excludeIdx].match_prefix)
     167        38427 :             cmplen++;
     168        51207 :         if (strncmp(skip[excludeIdx].name, fn, cmplen) == 0)
     169          146 :             return true;
     170              :     }
     171              : 
     172        12704 :     return false;
     173              : }
     174              : 
     175              : static void
     176         8872 : scan_file(const char *fn, int segmentno)
     177              : {
     178              :     PGIOAlignedBlock buf;
     179         8872 :     PageHeader  header = (PageHeader) buf.data;
     180              :     int         f;
     181              :     BlockNumber blockno;
     182              :     int         flags;
     183         8872 :     int64       blocks_written_in_file = 0;
     184              : 
     185              :     Assert(mode == PG_MODE_ENABLE ||
     186              :            mode == PG_MODE_CHECK);
     187              : 
     188         8872 :     flags = (mode == PG_MODE_ENABLE) ? O_RDWR : O_RDONLY;
     189         8872 :     f = open(fn, PG_BINARY | flags, 0);
     190              : 
     191         8872 :     if (f < 0)
     192            0 :         pg_fatal("could not open file \"%s\": %m", fn);
     193              : 
     194         8872 :     files_scanned++;
     195              : 
     196         8872 :     for (blockno = 0;; blockno++)
     197        26703 :     {
     198              :         uint16      csum;
     199        35575 :         int         r = read(f, buf.data, BLCKSZ);
     200              : 
     201        35575 :         if (r == 0)
     202         8864 :             break;
     203        26711 :         if (r != BLCKSZ)
     204              :         {
     205            8 :             if (r < 0)
     206            0 :                 pg_fatal("could not read block %u in file \"%s\": %m",
     207              :                          blockno, fn);
     208              :             else
     209            8 :                 pg_fatal("could not read block %u in file \"%s\": read %d of %d",
     210              :                          blockno, fn, r, BLCKSZ);
     211              :         }
     212        26703 :         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        26703 :         current_size += r;
     221              : 
     222              :         /* New pages have no checksum yet */
     223        26703 :         if (PageIsNew(buf.data))
     224          114 :             continue;
     225              : 
     226        26589 :         csum = pg_checksum_page(buf.data, blockno + segmentno * RELSEG_SIZE);
     227        26589 :         if (mode == PG_MODE_CHECK)
     228              :         {
     229        20791 :             if (csum != header->pd_checksum)
     230              :             {
     231            4 :                 if (ControlFile->data_checksum_version == PG_DATA_CHECKSUM_VERSION)
     232            4 :                     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            4 :                 badblocks++;
     235              :             }
     236              :         }
     237         5798 :         else if (mode == PG_MODE_ENABLE)
     238              :         {
     239              :             int         w;
     240              : 
     241              :             /*
     242              :              * Do not rewrite if the checksum is already set to the expected
     243              :              * value.
     244              :              */
     245         5798 :             if (header->pd_checksum == csum)
     246         2899 :                 continue;
     247              : 
     248         2899 :             blocks_written_in_file++;
     249              : 
     250              :             /* Set checksum in page header */
     251         2899 :             header->pd_checksum = csum;
     252              : 
     253              :             /* Seek back to beginning of block */
     254         2899 :             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         2899 :             w = write(f, buf.data, BLCKSZ);
     259         2899 :             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              :             }
     268              :         }
     269              : 
     270        23690 :         if (showprogress)
     271            0 :             progress_report(false);
     272              :     }
     273              : 
     274         8864 :     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              :     }
     281              : 
     282              :     /* Update write counters if any write activity has happened */
     283         8864 :     if (blocks_written_in_file > 0)
     284              :     {
     285          781 :         files_written++;
     286          781 :         blocks_written += blocks_written_in_file;
     287              :     }
     288              : 
     289         8864 :     close(f);
     290         8864 : }
     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           96 : scan_directory(const char *basedir, const char *subdir, bool sizeonly)
     301              : {
     302           96 :     int64       dirsize = 0;
     303              :     char        path[MAXPGPATH];
     304              :     DIR        *dir;
     305              :     struct dirent *de;
     306              : 
     307           96 :     snprintf(path, sizeof(path), "%s/%s", basedir, subdir);
     308           96 :     dir = opendir(path);
     309           96 :     if (!dir)
     310            0 :         pg_fatal("could not open directory \"%s\": %m", path);
     311        13219 :     while ((de = readdir(dir)) != NULL)
     312              :     {
     313              :         char        fn[MAXPGPATH];
     314              :         struct stat st;
     315              : 
     316        13131 :         if (strcmp(de->d_name, ".") == 0 ||
     317        13040 :             strcmp(de->d_name, "..") == 0)
     318         4210 :             continue;
     319              : 
     320              :         /* Skip temporary files */
     321        12949 :         if (strncmp(de->d_name,
     322              :                     PG_TEMP_FILE_PREFIX,
     323              :                     strlen(PG_TEMP_FILE_PREFIX)) == 0)
     324           31 :             continue;
     325              : 
     326              :         /* Skip temporary folders */
     327        12918 :         if (strncmp(de->d_name,
     328              :                     PG_TEMP_FILES_DIR,
     329              :                     strlen(PG_TEMP_FILES_DIR)) == 0)
     330            0 :             continue;
     331              : 
     332              :         /* Skip macOS system files */
     333        12918 :         if (strcmp(de->d_name, ".DS_Store") == 0)
     334           19 :             continue;
     335              : 
     336        12899 :         snprintf(fn, sizeof(fn), "%s/%s", path, de->d_name);
     337        12899 :         if (lstat(fn, &st) < 0)
     338            0 :             pg_fatal("could not stat file \"%s\": %m", fn);
     339        12899 :         if (S_ISREG(st.st_mode))
     340              :         {
     341              :             char        fnonly[MAXPGPATH];
     342              :             char       *forkpath,
     343              :                        *segmentpath;
     344        12850 :             int         segmentno = 0;
     345              : 
     346        12850 :             if (skipfile(de->d_name))
     347         3978 :                 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        12704 :             strlcpy(fnonly, de->d_name, sizeof(fnonly));
     356        12704 :             segmentpath = strchr(fnonly, '.');
     357        12704 :             if (segmentpath != NULL)
     358              :             {
     359           71 :                 *segmentpath++ = '\0';
     360           71 :                 segmentno = atoi(segmentpath);
     361           71 :                 if (segmentno == 0)
     362            0 :                     pg_fatal("invalid segment number %d in file name \"%s\"",
     363              :                              segmentno, fn);
     364              :             }
     365              : 
     366        12704 :             forkpath = strchr(fnonly, '_');
     367        12704 :             if (forkpath != NULL)
     368         3151 :                 *forkpath++ = '\0';
     369              : 
     370        12704 :             if (only_filenode && strcmp(only_filenode, fnonly) != 0)
     371              :                 /* filenode not to be included */
     372         3832 :                 continue;
     373              : 
     374         8872 :             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         8872 :             if (!sizeonly)
     381         8872 :                 scan_file(fn, segmentno);
     382              :         }
     383           49 :         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           49 :             if (strncmp(PG_TBLSPC_DIR, subdir, strlen(PG_TBLSPC_DIR)) == 0)
     392              :             {
     393              :                 char        tblspc_path[MAXPGPATH];
     394              :                 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            5 :                 snprintf(tblspc_path, sizeof(tblspc_path), "%s/%s/%s",
     404            5 :                          path, de->d_name, TABLESPACE_VERSION_DIRECTORY);
     405              : 
     406            5 :                 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            5 :                 snprintf(tblspc_path, sizeof(tblspc_path), "%s/%s",
     415            5 :                          path, de->d_name);
     416              : 
     417              :                 /* Looks like a valid tablespace location */
     418            5 :                 dirsize += scan_directory(tblspc_path,
     419              :                                           TABLESPACE_VERSION_DIRECTORY,
     420              :                                           sizeonly);
     421              :             }
     422              :             else
     423              :             {
     424           44 :                 dirsize += scan_directory(path, de->d_name, sizeonly);
     425              :             }
     426              :         }
     427              :     }
     428           88 :     closedir(dir);
     429           88 :     return dirsize;
     430              : }
     431              : 
     432              : int
     433           31 : 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           31 :     char       *DataDir = NULL;
     449              :     int         c;
     450              :     int         option_index;
     451              :     bool        crc_ok;
     452              :     uint32      major_version;
     453              :     char       *version_str;
     454              : 
     455           31 :     pg_logging_init(argv[0]);
     456           31 :     set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_checksums"));
     457           31 :     progname = get_progname(argv[0]);
     458              : 
     459           31 :     if (argc > 1)
     460              :     {
     461           31 :         if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
     462              :         {
     463            1 :             usage();
     464            1 :             exit(0);
     465              :         }
     466           30 :         if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
     467              :         {
     468            1 :             puts("pg_checksums (PostgreSQL) " PG_VERSION);
     469            1 :             exit(0);
     470              :         }
     471              :     }
     472              : 
     473           93 :     while ((c = getopt_long(argc, argv, "cdD:ef:NPv", long_options, &option_index)) != -1)
     474              :     {
     475           65 :         switch (c)
     476              :         {
     477           19 :             case 'c':
     478           19 :                 mode = PG_MODE_CHECK;
     479           19 :                 break;
     480            3 :             case 'd':
     481            3 :                 mode = PG_MODE_DISABLE;
     482            3 :                 break;
     483           28 :             case 'D':
     484           28 :                 DataDir = optarg;
     485           28 :                 break;
     486            4 :             case 'e':
     487            4 :                 mode = PG_MODE_ENABLE;
     488            4 :                 break;
     489            6 :             case 'f':
     490            6 :                 if (!option_parse_int(optarg, "-f/--filenode", 0,
     491              :                                       INT_MAX,
     492              :                                       NULL))
     493            0 :                     exit(1);
     494            6 :                 only_filenode = pstrdup(optarg);
     495            6 :                 break;
     496            4 :             case 'N':
     497            4 :                 do_sync = false;
     498            4 :                 break;
     499            0 :             case 'P':
     500            0 :                 showprogress = true;
     501            0 :                 break;
     502            0 :             case 'v':
     503            0 :                 verbose = true;
     504            0 :                 break;
     505            0 :             case 1:
     506            0 :                 if (!parse_sync_method(optarg, &sync_method))
     507            0 :                     exit(1);
     508            0 :                 break;
     509            1 :             default:
     510              :                 /* getopt_long already emitted a complaint */
     511            1 :                 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     512            1 :                 exit(1);
     513              :         }
     514              :     }
     515              : 
     516           28 :     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              :     }
     531              : 
     532              :     /* Complain if any arguments remain */
     533           28 :     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           28 :     if (mode != PG_MODE_CHECK && only_filenode)
     543              :     {
     544            2 :         pg_log_error("option -f/--filenode can only be used with --check");
     545            2 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     546            2 :         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           26 :     major_version = GET_PG_MAJORVERSION_NUM(get_pg_version(DataDir, &version_str));
     555           26 :     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           26 :     ControlFile = get_controlfile(DataDir, &crc_ok);
     565           26 :     if (!crc_ok)
     566            0 :         pg_fatal("pg_control CRC value is incorrect");
     567              : 
     568           26 :     if (ControlFile->pg_control_version != PG_CONTROL_VERSION)
     569            0 :         pg_fatal("cluster is not compatible with this version of pg_checksums");
     570              : 
     571           26 :     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           26 :     if (ControlFile->state != DB_SHUTDOWNED &&
     585            1 :         ControlFile->state != DB_SHUTDOWNED_IN_RECOVERY)
     586            1 :         pg_fatal("cluster must be shut down");
     587              : 
     588           25 :     if (ControlFile->data_checksum_version == 0 &&
     589            4 :         mode == PG_MODE_CHECK)
     590            1 :         pg_fatal("data checksums are not enabled in cluster");
     591              : 
     592           24 :     if (ControlFile->data_checksum_version == 0 &&
     593            3 :         mode == PG_MODE_DISABLE)
     594            1 :         pg_fatal("data checksums are already disabled in cluster");
     595              : 
     596           23 :     if (ControlFile->data_checksum_version > 0 &&
     597           21 :         mode == PG_MODE_ENABLE)
     598            1 :         pg_fatal("data checksums are already enabled in cluster");
     599              : 
     600              :     /* Operate on all files if checking or enabling checksums */
     601           22 :     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           21 :         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              :         }
     614              : 
     615           21 :         (void) scan_directory(DataDir, "global", false);
     616           13 :         (void) scan_directory(DataDir, "base", false);
     617           13 :         (void) scan_directory(DataDir, PG_TBLSPC_DIR, false);
     618              : 
     619           13 :         if (showprogress)
     620            0 :             progress_report(true);
     621              : 
     622           13 :         printf(_("Checksum operation completed\n"));
     623           13 :         printf(_("Files scanned:   %" PRId64 "\n"), files_scanned);
     624           13 :         printf(_("Blocks scanned:  %" PRId64 "\n"), blocks_scanned);
     625           13 :         if (mode == PG_MODE_CHECK)
     626              :         {
     627           11 :             printf(_("Bad checksums:  %" PRId64 "\n"), badblocks);
     628           11 :             printf(_("Data checksum version: %u\n"), ControlFile->data_checksum_version);
     629              : 
     630           11 :             if (badblocks > 0)
     631            4 :                 exit(1);
     632              :         }
     633            2 :         else if (mode == PG_MODE_ENABLE)
     634              :         {
     635            2 :             printf(_("Files written:  %" PRId64 "\n"), files_written);
     636            2 :             printf(_("Blocks written: %" PRId64 "\n"), blocks_written);
     637              :         }
     638              :     }
     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           10 :     if (mode == PG_MODE_ENABLE || mode == PG_MODE_DISABLE)
     646              :     {
     647            3 :         ControlFile->data_checksum_version =
     648            3 :             (mode == PG_MODE_ENABLE) ? PG_DATA_CHECKSUM_VERSION : 0;
     649              : 
     650            3 :         if (do_sync)
     651              :         {
     652            1 :             pg_log_info("syncing data directory");
     653            1 :             sync_pgdata(DataDir, PG_VERSION_NUM, sync_method, true);
     654              :         }
     655              : 
     656            3 :         pg_log_info("updating control file");
     657            3 :         update_controlfile(DataDir, ControlFile, do_sync);
     658              : 
     659            3 :         if (verbose)
     660            0 :             printf(_("Data checksum version: %u\n"), ControlFile->data_checksum_version);
     661            3 :         if (mode == PG_MODE_ENABLE)
     662            2 :             printf(_("Checksums enabled in cluster\n"));
     663              :         else
     664            1 :             printf(_("Checksums disabled in cluster\n"));
     665              :     }
     666              : 
     667           10 :     return 0;
     668              : }
        

Generated by: LCOV version 2.0-1