Subversion Repositories QNX 8.QNX8 IFS tool

Rev

Rev 16 | Rev 27 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
16 pmbaty 1
// dump.c -- information dumping routines for ifstool
2
 
3
// standard C includes
4
#include <stdint.h>
5
#include <stdbool.h>
6
#include <stdlib.h>
7
#include <stdarg.h>
8
#include <stdio.h>
9
#include <string.h>
10
#include <errno.h>
11
#include <sys/stat.h>
12
#include <ctype.h>
13
#include <time.h>
14
 
15
// platform-specific includes
16
#ifdef _MSC_VER
17
#include <direct.h>
18
#include <sys/utime.h>
19
#else // !_MSC_VER
20
#include <sys/param.h>
21
#include <unistd.h>
22
#include <utime.h>
23
#endif // _MSC_VER
24
 
25
// own includes
26 pmbaty 26
#include "ucl/ucl.h"
27
#include "minilzo.h"
16 pmbaty 28
#include "buffer.h"
29
#include "sha512.h"
30
#include "ifsfile.h"
31
#include "elffile.h"
32
#include "utility.h"
33
 
34
 
35
// imported global variables
36
extern int verbose_level; // from ifstool.c
37
 
38
 
39
// exported function prototypes
26 pmbaty 40
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
16 pmbaty 41
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
42
int dump_file_hex (const char *pathname); // hexdump the content of pathname, returns 0 on success and != 0 on error
43
 
44
 
45
// prototypes of local functions
46
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
47
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)
48
static char *binary (const uint8_t x, char char_for_zero, char char_for_one); // returns the binary representation of x as a string
49
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
50
 
51
 
52
// imported function prototypes
53
extern int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness); // from ifstool.c
54
 
55
 
56
static int create_intermediate_dirs (const char *file_pathname)
57
{
58
   // creates all intermediate directories from root (or cwd) up to file_path
59
 
60
   char *temp_pathname;
61
   char *separator;
62
   char *ctx;
63
   size_t string_index;
64
   size_t length;
65
 
66
   temp_pathname = strdup (file_pathname); // have a working copy of file_pathname
67
   if (temp_pathname == NULL)
68
      return (-1); // on strdup() failure, return an error value (errno is set)
69
   length = strlen (temp_pathname);
70
   for (string_index = length - 1; string_index != SIZE_MAX; string_index--) // i.e. loop until it overflows
71
      if ((temp_pathname[string_index] == '/') || (temp_pathname[string_index] == '\\'))
72
         break; // look for the last directory separator and stop as soon as we find it
73
   if (string_index != SIZE_MAX)
74
   {
75
      for (; string_index < length; string_index++)
76
         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
77
      separator = strtok_r (&temp_pathname[1], "/\\", &ctx); // for each separator in the remaining string past the first one...
78
      while (separator != NULL)
79
      {
80
         (void) mkdir (temp_pathname, 0755); // create directories recursively
81
         temp_pathname[strlen (temp_pathname)] = '/'; // put the separator back
82
         separator = strtok_r (NULL, "/\\", &ctx); // and look for the next one
83
      }
84
   }
85
 
86
   free (temp_pathname); // release our working copy of file_pathname
87
   return (0);
88
}
89
 
90
 
91
static void hex_fprintf (FILE *fp, const uint8_t *data, size_t data_size, int howmany_columns, const char *fmt, ...)
92
{
93
   // this function logs hexadecimal data to an opened file pointer (or to stdout/stderr)
94
 
95
   va_list argptr;
96
   size_t index;
97
   int i;
98
 
99
   // concatenate all the arguments in one string and write it to the file
100
   va_start (argptr, fmt);
101
   vfprintf (fp, fmt, argptr);
102
   va_end (argptr);
103
 
104
   // for each row of howmany_columns bytes of data...
105
   for (index = 0; index < data_size; index += howmany_columns)
106
   {
107
      fprintf (fp, "    %05zu  ", index); // print array address of row
108
      for (i = 0; i < howmany_columns; i++)
109
         if (index + i < data_size)
110
            fprintf (fp, " %02X", data[index + i]); // if row contains data, print data as hex bytes
111
         else
112
            fprintf (fp, "   "); // else fill the space with blanks
113
      fprintf (fp, "   ");
114
      for (i = 0; i < howmany_columns; i++)
115
         if (index + i < data_size)
116
            fputc ((data[index + i] >= 32) && (data[index + i] < 127) ? data[index + i] : '.', fp); // now if row contains data, print data as ASCII
117
         else
118
            fputc (' ', fp); // else fill the space with blanks
119
      fputc ('\n', fp);
120
   }
121
 
122
   return; // and return
123
}
124
 
125
 
126
static char *binary (const uint8_t x, char char_for_zero, char char_for_one)
127
{
128
   // returns the binary representation of x as a string
129
 
130
   static thread_local char outstr[9] = "00000000";
131
   for (int i = 0; i < 8; i++)
132
      outstr[i] = (x & (0x80 >> i) ? char_for_one : char_for_zero);
133
   return (outstr);
134
}
135
 
136
 
137
static char *describe_uint8 (const uint8_t x, const char *bitwise_stringdescs[8])
138
{
139
   // returns the ORed description of byte 'x' according to the description strings for each bit
140
 
141
   static char *default_bitstrings[8] = { "bit0", "bit1", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7" };
142
   static thread_local char outstr[8 * 64] = "";
143
 
144
   outstr[0] = 0;
145
   for (int i = 0; i < 8; i++)
146
      if (x & (1 << i))
147
      {
148
         if (outstr[0] != 0)
149
            strcat_s (outstr, sizeof (outstr), "|");
150
         strcat_s (outstr, sizeof (outstr), ((bitwise_stringdescs != NULL) && (*bitwise_stringdescs[i] != 0) ? bitwise_stringdescs[i] : default_bitstrings[i]));
151
      }
152
   return (outstr);
153
}
154
 
155
 
26 pmbaty 156
int dump_ifs_info (const char *ifs_pathname, bool want_everything, bool hide_filename)
16 pmbaty 157
{
158
   #define hex_printf(buf,size,...) do { \
159
      if (want_everything || ((size) <= 16 * 1024)) /* only print when it's not too big (up to 16 kb) */\
160
         hex_fprintf (stdout, (buf), (size), 16, __VA_ARGS__); /* use 16 columns in hex output to stdout */ \
161
      else { \
162
         printf (__VA_ARGS__); \
163
         hex_fprintf (stdout, (buf), 1024, 16, "   first kilobyte:\n"); \
164
      } \
165
   } while (0)
166
   #define BINARY(x) binary ((x), '-', 'x')
167
 
168
   static const char *startupheader_flags1_strings[8] = {
169
      "VIRTUAL", // bit 0
170
      "BIGENDIAN", // bit 1
171
      "COMPRESS_BIT1", // bit 2
172
      "COMPRESS_BIT2", // bit 3
173
      "COMPRESS_BIT3", // bit 4
174
      "TRAILER_V2", // bit 5
175
      "", // bit 6
176
      "", // bit 7
177
   };
178
   static const char *imageheader_flags_strings[8] = {
179
      "BIGENDIAN", // bit 0
180
      "READONLY", // bit 1
181
      "INO_BITS", // bit 2
182
      "SORTED", // bit 3
183
      "TRAILER_V2", // bit 4
184
      "", // bit 5
185
      "", // bit 6
186
      "", // bit 7
187
   };
188
 
189
   startup_header_t *startup_header = NULL;
190
   size_t startupheader_offset = 0;
191
   startup_trailer_v1_t *startup_trailer_v1 = NULL;
192
   startup_trailer_v2_t *startup_trailer_v2 = NULL;
193
   size_t startuptrailer_offset = 0;
194
   image_header_t *image_header = NULL;
195
   size_t imageheader_offset = 0;
196
   image_trailer_v1_t *image_trailer_v1 = NULL;
197
   image_trailer_v2_t *image_trailer_v2 = NULL;
198
   size_t imagetrailer_offset = 0;
199
   fsentry_t **fsentries = NULL; // mallocated
200
   size_t fsentry_count = 0;
201
   fsentry_t *current_fsentry = NULL;
26 pmbaty 202
   buffer_t decompression_dst;
16 pmbaty 203
   char recorded_sha512[2 * SHA512_DIGEST_LENGTH + 1] = "";
204
   char computed_sha512[2 * SHA512_DIGEST_LENGTH + 1] = "";
205
   size_t startupfile_blobsize = 0;
26 pmbaty 206
   size_t compressed_blocksize;
16 pmbaty 207
   void *reallocated_ptr;
208
   bool is_foreign_endianness;
26 pmbaty 209
   uint8_t *decompressor_out;
210
   uint8_t *decompressor_in;
211
   size_t decompressor_outlen;
16 pmbaty 212
   size_t bootfile_blobsize = 0;
213
   size_t current_offset;
214
   size_t fsentry_index;
215
   size_t nearest_distance;
216
   size_t nearest_index;
217
   size_t byte_index;
218
   uint32_t recorded_checksum;
219
   uint32_t computed_checksum;
220
   buffer_t file;
221
   time_t mtime;
26 pmbaty 222
   int cf;
16 pmbaty 223
 
224
   // open and read IFS file
225
   if (!Buffer_ReadFromFile (&file, ifs_pathname))
226
      DIE_WITH_EXITCODE (1, "can't open \"%s\" for reading: %s", ifs_pathname, strerror (errno));
227
 
228
   printf ("QNX In-kernel Filesystem analysis produced by ifstool version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD);
26 pmbaty 229
   if (hide_filename)
230
      printf ("IFS file - size 0x%zx (%zd) bytes\n", file.size, file.size);
231
   else
232
      printf ("IFS file \"%s\" - size 0x%zx (%zd) bytes\n", ifs_pathname, file.size, file.size);
16 pmbaty 233
 
234
   // parse file from start to end
235
   current_offset = 0;
26 pmbaty 236
   cf = STARTUP_HDR_FLAGS1_COMPRESS_NONE;
16 pmbaty 237
   for (;;)
238
   {
239
      // does a startup header start here ?
26 pmbaty 240
      if ((current_offset + sizeof (startup_header_t) < file.size)
241
          && (startup_header == NULL)
242
          && (memcmp (&file.bytes[current_offset], "\xeb\x7e\xff\x00", 4) == 0))
16 pmbaty 243
      {
244
         startupheader_offset = current_offset;
245
         startup_header = (startup_header_t *) &file.bytes[startupheader_offset];
246
 
247
         // layout:
248
         // [STARTUP HEADER]
249
         // (startup file blob)
250
         // [STARTUP TRAILER v1 or v2]
251
 
252
         printf ("\n");
253
         printf ("Startup header at offset 0x%zx (%zd):\n", current_offset, current_offset);
254
         printf ("   signature     = %02x %02x %02x %02x - good\n", startup_header->signature[0], startup_header->signature[1], startup_header->signature[2], startup_header->signature[3]);
255
         printf ("   version       = 0x%04x (%d) - %s\n", startup_header->version, startup_header->version, (startup_header->version == 1 ? "looks good" : "???"));
26 pmbaty 256
         cf = startup_header->flags1 & STARTUP_HDR_FLAGS1_COMPRESS_MASK;
257
         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)")))));
16 pmbaty 258
         printf ("   flags2        = 0x%02x (%s) - %s\n", startup_header->flags2, BINARY (startup_header->flags2), (startup_header->flags2 == 0 ? "looks good" : "???"));
259
         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"));
260
         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")));
261
         printf ("   startup_vaddr = 0x%08x (%d) - virtual address to transfer to after IPL is done\n", startup_header->startup_vaddr, startup_header->startup_vaddr);
262
         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);
263
         printf ("   image_paddr   = 0x%08x (%d) - physical address of image\n", startup_header->image_paddr, startup_header->image_paddr);
264
         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);
265
         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);
