ES: Use new filesystem interface in TitleManagement
This commit is contained in:
parent
9ee2654be6
commit
923b450268
|
@ -13,8 +13,6 @@
|
||||||
#include <mbedtls/sha1.h>
|
#include <mbedtls/sha1.h>
|
||||||
|
|
||||||
#include "Common/Align.h"
|
#include "Common/Align.h"
|
||||||
#include "Common/File.h"
|
|
||||||
#include "Common/FileUtil.h"
|
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
#include "Common/NandPaths.h"
|
#include "Common/NandPaths.h"
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
|
@ -29,19 +27,22 @@ namespace HLE
|
||||||
{
|
{
|
||||||
namespace Device
|
namespace Device
|
||||||
{
|
{
|
||||||
static ReturnCode WriteTicket(const IOS::ES::TicketReader& ticket)
|
static ReturnCode WriteTicket(FS::FileSystem* fs, const IOS::ES::TicketReader& ticket)
|
||||||
{
|
{
|
||||||
const u64 title_id = ticket.GetTitleId();
|
const u64 title_id = ticket.GetTitleId();
|
||||||
|
|
||||||
const std::string ticket_path = Common::GetTicketFileName(title_id, Common::FROM_SESSION_ROOT);
|
const std::string path = Common::GetTicketFileName(title_id);
|
||||||
File::CreateFullPath(ticket_path);
|
fs->CreateFullPath(PID_KERNEL, PID_KERNEL, path, 0, FS::Mode::ReadWrite, FS::Mode::ReadWrite,
|
||||||
|
FS::Mode::None);
|
||||||
|
fs->CreateFile(PID_KERNEL, PID_KERNEL, path, 0, FS::Mode::ReadWrite, FS::Mode::ReadWrite,
|
||||||
|
FS::Mode::None);
|
||||||
|
|
||||||
File::IOFile ticket_file(ticket_path, "wb");
|
const auto file = fs->OpenFile(PID_KERNEL, PID_KERNEL, path, FS::Mode::Write);
|
||||||
if (!ticket_file)
|
if (!file)
|
||||||
return ES_EIO;
|
return FS::ConvertResult(file.Error());
|
||||||
|
|
||||||
const std::vector<u8>& raw_ticket = ticket.GetBytes();
|
const std::vector<u8>& raw_ticket = ticket.GetBytes();
|
||||||
return ticket_file.WriteBytes(raw_ticket.data(), raw_ticket.size()) ? IPC_SUCCESS : ES_EIO;
|
return file->Write(raw_ticket.data(), raw_ticket.size()) ? IPC_SUCCESS : ES_EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ES::TitleImportExportContext::DoState(PointerWrap& p)
|
void ES::TitleImportExportContext::DoState(PointerWrap& p)
|
||||||
|
@ -85,7 +86,7 @@ ReturnCode ES::ImportTicket(const std::vector<u8>& ticket_bytes, const std::vect
|
||||||
if (verify_ret != IPC_SUCCESS)
|
if (verify_ret != IPC_SUCCESS)
|
||||||
return verify_ret;
|
return verify_ret;
|
||||||
|
|
||||||
const ReturnCode write_ret = WriteTicket(ticket);
|
const ReturnCode write_ret = WriteTicket(m_ios.GetFS().get(), ticket);
|
||||||
if (write_ret != IPC_SUCCESS)
|
if (write_ret != IPC_SUCCESS)
|
||||||
return write_ret;
|
return write_ret;
|
||||||
|
|
||||||
|
@ -343,8 +344,7 @@ static bool CheckIfContentHashMatches(const std::vector<u8>& content, const IOS:
|
||||||
|
|
||||||
static std::string GetImportContentPath(u64 title_id, u32 content_id)
|
static std::string GetImportContentPath(u64 title_id, u32 content_id)
|
||||||
{
|
{
|
||||||
return Common::GetImportTitlePath(title_id, Common::FROM_SESSION_ROOT) +
|
return Common::GetImportTitlePath(title_id) + StringFromFormat("/content/%08x.app", content_id);
|
||||||
StringFromFormat("/content/%08x.app", content_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnCode ES::ImportContentEnd(Context& context, u32 content_fd)
|
ReturnCode ES::ImportContentEnd(Context& context, u32 content_fd)
|
||||||
|
@ -371,38 +371,39 @@ ReturnCode ES::ImportContentEnd(Context& context, u32 content_fd)
|
||||||
return ES_HASH_MISMATCH;
|
return ES_HASH_MISMATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto fs = m_ios.GetFS();
|
||||||
std::string content_path;
|
std::string content_path;
|
||||||
if (content_info.IsShared())
|
if (content_info.IsShared())
|
||||||
{
|
{
|
||||||
IOS::ES::SharedContentMap shared_content{m_ios.GetFS()};
|
IOS::ES::SharedContentMap shared_content{fs};
|
||||||
content_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) +
|
content_path = shared_content.AddSharedContent(content_info.sha1);
|
||||||
shared_content.AddSharedContent(content_info.sha1);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
content_path = GetImportContentPath(context.title_import_export.tmd.GetTitleId(),
|
content_path = GetImportContentPath(context.title_import_export.tmd.GetTitleId(),
|
||||||
context.title_import_export.content.id);
|
context.title_import_export.content.id);
|
||||||
}
|
}
|
||||||
File::CreateFullPath(content_path);
|
|
||||||
|
|
||||||
const std::string temp_path =
|
const std::string temp_path =
|
||||||
Common::RootUserPath(Common::FROM_SESSION_ROOT) +
|
"/tmp/" + content_path.substr(content_path.find_last_of('/') + 1, std::string::npos);
|
||||||
StringFromFormat("/tmp/%08x.app", context.title_import_export.content.id);
|
|
||||||
File::CreateFullPath(temp_path);
|
|
||||||
|
|
||||||
|
fs->CreateFile(PID_KERNEL, PID_KERNEL, temp_path, 0, FS::Mode::ReadWrite, FS::Mode::ReadWrite,
|
||||||
|
FS::Mode::None);
|
||||||
{
|
{
|
||||||
File::IOFile file(temp_path, "wb");
|
const auto file = fs->OpenFile(PID_KERNEL, PID_KERNEL, temp_path, FS::Mode::Write);
|
||||||
if (!file.WriteBytes(decrypted_data.data(), content_info.size))
|
if (!file || !file->Write(decrypted_data.data(), content_info.size))
|
||||||
{
|
{
|
||||||
ERROR_LOG(IOS_ES, "ImportContentEnd: Failed to write to %s", temp_path.c_str());
|
ERROR_LOG(IOS_ES, "ImportContentEnd: Failed to write to %s", temp_path.c_str());
|
||||||
return ES_EIO;
|
return ES_EIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!File::Rename(temp_path, content_path))
|
const FS::ResultCode rename_result = fs->Rename(PID_KERNEL, PID_KERNEL, temp_path, content_path);
|
||||||
|
if (rename_result != FS::ResultCode::Success)
|
||||||
{
|
{
|
||||||
|
fs->Delete(PID_KERNEL, PID_KERNEL, temp_path);
|
||||||
ERROR_LOG(IOS_ES, "ImportContentEnd: Failed to move content to %s", content_path.c_str());
|
ERROR_LOG(IOS_ES, "ImportContentEnd: Failed to move content to %s", content_path.c_str());
|
||||||
return ES_EIO;
|
return FS::ConvertResult(rename_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.title_import_export.content = {};
|
context.title_import_export.content = {};
|
||||||
|
@ -437,8 +438,9 @@ ReturnCode ES::ImportTitleDone(Context& context)
|
||||||
|
|
||||||
// Note: the import hasn't been finalised yet, so the whole title directory
|
// Note: the import hasn't been finalised yet, so the whole title directory
|
||||||
// is still in /import, not /title.
|
// is still in /import, not /title.
|
||||||
return File::Exists(Common::GetImportTitlePath(title_id, Common::FROM_SESSION_ROOT) +
|
const std::string path = Common::GetImportTitlePath(title_id) +
|
||||||
StringFromFormat("/content/%08x.app", content.id));
|
StringFromFormat("/content/%08x.app", content.id);
|
||||||
|
return m_ios.GetFS()->GetMetadata(PID_KERNEL, PID_KERNEL, path).Succeeded();
|
||||||
});
|
});
|
||||||
if (!has_all_required_contents)
|
if (!has_all_required_contents)
|
||||||
return ES_EINVAL;
|
return ES_EINVAL;
|
||||||
|
@ -499,17 +501,8 @@ ReturnCode ES::DeleteTitle(u64 title_id)
|
||||||
if (!CanDeleteTitle(title_id))
|
if (!CanDeleteTitle(title_id))
|
||||||
return ES_EINVAL;
|
return ES_EINVAL;
|
||||||
|
|
||||||
const std::string title_dir = Common::GetTitlePath(title_id, Common::FROM_SESSION_ROOT);
|
const std::string title_dir = Common::GetTitlePath(title_id);
|
||||||
if (!File::IsDirectory(title_dir))
|
return FS::ConvertResult(m_ios.GetFS()->Delete(PID_KERNEL, PID_KERNEL, title_dir));
|
||||||
return FS_ENOENT;
|
|
||||||
|
|
||||||
if (!File::DeleteDirRecursively(title_dir))
|
|
||||||
{
|
|
||||||
ERROR_LOG(IOS_ES, "DeleteTitle: Failed to delete title directory: %s", title_dir.c_str());
|
|
||||||
return FS_EACCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
return IPC_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::DeleteTitle(const IOCtlVRequest& request)
|
IPCCommandResult ES::DeleteTitle(const IOCtlVRequest& request)
|
||||||
|
@ -523,6 +516,7 @@ IPCCommandResult ES::DeleteTitle(const IOCtlVRequest& request)
|
||||||
|
|
||||||
ReturnCode ES::DeleteTicket(const u8* ticket_view)
|
ReturnCode ES::DeleteTicket(const u8* ticket_view)
|
||||||
{
|
{
|
||||||
|
const auto fs = m_ios.GetFS();
|
||||||
const u64 title_id = Common::swap64(ticket_view + offsetof(IOS::ES::TicketView, title_id));
|
const u64 title_id = Common::swap64(ticket_view + offsetof(IOS::ES::TicketView, title_id));
|
||||||
|
|
||||||
if (!CanDeleteTitle(title_id))
|
if (!CanDeleteTitle(title_id))
|
||||||
|
@ -536,24 +530,26 @@ ReturnCode ES::DeleteTicket(const u8* ticket_view)
|
||||||
ticket.DeleteTicket(ticket_id);
|
ticket.DeleteTicket(ticket_id);
|
||||||
|
|
||||||
const std::vector<u8>& new_ticket = ticket.GetBytes();
|
const std::vector<u8>& new_ticket = ticket.GetBytes();
|
||||||
const std::string ticket_path = Common::GetTicketFileName(title_id, Common::FROM_SESSION_ROOT);
|
const std::string ticket_path = Common::GetTicketFileName(title_id);
|
||||||
|
if (!new_ticket.empty())
|
||||||
{
|
{
|
||||||
File::IOFile ticket_file(ticket_path, "wb");
|
const auto file = fs->OpenFile(PID_KERNEL, PID_KERNEL, ticket_path, FS::Mode::ReadWrite);
|
||||||
if (!ticket_file || !ticket_file.WriteBytes(new_ticket.data(), new_ticket.size()))
|
if (!file || !file->Write(new_ticket.data(), new_ticket.size()))
|
||||||
return ES_EIO;
|
return ES_EIO;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// Delete the ticket file if it is now empty.
|
{
|
||||||
if (new_ticket.empty())
|
// Delete the ticket file if it is now empty.
|
||||||
File::Delete(ticket_path);
|
fs->Delete(PID_KERNEL, PID_KERNEL, ticket_path);
|
||||||
|
}
|
||||||
|
|
||||||
// Delete the ticket directory if it is now empty.
|
// Delete the ticket directory if it is now empty.
|
||||||
const std::string ticket_parent_dir =
|
const std::string ticket_parent_dir =
|
||||||
Common::RootUserPath(Common::FROM_CONFIGURED_ROOT) +
|
|
||||||
StringFromFormat("/ticket/%08x", static_cast<u32>(title_id >> 32));
|
StringFromFormat("/ticket/%08x", static_cast<u32>(title_id >> 32));
|
||||||
const auto ticket_parent_dir_entries = File::ScanDirectoryTree(ticket_parent_dir, false);
|
const auto ticket_parent_dir_entries =
|
||||||
if (ticket_parent_dir_entries.children.empty())
|
fs->ReadDirectory(PID_KERNEL, PID_KERNEL, ticket_parent_dir);
|
||||||
File::DeleteDir(ticket_parent_dir);
|
if (ticket_parent_dir_entries && ticket_parent_dir_entries->empty())
|
||||||
|
fs->Delete(PID_KERNEL, PID_KERNEL, ticket_parent_dir);
|
||||||
|
|
||||||
return IPC_SUCCESS;
|
return IPC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -573,14 +569,15 @@ ReturnCode ES::DeleteTitleContent(u64 title_id) const
|
||||||
if (!CanDeleteTitle(title_id))
|
if (!CanDeleteTitle(title_id))
|
||||||
return ES_EINVAL;
|
return ES_EINVAL;
|
||||||
|
|
||||||
const std::string content_dir = Common::GetTitleContentPath(title_id, Common::FROM_SESSION_ROOT);
|
const std::string content_dir = Common::GetTitleContentPath(title_id);
|
||||||
if (!File::IsDirectory(content_dir))
|
const auto files = m_ios.GetFS()->ReadDirectory(PID_KERNEL, PID_KERNEL, content_dir);
|
||||||
return FS_ENOENT;
|
if (!files)
|
||||||
|
return FS::ConvertResult(files.Error());
|
||||||
|
|
||||||
for (const auto& file : File::ScanDirectoryTree(content_dir, false).children)
|
for (const std::string& file_name : *files)
|
||||||
{
|
{
|
||||||
if (file.virtualName.size() == 12 && file.virtualName.compare(8, 4, ".app") == 0)
|
if (file_name.size() == 12 && file_name.compare(8, 4, ".app") == 0)
|
||||||
File::Delete(file.physicalName);
|
m_ios.GetFS()->Delete(PID_KERNEL, PID_KERNEL, content_dir + '/' + file_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return IPC_SUCCESS;
|
return IPC_SUCCESS;
|
||||||
|
@ -606,13 +603,9 @@ ReturnCode ES::DeleteContent(u64 title_id, u32 content_id) const
|
||||||
if (!tmd.FindContentById(content_id, &content))
|
if (!tmd.FindContentById(content_id, &content))
|
||||||
return ES_EINVAL;
|
return ES_EINVAL;
|
||||||
|
|
||||||
if (!File::Delete(Common::GetTitleContentPath(title_id, Common::FROM_SESSION_ROOT) +
|
return FS::ConvertResult(m_ios.GetFS()->Delete(PID_KERNEL, PID_KERNEL,
|
||||||
StringFromFormat("/%08x.app", content_id)))
|
Common::GetTitleContentPath(title_id) +
|
||||||
{
|
StringFromFormat("/%08x.app", content_id)));
|
||||||
return FS_ENOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return IPC_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::DeleteContent(const IOCtlVRequest& request)
|
IPCCommandResult ES::DeleteContent(const IOCtlVRequest& request)
|
||||||
|
@ -812,7 +805,7 @@ ReturnCode ES::DeleteSharedContent(const std::array<u8, 20>& sha1) const
|
||||||
return ES_EINVAL;
|
return ES_EINVAL;
|
||||||
|
|
||||||
// Delete the shared content and update the content map.
|
// Delete the shared content and update the content map.
|
||||||
const auto delete_result = m_ios.GetFS()->Delete(0, 0, *content_path);
|
const auto delete_result = m_ios.GetFS()->Delete(PID_KERNEL, PID_KERNEL, *content_path);
|
||||||
if (delete_result != FS::ResultCode::Success)
|
if (delete_result != FS::ResultCode::Success)
|
||||||
return FS::ConvertResult(delete_result);
|
return FS::ConvertResult(delete_result);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue