Subversion Repositories QNX 8.QNX8 IFS tool

Rev

Rev 12 | Rev 15 | 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
 
12 pmbaty 3
// TODO: preboot file stripping
4
// TODO: startup file stripping
5
// TODO: kernel file stripping
6
// TODO: boot script compiler
7
 
1 pmbaty 8
#include <stdint.h>
9
#include <stdbool.h>
10
#include <stdlib.h>
2 pmbaty 11
#include <stdarg.h>
1 pmbaty 12
#include <stdio.h>
13
#include <string.h>
14
#include <errno.h>
15
#include <sys/stat.h>
16
#include <ctype.h>
17
#include <time.h>
18
 
19
 
11 pmbaty 20
// compiler-specific glue
1 pmbaty 21
#ifdef _MSC_VER
22
#include <io.h>
14 pmbaty 23
#include <direct.h>
24
#include <sys/utime.h>
7 pmbaty 25
#define __x86_64__ 1
5 pmbaty 26
#define __ORDER_BIG_ENDIAN__    4321
1 pmbaty 27
#define __ORDER_LITTLE_ENDIAN__ 1234
28
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
29
#define __attribute__(x)
7 pmbaty 30
#define __builtin_bswap16(x) _byteswap_ushort ((unsigned short) (x))
5 pmbaty 31
#define __builtin_bswap32(x) _byteswap_ulong ((unsigned long) (x))
1 pmbaty 32
#define __builtin_bswap64(x) _byteswap_uint64 ((unsigned long long) (x))
33
#define S_IFIFO 0x1000
34
#define S_IFLNK 0xa000
35
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
36
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
37
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
38
#define strdup(s) _strdup ((s))
2 pmbaty 39
#define strcasecmp(s1,s2) _stricmp ((s1), (s2))
1 pmbaty 40
#define fseek(fp,off,m) _fseeki64 ((fp), (off), (m))
41
#define access(p,m) _access ((p), (m))
14 pmbaty 42
#define mkdir(p,m) _mkdir ((p))
43
#define chmod(p,m) _chmod ((p), (m))
44
#define utimbuf __utimbuf32
45
#define utime(p,t) _utime32 ((p), (t))
1 pmbaty 46
#define MAXPATHLEN 1024
8 pmbaty 47
#ifndef thread_local
48
#define thread_local __declspec(thread) // the thread_local keyword wasn't defined before C++11 and C23
49
#endif // !thread_local
11 pmbaty 50
#define START_OF_PACKED_STRUCT() __pragma(pack(push)) __pragma(pack(1))
51
#define END_OF_PACKED_STRUCT() __pragma(pack(pop))
52
#define PACKED(thing) thing
1 pmbaty 53
#else // !_MSC_VER
54
#include <sys/param.h>
55
#include <unistd.h>
14 pmbaty 56
#include <utime.h>
8 pmbaty 57
#ifndef thread_local
58
#define thread_local __thread // the thread_local keyword wasn't defined before C++11 and C23
59
#endif // !thread_local
11 pmbaty 60
#define START_OF_PACKED_STRUCT()
61
#define END_OF_PACKED_STRUCT()
62
#define PACKED(thing) thing __attribute__((packed))
1 pmbaty 63
#endif // _MSC_VER
64
 
65
 
4 pmbaty 66
// 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 67
#ifndef VERSION_ARG_YYYYMMDD
4 pmbaty 68
#define BUILDDATE_YEAR  (&__DATE__[7]) // compiler will optimize this into a const string, e.g. "2021"
11 pmbaty 69
#define BUILDDATE_MONTH ( \
70
   *((uint32_t *) __DATE__) == *((uint32_t *) "Jan ") ? "01" : \
71
   *((uint32_t *) __DATE__) == *((uint32_t *) "Feb ") ? "02" : \
72
   *((uint32_t *) __DATE__) == *((uint32_t *) "Mar ") ? "03" : \
73
   *((uint32_t *) __DATE__) == *((uint32_t *) "Apr ") ? "04" : \
74
   *((uint32_t *) __DATE__) == *((uint32_t *) "May ") ? "05" : \
75
   *((uint32_t *) __DATE__) == *((uint32_t *) "Jun ") ? "06" : \
76
   *((uint32_t *) __DATE__) == *((uint32_t *) "Jul ") ? "07" : \
77
   *((uint32_t *) __DATE__) == *((uint32_t *) "Aug ") ? "08" : \
78
   *((uint32_t *) __DATE__) == *((uint32_t *) "Sep ") ? "09" : \
79
   *((uint32_t *) __DATE__) == *((uint32_t *) "Oct ") ? "10" : \
80
   *((uint32_t *) __DATE__) == *((uint32_t *) "Nov ") ? "11" : \
81
   *((uint32_t *) __DATE__) == *((uint32_t *) "Dec ") ? "12" : \
82
   "XX" \
83
) // compiler will optimize this into a const string, e.g. "11"
84
#define BUILDDATE_DAY ( \
85
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  1 ") ? "01" : \
86
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  2 ") ? "02" : \
87
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  3 ") ? "03" : \
88
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  4 ") ? "04" : \
89
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  5 ") ? "05" : \
90
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  6 ") ? "06" : \
91
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  7 ") ? "07" : \
92
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  8 ") ? "08" : \
93
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  9 ") ? "09" : \
94
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 10 ") ? "10" : \
95
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 11 ") ? "11" : \
96
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 12 ") ? "12" : \
97
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 13 ") ? "13" : \
98
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 14 ") ? "14" : \
99
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 15 ") ? "15" : \
100
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 16 ") ? "16" : \
101
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 17 ") ? "17" : \
102
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 18 ") ? "18" : \
103
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 19 ") ? "19" : \
104
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 20 ") ? "20" : \
105
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 21 ") ? "21" : \
106
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 22 ") ? "22" : \
107
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 23 ") ? "23" : \
108
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 24 ") ? "24" : \
109
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 25 ") ? "25" : \
110
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 26 ") ? "26" : \
111
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 27 ") ? "27" : \
112
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 28 ") ? "28" : \
113
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 29 ") ? "29" : \
114
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 30 ") ? "30" : \
115
   *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 31 ") ? "31" : \
116
   "XX" \
117
) // compiler will optimize this into a const string, e.g. "14"
14 pmbaty 118
#define VERSION_FMT_YYYYMMDD "%s%s%s"
4 pmbaty 119
#define VERSION_ARG_YYYYMMDD BUILDDATE_YEAR, BUILDDATE_MONTH, BUILDDATE_DAY
7 pmbaty 120
#endif // !VERSION_ARG_YYYYMMDD
4 pmbaty 121
 
122
 
11 pmbaty 123
// macro to bring __FILE_NAME__ support to moronic compilers
124
#ifndef __FILE_NAME__ // Clang 9+ has the macro, GCC 12+ added it too in 2021, MSVC obviously won't do it. Heh.
125
#define __FILE_NAME__ ( \
126
   (sizeof (__FILE__) >  2) && (__FILE__[sizeof (__FILE__) -  2] == '/') ? &__FILE__[sizeof (__FILE__) -  1] : \
127
   (sizeof (__FILE__) >  3) && (__FILE__[sizeof (__FILE__) -  3] == '/') ? &__FILE__[sizeof (__FILE__) -  2] : \
128
   (sizeof (__FILE__) >  4) && (__FILE__[sizeof (__FILE__) -  4] == '/') ? &__FILE__[sizeof (__FILE__) -  3] : \
129
   (sizeof (__FILE__) >  5) && (__FILE__[sizeof (__FILE__) -  5] == '/') ? &__FILE__[sizeof (__FILE__) -  4] : \
130
   (sizeof (__FILE__) >  6) && (__FILE__[sizeof (__FILE__) -  6] == '/') ? &__FILE__[sizeof (__FILE__) -  5] : \
131
   (sizeof (__FILE__) >  7) && (__FILE__[sizeof (__FILE__) -  7] == '/') ? &__FILE__[sizeof (__FILE__) -  6] : \
132
   (sizeof (__FILE__) >  8) && (__FILE__[sizeof (__FILE__) -  8] == '/') ? &__FILE__[sizeof (__FILE__) -  7] : \
133
   (sizeof (__FILE__) >  9) && (__FILE__[sizeof (__FILE__) -  9] == '/') ? &__FILE__[sizeof (__FILE__) -  8] : \
134
   (sizeof (__FILE__) > 10) && (__FILE__[sizeof (__FILE__) - 10] == '/') ? &__FILE__[sizeof (__FILE__) -  9] : \
135
   (sizeof (__FILE__) > 11) && (__FILE__[sizeof (__FILE__) - 11] == '/') ? &__FILE__[sizeof (__FILE__) - 10] : \
136
   (sizeof (__FILE__) > 12) && (__FILE__[sizeof (__FILE__) - 12] == '/') ? &__FILE__[sizeof (__FILE__) - 11] : \
137
   (sizeof (__FILE__) > 13) && (__FILE__[sizeof (__FILE__) - 13] == '/') ? &__FILE__[sizeof (__FILE__) - 12] : \
138
   (sizeof (__FILE__) > 14) && (__FILE__[sizeof (__FILE__) - 14] == '/') ? &__FILE__[sizeof (__FILE__) - 13] : \
139
   (sizeof (__FILE__) > 15) && (__FILE__[sizeof (__FILE__) - 15] == '/') ? &__FILE__[sizeof (__FILE__) - 14] : \
140
   (sizeof (__FILE__) > 16) && (__FILE__[sizeof (__FILE__) - 16] == '/') ? &__FILE__[sizeof (__FILE__) - 15] : \
141
   (sizeof (__FILE__) > 17) && (__FILE__[sizeof (__FILE__) - 17] == '/') ? &__FILE__[sizeof (__FILE__) - 16] : \
142
   (sizeof (__FILE__) > 18) && (__FILE__[sizeof (__FILE__) - 18] == '/') ? &__FILE__[sizeof (__FILE__) - 17] : \
143
   (sizeof (__FILE__) > 19) && (__FILE__[sizeof (__FILE__) - 19] == '/') ? &__FILE__[sizeof (__FILE__) - 18] : \
144
   (sizeof (__FILE__) > 20) && (__FILE__[sizeof (__FILE__) - 20] == '/') ? &__FILE__[sizeof (__FILE__) - 19] : \
145
   (sizeof (__FILE__) > 21) && (__FILE__[sizeof (__FILE__) - 21] == '/') ? &__FILE__[sizeof (__FILE__) - 20] : \
146
   (sizeof (__FILE__) > 22) && (__FILE__[sizeof (__FILE__) - 22] == '/') ? &__FILE__[sizeof (__FILE__) - 21] : \
147
   (sizeof (__FILE__) > 23) && (__FILE__[sizeof (__FILE__) - 23] == '/') ? &__FILE__[sizeof (__FILE__) - 22] : \
148
   (sizeof (__FILE__) > 24) && (__FILE__[sizeof (__FILE__) - 24] == '/') ? &__FILE__[sizeof (__FILE__) - 23] : \
149
   (sizeof (__FILE__) > 25) && (__FILE__[sizeof (__FILE__) - 25] == '/') ? &__FILE__[sizeof (__FILE__) - 24] : \
150
   (sizeof (__FILE__) > 26) && (__FILE__[sizeof (__FILE__) - 26] == '/') ? &__FILE__[sizeof (__FILE__) - 25] : \
151
   (sizeof (__FILE__) > 27) && (__FILE__[sizeof (__FILE__) - 27] == '/') ? &__FILE__[sizeof (__FILE__) - 26] : \
152
   (sizeof (__FILE__) > 28) && (__FILE__[sizeof (__FILE__) - 28] == '/') ? &__FILE__[sizeof (__FILE__) - 27] : \
153
   (sizeof (__FILE__) > 29) && (__FILE__[sizeof (__FILE__) - 29] == '/') ? &__FILE__[sizeof (__FILE__) - 28] : \
154
   (sizeof (__FILE__) > 30) && (__FILE__[sizeof (__FILE__) - 30] == '/') ? &__FILE__[sizeof (__FILE__) - 29] : \
155
   (sizeof (__FILE__) > 31) && (__FILE__[sizeof (__FILE__) - 31] == '/') ? &__FILE__[sizeof (__FILE__) - 30] : \
156
   (sizeof (__FILE__) > 32) && (__FILE__[sizeof (__FILE__) - 32] == '/') ? &__FILE__[sizeof (__FILE__) - 31] : \
157
   (sizeof (__FILE__) > 33) && (__FILE__[sizeof (__FILE__) - 33] == '/') ? &__FILE__[sizeof (__FILE__) - 32] : \
158
   __FILE__) // this *COMPILE-TIME* macro complements the __FILE__ macro defined by the C standard by returning just the filename portion of the full path. Supports filenames up to 32 chars. Expand as necessary.
159
#endif // !__FILE_NAME__
8 pmbaty 160
 
161
 
11 pmbaty 162
// macro to exit less brutally than with abort() if something doesn't go the way we'd like to
163
#define WELLMANNERED_ASSERT(is_is_true,...) do { if (!(is_is_true)) { fprintf (stderr, "ifstool: fatal error: consistency check in %s() at %s line %d failed: ", __FUNCTION__, __FILE_NAME__, __LINE__); fprintf (stderr, __VA_ARGS__); fputc ('\n', stderr); exit (1); } } while (0)
8 pmbaty 164
 
165
 
11 pmbaty 166
// macros for checked read/write/seek operations
167
#define fseek_or_die(fp,pos,mode) WELLMANNERED_ASSERT (fseek ((fp), (pos), (mode)) == 0, "fseek() failed with errno %d (%s)", errno, strerror (errno))
168
#define fread_or_die(buf,sz,len,fp) WELLMANNERED_ASSERT (fread ((buf), (sz), (len), (fp)) == (len), "fread() failed with errno %d (%s)", errno, strerror (errno))
169
#define fwrite_or_die(buf,sz,len,fp) WELLMANNERED_ASSERT ((fwrite ((buf), (sz), (len), (fp)) == (len)) && (fflush ((fp)) == 0), "flushed fwrite() failed with errno %d (%s)", errno, strerror (errno))
8 pmbaty 170
 
171
 
