Virtual Filesystem (#597)
* Add VfsFile and VfsDirectory classes * Finish abstract Vfs classes * Implement RealVfsFile (computer fs backend) * Finish RealVfsFile and RealVfsDirectory * Finished OffsetVfsFile * More changes * Fix import paths * Major refactor * Remove double const * Use experimental/filesystem or filesystem depending on compiler * Port partition_filesystem * More changes * More Overhaul * FSP_SRV fixes * Fixes and testing * Try to get filesystem to compile * Filesystem on linux * Remove std::filesystem and document/test * Compile fixes * Missing include * Bug fixes * Fixes * Rename v_file and v_dir * clang-format fix * Rename NGLOG_* to LOG_* * Most review changes * Fix TODO * Guess 'main' to be Directory by filename
This commit is contained in:
parent
51bd76a5fd
commit
77c684c114
45 changed files with 1796 additions and 1688 deletions
|
@ -621,7 +621,7 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
|
|||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
FileSys::Path unused;
|
||||
auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData, unused);
|
||||
auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData);
|
||||
if (savedata.Failed()) {
|
||||
// Create the save data and return an error indicating that the operation was performed.
|
||||
FileSystem::FormatFileSystem(FileSystem::Type::SaveData);
|
||||
|
|
|
@ -2,36 +2,221 @@
|
|||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include "boost/container/flat_map.hpp"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_paths.h"
|
||||
#include "common/file_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/file_sys/sdmc_factory.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/filesystem/fsp_srv.h"
|
||||
|
||||
namespace Service::FileSystem {
|
||||
|
||||
// Size of emulated sd card free space, reported in bytes.
|
||||
// Just using 32GB because thats reasonable
|
||||
// TODO(DarkLordZach): Eventually make this configurable in settings.
|
||||
constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
|
||||
|
||||
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
|
||||
const std::string& dir_name) {
|
||||
if (dir_name == "." || dir_name == "" || dir_name == "/" || dir_name == "\\")
|
||||
return base;
|
||||
|
||||
return base->GetDirectoryRelative(dir_name);
|
||||
}
|
||||
|
||||
VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
|
||||
: backing(backing_) {}
|
||||
|
||||
std::string VfsDirectoryServiceWrapper::GetName() const {
|
||||
return backing->GetName();
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 size) const {
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
auto file = dir->CreateFile(FileUtil::GetFilename(path));
|
||||
if (file == nullptr)
|
||||
return ResultCode(-1);
|
||||
if (!file->Resize(size))
|
||||
return ResultCode(-1);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const {
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
if (!backing->DeleteFile(FileUtil::GetFilename(path)))
|
||||
return ResultCode(-1);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const {
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
|
||||
dir = backing;
|
||||
auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path));
|
||||
if (new_dir == nullptr)
|
||||
return ResultCode(-1);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const {
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path)))
|
||||
return ResultCode(-1);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const {
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path)))
|
||||
return ResultCode(-1);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
|
||||
const std::string& dest_path) const {
|
||||
auto src = backing->GetFileRelative(src_path);
|
||||
if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
|
||||
// Use more-optimized vfs implementation rename.
|
||||
if (src == nullptr)
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
if (!src->Rename(FileUtil::GetFilename(dest_path)))
|
||||
return ResultCode(-1);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// Move by hand -- TODO(DarkLordZach): Optimize
|
||||
auto c_res = CreateFile(dest_path, src->GetSize());
|
||||
if (c_res != RESULT_SUCCESS)
|
||||
return c_res;
|
||||
|
||||
auto dest = backing->GetFileRelative(dest_path);
|
||||
ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
|
||||
|
||||
ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
|
||||
"Could not write all of the bytes but everything else has succeded.");
|
||||
|
||||
if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path)))
|
||||
return ResultCode(-1);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path,
|
||||
const std::string& dest_path) const {
|
||||
auto src = GetDirectoryRelativeWrapped(backing, src_path);
|
||||
if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
|
||||
// Use more-optimized vfs implementation rename.
|
||||
if (src == nullptr)
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
if (!src->Rename(FileUtil::GetFilename(dest_path)))
|
||||
return ResultCode(-1);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// TODO(DarkLordZach): Implement renaming across the tree (move).
|
||||
ASSERT_MSG(false,
|
||||
"Could not rename directory with path \"{}\" to new path \"{}\" because parent dirs "
|
||||
"don't match -- UNIMPLEMENTED",
|
||||
src_path, dest_path);
|
||||
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path,
|
||||
FileSys::Mode mode) const {
|
||||
auto file = backing->GetFileRelative(path);
|
||||
if (file == nullptr)
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
|
||||
if ((static_cast<u32>(mode) & static_cast<u32>(FileSys::Mode::Append)) != 0) {
|
||||
return MakeResult<FileSys::VirtualFile>(
|
||||
std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize()));
|
||||
}
|
||||
|
||||
return MakeResult<FileSys::VirtualFile>(file);
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) {
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, path);
|
||||
if (dir == nullptr)
|
||||
return ResultCode(-1);
|
||||
return MakeResult(dir);
|
||||
}
|
||||
|
||||
u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
|
||||
if (backing->IsWritable())
|
||||
return EMULATED_SD_REPORTED_SIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
|
||||
const std::string& path) const {
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
if (dir == nullptr)
|
||||
return ResultCode(-1);
|
||||
auto filename = FileUtil::GetFilename(path);
|
||||
if (dir->GetFile(filename) != nullptr)
|
||||
return MakeResult(FileSys::EntryType::File);
|
||||
if (dir->GetSubdirectory(filename) != nullptr)
|
||||
return MakeResult(FileSys::EntryType::Directory);
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
// A deferred filesystem for nand save data.
|
||||
// This must be deferred because the directory is dependent on title id, which is not set at
|
||||
// registration time.
|
||||
struct SaveDataDeferredFilesystem : DeferredFilesystem {
|
||||
protected:
|
||||
FileSys::VirtualDir CreateFilesystem() override {
|
||||
u64 title_id = Core::CurrentProcess()->program_id;
|
||||
// TODO(DarkLordZach): Users
|
||||
u32 user_id = 0;
|
||||
std::string nand_directory = fmt::format(
|
||||
"{}save/{:016X}/{:08X}/", FileUtil::GetUserPath(D_NAND_IDX), title_id, user_id);
|
||||
|
||||
auto savedata =
|
||||
std::make_shared<FileSys::RealVfsDirectory>(nand_directory, FileSys::Mode::Write);
|
||||
return savedata;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Map of registered file systems, identified by type. Once an file system is registered here, it
|
||||
* is never removed until UnregisterFileSystems is called.
|
||||
*/
|
||||
static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map;
|
||||
static boost::container::flat_map<Type, std::unique_ptr<DeferredFilesystem>> filesystem_map;
|
||||
static FileSys::VirtualFile filesystem_romfs = nullptr;
|
||||
|
||||
ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type) {
|
||||
ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& factory, Type type) {
|
||||
auto result = filesystem_map.emplace(type, std::move(factory));
|
||||
|
||||
bool inserted = result.second;
|
||||
ASSERT_MSG(inserted, "Tried to register more than one system with same id code");
|
||||
|
||||
auto& filesystem = result.first->second;
|
||||
LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(),
|
||||
static_cast<u32>(type));
|
||||
LOG_DEBUG(Service_FS, "Registered file system with id code 0x{:08X}", static_cast<u32>(type));
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
|
||||
FileSys::Path& path) {
|
||||
ResultCode RegisterRomFS(FileSys::VirtualFile filesystem) {
|
||||
ASSERT_MSG(filesystem_romfs == nullptr,
|
||||
"Tried to register more than one system with same id code");
|
||||
|
||||
filesystem_romfs = filesystem;
|
||||
LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(),
|
||||
static_cast<u32>(Type::RomFS));
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type) {
|
||||
LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type));
|
||||
|
||||
auto itr = filesystem_map.find(type);
|
||||
|
@ -40,7 +225,13 @@ ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
|
|||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
return itr->second->Open(path);
|
||||
return MakeResult(itr->second->Get());
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFS() {
|
||||
if (filesystem_romfs == nullptr)
|
||||
return ResultCode(-1);
|
||||
return MakeResult(filesystem_romfs);
|
||||
}
|
||||
|
||||
ResultCode FormatFileSystem(Type type) {
|
||||
|
@ -52,21 +243,21 @@ ResultCode FormatFileSystem(Type type) {
|
|||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
FileSys::Path unused;
|
||||
return itr->second->Format(unused);
|
||||
return itr->second->Get()->GetParentDirectory()->DeleteSubdirectory(
|
||||
itr->second->Get()->GetName())
|
||||
? RESULT_SUCCESS
|
||||
: ResultCode(-1);
|
||||
}
|
||||
|
||||
void RegisterFileSystems() {
|
||||
filesystem_map.clear();
|
||||
filesystem_romfs = nullptr;
|
||||
|
||||
std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
|
||||
std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX);
|
||||
auto sdcard = std::make_shared<FileSys::RealVfsDirectory>(sd_directory, FileSys::Mode::Write);
|
||||
RegisterFileSystem(std::make_unique<DeferredFilesystem>(sdcard), Type::SDMC);
|
||||
|
||||
auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory));
|
||||
RegisterFileSystem(std::move(savedata), Type::SaveData);
|
||||
|
||||
auto sdcard = std::make_unique<FileSys::SDMC_Factory>(std::move(sd_directory));
|
||||
RegisterFileSystem(std::move(sdcard), Type::SDMC);
|
||||
RegisterFileSystem(std::make_unique<SaveDataDeferredFilesystem>(), Type::SaveData);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
@ -29,12 +31,136 @@ enum class Type {
|
|||
SDMC = 3,
|
||||
};
|
||||
|
||||
// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
|
||||
// pointers and booleans. This makes using a VfsDirectory with switch services much easier and
|
||||
// avoids repetitive code.
|
||||
class VfsDirectoryServiceWrapper {
|
||||
public:
|
||||
explicit VfsDirectoryServiceWrapper(FileSys::VirtualDir backing);
|
||||
|
||||
/**
|
||||
* Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
|
||||
*/
|
||||
std::string GetName() const;
|
||||
|
||||
/**
|
||||
* Create a file specified by its path
|
||||
* @param path Path relative to the Archive
|
||||
* @param size The size of the new file, filled with zeroes
|
||||
* @return Result of the operation
|
||||
*/
|
||||
ResultCode CreateFile(const std::string& path, u64 size) const;
|
||||
|
||||
/**
|
||||
* Delete a file specified by its path
|
||||
* @param path Path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
ResultCode DeleteFile(const std::string& path) const;
|
||||
|
||||
/**
|
||||
* Create a directory specified by its path
|
||||
* @param path Path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
ResultCode CreateDirectory(const std::string& path) const;
|
||||
|
||||
/**
|
||||
* Delete a directory specified by its path
|
||||
* @param path Path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
ResultCode DeleteDirectory(const std::string& path) const;
|
||||
|
||||
/**
|
||||
* Delete a directory specified by its path and anything under it
|
||||
* @param path Path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
ResultCode DeleteDirectoryRecursively(const std::string& path) const;
|
||||
|
||||
/**
|
||||
* Rename a File specified by its path
|
||||
* @param src_path Source path relative to the archive
|
||||
* @param dest_path Destination path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const;
|
||||
|
||||
/**
|
||||
* Rename a Directory specified by its path
|
||||
* @param src_path Source path relative to the archive
|
||||
* @param dest_path Destination path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
ResultCode RenameDirectory(const std::string& src_path, const std::string& dest_path) const;
|
||||
|
||||
/**
|
||||
* Open a file specified by its path, using the specified mode
|
||||
* @param path Path relative to the archive
|
||||
* @param mode Mode to open the file with
|
||||
* @return Opened file, or error code
|
||||
*/
|
||||
ResultVal<FileSys::VirtualFile> OpenFile(const std::string& path, FileSys::Mode mode) const;
|
||||
|
||||
/**
|
||||
* Open a directory specified by its path
|
||||
* @param path Path relative to the archive
|
||||
* @return Opened directory, or error code
|
||||
*/
|
||||
ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path);
|
||||
|
||||
/**
|
||||
* Get the free space
|
||||
* @return The number of free bytes in the archive
|
||||
*/
|
||||
u64 GetFreeSpaceSize() const;
|
||||
|
||||
/**
|
||||
* Get the type of the specified path
|
||||
* @return The type of the specified path or error code
|
||||
*/
|
||||
ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const;
|
||||
|
||||
private:
|
||||
FileSys::VirtualDir backing;
|
||||
};
|
||||
|
||||
// A class that deferres the creation of a filesystem until a later time.
|
||||
// This is useful if construction depends on a variable not known when the filesystem is registered.
|
||||
// Construct this with a filesystem (VirtualDir) to avoid the deferrence feature or override the
|
||||
// CreateFilesystem method which will be called on first use.
|
||||
struct DeferredFilesystem {
|
||||
DeferredFilesystem() = default;
|
||||
|
||||
explicit DeferredFilesystem(FileSys::VirtualDir vfs_directory) : fs(std::move(vfs_directory)) {}
|
||||
|
||||
FileSys::VirtualDir Get() {
|
||||
if (fs == nullptr)
|
||||
fs = CreateFilesystem();
|
||||
|
||||
return fs;
|
||||
}
|
||||
|
||||
virtual ~DeferredFilesystem() = default;
|
||||
|
||||
protected:
|
||||
virtual FileSys::VirtualDir CreateFilesystem() {
|
||||
return fs;
|
||||
}
|
||||
|
||||
private:
|
||||
FileSys::VirtualDir fs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers a FileSystem, instances of which can later be opened using its IdCode.
|
||||
* @param factory FileSystem backend interface to use
|
||||
* @param type Type used to access this type of FileSystem
|
||||
*/
|
||||
ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type);
|
||||
ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& fs, Type type);
|
||||
|
||||
ResultCode RegisterRomFS(FileSys::VirtualFile fs);
|
||||
|
||||
/**
|
||||
* Opens a file system
|
||||
|
@ -42,8 +168,9 @@ ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& fact
|
|||
* @param path Path to the file system, used with Binary paths
|
||||
* @return FileSys::FileSystemBackend interface to the file system
|
||||
*/
|
||||
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
|
||||
FileSys::Path& path);
|
||||
ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type);
|
||||
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFS();
|
||||
|
||||
/**
|
||||
* Formats a file system
|
||||
|
|
|
@ -19,8 +19,8 @@ namespace Service::FileSystem {
|
|||
|
||||
class IStorage final : public ServiceFramework<IStorage> {
|
||||
public:
|
||||
IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend)
|
||||
: ServiceFramework("IStorage"), backend(std::move(backend)) {
|
||||
IStorage(FileSys::VirtualFile backend_)
|
||||
: ServiceFramework("IStorage"), backend(std::move(backend_)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"},
|
||||
{3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"},
|
||||
|
@ -29,7 +29,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::StorageBackend> backend;
|
||||
FileSys::VirtualFile backend;
|
||||
|
||||
void Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
@ -51,8 +51,8 @@ private:
|
|||
}
|
||||
|
||||
// Read the data from the Storage backend
|
||||
std::vector<u8> output(length);
|
||||
ResultVal<size_t> res = backend->Read(offset, length, output.data());
|
||||
std::vector<u8> output = backend->ReadBytes(length, offset);
|
||||
auto res = MakeResult<size_t>(output.size());
|
||||
if (res.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
|
@ -69,8 +69,8 @@ private:
|
|||
|
||||
class IFile final : public ServiceFramework<IFile> {
|
||||
public:
|
||||
explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend)
|
||||
: ServiceFramework("IFile"), backend(std::move(backend)) {
|
||||
explicit IFile(FileSys::VirtualFile backend_)
|
||||
: ServiceFramework("IFile"), backend(std::move(backend_)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"},
|
||||
{2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"},
|
||||
|
@ -80,7 +80,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::StorageBackend> backend;
|
||||
FileSys::VirtualFile backend;
|
||||
|
||||
void Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
@ -103,8 +103,8 @@ private:
|
|||
}
|
||||
|
||||
// Read the data from the Storage backend
|
||||
std::vector<u8> output(length);
|
||||
ResultVal<size_t> res = backend->Read(offset, length, output.data());
|
||||
std::vector<u8> output = backend->ReadBytes(length, offset);
|
||||
auto res = MakeResult<size_t>(output.size());
|
||||
if (res.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
|
@ -139,9 +139,10 @@ private:
|
|||
return;
|
||||
}
|
||||
|
||||
// Write the data to the Storage backend
|
||||
std::vector<u8> data = ctx.ReadBuffer();
|
||||
ResultVal<size_t> res = backend->Write(offset, length, true, data.data());
|
||||
data.resize(length);
|
||||
// Write the data to the Storage backend
|
||||
auto res = MakeResult<size_t>(backend->WriteBytes(data, offset));
|
||||
if (res.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
|
@ -154,7 +155,8 @@ private:
|
|||
|
||||
void Flush(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
backend->Flush();
|
||||
|
||||
// Exists for SDK compatibiltity -- No need to flush file.
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
@ -163,7 +165,7 @@ private:
|
|||
void SetSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 size = rp.Pop<u64>();
|
||||
backend->SetSize(size);
|
||||
backend->Resize(size);
|
||||
LOG_DEBUG(Service_FS, "called, size={}", size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
@ -180,19 +182,38 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data,
|
||||
FileSys::EntryType type) {
|
||||
for (const auto& new_entry : new_data) {
|
||||
FileSys::Entry entry;
|
||||
entry.filename[0] = '\0';
|
||||
std::strncat(entry.filename, new_entry->GetName().c_str(), FileSys::FILENAME_LENGTH - 1);
|
||||
entry.type = type;
|
||||
entry.file_size = new_entry->GetSize();
|
||||
entries.emplace_back(std::move(entry));
|
||||
}
|
||||
}
|
||||
|
||||
class IDirectory final : public ServiceFramework<IDirectory> {
|
||||
public:
|
||||
explicit IDirectory(std::unique_ptr<FileSys::DirectoryBackend>&& backend)
|
||||
: ServiceFramework("IDirectory"), backend(std::move(backend)) {
|
||||
explicit IDirectory(FileSys::VirtualDir backend_)
|
||||
: ServiceFramework("IDirectory"), backend(std::move(backend_)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IDirectory::Read, "Read"},
|
||||
{1, &IDirectory::GetEntryCount, "GetEntryCount"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
// Build entry index now to save time later.
|
||||
BuildEntryIndex(entries, backend->GetFiles(), FileSys::File);
|
||||
BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::Directory);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::DirectoryBackend> backend;
|
||||
FileSys::VirtualDir backend;
|
||||
std::vector<FileSys::Entry> entries;
|
||||
u64 next_entry_index = 0;
|
||||
|
||||
void Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
@ -203,26 +224,31 @@ private:
|
|||
// Calculate how many entries we can fit in the output buffer
|
||||
u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
|
||||
|
||||
// Cap at total number of entries.
|
||||
u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
|
||||
|
||||
// Read the data from the Directory backend
|
||||
std::vector<FileSys::Entry> entries(count_entries);
|
||||
u64 read_entries = backend->Read(count_entries, entries.data());
|
||||
std::vector<FileSys::Entry> entry_data(entries.begin() + next_entry_index,
|
||||
entries.begin() + next_entry_index + actual_entries);
|
||||
|
||||
next_entry_index += actual_entries;
|
||||
|
||||
// Convert the data into a byte array
|
||||
std::vector<u8> output(entries.size() * sizeof(FileSys::Entry));
|
||||
std::memcpy(output.data(), entries.data(), output.size());
|
||||
std::vector<u8> output(entry_data.size() * sizeof(FileSys::Entry));
|
||||
std::memcpy(output.data(), entry_data.data(), output.size());
|
||||
|
||||
// Write the data to memory
|
||||
ctx.WriteBuffer(output);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(read_entries);
|
||||
rb.Push(actual_entries);
|
||||
}
|
||||
|
||||
void GetEntryCount(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
u64 count = backend->GetEntryCount();
|
||||
u64 count = entries.size() - next_entry_index;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
@ -232,7 +258,7 @@ private:
|
|||
|
||||
class IFileSystem final : public ServiceFramework<IFileSystem> {
|
||||
public:
|
||||
explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend)
|
||||
explicit IFileSystem(FileSys::VirtualDir backend)
|
||||
: ServiceFramework("IFileSystem"), backend(std::move(backend)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IFileSystem::CreateFile, "CreateFile"},
|
||||
|
@ -267,7 +293,7 @@ public:
|
|||
LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend->CreateFile(name, size));
|
||||
rb.Push(backend.CreateFile(name, size));
|
||||
}
|
||||
|
||||
void DeleteFile(Kernel::HLERequestContext& ctx) {
|
||||
|
@ -279,7 +305,7 @@ public:
|
|||
LOG_DEBUG(Service_FS, "called file {}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend->DeleteFile(name));
|
||||
rb.Push(backend.DeleteFile(name));
|
||||
}
|
||||
|
||||
void CreateDirectory(Kernel::HLERequestContext& ctx) {
|
||||
|
@ -291,7 +317,7 @@ public:
|
|||
LOG_DEBUG(Service_FS, "called directory {}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend->CreateDirectory(name));
|
||||
rb.Push(backend.CreateDirectory(name));
|
||||
}
|
||||
|
||||
void RenameFile(Kernel::HLERequestContext& ctx) {
|
||||
|
@ -309,7 +335,7 @@ public:
|
|||
LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend->RenameFile(src_name, dst_name));
|
||||
rb.Push(backend.RenameFile(src_name, dst_name));
|
||||
}
|
||||
|
||||
void OpenFile(Kernel::HLERequestContext& ctx) {
|
||||
|
@ -322,14 +348,14 @@ public:
|
|||
|
||||
LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode));
|
||||
|
||||
auto result = backend->OpenFile(name, mode);
|
||||
auto result = backend.OpenFile(name, mode);
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
auto file = std::move(result.Unwrap());
|
||||
IFile file(result.Unwrap());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
@ -347,14 +373,14 @@ public:
|
|||
|
||||
LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags);
|
||||
|
||||
auto result = backend->OpenDirectory(name);
|
||||
auto result = backend.OpenDirectory(name);
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
auto directory = std::move(result.Unwrap());
|
||||
IDirectory directory(result.Unwrap());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
@ -369,7 +395,7 @@ public:
|
|||
|
||||
LOG_DEBUG(Service_FS, "called file {}", name);
|
||||
|
||||
auto result = backend->GetEntryType(name);
|
||||
auto result = backend.GetEntryType(name);
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
|
@ -389,7 +415,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::FileSystemBackend> backend;
|
||||
VfsDirectoryServiceWrapper backend;
|
||||
};
|
||||
|
||||
FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
|
@ -490,8 +516,7 @@ void FSP_SRV::TryLoadRomFS() {
|
|||
if (romfs) {
|
||||
return;
|
||||
}
|
||||
FileSys::Path unused;
|
||||
auto res = OpenFileSystem(Type::RomFS, unused);
|
||||
auto res = OpenRomFS();
|
||||
if (res.Succeeded()) {
|
||||
romfs = std::move(res.Unwrap());
|
||||
}
|
||||
|
@ -507,8 +532,7 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
|
|||
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
FileSys::Path unused;
|
||||
auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap();
|
||||
IFileSystem filesystem(OpenFileSystem(Type::SDMC).Unwrap());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
@ -531,8 +555,7 @@ void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
|
|||
void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
FileSys::Path unused;
|
||||
auto filesystem = OpenFileSystem(Type::SaveData, unused).Unwrap();
|
||||
IFileSystem filesystem(OpenFileSystem(Type::SaveData).Unwrap());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
@ -559,18 +582,11 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Attempt to open a StorageBackend interface to the RomFS
|
||||
auto storage = romfs->OpenFile({}, {});
|
||||
if (storage.Failed()) {
|
||||
LOG_CRITICAL(Service_FS, "no storage interface available!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(storage.Code());
|
||||
return;
|
||||
}
|
||||
IStorage storage(romfs);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap()));
|
||||
rb.PushIpcInterface<IStorage>(std::move(storage));
|
||||
}
|
||||
|
||||
void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
|
||||
|
|
|
@ -29,7 +29,7 @@ private:
|
|||
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
|
||||
void OpenRomStorage(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::unique_ptr<FileSys::FileSystemBackend> romfs;
|
||||
FileSys::VirtualFile romfs;
|
||||
};
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue