LCOV - code coverage report
Current view: top level - src/bin/pg_checksums - pg_checksums.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 77.2 % 263 203
Test Date: 2026-07-25 22:15:46 Functions: 83.3 % 6 5
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 68.7 % 182 125

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

Generated by: LCOV version 2.0-1