Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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

Rev Author Line No. Line
1 pmbaty 1
// ifstool.c -- portable reimplementation of QNX's mkifs by Pierre-Marie Baty <pm@pmbaty.com>
2
 
3
#include <stdint.h>
4
#include <stdbool.h>
5
#include <stdlib.h>
2 pmbaty 6
#include <stdarg.h>
1 pmbaty 7
#include <stdio.h>
8
#include <string.h>
9
#include <errno.h>
10
#include <sys/stat.h>
11
#include <ctype.h>
12
#include <time.h>
13
 
14
 
15
#ifdef _MSC_VER
16
#include <io.h>
17
#define __ORDER_LITTLE_ENDIAN__ 1234
18
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
19
#define __attribute__(x)
20
#define __builtin_bswap64(x) _byteswap_uint64 ((unsigned long long) (x))
21
#define S_IFIFO 0x1000
22
#define S_IFLNK 0xa000
23
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
24
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
25
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
26
#define strdup(s) _strdup ((s))
2 pmbaty 27
#define strcasecmp(s1,s2) _stricmp ((s1), (s2))
1 pmbaty 28
#define fseek(fp,off,m) _fseeki64 ((fp), (off), (m))
29
#define access(p,m) _access ((p), (m))
30
#define MAXPATHLEN 1024
31
#else // !_MSC_VER
32
#include <sys/param.h>
33
#include <unistd.h>
34
#endif // _MSC_VER
35
 
36
#ifdef _MSC_VER
37
#pragma pack(push)
38
#pragma pack(1)
39
#endif // _MSC_VER
40
 
41
 
42
#define ROUND_TO_UPPER_MULTIPLE(val,multiple) ((((val) + (size_t) (multiple) - 1) / (multiple)) * (multiple)) // note that val is being evaluated once, so it can be the result of a function call
43
#ifdef _WIN32
44
#define IS_DIRSEP(c) (((c) == '/') || ((c) == '\\'))
45
#define PATH_SEP ';'
46
#else // !_WIN32, thus POSIX
47
#define IS_DIRSEP(c) ((c) == '/')
48
#define PATH_SEP ':'
49
#endif // _WIN32
50
#define RECORD_SEP '\x1e' // ASCII record separator
51
#define RECORD_SEP_STR "\x1e" // ASCII record separator (as string)
52
 
53
#define WILL_BE_FILLED_LATER 0xbaadf00d
54
 
55
 
56
// bitmapped flags used in the flags1 member of the startup header
57
#define STARTUP_HDR_FLAGS1_VIRTUAL        (1 << 0)
58
#define STARTUP_HDR_FLAGS1_BIGENDIAN      (1 << 1)
59
//#define STARTUP_HDR_FLAGS1_COMPRESS_MASK  0x1c
60
//#define STARTUP_HDR_FLAGS1_COMPRESS_SHIFT 0x02
61
//#define STARTUP_HDR_FLAGS1_COMPRESS_NONE  0x00
62
//#define STARTUP_HDR_FLAGS1_COMPRESS_ZLIB  0x04
63
//#define STARTUP_HDR_FLAGS1_COMPRESS_LZO   0x08
64
//#define STARTUP_HDR_FLAGS1_COMPRESS_UCL   0x0c
65
#define STARTUP_HDR_FLAGS1_TRAILER_V2     (1 << 5) // if set, then a struct startup_trailer_v2 follows the startup. If the image is compressed, then the compressed imagefs is followed by a struct image_trailer_v2
66
 
67
 
68
#define STARTUP_HDR_MACHINE_X86_64  0x3e
69
#define STARTUP_HDR_MACHINE_AARCH64 0xb7
70
 
71
 
72
// bitmapped flags used in the flags member of the image header
73
#define IMAGE_FLAGS_BIGENDIAN  (1 << 0) // header, trailer, dirents in big-endian format
74
#define IMAGE_FLAGS_READONLY   (1 << 1) // do not try to write to image (rom/flash)
75
#define IMAGE_FLAGS_INO_BITS   (1 << 2) // inode bits valid
76
#define IMAGE_FLAGS_SORTED     (1 << 3) // dirent section is sorted (by pathname)
77
#define IMAGE_FLAGS_TRAILER_V2 (1 << 4) // image uses struct image_trailer_v2
78
 
79
 
80
// bitmapped flags superposed to a filesystem entry's inode number
81
#define IFS_INO_PROCESSED_ELF 0x80000000
82
#define IFS_INO_RUNONCE_ELF   0x40000000
83
#define IFS_INO_BOOTSTRAP_EXE 0x20000000
84
 
85
 
86
// SHA-512 block and digest sizes
87
#define SHA512_BLOCK_LENGTH 128 // in bytes
88
#define SHA512_DIGEST_LENGTH 64 // in bytes
89
 
90
 
91
// SHA-512 computation context structure type definition
92
typedef struct sha512_ctx_s
93
{
94
   uint64_t state[8];
95
   uint64_t bitcount[2];
96
   uint8_t buffer[SHA512_BLOCK_LENGTH];
97
} SHA512_CTX;
98
 
99
 
100
#if 0 // TODO: startup script compiler. Someday.
101
#define SCRIPT_FLAGS_EXTSCHED   0x01
102
#define SCRIPT_FLAGS_SESSION    0x02
103
#define SCRIPT_FLAGS_SCHED_SET  0x04
104
#define SCRIPT_FLAGS_CPU_SET    0x08
105
#define SCRIPT_FLAGS_BACKGROUND 0x20
106
#define SCRIPT_FLAGS_KDEBUG     0x40
107
 
108
#define SCRIPT_POLICY_NOCHANGE 0
109
#define SCRIPT_POLICY_FIFO     1
110
#define SCRIPT_POLICY_RR       2
111
#define SCRIPT_POLICY_OTHER    3
112
 
113
#define SCRIPT_TYPE_EXTERNAL        0
114
#define SCRIPT_TYPE_WAITFOR         1
115
#define SCRIPT_TYPE_REOPEN          2
116
#define SCRIPT_TYPE_DISPLAY_MSG     3
117
#define SCRIPT_TYPE_PROCMGR_SYMLINK 4
118
#define SCRIPT_TYPE_EXTSCHED_APS    5
119
 
120
#define SCRIPT_CHECKS_MS 100
121
 
122
#define SCRIPT_SCHED_EXT_NONE 0
123
#define SCRIPT_SCHED_EXT_APS  1
124
 
125
#define SCRIPT_APS_SYSTEM_PARTITION_ID   0
126
#define SCRIPT_APS_SYSTEM_PARTITION_NAME "System"
127
#define SCRIPT_APS_PARTITION_NAME_LENGTH 15
128
#define SCRIPT_APS_MAX_PARTITIONS        8
129
 
130
 
131
typedef struct __attribute__((packed)) bootscriptcmd_header_s
132
{
133
   uint16_t size; // size of cmd entry
134
   uint8_t type;
135
   uint8_t spare;
136
} bootscriptcmd_header_t;
137
 
138
 
139
typedef union bootscriptcmd_s
140
{
141
   struct __attribute__((packed)) script_external
142
   {
143
      bootscriptcmd_header_t hdr;
144
      uint8_t cpu; // CPU (turn into runmask)
145
      uint8_t flags;
146
      union script_external_extsched
147
      {
148
         uint8_t reserved[2];
149
         struct __attribute__((packed))
150
         {
151
            uint8_t id;
152
            uint8_t reserved[1];
153
         } aps;
154
      } extsched; // extended scheduler
155
      uint8_t policy; // POLICY_FIFO, POLICY_RR, ...
156
      uint8_t priority; // priority to run cmd at
157
      uint8_t argc; // # of args
158
      uint8_t envc; // # of environment entries
159
      char args[0]; // executable, argv, envp (null padded to 32-bit align)
160
   } external;
161
   struct __attribute__((packed)) script_waitfor_reopen
162
   {
163
      bootscriptcmd_header_t hdr;
164
      uint16_t checks;
165
      char fname[0]; // char fname[] (null padded to 32-bit align)
166
   } waitfor_reopen;
167
   struct __attribute__((packed)) script_display_msg
168
   {
169
      bootscriptcmd_header_t hdr;
170
      char msg[0]; // char msg[] (null padded to 32-bit align)
171
   } display_msg;
172
   struct __attribute__((packed)) script_procmgr_symlink
173
   {
174
      bootscriptcmd_header_t hdr;
175
      char src_dest[0]; // <src_name>, '\0', <dest_name> '\0' (null padded to 32-bit align)
176
   } procmgr_symlink;
177
   struct __attribute__((packed)) script_extsched_aps
178
   {
179
      bootscriptcmd_header_t hdr;
180
      uint8_t parent;
181
      uint8_t budget;
182
      uint16_t critical;
183
      uint8_t id;
184
      char pname[0]; // char pname[] (null padded to 32-bit align)
185
   } extsched_aps;
186
} bootscriptcmd_t;
187
#endif // 0
188
 
189
 
190
#define INITIAL_STARTUP_SCRIPT \
191
   /* procmgr_symlink /proc/boot/ldqnx-64.so.2 /usr/lib/ldqnx-64.so.2 */ \
192
   "\x34\x00" /*size*/ "\x04" /*type*/ "\x00" /*spare*/ "/proc/boot/ldqnx-64.so\0" "/usr/lib/ldqnx-64.so.2\0" \
193
   /* sh /proc/boot/startup.sh */ \
194
   "\x88\x00" /*size*/ "\x00" /*type*/ "\x00" /*spare*/ "\x00" /*CPU mask*/ "\x00" /*flags*/ "\x00\x00" /*reserved*/ "\x00" /*policy*/ "\x00" /*priority*/ "\02" /*argc*/ "\x02" /*envc*/ "sh\0" /*executable*/ "sh\0" "/proc/boot/startup.sh\0" /*argv*/ "PATH=/sbin:/usr/sbin:/bin:/usr/bin:/proc/boot\0" "LD_LIBRARY_PATH=/proc/boot:/lib:/lib/dll:/usr/lib\0" /*envp*/ \
195
   /* display_msg "Startup complete */ \
196
   "\x18\x00" /*size*/ "\x03" /*type*/ "\x00" /*spare*/ "Startup complete\n\0" "\x00\00" /*padding*/ \
197
   /* trailer */ \
198
   "\x00\x00\x00\x00"
199
 
200
 
2 pmbaty 201
typedef struct __attribute__((packed)) fsentry_s
1 pmbaty 202
{
203
   struct __attribute__((packed)) fsentry_header_s
204
   {
205
      uint16_t size; // size of dirent
206
      uint16_t extattr_offset; // if zero, no extattr data
207
      uint32_t ino; // if zero, skip entry
208
      uint32_t mode; // mode and perms of entry
209
      uint32_t gid;
210
      uint32_t uid;
211
      uint32_t mtime;
212
   } header;
2 pmbaty 213
   union __attribute__((packed)) fsentry_specific_u
1 pmbaty 214
   {
2 pmbaty 215
      struct __attribute__((packed)) fsentry_file_s // when (mode & S_IFMT) == S_IFREG
1 pmbaty 216
      {
217
         uint32_t offset; // offset from header
218
         uint32_t size;
219
         char *path; // null terminated path (no leading slash)
220
         char *UNSAVED_databuf; // file data blob buffer (NOT SAVED IN THE IFS)
221
      } file;
2 pmbaty 222
      struct __attribute__((packed)) fsentry_dir_s // when (mode & S_IFMT) == S_IFDIR
1 pmbaty 223
      {
224
         char *path; // null terminated path (no leading slash)
225
      } dir;
2 pmbaty 226
      struct __attribute__((packed)) fsentry_symlink_s // when (mode & S_IFMT) == S_IFLNK
1 pmbaty 227
      {
228
         uint16_t sym_offset; // offset to 'contents' from 'path'
229
         uint16_t sym_size; // strlen (contents)
230
         char *path; // null terminated path (no leading slash)
231
         char *contents; // null terminated symlink contents
232
      } symlink;
2 pmbaty 233
      struct __attribute__((packed)) fsentry_device_s // when (mode & S_IFMT) == S_IF<CHR|BLK|FIFO|NAM|SOCK>
1 pmbaty 234
      {
235
         uint32_t dev;
236
         uint32_t rdev;
237
         char *path; // null terminated path (no leading slash)
238
      } device;
239
   } u;
240
   bool UNSAVED_was_data_written; // whether this entry's data was written to the image (NOT SAVED IN THE IFS)
241
} fsentry_t;
242
 
243
 
244
typedef struct __attribute__((packed)) startup_header_s // size 256 bytes
245
{
246
                           // I - used by the QNX IPL
247
                           // S - used by the startup program
248
   uint8_t signature[4];   // [I ] Header signature, "\xeb\x7e\xff\x00"
249
   uint16_t version;       // [I ] Header version, i.e. 1
250
   uint8_t flags1;         // [IS] Misc flags, 0x21 (= 0x20 | STARTUP_HDR_FLAGS1_VIRTUAL)
251
   uint8_t flags2;         // [  ] No flags defined yet (0)
252
   uint16_t header_size;   // [ S] sizeof(struct startup_header), i.e. 256
253
   uint16_t machine;       // [IS] Machine type from elfdefinitions.h, i.e. 0x003E --> _ELF_DEFINE_EM(EM_X86_64, 62, "AMD x86-64 architecture")
254
   uint32_t startup_vaddr; // [I ] Virtual Address to transfer to after IPL is done, here 0x01403008 (appears in "Entry" column for "startup.*")
255
   uint32_t paddr_bias;    // [ S] Value to add to physical address to get a value to put into a pointer and indirected through, here 0 (no indirections)
256
   uint32_t image_paddr;   // [IS] Physical address of image, here 0x01400f30 (appears in "Offset" column for "startup-header" which is the first entry/start of file)
257
   uint32_t ram_paddr;     // [IS] Physical address of RAM to copy image to (startup_size bytes copied), here 0x01400f30 (same as above)
258
   uint32_t ram_size;      // [ S] Amount of RAM used by the startup program and executables contained in the file system, here 0x00cd6128 i.e. 13 459 752 dec. which is 13 Mb. i.e. IFS file size minus 0x9eee
259
   uint32_t startup_size;  // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes
260
   uint32_t stored_size;   // [I ] Size of entire image, here 0x00cd6128 (same as ram_size)
261
   uint32_t imagefs_paddr; // [IS] Set by IPL to where the imagefs is when startup runs (0)
262
   uint32_t imagefs_size;  // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes
263
   uint16_t preboot_size;  // [I ] Size of loaded before header, here 0xf30 or 3888 bytes (size of "bios.boot" file))
264
   uint16_t zero0;         // [  ] Zeros
265
   uint32_t zero[1];       // [  ] Zeros
266
   uint64_t addr_off;      // [ S] Offset to add to startup_vaddr, image_paddr, ram_paddr, and imagefs_paddr members, here zero (0)
267
   uint32_t info[48];      // [IS] Array of startup_info* structures (zero filled)
268
} startup_header_t;
269
 