9 pmbaty 172
// macros for accessing ELF files
7 pmbaty 173
#define ELF_MAGIC_STR "\x7f" "ELF"
10 pmbaty 174
#define ELF_ENDIAN_LITTLE 1 // 'endianness' member of an ELF header: ELF file is little endian
175
#define ELF_ENDIAN_BIG    2 // 'endianness' member of an ELF header: ELF file is big endian
176
#define ELF_MACHINE_X86_64  0x3e // 'instruction_set' member of an ELF header, also used in the IFS startup header: ELF file is for x86_64 processors (62 decimal)
177
#define ELF_MACHINE_AARCH64 0xb7 // 'instruction_set' member of an ELF header, also used in the IFS startup header: ELF file is for ARM64 processors (183 decimal)
178
#define ELF_SECTIONTYPE_STRINGTABLE 3
7 pmbaty 179
#define ELF_DT_NULL    0 // marks end of dynamic section
180
#define ELF_DT_SONAME 14 // canonical name of shared object
10 pmbaty 181
#define ELF_GET_NUMERIC(elfhdr,elfstruct,member) ((elfhdr)->u.elf.platform_size == 2 ? /* is it a 64-bit ELF file ? */ \
9 pmbaty 182
   ( \
183
      (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 ? */ \
184
         (elfstruct)->u.elf64.member /* same endianness, or single byte required: don't swap */ \
185
      : /* else */ \
186
         (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 */ \
187
   ) \
188
   : /* else peek at 32-bit ELF */ \
189
   ( \
190
      (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 ? */ \
191
         (elfstruct)->u.elf32.member /* same endianness, or single byte required: don't swap */ \
192
      : /* else */ \
193
         (sizeof ((elfstruct)->u.elf32.member) == 4 ? __builtin_bswap32 ((elfstruct)->u.elf32.member) : __builtin_bswap16 ((elfstruct)->u.elf32.member)) /* different endianness: swap */ \
194
   ) \
195
) // this macro supports 32- and 64-bit ELF files in low and big endianness transparently
10 pmbaty 196
#define ELF_SET_NUMERIC(elfhdr,elfstruct,member,data) ((elfhdr)->u.elf.platform_size == 2 ? /* is it a 64-bit ELF file ? */ \
197
   ((elfstruct)->u.elf64.member = ( \
198
      (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 ? */ \
199
         (sizeof ((elfstruct)->u.elf64.member) == 8 ? (uint64_t) ((data)) : (sizeof ((elfstruct)->u.elf64.member) == 4 ? (uint32_t) ((data)) : (sizeof ((elfstruct)->u.elf64.member) == 2 ? (uint16_t) ((data)) : (uint8_t) ((data))))) /* same endianness, or single byte required: don't swap */ \
200
      : /* else */ \
201
         (sizeof ((elfstruct)->u.elf64.member) == 8 ? __builtin_bswap64 ((data)) : (sizeof ((elfstruct)->u.elf64.member) == 4 ? __builtin_bswap32 ((data)) : __builtin_bswap16 ((data)))) /* different endianness: swap */ \
202
   )) \
203
   : /* else poke at 32-bit ELF */ \
204
   ((elfstruct)->u.elf32.member = ( \
205
      (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 ? */ \
206
         (sizeof ((elfstruct)->u.elf64.member) == 4 ? (uint32_t) ((data)) : (sizeof ((elfstruct)->u.elf64.member) == 2 ? (uint16_t) ((data)) : (uint8_t) ((data)))) /* same endianness, or single byte required: don't swap */ \
207
      : /* else */ \
208
         (sizeof ((elfstruct)->u.elf32.member) == 4 ? __builtin_bswap32 ((data)) : __builtin_bswap16 ((data))) /* different endianness: swap */ \
209
   )) \
210
) // this macro supports 32- and 64-bit ELF files in low and big endianness transparently
211
#define ELF_GET_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
212
#define ELF_SET_STRING(elfhdr,elfstruct,member,data,len) memcpy (((elfhdr)->u.elf.platform_size == 2 ? (elfstruct)->u.elf64.member : (elfstruct)->u.elf32.member), (data), (len)) // this macro supports 32- and 64-bit ELF files transparently
9 pmbaty 213
#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 214
 
10 pmbaty 215
 
11 pmbaty 216
// placeholder value
217
#define WILL_BE_FILLED_LATER 0xbaadf00d // urgh
10 pmbaty 218
 
1 pmbaty 219
 
220
// bitmapped flags used in the flags1 member of the startup header
221
#define STARTUP_HDR_FLAGS1_VIRTUAL        (1 << 0)
222
#define STARTUP_HDR_FLAGS1_BIGENDIAN      (1 << 1)
223
//#define STARTUP_HDR_FLAGS1_COMPRESS_MASK  0x1c
224
//#define STARTUP_HDR_FLAGS1_COMPRESS_SHIFT 0x02
225
//#define STARTUP_HDR_FLAGS1_COMPRESS_NONE  0x00
226
//#define STARTUP_HDR_FLAGS1_COMPRESS_ZLIB  0x04
227
//#define STARTUP_HDR_FLAGS1_COMPRESS_LZO   0x08
228
//#define STARTUP_HDR_FLAGS1_COMPRESS_UCL   0x0c
229
#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
230
 
231
 
232
// bitmapped flags used in the flags member of the image header
233
#define IMAGE_FLAGS_BIGENDIAN  (1 << 0) // header, trailer, dirents in big-endian format
234
#define IMAGE_FLAGS_READONLY   (1 << 1) // do not try to write to image (rom/flash)
235
#define IMAGE_FLAGS_INO_BITS   (1 << 2) // inode bits valid
236
#define IMAGE_FLAGS_SORTED     (1 << 3) // dirent section is sorted (by pathname)
237
#define IMAGE_FLAGS_TRAILER_V2 (1 << 4) // image uses struct image_trailer_v2
238
 
239
 
240
// bitmapped flags superposed to a filesystem entry's inode number
241
#define IFS_INO_PROCESSED_ELF 0x80000000
242
#define IFS_INO_RUNONCE_ELF   0x40000000
243
#define IFS_INO_BOOTSTRAP_EXE 0x20000000
244
 
245
 
11 pmbaty 246
// miscellaneous macros
247
#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
248
#ifdef _WIN32
249
#define IS_DIRSEP(c) (((c) == '/') || ((c) == '\\')) // platform-specific directory separator, Win32 variant
250
#define PATH_SEP ';' // platform-specific PATH element separator, Win32 variant
251
#define PATH_SEP_STR ";" // platform-specific PATH element separator (as string), Win32 variant
252
#else // !_WIN32, thus POSIX
253
#define IS_DIRSEP(c) ((c) == '/') // platform-specific directory separator, UNIX variant
254
#define PATH_SEP ':' // platform-specific PATH element separator, UNIX variant
255
#define PATH_SEP_STR ":" // platform-specific PATH element separator (as string), UNIX variant
256
#endif // _WIN32
257
#define RECORD_SEP '\x1e' // arbitrarily-chosen ASCII record separator
258
#define RECORD_SEP_STR "\x1e" // arbitrarily-chosen ASCII record separator (as string)
259
 
260
 
1 pmbaty 261
// SHA-512 block and digest sizes
262
#define SHA512_BLOCK_LENGTH 128 // in bytes
263
#define SHA512_DIGEST_LENGTH 64 // in bytes
264
 
265
 
266
// SHA-512 computation context structure type definition
267
typedef struct sha512_ctx_s
268
{
269
   uint64_t state[8];
270
   uint64_t bitcount[2];
271
   uint8_t buffer[SHA512_BLOCK_LENGTH];
272
} SHA512_CTX;
273
 
274
 
275
#if 0 // TODO: startup script compiler. Someday.
276
#define SCRIPT_FLAGS_EXTSCHED   0x01
277
#define SCRIPT_FLAGS_SESSION    0x02
278
#define SCRIPT_FLAGS_SCHED_SET  0x04
279
#define SCRIPT_FLAGS_CPU_SET    0x08
280
#define SCRIPT_FLAGS_BACKGROUND 0x20
281
#define SCRIPT_FLAGS_KDEBUG     0x40
282
 
283
#define SCRIPT_POLICY_NOCHANGE 0
284
#define SCRIPT_POLICY_FIFO     1
285
#define SCRIPT_POLICY_RR       2
286
#define SCRIPT_POLICY_OTHER    3
287
 
288
#define SCRIPT_TYPE_EXTERNAL        0
289
#define SCRIPT_TYPE_WAITFOR         1
290
#define SCRIPT_TYPE_REOPEN          2
291
#define SCRIPT_TYPE_DISPLAY_MSG     3
292
#define SCRIPT_TYPE_PROCMGR_SYMLINK 4
293
#define SCRIPT_TYPE_EXTSCHED_APS    5
294
 
295
#define SCRIPT_CHECKS_MS 100
296
 
297
#define SCRIPT_SCHED_EXT_NONE 0
298
#define SCRIPT_SCHED_EXT_APS  1
299
 
300
#define SCRIPT_APS_SYSTEM_PARTITION_ID   0
301
#define SCRIPT_APS_SYSTEM_PARTITION_NAME "System"
302
#define SCRIPT_APS_PARTITION_NAME_LENGTH 15
303
#define SCRIPT_APS_MAX_PARTITIONS        8
304
 
305
 
11 pmbaty 306
START_OF_PACKED_STRUCT () // we need byte-alignment for this struct
307
typedef PACKED (struct) bootscriptcmd_header_s
1 pmbaty 308
{
309
   uint16_t size; // size of cmd entry
310
   uint8_t type;
311
   uint8_t spare;
312
} bootscriptcmd_header_t;
11 pmbaty 313
END_OF_PACKED_STRUCT () // restore default alignment
1 pmbaty 314
 
315
 
11 pmbaty 316
START_OF_PACKED_STRUCT () // we need byte-alignment for this struct
1 pmbaty 317
typedef union bootscriptcmd_s
318
{
11 pmbaty 319
   PACKED (struct) script_external
1 pmbaty 320
   {
321
      bootscriptcmd_header_t hdr;
322
      uint8_t cpu; // CPU (turn into runmask)
323
      uint8_t flags;
324
      union script_external_extsched
325
      {
326
         uint8_t reserved[2];
11 pmbaty 327
         PACKED (struct)
1 pmbaty 328
         {
329
            uint8_t id;
330
            uint8_t reserved[1];
331
         } aps;
332
      } extsched; // extended scheduler
333
      uint8_t policy; // POLICY_FIFO, POLICY_RR, ...
334
      uint8_t priority; // priority to run cmd at
335
      uint8_t argc; // # of args
336
      uint8_t envc; // # of environment entries
337
      char args[0]; // executable, argv, envp (null padded to 32-bit align)
338
   } external;
11 pmbaty 339
   PACKED (struct) script_waitfor_reopen
1 pmbaty 340
   {
341
      bootscriptcmd_header_t hdr;
342
      uint16_t checks;
343
      char fname[0]; // char fname[] (null padded to 32-bit align)
344
   } waitfor_reopen;
11 pmbaty 345
   PACKED (struct) script_display_msg
1 pmbaty 346
   {
347
      bootscriptcmd_header_t hdr;
348
      char msg[0]; // char msg[] (null padded to 32-bit align)
349
   } display_msg;
11 pmbaty 350
   PACKED (struct) script_procmgr_symlink
1 pmbaty 351
   {
352
      bootscriptcmd_header_t hdr;
353
      char src_dest[0]; // <src_name>, '\0', <dest_name> '\0' (null padded to 32-bit align)
354
   } procmgr_symlink;
11 pmbaty 355
   PACKED (struct) script_extsched_aps
1 pmbaty 356
   {
357
      bootscriptcmd_header_t hdr;
358
      uint8_t parent;
359
      uint8_t budget;
360
      uint16_t critical;
361
      uint8_t id;
362
      char pname[0]; // char pname[] (null padded to 32-bit align)
363
   } extsched_aps;
364
} bootscriptcmd_t;
11 pmbaty 365
END_OF_PACKED_STRUCT () // restore default alignment
1 pmbaty 366
#endif // 0
367
 
368
 
369
#define INITIAL_STARTUP_SCRIPT \
370
   /* procmgr_symlink /proc/boot/ldqnx-64.so.2 /usr/lib/ldqnx-64.so.2 */ \
7 pmbaty 371
   "\x34\x00" /*size*/ "\x04" /*type*/ "\x00" /*spare*/ "/proc/boot/ldqnx-64.so.2\0" "/usr/lib/ldqnx-64.so.2\0" \
1 pmbaty 372
   /* sh /proc/boot/startup.sh */ \
373
   "\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*/ \
374
   /* display_msg "Startup complete */ \
375
   "\x18\x00" /*size*/ "\x03" /*type*/ "\x00" /*spare*/ "Startup complete\n\0" "\x00\00" /*padding*/ \
376
   /* trailer */ \
377
   "\x00\x00\x00\x00"
378
 
379
 
11 pmbaty 380
START_OF_PACKED_STRUCT () // we need byte-alignment for this struct
381
typedef PACKED (struct) fsentry_s
1 pmbaty 382
{
11 pmbaty 383
   PACKED (struct) fsentry_header_s
1 pmbaty 384
   {
385
      uint16_t size; // size of dirent
386
      uint16_t extattr_offset; // if zero, no extattr data
387
      uint32_t ino; // if zero, skip entry
388
      uint32_t mode; // mode and perms of entry
389
      uint32_t gid;
390
      uint32_t uid;
391
      uint32_t mtime;
392
   } header;
11 pmbaty 393
   PACKED (union) fsentry_specific_u
1 pmbaty 394
   {
11 pmbaty 395
      PACKED (struct) fsentry_file_s // when (mode & S_IFMT) == S_IFREG
1 pmbaty 396
      {
397
         uint32_t offset; // offset from header
398
         uint32_t size;
399
         char *path; // null terminated path (no leading slash)
400
         char *UNSAVED_databuf; // file data blob buffer (NOT SAVED IN THE IFS)
401
      } file;
11 pmbaty 402
      PACKED (struct) fsentry_dir_s // when (mode & S_IFMT) == S_IFDIR
1 pmbaty 403
      {
404
         char *path; // null terminated path (no leading slash)
405
      } dir;
11 pmbaty 406
      PACKED (struct) fsentry_symlink_s // when (mode & S_IFMT) == S_IFLNK
1 pmbaty 407
      {
408
         uint16_t sym_offset; // offset to 'contents' from 'path'
409
         uint16_t sym_size; // strlen (contents)
410
         char *path; // null terminated path (no leading slash)
411
         char *contents; // null terminated symlink contents
412
      } symlink;
11 pmbaty 413
      PACKED (struct) fsentry_device_s // when (mode & S_IFMT) == S_IF<CHR|BLK|FIFO|NAM|SOCK>
1 pmbaty 414
      {
415
         uint32_t dev;
416
         uint32_t rdev;
417
         char *path; // null terminated path (no leading slash)
418
      } device;
419
   } u;
420
   bool UNSAVED_was_data_written; // whether this entry's data was written to the image (NOT SAVED IN THE IFS)
421
} fsentry_t;
11 pmbaty 422
END_OF_PACKED_STRUCT () // restore default alignment
1 pmbaty 423
 
424
 
11 pmbaty 425
START_OF_PACKED_STRUCT () // we need byte-alignment for this struct
426
typedef PACKED (struct) startup_header_s // size 256 bytes
1 pmbaty 427
{
11 pmbaty 428
   // I - used by the QNX IPL
429
   // S - used by the startup program
1 pmbaty 430
   uint8_t signature[4];   // [I ] Header signature, "\xeb\x7e\xff\x00"
431
   uint16_t version;       // [I ] Header version, i.e. 1
432
   uint8_t flags1;         // [IS] Misc flags, 0x21 (= 0x20 | STARTUP_HDR_FLAGS1_VIRTUAL)
433
   uint8_t flags2;         // [  ] No flags defined yet (0)
434
   uint16_t header_size;   // [ S] sizeof(struct startup_header), i.e. 256
435
   uint16_t machine;       // [IS] Machine type from elfdefinitions.h, i.e. 0x003E --> _ELF_DEFINE_EM(EM_X86_64, 62, "AMD x86-64 architecture")
436
   uint32_t startup_vaddr; // [I ] Virtual Address to transfer to after IPL is done, here 0x01403008 (appears in "Entry" column for "startup.*")
437
   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)
438
   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)
439
   uint32_t ram_paddr;     // [IS] Physical address of RAM to copy image to (startup_size bytes copied), here 0x01400f30 (same as above)
440
   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
441
   uint32_t startup_size;  // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes
442
   uint32_t stored_size;   // [I ] Size of entire image, here 0x00cd6128 (same as ram_size)
443
   uint32_t imagefs_paddr; // [IS] Set by IPL to where the imagefs is when startup runs (0)
444
   uint32_t imagefs_size;  // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes
445
   uint16_t preboot_size;  // [I ] Size of loaded before header, here 0xf30 or 3888 bytes (size of "bios.boot" file))
446
   uint16_t zero0;         // [  ] Zeros
447
   uint32_t zero[1];       // [  ] Zeros
448
   uint64_t addr_off;      // [ S] Offset to add to startup_vaddr, image_paddr, ram_paddr, and imagefs_paddr members, here zero (0)
449
   uint32_t info[48];      // [IS] Array of startup_info* structures (zero filled)
450
} startup_header_t;
11 pmbaty 451
END_OF_PACKED_STRUCT () // restore default alignment
1 pmbaty 452
 
453
 
11 pmbaty 454
START_OF_PACKED_STRUCT () // we need byte-alignment for this struct
455
typedef PACKED (struct) startup_trailer_s
1 pmbaty 456
{
457
   uint32_t cksum; // checksum from start of header to start of trailer
2 pmbaty 458
} startup_trailer_v1_t;
11 pmbaty 459
END_OF_PACKED_STRUCT () // restore default alignment
1 pmbaty 460
 
461
 
462
// NOTE: The checksums in this trailer will only be valid prior to entering startup.
463
// Because the startup binary is executed in-place, its data segment will change once the program is running.
464
// Hence, any checksum validation would need to be done by the boot loader / IFS.
11 pmbaty 465
START_OF_PACKED_STRUCT () // we need byte-alignment for this struct
466
typedef PACKED (struct) startup_trailer_v2_s
1 pmbaty 467
{
468
   uint8_t sha512[64]; // SHA512 from start of header to start of trailer
469
   uint32_t cksum; // checksum from start of header to start of this member
470
} startup_trailer_v2_t;
11 pmbaty 471
END_OF_PACKED_STRUCT () // restore default alignment
1 pmbaty 472
 
473
 
11 pmbaty 474
START_OF_PACKED_STRUCT () // we need byte-alignment for this struct
475
typedef PACKED (struct) image_header_s
1 pmbaty 476
{
477
   uint8_t signature[7]; // image filesystem signature, i.e. "imagefs"
478
   uint8_t flags; // endian neutral flags, 0x1c
6 pmbaty 479
   uint32_t image_size; // size from start of header to end of trailer (here 0xca6fe0 or 13 266 912)
480
   uint32_t hdr_dir_size; // size from start of header to last dirent (here 0x12b8 or 4792)
481
   uint32_t dir_offset; // offset from start of header to start of first dirent (here 0x5c or 92)
1 pmbaty 482
   uint32_t boot_ino[4]; // inode of files for bootstrap pgms (here 0xa0000002, 0, 0, 0)
483
   uint32_t script_ino; // inode of file for script (here 3)
484
   uint32_t chain_paddr; // offset to next filesystem signature (0)
485
   uint32_t spare[10]; // zerofill
486
   uint32_t mountflags; // default _MOUNT_* from sys/iomsg.h (0)
487
   char mountpoint[4]; // default mountpoint for image ("/" + "\0\0\0")
488
} image_header_t;
11 pmbaty 489
END_OF_PACKED_STRUCT () // restore default alignment
1 pmbaty 490
 
491
 
11 pmbaty 492
START_OF_PACKED_STRUCT () // we need byte-alignment for this struct
493
typedef PACKED (struct) image_trailer_v1_s
1 pmbaty 494
{
495
   uint32_t cksum; // checksum from start of header to start of trailer
2 pmbaty 496
} image_trailer_v1_t; // NOTE: this is the same structure as startup_trailer_v1_t
11 pmbaty 497
END_OF_PACKED_STRUCT () // restore default alignment
1 pmbaty 498
 
499
 
500
// NOTE: the checksums in this trailer will only be valid until the first non-startup bootstrap binary (e.g., startup-verifier, procnto, ...) is invoked.
501
// Because bootstrap binaries execute in-place, their data segments will change once the programs are running.
502
// Hence, any checksum validation would need to be done either by the boot loader / IFS or by the startup.
11 pmbaty 503
START_OF_PACKED_STRUCT () // we need byte-alignment for this struct
504
typedef PACKED (struct) image_trailer_v2_s
1 pmbaty 505
{
506
   uint8_t sha512[64]; // SHA512 from start of image header to start of trailer
507
   uint32_t cksum; // checksum from start of header to start of this member
2 pmbaty 508
} image_trailer_v2_t; // NOTE: this is the same structure as startup_trailer_v2_t
11 pmbaty 509
END_OF_PACKED_STRUCT () // restore default alignment
1 pmbaty 510
 
511
 
7 pmbaty 512
// Executable and Linkable Format master header structure type definition
11 pmbaty 513
START_OF_PACKED_STRUCT () // we need byte-alignment for this struct
514
typedef PACKED (struct) elf_header_s
7 pmbaty 515
{
11 pmbaty 516
   PACKED (union)
7 pmbaty 517
   {
11 pmbaty 518
      PACKED (struct)
7 pmbaty 519
      {
10 pmbaty 520
         uint8_t magic[4];                     // offset 0: "\x7f" + "ELF"
7 pmbaty 521
         uint8_t platform_size;                // offset 4: 1 = 32-bit, 2 = 64-bit
522
         uint8_t endianness;                   // offset 5: 1 = little endian, 2 = big endian
523
         uint8_t header_version;               // offset 6: typically 1
524
         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
525
         uint8_t spare[8];                     // offset 8: zeroes
526
         uint16_t type;                        // offset 16: 1 = relocatable, 2 = executable, 3 = shared, 4 = core dump
527
         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
528
         uint32_t elf_version;                 // offset 20: typically 1
529
      } elf;
11 pmbaty 530
      PACKED (struct) // size == 52
7 pmbaty 531
      {
10 pmbaty 532
         uint8_t magic[4];                     // offset 0: "\x7f" + "ELF"
7 pmbaty 533
         uint8_t platform_size;                // offset 4: 1 = 32-bit, 2 = 64-bit
534
         uint8_t endianness;                   // offset 5: 1 = little endian, 2 = big endian
535
         uint8_t header_version;               // offset 6: typically 1
536
         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
537
         uint8_t spare[8];                     // offset 8: zeroes
538
         uint16_t type;                        // offset 16: 1 = relocatable, 2 = executable, 3 = shared, 4 = core dump
539
         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
540
         uint32_t elf_version;                 // offset 20: typically 1
541
         uint32_t entrypoint_offset;           // offset 24: offset to program entrypoint
542
         uint32_t program_header_table_offset; // offset 28: offset to program header table
543
         uint32_t section_header_table_offset; // offset 32: offset to section header table
544
         uint32_t flags;                       // offset 36: flags (architecture-dependent, none for x86)
545
         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!
546
         uint16_t program_header_item_size;    // offset 42: size of an entry in the program header table
547
         uint16_t program_header_table_len;    // offset 44: number of entries in the program header table
548
         uint16_t section_header_item_size;    // offset 46: size of an entry in the section header table
549
         uint16_t section_header_table_len;    // offset 48: number of entries in the section header table
550
         uint16_t section_header_names_idx;    // offset 50: index of the entry in the section header table that contains the section names
10 pmbaty 551
      } elf32; // size == 52
11 pmbaty 552
      PACKED (struct) // size == 64
7 pmbaty 553
      {
10 pmbaty 554
         uint8_t magic[4];                     // offset 0: "\x7f" + "ELF"
7 pmbaty 555
         uint8_t platform_size;                // offset 4: 1 = 32-bit, 2 = 64-bit
556
         uint8_t endianness;                   // offset 5: 1 = little endian, 2 = big endian
557
         uint8_t header_version;               // offset 6: typically 1
558
         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
559
         uint8_t spare[8];                     // offset 8: zeroes
560
         uint16_t type;                        // offset 16: 1 = relocatable, 2 = executable, 3 = shared, 4 = core dump
561
         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
562
         uint32_t elf_version;                 // offset 20: typically 1
563
         uint64_t entrypoint_offset;           // offset 24: program entry offset
564
         uint64_t program_header_table_offset; // offset 32: offset to program header table
565
         uint64_t section_header_table_offset; // offset 40: offset to section header table
566
         uint32_t flags;                       // offset 48: flags (architecture-dependent, none for x86)
567
         uint16_t header_size;                 // offset 52: size of ELF header, 52 for 32-bit ELF and 64 for 64-bit ELF
568
         uint16_t program_header_item_size;    // offset 54: size of an entry in the program header table
569
         uint16_t program_header_table_len;    // offset 56: number of entries in the program header table
570
         uint16_t section_header_item_size;    // offset 58: size of an entry in the section header table
571
         uint16_t section_header_table_len;    // offset 60: number of entries in the section header table
572
         uint16_t section_header_names_idx;    // offset 62: index of the entry in the section header table that contains the section names
10 pmbaty 573
      } elf64; // size == 64
7 pmbaty 574
   } u;
575
} elf_header_t;
11 pmbaty 576
END_OF_PACKED_STRUCT () // restore default alignment
7 pmbaty 577
 
578
 
10 pmbaty 579
// Executable and Linkable Format program header structure type definition
11 pmbaty 580
START_OF_PACKED_STRUCT () // we need byte-alignment for this struct
581
typedef PACKED (struct) elf_program_header_s
10 pmbaty 582
{
11 pmbaty 583
   PACKED (union)
10 pmbaty 584
   {
11 pmbaty 585
      PACKED (struct)
10 pmbaty 586
      {
587
         uint32_t segment_type; // offset 0: type of segment (0: unused table entry, 1: loadable, 2: dynamic linking information, 3: interpreter information, 4: auxiliary information, 5: reserved, 6: this very segment, 7: TLS template)
588
      } elf;
11 pmbaty 589
      PACKED (struct) // size == 32
10 pmbaty 590
      {
591
         uint32_t segment_type;   // offset 0: type of segment (0: unused table entry, 1: loadable, 2: dynamic linking information, 3: interpreter information, 4: auxiliary information, 5: reserved, 6: this very segment, 7: TLS template)
592
         uint32_t file_offset;    // offset 4: file offset of this segment
593
         uint32_t virtual_addr;   // offset 8: virtual address where this segment should be mapped in memory
594
         uint32_t physical_addr;  // offset 12: on systems where this is relevant, PHYSICAL address where this segment should be mapped in memory
595
         uint32_t size_in_file;   // offset 16: size of this segment in the ELF file (may be zero)
596
         uint32_t size_in_memory; // offset 20: size of this segment in memory (may be zero)
597
         uint32_t segment_flags;  // offset 24: bitmap of segment flags (1: executable, 2: writable, 4: readable)
598
         uint32_t alignment;      // offset 28: memory alignment (0 or 1 mean non alignment, else must be a power of 2 where virtual_addr == file_offset % alignment)
599
      } elf32; // size == 32
11 pmbaty 600
      PACKED (struct) // size == 56
10 pmbaty 601
      {
602
         uint32_t segment_type;   // offset 0: type of segment (0: unused table entry, 1: loadable, 2: dynamic linking information, 3: interpreter information, 4: auxiliary information, 5: reserved, 6: this very segment, 7: TLS template)
603
         uint32_t segment_flags;  // offset 4: bitmap of segment flags (1: executable, 2: writable, 4: readable)
604
         uint64_t file_offset;    // offset 8: file offset of this segment
605
         uint64_t virtual_addr;   // offset 16: virtual address where this segment should be mapped in memory
606
         uint64_t physical_addr;  // offset 24: on systems where this is relevant, PHYSICAL address where this segment should be mapped in memory
607
         uint64_t size_in_file;   // offset 32: size of this segment in the ELF file (may be zero)
608
         uint64_t size_in_memory; // offset 40: size of this segment in memory (may be zero)
609
         uint64_t alignment;      // offset 48: memory alignment (0 or 1 mean non alignment, else must be a power of 2 where virtual_addr == file_offset % alignment)
610
      } elf64; // size == 56
611
   } u;
612
} elf_program_header_t;
11 pmbaty 613
END_OF_PACKED_STRUCT () // restore default alignment
10 pmbaty 614
 
615
 
7 pmbaty 616
// Executable and Linkable Format section header structure type definition
11 pmbaty 617
START_OF_PACKED_STRUCT () // we need byte-alignment for this struct
618
typedef PACKED (struct) elf_section_header_s
7 pmbaty 619
{
11 pmbaty 620
   PACKED (union)
7 pmbaty 621
   {
11 pmbaty 622
      PACKED (struct)
7 pmbaty 623
      {
11 pmbaty 624
         uint32_t name_offset; // offset 0: offset in the string table of the name of this section
625
         uint32_t type;        // offset 4: section type (0: unused, 1: program data, 2: symbols table, 3: strings table, 4: relocs with addends, 5: symbols hash table, 6: dyld info, 7: notes, 8: BSS, 9: relocs without addends, 11: dyld symbols table, 14: constructors, 15: destructors, 16, preconstructors, 17: group, 18: extended section indices, 19: number of typedefs ...)
626
      } elf;
627
      PACKED (struct) // size == 40
628
      {
7 pmbaty 629
         uint32_t name_offset;  // offset 0: offset in the string table of the name of this section
11 pmbaty 630
         uint32_t type;         // offset 4: section type (0: unused, 1: program data, 2: symbols table, 3: strings table, 4: relocs with addends, 5: symbols hash table, 6: dyld info, 7: notes, 8: BSS, 9: relocs without addends, 11: dyld symbols table, 14: constructors, 15: destructors, 16, preconstructors, 17: group, 18: extended section indices, 19: number of typedefs ...)
631
         uint32_t flags;        // offset 8: bitmapped flags (1: writable, 2: takes RAM, 4: executable, 8: reserved, 16: mergeable, 32: contains C-strings, 64: sh_info contains SHT index, 128: preserve order, 256: OS-specific, 512: group member, 1024: TLS template ...)
7 pmbaty 632
         uint32_t virtual_addr; // offset 12: address in virtual memory where this section may be loaded
633
         uint32_t file_offset;  // offset 16: offset of this section in the ELF file
634
         uint32_t size;         // offset 20: size of this section
635
         uint32_t linked_index; // offset 24: optional section index of an associated section
636
         uint32_t info;         // offset 28: optional extra information
637
         uint32_t alignment;    // offset 32: required memory alignment (must be a power of 2)
638
         uint32_t entry_size;   // offset 36: for table-like sections, size of an element in the table
10 pmbaty 639
      } elf32; // size == 40
11 pmbaty 640
      PACKED (struct) // size == 64
7 pmbaty 641
      {
642
         uint32_t name_offset;  // offset 0: offset in the string table of the name of this section
11 pmbaty 643
         uint32_t type;         // offset 4: section type (0: unused, 1: program data, 2: symbols table, 3: strings table, 4: relocs with addends, 5: symbols hash table, 6: dyld info, 7: notes, 8: BSS, 9: relocs without addends, 11: dyld symbols table, 14: constructors, 15: destructors, 16, preconstructors, 17: group, 18: extended section indices, 19: number of typedefs ...)
644
         uint64_t flags;        // offset 8: bitmapped flags (1: writable, 2: takes RAM, 4: executable, 8: reserved, 16: mergeable, 32: contains C-strings, 64: sh_info contains SHT index, 128: preserve order, 256: OS-specific, 512: group member, 1024: TLS template ...)
7 pmbaty 645
         uint64_t virtual_addr; // offset 16: address in virtual memory where this section may be loaded
646
         uint64_t file_offset;  // offset 24: offset of this section in the ELF file
647
         uint64_t size;         // offset 32: size of this section
648
         uint32_t linked_index; // offset 40: optional section index of an associated section
649
         uint32_t info;         // offset 44: optional extra information
650
         uint64_t alignment;    // offset 48: required memory alignment (must be a power of 2)
651
         uint64_t entry_size;   // offset 56: for table-like sections, size of an element in the table
10 pmbaty 652
      } elf64; // size == 64
7 pmbaty 653
   } u;
654
} elf_section_header_t;
11 pmbaty 655
END_OF_PACKED_STRUCT () // restore default alignment
7 pmbaty 656
 
657
 
658
// Executable and Linkable Format dynamic section entry structure type definition
11 pmbaty 659
START_OF_PACKED_STRUCT () // we need byte-alignment for this struct
660
typedef PACKED (struct) elf_dynamic_section_entry_s
7 pmbaty 661
{
11 pmbaty 662
   PACKED (union)
7 pmbaty 663
   {
11 pmbaty 664
      PACKED (struct) // size == 8
7 pmbaty 665
      {
666
         int32_t tag; // dynamic entry type (one of ELF_DT_xxx #defines)
11 pmbaty 667
         uint32_t value; // value (as integer, or as pointed address)
10 pmbaty 668
      } elf32; // size == 8
11 pmbaty 669
      PACKED (struct) // size == 16
7 pmbaty 670
      {
671
         int64_t tag; // dynamic entry type (one of ELF_DT_xxx #defines)
11 pmbaty 672
         uint64_t value; // value (as integer, or as pointed address)
10 pmbaty 673
      } elf64; // size == 16
7 pmbaty 674
   } u;
675
} elf_dynamic_section_entry_t;
11 pmbaty 676
END_OF_PACKED_STRUCT () // restore default alignment
7 pmbaty 677
 
678
 
11 pmbaty 679
// generic buffer structure type definition
680
typedef struct buffer_s
681
{
682
   uint8_t *bytes; // mallocated data
683
   size_t len; // length of allocated data
684
} buffer_t;
1 pmbaty 685
 
686
 
11 pmbaty 687
// IFS directory entry insertion parameters structure type definition
1 pmbaty 688
typedef struct parms_s
689
{
690
   int dperms; // directory permissions (e.g. 0755)
691
   int perms; // file permissions (e.g. 0644)
692
   int uid; // owner user ID (e.g. 0 = root)
693
   int gid; // owner group ID (e.g. 0 = root)
694
   int st_mode; // entry type (e.g. S_IFREG for files) and permissions
7 pmbaty 695
   uint32_t mtime; // entry's modification time POSIX timestamp - set to UINT32_MAX to use the concerned files' mtime on the build host
696
   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 697
   char prefix[MAXPATHLEN]; // install path (e.g. "proc/boot")
7 pmbaty 698
   bool should_follow_symlinks; // follow symlinks
699
   bool should_autosymlink_dylib; // dynamic libraries should be written under their official SONAME and a named symlink be created pointing at them
2 pmbaty 700
   bool is_compiled_bootscript; // entry has [+script] attribute
10 pmbaty 701
   int extra_ino_flags; // bitmap of extra inode flags (IFS_INO_xxx)
1 pmbaty 702
   char search[10 * MAXPATHLEN]; // binary search path (the default one will be constructed at startup)
2 pmbaty 703
 
11 pmbaty 704
   buffer_t data;
1 pmbaty 705
} parms_t;
706
 
707
 
708
// global variables
709
static char line_buffer[4096]; // scrap buffer for the IFS build file parser
710
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
711
static uint32_t image_end = UINT32_MAX; // default image end (no limit)
712
static uint32_t image_maxsize = UINT32_MAX; // default image max size (no limit)
713
static uint32_t image_totalsize = 0; // image total size, measured once all the blocks have been written to the output IFS file
714
static uint32_t image_align = 4; // default image alignment, as per QNX docs
715
static uint32_t image_kernel_ino = 0;
716
static uint32_t image_bootscript_ino = 0;
7 pmbaty 717
#if defined(__x86_64__)
1 pmbaty 718
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 719
#elif defined(__aarch64__)
720
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)
721
#else // unknown platform
722
#error Please port ifstool to this platform
723
#endif
1 pmbaty 724
static char *buildfile_pathname = NULL; // pathname of IFS build file
7 pmbaty 725
static char *current_line = NULL; // copy of current line in IFS build file
1 pmbaty 726
static int lineno = 0; // current line number in IFS build file
727
static char *QNX_TARGET = NULL; // value of the $QNX_TARGET environment variable
7 pmbaty 728
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 729
 
730
 
731
// prototypes of local functions
732
static void sha512_private_transform (SHA512_CTX *context, const uint64_t *data); // used internally in SHA512_Update() and SHA512_Final()
733
static void SHA512_Init (SHA512_CTX *context);
734
static void SHA512_Update (SHA512_CTX *context, void *data, size_t len);
735
static void SHA512_Final (uint8_t digest[SHA512_DIGEST_LENGTH], SHA512_CTX *context);
736
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 737
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 738
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 739
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)
740
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
741
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
11 pmbaty 742
static char *read_filecontents (const char *pathname, const char *search_path, buffer_t *outbuf); // 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 743
static int fwrite_filecontents (const char *pathname, FILE *fp); // dumps the contents of pathname into fp
11 pmbaty 744
static int relative_offset_of_in (const char *name, const buffer_t *stringbuf); // returns the relative offset of a particular string in a string table
10 pmbaty 745
static elf_section_header_t *elf_get_section_header_by_name (const elf_header_t *elf, const char *section_name); // get a pointer to a named section header in an ELF file
1 pmbaty 746
static size_t fwrite_fsentry (const fsentry_t *fsentry, FILE *fp); // writes the given filesystem entry into fp (without its contents)
7 pmbaty 747
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 748
static int fsentry_compare_pathnames_cb (const void *a, const void *b); // qsort() comparison callback that sorts filesystem entries by pathnames
7 pmbaty 749
static void update_MKIFS_PATH (const char *processor);
10 pmbaty 750
static int dump_ifs_info (const char *ifs_pathname, bool want_everything); // dumps detailed info about a particular IFS file on the standard output, returns 0 on success and >0 on error
14 pmbaty 751
static int create_intermediate_dirs (const char *file_pathname); // creates all intermediate directories to file_pathname so that fopen(file_pathname, "w") doesn't fail
752
static int dump_ifs_contents (const char *ifs_pathname, const char *outdir); // dumps the IFS filesystem contents in outdir, returns 0 on success and >0 on error
1 pmbaty 753
 
