Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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