270
 
271
typedef struct __attribute__((packed)) startup_trailer_s
272
{
273
   uint32_t cksum; // checksum from start of header to start of trailer
2 pmbaty 274
} startup_trailer_v1_t;
1 pmbaty 275
 
276
 
277
// NOTE: The checksums in this trailer will only be valid prior to entering startup.
278
// Because the startup binary is executed in-place, its data segment will change once the program is running.
279
// Hence, any checksum validation would need to be done by the boot loader / IFS.
280
typedef struct __attribute__((packed)) startup_trailer_v2_s
281
{
282
   uint8_t sha512[64]; // SHA512 from start of header to start of trailer
283
   uint32_t cksum; // checksum from start of header to start of this member
284
} startup_trailer_v2_t;
285
 
286
 
287
typedef struct __attribute__((packed)) image_header_s
288
{
289
   uint8_t signature[7]; // image filesystem signature, i.e. "imagefs"
290
   uint8_t flags; // endian neutral flags, 0x1c
291
   uint32_t image_size; // size from header to end of trailer (here 0xca6fe0 or 13 266 912)
292
   uint32_t hdr_dir_size; // size from header to last dirent (here 0x12b8 or 4792)
293
   uint32_t dir_offset; // offset from header to first dirent (here 0x5c or 92)
294
   uint32_t boot_ino[4]; // inode of files for bootstrap pgms (here 0xa0000002, 0, 0, 0)
295
   uint32_t script_ino; // inode of file for script (here 3)
296
   uint32_t chain_paddr; // offset to next filesystem signature (0)
297
   uint32_t spare[10]; // zerofill
298
   uint32_t mountflags; // default _MOUNT_* from sys/iomsg.h (0)
299
   char mountpoint[4]; // default mountpoint for image ("/" + "\0\0\0")
300
} image_header_t;
301
 
302
 
303
typedef struct __attribute__((packed)) image_trailer_v1_s
304
{
305
   uint32_t cksum; // checksum from start of header to start of trailer
2 pmbaty 306
} image_trailer_v1_t; // NOTE: this is the same structure as startup_trailer_v1_t
1 pmbaty 307
 
308
 
309
// NOTE: the checksums in this trailer will only be valid until the first non-startup bootstrap binary (e.g., startup-verifier, procnto, ...) is invoked.
310
// Because bootstrap binaries execute in-place, their data segments will change once the programs are running.
311
// Hence, any checksum validation would need to be done either by the boot loader / IFS or by the startup.
312
typedef struct __attribute__((packed)) image_trailer_v2_s
313
{
314
   uint8_t sha512[64]; // SHA512 from start of image header to start of trailer
315
   uint32_t cksum; // checksum from start of header to start of this member
2 pmbaty 316
} image_trailer_v2_t; // NOTE: this is the same structure as startup_trailer_v2_t
1 pmbaty 317
 
318
 
319
#ifdef _MSC_VER
320
#pragma pack(pop)
321
#endif // _MSC_VER
322
 
323
 
324
typedef struct parms_s
325
{
326
   int dperms; // directory permissions (e.g. 0755)
327
   int perms; // file permissions (e.g. 0644)
328
   int uid; // owner user ID (e.g. 0 = root)
329
   int gid; // owner group ID (e.g. 0 = root)
330
   int st_mode; // entry type (e.g. S_IFREG for files) and permissions
331
   char prefix[MAXPATHLEN]; // install path (e.g. "proc/boot")
2 pmbaty 332
   bool is_compiled_bootscript; // entry has [+script] attribute
1 pmbaty 333
   char search[10 * MAXPATHLEN]; // binary search path (the default one will be constructed at startup)
2 pmbaty 334
 
335
   uint8_t *precompiled_data;
336
   size_t precompiled_datalen;
1 pmbaty 337
} parms_t;
338
 
339
 
340
// global variables
341
static char line_buffer[4096]; // scrap buffer for the IFS build file parser
342
static uint32_t image_base = 4 * 1024 * 1024; // default image base, as per QNX docs -- can be changed with the [image=XXXX] attribute in the IFS build file
343
static uint32_t image_end = UINT32_MAX; // default image end (no limit)
344
static uint32_t image_maxsize = UINT32_MAX; // default image max size (no limit)
345
static uint32_t image_totalsize = 0; // image total size, measured once all the blocks have been written to the output IFS file
346
static uint32_t image_align = 4; // default image alignment, as per QNX docs
347
static uint32_t image_kernel_ino = 0;
348
static uint32_t image_bootscript_ino = 0;
349
static char image_processor[16] = "x86_64"; // default CPU type for which this image is built, either "x86_64" or "aarch64le" (will be used to find out the right include paths under $QNX_TARGET)
350
static char *buildfile_pathname = NULL; // pathname of IFS build file
351
static int lineno = 0; // current line number in IFS build file
352
static char *QNX_TARGET = NULL; // value of the $QNX_TARGET environment variable
353
 
354
 
355
// prototypes of local functions
356
static void sha512_private_transform (SHA512_CTX *context, const uint64_t *data); // used internally in SHA512_Update() and SHA512_Final()
357
static void SHA512_Init (SHA512_CTX *context);
358
static void SHA512_Update (SHA512_CTX *context, void *data, size_t len);
359
static void SHA512_Final (uint8_t digest[SHA512_DIGEST_LENGTH], SHA512_CTX *context);
360
static uint8_t *SHA512 (void *data, size_t data_len, uint8_t *digest); // computes a SHA-512 in one pass (shortcut for SHA512_Init(), SHA512_Update() N times and SHA512_Final())
2 pmbaty 361
static int32_t update_checksum32 (const int32_t start_value, const void *data, const size_t len); // update the sum of an array of 32-bit signed integers
1 pmbaty 362
static long long read_integer (const char *str); // reads an integer number for a string that may be specified in either hex, octal or decimal base, and may have an optional unit suffix (k, m, g, t)
2 pmbaty 363
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)
364
static char *binary (const uint8_t x, char char_for_zero, char char_for_one); // returns the binary representation of byte 'x' as a string
365
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
1 pmbaty 366
static int fwrite_filecontents (const char *pathname, FILE *fp); // dumps the contents of pathname into fp
367
static size_t fwrite_fsentry (const fsentry_t *fsentry, FILE *fp); // writes the given filesystem entry into fp (without its contents)
368
static size_t add_fsentry (fsentry_t **fsentries, size_t *fsentry_count, const char *stored_pathname, parms_t *entry_parms, const uint8_t *data, const size_t entry_datalen); // stack up a new filesystem entry
369
static int fsentry_compare_pathnames_cb (const void *a, const void *b); // qsort() comparison callback that sorts filesystem entries by pathnames
370
static int fsentry_compare_sizes_cb (const void *a, const void *b); // qsort() comparison callback that sorts filesystem entries by size
2 pmbaty 371
static int dump_ifs_info (const char *ifs_pathname); // dumps detailed info about a particular IFS file on the standard output, returns 0 on success and >0 on error
1 pmbaty 372
 
373
 
374
static void sha512_private_transform (SHA512_CTX *context, const uint64_t *data)
375
{
376
   // logical functions used in SHA-384 and SHA-512
377
   #define S64(b,x)      (((x) >> (b)) | ((x) << (64 - (b)))) // 64-bit rotate right
378
   #define Ch(x,y,z)     (((x) & (y)) ^ ((~(x)) & (z)))
379
   #define Maj(x,y,z)    (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
380
   #define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
381
   #define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
382
   #define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ ((x) >> 7))
383
   #define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ ((x) >> 6))
384
 
385
   // hash constant words K for SHA-384 and SHA-512
386
   static const uint64_t K512[80] = {
387
      0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
388
      0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
389
      0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
390
      0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
391
      0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
392
      0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
393
      0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
394
      0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
395
      0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
396
      0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
397
   };
398
 
399
   uint64_t     a, b, c, d, e, f, g, h, s0, s1;
400
   uint64_t     T1, T2, *W512 = (uint64_t *) context->buffer;
401
   int j;
402
 
403
   // initialize registers with the prev. intermediate value
404
   a = context->state[0]; b = context->state[1]; c = context->state[2]; d = context->state[3]; e = context->state[4]; f = context->state[5]; g = context->state[6]; h = context->state[7];
405
 
406
   for (j = 0; j < 16; j++)
407
   {
408
#if __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
409
      W512[j] = __builtin_bswap64 (*data); // convert to host byte order
410
#elif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
411
      W512[j] = *data;
412
#else // __BYTE_ORDER__ == ???
413
#error Please port this SHA-512 code to your exotic endianness platform. What are you compiling this on? PDP? Honeywell?
414
#endif // __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
415
 
416
      // apply the SHA-512 compression function to update a..h
417
      T1 = h + Sigma1_512 (e) + Ch (e, f, g) + K512[j] + W512[j];
418
      T2 = Sigma0_512 (a) + Maj (a, b, c);
419
 
420
      // update registers
421
      h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2;
422
 
423
      data++;
424
   }
425
 
426
   for (; j < 80; j++)
427
   {
428
      // part of the message block expansion
429
      s0 = W512[(j + 1) & 0x0f];
430
      s0 = sigma0_512 (s0);
431
      s1 = W512[(j + 14) & 0x0f];
432
      s1 = sigma1_512 (s1);
433
 
434
      // apply the SHA-512 compression function to update a..h
435
      T1 = h + Sigma1_512 (e) + Ch (e, f, g) + K512[j] + (W512[j & 0x0f] += s1 + W512[(j + 9) & 0x0f] + s0);
436
      T2 = Sigma0_512 (a) + Maj (a, b, c);
437
 
438
      // update registers
439
      h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2;
440
   }
441
 
442
   // compute the current intermediate hash value
443
   context->state[0] += a; context->state[1] += b; context->state[2] += c; context->state[3] += d; context->state[4] += e; context->state[5] += f; context->state[6] += g; context->state[7] += h;
444
 
445
   // clean up
446
   a = b = c = d = e = f = g = h = T1 = T2 = 0;
447
   #undef sigma1_512
448
   #undef sigma0_512
449
   #undef Sigma1_512
450
   #undef Sigma0_512
451
   #undef Maj
452
   #undef Ch
453
   #undef S64
454
   return;
455
}
456
 
457
 
458
static void SHA512_Init (SHA512_CTX *context)
459
{
460
   // initial hash value H for SHA-512
461
   static const uint64_t sha512_initial_hash_value[8] = {
462
      0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
463
   };
464
 
465
   memcpy (context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH);
466
   memset (context->buffer, 0, SHA512_BLOCK_LENGTH);
467
   context->bitcount[0] = context->bitcount[1] = 0;
468
}
469
 
470
 
471
void SHA512_Update (SHA512_CTX *context, void *datain, size_t len)
472
{
473
   #define ADDINC128(w,n) do { \
474
           (w)[0] += (uint64_t) (n); \
475
           if ((w)[0] < (n)) \
476
                   (w)[1]++; \
477
   } while (0) // macro for incrementally adding the unsigned 64-bit integer n to the unsigned 128-bit integer (represented using a two-element array of 64-bit words
478
 
479
   size_t freespace, usedspace;
480
   const uint8_t *data = (const uint8_t *) datain;
481
 
482
   if (len == 0)
483
      return; // calling with empty data is valid - we do nothing
484
 
485
   usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
486
   if (usedspace > 0)
487
   {
488
      // calculate how much free space is available in the buffer
489
      freespace = SHA512_BLOCK_LENGTH - usedspace;
490
 
491
      if (len >= freespace)
492
      {
493
         // fill the buffer completely and process it
494
         memcpy (&context->buffer[usedspace], data, freespace);
495
         ADDINC128 (context->bitcount, freespace << 3);
496
         len -= freespace;
497
         data += freespace;
498
         sha512_private_transform (context, (uint64_t *) context->buffer);
499
      }
500
      else
501
      {
502
         // the buffer is not full yet
503
         memcpy (&context->buffer[usedspace], data, len);
504
         ADDINC128 (context->bitcount, len << 3);
505
 
506
         // clean up
507
         usedspace = freespace = 0;
508
         return;
509
      }
510
   }
511
 
512
   while (len >= SHA512_BLOCK_LENGTH)
513
   {
514
      // process as many complete blocks as we can
515
      sha512_private_transform (context, (uint64_t *) data);
516
      ADDINC128 (context->bitcount, SHA512_BLOCK_LENGTH << 3);
517
      len -= SHA512_BLOCK_LENGTH;
518
      data += SHA512_BLOCK_LENGTH;
519
   }
520
 
521
   if (len > 0)
522
   {
523
      // save leftovers
524
      memcpy (context->buffer, data, len);
525
      ADDINC128 (context->bitcount, len << 3);
526
   }
527
 
528
   // clean up
529
   usedspace = freespace = 0;
530
   #undef ADDINC128
531
   return;
532
}
533
 
534
 
