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.
This commit is contained in:
misson20000 2019-12-12 22:56:50 -08:00 committed by SciresM
parent 0a18c63f24
commit f1a9dd8a98

View file

@ -28,15 +28,41 @@ namespace ams::ldr::ecs {
NON_COPYABLE(ExternalContentSource); NON_COPYABLE(ExternalContentSource);
NON_MOVEABLE(ExternalContentSource); NON_MOVEABLE(ExternalContentSource);
private: private:
bool has_mounted = false;
char device_name[DeviceNameSizeMax]; 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: 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)); std::strncpy(this->device_name, dn, sizeof(this->device_name));
this->device_name[sizeof(this->device_name) - 1] = '\0'; this->device_name[sizeof(this->device_name) - 1] = '\0';
} }
Result EnsureMounted() {
if (!this->has_mounted) {
return Mount();
}
return ResultSuccess();
}
~ExternalContentSource() { ~ExternalContentSource() {
fsdevUnmountDevice(this->device_name); if (this->has_mounted) {
fsdevUnmountDevice(this->device_name);
}
} }
const char *GetDeviceName() const { const char *GetDeviceName() const {
@ -54,6 +80,10 @@ namespace ams::ldr::ecs {
if (it == g_map.end()) { if (it == g_map.end()) {
return nullptr; return nullptr;
} }
if (R_FAILED(it->second.EnsureMounted())) {
g_map.erase(it);
return nullptr;
}
return it->second.GetDeviceName(); return it->second.GetDeviceName();
} }
@ -72,18 +102,13 @@ namespace ams::ldr::ecs {
os::ManagedHandle server, client; os::ManagedHandle server, client;
R_TRY(svcCreateSession(server.GetPointer(), client.GetPointer(), 0, 0)); R_TRY(svcCreateSession(server.GetPointer(), client.GetPointer(), 0, 0));
/* Create service. */ /* Do not create service yet. */
Service srv; /* Defer until we've handed the server side back so we don't deadlock on querying pointer buffer size. */
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();
/* Add to map. */ /* Add to map. */
g_map.emplace(static_cast<u64>(program_id), device_name); g_map.emplace(std::piecewise_construct,
std::make_tuple(static_cast<u64>(program_id)),
std::make_tuple(device_name, std::move(client)));
*out = server.Move(); *out = server.Move();
return ResultSuccess(); return ResultSuccess();
} }