754
 
755
static void sha512_private_transform (SHA512_CTX *context, const uint64_t *data)
756
{
757
   // logical functions used in SHA-384 and SHA-512
758
   #define S64(b,x)      (((x) >> (b)) | ((x) << (64 - (b)))) // 64-bit rotate right
759
   #define Ch(x,y,z)     (((x) & (y)) ^ ((~(x)) & (z)))
760
   #define Maj(x,y,z)    (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
761
   #define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
762
   #define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
763
   #define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ ((x) >> 7))
764
   #define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ ((x) >> 6))
765
 
766
   // hash constant words K for SHA-384 and SHA-512
767
   static const uint64_t K512[80] = {
768
      0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
769
      0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
770
      0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
771
      0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
772
      0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
773
      0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
774
      0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
775
      0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
776
      0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
777
      0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
778
   };
779
 
780
   uint64_t     a, b, c, d, e, f, g, h, s0, s1;
781
   uint64_t     T1, T2, *W512 = (uint64_t *) context->buffer;
782
   int j;
783
 
784
   // initialize registers with the prev. intermediate value
785
   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];
786
 
787
   for (j = 0; j < 16; j++)
788
   {
789
#if __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
790
      W512[j] = __builtin_bswap64 (*data); // convert to host byte order
791
#elif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
792
      W512[j] = *data;
793
#else // __BYTE_ORDER__ == ???
794
#error Please port this SHA-512 code to your exotic endianness platform. What are you compiling this on? PDP? Honeywell?
795
#endif // __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
796
 
797
      // apply the SHA-512 compression function to update a..h
798
      T1 = h + Sigma1_512 (e) + Ch (e, f, g) + K512[j] + W512[j];
799
      T2 = Sigma0_512 (a) + Maj (a, b, c);
800
 
801
      // update registers
802
      h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2;
803
 
804
      data++;
805
   }
806
 
807
   for (; j < 80; j++)
808
   {
809
      // part of the message block expansion
810
      s0 = W512[(j + 1) & 0x0f];
811
      s0 = sigma0_512 (s0);
812
      s1 = W512[(j + 14) & 0x0f];
813
      s1 = sigma1_512 (s1);
814
 
815
      // apply the SHA-512 compression function to update a..h
816
      T1 = h + Sigma1_512 (e) + Ch (e, f, g) + K512[j] + (W512[j & 0x0f] += s1 + W512[(j + 9) & 0x0f] + s0);
817
      T2 = Sigma0_512 (a) + Maj (a, b, c);
818
 
819
      // update registers
820
      h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2;
821
   }
822
 
823
   // compute the current intermediate hash value
824
   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;
825
 
826
   // clean up
827
   a = b = c = d = e = f = g = h = T1 = T2 = 0;
828
   #undef sigma1_512
829
   #undef sigma0_512
830
   #undef Sigma1_512
831
   #undef Sigma0_512
832
   #undef Maj
833
   #undef Ch
834
   #undef S64
835
   return;
836
}
837
 
838
 
839
static void SHA512_Init (SHA512_CTX *context)
840
{
841
   // initial hash value H for SHA-512
842
   static const uint64_t sha512_initial_hash_value[8] = {
843
      0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
844
   };
845
 
846
   memcpy (context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH);
847
   memset (context->buffer, 0, SHA512_BLOCK_LENGTH);
848
   context->bitcount[0] = context->bitcount[1] = 0;
849
}
850
 
851
 
852
void SHA512_Update (SHA512_CTX *context, void *datain, size_t len)
853
{
854
   #define ADDINC128(w,n) do { \
855
           (w)[0] += (uint64_t) (n); \
856
           if ((w)[0] < (n)) \
857
                   (w)[1]++; \
858
   } 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
859
 
860
   size_t freespace, usedspace;
861
   const uint8_t *data = (const uint8_t *) datain;
862
 
863
   if (len == 0)
864
      return; // calling with empty data is valid - we do nothing
865
 
866
   usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
867
   if (usedspace > 0)
868
   {
869
      // calculate how much free space is available in the buffer
870
      freespace = SHA512_BLOCK_LENGTH - usedspace;
871
 
872
      if (len >= freespace)
873
      {
874
         // fill the buffer completely and process it
875
         memcpy (&context->buffer[usedspace], data, freespace);
876
         ADDINC128 (context->bitcount, freespace << 3);
877
         len -= freespace;
878
         data += freespace;
879
         sha512_private_transform (context, (uint64_t *) context->buffer);
880
      }
881
      else
882
      {
883
         // the buffer is not full yet
884
         memcpy (&context->buffer[usedspace], data, len);
885
         ADDINC128 (context->bitcount, len << 3);
886
 
887
         // clean up
888
         usedspace = freespace = 0;
889
         return;
890
      }
891
   }
892
 
893
   while (len >= SHA512_BLOCK_LENGTH)
894
   {
895
      // process as many complete blocks as we can
896
      sha512_private_transform (context, (uint64_t *) data);
897
      ADDINC128 (context->bitcount, SHA512_BLOCK_LENGTH << 3);
898
      len -= SHA512_BLOCK_LENGTH;
899
      data += SHA512_BLOCK_LENGTH;
900
   }
901
 
902
   if (len > 0)
903
   {
904
      // save leftovers
905
      memcpy (context->buffer, data, len);
906
      ADDINC128 (context->bitcount, len << 3);
907
   }
908
 
909
   // clean up
910
   usedspace = freespace = 0;
911
   #undef ADDINC128
912
   return;
913
}
914
 
915
 
916
static void SHA512_Final (uint8_t digest[SHA512_DIGEST_LENGTH], SHA512_CTX *context)
917
{
918
   #define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16)
919
 
920
   size_t usedspace;
921
   union { uint8_t *as_bytes; uint64_t *as_uint64s; } cast_var = { NULL };
922
 
923
   // if no digest buffer is passed, don't bother finalizing the computation
924
   if (digest != NULL)
925
   {
926
      usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
927
 
928
#if __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
929
      context->bitcount[0] = __builtin_bswap64 (context->bitcount[0]); // convert from host byte order
930
      context->bitcount[1] = __builtin_bswap64 (context->bitcount[1]); // convert from host byte order
931
#endif // __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
932
 
933
      if (usedspace > 0)
934
      {
935
         // begin padding with a 1 bit
936
         context->buffer[usedspace++] = 0x80;
937
 
938
         if (usedspace <= SHA512_SHORT_BLOCK_LENGTH)
939
            memset (&context->buffer[usedspace], 0, SHA512_SHORT_BLOCK_LENGTH - usedspace); // set-up for the last transform
940
         else
941
         {
942
            if (usedspace < SHA512_BLOCK_LENGTH)
943
               memset (&context->buffer[usedspace], 0, SHA512_BLOCK_LENGTH - usedspace);
944
 
945
            sha512_private_transform (context, (uint64_t *) context->buffer); // do second-to-last transform
946
            memset (context->buffer, 0, SHA512_BLOCK_LENGTH - 2); // and set-up for the last transform
947
         }
948
      }
949
      else // usedspace == 0
950
      {
951
         memset (context->buffer, 0, SHA512_SHORT_BLOCK_LENGTH); // prepare for final transform
952
         *context->buffer = 0x80; // begin padding with a 1 bit
953
      }
954
 
955
      // store the length of input data (in bits)
956
      cast_var.as_bytes = context->buffer;
957
      cast_var.as_uint64s[SHA512_SHORT_BLOCK_LENGTH / 8 + 0] = context->bitcount[1];
958
      cast_var.as_uint64s[SHA512_SHORT_BLOCK_LENGTH / 8 + 1] = context->bitcount[0];
959
 
960
      // final transform
961
      sha512_private_transform (context, (uint64_t *) context->buffer);
962
 
963
      // save the hash data for output
964
#if __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
965
      for (int j = 0; j < 8; j++)
966
         context->state[j] = __builtin_bswap64 (context->state[j]); // convert to host byte order
967
#endif // __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
968
      memcpy (digest, context->state, SHA512_DIGEST_LENGTH);
969
   }
970
 
971
   // zero out state data
972
   memset (context, 0, sizeof (SHA512_CTX));
973
   #undef SHA512_SHORT_BLOCK_LENGTH
974
   return;
975
}
976
 
977
 
2 pmbaty 978
static uint8_t *SHA512 (void *data, size_t data_len, uint8_t *digest_or_NULL)
1 pmbaty 979
{
2 pmbaty 980
   // 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
981
   // returns the STRING REPRESENTATION of digest in a statically-allocated string
1 pmbaty 982
 
8 pmbaty 983
   static thread_local uint8_t static_digest[SHA512_DIGEST_LENGTH] = "";
984
   static thread_local char digest_as_string[2 * SHA512_DIGEST_LENGTH + 1] = "";
2 pmbaty 985
 
1 pmbaty 986
   SHA512_CTX ctx;
2 pmbaty 987
   size_t byte_index;
988
 
1 pmbaty 989
   SHA512_Init (&ctx);
990
   SHA512_Update (&ctx, data, data_len);
2 pmbaty 991
   if (digest_or_NULL == NULL)
992
      digest_or_NULL = static_digest;
993
   SHA512_Final (digest_or_NULL, &ctx);
994
 
995
   for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
996
      sprintf (&digest_as_string[2 * byte_index], "%02x", digest_or_NULL[byte_index]);
997
   return (digest_as_string);
1 pmbaty 998
}
999
 
1000
 
7 pmbaty 1001
static int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness)
1 pmbaty 1002
{
6 pmbaty 1003
   // 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 1004
 
5 pmbaty 1005
   uint8_t accumulator[4] = { 0, 0, 0, 0 };
7 pmbaty 1006
   const char *current_char_ptr;
1007
   int32_t image_cksum;
5 pmbaty 1008
   size_t i;
1009
 
7 pmbaty 1010
   image_cksum = 0;
1011
   current_char_ptr = data;
5 pmbaty 1012
   for (i = 0; i < data_len; i++)
1013
   {
1014
      accumulator[i % 4] = *current_char_ptr;
1015
      if (i % 4 == 3)
1016
         if (is_foreign_endianness)
8 pmbaty 1017
            image_cksum += (accumulator[3] << 0) + (accumulator[2] << 8) + (accumulator[1] << 16) + (accumulator[0] << 24);
5 pmbaty 1018
         else
8 pmbaty 1019
            image_cksum += (accumulator[0] << 0) + (accumulator[1] << 8) + (accumulator[2] << 16) + (accumulator[3] << 24);
5 pmbaty 1020
      current_char_ptr++;
1021
   }
1022
 
1023
   return (is_foreign_endianness ? __builtin_bswap32 (-image_cksum) : -image_cksum);
1024
}
1025
 
1026
 
1 pmbaty 1027
static long long read_integer (const char *str)
1028
{
1029
   // 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)
1030
 
1031
   char *endptr = NULL;
1032
   long long ret = strtoll (str, &endptr, 0); // use strtoll() to handle hexadecimal (0x...), octal (0...) and decimal (...) bases
1033
   if (endptr != NULL)
1034
   {
1035
      if ((*endptr == 'k') || (*endptr == 'K')) ret *= (size_t) 1024;
1036
      else if ((*endptr == 'm') || (*endptr == 'M')) ret *= (size_t) 1024 * 1024;
1037
      else if ((*endptr == 'g') || (*endptr == 'G')) ret *= (size_t) 1024 * 1024 * 1024;
1038
      else if ((*endptr == 't') || (*endptr == 'T')) ret *= (size_t) 1024 * 1024 * 1024 * 1024; // future-proof enough, I suppose?
1039
   }
1040
   return (ret);
1041
}
1042
 
1043
 
2 pmbaty 1044
static void hex_fprintf (FILE *fp, const uint8_t *data, size_t data_size, int howmany_columns, const char *fmt, ...)
1045
{
1046
   // this function logs hexadecimal data to an opened file pointer (or to stdout/stderr)
1047
 
1048
   va_list argptr;
1049
   size_t index;
1050
   int i;
1051
 
1052
   // concatenate all the arguments in one string and write it to the file
1053
   va_start (argptr, fmt);
1054
   vfprintf (fp, fmt, argptr);
1055
   va_end (argptr);
1056
 
1057
   // for each row of howmany_columns bytes of data...
1058
   for (index = 0; index < data_size; index += howmany_columns)
1059
   {
1060
      fprintf (fp, "    %05zu  ", index); // print array address of row
1061
      for (i = 0; i < howmany_columns; i++)
1062
         if (index + i < data_size)
1063
            fprintf (fp, " %02X", data[index + i]); // if row contains data, print data as hex bytes
1064
         else
1065
            fprintf (fp, "   "); // else fill the space with blanks
1066
      fprintf (fp, "   ");
1067
      for (i = 0; i < howmany_columns; i++)
1068
         if (index + i < data_size)
1069
            fputc ((data[index + i] >= 32) && (data[index + i] < 127) ? data[index + i] : '.', fp); // now if row contains data, print data as ASCII
1070
         else
1071
            fputc (' ', fp); // else fill the space with blanks
1072
      fputc ('\n', fp);
1073
   }
1074
 
1075
   return; // and return
1076
}
1077
 
1078
 
1079
static char *binary (const uint8_t x, char char_for_zero, char char_for_one)
1080
{
1081
   // returns the binary representation of x as a string
1082
 
8 pmbaty 1083
   static thread_local char outstr[9] = "00000000";
2 pmbaty 1084
   for (int i = 0; i < 8; i++)
1085
      outstr[i] = (x & (0x80 >> i) ? char_for_one : char_for_zero);
1086
   return (outstr);
1087
}
1088
 
1089
 
1090
static char *describe_uint8 (const uint8_t x, const char *bitwise_stringdescs[8])
1091
{
1092
   // returns the ORed description of byte 'x' according to the description strings for each bit
1093
 
8 pmbaty 1094
   static thread_local char *default_bitstrings[8] = { "bit0", "bit1", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7" };
1095
   static thread_local char outstr[8 * 64] = "";
2 pmbaty 1096
 
1097
   outstr[0] = 0;
1098
   for (int i = 0; i < 8; i++)
1099
      if (x & (1 << i))
1100
      {
1101
         if (outstr[0] != 0)
1102
            strcat (outstr, "|");
1103
         strcat (outstr, ((bitwise_stringdescs != NULL) && (*bitwise_stringdescs[i] != 0) ? bitwise_stringdescs[i] : default_bitstrings[i]));
1104
      }
1105
   return (outstr);
1106
}
1107
 
1108
 
11 pmbaty 1109
static char *read_filecontents (const char *pathname, const char *search_path, buffer_t *outbuf)
7 pmbaty 1110
{
1111
   // locates pathname among MKIFS_PATH, and places its contents in a buffer (caller frees). Returns resolved pathname (static buffer) or NULL.
1112
 
8 pmbaty 1113
   static thread_local char final_pathname[MAXPATHLEN];
7 pmbaty 1114
 
1115
   const char *nextsep;
1116
   const char *token;
1117
   FILE *fp;
1118
 
1119
   // is it an absolute pathname (POSIX and Windows variants) ?
1120
   if (IS_DIRSEP (pathname[0]) || (isalpha (pathname[0]) && (pathname[1] == ':') && IS_DIRSEP (pathname[2])))
1121
      strcpy (final_pathname, pathname); // in this case, it MUST exist at its designated location (either absolute or relative to the current working directory)
1122
   else // the path is relative, search it among the search paths we have
1123
   {
1124
      // construct a potential final path using each element of the search path
8 pmbaty 1125
      token = (*search_path != 0 ? search_path : NULL);
1126
      nextsep = (token != NULL ? &token[strcspn (token, PATH_SEP_STR)] : NULL);
7 pmbaty 1127
      while (token != NULL)
1128
      {
1129
         sprintf (final_pathname, "%.*s/%s", (int) (nextsep - token), token, pathname);
1130
         if (access (final_pathname, 0) == 0)
1131
            break; // if a file can indeed be found at this location, stop searching
1132
 
1133
         token = (*nextsep != 0 ? nextsep + 1 : NULL);
1134
         nextsep = (token != NULL ? &token[strcspn (token, PATH_SEP_STR)] : NULL);
1135
      }
1136
 
1137
      // have we exhausted all possibilities ?
1138
      if (token == NULL)
1139
      {
1140
         errno = ENOENT;
1141
         return (NULL); // file not found, return with ENOENT
1142
      }
1143
   }
1144
 
1145
   // now open and read the file
1146
   fp = fopen (final_pathname, "rb");
1147
   if (fp == NULL)
1148
      return (NULL); // unexistent file (errno is set to ENOENT)
1149
 
1150
   fseek (fp, 0, SEEK_END);
11 pmbaty 1151
   outbuf->len = ftell (fp); // measure file length
7 pmbaty 1152
   fseek (fp, 0, SEEK_SET);
11 pmbaty 1153
   outbuf->bytes = malloc (outbuf->len);
1154
   if (outbuf->bytes == NULL)
7 pmbaty 1155
   {
1156
      fclose (fp);
11 pmbaty 1157
      outbuf->len = 0;
7 pmbaty 1158
      return (NULL); // out of memory (errno is set to ENOMEM)
1159
   }
11 pmbaty 1160
   if (fread (outbuf->bytes, 1, outbuf->len, fp) != outbuf->len) // read the file in whole
7 pmbaty 1161
   {
1162
      fclose (fp);
11 pmbaty 1163
      outbuf->len = 0;
7 pmbaty 1164
      return (NULL); // short read (errno is set)
1165
   }
1166
   fclose (fp); // close the file
1167
 
1168
   return (final_pathname); // file was read successfully and its content put in databuf with size datalen
1169
}
1170
 
1171
 
1 pmbaty 1172
static int fwrite_filecontents (const char *pathname, FILE *fp)
1173
{
1174
   // dumps the binary contents of pathname to fp
1175
 
1176
   uint8_t *blob_buffer;
1177
   size_t blob_size;
1178
   FILE *blob_fp;
1179
   int ret;
1180
 
1181
   blob_fp = fopen (pathname, "rb");
1182
   if (blob_fp == NULL)
1183
      return (-1); // errno is set
1184
 
1185
   fseek (blob_fp, 0, SEEK_END);
1186
   blob_size = ftell (blob_fp);
1187
   blob_buffer = malloc (blob_size);
1188
   if (blob_buffer == NULL)
1189
   {
1190
      fclose (blob_fp);
1191
      return (-1); // errno is set to ENOMEM
1192
   }
1193
   fseek (blob_fp, 0, SEEK_SET);
1194
   fread (blob_buffer, 1, blob_size, blob_fp);
1195
   fclose (blob_fp);
1196
 
1197
   ret = (int) fwrite (blob_buffer, 1, blob_size, fp);
8 pmbaty 1198
   fflush (fp); // force flush to disk, because the C stream API is *buffered*
1 pmbaty 1199
   free (blob_buffer);
1200
   return (ret);
1201
}
1202
 
1203
 
11 pmbaty 1204
static int relative_offset_of_in (const char *name, const buffer_t *stringbuf)
10 pmbaty 1205
{
1206
   int name_len = (int) strlen (name) + 1;
11 pmbaty 1207
   WELLMANNERED_ASSERT (name_len < stringbuf->len, "bad call (name longer than string table)");
1208
   for (int idx = 0; idx <= stringbuf->len - name_len; idx++)
1209
      if (memcmp (&stringbuf->bytes[idx], name, name_len) == 0)
10 pmbaty 1210
         return (idx);
1211
   WELLMANNERED_ASSERT (false, "bad call (name '%s' not found in string table)", name);
1212
   return (0);
1213
}
1214
 
1215
 
1216
static elf_section_header_t *elf_get_section_header_by_name (const elf_header_t *elf, const char *section_name)
1217
{
1218
   elf_section_header_t *shdr_shstrtab; // section header of the section header strings table
1219
   elf_section_header_t *shdr;
1220
   size_t table_count;
1221
   size_t table_index;
1222
   char *shstrtab; // section header strings table
1223
   char *name;
1224
 
1225
   shdr_shstrtab = (elf_section_header_t *) ((uint8_t *) elf + ELF_GET_NUMERIC (elf, elf, section_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, section_header_item_size) * ELF_GET_NUMERIC (elf, elf, section_header_names_idx)); // quick access to section header for the section that contains the section names
1226
   shstrtab = ((uint8_t *) elf + ELF_GET_NUMERIC (elf, shdr_shstrtab, file_offset)); // locate the start of the strings table that contains the section names
1227
 
1228
   // cycle through the sections table
1229
   table_count = ELF_GET_NUMERIC (elf, elf, section_header_table_len);
1230
   for (table_index = 0; table_index < table_count; table_index++)
1231
   {
1232
      shdr = (elf_section_header_t *) ((uint8_t *) elf + ELF_GET_NUMERIC (elf, elf, section_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, section_header_item_size) * table_index); // quick access to section header
1233
      name = &shstrtab[ELF_GET_NUMERIC (elf, shdr, name_offset)]; // peek at its name
1234
      if (strcmp (name, section_name) == 0)
1235
         return (shdr); // if found, return a pointer to this section header
1236
   }
1237
 
1238
   return (NULL); // section not found
1239
}
1240
 
1241
 
1 pmbaty 1242
static size_t fwrite_fsentry (const fsentry_t *fsentry, FILE *fp)
1243
{
7 pmbaty 1244
   // writes a directory entry in the image filesystem file pointed to by fp (or fakes so if fp is NULL)
1245
   // and return the number of bytes written (or that would have been written)
1246
 
1 pmbaty 1247
   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";
1248
 
1249
   size_t datalen;
1250
   size_t count;
1251
 
1252
   count = 0;
1253
   if (fp != NULL)
8 pmbaty 1254
      fwrite_or_die (&fsentry->header, 1, sizeof (fsentry->header), fp); // write the entry header (PACKED STRUCT)
1 pmbaty 1255
   count += sizeof (fsentry->header);
1256
   if (S_ISREG (fsentry->header.mode))
1257
   {
1258
      if (fp != NULL)
1259
      {
8 pmbaty 1260
         fwrite_or_die (&fsentry->u.file.offset, 1, sizeof (uint32_t), fp); // write offset
1261
         fwrite_or_die (&fsentry->u.file.size,   1, sizeof (uint32_t), fp); // write size
1 pmbaty 1262
      }
1263
      count += 2 * sizeof (uint32_t);
1264
      datalen = strlen (fsentry->u.file.path) + 1;
1265
      if (fp != NULL)
8 pmbaty 1266
         fwrite_or_die (fsentry->u.file.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash)
1 pmbaty 1267
      count += datalen;
1268
   }
1269
   else if (S_ISDIR (fsentry->header.mode))
1270
   {
1271
      datalen = strlen (fsentry->u.dir.path) + 1;
1272
      if (fp != NULL)
8 pmbaty 1273
         fwrite_or_die (fsentry->u.dir.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash)
1 pmbaty 1274
      count += datalen;
1275
   }
1276
   else if (S_ISLNK (fsentry->header.mode))
1277
   {
1278
      if (fp != NULL)
1279
      {
8 pmbaty 1280
         fwrite_or_die (&fsentry->u.symlink.sym_offset, 1, sizeof (uint16_t), fp); // write offset
1281
         fwrite_or_die (&fsentry->u.symlink.sym_size,   1, sizeof (uint16_t), fp); // write size
1 pmbaty 1282
      }
1283
      count += 2 * sizeof (uint16_t);
1284
      datalen = strlen (fsentry->u.symlink.path) + 1;
1285
      if (fp != NULL)
8 pmbaty 1286
         fwrite_or_die (fsentry->u.symlink.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash)
1 pmbaty 1287
      count += datalen;
1288
      datalen = strlen (fsentry->u.symlink.contents) + 1;
1289
      if (fp != NULL)
8 pmbaty 1290
         fwrite_or_die (fsentry->u.symlink.contents, 1, (size_t) datalen, fp); // write null-terminated symlink contents
1 pmbaty 1291
      count += datalen;
1292
   }
1293
   else
1294
   {
1295
      if (fp != NULL)
1296
      {
8 pmbaty 1297
         fwrite_or_die (&fsentry->u.device.dev,  1, sizeof (uint32_t), fp); // write dev number
1298
         fwrite_or_die (&fsentry->u.device.rdev, 1, sizeof (uint32_t), fp); // write rdev number
1 pmbaty 1299
      }
1300
      count += 2 * sizeof (uint32_t);
1301
      datalen = strlen (fsentry->u.device.path) + 1;
1302
      if (fp != NULL)
8 pmbaty 1303
         fwrite_or_die (fsentry->u.device.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash)
1 pmbaty 1304
      count += datalen;
1305
   }
1306
 
7 pmbaty 1307
   if (count < fsentry->header.size)
1308
   {
1309
      if (fp != NULL)
8 pmbaty 1310
         fwrite_or_die (zeropad_buffer, 1, fsentry->header.size - count, fp); // pad as necessary
7 pmbaty 1311
      count += fsentry->header.size - count;
1312
   }
1313
   else if (count > fsentry->header.size)
1314
   {
1315
      fprintf (stderr, "ERROR: attempt to write invalid dirent (claimed size %zd, written size %zd). Aborting.\n", (size_t) fsentry->header.size, count);
1316
      exit (1);
1317
   }
1 pmbaty 1318
 
1319
   return (count);
1320
}
1321
 
1322
 
7 pmbaty 1323
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 1324
{
11 pmbaty 1325
   #define ADD_NAME_TO_STRINGTABLE(name,strtab) do { \
10 pmbaty 1326
      name_len = strlen ((name)) + 1; \
11 pmbaty 1327
      reallocated_ptr = realloc ((strtab).bytes, (strtab).len + name_len); \
10 pmbaty 1328
      WELLMANNERED_ASSERT (reallocated_ptr, "out of memory"); \
11 pmbaty 1329
      (strtab).bytes = reallocated_ptr; \
1330
      memcpy (&(strtab).bytes[(strtab).len], (name), name_len); \
1331
      (strtab).len += name_len; \
10 pmbaty 1332
   } while (0)
11 pmbaty 1333
   #define APPEND_SECTION_DATA(section,sectionhdr_offset) do { \
1334
      memcpy (&entry_parms->data.bytes[entry_parms->data.len], (section).bytes, (section).len); /* write section in place */ \
1335
      free ((section).bytes); /* free it */ \
1336
      new_shdr = (elf_section_header_t *) &new_shtab.bytes[(sectionhdr_offset)]; /* now fix this section header */ \
1337
      ELF_SET_NUMERIC (elf, new_shdr, file_offset, entry_parms->data.len); /* fix section offset in the new section headers table */ \
1338
      entry_parms->data.len += (section).len; /* update new ELF file length */ \
1339
   } while (0)
