1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-22 18:26:39 +00:00

romfs: slightly improve interface.

This commit is contained in:
Pablo Curiel 2022-07-06 11:57:31 +02:00
parent c910fe6c0a
commit d8c0984115
6 changed files with 270 additions and 97 deletions

View file

@ -99,14 +99,16 @@ static void read_thread_func(void *arg)
goto end; goto end;
} }
u64 file_table_offset = 0;
u64 file_table_size = shared_data->romfs_ctx->file_table_size;
RomFileSystemFileEntry *file_entry = NULL; RomFileSystemFileEntry *file_entry = NULL;
char path[FS_MAX_PATH] = {0}; char path[FS_MAX_PATH] = {0};
sprintf(path, "sdmc:/romfs"); sprintf(path, "sdmc:/romfs");
while(file_table_offset < file_table_size) /* Reset current file table offset. */
romfsResetFileTableOffset(shared_data->romfs_ctx);
/* Loop through all file entries. */
while(romfsCanMoveToNextFileEntry(shared_data->romfs_ctx))
{ {
/* Check if the transfer has been cancelled by the user. */ /* Check if the transfer has been cancelled by the user. */
if (shared_data->transfer_cancelled) if (shared_data->transfer_cancelled)
@ -130,7 +132,7 @@ static void read_thread_func(void *arg)
} }
/* Retrieve RomFS file entry information. */ /* Retrieve RomFS file entry information. */
shared_data->read_error = (!(file_entry = romfsGetFileEntryByOffset(shared_data->romfs_ctx, file_table_offset)) || \ shared_data->read_error = (!(file_entry = romfsGetCurrentFileEntry(shared_data->romfs_ctx)) || \
!romfsGeneratePathFromFileEntry(shared_data->romfs_ctx, file_entry, path + 11, FS_MAX_PATH - 11, RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly)); !romfsGeneratePathFromFileEntry(shared_data->romfs_ctx, file_entry, path + 11, FS_MAX_PATH - 11, RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly));
if (shared_data->read_error) if (shared_data->read_error)
{ {
@ -197,7 +199,13 @@ static void read_thread_func(void *arg)
if (shared_data->read_error || shared_data->write_error || shared_data->transfer_cancelled) break; if (shared_data->read_error || shared_data->write_error || shared_data->transfer_cancelled) break;
file_table_offset += ALIGN_UP(sizeof(RomFileSystemFileEntry) + file_entry->name_length, 4); /* Move to the next file entry. */
shared_data->read_error = !romfsMoveToNextFileEntry(shared_data->romfs_ctx);
if (shared_data->read_error)
{
condvarWakeAll(&g_writeCondvar);
break;
}
} }
/* Wait until the previous file data chunk has been written. */ /* Wait until the previous file data chunk has been written. */

View file

@ -160,7 +160,6 @@ static void dumpRomFs(TitleInfo *info, NcaFsSectionContext *nca_fs_ctx)
{ {
if (!buf || !info || !nca_fs_ctx) return; if (!buf || !info || !nca_fs_ctx) return;
u64 romfs_file_table_offset = 0;
RomFileSystemContext romfs_ctx = {0}; RomFileSystemContext romfs_ctx = {0};
RomFileSystemFileEntry *romfs_file_entry = NULL; RomFileSystemFileEntry *romfs_file_entry = NULL;
@ -178,12 +177,12 @@ static void dumpRomFs(TitleInfo *info, NcaFsSectionContext *nca_fs_ctx)
utilsCreateDirectoryTree(path, true); utilsCreateDirectoryTree(path, true);
path_len = strlen(path); path_len = strlen(path);
while(romfs_file_table_offset < romfs_ctx.file_table_size) while(romfsCanMoveToNextFileEntry(&romfs_ctx))
{ {
if (!(romfs_file_entry = romfsGetFileEntryByOffset(&romfs_ctx, romfs_file_table_offset)) || \ if (!(romfs_file_entry = romfsGetCurrentFileEntry(&romfs_ctx)) || \
!romfsGeneratePathFromFileEntry(&romfs_ctx, romfs_file_entry, path + path_len, sizeof(path) - path_len, RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly)) !romfsGeneratePathFromFileEntry(&romfs_ctx, romfs_file_entry, path + path_len, sizeof(path) - path_len, RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly))
{ {
consolePrint("romfs get entry / generate path failed for 0x%lX!\n", romfs_file_table_offset); consolePrint("romfs get entry / generate path failed for 0x%lX!\n", romfs_ctx.cur_file_offset);
goto end; goto end;
} }
@ -215,7 +214,11 @@ static void dumpRomFs(TitleInfo *info, NcaFsSectionContext *nca_fs_ctx)
fclose(filefd); fclose(filefd);
filefd = NULL; filefd = NULL;
romfs_file_table_offset += ALIGN_UP(sizeof(RomFileSystemFileEntry) + romfs_file_entry->name_length, 4); if (!romfsMoveToNextFileEntry(&romfs_ctx))
{
consolePrint("failed to move to next file entry!\n");
goto end;
}
} }
consolePrint("romfs dump complete\n"); consolePrint("romfs dump complete\n");

View file

@ -100,12 +100,14 @@ static void read_thread_func(void *arg)
goto end; goto end;
} }
u64 file_table_offset = 0;
u64 file_table_size = shared_data->romfs_ctx->file_table_size;
RomFileSystemFileEntry *file_entry = NULL; RomFileSystemFileEntry *file_entry = NULL;
char path[FS_MAX_PATH] = {0}; char path[FS_MAX_PATH] = {0};
while(file_table_offset < file_table_size) /* Reset current file table offset. */
romfsResetFileTableOffset(shared_data->romfs_ctx);
/* Loop through all file entries. */
while(romfsCanMoveToNextFileEntry(shared_data->romfs_ctx))
{ {
/* Check if the transfer has been cancelled by the user */ /* Check if the transfer has been cancelled by the user */
if (shared_data->transfer_cancelled) if (shared_data->transfer_cancelled)
@ -115,7 +117,7 @@ static void read_thread_func(void *arg)
} }
/* Retrieve RomFS file entry information */ /* Retrieve RomFS file entry information */
shared_data->read_error = (!(file_entry = romfsGetFileEntryByOffset(shared_data->romfs_ctx, file_table_offset)) || \ shared_data->read_error = (!(file_entry = romfsGetCurrentFileEntry(shared_data->romfs_ctx)) || \
!romfsGeneratePathFromFileEntry(shared_data->romfs_ctx, file_entry, path, FS_MAX_PATH, RomFileSystemPathIllegalCharReplaceType_IllegalFsChars)); !romfsGeneratePathFromFileEntry(shared_data->romfs_ctx, file_entry, path, FS_MAX_PATH, RomFileSystemPathIllegalCharReplaceType_IllegalFsChars));
if (shared_data->read_error) if (shared_data->read_error)
{ {
@ -178,7 +180,13 @@ static void read_thread_func(void *arg)
if (shared_data->read_error || shared_data->write_error || shared_data->transfer_cancelled) break; if (shared_data->read_error || shared_data->write_error || shared_data->transfer_cancelled) break;
file_table_offset += ALIGN_UP(sizeof(RomFileSystemFileEntry) + file_entry->name_length, 4); /* Move to the next file entry. */
shared_data->read_error = !romfsMoveToNextFileEntry(shared_data->romfs_ctx);
if (shared_data->read_error)
{
condvarWakeAll(&g_writeCondvar);
break;
}
} }
free(buf); free(buf);

