2018-01-16 22:20:12 -05:00
|
|
|
// Copyright 2018 yuzu emulator team
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2018-07-06 10:51:32 -04:00
|
|
|
#include "boost/container/flat_map.hpp"
|
|
|
|
#include "common/assert.h"
|
|
|
|
#include "common/common_paths.h"
|
2018-02-19 00:32:00 -05:00
|
|
|
#include "common/file_util.h"
|
2018-07-06 10:51:32 -04:00
|
|
|
#include "core/core.h"
|
|
|
|
#include "core/file_sys/errors.h"
|
2018-01-16 22:20:12 -05:00
|
|
|
#include "core/file_sys/filesystem.h"
|
2018-07-06 10:51:32 -04:00
|
|
|
#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"
|
2018-01-16 22:20:12 -05:00
|
|
|
#include "core/hle/service/filesystem/filesystem.h"
|
|
|
|
#include "core/hle/service/filesystem/fsp_srv.h"
|
|
|
|
|
2018-04-19 21:41:44 -04:00
|
|
|
namespace Service::FileSystem {
|
2018-01-16 22:20:12 -05:00
|
|
|
|
2018-07-06 10:51:32 -04:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-01-16 22:20:12 -05:00
|
|
|
/**
|
|
|
|
* Map of registered file systems, identified by type. Once an file system is registered here, it
|
|
|
|
* is never removed until UnregisterFileSystems is called.
|
|
|
|
*/
|
2018-07-06 10:51:32 -04:00
|
|
|
static boost::container::flat_map<Type, std::unique_ptr<DeferredFilesystem>> filesystem_map;
|
|
|
|
static FileSys::VirtualFile filesystem_romfs = nullptr;
|
2018-01-16 22:20:12 -05:00
|
|
|
|
2018-07-06 10:51:32 -04:00
|
|
|
ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& factory, Type type) {
|
2018-01-16 22:20:12 -05:00
|
|
|
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;
|
2018-07-06 10:51:32 -04:00
|
|
|
LOG_DEBUG(Service_FS, "Registered file system with id code 0x{:08X}", static_cast<u32>(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;
|
2018-07-02 12:20:50 -04:00
|
|
|
LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(),
|
2018-07-06 10:51:32 -04:00
|
|
|
static_cast<u32>(Type::RomFS));
|
2018-01-16 22:20:12 -05:00
|
|
|
return RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2018-07-06 10:51:32 -04:00
|
|
|
ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type) {
|
2018-07-02 12:13:26 -04:00
|
|
|
LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type));
|
2018-01-16 22:20:12 -05:00
|
|
|
|
|
|
|
auto itr = filesystem_map.find(type);
|
|
|
|
if (itr == filesystem_map.end()) {
|
|
|
|
// TODO(bunnei): Find a better error code for this
|
|
|
|
return ResultCode(-1);
|
|
|
|
}
|
|
|
|
|
2018-07-06 10:51:32 -04:00
|
|
|
return MakeResult(itr->second->Get());
|
|
|
|
}
|
|
|
|
|
|
|
|
ResultVal<FileSys::VirtualFile> OpenRomFS() {
|
|
|
|
if (filesystem_romfs == nullptr)
|
|
|
|
return ResultCode(-1);
|
|
|
|
return MakeResult(filesystem_romfs);
|
2018-01-16 22:20:12 -05:00
|
|
|
}
|
|
|
|
|
2018-03-04 13:03:58 -05:00
|
|
|
ResultCode FormatFileSystem(Type type) {
|
2018-07-02 12:13:26 -04:00
|
|
|
LOG_TRACE(Service_FS, "Formatting FileSystem with type={}", static_cast<u32>(type));
|
2018-03-04 13:03:58 -05:00
|
|
|
|
|
|
|
auto itr = filesystem_map.find(type);
|
|
|
|
if (itr == filesystem_map.end()) {
|
|
|
|
// TODO(bunnei): Find a better error code for this
|
|
|
|
return ResultCode(-1);
|
|
|
|
}
|
|
|
|
|
2018-07-06 10:51:32 -04:00
|
|
|
return itr->second->Get()->GetParentDirectory()->DeleteSubdirectory(
|
|
|
|
itr->second->Get()->GetName())
|
|
|
|
? RESULT_SUCCESS
|
|
|
|
: ResultCode(-1);
|
2018-03-04 13:03:58 -05:00
|
|
|
}
|
|
|
|
|
2018-02-19 00:32:00 -05:00
|
|
|
void RegisterFileSystems() {
|
2018-01-16 22:20:12 -05:00
|
|
|
filesystem_map.clear();
|
2018-07-06 10:51:32 -04:00
|
|
|
filesystem_romfs = nullptr;
|
2018-02-19 00:32:00 -05:00
|
|
|
|
2018-03-19 22:17:15 -04:00
|
|
|
std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX);
|
2018-07-06 10:51:32 -04:00
|
|
|
auto sdcard = std::make_shared<FileSys::RealVfsDirectory>(sd_directory, FileSys::Mode::Write);
|
|
|
|
RegisterFileSystem(std::make_unique<DeferredFilesystem>(sdcard), Type::SDMC);
|
2018-02-19 00:32:00 -05:00
|
|
|
|
2018-07-06 10:51:32 -04:00
|
|
|
RegisterFileSystem(std::make_unique<SaveDataDeferredFilesystem>(), Type::SaveData);
|
2018-01-16 22:20:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
2018-02-19 00:32:00 -05:00
|
|
|
RegisterFileSystems();
|
2018-01-16 22:20:12 -05:00
|
|
|
std::make_shared<FSP_SRV>()->InstallAsService(service_manager);
|
|
|
|
}
|
|
|
|
|
2018-04-19 21:41:44 -04:00
|
|
|
} // namespace Service::FileSystem
|