10 pmbaty 1340
 
8 pmbaty 1341
   static thread_local char candidate_pathname[1024];
1 pmbaty 1342
   static int inode_count = 0; // will be preincremented each time this function is called
1343
 
7 pmbaty 1344
   const char *original_stored_pathname = NULL;
1345
   const elf_dynamic_section_entry_t *dynamic_entry; // dynamic section entry
1346
   const elf_section_header_t *shdr_dynstr; // dynamic strings
1347
   const elf_section_header_t *shdr_dynamic; // dynamic section
1348
   const elf_section_header_t *shdr;
10 pmbaty 1349
   elf_section_header_t *new_shdr;
1350
   size_t new_qnxinfo_shdr_offset;
1351
   size_t new_debuglink_shdr_offset;
1352
   size_t new_qnxusage_shdr_offset;
1353
   size_t new_buildid_shdr_offset;
1354
   size_t new_shstrtab_shdr_offset;
1355
   elf_program_header_t *phdr;
7 pmbaty 1356
   const char *canonical_dylib_name;
1357
   const char *dynamic_strings; // strings table of the ".dynamic" section
1358
   const char *last_dirsep;
10 pmbaty 1359
   elf_header_t *elf;
11 pmbaty 1360
   buffer_t new_shtab = { NULL, 0 };
1361
   buffer_t elfsection_qnxinfo   = { NULL, 0 };
1362
   buffer_t elfsection_qnxusage  = { NULL, 0 };
1363
   buffer_t elfsection_debuglink = { NULL, 0 };
1364
   buffer_t elfsection_buildid   = { NULL, 0 };
1365
   buffer_t elfsection_shstrtab  = { NULL, 0 };
7 pmbaty 1366
   char *resolved_pathname;
1 pmbaty 1367
   void *reallocated_ptr;
7 pmbaty 1368
   void *old_data;
1 pmbaty 1369
   struct stat stat_buf;
10 pmbaty 1370
   size_t new_shdrtable_offset;
1371
   size_t end_padding_offset;
7 pmbaty 1372
   size_t table_index;
1373
   size_t table_count;
10 pmbaty 1374
   size_t name_len;
1 pmbaty 1375
   fsentry_t *fsentry;
1376
 
1377
   if (S_ISDIR (entry_parms->st_mode)) // are we storing a directory ?
1378
   {
1379
      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);
1380
   }
1381
   else if (S_ISREG (entry_parms->st_mode)) // else are we storing a regular file ?
1382
   {
7 pmbaty 1383
      if (strcmp (stored_pathname, "/proc/boot/boot") == 0) // is it the kernel ?
1 pmbaty 1384
      {
7 pmbaty 1385
         // HACK: for now just consider the kernel as a binary blob
1386
         // FIXME: reimplement properly
1387
         sprintf (candidate_pathname, "%s/procnto-smp-instr", entry_parms->prefix); // fix the entry name
1388
         stored_pathname = candidate_pathname;
10 pmbaty 1389
         entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE; // procnto needs to have these flags stamped on the inode
8 pmbaty 1390
         entry_parms->st_mode = S_IFREG | 0700; // procnto requires 0700 permissions
10 pmbaty 1391
         image_kernel_ino = entry_parms->extra_ino_flags | (inode_count + 1);
7 pmbaty 1392
      }
1393
      else if (entry_parms->is_compiled_bootscript) // else is it a startup script ?
1394
         image_bootscript_ino = inode_count + 1; // save boot script inode number for image header
2 pmbaty 1395
 
7 pmbaty 1396
      // do we already know the data for this data blob ?
11 pmbaty 1397
      if (entry_parms->data.bytes != NULL)
7 pmbaty 1398
      {
1399
         entry_parms->mtime = entry_parms->mtime_for_inline_files;
11 pmbaty 1400
         fprintf (stderr, "file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" blob (len %zd)\n", entry_parms->extra_ino_flags | (inode_count + 1), entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_parms->data.len);
7 pmbaty 1401
      }
8 pmbaty 1402
      else if (buildhost_pathname != NULL) // else was a source file pathname supplied ?
1 pmbaty 1403
      {
11 pmbaty 1404
         resolved_pathname = read_filecontents (buildhost_pathname, (entry_parms->search[0] != 0 ? entry_parms->search : MKIFS_PATH), &entry_parms->data); // locate the file
7 pmbaty 1405
         if (resolved_pathname == NULL)
1 pmbaty 1406
         {
7 pmbaty 1407
            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));
1408
            exit (1);
1 pmbaty 1409
         }
7 pmbaty 1410
         stat (resolved_pathname, &stat_buf); // can't fail
1411
         if (entry_parms->mtime == UINT32_MAX)
1412
            entry_parms->mtime = (uint32_t) stat_buf.st_mtime;
11 pmbaty 1413
         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->data.len);
8 pmbaty 1414
      }
1 pmbaty 1415
 
9 pmbaty 1416
      // is the file we're storing an ELF file ?
11 pmbaty 1417
      if ((entry_parms->data.len > 52) // file is big enough to contain an ELF header
1418
          && ((elf = (elf_header_t *) entry_parms->data.bytes) != NULL) // cast (necessary true)
10 pmbaty 1419
          && (memcmp (ELF_GET_STRING (elf, elf, magic), ELF_MAGIC_STR, 4) == 0)) // file starts with the ELF magic
7 pmbaty 1420
      {
9 pmbaty 1421
         // is the file we're storing a relocatable executable (i.e. a dynamic library) and should we check for its canonical name ?
10 pmbaty 1422
         if ((ELF_GET_NUMERIC (elf, elf, type) == 3) && entry_parms->should_autosymlink_dylib)
7 pmbaty 1423
         {
9 pmbaty 1424
            // we need to find the SONAME of this library
1425
            canonical_dylib_name = NULL;
1 pmbaty 1426
 
9 pmbaty 1427
            // locate the sections we need (the dynamic section and its strings table)
10 pmbaty 1428
            shdr_dynamic = elf_get_section_header_by_name (elf, ".dynamic");
1429
            shdr_dynstr = elf_get_section_header_by_name (elf, ".dynstr");
7 pmbaty 1430
 
9 pmbaty 1431
            // make sure we have both the dynamic section header and its own strings table header
1432
            if ((shdr_dynamic != NULL) && (shdr_dynstr != NULL))
1433
            {
11 pmbaty 1434
               dynamic_strings = (char *) &entry_parms->data.bytes[ELF_GET_NUMERIC (elf, shdr_dynstr, file_offset)]; // quick access to dynamic sections strings table
7 pmbaty 1435
 
9 pmbaty 1436
               // walk through the dynamic section, look for the DT_SONAME entry
11 pmbaty 1437
               for (dynamic_entry = (elf_dynamic_section_entry_t *) &entry_parms->data.bytes[ELF_GET_NUMERIC (elf, shdr_dynamic, file_offset)];
10 pmbaty 1438
                    (ELF_GET_NUMERIC (elf, dynamic_entry, tag) != ELF_DT_NULL);
9 pmbaty 1439
                    dynamic_entry = (elf_dynamic_section_entry_t *) ((uint8_t *) dynamic_entry + ELF_STRUCT_SIZE (elf, dynamic_entry)))
10 pmbaty 1440
                  if (ELF_GET_NUMERIC (elf, dynamic_entry, tag) == ELF_DT_SONAME)
9 pmbaty 1441
                  {
11 pmbaty 1442
                     canonical_dylib_name = dynamic_strings + ELF_GET_NUMERIC (elf, dynamic_entry, value);
9 pmbaty 1443
                     break;
1444
                  }
1445
 
1446
               // do we have it ?
1447
               if ((canonical_dylib_name != NULL) && (canonical_dylib_name[0] != 0))
7 pmbaty 1448
               {
9 pmbaty 1449
                  sprintf (candidate_pathname, "%s/%s", entry_parms->prefix, canonical_dylib_name);
1450
                  if (strcmp (candidate_pathname, stored_pathname) != 0) // claimed dylib name differs from passed name ?
1451
                  {
1452
                     original_stored_pathname = stored_pathname; // if so, remember to create a symlink here
1453
                     stored_pathname = candidate_pathname;
1454
                  }
7 pmbaty 1455
               }
1 pmbaty 1456
            }
9 pmbaty 1457
         } // end if the file we're storing is a dylib
10 pmbaty 1458
 
1459
         // now strip this ELF file if necessary
1460
         if (!(entry_parms->extra_ino_flags & IFS_INO_PROCESSED_ELF))
1461
         {
1462
            // NOTE: for each ELF file, mkifs
1463
            // -> alters the program header table and offsets each p_addr (physical address) member by 0x1400000 plus the current file offset (this cannot be done right now, will need to be done once they are known)
1464
            // -> throws away and reconstructs the sections table by keeping only the sections that are in the program header, and writes the section table at the start of the first thrown-away section
11 pmbaty 1465
            // FIXME: what if a thrown away section is located between two program segments ? are they collapsed, moving the segments beyond it one slot down ?
10 pmbaty 1466
 
1467
            // reconstructed ELF:
1468
            // ==== START OF FILE ====
1469
            // ELF header
1470
            // program header table
11 pmbaty 1471
            //  (same sections, just p_addr offset changed)
10 pmbaty 1472
            // section data 5 (named ".note.gnu.build-id")
1473
            //  "............GNU....ZY.....c.o..l"
1474
            // PROGRAM
1475
            // sections table
1476
            // + section 1: ALL ZEROES
1477
            // + section 2: fileoffs 0x21a8 size 0xfd --> "QNX_info" --> QNX binary description: "NAME=pci_debug2.so.3.0\nDESCRIPTION=PCI Server System Debug Module\nDATE=2023/11/19-10:01:13-EST\nSTATE=lookup\nHOST=docker-n1.bts.rim.net\nUSER=builder\nVERSION=QNXOS_main\nTAGID=QNXOS_800-135\nPACKAGE=com.qnx.qnx800.target.pci.debug/3.0.0.00135T202311191043L\n"
1478
            // + section 3: fileoffs 0x22a5 size 0x1c --> ".gnu_debuglink" --> indicates the debug file (and a possible checksum?): "pci_debug2.so.3.0.sym" "\0\0\0" "VX2p"
1479
            // + section 4: fileoffs 0x22c1 size 0x2ad --> "QNX_usage" --> HELP TEXT: "\n-------------------------------------------------------------------------------\n%C\n\nThis module implements debug logging for all PCI server modules. It is\nincluded by setting the environment variable PCI_DEBUG_MODULE and uses\nthe slogger2 APIs.\nNOTE:.On systems which support slogger2, you are encouraged to use this module.instead of pci_debug.so...Release History.---------------..3.0 - This module is functionally equivalent to the previous 2.x version.      however it is incompatible with all pre v3.x PCI components..2.1 - fixes a bug whereby if slogger2 is not running and the PCI_DEBUG_MODULE.      environment variable is set, the client will SIGSEGV..2.0 - initial release.."
1480
            // + section 5: fileoffs 0x190 size 0x32 --> ".note.gnu.build-id" --> GNU build ID
1481
            // + section 6: fileoffs 0x256e size 0x40 --> ".shstrtab" --> sections names strings table
1482
            // section data 2 (named "QNX_info")
1483
            //  (QNX binary description)
1484
            // section data 3 (named ".gnu_debuglink")
1485
            //  (debug file)
1486
            // section data 4 (named "QNX_usage")
1487
            //  (help text)
1488
            // section data 6 (named ".shstrtab")
1489
            //  "\0"
1490
            //  ".shstrtab\0"
1491
            //  "QNX_info\0"
1492
            //  ".gnu_debuglink\0"
1493
            //  "QNX_usage\0"
1494
            //  ".note.gnu.build-id\0"
1495
            // ==== END OF FILE ====
1496
 
11 pmbaty 1497
            // parse the program header table, and measure the farthest offset known by this table where we'll write the reconstructed section headers table
10 pmbaty 1498
 
1499
            new_shdrtable_offset = 0;
1500
            table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len);
1501
            for (table_index = 0; table_index < table_count; table_index++)
1502
            {
11 pmbaty 1503
               phdr = (elf_program_header_t *) &entry_parms->data.bytes[ELF_GET_NUMERIC (elf, elf, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, program_header_item_size) * table_index]; // quick access to program header
10 pmbaty 1504
               if (ELF_GET_NUMERIC (elf, phdr, file_offset) + ELF_GET_NUMERIC (elf, phdr, size_in_file) > new_shdrtable_offset)
1505
                  new_shdrtable_offset = ELF_GET_NUMERIC (elf, phdr, file_offset) + ELF_GET_NUMERIC (elf, phdr, size_in_file);
1506
            }
1507
 
11 pmbaty 1508
            // re-create the section header table
10 pmbaty 1509
 
11 pmbaty 1510
            elfsection_shstrtab.bytes = malloc (1); // initialize an empty section headers strings table
1511
            WELLMANNERED_ASSERT (elfsection_shstrtab.bytes, "out of memory");
1512
            elfsection_shstrtab.bytes[0] = 0;
1513
            elfsection_shstrtab.len = 1;
1514
            ADD_NAME_TO_STRINGTABLE (".shstrtab", elfsection_shstrtab);
10 pmbaty 1515
 
11 pmbaty 1516
            new_shtab.bytes = malloc (ELF_STRUCT_SIZE (elf, shdr)); // prepare a section headers table with just the default entry
1517
            WELLMANNERED_ASSERT (new_shtab.bytes, "out of memory");
1518
            memset (new_shtab.bytes, 0, ELF_STRUCT_SIZE (elf, shdr)); // the first section header is always zerofilled
1519
            new_shtab.len = ELF_STRUCT_SIZE (elf, shdr); // and remember how big the section headers table is now
10 pmbaty 1520
            if ((shdr = elf_get_section_header_by_name (elf, "QNX_info")) != NULL)
1521
            {
1522
               if (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset) // if this section needs to be moved around, have a copy of it
1523
               {
11 pmbaty 1524
                  elfsection_qnxinfo.len = ELF_GET_NUMERIC (elf, shdr, size);
1525
                  elfsection_qnxinfo.bytes = malloc (elfsection_qnxinfo.len);
1526
                  WELLMANNERED_ASSERT (elfsection_qnxinfo.bytes, "out of memory");
1527
                  memcpy (elfsection_qnxinfo.bytes, &entry_parms->data.bytes[ELF_GET_NUMERIC (elf, shdr, file_offset)], elfsection_qnxinfo.len);
10 pmbaty 1528
               }
11 pmbaty 1529
               reallocated_ptr = realloc (new_shtab.bytes, new_shtab.len + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more
10 pmbaty 1530
               WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
11 pmbaty 1531
               new_shtab.bytes = reallocated_ptr; // reallocation succeeded, save the new pointer
1532
               new_qnxinfo_shdr_offset = new_shtab.len; // remember the new offset of this section header
1533
               new_shtab.len += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now
10 pmbaty 1534
 
11 pmbaty 1535
               new_shdr = (elf_section_header_t *) &new_shtab.bytes[new_qnxinfo_shdr_offset]; // now populate this section header
1536
               ADD_NAME_TO_STRINGTABLE ("QNX_info", elfsection_shstrtab);
1537
               ELF_SET_NUMERIC (elf, new_shdr, name_offset, relative_offset_of_in ("QNX_info", &elfsection_shstrtab)); // update the relative offset of the section name
10 pmbaty 1538
               ELF_SET_NUMERIC (elf, new_shdr, type,         ELF_GET_NUMERIC (elf, shdr, type)); // duplicate section type
1539
               ELF_SET_NUMERIC (elf, new_shdr, flags,        ELF_GET_NUMERIC (elf, shdr, flags)); // duplicate section flags
1540
               ELF_SET_NUMERIC (elf, new_shdr, virtual_addr, ELF_GET_NUMERIC (elf, shdr, virtual_addr)); // duplicate section virtual address
1541
               ELF_SET_NUMERIC (elf, new_shdr, file_offset, (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset ? WILL_BE_FILLED_LATER : ELF_GET_NUMERIC (elf, shdr, file_offset))); // duplicate section offset only if it doesn't move
1542
               ELF_SET_NUMERIC (elf, new_shdr, size,         ELF_GET_NUMERIC (elf, shdr, size)); // duplicate section size
12 pmbaty 1543
               ELF_SET_NUMERIC (elf, new_shdr, linked_index, ELF_GET_NUMERIC (elf, shdr, linked_index)); // duplicate section linked index (which should be zero anyway)
10 pmbaty 1544
               ELF_SET_NUMERIC (elf, new_shdr, info,         ELF_GET_NUMERIC (elf, shdr, info)); // duplicate section info
1545
               ELF_SET_NUMERIC (elf, new_shdr, alignment,    ELF_GET_NUMERIC (elf, shdr, alignment)); // duplicate section alignment
1546
               ELF_SET_NUMERIC (elf, new_shdr, entry_size,   ELF_GET_NUMERIC (elf, shdr, entry_size)); // duplicate section entry size
1547
            }
1548
            if ((shdr = elf_get_section_header_by_name (elf, ".gnu_debuglink")) != NULL)
1549
            {
1550
               if (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset) // if this section needs to be moved around, have a copy of it
1551
               {
11 pmbaty 1552
                  elfsection_debuglink.len = ELF_GET_NUMERIC (elf, shdr, size);
1553
                  elfsection_debuglink.bytes = malloc (elfsection_debuglink.len);
1554
                  WELLMANNERED_ASSERT (elfsection_debuglink.bytes, "out of memory");
1555
                  memcpy (elfsection_debuglink.bytes, &entry_parms->data.bytes[ELF_GET_NUMERIC (elf, shdr, file_offset)], elfsection_debuglink.len);
10 pmbaty 1556
               }
11 pmbaty 1557
               reallocated_ptr = realloc (new_shtab.bytes, new_shtab.len + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more
10 pmbaty 1558
               WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
11 pmbaty 1559
               new_shtab.bytes = reallocated_ptr; // reallocation succeeded, save the new pointer
1560
               new_debuglink_shdr_offset = new_shtab.len; // remember the new offset of this section header
1561
               new_shtab.len += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now
10 pmbaty 1562
 
11 pmbaty 1563
               new_shdr = (elf_section_header_t *) &new_shtab.bytes[new_debuglink_shdr_offset]; // now populate this section header
1564
               ADD_NAME_TO_STRINGTABLE (".gnu_debuglink", elfsection_shstrtab);
1565
               ELF_SET_NUMERIC (elf, new_shdr, name_offset, relative_offset_of_in (".gnu_debuglink", &elfsection_shstrtab)); // update the relative offset of the section name
10 pmbaty 1566
               ELF_SET_NUMERIC (elf, new_shdr, type,         ELF_GET_NUMERIC (elf, shdr, type)); // duplicate section type
1567
               ELF_SET_NUMERIC (elf, new_shdr, flags,        ELF_GET_NUMERIC (elf, shdr, flags)); // duplicate section flags
1568
               ELF_SET_NUMERIC (elf, new_shdr, virtual_addr, ELF_GET_NUMERIC (elf, shdr, virtual_addr)); // duplicate section virtual address
1569
               ELF_SET_NUMERIC (elf, new_shdr, file_offset, (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset ? WILL_BE_FILLED_LATER : ELF_GET_NUMERIC (elf, shdr, file_offset))); // duplicate section offset only if it doesn't move
1570
               ELF_SET_NUMERIC (elf, new_shdr, size,         ELF_GET_NUMERIC (elf, shdr, size)); // duplicate section size
12 pmbaty 1571
               ELF_SET_NUMERIC (elf, new_shdr, linked_index, ELF_GET_NUMERIC (elf, shdr, linked_index)); // duplicate section linked index (which should be zero anyway)
10 pmbaty 1572
               ELF_SET_NUMERIC (elf, new_shdr, info,         ELF_GET_NUMERIC (elf, shdr, info)); // duplicate section info
1573
               ELF_SET_NUMERIC (elf, new_shdr, alignment,    ELF_GET_NUMERIC (elf, shdr, alignment)); // duplicate section alignment
1574
               ELF_SET_NUMERIC (elf, new_shdr, entry_size,   ELF_GET_NUMERIC (elf, shdr, entry_size)); // duplicate section entry size
1575
            }
1576
            if ((shdr = elf_get_section_header_by_name (elf, "QNX_usage")) != NULL)
1577
            {
1578
               if (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset) // if this section needs to be moved around, have a copy of it
1579
               {
11 pmbaty 1580
                  elfsection_qnxusage.len = ELF_GET_NUMERIC (elf, shdr, size);
1581
                  elfsection_qnxusage.bytes = malloc (elfsection_qnxusage.len);
1582
                  WELLMANNERED_ASSERT (elfsection_qnxusage.bytes, "out of memory");
1583
                  memcpy (elfsection_qnxusage.bytes, &entry_parms->data.bytes[ELF_GET_NUMERIC (elf, shdr, file_offset)], elfsection_qnxusage.len);
10 pmbaty 1584
               }
11 pmbaty 1585
               reallocated_ptr = realloc (new_shtab.bytes, new_shtab.len + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more
10 pmbaty 1586
               WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
11 pmbaty 1587
               new_shtab.bytes = reallocated_ptr; // reallocation succeeded, save the new pointer
1588
               new_qnxusage_shdr_offset = new_shtab.len; // remember the new offset of this section header
1589
               new_shtab.len += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now
10 pmbaty 1590
 
11 pmbaty 1591
               new_shdr = (elf_section_header_t *) &new_shtab.bytes[new_qnxusage_shdr_offset]; // now populate this section header
1592
               ADD_NAME_TO_STRINGTABLE ("QNX_usage", elfsection_shstrtab);
1593
               ELF_SET_NUMERIC (elf, new_shdr, name_offset, relative_offset_of_in ("QNX_usage", &elfsection_shstrtab)); // update the relative offset of the section name
10 pmbaty 1594
               ELF_SET_NUMERIC (elf, new_shdr, type,         ELF_GET_NUMERIC (elf, shdr, type)); // duplicate section type
1595
               ELF_SET_NUMERIC (elf, new_shdr, flags,        ELF_GET_NUMERIC (elf, shdr, flags)); // duplicate section flags
1596
               ELF_SET_NUMERIC (elf, new_shdr, virtual_addr, ELF_GET_NUMERIC (elf, shdr, virtual_addr)); // duplicate section virtual address
1597
               ELF_SET_NUMERIC (elf, new_shdr, file_offset, (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset ? WILL_BE_FILLED_LATER : ELF_GET_NUMERIC (elf, shdr, file_offset))); // duplicate section offset only if it doesn't move
1598
               ELF_SET_NUMERIC (elf, new_shdr, size,         ELF_GET_NUMERIC (elf, shdr, size)); // duplicate section size
12 pmbaty 1599
               ELF_SET_NUMERIC (elf, new_shdr, linked_index, ELF_GET_NUMERIC (elf, shdr, linked_index)); // duplicate section linked index (which should be zero anyway)
10 pmbaty 1600
               ELF_SET_NUMERIC (elf, new_shdr, info,         ELF_GET_NUMERIC (elf, shdr, info)); // duplicate section info
1601
               ELF_SET_NUMERIC (elf, new_shdr, alignment,    ELF_GET_NUMERIC (elf, shdr, alignment)); // duplicate section alignment
1602
               ELF_SET_NUMERIC (elf, new_shdr, entry_size,   ELF_GET_NUMERIC (elf, shdr, entry_size)); // duplicate section entry size
1603
            }
1604
            if ((shdr = elf_get_section_header_by_name (elf, ".note.gnu.build-id")) != NULL)
1605
            {
1606
               if (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset) // if this section needs to be moved around, have a copy of it
1607
               {
11 pmbaty 1608
                  elfsection_buildid.len = ELF_GET_NUMERIC (elf, shdr, size);
1609
                  elfsection_buildid.bytes = malloc (elfsection_buildid.len);
1610
                  WELLMANNERED_ASSERT (elfsection_buildid.bytes, "out of memory");
1611
                  memcpy (elfsection_buildid.bytes, &entry_parms->data.bytes[ELF_GET_NUMERIC (elf, shdr, file_offset)], elfsection_buildid.len);
10 pmbaty 1612
               }
11 pmbaty 1613
               reallocated_ptr = realloc (new_shtab.bytes, new_shtab.len + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more
10 pmbaty 1614
               WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
11 pmbaty 1615
               new_shtab.bytes = reallocated_ptr; // reallocation succeeded, save the new pointer
1616
               new_buildid_shdr_offset = new_shtab.len; // remember the new offset of this section header
1617
               new_shtab.len += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now
10 pmbaty 1618
 
11 pmbaty 1619
               new_shdr = (elf_section_header_t *) &new_shtab.bytes[new_buildid_shdr_offset]; // now populate this section header
1620
               ADD_NAME_TO_STRINGTABLE (".note.gnu.build-id", elfsection_shstrtab);
1621
               ELF_SET_NUMERIC (elf, new_shdr, name_offset, relative_offset_of_in (".note.gnu.build-id", &elfsection_shstrtab)); // update the relative offset of the section name
10 pmbaty 1622
               ELF_SET_NUMERIC (elf, new_shdr, type,         ELF_GET_NUMERIC (elf, shdr, type)); // duplicate section type
1623
               ELF_SET_NUMERIC (elf, new_shdr, flags,        ELF_GET_NUMERIC (elf, shdr, flags)); // duplicate section flags
1624
               ELF_SET_NUMERIC (elf, new_shdr, virtual_addr, ELF_GET_NUMERIC (elf, shdr, virtual_addr)); // duplicate section virtual address
1625
               ELF_SET_NUMERIC (elf, new_shdr, file_offset, (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset ? WILL_BE_FILLED_LATER : ELF_GET_NUMERIC (elf, shdr, file_offset))); // duplicate section offset only if it doesn't move
1626
               ELF_SET_NUMERIC (elf, new_shdr, size,         ELF_GET_NUMERIC (elf, shdr, size)); // duplicate section size
12 pmbaty 1627
               ELF_SET_NUMERIC (elf, new_shdr, linked_index, ELF_GET_NUMERIC (elf, shdr, linked_index)); // duplicate section linked index (which should be zero anyway)
10 pmbaty 1628
               ELF_SET_NUMERIC (elf, new_shdr, info,         ELF_GET_NUMERIC (elf, shdr, info)); // duplicate section info
1629
               ELF_SET_NUMERIC (elf, new_shdr, alignment,    ELF_GET_NUMERIC (elf, shdr, alignment)); // duplicate section alignment
1630
               ELF_SET_NUMERIC (elf, new_shdr, entry_size,   ELF_GET_NUMERIC (elf, shdr, entry_size)); // duplicate section entry size
1631
            }
11 pmbaty 1632
            reallocated_ptr = realloc (new_shtab.bytes, new_shtab.len + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more
10 pmbaty 1633
            WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
11 pmbaty 1634
            new_shtab.bytes = reallocated_ptr; // reallocation succeeded, save the new pointer
1635
            new_shstrtab_shdr_offset = new_shtab.len; // remember the new offset of this section header
1636
            new_shtab.len += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now
10 pmbaty 1637
 
11 pmbaty 1638
            new_shdr = (elf_section_header_t *) &new_shtab.bytes[new_shstrtab_shdr_offset]; // now populate this section header
1639
            ELF_SET_NUMERIC (elf, new_shdr, name_offset, relative_offset_of_in (".shstrtab", &elfsection_shstrtab)); // update the relative offset of the section name
10 pmbaty 1640
            ELF_SET_NUMERIC (elf, new_shdr, type,         ELF_SECTIONTYPE_STRINGTABLE); // section type (SHT_STRTAB)
1641
            ELF_SET_NUMERIC (elf, new_shdr, flags,        0); // section flags (we could set SHF_STRINGS i.e. 0x20 here, but mkifs does not, so mimic that)
1642
            ELF_SET_NUMERIC (elf, new_shdr, virtual_addr, 0); // this section does not need to be mapped
11 pmbaty 1643
            ELF_SET_NUMERIC (elf, new_shdr, file_offset, WILL_BE_FILLED_LATER); // will be filled once we know it
1644
            ELF_SET_NUMERIC (elf, new_shdr, size,         elfsection_shstrtab.len); // section size
10 pmbaty 1645
            ELF_SET_NUMERIC (elf, new_shdr, linked_index, 0); // this section is not linked to any other
1646
            ELF_SET_NUMERIC (elf, new_shdr, info,         0); // this section has no additional info
1647
            ELF_SET_NUMERIC (elf, new_shdr, alignment,    1); // this section is byte-aligned
1648
            ELF_SET_NUMERIC (elf, new_shdr, entry_size,   0); // this section is not a table, so entry_size is zero
1649
 
1650
            // jump over the new section headers table and write the sections that need to be relocated after the section headers table
11 pmbaty 1651
            entry_parms->data.len = new_shdrtable_offset + new_shtab.len; // assume there are no sections beyond the section headers table until known otherwise
1652
            if (elfsection_qnxinfo.bytes != NULL)
1653
               APPEND_SECTION_DATA (elfsection_qnxinfo, new_qnxinfo_shdr_offset); // write "QNX_info" section data if we have such a section
1654
            if (elfsection_debuglink.bytes != NULL)
1655
               APPEND_SECTION_DATA (elfsection_debuglink, new_debuglink_shdr_offset); // write ".gnu_debuglink" section data if we have such a section
1656
            if (elfsection_qnxusage.bytes != NULL)
1657
               APPEND_SECTION_DATA (elfsection_qnxusage, new_qnxusage_shdr_offset); // write "QNX_usage" section data if we have such a section
1658
            if (elfsection_buildid.bytes != NULL)
1659
               APPEND_SECTION_DATA (elfsection_buildid, new_buildid_shdr_offset); // write ".note.gnu.build-id" section data if we have such a section
1660
            APPEND_SECTION_DATA (elfsection_shstrtab, new_shstrtab_shdr_offset); // write the section header strings table as the last section
10 pmbaty 1661
 
1662
            // now write the section headers table
11 pmbaty 1663
            memcpy (&entry_parms->data.bytes[new_shdrtable_offset], new_shtab.bytes, new_shtab.len);
1664
            free (new_shtab.bytes); // free it
10 pmbaty 1665
 
1666
            // and finally fix the ELF header
1667
            ELF_SET_NUMERIC (elf, elf, section_header_table_offset, new_shdrtable_offset);
11 pmbaty 1668
            ELF_SET_NUMERIC (elf, elf, section_header_table_len, new_shtab.len / ELF_STRUCT_SIZE (elf, shdr));
1669
            ELF_SET_NUMERIC (elf, elf, section_header_names_idx, new_shtab.len / ELF_STRUCT_SIZE (elf, shdr) - 1); // the section headers strings table is the last section
10 pmbaty 1670
 
1671
            // align size with page size (4096 on x86, 16k on ARM)
11 pmbaty 1672
            end_padding_offset = entry_parms->data.len;
10 pmbaty 1673
            if (ELF_GET_NUMERIC (elf, elf, instruction_set) == ELF_MACHINE_X86_64)
11 pmbaty 1674
               entry_parms->data.len = ROUND_TO_UPPER_MULTIPLE (end_padding_offset, 4 * 1024); // 4 kb pages on Intel processors
10 pmbaty 1675
            else if (ELF_GET_NUMERIC (elf, elf, instruction_set) == ELF_MACHINE_AARCH64)
11 pmbaty 1676
               entry_parms->data.len = ROUND_TO_UPPER_MULTIPLE (end_padding_offset, 16 * 1024); // 16 kb pages on ARM64
10 pmbaty 1677
            else
1678
            {
1679
               fprintf (stderr, "fatal error: this ELF file \"%s\" does not belong to an architecture supported by ifstool (neither x86_64, nor aarch64)\n", stored_pathname);
1680
               exit (1);
1681
            }
11 pmbaty 1682
            memset (&entry_parms->data.bytes[end_padding_offset], 0, entry_parms->data.len - end_padding_offset); // zerofill
10 pmbaty 1683
 
1684
            entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF; // mark this inode as a preprocessed ELF file
1685
         } // end if the file is not yet a processed ELF
9 pmbaty 1686
      } // end if the file we're storing is an ELF file
1 pmbaty 1687
   }
1688
   else if (S_ISLNK (entry_parms->st_mode)) // else are we storing a symbolic link ?
11 pmbaty 1689
      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.bytes);
1 pmbaty 1690
   else // we must be storing a FIFO
1691
   {
11 pmbaty 1692
      if (strchr (entry_parms->data.bytes, ':') == NULL)
1 pmbaty 1693
      {
1694
         fprintf (stderr, "fatal error: device entry \"%s\" malformed (no 'dev:rdev' pair)\n", stored_pathname);
1695
         exit (1);
1696
      }
11 pmbaty 1697
      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.bytes);
1 pmbaty 1698
   }
1699
 
7 pmbaty 1700
   // grow filesystem entries array to hold one more slot
1 pmbaty 1701
   reallocated_ptr = realloc (*fsentries, (*fsentry_count + 1) * sizeof (fsentry_t)); // attempt to reallocate
1702
   if (reallocated_ptr == NULL)
1703
   {
1704
      fprintf (stderr, "fatal error: out of memory\n");
1705
      exit (1);
1706
   }
1707
   *fsentries = reallocated_ptr; // save reallocated pointer
1708
   fsentry = &(*fsentries)[*fsentry_count]; // quick access to fs entry slot
1709
   //fsentry->header.size = 0; // will be filled once we know it
1710
   fsentry->header.extattr_offset = 0;
10 pmbaty 1711
   fsentry->header.ino = entry_parms->extra_ino_flags | (++inode_count);
1 pmbaty 1712
   fsentry->header.mode = entry_parms->st_mode;
1713
   fsentry->header.gid = entry_parms->gid;
1714
   fsentry->header.uid = entry_parms->uid;
7 pmbaty 1715
   fsentry->header.mtime = (entry_parms->mtime == UINT32_MAX ? (uint32_t) time (NULL) : entry_parms->mtime);
1 pmbaty 1716
   if (S_ISDIR (entry_parms->st_mode))
1717
   {
7 pmbaty 1718
      fsentry->u.dir.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
1719
      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 1720
      fsentry->UNSAVED_was_data_written = true; // no data to save
1721
   }
1722
   else if (S_ISREG (entry_parms->st_mode))
1723
   {
1724
      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
11 pmbaty 1725
      fsentry->u.file.size = (uint32_t) entry_parms->data.len;
7 pmbaty 1726
      fsentry->u.file.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
11 pmbaty 1727
      fsentry->u.file.UNSAVED_databuf = malloc (entry_parms->data.len);
8 pmbaty 1728
      WELLMANNERED_ASSERT (fsentry->u.file.UNSAVED_databuf, "out of memory");
11 pmbaty 1729
      memcpy (fsentry->u.file.UNSAVED_databuf, entry_parms->data.bytes, entry_parms->data.len);
7 pmbaty 1730
      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 1731
      fsentry->UNSAVED_was_data_written = false; // there *IS* data to save
1732
   }
1733
   else if (S_ISLNK (entry_parms->st_mode))
1734
   {
7 pmbaty 1735
      fsentry->u.symlink.sym_offset = (uint16_t) (strlen (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname) + 1);
11 pmbaty 1736
      fsentry->u.symlink.sym_size = (uint16_t) entry_parms->data.len;
7 pmbaty 1737
      fsentry->u.symlink.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
11 pmbaty 1738
      fsentry->u.symlink.contents = strdup (entry_parms->data.bytes);
8 pmbaty 1739
      WELLMANNERED_ASSERT (fsentry->u.symlink.contents, "out of memory");
7 pmbaty 1740
      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 1741
      fsentry->UNSAVED_was_data_written = true; // no data to save
1742
   }
7 pmbaty 1743
   else // necessarily a device node
1 pmbaty 1744
   {
11 pmbaty 1745
      fsentry->u.device.dev  = strtol (entry_parms->data.bytes, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
1746
      fsentry->u.device.rdev = strtol (strchr (entry_parms->data.bytes, ':') + 1, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
7 pmbaty 1747
      fsentry->u.device.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
1748
      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 1749
      fsentry->UNSAVED_was_data_written = true; // no data to save
1750
   }
1751
   (*fsentry_count)++;
7 pmbaty 1752
 
1753
   // should we also add a symlink to this entry ? (in case we stored a dylib file under its canonical name)
1754
   if (original_stored_pathname != NULL)
1755
   {
1756
      entry_parms->is_compiled_bootscript = false;
1757
      entry_parms->should_autosymlink_dylib = false;
1758
      entry_parms->should_follow_symlinks = false;
1759
      entry_parms->st_mode = S_IFLNK | 0777; // NOTE: mkifs stores symlink permissions as rwxrwxrwx !
10 pmbaty 1760
      entry_parms->extra_ino_flags = (fsentry->header.ino & (IFS_INO_PROCESSED_ELF | IFS_INO_RUNONCE_ELF | IFS_INO_BOOTSTRAP_EXE)); // preserve target's inode flags
7 pmbaty 1761
      last_dirsep = strrchr (stored_pathname, '/');
11 pmbaty 1762
      old_data = entry_parms->data.bytes; // backup previous data pointer
1763
      entry_parms->data.bytes = (uint8_t *) (last_dirsep == NULL ? stored_pathname : last_dirsep + 1); // store symlink target in dirent data
1764
      entry_parms->data.len = strlen (entry_parms->data.bytes);
7 pmbaty 1765
      add_fsentry (fsentries, fsentry_count, entry_parms, original_stored_pathname, NULL);
11 pmbaty 1766
      entry_parms->data.bytes = old_data; // restore previous data pointer so that it can be freed normally
7 pmbaty 1767
   }
1768
 
1 pmbaty 1769
   return (*fsentry_count);
1770
}
1771
 
1772
 
1773
static int fsentry_compare_pathnames_cb (const void *a, const void *b)
1774
{
1775
   // qsort() callback that compares two imagefs filesystem entries and sort them alphabetically by pathname
1776
 
3 pmbaty 1777
   const fsentry_t *entry_a = (const fsentry_t *) a;
1778
   const fsentry_t *entry_b = (const fsentry_t *) b;
1 pmbaty 1779
   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)));
