Subversion Repositories QNX 8.QNX8 IFS tool

Rev

Rev 34 | Rev 38 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 34 Rev 35
Line 166... Line 166...
166
static char *bootfile_pathname = NULL;           // FIXME: HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS in BIOS mode
166
static char *bootfile_pathname = NULL;           // FIXME: HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS in BIOS mode
167
static size_t bootfile_size = 0;                 // FIXME: HACK: size of the bootcode binary blob file to put at the start of a bootable IFS in BIOS mode
167
static size_t bootfile_size = 0;                 // FIXME: HACK: size of the bootcode binary blob file to put at the start of a bootable IFS in BIOS mode
168
static char *startupfile_pathname = NULL;        // FIXME: HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS
168
static char *startupfile_pathname = NULL;        // FIXME: HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS
169
static size_t startupfile_ep_from_imagebase = 0; // FIXME: HACK: startup code entrypoint offset from image base for a bootable IFS 
169
static size_t startupfile_ep_from_imagebase = 0; // FIXME: HACK: startup code entrypoint offset from image base for a bootable IFS 
170
static size_t kernelfile_offset = 0;             // kernel file offset in the IFS (first offset rounded at pagesize after the dirents table)
170
static size_t kernelfile_offset = 0;             // kernel file offset in the IFS (first offset rounded at pagesize after the dirents table)
-
 
171
static size_t procnto_bootargs_offset = 0;       // offset in the procnto file to the boot args structure, so that it can be patched late
171
 
172
 
172
 
173
 
173
// exported function prototypes
174
// exported function prototypes
174
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
175
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
175
 
176
 
Line 883... Line 884...
883
      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);
884
      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);
884
   }
885
   }
885
   else // necessarily a regular file (either S_IFREG is specified, or st_mode is zero)
886
   else // necessarily a regular file (either S_IFREG is specified, or st_mode is zero)
886
   {
887
   {
887
      entry_parms->st_mode |= S_IFREG; // make this explicit
888
      entry_parms->st_mode |= S_IFREG; // make this explicit
-
 
889
      entry_parms->mtime = entry_parms->mtime_for_inline_files; // set a default mtime equal to the mtime to use for inline files until told otherwise
888
 
890
 
-
 
891
      ASSERT ((entry_parms->data.bytes != NULL) || (buildhost_pathname != NULL), "unexpected code path: can't store a file without neither explicit contents nor a host pathname. This is a bug in the program. Please contact the author.");
-
 
892
 
-
 
893
      // do we NOT know the file data yet AND was a build host pathname specified ? which means we need to resolve the file on the build host's filesystem
-
 
894
      if ((entry_parms->data.bytes == NULL) && (buildhost_pathname != NULL))
-
 
895
      {
-
 
896
         resolved_pathname = resolve_pathname (buildhost_pathname, entry_parms->search); // locate the file
-
 
897
         if (resolved_pathname == NULL)
-
 
898
         {
-
 
899
            if (entry_parms->should_allow_nonexistent_files)
-
 
900
            {
-
 
901
               LOG_WARNING ("filesystem entry \"%s\" specified in \"%s\" line %d not found on build host: ignoring", buildhost_pathname, buildfile_pathname, lineno);
-
 
902
               return; // if we're allowed to continue when a file to add doesn't exist, do so, else die with an error message
-
 
903
            }
-
 
904
            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));
-
 
905
         }
-
 
906
         if (!Buffer_ReadFromFile (&entry_parms->data, resolved_pathname))
-
 
907
            DIE_WITH_EXITCODE (1, "filesystem entry \"%s\" specified in \"%s\" line %d can't be read from \"%s\": %s", buildhost_pathname, buildfile_pathname, lineno, resolved_pathname, strerror (errno));
-
 
908
         stat (resolved_pathname, &stat_buf); // can't fail, since we could read it
-
 
909
         if (entry_parms->mtime == UINT32_MAX)
-
 
910
            entry_parms->mtime = (uint32_t) stat_buf.st_mtime; // now we know which mtime to set this file
-
 
911
      }
-
 
912
 
-
 
913
      // is it the bootstrap file [startup=], or a "compiled" bootscript [+script] ?
889
      if (entry_parms->is_bootstrap_file) // is it the bootstrap file ?
