Skip to content

Commit

Permalink
Merge pull request #6772 from leoetlino/fs-es
Browse files Browse the repository at this point in the history
IOS/ES: Migrate to new filesystem interface
  • Loading branch information
leoetlino committed May 8, 2018
2 parents f97711a + 606d252 commit 90f869e
Show file tree
Hide file tree
Showing 13 changed files with 395 additions and 325 deletions.
145 changes: 87 additions & 58 deletions Source/Core/Core/IOS/ES/ES.cpp
Expand Up @@ -14,8 +14,6 @@
#include <mbedtls/sha1.h>

#include "Common/ChunkFile.h"
#include "Common/File.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/NandPaths.h"
Expand All @@ -25,6 +23,7 @@
#include "Core/ConfigManager.h"
#include "Core/HW/Memmap.h"
#include "Core/IOS/ES/Formats.h"
#include "Core/IOS/FS/FileSystem.h"
#include "Core/IOS/IOSC.h"
#include "Core/IOS/VersionInfo.h"

Expand All @@ -40,41 +39,42 @@ static u64 s_title_to_launch;
struct DirectoryToCreate
{
const char* path;
u32 attributes;
OpenMode owner_perm;
OpenMode group_perm;
OpenMode other_perm;
FS::FileAttribute attribute;
FS::Mode owner_mode;
FS::Mode group_mode;
FS::Mode other_mode;
FS::Uid uid = PID_KERNEL;
FS::Gid gid = PID_KERNEL;
};

constexpr std::array<DirectoryToCreate, 9> s_directories_to_create = {{
{"/sys", 0, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_NONE},
{"/ticket", 0, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_NONE},
{"/title", 0, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_READ},
{"/shared1", 0, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_NONE},
{"/shared2", 0, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_RW},
{"/tmp", 0, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_RW},
{"/import", 0, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_NONE},
{"/meta", 0, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_RW},
{"/wfs", 0, OpenMode::IOS_OPEN_RW, OpenMode::IOS_OPEN_NONE, OpenMode::IOS_OPEN_NONE},
{"/sys", 0, FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::None},
{"/ticket", 0, FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::None},
{"/title", 0, FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::Read},
{"/shared1", 0, FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::None},
{"/shared2", 0, FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::ReadWrite},
{"/tmp", 0, FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::ReadWrite},
{"/import", 0, FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::None},
{"/meta", 0, FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::ReadWrite,
IOS::ES::FIRST_PPC_UID, 0x1},
{"/wfs", 0, FS::Mode::ReadWrite, FS::Mode::None, FS::Mode::None, PID_UNKNOWN, PID_UNKNOWN},
}};

