Subversion Repositories QNX 8.QNX8 IFS tool

Rev

Rev 5 | Rev 7 | 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
6 pmbaty 342
   uint32_t image_size; // size from start of header to end of trailer (here 0xca6fe0 or 13 266 912)
343
   uint32_t hdr_dir_size; // size from start of header to last dirent (here 0x12b8 or 4792)
344
   uint32_t dir_offset; // offset from start of header to start of first dirent (here 0x5c or 92)
1 pmbaty 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())
6 pmbaty 412
static int32_t update_checksum (const int32_t start_value, const void *data, const size_t data_len, const bool is_foreign_endianness); // compute an IFS image or startup checksum to store in the trailer
1 pmbaty 413
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 414
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)
415
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
416
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 417
static int fwrite_filecontents (const char *pathname, FILE *fp); // dumps the contents of pathname into fp
418
static size_t fwrite_fsentry (const fsentry_t *fsentry, FILE *fp); // writes the given filesystem entry into fp (without its contents)
419
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
420
static int fsentry_compare_pathnames_cb (const void *a, const void *b); // qsort() comparison callback that sorts filesystem entries by pathnames
421
static int fsentry_compare_sizes_cb (const void *a, const void *b); // qsort() comparison callback that sorts filesystem entries by size
2 pmbaty 422
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 423
 
424
 
425
static void sha512_private_transform (SHA512_CTX *context, const uint64_t *data)
426
{
427
   // logical functions used in SHA-384 and SHA-512
428
   #define S64(b,x)      (((x) >> (b)) | ((x) << (64 - (b)))) // 64-bit rotate right
429
   #define Ch(x,y,z)     (((x) & (y)) ^ ((~(x)) & (z)))
430
   #define Maj(x,y,z)    (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
431
   #define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
432
   #define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
433
   #define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ ((x) >> 7))
434
   #define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ ((x) >> 6))
435
 
436
   // hash constant words K for SHA-384 and SHA-512
437
   static const uint64_t K512[80] = {
438
      0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
439
      0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
440
      0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
441
      0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
442
      0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
443
      0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
444
      0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
445
      0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
446
      0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
447
      0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
448
   };
449
 
450
   uint64_t     a, b, c, d, e, f, g, h, s0, s1;
451
   uint64_t     T1, T2, *W512 = (uint64_t *) context->buffer;
452
   int j;
453
 
454
   // initialize registers with the prev. intermediate value
455
   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];
456
 
457
   for (j = 0; j < 16; j++)
458
   {
459
#if __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
460
      W512[j] = __builtin_bswap64 (*data); // convert to host byte order
461
#elif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
462
      W512[j] = *data;
463
#else // __BYTE_ORDER__ == ???
464
#error Please port this SHA-512 code to your exotic endianness platform. What are you compiling this on? PDP? Honeywell?
465
#endif // __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
466
 
467
      // apply the SHA-512 compression function to update a..h
468
      T1 = h + Sigma1_512 (e) + Ch (e, f, g) + K512[j] + W512[j];
469
      T2 = Sigma0_512 (a) + Maj (a, b, c);
470
 
471
      // update registers
472
      h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2;
473
 
474
      data++;
475
   }
476
 
477
   for (; j < 80; j++)
478
   {
479
      // part of the message block expansion
480
      s0 = W512[(j + 1) & 0x0f];
481
      s0 = sigma0_512 (s0);
482
      s1 = W512[(j + 14) & 0x0f];
483
      s1 = sigma1_512 (s1);
484
 
485
      // apply the SHA-512 compression function to update a..h
486
      T1 = h + Sigma1_512 (e) + Ch (e, f, g) + K512[j] + (W512[j & 0x0f] += s1 + W512[(j + 9) & 0x0f] + s0);
487
      T2 = Sigma0_512 (a) + Maj (a, b, c);
488
 
489
      // update registers
490
      h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2;
491
   }
492
 
493
   // compute the current intermediate hash value
494
   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;
495
 
496
   // clean up
497
   a = b = c = d = e = f = g = h = T1 = T2 = 0;
498
   #undef sigma1_512
499
   #undef sigma0_512
500
   #undef Sigma1_512
501
   #undef Sigma0_512
502
   #undef Maj
503
   #undef Ch
504
   #undef S64
505
   return;
506
}
507
 
508
 
509
static void SHA512_Init (SHA512_CTX *context)
510
{
511
   // initial hash value H for SHA-512
512
   static const uint64_t sha512_initial_hash_value[8] = {
513
      0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
514
   };
515
 
516
   memcpy (context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH);
517
   memset (context->buffer, 0, SHA512_BLOCK_LENGTH);
518
   context->bitcount[0] = context->bitcount[1] = 0;
519
}
520
 
521
 
522
void SHA512_Update (SHA512_CTX *context, void *datain, size_t len)
523
{
524
   #define ADDINC128(w,n) do { \
525
           (w)[0] += (uint64_t) (n); \
526
           if ((w)[0] < (n)) \
527
                   (w)[1]++; \
528
   } 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
529
 
530
   size_t freespace, usedspace;
531
   const uint8_t *data = (const uint8_t *) datain;
532
 
533
   if (len == 0)
534
      return; // calling with empty data is valid - we do nothing
535
 
536
   usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
537
   if (usedspace > 0)
538
   {
539
      // calculate how much free space is available in the buffer
540
      freespace = SHA512_BLOCK_LENGTH - usedspace;
541
 
542
      if (len >= freespace)
543
      {
544
         // fill the buffer completely and process it
545
         memcpy (&context->buffer[usedspace], data, freespace);
546
         ADDINC128 (context->bitcount, freespace << 3);
547
         len -= freespace;
548
         data += freespace;
549
         sha512_private_transform (context, (uint64_t *) context->buffer);
550
      }
551
      else
552
      {
553
         // the buffer is not full yet
554
         memcpy (&context->buffer[usedspace], data, len);
555
         ADDINC128 (context->bitcount, len << 3);
556
 
557
         // clean up
558
         usedspace = freespace = 0;
559
         return;
560
      }
561
   }
562
 
563
   while (len >= SHA512_BLOCK_LENGTH)
564
   {
565
      // process as many complete blocks as we can
566
      sha512_private_transform (context, (uint64_t *) data);
567
      ADDINC128 (context->bitcount, SHA512_BLOCK_LENGTH << 3);
568
      len -= SHA512_BLOCK_LENGTH;
569
      data += SHA512_BLOCK_LENGTH;
570
   }
571
 
572
   if (len > 0)
573
   {
574
      // save leftovers
575
      memcpy (context->buffer, data, len);
576
      ADDINC128 (context->bitcount, len << 3);
577
   }
578
 
579
   // clean up
580
   usedspace = freespace = 0;
581
   #undef ADDINC128
582
   return;
583
}
584
 
585
 
586
static void SHA512_Final (uint8_t digest[SHA512_DIGEST_LENGTH], SHA512_CTX *context)
587
{
588
   #define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16)
589
 
590
   size_t usedspace;
591
   union { uint8_t *as_bytes; uint64_t *as_uint64s; } cast_var = { NULL };
592
 
593
   // if no digest buffer is passed, don't bother finalizing the computation
594
   if (digest != NULL)
595
   {
596
      usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
597
 
598
#if __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
599
      context->bitcount[0] = __builtin_bswap64 (context->bitcount[0]); // convert from host byte order
600
      context->bitcount[1] = __builtin_bswap64 (context->bitcount[1]); // convert from host byte order
601
#endif // __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
602
 
603
      if (usedspace > 0)
604
      {
605
         // begin padding with a 1 bit
606
         context->buffer[usedspace++] = 0x80;
607
 
608
         if (usedspace <= SHA512_SHORT_BLOCK_LENGTH)
609
            memset (&context->buffer[usedspace], 0, SHA512_SHORT_BLOCK_LENGTH - usedspace); // set-up for the last transform
610
         else
611
         {
612
            if (usedspace < SHA512_BLOCK_LENGTH)
613
               memset (&context->buffer[usedspace], 0, SHA512_BLOCK_LENGTH - usedspace);
614
 
615
            sha512_private_transform (context, (uint64_t *) context->buffer); // do second-to-last transform
616
            memset (context->buffer, 0, SHA512_BLOCK_LENGTH - 2); // and set-up for the last transform
617
         }
618
      }
619
      else // usedspace == 0
620
      {
621
         memset (context->buffer, 0, SHA512_SHORT_BLOCK_LENGTH); // prepare for final transform
622
         *context->buffer = 0x80; // begin padding with a 1 bit
623
      }
624
 
625
      // store the length of input data (in bits)
626
      cast_var.as_bytes = context->buffer;
627
      cast_var.as_uint64s[SHA512_SHORT_BLOCK_LENGTH / 8 + 0] = context->bitcount[1];
628
      cast_var.as_uint64s[SHA512_SHORT_BLOCK_LENGTH / 8 + 1] = context->bitcount[0];
629
 
630
      // final transform
631
      sha512_private_transform (context, (uint64_t *) context->buffer);
632
 
633
      // save the hash data for output
634
#if __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
635
      for (int j = 0; j < 8; j++)
636
         context->state[j] = __builtin_bswap64 (context->state[j]); // convert to host byte order
637
#endif // __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
638
      memcpy (digest, context->state, SHA512_DIGEST_LENGTH);
639
   }
640
 
641
   // zero out state data
642
   memset (context, 0, sizeof (SHA512_CTX));
643
   #undef SHA512_SHORT_BLOCK_LENGTH
644
   return;
645
}
646
 
647
 
2 pmbaty 648
static uint8_t *SHA512 (void *data, size_t data_len, uint8_t *digest_or_NULL)
1 pmbaty 649
{
2 pmbaty 650
   // 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
651
   // returns the STRING REPRESENTATION of digest in a statically-allocated string
1 pmbaty 652
 
2 pmbaty 653
   static uint8_t static_digest[SHA512_DIGEST_LENGTH] = "";
654
   static char digest_as_string[2 * SHA512_DIGEST_LENGTH + 1] = "";
655
 
1 pmbaty 656
   SHA512_CTX ctx;
2 pmbaty 657
   size_t byte_index;
658
 
1 pmbaty 659
   SHA512_Init (&ctx);
660
   SHA512_Update (&ctx, data, data_len);
2 pmbaty 661
   if (digest_or_NULL == NULL)
662
      digest_or_NULL = static_digest;
663
   SHA512_Final (digest_or_NULL, &ctx);
664
 
665
   for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
666
      sprintf (&digest_as_string[2 * byte_index], "%02x", digest_or_NULL[byte_index]);
667
   return (digest_as_string);
1 pmbaty 668
}
669
 
670
 
6 pmbaty 671
static int32_t update_checksum (const int32_t start_value, const void *data, const size_t data_len, const bool is_foreign_endianness)
1 pmbaty 672
{
6 pmbaty 673
   // computes the checksum of an IFS image or startup section, i.e. from the start of the header to the end of the trailer minus the last 4 bytes where the checksum is stored
1 pmbaty 674
 
5 pmbaty 675
   uint8_t accumulator[4] = { 0, 0, 0, 0 };
676
   int32_t image_cksum = start_value;
6 pmbaty 677
   const char *current_char_ptr = data;
5 pmbaty 678
   size_t i;
679
 
680
   for (i = 0; i < data_len; i++)
681
   {
682
      accumulator[i % 4] = *current_char_ptr;
683
      if (i % 4 == 3)
684
         if (is_foreign_endianness)
685
            image_cksum += (accumulator[3] <<  0) + (accumulator[2] <<  8) + (accumulator[1] << 16) + (accumulator[0] << 24);
686
         else
687
            image_cksum += (accumulator[0] <<  0) + (accumulator[1] <<  8) + (accumulator[2] << 16) + (accumulator[3] << 24);
688
      current_char_ptr++;
689
   }
690
 
691
   return (is_foreign_endianness ? __builtin_bswap32 (-image_cksum) : -image_cksum);
692
}
693
 
