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 17:13:01 Functions: 83.3 % 6 5 1 1 2 2 1
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (60,120] days: 57.1 % 7 4 3 4
Legend: Lines: hit not hit (180,240] days: 100.0 % 1 1 1 1
(240..) days: 78.1 % 247 193 8 15 31 7 40 1 145 13 41
Function coverage date bins:
(240..) days: 71.4 % 7 5 1 1 2 2 1

 Age         Owner                  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
 1682 tgl                        77 CBC           1 : usage(void)
                                 78                 : {
 1378 peter                      79               1 :     printf(_("%s enables, disables, or verifies data checksums in a PostgreSQL database cluster.\n\n"), progname);
 1830 magnus                     80               1 :     printf(_("Usage:\n"));
 1690 peter_e                    81               1 :     printf(_("  %s [OPTION]... [DATADIR]\n"), progname);
 1830 magnus                     82               1 :     printf(_("\nOptions:\n"));
 1410 michael                    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"));
 1830 magnus                     93               1 :     printf(_("\nIf no data directory (DATADIR) is specified, "
                                 94                 :              "the environment variable PGDATA\nis used.\n\n"));
 1136 peter                      95               1 :     printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
                                 96               1 :     printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
 1830 magnus                     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
  965 heikki.linnakangas        132 UBC           0 : progress_report(bool finished)
                                133                 : {
                                134                 :     int         percent;
                                135                 :     pg_time_t   now;
                                136                 : 
 1468 michael                   137               0 :     Assert(showprogress);
                                138                 : 
                                139               0 :     now = time(NULL);
  965 heikki.linnakangas        140               0 :     if (now == last_progress_report && !finished)
 1468 michael                   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                 : 
  384 tgl                       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                 :      */
  964 heikki.linnakangas        162               0 :     fputc((!finished && isatty(fileno(stderr))) ? '\r' : '\n', stderr);
                                163                 : }
                                164                 : 
                                165                 : static bool
 1591 michael                   166 CBC       12858 : skipfile(const char *fn)
                                167                 : {
                                168                 :     int         excludeIdx;
                                169                 : 
 1140                           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)
 1591                           177             150 :             return true;
                                178                 :     }
                                179                 : 
                                180           12708 :     return false;
                                181                 : }
                                182                 : 
                                183                 : static void
  480 peter                     184            8884 : scan_file(const char *fn, int segmentno)
                                185                 : {
                                186                 :     PGIOAlignedBlock buf;
 1681 tgl                       187            8884 :     PageHeader  header = (PageHeader) buf.data;
                                188                 :     int         f;
                                189                 :     BlockNumber blockno;
                                190                 :     int         flags;
  648 michael                   191            8884 :     int64       blocks_written_in_file = 0;
                                192                 : 
 1478                           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                 : 
 1830 magnus                    199            8884 :     if (f < 0)
  366 tgl                       200 UBC           0 :         pg_fatal("could not open file \"%s\": %m", fn);
                                201                 : 
  648 michael                   202 CBC        8884 :     files_scanned++;
                                203                 : 
 1830 magnus                    204            8884 :     for (blockno = 0;; blockno++)
                                205           26402 :     {
                                206                 :         uint16      csum;
 1681 tgl                       207           35286 :         int         r = read(f, buf.data, BLCKSZ);
                                208                 : 
 1830 magnus                    209           35286 :         if (r == 0)
                                210            8876 :             break;
                                211           26410 :         if (r != BLCKSZ)
                                212                 :         {
 1314 peter                     213               8 :             if (r < 0)
  366 tgl                       214 UBC           0 :                 pg_fatal("could not read block %u in file \"%s\": %m",
                                215                 :                          blockno, fn);
                                216                 :             else
  366 tgl                       217 CBC           8 :                 pg_fatal("could not read block %u in file \"%s\": read %d of %d",
                                218                 :                          blockno, fn, r, BLCKSZ);
                                219                 :         }
  648 michael                   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                 :          */
  736 fujii                     228           26402 :         current_size += r;
                                229                 : 
                                230                 :         /* New pages have no checksum yet */
  272 peter                     231 GNC       26402 :         if (PageIsNew(buf.data))
 1820 magnus                    232 UBC           0 :             continue;
                                233                 : 
 1681 tgl                       234 CBC       26402 :         csum = pg_checksum_page(buf.data, blockno + segmentno * RELSEG_SIZE);
 1478 michael                   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)
 1469 peter                     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);
 1478 michael                   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                 :              */
  648                           253            5748 :             if (header->pd_checksum == csum)
                                254            2874 :                 continue;
                                255                 : 
                                256            2874 :             blocks_written_in_file++;
                                257                 : 
                                258                 :             /* Set checksum in page header */
 1478                           259            2874 :             header->pd_checksum = csum;
                                260                 : 
                                261                 :             /* Seek back to beginning of block */
                                262            2874 :             if (lseek(f, -BLCKSZ, SEEK_CUR) < 0)
  366 tgl                       263 UBC           0 :                 pg_fatal("seek failed for block %u in file \"%s\": %m", blockno, fn);
                                264                 : 
                                265                 :             /* Write block with checksum */
 1314 peter                     266 CBC        2874 :             w = write(f, buf.data, BLCKSZ);
                                267            2874 :             if (w != BLCKSZ)
                                268                 :             {
 1314 peter                     269 UBC           0 :                 if (w < 0)
  366 tgl                       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                 : 
 1468 michael                   278 CBC       23528 :         if (showprogress)
 1468 michael                   279 UBC           0 :             progress_report(false);
                                280                 :     }
                                281                 : 
 1683 alvherre                  282 CBC        8876 :     if (verbose)
                                283                 :     {
 1478 michael                   284 UBC           0 :         if (mode == PG_MODE_CHECK)
 1469 peter                     285               0 :             pg_log_info("checksums verified in file \"%s\"", fn);
 1478 michael                   286               0 :         if (mode == PG_MODE_ENABLE)
 1469 peter                     287               0 :             pg_log_info("checksums enabled in file \"%s\"", fn);
                                288                 :     }
                                289                 : 
                                290                 :     /* Update write counters if any write activity has happened */
  648 michael                   291 CBC        8876 :     if (blocks_written_in_file > 0)
                                292                 :     {
                                293             780 :         files_written++;
                                294             780 :         blocks_written += blocks_written_in_file;
                                295                 :     }
                                296                 : 
 1830 magnus                    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
 1468 michael                   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                 : 
 1829 peter_e                   315              96 :     snprintf(path, sizeof(path), "%s/%s", basedir, subdir);
 1830 magnus                    316              96 :     dir = opendir(path);
                                317              96 :     if (!dir)
  366 tgl                       318 UBC           0 :         pg_fatal("could not open directory \"%s\": %m", path);
 1830 magnus                    319 CBC       13223 :     while ((de = readdir(dir)) != NULL)
                                320                 :     {
                                321                 :         char        fn[MAXPGPATH];
                                322                 :         struct stat st;
                                323                 : 
 1591 michael                   324           13135 :         if (strcmp(de->d_name, ".") == 0 ||
                                325           13039 :             strcmp(de->d_name, "..") == 0)
 1830 magnus                    326            4202 :             continue;
                                327                 : 
                                328                 :         /* Skip temporary files */
 1591 michael                   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)
 1487 michael                   338 UBC           0 :             continue;
                                339                 : 
 1829 peter_e                   340 CBC       12907 :         snprintf(fn, sizeof(fn), "%s/%s", path, de->d_name);
 1830 magnus                    341           12907 :         if (lstat(fn, &st) < 0)
  366 tgl                       342 UBC           0 :             pg_fatal("could not stat file \"%s\": %m", fn);
 1830 magnus                    343 CBC       12907 :         if (S_ISREG(st.st_mode))
                                344                 :         {
                                345                 :             char        fnonly[MAXPGPATH];
                                346                 :             char       *forkpath,
                                347                 :                        *segmentpath;
  480 peter                     348           12858 :             int         segmentno = 0;
                                349                 : 
 1591 michael                   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                 :              */
 1682 tgl                       359           12708 :             strlcpy(fnonly, de->d_name, sizeof(fnonly));
                                360           12708 :             segmentpath = strchr(fnonly, '.');
 1830 magnus                    361           12708 :             if (segmentpath != NULL)
                                362                 :             {
                                363              72 :                 *segmentpath++ = '\0';
                                364              72 :                 segmentno = atoi(segmentpath);
                                365              72 :                 if (segmentno == 0)
  366 tgl                       366 UBC           0 :                     pg_fatal("invalid segment number %d in file name \"%s\"",
                                367                 :                              segmentno, fn);
                                368                 :             }
                                369                 : 
 1682 tgl                       370 CBC       12708 :             forkpath = strchr(fnonly, '_');
 1830 magnus                    371           12708 :             if (forkpath != NULL)
                                372            3145 :                 *forkpath++ = '\0';
                                373                 : 
 1410 michael                   374           12708 :             if (only_filenode && strcmp(only_filenode, fnonly) != 0)
                                375                 :                 /* filenode not to be included */
 1830 magnus                    376            3824 :                 continue;
                                377                 : 
 1468 michael                   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                 :         }
 1830 magnus                    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
 1137 michael                   391 ECB             :              * on tablespace locations where only TABLESPACE_VERSION_DIRECTORY
                                392                 :              * is valid, resolving the linked locations and dive into them
                                393                 :              * directly.
                                394                 :              */
 1137 michael                   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
 1137 michael                   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                 :                  */
 1137 michael                   407 GBC           5 :                 snprintf(tblspc_path, sizeof(tblspc_path), "%s/%s/%s",
 1137 michael                   408 GIC           5 :                          path, de->d_name, TABLESPACE_VERSION_DIRECTORY);
                                409                 : 
                                410               5 :                 if (lstat(tblspc_path, &tblspc_st) < 0)
  366 tgl                       411 UIC           0 :                     pg_fatal("could not stat file \"%s\": %m",
                                412                 :                              tblspc_path);
                                413                 : 
 1137 michael                   414 ECB             :                 /*
                                415                 :                  * Move backwards once as the scan needs to happen for the
                                416                 :                  * contents of TABLESPACE_VERSION_DIRECTORY.
                                417                 :                  */
 1137 michael                   418 CBC           5 :                 snprintf(tblspc_path, sizeof(tblspc_path), "%s/%s",
 1137 michael                   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,
 1137 michael                   424 ECB             :                                           sizeonly);
                                425                 :             }
                                426                 :             else
                                427                 :             {
 1137 michael                   428 CBC          44 :                 dirsize += scan_directory(path, de->d_name, sizeonly);
 1137 michael                   429 ECB             :             }
                                430                 :         }
                                431                 :     }
 1830 magnus                    432 GIC          88 :     closedir(dir);
 1468 michael                   433 CBC          88 :     return dirsize;
                                434                 : }
                                435                 : 
                                436                 : int
 1830 magnus                    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'},
 1683 alvherre                  447 ECB             :         {"verbose", no_argument, NULL, 'v'},
                                448                 :         {NULL, 0, NULL, 0}
                                449                 :     };
                                450                 : 
 1830 magnus                    451 GIC          31 :     char       *DataDir = NULL;
 1830 magnus                    452 ECB             :     int         c;
 1754                           453                 :     int         option_index;
 1830                           454                 :     bool        crc_ok;
                                455                 : 
 1469 peter                     456 CBC          31 :     pg_logging_init(argv[0]);
 1488 michael                   457 GIC          31 :     set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_checksums"));
 1830 magnus                    458 CBC          31 :     progname = get_progname(argv[0]);
                                459                 : 
                                460              31 :     if (argc > 1)
 1830 magnus                    461 ECB             :     {
 1830 magnus                    462 GIC          31 :         if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
 1830 magnus                    463 ECB             :         {
 1830 magnus                    464 GIC           1 :             usage();
 1830 magnus                    465 CBC           1 :             exit(0);
 1830 magnus                    466 ECB             :         }
 1830 magnus                    467 GIC          30 :         if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
                                468                 :         {
 1488 michael                   469               1 :             puts("pg_checksums (PostgreSQL) " PG_VERSION);
 1830 magnus                    470 CBC           1 :             exit(0);
                                471                 :         }
 1830 magnus                    472 ECB             :     }
                                473                 : 
  118 peter                     474 GNC          93 :     while ((c = getopt_long(argc, argv, "cdD:ef:NPv", long_options, &option_index)) != -1)
 1830 magnus                    475 ECB             :     {
 1830 magnus                    476 CBC          65 :         switch (c)
 1830 magnus                    477 ECB             :         {
 1478 michael                   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;
  118 peter                     484 GNC          28 :             case 'D':
                                485              28 :                 DataDir = optarg;
                                486              28 :                 break;
 1478 michael                   487 CBC           4 :             case 'e':
                                488               4 :                 mode = PG_MODE_ENABLE;
                                489               4 :                 break;
 1410                           490               6 :             case 'f':
  193 rhaas                     491 GIC           6 :                 if (!option_parse_int(optarg, "-f/--filenode", 0,
                                492                 :                                       INT_MAX,
  193 rhaas                     493 EUB             :                                       NULL))
 1410 michael                   494 LBC           0 :                     exit(1);
 1410 michael                   495 CBC           6 :                 only_filenode = pstrdup(optarg);
                                496               6 :                 break;
 1478                           497               4 :             case 'N':
                                498               4 :                 do_sync = false;
 1478 michael                   499 GBC           4 :                 break;
 1468 michael                   500 UIC           0 :             case 'P':
 1468 michael                   501 LBC           0 :                 showprogress = true;
                                502               0 :                 break;
  118 peter                     503 UNC           0 :             case 'v':
                                504               0 :                 verbose = true;
                                505               0 :                 break;
 1830 magnus                    506 GIC           1 :             default:
                                507                 :                 /* getopt_long already emitted a complaint */
  366 tgl                       508               1 :                 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 1830 magnus                    509 CBC           1 :                 exit(1);
                                510                 :         }
 1830 magnus                    511 EUB             :     }
                                512                 : 
 1830 magnus                    513 GIC          28 :     if (DataDir == NULL)
 1830 magnus                    514 EUB             :     {
 1830 magnus                    515 UIC           0 :         if (optind < argc)
                                516               0 :             DataDir = argv[optind++];
 1830 magnus                    517 EUB             :         else
 1830 magnus                    518 UIC           0 :             DataDir = getenv("PGDATA");
 1830 magnus                    519 EUB             : 
                                520                 :         /* If no DataDir was specified, and none could be found, error out */
 1830 magnus                    521 UBC           0 :         if (DataDir == NULL)
                                522                 :         {
 1469 peter                     523 UIC           0 :             pg_log_error("no data directory specified");
  366 tgl                       524               0 :             pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 1830 magnus                    525               0 :             exit(1);
 1830 magnus                    526 ECB             :         }
                                527                 :     }
 1830 magnus                    528 EUB             : 
                                529                 :     /* Complain if any arguments remain */
 1830 magnus                    530 GBC          28 :     if (optind < argc)
 1830 magnus                    531 EUB             :     {
 1469 peter                     532 UIC           0 :         pg_log_error("too many command-line arguments (first is \"%s\")",
                                533                 :                      argv[optind]);
  366 tgl                       534               0 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 1830 magnus                    535 LBC           0 :         exit(1);
                                536                 :     }
 1830 magnus                    537 ECB             : 
 1410 michael                   538                 :     /* filenode checking only works in --check mode */
 1410 michael                   539 CBC          28 :     if (mode != PG_MODE_CHECK && only_filenode)
                                540                 :     {
 1370 peter                     541 GIC           2 :         pg_log_error("option -f/--filenode can only be used with --check");
  366 tgl                       542               2 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 1478 michael                   543 CBC           2 :         exit(1);
 1478 michael                   544 ECB             :     }
 1478 michael                   545 EUB             : 
                                546                 :     /* Read the control file and check compatibility */
 1469 peter                     547 CBC          26 :     ControlFile = get_controlfile(DataDir, &crc_ok);
 1830 magnus                    548 GBC          26 :     if (!crc_ok)
  366 tgl                       549 UIC           0 :         pg_fatal("pg_control CRC value is incorrect");
 1830 magnus                    550 ECB             : 
 1488 michael                   551 GIC          26 :     if (ControlFile->pg_control_version != PG_CONTROL_VERSION)
  366 tgl                       552 UBC           0 :         pg_fatal("cluster is not compatible with this version of pg_checksums");
 1488 michael                   553 EUB             : 
 1483 michael                   554 GIC          26 :     if (ControlFile->blcksz != BLCKSZ)
 1483 michael                   555 EUB             :     {
 1469 peter                     556 UIC           0 :         pg_log_error("database cluster is not compatible");
  366 tgl                       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);
 1483 michael                   559               0 :         exit(1);
                                560                 :     }
                                561                 : 
                                562                 :     /*
 1402 michael                   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                 :      */
 1830 magnus                    567 CBC          26 :     if (ControlFile->state != DB_SHUTDOWNED &&
                                568               1 :         ControlFile->state != DB_SHUTDOWNED_IN_RECOVERY)
  366 tgl                       569               1 :         pg_fatal("cluster must be shut down");
                                570                 : 
 1478 michael                   571              25 :     if (ControlFile->data_checksum_version == 0 &&
                                572               4 :         mode == PG_MODE_CHECK)
  366 tgl                       573               1 :         pg_fatal("data checksums are not enabled in cluster");
                                574                 : 
 1478 michael                   575              24 :     if (ControlFile->data_checksum_version == 0 &&
                                576               3 :         mode == PG_MODE_DISABLE)
  366 tgl                       577               1 :         pg_fatal("data checksums are already disabled in cluster");
                                578                 : 
 1478 michael                   579 GIC          23 :     if (ControlFile->data_checksum_version > 0 &&
 1478 michael                   580 CBC          21 :         mode == PG_MODE_ENABLE)
  366 tgl                       581 GIC           1 :         pg_fatal("data checksums are already enabled in cluster");
                                582                 : 
                                583                 :     /* Operate on all files if checking or enabling checksums */
 1478 michael                   584              22 :     if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE)
                                585                 :     {
                                586                 :         /*
 1468 michael                   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
 1468 michael                   589 EUB             :          * processed and once to do the real work.
                                590                 :          */
 1468 michael                   591 GBC          21 :         if (showprogress)
                                592                 :         {
 1468 michael                   593 UIC           0 :             total_size = scan_directory(DataDir, "global", true);
 1468 michael                   594 LBC           0 :             total_size += scan_directory(DataDir, "base", true);
                                595               0 :             total_size += scan_directory(DataDir, "pg_tblspc", true);
 1468 michael                   596 ECB             :         }
                                597                 : 
 1468 michael                   598 CBC          21 :         (void) scan_directory(DataDir, "global", false);
 1468 michael                   599 GBC          13 :         (void) scan_directory(DataDir, "base", false);
 1468 michael                   600 GIC          13 :         (void) scan_directory(DataDir, "pg_tblspc", false);
 1468 michael                   601 ECB             : 
 1468 michael                   602 CBC          13 :         if (showprogress)
 1468 michael                   603 LBC           0 :             progress_report(true);
 1478 michael                   604 ECB             : 
 1478 michael                   605 GIC          13 :         printf(_("Checksum operation completed\n"));
  384 tgl                       606 CBC          13 :         printf(_("Files scanned:   %lld\n"), (long long) files_scanned);
                                607              13 :         printf(_("Blocks scanned:  %lld\n"), (long long) blocks_scanned);
 1478 michael                   608 GIC          13 :         if (mode == PG_MODE_CHECK)
 1478 michael                   609 ECB             :         {
  384 tgl                       610 CBC          11 :             printf(_("Bad checksums:  %lld\n"), (long long) badblocks);
  859 bruce                     611 GIC          11 :             printf(_("Data checksum version: %u\n"), ControlFile->data_checksum_version);
 1478 michael                   612 ECB             : 
 1478 michael                   613 GIC          11 :             if (badblocks > 0)
 1478 michael                   614 CBC           4 :                 exit(1);
 1478 michael                   615 ECB             :         }
  648 michael                   616 GIC           2 :         else if (mode == PG_MODE_ENABLE)
                                617                 :         {
  384 tgl                       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                 :     /*
 1478 michael                   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                 :      */
 1478 michael                   628 GIC          10 :     if (mode == PG_MODE_ENABLE || mode == PG_MODE_DISABLE)
 1478 michael                   629 ECB             :     {
 1478 michael                   630 GIC           3 :         ControlFile->data_checksum_version =
 1478 michael                   631 CBC           3 :             (mode == PG_MODE_ENABLE) ? PG_DATA_CHECKSUM_VERSION : 0;
 1830 magnus                    632 ECB             : 
 1478 michael                   633 GIC           3 :         if (do_sync)
                                634                 :         {
 1469 peter                     635 CBC           1 :             pg_log_info("syncing data directory");
                                636               1 :             fsync_pgdata(DataDir, PG_VERSION_NUM);
                                637                 :         }
 1830 magnus                    638 ECB             : 
 1469 peter                     639 GBC           3 :         pg_log_info("updating control file");
 1469 peter                     640 CBC           3 :         update_controlfile(DataDir, ControlFile, do_sync);
 1830 magnus                    641 ECB             : 
 1478 michael                   642 GIC           3 :         if (verbose)
  859 bruce                     643 LBC           0 :             printf(_("Data checksum version: %u\n"), ControlFile->data_checksum_version);
 1478 michael                   644 GIC           3 :         if (mode == PG_MODE_ENABLE)
                                645               2 :             printf(_("Checksums enabled in cluster\n"));
 1478 michael                   646 ECB             :         else
 1478 michael                   647 GIC           1 :             printf(_("Checksums disabled in cluster\n"));
                                648                 :     }
                                649                 : 
 1830 magnus                    650              10 :     return 0;
                                651                 : }
        

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