Merge remote-tracking branch 'origin/feature/wsl-for-apps' into user/ptrivedi/create-cont

This commit is contained in:
Blue 2025-11-26 13:59:42 -08:00
commit e4822ba4fe
38 changed files with 462 additions and 3152 deletions

1
.gitignore vendored
View File

@ -6,6 +6,7 @@ tmp
/.vs/
/.vscode/
*.sln
*.slnx
*.user
*.csproj
*.vcxproj

1368
NOTICE.txt

File diff suppressed because it is too large Load Diff

View File

@ -151,12 +151,12 @@
"FriendlyName": "AlmaLinux OS 10",
"Default": true,
"Amd64Url": {
"Url": "https://github.com/AlmaLinux/wsl-images/releases/download/v10.0.20250529.0/AlmaLinux-10.0_x64_20250529.0.wsl",
"Sha256": "6775711048b86743588da7173ab45ca449b5c50a72fb87635313f059a9813d4b"
"Url": "https://github.com/AlmaLinux/wsl-images/releases/download/v10.1.20251124.0/AlmaLinux-10.1_x64_20251124.0.wsl",
"Sha256": "24e8fa286a4081979d97e83a227fb89f332bcf731fe4b422679a3b455ab0be37"
},
"Arm64Url": {
"Url": "https://github.com/AlmaLinux/wsl-images/releases/download/v10.0.20250529.0/AlmaLinux-10.0_ARM64_20250529.0.wsl",
"Sha256": "bc424bd9f954d36e871d4d874d35bc25e3ea7bdfe48337c30b927ed73a79b2fa"
"Url": "https://github.com/AlmaLinux/wsl-images/releases/download/v10.1.20251124.0/AlmaLinux-10.1_ARM64_20251124.0.wsl",
"Sha256": "20700a4467214074f8a1a3d4e0e1cad25af36b8127d047ab6d5b4a1355e998b8"
}
}
],

View File

@ -6,15 +6,15 @@
<package id="Microsoft.Direct3D.Linux" version="1.611.1-81528511" targetFramework="native" />
<package id="Microsoft.DXCore.Linux.amd64fre" version="10.0.26100.1-240331-1435.ge-release" targetFramework="native" />
<package id="Microsoft.DXCore.Linux.arm64fre" version="10.0.26100.1-240331-1435.ge-release" targetFramework="native" />
<package id="Microsoft.Extensions.Hosting" version="9.0.8" />
<package id="Microsoft.Extensions.Hosting" version="10.0.0" />
<package id="Microsoft.Identity.MSAL.WSL.Proxy" version="0.1.1" />
<package id="Microsoft.NETCore.App.Runtime.win-arm64" version="9.0.10" />
<package id="Microsoft.NETCore.App.Runtime.win-x64" version="9.0.10" />
<package id="Microsoft.RemoteDesktop.Client.MSRDC.SessionHost" version="1.2.6353" />
<package id="Microsoft.Taef" version="10.97.250317001" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.250325.1" targetFramework="native" />
<package id="Microsoft.Windows.SDK.NET.Ref" version="10.0.26100.75" />
<package id="Microsoft.WindowsAppSDK" version="1.7.250606001" />
<package id="Microsoft.NETCore.App.Runtime.win-arm64" version="10.0.0" />
<package id="Microsoft.NETCore.App.Runtime.win-x64" version="10.0.0" />
<package id="Microsoft.RemoteDesktop.Client.MSRDC.SessionHost" version="1.2.6676" />
<package id="Microsoft.Taef" version="10.100.251104001" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.251108.1" targetFramework="native" />
<package id="Microsoft.Windows.SDK.NET.Ref" version="10.0.26100.81" />
<package id="Microsoft.WindowsAppSDK" version="1.8.251106002" />
<package id="Microsoft.WSL.bsdtar" version="0.0.2-2" />
<package id="Microsoft.WSL.Dependencies.amd64fre" version="10.0.27820.1000-250318-1700.rs-base2-hyp" targetFramework="native" />
<package id="Microsoft.WSL.Dependencies.arm64fre" version="10.0.27820.1000-250318-1700.rs-base2-hyp" targetFramework="native" />
@ -27,8 +27,8 @@
<package id="Microsoft.WSL.WSLATestDistro" version="0.1.0" />
<package id="Microsoft.WSLg" version="1.0.71" />
<package id="Microsoft.Xaml.Behaviors.WinUI.Managed" version="3.0.0" />
<package id="StrawberryPerl" version="5.28.0.1" />
<package id="StrawberryPerl" version="5.32.1.1" />
<package id="vswhere" version="3.1.7" />
<package id="WinUIEx" version="2.5.1" />
<package id="WinUIEx" version="2.9.0" />
<package id="Wix" version="5.0.2" />
</packages>

View File

@ -1666,10 +1666,8 @@ struct WSLA_TTY_RELAY
int32_t TtyInput;
int32_t TtyOutput;
int32_t TtyControl;
uint32_t Rows;
uint32_t Columns;
PRETTY_PRINT(FIELD(Header), FIELD(TtyMaster), FIELD(TtyInput), FIELD(TtyOutput), FIELD(TtyControl), FIELD(Rows), FIELD(Columns));
PRETTY_PRINT(FIELD(Header), FIELD(TtyMaster), FIELD(TtyInput), FIELD(TtyOutput), FIELD(TtyControl));
};
struct WSLA_ACCEPT

View File

@ -87,6 +87,9 @@ set(HEADERS
hcs.hpp
hcs_schema.h
helpers.hpp
interop.hpp
ExecutionContext.h
socket.hpp
hvsocket.hpp
INetworkingEngine.h
interop.hpp

View File

@ -48,6 +48,12 @@ void WSLAProcessLauncher::AddFd(WSLA_PROCESS_FD Fd)
m_fds.push_back(Fd);
}
void WSLAProcessLauncher::SetTtySize(ULONG Rows, ULONG Columns)
{
m_rows = Rows;
m_columns = Columns;
}
std::tuple<WSLA_PROCESS_OPTIONS, std::vector<const char*>, std::vector<const char*>> WSLAProcessLauncher::CreateProcessOptions()
{
std::vector<const char*> commandLine;
@ -64,6 +70,8 @@ std::tuple<WSLA_PROCESS_OPTIONS, std::vector<const char*>, std::vector<const cha
options.FdsCount = static_cast<DWORD>(m_fds.size());
options.Environment = environment.data();
options.EnvironmentCount = static_cast<DWORD>(environment.size());
options.TtyColumns = m_columns;
options.TtyRows = m_rows;
return std::make_tuple(options, std::move(commandLine), std::move(environment));
}

View File

@ -87,6 +87,7 @@ public:
ProcessFlags Flags = ProcessFlags::Stdout | ProcessFlags::Stderr);
void AddFd(WSLA_PROCESS_FD Fd);
void SetTtySize(ULONG Rows, ULONG Columns);
// TODO: Add overloads for IWSLAContainer once implemented.
ClientRunningWSLAProcess Launch(IWSLASession& Session);
@ -100,6 +101,8 @@ protected:
std::string m_executable;
std::vector<std::string> m_arguments;
std::vector<std::string> m_environment;
DWORD m_rows = 0;
DWORD m_columns = 0;
};
} // namespace wsl::windows::common

View File

@ -1620,6 +1620,19 @@ int WslaShell(_In_ std::wstring_view commandLine)
std::optional<wil::com_ptr<IWSLAContainer>> container;
std::optional<wsl::windows::common::ClientRunningWSLAProcess> process;
// Get the terminal size.
HANDLE Stdout = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE Stdin = GetStdHandle(STD_INPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFOEX Info{};
Info.cbSize = sizeof(Info);
THROW_IF_WIN32_BOOL_FALSE(::GetConsoleScreenBufferInfoEx(Stdout, &Info));
wsl::windows::common::WSLAProcessLauncher launcher{shell, {shell}, {"TERM=xterm-256color"}, ProcessFlags::None};
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 0, .Type = WSLAFdTypeTerminalInput});
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 1, .Type = WSLAFdTypeTerminalOutput});
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 2, .Type = WSLAFdTypeTerminalControl});
launcher.SetTtySize(Info.srWindow.Bottom - Info.srWindow.Top + 1, Info.srWindow.Right - Info.srWindow.Left + 1);
if (containerImage.empty())
{
@ -1653,9 +1666,6 @@ int WslaShell(_In_ std::wstring_view commandLine)
}
// Configure console for interactive usage.
HANDLE Stdout = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE Stdin = GetStdHandle(STD_INPUT_HANDLE);
{
DWORD OutputMode{};
THROW_LAST_ERROR_IF(!::GetConsoleMode(Stdout, &OutputMode));

View File

@ -167,16 +167,13 @@ wsl::windows::common::SvcComm::SvcComm()
};
wsl::shared::retry::RetryWithTimeout<void>(
[this]() {
THROW_IF_FAILED(CoCreateInstance(__uuidof(LxssUserSession), nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&m_userSession)));
},
[this]() { m_userSession = wil::CoCreateInstance<LxssUserSession, ILxssUserSession>(CLSCTX_LOCAL_SERVER); },
std::chrono::seconds(1),
std::chrono::minutes(1),
retry_pred);
// Query client security interface.
wil::com_ptr_nothrow<IClientSecurity> clientSecurity;
THROW_IF_FAILED(m_userSession->QueryInterface(IID_PPV_ARGS(&clientSecurity)));
auto clientSecurity = m_userSession.query<IClientSecurity>();
// Get the current proxy blanket settings.
DWORD authnSvc, authzSvc, authnLvl, capabilities;

View File

