IOS/ES: Make sure all contents are imported

This adds a check to ImportTitleDone to make sure all required contents
that are listed in the TMD have been imported before allowing to finish
the import. Not checking for this could allow titles to be left in an
inconsistent state.
This commit is contained in:
Léo Lam 2017-06-20 12:15:48 +02:00
parent 4d08e90f26
commit 3613f33c9b
3 changed files with 25 additions and 1 deletions

View file

@ -61,6 +61,11 @@ bool Content::IsShared() const
return (type & 0x8000) != 0; return (type & 0x8000) != 0;
} }
bool Content::IsOptional() const
{
return (type & 0x4000) != 0;
}
SignedBlobReader::SignedBlobReader(const std::vector<u8>& bytes) : m_bytes(bytes) SignedBlobReader::SignedBlobReader(const std::vector<u8>& bytes) : m_bytes(bytes)
{ {
} }

View file

@ -83,6 +83,7 @@ static_assert(sizeof(TMDHeader) == 0x1e4, "TMDHeader has the wrong size");
struct Content struct Content
{ {
bool IsShared() const; bool IsShared() const;
bool IsOptional() const;
u32 id; u32 id;
u16 index; u16 index;
u16 type; u16 type;

View file

@ -353,13 +353,31 @@ ReturnCode ES::ImportTitleDone(Context& context)
if (!context.title_import.tmd.IsValid() || context.title_import.content_id != 0xFFFFFFFF) if (!context.title_import.tmd.IsValid() || context.title_import.content_id != 0xFFFFFFFF)
return ES_EINVAL; return ES_EINVAL;
// Make sure all listed, non-optional contents have been imported.
const u64 title_id = context.title_import.tmd.GetTitleId();
const std::vector<IOS::ES::Content> contents = context.title_import.tmd.GetContents();
const IOS::ES::SharedContentMap shared_content_map{Common::FROM_SESSION_ROOT};
const bool has_all_required_contents =
std::all_of(contents.cbegin(), contents.cend(), [&](const IOS::ES::Content& content) {
if (content.IsOptional())
return true;
if (content.IsShared())
return shared_content_map.GetFilenameFromSHA1(content.sha1).has_value();
return File::Exists(Common::GetTitleContentPath(title_id, Common::FROM_SESSION_ROOT) +
StringFromFormat("%08x.app", content.id));
});
if (!has_all_required_contents)
return ES_EINVAL;
if (!WriteImportTMD(context.title_import.tmd)) if (!WriteImportTMD(context.title_import.tmd))
return ES_EIO; return ES_EIO;
if (!FinishImport(context.title_import.tmd)) if (!FinishImport(context.title_import.tmd))
return ES_EIO; return ES_EIO;
INFO_LOG(IOS_ES, "ImportTitleDone: title %016" PRIx64, context.title_import.tmd.GetTitleId()); INFO_LOG(IOS_ES, "ImportTitleDone: title %016" PRIx64, title_id);
context.title_import.tmd.SetBytes({}); context.title_import.tmd.SetBytes({});
return IPC_SUCCESS; return IPC_SUCCESS;
} }