fatfs: use devoptab mounting. devoptab: add config for hidding from fs and dump, fix http being writeable.

This commit is contained in:
ITotalJustice
2025-09-07 17:35:37 +01:00
parent ba78fd0dc5
commit 61b398a89a
12 changed files with 332 additions and 292 deletions

View File

@@ -90,7 +90,6 @@ add_executable(sphaira
source/threaded_file_transfer.cpp
source/title_info.cpp
source/minizip_helper.cpp
source/fatfs.cpp
source/usbdvd.cpp
source/utils/utils.cpp
@@ -111,6 +110,7 @@ add_executable(sphaira
source/utils/devoptab_ftp.cpp
source/utils/devoptab_webdav.cpp
source/utils/devoptab_vfs.cpp
source/utils/devoptab_fatfs.cpp
source/usb/base.cpp
source/usb/usbds.cpp

View File

@@ -1,10 +0,0 @@
#pragma once
#include <switch.h>
namespace sphaira::fatfs {
Result MountAll();
void UnmountAll();
} // namespace sphaira::fatfs

View File

@@ -23,12 +23,15 @@ struct StdioEntry {
u32 flags{};
// optional dump path inside the mount point.
std::string dump_path{};
// set to hide for filebrowser.
bool fs_hidden{};
// set to hide in dump list.
bool dump_hidden{};
};
using StdioEntries = std::vector<StdioEntry>;
// set write=true to filter out write protected devices.
auto GetStdio(bool write) -> StdioEntries;
auto GetFat() -> StdioEntries;
} // namespace sphaira::location

View File

@@ -39,6 +39,7 @@ Result MountHttpAll();
Result MountFtpAll();
Result MountNfsAll();
Result MountSmb2All();
Result MountFatfsAll();
Result GetNetworkDevices(location::StdioEntries& out);
void UmountAllNeworkDevices();

View File

@@ -134,6 +134,8 @@ struct MountConfig {
bool read_only{};
bool no_stat_file{true};
bool no_stat_dir{true};
bool fs_hidden{};
bool dump_hidden{};
std::unordered_map<std::string, std::string> extra{};
};
@@ -212,6 +214,9 @@ private:
};
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

View File

