ES: Use new filesystem interface in TitleManagement

This commit is contained in:
Léo Lam 2018-05-06 19:05:57 +02:00
parent 9ee2654be6
commit 923b450268
1 changed files with 54 additions and 61 deletions

View File

@ -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. // Delete the ticket file if it is now empty.
if (new_ticket.empty()) fs->Delete(PID_KERNEL, PID_KERNEL, ticket_path);
File::Delete(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);