From 9ee2654be68abd5412a0c77dede78c4746fdff26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 6 May 2018 18:49:43 +0200 Subject: [PATCH] ES: Use new filesystem interface in NandUtils --- Source/Core/Core/IOS/ES/ES.h | 2 +- Source/Core/Core/IOS/ES/NandUtils.cpp | 225 ++++++++++++-------- Source/Core/Core/IOS/ES/TitleManagement.cpp | 4 +- Source/Core/Core/IOS/IOS.cpp | 29 ++- 4 files changed, 155 insertions(+), 105 deletions(-) diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h index 852f42a0fe..38492d39da 100644 --- a/Source/Core/Core/IOS/ES/ES.h +++ b/Source/Core/Core/IOS/ES/ES.h @@ -326,7 +326,7 @@ private: const std::vector& cert_chain, u32 iosc_handle = 0); // Start a title import. - bool InitImport(u64 title_id); + bool InitImport(const IOS::ES::TMDReader& tmd); // Clean up the import content directory and move it back to /title. bool FinishImport(const IOS::ES::TMDReader& tmd); // Write a TMD for a title in /import atomically. diff --git a/Source/Core/Core/IOS/ES/NandUtils.cpp b/Source/Core/Core/IOS/ES/NandUtils.cpp index dd6bd2cac1..738ddeaafd 100644 --- a/Source/Core/Core/IOS/ES/NandUtils.cpp +++ b/Source/Core/Core/IOS/ES/NandUtils.cpp @@ -13,8 +13,6 @@ #include #include "Common/CommonTypes.h" -#include "Common/File.h" -#include "Common/FileUtil.h" #include "Common/Logging/Log.h" #include "Common/NandPaths.h" #include "Common/StringUtil.h" @@ -27,14 +25,14 @@ namespace HLE { namespace Device { -static IOS::ES::TMDReader FindTMD(u64 title_id, const std::string& tmd_path) +static IOS::ES::TMDReader FindTMD(FS::FileSystem* fs, u64 title_id, const std::string& tmd_path) { - File::IOFile file(tmd_path, "rb"); + const auto file = fs->OpenFile(PID_KERNEL, PID_KERNEL, tmd_path, FS::Mode::Read); if (!file) return {}; - std::vector tmd_bytes(file.GetSize()); - if (!file.ReadBytes(tmd_bytes.data(), tmd_bytes.size())) + std::vector tmd_bytes(file->GetStatus()->size); + if (!file->Read(tmd_bytes.data(), tmd_bytes.size())) return {}; return IOS::ES::TMDReader{std::move(tmd_bytes)}; @@ -42,24 +40,24 @@ static IOS::ES::TMDReader FindTMD(u64 title_id, const std::string& tmd_path) IOS::ES::TMDReader ES::FindImportTMD(u64 title_id) const { - return FindTMD(title_id, Common::GetImportTitlePath(title_id, Common::FROM_SESSION_ROOT) + - "/content/title.tmd"); + return FindTMD(m_ios.GetFS().get(), title_id, + Common::GetImportTitlePath(title_id) + "/content/title.tmd"); } IOS::ES::TMDReader ES::FindInstalledTMD(u64 title_id) const { - return FindTMD(title_id, Common::GetTMDFileName(title_id, Common::FROM_SESSION_ROOT)); + return FindTMD(m_ios.GetFS().get(), title_id, Common::GetTMDFileName(title_id)); } IOS::ES::TicketReader ES::FindSignedTicket(u64 title_id) const { - const std::string path = Common::GetTicketFileName(title_id, Common::FROM_SESSION_ROOT); - File::IOFile ticket_file(path, "rb"); + const std::string path = Common::GetTicketFileName(title_id); + const auto ticket_file = m_ios.GetFS()->OpenFile(PID_KERNEL, PID_KERNEL, path, FS::Mode::Read); if (!ticket_file) return {}; - std::vector signed_ticket(ticket_file.GetSize()); - if (!ticket_file.ReadBytes(signed_ticket.data(), signed_ticket.size())) + std::vector signed_ticket(ticket_file->GetStatus()->size); + if (!ticket_file->Read(signed_ticket.data(), signed_ticket.size())) return {}; return IOS::ES::TicketReader{std::move(signed_ticket)}; @@ -73,9 +71,10 @@ static bool IsValidPartOfTitleID(const std::string& string) [](const auto character) { return std::isxdigit(character) != 0; }); } -static std::vector GetTitlesInTitleOrImport(const std::string& titles_dir) +static std::vector GetTitlesInTitleOrImport(FS::FileSystem* fs, const std::string& titles_dir) { - if (!File::IsDirectory(titles_dir)) + const auto entries = fs->ReadDirectory(PID_KERNEL, PID_KERNEL, titles_dir); + if (!entries) { ERROR_LOG(IOS_ES, "%s is not a directory", titles_dir.c_str()); return {}; @@ -85,22 +84,26 @@ static std::vector GetTitlesInTitleOrImport(const std::string& titles_dir) // The /title and /import directories contain one directory per title type, and each of them has // a directory per title (where the name is the low 32 bits of the title ID in %08x format). - const auto entries = File::ScanDirectoryTree(titles_dir, true); - for (const File::FSTEntry& title_type : entries.children) + for (const std::string& title_type : *entries) { - if (!title_type.isDirectory || !IsValidPartOfTitleID(title_type.virtualName)) + if (!IsValidPartOfTitleID(title_type)) continue; - if (title_type.children.empty()) + const auto title_entries = + fs->ReadDirectory(PID_KERNEL, PID_KERNEL, titles_dir + '/' + title_type); + if (!title_entries) continue; - for (const File::FSTEntry& title_identifier : title_type.children) + for (const std::string& title_identifier : *title_entries) { - if (!title_identifier.isDirectory || !IsValidPartOfTitleID(title_identifier.virtualName)) + if (!IsValidPartOfTitleID(title_identifier)) + continue; + if (!fs->ReadDirectory(PID_KERNEL, PID_KERNEL, + titles_dir + '/' + title_type + '/' + title_identifier)) continue; - const u32 type = std::stoul(title_type.virtualName, nullptr, 16); - const u32 identifier = std::stoul(title_identifier.virtualName, nullptr, 16); + const u32 type = std::stoul(title_type, nullptr, 16); + const u32 identifier = std::stoul(title_identifier, nullptr, 16); title_ids.push_back(static_cast(type) << 32 | identifier); } } @@ -116,18 +119,19 @@ static std::vector GetTitlesInTitleOrImport(const std::string& titles_dir) std::vector ES::GetInstalledTitles() const { - return GetTitlesInTitleOrImport(Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/title"); + return GetTitlesInTitleOrImport(m_ios.GetFS().get(), "/title"); } std::vector ES::GetTitleImports() const { - return GetTitlesInTitleOrImport(Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/import"); + return GetTitlesInTitleOrImport(m_ios.GetFS().get(), "/import"); } std::vector ES::GetTitlesWithTickets() const { - const std::string tickets_dir = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/ticket"; - if (!File::IsDirectory(tickets_dir)) + const auto fs = m_ios.GetFS(); + const auto entries = fs->ReadDirectory(PID_KERNEL, PID_KERNEL, "/ticket"); + if (!entries) { ERROR_LOG(IOS_ES, "/ticket is not a directory"); return {}; @@ -137,25 +141,25 @@ std::vector ES::GetTitlesWithTickets() const // The /ticket directory contains one directory per title type, and each of them contains // one ticket per title (where the name is the low 32 bits of the title ID in %08x format). - const auto entries = File::ScanDirectoryTree(tickets_dir, true); - for (const File::FSTEntry& title_type : entries.children) + for (const std::string& title_type : *entries) { - if (!title_type.isDirectory || !IsValidPartOfTitleID(title_type.virtualName)) + if (!IsValidPartOfTitleID(title_type)) continue; - if (title_type.children.empty()) + const auto sub_entries = fs->ReadDirectory(PID_KERNEL, PID_KERNEL, "/ticket/" + title_type); + if (!sub_entries) continue; - for (const File::FSTEntry& ticket : title_type.children) + for (const std::string& file_name : *sub_entries) { - const std::string name_without_ext = ticket.virtualName.substr(0, 8); - if (ticket.isDirectory || !IsValidPartOfTitleID(name_without_ext) || - name_without_ext + ".tik" != ticket.virtualName) + const std::string name_without_ext = file_name.substr(0, 8); + if (fs->ReadDirectory(PID_KERNEL, PID_KERNEL, "/ticket/" + title_type + '/' + file_name) || + !IsValidPartOfTitleID(name_without_ext) || name_without_ext + ".tik" != file_name) { continue; } - const u32 type = std::stoul(title_type.virtualName, nullptr, 16); + const u32 type = std::stoul(title_type, nullptr, 16); const u32 identifier = std::stoul(name_without_ext, nullptr, 16); title_ids.push_back(static_cast(type) << 32 | identifier); } @@ -177,7 +181,8 @@ std::vector ES::GetStoredContentsFromTMD(const IOS::ES::TMDRea std::copy_if(contents.begin(), contents.end(), std::back_inserter(stored_contents), [this, &tmd, &map](const IOS::ES::Content& content) { const std::string path = GetContentPath(tmd.GetTitleId(), content, map); - return !path.empty() && m_ios.GetFS()->GetMetadata(0, 0, path).Succeeded(); + return !path.empty() && + m_ios.GetFS()->GetMetadata(PID_KERNEL, PID_KERNEL, path).Succeeded(); }); return stored_contents; @@ -185,12 +190,11 @@ std::vector ES::GetStoredContentsFromTMD(const IOS::ES::TMDRea u32 ES::GetSharedContentsCount() const { - const std::string shared1_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/shared1"; - const auto entries = File::ScanDirectoryTree(shared1_path, false); + const auto entries = m_ios.GetFS()->ReadDirectory(PID_KERNEL, PID_KERNEL, "/shared1"); return static_cast( - std::count_if(entries.children.begin(), entries.children.end(), [](const auto& entry) { - return !entry.isDirectory && entry.virtualName.size() == 12 && - entry.virtualName.compare(8, 4, ".app") == 0; + std::count_if(entries->begin(), entries->end(), [this](const std::string& entry) { + return !m_ios.GetFS()->ReadDirectory(PID_KERNEL, PID_KERNEL, "/shared1/" + entry) && + entry.size() == 12 && entry.compare(8, 4, ".app") == 0; })); } @@ -200,65 +204,102 @@ std::vector> ES::GetSharedContents() const return map.GetHashes(); } -bool ES::InitImport(u64 title_id) +static bool DeleteDirectoriesIfEmpty(FS::FileSystem* fs, const std::string& path) { - const std::string content_dir = Common::GetTitleContentPath(title_id, Common::FROM_SESSION_ROOT); - const std::string data_dir = Common::GetTitleDataPath(title_id, Common::FROM_SESSION_ROOT); - for (const auto& dir : {content_dir, data_dir}) + std::string::size_type position = std::string::npos; + do { - if (!File::IsDirectory(dir) && !File::CreateFullPath(dir) && !File::CreateDir(dir)) + const auto directory = fs->ReadDirectory(PID_KERNEL, PID_KERNEL, path.substr(0, position)); + if ((directory && directory->empty()) || + (!directory && directory.Error() != FS::ResultCode::NotFound)) { - ERROR_LOG(IOS_ES, "InitImport: Failed to create title dirs for %016" PRIx64, title_id); - return false; + if (fs->Delete(PID_KERNEL, PID_KERNEL, path.substr(0, position)) != FS::ResultCode::Success) + return false; } + position = path.find_last_of('/', position - 1); + } while (position != 0); + return true; +} + +bool ES::InitImport(const IOS::ES::TMDReader& tmd) +{ + const auto fs = m_ios.GetFS(); + const std::string content_dir = Common::GetTitleContentPath(tmd.GetTitleId()); + const std::string import_content_dir = Common::GetImportTitlePath(tmd.GetTitleId()) + "/content"; + + const auto result1 = fs->CreateFullPath(PID_KERNEL, PID_KERNEL, content_dir + '/', 0, + FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::Read); + const auto result2 = fs->SetMetadata(PID_KERNEL, content_dir, PID_KERNEL, PID_KERNEL, 0, + FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::None); + const auto result3 = fs->CreateFullPath(PID_KERNEL, PID_KERNEL, import_content_dir + '/', 0, + FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::None); + if (result1 != FS::ResultCode::Success || result2 != FS::ResultCode::Success || + result3 != FS::ResultCode::Success) + { + ERROR_LOG(IOS_ES, "InitImport: Failed to create content dir for %016" PRIx64, tmd.GetTitleId()); + return false; } - IOS::ES::UIDSys uid_sys{m_ios.GetFS()}; - uid_sys.GetOrInsertUIDForTitle(title_id); + const std::string data_dir = Common::GetTitleDataPath(tmd.GetTitleId()); + const auto data_dir_contents = fs->ReadDirectory(PID_KERNEL, PID_KERNEL, data_dir); + if (!data_dir_contents && + (data_dir_contents.Error() != FS::ResultCode::NotFound || + fs->CreateDirectory(PID_KERNEL, PID_KERNEL, data_dir, 0, FS::Mode::ReadWrite, FS::Mode::None, + FS::Mode::None) != FS::ResultCode::Success)) + { + return false; + } + + IOS::ES::UIDSys uid_sys{fs}; + const u32 uid = uid_sys.GetOrInsertUIDForTitle(tmd.GetTitleId()); + if (fs->SetMetadata(0, data_dir, uid, tmd.GetGroupId(), 0, FS::Mode::ReadWrite, FS::Mode::None, + FS::Mode::None) != FS::ResultCode::Success) + { + return false; + } // IOS moves the title content directory to /import if the TMD exists during an import. - if (File::Exists(Common::GetTMDFileName(title_id, Common::FROM_SESSION_ROOT))) - { - const std::string import_content_dir = - Common::GetImportTitlePath(title_id, Common::FROM_SESSION_ROOT) + "/content"; - File::CreateFullPath(import_content_dir); - if (!File::Rename(content_dir, import_content_dir)) - { - ERROR_LOG(IOS_ES, "InitImport: Failed to move content dir for %016" PRIx64, title_id); - return false; - } - } + const auto file_info = + fs->GetMetadata(PID_KERNEL, PID_KERNEL, Common::GetTMDFileName(tmd.GetTitleId())); + if (!file_info || !file_info->is_file) + return true; + const auto result = fs->Rename(PID_KERNEL, PID_KERNEL, content_dir, import_content_dir); + if (result != FS::ResultCode::Success) + { + ERROR_LOG(IOS_ES, "InitImport: Failed to move content dir for %016" PRIx64, tmd.GetTitleId()); + return false; + } + DeleteDirectoriesIfEmpty(m_ios.GetFS().get(), import_content_dir); return true; } bool ES::FinishImport(const IOS::ES::TMDReader& tmd) { + const auto fs = m_ios.GetFS(); const u64 title_id = tmd.GetTitleId(); - const std::string import_content_dir = - Common::GetImportTitlePath(title_id, Common::FROM_SESSION_ROOT) + "/content"; + const std::string import_content_dir = Common::GetImportTitlePath(title_id) + "/content"; // Remove everything not listed in the TMD. std::unordered_set expected_entries = {"title.tmd"}; for (const auto& content_info : tmd.GetContents()) expected_entries.insert(StringFromFormat("%08x.app", content_info.id)); - const auto entries = File::ScanDirectoryTree(import_content_dir, false); - for (const File::FSTEntry& entry : entries.children) + const auto entries = fs->ReadDirectory(PID_KERNEL, PID_KERNEL, import_content_dir); + if (!entries) + return false; + for (const std::string& name : *entries) { + const std::string absolute_path = import_content_dir + '/' + name; // There should not be any directory in there. Remove it. - if (entry.isDirectory) - File::DeleteDirRecursively(entry.physicalName); - else if (expected_entries.find(entry.virtualName) == expected_entries.end()) - File::Delete(entry.physicalName); + if (fs->ReadDirectory(PID_KERNEL, PID_KERNEL, absolute_path)) + fs->Delete(PID_KERNEL, PID_KERNEL, absolute_path); + else if (expected_entries.find(name) == expected_entries.end()) + fs->Delete(PID_KERNEL, PID_KERNEL, absolute_path); } - const std::string content_dir = Common::GetTitleContentPath(title_id, Common::FROM_SESSION_ROOT); - if (File::IsDirectory(content_dir)) - { - WARN_LOG(IOS_ES, "FinishImport: %s already exists -- removing", content_dir.c_str()); - File::DeleteDirRecursively(content_dir); - } - if (!File::Rename(import_content_dir, content_dir)) + const std::string content_dir = Common::GetTitleContentPath(title_id); + if (fs->Rename(PID_KERNEL, PID_KERNEL, import_content_dir, content_dir) != + FS::ResultCode::Success) { ERROR_LOG(IOS_ES, "FinishImport: Failed to rename import directory to %s", content_dir.c_str()); return false; @@ -268,28 +309,34 @@ bool ES::FinishImport(const IOS::ES::TMDReader& tmd) bool ES::WriteImportTMD(const IOS::ES::TMDReader& tmd) { - const std::string tmd_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/tmp/title.tmd"; - File::CreateFullPath(tmd_path); - + const auto fs = m_ios.GetFS(); + const std::string tmd_path = "/tmp/title.tmd"; { - File::IOFile file(tmd_path, "wb"); - if (!file.WriteBytes(tmd.GetBytes().data(), tmd.GetBytes().size())) + fs->CreateFile(PID_KERNEL, PID_KERNEL, tmd_path, 0, FS::Mode::ReadWrite, FS::Mode::ReadWrite, + FS::Mode::None); + const auto file = fs->OpenFile(PID_KERNEL, PID_KERNEL, tmd_path, FS::Mode::Write); + if (!file || !file->Write(tmd.GetBytes().data(), tmd.GetBytes().size())) return false; } - const std::string dest = Common::GetImportTitlePath(tmd.GetTitleId(), Common::FROM_SESSION_ROOT) + - "/content/title.tmd"; - return File::Rename(tmd_path, dest); + const std::string dest = Common::GetImportTitlePath(tmd.GetTitleId()) + "/content/title.tmd"; + return fs->Rename(PID_KERNEL, PID_KERNEL, tmd_path, dest) == FS::ResultCode::Success; } void ES::FinishStaleImport(u64 title_id) { + const auto fs = m_ios.GetFS(); const auto import_tmd = FindImportTMD(title_id); if (!import_tmd.IsValid()) - File::DeleteDirRecursively(Common::GetImportTitlePath(title_id, Common::FROM_SESSION_ROOT) + - "/content"); + { + fs->Delete(PID_KERNEL, PID_KERNEL, Common::GetImportTitlePath(title_id) + "/content"); + DeleteDirectoriesIfEmpty(fs.get(), Common::GetImportTitlePath(title_id)); + DeleteDirectoriesIfEmpty(fs.get(), Common::GetTitlePath(title_id)); + } else + { FinishImport(import_tmd); + } } void ES::FinishAllStaleImports() @@ -297,10 +344,6 @@ void ES::FinishAllStaleImports() const std::vector titles = GetTitleImports(); for (const u64& title_id : titles) FinishStaleImport(title_id); - - const std::string import_dir = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/import"; - File::DeleteDirRecursively(import_dir); - File::CreateDir(import_dir); } std::string ES::GetContentPath(const u64 title_id, const IOS::ES::Content& content, diff --git a/Source/Core/Core/IOS/ES/TitleManagement.cpp b/Source/Core/Core/IOS/ES/TitleManagement.cpp index fa9fe2228b..8cbde024ac 100644 --- a/Source/Core/Core/IOS/ES/TitleManagement.cpp +++ b/Source/Core/Core/IOS/ES/TitleManagement.cpp @@ -156,7 +156,7 @@ ReturnCode ES::ImportTmd(Context& context, const std::vector& tmd_bytes) if (ret != IPC_SUCCESS) return ret; - if (!InitImport(context.title_import_export.tmd.GetTitleId())) + if (!InitImport(context.title_import_export.tmd)) return ES_EIO; ret = @@ -238,7 +238,7 @@ ReturnCode ES::ImportTitleInit(Context& context, const std::vector& tmd_byte if (ret != IPC_SUCCESS) return ret; - if (!InitImport(context.title_import_export.tmd.GetTitleId())) + if (!InitImport(context.title_import_export.tmd)) return ES_EIO; context.title_import_export.valid = true; diff --git a/Source/Core/Core/IOS/IOS.cpp b/Source/Core/Core/IOS/IOS.cpp index fe62d8373c..325e6bb5ff 100644 --- a/Source/Core/Core/IOS/IOS.cpp +++ b/Source/Core/Core/IOS/IOS.cpp @@ -266,11 +266,27 @@ u16 Kernel::GetGidForPPC() const return m_ppc_gid; } +static std::vector ReadBootContent(FS::FileSystem* fs, const std::string& path, size_t max_size) +{ + const auto file = fs->OpenFile(0, 0, path, FS::Mode::Read); + if (!file) + return {}; + + const size_t file_size = file->GetStatus()->size; + if (max_size != 0 && file_size > max_size) + return {}; + + std::vector buffer(file_size); + if (!file->Read(buffer.data(), buffer.size())) + return {}; + return buffer; +} + // This corresponds to syscall 0x41, which loads a binary from the NAND and bootstraps the PPC. // Unlike 0x42, IOS will set up some constants in memory before booting the PPC. bool Kernel::BootstrapPPC(const std::string& boot_content_path) { - const DolReader dol{boot_content_path}; + const DolReader dol{ReadBootContent(m_fs.get(), boot_content_path, 0)}; if (!dol.IsValid()) return false; @@ -329,16 +345,7 @@ bool Kernel::BootIOS(const u64 ios_title_id, const std::string& boot_content_pat // Load the ARM binary to memory (if possible). // Because we do not actually emulate the Starlet, only load the sections that are in MEM1. - File::IOFile file{boot_content_path, "rb"}; - // TODO: should return IPC_ERROR_MAX. - if (file.GetSize() > 0xB00000) - return false; - - std::vector data(file.GetSize()); - if (!file.ReadBytes(data.data(), data.size())) - return false; - - ARMBinary binary{std::move(data)}; + ARMBinary binary{ReadBootContent(m_fs.get(), boot_content_path, 0xB00000)}; if (!binary.IsValid()) return false;