1/*-------------------------------------------------------------------------
4 * Checks, enables or disables page level checksums for an offline
7 * Copyright (c) 2010-2025, PostgreSQL Global Development Group
10 * src/bin/pg_checksums/pg_checksums.c
12 *-------------------------------------------------------------------------
60 * Progress status information.
69 printf(
_(
"%s enables, disables, or verifies data checksums in a PostgreSQL database cluster.\n\n"),
progname);
73 printf(
_(
" [-D, --pgdata=]DATADIR data directory\n"));
74 printf(
_(
" -c, --check check data checksums (default)\n"));
75 printf(
_(
" -d, --disable disable data checksums\n"));
76 printf(
_(
" -e, --enable enable data checksums\n"));
77 printf(
_(
" -f, --filenode=FILENODE check only relation with specified filenode\n"));
78 printf(
_(
" -N, --no-sync do not wait for changes to be written safely to disk\n"));
79 printf(
_(
" -P, --progress show progress information\n"));
80 printf(
_(
" --sync-method=METHOD set method for syncing files to disk\n"));
81 printf(
_(
" -v, --verbose output verbose messages\n"));
82 printf(
_(
" -V, --version output version information, then exit\n"));
83 printf(
_(
" -?, --help show this help, then exit\n"));
84 printf(
_(
"\nIf no data directory (DATADIR) is specified, "
85 "the environment variable PGDATA\nis used.\n\n"));
86 printf(
_(
"Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
87 printf(
_(
"%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
91 * Definition of one element part of an exclusion list, used for files
92 * to exclude from checksum validation. "name" is the name of the file
93 * or path to check for exclusion. If "match_prefix" is true, any items
94 * matching the name as prefix are excluded.
103 * List of files excluded from checksum validation.
105 * Note: this list should be kept in sync with what basebackup.c includes.
108 {
"pg_control",
false},
109 {
"pg_filenode.map",
false},
110 {
"pg_internal.init",
true},
111 {
"PG_VERSION",
false},
113 {
"config_exec_params",
true},
119 * Report current progress status. Parts borrowed from
120 * src/bin/pg_basebackup/pg_basebackup.c.
132 return;
/* Max once per second */
134 /* Save current time */
137 /* Adjust total size if current_size is larger */
141 /* Calculate current percentage of size done */
144 fprintf(stderr,
_(
"%" PRId64
"/%" PRId64
" MB (%d%%) computed"),
150 * Stay on the same line if reporting to a terminal and we're not done
153 fputc((!finished && isatty(fileno(stderr))) ?
'\r' :
'\n', stderr);
161 for (excludeIdx = 0;
skip[excludeIdx].
name != NULL; excludeIdx++)
163 int cmplen = strlen(
skip[excludeIdx].
name);
167 if (strncmp(
skip[excludeIdx].
name,
fn, cmplen) == 0)
182 int64 blocks_written_in_file = 0;
191 pg_fatal(
"could not open file \"%s\": %m",
fn);
195 for (blockno = 0;; blockno++)
198 int r =
read(f,
buf.data, BLCKSZ);
205 pg_fatal(
"could not read block %u in file \"%s\": %m",
208 pg_fatal(
"could not read block %u in file \"%s\": read %d of %d",
209 blockno,
fn, r, BLCKSZ);
214 * Since the file size is counted as total_size for progress status
215 * information, the sizes of all pages including new ones in the file
216 * should be counted as current_size. Otherwise the progress reporting
217 * calculated using those counters may not reach 100%.
221 /* New pages have no checksum yet */
231 pg_log_error(
"checksum verification failed in file \"%s\", block %u: calculated checksum %X but block contains %X",
241 * Do not rewrite if the checksum is already set to the expected
247 blocks_written_in_file++;
249 /* Set checksum in page header */
252 /* Seek back to beginning of block */
253 if (lseek(f, -BLCKSZ, SEEK_CUR) < 0)
254 pg_fatal(
"seek failed for block %u in file \"%s\": %m", blockno,
fn);
256 /* Write block with checksum */
261 pg_fatal(
"could not write block %u in file \"%s\": %m",
264 pg_fatal(
"could not write block %u in file \"%s\": wrote %d of %d",
265 blockno,
fn, w, BLCKSZ);
281 /* Update write counters if any write activity has happened */
282 if (blocks_written_in_file > 0)
292 * Scan the given directory for items which can be checksummed and
293 * operate on each one of them. If "sizeonly" is true, the size of
294 * all the items which have checksums is computed and returned back
295 * to the caller without operating on the files. This is used to compile
296 * the total size of the data directory for progress reports.
309 pg_fatal(
"could not open directory \"%s\": %m", path);
310 while ((de =
readdir(dir)) != NULL)
315 if (strcmp(de->
d_name,
".") == 0 ||
316 strcmp(de->
d_name,
"..") == 0)
319 /* Skip temporary files */
325 /* Skip temporary folders */
331 /* Skip macOS system files */
332 if (strcmp(de->
d_name,
".DS_Store") == 0)
337 pg_fatal(
"could not stat file \"%s\": %m",
fn);
349 * Cut off at the segment boundary (".") to get the segment number
350 * in order to mix it into the checksum. Then also cut off at the
351 * fork boundary, to get the filenode the file belongs to for
355 segmentpath = strchr(fnonly,
'.');
356 if (segmentpath != NULL)
358 *segmentpath++ =
'0円';
359 segmentno = atoi(segmentpath);
361 pg_fatal(
"invalid segment number %d in file name \"%s\"",
365 forkpath = strchr(fnonly,
'_');
366 if (forkpath != NULL)
370 /* filenode not to be included */
376 * No need to work on the file when calculating only the size of
377 * the items in the data folder.
385 * If going through the entries of pg_tblspc, we assume to operate
386 * on tablespace locations where only TABLESPACE_VERSION_DIRECTORY
387 * is valid, resolving the linked locations and dive into them
393 struct stat tblspc_st;
396 * Resolve tablespace location path and check whether
397 * TABLESPACE_VERSION_DIRECTORY exists. Not finding a valid
398 * location is unexpected, since there should be no orphaned
399 * links and no links pointing to something else than a
402 snprintf(tblspc_path,
sizeof(tblspc_path),
"%s/%s/%s",
405 if (
lstat(tblspc_path, &tblspc_st) < 0)
406 pg_fatal(
"could not stat file \"%s\": %m",
410 * Move backwards once as the scan needs to happen for the
411 * contents of TABLESPACE_VERSION_DIRECTORY.
413 snprintf(tblspc_path,
sizeof(tblspc_path),
"%s/%s",
416 /* Looks like a valid tablespace location */
434 static struct option long_options[] = {
458 if (strcmp(argv[1],
"--help") == 0 || strcmp(argv[1],
"-?") == 0)
463 if (strcmp(argv[1],
"--version") == 0 || strcmp(argv[1],
"-V") == 0)
465 puts(
"pg_checksums (PostgreSQL) " PG_VERSION);
470 while ((
c =
getopt_long(argc, argv,
"cdD:ef:NPv", long_options, &option_index)) != -1)
507 /* getopt_long already emitted a complaint */
520 /* If no DataDir was specified, and none could be found, error out */
529 /* Complain if any arguments remain */
532 pg_log_error(
"too many command-line arguments (first is \"%s\")",
538 /* filenode checking only works in --check mode */
541 pg_log_error(
"option -f/--filenode can only be used with --check");
546 /* Read the control file and check compatibility */
549 pg_fatal(
"pg_control CRC value is incorrect");
552 pg_fatal(
"cluster is not compatible with this version of pg_checksums");
557 pg_log_error_detail(
"The database cluster was initialized with block size %u, but pg_checksums was compiled with block size %u.",
563 * 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.
569 pg_fatal(
"cluster must be shut down");
573 pg_fatal(
"data checksums are not enabled in cluster");
577 pg_fatal(
"data checksums are already disabled in cluster");
581 pg_fatal(
"data checksums are already enabled in cluster");
583 /* Operate on all files if checking or enabling checksums */
587 * 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
589 * processed and once to do the real work.
605 printf(
_(
"Checksum operation completed\n"));
624 * 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.
645 printf(
_(
"Checksums enabled in cluster\n"));
647 printf(
_(
"Checksums disabled in cluster\n"));
Datum now(PG_FUNCTION_ARGS)
PageHeaderData * PageHeader
static bool PageIsNew(const PageData *page)
#define PG_DATA_CHECKSUM_VERSION
#define PG_TEXTDOMAIN(domain)
uint16 pg_checksum_page(char *page, BlockNumber blkno)
void set_pglocale_pgservice(const char *argv0, const char *app)
void update_controlfile(const char *DataDir, ControlFileData *ControlFile, bool do_sync)
ControlFileData * get_controlfile(const char *DataDir, bool *crc_ok_p)
#define fprintf(file, fmt, msg)
struct dirent * readdir(DIR *)
DIR * opendir(const char *)
#define PG_TEMP_FILES_DIR
#define PG_TEMP_FILE_PREFIX
@ DATA_DIR_SYNC_METHOD_FSYNC
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
#define required_argument
Assert(PointerIsAligned(start, uint64))
void pg_logging_init(const char *argv0)
#define pg_log_error(...)
#define pg_log_error_hint(...)
#define pg_log_error_detail(...)
char * pstrdup(const char *in)
bool option_parse_int(const char *optarg, const char *optname, int min_range, int max_range, int *result)
bool parse_sync_method(const char *optarg, DataDirSyncMethod *sync_method)
int main(int argc, char *argv[])
static void scan_file(const char *fn, int segmentno)
static PgChecksumMode mode
static char * only_filenode
static int64 blocks_scanned
static int64 current_size
static void progress_report(bool finished)
static pg_time_t last_progress_report
static const struct exclude_list_item skip[]
static int64 scan_directory(const char *basedir, const char *subdir, bool sizeonly)
static int64 files_scanned
static bool skipfile(const char *fn)
static ControlFileData * ControlFile
static DataDirSyncMethod sync_method
static int64 blocks_written
static const char * progname
static int64 files_written
#define PG_CONTROL_VERSION
@ DB_SHUTDOWNED_IN_RECOVERY
PGDLLIMPORT char * optarg
const char * get_progname(const char *argv0)
size_t strlcpy(char *dst, const char *src, size_t siz)
#define TABLESPACE_VERSION_DIRECTORY
uint32 pg_control_version
uint32 data_checksum_version
static void * fn(void *arg)