Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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

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