535
static void SHA512_Final (uint8_t digest[SHA512_DIGEST_LENGTH], SHA512_CTX *context)
536
{
537
   #define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16)
538
 
539
   size_t usedspace;
540
   union { uint8_t *as_bytes; uint64_t *as_uint64s; } cast_var = { NULL };
541
 
542
   // if no digest buffer is passed, don't bother finalizing the computation
543
   if (digest != NULL)
544
   {
545
      usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
546
 
547
#if __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
548
      context->bitcount[0] = __builtin_bswap64 (context->bitcount[0]); // convert from host byte order
549
      context->bitcount[1] = __builtin_bswap64 (context->bitcount[1]); // convert from host byte order
550
#endif // __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
551
 
552
      if (usedspace > 0)
553
      {
554
         // begin padding with a 1 bit
555
         context->buffer[usedspace++] = 0x80;
556
 
557
         if (usedspace <= SHA512_SHORT_BLOCK_LENGTH)
558
            memset (&context->buffer[usedspace], 0, SHA512_SHORT_BLOCK_LENGTH - usedspace); // set-up for the last transform
559
         else
560
         {
561
            if (usedspace < SHA512_BLOCK_LENGTH)
562
               memset (&context->buffer[usedspace], 0, SHA512_BLOCK_LENGTH - usedspace);
563
 
564
            sha512_private_transform (context, (uint64_t *) context->buffer); // do second-to-last transform
565
            memset (context->buffer, 0, SHA512_BLOCK_LENGTH - 2); // and set-up for the last transform
566
         }
567
      }
568
      else // usedspace == 0
569
      {
570
         memset (context->buffer, 0, SHA512_SHORT_BLOCK_LENGTH); // prepare for final transform
571
         *context->buffer = 0x80; // begin padding with a 1 bit
572
      }
573
 
574
      // store the length of input data (in bits)
575
      cast_var.as_bytes = context->buffer;
576
      cast_var.as_uint64s[SHA512_SHORT_BLOCK_LENGTH / 8 + 0] = context->bitcount[1];
577
      cast_var.as_uint64s[SHA512_SHORT_BLOCK_LENGTH / 8 + 1] = context->bitcount[0];
578
 
579
      // final transform
580
      sha512_private_transform (context, (uint64_t *) context->buffer);
581
 
582
      // save the hash data for output
583
#if __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
584
      for (int j = 0; j < 8; j++)
585
         context->state[j] = __builtin_bswap64 (context->state[j]); // convert to host byte order
586
#endif // __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
587
      memcpy (digest, context->state, SHA512_DIGEST_LENGTH);
588
   }
589
 
590
   // zero out state data
591
   memset (context, 0, sizeof (SHA512_CTX));
592
   #undef SHA512_SHORT_BLOCK_LENGTH
593
   return;
594
}
595
 
596
 
2 pmbaty 597
static uint8_t *SHA512 (void *data, size_t data_len, uint8_t *digest_or_NULL)
1 pmbaty 598
{
2 pmbaty 599
   // computes the SHA-512 hash of a block of data in one pass and write it to digest, or to a static buffer if NULL
600
   // returns the STRING REPRESENTATION of digest in a statically-allocated string
1 pmbaty 601
 
2 pmbaty 602
   static uint8_t static_digest[SHA512_DIGEST_LENGTH] = "";
603
   static char digest_as_string[2 * SHA512_DIGEST_LENGTH + 1] = "";
604
 
1 pmbaty 605
   SHA512_CTX ctx;
2 pmbaty 606
   size_t byte_index;
607
 
1 pmbaty 608
   SHA512_Init (&ctx);
609
   SHA512_Update (&ctx, data, data_len);
2 pmbaty 610
   if (digest_or_NULL == NULL)
611
      digest_or_NULL = static_digest;
612
   SHA512_Final (digest_or_NULL, &ctx);
613
 
614
   for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
615
      sprintf (&digest_as_string[2 * byte_index], "%02x", digest_or_NULL[byte_index]);
616
   return (digest_as_string);
1 pmbaty 617
}
618
 
619
 
2 pmbaty 620
static int32_t update_checksum32 (const int32_t start_value, const void *data, const size_t len)
1 pmbaty 621
{
622
   // compute the sum of an array of 32-bit signed integers
623
 
2 pmbaty 624
   const int32_t *values_array = data;
1 pmbaty 625
   int32_t sum = start_value;
2 pmbaty 626
 
627
   for (size_t value_index = 0; value_index < len / sizeof (int32_t); value_index++)
628
      sum += values_array[value_index];
629
 
1 pmbaty 630
   return (sum);
631
}
632
 
633
 
634
static long long read_integer (const char *str)
635
{
636
   // reads a number for a string that may be specified in either hex, octal or decimal base, and may have an optional unit suffix (k, m, g, t)
637
 
638
   char *endptr = NULL;
639
   long long ret = strtoll (str, &endptr, 0); // use strtoll() to handle hexadecimal (0x...), octal (0...) and decimal (...) bases
640
   if (endptr != NULL)
641
   {
642
      if ((*endptr == 'k') || (*endptr == 'K')) ret *= (size_t) 1024;
643
      else if ((*endptr == 'm') || (*endptr == 'M')) ret *= (size_t) 1024 * 1024;
644
      else if ((*endptr == 'g') || (*endptr == 'G')) ret *= (size_t) 1024 * 1024 * 1024;
645
      else if ((*endptr == 't') || (*endptr == 'T')) ret *= (size_t) 1024 * 1024 * 1024 * 1024; // future-proof enough, I suppose?
646
   }
647
   return (ret);
648
}
649
 
650
 
2 pmbaty 651
static void hex_fprintf (FILE *fp, const uint8_t *data, size_t data_size, int howmany_columns, const char *fmt, ...)
652
{
653
   // this function logs hexadecimal data to an opened file pointer (or to stdout/stderr)
654
 
655
   va_list argptr;
656
   size_t index;
657
   int i;
658
 
659
   // concatenate all the arguments in one string and write it to the file
660
   va_start (argptr, fmt);
661
   vfprintf (fp, fmt, argptr);
662
   va_end (argptr);
663
 
664
   // for each row of howmany_columns bytes of data...
665
   for (index = 0; index < data_size; index += howmany_columns)
666
   {
667
      fprintf (fp, "    %05zu  ", index); // print array address of row
668
      for (i = 0; i < howmany_columns; i++)
669
         if (index + i < data_size)
670
            fprintf (fp, " %02X", data[index + i]); // if row contains data, print data as hex bytes
671
         else
672
            fprintf (fp, "   "); // else fill the space with blanks
673
      fprintf (fp, "   ");
674
      for (i = 0; i < howmany_columns; i++)
675
         if (index + i < data_size)
676
            fputc ((data[index + i] >= 32) && (data[index + i] < 127) ? data[index + i] : '.', fp); // now if row contains data, print data as ASCII
677
         else
678
            fputc (' ', fp); // else fill the space with blanks
679
      fputc ('\n', fp);
680
   }
681
 
682
   return; // and return
683
}
684
 
685
 
686
static char *binary (const uint8_t x, char char_for_zero, char char_for_one)
687
{
688
   // returns the binary representation of x as a string
689
 
690
   static char outstr[9] = "00000000";
691
   for (int i = 0; i < 8; i++)
692
      outstr[i] = (x & (0x80 >> i) ? char_for_one : char_for_zero);
693
   return (outstr);
694
}
695
 
696
 
