// dump.c -- information dumping routines for ifstool
 
 
 
// standard C includes
 
#include <stdint.h>
 
#include <stdbool.h>
 
#include <stdlib.h>
 
#include <stdarg.h>
 
#include <stdio.h>
 
#include <string.h>
 
#include <errno.h>
 
#include <sys/stat.h>
 
#include <ctype.h>
 
#include <time.h>
 
 
 
// platform-specific includes
 
#ifdef _MSC_VER
 
#include <direct.h>
 
#include <sys/utime.h>
 
#else // !_MSC_VER
 
#include <sys/param.h>
 
#include <unistd.h>
 
#include <utime.h>
 
#endif // _MSC_VER
 
 
 
// own includes
 
#include "ucl/ucl.h"
 
#include "minilzo.h"
 
#include "buffer.h"
 
#include "sha512.h"
 
#include "ifsfile.h"
 
#include "elffile.h"
 
#include "utility.h"
 
 
 
 
 
// imported global variables
 
extern int verbose_level; // from ifstool.c
 
 
 
 
 
// exported function prototypes
 
int dump_ifs_info (const char *ifs_pathname, bool want_everything, bool hide_filename); // dumps detailed info about a particular IFS file on the standard output, returns 0 on success and >0 on error
 
int dump_ifs_contents (const char *ifs_pathname, const char *outdir); // dumps the IFS filesystem contents in outdir, returns 0 on success and >0 on error
 
int dump_file_hex (const char *pathname); // hexdump the content of pathname, returns 0 on success and != 0 on error
 
 
 
 
 
// prototypes of local functions
 
static int create_intermediate_dirs (const char *file_pathname); // creates all intermediate directories from root (or cwd) up to file_path, returns 0 on success and != 0 on error
 
static void hex_fprintf (FILE *fp, const uint8_t *data, size_t data_size, int howmany_columns, const char *fmt, ...); // hexdump-style formatted output to a file stream (which may be stdout/stderr)
 
static char *binary (const uint8_t x, char char_for_zero, char char_for_one); // returns the binary representation of x as a string
 
static char *describe_uint8 (const uint8_t x, const char *bitwise_stringdescs[8]); // returns the ORed description of byte 'x' according to the description strings for each bit
 
 
 
 
 
// imported function prototypes
 
extern int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness); // from ifstool.c
 
 
 
 
 
static int create_intermediate_dirs (const char *file_pathname)
 
{
 
   // creates all intermediate directories from root (or cwd) up to file_path
 
 
 
   char *temp_pathname;
 
   char *separator;
 
   char *ctx;
 
   size_t string_index;
 
   size_t length;
 
 
 
   temp_pathname = strdup (file_pathname); // have a working copy of file_pathname
 
   if (temp_pathname == NULL)
 
      return (-1); // on strdup() failure, return an error value (errno is set)
 
   length 
= strlen (temp_pathname
); 
   for (string_index = length - 1; string_index != SIZE_MAX; string_index--) // i.e. loop until it overflows
 
      if ((temp_pathname[string_index] == '/') || (temp_pathname[string_index] == '\\'))
 
         break; // look for the last directory separator and stop as soon as we find it
 
   if (string_index != SIZE_MAX)
 
   {
 
      for (; string_index < length; string_index++)
 
         temp_pathname[string_index] = 0; // if we found one, break there so as to have just the path and clear the rest of the string
 
      separator = strtok_r (&temp_pathname[1], "/\\", &ctx); // for each separator in the remaining string past the first one...
 
      while (separator != NULL)
 
      {
 
         (void) mkdir (temp_pathname, 0755); // create directories recursively
 
         temp_pathname
[strlen (temp_pathname
)] = '/'; // put the separator back 
         separator = strtok_r (NULL, "/\\", &ctx); // and look for the next one
 
      }
 
   }
 
 
 
   free (temp_pathname
); // release our working copy of file_pathname  
   return (0);
 
}
 
 
 
 
 
static void hex_fprintf (FILE *fp, const uint8_t *data, size_t data_size, int howmany_columns, const char *fmt, ...)
 
{
 
   // this function logs hexadecimal data to an opened file pointer (or to stdout/stderr)
 
 
 
   va_list argptr;
 
   size_t index;
 
   int i;
 
 
 
   // concatenate all the arguments in one string and write it to the file
 
 
 
   // for each row of howmany_columns bytes of data...
 
   for (index = 0; index < data_size; index += howmany_columns)
 
   {
 
      fprintf (fp
, "    %05zu  ", index
); // print array address of row  
      for (i = 0; i < howmany_columns; i++)
 
         if (index + i < data_size)
 
            fprintf (fp
, " %02X", data
[index 
+ i
]); // if row contains data, print data as hex bytes  
         else
 
            fprintf (fp
, "   "); // else fill the space with blanks  
      for (i = 0; i < howmany_columns; i++)
 
         if (index + i < data_size)
 
            fputc ((data
[index 
+ i
] >= 32) && (data
[index 
+ i
] < 127) ? data
[index 
+ i
] : '.', fp
); // now if row contains data, print data as ASCII  
         else
 
            fputc (' ', fp
); // else fill the space with blanks  
   }
 
 
 
   return; // and return
 
}
 
 
 
 
 
static char *binary (const uint8_t x, char char_for_zero, char char_for_one)
 
{
 
   // returns the binary representation of x as a string
 
 
 
   static thread_local char outstr[9] = "00000000";
 
   for (int i = 0; i < 8; i++)
 
      outstr[i] = (x & (0x80 >> i) ? char_for_one : char_for_zero);
 
   return (outstr);
 
}
 
 
 
 
 
static char *describe_uint8 (const uint8_t x, const char *bitwise_stringdescs[8])
 
