ams: support building unit test programs on windows/linux/macos
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fssrv::fscreator {
|
||||
|
||||
Result LocalFileSystemCreator::Create(std::shared_ptr<fs::fsa::IFileSystem> *out, const fs::Path &path, bool case_sensitive, bool ensure_root, Result on_path_not_found) {
|
||||
/* Check that we're configured for development. */
|
||||
R_UNLESS(m_is_development, fs::ResultPermissionDeniedForCreateHostFileSystem());
|
||||
|
||||
/* Allocate a local filesystem. */
|
||||
auto local_fs = fs::AllocateShared<fssystem::LocalFileSystem>();
|
||||
R_UNLESS(local_fs != nullptr, fs::ResultAllocationFailureInLocalFileSystemCreatorA());
|
||||
|
||||
/* If we're supposed to make sure the root path exists, do so. */
|
||||
if (ensure_root) {
|
||||
/* Sanity check that the path will be possible to create. */
|
||||
AMS_ASSERT(!path.IsEmpty());
|
||||
|
||||
/* Initialize the local fs with an empty path. */
|
||||
fs::Path empty_path;
|
||||
R_TRY(empty_path.InitializeAsEmpty());
|
||||
R_TRY(local_fs->Initialize(empty_path, fssystem::PathCaseSensitiveMode_CaseInsensitive));
|
||||
|
||||
/* Ensure the directory exists. */
|
||||
if (const Result ensure_result = fssystem::EnsureDirectory(local_fs.get(), path); R_FAILED(ensure_result)) {
|
||||
if (R_SUCCEEDED(on_path_not_found)) {
|
||||
R_THROW(ensure_result);
|
||||
} else {
|
||||
R_THROW(on_path_not_found);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the local filesystem. */
|
||||
R_TRY_CATCH(local_fs->Initialize(path, case_sensitive ? fssystem::PathCaseSensitiveMode_CaseSensitive : fssystem::PathCaseSensitiveMode_CaseInsensitive)) {
|
||||
R_CATCH(fs::ResultPathNotFound) {
|
||||
if (R_SUCCEEDED(on_path_not_found)) {
|
||||
R_THROW(R_CURRENT_RESULT);
|
||||
} else {
|
||||
R_THROW(on_path_not_found);
|
||||
}
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* Set the output fs. */
|
||||
*out = std::move(local_fs);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fssrv::fscreator {
|
||||
|
||||
Result SubDirectoryFileSystemCreator::Create(std::shared_ptr<fs::fsa::IFileSystem> *out, std::shared_ptr<fs::fsa::IFileSystem> base_fs, const fs::Path &path) {
|
||||
/* Verify that we can the directory on the base filesystem. */
|
||||
{
|
||||
std::unique_ptr<fs::fsa::IDirectory> sub_dir;
|
||||
R_TRY(base_fs->OpenDirectory(std::addressof(sub_dir), path, fs::OpenDirectoryMode_Directory));
|
||||
}
|
||||
|
||||
/* Allocate a SubDirectoryFileSystem. */
|
||||
auto sub_dir_fs = fs::AllocateShared<fssystem::SubDirectoryFileSystem>(std::move(base_fs));
|
||||
R_UNLESS(sub_dir_fs != nullptr, fs::ResultAllocationFailureInSubDirectoryFileSystemCreatorA());
|
||||
|
||||
/* Initialize the new filesystem. */
|
||||
R_TRY(sub_dir_fs->Initialize(path));
|
||||
|
||||
/* Return the new filesystem. */
|
||||
*out = std::move(sub_dir_fs);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,8 +42,8 @@ namespace ams::fssrv {
|
||||
|
||||
constexpr size_t NumSessions = FileSystemProxyMaxSessions + ProgramRegistryMaxSessions + FileSystemProxyForLoaderMaxSessions;
|
||||
|
||||
constinit os::SemaphoreType g_semaphore_for_file_system_proxy_for_loader;
|
||||
constinit os::SemaphoreType g_semaphore_for_program_registry;
|
||||
constinit os::SemaphoreType g_semaphore_for_file_system_proxy_for_loader = {};
|
||||
constinit os::SemaphoreType g_semaphore_for_program_registry = {};
|
||||
|
||||
class FileSystemProxyServerManager final : public ams::sf::hipc::ServerManager<PortIndex_Count, FileSystemProxyServerOptions, NumSessions> {
|
||||
private:
|
||||
@@ -89,11 +89,11 @@ namespace ams::fssrv {
|
||||
}
|
||||
};
|
||||
|
||||
constinit util::TypedStorage<FileSystemProxyServerManager> g_server_manager_storage;
|
||||
constinit util::TypedStorage<FileSystemProxyServerManager> g_server_manager_storage = {};
|
||||
constinit FileSystemProxyServerManager *g_server_manager = nullptr;
|
||||
|
||||
constinit os::BarrierType g_server_loop_barrier;
|
||||
constinit os::EventType g_resume_wait_event;
|
||||
constinit os::BarrierType g_server_loop_barrier = {};
|
||||
constinit os::EventType g_resume_wait_event = {};
|
||||
|
||||
constinit bool g_is_suspended = false;
|
||||
|
||||
@@ -103,9 +103,9 @@ namespace ams::fssrv {
|
||||
|
||||
}
|
||||
|
||||
void InitializeForFileSystemProxy(fscreator::FileSystemCreatorInterfaces *fs_creator_interfaces, fssystem::IBufferManager *buffer_manager, bool is_development_function_enabled) {
|
||||
void InitializeForFileSystemProxy(const FileSystemProxyConfiguration &config) {
|
||||
/* TODO FS-REIMPL */
|
||||
AMS_UNUSED(fs_creator_interfaces, buffer_manager, is_development_function_enabled);
|
||||
AMS_UNUSED(config);
|
||||
}
|
||||
|
||||
void InitializeFileSystemProxyServer(int threads) {
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
|
||||
#include "impl/fssrv_allocator_for_service_framework.hpp"
|
||||
#include "impl/fssrv_program_info.hpp"
|
||||
|
||||
namespace ams::fssrv {
|
||||
@@ -27,6 +29,420 @@ namespace ams::fssrv {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
Result FileSystemProxyImpl::OpenFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u32 type) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::FileSystemProxyImpl::SetCurrentProcess(const ams::sf::ClientProcessId &client_pid) {
|
||||
/* Set current process. */
|
||||
m_process_id = client_pid.GetValue().value;
|
||||
|
||||
/* TODO: Allocate NcaFileSystemService. */
|
||||
/* TODO: Allocate SaveDataFileSystemService. */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenDataFileSystemByCurrentProcess(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenFileSystemWithPatch(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id, u32 type) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenFileSystemWithId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u64 program_id, u32 type) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenDataFileSystemByProgramId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenBisFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u32 id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenBisStorage(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u32 id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::InvalidateBisCache() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenHostFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path) {
|
||||
/* Invoke the modern API from the legacy API. */
|
||||
R_RETURN(this->OpenHostFileSystemWithOption(out, path, fs::MountHostOption::None._value));
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenSdCardFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::FormatSdCardFileSystem() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::DeleteSaveDataFileSystem(u64 save_data_id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::CreateSaveDataFileSystem(const fs::SaveDataAttribute &attribute, const fs::SaveDataCreationInfo &creation_info, const fs::SaveDataMetaInfo &meta_info) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::CreateSaveDataFileSystemBySystemSaveDataId(const fs::SaveDataAttribute &attribute, const fs::SaveDataCreationInfo &creation_info) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::RegisterSaveDataFileSystemAtomicDeletion(const ams::sf::InBuffer &save_data_ids) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::DeleteSaveDataFileSystemBySaveDataSpaceId(u8 indexer_space_id, u64 save_data_id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::FormatSdCardDryRun() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::IsExFatSupported(ams::sf::Out<bool> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::DeleteSaveDataFileSystemBySaveDataAttribute(u8 space_id, const fs::SaveDataAttribute &attribute) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenGameCardStorage(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u32 handle, u32 partition) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenGameCardFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u32 handle, u32 partition) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::ExtendSaveDataFileSystem(u8 space_id, u64 save_data_id, s64 available_size, s64 journal_size) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::DeleteCacheStorage(u16 index) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::GetCacheStorageSize(ams::sf::Out<s64> out_size, ams::sf::Out<s64> out_journal_size, u16 index) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::CreateSaveDataFileSystemWithHashSalt(const fs::SaveDataAttribute &attribute, const fs::SaveDataCreationInfo &creation_info, const fs::SaveDataMetaInfo &meta_info, const fs::HashSalt &salt) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenHostFileSystemWithOption(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u32 _option) {
|
||||
/* TODO: GetProgramInfo */
|
||||
/* TODO: GetAccessibility. */
|
||||
/* TODO: Check Accessibility CanRead/CanWrite */
|
||||
|
||||
/* Convert the path. */
|
||||
fs::Path normalized_path;
|
||||
#if defined(ATMOSPHERE_OS_WINDOWS)
|
||||
R_TRY(normalized_path.Initialize(path.str));
|
||||
#else
|
||||
if (path.str[0] == '/' && path.str[1] == '/') {
|
||||
R_TRY(normalized_path.Initialize(path.str));
|
||||
} else {
|
||||
R_TRY(normalized_path.InitializeWithReplaceUnc(path.str));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Normalize the path. */
|
||||
fs::PathFlags path_flags;
|
||||
path_flags.AllowWindowsPath();
|
||||
path_flags.AllowRelativePath();
|
||||
path_flags.AllowEmptyPath();
|
||||
R_TRY(normalized_path.Normalize(path_flags));
|
||||
|
||||
/* Parse option. */
|
||||
const fs::MountHostOption option{ _option };
|
||||
|
||||
/* TODO: FileSystemProxyCoreImpl::OpenHostFileSystem */
|
||||
/* TODO: use creator interfaces */
|
||||
std::shared_ptr<fs::fsa::IFileSystem> fs;
|
||||
{
|
||||
fssrv::fscreator::LocalFileSystemCreator local_fs_creator(true);
|
||||
|
||||
R_TRY(static_cast<fscreator::ILocalFileSystemCreator &>(local_fs_creator).Create(std::addressof(fs), normalized_path, option.HasPseudoCaseSensitiveFlag()));
|
||||
}
|
||||
|
||||
/* Determine path flags for the result fs. */
|
||||
fs::PathFlags host_path_flags;
|
||||
if (path.str[0] == 0) {
|
||||
host_path_flags.AllowWindowsPath();
|
||||
}
|
||||
|
||||
/* Create an interface adapter. */
|
||||
auto sf_fs = impl::FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, impl::FileSystemInterfaceAdapter>(std::move(fs), host_path_flags, false);
|
||||
R_UNLESS(sf_fs != nullptr, fs::ResultAllocationFailureInFileSystemProxyImplA());
|
||||
|
||||
/* Set the output. */
|
||||
*out = std::move(sf_fs);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenSaveDataFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 space_id, const fs::SaveDataAttribute &attribute) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenSaveDataFileSystemBySystemSaveDataId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 space_id, const fs::SaveDataAttribute &attribute) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenReadOnlySaveDataFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 space_id, const fs::SaveDataAttribute &attribute) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(const ams::sf::OutBuffer &buffer, u8 space_id, u64 save_data_id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::ReadSaveDataFileSystemExtraData(const ams::sf::OutBuffer &buffer, u64 save_data_id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::WriteSaveDataFileSystemExtraData(u64 save_data_id, u8 space_id, const ams::sf::InBuffer &buffer) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result FileSystemProxyImpl::OpenImageDirectoryFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u32 id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result FileSystemProxyImpl::OpenContentStorageFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u32 id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result FileSystemProxyImpl::OpenDataStorageByCurrentProcess(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenDataStorageByProgramId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, ncm::ProgramId program_id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenDataStorageByDataId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, ncm::DataId data_id, u8 storage_id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenPatchDataStorageByCurrentProcess(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenDataFileSystemWithProgramIndex(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 index) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenDataStorageWithProgramIndex(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u8 index) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenDataStorageByPath(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, const fssrv::sf::FspPath &path, u32 type) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenDeviceOperator(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IDeviceOperator>> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenSdCardDetectionEventNotifier(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenGameCardDetectionEventNotifier(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenSystemDataUpdateEventNotifier(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::NotifySystemDataUpdateEvent() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result FileSystemProxyImpl::SetCurrentPosixTime(s64 posix_time) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result FileSystemProxyImpl::GetRightsId(ams::sf::Out<fs::RightsId> out, ncm::ProgramId program_id, ncm::StorageId storage_id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::RegisterExternalKey(const fs::RightsId &rights_id, const spl::AccessKey &access_key) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::UnregisterAllExternalKey() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::GetRightsIdByPath(ams::sf::Out<fs::RightsId> out, const fssrv::sf::FspPath &path) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::GetRightsIdAndKeyGenerationByPath(ams::sf::Out<fs::RightsId> out, ams::sf::Out<u8> out_key_generation, const fssrv::sf::FspPath &path) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::SetCurrentPosixTimeWithTimeDifference(s64 posix_time, s32 time_difference) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::GetFreeSpaceSizeForSaveData(ams::sf::Out<s64> out, u8 space_id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::VerifySaveDataFileSystemBySaveDataSpaceId() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::CorruptSaveDataFileSystemBySaveDataSpaceId() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::QuerySaveDataInternalStorageTotalSize() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::GetSaveDataCommitId() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::UnregisterExternalKey(const fs::RightsId &rights_id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::SetSdCardEncryptionSeed(const fs::EncryptionSeed &seed) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::SetSdCardAccessibility(bool accessible) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::IsSdCardAccessible(ams::sf::Out<bool> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::IsSignedSystemPartitionOnSdCardValid(ams::sf::Out<bool> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenAccessFailureDetectionEventNotifier() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result FileSystemProxyImpl::RegisterProgramIndexMapInfo(const ams::sf::InBuffer &buffer, s32 count) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::SetBisRootForHost(u32 id, const fssrv::sf::FspPath &path) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::SetSaveDataSize(s64 size, s64 journal_size) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::SetSaveDataRootPath(const fssrv::sf::FspPath &path) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::DisableAutoSaveDataCreation() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::SetGlobalAccessLogMode(u32 mode) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::GetGlobalAccessLogMode(ams::sf::Out<u32> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OutputAccessLogToSdCard(const ams::sf::InBuffer &buf) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::RegisterUpdatePartition() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OpenRegisteredUpdatePartition(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result FileSystemProxyImpl::GetProgramIndexForAccessLog(ams::sf::Out<u32> out_idx, ams::sf::Out<u32> out_count) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::GetFsStackUsage(ams::sf::Out<u32> out, u32 type) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::UnsetSaveDataRootPath() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OutputMultiProgramTagAccessLog() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::FlushAccessLogOnSdCard() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OutputApplicationInfoAccessLog() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::RegisterDebugConfiguration(u32 key, s64 value) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::UnregisterDebugConfiguration(u32 key) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::OverrideSaveDataTransferTokenSignVerificationKey(const ams::sf::InBuffer &buf) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::CorruptSaveDataFileSystemByOffset(u8 space_id, u64 save_data_id, s64 offset) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
Result FileSystemProxyImpl::OpenCodeFileSystemDeprecated(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, const fssrv::sf::Path &path, ncm::ProgramId program_id) {
|
||||
AMS_ABORT("TODO");
|
||||
AMS_UNUSED(out_fs, path, program_id);
|
||||
@@ -42,12 +458,4 @@ namespace ams::fssrv {
|
||||
AMS_UNUSED(out, process_id);
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::SetCurrentProcess(const ams::sf::ClientProcessId &client_pid) {
|
||||
/* Set current process. */
|
||||
m_process_id = client_pid.GetValue().value;
|
||||
|
||||
/* TODO: Allocate NcaFileSystemService. */
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,77 +16,83 @@
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
|
||||
#include "impl/fssrv_allocator_for_service_framework.hpp"
|
||||
#include "fssrv_retry_utility.hpp"
|
||||
|
||||
namespace ams::fssrv::impl {
|
||||
|
||||
FileInterfaceAdapter::FileInterfaceAdapter(std::unique_ptr<fs::fsa::IFile> &&file, FileSystemInterfaceAdapter *parent, util::unique_lock<fssystem::SemaphoreAdapter> &&sema)
|
||||
: m_parent_filesystem(parent, true), m_base_file(std::move(file)), m_open_count_semaphore(std::move(sema))
|
||||
namespace {
|
||||
|
||||
constexpr const char *RootDirectory = "/";
|
||||
|
||||
}
|
||||
|
||||
FileInterfaceAdapter::FileInterfaceAdapter(std::unique_ptr<fs::fsa::IFile> &&file, FileSystemInterfaceAdapter *parent, bool allow_all)
|
||||
: m_parent_filesystem(parent, true), m_base_file(std::move(file)), m_allow_all_operations(allow_all)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
FileInterfaceAdapter::~FileInterfaceAdapter() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
void FileInterfaceAdapter::InvalidateCache() {
|
||||
AMS_ABORT_UNLESS(m_parent_filesystem->IsDeepRetryEnabled());
|
||||
std::scoped_lock<os::ReaderWriterLock> scoped_write_lock(m_parent_filesystem->GetReaderWriterLockForCacheInvalidation());
|
||||
m_base_file->OperateRange(nullptr, 0, fs::OperationId::Invalidate, 0, std::numeric_limits<s64>::max(), nullptr, 0);
|
||||
}
|
||||
|
||||
Result FileInterfaceAdapter::Read(ams::sf::Out<s64> out, s64 offset, const ams::sf::OutNonSecureBuffer &buffer, s64 size, fs::ReadOption option) {
|
||||
/* TODO: N retries on fs::ResultDataCorrupted, we may want to eventually. */
|
||||
/* TODO: Deep retry */
|
||||
R_UNLESS(offset >= 0, fs::ResultInvalidOffset());
|
||||
R_UNLESS(size >= 0, fs::ResultInvalidSize());
|
||||
/* Check pre-conditions. */
|
||||
R_UNLESS(0 <= offset, fs::ResultInvalidOffset());
|
||||
R_UNLESS(0 <= size, fs::ResultInvalidSize());
|
||||
R_UNLESS(size <= static_cast<s64>(buffer.GetSize()), fs::ResultInvalidSize());
|
||||
|
||||
/* Read the data, retrying on corruption. */
|
||||
size_t read_size = 0;
|
||||
R_TRY(m_base_file->Read(std::addressof(read_size), offset, buffer.GetPointer(), static_cast<size_t>(size), option));
|
||||
R_TRY(RetryFinitelyForDataCorrupted([&] () ALWAYS_INLINE_LAMBDA {
|
||||
R_RETURN(m_base_file->Read(std::addressof(read_size), offset, buffer.GetPointer(), static_cast<size_t>(size), option));
|
||||
}));
|
||||
|
||||
out.SetValue(read_size);
|
||||
return ResultSuccess();
|
||||
/* Set the output size. */
|
||||
*out = read_size;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FileInterfaceAdapter::Write(s64 offset, const ams::sf::InNonSecureBuffer &buffer, s64 size, fs::WriteOption option) {
|
||||
/* TODO: N increases thread priority temporarily when writing. We may want to eventually. */
|
||||
R_UNLESS(offset >= 0, fs::ResultInvalidOffset());
|
||||
R_UNLESS(size >= 0, fs::ResultInvalidSize());
|
||||
/* Check pre-conditions. */
|
||||
R_UNLESS(0 <= offset, fs::ResultInvalidOffset());
|
||||
R_UNLESS(0 <= size, fs::ResultInvalidSize());
|
||||
R_UNLESS(size <= static_cast<s64>(buffer.GetSize()), fs::ResultInvalidSize());
|
||||
|
||||
auto read_lock = m_parent_filesystem->AcquireCacheInvalidationReadLock();
|
||||
return m_base_file->Write(offset, buffer.GetPointer(), size, option);
|
||||
/* Temporarily increase our thread's priority. */
|
||||
fssystem::ScopedThreadPriorityChangerByAccessPriority cp(fssystem::ScopedThreadPriorityChangerByAccessPriority::AccessMode::Write);
|
||||
|
||||
R_RETURN(m_base_file->Write(offset, buffer.GetPointer(), size, option));
|
||||
}
|
||||
|
||||
Result FileInterfaceAdapter::Flush() {
|
||||
auto read_lock = m_parent_filesystem->AcquireCacheInvalidationReadLock();
|
||||
return m_base_file->Flush();
|
||||
R_RETURN(m_base_file->Flush());
|
||||
}
|
||||
|
||||
Result FileInterfaceAdapter::SetSize(s64 size) {
|
||||
R_UNLESS(size >= 0, fs::ResultInvalidSize());
|
||||
auto read_lock = m_parent_filesystem->AcquireCacheInvalidationReadLock();
|
||||
return m_base_file->SetSize(size);
|
||||
R_RETURN(m_base_file->SetSize(size));
|
||||
}
|
||||
|
||||
Result FileInterfaceAdapter::GetSize(ams::sf::Out<s64> out) {
|
||||
auto read_lock = m_parent_filesystem->AcquireCacheInvalidationReadLock();
|
||||
return m_base_file->GetSize(out.GetPointer());
|
||||
/* Get the size, retrying on corruption. */
|
||||
R_RETURN(RetryFinitelyForDataCorrupted([&] () ALWAYS_INLINE_LAMBDA {
|
||||
R_RETURN(m_base_file->GetSize(out.GetPointer()));
|
||||
}));
|
||||
}
|
||||
|
||||
Result FileInterfaceAdapter::OperateRange(ams::sf::Out<fs::FileQueryRangeInfo> out, s32 op_id, s64 offset, s64 size) {
|
||||
/* N includes this redundant check, so we will too. */
|
||||
R_UNLESS(out.GetPointer() != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* Clear the range info. */
|
||||
out->Clear();
|
||||
if (op_id == static_cast<s32>(fs::OperationId::QueryRange)) {
|
||||
auto read_lock = m_parent_filesystem->AcquireCacheInvalidationReadLock();
|
||||
|
||||
if (op_id == static_cast<s32>(fs::OperationId::QueryRange)) {
|
||||
fs::FileQueryRangeInfo info;
|
||||
R_TRY(m_base_file->OperateRange(std::addressof(info), sizeof(info), fs::OperationId::QueryRange, offset, size, nullptr, 0));
|
||||
out->Merge(info);
|
||||
} else if (op_id == static_cast<s32>(fs::OperationId::Invalidate)) {
|
||||
R_TRY(m_base_file->OperateRange(nullptr, 0, fs::OperationId::Invalidate, offset, size, nullptr, 0));
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FileInterfaceAdapter::OperateRangeWithBuffer(const ams::sf::OutNonSecureBuffer &out_buf, const ams::sf::InNonSecureBuffer &in_buf, s32 op_id, s64 offset, s64 size) {
|
||||
@@ -99,265 +105,275 @@ namespace ams::fssrv::impl {
|
||||
/* Lazy load/unprepared operations are always allowed to be performed with buffer. */
|
||||
break;
|
||||
default:
|
||||
/* TODO: Nintendo requires that a class member here be true here, but this class member seems to always be false. */
|
||||
/* If this changes (or reverse engineering is wrong), this should be updated. */
|
||||
return fs::ResultPermissionDenied();
|
||||
R_UNLESS(m_allow_all_operations, fs::ResultPermissionDenied());
|
||||
}
|
||||
|
||||
/* Perform the operation. */
|
||||
R_TRY(m_base_file->OperateRange(out_buf.GetPointer(), out_buf.GetSize(), static_cast<fs::OperationId>(op_id), offset, size, in_buf.GetPointer(), in_buf.GetSize()));
|
||||
|
||||
return ResultSuccess();
|
||||
R_RETURN(m_base_file->OperateRange(out_buf.GetPointer(), out_buf.GetSize(), static_cast<fs::OperationId>(op_id), offset, size, in_buf.GetPointer(), in_buf.GetSize()));
|
||||
}
|
||||
|
||||
DirectoryInterfaceAdapter::DirectoryInterfaceAdapter(std::unique_ptr<fs::fsa::IDirectory> &&dir, FileSystemInterfaceAdapter *parent, util::unique_lock<fssystem::SemaphoreAdapter> &&sema)
|
||||
: m_parent_filesystem(parent, true), m_base_dir(std::move(dir)), m_open_count_semaphore(std::move(sema))
|
||||
DirectoryInterfaceAdapter::DirectoryInterfaceAdapter(std::unique_ptr<fs::fsa::IDirectory> &&dir, FileSystemInterfaceAdapter *parent, bool allow_all)
|
||||
: m_parent_filesystem(parent, true), m_base_dir(std::move(dir)), m_allow_all_operations(allow_all)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
DirectoryInterfaceAdapter::~DirectoryInterfaceAdapter() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
Result DirectoryInterfaceAdapter::Read(ams::sf::Out<s64> out, const ams::sf::OutBuffer &out_entries) {
|
||||
auto read_lock = m_parent_filesystem->AcquireCacheInvalidationReadLock();
|
||||
|
||||
/* Get the maximum number of entries we can read into the buffer. */
|
||||
const s64 max_num_entries = out_entries.GetSize() / sizeof(fs::DirectoryEntry);
|
||||
R_UNLESS(max_num_entries >= 0, fs::ResultInvalidSize());
|
||||
|
||||
/* TODO: N retries on fs::ResultDataCorrupted, we may want to eventually. */
|
||||
return m_base_dir->Read(out.GetPointer(), reinterpret_cast<fs::DirectoryEntry *>(out_entries.GetPointer()), max_num_entries);
|
||||
/* Get the size, retrying on corruption. */
|
||||
s64 num_read = 0;
|
||||
R_TRY(RetryFinitelyForDataCorrupted([&] () ALWAYS_INLINE_LAMBDA {
|
||||
R_RETURN(m_base_dir->Read(std::addressof(num_read), reinterpret_cast<fs::DirectoryEntry *>(out_entries.GetPointer()), max_num_entries));
|
||||
}));
|
||||
|
||||
/* Set the output. */
|
||||
*out = num_read;
|
||||
R_SUCCEED();
|
||||
|
||||
}
|
||||
|
||||
Result DirectoryInterfaceAdapter::GetEntryCount(ams::sf::Out<s64> out) {
|
||||
auto read_lock = m_parent_filesystem->AcquireCacheInvalidationReadLock();
|
||||
return m_base_dir->GetEntryCount(out.GetPointer());
|
||||
R_RETURN(m_base_dir->GetEntryCount(out.GetPointer()));
|
||||
}
|
||||
|
||||
FileSystemInterfaceAdapter::FileSystemInterfaceAdapter(std::shared_ptr<fs::fsa::IFileSystem> &&fs, bool open_limited)
|
||||
: m_base_fs(std::move(fs)), m_open_count_limited(open_limited), m_deep_retry_enabled(false)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
FileSystemInterfaceAdapter::~FileSystemInterfaceAdapter() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
bool FileSystemInterfaceAdapter::IsDeepRetryEnabled() const {
|
||||
return m_deep_retry_enabled;
|
||||
}
|
||||
|
||||
bool FileSystemInterfaceAdapter::IsAccessFailureDetectionObserved() const {
|
||||
/* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */
|
||||
AMS_ABORT_UNLESS(false);
|
||||
}
|
||||
|
||||
util::optional<std::shared_lock<os::ReaderWriterLock>> FileSystemInterfaceAdapter::AcquireCacheInvalidationReadLock() {
|
||||
util::optional<std::shared_lock<os::ReaderWriterLock>> lock;
|
||||
if (m_deep_retry_enabled) {
|
||||
lock.emplace(m_invalidation_lock);
|
||||
Result FileSystemInterfaceAdapter::SetUpPath(fs::Path *out, const fssrv::sf::Path &sf_path) {
|
||||
/* Initialize the fs path. */
|
||||
if (m_path_flags.IsWindowsPathAllowed()) {
|
||||
R_TRY(out->InitializeWithReplaceUnc(sf_path.str));
|
||||
} else {
|
||||
R_TRY(out->Initialize(sf_path.str));
|
||||
}
|
||||
return lock;
|
||||
}
|
||||
|
||||
os::ReaderWriterLock &FileSystemInterfaceAdapter::GetReaderWriterLockForCacheInvalidation() {
|
||||
return m_invalidation_lock;
|
||||
/* Ensure the path is normalized. */
|
||||
R_RETURN(out->Normalize(m_path_flags));
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::CreateFile(const fssrv::sf::Path &path, s64 size, s32 option) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
|
||||
/* Check pre-conditions. */
|
||||
R_UNLESS(size >= 0, fs::ResultInvalidSize());
|
||||
|
||||
PathNormalizer normalizer(path.str);
|
||||
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
|
||||
/* Normalize the input path. */
|
||||
fs::Path fs_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
|
||||
|
||||
return m_base_fs->CreateFile(normalizer.GetPath(), size, option);
|
||||
R_RETURN(m_base_fs->CreateFile(fs_path, size, option));
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::DeleteFile(const fssrv::sf::Path &path) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
/* Normalize the input path. */
|
||||
fs::Path fs_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
|
||||
|
||||
PathNormalizer normalizer(path.str);
|
||||
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
|
||||
|
||||
return m_base_fs->DeleteFile(normalizer.GetPath());
|
||||
R_RETURN(m_base_fs->DeleteFile(fs_path));
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::CreateDirectory(const fssrv::sf::Path &path) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
/* Normalize the input path. */
|
||||
fs::Path fs_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
|
||||
|
||||
PathNormalizer normalizer(path.str);
|
||||
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
|
||||
/* Sanity check that the directory isn't the root. */
|
||||
R_UNLESS(fs_path != RootDirectory, fs::ResultPathAlreadyExists());
|
||||
|
||||
R_UNLESS(strncmp(normalizer.GetPath(), "/", 2) != 0, fs::ResultPathAlreadyExists());
|
||||
|
||||
return m_base_fs->CreateDirectory(normalizer.GetPath());
|
||||
R_RETURN(m_base_fs->CreateDirectory(fs_path));
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::DeleteDirectory(const fssrv::sf::Path &path) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
/* Normalize the input path. */
|
||||
fs::Path fs_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
|
||||
|
||||
PathNormalizer normalizer(path.str);
|
||||
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
|
||||
/* Sanity check that the directory isn't the root. */
|
||||
R_UNLESS(fs_path != RootDirectory, fs::ResultDirectoryNotDeletable());
|
||||
|
||||
R_UNLESS(strncmp(normalizer.GetPath(), "/", 2) != 0, fs::ResultDirectoryNotDeletable());
|
||||
|
||||
return m_base_fs->DeleteDirectory(normalizer.GetPath());
|
||||
R_RETURN(m_base_fs->DeleteDirectory(fs_path));
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::DeleteDirectoryRecursively(const fssrv::sf::Path &path) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
/* Normalize the input path. */
|
||||
fs::Path fs_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
|
||||
|
||||
PathNormalizer normalizer(path.str);
|
||||
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
|
||||
/* Sanity check that the directory isn't the root. */
|
||||
R_UNLESS(fs_path != RootDirectory, fs::ResultDirectoryNotDeletable());
|
||||
|
||||
R_UNLESS(strncmp(normalizer.GetPath(), "/", 2) != 0, fs::ResultDirectoryNotDeletable());
|
||||
|
||||
return m_base_fs->DeleteDirectoryRecursively(normalizer.GetPath());
|
||||
R_RETURN(m_base_fs->DeleteDirectoryRecursively(fs_path));
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::RenameFile(const fssrv::sf::Path &old_path, const fssrv::sf::Path &new_path) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
/* Normalize the input paths. */
|
||||
fs::Path fs_old_path;
|
||||
fs::Path fs_new_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_old_path), old_path));
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_new_path), new_path));
|
||||
|
||||
PathNormalizer old_normalizer(old_path.str);
|
||||
PathNormalizer new_normalizer(new_path.str);
|
||||
R_UNLESS(old_normalizer.GetPath() != nullptr, old_normalizer.GetResult());
|
||||
R_UNLESS(new_normalizer.GetPath() != nullptr, new_normalizer.GetResult());
|
||||
|
||||
return m_base_fs->RenameFile(old_normalizer.GetPath(), new_normalizer.GetPath());
|
||||
R_RETURN(m_base_fs->RenameFile(fs_old_path, fs_new_path));
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::RenameDirectory(const fssrv::sf::Path &old_path, const fssrv::sf::Path &new_path) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
/* Normalize the input paths. */
|
||||
fs::Path fs_old_path;
|
||||
fs::Path fs_new_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_old_path), old_path));
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_new_path), new_path));
|
||||
|
||||
PathNormalizer old_normalizer(old_path.str);
|
||||
PathNormalizer new_normalizer(new_path.str);
|
||||
R_UNLESS(old_normalizer.GetPath() != nullptr, old_normalizer.GetResult());
|
||||
R_UNLESS(new_normalizer.GetPath() != nullptr, new_normalizer.GetResult());
|
||||
R_UNLESS(!fs::IsSubPath(fs_old_path.GetString(), fs_new_path.GetString()), fs::ResultDirectoryNotRenamable());
|
||||
|
||||
const bool is_subpath = fs::IsSubPath(old_normalizer.GetPath(), new_normalizer.GetPath());
|
||||
R_UNLESS(!is_subpath, fs::ResultDirectoryNotRenamable());
|
||||
|
||||
return m_base_fs->RenameFile(old_normalizer.GetPath(), new_normalizer.GetPath());
|
||||
R_RETURN(m_base_fs->RenameDirectory(fs_old_path, fs_new_path));
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::GetEntryType(ams::sf::Out<u32> out, const fssrv::sf::Path &path) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
|
||||
PathNormalizer normalizer(path.str);
|
||||
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
|
||||
/* Normalize the input path. */
|
||||
fs::Path fs_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
|
||||
|
||||
static_assert(sizeof(*out.GetPointer()) == sizeof(fs::DirectoryEntryType));
|
||||
return m_base_fs->GetEntryType(reinterpret_cast<fs::DirectoryEntryType *>(out.GetPointer()), normalizer.GetPath());
|
||||
R_RETURN(m_base_fs->GetEntryType(reinterpret_cast<fs::DirectoryEntryType *>(out.GetPointer()), fs_path));
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::OpenFile(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFile>> out, const fssrv::sf::Path &path, u32 mode) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
/* Normalize the input path. */
|
||||
fs::Path fs_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
|
||||
|
||||
util::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore;
|
||||
if (m_open_count_limited) {
|
||||
/* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */
|
||||
AMS_ABORT_UNLESS(false);
|
||||
/* Open the file, retrying on corruption. */
|
||||
std::unique_ptr<fs::fsa::IFile> file;
|
||||
R_TRY(RetryFinitelyForDataCorrupted([&] () ALWAYS_INLINE_LAMBDA {
|
||||
R_RETURN(m_base_fs->OpenFile(std::addressof(file), fs_path, static_cast<fs::OpenMode>(mode)));
|
||||
}));
|
||||
|
||||
/* If we're a mitm interface, we should preserve the resulting target object id. */
|
||||
if (m_is_mitm_interface) {
|
||||
/* TODO: This is a hack to get the mitm API to work. Better solution? */
|
||||
const auto target_object_id = file->GetDomainObjectId();
|
||||
|
||||
ams::sf::SharedPointer<fssrv::sf::IFile> file_intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IFile, FileInterfaceAdapter>(std::move(file), this, m_allow_all_operations);
|
||||
R_UNLESS(file_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
|
||||
|
||||
out.SetValue(std::move(file_intf), target_object_id);
|
||||
} else {
|
||||
ams::sf::SharedPointer<fssrv::sf::IFile> file_intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IFile, FileInterfaceAdapter>(std::move(file), this, m_allow_all_operations);
|
||||
R_UNLESS(file_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
|
||||
|
||||
out.SetValue(std::move(file_intf));
|
||||
}
|
||||
|
||||
PathNormalizer normalizer(path.str);
|
||||
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
|
||||
|
||||
/* TODO: N retries on fs::ResultDataCorrupted, we may want to eventually. */
|
||||
std::unique_ptr<fs::fsa::IFile> file;
|
||||
R_TRY(m_base_fs->OpenFile(std::addressof(file), normalizer.GetPath(), static_cast<fs::OpenMode>(mode)));
|
||||
|
||||
/* TODO: This is a hack to get the mitm API to work. Better solution? */
|
||||
const auto target_object_id = file->GetDomainObjectId();
|
||||
|
||||
/* TODO: N creates an nn::fssystem::AsynchronousAccessFile here. */
|
||||
|
||||
ams::sf::SharedPointer<fssrv::sf::IFile> file_intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IFile, FileInterfaceAdapter>(std::move(file), this, std::move(open_count_semaphore));
|
||||
R_UNLESS(file_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
|
||||
|
||||
out.SetValue(std::move(file_intf), target_object_id);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::OpenDirectory(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IDirectory>> out, const fssrv::sf::Path &path, u32 mode) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
/* Normalize the input path. */
|
||||
fs::Path fs_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
|
||||
|
||||
util::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore;
|
||||
if (m_open_count_limited) {
|
||||
/* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */
|
||||
AMS_ABORT_UNLESS(false);
|
||||
/* Open the directory, retrying on corruption. */
|
||||
std::unique_ptr<fs::fsa::IDirectory> dir;
|
||||
R_TRY(RetryFinitelyForDataCorrupted([&] () ALWAYS_INLINE_LAMBDA {
|
||||
R_RETURN(m_base_fs->OpenDirectory(std::addressof(dir), fs_path, static_cast<fs::OpenDirectoryMode>(mode)));
|
||||
}));
|
||||
|
||||
/* If we're a mitm interface, we should preserve the resulting target object id. */
|
||||
if (m_is_mitm_interface) {
|
||||
/* TODO: This is a hack to get the mitm API to work. Better solution? */
|
||||
const auto target_object_id = dir->GetDomainObjectId();
|
||||
|
||||
ams::sf::SharedPointer<fssrv::sf::IDirectory> dir_intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IDirectory, DirectoryInterfaceAdapter>(std::move(dir), this, m_allow_all_operations);
|
||||
R_UNLESS(dir_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
|
||||
|
||||
out.SetValue(std::move(dir_intf), target_object_id);
|
||||
} else {
|
||||
ams::sf::SharedPointer<fssrv::sf::IDirectory> dir_intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IDirectory, DirectoryInterfaceAdapter>(std::move(dir), this, m_allow_all_operations);
|
||||
R_UNLESS(dir_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
|
||||
|
||||
out.SetValue(std::move(dir_intf));
|
||||
}
|
||||
|
||||
PathNormalizer normalizer(path.str);
|
||||
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
|
||||
|
||||
/* TODO: N retries on fs::ResultDataCorrupted, we may want to eventually. */
|
||||
std::unique_ptr<fs::fsa::IDirectory> dir;
|
||||
R_TRY(m_base_fs->OpenDirectory(std::addressof(dir), normalizer.GetPath(), static_cast<fs::OpenDirectoryMode>(mode)));
|
||||
|
||||
/* TODO: This is a hack to get the mitm API to work. Better solution? */
|
||||
const auto target_object_id = dir->GetDomainObjectId();
|
||||
|
||||
ams::sf::SharedPointer<fssrv::sf::IDirectory> dir_intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IDirectory, DirectoryInterfaceAdapter>(std::move(dir), this, std::move(open_count_semaphore));
|
||||
R_UNLESS(dir_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
|
||||
|
||||
out.SetValue(std::move(dir_intf), target_object_id);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::Commit() {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
|
||||
return m_base_fs->Commit();
|
||||
R_RETURN(m_base_fs->Commit());
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::GetFreeSpaceSize(ams::sf::Out<s64> out, const fssrv::sf::Path &path) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
/* Normalize the input path. */
|
||||
fs::Path fs_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
|
||||
|
||||
PathNormalizer normalizer(path.str);
|
||||
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
|
||||
|
||||
return m_base_fs->GetFreeSpaceSize(out.GetPointer(), normalizer.GetPath());
|
||||
R_RETURN(m_base_fs->GetFreeSpaceSize(out.GetPointer(), fs_path));
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::GetTotalSpaceSize(ams::sf::Out<s64> out, const fssrv::sf::Path &path) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
/* Normalize the input path. */
|
||||
fs::Path fs_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
|
||||
|
||||
PathNormalizer normalizer(path.str);
|
||||
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
|
||||
|
||||
return m_base_fs->GetTotalSpaceSize(out.GetPointer(), normalizer.GetPath());
|
||||
R_RETURN(m_base_fs->GetTotalSpaceSize(out.GetPointer(), fs_path));
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::CleanDirectoryRecursively(const fssrv::sf::Path &path) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
/* Normalize the input path. */
|
||||
fs::Path fs_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
|
||||
|
||||
PathNormalizer normalizer(path.str);
|
||||
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
|
||||
|
||||
return m_base_fs->CleanDirectoryRecursively(normalizer.GetPath());
|
||||
R_RETURN(m_base_fs->CleanDirectoryRecursively(fs_path));
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::GetFileTimeStampRaw(ams::sf::Out<fs::FileTimeStampRaw> out, const fssrv::sf::Path &path) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
/* Normalize the input path. */
|
||||
fs::Path fs_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
|
||||
|
||||
PathNormalizer normalizer(path.str);
|
||||
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
|
||||
|
||||
return m_base_fs->GetFileTimeStampRaw(out.GetPointer(), normalizer.GetPath());
|
||||
R_RETURN(m_base_fs->GetFileTimeStampRaw(out.GetPointer(), fs_path));
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::QueryEntry(const ams::sf::OutBuffer &out_buf, const ams::sf::InBuffer &in_buf, s32 query_id, const fssrv::sf::Path &path) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
/* Check that we have permission to perform the operation. */
|
||||
switch (static_cast<fs::fsa::QueryId>(query_id)) {
|
||||
using enum fs::fsa::QueryId;
|
||||
case SetConcatenationFileAttribute:
|
||||
case IsSignedSystemPartitionOnSdCardValid:
|
||||
case QueryUnpreparedFileInformation:
|
||||
/* Only certain operations are unconditionally allowable. */
|
||||
break;
|
||||
default:
|
||||
R_UNLESS(m_allow_all_operations, fs::ResultPermissionDenied());
|
||||
}
|
||||
|
||||
/* TODO: Nintendo does not normalize the path. Should we? */
|
||||
/* Normalize the input path. */
|
||||
fs::Path fs_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
|
||||
|
||||
char *dst = reinterpret_cast< char *>(out_buf.GetPointer());
|
||||
char *dst = reinterpret_cast<char *>(out_buf.GetPointer());
|
||||
const char *src = reinterpret_cast<const char *>(in_buf.GetPointer());
|
||||
return m_base_fs->QueryEntry(dst, out_buf.GetSize(), src, in_buf.GetSize(), static_cast<fs::fsa::QueryId>(query_id), path.str);
|
||||
R_RETURN(m_base_fs->QueryEntry(dst, out_buf.GetSize(), src, in_buf.GetSize(), static_cast<fs::fsa::QueryId>(query_id), fs_path));
|
||||
}
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
Result RemoteFileSystem::OpenFile(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFile>> out, const fssrv::sf::Path &path, u32 mode) {
|
||||
FsFile f;
|
||||
R_TRY(fsFsOpenFile(std::addressof(m_base_fs), path.str, mode, std::addressof(f)));
|
||||
|
||||
auto intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IFile, RemoteFile>(f);
|
||||
R_UNLESS(intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
|
||||
|
||||
out.SetValue(std::move(intf));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result RemoteFileSystem::OpenDirectory(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IDirectory>> out, const fssrv::sf::Path &path, u32 mode) {
|
||||
FsDir d;
|
||||
R_TRY(fsFsOpenDirectory(std::addressof(m_base_fs), path.str, mode, std::addressof(d)));
|
||||
|
||||
auto intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IDirectory, RemoteDirectory>(d);
|
||||
R_UNLESS(intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
|
||||
|
||||
out.SetValue(std::move(intf));
|
||||
R_SUCCEED();
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fssrv {
|
||||
|
||||
Result PathNormalizer::Normalize(const char **out_path, Buffer *out_buf, const char *path, bool preserve_unc, bool preserve_tail_sep, bool has_mount_name) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(out_path != nullptr);
|
||||
AMS_ASSERT(out_buf != nullptr);
|
||||
|
||||
/* Clear output. */
|
||||
*out_path = nullptr;
|
||||
*out_buf = Buffer();
|
||||
|
||||
/* Check if we're normalized. */
|
||||
bool normalized = false;
|
||||
R_TRY(fs::PathNormalizer::IsNormalized(std::addressof(normalized), path, preserve_unc, has_mount_name));
|
||||
|
||||
if (normalized) {
|
||||
/* If we're already normalized, no allocation is needed. */
|
||||
*out_path = path;
|
||||
} else {
|
||||
/* Allocate a new buffer. */
|
||||
auto buffer = fs::impl::MakeUnique<char[]>(fs::EntryNameLengthMax + 1);
|
||||
R_UNLESS(buffer != nullptr, fs::ResultAllocationFailureInPathNormalizer());
|
||||
|
||||
/* Generate normalized path. */
|
||||
size_t normalized_len = 0;
|
||||
R_TRY(fs::PathNormalizer::Normalize(buffer.get(), std::addressof(normalized_len), path, fs::EntryNameLengthMax + 1, preserve_unc, has_mount_name));
|
||||
|
||||
/* Preserve the tail separator, if we should. */
|
||||
if (preserve_tail_sep) {
|
||||
if (fs::PathNormalizer::IsSeparator(path[strnlen(path, fs::EntryNameLengthMax) - 1]) && !fs::PathNormalizer::IsSeparator(buffer[normalized_len - 1])) {
|
||||
AMS_ASSERT(normalized_len < fs::EntryNameLengthMax);
|
||||
buffer[normalized_len] = fs::StringTraits::DirectorySeparator;
|
||||
buffer[normalized_len + 1] = fs::StringTraits::NullTerminator;
|
||||
}
|
||||
}
|
||||
|
||||
/* Save output. */
|
||||
*out_path = buffer.get();
|
||||
*out_buf = std::move(buffer);
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fssrv::impl {
|
||||
|
||||
template<typename F>
|
||||
ALWAYS_INLINE Result RetryFinitelyForDataCorrupted(F f) {
|
||||
/* All official uses of this retry once, for two tries total. */
|
||||
constexpr auto MaxTryCount = 2;
|
||||
|
||||
/* Perform the operation, retrying on fs::ResultDataCorrupted. */
|
||||
auto tries = 0;
|
||||
while (true) {
|
||||
/* Try to perform the operation. */
|
||||
const auto rc = f();
|
||||
|
||||
/* If we should, retry. */
|
||||
if (fs::ResultDataCorrupted::Includes(rc)) {
|
||||
if ((++tries) < MaxTryCount) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure the current attempt succeeded. */
|
||||
R_TRY(rc);
|
||||
|
||||
/* Return success. */
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,80 +15,62 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
|
||||
#include "fssrv_retry_utility.hpp"
|
||||
|
||||
namespace ams::fssrv::impl {
|
||||
|
||||
StorageInterfaceAdapter::StorageInterfaceAdapter(fs::IStorage *storage) : m_base_storage(storage) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
StorageInterfaceAdapter::StorageInterfaceAdapter(std::unique_ptr<fs::IStorage> storage) : m_base_storage(storage.release()) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
StorageInterfaceAdapter::StorageInterfaceAdapter(std::shared_ptr<fs::IStorage> storage) : m_base_storage(std::move(storage)) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
StorageInterfaceAdapter::~StorageInterfaceAdapter() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
util::optional<std::shared_lock<os::ReaderWriterLock>> StorageInterfaceAdapter::AcquireCacheInvalidationReadLock() {
|
||||
util::optional<std::shared_lock<os::ReaderWriterLock>> lock;
|
||||
if (m_deep_retry_enabled) {
|
||||
lock.emplace(m_invalidation_lock);
|
||||
}
|
||||
return lock;
|
||||
}
|
||||
|
||||
Result StorageInterfaceAdapter::Read(s64 offset, const ams::sf::OutNonSecureBuffer &buffer, s64 size) {
|
||||
/* TODO: N retries on fs::ResultDataCorrupted, we may want to eventually. */
|
||||
/* TODO: Deep retry */
|
||||
R_UNLESS(offset >= 0, fs::ResultInvalidOffset());
|
||||
R_UNLESS(size >= 0, fs::ResultInvalidSize());
|
||||
return m_base_storage->Read(offset, buffer.GetPointer(), size);
|
||||
/* Check pre-conditions. */
|
||||
R_UNLESS(0 <= offset, fs::ResultInvalidOffset());
|
||||
R_UNLESS(0 <= size, fs::ResultInvalidSize());
|
||||
R_UNLESS(size <= static_cast<s64>(buffer.GetSize()), fs::ResultInvalidSize());
|
||||
|
||||
R_RETURN(RetryFinitelyForDataCorrupted([&] () ALWAYS_INLINE_LAMBDA {
|
||||
R_RETURN(m_base_storage->Read(offset, buffer.GetPointer(), size));
|
||||
}));
|
||||
}
|
||||
|
||||
Result StorageInterfaceAdapter::Write(s64 offset, const ams::sf::InNonSecureBuffer &buffer, s64 size) {
|
||||
/* TODO: N increases thread priority temporarily when writing. We may want to eventually. */
|
||||
R_UNLESS(offset >= 0, fs::ResultInvalidOffset());
|
||||
R_UNLESS(size >= 0, fs::ResultInvalidSize());
|
||||
/* Check pre-conditions. */
|
||||
R_UNLESS(0 <= offset, fs::ResultInvalidOffset());
|
||||
R_UNLESS(0 <= size, fs::ResultInvalidSize());
|
||||
R_UNLESS(size <= static_cast<s64>(buffer.GetSize()), fs::ResultInvalidSize());
|
||||
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
return m_base_storage->Write(offset, buffer.GetPointer(), size);
|
||||
/* Temporarily increase our thread's priority. */
|
||||
fssystem::ScopedThreadPriorityChangerByAccessPriority cp(fssystem::ScopedThreadPriorityChangerByAccessPriority::AccessMode::Write);
|
||||
|
||||
R_RETURN(m_base_storage->Write(offset, buffer.GetPointer(), size));
|
||||
}
|
||||
|
||||
Result StorageInterfaceAdapter::Flush() {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
return m_base_storage->Flush();
|
||||
R_RETURN(m_base_storage->Flush());
|
||||
}
|
||||
|
||||
Result StorageInterfaceAdapter::SetSize(s64 size) {
|
||||
R_UNLESS(size >= 0, fs::ResultInvalidSize());
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
return m_base_storage->SetSize(size);
|
||||
R_RETURN(m_base_storage->SetSize(size));
|
||||
}
|
||||
|
||||
Result StorageInterfaceAdapter::GetSize(ams::sf::Out<s64> out) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
return m_base_storage->GetSize(out.GetPointer());
|
||||
R_RETURN(m_base_storage->GetSize(out.GetPointer()));
|
||||
}
|
||||
|
||||
Result StorageInterfaceAdapter::OperateRange(ams::sf::Out<fs::StorageQueryRangeInfo> out, s32 op_id, s64 offset, s64 size) {
|
||||
/* N includes this redundant check, so we will too. */
|
||||
R_UNLESS(out.GetPointer() != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* Clear the range info. */
|
||||
out->Clear();
|
||||
if (op_id == static_cast<s32>(fs::OperationId::QueryRange)) {
|
||||
auto read_lock = this->AcquireCacheInvalidationReadLock();
|
||||
|
||||
fs::StorageQueryRangeInfo info;
|
||||
if (op_id == static_cast<s32>(fs::OperationId::QueryRange)) {
|
||||
fs::FileQueryRangeInfo info;
|
||||
R_TRY(m_base_storage->OperateRange(std::addressof(info), sizeof(info), fs::OperationId::QueryRange, offset, size, nullptr, 0));
|
||||
out->Merge(info);
|
||||
} else if (op_id == static_cast<s32>(fs::OperationId::Invalidate)) {
|
||||
R_TRY(m_base_storage->OperateRange(nullptr, 0, fs::OperationId::Invalidate, offset, size, nullptr, 0));
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,15 +20,7 @@ namespace ams::fssrv::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit os::SdkMutex g_mutex;
|
||||
constinit bool g_initialized = false;
|
||||
|
||||
constinit u64 g_initial_process_id_min = 0;
|
||||
constinit u64 g_initial_process_id_max = 0;
|
||||
|
||||
constinit u64 g_current_process_id = 0;
|
||||
|
||||
constinit std::aligned_storage<0x80>::type g_static_buffer_for_program_info_for_initial_process;
|
||||
constinit std::aligned_storage<0x80>::type g_static_buffer_for_program_info_for_initial_process = {};
|
||||
|
||||
template<typename T>
|
||||
class StaticAllocatorForProgramInfoForInitialProcess : public std::allocator<T> {
|
||||
@@ -56,6 +48,16 @@ namespace ams::fssrv::impl {
|
||||
constexpr const u32 FileAccessControlForInitialProgram[0x1C / sizeof(u32)] = {0x00000001, 0x00000000, 0x80000000, 0x0000001C, 0x00000000, 0x0000001C, 0x00000000};
|
||||
constexpr const u32 FileAccessControlDescForInitialProgram[0x2C / sizeof(u32)] = {0x00000001, 0x00000000, 0x80000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF};
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
|
||||
constinit os::SdkMutex g_mutex;
|
||||
constinit bool g_initialized = false;
|
||||
|
||||
constinit u64 g_initial_process_id_min = 0;
|
||||
constinit u64 g_initial_process_id_max = 0;
|
||||
|
||||
constinit u64 g_current_process_id = 0;
|
||||
|
||||
ALWAYS_INLINE void InitializeInitialAndCurrentProcessId() {
|
||||
if (AMS_UNLIKELY(!g_initialized)) {
|
||||
std::scoped_lock lk(g_mutex);
|
||||
@@ -75,45 +77,49 @@ namespace ams::fssrv::impl {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
std::shared_ptr<ProgramInfo> ProgramInfo::GetProgramInfoForInitialProcess() {
|
||||
static constinit os::SdkMutex s_mutex;
|
||||
static constinit bool s_initialized = false;
|
||||
static constinit std::shared_ptr<ProgramInfo> s_initial_program_info = nullptr;
|
||||
class ProgramInfoHelper : public ProgramInfo {
|
||||
public:
|
||||
ProgramInfoHelper(const void *data, s64 data_size, const void *desc, s64 desc_size) : ProgramInfo(data, data_size, desc, desc_size) { /* ... */ }
|
||||
};
|
||||
|
||||
/* Ensure we've initialized the program info. */
|
||||
if (AMS_UNLIKELY(!s_initialized)) {
|
||||
std::scoped_lock lk(s_mutex);
|
||||
if (AMS_LIKELY(!s_initialized)) {
|
||||
class ProgramInfoHelper : public ProgramInfo {
|
||||
public:
|
||||
ProgramInfoHelper(const void *data, s64 data_size, const void *desc, s64 desc_size) : ProgramInfo(data, data_size, desc, desc_size) { /* ... */ }
|
||||
};
|
||||
|
||||
s_initial_program_info = std::allocate_shared<ProgramInfoHelper>(StaticAllocatorForProgramInfoForInitialProcess<char>{}, FileAccessControlForInitialProgram, sizeof(FileAccessControlForInitialProgram), FileAccessControlDescForInitialProgram, sizeof(FileAccessControlDescForInitialProgram));
|
||||
s_initialized = true;
|
||||
}
|
||||
}
|
||||
AMS_FUNCTION_LOCAL_STATIC(std::shared_ptr<ProgramInfo>, s_initial_program_info, std::allocate_shared<ProgramInfoHelper>(StaticAllocatorForProgramInfoForInitialProcess<char>{}, FileAccessControlForInitialProgram, sizeof(FileAccessControlForInitialProgram), FileAccessControlDescForInitialProgram, sizeof(FileAccessControlDescForInitialProgram)));
|
||||
|
||||
return s_initial_program_info;
|
||||
}
|
||||
|
||||
bool IsInitialProgram(u64 process_id) {
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
/* Initialize/sanity check. */
|
||||
InitializeInitialAndCurrentProcessId();
|
||||
AMS_ABORT_UNLESS(g_initial_process_id_min > 0);
|
||||
|
||||
/* Check process id in range. */
|
||||
return g_initial_process_id_min <= process_id && process_id <= g_initial_process_id_max;
|
||||
#elif defined(ATMOSPHERE_OS_WINDOWS) || defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
|
||||
AMS_UNUSED(process_id);
|
||||
return true;
|
||||
#else
|
||||
#error "Unknown os for fssrv::impl::IsInitialProgram"
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsCurrentProcess(u64 process_id) {
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
/* Initialize. */
|
||||
InitializeInitialAndCurrentProcessId();
|
||||
|
||||
return process_id == g_current_process_id;
|
||||
#elif defined(ATMOSPHERE_OS_WINDOWS) || defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
|
||||
AMS_UNUSED(process_id);
|
||||
return true;
|
||||
#else
|
||||
#error "Unknown os for fssrv::impl::IsCurrentProcess"
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace ams::fssrv::impl {
|
||||
public:
|
||||
static std::shared_ptr<ProgramInfo> GetProgramInfoForInitialProcess();
|
||||
private:
|
||||
ProgramInfo(const void *data, s64 data_size, const void *desc, s64 desc_size) : m_process_id(0), m_program_id(0), m_storage_id(static_cast<ncm::StorageId>(0)) /* TODO: m_access_control */ {
|
||||
ProgramInfo(const void *data, s64 data_size, const void *desc, s64 desc_size) : m_process_id(os::InvalidProcessId), m_program_id(ncm::InvalidProgramId), m_storage_id(static_cast<ncm::StorageId>(0)) /* TODO: m_access_control */ {
|
||||
/* TODO */
|
||||
AMS_UNUSED(data, data_size, desc, desc_size);
|
||||
}
|
||||
|
||||
107
libraries/libstratosphere/source/fssrv/impl/fssrv_utility.cpp
Normal file
107
libraries/libstratosphere/source/fssrv/impl/fssrv_utility.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fssrv_utility.hpp"
|
||||
|
||||
#if defined(ATMOSPHERE_OS_LINUX)
|
||||
#include <unistd.h>
|
||||
#elif defined(ATMOSPHERE_OS_MACOS)
|
||||
#include <mach-o/dyld.h>
|
||||
#endif
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
class PathOnExecutionDirectory {
|
||||
private:
|
||||
char m_path[fs::EntryNameLengthMax + 1];
|
||||
public:
|
||||
PathOnExecutionDirectory() {
|
||||
#if defined(ATMOSPHERE_OS_WINDOWS)
|
||||
{
|
||||
/* Get the module file name. */
|
||||
wchar_t module_file_name[fs::EntryNameLengthMax + 1];
|
||||
if (::GetModuleFileNameW(0, module_file_name, util::size(module_file_name)) == 0) {
|
||||
AMS_FS_R_ABORT_UNLESS(fs::ResultUnexpectedInPathOnExecutionDirectoryA());
|
||||
}
|
||||
|
||||
/* Split the path. */
|
||||
wchar_t drive_name[3];
|
||||
wchar_t dir_name[fs::EntryNameLengthMax + 1];
|
||||
::_wsplitpath_s(module_file_name, drive_name, util::size(drive_name), dir_name, util::size(dir_name), nullptr, 0, nullptr, 0);
|
||||
|
||||
/* Print the drive and directory. */
|
||||
wchar_t path[fs::EntryNameLengthMax + 1];
|
||||
::swprintf_s(path, util::size(path), L"%s%s", drive_name, dir_name);
|
||||
|
||||
/* Convert to utf-8. */
|
||||
const auto res = ::WideCharToMultiByte(CP_UTF8, 0, path, -1, m_path, util::size(m_path), nullptr, nullptr);
|
||||
if (res == 0) {
|
||||
AMS_FS_R_ABORT_UNLESS(fs::ResultUnexpectedInPathOnExecutionDirectoryB());
|
||||
}
|
||||
}
|
||||
#elif defined(ATMOSPHERE_OS_LINUX)
|
||||
{
|
||||
char full_path[PATH_MAX] = {};
|
||||
if (::readlink("/proc/self/exe", full_path, sizeof(full_path)) == -1) {
|
||||
AMS_FS_R_ABORT_UNLESS(fs::ResultUnexpectedInPathOnExecutionDirectoryA());
|
||||
}
|
||||
|
||||
const int len = std::strlen(full_path);
|
||||
std::memcpy(m_path, full_path, len + 1);
|
||||
|
||||
for (int i = len - 1; i >= 0; --i) {
|
||||
if (m_path[i] == '/') {
|
||||
m_path[i + 1] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(ATMOSPHERE_OS_MACOS)
|
||||
{
|
||||
char full_path[PATH_MAX + 1] = {};
|
||||
uint32_t size = sizeof(full_path);
|
||||
AMS_ABORT_UNLESS(_NSGetExecutablePath(full_path, std::addressof(size)) == 0);
|
||||
|
||||
const int len = std::strlen(full_path);
|
||||
std::memcpy(m_path, full_path, len + 1);
|
||||
|
||||
for (int i = len - 1; i >= 0; --i) {
|
||||
if (m_path[i] == '/') {
|
||||
m_path[i + 1] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
AMS_ABORT("TODO: Unknown OS for PathOnExecutionDirectory");
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *Get() const {
|
||||
return m_path;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace ams::fssrv::impl {
|
||||
|
||||
const char *GetExecutionDirectoryPath() {
|
||||
AMS_FUNCTION_LOCAL_STATIC(fssystem::PathOnExecutionDirectory, s_path_on_execution_directory);
|
||||
return s_path_on_execution_directory.Get();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fssrv::impl {
|
||||
|
||||
const char *GetExecutionDirectoryPath();
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user