697
static char *describe_uint8 (const uint8_t x, const char *bitwise_stringdescs[8])
698
{
699
   // returns the ORed description of byte 'x' according to the description strings for each bit
700
 
701
   static char *default_bitstrings[8] = { "bit0", "bit1", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7" };
702
   static char outstr[8 * 64] = "";
703
 
704
   outstr[0] = 0;
705
   for (int i = 0; i < 8; i++)
706
      if (x & (1 << i))
707
      {
708
         if (outstr[0] != 0)
709
            strcat (outstr, "|");
710
         strcat (outstr, ((bitwise_stringdescs != NULL) && (*bitwise_stringdescs[i] != 0) ? bitwise_stringdescs[i] : default_bitstrings[i]));
711
      }
712
   return (outstr);
713
}
714
 
715
 
1 pmbaty 716
static int fwrite_filecontents (const char *pathname, FILE *fp)
717
{
718
   // dumps the binary contents of pathname to fp
719
 
720
   uint8_t *blob_buffer;
721
   size_t blob_size;
722
   FILE *blob_fp;
723
   int ret;
724
 
725
   blob_fp = fopen (pathname, "rb");
726
   if (blob_fp == NULL)
727
      return (-1); // errno is set
728
 
729
   fseek (blob_fp, 0, SEEK_END);
730
   blob_size = ftell (blob_fp);
731
   blob_buffer = malloc (blob_size);
732
   if (blob_buffer == NULL)
733
   {
734
      fclose (blob_fp);
735
      return (-1); // errno is set to ENOMEM
736
   }
737
   fseek (blob_fp, 0, SEEK_SET);
738
   fread (blob_buffer, 1, blob_size, blob_fp);
739
   fclose (blob_fp);
740
 
741
   ret = (int) fwrite (blob_buffer, 1, blob_size, fp);
742
   free (blob_buffer);
743
   return (ret);
744
}
745
 
746
 
747
static size_t fwrite_fsentry (const fsentry_t *fsentry, FILE *fp)
748
{
749
   static const uint8_t zeropad_buffer[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
750
 
751
   size_t datalen;
752
   size_t count;
753
 
754
   count = 0;
755
   if (fp != NULL)
756
      fwrite (&fsentry->header, sizeof (fsentry->header), 1, fp); // write the entry header (PACKED STRUCT)
757
   count += sizeof (fsentry->header);
758
   if (S_ISREG (fsentry->header.mode))
759
   {
760
      if (fp != NULL)
761
      {
762
         fwrite (&fsentry->u.file.offset, sizeof (uint32_t), 1, fp); // write offset
763
         fwrite (&fsentry->u.file.size,   sizeof (uint32_t), 1, fp); // write size
764
      }
765
      count += 2 * sizeof (uint32_t);
766
      datalen = strlen (fsentry->u.file.path) + 1;
767
      if (fp != NULL)
768
         fwrite (fsentry->u.file.path, (size_t) datalen, 1, fp); // write null-terminated path (no leading slash)
769
      count += datalen;
770
   }
771
   else if (S_ISDIR (fsentry->header.mode))
772
   {
773
      datalen = strlen (fsentry->u.dir.path) + 1;
774
      if (fp != NULL)
775
         fwrite (fsentry->u.dir.path, (size_t) datalen, 1, fp); // write null-terminated path (no leading slash)
776
      count += datalen;
777
   }
778
   else if (S_ISLNK (fsentry->header.mode))
779
   {
780
      if (fp != NULL)
781
      {
782
         fwrite (&fsentry->u.symlink.sym_offset, sizeof (uint16_t), 1, fp); // write offset
783
         fwrite (&fsentry->u.symlink.sym_size,   sizeof (uint16_t), 1, fp); // write size
784
      }
785
      count += 2 * sizeof (uint16_t);
786
      datalen = strlen (fsentry->u.symlink.path) + 1;
787
      if (fp != NULL)
788
         fwrite (fsentry->u.symlink.path, (size_t) datalen, 1, fp); // write null-terminated path (no leading slash)
789
      count += datalen;
790
      datalen = strlen (fsentry->u.symlink.contents) + 1;
791
      if (fp != NULL)
792
         fwrite (fsentry->u.symlink.contents, (size_t) datalen, 1, fp); // write null-terminated symlink contents
793
      count += datalen;
794
   }
795
   else
796
   {
797
      if (fp != NULL)
798
      {
799
         fwrite (&fsentry->u.device.dev,  sizeof (uint32_t), 1, fp); // write dev number
800
         fwrite (&fsentry->u.device.rdev, sizeof (uint32_t), 1, fp); // write rdev number
801
      }
802
      count += 2 * sizeof (uint32_t);
803
      datalen = strlen (fsentry->u.device.path) + 1;
804
      if (fp != NULL)
805
         fwrite (fsentry->u.device.path, (size_t) datalen, 1, fp); // write null-terminated path (no leading slash)
806
      count += datalen;
807
   }
808
 
809
   if ((fp != NULL) && (count % image_align != 0))
810
      fwrite (zeropad_buffer, count % image_align, 1, fp); // pad as necessary
811
   count = ROUND_TO_UPPER_MULTIPLE (count, image_align);
812
 
813
   return (count);
814
}
815
 
816
 
817
static size_t add_fsentry (fsentry_t **fsentries, size_t *fsentry_count, const char *stored_pathname, parms_t *entry_parms, const uint8_t *entry_data, const size_t entry_datalen)
818
{
819
   static char candidate_pathname[1024];
820
   static char *MKIFS_PATH = NULL;
821
   static int inode_count = 0; // will be preincremented each time this function is called
822
 
823
   void *reallocated_ptr;
824
   char processor_base[16];
825
   struct stat stat_buf;
826
   uint8_t *data_buffer = NULL;
827
   size_t data_len = 0;
2 pmbaty 828
   uint32_t mtime = (uint32_t) time (NULL);
1 pmbaty 829
   uint32_t extra_ino_flags = 0;
830
   fsentry_t *fsentry;
831
   char *token;
832
   FILE *fp;
833
 
834
   if (S_ISDIR (entry_parms->st_mode)) // are we storing a directory ?
835
   {
836
      fprintf (stderr, "directory: ino 0x%x uid %d gid %d mode 0%o path \"%s\"\n", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname);
837
   }
838
   else if (S_ISREG (entry_parms->st_mode)) // else are we storing a regular file ?
839
   {
840
      if ((entry_data != NULL) && (entry_datalen > 0)) // was an explicit contents blob supplied ?
841
      {
842
         if (strcmp (stored_pathname, "proc/boot/boot") == 0) // is it the kernel ?
843
         {
844
            // HACK: for now just consider the kernel as a binary blob
845
            // FIXME: reimplement properly
2 pmbaty 846
            data_len = entry_datalen;
1 pmbaty 847
            data_buffer = malloc (data_len);
848
            if (data_buffer == NULL)
849
            {
850
               fprintf (stderr, "fatal error: out of memory\n");
851
               exit (1);
852
            }
2 pmbaty 853
            memcpy (data_buffer, entry_data, data_len);
854
 
855
            sprintf (candidate_pathname, "%s/procnto-smp-instr", entry_parms->prefix); // fix the entry name
1 pmbaty 856
            stored_pathname = candidate_pathname;
2 pmbaty 857
            extra_ino_flags = IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE; // procnto needs to have these flags stamped on the inode
1 pmbaty 858
            image_kernel_ino = extra_ino_flags | (inode_count + 1);
859
         }
2 pmbaty 860
         else if (entry_parms->is_compiled_bootscript) // else is it a startup script ?
861
            image_bootscript_ino = inode_count + 1; // save boot script inode number for image header
862
 
863
         // should we substitute precompiled data to this data blob ?
864
         if ((entry_parms->precompiled_data != NULL) && (entry_parms->precompiled_datalen > 0))
1 pmbaty 865
         {
2 pmbaty 866
            data_len = entry_parms->precompiled_datalen;
867
            data_buffer = malloc (data_len);
1 pmbaty 868
            if (data_buffer == NULL)
869
            {
870
               fprintf (stderr, "fatal error: out of memory\n");
871
               exit (1);
872
            }
2 pmbaty 873
            memcpy (data_buffer, entry_parms->precompiled_data, data_len);
1 pmbaty 874
         }
2 pmbaty 875
         else // else use the supplied data blob
1 pmbaty 876
         {
877
            data_len = entry_datalen;
878
            data_buffer = malloc (data_len);
879
            if (data_buffer == NULL)
880
            {
881
               fprintf (stderr, "fatal error: out of memory\n");
882
               exit (1);
883
            }
884
            memcpy (data_buffer, entry_data, data_len);
885
         }
2 pmbaty 886
/*
887
         else if (entry_parms->should_compile_contents_as_startup_script) // should we compile this contents as a startup script ?
888
         {
889
            // HACK: for now just use a precompiled script
890
            // FIXME: replace this with a true compilation with the rules defined above
891
            data_buffer = malloc (INITIAL_STARTUP_SCRIPT_LEN);
892
            if (data_buffer == NULL)
893
            {
894
               fprintf (stderr, "fatal error: out of memory\n");
895
               exit (1);
896
            }
897
            memcpy (data_buffer, INITIAL_STARTUP_SCRIPT, INITIAL_STARTUP_SCRIPT_LEN);
898
            data_len = INITIAL_STARTUP_SCRIPT_LEN;
899
            image_bootscript_ino = inode_count + 1; // save boot script inode number for image header
900
         }*/
1 pmbaty 901
 
902
         fprintf (stderr, "file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" blob (len %zd)\n", extra_ino_flags | (inode_count + 1), entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, data_len);
903
      }
904
      else if ((entry_data != NULL) && (entry_data[0] != 0)) // else entry_datalen == 0, was some sort of pathname supplied ?
905
      {
906
         // is it an absolute pathname ?
907
         if (IS_DIRSEP (entry_data[0])
908
             || (isalpha (entry_data[0])
909
                 && (entry_data[1] == ':')
910
                 && IS_DIRSEP (entry_data[2])))
911
         {
912
            // in this case, it MUST exist at its designated location (either absolute or relative to the current working directory)
913
            strcpy (candidate_pathname, entry_data);
914
            if (stat (candidate_pathname, &stat_buf) != 0)
915
            {
916
               fprintf (stderr, "fatal error: filesystem entry \"%s\" specified in \"%s\" line %d not found on build host\n", entry_data, buildfile_pathname, lineno);
917
               exit (1);
918
            }
919
         }
920
         else // the path is relative, search it among the search paths we have
921
         {
922
            // is the search path NOT defined ?
923
            if (entry_parms->search[0] == 0)
924
            {
925
               // initialize the default search path in MKIFS_PATH
926
               if (MKIFS_PATH == NULL)
927
               {
928
                  MKIFS_PATH = getenv ("MKIFS_PATH"); // look in the environment first, and construct a default one if not supplied
929
                  if (MKIFS_PATH == NULL)
930
                  {
931
                     strcpy (processor_base, image_processor); // construct PROCESSOR_BASE
932
                     token = strchr (processor_base, '-');
933
                     if (token != NULL)
934
                        *token = 0; // split anything from the first dash onwards
935
                     data_len = strlen (processor_base);
936
                     if ((data_len > 2) && ((processor_base[data_len - 2] == 'b') || (processor_base[data_len - 2] == 'l')) && (processor_base[data_len - 1] == 'e'))
937
                        processor_base[data_len - 2] = 0; // if it ends with "le" or "be", strip that too
938
 
939
                     MKIFS_PATH = malloc (10 * MAXPATHLEN); // construct a default MKIFS_PATH now
940
                     if (MKIFS_PATH == NULL)
941
                     {
942
                        fprintf (stderr, "fatal error: out of memory\n");
943
                        exit (1);
944
                     }
945
                     sprintf (MKIFS_PATH, ".|%s/%s/sbin|%s/%s/usr/sbin|%s/%s/boot/sys|%s/%s/boot/sys|%s/%s/bin|%s/%s/usr/bin|%s/%s/lib|%s/%s/lib/dll|%s/%s/usr/lib", // use a platform-agnostic character as path separator
946
                                          QNX_TARGET, image_processor,
947
                                          QNX_TARGET, image_processor,
948
                                          QNX_TARGET, image_processor,
949
                                          QNX_TARGET, processor_base,
950
                                          QNX_TARGET, image_processor,
951
                                          QNX_TARGET, image_processor,
952
                                          QNX_TARGET, image_processor,
953
                                          QNX_TARGET, image_processor,
954
                                          QNX_TARGET, image_processor);
955
                  }
956
               } // at this point MKIFS_PATH is defined
957
 
958
               strcpy (entry_parms->search, MKIFS_PATH); // if entry search path is not defined, use the contents of MKIFS_PATH
959
            }
960
 
961
            // convert path separators in the MKIFS_PATH environment variable into something platform-agnostic
962
            for (token = entry_parms->search; *token != 0; token++)
963
               if (*token == PATH_SEP)
964
                  *token = '|';
965
 
966
            token = strtok (entry_parms->search, "|");
967
            while (token != NULL)
968
            {
969
               sprintf (candidate_pathname, "%s/%s", token, entry_data);
970
               if (stat (candidate_pathname, &stat_buf) == 0)
971
                  break;
972
               token = strtok (NULL, "|");
973
            }
974
            if (token == NULL)
975
            {
976
               fprintf (stderr, "fatal error: filesystem entry \"%s\" specified in \"%s\" line %d not found on build host\n", entry_data, buildfile_pathname, lineno);
977
               exit (1);
978
            }
979
         }
980
 
981
         data_len = stat_buf.st_size;
982
         mtime = (uint32_t) stat_buf.st_mtime;
983
 
984
         data_buffer = malloc (data_len);
985
         if (data_buffer == NULL)
986
         {
987
            fprintf (stderr, "fatal error: out of memory\n");
988
            exit (1);
989
         }
990
         fp = fopen (candidate_pathname, "rb");
991
         fread (data_buffer, 1, data_len, fp);
992
         fclose (fp);
993
 
994
         fprintf (stderr, "file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" buildhost_file \"%s\" (len %zd)\n", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_data, data_len);
995
      }
996
   }
997
   else if (S_ISLNK (entry_parms->st_mode)) // else are we storing a symbolic link ?
998
   {
999
      data_len = strlen (entry_data);
1000
 
1001
      data_buffer = malloc (data_len + 1);
1002
      if (data_buffer == NULL)
1003
      {
1004
         fprintf (stderr, "fatal error: out of memory\n");
1005
         exit (1);
1006
      }
1007
      memcpy (data_buffer, entry_data, data_len + 1); // copy including null terminator
1008
 
1009
      fprintf (stderr, "symlink: ino 0x%x uid %d gid %d mode 0%o path \"%s\" -> \"%s\"\n", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, data_buffer);
1010
   }
1011
   else // we must be storing a FIFO
1012
   {
1013
      if (strchr (entry_data, ':') == NULL)
1014
      {
1015
         fprintf (stderr, "fatal error: device entry \"%s\" malformed (no 'dev:rdev' pair)\n", stored_pathname);
1016
         exit (1);
1017
      }
1018
 
1019
      fprintf (stderr, "fifo: ino 0x%x uid %d gid %d mode 0%o path \"%s\" dev rdev %s)\n", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_data);
1020
   }
1021
 
1022
   // reallocate filesystem entries array to hold one more slot
1023
   reallocated_ptr = realloc (*fsentries, (*fsentry_count + 1) * sizeof (fsentry_t)); // attempt to reallocate
1024
   if (reallocated_ptr == NULL)
1025
   {
1026
      fprintf (stderr, "fatal error: out of memory\n");
1027
      exit (1);
1028
   }
1029
   *fsentries = reallocated_ptr; // save reallocated pointer
1030
   fsentry = &(*fsentries)[*fsentry_count]; // quick access to fs entry slot
1031
   //fsentry->header.size = 0; // will be filled once we know it
1032
   fsentry->header.extattr_offset = 0;
1033
   fsentry->header.ino = extra_ino_flags | (++inode_count);
1034
   fsentry->header.mode = entry_parms->st_mode;
1035
   fsentry->header.gid = entry_parms->gid;
1036
   fsentry->header.uid = entry_parms->uid;
1037
   fsentry->header.mtime = mtime;
1038
   if (S_ISDIR (entry_parms->st_mode))
1039
   {
1040
      fsentry->u.dir.path = strdup (stored_pathname);
1041
      fsentry->header.size = (uint16_t) (sizeof (fsentry->header) + ROUND_TO_UPPER_MULTIPLE (strlen (fsentry->u.dir.path) + 1, image_align)); // now we can set the size
1042
      fsentry->UNSAVED_was_data_written = true; // no data to save
1043
   }
1044
   else if (S_ISREG (entry_parms->st_mode))
1045
   {
1046
      fsentry->u.file.offset = WILL_BE_FILLED_LATER; // will be filled later in main() when the file's data blob will be written to the output file
1047
      fsentry->u.file.size = (uint32_t) data_len;
1048
      fsentry->u.file.path = strdup (stored_pathname);
1049
      fsentry->u.file.UNSAVED_databuf = data_buffer;
1050
      fsentry->header.size = (uint16_t) (sizeof (fsentry->header) + ROUND_TO_UPPER_MULTIPLE (strlen (fsentry->u.file.path) + 1, image_align)); // now we can set the size
1051
      fsentry->UNSAVED_was_data_written = false; // there *IS* data to save
1052
   }
1053
   else if (S_ISLNK (entry_parms->st_mode))
1054
   {
1055
      fsentry->u.symlink.sym_offset = (uint16_t) (strlen (stored_pathname) + 1);
1056
      fsentry->u.symlink.sym_size = (uint16_t) data_len;
1057
      fsentry->u.symlink.path = strdup (stored_pathname);
1058
      fsentry->u.symlink.contents = data_buffer;
1059
      fsentry->header.size = (uint16_t) (sizeof (fsentry->header) + ROUND_TO_UPPER_MULTIPLE ((size_t) fsentry->u.symlink.sym_offset + fsentry->u.symlink.sym_size + 1, image_align)); // now we can set the size
1060
      fsentry->UNSAVED_was_data_written = true; // no data to save
1061
   }
1062
   else
1063
   {
1064
      fsentry->u.device.dev  = strtol (entry_data, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
1065
      fsentry->u.device.rdev = strtol (strchr (entry_data, ':') + 1, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
1066
      fsentry->u.device.path = strdup (stored_pathname);
1067
      fsentry->header.size = (uint16_t) (sizeof (fsentry->header) + ROUND_TO_UPPER_MULTIPLE (strlen (fsentry->u.device.path), image_align)); // now we can set the size
1068
      fsentry->UNSAVED_was_data_written = true; // no data to save
1069
   }
1070
   (*fsentry_count)++;
1071
   return (*fsentry_count);
1072
}
1073
 
1074
 
1075
static int fsentry_compare_pathnames_cb (const void *a, const void *b)
1076
{
1077
   // qsort() callback that compares two imagefs filesystem entries and sort them alphabetically by pathname
1078
 
1079
   fsentry_t *entry_a = (fsentry_t *) a;
1080
   fsentry_t *entry_b = (fsentry_t *) b;
1081
   const char *pathname_a = (S_ISDIR (entry_a->header.mode) ? entry_a->u.dir.path : (S_ISREG (entry_a->header.mode) ? entry_a->u.file.path : (S_ISLNK (entry_a->header.mode) ? entry_a->u.symlink.path : entry_a->u.device.path)));
1082
   const char *pathname_b = (S_ISDIR (entry_b->header.mode) ? entry_b->u.dir.path : (S_ISREG (entry_b->header.mode) ? entry_b->u.file.path : (S_ISLNK (entry_b->header.mode) ? entry_b->u.symlink.path : entry_b->u.device.path)));
1083
   return (strcmp (pathname_a, pathname_b));
1084
}
1085
 
1086
 
1087
static int fsentry_compare_sizes_cb (const void *a, const void *b)
1088
{
1089
   // qsort() callback that compares two imagefs filesystem entries and sort them alphabetically by pathname
1090
 
1091
   fsentry_t *entry_a = (fsentry_t *) a;
1092
   fsentry_t *entry_b = (fsentry_t *) b;
1093
   const int32_t size_a = (S_ISREG (entry_a->header.mode) ? entry_a->u.file.size : 0); // only files (i.e. entries wearing the S_IFREG flag) have a separate data block
1094
   const int32_t size_b = (S_ISREG (entry_b->header.mode) ? entry_b->u.file.size : 0); // only files (i.e. entries wearing the S_IFREG flag) have a separate data block
1095
   return ((int) size_b - (int) size_a);
1096
}
1097
 
1098
 
1099
int main (int argc, char **argv)
1100
{
1101
   // program entrypoint
1102
 
1103
   #define PAD_OUTFILE_TO(val) do { curr_offset = ftell (fp); while (curr_offset < (val)) { putc (0, fp); curr_offset++; } } while (0)
1104
 
2 pmbaty 1105
   static startup_header_t startup_header = { 0 }; // output IFS's startup header
1106
   static startup_trailer_v2_t startup_trailer = { 0 }; // output IFS's startup trailer (version 2, with SHA-512 checksum and int32 checksum)
1107
   static image_header_t image_header = { 0 }; // output IFS's imagefs header
1108
   static image_trailer_v2_t image_trailer = { 0 }; // output IFS's imagefs trailer (version 2, with SHA-512 checksum and int32 checksum)
1109
   static fsentry_t *fsentries = NULL; // output IFS's filesystem entries
1110
   static size_t fsentry_count = 0; // number of entries in the IFS filesystem
1111
   static parms_t default_parms = { 0755, 0644, 0, 0, S_IFREG, "/proc/boot", false, "", NULL, 0 }; // default parameters for a filesystem entry
1112
   static parms_t entry_parms = { 0 }; // current parameters for a filesystem entry (will be initialized to default_parms each time a new entry is parsed in the build file)
1 pmbaty 1113
 
1114
   // bootable IFS support
1115
   char *bootfile_pathname = NULL;           // HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS
2 pmbaty 1116
   size_t bootfile_size = 0;                 // HACK: size of the bootcode binary blob file to put at the start of a bootable IFS
1 pmbaty 1117
   char *startupfile_pathname = NULL;        // HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS
1118
   size_t startupfile_ep_from_imagebase = 0; // HACK: startup code entrypoint offset from image base for a bootable IFS 
1119
   char *kernelfile_pathname = NULL;         // HACK: pathname to precompiled kernel file blob to put in a bootable IFS
1120
   size_t kernelfile_offset = 0;             // HACK: kernel file offset in bootable IFS
1121
 
1122
   char path_in_ifs[MAXPATHLEN];
1123
   char *ifs_pathname = NULL;
1124
   void *reallocated_ptr;
1125
   struct stat stat_buf;
1126
   size_t startuptrailer_offset;
1127
   size_t startupheader_offset;
1128
   size_t imgtrailer_offset;
1129
   size_t imgheader_offset;
1130
   size_t imgdir_offset;
1131
   size_t imgdir_size;
1132
   size_t final_size;
1133
   size_t fsentry_index;
1134
   size_t curr_offset;
1135
   size_t blob_datasize; // mallocated size
1136
   size_t blob_datalen; // used size
1137
   uint8_t *blob_data = NULL; // mallocated
1138
   char *line_ptr;
1139
   char *directiveblock_start;
1140
   char *write_ptr;
1141
   char *token;
1142
   char *value;
1143
   char *sep;
1144
   //char *ctx;
1145
   int arg_index;
1146
   bool is_quoted_context = false;
1147
   bool is_escaped_char = false;
1148
   bool want_info = false;
1149
   bool want_help = false;
1150
   int string_len;
1151
   int read_char;
1152
   FILE *buildfile_fp;
1153
   FILE *fp;
1154
 
1155
   // parse arguments
1156
   for (arg_index = 1; arg_index < argc; arg_index++)
1157
   {
1158
      if ((strcmp (argv[arg_index], "--bootfile") == 0) && (arg_index + 1 < argc)) // --bootfile path/to/blob.bin
1159
         bootfile_pathname = argv[++arg_index];
1160
      else if ((strcmp (argv[arg_index], "--startupfile") == 0) && (arg_index + 1 < argc)) // --startupfile path/to/blob.bin@0x1030
1161
      {
1162
         sep = strchr (argv[++arg_index], '@');
1163
         if ((sep == NULL) || (sep[1] == 0))
1164
         {
1165
            fprintf (stderr, "error: the --startupfile arguments expects <pathname>@<entrypoint_from_image_base>\n");
1166
            exit (1);
1167
         }
1168
         *sep = 0;
1169
         startupfile_pathname = argv[arg_index];
1170
         startupfile_ep_from_imagebase = (size_t) read_integer (sep + 1);
1171
      }
1172
      else if ((strcmp (argv[arg_index], "--kernelfile") == 0) && (arg_index + 1 < argc)) // --kernelfile path/to/blob.bin@0x32000
1173
      {
1174
         sep = strchr (argv[++arg_index], '@');
1175
         if ((sep == NULL) || (sep[1] == 0))
1176
         {
1177
            fprintf (stderr, "error: the --kernelfile arguments expects <pathname>@<fileoffset>\n");
1178
            exit (1);
1179
         }
1180
         *sep = 0;
1181
         kernelfile_pathname = argv[arg_index];
1182
         kernelfile_offset = (size_t) read_integer (sep + 1);
1183
      }
1184
      else if (strcmp (argv[arg_index], "--info") == 0)
1185
         want_info = true;
1186
      else if ((strcmp (argv[arg_index], "-?") == 0) || (strcmp (argv[arg_index], "--help") == 0))
1187
         want_help = true;
1188
      else if (buildfile_pathname == NULL)
1189
         buildfile_pathname = argv[arg_index];
1190
      else if (ifs_pathname == NULL)
1191
         ifs_pathname = argv[arg_index];
1192
   }
1193
 
1194
   // do we not have enough information to run ?
2 pmbaty 1195
   if (want_help || (buildfile_pathname == NULL) || (!want_info && (ifs_pathname == NULL)))
1 pmbaty 1196
   {
1197
      if (want_help)
1198
         fprintf (stdout, "ifstool - QNX in-kernel filesystem creation utility by Pierre-Marie Baty <pm@pmbaty.com>\n");
1199
      else
1200
         fprintf (stderr, "error: missing parameters\n");
1201
      fprintf ((want_help ? stdout : stderr), "usage:\n");
1202
      fprintf ((want_help ? stdout : stderr), "    ifstool [--bootfile <pathname>] [--startupfile <pathname>@<EP_from_imgbase>] [--kernelfile <pathname>@<fileoffs>] <buildfile> <outfile>\n");
1203
      fprintf ((want_help ? stdout : stderr), "    ifstool --info <ifs file>\n");
1204
      fprintf ((want_help ? stdout : stderr), "    ifstool --help\n");
1205
      exit (want_help ? 0 : 1);
1206
   }
1207
 
2 pmbaty 1208
   // do we want info about a particular IFS ? if so, dump it
1209
   if (want_info)
1210
      exit (dump_ifs_info (buildfile_pathname)); // NOTE: the first argument after --info is actually the IFS file, not a build file, but the arguments are collected in this order
1211
 
1 pmbaty 1212
   // make sure we have ${QNX_TARGET} pointing somewhere
1213
   QNX_TARGET = getenv ("QNX_TARGET");
1214
   if (QNX_TARGET == NULL)
1215
   {
1216
      fprintf (stderr, "error: the QNX_TARGET environment variable is not set\n");
1217
      exit (1);
1218
   }
1219
   else if (access (QNX_TARGET, 0) != 0)
1220
   {
1221
      fprintf (stderr, "error: the QNX_TARGET environment variable doesn't point to an existing directory\n");
1222
      exit (1);
1223
   }
1224
 
1225
   // open build file
1226
   buildfile_fp = fopen (buildfile_pathname, "rb");
1227
   if (buildfile_fp == NULL)
1228
   {
1229
      fprintf (stderr, "error: unable to open build file \"%s\" for reading (%s)\n", buildfile_pathname, strerror (errno));
1230
      exit (1);
1231
   }
1232
 
1233
   // stack up filesystem entries
1234
   memcpy (&entry_parms, &default_parms, sizeof (default_parms));
1235
   entry_parms.st_mode = S_IFDIR | default_parms.dperms;
1236
   add_fsentry (&fsentries, &fsentry_count, "", &entry_parms, NULL, 0); // add the root dir first
1237
 
1238
   while (fgets (line_buffer, sizeof (line_buffer), buildfile_fp) != NULL)
1239
   {
1240
      lineno++; // keep track of current line number
1241
      //fprintf (stderr, "read buildfile line %d: {%s}\n", lineno, line_buffer);
1242
 
1243
      line_ptr = line_buffer;
1244
      while ((*line_ptr != 0) && isspace (*line_ptr))
1245
         line_ptr++; // skip leading spaces
1246
 
1247
      if ((*line_ptr == 0) || (*line_ptr == '#'))
1248
         continue; // skip empty or comment lines
1249
 
1250
      string_len = (int) strlen (line_buffer);
1251
      if ((string_len > 0) && (line_buffer[string_len - 1] == '\n'))
1252
         line_buffer[string_len - 1] = 0; // chop off newline for easier debug output
1253
 
1254
      // reset entry values
1255
      memcpy (&entry_parms, &default_parms, sizeof (default_parms));
1256
 
1257
      //fprintf (stderr, "parsing buildfile line %d: [%s]\n", lineno, line_ptr);
1258
 
1259
      // does this line start with an attribute block ?
1260
      if (*line_ptr == '[')
1261
      {
1262
         line_ptr++; // skip the leading square bracket
1263
         directiveblock_start = line_ptr; // remember where it starts
1264
         is_quoted_context = false;
1265
         while ((*line_ptr != 0) && !((*line_ptr == ']') && (line_ptr[-1] != '\\')))
1266
         {
1267
            if (*line_ptr == '"')
1268
               is_quoted_context ^= true; // remember when we're between quotes
1269
            else if (!is_quoted_context && (*line_ptr == ' '))
1270
               *line_ptr = RECORD_SEP; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
1271
            line_ptr++; // reach the next unescaped closing square bracket
1272
         }
1273
         if (*line_ptr != ']')
1274
         {
1275
            fprintf (stderr, "warning: syntax error in \"%s\" line %d: unterminated attributes block (skipping)\n", buildfile_pathname, lineno);
1276
            continue; // invalid attribute block, skip line
1277
         }
1278
         *line_ptr = 0; // end the attribute block so that it is a parsable C string
1279
 
1280
         // now parse the attribute tokens
1281
         // DOCUMENTATION: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/m/mkifs.html#mkifs__description
1282
         token = strtok (directiveblock_start, RECORD_SEP_STR);
1283
         while (token != NULL)
1284
         {
1285
            // evaluate attribute token
1286
            #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
1287
            if      (strncmp (token, "uid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.uid     = (int) read_integer (value); }
1288
            else if (strncmp (token, "gid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.gid     = (int) read_integer (value); }
1289
            else if (strncmp (token, "dperms=",  7) == 0) { REACH_TOKEN_VALUE (); entry_parms.dperms  = (int) read_integer (value); }
1290
            else if (strncmp (token, "perms=",   6) == 0) { REACH_TOKEN_VALUE (); entry_parms.perms   = (int) read_integer (value); }
1291
            else if (strncmp (token, "type=",    5) == 0) { REACH_TOKEN_VALUE (); entry_parms.st_mode = (strcmp (value, "dir") == 0 ? S_IFDIR : (strcmp (value, "file") == 0 ? S_IFREG : (strcmp (value, "link") == 0 ? S_IFLNK : (strcmp (value, "fifo") == 0 ? S_IFIFO : (fprintf (stderr, "warning: invalid 'type' attribute in \"%s\" line %d: '%s', defaulting to 'file'\n", buildfile_pathname, lineno, value), S_IFREG))))); }
1292
            else if (strncmp (token, "prefix=",  7) == 0) { REACH_TOKEN_VALUE (); strcpy (entry_parms.prefix, (*value == '/' ? value + 1 : value)); } // skip possible leading slash in prefix
1293
            else if (strncmp (token, "image=",   6) == 0) { REACH_TOKEN_VALUE ();
1294
               image_base = (uint32_t) read_integer (value); // read image base address
1295
               if ((sep = strchr (value, '-')) != NULL) image_end       = (uint32_t) read_integer (sep + 1); // if we have a dash, read optional image end (FIXME: check this value and produce an error in the relevant case. Not important.)
1296
               if ((sep = strchr (value, ',')) != NULL) image_maxsize   = (uint32_t) read_integer (sep + 1); // if we have a comma, read optional image max size
1297
               if ((sep = strchr (value, '=')) != NULL) image_totalsize = (uint32_t) read_integer (sep + 1); // if we have an equal sign, read optional image padding size
1298
               if ((sep = strchr (value, '%')) != NULL) image_align     = (uint32_t) read_integer (sep + 1); // if we have a modulo sign, read optional image aligmnent
1299
               fprintf (stderr, "info: image 0x%x-0x%x maxsize %d totalsize %d align %d\n", image_base, image_end, image_maxsize, image_totalsize, image_align);
1300
            }
1301
            else if (strncmp (token, "virtual=", 8) == 0) { REACH_TOKEN_VALUE ();
1302
               if ((bootfile_pathname == NULL) || (startupfile_pathname == NULL) || (kernelfile_pathname == NULL)) // HACK until I figure out how to re-create them
1303
               {
1304
                  fprintf (stderr, "error: creating bootable images require the --bootfile, --startupfile and --kernelfile command-line options in \"%s\" line %d\n", buildfile_pathname, lineno);
1305
                  exit (1);
1306
               }
1307
               if ((sep = strchr (value, ',')) != NULL) // do we have a comma separating (optional) processor and boot file name ?
1308
               {
1309
                  *sep = 0;
1310
                  strcpy (image_processor, value); // save processor
1311
                  value = sep + 1;
1312
               }
1313
               //sprintf (image_bootfile, "%s/%s/boot/sys/%s.boot", QNX_TARGET, image_processor, value); // save preboot file name (FIXME: we should search in MKIFS_PATH instead of this. Not important.)
2 pmbaty 1314
               //strcpy (image_bootfile, bootfile_pathname); // FIXME: HACK
1315
               if (stat (bootfile_pathname, &stat_buf) != 0)
1 pmbaty 1316
               {
2 pmbaty 1317
                  fprintf (stderr, "error: unable to stat the boot file \"%s\" specified in \"%s\" line %d: %s\n", bootfile_pathname, buildfile_pathname, lineno, strerror (errno));
1 pmbaty 1318
                  exit (1);
1319
               }
2 pmbaty 1320
               bootfile_size = stat_buf.st_size; // save preboot file size
1321
               fprintf (stderr, "info: processor \"%s\" bootfile \"%s\"\n", image_processor, bootfile_pathname);
1322
               if (stat (kernelfile_pathname, &stat_buf) != 0)
1323
               {
1324
                  fprintf (stderr, "fatal error: unable to read precompiled kernel file \"%s\" specified in --kernelfile argument\n", kernelfile_pathname);
1325
                  exit (1);
1326
               }
1327
               entry_parms.precompiled_data = malloc (stat_buf.st_size);
1328
               if (entry_parms.precompiled_data == NULL)
1329
               {
1330
                  fprintf (stderr, "fatal error: out of memory\n");
1331
                  exit (1);
1332
               }
1333
               fp = fopen ("procnto-smp-instr.bin", "rb");
1334
               fread (entry_parms.precompiled_data, 1, stat_buf.st_size, fp);
1335
               fclose (fp);
1336
               entry_parms.precompiled_datalen = stat_buf.st_size;
1 pmbaty 1337
            }
2 pmbaty 1338
            else if (strcmp (token, "+script") == 0) {
1339
               entry_parms.is_compiled_bootscript = true;
1340
               entry_parms.precompiled_data = INITIAL_STARTUP_SCRIPT; // HACK until the script compiler is implemented
1341
               entry_parms.precompiled_datalen = sizeof (INITIAL_STARTUP_SCRIPT) - 1;
1342
            }
1 pmbaty 1343
            else fprintf (stderr, "warning: unimplemented attribute in \"%s\" line %d: '%s'\n", buildfile_pathname, lineno, token);
1344
            #undef REACH_TOKEN_VALUE
1345
 
1346
            token = strtok (NULL, RECORD_SEP_STR); // proceed to next attribute token
1347
         }
1348
 
1349
         line_ptr++; // reach the next character
1350
         while ((*line_ptr != 0) && isspace (*line_ptr))
1351
            line_ptr++; // skip leading spaces
1352
 
1353
         // are we at the end of the line ? if so, it means the attribute values that are set should become the default
1354
         if ((*line_ptr == 0) || (*line_ptr == '#'))
1355
         {
1356
            #define APPLY_DEFAULT_ATTR_NUM(attr,descr,fmt) do { if (entry_parms.attr != default_parms.attr) { \
1357
                  fprintf (stderr, "info: changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %d\n", default_parms.attr, entry_parms.attr, buildfile_pathname, lineno); \
1358
                  default_parms.attr = entry_parms.attr; \
1359
               } } while (0)
1360
            #define APPLY_DEFAULT_ATTR_STR(attr,descr,fmt) do { if (strcmp (entry_parms.attr, default_parms.attr) != 0) { \
1361
                  fprintf (stderr, "info: changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %d\n", default_parms.attr, entry_parms.attr, buildfile_pathname, lineno); \
1362
                  strcpy (default_parms.attr, entry_parms.attr); \
1363
               } } while (0)
1364
            APPLY_DEFAULT_ATTR_NUM (dperms,  "directory permissions", "0%o");
1365
            APPLY_DEFAULT_ATTR_NUM (perms,   "file permissions",      "0%o");
1366
            APPLY_DEFAULT_ATTR_NUM (uid,     "owner ID",              "%d");
1367
            APPLY_DEFAULT_ATTR_NUM (gid,     "group ID",              "%d");
1368
            APPLY_DEFAULT_ATTR_NUM (st_mode, "inode type",            "0%o");
1369
            APPLY_DEFAULT_ATTR_STR (prefix,  "prefix",                "\"%s\"");
2 pmbaty 1370
            APPLY_DEFAULT_ATTR_NUM (is_compiled_bootscript, "compiled script state", "%d");
1 pmbaty 1371
            #undef APPLY_DEFAULT_ATTR_STR
1372
            #undef APPLY_DEFAULT_ATTR_NUM
1373
            continue; // end of line reached, proceed to the next line
1374
         }
1375
         // end of attributes parsing
1376
      } // end of "this line starts with an attributes block"
1377
 
1378
      // there's data in this line. We expect a filename in the IFS. Read it and unescape escaped characters
1379
      string_len = sprintf (path_in_ifs, "%s", entry_parms.prefix);
1380
      while ((string_len > 0) && (path_in_ifs[string_len - 1] == '/'))
1381
         string_len--; // chop off any trailing slashes from prefix
1382
      write_ptr = &path_in_ifs[string_len];
1383
      *write_ptr++ = '/'; // add ONE trailing slash
1384
      if (*line_ptr == '/')
1385
      {
1386
         fprintf (stderr, "warning: paths in the IFS file should not begin with a leading '/' in \"%s\" line %d\n", buildfile_pathname, lineno);
1387
         line_ptr++; // consistency check: paths in the IFS should not begin with a '/'
1388
      }
1389
      while ((*line_ptr != 0) && (*line_ptr != '=') && !isspace (*line_ptr))
1390
      {
1391
         if (*line_ptr == '\\')
1392
         {
1393
            line_ptr++;
1394
            *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
1395
         }
1396
         else
1397
            *write_ptr++ = *line_ptr;
1398
         line_ptr++;
1399
      }
1400
      *write_ptr = 0; // terminate the string
1401
 
1402
      // we reached a space OR an equal sign
1403
      while ((*line_ptr != 0) && isspace (*line_ptr))
1404
         line_ptr++; // skip optional spaces after the filename in the IFS
1405
 
1406
      blob_data = NULL;
1407
      blob_datasize = 0;
1408
      blob_datalen = 0;
1409
 
1410
      // do we have an equal sign ?
1411
      if (*line_ptr == '=') // we must be creating either a directory or a file, do we have an equal sign ?
1412
      {
1413
         line_ptr++; // skip the equal sign
1414
         while ((*line_ptr != 0) && isspace (*line_ptr))
1415
            line_ptr++; // skip optional spaces after the equal sign
1416
 
1417
         if (*line_ptr == 0)
1418
         {
1419
            fprintf (stderr, "warning: syntax error in \"%s\" line %d: missing data specification after equal sign (skipping)\n", buildfile_pathname, lineno);
1420
            continue; // invalid symlink specification, skip line
1421
         }
1422
 
1423
         // read the host system's path, it may be either a path or a contents definition. Is it a content definition ?
1424
         if (*line_ptr == '{')
1425
         {
1426
            line_ptr++; // skip the leading content definition
1427
            is_escaped_char = false;
1428
            for (;;)
1429
            {
1430
               read_char = fgetc (buildfile_fp);
1431
               if (read_char == EOF)
1432
               {
1433
                  fprintf (stderr, "fatal error: syntax error in \"%s\" line %d: unterminated contents block (end of file reached)\n", buildfile_pathname, lineno);
1434
                  exit (1); // invalid contents block
1435
               }
1436
               else if (read_char == '\\')
1437
                  is_escaped_char = true; // remember the next char is escaped
1438
               else if ((read_char == '}') && !is_escaped_char)
1439
                  break; // found an unescaped closing bracked, stop parsing
1440
               else
1441
               {
1442
                  is_escaped_char = false; // any other char, meaning the next one will not be escaped
1443
                  if (blob_datalen == blob_datasize) // reallocate in 4 kb blocks
1444
                  {
1445
                     reallocated_ptr = realloc (blob_data, blob_datasize + 4096);
1446
                     if (reallocated_ptr == NULL)
1447
                     {
1448
                        fprintf (stderr, "fatal error: out of memory\n");
1449
                        exit (1);
1450
                     }
1451
                     blob_data = reallocated_ptr;
1452
                     blob_datasize += 4096;
1453
                  }
1454
                  blob_data[blob_datalen++] = read_char;
1455
                  if (read_char == '\n')
1456
                     lineno++;
1457
               }
1458
            }
1459
         }
1460
         else // not a content definition between { brackets }, meaning it's a build host filesystem path
1461
         {
1462
            blob_data = line_ptr; // explicit pathname on build host
1463
            blob_datalen = 0;
1464
         }
1465
      }
1466
      else // no equal sign, meaning the file will have the same name on the build host filesystem
1467
      {
1468
         // consistency check: symlinks MUST have an equal sign
1469
         if (entry_parms.st_mode == S_IFLNK)
1470
         {
1471
            fprintf (stderr, "warning: syntax error in \"%s\" line %d: missing equal sign and symlink target (skipping)\n", buildfile_pathname, lineno);
1472
            continue; // invalid symlink specification, skip line
1473
         }
1474
 
1475
         blob_data = &path_in_ifs[strlen (entry_parms.prefix) + 1]; // same pathname
1476
         blob_datalen = 0;
1477
      }
1478
 
1479
      // now add this entry to the image filesystem
1480
      entry_parms.st_mode |= (S_ISDIR (entry_parms.st_mode) ? entry_parms.dperms : entry_parms.perms); // complete entry permissions
1481
      add_fsentry (&fsentries, &fsentry_count, path_in_ifs, &entry_parms, blob_data, blob_datalen); // and add filesystem entry
1482
      if (blob_datasize > 0)
1483
         free (blob_data); // if blob data was allocated, free it
1484
   }
1485
 
1486
   // sort the filesystem entries by pathname
1487
   qsort (fsentries, fsentry_count, sizeof (fsentry_t), fsentry_compare_pathnames_cb);
1488
 
1489
   // calculate filesystem entries size
1490
   imgdir_size = sizeof (image_header);
1491
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
1492
   {
1493
      fprintf (stderr, "info: sorted entry: %s\n", (S_ISDIR (fsentries[fsentry_index].header.mode) ? fsentries[fsentry_index].u.dir.path : (S_ISREG (fsentries[fsentry_index].header.mode) ? fsentries[fsentry_index].u.file.path : (S_ISLNK (fsentries[fsentry_index].header.mode) ? fsentries[fsentry_index].u.symlink.path : fsentries[fsentry_index].u.device.path))));
1494
      imgdir_size += fwrite_fsentry (&fsentries[fsentry_index], NULL);
1495
   }
1496
   fprintf (stderr, "info: image directory size: %zd (0x%zx)\n", imgdir_size, imgdir_size);
1497
 
1498
   // write IFS file
1499
   fp = fopen (ifs_pathname, "wb");
1500
   if (fp == NULL)
1501
   {
1502
      fprintf (stderr, "error: failed to open \"%s\" for writing (%s)\n", ifs_pathname, strerror (errno));
1503
      exit (1);
1504
   }
1505
 
2 pmbaty 1506
   // do we have a startup file ? if so, this is a bootable image
1507
   if (startupfile_pathname != NULL)
1 pmbaty 1508
   {
1509
      // write boot prefix
2 pmbaty 1510
      fwrite_filecontents (bootfile_pathname, fp);
1 pmbaty 1511
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1512
 
1513
      startupheader_offset = ftell (fp); // save startup header offset
1514
      memset (&startup_header, 0, sizeof (startup_header)); // prepare startup header
1515
      memcpy (startup_header.signature, "\xeb\x7e\xff\x00", 4); // startup header signature, i.e. 0xff7eeb
1516
      startup_header.version       = 1;
1517
      startup_header.flags1        = STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2; // flags, 0x21 (STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2)
1518
      startup_header.header_size   = sizeof (startup_header); // 256
1519
      if (strcmp (image_processor, "x86_64") == 0)
1520
         startup_header.machine = STARTUP_HDR_MACHINE_X86_64; // EM_X86_64
1521
      else if (strcmp (image_processor, "aarch64le") == 0)
1522
         startup_header.machine = STARTUP_HDR_MACHINE_AARCH64; // EM_AARCH64
1523
      else
1524
      {
1525
         fprintf (stderr, "fatal error: unsupported processor type '%s' found in build file \"%s\"\n", image_processor, buildfile_pathname);
1526
         exit (1);
1527
      }
1528
      startup_header.startup_vaddr = image_base + (uint32_t) startupfile_ep_from_imagebase; // [I ] Virtual Address to transfer to after IPL is done, here 0x01403008 (appears in "Entry" column for "startup.*")
2 pmbaty 1529
      startup_header.image_paddr   = image_base + (uint32_t) bootfile_size;                 // F[IS] Physical address of image, here 0x01400f30 (appears in "Offset" column for "startup-header" which is the first entry/start of file)
1 pmbaty 1530
      startup_header.ram_paddr     = startup_header.image_paddr;                            // [IS] Physical address of RAM to copy image to (startup_size bytes copied), here 0x01400f30 (same as above)
1531
      startup_header.ram_size      = WILL_BE_FILLED_LATER;                                  // [ S] Amount of RAM used by the startup program and executables contained in the file system, here 0x00cd6128 i.e. 13 459 752 dec. which is 13 Mb. i.e. IFS file size minus 0x9eee (40686)
1532
      startup_header.startup_size  = WILL_BE_FILLED_LATER;                                  // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes
2 pmbaty 1533
      startup_header.stored_size   = WILL_BE_FILLED_LATER;                                  // [I ] Size of entire image, here 0x00cd6128 (same as ram_size)
1 pmbaty 1534
      startup_header.imagefs_size  = WILL_BE_FILLED_LATER;                                  // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes
2 pmbaty 1535
      startup_header.preboot_size  = (uint16_t) bootfile_size;                              // [I ] Size of loaded before header, here 0xf30 or 3888 bytes (size of "bios.boot" file))
1 pmbaty 1536
      fwrite (&startup_header, sizeof (startup_header), 1, fp); // write startup header
1537
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1538
 
1539
      // ######################################################################################################################################################################################################################################
1540
      // # FIXME: figure out how to re-create it: linker call involved
1541
      // # $ x86_64-pc-nto-qnx8.0.0-ld --sysroot=${QNX_TARGET}/x86_64/ -T${QNX_TARGET}/x86_64/lib/nto.link --section-start .text=0x1401030 --no-relax ${QNX_TARGET}/x86_64/boot/sys/startup-x86 -o startup.bin.UNSTRIPPED
1542
      // ######################################################################################################################################################################################################################################
1543
      fwrite_filecontents (startupfile_pathname, fp); // write startup code from blob file
1544
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1545
 
1546
      startuptrailer_offset = ftell (fp); // save startup trailer offset
1547
      fwrite (&startup_trailer, sizeof (startup_trailer), 1, fp); // write startup trailer
1548
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1549
   }
1550
 
1551
   imgheader_offset = ftell (fp); // save image header offset
1552
   memset (&image_header, 0, sizeof (image_header)); // prepare image header
1553
   memcpy (&image_header.signature, "imagefs", 7); // image filesystem signature, i.e. "imagefs"
1554
   image_header.flags         = IMAGE_FLAGS_TRAILER_V2 | IMAGE_FLAGS_SORTED | IMAGE_FLAGS_INO_BITS; // endian neutral flags, 0x1c (IMAGE_FLAGS_TRAILER_V2 | IMAGE_FLAGS_SORTED | IMAGE_FLAGS_INO_BITS)
1555
   image_header.image_size    = WILL_BE_FILLED_LATER; // size from header to end of trailer (here 0xca6fe0 or 13 266 912)
1556
   image_header.hdr_dir_size  = (uint32_t) imgdir_size; // size from header to last dirent (here 0x12b8 or 4792)
1557
   image_header.dir_offset    = sizeof (image_header); // offset from header to first dirent (here 0x5c or 92)
1558
   image_header.boot_ino[0]   = image_kernel_ino; // inode of files for bootstrap p[ro?]g[ra?]ms (here 0xa0000002, 0, 0, 0)
1559
   image_header.script_ino    = image_bootscript_ino; // inode of file for script (here 3)
1560
   image_header.mountpoint[0] = '/'; // default mountpoint for image ("/" + "\0\0\0")
1561
   fwrite (&image_header, sizeof (image_header), 1, fp); // write image header
1562
   PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1563
 
1564
   // write empty image directory
1565
   imgdir_offset = ftell (fp);
1566
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
1567
      fwrite_fsentry (&fsentries[fsentry_index], fp);
1568
   PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1569
 
1570
   // is it a bootable image with a kernel file ?
2 pmbaty 1571
   if ((startupfile_pathname != NULL) && (kernelfile_pathname != NULL))
1 pmbaty 1572
   {
1573
      // sort the filesystem entries by sizes
1574
      qsort (fsentries, fsentry_count, sizeof (fsentry_t), fsentry_compare_sizes_cb);
1575
 
1576
      // write as many small files as we can before reaching the kernel offset 
1577
      for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
1578
      {
1579
         if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written)
1580
            continue; // skip all entries that don't have a separate data block and those who were written already
1581
         curr_offset = ftell (fp);
1582
         if (curr_offset + fsentries[fsentry_index].u.file.size >= kernelfile_offset)
1583
            break; // stop writing entries as soon as we reach the kernel file offset
1584
         fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imgheader_offset); // save file data blob offset in file structure
1585
         fwrite (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob
1586
         PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1587
         fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1588
      }
1589
      PAD_OUTFILE_TO (kernelfile_offset); // reach the kernel offset
1590
 
1591
      // ######################################################################################################################################################################################################################################
1592
      // # FIXME: figure out how to re-create it: linker call involved
1593
      // # $ x86_64-pc-nto-qnx8.0.0-ld --sysroot=${QNX_TARGET}/x86_64/ -T${QNX_TARGET}/x86_64/lib/nto.link --section-start .text=0xffff800000001000 --no-relax ${QNX_TARGET}/x86_64/boot/sys/procnto-smp-instr -o procnto-smp-instr.sym.UNSTRIPPED
1594
      // ######################################################################################################################################################################################################################################
1595
      fwrite_filecontents (kernelfile_pathname, fp); // write kernel from blob file
1596
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1597
   }
1598
 
1599
   // then write all the other files
1600
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
1601
   {
1602
      if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written)
1603
         continue; // skip all entries that don't have a separate data block and those who were written already
1604
      curr_offset = ftell (fp);
1605
      fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imgheader_offset); // save file data blob offset in file structure
1606
      fwrite (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob
1607
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1608
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1609
   }
1610
 
1611
   // finally, write trailer (including empty checksum)
1612
   imgtrailer_offset = ftell (fp); // save image trailer offset
1613
   fwrite (&image_trailer, sizeof (image_trailer), 1, fp); // write image trailer
1614
   PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1615
 
1616
   // if we need to pad it to a specific length, do so
1617
   PAD_OUTFILE_TO (image_totalsize);
1618
   final_size = ftell (fp);
1619
 
1620
   // see if we are past the image max size, in which case it's an error
1621
   if (final_size > image_maxsize)
1622
   {
1623
      fprintf (stderr, "error: image file \"%s\" size %zd exceeds max size (%zd)\n", ifs_pathname, final_size, (size_t) image_maxsize);
1624
      exit (1);
1625
   }
1626
 
2 pmbaty 1627
   // do we have a startup file ? if so, this is a bootable image
1628
   if (startupfile_pathname != NULL)
1 pmbaty 1629
   {
1630
      // rewrite startup header with final values
1631
      fseek (fp, startupheader_offset, SEEK_SET);
1632
      startup_header.startup_size = (uint32_t) (imgheader_offset - startupheader_offset); // size of startup header up to image header
1633
      startup_header.imagefs_size = (uint32_t) (final_size - imgheader_offset); // size of uncompressed imagefs
1634
      startup_header.ram_size = (uint32_t) final_size; // FIXME: this is necessarily a bit less, but should we really bother calculating the right size ?
2 pmbaty 1635
      startup_header.stored_size = startup_header.ram_size;
1 pmbaty 1636
      fwrite (&startup_header, sizeof (startup_header), 1, fp); // write startup header
1637
 
1638
      // compute SHA-512 checksum and V1 checksum of startup block
1639
      blob_datasize = startuptrailer_offset - startupheader_offset;
1640
      blob_data = malloc (blob_datasize);
1641
      if (blob_data == NULL)
1642
      {
1643
         fprintf (stderr, "fatal error: out of memory\n");
1644
         exit (1);
1645
      }
1646
      fseek (fp, startupheader_offset, SEEK_SET);
1647
      fread (blob_data, 1, blob_datasize, fp);
1648
      SHA512 (blob_data, blob_datasize, startup_trailer.sha512); // compute SHA512 checksum
1649
      startup_trailer.cksum = 0; // compute old checksum
2 pmbaty 1650
      startup_trailer.cksum = update_checksum32 (startup_trailer.cksum, (const uint32_t *) blob_data, blob_datasize);
1651
      startup_trailer.cksum = update_checksum32 (startup_trailer.cksum, (const uint32_t *) startup_trailer.sha512, sizeof (startup_trailer.sha512));
1 pmbaty 1652
      free (blob_data);
1653
 
1654
      // rewrite startup trailer with final values
1655
      fseek (fp, startuptrailer_offset, SEEK_SET);
1656
      fwrite (&startup_trailer, sizeof (startup_trailer), 1, fp); // write startup trailer
1657
   }
1658
 
1659
   // rewrite image header with final values
1660
   fseek (fp, imgheader_offset, SEEK_SET);
1661
   image_header.image_size = (uint32_t) (final_size - imgheader_offset); // size of uncompressed imagefs
1662
   fwrite (&image_header, sizeof (image_header), 1, fp); // write image header
1663
 
1664
   // rewrite image directory with final checksum values
1665
   fseek (fp, imgdir_offset, SEEK_SET);
1666
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
1667
      fwrite_fsentry (&fsentries[fsentry_index], fp);
1668
 
1669
   // compute SHA-512 checksum and V1 checksum of image block
1670
   blob_datasize = imgtrailer_offset - imgheader_offset;
1671
   blob_data = malloc (blob_datasize);
1672
   if (blob_data == NULL)
1673
   {
1674
      fprintf (stderr, "fatal error: out of memory\n");
1675
      exit (1);
1676
   }
1677
   fseek (fp, imgheader_offset, SEEK_SET);
1678
   fread (blob_data, 1, blob_datasize, fp);
1679
   SHA512 (blob_data, blob_datasize, image_trailer.sha512); // compute SHA512 checksum
1680
   image_trailer.cksum = 0; // compute old checksum
2 pmbaty 1681
   image_trailer.cksum = update_checksum32 (image_trailer.cksum, (const uint32_t *) blob_data, blob_datasize);
1682
   image_trailer.cksum = update_checksum32 (image_trailer.cksum, (const uint32_t *) image_trailer.sha512, sizeof (image_trailer.sha512));
1 pmbaty 1683
   free (blob_data);
1684
 
1685
   // rewrite image trailer with final checksum values
1686
   fseek (fp, imgtrailer_offset, SEEK_SET);
1687
   fwrite (&image_trailer, sizeof (image_trailer), 1, fp); // write image trailer
1688
 
1689
   // finished, close IFS file and exit with a success code
1690
   fclose (fp);
1691
   fprintf (stdout, "Success\n");
1692
   exit (0);
1693
}
2 pmbaty 1694
 
1695
 
1696
static int dump_ifs_info (const char *ifs_pathname)
1697
{
1698
   #define hex_printf(buf,size,...) hex_fprintf (stdout, (buf), (size), 16, __VA_ARGS__) // use 16 columns in hex output to stdout
1699
   #define BINARY(x) binary ((x), '-', 'x')
1700
 
1701
   static const char *startupheader_flags1_strings[8] = {
1702
      "VIRTUAL", // bit 0
1703
      "BIGENDIAN", // bit 1
1704
      "COMPRESS_BIT1", // bit 2
1705
      "COMPRESS_BIT2", // bit 3
1706
      "COMPRESS_BIT3", // bit 4
1707
      "TRAILER_V2", // bit 5
1708
      "", // bit 6
1709
      "", // bit 7
1710
   };
1711
   static const char *imageheader_flags_strings[8] = {
1712
      "BIGENDIAN", // bit 0
1713
      "READONLY", // bit 1
1714
      "INO_BITS", // bit 2
1715
      "SORTED", // bit 3
1716
      "TRAILER_V2", // bit 4
1717
      "", // bit 5
1718
      "", // bit 6
1719
      "", // bit 7
1720
   };
1721
 
1722
   startup_header_t *startup_header = NULL;
1723
   startup_trailer_v1_t *startup_trailer_v1 = NULL;
1724
   startup_trailer_v2_t *startup_trailer_v2 = NULL;
1725
   image_header_t *image_header = NULL;
1726
   size_t imageheader_offset = 0;
1727
   image_trailer_v1_t *image_trailer_v1 = NULL;
1728
   image_trailer_v2_t *image_trailer_v2 = NULL;
1729
   fsentry_t *current_fsentry = NULL;
1730
   char recorded_sha512[2 * SHA512_DIGEST_LENGTH + 1] = "";
1731
   char computed_sha512[2 * SHA512_DIGEST_LENGTH + 1] = "";
1732
   size_t startupfile_blobsize = 0;
1733
   size_t bootfile_blobsize = 0;
1734
   size_t current_offset;
1735
   size_t byte_index;
1736
   uint32_t recorded_checksum;
1737
   uint32_t computed_checksum;
1738
   uint8_t *filedata;
1739
   size_t filesize;
1740
   time_t mtime;
1741
   FILE *fp;
1742
 
1743
   // open and read IFS file
1744
   fp = fopen (ifs_pathname, "rb");
1745
   if (fp == NULL)
1746
   {
1747
      fprintf (stderr, "error: can't open \"%s\" for reading: %s\n", ifs_pathname, strerror (errno));
1748
      return (1);
1749
   }
1750
   fseek (fp, 0, SEEK_END);
1751
   filesize = ftell (fp);
1752
   filedata = malloc (filesize);
1753
   if (filedata == NULL)
1754
   {
1755
      fprintf (stderr, "fatal error: out of memory\n");
1756
      exit (1);
1757
   }
1758
   fseek (fp, 0, SEEK_SET);
1759
   fread (filedata, 1, filesize, fp);
1760
   fclose (fp);
1761
 
1762
   printf ("IFS file \"%s\" - size 0x%zx (%zd) bytes\n", ifs_pathname, filesize, filesize);
1763
 
1764
   // parse file from start to end
1765
   current_offset = 0;
1766
   for (;;)
1767
   {
1768
      // does a startup header start here ?
1769
      if ((current_offset + sizeof (startup_header_t) < filesize) && (memcmp (&filedata[current_offset], "\xeb\x7e\xff\x00", 4) == 0))
1770
      {
1771
         startup_header = (startup_header_t *) &filedata[current_offset];
1772
 
1773
         // layout:
1774
         // [STARTUP HEADER]
1775
         // (startup file blob)
1776
         // [STARTUP TRAILER v1 or v2]
1777
 
1778
         printf ("\n");
1779
         printf ("Startup header at offset 0x%zx (%zd):\n", current_offset, current_offset);
1780
         printf ("   signature     = %02x %02x %02x %02x - good\n", startup_header->signature[0], startup_header->signature[1], startup_header->signature[2], startup_header->signature[3]);
1781
         printf ("   version       = 0x%04x (%d) - %s\n", startup_header->version, startup_header->version, (startup_header->version == 1 ? "looks good" : "???"));
1782
         printf ("   flags1        = 0x%02x (%s)\n", startup_header->flags1, describe_uint8 (startup_header->flags1, startupheader_flags1_strings));
1783
         printf ("   flags2        = 0x%02x (%s) - %s\n", startup_header->flags2, BINARY (startup_header->flags2), (startup_header->flags2 == 0 ? "looks good" : "???"));
1784
         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"));
1785
         printf ("   machine       = 0x%04x (%d) - %s\n", startup_header->machine, startup_header->machine, (startup_header->machine == STARTUP_HDR_MACHINE_X86_64 ? "x86_64" : (startup_header->machine == STARTUP_HDR_MACHINE_AARCH64 ? "aarch64" : "unknown")));
1786
         printf ("   startup_vaddr = 0x%08x (%d) - virtual address to transfer to after IPL is done\n", startup_header->startup_vaddr, startup_header->startup_vaddr);
1787
         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);
1788
         printf ("   image_paddr   = 0x%08x (%d) - physical address of image\n", startup_header->image_paddr, startup_header->image_paddr);
1789
         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);
1790
         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);
1791
         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)) < filesize ? "looks good" : "BAD (IFS file too short)"));
