Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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