Move the logic to mount the root VHD & basic filesystems to the service (#13726)

This commit is contained in:
Blue 2025-11-15 00:58:58 +00:00 committed by GitHub
parent d63fee34f2
commit 14257f99de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 61 additions and 146 deletions

View File

@ -1574,21 +1574,13 @@ int WslaShell(_In_ std::wstring_view commandLine)
wil::com_ptr<IWSLAVirtualMachine> virtualMachine;
WSLA_SESSION_SETTINGS sessionSettings{L"my-display-name"};
wil::com_ptr<IWSLASession> session;
settings.RootVhd = vhd.c_str();
settings.RootVhdType = fsType.c_str();
THROW_IF_FAILED(userSession->CreateSession(&sessionSettings, &settings, &session));
THROW_IF_FAILED(session->GetVirtualMachine(&virtualMachine));
wsl::windows::common::security::ConfigureForCOMImpersonation(userSession.get());
wil::unique_cotaskmem_ansistring diskDevice;
ULONG Lun{};
THROW_IF_FAILED(virtualMachine->AttachDisk(vhd.c_str(), true, &diskDevice, &Lun));
THROW_IF_FAILED(virtualMachine->Mount(diskDevice.get(), "/mnt", fsType.c_str(), "ro", WslMountFlagsChroot | WslMountFlagsWriteableOverlayFs));
THROW_IF_FAILED(virtualMachine->Mount(nullptr, "/dev", "devtmpfs", "", 0));
THROW_IF_FAILED(virtualMachine->Mount(nullptr, "/sys", "sysfs", "", 0));
THROW_IF_FAILED(virtualMachine->Mount(nullptr, "/proc", "proc", "", 0));
THROW_IF_FAILED(virtualMachine->Mount(nullptr, "/dev/pts", "devpts", "noatime,nosuid,noexec,gid=5,mode=620", 0));
wsl::windows::common::WSLAProcessLauncher launcher{shell, {shell}, {"TERM=xterm-256color"}, ProcessFlags::None};
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 0, .Type = WslFdTypeTerminalInput});
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 1, .Type = WslFdTypeTerminalOutput});

View File

