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