266
         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)"));
26 pmbaty 267
         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" : "???"));
16 pmbaty 268
         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"));
269
         printf ("   imagefs_size  = 0x%08x (%d) - size of uncompressed imagefs\n", startup_header->imagefs_size, startup_header->imagefs_size);
270
         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" : "???"));
271
         printf ("   zero0         = 0x%04x (%d) - zeros - %s\n", startup_header->zero0, startup_header->zero0, (startup_header->zero0 == 0 ? "looks good" : "??? should be zero"));
272
         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"));
273
         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"));
274
         hex_printf ((uint8_t *) &startup_header->info[0], sizeof (startup_header->info), "   info[48] =\n");
275
 
276
         // validate that the file can contain up to the startup trailer
277
         if (current_offset + startup_header->startup_size > file.size)
278
         {
279
            LOG_WARNING ("this IFS file is corrupted (startup trailer extends past end of file)");
280
            goto endofdata;
281
         }
282
 
283
         // check if this endianness is ours
284
         if (   ( (startup_header->flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
285
             || (!(startup_header->flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
286
            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
287
         else
288
            is_foreign_endianness = false; // else this header is for the same endianness as us
289
 
290
         // locate the right startup trailer at the right offset
291
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
292
         {
293
            startuptrailer_offset = current_offset + startup_header->startup_size - sizeof (startup_trailer_v2_t);
294
            startup_trailer_v2 = (startup_trailer_v2_t *) &file.bytes[startuptrailer_offset];
295
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v2_t);
296
         }
297
         else // old V1 trailer
298
         {
299
            startuptrailer_offset = current_offset + startup_header->startup_size - sizeof (startup_trailer_v1_t);
300
            startup_trailer_v1 = (startup_trailer_v1_t *) &file.bytes[startuptrailer_offset];
301
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v1_t);
302
         }
303
 
304
         current_offset += sizeof (startup_header_t); // jump over the startup header and reach the startup blob
305
         printf ("\n");
306
         printf ("Startup blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
307
         printf ("   size 0x%zx (%zd) bytes\n", startupfile_blobsize, startupfile_blobsize);
308
         printf ("   checksum 0x%08x\n", update_checksum (&file.bytes[current_offset], startupfile_blobsize, is_foreign_endianness));
309
 
310
         current_offset += startupfile_blobsize; // jump over the startup blob and reach the startup trailer
311
         printf ("\n");
312
         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));
313
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
314
         {
315
            for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
316
               sprintf_s (&recorded_sha512[2 * byte_index], 3, "%02x", startup_trailer_v2->sha512[byte_index]);
317
            strcpy_s (computed_sha512, sizeof (computed_sha512), SHA512 (startup_header, startuptrailer_offset - startupheader_offset, NULL));
318
            recorded_checksum = startup_trailer_v2->cksum;
319
            computed_checksum = update_checksum (startup_header, startuptrailer_offset + SHA512_DIGEST_LENGTH - startupheader_offset, is_foreign_endianness);
320
            printf ("    sha512([0x%zx-0x%zx[) = %s - %s\n", startupheader_offset, startuptrailer_offset, recorded_sha512, (strcasecmp (computed_sha512, recorded_sha512) == 0 ? "GOOD" : "BAD"));
321
            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"));
322
            if (strcasecmp (computed_sha512, recorded_sha512) != 0)
323
               printf ("Computed SHA-512: %s\n", computed_sha512);
324
            if (computed_checksum != recorded_checksum)
325
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
326
         }
327
         else // old v1 trailer
328
         {
329
            recorded_checksum = startup_trailer_v1->cksum;
330
            computed_checksum = update_checksum (startup_header, sizeof (startup_header) + startupfile_blobsize, is_foreign_endianness);
331
            printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", startupheader_offset, startuptrailer_offset, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
332
            if (computed_checksum != recorded_checksum)
333
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
334
         }
335
 
336
         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
337
      }
338
 
339
      // else does an image header start here ?
26 pmbaty 340
      else if ((current_offset + sizeof (image_header_t) < file.size)
341
               && (image_header == NULL)
342
               && (   ((cf == STARTUP_HDR_FLAGS1_COMPRESS_NONE) && (memcmp (&file.bytes[current_offset], "imagefs", 7) == 0))
343
                   || (cf != STARTUP_HDR_FLAGS1_COMPRESS_NONE) && (startup_header->imagefs_size > 0)))
16 pmbaty 344
      {
345
         imageheader_offset = current_offset;
346
         image_header = (image_header_t *) &file.bytes[imageheader_offset];
347
 
26 pmbaty 348
         // should we decompress it ?
349
         if (cf != STARTUP_HDR_FLAGS1_COMPRESS_NONE)
350
         {
351
            // it appears mkifs compresses data in blocks, prefixed by 2-byte block size in BIG ENDIAN
352
            Buffer_InitWithSize (&decompression_dst, startup_header->imagefs_size * 11 / 10); // mallocate and add 10% for safety
353
            decompression_dst.size = 0;
354
 
355
            if (cf == STARTUP_HDR_FLAGS1_COMPRESS_UCL)
356
               ASSERT (ucl_init () == UCL_E_OK, "UCL library initialization failed -- please recompile this tool with less aggressive optimizations");
357
            else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_LZO)
358
               ASSERT (lzo_init () == LZO_E_OK, "LZO library initialization failed -- please recompile this tool with less aggressive optimizations");
359
            else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_ZLIB)
360
            {
361
               LOG_WARNING ("unimplemented compression scheme: zlib (FIXME)");
362
               goto endofdata;
363
            }
364
            else
365
            {
366
               LOG_WARNING ("unsupported compression flags: 0x%2x", cf);
367
               goto endofdata;
368
            }
369
 
370
            // run the compressed payload (the imagefs) through the right decompression algorithm
371
            for (;;)
372
            {
373
               compressed_blocksize = (file.bytes[current_offset + 0] << 8) | (file.bytes[current_offset + 1] << 0); // read block size word (in big engian)
374
               current_offset += 2; // skip it
375
               if (compressed_blocksize == 0)
376
                  break; // a nil block size means end of stream is reached
377
               LOG_DEBUG ("about to decompress block of %zd bytes", compressed_blocksize);
378
               decompressor_in = &file.bytes[current_offset];
379
               decompressor_out = &decompression_dst.bytes[decompression_dst.size];
380
               decompressor_outlen = 0;
381
 
382
               if (cf == STARTUP_HDR_FLAGS1_COMPRESS_UCL)
383
               {
384
                  // UCL compression. NOTE: the decompressor function used in startup-x86 is "ucl_nrv2b_decompress_8 / ucl_nrv2b_decompress_le16 / ucl_nrv2b_decompress_le32"
385
                  static ucl_uint ucl_outlen; // have a different variable because of pointer size mismatch
386
                  if (ucl_nrv2b_decompress_8 (decompressor_in, (ucl_uint) compressed_blocksize, decompressor_out, &ucl_outlen, NULL) != UCL_E_OK)
387
                  {
388
                     LOG_WARNING ("this IFS file is corrupted (UCL decompression failed)");
389
                     goto endofdata;
390
                  }
391
                  decompressor_outlen = ucl_outlen;
392
               }
393
               else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_LZO)
394
               {
395
                  // LZO decompression. NOTE: mkifs uses the full LZO package, whereas I use minilzo.
396
                  static lzo_uint lzo_outlen; // have a different variable because of pointer size mismatch
397
                  if (lzo1x_decompress (decompressor_in, (lzo_uint) compressed_blocksize, decompressor_out, &lzo_outlen, NULL) != LZO_E_OK)
398
                  {
399
                     LOG_WARNING ("this IFS file is corrupted (UCL decompression failed)");
400
                     goto endofdata;
401
                  }
402
                  decompressor_outlen = lzo_outlen;
403
               }
404
               else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_ZLIB)
