Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * xlogreader.c
4 : * Generic XLog reading facility
5 : *
6 : * Portions Copyright (c) 2013-2017, PostgreSQL Global Development Group
7 : *
8 : * IDENTIFICATION
9 : * src/backend/access/transam/xlogreader.c
10 : *
11 : * NOTES
12 : * See xlogreader.h for more notes on this facility.
13 : *
14 : * This file is compiled as both front-end and backend code, so it
15 : * may not use ereport, server-defined static variables, etc.
16 : *-------------------------------------------------------------------------
17 : */
18 : #include "postgres.h"
19 :
20 : #include "access/transam.h"
21 : #include "access/xlogrecord.h"
22 : #include "access/xlog_internal.h"
23 : #include "access/xlogreader.h"
24 : #include "catalog/pg_control.h"
25 : #include "common/pg_lzcompress.h"
26 : #include "replication/origin.h"
27 :
28 : static bool allocate_recordbuf(XLogReaderState *state, uint32 reclength);
29 :
30 : static bool ValidXLogPageHeader(XLogReaderState *state, XLogRecPtr recptr,
31 : XLogPageHeader hdr);
32 : static bool ValidXLogRecordHeader(XLogReaderState *state, XLogRecPtr RecPtr,
33 : XLogRecPtr PrevRecPtr, XLogRecord *record, bool randAccess);
34 : static bool ValidXLogRecord(XLogReaderState *state, XLogRecord *record,
35 : XLogRecPtr recptr);
36 : static int ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr,
37 : int reqLen);
38 : static void report_invalid_record(XLogReaderState *state, const char *fmt,...) pg_attribute_printf(2, 3);
39 :
40 : static void ResetDecoder(XLogReaderState *state);
41 :
42 : /* size of the buffer allocated for error message. */
43 : #define MAX_ERRORMSG_LEN 1000
44 :
45 : /*
46 : * Construct a string in state->errormsg_buf explaining what's wrong with
47 : * the current record being read.
48 : */
49 : static void
50 0 : report_invalid_record(XLogReaderState *state, const char *fmt,...)
51 : {
52 : va_list args;
53 :
54 0 : fmt = _(fmt);
55 :
56 0 : va_start(args, fmt);
57 0 : vsnprintf(state->errormsg_buf, MAX_ERRORMSG_LEN, fmt, args);
58 0 : va_end(args);
59 0 : }
60 :
61 : /*
62 : * Allocate and initialize a new XLogReader.
63 : *
64 : * Returns NULL if the xlogreader couldn't be allocated.
65 : */
66 : XLogReaderState *
67 9 : XLogReaderAllocate(XLogPageReadCB pagereadfunc, void *private_data)
68 : {
69 : XLogReaderState *state;
70 :
71 9 : state = (XLogReaderState *)
72 : palloc_extended(sizeof(XLogReaderState),
73 : MCXT_ALLOC_NO_OOM | MCXT_ALLOC_ZERO);
74 9 : if (!state)
75 0 : return NULL;
76 :
77 9 : state->max_block_id = -1;
78 :
79 : /*
80 : * Permanently allocate readBuf. We do it this way, rather than just
81 : * making a static array, for two reasons: (1) no need to waste the
82 : * storage in most instantiations of the backend; (2) a static char array
83 : * isn't guaranteed to have any particular alignment, whereas
84 : * palloc_extended() will provide MAXALIGN'd storage.
85 : */
86 9 : state->readBuf = (char *) palloc_extended(XLOG_BLCKSZ,
87 : MCXT_ALLOC_NO_OOM);
88 9 : if (!state->readBuf)
89 : {
90 0 : pfree(state);
91 0 : return NULL;
92 : }
93 :
94 9 : state->read_page = pagereadfunc;
95 : /* system_identifier initialized to zeroes above */
96 9 : state->private_data = private_data;
97 : /* ReadRecPtr and EndRecPtr initialized to zeroes above */
98 : /* readSegNo, readOff, readLen, readPageTLI initialized to zeroes above */
99 9 : state->errormsg_buf = palloc_extended(MAX_ERRORMSG_LEN + 1,
100 : MCXT_ALLOC_NO_OOM);
101 9 : if (!state->errormsg_buf)
102 : {
103 0 : pfree(state->readBuf);
104 0 : pfree(state);
105 0 : return NULL;
106 : }
107 9 : state->errormsg_buf[0] = '\0';
108 :
109 : /*
110 : * Allocate an initial readRecordBuf of minimal size, which can later be
111 : * enlarged if necessary.
112 : */
113 9 : if (!allocate_recordbuf(state, 0))
114 : {
115 0 : pfree(state->errormsg_buf);
116 0 : pfree(state->readBuf);
117 0 : pfree(state);
118 0 : return NULL;
119 : }
120 :
121 9 : return state;
122 : }
123 :
124 : void
125 9 : XLogReaderFree(XLogReaderState *state)
126 : {
127 : int block_id;
128 :
129 306 : for (block_id = 0; block_id <= XLR_MAX_BLOCK_ID; block_id++)
130 : {
131 297 : if (state->blocks[block_id].data)
132 0 : pfree(state->blocks[block_id].data);
133 : }
134 9 : if (state->main_data)
135 9 : pfree(state->main_data);
136 :
137 9 : pfree(state->errormsg_buf);
138 9 : if (state->readRecordBuf)
139 9 : pfree(state->readRecordBuf);
140 9 : pfree(state->readBuf);
141 9 : pfree(state);
142 9 : }
143 :
144 : /*
145 : * Allocate readRecordBuf to fit a record of at least the given length.
146 : * Returns true if successful, false if out of memory.
147 : *
148 : * readRecordBufSize is set to the new buffer size.
149 : *
150 : * To avoid useless small increases, round its size to a multiple of
151 : * XLOG_BLCKSZ, and make sure it's at least 5*Max(BLCKSZ, XLOG_BLCKSZ) to start
152 : * with. (That is enough for all "normal" records, but very large commit or
153 : * abort records might need more space.)
154 : */
155 : static bool
156 9 : allocate_recordbuf(XLogReaderState *state, uint32 reclength)
157 : {
158 9 : uint32 newSize = reclength;
159 :
160 9 : newSize += XLOG_BLCKSZ - (newSize % XLOG_BLCKSZ);
161 9 : newSize = Max(newSize, 5 * Max(BLCKSZ, XLOG_BLCKSZ));
162 :
163 9 : if (state->readRecordBuf)
164 0 : pfree(state->readRecordBuf);
165 9 : state->readRecordBuf =
166 9 : (char *) palloc_extended(newSize, MCXT_ALLOC_NO_OOM);
167 9 : if (state->readRecordBuf == NULL)
168 : {
169 0 : state->readRecordBufSize = 0;
170 0 : return false;
171 : }
172 9 : state->readRecordBufSize = newSize;
173 9 : return true;
174 : }
175 :
176 : /*
177 : * Attempt to read an XLOG record.
178 : *
179 : * If RecPtr is valid, try to read a record at that position. Otherwise
180 : * try to read a record just after the last one previously read.
181 : *
182 : * If the read_page callback fails to read the requested data, NULL is
183 : * returned. The callback is expected to have reported the error; errormsg
184 : * is set to NULL.
185 : *
186 : * If the reading fails for some other reason, NULL is also returned, and
187 : * *errormsg is set to a string with details of the failure.
188 : *
189 : * The returned pointer (or *errormsg) points to an internal buffer that's
190 : * valid until the next call to XLogReadRecord.
191 : */
192 : XLogRecord *
193 12 : XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
194 : {
195 : XLogRecord *record;
196 : XLogRecPtr targetPagePtr;
197 : bool randAccess;
198 : uint32 len,
199 : total_len;
200 : uint32 targetRecOff;
201 : uint32 pageHeaderSize;
202 : bool gotheader;
203 : int readOff;
204 :
205 : /*
206 : * randAccess indicates whether to verify the previous-record pointer of
207 : * the record we're reading. We only do this if we're reading
208 : * sequentially, which is what we initially assume.
209 : */
210 12 : randAccess = false;
211 :
212 : /* reset error state */
213 12 : *errormsg = NULL;
214 12 : state->errormsg_buf[0] = '\0';
215 :
216 12 : ResetDecoder(state);
217 :
218 12 : if (RecPtr == InvalidXLogRecPtr)
219 : {
220 : /* No explicit start point; read the record after the one we just read */
221 0 : RecPtr = state->EndRecPtr;
222 :
223 0 : if (state->ReadRecPtr == InvalidXLogRecPtr)
224 0 : randAccess = true;
225 :
226 : /*
227 : * RecPtr is pointing to end+1 of the previous WAL record. If we're
228 : * at a page boundary, no more records can fit on the current page. We
229 : * must skip over the page header, but we can't do that until we've
230 : * read in the page, since the header size is variable.
231 : */
232 : }
233 : else
234 : {
235 : /*
236 : * Caller supplied a position to start at.
237 : *
238 : * In this case, the passed-in record pointer should already be
239 : * pointing to a valid record starting position.
240 : */
241 12 : Assert(XRecOffIsValid(RecPtr));
242 12 : randAccess = true;
243 : }
244 :
245 12 : state->currRecPtr = RecPtr;
246 :
247 12 : targetPagePtr = RecPtr - (RecPtr % XLOG_BLCKSZ);
248 12 : targetRecOff = RecPtr % XLOG_BLCKSZ;
249 :
250 : /*
251 : * Read the page containing the record into state->readBuf. Request enough
252 : * byte to cover the whole record header, or at least the part of it that
253 : * fits on the same page.
254 : */
255 12 : readOff = ReadPageInternal(state,
256 : targetPagePtr,
257 12 : Min(targetRecOff + SizeOfXLogRecord, XLOG_BLCKSZ));
258 12 : if (readOff < 0)
259 0 : goto err;
260 :
261 : /*
262 : * ReadPageInternal always returns at least the page header, so we can
263 : * examine it now.
264 : */
265 12 : pageHeaderSize = XLogPageHeaderSize((XLogPageHeader) state->readBuf);
266 12 : if (targetRecOff == 0)
267 : {
268 : /*
269 : * At page start, so skip over page header.
270 : */
271 0 : RecPtr += pageHeaderSize;
272 0 : targetRecOff = pageHeaderSize;
273 : }
274 12 : else if (targetRecOff < pageHeaderSize)
275 : {
276 0 : report_invalid_record(state, "invalid record offset at %X/%X",
277 0 : (uint32) (RecPtr >> 32), (uint32) RecPtr);
278 0 : goto err;
279 : }
280 :
281 12 : if ((((XLogPageHeader) state->readBuf)->xlp_info & XLP_FIRST_IS_CONTRECORD) &&
282 : targetRecOff == pageHeaderSize)
283 : {
284 0 : report_invalid_record(state, "contrecord is requested by %X/%X",
285 0 : (uint32) (RecPtr >> 32), (uint32) RecPtr);
286 0 : goto err;
287 : }
288 :
289 : /* ReadPageInternal has verified the page header */
290 12 : Assert(pageHeaderSize <= readOff);
291 :
292 : /*
293 : * Read the record length.
294 : *
295 : * NB: Even though we use an XLogRecord pointer here, the whole record
296 : * header might not fit on this page. xl_tot_len is the first field of the
297 : * struct, so it must be on this page (the records are MAXALIGNed), but we
298 : * cannot access any other fields until we've verified that we got the
299 : * whole header.
300 : */
301 12 : record = (XLogRecord *) (state->readBuf + RecPtr % XLOG_BLCKSZ);
302 12 : total_len = record->xl_tot_len;
303 :
304 : /*
305 : * If the whole record header is on this page, validate it immediately.
306 : * Otherwise do just a basic sanity check on xl_tot_len, and validate the
307 : * rest of the header after reading it from the next page. The xl_tot_len
308 : * check is necessary here to ensure that we enter the "Need to reassemble
309 : * record" code path below; otherwise we might fail to apply
310 : * ValidXLogRecordHeader at all.
311 : */
312 12 : if (targetRecOff <= XLOG_BLCKSZ - SizeOfXLogRecord)
313 : {
314 12 : if (!ValidXLogRecordHeader(state, RecPtr, state->ReadRecPtr, record,
315 : randAccess))
316 0 : goto err;
317 12 : gotheader = true;
318 : }
319 : else
320 : {
321 : /* XXX: more validation should be done here */
322 0 : if (total_len < SizeOfXLogRecord)
323 : {
324 0 : report_invalid_record(state,
325 : "invalid record length at %X/%X: wanted %u, got %u",
326 0 : (uint32) (RecPtr >> 32), (uint32) RecPtr,
327 : (uint32) SizeOfXLogRecord, total_len);
328 0 : goto err;
329 : }
330 0 : gotheader = false;
331 : }
332 :
333 : /*
334 : * Enlarge readRecordBuf as needed.
335 : */
336 12 : if (total_len > state->readRecordBufSize &&
337 0 : !allocate_recordbuf(state, total_len))
338 : {
339 : /* We treat this as a "bogus data" condition */
340 0 : report_invalid_record(state, "record length %u at %X/%X too long",
341 : total_len,
342 0 : (uint32) (RecPtr >> 32), (uint32) RecPtr);
343 0 : goto err;
344 : }
345 :
346 12 : len = XLOG_BLCKSZ - RecPtr % XLOG_BLCKSZ;
347 12 : if (total_len > len)
348 : {
349 : /* Need to reassemble record */
350 : char *contdata;
351 : XLogPageHeader pageHeader;
352 : char *buffer;
353 : uint32 gotlen;
354 :
355 : /* Copy the first fragment of the record from the first page. */
356 2 : memcpy(state->readRecordBuf,
357 2 : state->readBuf + RecPtr % XLOG_BLCKSZ, len);
358 1 : buffer = state->readRecordBuf + len;
359 1 : gotlen = len;
360 :
361 : do
362 : {
363 : /* Calculate pointer to beginning of next page */
364 1 : targetPagePtr += XLOG_BLCKSZ;
365 :
366 : /* Wait for the next page to become available */
367 1 : readOff = ReadPageInternal(state, targetPagePtr,
368 1 : Min(total_len - gotlen + SizeOfXLogShortPHD,
369 : XLOG_BLCKSZ));
370 :
371 1 : if (readOff < 0)
372 0 : goto err;
373 :
374 1 : Assert(SizeOfXLogShortPHD <= readOff);
375 :
376 : /* Check that the continuation on next page looks valid */
377 1 : pageHeader = (XLogPageHeader) state->readBuf;
378 1 : if (!(pageHeader->xlp_info & XLP_FIRST_IS_CONTRECORD))
379 : {
380 0 : report_invalid_record(state,
381 : "there is no contrecord flag at %X/%X",
382 0 : (uint32) (RecPtr >> 32), (uint32) RecPtr);
383 0 : goto err;
384 : }
385 :
386 : /*
387 : * Cross-check that xlp_rem_len agrees with how much of the record
388 : * we expect there to be left.
389 : */
390 2 : if (pageHeader->xlp_rem_len == 0 ||
391 1 : total_len != (pageHeader->xlp_rem_len + gotlen))
392 : {
393 0 : report_invalid_record(state,
394 : "invalid contrecord length %u at %X/%X",
395 : pageHeader->xlp_rem_len,
396 0 : (uint32) (RecPtr >> 32), (uint32) RecPtr);
397 0 : goto err;
398 : }
399 :
400 : /* Append the continuation from this page to the buffer */
401 1 : pageHeaderSize = XLogPageHeaderSize(pageHeader);
402 :
403 1 : if (readOff < pageHeaderSize)
404 0 : readOff = ReadPageInternal(state, targetPagePtr,
405 : pageHeaderSize);
406 :
407 1 : Assert(pageHeaderSize <= readOff);
408 :
409 1 : contdata = (char *) state->readBuf + pageHeaderSize;
410 1 : len = XLOG_BLCKSZ - pageHeaderSize;
411 1 : if (pageHeader->xlp_rem_len < len)
412 1 : len = pageHeader->xlp_rem_len;
413 :
414 1 : if (readOff < pageHeaderSize + len)
415 0 : readOff = ReadPageInternal(state, targetPagePtr,
416 0 : pageHeaderSize + len);
417 :
418 1 : memcpy(buffer, (char *) contdata, len);
419 1 : buffer += len;
420 1 : gotlen += len;
421 :
422 : /* If we just reassembled the record header, validate it. */
423 1 : if (!gotheader)
424 : {
425 0 : record = (XLogRecord *) state->readRecordBuf;
426 0 : if (!ValidXLogRecordHeader(state, RecPtr, state->ReadRecPtr,
427 : record, randAccess))
428 0 : goto err;
429 0 : gotheader = true;
430 : }
431 1 : } while (gotlen < total_len);
432 :
433 1 : Assert(gotheader);
434 :
435 1 : record = (XLogRecord *) state->readRecordBuf;
436 1 : if (!ValidXLogRecord(state, record, RecPtr))
437 0 : goto err;
438 :
439 1 : pageHeaderSize = XLogPageHeaderSize((XLogPageHeader) state->readBuf);
440 1 : state->ReadRecPtr = RecPtr;
441 2 : state->EndRecPtr = targetPagePtr + pageHeaderSize
442 1 : + MAXALIGN(pageHeader->xlp_rem_len);
443 : }
444 : else
445 : {
446 : /* Wait for the record data to become available */
447 11 : readOff = ReadPageInternal(state, targetPagePtr,
448 11 : Min(targetRecOff + total_len, XLOG_BLCKSZ));
449 11 : if (readOff < 0)
450 0 : goto err;
451 :
452 : /* Record does not cross a page boundary */
453 11 : if (!ValidXLogRecord(state, record, RecPtr))
454 0 : goto err;
455 :
456 11 : state->EndRecPtr = RecPtr + MAXALIGN(total_len);
457 :
458 11 : state->ReadRecPtr = RecPtr;
459 11 : memcpy(state->readRecordBuf, record, total_len);
460 : }
461 :
462 : /*
463 : * Special processing if it's an XLOG SWITCH record
464 : */
465 18 : if (record->xl_rmid == RM_XLOG_ID &&
466 6 : (record->xl_info & ~XLR_INFO_MASK) == XLOG_SWITCH)
467 : {
468 : /* Pretend it extends to end of segment */
469 0 : state->EndRecPtr += XLogSegSize - 1;
470 0 : state->EndRecPtr -= state->EndRecPtr % XLogSegSize;
471 : }
472 :
473 12 : if (DecodeXLogRecord(state, record, errormsg))
474 12 : return record;
475 : else
476 0 : return NULL;
477 :
478 : err:
479 :
480 : /*
481 : * Invalidate the read state. We might read from a different source after
482 : * failure.
483 : */
484 0 : XLogReaderInvalReadState(state);
485 :
486 0 : if (state->errormsg_buf[0] != '\0')
487 0 : *errormsg = state->errormsg_buf;
488 :
489 0 : return NULL;
490 : }
491 :
492 : /*
493 : * Read a single xlog page including at least [pageptr, reqLen] of valid data
494 : * via the read_page() callback.
495 : *
496 : * Returns -1 if the required page cannot be read for some reason; errormsg_buf
497 : * is set in that case (unless the error occurs in the read_page callback).
498 : *
499 : * We fetch the page from a reader-local cache if we know we have the required
500 : * data and if there hasn't been any error since caching the data.
501 : */
502 : static int
503 24 : ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
504 : {
505 : int readLen;
506 : uint32 targetPageOff;
507 : XLogSegNo targetSegNo;
508 : XLogPageHeader hdr;
509 :
510 24 : Assert((pageptr % XLOG_BLCKSZ) == 0);
511 :
512 24 : XLByteToSeg(pageptr, targetSegNo);
513 24 : targetPageOff = (pageptr % XLogSegSize);
514 :
515 : /* check whether we have all the requested data already */
516 38 : if (targetSegNo == state->readSegNo && targetPageOff == state->readOff &&
517 14 : reqLen < state->readLen)
518 14 : return state->readLen;
519 :
520 : /*
521 : * Data is not in our buffer.
522 : *
523 : * Every time we actually read the page, even if we looked at parts of it
524 : * before, we need to do verification as the read_page callback might now
525 : * be rereading data from a different source.
526 : *
527 : * Whenever switching to a new WAL segment, we read the first page of the
528 : * file and validate its header, even if that's not where the target
529 : * record is. This is so that we can check the additional identification
530 : * info that is present in the first page's "long" header.
531 : */
532 10 : if (targetSegNo != state->readSegNo && targetPageOff != 0)
533 : {
534 : XLogPageHeader hdr;
535 8 : XLogRecPtr targetSegmentPtr = pageptr - targetPageOff;
536 :
537 8 : readLen = state->read_page(state, targetSegmentPtr, XLOG_BLCKSZ,
538 : state->currRecPtr,
539 : state->readBuf, &state->readPageTLI);
540 8 : if (readLen < 0)
541 0 : goto err;
542 :
543 : /* we can be sure to have enough WAL available, we scrolled back */
544 8 : Assert(readLen == XLOG_BLCKSZ);
545 :
546 8 : hdr = (XLogPageHeader) state->readBuf;
547 :
548 8 : if (!ValidXLogPageHeader(state, targetSegmentPtr, hdr))
549 0 : goto err;
550 : }
551 :
552 : /*
553 : * First, read the requested data length, but at least a short page header
554 : * so that we can validate it.
555 : */
556 10 : readLen = state->read_page(state, pageptr, Max(reqLen, SizeOfXLogShortPHD),
557 : state->currRecPtr,
558 : state->readBuf, &state->readPageTLI);
559 10 : if (readLen < 0)
560 0 : goto err;
561 :
562 10 : Assert(readLen <= XLOG_BLCKSZ);
563 :
564 : /* Do we have enough data to check the header length? */
565 10 : if (readLen <= SizeOfXLogShortPHD)
566 0 : goto err;
567 :
568 10 : Assert(readLen >= reqLen);
569 :
570 10 : hdr = (XLogPageHeader) state->readBuf;
571 :
572 : /* still not enough */
573 10 : if (readLen < XLogPageHeaderSize(hdr))
574 : {
575 0 : readLen = state->read_page(state, pageptr, XLogPageHeaderSize(hdr),
576 : state->currRecPtr,
577 : state->readBuf, &state->readPageTLI);
578 0 : if (readLen < 0)
579 0 : goto err;
580 : }
581 :
582 : /*
583 : * Now that we know we have the full header, validate it.
584 : */
585 10 : if (!ValidXLogPageHeader(state, pageptr, hdr))
586 0 : goto err;
587 :
588 : /* update read state information */
589 10 : state->readSegNo = targetSegNo;
590 10 : state->readOff = targetPageOff;
591 10 : state->readLen = readLen;
592 :
593 10 : return readLen;
594 :
595 : err:
596 0 : XLogReaderInvalReadState(state);
597 0 : return -1;
598 : }
599 :
600 : /*
601 : * Invalidate the xlogreader's read state to force a re-read.
602 : */
603 : void
604 0 : XLogReaderInvalReadState(XLogReaderState *state)
605 : {
606 0 : state->readSegNo = 0;
607 0 : state->readOff = 0;
608 0 : state->readLen = 0;
609 0 : }
610 :
611 : /*
612 : * Validate an XLOG record header.
613 : *
614 : * This is just a convenience subroutine to avoid duplicated code in
615 : * XLogReadRecord. It's not intended for use from anywhere else.
616 : */
617 : static bool
618 12 : ValidXLogRecordHeader(XLogReaderState *state, XLogRecPtr RecPtr,
619 : XLogRecPtr PrevRecPtr, XLogRecord *record,
620 : bool randAccess)
621 : {
622 12 : if (record->xl_tot_len < SizeOfXLogRecord)
623 : {
624 0 : report_invalid_record(state,
625 : "invalid record length at %X/%X: wanted %u, got %u",
626 0 : (uint32) (RecPtr >> 32), (uint32) RecPtr,
627 : (uint32) SizeOfXLogRecord, record->xl_tot_len);
628 0 : return false;
629 : }
630 12 : if (record->xl_rmid > RM_MAX_ID)
631 : {
632 0 : report_invalid_record(state,
633 : "invalid resource manager ID %u at %X/%X",
634 0 : record->xl_rmid, (uint32) (RecPtr >> 32),
635 : (uint32) RecPtr);
636 0 : return false;
637 : }
638 12 : if (randAccess)
639 : {
640 : /*
641 : * We can't exactly verify the prev-link, but surely it should be less
642 : * than the record's own address.
643 : */
644 12 : if (!(record->xl_prev < RecPtr))
645 : {
646 0 : report_invalid_record(state,
647 : "record with incorrect prev-link %X/%X at %X/%X",
648 0 : (uint32) (record->xl_prev >> 32),
649 0 : (uint32) record->xl_prev,
650 0 : (uint32) (RecPtr >> 32), (uint32) RecPtr);
651 0 : return false;
652 : }
653 : }
654 : else
655 : {
656 : /*
657 : * Record's prev-link should exactly match our previous location. This
658 : * check guards against torn WAL pages where a stale but valid-looking
659 : * WAL record starts on a sector boundary.
660 : */
661 0 : if (record->xl_prev != PrevRecPtr)
662 : {
663 0 : report_invalid_record(state,
664 : "record with incorrect prev-link %X/%X at %X/%X",
665 0 : (uint32) (record->xl_prev >> 32),
666 0 : (uint32) record->xl_prev,
667 0 : (uint32) (RecPtr >> 32), (uint32) RecPtr);
668 0 : return false;
669 : }
670 : }
671 :
672 12 : return true;
673 : }
674 :
675 :
676 : /*
677 : * CRC-check an XLOG record. We do not believe the contents of an XLOG
678 : * record (other than to the minimal extent of computing the amount of
679 : * data to read in) until we've checked the CRCs.
680 : *
681 : * We assume all of the record (that is, xl_tot_len bytes) has been read
682 : * into memory at *record. Also, ValidXLogRecordHeader() has accepted the
683 : * record's header, which means in particular that xl_tot_len is at least
684 : * SizeOfXlogRecord.
685 : */
686 : static bool
687 12 : ValidXLogRecord(XLogReaderState *state, XLogRecord *record, XLogRecPtr recptr)
688 : {
689 : pg_crc32c crc;
690 :
691 : /* Calculate the CRC */
692 12 : INIT_CRC32C(crc);
693 12 : COMP_CRC32C(crc, ((char *) record) + SizeOfXLogRecord, record->xl_tot_len - SizeOfXLogRecord);
694 : /* include the record header last */
695 12 : COMP_CRC32C(crc, (char *) record, offsetof(XLogRecord, xl_crc));
696 12 : FIN_CRC32C(crc);
697 :
698 12 : if (!EQ_CRC32C(record->xl_crc, crc))
699 : {
700 0 : report_invalid_record(state,
701 : "incorrect resource manager data checksum in record at %X/%X",
702 0 : (uint32) (recptr >> 32), (uint32) recptr);
703 0 : return false;
704 : }
705 :
706 12 : return true;
707 : }
708 :
709 : /*
710 : * Validate a page header
711 : */
712 : static bool
713 18 : ValidXLogPageHeader(XLogReaderState *state, XLogRecPtr recptr,
714 : XLogPageHeader hdr)
715 : {
716 : XLogRecPtr recaddr;
717 : XLogSegNo segno;
718 : int32 offset;
719 :
720 18 : Assert((recptr % XLOG_BLCKSZ) == 0);
721 :
722 18 : XLByteToSeg(recptr, segno);
723 18 : offset = recptr % XLogSegSize;
724 :
725 18 : XLogSegNoOffsetToRecPtr(segno, offset, recaddr);
726 :
727 18 : if (hdr->xlp_magic != XLOG_PAGE_MAGIC)
728 : {
729 : char fname[MAXFNAMELEN];
730 :
731 0 : XLogFileName(fname, state->readPageTLI, segno);
732 :
733 0 : report_invalid_record(state,
734 : "invalid magic number %04X in log segment %s, offset %u",
735 0 : hdr->xlp_magic,
736 : fname,
737 : offset);
738 0 : return false;
739 : }
740 :
741 18 : if ((hdr->xlp_info & ~XLP_ALL_FLAGS) != 0)
742 : {
743 : char fname[MAXFNAMELEN];
744 :
745 0 : XLogFileName(fname, state->readPageTLI, segno);
746 :
747 0 : report_invalid_record(state,
748 : "invalid info bits %04X in log segment %s, offset %u",
749 0 : hdr->xlp_info,
750 : fname,
751 : offset);
752 0 : return false;
753 : }
754 :
755 18 : if (hdr->xlp_info & XLP_LONG_HEADER)
756 : {
757 9 : XLogLongPageHeader longhdr = (XLogLongPageHeader) hdr;
758 :
759 12 : if (state->system_identifier &&
760 3 : longhdr->xlp_sysid != state->system_identifier)
761 : {
762 : char fhdrident_str[32];
763 : char sysident_str[32];
764 :
765 : /*
766 : * Format sysids separately to keep platform-dependent format code
767 : * out of the translatable message string.
768 : */
769 0 : snprintf(fhdrident_str, sizeof(fhdrident_str), UINT64_FORMAT,
770 : longhdr->xlp_sysid);
771 0 : snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
772 : state->system_identifier);
773 0 : report_invalid_record(state,
774 : "WAL file is from different database system: WAL file database system identifier is %s, pg_control database system identifier is %s",
775 : fhdrident_str, sysident_str);
776 0 : return false;
777 : }
778 9 : else if (longhdr->xlp_seg_size != XLogSegSize)
779 : {
780 0 : report_invalid_record(state,
781 : "WAL file is from different database system: incorrect XLOG_SEG_SIZE in page header");
782 0 : return false;
783 : }
784 9 : else if (longhdr->xlp_xlog_blcksz != XLOG_BLCKSZ)
785 : {
786 0 : report_invalid_record(state,
787 : "WAL file is from different database system: incorrect XLOG_BLCKSZ in page header");
788 0 : return false;
789 : }
790 : }
791 9 : else if (offset == 0)
792 : {
793 : char fname[MAXFNAMELEN];
794 :
795 0 : XLogFileName(fname, state->readPageTLI, segno);
796 :
797 : /* hmm, first page of file doesn't have a long header? */
798 0 : report_invalid_record(state,
799 : "invalid info bits %04X in log segment %s, offset %u",
800 0 : hdr->xlp_info,
801 : fname,
802 : offset);
803 0 : return false;
804 : }
805 :
806 18 : if (hdr->xlp_pageaddr != recaddr)
807 : {
808 : char fname[MAXFNAMELEN];
809 :
810 0 : XLogFileName(fname, state->readPageTLI, segno);
811 :
812 0 : report_invalid_record(state,
813 : "unexpected pageaddr %X/%X in log segment %s, offset %u",
814 0 : (uint32) (hdr->xlp_pageaddr >> 32), (uint32) hdr->xlp_pageaddr,
815 : fname,
816 : offset);
817 0 : return false;
818 : }
819 :
820 : /*
821 : * Since child timelines are always assigned a TLI greater than their
822 : * immediate parent's TLI, we should never see TLI go backwards across
823 : * successive pages of a consistent WAL sequence.
824 : *
825 : * Sometimes we re-read a segment that's already been (partially) read. So
826 : * we only verify TLIs for pages that are later than the last remembered
827 : * LSN.
828 : */
829 18 : if (recptr > state->latestPagePtr)
830 : {
831 18 : if (hdr->xlp_tli < state->latestPageTLI)
832 : {
833 : char fname[MAXFNAMELEN];
834 :
835 0 : XLogFileName(fname, state->readPageTLI, segno);
836 :
837 0 : report_invalid_record(state,
838 : "out-of-sequence timeline ID %u (after %u) in log segment %s, offset %u",
839 : hdr->xlp_tli,
840 : state->latestPageTLI,
841 : fname,
842 : offset);
843 0 : return false;
844 : }
845 : }
846 18 : state->latestPagePtr = recptr;
847 18 : state->latestPageTLI = hdr->xlp_tli;
848 :
849 18 : return true;
850 : }
851 :
852 : #ifdef FRONTEND
853 : /*
854 : * Functions that are currently not needed in the backend, but are better
855 : * implemented inside xlogreader.c because of the internal facilities available
856 : * here.
857 : */
858 :
859 : /*
860 : * Find the first record with an lsn >= RecPtr.
861 : *
862 : * Useful for checking whether RecPtr is a valid xlog address for reading, and
863 : * to find the first valid address after some address when dumping records for
864 : * debugging purposes.
865 : */
866 : XLogRecPtr
867 : XLogFindNextRecord(XLogReaderState *state, XLogRecPtr RecPtr)
868 : {
869 : XLogReaderState saved_state = *state;
870 : XLogRecPtr tmpRecPtr;
871 : XLogRecPtr found = InvalidXLogRecPtr;
872 : XLogPageHeader header;
873 : char *errormsg;
874 :
875 : Assert(!XLogRecPtrIsInvalid(RecPtr));
876 :
877 : /*
878 : * skip over potential continuation data, keeping in mind that it may span
879 : * multiple pages
880 : */
881 : tmpRecPtr = RecPtr;
882 : while (true)
883 : {
884 : XLogRecPtr targetPagePtr;
885 : int targetRecOff;
886 : uint32 pageHeaderSize;
887 : int readLen;
888 :
889 : /*
890 : * Compute targetRecOff. It should typically be equal or greater than
891 : * short page-header since a valid record can't start anywhere before
892 : * that, except when caller has explicitly specified the offset that
893 : * falls somewhere there or when we are skipping multi-page
894 : * continuation record. It doesn't matter though because
895 : * ReadPageInternal() is prepared to handle that and will read at
896 : * least short page-header worth of data
897 : */
898 : targetRecOff = tmpRecPtr % XLOG_BLCKSZ;
899 :
900 : /* scroll back to page boundary */
901 : targetPagePtr = tmpRecPtr - targetRecOff;
902 :
903 : /* Read the page containing the record */
904 : readLen = ReadPageInternal(state, targetPagePtr, targetRecOff);
905 : if (readLen < 0)
906 : goto err;
907 :
908 : header = (XLogPageHeader) state->readBuf;
909 :
910 : pageHeaderSize = XLogPageHeaderSize(header);
911 :
912 : /* make sure we have enough data for the page header */
913 : readLen = ReadPageInternal(state, targetPagePtr, pageHeaderSize);
914 : if (readLen < 0)
915 : goto err;
916 :
917 : /* skip over potential continuation data */
918 : if (header->xlp_info & XLP_FIRST_IS_CONTRECORD)
919 : {
920 : /*
921 : * If the length of the remaining continuation data is more than
922 : * what can fit in this page, the continuation record crosses over
923 : * this page. Read the next page and try again. xlp_rem_len in the
924 : * next page header will contain the remaining length of the
925 : * continuation data
926 : *
927 : * Note that record headers are MAXALIGN'ed
928 : */
929 : if (MAXALIGN(header->xlp_rem_len) > (XLOG_BLCKSZ - pageHeaderSize))
930 : tmpRecPtr = targetPagePtr + XLOG_BLCKSZ;
931 : else
932 : {
933 : /*
934 : * The previous continuation record ends in this page. Set
935 : * tmpRecPtr to point to the first valid record
936 : */
937 : tmpRecPtr = targetPagePtr + pageHeaderSize
938 : + MAXALIGN(header->xlp_rem_len);
939 : break;
940 : }
941 : }
942 : else
943 : {
944 : tmpRecPtr = targetPagePtr + pageHeaderSize;
945 : break;
946 : }
947 : }
948 :
949 : /*
950 : * we know now that tmpRecPtr is an address pointing to a valid XLogRecord
951 : * because either we're at the first record after the beginning of a page
952 : * or we just jumped over the remaining data of a continuation.
953 : */
954 : while (XLogReadRecord(state, tmpRecPtr, &errormsg) != NULL)
955 : {
956 : /* continue after the record */
957 : tmpRecPtr = InvalidXLogRecPtr;
958 :
959 : /* past the record we've found, break out */
960 : if (RecPtr <= state->ReadRecPtr)
961 : {
962 : found = state->ReadRecPtr;
963 : goto out;
964 : }
965 : }
966 :
967 : err:
968 : out:
969 : /* Reset state to what we had before finding the record */
970 : state->ReadRecPtr = saved_state.ReadRecPtr;
971 : state->EndRecPtr = saved_state.EndRecPtr;
972 : XLogReaderInvalReadState(state);
973 :
974 : return found;
975 : }
976 :
977 : #endif /* FRONTEND */
978 :
979 :
980 : /* ----------------------------------------
981 : * Functions for decoding the data and block references in a record.
982 : * ----------------------------------------
983 : */
984 :
985 : /* private function to reset the state between records */
986 : static void
987 24 : ResetDecoder(XLogReaderState *state)
988 : {
989 : int block_id;
990 :
991 24 : state->decoded_record = NULL;
992 :
993 24 : state->main_data_len = 0;
994 :
995 24 : for (block_id = 0; block_id <= state->max_block_id; block_id++)
996 : {
997 0 : state->blocks[block_id].in_use = false;
998 0 : state->blocks[block_id].has_image = false;
999 0 : state->blocks[block_id].has_data = false;
1000 0 : state->blocks[block_id].apply_image = false;
1001 : }
1002 24 : state->max_block_id = -1;
1003 24 : }
1004 :
1005 : /*
1006 : * Decode the previously read record.
1007 : *
1008 : * On error, a human-readable error message is returned in *errormsg, and
1009 : * the return value is false.
1010 : */
1011 : bool
1012 12 : DecodeXLogRecord(XLogReaderState *state, XLogRecord *record, char **errormsg)
1013 : {
1014 : /*
1015 : * read next _size bytes from record buffer, but check for overrun first.
1016 : */
1017 : #define COPY_HEADER_FIELD(_dst, _size) \
1018 : do { \
1019 : if (remaining < _size) \
1020 : goto shortdata_err; \
1021 : memcpy(_dst, ptr, _size); \
1022 : ptr += _size; \
1023 : remaining -= _size; \
1024 : } while(0)
1025 :
1026 : char *ptr;
1027 : uint32 remaining;
1028 : uint32 datatotal;
1029 12 : RelFileNode *rnode = NULL;
1030 : uint8 block_id;
1031 :
1032 12 : ResetDecoder(state);
1033 :
1034 12 : state->decoded_record = record;
1035 12 : state->record_origin = InvalidRepOriginId;
1036 :
1037 12 : ptr = (char *) record;
1038 12 : ptr += SizeOfXLogRecord;
1039 12 : remaining = record->xl_tot_len - SizeOfXLogRecord;
1040 :
1041 : /* Decode the headers */
1042 12 : datatotal = 0;
1043 24 : while (remaining > datatotal)
1044 : {
1045 12 : COPY_HEADER_FIELD(&block_id, sizeof(uint8));
1046 :
1047 12 : if (block_id == XLR_BLOCK_ID_DATA_SHORT)
1048 : {
1049 : /* XLogRecordDataHeaderShort */
1050 : uint8 main_data_len;
1051 :
1052 6 : COPY_HEADER_FIELD(&main_data_len, sizeof(uint8));
1053 :
1054 6 : state->main_data_len = main_data_len;
1055 6 : datatotal += main_data_len;
1056 6 : break; /* by convention, the main data fragment is
1057 : * always last */
1058 : }
1059 6 : else if (block_id == XLR_BLOCK_ID_DATA_LONG)
1060 : {
1061 : /* XLogRecordDataHeaderLong */
1062 : uint32 main_data_len;
1063 :
1064 6 : COPY_HEADER_FIELD(&main_data_len, sizeof(uint32));
1065 6 : state->main_data_len = main_data_len;
1066 6 : datatotal += main_data_len;
1067 6 : break; /* by convention, the main data fragment is
1068 : * always last */
1069 : }
1070 0 : else if (block_id == XLR_BLOCK_ID_ORIGIN)
1071 : {
1072 0 : COPY_HEADER_FIELD(&state->record_origin, sizeof(RepOriginId));
1073 : }
1074 0 : else if (block_id <= XLR_MAX_BLOCK_ID)
1075 : {
1076 : /* XLogRecordBlockHeader */
1077 : DecodedBkpBlock *blk;
1078 : uint8 fork_flags;
1079 :
1080 0 : if (block_id <= state->max_block_id)
1081 : {
1082 0 : report_invalid_record(state,
1083 : "out-of-order block_id %u at %X/%X",
1084 : block_id,
1085 0 : (uint32) (state->ReadRecPtr >> 32),
1086 0 : (uint32) state->ReadRecPtr);
1087 0 : goto err;
1088 : }
1089 0 : state->max_block_id = block_id;
1090 :
1091 0 : blk = &state->blocks[block_id];
1092 0 : blk->in_use = true;
1093 0 : blk->apply_image = false;
1094 :
1095 0 : COPY_HEADER_FIELD(&fork_flags, sizeof(uint8));
1096 0 : blk->forknum = fork_flags & BKPBLOCK_FORK_MASK;
1097 0 : blk->flags = fork_flags;
1098 0 : blk->has_image = ((fork_flags & BKPBLOCK_HAS_IMAGE) != 0);
1099 0 : blk->has_data = ((fork_flags & BKPBLOCK_HAS_DATA) != 0);
1100 :
1101 0 : COPY_HEADER_FIELD(&blk->data_len, sizeof(uint16));
1102 : /* cross-check that the HAS_DATA flag is set iff data_length > 0 */
1103 0 : if (blk->has_data && blk->data_len == 0)
1104 : {
1105 0 : report_invalid_record(state,
1106 : "BKPBLOCK_HAS_DATA set, but no data included at %X/%X",
1107 0 : (uint32) (state->ReadRecPtr >> 32), (uint32) state->ReadRecPtr);
1108 0 : goto err;
1109 : }
1110 0 : if (!blk->has_data && blk->data_len != 0)
1111 : {
1112 0 : report_invalid_record(state,
1113 : "BKPBLOCK_HAS_DATA not set, but data length is %u at %X/%X",
1114 0 : (unsigned int) blk->data_len,
1115 0 : (uint32) (state->ReadRecPtr >> 32), (uint32) state->ReadRecPtr);
1116 0 : goto err;
1117 : }
1118 0 : datatotal += blk->data_len;
1119 :
1120 0 : if (blk->has_image)
1121 : {
1122 0 : COPY_HEADER_FIELD(&blk->bimg_len, sizeof(uint16));
1123 0 : COPY_HEADER_FIELD(&blk->hole_offset, sizeof(uint16));
1124 0 : COPY_HEADER_FIELD(&blk->bimg_info, sizeof(uint8));
1125 :
1126 0 : blk->apply_image = ((blk->bimg_info & BKPIMAGE_APPLY) != 0);
1127 :
1128 0 : if (blk->bimg_info & BKPIMAGE_IS_COMPRESSED)
1129 : {
1130 0 : if (blk->bimg_info & BKPIMAGE_HAS_HOLE)
1131 0 : COPY_HEADER_FIELD(&blk->hole_length, sizeof(uint16));
1132 : else
1133 0 : blk->hole_length = 0;
1134 : }
1135 : else
1136 0 : blk->hole_length = BLCKSZ - blk->bimg_len;
1137 0 : datatotal += blk->bimg_len;
1138 :
1139 : /*
1140 : * cross-check that hole_offset > 0, hole_length > 0 and
1141 : * bimg_len < BLCKSZ if the HAS_HOLE flag is set.
1142 : */
1143 0 : if ((blk->bimg_info & BKPIMAGE_HAS_HOLE) &&
1144 0 : (blk->hole_offset == 0 ||
1145 0 : blk->hole_length == 0 ||
1146 0 : blk->bimg_len == BLCKSZ))
1147 : {
1148 0 : report_invalid_record(state,
1149 : "BKPIMAGE_HAS_HOLE set, but hole offset %u length %u block image length %u at %X/%X",
1150 0 : (unsigned int) blk->hole_offset,
1151 0 : (unsigned int) blk->hole_length,
1152 0 : (unsigned int) blk->bimg_len,
1153 0 : (uint32) (state->ReadRecPtr >> 32), (uint32) state->ReadRecPtr);
1154 0 : goto err;
1155 : }
1156 :
1157 : /*
1158 : * cross-check that hole_offset == 0 and hole_length == 0 if
1159 : * the HAS_HOLE flag is not set.
1160 : */
1161 0 : if (!(blk->bimg_info & BKPIMAGE_HAS_HOLE) &&
1162 0 : (blk->hole_offset != 0 || blk->hole_length != 0))
1163 : {
1164 0 : report_invalid_record(state,
1165 : "BKPIMAGE_HAS_HOLE not set, but hole offset %u length %u at %X/%X",
1166 0 : (unsigned int) blk->hole_offset,
1167 0 : (unsigned int) blk->hole_length,
1168 0 : (uint32) (state->ReadRecPtr >> 32), (uint32) state->ReadRecPtr);
1169 0 : goto err;
1170 : }
1171 :
1172 : /*
1173 : * cross-check that bimg_len < BLCKSZ if the IS_COMPRESSED
1174 : * flag is set.
1175 : */
1176 0 : if ((blk->bimg_info & BKPIMAGE_IS_COMPRESSED) &&
1177 0 : blk->bimg_len == BLCKSZ)
1178 : {
1179 0 : report_invalid_record(state,
1180 : "BKPIMAGE_IS_COMPRESSED set, but block image length %u at %X/%X",
1181 0 : (unsigned int) blk->bimg_len,
1182 0 : (uint32) (state->ReadRecPtr >> 32), (uint32) state->ReadRecPtr);
1183 0 : goto err;
1184 : }
1185 :
1186 : /*
1187 : * cross-check that bimg_len = BLCKSZ if neither HAS_HOLE nor
1188 : * IS_COMPRESSED flag is set.
1189 : */
1190 0 : if (!(blk->bimg_info & BKPIMAGE_HAS_HOLE) &&
1191 0 : !(blk->bimg_info & BKPIMAGE_IS_COMPRESSED) &&
1192 0 : blk->bimg_len != BLCKSZ)
1193 : {
1194 0 : report_invalid_record(state,
1195 : "neither BKPIMAGE_HAS_HOLE nor BKPIMAGE_IS_COMPRESSED set, but block image length is %u at %X/%X",
1196 0 : (unsigned int) blk->data_len,
1197 0 : (uint32) (state->ReadRecPtr >> 32), (uint32) state->ReadRecPtr);
1198 0 : goto err;
1199 : }
1200 : }
1201 0 : if (!(fork_flags & BKPBLOCK_SAME_REL))
1202 : {
1203 0 : COPY_HEADER_FIELD(&blk->rnode, sizeof(RelFileNode));
1204 0 : rnode = &blk->rnode;
1205 : }
1206 : else
1207 : {
1208 0 : if (rnode == NULL)
1209 : {
1210 0 : report_invalid_record(state,
1211 : "BKPBLOCK_SAME_REL set but no previous rel at %X/%X",
1212 0 : (uint32) (state->ReadRecPtr >> 32), (uint32) state->ReadRecPtr);
1213 0 : goto err;
1214 : }
1215 :
1216 0 : blk->rnode = *rnode;
1217 : }
1218 0 : COPY_HEADER_FIELD(&blk->blkno, sizeof(BlockNumber));
1219 : }
1220 : else
1221 : {
1222 0 : report_invalid_record(state,
1223 : "invalid block_id %u at %X/%X",
1224 : block_id,
1225 0 : (uint32) (state->ReadRecPtr >> 32),
1226 0 : (uint32) state->ReadRecPtr);
1227 0 : goto err;
1228 : }
1229 : }
1230 :
1231 12 : if (remaining != datatotal)
1232 0 : goto shortdata_err;
1233 :
1234 : /*
1235 : * Ok, we've parsed the fragment headers, and verified that the total
1236 : * length of the payload in the fragments is equal to the amount of data
1237 : * left. Copy the data of each fragment to a separate buffer.
1238 : *
1239 : * We could just set up pointers into readRecordBuf, but we want to align
1240 : * the data for the convenience of the callers. Backup images are not
1241 : * copied, however; they don't need alignment.
1242 : */
1243 :
1244 : /* block data first */
1245 12 : for (block_id = 0; block_id <= state->max_block_id; block_id++)
1246 : {
1247 0 : DecodedBkpBlock *blk = &state->blocks[block_id];
1248 :
1249 0 : if (!blk->in_use)
1250 0 : continue;
1251 :
1252 0 : Assert(blk->has_image || !blk->apply_image);
1253 :
1254 0 : if (blk->has_image)
1255 : {
1256 0 : blk->bkp_image = ptr;
1257 0 : ptr += blk->bimg_len;
1258 : }
1259 0 : if (blk->has_data)
1260 : {
1261 0 : if (!blk->data || blk->data_len > blk->data_bufsz)
1262 : {
1263 0 : if (blk->data)
1264 0 : pfree(blk->data);
1265 0 : blk->data_bufsz = blk->data_len;
1266 0 : blk->data = palloc(blk->data_bufsz);
1267 : }
1268 0 : memcpy(blk->data, ptr, blk->data_len);
1269 0 : ptr += blk->data_len;
1270 : }
1271 : }
1272 :
1273 : /* and finally, the main data */
1274 12 : if (state->main_data_len > 0)
1275 : {
1276 12 : if (!state->main_data || state->main_data_len > state->main_data_bufsz)
1277 : {
1278 9 : if (state->main_data)
1279 0 : pfree(state->main_data);
1280 9 : state->main_data_bufsz = state->main_data_len;
1281 9 : state->main_data = palloc(state->main_data_bufsz);
1282 : }
1283 12 : memcpy(state->main_data, ptr, state->main_data_len);
1284 12 : ptr += state->main_data_len;
1285 : }
1286 :
1287 12 : return true;
1288 :
1289 : shortdata_err:
1290 0 : report_invalid_record(state,
1291 : "record with invalid length at %X/%X",
1292 0 : (uint32) (state->ReadRecPtr >> 32), (uint32) state->ReadRecPtr);
1293 : err:
1294 0 : *errormsg = state->errormsg_buf;
1295 :
1296 0 : return false;
1297 : }
1298 :
1299 : /*
1300 : * Returns information about the block that a block reference refers to.
1301 : *
1302 : * If the WAL record contains a block reference with the given ID, *rnode,
1303 : * *forknum, and *blknum are filled in (if not NULL), and returns TRUE.
1304 : * Otherwise returns FALSE.
1305 : */
1306 : bool
1307 0 : XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id,
1308 : RelFileNode *rnode, ForkNumber *forknum, BlockNumber *blknum)
1309 : {
1310 : DecodedBkpBlock *bkpb;
1311 :
1312 0 : if (!record->blocks[block_id].in_use)
1313 0 : return false;
1314 :
1315 0 : bkpb = &record->blocks[block_id];
1316 0 : if (rnode)
1317 0 : *rnode = bkpb->rnode;
1318 0 : if (forknum)
1319 0 : *forknum = bkpb->forknum;
1320 0 : if (blknum)
1321 0 : *blknum = bkpb->blkno;
1322 0 : return true;
1323 : }
1324 :
1325 : /*
1326 : * Returns the data associated with a block reference, or NULL if there is
1327 : * no data (e.g. because a full-page image was taken instead). The returned
1328 : * pointer points to a MAXALIGNed buffer.
1329 : */
1330 : char *
1331 0 : XLogRecGetBlockData(XLogReaderState *record, uint8 block_id, Size *len)
1332 : {
1333 : DecodedBkpBlock *bkpb;
1334 :
1335 0 : if (!record->blocks[block_id].in_use)
1336 0 : return NULL;
1337 :
1338 0 : bkpb = &record->blocks[block_id];
1339 :
1340 0 : if (!bkpb->has_data)
1341 : {
1342 0 : if (len)
1343 0 : *len = 0;
1344 0 : return NULL;
1345 : }
1346 : else
1347 : {
1348 0 : if (len)
1349 0 : *len = bkpb->data_len;
1350 0 : return bkpb->data;
1351 : }
1352 : }
1353 :
1354 : /*
1355 : * Restore a full-page image from a backup block attached to an XLOG record.
1356 : *
1357 : * Returns the buffer number containing the page.
1358 : */
1359 : bool
1360 0 : RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
1361 : {
1362 : DecodedBkpBlock *bkpb;
1363 : char *ptr;
1364 : char tmp[BLCKSZ];
1365 :
1366 0 : if (!record->blocks[block_id].in_use)
1367 0 : return false;
1368 0 : if (!record->blocks[block_id].has_image)
1369 0 : return false;
1370 :
1371 0 : bkpb = &record->blocks[block_id];
1372 0 : ptr = bkpb->bkp_image;
1373 :
1374 0 : if (bkpb->bimg_info & BKPIMAGE_IS_COMPRESSED)
1375 : {
1376 : /* If a backup block image is compressed, decompress it */
1377 0 : if (pglz_decompress(ptr, bkpb->bimg_len, tmp,
1378 0 : BLCKSZ - bkpb->hole_length) < 0)
1379 : {
1380 0 : report_invalid_record(record, "invalid compressed image at %X/%X, block %d",
1381 0 : (uint32) (record->ReadRecPtr >> 32),
1382 0 : (uint32) record->ReadRecPtr,
1383 : block_id);
1384 0 : return false;
1385 : }
1386 0 : ptr = tmp;
1387 : }
1388 :
1389 : /* generate page, taking into account hole if necessary */
1390 0 : if (bkpb->hole_length == 0)
1391 : {
1392 0 : memcpy(page, ptr, BLCKSZ);
1393 : }
1394 : else
1395 : {
1396 0 : memcpy(page, ptr, bkpb->hole_offset);
1397 : /* must zero-fill the hole */
1398 0 : MemSet(page + bkpb->hole_offset, 0, bkpb->hole_length);
1399 0 : memcpy(page + (bkpb->hole_offset + bkpb->hole_length),
1400 0 : ptr + bkpb->hole_offset,
1401 0 : BLCKSZ - (bkpb->hole_offset + bkpb->hole_length));
1402 : }
1403 :
1404 0 : return true;
1405 : }
|