{
 
   // returns the ORed description of byte 'x' according to the description strings for each bit
 
 
 
   static char *default_bitstrings[8] = { "bit0", "bit1", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7" };
 
   static thread_local char outstr[8 * 64] = "";
 
 
 
   outstr[0] = 0;
 
   for (int i = 0; i < 8; i++)
 
      if (x & (1 << i))
 
      {
 
         if (outstr[0] != 0)
 
            strcat_s (outstr, sizeof (outstr), "|");
 
         strcat_s (outstr, sizeof (outstr), ((bitwise_stringdescs != NULL) && (*bitwise_stringdescs[i] != 0) ? bitwise_stringdescs[i] : default_bitstrings[i]));
 
      }
 
   return (outstr);
 
}
 
 
 
 
 
int dump_ifs_info (const char *ifs_pathname, bool want_everything, bool hide_filename)
 
{
 
   #define hex_printf(buf,size,...) do { \
 
      if (want_everything || ((size) <= 16 * 1024)) /* only print when it's not too big (up to 16 kb) */\
 
         hex_fprintf (stdout, (buf), (size), 16, __VA_ARGS__); /* use 16 columns in hex output to stdout */ \
 
      else { \
 
         printf (__VA_ARGS__); \
 
         hex_fprintf (stdout, (buf), 1024, 16, "   first kilobyte:\n"); \
 
      } \
 
   } while (0)
 
   #define BINARY(x) binary ((x), '-', 'x')
 
 
 
   static const char *startupheader_flags1_strings[8] = {
 
      "VIRTUAL", // bit 0
 
      "BIGENDIAN", // bit 1
 
      "COMPRESS_BIT1", // bit 2
 
      "COMPRESS_BIT2", // bit 3
 
      "COMPRESS_BIT3", // bit 4
 
      "TRAILER_V2", // bit 5
 
      "", // bit 6
 
      "", // bit 7
 
   };
 
   static const char *imageheader_flags_strings[8] = {
 
      "BIGENDIAN", // bit 0
 
      "READONLY", // bit 1
 
      "INO_BITS", // bit 2
 
      "SORTED", // bit 3
 
      "TRAILER_V2", // bit 4
 
      "", // bit 5
 
      "", // bit 6
 
      "", // bit 7
 
   };
 
 
 
   startup_header_t *startup_header = NULL;
 
   size_t startupheader_offset = 0;
 
   startup_trailer_v1_t *startup_trailer_v1 = NULL;
 
   startup_trailer_v2_t *startup_trailer_v2 = NULL;
 
   size_t startuptrailer_offset = 0;
 
   image_header_t *image_header = NULL;
 
   size_t imageheader_offset = 0;
 
   image_trailer_v1_t *image_trailer_v1 = NULL;
 
   image_trailer_v2_t *image_trailer_v2 = NULL;
 
   size_t imagetrailer_offset = 0;
 
   fsentry_t **fsentries = NULL; // mallocated
 
   size_t fsentry_count = 0;
 
   fsentry_t *current_fsentry = NULL;
 
   buffer_t decompression_dst;
 
   char recorded_sha512[2 * SHA512_DIGEST_LENGTH + 1] = "";
 
   char computed_sha512[2 * SHA512_DIGEST_LENGTH + 1] = "";
 
   char *executable_format = NULL;
 
   size_t startupfile_blobsize = 0;
 
   size_t compressed_blocksize;
 
   void *reallocated_ptr;
 
   bool is_foreign_endianness;
 
   uint8_t *decompressor_out;
 
   uint8_t *decompressor_in;
 
   size_t decompressor_outlen;
 
   size_t bootfile_blobsize = 0;
 
   size_t current_offset;
 
   size_t fsentry_index;
 
   size_t nearest_distance;
 
   size_t nearest_index;
 
   size_t byte_index;
 
   uint32_t recorded_checksum;
 
   uint32_t computed_checksum;
 
   buffer_t file;
 
   time_t mtime;
 
   int cf;
 
 
 
   // open and read IFS file
 
   if (!Buffer_ReadFromFile (&file, ifs_pathname))
 
      DIE_WITH_EXITCODE 
(1, "can't open \"%s\" for reading: %s", ifs_pathname
, strerror (errno
)); 
 
 
   printf ("QNX In-kernel Filesystem analysis produced by ifstool version " VERSION_FMT_YYYYMMDD 
"\n", VERSION_ARG_YYYYMMDD
);  
   if (hide_filename)
 
      printf ("IFS file - size 0x%zx (%zd) bytes\n", file.
size, file.
size);  
   else
 
      printf ("IFS file \"%s\" - size 0x%zx (%zd) bytes\n", ifs_pathname
, file.
size, file.
size);  
 
 
   // parse file from start to end
 
   current_offset = 0;
 
   cf = STARTUP_HDR_FLAGS1_COMPRESS_NONE;
 
   for (;;)
 
   {
 
      // does a startup header start here ?
 
      if ((current_offset + sizeof (startup_header_t) < file.size)
 
          && (startup_header == NULL)
 
          && (memcmp (&file.
bytes[current_offset
], "\xeb\x7e\xff\x00", 4) == 0))  
      {
 
         startupheader_offset = current_offset;
 
         startup_header = (startup_header_t *) &file.bytes[startupheader_offset];
 
 
 
         // layout:
 
         // [STARTUP HEADER]
 
         // (startup file blob)
 
         // [STARTUP TRAILER v1 or v2]
 
 
 
         printf ("Startup header at offset 0x%zx (%zd):\n", current_offset
, current_offset
);  
         printf ("   signature     = %02x %02x %02x %02x - good\n", startup_header
->signature
[0], startup_header
->signature
[1], startup_header
->signature
[2], startup_header
->signature
[3]);  
         printf ("   version       = 0x%04x (%d) - %s\n", startup_header
->version
, startup_header
->version
, (startup_header
->version 
== 1 ? "looks good" : "???"));  
         cf = startup_header->flags1 & STARTUP_HDR_FLAGS1_COMPRESS_MASK;
 
         printf ("   flags1        = 0x%02x (%s) - %s\n", startup_header
->flags1
, describe_uint8 
(startup_header
->flags1
, startupheader_flags1_strings
), (cf 
== STARTUP_HDR_FLAGS1_COMPRESS_NONE 
? "uncompressed image" : (cf 
== STARTUP_HDR_FLAGS1_COMPRESS_ZLIB 
? "Zlib-compressed image (non-bootable)" : (cf 
== STARTUP_HDR_FLAGS1_COMPRESS_LZO 
? "LZO-compressed image" : (cf 
== STARTUP_HDR_FLAGS1_COMPRESS_UCL 
? "UCL-compressed image" : "compressed image (unknown algorithm)")))));  
         printf ("   flags2        = 0x%02x (%s) - %s\n", startup_header
->flags2
, BINARY 
(startup_header
->flags2
), (startup_header
->flags2 
== 0 ? "looks good" : "???"));  
         printf ("   header_size   = 0x%04x (%d) - %s\n", startup_header
->header_size
, startup_header
->header_size
, (startup_header
->header_size 
== sizeof (startup_header_t
) ? "looks good" : "BAD"));  
         printf ("   machine       = 0x%04x (%d) - %s\n", startup_header
->machine
, startup_header
->machine
, (startup_header
->machine 
== ELF_MACHINE_X86_64 
? "x86_64" : (startup_header
->machine 
== ELF_MACHINE_AARCH64 
? "aarch64" : "unknown")));  
         printf ("   startup_vaddr = 0x%08x (%d) - virtual address to transfer to after IPL is done\n", startup_header
->startup_vaddr
, startup_header
->startup_vaddr
);  
         printf ("   paddr_bias    = 0x%08x (%d) - value to add to physical addresses to get an indirectable pointer value\n", startup_header
->paddr_bias
, startup_header
->paddr_bias
);  
         printf ("   image_paddr   = 0x%08x (%d) - physical address of image\n", startup_header
->image_paddr
, startup_header
->image_paddr
);  
         printf ("   ram_paddr     = 0x%08x (%d) - physical address of RAM to copy image to (startup_size bytes copied)\n", startup_header
->ram_paddr
, startup_header
->ram_paddr
);  
         printf ("   ram_size      = 0x%08x (%d) - amount of RAM used by the startup program and executables in the fs\n", startup_header
->ram_size
, startup_header
->ram_size
);  
         printf ("   startup_size  = 0x%08x (%d) - size of startup (never compressed) - %s\n", startup_header
->startup_size
, startup_header
->startup_size
, (current_offset 
+ sizeof (image_header_t
) + startup_header
->startup_size 
+ (startup_header
->flags1 
& STARTUP_HDR_FLAGS1_TRAILER_V2 
? sizeof (image_trailer_v2_t
) : sizeof (image_trailer_v1_t
)) < file.
size ? "looks good" : "BAD (IFS file too short)"));  
         printf ("   stored_size   = 0x%08x (%d) - stored size of image - %s\n", startup_header
->stored_size
, startup_header
->stored_size
, (startup_header
->stored_size 
<= startup_header
->ram_size 
? "looks good" : "???"));  
         printf ("   imagefs_paddr = 0x%08x (%d) - set by IPL when startup runs - %s\n", startup_header
->imagefs_paddr
, startup_header
->imagefs_paddr
, (startup_header
->imagefs_paddr 
== 0 ? "looks good" : "??? should be zero"));  
         printf ("   imagefs_size  = 0x%08x (%d) - size of uncompressed imagefs\n", startup_header
->imagefs_size
, startup_header
->imagefs_size
);  
         printf ("   preboot_size  = 0x%04x (%d) - size of loaded before header - %s\n", startup_header
->preboot_size
, startup_header
->preboot_size
, (startup_header
->preboot_size 
== current_offset 
? "looks good" : "???"));  
         printf ("   zero0         = 0x%04x (%d) - zeros - %s\n", startup_header
->zero0
, startup_header
->zero0
, (startup_header
->zero0 
== 0 ? "looks good" : "??? should be zero"));  
         printf ("   zero[0]       = 0x%08x (%d) - zeros - %s\n", startup_header
->zero
[0], startup_header
->zero
[0], (startup_header
->zero
[0] == 0 ? "looks good" : "??? should be zero"));  
         printf ("   addr_off      = 0x%016llx (%lld) - offset for startup_vaddr and [image|ram|imagefs]_paddr - %s\n", (unsigned long long) startup_header
->addr_off
, (unsigned long long) startup_header
->addr_off
, (startup_header
->addr_off 
== 0 ? "looks good" : "??? should be zero"));  
         hex_printf ((uint8_t *) &startup_header->info[0], sizeof (startup_header->info), "   info[48] =\n");
 
 
 
         // validate that the file can contain up to the startup trailer
 
         if (current_offset + startup_header->startup_size > file.size)
 
         {
 
            LOG_WARNING ("this IFS file is corrupted (startup trailer extends past end of file)");
 
            goto endofdata;
 
         }
 
 
 
         // check if this endianness is ours
 
         if (   ( (startup_header->flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
 
             || (!(startup_header->flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
 
            is_foreign_endianness = true; // if the header is big endian and we're on a little endian machine, or the other way around, it's a foreign endianness
 
         else
 
            is_foreign_endianness = false; // else this header is for the same endianness as us
 
 
 
         // locate the right startup trailer at the right offset
 
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
 
         {
 
            startuptrailer_offset = current_offset + startup_header->startup_size - sizeof (startup_trailer_v2_t);
 
            startup_trailer_v2 = (startup_trailer_v2_t *) &file.bytes[startuptrailer_offset];
 
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v2_t);
 
         }
 
         else // old V1 trailer
 
         {
 
            startuptrailer_offset = current_offset + startup_header->startup_size - sizeof (startup_trailer_v1_t);
 
            startup_trailer_v1 = (startup_trailer_v1_t *) &file.bytes[startuptrailer_offset];
 
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v1_t);
 
         }
 
 
 
         current_offset += sizeof (startup_header_t); // jump over the startup header and reach the startup blob
 
         printf ("Startup blob at offset 0x%zx (%zd):\n", current_offset
, current_offset
);  
         printf ("   size 0x%zx (%zd) bytes\n", startupfile_blobsize
, startupfile_blobsize
);  
         printf ("   checksum 0x%08x\n", update_checksum 
(&file.
bytes[current_offset
], startupfile_blobsize
, is_foreign_endianness
));  
 
 
         current_offset += startupfile_blobsize; // jump over the startup blob and reach the startup trailer
 
         printf ("Startup trailer at offset 0x%zx (%zd) - version %d:\n", current_offset
, current_offset
, (startup_header
->flags1 
& STARTUP_HDR_FLAGS1_TRAILER_V2 
? 2 : 1));  
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
 
         {
 
            for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
 
               sprintf_s (&recorded_sha512[2 * byte_index], 3, "%02x", startup_trailer_v2->sha512[byte_index]);
 
            strcpy_s (computed_sha512, sizeof (computed_sha512), (const char *) SHA512 (startup_header, startuptrailer_offset - startupheader_offset, NULL));
 
            recorded_checksum = startup_trailer_v2->cksum;
 
            computed_checksum = update_checksum (startup_header, startuptrailer_offset + SHA512_DIGEST_LENGTH - startupheader_offset, is_foreign_endianness);
 
            printf ("    sha512([0x%zx-0x%zx[) = %s - %s\n", startupheader_offset
, startuptrailer_offset
, recorded_sha512
, (strcasecmp 
(computed_sha512
, recorded_sha512
) == 0 ? "GOOD" : "BAD"));  
            printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", startupheader_offset
, startuptrailer_offset 
+ SHA512_DIGEST_LENGTH
, recorded_checksum
, (computed_checksum 
== recorded_checksum 
? "GOOD" : "BAD"));  
            if (strcasecmp (computed_sha512, recorded_sha512) != 0)
 
               printf ("Computed SHA-512: %s\n", computed_sha512
);  
            if (computed_checksum != recorded_checksum)
 
               printf ("Computed cksum: 0x%08x\n", computed_checksum
);  
         }
 
         else // old v1 trailer
 
         {
 
            recorded_checksum = startup_trailer_v1->cksum;
 
            computed_checksum = update_checksum (startup_header, sizeof (startup_header) + startupfile_blobsize, is_foreign_endianness);
 
            printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", startupheader_offset
, startuptrailer_offset
, recorded_checksum
, (computed_checksum 
== recorded_checksum 
? "GOOD" : "BAD"));  
            if (computed_checksum != recorded_checksum)
 
               printf ("Computed cksum: 0x%08x\n", computed_checksum
);  
         }
 
 
 
         current_offset += (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2 ? sizeof (startup_trailer_v2_t) : sizeof (startup_trailer_v1_t)); // now reach the next segment
 
      }
 
 
 
      // else does an image header start here ?
 
      else if ((current_offset + sizeof (image_header_t) < file.size)
 
               && (image_header == NULL)
 
               && (   ((cf 
== STARTUP_HDR_FLAGS1_COMPRESS_NONE
) && (memcmp (&file.
bytes[current_offset
], "imagefs", 7) == 0))  
                   || (cf != STARTUP_HDR_FLAGS1_COMPRESS_NONE) && (startup_header->imagefs_size > 0)))
 
      {
 
         imageheader_offset = current_offset;
 
         image_header = (image_header_t *) &file.bytes[imageheader_offset];
 
 
 
         // should we decompress it ?
 
         if (cf != STARTUP_HDR_FLAGS1_COMPRESS_NONE)
 
         {
 
            // it appears mkifs compresses data in blocks, prefixed by 2-byte block size in BIG ENDIAN
 
            Buffer_InitWithSize (&decompression_dst, startup_header->imagefs_size * 11 / 10); // mallocate and add 10% for safety
 
            decompression_dst.size = 0;
 
 
 
            if (cf == STARTUP_HDR_FLAGS1_COMPRESS_UCL)
 
               ASSERT (ucl_init () == UCL_E_OK, "UCL library initialization failed -- please recompile this tool with less aggressive optimizations");
 
            else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_LZO)
 
               ASSERT (lzo_init () == LZO_E_OK, "LZO library initialization failed -- please recompile this tool with less aggressive optimizations");
 
            else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_ZLIB)
 
            {
 
               LOG_WARNING ("unimplemented compression scheme: zlib (FIXME)");
 
               goto endofdata;
 
            }
 
            else
 
            {
 
               LOG_WARNING ("unsupported compression flags: 0x%2x", cf);
 
               goto endofdata;
 
            }
 
 
 
            // run the compressed payload (the imagefs) through the right decompression algorithm
 
            for (;;)
 
            {
 
               compressed_blocksize = (file.bytes[current_offset + 0] << 8) | (file.bytes[current_offset + 1] << 0); // read block size word (in big engian)
 
               current_offset += 2; // skip it
 
               if (compressed_blocksize == 0)
 
                  break; // a nil block size means end of stream is reached
 
               //LOG_DEBUG ("about to decompress block of %zd bytes", compressed_blocksize);
 
               decompressor_in = &file.bytes[current_offset];
 
               decompressor_out = &decompression_dst.bytes[decompression_dst.size];
 
               decompressor_outlen = 0;
 
 
 
               if (cf == STARTUP_HDR_FLAGS1_COMPRESS_UCL)
 
               {
 
                  // UCL compression. NOTE: the decompressor function used in startup-x86 is "ucl_nrv2b_decompress_8 / ucl_nrv2b_decompress_le16 / ucl_nrv2b_decompress_le32"
 
                  static ucl_uint ucl_outlen; // have a different variable because of pointer size mismatch
 
                  if (ucl_nrv2b_decompress_8 (decompressor_in, (ucl_uint) compressed_blocksize, decompressor_out, &ucl_outlen, NULL) != UCL_E_OK)
 
                  {
 
                     LOG_WARNING ("this IFS file is corrupted (UCL decompression failed)");
 
                     goto endofdata;
 
                  }
 
                  decompressor_outlen = ucl_outlen;
 
               }
 
               else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_LZO)
 
               {
 
                  // LZO decompression. NOTE: mkifs uses the full LZO package, whereas I use minilzo.
 
                  static lzo_uint lzo_outlen; // have a different variable because of pointer size mismatch
 
                  if (lzo1x_decompress (decompressor_in, (lzo_uint) compressed_blocksize, decompressor_out, &lzo_outlen, NULL) != LZO_E_OK)
 
                  {
 
                     LOG_WARNING ("this IFS file is corrupted (UCL decompression failed)");
 
                     goto endofdata;
 
                  }
 
                  decompressor_outlen = lzo_outlen;
 
               }
 
               else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_ZLIB)
 
                  ; // TODO
 
 
 
               current_offset += compressed_blocksize;
 
               decompression_dst.size += decompressor_outlen;
 
            }
 
 
 
            LOG_INFO ("decompressed %zd bytes into %zd bytes\n", file.size - imageheader_offset, decompression_dst.size);
 
 
 
            // now place the decompressed buffer in the payload at the imagefs offset
 
            ASSERT_WITH_ERRNO (Buffer_WriteBufferAt (&file, imageheader_offset, &decompression_dst));
 
            current_offset = imageheader_offset; // jump back to where we put the uncompressed data
 
            file.size = imageheader_offset + decompression_dst.size; // update IFS data size
 
 
 
            startup_header = (startup_header_t *) &file.bytes[startupheader_offset]; // fix the pointers that might have changed
 
            if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
 
               startup_trailer_v2 = (startup_trailer_v2_t *) &file.bytes[startuptrailer_offset];
 
            else // old V1 trailer
 
               startup_trailer_v1 = (startup_trailer_v1_t *) &file.bytes[startuptrailer_offset];
 
            image_header = (image_header_t *) &file.bytes[imageheader_offset]; // fix the pointers that might have changed
 
         }
 
 
 
         // layout:
 
         // [IMAGE HEADER]
 
         // [image directory entries]
 
         // [smallest file blobs up to KERNEL]
 
         // [padding]
 
         // [KERNEL]
 
         // [rest of file blobs]
 
         // [IMAGE FOOTER]
 
 
 
         printf ("Image header at offset %zx (%zd):\n", current_offset
, current_offset
);  
         printf ("   signature    = %02x %02x %02x %02x %02x %02x %02x (\"%.7s\") - good\n", image_header
->signature
[0], image_header
->signature
[1], image_header
->signature
[2], image_header
->signature
[3], image_header
->signature
[4], image_header
->signature
[5], image_header
->signature
[6], image_header
->signature
);  
         printf ("   flags        = 0x%02x (%s)\n", image_header
->flags
, describe_uint8 
(image_header
->flags
, imageheader_flags_strings
));  
         printf ("   image_size   = 0x%08x (%d) - size from header to end of trailer - %s\n", image_header
->image_size
, image_header
->image_size
, (current_offset 
+ image_header
->image_size 
<= file.
size ? "looks good" : "BAD (IFS file too short)"));  
         printf ("   hdr_dir_size = 0x%08x (%d) - size from header to last dirent - %s\n", image_header
->hdr_dir_size
, image_header
->hdr_dir_size
, (current_offset 
+ image_header
->hdr_dir_size 
< file.
size ? "looks good" : "BAD (IFS file too short)"));  
         printf ("   dir_offset   = 0x%08x (%d) - offset from header to first dirent - %s\n", image_header
->dir_offset
, image_header
->dir_offset
, (current_offset 
+ image_header
->dir_offset 
>= file.
size ? "BAD (IFS file too short)" : (image_header
->dir_offset 
> image_header
->hdr_dir_size 
? "BAD" : "looks good")));  
         printf ("   boot_ino[4]  = { 0x%08x, 0x%08x, 0x%08x, 0x%08x }\n", image_header
->boot_ino
[0], image_header
->boot_ino
[1], image_header
->boot_ino
[2], image_header
->boot_ino
[3]);  
         printf ("   script_ino   = 0x%08x (%d) - inode of compiled bootscript\n", image_header
->script_ino
, image_header
->script_ino
);  
         printf ("   chain_paddr  = 0x%08x (%d) - offset to next fs signature\n", image_header
->chain_paddr
, image_header
->chain_paddr
);  
         hex_printf ((uint8_t *) &image_header->spare[0], sizeof (image_header->spare), "   spare[10] =\n");
 
         printf ("   mountflags   = 0x%08x (%s %s %s %s)\n", image_header
->mountflags
, BINARY 
(((uint8_t *) &image_header
->mountflags
)[0]), BINARY 
(((uint8_t *) &image_header
->mountflags
)[1]), BINARY 
(((uint8_t *) &image_header
->mountflags
)[2]), BINARY 
(((uint8_t *) &image_header
->mountflags
)[3]));  
         printf ("   mountpoint   = \"%s\"\n", image_header
->mountpoint
);  
 
 
         // validate that the file can contain up to the image trailer
 
         if (current_offset + image_header->image_size > file.size)
 
         {
 
            LOG_WARNING ("this IFS file is corrupted (image trailer extends past end of file)");
 
            goto endofdata;
 
         }
 
 
 
         // check if this endianness is ours
 
         if (   ( (image_header->flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
 
             || (!(image_header->flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
 
            is_foreign_endianness = true; // if the header is big endian and we're on a little endian machine, or the other way around, it's a foreign endianness
 
         else
 
            is_foreign_endianness = false; // else this header is for the same endianness as us
 
 
 
         // locate the image trailer at the right offset
 
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
 
         {
 
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v2_t);
 
            image_trailer_v2 = (image_trailer_v2_t *) &file.bytes[imagetrailer_offset];
 
         }
 
         else // old V1 trailer
 
         {
 
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v1_t);
 
            image_trailer_v1 = (image_trailer_v1_t *) &file.bytes[imagetrailer_offset];
 
         }
 
 
 
         current_offset += sizeof (image_header_t); // jump over the image header and reach the first directory entry
 
 
 
         // there may be padding before the first directory entry
 
         if (image_header->dir_offset - sizeof (image_header_t) > 0)
 
            hex_printf (&file.bytes[current_offset], image_header->dir_offset - sizeof (image_header_t), "\n" "%zd padding bytes at offset 0x%zd (%zd):\n", image_header->dir_offset - sizeof (image_header_t), current_offset, current_offset);
 
         current_offset += image_header->dir_offset - sizeof (image_header_t); // padding was processed, jump over it
 
 
 
         // dump all directory entries until the last one included
 
         fsentries = NULL;
 
         fsentry_count = 0;
 
         while (current_offset < imageheader_offset + image_header->hdr_dir_size)
 
         {
 
            current_fsentry = (fsentry_t *) &file.bytes[current_offset];
 
 
 
            if (imageheader_offset + image_header->hdr_dir_size - current_offset < sizeof (current_fsentry->header))
 
               break; // end padding reached
 
 
 
            // stack up the filesystem entry pointers in an array while we read them
 
            reallocated_ptr 
= realloc (fsentries
, (fsentry_count 
+ 1) * sizeof (fsentry_t 
*)); 
            ASSERT_WITH_ERRNO (reallocated_ptr);
 
            fsentries = reallocated_ptr;
 
            fsentries[fsentry_count] = current_fsentry;
 
            fsentry_count++;
 
 
 
            printf ("Filesystem entry at offset 0x%zx (%zd) - last one at 0x%zx (%zd):\n", current_offset
, current_offset
, imageheader_offset 
+ image_header
->hdr_dir_size
, imageheader_offset 
+ image_header
->hdr_dir_size
);  
            printf ("   size           = 0x%04x (%d) - size of dirent - %s\n", current_fsentry
->header.
size, current_fsentry
->header.
size, ((current_fsentry
->header.
size > 0) && (current_offset 
+ current_fsentry
->header.
size < file.
size) ? "looks good" : "BAD"));  
            printf ("   extattr_offset = 0x%04x (%d) - %s\n", current_fsentry
->header.
extattr_offset, current_fsentry
->header.
extattr_offset, (current_fsentry
->header.
extattr_offset == 0 ? "no extattr" : "has extattr"));  
            printf ("   ino            = 0x%08x (%d) - inode number (%s%s%s%s)\n", current_fsentry
->header.
ino, current_fsentry
->header.
ino, (current_fsentry
->header.
ino & 0xE0000000 ? "is" : "nothing special"), (current_fsentry
->header.
ino & IFS_INO_PROCESSED_ELF 
? " PROCESSED_ELF" : ""), (current_fsentry
->header.
ino & IFS_INO_RUNONCE_ELF 
? " RUNONCE_ELF" : ""), (current_fsentry
->header.
ino & IFS_INO_BOOTSTRAP_EXE 
? " BOOTSTRAP_EXE" : ""));  
            printf ("   mode           = 0x%08x (%d) - %s (0%o), POSIX permissions 0%o\n", current_fsentry
->header.
mode, current_fsentry
->header.
mode, (S_ISDIR 
(current_fsentry
->header.
mode) ? "directory" : (S_ISREG 
(current_fsentry
->header.
mode) ? "file" : (S_ISLNK 
(current_fsentry
->header.
mode) ? "symlink" : "device"))), (current_fsentry
->header.
mode & 0xF000) >> 12, current_fsentry
->header.
mode & 0xFFF);  
            printf ("   gid            = 0x%08x (%d) - owner group ID%s\n", current_fsentry
->header.
gid, current_fsentry
->header.
gid, (current_fsentry
->header.
gid == 0 ? " (root)" : ""));  
            printf ("   uid            = 0x%08x (%d) - owner user ID%s\n", current_fsentry
->header.
uid, current_fsentry
->header.
uid, (current_fsentry
->header.
uid == 0 ? " (root)" : ""));  
            mtime = (time_t) current_fsentry->header.mtime;
 
            printf ("   mtime          = 0x%08x (%d) - POSIX timestamp: %s", current_fsentry
->header.
mtime, current_fsentry
->header.
mtime, asctime (localtime (&mtime
))); // NOTE: asctime() provides the newline  
            if (S_ISDIR (current_fsentry->header.mode))
 
               printf ("   [DIRECTORY] path = \"%s\"\n", (char *) ¤t_fsentry
->u.
dir.
path); // convert from pointer to char array  
            else if (S_ISREG (current_fsentry->header.mode))
 
            {
 
               printf ("   [FILE] offset = 0x%08x (%d) - %s\n", current_fsentry
->u.
file.
offset, current_fsentry
->u.
file.
offset, (imageheader_offset 
+ current_fsentry
->u.
file.
offset < file.
size ? "looks good" : "BAD (IFS file too short)"));  
               printf ("   [FILE] size   = 0x%08x (%d) - %s\n", current_fsentry
->u.
file.
size, current_fsentry
->u.
file.
size, (imageheader_offset 
+ current_fsentry
->u.
file.
offset + current_fsentry
->u.
file.
size < file.
size ? "looks good" : "BAD (IFS file too short)"));  
               printf ("   [FILE] path   = \"%s\"\n", (char *) ¤t_fsentry
->u.
file.
path); // convert from pointer to char array  
            }
 
            else if (S_ISLNK (current_fsentry->header.mode))
 
            {
 
               printf ("   [SYMLINK] sym_offset = 0x%04x (%d) - %s\n", current_fsentry
->u.
symlink.
sym_offset, current_fsentry
->u.
symlink.
sym_offset, (sizeof (current_fsentry
->header
) + 2 * sizeof (uint16_t) + current_fsentry
->u.
symlink.
sym_offset <= current_fsentry
->header.
size ? "looks good" : "BAD (dirent too short)"));  
               printf ("   [SYMLINK] sym_size   = 0x%04x (%d) - %s\n", current_fsentry
->u.
symlink.
sym_size, current_fsentry
->u.
symlink.
sym_size, (sizeof (current_fsentry
->header
) + 2 * sizeof (uint16_t) + current_fsentry
->u.
symlink.
sym_offset + current_fsentry
->u.
symlink.
sym_size <= current_fsentry
->header.
size ? "looks good" : "BAD (dirent too short)"));  
               printf ("   [SYMLINK] path       = \"%s\"\n", (char *) ¤t_fsentry
->u.
symlink.
path); // convert from pointer to char array  
               printf ("   [SYMLINK] contents   = \"%s\"\n", ((char *) ¤t_fsentry
->u.
symlink.
path) + current_fsentry
->u.
symlink.
sym_offset); // convert from pointer to char array  
            }
 
            else // can only be a device
 
            {
 
               printf ("   [DEVICE] dev  = 0x%08x (%d)\n", current_fsentry
->u.
device.
dev, current_fsentry
->u.
device.
dev);  
               printf ("   [DEVICE] rdev = 0x%08x (%d)\n", current_fsentry
->u.
device.
rdev, current_fsentry
->u.
device.
rdev);  
               printf ("   [DEVICE] path = \"%s\"\n", (char *) ¤t_fsentry
->u.
device.
path); // convert from pointer to char array  
            }
 
 
 
            if ((current_fsentry->header.size == 0) || (current_offset + current_fsentry->header.size >= file.size))
 
            {
 
               LOG_WARNING ("this IFS file is corrupted (the size of this directory entry is invalid)");
 
               goto endofdata;
 
            }
 
 
 
            current_offset += current_fsentry->header.size;
 
         }
 
         if (imageheader_offset + image_header->hdr_dir_size < current_offset + sizeof (current_fsentry->header))
 
            hex_printf (&file.bytes[current_offset], imageheader_offset + image_header->hdr_dir_size - current_offset, "\n" "%zd padding bytes at offset 0x%zx (%zd):\n", imageheader_offset + image_header->hdr_dir_size - current_offset, current_offset, current_offset);
 
         current_offset += imageheader_offset + image_header->hdr_dir_size - current_offset; // padding was processed, jump over it
 
 
 
         // at this point we are past the directory entries; what is stored now, up to and until the image trailer, is the files' data
 
         if (fsentry_count > 0)
 
         {
 
            while (current_offset < imagetrailer_offset) // and parse data up to the trailer
 
            {
 
               nearest_distance = SIZE_MAX;
 
               nearest_index = SIZE_MAX;
 
               for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
 
                  if (S_ISREG (fsentries[fsentry_index]->header.mode) // if this directory entry a file (i.e. it has a data blob)...
 
                      && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset >= current_offset) // ... AND its data blob is still ahead of our current pointer ...
 
                      && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset < nearest_distance)) // ... AND it's the closest to us we've found so far
 
                  {
 
                     nearest_distance = imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset; // then remember it
 
                     nearest_index = fsentry_index;
 
                  }
 
               if (nearest_index == SIZE_MAX)
 
                  break; // found no file ahead, which means we've parsed the whole file data area, so stop the loop so as to proceed to the image trailer
 
 
 
               fsentry_index = nearest_index;
 
               current_fsentry = fsentries[fsentry_index]; // quick access to closest fsentry
 
 
 
               // there may be padding before the file data
 
               if (imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset > 0)
 
                  hex_printf (&file.bytes[current_offset], imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset, "\n" "%zd padding bytes at offset 0x%zx (%zd):\n", imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset, current_offset, current_offset);
 
               current_offset += imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset; // padding was processed, jump over it
 
 
 
               printf ("File data blob at offset 0x%zx (%zd):\n", current_offset
, current_offset
);  
               printf ("   corresponding dirent index: %zd/%zd\n", fsentry_index
, fsentry_count
);  
               printf ("   corresponding inode 0x%08x (%d) -%s%s%s%s\n", current_fsentry
->header.
ino, current_fsentry
->header.
ino, (current_fsentry
->header.
ino & 0xE0000000 ? "" : " nothing special"), (current_fsentry
->header.
ino & IFS_INO_PROCESSED_ELF 
? " PROCESSED_ELF" : ""), (current_fsentry
->header.
ino & IFS_INO_RUNONCE_ELF 
? " RUNONCE_ELF" : ""), (current_fsentry
->header.
ino & IFS_INO_BOOTSTRAP_EXE 
? " BOOTSTRAP_EXE" : ""));  
               printf ("   corresponding path: \"%s\"\n", (char *) ¤t_fsentry
->u.
file.
path); // convert from pointer to char array  
               printf ("   size 0x%zx (%zd) bytes\n", (size_t) current_fsentry
->u.
file.
size, (size_t) current_fsentry
->u.
file.
size);  
               if (current_offset + 4 < file.size)
 
                  hex_printf (&file.bytes[current_offset], current_fsentry->u.file.size, "   data:\n");
 
               if (current_offset + current_fsentry->u.file.size < file.size)
 
                  printf ("   checksum %d\n", update_checksum 
(&file.
bytes[current_offset
], current_fsentry
->u.
file.
size, is_foreign_endianness
));  
               else
 
               {
 
                  LOG_WARNING ("this IFS file is corrupted (the size of this file data extends past the IFS size)");
 
                  goto endofdata;
 
               }
 
 
 
               current_offset += current_fsentry->u.file.size; // now jump over this file's data
 
            }
 
         }
 
 
 
         // ad this point we're past the last file data, there may be padding before the image trailer
 
         if (imagetrailer_offset - current_offset > 0)
 
            hex_printf (&file.bytes[current_offset], imagetrailer_offset - current_offset, "\n" "%zd padding bytes at offset %zx (%zd):\n", imagetrailer_offset - current_offset, current_offset, current_offset);
 
         current_offset += imagetrailer_offset - current_offset; // padding was processed, jump over it
 
 
 
         printf ("Image trailer at offset 0x%zx (%zd) - version %d:\n", current_offset
, current_offset
, (image_header
->flags 
& IMAGE_FLAGS_TRAILER_V2 
? 2 : 1));  
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
 
         {
 
            for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
 
               sprintf_s (&recorded_sha512[2 * byte_index], 3, "%02x", image_trailer_v2->sha512[byte_index]);
 
            strcpy_s (computed_sha512, sizeof (computed_sha512), (const char *) SHA512 (image_header, imagetrailer_offset - imageheader_offset, NULL));
 
            recorded_checksum = image_trailer_v2->cksum;
 
            computed_checksum = update_checksum (image_header, imagetrailer_offset + SHA512_DIGEST_LENGTH - imageheader_offset, is_foreign_endianness);
 
            printf ("    sha512([0x%zx-0x%zx[) = %s - %s\n", imageheader_offset
, imagetrailer_offset
, recorded_sha512
, (strcasecmp 
(computed_sha512
, recorded_sha512
) == 0 ? "GOOD" : "BAD"));  
            printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", imageheader_offset
, imagetrailer_offset 
+ SHA512_DIGEST_LENGTH
, recorded_checksum
, (computed_checksum 
== recorded_checksum 
? "GOOD" : "BAD"));  
            if (strcasecmp (computed_sha512, recorded_sha512) != 0)
 
               printf ("Computed SHA-512: %s\n", computed_sha512
);  
            if (computed_checksum != recorded_checksum)
 
               printf ("Computed cksum: 0x%08x\n", computed_checksum
);  
         }
 
         else // old v1 trailer
 
         {
 
            recorded_checksum = image_trailer_v1->cksum;
 
            computed_checksum = update_checksum (image_header, image_header->image_size - sizeof (image_trailer_v1_t), is_foreign_endianness);
 
            printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", imageheader_offset
, imagetrailer_offset
, recorded_checksum
, (computed_checksum 
== recorded_checksum 
? "GOOD" : "BAD"));  
            if (computed_checksum != recorded_checksum)
 
               printf ("Computed cksum: 0x%08x\n", computed_checksum
);  
         }
 
 
 
         current_offset += (image_header->flags & IMAGE_FLAGS_TRAILER_V2 ? sizeof (image_trailer_v2_t) : sizeof (image_trailer_v1_t)); // now reach the next segment (typically end of file)
 
      }
 
 
 
      // else it has to be a boot blob, of which we don't know the size, except that it has to fit in 0xffff bytes and be immediately followed by a startup header
 
      else
 
      {
 
         // if the first two bytes are Mark Zbikowski's initials, it's a PE executable, which means this is an EFI image
 
         executable_format 
= ((file.
size > 2) && (memcmp (file.
bytes, "MZ", 2) == 0) ? "UEFI" : "BIOS"); // anything else is a BIOS image 
 
 
         // scan for the first startup header magic and version (which makes us 6 bytes to scan for, i.e. "\xeb\x7e\xff\x00" for the magic and "\x01\x00" (LSB) for the version 1)
 
         for (byte_index = current_offset; byte_index < file.size - 6; byte_index++)
 
            if (memcmp (&file.
bytes[byte_index
], "\xeb\x7e\xff\x00" "\x01\x00", 4 + 2) == 0)  
               break; // stop as soon as we find it
 
 
 
         if (byte_index >= file.size - 6)
 
            break; // if not found, stop scanning
 
 
 
         bootfile_blobsize = byte_index - current_offset;
 
         printf ("%s boot code at offset 0x%zx (%zd):\n", executable_format
, current_offset
, current_offset
);  
         if ((strcmp (executable_format
, "UEFI") == 0) && (byte_index 
>= sizeof (uefi64_header_t
)))  
         {
 
            uefi64_header_t *uefi_header = (uefi64_header_t *) file.bytes; // dump UEFI header details
 
            printf ("   MZ header: signature: 5a 4d - 'MZ', good\n"); // we already validated this  
            printf ("   MZ header: number of bytes in last page: %d - %s\n", uefi_header
->dos_header.
bytes_in_last_page, (uefi_header
->dos_header.
bytes_in_last_page == 144 ? "good" : "unexpected value!?"));  
            printf ("   MZ header: number of 512-byte pages: %d - %s\n", uefi_header
->dos_header.
number_of_pages, (uefi_header
->dos_header.
number_of_pages == 3 ? "good" : "unexpected value!?"));  
            printf ("   MZ header: number of relocations: %d - %s\n", uefi_header
->dos_header.
number_of_relocations, (uefi_header
->dos_header.
number_of_relocations == 0 ? "good" : "unexpected value!?"));  
            printf ("   MZ header: header size in 16-byte paragraphs: %d - %s\n", uefi_header
->dos_header.
header_size_in_paragraphs, (uefi_header
->dos_header.
header_size_in_paragraphs == 4 ? "good" : "unexpected value!?"));  
            printf ("   MZ header: required/requested 16-byte paragraphs: %d/%d - %s\n", uefi_header
->dos_header.
required_paragraphs, uefi_header
->dos_header.
requested_paragraphs, (memcmp (&uefi_header
->dos_header.
required_paragraphs, "\0\0\xff\xff", 4) == 0 ? "good" : "unexpected value!?"));  
            printf ("   MZ header: initial relative relocatable segment offset fo SS: 0x%x - %s\n", uefi_header
->dos_header.
initial_relative_relocatable_segment_address_for_ss, (uefi_header
->dos_header.
initial_relative_relocatable_segment_address_for_ss == 0 ? "good" : "unexpected value!?"));  
            printf ("   MZ header: initial stack pointer value: 0x%04x - %s\n", uefi_header
->dos_header.
initial_stack_pointer_value, (uefi_header
->dos_header.
initial_stack_pointer_value == 184 ? "good" : "unexpected value!?"));  
            printf ("   MZ header: checksum (unused): 0x%x - %s\n", uefi_header
->dos_header.
unused_checksum, (uefi_header
->dos_header.
unused_checksum == 0 ? "good" : "unexpected value!?"));  
            printf ("   MZ header: initial instruction pointer value: 0x%04x - %s\n", uefi_header
->dos_header.
initial_instruction_pointer_value, (uefi_header
->dos_header.
initial_instruction_pointer_value == 0 ? "good" : "unexpected value!?"));  
            printf ("   MZ header: initial relative relocatable segment offset fo CS: 0x%x - %s\n", uefi_header
->dos_header.
initial_relative_relocatable_segment_address_for_cs, (uefi_header
->dos_header.
initial_relative_relocatable_segment_address_for_cs == 0 ? "good" : "unexpected value!?"));  
            printf ("   MZ header: absolute offset to relocation table: 0x%x - %s\n", uefi_header
->dos_header.
absolute_offset_to_relocation_table, (uefi_header
->dos_header.
absolute_offset_to_relocation_table == 64 ? "good" : "unexpected value!?"));  
            printf ("   MZ header: reserved_words1[4]: %s\n", (memcmp (uefi_header
->dos_header.
reserved_words1, "\0\0\0\0\0\0\0\0", 4 * sizeof (uint16_t)) == 0 ? "zerofilled, good" : "contains data, unexpected value!?"));  
            printf ("   MZ header: OEM identifier: 0x%04x - %s\n", uefi_header
->dos_header.
oem_identifier, (uefi_header
->dos_header.
oem_identifier == 0 ? "good" : "unexpected value!?"));  
            printf ("   MZ header: OEM info: 0x%04x - %s\n", uefi_header
->dos_header.
oem_info, (uefi_header
->dos_header.
oem_info == 0 ? "good" : "unexpected value!?"));  
            printf ("   MZ header: reserved_words2[10]: %s\n", (memcmp (uefi_header
->dos_header.
reserved_words2, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 10 * sizeof (uint16_t)) == 0 ? "zerofilled, good" : "contains data, unexpected value!?"));  
            printf ("   MZ header: absolute offset to PE header: 0x%08x (%d) - %s\n", uefi_header
->dos_header.
absolute_offset_to_pe_header, uefi_header
->dos_header.
absolute_offset_to_pe_header, (uefi_header
->dos_header.
absolute_offset_to_pe_header == 128 ? "good" : "unexpected value!?"));  
            hex_printf (uefi_header->dos_stub_bytes, 64, "   MZ header: DOS stub code bytes (64 bytes):\n");
 
            printf ("   PE header: signature: %02x %02x %02x %02x - %s\n", uefi_header
->pe_header.
signature[0], uefi_header
->pe_header.
signature[1], uefi_header
->pe_header.
signature[2], uefi_header
->pe_header.
signature[3], (memcmp (uefi_header
->pe_header.
signature, "PE\0\0", 4) == 0 ? "'PE\\0\\0' - good" : "unexpected value!?"));  
            printf ("   PE header: machine type: 0x%04x - %s\n", uefi_header
->pe_header.
machine_type, (uefi_header
->pe_header.
machine_type == 0x8664 ? "AMD64, good" : "unexpected value!?"));  
            printf ("   PE header: number of sections: %d - %s\n", uefi_header
->pe_header.
number_of_sections, (uefi_header
->pe_header.
number_of_sections == 1 ? "good" : "unexpected value!?"));  
            printf ("   PE header: symbols table offset: 0x%x (%d) - %s\n", uefi_header
->pe_header.
offset_to_symbols_table, uefi_header
->pe_header.
offset_to_symbols_table, (uefi_header
->pe_header.
offset_to_symbols_table == 0 ? "good" : "unexpected value!?"));  
            printf ("   PE header: number of symbols: %d - %s\n", uefi_header
->pe_header.
number_of_symbols, (uefi_header
->pe_header.
number_of_symbols == 0 ? "good" : "unexpected value!?"));  
            printf ("   PE header: size of optional header: %d - %s\n", uefi_header
->pe_header.
size_of_optional_header, (uefi_header
->pe_header.
size_of_optional_header == 240 ? "good" : "unexpected value!?"));  
            printf ("   PE header: characteristics bitmap: 0x%04x - %s\n", uefi_header
->pe_header.
characteristics_bitmap, (uefi_header
->pe_header.
characteristics_bitmap == 0x223 ? "executable + uses large addresses + relocs stripped + debug info stripped, good" : "unexpected value!?"));  
            printf ("   optional header: signature: %02x %02x - %s\n", uefi_header
->optional_header64.
signature[0], uefi_header
->optional_header64.
signature[1], (memcmp (uefi_header
->optional_header64.
signature, "\x0b\x02", 2) == 0 ? "64-bit optional header magic, good" : "unexpected value!?"));  
            printf ("   optional header: linker version: %u.%u - %s\n", uefi_header
->optional_header64.
linker_version_major, uefi_header
->optional_header64.
linker_version_minor, (memcmp (&uefi_header
->optional_header64.
linker_version_major, "\0\0", 2) == 0 ? "good" : "unexpected value!?"));  
            printf ("   optional header: code size: 0x%x (%d) - %s\n", uefi_header
->optional_header64.
code_size, uefi_header
->optional_header64.
code_size, (byte_index 
+ uefi_header
->optional_header64.
code_size == file.
size ? "good" : "BAD (should equal file size minus UEFI boot prefix size)"));  
            printf ("   optional header: size of initialized data: 0x%x (%d) - %s\n", uefi_header
->optional_header64.
size_of_initialized_data, uefi_header
->optional_header64.
size_of_initialized_data, (uefi_header
->optional_header64.
size_of_initialized_data == 0 ? "good" : "unexpected value!?"));  
            printf ("   optional header: size of uninitialized data: 0x%x (%d) - %s\n", uefi_header
->optional_header64.
size_of_uninitialized_data, uefi_header
->optional_header64.
size_of_uninitialized_data, (uefi_header
->optional_header64.
size_of_uninitialized_data == 0 ? "good" : "unexpected value!?"));  
            printf ("   optional header: entrypoint address: 0x%x (%d)\n", uefi_header
->optional_header64.
entrypoint_address, uefi_header
->optional_header64.
entrypoint_address); // TODO: validate  
            printf ("   optional header: code base: 0x%x (%d) - %s\n", uefi_header
->optional_header64.
code_base, uefi_header
->optional_header64.
code_base, (uefi_header
->optional_header64.
code_base == 0 ? "good" : "unexpected value!?"));  
            printf ("   optional header: image base: 0x%llx (%llu)\n", uefi_header
->optional_header64.
image_base, uefi_header
->optional_header64.
image_base);  
            printf ("   optional header: section alignment: 0x%x (%d) - %s\n", uefi_header
->optional_header64.
section_alignment, uefi_header
->optional_header64.
section_alignment, ((uefi_header
->pe_header.
machine_type == 0x8664) && (uefi_header
->optional_header64.
section_alignment == 4096) ? "good" : "unexpected value!? (should equal system page size)"));  
            printf ("   optional header: file alignment: 0x%x (%d) - %s\n", uefi_header
->optional_header64.
file_alignment, uefi_header
->optional_header64.
file_alignment, (uefi_header
->optional_header64.
file_alignment == 512 ? "good" : "unexpected value!? (must be a power of 2 between 512 and 65536)"));  
            printf ("   optional header: OS version: %u.%u - %s\n", uefi_header
->optional_header64.
os_version_major, uefi_header
->optional_header64.
os_version_minor, (memcmp (&uefi_header
->optional_header64.
os_version_major, "\0\0", 2) == 0 ? "good" : "unexpected value!?"));  
            printf ("   optional header: image version: %u.%u - %s\n", uefi_header
->optional_header64.
image_version_major, uefi_header
->optional_header64.
image_version_minor, (memcmp (&uefi_header
->optional_header64.
image_version_major, "\0\0", 2) == 0 ? "good" : "unexpected value!?"));  
            printf ("   optional header: subsystem version: %u.%u - %s\n", uefi_header
->optional_header64.
subsystem_version_major, uefi_header
->optional_header64.
subsystem_version_minor, (memcmp (&uefi_header
->optional_header64.
subsystem_version_major, "\0\0", 2) == 0 ? "good" : "unexpected value!?"));  
            printf ("   optional header: Win32 version value: 0x%x (%d) - %s\n", uefi_header
->optional_header64.
win32_version_value, uefi_header
->optional_header64.
win32_version_value, (uefi_header
->optional_header64.
win32_version_value == 0 ? "good" : "unexpected value!?"));  
            printf ("   optional header: image size: 0x%x (%d) - %s\n", uefi_header
->optional_header64.
image_size, uefi_header
->optional_header64.
image_size, (uefi_header
->optional_header64.
image_size == file.
size ? "good" : "BAD (should equal file size)"));  
            printf ("   optional header: size of headers: 0x%x (%d) - %s\n", uefi_header
->optional_header64.
size_of_headers, uefi_header
->optional_header64.
size_of_headers, (uefi_header
->optional_header64.
size_of_headers == byte_index 
? "good" : "BAD (should equal UEFI boot prefix size)"));  
            printf ("   optional header: checksum (unused): 0x%x - %s\n", uefi_header
->optional_header64.
unused_checksum, (uefi_header
->optional_header64.
unused_checksum == 0 ? "good" : "unexpected value!?"));  
            printf ("   optional header: subsystem type: 0x%x (%d) - %s\n", uefi_header
->optional_header64.
subsystem_type, uefi_header
->optional_header64.
subsystem_type, (uefi_header
->optional_header64.
subsystem_type == 10 ? "IMAGE_SUBSYSTEM_EFI_APPLICATION, good" : "BAD (should be IMAGE_SUBSYSTEM_EFI_APPLICATION i.e. 10 decimal)"));  
            printf ("   optional header: DLL characteristics bitmap: 0x%x (%d) - %s\n", uefi_header
->optional_header64.
dll_characteristics_bitmap, uefi_header
->optional_header64.
dll_characteristics_bitmap, (uefi_header
->optional_header64.
dll_characteristics_bitmap == 0 ? "nil, good" : "BAD (should be 0)"));  
            printf ("   optional header: stack commit/reserve size: %llu/%llu - %s\n", uefi_header
->optional_header64.
stack_commit_size, uefi_header
->optional_header64.
stack_reserve_size, ((uefi_header
->optional_header64.
stack_commit_size == uefi_header
->optional_header64.
section_alignment) && (uefi_header
->optional_header64.
stack_reserve_size == uefi_header
->optional_header64.
section_alignment) ? "good" : "unexpected value!? (both should equal system page size)"));  
            printf ("   optional header: heap commit/reserve size: %llu/%llu - %s\n", uefi_header
->optional_header64.
heap_commit_size, uefi_header
->optional_header64.
heap_reserve_size, ((uefi_header
->optional_header64.
heap_commit_size == 0) && (uefi_header
->optional_header64.
heap_reserve_size == 0) ? "good" : "unexpected value!? (both should be zero)"));  
            printf ("   optional header: loader flags: 0x%x (%d) - %s\n", uefi_header
->optional_header64.
unused_loader_flags, uefi_header
->optional_header64.
unused_loader_flags, (uefi_header
->optional_header64.
unused_loader_flags == 0 ? "good" : "BAD (should be zero)"));  
            printf ("   optional header: number of data directories: %d - %s\n", uefi_header
->optional_header64.
number_of_data_directories, (uefi_header
->optional_header64.
number_of_data_directories == 16 ? "good" : "unexpected value!?"));  
            printf ("   optional header: data directories: %s\n", (memcmp (uefi_header
->optional_header64.
data_directories, "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0", 8 * 16) == 0 ? "zerofilled, good" : "contains data, unexpected value!?"));  
            printf ("   image section header: section name: '%.8s' - %s\n", uefi_header
->unique_section.
section_name, (memcmp (uefi_header
->unique_section.
section_name, "image\0\0\0", 8) == 0 ? "good" : "unexpected value!?"));  
            printf ("   image section header: virtual size: 0x%x (%d) - %s\n", uefi_header
->unique_section.
virtual_size, uefi_header
->unique_section.
virtual_size, (uefi_header
->unique_section.
virtual_size == uefi_header
->optional_header64.
code_size ? "good" : "BAD (should equal code size)"));  
            printf ("   image section header: virtual address: 0x%x (%d) - %s\n", uefi_header
->unique_section.
virtual_address, uefi_header
->unique_section.
virtual_address, (uefi_header
->unique_section.
virtual_address == byte_index 
? "good" : "BAD (should equal UEFI boot prefix size)"));  
            printf ("   image section header: raw data size: 0x%x (%d) - %s\n", uefi_header
->unique_section.
rawdata_size, uefi_header
->unique_section.
rawdata_size, (uefi_header
->unique_section.
rawdata_size == uefi_header
->optional_header64.
code_size ? "good" : "BAD (should equal code size)"));  
            printf ("   image section header: raw data offset: 0x%x (%d) - %s\n", uefi_header
->unique_section.
rawdata_offset, uefi_header
->unique_section.
rawdata_offset, (uefi_header
->unique_section.
rawdata_offset == byte_index 
? "good" : "BAD (should equal UEFI boot prefix size)"));  
            printf ("   image section header: offset to relocations: 0x%x (%d) - %s\n", uefi_header
->unique_section.
offset_to_relocations, uefi_header
->unique_section.
offset_to_relocations, (uefi_header
->unique_section.
offset_to_relocations == 0 ? "good" : "unexpected value!?"));  
            printf ("   image section header: offset to line numbers: 0x%x (%d) - %s\n", uefi_header
->unique_section.
offset_to_linenos, uefi_header
->unique_section.
offset_to_linenos, (uefi_header
->unique_section.
offset_to_linenos == 0 ? "good" : "unexpected value!?"));  
            printf ("   image section header: number of relocations: 0x%x (%d) - %s\n", uefi_header
->unique_section.
relocation_count, uefi_header
->unique_section.
relocation_count, (uefi_header
->unique_section.
relocation_count == 0 ? "good" : "unexpected value!?"));  
            printf ("   image section header: number of line numbers: 0x%x (%d) - %s\n", uefi_header
->unique_section.
lineno_count, uefi_header
->unique_section.
lineno_count, (uefi_header
->unique_section.
lineno_count == 0 ? "good" : "unexpected value!?"));  
            printf ("   image section header: characteristics bitmap: 0x%04x - %s\n", uefi_header
->unique_section.
characteristics_bitmap, (uefi_header
->unique_section.
characteristics_bitmap == 0x60 ? "image contains code + image contains initialized data, good" : "unexpected value!?"));  
            hex_printf (&file.bytes[sizeof (uefi64_header_t)], byte_index - sizeof (uefi64_header_t), "   trailing padding (%zd bytes):\n", byte_index - sizeof (uefi64_header_t));
 
         }
 
         else // BIOS boot prefixes are opaque so far (FIXME: decompile to ASM?)
 
         {
 
            printf ("   size 0x%zx (%zd) bytes\n", bootfile_blobsize
, bootfile_blobsize
);  
            printf ("   checksum 0x%08x\n", update_checksum 
(&file.
bytes[current_offset
], bootfile_blobsize
, false)); // NOTE: endianness is not known yet -- assume same  
         }
 
 
 
         current_offset = byte_index; // now reach the next segment
 
      }
 
   }
 
 
 