405
                  ; // TODO
406
 
407
               current_offset += compressed_blocksize;
408
               decompression_dst.size += decompressor_outlen;
409
            }
410
 
411
            LOG_INFO ("decompressed %zd bytes into %zd bytes\n", file.size - imageheader_offset, decompression_dst.size);
412
 
413
            // now place the decompressed buffer in the payload at the imagefs offset
414
            ASSERT_WITH_ERRNO (Buffer_WriteBufferAt (&file, imageheader_offset, &decompression_dst));
415
            current_offset = imageheader_offset; // jump back to where we put the uncompressed data
416
            file.size = imageheader_offset + decompression_dst.size; // update IFS data size
417
 
418
            startup_header = (startup_header_t *) &file.bytes[startupheader_offset]; // fix the pointers that might have changed
419
            if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
420
               startup_trailer_v2 = (startup_trailer_v2_t *) &file.bytes[startuptrailer_offset];
421
            else // old V1 trailer
422
               startup_trailer_v1 = (startup_trailer_v1_t *) &file.bytes[startuptrailer_offset];
423
            image_header = (image_header_t *) &file.bytes[imageheader_offset]; // fix the pointers that might have changed
424
         }
425
 
16 pmbaty 426
         // layout:
427
         // [IMAGE HEADER]
428
         // [image directory entries]
429
         // [smallest file blobs up to KERNEL]
430
         // [padding]
431
         // [KERNEL]
432
         // [rest of file blobs]
433
         // [IMAGE FOOTER]
434
 
435
         printf ("\n");
436
         printf ("Image header at offset %zx (%zd):\n", current_offset, current_offset);
437
         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);
438
         printf ("   flags        = 0x%02x (%s)\n", image_header->flags, describe_uint8 (image_header->flags, imageheader_flags_strings));
439
         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)"));
440
         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)"));
441
         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")));
