mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-11-16 17:11:18 +01:00
NOTE: This work is not yet fully complete; kernel is done, but it was taking an exceedingly long time to get through libstratosphere. Thus, I've temporarily added -Wno-error=unused-result for libstratosphere/stratosphere. All warnings should be fixed to do the same thing Nintendo does as relevant, but this is taking a phenomenally long time and is not actually the most important work to do, so it can be put off for some time to prioritize other tasks for 21.0.0 support.
1626 lines
56 KiB
C++
1626 lines
56 KiB
C++
/*
|
|
* Copyright (c) Atmosphère-NX
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <stratosphere.hpp>
|
|
#include "htcfs_client_impl.hpp"
|
|
#include "htcfs_result.hpp"
|
|
|
|
namespace ams::htcfs {
|
|
|
|
namespace {
|
|
|
|
/* TODO: Move to a header? */
|
|
constexpr u16 RpcChannelId = 0;
|
|
constexpr u16 DataChannelId = 1;
|
|
|
|
alignas(os::ThreadStackAlignment) constinit u8 g_monitor_thread_stack[os::MemoryPageSize];
|
|
|
|
constexpr size_t FileDataCacheSize = 32_KB;
|
|
constinit u8 g_cache[FileDataCacheSize];
|
|
|
|
ALWAYS_INLINE Result ConvertNativeResult(s64 value) {
|
|
return result::impl::MakeResult(value);
|
|
}
|
|
|
|
}
|
|
|
|
ClientImpl::ClientImpl(htclow::HtclowManager *manager)
|
|
: m_htclow_manager(manager),
|
|
m_cache_manager(g_cache, sizeof(g_cache)),
|
|
m_header_factory(),
|
|
m_mutex(),
|
|
m_module(htclow::ModuleId::Htcfs),
|
|
m_rpc_channel(manager),
|
|
m_data_channel(manager),
|
|
m_connected(false),
|
|
m_event(os::EventClearMode_ManualClear)
|
|
{
|
|
/* Start our thread. */
|
|
this->Start();
|
|
}
|
|
|
|
void ClientImpl::Start() {
|
|
/* Create our thread. */
|
|
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_monitor_thread), ThreadEntry, this, g_monitor_thread_stack, sizeof(g_monitor_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtcfsMonitor)));
|
|
|
|
/* Set thread name pointer. */
|
|
os::SetThreadNamePointer(std::addressof(m_monitor_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcfsMonitor));
|
|
|
|
/* Start our thread. */
|
|
os::StartThread(std::addressof(m_monitor_thread));
|
|
}
|
|
|
|
void ClientImpl::Cancel() {
|
|
/* Signal our event. */
|
|
m_event.Signal();
|
|
}
|
|
|
|
void ClientImpl::Wait() {
|
|
/* Wait for and destroy our thread. */
|
|
os::WaitThread(std::addressof(m_monitor_thread));
|
|
os::DestroyThread(std::addressof(m_monitor_thread));
|
|
}
|
|
|
|
void ClientImpl::ThreadBody() {
|
|
/* Loop forever, until we're cancelled. */
|
|
while (!m_event.TryWait()) {
|
|
/* Lock ourselves. */
|
|
std::scoped_lock lk(m_mutex);
|
|
|
|
/* Open our channel. */
|
|
R_ABORT_UNLESS(m_rpc_channel.Open(std::addressof(m_module), RpcChannelId));
|
|
|
|
/* Ensure that we clean up our channel when we're done. */
|
|
ON_SCOPE_EXIT {
|
|
m_rpc_channel.Close();
|
|
m_cache_manager.Invalidate();
|
|
};
|
|
|
|
/* Set our channel config and buffers. */
|
|
m_rpc_channel.SetConfig(htclow::DefaultChannelConfig);
|
|
m_rpc_channel.SetReceiveBuffer(m_receive_buffer, sizeof(m_receive_buffer));
|
|
m_rpc_channel.SetSendBuffer(m_send_buffer, sizeof(m_send_buffer));
|
|
|
|
/* Wait for our channel state to be connectable. */
|
|
if (this->WaitAny(htclow::ChannelState_Connectable, m_event.GetBase()) != 0) {
|
|
return;
|
|
}
|
|
|
|
/* Ensure that when we're done, we reset our connected status. */
|
|
ON_SCOPE_EXIT { m_connected = false; };
|
|
|
|
/* Try to connect. */
|
|
const Result conn_result = m_rpc_channel.Connect();
|
|
if (R_FAILED(conn_result)) {
|
|
/* DEBUG */
|
|
R_ABORT_UNLESS(conn_result);
|
|
continue;
|
|
}
|
|
|
|
/* Ensure that we manage our connection correctly. */
|
|
auto conn_guard = SCOPE_GUARD { m_rpc_channel.Shutdown(); };
|
|
|
|
/* Try to set up the protocol. */
|
|
const Result setup_result = this->SetUpProtocol();
|
|
if (R_FAILED(setup_result)) {
|
|
R_ABORT_UNLESS(setup_result);
|
|
continue;
|
|
}
|
|
|
|
/* We're properly connected now. */
|
|
m_connected = true;
|
|
conn_guard.Cancel();
|
|
|
|
/* Tear down the protocol when we're done processing. */
|
|
ON_SCOPE_EXIT { this->TearDownProtocol(); };
|
|
|
|
/* Wait to become disconnected. */
|
|
if (this->WaitAny(htclow::ChannelState_Disconnected, m_event.GetBase()) != 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int ClientImpl::WaitAny(htclow::ChannelState state, os::EventType *event) {
|
|
/* Wait. */
|
|
int idx = 0;
|
|
while (m_rpc_channel.GetChannelState() != state) {
|
|
/* Get the channel state event. */
|
|
os::EventType *channel_state_event = m_rpc_channel.GetChannelStateEvent();
|
|
|
|
/* Perform wait with lock temporarily not held. */
|
|
{
|
|
m_mutex.Unlock();
|
|
idx = os::WaitAny(channel_state_event, event);
|
|
m_mutex.Lock();
|
|
}
|
|
|
|
/* If we're cancel-signalled, we're done. */
|
|
if (idx != 0) {
|
|
break;
|
|
}
|
|
|
|
/* Clear the channel state event. */
|
|
os::ClearEvent(channel_state_event);
|
|
}
|
|
return idx;
|
|
}
|
|
|
|
Result ClientImpl::SetUpProtocol() {
|
|
/* Get the maximum supported protocol on the host side. */
|
|
s16 max_host_protocol;
|
|
R_TRY(this->GetMaxProtocolVersion(std::addressof(max_host_protocol)));
|
|
|
|
/* Verify that the host protocol is >= 0. */
|
|
R_UNLESS(max_host_protocol >= 0, htcfs::ResultUnsupportedProtocolVersion());
|
|
|
|
/* Inform the host what protocol we're using. */
|
|
const auto use_version = std::min(MaxProtocolVersion, max_host_protocol);
|
|
R_TRY(this->SetProtocolVersion(use_version));
|
|
|
|
/* Set the version in our header factory. */
|
|
m_header_factory.SetVersion(use_version);
|
|
R_SUCCEED();
|
|
}
|
|
|
|
void ClientImpl::TearDownProtocol() {
|
|
/* Set the header factory version to zero. */
|
|
m_header_factory.SetVersion(0);
|
|
}
|
|
|
|
Result ClientImpl::CheckResponseHeaderWithoutVersion(const Header &response, PacketType packet_type) {
|
|
/* Check the protocol. */
|
|
R_UNLESS(response.protocol == HtcfsProtocol, htcfs::ResultUnexpectedResponseProtocolId());
|
|
|
|
/* Check the packet category. */
|
|
R_UNLESS(response.packet_category == PacketCategory::Response, htcfs::ResultUnexpectedResponsePacketCategory());
|
|
|
|
/* Check the type. */
|
|
R_UNLESS(response.packet_type == packet_type, htcfs::ResultUnexpectedResponsePacketType());
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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());
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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());
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result ClientImpl::GetMaxProtocolVersion(s16 *out) {
|
|
/* Create space for request and response. */
|
|
Header request, response;
|
|
|
|
/* Create header for the request. */
|
|
m_header_factory.MakeGetMaxProtocolVersionHeader(std::addressof(request));
|
|
|
|
/* Send the request to the host. */
|
|
R_TRY(this->SendToRpcChannel(std::addressof(request), sizeof(request)));
|
|
|
|
/* Receive response from the host. */
|
|
R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response)));
|
|
|
|
/* Check the response header. */
|
|
R_TRY(this->CheckResponseHeaderWithoutVersion(response, request.packet_type));
|
|
|
|
/* Check that we succeeded. */
|
|
R_TRY(ConvertHtcfsResult(response.params[0]));
|
|
|
|
/* Set the maximum protocol version. */
|
|
*out = response.params[1];
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result ClientImpl::SetProtocolVersion(s16 version) {
|
|
/* Create space for request and response. */
|
|
Header request, response;
|
|
|
|
/* Create header for the request. */
|
|
m_header_factory.MakeSetProtocolVersionHeader(std::addressof(request), version);
|
|
|
|
/* Send the request to the host. */
|
|
R_TRY(this->SendToRpcChannel(std::addressof(request), sizeof(request)));
|
|
|
|
/* Receive response from the host. */
|
|
R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response)));
|
|
|
|
/* Check the response header. */
|
|
R_TRY(this->CheckResponseHeaderWithoutVersion(response, request.packet_type));
|
|
|
|
/* Check that we succeeded. */
|
|
R_TRY(ConvertHtcfsResult(response.params[0]));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result ClientImpl::SendToRpcChannel(const void *src, s64 size) {
|
|
R_RETURN(this->SendToHtclow(src, size, std::addressof(m_rpc_channel)));
|
|
}
|
|
|
|
Result ClientImpl::ReceiveFromRpcChannel(void *dst, s64 size) {
|
|
R_RETURN(this->ReceiveFromHtclow(dst, size, std::addressof(m_rpc_channel)));
|
|
}
|
|
|
|
Result ClientImpl::ReceiveFromDataChannel(s64 size) {
|
|
R_RETURN(m_data_channel.WaitReceive(size));
|
|
}
|
|
|
|
Result ClientImpl::SendToDataChannel() {
|
|
R_RETURN(m_data_channel.Flush());
|
|
}
|
|
|
|
Result ClientImpl::SendToHtclow(const void *src, s64 size, htclow::Channel *channel) {
|
|
/* Check size. */
|
|
R_UNLESS(size >= 0, htcfs::ResultInvalidArgument());
|
|
|
|
/* Iteratively send. */
|
|
s64 sent;
|
|
for (s64 total = 0; total < size; total += sent) {
|
|
/* Send the current batch of data. */
|
|
R_TRY(channel->Send(std::addressof(sent), static_cast<const u8 *>(src) + total, size - total));
|
|
|
|
/* Check that we sent the right amount. */
|
|
R_UNLESS(sent == size - total, htcfs::ResultHtclowChannelClosed());
|
|
}
|
|
|
|
/* Flush. */
|
|
R_TRY(channel->Flush());
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result ClientImpl::ReceiveFromHtclow(void *dst, s64 size, htclow::Channel *channel) {
|
|
/* Check size. */
|
|
R_UNLESS(size >= 0, htcfs::ResultInvalidArgument());
|
|
|
|
/* Iteratively receive. */
|
|
s64 received;
|
|
for (s64 total = 0; total < size; total += received) {
|
|
/* Receive the current batch of data. */
|
|
R_TRY(channel->Receive(std::addressof(received), static_cast<u8 *>(dst) + total, size - total, htclow::ReceiveOption_ReceiveAllData));
|
|
|
|
/* Check that we received the right amount. */
|
|
R_UNLESS(received == size - total, htcfs::ResultHtclowChannelClosed());
|
|
}
|
|
|
|
/* Flush. */
|
|
R_TRY(channel->Flush());
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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());
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
void ClientImpl::InitializeDataChannelForReceive(void *dst, size_t size) {
|
|
/* Open the data channel. */
|
|
R_ABORT_UNLESS(m_data_channel.Open(std::addressof(m_module), DataChannelId));
|
|
|
|
/* Set our config. */
|
|
constexpr htclow::ChannelConfig BulkReceiveConfig = {
|
|
.flow_control_enabled = false,
|
|
.handshake_enabled = false,
|
|
.max_packet_size = 0x3E000,
|
|
};
|
|
m_data_channel.SetConfig(BulkReceiveConfig);
|
|
|
|
/* Set receive buffer. */
|
|
m_data_channel.SetReceiveBuffer(dst, size);
|
|
|
|
/* Connect. */
|
|
R_ABORT_UNLESS(m_data_channel.Connect());
|
|
}
|
|
|
|
void ClientImpl::InitializeDataChannelForSend(const void *src, size_t size) {
|
|
/* Open the data channel. */
|
|
R_ABORT_UNLESS(m_data_channel.Open(std::addressof(m_module), DataChannelId));
|
|
|
|
/* Check that the size is valid. */
|
|
AMS_ASSERT(util::IsIntValueRepresentable<s64>(size));
|
|
|
|
/* Set our config. */
|
|
constexpr htclow::ChannelConfig BulkSendConfig = {
|
|
.flow_control_enabled = false,
|
|
.handshake_enabled = false,
|
|
.max_packet_size = 0xE020,
|
|
};
|
|
m_data_channel.SetConfig(BulkSendConfig);
|
|
|
|
/* Set our send buffer. */
|
|
m_data_channel.SetSendBufferWithData(src, size);
|
|
|
|
/* Connect. */
|
|
R_ABORT_UNLESS(m_data_channel.Connect());
|
|
}
|
|
|
|
void ClientImpl::FinalizeDataChannel() {
|
|
/* Close our data channel. */
|
|
m_data_channel.Close();
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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<size_t>(response.body_size) <= MaxPacketBodySize, htcfs::ResultUnexpectedResponseBodySize());
|
|
|
|
/* Receive the response body. */
|
|
if (response.body_size > 0) {
|
|
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);
|
|
}
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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;
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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]));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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]));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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<fs::DirectoryEntryType>(response.params[2]);
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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<s32>(response.params[2]);
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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;
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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]));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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]));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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]));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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]));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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<u64>(response.params[2]);
|
|
*out_access = static_cast<u64>(response.params[3]);
|
|
*out_modify = static_cast<u64>(response.params[4]);
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result ClientImpl::GetCaseSensitivePath(char *dst, size_t dst_size, const char *path) {
|
|
/* Sanity check the output buffer. */
|
|
R_UNLESS(util::IsIntValueRepresentable<s64>(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());
|
|
R_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());
|
|
R_RETURN(native_result);
|
|
}
|
|
|
|
/* Check the body size. */
|
|
R_UNLESS(response.body_size < static_cast<s64>(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';
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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];
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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]));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result ClientImpl::GetEntryCount(s64 *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.MakeGetEntryCountHeader(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 count. */
|
|
*out = response.params[2];
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result ClientImpl::ReadDirectory(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, 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.MakeReadDirectoryHeader(std::addressof(request), handle, max_out_entries);
|
|
|
|
/* 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. */
|
|
R_TRY(ConvertHtcfsResult(response.params[0]));
|
|
|
|
/* Check our operation's result. */
|
|
R_TRY(ConvertNativeResult(response.params[1]));
|
|
|
|
/* Check that the response body size is expected. */
|
|
R_UNLESS(static_cast<size_t>(response.body_size) == max_out_entries * sizeof(*out_entries), htcfs::ResultUnexpectedResponseBody());
|
|
|
|
/* Check that the number of entries read is allowable. */
|
|
R_UNLESS(static_cast<size_t>(response.params[2]) <= max_out_entries, htcfs::ResultUnexpectedResponseBody());
|
|
|
|
/* Receive the entries. */
|
|
*out = response.params[2];
|
|
R_RETURN(this->ReceiveFromRpcChannel(out_entries, response.body_size));
|
|
}
|
|
|
|
Result ClientImpl::ReadDirectoryLarge(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, s32 handle) {
|
|
/* Lock ourselves. */
|
|
std::scoped_lock lk(m_mutex);
|
|
|
|
/* Initialize our rpc channel. */
|
|
R_TRY(this->InitializeRpcChannel());
|
|
|
|
/* Setup data channel. */
|
|
const bool use_data_channel = max_out_entries > 0;
|
|
if (use_data_channel) {
|
|
this->InitializeDataChannelForReceive(out_entries, max_out_entries * sizeof(*out_entries));
|
|
}
|
|
ON_SCOPE_EXIT { if (use_data_channel) { this->FinalizeDataChannel(); } };
|
|
|
|
/* Create space for request and response. */
|
|
Header request, response;
|
|
|
|
/* Create header for the request. */
|
|
m_header_factory.MakeReadDirectoryLargeHeader(std::addressof(request), handle, max_out_entries, 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());
|
|
R_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());
|
|
R_RETURN(native_result);
|
|
}
|
|
|
|
/* Check that the number of entries read is allowable. */
|
|
R_UNLESS(static_cast<size_t>(response.params[2]) <= max_out_entries, htcfs::ResultUnexpectedResponseBody());
|
|
|
|
/* Read the entries, if there are any. */
|
|
if (response.params[2] > 0) {
|
|
R_TRY(this->ReceiveFromDataChannel(response.params[2] * sizeof(*out_entries)));
|
|
}
|
|
|
|
/* Set the number of output entries. */
|
|
*out = response.params[2];
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result ClientImpl::GetPriorityForDirectory(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.MakeGetPriorityForDirectoryHeader(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<s32>(response.params[1]);
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result ClientImpl::SetPriorityForDirectory(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.MakeSetPriorityForDirectoryHeader(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]));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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]));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result ClientImpl::ReadFile(s64 *out, void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::ReadOption option) {
|
|
AMS_UNUSED(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<size_t>(offset) && util::IsIntValueRepresentable<size_t>(buffer_size)) {
|
|
size_t read_size;
|
|
if (m_cache_manager.ReadFile(std::addressof(read_size), buffer, handle, static_cast<size_t>(offset), static_cast<size_t>(buffer_size))) {
|
|
AMS_ASSERT(util::IsIntValueRepresentable<s64>(read_size));
|
|
|
|
*out = static_cast<s64>(read_size);
|
|
R_SUCCEED();
|
|
}
|
|
}
|
|
|
|
/* 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());
|
|
R_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());
|
|
R_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;
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result ClientImpl::ReadFileLarge(s64 *out, void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::ReadOption option) {
|
|
AMS_UNUSED(option);
|
|
|
|
/* Check our buffer size. */
|
|
R_UNLESS(util::IsIntValueRepresentable<size_t>(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());
|
|
R_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());
|
|
R_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];
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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]));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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));
|
|
|
|
/* 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<HtcfsResult>(response.params[0]) != HtcfsResult::Ready) {
|
|
R_RETURN(ConvertHtcfsResult(response.params[0]));
|
|
}
|
|
|
|
/* Verify that our send will be valid. */
|
|
AMS_ASSERT(util::IsIntValueRepresentable<size_t>(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]));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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];
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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]));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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]));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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<s32>(response.params[1]);
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
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]));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result ClientImpl::GetWorkingDirectory(char *dst, size_t dst_size) {
|
|
/* 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.MakeGetWorkingDirectoryHeader(std::addressof(request));
|
|
|
|
/* 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());
|
|
R_RETURN(htcfs_result);
|
|
}
|
|
|
|
/* Check that the body size is valid. */
|
|
R_UNLESS(response.body_size < static_cast<s64>(dst_size), htcfs::ResultUnexpectedResponseBodySize());
|
|
|
|
/* Receive the response body. */
|
|
R_TRY(this->ReceiveFromRpcChannel(dst, response.body_size));
|
|
|
|
/* Null-terminate the response body. */
|
|
dst[response.body_size] = '\x00';
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result ClientImpl::GetWorkingDirectorySize(s32 *out) {
|
|
/* 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.MakeGetWorkingDirectorySizeHeader(std::addressof(request));
|
|
|
|
/* 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());
|
|
R_RETURN(htcfs_result);
|
|
}
|
|
|
|
/* Check that the size is representable. */
|
|
R_UNLESS(util::IsIntValueRepresentable<s32>(response.params[1]), htcfs::ResultInvalidSize());
|
|
|
|
/* Set the output size. */
|
|
*out = static_cast<s32>(response.params[1]);
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
}
|