@ -91,26 +91,6 @@ try
}
CATCH_RETURN();
HRESULT WslAttachDisk(WslVirtualMachineHandle VirtualMachine, const WslDiskAttachSettings* Settings, WslAttachedDiskInformation* AttachedDisk)
{
wil::unique_cotaskmem_ansistring device;
RETURN_IF_FAILED(reinterpret_cast<IWSLAVirtualMachine*>(VirtualMachine)
->AttachDisk(Settings->WindowsPath, Settings->ReadOnly, &device, &AttachedDisk->ScsiLun));
auto deviceSize = strlen(device.get());
WI_VERIFY(deviceSize < sizeof(WslAttachedDiskInformation::Device));
strncpy(AttachedDisk->Device, device.get(), sizeof(WslAttachedDiskInformation::Device));
return S_OK;
}
HRESULT WslMount(WslVirtualMachineHandle VirtualMachine, const WslMountSettings* Settings)
{
return reinterpret_cast<IWSLAVirtualMachine*>(VirtualMachine)
->Mount(Settings->Device, Settings->Target, Settings->Type, Settings->Options, Settings->Flags);
}
HRESULT WslCreateLinuxProcess(WslVirtualMachineHandle VirtualMachine, WslCreateProcessSettings* UserSettings, int32_t* Pid)
{
return S_OK;

View File

@ -96,14 +96,6 @@ struct WslDiskAttachSettings
bool ReadOnly;
};
struct WslAttachedDiskInformation
{
ULONG ScsiLun;
char Device[10];
};
HRESULT WslAttachDisk(WslVirtualMachineHandle VirtualMachine, const struct WslDiskAttachSettings* Settings, struct WslAttachedDiskInformation* AttachedDisk);
enum WslMountFlags
{
WslMountFlagsNone = 0,
@ -111,17 +103,6 @@ enum WslMountFlags
WslMountFlagsWriteableOverlayFs = 2,
};
struct WslMountSettings
{
const char* Device;
const char* Target;
const char* Type;
const char* Options;
uint32_t Flags;
};
HRESULT WslMount(WslVirtualMachineHandle VirtualMachine, const struct WslMountSettings* Settings);
enum WslFdType
{
WslFdTypeDefault = 0,

View File

@ -3,8 +3,6 @@ LIBRARY wslaclient
EXPORTS
WslGetVersion
WslCreateVirtualMachine
WslAttachDisk
WslMount
WslCreateLinuxProcess
WslWaitForLinuxProcess
WslSignalLinuxProcess

View File

@ -331,15 +331,8 @@ void WSLAVirtualMachine::Start()
#endif
wil::unique_cotaskmem_ansistring device;
ULONG lun{};
THROW_IF_FAILED(AttachDisk(kernelModulesPath.c_str(), true, &device, &lun));
THROW_HR_IF_MSG(
E_FAIL,
MountImpl(m_initChannel, device.get(), "", "ext4", "ro", WSLA_MOUNT::KernelModules) != 0,
"Failed to mount the kernel modules from: %hs",
device.get());
auto [_, device] = AttachDisk(kernelModulesPath.c_str(), true);
Mount(m_initChannel, device.c_str(), "", "ext4", "ro", WSLA_MOUNT::KernelModules);
// Configure GPU if requested.
if (m_settings.EnableGPU)
@ -358,7 +351,9 @@ void WSLAVirtualMachine::Start()
wsl::windows::common::hcs::ModifyComputeSystem(m_computeSystem.get(), wsl::shared::ToJsonW(gpuRequest).c_str());
}
auto [_, __, childChannel] = Fork(WSLA_FORK::Thread);
ConfigureMounts();
auto [__, ___, childChannel] = Fork(WSLA_FORK::Thread);
WSLA_WATCH_PROCESSES watchMessage{};
childChannel.SendMessage(watchMessage);
@ -368,6 +363,19 @@ void WSLAVirtualMachine::Start()
m_processExitThread = std::thread(std::bind(&WSLAVirtualMachine::WatchForExitedProcesses, this, std::move(childChannel)));
}
void WSLAVirtualMachine::ConfigureMounts()
{
auto [_, device] = AttachDisk(m_settings.RootVhd, true);
Mount(m_initChannel, device.c_str(), "/mnt", m_settings.RootVhdType, "ro", WslMountFlagsChroot | WslMountFlagsWriteableOverlayFs);
Mount(m_initChannel, nullptr, "/dev", "devtmpfs", "", 0);
Mount(m_initChannel, nullptr, "/sys", "sysfs", "", 0);
Mount(m_initChannel, nullptr, "/proc", "proc", "", 0);
Mount(m_initChannel, nullptr, "/dev/pts", "devpts", "noatime,nosuid,noexec,gid=5,mode=620", 0);
// TODO: Mount storage VHD here.
}
void WSLAVirtualMachine::WatchForExitedProcesses(wsl::shared::SocketChannel& Channel)
try
{
@ -566,10 +574,11 @@ void WSLAVirtualMachine::OnCrash(_In_ const HCS_EVENT* Event)
}
}
HRESULT WSLAVirtualMachine::AttachDisk(_In_ PCWSTR Path, _In_ BOOL ReadOnly, _Out_ LPSTR* Device, _Out_ ULONG* Lun)
try
std::pair<ULONG, std::string> WSLAVirtualMachine::AttachDisk(_In_ PCWSTR Path, _In_ BOOL ReadOnly)
{
*Device = nullptr;
ULONG Lun{};
std::string Device;
auto result = wil::ResultFromException([&]() {
std::lock_guard lock{m_lock};
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_running);
@ -588,17 +597,16 @@ try
grantDiskAccess();
}
*Lun = 0;
while (m_attachedDisks.find(*Lun) != m_attachedDisks.end())
while (m_attachedDisks.find(Lun) != m_attachedDisks.end())
{
(*Lun)++;
Lun++;
}
bool vhdAdded = false;
auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
if (vhdAdded)
{
wsl::windows::common::hcs::RemoveScsiDisk(m_computeSystem.get(), *Lun);
wsl::windows::common::hcs::RemoveScsiDisk(m_computeSystem.get(), Lun);
}
if (disk.AccessGranted)
@ -608,12 +616,12 @@ try
});
auto result =
wil::ResultFromException([&]() { wsl::windows::common::hcs::AddVhd(m_computeSystem.get(), Path, *Lun, ReadOnly); });
wil::ResultFromException([&]() { wsl::windows::common::hcs::AddVhd(m_computeSystem.get(), Path, Lun, ReadOnly); });
if (result == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) && !disk.AccessGranted)
{
grantDiskAccess();
wsl::windows::common::hcs::AddVhd(m_computeSystem.get(), Path, *Lun, ReadOnly);
wsl::windows::common::hcs::AddVhd(m_computeSystem.get(), Path, Lun, ReadOnly);
}
else
{
@ -625,7 +633,7 @@ try
WSLA_GET_DISK message{};
message.Header.MessageSize = sizeof(message);
message.Header.MessageType = WSLA_GET_DISK::Type;
message.ScsiLun = *Lun;
message.ScsiLun = Lun;
const auto& response = m_initChannel.Transaction(message);
THROW_HR_IF_MSG(E_FAIL, response.Result != 0, "Failed to attach disk, init returned: %lu", response.Result);
@ -633,35 +641,19 @@ try
cleanup.release();
disk.Device = response.Buffer;
m_attachedDisks.emplace(*Lun, std::move(disk));
*Device = wil::make_unique_ansistring<wil::unique_cotaskmem_ansistring>(response.Buffer).release();
Device = disk.Device;
m_attachedDisks.emplace(Lun, std::move(disk));
});
WSL_LOG(
"WSLAAttachDisk",
TraceLoggingValue(Path, "Path"),
TraceLoggingValue(ReadOnly, "ReadOnly"),
TraceLoggingValue(*Device == nullptr ? "<null>" : *Device, "Device"),
TraceLoggingValue(Device.c_str(), "Device"),
TraceLoggingValue(result, "Result"));
return result;
return {Lun, Device};
}
CATCH_RETURN();
HRESULT WSLAVirtualMachine::Mount(_In_ LPCSTR Source, _In_ LPCSTR Target, _In_ LPCSTR Type, _In_ LPCSTR Options, _In_ ULONG Flags)
try
{
THROW_HR_IF(E_INVALIDARG, WI_IsAnyFlagSet(Flags, ~(WslMountFlagsChroot | WslMountFlagsWriteableOverlayFs)));
std::lock_guard lock{m_lock};
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_running);
THROW_HR_IF(E_FAIL, MountImpl(m_initChannel, Source, Target, Type, Options, Flags) != 0);
return S_OK;
}
CATCH_RETURN();
HRESULT WSLAVirtualMachine::Unmount(_In_ const char* Path)
try
@ -893,7 +885,7 @@ Microsoft::WRL::ComPtr<WSLAProcess> WSLAVirtualMachine::CreateLinuxProcessImpl(
return process;
}
int32_t WSLAVirtualMachine::MountImpl(shared::SocketChannel& Channel, LPCSTR Source, LPCSTR Target, LPCSTR Type, LPCSTR Options, ULONG Flags)
void WSLAVirtualMachine::Mount(shared::SocketChannel& Channel, LPCSTR Source, LPCSTR Target, LPCSTR Type, LPCSTR Options, ULONG Flags)
{
static_assert(WslMountFlagsNone == WSLA_MOUNT::None);
static_assert(WslMountFlagsChroot == WSLA_MOUNT::Chroot);
@ -925,7 +917,7 @@ int32_t WSLAVirtualMachine::MountImpl(shared::SocketChannel& Channel, LPCSTR Sou
TraceLoggingValue(Flags, "Flags"),
TraceLoggingValue(response.Result, "Result"));
return response.Result;
THROW_HR_IF(E_FAIL, response.Result != 0);
}
int32_t WSLAVirtualMachine::ExpectClosedChannelOrError(wsl::shared::SocketChannel& Channel)
@ -1175,7 +1167,7 @@ try
auto mountOptions =
std::format("msize={},trans=fd,rfdno={},wfdno={},aname={},cache=mmap", LX_INIT_UTILITY_VM_PLAN9_BUFFER_SIZE, fd, fd, shareNameUtf8);
THROW_HR_IF(E_FAIL, MountImpl(channel, shareNameUtf8.c_str(), LinuxPath, "9p", mountOptions.c_str(), Flags) != 0);
Mount(channel, shareNameUtf8.c_str(), LinuxPath, "9p", mountOptions.c_str(), Flags);
deleteOnFailure.release();
return S_OK;
@ -1250,7 +1242,7 @@ try
options += ":" + inboxLibMountPoint.value();
}
RETURN_IF_FAILED(Mount("none", LibrariesMountPoint, "overlay", options.c_str(), Flags));
Mount(m_initChannel, "none", LibrariesMountPoint, "overlay", options.c_str(), Flags);
return S_OK;
}
CATCH_RETURN();

