Subversion Repositories QNX 8.QNX8 IFS tool

Rev

Rev 16 | Rev 18 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
// ifstool.c -- portable reimplementation of QNX's mkifs by Pierre-Marie Baty <pm@pmbaty.com>
2
 
12 pmbaty 3
// TODO: preboot file stripping
4
// TODO: startup file stripping
5
// TODO: kernel file stripping
6
// TODO: boot script compiler
7
 
16 pmbaty 8
// standard C includes
1 pmbaty 9
#include <stdint.h>
10
#include <stdbool.h>
11
#include <stdlib.h>
2 pmbaty 12
#include <stdarg.h>
1 pmbaty 13
#include <stdio.h>
14
#include <string.h>
16 pmbaty 15
#include <limits.h>
1 pmbaty 16
#include <errno.h>
17
#include <sys/stat.h>
18
#include <ctype.h>
19
#include <time.h>
20
 
16 pmbaty 21
// platform-specific includes
1 pmbaty 22
#ifdef _MSC_VER
23
#include <io.h>
14 pmbaty 24
#include <direct.h>
25
#include <sys/utime.h>
1 pmbaty 26
#else // !_MSC_VER
27
#include <sys/param.h>
28
#include <unistd.h>
14 pmbaty 29
#include <utime.h>
1 pmbaty 30
#endif // _MSC_VER
31
 
16 pmbaty 32
// own includes
33
#include "buffer.h"
34
#include "sha512.h"
35
#include "elffile.h"
36
#include "ifsfile.h"
37
#include "utility.h"
1 pmbaty 38
 
4 pmbaty 39
 
16 pmbaty 40
// compiler-specific glue
41
#ifndef _MSC_VER
42
#define sscanf_s sscanf // WARNING: TRUE FOR THIS FILE ONLY!
43
#endif // !_MSC_VER
4 pmbaty 44
 
8 pmbaty 45
 
16 pmbaty 46
// libasan (Address Sanitizer) options: this is not a daemon, so I don't care about leaks: they will be recovered by the OS at program exit
47
const char *__asan_default_options () { return ("detect_leaks=0"); }
8 pmbaty 48
 
15 pmbaty 49
 
11 pmbaty 50
// placeholder value
51
#define WILL_BE_FILLED_LATER 0xbaadf00d // urgh
10 pmbaty 52
 
1 pmbaty 53
 
11 pmbaty 54
// miscellaneous macros
55
#define ROUND_TO_UPPER_MULTIPLE(val,multiple) ((((val) + (size_t) (multiple) - 1) / (multiple)) * (multiple)) // note that val is being evaluated once, so it can be the result of a function call
56
#ifdef _WIN32
57
#define IS_DIRSEP(c) (((c) == '/') || ((c) == '\\')) // platform-specific directory separator, Win32 variant
16 pmbaty 58
#define PATH_SEP ";" // platform-specific PATH element separator (as string), Win32 variant
11 pmbaty 59
#else // !_WIN32, thus POSIX
60
#define IS_DIRSEP(c) ((c) == '/') // platform-specific directory separator, UNIX variant
16 pmbaty 61
#define PATH_SEP ":" // platform-specific PATH element separator (as string), UNIX variant
11 pmbaty 62
#endif // _WIN32
16 pmbaty 63
#define RECORD_SEP "\x1e" // arbitrarily-chosen ASCII record separator, as a C string suitable for e.g. strtok()
11 pmbaty 64
 
65
 
1 pmbaty 66
#define INITIAL_STARTUP_SCRIPT \
67
   /* procmgr_symlink /proc/boot/ldqnx-64.so.2 /usr/lib/ldqnx-64.so.2 */ \
7 pmbaty 68
   "\x34\x00" /*size*/ "\x04" /*type*/ "\x00" /*spare*/ "/proc/boot/ldqnx-64.so.2\0" "/usr/lib/ldqnx-64.so.2\0" \
1 pmbaty 69
   /* sh /proc/boot/startup.sh */ \
70
   "\x88\x00" /*size*/ "\x00" /*type*/ "\x00" /*spare*/ "\x00" /*CPU mask*/ "\x00" /*flags*/ "\x00\x00" /*reserved*/ "\x00" /*policy*/ "\x00" /*priority*/ "\02" /*argc*/ "\x02" /*envc*/ "sh\0" /*executable*/ "sh\0" "/proc/boot/startup.sh\0" /*argv*/ "PATH=/sbin:/usr/sbin:/bin:/usr/bin:/proc/boot\0" "LD_LIBRARY_PATH=/proc/boot:/lib:/lib/dll:/usr/lib\0" /*envp*/ \
71
   /* display_msg "Startup complete */ \
72
   "\x18\x00" /*size*/ "\x03" /*type*/ "\x00" /*spare*/ "Startup complete\n\0" "\x00\00" /*padding*/ \
73
   /* trailer */ \
74
   "\x00\x00\x00\x00"
75
 
76
 
11 pmbaty 77
// IFS directory entry insertion parameters structure type definition
1 pmbaty 78
typedef struct parms_s
79
{
80
   int dperms; // directory permissions (e.g. 0755)
81
   int perms; // file permissions (e.g. 0644)
82
   int uid; // owner user ID (e.g. 0 = root)
83
   int gid; // owner group ID (e.g. 0 = root)
84
   int st_mode; // entry type (e.g. S_IFREG for files) and permissions
7 pmbaty 85
   uint32_t mtime; // entry's modification time POSIX timestamp - set to UINT32_MAX to use the concerned files' mtime on the build host
86
   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 87
   char *prefix; // [prefix=path] install path (e.g. "proc/boot")
7 pmbaty 88
   bool should_follow_symlinks; // follow symlinks
89
   bool should_autosymlink_dylib; // dynamic libraries should be written under their official SONAME and a named symlink be created pointing at them
15 pmbaty 90
   bool should_keep_ld_output; // whether to keep .sym files produced by ld calls, togglable by the [+keeplinked] attribute
2 pmbaty 91
   bool is_compiled_bootscript; // entry has [+script] attribute
10 pmbaty 92
   int extra_ino_flags; // bitmap of extra inode flags (IFS_INO_xxx)
17 pmbaty 93
   char *search; // [search=path[:path]] binary search path (the default one will be constructed at startup)
2 pmbaty 94
 
16 pmbaty 95
   buffer_t data; // the resolved file's own data bytes
1 pmbaty 96
} parms_t;
97
 
98
 
16 pmbaty 99
// exported globals
100
int verbose_level = 1; // verbosity level, can be increased with multiple -v[...] flags
101
 
102
 
103
// global variables used in this module only
1 pmbaty 104
static char line_buffer[4096]; // scrap buffer for the IFS build file parser
105
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
106
static uint32_t image_end = UINT32_MAX; // default image end (no limit)
107
static uint32_t image_maxsize = UINT32_MAX; // default image max size (no limit)
108
static uint32_t image_totalsize = 0; // image total size, measured once all the blocks have been written to the output IFS file
109
static uint32_t image_align = 4; // default image alignment, as per QNX docs
110
static uint32_t image_kernel_ino = 0;
111
static uint32_t image_bootscript_ino = 0;
7 pmbaty 112
#if defined(__x86_64__)
1 pmbaty 113
static char image_processor[16] = "x86_64"; // default CPU type for which this image is built, either "x86_64" or "aarch64le" (will be used to find out the right include paths under $QNX_TARGET)
17 pmbaty 114
static char image_processor_base[16] = "x86_64"; // default base CPU type for which this image is built, either "x86_64" or "aarch64le" (will be used to find out the right include paths under $QNX_TARGET)
7 pmbaty 115
#elif defined(__aarch64__)
116
static char image_processor[16] = "aarch64le"; // default CPU type for which this image is built, either "x86_64" or "aarch64le" (will be used to find out the right include paths under $QNX_TARGET)
17 pmbaty 117
static char image_processor_base[16] = "aarch64"; // default base CPU type for which this image is built, either "x86_64" or "aarch64le" (will be used to find out the right include paths under $QNX_TARGET)
7 pmbaty 118
#else // unknown platform
119
#error Please port ifstool to this platform
120
#endif
1 pmbaty 121
static char *buildfile_pathname = NULL; // pathname of IFS build file
7 pmbaty 122
static char *current_line = NULL; // copy of current line in IFS build file
1 pmbaty 123
static int lineno = 0; // current line number in IFS build file
124
static char *QNX_TARGET = NULL; // value of the $QNX_TARGET environment variable
17 pmbaty 125
static char *SEARCH_PATH = NULL; // mallocated string of search paths, populated by the -r command-line argument
126
static char **saved_ELF_sections = NULL; // mallocated array of const strings, populated by the -s command-line argument
127
static size_t saved_ELF_section_count = 0; // number of elements in the saved_ELF_sections array
1 pmbaty 128
 
15 pmbaty 129
// bootable IFS support
130
static char *bootfile_pathname = NULL;           // HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS
131
static size_t bootfile_size = 0;                 // HACK: size of the bootcode binary blob file to put at the start of a bootable IFS
132
static char *startupfile_pathname = NULL;        // HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS
133
static size_t startupfile_ep_from_imagebase = 0; // HACK: startup code entrypoint offset from image base for a bootable IFS 
134
static char *kernelfile_pathname = NULL;         // HACK: pathname to precompiled kernel file blob to put in a bootable IFS
135
static size_t kernelfile_offset = 0;             // HACK: kernel file offset in bootable IFS
1 pmbaty 136
 
15 pmbaty 137
 
16 pmbaty 138
// exported function prototypes
139
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
140
 
141
 
1 pmbaty 142
// prototypes of local functions
143
static long long read_integer (const char *str); // reads an integer number for a string that may be specified in either hex, octal or decimal base, and may have an optional unit suffix (k, m, g, t)
17 pmbaty 144
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 145
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 146
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
147
static size_t Buffer_AppendIFSFileData (buffer_t *ifs_data, fsentry_t *fsentry); // writes the given filesystem entry's file data (i.e. its contents) to the IFS buffer
17 pmbaty 148
static int Buffer_StripELFFile (buffer_t *file, const char **saved_sections, const size_t saved_section_count, const char *indicative_pathname); // strips an ELF file buffer the way mkifs does it and returns whether it succeeded
7 pmbaty 149
static size_t add_fsentry (fsentry_t **fsentries, size_t *fsentry_count, parms_t *entry_parms, const char *stored_pathname, const char *buildhost_pathname); // stack up a new filesystem entry
1 pmbaty 150
static int fsentry_compare_pathnames_cb (const void *a, const void *b); // qsort() comparison callback that sorts filesystem entries by pathnames
17 pmbaty 151
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 152
 
153
 
16 pmbaty 154
// imported function prototypes
155
extern int dump_ifs_info (const char *ifs_pathname, bool want_everything); // [implemented in ifsdump.c] dumps detailed info about a particular IFS file on the standard output, returns 0 on success and >0 on error
156
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
157
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 158
 
159
 
16 pmbaty 160
int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness)
1 pmbaty 161
{
6 pmbaty 162
   // 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 163
 
5 pmbaty 164
   uint8_t accumulator[4] = { 0, 0, 0, 0 };
7 pmbaty 165
   const char *current_char_ptr;
166
   int32_t image_cksum;
5 pmbaty 167
   size_t i;
168
 
7 pmbaty 169
   image_cksum = 0;
170
   current_char_ptr = data;
5 pmbaty 171
   for (i = 0; i < data_len; i++)
172
   {
173
      accumulator[i % 4] = *current_char_ptr;
174
      if (i % 4 == 3)
175
         if (is_foreign_endianness)
8 pmbaty 176
            image_cksum += (accumulator[3] << 0) + (accumulator[2] << 8) + (accumulator[1] << 16) + (accumulator[0] << 24);
5 pmbaty 177
         else
8 pmbaty 178
            image_cksum += (accumulator[0] << 0) + (accumulator[1] << 8) + (accumulator[2] << 16) + (accumulator[3] << 24);
5 pmbaty 179
      current_char_ptr++;
180
   }
181
 
182
   return (is_foreign_endianness ? __builtin_bswap32 (-image_cksum) : -image_cksum);
183
}
184
 
185
 
1 pmbaty 186
static long long read_integer (const char *str)
187
{
188
   // 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)
189
 
190
   char *endptr = NULL;
191
   long long ret = strtoll (str, &endptr, 0); // use strtoll() to handle hexadecimal (0x...), octal (0...) and decimal (...) bases
192
   if (endptr != NULL)
193
   {
15 pmbaty 194
      if      ((*endptr == 'k') || (*endptr == 'K')) ret *= (size_t) 1024;
1 pmbaty 195
      else if ((*endptr == 'm') || (*endptr == 'M')) ret *= (size_t) 1024 * 1024;
196
      else if ((*endptr == 'g') || (*endptr == 'G')) ret *= (size_t) 1024 * 1024 * 1024;
197
      else if ((*endptr == 't') || (*endptr == 'T')) ret *= (size_t) 1024 * 1024 * 1024 * 1024; // future-proof enough, I suppose?
198
   }
199
   return (ret);
200
}
201
 
202
 
17 pmbaty 203
static char *resolve_pathname (const char *pathname, const char *search_paths_or_NULL_for_MKIFS_PATH_envvar)
2 pmbaty 204
{
16 pmbaty 205
   // locates pathname among search path and returns resolved pathname (static buffer) or NULL.
2 pmbaty 206
 
17 pmbaty 207
   typedef struct default_path_s { bool uses_processor_base; char *subpath; } default_path_t;
208
 
209
   static const default_path_t default_paths[] =
210
   {
211
      { false, "/sbin"     }, // prefix with $PROCESSOR/
212
      { false, "/usr/sbin" }, // prefix with $PROCESSOR/
213
      { false, "/boot/sys" }, // prefix with $PROCESSOR/
214
      { true,  "/boot/sys" }, // prefix with $PROCESSOR_BASE/
215
      { false, "/bin"      }, // prefix with $PROCESSOR/
216
      { false, "/usr/bin"  }, // prefix with $PROCESSOR/
217
      { false, "/lib"      }, // prefix with $PROCESSOR/
218
      { false, "/lib/dll"  }, // prefix with $PROCESSOR/
219
      { false, "/usr/lib"  }  // prefix with $PROCESSOR/
220
   };
16 pmbaty 221
   static thread_local char *resolved_pathname = NULL;
2 pmbaty 222
 
17 pmbaty 223
   size_t defaultpath_index;
16 pmbaty 224
   struct stat stat_buf;
225
   const char *nextsep;
226
   const char *token;
2 pmbaty 227
 
16 pmbaty 228
   // initial allocation (per thread)
229
   if (resolved_pathname == NULL)
2 pmbaty 230
   {
16 pmbaty 231
      resolved_pathname = malloc (MAXPATHLEN);
232
      ASSERT_WITH_ERRNO (resolved_pathname);
2 pmbaty 233
   }
234
 
7 pmbaty 235
   // is it an absolute pathname (POSIX and Windows variants) ?
17 pmbaty 236
   if (IS_DIRSEP (pathname[0])
237
#ifdef _WIN32
238
       || (isalpha (pathname[0]) && (pathname[1] == ':') && IS_DIRSEP (pathname[2]))
239
#endif // _WIN32
240
       )
16 pmbaty 241
      strcpy_s (resolved_pathname, MAXPATHLEN, pathname); // in this case, it MUST exist at its designated location (either absolute or relative to the current working directory)
7 pmbaty 242
   else // the path is relative, search it among the search paths we have
243
   {
17 pmbaty 244
      // QNX docs:
245
      // When searching for host files to be included in the image, search
246
      // the default paths used for storing binaries within the specified
247
      // directory before searching the default paths within $QNX_TARGET.
248
      // You can define multiple -r options; each adds a set of paths to
249
      // search for files. The -r options are evaluated from left to right
250
      // meaning the paths prefixed with the first (leftmost) rootdir are
251
      // searched first, then those prefixed with the second rootdir, and
252
      // so on.
253
      // Normally, mkifs searches any paths defined in $MKIFS_PATH when
254
      // it was called and then the default paths within $QNX_TARGET. The
255
      // default paths are based on the CPU architecture specified by
256
      // $PROCESSOR and $PROCESSOR_BASE. If you specify -r options, mkifs
257
      // searches the default paths prefixed with each dir variable before
258
      // searching those within $QNX_TARGET. These paths are:
259
      //   dir/${PROCESSOR}/sbin
260
      //   dir/${PROCESSOR}/usr/sbin
261
      //   dir/${PROCESSOR}/boot/sys
262
      //   dir/${PROCESSOR_BASE}/boot/sys
263
      //   dir/${PROCESSOR}/bin
264
      //   dir/${PROCESSOR}/usr/bin
265
      //   dir/${PROCESSOR}/lib
266
      //   dir/${PROCESSOR}/lib/dll
267
      //   dir/${PROCESSOR}/usr/lib
268
      // NOTE: The structure of the directory paths under dir must be
269
      // identical to that of the default paths under $QNX_TARGET, but the
270
      // root dir itself may be any path you choose. For example, if you
271
      // wanted to include /scratch/aarch64le/sbin/devb-sata, you would
272
      // specify a -r option like this:
273
      //   -r /scratch
274
      // Note that you don't include $PROCESSOR or $PROCESSOR_BASE in dir.
275
 
276
      //  - search all paths in explicit path/[default paths] (if explicit path supplied)
277
      //  - search all paths in (-r flags if have some|MKIFS_PATH)/[default paths] (if no explicit path supplied)
278
      //  - search all paths in $QNX_TARGET/[default paths]
279
 
280
      // 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
281
      if (search_paths_or_NULL_for_MKIFS_PATH_envvar == NULL)
282
         search_paths_or_NULL_for_MKIFS_PATH_envvar = (SEARCH_PATH != NULL ? SEARCH_PATH : getenv ("MKIFS_PATH"));
283
 
7 pmbaty 284
      // construct a potential final path using each element of the search path
17 pmbaty 285
      if (search_paths_or_NULL_for_MKIFS_PATH_envvar != NULL)
7 pmbaty 286
      {
17 pmbaty 287
         token = (*search_paths_or_NULL_for_MKIFS_PATH_envvar != 0 ? search_paths_or_NULL_for_MKIFS_PATH_envvar : NULL);
288
         nextsep = (token != NULL ? &token[strcspn (token, PATH_SEP)] : NULL);
289
         while (token != NULL)
290
         {
291
            for (defaultpath_index = 0; defaultpath_index < sizeof (default_paths) / sizeof (default_paths[0]); defaultpath_index++)
292
            {
293
               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);
294
               if ((stat (resolved_pathname, &stat_buf) == 0) && S_ISREG (stat_buf.st_mode))
295
                  return (resolved_pathname); // if a file can indeed be found at this location, stop searching
296
            }
297
 
298
            token = (*nextsep != 0 ? nextsep + 1 : NULL);
299
            nextsep = (token != NULL ? &token[strcspn (token, PATH_SEP)] : NULL);
300
         }
301
      }
302
 
303
      // file not found in search paths: look under QNX_TARGET
304
      for (defaultpath_index = 0; defaultpath_index < sizeof (default_paths) / sizeof (default_paths[0]); defaultpath_index++)
305
      {
306
         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);
16 pmbaty 307
         if ((stat (resolved_pathname, &stat_buf) == 0) && S_ISREG (stat_buf.st_mode))
308
            return (resolved_pathname); // if a file can indeed be found at this location, stop searching
7 pmbaty 309
      }
310
   }
311
 
16 pmbaty 312
   errno = ENOENT; // we exhausted all possibilities
313
   return (NULL); // file not found, return with ENOENT
314
}
7 pmbaty 315
 
16 pmbaty 316
 
317
static size_t Buffer_WriteIFSDirectoryEntryAt (buffer_t *ifs, const size_t write_offset, const fsentry_t *fsentry)
318
{
319
   // writes a directory entry in the image filesystem buffer pointed to by ifs at write_offset (or fakes so if ifs is NULL)
320
   // and return the number of bytes written (or that would have been written)
321
 
322
   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";
323
 
324
   size_t datalen;
325
   size_t count;
326
 
327
   count = 0;
328
   if (ifs != NULL)
329
      ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->header, sizeof (fsentry->header))); // write the entry header (PACKED STRUCT)
330
   count += sizeof (fsentry->header);
331
   if (S_ISREG (fsentry->header.mode))
15 pmbaty 332
   {
16 pmbaty 333
      if (ifs != NULL)
334
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.file.offset, sizeof (uint32_t))); // write offset
335
      count += sizeof (uint32_t);
336
      if (ifs != NULL)
337
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.file.size,   sizeof (uint32_t))); // write size
338
      count += sizeof (uint32_t);
339
      datalen = strlen (fsentry->u.file.path) + 1;
340
      if (ifs != NULL)
341
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.file.path, datalen)); // write null-terminated path (no leading slash)
342
      count += datalen;
15 pmbaty 343
   }
16 pmbaty 344
   else if (S_ISDIR (fsentry->header.mode))
7 pmbaty 345
   {
16 pmbaty 346
      datalen = strlen (fsentry->u.dir.path) + 1;
347
      if (ifs != NULL)
348
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.dir.path, datalen)); // write null-terminated path (no leading slash)
349
      count += datalen;
7 pmbaty 350
   }
16 pmbaty 351
   else if (S_ISLNK (fsentry->header.mode))
7 pmbaty 352
   {
16 pmbaty 353
      if (ifs != NULL)
354
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.symlink.sym_offset, sizeof (uint16_t))); // write offset
355
      count += sizeof (uint16_t);
356
      if (ifs != NULL)
357
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.symlink.sym_size,   sizeof (uint16_t))); // write size
358
      count += sizeof (uint16_t);
359
      datalen = strlen (fsentry->u.symlink.path) + 1;
360
      if (ifs != NULL)
361
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.symlink.path, datalen)); // write null-terminated path (no leading slash)
362
      count += datalen;
363
      datalen = strlen (fsentry->u.symlink.contents) + 1;
364
      if (ifs != NULL)
365
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.symlink.contents, datalen)); // write null-terminated symlink contents
366
      count += datalen;
7 pmbaty 367
   }
16 pmbaty 368
   else
369
   {
370
      if (ifs != NULL)
371
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.device.dev,  sizeof (uint32_t))); // write dev number
372
      count += sizeof (uint32_t);
373
      if (ifs != NULL)
374
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.device.rdev, sizeof (uint32_t))); // write rdev number
375
      count += sizeof (uint32_t);
376
      datalen = strlen (fsentry->u.device.path) + 1;
377
      if (ifs != NULL)
378
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.device.path, datalen)); // write null-terminated path (no leading slash)
379
      count += datalen;
380
   }
7 pmbaty 381
 
16 pmbaty 382
   ASSERT (count <= fsentry->header.size, "attempt to write invalid dirent (claimed size %zd, written size %zd). Aborting.", (size_t) fsentry->header.size, count);
383
   if (count < fsentry->header.size)
384
   {
385
      if (ifs != NULL)
386
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, zeropad_buffer, fsentry->header.size - count)); // pad as necessary
387
      count += fsentry->header.size - count;
388
   }
389
 
390
   return (count);
7 pmbaty 391
}
392
 
393
 
16 pmbaty 394
static size_t Buffer_AppendIFSFileData (buffer_t *ifs_data, fsentry_t *fsentry)
1 pmbaty 395
{
16 pmbaty 396
   // writes the given filesystem entry's file data (i.e. its contents) to the IFS buffer
1 pmbaty 397
 
16 pmbaty 398
   elf_program_header_t *phdr;
399
   elf_header_t *elf;
17 pmbaty 400
   size_t fixed_physical_addr;
16 pmbaty 401
   size_t corrective_offset;
402
   //size_t segment_type;
403
   size_t segment_size;
404
   size_t table_index;
405
   size_t table_count;
406
   size_t data_offset;
1 pmbaty 407
 
16 pmbaty 408
   ASSERT (S_ISREG (fsentry->header.mode), "function called for invalid dirent"); // consistency check
409
   data_offset = ifs_data->size; // see where we are
1 pmbaty 410
 
16 pmbaty 411
   // is the file we're storing a preprocessed ELF file ?
412
   if ((fsentry->header.ino & IFS_INO_PROCESSED_ELF)
413
#ifndef PROCNTO_WIP
414
      && (strstr (fsentry->u.file.path, "/procnto-smp-instr") == NULL)
415
#endif // !PROCNTO_WIP
416
       )
1 pmbaty 417
   {
16 pmbaty 418
 
419
      elf = (elf_header_t *) fsentry->u.file.UNSAVED_databuf; // quick access to ELF header
420
      table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); // get the number of program headers
421
      for (table_index = 0; table_index < table_count; table_index++)
422
      {
423
         phdr = (elf_program_header_t *) &fsentry->u.file.UNSAVED_databuf[ELF_GET_NUMERIC (elf, elf, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, program_header_item_size) * table_index]; // quick access to program header
424
         //segment_type = ELF_GET_NUMERIC (elf, phdr, segment_type); // get segment type
425
         //if (!((segment_type >= 2) && (segment_type <= 7) || ((segment_type >= 0x6474e550) && (segment_type <= 0x6474e552)) || (segment_type == 0x70000001)))
426
         //   continue; // NOTE: only certain segments types must be corrected
427
 
428
 
429
         corrective_offset = ELF_GET_NUMERIC (elf, phdr, virtual_addr) - ELF_GET_NUMERIC (elf, phdr, file_offset);
430
         segment_size = ELF_GET_NUMERIC (elf, phdr, size_in_memory); // get this ELF segment's occupied size in memory
431
         if (segment_size != 0) // only patch the physical address of segments that have an actual size in memory
17 pmbaty 432
         {
433
            fixed_physical_addr = ELF_GET_NUMERIC (elf, phdr, physical_addr) + image_base + data_offset - corrective_offset;
434
            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)
435
         }
16 pmbaty 436
      }
1 pmbaty 437
   }
438
 
16 pmbaty 439
   ASSERT_WITH_ERRNO (Buffer_Append (ifs_data, fsentry->u.file.UNSAVED_databuf, fsentry->u.file.size)); // write file data blob
440
   return (ifs_data->size - data_offset); // return the number of bytes written
1 pmbaty 441
}
442
 
443
 
16 pmbaty 444
static inline size_t Buffer_LocateOrAppendIfNecessaryAndReturnOffsetOf (buffer_t *buffer, const char *str)
10 pmbaty 445
{
16 pmbaty 446
   // helper function used in add_fsentry(): locates or appends str to buffer and returns its relative offset in the buffer
447
 
448
   size_t str_len_including_terminator = strlen (str) + 1;
449
   void *occurrence = Buffer_FindFirst (buffer, str, str_len_including_terminator);
450
   if (occurrence == NULL)
451
   {
452
      ASSERT_WITH_ERRNO (Buffer_Append (buffer, str, str_len_including_terminator));
453
      occurrence = Buffer_FindFirst (buffer, str, str_len_including_terminator);
454
      ASSERT_WITH_ERRNO (occurrence);
455
   }
456
   return (Buffer_OffsetOf (buffer, occurrence)); // can't fail
10 pmbaty 457
}
458
 
459
 
17 pmbaty 460
static int Buffer_StripELFFile (buffer_t *file, const char **saved_sections, const size_t saved_section_count, const char *indicative_pathname)
10 pmbaty 461
{
16 pmbaty 462
   // NOTE: for each ELF file, mkifs
463
   // -> 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)
464
   // -> 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
465
   // 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 466
 
16 pmbaty 467
   // reconstructed ELF:
468
   // ==== START OF FILE ====
469
   // ELF header
470
   // program header table
471
   //  (same sections, just p_addr offset changed)
472
   // section data 5 (named ".note.gnu.build-id")
473
   //  "............GNU....ZY.....c.o..l"
474
   // PROGRAM
475
   // sections table
476
   // + section 1: ALL ZEROES
477
   // + 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"
478
   // + 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"
479
   // + 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.."
480
   // + section 5: fileoffs 0x190 size 0x32 --> ".note.gnu.build-id" --> GNU build ID
481
   // + section 6: fileoffs 0x256e size 0x40 --> ".shstrtab" --> sections names strings table
482
   // section data 2 (named "QNX_info")
483
   //  (QNX binary description)
484
   // section data 3 (named ".gnu_debuglink")
485
   //  (debug file)
486
   // section data 4 (named "QNX_usage")
487
   //  (help text)
488
   // section data 6 (named ".shstrtab")
489
   //  "\0"
490
   //  ".shstrtab\0"
491
   //  "QNX_info\0"
492
   //  ".gnu_debuglink\0"
493
   //  "QNX_usage\0"
494
   //  ".note.gnu.build-id\0"
495
   // ==== END OF FILE ====
10 pmbaty 496
 
16 pmbaty 497
   #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
498
   #define ADD_SECTION(section_name,section_ptr) do { \
499
      void *reallocated_ptr = realloc (elf_sections, (elf_section_count + 1) * sizeof (elf_section_t)); \
500
      ASSERT_WITH_ERRNO (reallocated_ptr); \
501
      elf_sections = reallocated_ptr; \
502
      elf_sections[elf_section_count].name = (section_name); \
503
      Buffer_Initialize (&elf_sections[elf_section_count].data); \
504
      *(section_ptr) = &elf_sections[elf_section_count]; \
505
      elf_section_count++; \
506
   } while (0)
507
 
508
   typedef struct elf_section_s
10 pmbaty 509
   {
16 pmbaty 510
      const char *name;
511
      elf_section_header_t header;
512
      buffer_t data;
513
   } elf_section_t;
10 pmbaty 514
 
16 pmbaty 515
   const elf_program_header_t *phdr;
516
   const elf_section_header_t *shdr;
517
   elf_section_t *elf_sections = NULL; // mallocated
518
   elf_section_t *elf_section = NULL;
519
   size_t elf_section_count = 0;
520
   size_t new_shdrtable_offset;
17 pmbaty 521
   size_t new_shdrtable_len;
16 pmbaty 522
   size_t sectiondata_start;
523
   size_t sectiondata_size;
524
   size_t array_index;
525
   size_t table_index;
526
   size_t table_count;
527
   size_t page_size;
10 pmbaty 528
 
16 pmbaty 529
   // find out the platform page size
530
   if (ELF_GET_NUMERIC (ELFHDR, ELFHDR, instruction_set) == ELF_MACHINE_X86_64)
531
      page_size = 4 * 1024; // 4 kb pages on Intel processors
532
   else if (ELF_GET_NUMERIC (ELFHDR, ELFHDR, instruction_set) == ELF_MACHINE_AARCH64)
533
      page_size = 16 * 1024; // 16 kb pages on ARM64
534
   else
535
   {
536
      errno = ENOTSUP; // unsupported architecture: set errno to something meaningful
537
      return (0); // and return an error value
538
   }
7 pmbaty 539
 
16 pmbaty 540
   // parse the program header table, and measure the farthest offset known by this table where we'll write the reconstructed section headers table
1 pmbaty 541
 
16 pmbaty 542
   new_shdrtable_offset = 0;
543
   table_count = ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_len);
544
   for (table_index = 0; table_index < table_count; table_index++)
1 pmbaty 545
   {
16 pmbaty 546
      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
547
      if (ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_file) > new_shdrtable_offset)
548
         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 549
   }