ES::ES(Kernel& ios, const std::string& device_name) : Device(ios, device_name)
{
for (const auto& directory : s_directories_to_create)
{
const std::string path = Common::RootUserPath(Common::FROM_SESSION_ROOT) + directory.path;

// Create the directory if it does not exist.
if (File::IsDirectory(path))
continue;

File::CreateFullPath(path);
if (File::CreateDir(path))
INFO_LOG(IOS_ES, "Created %s (at %s)", directory.path, path.c_str());
else
ERROR_LOG(IOS_ES, "Failed to create %s (at %s)", directory.path, path.c_str());

// TODO: Set permissions.
// Note: ES sets its own UID and GID to 0/0 at boot, so all filesystem accesses in ES are done
// as UID 0 even though its PID is 1.
const auto result = m_ios.GetFS()->CreateDirectory(PID_KERNEL, PID_KERNEL, directory.path,
directory.attribute, directory.owner_mode,
directory.group_mode, directory.other_mode);
if (result != FS::ResultCode::Success && result != FS::ResultCode::AlreadyExists)
ERROR_LOG(IOS_ES, "Failed to create %s: error %d", directory.path, FS::ConvertResult(result));

// Now update the UID/GID and other attributes.
m_ios.GetFS()->SetMetadata(0, directory.path, directory.uid, directory.gid, directory.attribute,
directory.owner_mode, directory.group_mode, directory.other_mode);
}

FinishAllStaleImports();
Expand Down Expand Up @@ -161,7 +161,7 @@ IPCCommandResult ES::GetTitleId(const IOCtlVRequest& request)

static bool UpdateUIDAndGID(Kernel& kernel, const IOS::ES::TMDReader& tmd)
{
IOS::ES::UIDSys uid_sys{Common::FromWhichRoot::FROM_SESSION_ROOT};
IOS::ES::UIDSys uid_sys{kernel.GetFS()};
const u64 title_id = tmd.GetTitleId();
const u32 uid = uid_sys.GetOrInsertUIDForTitle(title_id);
if (!uid)
Expand All @@ -174,9 +174,9 @@ static bool UpdateUIDAndGID(Kernel& kernel, const IOS::ES::TMDReader& tmd)
return true;
}

static ReturnCode CheckIsAllowedToSetUID(const u32 caller_uid)
static ReturnCode CheckIsAllowedToSetUID(Kernel& kernel, const u32 caller_uid)
{
IOS::ES::UIDSys uid_map{Common::FromWhichRoot::FROM_SESSION_ROOT};
IOS::ES::UIDSys uid_map{kernel.GetFS()};
const u32 system_menu_uid = uid_map.GetOrInsertUIDForTitle(Titles::SYSTEM_MENU);
if (!system_menu_uid)
return ES_SHORT_READ;
Expand All @@ -190,7 +190,7 @@ IPCCommandResult ES::SetUID(u32 uid, const IOCtlVRequest& request)

const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);

const s32 ret = CheckIsAllowedToSetUID(uid);
const s32 ret = CheckIsAllowedToSetUID(m_ios, uid);
if (ret < 0)
{
ERROR_LOG(IOS_ES, "SetUID: Permission check failed with error %d", ret);
Expand Down Expand Up @@ -343,12 +343,8 @@ void ES::DoState(PointerWrap& p)
p.Do(entry.m_opened);
p.Do(entry.m_title_id);
p.Do(entry.m_content);
p.Do(entry.m_position);
p.Do(entry.m_fd);
p.Do(entry.m_uid);
if (entry.m_opened)
entry.m_opened = entry.m_file.Open(GetContentPath(entry.m_title_id, entry.m_content), "rb");
else
entry.m_file.Close();
}

m_title_context.DoState(p);
Expand Down Expand Up @@ -623,6 +619,32 @@ IPCCommandResult ES::DIVerify(const IOCtlVRequest& request)
return GetDefaultReply(ES_EINVAL);
}

static s32 WriteTmdForDiVerify(FS::FileSystem* fs, const IOS::ES::TMDReader& tmd)
{
const std::string temp_path = "/tmp/title.tmd";
fs->Delete(PID_KERNEL, PID_KERNEL, temp_path);
fs->CreateFile(PID_KERNEL, PID_KERNEL, temp_path, 0, FS::Mode::ReadWrite, FS::Mode::ReadWrite,
FS::Mode::None);
{
const auto file = fs->OpenFile(PID_KERNEL, PID_KERNEL, temp_path, FS::Mode::Write);
if (!file)
return FS::ConvertResult(file.Error());
if (!file->Write(tmd.GetBytes().data(), tmd.GetBytes().size()))
return ES_EIO;
}

const std::string tmd_dir = Common::GetTitleContentPath(tmd.GetTitleId());
const std::string tmd_path = Common::GetTMDFileName(tmd.GetTitleId());
const auto result = fs->CreateFullPath(PID_KERNEL, PID_KERNEL, tmd_path, 0, FS::Mode::ReadWrite,
FS::Mode::ReadWrite, FS::Mode::Read);
if (result != FS::ResultCode::Success)
return FS::ConvertResult(result);

fs->SetMetadata(PID_KERNEL, tmd_dir, PID_KERNEL, PID_KERNEL, 0, FS::Mode::ReadWrite,
FS::Mode::ReadWrite, FS::Mode::None);
return FS::ConvertResult(fs->Rename(PID_KERNEL, PID_KERNEL, temp_path, tmd_path));
}

