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

Generated by: LCOV version 1.16