/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}