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

Generated by: LCOV version 1.13