IOS/ES: Implement ES_DeleteSharedContent

This commit is contained in:
Léo Lam 2017-05-20 12:41:04 +02:00
parent a0e4bb4aa6
commit 2e8e420623
5 changed files with 75 additions and 6 deletions

View File

@ -471,6 +471,8 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
return DeleteTicket(request);
case IOCTL_ES_DELETETITLECONTENT:
return DeleteTitleContent(request);
case IOCTL_ES_DELETESHAREDCONTENT:
return DeleteSharedContent(request);
case IOCTL_ES_GETSTOREDTMDSIZE:
return GetStoredTMDSize(request);
case IOCTL_ES_GETSTOREDTMD:
@ -503,7 +505,6 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
return GetBoot2Version(request);
case IOCTL_ES_VERIFYSIGN:
case IOCTL_ES_DELETESHAREDCONTENT:
case IOCTL_ES_UNKNOWN_3B:
case IOCTL_ES_UNKNOWN_3C:
case IOCTL_ES_UNKNOWN_3D:

View File

@ -114,6 +114,7 @@ public:
ReturnCode DeleteTitle(u64 title_id);
ReturnCode DeleteTitleContent(u64 title_id) const;
ReturnCode DeleteTicket(const u8* ticket_view);
ReturnCode DeleteSharedContent(const std::array<u8, 20>& sha1) const;
private:
enum
@ -209,6 +210,7 @@ private:
IPCCommandResult DeleteTitle(const IOCtlVRequest& request);
IPCCommandResult DeleteTitleContent(const IOCtlVRequest& request);
IPCCommandResult DeleteTicket(const IOCtlVRequest& request);
IPCCommandResult DeleteSharedContent(const IOCtlVRequest& request);
// Device identity and encryption
IPCCommandResult GetConsoleID(const IOCtlVRequest& request);

View File

@ -423,16 +423,34 @@ std::string SharedContentMap::AddSharedContent(const std::array<u8, 20>& sha1)
entry.sha1 = sha1;
m_entries.push_back(entry);
File::CreateFullPath(m_file_path);
File::IOFile file(m_file_path, "ab");
file.WriteArray(&entry, 1);
WriteEntries();
filename = Common::RootUserPath(m_root) + StringFromFormat("/shared1/%s.app", id.c_str());
m_last_id++;
return filename;
}
bool SharedContentMap::DeleteSharedContent(const std::array<u8, 20>& sha1)
{
m_entries.erase(std::remove_if(m_entries.begin(), m_entries.end(),
[&sha1](const auto& entry) { return entry.sha1 == sha1; }),
m_entries.end());
return WriteEntries();
}
bool SharedContentMap::WriteEntries() const
{
// Temporary files in ES are only 12 characters long (excluding /tmp/).
const std::string temp_path = Common::RootUserPath(m_root) + "/tmp/shared1/cont";
File::CreateFullPath(temp_path);
// Atomically write the new content map.
File::IOFile file(temp_path, "w+b");
if (!file.WriteArray(m_entries.data(), m_entries.size()))
return false;
File::CreateFullPath(m_file_path);
return File::RenameSync(temp_path, m_file_path);
}
static std::pair<u32, u64> ReadUidSysEntry(File::IOFile& file)
{
u64 title_id = 0;

View File

@ -212,9 +212,12 @@ public:
std::string GetFilenameFromSHA1(const std::array<u8, 20>& sha1) const;
std::string AddSharedContent(const std::array<u8, 20>& sha1);
bool DeleteSharedContent(const std::array<u8, 20>& sha1);
std::vector<std::array<u8, 20>> GetHashes() const;
private:
bool WriteEntries() const;
struct Entry;
Common::FromWhichRoot m_root;
u32 m_last_id = 0;

View File

@ -622,6 +622,51 @@ IPCCommandResult ES::ExportTitleDone(Context& context, const IOCtlVRequest& requ
{
return GetDefaultReply(ExportTitleDone(context));
}
ReturnCode ES::DeleteSharedContent(const std::array<u8, 20>& sha1) const
{
IOS::ES::SharedContentMap map{Common::FromWhichRoot::FROM_SESSION_ROOT};
const std::string content_path = map.GetFilenameFromSHA1(sha1);
if (content_path == "unk")
return ES_EINVAL;
// Check whether the shared content is used by a system title.
const std::vector<u64> titles = IOS::ES::GetInstalledTitles();
const bool is_used_by_system_title = std::any_of(titles.begin(), titles.end(), [&sha1](u64 id) {
if (!IOS::ES::IsTitleType(id, IOS::ES::TitleType::System))
return false;
const auto tmd = IOS::ES::FindInstalledTMD(id);
if (!tmd.IsValid())
return true;
const auto contents = tmd.GetContents();
return std::any_of(contents.begin(), contents.end(),
[&sha1](const auto& content) { return content.sha1 == sha1; });
});
// Any shared content used by a system title cannot be deleted.
if (is_used_by_system_title)
return ES_EINVAL;
// Delete the shared content and update the content map.
if (!File::Delete(content_path))
return FS_ENOENT;
if (!map.DeleteSharedContent(sha1))
return ES_EIO;
return IPC_SUCCESS;
}
IPCCommandResult ES::DeleteSharedContent(const IOCtlVRequest& request)
{
std::array<u8, 20> sha1;
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sha1.size())
return GetDefaultReply(ES_EINVAL);
Memory::CopyFromEmu(sha1.data(), request.in_vectors[0].address, request.in_vectors[0].size);
return GetDefaultReply(DeleteSharedContent(sha1));
}
} // namespace Device
} // namespace HLE
} // namespace IOS