// 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 *) ¤t_fsentry
->u.
dir.
path); // convert from pointer to char array
else if (S_ISREG (current_fsentry->header.mode))
{
printf (" [FILE] offset = 0x%08x (%d) - %s\n", current_fsentry
->u.
file.
offset, current_fsentry
->u.
file.
offset, (imageheader_offset
+ current_fsentry
->u.
file.
offset < file.
size ? "looks good" : "BAD (IFS file too short)"));
printf (" [FILE] size = 0x%08x (%d) - %s\n", current_fsentry
->u.
file.
size, current_fsentry
->u.
file.
size, (imageheader_offset
+ current_fsentry
->u.
file.
offset + current_fsentry
->u.
file.
size < file.
size ? "looks good" : "BAD (IFS file too short)"));
printf (" [FILE] path = \"%s\"\n", (char *) ¤t_fsentry
->u.
file.
path); // convert from pointer to char array
}
else if (S_ISLNK (current_fsentry->header.mode))
{
printf (" [SYMLINK] sym_offset = 0x%04x (%d) - %s\n", current_fsentry
->u.
symlink.
sym_offset, current_fsentry
->u.
symlink.
sym_offset, (sizeof (current_fsentry
->header
) + 2 * sizeof (uint16_t) + current_fsentry
->u.
symlink.
sym_offset <= current_fsentry
->header.
size ? "looks good" : "BAD (dirent too short)"));
printf (" [SYMLINK] sym_size = 0x%04x (%d) - %s\n", current_fsentry
->u.
symlink.
sym_size, current_fsentry
->u.
symlink.
sym_size, (sizeof (current_fsentry
->header
) + 2 * sizeof (uint16_t) + current_fsentry
->u.
symlink.
sym_offset + current_fsentry
->u.
symlink.
sym_size <= current_fsentry
->header.
size ? "looks good" : "BAD (dirent too short)"));
printf (" [SYMLINK] path = \"%s\"\n", (char *) ¤t_fsentry
->u.
symlink.
path); // convert from pointer to char array
printf (" [SYMLINK] contents = \"%s\"\n", ((char *) ¤t_fsentry
->u.
symlink.
path) + current_fsentry
->u.
symlink.
sym_offset); // convert from pointer to char array
}
else // can only be a device
{
printf (" [DEVICE] dev = 0x%08x (%d)\n", current_fsentry
->u.
device.
dev, current_fsentry
->u.
device.
dev);
printf (" [DEVICE] rdev = 0x%08x (%d)\n", current_fsentry
->u.
device.
rdev, current_fsentry
->u.
device.
rdev);
printf (" [DEVICE] path = \"%s\"\n", (char *) ¤t_fsentry
->u.
device.
path); // convert from pointer to char array
}
if ((current_fsentry->header.size == 0) || (current_offset + current_fsentry->header.size >= file.size))
{
LOG_WARNING ("this IFS file is corrupted (the size of this directory entry is invalid)");
goto endofdata;
}
current_offset += current_fsentry->header.size;
}
if (imageheader_offset + image_header->hdr_dir_size < current_offset + sizeof (current_fsentry->header))
hex_printf (&file.bytes[current_offset], imageheader_offset + image_header->hdr_dir_size - current_offset, "\n" "%zd padding bytes at offset 0x%zx (%zd):\n", imageheader_offset + image_header->hdr_dir_size - current_offset, current_offset, current_offset);
current_offset += imageheader_offset + image_header->hdr_dir_size - current_offset; // padding was processed, jump over it
// at this point we are past the directory entries; what is stored now, up to and until the image trailer, is the files' data
if (fsentry_count > 0)
{
while (current_offset < imagetrailer_offset) // and parse data up to the trailer
{
nearest_distance = SIZE_MAX;
nearest_index = SIZE_MAX;
for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
if (S_ISREG (fsentries[fsentry_index]->header.mode) // if this directory entry a file (i.e. it has a data blob)...
&& (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset >= current_offset) // ... AND its data blob is still ahead of our current pointer ...
&& (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset < nearest_distance)) // ... AND it's the closest to us we've found so far
{
nearest_distance = imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset; // then remember it
nearest_index = fsentry_index;
}
if (nearest_index == SIZE_MAX)
break; // found no file ahead, which means we've parsed the whole file data area, so stop the loop so as to proceed to the image trailer
fsentry_index = nearest_index;
current_fsentry = fsentries[fsentry_index]; // quick access to closest fsentry
// there may be padding before the file data
if (imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset > 0)
hex_printf (&file.bytes[current_offset], imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset, "\n" "%zd padding bytes at offset 0x%zx (%zd):\n", imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset, current_offset, current_offset);
current_offset += imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset; // padding was processed, jump over it
printf ("File data blob at offset 0x%zx (%zd):\n", current_offset
, current_offset
);
printf (" corresponding dirent index: %zd/%zd\n", fsentry_index
, fsentry_count
);
printf (" corresponding inode 0x%08x (%d) -%s%s%s%s\n", current_fsentry
->header.
ino, current_fsentry
->header.
ino, (current_fsentry
->header.
ino & 0xE0000000 ? "" : " nothing special"), (current_fsentry
->header.
ino & IFS_INO_PROCESSED_ELF
? " PROCESSED_ELF" : ""), (current_fsentry
->header.
ino & IFS_INO_RUNONCE_ELF
? " RUNONCE_ELF" : ""), (current_fsentry
->header.
ino & IFS_INO_BOOTSTRAP_EXE
? " BOOTSTRAP_EXE" : ""));
printf (" corresponding path: \"%s\"\n", (char *) ¤t_fsentry
->u.
file.
path); // convert from pointer to char array
printf (" size 0x%zx (%zd) bytes\n", (size_t) current_fsentry
->u.
file.
size, (size_t) current_fsentry
->u.
file.
size);
if (current_offset + 4 < file.size)
hex_printf (&file.bytes[current_offset], current_fsentry->u.file.size, " data:\n");
if (current_offset + current_fsentry->u.file.size < file.size)
printf (" checksum %d\n", update_checksum
(&file.
bytes[current_offset
], current_fsentry
->u.
file.
size, is_foreign_endianness
));
else
{
LOG_WARNING ("this IFS file is corrupted (the size of this file data extends past the IFS size)");
goto endofdata;
}
current_offset += current_fsentry->u.file.size; // now jump over this file's data
}
}
// ad this point we're past the last file data, there may be padding before the image trailer
if (imagetrailer_offset - current_offset > 0)
hex_printf (&file.bytes[current_offset], imagetrailer_offset - current_offset, "\n" "%zd padding bytes at offset %zx (%zd):\n", imagetrailer_offset - current_offset, current_offset, current_offset);
current_offset += imagetrailer_offset - current_offset; // padding was processed, jump over it
printf ("Image trailer at offset 0x%zx (%zd) - version %d:\n", current_offset
, current_offset
, (image_header
->flags
& IMAGE_FLAGS_TRAILER_V2
? 2 : 1));
if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
{
for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
sprintf_s (&recorded_sha512[2 * byte_index], 3, "%02x", image_trailer_v2->sha512[byte_index]);
strcpy_s (computed_sha512, sizeof (computed_sha512), 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;
size_t startupfile_blobsize = 0;
struct utimbuf file_times = { 0, 0 };
void *reallocated_ptr;
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;
// 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) && (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;
}
// 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) && (memcmp (&file.
bytes[current_offset
], "imagefs", 7) == 0))
{
imageheader_offset = current_offset;
image_header = (image_header_t *) &file.bytes[imageheader_offset];
// layout:
// [IMAGE HEADER]
// [image directory entries]
// [smallest file blobs up to KERNEL]
// [padding]
// [KERNEL]
// [rest of file blobs]
// [IMAGE FOOTER]
// validate that the file can contain up to the image trailer
if (current_offset + image_header->image_size > file.size)
{
LOG_WARNING ("this IFS file is corrupted (image trailer extends past end of file)");
goto endofdata;
}
// locate the image trailer at the right offset
if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v2_t);
else // old V1 trailer
imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v1_t);
current_offset += sizeof (image_header_t); // jump over the image header
current_offset += image_header->dir_offset - sizeof (image_header_t); // jump over possible padding
// dump all directory entries until the last one included
fsentries = NULL;
fsentry_count = 0;
while (current_offset < imageheader_offset + image_header->hdr_dir_size)
{
current_fsentry = (fsentry_t *) &file.bytes[current_offset];
if (imageheader_offset + image_header->hdr_dir_size - current_offset < sizeof (current_fsentry->header))
break; // end padding reached
// stack up the filesystem entry pointers in an array while we read them
reallocated_ptr
= realloc (fsentries
, (fsentry_count
+ 1) * sizeof (fsentry_t
*));
ASSERT_WITH_ERRNO (reallocated_ptr);
fsentries = reallocated_ptr;
fsentries[fsentry_count] = current_fsentry;
fsentry_count++;
if ((current_fsentry->header.size == 0) || (current_offset + current_fsentry->header.size >= file.size))
{
LOG_WARNING ("this IFS file is corrupted (the size of this directory entry is invalid)");
goto endofdata;
}
current_offset += current_fsentry->header.size;
}
current_offset += imageheader_offset + image_header->hdr_dir_size - current_offset; // jump over possible padding
// at this point we are past the directory entries; what is stored now, up to and until the image trailer, is the files' data
if (fsentry_count > 0)
{
while (current_offset < imagetrailer_offset) // and parse data up to the trailer
{
nearest_distance = SIZE_MAX;
nearest_index = SIZE_MAX;
for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
if (S_ISREG (fsentries[fsentry_index]->header.mode) // if this directory entry a file (i.e. it has a data blob)...
&& (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset >= current_offset) // ... AND its data blob is still ahead of our current pointer ...
&& (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset < nearest_distance)) // ... AND it's the closest to us we've found so far
{
nearest_distance = imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset; // then remember it
nearest_index = fsentry_index;
}
if (nearest_index == SIZE_MAX)
break; // found no file ahead, which means we've parsed the whole file data area, so stop the loop so as to proceed to the image trailer
fsentry_index = nearest_index;
current_fsentry = fsentries[fsentry_index]; // quick access to closest fsentry
current_offset += imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset; // jump over possible padding
if (current_offset + current_fsentry->u.file.size >= file.size)
{
LOG_WARNING ("this IFS file is corrupted (the size of this file data extends past the IFS size)");
goto endofdata;
}
// write filesystem data entry
if (S_ISDIR (current_fsentry->header.mode))
{
sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) ¤t_fsentry->u.dir.path); // convert from pointer to char array
create_intermediate_dirs (outfile_pathname);
(void) mkdir (outfile_pathname, current_fsentry->header.mode & 0777);
}
else if (S_ISLNK (current_fsentry->header.mode))
{
sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) ¤t_fsentry->u.symlink.path); // convert from pointer to char array
create_intermediate_dirs (outfile_pathname);
#ifdef _WIN32
fopen_s (&fp, outfile_pathname, "wb"); // on Windows create symlinks as plain files
ASSERT
(fp
, "failed to open '%s': %s", outfile_pathname
, strerror (errno
));
fwrite ((char *) ¤t_fsentry
->u.
symlink.
path + current_fsentry
->u.
symlink.
sym_offset, 1, current_fsentry
->u.
symlink.
sym_size, fp
); // convert from pointer to char array
#else // !_WIN32, thus POSIX
symlink (current_fsentry->u.symlink.contents, outfile_pathname); // on UNIX systems, just create the symlink for real
#endif // _WIN32
}
else if (S_ISREG (current_fsentry->header.mode))
{
sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) ¤t_fsentry->u.file.path); // convert from pointer to char array
create_intermediate_dirs (outfile_pathname);
fopen_s (&fp, outfile_pathname, "wb"); // on Windows create symlinks as plain files
ASSERT
(fp
, "failed to open '%s': %s", outfile_pathname
, strerror (errno
));
fwrite (&file.
bytes[current_offset
], 1, current_fsentry
->u.
file.
size, fp
);
}
else // must be a device node. Since we might not be the super-user and/or on Win32, create plain file with "X:Y" as data
{
sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) ¤t_fsentry->u.device.path); // convert from pointer to char array
create_intermediate_dirs (outfile_pathname);
fopen_s (&fp, outfile_pathname, "wb"); // on Windows create symlinks as plain files
ASSERT
(fp
, "failed to open '%s': %s", outfile_pathname
, strerror (errno
));
fprintf (fp
, "%u:%u", current_fsentry
->u.
device.
dev, current_fsentry
->u.
device.
rdev);
}
// set created file mtime
file_times.actime = current_fsentry->header.mtime;
file_times.modtime = current_fsentry->header.mtime;
utime (outfile_pathname, &file_times);
// set created file mode
#ifndef _WIN32
(void) chmod (outfile_pathname, current_fsentry->header.mode & 0777); // only on POSIX systems
#endif // !_WIN32
current_offset += current_fsentry->u.file.size; // now jump over this file's data
}
}
// ad this point we're past the last file data, there may be padding before the image trailer
current_offset += imagetrailer_offset - current_offset; // jump over possible padding and reach the image trailer
current_offset += (image_header->flags & IMAGE_FLAGS_TRAILER_V2 ? sizeof (image_trailer_v2_t) : sizeof (image_trailer_v1_t)); // now jump over the image trailer and reach the next segment (typically end of file)
}
// else it has to be a boot blob, of which we don't know the size, except that it has to fit in 0xffff bytes and be immediately followed by a startup header
else
{
// 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);
}