Fix: Update plan9 registration with the new default uid after OOBE (#40977)

Currently, after a fresh distro installation, operations in the linux plan9 share will use the uid 0. Because the uid was cached before the OOBE is complete. And this only recovers after a distro termination. This PR updates the plan9 registration after the OOBE completes with the new default uid.

Feng Wang committed Aug 7, 2026 at 11:36 UTC 70f6890c83bb5e56b8ea17abdbb834b55e92dc80
5 files changed +55 -9
src/windows/common/Redirector.cpp
+29 -8
@@ -226,14 +226,14 @@ void ConnectionTargetManager::AddConnectionTarget(
226 if (!Contains(security.LogonId))
227 {
228 redirector::AddConnectionTarget(m_name, security.LogonId, aname, uid, unixSocketPath, instanceId, port);
229 - m_logonIds.push_back(security.LogonId);
229 + m_targets.emplace_back(security.LogonId, std::string(aname), uid, std::wstring(unixSocketPath), instanceId, port);
230 }
231
232 // Checking the list also catches the case where the logon ID and linked logon ID are equal.
233 if (!Contains(security.LinkedLogonId))
234 {
235 redirector::AddConnectionTarget(m_name, security.LinkedLogonId, aname, uid, unixSocketPath, instanceId, port);
236 - m_logonIds.push_back(security.LinkedLogonId);
236 + m_targets.emplace_back(security.LinkedLogonId, std::string(aname), uid, std::wstring(unixSocketPath), instanceId, port);
237 }
238 }
239
@@ -241,20 +241,41 @@ void ConnectionTargetManager::AddConnectionTarget(
241 void ConnectionTargetManager::RemoveAll()
242 {
243 auto lock = m_lock.lock_exclusive();
244 - for (const auto logonId : m_logonIds)
244 + for (const auto& target : m_targets)
245 {
246 - RemoveConnectionTarget(m_name, logonId);
246 + RemoveConnectionTarget(m_name, target.logonId);
247 }
248
249 - m_logonIds.clear();
249 + m_targets.clear();
250 }
251
252 -// Checks whether the list of logon IDs contains the specified ID.
252 +// Checks whether the list of targets contains the specified ID.
253 bool ConnectionTargetManager::Contains(LUID luid) const
254 {
255 - const auto it = std::find_if(m_logonIds.begin(), m_logonIds.end(), [&luid](auto& item) { return RtlEqualLuid(&luid, &item); });
255 + const auto it =
256 + std::find_if(m_targets.begin(), m_targets.end(), [&luid](auto& item) { return RtlEqualLuid(&luid, &item.logonId); });
257
257 - return it != m_logonIds.end();
258 + return it != m_targets.end();
259 +}
260 +
261 +// Re-add all connection targets with the new UID.
262 +void ConnectionTargetManager::UpdateUid(LX_UID_T uid)
263 +{
264 + auto lock = m_lock.lock_exclusive();
265 +
266 + for (auto& target : m_targets)
267 + {
268 + if (target.uid == uid)
269 + {
270 + continue;
271 + }
272 +
273 + redirector::RemoveConnectionTarget(m_name, target.logonId);
274 + redirector::AddConnectionTarget(
275 + m_name, target.logonId, target.aname, uid, target.unixSocketPath, target.instanceId, target.port);
276 +
277 + target.uid = uid;
278 + }
279 }
280
281 } // namespace wsl::windows::common::redirector
src/windows/common/Redirector.h
+13 -1
@@ -26,11 +26,23 @@ public:
26
27 void RemoveAll();
28
29 + void UpdateUid(LX_UID_T uid);
30 +
31 private:
32 + struct ConnectionTarget
33 + {
34 + LUID logonId;
35 + std::string aname;
36 + LX_UID_T uid;
37 + std::wstring unixSocketPath;
38 + GUID instanceId;
39 + ULONG port;
40 + };
41 +
42 bool Contains(LUID luid) const;
43
44 std::wstring_view m_name;
33 - std::vector<LUID> m_logonIds;
45 + std::vector<ConnectionTarget> m_targets;
46 wil::srwlock m_lock;
47 };
48
src/windows/service/exe/LxssInstance.cpp
+2
@@ -577,6 +577,8 @@ wil::unique_handle LxssInstance::_CreateLxProcess(
577 registration.Write(wsl::windows::service::Property::DefaultUid, static_cast<int>(OobeResult->DefaultUid));
578 m_defaultUid = static_cast<int>(OobeResult->DefaultUid);
579 }
580 +
581 + m_redirectorConnectionTargets.UpdateUid(m_defaultUid);
582 }
583 }
584 CATCH_LOG()
src/windows/service/exe/WslCoreInstance.cpp
+2
@@ -309,6 +309,8 @@ void WslCoreInstance::ReadOOBEResult(wil::unique_socket&& Socket, wsl::windows::
309 registration.Write(wsl::windows::service::Property::DefaultUid, static_cast<int>(oobeResult->DefaultUid));
310 m_defaultUid = static_cast<int>(oobeResult->DefaultUid);
311 }
312 +
313 + m_redirectorConnectionTargets.UpdateUid(m_defaultUid);
314 }
315 }
316
test/windows/UnitTests.cpp
+9
@@ -4603,6 +4603,15 @@ VERSION_ID="Invalid|Format"
4603 // Validate that DefaultUid was set
4604 validateOutput(L"id -u", L"1010\n");
4605 VERIFY_ARE_EQUAL(defaultUid.Get(), 1010);
4606 +
4607 + // New file should be created with the correct uid.
4608 + const std::wstring testFilePathLinux = L"/tmp/oobe_file_test";
4609 + const std::wstring testFilePathWindows = L"\\\\wsl.localhost\\" LXSS_DISTRO_NAME_TEST_L L"\\tmp\\oobe_file_test";
4610 +
4611 + const wil::unique_hfile file(CreateFile(
4612 + testFilePathWindows.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
4613 + VERIFY_IS_TRUE(file.is_valid());
4614 + validateOutput(std::format(L"stat -c %u {}", testFilePathLinux).c_str(), L"1010\n");
4615 }
4616
4617 // Verify that the default UID isn't changed if it's not present in wsl-distribution.conf.