Subversion Repositories QNX 8.QNX8 IFS tool

Rev

Rev 24 | Rev 26 | 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
 
16 pmbaty 6
// standard C includes
1 pmbaty 7
#include <stdint.h>
8
#include <stdbool.h>
9
#include <stdlib.h>
2 pmbaty 10
#include <stdarg.h>
1 pmbaty 11
#include <stdio.h>
12
#include <string.h>
16 pmbaty 13
#include <limits.h>
1 pmbaty 14
#include <errno.h>
15
#include <sys/stat.h>
16
#include <ctype.h>
17
#include <time.h>
18
 
16 pmbaty 19
// platform-specific includes
1 pmbaty 20
#ifdef _MSC_VER
14 pmbaty 21
#include <sys/utime.h>
20 pmbaty 22
#include <process.h>
1 pmbaty 23
#else // !_MSC_VER
24
#include <sys/param.h>
19 pmbaty 25
#include <sys/sysmacros.h>
20 pmbaty 26
#include <sys/wait.h>
1 pmbaty 27
#include <unistd.h>
19 pmbaty 28
#include <dirent.h>
14 pmbaty 29
#include <utime.h>
1 pmbaty 30
#endif // _MSC_VER
31
 
16 pmbaty 32
// own includes
33
#include "buffer.h"
34
#include "sha512.h"
35
#include "elffile.h"
36
#include "ifsfile.h"
37
#include "utility.h"
1 pmbaty 38
 
4 pmbaty 39
 
16 pmbaty 40
// compiler-specific glue
41
#ifndef _MSC_VER
42
#define sscanf_s sscanf // WARNING: TRUE FOR THIS FILE ONLY!
43
#endif // !_MSC_VER
4 pmbaty 44
 
8 pmbaty 45
 
16 pmbaty 46
// libasan (Address Sanitizer) options: this is not a daemon, so I don't care about leaks: they will be recovered by the OS at program exit
47
const char *__asan_default_options () { return ("detect_leaks=0"); }
8 pmbaty 48
 
15 pmbaty 49
 
11 pmbaty 50
// placeholder value
51
#define WILL_BE_FILLED_LATER 0xbaadf00d // urgh
10 pmbaty 52
 
1 pmbaty 53
 
11 pmbaty 54
// miscellaneous macros
55
#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
56
#ifdef _WIN32
57
#define IS_DIRSEP(c) (((c) == '/') || ((c) == '\\')) // platform-specific directory separator, Win32 variant
16 pmbaty 58
#define PATH_SEP ";" // platform-specific PATH element separator (as string), Win32 variant
11 pmbaty 59
#else // !_WIN32, thus POSIX
60
#define IS_DIRSEP(c) ((c) == '/') // platform-specific directory separator, UNIX variant
16 pmbaty 61
#define PATH_SEP ":" // platform-specific PATH element separator (as string), UNIX variant
11 pmbaty 62
#endif // _WIN32
16 pmbaty 63
#define RECORD_SEP "\x1e" // arbitrarily-chosen ASCII record separator, as a C string suitable for e.g. strtok()
11 pmbaty 64
 
65
 
22 pmbaty 66
// macros for constructing and destructing string arrays
67
#define STRINGARRAY_INIT(string_array) do { (string_array)->args = NULL; (string_array)->count = 0; } while (0)
68
#define STRINGARRAY_PUSH(string_array,str) do { \
69
      reallocated_ptr = realloc ((string_array)->args, ((string_array)->count + 1) * sizeof (char *)); \
70
      ASSERT_WITH_ERRNO (reallocated_ptr); \
71
      (string_array)->args = reallocated_ptr; \
72
      (string_array)->args[(string_array)->count] = ((str) != NULL ? strdup ((str)) : NULL); \
73
      if ((str) != NULL) \
74
         ASSERT_WITH_ERRNO ((string_array)->args[(string_array)->count]); \
75
      (string_array)->count++; \
76
   } while (0)
77
#define STRINGARRAY_FREE(string_array) do { \
78
      if ((string_array)->args != NULL) { \
79
         for (array_index = 0; array_index < (string_array)->count; array_index++) \
80
            if ((string_array)->args[array_index] != NULL) \
81
               free ((string_array)->args[array_index]); \
82
         free ((string_array)->args); \
83
         (string_array)->args = NULL; \
84
      } \
85
      (string_array)->count = 0; \
86
   } while (0)
1 pmbaty 87
 
88
 
22 pmbaty 89
// string array structure type definition
90
typedef struct stringarray_s
91
{
92
   char **args;
93
   size_t count;
94
} stringarray_t;
95
 
96
 
11 pmbaty 97
// IFS directory entry insertion parameters structure type definition
1 pmbaty 98
typedef struct parms_s
99
{
100
   int dperms; // directory permissions (e.g. 0755)
101
   int perms; // file permissions (e.g. 0644)
102
   int uid; // owner user ID (e.g. 0 = root)
103
   int gid; // owner group ID (e.g. 0 = root)
104
   int st_mode; // entry type (e.g. S_IFREG for files) and permissions
7 pmbaty 105
   uint32_t mtime; // entry's modification time POSIX timestamp - set to UINT32_MAX to use the concerned files' mtime on the build host
106
   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)
17 pmbaty 107
   char *prefix; // [prefix=path] install path (e.g. "proc/boot")
7 pmbaty 108
   bool should_follow_symlinks; // follow symlinks
109
   bool should_autosymlink_dylib; // dynamic libraries should be written under their official SONAME and a named symlink be created pointing at them
15 pmbaty 110
   bool should_keep_ld_output; // whether to keep .sym files produced by ld calls, togglable by the [+keeplinked] attribute
18 pmbaty 111
   bool should_ignore_duplicates; // [+|-dupignore] whether to ignore duplicates
112
   bool should_allow_nonexistent_files; // [+|-optional] whether to continue processing on unexistent files
20 pmbaty 113
   bool is_bootstrap_file; // entry has the [virtual] attribute
2 pmbaty 114
   bool is_compiled_bootscript; // entry has [+script] attribute
10 pmbaty 115
   int extra_ino_flags; // bitmap of extra inode flags (IFS_INO_xxx)
17 pmbaty 116
   char *search; // [search=path[:path]] binary search path (the default one will be constructed at startup)
2 pmbaty 117
 
16 pmbaty 118
   buffer_t data; // the resolved file's own data bytes
1 pmbaty 119
} parms_t;
120
 
121
 
16 pmbaty 122
// exported globals
123
int verbose_level = 1; // verbosity level, can be increased with multiple -v[...] flags
124
 
125
 
126
// global variables used in this module only
1 pmbaty 127
static char line_buffer[4096]; // scrap buffer for the IFS build file parser
128
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
129
static uint32_t image_end = UINT32_MAX; // default image end (no limit)
130
static uint32_t image_maxsize = UINT32_MAX; // default image max size (no limit)
131
static uint32_t image_totalsize = 0; // image total size, measured once all the blocks have been written to the output IFS file
132
static uint32_t image_align = 4; // default image alignment, as per QNX docs
133
static uint32_t image_kernel_ino = 0;
134
static uint32_t image_bootscript_ino = 0;
7 pmbaty 135
#if defined(__x86_64__)
1 pmbaty 136
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)
17 pmbaty 137
static char image_processor_base[16] = "x86_64"; // default base 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 138
#elif defined(__aarch64__)
139
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)
17 pmbaty 140
static char image_processor_base[16] = "aarch64"; // default base 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 141
#else // unknown platform
142
#error Please port ifstool to this platform
143
#endif
1 pmbaty 144
static char *buildfile_pathname = NULL; // pathname of IFS build file
7 pmbaty 145
static char *current_line = NULL; // copy of current line in IFS build file
1 pmbaty 146
static int lineno = 0; // current line number in IFS build file
147
static char *QNX_TARGET = NULL; // value of the $QNX_TARGET environment variable
17 pmbaty 148
static char *SEARCH_PATH = NULL; // mallocated string of search paths, populated by the -r command-line argument
149
static char **saved_ELF_sections = NULL; // mallocated array of const strings, populated by the -s command-line argument
150
static size_t saved_ELF_section_count = 0; // number of elements in the saved_ELF_sections array
19 pmbaty 151
static char *sym_suffix = ""; // .sym files extra suffix, settable with the -a command-line argument
1 pmbaty 152
 
15 pmbaty 153
// bootable IFS support
22 pmbaty 154
static char *bootfile_pathname = NULL;           // FIXME: HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS
155
static size_t bootfile_size = 0;                 // FIXME: HACK: size of the bootcode binary blob file to put at the start of a bootable IFS
156
static char *startupfile_pathname = NULL;        // FIXME: HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS
157
static size_t startupfile_ep_from_imagebase = 0; // FIXME: HACK: startup code entrypoint offset from image base for a bootable IFS 
21 pmbaty 158
static size_t kernelfile_offset = 0x32000;       // kernel file offset in the IFS (is it ever supposed to change?)
1 pmbaty 159
 
15 pmbaty 160
 
16 pmbaty 161
// exported function prototypes
162
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
163
 
164
 
1 pmbaty 165
// prototypes of local functions
166
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)
24 pmbaty 167
static char *resolve_envvars (const char *str); // resolves environment variables in str and replaces them with their value, or an empty string if undefined. Returns a mallocated string (caller frees)
17 pmbaty 168
static char *resolve_pathname (const char *pathname, const char *search_paths_or_NULL_for_MKIFS_PATH_envvar); // locates pathname among the known search paths and returns a pointer to the resolved pathname (static string)
10 pmbaty 169
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
16 pmbaty 170
static size_t Buffer_WriteIFSDirectoryEntryAt (buffer_t *ifs_data, const size_t write_offset, const fsentry_t *fsentry); // writes the given filesystem entry (without its contents) to the IFS buffer
20 pmbaty 171
static size_t Buffer_AppendIFSFileData (buffer_t *ifs_data, const fsentry_t *fsentry); // writes the given filesystem entry's file data (i.e. its contents) to the IFS buffer
172
static int Buffer_StripELFFile (buffer_t *file, const char **saved_sections, const size_t saved_section_count, const bool should_align_segsize_with_ramsize, const char *indicative_pathname); // strips an ELF file buffer the way mkifs does it and returns whether it succeeded
19 pmbaty 173
static void 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 in the the fsentries array
174
static void add_directory_contents_recursively (fsentry_t **fsentries, size_t *fsentry_count, const char *dir_pathname, const size_t start_pathname_len, parms_t *default_parms); // adds the contents of the directory pointed to by dir_pathname to the fsentries array, recursively
1 pmbaty 175
static int fsentry_compare_pathnames_cb (const void *a, const void *b); // qsort() comparison callback that sorts filesystem entries by pathnames
17 pmbaty 176
static void parse_line (FILE *buildfile_fp, char *line_buffer, fsentry_t **fsentries, size_t *fsentry_count, parms_t *default_parms); // parses a line in the build file and make the relevant changes to the fsentries array
1 pmbaty 177
 
178
 
16 pmbaty 179
// imported function prototypes
180
extern int dump_ifs_info (const char *ifs_pathname, bool want_everything); // [implemented in ifsdump.c] dumps detailed info about a particular IFS file on the standard output, returns 0 on success and >0 on error
181
extern int dump_ifs_contents (const char *ifs_pathname, const char *outdir); // [implemented in ifsdump.c] dumps the IFS filesystem contents in outdir, returns 0 on success and >0 on error
182
extern int dump_file_hex (const char *pathname); // [implemented in ifsdump.c] dumps the contents of pathname to stdout in mixed hexadecimal + ASCII (hex editor) format
1 pmbaty 183
 
184
 
16 pmbaty 185
int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness)
1 pmbaty 186
{
6 pmbaty 187
   // 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 188
 
5 pmbaty 189
   uint8_t accumulator[4] = { 0, 0, 0, 0 };
7 pmbaty 190
   const char *current_char_ptr;
191
   int32_t image_cksum;
5 pmbaty 192
   size_t i;
193
 
7 pmbaty 194
   image_cksum = 0;
195
   current_char_ptr = data;
5 pmbaty 196
   for (i = 0; i < data_len; i++)
197
   {
198
      accumulator[i % 4] = *current_char_ptr;
199
      if (i % 4 == 3)
200
         if (is_foreign_endianness)
8 pmbaty 201
            image_cksum += (accumulator[3] << 0) + (accumulator[2] << 8) + (accumulator[1] << 16) + (accumulator[0] << 24);
5 pmbaty 202
         else
8 pmbaty 203
            image_cksum += (accumulator[0] << 0) + (accumulator[1] << 8) + (accumulator[2] << 16) + (accumulator[3] << 24);
5 pmbaty 204
      current_char_ptr++;
205
   }
206
 
207
   return (is_foreign_endianness ? __builtin_bswap32 (-image_cksum) : -image_cksum);
208
}
209
 
210
 
1 pmbaty 211
static long long read_integer (const char *str)
212
{
213
   // 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)
214
 
215
   char *endptr = NULL;
216
   long long ret = strtoll (str, &endptr, 0); // use strtoll() to handle hexadecimal (0x...), octal (0...) and decimal (...) bases
217
   if (endptr != NULL)
218
   {
15 pmbaty 219
      if      ((*endptr == 'k') || (*endptr == 'K')) ret *= (size_t) 1024;
1 pmbaty 220
      else if ((*endptr == 'm') || (*endptr == 'M')) ret *= (size_t) 1024 * 1024;
221
      else if ((*endptr == 'g') || (*endptr == 'G')) ret *= (size_t) 1024 * 1024 * 1024;
222
      else if ((*endptr == 't') || (*endptr == 'T')) ret *= (size_t) 1024 * 1024 * 1024 * 1024; // future-proof enough, I suppose?
223
   }
224
   return (ret);
225
}
226
 
227
 
24 pmbaty 228
static char *resolve_envvars (const char *str)
229
{
230
   // resolves environment variables in str and replaces them with their value, or an empty string if undefined
231
   // returns a mallocated string (caller frees), or dies with errno
232
 
233
   signed int erase_index;
234
   void *reallocated_ptr;
235
   size_t replacement_len;
236
   size_t middlebit_len;
237
   size_t old_str_len;
238
   size_t new_str_len;
239
   size_t endbit_len;
240
   char erased_char;
241
   char *resolved_str;
242
   char *replacement;
243
   char *varname;
244
   char *endbit;
245
   char *token;
246
 
247
   resolved_str = strdup (str); // have a working copy of the input string
248
   ASSERT_WITH_ERRNO (resolved_str);
249
   while ((((token = strstr (resolved_str, "${")) != NULL) && ((endbit = strchr (token, '}')) != NULL)) // look for variables in the "${VARNAME}" format *AND* in "$VARNAME" format
250
          || (((token = strstr (resolved_str, "$")) != NULL) && ((middlebit_len = strspn (token, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_")) != strlen (token))))
251
   {
252
      if (token[1] == '{') // "${VARNAME}" format
253
      {
254
         endbit++; // locate where the end bit begins
255
         varname = token + 2; // skip the leading two characters: "${"
256
         erase_index = -1; // we shall split the string at the character that's *just before* where the end bit starts
257
      }
258
      else // "$VARNAME" format
259
      {
260
         endbit = &token[middlebit_len]; // locate where the end bit begins
261
         varname = token + 1; // skip the leading '$'
262
         erase_index = 0; // we shall split the string at the character that's *right where* the end bit starts
263
      }
264
      old_str_len = strlen (resolved_str); // measure current string length
265
      endbit_len = strlen (endbit); // measure the length of the end bit (skip the closing curly brace)
266
      erased_char = endbit[erase_index]; // remember which is the character we're going to erase
267
      endbit[erase_index] = 0; // split the string at the end of the variable name
268
      if (strcmp (varname, "PFS") == 0)
269
         replacement = PATH_SEP; // special case: if it's the PFS variable, select ":" or ";" based on the host platform
270
      else
271
         replacement = getenv (varname); // peek at the environment for its value
272
      if (replacement == NULL)
273
         replacement = ""; // if this variable isn't defined, fallback to an empty string, just like what a UNIX shell does
274
      endbit[erase_index] = erased_char; // put the erased character back
275
      replacement_len = strlen (replacement); // measure replacement length
276
      new_str_len = (size_t) token - (size_t) resolved_str + replacement_len + endbit_len; // measure updated string len
277
      if (new_str_len > old_str_len)
278
      {
279
         reallocated_ptr = realloc (resolved_str, new_str_len + 1); // grow it if necessary
280
         ASSERT_WITH_ERRNO (reallocated_ptr);
281
         token = &((char *) reallocated_ptr)[token - resolved_str]; // fix the pointers that may have moved
282
         endbit = &((char *) reallocated_ptr)[endbit - resolved_str]; // fix the pointers that may have moved
283
         resolved_str = reallocated_ptr;
284
      }
285
      memmove (token + replacement_len, endbit, endbit_len + 1); // move the end bit to its final location (including its nul terminator)
286
      memcpy (token, replacement, replacement_len); // and patch the replacement in between
287
   }
288
 
289
   return (resolved_str); // finished, return the mallocated resolved string (caller frees)
290
}
291
 
292
 
17 pmbaty 293
static char *resolve_pathname (const char *pathname, const char *search_paths_or_NULL_for_MKIFS_PATH_envvar)
2 pmbaty 294
{
16 pmbaty 295
   // locates pathname among search path and returns resolved pathname (static buffer) or NULL.
2 pmbaty 296
 
17 pmbaty 297
   typedef struct default_path_s { bool uses_processor_base; char *subpath; } default_path_t;
298
 
299
   static const default_path_t default_paths[] =
300
   {
301
      { false, "/sbin"     }, // prefix with $PROCESSOR/
302
      { false, "/usr/sbin" }, // prefix with $PROCESSOR/
303
      { false, "/boot/sys" }, // prefix with $PROCESSOR/
304
      { true,  "/boot/sys" }, // prefix with $PROCESSOR_BASE/
305
      { false, "/bin"      }, // prefix with $PROCESSOR/
306
      { false, "/usr/bin"  }, // prefix with $PROCESSOR/
307
      { false, "/lib"      }, // prefix with $PROCESSOR/
308
      { false, "/lib/dll"  }, // prefix with $PROCESSOR/
309
      { false, "/usr/lib"  }  // prefix with $PROCESSOR/
310
   };
16 pmbaty 311
   static thread_local char *resolved_pathname = NULL;
2 pmbaty 312
 
24 pmbaty 313
   char *pathname_without_envvars;
18 pmbaty 314
   char *resolved_search_path;
17 pmbaty 315
   size_t defaultpath_index;
16 pmbaty 316
   struct stat stat_buf;
18 pmbaty 317
   char *nextsep;
318
   char *token;
2 pmbaty 319
 
24 pmbaty 320
   // resolve possible environment variables in pathname
321
   pathname_without_envvars = resolve_envvars (pathname);
2 pmbaty 322
 
25 pmbaty 323
   // NOTE: the QNX documentation states:
324
   // "- If path starts with a slash (/) on a Linux development host, or a disk volume label (i.e., drive letter and a colon) followed by a backslash (\) on a Windows host, the path is absolute and mkifs looks for the file at that exact host location. [...]
325
   //  - If path contains a slash or backslash character that's not at the start, the path is relative and mkifs tries to resolve it relative to the current working directory (CWD).
326
   //  - If path does not contain a directory separator or the file could not be found relative to the CWD, mkifs tries to resolve it relative to all directories given in the search attribute, in succession."
327
 
328
   // is it an absolute pathname (POSIX and Windows variants) ?
329
   if (IS_DIRSEP (pathname_without_envvars[0])
17 pmbaty 330
#ifdef _WIN32
25 pmbaty 331
       || (isalpha (pathname_without_envvars[0]) && (pathname_without_envvars[1] == ':') && IS_DIRSEP (pathname_without_envvars[2]))
17 pmbaty 332
#endif // _WIN32
333
       )
25 pmbaty 334
      return (pathname_without_envvars); // in this case, it MUST exist at its designated location
335
 
336
   // else is it a relative pathname ?
337
   else if (((strchr (pathname_without_envvars, '/') != NULL)
338
#ifdef _WIN32
339
             || (strchr (pathname_without_envvars, '\\') != NULL)
340
#endif // _WIN32
341
            ) && (stat (pathname_without_envvars, &stat_buf) == 0) && S_ISREG (stat_buf.st_mode))
342
      return (pathname_without_envvars); // in this case, see if it exists relatively to the current working directory, and if it does, return it
343
 
344
   // what we've been given is just a basename, so search it among the search paths we have
345
 
346
   // QNX docs:
347
   // When searching for host files to be included in the image, search the default paths used for storing binaries within the specified directory before searching the default paths within $QNX_TARGET.
348
   // You can define multiple -r options; each adds a set of paths to search for files.
349
   // The -r options are evaluated from left to right meaning the paths prefixed with the first (leftmost) rootdir are searched first, then those prefixed with the second rootdir, and so on.
350
   // Normally, mkifs searches any paths defined in $MKIFS_PATH when it was called and then the default paths within $QNX_TARGET.
351
   // The default paths are based on the CPU architecture specified by $PROCESSOR and $PROCESSOR_BASE.
352
   // If you specify -r options, mkifs searches the default paths prefixed with each dir variable before searching those within $QNX_TARGET.
353
   // These paths are:
354
   //   dir/${PROCESSOR}/sbin
355
   //   dir/${PROCESSOR}/usr/sbin
356
   //   dir/${PROCESSOR}/boot/sys
357
   //   dir/${PROCESSOR_BASE}/boot/sys
358
   //   dir/${PROCESSOR}/bin
359
   //   dir/${PROCESSOR}/usr/bin
360
   //   dir/${PROCESSOR}/lib
361
   //   dir/${PROCESSOR}/lib/dll
362
   //   dir/${PROCESSOR}/usr/lib
363
   // NOTE: The structure of the directory paths under dir must be identical to that of the default paths under $QNX_TARGET, but the root dir itself may be any path you choose.
364
   // For example, if you wanted to include /scratch/aarch64le/sbin/devb-sata, you would specify a -r option like this:
365
   //   -r /scratch
366
   // Note that you don't include $PROCESSOR or $PROCESSOR_BASE in dir.
367
 
368
   //  - search all paths in explicit path/[default paths] (if explicit path supplied)
369
   //  - search all paths in (-r flags if have some|MKIFS_PATH)/[default paths] (if no explicit path supplied)
370
   //  - search all paths in $QNX_TARGET/[default paths]
371
 
372
   // initial allocation (per thread)
373
   if (resolved_pathname == NULL)
7 pmbaty 374
   {
25 pmbaty 375
      resolved_pathname = malloc (MAXPATHLEN);
376
      ASSERT_WITH_ERRNO (resolved_pathname);
377
   }
17 pmbaty 378
 
25 pmbaty 379
   // if no file-specific explicit search path was supplied, use the path list supplied by the -r command-line arguments, else fallback to MKIFS_PATH if we don't have any
380
   if (search_paths_or_NULL_for_MKIFS_PATH_envvar == NULL)
381
      search_paths_or_NULL_for_MKIFS_PATH_envvar = (SEARCH_PATH != NULL ? SEARCH_PATH : getenv ("MKIFS_PATH"));
17 pmbaty 382
 
25 pmbaty 383
   // construct a potential final path using each element of the search path
384
   if (search_paths_or_NULL_for_MKIFS_PATH_envvar != NULL)
385
   {
386
      // the first step is to resolve all environment variables in the search path
387
      resolved_search_path = resolve_envvars (search_paths_or_NULL_for_MKIFS_PATH_envvar);
24 pmbaty 388
 
25 pmbaty 389
      // now split this search path string into multiple tokens and process them one after the other
390
      token = (*resolved_search_path != 0 ? resolved_search_path : NULL);
391
      nextsep = (token != NULL ? &token[strcspn (token, PATH_SEP)] : NULL);
392
      while (token != NULL)
7 pmbaty 393
      {
25 pmbaty 394
         // look under this search path at each of the known subpaths
395
         for (defaultpath_index = 0; defaultpath_index < sizeof (default_paths) / sizeof (default_paths[0]); defaultpath_index++)
17 pmbaty 396
         {
25 pmbaty 397
            sprintf_s (resolved_pathname, MAXPATHLEN, "%.*s/%s/%s/%s", (int) (nextsep - token), token, (default_paths[defaultpath_index].uses_processor_base ? image_processor_base : image_processor), default_paths[defaultpath_index].subpath, pathname_without_envvars);
398
            if ((stat (resolved_pathname, &stat_buf) == 0) && S_ISREG (stat_buf.st_mode))
17 pmbaty 399
            {
25 pmbaty 400
               free (pathname_without_envvars);
401
               return (resolved_pathname); // if a file can indeed be found at this location, stop searching
17 pmbaty 402
            }
25 pmbaty 403
         }
17 pmbaty 404
 
25 pmbaty 405
         token = (*nextsep != 0 ? nextsep + 1 : NULL);
406
         nextsep = (token != NULL ? &token[strcspn (token, PATH_SEP)] : NULL);
17 pmbaty 407
      }
25 pmbaty 408
   }
17 pmbaty 409
 
25 pmbaty 410
   // file not found in search paths: look under QNX_TARGET at each of the known subpaths
411
   for (defaultpath_index = 0; defaultpath_index < sizeof (default_paths) / sizeof (default_paths[0]); defaultpath_index++)
412
   {
413
      sprintf_s (resolved_pathname, MAXPATHLEN, "%s/%s/%s/%s", QNX_TARGET, (default_paths[defaultpath_index].uses_processor_base ? image_processor_base : image_processor), default_paths[defaultpath_index].subpath, pathname_without_envvars);
414
      if ((stat (resolved_pathname, &stat_buf) == 0) && S_ISREG (stat_buf.st_mode))
17 pmbaty 415
      {
25 pmbaty 416
         free (pathname_without_envvars);
417
         return (resolved_pathname); // if a file can indeed be found at this location, stop searching
7 pmbaty 418
      }
419
   }
420
 
24 pmbaty 421
   free (pathname_without_envvars);
16 pmbaty 422
   errno = ENOENT; // we exhausted all possibilities
423
   return (NULL); // file not found, return with ENOENT
424
}
7 pmbaty 425
 
16 pmbaty 426
 
427
static size_t Buffer_WriteIFSDirectoryEntryAt (buffer_t *ifs, const size_t write_offset, const fsentry_t *fsentry)
428
{
429
   // writes a directory entry in the image filesystem buffer pointed to by ifs at write_offset (or fakes so if ifs is NULL)
430
   // and return the number of bytes written (or that would have been written)
431
 
432
   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";
433
 
434
   size_t datalen;
435
   size_t count;
436
 
437
   count = 0;
438
   if (ifs != NULL)
439
      ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->header, sizeof (fsentry->header))); // write the entry header (PACKED STRUCT)
440
   count += sizeof (fsentry->header);
441
   if (S_ISREG (fsentry->header.mode))
15 pmbaty 442
   {
16 pmbaty 443
      if (ifs != NULL)
444
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.file.offset, sizeof (uint32_t))); // write offset
445
      count += sizeof (uint32_t);
446
      if (ifs != NULL)
447
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.file.size,   sizeof (uint32_t))); // write size
448
      count += sizeof (uint32_t);
449
      datalen = strlen (fsentry->u.file.path) + 1;
450
      if (ifs != NULL)
451
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.file.path, datalen)); // write null-terminated path (no leading slash)
452
      count += datalen;
15 pmbaty 453
   }
16 pmbaty 454
   else if (S_ISDIR (fsentry->header.mode))
7 pmbaty 455
   {
16 pmbaty 456
      datalen = strlen (fsentry->u.dir.path) + 1;
457
      if (ifs != NULL)
458
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.dir.path, datalen)); // write null-terminated path (no leading slash)
459
      count += datalen;
7 pmbaty 460
   }
16 pmbaty 461
   else if (S_ISLNK (fsentry->header.mode))
7 pmbaty 462
   {
16 pmbaty 463
      if (ifs != NULL)
464
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.symlink.sym_offset, sizeof (uint16_t))); // write offset
465
      count += sizeof (uint16_t);
466
      if (ifs != NULL)
467
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.symlink.sym_size,   sizeof (uint16_t))); // write size
468
      count += sizeof (uint16_t);
469
      datalen = strlen (fsentry->u.symlink.path) + 1;
470
      if (ifs != NULL)
471
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.symlink.path, datalen)); // write null-terminated path (no leading slash)
472
      count += datalen;
473
      datalen = strlen (fsentry->u.symlink.contents) + 1;
474
      if (ifs != NULL)
475
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.symlink.contents, datalen)); // write null-terminated symlink contents
476
      count += datalen;
7 pmbaty 477
   }
16 pmbaty 478
   else
479
   {
480
      if (ifs != NULL)
481
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.device.dev,  sizeof (uint32_t))); // write dev number
482
      count += sizeof (uint32_t);
483
      if (ifs != NULL)
484
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.device.rdev, sizeof (uint32_t))); // write rdev number
485
      count += sizeof (uint32_t);
486
      datalen = strlen (fsentry->u.device.path) + 1;
487
      if (ifs != NULL)
488
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.device.path, datalen)); // write null-terminated path (no leading slash)
489
      count += datalen;
490
   }
7 pmbaty 491
 
16 pmbaty 492
   ASSERT (count <= fsentry->header.size, "attempt to write invalid dirent (claimed size %zd, written size %zd). Aborting.", (size_t) fsentry->header.size, count);
493
   if (count < fsentry->header.size)
494
   {
495
      if (ifs != NULL)
496
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, zeropad_buffer, fsentry->header.size - count)); // pad as necessary
497
      count += fsentry->header.size - count;
498
   }
499
 
500
   return (count);
7 pmbaty 501
}
502
 
503
 
20 pmbaty 504
static size_t Buffer_AppendIFSFileData (buffer_t *ifs_data, const fsentry_t *fsentry)
1 pmbaty 505
{
16 pmbaty 506
   // writes the given filesystem entry's file data (i.e. its contents) to the IFS buffer
1 pmbaty 507
 
16 pmbaty 508
   elf_program_header_t *phdr;
509
   elf_header_t *elf;
17 pmbaty 510
   size_t fixed_physical_addr;
16 pmbaty 511
   size_t corrective_offset;
512
   //size_t segment_type;
20 pmbaty 513
   size_t size_in_memory;
16 pmbaty 514
   size_t table_index;
515
   size_t table_count;
516
   size_t data_offset;
1 pmbaty 517
 
16 pmbaty 518
   ASSERT (S_ISREG (fsentry->header.mode), "function called for invalid dirent"); // consistency check
519
   data_offset = ifs_data->size; // see where we are
1 pmbaty 520
 
16 pmbaty 521
   // is the file we're storing a preprocessed ELF file ?
20 pmbaty 522
   if (fsentry->header.ino & IFS_INO_PROCESSED_ELF)
1 pmbaty 523
   {
16 pmbaty 524
 
525
      elf = (elf_header_t *) fsentry->u.file.UNSAVED_databuf; // quick access to ELF header
526
      table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); // get the number of program headers
527
      for (table_index = 0; table_index < table_count; table_index++)
528
      {
529
         phdr = (elf_program_header_t *) &fsentry->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
530
         //segment_type = ELF_GET_NUMERIC (elf, phdr, segment_type); // get segment type
531
         //if (!((segment_type >= 2) && (segment_type <= 7) || ((segment_type >= 0x6474e550) && (segment_type <= 0x6474e552)) || (segment_type == 0x70000001)))
532
         //   continue; // NOTE: only certain segments types must be corrected
533
 
534
 
535
         corrective_offset = ELF_GET_NUMERIC (elf, phdr, virtual_addr) - ELF_GET_NUMERIC (elf, phdr, file_offset);
20 pmbaty 536
         size_in_memory = ELF_GET_NUMERIC (elf, phdr, size_in_memory); // get this ELF segment's occupied size in memory
537
         if (size_in_memory != 0) // only patch the physical address of segments that have an actual size in memory
17 pmbaty 538
         {
539
            fixed_physical_addr = ELF_GET_NUMERIC (elf, phdr, physical_addr) + image_base + data_offset - corrective_offset;
540
            ELF_SET_NUMERIC (elf, phdr, physical_addr, fixed_physical_addr); // patch the physical address member of the program header table (NOTE: data_offset is the location where the file data is about to be written)
541
         }
16 pmbaty 542
      }
1 pmbaty 543
   }
544
 
16 pmbaty 545
   ASSERT_WITH_ERRNO (Buffer_Append (ifs_data, fsentry->u.file.UNSAVED_databuf, fsentry->u.file.size)); // write file data blob
546
   return (ifs_data->size - data_offset); // return the number of bytes written
1 pmbaty 547
}
548
 
549
 
16 pmbaty 550
static inline size_t Buffer_LocateOrAppendIfNecessaryAndReturnOffsetOf (buffer_t *buffer, const char *str)
10 pmbaty 551
{
16 pmbaty 552
   // helper function used in add_fsentry(): locates or appends str to buffer and returns its relative offset in the buffer
553
 
554
   size_t str_len_including_terminator = strlen (str) + 1;
555
   void *occurrence = Buffer_FindFirst (buffer, str, str_len_including_terminator);
556
   if (occurrence == NULL)
557
   {
558
      ASSERT_WITH_ERRNO (Buffer_Append (buffer, str, str_len_including_terminator));
559
      occurrence = Buffer_FindFirst (buffer, str, str_len_including_terminator);
560
      ASSERT_WITH_ERRNO (occurrence);
561
   }
562
   return (Buffer_OffsetOf (buffer, occurrence)); // can't fail
10 pmbaty 563
}
564
 
565
 
20 pmbaty 566
static int Buffer_StripELFFile (buffer_t *file, const char **saved_sections, const size_t saved_section_count, const bool should_align_segsize_with_ramsize, const char *indicative_pathname)
10 pmbaty 567
{
16 pmbaty 568
   // NOTE: for each ELF file, mkifs
569
   // -> alters the program header table and offsets each p_addr (physical address) member by <image_base> plus the current file offset (this cannot be done right now, will need to be done once they are known)
570
   // -> 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
571
   // 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 572
 
16 pmbaty 573
   // reconstructed ELF:
574
   // ==== START OF FILE ====
575
   // ELF header
576
   // program header table
577
   //  (same sections, just p_addr offset changed)
578
   // section data 5 (named ".note.gnu.build-id")
579
   //  "............GNU....ZY.....c.o..l"
580
   // PROGRAM
581
   // sections table
582
   // + section 1: ALL ZEROES
583
   // + 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"
584
   // + section 3: fileoffs 0x22a5 size 0x1c --> ".gnu_debuglink" --> indicates the debug file and its checksum: "pci_debug2.so.3.0.sym" "\0\0\0" "VX2p"
585
   // + 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.."
586
   // + section 5: fileoffs 0x190 size 0x32 --> ".note.gnu.build-id" --> GNU build ID
587
   // + section 6: fileoffs 0x256e size 0x40 --> ".shstrtab" --> sections names strings table
588
   // section data 2 (named "QNX_info")
589
   //  (QNX binary description)
590
   // section data 3 (named ".gnu_debuglink")
591
   //  (debug file)
592
   // section data 4 (named "QNX_usage")
593
   //  (help text)
594
   // section data 6 (named ".shstrtab")
595
   //  "\0"
596
   //  ".shstrtab\0"
597
   //  "QNX_info\0"
598
   //  ".gnu_debuglink\0"
599
   //  "QNX_usage\0"
600
   //  ".note.gnu.build-id\0"
601
   // ==== END OF FILE ====
10 pmbaty 602
 
16 pmbaty 603
   #define ELFHDR ((elf_header_t *) file->bytes) // this convenient definition will make sure the ELF header points at the right location, even after entry_parms.data->byte is reallocated
604
   #define ADD_SECTION(section_name,section_ptr) do { \
605
      void *reallocated_ptr = realloc (elf_sections, (elf_section_count + 1) * sizeof (elf_section_t)); \
606
      ASSERT_WITH_ERRNO (reallocated_ptr); \
607
      elf_sections = reallocated_ptr; \
608
      elf_sections[elf_section_count].name = (section_name); \
609
      Buffer_Initialize (&elf_sections[elf_section_count].data); \
610
      *(section_ptr) = &elf_sections[elf_section_count]; \
611
      elf_section_count++; \
612
   } while (0)
613
 
614
   typedef struct elf_section_s
10 pmbaty 615
   {
16 pmbaty 616
      const char *name;
617
      elf_section_header_t header;
618
      buffer_t data;
619
   } elf_section_t;
10 pmbaty 620
 
16 pmbaty 621
   const elf_section_header_t *shdr;
20 pmbaty 622
   elf_program_header_t *phdr;
623
   elf_program_header_t *other_phdr;
16 pmbaty 624
   elf_section_t *elf_sections = NULL; // mallocated
625
   elf_section_t *elf_section = NULL;
626
   size_t elf_section_count = 0;
627
   size_t new_shdrtable_offset;
17 pmbaty 628
   size_t new_shdrtable_len;
16 pmbaty 629
   size_t sectiondata_start;
630
   size_t sectiondata_size;
20 pmbaty 631
   size_t size_in_memory;
632
   size_t size_in_file;
633
   size_t file_offset;
16 pmbaty 634
   size_t array_index;
635
   size_t table_index;
636
   size_t table_count;
637
   size_t page_size;
10 pmbaty 638
 
16 pmbaty 639
   // find out the platform page size
640
   if (ELF_GET_NUMERIC (ELFHDR, ELFHDR, instruction_set) == ELF_MACHINE_X86_64)
641
      page_size = 4 * 1024; // 4 kb pages on Intel processors
642
   else if (ELF_GET_NUMERIC (ELFHDR, ELFHDR, instruction_set) == ELF_MACHINE_AARCH64)
643
      page_size = 16 * 1024; // 16 kb pages on ARM64
644
   else
645
   {
646
      errno = ENOTSUP; // unsupported architecture: set errno to something meaningful
647
      return (0); // and return an error value
648
   }
7 pmbaty 649
 
20 pmbaty 650
   // if we should align the segment sizes in the ELF file with their occupied memory size (such is the case for e.g. procnto), do that first
651
   table_count = ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_len); // get the number of program headers
652
   for (table_index = 0; table_index < table_count; table_index++)
653
   {
654
      phdr = (elf_program_header_t *) &file->bytes[ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_item_size) * table_index]; // quick access to program header
655
      file_offset    = ELF_GET_NUMERIC (ELFHDR, phdr, file_offset); // get this ELF segment's start offset in the ELF file
656
      size_in_memory = ELF_GET_NUMERIC (ELFHDR, phdr, size_in_memory); // get this ELF segment's occupied size in memory
657
      size_in_file   = ELF_GET_NUMERIC (ELFHDR, phdr, size_in_file); // get this ELF segment's occupied size in the ELF file
658
      if (should_align_segsize_with_ramsize && (size_in_memory != size_in_file)) // should we align this segment's file size with its claimed RAM size ? (such is the case for e.g. procnto)
659
      {
660
         if (size_in_memory > size_in_file) // is it bigger ? if so, make sure we won't be overwriting other segments beyond this one
661
         {
662
            for (array_index = 0; array_index < table_count; array_index++)
663
            {
664
               other_phdr = (elf_program_header_t *) &file->bytes[ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_item_size) * array_index]; // quick access to program header
665
               if (other_phdr == phdr)
666
                  continue; // skip self
667
               if (ELF_GET_NUMERIC (ELFHDR, other_phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, other_phdr, size_in_file) < file_offset)
668
                  continue; // skip segments that are located before this one
669
               if (ELF_GET_NUMERIC (ELFHDR, other_phdr, file_offset) > file_offset + size_in_memory)
670
                  continue; // skip segments that are located after this one, including its corrected size
671
               DIE_WITH_EXITCODE (1, "remapping ELF segment would overwrite segment #%zd in the same file", array_index);
672
            }
1 pmbaty 673
 
20 pmbaty 674
            // finally, memset() the extra area
675
            Buffer_WriteAt (file, file_offset + size_in_memory, NULL, 0); // reallocate the ELF file data buffer if necessary
22 pmbaty 676
            phdr = (elf_program_header_t *) &file->bytes[ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_item_size) * table_index]; // restore access to program header (which may have moved)
20 pmbaty 677
            memset (&file->bytes[file_offset + size_in_file], 0, size_in_memory - size_in_file); // and write zeroes over the extra space
678
         }
679
         ELF_SET_NUMERIC (ELFHDR, phdr, size_in_file, size_in_memory); // patch this segment's size in the ELF file so that it matches the RAM size
680
      }
681
   }
682
 
683
   // now parse the program header table, and measure the farthest offset known by this table where we'll write the reconstructed section headers table
16 pmbaty 684
   new_shdrtable_offset = 0;
685
   table_count = ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_len);
686
   for (table_index = 0; table_index < table_count; table_index++)
1 pmbaty 687
   {
16 pmbaty 688
      phdr = (elf_program_header_t *) &file->bytes[ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_item_size) * table_index]; // quick access to program header
689
      if (ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_file) > new_shdrtable_offset)
690
         new_shdrtable_offset = ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_file); // keep track of the farthest offset known by the program headers table
1 pmbaty 691
   }
16 pmbaty 692
   /*
693
   size_t new_shdrtable_offset_method2 = 0;
694
   for (table_index = 0; table_index < table_count; table_index++)
1 pmbaty 695
   {
16 pmbaty 696
      phdr = (elf_program_header_t *) &file->bytes[ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_item_size) * table_index]; // quick access to program header
697
      size_t segment_type = ELF_GET_NUMERIC (ELFHDR, phdr, segment_type); // get segment type
698
      if (!((segment_type >= 2) && (segment_type <= 7)))
699
         continue; // NOTE: only certain segments types must be corrected
700
      if (ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_memory) > new_shdrtable_offset_method2)
701
         new_shdrtable_offset_method2 = ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_memory);
1 pmbaty 702
   }
16 pmbaty 703
   if (new_shdrtable_offset_method2 > new_shdrtable_offset)
704
      LOG_DEBUG ("METHOD2: %llx > %llx", new_shdrtable_offset_method2, new_shdrtable_offset);*/
705
   //new_shdrtable_offset = ROUND_TO_UPPER_MULTIPLE (new_shdrtable_offset, page_size); // round to page size
706
 
707
   // re-create the section header table
708
   ADD_SECTION (".shstrtab", &elf_section); // the first section will be the section names strings table
709
   ASSERT_WITH_ERRNO (Buffer_InitWithByteArray (&elf_section->data, "\0")); // initialize an empty section headers strings table
710
   ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&elf_section->data, ".shstrtab\0")); // append ".shstrtab" *INCLUDING* its null terminator
711
 
712
   // go through the saved sections array and see if such an ELF section is present in the ELF file
17 pmbaty 713
   for (array_index = 0; array_index < saved_section_count; array_index++)
16 pmbaty 714
      if ((shdr = elf_get_section_header_by_name (ELFHDR, saved_sections[array_index])) != NULL) // does this ELF have such a section ?
1 pmbaty 715
      {
16 pmbaty 716
         ADD_SECTION (saved_sections[array_index], &elf_section); // yes, so save it
717
         sectiondata_start = ELF_GET_NUMERIC (ELFHDR, shdr, file_offset); // identify section data start offset
718
         sectiondata_size = ELF_GET_NUMERIC (ELFHDR, shdr, size); // identify section data length
719
         if (sectiondata_start + sectiondata_size >= new_shdrtable_offset) // should this section be moved ?
720
            ASSERT_WITH_ERRNO (Buffer_InitWithData (&elf_section->data, &file->bytes[sectiondata_start], sectiondata_size)); // have a copy of this section's data
721
         else
722
            Buffer_Initialize (&elf_section->data); // this section is located before the place where we'll write the new section headers table, thus it doesn't need to be moved
17 pmbaty 723
         //LOG_DEBUG ("%s: section '%s' start 0x%llx len 0x%llx", indicative_pathname, saved_ELF_sections[array_index], (unsigned long long) sectiondata_start, (unsigned long long) sectiondata_size);
16 pmbaty 724
 
725
         // prepare this section's "fixed" header
726
         memcpy (&elf_section->header, shdr, ELF_STRUCT_SIZE (ELFHDR, shdr)); // have a copy of the old section header first
727
         ELF_SET_NUMERIC (ELFHDR, &elf_section->header, name_offset, Buffer_LocateOrAppendIfNecessaryAndReturnOffsetOf (&elf_sections[0].data, elf_section->name)); // make sure this section name is in the ELF sections section header strings table and update the relative offset of the section name
1 pmbaty 728
      }