View File

@ -36,8 +36,6 @@ public:
void Start();
void OnSessionTerminating();
IFACEMETHOD(AttachDisk(_In_ PCWSTR Path, _In_ BOOL ReadOnly, _Out_ LPSTR* Device, _Out_ ULONG* Lun)) override;
IFACEMETHOD(Mount(_In_ LPCSTR Source, _In_ LPCSTR Target, _In_ LPCSTR Type, _In_ LPCSTR Options, _In_ ULONG Flags)) override;
IFACEMETHOD(CreateLinuxProcess(_In_ const WSLA_PROCESS_OPTIONS* Options, _Out_ IWSLAProcess** Process, _Out_ int* Errno)) override;
IFACEMETHOD(WaitPid(_In_ LONG Pid, _In_ ULONGLONG TimeoutMs, _Out_ ULONG* State, _Out_ int* Code)) override;
IFACEMETHOD(Signal(_In_ LONG Pid, _In_ int Signal)) override;
@ -62,12 +60,15 @@ private:
using TPrepareCommandLine = std::function<void(const std::vector<ConnectedSocket>&)>;
static int32_t MountImpl(wsl::shared::SocketChannel& Channel, LPCSTR Source, _In_ LPCSTR Target, _In_ LPCSTR Type, _In_ LPCSTR Options, _In_ ULONG Flags);
std::pair<ULONG, std::string> AttachDisk(_In_ PCWSTR Path, _In_ BOOL ReadOnly);
static void Mount(wsl::shared::SocketChannel& Channel, LPCSTR Source, _In_ LPCSTR Target, _In_ LPCSTR Type, _In_ LPCSTR Options, _In_ ULONG Flags);
static void CALLBACK s_OnExit(_In_ HCS_EVENT* Event, _In_opt_ void* Context);
static bool ParseTtyInformation(
const WSLA_PROCESS_FD* Fds, ULONG FdCount, const WSLA_PROCESS_FD** TtyInput, const WSLA_PROCESS_FD** TtyOutput, const WSLA_PROCESS_FD** TtyControl);
void ConfigureNetworking();
void ConfigureMounts();
void OnExit(_In_ const HCS_EVENT* Event);
void OnCrash(_In_ const HCS_EVENT* Event);