1780
   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)));
1781
   return (strcmp (pathname_a, pathname_b));
1782
}
1783
 
1784
 
7 pmbaty 1785
static void update_MKIFS_PATH (const char *processor)
1 pmbaty 1786
{
7 pmbaty 1787
   // updates the value of MKIFS_PATH according to the passed processor name string, unless an environment variable already defines it
1 pmbaty 1788
 
7 pmbaty 1789
   char processor_base[16];
1790
   size_t data_len;
1791
   char *envvar;
1792
   char *token;
1793
 
1794
   envvar = getenv ("MKIFS_PATH"); // look in the environment first, and construct a default one if not supplied
1795
   if (envvar != NULL)
1796
      MKIFS_PATH = envvar; // if envvar is present, set MKIFS_PATH to point to it
1797
   else // envvar not present
1798
   {
1799
      if (MKIFS_PATH != NULL)
1800
         free (MKIFS_PATH); // free any MKIFS_PATH that we constructed earlier
1801
 
1802
      strcpy (processor_base, processor); // construct PROCESSOR_BASE
1803
      token = strchr (processor_base, '-');
1804
      if (token != NULL)
1805
         *token = 0; // split anything from the first dash onwards
1806
      data_len = strlen (processor_base);
1807
      if ((data_len > 2) && ((processor_base[data_len - 2] == 'b') || (processor_base[data_len - 2] == 'l')) && (processor_base[data_len - 1] == 'e'))
1808
         processor_base[data_len - 2] = 0; // if it ends with "le" or "be", strip that too
1809
 
1810
      MKIFS_PATH = malloc (10 * MAXPATHLEN); // construct a default MKIFS_PATH now
8 pmbaty 1811
      WELLMANNERED_ASSERT (MKIFS_PATH, "out of memory");
7 pmbaty 1812
      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
1813
               QNX_TARGET, processor,
1814
               QNX_TARGET, processor,
1815
               QNX_TARGET, processor,
1816
               QNX_TARGET, processor_base,
1817
               QNX_TARGET, processor,
1818
               QNX_TARGET, processor,
1819
               QNX_TARGET, processor,
1820
               QNX_TARGET, processor,
1821
               QNX_TARGET, processor);
1822
   }
1823
 
1824
   return;
1 pmbaty 1825
}
1826
 
1827
 
1828
int main (int argc, char **argv)
1829
{
1830
   // program entrypoint
1831
 
1832
   #define PAD_OUTFILE_TO(val) do { curr_offset = ftell (fp); while (curr_offset < (val)) { putc (0, fp); curr_offset++; } } while (0)
1833
 
2 pmbaty 1834
   static startup_header_t startup_header = { 0 }; // output IFS's startup header
1835
   static startup_trailer_v2_t startup_trailer = { 0 }; // output IFS's startup trailer (version 2, with SHA-512 checksum and int32 checksum)
1836
   static image_header_t image_header = { 0 }; // output IFS's imagefs header
1837
   static image_trailer_v2_t image_trailer = { 0 }; // output IFS's imagefs trailer (version 2, with SHA-512 checksum and int32 checksum)
1838
   static fsentry_t *fsentries = NULL; // output IFS's filesystem entries
1839
   static size_t fsentry_count = 0; // number of entries in the IFS filesystem
7 pmbaty 1840
   static parms_t default_parms = { // default parameters for a filesystem entry
1841
      .dperms = 0755,
1842
      .perms = 0644,
1843
      .uid = 0,
1844
      .gid = 0,
1845
      .st_mode = S_IFREG,
1846
      .mtime = UINT32_MAX,
1847
      .mtime_for_inline_files = UINT32_MAX,
1848
      .prefix = "/proc/boot",
1849
      .should_follow_symlinks = true, // [+|-followlink]
1850
      .should_autosymlink_dylib = true, // [+|-autolink]
1851
      .is_compiled_bootscript = false, // [+|-script]
10 pmbaty 1852
      .extra_ino_flags = 0,
7 pmbaty 1853
      .search = "",
11 pmbaty 1854
      .data = { NULL, 0 }
7 pmbaty 1855
   };
2 pmbaty 1856
   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 1857
 
1858
   // bootable IFS support
1859
   char *bootfile_pathname = NULL;           // HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS
2 pmbaty 1860
   size_t bootfile_size = 0;                 // HACK: size of the bootcode binary blob file to put at the start of a bootable IFS
1 pmbaty 1861
   char *startupfile_pathname = NULL;        // HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS
1862
   size_t startupfile_ep_from_imagebase = 0; // HACK: startup code entrypoint offset from image base for a bootable IFS 
1863
   char *kernelfile_pathname = NULL;         // HACK: pathname to precompiled kernel file blob to put in a bootable IFS
1864
   size_t kernelfile_offset = 0;             // HACK: kernel file offset in bootable IFS
1865
 
7 pmbaty 1866
   char path_on_buildhost[MAXPATHLEN] = "";
1867
   char path_in_ifs[MAXPATHLEN] = "";
1 pmbaty 1868
   char *ifs_pathname = NULL;
1869
   void *reallocated_ptr;
10 pmbaty 1870
   const elf_header_t *elf;
1871
   elf_program_header_t *phdr;
7 pmbaty 1872
   struct tm utc_time;
1 pmbaty 1873
   struct stat stat_buf;
1874
   size_t startuptrailer_offset;
1875
   size_t startupheader_offset;
8 pmbaty 1876
   size_t imagetrailer_offset;
1877
   size_t imageheader_offset;
11 pmbaty 1878
   size_t corrective_offset;
1 pmbaty 1879
   size_t imgdir_offset;
1880
   size_t imgdir_size;
1881
   size_t final_size;
7 pmbaty 1882
   size_t available_space;
1883
   size_t allocated_size;
1 pmbaty 1884
   size_t fsentry_index;
7 pmbaty 1885
   size_t largest_index;
1886
   size_t largest_size;
1 pmbaty 1887
   size_t curr_offset;
10 pmbaty 1888
   size_t table_index;
1889
   size_t table_count;
11 pmbaty 1890
   buffer_t blob;
8 pmbaty 1891
   int32_t checksum;
7 pmbaty 1892
   char *specifiedpathname_start;
1 pmbaty 1893
   char *directiveblock_start;
1894
   char *write_ptr;
7 pmbaty 1895
   char *line_ptr;
14 pmbaty 1896
   char *outdir = ".";
1 pmbaty 1897
   char *token;
1898
   char *value;
1899
   char *sep;
1900
   //char *ctx;
1901
   int arg_index;
1902
   bool is_quoted_context = false;
1903
   bool is_escaped_char = false;
11 pmbaty 1904
   bool should_discard_inline_contents = false;
1 pmbaty 1905
   bool want_info = false;
10 pmbaty 1906
   bool want_everything = false;
1 pmbaty 1907
   bool want_help = false;
14 pmbaty 1908
   bool want_dump = false;
5 pmbaty 1909
   bool is_foreign_endianness;
1 pmbaty 1910
   int string_len;
1911
   int read_char;
1912
   FILE *buildfile_fp;
1913
   FILE *fp;
1914
 
1915
   // parse arguments
1916
   for (arg_index = 1; arg_index < argc; arg_index++)
1917
   {
1918
      if ((strcmp (argv[arg_index], "--bootfile") == 0) && (arg_index + 1 < argc)) // --bootfile path/to/blob.bin
1919
         bootfile_pathname = argv[++arg_index];
1920
      else if ((strcmp (argv[arg_index], "--startupfile") == 0) && (arg_index + 1 < argc)) // --startupfile path/to/blob.bin@0x1030
1921
      {
1922
         sep = strchr (argv[++arg_index], '@');
1923
         if ((sep == NULL) || (sep[1] == 0))
1924
         {
1925
            fprintf (stderr, "error: the --startupfile arguments expects <pathname>@<entrypoint_from_image_base>\n");
1926
            exit (1);
1927
         }
1928
         *sep = 0;
1929
         startupfile_pathname = argv[arg_index];
1930
         startupfile_ep_from_imagebase = (size_t) read_integer (sep + 1);
1931
      }
1932
      else if ((strcmp (argv[arg_index], "--kernelfile") == 0) && (arg_index + 1 < argc)) // --kernelfile path/to/blob.bin@0x32000
1933
      {
1934
         sep = strchr (argv[++arg_index], '@');
1935
         if ((sep == NULL) || (sep[1] == 0))
1936
         {
1937
            fprintf (stderr, "error: the --kernelfile arguments expects <pathname>@<fileoffset>\n");
1938
            exit (1);
1939
         }
1940
         *sep = 0;
1941
         kernelfile_pathname = argv[arg_index];
1942
         kernelfile_offset = (size_t) read_integer (sep + 1);
1943
      }
7 pmbaty 1944
      else if (strcmp (argv[arg_index], "-n") == 0)
1945
         default_parms.mtime_for_inline_files = 0; // inline files should have a mtime set to zero
1946
      else if (strcmp (argv[arg_index], "-nn") == 0)
1947
      {
11 pmbaty 1948
         default_parms.mtime = 0; // *all* files should have a mtime set to zero
7 pmbaty 1949
         default_parms.mtime_for_inline_files = 0;
1950
      }
14 pmbaty 1951
      else if ((strcmp (argv[arg_index], "--outdir") == 0) && (arg_index + 1 < argc)) // --outdir path
1952
         outdir = argv[++arg_index];
1 pmbaty 1953
      else if (strcmp (argv[arg_index], "--info") == 0)
1954
         want_info = true;
14 pmbaty 1955
      else if (strcmp (argv[arg_index], "--dump") == 0)
1956
         want_dump = true;
10 pmbaty 1957
      else if (strcmp (argv[arg_index], "--everything") == 0)
1958
         want_everything = true;
1 pmbaty 1959
      else if ((strcmp (argv[arg_index], "-?") == 0) || (strcmp (argv[arg_index], "--help") == 0))
1960
         want_help = true;
1961
      else if (buildfile_pathname == NULL)
1962
         buildfile_pathname = argv[arg_index];
1963
      else if (ifs_pathname == NULL)
1964
         ifs_pathname = argv[arg_index];
1965
   }
1966
 
1967
   // do we not have enough information to run ?
14 pmbaty 1968
   if (want_help || (buildfile_pathname == NULL) || (!want_info && !want_dump && (ifs_pathname == NULL)))
1 pmbaty 1969
   {
12 pmbaty 1970
      fp = (want_help ? stdout : stderr); // select the right output channel
1971
      fprintf (fp, "ifstool - QNX in-kernel filesystem creation utility by Pierre-Marie Baty <pm@pmbaty.com>\n");
1972
      fprintf (fp, "          version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD);
4 pmbaty 1973
      if (!want_help)
12 pmbaty 1974
         fprintf (fp, "error: missing parameters\n");
1975
      fprintf (fp, "usage:\n");
1976
      fprintf (fp, "    ifstool [--bootfile <pathname>] [--startupfile <pathname>@<EP_from_imgbase>] [--kernelfile <pathname>@<fileoffs>] [-n[n]] <buildfile> <outfile>\n");
1977
      fprintf (fp, "    ifstool --info [--everything] <ifs file>\n");
14 pmbaty 1978
      fprintf (fp, "    ifstool --dump [--outdir <path>] <ifs file>\n");
12 pmbaty 1979
      fprintf (fp, "    ifstool --help\n");
1980
      fprintf (fp, "NOTE: the compilation feature requires predigested boot, startup and kernel files produced by mkifs.\n");
1 pmbaty 1981
      exit (want_help ? 0 : 1);
1982
   }
1983
 
14 pmbaty 1984
   // do we want info about a particular IFS ? if so, dissecate it
2 pmbaty 1985
   if (want_info)
10 pmbaty 1986
      exit (dump_ifs_info (buildfile_pathname, want_everything)); // NOTE: the first argument after --info is actually the IFS file, not a build file, but the arguments are collected in this order
2 pmbaty 1987
 
14 pmbaty 1988
   // else do we want to dump its contents ? if so, do so
1989
   else if (want_dump)
1990
      exit (dump_ifs_contents (buildfile_pathname, outdir)); // NOTE: the first argument after --info is actually the IFS file, not a build file, but the arguments are collected in this order
1991
 
1 pmbaty 1992
   // make sure we have ${QNX_TARGET} pointing somewhere
1993
   QNX_TARGET = getenv ("QNX_TARGET");
1994
   if (QNX_TARGET == NULL)
1995
   {
1996
      fprintf (stderr, "error: the QNX_TARGET environment variable is not set\n");
1997
      exit (1);
1998
   }
1999
   else if (access (QNX_TARGET, 0) != 0)
2000
   {
2001
      fprintf (stderr, "error: the QNX_TARGET environment variable doesn't point to an existing directory\n");
2002
      exit (1);
2003
   }
2004
 
7 pmbaty 2005
   // prepare a default MKIFS_PATH assuming the host processor
2006
   update_MKIFS_PATH (image_processor);
2007
 
1 pmbaty 2008
   // open build file
2009
   buildfile_fp = fopen (buildfile_pathname, "rb");
2010
   if (buildfile_fp == NULL)
2011
   {
2012
      fprintf (stderr, "error: unable to open build file \"%s\" for reading (%s)\n", buildfile_pathname, strerror (errno));
2013
      exit (1);
2014
   }
2015
 
2016
   // stack up filesystem entries
2017
   memcpy (&entry_parms, &default_parms, sizeof (default_parms));
2018
   entry_parms.st_mode = S_IFDIR | default_parms.dperms;
7 pmbaty 2019
   add_fsentry (&fsentries, &fsentry_count, &entry_parms, "", NULL); // add the root dir first
1 pmbaty 2020
 
11 pmbaty 2021
   // parse the IFS build file line per line
1 pmbaty 2022
   while (fgets (line_buffer, sizeof (line_buffer), buildfile_fp) != NULL)
2023
   {
7 pmbaty 2024
      if (current_line != NULL)
2025
         free (current_line);
2026
      current_line = strdup (line_buffer);
8 pmbaty 2027
      WELLMANNERED_ASSERT (current_line, "out of memory");
1 pmbaty 2028
      lineno++; // keep track of current line number
2029
 
2030
      line_ptr = line_buffer;
2031
      while ((*line_ptr != 0) && isspace (*line_ptr))
2032
         line_ptr++; // skip leading spaces
2033
 
2034
      if ((*line_ptr == 0) || (*line_ptr == '#'))
2035
         continue; // skip empty or comment lines
2036
 
2037
      string_len = (int) strlen (line_buffer);
2038
      if ((string_len > 0) && (line_buffer[string_len - 1] == '\n'))
2039
         line_buffer[string_len - 1] = 0; // chop off newline for easier debug output
2040
 
2041
      // reset entry values
2042
      memcpy (&entry_parms, &default_parms, sizeof (default_parms));
7 pmbaty 2043
      path_in_ifs[0] = 0;
2044
      path_on_buildhost[0] = 0;
11 pmbaty 2045
      should_discard_inline_contents = false;
1 pmbaty 2046
 
2047
      // does this line start with an attribute block ?
2048
      if (*line_ptr == '[')
2049
      {
2050
         line_ptr++; // skip the leading square bracket
2051
         directiveblock_start = line_ptr; // remember where it starts
2052
         is_quoted_context = false;
2053
         while ((*line_ptr != 0) && !((*line_ptr == ']') && (line_ptr[-1] != '\\')))
2054
         {
2055
            if (*line_ptr == '"')
2056
               is_quoted_context ^= true; // remember when we're between quotes
2057
            else if (!is_quoted_context && (*line_ptr == ' '))
2058
               *line_ptr = RECORD_SEP; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
2059
            line_ptr++; // reach the next unescaped closing square bracket
2060
         }
2061
         if (*line_ptr != ']')
2062
         {
2063
            fprintf (stderr, "warning: syntax error in \"%s\" line %d: unterminated attributes block (skipping)\n", buildfile_pathname, lineno);
2064
            continue; // invalid attribute block, skip line
2065
         }
2066
         *line_ptr = 0; // end the attribute block so that it is a parsable C string
2067
 
2068
         // now parse the attribute tokens
2069
         // DOCUMENTATION: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/m/mkifs.html#mkifs__description
2070
         token = strtok (directiveblock_start, RECORD_SEP_STR);
2071
         while (token != NULL)
2072
         {
2073
            // evaluate attribute token
2074
            #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
2075
            if      (strncmp (token, "uid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.uid     = (int) read_integer (value); }
2076
            else if (strncmp (token, "gid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.gid     = (int) read_integer (value); }
2077
            else if (strncmp (token, "dperms=",  7) == 0) { REACH_TOKEN_VALUE (); entry_parms.dperms  = (int) read_integer (value); }
2078
            else if (strncmp (token, "perms=",   6) == 0) { REACH_TOKEN_VALUE (); entry_parms.perms   = (int) read_integer (value); }
2079
            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))))); }
2080
            else if (strncmp (token, "prefix=",  7) == 0) { REACH_TOKEN_VALUE (); strcpy (entry_parms.prefix, (*value == '/' ? value + 1 : value)); } // skip possible leading slash in prefix
2081
            else if (strncmp (token, "image=",   6) == 0) { REACH_TOKEN_VALUE ();
2082
               image_base = (uint32_t) read_integer (value); // read image base address
12 pmbaty 2083
               if ((sep = strchr (value, '-')) != NULL) image_end       = (uint32_t) read_integer (sep + 1); // if we have a dash, read optional image end (TODO: check this value and produce an error in the relevant case. Not important.)
1 pmbaty 2084
               if ((sep = strchr (value, ',')) != NULL) image_maxsize   = (uint32_t) read_integer (sep + 1); // if we have a comma, read optional image max size
2085
               if ((sep = strchr (value, '=')) != NULL) image_totalsize = (uint32_t) read_integer (sep + 1); // if we have an equal sign, read optional image padding size
2086
               if ((sep = strchr (value, '%')) != NULL) image_align     = (uint32_t) read_integer (sep + 1); // if we have a modulo sign, read optional image aligmnent
2087
               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);
2088
            }
2089
            else if (strncmp (token, "virtual=", 8) == 0) { REACH_TOKEN_VALUE ();
2090
               if ((bootfile_pathname == NULL) || (startupfile_pathname == NULL) || (kernelfile_pathname == NULL)) // HACK until I figure out how to re-create them
2091
               {
2092
                  fprintf (stderr, "error: creating bootable images require the --bootfile, --startupfile and --kernelfile command-line options in \"%s\" line %d\n", buildfile_pathname, lineno);
2093
                  exit (1);
2094
               }
2095
               if ((sep = strchr (value, ',')) != NULL) // do we have a comma separating (optional) processor and boot file name ?
2096
               {
2097
                  *sep = 0;
2098
                  strcpy (image_processor, value); // save processor
7 pmbaty 2099
                  update_MKIFS_PATH (image_processor);
1 pmbaty 2100
                  value = sep + 1;
2101
               }
12 pmbaty 2102
               //sprintf (image_bootfile, "%s/%s/boot/sys/%s.boot", QNX_TARGET, image_processor, value); // save preboot file name (TODO: we should search in MKIFS_PATH instead of this. Not important.)
2 pmbaty 2103
               //strcpy (image_bootfile, bootfile_pathname); // FIXME: HACK
2104
               if (stat (bootfile_pathname, &stat_buf) != 0)
1 pmbaty 2105
               {
2 pmbaty 2106
                  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 2107
                  exit (1);
2108
               }
2 pmbaty 2109
               bootfile_size = stat_buf.st_size; // save preboot file size
2110
               fprintf (stderr, "info: processor \"%s\" bootfile \"%s\"\n", image_processor, bootfile_pathname);
11 pmbaty 2111
#if 1
2112
               // ######################################################################################################################################################################################################################################
2113
               // # FIXME: figure out how to re-create it: linker call involved
2114
               // # $ 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
2115
               // ######################################################################################################################################################################################################################################
2116
               if (read_filecontents (kernelfile_pathname, ".", &entry_parms.data) == NULL)
2 pmbaty 2117
               {
2118
                  fprintf (stderr, "fatal error: unable to read precompiled kernel file \"%s\" specified in --kernelfile argument\n", kernelfile_pathname);
2119
                  exit (1);
2120
               }
11 pmbaty 2121
#else // nonworking
2122
               strcpy (path_on_buildhost, "procnto-smp-instr");
2123
#endif // nonworking
2124
 
2125
               should_discard_inline_contents = true; // remember we already have data (so as to discard the inline block's contents)
1 pmbaty 2126
            }
7 pmbaty 2127
            else if (strncmp (token, "mtime=", 6) == 0) { REACH_TOKEN_VALUE (); if (strcmp (value, "*") == 0) entry_parms.mtime = UINT32_MAX; else {
2128
                  // value *must* be "YYYY-MM-DD-HH:MM:SS" by specification
2129
                  memset (&utc_time, 0, sizeof (utc_time));
2130
                  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)
2131
                  {
2132
                     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);
2133
                     continue; // invalid attribute block, skip line
2134
                  }
2135
                  utc_time.tm_mon--; // convert month from [1-12] to [0-11]
2136
                  entry_parms.mtime = (uint32_t) mktime (&utc_time);
2137
               }
2138
            }