442
         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]);
443
         printf ("   script_ino   = 0x%08x (%d) - inode of compiled bootscript\n", image_header->script_ino, image_header->script_ino);
444
         printf ("   chain_paddr  = 0x%08x (%d) - offset to next fs signature\n", image_header->chain_paddr, image_header->chain_paddr);
445
         hex_printf ((uint8_t *) &image_header->spare[0], sizeof (image_header->spare), "   spare[10] =\n");
446
         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]));
447
         printf ("   mountpoint   = \"%s\"\n", image_header->mountpoint);
448
 
449
         // validate that the file can contain up to the image trailer
450
         if (current_offset + image_header->image_size > file.size)
451
         {
452
            LOG_WARNING ("this IFS file is corrupted (image trailer extends past end of file)");
453
            goto endofdata;
454
         }
455
 
456
         // check if this endianness is ours
457
         if (   ( (image_header->flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
458
             || (!(image_header->flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
459
            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
460
         else
461
            is_foreign_endianness = false; // else this header is for the same endianness as us
462
 
463
         // locate the image trailer at the right offset
464
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
465
         {
466
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v2_t);
467
            image_trailer_v2 = (image_trailer_v2_t *) &file.bytes[imagetrailer_offset];
468
         }
469
         else // old V1 trailer
470
         {
471
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v1_t);
472
            image_trailer_v1 = (image_trailer_v1_t *) &file.bytes[imagetrailer_offset];
473
         }
474
 
475
         current_offset += sizeof (image_header_t); // jump over the image header and reach the first directory entry
476
 
477
         // there may be padding before the first directory entry
478
         if (image_header->dir_offset - sizeof (image_header_t) > 0)
479
            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);
480
         current_offset += image_header->dir_offset - sizeof (image_header_t); // padding was processed, jump over it
481
 
482
         // dump all directory entries until the last one included
483
         fsentries = NULL;
484
         fsentry_count = 0;
485
         while (current_offset < imageheader_offset + image_header->hdr_dir_size)
486
         {
487
            current_fsentry = (fsentry_t *) &file.bytes[current_offset];
488
 
489
            if (imageheader_offset + image_header->hdr_dir_size - current_offset < sizeof (current_fsentry->header))
490
               break; // end padding reached
491
 
492
            // stack up the filesystem entry pointers in an array while we read them
493
            reallocated_ptr = realloc (fsentries, (fsentry_count + 1) * sizeof (fsentry_t *));
494
            ASSERT_WITH_ERRNO (reallocated_ptr);
495
            fsentries = reallocated_ptr;
496
            fsentries[fsentry_count] = current_fsentry;
497
            fsentry_count++;
498
 
499
            printf ("\n");
500
            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);
501
            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"));
502
            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"));
503
            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" : ""));
504
            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);
505
            printf ("   gid            = 0x%08x (%d) - owner group ID%s\n", current_fsentry->header.gid, current_fsentry->header.gid, (current_fsentry->header.gid == 0 ? " (root)" : ""));
506
            printf ("   uid            = 0x%08x (%d) - owner user ID%s\n", current_fsentry->header.uid, current_fsentry->header.uid, (current_fsentry->header.uid == 0 ? " (root)" : ""));
507
            mtime = (time_t) current_fsentry->header.mtime;
508
            printf ("   mtime          = 0x%08x (%d) - POSIX timestamp: %s", current_fsentry->header.mtime, current_fsentry->header.mtime, asctime (localtime (&mtime))); // NOTE: asctime() provides the newline
509
            if (S_ISDIR (current_fsentry->header.mode))
510
               printf ("   [DIRECTORY] path = \"%s\"\n", (char *) &current_fsentry->u.dir.path); // convert from pointer to char array
511
            else if (S_ISREG (current_fsentry->header.mode))
512
            {
513
               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)"));
514
               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)"));
515
               printf ("   [FILE] path   = \"%s\"\n", (char *) &current_fsentry->u.file.path); // convert from pointer to char array
516
            }
517
            else if (S_ISLNK (current_fsentry->header.mode))
518
            {
519
               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)"));
520
               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)"));
521
               printf ("   [SYMLINK] path       = \"%s\"\n", (char *) &current_fsentry->u.symlink.path); // convert from pointer to char array
522
               printf ("   [SYMLINK] contents   = \"%s\"\n", ((char *) &current_fsentry->u.symlink.path) + current_fsentry->u.symlink.sym_offset); // convert from pointer to char array
523
            }
524
            else // can only be a device
525
            {
526
               printf ("   [DEVICE] dev  = 0x%08x (%d)\n", current_fsentry->u.device.dev, current_fsentry->u.device.dev);
527
               printf ("   [DEVICE] rdev = 0x%08x (%d)\n", current_fsentry->u.device.rdev, current_fsentry->u.device.rdev);
528
               printf ("   [DEVICE] path = \"%s\"\n", (char *) &current_fsentry->u.device.path); // convert from pointer to char array
529
            }
530
 
531
            if ((current_fsentry->header.size == 0) || (current_offset + current_fsentry->header.size >= file.size))
532
            {
533
               LOG_WARNING ("this IFS file is corrupted (the size of this directory entry is invalid)");
534
               goto endofdata;
535
            }
536
 
537
            current_offset += current_fsentry->header.size;
538
         }
539
         if (imageheader_offset + image_header->hdr_dir_size < current_offset + sizeof (current_fsentry->header))
540
            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);
