>From 09cdce611dc74082901ca1a646135a5ea1af709c Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Tue, 2 Apr 2013 11:43:23 +0200
Subject: [PATCH] pg_xlogdump: add option for dumping full page writes into a
 directory

---
 contrib/pg_xlogdump/pg_xlogdump.c |   56 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 55 insertions(+), 1 deletion(-)

diff --git a/contrib/pg_xlogdump/pg_xlogdump.c b/contrib/pg_xlogdump/pg_xlogdump.c
index d6d5498..a5e4186 100644
--- a/contrib/pg_xlogdump/pg_xlogdump.c
+++ b/contrib/pg_xlogdump/pg_xlogdump.c
@@ -40,6 +40,7 @@ typedef struct XLogDumpConfig
 	bool		bkp_details;
 	int			stop_after_records;
 	int			already_displayed_records;
+	char		*dump_bkp;
 
 	/* filter options */
 	int			filter_by_rmgr;
@@ -373,6 +374,46 @@ XLogDumpDisplayRecord(XLogDumpConfig *config, XLogRecPtr ReadRecPtr, XLogRecord
 				   bkpb.block, bkpb.hole_offset, bkpb.hole_length);
 		}
 	}
+
+	if (config->dump_bkp != NULL)
+	{
+		int			bkpnum;
+		char	   *blk = (char *) XLogRecGetData(record) + record->xl_len;
+
+		for (bkpnum = 0; bkpnum < XLR_MAX_BKP_BLOCKS; bkpnum++)
+		{
+			BkpBlock	bkpb;
+			char		bkpbdata[BLCKSZ];
+			int			outfd;
+			char		outpath[MAXPGPATH];
+
+			if (!(XLR_BKP_BLOCK(bkpnum) & record->xl_info))
+				continue;
+
+			memcpy(&bkpb, blk, sizeof(BkpBlock));
+			blk += sizeof(BkpBlock);
+
+			memcpy(bkpbdata, blk, bkpb.hole_offset);
+			memset(bkpbdata + bkpb.hole_offset, 0, bkpb.hole_length);
+			memcpy(bkpbdata + bkpb.hole_offset + bkpb.hole_length,
+			       blk + bkpb.hole_offset,
+			       BLCKSZ - bkpb.hole_offset - bkpb.hole_length);
+
+			blk += BLCKSZ - bkpb.hole_length;
+
+			sprintf(outpath, "%s/%u-%u-%u:%u_%s", config->dump_bkp,
+			        bkpb.node.spcNode, bkpb.node.dbNode, bkpb.node.relNode,
+			        bkpb.block, forkNames[bkpb.fork]);
+			outfd = open(outpath, O_WRONLY|O_TRUNC|O_CREAT, S_IRUSR|S_IWUSR);
+			if (outfd < 0)
+				fatal_error("could not open output file %s: %s",
+				            outpath, strerror(errno));
+			if (write(outfd, bkpbdata, BLCKSZ) != BLCKSZ)
+				fatal_error("could not successfully write output block %s: %s",
+				            outpath, strerror(errno));
+			close(outfd);
+		}
+	}
 }
 
 static void
@@ -387,6 +428,7 @@ usage(void)
 	printf("  -?, --help             show this help, then exit\n");
 	printf("\nContent options:\n");
 	printf("  -b, --bkp-details      output detailed information about backup blocks\n");
+	printf("  -d, --dump-bkp=path    dump all full page images into PATH\n");
 	printf("  -e, --end=RECPTR       stop reading at log position RECPTR\n");
 	printf("  -n, --limit=N          number of records to display\n");
 	printf("  -p, --path=PATH        directory in which to find log segment files\n");
@@ -413,6 +455,7 @@ main(int argc, char **argv)
 
 	static struct option long_options[] = {
 		{"bkp-details", no_argument, NULL, 'b'},
+		{"dump-bkp", required_argument, NULL, 'd'},
 		{"end", required_argument, NULL, 'e'},
 		{"help", no_argument, NULL, '?'},
 		{"limit", required_argument, NULL, 'n'},
@@ -438,6 +481,7 @@ main(int argc, char **argv)
 	private.endptr = InvalidXLogRecPtr;
 
 	config.bkp_details = false;
+	config.dump_bkp = NULL;
 	config.stop_after_records = -1;
 	config.already_displayed_records = 0;
 	config.filter_by_rmgr = -1;
@@ -450,7 +494,7 @@ main(int argc, char **argv)
 		goto bad_argument;
 	}
 
-	while ((option = getopt_long(argc, argv, "be:?n:p:r:s:t:Vx:",
+	while ((option = getopt_long(argc, argv, "bd:e:?n:p:r:s:t:Vx:",
 								 long_options, &optindex)) != -1)
 	{
 		switch (option)
@@ -458,6 +502,16 @@ main(int argc, char **argv)
 			case 'b':
 				config.bkp_details = true;
 				break;
+			case 'd':
+				config.dump_bkp = pg_strdup(optarg);
+				if (!verify_directory(config.dump_bkp))
+				{
+					fprintf(stderr,
+					        "%s: path \"%s\" cannot be opened: %s",
+					        progname, config.dump_bkp, strerror(errno));
+					goto bad_argument;
+				}
+				break;
 			case 'e':
 				if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
 				{
-- 
1.7.10.4

