From d40277603203040c4f6e951fdcae623f72eed514 Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Wed, 20 Dec 2023 20:32:48 +0100 Subject: [PATCH] Custom devoptab wrappers (part 1). This commit implements a devoptab wrapper for Partition FS sections within NCAs, using code from pfs.c/h. Other changes include: * codebase: use NX_IGNORE_ARG macro where needed. * hfs: slight tweaks to some of the static functions. * pfs: slight tweaks to some of the static functions. * pfs: use pfsIsValidContext() where needed. * utils: update utilsInitializeResources() to use __system_argc and __system_argv variables from libnx. * todo: update text file. --- Makefile | 4 +- code_templates/nxdt_rw_poc.c | 24 +- code_templates/xml_generator.c | 5 +- include/core/hfs.h | 15 +- include/core/nxdt_includes.h | 1 + include/core/nxdt_utils.h | 2 +- include/core/pfs.h | 31 +-- include/devoptab/nxdt_devoptab.h | 104 +++++++ include/devoptab/ro_dev.h | 55 ++++ include/download_task.hpp | 8 +- source/core/gamecard.c | 2 +- source/core/nxdt_utils.c | 25 +- source/core/pfs.c | 6 +- source/core/title.c | 2 +- source/core/ums.c | 2 +- source/core/usb.c | 2 +- source/devoptab/nxdt_devoptab.c | 253 +++++++++++++++++ source/devoptab/pfs_dev.c | 456 +++++++++++++++++++++++++++++++ source/devoptab/ro_dev.c | 149 ++++++++++ source/main.cpp | 5 +- todo.txt | 5 - 21 files changed, 1090 insertions(+), 66 deletions(-) create mode 100644 include/devoptab/nxdt_devoptab.h create mode 100644 include/devoptab/ro_dev.h create mode 100644 source/devoptab/nxdt_devoptab.c create mode 100644 source/devoptab/pfs_dev.c create mode 100644 source/devoptab/ro_dev.c diff --git a/Makefile b/Makefile index 96d943d..9bda507 100644 --- a/Makefile +++ b/Makefile @@ -68,10 +68,10 @@ BUILD_TIMESTAMP := $(strip $(shell date --utc '+%Y-%m-%d %T UTC')) TARGET := ${APP_TITLE} BUILD := build -SOURCES := source source/core source/fatfs +SOURCES := source source/core source/fatfs source/devoptab DATA := data ICON := romfs/icon/${APP_TITLE}.jpg -INCLUDES := include include/core include/fatfs +INCLUDES := include include/core include/fatfs include/devoptab ROMFS := romfs BOREALIS_PATH := libs/borealis diff --git a/code_templates/nxdt_rw_poc.c b/code_templates/nxdt_rw_poc.c index 7050425..3d2a628 100644 --- a/code_templates/nxdt_rw_poc.c +++ b/code_templates/nxdt_rw_poc.c @@ -28,6 +28,7 @@ #include "legal_info.h" #include "cert.h" #include "usb.h" +#include "nxdt_devoptab.h" #define BLOCK_SIZE USB_TRANSFER_BUFFER_SIZE #define WAIT_TIME_LIMIT 30 @@ -924,9 +925,12 @@ static char path[FS_MAX_PATH] = {0}; int main(int argc, char *argv[]) { + NX_IGNORE_ARG(argc); + NX_IGNORE_ARG(argv); + int ret = EXIT_SUCCESS; - if (!utilsInitializeResources(argc, (const char**)argv)) + if (!utilsInitializeResources()) { ret = EXIT_FAILURE; goto end; @@ -2323,7 +2327,7 @@ static bool resetSettings(void *userdata) static bool saveGameCardImage(void *userdata) { - (void)userdata; + NX_IGNORE_ARG(userdata); u64 gc_size = 0, free_space = 0; @@ -2480,7 +2484,7 @@ end: static bool saveGameCardHeader(void *userdata) { - (void)userdata; + NX_IGNORE_ARG(userdata); GameCardHeader gc_header = {0}; bool success = false; @@ -2514,7 +2518,7 @@ end: static bool saveGameCardCardInfo(void *userdata) { - (void)userdata; + NX_IGNORE_ARG(userdata); GameCardInfo gc_cardinfo = {0}; bool success = false; @@ -2548,7 +2552,7 @@ end: static bool saveGameCardCertificate(void *userdata) { - (void)userdata; + NX_IGNORE_ARG(userdata); FsGameCardCertificate gc_cert = {0}; bool success = false; @@ -2582,7 +2586,7 @@ end: static bool saveGameCardInitialData(void *userdata) { - (void)userdata; + NX_IGNORE_ARG(userdata); GameCardSecurityInformation gc_security_information = {0}; bool success = false; @@ -2615,7 +2619,7 @@ end: /* Instead, take a look at saveGameCardIdSet and saveGameCardUid which is a more standardised format of the Gamecard ID data. */ static bool saveGameCardSpecificData(void *userdata) { - (void)userdata; + NX_IGNORE_ARG(userdata); GameCardSecurityInformation gc_security_information = {0}; bool success = false; @@ -2643,7 +2647,7 @@ end: static bool saveGameCardIdSet(void *userdata) { - (void)userdata; + NX_IGNORE_ARG(userdata); FsGameCardIdSet id_set = {0}; bool success = false; @@ -2676,7 +2680,7 @@ end: static bool saveGameCardUid(void *userdata) { - (void)userdata; + NX_IGNORE_ARG(userdata); GameCardSecurityInformation gc_security_information = {0}; bool success = false; @@ -2870,7 +2874,7 @@ end: static bool saveConsoleLafwBlob(void *userdata) { - (void)userdata; + NX_IGNORE_ARG(userdata); u64 lafw_version = 0; LotusAsicFirmwareBlob lafw_blob = {0}; diff --git a/code_templates/xml_generator.c b/code_templates/xml_generator.c index 9f35b75..f058279 100644 --- a/code_templates/xml_generator.c +++ b/code_templates/xml_generator.c @@ -81,9 +81,12 @@ static void writeFile(void *buf, size_t buf_size, const char *path) int main(int argc, char *argv[]) { + NX_IGNORE_ARG(argc); + NX_IGNORE_ARG(argv); + int ret = EXIT_SUCCESS; - if (!utilsInitializeResources(argc, (const char**)argv)) + if (!utilsInitializeResources()) { ret = EXIT_FAILURE; goto out; diff --git a/include/core/hfs.h b/include/core/hfs.h index cd7f7d5..521b196 100644 --- a/include/core/hfs.h +++ b/include/core/hfs.h @@ -107,21 +107,18 @@ NX_INLINE bool hfsIsValidContext(HashFileSystemContext *ctx) NX_INLINE u32 hfsGetEntryCount(HashFileSystemContext *ctx) { - if (!ctx || !ctx->header_size || !ctx->header) return 0; - return ((HashFileSystemHeader*)ctx->header)->entry_count; + return (hfsIsValidContext(ctx) ? ((HashFileSystemHeader*)ctx->header)->entry_count : 0); } NX_INLINE HashFileSystemEntry *hfsGetEntryByIndex(HashFileSystemContext *ctx, u32 idx) { - if (idx >= hfsGetEntryCount(ctx)) return NULL; - return (HashFileSystemEntry*)(ctx->header + sizeof(HashFileSystemHeader) + (idx * sizeof(HashFileSystemEntry))); + return (idx < hfsGetEntryCount(ctx) ? (HashFileSystemEntry*)(ctx->header + sizeof(HashFileSystemHeader) + (idx * sizeof(HashFileSystemEntry))) : NULL); } NX_INLINE char *hfsGetNameTable(HashFileSystemContext *ctx) { u32 entry_count = hfsGetEntryCount(ctx); - if (!entry_count) return NULL; - return (char*)(ctx->header + sizeof(HashFileSystemHeader) + (entry_count * sizeof(HashFileSystemEntry))); + return (entry_count ? (char*)(ctx->header + sizeof(HashFileSystemHeader) + (entry_count * sizeof(HashFileSystemEntry))) : NULL); } NX_INLINE char *hfsGetEntryName(HashFileSystemContext *ctx, HashFileSystemEntry *fs_entry) @@ -135,15 +132,13 @@ NX_INLINE char *hfsGetEntryNameByIndex(HashFileSystemContext *ctx, u32 idx) { HashFileSystemEntry *fs_entry = hfsGetEntryByIndex(ctx, idx); char *name_table = hfsGetNameTable(ctx); - if (!fs_entry || !name_table) return NULL; - return (name_table + fs_entry->name_offset); + return ((fs_entry && name_table) ? (name_table + fs_entry->name_offset) : NULL); } NX_INLINE HashFileSystemEntry *hfsGetEntryByName(HashFileSystemContext *ctx, const char *name) { u32 idx = 0; - if (!hfsGetEntryIndexByName(ctx, name, &idx)) return NULL; - return hfsGetEntryByIndex(ctx, idx); + return (hfsGetEntryIndexByName(ctx, name, &idx) ? hfsGetEntryByIndex(ctx, idx) : NULL); } #ifdef __cplusplus diff --git a/include/core/nxdt_includes.h b/include/core/nxdt_includes.h index ab55ae6..8599491 100644 --- a/include/core/nxdt_includes.h +++ b/include/core/nxdt_includes.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include diff --git a/include/core/nxdt_utils.h b/include/core/nxdt_utils.h index d8305cc..c319e34 100644 --- a/include/core/nxdt_utils.h +++ b/include/core/nxdt_utils.h @@ -66,7 +66,7 @@ typedef struct { /// Resource initialization. /// Called at program startup. -bool utilsInitializeResources(const int program_argc, const char **program_argv); +bool utilsInitializeResources(void); /// Resource deinitialization. /// Called at program exit. diff --git a/include/core/pfs.h b/include/core/pfs.h index 2f98268..500f035 100644 --- a/include/core/pfs.h +++ b/include/core/pfs.h @@ -112,23 +112,27 @@ NX_INLINE void pfsFreeContext(PartitionFileSystemContext *ctx) memset(ctx, 0, sizeof(PartitionFileSystemContext)); } +/// Checks if the provided PartitionFileSystemContext is valid. +NX_INLINE bool pfsIsValidContext(PartitionFileSystemContext *ctx) +{ + return (ctx && ncaStorageIsValidContext(&(ctx->storage_ctx)) && ctx->nca_fs_ctx == ctx->storage_ctx.nca_fs_ctx && \ + ctx->storage_ctx.base_storage_type == NcaStorageBaseStorageType_Regular && ctx->size && ctx->header_size && ctx->header); +} + NX_INLINE u32 pfsGetEntryCount(PartitionFileSystemContext *ctx) { - if (!ctx || !ctx->header_size || !ctx->header) return 0; - return ((PartitionFileSystemHeader*)ctx->header)->entry_count; + return (pfsIsValidContext(ctx) ? ((PartitionFileSystemHeader*)ctx->header)->entry_count : 0); } NX_INLINE PartitionFileSystemEntry *pfsGetEntryByIndex(PartitionFileSystemContext *ctx, u32 idx) { - if (idx >= pfsGetEntryCount(ctx)) return NULL; - return (PartitionFileSystemEntry*)(ctx->header + sizeof(PartitionFileSystemHeader) + (idx * sizeof(PartitionFileSystemEntry))); + return (idx < pfsGetEntryCount(ctx) ? (PartitionFileSystemEntry*)(ctx->header + sizeof(PartitionFileSystemHeader) + (idx * sizeof(PartitionFileSystemEntry))) : NULL); } NX_INLINE char *pfsGetNameTable(PartitionFileSystemContext *ctx) { u32 entry_count = pfsGetEntryCount(ctx); - if (!entry_count) return NULL; - return (char*)(ctx->header + sizeof(PartitionFileSystemHeader) + (entry_count * sizeof(PartitionFileSystemEntry))); + return (entry_count ? (char*)(ctx->header + sizeof(PartitionFileSystemHeader) + (entry_count * sizeof(PartitionFileSystemEntry))) : NULL); } NX_INLINE char *pfsGetEntryName(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry) @@ -142,21 +146,18 @@ NX_INLINE char *pfsGetEntryNameByIndex(PartitionFileSystemContext *ctx, u32 idx) { PartitionFileSystemEntry *fs_entry = pfsGetEntryByIndex(ctx, idx); char *name_table = pfsGetNameTable(ctx); - if (!fs_entry || !name_table) return NULL; - return (name_table + fs_entry->name_offset); + return ((fs_entry && name_table) ? (name_table + fs_entry->name_offset) : NULL); } NX_INLINE PartitionFileSystemEntry *pfsGetEntryByName(PartitionFileSystemContext *ctx, const char *name) { u32 idx = 0; - if (!pfsGetEntryIndexByName(ctx, name, &idx)) return NULL; - return pfsGetEntryByIndex(ctx, idx); + return (pfsGetEntryIndexByName(ctx, name, &idx) ? pfsGetEntryByIndex(ctx, idx) : NULL); } NX_INLINE void pfsWriteEntryPatchToMemoryBuffer(PartitionFileSystemContext *ctx, NcaHierarchicalSha256Patch *patch, void *buf, u64 buf_size, u64 buf_offset) { - if (!ctx || !ncaStorageIsValidContext(&(ctx->storage_ctx)) || ctx->nca_fs_ctx != ctx->storage_ctx.nca_fs_ctx || \ - ctx->storage_ctx.base_storage_type != NcaStorageBaseStorageType_Regular) return; + if (!pfsIsValidContext(ctx)) return; ncaWriteHierarchicalSha256PatchToMemoryBuffer(ctx->nca_fs_ctx->nca_ctx, patch, buf, buf_size, buf_offset); } @@ -187,15 +188,13 @@ NX_INLINE u32 pfsGetEntryCountFromImageContext(PartitionFileSystemImageContext * NX_INLINE PartitionFileSystemEntry *pfsGetEntryByIndexFromImageContext(PartitionFileSystemImageContext *ctx, u32 idx) { - if (idx >= pfsGetEntryCountFromImageContext(ctx)) return NULL; - return &(ctx->entries[idx]); + return (idx < pfsGetEntryCountFromImageContext(ctx) ? &(ctx->entries[idx]) : NULL); } NX_INLINE char *pfsGetEntryNameByIndexFromImageContext(PartitionFileSystemImageContext *ctx, u32 idx) { PartitionFileSystemEntry *fs_entry = pfsGetEntryByIndexFromImageContext(ctx, idx); - if (!fs_entry || !ctx->name_table) return NULL; - return (ctx->name_table + fs_entry->name_offset); + return ((fs_entry && ctx->name_table) ? (ctx->name_table + fs_entry->name_offset) : NULL); } #ifdef __cplusplus diff --git a/include/devoptab/nxdt_devoptab.h b/include/devoptab/nxdt_devoptab.h new file mode 100644 index 0000000..d2d9ab5 --- /dev/null +++ b/include/devoptab/nxdt_devoptab.h @@ -0,0 +1,104 @@ +/* + * nxdt_devoptab.h + * + * Copyright (c) 2020-2023, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * nxdumptool is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#ifndef __NXDT_DEVOPTAB_H__ +#define __NXDT_DEVOPTAB_H__ + +#include "pfs.h" +#include "hfs.h" +#include "romfs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEVOPTAB_MOUNT_NAME_LENGTH 32 // Including NULL terminator. + +#define DEVOPTAB_DECL_ERROR_STATE int _errno = 0 +#define DEVOPTAB_DECL_DEV_CTX DevoptabDeviceContext *dev_ctx = (DevoptabDeviceContext*)r->deviceData +#define DEVOPTAB_DECL_FS_CTX(type) type *fs_ctx = (type*)dev_ctx->fs_ctx +#define DEVOPTAB_DECL_FILE_STATE(type) type *file = (type*)fd +#define DEVOPTAB_DECL_DIR_STATE(type) type *dir = (type*)dirState->dirStruct + +#define DEVOPTAB_SET_ERROR(x) r->_errno = _errno = (x) +#define DEVOPTAB_IS_ERROR_SET (_errno != 0) + +#define DEVOPTAB_EXIT goto end +#define DEVOPTAB_SET_ERROR_AND_EXIT(x) \ +do { \ + DEVOPTAB_SET_ERROR(x); \ + DEVOPTAB_EXIT; \ +} while(0) + +#define DEVOPTAB_RETURN_INT(x) return (DEVOPTAB_IS_ERROR_SET ? -1 : (x)) +#define DEVOPTAB_RETURN_PTR(x) return (DEVOPTAB_IS_ERROR_SET ? NULL : (x)) +#define DEVOPTAB_RETURN_BOOL return (DEVOPTAB_IS_ERROR_SET ? false : true) +#define DEVOPTAB_RETURN_UNSUPPORTED_OP r->_errno = ENOSYS; \ + return -1; + +#define DEVOPTAB_INIT_VARS(type, decl) devoptabControlMutex(true); \ + DEVOPTAB_DECL_ERROR_STATE; \ + decl + +#define DEVOPTAB_INIT_VARS_WITH_FILE_STATE(fs_type, file_type) DEVOPTAB_INIT_VARS(fs_type,); \ + DEVOPTAB_DECL_FILE_STATE(file_type) + +#define DEVOPTAB_INIT_VARS_WITH_DIR_STATE(fs_type, dir_type) DEVOPTAB_INIT_VARS(fs_type,); \ + DEVOPTAB_DECL_DIR_STATE(dir_type) + +#define DEVOPTAB_INIT_FS_ACCESS(type) DEVOPTAB_DECL_DEV_CTX; \ + DEVOPTAB_DECL_FS_CTX(type) + +#define DEVOPTAB_DEINIT_VARS devoptabControlMutex(false) + +typedef struct { + bool initialized; ///< Device initialization flag. + char name[DEVOPTAB_MOUNT_NAME_LENGTH]; ///< Mount name string, without a trailing colon (:). + time_t mount_time; ///< Mount time. + devoptab_t device; ///< Devoptab virtual device interface. Provides a way to use libcstd I/O calls on the mounted filesystem. + void *fs_ctx; ///< Pointer to actual type-specific filesystem context (PartitionFileSystemContext, HashFileSystemContext, RomFileSystemContext). +} DevoptabDeviceContext; + +/// Mounts a virtual Partition FS device using the provided Partition FS context and a mount name. +bool devoptabMountPartitionFileSystemDevice(PartitionFileSystemContext *pfs_ctx, const char *name); + +/// Mounts a virtual Hash FS device using the provided Hash FS context and a mount name. +bool devoptabMountHashFileSystemDevice(HashFileSystemContext *hfs_ctx, const char *name); + +/// Mounts a virtual RomFS device using the provided RomFS context and a mount name. +bool devoptabMountRomFileSystemDevice(RomFileSystemContext *romfs_ctx, const char *name); + +/// Unmounts a previously mounted virtual device. +void devoptabUnmountDevice(const char *name); + +/// Unmounts all previously mounted virtual devices. +void devoptabUnmountAllDevices(void); + +/// (Un)locks the devoptab mutex. Used by filesystem-specific devoptab interfaces. +void devoptabControlMutex(bool lock); + +#ifdef __cplusplus +} +#endif + +#endif /* __NXDT_DEVOPTAB_H__ */ diff --git a/include/devoptab/ro_dev.h b/include/devoptab/ro_dev.h new file mode 100644 index 0000000..d603dc0 --- /dev/null +++ b/include/devoptab/ro_dev.h @@ -0,0 +1,55 @@ +/* + * ro_dev.h + * + * Copyright (c) 2020-2023, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * nxdumptool is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#ifndef __RO_DEV__ +#define __RO_DEV__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Bogus functions for devoptab write operations -- all of these set errno to ENOSYS and return -1. */ +/* We don't provide support for relative directories, so chdir is discarded as well. */ + +ssize_t rodev_write(struct _reent *r, void *fd, const char *ptr, size_t len); +int rodev_link(struct _reent *r, const char *existing, const char *newLink); +int rodev_unlink(struct _reent *r, const char *name); +int rodev_chdir(struct _reent *r, const char *name); +int rodev_rename(struct _reent *r, const char *oldName, const char *newName); +int rodev_mkdir(struct _reent *r, const char *path, int mode); +int rodev_ftruncate(struct _reent *r, void *fd, off_t len); +int rodev_fsync(struct _reent *r, void *fd); +int rodev_chmod(struct _reent *r, const char *path, mode_t mode); +int rodev_fchmod(struct _reent *r, void *fd, mode_t mode); +int rodev_rmdir(struct _reent *r, const char *name); +int rodev_utimes(struct _reent *r, const char *filename, const struct timeval times[2]); +long rodev_fpathconf(struct _reent *r, void *fd, int name); +long rodev_pathconf(struct _reent *r, const char *path, int name); +int rodev_symlink(struct _reent *r, const char *target, const char *linkpath); +ssize_t rodev_readlink(struct _reent *r, const char *path, char *buf, size_t bufsiz); + +#ifdef __cplusplus +} +#endif + +#endif /* __RO_DEV__ */ diff --git a/include/download_task.hpp b/include/download_task.hpp index 58be2d0..f6cd196 100644 --- a/include/download_task.hpp +++ b/include/download_task.hpp @@ -150,8 +150,8 @@ namespace nxdt::tasks template int DownloadTask::HttpProgressCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { - (void)ultotal; - (void)ulnow; + NX_IGNORE_ARG(ultotal); + NX_IGNORE_ARG(ulnow); DownloadTaskProgress progress = {0}; DownloadTask* task = static_cast*>(clientp); @@ -173,7 +173,7 @@ namespace nxdt::tasks template void DownloadTask::onCancelled(const Result& result) { - (void)result; + NX_IGNORE_ARG(result); /* Pause task handler. */ this->task_handler->pause(); @@ -185,7 +185,7 @@ namespace nxdt::tasks template void DownloadTask::onPostExecute(const Result& result) { - (void)result; + NX_IGNORE_ARG(result); /* Fire task handler immediately to get the last result from loopCallback(), then pause it. */ this->task_handler->fireNow(); diff --git a/source/core/gamecard.c b/source/core/gamecard.c index 2796b59..4f77af4 100644 --- a/source/core/gamecard.c +++ b/source/core/gamecard.c @@ -662,7 +662,7 @@ static void gamecardDestroyDetectionThread(void) static void gamecardDetectionThreadFunc(void *arg) { - (void)arg; + NX_IGNORE_ARG(arg); Result rc = 0; int idx = 0; diff --git a/source/core/nxdt_utils.c b/source/core/nxdt_utils.c index 9d16011..c82cd2f 100644 --- a/source/core/nxdt_utils.c +++ b/source/core/nxdt_utils.c @@ -30,6 +30,7 @@ #include "title.h" #include "bfttf.h" #include "nxdt_bfsar.h" +#include "nxdt_devoptab.h" #include "fatfs/ff.h" /// Reference: https://docs.microsoft.com/en-us/windows/win32/fileio/filesystem-functionality-comparison#limits. @@ -48,6 +49,9 @@ typedef struct { /* Global variables. */ +extern int __system_argc; +extern char **__system_argv; + static bool g_resourcesInit = false; static Mutex g_resourcesMutex = 0; @@ -95,7 +99,7 @@ static bool g_appUpdated = false; /* Function prototypes. */ -static void _utilsGetLaunchPath(int program_argc, const char **program_argv); +static void _utilsGetLaunchPath(void); static void _utilsGetCustomFirmwareType(void); @@ -115,7 +119,7 @@ static size_t utilsGetUtf8StringLimit(const char *str, size_t str_size, size_t b static char utilsConvertHexDigitToBinary(char c); -bool utilsInitializeResources(const int program_argc, const char **program_argv) +bool utilsInitializeResources(void) { Result rc = 0; bool ret = false; @@ -129,7 +133,7 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv) appletLockExit(); /* Retrieve pointer to the application launch path. */ - _utilsGetLaunchPath(program_argc, program_argv); + _utilsGetLaunchPath(); /* Retrieve pointer to the SD card FsFileSystem element. */ if (!(g_sdCardFileSystem = fsdevGetDeviceFileSystem(DEVOPTAB_SDMC_DEVICE))) @@ -289,6 +293,9 @@ void utilsCloseResources(void) { SCOPED_LOCK(&g_resourcesMutex) { + /* Unmount all custom devoptab devices. */ + devoptabUnmountAllDevices(); + /* Unset long running process state. */ utilsSetLongRunningProcessState(false); @@ -1169,15 +1176,15 @@ bool utilsIsApplicationUpdatable(const char *version, const char *commit_hash) return ret; } -static void _utilsGetLaunchPath(int program_argc, const char **program_argv) +static void _utilsGetLaunchPath(void) { - if (program_argc <= 0 || !program_argv) return; + if (__system_argc <= 0 || !__system_argv) return; - for(int i = 0; i < program_argc; i++) + for(int i = 0; i < __system_argc; i++) { - if (program_argv[i] && !strncmp(program_argv[i], DEVOPTAB_SDMC_DEVICE "/", strlen(DEVOPTAB_SDMC_DEVICE))) + if (__system_argv[i] && !strncmp(__system_argv[i], DEVOPTAB_SDMC_DEVICE "/", strlen(DEVOPTAB_SDMC_DEVICE))) { - g_appLaunchPath = program_argv[i]; + g_appLaunchPath = __system_argv[i]; break; } } @@ -1279,7 +1286,7 @@ static void utilsOverclockSystem(bool overclock) static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param) { - (void)param; + NX_IGNORE_ARG(param); /* Don't proceed if we're not dealing with a desired hook type. */ if (hook != AppletHookType_OnOperationMode && hook != AppletHookType_OnPerformanceMode) return; diff --git a/source/core/pfs.c b/source/core/pfs.c index 2f85a7c..7e2706e 100644 --- a/source/core/pfs.c +++ b/source/core/pfs.c @@ -122,7 +122,7 @@ end: bool pfsReadPartitionData(PartitionFileSystemContext *ctx, void *out, u64 read_size, u64 offset) { - if (!ctx || !ncaStorageIsValidContext(&(ctx->storage_ctx)) || !ctx->size || !out || !read_size || (offset + read_size) > ctx->size) + if (!pfsIsValidContext(ctx) || !out || !read_size || (offset + read_size) > ctx->size) { LOG_MSG_ERROR("Invalid parameters!"); return false; @@ -226,8 +226,8 @@ bool pfsGetTotalDataSize(PartitionFileSystemContext *ctx, u64 *out_size) bool pfsGenerateEntryPatch(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out) { - if (!ctx || !ncaStorageIsValidContext(&(ctx->storage_ctx)) || ctx->storage_ctx.base_storage_type != NcaStorageBaseStorageType_Regular || !ctx->header_size || !ctx->header || \ - !fs_entry || !fs_entry->size || (fs_entry->offset + fs_entry->size) > ctx->size || !data || !data_size || (data_offset + data_size) > fs_entry->size || !out) + if (!pfsIsValidContext(ctx) || !fs_entry || !fs_entry->size || (fs_entry->offset + fs_entry->size) > ctx->size || !data || !data_size || \ + (data_offset + data_size) > fs_entry->size || !out) { LOG_MSG_ERROR("Invalid parameters!"); return false; diff --git a/source/core/title.c b/source/core/title.c index 9c9affd..b5d97ec 100644 --- a/source/core/title.c +++ b/source/core/title.c @@ -2303,7 +2303,7 @@ static void titleDestroyGameCardInfoThread(void) static void titleGameCardInfoThreadFunc(void *arg) { - (void)arg; + NX_IGNORE_ARG(arg); Result rc = 0; int idx = 0; diff --git a/source/core/ums.c b/source/core/ums.c index 8620b86..54272b1 100644 --- a/source/core/ums.c +++ b/source/core/ums.c @@ -139,7 +139,7 @@ static void umsFreeDeviceData(void) static void umsPopulateCallback(const UsbHsFsDevice *devices, u32 device_count, void *user_data) { - (void)user_data; + NX_IGNORE_ARG(user_data); SCOPED_LOCK(&g_umsMutex) { diff --git a/source/core/usb.c b/source/core/usb.c index cd788a1..e88fd45 100644 --- a/source/core/usb.c +++ b/source/core/usb.c @@ -566,7 +566,7 @@ static void usbDestroyDetectionThread(void) static void usbDetectionThreadFunc(void *arg) { - (void)arg; + NX_IGNORE_ARG(arg); Result rc = 0; int idx = 0; diff --git a/source/devoptab/nxdt_devoptab.c b/source/devoptab/nxdt_devoptab.c new file mode 100644 index 0000000..a27ed7e --- /dev/null +++ b/source/devoptab/nxdt_devoptab.c @@ -0,0 +1,253 @@ +/* + * nxdt_devoptab.c + * + * Copyright (c) 2020-2023, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * nxdumptool is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "nxdt_utils.h" +#include "nxdt_devoptab.h" + +#define DEVOPTAB_DEVICE_COUNT 4 + +/* Type definitions. */ + +typedef enum { + DevoptabDeviceType_PartitionFileSystem = 0, + DevoptabDeviceType_HashFileSystem = 1, + DevoptabDeviceType_RomFileSystem = 2, + DevoptabDeviceType_Count = 3 ///< Total values supported by this enum. +} DevoptabDeviceType; + +/* Global variables. */ + +static Mutex g_devoptabMutex = 0; +static DevoptabDeviceContext g_devoptabDevices[DEVOPTAB_DEVICE_COUNT] = {0}; +static const u32 g_devoptabDeviceCount = MAX_ELEMENTS(g_devoptabDevices); + +/* Function prototypes. */ + +const devoptab_t *pfsdev_get_devoptab(); + +static bool devoptabMountDevice(void *fs_ctx, const char *name, u8 type); +static DevoptabDeviceContext *devoptabFindDevice(const char *name); +static void devoptabResetDevice(DevoptabDeviceContext *dev_ctx); + +bool devoptabMountPartitionFileSystemDevice(PartitionFileSystemContext *pfs_ctx, const char *name) +{ + if (!pfsIsValidContext(pfs_ctx) || !name || !*name) + { + LOG_MSG_ERROR("Invalid parameters!"); + return false; + } + + bool ret = false; + + SCOPED_LOCK(&g_devoptabMutex) + { + ret = devoptabMountDevice(pfs_ctx, name, DevoptabDeviceType_PartitionFileSystem); + } + + return ret; +} + +bool devoptabMountHashFileSystemDevice(HashFileSystemContext *hfs_ctx, const char *name) +{ + if (!hfsIsValidContext(hfs_ctx) || !name || !*name) + { + LOG_MSG_ERROR("Invalid parameters!"); + return false; + } + + bool ret = false; + + SCOPED_LOCK(&g_devoptabMutex) + { + ret = devoptabMountDevice(hfs_ctx, name, DevoptabDeviceType_HashFileSystem); + } + + return ret; +} + +bool devoptabMountRomFileSystemDevice(RomFileSystemContext *romfs_ctx, const char *name) +{ + if (!romfsIsValidContext(romfs_ctx) || !name || !*name) + { + LOG_MSG_ERROR("Invalid parameters!"); + return false; + } + + bool ret = false; + + SCOPED_LOCK(&g_devoptabMutex) + { + ret = devoptabMountDevice(romfs_ctx, name, DevoptabDeviceType_RomFileSystem); + } + + return ret; +} + +void devoptabUnmountDevice(const char *name) +{ + if (!name || !*name) + { + LOG_MSG_ERROR("Invalid parameters!"); + return; + } + + SCOPED_LOCK(&g_devoptabMutex) + { + /* Get device entry. */ + DevoptabDeviceContext *dev_ctx = devoptabFindDevice(name); + if (dev_ctx) + { + /* Reset device. */ + devoptabResetDevice(dev_ctx); + } else { + LOG_MSG_ERROR("Error: unable to find devoptab device \"%s\".", name); + } + } +} + +void devoptabUnmountAllDevices(void) +{ + SCOPED_LOCK(&g_devoptabMutex) + { + /* Loop through all of our device entries and reset them all. */ + for(u32 i = 0; i < g_devoptabDeviceCount; i++) devoptabResetDevice(&(g_devoptabDevices[i])); + } +} + +void devoptabControlMutex(bool lock) +{ + bool locked = mutexIsLockedByCurrentThread(&g_devoptabMutex); + + if (!locked && lock) + { + mutexLock(&g_devoptabMutex); + } else + if (locked && !lock) + { + mutexUnlock(&g_devoptabMutex); + } +} + +static bool devoptabMountDevice(void *fs_ctx, const char *name, u8 type) +{ + if (!fs_ctx || !name || !*name || type >= DevoptabDeviceType_Count) + { + LOG_MSG_ERROR("Invalid parameters!"); + return false; + } + + DevoptabDeviceContext *dev_ctx = NULL; + const devoptab_t *device = NULL; + bool ret = false; + + /* Retrieve a pointer to the first unused device entry. */ + if (!(dev_ctx = devoptabFindDevice(NULL))) + { + LOG_MSG_ERROR("Error: unable to find an empty device slot for \"%s\" (type 0x%02X).", name, type); + return false; + } + + /* Retrieve a pointer to the appropriate devoptab interface for this filesystem type. */ + switch(type) + { + case DevoptabDeviceType_PartitionFileSystem: + device = pfsdev_get_devoptab(); + break; + case DevoptabDeviceType_HashFileSystem: + break; + case DevoptabDeviceType_RomFileSystem: + break; + default: + break; + } + + if (!device) + { + LOG_MSG_ERROR("Error: unable to retrieve a devoptab interface for \"%s\" (type 0x%02X).", name, type); + return false; + } + + /* Populate device entry. */ + snprintf(dev_ctx->name, MAX_ELEMENTS(dev_ctx->name), "%s", name); + + dev_ctx->mount_time = time(NULL); + + memcpy(&(dev_ctx->device), device, sizeof(devoptab_t)); + dev_ctx->device.name = dev_ctx->name; + dev_ctx->device.deviceData = dev_ctx; + + dev_ctx->fs_ctx = fs_ctx; + + /* Add devoptab device. */ + int res = AddDevice(&(dev_ctx->device)); + if (res < 0) + { + LOG_MSG_ERROR("Error: AddDevice failed! (%d).", res); + goto end; + } + + /* Update flags. */ + ret = dev_ctx->initialized = true; + +end: + if (!ret) memset(dev_ctx, 0, sizeof(DevoptabDeviceContext)); + + return ret; +} + +static DevoptabDeviceContext *devoptabFindDevice(const char *name) +{ + DevoptabDeviceContext *dev_ctx = NULL; + + for(u32 i = 0; i < g_devoptabDeviceCount; i++) + { + dev_ctx = &(g_devoptabDevices[i]); + + if (!name) + { + /* Find an unused entry. */ + if (!dev_ctx->initialized) break; + } else + if (dev_ctx->initialized) + { + /* Find an entry with a matching mount name. */ + if (!strncmp(dev_ctx->name, name, sizeof(dev_ctx->name))) break; + } + + dev_ctx = NULL; + } + + return dev_ctx; +} + +static void devoptabResetDevice(DevoptabDeviceContext *dev_ctx) +{ + if (!dev_ctx || !dev_ctx->initialized) return; + + char tmp_name[DEVOPTAB_MOUNT_NAME_LENGTH + 2] = {0}; + + snprintf(tmp_name, MAX_ELEMENTS(tmp_name), "%s:", dev_ctx->name); + RemoveDevice(tmp_name); + + memset(dev_ctx, 0, sizeof(DevoptabDeviceContext)); + + LOG_MSG_DEBUG("Successfully unmounted device \"%s\".", tmp_name); +} diff --git a/source/devoptab/pfs_dev.c b/source/devoptab/pfs_dev.c new file mode 100644 index 0000000..9a87dab --- /dev/null +++ b/source/devoptab/pfs_dev.c @@ -0,0 +1,456 @@ +/* + * pfs_dev.c + * + * Loosely based on fs_dev.c from libnx, et al. + * + * Copyright (c) 2020-2023, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * nxdumptool is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "nxdt_utils.h" +#include "nxdt_devoptab.h" +#include "ro_dev.h" + +/* Helper macros. */ + +#define PFS_DEV_INIT_VARS DEVOPTAB_INIT_VARS(PartitionFileSystemContext,) +#define PFS_DEV_INIT_FILE_VARS DEVOPTAB_INIT_VARS_WITH_FILE_STATE(PartitionFileSystemContext, PartitionFileSystemFileState) +#define PFS_DEV_INIT_DIR_VARS DEVOPTAB_INIT_VARS_WITH_DIR_STATE(PartitionFileSystemContext, PartitionFileSystemDirectoryState) +#define PFS_DEV_INIT_FS_ACCESS DEVOPTAB_INIT_FS_ACCESS(PartitionFileSystemContext) + +/* Type definitions. */ + +typedef struct { + PartitionFileSystemEntry *pfs_entry; ///< Partition FS entry metadata. + const char *name; ///< Entry name. + u64 offset; ///< Current offset within Partition FS entry data. +} PartitionFileSystemFileState; + +typedef struct { + u32 index; ///< Current Partition FS entry index. +} PartitionFileSystemDirectoryState; + +/* Function prototypes. */ + +static int pfsdev_open(struct _reent *r, void *fd, const char *path, int flags, int mode); +static int pfsdev_close(struct _reent *r, void *fd); +static ssize_t pfsdev_read(struct _reent *r, void *fd, char *ptr, size_t len); +static off_t pfsdev_seek(struct _reent *r, void *fd, off_t pos, int dir); +static int pfsdev_fstat(struct _reent *r, void *fd, struct stat *st); +static int pfsdev_stat(struct _reent *r, const char *file, struct stat *st); +static DIR_ITER* pfsdev_diropen(struct _reent *r, DIR_ITER *dirState, const char *path); +static int pfsdev_dirreset(struct _reent *r, DIR_ITER *dirState); +static int pfsdev_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat); +static int pfsdev_dirclose(struct _reent *r, DIR_ITER *dirState); +static int pfsdev_statvfs(struct _reent *r, const char *path, struct statvfs *buf); + +static const char *pfsdev_get_truncated_path(struct _reent *r, const char *path); + +static void pfsdev_fill_stat(struct stat *st, const PartitionFileSystemEntry *pfs_entry, time_t mount_time); + +/* Global variables. */ + +static const devoptab_t pfsdev_devoptab = { + .name = NULL, + .structSize = sizeof(PartitionFileSystemFileState), + .open_r = pfsdev_open, + .close_r = pfsdev_close, + .write_r = rodev_write, ///< Not supported by Partition FS sections. + .read_r = pfsdev_read, + .seek_r = pfsdev_seek, + .fstat_r = pfsdev_fstat, + .stat_r = pfsdev_stat, + .link_r = rodev_link, ///< Not supported by Partition FS sections. + .unlink_r = rodev_unlink, ///< Not supported by Partition FS sections. + .chdir_r = rodev_chdir, ///< No need to deal with cwd shenanigans, so we won't support it. + .rename_r = rodev_rename, ///< Not supported by Partition FS sections. + .mkdir_r = rodev_mkdir, ///< Not supported by Partition FS sections. + .dirStateSize = sizeof(PartitionFileSystemDirectoryState), + .diropen_r = pfsdev_diropen, ///< Partition FS sections don't support directories, but we'll allow operations on the FS root. + .dirreset_r = pfsdev_dirreset, ///< Partition FS sections don't support directories, but we'll allow operations on the FS root. + .dirnext_r = pfsdev_dirnext, ///< Partition FS sections don't support directories, but we'll allow operations on the FS root. + .dirclose_r = pfsdev_dirclose, ///< Partition FS sections don't support directories, but we'll allow operations on the FS root. + .statvfs_r = pfsdev_statvfs, + .ftruncate_r = rodev_ftruncate, ///< Not supported by Partition FS sections. + .fsync_r = rodev_fsync, ///< Not supported by Partition FS sections. + .deviceData = NULL, + .chmod_r = rodev_chmod, ///< Not supported by Partition FS sections. + .fchmod_r = rodev_fchmod, ///< Not supported by Partition FS sections. + .rmdir_r = rodev_rmdir, ///< Not supported by Partition FS sections. + .lstat_r = pfsdev_stat, ///< Symlinks aren't supported, so we'll just alias lstat() to stat(). + .utimes_r = rodev_utimes, ///< Not supported by Partition FS sections. + .fpathconf_r = rodev_fpathconf, ///< Not supported by Partition FS sections. + .pathconf_r = rodev_pathconf, ///< Not supported by Partition FS sections. + .symlink_r = rodev_symlink, ///< Not supported by Partition FS sections. + .readlink_r = rodev_readlink ///< Not supported by Partition FS sections. +}; + +const devoptab_t *pfsdev_get_devoptab() +{ + return &pfsdev_devoptab; +} + +static int pfsdev_open(struct _reent *r, void *fd, const char *path, int flags, int mode) +{ + NX_IGNORE_ARG(mode); + + PFS_DEV_INIT_FILE_VARS; + PFS_DEV_INIT_FS_ACCESS; + + /* Validate input. */ + if (!file || (flags & (O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC | O_EXCL))) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + + /* 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); + + /* Reset file descriptor. */ + memset(file, 0, sizeof(PartitionFileSystemFileState)); + + /* Get Partition FS entry. */ + if (!(file->pfs_entry = pfsGetEntryByName(fs_ctx, path)) || !(file->name = pfsGetEntryName(fs_ctx, file->pfs_entry))) DEVOPTAB_SET_ERROR(ENOENT); + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(0); +} + +static int pfsdev_close(struct _reent *r, void *fd) +{ + PFS_DEV_INIT_FILE_VARS; + + /* Sanity check. */ + if (!file) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + +#if LOG_LEVEL == LOG_LEVEL_DEBUG + DEVOPTAB_DECL_DEV_CTX; + LOG_MSG_DEBUG("Closing \"%s:/%s\".", dev_ctx->name, file->name); +#endif + + /* Reset file descriptor. */ + memset(file, 0, sizeof(PartitionFileSystemFileState)); + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(0); +} + +static ssize_t pfsdev_read(struct _reent *r, void *fd, char *ptr, size_t len) +{ + PFS_DEV_INIT_FILE_VARS; + PFS_DEV_INIT_FS_ACCESS; + + /* 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); + + /* Read file data. */ + if (!pfsReadEntryData(fs_ctx, file->pfs_entry, ptr, len, file->offset)) DEVOPTAB_SET_ERROR_AND_EXIT(EIO); + + /* Adjust offset. */ + file->offset += len; + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT((ssize_t)len); +} + +static off_t pfsdev_seek(struct _reent *r, void *fd, off_t pos, int dir) +{ + off_t offset = 0; + + PFS_DEV_INIT_FILE_VARS; + + /* Sanity check. */ + if (!file) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + + /* Find the offset to seek from. */ + switch(dir) + { + case SEEK_SET: /* Set absolute position relative to zero (start offset). */ + break; + case SEEK_CUR: /* Set position relative to the current position. */ + offset = (off_t)file->offset; + break; + case SEEK_END: /* Set position relative to EOF. */ + offset = (off_t)file->pfs_entry->size; + break; + default: /* Invalid option. */ + DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + } + + /* Don't allow negative seeks beyond the beginning of file. */ + if (pos < 0 && offset < -pos) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + + /* Calculate actual offset. */ + offset += pos; + + /* Don't allow positive seeks beyond the end of file. */ + if (offset > (off_t)file->pfs_entry->size) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + +#if LOG_LEVEL == LOG_LEVEL_DEBUG + DEVOPTAB_DECL_DEV_CTX; + LOG_MSG_DEBUG("Seeking to offset 0x%lX from \"%s:/%s\".", offset, dev_ctx->name, file->name); +#endif + + /* Adjust offset. */ + file->offset = (u64)offset; + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(offset); +} + +static int pfsdev_fstat(struct _reent *r, void *fd, struct stat *st) +{ + PFS_DEV_INIT_FILE_VARS; + DEVOPTAB_DECL_DEV_CTX; + + /* 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); + + /* Fill stat info. */ + pfsdev_fill_stat(st, file->pfs_entry, dev_ctx->mount_time); + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(0); +} + +static int pfsdev_stat(struct _reent *r, const char *file, struct stat *st) +{ + PartitionFileSystemEntry *pfs_entry = NULL; + + PFS_DEV_INIT_VARS; + PFS_DEV_INIT_FS_ACCESS; + + /* Sanity check. */ + if (!st) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + + /* 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); + + /* Get Partition FS entry. */ + if (!(pfs_entry = pfsGetEntryByName(fs_ctx, file))) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + + /* Fill stat info. */ + pfsdev_fill_stat(st, pfs_entry, dev_ctx->mount_time); + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(0); +} + +static DIR_ITER *pfsdev_diropen(struct _reent *r, DIR_ITER *dirState, const char *path) +{ + DIR_ITER *ret = NULL; + + PFS_DEV_INIT_DIR_VARS; + + /* Get truncated path. */ + /* We can only work with the FS root here, so we won't accept anything else. */ + if (!(path = pfsdev_get_truncated_path(r, path))) DEVOPTAB_EXIT; + if (*path) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + +#if LOG_LEVEL == LOG_LEVEL_DEBUG + DEVOPTAB_DECL_DEV_CTX; + LOG_MSG_DEBUG("Opening directory \"%s:/\".", dev_ctx->name); +#endif + + /* Reset directory state. */ + memset(dir, 0, sizeof(PartitionFileSystemDirectoryState)); + + /* Update return value. */ + ret = dirState; + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_PTR(ret); +} + +static int pfsdev_dirreset(struct _reent *r, DIR_ITER *dirState) +{ + PFS_DEV_INIT_DIR_VARS; + +#if LOG_LEVEL == LOG_LEVEL_DEBUG + DEVOPTAB_DECL_DEV_CTX; + LOG_MSG_DEBUG("Resetting directory state for \"%s:/\".", dev_ctx->name); +#endif + + /* Reset directory state. */ + dir->index = 0; + + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(0); +} + +static int pfsdev_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) +{ + PartitionFileSystemEntry *pfs_entry = NULL; + const char *fname = NULL; + + PFS_DEV_INIT_DIR_VARS; + PFS_DEV_INIT_FS_ACCESS; + + /* Sanity check. */ + if (!filename || !filestat) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + + LOG_MSG_DEBUG("Getting info for next directory entry in \"%s:/\" (index %u).", dev_ctx->name, dir->index); + + /* Check if we haven't reached EOD. */ + if (dir->index >= pfsGetEntryCount(fs_ctx)) DEVOPTAB_SET_ERROR_AND_EXIT(ENOENT); + + /* Get Partition FS entry. */ + if (!(pfs_entry = pfsGetEntryByIndex(fs_ctx, dir->index)) || !(fname = pfsGetEntryName(fs_ctx, pfs_entry))) DEVOPTAB_SET_ERROR_AND_EXIT(EIO); + + /* Copy filename. */ + strcpy(filename, fname); + + /* Fill stat info. */ + pfsdev_fill_stat(filestat, pfs_entry, dev_ctx->mount_time); + + /* Adjust index. */ + dir->index++; + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(0); +} + +static int pfsdev_dirclose(struct _reent *r, DIR_ITER *dirState) +{ + PFS_DEV_INIT_DIR_VARS; + +#if LOG_LEVEL == LOG_LEVEL_DEBUG + DEVOPTAB_DECL_DEV_CTX; + LOG_MSG_DEBUG("Closing directory \"%s:/\".", dev_ctx->name); +#endif + + /* Reset directory state. */ + memset(dir, 0, sizeof(PartitionFileSystemDirectoryState)); + + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(0); +} + +static int pfsdev_statvfs(struct _reent *r, const char *path, struct statvfs *buf) +{ + NX_IGNORE_ARG(path); + + u64 ext_fs_size = 0; + + PFS_DEV_INIT_VARS; + PFS_DEV_INIT_FS_ACCESS; + + /* Sanity check. */ + if (!buf) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + + 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); + + /* Fill filesystem stats. */ + memset(buf, 0, sizeof(struct statvfs)); + + buf->f_bsize = 1; + buf->f_frsize = 1; + buf->f_blocks = ext_fs_size; + buf->f_bfree = 0; + buf->f_bavail = 0; + buf->f_files = 0; + buf->f_ffree = 0; + buf->f_favail = 0; + buf->f_fsid = 0; + buf->f_flag = ST_NOSUID; + buf->f_namemax = FS_MAX_PATH; + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(0); +} + +static const char *pfsdev_get_truncated_path(struct _reent *r, const char *path) +{ + const u8 *p = (const u8*)path; + ssize_t units = 0; + u32 code = 0; + size_t len = 0; + bool path_sep_skipped = false; + + DEVOPTAB_DECL_ERROR_STATE; + + if (!r || !path || !*path) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + + LOG_MSG_DEBUG("Input path: \"%s\".", path); + + /* Move the path pointer to the start of the actual path. */ + do { + units = decode_utf8(&code, p); + if (units < 0) DEVOPTAB_SET_ERROR_AND_EXIT(EILSEQ); + p += units; + } while(code >= ' ' && code != ':'); + + /* We found a colon; p points to the actual path. */ + if (code == ':') path = (const char*)p; + + /* Skip the leading slash, if available. */ + if (path[0] == '/') + { + path++; + path_sep_skipped = true; + } + + /* Make sure there are no more colons or slashes and that the remainder of the string is valid UTF-8. */ + p = (const u8*)path; + + do { + units = decode_utf8(&code, p); + if (units < 0) DEVOPTAB_SET_ERROR_AND_EXIT(EILSEQ); + if (code == ':' || code == '/') DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + p += units; + } while(code >= ' '); + + /* Verify fixed path length. */ + len = strlen(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); + +end: + DEVOPTAB_RETURN_PTR(path); +} + +static void pfsdev_fill_stat(struct stat *st, const PartitionFileSystemEntry *pfs_entry, time_t mount_time) +{ + /* Clear stat struct. */ + memset(st, 0, sizeof(struct stat)); + + /* Fill stat struct. */ + /* We're always dealing with a file entry. */ + st->st_nlink = 1; + st->st_size = (off_t)pfs_entry->size; + st->st_mode = (S_IFREG | S_IRUSR | S_IRGRP | S_IROTH); + st->st_atime = st->st_mtime = st->st_ctime = mount_time; +} diff --git a/source/devoptab/ro_dev.c b/source/devoptab/ro_dev.c new file mode 100644 index 0000000..2e5dac4 --- /dev/null +++ b/source/devoptab/ro_dev.c @@ -0,0 +1,149 @@ +/* + * ro_dev.c + * + * Copyright (c) 2020-2023, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * nxdumptool is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "nxdt_utils.h" +#include "nxdt_devoptab.h" + +ssize_t rodev_write(struct _reent *r, void *fd, const char *ptr, size_t len) +{ + NX_IGNORE_ARG(fd); + NX_IGNORE_ARG(ptr); + NX_IGNORE_ARG(len); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + +int rodev_link(struct _reent *r, const char *existing, const char *newLink) +{ + NX_IGNORE_ARG(existing); + NX_IGNORE_ARG(newLink); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + +int rodev_unlink(struct _reent *r, const char *name) +{ + NX_IGNORE_ARG(name); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + +int rodev_chdir(struct _reent *r, const char *name) +{ + NX_IGNORE_ARG(name); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + +int rodev_rename(struct _reent *r, const char *oldName, const char *newName) +{ + NX_IGNORE_ARG(oldName); + NX_IGNORE_ARG(newName); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + +int rodev_mkdir(struct _reent *r, const char *path, int mode) +{ + NX_IGNORE_ARG(path); + NX_IGNORE_ARG(mode); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + +int rodev_ftruncate(struct _reent *r, void *fd, off_t len) +{ + NX_IGNORE_ARG(fd); + NX_IGNORE_ARG(len); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + +int rodev_fsync(struct _reent *r, void *fd) +{ + NX_IGNORE_ARG(fd); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + +int rodev_chmod(struct _reent *r, const char *path, mode_t mode) +{ + NX_IGNORE_ARG(path); + NX_IGNORE_ARG(mode); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + +int rodev_fchmod(struct _reent *r, void *fd, mode_t mode) +{ + NX_IGNORE_ARG(fd); + NX_IGNORE_ARG(mode); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + +int rodev_rmdir(struct _reent *r, const char *name) +{ + NX_IGNORE_ARG(name); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + +int rodev_utimes(struct _reent *r, const char *filename, const struct timeval times[2]) +{ + NX_IGNORE_ARG(filename); + NX_IGNORE_ARG(times); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + +long rodev_fpathconf(struct _reent *r, void *fd, int name) +{ + NX_IGNORE_ARG(fd); + NX_IGNORE_ARG(name); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + +long rodev_pathconf(struct _reent *r, const char *path, int name) +{ + NX_IGNORE_ARG(path); + NX_IGNORE_ARG(name); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + +int rodev_symlink(struct _reent *r, const char *target, const char *linkpath) +{ + NX_IGNORE_ARG(target); + NX_IGNORE_ARG(linkpath); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + +ssize_t rodev_readlink(struct _reent *r, const char *path, char *buf, size_t bufsiz) +{ + NX_IGNORE_ARG(path); + NX_IGNORE_ARG(buf); + NX_IGNORE_ARG(bufsiz); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} diff --git a/source/main.cpp b/source/main.cpp index c977819..31049be 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -30,11 +30,14 @@ bool g_borealisInitialized = false; int main(int argc, char *argv[]) { + NX_IGNORE_ARG(argc); + NX_IGNORE_ARG(argv); + /* Set scope guard to clean up resources at exit. */ ON_SCOPE_EXIT { utilsCloseResources(); }; /* Initialize application resources. */ - if (!utilsInitializeResources(argc, (const char**)argv)) return EXIT_FAILURE; + if (!utilsInitializeResources()) return EXIT_FAILURE; /* Load Borealis translation files. */ brls::i18n::loadTranslations(); diff --git a/todo.txt b/todo.txt index 7a87fe9..4c8e642 100644 --- a/todo.txt +++ b/todo.txt @@ -56,8 +56,3 @@ list of top level functions designed to alter nca data in order of (possible) us * calls ncaWriteHierarchicalSha256PatchToMemoryBuffer / ncaWriteHierarchicalIntegrityPatchToMemoryBuffer * cnmtUpdateContentInfo (used to update content entry info in the raw cnmt copy after dumping each one - ignores the current content if its a meta nca) - -minor steps to take into account: - - * check if rights_id_available == true and titlekey_retrieved == false (preload handling) - * actually, just inform the user about it - this is being handled