Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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