16 pmbaty 729
 
730
   // jump over the new section headers table and write the saved sections data after the section headers table
731
   file->size = new_shdrtable_offset + (1 + elf_section_count) * ELF_STRUCT_SIZE (ELFHDR, &elf_sections[0].header); // start by truncating the ELF file: assume there are no sections beyond the section headers table until known otherwise
732
   for (table_index = 1; table_index < elf_section_count; table_index++)
1 pmbaty 733
   {
16 pmbaty 734
      elf_section = &elf_sections[table_index]; // quick access to ELF section about to be written
735
      if (elf_section->data.bytes != NULL) // was this section data backed up waiting to be relocated ?
1 pmbaty 736
      {
16 pmbaty 737
         ELF_SET_NUMERIC (ELFHDR, &elf_section->header, file_offset, file->size); // fix section offset
738
         Buffer_AppendBuffer (file, &elf_section->data); // append this section's data to the ELF file
1 pmbaty 739
      }
740
   }
16 pmbaty 741
   // write the section header strings table as the last section
742
   elf_section = &elf_sections[0]; // quick access to ELF section about to be written
743
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, name_offset, Buffer_LocateOrAppendIfNecessaryAndReturnOffsetOf (&elf_sections[0].data, elf_section->name)); // update the relative offset of the section name
744
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, type, ELF_SECTIONTYPE_STRINGTABLE); // section type (SHT_STRTAB)
745
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, flags, 0); // section flags (we could set SHF_STRINGS i.e. 0x20 here, but mkifs does not, so mimic that)
746
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, virtual_addr, 0); // this section does not need to be mapped
747
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, file_offset, file->size); // fix section offset
748
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, size, elf_sections[0].data.size); // section size
749
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, linked_index, 0); // this section is not linked to any other
750
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, info, 0); // this section has no additional info
751
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, alignment, 1); // this section is byte-aligned
752
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, entry_size, 0); // this section is not a table, so entry_size is zero
753
   Buffer_AppendBuffer (file, &elf_section->data); // append section headers strings table section data to ELF file
1 pmbaty 754
 
16 pmbaty 755
   // now write the section headers table
756
   memset (&file->bytes[new_shdrtable_offset], 0, ELF_STRUCT_SIZE (ELFHDR, &elf_sections[0].header)); // the first section header is always zerofilled
757
   for (table_index = 1; table_index < elf_section_count; table_index++)
758
      Buffer_WriteAt (file, new_shdrtable_offset + table_index * ELF_STRUCT_SIZE (ELFHDR, &elf_sections[table_index].header), &elf_sections[table_index].header, ELF_STRUCT_SIZE (ELFHDR, &elf_sections[table_index].header)); // write each section header
759
   Buffer_WriteAt (file, new_shdrtable_offset + table_index * ELF_STRUCT_SIZE (ELFHDR, &elf_sections[table_index].header), &elf_sections[0].header, ELF_STRUCT_SIZE (ELFHDR, &elf_sections[0].header)); // write the section header names section header last
1 pmbaty 760
 
16 pmbaty 761
   // and finally fix the ELF master header
17 pmbaty 762
   new_shdrtable_len = 1 + elf_section_count; // take in account that the first entry in the section headers table is empty
16 pmbaty 763
   ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_table_offset, new_shdrtable_offset);
17 pmbaty 764
   ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_table_len, new_shdrtable_len);
16 pmbaty 765
   ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_names_idx, elf_section_count); // the section headers strings table is the last section
766
 
767
   // align size with page size (4096 on x86, 16k on ARM), zerofilling the extra space
768
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (file, ROUND_TO_UPPER_MULTIPLE (file->size, page_size)));
769
 
770
   // cleanup
771
   for (table_index = 0; table_index < elf_section_count; table_index++)
772
      Buffer_Forget (&elf_sections[table_index].data); // free all sections' backing buffers
773
 
774
   #undef ELFHDR // undefine the macro that used to always point to the ELF header at the beginning of the file
775
   return (1); // success
1 pmbaty 776
}
777
 
778
 
19 pmbaty 779
static void add_fsentry (fsentry_t **fsentries, size_t *fsentry_count, parms_t *entry_parms, const char *stored_pathname, const char *buildhost_pathname)
1 pmbaty 780
{
16 pmbaty 781
   static thread_local char *candidate_pathname = NULL;
21 pmbaty 782
   static thread_local parms_t default_parms = { 0 };
22 pmbaty 783
   static thread_local stringarray_t global_envp = { NULL, 0 };
784
   static thread_local stringarray_t aps_partnames = { NULL, 0 };
1 pmbaty 785
   static int inode_count = 0; // will be preincremented each time this function is called
786
 
22 pmbaty 787
   typedef struct scriptcmd_s
788
   {
24 pmbaty 789
      char *argv0;
22 pmbaty 790
      int cpu_number;
791
      bool is_external;
792
      int priority;
793
      int sched_policy;
794
      int aps_partindex;
795
      bool is_session_leader;
796
      bool is_background_task;
797
      bool has_debug_flag;
798
   } scriptcmd_t;
799
 
800
   scriptcmd_t default_scriptcmd_params = { NULL, -1, false, -1, -1, -1, false, false, false };
801
   scriptcmd_t current_scriptcmd_params = { 0 };
20 pmbaty 802
   stringarray_t global_argv = { NULL, 0 };
803
   stringarray_t line_argv = { NULL, 0 };
804
   stringarray_t line_envp = { NULL, 0 };
805
   stringarray_t startup_argv = { NULL, 0 };
806
   stringarray_t startup_envp = { NULL, 0 };
807
   stringarray_t procnto_argv = { NULL, 0 };
808
   stringarray_t procnto_envp = { NULL, 0 };
809
   stringarray_t linker_argv = { NULL, 0 };
18 pmbaty 810
   const char *stored_pathname_without_leading_slash;
7 pmbaty 811
   const char *original_stored_pathname = NULL;
20 pmbaty 812
   buffer_t current_line;
22 pmbaty 813
   buffer_t compiled_script;
814
   buffer_t compiled_scriptline;
16 pmbaty 815
   buffer_t *shstrtab = NULL;
7 pmbaty 816
   const char *canonical_dylib_name;
817
   const char *dynamic_strings; // strings table of the ".dynamic" section
818
   const char *last_dirsep;
20 pmbaty 819
   size_t array_index;
820
   size_t line_index;
18 pmbaty 821
   size_t fsentry_index;
22 pmbaty 822
   size_t wait_time;
7 pmbaty 823
   char *resolved_pathname;
20 pmbaty 824
   char *linebit_start;
825
   char *write_ptr;
826
   char *read_ptr;
827
   char *token;
828
   char *value;
829
   char *ctx;
1 pmbaty 830
   void *reallocated_ptr;
7 pmbaty 831
   void *old_data;
20 pmbaty 832
   bool is_quoted_context;
833
   bool is_end_of_line;
1 pmbaty 834
   struct stat stat_buf;
835
   fsentry_t *fsentry;
19 pmbaty 836
   int retval;
1 pmbaty 837
 
16 pmbaty 838
   // initial allocation (per thread)
839
   if (candidate_pathname == NULL)
840
   {
841
      candidate_pathname = malloc (MAXPATHLEN);
842
      ASSERT_WITH_ERRNO (candidate_pathname);
843
   }
844
 
1 pmbaty 845
   if (S_ISDIR (entry_parms->st_mode)) // are we storing a directory ?
846
   {
24 pmbaty 847
      if ((buildhost_pathname != NULL) && (buildhost_pathname[0] != 0)) // was a source file pathname supplied ?
21 pmbaty 848
      {
849
         memcpy (&default_parms, entry_parms, sizeof (parms_t)); // apply current entry parameters when including a directory recursively
850
         add_directory_contents_recursively (fsentries, fsentry_count, buildhost_pathname, strlen (buildhost_pathname), &default_parms); // if so, add this diretory contents recursively
851
      }
15 pmbaty 852
      LOG_INFO ("directory: ino 0x%x uid %d gid %d mode 0%o path \"%s\"", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname);
1 pmbaty 853
   }
21 pmbaty 854
   else if (S_ISLNK (entry_parms->st_mode)) // else are we storing a symbolic link ?
1 pmbaty 855
   {
21 pmbaty 856
      // do we already know the data for this data blob ?
857
      if (entry_parms->data.bytes != NULL)
858
      {
859
         entry_parms->mtime = entry_parms->mtime_for_inline_files; // if so, set it a mtime equal to the mtime to use for inline files
860
         LOG_INFO ("symlink: ino 0x%x uid %d gid %d mode 0%o path \"%s\" -> \"%s\"", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_parms->data.bytes);
861
      }
862
      else if (buildhost_pathname != NULL) // else was a source file pathname supplied ?
863
      {
864
         entry_parms->data.bytes = malloc (MAXPATHLEN); // allocate enough space for symlink data
865
         ASSERT_WITH_ERRNO (entry_parms->data.bytes);
866
         retval = readlink (buildhost_pathname, entry_parms->data.bytes, MAXPATHLEN); // read symlink contents
867
         ASSERT_WITH_ERRNO (retval > 0);
868
         entry_parms->data.size = retval; // save symlink target length
869
      }
870
      else
871
         DIE_WITH_EXITCODE (1, "unexpected code path: can't store a symlink without neither explicit contents nor a host pathname. This is a bug in the program. Please contact the author.");
872
   }
873
   else if (S_ISFIFO (entry_parms->st_mode)) // else are we storing a FIFO ?
874
   {
875
      if ((entry_parms->data.bytes == NULL) || (strchr (entry_parms->data.bytes, ':') == NULL))
876
         DIE_WITH_EXITCODE (1, "device entry \"%s\" malformed (no 'dev:rdev' pair)", stored_pathname);
877
      LOG_INFO ("fifo: ino 0x%x uid %d gid %d mode 0%o path \"%s\" dev:rdev %s)", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_parms->data.bytes);
878
   }
879
   else // necessarily a regular file (either S_IFREG is specified, or st_mode is zero)
880
   {
881
      entry_parms->st_mode |= S_IFREG; // make this explicit
882
 
20 pmbaty 883
      if (entry_parms->is_bootstrap_file) // is it the bootstrap file ?
1 pmbaty 884
      {
20 pmbaty 885
         // parse each line of contents
886
         ASSERT (entry_parms->data.size > 0, "kernel specification without inline contents");
15 pmbaty 887
 
20 pmbaty 888
         // parse buffer (non-destructively) line after line
889
         Buffer_Initialize (&current_line);
890
         for (line_index = 0; Buffer_GetNthLine (&entry_parms->data, line_index, &current_line); line_index++)
15 pmbaty 891
         {
20 pmbaty 892
            read_ptr = current_line.bytes;
893
            while (isspace (*read_ptr))
894
               read_ptr++; // skip leading spaces
895
            if ((*read_ptr == '#') || (*read_ptr == 0))
15 pmbaty 896
               continue; // skip comments and empty lines
897
 
898
            // format of a line: [attributes] [env assignation] [...] [executable] [arg] [...] [comment]
899
            // example: "[uid=0 gid=0 perms=0700] CONFIG_PATH=/proc/boot:/etc procnto-smp-instr -v -mr -d 0777 -u 0777"
900
 
20 pmbaty 901
            LOG_DEBUG ("parsing line: %s", read_ptr);
902
 
15 pmbaty 903
            // does this line start with an attribute block ?
20 pmbaty 904
            if (*read_ptr == '[')
15 pmbaty 905
            {
20 pmbaty 906
               read_ptr++; // skip the leading square bracket
907
               linebit_start = read_ptr; // remember where it starts
15 pmbaty 908
               is_quoted_context = false; // reach the next unescaped closing square bracket that is not between quotes
20 pmbaty 909
               while ((*read_ptr != 0) && !((*read_ptr == ']') && (read_ptr[-1] != '\\') && !is_quoted_context))
15 pmbaty 910
               {
20 pmbaty 911
                  if (*read_ptr == '"')
15 pmbaty 912
                     is_quoted_context ^= true; // remember when we're between quotes
20 pmbaty 913
                  else if (!is_quoted_context && (*read_ptr == ' '))
914
                     *read_ptr = RECORD_SEP[0]; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
915
                  read_ptr++; // reach the next unescaped closing square bracket
15 pmbaty 916
               }
20 pmbaty 917
               if (*read_ptr != ']')
15 pmbaty 918
               {
22 pmbaty 919
                  LOG ("warning", 0, "syntax error in \"%s\" line %zd of inline document '%s': unterminated attributes block (skipping)", buildfile_pathname, 1 + line_index, stored_pathname);
15 pmbaty 920
                  continue; // invalid attribute block, skip line
921
               }
20 pmbaty 922
               is_end_of_line = (*read_ptr == 0); // see if we're at the end of line already
923
               *read_ptr = 0; // end the attribute block in all cases so that it is a parsable C string
15 pmbaty 924
 
925
               // now parse the attribute tokens (NOTE: THE LIST OF ALLOWED ATTRIBUTES HERE IS NOT DOCUMENTED)
16 pmbaty 926
               token = strtok_r (linebit_start, RECORD_SEP, &ctx);
15 pmbaty 927
               while (token != NULL)
928
               {
929
                  #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
20 pmbaty 930
                  if (false) {}
17 pmbaty 931
                  else if (strncmp (token, "prefix=",  7) == 0) { REACH_TOKEN_VALUE (); entry_parms->prefix  = (*value == '/' ? value + 1 : value); } // skip possible leading slash in prefix (NOTE: stolen pointer. Do not free.)
932
                  else if (strncmp (token, "uid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms->uid     = (int) read_integer (value); }
15 pmbaty 933
                  else if (strncmp (token, "gid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms->gid     = (int) read_integer (value); }
934
                  else if (strncmp (token, "perms=",   6) == 0) { REACH_TOKEN_VALUE (); entry_parms->perms   = (int) read_integer (value); }
935
                  else if (strcmp (token, "+followlink") == 0) entry_parms->should_follow_symlinks = true;
936
                  else if (strcmp (token, "-followlink") == 0) entry_parms->should_follow_symlinks = false;
937
                  else if (strcmp (token, "+keeplinked") == 0) entry_parms->should_keep_ld_output = true;
938
                  else if (strcmp (token, "-keeplinked") == 0) entry_parms->should_keep_ld_output = false;
22 pmbaty 939
                  else LOG_WARNING ("unimplemented bootstrap executable attribute in \"%s\" line %zd of inline document '%s': '%s'", buildfile_pathname, 1 + line_index, stored_pathname, token);
15 pmbaty 940
                  #undef REACH_TOKEN_VALUE
16 pmbaty 941
                  token = strtok_r (NULL, RECORD_SEP, &ctx); // proceed to next attribute token
15 pmbaty 942
               }
943
 
20 pmbaty 944
               if (is_end_of_line)
945
                  continue; // if end of line was reached, proceed to the next line
946
               else
947
                  read_ptr++; // else reach the next character (after the NUL split) and continue processing the same line
15 pmbaty 948
            } // end of "this line starts with an attributes block"
949
 
20 pmbaty 950
            // at this point we are past the attributes block
951
 
952
            // reset contextual argv/envp arrays
953
            line_argv.args = NULL;
954
            line_argv.count = 0;
955
            line_envp.args = NULL;
956
            line_envp.count = 0;
957
 
958
            // now read each word (or quoted group of words), unescaping escaped characters
959
            while (*read_ptr != 0)
15 pmbaty 960
            {
20 pmbaty 961
               while ((*read_ptr != 0) && isspace (*read_ptr))
962
                  read_ptr++; // skip intermediate spaces and reach the next word
963
 
964
               if (*read_ptr == '#')
965
                  break; // if the rest of the line is commented out, stop parsing it and proceed to the next line
966
 
967
               linebit_start = read_ptr; // remember the word (or quoted group of words) starts here
968
               write_ptr = read_ptr;
969
               is_quoted_context = (*read_ptr == '"'); // see if we're entering a quoted context or not
15 pmbaty 970
               if (is_quoted_context)
20 pmbaty 971
                  read_ptr++; // skip a possible initial quote in the word
22 pmbaty 972
               while ((*read_ptr != 0) && ((!is_quoted_context && !isspace (*read_ptr)) || (is_quoted_context && (*read_ptr != '"'))))
15 pmbaty 973
               {
20 pmbaty 974
                  if (*read_ptr == '\\')
975
                     read_ptr++; // unescape characters that are escaped with '\' by advancing the read pointer
976
                  *write_ptr++ = *read_ptr++; // recopy characters as we read them
15 pmbaty 977
               }
20 pmbaty 978
               is_end_of_line = (*read_ptr == 0); // see if we're at the end of line already
979
               *write_ptr = 0; // stop the rewritten string here
15 pmbaty 980
 
20 pmbaty 981
               // end of word, i.e. we reached either a closing quote or a space. The string bit has been rewritted at linebit_start without quotes and with characters unescaped.
982
 
983
               if ((strchr (linebit_start, '=') != NULL) && (line_argv.count == 0)) // is it an assignation AND have we not started constructing argv yet?
15 pmbaty 984
               {
20 pmbaty 985
                  STRINGARRAY_PUSH (&line_envp, linebit_start); // linebit_start is of the form "NAME=VALUE": it's an environment variable assignation
986
                  LOG_DEBUG ("collected envp: [%s]", linebit_start);
987
               }
988
               else // it's an executable argument (argv)
989
               {
990
                  STRINGARRAY_PUSH (&line_argv, linebit_start); // linebit_start is either NOT of the form "NAME=VALUE" OR we started constructing argv: it's a command-line argument
991
                  LOG_DEBUG ("collected argv: [%s]", linebit_start);
992
               }
15 pmbaty 993
 
20 pmbaty 994
               if (!is_end_of_line)
995
                  read_ptr++; // if we haven't reach the end of the line yet, advance to the next character (after the NUL split)
996
            } // end while (*read_ptr != 0)
15 pmbaty 997
 
20 pmbaty 998
            // we finished parsing the line
15 pmbaty 999
 
20 pmbaty 1000
            // did we fill an executable argv? As per QNX docs, the first executable must be startup-*, the last executable must be procnto.
1001
            if (line_argv.count > 0)
1002
            {
1003
               if (startup_argv.args == NULL)
1004
               {
1005
                  startup_argv.args = line_argv.args; // relocate these pointers to the right place
1006
                  startup_argv.count = line_argv.count;
1007
                  startup_envp.args = line_envp.args; // relocate these pointers to the right place
1008
                  startup_envp.count = line_envp.count;
15 pmbaty 1009
               }
20 pmbaty 1010
               else
15 pmbaty 1011
               {
20 pmbaty 1012
                  STRINGARRAY_FREE (&procnto_argv); // if procnto's argv was already assigned, free the previous array as we'll be replacing it with a new one
1013
                  procnto_argv.args = line_argv.args; // relocate these pointers to the right place
1014
                  procnto_argv.count = line_argv.count;
1015
                  STRINGARRAY_FREE (&procnto_envp); // if procnto's envp was already assigned, free the previous array as we'll be replacing it with a new one
1016
                  procnto_envp.args = line_envp.args; // relocate these pointers to the right place
1017
                  procnto_envp.count = line_envp.count;
1018
               }
1019
               line_argv.args = NULL; // void the line_argv array so as to not free it as we stole its args pointers
1020
               line_argv.count = 0;
1021
               line_envp.args = NULL; // void the line_envp array so as to not free it as we stole its args pointers
1022
               line_envp.count = 0;
1023
            }
1024
            else // this line contained no executable invokation, so stack up its envp assignations into the global envp array
1025
               for (array_index = 0; array_index < line_envp.count; array_index++)
1026
                  STRINGARRAY_PUSH (&global_envp, line_envp.args[array_index]);
15 pmbaty 1027
 
20 pmbaty 1028
            // release the contextual argv/envp arrays
1029
            STRINGARRAY_FREE (&line_argv);
1030
            STRINGARRAY_FREE (&line_envp);
15 pmbaty 1031
 
20 pmbaty 1032
         } // end for (line_index = 0; Buffer_GetNthLine (&entry_parms->data, line_index, &current_line); line_index++)
22 pmbaty 1033
         Buffer_Forget (&entry_parms->data); // free the inline specification once it's parsed
15 pmbaty 1034
 
20 pmbaty 1035
         ASSERT (startup_argv.args && startup_argv.args[0] && *startup_argv.args[0], "the QNX startup executable (startup-*) is missing in this bootstrap inline specification");
1036
         ASSERT (procnto_argv.args && procnto_argv.args[0] && *procnto_argv.args[0], "the QNX kernel (procnto-*) is missing in this bootstrap inline specification");
15 pmbaty 1037
 
1038
         // now we know which startup and procnto executables to use
20 pmbaty 1039
         LOG_DEBUG ("Startup: %s", startup_argv.args[0]);
1040
         LOG_DEBUG ("Kernel: %s",  procnto_argv.args[0]);
15 pmbaty 1041
 
1042
         static thread_local char linker_pathname[MAXPATHLEN] = "";
1043
         static thread_local char linker_sysroot_arg[MAXPATHLEN] = "";
1044
         static thread_local char linker_script_pathname_arg[MAXPATHLEN] = "";
1045
         static thread_local char procnto_buildhost_pathname[MAXPATHLEN] = "";
1046
         static thread_local char procnto_sym_filename[MAXPATHLEN] = "";
20 pmbaty 1047
         buffer_t bootargs_buffer = { 0 };
1048
         char *bootargs_location;
15 pmbaty 1049
 
1050
         // construct the arguments that are based on environment variables (infer QNX_HOST from QNX_TARGET)
1051
#if defined(_WIN32)
22 pmbaty 1052
         sprintf_s (linker_pathname, sizeof (linker_pathname), "%s/../../host/win64/x86_64/usr/bin/%s-ld" /*"-2.41.0"*/ ".exe", QNX_TARGET, (strcmp (image_processor, "x86_64") == 0 ? "x86_64-pc-nto-qnx8.0.0" : "aarch64-unknown-nto-qnx8.0.0")); // Win32: note the .exe extension
15 pmbaty 1053
#elif defined(__linux__)
22 pmbaty 1054
         sprintf_s (linker_pathname, sizeof (linker_pathname), "%s/../../host/linux/x86_64/usr/bin/%s-ld" /*"-2.41.0"*/, QNX_TARGET, (strcmp (image_processor, "x86_64") == 0 ? "x86_64-pc-nto-qnx8.0.0" : "aarch64-unknown-nto-qnx8.0.0"));
15 pmbaty 1055
#elif defined(__QNXNTO__)
22 pmbaty 1056
         sprintf_s (linker_pathname, sizeof (linker_pathname), "%s/../../host/qnx8/x86_64/usr/bin/%s-ld" /*"-2.41.0"*/, QNX_TARGET, (strcmp (image_processor, "x86_64") == 0 ? "x86_64-pc-nto-qnx8.0.0" : "aarch64-unknown-nto-qnx8.0.0"));
15 pmbaty 1057
#else // wtf are you building this on?
1058
#error Please port the GNU linker x86_64-pc-nto-qnx8.0.0-ld and aarch64-unknown-nto-qnx8.0.0-ld to your host architecture first before compiling ifstool.
1059
#endif
16 pmbaty 1060
         ASSERT (access (linker_pathname, 0) == 0, "host cross-linker for QNX8 \"%s\" not found", linker_pathname);
20 pmbaty 1061
         sprintf_s (linker_sysroot_arg, sizeof (linker_sysroot_arg), "--sysroot=%s/%s/", QNX_TARGET, image_processor);
1062
         sprintf_s (linker_script_pathname_arg, sizeof (linker_script_pathname_arg), "-T%s/%s/lib/nto.link", QNX_TARGET, image_processor);
15 pmbaty 1063
 
20 pmbaty 1064
         resolved_pathname = resolve_pathname (procnto_argv.args[0], entry_parms->search); // locate the procnto kernel location
1065
         ASSERT (resolved_pathname, "QNX kernel \"%s\" not found in search path", procnto_argv.args[0]);
1066
         strcpy_s (procnto_buildhost_pathname, sizeof (procnto_buildhost_pathname), resolved_pathname);
15 pmbaty 1067
 
20 pmbaty 1068
         sprintf_s (procnto_sym_filename, sizeof (procnto_sym_filename), "%s.sym%s", procnto_argv.args[0], sym_suffix);
15 pmbaty 1069
 
20 pmbaty 1070
         // construct the linker invokation command-line arguments array (argv)
1071
         STRINGARRAY_INIT (&linker_argv);
1072
         STRINGARRAY_PUSH (&linker_argv, strrchr (linker_pathname, '/') + 1); // "${TARGET_TRIPLE}-ld"
1073
         STRINGARRAY_PUSH (&linker_argv, linker_sysroot_arg); // "--sysroot=${QNX_TARGET}/${TARGET_CPU}/"
1074
         STRINGARRAY_PUSH (&linker_argv, linker_script_pathname_arg); // "-T${QNX_TARGET}/${TARGET_CPU}/lib/nto.link"
1075
         STRINGARRAY_PUSH (&linker_argv, "--section-start");
1076
         STRINGARRAY_PUSH (&linker_argv, ".text=0xffff800000001000");
1077
         STRINGARRAY_PUSH (&linker_argv, "--no-relax");
1078
         STRINGARRAY_PUSH (&linker_argv, procnto_buildhost_pathname); // "${QNX_TARGET}/${TARGET_CPU}/boot/sys/procnto-smp-instr"
1079
         STRINGARRAY_PUSH (&linker_argv, "-o");
1080
         STRINGARRAY_PUSH (&linker_argv, procnto_sym_filename); // "procnto-smp-instr.sym"
22 pmbaty 1081
#ifdef __GNUC__
1082
#pragma GCC diagnostic push
1083
#pragma GCC diagnostic ignored "-Wnonnull" // the GCC linter is wrong here: I *do* check for NULL before calling strdup()
1084
#endif // __GNUC__
20 pmbaty 1085
         STRINGARRAY_PUSH (&linker_argv, NULL); // don't forget to terminate the argv array with a NULL pointer
22 pmbaty 1086
#ifdef __GNUC__
1087
#pragma GCC diagnostic pop
1088
#endif // __GNUC__
15 pmbaty 1089
         if (verbose_level > 2)
1090
         {
1091
            fprintf (stderr, "ifstool: calling:");
20 pmbaty 1092
            for (array_index = 0; array_index < linker_argv.count - 1; array_index++)
1093
               fprintf (stderr, " '%s'", linker_argv.args[array_index]);
15 pmbaty 1094
            fputc ('\n', stderr);
1095
         }
20 pmbaty 1096
#ifdef _WIN32
1097
         _spawnv (_P_WAIT, linker_pathname, linker_argv.args); // spawn the linker and produce a stripped procnto (wait for completion)
1098
#else // !_WIN32, thus POSIX
1099
         do {
1100
            int status;
1101
            pid_t pid = fork (); // duplicate ourselves so as to create a new process
1102
            ASSERT_WITH_ERRNO (pid != -1);
22 pmbaty 1103
            if (pid == 0) // we are the child
20 pmbaty 1104
            {
22 pmbaty 1105
               execv (linker_pathname, linker_argv.args); // execute the linker and produce a stripped procnto (wait for completion)
20 pmbaty 1106
               DIE_WITH_EXITCODE (1, "execve() failed"); // exec never returns
1107
            }
22 pmbaty 1108
            else // we are the parent
20 pmbaty 1109
               waitpid (pid, &status, 0); // wait for the child to finish
1110
         } while (0);
1111
#endif // _WIN32
1112
         STRINGARRAY_FREE (&linker_argv);
16 pmbaty 1113
         if (!Buffer_ReadFromFile (&entry_parms->data, procnto_sym_filename)) // load the output file
15 pmbaty 1114
            DIE_WITH_EXITCODE (1, "the host cross-linker failed to produce a readable stripped \"%s\" kernel: %s", procnto_sym_filename, strerror (errno));
1115
         if (!entry_parms->should_keep_ld_output)
1116
            unlink (procnto_sym_filename); // remove the linker output file if we want to
17 pmbaty 1117
 
20 pmbaty 1118
         // save the boot arguments. The magic to look for is "ddpvbskr" -- whatever that means
1119
         if ((bootargs_location = Buffer_FindFirstByteArray (&entry_parms->data, "ddpvbskr")) == NULL)
1120
            DIE_WITH_EXITCODE (1, "unable to find boot args location in the stripped \"%s\" kernel", stored_pathname);
1121
         Buffer_InitWithSize (&bootargs_buffer, sizeof (bootargs_entry_t)); // prepare a boot args entry
1122
         ((bootargs_entry_t *) bootargs_buffer.bytes)->argc = (uint8_t) procnto_argv.count;
1123
         ((bootargs_entry_t *) bootargs_buffer.bytes)->envc = (uint8_t) (global_envp.count + procnto_envp.count);
1124
         ((bootargs_entry_t *) bootargs_buffer.bytes)->shdr_addr = (uint32_t) (image_base + bootfile_size); // same value as startup_header.image_paddr (which is not set yet) (TODO: support 64-bit shdr_addr offsets -- see comment in bootargs_entry_t struct)
1125
         for (array_index = 0; array_index < procnto_argv.count; array_index++)
1126
            ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, procnto_argv.args[array_index], strlen (procnto_argv.args[array_index]) + 1)); // append string including NUL terminator
1127
         for (array_index = 0; array_index < global_envp.count; array_index++)
1128
            ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, global_envp.args[array_index], strlen (global_envp.args[array_index]) + 1)); // append string including NUL terminator
1129
         for (array_index = 0; array_index < procnto_envp.count; array_index++)
1130
            ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, procnto_envp.args[array_index], strlen (procnto_envp.args[array_index]) + 1)); // append string including NUL terminator
1131
         ((bootargs_entry_t *) bootargs_buffer.bytes)->size_hi = (uint8_t) ((bootargs_buffer.size >> 8) & 0xff);
1132
         ((bootargs_entry_t *) bootargs_buffer.bytes)->size_lo = (uint8_t) ((bootargs_buffer.size >> 0) & 0xff);
1133
         ASSERT_WITH_ERRNO (Buffer_WriteBufferAt (&entry_parms->data, (size_t) bootargs_location - (size_t) entry_parms->data.bytes, &bootargs_buffer));
1134
         Buffer_Forget (&bootargs_buffer); // release the boot args buffer once it's written
1135
 
17 pmbaty 1136
         // now strip this prelinked ELF kernel file
20 pmbaty 1137
         ASSERT_WITH_ERRNO (Buffer_StripELFFile (&entry_parms->data, (const char **) saved_ELF_sections, 1, true, stored_pathname)); // strip the ELF file as per QNX docs (only keep ONE section, which is "QNX_info", and align the segment size in file with the size it occupies in memory)
17 pmbaty 1138
 
20 pmbaty 1139
         sprintf_s (candidate_pathname, MAXPATHLEN, "%s/%s", (entry_parms->prefix != NULL ? entry_parms->prefix : ""), procnto_argv.args[0]); // fix the entry name
1140
         stored_pathname = candidate_pathname;
1141
 
1142
         entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE; // mark this inode as a preprocessed *bootstrap* ELF file
1143
         entry_parms->st_mode = S_IFREG | entry_parms->perms; // procnto is a regular file
1144
         image_kernel_ino = entry_parms->extra_ino_flags | (inode_count + 1);
1145
 
1146
         STRINGARRAY_FREE (&procnto_argv); // release procnto's argv array
1147
         STRINGARRAY_FREE (&procnto_envp); // release procnto's envp array
22 pmbaty 1148
         //STRINGARRAY_FREE (&global_envp); // DO NOT release the global envp array. It is inherited by the boot scripts.
1149
      } // end of "is bootstrap file"