2139
            else if (strcmp (token, "+script")     == 0) {
2 pmbaty 2140
               entry_parms.is_compiled_bootscript = true;
11 pmbaty 2141
               entry_parms.data.bytes = malloc (sizeof (INITIAL_STARTUP_SCRIPT) - 1);
2142
               WELLMANNERED_ASSERT (entry_parms.data.bytes, "out of memory");
2143
               memcpy (entry_parms.data.bytes, INITIAL_STARTUP_SCRIPT, sizeof (INITIAL_STARTUP_SCRIPT) - 1); // FIXME: HACK until the script compiler is implemented
2144
               entry_parms.data.len = sizeof (INITIAL_STARTUP_SCRIPT) - 1;
2145
               should_discard_inline_contents = true; // remember we already have data (so as to discard the inline block's contents)
2 pmbaty 2146
            }
7 pmbaty 2147
            else if (strcmp (token, "-script")     == 0) entry_parms.is_compiled_bootscript = false;
2148
            else if (strcmp (token, "+followlink") == 0) entry_parms.should_follow_symlinks = true;
2149
            else if (strcmp (token, "-followlink") == 0) entry_parms.should_follow_symlinks = false;
2150
            else if (strcmp (token, "+autolink")   == 0) entry_parms.should_autosymlink_dylib = true;
2151
            else if (strcmp (token, "-autolink")   == 0) entry_parms.should_autosymlink_dylib = false;
1 pmbaty 2152
            else fprintf (stderr, "warning: unimplemented attribute in \"%s\" line %d: '%s'\n", buildfile_pathname, lineno, token);
2153
            #undef REACH_TOKEN_VALUE
2154
 
2155
            token = strtok (NULL, RECORD_SEP_STR); // proceed to next attribute token
2156
         }
2157
 
2158
         line_ptr++; // reach the next character
2159
         while ((*line_ptr != 0) && isspace (*line_ptr))
2160
            line_ptr++; // skip leading spaces
2161
 
2162
         // are we at the end of the line ? if so, it means the attribute values that are set should become the default
2163
         if ((*line_ptr == 0) || (*line_ptr == '#'))
2164
         {
2165
            #define APPLY_DEFAULT_ATTR_NUM(attr,descr,fmt) do { if (entry_parms.attr != default_parms.attr) { \
2166
                  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); \
2167
                  default_parms.attr = entry_parms.attr; \
2168
               } } while (0)
2169
            #define APPLY_DEFAULT_ATTR_STR(attr,descr,fmt) do { if (strcmp (entry_parms.attr, default_parms.attr) != 0) { \
2170
                  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); \
2171
                  strcpy (default_parms.attr, entry_parms.attr); \
2172
               } } while (0)
7 pmbaty 2173
            APPLY_DEFAULT_ATTR_NUM (dperms,                   "directory permissions",           "0%o");
2174
            APPLY_DEFAULT_ATTR_NUM (perms,                    "file permissions",                "0%o");
2175
            APPLY_DEFAULT_ATTR_NUM (uid,                      "owner ID",                        "%d");
2176
            APPLY_DEFAULT_ATTR_NUM (gid,                      "group ID",                        "%d");
2177
            APPLY_DEFAULT_ATTR_NUM (st_mode,                  "inode type",                      "0%o");
2178
            APPLY_DEFAULT_ATTR_STR (prefix,                   "prefix",                          "\"%s\"");
2179
            APPLY_DEFAULT_ATTR_NUM (is_compiled_bootscript,   "compiled script state",           "%d");
2180
            APPLY_DEFAULT_ATTR_NUM (should_follow_symlinks,   "symlink resolution",              "%d");
2181
            APPLY_DEFAULT_ATTR_NUM (should_autosymlink_dylib, "dylib canonical name symlinking", "%d");
1 pmbaty 2182
            #undef APPLY_DEFAULT_ATTR_STR
2183
            #undef APPLY_DEFAULT_ATTR_NUM
2184
            continue; // end of line reached, proceed to the next line
2185
         }
2186
         // end of attributes parsing
2187
      } // end of "this line starts with an attributes block"
2188
 
2189
      // there's data in this line. We expect a filename in the IFS. Read it and unescape escaped characters
2190
      string_len = sprintf (path_in_ifs, "%s", entry_parms.prefix);
2191
      while ((string_len > 0) && (path_in_ifs[string_len - 1] == '/'))
2192
         string_len--; // chop off any trailing slashes from prefix
2193
      write_ptr = &path_in_ifs[string_len];
2194
      *write_ptr++ = '/'; // add ONE trailing slash
7 pmbaty 2195
      specifiedpathname_start = write_ptr; // remember the specified pathname will start here
2196
      is_quoted_context = (*line_ptr == '"');
2197
      if (is_quoted_context)
2198
         line_ptr++; // skip a possible initial quote
1 pmbaty 2199
      if (*line_ptr == '/')
2200
      {
2201
         fprintf (stderr, "warning: paths in the IFS file should not begin with a leading '/' in \"%s\" line %d\n", buildfile_pathname, lineno);
2202
         line_ptr++; // consistency check: paths in the IFS should not begin with a '/'
2203
      }
7 pmbaty 2204
      while ((*line_ptr != 0) && ((!is_quoted_context && (*line_ptr != '=') && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr == '"'))))
1 pmbaty 2205
      {
2206
         if (*line_ptr == '\\')
2207
         {
2208
            line_ptr++;
2209
            *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
2210
         }
2211
         else
2212
            *write_ptr++ = *line_ptr;
2213
         line_ptr++;
2214
      }
2215
      *write_ptr = 0; // terminate the string
7 pmbaty 2216
      if (is_quoted_context && (*line_ptr == '"'))
2217
         line_ptr++; // skip a possible final quote
1 pmbaty 2218
 
2219
      // we reached a space OR an equal sign
2220
      while ((*line_ptr != 0) && isspace (*line_ptr))
2221
         line_ptr++; // skip optional spaces after the filename in the IFS
2222
 
2223
      // do we have an equal sign ?
2224
      if (*line_ptr == '=') // we must be creating either a directory or a file, do we have an equal sign ?
2225
      {
2226
         line_ptr++; // skip the equal sign
2227
         while ((*line_ptr != 0) && isspace (*line_ptr))
2228
            line_ptr++; // skip optional spaces after the equal sign
2229
 
2230
         if (*line_ptr == 0)
2231
         {
2232
            fprintf (stderr, "warning: syntax error in \"%s\" line %d: missing data specification after equal sign (skipping)\n", buildfile_pathname, lineno);
2233
            continue; // invalid symlink specification, skip line
2234
         }
2235
 
2236
         // read the host system's path, it may be either a path or a contents definition. Is it a content definition ?
2237
         if (*line_ptr == '{')
2238
         {
7 pmbaty 2239
            allocated_size = 0;
2240
 
1 pmbaty 2241
            line_ptr++; // skip the leading content definition
2242
            is_escaped_char = false;
2243
            for (;;)
2244
            {
2245
               read_char = fgetc (buildfile_fp);
2246
               if (read_char == EOF)
2247
               {
2248
                  fprintf (stderr, "fatal error: syntax error in \"%s\" line %d: unterminated contents block (end of file reached)\n", buildfile_pathname, lineno);
2249
                  exit (1); // invalid contents block
2250
               }
8 pmbaty 2251
               else if ((read_char == '\\') && !is_escaped_char)
1 pmbaty 2252
                  is_escaped_char = true; // remember the next char is escaped
2253
               else if ((read_char == '}') && !is_escaped_char)
2254
                  break; // found an unescaped closing bracked, stop parsing
2255
               else
2256
               {
2257
                  is_escaped_char = false; // any other char, meaning the next one will not be escaped
11 pmbaty 2258
                  if (!should_discard_inline_contents) // only store the contents if we do NOT know the data yet
1 pmbaty 2259
                  {
11 pmbaty 2260
                     if (entry_parms.data.len == allocated_size) // reallocate in 4 kb blocks
1 pmbaty 2261
                     {
11 pmbaty 2262
                        reallocated_ptr = realloc (entry_parms.data.bytes, allocated_size + 4096);
2263
                        WELLMANNERED_ASSERT (reallocated_ptr != NULL, "out of memory");
2264
                        entry_parms.data.bytes = reallocated_ptr;
7 pmbaty 2265
                        allocated_size += 4096;
1 pmbaty 2266
                     }
11 pmbaty 2267
                     entry_parms.data.bytes[entry_parms.data.len++] = read_char;
1 pmbaty 2268
                  }
2269
                  if (read_char == '\n')
8 pmbaty 2270
                     lineno++; // update line counter as we parse the inline content
1 pmbaty 2271
               }
8 pmbaty 2272
            } // end for
1 pmbaty 2273
         }
7 pmbaty 2274
         else // not a content definition between { brackets }, must be either a pathname on the build host, or the target of a symlink
1 pmbaty 2275
         {
7 pmbaty 2276
            is_quoted_context = (*line_ptr == '"');
2277
            if (is_quoted_context)
2278
               line_ptr++; // skip a possible initial quote
2279
            specifiedpathname_start = line_ptr; // remember where the specified pathname starts
2280
            write_ptr = line_ptr; // now unescape all characters
2281
            while ((*line_ptr != 0) && ((!is_quoted_context && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr == '"'))))
2282
            {
2283
               if (*line_ptr == '\\')
2284
               {
2285
                  line_ptr++;
2286
                  *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
2287
               }
2288
               else
2289
                  *write_ptr++ = *line_ptr;
2290
               line_ptr++;
2291
            }
2292
            *write_ptr = 0; // terminate the string
2293
            if (is_quoted_context && (*line_ptr == '"'))
2294
               line_ptr++; // skip a possible final quote
2295
 
2296
            if (S_ISLNK (entry_parms.st_mode)) // are we storing a symlink ?
2297
            {
11 pmbaty 2298
               entry_parms.data.bytes = strdup (specifiedpathname_start); // if so, store the symlink target as the dirent's blob data
2299
               WELLMANNERED_ASSERT (entry_parms.data.bytes != NULL, "out of memory");
2300
               entry_parms.data.len = strlen (specifiedpathname_start);
7 pmbaty 2301
            }
2302
            else // it's a build host filesystem path
2303
               strcpy (path_on_buildhost, line_ptr); // the path on the build host is given after the equal sign
1 pmbaty 2304
         }
2305
      }
2306
      else // no equal sign, meaning the file will have the same name on the build host filesystem
2307
      {
2308
         // consistency check: symlinks MUST have an equal sign
2309
         if (entry_parms.st_mode == S_IFLNK)
2310
         {
2311
            fprintf (stderr, "warning: syntax error in \"%s\" line %d: missing equal sign and symlink target (skipping)\n", buildfile_pathname, lineno);
2312
            continue; // invalid symlink specification, skip line
2313
         }
2314
 
7 pmbaty 2315
         strcpy (path_on_buildhost, specifiedpathname_start); // the path on the build host is the one specified
2316
         sep = strrchr (specifiedpathname_start, '/');
2317
         if (sep != NULL)
2318
            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 2319
      }
2320
 
2321
      // now add this entry to the image filesystem
7 pmbaty 2322
      if (S_ISDIR (entry_parms.st_mode))
2323
         entry_parms.st_mode |= entry_parms.dperms;
2324
      else if (S_ISLNK (entry_parms.st_mode))
2325
         entry_parms.st_mode |= 0777; // NOTE: mkifs sets symlink permissions to rwxrwxrwx !?
2326
      else // file or device node
2327
         entry_parms.st_mode |= entry_parms.perms;
1 pmbaty 2328
 
7 pmbaty 2329
      add_fsentry (&fsentries, &fsentry_count, &entry_parms, path_in_ifs, path_on_buildhost); // and add filesystem entry
1 pmbaty 2330
 
11 pmbaty 2331
      if (entry_parms.data.bytes != NULL)
2332
         free (entry_parms.data.bytes); // if blob data was allocated, free it
1 pmbaty 2333
   }
2334
 
2335
   // write IFS file
7 pmbaty 2336
   fp = fopen (ifs_pathname, "w+b");
1 pmbaty 2337
   if (fp == NULL)
2338
   {
2339
      fprintf (stderr, "error: failed to open \"%s\" for writing (%s)\n", ifs_pathname, strerror (errno));
2340
      exit (1);
2341
   }
2342
 
2 pmbaty 2343
   // do we have a startup file ? if so, this is a bootable image
2344
   if (startupfile_pathname != NULL)
1 pmbaty 2345
   {
2346
      // write boot prefix
11 pmbaty 2347
      // ######################################################################################################################################################################################################################################
2348
      // # FIXME: figure out how to re-create it
2349
      // ######################################################################################################################################################################################################################################
2 pmbaty 2350
      fwrite_filecontents (bootfile_pathname, fp);
1 pmbaty 2351
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
2352
 
2353
      startupheader_offset = ftell (fp); // save startup header offset
2354
      memset (&startup_header, 0, sizeof (startup_header)); // prepare startup header
2355
      memcpy (startup_header.signature, "\xeb\x7e\xff\x00", 4); // startup header signature, i.e. 0xff7eeb
2356
      startup_header.version       = 1;
2357
      startup_header.flags1        = STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2; // flags, 0x21 (STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2)
2358
      startup_header.header_size   = sizeof (startup_header); // 256
2359
      if (strcmp (image_processor, "x86_64") == 0)
10 pmbaty 2360
         startup_header.machine = ELF_MACHINE_X86_64; // EM_X86_64
1 pmbaty 2361
      else if (strcmp (image_processor, "aarch64le") == 0)
10 pmbaty 2362
         startup_header.machine = ELF_MACHINE_AARCH64; // EM_AARCH64
1 pmbaty 2363
      else
2364
      {
2365
         fprintf (stderr, "fatal error: unsupported processor type '%s' found in build file \"%s\"\n", image_processor, buildfile_pathname);
2366
         exit (1);
2367
      }
2368
      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 2369
      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 2370
      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)
2371
      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)
2372
      startup_header.startup_size  = WILL_BE_FILLED_LATER;                                  // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes
2 pmbaty 2373
      startup_header.stored_size   = WILL_BE_FILLED_LATER;                                  // [I ] Size of entire image, here 0x00cd6128 (same as ram_size)
1 pmbaty 2374
      startup_header.imagefs_size  = WILL_BE_FILLED_LATER;                                  // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes
2 pmbaty 2375
      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 2376
      fwrite_or_die (&startup_header, 1, sizeof (startup_header), fp); // write startup header
1 pmbaty 2377
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
2378
 
2379
      // ######################################################################################################################################################################################################################################
11 pmbaty 2380
      // # FIXME: figure out how to re-create it:
2381
      // first: open "startup-x86" ELF file,
2382
      //        lookup section headers table (there is no program headers table in this one)
2383
      //        FIXME: figure out something in there where the result is 0x1401030 !!!
2384
      // then: call the linker: 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
2385
      // then: parse resulting ELF file, take all program segments and concatenate them --> this is the blob (FIXME: wrong?)
1 pmbaty 2386
      // ######################################################################################################################################################################################################################################
11 pmbaty 2387
#if 0 // nonworking
2388
      {
2389
         buffer_t startupfile;
2390
         elf_section_header_t *shdr_text;
2391
         size_t segment_len;
2392
FILE *control_fp = fopen ("startup.bin.MYSTRIPPED", "wb");
2393
 
2394
         if (read_filecontents ("startup.bin.UNSTRIPPED", MKIFS_PATH, &startupfile) == NULL)
2395
         {
2396
            fprintf (stderr, "fatal error: couldn't read startup-x86\n");
2397
            exit (1);
2398
         }
2399
         elf = (elf_header_t *) startupfile.bytes; // quick access to ELF header
2400
         table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); // get the number of program headers
2401
         for (table_index = 0; table_index < table_count; table_index++) // cycle through program headers
2402
         {
2403
            phdr = (elf_program_header_t *) &startupfile.bytes[ELF_GET_NUMERIC (elf, elf, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, program_header_item_size) * table_index]; // quick access to program header
2404
            segment_len = ELF_GET_NUMERIC (elf, phdr, size_in_file);
2405
            fwrite_or_die (&startupfile.bytes[ELF_GET_NUMERIC (elf, phdr, file_offset)], 1, segment_len, fp); // dump program segment
2406
fwrite_or_die (&startupfile.bytes[ELF_GET_NUMERIC (elf, phdr, file_offset)], 1, segment_len, control_fp); // dump program segment
2407
while (segment_len % 4096 > 0)
2408
{
2409
   fputc (0, control_fp);
2410
   segment_len++;
2411
}
2412
         }
2413
 
2414
fclose (control_fp);
2415
         free (startupfile.bytes);
2416
      }
2417
#else // working
1 pmbaty 2418
      fwrite_filecontents (startupfile_pathname, fp); // write startup code from blob file
11 pmbaty 2419
#endif // working
1 pmbaty 2420
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
2421
 
2422
      startuptrailer_offset = ftell (fp); // save startup trailer offset
8 pmbaty 2423
      fwrite_or_die (&startup_trailer, 1, sizeof (startup_trailer), fp); // write startup trailer
1 pmbaty 2424
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
2425
   }
2426
 
8 pmbaty 2427
   imageheader_offset = ftell (fp); // save image header offset