914
      if (entry_parms->is_bootstrap_file) // [startup=...]
890
      {
915
      {
891
         // parse each line of contents
916
         // parse each line of contents
892
         ASSERT (entry_parms->data.size > 0, "kernel specification without inline contents");
917
         ASSERT (entry_parms->data.size > 0, "kernel specification without inline contents");
893
 
918
 
894
         // parse buffer (non-destructively) line after line
919
         // parse buffer (non-destructively) line after line
Line 1100... Line 1125...
1100
            fputc ('\n', stderr);
1125
            fputc ('\n', stderr);
1101
         }
1126
         }
1102
#ifdef _WIN32
1127
#ifdef _WIN32
1103
         _spawnv (_P_WAIT, linker_pathname, linker_argv.args); // spawn the linker and produce a stripped procnto (wait for completion)
1128
         _spawnv (_P_WAIT, linker_pathname, linker_argv.args); // spawn the linker and produce a stripped procnto (wait for completion)
1104
#else // !_WIN32, thus POSIX
1129
#else // !_WIN32, thus POSIX
1105
         do {
1130
         do { // QNX does have spawnv(), but Linux does not. So let's stick to common POSIX ground, i.e. fork/exec/wait.
1106
            int status;
1131
            int status;
1107
            pid_t pid = fork (); // duplicate ourselves so as to create a new process
1132
            pid_t pid = fork (); // duplicate ourselves so as to create a new process
1108
            ASSERT_WITH_ERRNO (pid != -1);
1133
            ASSERT_WITH_ERRNO (pid != -1);
1109
            if (pid == 0) // we are the child
1134
            if (pid == 0) // we are the child
1110
            {
1135
            {
Line 1118... Line 1143...
1118
         STRINGARRAY_FREE (&linker_argv);
1143
         STRINGARRAY_FREE (&linker_argv);
1119
         if (!Buffer_ReadFromFile (&entry_parms->data, procnto_sym_filename)) // load the output file
1144
         if (!Buffer_ReadFromFile (&entry_parms->data, procnto_sym_filename)) // load the output file
1120
            DIE_WITH_EXITCODE (1, "the host cross-linker failed to produce a readable stripped \"%s\" kernel: %s", procnto_sym_filename, strerror (errno));
1145
            DIE_WITH_EXITCODE (1, "the host cross-linker failed to produce a readable stripped \"%s\" kernel: %s", procnto_sym_filename, strerror (errno));
1121
         if (!entry_parms->should_keep_ld_output)
1146
         if (!entry_parms->should_keep_ld_output)
1122
            unlink (procnto_sym_filename); // remove the linker output file if we want to
1147
            unlink (procnto_sym_filename); // remove the linker output file if we want to
-
 
1148
 
-
 
1149
         // strip this prelinked ELF kernel file from all the sections we don't need
-
 
1150
         ASSERT_WITH_ERRNO (Buffer_StripELFFile (&entry_parms->data, (const char **) saved_ELF_sections, 1, true, stored_pathname)); // strip the ELF file as per QNX docs (only keep ONE section, which is "QNX_info", and align the segment size in file with the size it occupies in memory)
1123
 
1151
 
1124
         // save the boot arguments. The magic to look for is "ddpvbskr" -- whatever that means
1152
         // save the boot arguments. The magic to look for is "ddpvbskr" -- whatever that means
1125
         if ((bootargs_location = Buffer_FindFirstByteArray (&entry_parms->data, "ddpvbskr")) == NULL)
1153
         if ((bootargs_location = Buffer_FindFirstByteArray (&entry_parms->data, "ddpvbskr")) == NULL)
1126
            DIE_WITH_EXITCODE (1, "unable to find boot args location in the stripped \"%s\" kernel", stored_pathname);
1154
            DIE_WITH_EXITCODE (1, "unable to find boot args location in the stripped \"%s\" kernel", stored_pathname);
1127
         Buffer_InitWithSize (&bootargs_buffer, sizeof (bootargs_entry_t)); // prepare a boot args entry
1155
         Buffer_InitWithSize (&bootargs_buffer, sizeof (bootargs_entry_t)); // prepare a boot args entry
1128
         ((bootargs_entry_t *) bootargs_buffer.bytes)->argc = (uint8_t) procnto_argv.count;
1156
         ((bootargs_entry_t *) bootargs_buffer.bytes)->argc = (uint8_t) procnto_argv.count;
1129
         ((bootargs_entry_t *) bootargs_buffer.bytes)->envc = (uint8_t) (global_envp.count + procnto_envp.count);
1157
         ((bootargs_entry_t *) bootargs_buffer.bytes)->envc = (uint8_t) (global_envp.count + procnto_envp.count);
1130
         ((bootargs_entry_t *) bootargs_buffer.bytes)->shdr_addr = (uint32_t) (image_base + bootfile_size); // same value as startup_header.image_paddr (which is not set yet) (TODO: support 64-bit shdr_addr offsets -- see comment in bootargs_entry_t struct)
1158
         ((bootargs_entry_t *) bootargs_buffer.bytes)->shdr_addr = WILL_BE_FILLED_LATER; // same value as startup_header.image_paddr (which is not set yet) (TODO: support 64-bit shdr_addr offsets -- see comment in bootargs_entry_t struct)
1131
         for (array_index = 0; array_index < procnto_argv.count; array_index++)
1159
         for (array_index = 0; array_index < procnto_argv.count; array_index++)
1132
            ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, procnto_argv.args[array_index], strlen (procnto_argv.args[array_index]) + 1)); // append string including NUL terminator
1160
            ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, procnto_argv.args[array_index], strlen (procnto_argv.args[array_index]) + 1)); // append string including NUL terminator
