1
1
/*-------------------------------------------------------------------------
2
2
*
3
3
* 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
5
6
*
6
7
* Copyright (c) 2010-2019, PostgreSQL Global Development Group
7
8
*
17
18
#include <sys/stat.h>
18
19
#include <unistd.h>
19
20
20
- #include "catalog/pg_control .h"
21
+ #include "access/xlog_internal .h"
21
22
#include "common/controldata_utils.h"
23
+ #include "common/file_perm.h"
24
+ #include "common/file_utils.h"
22
25
#include "getopt_long.h"
23
26
#include "pg_getopt.h"
24
27
#include "storage/bufpage.h"
25
28
#include "storage/checksum.h"
26
29
#include "storage/checksum_impl.h"
27
- #include "storage/fd.h"
28
30
29
31
30
32
static int64 files = 0 ;
@@ -35,16 +37,38 @@ static ControlFileData *ControlFile;
35
37
static char * only_relfilenode = NULL ;
36
38
static bool verbose = false;
37
39
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
+
38
59
static const char * progname ;
39
60
40
61
static void
41
62
usage (void )
42
63
{
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 );
44
65
printf (_ ("Usage:\n" ));
45
66
printf (_ (" %s [OPTION]... [DATADIR]\n" ), progname );
46
67
printf (_ ("\nOptions:\n" ));
47
68
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" ));
48
72
printf (_ (" -v, --verbose output verbose messages\n" ));
49
73
printf (_ (" -r RELFILENODE check only relation with specified relfilenode\n" ));
50
74
printf (_ (" -V, --version output version information, then exit\n" ));
@@ -90,8 +114,14 @@ scan_file(const char *fn, BlockNumber segmentno)
90
114
PageHeader header = (PageHeader ) buf .data ;
91
115
int f ;
92
116
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 );
93
124
94
- f = open (fn , O_RDONLY | PG_BINARY , 0 );
95
125
if (f < 0 )
96
126
{
97
127
fprintf (stderr , _ ("%s: could not open file \"%s\": %s\n" ),
@@ -121,18 +151,47 @@ scan_file(const char *fn, BlockNumber segmentno)
121
151
continue ;
122
152
123
153
csum = pg_checksum_page (buf .data , blockno + segmentno * RELSEG_SIZE );
124
- if (csum != header -> pd_checksum )
154
+ if (mode == PG_MODE_CHECK )
125
155
{
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
+ }
130
183
}
131
184
}
132
185
133
186
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
+ }
136
195
137
196
close (f );
138
197
}
234
293
main (int argc , char * argv [])
235
294
{
236
295
static struct option long_options [] = {
296
+ {"check" , no_argument , NULL , 'c' },
237
297
{"pgdata" , required_argument , NULL , 'D' },
298
+ {"disable" , no_argument , NULL , 'd' },
299
+ {"enable" , no_argument , NULL , 'e' },
238
300
{"verbose" , no_argument , NULL , 'v' },
239
301
{NULL , 0 , NULL , 0 }
240
302
};
@@ -262,10 +324,19 @@ main(int argc, char *argv[])
262
324
}
263
325
}
264
326
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 )
266
328
{
267
329
switch (c )
268
330
{
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 ;
269
340
case 'v' :
270
341
verbose = true;
271
342
break ;
@@ -312,6 +383,15 @@ main(int argc, char *argv[])
312
383
exit (1 );
313
384
}
314
385
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
+
315
395
/* Check if cluster is running */
316
396
ControlFile = get_controlfile (DataDir , progname , & crc_ok );
317
397
if (!crc_ok )
@@ -339,29 +419,72 @@ main(int argc, char *argv[])
339
419
if (ControlFile -> state != DB_SHUTDOWNED &&
340
420
ControlFile -> state != DB_SHUTDOWNED_IN_RECOVERY )
341
421
{
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 );
343
423
exit (1 );
344
424
}
345
425
346
- if (ControlFile -> data_checksum_version == 0 )
426
+ if (ControlFile -> data_checksum_version == 0 &&
427
+ mode == PG_MODE_CHECK )
347
428
{
348
429
fprintf (stderr , _ ("%s: data checksums are not enabled in cluster\n" ), progname );
349
430
exit (1 );
350
431
}
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 ;
351
474
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 );
356
477
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);
362
480
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
+ }
365
488
366
489
return 0 ;
367
490
}
0 commit comments