diff --git a/Makefile b/Makefile index 7a616fec..08d494fe 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ include $(DEVKITPRO)/libnx/switch_rules # INCLUDES is a list of directories containing header files #--------------------------------------------------------------------------------- TARGET := $(notdir $(CURDIR)) -SOURCES := source source/ams source/hos source/result source/os source/os/impl source/dd source/fs source/sf source/sf/cmif source/sf/hipc source/dmnt source/spl source/spl/smc source/updater source/patcher source/map source/rnd source/util source/sm source/cfg source/pm source/hid source/ldr source/kvdb source/settings source/boot2 +SOURCES ?= $(shell find source -type d) DATA := data INCLUDES := include diff --git a/include/atmosphere/common_includes.hpp b/include/atmosphere/common_includes.hpp index fec9205c..82887c8a 100644 --- a/include/atmosphere/common_includes.hpp +++ b/include/atmosphere/common_includes.hpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include diff --git a/include/atmosphere/results/fs_results.hpp b/include/atmosphere/results/fs_results.hpp index b3877c33..f7fe264a 100644 --- a/include/atmosphere/results/fs_results.hpp +++ b/include/atmosphere/results/fs_results.hpp @@ -50,6 +50,8 @@ namespace ams::fs { R_DEFINE_ERROR_RANGE(AllocationFailure, 3200, 3499); R_DEFINE_ERROR_RESULT(AllocationFailureInDirectorySaveDataFileSystem, 3321); R_DEFINE_ERROR_RESULT(AllocationFailureInSubDirectoryFileSystem, 3355); + R_DEFINE_ERROR_RESULT(AllocationFailureInPathNormalizer, 3367); + R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemInterfaceAdapter, 3407); R_DEFINE_ERROR_RANGE(MmcAccessFailed, 3500, 3999); @@ -79,6 +81,12 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(DirectoryUnobtainable, 6006); R_DEFINE_ERROR_RESULT(NotNormalized, 6007); + R_DEFINE_ERROR_RANGE(InvalidPathForOperation, 6030, 6059); + R_DEFINE_ERROR_RESULT(DirectoryNotDeletable, 6031); + R_DEFINE_ERROR_RESULT(DirectoryNotRenamable, 6032); + R_DEFINE_ERROR_RESULT(IncompatiblePath, 6033); + R_DEFINE_ERROR_RESULT(RenameToOtherFileSystem, 6034); + R_DEFINE_ERROR_RESULT(InvalidOffset, 6061); R_DEFINE_ERROR_RESULT(InvalidSize, 6062); R_DEFINE_ERROR_RESULT(NullptrArgument, 6063); diff --git a/include/stratosphere.hpp b/include/stratosphere.hpp index 19ba7185..a432ace7 100644 --- a/include/stratosphere.hpp +++ b/include/stratosphere.hpp @@ -51,3 +51,5 @@ /* Include FS last. */ #include "stratosphere/fs.hpp" +#include "stratosphere/fssrv.hpp" +#include "stratosphere/fssystem.hpp" diff --git a/include/stratosphere/fs.hpp b/include/stratosphere/fs.hpp index 08a38d89..2e417f1a 100644 --- a/include/stratosphere/fs.hpp +++ b/include/stratosphere/fs.hpp @@ -24,3 +24,5 @@ #include "fs/fs_remote_storage.hpp" #include "fs/fs_file_storage.hpp" #include "fs/fs_query_range.hpp" +#include "fs/fs_path_tool.hpp" +#include "fs/fs_path_utils.hpp" diff --git a/include/stratosphere/fs/fs_common.hpp b/include/stratosphere/fs/fs_common.hpp index cbeaeede..7e0eaa65 100644 --- a/include/stratosphere/fs/fs_common.hpp +++ b/include/stratosphere/fs/fs_common.hpp @@ -18,3 +18,10 @@ #include "../os.hpp" #include "../ncm.hpp" #include "../sf.hpp" + +namespace ams::fs { + + /* TODO: Better place for this? */ + constexpr inline size_t MountNameLengthMax = 15; + +} diff --git a/include/stratosphere/fs/fs_directory.hpp b/include/stratosphere/fs/fs_directory.hpp index 01c8eb2d..723f0c9f 100644 --- a/include/stratosphere/fs/fs_directory.hpp +++ b/include/stratosphere/fs/fs_directory.hpp @@ -18,6 +18,8 @@ namespace ams::fs { + constexpr inline size_t EntryNameLengthMax = 0x300; + using DirectoryEntry = ::FsDirectoryEntry; } diff --git a/include/stratosphere/fs/fs_path_tool.hpp b/include/stratosphere/fs/fs_path_tool.hpp new file mode 100644 index 00000000..c74c6831 --- /dev/null +++ b/include/stratosphere/fs/fs_path_tool.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include "fs_common.hpp" +#include "../fssrv/fssrv_sf_path.hpp" + +namespace ams::fs { + + namespace StringTraits { + + constexpr inline char DirectorySeparator = '/'; + constexpr inline char DriveSeparator = ':'; + constexpr inline char Dot = '.'; + constexpr inline char NullTerminator = '\x00'; + + } + + class PathTool { + public: + static constexpr fssrv::sf::Path RootPath = fssrv::sf::FspPath::Encode("/"); + public: + static constexpr inline bool IsSeparator(char c) { + return c == StringTraits::DirectorySeparator; + } + + static constexpr inline bool IsNullTerminator(char c) { + return c == StringTraits::NullTerminator; + } + + static constexpr inline bool IsDot(char c) { + return c == StringTraits::Dot; + } + + static constexpr inline bool IsWindowsDriveCharacter(char c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); + } + + static constexpr inline bool IsDriveSeparator(char c) { + return c == StringTraits::DriveSeparator; + } + + static constexpr inline bool IsWindowsAbsolutePath(const char *p) { + return IsWindowsDriveCharacter(p[0]) && IsDriveSeparator(p[1]); + } + + static constexpr inline bool IsCurrentDirectory(const char *p) { + return IsDot(p[0]) && (IsSeparator(p[1]) || IsNullTerminator(p[1])); + } + + static constexpr inline bool IsParentDirectory(const char *p) { + return IsDot(p[0]) && IsDot(p[1]) && (IsSeparator(p[2]) || IsNullTerminator(p[2])); + } + + static Result Normalize(char *out, size_t *out_len, const char *src, size_t max_out_size, bool unc_preserved = false); + static Result IsNormalized(bool *out, const char *path); + + static bool IsSubPath(const char *lhs, const char *rhs); + }; + +} diff --git a/include/stratosphere/fs/fs_path_utils.hpp b/include/stratosphere/fs/fs_path_utils.hpp new file mode 100644 index 00000000..f4598eb9 --- /dev/null +++ b/include/stratosphere/fs/fs_path_utils.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include "fs_common.hpp" +#include "../fssrv/fssrv_sf_path.hpp" + +namespace ams::fs { + + inline void Replace(char *dst, size_t dst_size, char old_char, char new_char) { + for (char *cur = dst; cur < dst + dst_size && *cur != '\x00'; cur++) { + if (*cur == old_char) { + *cur = new_char; + } + } + } + + inline Result FspPathPrintf(fssrv::sf::FspPath *dst, const char *format, ...) { + /* Format the path. */ + std::va_list va_list; + va_start(va_list, format); + const size_t len = std::vsnprintf(dst->str, sizeof(dst->str), format, va_list); + va_end(va_list); + + /* Validate length. */ + R_UNLESS(len < sizeof(dst->str), fs::ResultTooLongPath()); + + /* Fix slashes. */ + Replace(dst->str, sizeof(dst->str) - 1, '\\', '/'); + + return ResultSuccess(); + } + + Result VerifyPath(const char *path, size_t max_path_len, size_t max_name_len); + +} diff --git a/include/stratosphere/fs/fs_remote_filesystem.hpp b/include/stratosphere/fs/fs_remote_filesystem.hpp index d797d44a..8c39b461 100644 --- a/include/stratosphere/fs/fs_remote_filesystem.hpp +++ b/include/stratosphere/fs/fs_remote_filesystem.hpp @@ -57,6 +57,10 @@ namespace ams::fs { /* TODO: How should this be handled? */ return fs::ResultNotImplemented(); } + public: + virtual sf::cmif::DomainObjectId GetDomainObjectId() const override { + return sf::cmif::DomainObjectId{serviceGetObjectId(&this->base_file->s)}; + } }; class RemoteDirectory : public fsa::IDirectory { @@ -78,6 +82,10 @@ namespace ams::fs { virtual Result GetEntryCountImpl(s64 *out) override final { return fsDirGetEntryCount(this->base_dir.get(), out); } + public: + virtual sf::cmif::DomainObjectId GetDomainObjectId() const override { + return sf::cmif::DomainObjectId{serviceGetObjectId(&this->base_dir->s)}; + } }; class RemoteFileSystem : public fsa::IFileSystem { diff --git a/include/stratosphere/fs/fsa/fs_idirectory.hpp b/include/stratosphere/fs/fsa/fs_idirectory.hpp index ab3e06b4..335d7717 100644 --- a/include/stratosphere/fs/fsa/fs_idirectory.hpp +++ b/include/stratosphere/fs/fsa/fs_idirectory.hpp @@ -39,6 +39,9 @@ namespace ams::fs::fsa { R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); return this->GetEntryCountImpl(out); } + public: + /* TODO: This is a hack to allow the mitm API to work. Find a better way? */ + virtual sf::cmif::DomainObjectId GetDomainObjectId() const = 0; protected: /* ...? */ private: diff --git a/include/stratosphere/fs/fsa/fs_ifile.hpp b/include/stratosphere/fs/fsa/fs_ifile.hpp index e81cae59..9ecc920a 100644 --- a/include/stratosphere/fs/fsa/fs_ifile.hpp +++ b/include/stratosphere/fs/fsa/fs_ifile.hpp @@ -78,6 +78,9 @@ namespace ams::fs::fsa { Result OperateRange(OperationId op_id, s64 offset, s64 size) { return this->OperateRangeImpl(nullptr, 0, op_id, offset, size, nullptr, 0); } + public: + /* TODO: This is a hack to allow the mitm API to work. Find a better way? */ + virtual sf::cmif::DomainObjectId GetDomainObjectId() const = 0; protected: /* ...? */ private: diff --git a/include/stratosphere/fs/fsa/fs_ifilesystem.hpp b/include/stratosphere/fs/fsa/fs_ifilesystem.hpp index 7d5ac04e..815cc159 100644 --- a/include/stratosphere/fs/fsa/fs_ifilesystem.hpp +++ b/include/stratosphere/fs/fsa/fs_ifilesystem.hpp @@ -151,9 +151,9 @@ namespace ams::fs::fsa { virtual Result DeleteDirectoryRecursivelyImpl(const char *path) = 0; virtual Result RenameFileImpl(const char *old_path, const char *new_path) = 0; virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) = 0; - virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) = 0; - virtual Result OpenFileImpl(std::unique_ptr *out_file, const char *path, OpenMode mode) = 0; - virtual Result OpenDirectoryImpl(std::unique_ptr *out_dir, const char *path, OpenDirectoryMode mode) = 0; + virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) = 0; + virtual Result OpenFileImpl(std::unique_ptr *out_file, const char *path, fs::OpenMode mode) = 0; + virtual Result OpenDirectoryImpl(std::unique_ptr *out_dir, const char *path, fs::OpenDirectoryMode mode) = 0; virtual Result CommitImpl() = 0; virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) { @@ -166,11 +166,11 @@ namespace ams::fs::fsa { virtual Result CleanDirectoryRecursivelyImpl(const char *path) = 0; - virtual Result GetFileTimeStampRawImpl(FileTimeStampRaw *out, const char *path) { + virtual Result GetFileTimeStampRawImpl(fs::FileTimeStampRaw *out, const char *path) { return fs::ResultNotImplemented(); } - virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, QueryId query, const char *path) { + virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const char *path) { return fs::ResultNotImplemented(); } diff --git a/include/stratosphere/fssrv.hpp b/include/stratosphere/fssrv.hpp new file mode 100644 index 00000000..3f72bb4a --- /dev/null +++ b/include/stratosphere/fssrv.hpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2018-2019 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 . + */ + +#pragma once +#include "fssrv/fssrv_sf_path.hpp" +#include "fssrv/fssrv_path_normalizer.hpp" diff --git a/include/stratosphere/fssrv/fssrv_interface_adapters.hpp b/include/stratosphere/fssrv/fssrv_interface_adapters.hpp new file mode 100644 index 00000000..52fbb718 --- /dev/null +++ b/include/stratosphere/fssrv/fssrv_interface_adapters.hpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include "interface_adapters/fssrv_storage_interface_adapter.hpp" +#include "interface_adapters/fssrv_filesystem_interface_adapter.hpp" diff --git a/include/stratosphere/fssrv/fssrv_path_normalizer.hpp b/include/stratosphere/fssrv/fssrv_path_normalizer.hpp new file mode 100644 index 00000000..3411d961 --- /dev/null +++ b/include/stratosphere/fssrv/fssrv_path_normalizer.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include "../fs/fs_common.hpp" + +namespace ams::fssrv { + + /* This is in fssrv::detail in official code. */ + /* TODO: Consider moving to ::impl? */ + + class PathNormalizer { + public: + enum Option : u32 { + Option_None = BIT(0), + Option_PreserveUnc = BIT(1), + Option_PreserveTailSeparator = BIT(2), + Option_HasMountName = BIT(3), + Option_AcceptEmpty = BIT(4), + }; + private: + using Buffer = std::unique_ptr; + private: + Buffer buffer; + const char *path; + Result result; + private: + static Result Normalize(const char **out_path, Buffer *out_buf, const char *path, bool preserve_unc, bool preserve_tail_sep, bool has_mount_name); + public: + explicit PathNormalizer(const char *p) : buffer(), path(nullptr), result(ResultSuccess()) { + this->result = Normalize(&this->path, &this->buffer, p, false, false, false); + } + + PathNormalizer(const char *p, u32 option) : buffer(), path(nullptr), result(ResultSuccess()) { + if ((option & Option_AcceptEmpty) && p[0] == '\x00') { + this->path = path; + } else { + const bool preserve_unc = (option & Option_PreserveUnc); + const bool preserve_tail_sep = (option & Option_PreserveTailSeparator); + const bool has_mount_name = (option & Option_HasMountName); + this->result = Normalize(&this->path, &this->buffer, p, preserve_unc, preserve_tail_sep, has_mount_name); + } + } + + inline Result GetResult() const { + return this->result; + } + + inline const char * GetPath() const { + return this->path; + } + }; + +} diff --git a/include/stratosphere/fssrv/fssrv_sf_path.hpp b/include/stratosphere/fssrv/fssrv_sf_path.hpp new file mode 100644 index 00000000..a15c3fa5 --- /dev/null +++ b/include/stratosphere/fssrv/fssrv_sf_path.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include "../fs/fs_common.hpp" +#include "../fs/fs_directory.hpp" +#include "../sf/sf_buffer_tags.hpp" + +namespace ams::fssrv::sf { + + struct Path : ams::sf::LargeData { + char str[fs::EntryNameLengthMax + 1]; + + static constexpr Path Encode(const char *p) { + Path path = {}; + for (size_t i = 0; i < sizeof(path) - 1; i++) { + path.str[i] = p[i]; + if (p[i] == '\x00') { + break; + } + } + return path; + } + + static constexpr size_t GetPathLength(const Path &path) { + size_t len = 0; + for (size_t i = 0; i < sizeof(path) - 1 && path.str[i] != '\x00'; i++) { + len++; + } + return len; + } + }; + + static_assert(std::is_pod::value && sizeof(Path) == FS_MAX_PATH); + + using FspPath = Path; + +} diff --git a/include/stratosphere/fssrv/interface_adapters/fssrv_filesystem_interface_adapter.hpp b/include/stratosphere/fssrv/interface_adapters/fssrv_filesystem_interface_adapter.hpp new file mode 100644 index 00000000..970f24bb --- /dev/null +++ b/include/stratosphere/fssrv/interface_adapters/fssrv_filesystem_interface_adapter.hpp @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include "../../fs/fs_common.hpp" +#include "../../fs/fs_file.hpp" +#include "../../fs/fs_directory.hpp" +#include "../../fs/fs_filesystem.hpp" +#include "../../fs/fs_query_range.hpp" +#include "../../fssrv/fssrv_sf_path.hpp" +#include "../../fssystem/fssystem_utility.hpp" + +namespace ams::fs::fsa { + + class IFile; + class IDirectory; + class IFileSystem; + +} + +namespace ams::fssrv::impl { + + class FileSystemInterfaceAdapter; + + class FileInterfaceAdapter final : public ams::sf::IServiceObject { + NON_COPYABLE(FileInterfaceAdapter); + public: + enum class CommandId { + Read = 0, + Write = 1, + Flush = 2, + SetSize = 3, + GetSize = 4, + OperateRange = 5, + }; + private: + std::shared_ptr parent_filesystem; + std::unique_ptr base_file; + std::unique_lock open_count_semaphore; + public: + FileInterfaceAdapter(std::unique_ptr &&file, std::shared_ptr &&parent, std::unique_lock &&sema); + ~FileInterfaceAdapter(); + private: + void InvalidateCache(); + public: + /* Command API. */ + Result Read(ams::sf::Out out, s64 offset, const ams::sf::OutNonSecureBuffer &buffer, s64 size, fs::ReadOption option); + Result Write(s64 offset, const ams::sf::InNonSecureBuffer &buffer, s64 size, fs::WriteOption option); + Result Flush(); + Result SetSize(s64 size); + Result GetSize(ams::sf::Out out); + Result OperateRange(ams::sf::Out out, s32 op_id, s64 offset, s64 size); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* 1.0.0- */ + MAKE_SERVICE_COMMAND_META(Read), + MAKE_SERVICE_COMMAND_META(Write), + MAKE_SERVICE_COMMAND_META(Flush), + MAKE_SERVICE_COMMAND_META(SetSize), + MAKE_SERVICE_COMMAND_META(GetSize), + + /* 4.0.0- */ + MAKE_SERVICE_COMMAND_META(OperateRange, hos::Version_400), + }; + }; + + class DirectoryInterfaceAdapter final : public ams::sf::IServiceObject { + NON_COPYABLE(DirectoryInterfaceAdapter); + public: + enum class CommandId { + Read = 0, + GetEntryCount = 1, + }; + private: + std::shared_ptr parent_filesystem; + std::unique_ptr base_dir; + std::unique_lock open_count_semaphore; + public: + DirectoryInterfaceAdapter(std::unique_ptr &&dir, std::shared_ptr &&parent, std::unique_lock &&sema); + ~DirectoryInterfaceAdapter(); + public: + /* Command API */ + Result Read(ams::sf::Out out, const ams::sf::OutBuffer &out_entries); + Result GetEntryCount(ams::sf::Out out); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(Read), + MAKE_SERVICE_COMMAND_META(GetEntryCount), + }; + }; + + class FileSystemInterfaceAdapter final : std::enable_shared_from_this, public ams::sf::IServiceObject { + NON_COPYABLE(FileSystemInterfaceAdapter); + public: + enum class CommandId { + /* 1.0.0+ */ + CreateFile = 0, + DeleteFile = 1, + CreateDirectory = 2, + DeleteDirectory = 3, + DeleteDirectoryRecursively = 4, + RenameFile = 5, + RenameDirectory = 6, + GetEntryType = 7, + OpenFile = 8, + OpenDirectory = 9, + Commit = 10, + GetFreeSpaceSize = 11, + GetTotalSpaceSize = 12, + + /* 3.0.0+ */ + CleanDirectoryRecursively = 13, + GetFileTimeStampRaw = 14, + + /* 4.0.0+ */ + QueryEntry = 15, + }; + private: + std::shared_ptr base_fs; + std::unique_lock mount_count_semaphore; + os::ReadWriteLock invalidation_lock; + bool open_count_limited; + bool deep_retry_enabled = false; + public: + FileSystemInterfaceAdapter(std::shared_ptr &&fs, bool open_limited); + /* TODO: Other constructors. */ + + ~FileSystemInterfaceAdapter(); + public: + bool IsDeepRetryEnabled() const; + bool IsAccessFailureDetectionObserved() const; + std::optional> AcquireCacheInvalidationReadLock(); + os::ReadWriteLock &GetReadWriteLockForCacheInvalidation(); + public: + /* Command API. */ + Result CreateFile(const fssrv::sf::Path &path, s64 size, s32 option); + Result DeleteFile(const fssrv::sf::Path &path); + Result CreateDirectory(const fssrv::sf::Path &path); + Result DeleteDirectory(const fssrv::sf::Path &path); + Result DeleteDirectoryRecursively(const fssrv::sf::Path &path); + Result RenameFile(const fssrv::sf::Path &old_path, const fssrv::sf::Path &new_path); + Result RenameDirectory(const fssrv::sf::Path &old_path, const fssrv::sf::Path &new_path); + Result GetEntryType(ams::sf::Out out, const fssrv::sf::Path &path); + Result OpenFile(ams::sf::Out> out, const fssrv::sf::Path &path, u32 mode); + Result OpenDirectory(ams::sf::Out> out, const fssrv::sf::Path &path, u32 mode); + Result Commit(); + Result GetFreeSpaceSize(ams::sf::Out out, const fssrv::sf::Path &path); + Result GetTotalSpaceSize(ams::sf::Out out, const fssrv::sf::Path &path); + + Result CleanDirectoryRecursively(const fssrv::sf::Path &path); + Result GetFileTimeStampRaw(ams::sf::Out out, const fssrv::sf::Path &path); + + Result QueryEntry(const ams::sf::OutBuffer &out_buf, const ams::sf::InBuffer &in_buf, s32 query_id, const fssrv::sf::Path &path); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* 1.0.0- */ + MAKE_SERVICE_COMMAND_META(CreateFile), + MAKE_SERVICE_COMMAND_META(DeleteFile), + MAKE_SERVICE_COMMAND_META(CreateDirectory), + MAKE_SERVICE_COMMAND_META(DeleteDirectory), + MAKE_SERVICE_COMMAND_META(DeleteDirectoryRecursively), + MAKE_SERVICE_COMMAND_META(RenameFile), + MAKE_SERVICE_COMMAND_META(RenameDirectory), + MAKE_SERVICE_COMMAND_META(GetEntryType), + MAKE_SERVICE_COMMAND_META(OpenFile), + MAKE_SERVICE_COMMAND_META(OpenDirectory), + MAKE_SERVICE_COMMAND_META(Commit), + MAKE_SERVICE_COMMAND_META(GetFreeSpaceSize), + MAKE_SERVICE_COMMAND_META(GetTotalSpaceSize), + + /* 3.0.0- */ + MAKE_SERVICE_COMMAND_META(CleanDirectoryRecursively, hos::Version_300), + MAKE_SERVICE_COMMAND_META(GetFileTimeStampRaw, hos::Version_300), + + /* 4.0.0- */ + MAKE_SERVICE_COMMAND_META(QueryEntry, hos::Version_400), + }; + }; + +} diff --git a/include/stratosphere/fssrv/interface_adapters/fssrv_storage_interface_adapter.hpp b/include/stratosphere/fssrv/interface_adapters/fssrv_storage_interface_adapter.hpp new file mode 100644 index 00000000..dc404085 --- /dev/null +++ b/include/stratosphere/fssrv/interface_adapters/fssrv_storage_interface_adapter.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include "../../fs/fs_common.hpp" +#include "../../fs/fs_query_range.hpp" +#include "../../fssystem/fssystem_utility.hpp" + +namespace ams::fs { + + class IStorage; + +} + +namespace ams::fssrv::impl { + + class StorageInterfaceAdapter final : public ams::sf::IServiceObject { + NON_COPYABLE(StorageInterfaceAdapter); + public: + enum class CommandId { + Read = 0, + Write = 1, + Flush = 2, + SetSize = 3, + GetSize = 4, + OperateRange = 5, + }; + private: + /* TODO: Nintendo uses fssystem::AsynchronousAccessStorage here. */ + std::shared_ptr base_storage; + std::unique_lock open_count_semaphore; + os::ReadWriteLock invalidation_lock; + /* TODO: DataStorageContext. */ + bool deep_retry_enabled = false; + public: + StorageInterfaceAdapter(fs::IStorage *storage); + StorageInterfaceAdapter(std::unique_ptr storage); + explicit StorageInterfaceAdapter(std::shared_ptr &&storage); + /* TODO: Other constructors. */ + + ~StorageInterfaceAdapter(); + private: + std::optional> AcquireCacheInvalidationReadLock(); + private: + /* Command API. */ + Result Read(s64 offset, const ams::sf::OutNonSecureBuffer &buffer, s64 size); + Result Write(s64 offset, const ams::sf::InNonSecureBuffer &buffer, s64 size); + Result Flush(); + Result SetSize(s64 size); + Result GetSize(ams::sf::Out out); + Result OperateRange(ams::sf::Out out, s32 op_id, s64 offset, s64 size); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* 1.0.0- */ + MAKE_SERVICE_COMMAND_META(Read), + MAKE_SERVICE_COMMAND_META(Write), + MAKE_SERVICE_COMMAND_META(Flush), + MAKE_SERVICE_COMMAND_META(SetSize), + MAKE_SERVICE_COMMAND_META(GetSize), + + /* 4.0.0- */ + MAKE_SERVICE_COMMAND_META(OperateRange, hos::Version_400), + }; + }; + +} diff --git a/include/stratosphere/fssystem.hpp b/include/stratosphere/fssystem.hpp new file mode 100644 index 00000000..90d84444 --- /dev/null +++ b/include/stratosphere/fssystem.hpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018-2019 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 . + */ + +#pragma once +#include "fssystem/fssystem_utility.hpp" +#include "fssystem/fssystem_path_tool.hpp" +#include "fssystem/fssystem_subdirectory_filesystem.hpp" +#include "fssystem/fssystem_directory_redirection_filesystem.hpp" diff --git a/include/stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp b/include/stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp new file mode 100644 index 00000000..3d56f3e6 --- /dev/null +++ b/include/stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include "fssystem_path_resolution_filesystem.hpp" + +namespace ams::fssystem { + + class DirectoryRedirectionFileSystem : public IPathResolutionFileSystem { + NON_COPYABLE(DirectoryRedirectionFileSystem); + private: + using PathResolutionFileSystem = IPathResolutionFileSystem; + private: + char *before_dir; + size_t before_dir_len; + char *after_dir; + size_t after_dir_len; + bool unc_preserved; + public: + DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after); + DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after, bool unc); + + virtual ~DirectoryRedirectionFileSystem(); + private: + Result GetNormalizedDirectoryPath(char **out, size_t *out_size, const char *dir); + Result Initialize(const char *before, const char *after); + public: + Result ResolveFullPath(char *out, size_t out_size, const char *relative_path); + }; + +} diff --git a/include/stratosphere/fssystem/fssystem_path_resolution_filesystem.hpp b/include/stratosphere/fssystem/fssystem_path_resolution_filesystem.hpp new file mode 100644 index 00000000..d0d99e2b --- /dev/null +++ b/include/stratosphere/fssystem/fssystem_path_resolution_filesystem.hpp @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include "../fs/fs_common.hpp" +#include "../fs/fsa/fs_ifile.hpp" +#include "../fs/fsa/fs_idirectory.hpp" +#include "../fs/fsa/fs_ifilesystem.hpp" + +namespace ams::fssystem { + + template + class IPathResolutionFileSystem : public fs::fsa::IFileSystem { + NON_COPYABLE(IPathResolutionFileSystem); + private: + std::shared_ptr shared_fs; + fs::fsa::IFileSystem *base_fs; + bool unc_preserved; + public: + IPathResolutionFileSystem(std::shared_ptr fs) : shared_fs(std::move(fs)), unc_preserved(false) { + this->base_fs = this->shared_fs.get(); + } + + IPathResolutionFileSystem(std::shared_ptr fs, bool unc) : shared_fs(std::move(fs)), unc_preserved(unc) { + this->base_fs = this->shared_fs.get(); + } + + virtual ~IPathResolutionFileSystem() { /* ... */ } + protected: + constexpr inline bool IsUncPreserved() const { + return this->unc_preserved; + } + public: + virtual Result CreateFileImpl(const char *path, s64 size, int option) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->CreateFile(full_path, size, option); + } + + virtual Result DeleteFileImpl(const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->DeleteFile(full_path); + } + + virtual Result CreateDirectoryImpl(const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->CreateDirectory(full_path); + } + + virtual Result DeleteDirectoryImpl(const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->DeleteDirectory(full_path); + } + + virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->DeleteDirectoryRecursively(full_path); + } + + virtual Result RenameFileImpl(const char *old_path, const char *new_path) override { + char old_full_path[fs::EntryNameLengthMax + 1]; + char new_full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(old_full_path, sizeof(old_full_path), old_path)); + R_TRY(static_cast(this)->ResolveFullPath(new_full_path, sizeof(new_full_path), new_path)); + + return this->base_fs->RenameFile(old_path, new_path); + } + + virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override { + char old_full_path[fs::EntryNameLengthMax + 1]; + char new_full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(old_full_path, sizeof(old_full_path), old_path)); + R_TRY(static_cast(this)->ResolveFullPath(new_full_path, sizeof(new_full_path), new_path)); + + return this->base_fs->RenameDirectory(old_path, new_path); + } + + virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->GetEntryType(out, full_path); + } + + virtual Result OpenFileImpl(std::unique_ptr *out_file, const char *path, fs::OpenMode mode) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->OpenFile(out_file, full_path, mode); + } + + virtual Result OpenDirectoryImpl(std::unique_ptr *out_dir, const char *path, fs::OpenDirectoryMode mode) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->OpenDirectory(out_dir, full_path, mode); + } + + virtual Result CommitImpl() override { + return this->base_fs->Rollback(); + } + + virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->GetFreeSpaceSize(out, full_path); + } + + virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->GetTotalSpaceSize(out, full_path); + } + + virtual Result CleanDirectoryRecursivelyImpl(const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->CleanDirectoryRecursively(full_path); + } + + virtual Result GetFileTimeStampRawImpl(fs::FileTimeStampRaw *out, const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->GetFileTimeStampRaw(out, full_path); + } + + virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->QueryEntry(dst, dst_size, src, src_size, query, full_path); + } + + /* These aren't accessible as commands. */ + virtual Result CommitProvisionallyImpl(s64 counter) override { + return this->base_fs->CommitProvisionally(counter); + } + + virtual Result RollbackImpl() override { + return this->base_fs->Rollback(); + } + + virtual Result FlushImpl() override { + return this->base_fs->Flush(); + } + }; + +} diff --git a/include/stratosphere/fssystem/fssystem_path_tool.hpp b/include/stratosphere/fssystem/fssystem_path_tool.hpp new file mode 100644 index 00000000..2e50cce9 --- /dev/null +++ b/include/stratosphere/fssystem/fssystem_path_tool.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include "../fs/fs_common.hpp" +#include "../fs/fs_path_tool.hpp" + +namespace ams::fssystem { + + namespace StringTraits = ::ams::fs::StringTraits; + + using PathTool = ::ams::fs::PathTool; + +} diff --git a/include/stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp b/include/stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp new file mode 100644 index 00000000..0f827e06 --- /dev/null +++ b/include/stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include "fssystem_path_resolution_filesystem.hpp" + +namespace ams::fssystem { + + class SubDirectoryFileSystem : public IPathResolutionFileSystem { + NON_COPYABLE(SubDirectoryFileSystem); + private: + using PathResolutionFileSystem = IPathResolutionFileSystem; + private: + char *base_path; + size_t base_path_len; + bool unc_preserved; + public: + SubDirectoryFileSystem(std::shared_ptr fs, const char *bp); + SubDirectoryFileSystem(std::shared_ptr fs, const char *bp, bool unc); + + virtual ~SubDirectoryFileSystem(); + private: + Result Initialize(const char *bp); + public: + Result ResolveFullPath(char *out, size_t out_size, const char *relative_path); + }; + +} diff --git a/include/stratosphere/fssystem/fssystem_utility.hpp b/include/stratosphere/fssystem/fssystem_utility.hpp new file mode 100644 index 00000000..4376c601 --- /dev/null +++ b/include/stratosphere/fssystem/fssystem_utility.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include "../fs/fs_common.hpp" + +namespace ams::fssystem { + + class SemaphoreAdapter : public os::Semaphore { + public: + SemaphoreAdapter(int c, int mc) : os::Semaphore(c, mc) { /* ... */ } + + bool try_lock() { + return this->TryAcquire(); + } + + void unlock() { + this->Release(); + } + }; + +} diff --git a/include/stratosphere/os.hpp b/include/stratosphere/os.hpp index d899759b..21e54e43 100644 --- a/include/stratosphere/os.hpp +++ b/include/stratosphere/os.hpp @@ -17,6 +17,7 @@ #pragma once #include "os/os_common_types.hpp" +#include "os/os_memory_common.hpp" #include "os/os_managed_handle.hpp" #include "os/os_process_handle.hpp" #include "os/os_mutex.hpp" diff --git a/include/stratosphere/os/os_memory_common.hpp b/include/stratosphere/os/os_memory_common.hpp new file mode 100644 index 00000000..d267cbb6 --- /dev/null +++ b/include/stratosphere/os/os_memory_common.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include + +namespace ams::os { + + constexpr inline size_t MemoryPageSize = 0x1000; + + constexpr inline size_t MemoryBlockUnitSize = 0x200000; + +} diff --git a/include/stratosphere/os/os_semaphore.hpp b/include/stratosphere/os/os_semaphore.hpp index 7cda2c86..0941ce5c 100644 --- a/include/stratosphere/os/os_semaphore.hpp +++ b/include/stratosphere/os/os_semaphore.hpp @@ -15,34 +15,41 @@ */ #pragma once -#include "os_common_types.hpp" +#include "os_mutex.hpp" +#include "os_condvar.hpp" namespace ams::os { + namespace impl { + + class WaitableObjectList; + class WaitableHolderOfSemaphore; + + } + class Semaphore { + friend class impl::WaitableHolderOfSemaphore; NON_COPYABLE(Semaphore); NON_MOVEABLE(Semaphore); private: - ::Semaphore s; + util::TypedStorage waitlist; + os::Mutex mutex; + os::ConditionVariable condvar; + int count; + int max_count; public: - Semaphore() { - semaphoreInit(&s, 0); - } + explicit Semaphore(int c, int mc); + ~Semaphore(); - Semaphore(u64 c) { - semaphoreInit(&s, c); - } + void Acquire(); + bool TryAcquire(); + bool TimedAcquire(u64 timeout); - void Signal() { - semaphoreSignal(&s); - } + void Release(); + void Release(int count); - void Wait() { - semaphoreWait(&s); - } - - bool TryWait() { - return semaphoreTryWait(&s); + constexpr inline int GetCurrentCount() const { + return this->count; } }; diff --git a/include/stratosphere/os/os_thread.hpp b/include/stratosphere/os/os_thread.hpp index d1b35d7c..a3e809e0 100644 --- a/include/stratosphere/os/os_thread.hpp +++ b/include/stratosphere/os/os_thread.hpp @@ -16,6 +16,7 @@ #pragma once #include "os_common_types.hpp" +#include "os_memory_common.hpp" namespace ams::os { @@ -62,9 +63,9 @@ namespace ams::os { class StaticThread { NON_COPYABLE(StaticThread); NON_MOVEABLE(StaticThread); - static_assert(util::IsAligned(StackSize, 0x1000), "StaticThread must have aligned resource size"); + static_assert(util::IsAligned(StackSize, os::MemoryPageSize), "StaticThread must have aligned resource size"); private: - alignas(0x1000) u8 stack_mem[StackSize]; + alignas(os::MemoryPageSize) u8 stack_mem[StackSize]; ::Thread thr; public: constexpr StaticThread() : stack_mem{}, thr{} { /* ... */ } diff --git a/include/stratosphere/os/os_waitable_holder.hpp b/include/stratosphere/os/os_waitable_holder.hpp index ea3216b4..03c9dcf0 100644 --- a/include/stratosphere/os/os_waitable_holder.hpp +++ b/include/stratosphere/os/os_waitable_holder.hpp @@ -25,6 +25,7 @@ namespace ams::os { class InterruptEvent; class Thread; class MessageQueue; + class Semaphore; namespace impl { @@ -47,6 +48,7 @@ namespace ams::os { WaitableHolder(SystemEvent *event); WaitableHolder(InterruptEvent *event); WaitableHolder(Thread *thread); + WaitableHolder(Semaphore *semaphore); WaitableHolder(MessageQueue *message_queue, MessageQueueWaitKind wait_kind); ~WaitableHolder(); diff --git a/source/ams/ams_emummc_api.cpp b/source/ams/ams_emummc_api.cpp index 1eb304f9..4cb1f126 100644 --- a/source/ams/ams_emummc_api.cpp +++ b/source/ams/ams_emummc_api.cpp @@ -74,7 +74,7 @@ namespace ams::emummc { /* Retrieve and cache values. */ { - typename std::aligned_storage<2 * (MaxDirLen + 1), 0x1000>::type path_storage; + typename std::aligned_storage<2 * (MaxDirLen + 1), os::MemoryPageSize>::type path_storage; struct { char file_path[MaxDirLen + 1]; diff --git a/source/dd/dd_io_mappings.cpp b/source/dd/dd_io_mappings.cpp index c423fd4a..4376374a 100644 --- a/source/dd/dd_io_mappings.cpp +++ b/source/dd/dd_io_mappings.cpp @@ -19,7 +19,7 @@ namespace ams::dd { uintptr_t QueryIoMapping(uintptr_t phys_addr, size_t size) { u64 virtual_addr; - const u64 aligned_addr = util::AlignDown(phys_addr, 0x1000); + const u64 aligned_addr = util::AlignDown(phys_addr, os::MemoryPageSize); const size_t offset = phys_addr - aligned_addr; const u64 aligned_size = size + offset; R_TRY_CATCH(svcQueryIoMapping(&virtual_addr, aligned_addr, aligned_size)) { diff --git a/source/fs/fs_path_tool.cpp b/source/fs/fs_path_tool.cpp new file mode 100644 index 00000000..74eac03a --- /dev/null +++ b/source/fs/fs_path_tool.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#include + +namespace ams::fs { + + + Result PathTool::Normalize(char *out, size_t *out_len, const char *src, size_t max_out_size, bool unc_preserved) { + /* Paths must start with / */ + R_UNLESS(IsSeparator(src[0]), fs::ResultInvalidPathFormat()); + + bool skip_next_sep = false; + size_t i = 0; + size_t len = 0; + + while (!IsNullTerminator(src[i])) { + if (IsSeparator(src[i])) { + /* Swallow separators. */ + while (IsSeparator(src[++i])) { } + if (IsNullTerminator(src[i])) { + break; + } + + /* Handle skip if needed */ + if (!skip_next_sep) { + if (len + 1 == max_out_size) { + out[len] = StringTraits::NullTerminator; + if (out_len != nullptr) { + *out_len = len; + } + return fs::ResultTooLongPath(); + } + + out[len++] = StringTraits::DirectorySeparator; + + if (unc_preserved && len == 1) { + while (len < i) { + if (len + 1 == max_out_size) { + out[len] = StringTraits::NullTerminator; + if (out_len != nullptr) { + *out_len = len; + } + return fs::ResultTooLongPath(); + } + out[len++] = StringTraits::DirectorySeparator; + } + } + } + skip_next_sep = false; + } + + /* See length of current dir. */ + size_t dir_len = 0; + while (!IsSeparator(src[i+dir_len]) && !IsNullTerminator(src[i+dir_len])) { + dir_len++; + } + + if (IsCurrentDirectory(&src[i])) { + skip_next_sep = true; + } else if (IsParentDirectory(&src[i])) { + AMS_ASSERT(IsSeparator(out[0])); + AMS_ASSERT(IsSeparator(out[len - 1])); + R_UNLESS(len != 1, fs::ResultDirectoryUnobtainable()); + + /* Walk up a directory. */ + len -= 2; + while (!IsSeparator(out[len])) { + len--; + } + } else { + /* Copy, possibly truncating. */ + if (len + dir_len + 1 <= max_out_size) { + for (size_t j = 0; j < dir_len; j++) { + out[len++] = src[i+j]; + } + } else { + const size_t copy_len = max_out_size - 1 - len; + for (size_t j = 0; j < copy_len; j++) { + out[len++] = src[i+j]; + } + out[len] = StringTraits::NullTerminator; + if (out_len != nullptr) { + *out_len = len; + } + return fs::ResultTooLongPath(); + } + } + + i += dir_len; + } + + if (skip_next_sep) { + len--; + } + + if (len == 0 && max_out_size) { + out[len++] = StringTraits::DirectorySeparator; + } + + R_UNLESS(max_out_size >= len - 1, fs::ResultTooLongPath()); + + /* Null terminate. */ + out[len] = StringTraits::NullTerminator; + if (out_len != nullptr) { + *out_len = len; + } + + /* Assert normalized. */ + bool normalized = false; + AMS_ASSERT(unc_preserved || (R_SUCCEEDED(IsNormalized(&normalized, out)) && normalized)); + + return ResultSuccess(); + } + + Result PathTool::IsNormalized(bool *out, const char *path) { + /* Nintendo uses a state machine here. */ + enum class PathState { + Start, + Normal, + FirstSeparator, + Separator, + CurrentDir, + ParentDir, + WindowsDriveLetter, + }; + + PathState state = PathState::Start; + + for (const char *cur = path; *cur != StringTraits::NullTerminator; cur++) { + const char c = *cur; + switch (state) { + case PathState::Start: + if (IsWindowsDriveCharacter(c)) { + state = PathState::WindowsDriveLetter; + } else if (IsSeparator(c)) { + state = PathState::FirstSeparator; + } else { + return fs::ResultInvalidPathFormat(); + } + break; + case PathState::Normal: + if (IsSeparator(c)) { + state = PathState::Separator; + } + break; + case PathState::FirstSeparator: + case PathState::Separator: + if (IsSeparator(c)) { + *out = false; + return ResultSuccess(); + } else if (IsDot(c)) { + state = PathState::CurrentDir; + } else { + state = PathState::Normal; + } + break; + case PathState::CurrentDir: + if (IsSeparator(c)) { + *out = false; + return ResultSuccess(); + } else if (IsDot(c)) { + state = PathState::ParentDir; + } else { + state = PathState::Normal; + } + break; + case PathState::ParentDir: + if (IsSeparator(c)) { + *out = false; + return ResultSuccess(); + } else { + state = PathState::Normal; + } + break; + case PathState::WindowsDriveLetter: + if (IsDriveSeparator(c)) { + *out = true; + return ResultSuccess(); + } else { + return fs::ResultInvalidPathFormat(); + } + break; + } + } + + switch (state) { + case PathState::Start: + case PathState::WindowsDriveLetter: + return fs::ResultInvalidPathFormat(); + case PathState::FirstSeparator: + case PathState::Normal: + *out = true; + break; + case PathState::CurrentDir: + case PathState::ParentDir: + case PathState::Separator: + *out = false; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return ResultSuccess(); + } + + bool PathTool::IsSubPath(const char *lhs, const char *rhs) { + AMS_ASSERT(lhs != nullptr); + AMS_ASSERT(rhs != nullptr); + + /* Special case certain paths. */ + if (IsSeparator(lhs[0]) && !IsSeparator(lhs[1]) && IsSeparator(rhs[0]) && IsSeparator(rhs[1])) { + return false; + } + if (IsSeparator(rhs[0]) && !IsSeparator(rhs[1]) && IsSeparator(lhs[0]) && IsSeparator(lhs[1])) { + return false; + } + if (IsSeparator(lhs[0]) && IsNullTerminator(lhs[1]) && IsSeparator(rhs[0]) && !IsNullTerminator(rhs[1])) { + return true; + } + if (IsSeparator(rhs[0]) && IsNullTerminator(rhs[1]) && IsSeparator(lhs[0]) && !IsNullTerminator(lhs[1])) { + return true; + } + + /* Check subpath. */ + for (size_t i = 0; /* No terminating condition */; i++) { + if (IsNullTerminator(lhs[i])) { + return IsSeparator(rhs[i]); + } else if (IsNullTerminator(rhs[i])) { + return IsSeparator(lhs[i]); + } else if (lhs[i] != rhs[i]) { + return false; + } + } + } + +} diff --git a/source/fs/fs_path_utils.cpp b/source/fs/fs_path_utils.cpp new file mode 100644 index 00000000..ba249524 --- /dev/null +++ b/source/fs/fs_path_utils.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#include + +namespace ams::fs { + + Result VerifyPath(const char *path, size_t max_path_len, size_t max_name_len) { + const char *cur = path; + size_t name_len = 0; + + for (size_t path_len = 0; path_len <= max_path_len && name_len <= max_name_len; path_len++) { + const char c = *(cur++); + + /* If terminated, we're done. */ + R_UNLESS(c != StringTraits::NullTerminator, ResultSuccess()); + + /* TODO: Nintendo converts the path from utf-8 to utf-32, one character at a time. */ + /* We should do this. */ + + /* Banned characters: :*?<>| */ + R_UNLESS((c != ':' && c != '*' && c != '?' && c != '<' && c != '>' && c != '|'), fs::ResultInvalidCharacter()); + + name_len++; + /* Check for separator. */ + if (c == '\\' || c == '/') { + name_len = 0; + } + + } + + return fs::ResultTooLongPath(); + } + +} diff --git a/source/fssrv/fssrv_filesystem_interface_adapter.cpp b/source/fssrv/fssrv_filesystem_interface_adapter.cpp new file mode 100644 index 00000000..1b667702 --- /dev/null +++ b/source/fssrv/fssrv_filesystem_interface_adapter.cpp @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#include +#include + +namespace ams::fssrv::impl { + + FileInterfaceAdapter::FileInterfaceAdapter(std::unique_ptr &&file, std::shared_ptr &&parent, std::unique_lock &&sema) + : parent_filesystem(std::move(parent)), base_file(std::move(file)), open_count_semaphore(std::move(sema)) + { + /* ... */ + } + + FileInterfaceAdapter::~FileInterfaceAdapter() { + /* ... */ + } + + void FileInterfaceAdapter::InvalidateCache() { + AMS_ASSERT(this->parent_filesystem->IsDeepRetryEnabled()); + std::scoped_lock scoped_write_lock(this->parent_filesystem->GetReadWriteLockForCacheInvalidation()); + this->base_file->OperateRange(nullptr, 0, fs::OperationId::InvalidateCache, 0, std::numeric_limits::max(), nullptr, 0); + } + + Result FileInterfaceAdapter::Read(ams::sf::Out out, s64 offset, const ams::sf::OutNonSecureBuffer &buffer, s64 size, fs::ReadOption option) { + /* TODO: N retries on ResultDataCorrupted, we may want to eventually. */ + /* TODO: Deep retry */ + R_UNLESS(offset >= 0, fs::ResultInvalidOffset()); + R_UNLESS(size >= 0, fs::ResultInvalidSize()); + + size_t read_size = 0; + R_TRY(this->base_file->Read(&read_size, offset, buffer.GetPointer(), static_cast(size), option)); + + out.SetValue(read_size); + return ResultSuccess(); + } + + 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()); + + auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock(); + return this->base_file->Write(offset, buffer.GetPointer(), size, option); + } + + Result FileInterfaceAdapter::Flush() { + auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock(); + return this->base_file->Flush(); + } + + Result FileInterfaceAdapter::SetSize(s64 size) { + R_UNLESS(size >= 0, fs::ResultInvalidSize()); + auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock(); + return this->base_file->SetSize(size); + } + + Result FileInterfaceAdapter::GetSize(ams::sf::Out out) { + auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock(); + return this->base_file->GetSize(out.GetPointer()); + } + + Result FileInterfaceAdapter::OperateRange(ams::sf::Out out, s32 op_id, s64 offset, s64 size) { + /* N includes this redundant check, so we will too. */ + R_UNLESS(out.GetPointer() != nullptr, fs::ResultNullptrArgument()); + + out->Clear(); + if (op_id == static_cast(fs::OperationId::QueryRange)) { + auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock(); + + fs::FileQueryRangeInfo info; + R_TRY(this->base_file->OperateRange(&info, sizeof(info), fs::OperationId::QueryRange, offset, size, nullptr, 0)); + out->Merge(info); + } + + return ResultSuccess(); + } + + DirectoryInterfaceAdapter::DirectoryInterfaceAdapter(std::unique_ptr &&dir, std::shared_ptr &&parent, std::unique_lock &&sema) + : parent_filesystem(std::move(parent)), base_dir(std::move(dir)), open_count_semaphore(std::move(sema)) + { + /* ... */ + } + + DirectoryInterfaceAdapter::~DirectoryInterfaceAdapter() { + /* ... */ + } + + Result DirectoryInterfaceAdapter::Read(ams::sf::Out out, const ams::sf::OutBuffer &out_entries) { + auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock(); + + const size_t max_num_entries = out_entries.GetSize() / sizeof(fs::DirectoryEntry); + R_UNLESS(max_num_entries >= 0, fs::ResultInvalidSize()); + + /* TODO: N retries on ResultDataCorrupted, we may want to eventually. */ + return this->base_dir->Read(out.GetPointer(), reinterpret_cast(out_entries.GetPointer()), max_num_entries); + } + + Result DirectoryInterfaceAdapter::GetEntryCount(ams::sf::Out out) { + auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock(); + return this->base_dir->GetEntryCount(out.GetPointer()); + } + + FileSystemInterfaceAdapter::FileSystemInterfaceAdapter(std::shared_ptr &&fs, bool open_limited) + : base_fs(std::move(fs)), open_count_limited(open_limited), deep_retry_enabled(false) + { + /* ... */ + } + + FileSystemInterfaceAdapter::~FileSystemInterfaceAdapter() { + /* ... */ + } + + bool FileSystemInterfaceAdapter::IsDeepRetryEnabled() const { + return this->deep_retry_enabled; + } + + bool FileSystemInterfaceAdapter::IsAccessFailureDetectionObserved() const { + /* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */ + AMS_ASSERT(false); + } + + std::optional> FileSystemInterfaceAdapter::AcquireCacheInvalidationReadLock() { + std::optional> lock; + if (this->deep_retry_enabled) { + lock.emplace(this->invalidation_lock); + } + return lock; + } + + os::ReadWriteLock &FileSystemInterfaceAdapter::GetReadWriteLockForCacheInvalidation() { + return this->invalidation_lock; + } + + Result FileSystemInterfaceAdapter::CreateFile(const fssrv::sf::Path &path, s64 size, s32 option) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + R_UNLESS(size >= 0, fs::ResultInvalidSize()); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + return this->base_fs->CreateFile(normalizer.GetPath(), size, option); + } + + Result FileSystemInterfaceAdapter::DeleteFile(const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + return this->base_fs->DeleteFile(normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::CreateDirectory(const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + R_UNLESS(strncmp(normalizer.GetPath(), "/", 2) != 0, fs::ResultPathAlreadyExists()); + + return this->base_fs->CreateDirectory(normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::DeleteDirectory(const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + R_UNLESS(strncmp(normalizer.GetPath(), "/", 2) != 0, fs::ResultDirectoryNotDeletable()); + + return this->base_fs->DeleteDirectory(normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::DeleteDirectoryRecursively(const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + R_UNLESS(strncmp(normalizer.GetPath(), "/", 2) != 0, fs::ResultDirectoryNotDeletable()); + + return this->base_fs->DeleteDirectoryRecursively(normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::RenameFile(const fssrv::sf::Path &old_path, const fssrv::sf::Path &new_path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + 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 this->base_fs->RenameFile(old_normalizer.GetPath(), new_normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::RenameDirectory(const fssrv::sf::Path &old_path, const fssrv::sf::Path &new_path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + 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()); + + const bool is_subpath = fssystem::PathTool::IsSubPath(old_normalizer.GetPath(), new_normalizer.GetPath()); + R_UNLESS(!is_subpath, fs::ResultDirectoryNotRenamable()); + + return this->base_fs->RenameFile(old_normalizer.GetPath(), new_normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::GetEntryType(ams::sf::Out out, const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + static_assert(sizeof(*out.GetPointer()) == sizeof(fs::DirectoryEntryType)); + return this->base_fs->GetEntryType(reinterpret_cast(out.GetPointer()), normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::OpenFile(ams::sf::Out> out, const fssrv::sf::Path &path, u32 mode) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + std::unique_lock open_count_semaphore; + if (this->open_count_limited) { + /* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */ + AMS_ASSERT(false); + } + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + /* TODO: N retries on ResultDataCorrupted, we may want to eventually. */ + std::unique_ptr file; + R_TRY(this->base_fs->OpenFile(&file, normalizer.GetPath(), static_cast(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. */ + + std::shared_ptr shared_this = this->shared_from_this(); + std::shared_ptr file_intf = std::make_shared(std::move(file), std::move(shared_this), std::move(open_count_semaphore)); + R_UNLESS(file_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter()); + + out.SetValue(std::move(file_intf), target_object_id); + return ResultSuccess(); + } + + Result FileSystemInterfaceAdapter::OpenDirectory(ams::sf::Out> out, const fssrv::sf::Path &path, u32 mode) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + std::unique_lock open_count_semaphore; + if (this->open_count_limited) { + /* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */ + AMS_ASSERT(false); + } + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + /* TODO: N retries on ResultDataCorrupted, we may want to eventually. */ + std::unique_ptr dir; + R_TRY(this->base_fs->OpenDirectory(&dir, normalizer.GetPath(), static_cast(mode))); + + /* TODO: This is a hack to get the mitm API to work. Better solution? */ + const auto target_object_id = dir->GetDomainObjectId(); + + std::shared_ptr shared_this = this->shared_from_this(); + std::shared_ptr dir_intf = std::make_shared(std::move(dir), std::move(shared_this), std::move(open_count_semaphore)); + R_UNLESS(dir_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter()); + + out.SetValue(std::move(dir_intf), target_object_id); + return ResultSuccess(); + } + + Result FileSystemInterfaceAdapter::Commit() { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + return this->base_fs->Commit(); + } + + Result FileSystemInterfaceAdapter::GetFreeSpaceSize(ams::sf::Out out, const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + return this->base_fs->GetFreeSpaceSize(out.GetPointer(), normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::GetTotalSpaceSize(ams::sf::Out out, const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + return this->base_fs->GetTotalSpaceSize(out.GetPointer(), normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::CleanDirectoryRecursively(const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + return this->base_fs->CleanDirectoryRecursively(normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::GetFileTimeStampRaw(ams::sf::Out out, const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + return this->base_fs->GetFileTimeStampRaw(out.GetPointer(), normalizer.GetPath()); + } + + 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(); + + /* TODO: Nintendo does not normalize the path. Should we? */ + + char *dst = reinterpret_cast< char *>(out_buf.GetPointer()); + const char *src = reinterpret_cast(in_buf.GetPointer()); + return this->base_fs->QueryEntry(dst, out_buf.GetSize(), src, in_buf.GetSize(), static_cast(query_id), path.str); + } + +} diff --git a/source/fssrv/fssrv_path_normalizer.cpp b/source/fssrv/fssrv_path_normalizer.cpp new file mode 100644 index 00000000..3de43473 --- /dev/null +++ b/source/fssrv/fssrv_path_normalizer.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#include + +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) { + /* Clear output. */ + *out_path = nullptr; + *out_buf = Buffer(); + + /* Find start of path. */ + const char *path_start = path; + if (has_mount_name) { + while (path_start < path + fs::MountNameLengthMax + 1) { + if (fssystem::PathTool::IsNullTerminator(*path_start)) { + break; + } else if (fssystem::PathTool::IsDriveSeparator(*(path_start++))) { + break; + } + } + R_UNLESS(path < path_start - 1, fs::ResultInvalidPath()); + R_UNLESS(fssystem::PathTool::IsDriveSeparator(*(path_start - 1)), fs::ResultInvalidPath()); + } + + /* Check if we're normalized. */ + bool normalized = false; + R_TRY(fssystem::PathTool::IsNormalized(&normalized, path_start)); + + if (normalized) { + /* If we're already normalized, no allocation is needed. */ + *out_path = path; + } else { + /* Allocate a new buffer. */ + auto buffer = std::make_unique(fs::EntryNameLengthMax + 1); + R_UNLESS(buffer != nullptr, fs::ResultAllocationFailureInPathNormalizer()); + + /* Copy in mount name. */ + const size_t mount_name_len = path_start - path; + std::memcpy(buffer.get(), path, mount_name_len); + + /* Generate normalized path. */ + size_t normalized_len = 0; + R_TRY(fssystem::PathTool::Normalize(buffer.get() + mount_name_len, &normalized_len, path_start, fs::EntryNameLengthMax + 1 - mount_name_len, preserve_unc)); + + /* Preserve the tail separator, if we should. */ + if (preserve_tail_sep) { + if (fssystem::PathTool::IsSeparator(path[strnlen(path, fs::EntryNameLengthMax) - 1])) { + /* Nintendo doesn't actually validate this. */ + R_UNLESS(mount_name_len + normalized_len < fs::EntryNameLengthMax, fs::ResultTooLongPath()); + buffer[mount_name_len + normalized_len] = fssystem::StringTraits::DirectorySeparator; + buffer[mount_name_len + normalized_len + 1] = fssystem::StringTraits::NullTerminator; + } + } + + /* Save output. */ + *out_path = buffer.get(); + *out_buf = std::move(buffer); + } + + return ResultSuccess(); + } + +} diff --git a/source/fssrv/fssrv_storage_interface_adapter.cpp b/source/fssrv/fssrv_storage_interface_adapter.cpp new file mode 100644 index 00000000..95ecb272 --- /dev/null +++ b/source/fssrv/fssrv_storage_interface_adapter.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#include +#include + +namespace ams::fssrv::impl { + + StorageInterfaceAdapter::StorageInterfaceAdapter(fs::IStorage *storage) : base_storage(storage) { + /* ... */ + } + + StorageInterfaceAdapter::StorageInterfaceAdapter(std::unique_ptr storage) : base_storage(storage.release()) { + /* ... */ + } + + StorageInterfaceAdapter::StorageInterfaceAdapter(std::shared_ptr &&storage) : base_storage(std::move(storage)) { + /* ... */ + } + + StorageInterfaceAdapter::~StorageInterfaceAdapter() { + /* ... */ + } + + std::optional> StorageInterfaceAdapter::AcquireCacheInvalidationReadLock() { + std::optional> lock; + if (this->deep_retry_enabled) { + lock.emplace(this->invalidation_lock); + } + return lock; + } + + Result StorageInterfaceAdapter::Read(s64 offset, const ams::sf::OutNonSecureBuffer &buffer, s64 size) { + /* TODO: N retries on ResultDataCorrupted, we may want to eventually. */ + /* TODO: Deep retry */ + R_UNLESS(offset >= 0, fs::ResultInvalidOffset()); + R_UNLESS(size >= 0, fs::ResultInvalidSize()); + return this->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()); + + auto read_lock = this->AcquireCacheInvalidationReadLock(); + return this->base_storage->Write(offset, buffer.GetPointer(), size); + } + + Result StorageInterfaceAdapter::Flush() { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + return this->base_storage->Flush(); + } + + Result StorageInterfaceAdapter::SetSize(s64 size) { + R_UNLESS(size >= 0, fs::ResultInvalidSize()); + auto read_lock = this->AcquireCacheInvalidationReadLock(); + return this->base_storage->SetSize(size); + } + + Result StorageInterfaceAdapter::GetSize(ams::sf::Out out) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + return this->base_storage->GetSize(out.GetPointer()); + } + + Result StorageInterfaceAdapter::OperateRange(ams::sf::Out out, s32 op_id, s64 offset, s64 size) { + /* N includes this redundant check, so we will too. */ + R_UNLESS(out.GetPointer() != nullptr, fs::ResultNullptrArgument()); + + out->Clear(); + if (op_id == static_cast(fs::OperationId::QueryRange)) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + fs::StorageQueryRangeInfo info; + R_TRY(this->base_storage->OperateRange(&info, sizeof(info), fs::OperationId::QueryRange, offset, size, nullptr, 0)); + out->Merge(info); + } + + return ResultSuccess(); + } + +} diff --git a/source/fssystem/fssystem_directory_redirection_filesystem.cpp b/source/fssystem/fssystem_directory_redirection_filesystem.cpp new file mode 100644 index 00000000..24d17928 --- /dev/null +++ b/source/fssystem/fssystem_directory_redirection_filesystem.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#include + +namespace ams::fssystem { + + DirectoryRedirectionFileSystem::DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after) : PathResolutionFileSystem(fs) { + this->before_dir = nullptr; + this->after_dir = nullptr; + R_ASSERT(this->Initialize(before, after)); + } + + DirectoryRedirectionFileSystem::DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after, bool unc) : PathResolutionFileSystem(fs, unc) { + this->before_dir = nullptr; + this->after_dir = nullptr; + R_ASSERT(this->Initialize(before, after)); + } + + DirectoryRedirectionFileSystem::~DirectoryRedirectionFileSystem() { + if (this->before_dir != nullptr) { + std::free(this->before_dir); + } + if (this->after_dir != nullptr) { + std::free(this->after_dir); + } + } + + Result DirectoryRedirectionFileSystem::GetNormalizedDirectoryPath(char **out, size_t *out_size, const char *dir) { + /* Clear output. */ + *out = nullptr; + *out_size = 0; + + /* Make sure the path isn't too long. */ + R_UNLESS(strnlen(dir, fs::EntryNameLengthMax + 1) <= fs::EntryNameLengthMax, fs::ResultTooLongPath()); + + /* Normalize the path. */ + char normalized_path[fs::EntryNameLengthMax + 2]; + size_t normalized_path_len; + R_TRY(PathTool::Normalize(normalized_path, &normalized_path_len, dir, sizeof(normalized_path), this->IsUncPreserved())); + + /* Ensure terminating '/' */ + if (!PathTool::IsSeparator(normalized_path[normalized_path_len - 1])) { + AMS_ASSERT(normalized_path_len + 2 <= sizeof(normalized_path)); + normalized_path[normalized_path_len] = StringTraits::DirectorySeparator; + normalized_path[normalized_path_len + 1] = StringTraits::NullTerminator; + + normalized_path_len++; + } + + /* Allocate new path. */ + const size_t size = normalized_path_len + 1; + char *new_dir = static_cast(std::malloc(size)); + AMS_ASSERT(new_dir != nullptr); + /* TODO: custom ResultAllocationFailure? */ + + /* Copy path in. */ + std::memcpy(new_dir, normalized_path, normalized_path_len); + new_dir[normalized_path_len] = StringTraits::NullTerminator; + + /* Set output. */ + *out = new_dir; + *out_size = size; + return ResultSuccess(); + } + + Result DirectoryRedirectionFileSystem::Initialize(const char *before, const char *after) { + /* Normalize both directories. */ + this->GetNormalizedDirectoryPath(&this->before_dir, &this->before_dir_len, before); + this->GetNormalizedDirectoryPath(&this->after_dir, &this->after_dir_len, after); + + return ResultSuccess(); + } + + Result DirectoryRedirectionFileSystem::ResolveFullPath(char *out, size_t out_size, const char *relative_path) { + /* Normalize the relative path. */ + char normalized_rel_path[fs::EntryNameLengthMax + 1]; + size_t normalized_rel_path_len; + R_TRY(PathTool::Normalize(normalized_rel_path, &normalized_rel_path_len, relative_path, sizeof(normalized_rel_path), this->IsUncPreserved())); + + const bool is_prefixed = std::memcmp(normalized_rel_path, this->before_dir, this->before_dir_len - 2) == 0 && + (PathTool::IsSeparator(normalized_rel_path[this->before_dir_len - 2]) || PathTool::IsNullTerminator(normalized_rel_path[this->before_dir_len - 2])); + if (is_prefixed) { + const size_t before_prefix_len = this->before_dir_len - 2; + const size_t after_prefix_len = this->after_dir_len - 2; + const size_t final_str_len = after_prefix_len + normalized_rel_path_len - before_prefix_len; + R_UNLESS(final_str_len < out_size, fs::ResultTooLongPath()); + + /* Copy normalized path. */ + std::memcpy(out, this->after_dir, after_prefix_len); + std::memcpy(out + after_prefix_len, normalized_rel_path + before_prefix_len, normalized_rel_path_len - before_prefix_len); + out[final_str_len] = StringTraits::NullTerminator; + } else { + /* Path is not prefixed. */ + R_UNLESS(normalized_rel_path_len + 1 <= out_size, fs::ResultTooLongPath()); + std::memcpy(out, normalized_rel_path, normalized_rel_path_len); + out[normalized_rel_path_len] = StringTraits::NullTerminator; + } + + return ResultSuccess(); + } + +} diff --git a/source/fssystem/fssystem_subdirectory_filesystem.cpp b/source/fssystem/fssystem_subdirectory_filesystem.cpp new file mode 100644 index 00000000..bcfafcac --- /dev/null +++ b/source/fssystem/fssystem_subdirectory_filesystem.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#include + +namespace ams::fssystem { + + SubDirectoryFileSystem::SubDirectoryFileSystem(std::shared_ptr fs, const char *bp) : PathResolutionFileSystem(fs) { + this->base_path = nullptr; + R_ASSERT(this->Initialize(bp)); + } + + SubDirectoryFileSystem::SubDirectoryFileSystem(std::shared_ptr fs, const char *bp, bool unc) : PathResolutionFileSystem(fs, unc) { + this->base_path = nullptr; + R_ASSERT(this->Initialize(bp)); + } + + SubDirectoryFileSystem::~SubDirectoryFileSystem() { + if (this->base_path != nullptr) { + std::free(this->base_path); + } + } + + Result SubDirectoryFileSystem::Initialize(const char *bp) { + /* Make sure the path isn't too long. */ + R_UNLESS(strnlen(bp, fs::EntryNameLengthMax + 1) <= fs::EntryNameLengthMax, fs::ResultTooLongPath()); + + /* Normalize the path. */ + char normalized_path[fs::EntryNameLengthMax + 2]; + size_t normalized_path_len; + R_TRY(PathTool::Normalize(normalized_path, &normalized_path_len, bp, sizeof(normalized_path), this->IsUncPreserved())); + + /* Ensure terminating '/' */ + if (!PathTool::IsSeparator(normalized_path[normalized_path_len - 1])) { + AMS_ASSERT(normalized_path_len + 2 <= sizeof(normalized_path)); + normalized_path[normalized_path_len] = StringTraits::DirectorySeparator; + normalized_path[normalized_path_len + 1] = StringTraits::NullTerminator; + + normalized_path_len++; + } + + /* Allocate new path. */ + this->base_path_len = normalized_path_len + 1; + this->base_path = static_cast(std::malloc(this->base_path_len)); + R_UNLESS(this->base_path != nullptr, fs::ResultAllocationFailureInSubDirectoryFileSystem()); + + /* Copy path in. */ + std::memcpy(this->base_path, normalized_path, normalized_path_len); + this->base_path[normalized_path_len] = StringTraits::NullTerminator; + return ResultSuccess(); + } + + Result SubDirectoryFileSystem::ResolveFullPath(char *out, size_t out_size, const char *relative_path) { + /* Ensure path will fit. */ + R_UNLESS(this->base_path_len + strnlen(relative_path, fs::EntryNameLengthMax + 1) <= out_size, fs::ResultTooLongPath()); + + /* Copy base path. */ + std::memcpy(out, this->base_path, this->base_path_len); + + /* Normalize it. */ + const size_t prefix_size = this->base_path_len - 2; + size_t normalized_len; + return PathTool::Normalize(out + prefix_size, &normalized_len, relative_path, out_size - prefix_size, this->IsUncPreserved()); + } + +} diff --git a/source/os/impl/os_waitable_holder_impl.hpp b/source/os/impl/os_waitable_holder_impl.hpp index ccd7a335..d8704409 100644 --- a/source/os/impl/os_waitable_holder_impl.hpp +++ b/source/os/impl/os_waitable_holder_impl.hpp @@ -19,6 +19,7 @@ #include "os_waitable_holder_of_inter_process_event.hpp" #include "os_waitable_holder_of_interrupt_event.hpp" #include "os_waitable_holder_of_thread.hpp" +#include "os_waitable_holder_of_semaphore.hpp" #include "os_waitable_holder_of_message_queue.hpp" namespace ams::os::impl { @@ -30,6 +31,7 @@ namespace ams::os::impl { TYPED_STORAGE(WaitableHolderOfInterProcessEvent) holder_of_inter_process_event_storage; TYPED_STORAGE(WaitableHolderOfInterruptEvent) holder_of_interrupt_event_storage; TYPED_STORAGE(WaitableHolderOfThread) holder_of_thread_storage; + TYPED_STORAGE(WaitableHolderOfSemaphore) holder_of_semaphore_storage; TYPED_STORAGE(WaitableHolderOfMessageQueueForNotFull) holder_of_mq_for_not_full_storage; TYPED_STORAGE(WaitableHolderOfMessageQueueForNotEmpty) holder_of_mq_for_not_empty_storage; }; @@ -43,6 +45,7 @@ namespace ams::os::impl { CHECK_HOLDER(WaitableHolderOfInterProcessEvent); CHECK_HOLDER(WaitableHolderOfInterruptEvent); CHECK_HOLDER(WaitableHolderOfThread); + CHECK_HOLDER(WaitableHolderOfSemaphore); CHECK_HOLDER(WaitableHolderOfMessageQueueForNotFull); CHECK_HOLDER(WaitableHolderOfMessageQueueForNotEmpty); diff --git a/source/os/impl/os_waitable_holder_of_semaphore.hpp b/source/os/impl/os_waitable_holder_of_semaphore.hpp new file mode 100644 index 00000000..36939ef7 --- /dev/null +++ b/source/os/impl/os_waitable_holder_of_semaphore.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include "os_waitable_holder_base.hpp" +#include "os_waitable_object_list.hpp" + +namespace ams::os::impl { + + class WaitableHolderOfSemaphore : public WaitableHolderOfUserObject { + private: + Semaphore *semaphore; + private: + TriBool IsSignaledImpl() const { + return this->semaphore->count > 0 ? TriBool::True : TriBool::False; + } + public: + explicit WaitableHolderOfSemaphore(Semaphore *s) : semaphore(s) { /* ... */ } + + /* IsSignaled, Link, Unlink implemented. */ + virtual TriBool IsSignaled() const override { + std::scoped_lock lk(this->semaphore->mutex); + return this->IsSignaledImpl(); + } + + virtual TriBool LinkToObjectList() override { + std::scoped_lock lk(this->semaphore->mutex); + + GetReference(this->semaphore->waitlist).LinkWaitableHolder(*this); + return this->IsSignaledImpl(); + } + + virtual void UnlinkFromObjectList() override { + std::scoped_lock lk(this->semaphore->mutex); + + GetReference(this->semaphore->waitlist).UnlinkWaitableHolder(*this); + } + }; + +} diff --git a/source/os/os_semaphore.cpp b/source/os/os_semaphore.cpp new file mode 100644 index 00000000..12c7c532 --- /dev/null +++ b/source/os/os_semaphore.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#include "impl/os_waitable_object_list.hpp" + +namespace ams::os { + + Semaphore::Semaphore(int c, int mc) : count(c), max_count(mc) { + new (GetPointer(this->waitlist)) impl::WaitableObjectList(); + } + + Semaphore::~Semaphore() { + GetReference(this->waitlist).~WaitableObjectList(); + } + + void Semaphore::Acquire() { + std::scoped_lock lk(this->mutex); + + while (this->count == 0) { + this->condvar.Wait(&this->mutex); + } + + this->count--; + } + + bool Semaphore::TryAcquire() { + std::scoped_lock lk(this->mutex); + + if (this->count == 0) { + return false; + } + + this->count--; + return true; + } + + bool Semaphore::TimedAcquire(u64 timeout) { + std::scoped_lock lk(this->mutex); + TimeoutHelper timeout_helper(timeout); + + while (this->count == 0) { + if (timeout_helper.TimedOut()) { + return false; + } + + this->condvar.TimedWait(&this->mutex, timeout_helper.NsUntilTimeout()); + } + + this->count--; + return true; + } + + void Semaphore::Release() { + std::scoped_lock lk(this->mutex); + + AMS_ASSERT(this->count + 1 <= this->max_count); + this->count++; + + this->condvar.Signal(); + GetReference(this->waitlist).SignalAllThreads(); + } + + void Semaphore::Release(int count) { + std::scoped_lock lk(this->mutex); + + AMS_ASSERT(this->count + count <= this->max_count); + this->count += count; + + this->condvar.Broadcast(); + GetReference(this->waitlist).SignalAllThreads(); + } + +} diff --git a/source/os/os_waitable_holder.cpp b/source/os/os_waitable_holder.cpp index 1221e446..d653cd2a 100644 --- a/source/os/os_waitable_holder.cpp +++ b/source/os/os_waitable_holder.cpp @@ -70,6 +70,14 @@ namespace ams::os { this->user_data = 0; } + WaitableHolder::WaitableHolder(Semaphore *semaphore) { + /* Initialize appropriate holder. */ + new (GetPointer(this->impl_storage)) impl::WaitableHolderOfSemaphore(semaphore); + + /* Set user-data. */ + this->user_data = 0; + } + WaitableHolder::WaitableHolder(MessageQueue *message_queue, MessageQueueWaitKind wait_kind) { /* Initialize appropriate holder. */ switch (wait_kind) { diff --git a/source/spl/smc/spl_smc.cpp b/source/spl/smc/spl_smc.cpp index 03668251..bfd4f0e5 100644 --- a/source/spl/smc/spl_smc.cpp +++ b/source/spl/smc/spl_smc.cpp @@ -363,7 +363,7 @@ namespace ams::spl::smc { Result AtmosphereGetEmummcConfig(void *out_config, void *out_paths, u32 storage_id) { const u64 paths = reinterpret_cast(out_paths); - AMS_ASSERT(util::IsAligned(paths, 0x1000)); + AMS_ASSERT(util::IsAligned(paths, os::MemoryPageSize)); SecmonArgs args = {}; args.X[0] = static_cast(FunctionId::AtmosphereGetEmummcConfig); diff --git a/source/updater/updater_api.cpp b/source/updater/updater_api.cpp index cb7a7ddb..510a366e 100644 --- a/source/updater/updater_api.cpp +++ b/source/updater/updater_api.cpp @@ -54,15 +54,9 @@ namespace ams::updater { /* Implementations. */ Result ValidateWorkBuffer(const void *work_buffer, size_t work_buffer_size) { - if (work_buffer_size < BctSize + EksSize) { - return ResultTooSmallWorkBuffer(); - } - if (!util::IsAligned(work_buffer, 0x1000)) { - return ResultNotAlignedWorkBuffer(); - } - if (util::IsAligned(work_buffer_size, 0x200)) { - return ResultNotAlignedWorkBuffer(); - } + R_UNLESS(work_buffer_size >= BctSize + EksSize, ResultTooSmallWorkBuffer()); + R_UNLESS(util::IsAligned(work_buffer, os::MemoryPageSize), ResultNotAlignedWorkBuffer()); + R_UNLESS(util::IsAligned(work_buffer_size, 0x200), ResultNotAlignedWorkBuffer()); return ResultSuccess(); } diff --git a/source/updater/updater_bis_save.cpp b/source/updater/updater_bis_save.cpp index fba42267..30af92cd 100644 --- a/source/updater/updater_bis_save.cpp +++ b/source/updater/updater_bis_save.cpp @@ -30,7 +30,7 @@ namespace ams::updater { Result BisSave::Initialize(void *work_buffer, size_t work_buffer_size) { AMS_ASSERT(work_buffer_size >= SaveSize); - AMS_ASSERT(util::IsAligned(reinterpret_cast(work_buffer), 0x1000)); + AMS_ASSERT(util::IsAligned(reinterpret_cast(work_buffer), os::MemoryPageSize)); AMS_ASSERT(util::IsAligned(work_buffer_size, 0x200)); R_TRY(this->accessor.Initialize());