1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-12-22 16: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:
Pablo Curiel 2023-12-20 20:32:48 +01:00
parent bfdd9e056e
commit d402776032
21 changed files with 1090 additions and 66 deletions

View file

@ -68,10 +68,10 @@ BUILD_TIMESTAMP := $(strip $(shell date --utc '+%Y-%m-%d %T UTC'))
TARGET := ${APP_TITLE}
BUILD := build
SOURCES := source source/core source/fatfs
SOURCES := source source/core source/fatfs source/devoptab
DATA := data
ICON := romfs/icon/${APP_TITLE}.jpg
INCLUDES := include include/core include/fatfs
INCLUDES := include include/core include/fatfs include/devoptab
ROMFS := romfs
BOREALIS_PATH := libs/borealis

View file

@ -28,6 +28,7 @@
#include "legal_info.h"
#include "cert.h"
#include "usb.h"
#include "nxdt_devoptab.h"
#define BLOCK_SIZE USB_TRANSFER_BUFFER_SIZE
#define WAIT_TIME_LIMIT 30
@ -924,9 +925,12 @@ static char path[FS_MAX_PATH] = {0};
int main(int argc, char *argv[])
{
NX_IGNORE_ARG(argc);
NX_IGNORE_ARG(argv);
int ret = EXIT_SUCCESS;
if (!utilsInitializeResources(argc, (const char**)argv))
if (!utilsInitializeResources())
{
ret = EXIT_FAILURE;
goto end;
@ -2323,7 +2327,7 @@ static bool resetSettings(void *userdata)
static bool saveGameCardImage(void *userdata)
{
(void)userdata;
NX_IGNORE_ARG(userdata);
u64 gc_size = 0, free_space = 0;
@ -2480,7 +2484,7 @@ end:
static bool saveGameCardHeader(void *userdata)
{
(void)userdata;
NX_IGNORE_ARG(userdata);
GameCardHeader gc_header = {0};
bool success = false;
@ -2514,7 +2518,7 @@ end:
static bool saveGameCardCardInfo(void *userdata)
{
(void)userdata;
NX_IGNORE_ARG(userdata);
GameCardInfo gc_cardinfo = {0};
bool success = false;
@ -2548,7 +2552,7 @@ end:
static bool saveGameCardCertificate(void *userdata)
{
(void)userdata;
NX_IGNORE_ARG(userdata);
FsGameCardCertificate gc_cert = {0};
bool success = false;
@ -2582,7 +2586,7 @@ end:
static bool saveGameCardInitialData(void *userdata)
{
(void)userdata;
NX_IGNORE_ARG(userdata);
GameCardSecurityInformation gc_security_information = {0};
bool success = false;
@ -2615,7 +2619,7 @@ end:
/* Instead, take a look at saveGameCardIdSet and saveGameCardUid which is a more standardised format of the Gamecard ID data. */
static bool saveGameCardSpecificData(void *userdata)
{
(void)userdata;
NX_IGNORE_ARG(userdata);
GameCardSecurityInformation gc_security_information = {0};
bool success = false;
@ -2643,7 +2647,7 @@ end:
static bool saveGameCardIdSet(void *userdata)
{
(void)userdata;
NX_IGNORE_ARG(userdata);
FsGameCardIdSet id_set = {0};
bool success = false;
@ -2676,7 +2680,7 @@ end:
static bool saveGameCardUid(void *userdata)
{
(void)userdata;
NX_IGNORE_ARG(userdata);
GameCardSecurityInformation gc_security_information = {0};
bool success = false;
@ -2870,7 +2874,7 @@ end:
static bool saveConsoleLafwBlob(void *userdata)
{
(void)userdata;
NX_IGNORE_ARG(userdata);
u64 lafw_version = 0;
LotusAsicFirmwareBlob lafw_blob = {0};

View file

@ -81,9 +81,12 @@ static void writeFile(void *buf, size_t buf_size, const char *path)
int main(int argc, char *argv[])
{
NX_IGNORE_ARG(argc);
NX_IGNORE_ARG(argv);
int ret = EXIT_SUCCESS;
if (!utilsInitializeResources(argc, (const char**)argv))
if (!utilsInitializeResources())
{
ret = EXIT_FAILURE;
goto out;

View file

@ -107,21 +107,18 @@ NX_INLINE bool hfsIsValidContext(HashFileSystemContext *ctx)
NX_INLINE u32 hfsGetEntryCount(HashFileSystemContext *ctx)
{
if (!ctx || !ctx->header_size || !ctx->header) return 0;
return ((HashFileSystemHeader*)ctx->header)->entry_count;
return (hfsIsValidContext(ctx) ? ((HashFileSystemHeader*)ctx->header)->entry_count : 0);
}
NX_INLINE HashFileSystemEntry *hfsGetEntryByIndex(HashFileSystemContext *ctx, u32 idx)
{
if (idx >= hfsGetEntryCount(ctx)) return NULL;
return (HashFileSystemEntry*)(ctx->header + sizeof(HashFileSystemHeader) + (idx * sizeof(HashFileSystemEntry)));
return (idx < hfsGetEntryCount(ctx) ? (HashFileSystemEntry*)(ctx->header + sizeof(HashFileSystemHeader) + (idx * sizeof(HashFileSystemEntry))) : NULL);
}
NX_INLINE char *hfsGetNameTable(HashFileSystemContext *ctx)
{
u32 entry_count = hfsGetEntryCount(ctx);
if (!entry_count) return NULL;
return (char*)(ctx->header + sizeof(HashFileSystemHeader) + (entry_count * sizeof(HashFileSystemEntry)));
return (entry_count ? (char*)(ctx->header + sizeof(HashFileSystemHeader) + (entry_count * sizeof(HashFileSystemEntry))) : NULL);
}
NX_INLINE char *hfsGetEntryName(HashFileSystemContext *ctx, HashFileSystemEntry *fs_entry)
@ -135,15 +132,13 @@ NX_INLINE char *hfsGetEntryNameByIndex(HashFileSystemContext *ctx, u32 idx)
{
HashFileSystemEntry *fs_entry = hfsGetEntryByIndex(ctx, idx);
char *name_table = hfsGetNameTable(ctx);
if (!fs_entry || !name_table) return NULL;
return (name_table + fs_entry->name_offset);
return ((fs_entry && name_table) ? (name_table + fs_entry->name_offset) : NULL);
}
NX_INLINE HashFileSystemEntry *hfsGetEntryByName(HashFileSystemContext *ctx, const char *name)
{
u32 idx = 0;
if (!hfsGetEntryIndexByName(ctx, name, &idx)) return NULL;
return hfsGetEntryByIndex(ctx, idx);
return (hfsGetEntryIndexByName(ctx, name, &idx) ? hfsGetEntryByIndex(ctx, idx) : NULL);
}
#ifdef __cplusplus

View file

@ -38,6 +38,7 @@
#include <time.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <fcntl.h>
#include <dirent.h>
#include <assert.h>
#include <unistd.h>

View file

@ -66,7 +66,7 @@ typedef struct {
/// Resource initialization.
/// Called at program startup.
bool utilsInitializeResources(const int program_argc, const char **program_argv);
bool utilsInitializeResources(void);
/// Resource deinitialization.
/// Called at program exit.

View file

@ -112,23 +112,27 @@ NX_INLINE void pfsFreeContext(PartitionFileSystemContext *ctx)
memset(ctx, 0, sizeof(PartitionFileSystemContext));
}
/// Checks if the provided PartitionFileSystemContext is valid.
NX_INLINE bool pfsIsValidContext(PartitionFileSystemContext *ctx)
{
return (ctx && ncaStorageIsValidContext(&(ctx->storage_ctx)) && ctx->nca_fs_ctx == ctx->storage_ctx.nca_fs_ctx && \
ctx->storage_ctx.base_storage_type == NcaStorageBaseStorageType_Regular && ctx->size && ctx->header_size && ctx->header);
}
NX_INLINE u32 pfsGetEntryCount(PartitionFileSystemContext *ctx)
{
if (!ctx || !ctx->header_size || !ctx->header) return 0;
return ((PartitionFileSystemHeader*)ctx->header)->entry_count;
return (pfsIsValidContext(ctx) ? ((PartitionFileSystemHeader*)ctx->header)->entry_count : 0);
}
NX_INLINE PartitionFileSystemEntry *pfsGetEntryByIndex(PartitionFileSystemContext *ctx, u32 idx)
{
if (idx >= pfsGetEntryCount(ctx)) return NULL;
return (PartitionFileSystemEntry*)(ctx->header + sizeof(PartitionFileSystemHeader) + (idx * sizeof(PartitionFileSystemEntry)));
return (idx < pfsGetEntryCount(ctx) ? (PartitionFileSystemEntry*)(ctx->header + sizeof(PartitionFileSystemHeader) + (idx * sizeof(PartitionFileSystemEntry))) : NULL);
}
NX_INLINE char *pfsGetNameTable(PartitionFileSystemContext *ctx)
{
u32 entry_count = pfsGetEntryCount(ctx);
if (!entry_count) return NULL;
return (char*)(ctx->header + sizeof(PartitionFileSystemHeader) + (entry_count * sizeof(PartitionFileSystemEntry)));
return (entry_count ? (char*)(ctx->header + sizeof(PartitionFileSystemHeader) + (entry_count * sizeof(PartitionFileSystemEntry))) : NULL);
}
NX_INLINE char *pfsGetEntryName(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry)
@ -142,21 +146,18 @@ NX_INLINE char *pfsGetEntryNameByIndex(PartitionFileSystemContext *ctx, u32 idx)
{
PartitionFileSystemEntry *fs_entry = pfsGetEntryByIndex(ctx, idx);
char *name_table = pfsGetNameTable(ctx);
if (!fs_entry || !name_table) return NULL;
return (name_table + fs_entry->name_offset);
return ((fs_entry && name_table) ? (name_table + fs_entry->name_offset) : NULL);
}
NX_INLINE PartitionFileSystemEntry *pfsGetEntryByName(PartitionFileSystemContext *ctx, const char *name)
{
u32 idx = 0;
if (!pfsGetEntryIndexByName(ctx, name, &idx)) return NULL;
return pfsGetEntryByIndex(ctx, idx);
return (pfsGetEntryIndexByName(ctx, name, &idx) ? pfsGetEntryByIndex(ctx, idx) : NULL);
}
NX_INLINE void pfsWriteEntryPatchToMemoryBuffer(PartitionFileSystemContext *ctx, NcaHierarchicalSha256Patch *patch, void *buf, u64 buf_size, u64 buf_offset)
{
if (!ctx || !ncaStorageIsValidContext(&(ctx->storage_ctx)) || ctx->nca_fs_ctx != ctx->storage_ctx.nca_fs_ctx || \
ctx->storage_ctx.base_storage_type != NcaStorageBaseStorageType_Regular) return;
if (!pfsIsValidContext(ctx)) return;
ncaWriteHierarchicalSha256PatchToMemoryBuffer(ctx->nca_fs_ctx->nca_ctx, patch, buf, buf_size, buf_offset);
}
@ -187,15 +188,13 @@ NX_INLINE u32 pfsGetEntryCountFromImageContext(PartitionFileSystemImageContext *
NX_INLINE PartitionFileSystemEntry *pfsGetEntryByIndexFromImageContext(PartitionFileSystemImageContext *ctx, u32 idx)
{
if (idx >= pfsGetEntryCountFromImageContext(ctx)) return NULL;
return &(ctx->entries[idx]);
return (idx < pfsGetEntryCountFromImageContext(ctx) ? &(ctx->entries[idx]) : NULL);
}
NX_INLINE char *pfsGetEntryNameByIndexFromImageContext(PartitionFileSystemImageContext *ctx, u32 idx)
{
PartitionFileSystemEntry *fs_entry = pfsGetEntryByIndexFromImageContext(ctx, idx);
if (!fs_entry || !ctx->name_table) return NULL;
return (ctx->name_table + fs_entry->name_offset);
return ((fs_entry && ctx->name_table) ? (ctx->name_table + fs_entry->name_offset) : NULL);
}
#ifdef __cplusplus

View 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
View 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__ */

View file

@ -150,8 +150,8 @@ namespace nxdt::tasks
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)
{
(void)ultotal;
(void)ulnow;
NX_IGNORE_ARG(ultotal);
NX_IGNORE_ARG(ulnow);
DownloadTaskProgress progress = {0};
DownloadTask<Result, Params...>* task = static_cast<DownloadTask<Result, Params...>*>(clientp);
@ -173,7 +173,7 @@ namespace nxdt::tasks
template<typename Result, typename... Params>
void DownloadTask<Result, Params...>::onCancelled(const Result& result)
{
(void)result;
NX_IGNORE_ARG(result);
/* Pause task handler. */
this->task_handler->pause();
@ -185,7 +185,7 @@ namespace nxdt::tasks
template<typename Result, typename... Params>
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. */
this->task_handler->fireNow();

View file

@ -662,7 +662,7 @@ static void gamecardDestroyDetectionThread(void)
static void gamecardDetectionThreadFunc(void *arg)
{
(void)arg;
NX_IGNORE_ARG(arg);
Result rc = 0;
int idx = 0;

View file

@ -30,6 +30,7 @@
#include "title.h"
#include "bfttf.h"
#include "nxdt_bfsar.h"
#include "nxdt_devoptab.h"
#include "fatfs/ff.h"
/// Reference: https://docs.microsoft.com/en-us/windows/win32/fileio/filesystem-functionality-comparison#limits.
@ -48,6 +49,9 @@ typedef struct {
/* Global variables. */
extern int __system_argc;
extern char **__system_argv;
static bool g_resourcesInit = false;
static Mutex g_resourcesMutex = 0;
@ -95,7 +99,7 @@ static bool g_appUpdated = false;
/* Function prototypes. */
static void _utilsGetLaunchPath(int program_argc, const char **program_argv);
static void _utilsGetLaunchPath(void);
static void _utilsGetCustomFirmwareType(void);
@ -115,7 +119,7 @@ static size_t utilsGetUtf8StringLimit(const char *str, size_t str_size, size_t b
static char utilsConvertHexDigitToBinary(char c);
bool utilsInitializeResources(const int program_argc, const char **program_argv)
bool utilsInitializeResources(void)
{
Result rc = 0;
bool ret = false;
@ -129,7 +133,7 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
appletLockExit();
/* Retrieve pointer to the application launch path. */
_utilsGetLaunchPath(program_argc, program_argv);
_utilsGetLaunchPath();
/* Retrieve pointer to the SD card FsFileSystem element. */
if (!(g_sdCardFileSystem = fsdevGetDeviceFileSystem(DEVOPTAB_SDMC_DEVICE)))
@ -289,6 +293,9 @@ void utilsCloseResources(void)
{
SCOPED_LOCK(&g_resourcesMutex)
{
/* Unmount all custom devoptab devices. */
devoptabUnmountAllDevices();
/* Unset long running process state. */
utilsSetLongRunningProcessState(false);
@ -1169,15 +1176,15 @@ bool utilsIsApplicationUpdatable(const char *version, const char *commit_hash)
return ret;
}
static void _utilsGetLaunchPath(int program_argc, const char **program_argv)
static void _utilsGetLaunchPath(void)
{
if (program_argc <= 0 || !program_argv) return;
if (__system_argc <= 0 || !__system_argv) return;
for(int i = 0; i < program_argc; i++)
for(int i = 0; i < __system_argc; i++)
{
if (program_argv[i] && !strncmp(program_argv[i], DEVOPTAB_SDMC_DEVICE "/", strlen(DEVOPTAB_SDMC_DEVICE)))
if (__system_argv[i] && !strncmp(__system_argv[i], DEVOPTAB_SDMC_DEVICE "/", strlen(DEVOPTAB_SDMC_DEVICE)))
{
g_appLaunchPath = program_argv[i];
g_appLaunchPath = __system_argv[i];
break;
}
}
@ -1279,7 +1286,7 @@ static void utilsOverclockSystem(bool overclock)
static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param)
{
(void)param;
NX_IGNORE_ARG(param);
/* Don't proceed if we're not dealing with a desired hook type. */
if (hook != AppletHookType_OnOperationMode && hook != AppletHookType_OnPerformanceMode) return;

View file

@ -122,7 +122,7 @@ end:
bool pfsReadPartitionData(PartitionFileSystemContext *ctx, void *out, u64 read_size, u64 offset)
{
if (!ctx || !ncaStorageIsValidContext(&(ctx->storage_ctx)) || !ctx->size || !out || !read_size || (offset + read_size) > ctx->size)
if (!pfsIsValidContext(ctx) || !out || !read_size || (offset + read_size) > ctx->size)
{
LOG_MSG_ERROR("Invalid parameters!");
return false;
@ -226,8 +226,8 @@ bool pfsGetTotalDataSize(PartitionFileSystemContext *ctx, u64 *out_size)
bool pfsGenerateEntryPatch(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out)
{
if (!ctx || !ncaStorageIsValidContext(&(ctx->storage_ctx)) || ctx->storage_ctx.base_storage_type != NcaStorageBaseStorageType_Regular || !ctx->header_size || !ctx->header || \
!fs_entry || !fs_entry->size || (fs_entry->offset + fs_entry->size) > ctx->size || !data || !data_size || (data_offset + data_size) > fs_entry->size || !out)
if (!pfsIsValidContext(ctx) || !fs_entry || !fs_entry->size || (fs_entry->offset + fs_entry->size) > ctx->size || !data || !data_size || \
(data_offset + data_size) > fs_entry->size || !out)
{
LOG_MSG_ERROR("Invalid parameters!");
return false;

View file

@ -2303,7 +2303,7 @@ static void titleDestroyGameCardInfoThread(void)
static void titleGameCardInfoThreadFunc(void *arg)
{
(void)arg;
NX_IGNORE_ARG(arg);
Result rc = 0;
int idx = 0;

View file

@ -139,7 +139,7 @@ static void umsFreeDeviceData(void)
static void umsPopulateCallback(const UsbHsFsDevice *devices, u32 device_count, void *user_data)
{
(void)user_data;
NX_IGNORE_ARG(user_data);
SCOPED_LOCK(&g_umsMutex)
{

View file

@ -566,7 +566,7 @@ static void usbDestroyDetectionThread(void)
static void usbDetectionThreadFunc(void *arg)
{
(void)arg;
NX_IGNORE_ARG(arg);
Result rc = 0;
int idx = 0;

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

View file

@ -30,11 +30,14 @@ bool g_borealisInitialized = false;
int main(int argc, char *argv[])
{
NX_IGNORE_ARG(argc);
NX_IGNORE_ARG(argv);
/* Set scope guard to clean up resources at exit. */
ON_SCOPE_EXIT { utilsCloseResources(); };
/* Initialize application resources. */
if (!utilsInitializeResources(argc, (const char**)argv)) return EXIT_FAILURE;
if (!utilsInitializeResources()) return EXIT_FAILURE;
/* Load Borealis translation files. */
brls::i18n::loadTranslations();

View file

@ -56,8 +56,3 @@ list of top level functions designed to alter nca data in order of (possible) us
* calls ncaWriteHierarchicalSha256PatchToMemoryBuffer / ncaWriteHierarchicalIntegrityPatchToMemoryBuffer
* cnmtUpdateContentInfo (used to update content entry info in the raw cnmt copy after dumping each one - ignores the current content if its a meta nca)
minor steps to take into account:
* check if rights_id_available == true and titlekey_retrieved == false (preload handling)
* actually, just inform the user about it - this is being handled