s32 ES::DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& ticket)
{
m_title_context.Clear();
Expand All @@ -637,28 +659,30 @@ s32 ES::DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& tic
m_title_context.Update(tmd, ticket);
INFO_LOG(IOS_ES, "ES_DIVerify: Title context changed: %016" PRIx64, tmd.GetTitleId());

std::string tmd_path = Common::GetTMDFileName(tmd.GetTitleId(), Common::FROM_SESSION_ROOT);

File::CreateFullPath(tmd_path);
File::CreateFullPath(Common::GetTitleDataPath(tmd.GetTitleId(), Common::FROM_SESSION_ROOT));
// XXX: We are supposed to verify the TMD and ticket here, but cannot because
// this may cause issues with custom/patched games.

if (!File::Exists(tmd_path))
const auto fs = m_ios.GetFS();
if (!FindInstalledTMD(tmd.GetTitleId()).IsValid())
{
// XXX: We are supposed to verify the TMD and ticket here, but cannot because
// this may cause issues with custom/patched games.

File::IOFile tmd_file(tmd_path, "wb");
const std::vector<u8>& tmd_bytes = tmd.GetBytes();
if (!tmd_file.WriteBytes(tmd_bytes.data(), tmd_bytes.size()))
ERROR_LOG(IOS_ES, "DIVerify failed to write disc TMD to NAND.");
if (const s32 ret = WriteTmdForDiVerify(fs.get(), tmd))
{
ERROR_LOG(IOS_ES, "DiVerify failed to write disc TMD to NAND.");
return ret;
}
}

if (!UpdateUIDAndGID(*GetIOS(), m_title_context.tmd))
{
return ES_SHORT_READ;
}

return IPC_SUCCESS;
const std::string data_dir = Common::GetTitleDataPath(tmd.GetTitleId());
// Might already exist, so we only need to check whether the second operation succeeded.
fs->CreateDirectory(PID_KERNEL, PID_KERNEL, data_dir, 0, FS::Mode::ReadWrite, FS::Mode::None,
FS::Mode::None);
return FS::ConvertResult(fs->SetMetadata(0, data_dir, m_ios.GetUidForPPC(), m_ios.GetGidForPPC(),
0, FS::Mode::ReadWrite, FS::Mode::None, FS::Mode::None));
}

constexpr u32 FIRST_PPC_UID = 0x1000;
Expand Down Expand Up @@ -823,18 +847,20 @@ bool ES::IsIssuerCorrect(VerifyContainerType type, const IOS::ES::CertReader& is
}
}

static const std::string CERT_STORE_PATH = "/sys/cert.sys";

ReturnCode ES::ReadCertStore(std::vector<u8>* buffer) const
{
if (!SConfig::GetInstance().m_enable_signature_checks)
return IPC_SUCCESS;

const std::string store_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/sys/cert.sys";
File::IOFile store_file{store_path, "rb"};
const auto store_file =
m_ios.GetFS()->OpenFile(PID_KERNEL, PID_KERNEL, CERT_STORE_PATH, FS::Mode::Read);
if (!store_file)
return FS_ENOENT;
return FS::ConvertResult(store_file.Error());

buffer->resize(store_file.GetSize());
if (!store_file.ReadBytes(buffer->data(), buffer->size()))
buffer->resize(store_file->GetStatus()->size);
if (!store_file->Read(buffer->data(), buffer->size()))
return ES_SHORT_READ;
return IPC_SUCCESS;
}
Expand All @@ -853,10 +879,13 @@ ReturnCode ES::WriteNewCertToStore(const IOS::ES::CertReader& cert)
}

// Otherwise, write the new cert at the end of the store.
const std::string store_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/sys/cert.sys";
File::IOFile store_file{store_path, "ab"};
if (!store_file || !store_file.WriteBytes(cert.GetBytes().data(), cert.GetBytes().size()))
const auto store_file =
m_ios.GetFS()->OpenFile(PID_KERNEL, PID_KERNEL, CERT_STORE_PATH, FS::Mode::ReadWrite);
if (!store_file || !store_file->Seek(0, FS::SeekMode::End) ||
!store_file->Write(cert.GetBytes().data(), cert.GetBytes().size()))
{
return ES_EIO;
}
return IPC_SUCCESS;
}

Expand Down
12 changes: 5 additions & 7 deletions Source/Core/Core/IOS/ES/ES.h
Expand Up @@ -10,9 +10,9 @@
#include <vector>

#include "Common/CommonTypes.h"
#include "Common/File.h"
#include "Core/IOS/Device.h"
#include "Core/IOS/ES/Formats.h"
#include "Core/IOS/FS/FileSystem.h"
#include "Core/IOS/IOS.h"
#include "Core/IOS/IOSC.h"

Expand Down Expand Up @@ -326,7 +326,7 @@ class ES final : public Device
const std::vector<u8>& 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.
Expand All @@ -336,17 +336,15 @@ class ES final : public Device
void FinishAllStaleImports();

std::string GetContentPath(u64 title_id, const IOS::ES::Content& content,
const IOS::ES::SharedContentMap& map = IOS::ES::SharedContentMap{
Common::FROM_SESSION_ROOT}) const;
const IOS::ES::SharedContentMap& map) const;
std::string GetContentPath(u64 title_id, const IOS::ES::Content& content) const;

// TODO: reuse the FS code.
struct OpenedContent
{
bool m_opened = false;
File::IOFile m_file;
FS::Fd m_fd;
u64 m_title_id = 0;
IOS::ES::Content m_content;
u32 m_position = 0;
u32 m_uid = 0;
};

Expand Down

0 comments on commit 90f869e

Please sign in to comment.