diff --git a/code_templates/nxdt_rw_poc.c b/code_templates/nxdt_rw_poc.c index fe39cbd..44522aa 100644 --- a/code_templates/nxdt_rw_poc.c +++ b/code_templates/nxdt_rw_poc.c @@ -137,6 +137,26 @@ typedef struct { bool use_layeredfs_dir; } RomFsThreadData; +typedef struct { + bool highlight; + size_t size; + char size_str[0x10]; + struct dirent dt; +} FsBrowserEntry; + +typedef struct { + SharedThreadData shared_thread_data; + FILE *src; +} FsBrowserFileThreadData; + +typedef struct { + SharedThreadData shared_thread_data; + const char *dir_path; + const FsBrowserEntry *entries; + u32 entries_count; + const char *base_out_path; +} FsBrowserHighlightedEntriesThreadData; + /* Function prototypes. */ static void utilsScanPads(void); @@ -202,6 +222,14 @@ static bool saveTicket(void *userdata); static bool saveNintendoContentArchive(void *userdata); static bool saveNintendoContentArchiveFsSection(void *userdata); +static bool browseNintendoContentArchiveFsSection(void *userdata); + +static bool fsBrowser(const char *mount_name, const char *base_out_path); +static bool fsBrowserGetDirEntries(const char *dir_path, FsBrowserEntry **out_entries, u32 *out_entry_count); +static bool fsBrowserDumpFile(const char *dir_path, const FsBrowserEntry *entry, const char *base_out_path); +static bool fsBrowserDumpHighlightedEntries(const char *dir_path, const FsBrowserEntry *entries, u32 entries_count, const char *base_out_path); + +static bool initializeNcaFsContext(void *userdata, u8 *out_section_type, bool *out_use_layeredfs_dir, NcaContext **out_base_patch_nca_ctx, void **out_fs_ctx); static bool saveRawPartitionFsSection(PartitionFileSystemContext *pfs_ctx, bool use_layeredfs_dir); static bool saveExtractedPartitionFsSection(PartitionFileSystemContext *pfs_ctx, bool use_layeredfs_dir); @@ -222,6 +250,10 @@ static void extractedPartitionFsReadThreadFunc(void *arg); static void rawRomFsReadThreadFunc(void *arg); static void extractedRomFsReadThreadFunc(void *arg); +static void fsBrowserFileReadThreadFunc(void *arg); +static void fsBrowserHighlightedEntriesReadThreadFunc(void *arg); +static bool fsBrowserHighlightedEntriesReadThreadLoop(SharedThreadData *shared_thread_data, const char *dir_path, const FsBrowserEntry *entries, u32 entries_count, const char *base_out_path, void *buf1, void *buf2); + static void genericWriteThreadFunc(void *arg); static bool spanDumpThreads(ThreadFunc read_func, ThreadFunc write_func, void *arg); @@ -689,12 +721,19 @@ static MenuElementOption g_ncaFsSectionsSubMenuBasePatchElementOption = { static MenuElement *g_ncaFsSectionsSubMenuElements[] = { &(MenuElement){ - .str = "start nca fs dump", + .str = "start nca fs section dump", .child_menu = NULL, .task_func = &saveNintendoContentArchiveFsSection, .element_options = NULL, .userdata = NULL // Dynamically set }, + &(MenuElement){ + .str = "browse nca fs section", + .child_menu = NULL, + .task_func = &browseNintendoContentArchiveFsSection, + .element_options = NULL, + .userdata = NULL // Dynamically set + }, &(MenuElement){ .str = "use base/patch title", .child_menu = NULL, @@ -921,7 +960,7 @@ static Menu g_rootMenu = { static Mutex g_conMutex = 0, g_fileMutex = 0; static CondVar g_readCondvar = 0, g_writeCondvar = 0; -static char path[FS_MAX_PATH] = {0}; +static char path[FS_MAX_PATH * 2] = {0}; int main(int argc, char *argv[]) { @@ -978,6 +1017,7 @@ int main(int argc, char *argv[]) } consoleClear(); + consolePrint(APP_TITLE " v" APP_VERSION " (" GIT_REV ").\nBuilt on " BUILD_TIMESTAMP ".\n"); consolePrint("______________________________\n\n"); if (cur_menu->parent) consolePrint("press b to go back\n"); @@ -1245,7 +1285,7 @@ int main(int argc, char *argv[]) break; } - if (cur_menu->id > MenuId_Root) + if (cur_menu->id > MenuId_Root && (cur_menu->id != MenuId_NcaFsSectionsSubMenu || cur_menu->selected != 1)) { /* Wait for USB session (if needed). */ if (useUsbHost() && !waitForUsb()) @@ -1268,9 +1308,12 @@ int main(int argc, char *argv[]) selected_element->task_func(selected_element->userdata); } - /* Display prompt. */ - consolePrint("press any button to continue"); - utilsWaitForButtonPress(0); + if (g_appletStatus && (cur_menu->id != MenuId_NcaFsSectionsSubMenu || cur_menu->selected != 1)) + { + /* Display prompt. */ + consolePrint("press any button to continue"); + utilsWaitForButtonPress(0); + } } } else if (((btn_down & HidNpadButton_Down) || (btn_held & (HidNpadButton_StickLDown | HidNpadButton_StickRDown))) && element_count) @@ -1314,7 +1357,7 @@ int main(int argc, char *argv[]) if ((btn_down & (HidNpadButton_Right | HidNpadButton_StickLRight | HidNpadButton_StickRRight)) && selected_element_options) { /* Point to the next base/patch title. */ - if (cur_menu->id == MenuId_NcaFsSectionsSubMenu && cur_menu->selected == 1) + if (cur_menu->id == MenuId_NcaFsSectionsSubMenu && cur_menu->selected == 2) { if (selected_element_options->selected == 0 && g_ncaBasePatchTitleInfoBkp) { @@ -1338,7 +1381,7 @@ int main(int argc, char *argv[]) if (selected_element_options->setter_func) selected_element_options->setter_func(selected_element_options->selected); /* Point to the previous base/patch title. */ - if (cur_menu->id == MenuId_NcaFsSectionsSubMenu && cur_menu->selected == 1) + if (cur_menu->id == MenuId_NcaFsSectionsSubMenu && cur_menu->selected == 2) { if (selected_element_options->selected == 0 && g_ncaBasePatchTitleInfo) { @@ -1433,6 +1476,8 @@ int main(int argc, char *argv[]) break; } + if (!g_appletStatus) break; + utilsAppletLoopDelay(); } @@ -1948,7 +1993,7 @@ void freeNcaBasePatchList(void) g_ncaFsSectionsSubMenuBasePatchElementOption.selected = 0; g_ncaFsSectionsSubMenuBasePatchElementOption.options = NULL; - g_ncaFsSectionsSubMenuElements[0]->userdata = NULL; + g_ncaFsSectionsSubMenuElements[0]->userdata = g_ncaFsSectionsSubMenuElements[1]->userdata = NULL; if (g_ncaBasePatchTitleInfo && (g_ncaBasePatchTitleInfo->meta_key.type == NcmContentMetaType_AddOnContent || g_ncaBasePatchTitleInfo->meta_key.type == NcmContentMetaType_DataPatch)) { @@ -2041,7 +2086,7 @@ void updateNcaBasePatchList(TitleUserApplicationData *user_app_data, TitleInfo * g_ncaFsSectionsSubMenuBasePatchElementOption.options = g_ncaBasePatchOptions; - g_ncaFsSectionsSubMenuElements[0]->userdata = nca_fs_ctx; + g_ncaFsSectionsSubMenuElements[0]->userdata = g_ncaFsSectionsSubMenuElements[1]->userdata = nca_fs_ctx; g_ncaUserTitleInfo = title_info; @@ -3264,6 +3309,614 @@ end: } static bool saveNintendoContentArchiveFsSection(void *userdata) +{ + u8 section_type = 0; + bool use_layeredfs_dir = false; + NcaContext *base_patch_nca_ctx = NULL; + void *fs_ctx = NULL; + + bool write_raw_section = (bool)getNcaFsWriteRawSectionOption(); + bool success = false; + + /* Initialize NCA FS section context. */ + if (!initializeNcaFsContext(userdata, §ion_type, &use_layeredfs_dir, &base_patch_nca_ctx, &fs_ctx)) return false; + + /* Perform requested operation. */ + if (section_type == NcaFsSectionType_PartitionFs) + { + PartitionFileSystemContext *pfs_ctx = (PartitionFileSystemContext*)fs_ctx; + success = (write_raw_section ? saveRawPartitionFsSection(pfs_ctx, use_layeredfs_dir) : saveExtractedPartitionFsSection(pfs_ctx, use_layeredfs_dir)); + pfsFreeContext(pfs_ctx); + } else { + RomFileSystemContext *romfs_ctx = (RomFileSystemContext*)fs_ctx; + success = (write_raw_section ? saveRawRomFsSection(romfs_ctx, use_layeredfs_dir) : saveExtractedRomFsSection(romfs_ctx, use_layeredfs_dir)); + romfsFreeContext(romfs_ctx); + } + + /* Free data. */ + free(fs_ctx); + free(base_patch_nca_ctx); + + return success; +} + +static bool browseNintendoContentArchiveFsSection(void *userdata) +{ + u8 section_type = 0; + bool use_layeredfs_dir = false; + NcaContext *base_patch_nca_ctx = NULL; + void *fs_ctx = NULL; + + PartitionFileSystemContext *pfs_ctx = NULL; + RomFileSystemContext *romfs_ctx = NULL; + + NcaFsSectionContext *nca_fs_ctx = NULL; + NcaContext *nca_ctx = NULL; + + u64 title_id = 0; + u8 title_type = 0; + + char mount_name[DEVOPTAB_MOUNT_NAME_LENGTH] = {0}, subdir[0x20] = {0}, extension[FS_MAX_PATH] = {0}; + char *base_out_path = NULL; + + bool success = false; + + /* Initialize NCA FS section context. */ + if (!initializeNcaFsContext(userdata, §ion_type, &use_layeredfs_dir, &base_patch_nca_ctx, &fs_ctx)) goto end; + + /* Mount devoptab device. */ + if (section_type == NcaFsSectionType_PartitionFs) + { + pfs_ctx = (PartitionFileSystemContext*)fs_ctx; + nca_fs_ctx = pfs_ctx->nca_fs_ctx; + + snprintf(mount_name, MAX_ELEMENTS(mount_name), "%s", pfs_ctx->is_exefs ? "exefs" : "pfs"); + + if (!devoptabMountPartitionFileSystemDevice(pfs_ctx, mount_name)) + { + consolePrint("pfs ctx devoptab mount failed!\n"); + goto end; + } + } else { + romfs_ctx = (RomFileSystemContext*)fs_ctx; + nca_fs_ctx = romfs_ctx->default_storage_ctx->nca_fs_ctx; + + snprintf(mount_name, MAX_ELEMENTS(mount_name), "ncaromfs"); + + if (!devoptabMountRomFileSystemDevice(romfs_ctx, mount_name)) + { + consolePrint("romfs ctx devoptab mount failed!\n"); + goto end; + } + } + + /* Generate output base path. */ + nca_ctx = nca_fs_ctx->nca_ctx; + title_id = nca_ctx->title_id; + title_type = nca_ctx->title_type; + + if (use_layeredfs_dir) + { + /* Only use base title IDs if we're dealing with patches. */ + title_id = (title_type == NcmContentMetaType_Patch ? titleGetApplicationIdByPatchId(title_id) : \ + (title_type == NcmContentMetaType_DataPatch ? titleGetAddOnContentIdByDataPatchId(title_id) : title_id)); + + base_out_path = generateOutputLayeredFsFileName(title_id + nca_ctx->id_offset, NULL, "exefs"); + } else { + snprintf(subdir, MAX_ELEMENTS(subdir), "NCA FS/%s/Extracted", nca_ctx->storage_id == NcmStorageId_BuiltInSystem ? "System" : "User"); + snprintf(extension, MAX_ELEMENTS(extension), "/%s #%u/%u", titleGetNcmContentTypeName(nca_ctx->content_type), nca_ctx->id_offset, nca_fs_ctx->section_idx); + + TitleInfo *title_info = (title_id == g_ncaUserTitleInfo->meta_key.id ? g_ncaUserTitleInfo : g_ncaBasePatchTitleInfo); + base_out_path = generateOutputTitleFileName(title_info, subdir, extension); + } + + if (!base_out_path) goto end; + + /* Display file browser. */ + success = fsBrowser(mount_name, base_out_path); + + /* Unmount devoptab device. */ + devoptabUnmountDevice(mount_name); + +end: + /* Free data. */ + if (base_out_path) free(base_out_path); + if (pfs_ctx) pfsFreeContext(pfs_ctx); + if (romfs_ctx) romfsFreeContext(romfs_ctx); + if (fs_ctx) free(fs_ctx); + if (base_patch_nca_ctx) free(base_patch_nca_ctx); + + if (!success && g_appletStatus) + { + consolePrint("press any button to continue\n"); + utilsWaitForButtonPress(0); + } + + return success; +} + +static bool fsBrowser(const char *mount_name, const char *base_out_path) +{ + char dir_path[FS_MAX_PATH] = {0}; + size_t dir_path_len = 0; + + FsBrowserEntry *entries = NULL; + u32 entries_count = 0, depth = 0; + + u32 scroll = 0, selected = 0, highlighted = 0, page_size = 20; + + bool success = true; + + /* Get root directory entries. */ + snprintf(dir_path, MAX_ELEMENTS(dir_path), "%s:/", mount_name); + dir_path_len = strlen(dir_path); + + if (!(success = fsBrowserGetDirEntries(dir_path, &entries, &entries_count))) goto end; + + while((g_appletStatus = appletMainLoop())) + { + consoleClear(); + + consolePrint("press a to enter a directory / dump a file\n"); + consolePrint("press b to %s\n", depth > 0 ? "move back to the parent dir" : "exit the fs browser"); + consolePrint("press r to (un)highlight the selected entry\n"); + consolePrint("press l to invert the current selection\n"); + consolePrint("press zr to highlight all entries\n"); + consolePrint("press zl to unhighlight all entries\n"); + consolePrint("press y to dump the highlighted entries\n"); + consolePrint("use the sticks to scroll faster\n"); + consolePrint("press + to exit\n"); + consolePrint("______________________________\n\n"); + + consolePrint("entry: %u / %u\n", selected + 1, entries_count); + consolePrint("highlighted: %u / %u\n", highlighted, entries_count); + consolePrint("current path: %s\n", dir_path); + consolePrint("______________________________\n\n"); + + for(u32 i = scroll; i < entries_count; i++) + { + if (i >= (scroll + page_size)) break; + + FsBrowserEntry *cur_entry = &(entries[i]); + + consolePrint("%s", i == selected ? " -> " : " "); + + if (cur_entry->highlight) + { + consolePrintReversedColors("[%c] %s", cur_entry->dt.d_type == DT_DIR ? 'D' : 'F', cur_entry->dt.d_name); + if (cur_entry->dt.d_type == DT_REG) consolePrintReversedColors(" (%s)", cur_entry->size_str); + } else { + consolePrint("[%c] %s", cur_entry->dt.d_type == DT_DIR ? 'D' : 'F', cur_entry->dt.d_name); + if (cur_entry->dt.d_type == DT_REG) consolePrint(" (%s)", cur_entry->size_str); + } + + consolePrint("\n"); + } + + if (!entries_count) consolePrint("no elements available!"); + + consolePrint("\n"); + consoleRefresh(); + + u64 btn_down = 0, btn_held = 0; + + while((g_appletStatus = appletMainLoop())) + { + utilsScanPads(); + btn_down = utilsGetButtonsDown(); + btn_held = utilsGetButtonsHeld(); + if (btn_down || btn_held) break; + + utilsAppletLoopDelay(); + } + + if (!g_appletStatus) break; + + if ((btn_down & HidNpadButton_A) && entries_count) + { + FsBrowserEntry *selected_entry = &(entries[selected]); + + if (selected_entry->dt.d_type == DT_DIR) + { + /* Change directory. */ + snprintf(dir_path + dir_path_len, MAX_ELEMENTS(dir_path) - dir_path_len, "%s%s", depth > 0 ? "/" : "", selected_entry->dt.d_name); + + if (!(success = fsBrowserGetDirEntries(dir_path, &entries, &entries_count))) break; + + /* Update variables. */ + dir_path_len = strlen(dir_path); + scroll = selected = highlighted = 0; + depth++; + } else { + /* Dump file. */ + fsBrowserDumpFile(dir_path, selected_entry, base_out_path); + } + } else + if (btn_down & HidNpadButton_B) + { + if (depth > 0) + { + /* Go back to the parent directory. */ + char *ptr = strrchr(dir_path, '/'); + + if (depth > 1) + { + *ptr = '\0'; + } else { + *(++ptr) = '\0'; + } + + if (!(success = fsBrowserGetDirEntries(dir_path, &entries, &entries_count))) break; + + /* Update variables. */ + dir_path_len = strlen(dir_path); + scroll = selected = highlighted = 0; + depth--; + } else { + break; + } + } else + if ((btn_down & HidNpadButton_R) && entries_count) + { + /* (Un)highlight the selected entry. */ + FsBrowserEntry *selected_entry = &(entries[selected]); + selected_entry->highlight ^= 1; + highlighted += (selected_entry->highlight ? 1 : -1); + } else + if ((btn_down & HidNpadButton_L) && entries_count) + { + /* Invert current selection. */ + for(u32 i = 0; i < entries_count; i++) + { + FsBrowserEntry *cur_entry = &(entries[i]); + cur_entry->highlight ^= 1; + highlighted += (cur_entry->highlight ? 1 : -1); + } + } else + if ((btn_down & HidNpadButton_ZR) && entries_count) + { + /* Highlight all entries. */ + for(u32 i = 0; i < entries_count; i++) entries[i].highlight = true; + + /* Update counter. */ + highlighted = entries_count; + } else + if ((btn_down & HidNpadButton_ZL) && entries_count) + { + /* Unhighlight all entries. */ + for(u32 i = 0; i < entries_count; i++) entries[i].highlight = false; + + /* Reset counter. */ + highlighted = 0; + } else + if ((btn_down & HidNpadButton_Y) && entries_count && highlighted) + { + /* Dump highlighted entries. */ + fsBrowserDumpHighlightedEntries(dir_path, entries, entries_count, base_out_path); + + /* Unhighlight all entries. */ + for(u32 i = 0; i < entries_count; i++) entries[i].highlight = false; + + /* Reset counter. */ + highlighted = 0; + } else + if (((btn_down & HidNpadButton_Down) || (btn_held & (HidNpadButton_StickLDown | HidNpadButton_StickRDown))) && entries_count) + { + selected++; + + if (selected >= entries_count) + { + if (btn_down & HidNpadButton_Down) + { + scroll = 0; + selected = 0; + } else { + selected--; + } + } else + if (selected >= (scroll + (page_size / 2)) && entries_count > (scroll + page_size)) + { + scroll++; + } + } else + if (((btn_down & HidNpadButton_Up) || (btn_held & (HidNpadButton_StickLUp | HidNpadButton_StickRUp))) && entries_count) + { + selected--; + + if (selected == UINT32_MAX) + { + if (btn_down & HidNpadButton_Up) + { + selected = (entries_count - 1); + scroll = (entries_count >= page_size ? (entries_count - page_size) : 0); + } else { + selected = 0; + } + } else + if (selected < (scroll + (page_size / 2)) && scroll > 0) + { + scroll--; + } + } else + if (btn_down & HidNpadButton_Plus) + { + g_appletStatus = false; + break; + } + + utilsAppletLoopDelay(); + } + +end: + if (entries) free(entries); + + return success; +} + +static bool fsBrowserGetDirEntries(const char *dir_path, FsBrowserEntry **out_entries, u32 *out_entry_count) +{ + DIR *dp = NULL; + struct dirent *dt = NULL; + struct stat st = {0}; + FsBrowserEntry *entries = NULL, *entries_tmp = NULL; + char tmp_path[FS_MAX_PATH] = {0}; + u32 count = 0; + bool append_path_sep = (dir_path[strlen(dir_path) - 1] != '/'); + bool success = false; + + /* Free input pointer, if needed. */ + if (*out_entries) + { + free(*out_entries); + *out_entries = NULL; + } + + /* Open directory. */ + dp = opendir(dir_path); + if (!dp) + { + consolePrint("failed to open dir \"%s\"\n", dir_path); + goto end; + } + + /* Get entry count. */ + while((dt = readdir(dp))) + { + /* Skip "." and ".." entries. */ + if (!strcmp(dt->d_name, ".") || !strcmp(dt->d_name, "..") != 0) continue; + + /* Reallocate directory entries buffer. */ + if (!(entries_tmp = realloc(entries, (count + 1) * sizeof(FsBrowserEntry)))) + { + consolePrint("failed to allocate memory for dir entries in \"%s\"\n", dir_path); + goto end; + } + + entries = entries_tmp; + entries_tmp = NULL; + + /* Store entry data. */ + FsBrowserEntry *cur_entry = &(entries[count++]); + + memset(cur_entry, 0, sizeof(FsBrowserEntry)); + + if (dt->d_type == DT_REG) + { + /* Get file size. */ + snprintf(tmp_path, MAX_ELEMENTS(tmp_path), "%s%s%s", dir_path, append_path_sep ? "/" : "", dt->d_name); + stat(tmp_path, &st); + cur_entry->size = st.st_size; + utilsGenerateFormattedSizeString((double)st.st_size, cur_entry->size_str, sizeof(cur_entry->size_str)); + } + + memcpy(&(cur_entry->dt), dt, sizeof(struct dirent)); + } + + /* Short-circuit: handle empty directories. */ + if (!entries) + { + *out_entry_count = 0; + success = true; + goto end; + } + + /* Update output pointers. */ + *out_entries = entries; + *out_entry_count = count; + + /* Update return value. */ + success = true; + +end: + if (dp) closedir(dp); + + if (!success && entries) free(entries); + + return success; +} + +static bool fsBrowserDumpFile(const char *dir_path, const FsBrowserEntry *entry, const char *base_out_path) +{ + u64 free_space = 0; + + FsBrowserFileThreadData fs_browser_thread_data = {0}; + SharedThreadData *shared_thread_data = &(fs_browser_thread_data.shared_thread_data); + + u32 dev_idx = g_storageMenuElementOption.selected; + + bool success = false; + + shared_thread_data->total_size = entry->size; + + snprintf(path, MAX_ELEMENTS(path), "%s%s%s", dir_path, dir_path[strlen(dir_path) - 1] != '/' ? "/" : "", entry->dt.d_name); + + consoleClear(); + consolePrint("file path: %s\n", path); + consolePrint("file size: 0x%lX\n\n", entry->size); + + /* Open input file. */ + fs_browser_thread_data.src = fopen(path, "rb"); + if (!fs_browser_thread_data.src) + { + consolePrint("failed to open input file!\n"); + goto end; + } + + setvbuf(fs_browser_thread_data.src, NULL, _IONBF, 0); + + const char *dir_path_start = (strchr(dir_path, '/') + 1); + if (*dir_path_start) + { + snprintf(path, MAX_ELEMENTS(path), "%s/%s/%s", base_out_path, dir_path_start, entry->dt.d_name); + } else { + snprintf(path, MAX_ELEMENTS(path), "%s/%s", base_out_path, entry->dt.d_name); + } + + if (dev_idx == 1) + { + if (!waitForUsb()) goto end; + + if (!usbSendFileProperties(shared_thread_data->total_size, path)) + { + consolePrint("failed to send file properties for \"%s\"!\n", path); + goto end; + } + } else { + if (!utilsGetFileSystemStatsByPath(path, NULL, &free_space)) + { + consolePrint("failed to retrieve free space from selected device\n"); + goto end; + } + + if (shared_thread_data->total_size >= free_space) + { + consolePrint("dump size exceeds free space\n"); + goto end; + } + + utilsCreateDirectoryTree(path, false); + + if (dev_idx == 0) + { + if (shared_thread_data->total_size > FAT32_FILESIZE_LIMIT && !utilsCreateConcatenationFile(path)) + { + consolePrint("failed to create concatenation file for \"%s\"!\n", path); + goto end; + } + } else { + if (g_umsDevices[dev_idx - 2].fs_type < UsbHsFsDeviceFileSystemType_exFAT && shared_thread_data->total_size > FAT32_FILESIZE_LIMIT) + { + consolePrint("split dumps not supported for FAT12/16/32 volumes in UMS devices (yet)\n"); + goto end; + } + } + + shared_thread_data->fp = fopen(path, "wb"); + if (!shared_thread_data->fp) + { + consolePrint("failed to open \"%s\" for writing!\n", path); + goto end; + } + + setvbuf(shared_thread_data->fp, NULL, _IONBF, 0); + ftruncate(fileno(shared_thread_data->fp), (off_t)shared_thread_data->total_size); + } + + consoleRefresh(); + + success = spanDumpThreads(fsBrowserFileReadThreadFunc, genericWriteThreadFunc, &fs_browser_thread_data); + + if (success) + { + consolePrint("successfully saved file to \"%s\"\n", path); + consoleRefresh(); + } + +end: + if (shared_thread_data->fp) + { + fclose(shared_thread_data->fp); + shared_thread_data->fp = NULL; + + if (!success && dev_idx != 1) + { + if (dev_idx == 0) + { + utilsRemoveConcatenationFile(path); + utilsCommitSdCardFileSystemChanges(); + } else { + remove(path); + } + } + } + + if (fs_browser_thread_data.src) fclose(fs_browser_thread_data.src); + + consolePrint("press any button to continue\n"); + utilsWaitForButtonPress(0); + + return success; +} + +static bool fsBrowserDumpHighlightedEntries(const char *dir_path, const FsBrowserEntry *entries, u32 entries_count, const char *base_out_path) +{ + bool append_path_sep = (dir_path[strlen(dir_path) - 1] != '/'); + u64 data_size = 0; + + FsBrowserHighlightedEntriesThreadData fs_browser_thread_data = {0}; + SharedThreadData *shared_thread_data = &(fs_browser_thread_data.shared_thread_data); + + bool success = false; + + consoleClear(); + consolePrint("calculating dump size...\n"); + consoleRefresh(); + + /* Calculate dump size. */ + for(u32 i = 0; i < entries_count; i++) + { + const FsBrowserEntry *cur_entry = &(entries[i]); + if (!cur_entry->highlight) continue; + + if (cur_entry->dt.d_type == DT_DIR) + { + /* Get directory size. */ + u64 dir_size = 0; + snprintf(path, MAX_ELEMENTS(path), "%s%s%s", dir_path, append_path_sep ? "/" : "", cur_entry->dt.d_name); + + if (!utilsGetDirectorySize(path, &dir_size)) + { + consolePrint("failed to calculate size for dir \"%s\"\n", path); + goto end; + } + + /* Update dump size. */ + data_size += dir_size; + } else { + /* Update dump size. */ + data_size += cur_entry->size; + } + } + + fs_browser_thread_data.dir_path = dir_path; + fs_browser_thread_data.entries = entries; + fs_browser_thread_data.entries_count = entries_count; + fs_browser_thread_data.base_out_path = base_out_path; + shared_thread_data->total_size = data_size; + + consolePrint("dump size: 0x%lX\n", data_size); + consoleRefresh(); + + success = spanDumpThreads(fsBrowserHighlightedEntriesReadThreadFunc, genericWriteThreadFunc, &fs_browser_thread_data); + +end: + consolePrint("press any button to continue\n"); + utilsWaitForButtonPress(0); + + return success; +} + +static bool initializeNcaFsContext(void *userdata, u8 *out_section_type, bool *out_use_layeredfs_dir, NcaContext **out_base_patch_nca_ctx, void **out_fs_ctx) { NcaFsSectionContext *nca_fs_ctx = (NcaFsSectionContext*)userdata; NcaContext *nca_ctx = (nca_fs_ctx ? nca_fs_ctx->nca_ctx : NULL); @@ -3305,10 +3958,6 @@ static bool saveNintendoContentArchiveFsSection(void *userdata) NcaContext *base_patch_nca_ctx = NULL; NcaFsSectionContext *base_patch_nca_fs_ctx = NULL; - PartitionFileSystemContext pfs_ctx = {0}; - RomFileSystemContext romfs_ctx = {0}; - - bool write_raw_section = (bool)getNcaFsWriteRawSectionOption(); bool use_layeredfs_dir = (bool)getNcaFsUseLayeredFsDirOption(); bool success = false; @@ -3348,37 +3997,56 @@ static bool saveNintendoContentArchiveFsSection(void *userdata) if (section_type == NcaFsSectionType_PartitionFs) { /* Select the right NCA FS section context, depending on the sparse layer flag. */ - NcaFsSectionContext *pfs_nca_fs_ctx = (nca_fs_ctx->has_sparse_layer ? base_patch_nca_fs_ctx : nca_fs_ctx); + NcaFsSectionContext *pfs_nca_fs_ctx = ((title_type == NcmContentMetaType_Application && base_patch_nca_fs_ctx && base_patch_nca_fs_ctx->enabled) ? base_patch_nca_fs_ctx : nca_fs_ctx); /* Initialize PartitionFS context. */ - if (!pfsInitializeContext(&pfs_ctx, pfs_nca_fs_ctx)) + PartitionFileSystemContext *pfs_ctx = calloc(1, sizeof(PartitionFileSystemContext)); + if (!pfs_ctx) { - consolePrint("pfs initialize ctx failed!\n"); + consolePrint("pfs ctx alloc failed!\n"); goto end; } - success = (write_raw_section ? saveRawPartitionFsSection(&pfs_ctx, use_layeredfs_dir) : saveExtractedPartitionFsSection(&pfs_ctx, use_layeredfs_dir)); + if (!pfsInitializeContext(pfs_ctx, pfs_nca_fs_ctx)) + { + consolePrint("pfs initialize ctx failed!\n"); + free(pfs_ctx); + goto end; + } + + *out_fs_ctx = pfs_ctx; } else { /* Select the right base/patch NCA FS section contexts. */ NcaFsSectionContext *base_nca_fs_ctx = (section_type == NcaFsSectionType_PatchRomFs ? base_patch_nca_fs_ctx : nca_fs_ctx); NcaFsSectionContext *patch_nca_fs_ctx = (section_type == NcaFsSectionType_PatchRomFs ? nca_fs_ctx : base_patch_nca_fs_ctx); /* Initialize RomFS context. */ - if (!romfsInitializeContext(&romfs_ctx, base_nca_fs_ctx, patch_nca_fs_ctx)) + RomFileSystemContext *romfs_ctx = calloc(1, sizeof(RomFileSystemContext)); + if (!romfs_ctx) { - consolePrint("romfs initialize ctx failed!\n"); + consolePrint("romfs ctx alloc failed!\n"); goto end; } - success = (write_raw_section ? saveRawRomFsSection(&romfs_ctx, use_layeredfs_dir) : saveExtractedRomFsSection(&romfs_ctx, use_layeredfs_dir)); + if (!romfsInitializeContext(romfs_ctx, base_nca_fs_ctx, patch_nca_fs_ctx)) + { + consolePrint("romfs initialize ctx failed!\n"); + free(romfs_ctx); + goto end; + } + + *out_fs_ctx = romfs_ctx; } + /* Update output pointers. */ + *out_section_type = section_type; + *out_use_layeredfs_dir = use_layeredfs_dir; + *out_base_patch_nca_ctx = base_patch_nca_ctx; + + success = true; + end: - romfsFreeContext(&romfs_ctx); - - pfsFreeContext(&pfs_ctx); - - if (base_patch_nca_ctx) free(base_patch_nca_ctx); + if (!success && base_patch_nca_ctx) free(base_patch_nca_ctx); return success; } @@ -3928,7 +4596,7 @@ static void extractedHfsReadThreadFunc(void *arg) { fclose(shared_thread_data->fp); shared_thread_data->fp = NULL; - utilsCommitSdCardFileSystemChanges(); + if (dev_idx == 0) utilsCommitSdCardFileSystemChanges(); } } @@ -4316,7 +4984,7 @@ static void extractedPartitionFsReadThreadFunc(void *arg) { fclose(shared_thread_data->fp); shared_thread_data->fp = NULL; - utilsCommitSdCardFileSystemChanges(); + if (dev_idx == 0) utilsCommitSdCardFileSystemChanges(); } } @@ -4636,7 +5304,7 @@ static void extractedRomFsReadThreadFunc(void *arg) { fclose(shared_thread_data->fp); shared_thread_data->fp = NULL; - utilsCommitSdCardFileSystemChanges(); + if (dev_idx == 0) utilsCommitSdCardFileSystemChanges(); } } @@ -4785,6 +5453,364 @@ end: threadExit(); } +static void fsBrowserFileReadThreadFunc(void *arg) +{ + void *buf1 = NULL, *buf2 = NULL; + FsBrowserFileThreadData *fs_browser_thread_data = (FsBrowserFileThreadData*)arg; + SharedThreadData *shared_thread_data = &(fs_browser_thread_data->shared_thread_data); + FILE *src = fs_browser_thread_data->src; + + buf1 = usbAllocatePageAlignedBuffer(BLOCK_SIZE); + buf2 = usbAllocatePageAlignedBuffer(BLOCK_SIZE); + + if (!shared_thread_data->total_size || !src || !buf1 || !buf2) + { + shared_thread_data->read_error = true; + goto end; + } + + shared_thread_data->data = NULL; + shared_thread_data->data_size = 0; + + for(u64 offset = 0, blksize = BLOCK_SIZE; offset < shared_thread_data->total_size; offset += blksize) + { + if (blksize > (shared_thread_data->total_size - offset)) blksize = (shared_thread_data->total_size - offset); + + /* Check if the transfer has been cancelled by the user */ + if (shared_thread_data->transfer_cancelled) + { + condvarWakeAll(&g_writeCondvar); + break; + } + + /* Read current data chunk */ + shared_thread_data->read_error = (fread(buf1, 1, blksize, src) != blksize); + if (shared_thread_data->read_error) + { + condvarWakeAll(&g_writeCondvar); + break; + } + + /* Wait until the previous data chunk has been written */ + mutexLock(&g_fileMutex); + + if (shared_thread_data->data_size && !shared_thread_data->write_error) condvarWait(&g_readCondvar, &g_fileMutex); + + if (shared_thread_data->write_error) + { + mutexUnlock(&g_fileMutex); + break; + } + + /* Update shared object. */ + shared_thread_data->data = buf1; + shared_thread_data->data_size = blksize; + + /* Swap buffers. */ + buf1 = buf2; + buf2 = shared_thread_data->data; + + /* Wake up the write thread to continue writing data. */ + mutexUnlock(&g_fileMutex); + condvarWakeAll(&g_writeCondvar); + } + +end: + if (buf2) free(buf2); + if (buf1) free(buf1); + + threadExit(); +} + +static void fsBrowserHighlightedEntriesReadThreadFunc(void *arg) +{ + void *buf1 = NULL, *buf2 = NULL; + FsBrowserHighlightedEntriesThreadData *fs_browser_thread_data = (FsBrowserHighlightedEntriesThreadData*)arg; + SharedThreadData *shared_thread_data = &(fs_browser_thread_data->shared_thread_data); + + const char *dir_path = fs_browser_thread_data->dir_path; + const FsBrowserEntry *entries = fs_browser_thread_data->entries; + u32 entries_count = fs_browser_thread_data->entries_count; + const char *base_out_path = fs_browser_thread_data->base_out_path; + + u32 dev_idx = g_storageMenuElementOption.selected; + + buf1 = usbAllocatePageAlignedBuffer(BLOCK_SIZE); + buf2 = usbAllocatePageAlignedBuffer(BLOCK_SIZE); + + if (!shared_thread_data->total_size || !dir_path || !*dir_path || !entries || !entries_count || !base_out_path || !*base_out_path || !buf1 || !buf2) + { + shared_thread_data->read_error = true; + goto end; + } + + if (dev_idx != 1) + { + u64 free_space = 0; + + if (!utilsGetFileSystemStatsByPath(base_out_path, NULL, &free_space)) + { + consolePrint("failed to retrieve free space from selected device\n"); + shared_thread_data->read_error = true; + } + + if (!shared_thread_data->read_error && shared_thread_data->total_size >= free_space) + { + consolePrint("dump size exceeds free space\n"); + shared_thread_data->read_error = true; + } + } else { + if (!usbStartExtractedFsDump(shared_thread_data->total_size, base_out_path)) + { + consolePrint("failed to send extracted fs info to host\n"); + shared_thread_data->read_error = true; + } + } + + if (!shared_thread_data->read_error) + { + /* Dump highlighted entries. */ + fsBrowserHighlightedEntriesReadThreadLoop(shared_thread_data, dir_path, entries, entries_count, base_out_path, buf1, buf2); + + if (!shared_thread_data->read_error && !shared_thread_data->write_error && !shared_thread_data->transfer_cancelled) + { + if (dev_idx == 1) usbEndExtractedFsDump(); + + consolePrint("successfully saved dumped data to \"%s\"\n", base_out_path); + consoleRefresh(); + } + } else { + condvarWakeAll(&g_writeCondvar); + } + +end: + if (buf2) free(buf2); + if (buf1) free(buf1); + + threadExit(); +} + +static bool fsBrowserHighlightedEntriesReadThreadLoop(SharedThreadData *shared_thread_data, const char *dir_path, const FsBrowserEntry *entries, u32 entries_count, const char *base_out_path, void *buf1, void *buf2) +{ + bool append_path_sep = (dir_path[strlen(dir_path) - 1] != '/'); + u32 dev_idx = g_storageMenuElementOption.selected; + bool is_topmost = (entries && entries_count); /* If entry data is provided, it means we're dealing with the topmost directory. */ + const char *dir_path_start = (strchr(dir_path, '/') + 1); + + char *tmp_path = NULL; + FILE *src = NULL; + + /* Allocate memory for our temporary path. */ + tmp_path = calloc(sizeof(char), FS_MAX_PATH); + if ((shared_thread_data->read_error = (tmp_path == NULL))) + { + consolePrint("failed to allocate memory for path!\n"); + condvarWakeAll(&g_writeCondvar); + goto end; + } + + /* Get directory entries, if needed. */ + if (!is_topmost && (shared_thread_data->read_error = !fsBrowserGetDirEntries(dir_path, (FsBrowserEntry**)&entries, &entries_count))) + { + condvarWakeAll(&g_writeCondvar); + goto end; + } + + /* Loop through all highlighted entries. */ + for(u32 i = 0; i < entries_count; i++) + { + /* Get current entry. */ + const FsBrowserEntry *entry = &(entries[i]); + if (is_topmost && !entry->highlight) continue; + + /* Check if the transfer has been cancelled by the user. */ + if (shared_thread_data->transfer_cancelled) + { + condvarWakeAll(&g_writeCondvar); + break; + } + + if (dev_idx != 1) + { + /* Wait until the previous data chunk has been written */ + mutexLock(&g_fileMutex); + if (shared_thread_data->data_size && !shared_thread_data->write_error) condvarWait(&g_readCondvar, &g_fileMutex); + mutexUnlock(&g_fileMutex); + + if (shared_thread_data->write_error) break; + + /* Close file. */ + if (shared_thread_data->fp) + { + fclose(shared_thread_data->fp); + shared_thread_data->fp = NULL; + if (dev_idx == 0) utilsCommitSdCardFileSystemChanges(); + } + } + + /* Generate input path. */ + snprintf(tmp_path, FS_MAX_PATH, "%s%s%s", dir_path, append_path_sep ? "/" : "", entry->dt.d_name); + + if (entry->dt.d_type == DT_DIR) + { + /* Dump directory. */ + if (!fsBrowserHighlightedEntriesReadThreadLoop(shared_thread_data, tmp_path, NULL, 0, base_out_path, buf1, buf2)) break; + continue; + } + + /* Open input file. */ + src = fopen(tmp_path, "rb"); + if ((shared_thread_data->read_error = (src == NULL))) + { + consolePrint("failed to open file \"%s\" for reading!\n", tmp_path); + condvarWakeAll(&g_writeCondvar); + break; + } + + setvbuf(src, NULL, _IONBF, 0); + + /* Generate output path. */ + if (*dir_path_start) + { + snprintf(tmp_path, FS_MAX_PATH, "%s/%s/%s", base_out_path, dir_path_start, entry->dt.d_name); + } else { + snprintf(tmp_path, FS_MAX_PATH, "%s/%s", base_out_path, entry->dt.d_name); + } + + if (dev_idx == 1) + { + /* Wait until the previous data chunk has been written */ + mutexLock(&g_fileMutex); + if (shared_thread_data->data_size && !shared_thread_data->write_error) condvarWait(&g_readCondvar, &g_fileMutex); + mutexUnlock(&g_fileMutex); + + if (shared_thread_data->write_error) break; + + /* Send current file properties */ + shared_thread_data->read_error = !usbSendFileProperties(entry->size, tmp_path); + } else { + /* Create directory tree. */ + utilsCreateDirectoryTree(tmp_path, false); + + if (dev_idx == 0) + { + /* Create ConcatenationFile if we're dealing with a big file + SD card as the output storage. */ + if (entry->size > FAT32_FILESIZE_LIMIT && !utilsCreateConcatenationFile(tmp_path)) + { + consolePrint("failed to create concatenation file for \"%s\"!\n", tmp_path); + shared_thread_data->read_error = true; + } + } else { + /* Don't handle file chunks on FAT12/FAT16/FAT32 formatted UMS devices. */ + if (g_umsDevices[dev_idx - 2].fs_type < UsbHsFsDeviceFileSystemType_exFAT && entry->size > FAT32_FILESIZE_LIMIT) + { + consolePrint("split dumps not supported for FAT12/16/32 volumes in UMS devices (yet)\n"); + shared_thread_data->read_error = true; + } + } + + if (!shared_thread_data->read_error) + { + /* Open output file. */ + shared_thread_data->read_error = ((shared_thread_data->fp = fopen(tmp_path, "wb")) == NULL); + if (!shared_thread_data->read_error) + { + /* Set file size. */ + setvbuf(shared_thread_data->fp, NULL, _IONBF, 0); + ftruncate(fileno(shared_thread_data->fp), (off_t)entry->size); + } else { + consolePrint("failed to open \"%s\" for writing!\n", tmp_path); + } + } + } + + if (shared_thread_data->read_error) + { + condvarWakeAll(&g_writeCondvar); + break; + } + + /* Dump file. */ + for(u64 offset = 0, blksize = BLOCK_SIZE; offset < entry->size; offset += blksize) + { + if (blksize > (entry->size - offset)) blksize = (entry->size - offset); + + /* Check if the transfer has been cancelled by the user. */ + if (shared_thread_data->transfer_cancelled) + { + condvarWakeAll(&g_writeCondvar); + break; + } + + /* Read current file data chunk. */ + shared_thread_data->read_error = (fread(buf1, 1, blksize, src) != blksize); + if (shared_thread_data->read_error) + { + condvarWakeAll(&g_writeCondvar); + break; + } + + /* Wait until the previous file data chunk has been written. */ + mutexLock(&g_fileMutex); + + if (shared_thread_data->data_size && !shared_thread_data->write_error) condvarWait(&g_readCondvar, &g_fileMutex); + + if (shared_thread_data->write_error) + { + mutexUnlock(&g_fileMutex); + break; + } + + /* Update shared object. */ + shared_thread_data->data = buf1; + shared_thread_data->data_size = blksize; + + /* Swap buffers. */ + buf1 = buf2; + buf2 = shared_thread_data->data; + + /* Wake up the write thread to continue writing data. */ + mutexUnlock(&g_fileMutex); + condvarWakeAll(&g_writeCondvar); + } + + /* Close input file. */ + fclose(src); + src = NULL; + + if (shared_thread_data->read_error || shared_thread_data->write_error || shared_thread_data->transfer_cancelled) break; + } + + if (!shared_thread_data->read_error && !shared_thread_data->write_error && !shared_thread_data->transfer_cancelled) + { + /* Wait until the previous file data chunk has been written. */ + mutexLock(&g_fileMutex); + if (shared_thread_data->data_size) condvarWait(&g_readCondvar, &g_fileMutex); + mutexUnlock(&g_fileMutex); + } + +end: + if (shared_thread_data->fp) + { + fclose(shared_thread_data->fp); + shared_thread_data->fp = NULL; + + if ((shared_thread_data->read_error || shared_thread_data->write_error || shared_thread_data->transfer_cancelled) && dev_idx != 1) + { + utilsDeleteDirectoryRecursively(base_out_path); + if (dev_idx == 0) utilsCommitSdCardFileSystemChanges(); + } + } + + if (src) fclose(src); + + if (!is_topmost && entries) free((FsBrowserEntry*)entries); + + if (tmp_path) free(tmp_path); + + return !shared_thread_data->read_error; +} + static void genericWriteThreadFunc(void *arg) { SharedThreadData *shared_thread_data = (SharedThreadData*)arg; // UB but we don't care diff --git a/include/core/nxdt_utils.h b/include/core/nxdt_utils.h index c319e34..39bf451 100644 --- a/include/core/nxdt_utils.h +++ b/include/core/nxdt_utils.h @@ -157,6 +157,10 @@ bool utilsCreateConcatenationFile(const char *path); /// If 'create_last_element' is true, the last element from the provided path will be created as well. void utilsCreateDirectoryTree(const char *path, bool create_last_element); +/// Calculates the size of a directory by recursively traversing all of its child entries. +/// The provided path must be absolute and it must include the virtual device name it belongs to (e.g. "sdmc:/path/to/dir"). +bool utilsGetDirectorySize(const char *path, u64 *out_size); + /// Recursively deletes the directory located at the provided path and all of its contents. /// The provided path must be absolute and it must include the virtual device name it belongs to (e.g. "sdmc:/path/to/dir"). bool utilsDeleteDirectoryRecursively(const char *path); diff --git a/source/core/nxdt_utils.c b/source/core/nxdt_utils.c index c82cd2f..059736b 100644 --- a/source/core/nxdt_utils.c +++ b/source/core/nxdt_utils.c @@ -206,7 +206,7 @@ bool utilsInitializeResources(void) /* Load keyset. */ if (!keysLoadKeyset()) { - LOG_MSG_ERROR("Failed to load keyset!\nPlease update your keys file with Lockpick_RCM.\nYou can get an updated build at:" DISCORD_SERVER_URL); + LOG_MSG_ERROR("Failed to load keyset!\nPlease update your keys file with Lockpick_RCM.\nYou can get an updated build at: " DISCORD_SERVER_URL); break; } @@ -813,12 +813,84 @@ void utilsCreateDirectoryTree(const char *path, bool create_last_element) free(tmp); } +bool utilsGetDirectorySize(const char *path, u64 *out_size) +{ + u64 total_size = 0; + char *name_end = NULL, *entry_path = NULL; + DIR *dir = NULL; + struct dirent *entry = NULL; + struct stat st = {0}; + bool success = false; + + /* Sanity checks. */ + if (!path || !*path || !(name_end = strchr(path, ':')) || *(name_end + 1) != '/' || !*(name_end + 2) || !out_size) + { + LOG_MSG_ERROR("Invalid parameters!"); + return false; + } + + if (!(dir = opendir(path))) + { + LOG_MSG_ERROR("Failed to open directory \"%s\"! (%d).", path, errno); + goto end; + } + + if (!(entry_path = calloc(1, FS_MAX_PATH))) + { + LOG_MSG_ERROR("Failed to allocate memory for path buffer!"); + goto end; + } + + /* Read directory entries. */ + while((entry = readdir(dir))) + { + /* Skip current directory and parent directory entries. */ + if (!strcmp(".", entry->d_name) || !strcmp("..", entry->d_name)) continue; + + /* Generate path to the current entry. */ + snprintf(entry_path, FS_MAX_PATH, "%s/%s", path, entry->d_name); + + if (entry->d_type == DT_DIR) + { + /* Get directory size. */ + u64 dir_size = 0; + if (!utilsGetDirectorySize(entry_path, &dir_size)) goto end; + + /* Update size. */ + total_size += dir_size; + } else { + /* Get file properties. */ + if (stat(entry_path, &st) != 0) + { + LOG_MSG_ERROR("Failed to stat file \"%s\"! (%d).", entry_path, errno); + goto end; + } + + /* Update size. */ + total_size += st.st_size; + } + } + + /* Update output pointer. */ + *out_size = total_size; + + /* Update return value. */ + success = true; + +end: + if (entry_path) free(entry_path); + + if (dir) closedir(dir); + + return success; +} + bool utilsDeleteDirectoryRecursively(const char *path) { char *name_end = NULL, *entry_path = NULL; DIR *dir = NULL; struct dirent *entry = NULL; - bool success = true; + bool success = false; /* Sanity checks. */ if (!path || !*path || !(name_end = strchr(path, ':')) || *(name_end + 1) != '/' || !*(name_end + 2)) @@ -830,14 +902,12 @@ bool utilsDeleteDirectoryRecursively(const char *path) if (!(dir = opendir(path))) { LOG_MSG_ERROR("Failed to open directory \"%s\"! (%d).", path, errno); - success = false; goto end; } if (!(entry_path = calloc(1, FS_MAX_PATH))) { LOG_MSG_ERROR("Failed to allocate memory for path buffer!"); - success = false; goto end; } @@ -855,11 +925,7 @@ bool utilsDeleteDirectoryRecursively(const char *path) if (entry->d_type == DT_DIR) { /* Delete directory contents. */ - if (!utilsDeleteDirectoryRecursively(entry_path)) - { - success = false; - break; - } + if (!utilsDeleteDirectoryRecursively(entry_path)) goto end; /* Delete directory. */ status = rmdir(entry_path); @@ -870,20 +936,17 @@ bool utilsDeleteDirectoryRecursively(const char *path) if (status != 0) { - LOG_MSG_ERROR("Failed to delete \"%s\"! (%s, %d).", entry_path, entry->d_type == DT_DIR ? "dir" : "file", errno); - success = false; - break; + LOG_MSG_ERROR("Failed to delete %s \"%s\"! (%d).", entry->d_type == DT_DIR ? "directory" : "file", entry_path, errno); + goto end; } } - if (success) - { - closedir(dir); - dir = NULL; + /* Close topmost directory so we can delete it. */ + closedir(dir); + dir = NULL; - success = (rmdir(path) == 0); - if (!success) LOG_MSG_ERROR("Failed to delete topmost directory \"%s\"! (%d).", path, errno); - } + success = (rmdir(path) == 0); + if (!success) LOG_MSG_ERROR("Failed to delete topmost directory \"%s\"! (%d).", path, errno); end: if (entry_path) free(entry_path); diff --git a/source/devoptab/hfs_dev.c b/source/devoptab/hfs_dev.c index ae031d8..cb6b68b 100644 --- a/source/devoptab/hfs_dev.c +++ b/source/devoptab/hfs_dev.c @@ -119,7 +119,7 @@ static int hfsdev_open(struct _reent *r, void *fd, const char *path, int flags, /* Get truncated path. */ if (!(path = hfsdev_get_truncated_path(r, path))) DEVOPTAB_EXIT; - LOG_MSG_DEBUG("Opening \"%s:/%s\" with flags 0x%X.", dev_ctx->name, path, flags); + //LOG_MSG_DEBUG("Opening \"%s:/%s\" with flags 0x%X.", dev_ctx->name, path, flags); /* Reset file descriptor. */ memset(file, 0, sizeof(HashFileSystemFileState)); @@ -140,7 +140,7 @@ static int hfsdev_close(struct _reent *r, void *fd) /* Sanity check. */ if (!file) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Closing \"%s:/%s\".", dev_ctx->name, file->name); + //LOG_MSG_DEBUG("Closing \"%s:/%s\".", dev_ctx->name, file->name); /* Reset file descriptor. */ memset(file, 0, sizeof(HashFileSystemFileState)); @@ -158,7 +158,7 @@ static ssize_t hfsdev_read(struct _reent *r, void *fd, char *ptr, size_t len) /* Sanity check. */ if (!file || !ptr || !len) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Reading 0x%lX byte(s) at offset 0x%lX from \"%s:/%s\".", len, file->offset, dev_ctx->name, file->name); + //LOG_MSG_DEBUG("Reading 0x%lX byte(s) at offset 0x%lX from \"%s:/%s\".", len, file->offset, dev_ctx->name, file->name); /* Read file data. */ if (!hfsReadEntryData(fs_ctx, file->hfs_entry, ptr, len, file->offset)) DEVOPTAB_SET_ERROR_AND_EXIT(EIO); @@ -204,7 +204,7 @@ static off_t hfsdev_seek(struct _reent *r, void *fd, off_t pos, int dir) /* Don't allow positive seeks beyond the end of file. */ if (offset > (off_t)file->hfs_entry->size) DEVOPTAB_SET_ERROR_AND_EXIT(EOVERFLOW); - LOG_MSG_DEBUG("Seeking to offset 0x%lX from \"%s:/%s\".", offset, dev_ctx->name, file->name); + //LOG_MSG_DEBUG("Seeking to offset 0x%lX from \"%s:/%s\".", offset, dev_ctx->name, file->name); /* Adjust offset. */ file->offset = (u64)offset; @@ -221,7 +221,7 @@ static int hfsdev_fstat(struct _reent *r, void *fd, struct stat *st) /* Sanity check. */ if (!file || !st) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Getting file stats for \"%s:/%s\".", dev_ctx->name, file->name); + //LOG_MSG_DEBUG("Getting file stats for \"%s:/%s\".", dev_ctx->name, file->name); /* Fill stat info. */ hfsdev_fill_stat(st, file->index, file->hfs_entry, dev_ctx->mount_time); @@ -245,7 +245,7 @@ static int hfsdev_stat(struct _reent *r, const char *file, struct stat *st) /* Get truncated path. */ if (!(file = hfsdev_get_truncated_path(r, file))) DEVOPTAB_EXIT; - LOG_MSG_DEBUG("Getting file stats for \"%s:/%s\".", dev_ctx->name, file); + //LOG_MSG_DEBUG("Getting file stats for \"%s:/%s\".", dev_ctx->name, file); /* Get information about the requested Hash FS entry. */ if (!hfsGetEntryIndexByName(fs_ctx, file, &index) || !(hfs_entry = hfsGetEntryByIndex(fs_ctx, index))) DEVOPTAB_SET_ERROR_AND_EXIT(ENOENT); @@ -269,7 +269,7 @@ static DIR_ITER *hfsdev_diropen(struct _reent *r, DIR_ITER *dirState, const char if (!(path = hfsdev_get_truncated_path(r, path))) DEVOPTAB_EXIT; if (*path) DEVOPTAB_SET_ERROR_AND_EXIT(ENOENT); - LOG_MSG_DEBUG("Opening directory \"%s:/\".", dev_ctx->name); + //LOG_MSG_DEBUG("Opening directory \"%s:/\".", dev_ctx->name); /* Reset directory state. */ memset(dir, 0, sizeof(HashFileSystemDirectoryState)); @@ -286,7 +286,7 @@ static int hfsdev_dirreset(struct _reent *r, DIR_ITER *dirState) { HFS_DEV_INIT_DIR_VARS; - LOG_MSG_DEBUG("Resetting directory state for \"%s:/\".", dev_ctx->name); + //LOG_MSG_DEBUG("Resetting directory state for \"%s:/\".", dev_ctx->name); /* Reset directory state. */ dir->state = 0; @@ -308,7 +308,7 @@ static int hfsdev_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, /* Sanity check. */ if (!filename || !filestat) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Getting info for next directory entry in \"%s:/\" (state %u, index %u).", dev_ctx->name, dir->state, dir->index); + //LOG_MSG_DEBUG("Getting info for next directory entry in \"%s:/\" (state %u, index %u).", dev_ctx->name, dir->state, dir->index); if (dir->state < 2) { @@ -352,7 +352,7 @@ static int hfsdev_dirclose(struct _reent *r, DIR_ITER *dirState) { HFS_DEV_INIT_DIR_VARS; - LOG_MSG_DEBUG("Closing directory \"%s:/\".", dev_ctx->name); + //LOG_MSG_DEBUG("Closing directory \"%s:/\".", dev_ctx->name); /* Reset directory state. */ memset(dir, 0, sizeof(HashFileSystemDirectoryState)); @@ -374,7 +374,7 @@ static int hfsdev_statvfs(struct _reent *r, const char *path, struct statvfs *bu /* Sanity check. */ if (!buf) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Getting filesystem stats for \"%s:\"", dev_ctx->name); + //LOG_MSG_DEBUG("Getting filesystem stats for \"%s:\"", dev_ctx->name); /* Get Hash FS total data size. */ if (!hfsGetTotalDataSize(fs_ctx, &ext_fs_size)) DEVOPTAB_SET_ERROR_AND_EXIT(EIO); @@ -411,7 +411,7 @@ static const char *hfsdev_get_truncated_path(struct _reent *r, const char *path) if (!r || !path || !*path) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Input path: \"%s\".", path); + //LOG_MSG_DEBUG("Input path: \"%s\".", path); /* Move the path pointer to the start of the actual path. */ do { @@ -445,7 +445,7 @@ static const char *hfsdev_get_truncated_path(struct _reent *r, const char *path) if (!len && !path_sep_skipped) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); if (len >= FS_MAX_PATH) DEVOPTAB_SET_ERROR_AND_EXIT(ENAMETOOLONG); - LOG_MSG_DEBUG("Truncated path: \"%s\".", path); + //LOG_MSG_DEBUG("Truncated path: \"%s\".", path); end: DEVOPTAB_RETURN_PTR(path); diff --git a/source/devoptab/nxdt_romfs_dev.c b/source/devoptab/nxdt_romfs_dev.c index 686cf4f..18c852c 100644 --- a/source/devoptab/nxdt_romfs_dev.c +++ b/source/devoptab/nxdt_romfs_dev.c @@ -125,7 +125,7 @@ static int romfsdev_open(struct _reent *r, void *fd, const char *path, int flags /* Get truncated path. */ if (!(path = romfsdev_get_truncated_path(r, path))) DEVOPTAB_EXIT; - LOG_MSG_DEBUG("Opening \"%s:%s\" with flags 0x%X.", dev_ctx->name, path, flags); + //LOG_MSG_DEBUG("Opening \"%s:%s\" with flags 0x%X.", dev_ctx->name, path, flags); /* Reset file descriptor. */ memset(file, 0, sizeof(RomFileSystemFileState)); @@ -145,7 +145,7 @@ static int romfsdev_close(struct _reent *r, void *fd) /* Sanity check. */ if (!file) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Closing file \"%.*s\" from \"%s:\".", (int)file->file_entry->name_length, file->file_entry->name, dev_ctx->name); + //LOG_MSG_DEBUG("Closing file \"%.*s\" from \"%s:\".", (int)file->file_entry->name_length, file->file_entry->name, dev_ctx->name); /* Reset file descriptor. */ memset(file, 0, sizeof(RomFileSystemFileState)); @@ -163,8 +163,8 @@ static ssize_t romfsdev_read(struct _reent *r, void *fd, char *ptr, size_t len) /* Sanity check. */ if (!file || !ptr || !len) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Reading 0x%lX byte(s) at offset 0x%lX from file \"%.*s\" in \"%s:\".", len, file->data_offset, (int)file->file_entry->name_length, file->file_entry->name, \ - dev_ctx->name); + /*LOG_MSG_DEBUG("Reading 0x%lX byte(s) at offset 0x%lX from file \"%.*s\" in \"%s:\".", len, file->data_offset, (int)file->file_entry->name_length, file->file_entry->name, \ + dev_ctx->name);*/ /* Read file data. */ if (!romfsReadFileEntryData(fs_ctx, file->file_entry, ptr, len, file->data_offset)) DEVOPTAB_SET_ERROR_AND_EXIT(EIO); @@ -210,7 +210,7 @@ static off_t romfsdev_seek(struct _reent *r, void *fd, off_t pos, int dir) /* Don't allow positive seeks beyond the end of file. */ if (offset > (off_t)file->file_entry->size) DEVOPTAB_SET_ERROR_AND_EXIT(EOVERFLOW); - LOG_MSG_DEBUG("Seeking to offset 0x%lX from file \"%.*s\" in \"%s:\".", offset, (int)file->file_entry->name_length, file->file_entry->name, dev_ctx->name); + //LOG_MSG_DEBUG("Seeking to offset 0x%lX from file \"%.*s\" in \"%s:\".", offset, (int)file->file_entry->name_length, file->file_entry->name, dev_ctx->name); /* Adjust offset. */ file->data_offset = (u64)offset; @@ -228,7 +228,7 @@ static int romfsdev_fstat(struct _reent *r, void *fd, struct stat *st) /* Sanity check. */ if (!file || !st) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Getting stats for file \"%.*s\" in \"%s:\".", (int)file->file_entry->name_length, file->file_entry->name, dev_ctx->name); + //LOG_MSG_DEBUG("Getting stats for file \"%.*s\" in \"%s:\".", (int)file->file_entry->name_length, file->file_entry->name, dev_ctx->name); /* Fill stat info. */ romfsdev_fill_file_stat(st, fs_ctx, file->file_entry, dev_ctx->mount_time); @@ -251,7 +251,7 @@ static int romfsdev_stat(struct _reent *r, const char *file, struct stat *st) /* Get truncated path. */ if (!(file = romfsdev_get_truncated_path(r, file))) DEVOPTAB_EXIT; - LOG_MSG_DEBUG("Getting file stats for \"%s:%s\".", dev_ctx->name, file); + //LOG_MSG_DEBUG("Getting file stats for \"%s:%s\".", dev_ctx->name, file); /* Get information about the requested RomFS file entry. */ if (!(file_entry = romfsGetFileEntryByPath(fs_ctx, file))) DEVOPTAB_SET_ERROR_AND_EXIT(ENOENT); @@ -274,7 +274,7 @@ static DIR_ITER *romfsdev_diropen(struct _reent *r, DIR_ITER *dirState, const ch /* Get truncated path. */ if (!(path = romfsdev_get_truncated_path(r, path))) DEVOPTAB_EXIT; - LOG_MSG_DEBUG("Opening directory \"%s:%s\".", dev_ctx->name, path); + //LOG_MSG_DEBUG("Opening directory \"%s:%s\".", dev_ctx->name, path); /* Reset directory state. */ memset(dir, 0, sizeof(RomFileSystemDirectoryState)); @@ -297,7 +297,7 @@ static int romfsdev_dirreset(struct _reent *r, DIR_ITER *dirState) { ROMFS_DEV_INIT_DIR_VARS; - LOG_MSG_DEBUG("Resetting state for directory \"%.*s\" in \"%s:\".", (int)dir->dir_entry->name_length, dir->dir_entry->name, dev_ctx->name); + //LOG_MSG_DEBUG("Resetting state for directory \"%.*s\" in \"%s:\".", (int)dir->dir_entry->name_length, dir->dir_entry->name, dev_ctx->name); /* Reset directory state. */ dir->state = 0; @@ -317,8 +317,8 @@ static int romfsdev_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename /* Sanity check. */ if (!filename || !filestat) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Getting info for next entry from directory \"%.*s\" in \"%s:\" (state %u, cur_dir_offset 0x%lX, cur_file_offset 0x%lX).", \ - (int)dir->dir_entry->name_length, dir->dir_entry->name, dev_ctx->name, dir->state, dir->cur_dir_offset, dir->cur_file_offset); + /*LOG_MSG_DEBUG("Getting info for next entry from directory \"%.*s\" in \"%s:\" (state %u, cur_dir_offset 0x%lX, cur_file_offset 0x%lX).", \ + (int)dir->dir_entry->name_length, dir->dir_entry->name, dev_ctx->name, dir->state, dir->cur_dir_offset, dir->cur_file_offset);*/ if (dir->state < 2) { @@ -381,7 +381,7 @@ static int romfsdev_dirclose(struct _reent *r, DIR_ITER *dirState) { ROMFS_DEV_INIT_DIR_VARS; - LOG_MSG_DEBUG("Closing directory \"%.*s\" in \"%s:\".", (int)dir->dir_entry->name_length, dir->dir_entry->name, dev_ctx->name); + //LOG_MSG_DEBUG("Closing directory \"%.*s\" in \"%s:\".", (int)dir->dir_entry->name_length, dir->dir_entry->name, dev_ctx->name); /* Reset directory state. */ memset(dir, 0, sizeof(RomFileSystemDirectoryState)); @@ -403,7 +403,7 @@ static int romfsdev_statvfs(struct _reent *r, const char *path, struct statvfs * /* Sanity check. */ if (!buf) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Getting filesystem stats for \"%s:\"", dev_ctx->name); + //LOG_MSG_DEBUG("Getting filesystem stats for \"%s:\"", dev_ctx->name); /* Get RomFS total data size. */ if (!romfsGetTotalDataSize(fs_ctx, false, &ext_fs_size)) DEVOPTAB_SET_ERROR_AND_EXIT(EIO); @@ -439,7 +439,7 @@ static const char *romfsdev_get_truncated_path(struct _reent *r, const char *pat if (!r || !path || !*path) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Input path: \"%s\".", path); + //LOG_MSG_DEBUG("Input path: \"%s\".", path); /* Move the path pointer to the start of the actual path. */ do { @@ -468,7 +468,7 @@ static const char *romfsdev_get_truncated_path(struct _reent *r, const char *pat len = strlen(path); if (len >= FS_MAX_PATH) DEVOPTAB_SET_ERROR_AND_EXIT(ENAMETOOLONG); - LOG_MSG_DEBUG("Truncated path: \"%s\".", path); + //LOG_MSG_DEBUG("Truncated path: \"%s\".", path); end: DEVOPTAB_RETURN_PTR(path); diff --git a/source/devoptab/pfs_dev.c b/source/devoptab/pfs_dev.c index 751cda8..5aa9e5a 100644 --- a/source/devoptab/pfs_dev.c +++ b/source/devoptab/pfs_dev.c @@ -119,7 +119,7 @@ static int pfsdev_open(struct _reent *r, void *fd, const char *path, int flags, /* Get truncated path. */ if (!(path = pfsdev_get_truncated_path(r, path))) DEVOPTAB_EXIT; - LOG_MSG_DEBUG("Opening \"%s:/%s\" with flags 0x%X.", dev_ctx->name, path, flags); + //LOG_MSG_DEBUG("Opening \"%s:/%s\" with flags 0x%X.", dev_ctx->name, path, flags); /* Reset file descriptor. */ memset(file, 0, sizeof(PartitionFileSystemFileState)); @@ -140,7 +140,7 @@ static int pfsdev_close(struct _reent *r, void *fd) /* Sanity check. */ if (!file) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Closing \"%s:/%s\".", dev_ctx->name, file->name); + //LOG_MSG_DEBUG("Closing \"%s:/%s\".", dev_ctx->name, file->name); /* Reset file descriptor. */ memset(file, 0, sizeof(PartitionFileSystemFileState)); @@ -158,7 +158,7 @@ static ssize_t pfsdev_read(struct _reent *r, void *fd, char *ptr, size_t len) /* Sanity check. */ if (!file || !ptr || !len) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Reading 0x%lX byte(s) at offset 0x%lX from \"%s:/%s\".", len, file->offset, dev_ctx->name, file->name); + //LOG_MSG_DEBUG("Reading 0x%lX byte(s) at offset 0x%lX from \"%s:/%s\".", len, file->offset, dev_ctx->name, file->name); /* Read file data. */ if (!pfsReadEntryData(fs_ctx, file->pfs_entry, ptr, len, file->offset)) DEVOPTAB_SET_ERROR_AND_EXIT(EIO); @@ -204,7 +204,7 @@ static off_t pfsdev_seek(struct _reent *r, void *fd, off_t pos, int dir) /* Don't allow positive seeks beyond the end of file. */ if (offset > (off_t)file->pfs_entry->size) DEVOPTAB_SET_ERROR_AND_EXIT(EOVERFLOW); - LOG_MSG_DEBUG("Seeking to offset 0x%lX from \"%s:/%s\".", offset, dev_ctx->name, file->name); + //LOG_MSG_DEBUG("Seeking to offset 0x%lX from \"%s:/%s\".", offset, dev_ctx->name, file->name); /* Adjust offset. */ file->offset = (u64)offset; @@ -221,7 +221,7 @@ static int pfsdev_fstat(struct _reent *r, void *fd, struct stat *st) /* Sanity check. */ if (!file || !st) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Getting file stats for \"%s:/%s\".", dev_ctx->name, file->name); + //LOG_MSG_DEBUG("Getting file stats for \"%s:/%s\".", dev_ctx->name, file->name); /* Fill stat info. */ pfsdev_fill_stat(st, file->index, file->pfs_entry, dev_ctx->mount_time); @@ -245,7 +245,7 @@ static int pfsdev_stat(struct _reent *r, const char *file, struct stat *st) /* Get truncated path. */ if (!(file = pfsdev_get_truncated_path(r, file))) DEVOPTAB_EXIT; - LOG_MSG_DEBUG("Getting file stats for \"%s:/%s\".", dev_ctx->name, file); + //LOG_MSG_DEBUG("Getting file stats for \"%s:/%s\".", dev_ctx->name, file); /* Get information about the requested Partition FS entry. */ if (!pfsGetEntryIndexByName(fs_ctx, file, &index) || !(pfs_entry = pfsGetEntryByIndex(fs_ctx, index))) DEVOPTAB_SET_ERROR_AND_EXIT(ENOENT); @@ -269,7 +269,7 @@ static DIR_ITER *pfsdev_diropen(struct _reent *r, DIR_ITER *dirState, const char if (!(path = pfsdev_get_truncated_path(r, path))) DEVOPTAB_EXIT; if (*path) DEVOPTAB_SET_ERROR_AND_EXIT(ENOENT); - LOG_MSG_DEBUG("Opening directory \"%s:/\".", dev_ctx->name); + //LOG_MSG_DEBUG("Opening directory \"%s:/\".", dev_ctx->name); /* Reset directory state. */ memset(dir, 0, sizeof(PartitionFileSystemDirectoryState)); @@ -286,7 +286,7 @@ static int pfsdev_dirreset(struct _reent *r, DIR_ITER *dirState) { PFS_DEV_INIT_DIR_VARS; - LOG_MSG_DEBUG("Resetting directory state for \"%s:/\".", dev_ctx->name); + //LOG_MSG_DEBUG("Resetting directory state for \"%s:/\".", dev_ctx->name); /* Reset directory state. */ dir->state = 0; @@ -308,7 +308,7 @@ static int pfsdev_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, /* Sanity check. */ if (!filename || !filestat) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Getting info for next directory entry in \"%s:/\" (state %u, index %u).", dev_ctx->name, dir->state, dir->index); + //LOG_MSG_DEBUG("Getting info for next directory entry in \"%s:/\" (state %u, index %u).", dev_ctx->name, dir->state, dir->index); if (dir->state < 2) { @@ -352,7 +352,7 @@ static int pfsdev_dirclose(struct _reent *r, DIR_ITER *dirState) { PFS_DEV_INIT_DIR_VARS; - LOG_MSG_DEBUG("Closing directory \"%s:/\".", dev_ctx->name); + //LOG_MSG_DEBUG("Closing directory \"%s:/\".", dev_ctx->name); /* Reset directory state. */ memset(dir, 0, sizeof(PartitionFileSystemDirectoryState)); @@ -374,7 +374,7 @@ static int pfsdev_statvfs(struct _reent *r, const char *path, struct statvfs *bu /* Sanity check. */ if (!buf) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Getting filesystem stats for \"%s:\"", dev_ctx->name); + //LOG_MSG_DEBUG("Getting filesystem stats for \"%s:\"", dev_ctx->name); /* Get Partition FS total data size. */ if (!pfsGetTotalDataSize(fs_ctx, &ext_fs_size)) DEVOPTAB_SET_ERROR_AND_EXIT(EIO); @@ -411,7 +411,7 @@ static const char *pfsdev_get_truncated_path(struct _reent *r, const char *path) if (!r || !path || !*path) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); - LOG_MSG_DEBUG("Input path: \"%s\".", path); + //LOG_MSG_DEBUG("Input path: \"%s\".", path); /* Move the path pointer to the start of the actual path. */ do { @@ -445,7 +445,7 @@ static const char *pfsdev_get_truncated_path(struct _reent *r, const char *path) if (!len && !path_sep_skipped) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); if (len >= FS_MAX_PATH) DEVOPTAB_SET_ERROR_AND_EXIT(ENAMETOOLONG); - LOG_MSG_DEBUG("Truncated path: \"%s\".", path); + //LOG_MSG_DEBUG("Truncated path: \"%s\".", path); end: DEVOPTAB_RETURN_PTR(path);