11/*-------------------------------------------------------------------------
22 *
33 * pg_checksums.c
4- * Verifies page level checksums in an offline cluster.
4+ * Checks, enables or disables page level checksums for an offline
5+ * cluster
56 *
67 * Copyright (c) 2010-2019, PostgreSQL Global Development Group
78 *
1718#include <sys/stat.h>
1819#include <unistd.h>
1920
20- #include "catalog/pg_control .h"
21+ #include "access/xlog_internal .h"
2122#include "common/controldata_utils.h"
23+ #include "common/file_perm.h"
24+ #include "common/file_utils.h"
2225#include "getopt_long.h"
2326#include "pg_getopt.h"
2427#include "storage/bufpage.h"
2528#include "storage/checksum.h"
2629#include "storage/checksum_impl.h"
27- #include "storage/fd.h"
2830
2931
3032static int64 files = 0 ;
@@ -35,16 +37,38 @@ static ControlFileData *ControlFile;
3537static char * only_relfilenode = NULL ;
3638static bool verbose = false;
3739
40+ typedef enum
41+ {
42+ PG_MODE_CHECK ,
43+ PG_MODE_DISABLE ,
44+ PG_MODE_ENABLE
45+ } PgChecksumMode ;
46+
47+ /*
48+ * Filename components.
49+ *
50+ * XXX: fd.h is not declared here as frontend side code is not able to
51+ * interact with the backend-side definitions for the various fsync
52+ * wrappers.
53+ */
54+ #define PG_TEMP_FILES_DIR "pgsql_tmp"
55+ #define PG_TEMP_FILE_PREFIX "pgsql_tmp"
56+
57+ static PgChecksumMode mode = PG_MODE_CHECK ;
58+
3859static const char * progname ;
3960
4061static void
4162usage (void )
4263{
43- printf (_ ("%s verifies data checksums in a PostgreSQL database cluster.\n\n" ), progname );
64+ printf (_ ("%s enables, disables or verifies data checksums in a PostgreSQL database cluster.\n\n" ), progname );
4465 printf (_ ("Usage:\n" ));
4566 printf (_ (" %s [OPTION]... [DATADIR]\n" ), progname );
4667 printf (_ ("\nOptions:\n" ));
4768 printf (_ (" [-D, --pgdata=]DATADIR data directory\n" ));
69+ printf (_ (" -c, --check check data checksums (default)\n" ));
70+ printf (_ (" -d, --disable disable data checksums\n" ));
71+ printf (_ (" -e, --enable enable data checksums\n" ));
4872 printf (_ (" -v, --verbose output verbose messages\n" ));
4973 printf (_ (" -r RELFILENODE check only relation with specified relfilenode\n" ));
5074 printf (_ (" -V, --version output version information, then exit\n" ));
@@ -90,8 +114,14 @@ scan_file(const char *fn, BlockNumber segmentno)
90114 PageHeader header = (PageHeader ) buf .data ;
91115 int f ;
92116 BlockNumber blockno ;
117+ int flags ;
118+
119+ Assert (mode == PG_MODE_ENABLE ||
120+ mode == PG_MODE_CHECK );
121+
122+ flags = (mode == PG_MODE_ENABLE ) ? O_RDWR : O_RDONLY ;
123+ f = open (fn , PG_BINARY | flags , 0 );
93124
94- f = open (fn , O_RDONLY | PG_BINARY , 0 );
95125 if (f < 0 )
96126 {
97127 fprintf (stderr , _ ("%s: could not open file \"%s\": %s\n" ),
@@ -121,18 +151,47 @@ scan_file(const char *fn, BlockNumber segmentno)
121151 continue ;
122152
123153 csum = pg_checksum_page (buf .data , blockno + segmentno * RELSEG_SIZE );
124- if (csum != header -> pd_checksum )
154+ if (mode == PG_MODE_CHECK )
125155 {
126- if (ControlFile -> data_checksum_version == PG_DATA_CHECKSUM_VERSION )
127- fprintf (stderr , _ ("%s: checksum verification failed in file \"%s\", block %u: calculated checksum %X but block contains %X\n" ),
128- progname , fn , blockno , csum , header -> pd_checksum );
129- badblocks ++ ;
156+ if (csum != header -> pd_checksum )
157+ {
158+ if (ControlFile -> data_checksum_version == PG_DATA_CHECKSUM_VERSION )
159+ fprintf (stderr , _ ("%s: checksum verification failed in file \"%s\", block %u: calculated checksum %X but block contains %X\n" ),
160+ progname , fn , blockno , csum , header -> pd_checksum );
161+ badblocks ++ ;
162+ }
163+ }
164+ else if (mode == PG_MODE_ENABLE )
165+ {
166+ /* Set checksum in page header */
167+ header -> pd_checksum = csum ;
168+
169+ /* Seek back to beginning of block */
170+ if (lseek (f , - BLCKSZ , SEEK_CUR ) < 0 )
171+ {
172+ fprintf (stderr , _ ("%s: seek failed for block %d in file \"%s\": %s\n" ), progname , blockno , fn , strerror (errno ));
173+ exit (1 );
174+ }
175+
176+ /* Write block with checksum */
177+ if (write (f , buf .data , BLCKSZ ) != BLCKSZ )
178+ {
179+ fprintf (stderr , _ ("%s: could not update checksum of block %d in file \"%s\": %s\n" ),
180+ progname , blockno , fn , strerror (errno ));
181+ exit (1 );
182+ }
130183 }
131184 }
132185
133186 if (verbose )
134- fprintf (stderr ,
135- _ ("%s: checksums verified in file \"%s\"\n" ), progname , fn );
187+ {
188+ if (mode == PG_MODE_CHECK )
189+ fprintf (stderr ,
190+ _ ("%s: checksums verified in file \"%s\"\n" ), progname , fn );
191+ if (mode == PG_MODE_ENABLE )
192+ fprintf (stderr ,
193+ _ ("%s: checksums enabled in file \"%s\"\n" ), progname , fn );
194+ }
136195
137196 close (f );
138197}
234293main (int argc , char * argv [])
235294{
236295 static struct option long_options [] = {
296+ {"check" , no_argument , NULL , 'c' },
237297 {"pgdata" , required_argument , NULL , 'D' },
298+ {"disable" , no_argument , NULL , 'd' },
299+ {"enable" , no_argument , NULL , 'e' },
238300 {"verbose" , no_argument , NULL , 'v' },
239301 {NULL , 0 , NULL , 0 }
240302 };
@@ -262,10 +324,19 @@ main(int argc, char *argv[])
262324 }
263325 }
264326
265- while ((c = getopt_long (argc , argv , "D:r :v" , long_options , & option_index )) != -1 )
327+ while ((c = getopt_long (argc , argv , "cD:der :v" , long_options , & option_index )) != -1 )
266328 {
267329 switch (c )
268330 {
331+ case 'c' :
332+ mode = PG_MODE_CHECK ;
333+ break ;
334+ case 'd' :
335+ mode = PG_MODE_DISABLE ;
336+ break ;
337+ case 'e' :
338+ mode = PG_MODE_ENABLE ;
339+ break ;
269340 case 'v' :
270341 verbose = true;
271342 break ;
@@ -312,6 +383,15 @@ main(int argc, char *argv[])
312383 exit (1 );
313384 }
314385
386+ /* Relfilenode checking only works in --check mode */
387+ if (mode != PG_MODE_CHECK && only_relfilenode )
388+ {
389+ fprintf (stderr , _ ("%s: relfilenode option only possible with --check\n" ), progname );
390+ fprintf (stderr , _ ("Try \"%s --help\" for more information.\n" ),
391+ progname );
392+ exit (1 );
393+ }
394+
315395 /* Check if cluster is running */
316396 ControlFile = get_controlfile (DataDir , progname , & crc_ok );
317397 if (!crc_ok )
@@ -339,29 +419,72 @@ main(int argc, char *argv[])
339419 if (ControlFile -> state != DB_SHUTDOWNED &&
340420 ControlFile -> state != DB_SHUTDOWNED_IN_RECOVERY )
341421 {
342- fprintf (stderr , _ ("%s: cluster must be shut down to verify checksums \n" ), progname );
422+ fprintf (stderr , _ ("%s: cluster must be shut down\n" ), progname );
343423 exit (1 );
344424 }
345425
346- if (ControlFile -> data_checksum_version == 0 )
426+ if (ControlFile -> data_checksum_version == 0 &&
427+ mode == PG_MODE_CHECK )
347428 {
348429 fprintf (stderr , _ ("%s: data checksums are not enabled in cluster\n" ), progname );
349430 exit (1 );
350431 }
432+ if (ControlFile -> data_checksum_version == 0 &&
433+ mode == PG_MODE_DISABLE )
434+ {
435+ fprintf (stderr , _ ("%s: data checksums are already disabled in cluster.\n" ), progname );
436+ exit (1 );
437+ }
438+ if (ControlFile -> data_checksum_version > 0 &&
439+ mode == PG_MODE_ENABLE )
440+ {
441+ fprintf (stderr , _ ("%s: data checksums are already enabled in cluster.\n" ), progname );
442+ exit (1 );
443+ }
444+
445+ /* Operate on all files if checking or enabling checksums */
446+ if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE )
447+ {
448+ scan_directory (DataDir , "global" );
449+ scan_directory (DataDir , "base" );
450+ scan_directory (DataDir , "pg_tblspc" );
451+
452+ printf (_ ("Checksum operation completed\n" ));
453+ printf (_ ("Files scanned: %s\n" ), psprintf (INT64_FORMAT , files ));
454+ printf (_ ("Blocks scanned: %s\n" ), psprintf (INT64_FORMAT , blocks ));
455+ if (mode == PG_MODE_CHECK )
456+ {
457+ printf (_ ("Bad checksums: %s\n" ), psprintf (INT64_FORMAT , badblocks ));
458+ printf (_ ("Data checksum version: %d\n" ), ControlFile -> data_checksum_version );
459+
460+ if (badblocks > 0 )
461+ exit (1 );
462+ }
463+ }
464+
465+ /*
466+ * Finally make the data durable on disk if enabling or disabling
467+ * checksums. Flush first the data directory for safety, and then update
468+ * the control file to keep the switch consistent.
469+ */
470+ if (mode == PG_MODE_ENABLE || mode == PG_MODE_DISABLE )
471+ {
472+ ControlFile -> data_checksum_version =
473+ (mode == PG_MODE_ENABLE ) ? PG_DATA_CHECKSUM_VERSION : 0 ;
351474
352- /* Scan all files */
353- scan_directory (DataDir , "global" );
354- scan_directory (DataDir , "base" );
355- scan_directory (DataDir , "pg_tblspc" );
475+ printf (_ ("Syncing data directory\n" ));
476+ fsync_pgdata (DataDir , progname , PG_VERSION_NUM );
356477
357- printf (_ ("Checksum scan completed\n" ));
358- printf (_ ("Data checksum version: %d\n" ), ControlFile -> data_checksum_version );
359- printf (_ ("Files scanned: %s\n" ), psprintf (INT64_FORMAT , files ));
360- printf (_ ("Blocks scanned: %s\n" ), psprintf (INT64_FORMAT , blocks ));
361- printf (_ ("Bad checksums: %s\n" ), psprintf (INT64_FORMAT , badblocks ));
478+ printf (_ ("Updating control file\n" ));
479+ update_controlfile (DataDir , progname , ControlFile , true);
362480
363- if (badblocks > 0 )
364- return 1 ;
481+ if (verbose )
482+ printf (_ ("Data checksum version: %d\n" ), ControlFile -> data_checksum_version );
483+ if (mode == PG_MODE_ENABLE )
484+ printf (_ ("Checksums enabled in cluster\n" ));
485+ else
486+ printf (_ ("Checksums disabled in cluster\n" ));
487+ }
365488
366489 return 0 ;
367490}
0 commit comments