diff --git a/src/linux/init/init.cpp b/src/linux/init/init.cpp index 5bd4c793e..531166b44 100644 --- a/src/linux/init/init.cpp +++ b/src/linux/init/init.cpp @@ -344,6 +344,11 @@ int GenerateSystemdUnits(int Argc, char** Argv) File.reset(); } + if (automountRoot.empty() || automountRoot.back() != '/') + { + automountRoot += '/'; + } + // Mask systemd-networkd-wait-online.service since WSL always ensures that networking is configured during boot. // That unit can cause systemd boot timeouts since WSL's network interface is unmanaged by systemd. THROW_LAST_ERROR_IF(symlink("/dev/null", std::format("{}/systemd-networkd-wait-online.service", installPath).c_str()) < 0); @@ -355,6 +360,24 @@ int GenerateSystemdUnits(int Argc, char** Argv) // When multiple distros are running, the second distro's getty fails because the tty is already held. THROW_LAST_ERROR_IF(symlink("/dev/null", std::format("{}/console-getty.service", installPath).c_str()) < 0); + const auto sharedMountPath = automountRoot + "wsl"; + const auto mountGuardUnitContent = std::format( + R"(# Note: This file is generated by WSL to prevent shutdown unmounts from propagating to other distributions. + +[Unit] +Description=WSL Cross-Distribution Mount Guard +After=local-fs.target remote-fs.target +ConditionPathIsMountPoint={} + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/bin/true +ExecStop=-/bin/mount --make-rslave {})", + sharedMountPath, + sharedMountPath); + InstallSystemdUnit(installPath, "wsl-mnt-guard", mountGuardUnitContent.c_str()); + // Only create the wslg unit if both enabled in wsl.conf, and if the wslg folder actually exists. if (enableGuiApps && access("/mnt/wslg/runtime-dir", F_OK) == 0) { diff --git a/test/windows/UnitTests.cpp b/test/windows/UnitTests.cpp index 43d26041f..9f920c930 100644 --- a/test/windows/UnitTests.cpp +++ b/test/windows/UnitTests.cpp @@ -516,6 +516,49 @@ class UnitTests } } + WSL2_TEST_METHOD(SharedMountSurvivesDistroTermination) + { + constexpr auto peerDistroName = L"mount-guard-peer-test"; + + auto validate = [&](const std::string& automountRoot) { + const auto extraConfig = automountRoot.empty() ? "" : std::format("[automount]\nroot={}\n", automountRoot); + const auto effectiveAutomountRoot = automountRoot.empty() ? "/mnt" : automountRoot; + const auto mountPoint = std::format(L"{}/wsl/mount-guard-test", wsl::shared::string::MultiByteToWide(effectiveAutomountRoot)); + + auto cleanupVm = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { WslShutdown(); }); + auto cleanupSystemd = EnableSystemd(extraConfig); + + LxsstuLaunchWsl(std::format(L"--unregister {}", peerDistroName)); + VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L"--import {} . \"{}\" --version 2", peerDistroName, g_testDistroPath)), 0L); + auto cleanupPeer = wil::scope_exit_log( + WI_DIAGNOSTICS_INFO, [&]() { LxsstuLaunchWsl(std::format(L"--unregister {}", peerDistroName)); }); + + auto cleanupPeerSystemd = EnableSystemd(extraConfig, peerDistroName); + + VERIFY_ARE_EQUAL( + LxsstuLaunchWsl(std::format(L"-d {} -- sh -c \"systemctl is-system-running | grep -Eq 'running|degraded'\"", peerDistroName)), 0L); + + VERIFY_ARE_EQUAL( + LxsstuLaunchWsl(std::format( + L"sh -c 'mkdir -p {0} && mount -t tmpfs -o size=4M mount-guard-test {0} && echo survived > {0}/marker'", mountPoint)), + 0L); + auto cleanupMount = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { + LxsstuLaunchWsl(std::format(L"sh -c 'umount {0} 2>/dev/null || true; rmdir {0} 2>/dev/null || true'", mountPoint)); + }); + + VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L"-d {} -- findmnt -n {}", peerDistroName, mountPoint)), 0L); + VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L"-d {} -- grep -qx survived {}/marker", peerDistroName, mountPoint)), 0L); + + TerminateDistribution(peerDistroName); + + VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L"findmnt -n {}", mountPoint)), 0L); + VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L"grep -qx survived {}/marker", mountPoint)), 0L); + }; + + validate(""); + validate("/wsl-test-mount"); + } + WSL2_TEST_METHOD(ConfigUpdateLanguage) { // Validates that init populates $LANG from the distro locale configuration file.