View File

@ -166,8 +166,6 @@ interface IWSLAProcess : IUnknown
]
interface IWSLAVirtualMachine : IUnknown
{
HRESULT AttachDisk([in] LPCWSTR Path, [in] BOOL ReadOnly, [out] LPSTR* Device, [out] ULONG* Lun);
HRESULT Mount([in, unique] LPCSTR Source, [in] LPCSTR Target, [in] LPCSTR Type, [in] LPCSTR Options, [in] ULONG Flags);
HRESULT CreateLinuxProcess([in] const struct WSLA_PROCESS_OPTIONS* Options, [out] IWSLAProcess** Process, [out] int* Errno);
HRESULT WaitPid([in] LONG Pid, [in] ULONGLONG TimeoutMs, [out] ULONG* State, [out] int* Code);
HRESULT Signal([in] LONG Pid, [in] int Signal);
@ -194,6 +192,8 @@ struct _VIRTUAL_MACHINE_SETTINGS { // TODO: Delete once the new API is wired.
BOOL EnableDebugShell;
BOOL EnableEarlyBootDmesg;
BOOL EnableGPU;
LPCWSTR RootVhd; // Temporary option to provide the root VHD. TODO: Remove once runtime VHD is available.
LPCSTR RootVhdType; // Temporary option to provide the root VHD. TODO: Remove once runtime VHD is available.
} VIRTUAL_MACHINE_SETTINGS;

View File

@ -52,8 +52,10 @@ class WSLATests
return true;
}
wil::com_ptr<IWSLASession> CreateSession(const VIRTUAL_MACHINE_SETTINGS& vmSettings, const std::optional<std::wstring>& vhd = {})
wil::com_ptr<IWSLASession> CreateSession(VIRTUAL_MACHINE_SETTINGS& vmSettings)
{
vmSettings.RootVhdType = "ext4";
wil::com_ptr<IWSLAUserSession> userSession;
VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(WSLAUserSession), nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&userSession)));
wsl::windows::common::security::ConfigureForCOMImpersonation(userSession.get());
@ -62,23 +64,6 @@ class WSLATests
wil::com_ptr<IWSLASession> session;
VERIFY_SUCCEEDED(userSession->CreateSession(&settings, &vmSettings, &session));
// TODO: remove once the VM is wired to mount its rootfs inside WSLASession
wil::com_ptr<IWSLAVirtualMachine> virtualMachine;
VERIFY_SUCCEEDED(session->GetVirtualMachine(&virtualMachine));
wsl::windows::common::security::ConfigureForCOMImpersonation(virtualMachine.get());
wil::unique_cotaskmem_ansistring diskDevice;
ULONG Lun{};
THROW_IF_FAILED(virtualMachine->AttachDisk(vhd.value_or(testVhd).c_str(), true, &diskDevice, &Lun));
THROW_IF_FAILED(virtualMachine->Mount(diskDevice.get(), "/mnt", "ext4", "ro", WslMountFlagsChroot | WslMountFlagsWriteableOverlayFs));
THROW_IF_FAILED(virtualMachine->Mount(nullptr, "/dev", "devtmpfs", "", 0));
THROW_IF_FAILED(virtualMachine->Mount(nullptr, "/sys", "sysfs", "", 0));
THROW_IF_FAILED(virtualMachine->Mount(nullptr, "/proc", "proc", "", 0));
THROW_IF_FAILED(virtualMachine->Mount(nullptr, "/dev/pts", "devpts", "noatime,nosuid,noexec,gid=5,mode=620", 0));
return session;
}
@ -159,6 +144,7 @@ class WSLATests
settings.BootTimeoutMs = 30 * 1000;
settings.DmesgOutput = (ULONG) reinterpret_cast<ULONG_PTR>(write.get());
settings.EnableEarlyBootDmesg = earlyBootLogging;
settings.RootVhd = testVhd.c_str();
std::vector<char> dmesgContent;
auto readDmesg = [read = read.get(), &dmesgContent]() mutable {
@ -275,6 +261,7 @@ class WSLATests
settings.DisplayName = L"WSLA";
settings.MemoryMb = 2048;
settings.BootTimeoutMs = 30 * 1000;
settings.RootVhd = testVhd.c_str();
auto session = CreateSession(settings);
@ -330,6 +317,7 @@ class WSLATests
settings.MemoryMb = 2048;
settings.BootTimeoutMs = 30 * 1000;
settings.NetworkingMode = WslNetworkingModeNAT;
settings.RootVhd = testVhd.c_str();
auto session = CreateSession(settings);
@ -355,6 +343,7 @@ class WSLATests
settings.BootTimeoutMs = 30 * 1000;
settings.NetworkingMode = WslNetworkingModeNAT;
settings.EnableDnsTunneling = true;
settings.RootVhd = testVhd.c_str();
auto session = CreateSession(settings);
@ -381,6 +370,7 @@ class WSLATests
settings.DisplayName = L"WSLA";
settings.MemoryMb = 2048;
settings.BootTimeoutMs = 30 * 1000;
settings.RootVhd = testVhd.c_str();
auto session = CreateSession(settings);
@ -493,6 +483,7 @@ class WSLATests
settings.MemoryMb = 2048;
settings.BootTimeoutMs = 30 * 1000;
settings.NetworkingMode = WslNetworkingModeNAT;
settings.RootVhd = testVhd.c_str();
auto session = CreateSession(settings);
@ -637,6 +628,7 @@ class WSLATests
settings.DisplayName = L"WSLA";
settings.MemoryMb = 2048;
settings.BootTimeoutMs = 30 * 1000;
settings.RootVhd = testVhd.c_str();
auto session = CreateSession(settings);
@ -656,6 +648,7 @@ class WSLATests
settings.DisplayName = L"WSLA";
settings.MemoryMb = 2048;
settings.BootTimeoutMs = 30 * 1000;
settings.RootVhd = testVhd.c_str();
auto session = CreateSession(settings);
@ -742,6 +735,7 @@ class WSLATests
settings.DisplayName = L"WSLA";
settings.MemoryMb = 2048;
settings.BootTimeoutMs = 30 * 1000;
settings.RootVhd = testVhd.c_str();
auto session = CreateSession(settings);
auto result = ExpectCommandResult(
@ -856,6 +850,7 @@ class WSLATests
settings.DisplayName = L"WSLA";
settings.MemoryMb = 2048;
settings.BootTimeoutMs = 30 * 1000;
settings.RootVhd = testVhd.c_str();
// Use the system distro vhd for modprobe & lsmod.
@ -867,7 +862,9 @@ class WSLATests
auto rootfs = std::filesystem::path(wsl::windows::common::wslutil::GetMsiPackagePath().value()) / L"system.vhd";
#endif
auto session = CreateSession(settings, rootfs);
settings.RootVhd = rootfs.c_str();
auto session = CreateSession(settings);
// Sanity check.
ExpectCommandResult(session.get(), {"/bin/bash", "-c", "lsmod | grep ^xsk_diag"}, 1);
@ -879,33 +876,6 @@ class WSLATests
ExpectCommandResult(session.get(), {"/bin/bash", "-c", "lsmod | grep ^xsk_diag"}, 0);
}
TEST_METHOD(CreateSessionSmokeTest)
{
WSL2_TEST_ONLY();
wil::com_ptr<IWSLAUserSession> userSession;
VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(WSLAUserSession), nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&userSession)));
wsl::windows::common::security::ConfigureForCOMImpersonation(userSession.get());
WSLA_SESSION_SETTINGS settings{L"my-display-name"};
wil::com_ptr<IWSLASession> session;
VIRTUAL_MACHINE_SETTINGS vmSettings{};
vmSettings.BootTimeoutMs = 30 * 1000;
vmSettings.DisplayName = L"WSLA";
vmSettings.MemoryMb = 2048;
vmSettings.CpuCount = 4;
vmSettings.NetworkingMode = WslNetworkingModeNone;
vmSettings.EnableDebugShell = true;
VERIFY_SUCCEEDED(userSession->CreateSession(&settings, &vmSettings, &session));
wil::unique_cotaskmem_string returnedDisplayName;
VERIFY_SUCCEEDED(session->GetDisplayName(&returnedDisplayName));
VERIFY_ARE_EQUAL(returnedDisplayName.get(), std::wstring(L"my-display-name"));
}
TEST_METHOD(CreateRootNamespaceProcess)
{
WSL2_TEST_ONLY();
@ -915,6 +885,7 @@ class WSLATests
settings.DisplayName = L"WSLA";
settings.MemoryMb = 2048;
settings.BootTimeoutMs = 30 * 1000;
settings.RootVhd = testVhd.c_str();
auto session = CreateSession(settings);