1150
      else if (entry_parms->is_compiled_bootscript) // else is it a startup script that we need to compile ?
1151
      {
7 pmbaty 1152
         image_bootscript_ino = inode_count + 1; // save boot script inode number for image header
22 pmbaty 1153
         Buffer_Initialize (&compiled_script);
2 pmbaty 1154
 
22 pmbaty 1155
         // parse buffer (non-destructively) line after line
1156
         Buffer_Initialize (&current_line);
1157
         for (line_index = 0; Buffer_GetNthLine (&entry_parms->data, line_index, &current_line); line_index++)
1158
         {
1159
            read_ptr = current_line.bytes;
1160
            while (isspace (*read_ptr))
1161
               read_ptr++; // skip leading spaces
1162
            if ((*read_ptr == '#') || (*read_ptr == 0))
1163
               continue; // skip comments and empty lines
1164
 
1165
            // format of a line: [attributes] [env assignation] [...] [executable] [arg] [...] [&] [comment]
1166
            // example: "[pri=20f] devc-con -n9 &"
1167
 
1168
            LOG_DEBUG ("parsing line: %s", read_ptr);
1169
            Buffer_Initialize (&compiled_scriptline);
1170
            memcpy (&current_scriptcmd_params, &default_scriptcmd_params, sizeof (default_scriptcmd_params));
1171
 
1172
            // does this line start with an attribute block ?
1173
            if (*read_ptr == '[')
1174
            {
1175
               read_ptr++; // skip the leading square bracket
1176
               linebit_start = read_ptr; // remember where it starts
1177
               is_quoted_context = false; // reach the next unescaped closing square bracket that is not between quotes
1178
               while ((*read_ptr != 0) && !((*read_ptr == ']') && (read_ptr[-1] != '\\') && !is_quoted_context))
1179
               {
1180
                  if (*read_ptr == '"')
1181
                     is_quoted_context ^= true; // remember when we're between quotes
1182
                  else if (!is_quoted_context && (*read_ptr == ' '))
1183
                     *read_ptr = RECORD_SEP[0]; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
1184
                  read_ptr++; // reach the next unescaped closing square bracket
1185
               }
1186
               if (*read_ptr != ']')
1187
               {
1188
                  LOG ("warning", 0, "syntax error in \"%s\" line %zd of inline document '%s': unterminated attributes block (skipping)", buildfile_pathname, 1 + line_index, stored_pathname);
1189
                  continue; // invalid attribute block, skip line
1190
               }
1191
               is_end_of_line = (*read_ptr == 0); // see if we're at the end of line already
1192
               *read_ptr = 0; // end the attribute block in all cases so that it is a parsable C string
1193
 
1194
               // now parse the attribute tokens
1195
               token = strtok_r (linebit_start, RECORD_SEP, &ctx);
1196
               while (token != NULL)
1197
               {
1198
                  #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
1199
                  if (false) {}
1200
                  else if (strncmp (token, "argv0=",      6) == 0) { REACH_TOKEN_VALUE (); current_scriptcmd_params.argv0      = value; } // NOTE: stolen pointer. Do not free.
1201
                  else if (strncmp (token, "cpu=",        4) == 0) { REACH_TOKEN_VALUE (); current_scriptcmd_params.cpu_number = (int) atoi (value); }
1202
                  else if (strncmp (token, "pri=",        4) == 0) { REACH_TOKEN_VALUE (); current_scriptcmd_params.priority   = (int) strtol (value, &ctx, 0); if (ctx != NULL) current_scriptcmd_params.sched_policy = (*ctx == 'f' ? SCRIPTCMD_SCHEDULERPOLICY_FIFO : SCRIPTCMD_SCHEDULERPOLICY_RR); }
1203
                  else if (strncmp (token, "sched_aps=", 10) == 0) { REACH_TOKEN_VALUE ();
1204
                     for (array_index = 0; array_index < aps_partnames.count; array_index++) if (strcmp (aps_partnames.args[array_index], value) == 0) break;
1205
                     if (array_index == aps_partnames.count)
1206
                        DIE_WITH_EXITCODE (1, "syntax error in \"%s\" line %zd of inline document '%s': APS partition name '%s' not found: please declare it first", buildfile_pathname, 1 + line_index, stored_pathname, value); // consistency check (TODO: check that the sum of all budgets don't exceed 100%)
1207
                     current_scriptcmd_params.aps_partindex = (int) array_index;
1208
                  }
1209
                  else if (strcmp (token, "+external") == 0) current_scriptcmd_params.is_external = true;
1210
                  else if (strcmp (token, "-external") == 0) current_scriptcmd_params.is_external = false;
1211
                  else if (strcmp (token, "+session")  == 0) current_scriptcmd_params.is_session_leader = true;
1212
                  else if (strcmp (token, "-session")  == 0) current_scriptcmd_params.is_session_leader = false;
1213
                  else if (strcmp (token, "+debug")    == 0) current_scriptcmd_params.has_debug_flag = true;
1214
                  else if (strcmp (token, "-debug")    == 0) current_scriptcmd_params.has_debug_flag = false;
1215
                  else LOG_WARNING ("unimplemented boot script modifier in \"%s\" line %zd of inline document '%s': '%s'", buildfile_pathname, 1 + line_index, stored_pathname, token);
1216
                  #undef REACH_TOKEN_VALUE
1217
                  token = strtok_r (NULL, RECORD_SEP, &ctx); // proceed to next attribute token
1218
               }
1219
 
1220
               if (is_end_of_line)
1221
                  continue; // if end of line was reached, proceed to the next line
1222
               else
1223
                  read_ptr++; // else reach the next character (after the NUL split) and continue processing the same line
1224
            } // end of "this line starts with an attributes block"
1225
 
1226
            // at this point we are past the attributes block
1227
 
1228
            // reset contextual argv/envp arrays
1229
            line_argv.args = NULL;
1230
            line_argv.count = 0;
1231
            line_envp.args = NULL;
1232
            line_envp.count = 0;
1233
 
1234
            // now read each word (or quoted group of words), unescaping escaped characters
1235
            while (*read_ptr != 0)
1236
            {
1237
               while ((*read_ptr != 0) && isspace (*read_ptr))
1238
                  read_ptr++; // skip intermediate spaces and reach the next word
1239
 
1240
               if (*read_ptr == '#')
1241
                  break; // if the rest of the line is commented out, stop parsing it and proceed to the next line
1242
 
1243
               linebit_start = read_ptr; // remember the word (or quoted group of words) starts here
1244
               write_ptr = read_ptr;
1245
               is_quoted_context = (*read_ptr == '"'); // see if we're entering a quoted context or not
1246
               if (is_quoted_context)
1247
                  read_ptr++; // skip a possible initial quote in the word
1248
               while ((*read_ptr != 0) && ((!is_quoted_context && !isspace (*read_ptr)) || (is_quoted_context && (*read_ptr != '"'))))
1249
               {
1250
                  if (*read_ptr == '\\')
1251
                     read_ptr++; // unescape characters that are escaped with '\' by advancing the read pointer
1252
                  *write_ptr++ = *read_ptr++; // recopy characters as we read them
1253
               }
1254
               is_end_of_line = (*read_ptr == 0); // see if we're at the end of line already
1255
               *write_ptr = 0; // stop the rewritten string here
1256
 
1257
               // end of word, i.e. we reached either a closing quote or a space. The string bit has been rewritted at linebit_start without quotes and with characters unescaped.
1258
               STRINGARRAY_PUSH (&line_argv, linebit_start);
1259
               LOG_DEBUG ("collected bootscript argv: [%s]", linebit_start);
1260
 
1261
               if (!is_end_of_line)
1262
                  read_ptr++; // if we haven't reach the end of the line yet, advance to the next character (after the NUL split)
1263
            } // end while (*read_ptr != 0)
1264
 
1265
            // we finished parsing the line
1266
 
1267
            // did we fill an executable argv? As per QNX docs, the first executable must be startup-*, the last executable must be procnto.
1268
            if (line_argv.count > 0)
1269
            {
1270
               // is it one of the few builtin commands ?
1271
               if (!current_scriptcmd_params.is_external && (strcmp (line_argv.args[0], "waitfor") == 0))
1272
               {
1273
                  if (line_argv.count < 2)
1274
                     DIE_WITH_EXITCODE (1, "syntax error in \"%s\" line %zd of inline document '%s': waitfor requires 1 or 2 arguments", buildfile_pathname, 1 + line_index, stored_pathname);
1275
 
1276
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_WAITFOR "\x00", 4)); // size as u16LE, type, spare
1277
                  wait_time = (line_argv.count > 2 ? (size_t) (10.0 * atof (line_argv.args[2])) : 50); // convert dotted number to tenths of seconds. Default to 5 seconds (50 tenths)
1278
                  if (wait_time > 0xffff)
1279
                     wait_time = 0xffff;
1280
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 4, (wait_time >> 0) & 0xff)); // wait time lo
1281
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 5, (wait_time >> 8) & 0xff)); // wait time hi
1282
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[1], strlen (line_argv.args[1]) + 1));
1283
               }
1284
               else if (!current_scriptcmd_params.is_external && (strcmp (line_argv.args[0], "reopen") == 0))
1285
               {
1286
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_REOPEN "\x00", 4)); // size as u16LE, type, spare
1287
                  wait_time = (line_argv.count > 2 ? (size_t) (10.0 * atof (line_argv.args[2])) : 50); // convert dotted number to tenths of seconds. Default to 5 seconds (50 tenths)
1288
                  if (wait_time > 0xffff)
1289
                     wait_time = 0xffff;
1290
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 4, (wait_time >> 0) & 0xff)); // wait time lo
1291
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 5, (wait_time >> 8) & 0xff)); // wait time hi
1292
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, (line_argv.count > 1 ? line_argv.args[1] : "/dev/console"), strlen (line_argv.count > 1 ? line_argv.args[1] : "/dev/console") + 1));
1293
               }
1294
               else if (!current_scriptcmd_params.is_external && (strcmp (line_argv.args[0], "display_msg") == 0))
1295
               {
1296
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_DISPLAY_MSG "\x00", 4)); // size as u16LE, type, spare
1297
                  for (array_index = 1; array_index < line_argv.count; array_index++)
1298
                  {
1299
                     if (array_index > 1)
1300
                        ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&compiled_scriptline, " ")); // separate each arg with a space
1301
                     ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[array_index], strlen (line_argv.args[array_index])));
1302
                  }
1303
                  ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&compiled_scriptline, "\n\0")); // don't forget to append a newline to the message printed
1304
               }
1305
               else if (!current_scriptcmd_params.is_external && (strcmp (line_argv.args[0], "procmgr_symlink") == 0))
1306
               {
1307
                  if (line_argv.count < 3)
1308
                     DIE_WITH_EXITCODE (1, "syntax error in \"%s\" line %zd of inline document '%s': procmgr_symlink requires 2 arguments", buildfile_pathname, 1 + line_index, stored_pathname);
1309
 
1310
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_PROCMGR_SYMLINK "\x00", 4)); // size as u16LE, type, spare
1311
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[1], strlen (line_argv.args[1]) + 1));
1312
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[2], strlen (line_argv.args[2]) + 1));
1313
               }
1314
               else if (!current_scriptcmd_params.is_external && (strcmp (line_argv.args[0], "sched_aps") == 0))