endofdata:
 
   // at this point there's nothing left we're able to parse
 
   if (current_offset < file.size)
 
   {
 
      printf ("End of identifiable data reached.\n");  
      hex_printf (&file.bytes[current_offset], file.size - current_offset, "\n" "%zd extra bytes at offset %zx (%zd):\n", file.size - current_offset, current_offset, current_offset);
 
   }
 
 
 
   printf ("End of file reached at offset 0x%zx (%zd)\n", file.
size, file.
size);  
   printf ("IFS dissecation complete.\n");  
   return (0);
 
}
 
 
 
 
 
int dump_ifs_contents (const char *ifs_pathname, const char *outdir)
 
{
 
   static char outfile_pathname[MAXPATHLEN] = "";
 
 
 
   startup_header_t *startup_header = NULL;
 
   size_t startupheader_offset = 0;
 
   image_header_t *image_header = NULL;
 
   size_t imageheader_offset = 0;
 
   size_t imagetrailer_offset = 0;
 
   fsentry_t **fsentries = NULL; // mallocated
 
   size_t fsentry_count = 0;
 
   fsentry_t *current_fsentry = NULL;
 
   buffer_t decompression_dst;
 
   size_t startupfile_blobsize = 0;
 
   size_t compressed_blocksize;
 
   struct utimbuf file_times = { 0, 0 };
 
   char *exeformat_suffix = "";
 
   void *reallocated_ptr;
 
   uint8_t *decompressor_out;
 
   uint8_t *decompressor_in;
 
   size_t decompressor_outlen;
 
   size_t bootfile_blobsize = 0;
 
   size_t current_offset;
 
   size_t fsentry_index;
 
   size_t nearest_distance;
 
   size_t nearest_index;
 
   size_t byte_index;
 
   buffer_t file;
 
   FILE *fp;
 
   int cf;
 
 
 
   // open and read IFS file
 
   if (!Buffer_ReadFromFile (&file, ifs_pathname))
 
      DIE_WITH_EXITCODE 
(1, "can't open \"%s\" for reading: %s\n", ifs_pathname
, strerror (errno
)); 
 
 
   // create the output directory
 
   create_intermediate_dirs (outdir);
 
   (void) mkdir (outdir, 0755);
 
 
 
   // parse file from start to end
 
   current_offset = 0;
 
   for (;;)
 
   {
 
      // does a startup header start here ?
 
      if ((current_offset + sizeof (startup_header_t) < file.size)
 
          && (startup_header == NULL)
 
          && (memcmp (&file.
bytes[current_offset
], "\xeb\x7e\xff\x00", 4) == 0))  
      {
 
         startupheader_offset = current_offset;
 
         startup_header = (startup_header_t *) &file.bytes[startupheader_offset];
 
 
 
         // layout:
 
         // [STARTUP HEADER]
 
         // (startup file blob)
 
         // [STARTUP TRAILER v1 or v2]
 
 
 
         // validate that the file can contain up to the startup trailer
 
         if (current_offset + startup_header->startup_size > file.size)
 
         {
 
            LOG_WARNING ("this IFS file is corrupted (startup trailer extends past end of file)");
 
            goto endofdata;
 
         }
 
 
 
         // take note of the image compression flags
 
         cf = startup_header->flags1 & STARTUP_HDR_FLAGS1_COMPRESS_MASK;
 
 
 
         // locate the right startup trailer at the right offset
 
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
 
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v2_t);
 
         else // old V1 trailer
 
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v1_t);
 
 
 
         current_offset += sizeof (startup_header_t); // jump over the startup header and reach the startup blob
 
 
 
         // write startup blob
 
         sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/startup%s.bin", outdir, exeformat_suffix);
 
         fopen_s (&fp, outfile_pathname, "wb");
 
         ASSERT 
(fp
, "failed to open '%s': %s", outfile_pathname
, strerror (errno
)); 
         fwrite (&file.
bytes[current_offset
], 1, startupfile_blobsize
, fp
);  
 
 
         current_offset += startupfile_blobsize; // jump over the startup blob and reach the startup trailer
 
         current_offset += (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2 ? sizeof (startup_trailer_v2_t) : sizeof (startup_trailer_v1_t)); // jump over the startup trailer and reach the next segment
 
      }
 
 
 
      // else does an image header start here ?
 
      else if ((current_offset + sizeof (image_header_t) < file.size)
 
               && (image_header == NULL)
 
               && (((cf 
== STARTUP_HDR_FLAGS1_COMPRESS_NONE
) && (memcmp (&file.
bytes[current_offset
], "imagefs", 7) == 0))  
               || (cf != STARTUP_HDR_FLAGS1_COMPRESS_NONE) && (startup_header->imagefs_size > 0)))
 
      {
 
         imageheader_offset = current_offset;
 
         image_header = (image_header_t *) &file.bytes[imageheader_offset];
 
 
 
         // should we decompress it ?
 
         if (cf != STARTUP_HDR_FLAGS1_COMPRESS_NONE)
 
         {
 
            // it appears mkifs compresses data in blocks, prefixed by 2-byte block size in BIG ENDIAN
 
            Buffer_InitWithSize (&decompression_dst, startup_header->imagefs_size * 11 / 10); // mallocate and add 10% for safety
 
            decompression_dst.size = 0;
 
 
 
            if (cf == STARTUP_HDR_FLAGS1_COMPRESS_UCL)
 
               ASSERT (ucl_init () == UCL_E_OK, "UCL library initialization failed -- please recompile this tool with less aggressive optimizations");
 
            else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_LZO)
 
               ASSERT (lzo_init () == LZO_E_OK, "LZO library initialization failed -- please recompile this tool with less aggressive optimizations");
 
            else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_ZLIB)
 
            {
 
               LOG_WARNING ("unimplemented compression scheme: zlib (FIXME)");
 
               goto endofdata;
 
            }
 
            else
 
            {
 
               LOG_WARNING ("unsupported compression flags: 0x%2x", cf);
 
               goto endofdata;
 
            }
 
 
 
            // run the compressed payload (the imagefs) through the right decompression algorithm
 
            for (;;)
 
            {
 
               compressed_blocksize = (file.bytes[current_offset + 0] << 8) | (file.bytes[current_offset + 1] << 0); // read block size word (in big engian)
 
               current_offset += 2; // skip it
 
               if (compressed_blocksize == 0)
 
                  break; // a nil block size means end of stream is reached
 
               //LOG_DEBUG ("about to decompress block of %zd bytes", compressed_blocksize);
 
               decompressor_in = &file.bytes[current_offset];
 
               decompressor_out = &decompression_dst.bytes[decompression_dst.size];
 
               decompressor_outlen = 0;
 
 
 
               if (cf == STARTUP_HDR_FLAGS1_COMPRESS_UCL)
 
               {
 
                  // UCL compression. NOTE: the decompressor function used in startup-x86 is "ucl_nrv2b_decompress_8 / ucl_nrv2b_decompress_le16 / ucl_nrv2b_decompress_le32"
 
                  static ucl_uint ucl_outlen; // have a different variable because of pointer size mismatch
 
                  if (ucl_nrv2b_decompress_8 (decompressor_in, (ucl_uint) compressed_blocksize, decompressor_out, &ucl_outlen, NULL) != UCL_E_OK)
 
                  {
 
                     LOG_WARNING ("this IFS file is corrupted (UCL decompression failed)");
 
                     goto endofdata;
 
                  }
 
                  decompressor_outlen = ucl_outlen;
 
               }
 
               else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_LZO)
 
               {
 
                  // LZO decompression. NOTE: mkifs uses the full LZO package, whereas I use minilzo.
 
                  static lzo_uint lzo_outlen; // have a different variable because of pointer size mismatch
 
                  if (lzo1x_decompress (decompressor_in, (lzo_uint) compressed_blocksize, decompressor_out, &lzo_outlen, NULL) != LZO_E_OK)
 
                  {
 
                     LOG_WARNING ("this IFS file is corrupted (UCL decompression failed)");
 
                     goto endofdata;
 
                  }
 
                  decompressor_outlen = lzo_outlen;
 
               }
 
               else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_ZLIB)
 
                  ; // TODO
 
 
 
               current_offset += compressed_blocksize;
 
               decompression_dst.size += decompressor_outlen;
 
            }
 
 
 
            LOG_INFO ("decompressed %zd bytes into %zd bytes\n", file.size - imageheader_offset, decompression_dst.size);
 
 
 
            // now place the decompressed buffer in the payload at the imagefs offset
 
            ASSERT_WITH_ERRNO (Buffer_WriteBufferAt (&file, imageheader_offset, &decompression_dst));
 
            current_offset = imageheader_offset; // jump back to where we put the uncompressed data
 
            file.size = imageheader_offset + decompression_dst.size; // update IFS data size
 
 
 
            startup_header = (startup_header_t *) &file.bytes[startupheader_offset]; // fix the pointers that might have changed
 
            image_header = (image_header_t *) &file.bytes[imageheader_offset]; // fix the pointers that might have changed
 
         }
 
 
 
         // layout:
 
         // [IMAGE HEADER]
 
         // [image directory entries]
 
         // [smallest file blobs up to KERNEL]
 
         // [padding]
 
         // [KERNEL]
 
         // [rest of file blobs]
 
         // [IMAGE FOOTER]
 
 
 
         // validate that the file can contain up to the image trailer
 
         if (current_offset + image_header->image_size > file.size)
 
         {
 
            LOG_WARNING ("this IFS file is corrupted (image trailer extends past end of file)");
 
            goto endofdata;
 
         }
 
 
 
         // locate the image trailer at the right offset
 
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
 
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v2_t);
 
         else // old V1 trailer
 
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v1_t);
 
 
 
         current_offset += sizeof (image_header_t); // jump over the image header
 
         current_offset += image_header->dir_offset - sizeof (image_header_t); // jump over possible padding
 
 
 
         // dump all directory entries until the last one included
 
         fsentries = NULL;
 
         fsentry_count = 0;
 
         while (current_offset < imageheader_offset + image_header->hdr_dir_size)
 
         {
 
            current_fsentry = (fsentry_t *) &file.bytes[current_offset];
 
 
 
            if (imageheader_offset + image_header->hdr_dir_size - current_offset < sizeof (current_fsentry->header))
 
               break; // end padding reached
 
 
 
            // stack up the filesystem entry pointers in an array while we read them
 
            reallocated_ptr 
= realloc (fsentries
, (fsentry_count 
+ 1) * sizeof (fsentry_t 
*)); 
            ASSERT_WITH_ERRNO (reallocated_ptr);
 
            fsentries = reallocated_ptr;
 
            fsentries[fsentry_count] = current_fsentry;
 
            fsentry_count++;
 
 
 
            if ((current_fsentry->header.size == 0) || (current_offset + current_fsentry->header.size >= file.size))
 
            {
 
               LOG_WARNING ("this IFS file is corrupted (the size of this directory entry is invalid)");
 
               goto endofdata;
 
            }
 
 
 
            current_offset += current_fsentry->header.size;
 
         }
 
         current_offset += imageheader_offset + image_header->hdr_dir_size - current_offset; // jump over possible padding
 
 
 
         // at this point we are past the directory entries; what is stored now, up to and until the image trailer, is the files' data
 
         if (fsentry_count > 0)
 
         {
 
            while (current_offset < imagetrailer_offset) // and parse data up to the trailer
 
            {
 
               nearest_distance = SIZE_MAX;
 
               nearest_index = SIZE_MAX;
 
               for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
 
                  if (S_ISREG (fsentries[fsentry_index]->header.mode) // if this directory entry a file (i.e. it has a data blob)...
 
                      && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset >= current_offset) // ... AND its data blob is still ahead of our current pointer ...
 
                      && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset < nearest_distance)) // ... AND it's the closest to us we've found so far
 
                  {
 
                     nearest_distance = imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset; // then remember it
 
                     nearest_index = fsentry_index;
 
                  }
 
               if (nearest_index == SIZE_MAX)
 
                  break; // found no file ahead, which means we've parsed the whole file data area, so stop the loop so as to proceed to the image trailer
 
 
 
               fsentry_index = nearest_index;
 
               current_fsentry = fsentries[fsentry_index]; // quick access to closest fsentry
 
 
 
               current_offset += imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset; // jump over possible padding
 
 
 
               if (current_offset + current_fsentry->u.file.size >= file.size)
 
               {
 
                  LOG_WARNING ("this IFS file is corrupted (the size of this file data extends past the IFS size)");
 
                  goto endofdata;
 
               }
 
 
 
               // write filesystem data entry
 
               if (S_ISDIR (current_fsentry->header.mode))
 
               {
 
                  sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) ¤t_fsentry->u.dir.path); // convert from pointer to char array
 
                  create_intermediate_dirs (outfile_pathname);
 
                  (void) mkdir (outfile_pathname, current_fsentry->header.mode & 0777);
 
               }
 
               else if (S_ISLNK (current_fsentry->header.mode))
 
               {
 
                  sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) ¤t_fsentry->u.symlink.path); // convert from pointer to char array
 
                  create_intermediate_dirs (outfile_pathname);
 