541
         current_offset += imageheader_offset + image_header->hdr_dir_size - current_offset; // padding was processed, jump over it
542
 
543
         // at this point we are past the directory entries; what is stored now, up to and until the image trailer, is the files' data
544
         if (fsentry_count > 0)
545
         {
546
            while (current_offset < imagetrailer_offset) // and parse data up to the trailer
547
            {
548
               nearest_distance = SIZE_MAX;
549
               nearest_index = SIZE_MAX;
550
               for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
551
                  if (S_ISREG (fsentries[fsentry_index]->header.mode) // if this directory entry a file (i.e. it has a data blob)...
552
                      && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset >= current_offset) // ... AND its data blob is still ahead of our current pointer ...
553
                      && (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
554
                  {
555
                     nearest_distance = imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset; // then remember it
556
                     nearest_index = fsentry_index;
557
                  }
558
               if (nearest_index == SIZE_MAX)
559
                  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
560
 
561
               fsentry_index = nearest_index;
562
               current_fsentry = fsentries[fsentry_index]; // quick access to closest fsentry
563
 
564
               // there may be padding before the file data
565
               if (imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset > 0)
566
                  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);
567
               current_offset += imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset; // padding was processed, jump over it
568
 
569
               printf ("\n");
570
               printf ("File data blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
571
               printf ("   corresponding dirent index: %zd/%zd\n", fsentry_index, fsentry_count);
572
               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" : ""));
573
               printf ("   corresponding path: \"%s\"\n", (char *) &current_fsentry->u.file.path); // convert from pointer to char array
574
               printf ("   size 0x%zx (%zd) bytes\n", (size_t) current_fsentry->u.file.size, (size_t) current_fsentry->u.file.size);
575
               if (current_offset + 4 < file.size)
576
                  hex_printf (&file.bytes[current_offset], current_fsentry->u.file.size, "   data:\n");
577
               if (current_offset + current_fsentry->u.file.size < file.size)
578
                  printf ("   checksum %d\n", update_checksum (&file.bytes[current_offset], current_fsentry->u.file.size, is_foreign_endianness));
579
               else
580
               {
581
                  LOG_WARNING ("this IFS file is corrupted (the size of this file data extends past the IFS size)");
582
                  goto endofdata;
583
               }
584
 
585
               current_offset += current_fsentry->u.file.size; // now jump over this file's data
586
            }
587
         }
588
 
589
         // ad this point we're past the last file data, there may be padding before the image trailer
590
         if (imagetrailer_offset - current_offset > 0)
591
            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);
592
         current_offset += imagetrailer_offset - current_offset; // padding was processed, jump over it
593
 
594
         printf ("\n");
595
         printf ("Image trailer at offset 0x%zx (%zd) - version %d:\n", current_offset, current_offset, (image_header->flags & IMAGE_FLAGS_TRAILER_V2 ? 2 : 1));
596
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
597
         {
598
            for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
599
               sprintf_s (&recorded_sha512[2 * byte_index], 3, "%02x", image_trailer_v2->sha512[byte_index]);
600
            strcpy_s (computed_sha512, sizeof (computed_sha512), SHA512 (image_header, imagetrailer_offset - imageheader_offset, NULL));
601
            recorded_checksum = image_trailer_v2->cksum;
602
            computed_checksum = update_checksum (image_header, imagetrailer_offset + SHA512_DIGEST_LENGTH - imageheader_offset, is_foreign_endianness);
603
            printf ("    sha512([0x%zx-0x%zx[) = %s - %s\n", imageheader_offset, imagetrailer_offset, recorded_sha512, (strcasecmp (computed_sha512, recorded_sha512) == 0 ? "GOOD" : "BAD"));
604
            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"));
605
            if (strcasecmp (computed_sha512, recorded_sha512) != 0)
606
               printf ("Computed SHA-512: %s\n", computed_sha512);
607
            if (computed_checksum != recorded_checksum)
608
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
609
         }
610
         else // old v1 trailer
611
         {
612
            recorded_checksum = image_trailer_v1->cksum;
613
            computed_checksum = update_checksum (image_header, image_header->image_size - sizeof (image_trailer_v1_t), is_foreign_endianness);
614
            printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", imageheader_offset, imagetrailer_offset, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
615
            if (computed_checksum != recorded_checksum)
616
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
617
         }
618
 
619
         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)
620
      }
621
 
622
      // 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
623
      else
624
      {
625
         // 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)
626
         for (byte_index = current_offset; byte_index < file.size - 6; byte_index++)
627
            if (memcmp (&file.bytes[byte_index], "\xeb\x7e\xff\x00" "\x01\x00", 4 + 2) == 0)
628
               break; // stop as soon as we find it
629
 
630
         if (byte_index >= file.size - 6)
631
            break; // if not found, stop scanning
632
 
633
         bootfile_blobsize = byte_index - current_offset;
634
         printf ("Boot blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
635
         printf ("   size 0x%zx (%zd) bytes\n", bootfile_blobsize, bootfile_blobsize);
636
         printf ("   checksum 0x%08x\n", update_checksum (&file.bytes[current_offset], bootfile_blobsize, false)); // NOTE: endianness is not known yet -- assume same
637
 
638
         current_offset = byte_index; // now reach the next segment
639
      }
640
   }
641
 
642
endofdata:
643
   // at this point there's nothing left we're able to parse
644
   if (current_offset < file.size)
645
   {
646
      printf ("End of identifiable data reached.\n");
647
      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);