16 pmbaty 550
   /*
551
   size_t new_shdrtable_offset_method2 = 0;
552
   for (table_index = 0; table_index < table_count; table_index++)
1 pmbaty 553
   {
16 pmbaty 554
      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
555
      size_t segment_type = ELF_GET_NUMERIC (ELFHDR, phdr, segment_type); // get segment type
556
      if (!((segment_type >= 2) && (segment_type <= 7)))
557
         continue; // NOTE: only certain segments types must be corrected
558
      if (ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_memory) > new_shdrtable_offset_method2)
559
         new_shdrtable_offset_method2 = ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_memory);
1 pmbaty 560
   }
16 pmbaty 561
   if (new_shdrtable_offset_method2 > new_shdrtable_offset)
562
      LOG_DEBUG ("METHOD2: %llx > %llx", new_shdrtable_offset_method2, new_shdrtable_offset);*/
563
   //new_shdrtable_offset = ROUND_TO_UPPER_MULTIPLE (new_shdrtable_offset, page_size); // round to page size
564
 
565
   // re-create the section header table
566
   ADD_SECTION (".shstrtab", &elf_section); // the first section will be the section names strings table
567
   ASSERT_WITH_ERRNO (Buffer_InitWithByteArray (&elf_section->data, "\0")); // initialize an empty section headers strings table
568
   ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&elf_section->data, ".shstrtab\0")); // append ".shstrtab" *INCLUDING* its null terminator
569
 
570
   // go through the saved sections array and see if such an ELF section is present in the ELF file
17 pmbaty 571
   for (array_index = 0; array_index < saved_section_count; array_index++)
16 pmbaty 572
      if ((shdr = elf_get_section_header_by_name (ELFHDR, saved_sections[array_index])) != NULL) // does this ELF have such a section ?
1 pmbaty 573
      {
16 pmbaty 574
         ADD_SECTION (saved_sections[array_index], &elf_section); // yes, so save it
575
         sectiondata_start = ELF_GET_NUMERIC (ELFHDR, shdr, file_offset); // identify section data start offset
576
         sectiondata_size = ELF_GET_NUMERIC (ELFHDR, shdr, size); // identify section data length
577
         if (sectiondata_start + sectiondata_size >= new_shdrtable_offset) // should this section be moved ?
578
            ASSERT_WITH_ERRNO (Buffer_InitWithData (&elf_section->data, &file->bytes[sectiondata_start], sectiondata_size)); // have a copy of this section's data
579
         else
580
            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 581
         //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 582
 
583
         // prepare this section's "fixed" header
584
         memcpy (&elf_section->header, shdr, ELF_STRUCT_SIZE (ELFHDR, shdr)); // have a copy of the old section header first
585
         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 586
      }
16 pmbaty 587
 
588
   // jump over the new section headers table and write the saved sections data after the section headers table
589
   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
590
   for (table_index = 1; table_index < elf_section_count; table_index++)
1 pmbaty 591
   {
16 pmbaty 592
      elf_section = &elf_sections[table_index]; // quick access to ELF section about to be written
593
      if (elf_section->data.bytes != NULL) // was this section data backed up waiting to be relocated ?
1 pmbaty 594
      {
16 pmbaty 595
         ELF_SET_NUMERIC (ELFHDR, &elf_section->header, file_offset, file->size); // fix section offset
596
         Buffer_AppendBuffer (file, &elf_section->data); // append this section's data to the ELF file
1 pmbaty 597
      }
598
   }
16 pmbaty 599
   // write the section header strings table as the last section
600
   elf_section = &elf_sections[0]; // quick access to ELF section about to be written
601
   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
602
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, type, ELF_SECTIONTYPE_STRINGTABLE); // section type (SHT_STRTAB)
603
   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)
604
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, virtual_addr, 0); // this section does not need to be mapped
605
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, file_offset, file->size); // fix section offset
606
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, size, elf_sections[0].data.size); // section size
607
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, linked_index, 0); // this section is not linked to any other
608
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, info, 0); // this section has no additional info
609
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, alignment, 1); // this section is byte-aligned
610
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, entry_size, 0); // this section is not a table, so entry_size is zero
611
   Buffer_AppendBuffer (file, &elf_section->data); // append section headers strings table section data to ELF file
1 pmbaty 612
 
16 pmbaty 613
   // now write the section headers table
614
   memset (&file->bytes[new_shdrtable_offset], 0, ELF_STRUCT_SIZE (ELFHDR, &elf_sections[0].header)); // the first section header is always zerofilled
615
   for (table_index = 1; table_index < elf_section_count; table_index++)
616
      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
617
   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 618
 
16 pmbaty 619
   // and finally fix the ELF master header
17 pmbaty 620
   new_shdrtable_len = 1 + elf_section_count; // take in account that the first entry in the section headers table is empty
16 pmbaty 621
   ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_table_offset, new_shdrtable_offset);
17 pmbaty 622
   ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_table_len, new_shdrtable_len);
16 pmbaty 623
   ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_names_idx, elf_section_count); // the section headers strings table is the last section
624
 
625
   // align size with page size (4096 on x86, 16k on ARM), zerofilling the extra space
626
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (file, ROUND_TO_UPPER_MULTIPLE (file->size, page_size)));
627
 
628
   // cleanup
629
   for (table_index = 0; table_index < elf_section_count; table_index++)
630
      Buffer_Forget (&elf_sections[table_index].data); // free all sections' backing buffers
631
 
632
   #undef ELFHDR // undefine the macro that used to always point to the ELF header at the beginning of the file
633
   return (1); // success
1 pmbaty 634
}
635
 
636
 
7 pmbaty 637
static size_t add_fsentry (fsentry_t **fsentries, size_t *fsentry_count, parms_t *entry_parms, const char *stored_pathname, const char *buildhost_pathname)
1 pmbaty 638
{
16 pmbaty 639
   static thread_local char *candidate_pathname = NULL;
1 pmbaty 640
   static int inode_count = 0; // will be preincremented each time this function is called
641
 
7 pmbaty 642
   const char *original_stored_pathname = NULL;
16 pmbaty 643
   buffer_t *shstrtab = NULL;
7 pmbaty 644
   const char *canonical_dylib_name;
645
   const char *dynamic_strings; // strings table of the ".dynamic" section
646
   const char *last_dirsep;
15 pmbaty 647
   char *global_envstring = NULL;
648
   size_t global_envstring_len = 0;
649
   char *startup_name = NULL;
650
   char *procnto_name = NULL;
7 pmbaty 651
   char *resolved_pathname;
1 pmbaty 652
   void *reallocated_ptr;
7 pmbaty 653
   void *old_data;
1 pmbaty 654
   struct stat stat_buf;
655
   fsentry_t *fsentry;
656
 
16 pmbaty 657
   // initial allocation (per thread)
658
   if (candidate_pathname == NULL)
659
   {
660
      candidate_pathname = malloc (MAXPATHLEN);
661
      ASSERT_WITH_ERRNO (candidate_pathname);
662
   }
663
 
1 pmbaty 664
   if (S_ISDIR (entry_parms->st_mode)) // are we storing a directory ?
665
   {
15 pmbaty 666
      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 667
   }
668
   else if (S_ISREG (entry_parms->st_mode)) // else are we storing a regular file ?
669
   {
7 pmbaty 670
      if (strcmp (stored_pathname, "/proc/boot/boot") == 0) // is it the kernel ?
1 pmbaty 671
      {
7 pmbaty 672
         // HACK: for now just consider the kernel as a binary blob
673
         // FIXME: reimplement properly
15 pmbaty 674
#ifdef PROCNTO_WIP // FIXME: segment corruption somewhere!
675
         char *linebit_start;
676
         char *content_line;
677
         char *write_ptr;
678
         char *token;
679
         char *value;
16 pmbaty 680
         char *ctx;
15 pmbaty 681
         bool is_quoted_context;
682
         bool was_string_split;
683
 
684
         // parse each line of contents
16 pmbaty 685
         ASSERT (entry_parms->data.len > 0, "kernel specification without inline contents");
686
         for (content_line = strtok_r (entry_parms->data.bytes, "\n", &ctx); content_line != NULL; content_line = strtok_r (NULL, "\n", ctx))
15 pmbaty 687
         {
688
            while (isspace (*content_line))
689
               content_line++; // skip leading spaces
690
            if ((*content_line == '#') || (*content_line == 0))
691
               continue; // skip comments and empty lines
692
 
693
            // format of a line: [attributes] [env assignation] [...] [executable] [arg] [...] [comment]
694
            // example: "[uid=0 gid=0 perms=0700] CONFIG_PATH=/proc/boot:/etc procnto-smp-instr -v -mr -d 0777 -u 0777"
695
            //LOG_DEBUG ("parsing line: %s", content_line);
696
 
697
            // does this line start with an attribute block ?
698
            if (*content_line == '[')
699
            {
700
               content_line++; // skip the leading square bracket
701
               linebit_start = content_line; // remember where it starts
702
               is_quoted_context = false; // reach the next unescaped closing square bracket that is not between quotes
703
               while ((*content_line != 0) && !((*content_line == ']') && (content_line[-1] != '\\') && !is_quoted_context))
704
               {
705
                  if (*content_line == '"')
706
                     is_quoted_context ^= true; // remember when we're between quotes
707
                  else if (!is_quoted_context && (*content_line == ' '))
16 pmbaty 708
                     *content_line = RECORD_SEP[0]; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
15 pmbaty 709
                  content_line++; // reach the next unescaped closing square bracket
710
               }
711
               if (*content_line != ']')
712
               {
713
                  LOG ("warning", 0, "syntax error in \"%s\" line %d: unterminated attributes block (skipping)", buildfile_pathname, lineno);
714
                  continue; // invalid attribute block, skip line
715
               }
716
               *content_line = 0; // end the attribute block so that it is a parsable C string
717
 
718
               // now parse the attribute tokens (NOTE: THE LIST OF ALLOWED ATTRIBUTES HERE IS NOT DOCUMENTED)
16 pmbaty 719
               token = strtok_r (linebit_start, RECORD_SEP, &ctx);
15 pmbaty 720
               while (token != NULL)
721
               {
722
                  #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
17 pmbaty 723
                  if (false)
724
                  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.)
725
                  else if (strncmp (token, "uid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms->uid     = (int) read_integer (value); }
15 pmbaty 726
                  else if (strncmp (token, "gid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms->gid     = (int) read_integer (value); }
727
                  else if (strncmp (token, "perms=",   6) == 0) { REACH_TOKEN_VALUE (); entry_parms->perms   = (int) read_integer (value); }
728
                  else if (strcmp (token, "+followlink") == 0) entry_parms->should_follow_symlinks = true;
729
                  else if (strcmp (token, "-followlink") == 0) entry_parms->should_follow_symlinks = false;
730
                  else if (strcmp (token, "+keeplinked") == 0) entry_parms->should_keep_ld_output = true;
731
                  else if (strcmp (token, "-keeplinked") == 0) entry_parms->should_keep_ld_output = false;
732
                  else LOG_WARNING ("unimplemented bootstrap executable attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, token);
733
                  #undef REACH_TOKEN_VALUE
16 pmbaty 734
                  token = strtok_r (NULL, RECORD_SEP, &ctx); // proceed to next attribute token
15 pmbaty 735
               }
736
 
737
               content_line++; // reach the next character
738
               while ((*content_line != 0) && isspace (*content_line))
739
                  content_line++; // skip leading spaces
740
            } // end of "this line starts with an attributes block"
741
 
742
            // there's data in this line. We expect an executable OR a variable name. Read it and unescape escaped characters
743
            while (*content_line != 0)
744
            {
745
               linebit_start = content_line; // remember the name starts here
746
               write_ptr = linebit_start;
747
               is_quoted_context = (*content_line == '"');
748
               if (is_quoted_context)
749
                  content_line++; // skip a possible initial quote in the name
750
               while ((*content_line != 0) && ((!is_quoted_context && (*content_line != '=') && !isspace (*content_line)) || (is_quoted_context && (*content_line == '"'))))
751
               {
752
                  if (*content_line == '\\')
753
                  {
754
                     content_line++;
755
                     *write_ptr++ = *content_line; // unescape characters that are escaped with '\'
756
                  }
757
                  else
758
                     *write_ptr++ = *content_line;
759
                  content_line++;
760
               }
761
 
762
               // we reached a closing quote, a space OR an equal sign
763
               if (*content_line == '=')
764
               {
765
                  // it's an environment variable assignation
766
                  *write_ptr++ = *content_line++; // skip the equal sign
767
                  is_quoted_context = (*content_line == '"');
768
                  if (is_quoted_context)
769
                     content_line++; // skip a possible initial quote in the value
770
                  while ((*content_line != 0) && ((!is_quoted_context && (*content_line != '=') && !isspace (*content_line)) || (is_quoted_context && (*content_line == '"'))))
771
                  {
772
                     if (*content_line == '\\')
773
                     {
774
                        content_line++;
775
                        *write_ptr++ = *content_line; // unescape characters that are escaped with '\'
776
                     }
777
                     else
778
                        *write_ptr++ = *content_line;
779
                     content_line++;
780
                  }
781
                  if (*write_ptr != 0)
782
                  {
783
                     *write_ptr = 0; // terminate the string if necessary
784
                     was_string_split = true;
785
                  }
786
                  else
787
                     was_string_split = false;
788
                  if (is_quoted_context && (*content_line == '"'))
789
                     content_line++; // skip a possible final quote
790
                  while ((*content_line != 0) && isspace (*content_line))
791
                     content_line++; // skip spaces
792
 
793
                  // now linebit_start is of the form "NAME=VALUE"
794
                  LOG_DEBUG ("assignation: [%s]", linebit_start);
795
 
796
                  // TODO: grow global_envstring
797
 
798
                  //reallocated_ptr = realloc (global_envstring, global_envstring_len + strlen ())
799
 
800
                  if (was_string_split)
801
                     *write_ptr = ' '; // restore string continuity for parsing to continue
802
                  while ((*content_line != 0) && isspace (*content_line))
803
                     content_line++; // skip spaces
804
               }
805
               else // it's either a closing quote or a space
806
               {
807
                  *write_ptr = 0; // terminate the string
808
                  if (is_quoted_context && (*content_line == '"'))
809
                     content_line++; // skip a possible final quote
810
 
811
                  LOG_DEBUG ("exe name: [%s]", linebit_start);
812
 
813
                  while ((*content_line != 0) && isspace (*content_line))
814
                     content_line++; // skip leading spaces
815
 
816
                  // it's an executable name. As per QNX docs, the first executable must be startup-*, the last executable must be procnto.
817
                  if (startup_name == NULL)
818
                     startup_name = strdup (linebit_start);
819
                  else
820
                  {
821
                     if (procnto_name != NULL)
822
                        free (procnto_name);
823
                     procnto_name = strdup (linebit_start);
824
                  }
825
 
826
                  if ((*content_line == '#') || (*content_line == 0))
827
                     break; // if we reach the end of the line, stop parsing
828
 
829
                  // what comes after now must be optional arguments
830
                  while ((*content_line != 0) && isspace (*content_line))
831
                     content_line++; // skip leading spaces
832
 
833
                  // FIXME: parse executable command-line arguments
834
 
835
                  break; // stop parsing once all the arguments have been read
836
               }
837
            }
838
         } // end of parsing
839
         free (entry_parms->data.bytes); // free the inline specification once it's parsed
840
         entry_parms->data.bytes = NULL;
841
         entry_parms->data.len = 0;
842
 
16 pmbaty 843
         ASSERT (startup_name && *startup_name, "the QNX startup executable (startup-*) is missing in this bootstrap inline specification");
844
         ASSERT (procnto_name && *procnto_name, "the QNX kernel (procnto-*) is missing in this bootstrap inline specification");
15 pmbaty 845
 
846
         // now we know which startup and procnto executables to use
847
         LOG_DEBUG ("Startup: %s", startup_name);
848
         LOG_DEBUG ("Kernel: %s", procnto_name);
849
 
17 pmbaty 850
         sprintf (candidate_pathname, "%s/%s", (entry_parms->prefix != NULL ? entry_parms->prefix : ""), procnto_name); // fix the entry name
7 pmbaty 851
         stored_pathname = candidate_pathname;
15 pmbaty 852
         entry_parms->extra_ino_flags |= /*IFS_INO_PROCESSED_ELF | */IFS_INO_BOOTSTRAP_EXE; // procnto needs to have these flags stamped on the inode
853
         entry_parms->st_mode = S_IFREG | entry_parms->perms; // apply specified procnto permissions
10 pmbaty 854
         image_kernel_ino = entry_parms->extra_ino_flags | (inode_count + 1);
15 pmbaty 855
 
856
         static thread_local char linker_pathname[MAXPATHLEN] = "";
857
         static thread_local char linker_sysroot_arg[MAXPATHLEN] = "";
858
         static thread_local char linker_script_pathname_arg[MAXPATHLEN] = "";
859
         static thread_local char procnto_buildhost_pathname[MAXPATHLEN] = "";
860
         static thread_local char procnto_sym_filename[MAXPATHLEN] = "";
861
 
862
         // construct the arguments that are based on environment variables (infer QNX_HOST from QNX_TARGET)
863
#if defined(_WIN32)
864
         sprintf (linker_pathname, "%s/../../host/win64/x86_64/usr/bin/%s-ld.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
865
#elif defined(__linux__)
866
         sprintf (linker_pathname, "%s/../../host/linux/x86_64/usr/bin/%s-ld", QNX_TARGET, (strcmp (image_processor, "x86_64") == 0 ? "x86_64-pc-nto-qnx8.0.0" : "aarch64-unknown-nto-qnx8.0.0"));
867
#elif defined(__QNXNTO__)
868
         sprintf (linker_pathname, "%s/../../host/qnx8/x86_64/usr/bin/%s-ld", QNX_TARGET, (strcmp (image_processor, "x86_64") == 0 ? "x86_64-pc-nto-qnx8.0.0" : "aarch64-unknown-nto-qnx8.0.0"));
869
#else // wtf are you building this on?
870
#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.
871
#endif
16 pmbaty 872
         ASSERT (access (linker_pathname, 0) == 0, "host cross-linker for QNX8 \"%s\" not found", linker_pathname);
15 pmbaty 873
         sprintf (linker_sysroot_arg, "--sysroot=%s/%s/", QNX_TARGET, image_processor);
874
         sprintf (linker_script_pathname_arg, "-T%s/%s/lib/nto.link", QNX_TARGET, image_processor);
875
 
17 pmbaty 876
         resolved_pathname = resolve_pathname (procnto_name, entry_parms->search); // locate the procnto kernel location
16 pmbaty 877
         ASSERT (resolved_pathname, "QNX kernel \"%s\" not found in search path", procnto_name);
15 pmbaty 878
         strcpy (procnto_buildhost_pathname, resolved_pathname);
879
 
880
         sprintf (procnto_sym_filename, "%s.sym", procnto_name);
881
 
882
         const char *linker_argv[] = // construct the linker invokation argv
883
         {
884
            strrchr (linker_pathname, '/') + 1, // "${TARGET_TRIPLE}-ld"
885
            linker_sysroot_arg, // "--sysroot=${QNX_TARGET}/${TARGET_CPU}/"
886
            linker_script_pathname_arg, // "-T${QNX_TARGET}/${TARGET_CPU}/lib/nto.link"
887
            "--section-start",
888
            ".text=0xffff800000001000",
889
            "--no-relax",
890
            procnto_buildhost_pathname, // "${QNX_TARGET}/${TARGET_CPU}/boot/sys/procnto-smp-instr"
891
            "-o",
892
            procnto_sym_filename, // "procnto-smp-instr.sym"
893
            NULL
894
         };
895
         if (verbose_level > 2)
896
         {
897
            fprintf (stderr, "ifstool: calling:");
898
            for (table_index = 0; table_index < sizeof (linker_argv) / sizeof (linker_argv[0]) - 1; table_index++)
899
               fprintf (stderr, " '%s'", linker_argv[table_index]);
900
            fputc ('\n', stderr);
901
         }
902
         _spawnv (_P_WAIT, linker_pathname, linker_argv); // spawn the linker and produce a stripped procnto (wait for completion)
16 pmbaty 903
         if (!Buffer_ReadFromFile (&entry_parms->data, procnto_sym_filename)) // load the output file
15 pmbaty 904
            DIE_WITH_EXITCODE (1, "the host cross-linker failed to produce a readable stripped \"%s\" kernel: %s", procnto_sym_filename, strerror (errno));
905
         if (!entry_parms->should_keep_ld_output)
906
            unlink (procnto_sym_filename); // remove the linker output file if we want to
17 pmbaty 907
 
908
         // now strip this prelinked ELF kernel file
909
         Buffer_StripELFFile (&entry_parms->data, saved_ELF_sections, 1, stored_pathname); // strip the ELF file as per QNX docs (only keep ONE section, which is "QNX_info")
910
         entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF; // mark this inode as a preprocessed ELF file
911
 
15 pmbaty 912
#else // !PROCNTO_WIP
913
         /* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK */
914
         /* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK */
915
         /* HACK */
17 pmbaty 916
         /* HACK */ sprintf_s (candidate_pathname, MAXPATHLEN, "%s/procnto-smp-instr", (entry_parms->prefix != NULL ? entry_parms->prefix : "")); // HACK: fix the entry name
15 pmbaty 917
         /* HACK */ stored_pathname = candidate_pathname;
918
         /* HACK */ entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE; // procnto needs to have these flags stamped on the inode
919
         /* HACK */ entry_parms->st_mode = S_IFREG | 0700; // procnto requires 0700 permissions
920
         /* HACK */ image_kernel_ino = entry_parms->extra_ino_flags | (inode_count + 1);
921
         /* HACK */ free (entry_parms->data.bytes); // discard inline contents
16 pmbaty 922
         /* HACK */ Buffer_Initialize (&entry_parms->data);
923
         /* HACK */ if (!Buffer_ReadFromFile (&entry_parms->data, kernelfile_pathname)) // read kernel file as a precompiled binary blob
15 pmbaty 924
         /* HACK */ {
925
         /* HACK */    fprintf (stderr, "fatal error: unable to read precompiled kernel file \"%s\" specified in --kernelfile argument\n", kernelfile_pathname);
926
         /* HACK */    exit (1);
927
         /* HACK */ }
928
         /* HACK */
929
         /* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK */
930
         /* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK */
931
#endif // PROCNTO_WIP
7 pmbaty 932
      }
933
      else if (entry_parms->is_compiled_bootscript) // else is it a startup script ?
934
         image_bootscript_ino = inode_count + 1; // save boot script inode number for image header
2 pmbaty 935
 
7 pmbaty 936
      // do we already know the data for this data blob ?
11 pmbaty 937
      if (entry_parms->data.bytes != NULL)
7 pmbaty 938
      {
15 pmbaty 939
         entry_parms->mtime = entry_parms->mtime_for_inline_files; // if so, set it a mtime equal to the mtime to use for inline files
16 pmbaty 940
         LOG_INFO ("file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" blob (len %zd)", entry_parms->extra_ino_flags | (inode_count + 1), entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_parms->data.size);
7 pmbaty 941
      }
8 pmbaty 942
      else if (buildhost_pathname != NULL) // else was a source file pathname supplied ?
1 pmbaty 943
      {
17 pmbaty 944
         resolved_pathname = resolve_pathname (buildhost_pathname, entry_parms->search); // locate the file
7 pmbaty 945
         if (resolved_pathname == NULL)
15 pmbaty 946
            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));
16 pmbaty 947
         if (!Buffer_ReadFromFile (&entry_parms->data, resolved_pathname))
948
            DIE_WITH_EXITCODE (1, "filesystem entry \"%s\" specified in \"%s\" line %d can't be read: %s", buildhost_pathname, buildfile_pathname, lineno, strerror (errno));
949
         stat (resolved_pathname, &stat_buf); // can't fail, since we could read it
7 pmbaty 950
         if (entry_parms->mtime == UINT32_MAX)
951
            entry_parms->mtime = (uint32_t) stat_buf.st_mtime;
16 pmbaty 952
         LOG_INFO ("file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" buildhost_file \"%s\" (len %zd)", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, buildhost_pathname, entry_parms->data.size);
8 pmbaty 953
      }
1 pmbaty 954
 
9 pmbaty 955
      // is the file we're storing an ELF file ?
16 pmbaty 956
      #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
957
      if ((entry_parms->data.size > 52) // file is big enough to contain an ELF header
958
          && (memcmp (ELF_GET_STRING (ELFHDR, ELFHDR, magic), ELF_MAGIC_STR, 4) == 0)) // file starts with the ELF magic
7 pmbaty 959
      {
9 pmbaty 960
         // is the file we're storing a relocatable executable (i.e. a dynamic library) and should we check for its canonical name ?
16 pmbaty 961
         if ((ELF_GET_NUMERIC (ELFHDR, ELFHDR, type) == ELF_TYPE_DYNAMICLIB) && entry_parms->should_autosymlink_dylib)
7 pmbaty 962
         {
9 pmbaty 963
            // locate the sections we need (the dynamic section and its strings table)
16 pmbaty 964
            const elf_section_header_t *shdr_dynamic = elf_get_section_header_by_name (ELFHDR, ".dynamic");
965
            const elf_section_header_t *shdr_dynstr = elf_get_section_header_by_name (ELFHDR, ".dynstr");
7 pmbaty 966
 
9 pmbaty 967
            // make sure we have both the dynamic section header and its own strings table header
968
            if ((shdr_dynamic != NULL) && (shdr_dynstr != NULL))
969
            {
16 pmbaty 970
               dynamic_strings = (char *) &entry_parms->data.bytes[ELF_GET_NUMERIC (ELFHDR, shdr_dynstr, file_offset)]; // quick access to dynamic sections strings table
7 pmbaty 971
 
9 pmbaty 972
               // walk through the dynamic section, look for the DT_SONAME entry
16 pmbaty 973
               canonical_dylib_name = NULL; // assume none until told otherwise
974
               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)];
975
                    (ELF_GET_NUMERIC (ELFHDR, dynamic_entry, tag) != ELF_DT_NULL);
976
                    dynamic_entry = (elf_dynamic_section_entry_t *) ((uint8_t *) dynamic_entry + ELF_STRUCT_SIZE (ELFHDR, dynamic_entry)))
977
                  if (ELF_GET_NUMERIC (ELFHDR, dynamic_entry, tag) == ELF_DT_SONAME)
9 pmbaty 978
                  {
16 pmbaty 979
                     canonical_dylib_name = dynamic_strings + ELF_GET_NUMERIC (ELFHDR, dynamic_entry, value);
9 pmbaty 980
                     break;
981
                  }
982
 
983
               // do we have it ?
984
               if ((canonical_dylib_name != NULL) && (canonical_dylib_name[0] != 0))
7 pmbaty 985
               {
17 pmbaty 986
                  sprintf_s (candidate_pathname, MAXPATHLEN, "%s/%s", (entry_parms->prefix != NULL ? entry_parms->prefix : ""), canonical_dylib_name);
9 pmbaty 987
                  if (strcmp (candidate_pathname, stored_pathname) != 0) // claimed dylib name differs from passed name ?
988
                  {
989
                     original_stored_pathname = stored_pathname; // if so, remember to create a symlink here
990
                     stored_pathname = candidate_pathname;
991
                  }
7 pmbaty 992
               }
1 pmbaty 993
            }
9 pmbaty 994
         } // end if the file we're storing is a dylib
10 pmbaty 995
 
996
         // now strip this ELF file if necessary
997
         if (!(entry_parms->extra_ino_flags & IFS_INO_PROCESSED_ELF))
998
         {
17 pmbaty 999
            Buffer_StripELFFile (&entry_parms->data, saved_ELF_sections, saved_ELF_section_count, stored_pathname); // strip the ELF file à la mkifs
10 pmbaty 1000
            entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF; // mark this inode as a preprocessed ELF file
1001
         } // end if the file is not yet a processed ELF
9 pmbaty 1002
      } // end if the file we're storing is an ELF file
16 pmbaty 1003
      #undef ELFHDR // undefine the macro that used to always point to the ELF header at the beginning of the file
1 pmbaty 1004
   }
1005
   else if (S_ISLNK (entry_parms->st_mode)) // else are we storing a symbolic link ?
15 pmbaty 1006
      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);
1 pmbaty 1007
   else // we must be storing a FIFO
1008
   {
11 pmbaty 1009
      if (strchr (entry_parms->data.bytes, ':') == NULL)
15 pmbaty 1010
         DIE_WITH_EXITCODE (1, "device entry \"%s\" malformed (no 'dev:rdev' pair)", stored_pathname);
1011
      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);
1 pmbaty 1012
   }
1013
 
7 pmbaty 1014
   // grow filesystem entries array to hold one more slot
1 pmbaty 1015
   reallocated_ptr = realloc (*fsentries, (*fsentry_count + 1) * sizeof (fsentry_t)); // attempt to reallocate
16 pmbaty 1016
   ASSERT_WITH_ERRNO (reallocated_ptr);
1 pmbaty 1017
   *fsentries = reallocated_ptr; // save reallocated pointer
1018
   fsentry = &(*fsentries)[*fsentry_count]; // quick access to fs entry slot
1019
   fsentry->header.extattr_offset = 0;
10 pmbaty 1020
   fsentry->header.ino = entry_parms->extra_ino_flags | (++inode_count);
1 pmbaty 1021
   fsentry->header.mode = entry_parms->st_mode;
1022
   fsentry->header.gid = entry_parms->gid;
1023
   fsentry->header.uid = entry_parms->uid;
7 pmbaty 1024
   fsentry->header.mtime = (entry_parms->mtime == UINT32_MAX ? (uint32_t) time (NULL) : entry_parms->mtime);
1 pmbaty 1025
   if (S_ISDIR (entry_parms->st_mode))
1026
   {
7 pmbaty 1027
      fsentry->u.dir.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
16 pmbaty 1028
 
7 pmbaty 1029
      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 1030
      fsentry->UNSAVED_was_data_written = true; // no data to save
1031
   }
1032
   else if (S_ISREG (entry_parms->st_mode))
1033
   {
1034
      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 1035
      fsentry->u.file.size = (uint32_t) entry_parms->data.size;
7 pmbaty 1036
      fsentry->u.file.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
16 pmbaty 1037
      fsentry->u.file.UNSAVED_databuf = malloc (entry_parms->data.size);
1038
      ASSERT_WITH_ERRNO (fsentry->u.file.UNSAVED_databuf);
1039
      memcpy (fsentry->u.file.UNSAVED_databuf, entry_parms->data.bytes, entry_parms->data.size);
1040
 
7 pmbaty 1041
      fsentry->header.size = (uint16_t) ROUND_TO_UPPER_MULTIPLE (sizeof (fsentry->header) + sizeof (uint32_t) + sizeof (uint32_t) + strlen (fsentry->u.file.path) + 1, image_align); // now we can set the size
1 pmbaty 1042
      fsentry->UNSAVED_was_data_written = false; // there *IS* data to save
1043
   }
1044
   else if (S_ISLNK (entry_parms->st_mode))