#ifdef _WIN32
 
                  fopen_s (&fp, outfile_pathname, "wb"); // on Windows create symlinks as plain files
 
                  ASSERT 
(fp
, "failed to open '%s': %s", outfile_pathname
, strerror (errno
)); 
                  fwrite ((char *) ¤t_fsentry
->u.
symlink.
path + current_fsentry
->u.
symlink.
sym_offset, 1, current_fsentry
->u.
symlink.
sym_size, fp
); // convert from pointer to char array  
#else // !_WIN32, thus POSIX
 
                  symlink (current_fsentry->u.symlink.contents, outfile_pathname); // on UNIX systems, just create the symlink for real
 
#endif // _WIN32
 
               }
 
               else if (S_ISREG (current_fsentry->header.mode))
 
               {
 
                  sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) ¤t_fsentry->u.file.path); // convert from pointer to char array
 
                  create_intermediate_dirs (outfile_pathname);
 
                  fopen_s (&fp, outfile_pathname, "wb"); // on Windows create symlinks as plain files
 
                  ASSERT 
(fp
, "failed to open '%s': %s", outfile_pathname
, strerror (errno
)); 
                  fwrite (&file.
bytes[current_offset
], 1, current_fsentry
->u.
file.
size, fp
);  
               }
 
               else // must be a device node. Since we might not be the super-user and/or on Win32, create plain file with "X:Y" as data
 
               {
 
                  sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) ¤t_fsentry->u.device.path); // convert from pointer to char array
 
                  create_intermediate_dirs (outfile_pathname);
 
                  fopen_s (&fp, outfile_pathname, "wb"); // on Windows create symlinks as plain files
 
                  ASSERT 
