diff --git a/sphaira/include/utils/devoptab.hpp b/sphaira/include/utils/devoptab.hpp index cc65cad..676df61 100644 --- a/sphaira/include/utils/devoptab.hpp +++ b/sphaira/include/utils/devoptab.hpp @@ -9,29 +9,15 @@ namespace sphaira::devoptab { -// mounts to "lower_case_hex_id:/" Result MountSaveSystem(u64 id, fs::FsPath& out_path); -void UnmountSave(u64 id); - Result MountZip(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path); -void UmountZip(const fs::FsPath& mount); - Result MountNsp(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path); -void UmountNsp(const fs::FsPath& mount); - Result MountXci(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path); Result MountXciSource(const std::shared_ptr& source, s64 size, const fs::FsPath& path, fs::FsPath& out_path); -void UmountXci(const fs::FsPath& mount); - Result MountNca(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path); Result MountNcaNcm(NcmContentStorage* cs, const NcmContentId* id, fs::FsPath& out_path); -void UmountNca(const fs::FsPath& mount); - Result MountBfsar(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path); -void UmountBfsar(const fs::FsPath& mount); - Result MountNro(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path); -void UmountNro(const fs::FsPath& mount); Result MountVfsAll(); Result MountWebdavAll(); @@ -43,5 +29,6 @@ Result MountFatfsAll(); Result GetNetworkDevices(location::StdioEntries& out); void UmountAllNeworkDevices(); +void UmountNeworkDevice(const fs::FsPath& mount); } // namespace sphaira::devoptab diff --git a/sphaira/include/utils/devoptab_common.hpp b/sphaira/include/utils/devoptab_common.hpp index a9b0969..a328f1d 100644 --- a/sphaira/include/utils/devoptab_common.hpp +++ b/sphaira/include/utils/devoptab_common.hpp @@ -185,7 +185,6 @@ struct MountDevice { struct MountCurlDevice : MountDevice { using MountDevice::MountDevice; - // MountCurlDevice(const MountConfig& _config); virtual ~MountCurlDevice(); PushThreadData* CreatePushData(CURL* curl, const std::string& url, size_t offset); @@ -219,4 +218,6 @@ Result MountNetworkDevice(const CreateDeviceCallback& create_device, size_t file // 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); +bool MountReadOnlyIndexDevice(const CreateDeviceCallback& create_device, size_t file_size, size_t dir_size, const char* name, fs::FsPath& out_path); + } // namespace sphaira::devoptab::common diff --git a/sphaira/source/ui/menus/filebrowser.cpp b/sphaira/source/ui/menus/filebrowser.cpp index 0fc6c0e..4446a1c 100644 --- a/sphaira/source/ui/menus/filebrowser.cpp +++ b/sphaira/source/ui/menus/filebrowser.cpp @@ -711,15 +711,15 @@ void FsView::OnClick() { } }); } else if (IsExtension(entry.GetExtension(), NCA_EXTENSIONS)) { - MountFileFs(devoptab::MountNca, devoptab::UmountNca); + MountFileFs(devoptab::MountNca, devoptab::UmountNeworkDevice); } else if (IsExtension(entry.GetExtension(), NSP_EXTENSIONS)) { - MountFileFs(devoptab::MountNsp, devoptab::UmountNsp); + MountFileFs(devoptab::MountNsp, devoptab::UmountNeworkDevice); } else if (IsExtension(entry.GetExtension(), XCI_EXTENSIONS)) { - MountFileFs(devoptab::MountXci, devoptab::UmountXci); + MountFileFs(devoptab::MountXci, devoptab::UmountNeworkDevice); } else if (IsExtension(entry.GetExtension(), "zip")) { - MountFileFs(devoptab::MountZip, devoptab::UmountZip); + MountFileFs(devoptab::MountZip, devoptab::UmountNeworkDevice); } else if (IsExtension(entry.GetExtension(), "bfsar")) { - MountFileFs(devoptab::MountBfsar, devoptab::UmountBfsar); + MountFileFs(devoptab::MountBfsar, devoptab::UmountNeworkDevice); } else if (IsExtension(entry.GetExtension(), MUSIC_EXTENSIONS)) { App::Push(GetFs(), GetNewPathCurrent()); } else if (IsExtension(entry.GetExtension(), IMAGE_EXTENSIONS)) { diff --git a/sphaira/source/ui/menus/game_nca_menu.cpp b/sphaira/source/ui/menus/game_nca_menu.cpp index 7de3487..d1e7663 100644 --- a/sphaira/source/ui/menus/game_nca_menu.cpp +++ b/sphaira/source/ui/menus/game_nca_menu.cpp @@ -413,7 +413,7 @@ Result Menu::MountNcaFs() { R_TRY(devoptab::MountNcaNcm(m_meta.cs, &e.content_id, root)); auto fs = std::make_shared(root, [root](){ - devoptab::UmountNca(root); + devoptab::UmountNeworkDevice(root); }); filebrowser::MountFsHelper(fs, utils::hexIdToStr(e.content_id).str); diff --git a/sphaira/source/ui/menus/gc_menu.cpp b/sphaira/source/ui/menus/gc_menu.cpp index c3cfe83..d3c6bfa 100644 --- a/sphaira/source/ui/menus/gc_menu.cpp +++ b/sphaira/source/ui/menus/gc_menu.cpp @@ -1322,7 +1322,7 @@ Result Menu::MountGcFs() { R_TRY(devoptab::MountXciSource(source, m_storage_trimmed_size, e.lang_entry.name, root)); auto fs = std::make_shared(root, [root](){ - devoptab::UmountXci(root); + devoptab::UmountNeworkDevice(root); }); filebrowser::MountFsHelper(fs, e.lang_entry.name); diff --git a/sphaira/source/ui/menus/homebrew.cpp b/sphaira/source/ui/menus/homebrew.cpp index e49969b..092d515 100644 --- a/sphaira/source/ui/menus/homebrew.cpp +++ b/sphaira/source/ui/menus/homebrew.cpp @@ -527,7 +527,7 @@ Result Menu::MountNroFs() { R_TRY(devoptab::MountNro(App::GetApp()->m_fs.get(), e.path, root)); auto fs = std::make_shared(root, [root](){ - devoptab::UmountNro(root); + devoptab::UmountNeworkDevice(root); }); filebrowser::MountFsHelper(fs, root); diff --git a/sphaira/source/ui/menus/save_menu.cpp b/sphaira/source/ui/menus/save_menu.cpp index 6ecb2dd..a351d72 100644 --- a/sphaira/source/ui/menus/save_menu.cpp +++ b/sphaira/source/ui/menus/save_menu.cpp @@ -1131,8 +1131,8 @@ Result Menu::MountSaveFs() { fs::FsPath root; R_TRY(devoptab::MountSaveSystem(e.system_save_data_id, root)); - auto fs = std::make_shared(root, [&e](){ - devoptab::UnmountSave(e.system_save_data_id); + auto fs = std::make_shared(root, [root](){ + devoptab::UmountNeworkDevice(root); }); filebrowser::MountFsHelper(fs, e.GetName()); diff --git a/sphaira/source/utils/devoptab_bfsar.cpp b/sphaira/source/utils/devoptab_bfsar.cpp index 4bc03c7..bec740e 100644 --- a/sphaira/source/utils/devoptab_bfsar.cpp +++ b/sphaira/source/utils/devoptab_bfsar.cpp @@ -4,8 +4,6 @@ #include "defines.hpp" #include "log.hpp" -#include "yati/container/nsp.hpp" -#include "yati/container/xci.hpp" #include "yati/source/file.hpp" #include @@ -15,24 +13,16 @@ #include #include #include -#include namespace sphaira::devoptab { namespace { -struct Device { - PLSR_BFSAR bfsar; - std::FILE* file; // points to archive file. -}; - struct File { - Device* device; PLSR_BFWARFileInfo info; size_t off; }; struct Dir { - Device* device; u32 index; }; @@ -82,56 +72,69 @@ PLSR_RC GetFileInfo(const PLSR_BFSAR *bfsar, std::string_view path, PLSR_BFWARFi } -int set_errno(struct _reent *r, int err) { - r->_errno = err; - return -1; -} - -int devoptab_open(struct _reent *r, void *fileStruct, const char *_path, int flags, int mode) { - auto device = (Device*)r->deviceData; - auto file = static_cast(fileStruct); - std::memset(file, 0, sizeof(*file)); - - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - return set_errno(r, ENOENT); +struct Device final : common::MountDevice { + Device(const PLSR_BFSAR& _bfsar, const common::MountConfig& _config) + : MountDevice{_config} + , bfsar{_bfsar} { + this->file = this->bfsar.ar.handle->f; } + ~Device() { + plsrBFSARClose(&bfsar); + } + +private: + bool Mount() override { return true; } + 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: + PLSR_BFSAR bfsar; + std::FILE* file; // points to archive file. +}; + +int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mode) { + auto file = static_cast(fileStruct); + PLSR_BFWARFileInfo info; - if (R_FAILED(GetFileInfo(&device->bfsar, path, info))) { - return set_errno(r, ENOENT); + if (R_FAILED(GetFileInfo(&this->bfsar, path, info))) { + return -ENOENT; } - file->device = device; file->info = info; - return r->_errno = 0; + return 0; } -int devoptab_close(struct _reent *r, void *fd) { +int Device::devoptab_close(void *fd) { auto file = static_cast(fd); std::memset(file, 0, sizeof(*file)); - return r->_errno = 0; + return 0; } -ssize_t devoptab_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); const auto& info = file->info; - // const auto real_len = len; // plsr seems to read oob, so allow for some tollerance. const auto oob_allowed = 64; len = std::min(len, info.size + oob_allowed - file->off); - std::fseek(file->device->file, file->info.offset + file->off, SEEK_SET); - const auto bytes_read = std::fread(ptr, 1, len, file->device->file); - - // log_write("bytes read: %zu len: %zu real_len: %zu off: %zu size: %u\n", bytes_read, len, real_len, file->off, info.size); + std::fseek(this->file, file->info.offset + file->off, SEEK_SET); + const auto bytes_read = std::fread(ptr, 1, len, this->file); file->off += bytes_read; return bytes_read; } -off_t devoptab_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& info = file->info; @@ -141,11 +144,10 @@ off_t devoptab_seek(struct _reent *r, void *fd, off_t pos, int dir) { pos = info.size; } - r->_errno = 0; return file->off = std::clamp(pos, 0, info.size); } -int devoptab_fstat(struct _reent *r, void *fd, struct stat *st) { +int Device::devoptab_fstat(void *fd, struct stat *st) { auto file = static_cast(fd); const auto& info = file->info; @@ -153,51 +155,36 @@ int devoptab_fstat(struct _reent *r, void *fd, struct stat *st) { st->st_nlink = 1; st->st_size = info.size; st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - return r->_errno = 0; + return 0; } -DIR_ITER* devoptab_diropen(struct _reent *r, DIR_ITER *dirState, const char *_path) { - auto device = (Device*)r->deviceData; - auto dir = static_cast(dirState->dirStruct); - std::memset(dir, 0, sizeof(*dir)); - - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - set_errno(r, ENOENT); - return NULL; - } - +int Device::devoptab_diropen(void* fd, const char *path) { if (!std::strcmp(path, "/")) { - dir->device = device; - } else { - set_errno(r, ENOENT); - return NULL; + return 0; } - r->_errno = 0; - return dirState; + return -ENOENT; } -int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirreset(void* fd) { + auto dir = static_cast(fd); dir->index = 0; - return r->_errno = 0; + return 0; } -int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) { - auto dir = static_cast(dirState->dirStruct); - std::memset(filestat, 0, sizeof(*filestat)); +int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) { + auto dir = static_cast(fd); do { - if (dir->index >= plsrBFSARSoundCount(&dir->device->bfsar)) { - log_write("finished getting call entries: %u vs %u\n", dir->index, plsrBFSARSoundCount(&dir->device->bfsar)); - return set_errno(r, ENOENT); + if (dir->index >= plsrBFSARSoundCount(&this->bfsar)) { + log_write("finished getting call entries: %u vs %u\n", dir->index, plsrBFSARSoundCount(&this->bfsar)); + return -ENOENT; } PLSR_BFSARSoundInfo info{}; - if (R_FAILED(plsrBFSARSoundGet(&dir->device->bfsar, dir->index, &info))) { + if (R_FAILED(plsrBFSARSoundGet(&this->bfsar, dir->index, &info))) { continue; } @@ -206,7 +193,7 @@ int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struc continue; } - if (R_FAILED(plsrBFSARStringGet(&dir->device->bfsar, info.stringIndex, filename, NAME_MAX))) { + if (R_FAILED(plsrBFSARStringGet(&this->bfsar, info.stringIndex, filename, NAME_MAX))) { continue; } @@ -230,139 +217,52 @@ int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struc break; } while (dir->index++); - return r->_errno = 0; + return 0; } -int devoptab_dirclose(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirclose(void* fd) { + auto dir = static_cast(fd); std::memset(dir, 0, sizeof(*dir)); - log_write("[BFSAR] devoptab_dirclose\n"); - return r->_errno = 0; + return 0; } -int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) { - auto device = (Device*)r->deviceData; - - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - return set_errno(r, ENOENT); - } - - std::memset(st, 0, sizeof(*st)); +int Device::devoptab_lstat(const char *path, struct stat *st) { + st->st_nlink = 1; if (!std::strcmp(path, "/")) { st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; } else { PLSR_BFWARFileInfo info{}; - if (R_FAILED(GetFileInfo(&device->bfsar, path, info))) { - return set_errno(r, ENOENT); + if (R_FAILED(GetFileInfo(&this->bfsar, path, info))) { + return -ENOENT; } st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; st->st_size = info.size; } - st->st_nlink = 1; - - return r->_errno = 0; + return 0; } -constexpr devoptab_t DEVOPTAB = { - .structSize = sizeof(File), - .open_r = devoptab_open, - .close_r = devoptab_close, - .read_r = devoptab_read, - .seek_r = devoptab_seek, - .fstat_r = devoptab_fstat, - .stat_r = devoptab_lstat, - .dirStateSize = sizeof(Dir), - .diropen_r = devoptab_diropen, - .dirreset_r = devoptab_dirreset, - .dirnext_r = devoptab_dirnext, - .dirclose_r = devoptab_dirclose, - .lstat_r = devoptab_lstat, -}; - -struct Entry { - Device device{}; - devoptab_t devoptab{}; - fs::FsPath path{}; - fs::FsPath mount{}; - char name[32]{}; - s32 ref_count{}; - - ~Entry() { - log_write("[BFSAR] entry called\n"); - RemoveDevice(mount); - plsrBFSARClose(&device.bfsar); - } -}; - -Mutex g_mutex; -std::array, common::MAX_ENTRIES> g_entries; - } // namespace Result MountBfsar(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { - SCOPED_MUTEX(&g_mutex); + PLSR_BFSAR bfsar{}; + PLSR_RC_TRY(plsrBFSAROpen(path, &bfsar)); - // check if we already have the save mounted. - for (auto& e : g_entries) { - if (e && e->path == path) { - e->ref_count++; - out_path = e->mount; - R_SUCCEED(); - } + if (!common::MountReadOnlyIndexDevice( + [&bfsar](const common::MountConfig& config) { + return std::make_unique(bfsar, config); + }, + sizeof(File), sizeof(Dir), + "BFSAR", out_path + )) { + log_write("[BFSAR] Failed to mount %s\n", path.s); + R_THROW(0x1); } - // otherwise, find next free entry. - auto itr = std::ranges::find_if(g_entries, [](auto& e){ - return !e; - }); - R_UNLESS(itr != g_entries.end(), 0x1); - - const auto index = std::distance(g_entries.begin(), itr); - - auto entry = std::make_unique(); - entry->path = path; - entry->devoptab = DEVOPTAB; - entry->devoptab.name = entry->name; - entry->devoptab.deviceData = &entry->device; - std::snprintf(entry->name, sizeof(entry->name), "BFSAR_%zu", index); - std::snprintf(entry->mount, sizeof(entry->mount), "BFSAR_%zu:/", index); - - PLSR_RC_TRY(plsrBFSAROpen(path, &entry->device.bfsar)); - entry->device.file = entry->device.bfsar.ar.handle->f; - - R_UNLESS(AddDevice(&entry->devoptab) >= 0, 0x1); - log_write("[BFSAR] DEVICE SUCCESS %s %s\n", path.s, entry->name); - - out_path = entry->mount; - entry->ref_count++; - *itr = std::move(entry); - R_SUCCEED(); } -void UmountBfsar(const fs::FsPath& mount) { - SCOPED_MUTEX(&g_mutex); - - auto itr = std::ranges::find_if(g_entries, [&mount](auto& e){ - return e && e->mount == mount; - }); - - if (itr == g_entries.end()) { - return; - } - - if ((*itr)->ref_count) { - (*itr)->ref_count--; - } - - if (!(*itr)->ref_count) { - itr->reset(); - } -} - } // namespace sphaira::devoptab diff --git a/sphaira/source/utils/devoptab_common.cpp b/sphaira/source/utils/devoptab_common.cpp index 65335e6..f9a08d5 100644 --- a/sphaira/source/utils/devoptab_common.cpp +++ b/sphaira/source/utils/devoptab_common.cpp @@ -820,6 +820,39 @@ bool MountNetworkDevice2(std::unique_ptr&& device, const MountConfi return true; } +bool MountReadOnlyIndexDevice(const CreateDeviceCallback& create_device, size_t file_size, size_t dir_size, const char* name, fs::FsPath& out_path) { + static Mutex mutex{}; + static u32 next_index{}; + SCOPED_MUTEX(&mutex); + + MountConfig config{}; + config.read_only = true; + config.no_stat_dir = false; + config.no_stat_file = false; + config.fs_hidden = true; + config.dump_hidden = true; + + const auto index = next_index; + next_index = (next_index + 1) % 30; + + fs::FsPath _name{}; + std::snprintf(_name, sizeof(_name), "%s_%u", name, index); + + fs::FsPath _mount{}; + std::snprintf(_mount, sizeof(_mount), "%s_%u:/", name, index); + + if (!common::MountNetworkDevice2( + create_device(config), + config, file_size, dir_size, + _name, _mount + )) { + return false; + } + + out_path = _mount; + 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{}; @@ -1447,4 +1480,19 @@ void UmountAllNeworkDevices() { } } +void UmountNeworkDevice(const fs::FsPath& mount) { + SCOPED_RWLOCK(&g_rwlock, true); + + auto it = std::ranges::find_if(g_entries, [&](const auto& e){ + return e && e->mount == mount; + }); + + if (it != g_entries.end()) { + log_write("[DEVOPTAB] Unmounting %s\n", (*it)->device.config.url.c_str()); + it->reset(); + } else { + log_write("[DEVOPTAB] No such mount %s\n", mount.s); + } +} + } // sphaira::devoptab diff --git a/sphaira/source/utils/devoptab_fatfs.cpp b/sphaira/source/utils/devoptab_fatfs.cpp index bb1092a..cd8dc1d 100644 --- a/sphaira/source/utils/devoptab_fatfs.cpp +++ b/sphaira/source/utils/devoptab_fatfs.cpp @@ -428,7 +428,6 @@ Result MountFatfsAll() { const auto& bis = BIS_MOUNT_ENTRIES[i]; common::MountConfig config{}; - config.url = "fatfs dummy url"; config.read_only = true; config.dump_hidden = true; @@ -457,10 +456,7 @@ const char* VolumeStr[] { }; Result fatfs_read(u8 num, void* dst, u64 offset, u64 size) { - // log_write("[FAT] num: %u\n", 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_nca.cpp b/sphaira/source/utils/devoptab_nca.cpp index ba98d21..5d1f87d 100644 --- a/sphaira/source/utils/devoptab_nca.cpp +++ b/sphaira/source/utils/devoptab_nca.cpp @@ -20,7 +20,6 @@ #include #include #include -#include namespace sphaira::devoptab { namespace { @@ -74,25 +73,18 @@ struct DirEntry { const yati::container::Collections* pfs0; }; -struct Device { - std::vector collections; - std::unique_ptr source; -}; - struct File { - Device* device; FileEntry entry; size_t off; }; struct Dir { - Device* device; DirEntry entry; u32 index; bool is_root; }; -bool find_file(std::span named, std::string_view path, FileEntry& out) { +bool find_file(std::span named, std::string_view path, FileEntry& out) { for (auto& e : named) { if (path.starts_with("/" + e.name)) { out.fs_type = e.fs_type; @@ -154,55 +146,68 @@ bool find_dir(std::span named, std::string_view path, Dir return false; } -int set_errno(struct _reent *r, int err) { - r->_errno = err; - return -1; -} +struct Device final : common::MountDevice { + Device(std::unique_ptr&& _source, const std::vector& _collections, const common::MountConfig& _config) + : MountDevice{_config} + , source{std::forward(_source)} + , collections{_collections} { -int devoptab_open(struct _reent *r, void *fileStruct, const char *_path, int flags, int mode) { - auto device = (Device*)r->deviceData; + } + +private: + bool Mount() override { return true; } + 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: + std::unique_ptr source; + const std::vector collections; +}; + +int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mode) { auto file = static_cast(fileStruct); std::memset(file, 0, sizeof(*file)); - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - return set_errno(r, ENOENT); + FileEntry entry{}; + if (!find_file(this->collections, path, entry)) { + log_write("[NCAFS] failed to find file entry: %s\n", path); + return -ENOENT; } - FileEntry entry; - if (!find_file(device->collections, path, entry)) { - log_write("[NCAFS] failed to find file entry\n"); - return set_errno(r, ENOENT); - } - - file->device = device; file->entry = entry; - - return r->_errno = 0; + return 0; } -int devoptab_close(struct _reent *r, void *fd) { +int Device::devoptab_close(void *fd) { auto file = static_cast(fd); std::memset(file, 0, sizeof(*file)); - return r->_errno = 0; + return 0; } -ssize_t devoptab_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); const auto& entry = file->entry; u64 bytes_read; len = std::min(len, entry.size - file->off); - if (R_FAILED(file->device->source->Read(ptr, entry.offset + file->off, len, &bytes_read))) { - return set_errno(r, ENOENT); + if (R_FAILED(this->source->Read(ptr, entry.offset + file->off, len, &bytes_read))) { + return -EIO; } file->off += bytes_read; return bytes_read; } -off_t devoptab_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& entry = file->entry; @@ -212,54 +217,39 @@ off_t devoptab_seek(struct _reent *r, void *fd, off_t pos, int dir) { pos = entry.size; } - r->_errno = 0; return file->off = std::clamp(pos, 0, entry.size); } -int devoptab_fstat(struct _reent *r, void *fd, struct stat *st) { +int Device::devoptab_fstat(void *fd, struct stat *st) { auto file = static_cast(fd); const auto& entry = file->entry; - std::memset(st, 0, sizeof(*st)); st->st_nlink = 1; st->st_size = entry.size; st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - return r->_errno = 0; + return 0; } -DIR_ITER* devoptab_diropen(struct _reent *r, DIR_ITER *dirState, const char *_path) { - auto device = (Device*)r->deviceData; - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_diropen(void* fd, const char *path) { + auto dir = static_cast(fd); std::memset(dir, 0, sizeof(*dir)); - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - set_errno(r, ENOENT); - return NULL; - } - if (!std::strcmp(path, "/")) { - dir->device = device; dir->is_root = true; - r->_errno = 0; - return dirState; + return 0; } else { - DirEntry entry; - if (!find_dir(device->collections, path, entry)) { - set_errno(r, ENOENT); - return NULL; + DirEntry entry{}; + if (!find_dir(this->collections, path, entry)) { + return -ENOENT; } - dir->device = device; dir->entry = entry; - - r->_errno = 0; - return dirState; + return 0; } } -int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirreset(void* fd) { + auto dir = static_cast(fd); auto& entry = dir->entry; if (dir->is_root) { @@ -272,30 +262,29 @@ int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) { } } - return r->_errno = 0; + return 0; } -int devoptab_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); auto& entry = dir->entry; - std::memset(filestat, 0, sizeof(*filestat)); if (dir->is_root) { - if (dir->index >= dir->device->collections.size()) { - return set_errno(r, ENOENT); + if (dir->index >= this->collections.size()) { + return -ENOENT; } filestat->st_nlink = 1; filestat->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; - std::strcpy(filename, dir->device->collections[dir->index].name.c_str()); + std::strcpy(filename, this->collections[dir->index].name.c_str()); } else { if (entry.fs_type == nca::FileSystemType_RomFS) { if (!romfs::dirnext(entry.romfs, filename, filestat)) { - return set_errno(r, ENOENT); + return -ENOENT; } } else { if (dir->index >= entry.pfs0->size()) { - return set_errno(r, ENOENT); + return -ENOENT; } const auto& collection = (*entry.pfs0)[dir->index]; @@ -307,109 +296,49 @@ int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struc } dir->index++; - return r->_errno = 0; + return 0; } -int devoptab_dirclose(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirclose(void* fd) { + auto dir = static_cast(fd); std::memset(dir, 0, sizeof(*dir)); - return r->_errno = 0; + return 0; } -int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) { - auto device = (Device*)r->deviceData; - - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - return set_errno(r, ENOENT); - } - - std::memset(st, 0, sizeof(*st)); +int Device::devoptab_lstat(const char *path, struct stat *st) { st->st_nlink = 1; if (!std::strcmp(path, "/")) { st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; } else { // can be optimised for romfs. - FileEntry file_entry; - DirEntry dir_entry; - if (find_file(device->collections, path, file_entry)) { + FileEntry file_entry{}; + DirEntry dir_entry{}; + if (find_file(this->collections, path, file_entry)) { st->st_size = file_entry.size; st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - } else if (find_dir(device->collections, path, dir_entry)) { + } else if (find_dir(this->collections, path, dir_entry)) { st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; } else { - return set_errno(r, ENOENT); + return -ENOENT; } } - return r->_errno = 0; -} - -constexpr devoptab_t DEVOPTAB = { - .structSize = sizeof(File), - .open_r = devoptab_open, - .close_r = devoptab_close, - .read_r = devoptab_read, - .seek_r = devoptab_seek, - .fstat_r = devoptab_fstat, - .stat_r = devoptab_lstat, - .dirStateSize = sizeof(Dir), - .diropen_r = devoptab_diropen, - .dirreset_r = devoptab_dirreset, - .dirnext_r = devoptab_dirnext, - .dirclose_r = devoptab_dirclose, - .lstat_r = devoptab_lstat, -}; - -struct Entry { - Device device{}; - devoptab_t devoptab{}; - fs::FsPath path{}; - fs::FsPath mount{}; - char name[32]{}; - s32 ref_count{}; - - ~Entry() { - RemoveDevice(mount); - } -}; - -Mutex g_mutex; -std::array, common::MAX_ENTRIES> g_entries; - -bool IsAlreadyMounted(const fs::FsPath& path, fs::FsPath& out_path) { - // check if we already have the save mounted. - for (auto& e : g_entries) { - if (e && e->path == path) { - e->ref_count++; - out_path = e->mount; - return true; - } - } - - return false; + return 0; } Result MountNcaInternal(fs::Fs* fs, const std::shared_ptr& source, s64 size, const fs::FsPath& path, fs::FsPath& out_path) { - // otherwise, find next free entry. - auto itr = std::ranges::find_if(g_entries, [](auto& e){ - return !e; - }); - R_UNLESS(itr != g_entries.end(), 0x1); - const auto index = std::distance(g_entries.begin(), itr); - // todo: rather than manually fetching tickets, use spl to // decrypt the nca for use (somehow, look how ams does it?). keys::Keys keys; R_TRY(keys::parse_keys(keys, true)); - nca::Header header; + nca::Header header{}; R_TRY(source->Read2(&header, 0, sizeof(header))); R_TRY(nca::DecryptHeader(&header, keys, header)); - std::unique_ptr nca_reader; + std::unique_ptr nca_reader{}; log_write("[NCA] got header, type: %s\n", nca::GetContentTypeStr(header.content_type)); // check if this is a ncz. @@ -450,7 +379,7 @@ Result MountNcaInternal(fs::Fs* fs, const std::shared_ptr& s ); } - std::vector collections; + std::vector collections{}; const auto& content_type_fs = CONTENT_TYPE_FS_NAMES[header.content_type]; for (u32 i = 0; i < NCA_SECTION_TOTAL; i++) { @@ -489,7 +418,7 @@ Result MountNcaInternal(fs::Fs* fs, const std::shared_ptr& s continue; } - NamedCollection collection; + NamedCollection collection{}; collection.name = content_type_fs[i].name; collection.fs_type = fs_header.fs_type; @@ -535,22 +464,16 @@ Result MountNcaInternal(fs::Fs* fs, const std::shared_ptr& s R_UNLESS(!collections.empty(), 0x9); - auto entry = std::make_unique(); - entry->path = path; - entry->devoptab = DEVOPTAB; - entry->devoptab.name = entry->name; - entry->devoptab.deviceData = &entry->device; - entry->device.source = std::move(nca_reader); - entry->device.collections = std::move(collections); - std::snprintf(entry->name, sizeof(entry->name), "nca_%zu", index); - std::snprintf(entry->mount, sizeof(entry->mount), "nca_%zu:/", index); - - R_UNLESS(AddDevice(&entry->devoptab) >= 0, 0x1); - log_write("[NCA] DEVICE SUCCESS %s %s\n", path.s, entry->name); - - out_path = entry->mount; - entry->ref_count++; - *itr = std::move(entry); + if (!common::MountReadOnlyIndexDevice( + [&nca_reader, &collections](const common::MountConfig& config) { + return std::make_unique(std::move(nca_reader), collections, config); + }, + sizeof(File), sizeof(Dir), + "NCA", out_path + )) { + log_write("[NCA] Failed to mount %s\n", path.s); + R_THROW(0x1); + } R_SUCCEED(); } @@ -558,12 +481,6 @@ Result MountNcaInternal(fs::Fs* fs, const std::shared_ptr& s } // namespace Result MountNca(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { - SCOPED_MUTEX(&g_mutex); - - if (IsAlreadyMounted(path, out_path)) { - R_SUCCEED(); - } - s64 size; auto source = std::make_shared(fs, path); R_TRY(source->GetSize(&size)); @@ -572,42 +489,11 @@ Result MountNca(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { } Result MountNcaNcm(NcmContentStorage* cs, const NcmContentId* id, fs::FsPath& out_path) { - SCOPED_MUTEX(&g_mutex); - - fs::FsPath path; - const auto id_lower = std::byteswap(*(const u64*)id->c); - const auto id_upper = std::byteswap(*(const u64*)(id->c + 0x8)); - std::snprintf(path, sizeof(path), "%016lx%016lx", id_lower, id_upper); - - if (IsAlreadyMounted(path, out_path)) { - R_SUCCEED(); - } - s64 size; auto source = std::make_shared(cs, id); R_TRY(source->GetSize(&size)); - return MountNcaInternal(nullptr, source, size, path, out_path); -} - -void UmountNca(const fs::FsPath& mount) { - SCOPED_MUTEX(&g_mutex); - - auto itr = std::ranges::find_if(g_entries, [&mount](auto& e){ - return e && e->mount == mount; - }); - - if (itr == g_entries.end()) { - return; - } - - if ((*itr)->ref_count) { - (*itr)->ref_count--; - } - - if (!(*itr)->ref_count) { - itr->reset(); - } + return MountNcaInternal(nullptr, source, size, {}, out_path); } } // namespace sphaira::devoptab diff --git a/sphaira/source/utils/devoptab_nro.cpp b/sphaira/source/utils/devoptab_nro.cpp index 761b98d..0676a31 100644 --- a/sphaira/source/utils/devoptab_nro.cpp +++ b/sphaira/source/utils/devoptab_nro.cpp @@ -6,11 +6,6 @@ #include "log.hpp" #include "nro.hpp" -#include "yati/nx/es.hpp" -#include "yati/nx/nca.hpp" -#include "yati/nx/keys.hpp" -#include "yati/nx/crypto.hpp" -#include "yati/container/nsp.hpp" #include "yati/source/file.hpp" #include @@ -18,7 +13,6 @@ #include #include #include -#include namespace sphaira::devoptab { namespace { @@ -48,26 +42,18 @@ struct DirEntry { romfs::DirEntry romfs; }; -struct Device { - std::unique_ptr source; - std::vector collections; - FsTimeStampRaw timestamp; -}; - struct File { - Device* device; FileEntry entry; size_t off; }; struct Dir { - Device* device; DirEntry entry; u32 index; bool is_root; }; -bool find_file(std::span named, std::string_view path, FileEntry& out) { +bool find_file(std::span named, std::string_view path, FileEntry& out) { for (auto& e : named) { if (path.starts_with("/" + e.name)) { out.is_romfs = e.is_romfs; @@ -112,61 +98,75 @@ bool find_dir(std::span named, std::string_view path, Dir return false; } -void fill_timestamp_from_device(const Device* device, struct stat *st) { - st->st_atime = device->timestamp.accessed; - st->st_ctime = device->timestamp.created; - st->st_mtime = device->timestamp.modified; +void fill_timestamp_from_device(const FsTimeStampRaw& timestamp, struct stat *st) { + st->st_atime = timestamp.accessed; + st->st_ctime = timestamp.created; + st->st_mtime = timestamp.modified; } -int set_errno(struct _reent *r, int err) { - r->_errno = err; - return -1; -} +struct Device final : common::MountDevice { + Device(std::unique_ptr&& _source, const std::vector& _collections, const FsTimeStampRaw& _timestamp, const common::MountConfig& _config) + : MountDevice{_config} + , source{std::forward(_source)} + , collections{_collections} + , timestamp{_timestamp} { -int devoptab_open(struct _reent *r, void *fileStruct, const char *_path, int flags, int mode) { - auto device = (Device*)r->deviceData; + } + +private: + bool Mount() override { return true; } + 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: + std::unique_ptr source; + const std::vector collections; + const FsTimeStampRaw timestamp; +}; + +int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mode) { auto file = static_cast(fileStruct); - std::memset(file, 0, sizeof(*file)); - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - return set_errno(r, ENOENT); + FileEntry entry{}; + if (!find_file(this->collections, path, entry)) { + log_write("[NROFS] failed to find file entry: %s\n", path); + return -ENOENT; } - FileEntry entry; - if (!find_file(device->collections, path, entry)) { - log_write("[NROFS] failed to find file entry\n"); - return set_errno(r, ENOENT); - } - - file->device = device; file->entry = entry; - - return r->_errno = 0; + return 0; } -int devoptab_close(struct _reent *r, void *fd) { +int Device::devoptab_close(void *fd) { auto file = static_cast(fd); std::memset(file, 0, sizeof(*file)); - return r->_errno = 0; + return 0; } -ssize_t devoptab_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); const auto& entry = file->entry; u64 bytes_read; len = std::min(len, entry.size - file->off); - if (R_FAILED(file->device->source->Read(ptr, entry.offset + file->off, len, &bytes_read))) { - return set_errno(r, ENOENT); + if (R_FAILED(this->source->Read(ptr, entry.offset + file->off, len, &bytes_read))) { + return -EIO; } file->off += bytes_read; return bytes_read; } -off_t devoptab_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& entry = file->entry; @@ -176,56 +176,40 @@ off_t devoptab_seek(struct _reent *r, void *fd, off_t pos, int dir) { pos = entry.size; } - r->_errno = 0; return file->off = std::clamp(pos, 0, entry.size); } -int devoptab_fstat(struct _reent *r, void *fd, struct stat *st) { +int Device::devoptab_fstat(void *fd, struct stat *st) { auto file = static_cast(fd); const auto& entry = file->entry; - std::memset(st, 0, sizeof(*st)); st->st_nlink = 1; st->st_size = entry.size; st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - fill_timestamp_from_device(file->device, st); + fill_timestamp_from_device(this->timestamp, st); - return r->_errno = 0; + return 0; } -DIR_ITER* devoptab_diropen(struct _reent *r, DIR_ITER *dirState, const char *_path) { - auto device = (Device*)r->deviceData; - auto dir = static_cast(dirState->dirStruct); - std::memset(dir, 0, sizeof(*dir)); - - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - set_errno(r, ENOENT); - return NULL; - } +int Device::devoptab_diropen(void* fd, const char *path) { + auto dir = static_cast(fd); if (!std::strcmp(path, "/")) { - dir->device = device; dir->is_root = true; - r->_errno = 0; - return dirState; + return 0; } else { - DirEntry entry; - if (!find_dir(device->collections, path, entry)) { - set_errno(r, ENOENT); - return NULL; + DirEntry entry{}; + if (!find_dir(this->collections, path, entry)) { + return -ENOENT; } - dir->device = device; dir->entry = entry; - - r->_errno = 0; - return dirState; + return 0; } } -int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirreset(void* fd) { + auto dir = static_cast(fd); auto& entry = dir->entry; if (dir->is_root) { @@ -236,20 +220,19 @@ int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) { } } - return r->_errno = 0; + return 0; } -int devoptab_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); auto& entry = dir->entry; - std::memset(filestat, 0, sizeof(*filestat)); if (dir->is_root) { - if (dir->index >= dir->device->collections.size()) { - return set_errno(r, ENOENT); + if (dir->index >= this->collections.size()) { + return -ENOENT; } - const auto& e = dir->device->collections[dir->index]; + const auto& e = this->collections[dir->index]; if (e.is_romfs) { filestat->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; } else { @@ -262,126 +245,60 @@ int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struc } else { if (entry.is_romfs) { if (!romfs::dirnext(entry.romfs, filename, filestat)) { - return set_errno(r, ENOENT); + return -ENOENT; } } } - fill_timestamp_from_device(dir->device, filestat); + fill_timestamp_from_device(this->timestamp, filestat); dir->index++; - return r->_errno = 0; + return 0; } -int devoptab_dirclose(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirclose(void* fd) { + auto dir = static_cast(fd); std::memset(dir, 0, sizeof(*dir)); - return r->_errno = 0; + return 0; } -int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) { - auto device = (Device*)r->deviceData; - - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - return set_errno(r, ENOENT); - } - - std::memset(st, 0, sizeof(*st)); +int Device::devoptab_lstat(const char *path, struct stat *st) { st->st_nlink = 1; if (!std::strcmp(path, "/")) { st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; } else { // can be optimised for romfs. - FileEntry file_entry; - DirEntry dir_entry; - if (find_file(device->collections, path, file_entry)) { + FileEntry file_entry{}; + DirEntry dir_entry{}; + if (find_file(this->collections, path, file_entry)) { st->st_size = file_entry.size; st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - } else if (find_dir(device->collections, path, dir_entry)) { + } else if (find_dir(this->collections, path, dir_entry)) { st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; } else { - return set_errno(r, ENOENT); + return -ENOENT; } } - fill_timestamp_from_device(device, st); - return r->_errno = 0; -} - -constexpr devoptab_t DEVOPTAB = { - .structSize = sizeof(File), - .open_r = devoptab_open, - .close_r = devoptab_close, - .read_r = devoptab_read, - .seek_r = devoptab_seek, - .fstat_r = devoptab_fstat, - .stat_r = devoptab_lstat, - .dirStateSize = sizeof(Dir), - .diropen_r = devoptab_diropen, - .dirreset_r = devoptab_dirreset, - .dirnext_r = devoptab_dirnext, - .dirclose_r = devoptab_dirclose, - .lstat_r = devoptab_lstat, -}; - -struct Entry { - Device device{}; - devoptab_t devoptab{}; - fs::FsPath path{}; - fs::FsPath mount{}; - char name[32]{}; - s32 ref_count{}; - - ~Entry() { - RemoveDevice(mount); - } -}; - -Mutex g_mutex; -std::array, common::MAX_ENTRIES> g_entries; - -bool IsAlreadyMounted(const fs::FsPath& path, fs::FsPath& out_path) { - // check if we already have the save mounted. - for (auto& e : g_entries) { - if (e && e->path == path) { - e->ref_count++; - out_path = e->mount; - return true; - } - } - - return false; + fill_timestamp_from_device(this->timestamp, st); + return 0; } } // namespace Result MountNro(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { - SCOPED_MUTEX(&g_mutex); - - if (IsAlreadyMounted(path, out_path)) { - R_SUCCEED(); - } - - // otherwise, find next free entry. - auto itr = std::ranges::find_if(g_entries, [](auto& e){ - return !e; - }); - R_UNLESS(itr != g_entries.end(), 0x1); - - const auto index = std::distance(g_entries.begin(), itr); auto source = std::make_unique(fs, path); - NroData data; + NroData data{}; R_TRY(source->Read2(&data, 0, sizeof(data))); R_UNLESS(data.header.magic == NROHEADER_MAGIC, Result_NroBadMagic); - NroAssetHeader asset; + NroAssetHeader asset{}; R_TRY(source->Read2(&asset, data.header.size, sizeof(asset))); R_UNLESS(asset.magic == NROASSETHEADER_MAGIC, Result_NroBadMagic); - std::vector collections; + std::vector collections{}; if (asset.icon.size) { NamedCollection collection{"icon.jpg", false, AssetCollection{data.header.size + asset.icon.offset, asset.icon.size}}; @@ -400,45 +317,21 @@ Result MountNro(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { R_UNLESS(!collections.empty(), 0x9); - auto entry = std::make_unique(); - entry->path = path; - entry->devoptab = DEVOPTAB; - entry->devoptab.name = entry->name; - entry->devoptab.deviceData = &entry->device; - entry->device.source = std::move(source); - entry->device.collections = collections; - fs->GetFileTimeStampRaw(path, &entry->device.timestamp); - std::snprintf(entry->name, sizeof(entry->name), "nro_%zu", index); - std::snprintf(entry->mount, sizeof(entry->mount), "nro_%zu:/", index); + FsTimeStampRaw timestamp{}; + fs->GetFileTimeStampRaw(path, ×tamp); - R_UNLESS(AddDevice(&entry->devoptab) >= 0, 0x1); - log_write("[NRO] DEVICE SUCCESS %s %s\n", path.s, entry->name); - - out_path = entry->mount; - entry->ref_count++; - *itr = std::move(entry); + if (!common::MountReadOnlyIndexDevice( + [&source, &collections, ×tamp](const common::MountConfig& config) { + return std::make_unique(std::move(source), collections, timestamp, config); + }, + sizeof(File), sizeof(Dir), + "NRO", out_path + )) { + log_write("[NRO] Failed to mount %s\n", path.s); + R_THROW(0x1); + } R_SUCCEED(); } -void UmountNro(const fs::FsPath& mount) { - SCOPED_MUTEX(&g_mutex); - - auto itr = std::ranges::find_if(g_entries, [&mount](auto& e){ - return e && e->mount == mount; - }); - - if (itr == g_entries.end()) { - return; - } - - if ((*itr)->ref_count) { - (*itr)->ref_count--; - } - - if (!(*itr)->ref_count) { - itr->reset(); - } -} - } // namespace sphaira::devoptab diff --git a/sphaira/source/utils/devoptab_nsp.cpp b/sphaira/source/utils/devoptab_nsp.cpp index a11fb01..21f4708 100644 --- a/sphaira/source/utils/devoptab_nsp.cpp +++ b/sphaira/source/utils/devoptab_nsp.cpp @@ -13,76 +13,84 @@ #include #include #include -#include namespace sphaira::devoptab { namespace { -struct Device { - std::unique_ptr source; - yati::container::Collections collections; -}; +using Collections = yati::container::Collections; struct File { - Device* device; const yati::container::CollectionEntry* collection; size_t off; }; struct Dir { - Device* device; u32 index; }; -int set_errno(struct _reent *r, int err) { - r->_errno = err; - return -1; -} +struct Device final : common::MountDevice { + Device(std::unique_ptr&& _source, const Collections& _collections, const common::MountConfig& _config) + : MountDevice{_config} + , source{std::forward(_source)} + , collections{_collections} { -int devoptab_open(struct _reent *r, void *fileStruct, const char *_path, int flags, int mode) { - auto device = (Device*)r->deviceData; - auto file = static_cast(fileStruct); - std::memset(file, 0, sizeof(*file)); - - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - return set_errno(r, ENOENT); } - for (const auto& collection : device->collections) { +private: + bool Mount() override { return true; } + 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: + std::unique_ptr source; + const Collections collections; +}; + +int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mode) { + auto file = static_cast(fileStruct); + + for (const auto& collection : this->collections) { if (path == "/" + collection.name) { - file->device = device; file->collection = &collection; - return r->_errno = 0; + return 0; } } - return set_errno(r, ENOENT); + log_write("[NSP] failed to open file %s\n", path); + return -ENOENT; } -int devoptab_close(struct _reent *r, void *fd) { +int Device::devoptab_close(void *fd) { auto file = static_cast(fd); std::memset(file, 0, sizeof(*file)); - return r->_errno = 0; + return 0; } -ssize_t devoptab_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); const auto& collection = file->collection; len = std::min(len, collection->size - file->off); u64 bytes_read; - if (R_FAILED(file->device->source->Read(ptr, collection->offset + file->off, len, &bytes_read))) { - return set_errno(r, ENOENT); + if (R_FAILED(this->source->Read(ptr, collection->offset + file->off, len, &bytes_read))) { + return -EIO; } file->off += bytes_read; return bytes_read; } -off_t devoptab_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& collection = file->collection; @@ -92,159 +100,83 @@ off_t devoptab_seek(struct _reent *r, void *fd, off_t pos, int dir) { pos = collection->size; } - r->_errno = 0; return file->off = std::clamp(pos, 0, collection->size); } -int devoptab_fstat(struct _reent *r, void *fd, struct stat *st) { +int Device::devoptab_fstat(void *fd, struct stat *st) { auto file = static_cast(fd); const auto& collection = file->collection; - std::memset(st, 0, sizeof(*st)); st->st_nlink = 1; st->st_size = collection->size; st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - return r->_errno = 0; + return 0; } -DIR_ITER* devoptab_diropen(struct _reent *r, DIR_ITER *dirState, const char *_path) { - auto device = (Device*)r->deviceData; - auto dir = static_cast(dirState->dirStruct); - std::memset(dir, 0, sizeof(*dir)); - - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - set_errno(r, ENOENT); - return NULL; - } - +int Device::devoptab_diropen(void* fd, const char *path) { if (!std::strcmp(path, "/")) { - dir->device = device; - } else { - set_errno(r, ENOENT); - return NULL; + return 0; } - r->_errno = 0; - return dirState; + log_write("[NSP] failed to open dir %s\n", path); + return -ENOENT; } -int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirreset(void* fd) { + auto dir = static_cast(fd); dir->index = 0; - - return r->_errno = 0; + return 0; } -int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) { - auto dir = static_cast(dirState->dirStruct); - std::memset(filestat, 0, sizeof(*filestat)); +int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) { + auto dir = static_cast(fd); - if (dir->index >= dir->device->collections.size()) { - return set_errno(r, ENOENT); + if (dir->index >= this->collections.size()) { + return -ENOENT; } - const auto& collection = dir->device->collections[dir->index]; + const auto& collection = this->collections[dir->index]; filestat->st_nlink = 1; filestat->st_size = collection.size; filestat->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; std::strcpy(filename, collection.name.c_str()); dir->index++; - return r->_errno = 0; + return 0; } -int devoptab_dirclose(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirclose(void* fd) { + auto dir = static_cast(fd); std::memset(dir, 0, sizeof(*dir)); - return r->_errno = 0; + return 0; } -int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) { - auto device = (Device*)r->deviceData; - - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - return set_errno(r, ENOENT); - } - - std::memset(st, 0, sizeof(*st)); +int Device::devoptab_lstat(const char *path, struct stat *st) { + st->st_nlink = 1; if (!std::strcmp(path, "/")) { st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; } else { - const auto it = std::ranges::find_if(device->collections, [path](auto& e){ + const auto it = std::ranges::find_if(this->collections, [path](auto& e){ return path == "/" + e.name; }); - if (it == device->collections.end()) { - return set_errno(r, ENOENT); + if (it == this->collections.end()) { + return -ENOENT; } st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; st->st_size = it->size; } - st->st_nlink = 1; - - return r->_errno = 0; + return -ENOENT; } -constexpr devoptab_t DEVOPTAB = { - .structSize = sizeof(File), - .open_r = devoptab_open, - .close_r = devoptab_close, - .read_r = devoptab_read, - .seek_r = devoptab_seek, - .fstat_r = devoptab_fstat, - .stat_r = devoptab_lstat, - .dirStateSize = sizeof(Dir), - .diropen_r = devoptab_diropen, - .dirreset_r = devoptab_dirreset, - .dirnext_r = devoptab_dirnext, - .dirclose_r = devoptab_dirclose, - .lstat_r = devoptab_lstat, -}; - -struct Entry { - Device device{}; - devoptab_t devoptab{}; - fs::FsPath path{}; - fs::FsPath mount{}; - char name[32]{}; - s32 ref_count{}; - - ~Entry() { - RemoveDevice(mount); - } -}; - -Mutex g_mutex; -std::array, common::MAX_ENTRIES> g_entries; - } // namespace Result MountNsp(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { - SCOPED_MUTEX(&g_mutex); - - // check if we already have the save mounted. - for (auto& e : g_entries) { - if (e && e->path == path) { - e->ref_count++; - out_path = e->mount; - R_SUCCEED(); - } - } - - // otherwise, find next free entry. - auto itr = std::ranges::find_if(g_entries, [](auto& e){ - return !e; - }); - R_UNLESS(itr != g_entries.end(), 0x1); - - const auto index = std::distance(g_entries.begin(), itr); auto source = std::make_shared(fs, path); s64 size; @@ -255,44 +187,18 @@ Result MountNsp(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { yati::container::Collections collections; R_TRY(nsp.GetCollections(collections)); - auto entry = std::make_unique(); - entry->path = path; - entry->devoptab = DEVOPTAB; - entry->devoptab.name = entry->name; - entry->devoptab.deviceData = &entry->device; - entry->device.source = std::move(buffered); - entry->device.collections = collections; - std::snprintf(entry->name, sizeof(entry->name), "nsp_%zu", index); - std::snprintf(entry->mount, sizeof(entry->mount), "nsp_%zu:/", index); - - R_UNLESS(AddDevice(&entry->devoptab) >= 0, 0x1); - log_write("[NSP] DEVICE SUCCESS %s %s\n", path.s, entry->name); - - out_path = entry->mount; - entry->ref_count++; - *itr = std::move(entry); + if (!common::MountReadOnlyIndexDevice( + [&buffered, &collections](const common::MountConfig& config) { + return std::make_unique(std::move(buffered), collections, config); + }, + sizeof(File), sizeof(Dir), + "NSP", out_path + )) { + log_write("[NSP] Failed to mount %s\n", path.s); + R_THROW(0x1); + } R_SUCCEED(); } -void UmountNsp(const fs::FsPath& mount) { - SCOPED_MUTEX(&g_mutex); - - auto itr = std::ranges::find_if(g_entries, [&mount](auto& e){ - return e && e->mount == mount; - }); - - if (itr == g_entries.end()) { - return; - } - - if ((*itr)->ref_count) { - (*itr)->ref_count--; - } - - if (!(*itr)->ref_count) { - itr->reset(); - } -} - } // namespace sphaira::devoptab diff --git a/sphaira/source/utils/devoptab_save.cpp b/sphaira/source/utils/devoptab_save.cpp index 3781bb3..e84181b 100644 --- a/sphaira/source/utils/devoptab_save.cpp +++ b/sphaira/source/utils/devoptab_save.cpp @@ -12,18 +12,11 @@ #include #include #include -#include namespace sphaira::devoptab { namespace { -struct Device { - save_ctx_t* ctx; - hierarchical_save_file_table_ctx_t* file_table; -}; - struct File { - Device* device; save_fs_list_entry_t entry; allocation_table_storage_ctx_t storage; size_t off; @@ -35,138 +28,140 @@ struct DirNext { }; struct Dir { - Device* device; save_fs_list_entry_t entry; u32 next_directory; u32 next_file; }; -int set_errno(struct _reent *r, int err) { - r->_errno = err; - return -1; -} +struct Device final : common::MountDevice { + Device(save_ctx_t* _ctx, const common::MountConfig& _config) + : MountDevice{_config} + , ctx{_ctx} { + file_table = &ctx->save_filesystem_core.file_table; + } -int devoptab_open(struct _reent *r, void *fileStruct, const char *_path, int flags, int mode) { - auto device = (Device*)r->deviceData; + ~Device() { + save_close_savefile(&this->ctx); + } + +private: + bool Mount() override { return true; } + 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: + save_ctx_t* ctx; + hierarchical_save_file_table_ctx_t* file_table; +}; + +int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mode) { auto file = static_cast(fileStruct); - std::memset(file, 0, sizeof(*file)); - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - return set_errno(r, ENOENT); + if (!save_hierarchical_file_table_get_file_entry_by_path(this->file_table, path, &file->entry)) { + return -ENOENT; } - if (!save_hierarchical_file_table_get_file_entry_by_path(device->file_table, path, &file->entry)) { - return set_errno(r, ENOENT); + if (!save_open_fat_storage(&this->ctx->save_filesystem_core, &file->storage, file->entry.value.save_file_info.start_block)) { + return -ENOENT; } - if (!save_open_fat_storage(&device->ctx->save_filesystem_core, &file->storage, file->entry.value.save_file_info.start_block)) { - return set_errno(r, ENOENT); - } - - file->device = device; - return r->_errno = 0; + return 0; } -int devoptab_close(struct _reent *r, void *fd) { +int Device::devoptab_close(void *fd) { auto file = static_cast(fd); std::memset(file, 0, sizeof(*file)); - return r->_errno = 0; + return 0; } -ssize_t devoptab_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); + len = std::min(len, file->entry.value.save_file_info.length - file->off); + + if (!len) { + return 0; + } // todo: maybe eof here? const auto bytes_read = save_allocation_table_storage_read(&file->storage, ptr, file->off, len); if (!bytes_read) { - return set_errno(r, ENOENT); + return -ENOENT; } file->off += bytes_read; return bytes_read; } -off_t devoptab_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); if (dir == SEEK_CUR) { pos += file->off; } else if (dir == SEEK_END) { - pos = file->storage._length; + pos = file->entry.value.save_file_info.length; } - r->_errno = 0; - return file->off = std::clamp(pos, 0, file->storage._length); + return file->off = std::clamp(pos, 0, file->entry.value.save_file_info.length); } -int devoptab_fstat(struct _reent *r, void *fd, struct stat *st) { +int Device::devoptab_fstat(void *fd, struct stat *st) { auto file = static_cast(fd); - log_write("[\t\tDEV] fstat\n"); - std::memset(st, 0, sizeof(*st)); st->st_nlink = 1; - st->st_size = file->storage._length; + st->st_size = file->entry.value.save_file_info.length; st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - return r->_errno = 0; + return 0; } -DIR_ITER* devoptab_diropen(struct _reent *r, DIR_ITER *dirState, const char *_path) { - auto device = (Device*)r->deviceData; - auto dir = static_cast(dirState->dirStruct); - std::memset(dir, 0, sizeof(*dir)); - - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - set_errno(r, ENOENT); - return NULL; - } +int Device::devoptab_diropen(void* fd, const char *path) { + auto dir = static_cast(fd); if (!std::strcmp(path, "/")) { save_entry_key_t key{}; - const auto idx = save_fs_list_get_index_from_key(&device->file_table->directory_table, &key, NULL); + const auto idx = save_fs_list_get_index_from_key(&this->file_table->directory_table, &key, NULL); if (idx == 0xFFFFFFFF) { - set_errno(r, ENOENT); - return NULL; + return -ENOENT; } - if (!save_fs_list_get_value(&device->file_table->directory_table, idx, &dir->entry)) { - set_errno(r, ENOENT); - return NULL; + if (!save_fs_list_get_value(&this->file_table->directory_table, idx, &dir->entry)) { + return -ENOENT; } - } else if (!save_hierarchical_directory_table_get_file_entry_by_path(device->file_table, path, &dir->entry)) { - set_errno(r, ENOENT); - return NULL; + } else if (!save_hierarchical_directory_table_get_file_entry_by_path(this->file_table, path, &dir->entry)) { + return -ENOENT; } - dir->device = device; dir->next_file = dir->entry.value.save_find_position.next_file; dir->next_directory = dir->entry.value.save_find_position.next_directory; - r->_errno = 0; - return dirState; + return 0; } -int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirreset(void* fd) { + auto dir = static_cast(fd); dir->next_file = dir->entry.value.save_find_position.next_file; dir->next_directory = dir->entry.value.save_find_position.next_directory; - return r->_errno = 0; + return 0; } -int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) { - auto dir = static_cast(dirState->dirStruct); - - std::memset(filestat, 0, sizeof(*filestat)); +int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) { + auto dir = static_cast(fd); save_fs_list_entry_t entry{}; if (dir->next_directory) { // todo: use save_allocation_table_storage_read for faster reads - if (!save_fs_list_get_value(&dir->device->file_table->directory_table, dir->next_directory, &entry)) { - return set_errno(r, ENOENT); + if (!save_fs_list_get_value(&this->file_table->directory_table, dir->next_directory, &entry)) { + return -ENOENT; } filestat->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; @@ -174,113 +169,55 @@ int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struc } else if (dir->next_file) { // todo: use save_allocation_table_storage_read for faster reads - if (!save_fs_list_get_value(&dir->device->file_table->file_table, dir->next_file, &entry)) { - return set_errno(r, ENOENT); + if (!save_fs_list_get_value(&this->file_table->file_table, dir->next_file, &entry)) { + return -ENOENT; } filestat->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - // todo: confirm this. - filestat->st_size = entry.value.save_file_info.length; - // filestat->st_size = file->storage.block_size; dir->next_file = entry.value.next_sibling; } else { - return set_errno(r, ENOENT); + return -ENOENT; } filestat->st_nlink = 1; - strcpy(filename, entry.name); + std::strcpy(filename, entry.name); - return r->_errno = 0; + return 0; } -int devoptab_dirclose(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirclose(void* fd) { + auto dir = static_cast(fd); std::memset(dir, 0, sizeof(*dir)); - return r->_errno = 0; + return 0; } -int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) { - auto device = (Device*)r->deviceData; +int Device::devoptab_lstat(const char *path, struct stat *st) { + st->st_nlink = 1; - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - return set_errno(r, ENOENT); - } - - std::memset(st, 0, sizeof(*st)); save_fs_list_entry_t entry{}; // NOTE: this is very slow. - if (save_hierarchical_file_table_get_file_entry_by_path(device->file_table, path, &entry)) { + if (save_hierarchical_file_table_get_file_entry_by_path(this->file_table, path, &entry)) { st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; st->st_size = entry.value.save_file_info.length; - } else if (save_hierarchical_directory_table_get_file_entry_by_path(device->file_table, path, &entry)) { + } else if (save_hierarchical_directory_table_get_file_entry_by_path(this->file_table, path, &entry)) { st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; } else { - return set_errno(r, ENOENT); + return -ENOENT; } - st->st_nlink = 1; - - return r->_errno = 0; + return 0; } -constexpr devoptab_t DEVOPTAB = { - .structSize = sizeof(File), - .open_r = devoptab_open, - .close_r = devoptab_close, - .read_r = devoptab_read, - .seek_r = devoptab_seek, - .fstat_r = devoptab_fstat, - .stat_r = devoptab_lstat, - .dirStateSize = sizeof(Dir), - .diropen_r = devoptab_diropen, - .dirreset_r = devoptab_dirreset, - .dirnext_r = devoptab_dirnext, - .dirclose_r = devoptab_dirclose, - .lstat_r = devoptab_lstat, -}; - -struct Entry { - Device device{}; - devoptab_t devoptab{}; - u64 id{}; - fs::FsPath mount{}; - char name[32]{}; - s32 ref_count{}; - - ~Entry() { - RemoveDevice(mount); - save_close_savefile(&device.ctx); - } -}; - -Mutex g_mutex; -std::array, common::MAX_ENTRIES> g_entries; - } // namespace Result MountSaveSystem(u64 id, fs::FsPath& out_path) { - SCOPED_MUTEX(&g_mutex); + static Mutex mutex{}; + SCOPED_MUTEX(&mutex); - // check if we already have the save mounted. - for (auto& e : g_entries) { - if (e && e->id == id) { - e->ref_count++; - out_path = e->mount; - R_SUCCEED(); - } - } - - // otherwise, find next free entry. - auto itr = std::ranges::find_if(g_entries, [](auto& e){ - return !e; - }); - R_UNLESS(itr != g_entries.end(), 0x1); - - char path[256]; + fs::FsPath path{}; std::snprintf(path, sizeof(path), "SYSTEM:/save/%016lx", id); auto ctx = save_open_savefile(path, 0); @@ -288,46 +225,18 @@ Result MountSaveSystem(u64 id, fs::FsPath& out_path) { R_THROW(0x1); } - log_write("[SAVE] OPEN SUCCESS %s\n", path); - - auto entry = std::make_unique(); - entry->id = id; - entry->device.ctx = ctx; - entry->device.file_table = &ctx->save_filesystem_core.file_table; - entry->devoptab = DEVOPTAB; - entry->devoptab.name = entry->name; - entry->devoptab.deviceData = &entry->device; - std::snprintf(entry->name, sizeof(entry->name), "%016lx", id); - std::snprintf(entry->mount, sizeof(entry->mount), "%016lx:/", id); - - R_UNLESS(AddDevice(&entry->devoptab) >= 0, 0x1); - log_write("[SAVE] DEVICE SUCCESS %s %s\n", path, entry->name); - - out_path = entry->mount; - entry->ref_count++; - *itr = std::move(entry); + if (!common::MountReadOnlyIndexDevice( + [&ctx](const common::MountConfig& config) { + return std::make_unique(ctx, config); + }, + sizeof(File), sizeof(Dir), + "SAVE", out_path + )) { + log_write("[SAVE] Failed to mount %s\n", path.s); + R_THROW(0x1); + } R_SUCCEED(); } -void UnmountSave(u64 id) { - SCOPED_MUTEX(&g_mutex); - - auto itr = std::ranges::find_if(g_entries, [id](auto& e){ - return e && e->id == id; - }); - - if (itr == g_entries.end()) { - return; - } - - if ((*itr)->ref_count) { - (*itr)->ref_count--; - } - - if (!(*itr)->ref_count) { - itr->reset(); - } -} - } // namespace sphaira::devoptab diff --git a/sphaira/source/utils/devoptab_xci.cpp b/sphaira/source/utils/devoptab_xci.cpp index 73f0a97..a05ba00 100644 --- a/sphaira/source/utils/devoptab_xci.cpp +++ b/sphaira/source/utils/devoptab_xci.cpp @@ -12,79 +12,85 @@ #include #include #include -#include namespace sphaira::devoptab { namespace { -struct Device { - std::unique_ptr source; - yati::container::Xci::Partitions partitions; -}; - struct File { - Device* device; const yati::container::CollectionEntry* collection; size_t off; }; struct Dir { - Device* device; const yati::container::Collections* collections; u32 index; }; -int set_errno(struct _reent *r, int err) { - r->_errno = err; - return -1; -} +struct Device final : common::MountDevice { + Device(std::unique_ptr&& _source, const yati::container::Xci::Partitions& _partitions, const common::MountConfig& _config) + : MountDevice{_config} + , source{std::forward(_source)} + , partitions{_partitions} { -int devoptab_open(struct _reent *r, void *fileStruct, const char *_path, int flags, int mode) { - auto device = (Device*)r->deviceData; - auto file = static_cast(fileStruct); - std::memset(file, 0, sizeof(*file)); - - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - return set_errno(r, ENOENT); } - for (const auto& partition : device->partitions) { +private: + bool Mount() override { return true; } + 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: + std::unique_ptr source; + const yati::container::Xci::Partitions partitions; +}; + +int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mode) { + auto file = static_cast(fileStruct); + + for (const auto& partition : this->partitions) { for (const auto& collection : partition.collections) { if (path == "/" + partition.name + "/" + collection.name) { - file->device = device; file->collection = &collection; - return r->_errno = 0; + return 0; } } } - return set_errno(r, ENOENT); + log_write("[XCI] devoptab_open: failed to find path: %s\n", path); + return -ENOENT; } -int devoptab_close(struct _reent *r, void *fd) { +int Device::devoptab_close(void *fd) { auto file = static_cast(fd); std::memset(file, 0, sizeof(*file)); - return r->_errno = 0; + return 0; } -ssize_t devoptab_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); const auto& collection = file->collection; len = std::min(len, collection->size - file->off); u64 bytes_read; - if (R_FAILED(file->device->source->Read(ptr, collection->offset + file->off, len, &bytes_read))) { - return set_errno(r, ENOENT); + if (R_FAILED(this->source->Read(ptr, collection->offset + file->off, len, &bytes_read))) { + return -EIO; } file->off += bytes_read; return bytes_read; } -off_t devoptab_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& collection = file->collection; @@ -94,73 +100,58 @@ off_t devoptab_seek(struct _reent *r, void *fd, off_t pos, int dir) { pos = collection->size; } - r->_errno = 0; return file->off = std::clamp(pos, 0, collection->size); } -int devoptab_fstat(struct _reent *r, void *fd, struct stat *st) { +int Device::devoptab_fstat(void *fd, struct stat *st) { auto file = static_cast(fd); const auto& collection = file->collection; - std::memset(st, 0, sizeof(*st)); st->st_nlink = 1; st->st_size = collection->size; st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - return r->_errno = 0; + return 0; } -DIR_ITER* devoptab_diropen(struct _reent *r, DIR_ITER *dirState, const char *_path) { - auto device = (Device*)r->deviceData; - auto dir = static_cast(dirState->dirStruct); - std::memset(dir, 0, sizeof(*dir)); - - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - set_errno(r, ENOENT); - return NULL; - } +int Device::devoptab_diropen(void* fd, const char *path) { + auto dir = static_cast(fd); if (!std::strcmp(path, "/")) { - dir->device = device; - r->_errno = 0; - return dirState; + return 0; } else { - for (const auto& partition : device->partitions) { + for (const auto& partition : this->partitions) { if (path == "/" + partition.name) { dir->collections = &partition.collections; - r->_errno = 0; - return dirState; + return 0; } } } - set_errno(r, ENOENT); - return NULL; + return -ENOENT; } -int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirreset(void* fd) { + auto dir = static_cast(fd); dir->index = 0; - return r->_errno = 0; + return 0; } -int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) { - auto dir = static_cast(dirState->dirStruct); - std::memset(filestat, 0, sizeof(*filestat)); +int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) { + auto dir = static_cast(fd); if (!dir->collections) { - if (dir->index >= dir->device->partitions.size()) { - return set_errno(r, ENOENT); + if (dir->index >= this->partitions.size()) { + return -ENOENT; } filestat->st_nlink = 1; filestat->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; - std::strcpy(filename, dir->device->partitions[dir->index].name.c_str()); + std::strcpy(filename, this->partitions[dir->index].name.c_str()); } else { if (dir->index >= dir->collections->size()) { - return set_errno(r, ENOENT); + return -ENOENT; } const auto& collection = (*dir->collections)[dir->index]; @@ -171,133 +162,57 @@ int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struc } dir->index++; - return r->_errno = 0; + return 0; } -int devoptab_dirclose(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirclose(void* fd) { + auto dir = static_cast(fd); std::memset(dir, 0, sizeof(*dir)); - return r->_errno = 0; + return 0; } -int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) { - auto device = (Device*)r->deviceData; - - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - return set_errno(r, ENOENT); - } - - std::memset(st, 0, sizeof(*st)); +int Device::devoptab_lstat(const char *path, struct stat *st) { st->st_nlink = 1; if (!std::strcmp(path, "/")) { st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; } else { - for (const auto& partition : device->partitions) { + for (const auto& partition : this->partitions) { if (path == "/" + partition.name) { st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; - return r->_errno = 0; + return 0; } for (const auto& collection : partition.collections) { if (path == "/" + partition.name + "/" + collection.name) { st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; st->st_size = collection.size; - return r->_errno = 0; + return 0; } } } } - return set_errno(r, ENOENT); -} - -constexpr devoptab_t DEVOPTAB = { - .structSize = sizeof(File), - .open_r = devoptab_open, - .close_r = devoptab_close, - .read_r = devoptab_read, - .seek_r = devoptab_seek, - .fstat_r = devoptab_fstat, - .stat_r = devoptab_lstat, - .dirStateSize = sizeof(Dir), - .diropen_r = devoptab_diropen, - .dirreset_r = devoptab_dirreset, - .dirnext_r = devoptab_dirnext, - .dirclose_r = devoptab_dirclose, - .lstat_r = devoptab_lstat, -}; - -struct Entry { - Device device{}; - devoptab_t devoptab{}; - fs::FsPath path{}; - fs::FsPath mount{}; - char name[32]{}; - s32 ref_count{}; - - ~Entry() { - RemoveDevice(mount); - } -}; - -Mutex g_mutex; -std::array, common::MAX_ENTRIES> g_entries; - -bool IsAlreadyMounted(const fs::FsPath& path, fs::FsPath& out_path) { - // check if we already have the save mounted. - for (auto& e : g_entries) { - if (e && e->path == path) { - e->ref_count++; - out_path = e->mount; - return true; - } - } - - return false; + return -ENOENT; } Result MountXciInternal(const std::shared_ptr& source, s64 size, const fs::FsPath& path, fs::FsPath& out_path) { - // check if we already have the save mounted. - for (auto& e : g_entries) { - if (e && e->path == path) { - e->ref_count++; - out_path = e->mount; - R_SUCCEED(); - } - } - - // otherwise, find next free entry. - auto itr = std::ranges::find_if(g_entries, [](auto& e){ - return !e; - }); - R_UNLESS(itr != g_entries.end(), 0x1); - const auto index = std::distance(g_entries.begin(), itr); - auto buffered = std::make_unique(source, size); - yati::container::Xci xci{buffered.get()}; yati::container::Xci::Root root; R_TRY(xci.GetRoot(root)); - auto entry = std::make_unique(); - entry->path = path; - entry->devoptab = DEVOPTAB; - entry->devoptab.name = entry->name; - entry->devoptab.deviceData = &entry->device; - entry->device.source = std::move(buffered); - entry->device.partitions = root.partitions; - std::snprintf(entry->name, sizeof(entry->name), "xci_%zu", index); - std::snprintf(entry->mount, sizeof(entry->mount), "xci_%zu:/", index); - - R_UNLESS(AddDevice(&entry->devoptab) >= 0, 0x1); - log_write("[XCI] DEVICE SUCCESS %s %s\n", path.s, entry->name); - - out_path = entry->mount; - entry->ref_count++; - *itr = std::move(entry); + if (!common::MountReadOnlyIndexDevice( + [&buffered, &root](const common::MountConfig& config) { + return std::make_unique(std::move(buffered), root.partitions, config); + }, + sizeof(File), sizeof(Dir), + "XCI", out_path + )) { + log_write("[XCI] Failed to mount %s\n", path.s); + R_THROW(0x1); + } R_SUCCEED(); } @@ -305,12 +220,6 @@ Result MountXciInternal(const std::shared_ptr& source, s64 s } // namespace Result MountXci(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { - SCOPED_MUTEX(&g_mutex); - - if (IsAlreadyMounted(path, out_path)) { - R_SUCCEED(); - } - s64 size; auto source = std::make_shared(fs, path); R_TRY(source->GetSize(&size)); @@ -319,33 +228,7 @@ Result MountXci(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { } Result MountXciSource(const std::shared_ptr& source, s64 size, const fs::FsPath& path, fs::FsPath& out_path) { - SCOPED_MUTEX(&g_mutex); - - if (IsAlreadyMounted(path, out_path)) { - R_SUCCEED(); - } - return MountXciInternal(source, size, path, out_path); } -void UmountXci(const fs::FsPath& mount) { - SCOPED_MUTEX(&g_mutex); - - auto itr = std::ranges::find_if(g_entries, [&mount](auto& e){ - return e && e->mount == mount; - }); - - if (itr == g_entries.end()) { - return; - } - - if ((*itr)->ref_count) { - (*itr)->ref_count--; - } - - if (!(*itr)->ref_count) { - itr->reset(); - } -} - } // namespace sphaira::devoptab diff --git a/sphaira/source/utils/devoptab_zip.cpp b/sphaira/source/utils/devoptab_zip.cpp index adbb43e..2c6cf95 100644 --- a/sphaira/source/utils/devoptab_zip.cpp +++ b/sphaira/source/utils/devoptab_zip.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include namespace sphaira::devoptab { @@ -113,11 +112,6 @@ struct DirectoryEntry { using FileTableEntries = std::vector; -struct Device { - std::unique_ptr source; - DirectoryEntry root; -}; - struct Zfile { z_stream z; // zlib stream. Bytef* buffer; // buffer that compressed data is read into. @@ -126,7 +120,6 @@ struct Zfile { }; struct File { - Device* device; const FileEntry* entry; Zfile zfile; // only used if the file is compressed. size_t data_off; // offset of the file data. @@ -134,7 +127,6 @@ struct File { }; struct Dir { - Device* device; const DirectoryEntry* entry; u32 index; }; @@ -194,44 +186,58 @@ void set_stat_file(const FileEntry* entry, struct stat *st) { st->st_ctime = st->st_atime; } -int set_errno(struct _reent *r, int err) { - r->_errno = err; - return -1; -} +struct Device final : common::MountDevice { + Device(std::unique_ptr&& _source, const DirectoryEntry& _root, const common::MountConfig& _config) + : MountDevice{_config} + , source{std::forward(_source)} + , root{_root} { -int devoptab_open(struct _reent *r, void *fileStruct, const char *_path, int flags, int mode) { - auto device = (Device*)r->deviceData; - auto file = static_cast(fileStruct); - std::memset(file, 0, sizeof(*file)); - - char path[PATH_MAX]{}; - if (!common::fix_path(_path, path)) { - return set_errno(r, ENOENT); } - const auto entry = find_file_entry(device->root, path); +private: + bool Mount() override { return true; } + 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: + std::unique_ptr source; + const DirectoryEntry root; +}; + +int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mode) { + auto file = static_cast(fileStruct); + + const auto entry = find_file_entry(this->root, path); if (!entry) { - return set_errno(r, ENOENT); + return -ENOENT; } if ((entry->flags & mmz_Flag_Encrypted) || (entry->flags & mmz_Flag_StrongEncrypted)) { log_write("[ZIP] encrypted zip not supported\n"); - return set_errno(r, ENOENT); + return -ENOENT; } if (entry->compression_type != mmz_Compression_None && entry->compression_type != mmz_Compression_Deflate) { log_write("[ZIP] unsuported compression type: %u\n", entry->compression_type); - return set_errno(r, ENOENT); + return -ENOENT; } mmz_LocalHeader local_hdr{}; auto offset = entry->local_file_header_off; - if (R_FAILED(device->source->Read2(&local_hdr, offset, sizeof(local_hdr)))) { - return set_errno(r, ENOENT); + if (R_FAILED(this->source->Read2(&local_hdr, offset, sizeof(local_hdr)))) { + return -ENOENT; } if (local_hdr.sig != LOCAL_HEADER_SIG) { - return set_errno(r, ENOENT); + return -ENOENT; } offset += sizeof(local_hdr) + local_hdr.filename_len + local_hdr.extrafield_len; @@ -239,12 +245,12 @@ int devoptab_open(struct _reent *r, void *fileStruct, const char *_path, int fla // todo: does a decs take prio over file header? if (local_hdr.flags & mmz_Flag_DataDescriptor) { mmz_DataDescriptor data_desc{}; - if (R_FAILED(device->source->Read2(&data_desc, offset, sizeof(data_desc)))) { - return set_errno(r, ENOENT); + if (R_FAILED(this->source->Read2(&data_desc, offset, sizeof(data_desc)))) { + return -ENOENT; } if (data_desc.sig != DATA_DESCRIPTOR_SIG) { - return set_errno(r, ENOENT); + return -ENOENT; } offset += sizeof(data_desc); @@ -253,41 +259,39 @@ int devoptab_open(struct _reent *r, void *fileStruct, const char *_path, int fla if (entry->compression_type == mmz_Compression_Deflate) { auto& zfile = file->zfile; zfile.buffer_size = 1024 * 64; - zfile.buffer = (Bytef*)calloc(1, zfile.buffer_size); + zfile.buffer = (Bytef*)std::calloc(1, zfile.buffer_size); if (!zfile.buffer) { - return set_errno(r, ENOENT); + return -ENOENT; } // skip zlib header. if (Z_OK != inflateInit2(&zfile.z, -MAX_WBITS)) { - free(zfile.buffer); + std::free(zfile.buffer); zfile.buffer = nullptr; - return set_errno(r, ENOENT); + return -ENOENT; } } - file->device = device; file->entry = entry; file->data_off = offset; - return r->_errno = 0; + return 0; } -int devoptab_close(struct _reent *r, void *fd) { +int Device::devoptab_close(void *fd) { auto file = static_cast(fd); - if (file->entry->compression_type == mmz_Compression_Deflate) { + if (file->entry->compression_type == mmz_Compression_Deflate) { inflateEnd(&file->zfile.z); if (file->zfile.buffer) { - free(file->zfile.buffer); + std::free(file->zfile.buffer); } } - std::memset(file, 0, sizeof(*file)); - return r->_errno = 0; + return 0; } -ssize_t devoptab_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); len = std::min(len, file->entry->uncompressed_size - file->off); @@ -296,8 +300,8 @@ ssize_t devoptab_read(struct _reent *r, void *fd, char *ptr, size_t len) { } if (file->entry->compression_type == mmz_Compression_None) { - if (R_FAILED(file->device->source->Read2(ptr, file->data_off + file->off, len))) { - return set_errno(r, ENOENT); + if (R_FAILED(this->source->Read2(ptr, file->data_off + file->off, len))) { + return -ENOENT; } } else if (file->entry->compression_type == mmz_Compression_Deflate) { auto& zfile = file->zfile; @@ -309,8 +313,8 @@ ssize_t devoptab_read(struct _reent *r, void *fd, char *ptr, size_t len) { // check if we need to fetch more data. if (!zfile.z.next_in || !zfile.z.avail_in) { const auto clen = std::min(zfile.buffer_size, file->entry->compressed_size - zfile.compressed_off); - if (R_FAILED(file->device->source->Read2(zfile.buffer, file->data_off + zfile.compressed_off, clen))) { - return set_errno(r, ENOENT); + if (R_FAILED(this->source->Read2(zfile.buffer, file->data_off + zfile.compressed_off, clen))) { + return -ENOENT; } zfile.compressed_off += clen; @@ -324,7 +328,7 @@ ssize_t devoptab_read(struct _reent *r, void *fd, char *ptr, size_t len) { len -= zfile.z.avail_out; } else { log_write("[ZLIB] failed to inflate: %d %s\n", rc, zfile.z.msg); - return set_errno(r, ENOENT); + return -ENOENT; } } } @@ -334,7 +338,7 @@ ssize_t devoptab_read(struct _reent *r, void *fd, char *ptr, size_t len) { return len; } -off_t devoptab_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); // seek like normal. @@ -365,58 +369,44 @@ off_t devoptab_seek(struct _reent *r, void *fd, off_t pos, int dir) { } } - r->_errno = 0; return file->off = std::clamp(pos, 0, file->entry->uncompressed_size); } -int devoptab_fstat(struct _reent *r, void *fd, struct stat *st) { +int Device::devoptab_fstat(void *fd, struct stat *st) { auto file = static_cast(fd); - std::memset(st, 0, sizeof(*st)); set_stat_file(file->entry, st); - return r->_errno = 0; + return 0; } -DIR_ITER* devoptab_diropen(struct _reent *r, DIR_ITER *dirState, const char *_path) { - auto device = (Device*)r->deviceData; - 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); - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - set_errno(r, ENOENT); - return NULL; - } - - const auto entry = find_dir_entry(device->root, path); + const auto entry = find_dir_entry(this->root, path); if (!entry) { - set_errno(r, ENOENT); - return NULL; + return -ENOENT; } - dir->device = device; dir->entry = entry; - r->_errno = 0; - return dirState; + return 0; } -int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirreset(void* fd) { + auto dir = static_cast(fd); dir->index = 0; - return r->_errno = 0; + return 0; } -int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) { - auto dir = static_cast(dirState->dirStruct); - std::memset(filestat, 0, sizeof(*filestat)); +int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) { + auto dir = static_cast(fd); u32 index = dir->index; if (index >= dir->entry->dir_child.size()) { index -= dir->entry->dir_child.size(); if (index >= dir->entry->file_child.size()) { - return set_errno(r, ENOENT); + return -ENOENT; } else { const auto& entry = dir->entry->file_child[index]; const auto rel_path = entry.path.substr(entry.path.find_last_of('/') + 1); @@ -434,59 +424,31 @@ int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struc } dir->index++; - return r->_errno = 0; + return 0; } -int devoptab_dirclose(struct _reent *r, DIR_ITER *dirState) { - auto dir = static_cast(dirState->dirStruct); +int Device::devoptab_dirclose(void* fd) { + auto dir = static_cast(fd); std::memset(dir, 0, sizeof(*dir)); - return r->_errno = 0; + return 0; } -int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) { - auto device = (Device*)r->deviceData; - - if (!device) { - return set_errno(r, ENOENT); - } - - char path[PATH_MAX]; - if (!common::fix_path(_path, path)) { - return set_errno(r, ENOENT); - } - - std::memset(st, 0, sizeof(*st)); +int Device::devoptab_lstat(const char *path, struct stat *st) { st->st_nlink = 1; - if (find_dir_entry(device->root, path)) { + if (find_dir_entry(this->root, path)) { st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; - } else if (auto entry = find_file_entry(device->root, path)) { + } else if (auto entry = find_file_entry(this->root, path)) { set_stat_file(entry, st); } else { log_write("[ZIP] didn't find in lstat\n"); - return set_errno(r, ENOENT); + return -ENOENT; } - return r->_errno = 0; + return 0; } -constexpr devoptab_t DEVOPTAB = { - .structSize = sizeof(File), - .open_r = devoptab_open, - .close_r = devoptab_close, - .read_r = devoptab_read, - .seek_r = devoptab_seek, - .fstat_r = devoptab_fstat, - .stat_r = devoptab_lstat, - .dirStateSize = sizeof(Dir), - .diropen_r = devoptab_diropen, - .dirreset_r = devoptab_dirreset, - .dirnext_r = devoptab_dirnext, - .dirclose_r = devoptab_dirclose, - .lstat_r = devoptab_lstat, -}; - auto BuildPath(const std::string& path) -> std::string { if (path.starts_with('/')) { return path; @@ -600,48 +562,13 @@ Result ParseZip(common::LruBufferedData* source, s64 size, FileTableEntries& out R_SUCCEED(); } -struct Entry { - Device device{}; - devoptab_t devoptab{}; - fs::FsPath path{}; - fs::FsPath mount{}; - char name[32]{}; - s32 ref_count{}; - - ~Entry() { - RemoveDevice(mount); - } -}; - -Mutex g_mutex; -std::array, common::MAX_ENTRIES> g_entries; - } // namespace Result MountZip(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { - SCOPED_MUTEX(&g_mutex); - - // check if we already have the save mounted. - for (auto& e : g_entries) { - if (e && e->path == path) { - e->ref_count++; - out_path = e->mount; - R_SUCCEED(); - } - } - - // otherwise, find next free entry. - auto itr = std::ranges::find_if(g_entries, [](auto& e){ - return !e; - }); - R_UNLESS(itr != g_entries.end(), 0x1); - - const auto index = std::distance(g_entries.begin(), itr); auto source = std::make_shared(fs, path); s64 size; R_TRY(source->GetSize(&size)); - auto buffered = std::make_unique(source, size); FileTableEntries table_entries; @@ -651,44 +578,18 @@ Result MountZip(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { DirectoryEntry root; Parse(table_entries, root); - auto entry = std::make_unique(); - entry->path = path; - entry->devoptab = DEVOPTAB; - entry->devoptab.name = entry->name; - entry->devoptab.deviceData = &entry->device; - entry->device.source = std::move(buffered); - entry->device.root = root; - std::snprintf(entry->name, sizeof(entry->name), "zip_%zu", index); - std::snprintf(entry->mount, sizeof(entry->mount), "zip_%zu:/", index); - - R_UNLESS(AddDevice(&entry->devoptab) >= 0, 0x1); - log_write("[ZIP] DEVICE SUCCESS %s %s\n", path.s, entry->name); - - out_path = entry->mount; - entry->ref_count++; - *itr = std::move(entry); + if (!common::MountReadOnlyIndexDevice( + [&buffered, &root](const common::MountConfig& config) { + return std::make_unique(std::move(buffered), root, config); + }, + sizeof(File), sizeof(Dir), + "ZIP", out_path + )) { + log_write("[ZIP] Failed to mount %s\n", path.s); + R_THROW(0x1); + } R_SUCCEED(); } -void UmountZip(const fs::FsPath& mount) { - SCOPED_MUTEX(&g_mutex); - - auto itr = std::ranges::find_if(g_entries, [&mount](auto& e){ - return e && e->mount == mount; - }); - - if (itr == g_entries.end()) { - return; - } - - if ((*itr)->ref_count) { - (*itr)->ref_count--; - } - - if (!(*itr)->ref_count) { - itr->reset(); - } -} - } // namespace sphaira::devoptab