devoptab: refactor all custom mounts to inherit from helper struct.
This commit is contained in:
@@ -9,29 +9,15 @@
|
|||||||
|
|
||||||
namespace sphaira::devoptab {
|
namespace sphaira::devoptab {
|
||||||
|
|
||||||
// mounts to "lower_case_hex_id:/"
|
|
||||||
Result MountSaveSystem(u64 id, fs::FsPath& out_path);
|
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);
|
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);
|
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 MountXci(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path);
|
||||||
Result MountXciSource(const std::shared_ptr<sphaira::yati::source::Base>& source, s64 size, const fs::FsPath& path, fs::FsPath& out_path);
|
Result MountXciSource(const std::shared_ptr<sphaira::yati::source::Base>& 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 MountNca(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path);
|
||||||
Result MountNcaNcm(NcmContentStorage* cs, const NcmContentId* id, 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);
|
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);
|
Result MountNro(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path);
|
||||||
void UmountNro(const fs::FsPath& mount);
|
|
||||||
|
|
||||||
Result MountVfsAll();
|
Result MountVfsAll();
|
||||||
Result MountWebdavAll();
|
Result MountWebdavAll();
|
||||||
@@ -43,5 +29,6 @@ Result MountFatfsAll();
|
|||||||
|
|
||||||
Result GetNetworkDevices(location::StdioEntries& out);
|
Result GetNetworkDevices(location::StdioEntries& out);
|
||||||
void UmountAllNeworkDevices();
|
void UmountAllNeworkDevices();
|
||||||
|
void UmountNeworkDevice(const fs::FsPath& mount);
|
||||||
|
|
||||||
} // namespace sphaira::devoptab
|
} // namespace sphaira::devoptab
|
||||||
|
|||||||
@@ -185,7 +185,6 @@ struct MountDevice {
|
|||||||
|
|
||||||
struct MountCurlDevice : MountDevice {
|
struct MountCurlDevice : MountDevice {
|
||||||
using MountDevice::MountDevice;
|
using MountDevice::MountDevice;
|
||||||
// MountCurlDevice(const MountConfig& _config);
|
|
||||||
virtual ~MountCurlDevice();
|
virtual ~MountCurlDevice();
|
||||||
|
|
||||||
PushThreadData* CreatePushData(CURL* curl, const std::string& url, size_t offset);
|
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.
|
// same as above but takes in the device and expects the mount name to be set.
|
||||||
bool MountNetworkDevice2(std::unique_ptr<MountDevice>&& device, const MountConfig& config, size_t file_size, size_t dir_size, const char* name, const char* mount_name);
|
bool MountNetworkDevice2(std::unique_ptr<MountDevice>&& 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
|
} // namespace sphaira::devoptab::common
|
||||||
|
|||||||
@@ -711,15 +711,15 @@ void FsView::OnClick() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (IsExtension(entry.GetExtension(), NCA_EXTENSIONS)) {
|
} else if (IsExtension(entry.GetExtension(), NCA_EXTENSIONS)) {
|
||||||
MountFileFs(devoptab::MountNca, devoptab::UmountNca);
|
MountFileFs(devoptab::MountNca, devoptab::UmountNeworkDevice);
|
||||||
} else if (IsExtension(entry.GetExtension(), NSP_EXTENSIONS)) {
|
} else if (IsExtension(entry.GetExtension(), NSP_EXTENSIONS)) {
|
||||||
MountFileFs(devoptab::MountNsp, devoptab::UmountNsp);
|
MountFileFs(devoptab::MountNsp, devoptab::UmountNeworkDevice);
|
||||||
} else if (IsExtension(entry.GetExtension(), XCI_EXTENSIONS)) {
|
} else if (IsExtension(entry.GetExtension(), XCI_EXTENSIONS)) {
|
||||||
MountFileFs(devoptab::MountXci, devoptab::UmountXci);
|
MountFileFs(devoptab::MountXci, devoptab::UmountNeworkDevice);
|
||||||
} else if (IsExtension(entry.GetExtension(), "zip")) {
|
} else if (IsExtension(entry.GetExtension(), "zip")) {
|
||||||
MountFileFs(devoptab::MountZip, devoptab::UmountZip);
|
MountFileFs(devoptab::MountZip, devoptab::UmountNeworkDevice);
|
||||||
} else if (IsExtension(entry.GetExtension(), "bfsar")) {
|
} else if (IsExtension(entry.GetExtension(), "bfsar")) {
|
||||||
MountFileFs(devoptab::MountBfsar, devoptab::UmountBfsar);
|
MountFileFs(devoptab::MountBfsar, devoptab::UmountNeworkDevice);
|
||||||
} else if (IsExtension(entry.GetExtension(), MUSIC_EXTENSIONS)) {
|
} else if (IsExtension(entry.GetExtension(), MUSIC_EXTENSIONS)) {
|
||||||
App::Push<music::Menu>(GetFs(), GetNewPathCurrent());
|
App::Push<music::Menu>(GetFs(), GetNewPathCurrent());
|
||||||
} else if (IsExtension(entry.GetExtension(), IMAGE_EXTENSIONS)) {
|
} else if (IsExtension(entry.GetExtension(), IMAGE_EXTENSIONS)) {
|
||||||
|
|||||||
@@ -413,7 +413,7 @@ Result Menu::MountNcaFs() {
|
|||||||
R_TRY(devoptab::MountNcaNcm(m_meta.cs, &e.content_id, root));
|
R_TRY(devoptab::MountNcaNcm(m_meta.cs, &e.content_id, root));
|
||||||
|
|
||||||
auto fs = std::make_shared<filebrowser::FsStdioWrapper>(root, [root](){
|
auto fs = std::make_shared<filebrowser::FsStdioWrapper>(root, [root](){
|
||||||
devoptab::UmountNca(root);
|
devoptab::UmountNeworkDevice(root);
|
||||||
});
|
});
|
||||||
|
|
||||||
filebrowser::MountFsHelper(fs, utils::hexIdToStr(e.content_id).str);
|
filebrowser::MountFsHelper(fs, utils::hexIdToStr(e.content_id).str);
|
||||||
|
|||||||
@@ -1322,7 +1322,7 @@ Result Menu::MountGcFs() {
|
|||||||
R_TRY(devoptab::MountXciSource(source, m_storage_trimmed_size, e.lang_entry.name, root));
|
R_TRY(devoptab::MountXciSource(source, m_storage_trimmed_size, e.lang_entry.name, root));
|
||||||
|
|
||||||
auto fs = std::make_shared<filebrowser::FsStdioWrapper>(root, [root](){
|
auto fs = std::make_shared<filebrowser::FsStdioWrapper>(root, [root](){
|
||||||
devoptab::UmountXci(root);
|
devoptab::UmountNeworkDevice(root);
|
||||||
});
|
});
|
||||||
|
|
||||||
filebrowser::MountFsHelper(fs, e.lang_entry.name);
|
filebrowser::MountFsHelper(fs, e.lang_entry.name);
|
||||||
|
|||||||
@@ -527,7 +527,7 @@ Result Menu::MountNroFs() {
|
|||||||
R_TRY(devoptab::MountNro(App::GetApp()->m_fs.get(), e.path, root));
|
R_TRY(devoptab::MountNro(App::GetApp()->m_fs.get(), e.path, root));
|
||||||
|
|
||||||
auto fs = std::make_shared<filebrowser::FsStdioWrapper>(root, [root](){
|
auto fs = std::make_shared<filebrowser::FsStdioWrapper>(root, [root](){
|
||||||
devoptab::UmountNro(root);
|
devoptab::UmountNeworkDevice(root);
|
||||||
});
|
});
|
||||||
|
|
||||||
filebrowser::MountFsHelper(fs, root);
|
filebrowser::MountFsHelper(fs, root);
|
||||||
|
|||||||
@@ -1131,8 +1131,8 @@ Result Menu::MountSaveFs() {
|
|||||||
fs::FsPath root;
|
fs::FsPath root;
|
||||||
R_TRY(devoptab::MountSaveSystem(e.system_save_data_id, root));
|
R_TRY(devoptab::MountSaveSystem(e.system_save_data_id, root));
|
||||||
|
|
||||||
auto fs = std::make_shared<filebrowser::FsStdioWrapper>(root, [&e](){
|
auto fs = std::make_shared<filebrowser::FsStdioWrapper>(root, [root](){
|
||||||
devoptab::UnmountSave(e.system_save_data_id);
|
devoptab::UmountNeworkDevice(root);
|
||||||
});
|
});
|
||||||
|
|
||||||
filebrowser::MountFsHelper(fs, e.GetName());
|
filebrowser::MountFsHelper(fs, e.GetName());
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
#include "defines.hpp"
|
#include "defines.hpp"
|
||||||
#include "log.hpp"
|
#include "log.hpp"
|
||||||
|
|
||||||
#include "yati/container/nsp.hpp"
|
|
||||||
#include "yati/container/xci.hpp"
|
|
||||||
#include "yati/source/file.hpp"
|
#include "yati/source/file.hpp"
|
||||||
|
|
||||||
#include <pulsar.h>
|
#include <pulsar.h>
|
||||||
@@ -15,24 +13,16 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sys/iosupport.h>
|
|
||||||
|
|
||||||
namespace sphaira::devoptab {
|
namespace sphaira::devoptab {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct Device {
|
|
||||||
PLSR_BFSAR bfsar;
|
|
||||||
std::FILE* file; // points to archive file.
|
|
||||||
};
|
|
||||||
|
|
||||||
struct File {
|
struct File {
|
||||||
Device* device;
|
|
||||||
PLSR_BFWARFileInfo info;
|
PLSR_BFWARFileInfo info;
|
||||||
size_t off;
|
size_t off;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Dir {
|
struct Dir {
|
||||||
Device* device;
|
|
||||||
u32 index;
|
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) {
|
struct Device final : common::MountDevice {
|
||||||
r->_errno = err;
|
Device(const PLSR_BFSAR& _bfsar, const common::MountConfig& _config)
|
||||||
return -1;
|
: MountDevice{_config}
|
||||||
}
|
, bfsar{_bfsar} {
|
||||||
|
this->file = this->bfsar.ar.handle->f;
|
||||||
int devoptab_open(struct _reent *r, void *fileStruct, const char *_path, int flags, int mode) {
|
|
||||||
auto device = (Device*)r->deviceData;
|
|
||||||
auto file = static_cast<File*>(fileStruct);
|
|
||||||
std::memset(file, 0, sizeof(*file));
|
|
||||||
|
|
||||||
char path[PATH_MAX];
|
|
||||||
if (!common::fix_path(_path, path)) {
|
|
||||||
return set_errno(r, ENOENT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~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<File*>(fileStruct);
|
||||||
|
|
||||||
PLSR_BFWARFileInfo info;
|
PLSR_BFWARFileInfo info;
|
||||||
if (R_FAILED(GetFileInfo(&device->bfsar, path, info))) {
|
if (R_FAILED(GetFileInfo(&this->bfsar, path, info))) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
file->device = device;
|
|
||||||
file->info = info;
|
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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
std::memset(file, 0, sizeof(*file));
|
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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
const auto& info = file->info;
|
const auto& info = file->info;
|
||||||
|
|
||||||
// const auto real_len = len;
|
|
||||||
// plsr seems to read oob, so allow for some tollerance.
|
// plsr seems to read oob, so allow for some tollerance.
|
||||||
const auto oob_allowed = 64;
|
const auto oob_allowed = 64;
|
||||||
len = std::min(len, info.size + oob_allowed - file->off);
|
len = std::min(len, info.size + oob_allowed - file->off);
|
||||||
std::fseek(file->device->file, file->info.offset + file->off, SEEK_SET);
|
std::fseek(this->file, file->info.offset + file->off, SEEK_SET);
|
||||||
const auto bytes_read = std::fread(ptr, 1, len, file->device->file);
|
const auto bytes_read = std::fread(ptr, 1, len, this->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);
|
|
||||||
|
|
||||||
file->off += bytes_read;
|
file->off += bytes_read;
|
||||||
return 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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
const auto& info = file->info;
|
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;
|
pos = info.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
r->_errno = 0;
|
|
||||||
return file->off = std::clamp<u64>(pos, 0, info.size);
|
return file->off = std::clamp<u64>(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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
const auto& info = file->info;
|
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_nlink = 1;
|
||||||
st->st_size = info.size;
|
st->st_size = info.size;
|
||||||
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
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) {
|
int Device::devoptab_diropen(void* fd, const char *path) {
|
||||||
auto device = (Device*)r->deviceData;
|
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
|
||||||
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, "/")) {
|
if (!std::strcmp(path, "/")) {
|
||||||
dir->device = device;
|
return 0;
|
||||||
} else {
|
|
||||||
set_errno(r, ENOENT);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r->_errno = 0;
|
return -ENOENT;
|
||||||
return dirState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) {
|
int Device::devoptab_dirreset(void* fd) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
|
|
||||||
dir->index = 0;
|
dir->index = 0;
|
||||||
|
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) {
|
int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
std::memset(filestat, 0, sizeof(*filestat));
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (dir->index >= plsrBFSARSoundCount(&dir->device->bfsar)) {
|
if (dir->index >= plsrBFSARSoundCount(&this->bfsar)) {
|
||||||
log_write("finished getting call entries: %u vs %u\n", dir->index, plsrBFSARSoundCount(&dir->device->bfsar));
|
log_write("finished getting call entries: %u vs %u\n", dir->index, plsrBFSARSoundCount(&this->bfsar));
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
PLSR_BFSARSoundInfo info{};
|
PLSR_BFSARSoundInfo info{};
|
||||||
if (R_FAILED(plsrBFSARSoundGet(&dir->device->bfsar, dir->index, &info))) {
|
if (R_FAILED(plsrBFSARSoundGet(&this->bfsar, dir->index, &info))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +193,7 @@ int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struc
|
|||||||
continue;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,139 +217,52 @@ int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struc
|
|||||||
break;
|
break;
|
||||||
} while (dir->index++);
|
} while (dir->index++);
|
||||||
|
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirclose(struct _reent *r, DIR_ITER *dirState) {
|
int Device::devoptab_dirclose(void* fd) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
std::memset(dir, 0, sizeof(*dir));
|
std::memset(dir, 0, sizeof(*dir));
|
||||||
|
|
||||||
log_write("[BFSAR] devoptab_dirclose\n");
|
return 0;
|
||||||
return r->_errno = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) {
|
int Device::devoptab_lstat(const char *path, struct stat *st) {
|
||||||
auto device = (Device*)r->deviceData;
|
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));
|
|
||||||
|
|
||||||
if (!std::strcmp(path, "/")) {
|
if (!std::strcmp(path, "/")) {
|
||||||
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
} else {
|
} else {
|
||||||
PLSR_BFWARFileInfo info{};
|
PLSR_BFWARFileInfo info{};
|
||||||
if (R_FAILED(GetFileInfo(&device->bfsar, path, info))) {
|
if (R_FAILED(GetFileInfo(&this->bfsar, path, info))) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
st->st_size = info.size;
|
st->st_size = info.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
st->st_nlink = 1;
|
return 0;
|
||||||
|
|
||||||
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() {
|
|
||||||
log_write("[BFSAR] entry called\n");
|
|
||||||
RemoveDevice(mount);
|
|
||||||
plsrBFSARClose(&device.bfsar);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Mutex g_mutex;
|
|
||||||
std::array<std::unique_ptr<Entry>, common::MAX_ENTRIES> g_entries;
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Result MountBfsar(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) {
|
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.
|
if (!common::MountReadOnlyIndexDevice(
|
||||||
for (auto& e : g_entries) {
|
[&bfsar](const common::MountConfig& config) {
|
||||||
if (e && e->path == path) {
|
return std::make_unique<Device>(bfsar, config);
|
||||||
e->ref_count++;
|
},
|
||||||
out_path = e->mount;
|
sizeof(File), sizeof(Dir),
|
||||||
R_SUCCEED();
|
"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>();
|
|
||||||
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();
|
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
|
} // namespace sphaira::devoptab
|
||||||
|
|||||||
@@ -820,6 +820,39 @@ bool MountNetworkDevice2(std::unique_ptr<MountDevice>&& device, const MountConfi
|
|||||||
return true;
|
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) {
|
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{};
|
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
|
} // sphaira::devoptab
|
||||||
|
|||||||
@@ -428,7 +428,6 @@ Result MountFatfsAll() {
|
|||||||
const auto& bis = BIS_MOUNT_ENTRIES[i];
|
const auto& bis = BIS_MOUNT_ENTRIES[i];
|
||||||
|
|
||||||
common::MountConfig config{};
|
common::MountConfig config{};
|
||||||
config.url = "fatfs dummy url";
|
|
||||||
config.read_only = true;
|
config.read_only = true;
|
||||||
config.dump_hidden = true;
|
config.dump_hidden = true;
|
||||||
|
|
||||||
@@ -457,10 +456,7 @@ const char* VolumeStr[] {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Result fatfs_read(u8 num, void* dst, u64 offset, u64 size) {
|
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];
|
auto& fat = sphaira::devoptab::g_fat_storage[num];
|
||||||
log_write("[FAT] buffered: %p\n", fat.buffered.get());
|
|
||||||
return fat.buffered->Read2(dst, offset, size);
|
return fat.buffered->Read2(dst, offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sys/iosupport.h>
|
|
||||||
|
|
||||||
namespace sphaira::devoptab {
|
namespace sphaira::devoptab {
|
||||||
namespace {
|
namespace {
|
||||||
@@ -74,25 +73,18 @@ struct DirEntry {
|
|||||||
const yati::container::Collections* pfs0;
|
const yati::container::Collections* pfs0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Device {
|
|
||||||
std::vector<NamedCollection> collections;
|
|
||||||
std::unique_ptr<yati::source::Base> source;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct File {
|
struct File {
|
||||||
Device* device;
|
|
||||||
FileEntry entry;
|
FileEntry entry;
|
||||||
size_t off;
|
size_t off;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Dir {
|
struct Dir {
|
||||||
Device* device;
|
|
||||||
DirEntry entry;
|
DirEntry entry;
|
||||||
u32 index;
|
u32 index;
|
||||||
bool is_root;
|
bool is_root;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool find_file(std::span<NamedCollection> named, std::string_view path, FileEntry& out) {
|
bool find_file(std::span<const NamedCollection> named, std::string_view path, FileEntry& out) {
|
||||||
for (auto& e : named) {
|
for (auto& e : named) {
|
||||||
if (path.starts_with("/" + e.name)) {
|
if (path.starts_with("/" + e.name)) {
|
||||||
out.fs_type = e.fs_type;
|
out.fs_type = e.fs_type;
|
||||||
@@ -154,55 +146,68 @@ bool find_dir(std::span<const NamedCollection> named, std::string_view path, Dir
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int set_errno(struct _reent *r, int err) {
|
struct Device final : common::MountDevice {
|
||||||
r->_errno = err;
|
Device(std::unique_ptr<yati::source::Base>&& _source, const std::vector<NamedCollection>& _collections, const common::MountConfig& _config)
|
||||||
return -1;
|
: MountDevice{_config}
|
||||||
}
|
, source{std::forward<decltype(_source)>(_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<yati::source::Base> source;
|
||||||
|
const std::vector<NamedCollection> collections;
|
||||||
|
};
|
||||||
|
|
||||||
|
int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mode) {
|
||||||
auto file = static_cast<File*>(fileStruct);
|
auto file = static_cast<File*>(fileStruct);
|
||||||
std::memset(file, 0, sizeof(*file));
|
std::memset(file, 0, sizeof(*file));
|
||||||
|
|
||||||
char path[PATH_MAX];
|
FileEntry entry{};
|
||||||
if (!common::fix_path(_path, path)) {
|
if (!find_file(this->collections, path, entry)) {
|
||||||
return set_errno(r, ENOENT);
|
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;
|
file->entry = entry;
|
||||||
|
return 0;
|
||||||
return r->_errno = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_close(struct _reent *r, void *fd) {
|
int Device::devoptab_close(void *fd) {
|
||||||
auto file = static_cast<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
std::memset(file, 0, sizeof(*file));
|
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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
const auto& entry = file->entry;
|
const auto& entry = file->entry;
|
||||||
|
|
||||||
u64 bytes_read;
|
u64 bytes_read;
|
||||||
len = std::min(len, entry.size - file->off);
|
len = std::min(len, entry.size - file->off);
|
||||||
if (R_FAILED(file->device->source->Read(ptr, entry.offset + file->off, len, &bytes_read))) {
|
if (R_FAILED(this->source->Read(ptr, entry.offset + file->off, len, &bytes_read))) {
|
||||||
return set_errno(r, ENOENT);
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
file->off += bytes_read;
|
file->off += bytes_read;
|
||||||
return 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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
const auto& entry = file->entry;
|
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;
|
pos = entry.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
r->_errno = 0;
|
|
||||||
return file->off = std::clamp<u64>(pos, 0, entry.size);
|
return file->off = std::clamp<u64>(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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
const auto& entry = file->entry;
|
const auto& entry = file->entry;
|
||||||
|
|
||||||
std::memset(st, 0, sizeof(*st));
|
|
||||||
st->st_nlink = 1;
|
st->st_nlink = 1;
|
||||||
st->st_size = entry.size;
|
st->st_size = entry.size;
|
||||||
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
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) {
|
int Device::devoptab_diropen(void* fd, const char *path) {
|
||||||
auto device = (Device*)r->deviceData;
|
auto dir = static_cast<Dir*>(fd);
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
|
||||||
std::memset(dir, 0, sizeof(*dir));
|
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, "/")) {
|
if (!std::strcmp(path, "/")) {
|
||||||
dir->device = device;
|
|
||||||
dir->is_root = true;
|
dir->is_root = true;
|
||||||
r->_errno = 0;
|
return 0;
|
||||||
return dirState;
|
|
||||||
} else {
|
} else {
|
||||||
DirEntry entry;
|
DirEntry entry{};
|
||||||
if (!find_dir(device->collections, path, entry)) {
|
if (!find_dir(this->collections, path, entry)) {
|
||||||
set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dir->device = device;
|
|
||||||
dir->entry = entry;
|
dir->entry = entry;
|
||||||
|
return 0;
|
||||||
r->_errno = 0;
|
|
||||||
return dirState;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) {
|
int Device::devoptab_dirreset(void* fd) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
auto& entry = dir->entry;
|
auto& entry = dir->entry;
|
||||||
|
|
||||||
if (dir->is_root) {
|
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) {
|
int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
auto& entry = dir->entry;
|
auto& entry = dir->entry;
|
||||||
std::memset(filestat, 0, sizeof(*filestat));
|
|
||||||
|
|
||||||
if (dir->is_root) {
|
if (dir->is_root) {
|
||||||
if (dir->index >= dir->device->collections.size()) {
|
if (dir->index >= this->collections.size()) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
filestat->st_nlink = 1;
|
filestat->st_nlink = 1;
|
||||||
filestat->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
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 {
|
} else {
|
||||||
if (entry.fs_type == nca::FileSystemType_RomFS) {
|
if (entry.fs_type == nca::FileSystemType_RomFS) {
|
||||||
if (!romfs::dirnext(entry.romfs, filename, filestat)) {
|
if (!romfs::dirnext(entry.romfs, filename, filestat)) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (dir->index >= entry.pfs0->size()) {
|
if (dir->index >= entry.pfs0->size()) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& collection = (*entry.pfs0)[dir->index];
|
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++;
|
dir->index++;
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirclose(struct _reent *r, DIR_ITER *dirState) {
|
int Device::devoptab_dirclose(void* fd) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
std::memset(dir, 0, sizeof(*dir));
|
std::memset(dir, 0, sizeof(*dir));
|
||||||
|
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) {
|
int Device::devoptab_lstat(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));
|
|
||||||
st->st_nlink = 1;
|
st->st_nlink = 1;
|
||||||
|
|
||||||
if (!std::strcmp(path, "/")) {
|
if (!std::strcmp(path, "/")) {
|
||||||
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
} else {
|
} else {
|
||||||
// can be optimised for romfs.
|
// can be optimised for romfs.
|
||||||
FileEntry file_entry;
|
FileEntry file_entry{};
|
||||||
DirEntry dir_entry;
|
DirEntry dir_entry{};
|
||||||
if (find_file(device->collections, path, file_entry)) {
|
if (find_file(this->collections, path, file_entry)) {
|
||||||
st->st_size = file_entry.size;
|
st->st_size = file_entry.size;
|
||||||
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
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;
|
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
} else {
|
} else {
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
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<std::unique_ptr<Entry>, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result MountNcaInternal(fs::Fs* fs, const std::shared_ptr<yati::source::Base>& source, s64 size, const fs::FsPath& path, fs::FsPath& out_path) {
|
Result MountNcaInternal(fs::Fs* fs, const std::shared_ptr<yati::source::Base>& 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
|
// todo: rather than manually fetching tickets, use spl to
|
||||||
// decrypt the nca for use (somehow, look how ams does it?).
|
// decrypt the nca for use (somehow, look how ams does it?).
|
||||||
keys::Keys keys;
|
keys::Keys keys;
|
||||||
R_TRY(keys::parse_keys(keys, true));
|
R_TRY(keys::parse_keys(keys, true));
|
||||||
|
|
||||||
nca::Header header;
|
nca::Header header{};
|
||||||
R_TRY(source->Read2(&header, 0, sizeof(header)));
|
R_TRY(source->Read2(&header, 0, sizeof(header)));
|
||||||
R_TRY(nca::DecryptHeader(&header, keys, header));
|
R_TRY(nca::DecryptHeader(&header, keys, header));
|
||||||
|
|
||||||
std::unique_ptr<yati::source::Base> nca_reader;
|
std::unique_ptr<yati::source::Base> nca_reader{};
|
||||||
log_write("[NCA] got header, type: %s\n", nca::GetContentTypeStr(header.content_type));
|
log_write("[NCA] got header, type: %s\n", nca::GetContentTypeStr(header.content_type));
|
||||||
|
|
||||||
// check if this is a ncz.
|
// check if this is a ncz.
|
||||||
@@ -450,7 +379,7 @@ Result MountNcaInternal(fs::Fs* fs, const std::shared_ptr<yati::source::Base>& s
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<NamedCollection> collections;
|
std::vector<NamedCollection> collections{};
|
||||||
const auto& content_type_fs = CONTENT_TYPE_FS_NAMES[header.content_type];
|
const auto& content_type_fs = CONTENT_TYPE_FS_NAMES[header.content_type];
|
||||||
|
|
||||||
for (u32 i = 0; i < NCA_SECTION_TOTAL; i++) {
|
for (u32 i = 0; i < NCA_SECTION_TOTAL; i++) {
|
||||||
@@ -489,7 +418,7 @@ Result MountNcaInternal(fs::Fs* fs, const std::shared_ptr<yati::source::Base>& s
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
NamedCollection collection;
|
NamedCollection collection{};
|
||||||
collection.name = content_type_fs[i].name;
|
collection.name = content_type_fs[i].name;
|
||||||
collection.fs_type = fs_header.fs_type;
|
collection.fs_type = fs_header.fs_type;
|
||||||
|
|
||||||
@@ -535,22 +464,16 @@ Result MountNcaInternal(fs::Fs* fs, const std::shared_ptr<yati::source::Base>& s
|
|||||||
|
|
||||||
R_UNLESS(!collections.empty(), 0x9);
|
R_UNLESS(!collections.empty(), 0x9);
|
||||||
|
|
||||||
auto entry = std::make_unique<Entry>();
|
if (!common::MountReadOnlyIndexDevice(
|
||||||
entry->path = path;
|
[&nca_reader, &collections](const common::MountConfig& config) {
|
||||||
entry->devoptab = DEVOPTAB;
|
return std::make_unique<Device>(std::move(nca_reader), collections, config);
|
||||||
entry->devoptab.name = entry->name;
|
},
|
||||||
entry->devoptab.deviceData = &entry->device;
|
sizeof(File), sizeof(Dir),
|
||||||
entry->device.source = std::move(nca_reader);
|
"NCA", out_path
|
||||||
entry->device.collections = std::move(collections);
|
)) {
|
||||||
std::snprintf(entry->name, sizeof(entry->name), "nca_%zu", index);
|
log_write("[NCA] Failed to mount %s\n", path.s);
|
||||||
std::snprintf(entry->mount, sizeof(entry->mount), "nca_%zu:/", index);
|
R_THROW(0x1);
|
||||||
|
}
|
||||||
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);
|
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
@@ -558,12 +481,6 @@ Result MountNcaInternal(fs::Fs* fs, const std::shared_ptr<yati::source::Base>& s
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Result MountNca(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) {
|
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;
|
s64 size;
|
||||||
auto source = std::make_shared<yati::source::File>(fs, path);
|
auto source = std::make_shared<yati::source::File>(fs, path);
|
||||||
R_TRY(source->GetSize(&size));
|
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) {
|
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;
|
s64 size;
|
||||||
auto source = std::make_shared<ncm::NcmSource>(cs, id);
|
auto source = std::make_shared<ncm::NcmSource>(cs, id);
|
||||||
R_TRY(source->GetSize(&size));
|
R_TRY(source->GetSize(&size));
|
||||||
|
|
||||||
return MountNcaInternal(nullptr, source, size, path, out_path);
|
return MountNcaInternal(nullptr, source, size, {}, 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace sphaira::devoptab
|
} // namespace sphaira::devoptab
|
||||||
|
|||||||
@@ -6,11 +6,6 @@
|
|||||||
#include "log.hpp"
|
#include "log.hpp"
|
||||||
#include "nro.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 "yati/source/file.hpp"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -18,7 +13,6 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sys/iosupport.h>
|
|
||||||
|
|
||||||
namespace sphaira::devoptab {
|
namespace sphaira::devoptab {
|
||||||
namespace {
|
namespace {
|
||||||
@@ -48,26 +42,18 @@ struct DirEntry {
|
|||||||
romfs::DirEntry romfs;
|
romfs::DirEntry romfs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Device {
|
|
||||||
std::unique_ptr<yati::source::Base> source;
|
|
||||||
std::vector<NamedCollection> collections;
|
|
||||||
FsTimeStampRaw timestamp;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct File {
|
struct File {
|
||||||
Device* device;
|
|
||||||
FileEntry entry;
|
FileEntry entry;
|
||||||
size_t off;
|
size_t off;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Dir {
|
struct Dir {
|
||||||
Device* device;
|
|
||||||
DirEntry entry;
|
DirEntry entry;
|
||||||
u32 index;
|
u32 index;
|
||||||
bool is_root;
|
bool is_root;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool find_file(std::span<NamedCollection> named, std::string_view path, FileEntry& out) {
|
bool find_file(std::span<const NamedCollection> named, std::string_view path, FileEntry& out) {
|
||||||
for (auto& e : named) {
|
for (auto& e : named) {
|
||||||
if (path.starts_with("/" + e.name)) {
|
if (path.starts_with("/" + e.name)) {
|
||||||
out.is_romfs = e.is_romfs;
|
out.is_romfs = e.is_romfs;
|
||||||
@@ -112,61 +98,75 @@ bool find_dir(std::span<const NamedCollection> named, std::string_view path, Dir
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fill_timestamp_from_device(const Device* device, struct stat *st) {
|
void fill_timestamp_from_device(const FsTimeStampRaw& timestamp, struct stat *st) {
|
||||||
st->st_atime = device->timestamp.accessed;
|
st->st_atime = timestamp.accessed;
|
||||||
st->st_ctime = device->timestamp.created;
|
st->st_ctime = timestamp.created;
|
||||||
st->st_mtime = device->timestamp.modified;
|
st->st_mtime = timestamp.modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
int set_errno(struct _reent *r, int err) {
|
struct Device final : common::MountDevice {
|
||||||
r->_errno = err;
|
Device(std::unique_ptr<yati::source::Base>&& _source, const std::vector<NamedCollection>& _collections, const FsTimeStampRaw& _timestamp, const common::MountConfig& _config)
|
||||||
return -1;
|
: MountDevice{_config}
|
||||||
}
|
, source{std::forward<decltype(_source)>(_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<yati::source::Base> source;
|
||||||
|
const std::vector<NamedCollection> collections;
|
||||||
|
const FsTimeStampRaw timestamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mode) {
|
||||||
auto file = static_cast<File*>(fileStruct);
|
auto file = static_cast<File*>(fileStruct);
|
||||||
std::memset(file, 0, sizeof(*file));
|
|
||||||
|
|
||||||
char path[PATH_MAX];
|
FileEntry entry{};
|
||||||
if (!common::fix_path(_path, path)) {
|
if (!find_file(this->collections, path, entry)) {
|
||||||
return set_errno(r, ENOENT);
|
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;
|
file->entry = entry;
|
||||||
|
return 0;
|
||||||
return r->_errno = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_close(struct _reent *r, void *fd) {
|
int Device::devoptab_close(void *fd) {
|
||||||
auto file = static_cast<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
std::memset(file, 0, sizeof(*file));
|
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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
const auto& entry = file->entry;
|
const auto& entry = file->entry;
|
||||||
|
|
||||||
u64 bytes_read;
|
u64 bytes_read;
|
||||||
len = std::min(len, entry.size - file->off);
|
len = std::min(len, entry.size - file->off);
|
||||||
if (R_FAILED(file->device->source->Read(ptr, entry.offset + file->off, len, &bytes_read))) {
|
if (R_FAILED(this->source->Read(ptr, entry.offset + file->off, len, &bytes_read))) {
|
||||||
return set_errno(r, ENOENT);
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
file->off += bytes_read;
|
file->off += bytes_read;
|
||||||
return 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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
const auto& entry = file->entry;
|
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;
|
pos = entry.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
r->_errno = 0;
|
|
||||||
return file->off = std::clamp<u64>(pos, 0, entry.size);
|
return file->off = std::clamp<u64>(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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
const auto& entry = file->entry;
|
const auto& entry = file->entry;
|
||||||
|
|
||||||
std::memset(st, 0, sizeof(*st));
|
|
||||||
st->st_nlink = 1;
|
st->st_nlink = 1;
|
||||||
st->st_size = entry.size;
|
st->st_size = entry.size;
|
||||||
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
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) {
|
int Device::devoptab_diropen(void* fd, const char *path) {
|
||||||
auto device = (Device*)r->deviceData;
|
auto dir = static_cast<Dir*>(fd);
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
|
||||||
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, "/")) {
|
if (!std::strcmp(path, "/")) {
|
||||||
dir->device = device;
|
|
||||||
dir->is_root = true;
|
dir->is_root = true;
|
||||||
r->_errno = 0;
|
return 0;
|
||||||
return dirState;
|
|
||||||
} else {
|
} else {
|
||||||
DirEntry entry;
|
DirEntry entry{};
|
||||||
if (!find_dir(device->collections, path, entry)) {
|
if (!find_dir(this->collections, path, entry)) {
|
||||||
set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dir->device = device;
|
|
||||||
dir->entry = entry;
|
dir->entry = entry;
|
||||||
|
return 0;
|
||||||
r->_errno = 0;
|
|
||||||
return dirState;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) {
|
int Device::devoptab_dirreset(void* fd) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
auto& entry = dir->entry;
|
auto& entry = dir->entry;
|
||||||
|
|
||||||
if (dir->is_root) {
|
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) {
|
int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
auto& entry = dir->entry;
|
auto& entry = dir->entry;
|
||||||
std::memset(filestat, 0, sizeof(*filestat));
|
|
||||||
|
|
||||||
if (dir->is_root) {
|
if (dir->is_root) {
|
||||||
if (dir->index >= dir->device->collections.size()) {
|
if (dir->index >= this->collections.size()) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& e = dir->device->collections[dir->index];
|
const auto& e = this->collections[dir->index];
|
||||||
if (e.is_romfs) {
|
if (e.is_romfs) {
|
||||||
filestat->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
filestat->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
} else {
|
} else {
|
||||||
@@ -262,126 +245,60 @@ int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struc
|
|||||||
} else {
|
} else {
|
||||||
if (entry.is_romfs) {
|
if (entry.is_romfs) {
|
||||||
if (!romfs::dirnext(entry.romfs, filename, filestat)) {
|
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++;
|
dir->index++;
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirclose(struct _reent *r, DIR_ITER *dirState) {
|
int Device::devoptab_dirclose(void* fd) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
std::memset(dir, 0, sizeof(*dir));
|
std::memset(dir, 0, sizeof(*dir));
|
||||||
|
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) {
|
int Device::devoptab_lstat(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));
|
|
||||||
st->st_nlink = 1;
|
st->st_nlink = 1;
|
||||||
|
|
||||||
if (!std::strcmp(path, "/")) {
|
if (!std::strcmp(path, "/")) {
|
||||||
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
} else {
|
} else {
|
||||||
// can be optimised for romfs.
|
// can be optimised for romfs.
|
||||||
FileEntry file_entry;
|
FileEntry file_entry{};
|
||||||
DirEntry dir_entry;
|
DirEntry dir_entry{};
|
||||||
if (find_file(device->collections, path, file_entry)) {
|
if (find_file(this->collections, path, file_entry)) {
|
||||||
st->st_size = file_entry.size;
|
st->st_size = file_entry.size;
|
||||||
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
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;
|
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
} else {
|
} else {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fill_timestamp_from_device(device, st);
|
fill_timestamp_from_device(this->timestamp, st);
|
||||||
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() {
|
|
||||||
RemoveDevice(mount);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Mutex g_mutex;
|
|
||||||
std::array<std::unique_ptr<Entry>, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Result MountNro(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) {
|
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<yati::source::File>(fs, path);
|
auto source = std::make_unique<yati::source::File>(fs, path);
|
||||||
|
|
||||||
NroData data;
|
NroData data{};
|
||||||
R_TRY(source->Read2(&data, 0, sizeof(data)));
|
R_TRY(source->Read2(&data, 0, sizeof(data)));
|
||||||
R_UNLESS(data.header.magic == NROHEADER_MAGIC, Result_NroBadMagic);
|
R_UNLESS(data.header.magic == NROHEADER_MAGIC, Result_NroBadMagic);
|
||||||
|
|
||||||
NroAssetHeader asset;
|
NroAssetHeader asset{};
|
||||||
R_TRY(source->Read2(&asset, data.header.size, sizeof(asset)));
|
R_TRY(source->Read2(&asset, data.header.size, sizeof(asset)));
|
||||||
R_UNLESS(asset.magic == NROASSETHEADER_MAGIC, Result_NroBadMagic);
|
R_UNLESS(asset.magic == NROASSETHEADER_MAGIC, Result_NroBadMagic);
|
||||||
|
|
||||||
std::vector<NamedCollection> collections;
|
std::vector<NamedCollection> collections{};
|
||||||
|
|
||||||
if (asset.icon.size) {
|
if (asset.icon.size) {
|
||||||
NamedCollection collection{"icon.jpg", false, AssetCollection{data.header.size + asset.icon.offset, 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);
|
R_UNLESS(!collections.empty(), 0x9);
|
||||||
|
|
||||||
auto entry = std::make_unique<Entry>();
|
FsTimeStampRaw timestamp{};
|
||||||
entry->path = path;
|
fs->GetFileTimeStampRaw(path, ×tamp);
|
||||||
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);
|
|
||||||
|
|
||||||
R_UNLESS(AddDevice(&entry->devoptab) >= 0, 0x1);
|
if (!common::MountReadOnlyIndexDevice(
|
||||||
log_write("[NRO] DEVICE SUCCESS %s %s\n", path.s, entry->name);
|
[&source, &collections, ×tamp](const common::MountConfig& config) {
|
||||||
|
return std::make_unique<Device>(std::move(source), collections, timestamp, config);
|
||||||
out_path = entry->mount;
|
},
|
||||||
entry->ref_count++;
|
sizeof(File), sizeof(Dir),
|
||||||
*itr = std::move(entry);
|
"NRO", out_path
|
||||||
|
)) {
|
||||||
|
log_write("[NRO] Failed to mount %s\n", path.s);
|
||||||
|
R_THROW(0x1);
|
||||||
|
}
|
||||||
|
|
||||||
R_SUCCEED();
|
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
|
} // namespace sphaira::devoptab
|
||||||
|
|||||||
@@ -13,76 +13,84 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sys/iosupport.h>
|
|
||||||
|
|
||||||
namespace sphaira::devoptab {
|
namespace sphaira::devoptab {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct Device {
|
using Collections = yati::container::Collections;
|
||||||
std::unique_ptr<common::LruBufferedData> source;
|
|
||||||
yati::container::Collections collections;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct File {
|
struct File {
|
||||||
Device* device;
|
|
||||||
const yati::container::CollectionEntry* collection;
|
const yati::container::CollectionEntry* collection;
|
||||||
size_t off;
|
size_t off;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Dir {
|
struct Dir {
|
||||||
Device* device;
|
|
||||||
u32 index;
|
u32 index;
|
||||||
};
|
};
|
||||||
|
|
||||||
int set_errno(struct _reent *r, int err) {
|
struct Device final : common::MountDevice {
|
||||||
r->_errno = err;
|
Device(std::unique_ptr<common::LruBufferedData>&& _source, const Collections& _collections, const common::MountConfig& _config)
|
||||||
return -1;
|
: MountDevice{_config}
|
||||||
}
|
, source{std::forward<decltype(_source)>(_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<File*>(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<common::LruBufferedData> source;
|
||||||
|
const Collections collections;
|
||||||
|
};
|
||||||
|
|
||||||
|
int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mode) {
|
||||||
|
auto file = static_cast<File*>(fileStruct);
|
||||||
|
|
||||||
|
for (const auto& collection : this->collections) {
|
||||||
if (path == "/" + collection.name) {
|
if (path == "/" + collection.name) {
|
||||||
file->device = device;
|
|
||||||
file->collection = &collection;
|
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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
std::memset(file, 0, sizeof(*file));
|
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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
|
|
||||||
const auto& collection = file->collection;
|
const auto& collection = file->collection;
|
||||||
len = std::min(len, collection->size - file->off);
|
len = std::min(len, collection->size - file->off);
|
||||||
|
|
||||||
u64 bytes_read;
|
u64 bytes_read;
|
||||||
if (R_FAILED(file->device->source->Read(ptr, collection->offset + file->off, len, &bytes_read))) {
|
if (R_FAILED(this->source->Read(ptr, collection->offset + file->off, len, &bytes_read))) {
|
||||||
return set_errno(r, ENOENT);
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
file->off += bytes_read;
|
file->off += bytes_read;
|
||||||
return 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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
const auto& collection = file->collection;
|
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;
|
pos = collection->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
r->_errno = 0;
|
|
||||||
return file->off = std::clamp<u64>(pos, 0, collection->size);
|
return file->off = std::clamp<u64>(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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
const auto& collection = file->collection;
|
const auto& collection = file->collection;
|
||||||
|
|
||||||
std::memset(st, 0, sizeof(*st));
|
|
||||||
st->st_nlink = 1;
|
st->st_nlink = 1;
|
||||||
st->st_size = collection->size;
|
st->st_size = collection->size;
|
||||||
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
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) {
|
int Device::devoptab_diropen(void* fd, const char *path) {
|
||||||
auto device = (Device*)r->deviceData;
|
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
|
||||||
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, "/")) {
|
if (!std::strcmp(path, "/")) {
|
||||||
dir->device = device;
|
return 0;
|
||||||
} else {
|
|
||||||
set_errno(r, ENOENT);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r->_errno = 0;
|
log_write("[NSP] failed to open dir %s\n", path);
|
||||||
return dirState;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) {
|
int Device::devoptab_dirreset(void* fd) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
|
|
||||||
dir->index = 0;
|
dir->index = 0;
|
||||||
|
return 0;
|
||||||
return r->_errno = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) {
|
int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
std::memset(filestat, 0, sizeof(*filestat));
|
|
||||||
|
|
||||||
if (dir->index >= dir->device->collections.size()) {
|
if (dir->index >= this->collections.size()) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& collection = dir->device->collections[dir->index];
|
const auto& collection = this->collections[dir->index];
|
||||||
filestat->st_nlink = 1;
|
filestat->st_nlink = 1;
|
||||||
filestat->st_size = collection.size;
|
filestat->st_size = collection.size;
|
||||||
filestat->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
filestat->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
std::strcpy(filename, collection.name.c_str());
|
std::strcpy(filename, collection.name.c_str());
|
||||||
|
|
||||||
dir->index++;
|
dir->index++;
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirclose(struct _reent *r, DIR_ITER *dirState) {
|
int Device::devoptab_dirclose(void* fd) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
std::memset(dir, 0, sizeof(*dir));
|
std::memset(dir, 0, sizeof(*dir));
|
||||||
|
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) {
|
int Device::devoptab_lstat(const char *path, struct stat *st) {
|
||||||
auto device = (Device*)r->deviceData;
|
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));
|
|
||||||
|
|
||||||
if (!std::strcmp(path, "/")) {
|
if (!std::strcmp(path, "/")) {
|
||||||
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
} else {
|
} 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;
|
return path == "/" + e.name;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (it == device->collections.end()) {
|
if (it == this->collections.end()) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
st->st_size = it->size;
|
st->st_size = it->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
st->st_nlink = 1;
|
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<std::unique_ptr<Entry>, common::MAX_ENTRIES> g_entries;
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Result MountNsp(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) {
|
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<yati::source::File>(fs, path);
|
auto source = std::make_shared<yati::source::File>(fs, path);
|
||||||
|
|
||||||
s64 size;
|
s64 size;
|
||||||
@@ -255,44 +187,18 @@ Result MountNsp(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) {
|
|||||||
yati::container::Collections collections;
|
yati::container::Collections collections;
|
||||||
R_TRY(nsp.GetCollections(collections));
|
R_TRY(nsp.GetCollections(collections));
|
||||||
|
|
||||||
auto entry = std::make_unique<Entry>();
|
if (!common::MountReadOnlyIndexDevice(
|
||||||
entry->path = path;
|
[&buffered, &collections](const common::MountConfig& config) {
|
||||||
entry->devoptab = DEVOPTAB;
|
return std::make_unique<Device>(std::move(buffered), collections, config);
|
||||||
entry->devoptab.name = entry->name;
|
},
|
||||||
entry->devoptab.deviceData = &entry->device;
|
sizeof(File), sizeof(Dir),
|
||||||
entry->device.source = std::move(buffered);
|
"NSP", out_path
|
||||||
entry->device.collections = collections;
|
)) {
|
||||||
std::snprintf(entry->name, sizeof(entry->name), "nsp_%zu", index);
|
log_write("[NSP] Failed to mount %s\n", path.s);
|
||||||
std::snprintf(entry->mount, sizeof(entry->mount), "nsp_%zu:/", index);
|
R_THROW(0x1);
|
||||||
|
}
|
||||||
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);
|
|
||||||
|
|
||||||
R_SUCCEED();
|
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
|
} // namespace sphaira::devoptab
|
||||||
|
|||||||
@@ -12,18 +12,11 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sys/iosupport.h>
|
|
||||||
|
|
||||||
namespace sphaira::devoptab {
|
namespace sphaira::devoptab {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct Device {
|
|
||||||
save_ctx_t* ctx;
|
|
||||||
hierarchical_save_file_table_ctx_t* file_table;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct File {
|
struct File {
|
||||||
Device* device;
|
|
||||||
save_fs_list_entry_t entry;
|
save_fs_list_entry_t entry;
|
||||||
allocation_table_storage_ctx_t storage;
|
allocation_table_storage_ctx_t storage;
|
||||||
size_t off;
|
size_t off;
|
||||||
@@ -35,138 +28,140 @@ struct DirNext {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Dir {
|
struct Dir {
|
||||||
Device* device;
|
|
||||||
save_fs_list_entry_t entry;
|
save_fs_list_entry_t entry;
|
||||||
u32 next_directory;
|
u32 next_directory;
|
||||||
u32 next_file;
|
u32 next_file;
|
||||||
};
|
};
|
||||||
|
|
||||||
int set_errno(struct _reent *r, int err) {
|
struct Device final : common::MountDevice {
|
||||||
r->_errno = err;
|
Device(save_ctx_t* _ctx, const common::MountConfig& _config)
|
||||||
return -1;
|
: 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) {
|
~Device() {
|
||||||
auto device = (Device*)r->deviceData;
|
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<File*>(fileStruct);
|
auto file = static_cast<File*>(fileStruct);
|
||||||
std::memset(file, 0, sizeof(*file));
|
|
||||||
|
|
||||||
char path[PATH_MAX];
|
if (!save_hierarchical_file_table_get_file_entry_by_path(this->file_table, path, &file->entry)) {
|
||||||
if (!common::fix_path(_path, path)) {
|
return -ENOENT;
|
||||||
return set_errno(r, ENOENT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!save_hierarchical_file_table_get_file_entry_by_path(device->file_table, path, &file->entry)) {
|
if (!save_open_fat_storage(&this->ctx->save_filesystem_core, &file->storage, file->entry.value.save_file_info.start_block)) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!save_open_fat_storage(&device->ctx->save_filesystem_core, &file->storage, file->entry.value.save_file_info.start_block)) {
|
return 0;
|
||||||
return set_errno(r, ENOENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
file->device = device;
|
|
||||||
return r->_errno = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_close(struct _reent *r, void *fd) {
|
int Device::devoptab_close(void *fd) {
|
||||||
auto file = static_cast<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
std::memset(file, 0, sizeof(*file));
|
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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
|
len = std::min(len, file->entry.value.save_file_info.length - file->off);
|
||||||
|
|
||||||
|
if (!len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// todo: maybe eof here?
|
// todo: maybe eof here?
|
||||||
const auto bytes_read = save_allocation_table_storage_read(&file->storage, ptr, file->off, len);
|
const auto bytes_read = save_allocation_table_storage_read(&file->storage, ptr, file->off, len);
|
||||||
if (!bytes_read) {
|
if (!bytes_read) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
file->off += bytes_read;
|
file->off += bytes_read;
|
||||||
return 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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
|
|
||||||
if (dir == SEEK_CUR) {
|
if (dir == SEEK_CUR) {
|
||||||
pos += file->off;
|
pos += file->off;
|
||||||
} else if (dir == SEEK_END) {
|
} 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<u64>(pos, 0, file->entry.value.save_file_info.length);
|
||||||
return file->off = std::clamp<u64>(pos, 0, file->storage._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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
|
|
||||||
log_write("[\t\tDEV] fstat\n");
|
|
||||||
std::memset(st, 0, sizeof(*st));
|
|
||||||
st->st_nlink = 1;
|
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;
|
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) {
|
int Device::devoptab_diropen(void* fd, const char *path) {
|
||||||
auto device = (Device*)r->deviceData;
|
auto dir = static_cast<Dir*>(fd);
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
|
||||||
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, "/")) {
|
if (!std::strcmp(path, "/")) {
|
||||||
save_entry_key_t key{};
|
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) {
|
if (idx == 0xFFFFFFFF) {
|
||||||
set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!save_fs_list_get_value(&device->file_table->directory_table, idx, &dir->entry)) {
|
if (!save_fs_list_get_value(&this->file_table->directory_table, idx, &dir->entry)) {
|
||||||
set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
} else if (!save_hierarchical_directory_table_get_file_entry_by_path(device->file_table, path, &dir->entry)) {
|
} else if (!save_hierarchical_directory_table_get_file_entry_by_path(this->file_table, path, &dir->entry)) {
|
||||||
set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dir->device = device;
|
|
||||||
dir->next_file = dir->entry.value.save_find_position.next_file;
|
dir->next_file = dir->entry.value.save_find_position.next_file;
|
||||||
dir->next_directory = dir->entry.value.save_find_position.next_directory;
|
dir->next_directory = dir->entry.value.save_find_position.next_directory;
|
||||||
|
|
||||||
r->_errno = 0;
|
return 0;
|
||||||
return dirState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) {
|
int Device::devoptab_dirreset(void* fd) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
|
|
||||||
dir->next_file = dir->entry.value.save_find_position.next_file;
|
dir->next_file = dir->entry.value.save_find_position.next_file;
|
||||||
dir->next_directory = dir->entry.value.save_find_position.next_directory;
|
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) {
|
int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
|
|
||||||
std::memset(filestat, 0, sizeof(*filestat));
|
|
||||||
save_fs_list_entry_t entry{};
|
save_fs_list_entry_t entry{};
|
||||||
|
|
||||||
if (dir->next_directory) {
|
if (dir->next_directory) {
|
||||||
// todo: use save_allocation_table_storage_read for faster reads
|
// 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)) {
|
if (!save_fs_list_get_value(&this->file_table->directory_table, dir->next_directory, &entry)) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
filestat->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
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) {
|
else if (dir->next_file) {
|
||||||
// todo: use save_allocation_table_storage_read for faster reads
|
// 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)) {
|
if (!save_fs_list_get_value(&this->file_table->file_table, dir->next_file, &entry)) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
filestat->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
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;
|
dir->next_file = entry.value.next_sibling;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
filestat->st_nlink = 1;
|
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) {
|
int Device::devoptab_dirclose(void* fd) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
std::memset(dir, 0, sizeof(*dir));
|
std::memset(dir, 0, sizeof(*dir));
|
||||||
|
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) {
|
int Device::devoptab_lstat(const char *path, struct stat *st) {
|
||||||
auto device = (Device*)r->deviceData;
|
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{};
|
save_fs_list_entry_t entry{};
|
||||||
|
|
||||||
// NOTE: this is very slow.
|
// 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_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
st->st_size = entry.value.save_file_info.length;
|
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;
|
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
} else {
|
} else {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
st->st_nlink = 1;
|
return 0;
|
||||||
|
|
||||||
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{};
|
|
||||||
u64 id{};
|
|
||||||
fs::FsPath mount{};
|
|
||||||
char name[32]{};
|
|
||||||
s32 ref_count{};
|
|
||||||
|
|
||||||
~Entry() {
|
|
||||||
RemoveDevice(mount);
|
|
||||||
save_close_savefile(&device.ctx);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Mutex g_mutex;
|
|
||||||
std::array<std::unique_ptr<Entry>, common::MAX_ENTRIES> g_entries;
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Result MountSaveSystem(u64 id, fs::FsPath& out_path) {
|
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.
|
fs::FsPath path{};
|
||||||
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];
|
|
||||||
std::snprintf(path, sizeof(path), "SYSTEM:/save/%016lx", id);
|
std::snprintf(path, sizeof(path), "SYSTEM:/save/%016lx", id);
|
||||||
|
|
||||||
auto ctx = save_open_savefile(path, 0);
|
auto ctx = save_open_savefile(path, 0);
|
||||||
@@ -288,46 +225,18 @@ Result MountSaveSystem(u64 id, fs::FsPath& out_path) {
|
|||||||
R_THROW(0x1);
|
R_THROW(0x1);
|
||||||
}
|
}
|
||||||
|
|
||||||
log_write("[SAVE] OPEN SUCCESS %s\n", path);
|
if (!common::MountReadOnlyIndexDevice(
|
||||||
|
[&ctx](const common::MountConfig& config) {
|
||||||
auto entry = std::make_unique<Entry>();
|
return std::make_unique<Device>(ctx, config);
|
||||||
entry->id = id;
|
},
|
||||||
entry->device.ctx = ctx;
|
sizeof(File), sizeof(Dir),
|
||||||
entry->device.file_table = &ctx->save_filesystem_core.file_table;
|
"SAVE", out_path
|
||||||
entry->devoptab = DEVOPTAB;
|
)) {
|
||||||
entry->devoptab.name = entry->name;
|
log_write("[SAVE] Failed to mount %s\n", path.s);
|
||||||
entry->devoptab.deviceData = &entry->device;
|
R_THROW(0x1);
|
||||||
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);
|
|
||||||
|
|
||||||
R_SUCCEED();
|
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
|
} // namespace sphaira::devoptab
|
||||||
|
|||||||
@@ -12,79 +12,85 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sys/iosupport.h>
|
|
||||||
|
|
||||||
namespace sphaira::devoptab {
|
namespace sphaira::devoptab {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct Device {
|
|
||||||
std::unique_ptr<common::LruBufferedData> source;
|
|
||||||
yati::container::Xci::Partitions partitions;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct File {
|
struct File {
|
||||||
Device* device;
|
|
||||||
const yati::container::CollectionEntry* collection;
|
const yati::container::CollectionEntry* collection;
|
||||||
size_t off;
|
size_t off;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Dir {
|
struct Dir {
|
||||||
Device* device;
|
|
||||||
const yati::container::Collections* collections;
|
const yati::container::Collections* collections;
|
||||||
u32 index;
|
u32 index;
|
||||||
};
|
};
|
||||||
|
|
||||||
int set_errno(struct _reent *r, int err) {
|
struct Device final : common::MountDevice {
|
||||||
r->_errno = err;
|
Device(std::unique_ptr<common::LruBufferedData>&& _source, const yati::container::Xci::Partitions& _partitions, const common::MountConfig& _config)
|
||||||
return -1;
|
: MountDevice{_config}
|
||||||
}
|
, source{std::forward<decltype(_source)>(_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<File*>(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<common::LruBufferedData> source;
|
||||||
|
const yati::container::Xci::Partitions partitions;
|
||||||
|
};
|
||||||
|
|
||||||
|
int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mode) {
|
||||||
|
auto file = static_cast<File*>(fileStruct);
|
||||||
|
|
||||||
|
for (const auto& partition : this->partitions) {
|
||||||
for (const auto& collection : partition.collections) {
|
for (const auto& collection : partition.collections) {
|
||||||
if (path == "/" + partition.name + "/" + collection.name) {
|
if (path == "/" + partition.name + "/" + collection.name) {
|
||||||
file->device = device;
|
|
||||||
file->collection = &collection;
|
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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
std::memset(file, 0, sizeof(*file));
|
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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
|
|
||||||
const auto& collection = file->collection;
|
const auto& collection = file->collection;
|
||||||
len = std::min(len, collection->size - file->off);
|
len = std::min(len, collection->size - file->off);
|
||||||
|
|
||||||
u64 bytes_read;
|
u64 bytes_read;
|
||||||
if (R_FAILED(file->device->source->Read(ptr, collection->offset + file->off, len, &bytes_read))) {
|
if (R_FAILED(this->source->Read(ptr, collection->offset + file->off, len, &bytes_read))) {
|
||||||
return set_errno(r, ENOENT);
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
file->off += bytes_read;
|
file->off += bytes_read;
|
||||||
return 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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
const auto& collection = file->collection;
|
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;
|
pos = collection->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
r->_errno = 0;
|
|
||||||
return file->off = std::clamp<u64>(pos, 0, collection->size);
|
return file->off = std::clamp<u64>(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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
const auto& collection = file->collection;
|
const auto& collection = file->collection;
|
||||||
|
|
||||||
std::memset(st, 0, sizeof(*st));
|
|
||||||
st->st_nlink = 1;
|
st->st_nlink = 1;
|
||||||
st->st_size = collection->size;
|
st->st_size = collection->size;
|
||||||
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
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) {
|
int Device::devoptab_diropen(void* fd, const char *path) {
|
||||||
auto device = (Device*)r->deviceData;
|
auto dir = static_cast<Dir*>(fd);
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
|
||||||
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, "/")) {
|
if (!std::strcmp(path, "/")) {
|
||||||
dir->device = device;
|
return 0;
|
||||||
r->_errno = 0;
|
|
||||||
return dirState;
|
|
||||||
} else {
|
} else {
|
||||||
for (const auto& partition : device->partitions) {
|
for (const auto& partition : this->partitions) {
|
||||||
if (path == "/" + partition.name) {
|
if (path == "/" + partition.name) {
|
||||||
dir->collections = &partition.collections;
|
dir->collections = &partition.collections;
|
||||||
r->_errno = 0;
|
return 0;
|
||||||
return dirState;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) {
|
int Device::devoptab_dirreset(void* fd) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
|
|
||||||
dir->index = 0;
|
dir->index = 0;
|
||||||
|
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) {
|
int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
std::memset(filestat, 0, sizeof(*filestat));
|
|
||||||
|
|
||||||
if (!dir->collections) {
|
if (!dir->collections) {
|
||||||
if (dir->index >= dir->device->partitions.size()) {
|
if (dir->index >= this->partitions.size()) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
filestat->st_nlink = 1;
|
filestat->st_nlink = 1;
|
||||||
filestat->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
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 {
|
} else {
|
||||||
if (dir->index >= dir->collections->size()) {
|
if (dir->index >= dir->collections->size()) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& collection = (*dir->collections)[dir->index];
|
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++;
|
dir->index++;
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirclose(struct _reent *r, DIR_ITER *dirState) {
|
int Device::devoptab_dirclose(void* fd) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
std::memset(dir, 0, sizeof(*dir));
|
std::memset(dir, 0, sizeof(*dir));
|
||||||
|
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) {
|
int Device::devoptab_lstat(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));
|
|
||||||
st->st_nlink = 1;
|
st->st_nlink = 1;
|
||||||
|
|
||||||
if (!std::strcmp(path, "/")) {
|
if (!std::strcmp(path, "/")) {
|
||||||
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
} else {
|
} else {
|
||||||
for (const auto& partition : device->partitions) {
|
for (const auto& partition : this->partitions) {
|
||||||
if (path == "/" + partition.name) {
|
if (path == "/" + partition.name) {
|
||||||
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& collection : partition.collections) {
|
for (const auto& collection : partition.collections) {
|
||||||
if (path == "/" + partition.name + "/" + collection.name) {
|
if (path == "/" + partition.name + "/" + collection.name) {
|
||||||
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
st->st_size = collection.size;
|
st->st_size = collection.size;
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return set_errno(r, ENOENT);
|
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<std::unique_ptr<Entry>, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result MountXciInternal(const std::shared_ptr<yati::source::Base>& source, s64 size, const fs::FsPath& path, fs::FsPath& out_path) {
|
Result MountXciInternal(const std::shared_ptr<yati::source::Base>& 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<common::LruBufferedData>(source, size);
|
auto buffered = std::make_unique<common::LruBufferedData>(source, size);
|
||||||
|
|
||||||
yati::container::Xci xci{buffered.get()};
|
yati::container::Xci xci{buffered.get()};
|
||||||
yati::container::Xci::Root root;
|
yati::container::Xci::Root root;
|
||||||
R_TRY(xci.GetRoot(root));
|
R_TRY(xci.GetRoot(root));
|
||||||
|
|
||||||
auto entry = std::make_unique<Entry>();
|
if (!common::MountReadOnlyIndexDevice(
|
||||||
entry->path = path;
|
[&buffered, &root](const common::MountConfig& config) {
|
||||||
entry->devoptab = DEVOPTAB;
|
return std::make_unique<Device>(std::move(buffered), root.partitions, config);
|
||||||
entry->devoptab.name = entry->name;
|
},
|
||||||
entry->devoptab.deviceData = &entry->device;
|
sizeof(File), sizeof(Dir),
|
||||||
entry->device.source = std::move(buffered);
|
"XCI", out_path
|
||||||
entry->device.partitions = root.partitions;
|
)) {
|
||||||
std::snprintf(entry->name, sizeof(entry->name), "xci_%zu", index);
|
log_write("[XCI] Failed to mount %s\n", path.s);
|
||||||
std::snprintf(entry->mount, sizeof(entry->mount), "xci_%zu:/", index);
|
R_THROW(0x1);
|
||||||
|
}
|
||||||
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);
|
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
@@ -305,12 +220,6 @@ Result MountXciInternal(const std::shared_ptr<yati::source::Base>& source, s64 s
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Result MountXci(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) {
|
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;
|
s64 size;
|
||||||
auto source = std::make_shared<yati::source::File>(fs, path);
|
auto source = std::make_shared<yati::source::File>(fs, path);
|
||||||
R_TRY(source->GetSize(&size));
|
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<sphaira::yati::source::Base>& source, s64 size, const fs::FsPath& path, fs::FsPath& out_path) {
|
Result MountXciSource(const std::shared_ptr<sphaira::yati::source::Base>& 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);
|
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
|
} // namespace sphaira::devoptab
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sys/iosupport.h>
|
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
|
||||||
namespace sphaira::devoptab {
|
namespace sphaira::devoptab {
|
||||||
@@ -113,11 +112,6 @@ struct DirectoryEntry {
|
|||||||
|
|
||||||
using FileTableEntries = std::vector<FileEntry>;
|
using FileTableEntries = std::vector<FileEntry>;
|
||||||
|
|
||||||
struct Device {
|
|
||||||
std::unique_ptr<common::LruBufferedData> source;
|
|
||||||
DirectoryEntry root;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Zfile {
|
struct Zfile {
|
||||||
z_stream z; // zlib stream.
|
z_stream z; // zlib stream.
|
||||||
Bytef* buffer; // buffer that compressed data is read into.
|
Bytef* buffer; // buffer that compressed data is read into.
|
||||||
@@ -126,7 +120,6 @@ struct Zfile {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct File {
|
struct File {
|
||||||
Device* device;
|
|
||||||
const FileEntry* entry;
|
const FileEntry* entry;
|
||||||
Zfile zfile; // only used if the file is compressed.
|
Zfile zfile; // only used if the file is compressed.
|
||||||
size_t data_off; // offset of the file data.
|
size_t data_off; // offset of the file data.
|
||||||
@@ -134,7 +127,6 @@ struct File {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Dir {
|
struct Dir {
|
||||||
Device* device;
|
|
||||||
const DirectoryEntry* entry;
|
const DirectoryEntry* entry;
|
||||||
u32 index;
|
u32 index;
|
||||||
};
|
};
|
||||||
@@ -194,44 +186,58 @@ void set_stat_file(const FileEntry* entry, struct stat *st) {
|
|||||||
st->st_ctime = st->st_atime;
|
st->st_ctime = st->st_atime;
|
||||||
}
|
}
|
||||||
|
|
||||||
int set_errno(struct _reent *r, int err) {
|
struct Device final : common::MountDevice {
|
||||||
r->_errno = err;
|
Device(std::unique_ptr<common::LruBufferedData>&& _source, const DirectoryEntry& _root, const common::MountConfig& _config)
|
||||||
return -1;
|
: MountDevice{_config}
|
||||||
}
|
, source{std::forward<decltype(_source)>(_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<File*>(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<common::LruBufferedData> source;
|
||||||
|
const DirectoryEntry root;
|
||||||
|
};
|
||||||
|
|
||||||
|
int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mode) {
|
||||||
|
auto file = static_cast<File*>(fileStruct);
|
||||||
|
|
||||||
|
const auto entry = find_file_entry(this->root, path);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((entry->flags & mmz_Flag_Encrypted) || (entry->flags & mmz_Flag_StrongEncrypted)) {
|
if ((entry->flags & mmz_Flag_Encrypted) || (entry->flags & mmz_Flag_StrongEncrypted)) {
|
||||||
log_write("[ZIP] encrypted zip not supported\n");
|
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) {
|
if (entry->compression_type != mmz_Compression_None && entry->compression_type != mmz_Compression_Deflate) {
|
||||||
log_write("[ZIP] unsuported compression type: %u\n", entry->compression_type);
|
log_write("[ZIP] unsuported compression type: %u\n", entry->compression_type);
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
mmz_LocalHeader local_hdr{};
|
mmz_LocalHeader local_hdr{};
|
||||||
auto offset = entry->local_file_header_off;
|
auto offset = entry->local_file_header_off;
|
||||||
if (R_FAILED(device->source->Read2(&local_hdr, offset, sizeof(local_hdr)))) {
|
if (R_FAILED(this->source->Read2(&local_hdr, offset, sizeof(local_hdr)))) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (local_hdr.sig != LOCAL_HEADER_SIG) {
|
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;
|
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?
|
// todo: does a decs take prio over file header?
|
||||||
if (local_hdr.flags & mmz_Flag_DataDescriptor) {
|
if (local_hdr.flags & mmz_Flag_DataDescriptor) {
|
||||||
mmz_DataDescriptor data_desc{};
|
mmz_DataDescriptor data_desc{};
|
||||||
if (R_FAILED(device->source->Read2(&data_desc, offset, sizeof(data_desc)))) {
|
if (R_FAILED(this->source->Read2(&data_desc, offset, sizeof(data_desc)))) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data_desc.sig != DATA_DESCRIPTOR_SIG) {
|
if (data_desc.sig != DATA_DESCRIPTOR_SIG) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += sizeof(data_desc);
|
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) {
|
if (entry->compression_type == mmz_Compression_Deflate) {
|
||||||
auto& zfile = file->zfile;
|
auto& zfile = file->zfile;
|
||||||
zfile.buffer_size = 1024 * 64;
|
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) {
|
if (!zfile.buffer) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip zlib header.
|
// skip zlib header.
|
||||||
if (Z_OK != inflateInit2(&zfile.z, -MAX_WBITS)) {
|
if (Z_OK != inflateInit2(&zfile.z, -MAX_WBITS)) {
|
||||||
free(zfile.buffer);
|
std::free(zfile.buffer);
|
||||||
zfile.buffer = nullptr;
|
zfile.buffer = nullptr;
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file->device = device;
|
|
||||||
file->entry = entry;
|
file->entry = entry;
|
||||||
file->data_off = offset;
|
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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
|
|
||||||
if (file->entry->compression_type == mmz_Compression_Deflate) {
|
if (file->entry->compression_type == mmz_Compression_Deflate) {
|
||||||
inflateEnd(&file->zfile.z);
|
inflateEnd(&file->zfile.z);
|
||||||
|
|
||||||
if (file->zfile.buffer) {
|
if (file->zfile.buffer) {
|
||||||
free(file->zfile.buffer);
|
std::free(file->zfile.buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memset(file, 0, sizeof(*file));
|
return 0;
|
||||||
return r->_errno = 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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
len = std::min(len, file->entry->uncompressed_size - file->off);
|
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 (file->entry->compression_type == mmz_Compression_None) {
|
||||||
if (R_FAILED(file->device->source->Read2(ptr, file->data_off + file->off, len))) {
|
if (R_FAILED(this->source->Read2(ptr, file->data_off + file->off, len))) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
} else if (file->entry->compression_type == mmz_Compression_Deflate) {
|
} else if (file->entry->compression_type == mmz_Compression_Deflate) {
|
||||||
auto& zfile = file->zfile;
|
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.
|
// check if we need to fetch more data.
|
||||||
if (!zfile.z.next_in || !zfile.z.avail_in) {
|
if (!zfile.z.next_in || !zfile.z.avail_in) {
|
||||||
const auto clen = std::min(zfile.buffer_size, file->entry->compressed_size - zfile.compressed_off);
|
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))) {
|
if (R_FAILED(this->source->Read2(zfile.buffer, file->data_off + zfile.compressed_off, clen))) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
zfile.compressed_off += clen;
|
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;
|
len -= zfile.z.avail_out;
|
||||||
} else {
|
} else {
|
||||||
log_write("[ZLIB] failed to inflate: %d %s\n", rc, zfile.z.msg);
|
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;
|
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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
|
|
||||||
// seek like normal.
|
// 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<u64>(pos, 0, file->entry->uncompressed_size);
|
return file->off = std::clamp<u64>(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<File*>(fd);
|
auto file = static_cast<File*>(fd);
|
||||||
|
|
||||||
std::memset(st, 0, sizeof(*st));
|
|
||||||
set_stat_file(file->entry, 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) {
|
int Device::devoptab_diropen(void* fd, const char *path) {
|
||||||
auto device = (Device*)r->deviceData;
|
auto dir = static_cast<Dir*>(fd);
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
|
||||||
std::memset(dir, 0, sizeof(*dir));
|
|
||||||
|
|
||||||
char path[PATH_MAX];
|
const auto entry = find_dir_entry(this->root, path);
|
||||||
if (!common::fix_path(_path, path)) {
|
|
||||||
set_errno(r, ENOENT);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto entry = find_dir_entry(device->root, path);
|
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dir->device = device;
|
|
||||||
dir->entry = entry;
|
dir->entry = entry;
|
||||||
r->_errno = 0;
|
return 0;
|
||||||
return dirState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirreset(struct _reent *r, DIR_ITER *dirState) {
|
int Device::devoptab_dirreset(void* fd) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
|
|
||||||
dir->index = 0;
|
dir->index = 0;
|
||||||
|
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) {
|
int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
std::memset(filestat, 0, sizeof(*filestat));
|
|
||||||
|
|
||||||
u32 index = dir->index;
|
u32 index = dir->index;
|
||||||
if (index >= dir->entry->dir_child.size()) {
|
if (index >= dir->entry->dir_child.size()) {
|
||||||
index -= dir->entry->dir_child.size();
|
index -= dir->entry->dir_child.size();
|
||||||
if (index >= dir->entry->file_child.size()) {
|
if (index >= dir->entry->file_child.size()) {
|
||||||
return set_errno(r, ENOENT);
|
return -ENOENT;
|
||||||
} else {
|
} else {
|
||||||
const auto& entry = dir->entry->file_child[index];
|
const auto& entry = dir->entry->file_child[index];
|
||||||
const auto rel_path = entry.path.substr(entry.path.find_last_of('/') + 1);
|
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++;
|
dir->index++;
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_dirclose(struct _reent *r, DIR_ITER *dirState) {
|
int Device::devoptab_dirclose(void* fd) {
|
||||||
auto dir = static_cast<Dir*>(dirState->dirStruct);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
std::memset(dir, 0, sizeof(*dir));
|
std::memset(dir, 0, sizeof(*dir));
|
||||||
|
|
||||||
return r->_errno = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) {
|
int Device::devoptab_lstat(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));
|
|
||||||
st->st_nlink = 1;
|
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;
|
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);
|
set_stat_file(entry, st);
|
||||||
} else {
|
} else {
|
||||||
log_write("[ZIP] didn't find in lstat\n");
|
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 {
|
auto BuildPath(const std::string& path) -> std::string {
|
||||||
if (path.starts_with('/')) {
|
if (path.starts_with('/')) {
|
||||||
return path;
|
return path;
|
||||||
@@ -600,48 +562,13 @@ Result ParseZip(common::LruBufferedData* source, s64 size, FileTableEntries& out
|
|||||||
R_SUCCEED();
|
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<std::unique_ptr<Entry>, common::MAX_ENTRIES> g_entries;
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Result MountZip(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) {
|
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<yati::source::File>(fs, path);
|
auto source = std::make_shared<yati::source::File>(fs, path);
|
||||||
|
|
||||||
s64 size;
|
s64 size;
|
||||||
R_TRY(source->GetSize(&size));
|
R_TRY(source->GetSize(&size));
|
||||||
|
|
||||||
auto buffered = std::make_unique<common::LruBufferedData>(source, size);
|
auto buffered = std::make_unique<common::LruBufferedData>(source, size);
|
||||||
|
|
||||||
FileTableEntries table_entries;
|
FileTableEntries table_entries;
|
||||||
@@ -651,44 +578,18 @@ Result MountZip(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) {
|
|||||||
DirectoryEntry root;
|
DirectoryEntry root;
|
||||||
Parse(table_entries, root);
|
Parse(table_entries, root);
|
||||||
|
|
||||||
auto entry = std::make_unique<Entry>();
|
if (!common::MountReadOnlyIndexDevice(
|
||||||
entry->path = path;
|
[&buffered, &root](const common::MountConfig& config) {
|
||||||
entry->devoptab = DEVOPTAB;
|
return std::make_unique<Device>(std::move(buffered), root, config);
|
||||||
entry->devoptab.name = entry->name;
|
},
|
||||||
entry->devoptab.deviceData = &entry->device;
|
sizeof(File), sizeof(Dir),
|
||||||
entry->device.source = std::move(buffered);
|
"ZIP", out_path
|
||||||
entry->device.root = root;
|
)) {
|
||||||
std::snprintf(entry->name, sizeof(entry->name), "zip_%zu", index);
|
log_write("[ZIP] Failed to mount %s\n", path.s);
|
||||||
std::snprintf(entry->mount, sizeof(entry->mount), "zip_%zu:/", index);
|
R_THROW(0x1);
|
||||||
|
}
|
||||||
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);
|
|
||||||
|
|
||||||
R_SUCCEED();
|
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
|
} // namespace sphaira::devoptab
|
||||||
|
|||||||
Reference in New Issue
Block a user