(fp
, "failed to open '%s': %s", outfile_pathname
, strerror (errno
)); 
                  fprintf (fp
, "%u:%u", current_fsentry
->u.
device.
dev, current_fsentry
->u.
device.
rdev);  
               }
 
 
 
               // set created file mtime
 
               file_times.actime = current_fsentry->header.mtime;
 
               file_times.modtime = current_fsentry->header.mtime;
 
               utime (outfile_pathname, &file_times);
 
 
 
               // set created file mode
 
#ifndef _WIN32
 
               (void) chmod (outfile_pathname, current_fsentry->header.mode & 0777); // only on POSIX systems
 
#endif // !_WIN32
 
 
 
               current_offset += current_fsentry->u.file.size; // now jump over this file's data
 
            }
 
         }
 
 
 
         // ad this point we're past the last file data, there may be padding before the image trailer
 
         current_offset += imagetrailer_offset - current_offset; // jump over possible padding and reach the image trailer
 
         current_offset += (image_header->flags & IMAGE_FLAGS_TRAILER_V2 ? sizeof (image_trailer_v2_t) : sizeof (image_trailer_v1_t)); // now jump over the image trailer and reach the next segment (typically end of file)
 
      }
 
 
 
      // else it has to be a boot blob, of which we don't know the size, except that it has to fit in 0xffff bytes and be immediately followed by a startup header
 
      else
 
      {
 
         // if the first two bytes are Mark Zbikowski's initials, it's a PE executable, which means this is an EFI image
 
         exeformat_suffix 
= ((file.
size > 2) && (memcmp (file.
bytes, "MZ", 2) == 0) ? "-uefi" : "-bios"); // anything else is a BIOS image 
 
 
         // scan for the first startup header magic and version (which makes us 6 bytes to scan for, i.e. "\xeb\x7e\xff\x00" for the magic and "\x01\x00" (LSB) for the version 1)
 
         for (byte_index = current_offset; byte_index < file.size - 6; byte_index++)
 
            if (memcmp (&file.
bytes[byte_index
], "\xeb\x7e\xff\x00" "\x01\x00", 4 + 2) == 0)  
               break; // stop as soon as we find it
 
 
 
         if (byte_index >= file.size - 6)
 
            break; // if not found, stop scanning
 
 
 
         bootfile_blobsize = byte_index - current_offset;
 
 
 
         // write boot blob
 
         sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/boot%s.bin", outdir, exeformat_suffix);
 
         fopen_s (&fp, outfile_pathname, "wb");
 
         ASSERT 
(fp
, "failed to open '%s': %s", outfile_pathname
, strerror (errno
)); 
         fwrite (&file.
bytes[current_offset
], 1, bootfile_blobsize
, fp
);  
 
 
         current_offset = byte_index; // now reach the next segment
 
      }
 
   }
 
 
 
endofdata:
 
   return (0);
 
}
 
 
 
 
 
int dump_file_hex (const char *pathname)
 
{
 
   buffer_t file;
 
 
 
   ASSERT_WITH_ERRNO (Buffer_ReadFromFile (&file, pathname));
 
   hex_fprintf 
(stdout
, file.
bytes, file.
size, 16, "%s (%zd bytes):\n", &pathname
[strcspn (pathname
, "/\\")], file.
size); 
   return (0);
 
}