Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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