1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-12-22 08:32:03 +00:00

Create BIS storage interface.

Takes care of retrieving a FsStorage object for any FAT eMMC BIS partition, mounting it via FatFs and creating a virtual devoptab device that can be used to carry out FS operations. All write operations have been stubbed, disabled or ifdef'd out of the code.

Other changes include:

* cert: update code to use the new BIS storage interface.

* defines: remove BIS_SYSTEM_PARTITION_MOUNT_NAME macro.

* devoptab: slightly improve macros.
* devoptab: add operation table for FatFs devices.
* devoptab: add rodev_fstat().
* devoptab: add devoptabMountFatFsDevice().

* fatfs: update diskio code to use the new BIS storage interface.
* fatfs: update configuration.

* save: update code to use regular C I/O calls instead of FatFs calls.

* tik: update code to use the new BIS storage interface.

* utils: remove eMMC BIS System partition (un)mount code.
This commit is contained in:
Pablo Curiel 2024-10-28 14:31:58 +01:00
parent 160236c4de
commit 596928a3c6
19 changed files with 1138 additions and 291 deletions

View file

@ -0,0 +1,54 @@
/*
* bis_storage.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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__ */

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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.

240
source/core/bis_storage.c Normal file
View file

@ -0,0 +1,240 @@
/*
* bis_storage.c
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include <core/nxdt_utils.h>
#include <core/bis_storage.h>
#include <core/devoptab/nxdt_devoptab.h>
#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;
}

View file

@ -23,8 +23,9 @@
#include <core/cert.h>
#include <core/save.h>
#include <core/gamecard.h>
#include <core/bis_storage.h>
#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)

View file

@ -0,0 +1,532 @@
/*
* fat_dev.c
*
* Loosely based on ff_dev.c from libusbhsfs.
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include <core/nxdt_utils.h>
#include <core/devoptab/nxdt_devoptab.h>
#include <core/devoptab/ro_dev.h>
#include <core/fatfs/ff.h>
/* 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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -8,6 +8,7 @@
/*-----------------------------------------------------------------------*/
#include <core/nxdt_utils.h>
#include <core/bis_storage.h>
#include <core/fatfs/ff.h> /* Obtains integer types */
#include <core/fatfs/diskio.h> /* 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;
}

View file

@ -32,7 +32,7 @@
#include <core/nxdt_bfsar.h>
#include <core/system_update.h>
#include <core/devoptab/nxdt_devoptab.h>
#include <core/fatfs/ff.h>
#include <core/bis_storage.h>
/* 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);

View file

@ -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)

View file

@ -30,15 +30,16 @@
#include <core/mem.h>
#include <core/aes.h>
#include <core/rsa.h>
#include <core/bis_storage.h>
#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;
}