1792
         printf ("   stored_size   = 0x%08x (%d) - size of entire image - %s\n", startup_header->stored_size, startup_header->stored_size, (startup_header->stored_size == startup_header->ram_size ? "looks good" : "???"));
1793
         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"));
1794
         printf ("   imagefs_size  = 0x%08x (%d) - size of uncompressed imagefs\n", startup_header->imagefs_size, startup_header->imagefs_size);
1795
         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" : "???"));
1796
         printf ("   zero0         = 0x%04x (%d) - zeros - %s\n", startup_header->zero0, startup_header->zero0, (startup_header->zero0 == 0 ? "looks good" : "??? should be zero"));
1797
         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"));
1798
         printf ("   addr_off      = 0x%016llx (%lld) - offset for startup_vaddr and [image|ram|imagefs]_paddr - %s\n", startup_header->addr_off, startup_header->addr_off, (startup_header->addr_off == 0 ? "looks good" : "??? should be zero"));
1799
         hex_printf ((uint8_t *) &startup_header->info[0], sizeof (startup_header->info), "   info[48] =\n");
1800
 
1801
         // validate that the file can contain up to the startup trailer
1802
         if (current_offset + startup_header->startup_size > filesize)
1803
         {
1804
            fprintf (stderr, "WARNING: this IFS file is too short (startup trailer would be past end of file)\n");
1805
            break;
1806
         }
