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

Generated by: LCOV version 1.14