694
 
1 pmbaty 695
static long long read_integer (const char *str)
696
{
697
   // 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)
698
 
699
   char *endptr = NULL;
700
   long long ret = strtoll (str, &endptr, 0); // use strtoll() to handle hexadecimal (0x...), octal (0...) and decimal (...) bases
701
   if (endptr != NULL)
702
   {
703
      if ((*endptr == 'k') || (*endptr == 'K')) ret *= (size_t) 1024;
704
      else if ((*endptr == 'm') || (*endptr == 'M')) ret *= (size_t) 1024 * 1024;
705
      else if ((*endptr == 'g') || (*endptr == 'G')) ret *= (size_t) 1024 * 1024 * 1024;
706
      else if ((*endptr == 't') || (*endptr == 'T')) ret *= (size_t) 1024 * 1024 * 1024 * 1024; // future-proof enough, I suppose?
707
   }
708
   return (ret);
709
}
710
 
711
 
2 pmbaty 712
static void hex_fprintf (FILE *fp, const uint8_t *data, size_t data_size, int howmany_columns, const char *fmt, ...)
713
{
714
   // this function logs hexadecimal data to an opened file pointer (or to stdout/stderr)
715
 
716
   va_list argptr;
717
   size_t index;
718
   int i;
719
 
720
   // concatenate all the arguments in one string and write it to the file
721
   va_start (argptr, fmt);
722
   vfprintf (fp, fmt, argptr);
723
   va_end (argptr);
724
 
725
   // for each row of howmany_columns bytes of data...
726
   for (index = 0; index < data_size; index += howmany_columns)
727
   {
728
      fprintf (fp, "    %05zu  ", index); // print array address of row
729
      for (i = 0; i < howmany_columns; i++)
730
         if (index + i < data_size)
731
            fprintf (fp, " %02X", data[index + i]); // if row contains data, print data as hex bytes
732
         else
733
            fprintf (fp, "   "); // else fill the space with blanks
734
      fprintf (fp, "   ");
735
      for (i = 0; i < howmany_columns; i++)
736
         if (index + i < data_size)
737
            fputc ((data[index + i] >= 32) && (data[index + i] < 127) ? data[index + i] : '.', fp); // now if row contains data, print data as ASCII
738
         else
739
            fputc (' ', fp); // else fill the space with blanks
740
      fputc ('\n', fp);
741
   }
742
 
743
   return; // and return
744
}
745
 
746
 
747
static char *binary (const uint8_t x, char char_for_zero, char char_for_one)
748
{
749
   // returns the binary representation of x as a string
750
 
751
   static char outstr[9] = "00000000";
752
   for (int i = 0; i < 8; i++)
753
      outstr[i] = (x & (0x80 >> i) ? char_for_one : char_for_zero);
754
   return (outstr);
755
}
756
 
757
 
758
static char *describe_uint8 (const uint8_t x, const char *bitwise_stringdescs[8])
759
{
760
   // returns the ORed description of byte 'x' according to the description strings for each bit
761
 
762
   static char *default_bitstrings[8] = { "bit0", "bit1", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7" };
763
   static char outstr[8 * 64] = "";
764
 
765
   outstr[0] = 0;
766
   for (int i = 0; i < 8; i++)
767
      if (x & (1 << i))
768
      {
769
         if (outstr[0] != 0)
770
            strcat (outstr, "|");
771
         strcat (outstr, ((bitwise_stringdescs != NULL) && (*bitwise_stringdescs[i] != 0) ? bitwise_stringdescs[i] : default_bitstrings[i]));
772
      }
773
   return (outstr);
774
}
775
 
776
 
1 pmbaty 777
static int fwrite_filecontents (const char *pathname, FILE *fp)
778
{
779
   // dumps the binary contents of pathname to fp
780
 
781
   uint8_t *blob_buffer;
782
   size_t blob_size;
783
   FILE *blob_fp;
784
   int ret;
785
 
786
   blob_fp = fopen (pathname, "rb");
787
   if (blob_fp == NULL)
788
      return (-1); // errno is set
789
 
790
   fseek (blob_fp, 0, SEEK_END);
791
   blob_size = ftell (blob_fp);
792
   blob_buffer = malloc (blob_size);
793
   if (blob_buffer == NULL)
794
   {
795
      fclose (blob_fp);
796
      return (-1); // errno is set to ENOMEM
797
   }
798
   fseek (blob_fp, 0, SEEK_SET);
799
   fread (blob_buffer, 1, blob_size, blob_fp);
800
   fclose (blob_fp);
801
 
802
   ret = (int) fwrite (blob_buffer, 1, blob_size, fp);
803
   free (blob_buffer);
804
   return (ret);
805
}
806
 
807
 
808
static size_t fwrite_fsentry (const fsentry_t *fsentry, FILE *fp)
809
{
810
   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";
811
 
812
   size_t datalen;
813
   size_t count;
814
 
815
   count = 0;
816
   if (fp != NULL)
817
      fwrite (&fsentry->header, sizeof (fsentry->header), 1, fp); // write the entry header (PACKED STRUCT)
818
   count += sizeof (fsentry->header);
819
   if (S_ISREG (fsentry->header.mode))
820
   {
821
      if (fp != NULL)
822
      {
823
         fwrite (&fsentry->u.file.offset, sizeof (uint32_t), 1, fp); // write offset
824
         fwrite (&fsentry->u.file.size,   sizeof (uint32_t), 1, fp); // write size
825
      }
826
      count += 2 * sizeof (uint32_t);
827
      datalen = strlen (fsentry->u.file.path) + 1;
828
      if (fp != NULL)
829
         fwrite (fsentry->u.file.path, (size_t) datalen, 1, fp); // write null-terminated path (no leading slash)
830
      count += datalen;
831
   }
832
   else if (S_ISDIR (fsentry->header.mode))
833
   {
834
      datalen = strlen (fsentry->u.dir.path) + 1;
835
      if (fp != NULL)
836
         fwrite (fsentry->u.dir.path, (size_t) datalen, 1, fp); // write null-terminated path (no leading slash)
837
      count += datalen;
838
   }
839
   else if (S_ISLNK (fsentry->header.mode))
840
   {
841
      if (fp != NULL)
842
      {
843
         fwrite (&fsentry->u.symlink.sym_offset, sizeof (uint16_t), 1, fp); // write offset
844
         fwrite (&fsentry->u.symlink.sym_size,   sizeof (uint16_t), 1, fp); // write size
845
      }
846
      count += 2 * sizeof (uint16_t);
847
      datalen = strlen (fsentry->u.symlink.path) + 1;
848
      if (fp != NULL)
849
         fwrite (fsentry->u.symlink.path, (size_t) datalen, 1, fp); // write null-terminated path (no leading slash)
850
      count += datalen;
851
      datalen = strlen (fsentry->u.symlink.contents) + 1;
852
      if (fp != NULL)
853
         fwrite (fsentry->u.symlink.contents, (size_t) datalen, 1, fp); // write null-terminated symlink contents
854
      count += datalen;
855
   }
856
   else
857
   {
858
      if (fp != NULL)
859
      {
860
         fwrite (&fsentry->u.device.dev,  sizeof (uint32_t), 1, fp); // write dev number
861
         fwrite (&fsentry->u.device.rdev, sizeof (uint32_t), 1, fp); // write rdev number
862
      }
863
      count += 2 * sizeof (uint32_t);
864
      datalen = strlen (fsentry->u.device.path) + 1;
865
      if (fp != NULL)
866
         fwrite (fsentry->u.device.path, (size_t) datalen, 1, fp); // write null-terminated path (no leading slash)
867
      count += datalen;
868
   }
869
 
870
   if ((fp != NULL) && (count % image_align != 0))
871
      fwrite (zeropad_buffer, count % image_align, 1, fp); // pad as necessary
872
   count = ROUND_TO_UPPER_MULTIPLE (count, image_align);
873
 
874
   return (count);
875
}
876
 
877
 
878
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)
879
{
880
   static char candidate_pathname[1024];
881
   static char *MKIFS_PATH = NULL;
882
   static int inode_count = 0; // will be preincremented each time this function is called
883
 
884
   void *reallocated_ptr;
885
   char processor_base[16];
886
   struct stat stat_buf;
887
   uint8_t *data_buffer = NULL;
888
   size_t data_len = 0;
2 pmbaty 889
   uint32_t mtime = (uint32_t) time (NULL);
1 pmbaty 890
   uint32_t extra_ino_flags = 0;
891
   fsentry_t *fsentry;
892
   char *token;
893
   FILE *fp;
894
 
895
   if (S_ISDIR (entry_parms->st_mode)) // are we storing a directory ?
896
   {
897
      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);
898
   }
899
   else if (S_ISREG (entry_parms->st_mode)) // else are we storing a regular file ?
900
   {
901
      if ((entry_data != NULL) && (entry_datalen > 0)) // was an explicit contents blob supplied ?
902
      {
903
         if (strcmp (stored_pathname, "proc/boot/boot") == 0) // is it the kernel ?
904
         {
905
            // HACK: for now just consider the kernel as a binary blob
906
            // FIXME: reimplement properly
2 pmbaty 907
            data_len = entry_datalen;
1 pmbaty 908
            data_buffer = malloc (data_len);
909
            if (data_buffer == NULL)
910
            {
911
               fprintf (stderr, "fatal error: out of memory\n");
912
               exit (1);
913
            }
2 pmbaty 914
            memcpy (data_buffer, entry_data, data_len);
915
 
916
            sprintf (candidate_pathname, "%s/procnto-smp-instr", entry_parms->prefix); // fix the entry name
1 pmbaty 917
            stored_pathname = candidate_pathname;
2 pmbaty 918
            extra_ino_flags = IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE; // procnto needs to have these flags stamped on the inode
1 pmbaty 919
            image_kernel_ino = extra_ino_flags | (inode_count + 1);
920
         }
2 pmbaty 921
         else if (entry_parms->is_compiled_bootscript) // else is it a startup script ?
922
            image_bootscript_ino = inode_count + 1; // save boot script inode number for image header
923
 
924
         // should we substitute precompiled data to this data blob ?
925
         if ((entry_parms->precompiled_data != NULL) && (entry_parms->precompiled_datalen > 0))
1 pmbaty 926
         {
2 pmbaty 927
            data_len = entry_parms->precompiled_datalen;
928
            data_buffer = malloc (data_len);
1 pmbaty 929
            if (data_buffer == NULL)
930
            {
931
               fprintf (stderr, "fatal error: out of memory\n");
932
               exit (1);
933
            }
2 pmbaty 934
            memcpy (data_buffer, entry_parms->precompiled_data, data_len);
1 pmbaty 935
         }
2 pmbaty 936
         else // else use the supplied data blob
1 pmbaty 937
         {
938
            data_len = entry_datalen;
939
            data_buffer = malloc (data_len);
940
            if (data_buffer == NULL)
941
            {
942
               fprintf (stderr, "fatal error: out of memory\n");
943
               exit (1);
944
            }
945
            memcpy (data_buffer, entry_data, data_len);
946
         }
2 pmbaty 947
/*
948
         else if (entry_parms->should_compile_contents_as_startup_script) // should we compile this contents as a startup script ?
949
         {
950
            // HACK: for now just use a precompiled script
951
            // FIXME: replace this with a true compilation with the rules defined above
952
            data_buffer = malloc (INITIAL_STARTUP_SCRIPT_LEN);
953
            if (data_buffer == NULL)
954
            {
955
               fprintf (stderr, "fatal error: out of memory\n");
956
               exit (1);
957
            }
958
            memcpy (data_buffer, INITIAL_STARTUP_SCRIPT, INITIAL_STARTUP_SCRIPT_LEN);
959
            data_len = INITIAL_STARTUP_SCRIPT_LEN;
960
            image_bootscript_ino = inode_count + 1; // save boot script inode number for image header
961
         }*/
1 pmbaty 962
 
963
         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);
964
      }