1807
 
1808
         // locate the right startup trailer at the right offset
1809
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
1810
         {
1811
            startup_trailer_v2 = (startup_trailer_v2_t *) &filedata[current_offset + startup_header->startup_size - sizeof (startup_trailer_v2_t)];
1812
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v2_t);
1813
         }
1814
         else // old V1 trailer
1815
         {
1816
            startup_trailer_v1 = (startup_trailer_v1_t *) &filedata[current_offset + startup_header->startup_size - sizeof (startup_trailer_v1_t)];
1817
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v1_t);
1818
         }
1819
 
1820
         current_offset += sizeof (startup_header_t); // jump over the startup header and reach the startup blob
1821
         printf ("\n");
1822
         printf ("Startup blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
1823
         printf ("   size 0x%zx (%zd) bytes\n", startupfile_blobsize, startupfile_blobsize);
1824
         printf ("   checksum %d\n", update_checksum32 (0, (uint32_t *) &filedata[current_offset], startupfile_blobsize));
1825
 
1826
         current_offset += startupfile_blobsize; // jump over the startup blob and reach the startup trailer
1827
         printf ("\n");
1828
         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));
1829
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
1830
         {
1831
            for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
1832
               sprintf (&recorded_sha512[2 * byte_index], "%02x", startup_trailer_v2->sha512[byte_index]);
1833
            strcpy (computed_sha512, SHA512 (startup_header, (size_t) ((uint8_t *) startup_trailer_v2 - (uint8_t *) startup_header), NULL));
1834
            recorded_checksum = startup_trailer_v2->cksum;
1835
            computed_checksum = update_checksum32 (0, (uint32_t *) startup_header, sizeof (startup_header) + startupfile_blobsize + SHA512_DIGEST_LENGTH);
1836
            printf ("    sha512 = %s - %s\n", recorded_sha512, (strcasecmp (computed_sha512, recorded_sha512) == 0 ? "GOOD" : "BAD"));
1837
            printf ("    cksum = 0x%08x (%d) - %s\n", recorded_checksum, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
1838
            if (strcasecmp (computed_sha512, recorded_sha512) != 0)
1839
               printf ("Computed SHA-512: %s\n", computed_sha512);
1840
            if (computed_checksum != recorded_checksum)
1841
               printf ("Computed cksum: 0x%08x (%d)\n", computed_checksum, computed_checksum);
1842
         }
1843
         else // old v1 trailer
1844
         {
1845
            recorded_checksum = startup_trailer_v1->cksum;
1846
            computed_checksum = update_checksum32 (0, (uint32_t *) startup_header, sizeof (startup_header) + startupfile_blobsize);
1847
            printf ("    cksum = 0x%08x (%d) - %s\n", recorded_checksum, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
1848
            if (computed_checksum != recorded_checksum)
1849
               printf ("Computed cksum: 0x%08x (%d)\n", computed_checksum, computed_checksum);
1850
         }
1851
 
1852
         current_offset += (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2 ? sizeof (image_trailer_v2_t) : sizeof (image_trailer_v1_t)); // now reach the next segment
1853
      }
1854
 
1855
      // else does an image header start here ?
1856
      else if ((current_offset + sizeof (image_header_t) < filesize) && (memcmp (&filedata[current_offset], "imagefs", 7) == 0))
1857
      {
1858
         imageheader_offset = current_offset;
1859
         image_header = (image_header_t *) &filedata[imageheader_offset];
1860
 
1861
         // layout:
1862
         // [IMAGE HEADER]
1863
         // [image directory entries]
1864
         // [file blobs up to KERNEL]
1865
         // [padding]
1866
         // [KERNEL]
1867
         // [file blobs]
1868
         // [IMAGE FOOTER]
1869
 
1870
         printf ("\n");
1871
         printf ("Image header at offset %zx (%zd):\n", current_offset, current_offset);
1872
         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);
1873
         printf ("   flags        = 0x%02x (%s)\n", image_header->flags, describe_uint8 (image_header->flags, imageheader_flags_strings));
1874
         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 <= filesize ? "looks good" : "BAD (IFS file too short)"));
