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