LCOV - differential code coverage report
Current view: top level - src/bin/pg_checksums - pg_checksums.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 77.6 % 255 198 3 8 15 31 7 41 5 145 14 41 5 4
Current Date: 2023-04-08 15:15:32 Functions: 83.3 % 6 5 1 1 2 2 1
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a