Merge pull request #5707 from leoetlino/content-table

IOS/ES: Fix content table handling
This commit is contained in:
Leo Lam 2017-06-28 10:42:59 +02:00 committed by GitHub
commit 7454a20f4f
4 changed files with 125 additions and 143 deletions

View File

@ -339,34 +339,11 @@ void ES::DoState(PointerWrap& p)
{
Device::DoState(p);
p.Do(s_content_file);
p.Do(m_AccessIdentID);
p.Do(m_content_table);
s_title_context.DoState(p);
for (auto& context : m_contexts)
context.DoState(p);
u32 Count = (u32)(m_ContentAccessMap.size());
p.Do(Count);
if (p.GetMode() == PointerWrap::MODE_READ)
{
for (u32 i = 0; i < Count; i++)
{
u32 cfd = 0;
OpenedContent content;
p.Do(cfd);
p.Do(content);
cfd = OpenTitleContent(cfd, content.m_title_id, content.m_content.index);
}
}
else
{
for (const auto& pair : m_ContentAccessMap)
{
p.Do(pair.first);
p.Do(pair.second);
}
}
}
ES::ContextArray::iterator ES::FindActiveContext(s32 fd)
@ -403,10 +380,6 @@ ReturnCode ES::Close(u32 fd)
context->active = false;
context->ipc_fd = -1;
// FIXME: IOS doesn't clear the content access map here.
m_ContentAccessMap.clear();
m_AccessIdentID = 0;
INFO_LOG(IOS_ES, "ES: Close");
m_is_active = false;
// clear the NAND content cache to make sure nothing remains open.
@ -441,16 +414,18 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
return ImportTitleCancel(*context, request);
case IOCTL_ES_GETDEVICEID:
return GetDeviceId(request);
case IOCTL_ES_OPENTITLECONTENT:
return OpenTitleContent(context->uid, request);
case IOCTL_ES_OPENCONTENT:
return OpenContent(context->uid, request);
case IOCTL_ES_OPEN_ACTIVE_TITLE_CONTENT:
return OpenActiveTitleContent(context->uid, request);
case IOCTL_ES_READCONTENT:
return ReadContent(context->uid, request);
case IOCTL_ES_CLOSECONTENT:
return CloseContent(context->uid, request);
case IOCTL_ES_SEEKCONTENT:
return SeekContent(context->uid, request);
case IOCTL_ES_GETTITLEDIR:
return GetTitleDirectory(request);
case IOCTL_ES_GETTITLEID:

View File

@ -57,9 +57,11 @@ public:
struct OpenedContent
{
u64 m_title_id;
bool m_opened = false;
u64 m_title_id = 0;
IOS::ES::Content m_content;
u32 m_position;
u32 m_position = 0;
u32 m_uid = 0;
};
struct TitleImportContext
@ -152,7 +154,7 @@ private:
IOCTL_ES_ADDTITLEFINISH = 0x06,
IOCTL_ES_GETDEVICEID = 0x07,
IOCTL_ES_LAUNCH = 0x08,
IOCTL_ES_OPENCONTENT = 0x09,
IOCTL_ES_OPEN_ACTIVE_TITLE_CONTENT = 0x09,
IOCTL_ES_READCONTENT = 0x0A,
IOCTL_ES_CLOSECONTENT = 0x0B,
IOCTL_ES_GETOWNEDTITLECNT = 0x0C,
@ -179,7 +181,7 @@ private:
IOCTL_ES_SETUID = 0x21,
IOCTL_ES_DELETETITLECONTENT = 0x22,
IOCTL_ES_SEEKCONTENT = 0x23,
IOCTL_ES_OPENTITLECONTENT = 0x24,
IOCTL_ES_OPENCONTENT = 0x24,
IOCTL_ES_LAUNCHBC = 0x25,
IOCTL_ES_EXPORTTITLEINIT = 0x26,
IOCTL_ES_EXPORTCONTENTBEGIN = 0x27,
@ -258,7 +260,7 @@ private:
IPCCommandResult DeleteStreamKey(const IOCtlVRequest& request);
// Title contents
IPCCommandResult OpenTitleContent(u32 uid, const IOCtlVRequest& request);
IPCCommandResult OpenActiveTitleContent(u32 uid, const IOCtlVRequest& request);
IPCCommandResult OpenContent(u32 uid, const IOCtlVRequest& request);
IPCCommandResult ReadContent(u32 uid, const IOCtlVRequest& request);
IPCCommandResult CloseContent(u32 uid, const IOCtlVRequest& request);
@ -340,12 +342,10 @@ private:
static const DiscIO::NANDContentLoader& AccessContentDevice(u64 title_id);
u32 OpenTitleContent(u32 CFD, u64 TitleID, u16 Index);
s32 OpenContent(const IOS::ES::TMDReader& tmd, u16 content_index, u32 uid);
using ContentAccessMap = std::map<u32, OpenedContent>;
ContentAccessMap m_ContentAccessMap;
u32 m_AccessIdentID = 0;
using ContentTable = std::array<OpenedContent, 16>;
ContentTable m_content_table;
ContextArray m_contexts;
};

