mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-12-22 16:42:11 +00:00
Custom devoptab wrappers (part 2).
This commit implements a devoptab wrapper for Hash FS sections within gamecard images, using code from hfs.c/h. Other changes include: * poc: disable buffering on FILE objects via setvbuf(). * gamecard: fix crash in gamecardInitializeHashFileSystemContext() while calculating the size for the root Hash FS partition.
This commit is contained in:
parent
d402776032
commit
c392a9f126
4 changed files with 482 additions and 14 deletions
|
@ -2431,6 +2431,7 @@ static bool saveGameCardImage(void *userdata)
|
|||
goto end;
|
||||
}
|
||||
|
||||
setvbuf(shared_thread_data->fp, NULL, _IONBF, 0);
|
||||
ftruncate(fileno(shared_thread_data->fp), (off_t)shared_thread_data->total_size);
|
||||
|
||||
if (prepend_key_area && fwrite(&gc_key_area, 1, sizeof(GameCardKeyArea), shared_thread_data->fp) != sizeof(GameCardKeyArea))
|
||||
|
@ -2803,6 +2804,7 @@ static bool saveGameCardRawHfsPartition(HashFileSystemContext *hfs_ctx)
|
|||
goto end;
|
||||
}
|
||||
|
||||
setvbuf(shared_thread_data->fp, NULL, _IONBF, 0);
|
||||
ftruncate(fileno(shared_thread_data->fp), (off_t)shared_thread_data->total_size);
|
||||
}
|
||||
|
||||
|
@ -3222,6 +3224,7 @@ static bool saveNintendoContentArchive(void *userdata)
|
|||
goto end;
|
||||
}
|
||||
|
||||
setvbuf(shared_thread_data->fp, NULL, _IONBF, 0);
|
||||
ftruncate(fileno(shared_thread_data->fp), (off_t)shared_thread_data->total_size);
|
||||
}
|
||||
|
||||
|
@ -3465,6 +3468,7 @@ static bool saveRawPartitionFsSection(PartitionFileSystemContext *pfs_ctx, bool
|
|||
goto end;
|
||||
}
|
||||
|
||||
setvbuf(shared_thread_data->fp, NULL, _IONBF, 0);
|
||||
ftruncate(fileno(shared_thread_data->fp), (off_t)shared_thread_data->total_size);
|
||||
}
|
||||
|
||||
|
@ -3620,6 +3624,7 @@ static bool saveRawRomFsSection(RomFileSystemContext *romfs_ctx, bool use_layere
|
|||
goto end;
|
||||
}
|
||||
|
||||
setvbuf(shared_thread_data->fp, NULL, _IONBF, 0);
|
||||
ftruncate(fileno(shared_thread_data->fp), (off_t)shared_thread_data->total_size);
|
||||
}
|
||||
|
||||
|
@ -3978,6 +3983,7 @@ static void extractedHfsReadThreadFunc(void *arg)
|
|||
if (!shared_thread_data->read_error)
|
||||
{
|
||||
/* Set file size. */
|
||||
setvbuf(shared_thread_data->fp, NULL, _IONBF, 0);
|
||||
ftruncate(fileno(shared_thread_data->fp), (off_t)hfs_entry->size);
|
||||
} else {
|
||||
consolePrint("failed to open \"%s\" for writing!\n", hfs_path);
|
||||
|
@ -4365,6 +4371,7 @@ static void extractedPartitionFsReadThreadFunc(void *arg)
|
|||
if (!shared_thread_data->read_error)
|
||||
{
|
||||
/* Set file size. */
|
||||
setvbuf(shared_thread_data->fp, NULL, _IONBF, 0);
|
||||
ftruncate(fileno(shared_thread_data->fp), (off_t)pfs_entry->size);
|
||||
} else {
|
||||
consolePrint("failed to open \"%s\" for writing!\n", pfs_path);
|
||||
|
@ -4683,6 +4690,7 @@ static void extractedRomFsReadThreadFunc(void *arg)
|
|||
if (!shared_thread_data->read_error)
|
||||
{
|
||||
/* Set file size. */
|
||||
setvbuf(shared_thread_data->fp, NULL, _IONBF, 0);
|
||||
ftruncate(fileno(shared_thread_data->fp), (off_t)romfs_file_entry->size);
|
||||
} else {
|
||||
consolePrint("failed to open \"%s\" for writing!\n", romfs_path);
|
||||
|
@ -4948,7 +4956,7 @@ static void nspThreadFunc(void *arg)
|
|||
|
||||
u8 *buf = NULL;
|
||||
char *filename = NULL;
|
||||
FILE *fd = NULL;
|
||||
FILE *fp = NULL;
|
||||
|
||||
NcaContext *nca_ctx = NULL;
|
||||
|
||||
|
@ -5393,18 +5401,19 @@ static void nspThreadFunc(void *arg)
|
|||
}
|
||||
}
|
||||
|
||||
if (!(fd = fopen(filename, "wb")))
|
||||
if (!(fp = fopen(filename, "wb")))
|
||||
{
|
||||
consolePrint("fopen failed\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
// set file size
|
||||
ftruncate(fileno(fd), (off_t)nsp_size);
|
||||
setvbuf(fp, NULL, _IONBF, 0);
|
||||
ftruncate(fileno(fp), (off_t)nsp_size);
|
||||
|
||||
// write placeholder header
|
||||
memset(buf, 0, nsp_header_size);
|
||||
fwrite(buf, 1, nsp_header_size, fd);
|
||||
fwrite(buf, 1, nsp_header_size, fp);
|
||||
}
|
||||
|
||||
consolePrint("dump process started, please wait. hold b to cancel.\n");
|
||||
|
@ -5512,7 +5521,7 @@ static void nspThreadFunc(void *arg)
|
|||
goto end;
|
||||
}
|
||||
} else {
|
||||
fwrite(buf, 1, blksize, fd);
|
||||
fwrite(buf, 1, blksize, fp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5559,7 +5568,7 @@ static void nspThreadFunc(void *arg)
|
|||
goto end;
|
||||
}
|
||||
} else {
|
||||
fwrite(cnmt_ctx.authoring_tool_xml, 1, cnmt_ctx.authoring_tool_xml_size, fd);
|
||||
fwrite(cnmt_ctx.authoring_tool_xml, 1, cnmt_ctx.authoring_tool_xml_size, fp);
|
||||
}
|
||||
|
||||
nsp_offset += cnmt_ctx.authoring_tool_xml_size;
|
||||
|
@ -5613,7 +5622,7 @@ static void nspThreadFunc(void *arg)
|
|||
goto end;
|
||||
}
|
||||
} else {
|
||||
fwrite(icon_ctx->icon_data, 1, icon_ctx->icon_size, fd);
|
||||
fwrite(icon_ctx->icon_data, 1, icon_ctx->icon_size, fp);
|
||||
}
|
||||
|
||||
nsp_offset += icon_ctx->icon_size;
|
||||
|
@ -5650,7 +5659,7 @@ static void nspThreadFunc(void *arg)
|
|||
goto end;
|
||||
}
|
||||
} else {
|
||||
fwrite(authoring_tool_xml, 1, authoring_tool_xml_size, fd);
|
||||
fwrite(authoring_tool_xml, 1, authoring_tool_xml_size, fp);
|
||||
}
|
||||
|
||||
nsp_offset += authoring_tool_xml_size;
|
||||
|
@ -5676,7 +5685,7 @@ static void nspThreadFunc(void *arg)
|
|||
goto end;
|
||||
}
|
||||
} else {
|
||||
fwrite(tik.data, 1, tik.size, fd);
|
||||
fwrite(tik.data, 1, tik.size, fp);
|
||||
}
|
||||
|
||||
nsp_offset += tik.size;
|
||||
|
@ -5692,7 +5701,7 @@ static void nspThreadFunc(void *arg)
|
|||
goto end;
|
||||
}
|
||||
} else {
|
||||
fwrite(raw_cert_chain, 1, raw_cert_chain_size, fd);
|
||||
fwrite(raw_cert_chain, 1, raw_cert_chain_size, fp);
|
||||
}
|
||||
|
||||
nsp_offset += raw_cert_chain_size;
|
||||
|
@ -5714,8 +5723,8 @@ static void nspThreadFunc(void *arg)
|
|||
goto end;
|
||||
}
|
||||
} else {
|
||||
rewind(fd);
|
||||
fwrite(buf, 1, nsp_header_size, fd);
|
||||
rewind(fp);
|
||||
fwrite(buf, 1, nsp_header_size, fp);
|
||||
}
|
||||
|
||||
nsp_thread_data->data_written += nsp_header_size;
|
||||
|
@ -5729,9 +5738,9 @@ end:
|
|||
if (!success && !nsp_thread_data->transfer_cancelled) nsp_thread_data->error = true;
|
||||
mutexUnlock(&g_fileMutex);
|
||||
|
||||
if (fd)
|
||||
if (fp)
|
||||
{
|
||||
fclose(fd);
|
||||
fclose(fp);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
|
|
|
@ -1315,6 +1315,7 @@ static HashFileSystemContext *gamecardInitializeHashFileSystemContext(const char
|
|||
hfs_ctx->size = size;
|
||||
} else {
|
||||
/* Calculate root partition size. */
|
||||
hfs_ctx->size = 1; // Prevents hfsGetEntryByIndex() from returning NULL.
|
||||
HashFileSystemEntry *hfs_entry = hfsGetEntryByIndex(hfs_ctx, hfs_header.entry_count - 1);
|
||||
hfs_ctx->size = (hfs_ctx->header_size + hfs_entry->offset + hfs_entry->size);
|
||||
}
|
||||
|
|
456
source/devoptab/hfs_dev.c
Normal file
456
source/devoptab/hfs_dev.c
Normal file
|
@ -0,0 +1,456 @@
|
|||
/*
|
||||
* hfs_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 HFS_DEV_INIT_VARS DEVOPTAB_INIT_VARS(HashFileSystemContext,)
|
||||
#define HFS_DEV_INIT_FILE_VARS DEVOPTAB_INIT_VARS_WITH_FILE_STATE(HashFileSystemContext, HashFileSystemFileState)
|
||||
#define HFS_DEV_INIT_DIR_VARS DEVOPTAB_INIT_VARS_WITH_DIR_STATE(HashFileSystemContext, HashFileSystemDirectoryState)
|
||||
#define HFS_DEV_INIT_FS_ACCESS DEVOPTAB_INIT_FS_ACCESS(HashFileSystemContext)
|
||||
|
||||
/* Type definitions. */
|
||||
|
||||
typedef struct {
|
||||
HashFileSystemEntry *hfs_entry; ///< Hash FS entry metadata.
|
||||
const char *name; ///< Entry name.
|
||||
u64 offset; ///< Current offset within Hash FS entry data.
|
||||
} HashFileSystemFileState;
|
||||
|
||||
typedef struct {
|
||||
u32 index; ///< Current Hash FS entry index.
|
||||
} HashFileSystemDirectoryState;
|
||||
|
||||
/* Function prototypes. */
|
||||
|
||||
static int hfsdev_open(struct _reent *r, void *fd, const char *path, int flags, int mode);
|
||||
static int hfsdev_close(struct _reent *r, void *fd);
|
||||
static ssize_t hfsdev_read(struct _reent *r, void *fd, char *ptr, size_t len);
|
||||
static off_t hfsdev_seek(struct _reent *r, void *fd, off_t pos, int dir);
|
||||
static int hfsdev_fstat(struct _reent *r, void *fd, struct stat *st);
|
||||
static int hfsdev_stat(struct _reent *r, const char *file, struct stat *st);
|
||||
static DIR_ITER* hfsdev_diropen(struct _reent *r, DIR_ITER *dirState, const char *path);
|
||||
static int hfsdev_dirreset(struct _reent *r, DIR_ITER *dirState);
|
||||
static int hfsdev_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
|
||||
static int hfsdev_dirclose(struct _reent *r, DIR_ITER *dirState);
|
||||
static int hfsdev_statvfs(struct _reent *r, const char *path, struct statvfs *buf);
|
||||
|
||||
static const char *hfsdev_get_truncated_path(struct _reent *r, const char *path);
|
||||
|
||||
static void hfsdev_fill_stat(struct stat *st, const HashFileSystemEntry *hfs_entry, time_t mount_time);
|
||||
|
||||
/* Global variables. */
|
||||
|
||||
static const devoptab_t hfsdev_devoptab = {
|
||||
.name = NULL,
|
||||
.structSize = sizeof(HashFileSystemFileState),
|
||||
.open_r = hfsdev_open,
|
||||
.close_r = hfsdev_close,
|
||||
.write_r = rodev_write, ///< Not supported by Hash FS sections.
|
||||
.read_r = hfsdev_read,
|
||||
.seek_r = hfsdev_seek,
|
||||
.fstat_r = hfsdev_fstat,
|
||||
.stat_r = hfsdev_stat,
|
||||
.link_r = rodev_link, ///< Not supported by Hash FS sections.
|
||||
.unlink_r = rodev_unlink, ///< Not supported by Hash 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 Hash FS sections.
|
||||
.mkdir_r = rodev_mkdir, ///< Not supported by Hash FS sections.
|
||||
.dirStateSize = sizeof(HashFileSystemDirectoryState),
|
||||
.diropen_r = hfsdev_diropen, ///< Hash FS sections don't support directories, but we'll allow operations on the FS root.
|
||||
.dirreset_r = hfsdev_dirreset, ///< Hash FS sections don't support directories, but we'll allow operations on the FS root.
|
||||
.dirnext_r = hfsdev_dirnext, ///< Hash FS sections don't support directories, but we'll allow operations on the FS root.
|
||||
.dirclose_r = hfsdev_dirclose, ///< Hash FS sections don't support directories, but we'll allow operations on the FS root.
|
||||
.statvfs_r = hfsdev_statvfs,
|
||||
.ftruncate_r = rodev_ftruncate, ///< Not supported by Hash FS sections.
|
||||
.fsync_r = rodev_fsync, ///< Not supported by Hash FS sections.
|
||||
.deviceData = NULL,
|
||||
.chmod_r = rodev_chmod, ///< Not supported by Hash FS sections.
|
||||
.fchmod_r = rodev_fchmod, ///< Not supported by Hash FS sections.
|
||||
.rmdir_r = rodev_rmdir, ///< Not supported by Hash FS sections.
|
||||
.lstat_r = hfsdev_stat, ///< Symlinks aren't supported, so we'll just alias lstat() to stat().
|
||||
.utimes_r = rodev_utimes, ///< Not supported by Hash FS sections.
|
||||
.fpathconf_r = rodev_fpathconf, ///< Not supported by Hash FS sections.
|
||||
.pathconf_r = rodev_pathconf, ///< Not supported by Hash FS sections.
|
||||
.symlink_r = rodev_symlink, ///< Not supported by Hash FS sections.
|
||||
.readlink_r = rodev_readlink ///< Not supported by Hash FS sections.
|
||||
};
|
||||
|
||||
const devoptab_t *hfsdev_get_devoptab()
|
||||
{
|
||||
return &hfsdev_devoptab;
|
||||
}
|
||||
|
||||
static int hfsdev_open(struct _reent *r, void *fd, const char *path, int flags, int mode)
|
||||
{
|
||||
NX_IGNORE_ARG(mode);
|
||||
|
||||
HFS_DEV_INIT_FILE_VARS;
|
||||
HFS_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 = hfsdev_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(HashFileSystemFileState));
|
||||
|
||||
/* Get Hash FS entry. */
|
||||
if (!(file->hfs_entry = hfsGetEntryByName(fs_ctx, path)) || !(file->name = hfsGetEntryName(fs_ctx, file->hfs_entry))) DEVOPTAB_SET_ERROR(ENOENT);
|
||||
|
||||
end:
|
||||
DEVOPTAB_DEINIT_VARS;
|
||||
DEVOPTAB_RETURN_INT(0);
|
||||
}
|
||||
|
||||
static int hfsdev_close(struct _reent *r, void *fd)
|
||||
{
|
||||
HFS_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(HashFileSystemFileState));
|
||||
|
||||
end:
|
||||
DEVOPTAB_DEINIT_VARS;
|
||||
DEVOPTAB_RETURN_INT(0);
|
||||
}
|
||||
|
||||
static ssize_t hfsdev_read(struct _reent *r, void *fd, char *ptr, size_t len)
|
||||
{
|
||||
HFS_DEV_INIT_FILE_VARS;
|
||||
HFS_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 (!hfsReadEntryData(fs_ctx, file->hfs_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 hfsdev_seek(struct _reent *r, void *fd, off_t pos, int dir)
|
||||
{
|
||||
off_t offset = 0;
|
||||
|
||||
HFS_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->hfs_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->hfs_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 hfsdev_fstat(struct _reent *r, void *fd, struct stat *st)
|
||||
{
|
||||
HFS_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. */
|
||||
hfsdev_fill_stat(st, file->hfs_entry, dev_ctx->mount_time);
|
||||
|
||||
end:
|
||||
DEVOPTAB_DEINIT_VARS;
|
||||
DEVOPTAB_RETURN_INT(0);
|
||||
}
|
||||
|
||||
static int hfsdev_stat(struct _reent *r, const char *file, struct stat *st)
|
||||
{
|
||||
HashFileSystemEntry *hfs_entry = NULL;
|
||||
|
||||
HFS_DEV_INIT_VARS;
|
||||
HFS_DEV_INIT_FS_ACCESS;
|
||||
|
||||
/* Sanity check. */
|
||||
if (!st) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL);
|
||||
|
||||
/* Get truncated path. */
|
||||
if (!(file = hfsdev_get_truncated_path(r, file))) DEVOPTAB_EXIT;
|
||||
|
||||
LOG_MSG_DEBUG("Getting file stats for \"%s:/%s\".", dev_ctx->name, file);
|
||||
|
||||
/* Get Hash FS entry. */
|
||||
if (!(hfs_entry = hfsGetEntryByName(fs_ctx, file))) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL);
|
||||
|
||||
/* Fill stat info. */
|
||||
hfsdev_fill_stat(st, hfs_entry, dev_ctx->mount_time);
|
||||
|
||||
end:
|
||||
DEVOPTAB_DEINIT_VARS;
|
||||
DEVOPTAB_RETURN_INT(0);
|
||||
}
|
||||
|
||||
static DIR_ITER *hfsdev_diropen(struct _reent *r, DIR_ITER *dirState, const char *path)
|
||||
{
|
||||
DIR_ITER *ret = NULL;
|
||||
|
||||
HFS_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 = hfsdev_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(HashFileSystemDirectoryState));
|
||||
|
||||
/* Update return value. */
|
||||
ret = dirState;
|
||||
|
||||
end:
|
||||
DEVOPTAB_DEINIT_VARS;
|
||||
DEVOPTAB_RETURN_PTR(ret);
|
||||
}
|
||||
|
||||
static int hfsdev_dirreset(struct _reent *r, DIR_ITER *dirState)
|
||||
{
|
||||
HFS_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 hfsdev_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat)
|
||||
{
|
||||
HashFileSystemEntry *hfs_entry = NULL;
|
||||
const char *fname = NULL;
|
||||
|
||||
HFS_DEV_INIT_DIR_VARS;
|
||||
HFS_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 >= hfsGetEntryCount(fs_ctx)) DEVOPTAB_SET_ERROR_AND_EXIT(ENOENT);
|
||||
|
||||
/* Get Hash FS entry. */
|
||||
if (!(hfs_entry = hfsGetEntryByIndex(fs_ctx, dir->index)) || !(fname = hfsGetEntryName(fs_ctx, hfs_entry))) DEVOPTAB_SET_ERROR_AND_EXIT(EIO);
|
||||
|
||||
/* Copy filename. */
|
||||
strcpy(filename, fname);
|
||||
|
||||
/* Fill stat info. */
|
||||
hfsdev_fill_stat(filestat, hfs_entry, dev_ctx->mount_time);
|
||||
|
||||
/* Adjust index. */
|
||||
dir->index++;
|
||||
|
||||
end:
|
||||
DEVOPTAB_DEINIT_VARS;
|
||||
DEVOPTAB_RETURN_INT(0);
|
||||
}
|
||||
|
||||
static int hfsdev_dirclose(struct _reent *r, DIR_ITER *dirState)
|
||||
{
|
||||
HFS_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(HashFileSystemDirectoryState));
|
||||
|
||||
DEVOPTAB_DEINIT_VARS;
|
||||
DEVOPTAB_RETURN_INT(0);
|
||||
}
|
||||
|
||||
static int hfsdev_statvfs(struct _reent *r, const char *path, struct statvfs *buf)
|
||||
{
|
||||
NX_IGNORE_ARG(path);
|
||||
|
||||
u64 ext_fs_size = 0;
|
||||
|
||||
HFS_DEV_INIT_VARS;
|
||||
HFS_DEV_INIT_FS_ACCESS;
|
||||
|
||||
/* Sanity check. */
|
||||
if (!buf) DEVOPTAB_SET_ERROR_AND_EXIT(EINVAL);
|
||||
|
||||
LOG_MSG_DEBUG("Getting filesystem stats for \"%s:\"", dev_ctx->name);
|
||||
|
||||
/* Get Hash FS total data size. */
|
||||
if (!hfsGetTotalDataSize(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 *hfsdev_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 hfsdev_fill_stat(struct stat *st, const HashFileSystemEntry *hfs_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)hfs_entry->size;
|
||||
st->st_mode = (S_IFREG | S_IRUSR | S_IRGRP | S_IROTH);
|
||||
st->st_atime = st->st_mtime = st->st_ctime = mount_time;
|
||||
}
|
|
@ -42,6 +42,7 @@ static const u32 g_devoptabDeviceCount = MAX_ELEMENTS(g_devoptabDevices);
|
|||
/* Function prototypes. */
|
||||
|
||||
const devoptab_t *pfsdev_get_devoptab();
|
||||
const devoptab_t *hfsdev_get_devoptab();
|
||||
|
||||
static bool devoptabMountDevice(void *fs_ctx, const char *name, u8 type);
|
||||
static DevoptabDeviceContext *devoptabFindDevice(const char *name);
|
||||
|
@ -172,6 +173,7 @@ static bool devoptabMountDevice(void *fs_ctx, const char *name, u8 type)
|
|||
device = pfsdev_get_devoptab();
|
||||
break;
|
||||
case DevoptabDeviceType_HashFileSystem:
|
||||
device = hfsdev_get_devoptab();
|
||||
break;
|
||||
case DevoptabDeviceType_RomFileSystem:
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue