/* * Attempt to read an XLOG record into readRecordBuf. */ static bool ReadRecord(void) { char *buffer; XLogRecord *record; XLogContRecord *contrecord; uint32 len, total_len; int retries = 0; restart: while (logRecOff <= 0 || logRecOff > XLOG_BLCKSZ - SizeOfXLogRecord) { /* Need to advance to new page */ if (! readXLogPage()) return false; logRecOff = XLogPageHeaderSize((XLogPageHeader) pageBuffer); if ((((XLogPageHeader) pageBuffer)->xlp_info & ~XLP_LONG_HEADER) != 0) { printf("Unexpected page info flags %04X at offset %X\n", ((XLogPageHeader) pageBuffer)->xlp_info, logPageOff); /* Check for a continuation record */ if (((XLogPageHeader) pageBuffer)->xlp_info & XLP_FIRST_IS_CONTRECORD) { printf("Skipping unexpected continuation record at offset %X\n", logPageOff); contrecord = (XLogContRecord *) (pageBuffer + logRecOff); logRecOff += MAXALIGN(contrecord->xl_rem_len + SizeOfXLogContRecord); } } } curRecPtr.xlogid = logId; curRecPtr.xrecoff = logSeg * XLogSegSize + logPageOff + logRecOff; record = (XLogRecord *) (pageBuffer + logRecOff); if (record->xl_len == 0) { /* Stop if XLOG_SWITCH was found. */ if (record->xl_rmid == RM_XLOG_ID && record->xl_info == XLOG_SWITCH) { dumpXLogRecord(record, false); return false; } printf("ReadRecord: record with zero len at %u/%08X\n", curRecPtr.xlogid, curRecPtr.xrecoff); /* Attempt to recover on new page, but give up after a few... */ logRecOff = 0; if (++retries > 4) return false; goto restart; } if (record->xl_tot_len < SizeOfXLogRecord + record->xl_len || record->xl_tot_len > SizeOfXLogRecord + record->xl_len + XLR_MAX_BKP_BLOCKS * (sizeof(BkpBlock) + BLCKSZ)) { printf( "invalid record length(expected %lu ~ %lu, actual %d) at %X/%X\n", (unsigned long) (SizeOfXLogRecord + record->xl_len), (unsigned long) (SizeOfXLogRecord + record->xl_len + XLR_MAX_BKP_BLOCKS * (sizeof(BkpBlock) + BLCKSZ)), record->xl_tot_len, curRecPtr.xlogid, curRecPtr.xrecoff); printf("HINT: Make sure you're using the correct xlogdump binary built against\n" " the same architecture and version of PostgreSQL where the WAL file\n" " comes from.\n"); return false; } total_len = record->xl_tot_len; /* * Allocate or enlarge readRecordBuf as needed. To avoid useless * small increases, round its size to a multiple of XLOG_BLCKSZ, and make * sure it's at least 4*BLCKSZ to start with. (That is enough for all * "normal" records, but very large commit or abort records might need * more space.) */ if (total_len > readRecordBufSize) { uint32 newSize = total_len; newSize += XLOG_BLCKSZ - (newSize % XLOG_BLCKSZ); newSize = Max(newSize, 4 * XLOG_BLCKSZ); if (readRecordBuf) free(readRecordBuf); readRecordBuf = (char *) malloc(newSize); if (!readRecordBuf) { readRecordBufSize = 0; /* We treat this as a "bogus data" condition */ fprintf(stderr, "record length %u at %X/%X too long\n", total_len, curRecPtr.xlogid, curRecPtr.xrecoff); return false; } readRecordBufSize = newSize; } buffer = readRecordBuf; len = XLOG_BLCKSZ - curRecPtr.xrecoff % XLOG_BLCKSZ; /* available in block */ if (total_len > len) { /* Need to reassemble record */ uint32 gotlen = len; memcpy(buffer, record, len); record = (XLogRecord *) buffer; buffer += len; for (;;) { uint32 pageHeaderSize; if (! readXLogPage()) { /* XXX ought to be able to advance to new input file! */ fprintf(stderr, "Unable to read continuation page?\n"); dumpXLogRecord(record, true); return false; } if (!(((XLogPageHeader) pageBuffer)->xlp_info & XLP_FIRST_IS_CONTRECORD)) { printf("ReadRecord: there is no ContRecord flag in logfile %u seg %u off %u\n", logId, logSeg, logPageOff); return false; } pageHeaderSize = XLogPageHeaderSize((XLogPageHeader) pageBuffer); contrecord = (XLogContRecord *) (pageBuffer + pageHeaderSize); if (contrecord->xl_rem_len == 0 || total_len != (contrecord->xl_rem_len + gotlen)) { printf("ReadRecord: invalid cont-record len %u in logfile %u seg %u off %u\n", contrecord->xl_rem_len, logId, logSeg, logPageOff); return false; } len = XLOG_BLCKSZ - pageHeaderSize - SizeOfXLogContRecord; if (contrecord->xl_rem_len > len) { memcpy(buffer, (char *)contrecord + SizeOfXLogContRecord, len); gotlen += len; buffer += len; continue; } memcpy(buffer, (char *) contrecord + SizeOfXLogContRecord, contrecord->xl_rem_len); logRecOff = MAXALIGN(pageHeaderSize + SizeOfXLogContRecord + contrecord->xl_rem_len); break; } if (!RecordIsValid(record, curRecPtr)) return false; return true; } /* Record is contained in this page */ memcpy(buffer, record, total_len); record = (XLogRecord *) buffer; logRecOff += MAXALIGN(total_len); if (!RecordIsValid(record, curRecPtr)) return false; return true; }
/* * Attempt to read an XLOG record into readRecordBuf. * Function adapted from xlogdump utility. */ XLogRecord * readRecord(int *logFd, int *logRecOff, int32 *logPageOff, XLogRecPtr *curRecPtr, uint32 logId, uint32 logSeg, bool ignore_errors) { char *buffer; char *readRecordBuf = NULL; XLogRecord *record; XLogContRecord *contrecord; uint32 len, total_len; int retries = 0; static char pageBuffer[BLCKSZ]; uint32 readRecordBufSize = 0; restart: while (*logRecOff <= 0 || *logRecOff > BLCKSZ - SizeOfXLogRecord) { /* Need to advance to new page */ if (! readXLogPage(logFd, logPageOff, pageBuffer)) return false; *logRecOff = XLogPageHeaderSize((XLogPageHeader) pageBuffer); if ((((XLogPageHeader) pageBuffer)->xlp_info & ~XLP_LONG_HEADER) != 0) { ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Unexpected page info flags %04X at offset %X", ((XLogPageHeader) pageBuffer)->xlp_info, *logPageOff))); /* Check for a continuation record */ if (((XLogPageHeader) pageBuffer)->xlp_info & XLP_FIRST_IS_CONTRECORD) { ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Skipping unexpected continuation record at offset %X", *logPageOff))); contrecord = (XLogContRecord *) (pageBuffer + *logRecOff); *logRecOff += MAXALIGN(contrecord->xl_rem_len + SizeOfXLogContRecord); } } } curRecPtr->xlogid = logId; curRecPtr->xrecoff = logSeg * XLogSegSize + *logPageOff + *logRecOff; record = (XLogRecord *) (pageBuffer + *logRecOff); if (record->xl_len == 0) { ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("ReadRecord: record with zero len at %u/%08X", curRecPtr->xlogid, curRecPtr->xrecoff))); /* Attempt to recover on new page, but give up after a few... */ *logRecOff = 0; if (++retries > 4) return false; goto restart; } if (record->xl_tot_len < SizeOfXLogRecord + record->xl_len || record->xl_tot_len > SizeOfXLogRecord + record->xl_len + XLR_MAX_BKP_BLOCKS * (sizeof(BkpBlock) + BLCKSZ)) { ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("invalid record length at %X/%X", curRecPtr->xlogid, curRecPtr->xrecoff))); return false; } total_len = record->xl_tot_len; /* * Allocate or enlarge readRecordBuf as needed. To avoid useless * small increases, round its size to a multiple of BLCKSZ, and make * sure it's at least 4*BLCKSZ to start with. (That is enough for all * "normal" records, but very large commit or abort records might need * more space.) */ if (total_len > readRecordBufSize) { uint32 newSize = total_len; newSize += BLCKSZ - (newSize % BLCKSZ); newSize = Max(newSize, 4 * BLCKSZ); if (readRecordBuf) pfree(readRecordBuf); readRecordBuf = (char *) palloc(newSize); if (!readRecordBuf) { readRecordBufSize = 0; /* We treat this as a "bogus data" condition */ ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("record length %u at %X/%X too long", total_len, curRecPtr->xlogid, curRecPtr->xrecoff))); return false; } readRecordBufSize = newSize; } buffer = readRecordBuf; len = BLCKSZ - curRecPtr->xrecoff % BLCKSZ; /* available in block */ if (total_len > len) { /* Need to reassemble record */ uint32 gotlen = len; memcpy(buffer, record, len); record = (XLogRecord *) buffer; buffer += len; for (;;) { uint32 pageHeaderSize; if (! readXLogPage(logFd, logPageOff, pageBuffer)) { /* XXX ought to be able to advance to new input file! */ ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Unable to read continuation page?"))); return false; } if (!(((XLogPageHeader) pageBuffer)->xlp_info & XLP_FIRST_IS_CONTRECORD)) { ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("ReadRecord: there is no ContRecord flag in logfile %u seg %u off %u", logId, logSeg, *logPageOff))); return false; } pageHeaderSize = XLogPageHeaderSize((XLogPageHeader) pageBuffer); contrecord = (XLogContRecord *) (pageBuffer + pageHeaderSize); if (contrecord->xl_rem_len == 0 || total_len != (contrecord->xl_rem_len + gotlen)) { ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("ReadRecord: invalid cont-record len %u in logfile %u seg %u off %u", contrecord->xl_rem_len, logId, logSeg, *logPageOff))); return false; } len = BLCKSZ - pageHeaderSize - SizeOfXLogContRecord; if (contrecord->xl_rem_len > len) { memcpy(buffer, (char *)contrecord + SizeOfXLogContRecord, len); gotlen += len; buffer += len; continue; } memcpy(buffer, (char *) contrecord + SizeOfXLogContRecord, contrecord->xl_rem_len); *logRecOff = MAXALIGN(pageHeaderSize + SizeOfXLogContRecord + contrecord->xl_rem_len); break; } if (!recordIsValid(record, curRecPtr) && !ignore_errors) return false; return record; } /* Record is contained in this page */ memcpy(buffer, record, total_len); record = (XLogRecord *) buffer; *logRecOff += MAXALIGN(total_len); if (!recordIsValid(record, curRecPtr) && !ignore_errors) return false; return record; }