1045
   {
7 pmbaty 1046
      fsentry->u.symlink.sym_offset = (uint16_t) (strlen (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname) + 1);
16 pmbaty 1047
      fsentry->u.symlink.sym_size = (uint16_t) entry_parms->data.size;
7 pmbaty 1048
      fsentry->u.symlink.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
11 pmbaty 1049
      fsentry->u.symlink.contents = strdup (entry_parms->data.bytes);
16 pmbaty 1050
      ASSERT_WITH_ERRNO (fsentry->u.symlink.contents);
1051
 
7 pmbaty 1052
      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 1053
      fsentry->UNSAVED_was_data_written = true; // no data to save
1054
   }
7 pmbaty 1055
   else // necessarily a device node
1 pmbaty 1056
   {
11 pmbaty 1057
      fsentry->u.device.dev  = strtol (entry_parms->data.bytes, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
1058
      fsentry->u.device.rdev = strtol (strchr (entry_parms->data.bytes, ':') + 1, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
7 pmbaty 1059
      fsentry->u.device.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
16 pmbaty 1060
 
7 pmbaty 1061
      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 1062
      fsentry->UNSAVED_was_data_written = true; // no data to save
1063
   }
1064
   (*fsentry_count)++;
7 pmbaty 1065
 
1066
   // should we also add a symlink to this entry ? (in case we stored a dylib file under its canonical name)
1067
   if (original_stored_pathname != NULL)
1068
   {
1069
      entry_parms->is_compiled_bootscript = false;
1070
      entry_parms->should_autosymlink_dylib = false;
1071
      entry_parms->should_follow_symlinks = false;
1072
      entry_parms->st_mode = S_IFLNK | 0777; // NOTE: mkifs stores symlink permissions as rwxrwxrwx !
10 pmbaty 1073
      entry_parms->extra_ino_flags = (fsentry->header.ino & (IFS_INO_PROCESSED_ELF | IFS_INO_RUNONCE_ELF | IFS_INO_BOOTSTRAP_EXE)); // preserve target's inode flags
7 pmbaty 1074
      last_dirsep = strrchr (stored_pathname, '/');
11 pmbaty 1075
      old_data = entry_parms->data.bytes; // backup previous data pointer
1076
      entry_parms->data.bytes = (uint8_t *) (last_dirsep == NULL ? stored_pathname : last_dirsep + 1); // store symlink target in dirent data
16 pmbaty 1077
      entry_parms->data.size = strlen (entry_parms->data.bytes);
7 pmbaty 1078
      add_fsentry (fsentries, fsentry_count, entry_parms, original_stored_pathname, NULL);
11 pmbaty 1079
      entry_parms->data.bytes = old_data; // restore previous data pointer so that it can be freed normally
7 pmbaty 1080
   }
1081
 
1 pmbaty 1082
   return (*fsentry_count);
1083
}
1084
 
1085
 
1086
static int fsentry_compare_pathnames_cb (const void *a, const void *b)
1087
{
1088
   // qsort() callback that compares two imagefs filesystem entries and sort them alphabetically by pathname
1089
 
3 pmbaty 1090
   const fsentry_t *entry_a = (const fsentry_t *) a;
1091
   const fsentry_t *entry_b = (const fsentry_t *) b;
1 pmbaty 1092
   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)));
1093
   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)));
1094
   return (strcmp (pathname_a, pathname_b));
1095
}
1096
 
1097
 
17 pmbaty 1098
static void parse_line (FILE *buildfile_fp, char *line_buffer, fsentry_t **fsentries, size_t *fsentry_count, parms_t *default_parms)
1 pmbaty 1099
{
17 pmbaty 1100
   thread_local static char path_on_buildhost[MAXPATHLEN] = "";
1101
   thread_local static char path_in_ifs[MAXPATHLEN] = "";
1102
   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 1103
 
17 pmbaty 1104
   bool should_discard_inline_contents;
1105
   bool is_quoted_context;
1106
   bool is_escaped_char;
1107
   struct stat stat_buf;
1108
   struct tm utc_time;
1109
   void *reallocated_ptr;
1110
   size_t allocated_size;
1111
   size_t string_len;
1112
   char *specifiedpathname_start;
1113
   char *attrblock_start;
1114
   char *write_ptr;
1115
   char *line_ptr;
1116
   char *value;
7 pmbaty 1117
   char *token;
17 pmbaty 1118
   char *sep;
1119
   char *ctx;
1120
   int read_char;
1121
 
1122
   line_ptr = line_buffer;
1123
   while ((*line_ptr != 0) && isspace (*line_ptr))
1124
      line_ptr++; // skip leading spaces
1125
 
1126
   if ((*line_ptr == 0) || (*line_ptr == '#'))
1127
      return; // skip empty or comment lines
1128
 
1129
   string_len = (int) strlen (line_buffer);
1130
   if ((string_len > 0) && (line_buffer[string_len - 1] == '\n'))
1131
      line_buffer[string_len - 1] = 0; // chop off newline for easier debug output
1132
 
1133
   // reset entry values
1134
   memcpy (&entry_parms, default_parms, sizeof (parms_t));
1135
   path_in_ifs[0] = 0;
1136
   path_on_buildhost[0] = 0;
1137
   should_discard_inline_contents = false;
1138
 
1139
   // does this line start with an attribute block ?
1140
   if (*line_ptr == '[')
7 pmbaty 1141
   {
17 pmbaty 1142
      line_ptr++; // skip the leading square bracket
1143
      attrblock_start = line_ptr; // remember where it starts
1144
      is_quoted_context = false;
1145
      while ((*line_ptr != 0) && !((*line_ptr == ']') && (line_ptr[-1] != '\\') && !is_quoted_context))
1146
      {
1147
         if (*line_ptr == '"')
1148
            is_quoted_context ^= true; // remember when we're between quotes
1149
         else if (!is_quoted_context && (*line_ptr == ' '))
1150
            *line_ptr = RECORD_SEP[0]; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
1151
         line_ptr++; // reach the next unescaped closing square bracket
1152
      }
1153
      if (*line_ptr != ']')
1154
      {
1155
         LOG ("warning", 0, "syntax error in \"%s\" line %d: unterminated attributes block (skipping)", buildfile_pathname, lineno);
1156
         return; // invalid attribute block, skip line
1157
      }
1158
      *line_ptr = 0; // end the attribute block so that it is a parsable C string
7 pmbaty 1159
 
17 pmbaty 1160
      // now parse the attribute tokens
1161
      // DOCUMENTATION: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/m/mkifs.html#mkifs__description
1162
      token = strtok_r (attrblock_start, RECORD_SEP, &ctx);
1163
      while (token != NULL)
1164
      {
1165
         // evaluate attribute token
1166
         #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
1167
         if (false) {}
1168
         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.)
1169
         else if (strncmp (token, "uid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.uid     = (int) read_integer (value); }
1170
         else if (strncmp (token, "gid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.gid     = (int) read_integer (value); }
1171
         else if (strncmp (token, "dperms=",  7) == 0) { REACH_TOKEN_VALUE (); entry_parms.dperms  = (int) read_integer (value); }
1172
         else if (strncmp (token, "perms=",   6) == 0) { REACH_TOKEN_VALUE (); entry_parms.perms   = (int) read_integer (value); }
1173
         else if (strncmp (token, "type=",    5) == 0) { REACH_TOKEN_VALUE ();
1174
            if      (strcmp (value, "dir")  == 0) entry_parms.st_mode = S_IFDIR;
1175
            else if (strcmp (value, "file") == 0) entry_parms.st_mode = S_IFREG;
1176
            else if (strcmp (value, "link") == 0) entry_parms.st_mode = S_IFLNK;
1177
            else if (strcmp (value, "fifo") == 0) entry_parms.st_mode = S_IFIFO;
1178
            else DIE_WITH_EXITCODE (1, "invalid 'type' attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, value);
1179
         }
1180
         else if (strncmp (token, "image=",   6) == 0) { REACH_TOKEN_VALUE ();
1181
            image_base = (uint32_t) read_integer (value); // read image base address
1182
            if ((sep = strchr (value, '-')) != NULL) image_end       = (uint32_t) read_integer (sep + 1); // if we have a dash, read optional image end (TODO: check this value and produce an error in the relevant case. Not important.)
1183
            if ((sep = strchr (value, ',')) != NULL) image_maxsize   = (uint32_t) read_integer (sep + 1); // if we have a comma, read optional image max size
1184
            if ((sep = strchr (value, '=')) != NULL) image_totalsize = (uint32_t) read_integer (sep + 1); // if we have an equal sign, read optional image padding size
1185
            if ((sep = strchr (value, '%')) != NULL) image_align     = (uint32_t) read_integer (sep + 1); // if we have a modulo sign, read optional image aligmnent
1186
            LOG_INFO ("image 0x%x-0x%x maxsize %d totalsize %d align %d", image_base, image_end, image_maxsize, image_totalsize, image_align);
1187
         }
1188
         else if (strncmp (token, "virtual=", 8) == 0) { REACH_TOKEN_VALUE ();
1189
            if ((bootfile_pathname == NULL) || (startupfile_pathname == NULL) || (kernelfile_pathname == NULL)) // HACK until I figure out how to re-create them
1190
               DIE_WITH_EXITCODE (1, "creating bootable images require the --bootfile, --startupfile and --kernelfile command-line options in \"%s\" line %d", buildfile_pathname, lineno);
1191
            if ((sep = strchr (value, ',')) != NULL) // do we have a comma separating (optional) processor and boot file name ?
1192
            {
1193
               *sep = 0;
1194
               strcpy_s (image_processor, sizeof (image_processor), value); // save processor
1195
               value = sep + 1;
1196
            }
1197
            //sprintf (image_bootfile, "%s/%s/boot/sys/%s.boot", QNX_TARGET, image_processor, value); // save preboot file name (TODO: we should search in MKIFS_PATH instead of this. Not important.)
1198
            //strcpy (image_bootfile, bootfile_pathname); // FIXME: HACK
1199
            if (stat (bootfile_pathname, &stat_buf) != 0)
1200
               DIE_WITH_EXITCODE (1, "unable to stat the boot file \"%s\" specified in \"%s\" line %d: %s", bootfile_pathname, buildfile_pathname, lineno, strerror (errno));
1201
            bootfile_size = stat_buf.st_size; // save preboot file size
1202
            LOG_INFO ("processor \"%s\" bootfile \"%s\"\n", image_processor, bootfile_pathname);
1203
#if 1
1204
            // ######################################################################################################################################################################################################################################
1205
            // # FIXME: figure out how to re-create it: linker call involved
1206
            // # $ x86_64-pc-nto-qnx8.0.0-ld --sysroot=${QNX_TARGET}/x86_64/ -T${QNX_TARGET}/x86_64/lib/nto.link --section-start .text=0xffff800000001000 --no-relax ${QNX_TARGET}/x86_64/boot/sys/procnto-smp-instr -o procnto-smp-instr.sym.UNSTRIPPED
1207
            // ######################################################################################################################################################################################################################################
1208
//               if (!Buffer_ReadFromFile (&entry_parms.data, kernelfile_pathname))
1209
//                  DIE_WITH_EXITCODE (1, "unable to read precompiled kernel file \"%s\" specified in --kernelfile argument: %s", kernelfile_pathname, strerror (errno));
1210
#else // nonworking
1211
            strcpy (path_on_buildhost, "procnto-smp-instr");
1212
#endif // nonworking
1213
         }
1214
         else if (strncmp (token, "mtime=", 6) == 0) { REACH_TOKEN_VALUE (); if (strcmp (value, "*") == 0) entry_parms.mtime = UINT32_MAX; else {
1215
               // value *must* be "YYYY-MM-DD-HH:MM:SS" by specification
1216
               memset (&utc_time, 0, sizeof (utc_time));
1217
               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)
1218
               {
1219
                  LOG_WARNING ("syntax error in \"%s\" line %d: mtime specification not in YYYY-MM-DD-HH:MM:SS format (skipping)", buildfile_pathname, lineno);
1220
                  continue; // invalid attribute block, skip line
1221
               }
1222
               utc_time.tm_mon--; // convert month from [1-12] to [0-11]
1223
               entry_parms.mtime = (uint32_t) mktime (&utc_time);
1224
            }
1225
         }
1226
         else if (strcmp (token, "+script")     == 0) {
1227
            entry_parms.is_compiled_bootscript = true;
1228
            ASSERT_WITH_ERRNO (Buffer_InitWithByteArray (&entry_parms.data, INITIAL_STARTUP_SCRIPT)); // FIXME: HACK until the script compiler is implemented
1229
            should_discard_inline_contents = true; // remember we already have data (so as to discard the inline block's contents)
1230
         }
1231
         else if (strcmp (token, "-script")     == 0) entry_parms.is_compiled_bootscript = false;
1232
         else if (strcmp (token, "+followlink") == 0) entry_parms.should_follow_symlinks = true;
1233
         else if (strcmp (token, "-followlink") == 0) entry_parms.should_follow_symlinks = false;
1234
         else if (strcmp (token, "+autolink")   == 0) entry_parms.should_autosymlink_dylib = true;
1235
         else if (strcmp (token, "-autolink")   == 0) entry_parms.should_autosymlink_dylib = false;
1236
         else if (strcmp (token, "+keeplinked") == 0) entry_parms.should_keep_ld_output = true;
1237
         else if (strcmp (token, "-keeplinked") == 0) entry_parms.should_keep_ld_output = false;
1238
         else LOG_WARNING ("unimplemented attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, token);
1239
         #undef REACH_TOKEN_VALUE
7 pmbaty 1240
 
17 pmbaty 1241
         token = strtok_r (NULL, RECORD_SEP, &ctx); // proceed to next attribute token
1242
      }
1243
 
1244
      line_ptr++; // reach the next character
1245
      while ((*line_ptr != 0) && isspace (*line_ptr))
1246
         line_ptr++; // skip leading spaces
1247
 
1248
      // are we at the end of the line ? if so, it means the attribute values that are set should become the default
1249
      if ((*line_ptr == 0) || (*line_ptr == '#'))
1250
      {
1251
         #define APPLY_DEFAULT_ATTR_NUM(attr,descr,fmt) do { if (entry_parms.attr != default_parms->attr) { \
1252
               LOG_INFO ("changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %d", default_parms->attr, entry_parms.attr, buildfile_pathname, lineno); \
1253
               default_parms->attr = entry_parms.attr; \
1254
            } } while (0)
1255
         #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))) { \
1256
            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); \
1257
               default_parms->attr = entry_parms.attr; \
1258
            } } while (0)
1259
         //APPLY_DEFAULT_ATTR_STR (new_cwd,                  "current working directory",       "\"%s\"");
1260
         APPLY_DEFAULT_ATTR_STR (search,                   "search path list",                "\"%s\"");
1261
         APPLY_DEFAULT_ATTR_STR (prefix,                   "prefix",                          "\"%s\"");
1262
         APPLY_DEFAULT_ATTR_NUM (dperms,                   "directory permissions",           "0%o");
1263
         APPLY_DEFAULT_ATTR_NUM (perms,                    "file permissions",                "0%o");
1264
         APPLY_DEFAULT_ATTR_NUM (uid,                      "owner ID",                        "%d");
1265
         APPLY_DEFAULT_ATTR_NUM (gid,                      "group ID",                        "%d");
1266
         APPLY_DEFAULT_ATTR_NUM (st_mode,                  "inode type",                      "0%o");
1267
         APPLY_DEFAULT_ATTR_NUM (is_compiled_bootscript,   "compiled script state",           "%d");
1268
         APPLY_DEFAULT_ATTR_NUM (should_follow_symlinks,   "symlink resolution",              "%d");
1269
         APPLY_DEFAULT_ATTR_NUM (should_autosymlink_dylib, "dylib canonical name symlinking", "%d");
1270
         APPLY_DEFAULT_ATTR_NUM (should_keep_ld_output,    "linker output preservation",      "%d");
1271
         #undef APPLY_DEFAULT_ATTR_STR
1272
         #undef APPLY_DEFAULT_ATTR_NUM
1273
         return; // end of line reached, proceed to the next line
1274
      }
1275
      // end of attributes parsing
1276
   } // end of "this line starts with an attributes block"
