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