devoptab: add mounts (wrapper around all mounts, exposed via MTP/FTP). lots of fixes (see below).
- updated libhaze to 81154c1. - increase ftpsrv stack size as it would crash when modifying custom mounts. - fix warning for unused log data in haze. - fix eof read for nsp/xci source by instead returning 0 for bytes read, rather than error. - add support for lstat the root of a mount. - handle zero size reads when reading games via devoptab.
This commit is contained in:
@@ -133,6 +133,7 @@ add_executable(sphaira
|
||||
source/utils/devoptab_vfs.cpp
|
||||
source/utils/devoptab_fatfs.cpp
|
||||
source/utils/devoptab_game.cpp
|
||||
source/utils/devoptab_mounts.cpp
|
||||
source/utils/devoptab.cpp
|
||||
|
||||
source/usb/base.cpp
|
||||
@@ -399,7 +400,7 @@ endif()
|
||||
if (ENABLE_LIBHAZE)
|
||||
FetchContent_Declare(libhaze
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/libhaze.git
|
||||
GIT_TAG 6e24502
|
||||
GIT_TAG 81154c1
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(libhaze)
|
||||
|
||||
@@ -882,9 +882,52 @@ private:
|
||||
Mutex* const m_mutex;
|
||||
};
|
||||
|
||||
struct ScopedRMutex {
|
||||
ScopedRMutex(RMutex* _mutex) : mutex{_mutex} {
|
||||
rmutexLock(mutex);
|
||||
}
|
||||
|
||||
~ScopedRMutex() {
|
||||
rmutexUnlock(mutex);
|
||||
}
|
||||
|
||||
ScopedRMutex(const ScopedRMutex&) = delete;
|
||||
void operator=(const ScopedRMutex&) = delete;
|
||||
|
||||
private:
|
||||
RMutex* const mutex;
|
||||
};
|
||||
|
||||
struct ScopedRwLock {
|
||||
ScopedRwLock(RwLock* _lock, bool _write) : lock{_lock}, write{_write} {
|
||||
if (write) {
|
||||
rwlockWriteLock(lock);
|
||||
} else {
|
||||
rwlockReadLock(lock);
|
||||
}
|
||||
}
|
||||
|
||||
~ScopedRwLock() {
|
||||
if (write) {
|
||||
rwlockWriteUnlock(lock);
|
||||
} else {
|
||||
rwlockReadUnlock(lock);
|
||||
}
|
||||
}
|
||||
|
||||
ScopedRwLock(const ScopedRwLock&) = delete;
|
||||
void operator=(const ScopedRwLock&) = delete;
|
||||
|
||||
private:
|
||||
RwLock* const lock;
|
||||
bool const write;
|
||||
};
|
||||
|
||||
// #define ON_SCOPE_EXIT(_f) std::experimental::scope_exit ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){[&] { _f; }};
|
||||
#define ON_SCOPE_EXIT(_f) ScopeGuard ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){[&] { _f; }};
|
||||
#define SCOPED_MUTEX(_m) ScopedMutex ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){_m}
|
||||
#define SCOPED_RMUTEX(_m) ScopedRMutex ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){_m}
|
||||
#define SCOPED_RWLOCK(_m, _write) ScopedRwLock ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){_m, _write}
|
||||
|
||||
// #define ON_SCOPE_FAIL(_f) std::experimental::scope_exit ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){[&] { if (R_FAILED(rc)) { _f; } }};
|
||||
// #define ON_SCOPE_SUCCESS(_f) std::experimental::scope_exit ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){[&] { if (R_SUCCEEDED(rc)) { _f; } }};
|
||||
|
||||
@@ -28,6 +28,7 @@ Result MountNfsAll();
|
||||
Result MountSmb2All();
|
||||
Result MountFatfsAll();
|
||||
Result MountGameAll();
|
||||
Result MountInternalMounts();
|
||||
|
||||
Result GetNetworkDevices(location::StdioEntries& out);
|
||||
void UmountAllNeworkDevices();
|
||||
|
||||
@@ -1731,6 +1731,11 @@ App::App(const char* argv0) {
|
||||
devoptab::MountFatfsAll();
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TIMESTAMP("mounts init");
|
||||
devoptab::MountInternalMounts();
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TIMESTAMP("timestamp init");
|
||||
// ini_putl(GetExePath(), "timestamp", m_start_timestamp, App::PLAYLOG_PATH);
|
||||
|
||||
@@ -39,11 +39,11 @@ Mutex g_mutex{};
|
||||
|
||||
void ftp_log_callback(enum FTP_API_LOG_TYPE type, const char* msg) {
|
||||
log_write("[FTPSRV] %s\n", msg);
|
||||
sphaira::App::NotifyFlashLed();
|
||||
App::NotifyFlashLed();
|
||||
}
|
||||
|
||||
void ftp_progress_callback(void) {
|
||||
sphaira::App::NotifyFlashLed();
|
||||
App::NotifyFlashLed();
|
||||
}
|
||||
|
||||
InstallSharedData g_shared_data{};
|
||||
@@ -389,6 +389,17 @@ const char* vfs_stdio_readdir(void* user, void* user_entry) {
|
||||
}
|
||||
|
||||
int vfs_stdio_dirlstat(void* user, const void* user_entry, const char* _path, struct stat* st) {
|
||||
// could probably be optimised to th below, but we won't know its r/w perms.
|
||||
#if 0
|
||||
auto entry = static_cast<FtpVfsDirEntry*>(user_entry);
|
||||
if (entry->buf->d_type == DT_DIR) {
|
||||
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
||||
st->st_nlink = 1;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#endif
|
||||
|
||||
const auto path = vfs_stdio_fix_path(_path);
|
||||
return lstat(path, st);
|
||||
}
|
||||
@@ -512,6 +523,11 @@ void loop(void* arg) {
|
||||
.user = NULL,
|
||||
.func = &g_vfs_stdio,
|
||||
},
|
||||
{
|
||||
.name = "mounts",
|
||||
.user = NULL,
|
||||
.func = &g_vfs_stdio,
|
||||
},
|
||||
{
|
||||
.name = "install",
|
||||
.user = NULL,
|
||||
@@ -559,7 +575,7 @@ bool Init() {
|
||||
// or load everything in the init thread.
|
||||
|
||||
Result rc;
|
||||
if (R_FAILED(rc = utils::CreateThread(&g_thread, loop, nullptr, 1024*16))) {
|
||||
if (R_FAILED(rc = utils::CreateThread(&g_thread, loop, nullptr))) {
|
||||
log_write("[FTP] failed to create nxlink thread: 0x%X\n", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -65,11 +65,6 @@ struct FsProxyBase : haze::FileSystemProxyImpl {
|
||||
fs::FsPath buf;
|
||||
const auto len = std::strlen(GetName());
|
||||
|
||||
// if (!base || !base[0]) {
|
||||
// std::strcpy(buf, path);
|
||||
// return buf;
|
||||
// }
|
||||
|
||||
if (len && !strncasecmp(path, GetName(), len)) {
|
||||
std::snprintf(buf, sizeof(buf), "%s/%s", base, path + len);
|
||||
} else {
|
||||
@@ -636,9 +631,9 @@ struct FsInstallProxy final : FsProxyVfs {
|
||||
haze::FsEntries g_fs_entries{};
|
||||
|
||||
void haze_callback(const haze::CallbackData *data) {
|
||||
#if 0
|
||||
auto& e = *data;
|
||||
|
||||
#if 0
|
||||
switch (e.type) {
|
||||
case haze::CallbackType_OpenSession: log_write("[LIBHAZE] Opening Session\n"); break;
|
||||
case haze::CallbackType_CloseSession: log_write("[LIBHAZE] Closing Session\n"); break;
|
||||
@@ -677,6 +672,7 @@ bool Init() {
|
||||
g_fs_entries.emplace_back(std::make_shared<FsProxy>(std::make_unique<fs::FsNativeSd>(), "", "microSD card"));
|
||||
g_fs_entries.emplace_back(std::make_shared<FsProxy>(std::make_unique<fs::FsNativeImage>(FsImageDirectoryId_Sd), "Album", "Album (Image SD)"));
|
||||
g_fs_entries.emplace_back(std::make_shared<FsProxy>(std::make_unique<fs::FsStdio>(true, "games:/"), "Games", "Games"));
|
||||
g_fs_entries.emplace_back(std::make_shared<FsProxy>(std::make_unique<fs::FsStdio>(true, "mounts:/"), "Mounts", "Mounts"));
|
||||
g_fs_entries.emplace_back(std::make_shared<FsDevNullProxy>("DevNull", "DevNull (Speed Test)"));
|
||||
g_fs_entries.emplace_back(std::make_shared<FsInstallProxy>("install", "Install (NSP, XCI, NSZ, XCZ)"));
|
||||
|
||||
|
||||
@@ -234,6 +234,12 @@ Result CreateSave(u64 app_id, AccountUid uid) {
|
||||
} // namespace
|
||||
|
||||
Result NspEntry::Read(void* buf, s64 off, s64 size, u64* bytes_read) {
|
||||
if (off == nsp_size) {
|
||||
log_write("[NspEntry::Read] read at eof...\n");
|
||||
*bytes_read = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
if (off < nsp_data.size()) {
|
||||
*bytes_read = size = ClipSize(off, size, nsp_data.size());
|
||||
std::memcpy(buf, nsp_data.data() + off, size);
|
||||
|
||||
@@ -235,6 +235,12 @@ struct XciSource final : dump::BaseSource {
|
||||
int icon{};
|
||||
|
||||
Result Read(const std::string& path, void* buf, s64 off, s64 size, u64* bytes_read) override {
|
||||
if (off == xci_size) {
|
||||
log_write("[XciSource::Read] read at eof...\n");
|
||||
*bytes_read = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
if (path.ends_with(GetDumpTypeStr(DumpFileType_XCI)) || path.ends_with(GetDumpTypeStr(DumpFileType_XCZ))) {
|
||||
size = ClipSize(off, size, xci_size);
|
||||
*bytes_read = size;
|
||||
|
||||
@@ -47,30 +47,6 @@ const char* curl_url_strerror_wrap(CURLUcode code) {
|
||||
}
|
||||
}
|
||||
|
||||
struct ScopedRwLock {
|
||||
ScopedRwLock(RwLock* _lock, bool _write) : lock{_lock}, write{_write} {
|
||||
if (write) {
|
||||
rwlockWriteLock(lock);
|
||||
} else {
|
||||
rwlockReadLock(lock);
|
||||
}
|
||||
}
|
||||
|
||||
~ScopedRwLock() {
|
||||
if (write) {
|
||||
rwlockWriteUnlock(lock);
|
||||
} else {
|
||||
rwlockReadUnlock(lock);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
RwLock* const lock;
|
||||
bool const write;
|
||||
};
|
||||
|
||||
#define SCOPED_RWLOCK(_m, _write) ScopedRwLock ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){_m, _write}
|
||||
|
||||
struct Device {
|
||||
std::unique_ptr<MountDevice> mount_device;
|
||||
size_t file_size;
|
||||
@@ -408,6 +384,14 @@ int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) {
|
||||
SCOPED_RWLOCK(&g_rwlock, false);
|
||||
SCOPED_MUTEX(&device->mutex);
|
||||
|
||||
// special case: root of the device.
|
||||
const auto dilem = std::strchr(_path, ':');
|
||||
if (dilem && (dilem > _path) && (dilem[1] == '\0' || (dilem[1] == '/' && dilem[2] == '\0'))) {
|
||||
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
||||
st->st_nlink = 1;
|
||||
return r->_errno = 0;
|
||||
}
|
||||
|
||||
char path[PATH_MAX]{};
|
||||
if (!device->mount_device->fix_path(_path, path)) {
|
||||
return set_errno(r, ENOENT);
|
||||
@@ -494,7 +478,7 @@ int devoptab_utimes(struct _reent *r, const char *_path, const struct timeval ti
|
||||
SCOPED_MUTEX(&device->mutex);
|
||||
|
||||
if (!times) {
|
||||
log_write("[NFS] devoptab_utimes() times is null\n");
|
||||
log_write("[DEVOPTAB] devoptab_utimes() times is null\n");
|
||||
return set_errno(r, EINVAL);
|
||||
}
|
||||
|
||||
|
||||
@@ -299,9 +299,13 @@ ssize_t Device::devoptab_read(void *fd, char *ptr, size_t len) {
|
||||
|
||||
const auto& nsp = file->nsp;
|
||||
len = std::min<u64>(len, nsp->nsp_size - file->off);
|
||||
if (!len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 bytes_read;
|
||||
if (R_FAILED(nsp->Read(ptr, file->off, len, &bytes_read))) {
|
||||
log_write("[GAME] failed to read from nsp %s off: %zu len: %zu size: %zu\n", nsp->path.s, file->off, len, nsp->nsp_size);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@@ -398,28 +402,32 @@ int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) {
|
||||
filestat->st_nlink = 1;
|
||||
filestat->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
||||
std::strcpy(filename, entry.name.c_str());
|
||||
dir->index++;
|
||||
} else {
|
||||
auto& entry = dir->entry;
|
||||
if (dir->index >= entry->contents.size()) {
|
||||
log_write("[GAME] dirnext: no more entries\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
const auto& content = entry->contents[dir->index];
|
||||
if (!content.nsp) {
|
||||
if (!FindNspFromEntry(*entry, content.status.application_id)) {
|
||||
log_write("[GAME] failed to find nsp for content id: %016lx\n", content.status.application_id);
|
||||
do {
|
||||
if (dir->index >= entry->contents.size()) {
|
||||
log_write("[GAME] dirnext: no more entries\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
filestat->st_nlink = 1;
|
||||
filestat->st_size = content.nsp->nsp_size;
|
||||
filestat->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
||||
std::snprintf(filename, NAME_MAX, "%s", content.nsp->path.s);
|
||||
const auto& content = entry->contents[dir->index];
|
||||
if (!content.nsp) {
|
||||
if (!FindNspFromEntry(*entry, content.status.application_id)) {
|
||||
log_write("[GAME] failed to find nsp for content id: %016lx\n", content.status.application_id);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
filestat->st_nlink = 1;
|
||||
filestat->st_size = content.nsp->nsp_size;
|
||||
filestat->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
||||
std::snprintf(filename, NAME_MAX, "%s", content.nsp->path.s);
|
||||
dir->index++;
|
||||
break;
|
||||
} while (dir->index++);
|
||||
}
|
||||
|
||||
dir->index++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
311
sphaira/source/utils/devoptab_mounts.cpp
Normal file
311
sphaira/source/utils/devoptab_mounts.cpp
Normal file
@@ -0,0 +1,311 @@
|
||||
|
||||
#include "utils/devoptab.hpp"
|
||||
#include "utils/devoptab_common.hpp"
|
||||
#include "defines.hpp"
|
||||
#include "log.hpp"
|
||||
#include "location.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
|
||||
namespace sphaira::devoptab {
|
||||
namespace {
|
||||
|
||||
struct File {
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct Dir {
|
||||
DIR* dir;
|
||||
location::StdioEntries* entries;
|
||||
u32 index;
|
||||
};
|
||||
|
||||
struct Device final : common::MountDevice {
|
||||
Device(const common::MountConfig& _config)
|
||||
: MountDevice{_config} {
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
ssize_t devoptab_seek(void *fd, off_t pos, int dir) override;
|
||||
int devoptab_fstat(void *fd, struct stat *st) override;
|
||||
int devoptab_unlink(const char *path) override;
|
||||
int devoptab_rename(const char *oldName, const char *newName) override;
|
||||
int devoptab_mkdir(const char *path, int mode) override;
|
||||
int devoptab_rmdir(const char *path) 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;
|
||||
int devoptab_ftruncate(void *fd, off_t len) override;
|
||||
int devoptab_statvfs(const char *path, struct statvfs *buf) override;
|
||||
int devoptab_fsync(void *fd) override;
|
||||
int devoptab_utimes(const char *path, const struct timeval times[2]) override;
|
||||
};
|
||||
|
||||
// converts "/[SMB] pi:/folder/file.txt" to "pi:"
|
||||
auto FixPath(const char* path) -> std::pair<fs::FsPath, std::string_view> {
|
||||
while (*path == '/') {
|
||||
path++;
|
||||
}
|
||||
|
||||
std::string_view mount_name = path;
|
||||
const auto dilem = mount_name.find_first_of(':');
|
||||
if (dilem == std::string_view::npos) {
|
||||
return {path, {}};
|
||||
}
|
||||
mount_name = mount_name.substr(0, dilem + 1);
|
||||
|
||||
fs::FsPath fixed_path = path;
|
||||
if (fixed_path.ends_with(":")) {
|
||||
fixed_path += '/';
|
||||
}
|
||||
|
||||
log_write("[MOUNTS] FixPath: %s -> %s, mount: %.*s\n", path, fixed_path.s, (int)mount_name.size(), mount_name.data());
|
||||
return {fixed_path, mount_name};
|
||||
}
|
||||
|
||||
int Device::devoptab_open(void *fileStruct, const char *_path, int flags, int mode) {
|
||||
auto file = static_cast<File*>(fileStruct);
|
||||
|
||||
const auto [path, mount_name] = FixPath(_path);
|
||||
if (mount_name.empty()) {
|
||||
log_write("[MOUNTS] devoptab_open: invalid path: %s\n", _path);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
file->fd = open(path, flags, mode);
|
||||
if (file->fd < 0) {
|
||||
log_write("[MOUNTS] devoptab_open: failed to open %s: %s\n", path.s, std::strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Device::devoptab_close(void *fd) {
|
||||
auto file = static_cast<File*>(fd);
|
||||
std::memset(file, 0, sizeof(*file));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t Device::devoptab_read(void *fd, char *ptr, size_t len) {
|
||||
auto file = static_cast<File*>(fd);
|
||||
return read(file->fd, ptr, len);
|
||||
}
|
||||
|
||||
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
|
||||
auto file = static_cast<File*>(fd);
|
||||
return lseek(file->fd, pos, dir);
|
||||
}
|
||||
|
||||
int Device::devoptab_fstat(void *fd, struct stat *st) {
|
||||
auto file = static_cast<File*>(fd);
|
||||
return fstat(file->fd, st);
|
||||
}
|
||||
|
||||
int Device::devoptab_unlink(const char *_path) {
|
||||
const auto [path, mount_name] = FixPath(_path);
|
||||
if (mount_name.empty()) {
|
||||
log_write("[MOUNTS] devoptab_unlink: invalid path: %s\n", _path);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return unlink(path);
|
||||
}
|
||||
|
||||
int Device::devoptab_rename(const char *_oldName, const char *_newName) {
|
||||
const auto [oldName, old_mount_name] = FixPath(_oldName);
|
||||
const auto [newName, new_mount_name] = FixPath(_newName);
|
||||
if (old_mount_name.empty() || new_mount_name.empty() || old_mount_name != new_mount_name) {
|
||||
log_write("[MOUNTS] devoptab_rename: invalid path: %s or %s\n", _oldName, _newName);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return rename(oldName, newName);
|
||||
}
|
||||
|
||||
int Device::devoptab_mkdir(const char *_path, int mode) {
|
||||
const auto [path, mount_name] = FixPath(_path);
|
||||
if (mount_name.empty()) {
|
||||
log_write("[MOUNTS] devoptab_mkdir: invalid path: %s\n", _path);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return mkdir(path, mode);
|
||||
}
|
||||
|
||||
int Device::devoptab_rmdir(const char *_path) {
|
||||
const auto [path, mount_name] = FixPath(_path);
|
||||
if (mount_name.empty()) {
|
||||
log_write("[MOUNTS] devoptab_rmdir: invalid path: %s\n", _path);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return rmdir(path);
|
||||
}
|
||||
|
||||
int Device::devoptab_diropen(void* fd, const char *_path) {
|
||||
auto dir = static_cast<Dir*>(fd);
|
||||
const auto [path, mount_name] = FixPath(_path);
|
||||
|
||||
if (mount_name.empty()) {
|
||||
dir->entries = new location::StdioEntries();
|
||||
const auto entries = location::GetStdio(false);
|
||||
|
||||
for (auto& entry : entries) {
|
||||
if (entry.fs_hidden) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dir->entries->emplace_back(std::move(entry));
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
dir->dir = opendir(path);
|
||||
if (!dir->dir) {
|
||||
log_write("[MOUNTS] devoptab_diropen: failed to open dir %s: %s\n", path.s, std::strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int Device::devoptab_dirreset(void* fd) {
|
||||
auto dir = static_cast<Dir*>(fd);
|
||||
|
||||
if (dir->dir) {
|
||||
rewinddir(dir->dir);
|
||||
} else {
|
||||
dir->index = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) {
|
||||
log_write("[MOUNTS] devoptab_dirnext\n");
|
||||
auto dir = static_cast<Dir*>(fd);
|
||||
|
||||
if (dir->dir) {
|
||||
const auto entry = readdir(dir->dir);
|
||||
if (!entry) {
|
||||
log_write("[MOUNTS] devoptab_dirnext: no more entries\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
// todo: verify this.
|
||||
filestat->st_nlink = 1;
|
||||
filestat->st_mode = entry->d_type == DT_DIR ? S_IFDIR : S_IFREG;
|
||||
std::snprintf(filename, NAME_MAX, "%s", entry->d_name);
|
||||
} else {
|
||||
if (dir->index >= dir->entries->size()) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
const auto& entry = (*dir->entries)[dir->index];
|
||||
filestat->st_nlink = 1;
|
||||
filestat->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
||||
if (entry.mount.ends_with(":/")) {
|
||||
std::snprintf(filename, NAME_MAX, "%s", entry.mount.substr(0, entry.mount.size() - 1).c_str());
|
||||
} else {
|
||||
std::snprintf(filename, NAME_MAX, "%s", entry.mount.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
dir->index++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Device::devoptab_dirclose(void* fd) {
|
||||
auto dir = static_cast<Dir*>(fd);
|
||||
|
||||
if (dir->dir) {
|
||||
closedir(dir->dir);
|
||||
} else if (dir->entries) {
|
||||
delete dir->entries;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Device::devoptab_lstat(const char *_path, struct stat *st) {
|
||||
const auto [path, mount_name] = FixPath(_path);
|
||||
if (mount_name.empty()) {
|
||||
st->st_nlink = 1;
|
||||
st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
|
||||
} else {
|
||||
return lstat(path, st);
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int Device::devoptab_ftruncate(void *fd, off_t len) {
|
||||
auto file = static_cast<File*>(fd);
|
||||
return ftruncate(file->fd, len);
|
||||
}
|
||||
|
||||
int Device::devoptab_statvfs(const char *_path, struct statvfs *buf) {
|
||||
const auto [path, mount_name] = FixPath(_path);
|
||||
if (mount_name.empty()) {
|
||||
log_write("[MOUNTS] devoptab_statvfs: invalid path: %s\n", _path);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return statvfs(path, buf);
|
||||
}
|
||||
|
||||
int Device::devoptab_fsync(void *fd) {
|
||||
auto file = static_cast<File*>(fd);
|
||||
return fsync(file->fd);
|
||||
}
|
||||
|
||||
int Device::devoptab_utimes(const char *_path, const struct timeval times[2]) {
|
||||
const auto [path, mount_name] = FixPath(_path);
|
||||
if (mount_name.empty()) {
|
||||
log_write("[MOUNTS] devoptab_utimes: invalid path: %s\n", _path);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return utimes(path, times);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Result MountInternalMounts() {
|
||||
common::MountConfig config{};
|
||||
config.fs_hidden = true;
|
||||
config.dump_hidden = true;
|
||||
|
||||
if (!common::MountNetworkDevice2(
|
||||
std::make_unique<Device>(config),
|
||||
config,
|
||||
sizeof(File), sizeof(Dir),
|
||||
"mounts", "mounts:/"
|
||||
)) {
|
||||
log_write("[MOUNTS] Failed to mount\n");
|
||||
R_THROW(0x1);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace sphaira::devoptab
|
||||
Reference in New Issue
Block a user