Add support for decoding pg_filenode.map
authorRichard Yen <richard.yen@enterprisedb.com>
Wed, 28 Apr 2021 22:37:29 +0000 (15:37 -0700)
committerChristoph Berg <myon@debian.org>
Wed, 29 Sep 2021 15:45:25 +0000 (17:45 +0200)
README.pg_filedump
pg_filedump.c
pg_filedump.h

index 59b1e63c3eeaeb07bd49a33a2be76abec21e9942..c72f978d978ee0d8d04d9820f54d4ef698dfe6ae 100644 (file)
@@ -120,5 +120,9 @@ The following options are valid for control files:
   -f  Display formatted content dump along with interpretation
   -S  Force block size to [blocksize]
 
+Additional functions:
+       -m  Interpret file as pg_filenode.map file and print contents
+      (all other options will be ignored)
+
 In most cases it's recommended to use the -i and -f options to get
 the most useful dump output.
index f3650da63143a8b1cd5dcf811be36fdd303d33f1..d1609c0a8f5d267e2aed3a6d83260f1929191219 100644 (file)
@@ -83,9 +83,30 @@ static unsigned int bytesToFormat = 0;
 /* Block version number */
 static unsigned int blockVersion = 0;
 
+/* Flag to indicate pg_filenode.map file */
+static bool isRelMapFile = false;
+
 /* Program exit code */
 static int     exitCode = 0;
 
+/* Relmapper structs */
+typedef struct RelMapping
+{
+  Oid     mapoid;     /* OID of a catalog */
+  Oid     mapfilenode;  /* its filenode number */
+} RelMapping;
+
+/* crc and pad are ignored here, even though they are
+ * present in the backend code.  We assume that anyone
+ * seeking to inspect the contents of pg_filenode.map
+ * probably have a corrupted or non-functional cluster */
+typedef struct RelMapFile
+{
+  int32   magic;      /* always RELMAPPER_FILEMAGIC */
+  int32   num_mappings; /* number of valid RelMapping entries */
+  RelMapping  mappings[FLEXIBLE_ARRAY_MEMBER];
+} RelMapFile;
+
 /*
  * Function Prototypes
  */
@@ -127,6 +148,7 @@ static void FormatControl(char *buffer);
 static void FormatBinary(char *buffer,
                unsigned int numBytes, unsigned int startIndex);
 static void DumpBinaryBlock(char *buffer);
+static int PrintRelMappings(void);
 
 
 /* Send properly formed usage information to the user. */
@@ -179,6 +201,9 @@ DisplayOptions(unsigned int validOptions)
                 "  -c  Interpret the file listed as a control file\n"
                 "  -f  Display formatted content dump along with interpretation\n"
                 "  -S  Force block size to [blocksize]\n"
+                "Additional functions:\n"
+                "  -m  Interpret file as pg_filenode.map file and print contents (all\n"
+                "      other options will be ignored)\n" 
                 "\nReport bugs to <pgsql-bugs@postgresql.org>\n");
 }
 
@@ -515,6 +540,11 @@ ConsumeOptions(int numOptions, char **options)
                                                SET_OPTION(blockOptions, BLOCK_CHECKSUMS, 'k');
                                                break;
 
+                                               /* Treat file as pg_filenode.map file */
+                                       case 'm':
+                                               isRelMapFile = true;
+                                               break;
+
                                                /* Display old values. Ignore Xmax */
                                        case 'o':
                                                SET_OPTION(blockOptions, BLOCK_IGNORE_OLD, 'o');
@@ -2000,6 +2030,69 @@ DumpFileContents(unsigned int blockOptions,
        return result;
 }
 
+int
+PrintRelMappings(void)
+{
+       // For storing ingested data
+       char charbuf[RELMAPPER_FILESIZE];
+       RelMapFile *map;
+       RelMapping *mappings;
+       RelMapping m;
+       int bytesRead;
+
+       // For confirming Magic Number correctness
+       char m1[RELMAPPER_MAGICSIZE];
+       char m2[RELMAPPER_MAGICSIZE];
+       int magic_ref = RELMAPPER_FILEMAGIC;
+       int magic_val;
+       int num_loops;
+
+       // Read in the file
+       rewind(fp); // Make sure to start from the beginning
+       bytesRead = fread(charbuf,1,RELMAPPER_FILESIZE,fp);
+       if ( bytesRead != RELMAPPER_FILESIZE ) {
+               printf("Read %d bytes, expected %d\n", bytesRead, RELMAPPER_FILESIZE);
+               return 0;
+       }
+
+       // Convert to RelMapFile type for usability
+       map = (RelMapFile *) charbuf;
+
+
+       // Check and print Magic Number correctness
+       printf("Magic Number: 0x%x",map->magic);
+       magic_val = map->magic;
+
+       memcpy(m1,&magic_ref,RELMAPPER_MAGICSIZE);
+       memcpy(m2,&magic_val,RELMAPPER_MAGICSIZE);
+       if ( memcmp(m1,m2,RELMAPPER_MAGICSIZE) == 0 ) {
+               printf(" (CORRECT)\n");
+       } else {
+               printf(" (INCORRECT)\n");
+       }
+
+       // Print Mappings
+       printf("Num Mappings: %d\n",map->num_mappings);
+       printf("Detailed Mappings list:\n");
+       mappings = map->mappings;
+
+       // Limit number of mappings as per MAX_MAPPINGS
+       num_loops = map->num_mappings;
+       if ( map->num_mappings > MAX_MAPPINGS ) {
+               num_loops = MAX_MAPPINGS;
+               printf("  NOTE: listing has been limited to the first %d mappings\n", MAX_MAPPINGS);
+               printf("        (perhaps your file is not a valid pg_filenode.map file?)\n");
+       }
+
+       for (int i=0; i < num_loops; i++) {
+               m = mappings[i];
+               printf("OID: %u\tFilenode: %u\n",
+                       m.mapoid,
+                       m.mapfilenode);
+       }
+       return 1;
+}
+
 /* Consume the options and iterate through the given file, formatting as
  * requested. */
 int
@@ -2014,6 +2107,11 @@ main(int argv, char **argc)
         * where encountered */
        if (validOptions != OPT_RC_VALID)
                DisplayOptions(validOptions);
+       else if (isRelMapFile)
+       {
+               CreateDumpFileHeader(argv, argc);
+               exitCode = PrintRelMappings();
+       }
        else
        {
                /* Don't dump the header if we're dumping binary pages */
index fbb87924409d1aa31a3dd2410d350b6e7ad705dc..eaafde1a2da0aed4e31f646886cb3c7b838226ec 100644 (file)
@@ -137,6 +137,14 @@ typedef enum optionReturnCodes
 #define EOF_ENCOUNTERED (-1)   /* Indicator for partial read */
 #define BYTES_PER_LINE 16              /* Format the binary 16 bytes per line */
 
+/* Constants for pg_relnode.map decoding */
+#define RELMAPPER_MAGICSIZE   4
+#define RELMAPPER_FILESIZE    512
+/* From utils/cache/relmapper.c -- Maybe ask community to put
+ * these into utils/cache/relmapper.h? */
+#define RELMAPPER_FILEMAGIC   0x592717
+#define MAX_MAPPINGS          62
+
 extern char *fileName;
 
 /*