From 79ae47f028ef9994fcd9c390d7523ceb37ad608c Mon Sep 17 00:00:00 2001 From: Adubbz Date: Tue, 19 May 2020 01:03:38 +1000 Subject: [PATCH] ncm: implement firmware downgrading (#958) * ncm: implement firmware downgrading * ncm: make storage list const --- .../include/stratosphere/ncm.hpp | 1 + .../ncm/ncm_install_task_base.hpp | 4 +- .../ncm/ncm_package_system_downgrade_task.hpp | 30 ++++++++ .../ncm/ncm_package_system_update_task.hpp | 3 +- .../ncm/ncm_package_system_downgrade_task.cpp | 76 +++++++++++++++++++ 5 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_downgrade_task.hpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_package_system_downgrade_task.cpp diff --git a/libraries/libstratosphere/include/stratosphere/ncm.hpp b/libraries/libstratosphere/include/stratosphere/ncm.hpp index e87fdfa2c..04cb7515f 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm.hpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp index 4959f42fe..8243efbb2 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp @@ -139,10 +139,11 @@ namespace ams::ncm { Result CountInstallContentMetaData(s32 *out_count); Result GetInstallContentMetaData(InstallContentMeta *out_content_meta, s32 index); Result DeleteInstallContentMetaData(const ContentMetaKey *keys, s32 num_keys); + virtual Result GetInstallContentMetaInfo(InstallContentMetaInfo *out_info, const ContentMetaKey &key) = 0; virtual Result PrepareDependency(); Result PrepareSystemUpdateDependency(); - Result PrepareContentMetaIfLatest(const ContentMetaKey &key); + virtual Result PrepareContentMetaIfLatest(const ContentMetaKey &key); /* NOTE: This is not virtual in Nintendo's code. We do so to facilitate downgrades. */ u32 GetConfig() const { return this->config; } Result WriteContentMetaToPlaceHolder(InstallContentInfo *out_install_content_info, ContentStorage *storage, const InstallContentMetaInfo &meta_info, std::optional is_temporary); @@ -163,7 +164,6 @@ namespace ams::ncm { Result VerifyAllNotCommitted(const StorageContentMetaKey *keys, s32 num_keys); virtual Result PrepareInstallContentMetaData() = 0; - virtual Result GetInstallContentMetaInfo(InstallContentMetaInfo *out_info, const ContentMetaKey &key) = 0; virtual Result GetLatestVersion(std::optional *out_version, u64 id) { return ncm::ResultContentMetaNotFound(); } virtual Result OnExecuteComplete() { return ResultSuccess(); } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_downgrade_task.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_downgrade_task.hpp new file mode 100644 index 000000000..e70af190d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_downgrade_task.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019-2020 Adubbz, 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::ncm { + + class PackageSystemDowngradeTask : public PackageSystemUpdateTask { + public: + Result Commit(); + protected: + virtual Result PrepareContentMetaIfLatest(const ContentMetaKey &key) override; + private: + Result PreCommit(); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp index 4648d20f6..fdaf68217 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp @@ -34,9 +34,10 @@ namespace ams::ncm { std::optional GetSystemUpdateMetaKey(); protected: virtual Result PrepareInstallContentMetaData() override; + virtual Result GetInstallContentMetaInfo(InstallContentMetaInfo *out, const ContentMetaKey &key) override; + InstallTaskDataBase &GetInstallData() { return this->data; } /* Atmosphere extension. */ private: virtual Result PrepareDependency() override; - virtual Result GetInstallContentMetaInfo(InstallContentMetaInfo *out, const ContentMetaKey &key) override; Result GetContentInfoOfContentMeta(ContentInfo *out, const ContentMetaKey &key); }; diff --git a/libraries/libstratosphere/source/ncm/ncm_package_system_downgrade_task.cpp b/libraries/libstratosphere/source/ncm/ncm_package_system_downgrade_task.cpp new file mode 100644 index 000000000..95b7a518d --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_package_system_downgrade_task.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019-2020 Adubbz, 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 + +namespace ams::ncm { + + Result PackageSystemDowngradeTask::PreCommit() { + constexpr size_t MaxContentMetas = 0x40; + + auto &data = this->GetInstallData(); + + /* Count the number of content meta entries. */ + s32 count; + R_TRY(data.Count(std::addressof(count))); + + /* Iterate over content meta. */ + for (s32 i = 0; i < count; i++) { + /* Obtain the content meta. */ + InstallContentMeta content_meta; + R_TRY(data.Get(std::addressof(content_meta), i)); + + /* Create a reader. */ + const auto reader = content_meta.GetReader(); + const auto key = reader.GetKey(); + + /* Obtain a list of suitable storage ids. */ + const auto storage_list = GetStorageList(this->GetInstallStorage()); + + /* Iterate over storage ids. */ + for (s32 i = 0; i < storage_list.Count(); i++) { + /* Open the content meta database. */ + ContentMetaDatabase meta_db; + if (R_FAILED(OpenContentMetaDatabase(std::addressof(meta_db), storage_list[i]))) { + continue; + } + + /* List keys matching the type and id of the install content meta key. */ + ncm::ContentMetaKey keys[MaxContentMetas]; + const auto count = meta_db.ListContentMeta(keys, MaxContentMetas, key.type, { key.id }); + + /* Remove matching keys. */ + for (auto i = 0; i < count.total; i++) { + meta_db.Remove(keys[i]); + } + } + } + + return ResultSuccess(); + } + + Result PackageSystemDowngradeTask::Commit() { + R_TRY(this->PreCommit()); + return InstallTaskBase::Commit(); + } + + Result PackageSystemDowngradeTask::PrepareContentMetaIfLatest(const ContentMetaKey &key) { + /* Get and prepare install content meta info. We aren't concerned if our key is older. */ + InstallContentMetaInfo install_content_meta_info; + R_TRY(this->GetInstallContentMetaInfo(std::addressof(install_content_meta_info), key)); + return this->PrepareContentMeta(install_content_meta_info, key, std::nullopt); + } + +}