PageRenderTime 67ms CodeModel.GetById 20ms app.highlight 37ms RepoModel.GetById 0ms app.codeStats 0ms

/pg_filedump-9.1.0/pg_filedump.c

#
C | 1420 lines | 1095 code | 137 blank | 188 comment | 246 complexity | 2494cd9ee3f8c3e629d1def3f572b682 MD5 | raw file
   1/*
   2 * pg_filedump.c - PostgreSQL file dump utility for dumping and
   3 *                 formatting heap (data), index and control files.
   4 *
   5 * Copyright (c) 2002-2010 Red Hat, Inc.
   6 * Copyright (c) 2011, PostgreSQL Global Development Group
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21 *
  22 * Original Author: Patrick Macdonald <patrickm@redhat.com> 
  23 */
  24
  25#include "pg_filedump.h"
  26
  27// Global variables for ease of use mostly
  28static FILE *fp = NULL;		// File to dump or format
  29static char *fileName = NULL;	// File name for display
  30static char *buffer = NULL;	// Cache for current block
  31static unsigned int blockSize = 0;	// Current block size
  32static unsigned int currentBlock = 0;	// Current block in file
  33static unsigned int pageOffset = 0;	// Offset of current block
  34static unsigned int bytesToFormat = 0;	// Number of bytes to format
  35static unsigned int blockVersion = 0;	// Block version number
  36
  37// Function Prototypes
  38static void DisplayOptions (unsigned int validOptions);
  39static unsigned int ConsumeOptions (int numOptions, char **options);
  40static int GetOptionValue (char *optionString);
  41static void FormatBlock ();
  42static unsigned int GetBlockSize ();
  43static unsigned int GetSpecialSectionType (Page page);
  44static bool IsBtreeMetaPage(Page page);
  45static void CreateDumpFileHeader (int numOptions, char **options);
  46static int FormatHeader (Page page);
  47static void FormatItemBlock (Page page);
  48static void FormatItem (unsigned int numBytes, unsigned int startIndex,
  49			unsigned int formatAs);
  50static void FormatSpecial ();
  51static void FormatControl ();
  52static void FormatBinary (unsigned int numBytes, unsigned int startIndex);
  53static void DumpBinaryBlock ();
  54static void DumpFileContents ();
  55
  56
  57// Send properly formed usage information to the user. 
  58static void
  59DisplayOptions (unsigned int validOptions)
  60{
  61  if (validOptions == OPT_RC_COPYRIGHT)
  62    printf
  63      ("\nVersion %s (for %s)"
  64       "\nCopyright (c) 2002-2010 Red Hat, Inc."
  65       "\nCopyright (c) 2011, PostgreSQL Global Development Group\n",
  66       FD_VERSION, FD_PG_VERSION);
  67
  68  printf
  69    ("\nUsage: pg_filedump [-abcdfhixy] [-R startblock [endblock]] [-S blocksize] file\n\n"
  70     "Display formatted contents of a PostgreSQL heap/index/control file\n"
  71     "Defaults are: relative addressing, range of the entire file, block\n"
  72     "               size as listed on block 0 in the file\n\n"
  73     "The following options are valid for heap and index files:\n"
  74     "  -a  Display absolute addresses when formatting (Block header\n"
  75     "      information is always block relative)\n"
  76     "  -b  Display binary block images within a range (Option will turn\n"
  77     "      off all formatting options)\n"
  78     "  -d  Display formatted block content dump (Option will turn off\n"
  79     "      all other formatting options)\n"
  80     "  -f  Display formatted block content dump along with interpretation\n"
  81     "  -h  Display this information\n"
  82     "  -i  Display interpreted item details\n"
  83     "  -R  Display specific block ranges within the file (Blocks are\n"
  84     "      indexed from 0)\n" "        [startblock]: block to start at\n"
  85     "        [endblock]: block to end at\n"
  86     "      A startblock without an endblock will format the single block\n"
  87     "  -S  Force block size to [blocksize]\n"
  88     "  -x  Force interpreted formatting of block items as index items\n"
  89     "  -y  Force interpreted formatting of block items as heap items\n\n"
  90     "The following options are valid for control files:\n"
  91     "  -c  Interpret the file listed as a control file\n"
  92     "  -f  Display formatted content dump along with interpretation\n"
  93     "  -S  Force block size to [blocksize]\n"
  94     "\nReport bugs to <pgsql-bugs@postgresql.org>\n");
  95}
  96
  97// Iterate through the provided options and set the option flags.
  98// An error will result in a positive rc and will force a display
  99// of the usage information.  This routine returns enum 
 100// optionReturnCode values.
 101static unsigned int
 102ConsumeOptions (int numOptions, char **options)
 103{
 104  unsigned int rc = OPT_RC_VALID;
 105  unsigned int x;
 106  unsigned int optionStringLength;
 107  char *optionString;
 108  char duplicateSwitch = 0x00;
 109
 110  for (x = 1; x < numOptions; x++)
 111    {
 112      optionString = options[x];
 113      optionStringLength = strlen (optionString);
 114
 115      // Range is a special case where we have to consume the next 1 or 2
 116      // parameters to mark the range start and end
 117      if ((optionStringLength == 2) && (strcmp (optionString, "-R") == 0))
 118	{
 119	  int range = 0;
 120
 121	  SET_OPTION (blockOptions, BLOCK_RANGE, 'R');
 122	  // Only accept the range option once
 123	  if (rc == OPT_RC_DUPLICATE)
 124	    break;
 125
 126	  // Make sure there are options after the range identifier
 127	  if (x >= (numOptions - 2))
 128	    {
 129	      rc = OPT_RC_INVALID;
 130	      printf ("Error: Missing range start identifier.\n");
 131	      break;
 132	    }
 133
 134	  // Mark that we have the range and advance the option to what should
 135	  // be the range start. Check the value of the next parameter
 136	  optionString = options[++x];
 137	  if ((range = GetOptionValue (optionString)) < 0)
 138	    {
 139	      rc = OPT_RC_INVALID;
 140	      printf ("Error: Invalid range start identifier <%s>.\n",
 141		      optionString);
 142	      break;
 143	    }
 144
 145	  // The default is to dump only one block
 146	  blockStart = blockEnd = (unsigned int) range;
 147
 148	  // We have our range start marker, check if there is an end
 149	  // marker on the option line.  Assume that the last option
 150	  // is the file we are dumping, so check if there are options
 151	  // range start marker and the file
 152	  if (x <= (numOptions - 3))
 153	    {
 154	      if ((range = GetOptionValue (options[x + 1])) >= 0)
 155		{
 156		  // End range must be => start range
 157		  if (blockStart <= range)
 158		    {
 159		      blockEnd = (unsigned int) range;
 160		      x++;
 161		    }
 162		  else
 163		    {
 164		      rc = OPT_RC_INVALID;
 165		      printf ("Error: Requested block range start <%d> is "
 166			      "greater than end <%d>.\n", blockStart, range);
 167		      break;
 168		    }
 169		}
 170	    }
 171	}
 172      // Check for the special case where the user forces a block size
 173      // instead of having the tool determine it.  This is useful if  
 174      // the header of block 0 is corrupt and gives a garbage block size
 175      else if ((optionStringLength == 2)
 176	       && (strcmp (optionString, "-S") == 0))
 177	{
 178	  int localBlockSize;
 179
 180	  SET_OPTION (blockOptions, BLOCK_FORCED, 'S');
 181	  // Only accept the forced size option once
 182	  if (rc == OPT_RC_DUPLICATE)
 183	    break;
 184
 185	  // The token immediately following -S is the block size
 186	  if (x >= (numOptions - 2))
 187	    {
 188	      rc = OPT_RC_INVALID;
 189	      printf ("Error: Missing block size identifier.\n");
 190	      break;
 191	    }
 192
 193	  // Next option encountered must be forced block size
 194	  optionString = options[++x];
 195	  if ((localBlockSize = GetOptionValue (optionString)) > 0)
 196	    blockSize = (unsigned int) localBlockSize;
 197	  else
 198	    {
 199	      rc = OPT_RC_INVALID;
 200	      printf ("Error: Invalid block size requested <%s>.\n",
 201		      optionString);
 202	      break;
 203	    }
 204	}
 205      // The last option MUST be the file name
 206      else if (x == (numOptions - 1))
 207	{
 208	  // Check to see if this looks like an option string before opening
 209	  if (optionString[0] != '-')
 210	    {
 211	      fp = fopen (optionString, "rb");
 212	      if (fp)
 213		fileName = options[x];
 214	      else
 215		{
 216		  rc = OPT_RC_FILE;
 217		  printf ("Error: Could not open file <%s>.\n", optionString);
 218		  break;
 219		}
 220	    }
 221	  else
 222	    {
 223	      // Could be the case where the help flag is used without a 
 224	      // filename. Otherwise, the last option isn't a file            
 225	      if (strcmp (optionString, "-h") == 0)
 226		rc = OPT_RC_COPYRIGHT;
 227	      else
 228		{
 229		  rc = OPT_RC_FILE;
 230		  printf ("Error: Missing file name to dump.\n");
 231		}
 232	      break;
 233	    }
 234	}
 235      else
 236	{
 237	  unsigned int y;
 238
 239	  // Option strings must start with '-' and contain switches
 240	  if (optionString[0] != '-')
 241	    {
 242	      rc = OPT_RC_INVALID;
 243	      printf ("Error: Invalid option string <%s>.\n", optionString);
 244	      break;
 245	    }
 246
 247	  // Iterate through the singular option string, throw out
 248	  // garbage, duplicates and set flags to be used in formatting
 249	  for (y = 1; y < optionStringLength; y++)
 250	    {
 251	      switch (optionString[y])
 252		{
 253		  // Use absolute addressing              
 254		case 'a':
 255		  SET_OPTION (blockOptions, BLOCK_ABSOLUTE, 'a');
 256		  break;
 257
 258		  // Dump the binary contents of the page 
 259		case 'b':
 260		  SET_OPTION (blockOptions, BLOCK_BINARY, 'b');
 261		  break;
 262
 263		  // Dump the listed file as a control file
 264		case 'c':
 265		  SET_OPTION (controlOptions, CONTROL_DUMP, 'c');
 266		  break;
 267
 268		  // Do not interpret the data. Format to hex and ascii.
 269		case 'd':
 270		  SET_OPTION (blockOptions, BLOCK_NO_INTR, 'd');
 271		  break;
 272
 273		  // Format the contents of the block with interpretation
 274		  // of the headers
 275		case 'f':
 276		  SET_OPTION (blockOptions, BLOCK_FORMAT, 'f');
 277		  break;
 278
 279		  // Display the usage screen  
 280		case 'h':
 281		  rc = OPT_RC_COPYRIGHT;
 282		  break;
 283
 284		  // Format the items in detail
 285		case 'i':
 286		  SET_OPTION (itemOptions, ITEM_DETAIL, 'i');
 287		  break;
 288
 289		  // Interpret items as index values
 290		case 'x':
 291		  SET_OPTION (itemOptions, ITEM_INDEX, 'x');
 292		  if (itemOptions & ITEM_HEAP)
 293		    {
 294		      rc = OPT_RC_INVALID;
 295		      printf ("Error: Options <y> and <x> are "
 296			      "mutually exclusive.\n");
 297		    }
 298		  break;
 299
 300		  // Interpret items as heap values
 301		case 'y':
 302		  SET_OPTION (itemOptions, ITEM_HEAP, 'y');
 303		  if (itemOptions & ITEM_INDEX)
 304		    {
 305		      rc = OPT_RC_INVALID;
 306		      printf ("Error: Options <x> and <y> are "
 307			      "mutually exclusive.\n");
 308		    }
 309		  break;
 310
 311		default:
 312		  rc = OPT_RC_INVALID;
 313		  printf ("Error: Unknown option <%c>.\n", optionString[y]);
 314		  break;
 315		}
 316
 317	      if (rc)
 318		break;
 319	    }
 320	}
 321    }
 322
 323  if (rc == OPT_RC_DUPLICATE)
 324    printf ("Error: Duplicate option listed <%c>.\n", duplicateSwitch);
 325
 326  // If the user requested a control file dump, a pure binary
 327  // block dump or a non-interpreted formatted dump, mask off
 328  // all other block level options (with a few exceptions)   
 329  if (rc == OPT_RC_VALID)
 330    {
 331      // The user has requested a control file dump, only -f and
 332      // -S are valid... turn off all other formatting
 333      if (controlOptions & CONTROL_DUMP)
 334	{
 335	  if ((blockOptions & ~(BLOCK_FORMAT | BLOCK_FORCED))
 336	      || (itemOptions))
 337	    {
 338	      rc = OPT_RC_INVALID;
 339	      printf ("Error: Invalid options used for Control File dump.\n"
 340		      "       Only options <Sf> may be used with <c>.\n");
 341	    }
 342	  else
 343	    {
 344	      controlOptions |=
 345		(blockOptions & (BLOCK_FORMAT | BLOCK_FORCED));
 346	      blockOptions = itemOptions = 0;
 347	    }
 348	}
 349      // The user has requested a binary block dump... only -R and
 350      // -f are honoured
 351      else if (blockOptions & BLOCK_BINARY)
 352	{
 353	  blockOptions &= (BLOCK_BINARY | BLOCK_RANGE | BLOCK_FORCED);
 354	  itemOptions = 0;
 355	}
 356      // The user has requested a non-interpreted dump... only -a,
 357      // -R and -f are honoured
 358      else if (blockOptions & BLOCK_NO_INTR)
 359	{
 360	  blockOptions &=
 361	    (BLOCK_NO_INTR | BLOCK_ABSOLUTE | BLOCK_RANGE | BLOCK_FORCED);
 362	  itemOptions = 0;
 363	}
 364    }
 365
 366  return (rc);
 367}
 368
 369// Given the index into the parameter list, convert and return the 
 370// current string to a number if possible 
 371static int
 372GetOptionValue (char *optionString)
 373{
 374  unsigned int x;
 375  int value = -1;
 376  int optionStringLength = strlen (optionString);
 377
 378  // Verify the next option looks like a number
 379  for (x = 0; x < optionStringLength; x++)
 380    if (!isdigit ((int) optionString[x]))
 381      break;
 382
 383  // Convert the string to a number if it looks good
 384  if (x == optionStringLength)
 385    value = atoi (optionString);
 386
 387  return (value);
 388}
 389
 390// Read the page header off of block 0 to determine the block size
 391// used in this file.  Can be overridden using the -S option.  The
 392// returned value is the block size of block 0 on disk
 393static unsigned int
 394GetBlockSize ()
 395{
 396  unsigned int pageHeaderSize = sizeof (PageHeaderData);
 397  unsigned int localSize = 0;
 398  int bytesRead = 0;
 399  char localCache[pageHeaderSize];
 400
 401  // Read the first header off of block 0 to determine the block size
 402  bytesRead = fread (&localCache, 1, pageHeaderSize, fp);
 403  rewind (fp);
 404
 405  if (bytesRead == pageHeaderSize)
 406    localSize = (unsigned int) PageGetPageSize (&localCache);
 407  else
 408    printf ("Error: Unable to read full page header from block 0.\n"
 409	    "  ===> Read %u bytes", bytesRead);
 410  return (localSize);
 411}
 412
 413// Determine the contents of the special section on the block and
 414// return this enum value
 415static unsigned int
 416GetSpecialSectionType (Page page)
 417{
 418  unsigned int rc;
 419  unsigned int specialOffset;
 420  unsigned int specialSize;
 421  unsigned int specialValue;
 422  PageHeader pageHeader = (PageHeader) page;
 423
 424  // If this is not a partial header, check the validity of the 
 425  // special section offset and contents
 426  if (bytesToFormat > sizeof (PageHeaderData))
 427    {
 428      specialOffset = (unsigned int) pageHeader->pd_special;
 429
 430      // Check that the special offset can remain on the block or
 431      // the partial block
 432      if ((specialOffset == 0) ||
 433	  (specialOffset > blockSize) || (specialOffset > bytesToFormat))
 434	rc = SPEC_SECT_ERROR_BOUNDARY;
 435      else
 436	{
 437	  specialSize = blockSize - specialOffset;
 438
 439	  // If there is a special section, use its size to guess its 
 440	  // contents
 441	  if (specialSize == 0)
 442	    rc = SPEC_SECT_NONE;
 443	  else if (specialSize == MAXALIGN (sizeof (uint32)))
 444	    {
 445	      // If MAXALIGN is 8, this could be either a sequence or GIN
 446	      if (bytesToFormat == blockSize)
 447		{
 448		  specialValue = *((int *) (buffer + specialOffset));
 449		  if (specialValue == SEQUENCE_MAGIC)
 450		    rc = SPEC_SECT_SEQUENCE;
 451		  else if (specialSize == MAXALIGN (sizeof (GinPageOpaqueData)))
 452		    rc = SPEC_SECT_INDEX_GIN;
 453		  else
 454		    rc = SPEC_SECT_ERROR_UNKNOWN;
 455		}
 456	      else
 457		rc = SPEC_SECT_ERROR_UNKNOWN;
 458	    }
 459	  else if (specialSize == MAXALIGN (sizeof (GinPageOpaqueData)))
 460	      rc = SPEC_SECT_INDEX_GIN;
 461	  else if (specialSize > 2 && bytesToFormat == blockSize)
 462	    {
 463	      // As of 8.3, BTree, Hash, and GIST all have the same size
 464	      // special section, but the last two bytes of the section
 465	      // can be checked to determine what's what.
 466	      uint16 ptype = *(uint16 *) (buffer + blockSize - sizeof(uint16));
 467
 468	      if (ptype <= MAX_BT_CYCLE_ID &&
 469		  specialSize == MAXALIGN (sizeof (BTPageOpaqueData)))
 470		rc = SPEC_SECT_INDEX_BTREE;
 471	      else if (ptype == HASHO_PAGE_ID &&
 472		  specialSize == MAXALIGN (sizeof (HashPageOpaqueData)))
 473		rc = SPEC_SECT_INDEX_HASH;
 474	      else if (ptype == GIST_PAGE_ID &&
 475		       specialSize == MAXALIGN (sizeof (GISTPageOpaqueData)))
 476		rc = SPEC_SECT_INDEX_GIST;
 477	      else
 478		rc = SPEC_SECT_ERROR_UNKNOWN;
 479	    }
 480	  else
 481	    rc = SPEC_SECT_ERROR_UNKNOWN;
 482	}
 483    }
 484  else
 485    rc = SPEC_SECT_ERROR_UNKNOWN;
 486
 487  return (rc);
 488}
 489
 490// Check whether page is a btree meta page
 491static bool
 492IsBtreeMetaPage(Page page)
 493{
 494  PageHeader pageHeader = (PageHeader) page;
 495
 496  if ((PageGetSpecialSize (page) == (MAXALIGN (sizeof (BTPageOpaqueData))))
 497      && (bytesToFormat == blockSize))
 498    {
 499      BTPageOpaque btpo =
 500	(BTPageOpaque) ((char *) page + pageHeader->pd_special);
 501
 502      // Must check the cycleid to be sure it's really btree.
 503      if ((btpo->btpo_cycleid <= MAX_BT_CYCLE_ID) &&
 504	  (btpo->btpo_flags & BTP_META))
 505	return true;
 506    }
 507  return false;
 508}
 509
 510// Display a header for the dump so we know the file name, the options
 511// used and the time the dump was taken
 512static void
 513CreateDumpFileHeader (int numOptions, char **options)
 514{
 515  unsigned int x;
 516  char optionBuffer[52] = "\0";
 517  time_t rightNow = time (NULL);
 518
 519  // Iterate through the options and cache them.
 520  // The maximum we can display is 50 option characters + spaces.  
 521  for (x = 1; x < (numOptions - 1); x++)
 522    {
 523      if ((strlen (optionBuffer) + strlen (options[x])) > 50)
 524	break;
 525      strcat (optionBuffer, options[x]);
 526      strcat (optionBuffer, " ");
 527    }
 528
 529  printf
 530    ("\n*******************************************************************\n"
 531     "* PostgreSQL File/Block Formatted Dump Utility - Version %s\n"
 532     "*\n"
 533     "* File: %s\n"
 534     "* Options used: %s\n*\n"
 535     "* Dump created on: %s"
 536     "*******************************************************************\n",
 537     FD_VERSION, fileName, (strlen (optionBuffer)) ? optionBuffer : "None",
 538     ctime (&rightNow));
 539}
 540
 541// Dump out a formatted block header for the requested block
 542static int
 543FormatHeader (Page page)
 544{
 545  int rc = 0;
 546  unsigned int headerBytes;
 547  PageHeader pageHeader = (PageHeader) page;
 548
 549  printf ("<Header> -----\n");
 550
 551  // Only attempt to format the header if the entire header (minus the item
 552  // array) is available
 553  if (bytesToFormat < offsetof (PageHeaderData, pd_linp[0]))
 554    {
 555      headerBytes = bytesToFormat;
 556      rc = EOF_ENCOUNTERED;
 557    }
 558  else
 559    {
 560      XLogRecPtr pageLSN = PageGetLSN (page);
 561      int maxOffset = PageGetMaxOffsetNumber (page);
 562      char flagString[100];
 563
 564      headerBytes = offsetof (PageHeaderData, pd_linp[0]);
 565      blockVersion = (unsigned int) PageGetPageLayoutVersion (page);
 566
 567      // The full header exists but we have to check that the item array
 568      // is available or how far we can index into it
 569      if (maxOffset > 0)
 570	{
 571	  unsigned int itemsLength = maxOffset * sizeof (ItemIdData);
 572	  if (bytesToFormat < (headerBytes + itemsLength))
 573	    {
 574	      headerBytes = bytesToFormat;
 575	      rc = EOF_ENCOUNTERED;
 576	    }
 577	  else
 578	    headerBytes += itemsLength;
 579	}
 580
 581      flagString[0] = '\0';
 582      if (pageHeader->pd_flags & PD_HAS_FREE_LINES)
 583	  strcat (flagString, "HAS_FREE_LINES|");
 584      if (pageHeader->pd_flags & PD_PAGE_FULL)
 585	  strcat (flagString, "PAGE_FULL|");
 586      if (pageHeader->pd_flags & PD_ALL_VISIBLE)
 587	  strcat (flagString, "ALL_VISIBLE|");
 588      if (strlen (flagString))
 589	  flagString[strlen (flagString) - 1] = '\0';
 590
 591      // Interpret the content of the header
 592      printf
 593	(" Block Offset: 0x%08x         Offsets: Lower    %4u (0x%04hx)\n"
 594	 " Block: Size %4d  Version %4u            Upper    %4u (0x%04hx)\n"
 595	 " LSN:  logid %6d recoff 0x%08x      Special  %4u (0x%04hx)\n"
 596	 " Items: %4d                      Free Space: %4u\n"
 597	 " TLI: 0x%04x  Prune XID: 0x%08x  Flags: 0x%04x (%s)\n"
 598	 " Length (including item array): %u\n\n",
 599	 pageOffset, pageHeader->pd_lower, pageHeader->pd_lower,
 600	 (int) PageGetPageSize (page), blockVersion,
 601	 pageHeader->pd_upper, pageHeader->pd_upper,
 602	 pageLSN.xlogid, pageLSN.xrecoff,
 603	 pageHeader->pd_special, pageHeader->pd_special,
 604	 maxOffset, pageHeader->pd_upper - pageHeader->pd_lower,
 605	 pageHeader->pd_tli, pageHeader->pd_prune_xid,
 606	 pageHeader->pd_flags, flagString,
 607	 headerBytes);
 608
 609      // If it's a btree meta page, print the contents of the meta block.
 610      if (IsBtreeMetaPage(page))
 611	{
 612	  BTMetaPageData *btpMeta = BTPageGetMeta (buffer);
 613	  printf (" BTree Meta Data:  Magic (0x%08x)   Version (%u)\n"
 614		  "                   Root:     Block (%u)  Level (%u)\n"
 615		  "                   FastRoot: Block (%u)  Level (%u)\n\n",
 616		  btpMeta->btm_magic, btpMeta->btm_version,
 617		  btpMeta->btm_root, btpMeta->btm_level,
 618		  btpMeta->btm_fastroot, btpMeta->btm_fastlevel);
 619	  headerBytes += sizeof (BTMetaPageData);
 620	}
 621
 622      // Eye the contents of the header and alert the user to possible
 623      // problems.
 624      if ((maxOffset < 0) ||
 625	  (maxOffset > blockSize) ||
 626	  (blockVersion != PG_PAGE_LAYOUT_VERSION) || /* only one we support */
 627	  (pageHeader->pd_upper > blockSize) ||
 628	  (pageHeader->pd_upper > pageHeader->pd_special) ||
 629	  (pageHeader->pd_lower <
 630	   (sizeof (PageHeaderData) - sizeof (ItemIdData)))
 631	  || (pageHeader->pd_lower > blockSize)
 632	  || (pageHeader->pd_upper < pageHeader->pd_lower)
 633	  || (pageHeader->pd_special > blockSize))
 634	printf (" Error: Invalid header information.\n\n");
 635    }
 636
 637  // If we have reached the end of file while interpreting the header, let
 638  // the user know about it
 639  if (rc == EOF_ENCOUNTERED)
 640    printf
 641      (" Error: End of block encountered within the header."
 642       " Bytes read: %4u.\n\n", bytesToFormat);
 643
 644  // A request to dump the formatted binary of the block (header, 
 645  // items and special section).  It's best to dump even on an error
 646  // so the user can see the raw image.
 647  if (blockOptions & BLOCK_FORMAT)
 648    FormatBinary (headerBytes, 0);
 649
 650  return (rc);
 651}
 652
 653// Dump out formatted items that reside on this block 
 654static void
 655FormatItemBlock (Page page)
 656{
 657  unsigned int x;
 658  unsigned int itemSize;
 659  unsigned int itemOffset;
 660  unsigned int itemFlags;
 661  ItemId itemId;
 662  int maxOffset = PageGetMaxOffsetNumber (page);
 663
 664  // If it's a btree meta page, the meta block is where items would normally
 665  // be; don't print garbage.
 666  if (IsBtreeMetaPage(page))
 667    return;
 668
 669  printf ("<Data> ------ \n");
 670
 671  // Loop through the items on the block.  Check if the block is
 672  // empty and has a sensible item array listed before running 
 673  // through each item  
 674  if (maxOffset == 0)
 675    printf (" Empty block - no items listed \n\n");
 676  else if ((maxOffset < 0) || (maxOffset > blockSize))
 677    printf (" Error: Item index corrupt on block. Offset: <%d>.\n\n",
 678	    maxOffset);
 679  else
 680    {
 681      int formatAs;
 682      char textFlags[16];
 683
 684      // First, honour requests to format items a special way, then 
 685      // use the special section to determine the format style
 686      if (itemOptions & ITEM_INDEX)
 687	formatAs = ITEM_INDEX;
 688      else if (itemOptions & ITEM_HEAP)
 689	formatAs = ITEM_HEAP;
 690      else if (specialType != SPEC_SECT_NONE)
 691	formatAs = ITEM_INDEX;
 692      else
 693	formatAs = ITEM_HEAP;
 694
 695      for (x = 1; x < (maxOffset + 1); x++)
 696	{
 697	  itemId = PageGetItemId (page, x);
 698	  itemFlags = (unsigned int) ItemIdGetFlags (itemId);
 699	  itemSize = (unsigned int) ItemIdGetLength (itemId);
 700	  itemOffset = (unsigned int) ItemIdGetOffset (itemId);
 701
 702	  switch (itemFlags)
 703	  {
 704	    case LP_UNUSED:
 705	      strcpy (textFlags, "UNUSED");
 706	      break;
 707	    case LP_NORMAL:
 708	      strcpy (textFlags, "NORMAL");
 709	      break;
 710	    case LP_REDIRECT:
 711	      strcpy (textFlags, "REDIRECT");
 712	      break;
 713	    case LP_DEAD:
 714	      strcpy (textFlags, "DEAD");
 715	      break;
 716	    default:
 717	      // shouldn't be possible
 718	      sprintf (textFlags, "0x%02x", itemFlags);
 719	      break;
 720	  }
 721
 722	  printf (" Item %3u -- Length: %4u  Offset: %4u (0x%04x)"
 723		  "  Flags: %s\n", x, itemSize, itemOffset, itemOffset,
 724		  textFlags);
 725
 726	  // Make sure the item can physically fit on this block before
 727	  // formatting
 728	  if ((itemOffset + itemSize > blockSize) ||
 729	      (itemOffset + itemSize > bytesToFormat))
 730	    printf ("  Error: Item contents extend beyond block.\n"
 731		    "         BlockSize<%d> Bytes Read<%d> Item Start<%d>.\n",
 732		    blockSize, bytesToFormat, itemOffset + itemSize);
 733	  else
 734	    {
 735	      // If the user requests that the items be interpreted as
 736	      // heap or index items...     
 737	      if (itemOptions & ITEM_DETAIL)
 738		FormatItem (itemSize, itemOffset, formatAs);
 739
 740	      // Dump the items contents in hex and ascii 
 741	      if (blockOptions & BLOCK_FORMAT)
 742		FormatBinary (itemSize, itemOffset);
 743
 744	      if (x == maxOffset)
 745		printf ("\n");
 746	    }
 747	}
 748    }
 749}
 750
 751// Interpret the contents of the item based on whether it has a special
 752// section and/or the user has hinted
 753static void
 754FormatItem (unsigned int numBytes, unsigned int startIndex,
 755	    unsigned int formatAs)
 756{
 757  // It is an index item, so dump the index header
 758  if (formatAs == ITEM_INDEX)
 759    {
 760      if (numBytes < SizeOfIptrData)
 761	{
 762	  if (numBytes)
 763	    printf ("  Error: This item does not look like an index item.\n");
 764	}
 765      else
 766	{
 767	  IndexTuple itup = (IndexTuple) (&(buffer[startIndex]));
 768	  printf ("  Block Id: %u  linp Index: %u  Size: %d\n"
 769		  "  Has Nulls: %u  Has Varwidths: %u\n\n",
 770		  ((uint32) ((itup->t_tid.ip_blkid.bi_hi << 16) |
 771			     (uint16) itup->t_tid.ip_blkid.bi_lo)),
 772		  itup->t_tid.ip_posid,
 773		  (int) IndexTupleSize(itup),
 774		  IndexTupleHasNulls(itup) ? 1 : 0,
 775		  IndexTupleHasVarwidths(itup) ? 1 : 0);
 776
 777	  if (numBytes != IndexTupleSize (itup))
 778	    printf ("  Error: Item size difference. Given <%u>, "
 779		    "Internal <%d>.\n", numBytes, (int) IndexTupleSize (itup));
 780	}
 781    }
 782  else
 783    {
 784      // It is a heap item, so dump the heap header
 785      int alignedSize = MAXALIGN (sizeof (HeapTupleHeaderData));
 786
 787      if (numBytes < alignedSize)
 788	{
 789	  if (numBytes)
 790	    printf ("  Error: This item does not look like a heap item.\n");
 791	}
 792      else
 793	{
 794	  char flagString[256];
 795	  unsigned int x;
 796	  unsigned int bitmapLength = 0;
 797	  unsigned int oidLength = 0;
 798	  unsigned int computedLength;
 799	  unsigned int infoMask;
 800	  unsigned int infoMask2;
 801	  int localNatts;
 802	  unsigned int localHoff;
 803	  bits8 *localBits;
 804	  unsigned int localBitOffset;
 805
 806	  HeapTupleHeader htup = (HeapTupleHeader) (&buffer[startIndex]);
 807
 808	  infoMask = htup->t_infomask;
 809	  infoMask2 = htup->t_infomask2;
 810	  localBits = &(htup->t_bits[0]);
 811	  localNatts = HeapTupleHeaderGetNatts(htup);
 812	  localHoff = htup->t_hoff;
 813	  localBitOffset = offsetof (HeapTupleHeaderData, t_bits);
 814
 815	  printf ("  XMIN: %u  XMAX: %u  CID|XVAC: %u",
 816		  HeapTupleHeaderGetXmin(htup),
 817		  HeapTupleHeaderGetXmax(htup),
 818		  HeapTupleHeaderGetRawCommandId(htup));
 819
 820	  if (infoMask & HEAP_HASOID)
 821	    printf ("  OID: %u",
 822		    HeapTupleHeaderGetOid(htup));
 823
 824	  printf ("\n"
 825		  "  Block Id: %u  linp Index: %u   Attributes: %d   Size: %d\n",
 826		  ((uint32)
 827		   ((htup->t_ctid.ip_blkid.bi_hi << 16) | (uint16) htup->
 828		    t_ctid.ip_blkid.bi_lo)), htup->t_ctid.ip_posid,
 829		  localNatts, htup->t_hoff);
 830
 831	  // Place readable versions of the tuple info mask into a buffer.  
 832	  // Assume that the string can not expand beyond 256.
 833	  flagString[0] = '\0';
 834	  if (infoMask & HEAP_HASNULL)
 835	    strcat (flagString, "HASNULL|");
 836	  if (infoMask & HEAP_HASVARWIDTH)
 837	    strcat (flagString, "HASVARWIDTH|");
 838	  if (infoMask & HEAP_HASEXTERNAL)
 839	    strcat (flagString, "HASEXTERNAL|");
 840	  if (infoMask & HEAP_HASOID)
 841	    strcat (flagString, "HASOID|");
 842	  if (infoMask & HEAP_COMBOCID)
 843	    strcat (flagString, "COMBOCID|");
 844	  if (infoMask & HEAP_XMAX_EXCL_LOCK)
 845	    strcat (flagString, "XMAX_EXCL_LOCK|");
 846	  if (infoMask & HEAP_XMAX_SHARED_LOCK)
 847	    strcat (flagString, "XMAX_SHARED_LOCK|");
 848	  if (infoMask & HEAP_XMIN_COMMITTED)
 849	    strcat (flagString, "XMIN_COMMITTED|");
 850	  if (infoMask & HEAP_XMIN_INVALID)
 851	    strcat (flagString, "XMIN_INVALID|");
 852	  if (infoMask & HEAP_XMAX_COMMITTED)
 853	    strcat (flagString, "XMAX_COMMITTED|");
 854	  if (infoMask & HEAP_XMAX_INVALID)
 855	    strcat (flagString, "XMAX_INVALID|");
 856	  if (infoMask & HEAP_XMAX_IS_MULTI)
 857	    strcat (flagString, "XMAX_IS_MULTI|");
 858	  if (infoMask & HEAP_UPDATED)
 859	    strcat (flagString, "UPDATED|");
 860	  if (infoMask & HEAP_MOVED_OFF)
 861	    strcat (flagString, "MOVED_OFF|");
 862	  if (infoMask & HEAP_MOVED_IN)
 863	    strcat (flagString, "MOVED_IN|");
 864
 865	  if (infoMask2 & HEAP_HOT_UPDATED)
 866	    strcat (flagString, "HOT_UPDATED|");
 867	  if (infoMask2 & HEAP_ONLY_TUPLE)
 868	    strcat (flagString, "HEAP_ONLY|");
 869
 870	  if (strlen (flagString))
 871	    flagString[strlen (flagString) - 1] = '\0';
 872
 873	  printf ("  infomask: 0x%04x (%s) \n", infoMask, flagString);
 874
 875	  // As t_bits is a variable length array, determine the length of
 876	  // the header proper  
 877	  if (infoMask & HEAP_HASNULL)
 878	    bitmapLength = BITMAPLEN (localNatts);
 879	  else
 880	    bitmapLength = 0;
 881
 882	  if (infoMask & HEAP_HASOID)
 883	    oidLength += sizeof (Oid);
 884
 885	  computedLength =
 886	    MAXALIGN (localBitOffset + bitmapLength + oidLength);
 887
 888	  // Inform the user of a header size mismatch or dump the t_bits array
 889	  if (computedLength != localHoff)
 890	    printf
 891	      ("  Error: Computed header length not equal to header size.\n"
 892	       "         Computed <%u>  Header: <%d>\n", computedLength,
 893	       localHoff);
 894	  else if ((infoMask & HEAP_HASNULL) && bitmapLength)
 895	    {
 896	      printf ("  t_bits: ");
 897	      for (x = 0; x < bitmapLength; x++)
 898		{
 899		  printf ("[%u]: 0x%02x ", x, localBits[x]);
 900		  if (((x & 0x03) == 0x03) && (x < bitmapLength - 1))
 901		    printf ("\n          ");
 902		}
 903	      printf ("\n");
 904	    }
 905	  printf ("\n");
 906	}
 907    }
 908}
 909
 910
 911// On blocks that have special sections, we have to interpret the
 912// contents based on size of the special section (since there is
 913// no other way)
 914static void
 915FormatSpecial ()
 916{
 917  PageHeader pageHeader = (PageHeader) buffer;
 918  char flagString[100] = "\0";
 919  unsigned int specialOffset = pageHeader->pd_special;
 920  unsigned int specialSize =
 921    (blockSize >= specialOffset) ? (blockSize - specialOffset) : 0;
 922
 923  printf ("<Special Section> -----\n");
 924
 925  switch (specialType)
 926    {
 927    case SPEC_SECT_ERROR_UNKNOWN:
 928    case SPEC_SECT_ERROR_BOUNDARY:
 929      printf (" Error: Invalid special section encountered.\n");
 930      break;
 931
 932    case SPEC_SECT_SEQUENCE:
 933      printf (" Sequence: 0x%08x\n", SEQUENCE_MAGIC);
 934      break;
 935
 936      // Btree index section  
 937    case SPEC_SECT_INDEX_BTREE:
 938      {
 939	BTPageOpaque btreeSection = (BTPageOpaque) (buffer + specialOffset);
 940	if (btreeSection->btpo_flags & BTP_LEAF)
 941	  strcat (flagString, "LEAF|");
 942	if (btreeSection->btpo_flags & BTP_ROOT)
 943	  strcat (flagString, "ROOT|");
 944	if (btreeSection->btpo_flags & BTP_DELETED)
 945	  strcat (flagString, "DELETED|");
 946	if (btreeSection->btpo_flags & BTP_META)
 947	  strcat (flagString, "META|");
 948	if (btreeSection->btpo_flags & BTP_HALF_DEAD)
 949	  strcat (flagString, "HALFDEAD|");
 950	if (btreeSection->btpo_flags & BTP_SPLIT_END)
 951	  strcat (flagString, "SPLITEND|");
 952	if (btreeSection->btpo_flags & BTP_HAS_GARBAGE)
 953	  strcat (flagString, "HASGARBAGE|");
 954	if (strlen (flagString))
 955	  flagString[strlen (flagString) - 1] = '\0';
 956
 957	printf (" BTree Index Section:\n"
 958		"  Flags: 0x%04x (%s)\n"
 959		"  Blocks: Previous (%d)  Next (%d)  %s (%d)  CycleId (%d)\n\n",
 960		btreeSection->btpo_flags, flagString,
 961		btreeSection->btpo_prev, btreeSection->btpo_next,
 962		(btreeSection->
 963		 btpo_flags & BTP_DELETED) ? "Next XID" : "Level",
 964		btreeSection->btpo.level,
 965		btreeSection->btpo_cycleid);
 966      }
 967      break;
 968
 969      // Hash index section  
 970    case SPEC_SECT_INDEX_HASH:
 971      {
 972	HashPageOpaque hashSection = (HashPageOpaque) (buffer + specialOffset);
 973	if (hashSection->hasho_flag & LH_UNUSED_PAGE)
 974	  strcat (flagString, "UNUSED|");
 975	if (hashSection->hasho_flag & LH_OVERFLOW_PAGE)
 976	  strcat (flagString, "OVERFLOW|");
 977	if (hashSection->hasho_flag & LH_BUCKET_PAGE)
 978	  strcat (flagString, "BUCKET|");
 979	if (hashSection->hasho_flag & LH_BITMAP_PAGE)
 980	  strcat (flagString, "BITMAP|");
 981	if (hashSection->hasho_flag & LH_META_PAGE)
 982	  strcat (flagString, "META|");
 983	if (strlen (flagString))
 984	  flagString[strlen (flagString) - 1] = '\0';
 985	printf (" Hash Index Section:\n"
 986		"  Flags: 0x%04x (%s)\n"
 987		"  Bucket Number: 0x%04x\n"
 988		"  Blocks: Previous (%d)  Next (%d)\n\n",
 989		hashSection->hasho_flag, flagString,
 990		hashSection->hasho_bucket,
 991		hashSection->hasho_prevblkno, hashSection->hasho_nextblkno);
 992      }
 993      break;
 994
 995      // GIST index section
 996    case SPEC_SECT_INDEX_GIST:
 997      {
 998	GISTPageOpaque gistSection = (GISTPageOpaque) (buffer + specialOffset);
 999	if (gistSection->flags & F_LEAF)
1000	  strcat (flagString, "LEAF|");
1001	if (gistSection->flags & F_DELETED)
1002	  strcat (flagString, "DELETED|");
1003	if (gistSection->flags & F_TUPLES_DELETED)
1004	  strcat (flagString, "TUPLES_DELETED|");
1005	if (gistSection->flags & F_FOLLOW_RIGHT)
1006	  strcat (flagString, "FOLLOW_RIGHT|");
1007	if (strlen (flagString))
1008	  flagString[strlen (flagString) - 1] = '\0';
1009	printf (" GIST Index Section:\n"
1010		"  NSN: 0x%08x/0x%08x\n"
1011		"  RightLink: %d\n"
1012		"  Flags: 0x%08x (%s)\n\n",
1013		gistSection->nsn.xlogid, gistSection->nsn.xrecoff,
1014		gistSection->rightlink,
1015		gistSection->flags, flagString);
1016      }
1017      break;
1018
1019      // GIN index section
1020    case SPEC_SECT_INDEX_GIN:
1021      {
1022	GinPageOpaque ginSection = (GinPageOpaque) (buffer + specialOffset);
1023	if (ginSection->flags & GIN_DATA)
1024	  strcat (flagString, "DATA|");
1025	if (ginSection->flags & GIN_LEAF)
1026	  strcat (flagString, "LEAF|");
1027	if (ginSection->flags & GIN_DELETED)
1028	  strcat (flagString, "DELETED|");
1029	if (ginSection->flags & GIN_META)
1030	  strcat (flagString, "META|");
1031	if (ginSection->flags & GIN_LIST)
1032	  strcat (flagString, "LIST|");
1033	if (ginSection->flags & GIN_LIST_FULLROW)
1034	  strcat (flagString, "FULLROW|");
1035	if (strlen (flagString))
1036	  flagString[strlen (flagString) - 1] = '\0';
1037	printf (" GIN Index Section:\n"
1038		"  Flags: 0x%08x (%s)  Maxoff: %d\n"
1039		"  Blocks: RightLink (%d)\n\n",
1040		ginSection->flags, flagString,
1041		ginSection->maxoff,
1042		ginSection->rightlink);
1043      }
1044      break;
1045
1046      // No idea what type of special section this is
1047    default:
1048      printf (" Unknown special section type. Type: <%u>.\n", specialType);
1049      break;
1050    }
1051
1052  // Dump the formatted contents of the special section       
1053  if (blockOptions & BLOCK_FORMAT)
1054    {
1055      if (specialType == SPEC_SECT_ERROR_BOUNDARY)
1056	printf (" Error: Special section points off page."
1057		" Unable to dump contents.\n");
1058      else
1059	FormatBinary (specialSize, specialOffset);
1060    }
1061}
1062
1063// For each block, dump out formatted header and content information
1064static void
1065FormatBlock ()
1066{
1067  Page page = (Page) buffer;
1068  pageOffset = blockSize * currentBlock;
1069  specialType = GetSpecialSectionType (page);
1070
1071  printf ("\nBlock %4u **%s***************************************\n",
1072	  currentBlock,
1073	  (bytesToFormat ==
1074	   blockSize) ? "***************" : " PARTIAL BLOCK ");
1075
1076  // Either dump out the entire block in hex+acsii fashion or
1077  // interpret the data based on block structure 
1078  if (blockOptions & BLOCK_NO_INTR)
1079    FormatBinary (bytesToFormat, 0);
1080  else
1081    {
1082      int rc;
1083      // Every block contains a header, items and possibly a special
1084      // section.  Beware of partial block reads though            
1085      rc = FormatHeader (page);
1086
1087      // If we didn't encounter a partial read in the header, carry on... 
1088      if (rc != EOF_ENCOUNTERED)
1089	{
1090	  FormatItemBlock (page);
1091
1092	  if (specialType != SPEC_SECT_NONE)
1093	    FormatSpecial ();
1094	}
1095    }
1096}
1097
1098// Dump out the content of the PG control file
1099static void
1100FormatControl ()
1101{
1102  unsigned int localPgVersion = 0;
1103  unsigned int controlFileSize = 0;
1104
1105  printf
1106    ("\n<pg_control Contents> *********************************************\n\n");
1107
1108  // Check the version 
1109  if (bytesToFormat >= offsetof (ControlFileData, catalog_version_no))
1110    localPgVersion = ((ControlFileData *) buffer)->pg_control_version;
1111
1112  if (localPgVersion >= 72)
1113    controlFileSize = sizeof (ControlFileData);
1114  else
1115    {
1116      printf ("pg_filedump: pg_control version %u not supported.\n",
1117	      localPgVersion);
1118      return;
1119    }
1120
1121  // Interpret the control file if it's all there
1122  if (bytesToFormat >= controlFileSize)
1123    {
1124      ControlFileData *controlData = (ControlFileData *) buffer;
1125      CheckPoint *checkPoint = &(controlData->checkPointCopy);
1126      pg_crc32 crcLocal;
1127      char *dbState;
1128
1129      // Compute a local copy of the CRC to verify the one on disk
1130      INIT_CRC32 (crcLocal);
1131      COMP_CRC32 (crcLocal, buffer, offsetof(ControlFileData, crc));
1132      FIN_CRC32 (crcLocal);
1133
1134      // Grab a readable version of the database state
1135      switch (controlData->state)
1136	{
1137	case DB_STARTUP:
1138	  dbState = "STARTUP";
1139	  break;
1140	case DB_SHUTDOWNED:
1141	  dbState = "SHUTDOWNED";
1142	  break;
1143	case DB_SHUTDOWNING:
1144	  dbState = "SHUTDOWNING";
1145	  break;
1146	case DB_IN_CRASH_RECOVERY:
1147	  dbState = "IN CRASH RECOVERY";
1148	  break;
1149	case DB_IN_ARCHIVE_RECOVERY:
1150	  dbState = "IN ARCHIVE RECOVERY";
1151	  break;
1152	case DB_IN_PRODUCTION:
1153	  dbState = "IN PRODUCTION";
1154	  break;
1155	default:
1156	  dbState = "UNKNOWN";
1157	  break;
1158	}
1159
1160      printf ("                          CRC: %s\n"
1161	      "           pg_control Version: %u%s\n"
1162	      "              Catalog Version: %u\n"
1163	      "            System Identifier: " UINT64_FORMAT "\n"
1164	      "                        State: %s\n"
1165	      "                Last Mod Time: %s"
1166	      "       Last Checkpoint Record: Log File (%u) Offset (0x%08x)\n"
1167	      "   Previous Checkpoint Record: Log File (%u) Offset (0x%08x)\n"
1168	      "  Last Checkpoint Record Redo: Log File (%u) Offset (0x%08x)\n"
1169	      "             |-    TimeLineID: %u\n"
1170	      "             |-      Next XID: %u/%u\n"
1171	      "             |-      Next OID: %u\n"
1172	      "             |-    Next Multi: %u\n"
1173	      "             |- Next MultiOff: %u\n"
1174	      "             |-          Time: %s"
1175	      "       Minimum Recovery Point: Log File (%u) Offset (0x%08x)\n"
1176	      "       Maximum Data Alignment: %u\n"
1177	      "        Floating-Point Sample: %.7g%s\n"
1178	      "          Database Block Size: %u\n"
1179	      "           Blocks Per Segment: %u\n"
1180	      "              XLOG Block Size: %u\n"
1181	      "            XLOG Segment Size: %u\n"
1182	      "    Maximum Identifier Length: %u\n"
1183	      "           Maximum Index Keys: %u\n"
1184	      "             TOAST Chunk Size: %u\n"
1185	      "   Date and Time Type Storage: %s\n\n",
1186	      EQ_CRC32 (crcLocal,
1187			controlData->crc) ? "Correct" : "Not Correct",
1188	      controlData->pg_control_version,
1189	      (controlData->pg_control_version == PG_CONTROL_VERSION ?
1190	       "" : " (Not Correct!)"),
1191	      controlData->catalog_version_no,
1192	      controlData->system_identifier,
1193	      dbState,
1194	      ctime (&(controlData->time)),
1195	      controlData->checkPoint.xlogid, controlData->checkPoint.xrecoff,
1196	      controlData->prevCheckPoint.xlogid, controlData->prevCheckPoint.xrecoff,
1197	      checkPoint->redo.xlogid, checkPoint->redo.xrecoff,
1198	      checkPoint->ThisTimeLineID,
1199	      checkPoint->nextXidEpoch, checkPoint->nextXid,
1200	      checkPoint->nextOid,
1201	      checkPoint->nextMulti, checkPoint->nextMultiOffset,
1202	      ctime (&checkPoint->time),
1203	      controlData->minRecoveryPoint.xlogid, controlData->minRecoveryPoint.xrecoff,
1204	      controlData->maxAlign,
1205	      controlData->floatFormat,
1206	      (controlData->floatFormat == FLOATFORMAT_VALUE ?
1207	       "" : " (Not Correct!)"),
1208	      controlData->blcksz,
1209	      controlData->relseg_size,
1210	      controlData->xlog_blcksz,
1211	      controlData->xlog_seg_size,
1212	      controlData->nameDataLen,
1213	      controlData->indexMaxKeys,
1214	      controlData->toast_max_chunk_size,
1215	      (controlData->enableIntTimes ?
1216	       "64 bit Integers" : "Floating Point"));
1217    }
1218  else
1219    {
1220      printf (" Error: pg_control file size incorrect.\n"
1221	      "        Size: Correct <%u>  Received <%u>.\n\n",
1222	      controlFileSize, bytesToFormat);
1223
1224      // If we have an error, force a formatted dump so we can see 
1225      // where things are going wrong
1226      controlOptions |= CONTROL_FORMAT;
1227    }
1228
1229  // Dump hex and ascii representation of data 
1230  if (controlOptions & CONTROL_FORMAT)
1231    {
1232      printf ("<pg_control Formatted Dump> *****************"
1233	      "**********************\n\n");
1234      FormatBinary (bytesToFormat, 0);
1235    }
1236}
1237
1238// Dump out the contents of the block in hex and ascii. 
1239// BYTES_PER_LINE bytes are formatted in each line.
1240static void
1241FormatBinary (unsigned int numBytes, unsigned int startIndex)
1242{
1243  unsigned int index = 0;
1244  unsigned int stopIndex = 0;
1245  unsigned int x = 0;
1246  unsigned int lastByte = startIndex + numBytes;
1247
1248  if (numBytes)
1249    {
1250      // Iterate through a printable row detailing the current
1251      // address, the hex and ascii values         
1252      for (index = startIndex; index < lastByte; index += BYTES_PER_LINE)
1253	{
1254	  stopIndex = index + BYTES_PER_LINE;
1255
1256	  // Print out the address
1257	  if (blockOptions & BLOCK_ABSOLUTE)
1258	    printf ("  %08x: ", (unsigned int) (pageOffset + index));
1259	  else
1260	    printf ("  %04x: ", (unsigned int) index);
1261
1262	  // Print out the hex version of the data
1263	  for (x = index; x < stopIndex; x++)
1264	    {
1265	      if (x < lastByte)
1266		printf ("%02x", 0xff & ((unsigned) buffer[x]));
1267	      else
1268		printf ("  ");
1269	      if ((x & 0x03) == 0x03)
1270		printf (" ");
1271	    }
1272	  printf (" ");
1273
1274	  // Print out the ascii version of the data
1275	  for (x = index; x < stopIndex; x++)
1276	    {
1277	      if (x < lastByte)
1278		printf ("%c", isprint (buffer[x]) ? buffer[x] : '.');
1279	      else
1280		printf (" ");
1281	    }
1282	  printf ("\n");
1283	}
1284      printf ("\n");
1285    }
1286}
1287
1288// Dump the binary image of the block
1289static void
1290DumpBinaryBlock ()
1291{
1292  unsigned int x;
1293  for (x = 0; x < bytesToFormat; x++)
1294    putchar (buffer[x]);
1295}
1296
1297// Control the dumping of the blocks within the file
1298static void
1299DumpFileContents ()
1300{
1301  unsigned int initialRead = 1;
1302  unsigned int contentsToDump = 1;
1303
1304  // If the user requested a block range, seek to the correct position
1305  // within the file for the start block.
1306  if (blockOptions & BLOCK_RANGE)
1307    {
1308      unsigned int position = blockSize * blockStart;
1309      if (fseek (fp, position, SEEK_SET) != 0)
1310	{
1311	  printf ("Error: Seek error encountered before requested "
1312		  "start block <%d>.\n", blockStart);
1313	  contentsToDump = 0;
1314	}
1315      else
1316	currentBlock = blockStart;
1317    }
1318
1319  // Iterate through the blocks in the file until you reach the end or
1320  // the requested range end
1321  while (contentsToDump)
1322    {
1323      bytesToFormat = fread (buffer, 1, blockSize, fp);
1324
1325      if (bytesToFormat == 0)
1326	{
1327	  // fseek() won't pop an error if you seek passed eof.  The next
1328	  // subsequent read gets the error.    
1329	  if (initialRead)
1330	    printf ("Error: Premature end of file encountered.\n");
1331	  else if (!(blockOptions & BLOCK_BINARY))
1332	    printf ("\n*** End of File Encountered. Last Block "
1333		    "Read: %d ***\n", currentBlock - 1);
1334
1335	  contentsToDump = 0;
1336	}
1337      else
1338	{
1339	  if (blockOptions & BLOCK_BINARY)
1340	    DumpBinaryBlock ();
1341	  else
1342	    {
1343	      if (controlOptions & CONTROL_DUMP)
1344		{
1345		  FormatControl ();
1346		  contentsToDump = false;
1347		}
1348	      else
1349		FormatBlock ();
1350	    }
1351	}
1352
1353      // Check to see if we are at the end of the requested range.
1354      if ((blockOptions & BLOCK_RANGE) &&
1355	  (currentBlock >= blockEnd) && (contentsToDump))
1356	{
1357	  //Don't print out message if we're doing a binary dump
1358	  if (!(blockOptions & BLOCK_BINARY))
1359	    printf ("\n*** End of Requested Range Encountered. "
1360		    "Last Block Read: %d ***\n", currentBlock);
1361	  contentsToDump = 0;
1362	}
1363      else
1364	currentBlock++;
1365
1366      initialRead = 0;
1367    }
1368}
1369
1370// Consume the options and iterate through the given file, formatting as
1371// requested.
1372int
1373main (int argv, char **argc)
1374{
1375  // If there is a parameter list, validate the options 
1376  unsigned int validOptions;
1377  validOptions = (argv < 2) ? OPT_RC_COPYRIGHT : ConsumeOptions (argv, argc);
1378
1379  // Display valid options if no parameters are received or invalid options
1380  // where encountered
1381  if (validOptions != OPT_RC_VALID)
1382    DisplayOptions (validOptions);
1383  else
1384    {
1385      // Don't dump the header if we're dumping binary pages        
1386      if (!(blockOptions & BLOCK_BINARY))
1387	CreateDumpFileHeader (argv, argc);
1388
1389      // If the user has not forced a block size, use the size of the
1390      // control file data or the information from the block 0 header 
1391      if (controlOptions)
1392	{
1393	  if (!(controlOptions & CONTROL_FORCED))
1394	    blockSize = sizeof (ControlFileData);
1395	}
1396      else if (!(blockOptions & BLOCK_FORCED))
1397	blockSize = GetBlockSize ();
1398
1399      // On a positive block size, allocate a local buffer to store
1400      // the subsequent blocks
1401      if (blockSize > 0)
1402	{
1403	  buffer = (char *) malloc (blockSize);
1404	  if (buffer)
1405	    DumpFileContents ();
1406	  else
1407	    printf ("\nError: Unable to create buffer of size <%d>.\n",
1408		    blockSize);
1409	}
1410    }
1411
1412  // Close out the file and get rid of the allocated block buffer
1413  if (fp)
1414    fclose (fp);
1415
1416  if (buffer)
1417    free (buffer);
1418
1419  exit (0);
1420}