Merge pull request #9313 from leoetlino/check-content-hashes
WiiUtils: Check hashes to determine if a title is installed and up-to-date
This commit is contained in:
commit
19324e6ed9
|
@ -81,6 +81,12 @@ public:
|
||||||
s32 ipc_fd = -1;
|
s32 ipc_fd = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class CheckContentHashes : bool
|
||||||
|
{
|
||||||
|
Yes = true,
|
||||||
|
No = false,
|
||||||
|
};
|
||||||
|
|
||||||
IOS::ES::TMDReader FindImportTMD(u64 title_id) const;
|
IOS::ES::TMDReader FindImportTMD(u64 title_id) const;
|
||||||
IOS::ES::TMDReader FindInstalledTMD(u64 title_id) const;
|
IOS::ES::TMDReader FindInstalledTMD(u64 title_id) const;
|
||||||
IOS::ES::TicketReader FindSignedTicket(u64 title_id) const;
|
IOS::ES::TicketReader FindSignedTicket(u64 title_id) const;
|
||||||
|
@ -92,7 +98,9 @@ public:
|
||||||
// Get titles for which there is a ticket (in /ticket).
|
// Get titles for which there is a ticket (in /ticket).
|
||||||
std::vector<u64> GetTitlesWithTickets() const;
|
std::vector<u64> GetTitlesWithTickets() const;
|
||||||
|
|
||||||
std::vector<IOS::ES::Content> GetStoredContentsFromTMD(const IOS::ES::TMDReader& tmd) const;
|
std::vector<IOS::ES::Content>
|
||||||
|
GetStoredContentsFromTMD(const IOS::ES::TMDReader& tmd,
|
||||||
|
CheckContentHashes check_content_hashes = CheckContentHashes::No) const;
|
||||||
u32 GetSharedContentsCount() const;
|
u32 GetSharedContentsCount() const;
|
||||||
std::vector<std::array<u8, 20>> GetSharedContents() const;
|
std::vector<std::array<u8, 20>> GetSharedContents() const;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
#include <mbedtls/sha1.h>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
|
@ -163,7 +164,9 @@ std::vector<u64> ES::GetTitlesWithTickets() const
|
||||||
return title_ids;
|
return title_ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<IOS::ES::Content> ES::GetStoredContentsFromTMD(const IOS::ES::TMDReader& tmd) const
|
std::vector<IOS::ES::Content>
|
||||||
|
ES::GetStoredContentsFromTMD(const IOS::ES::TMDReader& tmd,
|
||||||
|
CheckContentHashes check_content_hashes) const
|
||||||
{
|
{
|
||||||
if (!tmd.IsValid())
|
if (!tmd.IsValid())
|
||||||
return {};
|
return {};
|
||||||
|
@ -174,10 +177,29 @@ std::vector<IOS::ES::Content> ES::GetStoredContentsFromTMD(const IOS::ES::TMDRea
|
||||||
std::vector<IOS::ES::Content> stored_contents;
|
std::vector<IOS::ES::Content> stored_contents;
|
||||||
|
|
||||||
std::copy_if(contents.begin(), contents.end(), std::back_inserter(stored_contents),
|
std::copy_if(contents.begin(), contents.end(), std::back_inserter(stored_contents),
|
||||||
[this, &tmd, &map](const IOS::ES::Content& content) {
|
[this, &tmd, &map, check_content_hashes](const IOS::ES::Content& content) {
|
||||||
|
const auto fs = m_ios.GetFS();
|
||||||
|
|
||||||
const std::string path = GetContentPath(tmd.GetTitleId(), content, map);
|
const std::string path = GetContentPath(tmd.GetTitleId(), content, map);
|
||||||
return !path.empty() &&
|
if (path.empty())
|
||||||
m_ios.GetFS()->GetMetadata(PID_KERNEL, PID_KERNEL, path).Succeeded();
|
return false;
|
||||||
|
|
||||||
|
// Check whether the content file exists.
|
||||||
|
const auto file = fs->OpenFile(PID_KERNEL, PID_KERNEL, path, FS::Mode::Read);
|
||||||
|
if (!file.Succeeded())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If content hash checks are disabled, all we have to do is check for existence.
|
||||||
|
if (check_content_hashes == CheckContentHashes::No)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Otherwise, check whether the installed content SHA1 matches the expected hash.
|
||||||
|
std::vector<u8> content_data(file->GetStatus()->size);
|
||||||
|
if (!file->Read(content_data.data(), content_data.size()))
|
||||||
|
return false;
|
||||||
|
std::array<u8, 20> sha1{};
|
||||||
|
mbedtls_sha1_ret(content_data.data(), content_data.size(), sha1.data());
|
||||||
|
return sha1 == content.sha1;
|
||||||
});
|
});
|
||||||
|
|
||||||
return stored_contents;
|
return stored_contents;
|
||||||
|
|
|
@ -205,6 +205,7 @@ public:
|
||||||
/// Reposition the file offset for a file descriptor.
|
/// Reposition the file offset for a file descriptor.
|
||||||
virtual Result<u32> SeekFile(Fd fd, u32 offset, SeekMode mode) = 0;
|
virtual Result<u32> SeekFile(Fd fd, u32 offset, SeekMode mode) = 0;
|
||||||
/// Get status for a file descriptor.
|
/// Get status for a file descriptor.
|
||||||
|
/// Guaranteed to succeed for a valid file descriptor.
|
||||||
virtual Result<FileStatus> GetFileStatus(Fd fd) = 0;
|
virtual Result<FileStatus> GetFileStatus(Fd fd) = 0;
|
||||||
|
|
||||||
/// Create a file with the specified path and metadata.
|
/// Create a file with the specified path and metadata.
|
||||||
|
|
|
@ -149,7 +149,8 @@ bool InstallWAD(IOS::HLE::Kernel& ios, const DiscIO::VolumeWAD& wad, InstallType
|
||||||
const u64 title_id = wad.GetTMD().GetTitleId();
|
const u64 title_id = wad.GetTMD().GetTitleId();
|
||||||
|
|
||||||
// Skip the install if the WAD is already installed.
|
// Skip the install if the WAD is already installed.
|
||||||
const auto installed_contents = ios.GetES()->GetStoredContentsFromTMD(wad.GetTMD());
|
const auto installed_contents = ios.GetES()->GetStoredContentsFromTMD(
|
||||||
|
wad.GetTMD(), IOS::HLE::Device::ES::CheckContentHashes::Yes);
|
||||||
if (wad.GetTMD().GetContents() == installed_contents)
|
if (wad.GetTMD().GetContents() == installed_contents)
|
||||||
{
|
{
|
||||||
// Clear the "temporary title ID" flag in case the user tries to permanently install a title
|
// Clear the "temporary title ID" flag in case the user tries to permanently install a title
|
||||||
|
|
Loading…
Reference in New Issue