From c392a9f1264c88fb751d19415873084be8974c50 Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Thu, 21 Dec 2023 00:39:56 +0100 Subject: [PATCH] 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. --- code_templates/nxdt_rw_poc.c | 37 ++- source/core/gamecard.c | 1 + source/devoptab/hfs_dev.c | 456 ++++++++++++++++++++++++++++++++ source/devoptab/nxdt_devoptab.c | 2 + 4 files changed, 482 insertions(+), 14 deletions(-) create mode 100644 source/devoptab/hfs_dev.c diff --git a/code_templates/nxdt_rw_poc.c b/code_templates/nxdt_rw_poc.c index 3d2a628..79dfdee 100644 --- a/code_templates/nxdt_rw_poc.c +++ b/code_templates/nxdt_rw_poc.c @@ -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) { diff --git a/source/core/gamecard.c b/source/core/gamecard.c index 4f77af4..a6c3a6b 100644 --- a/source/core/gamecard.c +++ b/source/core/gamecard.c @@ -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); } diff --git a/source/devoptab/hfs_dev.c b/source/devoptab/hfs_dev.c new file mode 100644 index 0000000..0752c7e --- /dev/null +++ b/source/devoptab/hfs_dev.c @@ -0,0 +1,456 @@ +/* + * hfs_dev.c + * + * Loosely based on fs_dev.c from libnx, et al. + * + * Copyright (c) 2020-2023, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * nxdumptool is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "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; +} diff --git a/source/devoptab/nxdt_devoptab.c b/source/devoptab/nxdt_devoptab.c index a27ed7e..8a6251d 100644 --- a/source/devoptab/nxdt_devoptab.c +++ b/source/devoptab/nxdt_devoptab.c @@ -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;