- // 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] = ""; 
-    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), 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 *) &- current_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 *) &- current_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 *) &- current_fsentry ->- u. symlink- . path); // convert from pointer to char array
 
-                printf ("   [SYMLINK] contents   = \"%s\"\n", ((char *) &- current_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 *) &- current_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 *) &- current_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), 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 
-       { 
-          // so 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 ("Boot blob at offset 0x%zx (%zd):\n",-  current_offset ,-  current_offset );
 
-          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 }; 
-    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.bin", outdir); 
-          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 *) &- current_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 
-       { 
-          // so 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.bin", outdir); 
-          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, file.size); 
-    return (0); 
- } 
-