1 pmbaty 2428
   memset (&image_header, 0, sizeof (image_header)); // prepare image header
2429
   memcpy (&image_header.signature, "imagefs", 7); // image filesystem signature, i.e. "imagefs"
2430
   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)
2431
   image_header.image_size    = WILL_BE_FILLED_LATER; // size from header to end of trailer (here 0xca6fe0 or 13 266 912)
7 pmbaty 2432
   image_header.hdr_dir_size  = WILL_BE_FILLED_LATER; // size from header to last dirent (here 0x12b8 or 4792)
1 pmbaty 2433
   image_header.dir_offset    = sizeof (image_header); // offset from header to first dirent (here 0x5c or 92)
2434
   image_header.boot_ino[0]   = image_kernel_ino; // inode of files for bootstrap p[ro?]g[ra?]ms (here 0xa0000002, 0, 0, 0)
2435
   image_header.script_ino    = image_bootscript_ino; // inode of file for script (here 3)
2436
   image_header.mountpoint[0] = '/'; // default mountpoint for image ("/" + "\0\0\0")
8 pmbaty 2437
   fwrite_or_die (&image_header, 1, sizeof (image_header), fp); // write image header
1 pmbaty 2438
   PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
2439
 
6 pmbaty 2440
   // write image directory (with the wrong file offsets)
1 pmbaty 2441
   imgdir_offset = ftell (fp);
7 pmbaty 2442
   imgdir_size = 0; // measure image dir size on the fly
1 pmbaty 2443
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
7 pmbaty 2444
      imgdir_size += fwrite_fsentry (&fsentries[fsentry_index], fp); // NOTE: padding is handled in this function
1 pmbaty 2445
 
8 pmbaty 2446
   fwrite_or_die ("\0\0\0\0", 1, 4, fp); // there seems to be 4 bytes of padding after the image directory
2447
   imgdir_size += 4;
2448
 
1 pmbaty 2449
   // is it a bootable image with a kernel file ?
2 pmbaty 2450
   if ((startupfile_pathname != NULL) && (kernelfile_pathname != NULL))
1 pmbaty 2451
   {
7 pmbaty 2452
      // start by writing the startup script data blob, if we have one
2453
      for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
2454
         if (fsentries[fsentry_index].header.ino == image_bootscript_ino)
8 pmbaty 2455
            break; // locate the startup script directory entry
7 pmbaty 2456
      if (fsentry_index < fsentry_count) // found it ?
1 pmbaty 2457
      {
2458
         curr_offset = ftell (fp);
2459
         if (curr_offset + fsentries[fsentry_index].u.file.size >= kernelfile_offset)
7 pmbaty 2460
         {
2461
            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);
2462
            exit (1);
2463
         }
8 pmbaty 2464
         fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure
2465
         fwrite_or_die (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob
1 pmbaty 2466
         fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2467
      }
7 pmbaty 2468
 
2469
      // now write the filesystem entries that may fit before the kernel
2470
      for (;;)
2471
      {
2472
         curr_offset = ftell (fp); // see where we are
2473
         available_space = kernelfile_offset - curr_offset; // measure the available space
2474
 
2475
         // look for the biggest one that can fit
2476
         largest_index = 0;
8 pmbaty 2477
         largest_size = 0;
7 pmbaty 2478
         for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
2479
         {
2480
            if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written || (fsentries[fsentry_index].u.file.size > available_space))
2481
               continue; // skip all entries that don't have a separate data block, those who were written already and those that wouldn't fit
2482
            if (fsentries[fsentry_index].u.file.size > largest_size)
2483
            {
2484
               largest_size = fsentries[fsentry_index].u.file.size;
2485
               largest_index = fsentry_index;
2486
            }
2487
         }
8 pmbaty 2488
         if (largest_size == 0)
7 pmbaty 2489
            break; // found none ? if so, stop searching
2490
 
11 pmbaty 2491
         fsentries[largest_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure
2492
 
10 pmbaty 2493
         // is the file we're storing a preprocessed ELF file ?
11 pmbaty 2494
         if (fsentries[largest_index].header.ino & IFS_INO_PROCESSED_ELF)
10 pmbaty 2495
         {
11 pmbaty 2496
            elf = (elf_header_t *) fsentries[largest_index].u.file.UNSAVED_databuf; // quick access to ELF header
10 pmbaty 2497
            table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); // get the number of program headers
2498
            for (table_index = 0; table_index < table_count; table_index++)
2499
            {
11 pmbaty 2500
               phdr = (elf_program_header_t *) &fsentries[largest_index].u.file.UNSAVED_databuf[ELF_GET_NUMERIC (elf, elf, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, program_header_item_size) * table_index]; // quick access to program header
2501
               corrective_offset = ELF_GET_NUMERIC (elf, phdr, virtual_addr) - ELF_GET_NUMERIC (elf, phdr, file_offset);
2502
               if (ELF_GET_NUMERIC (elf, phdr, size_in_memory) != 0) // only patch the physical address of segments that have an actual size in memory
14 pmbaty 2503
                  ELF_SET_NUMERIC (elf, phdr, physical_addr, ELF_GET_NUMERIC (elf, phdr, physical_addr) + image_base + curr_offset - corrective_offset); // patch the physical address member of the program header table
10 pmbaty 2504
            }
2505
         }
2506
 
8 pmbaty 2507
         fwrite_or_die (fsentries[largest_index].u.file.UNSAVED_databuf, 1, fsentries[largest_index].u.file.size, fp); // write file data blob
7 pmbaty 2508
         fsentries[largest_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2509
      }
10 pmbaty 2510
      fprintf (stderr, "Current offset: 0x%zx\n", curr_offset);
2511
      fprintf (stderr, "Kernel file offset: 0x%zx\n", kernelfile_offset);
1 pmbaty 2512
      PAD_OUTFILE_TO (kernelfile_offset); // reach the kernel offset
2513
 
8 pmbaty 2514
      // now write the QNX kernel
2515
      for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
2516
         if (fsentries[fsentry_index].header.ino == image_kernel_ino)
2517
            break; // locate the kernel directory entry (can't fail)
2518
      curr_offset = ftell (fp); // see where we are
2519
      fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure
11 pmbaty 2520
      fwrite_or_die (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write kernel file data blob
1 pmbaty 2521
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
8 pmbaty 2522
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1 pmbaty 2523
   }
2524
 
8 pmbaty 2525
   // then write all the other files by increasing inode number: ELF files first
7 pmbaty 2526
   for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
1 pmbaty 2527
   {
8 pmbaty 2528
      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
2529
          || (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 2530
         continue; // skip all entries that don't have a separate data block and those who were written already
2531
      curr_offset = ftell (fp);
8 pmbaty 2532
      fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure
10 pmbaty 2533
 
2534
      // is the file we're storing a preprocessed ELF file ?
2535
      if (fsentries[fsentry_index].header.ino & IFS_INO_PROCESSED_ELF)
2536
      {
2537
         elf = (elf_header_t *) fsentries[fsentry_index].u.file.UNSAVED_databuf; // quick access to ELF header
2538
         table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); // get the number of program headers
2539
         for (table_index = 0; table_index < table_count; table_index++)
2540
         {
2541
            phdr = (elf_program_header_t *) &fsentries[fsentry_index].u.file.UNSAVED_databuf[ELF_GET_NUMERIC (elf, elf, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, program_header_item_size) * table_index]; // quick access to program header
11 pmbaty 2542
            corrective_offset = ELF_GET_NUMERIC (elf, phdr, virtual_addr) - ELF_GET_NUMERIC (elf, phdr, file_offset);
2543
            if (ELF_GET_NUMERIC (elf, phdr, size_in_memory) != 0) // only patch the physical address of segments that have an actual size in memory
14 pmbaty 2544
               ELF_SET_NUMERIC (elf, phdr, physical_addr, ELF_GET_NUMERIC (elf, phdr, physical_addr) + image_base + curr_offset - corrective_offset); // patch the physical address member of the program header table
10 pmbaty 2545
         }
2546
      }
2547
 
8 pmbaty 2548
      fwrite_or_die (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob
1 pmbaty 2549
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2550
   }
8 pmbaty 2551
   for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++) // other files (non-ELF, e.g. scripts and data files) last
2552
   {
2553
      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
2554
         continue; // skip all entries that don't have a separate data block and those who were written already
2555
      curr_offset = ftell (fp);
2556
      fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure
2557
      fwrite_or_die (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob
2558
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2559
   }
2560
   PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
1 pmbaty 2561
 
2562
   // finally, write trailer (including empty checksum)
8 pmbaty 2563
   imagetrailer_offset = ftell (fp); // save image trailer offset
2564
   fwrite_or_die (&image_trailer, 1, sizeof (image_trailer), fp); // write image trailer
1 pmbaty 2565
   PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
2566
 
2567
   // if we need to pad it to a specific length, do so
2568
   PAD_OUTFILE_TO (image_totalsize);
2569
   final_size = ftell (fp);
2570
 
2571
   // see if we are past the image max size, in which case it's an error
2572
   if (final_size > image_maxsize)
2573
   {
2574
      fprintf (stderr, "error: image file \"%s\" size %zd exceeds max size (%zd)\n", ifs_pathname, final_size, (size_t) image_maxsize);
2575
      exit (1);
2576
   }
2577
 
2 pmbaty 2578
   // do we have a startup file ? if so, this is a bootable image
2579
   if (startupfile_pathname != NULL)
1 pmbaty 2580
   {
2581
      // rewrite startup header with final values
8 pmbaty 2582
      fseek_or_die (fp, startupheader_offset, SEEK_SET);
2583
      startup_header.startup_size = (uint32_t) (imageheader_offset - startupheader_offset); // size of startup header up to image header
2584
      startup_header.imagefs_size = (uint32_t) (final_size - imageheader_offset); // size of uncompressed imagefs
11 pmbaty 2585
      startup_header.ram_size     = (uint32_t) (final_size - startupheader_offset);
2586
      startup_header.stored_size  = (uint32_t) (final_size - startupheader_offset);
8 pmbaty 2587
      fwrite_or_die (&startup_header, 1, sizeof (startup_header), fp); // write startup header
2588
   }
1 pmbaty 2589
 
8 pmbaty 2590
   // rewrite image header with final values
2591
   fseek_or_die (fp, imageheader_offset, SEEK_SET);
2592
   image_header.image_size = (uint32_t) (final_size - imageheader_offset); // size of uncompressed imagefs
2593
   image_header.hdr_dir_size = sizeof (image_header) + (uint32_t) imgdir_size; // size from start of image header to last dirent
2594
   fwrite_or_die (&image_header, 1, sizeof (image_header), fp); // write image header
2595
 
2596
   // rewrite image directory with final offset values
2597
   fseek_or_die (fp, imgdir_offset, SEEK_SET);
2598
   if (image_header.flags & IMAGE_FLAGS_SORTED)
2599
      qsort (&fsentries[1], fsentry_count - 1, sizeof (fsentry_t), fsentry_compare_pathnames_cb); // sort the filesystem entries by pathname
2600
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
2601
      fwrite_fsentry (&fsentries[fsentry_index], fp);
2602
 
2603
   fclose (fp); // ensure everything is flushed
2604
 
2605
   // ALL CHECKSUMS AT THE VERY END
2606
 
11 pmbaty 2607
   read_filecontents (ifs_pathname, ".", &blob);
2608
   WELLMANNERED_ASSERT (blob.bytes != NULL, "failed to open IFS file for checksumming: %s", strerror (errno));
8 pmbaty 2609
 
2610
   // do we have a startup file ? if so, this is a bootable image
2611
   if (startupfile_pathname != NULL)
2612
   {
1 pmbaty 2613
      // compute SHA-512 checksum and V1 checksum of startup block
6 pmbaty 2614
      if (   ( (startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
2615
          || (!(startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
2616
         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
2617
      else
2618
         is_foreign_endianness = false; // else this header is for the same endianness as us
1 pmbaty 2619
 
11 pmbaty 2620
      SHA512 (&blob.bytes[startupheader_offset], startuptrailer_offset - startupheader_offset, &blob.bytes[startuptrailer_offset]); // compute SHA512 checksum and write it in place in blob data
2621
      checksum = update_checksum (&blob.bytes[startupheader_offset], startuptrailer_offset + SHA512_DIGEST_LENGTH - startupheader_offset, is_foreign_endianness); // compute old checksum
2622
      memcpy (&blob.bytes[startuptrailer_offset + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place
1 pmbaty 2623
   }
2624
 
2625
   // compute SHA-512 checksum and V1 checksum of image block
5 pmbaty 2626
   if (   ( (image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
2627
       || (!(image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
2628
      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
2629
   else
2630
      is_foreign_endianness = false; // else this header is for the same endianness as us
1 pmbaty 2631
 
11 pmbaty 2632
   SHA512 (&blob.bytes[imageheader_offset], imagetrailer_offset - imageheader_offset, &blob.bytes[imagetrailer_offset]); // compute SHA512 checksum and write it in place in blob data
2633
   checksum = update_checksum (&blob.bytes[imageheader_offset], imagetrailer_offset + SHA512_DIGEST_LENGTH - imageheader_offset, is_foreign_endianness); // compute old checksum
2634
   memcpy (&blob.bytes[imagetrailer_offset + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place
1 pmbaty 2635
 
8 pmbaty 2636
   // now rewrite IFS with the correct checksums
2637
   fp = fopen (ifs_pathname, "wb");
2638
   WELLMANNERED_ASSERT (fp, "failed to reopen IFS file for checksumming: %s", strerror (errno));
11 pmbaty 2639
   fwrite_or_die (blob.bytes, 1, blob.len, fp);
1 pmbaty 2640
   fclose (fp);
11 pmbaty 2641
   free (blob.bytes);
8 pmbaty 2642
 
2643
   // finished, exit with a success code
1 pmbaty 2644
   fprintf (stdout, "Success\n");
2645
   exit (0);
2646
}
2 pmbaty 2647
 
2648
 
10 pmbaty 2649
static int dump_ifs_info (const char *ifs_pathname, bool want_everything)
2 pmbaty 2650
{
7 pmbaty 2651
   #define hex_printf(buf,size,...) do { \
10 pmbaty 2652
      if (want_everything || ((size) <= 16 * 1024)) /* only print when it's not too big (up to 16 kb) */\
7 pmbaty 2653
         hex_fprintf (stdout, (buf), (size), 16, __VA_ARGS__); /* use 16 columns in hex output to stdout */ \
2654
      else { \
2655
         printf (__VA_ARGS__); \
10 pmbaty 2656
         hex_fprintf (stdout, (buf), 1024, 16, "   first kilobyte:\n"); \
7 pmbaty 2657
      } \
2658
   } while (0)
2 pmbaty 2659
   #define BINARY(x) binary ((x), '-', 'x')
2660
 
2661
   static const char *startupheader_flags1_strings[8] = {
2662
      "VIRTUAL", // bit 0
2663
      "BIGENDIAN", // bit 1
2664
      "COMPRESS_BIT1", // bit 2
2665
      "COMPRESS_BIT2", // bit 3
2666
      "COMPRESS_BIT3", // bit 4
2667
      "TRAILER_V2", // bit 5
2668
      "", // bit 6
2669
      "", // bit 7
2670
   };
2671
   static const char *imageheader_flags_strings[8] = {
2672
      "BIGENDIAN", // bit 0
2673
      "READONLY", // bit 1
2674
      "INO_BITS", // bit 2
2675
      "SORTED", // bit 3
2676
      "TRAILER_V2", // bit 4
2677
      "", // bit 5
2678
      "", // bit 6
2679
      "", // bit 7
2680
   };
2681
 
2682
   startup_header_t *startup_header = NULL;
8 pmbaty 2683
   size_t startupheader_offset = 0;
2 pmbaty 2684
   startup_trailer_v1_t *startup_trailer_v1 = NULL;
2685
   startup_trailer_v2_t *startup_trailer_v2 = NULL;
8 pmbaty 2686
   size_t startuptrailer_offset = 0;
2 pmbaty 2687
   image_header_t *image_header = NULL;
2688
   size_t imageheader_offset = 0;
2689
   image_trailer_v1_t *image_trailer_v1 = NULL;
2690
   image_trailer_v2_t *image_trailer_v2 = NULL;
3 pmbaty 2691
   size_t imagetrailer_offset = 0;
2692
   fsentry_t **fsentries = NULL; // mallocated
2693
   size_t fsentry_count = 0;
2 pmbaty 2694
   fsentry_t *current_fsentry = NULL;
2695
   char recorded_sha512[2 * SHA512_DIGEST_LENGTH + 1] = "";
2696
   char computed_sha512[2 * SHA512_DIGEST_LENGTH + 1] = "";
2697
   size_t startupfile_blobsize = 0;
3 pmbaty 2698
   void *reallocated_ptr;
5 pmbaty 2699
   bool is_foreign_endianness;
2 pmbaty 2700
   size_t bootfile_blobsize = 0;
2701
   size_t current_offset;
3 pmbaty 2702
   size_t fsentry_index;
2703
   size_t nearest_distance;
2704
   size_t nearest_index;
2 pmbaty 2705
   size_t byte_index;
2706
   uint32_t recorded_checksum;
2707
   uint32_t computed_checksum;
11 pmbaty 2708
   buffer_t file;
2 pmbaty 2709
   time_t mtime;
2710
 
2711
   // open and read IFS file
11 pmbaty 2712
   if (read_filecontents (ifs_pathname, ".", &file) == NULL)
2 pmbaty 2713
   {
2714
      fprintf (stderr, "error: can't open \"%s\" for reading: %s\n", ifs_pathname, strerror (errno));
2715
      return (1);
2716
   }
2717
 
4 pmbaty 2718
   printf ("QNX In-kernel Filesystem analysis produced by ifstool version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD);
11 pmbaty 2719
   printf ("IFS file \"%s\" - size 0x%zx (%zd) bytes\n", ifs_pathname, file.len, file.len);
2 pmbaty 2720
 
2721
   // parse file from start to end
2722
   current_offset = 0;
2723
   for (;;)
2724
   {
2725
      // does a startup header start here ?
11 pmbaty 2726
      if ((current_offset + sizeof (startup_header_t) < file.len) && (memcmp (&file.bytes[current_offset], "\xeb\x7e\xff\x00", 4) == 0))
2 pmbaty 2727
      {
8 pmbaty 2728
         startupheader_offset = current_offset;
11 pmbaty 2729
         startup_header = (startup_header_t *) &file.bytes[startupheader_offset];
2 pmbaty 2730
 
2731
         // layout:
2732
         // [STARTUP HEADER]
2733
         // (startup file blob)
2734
         // [STARTUP TRAILER v1 or v2]
2735
 
2736
         printf ("\n");
2737
         printf ("Startup header at offset 0x%zx (%zd):\n", current_offset, current_offset);
2738
         printf ("   signature     = %02x %02x %02x %02x - good\n", startup_header->signature[0], startup_header->signature[1], startup_header->signature[2], startup_header->signature[3]);
2739
         printf ("   version       = 0x%04x (%d) - %s\n", startup_header->version, startup_header->version, (startup_header->version == 1 ? "looks good" : "???"));
2740
         printf ("   flags1        = 0x%02x (%s)\n", startup_header->flags1, describe_uint8 (startup_header->flags1, startupheader_flags1_strings));
2741
         printf ("   flags2        = 0x%02x (%s) - %s\n", startup_header->flags2, BINARY (startup_header->flags2), (startup_header->flags2 == 0 ? "looks good" : "???"));
2742
         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"));
10 pmbaty 2743
         printf ("   machine       = 0x%04x (%d) - %s\n", startup_header->machine, startup_header->machine, (startup_header->machine == ELF_MACHINE_X86_64 ? "x86_64" : (startup_header->machine == ELF_MACHINE_AARCH64 ? "aarch64" : "unknown")));
2 pmbaty 2744
         printf ("   startup_vaddr = 0x%08x (%d) - virtual address to transfer to after IPL is done\n", startup_header->startup_vaddr, startup_header->startup_vaddr);
2745
         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);
2746
         printf ("   image_paddr   = 0x%08x (%d) - physical address of image\n", startup_header->image_paddr, startup_header->image_paddr);
2747
         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);
2748
         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);
11 pmbaty 2749
         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)) < file.len ? "looks good" : "BAD (IFS file too short)"));
2 pmbaty 2750
         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" : "???"));
2751
         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"));
2752
         printf ("   imagefs_size  = 0x%08x (%d) - size of uncompressed imagefs\n", startup_header->imagefs_size, startup_header->imagefs_size);
2753
         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" : "???"));
2754
         printf ("   zero0         = 0x%04x (%d) - zeros - %s\n", startup_header->zero0, startup_header->zero0, (startup_header->zero0 == 0 ? "looks good" : "??? should be zero"));
2755
         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"));
2756
         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"));
2757
         hex_printf ((uint8_t *) &startup_header->info[0], sizeof (startup_header->info), "   info[48] =\n");
2758
 
2759
         // validate that the file can contain up to the startup trailer
11 pmbaty 2760
         if (current_offset + startup_header->startup_size > file.len)
2 pmbaty 2761
         {
7 pmbaty 2762
            printf ("WARNING: this IFS file is corrupted (startup trailer extends past end of file)\n");
2763
            goto endofdata;
2 pmbaty 2764
         }
2765
 
5 pmbaty 2766
         // check if this endianness is ours
2767
         if (   ( (startup_header->flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
2768
             || (!(startup_header->flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
2769
            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
2770
         else
2771
            is_foreign_endianness = false; // else this header is for the same endianness as us
2772
 
2 pmbaty 2773
         // locate the right startup trailer at the right offset
2774
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
2775
         {
8 pmbaty 2776
            startuptrailer_offset = current_offset + startup_header->startup_size - sizeof (startup_trailer_v2_t);
11 pmbaty 2777
            startup_trailer_v2 = (startup_trailer_v2_t *) &file.bytes[startuptrailer_offset];
2 pmbaty 2778
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v2_t);
2779
         }
2780
         else // old V1 trailer
2781
         {
8 pmbaty 2782
            startuptrailer_offset = current_offset + startup_header->startup_size - sizeof (startup_trailer_v1_t);
11 pmbaty 2783
            startup_trailer_v1 = (startup_trailer_v1_t *) &file.bytes[startuptrailer_offset];
2 pmbaty 2784
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v1_t);
2785
         }
2786
 
2787
         current_offset += sizeof (startup_header_t); // jump over the startup header and reach the startup blob
2788
         printf ("\n");
2789
         printf ("Startup blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
2790
         printf ("   size 0x%zx (%zd) bytes\n", startupfile_blobsize, startupfile_blobsize);
11 pmbaty 2791
         printf ("   checksum %d\n", update_checksum (&file.bytes[current_offset], startupfile_blobsize, is_foreign_endianness));
2 pmbaty 2792
 
2793
         current_offset += startupfile_blobsize; // jump over the startup blob and reach the startup trailer
2794
         printf ("\n");
2795
         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));
2796
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
2797
         {
2798
            for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
2799
               sprintf (&recorded_sha512[2 * byte_index], "%02x", startup_trailer_v2->sha512[byte_index]);
8 pmbaty 2800
            strcpy (computed_sha512, SHA512 (startup_header, startuptrailer_offset - startupheader_offset, NULL));
2 pmbaty 2801
            recorded_checksum = startup_trailer_v2->cksum;
8 pmbaty 2802
            computed_checksum = update_checksum (startup_header, startuptrailer_offset + SHA512_DIGEST_LENGTH - startupheader_offset, is_foreign_endianness);
2803
            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 2804
            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 2805
            if (strcasecmp (computed_sha512, recorded_sha512) != 0)
2806
               printf ("Computed SHA-512: %s\n", computed_sha512);
2807
            if (computed_checksum != recorded_checksum)
9 pmbaty 2808
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
2 pmbaty 2809
         }
2810
         else // old v1 trailer
2811
         {
2812
            recorded_checksum = startup_trailer_v1->cksum;
7 pmbaty 2813
            computed_checksum = update_checksum (startup_header, sizeof (startup_header) + startupfile_blobsize, is_foreign_endianness);
9 pmbaty 2814
            printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", startupheader_offset, startuptrailer_offset, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
2 pmbaty 2815
            if (computed_checksum != recorded_checksum)
9 pmbaty 2816
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
2 pmbaty 2817
         }
2818
 
3 pmbaty 2819
         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 2820
      }
2821
 
2822
      // else does an image header start here ?
11 pmbaty 2823
      else if ((current_offset + sizeof (image_header_t) < file.len) && (memcmp (&file.bytes[current_offset], "imagefs", 7) == 0))
2 pmbaty 2824
      {
2825
         imageheader_offset = current_offset;
11 pmbaty 2826
         image_header = (image_header_t *) &file.bytes[imageheader_offset];
2 pmbaty 2827
 
2828
         // layout:
2829
         // [IMAGE HEADER]
2830
         // [image directory entries]
6 pmbaty 2831
         // [smallest file blobs up to KERNEL]
2 pmbaty 2832
         // [padding]
2833
         // [KERNEL]
6 pmbaty 2834
         // [rest of file blobs]
2 pmbaty 2835
         // [IMAGE FOOTER]
2836
 
2837
         printf ("\n");
2838
         printf ("Image header at offset %zx (%zd):\n", current_offset, current_offset);
2839
         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);
2840
         printf ("   flags        = 0x%02x (%s)\n", image_header->flags, describe_uint8 (image_header->flags, imageheader_flags_strings));
11 pmbaty 2841
         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 <= file.len ? "looks good" : "BAD (IFS file too short)"));
2842
         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 < file.len ? "looks good" : "BAD (IFS file too short)"));
2843
         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 >= file.len ? "BAD (IFS file too short)" : (image_header->dir_offset > image_header->hdr_dir_size ? "BAD" : "looks good")));
2 pmbaty 2844
         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]);
2845
         printf ("   script_ino   = 0x%08x (%d) - inode of compiled bootscript\n", image_header->script_ino, image_header->script_ino);
2846
         printf ("   chain_paddr  = 0x%08x (%d) - offset to next fs signature\n", image_header->chain_paddr, image_header->chain_paddr);
2847
         hex_printf ((uint8_t *) &image_header->spare[0], sizeof (image_header->spare), "   spare[10] =\n");
2848
         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]));
2849
         printf ("   mountpoint   = \"%s\"\n", image_header->mountpoint);
2850
 
2851
         // validate that the file can contain up to the image trailer
11 pmbaty 2852
         if (current_offset + image_header->image_size > file.len)
2 pmbaty 2853
         {
7 pmbaty 2854
            printf ("WARNING: this IFS file is corrupted (image trailer extends past end of file)\n");
2855
            goto endofdata;
2 pmbaty 2856
         }
2857
 
5 pmbaty 2858
         // check if this endianness is ours
2859
         if (   ( (image_header->flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
2860
             || (!(image_header->flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
2861
            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
2862
         else
2863
            is_foreign_endianness = false; // else this header is for the same endianness as us
2864
 
2 pmbaty 2865
         // locate the image trailer at the right offset
2866
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
3 pmbaty 2867
         {
2868
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v2_t);
11 pmbaty 2869
            image_trailer_v2 = (image_trailer_v2_t *) &file.bytes[imagetrailer_offset];
3 pmbaty 2870
         }
2 pmbaty 2871
         else // old V1 trailer
3 pmbaty 2872
         {
2873
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v1_t);
11 pmbaty 2874
            image_trailer_v1 = (image_trailer_v1_t *) &file.bytes[imagetrailer_offset];
3 pmbaty 2875
         }
2 pmbaty 2876
 
2877
         current_offset += sizeof (image_header_t); // jump over the image header and reach the first directory entry
3 pmbaty 2878
 
2879
         // there may be padding before the first directory entry
2 pmbaty 2880
         if (image_header->dir_offset - sizeof (image_header_t) > 0)
11 pmbaty 2881
            hex_printf (&file.bytes[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 2882
         current_offset += image_header->dir_offset - sizeof (image_header_t); // padding was processed, jump over it
2 pmbaty 2883
 
2884
         // dump all directory entries until the last one included
3 pmbaty 2885
         fsentries = NULL;
2886
         fsentry_count = 0;
6 pmbaty 2887
         while (current_offset < imageheader_offset + image_header->hdr_dir_size)
2 pmbaty 2888
         {
11 pmbaty 2889
            current_fsentry = (fsentry_t *) &file.bytes[current_offset];
2 pmbaty 2890
 
2891
            if (imageheader_offset + image_header->hdr_dir_size - current_offset < sizeof (current_fsentry->header))
2892
               break; // end padding reached
2893
 
3 pmbaty 2894
            // stack up the filesystem entry pointers in an array while we read them
2895
            reallocated_ptr = realloc (fsentries, (fsentry_count + 1) * sizeof (fsentry_t *));
8 pmbaty 2896
            WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
3 pmbaty 2897
            fsentries = reallocated_ptr;
2898
            fsentries[fsentry_count] = current_fsentry;
2899
            fsentry_count++;
2900
 
2 pmbaty 2901
            printf ("\n");
2902
            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);
11 pmbaty 2903
            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 < file.len) ? "looks good" : "BAD"));
2 pmbaty 2904
            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"));
2905
            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" : ""));
2906
            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);
2907
            printf ("   gid            = 0x%08x (%d) - owner group ID%s\n", current_fsentry->header.gid, current_fsentry->header.gid, (current_fsentry->header.gid == 0 ? " (root)" : ""));
2908
            printf ("   uid            = 0x%08x (%d) - owner user ID%s\n", current_fsentry->header.uid, current_fsentry->header.uid, (current_fsentry->header.uid == 0 ? " (root)" : ""));
2909
            mtime = (time_t) current_fsentry->header.mtime;
2910
            printf ("   mtime          = 0x%08x (%d) - POSIX timestamp: %s", current_fsentry->header.mtime, current_fsentry->header.mtime, asctime (localtime (&mtime))); // NOTE: asctime() provides the newline
2911
            if (S_ISDIR (current_fsentry->header.mode))
2912
               printf ("   [DIRECTORY] path = \"%s\"\n", (char *) &current_fsentry->u.dir.path); // convert from pointer to char array
2913
            else if (S_ISREG (current_fsentry->header.mode))
2914
            {
11 pmbaty 2915
               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 < file.len ? "looks good" : "BAD (IFS file too short)"));
2916
               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 < file.len ? "looks good" : "BAD (IFS file too short)"));