965
      else if ((entry_data != NULL) && (entry_data[0] != 0)) // else entry_datalen == 0, was some sort of pathname supplied ?
966
      {
967
         // is it an absolute pathname ?
968
         if (IS_DIRSEP (entry_data[0])
969
             || (isalpha (entry_data[0])
970
                 && (entry_data[1] == ':')
971
                 && IS_DIRSEP (entry_data[2])))
972
         {
973
            // in this case, it MUST exist at its designated location (either absolute or relative to the current working directory)
974
            strcpy (candidate_pathname, entry_data);
975
            if (stat (candidate_pathname, &stat_buf) != 0)
976
            {
977
               fprintf (stderr, "fatal error: filesystem entry \"%s\" specified in \"%s\" line %d not found on build host\n", entry_data, buildfile_pathname, lineno);
978
               exit (1);
979
            }
980
         }
981
         else // the path is relative, search it among the search paths we have
982
         {
983
            // is the search path NOT defined ?
984
            if (entry_parms->search[0] == 0)
985
            {
986
               // initialize the default search path in MKIFS_PATH
987
               if (MKIFS_PATH == NULL)
988
               {
989
                  MKIFS_PATH = getenv ("MKIFS_PATH"); // look in the environment first, and construct a default one if not supplied
990
                  if (MKIFS_PATH == NULL)
991
                  {
992
                     strcpy (processor_base, image_processor); // construct PROCESSOR_BASE
993
                     token = strchr (processor_base, '-');
994
                     if (token != NULL)
995
                        *token = 0; // split anything from the first dash onwards
996
                     data_len = strlen (processor_base);
997
                     if ((data_len > 2) && ((processor_base[data_len - 2] == 'b') || (processor_base[data_len - 2] == 'l')) && (processor_base[data_len - 1] == 'e'))
998
                        processor_base[data_len - 2] = 0; // if it ends with "le" or "be", strip that too
999
 
1000
                     MKIFS_PATH = malloc (10 * MAXPATHLEN); // construct a default MKIFS_PATH now
1001
                     if (MKIFS_PATH == NULL)
1002
                     {
1003
                        fprintf (stderr, "fatal error: out of memory\n");
1004
                        exit (1);
1005
                     }
1006
                     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
1007
                                          QNX_TARGET, image_processor,
1008
                                          QNX_TARGET, image_processor,
1009
                                          QNX_TARGET, image_processor,
1010
                                          QNX_TARGET, processor_base,
1011
                                          QNX_TARGET, image_processor,
1012
                                          QNX_TARGET, image_processor,
1013
                                          QNX_TARGET, image_processor,
1014
                                          QNX_TARGET, image_processor,
1015
                                          QNX_TARGET, image_processor);
1016
                  }
1017
               } // at this point MKIFS_PATH is defined
1018
 
1019
               strcpy (entry_parms->search, MKIFS_PATH); // if entry search path is not defined, use the contents of MKIFS_PATH
1020
            }
1021
 
1022
            // convert path separators in the MKIFS_PATH environment variable into something platform-agnostic
1023
            for (token = entry_parms->search; *token != 0; token++)
1024
               if (*token == PATH_SEP)
1025
                  *token = '|';
1026
 
1027
            token = strtok (entry_parms->search, "|");
1028
            while (token != NULL)
1029
            {
1030
               sprintf (candidate_pathname, "%s/%s", token, entry_data);
1031
               if (stat (candidate_pathname, &stat_buf) == 0)
1032
                  break;
1033
               token = strtok (NULL, "|");
1034
            }
1035
            if (token == NULL)
1036
            {
1037
               fprintf (stderr, "fatal error: filesystem entry \"%s\" specified in \"%s\" line %d not found on build host\n", entry_data, buildfile_pathname, lineno);
1038
               exit (1);
1039
            }
1040
         }
1041
 
1042
         data_len = stat_buf.st_size;
1043
         mtime = (uint32_t) stat_buf.st_mtime;
1044
 
1045
         data_buffer = malloc (data_len);
1046
         if (data_buffer == NULL)
1047
         {
1048
            fprintf (stderr, "fatal error: out of memory\n");
1049
            exit (1);
1050
         }
1051
         fp = fopen (candidate_pathname, "rb");
1052
         fread (data_buffer, 1, data_len, fp);
1053
         fclose (fp);
1054
 
1055
         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);
1056
      }
1057
   }
1058
   else if (S_ISLNK (entry_parms->st_mode)) // else are we storing a symbolic link ?
1059
   {
1060
      data_len = strlen (entry_data);
1061
 
1062
      data_buffer = malloc (data_len + 1);
1063
      if (data_buffer == NULL)
1064
      {
1065
         fprintf (stderr, "fatal error: out of memory\n");
1066
         exit (1);
1067
      }
1068
      memcpy (data_buffer, entry_data, data_len + 1); // copy including null terminator
1069
 
1070
      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);
1071
   }
1072
   else // we must be storing a FIFO
1073
   {
1074
      if (strchr (entry_data, ':') == NULL)
1075
      {
1076
         fprintf (stderr, "fatal error: device entry \"%s\" malformed (no 'dev:rdev' pair)\n", stored_pathname);
1077
         exit (1);
1078
      }
1079
 
1080
      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);
1081
   }
1082
 
1083
   // reallocate filesystem entries array to hold one more slot
1084
   reallocated_ptr = realloc (*fsentries, (*fsentry_count + 1) * sizeof (fsentry_t)); // attempt to reallocate
1085
   if (reallocated_ptr == NULL)
1086
   {
1087
      fprintf (stderr, "fatal error: out of memory\n");
1088
      exit (1);
1089
   }
1090
   *fsentries = reallocated_ptr; // save reallocated pointer
1091
   fsentry = &(*fsentries)[*fsentry_count]; // quick access to fs entry slot
1092
   //fsentry->header.size = 0; // will be filled once we know it
1093
   fsentry->header.extattr_offset = 0;
1094
   fsentry->header.ino = extra_ino_flags | (++inode_count);
1095
   fsentry->header.mode = entry_parms->st_mode;
1096
   fsentry->header.gid = entry_parms->gid;
1097
   fsentry->header.uid = entry_parms->uid;
1098
   fsentry->header.mtime = mtime;
1099
   if (S_ISDIR (entry_parms->st_mode))
1100
   {
1101
      fsentry->u.dir.path = strdup (stored_pathname);
1102
      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
1103
      fsentry->UNSAVED_was_data_written = true; // no data to save
1104
   }
1105
   else if (S_ISREG (entry_parms->st_mode))
1106
   {
1107
      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
1108
      fsentry->u.file.size = (uint32_t) data_len;
1109
      fsentry->u.file.path = strdup (stored_pathname);
1110
      fsentry->u.file.UNSAVED_databuf = data_buffer;
1111
      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
1112
      fsentry->UNSAVED_was_data_written = false; // there *IS* data to save
1113
   }
1114
   else if (S_ISLNK (entry_parms->st_mode))
1115
   {
1116
      fsentry->u.symlink.sym_offset = (uint16_t) (strlen (stored_pathname) + 1);
1117
      fsentry->u.symlink.sym_size = (uint16_t) data_len;
1118
      fsentry->u.symlink.path = strdup (stored_pathname);
1119
      fsentry->u.symlink.contents = data_buffer;
1120
      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
1121
      fsentry->UNSAVED_was_data_written = true; // no data to save
1122
   }
1123
   else
1124
   {
1125
      fsentry->u.device.dev  = strtol (entry_data, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
1126
      fsentry->u.device.rdev = strtol (strchr (entry_data, ':') + 1, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
1127
      fsentry->u.device.path = strdup (stored_pathname);
1128
      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
1129
      fsentry->UNSAVED_was_data_written = true; // no data to save
1130
   }
1131
   (*fsentry_count)++;
1132
   return (*fsentry_count);
1133
}
1134
 
1135
 
1136
static int fsentry_compare_pathnames_cb (const void *a, const void *b)
1137
{
1138
   // qsort() callback that compares two imagefs filesystem entries and sort them alphabetically by pathname
1139
 
3 pmbaty 1140
   const fsentry_t *entry_a = (const fsentry_t *) a;
1141
   const fsentry_t *entry_b = (const fsentry_t *) b;
1 pmbaty 1142
   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)));
1143
   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)));
1144
   return (strcmp (pathname_a, pathname_b));
1145
}
1146
 
1147
 
1148
static int fsentry_compare_sizes_cb (const void *a, const void *b)
1149
{
3 pmbaty 1150
   // qsort() callback that compares two imagefs filesystem entries and sort them by increasing data size
1 pmbaty 1151
 
3 pmbaty 1152
   const fsentry_t *entry_a = (const fsentry_t *) a;
1153
   const fsentry_t *entry_b = (const fsentry_t *) b;
1 pmbaty 1154
   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
1155
   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
1156
   return ((int) size_b - (int) size_a);
1157
}
1158
 
1159
 
