From daecd812b04e90da525e1858c40fd6c0a1e59ede Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 13 Dec 2017 13:56:53 -0500 Subject: [PATCH 1/2] HLE/Services: Allow specifying a SessionData template parameter to ServiceFramework. Some services can have multiple clients at the same time, and they identify the different clients using the server session as a key. This parameter (if present) should be a structure that contains the per-session data for each service. The data can be retrieved using ServiceFramework::GetSessionData(session) --- src/core/hle/kernel/hle_ipc.cpp | 14 +++++++++---- src/core/hle/kernel/hle_ipc.h | 34 ++++++++++++++++++++++++++----- src/core/hle/service/fs/archive.h | 4 ++++ src/core/hle/service/service.h | 16 +++++++++++++-- 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 9e45970d1..5b76a92cd 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -2,26 +2,32 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include -#include #include "common/assert.h" #include "common/common_types.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/server_session.h" namespace Kernel { +SessionRequestHandler::SessionInfo::SessionInfo(SharedPtr session, + std::unique_ptr data) + : session(std::move(session)), data(std::move(data)) {} + void SessionRequestHandler::ClientConnected(SharedPtr server_session) { server_session->SetHleHandler(shared_from_this()); - connected_sessions.push_back(server_session); + connected_sessions.emplace_back(std::move(server_session), MakeSessionData()); } void SessionRequestHandler::ClientDisconnected(SharedPtr server_session) { server_session->SetHleHandler(nullptr); - boost::range::remove_erase(connected_sessions, server_session); + connected_sessions.erase( + std::remove_if(connected_sessions.begin(), connected_sessions.end(), + [&](const SessionInfo& info) { return info.session == server_session; }), + connected_sessions.end()); } HLERequestContext::HLERequestContext(SharedPtr session) diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 338f3c7d1..ccff81a21 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include @@ -54,13 +55,36 @@ public: * associated ServerSession. * @param server_session ServerSession associated with the connection. */ - virtual void ClientDisconnected(SharedPtr server_session); + void ClientDisconnected(SharedPtr server_session); + + /// Empty placeholder structure for services with no per-session data. The session data classes + /// in each service must inherit from this. + struct SessionDataBase {}; protected: - /// List of sessions that are connected to this handler. - /// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list - // for the duration of the connection. - std::vector> connected_sessions; + /// Creates the storage for the session data of the service. + virtual std::unique_ptr MakeSessionData() const = 0; + + /// Returns the session data associated with the server session. + template + T* GetSessionData(SharedPtr session) { + static_assert(std::is_base_of(), + "T is not a subclass of SessionDataBase"); + auto itr = std::find_if(connected_sessions.begin(), connected_sessions.end(), + [&](const SessionInfo& info) { return info.session == session; }); + ASSERT(itr != connected_sessions.end()); + return static_cast(itr->data.get()); + } + + struct SessionInfo { + SessionInfo(SharedPtr session, std::unique_ptr data); + + SharedPtr session; + std::unique_ptr data; + }; + /// List of sessions that are connected to this handler. A ServerSession whose server endpoint + /// is an HLE implementation is kept alive by this list for the duration of the connection. + std::vector connected_sessions; }; class MappedBuffer { diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 01d67c3d0..65f6f207a 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -108,6 +108,10 @@ public: protected: void HandleSyncRequest(Kernel::SharedPtr server_session) override; + + std::unique_ptr MakeSessionData() const override { + return nullptr; + } }; /** diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 084994816..26894ce25 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -87,6 +87,10 @@ public: protected: void HandleSyncRequest(Kernel::SharedPtr server_session) override; + std::unique_ptr MakeSessionData() const override { + return nullptr; + } + /** * Registers the functions in the service */ @@ -144,7 +148,7 @@ protected: using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&); private: - template + template friend class ServiceFramework; struct FunctionInfoBase { @@ -190,7 +194,7 @@ private: * of the passed in function pointers and then delegate the actual work to the implementation in the * base class. */ -template +template class ServiceFramework : public ServiceFrameworkBase { protected: /// Contains information about a request type which is handled by the service. @@ -236,6 +240,14 @@ protected: RegisterHandlersBase(functions, n); } + std::unique_ptr MakeSessionData() const override { + return std::make_unique(); + } + + SessionData* GetSessionData(Kernel::SharedPtr server_session) { + return ServiceFrameworkBase::GetSessionData(server_session); + } + private: /** * This function is used to allow invocation of pointers to handlers stored in the base class From b5bfaaae2931a8c519298aefc1fa38e0c3a501b4 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 13 Dec 2017 13:58:25 -0500 Subject: [PATCH 2/2] HLE/FS: Use the SessionData parameter of ServiceFramework in the File class. --- src/core/hle/service/fs/archive.cpp | 100 +++++++++++----------------- src/core/hle/service/fs/archive.h | 23 +++---- 2 files changed, 46 insertions(+), 77 deletions(-) diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 92eb4ef04..db304812c 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -92,15 +92,15 @@ void File::Read(Kernel::HLERequestContext& ctx) { LOG_TRACE(Service_FS, "Read %s: offset=0x%" PRIx64 " length=0x%08X", GetName().c_str(), offset, length); - const SessionSlot& file = GetSessionSlot(ctx.Session()); + const FileSessionSlot* file = GetSessionData(ctx.Session()); - if (file.subfile && length > file.size) { + if (file->subfile && length > file->size) { LOG_WARNING(Service_FS, "Trying to read beyond the subfile size, truncating"); - length = file.size; + length = file->size; } // This file session might have a specific offset from where to start reading, apply it. - offset += file.offset; + offset += file->offset; if (offset + length > backend->GetSize()) { LOG_ERROR(Service_FS, "Reading from out of bounds offset=0x%" PRIx64 @@ -134,10 +134,10 @@ void File::Write(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); - const SessionSlot& file = GetSessionSlot(ctx.Session()); + const FileSessionSlot* file = GetSessionData(ctx.Session()); // Subfiles can not be written to - if (file.subfile) { + if (file->subfile) { rb.Push(FileSys::ERROR_UNSUPPORTED_OPEN_FLAGS); rb.Push(0); rb.PushMappedBuffer(buffer); @@ -160,28 +160,28 @@ void File::Write(Kernel::HLERequestContext& ctx) { void File::GetSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x0804, 0, 0); - const SessionSlot& file = GetSessionSlot(ctx.Session()); + const FileSessionSlot* file = GetSessionData(ctx.Session()); IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); rb.Push(RESULT_SUCCESS); - rb.Push(file.size); + rb.Push(file->size); } void File::SetSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x0805, 2, 0); u64 size = rp.Pop(); - SessionSlot& file = GetSessionSlot(ctx.Session()); + FileSessionSlot* file = GetSessionData(ctx.Session()); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); // SetSize can not be called on subfiles. - if (file.subfile) { + if (file->subfile) { rb.Push(FileSys::ERROR_UNSUPPORTED_OPEN_FLAGS); return; } - file.size = size; + file->size = size; backend->SetSize(size); rb.Push(RESULT_SUCCESS); } @@ -190,9 +190,9 @@ void File::Close(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x0808, 0, 0); // TODO(Subv): Only close the backend if this client is the only one left. - if (session_slots.size() > 1) + if (connected_sessions.size() > 1) LOG_WARNING(Service_FS, "Closing File backend but %zu clients still connected", - session_slots.size()); + connected_sessions.size()); backend->Close(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -204,10 +204,10 @@ void File::Flush(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - const SessionSlot& file = GetSessionSlot(ctx.Session()); + const FileSessionSlot* file = GetSessionData(ctx.Session()); // Subfiles can not be flushed. - if (file.subfile) { + if (file->subfile) { rb.Push(FileSys::ERROR_UNSUPPORTED_OPEN_FLAGS); return; } @@ -219,8 +219,8 @@ void File::Flush(Kernel::HLERequestContext& ctx) { void File::SetPriority(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x080A, 1, 0); - SessionSlot& file = GetSessionSlot(ctx.Session()); - file.priority = rp.Pop(); + FileSessionSlot* file = GetSessionData(ctx.Session()); + file->priority = rp.Pop(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -228,11 +228,11 @@ void File::SetPriority(Kernel::HLERequestContext& ctx) { void File::GetPriority(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x080B, 0, 0); - const SessionSlot& file = GetSessionSlot(ctx.Session()); + const FileSessionSlot* file = GetSessionData(ctx.Session()); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); - rb.Push(file.priority); + rb.Push(file->priority); } void File::OpenLinkFile(Kernel::HLERequestContext& ctx) { @@ -246,16 +246,13 @@ void File::OpenLinkFile(Kernel::HLERequestContext& ctx) { auto server = std::get>(sessions); ClientConnected(server); - const SessionSlot& original_file = GetSessionSlot(ctx.Session()); + FileSessionSlot* slot = GetSessionData(server); + const FileSessionSlot* original_file = GetSessionData(ctx.Session()); - SessionSlot slot{}; - slot.priority = original_file.priority; - slot.offset = 0; - slot.size = backend->GetSize(); - slot.session = server; - slot.subfile = false; - - session_slots.emplace_back(std::move(slot)); + slot->priority = original_file->priority; + slot->offset = 0; + slot->size = backend->GetSize(); + slot->subfile = false; rb.Push(RESULT_SUCCESS); rb.PushMoveObjects(std::get>(sessions)); @@ -268,9 +265,9 @@ void File::OpenSubFile(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - const SessionSlot& original_file = GetSessionSlot(ctx.Session()); + const FileSessionSlot* original_file = GetSessionData(ctx.Session()); - if (original_file.subfile) { + if (original_file->subfile) { // OpenSubFile can not be called on a file which is already as subfile rb.Push(FileSys::ERROR_UNSUPPORTED_OPEN_FLAGS); return; @@ -285,7 +282,7 @@ void File::OpenSubFile(Kernel::HLERequestContext& ctx) { // TODO(Subv): Check for overflow and return ERR_WRITE_BEYOND_END - if (end > original_file.size) { + if (end > original_file->size) { rb.Push(FileSys::ERR_WRITE_BEYOND_END); return; } @@ -297,47 +294,26 @@ void File::OpenSubFile(Kernel::HLERequestContext& ctx) { auto server = std::get>(sessions); ClientConnected(server); - SessionSlot slot{}; - slot.priority = original_file.priority; - slot.offset = offset; - slot.size = size; - slot.session = server; - slot.subfile = true; - - session_slots.emplace_back(std::move(slot)); + FileSessionSlot* slot = GetSessionData(server); + slot->priority = original_file->priority; + slot->offset = offset; + slot->size = size; + slot->subfile = true; rb.Push(RESULT_SUCCESS); rb.PushMoveObjects(std::get>(sessions)); } -File::SessionSlot& File::GetSessionSlot(Kernel::SharedPtr session) { - auto itr = std::find_if(session_slots.begin(), session_slots.end(), - [&](const SessionSlot& slot) { return slot.session == session; }); - ASSERT(itr != session_slots.end()); - return *itr; -} - -void File::ClientDisconnected(Kernel::SharedPtr server_session) { - session_slots.erase( - std::remove_if(session_slots.begin(), session_slots.end(), - [&](const SessionSlot& slot) { return slot.session == server_session; }), - session_slots.end()); - SessionRequestHandler::ClientDisconnected(server_session); -} - Kernel::SharedPtr File::Connect() { auto sessions = Kernel::ServerSession::CreateSessionPair(GetName()); auto server = std::get>(sessions); ClientConnected(server); - SessionSlot slot{}; - slot.priority = 0; - slot.offset = 0; - slot.size = backend->GetSize(); - slot.session = server; - slot.subfile = false; - - session_slots.emplace_back(std::move(slot)); + FileSessionSlot* slot = GetSessionData(server); + slot->priority = 0; + slot->offset = 0; + slot->size = backend->GetSize(); + slot->subfile = false; return std::get>(sessions); } diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 65f6f207a..df4c902d0 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -50,9 +50,16 @@ enum class MediaType : u32 { NAND = 0, SDMC = 1, GameCard = 2 }; typedef u64 ArchiveHandle; +struct FileSessionSlot : public Kernel::SessionRequestHandler::SessionDataBase { + u32 priority; ///< Priority of the file. TODO(Subv): Find out what this means + u64 offset; ///< Offset that this session will start reading from. + u64 size; ///< Max size of the file that this session is allowed to access + bool subfile; ///< Whether this file was opened via OpenSubFile or not. +}; + // TODO: File is not a real service, but it can still utilize ServiceFramework::RegisterHandlers. // Consider splitting ServiceFramework interface. -class File final : public ServiceFramework { +class File final : public ServiceFramework { public: File(std::unique_ptr&& backend, const FileSys::Path& path); ~File() = default; @@ -67,8 +74,6 @@ public: /// Creates a new session to this File and returns the ClientSession part of the connection. Kernel::SharedPtr Connect(); - void ClientDisconnected(Kernel::SharedPtr server_session) override; - private: void Read(Kernel::HLERequestContext& ctx); void Write(Kernel::HLERequestContext& ctx); @@ -80,18 +85,6 @@ private: void GetPriority(Kernel::HLERequestContext& ctx); void OpenLinkFile(Kernel::HLERequestContext& ctx); void OpenSubFile(Kernel::HLERequestContext& ctx); - - struct SessionSlot { - Kernel::SharedPtr session; ///< The session that this slot refers to. - u32 priority; ///< Priority of the file. TODO(Subv): Find out what this means - u64 offset; ///< Offset that this session will start reading from. - u64 size; ///< Max size of the file that this session is allowed to access - bool subfile; ///< Whether this file was opened via OpenSubFile or not. - }; - - std::vector session_slots; - - SessionSlot& GetSessionSlot(Kernel::SharedPtr session); }; class Directory final : public Kernel::SessionRequestHandler {