1133
         for (array_index = 0; array_index < global_envp.count; array_index++)
1161
         for (array_index = 0; array_index < global_envp.count; array_index++)
1134
            ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, global_envp.args[array_index], strlen (global_envp.args[array_index]) + 1)); // append string including NUL terminator
1162
            ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, global_envp.args[array_index], strlen (global_envp.args[array_index]) + 1)); // append string including NUL terminator
1135
         for (array_index = 0; array_index < procnto_envp.count; array_index++)
1163
         for (array_index = 0; array_index < procnto_envp.count; array_index++)
1136
            ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, procnto_envp.args[array_index], strlen (procnto_envp.args[array_index]) + 1)); // append string including NUL terminator
1164
            ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, procnto_envp.args[array_index], strlen (procnto_envp.args[array_index]) + 1)); // append string including NUL terminator
1137
         ((bootargs_entry_t *) bootargs_buffer.bytes)->size_hi = (uint8_t) ((bootargs_buffer.size >> 8) & 0xff);
1165
         ((bootargs_entry_t *) bootargs_buffer.bytes)->size_hi = (uint8_t) ((bootargs_buffer.size >> 8) & 0xff);
1138
         ((bootargs_entry_t *) bootargs_buffer.bytes)->size_lo = (uint8_t) ((bootargs_buffer.size >> 0) & 0xff);
1166
         ((bootargs_entry_t *) bootargs_buffer.bytes)->size_lo = (uint8_t) ((bootargs_buffer.size >> 0) & 0xff);
-
 
1167
         procnto_bootargs_offset = (size_t) bootargs_location - (size_t) entry_parms->data.bytes; // save the boot args offset so that the section header address in it can be patched late
1139
         ASSERT_WITH_ERRNO (Buffer_WriteBufferAt (&entry_parms->data, (size_t) bootargs_location - (size_t) entry_parms->data.bytes, &bootargs_buffer));
1168
         ASSERT_WITH_ERRNO (Buffer_WriteBufferAt (&entry_parms->data, procnto_bootargs_offset, &bootargs_buffer));
1140
         Buffer_Forget (&bootargs_buffer); // release the boot args buffer once it's written
1169
         Buffer_Forget (&bootargs_buffer); // release the boot args buffer once it's written
1141
 
-
 
1142
         // now strip this prelinked ELF kernel file
-
 
1143
         ASSERT_WITH_ERRNO (Buffer_StripELFFile (&entry_parms->data, (const char **) saved_ELF_sections, 1, true, stored_pathname)); // strip the ELF file as per QNX docs (only keep ONE section, which is "QNX_info", and align the segment size in file with the size it occupies in memory)
-
 
1144
 
1170
 
1145
         sprintf_s (candidate_pathname, MAXPATHLEN, "%s/%s", (entry_parms->prefix != NULL ? entry_parms->prefix : ""), procnto_argv.args[0]); // fix the entry name
