Subversion Repositories QNX 8.QNX8 IFS tool

Rev

Rev 2 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. // ifstool.c -- portable reimplementation of QNX's mkifs by Pierre-Marie Baty <pm@pmbaty.com>
  2.  
  3. #include <stdint.h>
  4. #include <stdbool.h>
  5. #include <stdlib.h>
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <errno.h>
  9. #include <sys/stat.h>
  10. #include <ctype.h>
  11. #include <time.h>
  12.  
  13.  
  14. #ifdef _MSC_VER
  15. #include <io.h>
  16. #define __ORDER_LITTLE_ENDIAN__ 1234
  17. #define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
  18. #define __attribute__(x)
  19. #define __builtin_bswap64(x) _byteswap_uint64 ((unsigned long long) (x))
  20. #define S_IFIFO 0x1000
  21. #define S_IFLNK 0xa000
  22. #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
  23. #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
  24. #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
  25. #define strdup(s) _strdup ((s))
  26. #define fseek(fp,off,m) _fseeki64 ((fp), (off), (m))
  27. #define access(p,m) _access ((p), (m))
  28. #define MAXPATHLEN 1024
  29. #else // !_MSC_VER
  30. #include <sys/param.h>
  31. #include <unistd.h>
  32. #endif // _MSC_VER
  33.  
  34. #ifdef _MSC_VER
  35. #pragma pack(push)
  36. #pragma pack(1)
  37. #endif // _MSC_VER
  38.  
  39.  
  40. #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
  41. #ifdef _WIN32
  42. #define IS_DIRSEP(c) (((c) == '/') || ((c) == '\\'))
  43. #define PATH_SEP ';'
  44. #else // !_WIN32, thus POSIX
  45. #define IS_DIRSEP(c) ((c) == '/')
  46. #define PATH_SEP ':'
  47. #endif // _WIN32
  48. #define RECORD_SEP '\x1e' // ASCII record separator
  49. #define RECORD_SEP_STR "\x1e" // ASCII record separator (as string)
  50.  
  51. #define WILL_BE_FILLED_LATER 0xbaadf00d
  52.  
  53.  
  54. // bitmapped flags used in the flags1 member of the startup header
  55. #define STARTUP_HDR_FLAGS1_VIRTUAL        (1 << 0)
  56. #define STARTUP_HDR_FLAGS1_BIGENDIAN      (1 << 1)
  57. //#define STARTUP_HDR_FLAGS1_COMPRESS_MASK  0x1c
  58. //#define STARTUP_HDR_FLAGS1_COMPRESS_SHIFT 0x02
  59. //#define STARTUP_HDR_FLAGS1_COMPRESS_NONE  0x00
  60. //#define STARTUP_HDR_FLAGS1_COMPRESS_ZLIB  0x04
  61. //#define STARTUP_HDR_FLAGS1_COMPRESS_LZO   0x08
  62. //#define STARTUP_HDR_FLAGS1_COMPRESS_UCL   0x0c
  63. #define STARTUP_HDR_FLAGS1_TRAILER_V2     (1 << 5) // if set, then a struct startup_trailer_v2 follows the startup. If the image is compressed, then the compressed imagefs is followed by a struct image_trailer_v2
  64.  
  65.  
  66. #define STARTUP_HDR_MACHINE_X86_64  0x3e
  67. #define STARTUP_HDR_MACHINE_AARCH64 0xb7
  68.  
  69.  
  70. // bitmapped flags used in the flags member of the image header
  71. #define IMAGE_FLAGS_BIGENDIAN  (1 << 0) // header, trailer, dirents in big-endian format
  72. #define IMAGE_FLAGS_READONLY   (1 << 1) // do not try to write to image (rom/flash)
  73. #define IMAGE_FLAGS_INO_BITS   (1 << 2) // inode bits valid
  74. #define IMAGE_FLAGS_SORTED     (1 << 3) // dirent section is sorted (by pathname)
  75. #define IMAGE_FLAGS_TRAILER_V2 (1 << 4) // image uses struct image_trailer_v2
  76.  
  77.  
  78. // bitmapped flags superposed to a filesystem entry's inode number
  79. #define IFS_INO_PROCESSED_ELF 0x80000000
  80. #define IFS_INO_RUNONCE_ELF   0x40000000
  81. #define IFS_INO_BOOTSTRAP_EXE 0x20000000
  82.  
  83.  
  84. // SHA-512 block and digest sizes
  85. #define SHA512_BLOCK_LENGTH 128 // in bytes
  86. #define SHA512_DIGEST_LENGTH 64 // in bytes
  87.  
  88.  
  89. // SHA-512 computation context structure type definition
  90. typedef struct sha512_ctx_s
  91. {
  92.    uint64_t state[8];
  93.    uint64_t bitcount[2];
  94.    uint8_t buffer[SHA512_BLOCK_LENGTH];
  95. } SHA512_CTX;
  96.  
  97.  
  98. #if 0 // TODO: startup script compiler. Someday.
  99. #define SCRIPT_FLAGS_EXTSCHED   0x01
  100. #define SCRIPT_FLAGS_SESSION    0x02
  101. #define SCRIPT_FLAGS_SCHED_SET  0x04
  102. #define SCRIPT_FLAGS_CPU_SET    0x08
  103. #define SCRIPT_FLAGS_BACKGROUND 0x20
  104. #define SCRIPT_FLAGS_KDEBUG     0x40
  105.  
  106. #define SCRIPT_POLICY_NOCHANGE 0
  107. #define SCRIPT_POLICY_FIFO     1
  108. #define SCRIPT_POLICY_RR       2
  109. #define SCRIPT_POLICY_OTHER    3
  110.  
  111. #define SCRIPT_TYPE_EXTERNAL        0
  112. #define SCRIPT_TYPE_WAITFOR         1
  113. #define SCRIPT_TYPE_REOPEN          2
  114. #define SCRIPT_TYPE_DISPLAY_MSG     3
  115. #define SCRIPT_TYPE_PROCMGR_SYMLINK 4
  116. #define SCRIPT_TYPE_EXTSCHED_APS    5
  117.  
  118. #define SCRIPT_CHECKS_MS 100
  119.  
  120. #define SCRIPT_SCHED_EXT_NONE 0
  121. #define SCRIPT_SCHED_EXT_APS  1
  122.  
  123. #define SCRIPT_APS_SYSTEM_PARTITION_ID   0
  124. #define SCRIPT_APS_SYSTEM_PARTITION_NAME "System"
  125. #define SCRIPT_APS_PARTITION_NAME_LENGTH 15
  126. #define SCRIPT_APS_MAX_PARTITIONS        8
  127.  
  128.  
  129. typedef struct __attribute__((packed)) bootscriptcmd_header_s
  130. {
  131.    uint16_t size; // size of cmd entry
  132.    uint8_t type;
  133.    uint8_t spare;
  134. } bootscriptcmd_header_t;
  135.  
  136.  
  137. typedef union bootscriptcmd_s
  138. {
  139.    struct __attribute__((packed)) script_external
  140.    {
  141.       bootscriptcmd_header_t hdr;
  142.       uint8_t cpu; // CPU (turn into runmask)
  143.       uint8_t flags;
  144.       union script_external_extsched
  145.       {
  146.          uint8_t reserved[2];
  147.          struct __attribute__((packed))
  148.          {
  149.             uint8_t id;
  150.             uint8_t reserved[1];
  151.          } aps;
  152.       } extsched; // extended scheduler
  153.       uint8_t policy; // POLICY_FIFO, POLICY_RR, ...
  154.       uint8_t priority; // priority to run cmd at
  155.       uint8_t argc; // # of args
  156.       uint8_t envc; // # of environment entries
  157.       char args[0]; // executable, argv, envp (null padded to 32-bit align)
  158.    } external;
  159.    struct __attribute__((packed)) script_waitfor_reopen
  160.    {
  161.       bootscriptcmd_header_t hdr;
  162.       uint16_t checks;
  163.       char fname[0]; // char fname[] (null padded to 32-bit align)
  164.    } waitfor_reopen;
  165.    struct __attribute__((packed)) script_display_msg
  166.    {
  167.       bootscriptcmd_header_t hdr;
  168.       char msg[0]; // char msg[] (null padded to 32-bit align)
  169.    } display_msg;
  170.    struct __attribute__((packed)) script_procmgr_symlink
  171.    {
  172.       bootscriptcmd_header_t hdr;
  173.       char src_dest[0]; // <src_name>, '\0', <dest_name> '\0' (null padded to 32-bit align)
  174.    } procmgr_symlink;
  175.    struct __attribute__((packed)) script_extsched_aps
  176.    {
  177.       bootscriptcmd_header_t hdr;
  178.       uint8_t parent;
  179.       uint8_t budget;
  180.       uint16_t critical;
  181.       uint8_t id;
  182.       char pname[0]; // char pname[] (null padded to 32-bit align)
  183.    } extsched_aps;
  184. } bootscriptcmd_t;
  185. #endif // 0
  186.  
  187.  
  188. #define INITIAL_STARTUP_SCRIPT \
  189.    /* procmgr_symlink /proc/boot/ldqnx-64.so.2 /usr/lib/ldqnx-64.so.2 */ \
  190.    "\x34\x00" /*size*/ "\x04" /*type*/ "\x00" /*spare*/ "/proc/boot/ldqnx-64.so\0" "/usr/lib/ldqnx-64.so.2\0" \
  191.    /* sh /proc/boot/startup.sh */ \
  192.    "\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*/ \
  193.    /* display_msg "Startup complete */ \
  194.    "\x18\x00" /*size*/ "\x03" /*type*/ "\x00" /*spare*/ "Startup complete\n\0" "\x00\00" /*padding*/ \
  195.    /* trailer */ \
  196.    "\x00\x00\x00\x00"
  197. #define INITIAL_STARTUP_SCRIPT_LEN (sizeof (INITIAL_STARTUP_SCRIPT) - 1)
  198.  
  199.  
  200. typedef struct fsentry_s
  201. {
  202.    struct __attribute__((packed)) fsentry_header_s
  203.    {
  204.       uint16_t size; // size of dirent
  205.       uint16_t extattr_offset; // if zero, no extattr data
  206.       uint32_t ino; // if zero, skip entry
  207.       uint32_t mode; // mode and perms of entry
  208.       uint32_t gid;
  209.       uint32_t uid;
  210.       uint32_t mtime;
  211.    } header;
  212.    union fsentry_specific_u
  213.    {
  214.       struct fsentry_file_s // when (mode & S_IFMT) == S_IFREG
  215.       {
  216.          uint32_t offset; // offset from header
  217.          uint32_t size;
  218.          char *path; // null terminated path (no leading slash)
  219.          char *UNSAVED_databuf; // file data blob buffer (NOT SAVED IN THE IFS)
  220.       } file;
  221.       struct fsentry_dir_s // when (mode & S_IFMT) == S_IFDIR
  222.       {
  223.          char *path; // null terminated path (no leading slash)
  224.       } dir;
  225.       struct fsentry_symlink_s // when (mode & S_IFMT) == S_IFLNK
  226.       {
  227.          uint16_t sym_offset; // offset to 'contents' from 'path'
  228.          uint16_t sym_size; // strlen (contents)
  229.          char *path; // null terminated path (no leading slash)
  230.          char *contents; // null terminated symlink contents
  231.       } symlink;
  232.       struct fsentry_device_s // when (mode & S_IFMT) == S_IF<CHR|BLK|FIFO|NAM|SOCK>
  233.       {
  234.          uint32_t dev;
  235.          uint32_t rdev;
  236.          char *path; // null terminated path (no leading slash)
  237.       } device;
  238.    } u;
  239.    bool UNSAVED_was_data_written; // whether this entry's data was written to the image (NOT SAVED IN THE IFS)
  240. } fsentry_t;
  241.  
  242.  
  243. typedef struct __attribute__((packed)) startup_header_s // size 256 bytes
  244. {
  245.                            // I - used by the QNX IPL
  246.                            // S - used by the startup program
  247.    uint8_t signature[4];   // [I ] Header signature, "\xeb\x7e\xff\x00"
  248.    uint16_t version;       // [I ] Header version, i.e. 1
  249.    uint8_t flags1;         // [IS] Misc flags, 0x21 (= 0x20 | STARTUP_HDR_FLAGS1_VIRTUAL)
  250.    uint8_t flags2;         // [  ] No flags defined yet (0)
  251.    uint16_t header_size;   // [ S] sizeof(struct startup_header), i.e. 256
  252.    uint16_t machine;       // [IS] Machine type from elfdefinitions.h, i.e. 0x003E --> _ELF_DEFINE_EM(EM_X86_64, 62, "AMD x86-64 architecture")
  253.    uint32_t startup_vaddr; // [I ] Virtual Address to transfer to after IPL is done, here 0x01403008 (appears in "Entry" column for "startup.*")
  254.    uint32_t paddr_bias;    // [ S] Value to add to physical address to get a value to put into a pointer and indirected through, here 0 (no indirections)
  255.    uint32_t image_paddr;   // [IS] Physical address of image, here 0x01400f30 (appears in "Offset" column for "startup-header" which is the first entry/start of file)
  256.    uint32_t ram_paddr;     // [IS] Physical address of RAM to copy image to (startup_size bytes copied), here 0x01400f30 (same as above)
  257.    uint32_t ram_size;      // [ S] Amount of RAM used by the startup program and executables contained in the file system, here 0x00cd6128 i.e. 13 459 752 dec. which is 13 Mb. i.e. IFS file size minus 0x9eee
  258.    uint32_t startup_size;  // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes
  259.    uint32_t stored_size;   // [I ] Size of entire image, here 0x00cd6128 (same as ram_size)
  260.    uint32_t imagefs_paddr; // [IS] Set by IPL to where the imagefs is when startup runs (0)
  261.    uint32_t imagefs_size;  // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes
  262.    uint16_t preboot_size;  // [I ] Size of loaded before header, here 0xf30 or 3888 bytes (size of "bios.boot" file))
  263.    uint16_t zero0;         // [  ] Zeros
  264.    uint32_t zero[1];       // [  ] Zeros
  265.    uint64_t addr_off;      // [ S] Offset to add to startup_vaddr, image_paddr, ram_paddr, and imagefs_paddr members, here zero (0)
  266.    uint32_t info[48];      // [IS] Array of startup_info* structures (zero filled)
  267. } startup_header_t;
  268.  
  269.  
  270. typedef struct __attribute__((packed)) startup_trailer_s
  271. {
  272.    uint32_t cksum; // checksum from start of header to start of trailer
  273. } startup_trailer_t;
  274.  
  275.  
  276. // NOTE: The checksums in this trailer will only be valid prior to entering startup.
  277. // Because the startup binary is executed in-place, its data segment will change once the program is running.
  278. // Hence, any checksum validation would need to be done by the boot loader / IFS.
  279. typedef struct __attribute__((packed)) startup_trailer_v2_s
  280. {
  281.    uint8_t sha512[64]; // SHA512 from start of header to start of trailer
  282.    uint32_t cksum; // checksum from start of header to start of this member
  283. } startup_trailer_v2_t;
  284.  
  285.  
  286. typedef struct __attribute__((packed)) image_header_s
  287. {
  288.    uint8_t signature[7]; // image filesystem signature, i.e. "imagefs"
  289.    uint8_t flags; // endian neutral flags, 0x1c
  290.    uint32_t image_size; // size from header to end of trailer (here 0xca6fe0 or 13 266 912)
  291.    uint32_t hdr_dir_size; // size from header to last dirent (here 0x12b8 or 4792)
  292.    uint32_t dir_offset; // offset from header to first dirent (here 0x5c or 92)
  293.    uint32_t boot_ino[4]; // inode of files for bootstrap pgms (here 0xa0000002, 0, 0, 0)
  294.    uint32_t script_ino; // inode of file for script (here 3)
  295.    uint32_t chain_paddr; // offset to next filesystem signature (0)
  296.    uint32_t spare[10]; // zerofill
  297.    uint32_t mountflags; // default _MOUNT_* from sys/iomsg.h (0)
  298.    char mountpoint[4]; // default mountpoint for image ("/" + "\0\0\0")
  299. } image_header_t;
  300.  
  301.  
  302. typedef struct __attribute__((packed)) image_trailer_v1_s
  303. {
  304.    uint32_t cksum; // checksum from start of header to start of trailer
  305. } image_trailer_v1_t;
  306.  
  307.  
  308. // NOTE: the checksums in this trailer will only be valid until the first non-startup bootstrap binary (e.g., startup-verifier, procnto, ...) is invoked.
  309. // Because bootstrap binaries execute in-place, their data segments will change once the programs are running.
  310. // Hence, any checksum validation would need to be done either by the boot loader / IFS or by the startup.
  311. typedef struct __attribute__((packed)) image_trailer_v2_s
  312. {
  313.    uint8_t sha512[64]; // SHA512 from start of image header to start of trailer
  314.    uint32_t cksum; // checksum from start of header to start of this member
  315. } image_trailer_v2_t;
  316.  
  317.  
  318. #ifdef _MSC_VER
  319. #pragma pack(pop)
  320. #endif // _MSC_VER
  321.  
  322.  
  323. typedef struct parms_s
  324. {
  325.    int dperms; // directory permissions (e.g. 0755)
  326.    int perms; // file permissions (e.g. 0644)
  327.    int uid; // owner user ID (e.g. 0 = root)
  328.    int gid; // owner group ID (e.g. 0 = root)
  329.    int st_mode; // entry type (e.g. S_IFREG for files) and permissions
  330.    char prefix[MAXPATHLEN]; // install path (e.g. "proc/boot")
  331.    bool should_compile_contents_as_startup_script;
  332.    char search[10 * MAXPATHLEN]; // binary search path (the default one will be constructed at startup)
  333. } parms_t;
  334.  
  335.  
  336. // global variables
  337. static char line_buffer[4096]; // scrap buffer for the IFS build file parser
  338. 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
  339. static uint32_t image_end = UINT32_MAX; // default image end (no limit)
  340. static uint32_t image_maxsize = UINT32_MAX; // default image max size (no limit)
  341. static uint32_t image_totalsize = 0; // image total size, measured once all the blocks have been written to the output IFS file
  342. static uint32_t image_align = 4; // default image alignment, as per QNX docs
  343. static uint32_t image_kernel_ino = 0;
  344. static uint32_t image_bootscript_ino = 0;
  345. 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)
  346. static char *buildfile_pathname = NULL; // pathname of IFS build file
  347. static int lineno = 0; // current line number in IFS build file
  348. static char *QNX_TARGET = NULL; // value of the $QNX_TARGET environment variable
  349.  
  350.  
  351. // prototypes of local functions
  352. static void sha512_private_transform (SHA512_CTX *context, const uint64_t *data); // used internally in SHA512_Update() and SHA512_Final()
  353. static void SHA512_Init (SHA512_CTX *context);
  354. static void SHA512_Update (SHA512_CTX *context, void *data, size_t len);
  355. static void SHA512_Final (uint8_t digest[SHA512_DIGEST_LENGTH], SHA512_CTX *context);
  356. static uint8_t *SHA512 (void *data, size_t data_len, uint8_t *digest); // computes a SHA-512 in one pass (shortcut for SHA512_Init(), SHA512_Update() N times and SHA512_Final())
  357. static int update_checksum_int32s (const int32_t start_value, const int32_t *data, const size_t len); // update the sum of an array of 32-bit signed integers
  358. 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)
  359. static int fwrite_filecontents (const char *pathname, FILE *fp); // dumps the contents of pathname into fp
  360. static size_t fwrite_fsentry (const fsentry_t *fsentry, FILE *fp); // writes the given filesystem entry into fp (without its contents)
  361. static size_t add_fsentry (fsentry_t **fsentries, size_t *fsentry_count, const char *stored_pathname, parms_t *entry_parms, const uint8_t *data, const size_t entry_datalen); // stack up a new filesystem entry
  362. static int fsentry_compare_pathnames_cb (const void *a, const void *b); // qsort() comparison callback that sorts filesystem entries by pathnames
  363. static int fsentry_compare_sizes_cb (const void *a, const void *b); // qsort() comparison callback that sorts filesystem entries by size
  364.  
  365.  
  366. static void sha512_private_transform (SHA512_CTX *context, const uint64_t *data)
  367. {
  368.    // logical functions used in SHA-384 and SHA-512
  369.    #define S64(b,x)      (((x) >> (b)) | ((x) << (64 - (b)))) // 64-bit rotate right
  370.    #define Ch(x,y,z)     (((x) & (y)) ^ ((~(x)) & (z)))
  371.    #define Maj(x,y,z)    (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
  372.    #define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
  373.    #define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
  374.    #define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ ((x) >> 7))
  375.    #define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ ((x) >> 6))
  376.  
  377.    // hash constant words K for SHA-384 and SHA-512
  378.    static const uint64_t K512[80] = {
  379.       0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
  380.       0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
  381.       0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
  382.       0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
  383.       0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
  384.       0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
  385.       0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
  386.       0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
  387.       0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
  388.       0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
  389.    };
  390.  
  391.    uint64_t     a, b, c, d, e, f, g, h, s0, s1;
  392.    uint64_t     T1, T2, *W512 = (uint64_t *) context->buffer;
  393.    int j;
  394.  
  395.    // initialize registers with the prev. intermediate value
  396.    a = context->state[0]; b = context->state[1]; c = context->state[2]; d = context->state[3]; e = context->state[4]; f = context->state[5]; g = context->state[6]; h = context->state[7];
  397.  
  398.    for (j = 0; j < 16; j++)
  399.    {
  400. #if __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
  401.       W512[j] = __builtin_bswap64 (*data); // convert to host byte order
  402. #elif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
  403.       W512[j] = *data;
  404. #else // __BYTE_ORDER__ == ???
  405. #error Please port this SHA-512 code to your exotic endianness platform. What are you compiling this on? PDP? Honeywell?
  406. #endif // __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
  407.  
  408.       // apply the SHA-512 compression function to update a..h
  409.       T1 = h + Sigma1_512 (e) + Ch (e, f, g) + K512[j] + W512[j];
  410.       T2 = Sigma0_512 (a) + Maj (a, b, c);
  411.  
  412.       // update registers
  413.       h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2;
  414.  
  415.       data++;
  416.    }
  417.  
  418.    for (; j < 80; j++)
  419.    {
  420.       // part of the message block expansion
  421.       s0 = W512[(j + 1) & 0x0f];
  422.       s0 = sigma0_512 (s0);
  423.       s1 = W512[(j + 14) & 0x0f];
  424.       s1 = sigma1_512 (s1);
  425.  
  426.       // apply the SHA-512 compression function to update a..h
  427.       T1 = h + Sigma1_512 (e) + Ch (e, f, g) + K512[j] + (W512[j & 0x0f] += s1 + W512[(j + 9) & 0x0f] + s0);
  428.       T2 = Sigma0_512 (a) + Maj (a, b, c);
  429.  
  430.       // update registers
  431.       h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2;
  432.    }
  433.  
  434.    // compute the current intermediate hash value
  435.    context->state[0] += a; context->state[1] += b; context->state[2] += c; context->state[3] += d; context->state[4] += e; context->state[5] += f; context->state[6] += g; context->state[7] += h;
  436.  
  437.    // clean up
  438.    a = b = c = d = e = f = g = h = T1 = T2 = 0;
  439.    #undef sigma1_512
  440.    #undef sigma0_512
  441.    #undef Sigma1_512
  442.    #undef Sigma0_512
  443.    #undef Maj
  444.    #undef Ch
  445.    #undef S64
  446.    return;
  447. }
  448.  
  449.  
  450. static void SHA512_Init (SHA512_CTX *context)
  451. {
  452.    // initial hash value H for SHA-512
  453.    static const uint64_t sha512_initial_hash_value[8] = {
  454.       0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
  455.    };
  456.  
  457.    memcpy (context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH);
  458.    memset (context->buffer, 0, SHA512_BLOCK_LENGTH);
  459.    context->bitcount[0] = context->bitcount[1] = 0;
  460. }
  461.  
  462.  
  463. void SHA512_Update (SHA512_CTX *context, void *datain, size_t len)
  464. {
  465.    #define ADDINC128(w,n) do { \
  466.            (w)[0] += (uint64_t) (n); \
  467.            if ((w)[0] < (n)) \
  468.                    (w)[1]++; \
  469.    } while (0) // macro for incrementally adding the unsigned 64-bit integer n to the unsigned 128-bit integer (represented using a two-element array of 64-bit words
  470.  
  471.    size_t freespace, usedspace;
  472.    const uint8_t *data = (const uint8_t *) datain;
  473.  
  474.    if (len == 0)
  475.       return; // calling with empty data is valid - we do nothing
  476.  
  477.    usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
  478.    if (usedspace > 0)
  479.    {
  480.       // calculate how much free space is available in the buffer
  481.       freespace = SHA512_BLOCK_LENGTH - usedspace;
  482.  
  483.       if (len >= freespace)
  484.       {
  485.          // fill the buffer completely and process it
  486.          memcpy (&context->buffer[usedspace], data, freespace);
  487.          ADDINC128 (context->bitcount, freespace << 3);
  488.          len -= freespace;
  489.          data += freespace;
  490.          sha512_private_transform (context, (uint64_t *) context->buffer);
  491.       }
  492.       else
  493.       {
  494.          // the buffer is not full yet
  495.          memcpy (&context->buffer[usedspace], data, len);
  496.          ADDINC128 (context->bitcount, len << 3);
  497.  
  498.          // clean up
  499.          usedspace = freespace = 0;
  500.          return;
  501.       }
  502.    }
  503.  
  504.    while (len >= SHA512_BLOCK_LENGTH)
  505.    {
  506.       // process as many complete blocks as we can
  507.       sha512_private_transform (context, (uint64_t *) data);
  508.       ADDINC128 (context->bitcount, SHA512_BLOCK_LENGTH << 3);
  509.       len -= SHA512_BLOCK_LENGTH;
  510.       data += SHA512_BLOCK_LENGTH;
  511.    }
  512.  
  513.    if (len > 0)
  514.    {
  515.       // save leftovers
  516.       memcpy (context->buffer, data, len);
  517.       ADDINC128 (context->bitcount, len << 3);
  518.    }
  519.  
  520.    // clean up
  521.    usedspace = freespace = 0;
  522.    #undef ADDINC128
  523.    return;
  524. }
  525.  
  526.  
  527. static void SHA512_Final (uint8_t digest[SHA512_DIGEST_LENGTH], SHA512_CTX *context)
  528. {
  529.    #define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16)
  530.  
  531.    size_t usedspace;
  532.    union { uint8_t *as_bytes; uint64_t *as_uint64s; } cast_var = { NULL };
  533.  
  534.    // if no digest buffer is passed, don't bother finalizing the computation
  535.    if (digest != NULL)
  536.    {
  537.       usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
  538.  
  539. #if __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
  540.       context->bitcount[0] = __builtin_bswap64 (context->bitcount[0]); // convert from host byte order
  541.       context->bitcount[1] = __builtin_bswap64 (context->bitcount[1]); // convert from host byte order
  542. #endif // __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
  543.  
  544.       if (usedspace > 0)
  545.       {
  546.          // begin padding with a 1 bit
  547.          context->buffer[usedspace++] = 0x80;
  548.  
  549.          if (usedspace <= SHA512_SHORT_BLOCK_LENGTH)
  550.             memset (&context->buffer[usedspace], 0, SHA512_SHORT_BLOCK_LENGTH - usedspace); // set-up for the last transform
  551.          else
  552.          {
  553.             if (usedspace < SHA512_BLOCK_LENGTH)
  554.                memset (&context->buffer[usedspace], 0, SHA512_BLOCK_LENGTH - usedspace);
  555.  
  556.             sha512_private_transform (context, (uint64_t *) context->buffer); // do second-to-last transform
  557.             memset (context->buffer, 0, SHA512_BLOCK_LENGTH - 2); // and set-up for the last transform
  558.          }
  559.       }
  560.       else // usedspace == 0
  561.       {
  562.          memset (context->buffer, 0, SHA512_SHORT_BLOCK_LENGTH); // prepare for final transform
  563.          *context->buffer = 0x80; // begin padding with a 1 bit
  564.       }
  565.  
  566.       // store the length of input data (in bits)
  567.       cast_var.as_bytes = context->buffer;
  568.       cast_var.as_uint64s[SHA512_SHORT_BLOCK_LENGTH / 8 + 0] = context->bitcount[1];
  569.       cast_var.as_uint64s[SHA512_SHORT_BLOCK_LENGTH / 8 + 1] = context->bitcount[0];
  570.  
  571.       // final transform
  572.       sha512_private_transform (context, (uint64_t *) context->buffer);
  573.  
  574.       // save the hash data for output
  575. #if __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
  576.       for (int j = 0; j < 8; j++)
  577.          context->state[j] = __builtin_bswap64 (context->state[j]); // convert to host byte order
  578. #endif // __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
  579.       memcpy (digest, context->state, SHA512_DIGEST_LENGTH);
  580.    }
  581.  
  582.    // zero out state data
  583.    memset (context, 0, sizeof (SHA512_CTX));
  584.    #undef SHA512_SHORT_BLOCK_LENGTH
  585.    return;
  586. }
  587.  
  588.  
  589. static uint8_t *SHA512 (void *data, size_t data_len, uint8_t *digest)
  590. {
  591.    // computes the SHA-512 hash of a block of data in one pass and write it to digest
  592.  
  593.    SHA512_CTX ctx;
  594.    SHA512_Init (&ctx);
  595.    SHA512_Update (&ctx, data, data_len);
  596.    SHA512_Final (digest, &ctx);
  597.    return (digest);
  598. }
  599.  
  600.  
  601. static int update_checksum_int32s (const int32_t start_value, const int32_t *data, const size_t len)
  602. {
  603.    // compute the sum of an array of 32-bit signed integers
  604.  
  605.    size_t byte_index;
  606.    int32_t sum = start_value;
  607.    for (byte_index = 0; byte_index < len; byte_index += sizeof (int32_t))
  608.       sum += *data++;
  609.    return (sum);
  610. }
  611.  
  612.  
  613. static long long read_integer (const char *str)
  614. {
  615.    // 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)
  616.  
  617.    char *endptr = NULL;
  618.    long long ret = strtoll (str, &endptr, 0); // use strtoll() to handle hexadecimal (0x...), octal (0...) and decimal (...) bases
  619.    if (endptr != NULL)
  620.    {
  621.       if ((*endptr == 'k') || (*endptr == 'K')) ret *= (size_t) 1024;
  622.       else if ((*endptr == 'm') || (*endptr == 'M')) ret *= (size_t) 1024 * 1024;
  623.       else if ((*endptr == 'g') || (*endptr == 'G')) ret *= (size_t) 1024 * 1024 * 1024;
  624.       else if ((*endptr == 't') || (*endptr == 'T')) ret *= (size_t) 1024 * 1024 * 1024 * 1024; // future-proof enough, I suppose?
  625.    }
  626.    return (ret);
  627. }
  628.  
  629.  
  630. static int fwrite_filecontents (const char *pathname, FILE *fp)
  631. {
  632.    // dumps the binary contents of pathname to fp
  633.  
  634.    uint8_t *blob_buffer;
  635.    size_t blob_size;
  636.    FILE *blob_fp;
  637.    int ret;
  638.  
  639.    blob_fp = fopen (pathname, "rb");
  640.    if (blob_fp == NULL)
  641.       return (-1); // errno is set
  642.  
  643.    fseek (blob_fp, 0, SEEK_END);
  644.    blob_size = ftell (blob_fp);
  645.    blob_buffer = malloc (blob_size);
  646.    if (blob_buffer == NULL)
  647.    {
  648.       fclose (blob_fp);
  649.       return (-1); // errno is set to ENOMEM
  650.    }
  651.    fseek (blob_fp, 0, SEEK_SET);
  652.    fread (blob_buffer, 1, blob_size, blob_fp);
  653.    fclose (blob_fp);
  654.  
  655.    ret = (int) fwrite (blob_buffer, 1, blob_size, fp);
  656.    free (blob_buffer);
  657.    return (ret);
  658. }
  659.  
  660.  
  661. static size_t fwrite_fsentry (const fsentry_t *fsentry, FILE *fp)
  662. {
  663.    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";
  664.  
  665.    size_t datalen;
  666.    size_t count;
  667.  
  668.    count = 0;
  669.    if (fp != NULL)
  670.       fwrite (&fsentry->header, sizeof (fsentry->header), 1, fp); // write the entry header (PACKED STRUCT)
  671.    count += sizeof (fsentry->header);
  672.    if (S_ISREG (fsentry->header.mode))
  673.    {
  674.       if (fp != NULL)
  675.       {
  676.          fwrite (&fsentry->u.file.offset, sizeof (uint32_t), 1, fp); // write offset
  677.          fwrite (&fsentry->u.file.size,   sizeof (uint32_t), 1, fp); // write size
  678.       }
  679.       count += 2 * sizeof (uint32_t);
  680.       datalen = strlen (fsentry->u.file.path) + 1;
  681.       if (fp != NULL)
  682.          fwrite (fsentry->u.file.path, (size_t) datalen, 1, fp); // write null-terminated path (no leading slash)
  683.       count += datalen;
  684.    }
  685.    else if (S_ISDIR (fsentry->header.mode))
  686.    {
  687.       datalen = strlen (fsentry->u.dir.path) + 1;
  688.       if (fp != NULL)
  689.          fwrite (fsentry->u.dir.path, (size_t) datalen, 1, fp); // write null-terminated path (no leading slash)
  690.       count += datalen;
  691.    }
  692.    else if (S_ISLNK (fsentry->header.mode))
  693.    {
  694.       if (fp != NULL)
  695.       {
  696.          fwrite (&fsentry->u.symlink.sym_offset, sizeof (uint16_t), 1, fp); // write offset
  697.          fwrite (&fsentry->u.symlink.sym_size,   sizeof (uint16_t), 1, fp); // write size
  698.       }
  699.       count += 2 * sizeof (uint16_t);
  700.       datalen = strlen (fsentry->u.symlink.path) + 1;
  701.       if (fp != NULL)
  702.          fwrite (fsentry->u.symlink.path, (size_t) datalen, 1, fp); // write null-terminated path (no leading slash)
  703.       count += datalen;
  704.       datalen = strlen (fsentry->u.symlink.contents) + 1;
  705.       if (fp != NULL)
  706.          fwrite (fsentry->u.symlink.contents, (size_t) datalen, 1, fp); // write null-terminated symlink contents
  707.       count += datalen;
  708.    }
  709.    else
  710.    {
  711.       if (fp != NULL)
  712.       {
  713.          fwrite (&fsentry->u.device.dev,  sizeof (uint32_t), 1, fp); // write dev number
  714.          fwrite (&fsentry->u.device.rdev, sizeof (uint32_t), 1, fp); // write rdev number
  715.       }
  716.       count += 2 * sizeof (uint32_t);
  717.       datalen = strlen (fsentry->u.device.path) + 1;
  718.       if (fp != NULL)
  719.          fwrite (fsentry->u.device.path, (size_t) datalen, 1, fp); // write null-terminated path (no leading slash)
  720.       count += datalen;
  721.    }
  722.  
  723.    if ((fp != NULL) && (count % image_align != 0))
  724.       fwrite (zeropad_buffer, count % image_align, 1, fp); // pad as necessary
  725.    count = ROUND_TO_UPPER_MULTIPLE (count, image_align);
  726.  
  727.    return (count);
  728. }
  729.  
  730.  
  731. static size_t add_fsentry (fsentry_t **fsentries, size_t *fsentry_count, const char *stored_pathname, parms_t *entry_parms, const uint8_t *entry_data, const size_t entry_datalen)
  732. {
  733.    static char candidate_pathname[1024];
  734.    static char *MKIFS_PATH = NULL;
  735.    static int inode_count = 0; // will be preincremented each time this function is called
  736.  
  737.    void *reallocated_ptr;
  738.    char processor_base[16];
  739.    struct stat stat_buf;
  740.    uint8_t *data_buffer = NULL;
  741.    size_t data_len = 0;
  742.    uint32_t mtime = 0;
  743.    uint32_t extra_ino_flags = 0;
  744.    fsentry_t *fsentry;
  745.    char *token;
  746.    FILE *fp;
  747.  
  748.    if (S_ISDIR (entry_parms->st_mode)) // are we storing a directory ?
  749.    {
  750.       fprintf (stderr, "directory: ino 0x%x uid %d gid %d mode 0%o path \"%s\"\n", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname);
  751.    }
  752.    else if (S_ISREG (entry_parms->st_mode)) // else are we storing a regular file ?
  753.    {
  754.       if ((entry_data != NULL) && (entry_datalen > 0)) // was an explicit contents blob supplied ?
  755.       {
  756.          if (strcmp (stored_pathname, "proc/boot/boot") == 0) // is it the kernel ?
  757.          {
  758.             // HACK: for now just consider the kernel as a binary blob
  759.             // FIXME: reimplement properly
  760.             if (stat ("procnto-smp-instr.bin", &stat_buf) != 0)
  761.             {
  762.                fprintf (stderr, "fatal error: unable to read procnto-smp-instr.bin\n");
  763.                exit (1);
  764.             }
  765.             data_len = stat_buf.st_size;
  766.             mtime = (uint32_t) time (NULL);
  767.             data_buffer = malloc (data_len);
  768.             if (data_buffer == NULL)
  769.             {
  770.                fprintf (stderr, "fatal error: out of memory\n");
  771.                exit (1);
  772.             }
  773.             fp = fopen ("procnto-smp-instr.bin", "rb");
  774.             fread (data_buffer, 1, data_len, fp);
  775.             fclose (fp);
  776.             sprintf (candidate_pathname, "%s/procnto-smp-instr", entry_parms->prefix);
  777.             stored_pathname = candidate_pathname;
  778.             extra_ino_flags = IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE;
  779.             image_kernel_ino = extra_ino_flags | (inode_count + 1);
  780.          }
  781.          else if (entry_parms->should_compile_contents_as_startup_script) // should we compile this contents as a startup script ?
  782.          {
  783.             // HACK: for now just use a precompiled script
  784.             // FIXME: replace this with a true compilation with the rules defined above
  785.             data_buffer = malloc (INITIAL_STARTUP_SCRIPT_LEN);
  786.             if (data_buffer == NULL)
  787.             {
  788.                fprintf (stderr, "fatal error: out of memory\n");
  789.                exit (1);
  790.             }
  791.             memcpy (data_buffer, INITIAL_STARTUP_SCRIPT, INITIAL_STARTUP_SCRIPT_LEN);
  792.             data_len = INITIAL_STARTUP_SCRIPT_LEN;
  793.             image_bootscript_ino = inode_count + 1; // save boot script inode number for image header
  794.          }
  795.          else // else it's an explicit blob of some other sort
  796.          {
  797.             data_len = entry_datalen;
  798.             mtime = (uint32_t) time (NULL);
  799.  
  800.             data_buffer = malloc (data_len);
  801.             if (data_buffer == NULL)
  802.             {
  803.                fprintf (stderr, "fatal error: out of memory\n");
  804.                exit (1);
  805.             }
  806.             memcpy (data_buffer, entry_data, data_len);
  807.          }
  808.  
  809.          fprintf (stderr, "file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" blob (len %zd)\n", extra_ino_flags | (inode_count + 1), entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, data_len);
  810.       }
  811.       else if ((entry_data != NULL) && (entry_data[0] != 0)) // else entry_datalen == 0, was some sort of pathname supplied ?
  812.       {
  813.          // is it an absolute pathname ?
  814.          if (IS_DIRSEP (entry_data[0])
  815.              || (isalpha (entry_data[0])
  816.                  && (entry_data[1] == ':')
  817.                  && IS_DIRSEP (entry_data[2])))
  818.          {
  819.             // in this case, it MUST exist at its designated location (either absolute or relative to the current working directory)
  820.             strcpy (candidate_pathname, entry_data);
  821.             if (stat (candidate_pathname, &stat_buf) != 0)
  822.             {
  823.                fprintf (stderr, "fatal error: filesystem entry \"%s\" specified in \"%s\" line %d not found on build host\n", entry_data, buildfile_pathname, lineno);
  824.                exit (1);
  825.             }
  826.          }
  827.          else // the path is relative, search it among the search paths we have
  828.          {
  829.             // is the search path NOT defined ?
  830.             if (entry_parms->search[0] == 0)
  831.             {
  832.                // initialize the default search path in MKIFS_PATH
  833.                if (MKIFS_PATH == NULL)
  834.                {
  835.                   MKIFS_PATH = getenv ("MKIFS_PATH"); // look in the environment first, and construct a default one if not supplied
  836.                   if (MKIFS_PATH == NULL)
  837.                   {
  838.                      strcpy (processor_base, image_processor); // construct PROCESSOR_BASE
  839.                      token = strchr (processor_base, '-');
  840.                      if (token != NULL)
  841.                         *token = 0; // split anything from the first dash onwards
  842.                      data_len = strlen (processor_base);
  843.                      if ((data_len > 2) && ((processor_base[data_len - 2] == 'b') || (processor_base[data_len - 2] == 'l')) && (processor_base[data_len - 1] == 'e'))
  844.                         processor_base[data_len - 2] = 0; // if it ends with "le" or "be", strip that too
  845.  
  846.                      MKIFS_PATH = malloc (10 * MAXPATHLEN); // construct a default MKIFS_PATH now
  847.                      if (MKIFS_PATH == NULL)
  848.                      {
  849.                         fprintf (stderr, "fatal error: out of memory\n");
  850.                         exit (1);
  851.                      }
  852.                      sprintf (MKIFS_PATH, ".|%s/%s/sbin|%s/%s/usr/sbin|%s/%s/boot/sys|%s/%s/boot/sys|%s/%s/bin|%s/%s/usr/bin|%s/%s/lib|%s/%s/lib/dll|%s/%s/usr/lib", // use a platform-agnostic character as path separator
  853.                                           QNX_TARGET, image_processor,
  854.                                           QNX_TARGET, image_processor,
  855.                                           QNX_TARGET, image_processor,
  856.                                           QNX_TARGET, processor_base,
  857.                                           QNX_TARGET, image_processor,
  858.                                           QNX_TARGET, image_processor,
  859.                                           QNX_TARGET, image_processor,
  860.                                           QNX_TARGET, image_processor,
  861.                                           QNX_TARGET, image_processor);
  862.                   }
  863.                } // at this point MKIFS_PATH is defined
  864.  
  865.                strcpy (entry_parms->search, MKIFS_PATH); // if entry search path is not defined, use the contents of MKIFS_PATH
  866.             }
  867.  
  868.             // convert path separators in the MKIFS_PATH environment variable into something platform-agnostic
  869.             for (token = entry_parms->search; *token != 0; token++)
  870.                if (*token == PATH_SEP)
  871.                   *token = '|';
  872.  
  873.             token = strtok (entry_parms->search, "|");
  874.             while (token != NULL)
  875.             {
  876.                sprintf (candidate_pathname, "%s/%s", token, entry_data);
  877.                if (stat (candidate_pathname, &stat_buf) == 0)
  878.                   break;
  879.                token = strtok (NULL, "|");
  880.             }
  881.             if (token == NULL)
  882.             {
  883.                fprintf (stderr, "fatal error: filesystem entry \"%s\" specified in \"%s\" line %d not found on build host\n", entry_data, buildfile_pathname, lineno);
  884.                exit (1);
  885.             }
  886.          }
  887.  
  888.          data_len = stat_buf.st_size;
  889.          mtime = (uint32_t) stat_buf.st_mtime;
  890.  
  891.          data_buffer = malloc (data_len);
  892.          if (data_buffer == NULL)
  893.          {
  894.             fprintf (stderr, "fatal error: out of memory\n");
  895.             exit (1);
  896.          }
  897.          fp = fopen (candidate_pathname, "rb");
  898.          fread (data_buffer, 1, data_len, fp);
  899.          fclose (fp);
  900.  
  901.          fprintf (stderr, "file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" buildhost_file \"%s\" (len %zd)\n", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_data, data_len);
  902.       }
  903.    }
  904.    else if (S_ISLNK (entry_parms->st_mode)) // else are we storing a symbolic link ?
  905.    {
  906.       data_len = strlen (entry_data);
  907.       mtime = (uint32_t) time (NULL);
  908.  
  909.       data_buffer = malloc (data_len + 1);
  910.       if (data_buffer == NULL)
  911.       {
  912.          fprintf (stderr, "fatal error: out of memory\n");
  913.          exit (1);
  914.       }
  915.       memcpy (data_buffer, entry_data, data_len + 1); // copy including null terminator
  916.  
  917.       fprintf (stderr, "symlink: ino 0x%x uid %d gid %d mode 0%o path \"%s\" -> \"%s\"\n", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, data_buffer);
  918.    }
  919.    else // we must be storing a FIFO
  920.    {
  921.       if (strchr (entry_data, ':') == NULL)
  922.       {
  923.          fprintf (stderr, "fatal error: device entry \"%s\" malformed (no 'dev:rdev' pair)\n", stored_pathname);
  924.          exit (1);
  925.       }
  926.  
  927.       fprintf (stderr, "fifo: ino 0x%x uid %d gid %d mode 0%o path \"%s\" dev rdev %s)\n", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_data);
  928.    }
  929.  
  930.    // reallocate filesystem entries array to hold one more slot
  931.    reallocated_ptr = realloc (*fsentries, (*fsentry_count + 1) * sizeof (fsentry_t)); // attempt to reallocate
  932.    if (reallocated_ptr == NULL)
  933.    {
  934.       fprintf (stderr, "fatal error: out of memory\n");
  935.       exit (1);
  936.    }
  937.    *fsentries = reallocated_ptr; // save reallocated pointer
  938.    fsentry = &(*fsentries)[*fsentry_count]; // quick access to fs entry slot
  939.    //fsentry->header.size = 0; // will be filled once we know it
  940.    fsentry->header.extattr_offset = 0;
  941.    fsentry->header.ino = extra_ino_flags | (++inode_count);
  942.    fsentry->header.mode = entry_parms->st_mode;
  943.    fsentry->header.gid = entry_parms->gid;
  944.    fsentry->header.uid = entry_parms->uid;
  945.    fsentry->header.mtime = mtime;
  946.    if (S_ISDIR (entry_parms->st_mode))
  947.    {
  948.       fsentry->u.dir.path = strdup (stored_pathname);
  949.       fsentry->header.size = (uint16_t) (sizeof (fsentry->header) + ROUND_TO_UPPER_MULTIPLE (strlen (fsentry->u.dir.path) + 1, image_align)); // now we can set the size
  950.       fsentry->UNSAVED_was_data_written = true; // no data to save
  951.    }
  952.    else if (S_ISREG (entry_parms->st_mode))
  953.    {
  954.       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
  955.       fsentry->u.file.size = (uint32_t) data_len;
  956.       fsentry->u.file.path = strdup (stored_pathname);
  957.       fsentry->u.file.UNSAVED_databuf = data_buffer;
  958.       fsentry->header.size = (uint16_t) (sizeof (fsentry->header) + ROUND_TO_UPPER_MULTIPLE (strlen (fsentry->u.file.path) + 1, image_align)); // now we can set the size
  959.       fsentry->UNSAVED_was_data_written = false; // there *IS* data to save
  960.    }
  961.    else if (S_ISLNK (entry_parms->st_mode))
  962.    {
  963.       fsentry->u.symlink.sym_offset = (uint16_t) (strlen (stored_pathname) + 1);
  964.       fsentry->u.symlink.sym_size = (uint16_t) data_len;
  965.       fsentry->u.symlink.path = strdup (stored_pathname);
  966.       fsentry->u.symlink.contents = data_buffer;
  967.       fsentry->header.size = (uint16_t) (sizeof (fsentry->header) + ROUND_TO_UPPER_MULTIPLE ((size_t) fsentry->u.symlink.sym_offset + fsentry->u.symlink.sym_size + 1, image_align)); // now we can set the size
  968.       fsentry->UNSAVED_was_data_written = true; // no data to save
  969.    }
  970.    else
  971.    {
  972.       fsentry->u.device.dev  = strtol (entry_data, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
  973.       fsentry->u.device.rdev = strtol (strchr (entry_data, ':') + 1, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
  974.       fsentry->u.device.path = strdup (stored_pathname);
  975.       fsentry->header.size = (uint16_t) (sizeof (fsentry->header) + ROUND_TO_UPPER_MULTIPLE (strlen (fsentry->u.device.path), image_align)); // now we can set the size
  976.       fsentry->UNSAVED_was_data_written = true; // no data to save
  977.    }
  978.    (*fsentry_count)++;
  979.    return (*fsentry_count);
  980. }
  981.  
  982.  
  983. static int fsentry_compare_pathnames_cb (const void *a, const void *b)
  984. {
  985.    // qsort() callback that compares two imagefs filesystem entries and sort them alphabetically by pathname
  986.  
  987.    fsentry_t *entry_a = (fsentry_t *) a;
  988.    fsentry_t *entry_b = (fsentry_t *) b;
  989.    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)));
  990.    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)));
  991.    return (strcmp (pathname_a, pathname_b));
  992. }
  993.  
  994.  
  995. static int fsentry_compare_sizes_cb (const void *a, const void *b)
  996. {
  997.    // qsort() callback that compares two imagefs filesystem entries and sort them alphabetically by pathname
  998.  
  999.    fsentry_t *entry_a = (fsentry_t *) a;
  1000.    fsentry_t *entry_b = (fsentry_t *) b;
  1001.    const int32_t size_a = (S_ISREG (entry_a->header.mode) ? entry_a->u.file.size : 0); // only files (i.e. entries wearing the S_IFREG flag) have a separate data block
  1002.    const int32_t size_b = (S_ISREG (entry_b->header.mode) ? entry_b->u.file.size : 0); // only files (i.e. entries wearing the S_IFREG flag) have a separate data block
  1003.    return ((int) size_b - (int) size_a);
  1004. }
  1005.  
  1006.  
  1007. int main (int argc, char **argv)
  1008. {
  1009.    // program entrypoint
  1010.  
  1011.    #define PAD_OUTFILE_TO(val) do { curr_offset = ftell (fp); while (curr_offset < (val)) { putc (0, fp); curr_offset++; } } while (0)
  1012.  
  1013.    startup_header_t startup_header = { 0 }; // output IFS's startup header
  1014.    startup_trailer_v2_t startup_trailer = { 0 }; // output IFS's startup trailer (version 2, with SHA-512 checksum and int32 checksum)
  1015.    image_header_t image_header = { 0 }; // output IFS's imagefs header
  1016.    image_trailer_v2_t image_trailer = { 0 }; // output IFS's imagefs trailer (version 2, with SHA-512 checksum and int32 checksum)
  1017.    fsentry_t *fsentries = NULL; // output IFS's filesystem entries
  1018.    size_t fsentry_count = 0; // number of entries in the IFS filesystem
  1019.    parms_t default_parms = { 0755, 0644, 0, 0, S_IFREG, "/proc/boot", false, "" }; // default parameters for a filesystem entry
  1020.    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)
  1021.  
  1022.    // bootable IFS support
  1023.    char *bootfile_pathname = NULL;           // HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS
  1024.    char *startupfile_pathname = NULL;        // HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS
  1025.    size_t startupfile_ep_from_imagebase = 0; // HACK: startup code entrypoint offset from image base for a bootable IFS
  1026.    char *kernelfile_pathname = NULL;         // HACK: pathname to precompiled kernel file blob to put in a bootable IFS
  1027.    size_t kernelfile_offset = 0;             // HACK: kernel file offset in bootable IFS
  1028.  
  1029.    char path_in_ifs[MAXPATHLEN];
  1030.    char image_bootfile[MAXPATHLEN];
  1031.    size_t image_bootfilesize = 0;
  1032.    char *ifs_pathname = NULL;
  1033.    void *reallocated_ptr;
  1034.    struct stat stat_buf;
  1035.    size_t startuptrailer_offset;
  1036.    size_t startupheader_offset;
  1037.    size_t imgtrailer_offset;
  1038.    size_t imgheader_offset;
  1039.    size_t imgdir_offset;
  1040.    size_t imgdir_size;
  1041.    size_t final_size;
  1042.    size_t fsentry_index;
  1043.    size_t curr_offset;
  1044.    size_t blob_datasize; // mallocated size
  1045.    size_t blob_datalen; // used size
  1046.    uint8_t *blob_data = NULL; // mallocated
  1047.    char *line_ptr;
  1048.    char *directiveblock_start;
  1049.    char *write_ptr;
  1050.    char *token;
  1051.    char *value;
  1052.    char *sep;
  1053.    //char *ctx;
  1054.    int arg_index;
  1055.    bool is_quoted_context = false;
  1056.    bool is_escaped_char = false;
  1057.    bool want_info = false;
  1058.    bool want_help = false;
  1059.    int string_len;
  1060.    int read_char;
  1061.    FILE *buildfile_fp;
  1062.    FILE *fp;
  1063.  
  1064.    // parse arguments
  1065.    for (arg_index = 1; arg_index < argc; arg_index++)
  1066.    {
  1067.       if ((strcmp (argv[arg_index], "--bootfile") == 0) && (arg_index + 1 < argc)) // --bootfile path/to/blob.bin
  1068.          bootfile_pathname = argv[++arg_index];
  1069.       else if ((strcmp (argv[arg_index], "--startupfile") == 0) && (arg_index + 1 < argc)) // --startupfile path/to/blob.bin@0x1030
  1070.       {
  1071.          sep = strchr (argv[++arg_index], '@');
  1072.          if ((sep == NULL) || (sep[1] == 0))
  1073.          {
  1074.             fprintf (stderr, "error: the --startupfile arguments expects <pathname>@<entrypoint_from_image_base>\n");
  1075.             exit (1);
  1076.          }
  1077.          *sep = 0;
  1078.          startupfile_pathname = argv[arg_index];
  1079.          startupfile_ep_from_imagebase = (size_t) read_integer (sep + 1);
  1080.       }
  1081.       else if ((strcmp (argv[arg_index], "--kernelfile") == 0) && (arg_index + 1 < argc)) // --kernelfile path/to/blob.bin@0x32000
  1082.       {
  1083.          sep = strchr (argv[++arg_index], '@');
  1084.          if ((sep == NULL) || (sep[1] == 0))
  1085.          {
  1086.             fprintf (stderr, "error: the --kernelfile arguments expects <pathname>@<fileoffset>\n");
  1087.             exit (1);
  1088.          }
  1089.          *sep = 0;
  1090.          kernelfile_pathname = argv[arg_index];
  1091.          kernelfile_offset = (size_t) read_integer (sep + 1);
  1092.       }
  1093.       else if (strcmp (argv[arg_index], "--info") == 0)
  1094.          want_info = true;
  1095.       else if ((strcmp (argv[arg_index], "-?") == 0) || (strcmp (argv[arg_index], "--help") == 0))
  1096.          want_help = true;
  1097.       else if (buildfile_pathname == NULL)
  1098.          buildfile_pathname = argv[arg_index];
  1099.       else if (ifs_pathname == NULL)
  1100.          ifs_pathname = argv[arg_index];
  1101.    }
  1102.  
  1103.    // do we not have enough information to run ?
  1104.    if (want_help || (ifs_pathname == NULL) || (!want_info && (buildfile_pathname == NULL)))
  1105.    {
  1106.       if (want_help)
  1107.          fprintf (stdout, "ifstool - QNX in-kernel filesystem creation utility by Pierre-Marie Baty <pm@pmbaty.com>\n");
  1108.       else
  1109.          fprintf (stderr, "error: missing parameters\n");
  1110.       fprintf ((want_help ? stdout : stderr), "usage:\n");
  1111.       fprintf ((want_help ? stdout : stderr), "    ifstool [--bootfile <pathname>] [--startupfile <pathname>@<EP_from_imgbase>] [--kernelfile <pathname>@<fileoffs>] <buildfile> <outfile>\n");
  1112.       fprintf ((want_help ? stdout : stderr), "    ifstool --info <ifs file>\n");
  1113.       fprintf ((want_help ? stdout : stderr), "    ifstool --help\n");
  1114.       exit (want_help ? 0 : 1);
  1115.    }
  1116.  
  1117.    // make sure we have ${QNX_TARGET} pointing somewhere
  1118.    QNX_TARGET = getenv ("QNX_TARGET");
  1119.    if (QNX_TARGET == NULL)
  1120.    {
  1121.       fprintf (stderr, "error: the QNX_TARGET environment variable is not set\n");
  1122.       exit (1);
  1123.    }
  1124.    else if (access (QNX_TARGET, 0) != 0)
  1125.    {
  1126.       fprintf (stderr, "error: the QNX_TARGET environment variable doesn't point to an existing directory\n");
  1127.       exit (1);
  1128.    }
  1129.  
  1130.    // open build file
  1131.    buildfile_fp = fopen (buildfile_pathname, "rb");
  1132.    if (buildfile_fp == NULL)
  1133.    {
  1134.       fprintf (stderr, "error: unable to open build file \"%s\" for reading (%s)\n", buildfile_pathname, strerror (errno));
  1135.       exit (1);
  1136.    }
  1137.  
  1138.    // stack up filesystem entries
  1139.    memcpy (&entry_parms, &default_parms, sizeof (default_parms));
  1140.    entry_parms.st_mode = S_IFDIR | default_parms.dperms;
  1141.    add_fsentry (&fsentries, &fsentry_count, "", &entry_parms, NULL, 0); // add the root dir first
  1142.  
  1143.    while (fgets (line_buffer, sizeof (line_buffer), buildfile_fp) != NULL)
  1144.    {
  1145.       lineno++; // keep track of current line number
  1146.       //fprintf (stderr, "read buildfile line %d: {%s}\n", lineno, line_buffer);
  1147.  
  1148.       line_ptr = line_buffer;
  1149.       while ((*line_ptr != 0) && isspace (*line_ptr))
  1150.          line_ptr++; // skip leading spaces
  1151.  
  1152.       if ((*line_ptr == 0) || (*line_ptr == '#'))
  1153.          continue; // skip empty or comment lines
  1154.  
  1155.       string_len = (int) strlen (line_buffer);
  1156.       if ((string_len > 0) && (line_buffer[string_len - 1] == '\n'))
  1157.          line_buffer[string_len - 1] = 0; // chop off newline for easier debug output
  1158.  
  1159.       // reset entry values
  1160.       memcpy (&entry_parms, &default_parms, sizeof (default_parms));
  1161.  
  1162.       //fprintf (stderr, "parsing buildfile line %d: [%s]\n", lineno, line_ptr);
  1163.  
  1164.       // does this line start with an attribute block ?
  1165.       if (*line_ptr == '[')
  1166.       {
  1167.          line_ptr++; // skip the leading square bracket
  1168.          directiveblock_start = line_ptr; // remember where it starts
  1169.          is_quoted_context = false;
  1170.          while ((*line_ptr != 0) && !((*line_ptr == ']') && (line_ptr[-1] != '\\')))
  1171.          {
  1172.             if (*line_ptr == '"')
  1173.                is_quoted_context ^= true; // remember when we're between quotes
  1174.             else if (!is_quoted_context && (*line_ptr == ' '))
  1175.                *line_ptr = RECORD_SEP; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
  1176.             line_ptr++; // reach the next unescaped closing square bracket
  1177.          }
  1178.          if (*line_ptr != ']')
  1179.          {
  1180.             fprintf (stderr, "warning: syntax error in \"%s\" line %d: unterminated attributes block (skipping)\n", buildfile_pathname, lineno);
  1181.             continue; // invalid attribute block, skip line
  1182.          }
  1183.          *line_ptr = 0; // end the attribute block so that it is a parsable C string
  1184.  
  1185.          // now parse the attribute tokens
  1186.          // DOCUMENTATION: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/m/mkifs.html#mkifs__description
  1187.          token = strtok (directiveblock_start, RECORD_SEP_STR);
  1188.          while (token != NULL)
  1189.          {
  1190.             // evaluate attribute token
  1191.             #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
  1192.             if      (strncmp (token, "uid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.uid     = (int) read_integer (value); }
  1193.             else if (strncmp (token, "gid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.gid     = (int) read_integer (value); }
  1194.             else if (strncmp (token, "dperms=",  7) == 0) { REACH_TOKEN_VALUE (); entry_parms.dperms  = (int) read_integer (value); }
  1195.             else if (strncmp (token, "perms=",   6) == 0) { REACH_TOKEN_VALUE (); entry_parms.perms   = (int) read_integer (value); }
  1196.             else if (strncmp (token, "type=",    5) == 0) { REACH_TOKEN_VALUE (); entry_parms.st_mode = (strcmp (value, "dir") == 0 ? S_IFDIR : (strcmp (value, "file") == 0 ? S_IFREG : (strcmp (value, "link") == 0 ? S_IFLNK : (strcmp (value, "fifo") == 0 ? S_IFIFO : (fprintf (stderr, "warning: invalid 'type' attribute in \"%s\" line %d: '%s', defaulting to 'file'\n", buildfile_pathname, lineno, value), S_IFREG))))); }
  1197.             else if (strncmp (token, "prefix=",  7) == 0) { REACH_TOKEN_VALUE (); strcpy (entry_parms.prefix, (*value == '/' ? value + 1 : value)); } // skip possible leading slash in prefix
  1198.             else if (strncmp (token, "image=",   6) == 0) { REACH_TOKEN_VALUE ();
  1199.                image_base = (uint32_t) read_integer (value); // read image base address
  1200.                if ((sep = strchr (value, '-')) != NULL) image_end       = (uint32_t) read_integer (sep + 1); // if we have a dash, read optional image end (FIXME: check this value and produce an error in the relevant case. Not important.)
  1201.                if ((sep = strchr (value, ',')) != NULL) image_maxsize   = (uint32_t) read_integer (sep + 1); // if we have a comma, read optional image max size
  1202.                if ((sep = strchr (value, '=')) != NULL) image_totalsize = (uint32_t) read_integer (sep + 1); // if we have an equal sign, read optional image padding size
  1203.                if ((sep = strchr (value, '%')) != NULL) image_align     = (uint32_t) read_integer (sep + 1); // if we have a modulo sign, read optional image aligmnent
  1204.                fprintf (stderr, "info: image 0x%x-0x%x maxsize %d totalsize %d align %d\n", image_base, image_end, image_maxsize, image_totalsize, image_align);
  1205.             }
  1206.             else if (strncmp (token, "virtual=", 8) == 0) { REACH_TOKEN_VALUE ();
  1207.                if ((bootfile_pathname == NULL) || (startupfile_pathname == NULL) || (kernelfile_pathname == NULL)) // HACK until I figure out how to re-create them
  1208.                {
  1209.                   fprintf (stderr, "error: creating bootable images require the --bootfile, --startupfile and --kernelfile command-line options in \"%s\" line %d\n", buildfile_pathname, lineno);
  1210.                   exit (1);
  1211.                }
  1212.                if ((sep = strchr (value, ',')) != NULL) // do we have a comma separating (optional) processor and boot file name ?
  1213.                {
  1214.                   *sep = 0;
  1215.                   strcpy (image_processor, value); // save processor
  1216.                   value = sep + 1;
  1217.                }
  1218.                //sprintf (image_bootfile, "%s/%s/boot/sys/%s.boot", QNX_TARGET, image_processor, value); // save preboot file name (FIXME: we should search in MKIFS_PATH instead of this. Not important.)
  1219.                strcpy (image_bootfile, bootfile_pathname); // FIXME: HACK
  1220.                if (stat (image_bootfile, &stat_buf) != 0)
  1221.                {
  1222.                   fprintf (stderr, "error: unable to stat the boot file \"%s\" specified in \"%s\" line %d: %s\n", image_bootfile, buildfile_pathname, lineno, strerror (errno));
  1223.                   exit (1);
  1224.                }
  1225.                image_bootfilesize = stat_buf.st_size; // save preboot file size
  1226.                fprintf (stderr, "info: processor \"%s\" bootfile \"%s\"\n", image_processor, image_bootfile);
  1227.             }
  1228.             else if (strcmp (token, "+script") == 0) entry_parms.should_compile_contents_as_startup_script = true;
  1229.             else fprintf (stderr, "warning: unimplemented attribute in \"%s\" line %d: '%s'\n", buildfile_pathname, lineno, token);
  1230.             #undef REACH_TOKEN_VALUE
  1231.  
  1232.             token = strtok (NULL, RECORD_SEP_STR); // proceed to next attribute token
  1233.          }
  1234.  
  1235.          line_ptr++; // reach the next character
  1236.          while ((*line_ptr != 0) && isspace (*line_ptr))
  1237.             line_ptr++; // skip leading spaces
  1238.  
  1239.          // are we at the end of the line ? if so, it means the attribute values that are set should become the default
  1240.          if ((*line_ptr == 0) || (*line_ptr == '#'))
  1241.          {
  1242.             #define APPLY_DEFAULT_ATTR_NUM(attr,descr,fmt) do { if (entry_parms.attr != default_parms.attr) { \
  1243.                   fprintf (stderr, "info: changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %d\n", default_parms.attr, entry_parms.attr, buildfile_pathname, lineno); \
  1244.                   default_parms.attr = entry_parms.attr; \
  1245.                } } while (0)
  1246.             #define APPLY_DEFAULT_ATTR_STR(attr,descr,fmt) do { if (strcmp (entry_parms.attr, default_parms.attr) != 0) { \
  1247.                   fprintf (stderr, "info: changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %d\n", default_parms.attr, entry_parms.attr, buildfile_pathname, lineno); \
  1248.                   strcpy (default_parms.attr, entry_parms.attr); \
  1249.                } } while (0)
  1250.             APPLY_DEFAULT_ATTR_NUM (dperms,  "directory permissions", "0%o");
  1251.             APPLY_DEFAULT_ATTR_NUM (perms,   "file permissions",      "0%o");
  1252.             APPLY_DEFAULT_ATTR_NUM (uid,     "owner ID",              "%d");
  1253.             APPLY_DEFAULT_ATTR_NUM (gid,     "group ID",              "%d");
  1254.             APPLY_DEFAULT_ATTR_NUM (st_mode, "inode type",            "0%o");
  1255.             APPLY_DEFAULT_ATTR_STR (prefix,  "prefix",                "\"%s\"");
  1256.             APPLY_DEFAULT_ATTR_NUM (should_compile_contents_as_startup_script, "compiled script state", "%d");
  1257.             #undef APPLY_DEFAULT_ATTR_STR
  1258.             #undef APPLY_DEFAULT_ATTR_NUM
  1259.             continue; // end of line reached, proceed to the next line
  1260.          }
  1261.          // end of attributes parsing
  1262.       } // end of "this line starts with an attributes block"
  1263.  
  1264.       // there's data in this line. We expect a filename in the IFS. Read it and unescape escaped characters
  1265.       string_len = sprintf (path_in_ifs, "%s", entry_parms.prefix);
  1266.       while ((string_len > 0) && (path_in_ifs[string_len - 1] == '/'))
  1267.          string_len--; // chop off any trailing slashes from prefix
  1268.       write_ptr = &path_in_ifs[string_len];
  1269.       *write_ptr++ = '/'; // add ONE trailing slash
  1270.       if (*line_ptr == '/')
  1271.       {
  1272.          fprintf (stderr, "warning: paths in the IFS file should not begin with a leading '/' in \"%s\" line %d\n", buildfile_pathname, lineno);
  1273.          line_ptr++; // consistency check: paths in the IFS should not begin with a '/'
  1274.       }
  1275.       while ((*line_ptr != 0) && (*line_ptr != '=') && !isspace (*line_ptr))
  1276.       {
  1277.          if (*line_ptr == '\\')
  1278.          {
  1279.             line_ptr++;
  1280.             *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
  1281.          }
  1282.          else
  1283.             *write_ptr++ = *line_ptr;
  1284.          line_ptr++;
  1285.       }
  1286.       *write_ptr = 0; // terminate the string
  1287.  
  1288.       // we reached a space OR an equal sign
  1289.       while ((*line_ptr != 0) && isspace (*line_ptr))
  1290.          line_ptr++; // skip optional spaces after the filename in the IFS
  1291.  
  1292.       blob_data = NULL;
  1293.       blob_datasize = 0;
  1294.       blob_datalen = 0;
  1295.  
  1296.       // do we have an equal sign ?
  1297.       if (*line_ptr == '=') // we must be creating either a directory or a file, do we have an equal sign ?
  1298.       {
  1299.          line_ptr++; // skip the equal sign
  1300.          while ((*line_ptr != 0) && isspace (*line_ptr))
  1301.             line_ptr++; // skip optional spaces after the equal sign
  1302.  
  1303.          if (*line_ptr == 0)
  1304.          {
  1305.             fprintf (stderr, "warning: syntax error in \"%s\" line %d: missing data specification after equal sign (skipping)\n", buildfile_pathname, lineno);
  1306.             continue; // invalid symlink specification, skip line
  1307.          }
  1308.  
  1309.          // read the host system's path, it may be either a path or a contents definition. Is it a content definition ?
  1310.          if (*line_ptr == '{')
  1311.          {
  1312.             line_ptr++; // skip the leading content definition
  1313.             is_escaped_char = false;
  1314.             for (;;)
  1315.             {
  1316.                read_char = fgetc (buildfile_fp);
  1317.                if (read_char == EOF)
  1318.                {
  1319.                   fprintf (stderr, "fatal error: syntax error in \"%s\" line %d: unterminated contents block (end of file reached)\n", buildfile_pathname, lineno);
  1320.                   exit (1); // invalid contents block
  1321.                }
  1322.                else if (read_char == '\\')
  1323.                   is_escaped_char = true; // remember the next char is escaped
  1324.                else if ((read_char == '}') && !is_escaped_char)
  1325.                   break; // found an unescaped closing bracked, stop parsing
  1326.                else
  1327.                {
  1328.                   is_escaped_char = false; // any other char, meaning the next one will not be escaped
  1329.                   if (blob_datalen == blob_datasize) // reallocate in 4 kb blocks
  1330.                   {
  1331.                      reallocated_ptr = realloc (blob_data, blob_datasize + 4096);
  1332.                      if (reallocated_ptr == NULL)
  1333.                      {
  1334.                         fprintf (stderr, "fatal error: out of memory\n");
  1335.                         exit (1);
  1336.                      }
  1337.                      blob_data = reallocated_ptr;
  1338.                      blob_datasize += 4096;
  1339.                   }
  1340.                   blob_data[blob_datalen++] = read_char;
  1341.                   if (read_char == '\n')
  1342.                      lineno++;
  1343.                }
  1344.             }
  1345.          }
  1346.          else // not a content definition between { brackets }, meaning it's a build host filesystem path
  1347.          {
  1348.             blob_data = line_ptr; // explicit pathname on build host
  1349.             blob_datalen = 0;
  1350.          }
  1351.       }
  1352.       else // no equal sign, meaning the file will have the same name on the build host filesystem
  1353.       {
  1354.          // consistency check: symlinks MUST have an equal sign
  1355.          if (entry_parms.st_mode == S_IFLNK)
  1356.          {
  1357.             fprintf (stderr, "warning: syntax error in \"%s\" line %d: missing equal sign and symlink target (skipping)\n", buildfile_pathname, lineno);
  1358.             continue; // invalid symlink specification, skip line
  1359.          }
  1360.  
  1361.          blob_data = &path_in_ifs[strlen (entry_parms.prefix) + 1]; // same pathname
  1362.          blob_datalen = 0;
  1363.       }
  1364.  
  1365.       // now add this entry to the image filesystem
  1366.       entry_parms.st_mode |= (S_ISDIR (entry_parms.st_mode) ? entry_parms.dperms : entry_parms.perms); // complete entry permissions
  1367.       add_fsentry (&fsentries, &fsentry_count, path_in_ifs, &entry_parms, blob_data, blob_datalen); // and add filesystem entry
  1368.       if (blob_datasize > 0)
  1369.          free (blob_data); // if blob data was allocated, free it
  1370.    }
  1371.  
  1372.    // sort the filesystem entries by pathname
  1373.    qsort (fsentries, fsentry_count, sizeof (fsentry_t), fsentry_compare_pathnames_cb);
  1374.  
  1375.    // calculate filesystem entries size
  1376.    imgdir_size = sizeof (image_header);
  1377.    for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
  1378.    {
  1379.       fprintf (stderr, "info: sorted entry: %s\n", (S_ISDIR (fsentries[fsentry_index].header.mode) ? fsentries[fsentry_index].u.dir.path : (S_ISREG (fsentries[fsentry_index].header.mode) ? fsentries[fsentry_index].u.file.path : (S_ISLNK (fsentries[fsentry_index].header.mode) ? fsentries[fsentry_index].u.symlink.path : fsentries[fsentry_index].u.device.path))));
  1380.       imgdir_size += fwrite_fsentry (&fsentries[fsentry_index], NULL);
  1381.    }
  1382.    fprintf (stderr, "info: image directory size: %zd (0x%zx)\n", imgdir_size, imgdir_size);
  1383.  
  1384.    // write IFS file
  1385.    fp = fopen (ifs_pathname, "wb");
  1386.    if (fp == NULL)
  1387.    {
  1388.       fprintf (stderr, "error: failed to open \"%s\" for writing (%s)\n", ifs_pathname, strerror (errno));
  1389.       exit (1);
  1390.    }
  1391.  
  1392.    // do we have a boot file ? if so, this is a bootable image
  1393.    if (image_bootfile[0] != 0)
  1394.    {
  1395.       // write boot prefix
  1396.       fwrite_filecontents (image_bootfile, fp);
  1397.       PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  1398.  
  1399.       startupheader_offset = ftell (fp); // save startup header offset
  1400.       memset (&startup_header, 0, sizeof (startup_header)); // prepare startup header
  1401.       memcpy (startup_header.signature, "\xeb\x7e\xff\x00", 4); // startup header signature, i.e. 0xff7eeb
  1402.       startup_header.version       = 1;
  1403.       startup_header.flags1        = STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2; // flags, 0x21 (STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2)
  1404.       startup_header.header_size   = sizeof (startup_header); // 256
  1405.       if (strcmp (image_processor, "x86_64") == 0)
  1406.          startup_header.machine = STARTUP_HDR_MACHINE_X86_64; // EM_X86_64
  1407.       else if (strcmp (image_processor, "aarch64le") == 0)
  1408.          startup_header.machine = STARTUP_HDR_MACHINE_AARCH64; // EM_AARCH64
  1409.       else
  1410.       {
  1411.          fprintf (stderr, "fatal error: unsupported processor type '%s' found in build file \"%s\"\n", image_processor, buildfile_pathname);
  1412.          exit (1);
  1413.       }
  1414.       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.*")
  1415.       startup_header.image_paddr   = image_base + (uint32_t) image_bootfilesize;            // F[IS] Physical address of image, here 0x01400f30 (appears in "Offset" column for "startup-header" which is the first entry/start of file)
  1416.       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)
  1417.       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)
  1418.       startup_header.startup_size  = WILL_BE_FILLED_LATER;                                  // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes
  1419.       startup_header.stored_size   = startup_header.ram_size;                               // [I ] Size of entire image, here 0x00cd6128 (same as ram_size)
  1420.       startup_header.imagefs_size  = WILL_BE_FILLED_LATER;                                  // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes
  1421.       startup_header.preboot_size  = (uint16_t) image_bootfilesize;                         // [I ] Size of loaded before header, here 0xf30 or 3888 bytes (size of "bios.boot" file))
  1422.       fwrite (&startup_header, sizeof (startup_header), 1, fp); // write startup header
  1423.       PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  1424.  
  1425.       // ######################################################################################################################################################################################################################################
  1426.       // # FIXME: figure out how to re-create it: linker call involved
  1427.       // # $ x86_64-pc-nto-qnx8.0.0-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
  1428.       // ######################################################################################################################################################################################################################################
  1429.       fwrite_filecontents (startupfile_pathname, fp); // write startup code from blob file
  1430.       PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  1431.  
  1432.       startuptrailer_offset = ftell (fp); // save startup trailer offset
  1433.       fwrite (&startup_trailer, sizeof (startup_trailer), 1, fp); // write startup trailer
  1434.       PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  1435.    }
  1436.  
  1437.    imgheader_offset = ftell (fp); // save image header offset
  1438.    memset (&image_header, 0, sizeof (image_header)); // prepare image header
  1439.    memcpy (&image_header.signature, "imagefs", 7); // image filesystem signature, i.e. "imagefs"
  1440.    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)
  1441.    image_header.image_size    = WILL_BE_FILLED_LATER; // size from header to end of trailer (here 0xca6fe0 or 13 266 912)
  1442.    image_header.hdr_dir_size  = (uint32_t) imgdir_size; // size from header to last dirent (here 0x12b8 or 4792)
  1443.    image_header.dir_offset    = sizeof (image_header); // offset from header to first dirent (here 0x5c or 92)
  1444.    image_header.boot_ino[0]   = image_kernel_ino; // inode of files for bootstrap p[ro?]g[ra?]ms (here 0xa0000002, 0, 0, 0)
  1445.    image_header.script_ino    = image_bootscript_ino; // inode of file for script (here 3)
  1446.    image_header.mountpoint[0] = '/'; // default mountpoint for image ("/" + "\0\0\0")
  1447.    fwrite (&image_header, sizeof (image_header), 1, fp); // write image header
  1448.    PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  1449.  
  1450.    // write empty image directory
  1451.    imgdir_offset = ftell (fp);
  1452.    for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
  1453.       fwrite_fsentry (&fsentries[fsentry_index], fp);
  1454.    PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  1455.  
  1456.    // is it a bootable image with a kernel file ?
  1457.    if ((image_bootfile[0] != 0) && (kernelfile_pathname != NULL))
  1458.    {
  1459.       // sort the filesystem entries by sizes
  1460.       qsort (fsentries, fsentry_count, sizeof (fsentry_t), fsentry_compare_sizes_cb);
  1461.  
  1462.       // write as many small files as we can before reaching the kernel offset
  1463.       for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
  1464.       {
  1465.          if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written)
  1466.             continue; // skip all entries that don't have a separate data block and those who were written already
  1467.          curr_offset = ftell (fp);
  1468.          if (curr_offset + fsentries[fsentry_index].u.file.size >= kernelfile_offset)
  1469.             break; // stop writing entries as soon as we reach the kernel file offset
  1470.          fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imgheader_offset); // save file data blob offset in file structure
  1471.          fwrite (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob
  1472.          PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  1473.          fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
  1474.       }
  1475.       PAD_OUTFILE_TO (kernelfile_offset); // reach the kernel offset
  1476.  
  1477.       // ######################################################################################################################################################################################################################################
  1478.       // # FIXME: figure out how to re-create it: linker call involved
  1479.       // # $ 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
  1480.       // ######################################################################################################################################################################################################################################
  1481.       fwrite_filecontents (kernelfile_pathname, fp); // write kernel from blob file
  1482.       PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  1483.    }
  1484.  
  1485.    // then write all the other files
  1486.    for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
  1487.    {
  1488.       if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written)
  1489.          continue; // skip all entries that don't have a separate data block and those who were written already
  1490.       curr_offset = ftell (fp);
  1491.       fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imgheader_offset); // save file data blob offset in file structure
  1492.       fwrite (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob
  1493.       PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  1494.       fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
  1495.    }
  1496.  
  1497.    // finally, write trailer (including empty checksum)
  1498.    imgtrailer_offset = ftell (fp); // save image trailer offset
  1499.    fwrite (&image_trailer, sizeof (image_trailer), 1, fp); // write image trailer
  1500.    PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  1501.  
  1502.    // if we need to pad it to a specific length, do so
  1503.    PAD_OUTFILE_TO (image_totalsize);
  1504.    final_size = ftell (fp);
  1505.  
  1506.    // see if we are past the image max size, in which case it's an error
  1507.    if (final_size > image_maxsize)
  1508.    {
  1509.       fprintf (stderr, "error: image file \"%s\" size %zd exceeds max size (%zd)\n", ifs_pathname, final_size, (size_t) image_maxsize);
  1510.       exit (1);
  1511.    }
  1512.  
  1513.    // do we have a boot file ? if so, this is a bootable image
  1514.    if (image_bootfile[0] == 0)
  1515.    {
  1516.       // rewrite startup header with final values
  1517.       fseek (fp, startupheader_offset, SEEK_SET);
  1518.       startup_header.startup_size = (uint32_t) (imgheader_offset - startupheader_offset); // size of startup header up to image header
  1519.       startup_header.imagefs_size = (uint32_t) (final_size - imgheader_offset); // size of uncompressed imagefs
  1520.       startup_header.ram_size = (uint32_t) final_size; // FIXME: this is necessarily a bit less, but should we really bother calculating the right size ?
  1521.       fwrite (&startup_header, sizeof (startup_header), 1, fp); // write startup header
  1522.  
  1523.       // compute SHA-512 checksum and V1 checksum of startup block
  1524.       blob_datasize = startuptrailer_offset - startupheader_offset;
  1525.       blob_data = malloc (blob_datasize);
  1526.       if (blob_data == NULL)
  1527.       {
  1528.          fprintf (stderr, "fatal error: out of memory\n");
  1529.          exit (1);
  1530.       }
  1531.       fseek (fp, startupheader_offset, SEEK_SET);
  1532.       fread (blob_data, 1, blob_datasize, fp);
  1533.       SHA512 (blob_data, blob_datasize, startup_trailer.sha512); // compute SHA512 checksum
  1534.       startup_trailer.cksum = 0; // compute old checksum
  1535.       startup_trailer.cksum = update_checksum_int32s (startup_trailer.cksum, (const int32_t *) blob_data, blob_datasize);
  1536.       startup_trailer.cksum = update_checksum_int32s (startup_trailer.cksum, (const int32_t *) startup_trailer.sha512, sizeof (startup_trailer.sha512));
  1537.       free (blob_data);
  1538.  
  1539.       // rewrite startup trailer with final values
  1540.       fseek (fp, startuptrailer_offset, SEEK_SET);
  1541.       fwrite (&startup_trailer, sizeof (startup_trailer), 1, fp); // write startup trailer
  1542.    }
  1543.  
  1544.    // rewrite image header with final values
  1545.    fseek (fp, imgheader_offset, SEEK_SET);
  1546.    image_header.image_size = (uint32_t) (final_size - imgheader_offset); // size of uncompressed imagefs
  1547.    fwrite (&image_header, sizeof (image_header), 1, fp); // write image header
  1548.  
  1549.    // rewrite image directory with final checksum values
  1550.    fseek (fp, imgdir_offset, SEEK_SET);
  1551.    for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
  1552.       fwrite_fsentry (&fsentries[fsentry_index], fp);
  1553.  
  1554.    // compute SHA-512 checksum and V1 checksum of image block
  1555.    blob_datasize = imgtrailer_offset - imgheader_offset;
  1556.    blob_data = malloc (blob_datasize);
  1557.    if (blob_data == NULL)
  1558.    {
  1559.       fprintf (stderr, "fatal error: out of memory\n");
  1560.       exit (1);
  1561.    }
  1562.    fseek (fp, imgheader_offset, SEEK_SET);
  1563.    fread (blob_data, 1, blob_datasize, fp);
  1564.    SHA512 (blob_data, blob_datasize, image_trailer.sha512); // compute SHA512 checksum
  1565.    image_trailer.cksum = 0; // compute old checksum
  1566.    image_trailer.cksum = update_checksum_int32s (image_trailer.cksum, (const int32_t *) blob_data, blob_datasize);
  1567.    image_trailer.cksum = update_checksum_int32s (image_trailer.cksum, (const int32_t *) image_trailer.sha512, sizeof (image_trailer.sha512));
  1568.    free (blob_data);
  1569.  
  1570.    // rewrite image trailer with final checksum values
  1571.    fseek (fp, imgtrailer_offset, SEEK_SET);
  1572.    fwrite (&image_trailer, sizeof (image_trailer), 1, fp); // write image trailer
  1573.  
  1574.    // finished, close IFS file and exit with a success code
  1575.    fclose (fp);
  1576.    fprintf (stdout, "Success\n");
  1577.    exit (0);
  1578. }
  1579.