diff --git a/include/core/bis_storage.h b/include/core/bis_storage.h new file mode 100644 index 0000000..b4c2c84 --- /dev/null +++ b/include/core/bis_storage.h @@ -0,0 +1,54 @@ +/* + * bis_storage.h + * + * Copyright (c) 2020-2024, 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 __BIS_STORAGE_H__ +#define __BIS_STORAGE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/// Mounts an eMMC BIS partition using its ID and provides a pointer to a string that holds its mount name, which can be used to carry out FS operations. +/// Only eMMC BIS partition IDs `CalibrationFile` (28) through `System` (31) are supported. +bool bisStorageMountPartition(u8 bis_partition_id, const char **out_mount_name); + +/// Unmounts a previously mounted eMMC BIS partition. +/// Only eMMC BIS partition IDs `CalibrationFile` (28) through `System` (31) are supported. +void bisStorageUnmountPartition(u8 bis_partition_id); + +/// Unmounts all previously mounted eMMC BIS partitions. +void bisStorageUnmountAllPartitions(void); + +/// Returns a pointer to a FsStorage object that matches the provided FatFs drive number, or NULL if it hasn't been mounted. +/// Only used by FatFs's diskio operations. +FsStorage *bisStorageGetFsStorageByFatFsDriveNumber(u8 drive_number); + +/// (Un)locks the BIS storage mutex. Can be used to block other threads and prevent them from altering the internal status of this interface. +/// Use with caution. +void bisStorageControlMutex(bool lock); + +#ifdef __cplusplus +} +#endif + +#endif /* __BIS_STORAGE_H__ */ diff --git a/include/core/devoptab/nxdt_devoptab.h b/include/core/devoptab/nxdt_devoptab.h index 99120a9..8b8d2dc 100644 --- a/include/core/devoptab/nxdt_devoptab.h +++ b/include/core/devoptab/nxdt_devoptab.h @@ -27,54 +27,56 @@ #include "../pfs.h" #include "../hfs.h" #include "../romfs.h" +#include "../fatfs/ff.h" #ifdef __cplusplus extern "C" { #endif -#define DEVOPTAB_MOUNT_NAME_LENGTH 32 // Including NULL terminator. +#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_INIT_ERROR_STATE r->_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_SET_ERROR(x) r->_errno = (x) +#define DEVOPTAB_IS_ERROR_SET (r->_errno != 0) -#define DEVOPTAB_EXIT goto end -#define DEVOPTAB_SET_ERROR_AND_EXIT(x) \ +#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_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) devoptabControlMutex(true); \ - DEVOPTAB_DECL_ERROR_STATE; \ - DEVOPTAB_DECL_DEV_CTX; \ - if (!dev_ctx->initialized) DEVOPTAB_SET_ERROR_AND_EXIT(ENODEV); +#define DEVOPTAB_INIT_VARS devoptabControlMutex(true); \ + DEVOPTAB_INIT_ERROR_STATE; \ + DEVOPTAB_DECL_DEV_CTX; \ + if (!dev_ctx->initialized) DEVOPTAB_SET_ERROR_AND_EXIT(ENODEV) -#define DEVOPTAB_INIT_FILE_VARS(fs_type, file_type) DEVOPTAB_INIT_VARS(fs_type); \ - DEVOPTAB_DECL_FILE_STATE(file_type) +#define DEVOPTAB_INIT_FILE_VARS(type) DEVOPTAB_INIT_VARS; \ + DEVOPTAB_DECL_FILE_STATE(type) -#define DEVOPTAB_INIT_DIR_VARS(fs_type, dir_type) DEVOPTAB_INIT_VARS(fs_type); \ - DEVOPTAB_DECL_DIR_STATE(dir_type) +#define DEVOPTAB_INIT_DIR_VARS(type) DEVOPTAB_INIT_VARS; \ + if (!dirState) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); \ + DEVOPTAB_DECL_DIR_STATE(type) -#define DEVOPTAB_DEINIT_VARS devoptabControlMutex(false) +#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). + void *fs_ctx; ///< Pointer to actual type-specific filesystem context (PartitionFileSystemContext, HashFileSystemContext, RomFileSystemContext, FATFS). } DevoptabDeviceContext; /// Mounts a virtual Partition FS device using the provided Partition FS context and a mount name. @@ -86,6 +88,9 @@ bool devoptabMountHashFileSystemDevice(HashFileSystemContext *hfs_ctx, const cha /// Mounts a virtual RomFS device using the provided RomFS context and a mount name. bool devoptabMountRomFileSystemDevice(RomFileSystemContext *romfs_ctx, const char *name); +/// Mounts a virtual FatFs device using the provided FATFS object and a mount name. +bool devoptabMountFatFsDevice(FATFS *fatfs, const char *name); + /// Unmounts a previously mounted virtual device. void devoptabUnmountDevice(const char *name); diff --git a/include/core/devoptab/ro_dev.h b/include/core/devoptab/ro_dev.h index 0e6dd13..0c54074 100644 --- a/include/core/devoptab/ro_dev.h +++ b/include/core/devoptab/ro_dev.h @@ -32,6 +32,7 @@ extern "C" { /* 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_fstat(struct _reent *r, void *fd, struct stat *st); 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); diff --git a/include/core/fatfs/ffconf.h b/include/core/fatfs/ffconf.h index a3d9068..312b064 100644 --- a/include/core/fatfs/ffconf.h +++ b/include/core/fatfs/ffconf.h @@ -166,12 +166,11 @@ / Drive/Volume Configurations /---------------------------------------------------------------------------*/ -#define FF_VOLUMES 1 +#define FF_VOLUMES 4 /* Number of volumes (logical drives) to be used. (1-10) */ #define FF_STR_VOLUME_ID 1 -#define FF_VOLUME_STRS "sys" /* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings. / When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive / number in the path name. FF_VOLUME_STRS defines the volume ID strings for each diff --git a/include/core/nxdt_utils.h b/include/core/nxdt_utils.h index 1e862db..62cef6b 100644 --- a/include/core/nxdt_utils.h +++ b/include/core/nxdt_utils.h @@ -114,9 +114,6 @@ bool utilsIsTerraUnit(void); /// Returns true if the application is running under applet mode. bool utilsIsAppletMode(void); -/// Returns a pointer to the FsStorage object for the eMMC BIS System partition. -FsStorage *utilsGetEmmcBisSystemPartitionStorage(void); - /// Blocks HOME button presses, disables screen dimming and auto sleep and overclocks system CPU/MEM. /// Must be called before starting long-running processes. /// If state is set to false, regular system behavior is restored. diff --git a/include/core/save.h b/include/core/save.h index 529268c..f7d2a66 100644 --- a/include/core/save.h +++ b/include/core/save.h @@ -25,8 +25,6 @@ #ifndef __SAVE_H__ #define __SAVE_H__ -#include "fatfs/ff.h" - #ifdef __cplusplus extern "C" { #endif @@ -45,7 +43,7 @@ extern "C" { #define MAGIC_RMAP 0x50414D52 #define MAGIC_IVFC 0x43465649 -#define ACTION_VERIFY (1<<2) +#define ACTION_VERIFY (1 << 2) typedef enum { VALIDITY_UNCHECKED = 0, @@ -232,7 +230,7 @@ typedef struct { enum base_storage_type type; u64 base_storage_offset; duplex_storage_ctx_t *duplex; - FIL *file; + FILE *file; } remap_storage_ctx_t; typedef struct { @@ -333,7 +331,7 @@ typedef struct { u32 block_size; u64 journal_data_offset; u64 _length; - FIL *file; + FILE *file; } journal_storage_ctx_t; typedef struct { @@ -470,9 +468,9 @@ typedef struct { struct save_ctx_t { save_header_t header; - FIL *file; + FILE *file; struct { - FIL *file; + FILE *file; u32 action; } tool_ctx; validity_t header_cmac_validity; @@ -549,7 +547,7 @@ bool save_hierarchical_file_table_find_path_recursive(hierarchical_save_file_tab bool save_hierarchical_file_table_get_file_entry_by_path(hierarchical_save_file_table_ctx_t *ctx, const char *path, save_fs_list_entry_t *entry); save_ctx_t *save_open_savefile(const char *path, u32 action); -void save_close_savefile(save_ctx_t *ctx); +void save_close_savefile(save_ctx_t **ctx); bool save_get_fat_storage_from_file_entry_by_path(save_ctx_t *ctx, const char *path, allocation_table_storage_ctx_t *out_fat_storage, u64 *out_file_entry_size); #ifdef __cplusplus diff --git a/include/defines.h b/include/defines.h index 714fac9..e264866 100644 --- a/include/defines.h +++ b/include/defines.h @@ -104,8 +104,6 @@ #define LOG_BUF_SIZE 0x400000 /* 4 MiB. */ #define LOG_FORCE_FLUSH 0 /* Forces a log buffer flush each time the logfile is written to. */ -#define BIS_SYSTEM_PARTITION_MOUNT_NAME "sys:" - /// Reference: https://docs.microsoft.com/en-us/windows/win32/fileio/filesystem-functionality-comparison#limits. /// Reference: https://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits. /// Most modern filesystems use a 255-byte limit instead of 255-character/codepoint limit, so that's what we're gonna use. diff --git a/source/core/bis_storage.c b/source/core/bis_storage.c new file mode 100644 index 0000000..049bca1 --- /dev/null +++ b/source/core/bis_storage.c @@ -0,0 +1,240 @@ +/* + * bis_storage.c + * + * Copyright (c) 2020-2024, 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 +#include +#include + +#define BIS_STORAGE_INDEX(type) ((type) - FsBisPartitionId_CalibrationFile) +#define BIS_STORAGE_FATFS_CTX(type) g_bisStorageContexts[BIS_STORAGE_INDEX(type)] +#define BIS_STORAGE_MOUNT_NAME(type) VolumeStr[BIS_STORAGE_INDEX(type)] + +/* Type definitions. */ + +typedef struct { + u8 bis_partition_id; ///< FsBisPartitionId. + FsStorage bis_storage; + FATFS fatfs; +} BisStorageFatFsContext; + +/* Global variables. */ + +static Mutex g_bisStorageMutex = 0; + +static BisStorageFatFsContext *g_bisStorageContexts[FF_VOLUMES] = {0}; + +/// Required by FatFs. +const char *VolumeStr[FF_VOLUMES] = { + "prodinfof", + "safe", + "user", + "system", +}; + +/* Function prototypes. */ + +static void _bisStorageUnmountPartition(u8 bis_partition_id); + +static BisStorageFatFsContext *bisStorageInitializeFatFsContext(u8 bis_partition_id); +static void bisStorageFreeFatFsContext(BisStorageFatFsContext **bis_fatfs_ctx); + +bool bisStorageMountPartition(u8 bis_partition_id, const char **out_mount_name) +{ + if (bis_partition_id < FsBisPartitionId_CalibrationFile || bis_partition_id > FsBisPartitionId_System || !out_mount_name) + { + LOG_MSG_ERROR("Invalid parameters!"); + return false; + } + + bool ret = false; + + SCOPED_LOCK(&g_bisStorageMutex) + { + BisStorageFatFsContext *bis_fatfs_ctx = NULL; + + /* Check if we have already mounted this eMMC partition. */ + bis_fatfs_ctx = BIS_STORAGE_FATFS_CTX(bis_partition_id); + if (bis_fatfs_ctx) + { + *out_mount_name = BIS_STORAGE_MOUNT_NAME(bis_partition_id); + ret = true; + break; + } + + /* Initialize BIS FatFs context. */ + bis_fatfs_ctx = bisStorageInitializeFatFsContext(bis_partition_id); + if (!bis_fatfs_ctx) break; + + /* Update output. */ + *out_mount_name = BIS_STORAGE_MOUNT_NAME(bis_partition_id); + ret = true; + } + + return ret; +} + +void bisStorageUnmountPartition(u8 bis_partition_id) +{ + if (bis_partition_id < FsBisPartitionId_CalibrationFile || bis_partition_id > FsBisPartitionId_System) + { + LOG_MSG_ERROR("Invalid parameters!"); + return; + } + + SCOPED_LOCK(&g_bisStorageMutex) _bisStorageUnmountPartition(bis_partition_id); +} + +void bisStorageUnmountAllPartitions(void) +{ + SCOPED_LOCK(&g_bisStorageMutex) + { + for(u8 i = 0; i < FF_VOLUMES; i++) _bisStorageUnmountPartition(i + FsBisPartitionId_CalibrationFile); + } +} + +FsStorage *bisStorageGetFsStorageByFatFsDriveNumber(u8 drive_number) +{ + FsStorage *bis_storage = NULL; + + SCOPED_LOCK(&g_bisStorageMutex) + { + for(u8 i = 0; i < FF_VOLUMES; i++) + { + if (!g_bisStorageContexts[i] || g_bisStorageContexts[i]->fatfs.pdrv != drive_number) continue; + bis_storage = &(g_bisStorageContexts[i]->bis_storage); + break; + } + } + + return bis_storage; +} + +void bisStorageControlMutex(bool lock) +{ + bool locked = mutexIsLockedByCurrentThread(&g_bisStorageMutex); + + if (!locked && lock) + { + mutexLock(&g_bisStorageMutex); + } else + if (locked && !lock) + { + mutexUnlock(&g_bisStorageMutex); + } +} + +static void _bisStorageUnmountPartition(u8 bis_partition_id) +{ + /* Check if we have already mounted this eMMC partition. */ + BisStorageFatFsContext *bis_fatfs_ctx = BIS_STORAGE_FATFS_CTX(bis_partition_id); + if (!bis_fatfs_ctx) return; + + /* Free BIS FatFs context. This will take care of unmounting the partition. */ + bisStorageFreeFatFsContext(&bis_fatfs_ctx); + + /* Update context array. */ + BIS_STORAGE_FATFS_CTX(bis_partition_id) = NULL; +} + +static BisStorageFatFsContext *bisStorageInitializeFatFsContext(u8 bis_partition_id) +{ + if (bis_partition_id < FsBisPartitionId_CalibrationFile || bis_partition_id > FsBisPartitionId_System) + { + LOG_MSG_ERROR("Invalid parameters!"); + return NULL; + } + + BisStorageFatFsContext *bis_fatfs_ctx = NULL; + + Result rc = 0; + + FRESULT fr = FR_OK; + const char *name = BIS_STORAGE_MOUNT_NAME(bis_partition_id); + + bool success = false; + + /* Allocate memory for our output context. */ + bis_fatfs_ctx = calloc(1, sizeof(BisStorageFatFsContext)); + if (!bis_fatfs_ctx) + { + LOG_MSG_ERROR("Failed to allocate memory for BIS FatFs context! (partition ID %u).", bis_partition_id); + goto end; + } + + /* Set BIS partition ID. */ + bis_fatfs_ctx->bis_partition_id = bis_partition_id; + + /* Open BIS storage. */ + rc = fsOpenBisStorage(&(bis_fatfs_ctx->bis_storage), bis_fatfs_ctx->bis_partition_id); + if (R_FAILED(rc)) + { + LOG_MSG_ERROR("Failed to open eMMC BIS partition storage! (0x%X, partition ID %u).", rc, bis_partition_id); + goto end; + } + + /* Update context array. */ + /* FatFs diskio demands we do this here. */ + BIS_STORAGE_FATFS_CTX(bis_partition_id) = bis_fatfs_ctx; + + /* Mount BIS partition using FatFs. */ + fr = f_mount(&(bis_fatfs_ctx->fatfs), name, 1); + if (fr != FR_OK) + { + LOG_MSG_ERROR("Failed to mount eMMC BIS partition via FatFs! (%u, partition ID %u).", fr, bis_partition_id); + goto end; + } + + /* Mount devoptab device. */ + if (!devoptabMountFatFsDevice(&(bis_fatfs_ctx->fatfs), name)) + { + LOG_MSG_ERROR("Failed to mount devoptab device for eMMC BIS partition %u!", bis_partition_id); + goto end; + } + + /* Update flag. */ + success = true; + +end: + if (!success) + { + bisStorageFreeFatFsContext(&bis_fatfs_ctx); + BIS_STORAGE_FATFS_CTX(bis_partition_id) = NULL; + } + + return bis_fatfs_ctx; +} + +static void bisStorageFreeFatFsContext(BisStorageFatFsContext **bis_fatfs_ctx) +{ + if (!bis_fatfs_ctx || !*bis_fatfs_ctx) return; + + if ((*bis_fatfs_ctx)->fatfs.fs_type) + { + const char *name = BIS_STORAGE_MOUNT_NAME((*bis_fatfs_ctx)->bis_partition_id); + devoptabUnmountDevice(name); + f_unmount(name); + } + + if (serviceIsActive(&((*bis_fatfs_ctx)->bis_storage.s))) fsStorageClose(&((*bis_fatfs_ctx)->bis_storage)); + + free(*bis_fatfs_ctx); + *bis_fatfs_ctx = NULL; +} diff --git a/source/core/cert.c b/source/core/cert.c index f057ed1..2f15ef0 100644 --- a/source/core/cert.c +++ b/source/core/cert.c @@ -23,8 +23,9 @@ #include #include #include +#include -#define CERT_SAVEFILE_PATH BIS_SYSTEM_PARTITION_MOUNT_NAME "/save/80000000000000e0" +#define CERT_BIS_SYSTEM_SAVEFILE_PATH "/save/80000000000000e0" #define CERT_SAVEFILE_STORAGE_BASE_PATH "/certificate/" #define CERT_TYPE(sig) (pub_key_type == CertPubKeyType_Rsa4096 ? CertType_Sig##sig##_PubKeyRsa4096 : \ @@ -32,8 +33,8 @@ /* Global variables. */ -static save_ctx_t *g_esCertSaveCtx = NULL; static Mutex g_esCertSaveMutex = 0; +static save_ctx_t *g_esCertSaveCtx = NULL; /* Function prototypes. */ @@ -60,9 +61,15 @@ bool certRetrieveCertificateByName(Certificate *dst, const char *name) SCOPED_LOCK(&g_esCertSaveMutex) { - if (!certOpenEsCertSaveFile()) break; - ret = _certRetrieveCertificateByName(dst, name); - certCloseEsCertSaveFile(); + bisStorageControlMutex(true); + + if (certOpenEsCertSaveFile()) + { + ret = _certRetrieveCertificateByName(dst, name); + certCloseEsCertSaveFile(); + } + + bisStorageControlMutex(false); } return ret; @@ -80,9 +87,15 @@ bool certRetrieveCertificateChainBySignatureIssuer(CertificateChain *dst, const SCOPED_LOCK(&g_esCertSaveMutex) { - if (!certOpenEsCertSaveFile()) break; - ret = _certRetrieveCertificateChainBySignatureIssuer(dst, issuer); - certCloseEsCertSaveFile(); + bisStorageControlMutex(true); + + if (certOpenEsCertSaveFile()) + { + ret = _certRetrieveCertificateChainBySignatureIssuer(dst, issuer); + certCloseEsCertSaveFile(); + } + + bisStorageControlMutex(false); } return ret; @@ -190,21 +203,44 @@ static bool certOpenEsCertSaveFile(void) { if (g_esCertSaveCtx) return true; - g_esCertSaveCtx = save_open_savefile(CERT_SAVEFILE_PATH, 0); + const char *mount_name = NULL; + char savefile_path[64] = {0}; + bool success = false; + + /* Mount eMMC BIS System partition. */ + if (!bisStorageMountPartition(FsBisPartitionId_System, &mount_name)) + { + LOG_MSG_ERROR("Failed to mount eMMC BIS System partition!"); + goto end; + } + + /* Generate savefile path. */ + snprintf(savefile_path, sizeof(savefile_path), "%s:%s", mount_name, CERT_BIS_SYSTEM_SAVEFILE_PATH); + + /* Initialize savefile context. */ + g_esCertSaveCtx = save_open_savefile(savefile_path, 0); if (!g_esCertSaveCtx) { LOG_MSG_ERROR("Failed to open ES certificate system savefile!"); - return false; + goto end; } - return true; + /* Update flag. */ + success = true; + +end: + if (!success && mount_name) bisStorageUnmountPartition(FsBisPartitionId_System); + + return success; } static void certCloseEsCertSaveFile(void) { if (!g_esCertSaveCtx) return; - save_close_savefile(g_esCertSaveCtx); - g_esCertSaveCtx = NULL; + + save_close_savefile(&g_esCertSaveCtx); + + bisStorageUnmountPartition(FsBisPartitionId_System); } static bool _certRetrieveCertificateByName(Certificate *dst, const char *name) diff --git a/source/core/devoptab/fat_dev.c b/source/core/devoptab/fat_dev.c new file mode 100644 index 0000000..3f90cd6 --- /dev/null +++ b/source/core/devoptab/fat_dev.c @@ -0,0 +1,532 @@ +/* + * fat_dev.c + * + * Loosely based on ff_dev.c from libusbhsfs. + * + * Copyright (c) 2020-2024, 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 +#include +#include +#include + +/* Helper macros. */ + +#define FAT_DEV_INIT_FILE_VARS DEVOPTAB_INIT_FILE_VARS(FIL) +#define FAT_DEV_INIT_DIR_VARS DEVOPTAB_INIT_DIR_VARS(FDIR) +#define FAT_DEV_INIT_FS_ACCESS DEVOPTAB_DECL_FS_CTX(FATFS) + +/* Function prototypes. */ + +static int fatdev_open(struct _reent *r, void *fd, const char *path, int flags, int mode); +static int fatdev_close(struct _reent *r, void *fd); +static ssize_t fatdev_read(struct _reent *r, void *fd, char *ptr, size_t len); +static off_t fatdev_seek(struct _reent *r, void *fd, off_t pos, int dir); +static int fatdev_stat(struct _reent *r, const char *file, struct stat *st); +static DIR_ITER* fatdev_diropen(struct _reent *r, DIR_ITER *dirState, const char *path); +static int fatdev_dirreset(struct _reent *r, DIR_ITER *dirState); +static int fatdev_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat); +static int fatdev_dirclose(struct _reent *r, DIR_ITER *dirState); +static int fatdev_statvfs(struct _reent *r, const char *path, struct statvfs *buf); + +static const char *fatdev_get_fixed_path(struct _reent *r, const char *path, FATFS *fatfs); + +static void fatdev_fill_stat(struct stat *st, const FILINFO *info); + +static int fatdev_translate_error(FRESULT res); + +/* Global variables. */ + +__thread char g_fatDevicePathBuffer[FS_MAX_PATH] = {0}; + +static const devoptab_t fatdev_devoptab = { + .name = NULL, + .structSize = sizeof(FIL), + .open_r = fatdev_open, + .close_r = fatdev_close, + .write_r = rodev_write, ///< Supported by FatFs, but disabled on purpose. + .read_r = fatdev_read, + .seek_r = fatdev_seek, + .fstat_r = rodev_fstat, ///< Not supported by FatFs. + .stat_r = fatdev_stat, + .link_r = rodev_link, ///< Supported by FatFs, but disabled on purpose. + .unlink_r = rodev_unlink, ///< Supported by FatFs, but disabled on purpose. + .chdir_r = rodev_chdir, ///< No need to deal with cwd shenanigans, so we won't support it. + .rename_r = rodev_rename, ///< Supported by FatFs, but disabled on purpose. + .mkdir_r = rodev_mkdir, ///< Supported by FatFs, but disabled on purpose. + .dirStateSize = sizeof(FDIR), + .diropen_r = fatdev_diropen, + .dirreset_r = fatdev_dirreset, + .dirnext_r = fatdev_dirnext, + .dirclose_r = fatdev_dirclose, + .statvfs_r = fatdev_statvfs, + .ftruncate_r = rodev_ftruncate, ///< Supported by FatFs, but disabled on purpose. + .fsync_r = rodev_fsync, ///< Supported by FatFs, but disabled on purpose. + .deviceData = NULL, + .chmod_r = rodev_chmod, ///< Supported by FatFs, but disabled on purpose. + .fchmod_r = rodev_fchmod, ///< Supported by FatFs, but disabled on purpose. + .rmdir_r = rodev_rmdir, ///< Supported by FatFs, but disabled on purpose. + .lstat_r = fatdev_stat, ///< Symlinks aren't supported, so we'll just alias lstat() to stat(). + .utimes_r = rodev_utimes, ///< Supported by FatFs, but disabled on purpose. + .fpathconf_r = rodev_fpathconf, ///< Not supported by FatFs. + .pathconf_r = rodev_pathconf, ///< Not supported by FatFs. + .symlink_r = rodev_symlink, ///< Not supported by FatFs. + .readlink_r = rodev_readlink ///< Not supported by FatFs. +}; + +const devoptab_t *fatdev_get_devoptab() +{ + return &fatdev_devoptab; +} + +static int fatdev_open(struct _reent *r, void *fd, const char *path, int flags, int mode) +{ + NX_IGNORE_ARG(mode); + + BYTE fatdev_flags = (FA_READ | FA_OPEN_EXISTING); + FRESULT res = FR_OK; + + FAT_DEV_INIT_FILE_VARS; + FAT_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(EROFS); + + /* Get fixed path. */ + if (!(path = fatdev_get_fixed_path(r, path, fs_ctx))) DEVOPTAB_EXIT; + + //LOG_MSG_DEBUG("Opening \"%s\" with flags 0x%X (volume \"%s:\").", path, fatdev_flags, dev_ctx->name); + + /* Reset file descriptor. */ + memset(file, 0, sizeof(FIL)); + + /* Open file. */ + res = f_open(file, path, fatdev_flags); + if (res != FR_OK) DEVOPTAB_SET_ERROR(fatdev_translate_error(res)); + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(0); +} + +static int fatdev_close(struct _reent *r, void *fd) +{ + FRESULT res = FR_OK; + + FAT_DEV_INIT_FILE_VARS; + + /* Sanity check. */ + if (!file) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + + //LOG_MSG_DEBUG("Closing file from \"%u:\" (volume \"%s:\").", file->obj.fs->pdrv, dev_ctx->name); + + /* Close file. */ + res = f_close(file); + if (res != FR_OK) DEVOPTAB_SET_ERROR_AND_EXIT(fatdev_translate_error(res)); + + /* Reset file descriptor. */ + memset(file, 0, sizeof(FIL)); + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(0); +} + +static ssize_t fatdev_read(struct _reent *r, void *fd, char *ptr, size_t len) +{ + UINT br = 0; + FRESULT res = FR_OK; + + FAT_DEV_INIT_FILE_VARS; + + /* Sanity check. */ + if (!file || !ptr || !len) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + + /* Check if the file was opened with read access. */ + if (!(file->flag & FA_READ)) DEVOPTAB_SET_ERROR_AND_EXIT(EBADF); + + //LOG_MSG_DEBUG("Reading 0x%lX byte(s) at offset 0x%lX from file in \"%u:\" (volume \"%s:\").", len, f_tell(file), ((FATFS*)dev_ctx->fs_ctx)->pdrv, dev_ctx->name); + + /* Read file data. */ + res = f_read(file, ptr, (UINT)len, &br); + if (res != FR_OK) DEVOPTAB_SET_ERROR(fatdev_translate_error(res)); + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT((ssize_t)br); +} + +static off_t fatdev_seek(struct _reent *r, void *fd, off_t pos, int dir) +{ + off_t offset = 0; + FRESULT res = FR_OK; + + FAT_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)f_tell(file); + break; + case SEEK_END: /* Set position relative to EOF. */ + offset = (off_t)f_size(file); + 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)f_size(file)) DEVOPTAB_SET_ERROR_AND_EXIT(EOVERFLOW); + + //LOG_MSG_DEBUG("Seeking to offset 0x%lX from file in \"%u:\" (volume \"%s:\").", offset, file->obj.fs->pdrv, dev_ctx->name); + + /* Perform file seek. */ + res = f_lseek(file, (FSIZE_t)offset); + if (res != FR_OK) DEVOPTAB_SET_ERROR(fatdev_translate_error(res)); + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(offset); +} + +static int fatdev_stat(struct _reent *r, const char *file, struct stat *st) +{ + FILINFO info = {0}; + FRESULT res = FR_OK; + + DEVOPTAB_INIT_VARS; + FAT_DEV_INIT_FS_ACCESS; + + /* Sanity check. */ + if (!file || !st) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + + /* Get fixed path. */ + if (!(file = fatdev_get_fixed_path(r, file, fs_ctx))) DEVOPTAB_EXIT; + + //LOG_MSG_DEBUG("Getting file stats for \"%s\" (volume \"%s:\").", file, dev_ctx->name); + + /* Get stats. */ + res = f_stat(file, &info); + if (res != FR_OK) DEVOPTAB_SET_ERROR_AND_EXIT(fatdev_translate_error(res)); + + /* Fill stat info. */ + fatdev_fill_stat(st, &info); + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(0); +} + +static DIR_ITER *fatdev_diropen(struct _reent *r, DIR_ITER *dirState, const char *path) +{ + FRESULT res = FR_OK; + DIR_ITER *ret = NULL; + + FAT_DEV_INIT_DIR_VARS; + FAT_DEV_INIT_FS_ACCESS; + + /* Get fixed path. */ + if (!(path = fatdev_get_fixed_path(r, path, fs_ctx))) DEVOPTAB_EXIT; + + //LOG_MSG_DEBUG("Opening directory \"%s\" (volume \"%s:\").", path, dev_ctx->name); + + /* Reset directory state. */ + memset(dir, 0, sizeof(FDIR)); + + /* Open directory. */ + res = f_opendir(dir, path); + if (res != FR_OK) DEVOPTAB_SET_ERROR_AND_EXIT(fatdev_translate_error(res)); + + /* Update return value. */ + ret = dirState; + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_PTR(ret); +} + +static int fatdev_dirreset(struct _reent *r, DIR_ITER *dirState) +{ + FRESULT res = FR_OK; + + FAT_DEV_INIT_DIR_VARS; + + //LOG_MSG_DEBUG("Resetting state for directory in \"%u:\" (volume \"%s:\").", dir->obj.fs->pdrv, dev_ctx->name); + + /* Reset directory state. */ + res = f_rewinddir(dir); + if (res != FR_OK) DEVOPTAB_SET_ERROR(fatdev_translate_error(res)); + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(0); +} + +static int fatdev_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) +{ + FILINFO info = {0}; + FRESULT res = FR_OK; + + FAT_DEV_INIT_DIR_VARS; + + /* Sanity check. */ + if (!filename || !filestat) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + + //LOG_MSG_DEBUG("Getting info from next directory entry in \"%u:\" (volume \"%s:\").", dir->obj.fs->pdrv, dev_ctx->name); + + /* Read directory. */ + res = f_readdir(dir, &info); + if (res != FR_OK) DEVOPTAB_SET_ERROR_AND_EXIT(fatdev_translate_error(res)); + + /* Check if we haven't reached EOD. */ + /* FatFs returns an empty string if so. */ + if (info.fname[0]) + { + /* Copy filename. */ + strcpy(filename, info.fname); + + /* Fill stat info. */ + fatdev_fill_stat(filestat, &info); + } else { + /* ENOENT signals EOD. */ + DEVOPTAB_SET_ERROR(ENOENT); + } + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(0); +} + +static int fatdev_dirclose(struct _reent *r, DIR_ITER *dirState) +{ + FRESULT res = FR_OK; + + FAT_DEV_INIT_DIR_VARS; + + //LOG_MSG_DEBUG("Closing directory from \"%s:\" (volume \"%u:\").", dir->obj.fs->pdrv, dev_ctx->name); + + /* Close directory. */ + res = f_closedir(dir); + if (res != FR_OK) DEVOPTAB_SET_ERROR_AND_EXIT(fatdev_translate_error(res)); + + /* Reset directory state. */ + memset(dir, 0, sizeof(FDIR)); + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(0); +} + +static int fatdev_statvfs(struct _reent *r, const char *path, struct statvfs *buf) +{ + NX_IGNORE_ARG(path); + + DEVOPTAB_INIT_VARS; + FAT_DEV_INIT_FS_ACCESS; + + /* Sanity check. */ + if (!buf) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + + //LOG_MSG_DEBUG("Getting filesystem stats for \"%u:\" (volume \"%s:\").", fs_ctx->pdrv, dev_ctx->name); + + /* Fill filesystem stats. */ + memset(buf, 0, sizeof(struct statvfs)); + + buf->f_bsize = FF_MIN_SS; /* Sector size. */ + buf->f_frsize = FF_MIN_SS; /* Sector size. */ + buf->f_blocks = ((fs_ctx->n_fatent - 2) * (DWORD)fs_ctx->csize); /* Total cluster count * cluster size in sectors. */ + 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 = FF_LFN_BUF; + +end: + DEVOPTAB_DEINIT_VARS; + DEVOPTAB_RETURN_INT(0); +} + +static const char *fatdev_get_fixed_path(struct _reent *r, const char *path, FATFS *fatfs) +{ + const u8 *p = (const u8*)path; + ssize_t units = 0; + u32 code = 0; + size_t len = 0; + char name[DEVOPTAB_MOUNT_NAME_LENGTH] = {0}; + + if (!r || !path || !*path || !fatfs) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + + //LOG_MSG_DEBUG("Input path: \"%s\".", path); + + /* Generate FatFs mount name ID. */ + snprintf(name, sizeof(name), "%u:", fatfs->pdrv); + + /* 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; + + /* Make sure the provided path starts with a slash. */ + if (path[0] != '/') DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + + /* Make sure there are no more colons 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 == ':') DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); + p += units; + } while(code >= ' '); + + /* Verify fixed path length. */ + len = (strlen(name) + strlen(path)); + if (len >= sizeof(g_fatDevicePathBuffer)) DEVOPTAB_SET_ERROR_AND_EXIT(ENAMETOOLONG); + + /* Generate fixed path. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-truncation" + snprintf(g_fatDevicePathBuffer, sizeof(g_fatDevicePathBuffer), "%s%s", name, path); +#pragma GCC diagnostic pop + + //LOG_MSG_DEBUG("Fixed path: \"%s\".", g_fatDevicePathBuffer); + +end: + DEVOPTAB_RETURN_PTR(g_fatDevicePathBuffer); +} + +static void fatdev_fill_stat(struct stat *st, const FILINFO *info) +{ + struct tm timeinfo = {0}; + + /* Clear stat struct. */ + memset(st, 0, sizeof(struct stat)); + + /* Fill stat struct. */ + st->st_nlink = 1; + + if (info->fattrib & AM_DIR) + { + /* We're dealing with a directory entry. */ + st->st_mode = (S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH); + } else { + /* We're dealing with a file entry. */ + st->st_size = (off_t)info->fsize; + st->st_mode = (S_IFREG | S_IRUSR | S_IRGRP | S_IROTH); + } + + /* Convert date/time into an actual UTC POSIX timestamp using the system local time. */ + timeinfo.tm_year = (((info->fdate >> 9) & 0x7F) + 80); /* DOS time: offset since 1980. POSIX time: offset since 1900. */ + timeinfo.tm_mon = (((info->fdate >> 5) & 0xF) - 1); /* DOS time: 1-12 range (inclusive). POSIX time: 0-11 range (inclusive). */ + timeinfo.tm_mday = (info->fdate & 0x1F); + timeinfo.tm_hour = ((info->ftime >> 11) & 0x1F); + timeinfo.tm_min = ((info->ftime >> 5) & 0x3F); + timeinfo.tm_sec = ((info->ftime & 0x1F) << 1); /* DOS time: 2-second intervals with a 0-29 range (inclusive, 58 seconds max). POSIX time: 0-59 range (inclusive). */ + + st->st_atime = 0; /* Not returned by FatFs + only available under exFAT. */ + st->st_mtime = mktime(&timeinfo); + st->st_ctime = 0; /* Not returned by FatFs + only available under exFAT. */ + + //LOG_MSG_DEBUG("DOS timestamp: 0x%04X%04X. Generated POSIX timestamp: %lu.", info->fdate, info->ftime, st->st_mtime); +} + +static int fatdev_translate_error(FRESULT res) +{ + int ret; + + switch(res) + { + case FR_OK: + ret = 0; + break; + case FR_DISK_ERR: + case FR_NOT_READY: + ret = EIO; + break; + case FR_INT_ERR: + case FR_INVALID_NAME: + case FR_INVALID_PARAMETER: + ret = EINVAL; + break; + case FR_NO_FILE: + case FR_NO_PATH: + ret = ENOENT; + break; + case FR_DENIED: + ret = EACCES; + break; + case FR_EXIST: + ret = EEXIST; + break; + case FR_INVALID_OBJECT: + ret = EFAULT; + break; + case FR_WRITE_PROTECTED: + ret = EROFS; + break; + case FR_INVALID_DRIVE: + ret = ENODEV; + break; + case FR_NOT_ENABLED: + ret = ENOEXEC; + break; + case FR_NO_FILESYSTEM: + ret = ENFILE; + break; + case FR_TIMEOUT: + ret = EAGAIN; + break; + case FR_LOCKED: + ret = EBUSY; + break; + case FR_NOT_ENOUGH_CORE: + ret = ENOMEM; + break; + case FR_TOO_MANY_OPEN_FILES: + ret = EMFILE; + break; + default: + ret = EPERM; + break; + } + + //LOG_MSG_DEBUG("FRESULT: %u. Translated errno: %d.", res, ret); + + return ret; +} diff --git a/source/core/devoptab/hfs_dev.c b/source/core/devoptab/hfs_dev.c index ecc25b1..07e6d9c 100644 --- a/source/core/devoptab/hfs_dev.c +++ b/source/core/devoptab/hfs_dev.c @@ -27,9 +27,8 @@ /* Helper macros. */ -#define HFS_DEV_INIT_VARS DEVOPTAB_INIT_VARS(HashFileSystemContext) -#define HFS_DEV_INIT_FILE_VARS DEVOPTAB_INIT_FILE_VARS(HashFileSystemContext, HashFileSystemFileState) -#define HFS_DEV_INIT_DIR_VARS DEVOPTAB_INIT_DIR_VARS(HashFileSystemContext, HashFileSystemDirectoryState) +#define HFS_DEV_INIT_FILE_VARS DEVOPTAB_INIT_FILE_VARS(HashFileSystemFileState) +#define HFS_DEV_INIT_DIR_VARS DEVOPTAB_INIT_DIR_VARS(HashFileSystemDirectoryState) #define HFS_DEV_INIT_FS_ACCESS DEVOPTAB_DECL_FS_CTX(HashFileSystemContext) /* Type definitions. */ @@ -236,7 +235,7 @@ static int hfsdev_stat(struct _reent *r, const char *file, struct stat *st) u32 index = 0; HashFileSystemEntry *hfs_entry = NULL; - HFS_DEV_INIT_VARS; + DEVOPTAB_INIT_VARS; HFS_DEV_INIT_FS_ACCESS; /* Sanity check. */ @@ -368,13 +367,13 @@ static int hfsdev_statvfs(struct _reent *r, const char *path, struct statvfs *bu u64 ext_fs_size = 0; - HFS_DEV_INIT_VARS; + DEVOPTAB_INIT_VARS; HFS_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); + //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); @@ -407,8 +406,6 @@ static const char *hfsdev_get_truncated_path(struct _reent *r, const char *path) 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); @@ -440,7 +437,7 @@ static const char *hfsdev_get_truncated_path(struct _reent *r, const char *path) p += units; } while(code >= ' '); - /* Verify fixed path length. */ + /* Verify truncated 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); diff --git a/source/core/devoptab/nxdt_devoptab.c b/source/core/devoptab/nxdt_devoptab.c index b11b7cb..00c2e86 100644 --- a/source/core/devoptab/nxdt_devoptab.c +++ b/source/core/devoptab/nxdt_devoptab.c @@ -30,12 +30,14 @@ typedef enum { DevoptabDeviceType_PartitionFileSystem = 0, DevoptabDeviceType_HashFileSystem = 1, DevoptabDeviceType_RomFileSystem = 2, - DevoptabDeviceType_Count = 3 ///< Total values supported by this enum. + DevoptabDeviceType_FatFs = 3, + DevoptabDeviceType_Count = 4 ///< 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); @@ -44,6 +46,7 @@ static const u32 g_devoptabDeviceCount = MAX_ELEMENTS(g_devoptabDevices); const devoptab_t *pfsdev_get_devoptab(); const devoptab_t *hfsdev_get_devoptab(); const devoptab_t *romfsdev_get_devoptab(); +const devoptab_t *fatdev_get_devoptab(); static bool devoptabMountDevice(void *fs_ctx, const char *name, u8 type); static DevoptabDeviceContext *devoptabFindDevice(const char *name); @@ -94,6 +97,21 @@ bool devoptabMountRomFileSystemDevice(RomFileSystemContext *romfs_ctx, const cha return ret; } +bool devoptabMountFatFsDevice(FATFS *fatfs, const char *name) +{ + if (!fatfs || !fatfs->fs_type || !name || !*name) + { + LOG_MSG_ERROR("Invalid parameters!"); + return false; + } + + bool ret = false; + + SCOPED_LOCK(&g_devoptabMutex) ret = devoptabMountDevice(fatfs, name, DevoptabDeviceType_FatFs); + + return ret; +} + void devoptabUnmountDevice(const char *name) { if (!name || !*name) @@ -111,7 +129,7 @@ void devoptabUnmountDevice(const char *name) /* Reset device. */ devoptabResetDevice(dev_ctx); } else { - LOG_MSG_ERROR("Error: unable to find devoptab device \"%s\".", name); + LOG_MSG_ERROR("Unable to find devoptab device \"%s\".", name); } } } @@ -154,7 +172,7 @@ static bool devoptabMountDevice(void *fs_ctx, const char *name, u8 type) /* 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); + LOG_MSG_ERROR("Unable to find an empty device slot for \"%s\" (type 0x%02X).", name, type); return false; } @@ -170,13 +188,16 @@ static bool devoptabMountDevice(void *fs_ctx, const char *name, u8 type) case DevoptabDeviceType_RomFileSystem: device = romfsdev_get_devoptab(); break; + case DevoptabDeviceType_FatFs: + device = fatdev_get_devoptab(); + break; default: break; } if (!device) { - LOG_MSG_ERROR("Error: unable to retrieve a devoptab interface for \"%s\" (type 0x%02X).", name, type); + LOG_MSG_ERROR("Unable to retrieve a devoptab interface for \"%s\" (type %u).", name, type); return false; } @@ -195,7 +216,7 @@ static bool devoptabMountDevice(void *fs_ctx, const char *name, u8 type) int res = AddDevice(&(dev_ctx->device)); if (res < 0) { - LOG_MSG_ERROR("Error: AddDevice failed! (%d).", res); + LOG_MSG_ERROR("AddDevice failed! (%d).", res); goto end; } diff --git a/source/core/devoptab/nxdt_romfs_dev.c b/source/core/devoptab/nxdt_romfs_dev.c index ca298a9..6aa728c 100644 --- a/source/core/devoptab/nxdt_romfs_dev.c +++ b/source/core/devoptab/nxdt_romfs_dev.c @@ -27,9 +27,8 @@ /* Helper macros. */ -#define ROMFS_DEV_INIT_VARS DEVOPTAB_INIT_VARS(RomFileSystemContext) -#define ROMFS_DEV_INIT_FILE_VARS DEVOPTAB_INIT_FILE_VARS(RomFileSystemContext, RomFileSystemFileState) -#define ROMFS_DEV_INIT_DIR_VARS DEVOPTAB_INIT_DIR_VARS(RomFileSystemContext, RomFileSystemDirectoryState) +#define ROMFS_DEV_INIT_FILE_VARS DEVOPTAB_INIT_FILE_VARS(RomFileSystemFileState) +#define ROMFS_DEV_INIT_DIR_VARS DEVOPTAB_INIT_DIR_VARS(RomFileSystemDirectoryState) #define ROMFS_DEV_INIT_FS_ACCESS DEVOPTAB_DECL_FS_CTX(RomFileSystemContext) #define ROMFS_FILE_INODE(file) ((u64)(file - fs_ctx->file_table) + (fs_ctx->dir_table_size / 4)) @@ -242,7 +241,7 @@ static int romfsdev_stat(struct _reent *r, const char *file, struct stat *st) { RomFileSystemFileEntry *file_entry = NULL; - ROMFS_DEV_INIT_VARS; + DEVOPTAB_INIT_VARS; ROMFS_DEV_INIT_FS_ACCESS; /* Sanity check. */ @@ -397,13 +396,13 @@ static int romfsdev_statvfs(struct _reent *r, const char *path, struct statvfs * u64 ext_fs_size = 0; - ROMFS_DEV_INIT_VARS; + DEVOPTAB_INIT_VARS; ROMFS_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); + //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); @@ -435,8 +434,6 @@ static const char *romfsdev_get_truncated_path(struct _reent *r, const char *pat u32 code = 0; size_t len = 0; - DEVOPTAB_DECL_ERROR_STATE; - if (!r || !path || !*path) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL); //LOG_MSG_DEBUG("Input path: \"%s\".", path); @@ -464,7 +461,7 @@ static const char *romfsdev_get_truncated_path(struct _reent *r, const char *pat p += units; } while(code >= ' '); - /* Verify fixed path length. */ + /* Verify truncated path length. */ len = strlen(path); if (len >= FS_MAX_PATH) DEVOPTAB_SET_ERROR_AND_EXIT(ENAMETOOLONG); diff --git a/source/core/devoptab/pfs_dev.c b/source/core/devoptab/pfs_dev.c index 8e70aa6..84482f7 100644 --- a/source/core/devoptab/pfs_dev.c +++ b/source/core/devoptab/pfs_dev.c @@ -27,9 +27,8 @@ /* Helper macros. */ -#define PFS_DEV_INIT_VARS DEVOPTAB_INIT_VARS(PartitionFileSystemContext) -#define PFS_DEV_INIT_FILE_VARS DEVOPTAB_INIT_FILE_VARS(PartitionFileSystemContext, PartitionFileSystemFileState) -#define PFS_DEV_INIT_DIR_VARS DEVOPTAB_INIT_DIR_VARS(PartitionFileSystemContext, PartitionFileSystemDirectoryState) +#define PFS_DEV_INIT_FILE_VARS DEVOPTAB_INIT_FILE_VARS(PartitionFileSystemFileState) +#define PFS_DEV_INIT_DIR_VARS DEVOPTAB_INIT_DIR_VARS(PartitionFileSystemDirectoryState) #define PFS_DEV_INIT_FS_ACCESS DEVOPTAB_DECL_FS_CTX(PartitionFileSystemContext) /* Type definitions. */ @@ -236,7 +235,7 @@ static int pfsdev_stat(struct _reent *r, const char *file, struct stat *st) u32 index = 0; PartitionFileSystemEntry *pfs_entry = NULL; - PFS_DEV_INIT_VARS; + DEVOPTAB_INIT_VARS; PFS_DEV_INIT_FS_ACCESS; /* Sanity check. */ @@ -368,13 +367,13 @@ static int pfsdev_statvfs(struct _reent *r, const char *path, struct statvfs *bu u64 ext_fs_size = 0; - PFS_DEV_INIT_VARS; + DEVOPTAB_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); + //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); @@ -407,8 +406,6 @@ static const char *pfsdev_get_truncated_path(struct _reent *r, const char *path) 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); @@ -440,7 +437,7 @@ static const char *pfsdev_get_truncated_path(struct _reent *r, const char *path) p += units; } while(code >= ' '); - /* Verify fixed path length. */ + /* Verify truncated 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); diff --git a/source/core/devoptab/ro_dev.c b/source/core/devoptab/ro_dev.c index 6afb560..4f1cc50 100644 --- a/source/core/devoptab/ro_dev.c +++ b/source/core/devoptab/ro_dev.c @@ -31,6 +31,14 @@ ssize_t rodev_write(struct _reent *r, void *fd, const char *ptr, size_t len) DEVOPTAB_RETURN_UNSUPPORTED_OP; } +int rodev_fstat(struct _reent *r, void *fd, struct stat *st) +{ + NX_IGNORE_ARG(fd); + NX_IGNORE_ARG(st); + + DEVOPTAB_RETURN_UNSUPPORTED_OP; +} + int rodev_link(struct _reent *r, const char *existing, const char *newLink) { NX_IGNORE_ARG(existing); diff --git a/source/core/fatfs/diskio.c b/source/core/fatfs/diskio.c index 271635e..79bb54e 100644 --- a/source/core/fatfs/diskio.c +++ b/source/core/fatfs/diskio.c @@ -8,6 +8,7 @@ /*-----------------------------------------------------------------------*/ #include +#include #include /* Obtains integer types */ #include /* Declarations of disk functions */ @@ -20,7 +21,7 @@ DSTATUS disk_status ( BYTE pdrv /* Physical drive number to identify the drive */ ) { - (void)pdrv; + NX_IGNORE_ARG(pdrv); return 0; } @@ -32,7 +33,7 @@ DSTATUS disk_initialize ( BYTE pdrv /* Physical drive number to identify the drive */ ) { - (void)pdrv; + NX_IGNORE_ARG(pdrv); return 0; } @@ -47,15 +48,34 @@ DRESULT disk_read ( UINT count /* Number of sectors to read */ ) { - (void)pdrv; - + FsStorage *bis_storage = NULL; + u64 offset = 0, size = 0; Result rc = 0; - u64 start_offset = ((u64)FF_MAX_SS * (u64)sector); - u64 read_size = ((u64)FF_MAX_SS * (u64)count); + DRESULT ret = RES_PARERR; - rc = fsStorageRead(utilsGetEmmcBisSystemPartitionStorage(), start_offset, buff, read_size); + bisStorageControlMutex(true); - return (R_SUCCEEDED(rc) ? RES_OK : RES_ERROR); + /* Get pointer to FsStorage object. */ + bis_storage = bisStorageGetFsStorageByFatFsDriveNumber(pdrv); + if (!bis_storage) + { + LOG_MSG_ERROR("Failed to retrieve FsStorage object for drive number %u!", pdrv); + goto end; + } + + /* Calculate data offset and size. */ + offset = ((u64)FF_MAX_SS * (u64)sector); + size = ((u64)FF_MAX_SS * (u64)count); + + /* Read BIS storage. */ + rc = fsStorageRead(bis_storage, (s64)offset, buff, size); + ret = (R_SUCCEEDED(rc) ? RES_OK : RES_ERROR); + if (ret == RES_ERROR) LOG_MSG_ERROR("Failed to read 0x%lX-byte long block at offset 0x%lX from drive number %u!", offset, size, pdrv); + +end: + bisStorageControlMutex(false); + + return ret; } /*-----------------------------------------------------------------------*/ @@ -71,10 +91,10 @@ DRESULT disk_write ( UINT count /* Number of sectors to write */ ) { - (void)pdrv; - (void)buff; - (void)sector; - (void)count; + NX_IGNORE_ARG(pdrv); + NX_IGNORE_ARG(buff); + NX_IGNORE_ARG(sector); + NX_IGNORE_ARG(count); return RES_OK; } @@ -90,8 +110,8 @@ DRESULT disk_ioctl ( void *buff /* Buffer to send/receive control data */ ) { - (void)pdrv; - (void)cmd; - (void)buff; + NX_IGNORE_ARG(pdrv); + NX_IGNORE_ARG(cmd); + NX_IGNORE_ARG(buff); return RES_OK; } diff --git a/source/core/nxdt_utils.c b/source/core/nxdt_utils.c index be2e4f9..163ed4e 100644 --- a/source/core/nxdt_utils.c +++ b/source/core/nxdt_utils.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include /* Type definitions. */ @@ -75,9 +75,6 @@ static bool g_isTerraUnit = false, g_isDevUnit = false; static AppletType g_programAppletType = AppletType_None; -static FsStorage g_emmcBisSystemPartitionStorage = {0}; -static FATFS *g_emmcBisSystemPartitionFatFsObj = NULL; - static AppletHookCookie g_systemOverclockCookie = {0}; static bool g_longRunningProcess = false; @@ -111,9 +108,6 @@ static bool utilsGetDevelopmentUnitFlag(void); static bool utilsGetTerraUnitFlag(void); -static bool utilsMountEmmcBisSystemPartitionStorage(void); -static void utilsUnmountEmmcBisSystemPartitionStorage(void); - static void utilsOverclockSystem(bool overclock); static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param); @@ -275,9 +269,6 @@ bool utilsInitializeResources(void) /* Initialize system update interface. */ if (!systemUpdateInitialize()) break; - /* Mount eMMC BIS System partition. */ - if (!utilsMountEmmcBisSystemPartitionStorage()) break; - /* Mount application RomFS. */ rc = romfsInit(); if (R_FAILED(rc)) @@ -347,6 +338,9 @@ void utilsCloseResources(void) /* Unmount all custom devoptab devices. */ devoptabUnmountAllDevices(); + /* Unmount all eMMC BIS partitions. */ + bisStorageUnmountAllPartitions(); + /* Unset long running process state. */ utilsSetLongRunningProcessState(false); @@ -359,9 +353,6 @@ void utilsCloseResources(void) /* Unmount application RomFS. */ romfsExit(); - /* Unmount eMMC BIS System partition. */ - utilsUnmountEmmcBisSystemPartitionStorage(); - /* Deinitialize system update interface. */ systemUpdateExit(); @@ -484,11 +475,6 @@ bool utilsIsAppletMode(void) return (g_programAppletType > AppletType_Application && g_programAppletType < AppletType_SystemApplication); } -FsStorage *utilsGetEmmcBisSystemPartitionStorage(void) -{ - return &g_emmcBisSystemPartitionStorage; -} - void utilsSetLongRunningProcessState(bool state) { SCOPED_LOCK(&g_resourcesMutex) @@ -1476,51 +1462,6 @@ static bool utilsGetTerraUnitFlag(void) return R_SUCCEEDED(rc); } -static bool utilsMountEmmcBisSystemPartitionStorage(void) -{ - Result rc = 0; - FRESULT fr = FR_OK; - - rc = fsOpenBisStorage(&g_emmcBisSystemPartitionStorage, FsBisPartitionId_System); - if (R_FAILED(rc)) - { - LOG_MSG_ERROR("Failed to open eMMC BIS System partition storage! (0x%X).", rc); - return false; - } - - g_emmcBisSystemPartitionFatFsObj = calloc(1, sizeof(FATFS)); - if (!g_emmcBisSystemPartitionFatFsObj) - { - LOG_MSG_ERROR("Unable to allocate memory for FatFs element!"); - return false; - } - - fr = f_mount(g_emmcBisSystemPartitionFatFsObj, BIS_SYSTEM_PARTITION_MOUNT_NAME, 1); - if (fr != FR_OK) - { - LOG_MSG_ERROR("Failed to mount eMMC BIS System partition! (%u).", fr); - return false; - } - - return true; -} - -static void utilsUnmountEmmcBisSystemPartitionStorage(void) -{ - if (g_emmcBisSystemPartitionFatFsObj) - { - f_unmount(BIS_SYSTEM_PARTITION_MOUNT_NAME); - free(g_emmcBisSystemPartitionFatFsObj); - g_emmcBisSystemPartitionFatFsObj = NULL; - } - - if (serviceIsActive(&(g_emmcBisSystemPartitionStorage.s))) - { - fsStorageClose(&g_emmcBisSystemPartitionStorage); - memset(&g_emmcBisSystemPartitionStorage, 0, sizeof(FsStorage)); - } -} - static void utilsOverclockSystem(bool overclock) { u32 cpu_rate = ((overclock ? CPU_CLKRT_OVERCLOCKED : CPU_CLKRT_NORMAL) * 1000000); diff --git a/source/core/save.c b/source/core/save.c index 1a86e68..6e136c8 100644 --- a/source/core/save.c +++ b/source/core/save.c @@ -237,7 +237,7 @@ static remap_entry_ctx_t *save_remap_get_map_entry(remap_storage_ctx_t *ctx, u64 return NULL; } -static u32 save_remap_read(remap_storage_ctx_t *ctx, void *buffer, u64 offset, size_t count) +static u64 save_remap_read(remap_storage_ctx_t *ctx, void *buffer, u64 offset, size_t count) { if (!ctx || (ctx->type == STORAGE_BYTES && !ctx->file) || (ctx->type == STORAGE_DUPLEX && !ctx->duplex) || (ctx->type != STORAGE_BYTES && ctx->type != STORAGE_DUPLEX) || !buffer || !count) { @@ -253,42 +253,43 @@ static u32 save_remap_read(remap_storage_ctx_t *ctx, void *buffer, u64 offset, s } u64 in_pos = offset; - u32 out_pos = 0; - u32 remaining = count; + u64 out_pos = 0; + u64 remaining = count; - UINT br = 0; - FRESULT fr; + int res = 0; while(remaining) { u64 entry_pos = (in_pos - entry->virtual_offset); - u32 bytes_to_read = ((entry->virtual_offset_end - in_pos) < remaining ? (u32)(entry->virtual_offset_end - in_pos) : remaining); + u64 bytes_to_read = ((entry->virtual_offset_end - in_pos) < remaining ? (entry->virtual_offset_end - in_pos) : remaining); + u64 read_bytes = 0; switch (ctx->type) { case STORAGE_BYTES: - fr = f_lseek(ctx->file, ctx->base_storage_offset + entry->physical_offset + entry_pos); - if (fr || f_tell(ctx->file) != (ctx->base_storage_offset + entry->physical_offset + entry_pos)) + res = fseek(ctx->file, ctx->base_storage_offset + entry->physical_offset + entry_pos, SEEK_SET); + if (res || ftell(ctx->file) != (ctx->base_storage_offset + entry->physical_offset + entry_pos)) { - LOG_MSG_ERROR("Failed to seek to offset 0x%lX in savefile! (%u).", ctx->base_storage_offset + entry->physical_offset + entry_pos, fr); + LOG_MSG_ERROR("Failed to seek to offset 0x%lX in savefile! (%d).", ctx->base_storage_offset + entry->physical_offset + entry_pos, errno); return out_pos; } - fr = f_read(ctx->file, (u8*)buffer + out_pos, bytes_to_read, &br); - if (fr || br != bytes_to_read) + read_bytes = fread((u8*)buffer + out_pos, 1, bytes_to_read, ctx->file); + if (read_bytes != bytes_to_read) { - LOG_MSG_ERROR("Failed to read %u bytes chunk from offset 0x%lX in savefile! (%u).", bytes_to_read, ctx->base_storage_offset + entry->physical_offset + entry_pos, fr); - return (out_pos + br); + LOG_MSG_ERROR("Failed to read 0x%lX-byte long chunk from offset 0x%lX in savefile! (read 0x%lX, errno %d).", bytes_to_read, ctx->base_storage_offset + entry->physical_offset + entry_pos, read_bytes, errno); + return (out_pos + read_bytes); } break; case STORAGE_DUPLEX: - br = save_duplex_storage_read(ctx->duplex, (u8*)buffer + out_pos, ctx->base_storage_offset + entry->physical_offset + entry_pos, bytes_to_read); - if (br != bytes_to_read) + read_bytes = save_duplex_storage_read(ctx->duplex, (u8*)buffer + out_pos, ctx->base_storage_offset + entry->physical_offset + entry_pos, bytes_to_read); + if (read_bytes != bytes_to_read) { LOG_MSG_ERROR("Failed to read remap data from duplex storage!"); - return (out_pos + br); + return (out_pos + read_bytes); } + break; default: break; @@ -304,7 +305,7 @@ static u32 save_remap_read(remap_storage_ctx_t *ctx, void *buffer, u64 offset, s return out_pos; } -static u32 save_journal_storage_read(journal_storage_ctx_t *ctx, remap_storage_ctx_t *remap, void *buffer, u64 offset, size_t count) +static u64 save_journal_storage_read(journal_storage_ctx_t *ctx, remap_storage_ctx_t *remap, void *buffer, u64 offset, size_t count) { if (!ctx || !ctx->block_size || !remap || !buffer || !count) { @@ -313,22 +314,21 @@ static u32 save_journal_storage_read(journal_storage_ctx_t *ctx, remap_storage_c } u64 in_pos = offset; - u32 out_pos = 0; - u32 remaining = count; - u32 br; + u64 out_pos = 0; + u64 remaining = count; while(remaining) { - u32 block_num = (u32)(in_pos / ctx->block_size); - u32 block_pos = (u32)(in_pos % ctx->block_size); + u64 block_num = (in_pos / ctx->block_size); + u64 block_pos = (in_pos % ctx->block_size); u64 physical_offset = (ctx->map.entries[block_num].physical_index * ctx->block_size + block_pos); - u32 bytes_to_read = ((ctx->block_size - block_pos) < remaining ? (ctx->block_size - block_pos) : remaining); + u64 bytes_to_read = ((ctx->block_size - block_pos) < remaining ? (ctx->block_size - block_pos) : remaining); - br = save_remap_read(remap, (u8*)buffer + out_pos, ctx->journal_data_offset + physical_offset, bytes_to_read); - if (br != bytes_to_read) + u64 read_bytes = save_remap_read(remap, (u8*)buffer + out_pos, ctx->journal_data_offset + physical_offset, bytes_to_read); + if (read_bytes != bytes_to_read) { LOG_MSG_ERROR("Failed to read journal storage data!"); - return (out_pos + br); + return (out_pos + read_bytes); } out_pos += bytes_to_read; @@ -462,42 +462,43 @@ static size_t save_ivfc_level_fread(ivfc_level_save_ctx_t *ctx, void *buffer, u6 return 0; } - UINT br = 0; - FRESULT fr; + size_t read_bytes = 0; + + int res = 0; switch (ctx->type) { case STORAGE_BYTES: - fr = f_lseek(ctx->save_ctx->file, ctx->hash_offset + offset); - if (fr || f_tell(ctx->save_ctx->file) != (ctx->hash_offset + offset)) + res = fseek(ctx->save_ctx->file, ctx->hash_offset + offset, SEEK_SET); + if (res || ftell(ctx->save_ctx->file) != (ctx->hash_offset + offset)) { - LOG_MSG_ERROR("Failed to seek to offset 0x%lX in savefile! (%u).", ctx->hash_offset + offset, fr); - return (size_t)br; + LOG_MSG_ERROR("Failed to seek to offset 0x%lX in savefile! (%d).", ctx->hash_offset + offset, errno); + return 0; } - fr = f_read(ctx->save_ctx->file, buffer, count, &br); - if (fr || br != count) + read_bytes = fread(buffer, 1, count, ctx->save_ctx->file); + if (read_bytes != count) { - LOG_MSG_ERROR("Failed to read IVFC level data from offset 0x%lX in savefile! (%u).", ctx->hash_offset + offset, fr); - return (size_t)br; + LOG_MSG_ERROR("Failed to read 0x%lX-byte long IVFC level data chunk from offset 0x%lX in savefile! (read 0x%lX, errno %d).", count, ctx->hash_offset + offset, read_bytes, errno); + return read_bytes; } break; case STORAGE_REMAP: - br = save_remap_read(&ctx->save_ctx->meta_remap_storage, buffer, ctx->data_offset + offset, count); - if (br != count) + read_bytes = save_remap_read(&ctx->save_ctx->meta_remap_storage, buffer, ctx->data_offset + offset, count); + if (read_bytes != count) { LOG_MSG_ERROR("Failed to read IVFC level data from remap storage!"); - return (size_t)br; + return read_bytes; } break; case STORAGE_JOURNAL: - br = save_journal_storage_read(&ctx->save_ctx->journal_storage, &ctx->save_ctx->data_remap_storage, buffer, ctx->data_offset + offset, count); - if (br != count) + read_bytes = save_journal_storage_read(&ctx->save_ctx->journal_storage, &ctx->save_ctx->data_remap_storage, buffer, ctx->data_offset + offset, count); + if (read_bytes != count) { LOG_MSG_ERROR("Failed to read IVFC level data from journal storage!"); - return (size_t)br; + return read_bytes; } break; @@ -1224,34 +1225,34 @@ bool save_process(save_ctx_t *ctx) return false; } - UINT br = 0; - FRESULT fr; + size_t read_bytes = 0; + int res = 0; bool success = false; /* Try to parse Header A. */ - f_rewind(ctx->file); + rewind(ctx->file); - fr = f_read(ctx->file, &ctx->header, sizeof(ctx->header), &br); - if (fr || br != sizeof(ctx->header)) + read_bytes = fread(&(ctx->header), 1, sizeof(ctx->header), ctx->file); + if (read_bytes != sizeof(ctx->header)) { - LOG_MSG_ERROR("Failed to read savefile header A! (%u).", fr); + LOG_MSG_ERROR("Failed to read savefile header A! (read 0x%lX, errno %d).", read_bytes, errno); return success; } if (!save_process_header(ctx) || ctx->header_hash_validity == VALIDITY_INVALID) { /* Try to parse Header B. */ - fr = f_lseek(ctx->file, 0x4000); - if (fr || f_tell(ctx->file) != 0x4000) + res = fseek(ctx->file, 0x4000, SEEK_SET); + if (res || ftell(ctx->file) != 0x4000) { - LOG_MSG_ERROR("Failed to seek to offset 0x4000 in savefile! (%u).", fr); + LOG_MSG_ERROR("Failed to seek to offset 0x4000 in savefile! (%d).", errno); return success; } - fr = f_read(ctx->file, &ctx->header, sizeof(ctx->header), &br); - if (fr || br != sizeof(ctx->header)) + read_bytes = fread(&(ctx->header), 1, sizeof(ctx->header), ctx->file); + if (read_bytes != sizeof(ctx->header)) { - LOG_MSG_ERROR("Failed to read savefile header B! (%u).", fr); + LOG_MSG_ERROR("Failed to read savefile header B! (read 0x%lX, errno %d).", read_bytes, errno); return success; } @@ -1280,19 +1281,19 @@ bool save_process(save_ctx_t *ctx) return success; } - fr = f_lseek(ctx->file, ctx->header.layout.file_map_entry_offset); - if (fr || f_tell(ctx->file) != ctx->header.layout.file_map_entry_offset) + res = fseek(ctx->file, ctx->header.layout.file_map_entry_offset, SEEK_SET); + if (res || ftell(ctx->file) != ctx->header.layout.file_map_entry_offset) { - LOG_MSG_ERROR("Failed to seek to file map entry offset 0x%lX in savefile! (%u).", ctx->header.layout.file_map_entry_offset, fr); + LOG_MSG_ERROR("Failed to seek to file map entry offset 0x%lX in savefile! (%d).", ctx->header.layout.file_map_entry_offset, errno); return success; } for(u32 i = 0; i < ctx->data_remap_storage.header->map_entry_count; i++) { - fr = f_read(ctx->file, &ctx->data_remap_storage.map_entries[i], 0x20, &br); - if (fr || br != 0x20) + read_bytes = fread(&(ctx->data_remap_storage.map_entries[i]), 1, 0x20, ctx->file); + if (read_bytes != 0x20) { - LOG_MSG_ERROR("Failed to read data remap storage entry #%u! (%u).", i, fr); + LOG_MSG_ERROR("Failed to read data remap storage entry #%u! (read 0x%lX, errno %d).", i, read_bytes, errno); goto end; } @@ -1417,19 +1418,19 @@ bool save_process(save_ctx_t *ctx) goto end; } - fr = f_lseek(ctx->file, ctx->header.layout.meta_map_entry_offset); - if (fr || f_tell(ctx->file) != ctx->header.layout.meta_map_entry_offset) + res = fseek(ctx->file, ctx->header.layout.meta_map_entry_offset, SEEK_SET); + if (res || ftell(ctx->file) != ctx->header.layout.meta_map_entry_offset) { - LOG_MSG_ERROR("Failed to seek to meta map entry offset 0x%lX in savefile! (%u).", ctx->header.layout.meta_map_entry_offset, fr); + LOG_MSG_ERROR("Failed to seek to meta map entry offset 0x%lX in savefile! (%d).", ctx->header.layout.meta_map_entry_offset, errno); goto end; } for(u32 i = 0; i < ctx->meta_remap_storage.header->map_entry_count; i++) { - fr = f_read(ctx->file, &ctx->meta_remap_storage.map_entries[i], 0x20, &br); - if (fr || br != 0x20) + read_bytes = fread(&(ctx->meta_remap_storage.map_entries[i]), 1, 0x20, ctx->file); + if (read_bytes != 0x20) { - LOG_MSG_ERROR("Failed to read meta remap storage entry #%u! (%u).", i, fr); + LOG_MSG_ERROR("Failed to read meta remap storage entry #%u! (read 0x%lX, errno %d).", i, read_bytes, errno); goto end; } @@ -1726,53 +1727,46 @@ save_ctx_t *save_open_savefile(const char *path, u32 action) return NULL; } - FRESULT fr = FR_OK; - FIL *save_fd = NULL; + FILE *save_fp = NULL; save_ctx_t *save_ctx = NULL; - bool open_savefile = false, success = false; + bool success = false; - save_fd = calloc(1, sizeof(FIL)); - if (!save_fd) + save_fp = fopen(path, "rb"); + if (!save_fp) { - LOG_MSG_ERROR("Unable to allocate memory for FatFs file descriptor!"); - return NULL; - } - - fr = f_open(save_fd, path, FA_READ | FA_OPEN_EXISTING); - if (fr != FR_OK) - { - LOG_MSG_ERROR("Failed to open \"%s\" savefile from BIS System partition! (%u).", path, fr); + LOG_MSG_ERROR("Failed to open savefile \"%s\"! (%d).", path, errno); goto end; } - open_savefile = true; - /* Code to dump the requested file in its entirety. Useful to retrieve protected system savefiles without exiting HOS. */ /*char sd_path[FS_MAX_PATH] = {0}; - sprintf(sd_path, DEVOPTAB_SDMC_DEVICE "/%s", strrchr(path, '/') + 1); + snprintf(sd_path, MAX_ELEMENTS(sd_path), DEVOPTAB_SDMC_DEVICE "/%s", strrchr(path, '/') + 1); - UINT blksize = 0x100000; + utilsCreateDirectoryTree(sd_path, false); + + u64 blksize = 0x100000; u8 *buf = malloc(blksize); - FILE *fd = fopen(sd_path, "wb"); + FILE *sd_fp = fopen(sd_path, "wb"); - if (buf && fd) + if (buf && sd_fp) { - u64 size = f_size(save_fd); - UINT br = 0; + fseek(save_fp, 0, SEEK_END); + u64 size = ftell(save_fp); + rewind(save_fp); - for(u64 i = 0; i < size; i += blksize) + for(u64 offset = 0; offset < size; offset += blksize) { - if ((size - i) < blksize) blksize = (size - i); - if (f_read(save_fd, buf, blksize, &br) != FR_OK || br != blksize) break; - fwrite(buf, 1, blksize, fd); + if ((size - offset) < blksize) blksize = (size - offset); + if (fread(buf, 1, blksize, save_fp) != blksize) break; + fwrite(buf, 1, blksize, sd_fp); } - f_rewind(save_fd); + rewind(save_fp); } - if (fd) + if (sd_fp) { - fclose(fd); + fclose(sd_fp); utilsCommitSdCardFileSystemChanges(); } @@ -1785,7 +1779,7 @@ save_ctx_t *save_open_savefile(const char *path, u32 action) goto end; } - save_ctx->file = save_fd; + save_ctx->file = save_fp; save_ctx->tool_ctx.action = action; success = save_process(save_ctx); @@ -1800,29 +1794,22 @@ end: save_ctx = NULL; } - if (save_fd) - { - if (open_savefile) f_close(save_fd); - free(save_fd); - } + if (save_fp) fclose(save_fp); } return save_ctx; } -void save_close_savefile(save_ctx_t *ctx) +void save_close_savefile(save_ctx_t **ctx) { - if (!ctx) return; + if (!ctx || !*ctx) return; - if (ctx->file) - { - f_close(ctx->file); - free(ctx->file); - } + if ((*ctx)->file) fclose((*ctx)->file); - save_free_contexts(ctx); + save_free_contexts(*ctx); - free(ctx); + free(*ctx); + *ctx = NULL; } bool save_get_fat_storage_from_file_entry_by_path(save_ctx_t *ctx, const char *path, allocation_table_storage_ctx_t *out_fat_storage, u64 *out_file_entry_size) diff --git a/source/core/tik.c b/source/core/tik.c index e2d51ea..fdf340e 100644 --- a/source/core/tik.c +++ b/source/core/tik.c @@ -30,15 +30,16 @@ #include #include #include +#include -#define TIK_COMMON_SAVEFILE_PATH BIS_SYSTEM_PARTITION_MOUNT_NAME "/save/80000000000000e1" -#define TIK_PERSONALIZED_SAVEFILE_PATH BIS_SYSTEM_PARTITION_MOUNT_NAME "/save/80000000000000e2" +#define TIK_COMMON_BIS_SYSTEM_SAVEFILE_PATH "/save/80000000000000e1" +#define TIK_PERSONALIZED_BIS_SYSTEM_SAVEFILE_PATH "/save/80000000000000e2" -#define TIK_LIST_STORAGE_PATH "/ticket_list.bin" -#define TIK_DB_STORAGE_PATH "/ticket.bin" +#define TIK_LIST_SAVEFILE_STORAGE_PATH "/ticket_list.bin" +#define TIK_DB_SAVEFILE_STORAGE_PATH "/ticket.bin" -#define TIK_COMMON_CERT_NAME "XS00000020" -#define TIK_DEV_CERT_ISSUER "CA00000004" +#define TIK_COMMON_CERT_NAME "XS00000020" +#define TIK_DEV_CERT_ISSUER "CA00000004" /* Type definitions. */ @@ -320,6 +321,8 @@ static bool tikRetrieveTicketFromEsSaveDataByRightsId(Ticket *dst, const FsRight u8 titlekey_type = 0; + const char *mount_name = NULL; + char savefile_path[64] = {0}; save_ctx_t *save_ctx = NULL; u64 buf_size = (SIGNED_TIK_MAX_SIZE * 0x100); @@ -328,6 +331,8 @@ static bool tikRetrieveTicketFromEsSaveDataByRightsId(Ticket *dst, const FsRight bool success = false; + bisStorageControlMutex(true); + /* Allocate memory to retrieve the ticket. */ if (!(buf = malloc(buf_size))) { @@ -342,8 +347,18 @@ static bool tikRetrieveTicketFromEsSaveDataByRightsId(Ticket *dst, const FsRight goto end; } + /* Mount eMMC BIS System partition. */ + if (!bisStorageMountPartition(FsBisPartitionId_System, &mount_name)) + { + LOG_MSG_ERROR("Failed to mount eMMC BIS System partition!"); + goto end; + } + + /* Generate savefile path. */ + snprintf(savefile_path, sizeof(savefile_path), "%s:%s", mount_name, titlekey_type == TikTitleKeyType_Common ? TIK_COMMON_BIS_SYSTEM_SAVEFILE_PATH : TIK_PERSONALIZED_BIS_SYSTEM_SAVEFILE_PATH); + /* Open ES common/personalized system savefile. */ - if (!(save_ctx = save_open_savefile(titlekey_type == TikTitleKeyType_Common ? TIK_COMMON_SAVEFILE_PATH : TIK_PERSONALIZED_SAVEFILE_PATH, 0))) + if (!(save_ctx = save_open_savefile(savefile_path, 0))) { LOG_MSG_ERROR("Failed to open ES %s ticket system savefile!", g_tikTitleKeyTypeStrings[titlekey_type]); goto end; @@ -352,7 +367,7 @@ static bool tikRetrieveTicketFromEsSaveDataByRightsId(Ticket *dst, const FsRight /* Get ticket entry offset from ticket_list.bin. */ if (!tikGetTicketEntryOffsetFromTicketList(save_ctx, buf, buf_size, id, titlekey_type, &ticket_offset)) { - LOG_MSG_ERROR("Unable to find an entry with a matching Rights ID in \"%s\" from ES %s ticket system save!", TIK_LIST_STORAGE_PATH, g_tikTitleKeyTypeStrings[titlekey_type]); + LOG_MSG_ERROR("Unable to find an entry with a matching Rights ID in \"%s\" from ES %s ticket system save!", TIK_LIST_SAVEFILE_STORAGE_PATH, g_tikTitleKeyTypeStrings[titlekey_type]); goto end; } @@ -374,10 +389,14 @@ static bool tikRetrieveTicketFromEsSaveDataByRightsId(Ticket *dst, const FsRight memcpy(dst->data, buf, dst->size); end: - if (save_ctx) save_close_savefile(save_ctx); + if (save_ctx) save_close_savefile(&save_ctx); + + if (mount_name) bisStorageUnmountPartition(FsBisPartitionId_System); if (buf) free(buf); + bisStorageControlMutex(false); + return success; } @@ -541,8 +560,8 @@ static bool tikGetDecryptedTitleKey(void *dst, const void *src, u8 key_generatio return true; } - static bool tikGetTitleKeyTypeFromRightsId(const FsRightsId *id, u8 *out) + { if (!id || !out) { @@ -661,16 +680,16 @@ static bool tikGetTicketEntryOffsetFromTicketList(save_ctx_t *save_ctx, u8 *buf, bool last_entry_found = false, success = false; /* Get FAT storage info for the ticket_list.bin stored within the opened system savefile. */ - if (!save_get_fat_storage_from_file_entry_by_path(save_ctx, TIK_LIST_STORAGE_PATH, &fat_storage, &ticket_list_bin_size)) + if (!save_get_fat_storage_from_file_entry_by_path(save_ctx, TIK_LIST_SAVEFILE_STORAGE_PATH, &fat_storage, &ticket_list_bin_size)) { - LOG_MSG_ERROR("Failed to locate \"%s\" in ES %s ticket system save!", TIK_LIST_STORAGE_PATH, g_tikTitleKeyTypeStrings[titlekey_type]); + LOG_MSG_ERROR("Failed to locate \"%s\" in ES %s ticket system save!", TIK_LIST_SAVEFILE_STORAGE_PATH, g_tikTitleKeyTypeStrings[titlekey_type]); goto end; } /* Validate ticket_list.bin size. */ if (ticket_list_bin_size < sizeof(TikListEntry) || (ticket_list_bin_size % sizeof(TikListEntry)) != 0) { - LOG_MSG_ERROR("Invalid size for \"%s\" in ES %s ticket system save! (0x%lX).", TIK_LIST_STORAGE_PATH, g_tikTitleKeyTypeStrings[titlekey_type], ticket_list_bin_size); + LOG_MSG_ERROR("Invalid size for \"%s\" in ES %s ticket system save! (0x%lX).", TIK_LIST_SAVEFILE_STORAGE_PATH, g_tikTitleKeyTypeStrings[titlekey_type], ticket_list_bin_size); goto end; } @@ -683,7 +702,7 @@ static bool tikGetTicketEntryOffsetFromTicketList(save_ctx_t *save_ctx, u8 *buf, /* Read current chunk. */ if ((br = save_allocation_table_storage_read(&fat_storage, buf, total_br, buf_size)) != buf_size) { - LOG_MSG_ERROR("Failed to read 0x%lX bytes chunk at offset 0x%lX from \"%s\" in ES %s ticket system save!", buf_size, total_br, TIK_LIST_STORAGE_PATH, g_tikTitleKeyTypeStrings[titlekey_type]); + LOG_MSG_ERROR("Failed to read 0x%lX bytes chunk at offset 0x%lX from \"%s\" in ES %s ticket system save!", buf_size, total_br, TIK_LIST_SAVEFILE_STORAGE_PATH, g_tikTitleKeyTypeStrings[titlekey_type]); break; } @@ -737,23 +756,23 @@ static bool tikRetrieveTicketEntryFromTicketBin(save_ctx_t *save_ctx, u8 *buf, u bool is_volatile = false, success = false; /* Get FAT storage info for the ticket.bin stored within the opened system savefile. */ - if (!save_get_fat_storage_from_file_entry_by_path(save_ctx, TIK_DB_STORAGE_PATH, &fat_storage, &ticket_bin_size)) + if (!save_get_fat_storage_from_file_entry_by_path(save_ctx, TIK_DB_SAVEFILE_STORAGE_PATH, &fat_storage, &ticket_bin_size)) { - LOG_MSG_ERROR("Failed to locate \"%s\" in ES %s ticket system save!", TIK_DB_STORAGE_PATH, g_tikTitleKeyTypeStrings[titlekey_type]); + LOG_MSG_ERROR("Failed to locate \"%s\" in ES %s ticket system save!", TIK_DB_SAVEFILE_STORAGE_PATH, g_tikTitleKeyTypeStrings[titlekey_type]); goto end; } /* Validate ticket.bin size. */ if (ticket_bin_size < SIGNED_TIK_MIN_SIZE || (ticket_bin_size % SIGNED_TIK_MAX_SIZE) != 0 || ticket_bin_size < (ticket_offset + SIGNED_TIK_MAX_SIZE)) { - LOG_MSG_ERROR("Invalid size for \"%s\" in ES %s ticket system save! (0x%lX).", TIK_DB_STORAGE_PATH, g_tikTitleKeyTypeStrings[titlekey_type], ticket_bin_size); + LOG_MSG_ERROR("Invalid size for \"%s\" in ES %s ticket system save! (0x%lX).", TIK_DB_SAVEFILE_STORAGE_PATH, g_tikTitleKeyTypeStrings[titlekey_type], ticket_bin_size); goto end; } /* Read ticket data. */ if ((br = save_allocation_table_storage_read(&fat_storage, buf, ticket_offset, SIGNED_TIK_MAX_SIZE)) != SIGNED_TIK_MAX_SIZE) { - LOG_MSG_ERROR("Failed to read 0x%X-byte long ticket at offset 0x%lX from \"%s\" in ES %s ticket system save!", SIGNED_TIK_MAX_SIZE, ticket_offset, TIK_DB_STORAGE_PATH, \ + LOG_MSG_ERROR("Failed to read 0x%X-byte long ticket at offset 0x%lX from \"%s\" in ES %s ticket system save!", SIGNED_TIK_MAX_SIZE, ticket_offset, TIK_DB_SAVEFILE_STORAGE_PATH, \ g_tikTitleKeyTypeStrings[titlekey_type]); goto end; } @@ -768,7 +787,7 @@ static bool tikRetrieveTicketEntryFromTicketBin(save_ctx_t *save_ctx, u8 *buf, u /* Attempt to decrypt the ticket. */ if (!tikDecryptVolatileTicket(buf, ticket_offset)) { - LOG_MSG_ERROR("Unable to decrypt volatile ticket at offset 0x%lX in \"%s\" from ES %s ticket system save!", ticket_offset, TIK_DB_STORAGE_PATH, g_tikTitleKeyTypeStrings[titlekey_type]); + LOG_MSG_ERROR("Unable to decrypt volatile ticket at offset 0x%lX in \"%s\" from ES %s ticket system save!", ticket_offset, TIK_DB_SAVEFILE_STORAGE_PATH, g_tikTitleKeyTypeStrings[titlekey_type]); goto end; }