@@ -22,7 +22,6 @@
#include "haze_helper.hpp"
#include "web.hpp"
#include "swkbd.hpp"
#include "fatfs.hpp"
#include "usbdvd.hpp"
#include "utils/profile.hpp"
@@ -1579,13 +1578,6 @@ App::App(const char* argv0) {
usbHsFsInitialize(1);
}
{
SCOPED_TIMESTAMP("fat init");
if (R_FAILED(fatfs::MountAll())) {
log_write("[FAT] failed to mount bis\n");
}
}
{
SCOPED_TIMESTAMP("usbdvd init");
if (R_FAILED(usbdvd::MountAll())) {
@@ -1629,6 +1621,11 @@ App::App(const char* argv0) {
devoptab::MountSmb2All();
}
{
SCOPED_TIMESTAMP("fatfs init");
devoptab::MountFatfsAll();
}
{
SCOPED_TIMESTAMP("timestamp init");
// ini_putl(GetExePath(), "timestamp", m_start_timestamp, App::PLAYLOG_PATH);
@@ -2226,11 +2223,6 @@ App::~App() {
usbHsFsExit();
}
{
SCOPED_TIMESTAMP("fatfs exit");
fatfs::UnmountAll();
}
// this has to come before curl exit as it uses curl global.
{
SCOPED_TIMESTAMP("devoptab exit");

View File

@@ -494,11 +494,17 @@ void DumpGetLocation(const std::string& title, u32 location_flags, const OnLocat
ui::PopupList::Items items;
std::vector<DumpEntry> dump_entries;
out.stdio = location::GetStdio(true);
const auto stdio_entries = location::GetStdio(true);
if (location_flags & (1 << DumpLocationType_Stdio)) {
for (s32 i = 0; i < std::size(out.stdio); i++) {
dump_entries.emplace_back(DumpLocationType_Stdio, i);
items.emplace_back(out.stdio[i].name);
for (auto& e : stdio_entries) {
if (e.dump_hidden) {
continue;
}
const auto index = out.stdio.size();
dump_entries.emplace_back(DumpLocationType_Stdio, index);
items.emplace_back(e.name);
out.stdio.emplace_back(e);
}
}

View File

@@ -4,9 +4,7 @@
#include "usbdvd.hpp"
#include "utils/devoptab.hpp"
#include <ff.h>
#include <cstring>
#include <minIni.h>
#include <usbhsfs.h>
namespace sphaira::location {
@@ -17,13 +15,17 @@ namespace {
auto GetStdio(bool write) -> StdioEntries {
StdioEntries out{};
const auto add_from_entries = [](const StdioEntries& entries, StdioEntries& out, bool write) {
for (const auto& e : entries) {
const auto add_from_entries = [](StdioEntries& entries, StdioEntries& out, bool write) {
for (auto& e : entries) {
if (write && (e.flags & FsEntryFlag::FsEntryFlag_ReadOnly)) {
log_write("[STDIO] skipping read only mount: %s\n", e.name.c_str());
continue;
}
if (e.flags & FsEntryFlag::FsEntryFlag_ReadOnly) {
e.name += " (Read Only)";
}
out.emplace_back(e);
}
};
@@ -80,20 +82,4 @@ auto GetStdio(bool write) -> StdioEntries {
return out;
}
auto GetFat() -> StdioEntries {
StdioEntries out{};
for (auto& e : VolumeStr) {
char path[64];
std::snprintf(path, sizeof(path), "%s:/", e);
char display_name[0x100];
std::snprintf(display_name, sizeof(display_name), "%s (Read Only)", path);
out.emplace_back(path, display_name, FsEntryFlag::FsEntryFlag_ReadOnly);
}
return out;
}
} // namespace sphaira::location

View File

@@ -1595,12 +1595,6 @@ void FsView::DisplayOptions() {
SidebarEntryArray::Items mount_items;
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) {
fs_entries.emplace_back(e);
mount_items.push_back(i18n::get(e.name));
@@ -1611,8 +1605,12 @@ void FsView::DisplayOptions() {
mount_items.push_back(m_menu->m_custom_fs_entry.name);
}
const auto fat_entries = location::GetFat();
for (const auto& e: fat_entries) {
const auto stdio_locations = location::GetStdio(false);
for (const auto& e: stdio_locations) {
if (e.fs_hidden) {
continue;
}
fs_entries.emplace_back(e.name, e.mount, FsType::Stdio, e.flags);
mount_items.push_back(e.name);
}

View File

@@ -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{};
SCOPED_MUTEX(&rw_lock_init_mutex);
@@ -809,6 +870,10 @@ Result MountNetworkDevice(const CreateDeviceCallback& create_device, size_t file
e->back().no_stat_file = ini_parse_getbool(Value, e->back().no_stat_file);
} else if (!std::strcmp(Key, "no_stat_dir")) {
e->back().no_stat_dir = ini_parse_getbool(Value, e->back().no_stat_dir);
} else if (!std::strcmp(Key, "fs_hidden")) {
e->back().fs_hidden = ini_parse_getbool(Value, e->back().fs_hidden);
} else if (!std::strcmp(Key, "dump_hidden")) {
e->back().dump_hidden = ini_parse_getbool(Value, e->back().dump_hidden);
} else {
log_write("[DEVOPTAB] INI: extra key %s=%s\n", Key, Value);
e->back().extra.emplace(Key, Value);
@@ -824,55 +889,31 @@ Result MountNetworkDevice(const CreateDeviceCallback& create_device, size_t file
ini_browse(cb, &configs, config_path);
log_write("[DEVOPTAB] Found %zu mount configs\n", configs.size());
for (const auto& config : configs) {
// check if we already have the http mounted.
bool already_mounted = false;
for (const auto& entry : g_entries) {
if (entry && entry->mount == config.name) {
already_mounted = true;
break;
}
}
if (already_mounted) {
log_write("[DEVOPTAB] Already mounted %s, skipping\n", config.name.c_str());
for (auto& config : configs) {
if (config.name.empty()) {
log_write("[DEVOPTAB] Skipping empty name\n");
continue;
}
// otherwise, find next free entry.
auto itr = std::ranges::find_if(g_entries, [](auto& e){
return !e;
});
if (itr == g_entries.end()) {
log_write("[DEVOPTAB] No free entries to mount %s\n", config.name.c_str());
break;
}
auto entry = std::make_unique<Entry>();
entry->device.mount_device = create_device(config);
entry->device.file_size = file_size;
entry->device.dir_size = dir_size;
entry->device.config = config;
if (!entry->device.mount_device) {
log_write("[DEVOPTAB] Failed to create device for %s\n", config.url.c_str());
if (config.url.empty()) {
log_write("[DEVOPTAB] Skipping empty url for %s\n", config.name.c_str());
continue;
}
entry->devoptab = DEVOPTAB;
entry->devoptab.name = entry->name;
entry->devoptab.deviceData = &entry->device;
std::snprintf(entry->name, sizeof(entry->name), "[%s] %s", name, config.name.c_str());
std::snprintf(entry->mount, sizeof(entry->mount), "[%s] %s:/", name, config.name.c_str());
common::update_devoptab_for_read_only(&entry->devoptab, config.read_only);
if (force_read_only) {
config.read_only = true;
}
R_UNLESS(AddDevice(&entry->devoptab) >= 0, 0x1);
log_write("[DEVOPTAB] DEVICE SUCCESS %s %s\n", entry->device.config.url.c_str(), entry->name);
fs::FsPath _name{};
std::snprintf(_name, sizeof(_name), "[%s] %s", name, config.name.c_str());
entry->ref_count++;
*itr = std::move(entry);
log_write("[DEVOPTAB] Mounted %s at /%s\n", config.url.c_str(), config.name.c_str());
fs::FsPath _mount{};
std::snprintf(_mount, sizeof(_mount), "[%s] %s:/", name, config.name.c_str());
if (!MountNetworkDevice2(create_device(config), config, file_size, dir_size, _name, _mount)) {
log_write("[DEVOPTAB] Failed to mount %s\n", config.name.c_str());
continue;
}
}
R_SUCCEED();
@@ -1373,18 +1414,20 @@ Result GetNetworkDevices(location::StdioEntries& out) {
for (const auto& entry : g_entries) {
if (entry) {
const auto& config = entry->device.config;
u32 flags = 0;
if (entry->device.config.read_only) {
if (config.read_only) {
flags |= location::FsEntryFlag::FsEntryFlag_ReadOnly;
}
if (entry->device.config.no_stat_file) {
if (config.no_stat_file) {
flags |= location::FsEntryFlag::FsEntryFlag_NoStatFile;
}
if (entry->device.config.no_stat_dir) {
if (config.no_stat_dir) {
flags |= location::FsEntryFlag::FsEntryFlag_NoStatDir;
}
out.emplace_back(entry->mount, entry->name, flags, entry->device.config.dump_path);
out.emplace_back(entry->mount, entry->name, flags, config.dump_path, config.fs_hidden, config.dump_hidden);
}
}

View File

@@ -1,27 +1,49 @@
#include "utils/devoptab.hpp"
#include "utils/devoptab_common.hpp"
#include "fatfs.hpp"
#include "defines.hpp"
#include "utils/profile.hpp"
#include "log.hpp"
#include "ff.h"
#include "defines.hpp"
#include <array>
#include <algorithm>
#include <span>
#include <fcntl.h>
#include <string>
#include <vector>
#include <memory>
#include <cstring>
#include <cstdio>
#include <cerrno>
#include <sys/iosupport.h>
#include <sys/stat.h>
#include <ff.h>
namespace sphaira::fatfs {
namespace sphaira::devoptab {
namespace {
auto is_archive(BYTE attr) -> bool {
const auto archive_attr = AM_DIR | AM_ARC;
return (attr & archive_attr) == archive_attr;
}
enum BisMountType {
BisMountType_PRODINFOF,
BisMountType_SAFE,
BisMountType_USER,
BisMountType_SYSTEM,
};
struct FatStorageEntry {
FsStorage storage;
std::unique_ptr<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
// in order to be more lcache efficient.
@@ -48,14 +70,51 @@ struct File {
FIL* files;
u32 file_count;
size_t off;
char path[256];
char path[PATH_MAX];
};
struct Dir {
FDIR dir;
char path[256];
char path[PATH_MAX];
};
struct Device final : common::MountDevice {
Device(BisMountType type, const common::MountConfig& _config)
: MountDevice{_config}
, m_type{type} {
}
~Device();
private:
bool fix_path(const char* str, char* out, bool strip_leading_slash = false) override {
std::strcpy(out, str);
return true;
}
bool Mount() override;
int devoptab_open(void *fileStruct, const char *path, int flags, int mode) override;
int devoptab_close(void *fd) override;
ssize_t devoptab_read(void *fd, char *ptr, size_t len) override;
off_t devoptab_seek(void *fd, off_t pos, int dir) override;
int devoptab_fstat(void *fd, struct stat *st) override;
int devoptab_diropen(void* fd, const char *path) override;
int devoptab_dirreset(void* fd) override;
int devoptab_dirnext(void* fd, char *filename, struct stat *filestat) override;
int devoptab_dirclose(void* fd) override;
int devoptab_lstat(const char *path, struct stat *st) override;
private:
const BisMountType m_type;
bool mounted{};
};
auto is_archive(BYTE attr) -> bool {
const auto archive_attr = AM_DIR | AM_ARC;
return (attr & archive_attr) == archive_attr;
}
u64 get_size_from_files(const File* file) {
u64 size = 0;
for (u32 i = 0; i < file->file_count; i++) {
@@ -90,38 +149,8 @@ void set_current_file_pos(File* file) {
}
}
enum BisMountType {
BisMountType_PRODINFOF,
BisMountType_SAFE,
BisMountType_USER,
BisMountType_SYSTEM,
};
struct FatStorageEntry {
FsStorage storage;
std::unique_ptr<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) {
memset(st, 0, sizeof(*st));
std::memset(st, 0, sizeof(*st));
st->st_nlink = 1;
@@ -133,7 +162,7 @@ void fill_stat(const char* path, const FILINFO* fno, struct stat *st) {
tm.tm_mon = ((fno->fdate >> 5) & 0xF) - 1;
tm.tm_year = (fno->fdate >> 9) + 80;
st->st_atime = mktime(&tm);
st->st_atime = std::mktime(&tm);
st->st_mtime = st->st_atime;
st->st_ctime = st->st_atime;
@@ -143,7 +172,7 @@ void fill_stat(const char* path, const FILINFO* fno, struct stat *st) {
char file_path[256];
for (u16 i = 0; i < 256; i++) {
std::snprintf(file_path, sizeof(file_path), "%s/%02u", path, i);
FILINFO file_info;
FILINFO file_info{};
if (FR_OK != f_stat(file_path, &file_info)) {
break;
}
@@ -152,8 +181,7 @@ void fill_stat(const char* path, const FILINFO* fno, struct stat *st) {
}
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
} else
if (fno->fattrib & AM_DIR) {
} else if (fno->fattrib & AM_DIR) {
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
} else {
st->st_size = fno->fsize;
@@ -161,31 +189,80 @@ void fill_stat(const char* path, const FILINFO* fno, struct stat *st) {
}
}
static int set_errno(struct _reent *r, int err) {
r->_errno = err;
return -1;
Device::~Device() {
if (mounted) {
auto& fat = g_fat_storage[m_type];
f_unmount(BIS_MOUNT_ENTRIES[m_type].mount_name);
fat.buffered.reset();
fsStorageClose(&fat.storage);
}
}
int fat_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode) {
bool Device::Mount() {
if (mounted) {
return true;
}
auto& fat = g_fat_storage[m_type];
if (!serviceIsActive(&fat.storage.s)) {
const auto res = fsOpenBisStorage(&fat.storage, BIS_MOUNT_ENTRIES[m_type].id);
if (R_FAILED(res)) {
log_write("[FATFS] fsOpenBisStorage(%d) failed: 0x%x\n", BIS_MOUNT_ENTRIES[m_type].id, res);
return false;
}
} else {
log_write("[FATFS] Storage for %s already opened\n", BIS_MOUNT_ENTRIES[m_type].mount_name);
}
if (!fat.buffered) {
auto source = std::make_shared<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);
std::memset(file, 0, sizeof(*file));
// todo: init array
// todo: handle dir.
FIL fil{};
if (FR_OK == f_open(&fil, path, FA_READ)) {
file->file_count = 1;
file->files = (FIL*)std::malloc(sizeof(*file->files));
if (!file->files) {
return -ENOMEM;
}
file->file_count = 1;
std::memcpy(file->files, &fil, sizeof(*file->files));
// todo: check what error code is returned here.
} else {
FILINFO info{};
if (FR_OK != f_stat(path, &info)) {
return set_errno(r, ENOENT);
return -ENOENT;
}
if (!(info.fattrib & AM_ARC)) {
return set_errno(r, ENOENT);
return -ENOENT;
}
char file_path[256];
@@ -198,33 +275,35 @@ int fat_open(struct _reent *r, void *fileStruct, const char *path, int flags, in
}
file->files = (FIL*)std::realloc(file->files, (i + 1) * sizeof(*file->files));
if (!file->files) {
return -ENOMEM;
}
std::memcpy(&file->files[i], &fil, sizeof(fil));
file->file_count++;
}
}
if (!file->files) {
return set_errno(r, ENOENT);
return -ENOENT;
}
std::snprintf(file->path, sizeof(file->path), "%s", path);
return r->_errno = 0;
return 0;
}
int fat_close(struct _reent *r, void *fd) {
int Device::devoptab_close(void *fd) {
auto file = static_cast<File*>(fd);
if (file->files) {
for (u32 i = 0; i < file->file_count; i++) {
f_close(&file->files[i]);
}
free(file->files);
std::free(file->files);
return 0;
}
return r->_errno = 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);
UINT total_bytes_read = 0;
@@ -233,11 +312,11 @@ ssize_t fat_read(struct _reent *r, void *fd, char *ptr, size_t len) {
auto fil = get_current_file(file);
if (!fil) {
log_write("[FATFS] failed to get fil\n");
return set_errno(r, ENOENT);
return -EIO;
}
if (FR_OK != f_read(fil, ptr, len, &bytes_read)) {
return set_errno(r, ENOENT);
return -EIO;
}
if (!bytes_read) {
@@ -252,7 +331,7 @@ ssize_t fat_read(struct _reent *r, void *fd, char *ptr, size_t len) {
return total_bytes_read;
}
off_t fat_seek(struct _reent *r, void *fd, off_t pos, int dir) {
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
const auto size = get_size_from_files(file);
@@ -265,11 +344,10 @@ off_t fat_seek(struct _reent *r, void *fd, off_t pos, int dir) {
file->off = std::clamp<u64>(pos, 0, size);
set_current_file_pos(file);
r->_errno = 0;
return file->off;
}
int fat_fstat(struct _reent *r, void *fd, struct stat *st) {
int Device::devoptab_fstat(void *fd, struct stat *st) {
auto file = static_cast<File*>(fd);
/* Only fill the attr and size field, leaving the timestamp blank. */
@@ -279,173 +357,110 @@ int fat_fstat(struct _reent *r, void *fd, struct stat *st) {
/* Fill stat info. */
fill_stat(nullptr, &info, st);
return r->_errno = 0;
return 0;
}
DIR_ITER* fat_diropen(struct _reent *r, DIR_ITER *dirState, const char *path) {
auto dir = static_cast<Dir*>(dirState->dirStruct);
std::memset(dir, 0, sizeof(*dir));
int Device::devoptab_diropen(void* fd, const char *path) {
auto dir = static_cast<Dir*>(fd);
log_write("[FATFS] diropen: %s\n", path);
if (FR_OK != f_opendir(&dir->dir, path)) {
set_errno(r, ENOENT);
return NULL;
log_write("[FATFS] f_opendir(%s) failed\n", path);
return -ENOENT;
}
r->_errno = 0;
return dirState;
log_write("[FATFS] Opened dir: %s\n", path);
return 0;
}
int fat_dirreset(struct _reent *r, DIR_ITER *dirState) {
auto dir = static_cast<Dir*>(dirState->dirStruct);
int Device::devoptab_dirreset(void* fd) {
auto dir = static_cast<Dir*>(fd);
if (FR_OK != f_rewinddir(&dir->dir)) {
return set_errno(r, ENOENT);
return -EIO;
}
return r->_errno = 0;
return 0;
}
int fat_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) {
auto dir = static_cast<Dir*>(dirState->dirStruct);
int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) {
auto dir = static_cast<Dir*>(fd);
FILINFO fno{};
if (FR_OK != f_readdir(&dir->dir, &fno)) {
return set_errno(r, ENOENT);
return -EIO;
}
if (!fno.fname[0]) {
return set_errno(r, ENOENT);
return -EIO;
}
strcpy(filename, fno.fname);
std::strcpy(filename, fno.fname);
fill_stat(dir->path, &fno, filestat);
return r->_errno = 0;
return 0;
}
int fat_dirclose(struct _reent *r, DIR_ITER *dirState) {
auto dir = static_cast<Dir*>(dirState->dirStruct);
int Device::devoptab_dirclose(void* fd) {
auto dir = static_cast<Dir*>(fd);
if (FR_OK != f_closedir(&dir->dir)) {
return set_errno(r, ENOENT);
return -EIO;
}
return r->_errno = 0;
return 0;
}
int fat_statvfs(struct _reent *r, const char *path, struct statvfs *buf) {
memset(buf, 0, sizeof(*buf));
// todo: find out how to calculate free size in read only.
const auto fat = (FatStorageEntry*)r->deviceData;
buf->f_bsize = FF_MAX_SS;
buf->f_frsize = FF_MAX_SS;
buf->f_blocks = ((fat->fs.n_fatent - 2) * (DWORD)fat->fs.csize);
buf->f_namemax = FF_LFN_BUF;
return r->_errno = 0;
}
int fat_lstat(struct _reent *r, const char *file, struct stat *st) {
int Device::devoptab_lstat(const char *path, struct stat *st) {
FILINFO fno;
if (FR_OK != f_stat(file, &fno)) {
return set_errno(r, ENOENT);
if (FR_OK != f_stat(path, &fno)) {
return -ENOENT;
}
fill_stat(file, &fno, st);
return r->_errno = 0;
fill_stat(path, &fno, st);
return 0;
}
constexpr devoptab_t DEVOPTAB = {
.structSize = sizeof(File),
.open_r = fat_open,
.close_r = fat_close,
.read_r = fat_read,
.seek_r = fat_seek,
.fstat_r = fat_fstat,
.stat_r = fat_lstat,
.dirStateSize = sizeof(Dir),
.diropen_r = fat_diropen,
.dirreset_r = fat_dirreset,
.dirnext_r = fat_dirnext,
.dirclose_r = fat_dirclose,
.statvfs_r = fat_statvfs,
.lstat_r = fat_lstat,
};
Mutex g_mutex{};
bool g_is_init{};
} // namespace
Result MountAll() {
SCOPED_MUTEX(&g_mutex);
Result MountFatfsAll() {
for (u32 i = 0; i < FF_VOLUMES; i++) {
const auto& bis = BIS_MOUNT_ENTRIES[i];
common::MountConfig config{};
config.url = "fatfs dummy url";
config.read_only = true;
config.dump_hidden = true;
if (!common::MountNetworkDevice2(
std::make_unique<Device>((BisMountType)i, config),
config,
sizeof(File), sizeof(Dir),
bis.volume_name, bis.mount_name
)) {
log_write("[FATFS] Failed to mount %s\n", bis.volume_name);
}
}
if (g_is_init) {
R_SUCCEED();
}
for (u32 i = 0; i < FF_VOLUMES; i++) {
auto& fat = g_fat_storage[i];
const auto& bis = BIS_MOUNT_ENTRIES[i];
// log_write("[FAT] %s\n", bis.volume_name);
fat.devoptab = DEVOPTAB;
fat.devoptab.name = bis.volume_name;
fat.devoptab.deviceData = &fat;
R_TRY(fsOpenBisStorage(&fat.storage, bis.id));
auto source = std::make_shared<FsStorageSource>(&fat.storage);
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();
}
void UnmountAll() {
SCOPED_MUTEX(&g_mutex);
if (!g_is_init) {
return;
}
for (u32 i = 0; i < FF_VOLUMES; i++) {
auto& fat = g_fat_storage[i];
const auto& bis = BIS_MOUNT_ENTRIES[i];
RemoveDevice(bis.mount_name);
f_unmount(bis.mount_name);
fsStorageClose(&fat.storage);
}
}
} // namespace sphaira::fatfs
} // namespace sphaira::devoptab
extern "C" {
const char* VolumeStr[] {
sphaira::fatfs::BIS_MOUNT_ENTRIES[0].volume_name,
sphaira::fatfs::BIS_MOUNT_ENTRIES[1].volume_name,
sphaira::fatfs::BIS_MOUNT_ENTRIES[2].volume_name,
sphaira::fatfs::BIS_MOUNT_ENTRIES[3].volume_name,
sphaira::devoptab::BIS_MOUNT_ENTRIES[0].volume_name,
sphaira::devoptab::BIS_MOUNT_ENTRIES[1].volume_name,
sphaira::devoptab::BIS_MOUNT_ENTRIES[2].volume_name,
sphaira::devoptab::BIS_MOUNT_ENTRIES[3].volume_name,
};
Result fatfs_read(u8 num, void* dst, u64 offset, u64 size) {
// log_write("[FAT] num: %u\n", num);
auto& fat = sphaira::fatfs::g_fat_storage[num];
log_write("[FAT] read: %s, off: 0x%lx, size: 0x%lx\n", VolumeStr[num], offset, size);
auto& fat = sphaira::devoptab::g_fat_storage[num];
log_write("[FAT] buffered: %p\n", fat.buffered.get());
return fat.buffered->Read2(dst, offset, size);
}

View File

@@ -410,7 +410,8 @@ Result MountHttpAll() {
return std::make_unique<Device>(config);
},
sizeof(File), sizeof(Dir),
"HTTP"
"HTTP",
true
);
}