1875
         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 < filesize ? "looks good" : "BAD (IFS file too short)"));
1876
         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 >= filesize ? "BAD (IFS file too short)" : (image_header->dir_offset > image_header->hdr_dir_size ? "BAD" : "looks good")));
1877
         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]);
1878
         printf ("   script_ino   = 0x%08x (%d) - inode of compiled bootscript\n", image_header->script_ino, image_header->script_ino);
1879
         printf ("   chain_paddr  = 0x%08x (%d) - offset to next fs signature\n", image_header->chain_paddr, image_header->chain_paddr);
1880
         hex_printf ((uint8_t *) &image_header->spare[0], sizeof (image_header->spare), "   spare[10] =\n");
1881
         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]));
1882
         printf ("   mountpoint   = \"%s\"\n", image_header->mountpoint);
1883
 
1884
         // validate that the file can contain up to the image trailer
1885
         if (current_offset + image_header->image_size > filesize)
1886
         {
1887
            fprintf (stderr, "WARNING: this IFS file is too short (image trailer would be past end of file)\n");
1888
            break;
1889
         }
1890
 
1891
         // locate the image trailer at the right offset
1892
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
1893
            image_trailer_v2 = (image_trailer_v2_t *) &filedata[current_offset + image_header->image_size - sizeof (image_trailer_v2_t)];
