From 77c684c1140f6bf3fb7d4560d06d2efb1a2ee5e2 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 6 Jul 2018 10:51:32 -0400 Subject: 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 --- src/core/hle/service/filesystem/filesystem.cpp | 225 +++++++++++++++++++++++-- 1 file changed, 208 insertions(+), 17 deletions(-) (limited to 'src/core/hle/service/filesystem/filesystem.cpp') diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index f58b518b6..25810c165 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -2,36 +2,221 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include +#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 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(mode) & static_cast(FileSys::Mode::Append)) != 0) { + return MakeResult( + std::make_shared(file, 0, file->GetSize())); + } + + return MakeResult(file); +} + +ResultVal 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 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(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> filesystem_map; +static boost::container::flat_map> filesystem_map; +static FileSys::VirtualFile filesystem_romfs = nullptr; -ResultCode RegisterFileSystem(std::unique_ptr&& factory, Type type) { +ResultCode RegisterFileSystem(std::unique_ptr&& 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}", static_cast(type)); + return RESULT_SUCCESS; +} + +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(type)); + static_cast(Type::RomFS)); return RESULT_SUCCESS; } -ResultVal> OpenFileSystem(Type type, - FileSys::Path& path) { +ResultVal OpenFileSystem(Type type) { LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast(type)); auto itr = filesystem_map.find(type); @@ -40,7 +225,13 @@ ResultVal> OpenFileSystem(Type type, return ResultCode(-1); } - return itr->second->Open(path); + return MakeResult(itr->second->Get()); +} + +ResultVal 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(sd_directory, FileSys::Mode::Write); + RegisterFileSystem(std::make_unique(sdcard), Type::SDMC); - auto savedata = std::make_unique(std::move(nand_directory)); - RegisterFileSystem(std::move(savedata), Type::SaveData); - - auto sdcard = std::make_unique(std::move(sd_directory)); - RegisterFileSystem(std::move(sdcard), Type::SDMC); + RegisterFileSystem(std::make_unique(), Type::SaveData); } void InstallInterfaces(SM::ServiceManager& service_manager) { -- cgit v1.2.3