diff --git a/libraries/libstratosphere/source/htcfs/htcfs_cache_manager.hpp b/libraries/libstratosphere/source/htcfs/htcfs_cache_manager.hpp index deb65fc35..f4b6ad395 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_cache_manager.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_cache_manager.hpp @@ -51,6 +51,16 @@ namespace ams::htcfs { m_has_cached_handle = false; } + void Invalidate(s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + if (m_has_cached_handle && m_cached_handle == handle) { + /* Note that we have no handle. */ + m_has_cached_handle = false; + } + } + void Record(s64 file_size, const void *data, s32 handle, size_t data_size) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp index 6c2396fc5..716d025c9 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp @@ -26,14 +26,40 @@ namespace ams::htcfs { public: Client(htclow::HtclowManager *manager) : m_impl(manager) { /* ... */ } public: + Result OpenFile(s32 *out_handle, const char *path, fs::OpenMode mode, bool case_sensitive) { return ConvertToFsResult(m_impl.OpenFile(out_handle, path, mode, case_sensitive)); } + Result FileExists(bool *out, const char *path, bool case_sensitive) { return ConvertToFsResult(m_impl.FileExists(out, path, case_sensitive)); } + Result DeleteFile(const char *path, bool case_sensitive) { return ConvertToFsResult(m_impl.DeleteFile(path, case_sensitive)); } + Result RenameFile(const char *old_path, const char *new_path, bool case_sensitive) { return ConvertToFsResult(m_impl.RenameFile(old_path, new_path, case_sensitive)); } + Result GetEntryType(fs::DirectoryEntryType *out, const char *path, bool case_sensitive) { return ConvertToFsResult(m_impl.GetEntryType(out, path, case_sensitive)); } Result OpenDirectory(s32 *out_handle, const char *path, fs::OpenDirectoryMode mode, bool case_sensitive) { return ConvertToFsResult(m_impl.OpenDirectory(out_handle, path, mode, case_sensitive)); } + Result DirectoryExists(bool *out, const char *path, bool case_sensitive) { return ConvertToFsResult(m_impl.DirectoryExists(out, path, case_sensitive)); } + Result CreateDirectory(const char *path, bool case_sensitive) { return ConvertToFsResult(m_impl.CreateDirectory(path, case_sensitive)); } + Result DeleteDirectory(const char *path, bool recursively, bool case_sensitive) { return ConvertToFsResult(m_impl.DeleteDirectory(path, recursively, case_sensitive)); } + Result RenameDirectory(const char *old_path, const char *new_path, bool case_sensitive) { return ConvertToFsResult(m_impl.RenameDirectory(old_path, new_path, case_sensitive)); } + Result CreateFile(const char *path, s64 size, bool case_sensitive) { return ConvertToFsResult(m_impl.CreateFile(path, size, case_sensitive)); } + Result GetFileTimeStamp(u64 *out_create, u64 *out_access, u64 *out_modify, const char *path, bool case_sensitive) { return ConvertToFsResult(m_impl.GetFileTimeStamp(out_create, out_access, out_modify, path, case_sensitive)); } + Result GetCaseSensitivePath(char *dst, size_t dst_size, const char *path) { return ConvertToFsResult(m_impl.GetCaseSensitivePath(dst, dst_size, path)); } + Result GetDiskFreeSpace(s64 *out_free, s64 *out_total, s64 *out_total_free, const char *path) { return ConvertToFsResult(m_impl.GetDiskFreeSpace(out_free, out_total, out_total_free, path)); } + Result CloseDirectory(s32 handle) { return ConvertToFsResult(m_impl.CloseDirectory(handle)); } Result GetEntryCount(s64 *out, s32 handle) { return ConvertToFsResult(m_impl.GetEntryCount(out, handle)); } Result ReadDirectory(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, s32 handle) { return ConvertToFsResult(m_impl.ReadDirectory(out, out_entries, max_out_entries, handle)); } - Result ReadDirectoryLarge(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, s32 handle) { return ConvertToFsResult( m_impl.ReadDirectoryLarge(out, out_entries, max_out_entries, handle)); } + Result ReadDirectoryLarge(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, s32 handle) { return ConvertToFsResult(m_impl.ReadDirectoryLarge(out, out_entries, max_out_entries, handle)); } Result GetPriorityForDirectory(s32 *out, s32 handle) { return ConvertToFsResult(m_impl.GetPriorityForDirectory(out, handle)); } Result SetPriorityForDirectory(s32 priority, s32 handle) { return ConvertToFsResult(m_impl.SetPriorityForDirectory(priority, handle)); } + + Result CloseFile(s32 handle) { return ConvertToFsResult(m_impl.CloseFile(handle)); } + + Result ReadFile(s64 *out, void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::ReadOption option) { return ConvertToFsResult(m_impl.ReadFile(out, buffer, handle, offset, buffer_size, option)); } + Result ReadFileLarge(s64 *out, void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::ReadOption option) { return ConvertToFsResult(m_impl.ReadFileLarge(out, buffer, handle, offset, buffer_size, option)); } + Result WriteFile(const void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::WriteOption option) { return ConvertToFsResult(m_impl.WriteFile(buffer, handle, offset, buffer_size, option)); } + Result WriteFileLarge(const void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::WriteOption option) { return ConvertToFsResult(m_impl.WriteFileLarge(buffer, handle, offset, buffer_size, option)); } + Result GetFileSize(s64 *out, s32 handle) { return ConvertToFsResult(m_impl.GetFileSize(out, handle)); } + Result SetFileSize(s64 size, s32 handle) { return ConvertToFsResult(m_impl.SetFileSize(size, handle)); } + Result FlushFile(s32 handle) { return ConvertToFsResult(m_impl.FlushFile(handle)); } + Result GetPriorityForFile(s32 *out, s32 handle) { return ConvertToFsResult(m_impl.GetPriorityForFile(out, handle)); } + Result SetPriorityForFile(s32 priority, s32 handle) { return ConvertToFsResult(m_impl.SetPriorityForFile(priority, 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 ad5a73420..67404632d 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp @@ -27,7 +27,8 @@ namespace ams::htcfs { alignas(os::ThreadStackAlignment) constinit u8 g_monitor_thread_stack[os::MemoryPageSize]; - constinit u8 g_cache[32_KB]; + constexpr size_t FileDataCacheSize = 32_KB; + constinit u8 g_cache[FileDataCacheSize]; ALWAYS_INLINE Result ConvertNativeResult(s64 value) { return result::impl::MakeResult(value); @@ -401,6 +402,191 @@ namespace ams::htcfs { return ResultSuccess(); } + Result ClientImpl::OpenFile(s32 *out_handle, const char *path, fs::OpenMode mode, bool case_sensitive) { + /* Invalidate the cache manager. */ + m_cache_manager.Invalidate(); + + /* 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.MakeOpenFileHeader(std::addressof(request), path_len, mode, case_sensitive, FileDataCacheSize); + + /* 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)); + + /* Check the response body size. */ + R_UNLESS(response.body_size > 0, htcfs::ResultUnexpectedResponseBodySize()); + R_UNLESS(static_cast(response.body_size) <= MaxPacketBodySize, htcfs::ResultUnexpectedResponseBodySize()); + + /* Receive the response body. */ + R_TRY(this->ReceiveFromRpcChannel(m_packet_buffer, response.body_size)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + /* Set our output handle. */ + *out_handle = response.params[2]; + + /* If we have data to cache, cache it. */ + if (response.params[3]) { + m_cache_manager.Record(response.params[4], m_packet_buffer, response.params[2], response.body_size); + } + + return ResultSuccess(); + } + + Result ClientImpl::FileExists(bool *out, const char *path, 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.MakeFileExistsHeader(std::addressof(request), path_len, 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. */ + *out = response.params[2] != 0; + + return ResultSuccess(); + } + + Result ClientImpl::DeleteFile(const char *path, 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.MakeDeleteFileHeader(std::addressof(request), path_len, 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])); + + return ResultSuccess(); + } + + Result ClientImpl::RenameFile(const char *old_path, const char *new_path, 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 old_path_len = std::strlen(new_path); + const auto new_path_len = std::strlen(old_path); + m_header_factory.MakeRenameFileHeader(std::addressof(request), old_path_len, new_path_len, case_sensitive); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, old_path, old_path_len, new_path, new_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])); + + return ResultSuccess(); + } + + Result ClientImpl::GetEntryType(fs::DirectoryEntryType *out, const char *path, 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.MakeGetEntryTypeHeader(std::addressof(request), path_len, 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. */ + *out = static_cast(response.params[2]); + + 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); @@ -436,6 +622,297 @@ namespace ams::htcfs { return ResultSuccess(); } + Result ClientImpl::DirectoryExists(bool *out, const char *path, 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.MakeDirectoryExistsHeader(std::addressof(request), path_len, 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. */ + *out = response.params[2] != 0; + + return ResultSuccess(); + } + + Result ClientImpl::CreateDirectory(const char *path, 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.MakeCreateDirectoryHeader(std::addressof(request), path_len, 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])); + + return ResultSuccess(); + } + + Result ClientImpl::DeleteDirectory(const char *path, bool recursively, 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.MakeDeleteDirectoryHeader(std::addressof(request), path_len, recursively, 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])); + + return ResultSuccess(); + } + + Result ClientImpl::RenameDirectory(const char *old_path, const char *new_path, 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 old_path_len = std::strlen(new_path); + const auto new_path_len = std::strlen(old_path); + m_header_factory.MakeRenameDirectoryHeader(std::addressof(request), old_path_len, new_path_len, case_sensitive); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, old_path, old_path_len, new_path, new_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])); + + return ResultSuccess(); + } + + Result ClientImpl::CreateFile(const char *path, s64 size, 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.MakeCreateFileHeader(std::addressof(request), path_len, size, 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])); + + return ResultSuccess(); + } + + Result ClientImpl::GetFileTimeStamp(u64 *out_create, u64 *out_access, u64 *out_modify, const char *path, 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.MakeGetFileTimeStampHeader(std::addressof(request), path_len, 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 output. */ + *out_create = static_cast(response.params[2]); + *out_access = static_cast(response.params[3]); + *out_modify = static_cast(response.params[4]); + + return ResultSuccess(); + } + + Result ClientImpl::GetCaseSensitivePath(char *dst, size_t dst_size, const char *path) { + /* Sanity check the output buffer. */ + R_UNLESS(util::IsIntValueRepresentable(dst_size), htcfs::ResultInvalidArgument()); + R_UNLESS(dst_size > 0, htcfs::ResultInvalidArgument()); + + /* 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.MakeGetCaseSensitivePathHeader(std::addressof(request), path_len); + + /* 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)); + + /* Check that we succeeded. */ + const auto htcfs_result = ConvertHtcfsResult(response.params[0]); + if (R_FAILED(htcfs_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return htcfs_result; + } + + /* Check our operation's result. */ + const auto native_result = ConvertNativeResult(response.params[1]); + if (R_FAILED(native_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return native_result; + } + + /* Check the body size. */ + R_UNLESS(response.body_size < static_cast(dst_size), htcfs::ResultUnexpectedResponseBodySize()); + + /* Receive the response body. */ + R_TRY(this->ReceiveFromRpcChannel(dst, response.body_size)); + + /* Null-terminate the output path. */ + dst[response.body_size] = '\x00'; + + return ResultSuccess(); + } + + Result ClientImpl::GetDiskFreeSpace(s64 *out_free, s64 *out_total, s64 *out_total_free, const char *path) { + /* 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.MakeGetDiskFreeSpaceHeader(std::addressof(request), path_len); + + /* 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 output. */ + *out_free = response.params[2]; + *out_total = response.params[3]; + *out_total_free = response.params[4]; + + return ResultSuccess(); + } + Result ClientImpl::CloseDirectory(s32 handle) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -570,10 +1047,18 @@ namespace ams::htcfs { R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); /* Check that we succeeded. */ - R_TRY(ConvertHtcfsResult(response.params[0])); + const auto htcfs_result = ConvertHtcfsResult(response.params[0]); + if (R_FAILED(htcfs_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return htcfs_result; + } /* Check our operation's result. */ - R_TRY(ConvertNativeResult(response.params[1])); + const auto native_result = ConvertNativeResult(response.params[1]); + if (R_FAILED(native_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return native_result; + } /* Check that the number of entries read is allowable. */ R_UNLESS(static_cast(response.params[2]) <= max_out_entries, htcfs::ResultUnexpectedResponseBody()); @@ -648,4 +1133,408 @@ namespace ams::htcfs { return ResultSuccess(); } + Result ClientImpl::CloseFile(s32 handle) { + /* Invalidate the cache. */ + m_cache_manager.Invalidate(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.MakeCloseFileHeader(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(); + } + + Result ClientImpl::ReadFile(s64 *out, void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::ReadOption option) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Try to read from our cache. */ + if (util::IsIntValueRepresentable(offset) && util::IsIntValueRepresentable(buffer_size)) { + size_t read_size; + if (m_cache_manager.ReadFile(std::addressof(read_size), buffer, handle, static_cast(offset), static_cast(buffer_size))) { + AMS_ASSERT(util::IsIntValueRepresentable(read_size)); + + *out = static_cast(read_size); + return ResultSuccess(); + } + } + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeReadFileHeader(std::addressof(request), handle, offset, buffer_size); + + /* 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)); + + /* Check that we succeeded. */ + const auto htcfs_result = ConvertHtcfsResult(response.params[0]); + if (R_FAILED(htcfs_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return htcfs_result; + } + + /* Check our operation's result. */ + const auto native_result = ConvertNativeResult(response.params[1]); + if (R_FAILED(native_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return native_result; + } + + /* Check the body size. */ + R_UNLESS(response.body_size <= buffer_size, htcfs::ResultUnexpectedResponseBodySize()); + + /* Receive the file data. */ + R_TRY(this->ReceiveFromRpcChannel(buffer, response.body_size)); + + /* Set the output size. */ + *out = response.body_size; + + return ResultSuccess(); + } + + Result ClientImpl::ReadFileLarge(s64 *out, void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::ReadOption option) { + /* Check our buffer size. */ + R_UNLESS(util::IsIntValueRepresentable(buffer_size), htcfs::ResultInvalidArgument()); + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Setup data channel. */ + this->InitializeDataChannelForReceive(buffer, buffer_size); + ON_SCOPE_EXIT { this->FinalizeDataChannel(); }; + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeReadFileLargeHeader(std::addressof(request), handle, offset, buffer_size, DataChannelId); + + /* 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. */ + const auto htcfs_result = ConvertHtcfsResult(response.params[0]); + if (R_FAILED(htcfs_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return htcfs_result; + } + + /* Check our operation's result. */ + const auto native_result = ConvertNativeResult(response.params[1]); + if (R_FAILED(native_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return native_result; + } + + /* Check that the size read is allowable. */ + R_UNLESS(response.params[2] <= buffer_size, htcfs::ResultUnexpectedResponseBodySize()); + + /* Read the entries, if there are any. */ + R_TRY(this->ReceiveFromDataChannel(response.params[2])); + + /* Set the number of output entries. */ + *out = response.params[2]; + + return ResultSuccess(); + } + + Result ClientImpl::WriteFile(const void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::WriteOption option) { + /* Invalidate the cache. */ + m_cache_manager.Invalidate(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.MakeWriteFileHeader(std::addressof(request), buffer_size, handle, option.value, offset); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, buffer, buffer_size)); + + /* 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(); + } + + Result ClientImpl::WriteFileLarge(const void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::WriteOption option) { + /* Invalidate the cache. */ + m_cache_manager.Invalidate(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.MakeWriteFileLargeHeader(std::addressof(request), handle, option.value, offset, buffer_size, DataChannelId); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, buffer, buffer_size)); + + /* 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)); + + /* Verify that the host reports ready to receive our data. */ + if (static_cast(response.params[0]) != HtcfsResult::Ready) { + return ConvertHtcfsResult(response.params[0]); + } + + /* Verify that our send will be valid. */ + AMS_ASSERT(util::IsIntValueRepresentable(buffer_size)); + + /* Perform the send. */ + { + /* Initialize data channel for our write. */ + this->InitializeDataChannelForSend(buffer, buffer_size); + + /* Ensure that we clean up our data channel. */ + ON_SCOPE_EXIT { this->FinalizeDataChannel(); }; + + /* Send to our data channel. */ + R_TRY(this->SendToDataChannel()); + } + + /* Receive the large-write response. */ + Header write_resp; + R_TRY(this->ReceiveFromRpcChannel(std::addressof(write_resp), sizeof(write_resp))); + + /* Check the write-response header. */ + R_TRY(this->CheckResponseHeader(write_resp, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(write_resp.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(write_resp.params[1])); + + return ResultSuccess(); + } + + Result ClientImpl::GetFileSize(s64 *out, s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Check if we have the file size cached. */ + R_SUCCEED_IF(m_cache_manager.GetFileSize(out, handle)); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeGetFileSizeHeader(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])); + + /* Set the output. */ + *out = response.params[2]; + + return ResultSuccess(); + } + + Result ClientImpl::SetFileSize(s64 size, s32 handle) { + /* Invalidate the cache. */ + m_cache_manager.Invalidate(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.MakeSetFileSizeHeader(std::addressof(request), handle, size); + + /* 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(); + } + + Result ClientImpl::FlushFile(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.MakeFlushFileHeader(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(); + } + + Result ClientImpl::GetPriorityForFile(s32 *out, 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.MakeGetPriorityForFileHeader(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])); + + /* Set the output. */ + *out = static_cast(response.params[1]); + + return ResultSuccess(); + } + + Result ClientImpl::SetPriorityForFile(s32 priority, 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.MakeSetPriorityForFileHeader(std::addressof(request), handle, priority); + + /* 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])); + + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp index e66fc7642..219f289a1 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp @@ -56,7 +56,21 @@ namespace ams::htcfs { void Cancel(); void Wait(); public: + Result OpenFile(s32 *out_handle, const char *path, fs::OpenMode mode, bool case_sensitive); + Result FileExists(bool *out, const char *path, bool case_sensitive); + Result DeleteFile(const char *path, bool case_sensitive); + Result RenameFile(const char *old_path, const char *new_path, bool case_sensitive); + Result GetEntryType(fs::DirectoryEntryType *out, const char *path, bool case_sensitive); Result OpenDirectory(s32 *out_handle, const char *path, fs::OpenDirectoryMode mode, bool case_sensitive); + Result DirectoryExists(bool *out, const char *path, bool case_sensitive); + Result CreateDirectory(const char *path, bool case_sensitive); + Result DeleteDirectory(const char *path, bool recursively, bool case_sensitive); + Result RenameDirectory(const char *old_path, const char *new_path, bool case_sensitive); + Result CreateFile(const char *path, s64 size, bool case_sensitive); + Result GetFileTimeStamp(u64 *out_create, u64 *out_access, u64 *out_modify, const char *path, bool case_sensitive); + Result GetCaseSensitivePath(char *dst, size_t dst_size, const char *path); + Result GetDiskFreeSpace(s64 *out_free, s64 *out_total, s64 *out_total_free, const char *path); + Result CloseDirectory(s32 handle); Result GetEntryCount(s64 *out, s32 handle); @@ -64,6 +78,18 @@ namespace ams::htcfs { Result ReadDirectoryLarge(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, s32 handle); Result GetPriorityForDirectory(s32 *out, s32 handle); Result SetPriorityForDirectory(s32 priority, s32 handle); + + Result CloseFile(s32 handle); + + Result ReadFile(s64 *out, void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::ReadOption option); + Result ReadFileLarge(s64 *out, void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::ReadOption option); + Result WriteFile(const void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::WriteOption option); + Result WriteFileLarge(const void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::WriteOption option); + Result GetFileSize(s64 *out, s32 handle); + Result SetFileSize(s64 size, s32 handle); + Result FlushFile(s32 handle); + Result GetPriorityForFile(s32 *out, s32 handle); + Result SetPriorityForFile(s32 priority, s32 handle); private: int WaitAny(htclow::ChannelState state, os::EventType *event); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp b/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp index f0db33f07..e37a3f89b 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp @@ -16,7 +16,6 @@ #include #include "htcfs_directory_service_object.hpp" #include "htcfs_client.hpp" -#include "../htclow/htclow_default_channel_config.hpp" namespace ams::htcfs { diff --git a/libraries/libstratosphere/source/htcfs/htcfs_file_service_object.cpp b/libraries/libstratosphere/source/htcfs/htcfs_file_service_object.cpp index edae19ad0..ff1025784 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_file_service_object.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_file_service_object.cpp @@ -15,42 +15,59 @@ */ #include #include "htcfs_file_service_object.hpp" +#include "htcfs_client.hpp" namespace ams::htcfs { FileServiceObject::FileServiceObject(s32 handle) : m_handle(handle) { /* ... */ } FileServiceObject::~FileServiceObject() { - /* TODO */ - AMS_ABORT("htcfs::GetClient().CloseFile(m_handle);"); + htcfs::GetClient().CloseFile(m_handle); } Result FileServiceObject::ReadFile(ams::sf::Out out, s64 offset, const ams::sf::OutNonSecureBuffer &buffer, ams::fs::ReadOption option) { - AMS_ABORT("FileServiceObject::ReadFile"); + /* Validate offset. */ + R_UNLESS(offset >= 0, htcfs::ResultInvalidArgument()); + + if (buffer.GetSize() >= ClientImpl::MaxPacketBodySize) { + return htcfs::GetClient().ReadFileLarge(out.GetPointer(), buffer.GetPointer(), m_handle, offset, buffer.GetSize(), option); + } else { + return htcfs::GetClient().ReadFile(out.GetPointer(), buffer.GetPointer(), m_handle, offset, buffer.GetSize(), option); + } } Result FileServiceObject::WriteFile(s64 offset, const ams::sf::InNonSecureBuffer &buffer, ams::fs::WriteOption option) { - AMS_ABORT("FileServiceObject::WriteFile"); + /* Validate offset. */ + R_UNLESS(offset >= 0, htcfs::ResultInvalidArgument()); + + if (buffer.GetSize() >= ClientImpl::MaxPacketBodySize) { + return htcfs::GetClient().WriteFileLarge(buffer.GetPointer(), m_handle, offset, buffer.GetSize(), option); + } else { + return htcfs::GetClient().WriteFile(buffer.GetPointer(), m_handle, offset, buffer.GetSize(), option); + } } Result FileServiceObject::GetFileSize(ams::sf::Out out) { - AMS_ABORT("FileServiceObject::GetFileSize"); + return htcfs::GetClient().GetFileSize(out.GetPointer(), m_handle); } Result FileServiceObject::SetFileSize(s64 size) { - AMS_ABORT("FileServiceObject::SetFileSize"); + /* Validate size. */ + R_UNLESS(size >= 0, htcfs::ResultInvalidArgument()); + + return htcfs::GetClient().SetFileSize(size, m_handle); } Result FileServiceObject::FlushFile() { - AMS_ABORT("FileServiceObject::FlushFile"); + return htcfs::GetClient().FlushFile(m_handle); } Result FileServiceObject::SetPriorityForFile(s32 priority) { - AMS_ABORT("FileServiceObject::SetPriorityForFile"); + return htcfs::GetClient().SetPriorityForFile(priority, m_handle); } Result FileServiceObject::GetPriorityForFile(ams::sf::Out out) { - AMS_ABORT("FileServiceObject::GetPriorityForFile"); + return htcfs::GetClient().GetPriorityForFile(out.GetPointer(), m_handle); } } 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 e93d3d6f4..468574bde 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp @@ -44,26 +44,75 @@ namespace ams::htcfs { return 0 < len && len < static_cast(fs::EntryNameLengthMax + 1); } + Result ConvertOpenMode(fs::OpenMode *out, u32 open_mode) { + switch (open_mode) { + case 1: + *out = fs::OpenMode_Read; + break; + case 2: + *out = static_cast(fs::OpenMode_Write | fs::OpenMode_AllowAppend); + break; + case 3: + *out = static_cast(fs::OpenMode_ReadWrite | fs::OpenMode_AllowAppend); + break; + default: + return htcfs::ResultInvalidArgument(); + } + + return ResultSuccess(); + } + } Result FileSystemServiceObject::OpenFile(sf::Out> out, const tma::Path &path, u32 open_mode, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::OpenFile"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Convert the open mode. */ + fs::OpenMode fs_open_mode; + R_TRY(ConvertOpenMode(std::addressof(fs_open_mode), open_mode)); + + /* Open the file. */ + s32 handle; + R_TRY(htcfs::GetClient().OpenFile(std::addressof(handle), path.str, fs_open_mode, case_sensitive)); + + /* Set the output file. */ + *out = FileServiceObjectFactory::CreateSharedEmplaced(handle); + return ResultSuccess(); } Result FileSystemServiceObject::FileExists(sf::Out out, const tma::Path &path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::FileExists"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Get whether the file exists. */ + return htcfs::GetClient().FileExists(out.GetPointer(), path.str, case_sensitive); } Result FileSystemServiceObject::DeleteFile(const tma::Path &path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::DeleteFile"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Delete the file. */ + return htcfs::GetClient().DeleteFile(path.str, case_sensitive); } Result FileSystemServiceObject::RenameFile(const tma::Path &old_path, const tma::Path &new_path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::RenameFile"); + /* Check that the paths are valid. */ + R_UNLESS(IsValidPath(old_path), htcfs::ResultInvalidArgument()); + R_UNLESS(IsValidPath(new_path), htcfs::ResultInvalidArgument()); + + /* Rename the file. */ + return htcfs::GetClient().RenameFile(old_path.str, new_path.str, case_sensitive); } Result FileSystemServiceObject::GetIOType(sf::Out out, const tma::Path &path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::GetIOType"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Get the entry type. */ + static_assert(sizeof(s32) == sizeof(fs::DirectoryEntryType)); + return htcfs::GetClient().GetEntryType(reinterpret_cast(out.GetPointer()), path.str, case_sensitive); } Result FileSystemServiceObject::OpenDirectory(sf::Out> out, const tma::Path &path, s32 open_mode, bool case_sensitive) { @@ -80,35 +129,68 @@ namespace ams::htcfs { } Result FileSystemServiceObject::DirectoryExists(sf::Out out, const tma::Path &path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::DirectoryExists"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Get whether the file exists. */ + return htcfs::GetClient().DirectoryExists(out.GetPointer(), path.str, case_sensitive); } Result FileSystemServiceObject::CreateDirectory(const tma::Path &path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::CreateDirectory"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Create the directory. */ + return htcfs::GetClient().CreateDirectory(path.str, case_sensitive); } Result FileSystemServiceObject::DeleteDirectory(const tma::Path &path, bool recursively, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::DeleteDirectory"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Delete the directory. */ + return htcfs::GetClient().DeleteDirectory(path.str, recursively, case_sensitive); } Result FileSystemServiceObject::RenameDirectory(const tma::Path &old_path, const tma::Path &new_path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::RenameDirectory"); + /* Check that the paths are valid. */ + R_UNLESS(IsValidPath(old_path), htcfs::ResultInvalidArgument()); + R_UNLESS(IsValidPath(new_path), htcfs::ResultInvalidArgument()); + + /* Rename the file. */ + return htcfs::GetClient().RenameDirectory(old_path.str, new_path.str, case_sensitive); } Result FileSystemServiceObject::CreateFile(const tma::Path &path, s64 size, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::CreateFile"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Create the file. */ + return htcfs::GetClient().CreateFile(path.str, size, case_sensitive); } Result FileSystemServiceObject::GetFileTimeStamp(sf::Out out_create, sf::Out out_access, sf::Out out_modify, const tma::Path &path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::GetFileTimeStamp"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Get the timestamp. */ + return htcfs::GetClient().GetFileTimeStamp(out_create.GetPointer(), out_access.GetPointer(), out_modify.GetPointer(), path.str, case_sensitive); } Result FileSystemServiceObject::GetCaseSensitivePath(const tma::Path &path, const sf::OutBuffer &out) { - AMS_ABORT("FileSystemServiceObject::GetCaseSensitivePath"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Get the case sensitive path. */ + return htcfs::GetClient().GetCaseSensitivePath(reinterpret_cast(out.GetPointer()), out.GetSize(), path.str); } Result FileSystemServiceObject::GetDiskFreeSpaceExW(sf::Out out_free, sf::Out out_total, sf::Out out_total_free, const tma::Path &path) { - AMS_ABORT("FileSystemServiceObject::GetDiskFreeSpaceExW"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Get the timestamp. */ + return htcfs::GetClient().GetDiskFreeSpace(out_free.GetPointer(), out_total.GetPointer(), out_total_free.GetPointer(), path.str); } } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp index 8417d34a9..c2fceba23 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp @@ -115,10 +115,62 @@ namespace ams::htcfs { return this->MakeRequestHeader(out, PacketType::SetProtocolVersion, 0, version); } + void MakeOpenFileHeader(Header *out, int path_len, fs::OpenMode mode, bool case_sensitive, s64 cache_size) { + return this->MakeRequestHeader(out, PacketType::OpenFile, path_len, static_cast(mode), case_sensitive ? 1 : 0, cache_size); + } + + void MakeFileExistsHeader(Header *out, int path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::FileExists, path_len, case_sensitive ? 1 : 0); + } + + void MakeDeleteFileHeader(Header *out, int path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::DeleteFile, path_len, case_sensitive ? 1 : 0); + } + + void MakeRenameFileHeader(Header *out, int old_path_len, int new_path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::RenameFile, old_path_len + new_path_len, old_path_len, new_path_len, case_sensitive ? 1 : 0); + } + + void MakeGetEntryTypeHeader(Header *out, int path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::GetEntryType, path_len, case_sensitive ? 1 : 0); + } + 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 MakeDirectoryExistsHeader(Header *out, int path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::DirectoryExists, path_len, case_sensitive ? 1 : 0); + } + + void MakeCreateDirectoryHeader(Header *out, int path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::CreateDirectory, path_len, case_sensitive ? 1 : 0); + } + + void MakeDeleteDirectoryHeader(Header *out, int path_len, bool recursively, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::DeleteDirectory, path_len, recursively ? 1 : 0, case_sensitive ? 1 : 0); + } + + void MakeRenameDirectoryHeader(Header *out, int old_path_len, int new_path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::RenameDirectory, old_path_len + new_path_len, old_path_len, new_path_len, case_sensitive ? 1 : 0); + } + + void MakeCreateFileHeader(Header *out, int path_len, s64 size, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::CreateDirectory, path_len, size, case_sensitive ? 1 : 0); + } + + void MakeGetFileTimeStampHeader(Header *out, int path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::GetFileTimeStamp, path_len, case_sensitive ? 1 : 0); + } + + void MakeGetCaseSensitivePathHeader(Header *out, int path_len) { + return this->MakeRequestHeader(out, PacketType::GetCaseSensitivePath, path_len); + } + + void MakeGetDiskFreeSpaceHeader(Header *out, int path_len) { + return this->MakeRequestHeader(out, PacketType::GetDiskFreeSpace, path_len); + } + void MakeCloseDirectoryHeader(Header *out, s32 handle) { return this->MakeRequestHeader(out, PacketType::CloseDirectory, 0, handle); } @@ -142,6 +194,46 @@ namespace ams::htcfs { void MakeSetPriorityForDirectoryHeader(Header *out, s32 handle, s32 priority) { return this->MakeRequestHeader(out, PacketType::SetPriorityForDirectory, 0, handle, priority); } + + void MakeCloseFileHeader(Header *out, s32 handle) { + return this->MakeRequestHeader(out, PacketType::CloseFile, 0, handle); + } + + void MakeReadFileHeader(Header *out, s32 handle, s64 offset, s64 buffer_size) { + return this->MakeRequestHeader(out, PacketType::ReadFile, 0, handle, offset, buffer_size); + } + + void MakeReadFileLargeHeader(Header *out, s32 handle, s64 offset, s64 buffer_size, u16 data_channel_id) { + return this->MakeRequestHeader(out, PacketType::ReadFileLarge, 0, handle, offset, buffer_size, data_channel_id); + } + + void MakeWriteFileHeader(Header *out, s64 buffer_size, s32 handle, u32 option, s64 offset) { + return this->MakeRequestHeader(out, PacketType::WriteFile, buffer_size, handle, option, offset); + } + + void MakeWriteFileLargeHeader(Header *out, s32 handle, u32 option, s64 offset, s64 buffer_size, u16 data_channel_id) { + return this->MakeRequestHeader(out, PacketType::WriteFileLarge, 0, handle, option, offset, buffer_size, data_channel_id); + } + + void MakeGetFileSizeHeader(Header *out, s32 handle) { + return this->MakeRequestHeader(out, PacketType::GetFileSize, 0, handle); + } + + void MakeSetFileSizeHeader(Header *out, s32 handle, s64 size) { + return this->MakeRequestHeader(out, PacketType::SetFileSize, 0, handle, size); + } + + void MakeFlushFileHeader(Header *out, s32 handle) { + return this->MakeRequestHeader(out, PacketType::FlushFile, 0, handle); + } + + void MakeGetPriorityForFileHeader(Header *out, s32 handle) { + return this->MakeRequestHeader(out, PacketType::GetPriorityForFile, 0, handle); + } + + void MakeSetPriorityForFileHeader(Header *out, s32 handle, s32 priority) { + return this->MakeRequestHeader(out, PacketType::SetPriorityForFile, 0, handle, priority); + } }; } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_result.hpp b/libraries/libstratosphere/source/htcfs/htcfs_result.hpp index 8346e66f6..4568fefd6 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_result.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_result.hpp @@ -26,6 +26,7 @@ namespace ams::htcfs { InvalidRequest = 3, InvalidHandle = 4, OutOfHandle = 5, + Ready = 6, }; inline Result ConvertHtcfsResult(HtcfsResult result) {