1160
int main (int argc, char **argv)
1161
{
1162
   // program entrypoint
1163
 
1164
   #define PAD_OUTFILE_TO(val) do { curr_offset = ftell (fp); while (curr_offset < (val)) { putc (0, fp); curr_offset++; } } while (0)
1165
 
2 pmbaty 1166
   static startup_header_t startup_header = { 0 }; // output IFS's startup header
1167
   static startup_trailer_v2_t startup_trailer = { 0 }; // output IFS's startup trailer (version 2, with SHA-512 checksum and int32 checksum)
1168
   static image_header_t image_header = { 0 }; // output IFS's imagefs header
1169
   static image_trailer_v2_t image_trailer = { 0 }; // output IFS's imagefs trailer (version 2, with SHA-512 checksum and int32 checksum)
1170
   static fsentry_t *fsentries = NULL; // output IFS's filesystem entries
1171
   static size_t fsentry_count = 0; // number of entries in the IFS filesystem
1172
   static parms_t default_parms = { 0755, 0644, 0, 0, S_IFREG, "/proc/boot", false, "", NULL, 0 }; // default parameters for a filesystem entry
1173
   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 1174
 
1175
   // bootable IFS support
1176
   char *bootfile_pathname = NULL;           // HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS
2 pmbaty 1177
   size_t bootfile_size = 0;                 // HACK: size of the bootcode binary blob file to put at the start of a bootable IFS
1 pmbaty 1178
   char *startupfile_pathname = NULL;        // HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS
1179
   size_t startupfile_ep_from_imagebase = 0; // HACK: startup code entrypoint offset from image base for a bootable IFS 
1180
   char *kernelfile_pathname = NULL;         // HACK: pathname to precompiled kernel file blob to put in a bootable IFS
1181
   size_t kernelfile_offset = 0;             // HACK: kernel file offset in bootable IFS
1182
 
1183
   char path_in_ifs[MAXPATHLEN];
1184
   char *ifs_pathname = NULL;
1185
   void *reallocated_ptr;
1186
   struct stat stat_buf;
1187
   size_t startuptrailer_offset;
1188
   size_t startupheader_offset;
1189
   size_t imgtrailer_offset;
1190
   size_t imgheader_offset;
1191
   size_t imgdir_offset;
1192
   size_t imgdir_size;
1193
   size_t final_size;
1194
   size_t fsentry_index;
1195
   size_t curr_offset;
1196
   size_t blob_datasize; // mallocated size
1197
   size_t blob_datalen; // used size
1198
   uint8_t *blob_data = NULL; // mallocated
1199
   char *line_ptr;
1200
   char *directiveblock_start;
1201
   char *write_ptr;
1202
   char *token;
1203
   char *value;
1204
   char *sep;
1205
   //char *ctx;
1206
   int arg_index;
1207
   bool is_quoted_context = false;
1208
   bool is_escaped_char = false;
1209
   bool want_info = false;
1210
   bool want_help = false;
5 pmbaty 1211
   bool is_foreign_endianness;
1 pmbaty 1212
   int string_len;
1213
   int read_char;
1214
   FILE *buildfile_fp;
1215
   FILE *fp;
1216
 
1217
   // parse arguments
1218
   for (arg_index = 1; arg_index < argc; arg_index++)
1219
   {
1220
      if ((strcmp (argv[arg_index], "--bootfile") == 0) && (arg_index + 1 < argc)) // --bootfile path/to/blob.bin
1221
         bootfile_pathname = argv[++arg_index];
1222
      else if ((strcmp (argv[arg_index], "--startupfile") == 0) && (arg_index + 1 < argc)) // --startupfile path/to/blob.bin@0x1030
1223
      {
1224
         sep = strchr (argv[++arg_index], '@');
1225
         if ((sep == NULL) || (sep[1] == 0))
1226
         {
1227
            fprintf (stderr, "error: the --startupfile arguments expects <pathname>@<entrypoint_from_image_base>\n");
1228
            exit (1);
1229
         }
1230
         *sep = 0;
1231
         startupfile_pathname = argv[arg_index];
1232
         startupfile_ep_from_imagebase = (size_t) read_integer (sep + 1);
1233
      }
1234
      else if ((strcmp (argv[arg_index], "--kernelfile") == 0) && (arg_index + 1 < argc)) // --kernelfile path/to/blob.bin@0x32000
1235
      {
1236
         sep = strchr (argv[++arg_index], '@');
1237
         if ((sep == NULL) || (sep[1] == 0))
1238
         {
1239
            fprintf (stderr, "error: the --kernelfile arguments expects <pathname>@<fileoffset>\n");
1240
            exit (1);
1241
         }
1242
         *sep = 0;
1243
         kernelfile_pathname = argv[arg_index];
1244
         kernelfile_offset = (size_t) read_integer (sep + 1);
1245
      }
1246
      else if (strcmp (argv[arg_index], "--info") == 0)
1247
         want_info = true;
1248
      else if ((strcmp (argv[arg_index], "-?") == 0) || (strcmp (argv[arg_index], "--help") == 0))
1249
         want_help = true;
1250
      else if (buildfile_pathname == NULL)
1251
         buildfile_pathname = argv[arg_index];
1252
      else if (ifs_pathname == NULL)
1253
         ifs_pathname = argv[arg_index];
1254
   }
1255
 
1256
   // do we not have enough information to run ?
2 pmbaty 1257
   if (want_help || (buildfile_pathname == NULL) || (!want_info && (ifs_pathname == NULL)))
1 pmbaty 1258
   {
4 pmbaty 1259
      fprintf ((want_help ? stdout : stderr), "ifstool - QNX in-kernel filesystem creation utility by Pierre-Marie Baty <pm@pmbaty.com>\n");
1260
      fprintf ((want_help ? stdout : stderr), "          version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD);
1261
      if (!want_help)
1 pmbaty 1262
         fprintf (stderr, "error: missing parameters\n");
1263
      fprintf ((want_help ? stdout : stderr), "usage:\n");
1264
      fprintf ((want_help ? stdout : stderr), "    ifstool [--bootfile <pathname>] [--startupfile <pathname>@<EP_from_imgbase>] [--kernelfile <pathname>@<fileoffs>] <buildfile> <outfile>\n");
1265
      fprintf ((want_help ? stdout : stderr), "    ifstool --info <ifs file>\n");
1266
      fprintf ((want_help ? stdout : stderr), "    ifstool --help\n");
1267
      exit (want_help ? 0 : 1);
1268
   }
1269
 
2 pmbaty 1270
   // do we want info about a particular IFS ? if so, dump it
1271
   if (want_info)
1272
      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
1273
 
1 pmbaty 1274
   // make sure we have ${QNX_TARGET} pointing somewhere
1275
   QNX_TARGET = getenv ("QNX_TARGET");
1276
   if (QNX_TARGET == NULL)
1277
   {
1278
      fprintf (stderr, "error: the QNX_TARGET environment variable is not set\n");
1279
      exit (1);
1280
   }
1281
   else if (access (QNX_TARGET, 0) != 0)
1282
   {
1283
      fprintf (stderr, "error: the QNX_TARGET environment variable doesn't point to an existing directory\n");
1284
      exit (1);
1285
   }
1286
 
1287
   // open build file
1288
   buildfile_fp = fopen (buildfile_pathname, "rb");
1289
   if (buildfile_fp == NULL)
1290
   {
1291
      fprintf (stderr, "error: unable to open build file \"%s\" for reading (%s)\n", buildfile_pathname, strerror (errno));
1292
      exit (1);
1293
   }
1294
 
1295
   // stack up filesystem entries
1296
   memcpy (&entry_parms, &default_parms, sizeof (default_parms));
1297
   entry_parms.st_mode = S_IFDIR | default_parms.dperms;
1298
   add_fsentry (&fsentries, &fsentry_count, "", &entry_parms, NULL, 0); // add the root dir first
1299
 
1300
   while (fgets (line_buffer, sizeof (line_buffer), buildfile_fp) != NULL)
1301
   {
1302
      lineno++; // keep track of current line number
1303
      //fprintf (stderr, "read buildfile line %d: {%s}\n", lineno, line_buffer);
1304
 
1305
      line_ptr = line_buffer;
1306
      while ((*line_ptr != 0) && isspace (*line_ptr))
1307
         line_ptr++; // skip leading spaces
1308
 
1309
      if ((*line_ptr == 0) || (*line_ptr == '#'))
1310
         continue; // skip empty or comment lines
1311
 
1312
      string_len = (int) strlen (line_buffer);
1313
      if ((string_len > 0) && (line_buffer[string_len - 1] == '\n'))
1314
         line_buffer[string_len - 1] = 0; // chop off newline for easier debug output
1315
 
1316
      // reset entry values
1317
      memcpy (&entry_parms, &default_parms, sizeof (default_parms));
1318
 
1319
      //fprintf (stderr, "parsing buildfile line %d: [%s]\n", lineno, line_ptr);
1320
 
1321
      // does this line start with an attribute block ?
1322
      if (*line_ptr == '[')
1323
      {
1324
         line_ptr++; // skip the leading square bracket
1325
         directiveblock_start = line_ptr; // remember where it starts
1326
         is_quoted_context = false;
1327
         while ((*line_ptr != 0) && !((*line_ptr == ']') && (line_ptr[-1] != '\\')))
1328
         {
1329
            if (*line_ptr == '"')
1330
               is_quoted_context ^= true; // remember when we're between quotes
1331
            else if (!is_quoted_context && (*line_ptr == ' '))
1332
               *line_ptr = RECORD_SEP; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
1333
            line_ptr++; // reach the next unescaped closing square bracket
1334
         }
1335
         if (*line_ptr != ']')
1336
         {
1337
            fprintf (stderr, "warning: syntax error in \"%s\" line %d: unterminated attributes block (skipping)\n", buildfile_pathname, lineno);
1338
            continue; // invalid attribute block, skip line
1339
         }
1340
         *line_ptr = 0; // end the attribute block so that it is a parsable C string
1341
 
1342
         // now parse the attribute tokens
1343
         // DOCUMENTATION: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/m/mkifs.html#mkifs__description
1344
         token = strtok (directiveblock_start, RECORD_SEP_STR);
1345
         while (token != NULL)
1346
         {
1347
            // evaluate attribute token
1348
            #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
1349
            if      (strncmp (token, "uid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.uid     = (int) read_integer (value); }
1350
            else if (strncmp (token, "gid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.gid     = (int) read_integer (value); }
1351
            else if (strncmp (token, "dperms=",  7) == 0) { REACH_TOKEN_VALUE (); entry_parms.dperms  = (int) read_integer (value); }
1352
            else if (strncmp (token, "perms=",   6) == 0) { REACH_TOKEN_VALUE (); entry_parms.perms   = (int) read_integer (value); }
1353
            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))))); }
1354
            else if (strncmp (token, "prefix=",  7) == 0) { REACH_TOKEN_VALUE (); strcpy (entry_parms.prefix, (*value == '/' ? value + 1 : value)); } // skip possible leading slash in prefix
1355
            else if (strncmp (token, "image=",   6) == 0) { REACH_TOKEN_VALUE ();
1356
               image_base = (uint32_t) read_integer (value); // read image base address
1357
               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.)
1358
               if ((sep = strchr (value, ',')) != NULL) image_maxsize   = (uint32_t) read_integer (sep + 1); // if we have a comma, read optional image max size
1359
               if ((sep = strchr (value, '=')) != NULL) image_totalsize = (uint32_t) read_integer (sep + 1); // if we have an equal sign, read optional image padding size
1360
               if ((sep = strchr (value, '%')) != NULL) image_align     = (uint32_t) read_integer (sep + 1); // if we have a modulo sign, read optional image aligmnent
1361
               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);
1362
            }
1363
            else if (strncmp (token, "virtual=", 8) == 0) { REACH_TOKEN_VALUE ();
1364
               if ((bootfile_pathname == NULL) || (startupfile_pathname == NULL) || (kernelfile_pathname == NULL)) // HACK until I figure out how to re-create them
1365
               {
1366
                  fprintf (stderr, "error: creating bootable images require the --bootfile, --startupfile and --kernelfile command-line options in \"%s\" line %d\n", buildfile_pathname, lineno);
1367
                  exit (1);
1368
               }
1369
               if ((sep = strchr (value, ',')) != NULL) // do we have a comma separating (optional) processor and boot file name ?
1370
               {
1371
                  *sep = 0;
1372
                  strcpy (image_processor, value); // save processor
1373
                  value = sep + 1;
1374
               }
1375
               //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 1376
               //strcpy (image_bootfile, bootfile_pathname); // FIXME: HACK
1377
               if (stat (bootfile_pathname, &stat_buf) != 0)
1 pmbaty 1378
               {
2 pmbaty 1379
                  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 1380
                  exit (1);
1381
               }
2 pmbaty 1382
               bootfile_size = stat_buf.st_size; // save preboot file size
1383
               fprintf (stderr, "info: processor \"%s\" bootfile \"%s\"\n", image_processor, bootfile_pathname);
1384
               if (stat (kernelfile_pathname, &stat_buf) != 0)
1385
               {
1386
                  fprintf (stderr, "fatal error: unable to read precompiled kernel file \"%s\" specified in --kernelfile argument\n", kernelfile_pathname);
1387
                  exit (1);
1388
               }
1389
               entry_parms.precompiled_data = malloc (stat_buf.st_size);
1390
               if (entry_parms.precompiled_data == NULL)
1391
               {
1392
                  fprintf (stderr, "fatal error: out of memory\n");
1393
                  exit (1);
1394
               }
6 pmbaty 1395
               fp = fopen (kernelfile_pathname, "rb");
2 pmbaty 1396
               fread (entry_parms.precompiled_data, 1, stat_buf.st_size, fp);
1397
               fclose (fp);
1398
               entry_parms.precompiled_datalen = stat_buf.st_size;
1 pmbaty 1399
            }
2 pmbaty 1400
            else if (strcmp (token, "+script") == 0) {
1401
               entry_parms.is_compiled_bootscript = true;
1402
               entry_parms.precompiled_data = INITIAL_STARTUP_SCRIPT; // HACK until the script compiler is implemented
1403
               entry_parms.precompiled_datalen = sizeof (INITIAL_STARTUP_SCRIPT) - 1;
1404
            }
1 pmbaty 1405
            else fprintf (stderr, "warning: unimplemented attribute in \"%s\" line %d: '%s'\n", buildfile_pathname, lineno, token);
1406
            #undef REACH_TOKEN_VALUE
1407
 
1408
            token = strtok (NULL, RECORD_SEP_STR); // proceed to next attribute token
1409
         }
