From 355010ad84e9a018b6345e00bfa49318dd14ec89 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 30 Apr 2021 04:21:03 -0700 Subject: [PATCH] erpt: implement forced shutdown detection --- .../stratosphere/erpt/srv/erpt_srv_types.hpp | 5 +- .../include/stratosphere/err.hpp | 2 + .../stratosphere/err/err_system_api.hpp | 25 ++ .../include/stratosphere/err/err_types.hpp | 38 ++ .../source/erpt/srv/erpt_srv_context_impl.cpp | 6 +- .../erpt/srv/erpt_srv_forced_shutdown.cpp | 331 ++++++++++++++++++ .../erpt/srv/erpt_srv_forced_shutdown.hpp | 32 ++ .../source/erpt/srv/erpt_srv_main.cpp | 15 +- .../source/erpt/srv/erpt_srv_service.cpp | 5 +- .../source/erpt/srv/erpt_srv_stream.cpp | 120 ++++--- .../source/erpt/srv/erpt_srv_stream.hpp | 1 + .../libstratosphere/source/err/err_api.cpp | 57 +++ .../source/err/impl/err_string_util.cpp | 26 ++ .../source/err/impl/err_string_util.hpp | 23 ++ .../include/vapours/results/err_results.hpp | 6 +- stratosphere/erpt/erpt.json | 2 +- stratosphere/fatal/source/fatal_service.cpp | 4 +- 17 files changed, 645 insertions(+), 53 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/err/err_system_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/err/err_types.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_forced_shutdown.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_forced_shutdown.hpp create mode 100644 libraries/libstratosphere/source/err/err_api.cpp create mode 100644 libraries/libstratosphere/source/err/impl/err_string_util.cpp create mode 100644 libraries/libstratosphere/source/err/impl/err_string_util.hpp diff --git a/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp b/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp index b5eaa84e2..e42d3b986 100644 --- a/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp @@ -22,8 +22,9 @@ namespace ams::erpt::srv { constexpr inline const char ReportOnSdStoragePath[] = "ersd"; - constexpr inline const char ReportStoragePath[] = "save"; - constexpr inline const char JournalFileName[] = "save:/journal"; + constexpr inline const char ReportStoragePath[] = "save"; + constexpr inline const char JournalFileName[] = "save:/journal"; + constexpr inline const char ForcedShutdownContextFileName[] = "save:/forced-shutdown"; constexpr size_t ReportFileNameLength = 64; constexpr size_t AttachmentFileNameLength = 64; diff --git a/libraries/libstratosphere/include/stratosphere/err.hpp b/libraries/libstratosphere/include/stratosphere/err.hpp index c8cb979a0..f8dd9fc6b 100644 --- a/libraries/libstratosphere/include/stratosphere/err.hpp +++ b/libraries/libstratosphere/include/stratosphere/err.hpp @@ -16,4 +16,6 @@ #pragma once +#include #include +#include diff --git a/libraries/libstratosphere/include/stratosphere/err/err_system_api.hpp b/libraries/libstratosphere/include/stratosphere/err/err_system_api.hpp new file mode 100644 index 000000000..db5c85b32 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/err/err_system_api.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::err { + + ErrorCode ConvertResultToErrorCode(const Result &result); + void GetErrorCodeString(char *dst, size_t dst_size, ErrorCode error_code); + +} diff --git a/libraries/libstratosphere/include/stratosphere/err/err_types.hpp b/libraries/libstratosphere/include/stratosphere/err/err_types.hpp new file mode 100644 index 000000000..9b785bac1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/err/err_types.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::err { + + using ErrorCodeCategory = u32; + using ErrorCodeNumber = u32; + + struct ErrorCode { + static constexpr auto StringLengthMax = 15; + + ErrorCodeCategory category; + ErrorCodeNumber number; + + constexpr ALWAYS_INLINE bool IsValid() const { return this->category > 0; } + }; + + constexpr inline ErrorCode InvalidErrorCode = { + .category = 0, + .number = 0, + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp index 41139a53f..b9dd8c082 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp @@ -19,6 +19,7 @@ #include "erpt_srv_context.hpp" #include "erpt_srv_reporter.hpp" #include "erpt_srv_journal.hpp" +#include "erpt_srv_forced_shutdown.hpp" namespace ams::erpt::srv { @@ -32,6 +33,8 @@ namespace ams::erpt::srv { R_UNLESS(ctx_size == sizeof(ContextEntry), erpt::ResultInvalidArgument()); R_UNLESS(data_size <= ArrayBufferSizeMax, erpt::ResultInvalidArgument()); + SubmitContextForForcedShutdownDetection(ctx, data, data_size); + return Context::SubmitContext(ctx, data, data_size); } @@ -171,7 +174,8 @@ namespace ams::erpt::srv { } Result ContextImpl::InvalidateForcedShutdownDetection() { - /* TODO: For greater accuracy, we should support the forced shutdown detection feature added in 12.0.0. */ + /* NOTE: Nintendo does not check the result here. */ + erpt::srv::InvalidateForcedShutdownDetection(); return ResultSuccess(); } diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_forced_shutdown.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_forced_shutdown.cpp new file mode 100644 index 000000000..df670b526 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_forced_shutdown.cpp @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_forced_shutdown.hpp" +#include "erpt_srv_context.hpp" +#include "erpt_srv_context_record.hpp" +#include "erpt_srv_reporter.hpp" +#include "erpt_srv_stream.hpp" + +namespace ams::erpt::srv { + + namespace { + + constexpr u32 ForcedShutdownContextBufferSize = 1_KB; + + constexpr u32 ForcedShutdownContextVersion = 1; + + struct ForcedShutdownContextHeader { + u32 version; + u32 num_contexts; + }; + static_assert(sizeof(ForcedShutdownContextHeader) == 8); + + struct ForcedShutdownContextEntry { + u32 version; + CategoryId category; + u32 field_count; + u32 array_buffer_size; + }; + static_assert(sizeof(ForcedShutdownContextEntry) == 16); + + os::Event g_forced_shutdown_update_event(os::EventClearMode_ManualClear); + + constinit ContextEntry g_forced_shutdown_contexts[] = { + { .category = CategoryId_RunningApplicationInfo, }, + { .category = CategoryId_RunningAppletInfo, }, + { .category = CategoryId_FocusedAppletHistoryInfo, }, + }; + + bool IsForceShutdownDetected() { + fs::DirectoryEntryType entry_type; + return R_SUCCEEDED(fs::GetEntryType(std::addressof(entry_type), ForcedShutdownContextFileName)); + } + + Result CreateForcedShutdownContext() { + /* Create the context. */ + { + /* Create the stream. */ + Stream stream; + R_TRY(stream.OpenStream(ForcedShutdownContextFileName, StreamMode_Write, 0)); + + /* Write a context header. */ + const ForcedShutdownContextHeader header = { .version = ForcedShutdownContextVersion, .num_contexts = 0, }; + R_TRY(stream.WriteStream(reinterpret_cast(std::addressof(header)), sizeof(header))); + } + + /* Commit the context. */ + R_TRY(Stream::CommitStream()); + + return ResultSuccess(); + } + + Result CreateReportForForcedShutdown() { + /* Create a new context record. */ + /* NOTE: Nintendo does not check that this allocation succeeds. */ + auto record = std::make_unique(CategoryId_ErrorInfo); + + /* Create error code for the report. */ + char error_code_str[err::ErrorCode::StringLengthMax]; + err::GetErrorCodeString(error_code_str, sizeof(error_code_str), err::ConvertResultToErrorCode(err::ResultForcedShutdownDetected())); + + /* Add error code to the context. */ + R_TRY(record->Add(FieldId_ErrorCode, error_code_str, std::strlen(error_code_str))); + + /* Create report. */ + R_TRY(Reporter::CreateReport(ReportType_Invisible, ResultSuccess(), std::move(record), nullptr, nullptr, 0)); + + return ResultSuccess(); + } + + Result LoadForcedShutdownContext() { + /* Create the stream to read the context. */ + Stream stream; + R_TRY(stream.OpenStream(ForcedShutdownContextFileName, StreamMode_Read, ForcedShutdownContextBufferSize)); + + /* Read the header. */ + u32 read_size; + ForcedShutdownContextHeader header; + R_TRY(stream.ReadStream(std::addressof(read_size), reinterpret_cast(std::addressof(header)), sizeof(header))); + + /* Validate the header. */ + R_SUCCEED_IF(read_size != sizeof(header)); + R_SUCCEED_IF(ForcedShutdownContextVersion); + R_SUCCEED_IF(header.num_contexts == 0); + + /* Read out the contexts. */ + for (u32 i = 0; i < header.num_contexts; ++i) { + /* Read the context entry header. */ + ForcedShutdownContextEntry entry_header; + R_TRY(stream.ReadStream(std::addressof(read_size), reinterpret_cast(std::addressof(entry_header)), sizeof(entry_header))); + + if (read_size != sizeof(entry_header)) { + break; + } + + if (entry_header.field_count == 0) { + continue; + } + + /* Read the saved data into a context entry. */ + ContextEntry ctx = { + .version = entry_header.version, + .field_count = entry_header.field_count, + .category = entry_header.category, + }; + + /* Check that the field count is valid. */ + AMS_ABORT_UNLESS(entry_header.field_count <= util::size(ctx.fields)); + + /* Read the fields. */ + R_TRY(stream.ReadStream(std::addressof(read_size), reinterpret_cast(std::addressof(ctx.fields)), entry_header.field_count * sizeof(ctx.fields[0]))); + if (read_size != entry_header.field_count * sizeof(ctx.fields[0])) { + break; + } + + /* Allocate an array buffer. */ + u8 *array_buffer = static_cast(Allocate(entry_header.array_buffer_size)); + if (array_buffer == nullptr) { + break; + } + ON_SCOPE_EXIT { Deallocate(array_buffer); }; + + /* Read the array buffer data. */ + R_TRY(stream.ReadStream(std::addressof(read_size), array_buffer, entry_header.array_buffer_size)); + if (read_size != entry_header.array_buffer_size) { + break; + } + + /* Create a record for the context. */ + auto record = std::make_unique(); + if (record == nullptr) { + break; + } + + /* Initialize the record. */ + R_TRY(record->Initialize(std::addressof(ctx), array_buffer, entry_header.array_buffer_size)); + + /* Submit the record. */ + R_TRY(Context::SubmitContextRecord(std::move(record))); + } + + return ResultSuccess(); + } + + u32 GetForcedShutdownContextCount() { + u32 count = 0; + for (const auto &ctx : g_forced_shutdown_contexts) { + if (ctx.field_count != 0) { + ++count; + } + } + return count; + } + + Result SaveForcedShutdownContextImpl() { + /* Save context to file. */ + { + /* Create the stream to write the context. */ + Stream stream; + R_TRY(stream.OpenStream(ForcedShutdownContextFileName, StreamMode_Write, ForcedShutdownContextBufferSize)); + + /* Write a context header. */ + const ForcedShutdownContextHeader header = { .version = ForcedShutdownContextVersion, .num_contexts = GetForcedShutdownContextCount(), }; + R_TRY(stream.WriteStream(reinterpret_cast(std::addressof(header)), sizeof(header))); + + /* Write each context. */ + for (const auto &ctx : g_forced_shutdown_contexts) { + /* If the context has no fields, continue. */ + if (ctx.field_count == 0) { + continue; + } + + /* Write a context entry header. */ + const ForcedShutdownContextEntry entry_header = { + .version = ctx.version, + .category = ctx.category, + .field_count = ctx.field_count, + .array_buffer_size = ctx.array_buffer_size, + }; + R_TRY(stream.WriteStream(reinterpret_cast(std::addressof(entry_header)), sizeof(entry_header))); + + /* Write all fields. */ + for (u32 i = 0; i < ctx.field_count; ++i) { + R_TRY(stream.WriteStream(reinterpret_cast(ctx.fields + i), sizeof(ctx.fields[0]))); + } + + /* Write the array buffer. */ + R_TRY(stream.WriteStream(ctx.array_buffer, ctx.array_buffer_size)); + } + } + + /* Commit the context. */ + R_TRY(Stream::CommitStream()); + + return ResultSuccess(); + } + + } + + os::Event *GetForcedShutdownUpdateEvent() { + return std::addressof(g_forced_shutdown_update_event); + } + + void InitializeForcedShutdownDetection() { + /* Check if the forced shutdown context exists; if it doesn't, we should create an empty one. */ + if (!IsForceShutdownDetected()) { + /* NOTE: Nintendo does not check result here. */ + CreateForcedShutdownContext(); + return; + } + + /* Load the forced shutdown context. */ + /* NOTE: Nintendo does not check that this succeeds. */ + LoadForcedShutdownContext(); + + /* Create report for the forced shutdown. */ + /* NOTE: Nintendo does not check that this succeeds. */ + CreateReportForForcedShutdown(); + + /* Clear the forced shutdown categories. */ + /* NOTE: Nintendo does not check that this succeeds. */ + Context::ClearContext(CategoryId_RunningApplicationInfo); + Context::ClearContext(CategoryId_RunningAppletInfo); + Context::ClearContext(CategoryId_FocusedAppletHistoryInfo); + + /* Save the forced shutdown context. */ + /* NOTE: Nintendo does not check that this succeeds. */ + SaveForcedShutdownContext(); + } + + void FinalizeForcedShutdownDetection() { + /* Try to delete the context. */ + const Result result = Stream::DeleteStream(ForcedShutdownContextFileName); + if (!fs::ResultPathNotFound::Includes(result)) { + /* We must have succeeded, if the file existed. */ + R_ABORT_UNLESS(result); + + /* Commit the deletion. */ + R_ABORT_UNLESS(Stream::CommitStream()); + } + } + + void SaveForcedShutdownContext() { + /* NOTE: Nintendo does not check that saving the report succeeds. */ + SaveForcedShutdownContextImpl(); + } + + void SubmitContextForForcedShutdownDetection(const ContextEntry *entry, const u8 *data, u32 data_size) { + /* If the context entry matches one of our tracked categories, update our stored category. */ + for (auto &ctx : g_forced_shutdown_contexts) { + /* Check for a match. */ + if (ctx.category != entry->category) { + continue; + } + + /* If we have an existing array buffer, free it. */ + if (ctx.array_buffer != nullptr) { + Deallocate(ctx.array_buffer); + ctx.array_buffer = nullptr; + ctx.array_buffer_size = 0; + ctx.array_free_count = 0; + } + + /* Copy in the context. */ + ctx = *entry; + + /* Add the submitted data. */ + if (data != nullptr && data_size > 0) { + /* Allocate new array buffer. */ + ctx.array_buffer = static_cast(Allocate(data_size)); + if (ctx.array_buffer == nullptr) { + /* We failed to allocate; this is okay, but clear our field count. */ + ctx.field_count = 0; + break; + } + + /* Copy in the data. */ + std::memcpy(ctx.array_buffer, data, data_size); + + /* Set buffer extents. */ + ctx.array_buffer_size = data_size; + ctx.array_free_count = 0; + } else { + ctx.array_buffer = nullptr; + ctx.array_buffer_size = 0; + ctx.array_free_count = 0; + } + + /* Signal, to notify that we had an update. */ + g_forced_shutdown_update_event.Signal(); + + /* We're done processing, since we found a match. */ + break; + } + } + + Result InvalidateForcedShutdownDetection() { + /* Delete the forced shutdown context. */ + R_TRY(Stream::DeleteStream(ForcedShutdownContextFileName)); + + /* Commit the deletion. */ + R_TRY(Stream::CommitStream()); + + return ResultSuccess(); + } + + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_forced_shutdown.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_forced_shutdown.hpp new file mode 100644 index 000000000..0aba57fd4 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_forced_shutdown.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::erpt::srv { + + os::Event *GetForcedShutdownUpdateEvent(); + + void InitializeForcedShutdownDetection(); + void FinalizeForcedShutdownDetection(); + + void SaveForcedShutdownContext(); + + void SubmitContextForForcedShutdownDetection(const ContextEntry *entry, const u8 *data, u32 data_size); + + Result InvalidateForcedShutdownDetection(); + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp index eaa24bbd5..30ad7108f 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp @@ -20,6 +20,7 @@ #include "erpt_srv_reporter.hpp" #include "erpt_srv_journal.hpp" #include "erpt_srv_service.hpp" +#include "erpt_srv_forced_shutdown.hpp" namespace ams::erpt::srv { @@ -96,6 +97,10 @@ namespace ams::erpt::srv { } Result InitializeAndStartService() { + /* Initialize forced shutdown detection. */ + /* NOTE: Nintendo does not check error code here. */ + InitializeForcedShutdownDetection(); + return InitializeService(); } @@ -131,7 +136,15 @@ namespace ams::erpt::srv { } void Wait() { - return WaitService(); + /* Get the update event. */ + os::Event *event = GetForcedShutdownUpdateEvent(); + + /* Forever wait, saving any updates. */ + while (true) { + event->Wait(); + event->Clear(); + SaveForcedShutdownContext(); + } } diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_service.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_service.cpp index f342c7292..63fd84a7d 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_service.cpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_service.cpp @@ -18,6 +18,7 @@ #include "erpt_srv_context_impl.hpp" #include "erpt_srv_session_impl.hpp" #include "erpt_srv_stream.hpp" +#include "erpt_srv_forced_shutdown.hpp" namespace ams::erpt::srv { @@ -117,8 +118,10 @@ namespace ams::erpt::srv { case psc::PmState_ReadyAwaken: Stream::EnableFsAccess(true); break; - case psc::PmState_ReadySleep: case psc::PmState_ReadyShutdown: + FinalizeForcedShutdownDetection(); + [[fallthrough]]; + case psc::PmState_ReadySleep: Stream::EnableFsAccess(false); break; default: diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.cpp index 117bf3a53..64d43aeea 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.cpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.cpp @@ -19,7 +19,8 @@ namespace ams::erpt::srv { - bool Stream::s_can_access_fs = true; + constinit bool Stream::s_can_access_fs = true; + constinit os::SdkMutex Stream::s_fs_commit_mutex; void Stream::EnableFsAccess(bool en) { s_can_access_fs = en; @@ -32,6 +33,9 @@ namespace ams::erpt::srv { Result Stream::CommitStream() { R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState()); + + std::scoped_lock lk(s_fs_commit_mutex); + fs::CommitSaveData(ReportStoragePath); return ResultSuccess(); } @@ -52,13 +56,22 @@ namespace ams::erpt::srv { Stream::~Stream() { this->CloseStream(); + AMS_ASSERT(!s_fs_commit_mutex.IsLockedByCurrentThread()); } Result Stream::OpenStream(const char *path, StreamMode mode, u32 buffer_size) { R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState()); R_UNLESS(!this->initialized, erpt::ResultAlreadyInitialized()); + auto lock_guard = SCOPE_GUARD { + if (s_fs_commit_mutex.IsLockedByCurrentThread()) { + s_fs_commit_mutex.Unlock(); + } + }; + if (mode == StreamMode_Write) { + s_fs_commit_mutex.Lock(); + while (true) { R_TRY_CATCH(fs::OpenFile(std::addressof(this->file_handle), path, fs::OpenMode_Write | fs::OpenMode_AllowAppend)) { R_CATCH(fs::ResultPathNotFound) { @@ -71,16 +84,23 @@ namespace ams::erpt::srv { fs::SetFileSize(this->file_handle, 0); } else { R_UNLESS(mode == StreamMode_Read, erpt::ResultInvalidArgument()); + + fs::OpenFile(std::addressof(this->file_handle), path, fs::OpenMode_Read); } - auto file_guard = SCOPE_GUARD { if (mode == StreamMode_Write) { fs::CloseFile(this->file_handle); } }; + auto file_guard = SCOPE_GUARD { fs::CloseFile(this->file_handle); }; std::strncpy(this->file_name, path, sizeof(this->file_name)); this->file_name[sizeof(this->file_name) - 1] = '\x00'; - this->buffer = reinterpret_cast(Allocate(buffer_size)); - R_UNLESS(this->buffer != nullptr, erpt::ResultOutOfMemory()); + if (buffer_size > 0) { + this->buffer = reinterpret_cast(Allocate(buffer_size)); + AMS_ASSERT(this->buffer != nullptr); + } else { + this->buffer = nullptr; + } - this->buffer_size = buffer_size; + + this->buffer_size = this->buffer != nullptr ? buffer_size : 0; this->buffer_count = 0; this->buffer_position = 0; this->file_position = 0; @@ -88,6 +108,7 @@ namespace ams::erpt::srv { this->initialized = true; file_guard.Cancel(); + lock_guard.Cancel(); return ResultSuccess(); } @@ -98,42 +119,38 @@ namespace ams::erpt::srv { R_UNLESS(out != nullptr, erpt::ResultInvalidArgument()); R_UNLESS(dst != nullptr, erpt::ResultInvalidArgument()); - fs::FileHandle tmp_file; size_t fs_read_size; u32 read_count = 0; - bool opened = false; ON_SCOPE_EXIT { *out = read_count; - - if (opened) { - fs::CloseFile(tmp_file); - } }; - while (dst_size > 0) { - if (u32 cur = std::min(this->buffer_count - this->buffer_position, dst_size); cur > 0) { - std::memcpy(dst, this->buffer + this->buffer_position, cur); - this->buffer_position += cur; - dst += cur; - dst_size -= cur; - read_count += cur; - } else { - if (!opened) { - R_TRY(fs::OpenFile(std::addressof(tmp_file), this->file_name, fs::OpenMode_Read)); - opened = true; - } + if (this->buffer != nullptr) { + while (dst_size > 0) { + if (u32 cur = std::min(this->buffer_count - this->buffer_position, dst_size); cur > 0) { + std::memcpy(dst, this->buffer + this->buffer_position, cur); + this->buffer_position += cur; + dst += cur; + dst_size -= cur; + read_count += cur; + } else { + R_TRY(fs::ReadFile(std::addressof(fs_read_size), this->file_handle, this->file_position, this->buffer, this->buffer_size)); - R_TRY(fs::ReadFile(std::addressof(fs_read_size), tmp_file, this->file_position, this->buffer, this->buffer_size)); + this->buffer_position = 0; + this->file_position += static_cast(fs_read_size); + this->buffer_count = static_cast(fs_read_size); - this->buffer_position = 0; - this->file_position += static_cast(fs_read_size); - this->buffer_count = static_cast(fs_read_size); - - if (this->buffer_count == 0) { - break; + if (this->buffer_count == 0) { + break; + } } } + } else { + R_TRY(fs::ReadFile(std::addressof(fs_read_size), this->file_handle, this->file_position, dst, dst_size)); + + this->file_position += static_cast(fs_read_size); + read_count = static_cast(fs_read_size); } return ResultSuccess(); @@ -145,17 +162,22 @@ namespace ams::erpt::srv { R_UNLESS(this->stream_mode == StreamMode_Write, erpt::ResultNotInitialized()); R_UNLESS(src != nullptr || src_size == 0, erpt::ResultInvalidArgument()); - while (src_size > 0) { - if (u32 cur = std::min(this->buffer_size - this->buffer_count, src_size); cur > 0) { - std::memcpy(this->buffer + this->buffer_count, src, cur); - this->buffer_count += cur; - src += cur; - src_size -= cur; - } + if (this->buffer != nullptr) { + while (src_size > 0) { + if (u32 cur = std::min(this->buffer_size - this->buffer_count, src_size); cur > 0) { + std::memcpy(this->buffer + this->buffer_count, src, cur); + this->buffer_count += cur; + src += cur; + src_size -= cur; + } - if (this->buffer_count == this->buffer_size) { - R_TRY(this->Flush()); + if (this->buffer_count == this->buffer_size) { + R_TRY(this->Flush()); + } } + } else { + R_TRY(fs::WriteFile(this->file_handle, this->file_position, src, src_size, fs::WriteOption::None)); + this->file_position += src_size; } return ResultSuccess(); @@ -163,13 +185,23 @@ namespace ams::erpt::srv { void Stream::CloseStream() { if (this->initialized) { - if (s_can_access_fs && this->stream_mode == StreamMode_Write) { - this->Flush(); - fs::FlushFile(this->file_handle); + if (s_can_access_fs) { + if (this->stream_mode == StreamMode_Write) { + this->Flush(); + fs::FlushFile(this->file_handle); + } fs::CloseFile(this->file_handle); } - Deallocate(this->buffer); + + if (this->buffer != nullptr) { + Deallocate(this->buffer); + } + this->initialized = false; + + if (s_fs_commit_mutex.IsLockedByCurrentThread()) { + s_fs_commit_mutex.Unlock(); + } } } @@ -178,6 +210,8 @@ namespace ams::erpt::srv { } Result Stream::Flush() { + AMS_ASSERT(s_fs_commit_mutex.IsLockedByCurrentThread()); + R_SUCCEED_IF(this->buffer_count == 0); R_TRY(fs::WriteFile(this->file_handle, this->file_position, this->buffer, this->buffer_count, fs::WriteOption::None)); diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.hpp index 3a5aaab1f..20035f153 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.hpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.hpp @@ -27,6 +27,7 @@ namespace ams::erpt::srv { class Stream { private: static bool s_can_access_fs; + static os::SdkMutex s_fs_commit_mutex; private: u32 buffer_size; u32 file_position; diff --git a/libraries/libstratosphere/source/err/err_api.cpp b/libraries/libstratosphere/source/err/err_api.cpp new file mode 100644 index 000000000..49da0db01 --- /dev/null +++ b/libraries/libstratosphere/source/err/err_api.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/err_string_util.hpp" + +namespace ams::err { + + namespace impl { + + namespace { + + constexpr int ErrorCodeCategoryPlatformPrefixForResultModule = 2000; + + ALWAYS_INLINE ErrorCode ConvertResultToErrorCode(const Result &result) { + return { + .category = static_cast(ErrorCodeCategoryPlatformPrefixForResultModule + result.GetModule()), + .number = static_cast(result.GetDescription()), + }; + } + + ALWAYS_INLINE Result ConvertErrorCodeToResult(const ErrorCode &error_code) { + const auto result_value = ::ams::result::impl::ResultTraits::MakeValue(error_code.category - ErrorCodeCategoryPlatformPrefixForResultModule, error_code.number); + return ::ams::result::impl::MakeResult(result_value); + } + + } + + } + + ErrorCode ConvertResultToErrorCode(const Result &result) { + AMS_ASSERT(R_FAILED(result)); + + return ::ams::err::impl::ConvertResultToErrorCode(result); + } + + void GetErrorCodeString(char *dst, size_t dst_size, ErrorCode error_code) { + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(dst_size >= static_cast(ErrorCode::StringLengthMax)); + AMS_ASSERT(error_code.IsValid()); + + return ::ams::err::impl::MakeErrorCodeString(dst, dst_size, error_code); + } + +} diff --git a/libraries/libstratosphere/source/err/impl/err_string_util.cpp b/libraries/libstratosphere/source/err/impl/err_string_util.cpp new file mode 100644 index 000000000..f5842da5e --- /dev/null +++ b/libraries/libstratosphere/source/err/impl/err_string_util.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "err_string_util.hpp" + +namespace ams::err::impl { + + void MakeErrorCodeString(char *dst, size_t dst_size, ErrorCode error_code) { + const auto len = util::TSNPrintf(dst, dst_size, "%04d-%04d", error_code.category, error_code.number); + AMS_ASSERT(static_cast(len) < dst_size); + } + +} diff --git a/libraries/libstratosphere/source/err/impl/err_string_util.hpp b/libraries/libstratosphere/source/err/impl/err_string_util.hpp new file mode 100644 index 000000000..7ece51684 --- /dev/null +++ b/libraries/libstratosphere/source/err/impl/err_string_util.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::err::impl { + + void MakeErrorCodeString(char *dst, size_t dst_size, ErrorCode error_code); + +} diff --git a/libraries/libvapours/include/vapours/results/err_results.hpp b/libraries/libvapours/include/vapours/results/err_results.hpp index 9aa60c016..731f9bd1a 100644 --- a/libraries/libvapours/include/vapours/results/err_results.hpp +++ b/libraries/libvapours/include/vapours/results/err_results.hpp @@ -21,7 +21,9 @@ namespace ams::err { R_DEFINE_NAMESPACE_RESULT_MODULE(162); - R_DEFINE_ERROR_RESULT(ApplicationAborted, 1); - R_DEFINE_ERROR_RESULT(SystemModuleAborted, 2); + R_DEFINE_ERROR_RESULT(ApplicationAbort, 1); + R_DEFINE_ERROR_RESULT(SystemProgramAbort, 2); + + R_DEFINE_ERROR_RESULT(ForcedShutdownDetected, 4); } diff --git a/stratosphere/erpt/erpt.json b/stratosphere/erpt/erpt.json index a8e64611b..9c8f57203 100644 --- a/stratosphere/erpt/erpt.json +++ b/stratosphere/erpt/erpt.json @@ -3,7 +3,7 @@ "title_id": "0x010000000000002b", "title_id_range_min": "0x010000000000002b", "title_id_range_max": "0x010000000000002b", - "main_thread_stack_size": "0x00001000", + "main_thread_stack_size": "0x00002000", "main_thread_priority": 49, "default_cpu_id": 3, "process_category": 0, diff --git a/stratosphere/fatal/source/fatal_service.cpp b/stratosphere/fatal/source/fatal_service.cpp index 7afccf666..54fdb3a0a 100644 --- a/stratosphere/fatal/source/fatal_service.cpp +++ b/stratosphere/fatal/source/fatal_service.cpp @@ -105,9 +105,9 @@ namespace ams::fatal::srv { /* Decide whether to generate a report. */ this->context.generate_error_report = (policy == FatalPolicy_ErrorReportAndErrorScreen); - /* Adjust error code (2000-0000 -> 2162-0002). */ + /* Adjust error code (ResultSuccess()/2000-0000 -> err::ResultSystemProgramAbort()/2162-0002). */ if (R_SUCCEEDED(this->context.result)) { - this->context.result = err::ResultSystemModuleAborted(); + this->context.result = err::ResultSystemProgramAbort(); } switch (policy) {