648
   }
649
 
650
   printf ("End of file reached at offset 0x%zx (%zd)\n", file.size, file.size);
651
   printf ("IFS dissecation complete.\n");
652
   return (0);
653
}
654
 
655
 
656
int dump_ifs_contents (const char *ifs_pathname, const char *outdir)
657
{
658
   static char outfile_pathname[MAXPATHLEN] = "";
659
 
660
   startup_header_t *startup_header = NULL;
661
   size_t startupheader_offset = 0;
662
   image_header_t *image_header = NULL;
663
   size_t imageheader_offset = 0;
664
   size_t imagetrailer_offset = 0;
665
   fsentry_t **fsentries = NULL; // mallocated
666
   size_t fsentry_count = 0;
667
   fsentry_t *current_fsentry = NULL;
668
   size_t startupfile_blobsize = 0;
669
   struct utimbuf file_times = { 0, 0 };
670
   void *reallocated_ptr;
671
   size_t bootfile_blobsize = 0;
672
   size_t current_offset;
673
   size_t fsentry_index;
674
   size_t nearest_distance;
675
   size_t nearest_index;
676
   size_t byte_index;
677
   buffer_t file;
678
   FILE *fp;
679
 
680
   // open and read IFS file
681
   if (!Buffer_ReadFromFile (&file, ifs_pathname))
682
      DIE_WITH_EXITCODE (1, "can't open \"%s\" for reading: %s\n", ifs_pathname, strerror (errno));
683
 
684
   // create the output directory
685
   create_intermediate_dirs (outdir);
686
   (void) mkdir (outdir, 0755);
687
 
688
   // parse file from start to end
689
   current_offset = 0;
690
   for (;;)
691
   {
692
      // does a startup header start here ?
693
      if ((current_offset + sizeof (startup_header_t) < file.size) && (memcmp (&file.bytes[current_offset], "\xeb\x7e\xff\x00", 4) == 0))
694
      {
695
         startupheader_offset = current_offset;
696
         startup_header = (startup_header_t *) &file.bytes[startupheader_offset];
697
 
698
         // layout:
699
         // [STARTUP HEADER]
700
         // (startup file blob)
701
         // [STARTUP TRAILER v1 or v2]
702
 
703
         // validate that the file can contain up to the startup trailer
704
         if (current_offset + startup_header->startup_size > file.size)
705
         {
706
            LOG_WARNING ("this IFS file is corrupted (startup trailer extends past end of file)");
707
            goto endofdata;
708
         }
709
 
710
         // locate the right startup trailer at the right offset
711
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
712
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v2_t);
713
         else // old V1 trailer
714
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v1_t);
715
 
716
         current_offset += sizeof (startup_header_t); // jump over the startup header and reach the startup blob
717
 
718
         // write startup blob
719
         sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/startup.bin", outdir);
720
         fopen_s (&fp, outfile_pathname, "wb");
721
         ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno));
722
         fwrite (&file.bytes[current_offset], 1, startupfile_blobsize, fp);
723
         fclose (fp);
724
 
725
         current_offset += startupfile_blobsize; // jump over the startup blob and reach the startup trailer
726
         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
727
      }
728
 
729
      // else does an image header start here ?
730
      else if ((current_offset + sizeof (image_header_t) < file.size) && (memcmp (&file.bytes[current_offset], "imagefs", 7) == 0))
731
      {
732
         imageheader_offset = current_offset;
733
         image_header = (image_header_t *) &file.bytes[imageheader_offset];
734
 
735
         // layout:
736
         // [IMAGE HEADER]
737
         // [image directory entries]
738
         // [smallest file blobs up to KERNEL]
739
         // [padding]
740
         // [KERNEL]
741
         // [rest of file blobs]
742
         // [IMAGE FOOTER]
743
 
744
         // validate that the file can contain up to the image trailer
745
         if (current_offset + image_header->image_size > file.size)
746
         {
747
            LOG_WARNING ("this IFS file is corrupted (image trailer extends past end of file)");
748
            goto endofdata;
749
         }
750
 
751
         // locate the image trailer at the right offset
752
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
753
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v2_t);
754
         else // old V1 trailer
755
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v1_t);
756
 
757
         current_offset += sizeof (image_header_t); // jump over the image header
758
         current_offset += image_header->dir_offset - sizeof (image_header_t); // jump over possible padding
759
 
760
         // dump all directory entries until the last one included
761
         fsentries = NULL;
762
         fsentry_count = 0;
763
         while (current_offset < imageheader_offset + image_header->hdr_dir_size)
764
         {
765
            current_fsentry = (fsentry_t *) &file.bytes[current_offset];
766
 
767
            if (imageheader_offset + image_header->hdr_dir_size - current_offset < sizeof (current_fsentry->header))
768
               break; // end padding reached
769
 
770
            // stack up the filesystem entry pointers in an array while we read them
771
            reallocated_ptr = realloc (fsentries, (fsentry_count + 1) * sizeof (fsentry_t *));
772
            ASSERT_WITH_ERRNO (reallocated_ptr);
773
            fsentries = reallocated_ptr;
774
            fsentries[fsentry_count] = current_fsentry;
775
            fsentry_count++;
776
 
777
            if ((current_fsentry->header.size == 0) || (current_offset + current_fsentry->header.size >= file.size))
778
            {
779
               LOG_WARNING ("this IFS file is corrupted (the size of this directory entry is invalid)");
780
               goto endofdata;
781
            }
782
 
783
            current_offset += current_fsentry->header.size;
784
         }
785
         current_offset += imageheader_offset + image_header->hdr_dir_size - current_offset; // jump over possible padding
786
 
787
         // at this point we are past the directory entries; what is stored now, up to and until the image trailer, is the files' data
788
         if (fsentry_count > 0)
789
         {
790
            while (current_offset < imagetrailer_offset) // and parse data up to the trailer
791
            {
792
               nearest_distance = SIZE_MAX;
793
               nearest_index = SIZE_MAX;
794
               for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
795
                  if (S_ISREG (fsentries[fsentry_index]->header.mode) // if this directory entry a file (i.e. it has a data blob)...
796
                      && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset >= current_offset) // ... AND its data blob is still ahead of our current pointer ...
797
                      && (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
798
                  {
799
                     nearest_distance = imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset; // then remember it
800
                     nearest_index = fsentry_index;
801
                  }
802
               if (nearest_index == SIZE_MAX)
803
                  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
804
 
805
               fsentry_index = nearest_index;
806
               current_fsentry = fsentries[fsentry_index]; // quick access to closest fsentry
807
 
808
               current_offset += imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset; // jump over possible padding
809
 
810
               if (current_offset + current_fsentry->u.file.size >= file.size)
811
               {
812
                  LOG_WARNING ("this IFS file is corrupted (the size of this file data extends past the IFS size)");
813
                  goto endofdata;
814
               }
815
 
816
               // write filesystem data entry
817
               if (S_ISDIR (current_fsentry->header.mode))
818
               {
819
                  sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) &current_fsentry->u.dir.path); // convert from pointer to char array
820
                  create_intermediate_dirs (outfile_pathname);
821
                  (void) mkdir (outfile_pathname, current_fsentry->header.mode & 0777);
822
               }
823
               else if (S_ISLNK (current_fsentry->header.mode))
824
               {
825
                  sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) &current_fsentry->u.symlink.path); // convert from pointer to char array
826
                  create_intermediate_dirs (outfile_pathname);
827
#ifdef _WIN32
828
                  fopen_s (&fp, outfile_pathname, "wb"); // on Windows create symlinks as plain files
829
                  ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno));
830
                  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
831
                  fclose (fp);
832
#else // !_WIN32, thus POSIX
833
                  symlink (current_fsentry->u.symlink.contents, outfile_pathname); // on UNIX systems, just create the symlink for real
834
#endif // _WIN32
835
               }
836
               else if (S_ISREG (current_fsentry->header.mode))
837
               {
838
                  sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) &current_fsentry->u.file.path); // convert from pointer to char array
839
                  create_intermediate_dirs (outfile_pathname);
840
                  fopen_s (&fp, outfile_pathname, "wb"); // on Windows create symlinks as plain files
841
                  ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno));
842
                  fwrite (&file.bytes[current_offset], 1, current_fsentry->u.file.size, fp);
843
                  fclose (fp);
844
               }
845
               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
846
               {
847
                  sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) &current_fsentry->u.device.path); // convert from pointer to char array
848
                  create_intermediate_dirs (outfile_pathname);
849
                  fopen_s (&fp, outfile_pathname, "wb"); // on Windows create symlinks as plain files
850
                  ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno));
851
                  fprintf (fp, "%u:%u", current_fsentry->u.device.dev, current_fsentry->u.device.rdev);
852
                  fclose (fp);
853
               }
854
 
855
               // set created file mtime
856
               file_times.actime = current_fsentry->header.mtime;
857
               file_times.modtime = current_fsentry->header.mtime;
858
               utime (outfile_pathname, &file_times);
859
 
860
               // set created file mode
861
#ifndef _WIN32
862
               (void) chmod (outfile_pathname, current_fsentry->header.mode & 0777); // only on POSIX systems
863
#endif // !_WIN32
864
 
865
               current_offset += current_fsentry->u.file.size; // now jump over this file's data
866
            }
867
         }
868
 
869
         // ad this point we're past the last file data, there may be padding before the image trailer
870
         current_offset += imagetrailer_offset - current_offset; // jump over possible padding and reach the image trailer
871
         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)
872
      }
873
 
874
      // 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
875
      else
876
      {
877
         // 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)
878
         for (byte_index = current_offset; byte_index < file.size - 6; byte_index++)
879
            if (memcmp (&file.bytes[byte_index], "\xeb\x7e\xff\x00" "\x01\x00", 4 + 2) == 0)
880
               break; // stop as soon as we find it
881
 
882
         if (byte_index >= file.size - 6)
883
            break; // if not found, stop scanning
884
 
885
         bootfile_blobsize = byte_index - current_offset;
886
 
887
         // write boot blob
888
         sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/boot.bin", outdir);
889
         fopen_s (&fp, outfile_pathname, "wb");
890
         ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno));
891
         fwrite (&file.bytes[current_offset], 1, bootfile_blobsize, fp);
892
         fclose (fp);
893
 
894
         current_offset = byte_index; // now reach the next segment
895
      }
896
   }
897
 
898
endofdata:
899
   return (0);
900
}
901
 
902
 
903
int dump_file_hex (const char *pathname)
904
{
905
   buffer_t file;
906
 
907
   ASSERT_WITH_ERRNO (Buffer_ReadFromFile (&file, pathname));
908
   hex_fprintf (stdout, file.bytes, file.size, 16, "%s (%zd bytes):\n", pathname, file.size);
909
   return (0);
910
}