@ -625,6 +625,11 @@ std::wstring wsl::windows::common::wslutil::DownloadFileImpl(
void wsl::windows::common::wslutil::EnforceFileLimit(LPCWSTR Path, size_t Limit, const std::function<bool(const std::filesystem::directory_entry&)>& pred)
{
if (Limit <= 0)
{
return;
}
std::map<std::filesystem::file_time_type, std::filesystem::path> files;
for (auto const& e : std::filesystem::directory_iterator{Path})
{
@ -634,7 +639,7 @@ void wsl::windows::common::wslutil::EnforceFileLimit(LPCWSTR Path, size_t Limit,
}
}
if (Limit < 0 || files.size() < Limit)
if (files.size() < Limit)
{
return;
}

View File

@ -79,6 +79,22 @@ void CoInitializeSecurity();
void ConfigureCrt();
/// <summary>
/// Creates a COM server with user impersonation.
/// </summary>
template <typename Interface>
wil::com_ptr_t<Interface> CreateComServerAsUser(_In_ REFCLSID RefClsId, _In_ HANDLE UserToken)
{
auto revert = wil::impersonate_token(UserToken);
return wil::CoCreateInstance<Interface>(RefClsId, (CLSCTX_LOCAL_SERVER | CLSCTX_ENABLE_CLOAKING | CLSCTX_ENABLE_AAA));
}
template <typename Class, typename Interface>
wil::com_ptr_t<Interface> CreateComServerAsUser(_In_ HANDLE UserToken)
{
return CreateComServerAsUser<Interface>(__uuidof(Class), UserToken);
}
std::wstring ConstructPipePath(_In_ std::wstring_view PipeName);
GUID CreateV5Uuid(const GUID& namespaceGuid, const std::span<const std::byte> name);

View File

@ -77,7 +77,7 @@ namespace Windows { namespace Internal {
// Tell COM how to mask fatal exceptions.
if (ownProcess)
{
Microsoft::WRL::ComPtr<IGlobalOptions> pIGLB;
wil::com_ptr<IGlobalOptions> pIGLB;
RETURN_IF_FAILED(CoCreateInstance(CLSID_GlobalOptions, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pIGLB)));
RETURN_IF_FAILED(pIGLB->Set(COMGLB_EXCEPTION_HANDLING, TExceptionPolicy));
}
@ -294,7 +294,7 @@ namespace Windows { namespace Internal {
bool m_addedModuleReference = false;
// COM callback object to support unloading shared-process services
Microsoft::WRL::ComPtr<IContextCallback> m_icc;
wil::com_ptr<IContextCallback> m_icc;
// COM Server descriptor
ServerDescriptor m_serverDescriptor{};

View File

@ -12,6 +12,7 @@ set(SOURCES
BridgedNetworking.cpp
GnsPortTrackerChannel.cpp
GnsRpcServer.cpp
GuestDeviceManager.cpp
GuestTelemetryLogger.cpp
Lifetime.cpp
LxssConsoleManager.cpp
@ -40,6 +41,7 @@ set(HEADERS
BridgedNetworking.h
GnsPortTrackerChannel.h
GnsRpcServer.h
GuestDeviceManager.h
GuestTelemetryLogger.h
IMirroredNetworkManager.h
Lifetime.h
@ -53,8 +55,6 @@ set(HEADERS
WslCoreTcpIpStateTracking.h
WslCoreVm.h)
include_directories(${CMAKE_SOURCE_DIR}/src/windows/wslaclient)
add_executable(wslservice ${SOURCES} ${HEADERS})
add_dependencies(wslservice wslserviceidl wslservicemc)
add_compile_definitions(__WRL_CLASSIC_COM__)

View File

@ -0,0 +1,149 @@
// Copyright (C) Microsoft Corporation. All rights reserved.
#include "precomp.h"
#include "GuestDeviceManager.h"
#include "DeviceHostProxy.h"
GuestDeviceManager::GuestDeviceManager(_In_ const std::wstring& machineId, _In_ const GUID& runtimeId) :
m_machineId(machineId), m_deviceHostSupport(wil::MakeOrThrow<DeviceHostProxy>(machineId, runtimeId))
{
}
_Requires_lock_not_held_(m_lock)
GUID GuestDeviceManager::AddGuestDevice(
_In_ const GUID& DeviceId, _In_ const GUID& ImplementationClsid, _In_ PCWSTR AccessName, _In_opt_ PCWSTR Options, _In_ PCWSTR Path, _In_ UINT32 Flags, _In_ HANDLE UserToken)
{
auto guestDeviceLock = m_lock.lock_exclusive();
return AddHdvShareWithOptions(DeviceId, ImplementationClsid, AccessName, Options, Path, Flags, UserToken);
}
_Requires_lock_held_(m_lock)
GUID GuestDeviceManager::AddHdvShareWithOptions(
_In_ const GUID& DeviceId, _In_ const GUID& ImplementationClsid, _In_ PCWSTR AccessName, _In_opt_ PCWSTR Options, _In_ PCWSTR Path, _In_ UINT32 Flags, _In_ HANDLE UserToken)
{
wil::com_ptr<IPlan9FileSystem> server;
// Options are appended to the name with a semi-colon separator.
// "name;key1=value1;key2=value2"
// The AddSharePath implementation is responsible for separating them out and interpreting them.
std::wstring nameWithOptions{AccessName};
if (ARGUMENT_PRESENT(Options))
{
nameWithOptions += L";";
nameWithOptions += Options;
}
{
auto revert = wil::impersonate_token(UserToken);
server = GetRemoteFileSystem(ImplementationClsid, c_defaultDeviceTag);
if (!server)
{
server = wil::CoCreateInstance<IPlan9FileSystem>(ImplementationClsid, (CLSCTX_LOCAL_SERVER | CLSCTX_ENABLE_CLOAKING | CLSCTX_ENABLE_AAA));
AddRemoteFileSystem(ImplementationClsid, c_defaultDeviceTag.c_str(), server);
}
THROW_IF_FAILED(server->AddSharePath(nameWithOptions.c_str(), Path, Flags));
}
// This requires more privileges than the user may have, so impersonation is disabled.
return AddNewDevice(DeviceId, server, AccessName);
}
GUID GuestDeviceManager::AddNewDevice(_In_ const GUID& deviceId, _In_ const wil::com_ptr<IPlan9FileSystem>& server, _In_ PCWSTR tag)
{
THROW_HR_IF(E_NOT_VALID_STATE, !m_deviceHostSupport);
return m_deviceHostSupport->AddNewDevice(deviceId, server, tag);
}
void GuestDeviceManager::AddRemoteFileSystem(_In_ REFCLSID clsid, _In_ PCWSTR tag, _In_ const wil::com_ptr<IPlan9FileSystem>& server)
{
THROW_HR_IF(E_NOT_VALID_STATE, !m_deviceHostSupport);
m_deviceHostSupport->AddRemoteFileSystem(clsid, tag, server);
}
void GuestDeviceManager::AddSharedMemoryDevice(_In_ const GUID& ImplementationClsid, _In_ PCWSTR Tag, _In_ PCWSTR Path, _In_ UINT32 SizeMb, _In_ HANDLE UserToken)
{
auto guestDeviceLock = m_lock.lock_exclusive();
auto objectLifetime = CreateSectionObjectRoot(Path, UserToken);
// For virtiofs hdv, the flags parameter has been overloaded. Flags are placed in the lower
// 16 bits, while the shared memory size in megabytes are placed in the upper 16 bits.
static constexpr auto VIRTIO_FS_FLAGS_SHMEM_SIZE_SHIFT = 16;
UINT32 flags = (SizeMb << VIRTIO_FS_FLAGS_SHMEM_SIZE_SHIFT);
WI_SetFlag(flags, VIRTIO_FS_FLAGS_TYPE_SECTIONS);
(void)AddHdvShareWithOptions(VIRTIO_VIRTIOFS_DEVICE_ID, ImplementationClsid, Tag, {}, objectLifetime.Path.c_str(), flags, UserToken);
m_objectDirectories.emplace_back(std::move(objectLifetime));
}
GuestDeviceManager::DirectoryObjectLifetime GuestDeviceManager::CreateSectionObjectRoot(_In_ std::wstring_view RelativeRootPath, _In_ HANDLE UserToken) const
{
auto revert = wil::impersonate_token(UserToken);
DWORD sessionId;
DWORD bytesWritten;
THROW_LAST_ERROR_IF(!GetTokenInformation(GetCurrentThreadToken(), TokenSessionId, &sessionId, sizeof(sessionId), &bytesWritten));
// /Sessions/1/BaseNamedObjects/WSL/<VM ID>/<Relative Path>
std::wstringstream sectionPathBuilder;
sectionPathBuilder << L"\\Sessions\\" << sessionId << L"\\BaseNamedObjects" << L"\\WSL\\" << m_machineId << L"\\" << RelativeRootPath;
auto sectionPath = sectionPathBuilder.str();
UNICODE_STRING ntPath{};
OBJECT_ATTRIBUTES attributes{};
attributes.Length = sizeof(OBJECT_ATTRIBUTES);
attributes.ObjectName = &ntPath;
std::vector<wil::unique_handle> directoryHierarchy;
auto remainingPath = std::wstring_view(sectionPath.data(), sectionPath.length());
while (remainingPath.length() > 0)
{
// Find the next path substring, ignoring the root path backslash.
auto nextDir = remainingPath;
const auto separatorPos = nextDir.find(L"\\", remainingPath[0] == L'\\' ? 1 : 0);
if (separatorPos != std::wstring_view::npos)
{
nextDir = nextDir.substr(0, separatorPos);
remainingPath = remainingPath.substr(separatorPos + 1, std::wstring_view::npos);
// Skip concurrent backslashes.
while (remainingPath.length() > 0 && remainingPath[0] == L'\\')
{
remainingPath = remainingPath.substr(1, std::wstring_view::npos);
}
}
else
{
remainingPath = remainingPath.substr(remainingPath.length(), std::wstring_view::npos);
}
attributes.RootDirectory = directoryHierarchy.size() > 0 ? directoryHierarchy.back().get() : nullptr;
ntPath.Buffer = const_cast<PWCH>(nextDir.data());
ntPath.Length = sizeof(WCHAR) * gsl::narrow_cast<USHORT>(nextDir.length());
ntPath.MaximumLength = ntPath.Length;
wil::unique_handle nextHandle;
NTSTATUS status = ZwCreateDirectoryObject(&nextHandle, DIRECTORY_ALL_ACCESS, &attributes);
if (status == STATUS_OBJECT_NAME_COLLISION)
{
status = NtOpenDirectoryObject(&nextHandle, MAXIMUM_ALLOWED, &attributes);
}
THROW_IF_NTSTATUS_FAILED(status);
directoryHierarchy.emplace_back(std::move(nextHandle));
}
return {std::move(sectionPath), std::move(directoryHierarchy)};
}
wil::com_ptr<IPlan9FileSystem> GuestDeviceManager::GetRemoteFileSystem(_In_ REFCLSID clsid, _In_ std::wstring_view tag)
{
THROW_HR_IF(E_NOT_VALID_STATE, !m_deviceHostSupport);
return m_deviceHostSupport->GetRemoteFileSystem(clsid, tag);
}
void GuestDeviceManager::Shutdown()
try
{
if (m_deviceHostSupport)
{
m_deviceHostSupport->Shutdown();
}
}
CATCH_LOG()

View File

@ -0,0 +1,72 @@
// Copyright (C) Microsoft Corporation. All rights reserved.
#pragma once
#include "DeviceHostProxy.h"
// Flags for virtiofs vdev device creation.
#define VIRTIO_FS_FLAGS_TYPE_FILES 0x8000
#define VIRTIO_FS_FLAGS_TYPE_SECTIONS 0x4000
// {872270E1-A899-4AF6-B454-7193634435AD}
DEFINE_GUID(VIRTIO_VIRTIOFS_DEVICE_ID, 0x872270E1, 0xA899, 0x4AF6, 0xB4, 0x54, 0x71, 0x93, 0x63, 0x44, 0x35, 0xAD);
// {ABB755FC-1B86-4255-83E2-E5787ABCF6C2}
DEFINE_GUID(VIRTIO_PMEM_CLASS_ID, 0xABB755FC, 0x1B86, 0x4255, 0x83, 0xe2, 0xe5, 0x78, 0x7a, 0xbc, 0xf6, 0xc2);
inline const std::wstring c_defaultDeviceTag = L"default";
//
// Provides synchronized access to guest device operations.
//
class GuestDeviceManager
{
public:
GuestDeviceManager(_In_ const std::wstring& machineId, _In_ const GUID& runtimeId);
_Requires_lock_not_held_(m_lock)
GUID AddGuestDevice(
_In_ const GUID& DeviceId,
_In_ const GUID& ImplementationClsid,
_In_ PCWSTR AccessName,
_In_opt_ PCWSTR Options,
_In_ PCWSTR Path,
_In_ UINT32 Flags,
_In_ HANDLE UserToken);
GUID AddNewDevice(_In_ const GUID& deviceId, _In_ const wil::com_ptr<IPlan9FileSystem>& server, _In_ PCWSTR tag);
void AddRemoteFileSystem(_In_ REFCLSID clsid, _In_ PCWSTR tag, _In_ const wil::com_ptr<IPlan9FileSystem>& server);
void AddSharedMemoryDevice(_In_ const GUID& ImplementationClsid, _In_ PCWSTR Tag, _In_ PCWSTR Path, _In_ UINT32 SizeMb, _In_ HANDLE UserToken);
wil::com_ptr<IPlan9FileSystem> GetRemoteFileSystem(_In_ REFCLSID clsid, _In_ std::wstring_view tag);
void Shutdown();
private:
_Requires_lock_held_(m_lock)
GUID AddHdvShareWithOptions(
_In_ const GUID& DeviceId,
_In_ const GUID& ImplementationClsid,
_In_ PCWSTR AccessName,
_In_opt_ PCWSTR Options,
_In_ PCWSTR Path,
_In_ UINT32 Flags,
_In_ HANDLE UserToken);
struct DirectoryObjectLifetime
{
std::wstring Path;
// Directory objects are temporary, even if they have children, so need to keep
// any created handles open in order for the directory to remain accessible.
std::vector<wil::unique_handle> HierarchyLifetimes;
};
DirectoryObjectLifetime CreateSectionObjectRoot(_In_ std::wstring_view RelativeRootPath, _In_ HANDLE UserToken) const;
wil::srwlock m_lock;
std::wstring m_machineId;
wil::com_ptr<DeviceHostProxy> m_deviceHostSupport;
_Guarded_by_(m_lock) std::vector<DirectoryObjectLifetime> m_objectDirectories;
};

View File

@ -335,7 +335,7 @@ const std::wstring LxssNetworkingFirewall::s_FriendlyNamePrefix(L"WSLRULE_177744
LxssNetworkingFirewall::LxssNetworkingFirewall()
{
THROW_IF_FAILED(::CoCreateInstance(__uuidof(NetFwPolicy2), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_firewall)));
m_firewall = wil::CoCreateInstance<NetFwPolicy2, INetFwPolicy2>(CLSCTX_INPROC_SERVER);
}
void LxssNetworkingFirewall::CopyPartialArray(SAFEARRAY* Destination, SAFEARRAY* Source, ULONG DestinationIndexStart, ULONG SourceIndexStart, ULONG ElementsToCopy)
@ -388,8 +388,7 @@ void LxssNetworkingFirewall::CopyPartialArray(SAFEARRAY* Destination, SAFEARRAY*
std::wstring LxssNetworkingFirewall::AddPortRule(const IP_ADDRESS_PREFIX& Address) const
{
Microsoft::WRL::ComPtr<INetFwRule> newRule;
THROW_IF_FAILED(::CoCreateInstance(__uuidof(NetFwRule), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&newRule)));
auto newRule = wil::CoCreateInstance<NetFwRule, INetFwRule>(CLSCTX_INPROC_SERVER);
// Open a port via the firewall by creating a rule that specifies the local
// address and the local port to allow. Currently this rule only applies to
@ -412,9 +411,9 @@ std::wstring LxssNetworkingFirewall::AddPortRule(const IP_ADDRESS_PREFIX& Addres
THROW_IF_FAILED(newRule->put_Description(s_DefaultRuleDescription.get()));
THROW_IF_FAILED(newRule->put_Enabled(VARIANT_TRUE));
// Add the rule to the existing set.
Microsoft::WRL::ComPtr<INetFwRules> rules;
wil::com_ptr<INetFwRules> rules;
THROW_IF_FAILED(m_firewall->get_Rules(&rules));
THROW_IF_FAILED(rules->Add(newRule.Get()));
THROW_IF_FAILED(rules->Add(newRule.get()));
// Return the unique rule name to the caller.
return generatedName;
}
@ -423,12 +422,11 @@ void LxssNetworkingFirewall::CleanupRemnants()
{
auto firewall = std::make_shared<LxssNetworkingFirewall>();
THROW_HR_IF(E_OUTOFMEMORY, !firewall);
Microsoft::WRL::ComPtr<INetFwRules> rules;
wil::com_ptr<INetFwRules> rules;
THROW_IF_FAILED(firewall->m_firewall->get_Rules(&rules));
Microsoft::WRL::ComPtr<IUnknown> enumInterface;
THROW_IF_FAILED(rules->get__NewEnum(enumInterface.GetAddressOf()));
Microsoft::WRL::ComPtr<IEnumVARIANT> rulesEnum;
THROW_IF_FAILED(enumInterface.As(&rulesEnum));
wil::com_ptr<IUnknown> enumInterface;
THROW_IF_FAILED(rules->get__NewEnum(enumInterface.addressof()));
auto rulesEnum = enumInterface.query<IEnumVARIANT>();
// Find any rules with the unique WSL prefix and destroy them.
for (;;)
{
@ -440,7 +438,7 @@ void LxssNetworkingFirewall::CleanupRemnants()
break;
}
Microsoft::WRL::ComPtr<INetFwRule> nextRule;
wil::com_ptr<INetFwRule> nextRule;
THROW_IF_FAILED(next.pdispVal->QueryInterface(IID_PPV_ARGS(&nextRule)));
wil::unique_bstr nextRuleName;
THROW_IF_FAILED(nextRule->get_Name(nextRuleName.addressof()));
@ -558,7 +556,7 @@ void LxssNetworkingFirewall::RemoveExcludedAdapter(const std::wstring& AdapterNa
void LxssNetworkingFirewall::RemovePortRule(const std::wstring& RuleName) const
{
Microsoft::WRL::ComPtr<INetFwRules> rules;
wil::com_ptr<INetFwRules> rules;
THROW_IF_FAILED(m_firewall->get_Rules(&rules));
THROW_IF_FAILED(rules->Remove(wil::make_bstr_failfast(RuleName.c_str()).get()));
}
@ -572,8 +570,7 @@ LxssNetworkingFirewallPort::LxssNetworkingFirewallPort(const std::shared_ptr<Lxs
return;
}
LxssNetworkingFirewallPort::LxssNetworkingFirewallPort(
const std::shared_ptr<LxssNetworkingFirewall>& Firewall, const Microsoft::WRL::ComPtr<INetFwRule>& Existing) :
LxssNetworkingFirewallPort::LxssNetworkingFirewallPort(const std::shared_ptr<LxssNetworkingFirewall>& Firewall, const wil::com_ptr<INetFwRule>& Existing) :
m_firewall(Firewall)
{
wil::unique_bstr ruleName;

View File

@ -262,7 +262,7 @@ private:
/// <summary>
/// COM firewall instance.
/// </summary>
Microsoft::WRL::ComPtr<INetFwPolicy2> m_firewall;
wil::com_ptr<INetFwPolicy2> m_firewall;
/// <summary>
/// Lock to protect class members.
@ -295,7 +295,7 @@ public:
/// <summary>
/// Constructor to take ownership of an existing rule.
/// </summary>
LxssNetworkingFirewallPort(const std::shared_ptr<LxssNetworkingFirewall>& Firewall, const Microsoft::WRL::ComPtr<INetFwRule>& Existing);
LxssNetworkingFirewallPort(const std::shared_ptr<LxssNetworkingFirewall>& Firewall, const wil::com_ptr<INetFwRule>& Existing);
/// <summary>
/// Destructor.

View File

@ -2674,8 +2674,7 @@ try
THROW_IF_FAILED(shellLink->SetArguments(commandLine.c_str()));
THROW_IF_FAILED(shellLink->SetIconLocation(ShortcutIcon, 0));
Microsoft::WRL::ComPtr<IPersistFile> storage;
THROW_IF_FAILED(shellLink->QueryInterface(IID_IPersistFile, &storage));
auto storage = shellLink.query<IPersistFile>();
THROW_IF_FAILED(storage->Save(shortcutPath.c_str(), true));
registration.Write(Property::ShortcutPath, shortcutPath.c_str());

View File

@ -18,7 +18,6 @@ Abstract:
#include "WslCoreFilesystem.h"
#include "LxssIpTables.h"
#include "LxssUserSessionFactory.h"
#include "WSLAUserSessionFactory.h"
#include <ctime>
using namespace wsl::windows::common::registry;

View File

@ -2,6 +2,7 @@
#include "precomp.h"
#include "VirtioNetworking.h"
#include "GuestDeviceManager.h"
#include "Stringify.h"
#include "stringshared.h"
@ -12,34 +13,18 @@ using wsl::core::VirtioNetworking;
static constexpr auto c_loopbackDeviceName = TEXT(LX_INIT_LOOPBACK_DEVICE_NAME);
VirtioNetworking::VirtioNetworking(GnsChannel&& gnsChannel, const Config& config) :
m_gnsChannel(std::move(gnsChannel)), m_config(config)
VirtioNetworking::VirtioNetworking(
GnsChannel&& gnsChannel, bool enableLocalhostRelay, std::shared_ptr<GuestDeviceManager> guestDeviceManager, wil::shared_handle userToken) :
m_guestDeviceManager(std::move(guestDeviceManager)),
m_userToken(std::move(userToken)),
m_gnsChannel(std::move(gnsChannel)),
m_enableLocalhostRelay(enableLocalhostRelay)
{
}
VirtioNetworking& VirtioNetworking::OnAddGuestDevice(const AddGuestDeviceRoutine& addGuestDeviceRoutine)
{
m_addGuestDeviceRoutine = addGuestDeviceRoutine;
return *this;
}
VirtioNetworking& VirtioNetworking::OnModifyOpenPorts(const ModifyOpenPortsCallback& modifyOpenPortsCallback)
{
m_modifyOpenPortsCallback = modifyOpenPortsCallback;
return *this;
}
VirtioNetworking& VirtioNetworking::OnGuestInterfaceStateChanged(const GuestInterfaceStateChangeCallback& guestInterfaceStateChangedCallback)
{
m_guestInterfaceStateChangeCallback = guestInterfaceStateChangedCallback;
return *this;
}
void VirtioNetworking::Initialize()
try
{
THROW_HR_IF(E_NOT_SET, !m_addGuestDeviceRoutine || !m_modifyOpenPortsCallback || !m_guestInterfaceStateChangeCallback);
m_networkSettings = GetHostEndpointSettings();
// TODO: Determine gateway MAC address
@ -83,11 +68,12 @@ try
device_options << L"nameservers=" << dns_servers;
}
// Add virtio net adapter to guest
m_adapterId = (*m_addGuestDeviceRoutine)(c_virtioNetworkClsid, c_virtioNetworkDeviceId, L"eth0", device_options.str().c_str());
auto lock = m_lock.lock_exclusive();
// Add virtio net adapter to guest
m_adapterId = m_guestDeviceManager->AddGuestDevice(
c_virtioNetworkDeviceId, c_virtioNetworkClsid, L"eth0", nullptr, device_options.str().c_str(), 0, m_userToken.get());
hns::HNSEndpoint endpointProperties;
endpointProperties.ID = m_adapterId;
endpointProperties.IPAddress = m_networkSettings->PreferredIpAddress.AddressString;
@ -121,7 +107,7 @@ try
UpdateDns(std::move(dnsSettings));
}
if (m_config.EnableLocalhostRelay)
if (m_enableLocalhostRelay)
{
SetupLoopbackDevice();
}
@ -132,8 +118,14 @@ CATCH_LOG()
void VirtioNetworking::SetupLoopbackDevice()
{
m_localhostAdapterId = (*m_addGuestDeviceRoutine)(
c_virtioNetworkClsid, c_virtioNetworkDeviceId, c_loopbackDeviceName, L"client_ip=127.0.0.1;client_mac=00:11:22:33:44:55");
m_localhostAdapterId = m_guestDeviceManager->AddGuestDevice(
c_virtioNetworkDeviceId,
c_virtioNetworkClsid,
c_loopbackDeviceName,
nullptr,
L"client_ip=127.0.0.1;client_mac=00:11:22:33:44:55",
0,
m_userToken.get());
hns::HNSEndpoint endpointProperties;
endpointProperties.ID = m_localhostAdapterId;
@ -162,7 +154,7 @@ void VirtioNetworking::StartPortTracker(wil::unique_socket&& socket)
m_gnsPortTrackerChannel.emplace(
std::move(socket),
[&](const SOCKADDR_INET& addr, int protocol, bool allocate) { return HandlePortNotification(addr, protocol, allocate); },
[&](_In_ const std::string& interfaceName, _In_ bool up) { (*m_guestInterfaceStateChangeCallback)(interfaceName, up); });
[](const std::string&, bool) {}); // TODO: reconsider if InterfaceStateCallback is needed.
}
HRESULT VirtioNetworking::HandlePortNotification(const SOCKADDR_INET& addr, int protocol, bool allocate) const noexcept
@ -181,7 +173,7 @@ HRESULT VirtioNetworking::HandlePortNotification(const SOCKADDR_INET& addr, int
}
}
if (m_config.EnableLocalhostRelay && (unspecified || loopback))
if (m_enableLocalhostRelay && (unspecified || loopback))
{
SOCKADDR_INET localAddr = addr;
if (!loopback)
@ -196,21 +188,65 @@ HRESULT VirtioNetworking::HandlePortNotification(const SOCKADDR_INET& addr, int
localAddr.Ipv6.sin6_port = addr.Ipv6.sin6_port;
}
}
result = (*m_modifyOpenPortsCallback)(c_virtioNetworkClsid, c_loopbackDeviceName, localAddr, protocol, allocate);
result = ModifyOpenPorts(c_virtioNetworkClsid, c_loopbackDeviceName, localAddr, protocol, allocate);
LOG_HR_IF_MSG(E_FAIL, result != S_OK, "Failure adding localhost relay port %d", localAddr.Ipv4.sin_port);
}
if (!loopback)
{
const int localResult = (*m_modifyOpenPortsCallback)(c_virtioNetworkClsid, L"eth0", addr, protocol, allocate);
const int localResult = ModifyOpenPorts(c_virtioNetworkClsid, L"eth0", addr, protocol, allocate);
LOG_HR_IF_MSG(E_FAIL, localResult != S_OK, "Failure adding relay port %d", addr.Ipv4.sin_port);
if (result == 0)
{
result = localResult;
}
}
return result;
}
int VirtioNetworking::ModifyOpenPorts(_In_ const GUID& clsid, _In_ PCWSTR tag, _In_ const SOCKADDR_INET& addr, _In_ int protocol, _In_ bool isOpen) const
{
if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
{
LOG_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), "Unsupported bind protocol %d", protocol);
return 0;
}
else if (addr.si_family == AF_INET6)
{
// The virtio net adapter does not yet support IPv6 packets, so any traffic would arrive via
// IPv4. If the caller wants IPv4 they will also likely listen on an IPv4 address, which will
// be handled as a separate callback to this same code.
return 0;
}
auto lock = m_lock.lock_exclusive();
const auto server = m_guestDeviceManager->GetRemoteFileSystem(clsid, c_defaultDeviceTag);
if (server)
{
std::wstring portString = std::format(L"tag={};port_number={}", tag, addr.Ipv4.sin_port);
if (protocol == IPPROTO_UDP)
{
portString += L";udp";
}
if (!isOpen)
{
portString += L";allocate=false";
}
else
{
wchar_t addrStr[16]; // "000.000.000.000" + null terminator
RtlIpv4AddressToStringW(&addr.Ipv4.sin_addr, addrStr);
portString += std::format(L";listen_addr={}", addrStr);
}
LOG_IF_FAILED(server->AddShare(portString.c_str(), nullptr, 0));
}
return 0;
}
void NETIOAPI_API_ VirtioNetworking::OnNetworkConnectivityChange(PVOID context, NL_NETWORK_CONNECTIVITY_HINT hint)
{
static_cast<VirtioNetworking*>(context)->RefreshGuestConnection(hint);

View File

@ -6,23 +6,16 @@
#include "GnsChannel.h"
#include "WslCoreHostDnsInfo.h"
#include "GnsPortTrackerChannel.h"
#include "GuestDeviceManager.h"
namespace wsl::core {
using AddGuestDeviceRoutine = std::function<GUID(const GUID& clsid, const GUID& deviceId, PCWSTR tag, PCWSTR options)>;
using ModifyOpenPortsCallback = std::function<int(const GUID& clsid, PCWSTR tag, const SOCKADDR_INET& addr, int protocol, bool isOpen)>;
using GuestInterfaceStateChangeCallback = std::function<void(const std::string& name, bool isUp)>;
class VirtioNetworking : public INetworkingEngine
{
public:
VirtioNetworking(GnsChannel&& gnsChannel, const Config& config);
VirtioNetworking(GnsChannel&& gnsChannel, bool enableLocalhostRelay, std::shared_ptr<GuestDeviceManager> guestDeviceManager, wil::shared_handle userToken);
~VirtioNetworking() = default;
VirtioNetworking& OnAddGuestDevice(const AddGuestDeviceRoutine& addGuestDeviceRoutine);
VirtioNetworking& OnModifyOpenPorts(const ModifyOpenPortsCallback& modifyOpenPortsCallback);
VirtioNetworking& OnGuestInterfaceStateChanged(const GuestInterfaceStateChangeCallback& guestInterfaceStateChangedCallback);
// Note: This class cannot be moved because m_networkNotifyHandle captures a 'this' pointer.
VirtioNetworking(const VirtioNetworking&) = delete;
VirtioNetworking(VirtioNetworking&&) = delete;
@ -42,6 +35,7 @@ private:
static std::optional<ULONGLONG> FindVirtioInterfaceLuid(const SOCKADDR_INET& virtioAddress, const NL_NETWORK_CONNECTIVITY_HINT& currentConnectivityHint);
HRESULT HandlePortNotification(const SOCKADDR_INET& addr, int protocol, bool allocate) const noexcept;
int ModifyOpenPorts(_In_ const GUID& clsid, _In_ PCWSTR tag, _In_ const SOCKADDR_INET& addr, _In_ int protocol, _In_ bool isOpen) const;
void RefreshGuestConnection(NL_NETWORK_CONNECTIVITY_HINT hint) noexcept;
void SetupLoopbackDevice();
void UpdateDns(wsl::shared::hns::DNS&& dnsSettings);
@ -49,17 +43,14 @@ private:
mutable wil::srwlock m_lock;
std::optional<AddGuestDeviceRoutine> m_addGuestDeviceRoutine;
std::shared_ptr<GuestDeviceManager> m_guestDeviceManager;
wil::shared_handle m_userToken;
GnsChannel m_gnsChannel;
std::optional<GnsPortTrackerChannel> m_gnsPortTrackerChannel;
std::shared_ptr<networking::NetworkSettings> m_networkSettings;
const Config& m_config;
bool m_enableLocalhostRelay;
GUID m_localhostAdapterId;
GUID m_adapterId;
std::optional<NL_NETWORK_CONNECTIVITY_LEVEL_HINT> m_connectivityLevel;
std::optional<NL_NETWORK_CONNECTIVITY_COST_HINT> m_connectivityCost;
std::optional<ModifyOpenPortsCallback> m_modifyOpenPortsCallback;
std::optional<GuestInterfaceStateChangeCallback> m_guestInterfaceStateChangeCallback;
std::optional<ULONGLONG> m_interfaceLuid;
ULONG m_networkMtu = 0;

View File

@ -1,87 +0,0 @@
/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
WSLAUserSession.cpp
Abstract:
TODO
--*/
#include "WSLAUserSession.h"
using wsl::windows::service::wsla::WSLAUserSessionImpl;
WSLAUserSessionImpl::WSLAUserSessionImpl(HANDLE Token, wil::unique_tokeninfo_ptr<TOKEN_USER>&& TokenInfo) :
m_tokenInfo(std::move(TokenInfo))
{
}
WSLAUserSessionImpl::~WSLAUserSessionImpl()
{
// Manually signal the VM termination events. This prevents being stuck on an API call that holds the VM lock.
{
std::lock_guard lock(m_virtualMachinesLock);
for (auto* e : m_virtualMachines)
{
e->OnSessionTerminating();
}
}
}
void WSLAUserSessionImpl::OnVmTerminated(WSLAVirtualMachine* machine)
{
std::lock_guard lock(m_virtualMachinesLock);
auto pred = [machine](const auto* e) { return machine == e; };
// Remove any stale VM reference.
m_virtualMachines.erase(std::remove_if(m_virtualMachines.begin(), m_virtualMachines.end(), pred), m_virtualMachines.end());
}
HRESULT WSLAUserSessionImpl::CreateVirtualMachine(const VIRTUAL_MACHINE_SETTINGS* Settings, IWSLAVirtualMachine** VirtualMachine)
{
auto vm = wil::MakeOrThrow<WSLAVirtualMachine>(*Settings, GetUserSid(), this);
{
std::lock_guard lock(m_virtualMachinesLock);
m_virtualMachines.emplace_back(vm.Get());
}
vm->Start();
THROW_IF_FAILED(vm.CopyTo(__uuidof(IWSLAVirtualMachine), (void**)VirtualMachine));
return S_OK;
}
PSID WSLAUserSessionImpl::GetUserSid() const
{
return m_tokenInfo->User.Sid;
}
wsl::windows::service::wsla::WSLAUserSession::WSLAUserSession(std::weak_ptr<WSLAUserSessionImpl>&& Session) :
m_session(std::move(Session))
{
}
HRESULT wsl::windows::service::wsla::WSLAUserSession::GetVersion(_Out_ WSL_VERSION* Version)
{
Version->Major = WSL_PACKAGE_VERSION_MAJOR;
Version->Minor = WSL_PACKAGE_VERSION_MINOR;
Version->Revision = WSL_PACKAGE_VERSION_REVISION;
return S_OK;
}
HRESULT wsl::windows::service::wsla::WSLAUserSession::CreateVirtualMachine(const VIRTUAL_MACHINE_SETTINGS* Settings, IWSLAVirtualMachine** VirtualMachine)
try
{
auto session = m_session.lock();
RETURN_HR_IF(RPC_E_DISCONNECTED, !session);
return session->CreateVirtualMachine(Settings, VirtualMachine);
}
CATCH_RETURN();

View File

@ -1,56 +0,0 @@
/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
WSLAUserSession.h
Abstract:
TODO
--*/
#pragma once
#include "WSLAVirtualMachine.h"
namespace wsl::windows::service::wsla {
class WSLAUserSessionImpl
{
public:
WSLAUserSessionImpl(HANDLE Token, wil::unique_tokeninfo_ptr<TOKEN_USER>&& TokenInfo);
WSLAUserSessionImpl(WSLAUserSessionImpl&&) = default;
WSLAUserSessionImpl& operator=(WSLAUserSessionImpl&&) = default;
~WSLAUserSessionImpl();
PSID GetUserSid() const;
HRESULT CreateVirtualMachine(const VIRTUAL_MACHINE_SETTINGS* Settings, IWSLAVirtualMachine** VirtualMachine);
void OnVmTerminated(WSLAVirtualMachine* machine);
private:
wil::unique_tokeninfo_ptr<TOKEN_USER> m_tokenInfo;
std::recursive_mutex m_virtualMachinesLock;
std::vector<WSLAVirtualMachine*> m_virtualMachines;
};
class DECLSPEC_UUID("a9b7a1b9-0671-405c-95f1-e0612cb4ce8f") WSLAUserSession
: public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IWSLAUserSession, IFastRundown>
{
public:
WSLAUserSession(std::weak_ptr<WSLAUserSessionImpl>&& Session);
WSLAUserSession(const WSLAUserSession&) = delete;
WSLAUserSession& operator=(const WSLAUserSession&) = delete;
IFACEMETHOD(GetVersion)(_Out_ WSL_VERSION* Version) override;
IFACEMETHOD(CreateVirtualMachine)(const VIRTUAL_MACHINE_SETTINGS* Settings, IWSLAVirtualMachine** VirtualMachine) override;
private:
std::weak_ptr<WSLAUserSessionImpl> m_session;
};
} // namespace wsl::windows::service::wsla

View File

@ -1,82 +0,0 @@
/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
WSLAUserSessionFactory.cpp
Abstract:
TODO
--*/
#include "precomp.h"
#include "WSLAUserSessionFactory.h"
#include "WSLAUserSession.h"
using wsl::windows::service::wsla::WSLAUserSessionFactory;
using wsl::windows::service::wsla::WSLAUserSessionImpl;
CoCreatableClassWithFactory(WSLAUserSession, WSLAUserSessionFactory);
static std::mutex g_mutex;
static std::optional<std::vector<std::shared_ptr<WSLAUserSessionImpl>>> g_sessions =
std::make_optional<std::vector<std::shared_ptr<WSLAUserSessionImpl>>>();
HRESULT WSLAUserSessionFactory::CreateInstance(_In_ IUnknown* pUnkOuter, _In_ REFIID riid, _Out_ void** ppCreated)
{
RETURN_HR_IF_NULL(E_POINTER, ppCreated);
*ppCreated = nullptr;
RETURN_HR_IF(CLASS_E_NOAGGREGATION, pUnkOuter != nullptr);
WSL_LOG("WSLAUserSessionFactory", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
try
{
const wil::unique_handle userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);
// Get the session ID and SID of the client process.
DWORD sessionId{};
DWORD length = 0;
THROW_IF_WIN32_BOOL_FALSE(::GetTokenInformation(userToken.get(), TokenSessionId, &sessionId, sizeof(sessionId), &length));
auto tokenInfo = wil::get_token_information<TOKEN_USER>(userToken.get());
std::lock_guard lock{g_mutex};
THROW_HR_IF(CO_E_SERVER_STOPPING, !g_sessions.has_value());
auto session = std::find_if(g_sessions->begin(), g_sessions->end(), [&tokenInfo](auto it) {
return EqualSid(it->GetUserSid(), &tokenInfo->User.Sid);
});
if (session == g_sessions->end())
{
session = g_sessions->insert(g_sessions->end(), std::make_shared<WSLAUserSessionImpl>(userToken.get(), std::move(tokenInfo)));
}
auto comInstance = wil::MakeOrThrow<WSLAUserSession>(std::weak_ptr<WSLAUserSessionImpl>(*session));
THROW_IF_FAILED(comInstance.CopyTo(riid, ppCreated));
}
catch (...)
{
const auto result = wil::ResultFromCaughtException();
// Note: S_FALSE will cause COM to retry if the service is stopping.
return result == CO_E_SERVER_STOPPING ? S_FALSE : result;
}
WSL_LOG("WSLAUserSessionFactory", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
return S_OK;
}
void wsl::windows::service::wsla::ClearWslaSessionsAndBlockNewInstances()
{
std::lock_guard lock{g_mutex};
g_sessions.reset();
}

View File

@ -1,28 +0,0 @@
/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
WSLAUserSessionFactory.h
Abstract:
TODO
--*/
#pragma once
#include <wil/resource.h>
namespace wsl::windows::service::wsla {
class WSLAUserSessionFactory : public Microsoft::WRL::ClassFactory<>
{
public:
WSLAUserSessionFactory() = default;
STDMETHODIMP CreateInstance(_In_ IUnknown* pUnkOuter, _In_ REFIID riid, _Out_ void** ppCreated) override;
};
void ClearWslaSessionsAndBlockNewInstances();
} // namespace wsl::windows::service::wsla

File diff suppressed because it is too large Load Diff

View File

@ -1,106 +0,0 @@
/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
WSLAVirtualMachine.h
Abstract:
TODO
--*/
#pragma once
#include "wslservice.h"
#include "INetworkingEngine.h"
#include "hcs.hpp"
#include "Dmesg.h"
#include "WSLAApi.h"
namespace wsl::windows::service::wsla {
class WSLAUserSessionImpl;
class DECLSPEC_UUID("0CFC5DC1-B6A7-45FC-8034-3FA9ED73CE30") WSLAVirtualMachine
: public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IWSLAVirtualMachine, IFastRundown>
{
public:
WSLAVirtualMachine(const VIRTUAL_MACHINE_SETTINGS& Settings, PSID Sid, WSLAUserSessionImpl* UserSession);
~WSLAVirtualMachine();
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_CREATE_PROCESS_OPTIONS* Options, _In_ ULONG FdCount, _In_ WSLA_PROCESS_FD* Fd, _Out_ ULONG* Handles, _Out_ WSLA_CREATE_PROCESS_RESULT* Result)) 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;
IFACEMETHOD(Shutdown(ULONGLONG _In_ TimeoutMs)) override;
IFACEMETHOD(RegisterCallback(_In_ ITerminationCallback* callback)) override;
IFACEMETHOD(GetDebugShellPipe(_Out_ LPWSTR* pipePath)) override;
IFACEMETHOD(MapPort(_In_ int Family, _In_ short WindowsPort, _In_ short LinuxPort, _In_ BOOL Remove)) override;
IFACEMETHOD(Unmount(_In_ const char* Path)) override;
IFACEMETHOD(DetachDisk(_In_ ULONG Lun)) override;
IFACEMETHOD(MountWindowsFolder(_In_ LPCWSTR WindowsPath, _In_ LPCSTR LinuxPath, _In_ BOOL ReadOnly)) override;
IFACEMETHOD(UnmountWindowsFolder(_In_ LPCSTR LinuxPath)) override;
IFACEMETHOD(MountGpuLibraries(_In_ LPCSTR LibrariesMountPoint, _In_ LPCSTR DriversMountpoint, _In_ DWORD Flags)) override;
private:
static int32_t MountImpl(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);
void ConfigureNetworking();
void OnExit(_In_ const HCS_EVENT* Event);
std::tuple<int32_t, int32_t, wsl::shared::SocketChannel> Fork(enum WSLA_FORK::ForkType Type);
std::tuple<int32_t, int32_t, wsl::shared::SocketChannel> Fork(wsl::shared::SocketChannel& Channel, enum WSLA_FORK::ForkType Type);
int32_t ExpectClosedChannelOrError(wsl::shared::SocketChannel& Channel);
wil::unique_socket ConnectSocket(wsl::shared::SocketChannel& Channel, int32_t Fd);
void OpenLinuxFile(wsl::shared::SocketChannel& Channel, const char* Path, uint32_t Flags, int32_t Fd);
void LaunchPortRelay();
std::vector<wil::unique_socket> CreateLinuxProcessImpl(
_In_ const WSLA_CREATE_PROCESS_OPTIONS* Options, _In_ ULONG FdCount, _In_ WSLA_PROCESS_FD* Fd, _Out_ WSLA_CREATE_PROCESS_RESULT* Result);
HRESULT MountWindowsFolderImpl(_In_ LPCWSTR WindowsPath, _In_ LPCSTR LinuxPath, _In_ BOOL ReadOnly, _In_ WslMountFlags Flags);
struct AttachedDisk
{
std::filesystem::path Path;
std::string Device;
bool AccessGranted = false;
};
VIRTUAL_MACHINE_SETTINGS m_settings;
GUID m_vmId{};
std::wstring m_vmIdString;
wsl::windows::common::helpers::WindowsVersion m_windowsVersion = wsl::windows::common::helpers::GetWindowsVersion();
int m_coldDiscardShiftSize{};
bool m_running = false;
PSID m_userSid{};
std::wstring m_debugShellPipe;
wsl::windows::common::hcs::unique_hcs_system m_computeSystem;
std::shared_ptr<DmesgCollector> m_dmesgCollector;
wil::unique_event m_vmExitEvent{wil::EventOptions::ManualReset};
wil::unique_event m_vmTerminatingEvent{wil::EventOptions::ManualReset};
wil::com_ptr<ITerminationCallback> m_terminationCallback;
std::unique_ptr<wsl::core::INetworkingEngine> m_networkEngine;
wsl::shared::SocketChannel m_initChannel;
wil::unique_handle m_portRelayChannelRead;
wil::unique_handle m_portRelayChannelWrite;
std::map<ULONG, AttachedDisk> m_attachedDisks;
std::map<std::string, std::wstring> m_plan9Mounts;
std::recursive_mutex m_lock;
std::mutex m_portRelaylock;
WSLAUserSessionImpl* m_userSession;
};
} // namespace wsl::windows::service::wsla

View File

@ -39,27 +39,16 @@ using namespace std::string_literals;
// Start of unaddressable memory if guest only supports the minimum 36-bit addressing.
#define MAX_36_BIT_PAGE_IN_MB (0x1000000000 / _1MB)
// This device type is implemented by the external virtiofs vdev.
// {872270E1-A899-4AF6-B454-7193634435AD}
DEFINE_GUID(VIRTIO_VIRTIOFS_DEVICE_ID, 0x872270E1, 0xA899, 0x4AF6, 0xB4, 0x54, 0x71, 0x93, 0x63, 0x44, 0x35, 0xAD);
// This device type is implemented by the external virtio-pmem vdev.
// {EDBB24BB-5E19-40F4-8A0F-8224313064FD}
DEFINE_GUID(VIRTIO_PMEM_DEVICE_ID, 0xEDBB24BB, 0x5E19, 0x40F4, 0x8A, 0x0F, 0x82, 0x24, 0x31, 0x30, 0x64, 0xFD);
// Flags for virtiofs vdev device creation.
#define VIRTIO_FS_FLAGS_TYPE_FILES 0x8000
#define VIRTIO_FS_FLAGS_TYPE_SECTIONS 0x4000
#define WSLG_SHARED_MEMORY_SIZE_MB 8192
#define PAGE_SIZE 0x1000
static constexpr size_t c_bootEntropy = 0x1000;
static constexpr auto c_localDevicesKey = L"SOFTWARE\\Microsoft\\Terminal Server Client\\LocalDevices";
// {ABB755FC-1B86-4255-83E2-E5787ABCF6C2}
static constexpr GUID c_pmemClassId = {0xABB755FC, 0x1B86, 0x4255, {0x83, 0xe2, 0xe5, 0x78, 0x7a, 0xbc, 0xf6, 0xc2}};
#define LXSS_ENABLE_GUI_APPS() (m_vmConfig.EnableGuiApps && (m_systemDistroDeviceId != ULONG_MAX))
using namespace wsl::windows::common;
@ -70,8 +59,6 @@ using wsl::shared::Localization;
using wsl::windows::common::Context;
using wsl::windows::common::ExecutionContext;
const std::wstring WslCoreVm::c_defaultTag = L"default"s;
namespace {
INT64
RequiredExtraMmioSpaceForPmemFileInMb(_In_ PCWSTR FilePath)
@ -335,7 +322,8 @@ void WslCoreVm::Initialize(const GUID& VmId, const wil::shared_handle& UserToken
m_runtimeId = wsl::windows::common::hcs::GetRuntimeId(m_system.get());
WI_ASSERT(IsEqualGUID(VmId, m_runtimeId));
m_deviceHostSupport = wil::MakeOrThrow<DeviceHostProxy>(m_machineId, m_runtimeId);
// Initialize the guest device manager.
m_guestDeviceManager = std::make_shared<GuestDeviceManager>(m_machineId, m_runtimeId);
// Create a socket listening for connections from mini_init.
m_listenSocket = wsl::windows::common::hvsocket::Listen(m_runtimeId, LX_INIT_UTILITY_VM_INIT_PORT);
@ -462,7 +450,7 @@ void WslCoreVm::Initialize(const GUID& VmId, const wil::shared_handle& UserToken
break;
case LxMiniInitMountDeviceTypePmem:
m_systemDistroDeviceId = MountFileAsPersistentMemory(c_pmemClassId, m_vmConfig.SystemDistroPath.c_str(), true);
m_systemDistroDeviceId = MountFileAsPersistentMemory(m_vmConfig.SystemDistroPath.c_str(), true);
break;
default:
@ -600,55 +588,8 @@ void WslCoreVm::Initialize(const GUID& VmId, const wil::shared_handle& UserToken
}
else if (m_vmConfig.NetworkingMode == NetworkingMode::VirtioProxy)
{
auto virtioNetworkingEngine = std::make_unique<wsl::core::VirtioNetworking>(std::move(gnsChannel), m_vmConfig);
virtioNetworkingEngine->OnAddGuestDevice([&](const GUID& Clsid, const GUID& DeviceId, PCWSTR Tag, PCWSTR Options) {
auto guestDeviceLock = m_guestDeviceLock.lock_exclusive();
return AddHdvShareWithOptions(DeviceId, Clsid, Tag, {}, Options, 0, m_userToken.get());
});
virtioNetworkingEngine->OnModifyOpenPorts([&](const GUID& Clsid, PCWSTR Tag, const SOCKADDR_INET& addr, int protocol, bool isOpen) {
if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
{
LOG_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), "Unsupported bind protocol %d", protocol);
return 0;
}
else if (addr.si_family == AF_INET6)
{
// The virtio net adapter does not yet support IPv6 packets, so any traffic would arrive via
// IPv4. If the caller wants IPv4 they will also likely listen on an IPv4 address, which will
// be handled as a separate callback to this same code.
return 0;
}
auto guestDeviceLock = m_guestDeviceLock.lock_exclusive();
const auto server = m_deviceHostSupport->GetRemoteFileSystem(Clsid, c_defaultTag);
if (server)
{
std::wstring portString(L"tag=");
portString += Tag;
portString += L";port_number=";
portString += std::to_wstring(addr.Ipv4.sin_port);
if (protocol == IPPROTO_UDP)
{
portString += L";udp";
}
if (!isOpen)
{
portString += L";allocate=false";
}
else
{
std::wstring addrStr(L"000.000.000.000\0");
RtlIpv4AddressToStringW(&addr.Ipv4.sin_addr, addrStr.data());
portString += L";listen_addr=";
portString += addrStr;
}
LOG_IF_FAILED(server->AddShare(portString.c_str(), nullptr, 0));
}
return 0;
});
virtioNetworkingEngine->OnGuestInterfaceStateChanged([&](const std::string& name, bool isUp) {});
m_networkingEngine.reset(virtioNetworkingEngine.release());
m_networkingEngine = std::make_unique<wsl::core::VirtioNetworking>(
std::move(gnsChannel), m_vmConfig.EnableLocalhostRelay, m_guestDeviceManager, m_userToken);
}
else if (m_vmConfig.NetworkingMode == NetworkingMode::Bridged)
{
@ -831,9 +772,9 @@ WslCoreVm::~WslCoreVm() noexcept
}
// Shutdown virtio device hosts.
if (m_deviceHostSupport)
if (m_guestDeviceManager)
{
m_deviceHostSupport->Shutdown();
m_guestDeviceManager->Shutdown();
}
// Call RevokeVmAccess on each VHD that was added to the utility VM. This
@ -956,7 +897,7 @@ void WslCoreVm::AddPlan9Share(
if (m_vmConfig.EnableVirtio9p)
{
server = m_deviceHostSupport->GetRemoteFileSystem(__uuidof(p9fs::Plan9FileSystem), VirtIoTag);
server = m_guestDeviceManager->GetRemoteFileSystem(__uuidof(p9fs::Plan9FileSystem), VirtIoTag);
}
else
{
@ -969,10 +910,10 @@ void WslCoreVm::AddPlan9Share(
if (!server)
{
server = CreateComServerAsUser<p9fs::Plan9FileSystem, IPlan9FileSystem>(UserToken);
server = wsl::windows::common::wslutil::CreateComServerAsUser<p9fs::Plan9FileSystem, IPlan9FileSystem>(UserToken);
if (m_vmConfig.EnableVirtio9p)
{
m_deviceHostSupport->AddRemoteFileSystem(__uuidof(p9fs::Plan9FileSystem), VirtIoTag, server);
m_guestDeviceManager->AddRemoteFileSystem(__uuidof(p9fs::Plan9FileSystem), VirtIoTag, server);
// Start with one device to handle the first mount request. After
// each mount, the Plan9 file-system will request additional
@ -1000,66 +941,10 @@ void WslCoreVm::AddPlan9Share(
if (addNewDevice)
{
// This requires more privileges than the user may have, so impersonation is disabled.
(void)m_deviceHostSupport->AddNewDevice(VIRTIO_PLAN9_DEVICE_ID, server, VirtIoTag);
(void)m_guestDeviceManager->AddNewDevice(VIRTIO_PLAN9_DEVICE_ID, server, VirtIoTag);
}
}
WslCoreVm::DirectoryObjectLifetime WslCoreVm::CreateSectionObjectRoot(_In_ std::wstring_view RelativeRootPath, _In_ HANDLE UserToken) const
{
auto revert = wil::impersonate_token(UserToken);
DWORD sessionId;
DWORD bytesWritten;
THROW_LAST_ERROR_IF(!GetTokenInformation(GetCurrentThreadToken(), TokenSessionId, &sessionId, sizeof(sessionId), &bytesWritten));
// /Sessions/1/BaseNamedObjects/WSL/<VM ID>/<Relative Path>
std::wstringstream sectionPathBuilder;
sectionPathBuilder << L"\\Sessions\\" << sessionId << L"\\BaseNamedObjects" << L"\\WSL\\" << m_machineId << L"\\" << RelativeRootPath;
auto sectionPath = sectionPathBuilder.str();
UNICODE_STRING ntPath{};
OBJECT_ATTRIBUTES attributes{};
attributes.Length = sizeof(OBJECT_ATTRIBUTES);
attributes.ObjectName = &ntPath;
std::vector<wil::unique_handle> directoryHierarchy;
auto remainingPath = std::wstring_view(sectionPath.data(), sectionPath.length());
while (remainingPath.length() > 0)
{
// Find the next path substring, ignoring the root path backslash.
auto nextDir = remainingPath;
const auto separatorPos = nextDir.find(L"\\", remainingPath[0] == L'\\' ? 1 : 0);
if (separatorPos != std::wstring_view::npos)
{
nextDir = nextDir.substr(0, separatorPos);
remainingPath = remainingPath.substr(separatorPos + 1, std::wstring_view::npos);
// Skip concurrent backslashes.
while (remainingPath.length() > 0 && remainingPath[0] == L'\\')
{
remainingPath = remainingPath.substr(1, std::wstring_view::npos);
}
}
else
{
remainingPath = remainingPath.substr(remainingPath.length(), std::wstring_view::npos);
}
attributes.RootDirectory = directoryHierarchy.size() > 0 ? directoryHierarchy.back().get() : nullptr;
ntPath.Buffer = const_cast<PWCH>(nextDir.data());
ntPath.Length = sizeof(WCHAR) * gsl::narrow_cast<USHORT>(nextDir.length());
ntPath.MaximumLength = ntPath.Length;
wil::unique_handle nextHandle;
NTSTATUS status = ZwCreateDirectoryObject(&nextHandle, DIRECTORY_ALL_ACCESS, &attributes);
if (status == STATUS_OBJECT_NAME_COLLISION)
{
status = NtOpenDirectoryObject(&nextHandle, MAXIMUM_ALLOWED, &attributes);
}
THROW_IF_NTSTATUS_FAILED(status);
directoryHierarchy.emplace_back(std::move(nextHandle));
}
return {std::move(sectionPath), std::move(directoryHierarchy)};
}
ULONG WslCoreVm::AttachDisk(_In_ PCWSTR Disk, _In_ DiskType Type, _In_ std::optional<ULONG> Lun, _In_ bool IsUserDisk, _In_ HANDLE UserToken)
{
auto lock = m_lock.lock_exclusive();
@ -1868,7 +1753,8 @@ void WslCoreVm::InitializeGuest()
{
try
{
MountSharedMemoryDevice(c_virtiofsClassId, L"wslg", L"wslg", WSLG_SHARED_MEMORY_SIZE_MB);
m_guestDeviceManager->AddSharedMemoryDevice(
c_virtiofsClassId, L"wslg", L"wslg", WSLG_SHARED_MEMORY_SIZE_MB, m_userToken.get());
m_sharedMemoryRoot = std::format(L"WSL\\{}\\wslg", m_machineId);
}
CATCH_LOG()
@ -1967,7 +1853,7 @@ bool WslCoreVm::InitializeDrvFsLockHeld(_In_ HANDLE UserToken)
{
// Before checking whether DrvFs is already initialized, make sure any existing Plan 9 servers
// are usable.
VerifyDrvFsServers();
VerifyPlan9Servers();
const auto elevated = wsl::windows::common::security::IsTokenElevated(UserToken);
if (elevated)
@ -1999,6 +1885,12 @@ bool WslCoreVm::IsDnsTunnelingSupported() const
return SUCCEEDED_LOG(wsl::core::networking::DnsResolver::LoadDnsResolverMethods());
}
bool WslCoreVm::IsVhdAttached(_In_ PCWSTR VhdPath)
{
auto lock = m_lock.lock_exclusive();
return m_attachedDisks.contains({DiskType::VHD, VhdPath});
}
WslCoreVm::DiskMountResult WslCoreVm::MountDisk(
_In_ PCWSTR Disk, _In_ DiskType MountDiskType, _In_ ULONG PartitionIndex, _In_opt_ PCWSTR Name, _In_opt_ PCWSTR Type, _In_opt_ PCWSTR Options)
{
@ -2098,33 +1990,8 @@ void WslCoreVm::MountRootNamespaceFolder(_In_ LPCWSTR HostPath, _In_ LPCWSTR Gue
ResultMessage.Result);
}
void WslCoreVm::MountSharedMemoryDevice(_In_ const GUID& ImplementationClsid, _In_ PCWSTR Tag, _In_ PCWSTR Path, _In_ UINT32 SizeMb)
{
if (!m_vmConfig.EnableVirtio)
{
return;
}
auto guestDeviceLock = m_guestDeviceLock.lock_exclusive();
MountSharedMemoryDeviceLockHeld(ImplementationClsid, Tag, Path, SizeMb);
}
_Requires_lock_held_(m_guestDeviceLock)
void WslCoreVm::MountSharedMemoryDeviceLockHeld(_In_ const GUID& ImplementationClsid, _In_ PCWSTR Tag, _In_ PCWSTR Path, _In_ UINT32 SizeMb)
{
auto objectLifetime = CreateSectionObjectRoot(Path, m_userToken.get());
// For virtiofs hdv, the flags parameter has been overloaded. Flags are placed in the lower
// 16 bits, while the shared memory size in megabytes are placed in the upper 16 bits.
static constexpr auto VIRTIO_FS_FLAGS_SHMEM_SIZE_SHIFT = 16;
UINT32 flags = (SizeMb << VIRTIO_FS_FLAGS_SHMEM_SIZE_SHIFT);
WI_SetFlag(flags, VIRTIO_FS_FLAGS_TYPE_SECTIONS);
(void)AddHdvShare(VIRTIO_VIRTIOFS_DEVICE_ID, ImplementationClsid, Tag, objectLifetime.Path.c_str(), flags, m_userToken.get());
m_objectDirectories.emplace_back(std::move(objectLifetime));
}
ULONG
WslCoreVm::MountFileAsPersistentMemory(_In_ const GUID& ImplementationClsid, _In_ PCWSTR FilePath, _In_ bool ReadOnly)
WslCoreVm::MountFileAsPersistentMemory(_In_ PCWSTR FilePath, _In_ bool ReadOnly)
{
hcs::Plan9ShareFlags flags{};
@ -2146,7 +2013,7 @@ WslCoreVm::MountFileAsPersistentMemory(_In_ const GUID& ImplementationClsid, _In
// a symlink that points to a path like:
// /sys/devices/LNXSYSTM:00/LNXSYBUS:00/ACPI0004:00/VMBUS:00/<GUID>/pcicceb:00//cceb:00:00.0/virtio1/ndbus0/region0/namespace0.0/block/pmem0
// Notice the GUID in the middle of that path. That GUID is the instance ID, which is randomly
// generated by AddHdvShare. So once we find a path with the instance ID, we know that
// generated by AddGuestDevice. So once we find a path with the instance ID, we know that
// eventually /dev/pmemX will appear in the guest.
auto persistentMemoryLock = m_persistentMemoryLock.lock_exclusive();
@ -2157,8 +2024,8 @@ WslCoreVm::MountFileAsPersistentMemory(_In_ const GUID& ImplementationClsid, _In
// added as part of VM creation and therefore any failure will result in VM termination
// (in which case there's no need to remove the device).
{
auto guestDeviceLock = m_guestDeviceLock.lock_exclusive();
(void)AddHdvShare(VIRTIO_PMEM_DEVICE_ID, ImplementationClsid, L"", FilePath, static_cast<UINT32>(flags), m_userToken.get());
(void)m_guestDeviceManager->AddGuestDevice(
VIRTIO_PMEM_DEVICE_ID, VIRTIO_PMEM_CLASS_ID, L"", nullptr, FilePath, static_cast<UINT32>(flags), m_userToken.get());
}
// Wait for the pmem device to appear in the VM at /dev/pmemX. Guess the value of X given the
@ -2207,56 +2074,6 @@ void WslCoreVm::WaitForPmemDeviceInVm(_In_ ULONG PmemId)
}
}
_Requires_lock_held_(m_guestDeviceLock)
GUID WslCoreVm::AddHdvShareWithOptions(
_In_ const GUID& DeviceId,
_In_ const GUID& ImplementationClsid,
_In_ std::wstring_view AccessName,
_In_ std::wstring_view Options,
_In_ std::wstring_view Path,
_In_ UINT32 Flags,
_In_ HANDLE UserToken)
{
wil::com_ptr<IPlan9FileSystem> server;
THROW_HR_IF(E_NOTIMPL, !m_vmConfig.EnableVirtio);
// Options are appended to the name with a semi-colon separator.
// "name;key1=value1;key2=value2"
// The AddSharePath implementation is responsible for separating them out and interpreting them.
std::wstring nameWithOptions{AccessName};
if (!Options.empty())
{
nameWithOptions += L";";
nameWithOptions += Options;
}
{
auto revert = wil::impersonate_token(UserToken);
server = m_deviceHostSupport->GetRemoteFileSystem(ImplementationClsid, c_defaultTag);
if (!server)
{
server = CreateComServerAsUser<IPlan9FileSystem>(ImplementationClsid, UserToken);
m_deviceHostSupport->AddRemoteFileSystem(ImplementationClsid, c_defaultTag, server);
}
const std::wstring SharePath(Path);
THROW_IF_FAILED(server->AddSharePath(nameWithOptions.c_str(), SharePath.c_str(), Flags));
}
// This requires more privileges than the user may have, so impersonation is disabled.
const std::wstring VirtioTag(AccessName);
return m_deviceHostSupport->AddNewDevice(DeviceId, server, VirtioTag.c_str());
}
_Requires_lock_held_(m_guestDeviceLock)
GUID WslCoreVm::AddHdvShare(
_In_ const GUID& DeviceId, _In_ const GUID& ImplementationClsid, _In_ PCWSTR AccessName, _In_ PCWSTR Path, _In_ UINT32 Flags, _In_ HANDLE UserToken)
{
return AddHdvShareWithOptions(DeviceId, ImplementationClsid, AccessName, {}, Path, Flags, UserToken);
}
_Requires_lock_held_(m_guestDeviceLock)
std::wstring WslCoreVm::AddVirtioFsShare(_In_ bool Admin, _In_ PCWSTR Path, _In_ PCWSTR Options, _In_opt_ HANDLE UserToken)
{
@ -2289,8 +2106,14 @@ std::wstring WslCoreVm::AddVirtioFsShare(_In_ bool Admin, _In_ PCWSTR Path, _In_
tag += std::to_wstring(m_virtioFsShares.size());
WI_ASSERT(!FindVirtioFsShare(tag.c_str(), Admin));
(void)AddHdvShareWithOptions(
VIRTIO_VIRTIOFS_DEVICE_ID, Admin ? c_virtiofsAdminClassId : c_virtiofsClassId, tag, key.OptionsString(), sharePath, VIRTIO_FS_FLAGS_TYPE_FILES, UserToken);
(void)m_guestDeviceManager->AddGuestDevice(
VIRTIO_VIRTIOFS_DEVICE_ID,
Admin ? c_virtiofsAdminClassId : c_virtiofsClassId,
tag.c_str(),
key.OptionsString().c_str(),
sharePath.c_str(),
VIRTIO_FS_FLAGS_TYPE_FILES,
UserToken);
m_virtioFsShares.emplace(std::move(key), tag);
created = true;
@ -2640,7 +2463,7 @@ std::pair<int, LX_MINI_MOUNT_STEP> WslCoreVm::UnmountVolume(_In_ const AttachedD
}
_Requires_lock_held_(m_guestDeviceLock)
void WslCoreVm::VerifyDrvFsServers()
void WslCoreVm::VerifyPlan9Servers()
{
for (auto it = m_plan9Servers.begin(); it != m_plan9Servers.end();)
{
@ -2810,12 +2633,6 @@ LX_INIT_DRVFS_MOUNT WslCoreVm::s_InitializeDrvFs(_Inout_ WslCoreVm* VmContext, _
}
}
bool WslCoreVm::IsVhdAttached(_In_ PCWSTR VhdPath)
{
auto lock = m_lock.lock_exclusive();
return m_attachedDisks.contains({DiskType::VHD, VhdPath});
}
void CALLBACK WslCoreVm::s_OnExit(_In_ HCS_EVENT* Event, _In_opt_ void* Context)
try
{

View File

@ -29,6 +29,7 @@ Abstract:
#include "INetworkingEngine.h"
#include "SocketChannel.h"
#include "DeviceHostProxy.h"
#include "GuestDeviceManager.h"
#define UTILITY_VM_SHUTDOWN_TIMEOUT (30 * 1000)
#define UTILITY_VM_TERMINATE_TIMEOUT (30 * 1000)
@ -114,10 +115,8 @@ public:
ReadOnly = 0x1
};
void MountSharedMemoryDevice(_In_ const GUID& ImplementationClsid, _In_ PCWSTR Tag, _In_ PCWSTR Path, _In_ UINT32 SizeMb);
ULONG
MountFileAsPersistentMemory(_In_ const GUID& ImplementationClsid, _In_ PCWSTR FilePath, _In_ bool ReadOnly);
MountFileAsPersistentMemory(_In_ PCWSTR FilePath, _In_ bool ReadOnly);
void MountRootNamespaceFolder(_In_ LPCWSTR HostPath, _In_ LPCWSTR GuestPath, _In_ bool ReadOnly, _In_ LPCWSTR Name);
@ -129,7 +128,7 @@ public:
void SaveAttachedDisksState();
_Requires_lock_held_(m_guestDeviceLock)
void VerifyDrvFsServers();
void VerifyPlan9Servers();
enum DiskStateFlags
{
@ -166,14 +165,6 @@ private:
DiskStateFlags Flags;
};
struct DirectoryObjectLifetime
{
std::wstring Path;
// Directory objects are temporary, even if they have children, so need to keep
// any created handles open in order for the directory to remain accessible.
std::vector<wil::unique_handle> HierarchyLifetimes;
};
struct VirtioFsShare
{
VirtioFsShare(PCWSTR Path, PCWSTR Options, bool Admin);
@ -196,19 +187,6 @@ private:
_Requires_lock_held_(m_guestDeviceLock)
void AddPlan9Share(_In_ PCWSTR AccessName, _In_ PCWSTR Path, _In_ UINT32 Port, _In_ wsl::windows::common::hcs::Plan9ShareFlags Flags, _In_ HANDLE UserToken, _In_ PCWSTR VirtIoTag);
_Requires_lock_held_(m_guestDeviceLock)
GUID AddHdvShare(_In_ const GUID& DeviceId, _In_ const GUID& ImplementationClsid, _In_ PCWSTR AccessName, _In_opt_ PCWSTR Path, _In_ UINT32 Flags, _In_ HANDLE UserToken);
_Requires_lock_held_(m_guestDeviceLock)
GUID AddHdvShareWithOptions(
_In_ const GUID& DeviceId,
_In_ const GUID& ImplementationClsid,
_In_ std::wstring_view AccessName,
_In_ std::wstring_view Options,
_In_ std::wstring_view Path,
_In_ UINT32 Flags,
_In_ HANDLE UserToken);
_Requires_lock_held_(m_guestDeviceLock)
std::wstring AddVirtioFsShare(_In_ bool Admin, _In_ PCWSTR Path, _In_ PCWSTR Options, _In_opt_ HANDLE UserToken = nullptr);
@ -217,19 +195,6 @@ private:
void CollectCrashDumps(wil::unique_socket&& socket) const;
template <typename Interface>
wil::com_ptr_t<Interface> CreateComServerAsUser(_In_ REFCLSID RefClsId, _In_ HANDLE UserToken)
{
auto revert = wil::impersonate_token(UserToken);
return wil::CoCreateInstance<Interface>(RefClsId, (CLSCTX_LOCAL_SERVER | CLSCTX_ENABLE_CLOAKING | CLSCTX_ENABLE_AAA));
}
template <typename Class, typename Interface>
wil::com_ptr_t<Interface> CreateComServerAsUser(_In_ HANDLE UserToken)
{
return CreateComServerAsUser<Interface>(__uuidof(Class), UserToken);
}
std::shared_ptr<LxssRunningInstance> CreateInstanceInternal(
_In_ const GUID& InstanceId,
_In_ const LXSS_DISTRO_CONFIGURATION& Configuration,
@ -239,8 +204,6 @@ private:
_In_ bool LaunchSystemDistro = false,
_Out_opt_ ULONG* ConnectPort = nullptr);
DirectoryObjectLifetime CreateSectionObjectRoot(_In_ std::wstring_view RelativeRootPath, _In_ HANDLE UserToken) const;
_Requires_lock_held_(m_lock)
void EjectVhdLockHeld(_In_ PCWSTR VhdPath);
@ -269,9 +232,6 @@ private:
DiskMountResult MountDiskLockHeld(
_In_ PCWSTR Disk, _In_ DiskType MountDiskType, _In_ ULONG PartitionIndex, _In_opt_ PCWSTR Name, _In_opt_ PCWSTR Type, _In_opt_ PCWSTR Options);
_Requires_lock_held_(m_guestDeviceLock)
void MountSharedMemoryDeviceLockHeld(_In_ const GUID& ImplementationClsid, _In_ PCWSTR Tag, _In_ PCWSTR Path, _In_ UINT32 SizeMb);
void WaitForPmemDeviceInVm(_In_ ULONG PmemId);
void OnCrash(_In_ LPCWSTR Details);
@ -302,12 +262,14 @@ private:
static void CALLBACK s_OnExit(_In_ HCS_EVENT* Event, _In_opt_ void* Context);
wil::srwlock m_lock;
wil::srwlock m_guestDeviceLock;
std::shared_ptr<GuestDeviceManager> m_guestDeviceManager;
_Guarded_by_(m_guestDeviceLock) std::future<bool> m_drvfsInitialResult;
_Guarded_by_(m_guestDeviceLock) wil::unique_handle m_drvfsToken;
_Guarded_by_(m_guestDeviceLock) wil::unique_handle m_adminDrvfsToken;
_Guarded_by_(m_guestDeviceLock) std::map<VirtioFsShare, std::wstring> m_virtioFsShares;
_Guarded_by_(m_guestDeviceLock) std::map<UINT32, wil::com_ptr<IPlan9FileSystem>> m_plan9Servers;
wil::srwlock m_lock;
_Guarded_by_(m_lock) wil::unique_event m_terminatingEvent { wil::EventOptions::ManualReset };
_Guarded_by_(m_lock) wil::unique_event m_vmExitEvent { wil::EventOptions::ManualReset };
wil::unique_event m_vmCrashEvent{wil::EventOptions::ManualReset};
@ -339,12 +301,9 @@ private:
wsl::shared::SocketChannel m_miniInitChannel;
wil::unique_socket m_notifyChannel;
SE_SID m_userSid;
Microsoft::WRL::ComPtr<DeviceHostProxy> m_deviceHostSupport;
std::shared_ptr<LxssRunningInstance> m_systemDistro;
_Guarded_by_(m_lock) std::bitset<MAX_VHD_COUNT> m_lunBitmap;
_Guarded_by_(m_lock) std::map<AttachedDisk, DiskState> m_attachedDisks;
_Guarded_by_(m_guestDeviceLock) std::map<UINT32, wil::com_ptr<IPlan9FileSystem>> m_plan9Servers;
_Guarded_by_(m_guestDeviceLock) std::vector<DirectoryObjectLifetime> m_objectDirectories;
std::tuple<std::uint32_t, std::uint32_t, std::uint32_t> m_kernelVersion;
std::wstring m_kernelVersionString;
bool m_seccompAvailable;
@ -363,8 +322,6 @@ private:
_Guarded_by_(m_persistentMemoryLock) ULONG m_nextPersistentMemoryId = 0;
std::unique_ptr<wsl::core::INetworkingEngine> m_networkingEngine;
static const std::wstring c_defaultTag;
};
DEFINE_ENUM_FLAG_OPERATORS(WslCoreVm::DiskStateFlags);

View File

@ -159,7 +159,6 @@ cpp_quote("const GUID CLSID_LxssUserSessionInBox = {0x4f476546, 0xb412, 0x4579,
cpp_quote("#ifdef __cplusplus")
cpp_quote("class DECLSPEC_UUID(\"a9b7a1b9-0671-405c-95f1-e0612cb4ce7e\") LxssUserSession;")
cpp_quote("class DECLSPEC_UUID(\"4f476546-b412-4579-b64c-123df331e3d6\") LxssUserSessionInBox;")
cpp_quote("class DECLSPEC_UUID(\"a9b7a1b9-0671-405c-95f1-e0612cb4ce8f\") WSLAUserSession;")
cpp_quote("#endif")

View File

@ -66,7 +66,6 @@ CATCH_RETURN();
Microsoft::WRL::ComPtr<WSLAContainer> WSLAContainer::Create(const WSLA_CONTAINER_OPTIONS& containerOptions, WSLAVirtualMachine& parentVM)
{
bool hasStdin = false;
bool hasTty = false;
for (size_t i = 0; i < containerOptions.InitProcessOptions.FdsCount; i++)

View File

@ -44,6 +44,7 @@ public:
private:
ServiceRunningProcess m_containerProcess;
WSLAVirtualMachine* m_parentVM = nullptr;
std::string m_id;
static std::vector<std::string> PrepareNerdctlRunCommand(const WSLA_CONTAINER_OPTIONS& options, std::vector<std::string>&& inputOptions);
};

View File

@ -250,7 +250,9 @@ void WSLAVirtualMachine::Start()
auto kernelPath = std::filesystem::path(WSL_KERNEL_PATH);
#else
auto kernelPath = std::filesystem::path(basePath) / L"tools" / LXSS_VM_MODE_KERNEL_NAME;
#endif
if constexpr (!wsl::shared::Arm64)
@ -262,11 +264,9 @@ void WSLAVirtualMachine::Start()
}
else
{
// TODO
THROW_HR(E_NOTIMPL);
auto bootThis = hcs::UefiBootEntry{};
bootThis.DeviceType = hcs::UefiBootDevice::VmbFs;
// bootThis.VmbFsRootPath = m_rootFsPath.c_str();
bootThis.VmbFsRootPath = (basePath / L"tools").c_str();
bootThis.DevicePath = L"\\" LXSS_VM_MODE_KERNEL_NAME;
bootThis.OptionalData = kernelCmdLine;
hcs::Uefi uefiSettings{};
@ -324,6 +324,15 @@ void WSLAVirtualMachine::Start()
auto socket = wsl::windows::common::hvsocket::Accept(listenSocket.get(), m_settings.BootTimeoutMs, m_vmTerminatingEvent.get());
m_initChannel = wsl::shared::SocketChannel{std::move(socket), "mini_init", m_vmTerminatingEvent.get()};
// Create a thread to watch for exited processes.
auto [__, ___, childChannel] = Fork(WSLA_FORK::Thread);
WSLA_WATCH_PROCESSES watchMessage{};
childChannel.SendMessage(watchMessage);
THROW_HR_IF(E_FAIL, childChannel.ReceiveMessage<RESULT_MESSAGE<uint32_t>>().Result != 0);
m_processExitThread = std::thread(std::bind(&WSLAVirtualMachine::WatchForExitedProcesses, this, std::move(childChannel)));
ConfigureNetworking();
// Mount the kernel modules VHD.
@ -358,15 +367,6 @@ void WSLAVirtualMachine::Start()
wsl::windows::common::hcs::ModifyComputeSystem(m_computeSystem.get(), wsl::shared::ToJsonW(gpuRequest).c_str());
}
auto [__, ___, childChannel] = Fork(WSLA_FORK::Thread);
WSLA_WATCH_PROCESSES watchMessage{};
childChannel.SendMessage(watchMessage);
THROW_HR_IF(E_FAIL, childChannel.ReceiveMessage<RESULT_MESSAGE<uint32_t>>().Result != 0);
m_processExitThread = std::thread(std::bind(&WSLAVirtualMachine::WatchForExitedProcesses, this, std::move(childChannel)));
ConfigureMounts();
}
@ -727,7 +727,8 @@ std::tuple<int32_t, int32_t, wsl::shared::SocketChannel> WSLAVirtualMachine::For
return Fork(m_initChannel, Type);
}
std::tuple<int32_t, int32_t, wsl::shared::SocketChannel> WSLAVirtualMachine::Fork(wsl::shared::SocketChannel& Channel, enum WSLA_FORK::ForkType Type)
std::tuple<int32_t, int32_t, wsl::shared::SocketChannel> WSLAVirtualMachine::Fork(
wsl::shared::SocketChannel& Channel, enum WSLA_FORK::ForkType Type, ULONG TtyRows, ULONG TtyColumns)
{
uint32_t port{};
int32_t pid{};
@ -737,8 +738,8 @@ std::tuple<int32_t, int32_t, wsl::shared::SocketChannel> WSLAVirtualMachine::For
WSLA_FORK message;
message.ForkType = Type;
message.TtyColumns = 80;
message.TtyRows = 80;
message.TtyColumns = static_cast<uint16_t>(TtyColumns);
message.TtyRows = static_cast<uint16_t>(TtyRows);
const auto& response = Channel.Transaction(message);
port = response.Port;
pid = response.Pid;
@ -802,6 +803,11 @@ CATCH_RETURN();
Microsoft::WRL::ComPtr<WSLAProcess> WSLAVirtualMachine::CreateLinuxProcess(_In_ const WSLA_PROCESS_OPTIONS& Options, int* Errno, const TPrepareCommandLine& PrepareCommandLine)
{
// N.B This check is there to prevent processes from being started before the VM is done initializing.
// to avoid potential deadlocks, since the processExitThread is required to signal the process exit events.
// std::thread::joinable() is const, so this can be called without acquiring the lock.
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_processExitThread.joinable());
auto setErrno = [Errno](int Error) {
if (Errno != nullptr)
{
@ -852,7 +858,7 @@ Microsoft::WRL::ComPtr<WSLAProcess> WSLAVirtualMachine::CreateLinuxProcess(_In_
// If this is an interactive tty, we need a relay process
if (interactiveTty)
{
auto [grandChildPid, ptyMaster, grandChildChannel] = Fork(childChannel, WSLA_FORK::Pty);
auto [grandChildPid, ptyMaster, grandChildChannel] = Fork(childChannel, WSLA_FORK::Pty, Options.TtyRows, Options.TtyColumns);
WSLA_TTY_RELAY relayMessage{};
relayMessage.TtyMaster = ptyMaster;
relayMessage.TtyInput = ttyInput->Fd;

View File

@ -82,7 +82,8 @@ private:
void OnCrash(_In_ const HCS_EVENT* Event);
std::tuple<int32_t, int32_t, wsl::shared::SocketChannel> Fork(enum WSLA_FORK::ForkType Type);
std::tuple<int32_t, int32_t, wsl::shared::SocketChannel> Fork(wsl::shared::SocketChannel& Channel, enum WSLA_FORK::ForkType Type);
std::tuple<int32_t, int32_t, wsl::shared::SocketChannel> Fork(
wsl::shared::SocketChannel& Channel, enum WSLA_FORK::ForkType Type, ULONG TtyRows = 0, ULONG TtyColumns = 0);
int32_t ExpectClosedChannelOrError(wsl::shared::SocketChannel& Channel);
ConnectedSocket ConnectSocket(wsl::shared::SocketChannel& Channel, int32_t Fd);

View File

@ -112,6 +112,8 @@ struct WSLA_PROCESS_OPTIONS
ULONG EnvironmentCount;
[unique, size_is(FdsCount)] WSLA_PROCESS_FD *Fds;
int FdsCount;
ULONG TtyRows; // Only needed when tty fd's are passed.
ULONG TtyColumns;
};
struct WSLA_VOLUME
@ -160,8 +162,8 @@ enum WSLA_CONTAINER_STATE
struct WSLA_CONTAINER
{
LPCWSTR Name;
LPCWSTR Image;
LPWSTR Name;
LPWSTR Image;
enum WSLA_CONTAINER_STATE State;
// TODO: Add creation timestamp and other fields that the command line tool might want to display.

View File

@ -327,7 +327,7 @@ class PolicyTest
const auto stop = std::chrono::steady_clock::now() + std::chrono::seconds{30};
for (;;)
{
Microsoft::WRL::ComPtr<ILxssUserSession> session;
wil::com_ptr<ILxssUserSession> session;
result = CoCreateInstance(CLSID_LxssUserSession, nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&session));
if (result == expectedResult || std::chrono::steady_clock::now() > stop)
{

View File

@ -2448,8 +2448,7 @@ Error code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND
// Validate that the shortcut is actually in the start menu
VERIFY_IS_TRUE(shortcutPath.find(startMenu) != std::string::npos);
Microsoft::WRL::ComPtr<IPersistFile> storage;
VERIFY_SUCCEEDED(shellLink->QueryInterface(IID_IPersistFile, &storage));
auto storage = shellLink.query<IPersistFile>();
VERIFY_SUCCEEDED(storage->Load(shortcutPath.c_str(), 0));