1277
 
1278
   // there's data in this line. We expect a filename in the IFS. Read it and unescape escaped characters
1279
   string_len = sprintf_s (path_in_ifs, sizeof (path_in_ifs), "%s", (entry_parms.prefix != NULL ? entry_parms.prefix : ""));
1280
   while ((string_len > 0) && (path_in_ifs[string_len - 1] == '/'))
1281
      string_len--; // chop off any trailing slashes from prefix
1282
   write_ptr = &path_in_ifs[string_len];
1283
   *write_ptr++ = '/'; // add ONE trailing slash
1284
   specifiedpathname_start = write_ptr; // remember the specified pathname will start here
1285
   is_quoted_context = (*line_ptr == '"');
1286
   if (is_quoted_context)
1287
      line_ptr++; // skip a possible initial quote
1288
   if (*line_ptr == '/')
1289
   {
1290
      LOG_WARNING ("paths in the IFS file should not begin with a leading '/' in \"%s\" line %d", buildfile_pathname, lineno);
1291
      line_ptr++; // consistency check: paths in the IFS should not begin with a '/'
7 pmbaty 1292
   }
17 pmbaty 1293
   while ((*line_ptr != 0) && ((!is_quoted_context && (*line_ptr != '=') && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr == '"'))))
1294
   {
1295
      if (*line_ptr == '\\')
1296
      {
1297
         line_ptr++;
1298
         *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
1299
      }
1300
      else
1301
         *write_ptr++ = *line_ptr;
1302
      line_ptr++;
1303
   }
1304
   *write_ptr = 0; // terminate the string
1305
   if (is_quoted_context && (*line_ptr == '"'))
1306
      line_ptr++; // skip a possible final quote
7 pmbaty 1307
 
17 pmbaty 1308
   // we reached a space OR an equal sign
1309
   while ((*line_ptr != 0) && isspace (*line_ptr))
1310
      line_ptr++; // skip optional spaces after the filename in the IFS
1311
 
1312
   // do we have an equal sign ?
1313
   if (*line_ptr == '=') // we must be creating either a directory or a file, do we have an equal sign ?
1314
   {
1315
      line_ptr++; // skip the equal sign
1316
      while ((*line_ptr != 0) && isspace (*line_ptr))
1317
         line_ptr++; // skip optional spaces after the equal sign
1318
 
1319
      if (*line_ptr == 0)
1320
      {
1321
         LOG_WARNING ("syntax error in \"%s\" line %d: missing data specification after equal sign (skipping)", buildfile_pathname, lineno);
1322
         return; // invalid symlink specification, skip line
1323
      }
1324
 
1325
      // read the host system's path, it may be either a path or a contents definition. Is it a content definition ?
1326
      if (*line_ptr == '{')
1327
      {
1328
         allocated_size = 0;
1329
 
1330
         line_ptr++; // skip the leading content definition
1331
         is_escaped_char = false;
1332
         for (;;)
1333
         {
1334
            read_char = fgetc (buildfile_fp);
1335
            if (read_char == EOF)
1336
               DIE_WITH_EXITCODE (1, "syntax error in \"%s\" line %d: unterminated contents block (end of file reached)", buildfile_pathname, lineno); // invalid contents block
1337
            else if ((read_char == '\\') && !is_escaped_char)
1338
               is_escaped_char = true; // remember the next char is escaped
1339
            else if ((read_char == '}') && !is_escaped_char)
1340
               break; // found an unescaped closing bracked, stop parsing
1341
            else
1342
            {
1343
               is_escaped_char = false; // any other char, meaning the next one will not be escaped
1344
               if (!should_discard_inline_contents) // only store the contents if we do NOT know the data yet
1345
               {
1346
                  if (entry_parms.data.size == allocated_size) // reallocate in 4 kb blocks
1347
                  {
1348
                     reallocated_ptr = realloc (entry_parms.data.bytes, allocated_size + 4096);
1349
                     ASSERT_WITH_ERRNO (reallocated_ptr);
1350
                     entry_parms.data.bytes = reallocated_ptr;
1351
                     allocated_size += 4096;
1352
                  }
1353
                  entry_parms.data.bytes[entry_parms.data.size++] = read_char;
1354
               }
1355
               if (read_char == '\n')
1356
                  lineno++; // update line counter as we parse the inline content
1357
            }
1358
         } // end for
1359
      }
1360
      else // not a content definition between { brackets }, must be either a pathname on the build host, or the target of a symlink
1361
      {
1362
         is_quoted_context = (*line_ptr == '"');
1363
         if (is_quoted_context)
1364
            line_ptr++; // skip a possible initial quote
1365
         specifiedpathname_start = line_ptr; // remember where the specified pathname starts
1366
         write_ptr = line_ptr; // now unescape all characters
1367
         while ((*line_ptr != 0) && ((!is_quoted_context && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr == '"'))))
1368
         {
1369
            if (*line_ptr == '\\')
1370
            {
1371
               line_ptr++;
1372
               *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
1373
            }
1374
            else
1375
               *write_ptr++ = *line_ptr;
1376
            line_ptr++;
1377
         }
1378
         *write_ptr = 0; // terminate the string
1379
         if (is_quoted_context && (*line_ptr == '"'))
1380
            line_ptr++; // skip a possible final quote
1381
 
1382
         if (S_ISLNK (entry_parms.st_mode)) // are we storing a symlink ?
1383
            ASSERT_WITH_ERRNO (Buffer_InitWithCString (&entry_parms.data, specifiedpathname_start)); // if so, store the symlink target as the dirent's blob data
1384
         else // it's a build host filesystem path
1385
            strcpy_s (path_on_buildhost, sizeof (path_on_buildhost), specifiedpathname_start); // the path on the build host is given after the equal sign
1386
      }
1387
   }
1388
   else // no equal sign, meaning the file will have the same name on the build host filesystem
1389
   {
1390
      // consistency check: symlinks MUST have an equal sign
1391
      if (entry_parms.st_mode == S_IFLNK)
1392
      {
1393
         LOG_WARNING ("syntax error in \"%s\" line %d: missing equal sign and symlink target (skipping)", buildfile_pathname, lineno);
1394
         return; // invalid symlink specification, skip line
1395
      }
1396
 
1397
      strcpy_s (path_on_buildhost, sizeof (path_on_buildhost), specifiedpathname_start); // the path on the build host is the one specified
1398
      sep = strrchr (specifiedpathname_start, '/');
1399
      if (sep != NULL)
1400
         memmove (specifiedpathname_start, sep + 1, strlen (sep + 1) + 1); // the path in the IFS will be the BASENAME of the path specified (after the prefix)
1401
   }
1402
 
1403
   // now add this entry to the image filesystem
1404
   if (S_ISDIR (entry_parms.st_mode))
1405
      entry_parms.st_mode |= entry_parms.dperms;
1406
   else if (S_ISLNK (entry_parms.st_mode))
1407
      entry_parms.st_mode |= 0777; // NOTE: mkifs sets symlink permissions to rwxrwxrwx !?
1408
   else // file or device node
1409
      entry_parms.st_mode |= entry_parms.perms;
1410
 
1411
   add_fsentry (fsentries, fsentry_count, &entry_parms, path_in_ifs, path_on_buildhost); // and add filesystem entry
1412
 
1413
   if (entry_parms.data.bytes != NULL)
1414
      free (entry_parms.data.bytes); // if blob data was allocated, free it
1415
 
1416
   return; //  finished parsing that line
1 pmbaty 1417
}
1418
 
1419
 
1420
int main (int argc, char **argv)
1421
{
1422
   // program entrypoint
1423
 
16 pmbaty 1424
   typedef struct ifs_offsets_s
1425
   {
1426
      size_t startupheader;
1427
      size_t startuptrailer;
1428
      size_t imageheader;
1429
      size_t imagedir;
1430
      size_t imagetrailer;
1431
   } ifs_offsets_t;
1432
   typedef struct ifs_s
1433
   {
1434
      buffer_t data;
1435
      ifs_offsets_t offsets;
1436
      size_t final_size; // final size: not known (because not set) until everything has been written
1437
   } ifs_t;
1 pmbaty 1438
 
2 pmbaty 1439
   static startup_header_t startup_header = { 0 }; // output IFS's startup header
1440
   static startup_trailer_v2_t startup_trailer = { 0 }; // output IFS's startup trailer (version 2, with SHA-512 checksum and int32 checksum)
1441
   static image_header_t image_header = { 0 }; // output IFS's imagefs header
1442
   static image_trailer_v2_t image_trailer = { 0 }; // output IFS's imagefs trailer (version 2, with SHA-512 checksum and int32 checksum)
1443
   static fsentry_t *fsentries = NULL; // output IFS's filesystem entries
1444
   static size_t fsentry_count = 0; // number of entries in the IFS filesystem
7 pmbaty 1445
   static parms_t default_parms = { // default parameters for a filesystem entry
1446
      .dperms = 0755,
1447
      .perms = 0644,
1448
      .uid = 0,
1449
      .gid = 0,
1450
      .st_mode = S_IFREG,
1451
      .mtime = UINT32_MAX,
1452
      .mtime_for_inline_files = UINT32_MAX,
1453
      .prefix = "/proc/boot",
1454
      .should_follow_symlinks = true, // [+|-followlink]
1455
      .should_autosymlink_dylib = true, // [+|-autolink]
1456
      .is_compiled_bootscript = false, // [+|-script]
10 pmbaty 1457
      .extra_ino_flags = 0,
17 pmbaty 1458
      .search = NULL,
11 pmbaty 1459
      .data = { NULL, 0 }
7 pmbaty 1460
   };
2 pmbaty 1461
   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 1462
 
7 pmbaty 1463
   char path_on_buildhost[MAXPATHLEN] = "";
1464
   char path_in_ifs[MAXPATHLEN] = "";
1 pmbaty 1465
   char *ifs_pathname = NULL;
1466
   void *reallocated_ptr;
17 pmbaty 1467
   size_t reallocated_size;
7 pmbaty 1468
   size_t available_space;
1 pmbaty 1469
   size_t fsentry_index;
7 pmbaty 1470
   size_t largest_index;
1471
   size_t largest_size;
17 pmbaty 1472
   size_t imgdir_size;
1 pmbaty 1473
   size_t curr_offset;
16 pmbaty 1474
   ifs_t ifs = { 0 };
8 pmbaty 1475
   int32_t checksum;
16 pmbaty 1476
   char *first_pathname = NULL;
1477
   char *second_pathname = NULL;
1 pmbaty 1478
   char *sep;
1479
   int arg_index;
1480
   bool is_quoted_context = false;
1481
   bool is_escaped_char = false;
11 pmbaty 1482
   bool should_discard_inline_contents = false;
1 pmbaty 1483
   bool want_info = false;
10 pmbaty 1484
   bool want_everything = false;
1 pmbaty 1485
   bool want_help = false;
14 pmbaty 1486
   bool want_dump = false;
16 pmbaty 1487
   bool want_strip = false;
15 pmbaty 1488
   bool want_hexdump = false;
5 pmbaty 1489
   bool is_foreign_endianness;
1 pmbaty 1490
   FILE *buildfile_fp;
1491
 
17 pmbaty 1492
   // initialize stuff
1493
   saved_ELF_sections = (char **) malloc (4 * sizeof (char *));
1494
   ASSERT_WITH_ERRNO (saved_ELF_sections);
1495
   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
1496
   saved_ELF_sections[1] = ".gnu_debuglink";
1497
   saved_ELF_sections[2] = "QNX_usage";
1498
   saved_ELF_sections[3] = ".note.gnu.build-id"; // undocumented by QNX, but nonetheless preserved
1499
   saved_ELF_section_count = 4;
1500
 
1 pmbaty 1501
   // parse arguments
1502
   for (arg_index = 1; arg_index < argc; arg_index++)
1503
   {
1504
      if ((strcmp (argv[arg_index], "--bootfile") == 0) && (arg_index + 1 < argc)) // --bootfile path/to/blob.bin
1505
         bootfile_pathname = argv[++arg_index];
1506
      else if ((strcmp (argv[arg_index], "--startupfile") == 0) && (arg_index + 1 < argc)) // --startupfile path/to/blob.bin@0x1030
1507
      {
1508
         sep = strchr (argv[++arg_index], '@');
1509
         if ((sep == NULL) || (sep[1] == 0))
15 pmbaty 1510
            DIE_WITH_EXITCODE (1, "the --startupfile arguments expects <pathname>@<entrypoint_from_image_base>");
1 pmbaty 1511
         *sep = 0;
1512
         startupfile_pathname = argv[arg_index];
1513
         startupfile_ep_from_imagebase = (size_t) read_integer (sep + 1);
1514
      }
1515
      else if ((strcmp (argv[arg_index], "--kernelfile") == 0) && (arg_index + 1 < argc)) // --kernelfile path/to/blob.bin@0x32000
1516
      {
1517
         sep = strchr (argv[++arg_index], '@');
1518
         if ((sep == NULL) || (sep[1] == 0))
15 pmbaty 1519
            DIE_WITH_EXITCODE (1, "the --kernelfile arguments expects <pathname>@<fileoffset>");
1 pmbaty 1520
         *sep = 0;
1521
         kernelfile_pathname = argv[arg_index];
1522
         kernelfile_offset = (size_t) read_integer (sep + 1);
1523
      }
7 pmbaty 1524
      else if (strcmp (argv[arg_index], "-n") == 0)
1525
         default_parms.mtime_for_inline_files = 0; // inline files should have a mtime set to zero
1526
      else if (strcmp (argv[arg_index], "-nn") == 0)
1527
      {
11 pmbaty 1528
         default_parms.mtime = 0; // *all* files should have a mtime set to zero
7 pmbaty 1529
         default_parms.mtime_for_inline_files = 0;
1530
      }
14 pmbaty 1531
      else if ((strcmp (argv[arg_index], "--outdir") == 0) && (arg_index + 1 < argc)) // --outdir path
16 pmbaty 1532
         second_pathname = argv[++arg_index];
1533
      else if ((strcmp (argv[arg_index], "--outfile") == 0) && (arg_index + 1 < argc)) // --outfile pathname
1534
         second_pathname = argv[++arg_index];
1 pmbaty 1535
      else if (strcmp (argv[arg_index], "--info") == 0)
1536
         want_info = true;
14 pmbaty 1537
      else if (strcmp (argv[arg_index], "--dump") == 0)
1538
         want_dump = true;
15 pmbaty 1539
      else if (strcmp (argv[arg_index], "--hexdump") == 0) // voluntarily undocumented
1540
         want_hexdump = true;
16 pmbaty 1541
      else if (strcmp (argv[arg_index], "--strip") == 0)
1542
         want_strip = true;
10 pmbaty 1543
      else if (strcmp (argv[arg_index], "--everything") == 0)
1544
         want_everything = true;
15 pmbaty 1545
      else if (strncmp (argv[arg_index], "-v", 2) == 0) // -v[....]
1546
         verbose_level += (int) strlen (argv[arg_index] + 1); // increase verbosity by the number of characters in this flag
17 pmbaty 1547
      else if ((strcmp (argv[arg_index], "-l") == 0) && (arg_index + 1 < argc))
1548
         arg_index++; // these args will be parsed once the build file is open
1549
      else if ((strcmp (argv[arg_index], "-r") == 0) && (arg_index + 1 < argc))
1550
      {
1551
         reallocated_size = (SEARCH_PATH != NULL ? strlen (SEARCH_PATH) + 1 : 0) + strlen (argv[arg_index + 1]) + 1;
1552
         reallocated_ptr = realloc (SEARCH_PATH, reallocated_size); // grow search prefixes array
1553
         ASSERT_WITH_ERRNO (reallocated_ptr);
1554
         if (SEARCH_PATH != NULL)
1555
            strcat_s (reallocated_ptr, reallocated_size, PATH_SEP);
1556
         strcat_s (reallocated_ptr, reallocated_size, argv[++arg_index]); // stack up another search prefix
1557
         SEARCH_PATH = reallocated_ptr;
1558
      }
1559
      else if ((strcmp (argv[arg_index], "-s") == 0) && (arg_index + 1 < argc))
1560
      {
1561
         reallocated_ptr = realloc (saved_ELF_sections, (saved_ELF_section_count + 1) * sizeof (char *)); // grow ELF sections array
1562
         ASSERT_WITH_ERRNO (reallocated_ptr);
1563
         saved_ELF_sections = reallocated_ptr;
1564
         saved_ELF_sections[saved_ELF_section_count++] = argv[++arg_index]; // stack up another ELF section name to preserve
1565
      }
1 pmbaty 1566
      else if ((strcmp (argv[arg_index], "-?") == 0) || (strcmp (argv[arg_index], "--help") == 0))
1567
         want_help = true;
16 pmbaty 1568
      else if (first_pathname == NULL)
1569
         first_pathname = argv[arg_index];
1570
      else if (second_pathname == NULL)
1571
         second_pathname = argv[arg_index];
1572
      else
1573
         DIE_WITH_EXITCODE (1, "unrecognized option: '%s'", argv[arg_index]);
1 pmbaty 1574
   }
1575
 
1576
   // do we not have enough information to run ?
16 pmbaty 1577
   if (want_help || (first_pathname == NULL) || (!want_info && !want_dump && !want_hexdump && !want_strip && (second_pathname == NULL)))
1 pmbaty 1578
   {
16 pmbaty 1579
      FILE *out = (want_help ? stdout : stderr); // select the right output channel
1580
      fprintf (out, "ifstool - QNX in-kernel filesystem creation utility by Pierre-Marie Baty <pm@pmbaty.com>\n");
1581
      fprintf (out, "          version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD);
4 pmbaty 1582
      if (!want_help)
16 pmbaty 1583
         fprintf (out, "error: missing parameters\n");
1584
      fprintf (out, "usage:\n");
1585
      fprintf (out, "    ifstool --info [--everything] <ifs file>\n");
1586
      fprintf (out, "    ifstool --dump [--outdir <path>] <ifs file>\n");
1587
      fprintf (out, "    ifstool --strip [--outfile <pathname>] <ELF file>\n");
17 pmbaty 1588
      fprintf (out, "    ifstool [-?|--help]\n");
1589
      fprintf (out, "    ifstool [--bootfile <pathname>] [--startupfile <pathname>@<EP_from_imgbase>] [--kernelfile <pathname>@<fileoffs>] [-l inputline] [-n[n]] [-r rootdir] [-v[...]] <buildfile> <outfile>\n");
1590
      fprintf (out, "NOTE: the compiler mode requires predigested boot, startup and kernel files produced by mkifs.\n");
1591
      fprintf (out, "options:\n");
1592
      fprintf (out, "    -?       Display some help information.\n");
1593
//      fprintf (out, "    -a .ext  Append a suffix to symbol files generated via [+keeplinked].\n");
1594
      fprintf (out, "    -l line  Process line before interpreting the buildfile. Input lines given\n");
1595
      fprintf (out, "             to mkifs should be quoted to prevent interpretation by the shell\n");
1596
      fprintf (out, "             (especially as mkifs input lines often contain spaces). Multiple\n");
1597
      fprintf (out, "             -l options are processed in the order specified. No default.\n");
1598
      fprintf (out, "    -n[n]    Force the modification times of all inline files to be 0. If you\n");
1599
      fprintf (out, "             specify -nn, mkifs sets the modification times of all files to 0.\n");
1600
      fprintf (out, "             When mkifs adds files to an IFS image, it uses the timestamp info\n");
1601
      fprintf (out, "             from the file on the host machine. If mkifs is creating an inline\n");
1602
      fprintf (out, "             file (which doesn't exist on the host machine), it must generate\n");
1603
      fprintf (out, "             its own timestamp information. By default, it's the time at which\n");
1604
      fprintf (out, "             the image is generated. This results in different checksum values\n");
1605
      fprintf (out, "             for two identical builds, because the file's times are different.\n");
1606
      fprintf (out, "             If you use -n, the checksum value is the same on all identical\n");
1607
      fprintf (out, "             builds. The -nn option addresses a quirk in NTFS with daylight\n");
1608
      fprintf (out, "             savings time. This forces the modification time for all files in\n");
1609
      fprintf (out, "             the IFS image to be set to 0. This ensures that subsequent builds\n");
1610
      fprintf (out, "             of the same IFS image have the same checksum.");
1611
//      fprintf (out, "    -o dir   Specify a directory to be used for all permanent build artifacts,\n");
1612
//      fprintf (out, "             other than the output image itself. The most common example is\n");
1613
//      fprintf (out, "             the .sym files generated by the [+keeplinked] attribute.\n");
1614
//      fprintf (out, "    -p file  Apply patching instructions from this file.\n");
1615
      fprintf (out, "    -r dir   When searching for host files to be included in the image, search\n");
1616
      fprintf (out, "             the default paths used for storing binaries within the specified\n");
1617
      fprintf (out, "             directory before searching the default paths within $QNX_TARGET.\n");
1618
      fprintf (out, "             You can define multiple -r options; each adds a set of paths to\n");
1619
      fprintf (out, "             search for files. The -r options are evaluated from left to right\n");
1620
      fprintf (out, "             meaning the paths prefixed with the first (leftmost) rootdir are\n");
1621
      fprintf (out, "             searched first, then those prefixed with the second rootdir, and\n");
1622
      fprintf (out, "             so on.\n");
1623
      fprintf (out, "             Normally, mkifs searches any paths defined in $MKIFS_PATH when\n");
1624
      fprintf (out, "             it was called and then the default paths within $QNX_TARGET. The\n");
1625
      fprintf (out, "             default paths are based on the CPU architecture specified by\n");
1626
      fprintf (out, "             $PROCESSOR and $PROCESSOR_BASE. If you specify -r options, mkifs\n");
1627
      fprintf (out, "             searches the default paths prefixed with each dir variable before\n");
1628
      fprintf (out, "             searching those within $QNX_TARGET. These paths are:\n");
1629
      fprintf (out, "               dir/${PROCESSOR}/sbin\n");
1630
      fprintf (out, "               dir/${PROCESSOR}/usr/sbin\n");
1631
      fprintf (out, "               dir/${PROCESSOR}/boot/sys\n");
1632
      fprintf (out, "               dir/${PROCESSOR_BASE}/boot/sys\n");
1633
      fprintf (out, "               dir/${PROCESSOR}/bin\n");
1634
      fprintf (out, "               dir/${PROCESSOR}/usr/bin\n");
1635
      fprintf (out, "               dir/${PROCESSOR}/lib\n");
1636
      fprintf (out, "               dir/${PROCESSOR}/lib/dll\n");
1637
      fprintf (out, "               dir/${PROCESSOR}/usr/lib\n");
1638
      fprintf (out, "             NOTE: The structure of the directory paths under dir must be\n");
1639
      fprintf (out, "             identical to that of the default paths under $QNX_TARGET, but the\n");
1640
      fprintf (out, "             root dir itself may be any path you choose. For example, if you\n");
1641
      fprintf (out, "             wanted to include /scratch/aarch64le/sbin/devb-sata, you would\n");
1642
      fprintf (out, "             specify a -r option like this:\n");
1643
      fprintf (out, "               -r /scratch\n");
1644
      fprintf (out, "             Note that you don't include $PROCESSOR or $PROCESSOR_BASE in dir.\n");
1645
      fprintf (out, "    -s name  Don't strip the named section from ELF executables when creating\n");
1646
      fprintf (out, "             an IFS image. You can use this option more than once to specify\n");
1647
      fprintf (out, "             additional sections. By default, mkifs doesn't strip:\n");
1648
      fprintf (out, "               .gnu_debuglink - the name and checksum of the debug info file\n");
1649
      fprintf (out, "               QNX_info       - build properties\n");
1650
      fprintf (out, "               QNX_usage      - usage message\n");
1651
      fprintf (out, "             You can use the keepsection attribute to specify the sections\n");
1652
      fprintf (out, "             that are not to be stripped from specific files in the image. For\n");
1653
      fprintf (out, "             files in the bootstrap section (like startup or procnto), the\n");
1654
      fprintf (out, "             global keepsection list affected by -s does not apply to these\n");
1655
      fprintf (out, "             files. For them, only the QNX_info section is kept.\n");
1656
      fprintf (out, "    -v[v..]  Operate verbosely. Specifying additional v options increases the\n");
1657
      fprintf (out, "             verbosity.\n");
1 pmbaty 1658
      exit (want_help ? 0 : 1);
1659
   }
1660
 
14 pmbaty 1661
   // do we want info about a particular IFS ? if so, dissecate it
2 pmbaty 1662
   if (want_info)
16 pmbaty 1663
      exit (dump_ifs_info (first_pathname, want_everything));
2 pmbaty 1664
 
14 pmbaty 1665
   // else do we want to dump its contents ? if so, do so
1666
   else if (want_dump)
16 pmbaty 1667
      exit (dump_ifs_contents (first_pathname, (second_pathname != NULL ? second_pathname : ".")));
14 pmbaty 1668
 
15 pmbaty 1669
   // else do we want to hex dump a file ? (this is voluntarily undocumented)
1670
   else if (want_hexdump)
16 pmbaty 1671
      exit (dump_file_hex (first_pathname));
1672
 
1673
   // else do we want to strip an ELF file ? if so, do so
1674
   else if (want_strip)
15 pmbaty 1675
   {
16 pmbaty 1676
      buffer_t file;
1677
      ASSERT (Buffer_ReadFromFile (&file, first_pathname), "can't open \"%s\" for reading: %s", first_pathname, strerror (errno));
17 pmbaty 1678
      ASSERT (Buffer_StripELFFile (&file, saved_ELF_sections, saved_ELF_section_count, first_pathname), "error stripping \"%s\": %s", first_pathname, strerror (errno));
16 pmbaty 1679
      ASSERT_WITH_ERRNO (Buffer_WriteToFile (&file, (second_pathname != NULL ? second_pathname : "<stdout>")));
15 pmbaty 1680
      exit (0);
1681
   }
1682
 
16 pmbaty 1683
   // we want to CREATE an IFS file
1684
   buildfile_pathname = first_pathname; // assign the pathnames properly
1685
   ifs_pathname = second_pathname;
1686
 
1 pmbaty 1687
   // make sure we have ${QNX_TARGET} pointing somewhere
1688
   QNX_TARGET = getenv ("QNX_TARGET");
1689
   if (QNX_TARGET == NULL)
15 pmbaty 1690
      DIE_WITH_EXITCODE (1, "the QNX_TARGET environment variable is not set");
1 pmbaty 1691
   else if (access (QNX_TARGET, 0) != 0)
15 pmbaty 1692
      DIE_WITH_EXITCODE (1, "the QNX_TARGET environment variable doesn't point to an existing directory");
1 pmbaty 1693
 
1694
   // open build file
16 pmbaty 1695
   fopen_s (&buildfile_fp, buildfile_pathname, "rb");
1 pmbaty 1696
   if (buildfile_fp == NULL)
15 pmbaty 1697
      DIE_WITH_EXITCODE (1, "unable to open build file \"%s\" for reading: %s", buildfile_pathname, strerror (errno));
1 pmbaty 1698
 
1699
   // stack up filesystem entries
1700
   memcpy (&entry_parms, &default_parms, sizeof (default_parms));
1701
   entry_parms.st_mode = S_IFDIR | default_parms.dperms;
7 pmbaty 1702
   add_fsentry (&fsentries, &fsentry_count, &entry_parms, "", NULL); // add the root dir first
1 pmbaty 1703
 
17 pmbaty 1704
   // parse -l arguments before everything else
1705
   for (arg_index = 1; arg_index < argc; arg_index++)
1706
      if ((strcmp (argv[arg_index], "-l") == 0) && (arg_index + 1 < argc))
1707
         parse_line (NULL, argv[++arg_index], &fsentries, &fsentry_count, &default_parms);
1708
 
11 pmbaty 1709
   // parse the IFS build file line per line
1 pmbaty 1710
   while (fgets (line_buffer, sizeof (line_buffer), buildfile_fp) != NULL)
1711
   {
7 pmbaty 1712
      if (current_line != NULL)
1713
         free (current_line);
1714
      current_line = strdup (line_buffer);
16 pmbaty 1715
      ASSERT_WITH_ERRNO (current_line);
1 pmbaty 1716
      lineno++; // keep track of current line number
17 pmbaty 1717
      parse_line (buildfile_fp, line_buffer, &fsentries, &fsentry_count, &default_parms);
1718
   }
1 pmbaty 1719
 
17 pmbaty 1720
   fclose (buildfile_fp); // finished parsing the build file
1 pmbaty 1721
 
17 pmbaty 1722
   // parse the IFS build file line per line
1723
   while (fgets (line_buffer, sizeof (line_buffer), buildfile_fp) != NULL)
1724
   {
1725
      if (current_line != NULL)
1726
         free (current_line);
1727
      current_line = strdup (line_buffer);
1728
      ASSERT_WITH_ERRNO (current_line);
1729
      lineno++; // keep track of current line number
1 pmbaty 1730
 
1731
   }
1732
 
16 pmbaty 1733
   fclose (buildfile_fp); // finished parsing the build file
1 pmbaty 1734
 
16 pmbaty 1735
   //////////////////////////////////
1736
   // start constructing the IFS file
1737
 
1738
   Buffer_Initialize (&ifs.data);
1739
 
2 pmbaty 1740
   // do we have a startup file ? if so, this is a bootable image
1741
   if (startupfile_pathname != NULL)
1 pmbaty 1742
   {
1743
      // write boot prefix
11 pmbaty 1744
      // ######################################################################################################################################################################################################################################
1745
      // # FIXME: figure out how to re-create it
1746
      // ######################################################################################################################################################################################################################################
16 pmbaty 1747
      buffer_t file;
1748
      if (!Buffer_ReadFromFile (&file, bootfile_pathname))
1749
         DIE_WITH_EXITCODE (1, "failed to open \"%s\" for reading: %s", bootfile_pathname, strerror (errno));
1750
      ASSERT_WITH_ERRNO (Buffer_AppendBuffer (&ifs.data, &file)); // write boot blob
1751
      Buffer_Forget (&file);
1752
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 1753
 
16 pmbaty 1754
      ifs.offsets.startupheader = ifs.data.size; // save startup header offset for future use
1 pmbaty 1755
      memset (&startup_header, 0, sizeof (startup_header)); // prepare startup header
1756
      memcpy (startup_header.signature, "\xeb\x7e\xff\x00", 4); // startup header signature, i.e. 0xff7eeb
1757
      startup_header.version       = 1;
1758
      startup_header.flags1        = STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2; // flags, 0x21 (STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2)
1759
      startup_header.header_size   = sizeof (startup_header); // 256
1760
      if (strcmp (image_processor, "x86_64") == 0)
10 pmbaty 1761
         startup_header.machine = ELF_MACHINE_X86_64; // EM_X86_64
1 pmbaty 1762
      else if (strcmp (image_processor, "aarch64le") == 0)
10 pmbaty 1763
         startup_header.machine = ELF_MACHINE_AARCH64; // EM_AARCH64
1 pmbaty 1764
      else
15 pmbaty 1765
         DIE_WITH_EXITCODE (1, "unsupported processor type '%s' found in build file \"%s\"", image_processor, buildfile_pathname); // should not happen
1 pmbaty 1766
      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 1767
      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 1768
      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)
1769
      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)
1770
      startup_header.startup_size  = WILL_BE_FILLED_LATER;                                  // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes
2 pmbaty 1771
      startup_header.stored_size   = WILL_BE_FILLED_LATER;                                  // [I ] Size of entire image, here 0x00cd6128 (same as ram_size)
1 pmbaty 1772
      startup_header.imagefs_size  = WILL_BE_FILLED_LATER;                                  // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes
2 pmbaty 1773
      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 1774
      ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &startup_header, sizeof (startup_header))); // write startup header