View file

@ -109,7 +109,7 @@ void utilsJoinThread(Thread *thread);
__attribute__((format(printf, 3, 4))) bool utilsAppendFormattedStringToBuffer(char **dst, size_t *dst_size, const char *fmt, ...); __attribute__((format(printf, 3, 4))) bool utilsAppendFormattedStringToBuffer(char **dst, size_t *dst_size, const char *fmt, ...);
/// Replaces illegal FAT characters in the provided UTF-8 string with underscores. /// Replaces illegal FAT characters in the provided UTF-8 string with underscores.
/// If 'ascii_only' is set to true, all codepoints outside the (0x20,0x7E] range will also be replaced with underscores. /// If 'ascii_only' is set to true, all codepoints outside the [0x20,0x7F) range will also be replaced with underscores.
/// Replacements are performed on a per-codepoint basis, which means the string length can be reduced by this function. /// Replacements are performed on a per-codepoint basis, which means the string length can be reduced by this function.
void utilsReplaceIllegalCharacters(char *str, bool ascii_only); void utilsReplaceIllegalCharacters(char *str, bool ascii_only);

View file

@ -33,7 +33,9 @@ extern "C" {
#define ROMFS_OLD_HEADER_SIZE 0x28 #define ROMFS_OLD_HEADER_SIZE 0x28
#define ROMFS_HEADER_SIZE 0x50 #define ROMFS_HEADER_SIZE 0x50
#define ROMFS_VOID_ENTRY 0xFFFFFFFF #define ROMFS_VOID_ENTRY UINT32_MAX
#define ROMFS_TABLE_ENTRY_ALIGNMENT 0x4
/// Header used by NCA0 RomFS sections. /// Header used by NCA0 RomFS sections.
typedef struct { typedef struct {
@ -80,28 +82,28 @@ typedef struct {
NXDT_ASSERT(RomFileSystemHeader, ROMFS_HEADER_SIZE); NXDT_ASSERT(RomFileSystemHeader, ROMFS_HEADER_SIZE);
/// Directory entry. Always aligned to a 4-byte boundary past the directory name. /// Directory entry. Always aligned to a ROMFS_TABLE_ENTRY_ALIGNMENT boundary past the directory name.
typedef struct { typedef struct {
u32 parent_offset; ///< Parent directory offset. u32 parent_offset; ///< Parent directory offset.
u32 next_offset; ///< Next sibling directory offset. u32 next_offset; ///< Next sibling directory offset. May be set to ROMFS_VOID_ENTRY if there are no other directory entries at this level.
u32 directory_offset; ///< First child directory offset. u32 directory_offset; ///< First child directory offset. May be set to ROMFS_VOID_ENTRY if there are no child directories entries.
u32 file_offset; ///< First child file offset. u32 file_offset; ///< First child file offset. May be set to ROMFS_VOID_ENTRY if there are no child file entries.
u32 bucket_offset; ///< Directory bucket offset. u32 bucket_offset; ///< Directory bucket offset.
u32 name_length; ///< Name length. u32 name_length; ///< Name length.
char name[]; ///< Name (UTF-8). char name[]; ///< Name (UTF-8, may not be NULL terminated depending on the whole entry alignment).
} RomFileSystemDirectoryEntry; } RomFileSystemDirectoryEntry;
NXDT_ASSERT(RomFileSystemDirectoryEntry, 0x18); NXDT_ASSERT(RomFileSystemDirectoryEntry, 0x18);
/// Directory entry. Always aligned to a 4-byte boundary past the file name. /// Directory entry. Always aligned to a ROMFS_TABLE_ENTRY_ALIGNMENT boundary past the file name.
typedef struct { typedef struct {
u32 parent_offset; ///< Parent directory offset. u32 parent_offset; ///< Parent directory offset.
u32 next_offset; ///< Next sibling file offset. u32 next_offset; ///< Next sibling file offset. May be set to ROMFS_VOID_ENTRY if there are no other file entries at this level.
u64 offset; ///< File data offset. u64 offset; ///< File data offset.
u64 size; ///< File data size. u64 size; ///< File data size.
u32 bucket_offset; ///< File bucket offset. u32 bucket_offset; ///< File bucket offset.
u32 name_length; ///< Name length. u32 name_length; ///< Name length.
char name[]; ///< Name (UTF-8). char name[]; ///< Name (UTF-8, may not be NULL terminated depending on the whole entry alignment).
} RomFileSystemFileEntry; } RomFileSystemFileEntry;
NXDT_ASSERT(RomFileSystemFileEntry, 0x20); NXDT_ASSERT(RomFileSystemFileEntry, 0x20);
@ -118,7 +120,8 @@ typedef struct {
u64 file_table_size; ///< RomFS file entries table size. u64 file_table_size; ///< RomFS file entries table size.
RomFileSystemFileEntry *file_table; ///< RomFS file entries table. RomFileSystemFileEntry *file_table; ///< RomFS file entries table.
u64 body_offset; ///< RomFS file data body offset (relative to the start of the RomFS). u64 body_offset; ///< RomFS file data body offset (relative to the start of the RomFS).
u32 cur_dir_offset; ///< Current RomFS directory offset (relative to the start of the directory entries table). Used for RomFS browsing. u64 cur_dir_offset; ///< Current RomFS directory offset (relative to the start of the directory entries table). Used for RomFS browsing.
u64 cur_file_offset; ///< Current RomFS file offset (relative to the start of the file entries table). Used for RomFS browsing.
} RomFileSystemContext; } RomFileSystemContext;
typedef struct { typedef struct {
@ -173,8 +176,7 @@ bool romfsGeneratePathFromFileEntry(RomFileSystemContext *ctx, RomFileSystemFile
/// Use the romfsWriteFileEntryPatchToMemoryBuffer() wrapper to write patch data generated by this function. /// Use the romfsWriteFileEntryPatchToMemoryBuffer() wrapper to write patch data generated by this function.
bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, const void *data, u64 data_size, u64 data_offset, RomFileSystemFileEntryPatch *out); bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, const void *data, u64 data_size, u64 data_offset, RomFileSystemFileEntryPatch *out);
/// Miscellaneous functions. /// Resets a previously initialized RomFileSystemContext.
NX_INLINE void romfsFreeContext(RomFileSystemContext *ctx) NX_INLINE void romfsFreeContext(RomFileSystemContext *ctx)
{ {
if (!ctx) return; if (!ctx) return;
@ -185,22 +187,93 @@ NX_INLINE void romfsFreeContext(RomFileSystemContext *ctx)
memset(ctx, 0, sizeof(RomFileSystemContext)); memset(ctx, 0, sizeof(RomFileSystemContext));
} }
NX_INLINE RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByOffset(RomFileSystemContext *ctx, u32 dir_entry_offset) /// Functions to reset the current directory/file entry offset.
NX_INLINE void romfsResetDirectoryTableOffset(RomFileSystemContext *ctx)
{ {
if (!ctx || !ctx->dir_table || (dir_entry_offset + sizeof(RomFileSystemDirectoryEntry)) > ctx->dir_table_size) return NULL; if (ctx) ctx->cur_dir_offset = 0;
return (RomFileSystemDirectoryEntry*)((u8*)ctx->dir_table + dir_entry_offset);
} }
NX_INLINE RomFileSystemFileEntry *romfsGetFileEntryByOffset(RomFileSystemContext *ctx, u32 file_entry_offset) NX_INLINE void romfsResetFileTableOffset(RomFileSystemContext *ctx)
{ {
if (!ctx || !ctx->file_table || (file_entry_offset + sizeof(RomFileSystemFileEntry)) > ctx->file_table_size) return NULL; if (ctx) ctx->cur_file_offset = 0;
return (RomFileSystemFileEntry*)((u8*)ctx->file_table + file_entry_offset);
} }
/// Checks if the provided RomFileSystemContext is valid.
NX_INLINE bool romfsIsValidContext(RomFileSystemContext *ctx)
{
return (ctx && ncaStorageIsValidContext(ctx->default_storage_ctx) && ctx->size && ctx->dir_table_size && ctx->dir_table && ctx->file_table_size && ctx->file_table && \
ctx->body_offset >= ctx->header.old_format.header_size && ctx->body_offset < ctx->size);
}
/// Functions to retrieve a directory/file entry.
NX_INLINE void *romfsGetEntryByOffset(RomFileSystemContext *ctx, void *entry_table, u64 entry_table_size, u64 entry_size, u64 entry_offset)
{
if (!romfsIsValidContext(ctx) || !entry_table || !entry_table_size || !entry_size || (entry_offset + entry_size) > entry_table_size) return NULL;
return ((u8*)entry_table + entry_offset);
}
NX_INLINE RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByOffset(RomFileSystemContext *ctx, u64 dir_entry_offset)
{
return (ctx ? (RomFileSystemDirectoryEntry*)romfsGetEntryByOffset(ctx, ctx->dir_table, ctx->dir_table_size, sizeof(RomFileSystemDirectoryEntry), dir_entry_offset) : NULL);
}
NX_INLINE RomFileSystemDirectoryEntry *romfsGetCurrentDirectoryEntry(RomFileSystemContext *ctx)
{
return (ctx ? romfsGetDirectoryEntryByOffset(ctx, ctx->cur_dir_offset) : NULL);
}
NX_INLINE RomFileSystemFileEntry *romfsGetFileEntryByOffset(RomFileSystemContext *ctx, u64 file_entry_offset)
{
return (ctx ? (RomFileSystemFileEntry*)romfsGetEntryByOffset(ctx, ctx->file_table, ctx->file_table_size, sizeof(RomFileSystemFileEntry), file_entry_offset) : NULL);
}
NX_INLINE RomFileSystemFileEntry *romfsGetCurrentFileEntry(RomFileSystemContext *ctx)
{
return (ctx ? romfsGetFileEntryByOffset(ctx, ctx->cur_file_offset) : NULL);
}
/// Functions to check if it's possible to move to the next directory/file entry based on the current directory/file entry offset.
NX_INLINE bool romfsCanMoveToNextEntry(RomFileSystemContext *ctx, void *entry_table, u64 entry_table_size, u64 entry_size, u64 entry_offset)
{
if (!romfsIsValidContext(ctx) || !entry_table || !entry_table_size || entry_size < 4 || (entry_offset + entry_size) > entry_table_size) return false;
u32 name_length = *((u32*)((u8*)entry_table + entry_offset + entry_size - 4));
return ((entry_offset + ALIGN_UP(entry_size + name_length, ROMFS_TABLE_ENTRY_ALIGNMENT)) <= entry_table_size);
}
NX_INLINE bool romfsCanMoveToNextDirectoryEntry(RomFileSystemContext *ctx)
{
return (ctx ? romfsCanMoveToNextEntry(ctx, ctx->dir_table, ctx->dir_table_size, sizeof(RomFileSystemDirectoryEntry), ctx->cur_dir_offset) : false);
}
NX_INLINE bool romfsCanMoveToNextFileEntry(RomFileSystemContext *ctx)
{
return (ctx ? romfsCanMoveToNextEntry(ctx, ctx->file_table, ctx->file_table_size, sizeof(RomFileSystemFileEntry), ctx->cur_file_offset) : false);
}
/// Functions to update the current directory/file entry offset to make it point to the next directory/file entry.
NX_INLINE bool romfsMoveToNextEntry(RomFileSystemContext *ctx, void *entry_table, u64 entry_table_size, u64 entry_size, u64 *entry_offset)
{
if (!romfsIsValidContext(ctx) || !entry_table || !entry_table_size || entry_size < 4 || !entry_offset || (*entry_offset + entry_size) > entry_table_size) return false;
u32 name_length = *((u32*)((u8*)entry_table + *entry_offset + entry_size - 4));
*entry_offset += ALIGN_UP(entry_size + name_length, ROMFS_TABLE_ENTRY_ALIGNMENT);
return true;
}
NX_INLINE bool romfsMoveToNextDirectoryEntry(RomFileSystemContext *ctx)
{
return (ctx ? romfsMoveToNextEntry(ctx, ctx->dir_table, ctx->dir_table_size, sizeof(RomFileSystemDirectoryEntry), &(ctx->cur_dir_offset)) : false);
}
NX_INLINE bool romfsMoveToNextFileEntry(RomFileSystemContext *ctx)
{
return (ctx ? romfsMoveToNextEntry(ctx, ctx->file_table, ctx->file_table_size, sizeof(RomFileSystemFileEntry), &(ctx->cur_file_offset)) : false);
}
/// NCA patch management functions.
NX_INLINE void romfsWriteFileEntryPatchToMemoryBuffer(RomFileSystemContext *ctx, RomFileSystemFileEntryPatch *patch, void *buf, u64 buf_size, u64 buf_offset) NX_INLINE void romfsWriteFileEntryPatchToMemoryBuffer(RomFileSystemContext *ctx, RomFileSystemFileEntryPatch *patch, void *buf, u64 buf_size, u64 buf_offset)
{ {
if (!ctx || ctx->is_patch || !ncaStorageIsValidContext(ctx->default_storage_ctx) || ctx->default_storage_ctx->base_storage_type != NcaStorageBaseStorageType_Regular || !patch || \ if (!romfsIsValidContext(ctx) || ctx->is_patch || ctx->default_storage_ctx->base_storage_type != NcaStorageBaseStorageType_Regular || !patch || \
(!patch->use_old_format_patch && ctx->default_storage_ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs) || \ (!patch->use_old_format_patch && ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs) || \
(patch->use_old_format_patch && ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs)) return; (patch->use_old_format_patch && ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs)) return;
NcaContext *nca_ctx = (NcaContext*)ctx->default_storage_ctx->nca_fs_ctx->nca_ctx; NcaContext *nca_ctx = (NcaContext*)ctx->default_storage_ctx->nca_fs_ctx->nca_ctx;

View file

@ -182,7 +182,7 @@ end:
bool romfsReadFileSystemData(RomFileSystemContext *ctx, void *out, u64 read_size, u64 offset) bool romfsReadFileSystemData(RomFileSystemContext *ctx, void *out, u64 read_size, u64 offset)
{ {
if (!ctx || !ncaStorageIsValidContext(ctx->default_storage_ctx) || !ctx->size || !out || !read_size || (offset + read_size) > ctx->size) if (!romfsIsValidContext(ctx) || !out || !read_size || (offset + read_size) > ctx->size)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
return false; return false;
@ -200,7 +200,8 @@ bool romfsReadFileSystemData(RomFileSystemContext *ctx, void *out, u64 read_size
bool romfsReadFileEntryData(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, void *out, u64 read_size, u64 offset) bool romfsReadFileEntryData(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, void *out, u64 read_size, u64 offset)
{ {
if (!ctx || !ctx->body_offset || !file_entry || !file_entry->size || (file_entry->offset + file_entry->size) > ctx->size || !out || !read_size || (offset + read_size) > file_entry->size) if (!romfsIsValidContext(ctx) || !file_entry || !file_entry->size || (file_entry->offset + file_entry->size) > ctx->size || !out || !read_size || \
(offset + read_size) > file_entry->size)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
return false; return false;
@ -218,75 +219,120 @@ bool romfsReadFileEntryData(RomFileSystemContext *ctx, RomFileSystemFileEntry *f
bool romfsGetTotalDataSize(RomFileSystemContext *ctx, u64 *out_size) bool romfsGetTotalDataSize(RomFileSystemContext *ctx, u64 *out_size)
{ {
if (!ctx || !ctx->file_table_size || !ctx->file_table || !out_size) if (!romfsIsValidContext(ctx) || !out_size)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
return false; return false;
} }
u64 offset = 0, total_size = 0;
RomFileSystemFileEntry *file_entry = NULL; RomFileSystemFileEntry *file_entry = NULL;
u64 total_size = 0;
bool success = false;
while(offset < ctx->file_table_size) /* Reset current file table offset. */
romfsResetFileTableOffset(ctx);
/* Loop through all file entries. */
while(romfsCanMoveToNextFileEntry(ctx))
{ {
if (!(file_entry = romfsGetFileEntryByOffset(ctx, offset))) /* Get current file entry. */
if (!(file_entry = romfsGetCurrentFileEntry(ctx)))
{ {
LOG_MSG("Failed to retrieve file entry!"); LOG_MSG("Failed to retrieve current file entry! (0x%lX, 0x%lX).", ctx->cur_file_offset, ctx->file_table_size);
return false; goto end;
} }
/* Update total data size. */
total_size += file_entry->size; total_size += file_entry->size;
offset += ALIGN_UP(sizeof(RomFileSystemFileEntry) + file_entry->name_length, 4);
/* Move to the next file entry. */
if (!romfsMoveToNextFileEntry(ctx))
{
LOG_MSG("Failed to move to the next file entry! (0x%lX, 0x%lX).", ctx->cur_file_offset, ctx->file_table_size);
goto end;
}
} }
/* Update output values. */
*out_size = total_size; *out_size = total_size;
success = true;
return true; end:
/* Reset current file table offset. */
romfsResetFileTableOffset(ctx);
return success;
} }
bool romfsGetDirectoryDataSize(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, u64 *out_size) bool romfsGetDirectoryDataSize(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, u64 *out_size)
{ {
if (!ctx || !ctx->dir_table_size || !ctx->dir_table || !ctx->file_table_size || !ctx->file_table || !dir_entry || (dir_entry->file_offset == ROMFS_VOID_ENTRY && \ if (!romfsIsValidContext(ctx) || !dir_entry || !out_size)
dir_entry->directory_offset == ROMFS_VOID_ENTRY) || !out_size)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
return false; return false;
} }
u64 total_size = 0, child_dir_size = 0; /* Short-circuit: check if we're dealing with an empty directory. */
u32 cur_file_offset = 0, cur_dir_offset = 0; if (dir_entry->file_offset == ROMFS_VOID_ENTRY && dir_entry->directory_offset == ROMFS_VOID_ENTRY)
{
*out_size = 0;
return true;
}
RomFileSystemFileEntry *cur_file_entry = NULL; RomFileSystemFileEntry *cur_file_entry = NULL;
RomFileSystemDirectoryEntry *cur_dir_entry = NULL; RomFileSystemDirectoryEntry *cur_dir_entry = NULL;
u64 total_size = 0, cur_entry_offset = 0, child_dir_size = 0;
bool success = false;
cur_file_offset = dir_entry->file_offset; /* Loop through the child file entries' linked list. */
while(cur_file_offset != ROMFS_VOID_ENTRY) cur_entry_offset = dir_entry->file_offset;
while(cur_entry_offset != ROMFS_VOID_ENTRY)
{ {
if (!(cur_file_entry = romfsGetFileEntryByOffset(ctx, cur_file_offset))) /* Get current file entry. */
if (!(cur_file_entry = romfsGetFileEntryByOffset(ctx, cur_entry_offset)))
{ {
LOG_MSG("Failed to retrieve file entry!"); LOG_MSG("Failed to retrieve file entry! (0x%lX, 0x%lX).", cur_entry_offset, ctx->file_table_size);
return false; goto end;
} }
/* Update total data size. */
total_size += cur_file_entry->size; total_size += cur_file_entry->size;
cur_file_offset = cur_file_entry->next_offset;
/* Update current file entry offset. */
cur_entry_offset = cur_file_entry->next_offset;
} }
cur_dir_offset = dir_entry->directory_offset; /* Loop through the child directory entries' linked list. */
while(cur_dir_offset != ROMFS_VOID_ENTRY) cur_entry_offset = dir_entry->directory_offset;
while(cur_entry_offset != ROMFS_VOID_ENTRY)
{ {
if (!(cur_dir_entry = romfsGetDirectoryEntryByOffset(ctx, cur_dir_offset)) || !romfsGetDirectoryDataSize(ctx, cur_dir_entry, &child_dir_size)) /* Get current directory entry. */
if (!(cur_dir_entry = romfsGetDirectoryEntryByOffset(ctx, cur_entry_offset)))
{ {
LOG_MSG("Failed to retrieve directory entry/size!"); LOG_MSG("Failed to retrieve directory entry! (0x%lX, 0x%lX).", cur_entry_offset, ctx->dir_table_size);
return false; goto end;
} }
/* Calculate directory size. */
if (!romfsGetDirectoryDataSize(ctx, cur_dir_entry, &child_dir_size))
{
LOG_MSG("Failed to get size for directory entry! (0x%lX, 0x%lX).", cur_entry_offset, ctx->dir_table_size);
goto end;
}
/* Update total data size. */
total_size += child_dir_size; total_size += child_dir_size;
cur_dir_offset = cur_dir_entry->next_offset;
/* Update current directory entry offset. */
cur_entry_offset = cur_dir_entry->next_offset;
} }
/* Update output values. */
*out_size = total_size; *out_size = total_size;
success = true;
return true; end:
return success;
} }
RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *ctx, const char *path) RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *ctx, const char *path)
@ -295,22 +341,27 @@ RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *
char *path_dup = NULL, *pch = NULL, *state = NULL; char *path_dup = NULL, *pch = NULL, *state = NULL;
RomFileSystemDirectoryEntry *dir_entry = NULL; RomFileSystemDirectoryEntry *dir_entry = NULL;
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !path || *path != '/' || !(path_len = strlen(path)) || !(dir_entry = romfsGetDirectoryEntryByOffset(ctx, 0))) if (!romfsIsValidContext(ctx) || !path || *path != '/' || !(dir_entry = romfsGetDirectoryEntryByOffset(ctx, 0)))
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
return NULL; return NULL;
} }
/* Check if the root directory was requested. */ /* Retrieve path length. */
path_len = strlen(path);
/* Short-circuit: check if the root directory was requested. */
if (path_len == 1) return dir_entry; if (path_len == 1) return dir_entry;
/* Duplicate path to avoid problems with strtok_r(). */ /* Duplicate path to avoid problems with strtok_r(). */
if (!(path_dup = strdup(path))) if (!(path_dup = strdup(path)))
{ {
LOG_MSG("Unable to duplicate input path! (\"%s\").", path); LOG_MSG("Unable to duplicate input path! (\"%s\").", path);
return NULL; dir_entry = NULL;
goto end;
} }
/* Tokenize duplicated path using path separators. */
pch = strtok_r(path_dup, "/", &state); pch = strtok_r(path_dup, "/", &state);
if (!pch) if (!pch)
{ {
@ -319,14 +370,17 @@ RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *
goto end; goto end;
} }
/* Loop through all path elements. */
while(pch) while(pch)
{ {
/* Get child directory entry using the current token. */
if (!(dir_entry = romfsGetChildDirectoryEntryByName(ctx, dir_entry, pch))) if (!(dir_entry = romfsGetChildDirectoryEntryByName(ctx, dir_entry, pch)))
{ {
LOG_MSG("Failed to retrieve directory entry by name for \"%s\"! (\"%s\").", pch, path); LOG_MSG("Failed to retrieve directory entry by name for \"%s\"! (\"%s\").", pch, path);
break; break;
} }
/* Move onto the next token. */
pch = strtok_r(NULL, "/", &state); pch = strtok_r(NULL, "/", &state);
} }
@ -345,20 +399,23 @@ RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const
RomFileSystemDirectoryEntry *dir_entry = NULL; RomFileSystemDirectoryEntry *dir_entry = NULL;
NcaContext *nca_ctx = NULL; NcaContext *nca_ctx = NULL;
if (!ctx || !ctx->file_table || !ctx->file_table_size || !ncaStorageIsValidContext(ctx->default_storage_ctx) || \ if (!romfsIsValidContext(ctx) || !(nca_ctx = (NcaContext*)ctx->default_storage_ctx->nca_fs_ctx->nca_ctx) || !path || *path != '/')
!(nca_ctx = (NcaContext*)ctx->default_storage_ctx->nca_fs_ctx->nca_ctx) || !path || *path != '/' || (path_len = strlen(path)) <= 1)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
return NULL; return NULL;
} }
/* Retrieve path length. */
path_len = strlen(path);
/* Retrieve NCA content type. */
content_type = nca_ctx->content_type; content_type = nca_ctx->content_type;
/* Duplicate path. */ /* Duplicate path. */
if (!(path_dup = strdup(path))) if (!(path_dup = strdup(path)))
{ {
LOG_MSG("Unable to duplicate input path! (\"%s\").", path); LOG_MSG("Unable to duplicate input path! (\"%s\").", path);
return NULL; goto end;
} }
/* Remove any trailing slashes. */ /* Remove any trailing slashes. */
@ -403,42 +460,45 @@ end:
bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, char *out_path, size_t out_path_size, u8 illegal_char_replace_type) bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, char *out_path, size_t out_path_size, u8 illegal_char_replace_type)
{ {
size_t path_len = 0; size_t path_len = 0;
u32 dir_offset = ROMFS_VOID_ENTRY, dir_entries_count = 0; u64 dir_offset = ROMFS_VOID_ENTRY;
u32 dir_entries_count = 0;
RomFileSystemDirectoryEntry **dir_entries = NULL, **tmp_dir_entries = NULL; RomFileSystemDirectoryEntry **dir_entries = NULL, **tmp_dir_entries = NULL;
bool success = false; bool success = false;
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !dir_entry || (!dir_entry->name_length && dir_entry->parent_offset) || !out_path || out_path_size < 2 || \ if (!romfsIsValidContext(ctx) || !dir_entry || (!dir_entry->name_length && dir_entry->parent_offset) || !out_path || out_path_size < 2 || \
illegal_char_replace_type > RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly) illegal_char_replace_type > RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
return false; return false;
} }
/* Check if we're dealing with the root directory entry. */ /* Short-circuit: check if we're dealing with the root directory entry. */
if (!dir_entry->name_length) if (!dir_entry->name_length)
{ {
sprintf(out_path, "/"); sprintf(out_path, "/");
return true; return true;
} }
/* Allocate memory for our directory entries. */ /* Allocate memory for our directory entries pointer array. */
dir_entries = calloc(1, sizeof(RomFileSystemDirectoryEntry*)); dir_entries = calloc(1, sizeof(RomFileSystemDirectoryEntry*));
if (!dir_entries) if (!dir_entries)
{ {
LOG_MSG("Unable to allocate memory for directory entries!"); LOG_MSG("Unable to allocate memory for directory entries!");
return false; goto end;
} }
/* Update stats. */
path_len = (1 + dir_entry->name_length); path_len = (1 + dir_entry->name_length);
*dir_entries = dir_entry; *dir_entries = dir_entry;
dir_entries_count++; dir_entries_count++;
while(true) while(true)
{ {
/* Get parent directory offset. Break out of the loop if we reached the root directory. */
dir_offset = dir_entries[dir_entries_count - 1]->parent_offset; dir_offset = dir_entries[dir_entries_count - 1]->parent_offset;
if (!dir_offset) break; if (!dir_offset) break;
/* Reallocate directory entries. */ /* Reallocate directory entries pointer array. */
if (!(tmp_dir_entries = realloc(dir_entries, (dir_entries_count + 1) * sizeof(RomFileSystemDirectoryEntry*)))) if (!(tmp_dir_entries = realloc(dir_entries, (dir_entries_count + 1) * sizeof(RomFileSystemDirectoryEntry*))))
{ {
LOG_MSG("Unable to reallocate directory entries buffer!"); LOG_MSG("Unable to reallocate directory entries buffer!");
@ -448,6 +508,7 @@ bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSyste
dir_entries = tmp_dir_entries; dir_entries = tmp_dir_entries;
tmp_dir_entries = NULL; tmp_dir_entries = NULL;
/* Retrieve parent directory entry using the offset we got earlier. */
RomFileSystemDirectoryEntry **cur_dir_entry = &(dir_entries[dir_entries_count]); RomFileSystemDirectoryEntry **cur_dir_entry = &(dir_entries[dir_entries_count]);
if (!(*cur_dir_entry = romfsGetDirectoryEntryByOffset(ctx, dir_offset)) || !(*cur_dir_entry)->name_length) if (!(*cur_dir_entry = romfsGetDirectoryEntryByOffset(ctx, dir_offset)) || !(*cur_dir_entry)->name_length)
{ {
@ -455,37 +516,44 @@ bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSyste
goto end; goto end;
} }
/* Update stats. */
path_len += (1 + (*cur_dir_entry)->name_length); path_len += (1 + (*cur_dir_entry)->name_length);
dir_entries_count++; dir_entries_count++;
} }
/* Make sure the output buffer is big enough to hold the full path + NULL terminator. */
if (path_len >= out_path_size) if (path_len >= out_path_size)
{ {
LOG_MSG("Output path length exceeds output buffer size!"); LOG_MSG("Output path length exceeds output buffer size! (%lu >= %lu).", path_len, out_path_size);
goto end; goto end;
} }
/* Generate output path. */ /* Generate output path, looping through our directory entries pointer array in reverse order. */
*out_path = '\0'; *out_path = '\0';
path_len = 0; path_len = 0;
for(u32 i = dir_entries_count; i > 0; i--) for(u32 i = dir_entries_count; i > 0; i--)
{ {
RomFileSystemDirectoryEntry **cur_dir_entry = &(dir_entries[i - 1]); /* Get current directory entry. */
RomFileSystemDirectoryEntry *cur_dir_entry = dir_entries[i - 1];
/* Concatenate path separator and current directory name to the output buffer. */
strcat(out_path, "/"); strcat(out_path, "/");
strncat(out_path, (*cur_dir_entry)->name, (*cur_dir_entry)->name_length); strncat(out_path, cur_dir_entry->name, cur_dir_entry->name_length);
path_len++; path_len++;
if (illegal_char_replace_type) if (illegal_char_replace_type)
{ {
/* Replace illegal characters within this directory name, then update the full path length. */
utilsReplaceIllegalCharacters(out_path + path_len, illegal_char_replace_type == RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly); utilsReplaceIllegalCharacters(out_path + path_len, illegal_char_replace_type == RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly);
path_len += strlen(out_path + path_len); path_len += strlen(out_path + path_len);
} else { } else {
path_len += (*cur_dir_entry)->name_length; /* Update full path length. */
path_len += cur_dir_entry->name_length;
} }
} }
/* Update return value. */
success = true; success = true;
end: end:
@ -498,8 +566,9 @@ bool romfsGeneratePathFromFileEntry(RomFileSystemContext *ctx, RomFileSystemFile
{ {
size_t path_len = 0; size_t path_len = 0;
RomFileSystemDirectoryEntry *dir_entry = NULL; RomFileSystemDirectoryEntry *dir_entry = NULL;
bool success = false;
if (!ctx || !ctx->file_table || !ctx->file_table_size || !file_entry || !file_entry->name_length || !out_path || out_path_size < 2 || \ if (!romfsIsValidContext(ctx) || !file_entry || !file_entry->name_length || !out_path || out_path_size < 2 || \
!(dir_entry = romfsGetDirectoryEntryByOffset(ctx, file_entry->parent_offset)) || illegal_char_replace_type > RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly) !(dir_entry = romfsGetDirectoryEntryByOffset(ctx, file_entry->parent_offset)) || illegal_char_replace_type > RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
@ -510,35 +579,41 @@ bool romfsGeneratePathFromFileEntry(RomFileSystemContext *ctx, RomFileSystemFile
if (!romfsGeneratePathFromDirectoryEntry(ctx, dir_entry, out_path, out_path_size, illegal_char_replace_type)) if (!romfsGeneratePathFromDirectoryEntry(ctx, dir_entry, out_path, out_path_size, illegal_char_replace_type))
{ {
LOG_MSG("Failed to retrieve RomFS directory path!"); LOG_MSG("Failed to retrieve RomFS directory path!");
return false; goto end;
} }
/* Check path length. */ /* Make sure the output buffer is big enough to hold the full path + NULL terminator. */
path_len = strlen(out_path); path_len = strlen(out_path);
if ((1 + file_entry->name_length) >= (out_path_size - path_len)) if ((path_len + 1 + file_entry->name_length) >= out_path_size)
{ {
LOG_MSG("Output path length exceeds output buffer size!"); LOG_MSG("Output path length exceeds output buffer size! (%lu >= %lu).", path_len + 1 + file_entry->name_length, out_path_size);
return false; goto end;
} }
/* Concatenate file entry name. */ /* Concatenate path separator if our parent directory isn't the root directory. */
if (file_entry->parent_offset) if (file_entry->parent_offset)
{ {
strcat(out_path, "/"); strcat(out_path, "/");
path_len++; path_len++;
} }
/* Concatenate file entry name. */
strncat(out_path, file_entry->name, file_entry->name_length); strncat(out_path, file_entry->name, file_entry->name_length);
/* Replace illegal characters within the file name, if needed. */
if (illegal_char_replace_type) utilsReplaceIllegalCharacters(out_path + path_len, illegal_char_replace_type == RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly); if (illegal_char_replace_type) utilsReplaceIllegalCharacters(out_path + path_len, illegal_char_replace_type == RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly);
return true; /* Update return value. */
success = true;
end:
return success;
} }
bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, const void *data, u64 data_size, u64 data_offset, RomFileSystemFileEntryPatch *out) bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, const void *data, u64 data_size, u64 data_offset, RomFileSystemFileEntryPatch *out)
{ {
if (!ctx || ctx->is_patch || !ncaStorageIsValidContext(ctx->default_storage_ctx) || ctx->default_storage_ctx->base_storage_type != NcaStorageBaseStorageType_Regular || \ if (!romfsIsValidContext(ctx) || ctx->is_patch || ctx->default_storage_ctx->base_storage_type != NcaStorageBaseStorageType_Regular || \
!ctx->body_offset || (ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs && ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs) || \ (ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs && ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs) || \
!file_entry || !file_entry->size || (file_entry->offset + file_entry->size) > ctx->size || !data || !data_size || (data_offset + data_size) > file_entry->size || !out) !file_entry || !file_entry->size || (file_entry->offset + file_entry->size) > ctx->size || !data || !data_size || (data_offset + data_size) > file_entry->size || !out)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
@ -572,24 +647,28 @@ static RomFileSystemDirectoryEntry *romfsGetChildDirectoryEntryByName(RomFileSys
size_t name_len = 0; size_t name_len = 0;
RomFileSystemDirectoryEntry *child_dir_entry = NULL; RomFileSystemDirectoryEntry *child_dir_entry = NULL;
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !dir_entry || (dir_offset = dir_entry->directory_offset) == ROMFS_VOID_ENTRY || !name || !(name_len = strlen(name))) if (!dir_entry || (dir_offset = dir_entry->directory_offset) == ROMFS_VOID_ENTRY || !name || !(name_len = strlen(name)))
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
return NULL; return NULL;
} }
/* Loop through the child directory entries' linked list. */
while(dir_offset != ROMFS_VOID_ENTRY) while(dir_offset != ROMFS_VOID_ENTRY)
{ {
/* Get current directory entry. */
if (!(child_dir_entry = romfsGetDirectoryEntryByOffset(ctx, dir_offset))) if (!(child_dir_entry = romfsGetDirectoryEntryByOffset(ctx, dir_offset)))
{ {
LOG_MSG("Failed to retrieve directory entry at offset 0x%lX!", dir_offset); LOG_MSG("Failed to retrieve directory entry! (0x%lX, 0x%lX).", dir_offset, ctx->dir_table_size);
break; break;
} }
/* Check if we found the right child directory entry. */
/* strncmp() is used here instead of strcmp() because names stored in RomFS sections are not always NULL terminated. */ /* strncmp() is used here instead of strcmp() because names stored in RomFS sections are not always NULL terminated. */
/* If the name ends at a 4-byte boundary, the next entry starts immediately. */ /* If the name ends at a 4-byte boundary, the next entry starts immediately. */
if (child_dir_entry->name_length == name_len && !strncmp(child_dir_entry->name, name, name_len)) return child_dir_entry; if (child_dir_entry->name_length == name_len && !strncmp(child_dir_entry->name, name, name_len)) return child_dir_entry;
/* Update current directory entry offset. */
dir_offset = child_dir_entry->next_offset; dir_offset = child_dir_entry->next_offset;
} }
@ -602,21 +681,23 @@ static RomFileSystemFileEntry *romfsGetChildFileEntryByName(RomFileSystemContext
size_t name_len = 0; size_t name_len = 0;
RomFileSystemFileEntry *child_file_entry = NULL; RomFileSystemFileEntry *child_file_entry = NULL;
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !ctx->file_table || !ctx->file_table_size || !dir_entry || (file_offset = dir_entry->file_offset) == ROMFS_VOID_ENTRY || \ if (!dir_entry || (file_offset = dir_entry->file_offset) == ROMFS_VOID_ENTRY || !name || !(name_len = strlen(name)))
!name || !(name_len = strlen(name)))
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
return NULL; return NULL;
} }
/* Loop through the child file entries' linked list. */
while(file_offset != ROMFS_VOID_ENTRY) while(file_offset != ROMFS_VOID_ENTRY)
{ {
/* Get current file entry. */
if (!(child_file_entry = romfsGetFileEntryByOffset(ctx, file_offset))) if (!(child_file_entry = romfsGetFileEntryByOffset(ctx, file_offset)))
{ {
LOG_MSG("Failed to retrieve file entry at offset 0x%lX!", file_offset); LOG_MSG("Failed to retrieve file entry! (0x%lX, 0x%lX).", file_offset, ctx->file_table_size);
break; break;
} }
/* Check if we found the right child file entry. */
/* strncmp() is used here instead of strcmp() because names stored in RomFS sections are not always NULL terminated. */ /* strncmp() is used here instead of strcmp() because names stored in RomFS sections are not always NULL terminated. */
/* If the name ends at a 4-byte boundary, the next entry starts immediately. */ /* If the name ends at a 4-byte boundary, the next entry starts immediately. */
if (child_file_entry->name_length == name_len && !strncmp(child_file_entry->name, name, name_len)) return child_file_entry; if (child_file_entry->name_length == name_len && !strncmp(child_file_entry->name, name, name_len)) return child_file_entry;