Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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