@@ -13,34 +13,36 @@
#include < mbedtls/sha1.h>
#include " Common/Align.h"
#include " Common/File.h"
#include " Common/FileUtil.h"
#include " Common/Logging/Log.h"
#include " Common/NandPaths.h"
#include " Common/StringUtil.h"
#include " Core/CommonTitles.h"
#include " Core/HW/Memmap.h"
#include " Core/IOS/ES/Formats.h"
#include " Core/IOS/FS/FileSystem.h"
namespace IOS
{
namespace HLE
{
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 std::string ticket_path = Common::GetTicketFileName (title_id, Common::FROM_SESSION_ROOT);
File::CreateFullPath (ticket_path);
const std::string path = Common::GetTicketFileName (title_id);
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 " );
if (!ticket_file )
return ES_EIO ;
const auto file = fs-> OpenFile (PID_KERNEL, PID_KERNEL, path, FS::Mode::Write );
if (!file )
return FS::ConvertResult (file. Error ()) ;
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)
@@ -84,7 +86,7 @@ ReturnCode ES::ImportTicket(const std::vector<u8>& ticket_bytes, const std::vect
if (verify_ret != IPC_SUCCESS)
return verify_ret;
const ReturnCode write_ret = WriteTicket (ticket);
const ReturnCode write_ret = WriteTicket (m_ios. GetFS (). get (), ticket);
if (write_ret != IPC_SUCCESS)
return write_ret;
@@ -155,7 +157,7 @@ ReturnCode ES::ImportTmd(Context& context, const std::vector<u8>& 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 =
@@ -237,7 +239,7 @@ ReturnCode ES::ImportTitleInit(Context& context, const std::vector<u8>& 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 ;
@@ -342,8 +344,7 @@ static bool CheckIfContentHashMatches(const std::vector<u8>& content, const IOS:
static std::string GetImportContentPath (u64 title_id, u32 content_id)
{
return Common::GetImportTitlePath (title_id, Common::FROM_SESSION_ROOT) +
StringFromFormat (" /content/%08x.app" , content_id);
return Common::GetImportTitlePath (title_id) + StringFromFormat (" /content/%08x.app" , content_id);
}
ReturnCode ES::ImportContentEnd (Context& context, u32 content_fd)
@@ -370,37 +371,39 @@ ReturnCode ES::ImportContentEnd(Context& context, u32 content_fd)
return ES_HASH_MISMATCH;
}
const auto fs = m_ios.GetFS ();
std::string content_path;
if (content_info.IsShared ())
{
IOS::ES::SharedContentMap shared_content{Common::FROM_SESSION_ROOT };
IOS::ES::SharedContentMap shared_content{fs };
content_path = shared_content.AddSharedContent (content_info.sha1 );
}
else
{
content_path = GetImportContentPath (context.title_import_export .tmd .GetTitleId (),
context.title_import_export .content .id );
}
File::CreateFullPath (content_path);
const std::string temp_path =
Common::RootUserPath (Common::FROM_SESSION_ROOT) +
StringFromFormat (" /tmp/%08x.app" , context.title_import_export .content .id );
File::CreateFullPath (temp_path);
" /tmp/" + content_path.substr (content_path.find_last_of (' /' ) + 1 , std::string::npos);
fs->CreateFile (PID_KERNEL, PID_KERNEL, temp_path, 0 , FS::Mode::ReadWrite, FS::Mode::ReadWrite,
FS::Mode::None);
{
File::IOFile file ( temp_path, " wb " );
if (!file. WriteBytes (decrypted_data.data (), content_info.size ))
const auto file = fs-> OpenFile (PID_KERNEL, PID_KERNEL, temp_path, FS::Mode::Write );
if (!file || !file-> Write (decrypted_data.data (), content_info.size ))
{
ERROR_LOG (IOS_ES, " ImportContentEnd: Failed to write to %s" , temp_path.c_str ());
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 ());
return ES_EIO ;
return FS::ConvertResult (rename_result) ;
}
context.title_import_export .content = {};
@@ -424,7 +427,7 @@ ReturnCode ES::ImportTitleDone(Context& context)
// Make sure all listed, non-optional contents have been imported.
const u64 title_id = context.title_import_export .tmd .GetTitleId ();
const std::vector<IOS::ES::Content> contents = context.title_import_export .tmd .GetContents ();
const IOS::ES::SharedContentMap shared_content_map{Common::FROM_SESSION_ROOT };
const IOS::ES::SharedContentMap shared_content_map{m_ios. GetFS () };
const bool has_all_required_contents =
std::all_of (contents.cbegin (), contents.cend (), [&](const IOS::ES::Content& content) {
if (content.IsOptional ())
@@ -435,8 +438,9 @@ ReturnCode ES::ImportTitleDone(Context& context)
// Note: the import hasn't been finalised yet, so the whole title directory
// is still in /import, not /title.
return File::Exists (Common::GetImportTitlePath (title_id, Common::FROM_SESSION_ROOT) +
StringFromFormat (" /content/%08x.app" , content.id ));
const std::string path = Common::GetImportTitlePath (title_id) +
StringFromFormat (" /content/%08x.app" , content.id );
return m_ios.GetFS ()->GetMetadata (PID_KERNEL, PID_KERNEL, path).Succeeded ();
});
if (!has_all_required_contents)
return ES_EINVAL;
@@ -497,17 +501,8 @@ ReturnCode ES::DeleteTitle(u64 title_id)
if (!CanDeleteTitle (title_id))
return ES_EINVAL;
const std::string title_dir = Common::GetTitlePath (title_id, Common::FROM_SESSION_ROOT);
if (!File::IsDirectory (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;
const std::string title_dir = Common::GetTitlePath (title_id);
return FS::ConvertResult (m_ios.GetFS ()->Delete (PID_KERNEL, PID_KERNEL, title_dir));
}
IPCCommandResult ES::DeleteTitle (const IOCtlVRequest& request)
@@ -521,6 +516,7 @@ IPCCommandResult ES::DeleteTitle(const IOCtlVRequest& request)
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));
if (!CanDeleteTitle (title_id))
@@ -534,24 +530,26 @@ ReturnCode ES::DeleteTicket(const u8* ticket_view)
ticket.DeleteTicket (ticket_id);
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 " );
if (!ticket_file || !ticket_file. WriteBytes (new_ticket.data (), new_ticket.size ()))
const auto file = fs-> OpenFile (PID_KERNEL, PID_KERNEL, ticket_path, FS::Mode::ReadWrite );
if (!file || !file-> Write (new_ticket.data (), new_ticket.size ()))
return ES_EIO;
}
// Delete the ticket file if it is now empty.
if (new_ticket.empty ())
File::Delete (ticket_path);
else
{
// Delete the ticket file if it is now empty.
fs->Delete (PID_KERNEL, PID_KERNEL, ticket_path);
}
// Delete the ticket directory if it is now empty.
const std::string ticket_parent_dir =
Common::RootUserPath (Common::FROM_CONFIGURED_ROOT) +
StringFromFormat (" /ticket/%08x" , static_cast <u32>(title_id >> 32 ));
const auto ticket_parent_dir_entries = File::ScanDirectoryTree (ticket_parent_dir, false );
if (ticket_parent_dir_entries.children .empty ())
File::DeleteDir (ticket_parent_dir);
const auto ticket_parent_dir_entries =
fs->ReadDirectory (PID_KERNEL, PID_KERNEL, 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;
}
@@ -571,14 +569,15 @@ ReturnCode ES::DeleteTitleContent(u64 title_id) const
if (!CanDeleteTitle (title_id))
return ES_EINVAL;
const std::string content_dir = Common::GetTitleContentPath (title_id, Common::FROM_SESSION_ROOT);
if (!File::IsDirectory (content_dir))
return FS_ENOENT;
const std::string content_dir = Common::GetTitleContentPath (title_id);
const auto files = m_ios.GetFS ()->ReadDirectory (PID_KERNEL, PID_KERNEL, content_dir);
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 )
File:: Delete (file. physicalName );
if (file_name. size () == 12 && file_name .compare (8 , 4 , " .app" ) == 0 )
m_ios. GetFS ()-> Delete (PID_KERNEL, PID_KERNEL, content_dir + ' / ' + file_name );
}
return IPC_SUCCESS;
@@ -604,13 +603,9 @@ ReturnCode ES::DeleteContent(u64 title_id, u32 content_id) const
if (!tmd.FindContentById (content_id, &content))
return ES_EINVAL;
if (!File::Delete (Common::GetTitleContentPath (title_id, Common::FROM_SESSION_ROOT) +
StringFromFormat (" /%08x.app" , content_id)))
{
return FS_ENOENT;
}
return IPC_SUCCESS;
return FS::ConvertResult (m_ios.GetFS ()->Delete (PID_KERNEL, PID_KERNEL,
Common::GetTitleContentPath (title_id) +
StringFromFormat (" /%08x.app" , content_id)));
}
IPCCommandResult ES::DeleteContent (const IOCtlVRequest& request)
@@ -785,7 +780,7 @@ IPCCommandResult ES::ExportTitleDone(Context& context, const IOCtlVRequest& requ
ReturnCode ES::DeleteSharedContent (const std::array<u8, 20 >& sha1) const
{
IOS::ES::SharedContentMap map{Common::FromWhichRoot::FROM_SESSION_ROOT };
IOS::ES::SharedContentMap map{m_ios. GetFS () };
const auto content_path = map.GetFilenameFromSHA1 (sha1);
if (!content_path)
return ES_EINVAL;
@@ -810,8 +805,9 @@ ReturnCode ES::DeleteSharedContent(const std::array<u8, 20>& sha1) const
return ES_EINVAL;
// Delete the shared content and update the content map.
if (!File::Delete (*content_path))
return FS_ENOENT;
const auto delete_result = m_ios.GetFS ()->Delete (PID_KERNEL, PID_KERNEL, *content_path);
if (delete_result != FS::ResultCode::Success)
return FS::ConvertResult (delete_result);
if (!map.DeleteSharedContent (sha1))
return ES_EIO;