Newer
Older
/*-------------------------------------------------------------------------
*
* pg_resetxlog.c
* A utility to "zero out" the xlog when it's corrupt beyond recovery.
* Can also rebuild pg_control if needed.
*
* The theory of reset operation is fairly simple:
* 1. Read the existing pg_control (which will include the last
* checkpoint record). If it is an old format then update to
* current format.
* 2. If pg_control is corrupt, attempt to rebuild the values,
* by scanning the old xlog; if it fail then try to guess it.
* 3. Modify pg_control to reflect a "shutdown" state with a checkpoint
* record at the start of xlog.
* 4. Flush the existing xlog files and write a new segment with
* just a checkpoint record in it. The new segment is positioned
* just past the end of the old xlog, so that existing LSNs in
* data pages will appear to be "in the past".
*
* The algorithm of restoring the pg_control value from old xlog file:
* 1. Retrieve all of the active xlog files from xlog direcotry into a list
* by increasing order, according their timeline, log id, segment id.
* 2. Search the list to find the oldest xlog file of the lastest time line.
* 3. Search the records from the oldest xlog file of latest time line
* to the latest xlog file of latest time line, if the checkpoint record
* has been found, update the latest checkpoint and previous checkpoint.
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <dirent.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include "access/multixact.h"
#include "access/xlog_internal.h"
#include "catalog/catversion.h"
#include "catalog/pg_control.h"
#define GUESS 0
#define WAL 1
extern int optind;
extern char *optarg;
static ControlFileData ControlFile; /* pg_control values */
static uint32 newXlogId,
newXlogSeg; /* ID/Segment of new XLOG segment */
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
static uint64 sysidentifier=-1;
/*
* We use a list to store the active xlog files we had found in the
* xlog directory in increasing order according the time line, logid,
* segment id.
*
*/
typedef struct XLogFileName {
TimeLineID tli;
uint32 logid;
uint32 seg;
char fname[256];
struct XLogFileName *next;
} XLogFileName;
/* The list head */
static XLogFileName *xlogfilelist=NULL;
/* LastXLogfile is the latest file in the latest time line,
CurXLogfile is the oldest file in the lastest time line
*/
static XLogFileName *CurXLogFile, *LastXLogFile;
/* The last checkpoint found in xlog file.*/
static CheckPoint lastcheckpoint;
/* The last and previous checkpoint pointers found in xlog file.*/
static XLogRecPtr prevchkp, lastchkp;
/* the database state.*/
static DBState state=DB_SHUTDOWNED;
/* the total checkpoint numbers which had been found in the xlog file.*/
static int found_checkpoint=0;
static bool ReadControlFile(void);
static bool RestoreControlValues(int mode);
static void PrintControlValues(void);
static void UpdateCtlFile4Reset(void);
static void RewriteControlFile(void);
static void KillExistingXLOG(void);
static void WriteEmptyXLOG(void);
static void usage(void);
static void GetXLogFiles(void);
static bool ValidXLogFileName(char * fname);
static bool ValidXLogFileHeader(XLogFileName *segfile);
static bool ValidXLOGPageHeader(XLogPageHeader hdr, uint32 tli, uint32 id, uint32 seg);
static bool CmpXLogFileOT(XLogFileName * f1, XLogFileName *f2);
static bool IsNextSeg(XLogFileName *prev, XLogFileName *cur);
static void InsertXLogFile( char * fname );
static bool ReadXLogPage(void);
static bool RecordIsValid(XLogRecord *record, XLogRecPtr recptr);
static bool FetchRecord(void);
static void UpdateCheckPoint(XLogRecord *record);
static void SelectStartXLog(void);
static int SearchLastCheckpoint(void);
static int OpenXLogFile(XLogFileName *sf);
static void CleanUpList(XLogFileName *list);
int
main(int argc, char *argv[])
{
int c;
bool force = false;
bool restore = false;
bool noupdate = false;
TransactionId set_xid = 0;
Oid set_oid = 0;
MultiXactOffset set_mxoff = -1;
uint32 minXlogTli = 0,
minXlogId = 0,
minXlogSeg = 0;
char *endptr;
char *endptr2;
char *DataDir;
int fd;
char path[MAXPGPATH];
bool ctlcorrupted = false;
bool PidLocked = false;
set_pglocale_pgservice(argv[0], "pg_resetxlog");
progname = get_progname(argv[0]);
if (argc > 1)
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
usage();
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
{
puts("pg_resetxlog (PostgreSQL) " PG_VERSION);
exit(0);
}
}
while ((c = getopt(argc, argv, "fl:m:no:O:x:r")) != -1)
{
switch (c)
{
case 'f':
force = true;
break;
case 'r':
restore = true;
break;
case 'n':
noupdate = true;
break;
case 'x':
set_xid = strtoul(optarg, &endptr, 0);
if (endptr == optarg || *endptr != '\0')
{
fprintf(stderr, _("%s: invalid argument for option -x\n"), progname);
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
}
if (set_xid == 0)
{
fprintf(stderr, _("%s: transaction ID (-x) must not be 0\n"), progname);
exit(1);
}
break;
case 'o':
set_oid = strtoul(optarg, &endptr, 0);
if (endptr == optarg || *endptr != '\0')
{
fprintf(stderr, _("%s: invalid argument for option -o\n"), progname);
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
}
if (set_oid == 0)
{
fprintf(stderr, _("%s: OID (-o) must not be 0\n"), progname);
exit(1);
}
break;
case 'm':
set_mxid = strtoul(optarg, &endptr, 0);
if (endptr == optarg || *endptr != '\0')
{
fprintf(stderr, _("%s: invalid argument for option -m\n"), progname);
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
}
if (set_mxid == 0)
{
fprintf(stderr, _("%s: multitransaction ID (-m) must not be 0\n"), progname);
exit(1);
}
break;
case 'O':
set_mxoff = strtoul(optarg, &endptr, 0);
if (endptr == optarg || *endptr != '\0')
{
fprintf(stderr, _("%s: invalid argument for option -O\n"), progname);
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
}
if (set_mxoff == -1)
{
fprintf(stderr, _("%s: multitransaction offset (-O) must not be -1\n"), progname);
exit(1);
}
break;
case 'l':
minXlogTli = strtoul(optarg, &endptr, 0);
if (endptr == optarg || *endptr != ',')
{
fprintf(stderr, _("%s: invalid argument for option -l\n"), progname);
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
}
minXlogId = strtoul(endptr + 1, &endptr2, 0);
if (endptr2 == endptr + 1 || *endptr2 != ',')
{
fprintf(stderr, _("%s: invalid argument for option -l\n"), progname);
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
}
minXlogSeg = strtoul(endptr2 + 1, &endptr3, 0);
if (endptr3 == endptr2 + 1 || *endptr3 != '\0')
{
fprintf(stderr, _("%s: invalid argument for option -l\n"), progname);
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
}
break;
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
}
}
if (optind == argc)
{
fprintf(stderr, _("%s: no data directory specified\n"), progname);
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
}
/*
* Don't allow pg_resetxlog to be run as root, to avoid overwriting the
* ownership of files in the data directory. We need only check for root
* -- any other user won't have sufficient permissions to modify files in
* the data directory.
*/
#ifndef WIN32
if (geteuid() == 0)
{
fprintf(stderr, _("%s: cannot be executed by \"root\"\n"),
progname);
fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"),
progname);
exit(1);
}
#endif
DataDir = argv[optind];
if (chdir(DataDir) < 0)
{
fprintf(stderr, _("%s: could not change directory to \"%s\": %s\n"),
progname, DataDir, strerror(errno));
exit(1);
}
/*
* Check for a postmaster lock file --- if there is one, refuse to
* proceed, on grounds we might be interfering with a live installation.
*/
snprintf(path, MAXPGPATH, "%s/postmaster.pid", DataDir);
if ((fd = open(path, O_RDONLY)) < 0)
{
if (errno != ENOENT)
{
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, path, strerror(errno));
exit(1);
}
}
else
{
PidLocked = true;
}
/*
* Attempt to read the existing pg_control file
*/
if (!ReadControlFile())
{
/* The control file has been corruptted.*/
ctlcorrupted = true;
}
* Adjust fields if required by switches. (Do this now so that printout,
* if any, includes these values.)
*/
if (set_xid != 0)
ControlFile.checkPointCopy.nextXid = set_xid;
if (set_oid != 0)
ControlFile.checkPointCopy.nextOid = set_oid;
if (set_mxid != 0)
ControlFile.checkPointCopy.nextMulti = set_mxid;
if (set_mxoff != -1)
ControlFile.checkPointCopy.nextMultiOffset = set_mxoff;
if (minXlogTli > ControlFile.checkPointCopy.ThisTimeLineID)
ControlFile.checkPointCopy.ThisTimeLineID = minXlogTli;
if (minXlogId > ControlFile.logId ||
(minXlogId == ControlFile.logId &&
minXlogSeg > ControlFile.logSeg))
{
ControlFile.logId = minXlogId;
ControlFile.logSeg = minXlogSeg;
}
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
/* retore the broken control file from WAL file.*/
if (restore)
{
/* If the control fine is fine, don't touch it.*/
if ( !ctlcorrupted )
{
printf(_("\nThe control file seems fine, not need to restore it.\n"));
printf(_("If you want to restore it anyway, use -f option, but this also will reset the log file.\n"));
exit(0);
}
/* Try to restore control values from old xlog file, or complain it.*/
if (RestoreControlValues(WAL))
{
/* Success in restoring the checkpoint information from old xlog file.*/
/* Print it out.*/
PrintControlValues();
/* In case the postmaster is crashed.
* But it may be dangerous for the living one.
* It may need a more good way.
*/
if (PidLocked)
{
ControlFile.state = DB_IN_PRODUCTION;
}
/* Write the new control file. */
RewriteControlFile();
printf(_("\nThe control file had been restored.\n"));
}
else
{
/* Fail in restoring the checkpoint information from old xlog file. */
printf(_("\nCan not restore the control file from XLog file..\n"));
printf(_("\nIf you want to restore it anyway, use -f option to guess the information, but this also will reset the log file.\n"));
}
exit(0);
}
if (PidLocked)
{
fprintf(stderr, _("%s: lock file \"%s\" exists\n"
"Is a server running? If not, delete the lock file and try again.\n"),
progname, path);
exit(1);
}
/*
* Print out the values in control file if -n is given. if the control file is
* corrupted, then inform user to restore it first.
*/
if (noupdate)
{
if (!ctlcorrupted)
{
/* The control file is fine, print the values out.*/
PrintControlValues();
exit(0);
}
else{
/* The control file is corrupted.*/
printf(_("The control file had been corrupted.\n"));
printf(_("Please use -r option to restore it first.\n"));
exit(1);
}
}
/*
* Don't reset from a dirty pg_control without -f, either.
*/
if (ControlFile.state != DB_SHUTDOWNED && !force && !ctlcorrupted)
{
printf(_("The database server was not shut down cleanly.\n"
"Resetting the transaction log may cause data to be lost.\n"
"If you want to proceed anyway, use -f to force reset.\n"));
exit(1);
}
/*
* Try to reset the xlog file.
*/
/* If the control file is corrupted, and -f option is given, resotre it first.*/
if ( ctlcorrupted )
{
if (force)
{
if (!RestoreControlValues(WAL))
{
printf(_("fails to recover the control file from old xlog files, so we had to guess it.\n"));
RestoreControlValues(GUESS);
}
printf(_("Restored the control file from old xlog files.\n"));
}
else
{
printf(_("Control file corrupted.\nIf you want to proceed anyway, use -f to force reset.\n"));
exit(1);
}
}
/* Reset the xlog fille.*/
UpdateCtlFile4Reset();
RewriteControlFile();
KillExistingXLOG();
WriteEmptyXLOG();
printf(_("Transaction log reset\n"));
return 0;
}
/*
* Try to read the existing pg_control file.
*
* This routine is also responsible for updating old pg_control versions
* to the current format. (Currently we don't do anything of the sort.)
*/
static bool
ReadControlFile(void)
{
int fd;
int len;
char *buffer;
pg_crc32 crc;
if ((fd = open(XLOG_CONTROL_FILE, O_RDONLY)) < 0)
* If pg_control is not there at all, or we can't read it, the odds
* are we've been handed a bad DataDir path, so give up. User can do
* "touch pg_control" to force us to proceed.
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
progname, XLOG_CONTROL_FILE, strerror(errno));
fprintf(stderr, _("If you are sure the data directory path is correct, execute\n"
" touch %s\n"
"and try again.\n"),
XLOG_CONTROL_FILE);
exit(1);
}
/* Use malloc to ensure we have a maxaligned buffer */
buffer = (char *) malloc(PG_CONTROL_SIZE);
len = read(fd, buffer, PG_CONTROL_SIZE);
fprintf(stderr, _("%s: could not read file \"%s\": %s\n"),
progname, XLOG_CONTROL_FILE, strerror(errno));
exit(1);
}
close(fd);
if (len >= sizeof(ControlFileData) &&
((ControlFileData *) buffer)->pg_control_version == PG_CONTROL_VERSION)
INIT_CRC32(crc);
COMP_CRC32(crc,
buffer,
offsetof(ControlFileData, crc));
FIN_CRC32(crc);
if (EQ_CRC32(crc, ((ControlFileData *) buffer)->crc))
{
/* Valid data... */
memcpy(&ControlFile, buffer, sizeof(ControlFile));
return true;
}
fprintf(stderr, _("%s: pg_control exists but has invalid CRC; proceed with caution\n"),
progname);
/* We will use the data anyway, but treat it as guessed. */
memcpy(&ControlFile, buffer, sizeof(ControlFile));
return true;
}
/* Looks like it's a mess. */
fprintf(stderr, _("%s: pg_control exists but is broken or unknown version; ignoring it\n"),
progname);
* Restore the pg_control values by scanning old xlog files or by guessing it.
*
* Input parameter:
* WAL: Restore the pg_control values by scanning old xlog files.
* GUESS: Restore the pg_control values by guessing.
* Return:
* TRUE: success in restoring.
* FALSE: fail to restore the values.
*
static bool
RestoreControlValues(int mode)
struct timeval tv;
bool successed = true;
/*
* Set up a completely default set of pg_control values.
*/
memset(&ControlFile, 0, sizeof(ControlFile));
ControlFile.pg_control_version = PG_CONTROL_VERSION;
ControlFile.catalog_version_no = CATALOG_VERSION_NO;
/*
* update the checkpoint value in control file,by searching
* xlog segment file, or just guessing it.
*/
if (mode == WAL)
{
int result = SearchLastCheckpoint();
if (result > 0) /* The last checkpoint had been found. */
{
ControlFile.checkPointCopy = lastcheckpoint;
ControlFile.checkPointCopy.ThisTimeLineID = LastXLogFile->tli;
ControlFile.checkPoint = lastchkp;
ControlFile.prevCheckPoint = prevchkp;
ControlFile.logId = LastXLogFile->logid;
ControlFile.logSeg = LastXLogFile->seg + 1;
ControlFile.state = state;
}
else
successed = false;
/* Clean up the list. */
CleanUpList(xlogfilelist);
}
else /* GUESS */
ControlFile.checkPointCopy.ThisTimeLineID = 2;
ControlFile.checkPointCopy.redo.xlogid = 0;
ControlFile.checkPointCopy.redo.xrecoff = SizeOfXLogLongPHD;
ControlFile.checkPointCopy.undo = ControlFile.checkPointCopy.redo;
ControlFile.checkPointCopy.nextXid = (TransactionId) 514; /* XXX */
ControlFile.checkPointCopy.nextOid = FirstBootstrapObjectId;
ControlFile.checkPointCopy.nextMulti = FirstMultiXactId;
ControlFile.checkPointCopy.nextMultiOffset = 0;
ControlFile.checkPointCopy.time = time(NULL);
ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
/*
* Create a new unique installation identifier, since we can no longer
* use any old XLOG records. See notes in xlog.c about the algorithm.
*/
gettimeofday(&tv, NULL);
sysidentifier = ((uint64) tv.tv_sec) << 32;
sysidentifier |= (uint32) (tv.tv_sec | tv.tv_usec);
ControlFile.state = DB_SHUTDOWNED;
}
ControlFile.system_identifier = sysidentifier;
ControlFile.maxAlign = MAXIMUM_ALIGNOF;
ControlFile.floatFormat = FLOATFORMAT_VALUE;
ControlFile.blcksz = BLCKSZ;
ControlFile.relseg_size = RELSEG_SIZE;
ControlFile.xlog_blcksz = XLOG_BLCKSZ;
ControlFile.xlog_seg_size = XLOG_SEG_SIZE;
ControlFile.nameDataLen = NAMEDATALEN;
ControlFile.indexMaxKeys = INDEX_MAX_KEYS;
#ifdef HAVE_INT64_TIMESTAMP
ControlFile.enableIntTimes = TRUE;
#else
ControlFile.enableIntTimes = FALSE;
#endif
ControlFile.localeBuflen = LOCALE_NAME_BUFLEN;
localeptr = setlocale(LC_COLLATE, "");
if (!localeptr)
{
fprintf(stderr, _("%s: invalid LC_COLLATE setting\n"), progname);
exit(1);
}
StrNCpy(ControlFile.lc_collate, localeptr, LOCALE_NAME_BUFLEN);
localeptr = setlocale(LC_CTYPE, "");
if (!localeptr)
{
fprintf(stderr, _("%s: invalid LC_CTYPE setting\n"), progname);
exit(1);
}
StrNCpy(ControlFile.lc_ctype, localeptr, LOCALE_NAME_BUFLEN);
return successed;
* Print the out pg_control values.
*
* NB: this display should be just those fields that will not be
* reset by RewriteControlFile().
*/
static void
PrintControlValues(void)
char sysident_str[32];
printf(_("pg_control values:\n\n"));
/*
* Format system_identifier separately to keep platform-dependent format
* code out of the translatable message string.
*/
snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
ControlFile.system_identifier);
printf(_("pg_control version number: %u\n"), ControlFile.pg_control_version);
printf(_("Catalog version number: %u\n"), ControlFile.catalog_version_no);
printf(_("Database system identifier: %s\n"), sysident_str);
printf(_("Current log file ID: %u\n"), ControlFile.logId);
printf(_("Next log file segment: %u\n"), ControlFile.logSeg);
printf(_("Latest checkpoint's TimeLineID: %u\n"), ControlFile.checkPointCopy.ThisTimeLineID);
printf(_("Latest checkpoint's NextXID: %u\n"), ControlFile.checkPointCopy.nextXid);
printf(_("Latest checkpoint's NextOID: %u\n"), ControlFile.checkPointCopy.nextOid);
printf(_("Latest checkpoint's NextMultiXactId: %u\n"), ControlFile.checkPointCopy.nextMulti);
printf(_("Latest checkpoint's NextMultiOffset: %u\n"), ControlFile.checkPointCopy.nextMultiOffset);
printf(_("Maximum data alignment: %u\n"), ControlFile.maxAlign);
/* we don't print floatFormat since can't say much useful about it */
printf(_("Database block size: %u\n"), ControlFile.blcksz);
printf(_("Blocks per segment of large relation: %u\n"), ControlFile.relseg_size);
printf(_("WAL block size: %u\n"), ControlFile.xlog_blcksz);
printf(_("Bytes per WAL segment: %u\n"), ControlFile.xlog_seg_size);
printf(_("Maximum length of identifiers: %u\n"), ControlFile.nameDataLen);
printf(_("Maximum columns in an index: %u\n"), ControlFile.indexMaxKeys);
printf(_("Date/time type storage: %s\n"),
(ControlFile.enableIntTimes ? _("64-bit integers") : _("floating-point numbers")));
printf(_("Maximum length of locale name: %u\n"), ControlFile.localeBuflen);
printf(_("LC_COLLATE: %s\n"), ControlFile.lc_collate);
printf(_("LC_CTYPE: %s\n"), ControlFile.lc_ctype);
* Update the control file before reseting it.
*/
static void
UpdateCtlFile4Reset(void)
{
/*
* Adjust fields as needed to force an empty XLOG starting at the next
* available segment.
*/
newXlogId = ControlFile.logId;
newXlogSeg = ControlFile.logSeg;
/* adjust in case we are changing segment size */
newXlogSeg *= ControlFile.xlog_seg_size;
newXlogSeg = (newXlogSeg + XLogSegSize - 1) / XLogSegSize;
/* be sure we wrap around correctly at end of a logfile */
NextLogSeg(newXlogId, newXlogSeg);
/* Now we can force the recorded xlog seg size to the right thing. */
ControlFile.xlog_seg_size = XLogSegSize;
ControlFile.checkPointCopy.redo.xlogid = newXlogId;
ControlFile.checkPointCopy.redo.xrecoff =
newXlogSeg * XLogSegSize + SizeOfXLogLongPHD;
ControlFile.checkPointCopy.undo = ControlFile.checkPointCopy.redo;
ControlFile.checkPointCopy.time = time(NULL);
ControlFile.state = DB_SHUTDOWNED;
ControlFile.time = time(NULL);
ControlFile.logId = newXlogId;
ControlFile.logSeg = newXlogSeg + 1;
ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
ControlFile.prevCheckPoint.xlogid = 0;
ControlFile.prevCheckPoint.xrecoff = 0;
}
/*
* Write out the new pg_control file.
*/
static void
RewriteControlFile(void)
{
int fd;
char buffer[PG_CONTROL_SIZE]; /* need not be aligned */
INIT_CRC32(ControlFile.crc);
COMP_CRC32(ControlFile.crc,
(char *) &ControlFile,
offsetof(ControlFileData, crc));
FIN_CRC32(ControlFile.crc);
* We write out PG_CONTROL_SIZE bytes into pg_control, zero-padding the
* excess over sizeof(ControlFileData). This reduces the odds of
* premature-EOF errors when reading pg_control. We'll still fail when we
* check the contents of the file, but hopefully with a more specific
* error than "couldn't read pg_control".
if (sizeof(ControlFileData) > PG_CONTROL_SIZE)
fprintf(stderr,
_("%s: internal error -- sizeof(ControlFileData) is too large ... fix PG_CONTROL_SIZE\n"),
progname);
memset(buffer, 0, PG_CONTROL_SIZE);
memcpy(buffer, &ControlFile, sizeof(ControlFileData));
unlink(XLOG_CONTROL_FILE);
fd = open(XLOG_CONTROL_FILE,
O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
S_IRUSR | S_IWUSR);
fprintf(stderr, _("%s: could not create pg_control file: %s\n"),
progname, strerror(errno));
if (write(fd, buffer, PG_CONTROL_SIZE) != PG_CONTROL_SIZE)
{
/* if write didn't set errno, assume problem is no disk space */
if (errno == 0)
errno = ENOSPC;
fprintf(stderr, _("%s: could not write pg_control file: %s\n"),
progname, strerror(errno));
exit(1);
}
if (fsync(fd) != 0)
{
fprintf(stderr, _("%s: fsync error: %s\n"), progname, strerror(errno));
exit(1);
}
close(fd);
}
/*
* Remove existing XLOG files
*/
static void
KillExistingXLOG(void)
{
DIR *xldir;
struct dirent *xlde;
char path[MAXPGPATH];
xldir = opendir(XLOGDIR);
fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
progname, XLOGDIR, strerror(errno));
exit(1);
}
errno = 0;
while ((xlde = readdir(xldir)) != NULL)
{
if (strlen(xlde->d_name) == 24 &&
strspn(xlde->d_name, "0123456789ABCDEF") == 24)
snprintf(path, MAXPGPATH, "%s/%s", XLOGDIR, xlde->d_name);
fprintf(stderr, _("%s: could not delete file \"%s\": %s\n"),
progname, path, strerror(errno));
* This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
* released version
if (GetLastError() == ERROR_NO_MORE_FILES)
errno = 0;
#endif
fprintf(stderr, _("%s: could not read from directory \"%s\": %s\n"),
progname, XLOGDIR, strerror(errno));
exit(1);
}
closedir(xldir);
}
/*
* Write an empty XLOG file, containing only the checkpoint record
* already set up in ControlFile.
*/
static void
WriteEmptyXLOG(void)
{
char *buffer;
XLogPageHeader page;
XLogLongPageHeader longpage;
pg_crc32 crc;
char path[MAXPGPATH];
int fd;
int nbytes;
/* Use malloc() to ensure buffer is MAXALIGNED */
buffer = (char *) malloc(XLOG_BLCKSZ);
memset(buffer, 0, XLOG_BLCKSZ);
/* Set up the XLOG page header */
page->xlp_info = XLP_LONG_HEADER;
page->xlp_tli = ControlFile.checkPointCopy.ThisTimeLineID;
page->xlp_pageaddr.xlogid =
ControlFile.checkPointCopy.redo.xlogid;
page->xlp_pageaddr.xrecoff =
ControlFile.checkPointCopy.redo.xrecoff - SizeOfXLogLongPHD;
longpage = (XLogLongPageHeader) page;
longpage->xlp_sysid = ControlFile.system_identifier;
longpage->xlp_seg_size = XLogSegSize;
longpage->xlp_xlog_blcksz = XLOG_BLCKSZ;
/* Insert the initial checkpoint record */
record = (XLogRecord *) ((char *) page + SizeOfXLogLongPHD);
record->xl_prev.xlogid = 0;
record->xl_prev.xrecoff = 0;
record->xl_xid = InvalidTransactionId;
record->xl_tot_len = SizeOfXLogRecord + sizeof(CheckPoint);
record->xl_len = sizeof(CheckPoint);
record->xl_info = XLOG_CHECKPOINT_SHUTDOWN;
record->xl_rmid = RM_XLOG_ID;
memcpy(XLogRecGetData(record), &ControlFile.checkPointCopy,
sizeof(CheckPoint));
INIT_CRC32(crc);
COMP_CRC32(crc, &ControlFile.checkPointCopy, sizeof(CheckPoint));
COMP_CRC32(crc, (char *) record + sizeof(pg_crc32),
SizeOfXLogRecord - sizeof(pg_crc32));
FIN_CRC32(crc);
record->xl_crc = crc;
/* Write the first page */
XLogFilePath(path, ControlFile.checkPointCopy.ThisTimeLineID,
newXlogId, newXlogSeg);
unlink(path);
fd = open(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
S_IRUSR | S_IWUSR);
if (fd < 0)
{
fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
progname, path, strerror(errno));
if (write(fd, buffer, XLOG_BLCKSZ) != XLOG_BLCKSZ)
{
/* if write didn't set errno, assume problem is no disk space */
if (errno == 0)
errno = ENOSPC;
fprintf(stderr, _("%s: could not write file \"%s\": %s\n"),
progname, path, strerror(errno));
exit(1);
}
/* Fill the rest of the file with zeroes */
memset(buffer, 0, XLOG_BLCKSZ);
for (nbytes = XLOG_BLCKSZ; nbytes < XLogSegSize; nbytes += XLOG_BLCKSZ)
if (write(fd, buffer, XLOG_BLCKSZ) != XLOG_BLCKSZ)
fprintf(stderr, _("%s: could not write file \"%s\": %s\n"),
progname, path, strerror(errno));
exit(1);
}
}
if (fsync(fd) != 0)
{
fprintf(stderr, _("%s: fsync error: %s\n"), progname, strerror(errno));
exit(1);
}
close(fd);
}
static void
usage(void)
{
printf(_("%s resets the PostgreSQL transaction log.\n\n"), progname);
printf(_("Usage:\n %s [OPTION]... DATADIR\n\n"), progname);
printf(_("Options:\n"));
printf(_(" -f force reset xlog to be done, if the control file is corrupted, then try to restore it.\n"));
printf(_(" -r restore the pg_control file from old XLog files, resets is not done..\n"));
printf(_(" -l TLI,FILE,SEG force minimum WAL starting location for new transaction log\n"));
printf(_(" -n show extracted control values of existing pg_control file.\n"));
printf(_(" -m multiXID set next multi transaction ID\n"));
printf(_(" -o OID set next OID\n"));
printf(_(" -O multiOffset set next multi transaction offset\n"));
printf(_(" -x XID set next transaction ID\n"));
printf(_(" --help show this help, then exit\n"));
printf(_(" --version output version information, then exit\n"));
printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
/*
* The following routines are mainly used for getting pg_control values
* from the xlog file.
*/
/* some local varaibles.*/
static int logFd=0; /* kernel FD for current input file */
static int logRecOff; /* offset of next record in page */