Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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