mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
sysupdater: Add ValidateUpdate, begin implementing Async logic
This commit is contained in:
parent
1bc271bab1
commit
f223c27bb0
19 changed files with 677 additions and 3 deletions
|
@ -45,6 +45,7 @@
|
|||
#include <stratosphere/cfg.hpp>
|
||||
#include <stratosphere/dmnt.hpp>
|
||||
#include <stratosphere/erpt.hpp>
|
||||
#include <stratosphere/err.hpp>
|
||||
#include <stratosphere/fatal.hpp>
|
||||
#include <stratosphere/hid.hpp>
|
||||
#include <stratosphere/hos.hpp>
|
||||
|
|
|
@ -62,6 +62,7 @@ namespace ams::impl {
|
|||
AMS_DEFINE_SYSTEM_THREAD(16, mitm_fs, RomFileSystemInitializeThread);
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, mitm, DebugThrowThread);
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, IpcServer);
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, AsyncPrepareSdCardUpdateTask);
|
||||
|
||||
/* boot2. */
|
||||
AMS_DEFINE_SYSTEM_THREAD(20, boot2, Main);
|
||||
|
@ -94,6 +95,7 @@ namespace ams::impl {
|
|||
|
||||
/* ns.*/
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, ns, ApplicationManagerIpcSession);
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, nssrv, AsyncPrepareCardUpdateTask);
|
||||
|
||||
/* settings. */
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, settings, Main);
|
||||
|
|
19
libraries/libstratosphere/include/stratosphere/err.hpp
Normal file
19
libraries/libstratosphere/include/stratosphere/err.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stratosphere/err/err_error_context.hpp>
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/sf/sf_buffer_tags.hpp>
|
||||
|
||||
namespace ams::err {
|
||||
|
||||
enum class ErrorContextType : u8 {
|
||||
None = 0,
|
||||
Http = 1,
|
||||
FileSystem = 2,
|
||||
WebMediaPlayer = 3,
|
||||
LocalContentShare = 4,
|
||||
};
|
||||
|
||||
struct PaddingErrorContext {
|
||||
u8 padding[0x200 - 8];
|
||||
};
|
||||
|
||||
struct ErrorContext : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
|
||||
ErrorContextType type;
|
||||
u8 reserved[7];
|
||||
|
||||
union {
|
||||
PaddingErrorContext padding;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(ErrorContext) == 0x200);
|
||||
static_assert(util::is_pod<ErrorContext>::value);
|
||||
|
||||
}
|
|
@ -45,7 +45,7 @@ namespace ams::ncm {
|
|||
};
|
||||
}
|
||||
|
||||
constexpr ContentMetaKey ToKey() {
|
||||
constexpr ContentMetaKey ToKey() const {
|
||||
return ContentMetaKey::Make(this->id, this->version, this->type);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace ams::os {
|
|||
constexpr inline s32 DefaultThreadPriority = ThreadPriorityRangeSize / 2;
|
||||
constexpr inline s32 LowestThreadPriority = ThreadPriorityRangeSize - 1;
|
||||
|
||||
constexpr inline s32 InvalidThreadPriority = 127;
|
||||
|
||||
constexpr inline s32 LowestSystemThreadPriority = 35;
|
||||
constexpr inline s32 HighestSystemThreadPriority = -12;
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ams::sf {
|
||||
|
|
|
@ -37,8 +37,10 @@
|
|||
#include <vapours/results/kvdb_results.hpp>
|
||||
#include <vapours/results/loader_results.hpp>
|
||||
#include <vapours/results/lr_results.hpp>
|
||||
#include <vapours/results/os_results.hpp>
|
||||
#include <vapours/results/ncm_results.hpp>
|
||||
#include <vapours/results/nim_results.hpp>
|
||||
#include <vapours/results/ns_results.hpp>
|
||||
#include <vapours/results/os_results.hpp>
|
||||
#include <vapours/results/pgl_results.hpp>
|
||||
#include <vapours/results/pm_results.hpp>
|
||||
#include <vapours/results/psc_results.hpp>
|
||||
|
|
26
libraries/libvapours/include/vapours/results/nim_results.hpp
Normal file
26
libraries/libvapours/include/vapours/results/nim_results.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/results/results_common.hpp>
|
||||
|
||||
namespace ams::nim {
|
||||
|
||||
R_DEFINE_NAMESPACE_RESULT_MODULE(137);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(HttpConnectionCanceled, 70);
|
||||
|
||||
}
|
27
libraries/libvapours/include/vapours/results/ns_results.hpp
Normal file
27
libraries/libvapours/include/vapours/results/ns_results.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/results/results_common.hpp>
|
||||
|
||||
namespace ams::ns {
|
||||
|
||||
R_DEFINE_NAMESPACE_RESULT_MODULE(16);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(Canceled, 90);
|
||||
R_DEFINE_ERROR_RESULT(OutOfMaxRunningTask, 110);
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "sysupdater_async_impl.hpp"
|
||||
#include "sysupdater_async_thread_allocator.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
Result AsyncBase::ToAsyncResult(Result result) {
|
||||
R_TRY_CATCH(result) {
|
||||
R_CONVERT(nim::ResultHttpConnectionCanceled, ns::ResultCanceled());
|
||||
R_CONVERT(ncm::ResultInstallTaskCancelled, ns::ResultCanceled());
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
AsyncPrepareSdCardUpdateImpl::~AsyncPrepareSdCardUpdateImpl() {
|
||||
if (this->thread_info) {
|
||||
os::WaitThread(this->thread_info->thread);
|
||||
os::DestroyThread(this->thread_info->thread);
|
||||
GetAsyncThreadAllocator()->Free(*this->thread_info);
|
||||
}
|
||||
}
|
||||
|
||||
Result AsyncPrepareSdCardUpdateImpl::Run() {
|
||||
/* Get a thread info. */
|
||||
ThreadInfo info;
|
||||
R_TRY(GetAsyncThreadAllocator()->Allocate(std::addressof(info)));
|
||||
|
||||
/* Set the thread info's priority. */
|
||||
info.priority = AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_sysupdater, AsyncPrepareSdCardUpdateTask);
|
||||
|
||||
/* Ensure that we clean up appropriately. */
|
||||
ON_SCOPE_EXIT {
|
||||
if (!this->thread_info) {
|
||||
GetAsyncThreadAllocator()->Free(info);
|
||||
}
|
||||
};
|
||||
|
||||
/* Create a thread for the task. */
|
||||
R_TRY(os::CreateThread(info.thread, [](void *arg) {
|
||||
auto *_this = reinterpret_cast<AsyncPrepareSdCardUpdateImpl *>(arg);
|
||||
_this->result = _this->Execute();
|
||||
_this->event.Signal();
|
||||
}, this, info.stack, info.stack_size, info.priority));
|
||||
|
||||
/* Set the thread name. */
|
||||
os::SetThreadNamePointer(info.thread, AMS_GET_SYSTEM_THREAD_NAME(mitm_sysupdater, AsyncPrepareSdCardUpdateTask));
|
||||
|
||||
/* Start the thread. */
|
||||
os::StartThread(info.thread);
|
||||
|
||||
/* Set our thread info. */
|
||||
this->thread_info = info;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result AsyncPrepareSdCardUpdateImpl::Execute() {
|
||||
return this->task->PrepareAndExecute();
|
||||
}
|
||||
|
||||
void AsyncPrepareSdCardUpdateImpl::CancelImpl() {
|
||||
this->task->Cancel();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "sysupdater_thread_allocator.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
class ErrorContextHolder {
|
||||
private:
|
||||
err::ErrorContext error_context;
|
||||
public:
|
||||
constexpr ErrorContextHolder() : error_context{} { /* ... */ }
|
||||
|
||||
virtual ~ErrorContextHolder() { /* ... */ }
|
||||
|
||||
template<typename T>
|
||||
Result SaveErrorContextIfFailed(T &async, Result result) {
|
||||
if (R_FAILED(result)) {
|
||||
async.GetErrorContext(std::addressof(this->error_context));
|
||||
return result;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result GetAndSaveErrorContext(T &async) {
|
||||
R_TRY(this->SaveErrorContextIfFailed(async, async.Get()));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result SaveInternalTaskErrorContextIfFailed(T &async, Result result) {
|
||||
if (R_FAILED(result)) {
|
||||
async.CreateErrorContext(std::addressof(this->error_context));
|
||||
return result;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
const err::ErrorContext &GetErrorContextImpl() {
|
||||
return this->error_context;
|
||||
}
|
||||
};
|
||||
|
||||
class AsyncBase {
|
||||
public:
|
||||
virtual ~AsyncBase() { /* ... */ }
|
||||
|
||||
Result Cancel() {
|
||||
this->CancelImpl();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
static Result ToAsyncResult(Result result);
|
||||
|
||||
virtual Result GetErrorContext(sf::Out<err::ErrorContext> out) {
|
||||
*out = {};
|
||||
return ResultSuccess();
|
||||
}
|
||||
private:
|
||||
virtual void CancelImpl() = 0;
|
||||
};
|
||||
|
||||
class AsyncResultBase : public AsyncBase {
|
||||
public:
|
||||
Result Get() {
|
||||
return ToAsyncResult(this->GetImpl());
|
||||
}
|
||||
private:
|
||||
virtual Result GetImpl() = 0;
|
||||
};
|
||||
|
||||
/* NOTE: Based off of ns AsyncPrepareCardUpdateImpl. */
|
||||
/* We don't implement the RequestServer::ManagedStop details, as we don't implement stoppable request list. */
|
||||
class AsyncPrepareSdCardUpdateImpl : public AsyncResultBase, private ErrorContextHolder {
|
||||
private:
|
||||
Result result;
|
||||
os::SystemEvent event;
|
||||
std::optional<ThreadInfo> thread_info;
|
||||
ncm::InstallTaskBase *task;
|
||||
public:
|
||||
AsyncPrepareSdCardUpdateImpl(ncm::InstallTaskBase *task) : result(ResultSuccess()), event(os::EventClearMode_ManualClear, true), thread_info(), task(task) { /* ... */ }
|
||||
virtual ~AsyncPrepareSdCardUpdateImpl();
|
||||
|
||||
os::SystemEvent &GetEvent() { return this->event; }
|
||||
|
||||
virtual Result GetErrorContext(sf::Out<err::ErrorContext> out) override {
|
||||
*out = ErrorContextHolder::GetErrorContextImpl();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result Run();
|
||||
private:
|
||||
Result Execute();
|
||||
|
||||
virtual void CancelImpl() override;
|
||||
virtual Result GetImpl() override { return this->result; }
|
||||
};
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "sysupdater_async_thread_allocator.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline int AsyncThreadCount = 1;
|
||||
constexpr inline size_t AsyncThreadStackSize = 16_KB;
|
||||
|
||||
os::ThreadType g_async_threads[AsyncThreadCount];
|
||||
alignas(os::ThreadStackAlignment) u8 g_async_thread_stack_heap[AsyncThreadCount * AsyncThreadStackSize];
|
||||
|
||||
constinit ThreadAllocator g_async_thread_allocator(g_async_threads, AsyncThreadCount, os::InvalidThreadPriority, g_async_thread_stack_heap, sizeof(g_async_thread_stack_heap), AsyncThreadStackSize);
|
||||
|
||||
}
|
||||
|
||||
ThreadAllocator *GetAsyncThreadAllocator() {
|
||||
return std::addressof(g_async_thread_allocator);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "sysupdater_thread_allocator.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
ThreadAllocator *GetAsyncThreadAllocator();
|
||||
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
#include "../amsmitm_initialization.hpp"
|
||||
#include "sysupdater_module.hpp"
|
||||
#include "sysupdater_service.hpp"
|
||||
#include "sysupdater_async_impl.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
|
|
|
@ -122,6 +122,159 @@ namespace ams::mitm::sysupdater {
|
|||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ValidateSystemUpdate(Result *out_result, UpdateValidationInfo *out_info, const ncm::PackagedContentMetaReader &update_reader, const char *package_root) {
|
||||
/* Clear output. */
|
||||
*out_result = ResultSuccess();
|
||||
|
||||
/* We want to track all content the update requires. */
|
||||
const size_t num_content_metas = update_reader.GetContentMetaCount();
|
||||
bool content_meta_valid[num_content_metas] = {};
|
||||
|
||||
/* Allocate a buffer to use for validation. */
|
||||
size_t data_buffer_size = 1_MB;
|
||||
void *data_buffer;
|
||||
do {
|
||||
data_buffer = std::malloc(data_buffer_size);
|
||||
if (data_buffer != nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
data_buffer_size /= 2;
|
||||
} while (data_buffer_size >= 16_KB);
|
||||
R_UNLESS(data_buffer != nullptr, fs::ResultAllocationFailureInNew());
|
||||
|
||||
ON_SCOPE_EXIT { std::free(data_buffer); };
|
||||
|
||||
/* Declare helper for result validation. */
|
||||
auto ValidateResult = [&] ALWAYS_INLINE_LAMBDA (Result result) -> Result {
|
||||
*out_result = result;
|
||||
return result;
|
||||
};
|
||||
|
||||
/* Iterate over all files to find all content metas. */
|
||||
R_TRY(ForEachFileInDirectory(package_root, [&](bool *done, const fs::DirectoryEntry &entry) -> Result {
|
||||
/* Clear output. */
|
||||
*out_info = {};
|
||||
|
||||
/* Don't early terminate by default. */
|
||||
*done = false;
|
||||
|
||||
/* We have nothing to list if we're not looking at a meta. */
|
||||
R_SUCCEED_IF(!PathView(entry.name).HasSuffix(".cnmt.nca"));
|
||||
|
||||
/* Read the content meta path, and build. */
|
||||
ncm::AutoBuffer package_meta;
|
||||
R_TRY(LoadContentMeta(std::addressof(package_meta), package_root, entry));
|
||||
|
||||
/* Create a reader. */
|
||||
const auto reader = ncm::PackagedContentMetaReader(package_meta.Get(), package_meta.GetSize());
|
||||
|
||||
/* Get the key for the reader. */
|
||||
const auto key = reader.GetKey();
|
||||
|
||||
/* Check if we need to validate this content. */
|
||||
bool need_validate = false;
|
||||
size_t validation_index = 0;
|
||||
for (size_t i = 0; i < num_content_metas; ++i) {
|
||||
if (update_reader.GetContentMetaInfo(i)->ToKey() == key) {
|
||||
need_validate = true;
|
||||
validation_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we don't need to validate, continue. */
|
||||
R_SUCCEED_IF(!need_validate);
|
||||
|
||||
/* We're validating. */
|
||||
out_info->invalid_key = key;
|
||||
|
||||
/* Validate all contents. */
|
||||
for (size_t i = 0; i < reader.GetContentCount(); ++i) {
|
||||
const auto *content_info = reader.GetContentInfo(i);
|
||||
const auto &content_id = content_info->GetId();
|
||||
const s64 content_size = content_info->info.GetSize();
|
||||
out_info->invalid_content_id = content_id;
|
||||
|
||||
/* Get the content id string. */
|
||||
auto content_id_str = ncm::GetContentIdString(content_id);
|
||||
|
||||
/* Open the file. */
|
||||
fs::FileHandle file;
|
||||
{
|
||||
char path[fs::EntryNameLengthMax];
|
||||
std::snprintf(path, sizeof(path), "%s%s%s", package_root, content_id_str.data, content_info->GetType() == ncm::ContentType::Meta ? ".cnmt.nca" : ".nca");
|
||||
if (R_FAILED(ValidateResult(fs::OpenFile(std::addressof(file), path, ams::fs::OpenMode_Read)))) {
|
||||
*done = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Validate the file size is correct. */
|
||||
s64 file_size;
|
||||
if (R_FAILED(ValidateResult(fs::GetFileSize(std::addressof(file_size), file)))) {
|
||||
*done = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
if (file_size != content_size) {
|
||||
*out_result = ncm::ResultInvalidContentHash();
|
||||
*done = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* Read and hash the file in chunks. */
|
||||
crypto::Sha256Generator sha;
|
||||
sha.Initialize();
|
||||
|
||||
s64 ofs = 0;
|
||||
while (ofs < content_size) {
|
||||
const size_t cur_size = std::min(static_cast<size_t>(content_size - ofs), data_buffer_size);
|
||||
if (R_FAILED(ValidateResult(fs::ReadFile(file, ofs, data_buffer, cur_size)))) {
|
||||
*done = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
sha.Update(data_buffer, cur_size);
|
||||
|
||||
ofs += cur_size;
|
||||
}
|
||||
|
||||
/* Get the hash. */
|
||||
ncm::Digest calc_digest;
|
||||
sha.GetHash(std::addressof(calc_digest), sizeof(calc_digest));
|
||||
|
||||
/* Validate the hash. */
|
||||
if (std::memcmp(std::addressof(calc_digest), std::addressof(content_info->digest), sizeof(ncm::Digest)) != 0) {
|
||||
*out_result = ncm::ResultInvalidContentHash();
|
||||
*done = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark the relevant content as validated. */
|
||||
content_meta_valid[validation_index] = true;
|
||||
*out_info = {};
|
||||
|
||||
return ResultSuccess();
|
||||
}));
|
||||
|
||||
/* If we're otherwise going to succeed, ensure that every content was found. */
|
||||
if (R_SUCCEEDED(*out_result)) {
|
||||
for (size_t i = 0; i < num_content_metas; ++i) {
|
||||
if (!content_meta_valid[i]) {
|
||||
*out_result = fs::ResultPathNotFound();
|
||||
*out_info = {
|
||||
.invalid_key = update_reader.GetContentMetaInfo(i)->ToKey(),
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ActivateSystemUpdateContentMetaDatabase() {
|
||||
/* TODO: Don't use gamecard db. */
|
||||
return ncm::ActivateContentMetaDatabase(ncm::StorageId::GameCard);
|
||||
|
@ -244,4 +397,29 @@ namespace ams::mitm::sysupdater {
|
|||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result SystemUpdateService::ValidateUpdate(sf::Out<Result> out_validate_result, sf::Out<UpdateValidationInfo> out_validate_info, const ncm::Path &path) {
|
||||
/* Adjust the path. */
|
||||
ncm::Path package_root;
|
||||
R_TRY(FormatUserPackagePath(std::addressof(package_root), path));
|
||||
|
||||
/* Parse the update. */
|
||||
{
|
||||
/* Get the content info for the system update. */
|
||||
ncm::ContentInfo content_info;
|
||||
R_TRY(GetSystemUpdateUpdateContentInfoFromPackage(std::addressof(content_info), package_root.str));
|
||||
|
||||
/* Read the content meta. */
|
||||
ncm::AutoBuffer content_meta_buffer;
|
||||
R_TRY(ReadContentMetaPath(std::addressof(content_meta_buffer), package_root.str, content_info));
|
||||
|
||||
/* Create a reader. */
|
||||
const auto reader = ncm::PackagedContentMetaReader(content_meta_buffer.Get(), content_meta_buffer.GetSize());
|
||||
|
||||
/* Validate the update. */
|
||||
R_TRY(ValidateSystemUpdate(out_validate_result.GetPointer(), out_validate_info.GetPointer(), reader, package_root.str));
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -27,16 +27,24 @@ namespace ams::mitm::sysupdater {
|
|||
ncm::FirmwareVariationId firmware_variation_ids[FirmwareVariationCountMax];
|
||||
};
|
||||
|
||||
struct UpdateValidationInfo {
|
||||
ncm::ContentMetaKey invalid_key;
|
||||
ncm::ContentId invalid_content_id;
|
||||
};
|
||||
|
||||
class SystemUpdateService final : public sf::IServiceObject {
|
||||
private:
|
||||
enum class CommandId {
|
||||
GetUpdateInformation = 0,
|
||||
ValidateUpdate = 1,
|
||||
};
|
||||
private:
|
||||
Result GetUpdateInformation(sf::Out<UpdateInformation> out, const ncm::Path &path);
|
||||
Result ValidateUpdate(sf::Out<Result> out_validate_result, sf::Out<UpdateValidationInfo> out_validate_info, const ncm::Path &path);
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
MAKE_SERVICE_COMMAND_META(GetUpdateInformation),
|
||||
MAKE_SERVICE_COMMAND_META(ValidateUpdate),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "sysupdater_thread_allocator.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
Result ThreadAllocator::Allocate(ThreadInfo *out) {
|
||||
std::scoped_lock lk(this->mutex);
|
||||
|
||||
for (int i = 0; i < this->thread_count; ++i) {
|
||||
const u64 mask = (static_cast<u64>(1) << i);
|
||||
if ((this->bitmap & mask) == 0) {
|
||||
*out = {
|
||||
.thread = this->thread_list + i,
|
||||
.priority = this->thread_priority,
|
||||
.stack = this->stack_heap + (this->stack_size * i),
|
||||
.stack_size = this->stack_size,
|
||||
};
|
||||
this->bitmap |= mask;
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
return ns::ResultOutOfMaxRunningTask();
|
||||
}
|
||||
|
||||
void ThreadAllocator::Free(const ThreadInfo &info) {
|
||||
std::scoped_lock lk(this->mutex);
|
||||
|
||||
for (int i = 0; i < this->thread_count; ++i) {
|
||||
if (info.thread == std::addressof(this->thread_list[i])) {
|
||||
const u64 mask = (static_cast<u64>(1) << i);
|
||||
this->bitmap &= ~mask;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AMS_ABORT("Invalid thread passed to ThreadAllocator::Free");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
struct ThreadInfo {
|
||||
os::ThreadType *thread;
|
||||
int priority;
|
||||
void *stack;
|
||||
size_t stack_size;
|
||||
};
|
||||
|
||||
/* NOTE: Nintendo uses a util::BitArray, but this seems excessive. */
|
||||
class ThreadAllocator {
|
||||
private:
|
||||
os::ThreadType *thread_list;
|
||||
const int thread_priority;
|
||||
const int thread_count;
|
||||
u8 *stack_heap;
|
||||
const size_t stack_heap_size;
|
||||
const size_t stack_size;
|
||||
u64 bitmap;
|
||||
os::SdkMutex mutex;
|
||||
public:
|
||||
constexpr ThreadAllocator(os::ThreadType *thread_list, int count, int priority, u8 *stack_heap, size_t stack_heap_size, size_t stack_size)
|
||||
: thread_list(thread_list), thread_priority(priority), thread_count(count), stack_heap(stack_heap), stack_heap_size(stack_heap_size), stack_size(stack_size), bitmap()
|
||||
{
|
||||
AMS_ASSERT(count <= static_cast<int>(stack_heap_size / stack_size));
|
||||
AMS_ASSERT(count <= static_cast<int>(BITSIZEOF(this->bitmap)));
|
||||
}
|
||||
|
||||
Result Allocate(ThreadInfo *out);
|
||||
void Free(const ThreadInfo &info);
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in a new issue