1775
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 1776
 
1777
      // ######################################################################################################################################################################################################################################
11 pmbaty 1778
      // # FIXME: figure out how to re-create it:
1779
      // first: open "startup-x86" ELF file,
1780
      //        lookup section headers table (there is no program headers table in this one)
1781
      //        FIXME: figure out something in there where the result is 0x1401030 !!!
1782
      // 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
1783
      // then: parse resulting ELF file, take all program segments and concatenate them --> this is the blob (FIXME: wrong?)
1 pmbaty 1784
      // ######################################################################################################################################################################################################################################
11 pmbaty 1785
#if 0 // nonworking
16 pmbaty 1786
      // <deleted>
11 pmbaty 1787
#else // working
16 pmbaty 1788
      if (!Buffer_ReadFromFile (&file, startupfile_pathname))
1789
         DIE_WITH_EXITCODE (1, "failed to open \"%s\" for reading: %s", startupfile_pathname, strerror (errno));
1790
      ASSERT_WITH_ERRNO (Buffer_AppendBuffer (&ifs.data, &file)); // write startup blob
1791
      Buffer_Forget (&file);
11 pmbaty 1792
#endif // working
16 pmbaty 1793
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 1794
 
16 pmbaty 1795
      ifs.offsets.startuptrailer = ifs.data.size; // save startup trailer offset for future use
1796
      ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &startup_trailer, sizeof (startup_trailer))); // write startup trailer
1797
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 1798
   }
1799
 
16 pmbaty 1800
   ifs.offsets.imageheader = ifs.data.size; // save image header offset for future use
1 pmbaty 1801
   memset (&image_header, 0, sizeof (image_header)); // prepare image header
1802
   memcpy (&image_header.signature, "imagefs", 7); // image filesystem signature, i.e. "imagefs"
1803
   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)
1804
   image_header.image_size    = WILL_BE_FILLED_LATER; // size from header to end of trailer (here 0xca6fe0 or 13 266 912)
7 pmbaty 1805
   image_header.hdr_dir_size  = WILL_BE_FILLED_LATER; // size from header to last dirent (here 0x12b8 or 4792)
1 pmbaty 1806
   image_header.dir_offset    = sizeof (image_header); // offset from header to first dirent (here 0x5c or 92)
1807
   image_header.boot_ino[0]   = image_kernel_ino; // inode of files for bootstrap p[ro?]g[ra?]ms (here 0xa0000002, 0, 0, 0)
1808
   image_header.script_ino    = image_bootscript_ino; // inode of file for script (here 3)
1809
   image_header.mountpoint[0] = '/'; // default mountpoint for image ("/" + "\0\0\0")
16 pmbaty 1810
   ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &image_header, sizeof (image_header))); // write image header
1811
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 1812
 
6 pmbaty 1813
   // write image directory (with the wrong file offsets)
16 pmbaty 1814
   ifs.offsets.imagedir = ifs.data.size; // save image directory offset for future use
1815
   curr_offset = ifs.offsets.imagedir;
1 pmbaty 1816
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
16 pmbaty 1817
   {
1818
      Buffer_WriteIFSDirectoryEntryAt (&ifs.data, curr_offset, &fsentries[fsentry_index]); // write each dirent (the unknown fields will be fixed later)
1819
      curr_offset += fsentries[fsentry_index].header.size; // advance to the next one
1820
   }
1821
   ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&ifs.data, "\0\0\0\0")); // there seems to be 4 bytes of padding after the image directory
1822
   imgdir_size = ifs.data.size - ifs.offsets.imagedir; // measure image dir size and save it for future use
1 pmbaty 1823
 
1824
   // is it a bootable image with a kernel file ?
2 pmbaty 1825
   if ((startupfile_pathname != NULL) && (kernelfile_pathname != NULL))
1 pmbaty 1826
   {
7 pmbaty 1827
      // start by writing the startup script data blob, if we have one
1828
      for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
1829
         if (fsentries[fsentry_index].header.ino == image_bootscript_ino)
8 pmbaty 1830
            break; // locate the startup script directory entry
7 pmbaty 1831
      if (fsentry_index < fsentry_count) // found it ?
1 pmbaty 1832
      {
16 pmbaty 1833
         if (ifs.data.size + fsentries[fsentry_index].u.file.size >= kernelfile_offset)
1834
            DIE_WITH_EXITCODE (1, "the compiled startup script is too big (%zd bytes, max is %zd) to fit at current offset %zd", (size_t) fsentries[fsentry_index].u.file.size, kernelfile_offset - ifs.data.size, ifs.data.size);
1835
         fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
1836
         Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
1 pmbaty 1837
         fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1838
      }
7 pmbaty 1839
 
1840
      // now write the filesystem entries that may fit before the kernel
1841
      for (;;)
1842
      {
16 pmbaty 1843
         available_space = kernelfile_offset - ifs.data.size; // measure the available space until the kernel
7 pmbaty 1844
 
1845
         // look for the biggest one that can fit
1846
         largest_index = 0;
8 pmbaty 1847
         largest_size = 0;
7 pmbaty 1848
         for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
1849
         {
1850
            if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written || (fsentries[fsentry_index].u.file.size > available_space))
1851
               continue; // skip all entries that don't have a separate data block, those who were written already and those that wouldn't fit
1852
            if (fsentries[fsentry_index].u.file.size > largest_size)
1853
            {
1854
               largest_size = fsentries[fsentry_index].u.file.size;
1855
               largest_index = fsentry_index;
1856
            }
1857
         }
8 pmbaty 1858
         if (largest_size == 0)
7 pmbaty 1859
            break; // found none ? if so, stop searching
16 pmbaty 1860
         fsentry_index = largest_index;
7 pmbaty 1861
 
16 pmbaty 1862
         fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
1863
         Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
1864
         fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
7 pmbaty 1865
      }
16 pmbaty 1866
      LOG_INFO ("Last written offset: 0x%zx", ifs.data.size);
15 pmbaty 1867
      LOG_INFO ("Kernel file offset: 0x%zx", kernelfile_offset);
16 pmbaty 1868
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, kernelfile_offset)); // reach the kernel offset
1 pmbaty 1869
 
8 pmbaty 1870
      // now write the QNX kernel
1871
      for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
1872
         if (fsentries[fsentry_index].header.ino == image_kernel_ino)
1873
            break; // locate the kernel directory entry (can't fail)
16 pmbaty 1874
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
15 pmbaty 1875
#ifdef PROCNTO_WIP
1876
      // is the kernel we're storing a preprocessed ELF kernel ?
1877
      if (fsentries[fsentry_index].header.ino & IFS_INO_PROCESSED_ELF)
1878
      {
1879
         elf = (elf_header_t *) fsentries[fsentry_index].u.file.UNSAVED_databuf; // quick access to ELF header
1880
         table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); // get the number of program headers
1881
         for (table_index = 0; table_index < table_count; table_index++)
1882
         {
1883
            phdr = (elf_program_header_t *) &fsentries[fsentry_index].u.file.UNSAVED_databuf[ELF_GET_NUMERIC (elf, elf, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, program_header_item_size) * table_index]; // quick access to program header
1884
            corrective_offset = ELF_GET_NUMERIC (elf, phdr, virtual_addr) - ELF_GET_NUMERIC (elf, phdr, file_offset);
1885
            if (ELF_GET_NUMERIC (elf, phdr, size_in_memory) != 0) // only patch the physical address of segments that have an actual size in memory
16 pmbaty 1886
               ELF_SET_NUMERIC (elf, phdr, physical_addr, ELF_GET_NUMERIC (elf, phdr, physical_addr) + image_base + ifs.data.size - corrective_offset); // patch the physical address member of the program header table (NOTE: ifs.data.size is the location where the file data is about to be written)
15 pmbaty 1887
         }
1888
      }
1889
#endif // PROCNTO_WIP
16 pmbaty 1890
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write kernel file data
8 pmbaty 1891
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1 pmbaty 1892
   }
1893
 
8 pmbaty 1894
   // then write all the other files by increasing inode number: ELF files first
7 pmbaty 1895
   for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
1 pmbaty 1896
   {
8 pmbaty 1897
      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
1898
          || (fsentries[fsentry_index].u.file.size < 4) || (memcmp (fsentries[fsentry_index].u.file.UNSAVED_databuf, ELF_MAGIC_STR, 4) != 0)) // filter out anything that's not an ELF file
1 pmbaty 1899
         continue; // skip all entries that don't have a separate data block and those who were written already
16 pmbaty 1900
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
1901
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
1 pmbaty 1902
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1903
   }
8 pmbaty 1904
   for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++) // other files (non-ELF, e.g. scripts and data files) last
1905
   {
1906
      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
1907
         continue; // skip all entries that don't have a separate data block and those who were written already
16 pmbaty 1908
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
1909
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
8 pmbaty 1910
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1911
   }
16 pmbaty 1912
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 1913
 
1914
   // finally, write trailer (including empty checksum)
16 pmbaty 1915
   ifs.offsets.imagetrailer = ifs.data.size; // save image trailer offset for future use
1916
   ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &image_trailer, sizeof (image_trailer))); // write image trailer
1917
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 1918
 
1919
   // if we need to pad it to a specific length, do so
16 pmbaty 1920
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, image_totalsize));
1921
   ifs.final_size = ifs.data.size; // and this is the final size of the IFS
1 pmbaty 1922
 
1923
   // see if we are past the image max size, in which case it's an error
16 pmbaty 1924
   if (ifs.final_size > image_maxsize)
1925
      DIE_WITH_EXITCODE (1, "image file \"%s\" size %zd exceeds max size (%zd)", ifs_pathname, ifs.final_size, (size_t) image_maxsize);
1 pmbaty 1926
 
2 pmbaty 1927
   // do we have a startup file ? if so, this is a bootable image
1928
   if (startupfile_pathname != NULL)
1 pmbaty 1929
   {
16 pmbaty 1930
      // patch the startup header with its final values
1931
      startup_header.startup_size = (uint32_t) (ifs.offsets.imageheader - ifs.offsets.startupheader); // size of startup header up to image header
1932
      startup_header.imagefs_size = (uint32_t) (ifs.final_size - ifs.offsets.imageheader); // size of uncompressed imagefs
1933
      startup_header.ram_size     = (uint32_t) (ifs.final_size - ifs.offsets.startupheader);
1934
      startup_header.stored_size  = (uint32_t) (ifs.final_size - ifs.offsets.startupheader);
1935
      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 1936
   }
1 pmbaty 1937
 
8 pmbaty 1938
   // rewrite image header with final values
16 pmbaty 1939
   image_header.image_size = (uint32_t) (ifs.final_size - ifs.offsets.imageheader); // size of uncompressed imagefs
8 pmbaty 1940
   image_header.hdr_dir_size = sizeof (image_header) + (uint32_t) imgdir_size; // size from start of image header to last dirent
16 pmbaty 1941
   ASSERT_WITH_ERRNO (Buffer_WriteAt (&ifs.data, ifs.offsets.imageheader, &image_header, sizeof (image_header))); // write image header
8 pmbaty 1942
 
1943
   // rewrite image directory with final offset values
1944
   if (image_header.flags & IMAGE_FLAGS_SORTED)
16 pmbaty 1945
      qsort (&fsentries[1], fsentry_count - 1, sizeof (fsentry_t), fsentry_compare_pathnames_cb); // sort the filesystem entries by pathname if necessary
1946
   curr_offset = ifs.offsets.imagedir; // position ourselves at the beginning of the image directory
8 pmbaty 1947
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
16 pmbaty 1948
   {
1949
      Buffer_WriteIFSDirectoryEntryAt (&ifs.data, curr_offset, &fsentries[fsentry_index]); // rewrite each dirent
1950
      curr_offset += fsentries[fsentry_index].header.size; // advance to the next one
1951
   }
8 pmbaty 1952
 
1953
   // ALL CHECKSUMS AT THE VERY END
1954
 
1955
   // do we have a startup file ? if so, this is a bootable image
1956
   if (startupfile_pathname != NULL)
1957
   {
1 pmbaty 1958
      // compute SHA-512 checksum and V1 checksum of startup block
6 pmbaty 1959
      if (   ( (startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
1960
          || (!(startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
1961
         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
1962
      else
1963
         is_foreign_endianness = false; // else this header is for the same endianness as us
1 pmbaty 1964
 
16 pmbaty 1965
      if (startup_header.flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2) // is it a V2 trailer ?
1966
      {
1967
         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
1968
         checksum = update_checksum (&ifs.data.bytes[ifs.offsets.startupheader], ifs.offsets.startuptrailer + SHA512_DIGEST_LENGTH - ifs.offsets.startupheader, is_foreign_endianness); // compute old checksum
1969
         memcpy (&ifs.data.bytes[ifs.offsets.startuptrailer + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place
1970
      }
1971
      else // old V1 trailer
1972
      {
1973
         checksum = update_checksum (&ifs.data.bytes[ifs.offsets.startupheader], ifs.offsets.startuptrailer - ifs.offsets.startupheader, is_foreign_endianness); // compute old checksum
1974
         memcpy (&ifs.data.bytes[ifs.offsets.startuptrailer], &checksum, 4); // and write it in place
1975
      }
1 pmbaty 1976
   }
1977
 
1978
   // compute SHA-512 checksum and V1 checksum of image block
5 pmbaty 1979
   if (   ( (image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
1980
       || (!(image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
1981
      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
1982
   else
1983
      is_foreign_endianness = false; // else this header is for the same endianness as us
1 pmbaty 1984
 
16 pmbaty 1985
   if (image_header.flags & IMAGE_FLAGS_TRAILER_V2) // is it a V2 trailer ?
2 pmbaty 1986
   {
16 pmbaty 1987
      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
1988
      checksum = update_checksum (&ifs.data.bytes[ifs.offsets.imageheader], ifs.offsets.imagetrailer + SHA512_DIGEST_LENGTH - ifs.offsets.imageheader, is_foreign_endianness); // compute old checksum
1989
      memcpy (&ifs.data.bytes[ifs.offsets.imagetrailer + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place
2 pmbaty 1990
   }
16 pmbaty 1991
   else // old V1 trailer
3 pmbaty 1992
   {
16 pmbaty 1993
      checksum = update_checksum (&ifs.data.bytes[ifs.offsets.imageheader], ifs.offsets.imagetrailer - ifs.offsets.imageheader, is_foreign_endianness); // compute old checksum
1994
      memcpy (&ifs.data.bytes[ifs.offsets.imagetrailer], &checksum, 4); // and write it in place
3 pmbaty 1995
   }
2 pmbaty 1996
 
16 pmbaty 1997
   // now rewrite IFS with the correct checksums
1998
   ASSERT_WITH_ERRNO (Buffer_WriteToFile (&ifs.data, ifs_pathname));
14 pmbaty 1999
 
16 pmbaty 2000
   // finished, cleanup
2001
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
14 pmbaty 2002
   {
2003
   }
2004
 
16 pmbaty 2005
   // and exit with a success code
2006
   LOG_INFO ("Success");
2007
   exit (0);
14 pmbaty 2008
}