ams: support building unit test programs on windows/linux/macos

This commit is contained in:
Michael Scire
2022-03-06 12:08:20 -08:00
committed by SciresM
parent 9a38be201a
commit 64a97576d0
756 changed files with 33359 additions and 9372 deletions

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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) {

View File

@@ -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");
}
}

View File

@@ -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
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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
}
}

View File

@@ -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);
}

View 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();
}
}

View File

@@ -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();
}