1410
 
1411
         line_ptr++; // reach the next character
1412
         while ((*line_ptr != 0) && isspace (*line_ptr))
1413
            line_ptr++; // skip leading spaces
1414
 
1415
         // are we at the end of the line ? if so, it means the attribute values that are set should become the default
1416
         if ((*line_ptr == 0) || (*line_ptr == '#'))
1417
         {
1418
            #define APPLY_DEFAULT_ATTR_NUM(attr,descr,fmt) do { if (entry_parms.attr != default_parms.attr) { \
1419
                  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); \
1420
                  default_parms.attr = entry_parms.attr; \
1421
               } } while (0)
1422
            #define APPLY_DEFAULT_ATTR_STR(attr,descr,fmt) do { if (strcmp (entry_parms.attr, default_parms.attr) != 0) { \
1423
                  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); \
1424
                  strcpy (default_parms.attr, entry_parms.attr); \
1425
               } } while (0)
1426
            APPLY_DEFAULT_ATTR_NUM (dperms,  "directory permissions", "0%o");
1427
            APPLY_DEFAULT_ATTR_NUM (perms,   "file permissions",      "0%o");
1428
            APPLY_DEFAULT_ATTR_NUM (uid,     "owner ID",              "%d");
1429
            APPLY_DEFAULT_ATTR_NUM (gid,     "group ID",              "%d");
1430
            APPLY_DEFAULT_ATTR_NUM (st_mode, "inode type",            "0%o");
1431
            APPLY_DEFAULT_ATTR_STR (prefix,  "prefix",                "\"%s\"");
2 pmbaty 1432
            APPLY_DEFAULT_ATTR_NUM (is_compiled_bootscript, "compiled script state", "%d");
1 pmbaty 1433
            #undef APPLY_DEFAULT_ATTR_STR
1434
            #undef APPLY_DEFAULT_ATTR_NUM
1435
            continue; // end of line reached, proceed to the next line
1436
         }
1437
         // end of attributes parsing
1438
      } // end of "this line starts with an attributes block"
1439
 
1440
      // there's data in this line. We expect a filename in the IFS. Read it and unescape escaped characters
1441
      string_len = sprintf (path_in_ifs, "%s", entry_parms.prefix);
1442
      while ((string_len > 0) && (path_in_ifs[string_len - 1] == '/'))
1443
         string_len--; // chop off any trailing slashes from prefix
1444
      write_ptr = &path_in_ifs[string_len];
1445
      *write_ptr++ = '/'; // add ONE trailing slash
1446
      if (*line_ptr == '/')
1447
      {
1448
         fprintf (stderr, "warning: paths in the IFS file should not begin with a leading '/' in \"%s\" line %d\n", buildfile_pathname, lineno);
1449
         line_ptr++; // consistency check: paths in the IFS should not begin with a '/'
1450
      }
1451
      while ((*line_ptr != 0) && (*line_ptr != '=') && !isspace (*line_ptr))
1452
      {
1453
         if (*line_ptr == '\\')
1454
         {
1455
            line_ptr++;
1456
            *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
1457
         }
1458
         else
1459
            *write_ptr++ = *line_ptr;
1460
         line_ptr++;
1461
      }
1462
      *write_ptr = 0; // terminate the string
1463
 
1464
      // we reached a space OR an equal sign
1465
      while ((*line_ptr != 0) && isspace (*line_ptr))
1466
         line_ptr++; // skip optional spaces after the filename in the IFS
1467
 
1468
      blob_data = NULL;
1469
      blob_datasize = 0;
1470
      blob_datalen = 0;
1471
 
1472
      // do we have an equal sign ?
1473
      if (*line_ptr == '=') // we must be creating either a directory or a file, do we have an equal sign ?
1474
      {
1475
         line_ptr++; // skip the equal sign
1476
         while ((*line_ptr != 0) && isspace (*line_ptr))
1477
            line_ptr++; // skip optional spaces after the equal sign
1478
 
1479
         if (*line_ptr == 0)
1480
         {
1481
            fprintf (stderr, "warning: syntax error in \"%s\" line %d: missing data specification after equal sign (skipping)\n", buildfile_pathname, lineno);
1482
            continue; // invalid symlink specification, skip line
1483
         }
1484
 
1485
         // read the host system's path, it may be either a path or a contents definition. Is it a content definition ?
1486
         if (*line_ptr == '{')
1487
         {
1488
            line_ptr++; // skip the leading content definition
1489
            is_escaped_char = false;
1490
            for (;;)
1491
            {
1492
               read_char = fgetc (buildfile_fp);
1493
               if (read_char == EOF)
1494
               {
1495
                  fprintf (stderr, "fatal error: syntax error in \"%s\" line %d: unterminated contents block (end of file reached)\n", buildfile_pathname, lineno);
1496
                  exit (1); // invalid contents block
1497
               }
1498
               else if (read_char == '\\')
1499
                  is_escaped_char = true; // remember the next char is escaped
1500
               else if ((read_char == '}') && !is_escaped_char)
1501
                  break; // found an unescaped closing bracked, stop parsing
1502
               else
1503
               {
1504
                  is_escaped_char = false; // any other char, meaning the next one will not be escaped
1505
                  if (blob_datalen == blob_datasize) // reallocate in 4 kb blocks
1506
                  {
1507
                     reallocated_ptr = realloc (blob_data, blob_datasize + 4096);
1508
                     if (reallocated_ptr == NULL)
1509
                     {
1510
                        fprintf (stderr, "fatal error: out of memory\n");
1511
                        exit (1);
1512
                     }
1513
                     blob_data = reallocated_ptr;
1514
                     blob_datasize += 4096;
1515
                  }
1516
                  blob_data[blob_datalen++] = read_char;
1517
                  if (read_char == '\n')
1518
                     lineno++;
1519
               }
1520
            }
1521
         }
1522
         else // not a content definition between { brackets }, meaning it's a build host filesystem path
1523
         {
1524
            blob_data = line_ptr; // explicit pathname on build host
1525
            blob_datalen = 0;
1526
         }
1527
      }
1528
      else // no equal sign, meaning the file will have the same name on the build host filesystem
1529
      {
1530
         // consistency check: symlinks MUST have an equal sign
1531
         if (entry_parms.st_mode == S_IFLNK)
1532
         {
1533
            fprintf (stderr, "warning: syntax error in \"%s\" line %d: missing equal sign and symlink target (skipping)\n", buildfile_pathname, lineno);
1534
            continue; // invalid symlink specification, skip line
1535
         }
1536
 
1537
         blob_data = &path_in_ifs[strlen (entry_parms.prefix) + 1]; // same pathname
1538
         blob_datalen = 0;
1539
      }
1540
 
1541
      // now add this entry to the image filesystem
1542
      entry_parms.st_mode |= (S_ISDIR (entry_parms.st_mode) ? entry_parms.dperms : entry_parms.perms); // complete entry permissions
1543
      add_fsentry (&fsentries, &fsentry_count, path_in_ifs, &entry_parms, blob_data, blob_datalen); // and add filesystem entry
1544
      if (blob_datasize > 0)
1545
         free (blob_data); // if blob data was allocated, free it
1546
   }
1547
 
1548
   // sort the filesystem entries by pathname
6 pmbaty 1549
   qsort (&fsentries[1], fsentry_count - 1, sizeof (fsentry_t), fsentry_compare_pathnames_cb);
1 pmbaty 1550
 
1551
   // calculate filesystem entries size
1552
   imgdir_size = sizeof (image_header);
1553
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
1554
   {
1555
      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))));
1556
      imgdir_size += fwrite_fsentry (&fsentries[fsentry_index], NULL);
1557
   }
1558
   fprintf (stderr, "info: image directory size: %zd (0x%zx)\n", imgdir_size, imgdir_size);
1559
 
1560
   // write IFS file
1561
   fp = fopen (ifs_pathname, "wb");
1562
   if (fp == NULL)
1563
   {
1564
      fprintf (stderr, "error: failed to open \"%s\" for writing (%s)\n", ifs_pathname, strerror (errno));
1565
      exit (1);
1566
   }
1567
 
2 pmbaty 1568
   // do we have a startup file ? if so, this is a bootable image
1569
   if (startupfile_pathname != NULL)
1 pmbaty 1570
   {
1571
      // write boot prefix
2 pmbaty 1572
      fwrite_filecontents (bootfile_pathname, fp);
1 pmbaty 1573
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1574
 
1575
      startupheader_offset = ftell (fp); // save startup header offset
1576
      memset (&startup_header, 0, sizeof (startup_header)); // prepare startup header
1577
      memcpy (startup_header.signature, "\xeb\x7e\xff\x00", 4); // startup header signature, i.e. 0xff7eeb
1578
      startup_header.version       = 1;
1579
      startup_header.flags1        = STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2; // flags, 0x21 (STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2)
1580
      startup_header.header_size   = sizeof (startup_header); // 256
1581
      if (strcmp (image_processor, "x86_64") == 0)
1582
         startup_header.machine = STARTUP_HDR_MACHINE_X86_64; // EM_X86_64
1583
      else if (strcmp (image_processor, "aarch64le") == 0)
1584
         startup_header.machine = STARTUP_HDR_MACHINE_AARCH64; // EM_AARCH64
1585
      else
1586
      {
1587
         fprintf (stderr, "fatal error: unsupported processor type '%s' found in build file \"%s\"\n", image_processor, buildfile_pathname);
1588
         exit (1);
1589
      }
1590
      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 1591
      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 1592
      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)
1593
      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)
1594
      startup_header.startup_size  = WILL_BE_FILLED_LATER;                                  // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes
2 pmbaty 1595
      startup_header.stored_size   = WILL_BE_FILLED_LATER;                                  // [I ] Size of entire image, here 0x00cd6128 (same as ram_size)
1 pmbaty 1596
      startup_header.imagefs_size  = WILL_BE_FILLED_LATER;                                  // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes
2 pmbaty 1597
      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 1598
      fwrite (&startup_header, sizeof (startup_header), 1, fp); // write startup header
1599
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1600
 
1601
      // ######################################################################################################################################################################################################################################
1602
      // # FIXME: figure out how to re-create it: linker call involved
1603
      // # $ 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
1604
      // ######################################################################################################################################################################################################################################
1605
      fwrite_filecontents (startupfile_pathname, fp); // write startup code from blob file
1606
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1607
 
1608
      startuptrailer_offset = ftell (fp); // save startup trailer offset
1609
      fwrite (&startup_trailer, sizeof (startup_trailer), 1, fp); // write startup trailer
1610
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1611
   }
1612
 
1613
   imgheader_offset = ftell (fp); // save image header offset
1614
   memset (&image_header, 0, sizeof (image_header)); // prepare image header
1615
   memcpy (&image_header.signature, "imagefs", 7); // image filesystem signature, i.e. "imagefs"
1616
   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)
1617
   image_header.image_size    = WILL_BE_FILLED_LATER; // size from header to end of trailer (here 0xca6fe0 or 13 266 912)
1618
   image_header.hdr_dir_size  = (uint32_t) imgdir_size; // size from header to last dirent (here 0x12b8 or 4792)
1619
   image_header.dir_offset    = sizeof (image_header); // offset from header to first dirent (here 0x5c or 92)
1620
   image_header.boot_ino[0]   = image_kernel_ino; // inode of files for bootstrap p[ro?]g[ra?]ms (here 0xa0000002, 0, 0, 0)
1621
   image_header.script_ino    = image_bootscript_ino; // inode of file for script (here 3)
1622
   image_header.mountpoint[0] = '/'; // default mountpoint for image ("/" + "\0\0\0")
1623
   fwrite (&image_header, sizeof (image_header), 1, fp); // write image header
1624
   PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1625
 
6 pmbaty 1626
   // write image directory (with the wrong file offsets)
1 pmbaty 1627
   imgdir_offset = ftell (fp);
1628
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
6 pmbaty 1629
      fwrite_fsentry (&fsentries[fsentry_index], fp); // NOTE: padding is handled in this function
1 pmbaty 1630
   PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1631
 
1632
   // is it a bootable image with a kernel file ?
2 pmbaty 1633
   if ((startupfile_pathname != NULL) && (kernelfile_pathname != NULL))
1 pmbaty 1634
   {
1635
      // sort the filesystem entries by sizes
1636
      qsort (fsentries, fsentry_count, sizeof (fsentry_t), fsentry_compare_sizes_cb);
1637
 
1638
      // write as many small files as we can before reaching the kernel offset 
1639
      for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
1640
      {
1641
         if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written)
1642
            continue; // skip all entries that don't have a separate data block and those who were written already
1643
         curr_offset = ftell (fp);
1644
         if (curr_offset + fsentries[fsentry_index].u.file.size >= kernelfile_offset)
1645
            break; // stop writing entries as soon as we reach the kernel file offset
1646
         fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imgheader_offset); // save file data blob offset in file structure
1647
         fwrite (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob
1648
         PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1649
         fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1650
      }
1651
      PAD_OUTFILE_TO (kernelfile_offset); // reach the kernel offset
1652
 
1653
      // ######################################################################################################################################################################################################################################
1654
      // # FIXME: figure out how to re-create it: linker call involved
1655
      // # $ 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
1656
      // ######################################################################################################################################################################################################################################
1657
      fwrite_filecontents (kernelfile_pathname, fp); // write kernel from blob file
1658
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1659
   }
1660
 
1661
   // then write all the other files
1662
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
1663
   {
1664
      if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written)
1665
         continue; // skip all entries that don't have a separate data block and those who were written already
1666
      curr_offset = ftell (fp);
1667
      fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imgheader_offset); // save file data blob offset in file structure
1668
      fwrite (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob
1669
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1670
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1671
   }
1672
 
1673
   // finally, write trailer (including empty checksum)
1674
   imgtrailer_offset = ftell (fp); // save image trailer offset
1675
   fwrite (&image_trailer, sizeof (image_trailer), 1, fp); // write image trailer
1676
   PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1677
 
1678
   // if we need to pad it to a specific length, do so
1679
   PAD_OUTFILE_TO (image_totalsize);
1680
   final_size = ftell (fp);
1681
 
1682
   // see if we are past the image max size, in which case it's an error
1683
   if (final_size > image_maxsize)
1684
   {
1685
      fprintf (stderr, "error: image file \"%s\" size %zd exceeds max size (%zd)\n", ifs_pathname, final_size, (size_t) image_maxsize);
1686
      exit (1);
1687
   }
1688
 
2 pmbaty 1689
   // do we have a startup file ? if so, this is a bootable image
1690
   if (startupfile_pathname != NULL)
1 pmbaty 1691
   {
1692
      // rewrite startup header with final values
1693
      fseek (fp, startupheader_offset, SEEK_SET);
1694
      startup_header.startup_size = (uint32_t) (imgheader_offset - startupheader_offset); // size of startup header up to image header
1695
      startup_header.imagefs_size = (uint32_t) (final_size - imgheader_offset); // size of uncompressed imagefs
1696
      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 1697
      startup_header.stored_size = startup_header.ram_size;
1 pmbaty 1698
      fwrite (&startup_header, sizeof (startup_header), 1, fp); // write startup header
1699
 
1700
      // compute SHA-512 checksum and V1 checksum of startup block
1701
      blob_datasize = startuptrailer_offset - startupheader_offset;
1702
      blob_data = malloc (blob_datasize);
1703
      if (blob_data == NULL)
1704
      {
1705
         fprintf (stderr, "fatal error: out of memory\n");
1706
         exit (1);
1707
      }
1708
      fseek (fp, startupheader_offset, SEEK_SET);
1709
      fread (blob_data, 1, blob_datasize, fp);
1710
      SHA512 (blob_data, blob_datasize, startup_trailer.sha512); // compute SHA512 checksum
6 pmbaty 1711
      if (   ( (startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
1712
          || (!(startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
1713
         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
1714
      else
1715
         is_foreign_endianness = false; // else this header is for the same endianness as us
1 pmbaty 1716
      startup_trailer.cksum = 0; // compute old checksum
6 pmbaty 1717
      startup_trailer.cksum = update_checksum (startup_trailer.cksum, (const uint32_t *) blob_data, blob_datasize, is_foreign_endianness);
1718
      startup_trailer.cksum = update_checksum (startup_trailer.cksum, (const uint32_t *) startup_trailer.sha512, sizeof (startup_trailer.sha512), is_foreign_endianness);
1 pmbaty 1719
      free (blob_data);
1720
 
1721
      // rewrite startup trailer with final values
1722
      fseek (fp, startuptrailer_offset, SEEK_SET);
1723
      fwrite (&startup_trailer, sizeof (startup_trailer), 1, fp); // write startup trailer
1724
   }
1725
 
1726
   // rewrite image header with final values
1727
   fseek (fp, imgheader_offset, SEEK_SET);
1728
   image_header.image_size = (uint32_t) (final_size - imgheader_offset); // size of uncompressed imagefs
1729
   fwrite (&image_header, sizeof (image_header), 1, fp); // write image header
1730
 
1731
   // rewrite image directory with final checksum values
1732
   fseek (fp, imgdir_offset, SEEK_SET);
1733
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
1734
      fwrite_fsentry (&fsentries[fsentry_index], fp);
1735
 
1736
   // compute SHA-512 checksum and V1 checksum of image block
1737
   blob_datasize = imgtrailer_offset - imgheader_offset;
1738
   blob_data = malloc (blob_datasize);
1739
   if (blob_data == NULL)
1740
   {
1741
      fprintf (stderr, "fatal error: out of memory\n");
1742
      exit (1);
1743
   }
1744
   fseek (fp, imgheader_offset, SEEK_SET);
1745
   fread (blob_data, 1, blob_datasize, fp);
1746
   SHA512 (blob_data, blob_datasize, image_trailer.sha512); // compute SHA512 checksum
5 pmbaty 1747
   if (   ( (image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
1748
       || (!(image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
1749
      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
1750
   else
1751
      is_foreign_endianness = false; // else this header is for the same endianness as us
1 pmbaty 1752
   image_trailer.cksum = 0; // compute old checksum
6 pmbaty 1753
   image_trailer.cksum = update_checksum (image_trailer.cksum, (const uint32_t *) blob_data, blob_datasize, is_foreign_endianness);
1754
   image_trailer.cksum = update_checksum (image_trailer.cksum, (const uint32_t *) image_trailer.sha512, sizeof (image_trailer.sha512), is_foreign_endianness);
1 pmbaty 1755
   free (blob_data);
1756
 
1757
   // rewrite image trailer with final checksum values
1758
   fseek (fp, imgtrailer_offset, SEEK_SET);
1759
   fwrite (&image_trailer, sizeof (image_trailer), 1, fp); // write image trailer
1760
 
1761
   // finished, close IFS file and exit with a success code
1762
   fclose (fp);
1763
   fprintf (stdout, "Success\n");
1764
   exit (0);
1765
}
2 pmbaty 1766
 
1767
 
1768
static int dump_ifs_info (const char *ifs_pathname)
1769
{
1770
   #define hex_printf(buf,size,...) hex_fprintf (stdout, (buf), (size), 16, __VA_ARGS__) // use 16 columns in hex output to stdout
1771
   #define BINARY(x) binary ((x), '-', 'x')
1772
 
1773
   static const char *startupheader_flags1_strings[8] = {
1774
      "VIRTUAL", // bit 0
1775
      "BIGENDIAN", // bit 1
1776
      "COMPRESS_BIT1", // bit 2
1777
      "COMPRESS_BIT2", // bit 3
1778
      "COMPRESS_BIT3", // bit 4
1779
      "TRAILER_V2", // bit 5
1780
      "", // bit 6
1781
      "", // bit 7
1782
   };
1783
   static const char *imageheader_flags_strings[8] = {
1784
      "BIGENDIAN", // bit 0
1785
      "READONLY", // bit 1
1786
      "INO_BITS", // bit 2
1787
      "SORTED", // bit 3
1788
      "TRAILER_V2", // bit 4
1789
      "", // bit 5
1790
      "", // bit 6
1791
      "", // bit 7
1792
   };
1793
 
1794
   startup_header_t *startup_header = NULL;
1795
   startup_trailer_v1_t *startup_trailer_v1 = NULL;
1796
   startup_trailer_v2_t *startup_trailer_v2 = NULL;
1797
   image_header_t *image_header = NULL;
1798
   size_t imageheader_offset = 0;
1799
   image_trailer_v1_t *image_trailer_v1 = NULL;
1800
   image_trailer_v2_t *image_trailer_v2 = NULL;
3 pmbaty 1801
   size_t imagetrailer_offset = 0;
1802
   fsentry_t **fsentries = NULL; // mallocated
1803
   size_t fsentry_count = 0;
2 pmbaty 1804
   fsentry_t *current_fsentry = NULL;
1805
   char recorded_sha512[2 * SHA512_DIGEST_LENGTH + 1] = "";
1806
   char computed_sha512[2 * SHA512_DIGEST_LENGTH + 1] = "";
1807
   size_t startupfile_blobsize = 0;
3 pmbaty 1808
   void *reallocated_ptr;
5 pmbaty 1809
   bool is_foreign_endianness;
2 pmbaty 1810
   size_t bootfile_blobsize = 0;
1811
   size_t current_offset;
3 pmbaty 1812
   size_t fsentry_index;
1813
   size_t nearest_distance;
1814
   size_t nearest_index;
2 pmbaty 1815
   size_t byte_index;
1816
   uint32_t recorded_checksum;
1817
   uint32_t computed_checksum;
1818
   uint8_t *filedata;
1819
   size_t filesize;
1820
   time_t mtime;
1821
   FILE *fp;
1822
 
1823
   // open and read IFS file
1824
   fp = fopen (ifs_pathname, "rb");
1825
   if (fp == NULL)
1826
   {
1827
      fprintf (stderr, "error: can't open \"%s\" for reading: %s\n", ifs_pathname, strerror (errno));
1828
      return (1);
1829
   }
1830
   fseek (fp, 0, SEEK_END);
1831
   filesize = ftell (fp);
1832
   filedata = malloc (filesize);
1833
   if (filedata == NULL)
1834
   {
1835
      fprintf (stderr, "fatal error: out of memory\n");
1836
      exit (1);
1837
   }
1838
   fseek (fp, 0, SEEK_SET);
1839
   fread (filedata, 1, filesize, fp);
1840
   fclose (fp);
1841
 
4 pmbaty 1842
   printf ("QNX In-kernel Filesystem analysis produced by ifstool version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD);
2 pmbaty 1843
   printf ("IFS file \"%s\" - size 0x%zx (%zd) bytes\n", ifs_pathname, filesize, filesize);
1844
 
1845
   // parse file from start to end
1846
   current_offset = 0;
1847
   for (;;)
1848
   {
1849
      // does a startup header start here ?
1850
      if ((current_offset + sizeof (startup_header_t) < filesize) && (memcmp (&filedata[current_offset], "\xeb\x7e\xff\x00", 4) == 0))
1851
      {
1852
         startup_header = (startup_header_t *) &filedata[current_offset];
1853
 
1854
         // layout:
1855
         // [STARTUP HEADER]
1856
         // (startup file blob)
1857
         // [STARTUP TRAILER v1 or v2]
1858
 
1859
         printf ("\n");
1860
         printf ("Startup header at offset 0x%zx (%zd):\n", current_offset, current_offset);
1861
         printf ("   signature     = %02x %02x %02x %02x - good\n", startup_header->signature[0], startup_header->signature[1], startup_header->signature[2], startup_header->signature[3]);
1862
         printf ("   version       = 0x%04x (%d) - %s\n", startup_header->version, startup_header->version, (startup_header->version == 1 ? "looks good" : "???"));
1863
         printf ("   flags1        = 0x%02x (%s)\n", startup_header->flags1, describe_uint8 (startup_header->flags1, startupheader_flags1_strings));
1864
         printf ("   flags2        = 0x%02x (%s) - %s\n", startup_header->flags2, BINARY (startup_header->flags2), (startup_header->flags2 == 0 ? "looks good" : "???"));
1865
         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"));
1866
         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")));
1867
         printf ("   startup_vaddr = 0x%08x (%d) - virtual address to transfer to after IPL is done\n", startup_header->startup_vaddr, startup_header->startup_vaddr);
1868
         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);
1869
         printf ("   image_paddr   = 0x%08x (%d) - physical address of image\n", startup_header->image_paddr, startup_header->image_paddr);
1870
         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);
1871
         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);
1872
         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)"));
1873
         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" : "???"));
1874
         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"));
1875
         printf ("   imagefs_size  = 0x%08x (%d) - size of uncompressed imagefs\n", startup_header->imagefs_size, startup_header->imagefs_size);
1876
         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" : "???"));
1877
         printf ("   zero0         = 0x%04x (%d) - zeros - %s\n", startup_header->zero0, startup_header->zero0, (startup_header->zero0 == 0 ? "looks good" : "??? should be zero"));
1878
         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"));
1879
         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"));
1880
         hex_printf ((uint8_t *) &startup_header->info[0], sizeof (startup_header->info), "   info[48] =\n");
1881
 
1882
         // validate that the file can contain up to the startup trailer
1883
         if (current_offset + startup_header->startup_size > filesize)
1884
         {
1885
            fprintf (stderr, "WARNING: this IFS file is too short (startup trailer would be past end of file)\n");
1886
            break;
1887
         }
1888
 
5 pmbaty 1889
         // check if this endianness is ours
1890
         if (   ( (startup_header->flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
1891
             || (!(startup_header->flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
1892
            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
1893
         else
1894
            is_foreign_endianness = false; // else this header is for the same endianness as us
1895
 
2 pmbaty 1896
         // locate the right startup trailer at the right offset
1897
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
1898
         {
1899
            startup_trailer_v2 = (startup_trailer_v2_t *) &filedata[current_offset + startup_header->startup_size - sizeof (startup_trailer_v2_t)];
1900
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v2_t);
1901
         }
1902
         else // old V1 trailer
1903
         {
1904
            startup_trailer_v1 = (startup_trailer_v1_t *) &filedata[current_offset + startup_header->startup_size - sizeof (startup_trailer_v1_t)];
1905
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v1_t);
1906
         }
1907
 
1908
         current_offset += sizeof (startup_header_t); // jump over the startup header and reach the startup blob
1909
         printf ("\n");
1910
         printf ("Startup blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
1911
         printf ("   size 0x%zx (%zd) bytes\n", startupfile_blobsize, startupfile_blobsize);
6 pmbaty 1912
         printf ("   checksum %d\n", update_checksum (0, &filedata[current_offset], startupfile_blobsize, is_foreign_endianness));
2 pmbaty 1913
 
1914
         current_offset += startupfile_blobsize; // jump over the startup blob and reach the startup trailer
1915
         printf ("\n");
1916
         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));
1917
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
1918
         {
1919
            for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
1920
               sprintf (&recorded_sha512[2 * byte_index], "%02x", startup_trailer_v2->sha512[byte_index]);
1921
            strcpy (computed_sha512, SHA512 (startup_header, (size_t) ((uint8_t *) startup_trailer_v2 - (uint8_t *) startup_header), NULL));
1922
            recorded_checksum = startup_trailer_v2->cksum;
6 pmbaty 1923
            computed_checksum = update_checksum (0, startup_header, sizeof (startup_header_t) + startupfile_blobsize + SHA512_DIGEST_LENGTH, is_foreign_endianness);
2 pmbaty 1924
            printf ("    sha512 = %s - %s\n", recorded_sha512, (strcasecmp (computed_sha512, recorded_sha512) == 0 ? "GOOD" : "BAD"));
1925
            printf ("    cksum = 0x%08x (%d) - %s\n", recorded_checksum, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
1926
            if (strcasecmp (computed_sha512, recorded_sha512) != 0)
1927
               printf ("Computed SHA-512: %s\n", computed_sha512);
1928
            if (computed_checksum != recorded_checksum)
1929
               printf ("Computed cksum: 0x%08x (%d)\n", computed_checksum, computed_checksum);
1930
         }
1931
         else // old v1 trailer
1932
         {
1933
            recorded_checksum = startup_trailer_v1->cksum;
6 pmbaty 1934
            computed_checksum = update_checksum (0, startup_header, sizeof (startup_header) + startupfile_blobsize, is_foreign_endianness);
2 pmbaty 1935
            printf ("    cksum = 0x%08x (%d) - %s\n", recorded_checksum, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
1936
            if (computed_checksum != recorded_checksum)
1937
               printf ("Computed cksum: 0x%08x (%d)\n", computed_checksum, computed_checksum);
1938
         }
1939
 
3 pmbaty 1940
         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 1941
      }
1942
 
1943
      // else does an image header start here ?
1944
      else if ((current_offset + sizeof (image_header_t) < filesize) && (memcmp (&filedata[current_offset], "imagefs", 7) == 0))
1945
      {
1946
         imageheader_offset = current_offset;
1947
         image_header = (image_header_t *) &filedata[imageheader_offset];
1948
 
1949
         // layout:
1950
         // [IMAGE HEADER]
1951
         // [image directory entries]
6 pmbaty 1952
         // [smallest file blobs up to KERNEL]
2 pmbaty 1953
         // [padding]
1954
         // [KERNEL]
6 pmbaty 1955
         // [rest of file blobs]
2 pmbaty 1956
         // [IMAGE FOOTER]
1957
 
1958
         printf ("\n");
1959
         printf ("Image header at offset %zx (%zd):\n", current_offset, current_offset);
1960
         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);
1961
         printf ("   flags        = 0x%02x (%s)\n", image_header->flags, describe_uint8 (image_header->flags, imageheader_flags_strings));
1962
         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)"));
1963
         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)"));
1964
         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")));
1965
         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]);
1966
         printf ("   script_ino   = 0x%08x (%d) - inode of compiled bootscript\n", image_header->script_ino, image_header->script_ino);
1967
         printf ("   chain_paddr  = 0x%08x (%d) - offset to next fs signature\n", image_header->chain_paddr, image_header->chain_paddr);
1968
         hex_printf ((uint8_t *) &image_header->spare[0], sizeof (image_header->spare), "   spare[10] =\n");
1969
         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]));
1970
         printf ("   mountpoint   = \"%s\"\n", image_header->mountpoint);
1971
 
1972
         // validate that the file can contain up to the image trailer
1973
         if (current_offset + image_header->image_size > filesize)
1974
         {
1975
            fprintf (stderr, "WARNING: this IFS file is too short (image trailer would be past end of file)\n");
1976
            break;
1977
         }
1978
 
5 pmbaty 1979
         // check if this endianness is ours
1980
         if (   ( (image_header->flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
1981
             || (!(image_header->flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
1982
            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
1983
         else
1984
            is_foreign_endianness = false; // else this header is for the same endianness as us
1985
 
2 pmbaty 1986
         // locate the image trailer at the right offset
1987
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
3 pmbaty 1988
         {
1989
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v2_t);
1990
            image_trailer_v2 = (image_trailer_v2_t *) &filedata[imagetrailer_offset];
1991
         }
2 pmbaty 1992
         else // old V1 trailer
3 pmbaty 1993
         {
1994
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v1_t);
1995
            image_trailer_v1 = (image_trailer_v1_t *) &filedata[imagetrailer_offset];
1996
         }
2 pmbaty 1997
 
1998
         current_offset += sizeof (image_header_t); // jump over the image header and reach the first directory entry
3 pmbaty 1999
 
2000
         // there may be padding before the first directory entry
2 pmbaty 2001
         if (image_header->dir_offset - sizeof (image_header_t) > 0)
6 pmbaty 2002
            hex_printf (&filedata[current_offset], image_header->dir_offset - sizeof (image_header_t), "\n" "%zd padding bytes at offset 0x%zd (%zd):\n", image_header->dir_offset - sizeof (image_header_t), current_offset, current_offset);
3 pmbaty 2003
         current_offset += image_header->dir_offset - sizeof (image_header_t); // padding was processed, jump over it
2 pmbaty 2004
 
2005
         // dump all directory entries until the last one included
3 pmbaty 2006
         fsentries = NULL;
2007
         fsentry_count = 0;
6 pmbaty 2008
         while (current_offset < imageheader_offset + image_header->hdr_dir_size)
2 pmbaty 2009
         {
2010
            current_fsentry = (fsentry_t *) &filedata[current_offset];
2011
 
2012
            if (imageheader_offset + image_header->hdr_dir_size - current_offset < sizeof (current_fsentry->header))
2013
               break; // end padding reached
2014
 
3 pmbaty 2015
            // stack up the filesystem entry pointers in an array while we read them
2016
            reallocated_ptr = realloc (fsentries, (fsentry_count + 1) * sizeof (fsentry_t *));
2017
            if (reallocated_ptr == NULL)
2018
            {
2019
               fprintf (stderr, "fatal error: out of memory\n");
2020
               exit (1);
2021
            }
2022
            fsentries = reallocated_ptr;
2023
            fsentries[fsentry_count] = current_fsentry;
2024
            fsentry_count++;
2025
 
2 pmbaty 2026
            printf ("\n");
2027
            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);
2028
            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"));
2029
            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"));
2030
            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" : ""));
2031
            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);
2032
            printf ("   gid            = 0x%08x (%d) - owner group ID%s\n", current_fsentry->header.gid, current_fsentry->header.gid, (current_fsentry->header.gid == 0 ? " (root)" : ""));
2033
            printf ("   uid            = 0x%08x (%d) - owner user ID%s\n", current_fsentry->header.uid, current_fsentry->header.uid, (current_fsentry->header.uid == 0 ? " (root)" : ""));
2034
            mtime = (time_t) current_fsentry->header.mtime;
2035
            printf ("   mtime          = 0x%08x (%d) - POSIX timestamp: %s", current_fsentry->header.mtime, current_fsentry->header.mtime, asctime (localtime (&mtime))); // NOTE: asctime() provides the newline
2036
            if (S_ISDIR (current_fsentry->header.mode))
2037
               printf ("   [DIRECTORY] path = \"%s\"\n", (char *) &current_fsentry->u.dir.path); // convert from pointer to char array
2038
            else if (S_ISREG (current_fsentry->header.mode))
2039
            {
2040
               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 2041
               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 2042
               printf ("   [FILE] path   = \"%s\"\n", (char *) &current_fsentry->u.file.path); // convert from pointer to char array
2043
            }
2044
            else if (S_ISLNK (current_fsentry->header.mode))
2045
            {
2046
               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 2047
               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 2048
               printf ("   [SYMLINK] path       = \"%s\"\n", (char *) &current_fsentry->u.symlink.path); // convert from pointer to char array
2049
               printf ("   [SYMLINK] contents   = \"%s\"\n", ((char *) &current_fsentry->u.symlink.path) + current_fsentry->u.symlink.sym_offset); // convert from pointer to char array
2050
            }
2051
            else // can only be a device
2052
            {
2053
               printf ("   [DEVICE] dev  = 0x%08x (%d)\n", current_fsentry->u.device.dev, current_fsentry->u.device.dev);
2054
               printf ("   [DEVICE] rdev = 0x%08x (%d)\n", current_fsentry->u.device.rdev, current_fsentry->u.device.rdev);
2055
               printf ("   [DEVICE] path = \"%s\"\n", (char *) &current_fsentry->u.device.path); // convert from pointer to char array
2056
            }
2057
 
2058
            current_offset += current_fsentry->header.size;
2059
         }
3 pmbaty 2060
         if (imageheader_offset + image_header->hdr_dir_size < current_offset + sizeof (current_fsentry->header))
6 pmbaty 2061
            hex_printf (&filedata[current_offset], imageheader_offset + image_header->hdr_dir_size - current_offset, "\n" "%zd padding bytes at offset 0x%zx (%zd):\n", imageheader_offset + image_header->hdr_dir_size - current_offset, current_offset, current_offset);
3 pmbaty 2062
         current_offset += imageheader_offset + image_header->hdr_dir_size - current_offset; // padding was processed, jump over it
2 pmbaty 2063
 
3 pmbaty 2064
         // at this point we are past the directory entries; what is stored now, up to and until the image trailer, is the files' data
2065
         if (fsentry_count > 0)
2066
         {
2067
            while (current_offset < imagetrailer_offset) // and parse data up to the trailer
2068
            {
2069
               nearest_distance = SIZE_MAX;
2070
               nearest_index = SIZE_MAX;
2071
               for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
2072
                  if (S_ISREG (fsentries[fsentry_index]->header.mode) // if this directory entry a file (i.e. it has a data blob)...
2073
                      && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset >= current_offset) // ... AND its data blob is still ahead of our current pointer ...
2074
                      && (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
2075
                  {
2076
                     nearest_distance = imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset; // then remember it
2077
                     nearest_index = fsentry_index;
2078
                  }
2079
               if (nearest_index == SIZE_MAX)
2080
                  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 2081
 
3 pmbaty 2082
               fsentry_index = nearest_index;
2083
               current_fsentry = fsentries[fsentry_index]; // quick access to closest fsentry
2 pmbaty 2084
 
3 pmbaty 2085
               // there may be padding before the file data
2086
               if (imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset > 0)
6 pmbaty 2087
                  hex_printf (&filedata[current_offset], imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset, "\n" "%zd padding bytes at offset 0x%zx (%zd):\n", imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset, current_offset, current_offset);
3 pmbaty 2088
               current_offset += imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset; // padding was processed, jump over it
2089
 
2090
               printf ("\n");
2091
               printf ("File data blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
2092
               printf ("   corresponding dirent index: %zd/%zd\n", fsentry_index, fsentry_count);
6 pmbaty 2093
               printf ("   corresponding inode 0x%08x (%d) -%s%s%s%s\n", current_fsentry->header.ino, current_fsentry->header.ino, (current_fsentry->header.ino & 0xE0000000 ? "" : " nothing special"), (current_fsentry->header.ino & IFS_INO_PROCESSED_ELF ? " PROCESSED_ELF" : ""), (current_fsentry->header.ino & IFS_INO_RUNONCE_ELF ? " RUNONCE_ELF" : ""), (current_fsentry->header.ino & IFS_INO_BOOTSTRAP_EXE ? " BOOTSTRAP_EXE" : ""));
3 pmbaty 2094
               printf ("   corresponding path: \"%s\"\n", (char *) &current_fsentry->u.file.path); // convert from pointer to char array
2095
               printf ("   size 0x%zx (%zd) bytes\n", (size_t) current_fsentry->u.file.size, (size_t) current_fsentry->u.file.size);
6 pmbaty 2096
               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" : "data file")));
2097
               printf ("   checksum %d\n", update_checksum (0, &filedata[current_offset], current_fsentry->u.file.size, is_foreign_endianness));
3 pmbaty 2098
 
2099
               current_offset += current_fsentry->u.file.size; // now jump over this file's data
2100
            }
2101
         }
2102
 
2103
         // ad this point we're past the last file data, there may be padding before the image trailer
2104
         if (imagetrailer_offset - current_offset > 0)
6 pmbaty 2105
            hex_printf (&filedata[current_offset], imagetrailer_offset - current_offset, "\n" "%zd padding bytes at offset %zx (%zd):\n", imagetrailer_offset - current_offset, current_offset, current_offset);
3 pmbaty 2106
         current_offset += imagetrailer_offset - current_offset; // padding was processed, jump over it
2107
 
2108
         printf ("\n");
2109
         printf ("Image trailer at offset 0x%zx (%zd) - version %d:\n", current_offset, current_offset, (image_header->flags & IMAGE_FLAGS_TRAILER_V2 ? 2 : 1));
2110
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
2111
         {
2112
            for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
2113
               sprintf (&recorded_sha512[2 * byte_index], "%02x", image_trailer_v2->sha512[byte_index]);
2114
            strcpy (computed_sha512, SHA512 (image_header, (size_t) ((uint8_t *) image_trailer_v2 - (uint8_t *) image_header), NULL));
2115
            recorded_checksum = image_trailer_v2->cksum;
6 pmbaty 2116
            computed_checksum = update_checksum (0, image_header, image_header->image_size - sizeof (image_trailer_v2_t) + SHA512_DIGEST_LENGTH, is_foreign_endianness);
3 pmbaty 2117
            printf ("    sha512 = %s - %s\n", recorded_sha512, (strcasecmp (computed_sha512, recorded_sha512) == 0 ? "GOOD" : "BAD"));
2118
            printf ("    cksum = 0x%08x (%d) - %s\n", recorded_checksum, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
2119
            if (strcasecmp (computed_sha512, recorded_sha512) != 0)
2120
               printf ("Computed SHA-512: %s\n", computed_sha512);
2121
            if (computed_checksum != recorded_checksum)
2122
               printf ("Computed cksum: 0x%08x (%d)\n", computed_checksum, computed_checksum);
2123
         }
2124
         else // old v1 trailer
2125
         {
2126
            recorded_checksum = image_trailer_v1->cksum;
6 pmbaty 2127
            computed_checksum = update_checksum (0, image_header, image_header->image_size - sizeof (image_trailer_v1_t), is_foreign_endianness);
3 pmbaty 2128
            printf ("    cksum = 0x%08x (%d) - %s\n", recorded_checksum, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
2129
            if (computed_checksum != recorded_checksum)
2130
               printf ("Computed cksum: 0x%08x (%d)\n", computed_checksum, computed_checksum);
2131
         }
2132
 
2133
         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 2134
      }
2135
 
2136
      // 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
2137
      else
2138
      {
2139
         // 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)
2140
         for (byte_index = current_offset; byte_index < filesize - 6; byte_index++)
2141
            if (memcmp (&filedata[byte_index], "\xeb\x7e\xff\x00" "\x01\x00", 4 + 2) == 0)
2142
               break; // stop as soon as we find it
2143
 
2144
         if (byte_index >= filesize - 6)
2145
            break; // if not found, stop scanning
2146
 
2147
         bootfile_blobsize = byte_index - current_offset;
2148
         printf ("Boot blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
2149
         printf ("   size 0x%zx (%zd) bytes\n", bootfile_blobsize, bootfile_blobsize);
6 pmbaty 2150
         printf ("   checksum 0x%08x\n", update_checksum (0, &filedata[current_offset], bootfile_blobsize, false)); // NOTE: endianness is not known yet -- assume same
2 pmbaty 2151
 
2152
         current_offset = byte_index; // now reach the next segment
2153
      }
2154
   }
2155
 
3 pmbaty 2156
   // at this point there's nothing left we're able to parse
2 pmbaty 2157
   if (current_offset < filesize)
3 pmbaty 2158
   {
2159
      printf ("End of identifiable data reached.\n");
6 pmbaty 2160
      if (filesize - current_offset < 16384)
2161
         hex_printf (&filedata[current_offset], filesize - current_offset, "\n" "%zd extra bytes at offset %zx (%zd):\n", filesize - current_offset, current_offset, current_offset);
2162
      else
2163
         printf ("\n" "%zd extra bytes at offset %zx (%zd) - size > 16k, not printed\n", filesize - current_offset, current_offset, current_offset);
3 pmbaty 2164
   }
2 pmbaty 2165
 
3 pmbaty 2166
   printf ("End of file reached at offset 0x%zx (%zd)\n", filesize, filesize);
2167
   printf ("IFS dissecation complete.\n");
2 pmbaty 2168
   return (0);
3 pmbaty 2169
}