2 pmbaty 2917
               printf ("   [FILE] path   = \"%s\"\n", (char *) &current_fsentry->u.file.path); // convert from pointer to char array
2918
            }
2919
            else if (S_ISLNK (current_fsentry->header.mode))
2920
            {
2921
               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 2922
               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 2923
               printf ("   [SYMLINK] path       = \"%s\"\n", (char *) &current_fsentry->u.symlink.path); // convert from pointer to char array
2924
               printf ("   [SYMLINK] contents   = \"%s\"\n", ((char *) &current_fsentry->u.symlink.path) + current_fsentry->u.symlink.sym_offset); // convert from pointer to char array
2925
            }
2926
            else // can only be a device
2927
            {
2928
               printf ("   [DEVICE] dev  = 0x%08x (%d)\n", current_fsentry->u.device.dev, current_fsentry->u.device.dev);
2929
               printf ("   [DEVICE] rdev = 0x%08x (%d)\n", current_fsentry->u.device.rdev, current_fsentry->u.device.rdev);
2930
               printf ("   [DEVICE] path = \"%s\"\n", (char *) &current_fsentry->u.device.path); // convert from pointer to char array
2931
            }
2932
 
11 pmbaty 2933
            if ((current_fsentry->header.size == 0) || (current_offset + current_fsentry->header.size >= file.len))
7 pmbaty 2934
            {
2935
               printf ("WARNING: this IFS file is corrupted (the size of this directory entry is invalid)\n");
2936
               goto endofdata;
2937
            }
2938
 
2 pmbaty 2939
            current_offset += current_fsentry->header.size;
2940
         }
3 pmbaty 2941
         if (imageheader_offset + image_header->hdr_dir_size < current_offset + sizeof (current_fsentry->header))
11 pmbaty 2942
            hex_printf (&file.bytes[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 2943
         current_offset += imageheader_offset + image_header->hdr_dir_size - current_offset; // padding was processed, jump over it
2 pmbaty 2944
 
3 pmbaty 2945
         // at this point we are past the directory entries; what is stored now, up to and until the image trailer, is the files' data
2946
         if (fsentry_count > 0)
2947
         {
2948
            while (current_offset < imagetrailer_offset) // and parse data up to the trailer
2949
            {
2950
               nearest_distance = SIZE_MAX;
2951
               nearest_index = SIZE_MAX;
2952
               for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
2953
                  if (S_ISREG (fsentries[fsentry_index]->header.mode) // if this directory entry a file (i.e. it has a data blob)...
2954
                      && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset >= current_offset) // ... AND its data blob is still ahead of our current pointer ...
2955
                      && (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
2956
                  {
2957
                     nearest_distance = imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset; // then remember it
2958
                     nearest_index = fsentry_index;
2959
                  }
2960
               if (nearest_index == SIZE_MAX)
2961
                  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 2962
 
3 pmbaty 2963
               fsentry_index = nearest_index;
2964
               current_fsentry = fsentries[fsentry_index]; // quick access to closest fsentry
2 pmbaty 2965
 
3 pmbaty 2966
               // there may be padding before the file data
2967
               if (imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset > 0)
11 pmbaty 2968
                  hex_printf (&file.bytes[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 2969
               current_offset += imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset; // padding was processed, jump over it
2970
 
2971
               printf ("\n");
2972
               printf ("File data blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
2973
               printf ("   corresponding dirent index: %zd/%zd\n", fsentry_index, fsentry_count);
6 pmbaty 2974
               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 2975
               printf ("   corresponding path: \"%s\"\n", (char *) &current_fsentry->u.file.path); // convert from pointer to char array
2976
               printf ("   size 0x%zx (%zd) bytes\n", (size_t) current_fsentry->u.file.size, (size_t) current_fsentry->u.file.size);
11 pmbaty 2977
               if (current_offset + 4 < file.len)
2978
                  hex_printf (&file.bytes[current_offset], current_fsentry->u.file.size, "   data:\n");
2979
               if (current_offset + current_fsentry->u.file.size < file.len)
2980
                  printf ("   checksum %d\n", update_checksum (&file.bytes[current_offset], current_fsentry->u.file.size, is_foreign_endianness));
7 pmbaty 2981
               else
2982
               {
2983
                  printf ("WARNING: this IFS file is corrupted (the size of this file data extends past the IFS size)\n");
2984
                  goto endofdata;
2985
               }
3 pmbaty 2986
 
2987
               current_offset += current_fsentry->u.file.size; // now jump over this file's data
2988
            }
2989
         }
2990
 
2991
         // ad this point we're past the last file data, there may be padding before the image trailer
2992
         if (imagetrailer_offset - current_offset > 0)
11 pmbaty 2993
            hex_printf (&file.bytes[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 2994
         current_offset += imagetrailer_offset - current_offset; // padding was processed, jump over it
2995
 
2996
         printf ("\n");
2997
         printf ("Image trailer at offset 0x%zx (%zd) - version %d:\n", current_offset, current_offset, (image_header->flags & IMAGE_FLAGS_TRAILER_V2 ? 2 : 1));
2998
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
2999
         {
3000
            for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
3001
               sprintf (&recorded_sha512[2 * byte_index], "%02x", image_trailer_v2->sha512[byte_index]);
8 pmbaty 3002
            strcpy (computed_sha512, SHA512 (image_header, imagetrailer_offset - imageheader_offset, NULL));
3 pmbaty 3003
            recorded_checksum = image_trailer_v2->cksum;
8 pmbaty 3004
            computed_checksum = update_checksum (image_header, imagetrailer_offset + SHA512_DIGEST_LENGTH - imageheader_offset, is_foreign_endianness);
3005
            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 3006
            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 3007
            if (strcasecmp (computed_sha512, recorded_sha512) != 0)
3008
               printf ("Computed SHA-512: %s\n", computed_sha512);
3009
            if (computed_checksum != recorded_checksum)
9 pmbaty 3010
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
3 pmbaty 3011
         }
3012
         else // old v1 trailer
3013
         {
3014
            recorded_checksum = image_trailer_v1->cksum;
7 pmbaty 3015
            computed_checksum = update_checksum (image_header, image_header->image_size - sizeof (image_trailer_v1_t), is_foreign_endianness);
9 pmbaty 3016
            printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", imageheader_offset, imagetrailer_offset, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
3 pmbaty 3017
            if (computed_checksum != recorded_checksum)
9 pmbaty 3018
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
3 pmbaty 3019
         }
3020
 
3021
         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 3022
      }
3023
 
3024
      // 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
3025
      else
3026
      {
3027
         // 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)
11 pmbaty 3028
         for (byte_index = current_offset; byte_index < file.len - 6; byte_index++)
3029
            if (memcmp (&file.bytes[byte_index], "\xeb\x7e\xff\x00" "\x01\x00", 4 + 2) == 0)
2 pmbaty 3030
               break; // stop as soon as we find it
3031
 
11 pmbaty 3032
         if (byte_index >= file.len - 6)
2 pmbaty 3033
            break; // if not found, stop scanning
3034
 
3035
         bootfile_blobsize = byte_index - current_offset;
3036
         printf ("Boot blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
3037
         printf ("   size 0x%zx (%zd) bytes\n", bootfile_blobsize, bootfile_blobsize);
11 pmbaty 3038
         printf ("   checksum 0x%08x\n", update_checksum (&file.bytes[current_offset], bootfile_blobsize, false)); // NOTE: endianness is not known yet -- assume same
2 pmbaty 3039
 
3040
         current_offset = byte_index; // now reach the next segment
3041
      }
3042
   }
3043
 
7 pmbaty 3044
endofdata:
3 pmbaty 3045
   // at this point there's nothing left we're able to parse
11 pmbaty 3046
   if (current_offset < file.len)
3 pmbaty 3047
   {
3048
      printf ("End of identifiable data reached.\n");
11 pmbaty 3049
      hex_printf (&file.bytes[current_offset], file.len - current_offset, "\n" "%zd extra bytes at offset %zx (%zd):\n", file.len - current_offset, current_offset, current_offset);
3 pmbaty 3050
   }
2 pmbaty 3051
 
11 pmbaty 3052
   printf ("End of file reached at offset 0x%zx (%zd)\n", file.len, file.len);
3 pmbaty 3053
   printf ("IFS dissecation complete.\n");
2 pmbaty 3054
   return (0);
3 pmbaty 3055
}
14 pmbaty 3056
 
3057
 
3058
static int create_intermediate_dirs (const char *file_pathname)
3059
{
3060
   // creates all intermediate directories from root (or cwd) up to file_path
3061
 
3062
   char *temp_pathname;
3063
   char *separator;
3064
   size_t string_index;
3065
   size_t length;
3066
 
3067
   temp_pathname = strdup (file_pathname); // have a working copy of file_pathname
3068
   if (temp_pathname == NULL)
3069
      return (-1); // on strdup() failure, return an error value (errno is set)
3070
   length = strlen (temp_pathname);
3071
   for (string_index = length - 1; string_index != SIZE_MAX; string_index--) // i.e. loop until it overflows
3072
      if ((temp_pathname[string_index] == '/') || (temp_pathname[string_index] == '\\'))
3073
         break; // look for the last directory separator and stop as soon as we find it
3074
   if (string_index != SIZE_MAX)
3075
   {
3076
      for (; string_index < length; string_index++)
3077
         temp_pathname[string_index] = 0; // if we found one, break there so as to have just the path and clear the rest of the string
3078
      separator = strtok (&temp_pathname[1], "/\\"); // for each separator in the remaining string past the first one...
3079
      while (separator != NULL)
3080
      {
3081
         (void) mkdir (temp_pathname, 0755); // create directories recursively
3082
         temp_pathname[strlen (temp_pathname)] = '/'; // put the separator back
3083
         separator = strtok (NULL, "/\\"); // and look for the next one
3084
      }
3085
   }
3086
 
3087
   free (temp_pathname); // release our working copy of file_pathname
3088
   return (0);
3089
}
3090
 
3091
 
3092
static int dump_ifs_contents (const char *ifs_pathname, const char *outdir)
3093
{
3094
   static char outfile_pathname[MAXPATHLEN] = "";
3095
 
3096
   startup_header_t *startup_header = NULL;
3097
   size_t startupheader_offset = 0;
3098
   image_header_t *image_header = NULL;
3099
   size_t imageheader_offset = 0;
3100
   size_t imagetrailer_offset = 0;
3101
   fsentry_t **fsentries = NULL; // mallocated
3102
   size_t fsentry_count = 0;
3103
   fsentry_t *current_fsentry = NULL;
3104
   size_t startupfile_blobsize = 0;
3105
   struct utimbuf file_times = { 0, 0 };
3106
   void *reallocated_ptr;
3107
   size_t bootfile_blobsize = 0;
3108
   size_t current_offset;
3109
   size_t fsentry_index;
3110
   size_t nearest_distance;
3111
   size_t nearest_index;
3112
   size_t byte_index;
3113
   buffer_t file;
3114
   FILE *fp;
3115
 
3116
   // open and read IFS file
3117
   if (read_filecontents (ifs_pathname, ".", &file) == NULL)
3118
   {
3119
      fprintf (stderr, "error: can't open \"%s\" for reading: %s\n", ifs_pathname, strerror (errno));
3120
      return (1);
3121
   }
3122
 
3123
   // create the output directory
3124
   create_intermediate_dirs (outdir);
3125
   (void) mkdir (outdir, 0755);
3126
 
3127
   // parse file from start to end
3128
   current_offset = 0;
3129
   for (;;)
3130
   {
3131
      // does a startup header start here ?
3132
      if ((current_offset + sizeof (startup_header_t) < file.len) && (memcmp (&file.bytes[current_offset], "\xeb\x7e\xff\x00", 4) == 0))
3133
      {
3134
         startupheader_offset = current_offset;
3135
         startup_header = (startup_header_t *) &file.bytes[startupheader_offset];
3136
 
3137
         // layout:
3138
         // [STARTUP HEADER]
3139
         // (startup file blob)
3140
         // [STARTUP TRAILER v1 or v2]
3141
 
3142
         // validate that the file can contain up to the startup trailer
3143
         if (current_offset + startup_header->startup_size > file.len)
3144
         {
3145
            printf ("WARNING: this IFS file is corrupted (startup trailer extends past end of file)\n");
3146
            goto endofdata;
3147
         }
3148
 
3149
         // locate the right startup trailer at the right offset
3150
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
3151
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v2_t);
3152
         else // old V1 trailer
3153
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v1_t);
3154
 
3155
         current_offset += sizeof (startup_header_t); // jump over the startup header and reach the startup blob
3156
 
3157
         // write startup blob
3158
         sprintf (outfile_pathname, "%s/STARTUP.BLOB", outdir);
3159
         fp = fopen (outfile_pathname, "wb");
3160
         WELLMANNERED_ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno));
3161
         fwrite (&file.bytes[current_offset], 1, startupfile_blobsize, fp);
3162
         fclose (fp);
3163
 
3164
         current_offset += startupfile_blobsize; // jump over the startup blob and reach the startup trailer
3165
         current_offset += (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2 ? sizeof (startup_trailer_v2_t) : sizeof (startup_trailer_v1_t)); // jump over the startup trailer and reach the next segment
3166
      }
3167
 
3168
      // else does an image header start here ?
3169
      else if ((current_offset + sizeof (image_header_t) < file.len) && (memcmp (&file.bytes[current_offset], "imagefs", 7) == 0))
3170
      {
3171
         imageheader_offset = current_offset;
3172
         image_header = (image_header_t *) &file.bytes[imageheader_offset];
3173
 
3174
         // layout:
3175
         // [IMAGE HEADER]
3176
         // [image directory entries]
3177
         // [smallest file blobs up to KERNEL]
3178
         // [padding]
3179
         // [KERNEL]
3180
         // [rest of file blobs]
3181
         // [IMAGE FOOTER]
3182
 
3183
         // validate that the file can contain up to the image trailer
3184
         if (current_offset + image_header->image_size > file.len)
3185
         {
3186
            printf ("WARNING: this IFS file is corrupted (image trailer extends past end of file)\n");
3187
            goto endofdata;
3188
         }
3189
 
3190
         // locate the image trailer at the right offset
3191
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
3192
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v2_t);
3193
         else // old V1 trailer
3194
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v1_t);
3195
 
3196
         current_offset += sizeof (image_header_t); // jump over the image header
3197
         current_offset += image_header->dir_offset - sizeof (image_header_t); // jump over possible padding
3198
 
3199
         // dump all directory entries until the last one included
3200
         fsentries = NULL;
3201
         fsentry_count = 0;
3202
         while (current_offset < imageheader_offset + image_header->hdr_dir_size)
3203
         {
3204
            current_fsentry = (fsentry_t *) &file.bytes[current_offset];
3205
 
3206
            if (imageheader_offset + image_header->hdr_dir_size - current_offset < sizeof (current_fsentry->header))
3207
               break; // end padding reached
3208
 
3209
            // stack up the filesystem entry pointers in an array while we read them
3210
            reallocated_ptr = realloc (fsentries, (fsentry_count + 1) * sizeof (fsentry_t *));
3211
            WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
3212
            fsentries = reallocated_ptr;
3213
            fsentries[fsentry_count] = current_fsentry;
3214
            fsentry_count++;
3215
 
3216
            if ((current_fsentry->header.size == 0) || (current_offset + current_fsentry->header.size >= file.len))
3217
            {
3218
               printf ("WARNING: this IFS file is corrupted (the size of this directory entry is invalid)\n");
3219
               goto endofdata;
3220
            }
3221
 
3222
            current_offset += current_fsentry->header.size;
3223
         }
3224
         current_offset += imageheader_offset + image_header->hdr_dir_size - current_offset; // jump over possible padding
3225
 
3226
         // at this point we are past the directory entries; what is stored now, up to and until the image trailer, is the files' data
3227
         if (fsentry_count > 0)
3228
         {
3229
            while (current_offset < imagetrailer_offset) // and parse data up to the trailer
3230
            {
3231
               nearest_distance = SIZE_MAX;
3232
               nearest_index = SIZE_MAX;
3233
               for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
3234
                  if (S_ISREG (fsentries[fsentry_index]->header.mode) // if this directory entry a file (i.e. it has a data blob)...
3235
                      && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset >= current_offset) // ... AND its data blob is still ahead of our current pointer ...
3236
                      && (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
3237
                  {
3238
                     nearest_distance = imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset; // then remember it
3239
                     nearest_index = fsentry_index;
3240
                  }
3241
               if (nearest_index == SIZE_MAX)
3242
                  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
3243
 
3244
               fsentry_index = nearest_index;
3245
               current_fsentry = fsentries[fsentry_index]; // quick access to closest fsentry
3246
 
3247
               current_offset += imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset; // jump over possible padding
3248
 
3249
               if (current_offset + current_fsentry->u.file.size >= file.len)
3250
               {
3251
                  printf ("WARNING: this IFS file is corrupted (the size of this file data extends past the IFS size)\n");
3252
                  goto endofdata;
3253
               }
3254
 
3255
               // write filesystem data entry
3256
               if (S_ISDIR (current_fsentry->header.mode))
3257
               {
3258
                  sprintf (outfile_pathname, "%s/%s", outdir, (char *) &current_fsentry->u.dir.path); // convert from pointer to char array
3259
                  create_intermediate_dirs (outfile_pathname);
3260
                  (void) mkdir (outfile_pathname, current_fsentry->header.mode & 0777);
3261
               }
3262
               else if (S_ISLNK (current_fsentry->header.mode))
3263
               {
3264
                  sprintf (outfile_pathname, "%s/%s", outdir, (char *) &current_fsentry->u.symlink.path); // convert from pointer to char array
3265
                  create_intermediate_dirs (outfile_pathname);
3266
#ifdef _WIN32
3267
                  fp = fopen (outfile_pathname, "wb"); // on Windows create symlinks as plain files
3268
                  WELLMANNERED_ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno));
3269
                  fwrite ((char *) &current_fsentry->u.symlink.path + current_fsentry->u.symlink.sym_offset, 1, current_fsentry->u.symlink.sym_size, fp); // convert from pointer to char array
3270
                  fclose (fp);
3271
#else // !_WIN32, thus POSIX
3272
                  symlink (current_fsentry->u.symlink.contents, outfile_pathname); // on UNIX systems, just create the symlink for real
3273
#endif // _WIN32
3274
               }
3275
               else if (S_ISREG (current_fsentry->header.mode))
3276
               {
3277
                  sprintf (outfile_pathname, "%s/%s", outdir, (char *) &current_fsentry->u.file.path); // convert from pointer to char array
3278
                  create_intermediate_dirs (outfile_pathname);
3279
                  fp = fopen (outfile_pathname, "wb"); // on Windows create symlinks as plain files
3280
                  WELLMANNERED_ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno));
3281
                  fwrite (&file.bytes[current_offset], 1, current_fsentry->u.file.size, fp);
3282
                  fclose (fp);
3283
               }
3284
               else // must be a device node. Since we might not be the super-user and/or on Win32, create plain file with "X:Y" as data
3285
               {
3286
                  sprintf (outfile_pathname, "%s/%s", outdir, (char *) &current_fsentry->u.device.path); // convert from pointer to char array
3287
                  create_intermediate_dirs (outfile_pathname);
3288
                  fp = fopen (outfile_pathname, "wb"); // on Windows create symlinks as plain files
3289
                  WELLMANNERED_ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno));
3290
                  fprintf (fp, "%u:%u", current_fsentry->u.device.dev, current_fsentry->u.device.rdev);
3291
                  fclose (fp);
3292
               }
3293
 
3294
               // set created file mtime
3295
               file_times.actime = current_fsentry->header.mtime;
3296
               file_times.modtime = current_fsentry->header.mtime;
3297
               utime (outfile_pathname, &file_times);
3298
 
3299
               // set created file mode
3300
               (void) chmod (outfile_pathname, current_fsentry->header.mode & 0777);
3301
 
3302
               current_offset += current_fsentry->u.file.size; // now jump over this file's data
3303
            }
3304
         }
3305
 
3306
         // ad this point we're past the last file data, there may be padding before the image trailer
3307
         current_offset += imagetrailer_offset - current_offset; // jump over possible padding and reach the image trailer
3308
         current_offset += (image_header->flags & IMAGE_FLAGS_TRAILER_V2 ? sizeof (image_trailer_v2_t) : sizeof (image_trailer_v1_t)); // now jump over the image trailer and reach the next segment (typically end of file)
3309
      }
3310
 
3311
      // 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
3312
      else
3313
      {
3314
         // 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)
3315
         for (byte_index = current_offset; byte_index < file.len - 6; byte_index++)
3316
            if (memcmp (&file.bytes[byte_index], "\xeb\x7e\xff\x00" "\x01\x00", 4 + 2) == 0)
3317
               break; // stop as soon as we find it
3318
 
3319
         if (byte_index >= file.len - 6)
3320
            break; // if not found, stop scanning
3321
 
3322
         bootfile_blobsize = byte_index - current_offset;
3323
 
3324
         // write boot blob
3325
         sprintf (outfile_pathname, "%s/BOOT.BLOB", outdir);
3326
         fp = fopen (outfile_pathname, "wb");
3327
         WELLMANNERED_ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno));
3328
         fwrite (&file.bytes[current_offset], 1, bootfile_blobsize, fp);
3329
         fclose (fp);
3330
 
3331
         current_offset = byte_index; // now reach the next segment
3332
      }
3333
   }
3334
 
3335
endofdata:
3336
   return (0);
3337
}