1171
         sprintf_s (candidate_pathname, MAXPATHLEN, "%s/%s", (entry_parms->prefix != NULL ? entry_parms->prefix : ""), procnto_argv.args[0]); // fix the entry name
1146
         stored_pathname = candidate_pathname;
1172
         stored_pathname = candidate_pathname;
1147
 
1173
 
1148
         entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE; // mark this inode as a preprocessed *bootstrap* ELF file
1174
         entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE; // mark this inode as a preprocessed *bootstrap* ELF file
Line 1151... Line 1177...
1151
 
1177
 
1152
         STRINGARRAY_FREE (&procnto_argv); // release procnto's argv array
1178
         STRINGARRAY_FREE (&procnto_argv); // release procnto's argv array
1153
         STRINGARRAY_FREE (&procnto_envp); // release procnto's envp array
1179
         STRINGARRAY_FREE (&procnto_envp); // release procnto's envp array
1154
         //STRINGARRAY_FREE (&global_envp); // DO NOT release the global envp array. It is inherited by the boot scripts.
1180
         //STRINGARRAY_FREE (&global_envp); // DO NOT release the global envp array. It is inherited by the boot scripts.
1155
      } // end of "is bootstrap file"
1181
      } // end of "is bootstrap file"
1156
      else if (entry_parms->is_compiled_bootscript) // else is it a startup script that we need to compile ?
1182
      else if (entry_parms->is_compiled_bootscript) // [+script]
1157
      {
1183
      {
1158
         image_bootscript_ino = inode_count + 1; // save boot script inode number for image header
1184
         image_bootscript_ino = inode_count + 1; // save boot script inode number for image header
1159
         Buffer_Initialize (&compiled_script);
1185
         Buffer_Initialize (&compiled_script);
1160
 
1186
 
1161
         // parse buffer (non-destructively) line after line
1187
         // parse buffer (non-destructively) line after line
Line 1423... Line 1449...
1423
         ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&compiled_script, "\x00\x00\x00\x00")); // terminate the compiled boot script with a 4-byte trailer
1449
         ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&compiled_script, "\x00\x00\x00\x00")); // terminate the compiled boot script with a 4-byte trailer
1424
         entry_parms->data.bytes = compiled_script.bytes; // and steal the compiled boot script buffer
1450
         entry_parms->data.bytes = compiled_script.bytes; // and steal the compiled boot script buffer
1425
         entry_parms->data.size = compiled_script.size;
1451
         entry_parms->data.size = compiled_script.size;
1426
      } // end of "is compiled bootscript"
1452
      } // end of "is compiled bootscript"
1427
 
1453
 
1428
      // do we already know the data for this data blob ?
-
 
1429
      if (entry_parms->data.bytes != NULL)
-
 
1430
      {
-
 
1431
         entry_parms->mtime = entry_parms->mtime_for_inline_files; // if so, set it a mtime equal to the mtime to use for inline files
-
 
1432
         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);
-
 
1433
      }
-
 
1434
      else if (buildhost_pathname != NULL) // else was a source file pathname supplied ?
-
 
1435
      {
-
 
1436
         resolved_pathname = resolve_pathname (buildhost_pathname, entry_parms->search); // locate the file
-
 
1437
         if (resolved_pathname == NULL)
-
 
1438
         {
-
 
1439
            if (entry_parms->should_allow_nonexistent_files)
-
 
1440
            {
-
 
1441
               LOG_WARNING ("filesystem entry \"%s\" specified in \"%s\" line %d not found on build host: ignoring", buildhost_pathname, buildfile_pathname, lineno);
-
 
1442
               return; // if we're allowed to continue when a file to add doesn't exist, do so, else die with an error message
-
 
1443
            }
-
 
1444
            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));
-
 
1445
         }
-
 
1446
         if (!Buffer_ReadFromFile (&entry_parms->data, resolved_pathname))
-
 
1447
            DIE_WITH_EXITCODE (1, "filesystem entry \"%s\" specified in \"%s\" line %d can't be read from \"%s\": %s", buildhost_pathname, buildfile_pathname, lineno, resolved_pathname, strerror (errno));
-
 
1448
         stat (resolved_pathname, &stat_buf); // can't fail, since we could read it
-
 
1449
         if (entry_parms->mtime == UINT32_MAX)
-
 
1450
            entry_parms->mtime = (uint32_t) stat_buf.st_mtime;
-
 
1451
         if ((entry_parms->data.size > 52) && (memcmp (entry_parms->data.bytes, ELF_MAGIC_STR, 4) == 0))
-
 
1452
            entry_parms->st_mode |= 0111; // add +x permissions to ELF entries (undocumented mkifs behaviour)
-
 
1453
         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);
1454
      LOG_INFO ("file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" buildhost_file \"%s\" (len %zd)", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, (buildhost_pathname != NULL ? buildhost_pathname : "<explicit blob>"), entry_parms->data.size);
1454
      }
-
 
1455
      else
-
 
1456
         DIE_WITH_EXITCODE (1, "unexpected code path: can't store a file without neither explicit contents nor a host pathname. This is a bug in the program. Please contact the author.");
-
 
1457
 
1455
 
1458
      // is the file we're storing an ELF file ?
1456
      // is the file we're storing an ELF file ?
1459
      #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
1457
      #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
1460
      if ((entry_parms->data.size > 52) // file is big enough to contain an ELF header
1458
      if ((entry_parms->data.size > 52) // file is big enough to contain an ELF header
1461
          && (memcmp (ELF_GET_STRING (ELFHDR, ELFHDR, magic), ELF_MAGIC_STR, 4) == 0)) // file starts with the ELF magic
1459
          && (memcmp (ELF_GET_STRING (ELFHDR, ELFHDR, magic), ELF_MAGIC_STR, 4) == 0)) // file starts with the ELF magic
1462
      {
1460
      {
-
 
1461
         if ((entry_parms->st_mode & 0111) == 0)
-
 
1462
            entry_parms->st_mode |= 0111; // add +x permissions to ELF entries if they have none (undocumented mkifs behaviour)
-
 
1463
 
1463
         // is the file we're storing a relocatable executable (i.e. a dynamic library) and should we check for its canonical name ?
1464
         // is the file we're storing a relocatable executable (i.e. a dynamic library) and should we check for its canonical name ?
1464
         if ((ELF_GET_NUMERIC (ELFHDR, ELFHDR, type) == ELF_TYPE_DYNAMICLIB) && entry_parms->should_autosymlink_dylib)
1465
         if ((ELF_GET_NUMERIC (ELFHDR, ELFHDR, type) == ELF_TYPE_DYNAMICLIB) && entry_parms->should_autosymlink_dylib)
1465
         {
1466
         {
1466
            // locate the sections we need (the dynamic section and its strings table)
1467
            // locate the sections we need (the dynamic section and its strings table)
1467
            const elf_section_header_t *shdr_dynamic = elf_get_section_header_by_name (ELFHDR, ".dynamic");
1468
            const elf_section_header_t *shdr_dynamic = elf_get_section_header_by_name (ELFHDR, ".dynamic");
Line 2557... Line 2558...
2557
      // now write the QNX kernel
2558
      // now write the QNX kernel
2558
      for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
2559
      for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
2559
         if (fsentries[fsentry_index].header.ino == image_kernel_ino)
2560
         if (fsentries[fsentry_index].header.ino == image_kernel_ino)
2560
            break; // locate the kernel directory entry (can't fail)
2561
            break; // locate the kernel directory entry (can't fail)
2561
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
2562
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
-
 
2563
      ASSERT (procnto_bootargs_offset + sizeof (bootargs_entry_t) < fsentries[fsentry_index].u.file.size, "can't fix boot args in procnto, would write beyond the end of file! This is a bug in the program. Please contact the author.");
-
 
2564
      ((bootargs_entry_t *) &fsentries[fsentry_index].UNSAVED_databuf[procnto_bootargs_offset])->shdr_addr = (uint32_t) (image_base + bootfile_size); // fix shdr_addr in procnto's arguments structure, same value as startup_header.image_paddr (which is not set yet) (TODO: support 64-bit shdr_addr offsets -- see comment in bootargs_entry_t struct)
2562
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write kernel file data
2565
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write kernel file data
2563
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2566
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2564
   }
2567
   }
2565
 
2568
 
2566
   // then write all the other files by increasing inode number: ELF files first
2569
   // then write all the other files by increasing inode number: ELF files first