View File

@ -20,103 +20,114 @@ namespace HLE
{
namespace Device
{
u32 ES::OpenTitleContent(u32 CFD, u64 TitleID, u16 Index)
s32 ES::OpenContent(const IOS::ES::TMDReader& tmd, u16 content_index, u32 uid)
{
const DiscIO::NANDContentLoader& Loader = AccessContentDevice(TitleID);
const u64 title_id = tmd.GetTitleId();
const DiscIO::NANDContentLoader& loader = AccessContentDevice(title_id);
if (!Loader.IsValid() || !Loader.GetTMD().IsValid() || !Loader.GetTicket().IsValid())
if (!loader.IsValid())
return FS_ENOENT;
const DiscIO::NANDContent* content = loader.GetContentByIndex(content_index);
if (!content)
return FS_ENOENT;
for (size_t i = 0; i < m_content_table.size(); ++i)
{
WARN_LOG(IOS_ES, "ES: loader not valid for %" PRIx64, TitleID);
return 0xffffffff;
OpenedContent& entry = m_content_table[i];
if (entry.m_opened)
continue;
entry.m_opened = true;
entry.m_position = 0;
entry.m_content = content->m_metadata;
entry.m_title_id = title_id;
entry.m_uid = uid;
INFO_LOG(IOS_ES, "OpenContent: title ID %016" PRIx64 ", UID 0x%x, CFD %zu", title_id, uid, i);
return static_cast<s32>(i);
}
const DiscIO::NANDContent* pContent = Loader.GetContentByIndex(Index);
if (pContent == nullptr)
{
return 0xffffffff; // TODO: what is the correct error value here?
}
OpenedContent content;
content.m_position = 0;
content.m_content = pContent->m_metadata;
content.m_title_id = TitleID;
pContent->m_Data->Open();
m_ContentAccessMap[CFD] = content;
return CFD;
}
IPCCommandResult ES::OpenTitleContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(3, 0))
return GetDefaultReply(ES_EINVAL);
u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
u32 Index = Memory::Read_U32(request.in_vectors[2].address);
s32 CFD = OpenTitleContent(m_AccessIdentID++, TitleID, Index);
INFO_LOG(IOS_ES, "IOCTL_ES_OPENTITLECONTENT: TitleID: %016" PRIx64 " Index %i -> got CFD %x",
TitleID, Index, CFD);
return GetDefaultReply(CFD);
return FS_EFDEXHAUSTED;
}
IPCCommandResult ES::OpenContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 0))
if (!request.HasNumberOfValidVectors(3, 0) || request.in_vectors[0].size != sizeof(u64) ||
request.in_vectors[1].size != sizeof(IOS::ES::TicketView) ||
request.in_vectors[2].size != sizeof(u32))
{
return GetDefaultReply(ES_EINVAL);
u32 Index = Memory::Read_U32(request.in_vectors[0].address);
}
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
const u32 content_index = Memory::Read_U32(request.in_vectors[2].address);
// TODO: check the ticket view, check permissions.
const auto tmd = FindInstalledTMD(title_id);
if (!tmd.IsValid())
return GetDefaultReply(FS_ENOENT);
return GetDefaultReply(OpenContent(tmd, content_index, uid));
}
IPCCommandResult ES::OpenActiveTitleContent(u32 caller_uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32))
return GetDefaultReply(ES_EINVAL);
const u32 content_index = Memory::Read_U32(request.in_vectors[0].address);
if (!GetTitleContext().active)
return GetDefaultReply(ES_EINVAL);
s32 CFD = OpenTitleContent(m_AccessIdentID++, GetTitleContext().tmd.GetTitleId(), Index);
INFO_LOG(IOS_ES, "IOCTL_ES_OPENCONTENT: Index %i -> got CFD %x", Index, CFD);
IOS::ES::UIDSys uid_map{Common::FROM_SESSION_ROOT};
const u32 uid = uid_map.GetOrInsertUIDForTitle(GetTitleContext().tmd.GetTitleId());
if (caller_uid != 0 && caller_uid != uid)
return GetDefaultReply(ES_EACCES);
return GetDefaultReply(CFD);
return GetDefaultReply(OpenContent(GetTitleContext().tmd, content_index, caller_uid));
}
IPCCommandResult ES::ReadContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 1))
if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != sizeof(u32))
return GetDefaultReply(ES_EINVAL);
u32 CFD = Memory::Read_U32(request.in_vectors[0].address);
u32 Size = request.io_vectors[0].size;
u32 Addr = request.io_vectors[0].address;
const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
u32 size = request.io_vectors[0].size;
const u32 addr = request.io_vectors[0].address;
auto itr = m_ContentAccessMap.find(CFD);
if (itr == m_ContentAccessMap.end())
if (cfd >= m_content_table.size())
return GetDefaultReply(ES_EINVAL);
OpenedContent& entry = m_content_table[cfd];
if (entry.m_uid != uid)
return GetDefaultReply(ES_EACCES);
if (!entry.m_opened)
return GetDefaultReply(IPC_EINVAL);
// XXX: make this reuse the FS code... ES just does a simple "IOS_Read" call here
// instead of all this duplicated filesystem logic.
if (entry.m_position + size > entry.m_content.size)
size = static_cast<u32>(entry.m_content.size) - entry.m_position;
if (size > 0)
{
return GetDefaultReply(-1);
}
OpenedContent& rContent = itr->second;
u8* pDest = Memory::GetPointer(Addr);
if (rContent.m_position + Size > rContent.m_content.size)
{
Size = static_cast<u32>(rContent.m_content.size) - rContent.m_position;
}
if (Size > 0)
{
if (pDest)
if (addr)
{
const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(rContent.m_title_id);
const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(entry.m_title_id);
// ContentLoader should never be invalid; rContent has been created by it.
if (ContentLoader.IsValid() && ContentLoader.GetTicket().IsValid())
{
const DiscIO::NANDContent* pContent =
ContentLoader.GetContentByIndex(rContent.m_content.index);
if (!pContent->m_Data->GetRange(rContent.m_position, Size, pDest))
ERROR_LOG(IOS_ES, "ES: failed to read %u bytes from %u!", Size, rContent.m_position);
ContentLoader.GetContentByIndex(entry.m_content.index);
pContent->m_Data->Open();
if (!pContent->m_Data->GetRange(entry.m_position, size, Memory::GetPointer(addr)))
ERROR_LOG(IOS_ES, "ES: failed to read %u bytes from %u!", size, entry.m_position);
}
rContent.m_position += Size;
entry.m_position += size;
}
else
{
@ -124,39 +135,35 @@ IPCCommandResult ES::ReadContent(u32 uid, const IOCtlVRequest& request)
}
}
DEBUG_LOG(IOS_ES,
"IOCTL_ES_READCONTENT: CFD %x, Address 0x%x, Size %i -> stream pos %i (Index %i)", CFD,
Addr, Size, rContent.m_position, rContent.m_content.index);
return GetDefaultReply(Size);
return GetDefaultReply(size);
}
IPCCommandResult ES::CloseContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 0))
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32))
return GetDefaultReply(ES_EINVAL);
u32 CFD = Memory::Read_U32(request.in_vectors[0].address);
const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
if (cfd >= m_content_table.size())
return GetDefaultReply(ES_EINVAL);
INFO_LOG(IOS_ES, "IOCTL_ES_CLOSECONTENT: CFD %x", CFD);
OpenedContent& entry = m_content_table[cfd];
if (entry.m_uid != uid)
return GetDefaultReply(ES_EACCES);
if (!entry.m_opened)
return GetDefaultReply(IPC_EINVAL);
auto itr = m_ContentAccessMap.find(CFD);
if (itr == m_ContentAccessMap.end())
{
return GetDefaultReply(-1);
}
const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(itr->second.m_title_id);
// XXX: again, this should be a simple IOS_Close.
const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(entry.m_title_id);
// ContentLoader should never be invalid; we shouldn't be here if ES_OPENCONTENT failed before.
if (ContentLoader.IsValid())
{
const DiscIO::NANDContent* pContent =
ContentLoader.GetContentByIndex(itr->second.m_content.index);
pContent->m_Data->Close();
const DiscIO::NANDContent* content = ContentLoader.GetContentByIndex(entry.m_content.index);
content->m_Data->Close();
}
m_ContentAccessMap.erase(itr);
entry = {};
INFO_LOG(IOS_ES, "CloseContent: CFD %u", cfd);
return GetDefaultReply(IPC_SUCCESS);
}
@ -165,36 +172,36 @@ IPCCommandResult ES::SeekContent(u32 uid, const IOCtlVRequest& request)
if (!request.HasNumberOfValidVectors(3, 0))
return GetDefaultReply(ES_EINVAL);
u32 CFD = Memory::Read_U32(request.in_vectors[0].address);
const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
if (cfd >= m_content_table.size())
return GetDefaultReply(ES_EINVAL);
OpenedContent& entry = m_content_table[cfd];
if (entry.m_uid != uid)
return GetDefaultReply(ES_EACCES);
if (!entry.m_opened)
return GetDefaultReply(IPC_EINVAL);
u32 Addr = Memory::Read_U32(request.in_vectors[1].address);
u32 Mode = Memory::Read_U32(request.in_vectors[2].address);
auto itr = m_ContentAccessMap.find(CFD);
if (itr == m_ContentAccessMap.end())
{
return GetDefaultReply(-1);
}
OpenedContent& rContent = itr->second;
// XXX: This should be a simple IOS_Seek.
switch (Mode)
{
case 0: // SET
rContent.m_position = Addr;
entry.m_position = Addr;
break;
case 1: // CUR
rContent.m_position += Addr;
entry.m_position += Addr;
break;
case 2: // END
rContent.m_position = static_cast<u32>(rContent.m_content.size) + Addr;
entry.m_position = static_cast<u32>(entry.m_content.size) + Addr;
break;
}
DEBUG_LOG(IOS_ES, "IOCTL_ES_SEEKCONTENT: CFD %x, Address 0x%x, Mode %i -> Pos %i", CFD, Addr,
Mode, rContent.m_position);
return GetDefaultReply(rContent.m_position);
return GetDefaultReply(entry.m_position);
}
} // namespace Device
} // namespace HLE

View File

@ -73,7 +73,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread;
// Don't forget to increase this after doing changes on the savestate system
static const u32 STATE_VERSION = 86; // Last changed in PR 2353
static const u32 STATE_VERSION = 87; // Last changed in PR 5707
// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,