1315
               {
1316
                  token = (line_argv.count > 1 ? line_argv.args[1] : "System");
1317
                  if ((strlen (token) > 15) || (strchr (token, '/') != NULL))
1318
                     DIE_WITH_EXITCODE (1, "syntax error in \"%s\" line %zd of inline document '%s': APS partition names must be less than 16 characters long and not contain a '/' separator", buildfile_pathname, 1 + line_index, stored_pathname); // consistency check (TODO: check that the sum of all budgets don't exceed 100%)
1319
                  for (array_index = 0; array_index < aps_partnames.count; array_index++)
1320
                     if (strcmp (aps_partnames.args[array_index], token) == 0)
1321
                        break; // find the APS partition ID in the global APS partition names table
1322
                  if (array_index == aps_partnames.count)
1323
                     STRINGARRAY_PUSH (&aps_partnames, token); // if not found, add a new partition name to the table
1324
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_EXTSCHED_APS "\x00", 4)); // size as u16LE, type, spare
1325
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 4, 0)); // parent (system partition)
1326
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 5, (line_argv.count > 2 ? (uint8_t) atoi (line_argv.args[2]) : 0))); // budget
1327
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 6, ((line_argv.count > 3 ? (uint8_t) atoi (line_argv.args[3]) : 0) >> 0) & 0xff)); // critical lo
1328
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 7, ((line_argv.count > 3 ? (uint8_t) atoi (line_argv.args[3]) : 0) >> 8) & 0xff)); // critical hi
1329
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 8, (uint8_t) array_index)); // APS partition ID
1330
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, token, strlen (token) + 1)); // partition name
1331
               }
1332
               else // not a builtin, which means it is an external command
1333
               {
1334
                  if (strcmp (line_argv.args[line_argv.count - 1], "&") == 0) // is the last argument an ampersand (fork sign) on its own ? (variant 1)
1335
                  {
1336
                     current_scriptcmd_params.is_background_task = true; // remember this is a background task
1337
                     free (line_argv.args[line_argv.count - 1]); // prevent leaking the last arg
1338
                     line_argv.count--; // and adjust the arg count
1339
                  }
1340
                  else if (((token = strrchr (line_argv.args[line_argv.count - 1], '&')) != NULL) && (token[1] == 0)) // else does the last argument END with a fork sign ? (variant 2)
1341
                  {
1342
                     current_scriptcmd_params.is_background_task = true; // remember this is a background task
1343
                     *token = 0; // and chop off the ampersand from that arg
1344
                  }
1345
 
1346
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_EXTERNAL "\x00", 4)); // size as u16LE, type, spare
1347
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 4, (current_scriptcmd_params.cpu_number != -1 ? (uint8_t) current_scriptcmd_params.cpu_number : 0))); // CPU
1348
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 5, (current_scriptcmd_params.aps_partindex != -1 ? SCRIPTCMD_FLAG_EXTSCHED   : 0)
1349
                                                                                | (current_scriptcmd_params.is_session_leader   ? SCRIPTCMD_FLAG_SESSION    : 0)
1350
                                                                                | (current_scriptcmd_params.sched_policy  != -1 ? SCRIPTCMD_FLAG_SCHED_SET  : 0)
1351
                                                                                | (current_scriptcmd_params.cpu_number    != -1 ? SCRIPTCMD_FLAG_CPU_SET    : 0)
1352
                                                                                | (current_scriptcmd_params.is_background_task  ? SCRIPTCMD_FLAG_BACKGROUND : 0)
1353
                                                                                | (current_scriptcmd_params.has_debug_flag      ? SCRIPTCMD_FLAG_KDEBUG     : 0))); // flags
1354
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 6, (current_scriptcmd_params.aps_partindex != -1 ? (uint8_t) current_scriptcmd_params.aps_partindex : 0))); // adaptative partitioning ID
1355
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 7, 0)); // reserved
1356
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 8, (current_scriptcmd_params.sched_policy != -1 ? current_scriptcmd_params.sched_policy : 0))); // scheduling policy
1357
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 9, (current_scriptcmd_params.priority != -1 ? current_scriptcmd_params.priority : 0))); // scheduling priority
1358
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 10, (uint8_t) line_argv.count)); // argc
1359
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 11, (uint8_t) global_envp.count)); // envc
1360
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[0], strlen (line_argv.args[0]) + 1)); // executable
1361
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, (current_scriptcmd_params.argv0 != NULL ? current_scriptcmd_params.argv0 : line_argv.args[0]), strlen (current_scriptcmd_params.argv0 != NULL ? current_scriptcmd_params.argv0 : line_argv.args[0]) + 1)); // argv[0]
1362
                  for (array_index = 1; array_index < line_argv.count; array_index++)
1363
                     ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[array_index], strlen (line_argv.args[array_index]) + 1)); // argv[n]
1364
                  for (array_index = 0; array_index < global_envp.count; array_index++)
1365
                     ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, global_envp.args[array_index], strlen (global_envp.args[array_index]) + 1)); // envp[n]
1366
               }
1367
               ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&compiled_scriptline, ROUND_TO_UPPER_MULTIPLE (compiled_scriptline.size, 4))); // pad compiled command buffer to upper 32-bit multiple
1368
 
1369
               // fix the size of this compiled boot script command
1370
               ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 0, (compiled_scriptline.size >> 0) & 0xff)); // size lo
1371
               ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 1, (compiled_scriptline.size >> 8) & 0xff)); // size hi
1372
 
1373
               // now concatenate this newly compiled boot script line to the compiled boot script buffer
1374
               ASSERT_WITH_ERRNO (Buffer_AppendBuffer (&compiled_script, &compiled_scriptline));
1375
               Buffer_Forget (&compiled_scriptline);
1376
            }
1377
            else // this line contained no executable invokation, so make the parameters that changed the default ones
1378
            {
1379
               #define APPLY_DEFAULT_ATTR_NUM(attr,descr,fmt) do { if (current_scriptcmd_params.attr != default_scriptcmd_params.attr) { \
1380
                     LOG_INFO ("changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %zd of inline document '%s'", default_scriptcmd_params.attr, current_scriptcmd_params.attr, buildfile_pathname, 1 + line_index, stored_pathname); \
1381
                     default_scriptcmd_params.attr = current_scriptcmd_params.attr; \
1382
                  } } while (0)
1383
               #define APPLY_DEFAULT_ATTR_STR(attr,descr,fmt) do { if (((default_scriptcmd_params.attr == NULL) && (current_scriptcmd_params.attr != NULL)) || ((default_scriptcmd_params.attr != NULL) && (current_scriptcmd_params.attr == NULL)) || ((default_scriptcmd_params.attr != NULL) && (current_scriptcmd_params.attr != NULL) && (strcmp (current_scriptcmd_params.attr, default_scriptcmd_params.attr) != 0))) { \
1384
                  LOG_INFO ("changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %zd of inline document '%s'", (default_scriptcmd_params.attr != NULL ? default_scriptcmd_params.attr : "none"), current_scriptcmd_params.attr, buildfile_pathname, 1 + line_index, stored_pathname); \
24 pmbaty 1385
                     if (default_scriptcmd_params.attr != NULL) free (default_scriptcmd_params.attr); \
1386
                     default_scriptcmd_params.attr = strdup (current_scriptcmd_params.attr); \
1387
                     ASSERT_WITH_ERRNO (default_scriptcmd_params.attr != NULL); \
22 pmbaty 1388
                     default_scriptcmd_params.attr = current_scriptcmd_params.attr; \
1389
                  } } while (0)
1390
               APPLY_DEFAULT_ATTR_STR (argv0,                          "executable name",                 "\"%s\"");
1391
               APPLY_DEFAULT_ATTR_NUM (cpu_number,                     "CPU mask",                        "0%o");
1392
               APPLY_DEFAULT_ATTR_NUM (is_external,                    "external command flag",           "0%o");
1393
               APPLY_DEFAULT_ATTR_NUM (priority,                       "scheduling priority",             "0%o");
1394
               APPLY_DEFAULT_ATTR_NUM (sched_policy,                   "scheduling policy",               "0%o");
1395
               APPLY_DEFAULT_ATTR_NUM (aps_partindex,                  "APS partition index",             "0%o");
1396
               APPLY_DEFAULT_ATTR_NUM (is_session_leader,              "session leader flag",             "0%o");
1397
               APPLY_DEFAULT_ATTR_NUM (is_background_task,             "background task flag",            "0%o");
1398
               APPLY_DEFAULT_ATTR_NUM (has_debug_flag,                 "debug flag",                      "0%o");
1399
               #undef APPLY_DEFAULT_ATTR_STR
1400
               #undef APPLY_DEFAULT_ATTR_NUM
1401
            }
1402
 
1403
            // release the contextual argv/envp arrays
1404
            STRINGARRAY_FREE (&line_argv);
1405
            STRINGARRAY_FREE (&line_envp);
1406
 
1407
         } // end for (line_index = 0; Buffer_GetNthLine (&entry_parms->data, line_index, &current_line); line_index++)
1408
         Buffer_Forget (&entry_parms->data); // free the inline specification once it's parsed
1409
 
1410
         ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&compiled_script, "\x00\x00\x00\x00")); // terminate the compiled boot script with a 4-byte trailer
1411
         entry_parms->data.bytes = compiled_script.bytes; // and steal the compiled boot script buffer
1412
         entry_parms->data.size = compiled_script.size;
1413
      } // end of "is compiled bootscript"
1414
 
7 pmbaty 1415
      // do we already know the data for this data blob ?
11 pmbaty 1416
      if (entry_parms->data.bytes != NULL)
7 pmbaty 1417
      {
15 pmbaty 1418
         entry_parms->mtime = entry_parms->mtime_for_inline_files; // if so, set it a mtime equal to the mtime to use for inline files
16 pmbaty 1419
         LOG_INFO ("file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" blob (len %zd)", entry_parms->extra_ino_flags | (inode_count + 1), entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_parms->data.size);
7 pmbaty 1420
      }
8 pmbaty 1421
      else if (buildhost_pathname != NULL) // else was a source file pathname supplied ?
1 pmbaty 1422
      {
17 pmbaty 1423
         resolved_pathname = resolve_pathname (buildhost_pathname, entry_parms->search); // locate the file
7 pmbaty 1424
         if (resolved_pathname == NULL)
18 pmbaty 1425
         {
1426
            if (entry_parms->should_allow_nonexistent_files)
1427
            {
1428
               LOG_WARNING ("filesystem entry \"%s\" specified in \"%s\" line %d not found on build host: ignoring", buildhost_pathname, buildfile_pathname, lineno);
19 pmbaty 1429
               return; // if we're allowed to continue when a file to add doesn't exist, do so, else die with an error message
18 pmbaty 1430
            }
15 pmbaty 1431
            DIE_WITH_EXITCODE (1, "filesystem entry \"%s\" specified in \"%s\" line %d not found on build host: %s", buildhost_pathname, buildfile_pathname, lineno, strerror (errno));
18 pmbaty 1432
         }
16 pmbaty 1433
         if (!Buffer_ReadFromFile (&entry_parms->data, resolved_pathname))
25 pmbaty 1434
            DIE_WITH_EXITCODE (1, "filesystem entry \"%s\" specified in \"%s\" line %d can't be read from \"%s\": %s", buildhost_pathname, buildfile_pathname, lineno, resolved_pathname, strerror (errno));
16 pmbaty 1435
         stat (resolved_pathname, &stat_buf); // can't fail, since we could read it
7 pmbaty 1436
         if (entry_parms->mtime == UINT32_MAX)
1437
            entry_parms->mtime = (uint32_t) stat_buf.st_mtime;
16 pmbaty 1438
         LOG_INFO ("file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" buildhost_file \"%s\" (len %zd)", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, buildhost_pathname, entry_parms->data.size);
8 pmbaty 1439
      }
19 pmbaty 1440
      else
1441
         DIE_WITH_EXITCODE (1, "unexpected code path: can't store a file without neither explicit contents nor a host pathname. This is a bug in the program. Please contact the author.");
1 pmbaty 1442
 
9 pmbaty 1443
      // is the file we're storing an ELF file ?
16 pmbaty 1444
      #define ELFHDR ((elf_header_t *) entry_parms->data.bytes) // this convenient definition will make sure the ELF header points at the right location, even after entry_parms.data->byte is reallocated
1445
      if ((entry_parms->data.size > 52) // file is big enough to contain an ELF header
1446
          && (memcmp (ELF_GET_STRING (ELFHDR, ELFHDR, magic), ELF_MAGIC_STR, 4) == 0)) // file starts with the ELF magic
7 pmbaty 1447
      {
9 pmbaty 1448
         // is the file we're storing a relocatable executable (i.e. a dynamic library) and should we check for its canonical name ?
16 pmbaty 1449
         if ((ELF_GET_NUMERIC (ELFHDR, ELFHDR, type) == ELF_TYPE_DYNAMICLIB) && entry_parms->should_autosymlink_dylib)
7 pmbaty 1450
         {
9 pmbaty 1451
            // locate the sections we need (the dynamic section and its strings table)
16 pmbaty 1452
            const elf_section_header_t *shdr_dynamic = elf_get_section_header_by_name (ELFHDR, ".dynamic");
1453
            const elf_section_header_t *shdr_dynstr = elf_get_section_header_by_name (ELFHDR, ".dynstr");
7 pmbaty 1454
 
9 pmbaty 1455
            // make sure we have both the dynamic section header and its own strings table header
1456
            if ((shdr_dynamic != NULL) && (shdr_dynstr != NULL))
1457
            {
16 pmbaty 1458
               dynamic_strings = (char *) &entry_parms->data.bytes[ELF_GET_NUMERIC (ELFHDR, shdr_dynstr, file_offset)]; // quick access to dynamic sections strings table
7 pmbaty 1459
 
9 pmbaty 1460
               // walk through the dynamic section, look for the DT_SONAME entry
16 pmbaty 1461
               canonical_dylib_name = NULL; // assume none until told otherwise
1462
               for (elf_dynamic_section_entry_t *dynamic_entry = (elf_dynamic_section_entry_t *) &entry_parms->data.bytes[ELF_GET_NUMERIC (ELFHDR, shdr_dynamic, file_offset)];
1463
                    (ELF_GET_NUMERIC (ELFHDR, dynamic_entry, tag) != ELF_DT_NULL);
1464
                    dynamic_entry = (elf_dynamic_section_entry_t *) ((uint8_t *) dynamic_entry + ELF_STRUCT_SIZE (ELFHDR, dynamic_entry)))
1465
                  if (ELF_GET_NUMERIC (ELFHDR, dynamic_entry, tag) == ELF_DT_SONAME)
9 pmbaty 1466
                  {
16 pmbaty 1467
                     canonical_dylib_name = dynamic_strings + ELF_GET_NUMERIC (ELFHDR, dynamic_entry, value);
9 pmbaty 1468
                     break;
1469
                  }
1470
 
1471
               // do we have it ?
1472
               if ((canonical_dylib_name != NULL) && (canonical_dylib_name[0] != 0))
7 pmbaty 1473
               {
17 pmbaty 1474
                  sprintf_s (candidate_pathname, MAXPATHLEN, "%s/%s", (entry_parms->prefix != NULL ? entry_parms->prefix : ""), canonical_dylib_name);
9 pmbaty 1475
                  if (strcmp (candidate_pathname, stored_pathname) != 0) // claimed dylib name differs from passed name ?
1476
                  {
1477
                     original_stored_pathname = stored_pathname; // if so, remember to create a symlink here
1478
                     stored_pathname = candidate_pathname;
1479
                  }
7 pmbaty 1480
               }
1 pmbaty 1481
            }
9 pmbaty 1482
         } // end if the file we're storing is a dylib
10 pmbaty 1483
 
1484
         // now strip this ELF file if necessary
1485
         if (!(entry_parms->extra_ino_flags & IFS_INO_PROCESSED_ELF))
1486
         {
20 pmbaty 1487
            Buffer_StripELFFile (&entry_parms->data, (const char **) saved_ELF_sections, saved_ELF_section_count, false, stored_pathname); // strip the ELF file à la mkifs
10 pmbaty 1488
            entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF; // mark this inode as a preprocessed ELF file
1489
         } // end if the file is not yet a processed ELF
9 pmbaty 1490
      } // end if the file we're storing is an ELF file
16 pmbaty 1491
      #undef ELFHDR // undefine the macro that used to always point to the ELF header at the beginning of the file
1 pmbaty 1492
   }
1493
 
18 pmbaty 1494
   // have a pointer to where the stored pathname actually starts, without the leading slash
1495
   stored_pathname_without_leading_slash = stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname;
1496
 
1497
   // see if this item already has an entry in the current list of filesystem entries
1498
   for (fsentry_index = 0; fsentry_index < *fsentry_count; fsentry_index++)
1499
   {
1500
      fsentry = &(*fsentries)[fsentry_index]; // quick access to fs entry slot
1501
      if (   (S_ISDIR  (fsentry->header.mode) && (strcmp (fsentry->u.dir.path,     stored_pathname_without_leading_slash) == 0))
19 pmbaty 1502
          || (S_ISREG  (fsentry->header.mode) && (strcmp (fsentry->u.file.path,    stored_pathname_without_leading_slash) == 0))
1503
          || (S_ISLNK  (fsentry->header.mode) && (strcmp (fsentry->u.symlink.path, stored_pathname_without_leading_slash) == 0))
1504
          || (S_ISFIFO (fsentry->header.mode) && (strcmp (fsentry->u.symlink.path, stored_pathname_without_leading_slash) == 0)))
18 pmbaty 1505
         break; // stop searching as soon as we find a duplicate
1506
   }
1507
 
1508
   // is there already an entry for this item ?
1509
   if (fsentry_index < *fsentry_count)
1510
   {
1511
      // if we should NOT ignore duplicates, bomb out, else just reuse that entry
1512
      if (!entry_parms->should_ignore_duplicates)
1513
         DIE_WITH_EXITCODE (1, "duplicate detected: entry \"%s\" specified in \"%s\" line %d already exists in IFS file", stored_pathname, buildfile_pathname, lineno);
1514
   }
1515
   else // this is a new entry: grow filesystem entries array to hold one more slot
1516
   {
1517
      reallocated_ptr = realloc (*fsentries, (*fsentry_count + 1) * sizeof (fsentry_t)); // attempt to reallocate
1518
      ASSERT_WITH_ERRNO (reallocated_ptr); // verify
1519
      *fsentries = reallocated_ptr; // save reallocated pointer
1520
      fsentry = &(*fsentries)[*fsentry_count]; // quick access to fs entry slot
1521
      (*fsentry_count)++; // remember there's one entry more in the array
1522
   }
1523
 
1524
   // save (or update) this entry's parameters
1 pmbaty 1525
   fsentry->header.extattr_offset = 0;
10 pmbaty 1526
   fsentry->header.ino = entry_parms->extra_ino_flags | (++inode_count);
1 pmbaty 1527
   fsentry->header.mode = entry_parms->st_mode;
1528
   fsentry->header.gid = entry_parms->gid;
1529
   fsentry->header.uid = entry_parms->uid;
7 pmbaty 1530
   fsentry->header.mtime = (entry_parms->mtime == UINT32_MAX ? (uint32_t) time (NULL) : entry_parms->mtime);
1 pmbaty 1531
   if (S_ISDIR (entry_parms->st_mode))
1532
   {
18 pmbaty 1533
      fsentry->u.dir.path = strdup (stored_pathname_without_leading_slash);
16 pmbaty 1534
 
7 pmbaty 1535
      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 1536
      fsentry->UNSAVED_was_data_written = true; // no data to save
1537
   }
1538
   else if (S_ISREG (entry_parms->st_mode))
1539
   {
1540
      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
16 pmbaty 1541
      fsentry->u.file.size = (uint32_t) entry_parms->data.size;
18 pmbaty 1542
      fsentry->u.file.path = strdup (stored_pathname_without_leading_slash);
16 pmbaty 1543
      fsentry->u.file.UNSAVED_databuf = malloc (entry_parms->data.size);
1544
      ASSERT_WITH_ERRNO (fsentry->u.file.UNSAVED_databuf);
1545
      memcpy (fsentry->u.file.UNSAVED_databuf, entry_parms->data.bytes, entry_parms->data.size);
1546
 
7 pmbaty 1547
      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 1548
      fsentry->UNSAVED_was_data_written = false; // there *IS* data to save
1549
   }
1550
   else if (S_ISLNK (entry_parms->st_mode))
1551
   {
18 pmbaty 1552
      fsentry->u.symlink.sym_offset = (uint16_t) (strlen (stored_pathname_without_leading_slash) + 1);
16 pmbaty 1553
      fsentry->u.symlink.sym_size = (uint16_t) entry_parms->data.size;
18 pmbaty 1554
      fsentry->u.symlink.path = strdup (stored_pathname_without_leading_slash);
11 pmbaty 1555
      fsentry->u.symlink.contents = strdup (entry_parms->data.bytes);
16 pmbaty 1556
      ASSERT_WITH_ERRNO (fsentry->u.symlink.contents);
1557
 
7 pmbaty 1558
      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 1559
      fsentry->UNSAVED_was_data_written = true; // no data to save
1560
   }
7 pmbaty 1561
   else // necessarily a device node
