From f1a9dd8a98072156d1a9e85c461131f33a89ae1c Mon Sep 17 00:00:00 2001 From: misson20000 Date: Thu, 12 Dec 2019 22:56:50 -0800 Subject: [PATCH] loader: fix ECS after new-ipc rewrite The call to serviceCreate(...) tries to query pointer buffer size, but since we haven't had a chance to return the server side of the session yet, this deadlocks. Instead, we defer creating the session and mounting the filesystem until the first time the ECS object is used. If mounting the filesystem fails, the ECS is silently discarded. --- stratosphere/loader/source/ldr_ecs.cpp | 49 +++++++++++++++++++------- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/stratosphere/loader/source/ldr_ecs.cpp b/stratosphere/loader/source/ldr_ecs.cpp index 91da78849..a49a3ebd7 100644 --- a/stratosphere/loader/source/ldr_ecs.cpp +++ b/stratosphere/loader/source/ldr_ecs.cpp @@ -28,15 +28,41 @@ namespace ams::ldr::ecs { NON_COPYABLE(ExternalContentSource); NON_MOVEABLE(ExternalContentSource); private: + bool has_mounted = false; char device_name[DeviceNameSizeMax]; + os::ManagedHandle client; + + Result Mount() { + /* Create service. */ + Service srv; + serviceCreate(&srv, client.Move()); + FsFileSystem fs = { srv }; + auto fs_guard = SCOPE_GUARD { fsFsClose(&fs); }; + + /* Try to mount. */ + R_UNLESS(fsdevMountDevice(device_name, fs) >= 0, fs::ResultMountNameAlreadyExists()); + fs_guard.Cancel(); + + this->has_mounted = true; + return ResultSuccess(); + } public: - ExternalContentSource(const char *dn) { + ExternalContentSource(const char *dn, os::ManagedHandle client) : client(std::move(client)) { std::strncpy(this->device_name, dn, sizeof(this->device_name)); this->device_name[sizeof(this->device_name) - 1] = '\0'; } + Result EnsureMounted() { + if (!this->has_mounted) { + return Mount(); + } + return ResultSuccess(); + } + ~ExternalContentSource() { - fsdevUnmountDevice(this->device_name); + if (this->has_mounted) { + fsdevUnmountDevice(this->device_name); + } } const char *GetDeviceName() const { @@ -54,6 +80,10 @@ namespace ams::ldr::ecs { if (it == g_map.end()) { return nullptr; } + if (R_FAILED(it->second.EnsureMounted())) { + g_map.erase(it); + return nullptr; + } return it->second.GetDeviceName(); } @@ -72,18 +102,13 @@ namespace ams::ldr::ecs { os::ManagedHandle server, client; R_TRY(svcCreateSession(server.GetPointer(), client.GetPointer(), 0, 0)); - /* Create service. */ - Service srv; - serviceCreate(&srv, client.Move()); - FsFileSystem fs = { srv }; - auto fs_guard = SCOPE_GUARD { fsFsClose(&fs); }; - - /* Try to mount. */ - R_UNLESS(fsdevMountDevice(device_name, fs) >= 0, fs::ResultMountNameAlreadyExists()); - fs_guard.Cancel(); + /* Do not create service yet. */ + /* Defer until we've handed the server side back so we don't deadlock on querying pointer buffer size. */ /* Add to map. */ - g_map.emplace(static_cast(program_id), device_name); + g_map.emplace(std::piecewise_construct, + std::make_tuple(static_cast(program_id)), + std::make_tuple(device_name, std::move(client))); *out = server.Move(); return ResultSuccess(); }