From 61b398a89aadab32bc596ead38258c63dce8ce0b Mon Sep 17 00:00:00 2001 From: ITotalJustice <47043333+ITotalJustice@users.noreply.github.com> Date: Sun, 7 Sep 2025 17:35:37 +0100 Subject: [PATCH] fatfs: use devoptab mounting. devoptab: add config for hidding from fs and dump, fix http being writeable. --- sphaira/CMakeLists.txt | 2 +- sphaira/include/fatfs.hpp | 10 - sphaira/include/location.hpp | 5 +- sphaira/include/utils/devoptab.hpp | 1 + sphaira/include/utils/devoptab_common.hpp | 7 +- sphaira/source/app.cpp | 18 +- sphaira/source/dumper.cpp | 14 +- sphaira/source/location.cpp | 26 +- sphaira/source/ui/menus/filebrowser.cpp | 14 +- sphaira/source/utils/devoptab_common.cpp | 135 +++--- .../{fatfs.cpp => utils/devoptab_fatfs.cpp} | 389 +++++++++--------- sphaira/source/utils/devoptab_http.cpp | 3 +- 12 files changed, 332 insertions(+), 292 deletions(-) delete mode 100644 sphaira/include/fatfs.hpp rename sphaira/source/{fatfs.cpp => utils/devoptab_fatfs.cpp} (54%) diff --git a/sphaira/CMakeLists.txt b/sphaira/CMakeLists.txt index f866269..dcf43b8 100644 --- a/sphaira/CMakeLists.txt +++ b/sphaira/CMakeLists.txt @@ -90,7 +90,6 @@ add_executable(sphaira source/threaded_file_transfer.cpp source/title_info.cpp source/minizip_helper.cpp - source/fatfs.cpp source/usbdvd.cpp source/utils/utils.cpp @@ -111,6 +110,7 @@ add_executable(sphaira source/utils/devoptab_ftp.cpp source/utils/devoptab_webdav.cpp source/utils/devoptab_vfs.cpp + source/utils/devoptab_fatfs.cpp source/usb/base.cpp source/usb/usbds.cpp diff --git a/sphaira/include/fatfs.hpp b/sphaira/include/fatfs.hpp deleted file mode 100644 index 0a2d759..0000000 --- a/sphaira/include/fatfs.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -namespace sphaira::fatfs { - -Result MountAll(); -void UnmountAll(); - -} // namespace sphaira::fatfs diff --git a/sphaira/include/location.hpp b/sphaira/include/location.hpp index aa43dd7..8a9523f 100644 --- a/sphaira/include/location.hpp +++ b/sphaira/include/location.hpp @@ -23,12 +23,15 @@ struct StdioEntry { u32 flags{}; // optional dump path inside the mount point. std::string dump_path{}; + // set to hide for filebrowser. + bool fs_hidden{}; + // set to hide in dump list. + bool dump_hidden{}; }; using StdioEntries = std::vector; // set write=true to filter out write protected devices. auto GetStdio(bool write) -> StdioEntries; -auto GetFat() -> StdioEntries; } // namespace sphaira::location diff --git a/sphaira/include/utils/devoptab.hpp b/sphaira/include/utils/devoptab.hpp index c86b43e..cc65cad 100644 --- a/sphaira/include/utils/devoptab.hpp +++ b/sphaira/include/utils/devoptab.hpp @@ -39,6 +39,7 @@ Result MountHttpAll(); Result MountFtpAll(); Result MountNfsAll(); Result MountSmb2All(); +Result MountFatfsAll(); Result GetNetworkDevices(location::StdioEntries& out); void UmountAllNeworkDevices(); diff --git a/sphaira/include/utils/devoptab_common.hpp b/sphaira/include/utils/devoptab_common.hpp index c6e4c9f..a9b0969 100644 --- a/sphaira/include/utils/devoptab_common.hpp +++ b/sphaira/include/utils/devoptab_common.hpp @@ -134,6 +134,8 @@ struct MountConfig { bool read_only{}; bool no_stat_file{true}; bool no_stat_dir{true}; + bool fs_hidden{}; + bool dump_hidden{}; std::unordered_map extra{}; }; @@ -212,6 +214,9 @@ private: }; using CreateDeviceCallback = std::function(const MountConfig& config)>; -Result MountNetworkDevice(const CreateDeviceCallback& create_device, size_t file_size, size_t dir_size, const char* name); +Result MountNetworkDevice(const CreateDeviceCallback& create_device, size_t file_size, size_t dir_size, const char* name, bool force_read_only = false); + +// same as above but takes in the device and expects the mount name to be set. +bool MountNetworkDevice2(std::unique_ptr&& device, const MountConfig& config, size_t file_size, size_t dir_size, const char* name, const char* mount_name); } // namespace sphaira::devoptab::common diff --git a/sphaira/source/app.cpp b/sphaira/source/app.cpp index c1126bd..ab244e9 100644 --- a/sphaira/source/app.cpp +++ b/sphaira/source/app.cpp @@ -22,7 +22,6 @@ #include "haze_helper.hpp" #include "web.hpp" #include "swkbd.hpp" -#include "fatfs.hpp" #include "usbdvd.hpp" #include "utils/profile.hpp" @@ -1579,13 +1578,6 @@ App::App(const char* argv0) { usbHsFsInitialize(1); } - { - SCOPED_TIMESTAMP("fat init"); - if (R_FAILED(fatfs::MountAll())) { - log_write("[FAT] failed to mount bis\n"); - } - } - { SCOPED_TIMESTAMP("usbdvd init"); if (R_FAILED(usbdvd::MountAll())) { @@ -1629,6 +1621,11 @@ App::App(const char* argv0) { devoptab::MountSmb2All(); } + { + SCOPED_TIMESTAMP("fatfs init"); + devoptab::MountFatfsAll(); + } + { SCOPED_TIMESTAMP("timestamp init"); // ini_putl(GetExePath(), "timestamp", m_start_timestamp, App::PLAYLOG_PATH); @@ -2226,11 +2223,6 @@ App::~App() { usbHsFsExit(); } - { - SCOPED_TIMESTAMP("fatfs exit"); - fatfs::UnmountAll(); - } - // this has to come before curl exit as it uses curl global. { SCOPED_TIMESTAMP("devoptab exit"); diff --git a/sphaira/source/dumper.cpp b/sphaira/source/dumper.cpp index d9c410d..41bd72d 100644 --- a/sphaira/source/dumper.cpp +++ b/sphaira/source/dumper.cpp @@ -494,11 +494,17 @@ void DumpGetLocation(const std::string& title, u32 location_flags, const OnLocat ui::PopupList::Items items; std::vector dump_entries; - out.stdio = location::GetStdio(true); + const auto stdio_entries = location::GetStdio(true); if (location_flags & (1 << DumpLocationType_Stdio)) { - for (s32 i = 0; i < std::size(out.stdio); i++) { - dump_entries.emplace_back(DumpLocationType_Stdio, i); - items.emplace_back(out.stdio[i].name); + for (auto& e : stdio_entries) { + if (e.dump_hidden) { + continue; + } + + const auto index = out.stdio.size(); + dump_entries.emplace_back(DumpLocationType_Stdio, index); + items.emplace_back(e.name); + out.stdio.emplace_back(e); } } diff --git a/sphaira/source/location.cpp b/sphaira/source/location.cpp index d513702..1bdbee8 100644 --- a/sphaira/source/location.cpp +++ b/sphaira/source/location.cpp @@ -4,9 +4,7 @@ #include "usbdvd.hpp" #include "utils/devoptab.hpp" -#include #include -#include #include namespace sphaira::location { @@ -17,13 +15,17 @@ namespace { auto GetStdio(bool write) -> StdioEntries { StdioEntries out{}; - const auto add_from_entries = [](const StdioEntries& entries, StdioEntries& out, bool write) { - for (const auto& e : entries) { + const auto add_from_entries = [](StdioEntries& entries, StdioEntries& out, bool write) { + for (auto& e : entries) { if (write && (e.flags & FsEntryFlag::FsEntryFlag_ReadOnly)) { log_write("[STDIO] skipping read only mount: %s\n", e.name.c_str()); continue; } + if (e.flags & FsEntryFlag::FsEntryFlag_ReadOnly) { + e.name += " (Read Only)"; + } + out.emplace_back(e); } }; @@ -80,20 +82,4 @@ auto GetStdio(bool write) -> StdioEntries { return out; } -auto GetFat() -> StdioEntries { - StdioEntries out{}; - - for (auto& e : VolumeStr) { - char path[64]; - std::snprintf(path, sizeof(path), "%s:/", e); - - char display_name[0x100]; - std::snprintf(display_name, sizeof(display_name), "%s (Read Only)", path); - - out.emplace_back(path, display_name, FsEntryFlag::FsEntryFlag_ReadOnly); - } - - return out; -} - } // namespace sphaira::location diff --git a/sphaira/source/ui/menus/filebrowser.cpp b/sphaira/source/ui/menus/filebrowser.cpp index 446416d..0fc6c0e 100644 --- a/sphaira/source/ui/menus/filebrowser.cpp +++ b/sphaira/source/ui/menus/filebrowser.cpp @@ -1595,12 +1595,6 @@ void FsView::DisplayOptions() { SidebarEntryArray::Items mount_items; std::vector fs_entries; - const auto stdio_locations = location::GetStdio(false); - for (const auto& e: stdio_locations) { - fs_entries.emplace_back(e.name, e.mount, FsType::Stdio, e.flags); - mount_items.push_back(e.name); - } - for (const auto& e: FS_ENTRIES) { fs_entries.emplace_back(e); mount_items.push_back(i18n::get(e.name)); @@ -1611,8 +1605,12 @@ void FsView::DisplayOptions() { mount_items.push_back(m_menu->m_custom_fs_entry.name); } - const auto fat_entries = location::GetFat(); - for (const auto& e: fat_entries) { + const auto stdio_locations = location::GetStdio(false); + for (const auto& e: stdio_locations) { + if (e.fs_hidden) { + continue; + } + fs_entries.emplace_back(e.name, e.mount, FsType::Stdio, e.flags); mount_items.push_back(e.name); } diff --git a/sphaira/source/utils/devoptab_common.cpp b/sphaira/source/utils/devoptab_common.cpp index d48b79c..65335e6 100644 --- a/sphaira/source/utils/devoptab_common.cpp +++ b/sphaira/source/utils/devoptab_common.cpp @@ -759,7 +759,68 @@ void update_devoptab_for_read_only(devoptab_t* devoptab, bool read_only) { } } -Result MountNetworkDevice(const CreateDeviceCallback& create_device, size_t file_size, size_t dir_size, const char* name) { +bool MountNetworkDevice2(std::unique_ptr&& device, const MountConfig& config, size_t file_size, size_t dir_size, const char* name, const char* mount_name) { + if (!device) { + log_write("[DEVOPTAB] No device for %s\n", mount_name); + return false; + } + + bool already_mounted = false; + for (const auto& entry : g_entries) { + if (entry && entry->mount == mount_name) { + already_mounted = true; + break; + } + } + + if (already_mounted) { + log_write("[DEVOPTAB] Already mounted %s, skipping\n", mount_name); + return false; + } + + // otherwise, find next free entry. + auto itr = std::ranges::find_if(g_entries, [](auto& e){ + return !e; + }); + + if (itr == g_entries.end()) { + log_write("[DEVOPTAB] No free entries to mount %s\n", mount_name); + return false; + } + + auto entry = std::make_unique(); + entry->device.mount_device = std::forward(device); + entry->device.file_size = file_size; + entry->device.dir_size = dir_size; + entry->device.config = config; + + if (!entry->device.mount_device) { + log_write("[DEVOPTAB] Failed to create device for %s\n", config.url.c_str()); + return false; + } + + entry->devoptab = DEVOPTAB; + entry->devoptab.name = entry->name; + entry->devoptab.deviceData = &entry->device; + std::snprintf(entry->name, sizeof(entry->name), "%s", name); + std::snprintf(entry->mount, sizeof(entry->mount), "%s", mount_name); + common::update_devoptab_for_read_only(&entry->devoptab, config.read_only); + + if (AddDevice(&entry->devoptab) < 0) { + log_write("[DEVOPTAB] Failed to add device %s\n", mount_name); + return false; + } + + log_write("[DEVOPTAB] DEVICE SUCCESS %s %s\n", name, mount_name); + + entry->ref_count++; + *itr = std::move(entry); + log_write("[DEVOPTAB] Mounted %s at /%s\n", name, mount_name); + + return true; +} + +Result MountNetworkDevice(const CreateDeviceCallback& create_device, size_t file_size, size_t dir_size, const char* name, bool force_read_only) { { static Mutex rw_lock_init_mutex{}; SCOPED_MUTEX(&rw_lock_init_mutex); @@ -809,6 +870,10 @@ Result MountNetworkDevice(const CreateDeviceCallback& create_device, size_t file e->back().no_stat_file = ini_parse_getbool(Value, e->back().no_stat_file); } else if (!std::strcmp(Key, "no_stat_dir")) { e->back().no_stat_dir = ini_parse_getbool(Value, e->back().no_stat_dir); + } else if (!std::strcmp(Key, "fs_hidden")) { + e->back().fs_hidden = ini_parse_getbool(Value, e->back().fs_hidden); + } else if (!std::strcmp(Key, "dump_hidden")) { + e->back().dump_hidden = ini_parse_getbool(Value, e->back().dump_hidden); } else { log_write("[DEVOPTAB] INI: extra key %s=%s\n", Key, Value); e->back().extra.emplace(Key, Value); @@ -824,55 +889,31 @@ Result MountNetworkDevice(const CreateDeviceCallback& create_device, size_t file ini_browse(cb, &configs, config_path); log_write("[DEVOPTAB] Found %zu mount configs\n", configs.size()); - for (const auto& config : configs) { - // check if we already have the http mounted. - bool already_mounted = false; - for (const auto& entry : g_entries) { - if (entry && entry->mount == config.name) { - already_mounted = true; - break; - } - } - - if (already_mounted) { - log_write("[DEVOPTAB] Already mounted %s, skipping\n", config.name.c_str()); + for (auto& config : configs) { + if (config.name.empty()) { + log_write("[DEVOPTAB] Skipping empty name\n"); continue; } - // otherwise, find next free entry. - auto itr = std::ranges::find_if(g_entries, [](auto& e){ - return !e; - }); - - if (itr == g_entries.end()) { - log_write("[DEVOPTAB] No free entries to mount %s\n", config.name.c_str()); - break; - } - - auto entry = std::make_unique(); - entry->device.mount_device = create_device(config); - entry->device.file_size = file_size; - entry->device.dir_size = dir_size; - entry->device.config = config; - - if (!entry->device.mount_device) { - log_write("[DEVOPTAB] Failed to create device for %s\n", config.url.c_str()); + if (config.url.empty()) { + log_write("[DEVOPTAB] Skipping empty url for %s\n", config.name.c_str()); continue; } - entry->devoptab = DEVOPTAB; - entry->devoptab.name = entry->name; - entry->devoptab.deviceData = &entry->device; - std::snprintf(entry->name, sizeof(entry->name), "[%s] %s", name, config.name.c_str()); - std::snprintf(entry->mount, sizeof(entry->mount), "[%s] %s:/", name, config.name.c_str()); - common::update_devoptab_for_read_only(&entry->devoptab, config.read_only); + if (force_read_only) { + config.read_only = true; + } - R_UNLESS(AddDevice(&entry->devoptab) >= 0, 0x1); - log_write("[DEVOPTAB] DEVICE SUCCESS %s %s\n", entry->device.config.url.c_str(), entry->name); + fs::FsPath _name{}; + std::snprintf(_name, sizeof(_name), "[%s] %s", name, config.name.c_str()); - entry->ref_count++; - *itr = std::move(entry); - log_write("[DEVOPTAB] Mounted %s at /%s\n", config.url.c_str(), config.name.c_str()); + fs::FsPath _mount{}; + std::snprintf(_mount, sizeof(_mount), "[%s] %s:/", name, config.name.c_str()); + + if (!MountNetworkDevice2(create_device(config), config, file_size, dir_size, _name, _mount)) { + log_write("[DEVOPTAB] Failed to mount %s\n", config.name.c_str()); + continue; + } } R_SUCCEED(); @@ -1373,18 +1414,20 @@ Result GetNetworkDevices(location::StdioEntries& out) { for (const auto& entry : g_entries) { if (entry) { + const auto& config = entry->device.config; + u32 flags = 0; - if (entry->device.config.read_only) { + if (config.read_only) { flags |= location::FsEntryFlag::FsEntryFlag_ReadOnly; } - if (entry->device.config.no_stat_file) { + if (config.no_stat_file) { flags |= location::FsEntryFlag::FsEntryFlag_NoStatFile; } - if (entry->device.config.no_stat_dir) { + if (config.no_stat_dir) { flags |= location::FsEntryFlag::FsEntryFlag_NoStatDir; } - out.emplace_back(entry->mount, entry->name, flags, entry->device.config.dump_path); + out.emplace_back(entry->mount, entry->name, flags, config.dump_path, config.fs_hidden, config.dump_hidden); } } diff --git a/sphaira/source/fatfs.cpp b/sphaira/source/utils/devoptab_fatfs.cpp similarity index 54% rename from sphaira/source/fatfs.cpp rename to sphaira/source/utils/devoptab_fatfs.cpp index 4114984..bb1092a 100644 --- a/sphaira/source/fatfs.cpp +++ b/sphaira/source/utils/devoptab_fatfs.cpp @@ -1,27 +1,49 @@ -#include "utils/devoptab.hpp" #include "utils/devoptab_common.hpp" -#include "fatfs.hpp" -#include "defines.hpp" +#include "utils/profile.hpp" + #include "log.hpp" -#include "ff.h" +#include "defines.hpp" -#include -#include -#include +#include + +#include +#include #include - #include -#include -#include -#include +#include +#include -namespace sphaira::fatfs { +namespace sphaira::devoptab { namespace { -auto is_archive(BYTE attr) -> bool { - const auto archive_attr = AM_DIR | AM_ARC; - return (attr & archive_attr) == archive_attr; -} +enum BisMountType { + BisMountType_PRODINFOF, + BisMountType_SAFE, + BisMountType_USER, + BisMountType_SYSTEM, +}; + +struct FatStorageEntry { + FsStorage storage; + std::unique_ptr buffered; + FATFS fs; +}; + +struct BisMountEntry { + const FsBisPartitionId id; + const char* volume_name; + const char* mount_name; +}; + +constexpr BisMountEntry BIS_MOUNT_ENTRIES[] { + [BisMountType_PRODINFOF] = { FsBisPartitionId_CalibrationFile, "PRODINFOF", "PRODINFOF:/" }, + [BisMountType_SAFE] = { FsBisPartitionId_SafeMode, "SAFE", "SAFE:/" }, + [BisMountType_USER] = { FsBisPartitionId_User, "USER", "USER:/" }, + [BisMountType_SYSTEM] = { FsBisPartitionId_System, "SYSTEM", "SYSTEM:/" }, +}; +static_assert(std::size(BIS_MOUNT_ENTRIES) == FF_VOLUMES); + +FatStorageEntry g_fat_storage[FF_VOLUMES]; // todo: replace with off+size and have the data be in another struct // in order to be more lcache efficient. @@ -48,14 +70,51 @@ struct File { FIL* files; u32 file_count; size_t off; - char path[256]; + char path[PATH_MAX]; }; struct Dir { FDIR dir; - char path[256]; + char path[PATH_MAX]; }; +struct Device final : common::MountDevice { + Device(BisMountType type, const common::MountConfig& _config) + : MountDevice{_config} + , m_type{type} { + + } + + ~Device(); + +private: + bool fix_path(const char* str, char* out, bool strip_leading_slash = false) override { + std::strcpy(out, str); + return true; + } + + bool Mount() override; + int devoptab_open(void *fileStruct, const char *path, int flags, int mode) override; + int devoptab_close(void *fd) override; + ssize_t devoptab_read(void *fd, char *ptr, size_t len) override; + off_t devoptab_seek(void *fd, off_t pos, int dir) override; + int devoptab_fstat(void *fd, struct stat *st) override; + int devoptab_diropen(void* fd, const char *path) override; + int devoptab_dirreset(void* fd) override; + int devoptab_dirnext(void* fd, char *filename, struct stat *filestat) override; + int devoptab_dirclose(void* fd) override; + int devoptab_lstat(const char *path, struct stat *st) override; + +private: + const BisMountType m_type; + bool mounted{}; +}; + +auto is_archive(BYTE attr) -> bool { + const auto archive_attr = AM_DIR | AM_ARC; + return (attr & archive_attr) == archive_attr; +} + u64 get_size_from_files(const File* file) { u64 size = 0; for (u32 i = 0; i < file->file_count; i++) { @@ -90,38 +149,8 @@ void set_current_file_pos(File* file) { } } -enum BisMountType { - BisMountType_PRODINFOF, - BisMountType_SAFE, - BisMountType_USER, - BisMountType_SYSTEM, -}; - -struct FatStorageEntry { - FsStorage storage; - std::unique_ptr buffered; - FATFS fs; - devoptab_t devoptab; -}; - -struct BisMountEntry { - const FsBisPartitionId id; - const char* volume_name; - const char* mount_name; -}; - -constexpr BisMountEntry BIS_MOUNT_ENTRIES[] { - [BisMountType_PRODINFOF] = { FsBisPartitionId_CalibrationFile, "PRODINFOF", "PRODINFOF:/" }, - [BisMountType_SAFE] = { FsBisPartitionId_SafeMode, "SAFE", "SAFE:/" }, - [BisMountType_USER] = { FsBisPartitionId_User, "USER", "USER:/" }, - [BisMountType_SYSTEM] = { FsBisPartitionId_System, "SYSTEM", "SYSTEM:/" }, -}; -static_assert(std::size(BIS_MOUNT_ENTRIES) == FF_VOLUMES); - -FatStorageEntry g_fat_storage[FF_VOLUMES]; - void fill_stat(const char* path, const FILINFO* fno, struct stat *st) { - memset(st, 0, sizeof(*st)); + std::memset(st, 0, sizeof(*st)); st->st_nlink = 1; @@ -133,7 +162,7 @@ void fill_stat(const char* path, const FILINFO* fno, struct stat *st) { tm.tm_mon = ((fno->fdate >> 5) & 0xF) - 1; tm.tm_year = (fno->fdate >> 9) + 80; - st->st_atime = mktime(&tm); + st->st_atime = std::mktime(&tm); st->st_mtime = st->st_atime; st->st_ctime = st->st_atime; @@ -143,7 +172,7 @@ void fill_stat(const char* path, const FILINFO* fno, struct stat *st) { char file_path[256]; for (u16 i = 0; i < 256; i++) { std::snprintf(file_path, sizeof(file_path), "%s/%02u", path, i); - FILINFO file_info; + FILINFO file_info{}; if (FR_OK != f_stat(file_path, &file_info)) { break; } @@ -152,8 +181,7 @@ void fill_stat(const char* path, const FILINFO* fno, struct stat *st) { } st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - } else - if (fno->fattrib & AM_DIR) { + } else if (fno->fattrib & AM_DIR) { st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; } else { st->st_size = fno->fsize; @@ -161,31 +189,80 @@ void fill_stat(const char* path, const FILINFO* fno, struct stat *st) { } } -static int set_errno(struct _reent *r, int err) { - r->_errno = err; - return -1; +Device::~Device() { + if (mounted) { + auto& fat = g_fat_storage[m_type]; + f_unmount(BIS_MOUNT_ENTRIES[m_type].mount_name); + fat.buffered.reset(); + fsStorageClose(&fat.storage); + } } -int fat_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode) { +bool Device::Mount() { + if (mounted) { + return true; + } + + auto& fat = g_fat_storage[m_type]; + + if (!serviceIsActive(&fat.storage.s)) { + const auto res = fsOpenBisStorage(&fat.storage, BIS_MOUNT_ENTRIES[m_type].id); + if (R_FAILED(res)) { + log_write("[FATFS] fsOpenBisStorage(%d) failed: 0x%x\n", BIS_MOUNT_ENTRIES[m_type].id, res); + return false; + } + } else { + log_write("[FATFS] Storage for %s already opened\n", BIS_MOUNT_ENTRIES[m_type].mount_name); + } + + if (!fat.buffered) { + auto source = std::make_shared(&fat.storage); + + s64 size; + if (R_FAILED(source->GetSize(&size))) { + log_write("[FATFS] Failed to get size of storage source\n"); + return false; + } + + fat.buffered = std::make_unique(source, size); + if (!fat.buffered) { + log_write("[FATFS] Failed to create LruBufferedData\n"); + return false; + } + } + + if (FR_OK != f_mount(&fat.fs, BIS_MOUNT_ENTRIES[m_type].mount_name, 1)) { + log_write("[FATFS] f_mount(%s) failed\n", BIS_MOUNT_ENTRIES[m_type].mount_name); + return false; + } + + log_write("[FATFS] Mounted %s at %s\n", BIS_MOUNT_ENTRIES[m_type].volume_name, BIS_MOUNT_ENTRIES[m_type].mount_name); + return mounted = true; +} + +int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mode) { auto file = static_cast(fileStruct); - std::memset(file, 0, sizeof(*file)); // todo: init array // todo: handle dir. FIL fil{}; if (FR_OK == f_open(&fil, path, FA_READ)) { - file->file_count = 1; file->files = (FIL*)std::malloc(sizeof(*file->files)); + if (!file->files) { + return -ENOMEM; + } + + file->file_count = 1; std::memcpy(file->files, &fil, sizeof(*file->files)); // todo: check what error code is returned here. } else { FILINFO info{}; if (FR_OK != f_stat(path, &info)) { - return set_errno(r, ENOENT); + return -ENOENT; } if (!(info.fattrib & AM_ARC)) { - return set_errno(r, ENOENT); + return -ENOENT; } char file_path[256]; @@ -198,33 +275,35 @@ int fat_open(struct _reent *r, void *fileStruct, const char *path, int flags, in } file->files = (FIL*)std::realloc(file->files, (i + 1) * sizeof(*file->files)); + if (!file->files) { + return -ENOMEM; + } + std::memcpy(&file->files[i], &fil, sizeof(fil)); file->file_count++; } } if (!file->files) { - return set_errno(r, ENOENT); + return -ENOENT; } std::snprintf(file->path, sizeof(file->path), "%s", path); - return r->_errno = 0; + return 0; } -int fat_close(struct _reent *r, void *fd) { +int Device::devoptab_close(void *fd) { auto file = static_cast(fd); - if (file->files) { - for (u32 i = 0; i < file->file_count; i++) { - f_close(&file->files[i]); - } - free(file->files); + for (u32 i = 0; i < file->file_count; i++) { + f_close(&file->files[i]); } - return r->_errno = 0; + std::free(file->files); + return 0; } -ssize_t fat_read(struct _reent *r, void *fd, char *ptr, size_t len) { +ssize_t Device::devoptab_read(void *fd, char *ptr, size_t len) { auto file = static_cast(fd); UINT total_bytes_read = 0; @@ -233,11 +312,11 @@ ssize_t fat_read(struct _reent *r, void *fd, char *ptr, size_t len) { auto fil = get_current_file(file); if (!fil) { log_write("[FATFS] failed to get fil\n"); - return set_errno(r, ENOENT); + return -EIO; } if (FR_OK != f_read(fil, ptr, len, &bytes_read)) { - return set_errno(r, ENOENT); + return -EIO; } if (!bytes_read) { @@ -252,7 +331,7 @@ ssize_t fat_read(struct _reent *r, void *fd, char *ptr, size_t len) { return total_bytes_read; } -off_t fat_seek(struct _reent *r, void *fd, off_t pos, int dir) { +off_t Device::devoptab_seek(void *fd, off_t pos, int dir) { auto file = static_cast(fd); const auto size = get_size_from_files(file); @@ -265,11 +344,10 @@ off_t fat_seek(struct _reent *r, void *fd, off_t pos, int dir) { file->off = std::clamp(pos, 0, size); set_current_file_pos(file); - r->_errno = 0; return file->off; } -int fat_fstat(struct _reent *r, void *fd, struct stat *st) { +int Device::devoptab_fstat(void *fd, struct stat *st) { auto file = static_cast(fd); /* Only fill the attr and size field, leaving the timestamp blank. */ @@ -279,173 +357,110 @@ int fat_fstat(struct _reent *r, void *fd, struct stat *st) { /* Fill stat info. */ fill_stat(nullptr, &info, st); - return r->_errno = 0; + return 0; } -DIR_ITER* fat_diropen(struct _reent *r, DIR_ITER *dirState, const char *path) { - auto dir = static_cast(dirState->dirStruct); - std::memset(dir, 0, sizeof(*dir)); +int Device::devoptab_diropen(void* fd, const char *path) { + auto dir = static_cast(fd); + log_write("[FATFS] diropen: %s\n", path); if (FR_OK != f_opendir(&dir->dir, path)) { - set_errno(r, ENOENT); - return NULL; + log_write("[FATFS] f_opendir(%s) failed\n", path); + return -ENOENT; } - r->_errno = 0; - return dirState; + log_write("[FATFS] Opened dir: %s\n", path); + return 0; } -int fat_dirreset(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirreset(void* fd) { + auto dir = static_cast(fd); if (FR_OK != f_rewinddir(&dir->dir)) { - return set_errno(r, ENOENT); + return -EIO; } - return r->_errno = 0; + return 0; } -int fat_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) { + auto dir = static_cast(fd); FILINFO fno{}; if (FR_OK != f_readdir(&dir->dir, &fno)) { - return set_errno(r, ENOENT); + return -EIO; } if (!fno.fname[0]) { - return set_errno(r, ENOENT); + return -EIO; } - strcpy(filename, fno.fname); + std::strcpy(filename, fno.fname); fill_stat(dir->path, &fno, filestat); - return r->_errno = 0; + return 0; } -int fat_dirclose(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirclose(void* fd) { + auto dir = static_cast(fd); if (FR_OK != f_closedir(&dir->dir)) { - return set_errno(r, ENOENT); + return -EIO; } - return r->_errno = 0; + return 0; } -int fat_statvfs(struct _reent *r, const char *path, struct statvfs *buf) { - memset(buf, 0, sizeof(*buf)); - - // todo: find out how to calculate free size in read only. - const auto fat = (FatStorageEntry*)r->deviceData; - buf->f_bsize = FF_MAX_SS; - buf->f_frsize = FF_MAX_SS; - buf->f_blocks = ((fat->fs.n_fatent - 2) * (DWORD)fat->fs.csize); - buf->f_namemax = FF_LFN_BUF; - - return r->_errno = 0; -} - -int fat_lstat(struct _reent *r, const char *file, struct stat *st) { +int Device::devoptab_lstat(const char *path, struct stat *st) { FILINFO fno; - if (FR_OK != f_stat(file, &fno)) { - return set_errno(r, ENOENT); + if (FR_OK != f_stat(path, &fno)) { + return -ENOENT; } - fill_stat(file, &fno, st); - return r->_errno = 0; + fill_stat(path, &fno, st); + return 0; } -constexpr devoptab_t DEVOPTAB = { - .structSize = sizeof(File), - .open_r = fat_open, - .close_r = fat_close, - .read_r = fat_read, - .seek_r = fat_seek, - .fstat_r = fat_fstat, - .stat_r = fat_lstat, - .dirStateSize = sizeof(Dir), - .diropen_r = fat_diropen, - .dirreset_r = fat_dirreset, - .dirnext_r = fat_dirnext, - .dirclose_r = fat_dirclose, - .statvfs_r = fat_statvfs, - .lstat_r = fat_lstat, -}; - -Mutex g_mutex{}; -bool g_is_init{}; - } // namespace -Result MountAll() { - SCOPED_MUTEX(&g_mutex); - - if (g_is_init) { - R_SUCCEED(); - } - +Result MountFatfsAll() { for (u32 i = 0; i < FF_VOLUMES; i++) { - auto& fat = g_fat_storage[i]; const auto& bis = BIS_MOUNT_ENTRIES[i]; - // log_write("[FAT] %s\n", bis.volume_name); + common::MountConfig config{}; + config.url = "fatfs dummy url"; + config.read_only = true; + config.dump_hidden = true; - fat.devoptab = DEVOPTAB; - fat.devoptab.name = bis.volume_name; - fat.devoptab.deviceData = &fat; - - R_TRY(fsOpenBisStorage(&fat.storage, bis.id)); - auto source = std::make_shared(&fat.storage); - - s64 size; - R_TRY(source->GetSize(&size)); - // log_write("[FAT] BIS SUCCESS %s\n", bis.volume_name); - - fat.buffered = std::make_unique(source, size); - - R_UNLESS(FR_OK == f_mount(&fat.fs, bis.mount_name, 1), 0x1); - // log_write("[FAT] MOUNT SUCCESS %s\n", bis.volume_name); - - R_UNLESS(AddDevice(&fat.devoptab) >= 0, 0x1); - // log_write("[FAT] DEVICE SUCCESS %s\n", bis.volume_name); + if (!common::MountNetworkDevice2( + std::make_unique((BisMountType)i, config), + config, + sizeof(File), sizeof(Dir), + bis.volume_name, bis.mount_name + )) { + log_write("[FATFS] Failed to mount %s\n", bis.volume_name); + } } - g_is_init = true; R_SUCCEED(); } -void UnmountAll() { - SCOPED_MUTEX(&g_mutex); - - if (!g_is_init) { - return; - } - - for (u32 i = 0; i < FF_VOLUMES; i++) { - auto& fat = g_fat_storage[i]; - const auto& bis = BIS_MOUNT_ENTRIES[i]; - - RemoveDevice(bis.mount_name); - f_unmount(bis.mount_name); - fsStorageClose(&fat.storage); - } -} - -} // namespace sphaira::fatfs +} // namespace sphaira::devoptab extern "C" { const char* VolumeStr[] { - sphaira::fatfs::BIS_MOUNT_ENTRIES[0].volume_name, - sphaira::fatfs::BIS_MOUNT_ENTRIES[1].volume_name, - sphaira::fatfs::BIS_MOUNT_ENTRIES[2].volume_name, - sphaira::fatfs::BIS_MOUNT_ENTRIES[3].volume_name, + sphaira::devoptab::BIS_MOUNT_ENTRIES[0].volume_name, + sphaira::devoptab::BIS_MOUNT_ENTRIES[1].volume_name, + sphaira::devoptab::BIS_MOUNT_ENTRIES[2].volume_name, + sphaira::devoptab::BIS_MOUNT_ENTRIES[3].volume_name, }; Result fatfs_read(u8 num, void* dst, u64 offset, u64 size) { // log_write("[FAT] num: %u\n", num); - auto& fat = sphaira::fatfs::g_fat_storage[num]; + log_write("[FAT] read: %s, off: 0x%lx, size: 0x%lx\n", VolumeStr[num], offset, size); + auto& fat = sphaira::devoptab::g_fat_storage[num]; + log_write("[FAT] buffered: %p\n", fat.buffered.get()); return fat.buffered->Read2(dst, offset, size); } diff --git a/sphaira/source/utils/devoptab_http.cpp b/sphaira/source/utils/devoptab_http.cpp index b6aecea..bb86095 100644 --- a/sphaira/source/utils/devoptab_http.cpp +++ b/sphaira/source/utils/devoptab_http.cpp @@ -410,7 +410,8 @@ Result MountHttpAll() { return std::make_unique(config); }, sizeof(File), sizeof(Dir), - "HTTP" + "HTTP", + true ); }