1 pmbaty 1562
   {
11 pmbaty 1563
      fsentry->u.device.dev  = strtol (entry_parms->data.bytes, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
1564
      fsentry->u.device.rdev = strtol (strchr (entry_parms->data.bytes, ':') + 1, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
18 pmbaty 1565
      fsentry->u.device.path = strdup (stored_pathname_without_leading_slash);
16 pmbaty 1566
 
7 pmbaty 1567
      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 1568
      fsentry->UNSAVED_was_data_written = true; // no data to save
1569
   }
7 pmbaty 1570
 
1571
   // should we also add a symlink to this entry ? (in case we stored a dylib file under its canonical name)
1572
   if (original_stored_pathname != NULL)
1573
   {
1574
      entry_parms->is_compiled_bootscript = false;
1575
      entry_parms->should_autosymlink_dylib = false;
1576
      entry_parms->should_follow_symlinks = false;
1577
      entry_parms->st_mode = S_IFLNK | 0777; // NOTE: mkifs stores symlink permissions as rwxrwxrwx !
10 pmbaty 1578
      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 1579
      last_dirsep = strrchr (stored_pathname, '/');
11 pmbaty 1580
      old_data = entry_parms->data.bytes; // backup previous data pointer
1581
      entry_parms->data.bytes = (uint8_t *) (last_dirsep == NULL ? stored_pathname : last_dirsep + 1); // store symlink target in dirent data
16 pmbaty 1582
      entry_parms->data.size = strlen (entry_parms->data.bytes);
7 pmbaty 1583
      add_fsentry (fsentries, fsentry_count, entry_parms, original_stored_pathname, NULL);
11 pmbaty 1584
      entry_parms->data.bytes = old_data; // restore previous data pointer so that it can be freed normally
7 pmbaty 1585
   }
1586
 
19 pmbaty 1587
   return; // finished, return to our caller
1 pmbaty 1588
}
1589
 
1590
 
19 pmbaty 1591
static void add_directory_contents_recursively (fsentry_t **fsentries, size_t *fsentry_count, const char *dir_pathname, const size_t start_pathname_len, parms_t *default_parms)
1592
{
1593
   // adds the contents of the directory pointed to by dir_pathname to the fsentries array, recursively
1594
   // start_pathname_len is initialized to the length of dir_pathname by the top caller, and passed down unchanged,
1595
   // so that each sublevel of the recursion knows the depth of the relative path in which it is.
1596
 
1597
   thread_local static char item_pathname[MAXPATHLEN] = "";
1598
   thread_local static parms_t entry_parms = { 0 };
1599
   thread_local static struct stat stat_buf = { 0 };
1600
   thread_local static char major_minor[64];
1601
 
1602
   DIR *dirp;
1603
   struct dirent *dp;
1604
 
1605
   // open the directory
1606
   dirp = opendir (dir_pathname);
1607
   if (dirp == NULL)
1608
      DIE_WITH_EXITCODE (1, "unable to open directory \"%s\" for recursive inclusion", dir_pathname);
1609
 
1610
   // enumerate its contents
1611
   while ((dp = readdir (dirp)) != NULL)
1612
   {
1613
      if ((strcmp (dp->d_name, ".") == 0) || (strcmp (dp->d_name, "..") == 0))
1614
         continue; // skip self and parent
1615
 
1616
      memcpy (&entry_parms, default_parms, sizeof (parms_t));
1617
      sprintf_s (item_pathname, sizeof (item_pathname), "%s/%s", dir_pathname, dp->d_name); // construct item's pathname
1618
      ASSERT_WITH_ERRNO (stat (item_pathname, &stat_buf) == 0); // peek info about this entry (or die trying)
1619
      if (S_ISDIR (stat_buf.st_mode))
1620
      {
1621
         entry_parms.st_mode |= entry_parms.dperms; // apply DIRECTORY default permissions
1622
         add_fsentry (fsentries, fsentry_count, &entry_parms, &item_pathname[start_pathname_len], NULL); // add a filesystem entry of type "directory"
1623
         add_directory_contents_recursively (fsentries, fsentry_count, item_pathname, start_pathname_len, default_parms); // dwell into this directory and add its children recursively
1624
      }
1625
      else if (S_ISLNK (stat_buf.st_mode))
1626
      {
1627
         entry_parms.st_mode |= 0777; // NOTE: mkifs sets symlink permissions to rwxrwxrwx !?
1628
         add_fsentry (fsentries, fsentry_count, &entry_parms, &item_pathname[start_pathname_len], item_pathname); // add a filesystem entry of type "link"
1629
      }
1630
      else if (S_ISREG (stat_buf.st_mode))
1631
      {
1632
         entry_parms.st_mode |= entry_parms.perms; // apply FILE default permissions
1633
         add_fsentry (fsentries, fsentry_count, &entry_parms, &item_pathname[start_pathname_len], item_pathname); // add a filesystem entry of type "regular file"
1634
      }
1635
      else if (S_ISFIFO (stat_buf.st_mode))
1636
      {
1637
         entry_parms.st_mode |= entry_parms.perms; // apply FILE default permissions
1638
         sprintf_s (major_minor, sizeof (major_minor), "%u:%u", (unsigned int) major (stat_buf.st_rdev), (unsigned int) minor (stat_buf.st_rdev));
1639
         entry_parms.data.bytes = major_minor;
1640
         add_fsentry (fsentries, fsentry_count, &entry_parms, &item_pathname[start_pathname_len], NULL); // add a filesystem entry of type "FIFO"
1641
      }
1642
      else
1643
         LOG_WARNING ("ignoring unsupported directory entry: \"%s\" (type 0%o)", item_pathname, stat_buf.st_mode & S_IFMT);
1644
   }
1645
 
1646
   closedir (dirp); // finished parsing this level, close the directory handle
1647
   return; // and return to our caller
1648
}
1649
 
1650
 
1 pmbaty 1651
static int fsentry_compare_pathnames_cb (const void *a, const void *b)
1652
{
1653
   // qsort() callback that compares two imagefs filesystem entries and sort them alphabetically by pathname
1654
 
3 pmbaty 1655
   const fsentry_t *entry_a = (const fsentry_t *) a;
1656
   const fsentry_t *entry_b = (const fsentry_t *) b;
1 pmbaty 1657
   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)));
1658
   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)));
1659
   return (strcmp (pathname_a, pathname_b));
1660
}
1661
 
1662
 
17 pmbaty 1663
static void parse_line (FILE *buildfile_fp, char *line_buffer, fsentry_t **fsentries, size_t *fsentry_count, parms_t *default_parms)
1 pmbaty 1664
{
17 pmbaty 1665
   thread_local static char path_on_buildhost[MAXPATHLEN] = "";
1666
   thread_local static char path_in_ifs[MAXPATHLEN] = "";
1667
   thread_local 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 1668
 
17 pmbaty 1669
   bool should_discard_inline_contents;
1670
   bool is_quoted_context;
1671
   bool is_escaped_char;
1672
   struct stat stat_buf;
1673
   struct tm utc_time;
1674
   void *reallocated_ptr;
1675
   size_t allocated_size;
1676
   size_t string_len;
1677
   char *specifiedpathname_start;
1678
   char *attrblock_start;
1679
   char *write_ptr;
1680
   char *line_ptr;
1681
   char *value;
7 pmbaty 1682
   char *token;
17 pmbaty 1683
   char *sep;
1684
   char *ctx;
1685
   int read_char;
1686
 
1687
   line_ptr = line_buffer;
1688
   while ((*line_ptr != 0) && isspace (*line_ptr))
1689
      line_ptr++; // skip leading spaces
1690
 
1691
   if ((*line_ptr == 0) || (*line_ptr == '#'))
19 pmbaty 1692
      return; // don't process empty lines and comments
17 pmbaty 1693
 
1694
   string_len = (int) strlen (line_buffer);
1695
   if ((string_len > 0) && (line_buffer[string_len - 1] == '\n'))
1696
      line_buffer[string_len - 1] = 0; // chop off newline for easier debug output
1697
 
1698
   // reset entry values
1699
   memcpy (&entry_parms, default_parms, sizeof (parms_t));
1700
   path_in_ifs[0] = 0;
1701
   path_on_buildhost[0] = 0;
1702
   should_discard_inline_contents = false;
1703
 
1704
   // does this line start with an attribute block ?
1705
   if (*line_ptr == '[')
7 pmbaty 1706
   {
17 pmbaty 1707
      line_ptr++; // skip the leading square bracket
1708
      attrblock_start = line_ptr; // remember where it starts
1709
      is_quoted_context = false;
1710
      while ((*line_ptr != 0) && !((*line_ptr == ']') && (line_ptr[-1] != '\\') && !is_quoted_context))
1711
      {
1712
         if (*line_ptr == '"')
1713
            is_quoted_context ^= true; // remember when we're between quotes
1714
         else if (!is_quoted_context && (*line_ptr == ' '))
1715
            *line_ptr = RECORD_SEP[0]; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
1716
         line_ptr++; // reach the next unescaped closing square bracket
1717
      }
1718
      if (*line_ptr != ']')
1719
      {
1720
         LOG ("warning", 0, "syntax error in \"%s\" line %d: unterminated attributes block (skipping)", buildfile_pathname, lineno);
1721
         return; // invalid attribute block, skip line
1722
      }
1723
      *line_ptr = 0; // end the attribute block so that it is a parsable C string
7 pmbaty 1724
 
17 pmbaty 1725
      // now parse the attribute tokens
1726
      // DOCUMENTATION: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/m/mkifs.html#mkifs__description
1727
      token = strtok_r (attrblock_start, RECORD_SEP, &ctx);
1728
      while (token != NULL)
1729
      {
1730
         // evaluate attribute token
1731
         #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
1732
         if (false) {}
1733
         else if (strncmp (token, "prefix=",  7) == 0) { REACH_TOKEN_VALUE (); entry_parms.prefix =  (*value == '/' ? value + 1 : value); } // skip possible leading slash in prefix (NOTE: stolen pointer. Do not free.)
1734
         else if (strncmp (token, "uid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.uid     = (int) read_integer (value); }
1735
         else if (strncmp (token, "gid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.gid     = (int) read_integer (value); }
1736
         else if (strncmp (token, "dperms=",  7) == 0) { REACH_TOKEN_VALUE (); entry_parms.dperms  = (int) read_integer (value); }
1737
         else if (strncmp (token, "perms=",   6) == 0) { REACH_TOKEN_VALUE (); entry_parms.perms   = (int) read_integer (value); }
1738
         else if (strncmp (token, "type=",    5) == 0) { REACH_TOKEN_VALUE ();
1739
            if      (strcmp (value, "dir")  == 0) entry_parms.st_mode = S_IFDIR;
1740
            else if (strcmp (value, "file") == 0) entry_parms.st_mode = S_IFREG;
1741
            else if (strcmp (value, "link") == 0) entry_parms.st_mode = S_IFLNK;
1742
            else if (strcmp (value, "fifo") == 0) entry_parms.st_mode = S_IFIFO;
1743
            else DIE_WITH_EXITCODE (1, "invalid 'type' attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, value);
1744
         }
1745
         else if (strncmp (token, "image=",   6) == 0) { REACH_TOKEN_VALUE ();
1746
            image_base = (uint32_t) read_integer (value); // read image base address
1747
            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.)
1748
            if ((sep = strchr (value, ',')) != NULL) image_maxsize   = (uint32_t) read_integer (sep + 1); // if we have a comma, read optional image max size
1749
            if ((sep = strchr (value, '=')) != NULL) image_totalsize = (uint32_t) read_integer (sep + 1); // if we have an equal sign, read optional image padding size
1750
            if ((sep = strchr (value, '%')) != NULL) image_align     = (uint32_t) read_integer (sep + 1); // if we have a modulo sign, read optional image aligmnent
1751
            LOG_INFO ("image 0x%x-0x%x maxsize %d totalsize %d align %d", image_base, image_end, image_maxsize, image_totalsize, image_align);
1752
         }
1753
         else if (strncmp (token, "virtual=", 8) == 0) { REACH_TOKEN_VALUE ();
22 pmbaty 1754
            if ((bootfile_pathname == NULL) || (startupfile_pathname == NULL)) // FIXME: HACK until I figure out how to re-create them
1755
               DIE_WITH_EXITCODE (1, "creating bootable images require the --bootfile and --startupfile command-line options in \"%s\" line %d", buildfile_pathname, lineno);
17 pmbaty 1756
            if ((sep = strchr (value, ',')) != NULL) // do we have a comma separating (optional) processor and boot file name ?
1757
            {
1758
               *sep = 0;
1759
               strcpy_s (image_processor, sizeof (image_processor), value); // save processor
1760
               value = sep + 1;
1761
            }
1762
            if (stat (bootfile_pathname, &stat_buf) != 0)
1763
               DIE_WITH_EXITCODE (1, "unable to stat the boot file \"%s\" specified in \"%s\" line %d: %s", bootfile_pathname, buildfile_pathname, lineno, strerror (errno));
1764
            bootfile_size = stat_buf.st_size; // save preboot file size
1765
            LOG_INFO ("processor \"%s\" bootfile \"%s\"\n", image_processor, bootfile_pathname);
20 pmbaty 1766
            entry_parms.is_bootstrap_file = true;
17 pmbaty 1767
         }
1768
         else if (strncmp (token, "mtime=", 6) == 0) { REACH_TOKEN_VALUE (); if (strcmp (value, "*") == 0) entry_parms.mtime = UINT32_MAX; else {
1769
               // value *must* be "YYYY-MM-DD-HH:MM:SS" by specification
1770
               memset (&utc_time, 0, sizeof (utc_time));
1771
               if (sscanf_s (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)
1772
               {
1773
                  LOG_WARNING ("syntax error in \"%s\" line %d: mtime specification not in YYYY-MM-DD-HH:MM:SS format (skipping)", buildfile_pathname, lineno);
1774
                  continue; // invalid attribute block, skip line
1775
               }
1776
               utc_time.tm_mon--; // convert month from [1-12] to [0-11]
1777
               entry_parms.mtime = (uint32_t) mktime (&utc_time);
1778
            }
1779
         }
22 pmbaty 1780
         else if (strcmp (token, "+script")     == 0) entry_parms.is_compiled_bootscript         = true;
19 pmbaty 1781
         else if (strcmp (token, "-script")     == 0) entry_parms.is_compiled_bootscript         = false;
1782
         else if (strcmp (token, "+followlink") == 0) entry_parms.should_follow_symlinks         = true;
1783
         else if (strcmp (token, "-followlink") == 0) entry_parms.should_follow_symlinks         = false;
1784
         else if (strcmp (token, "+autolink")   == 0) entry_parms.should_autosymlink_dylib       = true;
1785
         else if (strcmp (token, "-autolink")   == 0) entry_parms.should_autosymlink_dylib       = false;
1786
         else if (strcmp (token, "+keeplinked") == 0) entry_parms.should_keep_ld_output          = true;
1787
         else if (strcmp (token, "-keeplinked") == 0) entry_parms.should_keep_ld_output          = false;
1788
         else if (strcmp (token, "+dupignore")  == 0) entry_parms.should_ignore_duplicates       = true;
1789
         else if (strcmp (token, "-dupignore")  == 0) entry_parms.should_ignore_duplicates       = false;
18 pmbaty 1790
         else if (strcmp (token, "+optional")   == 0) entry_parms.should_allow_nonexistent_files = true;
1791
         else if (strcmp (token, "-optional")   == 0) entry_parms.should_allow_nonexistent_files = false;
17 pmbaty 1792
         else LOG_WARNING ("unimplemented attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, token);
1793
         #undef REACH_TOKEN_VALUE
7 pmbaty 1794
 
17 pmbaty 1795
         token = strtok_r (NULL, RECORD_SEP, &ctx); // proceed to next attribute token
1796
      }
1797
 
1798
      line_ptr++; // reach the next character
1799
      while ((*line_ptr != 0) && isspace (*line_ptr))
1800
         line_ptr++; // skip leading spaces
1801
 
1802
      // are we at the end of the line ? if so, it means the attribute values that are set should become the default
1803
      if ((*line_ptr == 0) || (*line_ptr == '#'))
1804
      {
1805
         #define APPLY_DEFAULT_ATTR_NUM(attr,descr,fmt) do { if (entry_parms.attr != default_parms->attr) { \
1806
               LOG_INFO ("changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %d", default_parms->attr, entry_parms.attr, buildfile_pathname, lineno); \
1807
               default_parms->attr = entry_parms.attr; \
1808
            } } while (0)
1809
         #define APPLY_DEFAULT_ATTR_STR(attr,descr,fmt) do { if (((default_parms->attr == NULL) && (entry_parms.attr != NULL)) || ((default_parms->attr != NULL) && (entry_parms.attr == NULL)) || ((default_parms->attr != NULL) && (entry_parms.attr != NULL) && (strcmp (entry_parms.attr, default_parms->attr) != 0))) { \
1810
            LOG_INFO ("changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %d", (default_parms->attr != NULL ? default_parms->attr : "none"), entry_parms.attr, buildfile_pathname, lineno); \
24 pmbaty 1811
               if (default_parms->attr != NULL) free (default_parms->attr); \
1812
               default_parms->attr = strdup (entry_parms.attr); \
1813
               ASSERT_WITH_ERRNO (default_parms->attr != NULL); \
17 pmbaty 1814
            } } while (0)
18 pmbaty 1815
         //APPLY_DEFAULT_ATTR_STR (new_cwd,                        "current working directory",       "\"%s\"");
1816
         APPLY_DEFAULT_ATTR_STR (search,                         "search path list",                "\"%s\"");
1817
         APPLY_DEFAULT_ATTR_STR (prefix,                         "prefix",                          "\"%s\"");
1818
         APPLY_DEFAULT_ATTR_NUM (dperms,                         "directory permissions",           "0%o");
1819
         APPLY_DEFAULT_ATTR_NUM (perms,                          "file permissions",                "0%o");
1820
         APPLY_DEFAULT_ATTR_NUM (uid,                            "owner ID",                        "%d");
1821
         APPLY_DEFAULT_ATTR_NUM (gid,                            "group ID",                        "%d");
1822
         APPLY_DEFAULT_ATTR_NUM (st_mode,                        "inode type",                      "0%o");
1823
         APPLY_DEFAULT_ATTR_NUM (is_compiled_bootscript,         "compiled script state",           "%d");
1824
         APPLY_DEFAULT_ATTR_NUM (should_follow_symlinks,         "symlink resolution flag",         "%d");
1825
         APPLY_DEFAULT_ATTR_NUM (should_autosymlink_dylib,       "dylib canonical name symlinking", "%d");
1826
         APPLY_DEFAULT_ATTR_NUM (should_keep_ld_output,          "linker output preservation flag", "%d");
1827
         APPLY_DEFAULT_ATTR_NUM (should_ignore_duplicates,       "ignore duplicates flag",          "%d");
1828
         APPLY_DEFAULT_ATTR_NUM (should_allow_nonexistent_files, "ignore nonexistent files flag",   "%d");
17 pmbaty 1829
         #undef APPLY_DEFAULT_ATTR_STR
1830
         #undef APPLY_DEFAULT_ATTR_NUM
1831
         return; // end of line reached, proceed to the next line
1832
      }
1833
      // end of attributes parsing
1834
   } // end of "this line starts with an attributes block"
1835
 
1836
   // there's data in this line. We expect a filename in the IFS. Read it and unescape escaped characters
1837
   string_len = sprintf_s (path_in_ifs, sizeof (path_in_ifs), "%s", (entry_parms.prefix != NULL ? entry_parms.prefix : ""));
1838
   while ((string_len > 0) && (path_in_ifs[string_len - 1] == '/'))
1839
      string_len--; // chop off any trailing slashes from prefix
1840
   write_ptr = &path_in_ifs[string_len];
1841
   *write_ptr++ = '/'; // add ONE trailing slash
1842
   specifiedpathname_start = write_ptr; // remember the specified pathname will start here
1843
   is_quoted_context = (*line_ptr == '"');
1844
   if (is_quoted_context)
1845
      line_ptr++; // skip a possible initial quote
1846
   if (*line_ptr == '/')
1847
   {
1848
      LOG_WARNING ("paths in the IFS file should not begin with a leading '/' in \"%s\" line %d", buildfile_pathname, lineno);
1849
      line_ptr++; // consistency check: paths in the IFS should not begin with a '/'
7 pmbaty 1850
   }
24 pmbaty 1851
   while ((*line_ptr != 0) && ((!is_quoted_context && (*line_ptr != '=') && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr != '"'))))
17 pmbaty 1852
   {
1853
      if (*line_ptr == '\\')
1854
      {
1855
         line_ptr++;
1856
         *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
1857
      }
1858
      else
1859
         *write_ptr++ = *line_ptr;
1860
      line_ptr++;
1861
   }
1862
   *write_ptr = 0; // terminate the string
1863
   if (is_quoted_context && (*line_ptr == '"'))
1864
      line_ptr++; // skip a possible final quote
7 pmbaty 1865
 
17 pmbaty 1866
   // we reached a space OR an equal sign
1867
   while ((*line_ptr != 0) && isspace (*line_ptr))
1868
      line_ptr++; // skip optional spaces after the filename in the IFS
1869
 
1870
   // do we have an equal sign ?
1871
   if (*line_ptr == '=') // we must be creating either a directory or a file, do we have an equal sign ?
1872
   {
1873
      line_ptr++; // skip the equal sign
1874
      while ((*line_ptr != 0) && isspace (*line_ptr))
1875
         line_ptr++; // skip optional spaces after the equal sign
1876
 
1877
      if (*line_ptr == 0)
1878
      {
1879
         LOG_WARNING ("syntax error in \"%s\" line %d: missing data specification after equal sign (skipping)", buildfile_pathname, lineno);
1880
         return; // invalid symlink specification, skip line
1881
      }
1882
 
1883
      // read the host system's path, it may be either a path or a contents definition. Is it a content definition ?
1884
      if (*line_ptr == '{')
1885
      {
1886
         allocated_size = 0;
1887
 
1888
         line_ptr++; // skip the leading content definition
1889
         is_escaped_char = false;
1890
         for (;;)
1891
         {
1892
            read_char = fgetc (buildfile_fp);
1893
            if (read_char == EOF)
1894
               DIE_WITH_EXITCODE (1, "syntax error in \"%s\" line %d: unterminated contents block (end of file reached)", buildfile_pathname, lineno); // invalid contents block
1895
            else if ((read_char == '\\') && !is_escaped_char)
1896
               is_escaped_char = true; // remember the next char is escaped
1897
            else if ((read_char == '}') && !is_escaped_char)
1898
               break; // found an unescaped closing bracked, stop parsing
1899
            else
1900
            {
1901
               is_escaped_char = false; // any other char, meaning the next one will not be escaped
1902
               if (!should_discard_inline_contents) // only store the contents if we do NOT know the data yet
1903
               {
1904
                  if (entry_parms.data.size == allocated_size) // reallocate in 4 kb blocks
1905
                  {
1906
                     reallocated_ptr = realloc (entry_parms.data.bytes, allocated_size + 4096);
1907
                     ASSERT_WITH_ERRNO (reallocated_ptr);
1908
                     entry_parms.data.bytes = reallocated_ptr;
1909
                     allocated_size += 4096;
1910
                  }
1911
                  entry_parms.data.bytes[entry_parms.data.size++] = read_char;
1912
               }
1913
               if (read_char == '\n')
1914
                  lineno++; // update line counter as we parse the inline content
1915
            }
1916
         } // end for
1917
      }
1918
      else // not a content definition between { brackets }, must be either a pathname on the build host, or the target of a symlink
1919
      {
1920
         is_quoted_context = (*line_ptr == '"');
1921
         if (is_quoted_context)
1922
            line_ptr++; // skip a possible initial quote
1923
         specifiedpathname_start = line_ptr; // remember where the specified pathname starts
1924
         write_ptr = line_ptr; // now unescape all characters
24 pmbaty 1925
         while ((*line_ptr != 0) && ((!is_quoted_context && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr != '"'))))
17 pmbaty 1926
         {
1927
            if (*line_ptr == '\\')
1928
            {
1929
               line_ptr++;
1930
               *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
1931
            }
1932
            else
1933
               *write_ptr++ = *line_ptr;
1934
            line_ptr++;
1935
         }
1936
         *write_ptr = 0; // terminate the string
1937
         if (is_quoted_context && (*line_ptr == '"'))
1938
            line_ptr++; // skip a possible final quote
1939
 
1940
         if (S_ISLNK (entry_parms.st_mode)) // are we storing a symlink ?
1941
            ASSERT_WITH_ERRNO (Buffer_InitWithCString (&entry_parms.data, specifiedpathname_start)); // if so, store the symlink target as the dirent's blob data
1942
         else // it's a build host filesystem path
1943
            strcpy_s (path_on_buildhost, sizeof (path_on_buildhost), specifiedpathname_start); // the path on the build host is given after the equal sign
1944
      }
1945
   }
1946
   else // no equal sign, meaning the file will have the same name on the build host filesystem
1947
   {
1948
      // consistency check: symlinks MUST have an equal sign
1949
      if (entry_parms.st_mode == S_IFLNK)
1950
      {
1951
         LOG_WARNING ("syntax error in \"%s\" line %d: missing equal sign and symlink target (skipping)", buildfile_pathname, lineno);
1952
         return; // invalid symlink specification, skip line
1953
      }
1954
 
1955
      strcpy_s (path_on_buildhost, sizeof (path_on_buildhost), specifiedpathname_start); // the path on the build host is the one specified
1956
      sep = strrchr (specifiedpathname_start, '/');
1957
      if (sep != NULL)
1958
         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)
1959
   }
1960
 
1961
   // now add this entry to the image filesystem
1962
   if (S_ISDIR (entry_parms.st_mode))
1963
      entry_parms.st_mode |= entry_parms.dperms;
1964
   else if (S_ISLNK (entry_parms.st_mode))
1965
      entry_parms.st_mode |= 0777; // NOTE: mkifs sets symlink permissions to rwxrwxrwx !?
1966
   else // file or device node
1967
      entry_parms.st_mode |= entry_parms.perms;
1968
 
1969
   add_fsentry (fsentries, fsentry_count, &entry_parms, path_in_ifs, path_on_buildhost); // and add filesystem entry
1970
 
1971
   if (entry_parms.data.bytes != NULL)
1972
      free (entry_parms.data.bytes); // if blob data was allocated, free it
1973
 
1974
   return; //  finished parsing that line
1 pmbaty 1975
}
1976
 
1977
 
1978
int main (int argc, char **argv)
1979
{
1980
   // program entrypoint
1981
 
16 pmbaty 1982
   typedef struct ifs_offsets_s
1983
   {
1984
      size_t startupheader;
1985
      size_t startuptrailer;
1986
      size_t imageheader;
1987
      size_t imagedir;
1988
      size_t imagetrailer;
1989
   } ifs_offsets_t;
1990
   typedef struct ifs_s
1991
   {
1992
      buffer_t data;
1993
      ifs_offsets_t offsets;
1994
      size_t final_size; // final size: not known (because not set) until everything has been written
1995
   } ifs_t;
1 pmbaty 1996
 
19 pmbaty 1997
   startup_header_t startup_header = { 0 }; // output IFS's startup header
1998
   startup_trailer_v2_t startup_trailer = { 0 }; // output IFS's startup trailer (version 2, with SHA-512 checksum and int32 checksum)
1999
   image_header_t image_header = { 0 }; // output IFS's imagefs header
2000
   image_trailer_v2_t image_trailer = { 0 }; // output IFS's imagefs trailer (version 2, with SHA-512 checksum and int32 checksum)
2001
   fsentry_t *fsentries = NULL; // output IFS's filesystem entries
2002
   size_t fsentry_count = 0; // number of entries in the IFS filesystem
2003
   parms_t default_parms = { // default parameters for a filesystem entry
7 pmbaty 2004
      .dperms = 0755,
2005
      .perms = 0644,
2006
      .mtime = UINT32_MAX,
2007
      .mtime_for_inline_files = UINT32_MAX,
24 pmbaty 2008
      .prefix = NULL, // will be initialized to a *mallocated* string: "/proc/boot"
7 pmbaty 2009
      .should_follow_symlinks = true, // [+|-followlink]
2010
      .should_autosymlink_dylib = true, // [+|-autolink]
2011
   };
19 pmbaty 2012
   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 2013
 
7 pmbaty 2014
   char path_on_buildhost[MAXPATHLEN] = "";
2015
   char path_in_ifs[MAXPATHLEN] = "";
19 pmbaty 2016
   const char *ifs_pathname = NULL;
2017
   const char *rootdir_pathname = NULL;
2018
   const fsentry_t *fsentry;
1 pmbaty 2019
   void *reallocated_ptr;
17 pmbaty 2020
   size_t reallocated_size;
7 pmbaty 2021
   size_t available_space;
1 pmbaty 2022
   size_t fsentry_index;
7 pmbaty 2023
   size_t largest_index;
2024
   size_t largest_size;
17 pmbaty 2025
   size_t imgdir_size;
1 pmbaty 2026
   size_t curr_offset;
16 pmbaty 2027
   ifs_t ifs = { 0 };
8 pmbaty 2028
   int32_t checksum;
16 pmbaty 2029
   char *first_pathname = NULL;
2030
   char *second_pathname = NULL;
19 pmbaty 2031
   char *third_pathname = NULL;
1 pmbaty 2032
   char *sep;
2033
   int arg_index;
2034
   bool is_quoted_context = false;
2035
   bool is_escaped_char = false;
11 pmbaty 2036
   bool should_discard_inline_contents = false;
1 pmbaty 2037
   bool want_info = false;
10 pmbaty 2038
   bool want_everything = false;
1 pmbaty 2039
   bool want_help = false;
14 pmbaty 2040
   bool want_dump = false;
16 pmbaty 2041
   bool want_strip = false;
15 pmbaty 2042
   bool want_hexdump = false;
5 pmbaty 2043
   bool is_foreign_endianness;
1 pmbaty 2044
   FILE *buildfile_fp;
2045
 
17 pmbaty 2046
   // initialize stuff
2047
   saved_ELF_sections = (char **) malloc (4 * sizeof (char *));
2048
   ASSERT_WITH_ERRNO (saved_ELF_sections);
2049
   saved_ELF_sections[0] = "QNX_info"; // NOTE: MUST BE THE FIRST ONE as we artificially shrink down the array to 1 when using it for boot ELF files
2050
   saved_ELF_sections[1] = ".gnu_debuglink";
2051
   saved_ELF_sections[2] = "QNX_usage";
2052
   saved_ELF_sections[3] = ".note.gnu.build-id"; // undocumented by QNX, but nonetheless preserved
2053
   saved_ELF_section_count = 4;
24 pmbaty 2054
   default_parms.prefix = strdup ("/proc/boot");
2055
   ASSERT_WITH_ERRNO (default_parms.prefix);
17 pmbaty 2056
 
1 pmbaty 2057
   // parse arguments
2058
   for (arg_index = 1; arg_index < argc; arg_index++)
2059
   {
2060
      if ((strcmp (argv[arg_index], "--bootfile") == 0) && (arg_index + 1 < argc)) // --bootfile path/to/blob.bin
2061
         bootfile_pathname = argv[++arg_index];
2062
      else if ((strcmp (argv[arg_index], "--startupfile") == 0) && (arg_index + 1 < argc)) // --startupfile path/to/blob.bin@0x1030
2063
      {
2064
         sep = strchr (argv[++arg_index], '@');
2065
         if ((sep == NULL) || (sep[1] == 0))
15 pmbaty 2066
            DIE_WITH_EXITCODE (1, "the --startupfile arguments expects <pathname>@<entrypoint_from_image_base>");
1 pmbaty 2067
         *sep = 0;
2068
         startupfile_pathname = argv[arg_index];
2069
         startupfile_ep_from_imagebase = (size_t) read_integer (sep + 1);
2070
      }
20 pmbaty 2071
      else if ((strcmp (argv[arg_index], "--kerneloffs") == 0) && (arg_index + 1 < argc)) // --kerneloffs 0x32000 (undocumented)
2072
         kernelfile_offset = (size_t) read_integer (argv[++arg_index]);
19 pmbaty 2073
      else if ((strcmp (argv[arg_index], "-a") == 0) && (arg_index + 1 < argc)) // -a suffix
2074
         sym_suffix = argv[++arg_index];
7 pmbaty 2075
      else if (strcmp (argv[arg_index], "-n") == 0)
2076
         default_parms.mtime_for_inline_files = 0; // inline files should have a mtime set to zero
2077
      else if (strcmp (argv[arg_index], "-nn") == 0)
2078
      {
11 pmbaty 2079
         default_parms.mtime = 0; // *all* files should have a mtime set to zero
7 pmbaty 2080
         default_parms.mtime_for_inline_files = 0;
2081
      }
14 pmbaty 2082
      else if ((strcmp (argv[arg_index], "--outdir") == 0) && (arg_index + 1 < argc)) // --outdir path
16 pmbaty 2083
         second_pathname = argv[++arg_index];
2084
      else if ((strcmp (argv[arg_index], "--outfile") == 0) && (arg_index + 1 < argc)) // --outfile pathname
2085
         second_pathname = argv[++arg_index];
1 pmbaty 2086
      else if (strcmp (argv[arg_index], "--info") == 0)
2087
         want_info = true;
14 pmbaty 2088
      else if (strcmp (argv[arg_index], "--dump") == 0)
2089
         want_dump = true;
15 pmbaty 2090
      else if (strcmp (argv[arg_index], "--hexdump") == 0) // voluntarily undocumented
2091
         want_hexdump = true;
16 pmbaty 2092
      else if (strcmp (argv[arg_index], "--strip") == 0)
2093
         want_strip = true;
10 pmbaty 2094
      else if (strcmp (argv[arg_index], "--everything") == 0)
2095
         want_everything = true;
15 pmbaty 2096
      else if (strncmp (argv[arg_index], "-v", 2) == 0) // -v[....]
2097
         verbose_level += (int) strlen (argv[arg_index] + 1); // increase verbosity by the number of characters in this flag
17 pmbaty 2098
      else if ((strcmp (argv[arg_index], "-l") == 0) && (arg_index + 1 < argc))
2099
         arg_index++; // these args will be parsed once the build file is open
2100
      else if ((strcmp (argv[arg_index], "-r") == 0) && (arg_index + 1 < argc))
2101
      {
2102
         reallocated_size = (SEARCH_PATH != NULL ? strlen (SEARCH_PATH) + 1 : 0) + strlen (argv[arg_index + 1]) + 1;
2103
         reallocated_ptr = realloc (SEARCH_PATH, reallocated_size); // grow search prefixes array
2104
         ASSERT_WITH_ERRNO (reallocated_ptr);
2105
         if (SEARCH_PATH != NULL)
2106
            strcat_s (reallocated_ptr, reallocated_size, PATH_SEP);
2107
         strcat_s (reallocated_ptr, reallocated_size, argv[++arg_index]); // stack up another search prefix
2108
         SEARCH_PATH = reallocated_ptr;
2109
      }
2110
      else if ((strcmp (argv[arg_index], "-s") == 0) && (arg_index + 1 < argc))
2111
      {
2112
         reallocated_ptr = realloc (saved_ELF_sections, (saved_ELF_section_count + 1) * sizeof (char *)); // grow ELF sections array
2113
         ASSERT_WITH_ERRNO (reallocated_ptr);
2114
         saved_ELF_sections = reallocated_ptr;
2115
         saved_ELF_sections[saved_ELF_section_count++] = argv[++arg_index]; // stack up another ELF section name to preserve
2116
      }
1 pmbaty 2117
      else if ((strcmp (argv[arg_index], "-?") == 0) || (strcmp (argv[arg_index], "--help") == 0))
2118
         want_help = true;
20 pmbaty 2119
      else if ((first_pathname == NULL) && (*argv[arg_index] != '-'))
16 pmbaty 2120
         first_pathname = argv[arg_index];
20 pmbaty 2121
      else if ((second_pathname == NULL) && (*argv[arg_index] != '-'))
16 pmbaty 2122
         second_pathname = argv[arg_index];
20 pmbaty 2123
      else if ((third_pathname == NULL) && (*argv[arg_index] != '-'))
19 pmbaty 2124
         third_pathname = argv[arg_index];
16 pmbaty 2125
      else
2126
         DIE_WITH_EXITCODE (1, "unrecognized option: '%s'", argv[arg_index]);
1 pmbaty 2127
   }
2128
 
19 pmbaty 2129
   // do we want to display help ? (TODO: everything that's commented out is pending implementation)
2130
   if (want_help)
1 pmbaty 2131
   {
16 pmbaty 2132
      FILE *out = (want_help ? stdout : stderr); // select the right output channel
2133
      fprintf (out, "ifstool - QNX in-kernel filesystem creation utility by Pierre-Marie Baty <pm@pmbaty.com>\n");
2134
      fprintf (out, "          version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD);
4 pmbaty 2135
      if (!want_help)
16 pmbaty 2136
         fprintf (out, "error: missing parameters\n");
2137
      fprintf (out, "usage:\n");
2138
      fprintf (out, "    ifstool --info [--everything] <ifs file>\n");
2139
      fprintf (out, "    ifstool --dump [--outdir <path>] <ifs file>\n");
2140
      fprintf (out, "    ifstool --strip [--outfile <pathname>] <ELF file>\n");
17 pmbaty 2141
      fprintf (out, "    ifstool [-?|--help]\n");
19 pmbaty 2142
      // mkifs [-?] [-l inputline] [-n[n]] [-o directory] [-p patchfile] [-r rootdir] [-s section] [-v] [buildfile] [directory] [outputfile]
21 pmbaty 2143
      fprintf (out, "    ifstool [--bootfile <pathname>] [--startupfile <pathname>@<EP_from_imgbase>] [--kerneloffs <fileoffs>] [-a suffix] [-l inputline] [-n[n]] [-r rootdir] [-s section] [-v[...]] [buildfile] [directory] [outputfile]\n");
2144
      fprintf (out, "NOTE: the compiler mode requires predigested boot and startup files produced by mkifs.\n");
17 pmbaty 2145
      fprintf (out, "options:\n");
2146
      fprintf (out, "    -?       Display some help information.\n");
19 pmbaty 2147
      fprintf (out, "    -a .ext  Append a suffix to symbol files generated via [+keeplinked].\n");
17 pmbaty 2148
      fprintf (out, "    -l line  Process line before interpreting the buildfile. Input lines given\n");
2149
      fprintf (out, "             to mkifs should be quoted to prevent interpretation by the shell\n");
2150
      fprintf (out, "             (especially as mkifs input lines often contain spaces). Multiple\n");
2151
      fprintf (out, "             -l options are processed in the order specified. No default.\n");
2152
      fprintf (out, "    -n[n]    Force the modification times of all inline files to be 0. If you\n");
2153
      fprintf (out, "             specify -nn, mkifs sets the modification times of all files to 0.\n");
2154
      fprintf (out, "             When mkifs adds files to an IFS image, it uses the timestamp info\n");
2155
      fprintf (out, "             from the file on the host machine. If mkifs is creating an inline\n");
2156
      fprintf (out, "             file (which doesn't exist on the host machine), it must generate\n");
2157
      fprintf (out, "             its own timestamp information. By default, it's the time at which\n");
2158
      fprintf (out, "             the image is generated. This results in different checksum values\n");
2159
      fprintf (out, "             for two identical builds, because the file's times are different.\n");
2160
      fprintf (out, "             If you use -n, the checksum value is the same on all identical\n");
2161
      fprintf (out, "             builds. The -nn option addresses a quirk in NTFS with daylight\n");
2162
      fprintf (out, "             savings time. This forces the modification time for all files in\n");
2163
      fprintf (out, "             the IFS image to be set to 0. This ensures that subsequent builds\n");
2164
      fprintf (out, "             of the same IFS image have the same checksum.");
2165
//      fprintf (out, "    -o dir   Specify a directory to be used for all permanent build artifacts,\n");
2166
//      fprintf (out, "             other than the output image itself. The most common example is\n");
2167
//      fprintf (out, "             the .sym files generated by the [+keeplinked] attribute.\n");
2168
//      fprintf (out, "    -p file  Apply patching instructions from this file.\n");
2169
      fprintf (out, "    -r dir   When searching for host files to be included in the image, search\n");
2170
      fprintf (out, "             the default paths used for storing binaries within the specified\n");
2171
      fprintf (out, "             directory before searching the default paths within $QNX_TARGET.\n");
2172
      fprintf (out, "             You can define multiple -r options; each adds a set of paths to\n");
2173
      fprintf (out, "             search for files. The -r options are evaluated from left to right\n");
2174
      fprintf (out, "             meaning the paths prefixed with the first (leftmost) rootdir are\n");
2175
      fprintf (out, "             searched first, then those prefixed with the second rootdir, and\n");
2176
      fprintf (out, "             so on.\n");
2177
      fprintf (out, "             Normally, mkifs searches any paths defined in $MKIFS_PATH when\n");
2178
      fprintf (out, "             it was called and then the default paths within $QNX_TARGET. The\n");
2179
      fprintf (out, "             default paths are based on the CPU architecture specified by\n");
2180
      fprintf (out, "             $PROCESSOR and $PROCESSOR_BASE. If you specify -r options, mkifs\n");
2181
      fprintf (out, "             searches the default paths prefixed with each dir variable before\n");
2182
      fprintf (out, "             searching those within $QNX_TARGET. These paths are:\n");
2183
      fprintf (out, "               dir/${PROCESSOR}/sbin\n");
2184
      fprintf (out, "               dir/${PROCESSOR}/usr/sbin\n");
2185
      fprintf (out, "               dir/${PROCESSOR}/boot/sys\n");
2186
      fprintf (out, "               dir/${PROCESSOR_BASE}/boot/sys\n");
2187
      fprintf (out, "               dir/${PROCESSOR}/bin\n");
2188
      fprintf (out, "               dir/${PROCESSOR}/usr/bin\n");
2189
      fprintf (out, "               dir/${PROCESSOR}/lib\n");
2190
      fprintf (out, "               dir/${PROCESSOR}/lib/dll\n");
2191
      fprintf (out, "               dir/${PROCESSOR}/usr/lib\n");
2192
      fprintf (out, "             NOTE: The structure of the directory paths under dir must be\n");
2193
      fprintf (out, "             identical to that of the default paths under $QNX_TARGET, but the\n");
2194
      fprintf (out, "             root dir itself may be any path you choose. For example, if you\n");
2195
      fprintf (out, "             wanted to include /scratch/aarch64le/sbin/devb-sata, you would\n");
2196
      fprintf (out, "             specify a -r option like this:\n");
2197
      fprintf (out, "               -r /scratch\n");
2198
      fprintf (out, "             Note that you don't include $PROCESSOR or $PROCESSOR_BASE in dir.\n");
2199
      fprintf (out, "    -s name  Don't strip the named section from ELF executables when creating\n");
2200
      fprintf (out, "             an IFS image. You can use this option more than once to specify\n");
2201
      fprintf (out, "             additional sections. By default, mkifs doesn't strip:\n");
2202
      fprintf (out, "               .gnu_debuglink - the name and checksum of the debug info file\n");
2203
      fprintf (out, "               QNX_info       - build properties\n");
2204
      fprintf (out, "               QNX_usage      - usage message\n");
2205
      fprintf (out, "             You can use the keepsection attribute to specify the sections\n");
2206
      fprintf (out, "             that are not to be stripped from specific files in the image. For\n");
2207
      fprintf (out, "             files in the bootstrap section (like startup or procnto), the\n");
2208
      fprintf (out, "             global keepsection list affected by -s does not apply to these\n");
2209
      fprintf (out, "             files. For them, only the QNX_info section is kept.\n");
2210
      fprintf (out, "    -v[v..]  Operate verbosely. Specifying additional v options increases the\n");
2211
      fprintf (out, "             verbosity.\n");
1 pmbaty 2212
      exit (want_help ? 0 : 1);
2213
   }
2214
 
19 pmbaty 2215
   // else do we want info about a particular IFS ? if so, dissecate it
2216
   else if (want_info)
16 pmbaty 2217
      exit (dump_ifs_info (first_pathname, want_everything));
2 pmbaty 2218
 
14 pmbaty 2219
   // else do we want to dump its contents ? if so, do so
2220
   else if (want_dump)
16 pmbaty 2221
      exit (dump_ifs_contents (first_pathname, (second_pathname != NULL ? second_pathname : ".")));
14 pmbaty 2222
 
15 pmbaty 2223
   // else do we want to hex dump a file ? (this is voluntarily undocumented)
2224
   else if (want_hexdump)
16 pmbaty 2225
      exit (dump_file_hex (first_pathname));
2226
 
2227
   // else do we want to strip an ELF file ? if so, do so
2228
   else if (want_strip)
15 pmbaty 2229
   {
16 pmbaty 2230
      buffer_t file;
2231
      ASSERT (Buffer_ReadFromFile (&file, first_pathname), "can't open \"%s\" for reading: %s", first_pathname, strerror (errno));
20 pmbaty 2232
      ASSERT (Buffer_StripELFFile (&file, (const char **) saved_ELF_sections, saved_ELF_section_count, false, first_pathname), "error stripping \"%s\": %s", first_pathname, strerror (errno));
16 pmbaty 2233
      ASSERT_WITH_ERRNO (Buffer_WriteToFile (&file, (second_pathname != NULL ? second_pathname : "<stdout>")));
15 pmbaty 2234
      exit (0);
2235
   }
2236
 
16 pmbaty 2237
   // we want to CREATE an IFS file
2238
   buildfile_pathname = first_pathname; // assign the pathnames properly
19 pmbaty 2239
   ifs_pathname = (third_pathname != NULL ? third_pathname : second_pathname); // this is some curious handling of cmdline args, but that's the way mkxfs does it
2240
   rootdir_pathname = (third_pathname != NULL ? second_pathname : NULL);
16 pmbaty 2241
 
1 pmbaty 2242
   // make sure we have ${QNX_TARGET} pointing somewhere
2243
   QNX_TARGET = getenv ("QNX_TARGET");
2244
   if (QNX_TARGET == NULL)
15 pmbaty 2245
      DIE_WITH_EXITCODE (1, "the QNX_TARGET environment variable is not set");
1 pmbaty 2246
   else if (access (QNX_TARGET, 0) != 0)
15 pmbaty 2247
      DIE_WITH_EXITCODE (1, "the QNX_TARGET environment variable doesn't point to an existing directory");
1 pmbaty 2248
 
2249
   // open build file
19 pmbaty 2250
   if ((buildfile_pathname != NULL) && (strcmp (buildfile_pathname, "-") != 0))
2251
   {
2252
      fopen_s (&buildfile_fp, buildfile_pathname, "rb"); // open it
2253
      if (buildfile_fp == NULL)
2254
         DIE_WITH_EXITCODE (1, "unable to open build file \"%s\" for reading: %s", buildfile_pathname, strerror (errno));
2255
   }
2256
   else // no build file specified: use stdin
2257
   {
2258
      buildfile_pathname = "<stdin>";
2259
      buildfile_fp = stdin;
2260
   }
1 pmbaty 2261
 
2262
   // stack up filesystem entries
2263
   memcpy (&entry_parms, &default_parms, sizeof (default_parms));
2264
   entry_parms.st_mode = S_IFDIR | default_parms.dperms;
7 pmbaty 2265
   add_fsentry (&fsentries, &fsentry_count, &entry_parms, "", NULL); // add the root dir first
1 pmbaty 2266
 
17 pmbaty 2267
   // parse -l arguments before everything else
2268
   for (arg_index = 1; arg_index < argc; arg_index++)
2269
      if ((strcmp (argv[arg_index], "-l") == 0) && (arg_index + 1 < argc))
2270
         parse_line (NULL, argv[++arg_index], &fsentries, &fsentry_count, &default_parms);
2271
 
11 pmbaty 2272
   // parse the IFS build file line per line
1 pmbaty 2273
   while (fgets (line_buffer, sizeof (line_buffer), buildfile_fp) != NULL)
2274
   {
7 pmbaty 2275
      if (current_line != NULL)
2276
         free (current_line);
2277
      current_line = strdup (line_buffer);
16 pmbaty 2278
      ASSERT_WITH_ERRNO (current_line);
1 pmbaty 2279
      lineno++; // keep track of current line number
17 pmbaty 2280
      parse_line (buildfile_fp, line_buffer, &fsentries, &fsentry_count, &default_parms);
2281
   }
1 pmbaty 2282
 
17 pmbaty 2283
   fclose (buildfile_fp); // finished parsing the build file
1 pmbaty 2284
 
19 pmbaty 2285
   // if a root dir was specified, open it as a directory and recursively add all of its contents to the filesystem
2286
   if (rootdir_pathname != NULL)
2287
      add_directory_contents_recursively (&fsentries, &fsentry_count, rootdir_pathname, strlen (rootdir_pathname), &default_parms);
1 pmbaty 2288
 
16 pmbaty 2289
   //////////////////////////////////
2290
   // start constructing the IFS file
2291
 
2292
   Buffer_Initialize (&ifs.data);
2293
 
2 pmbaty 2294
   // do we have a startup file ? if so, this is a bootable image
2295
   if (startupfile_pathname != NULL)
1 pmbaty 2296
   {
2297
      // write boot prefix
11 pmbaty 2298
      // ######################################################################################################################################################################################################################################
2299
      // # FIXME: figure out how to re-create it
2300
      // ######################################################################################################################################################################################################################################
16 pmbaty 2301
      buffer_t file;
2302
      if (!Buffer_ReadFromFile (&file, bootfile_pathname))
2303
         DIE_WITH_EXITCODE (1, "failed to open \"%s\" for reading: %s", bootfile_pathname, strerror (errno));
2304
      ASSERT_WITH_ERRNO (Buffer_AppendBuffer (&ifs.data, &file)); // write boot blob
2305
      Buffer_Forget (&file);
2306
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2307
 
16 pmbaty 2308
      ifs.offsets.startupheader = ifs.data.size; // save startup header offset for future use
1 pmbaty 2309
      memset (&startup_header, 0, sizeof (startup_header)); // prepare startup header
2310
      memcpy (startup_header.signature, "\xeb\x7e\xff\x00", 4); // startup header signature, i.e. 0xff7eeb
2311
      startup_header.version       = 1;
2312
      startup_header.flags1        = STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2; // flags, 0x21 (STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2)
2313
      startup_header.header_size   = sizeof (startup_header); // 256
2314
      if (strcmp (image_processor, "x86_64") == 0)
10 pmbaty 2315
         startup_header.machine = ELF_MACHINE_X86_64; // EM_X86_64
1 pmbaty 2316
      else if (strcmp (image_processor, "aarch64le") == 0)
10 pmbaty 2317
         startup_header.machine = ELF_MACHINE_AARCH64; // EM_AARCH64
1 pmbaty 2318
      else
15 pmbaty 2319
         DIE_WITH_EXITCODE (1, "unsupported processor type '%s' found in build file \"%s\"", image_processor, buildfile_pathname); // should not happen
1 pmbaty 2320
      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 2321
      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 2322
      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)
2323
      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)
2324
      startup_header.startup_size  = WILL_BE_FILLED_LATER;                                  // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes
2 pmbaty 2325
      startup_header.stored_size   = WILL_BE_FILLED_LATER;                                  // [I ] Size of entire image, here 0x00cd6128 (same as ram_size)
1 pmbaty 2326
      startup_header.imagefs_size  = WILL_BE_FILLED_LATER;                                  // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes
2 pmbaty 2327
      startup_header.preboot_size  = (uint16_t) bootfile_size;                              // [I ] Size of loaded before header, here 0xf30 or 3888 bytes (size of "bios.boot" file))
16 pmbaty 2328
      ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &startup_header, sizeof (startup_header))); // write startup header
