From d20bceff75baa30ec2e73f11525b71c690f0c60a Mon Sep 17 00:00:00 2001
From: Michael Scire <SciresM@gmail.com>
Date: Mon, 15 Feb 2021 18:56:22 -0800
Subject: [PATCH] htc: implement the remaining commands for htcfs

---
 .../source/htcfs/htcfs_cache_manager.hpp      |  10 +
 .../source/htcfs/htcfs_client.hpp             |  28 +-
 .../source/htcfs/htcfs_client_impl.cpp        | 895 +++++++++++++++++-
 .../source/htcfs/htcfs_client_impl.hpp        |  26 +
 .../htcfs/htcfs_directory_service_object.cpp  |   1 -
 .../htcfs/htcfs_file_service_object.cpp       |  35 +-
 .../htcfs_file_system_service_object.cpp      | 108 ++-
 .../source/htcfs/htcfs_header_factory.hpp     |  92 ++
 .../source/htcfs/htcfs_result.hpp             |   1 +
 9 files changed, 1169 insertions(+), 27 deletions(-)

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<size_t>(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<fs::DirectoryEntryType>(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<u64>(response.params[2]);
+        *out_access = static_cast<u64>(response.params[3]);
+        *out_modify = static_cast<u64>(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<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());
+            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<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';
+
+        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<size_t>(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<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);
+                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<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());
+            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<HtcfsResult>(response.params[0]) != HtcfsResult::Ready) {
+            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]));
+
+        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<s32>(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 <stratosphere.hpp>
 #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 <stratosphere.hpp>
 #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<s64> 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<s64> 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<s32> 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<int>(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>(fs::OpenMode_Write | fs::OpenMode_AllowAppend);
+                    break;
+                case 3:
+                    *out = static_cast<fs::OpenMode>(fs::OpenMode_ReadWrite | fs::OpenMode_AllowAppend);
+                    break;
+                default:
+                    return htcfs::ResultInvalidArgument();
+            }
+
+            return ResultSuccess();
+        }
+
     }
 
     Result FileSystemServiceObject::OpenFile(sf::Out<sf::SharedPointer<tma::IFileAccessor>> 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<tma::IFileAccessor, FileServiceObject>(handle);
+        return ResultSuccess();
     }
 
     Result FileSystemServiceObject::FileExists(sf::Out<bool> 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<s32> 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<fs::DirectoryEntryType *>(out.GetPointer()), path.str, case_sensitive);
     }
 
     Result FileSystemServiceObject::OpenDirectory(sf::Out<sf::SharedPointer<tma::IDirectoryAccessor>> out, const tma::Path &path, s32 open_mode, bool case_sensitive) {
@@ -80,35 +129,68 @@ namespace ams::htcfs {
     }
 
     Result FileSystemServiceObject::DirectoryExists(sf::Out<bool> 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<u64> out_create, sf::Out<u64> out_access, sf::Out<u64> 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<char *>(out.GetPointer()), out.GetSize(), path.str);
     }
 
     Result FileSystemServiceObject::GetDiskFreeSpaceExW(sf::Out<s64> out_free, sf::Out<s64> out_total, sf::Out<s64> 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<s64>(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<s64>(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) {