Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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