1894
         else // old V1 trailer
1895
            image_trailer_v1 = (image_trailer_v1_t *) &filedata[current_offset + image_header->image_size - sizeof (image_trailer_v1_t)];
1896
 
1897
         current_offset += sizeof (image_header_t); // jump over the image header and reach the first directory entry
1898
         if (image_header->dir_offset - sizeof (image_header_t) > 0)
1899
         {
1900
            hex_printf (&filedata[current_offset], image_header->dir_offset - sizeof (image_header_t), "\n%d extra bytes at offset 0x%zd (%zd):\n", image_header->dir_offset - sizeof (image_header_t), current_offset, current_offset);
1901
            current_offset += image_header->dir_offset - sizeof (image_header_t);
1902
         }
1903
 
1904
         // dump all directory entries until the last one included
1905
         while (&filedata[current_offset] < (uint8_t *) image_header + image_header->hdr_dir_size)
1906
         {
1907
            current_fsentry = (fsentry_t *) &filedata[current_offset];
1908
 
1909
            if (imageheader_offset + image_header->hdr_dir_size - current_offset < sizeof (current_fsentry->header))
1910
               break; // end padding reached
1911
 
1912
            printf ("\n");
1913
            printf ("Filesystem entry at offset 0x%zx (%zd) - last one at 0x%zd (%zd):\n", current_offset, current_offset, imageheader_offset + image_header->hdr_dir_size, imageheader_offset + image_header->hdr_dir_size);
1914
            printf ("   size           = 0x%04x (%d) - size of dirent - %s\n", current_fsentry->header.size, current_fsentry->header.size, (current_offset + current_fsentry->header.size < filesize ? "looks good" : "BAD"));
1915
            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"));
1916
            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" : ""));
1917
            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);
1918
            printf ("   gid            = 0x%08x (%d) - owner group ID%s\n", current_fsentry->header.gid, current_fsentry->header.gid, (current_fsentry->header.gid == 0 ? " (root)" : ""));
1919
            printf ("   uid            = 0x%08x (%d) - owner user ID%s\n", current_fsentry->header.uid, current_fsentry->header.uid, (current_fsentry->header.uid == 0 ? " (root)" : ""));
1920
            mtime = (time_t) current_fsentry->header.mtime;
1921
            printf ("   mtime          = 0x%08x (%d) - POSIX timestamp: %s", current_fsentry->header.mtime, current_fsentry->header.mtime, asctime (localtime (&mtime))); // NOTE: asctime() provides the newline
1922
            if (S_ISDIR (current_fsentry->header.mode))
1923
               printf ("   [DIRECTORY] path = \"%s\"\n", (char *) &current_fsentry->u.dir.path); // convert from pointer to char array
1924
            else if (S_ISREG (current_fsentry->header.mode))
1925
            {
1926
               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 < filesize ? "looks good" : "BAD (IFS file too short)"));
1927
               printf ("   [FILE] size   = 0x%08x (%d) - %s\n", current_fsentry->u.file.offset, current_fsentry->u.file.offset, (imageheader_offset + current_fsentry->u.file.offset + current_fsentry->u.file.size < filesize ? "looks good" : "BAD (IFS file too short)"));
1928
               printf ("   [FILE] path   = \"%s\"\n", (char *) &current_fsentry->u.file.path); // convert from pointer to char array
1929
            }
1930
            else if (S_ISLNK (current_fsentry->header.mode))
1931
            {
1932
               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)"));
1933
               printf ("   [SYMLINK] sym_size   = 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->u.symlink.sym_size <= current_fsentry->header.size ? "looks good" : "BAD (dirent too short)"));
1934
               printf ("   [SYMLINK] path       = \"%s\"\n", (char *) &current_fsentry->u.symlink.path); // convert from pointer to char array
1935
               printf ("   [SYMLINK] contents   = \"%s\"\n", ((char *) &current_fsentry->u.symlink.path) + current_fsentry->u.symlink.sym_offset); // convert from pointer to char array
1936
            }
1937
            else // can only be a device
1938
            {
1939
               printf ("   [DEVICE] dev  = 0x%08x (%d)\n", current_fsentry->u.device.dev, current_fsentry->u.device.dev);
1940
               printf ("   [DEVICE] rdev = 0x%08x (%d)\n", current_fsentry->u.device.rdev, current_fsentry->u.device.rdev);
1941
               printf ("   [DEVICE] path = \"%s\"\n", (char *) &current_fsentry->u.device.path); // convert from pointer to char array
1942
            }
1943
 
1944
            current_offset += current_fsentry->header.size;
1945
         }
1946
         if (imageheader_offset + image_header->hdr_dir_size - current_offset < sizeof (current_fsentry->header))
1947
            hex_printf (&filedata[current_offset], imageheader_offset + image_header->hdr_dir_size - current_offset, "\n%d padding bytes at offset 0x%zx (%zd):\n", imageheader_offset + image_header->hdr_dir_size - current_offset, current_offset, current_offset);
1948
 
1949
 
1950
            // now jump at the first
1951
 
1952
         exit (0);
1953
      }
1954
 
1955
      // 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
1956
      else
1957
      {
1958
         // 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)
1959
         for (byte_index = current_offset; byte_index < filesize - 6; byte_index++)
1960
            if (memcmp (&filedata[byte_index], "\xeb\x7e\xff\x00" "\x01\x00", 4 + 2) == 0)
1961
               break; // stop as soon as we find it
1962
 
1963
         if (byte_index >= filesize - 6)
1964
            break; // if not found, stop scanning
1965
 
1966
         bootfile_blobsize = byte_index - current_offset;
1967
         printf ("Boot blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
1968
         printf ("   size 0x%zx (%zd) bytes\n", bootfile_blobsize, bootfile_blobsize);
1969
         printf ("   checksum %d\n", update_checksum32 (0, (uint32_t *) &filedata[current_offset], bootfile_blobsize));
1970
 
1971
         current_offset = byte_index; // now reach the next segment
1972
      }
1973
   }
1974
 
1975
   printf ("End of identifiable data reached.\n");
1976
   if (current_offset < filesize)
1977
      hex_printf (&filedata[current_offset], filesize - current_offset, "\n%d extra bytes at offset %zx (%zd):\n", filesize - current_offset, current_offset, current_offset);
1978
   printf ("End of file reached at offset 0x%zx (%zd).\n", filesize, filesize);
1979
 
1980
   return (0);
1981
}