From e79417c37c1a7f97c2dc198f29857a05649166ea Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 13 Feb 2021 01:57:24 -0800 Subject: [PATCH] htcfs: implement OpenDirectory/CloseDirectory --- .../sf/impl/sf_impl_command_serialization.hpp | 9 +- .../source/htcfs/htcfs_client.cpp | 1 - .../source/htcfs/htcfs_client.hpp | 3 + .../source/htcfs/htcfs_client_impl.cpp | 130 ++++++++++++++++++ .../source/htcfs/htcfs_client_impl.hpp | 11 ++ .../htcfs/htcfs_directory_service_object.cpp | 4 +- .../htcfs_file_system_service_object.cpp | 39 +++++- .../source/htcfs/htcfs_header_factory.hpp | 11 ++ .../include/vapours/results/htcfs_results.hpp | 14 +- 9 files changed, 209 insertions(+), 13 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp index 6e576b092..16c2ab463 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp @@ -819,6 +819,7 @@ namespace ams::sf::impl { /* Useful defines. */ using ArgsTypeForInvoke = typename CommandMeta::ArgsTypeForInvoke; + using ArgsType = typename CommandMeta::ArgsType; using BufferArrayType = std::array; using OutRawHolderType = OutRawHolder; using OutHandleHolderType = OutHandleHolder; @@ -1004,8 +1005,8 @@ namespace ams::sf::impl { /* Argument deserialization. */ private: - template::type> - NX_CONSTEXPR T DeserializeArgumentImpl(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const OutRawHolderType &out_raw_holder, const BufferArrayType &buffers, OutHandleHolderType &out_handles_holder, InOutObjectHolderType &in_out_objects_holder) { + template::type> + NX_CONSTEXPR typename std::tuple_element::type DeserializeArgumentImpl(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const OutRawHolderType &out_raw_holder, const BufferArrayType &buffers, OutHandleHolderType &out_handles_holder, InOutObjectHolderType &in_out_objects_holder) { constexpr auto Info = CommandMeta::ArgumentSerializationInfos[Index]; if constexpr (Info.arg_type == ArgumentType::InData) { /* New in rawdata. */ @@ -1051,8 +1052,8 @@ namespace ams::sf::impl { constexpr auto Attributes = CommandMeta::BufferAttributes[Info.buffer_index]; if constexpr (Attributes & SfBufferAttr_In) { /* TODO: AMS_ABORT_UNLESS()? N does not bother. */ - static_assert(std::is_reference::value); - static_assert(std::is_const::type>::value); + using InvokeType = typename std::tuple_element::type; + static_assert(std::same_as); return *reinterpret_cast(buffers[Info.buffer_index].GetAddress()); } else if constexpr (Attributes & SfBufferAttr_Out) { return T(buffers[Info.buffer_index]); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client.cpp index 0d871278d..27972f265 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client.cpp @@ -43,5 +43,4 @@ namespace ams::htcfs { return GetReference(g_client_storage); } - } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp index 0b3da3889..977917da4 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp @@ -24,6 +24,9 @@ namespace ams::htcfs { ClientImpl m_impl; public: Client(htclow::HtclowManager *manager) : m_impl(manager) { /* ... */ } + public: + Result OpenDirectory(s32 *out_handle, const char *path, fs::OpenDirectoryMode mode, bool case_sensitive) { return m_impl.OpenDirectory(out_handle, path, mode, case_sensitive); } + Result CloseDirectory(s32 handle) { return m_impl.CloseDirectory(handle); } }; void InitializeClient(htclow::HtclowManager *manager); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp index ca218350e..d1baf3e24 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp @@ -29,6 +29,10 @@ namespace ams::htcfs { constinit u8 g_cache[32_KB]; + ALWAYS_INLINE Result ConvertNativeResult(s64 value) { + return result::impl::MakeResult(value); + } + } ClientImpl::ClientImpl(htclow::HtclowManager *manager) @@ -188,6 +192,26 @@ namespace ams::htcfs { return ResultSuccess(); } + Result ClientImpl::CheckResponseHeader(const Header &response, PacketType packet_type) { + /* Perform base checks. */ + R_TRY(this->CheckResponseHeaderWithoutVersion(response, packet_type)); + + /* Check the version. */ + R_UNLESS(response.version == m_header_factory.GetVersion(), htcfs::ResultUnexpectedResponseProtocolVersion()); + + return ResultSuccess(); + } + + Result ClientImpl::CheckResponseHeader(const Header &response, PacketType packet_type, s64 body_size) { + /* Perform base checks. */ + R_TRY(this->CheckResponseHeader(response, packet_type)); + + /* Check the body size. */ + R_UNLESS(response.body_size == body_size, htcfs::ResultUnexpectedResponseBodySize()); + + return ResultSuccess(); + } + Result ClientImpl::GetMaxProtocolVersion(s16 *out) { /* Create space for request and response. */ Header request, response; @@ -283,4 +307,110 @@ namespace ams::htcfs { return ResultSuccess(); } + Result ClientImpl::InitializeRpcChannel() { + /* Check that we're not cancelled. */ + R_UNLESS(!m_event.TryWait(), htcfs::ResultConnectionFailure()); + + /* Check that we're connected. */ + R_UNLESS(m_connected, htcfs::ResultConnectionFailure()); + + return ResultSuccess(); + } + + Result ClientImpl::SendRequest(const Header &request, const void *arg1, size_t arg1_size, const void *arg2, size_t arg2_size) { + /* Try to perform an optimized send. */ + if (sizeof(request) + arg1_size + arg2_size < sizeof(m_packet_buffer)) { + /* Setup our packet buffer. */ + std::memcpy(m_packet_buffer, std::addressof(request), sizeof(request)); + if (arg1_size > 0) { + std::memcpy(m_packet_buffer + sizeof(request), arg1, arg1_size); + } + if (arg2_size > 0) { + std::memcpy(m_packet_buffer + sizeof(request) + arg1_size, arg2, arg2_size); + } + + /* Send the request. */ + R_TRY(this->SendToRpcChannel(m_packet_buffer, sizeof(request) + arg1_size + arg2_size)); + } else { + /* We can't perform a single optimized send, so perform three separate sends. */ + R_TRY(this->SendToRpcChannel(std::addressof(request), sizeof(request))); + + if (arg1_size > 0) { + R_TRY(this->SendToRpcChannel(arg1, arg1_size)); + } + + if (arg2_size > 0) { + R_TRY(this->SendToRpcChannel(arg2, arg2_size)); + } + } + + return ResultSuccess(); + } + + Result ClientImpl::OpenDirectory(s32 *out_handle, const char *path, fs::OpenDirectoryMode mode, bool case_sensitive) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + const auto path_len = std::strlen(path); + m_header_factory.MakeOpenDirectoryHeader(std::addressof(request), path_len, mode, case_sensitive); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, path, path_len)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + /* Set the output handle. */ + *out_handle = static_cast(response.params[2]); + + return ResultSuccess(); + } + + Result ClientImpl::CloseDirectory(s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeCloseDirectoryHeader(std::addressof(request), handle); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp index 4e84b404e..b6166d336 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp @@ -52,6 +52,9 @@ namespace ams::htcfs { void Start(); void Cancel(); void Wait(); + public: + Result OpenDirectory(s32 *out_handle, const char *path, fs::OpenDirectoryMode mode, bool case_sensitive); + Result CloseDirectory(s32 handle); private: int WaitAny(htclow::ChannelState state, os::EventType *event); @@ -59,15 +62,23 @@ namespace ams::htcfs { void TearDownProtocol(); Result CheckResponseHeaderWithoutVersion(const Header &response, PacketType packet_type); + Result CheckResponseHeader(const Header &response, PacketType packet_type); + Result CheckResponseHeader(const Header &response, PacketType packet_type, s64 body_size); Result GetMaxProtocolVersion(s16 *out); Result SetProtocolVersion(s16 version); + Result InitializeRpcChannel(); + Result SendToRpcChannel(const void *src, s64 size); Result ReceiveFromRpcChannel(void *dst, s64 size); Result SendToHtclow(const void *src, s64 size, htclow::Channel *channel); Result ReceiveFromHtclow(void *dst, s64 size, htclow::Channel *channel); + + Result SendRequest(const Header &request) { return this->SendRequest(request, nullptr, 0, nullptr, 0); } + Result SendRequest(const Header &request, const void *arg1, size_t arg1_size) { return this->SendRequest(request, arg1, arg1_size, nullptr, 0); } + Result SendRequest(const Header &request, const void *arg1, size_t arg1_size, const void *arg2, size_t arg2_size); }; } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp b/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp index 1d0e53f80..9bd9397ff 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp @@ -15,14 +15,14 @@ */ #include #include "htcfs_directory_service_object.hpp" +#include "htcfs_client.hpp" namespace ams::htcfs { DirectoryServiceObject::DirectoryServiceObject(s32 handle) : m_handle(handle) { /* ... */ } DirectoryServiceObject::~DirectoryServiceObject() { - /* TODO */ - AMS_ABORT("htcfs::GetClient().CloseDirectory(m_handle);"); + htcfs::GetClient().CloseDirectory(m_handle); } Result DirectoryServiceObject::GetEntryCount(ams::sf::Out out) { diff --git a/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp b/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp index 353845c5b..e93d3d6f4 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp @@ -15,9 +15,37 @@ */ #include #include "htcfs_file_system_service_object.hpp" +#include "htcfs_file_service_object.hpp" +#include "htcfs_directory_service_object.hpp" +#include "htcfs_client.hpp" namespace ams::htcfs { + namespace { + + struct DirectoryServiceObjectAllocatorTag; + struct FileServiceObjectAllocatorTag; + + using DirectoryServiceObjectAllocator = ams::sf::ExpHeapStaticAllocator<4_KB, DirectoryServiceObjectAllocatorTag>; + using FileServiceObjectAllocator = ams::sf::ExpHeapStaticAllocator<4_KB, FileServiceObjectAllocatorTag>; + using DirectoryServiceObjectFactory = ams::sf::ObjectFactory; + using FileServiceObjectFactory = ams::sf::ObjectFactory; + + class StaticAllocatorInitializer { + public: + StaticAllocatorInitializer() { + DirectoryServiceObjectAllocator::Initialize(lmem::CreateOption_ThreadSafe); + FileServiceObjectAllocator::Initialize(lmem::CreateOption_ThreadSafe); + } + } g_static_allocator_initializer; + + constexpr bool IsValidPath(const tma::Path &path) { + const auto len = util::Strnlen(path.str, fs::EntryNameLengthMax + 1); + return 0 < len && len < static_cast(fs::EntryNameLengthMax + 1); + } + + } + Result FileSystemServiceObject::OpenFile(sf::Out> out, const tma::Path &path, u32 open_mode, bool case_sensitive) { AMS_ABORT("FileSystemServiceObject::OpenFile"); } @@ -39,7 +67,16 @@ namespace ams::htcfs { } Result FileSystemServiceObject::OpenDirectory(sf::Out> out, const tma::Path &path, s32 open_mode, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::OpenDirectory"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Open the directory. */ + s32 handle; + R_TRY(htcfs::GetClient().OpenDirectory(std::addressof(handle), path.str, static_cast(open_mode), case_sensitive)); + + /* Set the output directory. */ + *out = DirectoryServiceObjectFactory::CreateSharedEmplaced(handle); + return ResultSuccess(); } Result FileSystemServiceObject::DirectoryExists(sf::Out out, const tma::Path &path, bool case_sensitive) { diff --git a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp index 2556abdfb..e19349f73 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp @@ -102,6 +102,9 @@ namespace ams::htcfs { out->params[2] = param2; out->params[3] = param3; out->params[4] = param4; + + /* Clear reserved. */ + out->reserved = 0; } void MakeGetMaxProtocolVersionHeader(Header *out) { @@ -111,6 +114,14 @@ namespace ams::htcfs { void MakeSetProtocolVersionHeader(Header *out, s16 version) { return this->MakeRequestHeader(out, PacketType::SetProtocolVersion, 0, version); } + + void MakeOpenDirectoryHeader(Header *out, int path_len, fs::OpenDirectoryMode mode, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::OpenDirectory, path_len, static_cast(mode), case_sensitive ? 1 : 0); + } + + void MakeCloseDirectoryHeader(Header *out, s32 handle) { + return this->MakeRequestHeader(out, PacketType::CloseDirectory, 0, handle); + } }; } diff --git a/libraries/libvapours/include/vapours/results/htcfs_results.hpp b/libraries/libvapours/include/vapours/results/htcfs_results.hpp index cfad9663b..7fd689338 100644 --- a/libraries/libvapours/include/vapours/results/htcfs_results.hpp +++ b/libraries/libvapours/include/vapours/results/htcfs_results.hpp @@ -22,12 +22,16 @@ namespace ams::htcfs { R_DEFINE_ERROR_RESULT(InvalidArgument, 3); - R_DEFINE_ERROR_RESULT(HtclowChannelClosed, 101); - R_DEFINE_ERROR_RESULT(UnexpectedResponseProtocolId, 111); - R_DEFINE_ERROR_RESULT(UnexpectedResponseProtocolVersion, 112); - R_DEFINE_ERROR_RESULT(UnexpectedResponsePacketCategory, 113); - R_DEFINE_ERROR_RESULT(UnexpectedResponsePacketType, 114); + R_DEFINE_ERROR_RANGE(ConnectionFailure, 100, 199); + R_DEFINE_ERROR_RESULT(HtclowChannelClosed, 101); + + R_DEFINE_ERROR_RANGE(UnexpectedResponse, 110, 119); + R_DEFINE_ERROR_RESULT(UnexpectedResponseProtocolId, 111); + R_DEFINE_ERROR_RESULT(UnexpectedResponseProtocolVersion, 112); + R_DEFINE_ERROR_RESULT(UnexpectedResponsePacketCategory, 113); + R_DEFINE_ERROR_RESULT(UnexpectedResponsePacketType, 114); + R_DEFINE_ERROR_RESULT(UnexpectedResponseBodySize, 115); R_DEFINE_ERROR_RESULT(UnknownError, 211); R_DEFINE_ERROR_RESULT(UnsupportedProtocolVersion, 212);