2329
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2330
 
2331
      // ######################################################################################################################################################################################################################################
11 pmbaty 2332
      // # FIXME: figure out how to re-create it:
2333
      // first: open "startup-x86" ELF file,
2334
      //        lookup section headers table (there is no program headers table in this one)
2335
      //        FIXME: figure out something in there where the result is 0x1401030 !!!
2336
      // 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
2337
      // then: parse resulting ELF file, take all program segments and concatenate them --> this is the blob (FIXME: wrong?)
1 pmbaty 2338
      // ######################################################################################################################################################################################################################################
11 pmbaty 2339
#if 0 // nonworking
16 pmbaty 2340
      // <deleted>
11 pmbaty 2341
#else // working
16 pmbaty 2342
      if (!Buffer_ReadFromFile (&file, startupfile_pathname))
2343
         DIE_WITH_EXITCODE (1, "failed to open \"%s\" for reading: %s", startupfile_pathname, strerror (errno));
2344
      ASSERT_WITH_ERRNO (Buffer_AppendBuffer (&ifs.data, &file)); // write startup blob
2345
      Buffer_Forget (&file);
11 pmbaty 2346
#endif // working
16 pmbaty 2347
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2348
 
16 pmbaty 2349
      ifs.offsets.startuptrailer = ifs.data.size; // save startup trailer offset for future use
2350
      ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &startup_trailer, sizeof (startup_trailer))); // write startup trailer
2351
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2352
   }
2353
 
16 pmbaty 2354
   ifs.offsets.imageheader = ifs.data.size; // save image header offset for future use
1 pmbaty 2355
   memset (&image_header, 0, sizeof (image_header)); // prepare image header
2356
   memcpy (&image_header.signature, "imagefs", 7); // image filesystem signature, i.e. "imagefs"
2357
   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)
2358
   image_header.image_size    = WILL_BE_FILLED_LATER; // size from header to end of trailer (here 0xca6fe0 or 13 266 912)
7 pmbaty 2359
   image_header.hdr_dir_size  = WILL_BE_FILLED_LATER; // size from header to last dirent (here 0x12b8 or 4792)
1 pmbaty 2360
   image_header.dir_offset    = sizeof (image_header); // offset from header to first dirent (here 0x5c or 92)
2361
   image_header.boot_ino[0]   = image_kernel_ino; // inode of files for bootstrap p[ro?]g[ra?]ms (here 0xa0000002, 0, 0, 0)
2362
   image_header.script_ino    = image_bootscript_ino; // inode of file for script (here 3)
2363
   image_header.mountpoint[0] = '/'; // default mountpoint for image ("/" + "\0\0\0")
16 pmbaty 2364
   ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &image_header, sizeof (image_header))); // write image header
2365
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2366
 
6 pmbaty 2367
   // write image directory (with the wrong file offsets)
16 pmbaty 2368
   ifs.offsets.imagedir = ifs.data.size; // save image directory offset for future use
2369
   curr_offset = ifs.offsets.imagedir;
1 pmbaty 2370
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
16 pmbaty 2371
   {
2372
      Buffer_WriteIFSDirectoryEntryAt (&ifs.data, curr_offset, &fsentries[fsentry_index]); // write each dirent (the unknown fields will be fixed later)
2373
      curr_offset += fsentries[fsentry_index].header.size; // advance to the next one
2374
   }
2375
   ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&ifs.data, "\0\0\0\0")); // there seems to be 4 bytes of padding after the image directory
2376
   imgdir_size = ifs.data.size - ifs.offsets.imagedir; // measure image dir size and save it for future use
1 pmbaty 2377
 
20 pmbaty 2378
   // is it a bootable image with a startup file ?
2379
   if (startupfile_pathname != NULL)
1 pmbaty 2380
   {
7 pmbaty 2381
      // start by writing the startup script data blob, if we have one
2382
      for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
2383
         if (fsentries[fsentry_index].header.ino == image_bootscript_ino)
8 pmbaty 2384
            break; // locate the startup script directory entry
7 pmbaty 2385
      if (fsentry_index < fsentry_count) // found it ?
1 pmbaty 2386
      {
16 pmbaty 2387
         if (ifs.data.size + fsentries[fsentry_index].u.file.size >= kernelfile_offset)
20 pmbaty 2388
            DIE_WITH_EXITCODE (1, "the compiled startup script is too big (%zd bytes, max is %zd) to fit at current offset %zd. Use --kerneloffs <addr> to relocate (warning: untested)", (size_t) fsentries[fsentry_index].u.file.size, kernelfile_offset - ifs.data.size, ifs.data.size);
16 pmbaty 2389
         fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
2390
         Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
1 pmbaty 2391
         fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2392
      }
7 pmbaty 2393
 
2394
      // now write the filesystem entries that may fit before the kernel
2395
      for (;;)
2396
      {
16 pmbaty 2397
         available_space = kernelfile_offset - ifs.data.size; // measure the available space until the kernel
7 pmbaty 2398
 
2399
         // look for the biggest one that can fit
2400
         largest_index = 0;
8 pmbaty 2401
         largest_size = 0;
7 pmbaty 2402
         for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
2403
         {
2404
            if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written || (fsentries[fsentry_index].u.file.size > available_space))
2405
               continue; // skip all entries that don't have a separate data block, those who were written already and those that wouldn't fit
2406
            if (fsentries[fsentry_index].u.file.size > largest_size)
2407
            {
2408
               largest_size = fsentries[fsentry_index].u.file.size;
2409
               largest_index = fsentry_index;
2410
            }
2411
         }
8 pmbaty 2412
         if (largest_size == 0)
7 pmbaty 2413
            break; // found none ? if so, stop searching
16 pmbaty 2414
         fsentry_index = largest_index;
7 pmbaty 2415
 
16 pmbaty 2416
         fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
2417
         Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
2418
         fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
7 pmbaty 2419
      }
16 pmbaty 2420
      LOG_INFO ("Last written offset: 0x%zx", ifs.data.size);
15 pmbaty 2421
      LOG_INFO ("Kernel file offset: 0x%zx", kernelfile_offset);
16 pmbaty 2422
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, kernelfile_offset)); // reach the kernel offset
1 pmbaty 2423
 
8 pmbaty 2424
      // now write the QNX kernel
2425
      for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
2426
         if (fsentries[fsentry_index].header.ino == image_kernel_ino)
2427
            break; // locate the kernel directory entry (can't fail)
16 pmbaty 2428
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
2429
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write kernel file data
8 pmbaty 2430
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1 pmbaty 2431
   }
2432
 
8 pmbaty 2433
   // then write all the other files by increasing inode number: ELF files first
7 pmbaty 2434
   for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
1 pmbaty 2435
   {
8 pmbaty 2436
      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
2437
          || (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 2438
         continue; // skip all entries that don't have a separate data block and those who were written already
16 pmbaty 2439
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
2440
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
1 pmbaty 2441
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2442
   }
8 pmbaty 2443
   for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++) // other files (non-ELF, e.g. scripts and data files) last
2444
   {
2445
      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
2446
         continue; // skip all entries that don't have a separate data block and those who were written already
16 pmbaty 2447
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
2448
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
8 pmbaty 2449
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2450
   }
16 pmbaty 2451
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2452
 
2453
   // finally, write trailer (including empty checksum)
16 pmbaty 2454
   ifs.offsets.imagetrailer = ifs.data.size; // save image trailer offset for future use
2455
   ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &image_trailer, sizeof (image_trailer))); // write image trailer
2456
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2457
 
2458
   // if we need to pad it to a specific length, do so
16 pmbaty 2459
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, image_totalsize));
2460
   ifs.final_size = ifs.data.size; // and this is the final size of the IFS
1 pmbaty 2461
 
2462
   // see if we are past the image max size, in which case it's an error
16 pmbaty 2463
   if (ifs.final_size > image_maxsize)
19 pmbaty 2464
      DIE_WITH_EXITCODE (1, "image file size %zd exceeds max size (%zd)", ifs.final_size, (size_t) image_maxsize);
1 pmbaty 2465
 
2 pmbaty 2466
   // do we have a startup file ? if so, this is a bootable image
2467
   if (startupfile_pathname != NULL)
1 pmbaty 2468
   {
16 pmbaty 2469
      // patch the startup header with its final values
2470
      startup_header.startup_size = (uint32_t) (ifs.offsets.imageheader - ifs.offsets.startupheader); // size of startup header up to image header
2471
      startup_header.imagefs_size = (uint32_t) (ifs.final_size - ifs.offsets.imageheader); // size of uncompressed imagefs
2472
      startup_header.ram_size     = (uint32_t) (ifs.final_size - ifs.offsets.startupheader);
2473
      startup_header.stored_size  = (uint32_t) (ifs.final_size - ifs.offsets.startupheader);
2474
      ASSERT_WITH_ERRNO (Buffer_WriteAt (&ifs.data, ifs.offsets.startupheader, &startup_header, sizeof (startup_header))); // write the final startup header at its right offset
8 pmbaty 2475
   }
1 pmbaty 2476
 
8 pmbaty 2477
   // rewrite image header with final values
16 pmbaty 2478
   image_header.image_size = (uint32_t) (ifs.final_size - ifs.offsets.imageheader); // size of uncompressed imagefs
8 pmbaty 2479
   image_header.hdr_dir_size = sizeof (image_header) + (uint32_t) imgdir_size; // size from start of image header to last dirent
16 pmbaty 2480
   ASSERT_WITH_ERRNO (Buffer_WriteAt (&ifs.data, ifs.offsets.imageheader, &image_header, sizeof (image_header))); // write image header
8 pmbaty 2481
 
2482
   // rewrite image directory with final offset values
2483
   if (image_header.flags & IMAGE_FLAGS_SORTED)
16 pmbaty 2484
      qsort (&fsentries[1], fsentry_count - 1, sizeof (fsentry_t), fsentry_compare_pathnames_cb); // sort the filesystem entries by pathname if necessary
2485
   curr_offset = ifs.offsets.imagedir; // position ourselves at the beginning of the image directory
8 pmbaty 2486
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
16 pmbaty 2487
   {
2488
      Buffer_WriteIFSDirectoryEntryAt (&ifs.data, curr_offset, &fsentries[fsentry_index]); // rewrite each dirent
2489
      curr_offset += fsentries[fsentry_index].header.size; // advance to the next one
2490
   }
8 pmbaty 2491
 
2492
   // ALL CHECKSUMS AT THE VERY END
2493
 
2494
   // do we have a startup file ? if so, this is a bootable image
2495
   if (startupfile_pathname != NULL)
2496
   {
1 pmbaty 2497
      // compute SHA-512 checksum and V1 checksum of startup block
6 pmbaty 2498
      if (   ( (startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
2499
          || (!(startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
2500
         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
2501
      else
2502
         is_foreign_endianness = false; // else this header is for the same endianness as us
1 pmbaty 2503
 
16 pmbaty 2504
      if (startup_header.flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2) // is it a V2 trailer ?
2505
      {
2506
         SHA512 (&ifs.data.bytes[ifs.offsets.startupheader], ifs.offsets.startuptrailer - ifs.offsets.startupheader, &ifs.data.bytes[ifs.offsets.startuptrailer]); // compute SHA512 checksum and write it in place
2507
         checksum = update_checksum (&ifs.data.bytes[ifs.offsets.startupheader], ifs.offsets.startuptrailer + SHA512_DIGEST_LENGTH - ifs.offsets.startupheader, is_foreign_endianness); // compute old checksum
2508
         memcpy (&ifs.data.bytes[ifs.offsets.startuptrailer + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place
2509
      }
2510
      else // old V1 trailer
2511
      {
2512
         checksum = update_checksum (&ifs.data.bytes[ifs.offsets.startupheader], ifs.offsets.startuptrailer - ifs.offsets.startupheader, is_foreign_endianness); // compute old checksum
2513
         memcpy (&ifs.data.bytes[ifs.offsets.startuptrailer], &checksum, 4); // and write it in place
2514
      }
1 pmbaty 2515
   }
2516
 
2517
   // compute SHA-512 checksum and V1 checksum of image block
5 pmbaty 2518
   if (   ( (image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
2519
       || (!(image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
2520
      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
2521
   else
2522
      is_foreign_endianness = false; // else this header is for the same endianness as us
1 pmbaty 2523
 
16 pmbaty 2524
   if (image_header.flags & IMAGE_FLAGS_TRAILER_V2) // is it a V2 trailer ?
2 pmbaty 2525
   {
16 pmbaty 2526
      SHA512 (&ifs.data.bytes[ifs.offsets.imageheader], ifs.offsets.imagetrailer - ifs.offsets.imageheader, &ifs.data.bytes[ifs.offsets.imagetrailer]); // compute SHA512 checksum and write it in place
2527
      checksum = update_checksum (&ifs.data.bytes[ifs.offsets.imageheader], ifs.offsets.imagetrailer + SHA512_DIGEST_LENGTH - ifs.offsets.imageheader, is_foreign_endianness); // compute old checksum
2528
      memcpy (&ifs.data.bytes[ifs.offsets.imagetrailer + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place
2 pmbaty 2529
   }
16 pmbaty 2530
   else // old V1 trailer
3 pmbaty 2531
   {
16 pmbaty 2532
      checksum = update_checksum (&ifs.data.bytes[ifs.offsets.imageheader], ifs.offsets.imagetrailer - ifs.offsets.imageheader, is_foreign_endianness); // compute old checksum
2533
      memcpy (&ifs.data.bytes[ifs.offsets.imagetrailer], &checksum, 4); // and write it in place
3 pmbaty 2534
   }
2 pmbaty 2535
 
16 pmbaty 2536
   // now rewrite IFS with the correct checksums
19 pmbaty 2537
   ASSERT_WITH_ERRNO (Buffer_WriteToFile (&ifs.data, (ifs_pathname != NULL ? ifs_pathname : "<stdout>")));
14 pmbaty 2538
 
16 pmbaty 2539
   // finished, cleanup
2540
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
14 pmbaty 2541
   {
19 pmbaty 2542
      fsentry = &fsentries[fsentry_index]; // quick access to filesystem entry
2543
      if (S_ISDIR (fsentry->header.mode))
2544
         free (fsentry->u.dir.path);
2545
      else if (S_ISLNK (fsentry->header.mode))
2546
      {
2547
         free (fsentry->u.symlink.path);
2548
         free (fsentry->u.symlink.contents);
2549
      }
2550
      else if (S_ISREG (fsentry->header.mode))
2551
      {
2552
         free (fsentry->u.file.path);
2553
         free (fsentry->u.file.UNSAVED_databuf);
2554
      }
2555
      else if (S_ISFIFO (fsentry->header.mode))
2556
         free (fsentry->u.device.path);
14 pmbaty 2557
   }
2558
 
16 pmbaty 2559
   // and exit with a success code
2560
   LOG_INFO ("Success");
2561
   exit (0);
14 pmbaty 2562
}