mirror of
https://github.com/microsoft/WSL.git
synced 2025-12-10 00:44:55 -06:00
Merge remote-tracking branch 'origin/feature/wsl-for-apps' into user/yaosun/sdkdraft
This commit is contained in:
commit
07d0ac0416
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,6 +6,7 @@ tmp
|
||||
/.vs/
|
||||
/.vscode/
|
||||
*.sln
|
||||
*.slnx
|
||||
*.user
|
||||
*.csproj
|
||||
*.vcxproj
|
||||
|
||||
@ -379,10 +379,10 @@ if (DEFINED WSL_DEV_BINARY_PATH) # Development shortcut to make the package smal
|
||||
WSL_KERNEL_MODULES_PATH="${WSL_DEV_BINARY_PATH}/modules.vhd"
|
||||
WSL_DEV_INSTALL_PATH="${WSL_DEV_BINARY_PATH}"
|
||||
WSL_GPU_LIB_PATH="${WSL_DEV_BINARY_PATH}/lib")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED OFFICIAL_BUILD AND ${TARGET_PLATFORM} STREQUAL "x64")
|
||||
if (NOT OFFICIAL_BUILD AND ${TARGET_PLATFORM} STREQUAL "x64")
|
||||
add_compile_definitions(WSLA_TEST_DISTRO_PATH="${WSLA_TEST_DISTRO_SOURCE_DIR}/wslatestrootfs.vhd")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Common include paths
|
||||
|
||||
1368
NOTICE.txt
1368
NOTICE.txt
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@ -132,7 +132,7 @@
|
||||
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
|
||||
</data>
|
||||
<data name="MessageDiskAlreadyAttached" xml:space="preserve">
|
||||
<value>该磁盘“{}”已连接。</value>
|
||||
<value>磁盘“{}”已连接。</value>
|
||||
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
|
||||
</data>
|
||||
<data name="MessageDiskAlreadyMounted" xml:space="preserve">
|
||||
@ -683,7 +683,7 @@ Windows: {}</value>
|
||||
<value>正在导入,这可能需要几分钟时间。</value>
|
||||
</data>
|
||||
<data name="GuiApplicationsDisabled" xml:space="preserve">
|
||||
<value>通过 {} 或 /etc/wsl.conf 禁用 GUI 应用程序支持。</value>
|
||||
<value>已通过 {} 或 /etc/wsl.conf 禁用 GUI 应用程序支持。</value>
|
||||
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
|
||||
</data>
|
||||
<data name="MessageCheckingForUpdates" xml:space="preserve">
|
||||
@ -822,11 +822,11 @@ Windows: {}</value>
|
||||
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
|
||||
</data>
|
||||
<data name="MessageInvalidInstallDistributionName" xml:space="preserve">
|
||||
<value>分发名称无效:“{}”。</value>
|
||||
<value>无效分发名称:“{}”。</value>
|
||||
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
|
||||
</data>
|
||||
<data name="MessageUsingLegacyDistribution" xml:space="preserve">
|
||||
<value>使用旧分发注册。请考虑改用基于 tar 的分发。</value>
|
||||
<value>正在使用旧分发注册。请考虑改用基于 tar 的分发。</value>
|
||||
</data>
|
||||
<data name="MessageLegacyDistributionVersionArgNotSupported" xml:space="preserve">
|
||||
<value>旧分发注册不支持 --version 参数。</value>
|
||||
@ -864,7 +864,7 @@ Windows: {}</value>
|
||||
<value>此计算机上不支持嵌套虚拟化。</value>
|
||||
</data>
|
||||
<data name="MessageFailedToCreateNetworkEndpoint" xml:space="preserve">
|
||||
<value>无法创建地址为“{}”的网络终结点,已分配新地址:“{}”</value>
|
||||
<value>无法创建地址为“{}”的网络端点,已分配新地址:“{}”</value>
|
||||
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
|
||||
</data>
|
||||
<data name="MessageFailedToCreateNetwork" xml:space="preserve">
|
||||
@ -905,10 +905,10 @@ Windows: {}</value>
|
||||
<value>检测到 IPv6 代理配置,但未镜像到 WSL。NAT 模式下的 WSL 不支持 IPv6。</value>
|
||||
</data>
|
||||
<data name="MessageProxyLoopbackV6SettingsDropped" xml:space="preserve">
|
||||
<value>检测到 localhost IPv6 代理配置,但未镜像到 WSL 中。WSL 不支持 localhost IPv6 代理。</value>
|
||||
<value>检测到 localhost IPv6 代理配置,但未镜像到 WSL。WSL 不支持 localhost IPv6 代理。</value>
|
||||
</data>
|
||||
<data name="MessageProxyUnexpectedSettingsDropped" xml:space="preserve">
|
||||
<value>尝试解析代理设置时发生意外错误,代理设置未镜像到 WSL 中。</value>
|
||||
<value>尝试解析代理设置时发生意外错误,代理设置未镜像到 WSL。</value>
|
||||
</data>
|
||||
<data name="MessageRegistryError" xml:space="preserve">
|
||||
<value>访问注册表时出错。路径:“{}”。错误: {}</value>
|
||||
@ -947,14 +947,14 @@ Windows: {}</value>
|
||||
<comment>{FixedPlaceholder="{}"}{Locked=".wslconfig"}Command line arguments, file names and string inserts should not be translated</comment>
|
||||
</data>
|
||||
<data name="MessageDebugShellDisabled" xml:space="preserve">
|
||||
<value>计算机策略禁用调试 shell。</value>
|
||||
<value>计算机策略已禁用调试 shell。</value>
|
||||
</data>
|
||||
<data name="MessageWSLMountDisabled" xml:space="preserve">
|
||||
<value>wsl.exe --mount 被计算机策略禁用。</value>
|
||||
<comment>{Locked="--mount "}Command line arguments, file names and string inserts should not be translated</comment>
|
||||
</data>
|
||||
<data name="MessageWSL1Disabled" xml:space="preserve">
|
||||
<value>WSL1 被计算机策略禁用。</value>
|
||||
<value>计算机策略已禁用 WSL1。</value>
|
||||
</data>
|
||||
<data name="MessageUpgradeToWSL2" xml:space="preserve">
|
||||
<value>请运行“wsl.exe --set-version {} 2”以升级到 WSL2。</value>
|
||||
@ -1070,7 +1070,7 @@ Windows: {}</value>
|
||||
<comment>{Locked="--vhd "}Command line arguments, file names and string inserts should not be translated</comment>
|
||||
</data>
|
||||
<data name="MessageDistroStoreInstallFailed" xml:space="preserve">
|
||||
<value>未能从以下Microsoft Store安装 {}: {}
|
||||
<value>未能从 Microsoft Store 安装 {}:{}
|
||||
正在尝试 Web 下载...</value>
|
||||
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
|
||||
</data>
|
||||
@ -1082,7 +1082,7 @@ Windows: {}</value>
|
||||
</data>
|
||||
<data name="MessageInstallationCorrupted" xml:space="preserve">
|
||||
<value>WSL 安装似乎已损坏 (错误代码: {})。
|
||||
按任意键修复 WSL,或 CTRL-C 取消。
|
||||
按任意键修复 WSL,或按 CTRL-C 取消。
|
||||
此提示将在 60 秒后超时。</value>
|
||||
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
|
||||
</data>
|
||||
@ -1114,7 +1114,7 @@ Windows: {}</value>
|
||||
<value>提供的自定义内核模块未指定自定义内核。有关详细信息,请参阅 https://aka.ms/wslcustomkernel。</value>
|
||||
</data>
|
||||
<data name="MessageDnsTunnelingDisabled" xml:space="preserve">
|
||||
<value>由于当前与全球安全访问客户端的兼容性问题,DNS 隧道被禁用。</value>
|
||||
<value>由于当前与全局安全访问客户端的兼容性问题,DNS 隧道被禁用。</value>
|
||||
</data>
|
||||
<data name="MessageSparseVhdDisabled" xml:space="preserve">
|
||||
<value>由于潜在的数据损坏,目前已禁用稀疏 VHD 支持。
|
||||
@ -1127,7 +1127,7 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
|
||||
</data>
|
||||
<data name="MessageFstabMountFailed" xml:space="preserve">
|
||||
<value>使用装载 -a 处理 /etc/fstab 失败。</value>
|
||||
<value>使用 mount -a 处理 /etc/fstab 失败。</value>
|
||||
</data>
|
||||
<data name="MessageReadOnlyDistro" xml:space="preserve">
|
||||
<value>装载分发磁盘时出错,该磁盘作为回退以只读方式装载。
|
||||
@ -1138,7 +1138,7 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
|
||||
</data>
|
||||
<data name="Settings_ErrorTryAgainLater.Text" xml:space="preserve">
|
||||
<value>出现错误。请稍后再试一次。</value>
|
||||
<value>出现错误。请稍后再试。</value>
|
||||
</data>
|
||||
<data name="Settings_AboutPageTitle.Text" xml:space="preserve">
|
||||
<value>关于</value>
|
||||
@ -1160,28 +1160,28 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>自动内存回收</value>
|
||||
</data>
|
||||
<data name="Settings_AutoMemoryReclaim.Description" xml:space="preserve">
|
||||
<value>检测到空闲 CPU 使用情况后自动释放缓存内存。为缓存的即时释放设置为慢速释放和 dropcache。</value>
|
||||
<value>检测到空闲 CPU 使用情况后自动释放缓存内存。设为 gradual 缓慢释放;设为 dropcache 迅速释放。</value>
|
||||
</data>
|
||||
<data name="Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name" xml:space="preserve">
|
||||
<value>自动内存回收。</value>
|
||||
</data>
|
||||
<data name="Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText" xml:space="preserve">
|
||||
<value>检测到空闲 CPU 使用情况后自动释放缓存内存。为缓存的即时释放设置为慢速释放和 dropcache。</value>
|
||||
<value>检测到空闲 CPU 使用情况后自动释放缓存内存。设为 gradual 缓慢释放;设为 dropcache 迅速释放。</value>
|
||||
</data>
|
||||
<data name="Settings_AutoProxy.Header" xml:space="preserve">
|
||||
<value>已启用自动代理</value>
|
||||
</data>
|
||||
<data name="Settings_AutoProxy.Description" xml:space="preserve">
|
||||
<value>使 WSL 能够使用 Windows 的 HTTP 代理信息。</value>
|
||||
<value>允许 WSL 使用 Windows 的 HTTP 代理信息。</value>
|
||||
</data>
|
||||
<data name="Settings_AutoProxyToggleSwitch.AutomationProperties.Name" xml:space="preserve">
|
||||
<value>已启用自动代理。</value>
|
||||
</data>
|
||||
<data name="Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText" xml:space="preserve">
|
||||
<value>使 WSL 能够使用 Windows 的 HTTP 代理信息。</value>
|
||||
<value>允许 WSL 使用 Windows 的 HTTP 代理信息。</value>
|
||||
</data>
|
||||
<data name="Settings_BestEffortDNS.Header" xml:space="preserve">
|
||||
<value>尽力进行 DNS 分析</value>
|
||||
<value>尽力进行 DNS 解析</value>
|
||||
</data>
|
||||
<data name="Settings_BestEffortDNS.Description" xml:space="preserve">
|
||||
<value>仅当 wsl2.dnsTunneling 设置为 true 时适用。设置为 true 时,Windows 将从 DNS 请求中提取问题并尝试解决该问题,忽略未知记录。</value>
|
||||
@ -1191,7 +1191,7 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>尽力进行 DNS 分析。</value>
|
||||
</data>
|
||||
<data name="Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText" xml:space="preserve">
|
||||
<value>仅当 wsl2.dnsTunneling 设置为 true 时才适用。设置为 true 时,Windows 将从 DNS 请求中提取问题并尝试解决该问题,忽略未知记录。</value>
|
||||
<value>仅当 wsl2.dnsTunneling 设置为 true 时适用。设置为 true 时,Windows 将从 DNS 请求中提取问题并尝试解决该问题,忽略未知记录。</value>
|
||||
</data>
|
||||
<data name="Settings_CustomKernelPath.Header" xml:space="preserve">
|
||||
<value>自定义内核</value>
|
||||
@ -1230,7 +1230,7 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>浏览发行版</value>
|
||||
</data>
|
||||
<data name="Settings_CustomSystemDistroPathDescription.Text" xml:space="preserve">
|
||||
<value>指定 VHD 的路径,该路径将作为自定义系统发行版加载,主要用于为 WSL 中的 GUI 应用提供电源。[在此处详细了解系统发行]。</value>
|
||||
<value>指定一个 VHD 路径,该 VHD 将作为自定义系统发行版加载,主要用于在 WSL 中运行 GUI 应用。[在此处详细了解系统发行]。</value>
|
||||
<comment>{Locked="["}{Locked="]"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>
|
||||
</data>
|
||||
<data name="Settings_CustomSystemDistroPathDescription.NavigateUri" xml:space="preserve">
|
||||
@ -1241,7 +1241,7 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>自定义系统发行版</value>
|
||||
</data>
|
||||
<data name="Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText" xml:space="preserve">
|
||||
<value>指定 VHD 的路径,该路径将作为自定义系统发行版加载,主要用于为 WSL 中的 GUI 应用提供电源。</value>
|
||||
<value>指定一个 VHD 路径,该 VHD 将作为自定义系统发行版加载,主要用于在 WSL 中运行 GUI 应用。</value>
|
||||
</data>
|
||||
<data name="Settings_DebugConsole.Header" xml:space="preserve">
|
||||
<value>启用调试控制台</value>
|
||||
@ -1259,7 +1259,7 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>默认 VHD 大小</value>
|
||||
</data>
|
||||
<data name="Settings_DefaultVHDSize.Description" xml:space="preserve">
|
||||
<value>对于新创建的分发,可扩展 WSL 虚拟硬盘 (VHD) 的默认最大大小。</value>
|
||||
<value>为可扩展的 WSL 虚拟硬盘指定的默认最大大小 (VHD),仅用于新创建的分发。</value>
|
||||
</data>
|
||||
<data name="Settings_DefaultVHDSizeResetButton.Content" xml:space="preserve">
|
||||
<value>重设大小</value>
|
||||
@ -1268,7 +1268,7 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>默认 VHD 大小</value>
|
||||
</data>
|
||||
<data name="Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText" xml:space="preserve">
|
||||
<value>为可扩展的 WSL 虚拟硬盘指定的默认最大大小 (VHD) 仅用于新创建的分发。</value>
|
||||
<value>为可扩展的 WSL 虚拟硬盘指定的默认最大大小 (VHD),仅用于新创建的分发。</value>
|
||||
</data>
|
||||
<data name="Settings_DeveloperPageTitle.Text" xml:space="preserve">
|
||||
<value>开发人员</value>
|
||||
@ -1284,20 +1284,20 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>更改 DrvFS 模式</value>
|
||||
</data>
|
||||
<data name="Settings_DrvFSMode.Description" xml:space="preserve">
|
||||
<value>更改 WSL 中的跨 OS 文件访问实施。</value>
|
||||
<value>更改 WSL 中的跨系统文件访问实现。</value>
|
||||
</data>
|
||||
<data name="Settings_DNSProxy.Header" xml:space="preserve">
|
||||
<value>已启用 DNS 代理</value>
|
||||
</data>
|
||||
<data name="Settings_DNSProxy.Description" xml:space="preserve">
|
||||
<value>仅当 wsl2.networkingMode 设置为 NAT 时适用。布尔值通知 WSL 将 Linux 中的 DNS 服务器配置为主机上的 NAT。设置为 false 将镜像从 Windows 到 Linux 的 DNS 服务器。</value>
|
||||
<value>仅当 wsl2.networkingMode 设置为 NAT 时适用。用于通知 WSL 将 Linux 中的 DNS 服务器配置为主机上的 NAT 的布尔值。设置为 false 将把 Windows 中 DNS 服务器镜像到 Linux。</value>
|
||||
<comment>{Locked="wsl2.networkingMode"}.wslconfig property key names should not be translated</comment>
|
||||
</data>
|
||||
<data name="Settings_DNSProxyToggleSwitch.AutomationProperties.Name" xml:space="preserve">
|
||||
<value>已启用 DNS 代理。</value>
|
||||
</data>
|
||||
<data name="Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText" xml:space="preserve">
|
||||
<value>仅当 wsl2.networkingMode 设置为 NAT 时适用。布尔值通知 WSL 将 Linux 中的 DNS 服务器配置为主机上的 NAT。设置为 false 将镜像从 Windows 到 Linux 的 DNS 服务器。</value>
|
||||
<value>仅当 wsl2.networkingMode 设置为 NAT 时适用。用于通知 WSL 将 Linux 中的 DNS 服务器配置为主机上的 NAT 的布尔值。设置为 false 将把 Windows 中 DNS 服务器镜像到 Linux。</value>
|
||||
</data>
|
||||
<data name="Settings_DNSTunneling.Header" xml:space="preserve">
|
||||
<value>已启用 DNS 隧道</value>
|
||||
@ -1358,20 +1358,20 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>主机地址环回</value>
|
||||
</data>
|
||||
<data name="Settings_HostAddressLoopback.Description" xml:space="preserve">
|
||||
<value>仅当 wsl2.networkingMode 设置为镜像时适用。设置为 True 时,将允许容器通过分配给主机的 IP 地址连接到主机或主机连接到容器。请注意,始终可以使用 127.0.0.1 环回地址 - 此选项还允许使用所有额外分配的本地 IP 地址。</value>
|
||||
<value>仅当 wsl2.networkingMode 设置为镜像(mirrored)时适用。设置为 True 时,将允许容器与主机通过分配给主机的 IP 地址互相连接。请注意,始终可以使用 127.0.0.1 环回地址 - 此选项还允许使用所有额外分配的本地 IP 地址。</value>
|
||||
<comment>{Locked="wsl2.networkingMode"}.wslconfig property key names should not be translated</comment>
|
||||
</data>
|
||||
<data name="Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name" xml:space="preserve">
|
||||
<value>主机地址环回。</value>
|
||||
</data>
|
||||
<data name="Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText" xml:space="preserve">
|
||||
<value>仅当 wsl2.networkingMode 设置为镜像时适用。设置为 True 时,将允许容器通过分配给主机的 IP 地址连接到主机或主机连接到容器。请注意,始终可以使用 127.0.0.1 环回地址 - 此选项还允许使用所有额外分配的本地 IP 地址。</value>
|
||||
<value>仅当 wsl2.networkingMode 设置为镜像(mirrored)时适用。设置为 True 时,将允许容器与主机通过分配给主机的 IP 地址互相连接。请注意,始终可以使用 127.0.0.1 环回地址 - 此选项还允许使用所有额外分配的本地 IP 地址。</value>
|
||||
</data>
|
||||
<data name="Settings_IgnoredPorts.Header" xml:space="preserve">
|
||||
<value>忽略的端口</value>
|
||||
</data>
|
||||
<data name="Settings_IgnoredPorts.Description" xml:space="preserve">
|
||||
<value>仅当 wsl2.networkingMode 设置为镜像时适用。指定 Linux 应用程序可以绑定到的端口,不会在 Windows 中自动转发或考虑这些端口。应以逗号分隔的列表格式化,例如: 3000,9000,9090。</value>
|
||||
<value>仅当 wsl2.networkingMode 设置为镜像(mirrored)时适用。指定 Linux 应用程序可以绑定的端口,这些端口不会 Windows 自动转发或考虑。应采用逗号分隔的列表格式,例如:3000,9000,9090。</value>
|
||||
<comment>{Locked="wsl2.networkingMode"}.wslconfig property key names should not be translated</comment>
|
||||
</data>
|
||||
<data name="Settings_IgnoredPortsResetButton.Content" xml:space="preserve">
|
||||
@ -1381,13 +1381,13 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>忽略的端口</value>
|
||||
</data>
|
||||
<data name="Settings_IgnoredPortsTextBox.AutomationProperties.HelpText" xml:space="preserve">
|
||||
<value>仅当 wsl2.networkingMode 设置为镜像时适用。指定 Linux 应用程序可以绑定到的端口,不会在 Windows 中自动转发或考虑这些端口。应以逗号分隔的列表格式化,例如 3000、9000、9090。</value>
|
||||
<value>仅当 wsl2.networkingMode 设置为镜像(mirrored)时适用。指定 Linux 应用程序可以绑定的端口,这些端口不会在 Windows 中自动转发或考虑。应采用逗号分隔的列表格式,例如 3000,9000,9090。</value>
|
||||
</data>
|
||||
<data name="Settings_InitialAutoProxyTimeout.Header" xml:space="preserve">
|
||||
<value>初始自动代理超时</value>
|
||||
</data>
|
||||
<data name="Settings_InitialAutoProxyTimeout.Description" xml:space="preserve">
|
||||
<value>仅当 wsl2.autoProxy 设置为 true 时适用。配置在启动 WSL 容器时 WSL 等待检索 HTTP 代理信息) (毫秒。如果在此时间后解析代理设置,则必须重新启动 WSL 实例才能使用检索到的代理设置。</value>
|
||||
<value>仅当 wsl2.autoProxy 设置为 true 时适用。配置启动 WSL 容器时 WSL 等待检索 HTTP 代理信息的时间(以毫秒计)。如果在此时间后解析到代理设置,则必须重新启动 WSL 实例才能使用检索到的代理设置。</value>
|
||||
<comment>{Locked="wsl2.autoProxy"}.wslconfig property key names should not be translated</comment>
|
||||
</data>
|
||||
<data name="Settings_InitialAutoProxyTimeoutResetButton.Content" xml:space="preserve">
|
||||
@ -1397,25 +1397,25 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>初始自动代理超时</value>
|
||||
</data>
|
||||
<data name="Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText" xml:space="preserve">
|
||||
<value>仅当 wsl2.autoProxy 设置为 true 时才适用。配置启动 WSL 容器时 WSL 等待检索 HTTP 代理信息的时间,以毫秒为单位。如果在此时间后解析代理设置,则必须重新启动 WSL 实例才能使用检索到的代理设置。</value>
|
||||
<value>仅当 wsl2.autoProxy 设置为 true 时才适用。配置启动 WSL 容器时 WSL 等待检索 HTTP 代理信息的时间,以毫秒为单位。如果在此时间后解析到代理设置,则必须重新启动 WSL 实例才能使用检索到的代理设置。</value>
|
||||
</data>
|
||||
<data name="Settings_KernelCommandLine.Header" xml:space="preserve">
|
||||
<value>内核命令行</value>
|
||||
</data>
|
||||
<data name="Settings_KernelCommandLine.Description" xml:space="preserve">
|
||||
<value>其他内核命令行参数。</value>
|
||||
<value>额外的内核命令行参数。</value>
|
||||
</data>
|
||||
<data name="Settings_LocalhostForwarding.Header" xml:space="preserve">
|
||||
<value>启用 localhost 转发</value>
|
||||
</data>
|
||||
<data name="Settings_LocalhostForwarding.Description" xml:space="preserve">
|
||||
<value>布尔值,指定是否应通过 localhost:port 从主机连接到 WSL 2 VM 中通配符或 localhost 的端口。</value>
|
||||
<value>布尔值,用于指定 WSL 2 VM 中绑定通配符或本地主机的端口是否应允许主机通过`localhost:端口号`进行连接。</value>
|
||||
</data>
|
||||
<data name="Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name" xml:space="preserve">
|
||||
<value>启用 localhost 转发。</value>
|
||||
</data>
|
||||
<data name="Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText" xml:space="preserve">
|
||||
<value>一个布尔值,指定绑定到 WSL 2 VM 中的通配符或 localhost 的端口是否应可通过 localhost、colon、port 从主机连接。</value>
|
||||
<value>布尔值,用于指定 WSL 2 VM 中绑定通配符或本地主机的端口是否应允许主机通过`localhost:端口号`进行连接。</value>
|
||||
</data>
|
||||
<data name="Settings_MegabyteStringFormat" xml:space="preserve">
|
||||
<value>{0} MB</value>
|
||||
@ -1445,13 +1445,13 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>启用嵌套虚拟化</value>
|
||||
</data>
|
||||
<data name="Settings_NestedVirtualization.Description" xml:space="preserve">
|
||||
<value>用于打开或关闭嵌套虚拟化的布尔值,使其他嵌套 VM 能够在 WSL 2 内运行。</value>
|
||||
<value>用于打开或关闭嵌套虚拟化的布尔值,使其他嵌套虚拟机能够在 WSL 2 内运行。</value>
|
||||
</data>
|
||||
<data name="Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name" xml:space="preserve">
|
||||
<value>启用嵌套虚拟化。</value>
|
||||
</data>
|
||||
<data name="Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText" xml:space="preserve">
|
||||
<value>用于打开或关闭嵌套虚拟化的布尔值,使其他嵌套 VM 能够在 WSL 2 内运行。</value>
|
||||
<value>用于打开或关闭嵌套虚拟化的布尔值,使其他嵌套虚拟机能够在 WSL 2 内运行。</value>
|
||||
</data>
|
||||
<data name="Settings_NetworkingMode.Header" xml:space="preserve">
|
||||
<value>网络模式</value>
|
||||
@ -1472,7 +1472,7 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>你可以跨操作系统处理所有文件!</value>
|
||||
</data>
|
||||
<data name="Settings_OOBECrossOSFileAccess.Title" xml:space="preserve">
|
||||
<value>跨 OS 文件访问</value>
|
||||
<value>跨系统文件访问</value>
|
||||
</data>
|
||||
<data name="Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text" xml:space="preserve">
|
||||
<value>从 Linux 访问 Windows 文件</value>
|
||||
@ -1515,7 +1515,7 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>WSL 还包括一种称为镜像模式的新网络模式,该模式添加了 IPv6 支持等高级功能,并且能够访问局域网中的网络应用程序。</value>
|
||||
</data>
|
||||
<data name="Settings_OOBECrossOSFileAccessLink.Content" xml:space="preserve">
|
||||
<value>了解有关跨 OS 文件访问的详细信息</value>
|
||||
<value>了解有关跨系统文件访问的详细信息</value>
|
||||
</data>
|
||||
<data name="Settings_OOBECrossOSFileAccessLink.NavigateUri" xml:space="preserve">
|
||||
<value>https://aka.ms/wslfilesystems</value>
|
||||
@ -1552,7 +1552,7 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<comment>{Locked="-l -o"}Command line arguments and file names should not be translated</comment>
|
||||
</data>
|
||||
<data name="Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text" xml:space="preserve">
|
||||
<value>安装命名的 WSL 发行版命令</value>
|
||||
<value>安装指定名称的 WSL 发行版命令</value>
|
||||
</data>
|
||||
<data name="Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text" xml:space="preserve">
|
||||
<value>'wsl.exe --install <DistroName>'</value>
|
||||
@ -1568,11 +1568,11 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<data name="Settings_OOBEDockerDesktopIntegration.Description" xml:space="preserve">
|
||||
<value>Docker Desktop 非常适合与 WSL 配合使用,可帮助你使用 Linux 容器进行开发。
|
||||
|
||||
将 Docker Desktop 与 WSL 配合使用的一些优点是:
|
||||
一些将 Docker Desktop 与 WSL 配合使用的优点:
|
||||
|
||||
• 可以使用相同的 Docker 守护程序和映像在 WSL 或 Windows 中运行 Docker 命令。
|
||||
• 可以使用 WSL 中的 Windows 驱动器自动装载功能在 Windows 和 Linux 之间无缝共享文件和文件夹。
|
||||
• 由于 WSL 的互操作性,你可以使用首选的 Windows 工具和编辑器处理 Linux 代码和文件,反之亦然。</value>
|
||||
• 由于 WSL 的互操作性,你可以使用偏好的 Windows 工具和编辑器处理 Linux 代码和文件,反之亦然。</value>
|
||||
</data>
|
||||
<data name="Settings_OOBEDockerDesktopIntegration.Title" xml:space="preserve">
|
||||
<value>Docker Desktop 集成</value>
|
||||
@ -1754,7 +1754,7 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>关于</value>
|
||||
</data>
|
||||
<data name="Settings_Shell_Developer.Content" xml:space="preserve">
|
||||
<value>开发商</value>
|
||||
<value>开发人员</value>
|
||||
</data>
|
||||
<data name="Settings_Shell_DistroManagement.Content" xml:space="preserve">
|
||||
<value>发行版管理</value>
|
||||
@ -1802,7 +1802,7 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>新增功能</value>
|
||||
</data>
|
||||
<data name="Settings_Shell_WorkingAcrossFileSystems.Content" xml:space="preserve">
|
||||
<value>跨文件系统工作</value>
|
||||
<value>跨系统文件访问</value>
|
||||
</data>
|
||||
<data name="Settings_IssuesLink.Content" xml:space="preserve">
|
||||
<value>问题</value>
|
||||
@ -1857,7 +1857,7 @@ wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe</valu
|
||||
<value>启用 VirtIO</value>
|
||||
</data>
|
||||
<data name="Settings_Virtio.Description" xml:space="preserve">
|
||||
<value>使用 virtiofs 而不是计划 9 来访问主机文件,加快速度。</value>
|
||||
<value>使用 virtiofs 而不是 plan 9 来访问主机文件,加快速度。</value>
|
||||
</data>
|
||||
<data name="Settings_Virtio9p.Header" xml:space="preserve">
|
||||
<value>启用 Virtio 9p</value>
|
||||
|
||||
@ -40,6 +40,12 @@
|
||||
<File Id="system.vhd" Source="${WSLG_SOURCE_DIR}/${TARGET_PLATFORM}/system.vhd"/>
|
||||
<?endif?>
|
||||
|
||||
|
||||
<!-- Temporary runtime VHD. TODO: Update once the final VHD is available. -->
|
||||
<?if "${WSL_DEV_BINARY_PATH}" = "" AND "${TARGET_PLATFORM}" = "x64" ?>
|
||||
<File Id="wslarootfs.vhd" Name="wslarootfs.vhd" Source="${WSLA_TEST_DISTRO_SOURCE_DIR}/wslatestrootfs.vhd"/>
|
||||
<?endif?>
|
||||
|
||||
<!-- Installation folder -->
|
||||
<RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\MSI">
|
||||
<RegistryValue Name="InstallLocation" Value="[INSTALLDIR]" Type="string" />
|
||||
@ -337,6 +343,27 @@
|
||||
</RegistryKey>
|
||||
</RegistryKey>
|
||||
|
||||
<!-- wsldevicehost.dll -->
|
||||
<RegistryKey Root="HKCR" Key="AppID\{C457EA11-5486-4174-B90D-089909EDB170}">
|
||||
<RegistryValue Name="DllSurrogate" Value="" Type="string" />
|
||||
<RegistryValue Name="AppIDFlags" Value="2048" Type="integer" />
|
||||
|
||||
<!-- O:BAG:BAD:(A;;CCDCSW;;;AU)(A;;CCDCSW;;;PS)(A;;CCDCSW;;;SY) -->
|
||||
<RegistryValue Name="AccessPermission" Value="01000480580000006800000000000000140000000200440003000000000014000B00000001010000000000050B000000000014000B00000001010000000000050A000000000014000B0000000101000000000005120000000102000000000005200000002002000001020000000000052000000020020000" Type="binary" />
|
||||
<RegistryValue Name="LaunchPermission" Value="01000480580000006800000000000000140000000200440003000000000014000B00000001010000000000050B000000000014000B00000001010000000000050A000000000014000B0000000101000000000005120000000102000000000005200000002002000001020000000000052000000020020000" Type="binary" />
|
||||
</RegistryKey>
|
||||
|
||||
<!-- WslDeviceHost_VirtioNet -->
|
||||
<RegistryKey Root="HKCR" Key="CLSID\{7B3C9A42-8E1F-4D5A-9F2E-C4A7B8D3E6F1}">
|
||||
<RegistryValue Value="WslDeviceHost_Net" Type="string" />
|
||||
<RegistryValue Name="AppId" Value="{C457EA11-5486-4174-B90D-089909EDB170}" Type="string" />
|
||||
|
||||
<RegistryKey Key="InProcServer32">
|
||||
<RegistryValue Value="[INSTALLDIR]wsldevicehost.dll" Type="string" />
|
||||
<RegistryValue Name="ThreadingModel" Value="Both" Type="string" />
|
||||
</RegistryKey>
|
||||
</RegistryKey>
|
||||
|
||||
<File Id="wslaservice.exe" Source="${BIN}/wslaservice.exe" KeyPath="yes" />
|
||||
<File Id="wslaserviceproxystub.dll" Name="wslaserviceproxystub.dll" Source="${BIN}/wslaserviceproxystub.dll" />
|
||||
|
||||
|
||||
@ -6,19 +6,19 @@
|
||||
<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" />
|
||||
<package id="Microsoft.WSL.DeviceHost" version="1.0.0-20251015.1" />
|
||||
<package id="Microsoft.WSL.DeviceHost" version="1.0.0-20251202.1" />
|
||||
<package id="Microsoft.WSL.Kernel" version="6.6.87.2-1" targetFramework="native" />
|
||||
<package id="Microsoft.WSL.LinuxSdk" version="1.20.0" targetFramework="native" />
|
||||
<package id="Microsoft.WSL.LxUtil.amd64fre" version="10.0.26100.1-240331-1435.ge-release" />
|
||||
@ -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>
|
||||
|
||||
@ -30,6 +30,10 @@ Abstract:
|
||||
Type(Type&&) = delete; \
|
||||
Type& operator=(Type&&) = delete;
|
||||
|
||||
#define DEFAULT_MOVABLE(Type) \
|
||||
Type(Type&&) = default; \
|
||||
Type& operator=(Type&&) = default;
|
||||
|
||||
namespace wsl::shared {
|
||||
|
||||
inline constexpr std::uint32_t VersionMajor = WSL_PACKAGE_VERSION_MAJOR;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -2,16 +2,16 @@ set(SOURCES
|
||||
ConsoleProgressBar.cpp
|
||||
ConsoleProgressIndicator.cpp
|
||||
DeviceHostProxy.cpp
|
||||
DeviceHostProxy.h
|
||||
disk.cpp
|
||||
Distribution.cpp
|
||||
Dmesg.cpp
|
||||
Dmesg.h
|
||||
DnsResolver.cpp
|
||||
DnsTunnelingChannel.cpp
|
||||
ExecutionContext.cpp
|
||||
filesystem.cpp
|
||||
GnsChannel.cpp
|
||||
GnsPortTrackerChannel.cpp
|
||||
GuestDeviceManager.cpp
|
||||
HandleConsoleProgressBar.cpp
|
||||
hcs.cpp
|
||||
helpers.cpp
|
||||
@ -34,6 +34,8 @@ set(SOURCES
|
||||
SubProcess.cpp
|
||||
svccomm.cpp
|
||||
svccommio.cpp
|
||||
WSLAContainerLauncher.cpp
|
||||
VirtioNetworking.cpp
|
||||
WSLAProcessLauncher.cpp
|
||||
WslClient.cpp
|
||||
WslCoreConfig.cpp
|
||||
@ -55,11 +57,11 @@ set(HEADERS
|
||||
../inc/LxssDynamicFunction.h
|
||||
../inc/traceloggingconfig.h
|
||||
../inc/wdk.h
|
||||
../inc/wslconfig.h
|
||||
../inc/wsl.h
|
||||
../inc/wslconfig.h
|
||||
../inc/wslhost.h
|
||||
../inc/WslPluginApi.h
|
||||
../inc/wslpolicies.h
|
||||
../inc/WslPluginApi.h
|
||||
../inc/wslrelay.h
|
||||
../../shared/inc/CommandLine.h
|
||||
../../shared/inc/defs.h
|
||||
@ -75,13 +77,17 @@ set(HEADERS
|
||||
../../shared/inc/stringshared.h
|
||||
ConsoleProgressBar.h
|
||||
ConsoleProgressIndicator.h
|
||||
DeviceHostProxy.h
|
||||
disk.hpp
|
||||
Distribution.h
|
||||
Dmesg.h
|
||||
DnsResolver.h
|
||||
DnsTunnelingChannel.h
|
||||
ExecutionContext.h
|
||||
filesystem.hpp
|
||||
GnsChannel.h
|
||||
GnsPortTrackerChannel.h
|
||||
GuestDeviceManager.h
|
||||
HandleConsoleProgressBar.h
|
||||
hcs.hpp
|
||||
hcs_schema.h
|
||||
@ -106,6 +112,8 @@ set(HEADERS
|
||||
SubProcess.h
|
||||
svccomm.hpp
|
||||
svccommio.hpp
|
||||
WSLAContainerLauncher.h
|
||||
VirtioNetworking.h
|
||||
WSLAProcessLauncher.h
|
||||
WslClient.h
|
||||
WslCoreConfig.h
|
||||
@ -118,7 +126,7 @@ set(HEADERS
|
||||
WslInstall.h
|
||||
WslSecurity.h
|
||||
WslTelemetry.h
|
||||
wslutil.cpp
|
||||
wslutil.h
|
||||
)
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/src/windows/wslaclient)
|
||||
|
||||
143
src/windows/common/GuestDeviceManager.cpp
Normal file
143
src/windows/common/GuestDeviceManager.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
// 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)
|
||||
{
|
||||
return m_deviceHostSupport->AddNewDevice(deviceId, server, tag);
|
||||
}
|
||||
|
||||
void GuestDeviceManager::AddRemoteFileSystem(_In_ REFCLSID clsid, _In_ PCWSTR tag, _In_ const wil::com_ptr<IPlan9FileSystem>& server)
|
||||
{
|
||||
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_FS_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)
|
||||
{
|
||||
return m_deviceHostSupport->GetRemoteFileSystem(clsid, tag);
|
||||
}
|
||||
|
||||
void GuestDeviceManager::Shutdown()
|
||||
try
|
||||
{
|
||||
m_deviceHostSupport->Shutdown();
|
||||
}
|
||||
CATCH_LOG()
|
||||
71
src/windows/common/GuestDeviceManager.h
Normal file
71
src/windows/common/GuestDeviceManager.h
Normal file
@ -0,0 +1,71 @@
|
||||
// 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
|
||||
|
||||
inline const std::wstring c_defaultDeviceTag = L"default";
|
||||
|
||||
// These device types are implemented by the external wsldevicehost vdev.
|
||||
DEFINE_GUID(VIRTIO_FS_DEVICE_ID, 0x872270E1, 0xA899, 0x4AF6, 0xB4, 0x54, 0x71, 0x93, 0x63, 0x44, 0x35, 0xAD); // {872270E1-A899-4AF6-B454-7193634435AD}
|
||||
DEFINE_GUID(VIRTIO_NET_DEVICE_ID, 0xF07010D0, 0x0EA9, 0x447F, 0x88, 0xEF, 0xBD, 0x95, 0x2A, 0x4D, 0x2F, 0x14); // {F07010D0-0EA9-447F-88EF-BD952A4D2F14}
|
||||
DEFINE_GUID(VIRTIO_PMEM_DEVICE_ID, 0xEDBB24BB, 0x5E19, 0x40F4, 0x8A, 0x0F, 0x82, 0x24, 0x31, 0x30, 0x64, 0xFD); // {EDBB24BB-5E19-40F4-8A0F-8224313064FD}
|
||||
|
||||
//
|
||||
// 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;
|
||||
};
|
||||
@ -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, GUID classId, wil::shared_handle userToken) :
|
||||
m_guestDeviceManager(std::move(guestDeviceManager)),
|
||||
m_userToken(std::move(userToken)),
|
||||
m_gnsChannel(std::move(gnsChannel)),
|
||||
m_enableLocalhostRelay(enableLocalhostRelay),
|
||||
m_virtioNetworkClsid(classId)
|
||||
{
|
||||
}
|
||||
|
||||
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(
|
||||
VIRTIO_NET_DEVICE_ID, m_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,19 +107,24 @@ try
|
||||
UpdateDns(std::move(dnsSettings));
|
||||
}
|
||||
|
||||
if (m_config.EnableLocalhostRelay)
|
||||
if (m_enableLocalhostRelay)
|
||||
{
|
||||
SetupLoopbackDevice();
|
||||
}
|
||||
|
||||
THROW_IF_WIN32_ERROR(NotifyNetworkConnectivityHintChange(&VirtioNetworking::OnNetworkConnectivityChange, this, true, &m_networkNotifyHandle));
|
||||
}
|
||||
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(
|
||||
VIRTIO_NET_DEVICE_ID,
|
||||
m_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 +153,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 +172,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 +187,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_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(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_ 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(m_virtioNetworkClsid, 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);
|
||||
@ -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, GUID classId, 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_ 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,15 @@ 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;
|
||||
GUID m_virtioNetworkClsid;
|
||||
|
||||
std::optional<ULONGLONG> m_interfaceLuid;
|
||||
ULONG m_networkMtu = 0;
|
||||
@ -67,11 +59,6 @@ private:
|
||||
|
||||
// Note: this field must be destroyed first to stop the callbacks before any other field is destroyed.
|
||||
networking::unique_notify_handle m_networkNotifyHandle;
|
||||
|
||||
// 16479D2E-F0C3-4DBA-BF7A-04FFF0892B07
|
||||
static constexpr GUID c_virtioNetworkClsid = {0x16479D2E, 0xF0C3, 0x4DBA, {0xBF, 0x7A, 0x04, 0xFF, 0xF0, 0x89, 0x2B, 0x07}};
|
||||
// F07010D0-0EA9-447F-88EF-BD952A4D2F14
|
||||
static constexpr GUID c_virtioNetworkDeviceId = {0xF07010D0, 0x0EA9, 0x447F, {0x88, 0xEF, 0xBD, 0x95, 0x2A, 0x4D, 0x2F, 0x14}};
|
||||
};
|
||||
|
||||
} // namespace wsl::core
|
||||
74
src/windows/common/WSLAContainerLauncher.cpp
Normal file
74
src/windows/common/WSLAContainerLauncher.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
/*++
|
||||
|
||||
Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
Module Name:
|
||||
|
||||
WSLAContainerLauncher.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
This file contains the implementation for WSLAContainerLauncher.
|
||||
|
||||
--*/
|
||||
#include "WSLAContainerLauncher.h"
|
||||
|
||||
using wsl::windows::common::ClientRunningWSLAProcess;
|
||||
using wsl::windows::common::RunningWSLAContainer;
|
||||
using wsl::windows::common::WSLAContainerLauncher;
|
||||
|
||||
RunningWSLAContainer::RunningWSLAContainer(wil::com_ptr<IWSLAContainer>&& Container, std::vector<WSLA_PROCESS_FD>&& fds) :
|
||||
m_container(std::move(Container)), m_fds(std::move(fds))
|
||||
{
|
||||
}
|
||||
|
||||
IWSLAContainer& RunningWSLAContainer::Get()
|
||||
{
|
||||
return *m_container;
|
||||
}
|
||||
|
||||
WSLA_CONTAINER_STATE RunningWSLAContainer::State()
|
||||
{
|
||||
WSLA_CONTAINER_STATE state{};
|
||||
THROW_IF_FAILED(m_container->GetState(&state));
|
||||
return state;
|
||||
}
|
||||
|
||||
ClientRunningWSLAProcess RunningWSLAContainer::GetInitProcess()
|
||||
{
|
||||
wil::com_ptr<IWSLAProcess> process;
|
||||
THROW_IF_FAILED(m_container->GetInitProcess(&process));
|
||||
|
||||
return ClientRunningWSLAProcess{std::move(process), std::move(m_fds)};
|
||||
}
|
||||
|
||||
WSLAContainerLauncher::WSLAContainerLauncher(
|
||||
const std::string& Image,
|
||||
const std::string& Name,
|
||||
const std::string& EntryPoint,
|
||||
const std::vector<std::string>& Arguments,
|
||||
const std::vector<std::string>& Environment,
|
||||
ProcessFlags Flags) :
|
||||
WSLAProcessLauncher(EntryPoint, Arguments, Environment, Flags), m_image(Image), m_name(Name)
|
||||
{
|
||||
}
|
||||
|
||||
RunningWSLAContainer WSLAContainerLauncher::Launch(IWSLASession& Session)
|
||||
{
|
||||
WSLA_CONTAINER_OPTIONS options{};
|
||||
options.Image = m_image.c_str();
|
||||
options.Name = m_name.c_str();
|
||||
auto [processOptions, commandLinePtrs, environmentPtrs] = CreateProcessOptions();
|
||||
options.InitProcessOptions = processOptions;
|
||||
|
||||
if (m_executable.empty())
|
||||
{
|
||||
options.InitProcessOptions.Executable = nullptr;
|
||||
}
|
||||
|
||||
// TODO: Support volumes, ports, flags, shm size, container networking mode, etc.
|
||||
wil::com_ptr<IWSLAContainer> container;
|
||||
THROW_IF_FAILED(Session.CreateContainer(&options, &container));
|
||||
|
||||
return RunningWSLAContainer{std::move(container), std::move(m_fds)};
|
||||
}
|
||||
59
src/windows/common/WSLAContainerLauncher.h
Normal file
59
src/windows/common/WSLAContainerLauncher.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*++
|
||||
|
||||
Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
Module Name:
|
||||
|
||||
WSLAContainerLauncher.h
|
||||
|
||||
Abstract:
|
||||
|
||||
This file contains the definition for WSLAContainerLauncher.
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
#include "WSLAProcessLauncher.h"
|
||||
|
||||
namespace wsl::windows::common {
|
||||
|
||||
class RunningWSLAContainer
|
||||
{
|
||||
public:
|
||||
NON_COPYABLE(RunningWSLAContainer);
|
||||
DEFAULT_MOVABLE(RunningWSLAContainer);
|
||||
RunningWSLAContainer(wil::com_ptr<IWSLAContainer>&& Container, std::vector<WSLA_PROCESS_FD>&& fds);
|
||||
IWSLAContainer& Get();
|
||||
|
||||
WSLA_CONTAINER_STATE State();
|
||||
ClientRunningWSLAProcess GetInitProcess();
|
||||
|
||||
private:
|
||||
wil::com_ptr<IWSLAContainer> m_container;
|
||||
std::vector<WSLA_PROCESS_FD> m_fds;
|
||||
};
|
||||
|
||||
class WSLAContainerLauncher : public WSLAProcessLauncher
|
||||
{
|
||||
public:
|
||||
NON_COPYABLE(WSLAContainerLauncher);
|
||||
NON_MOVABLE(WSLAContainerLauncher);
|
||||
|
||||
WSLAContainerLauncher(
|
||||
const std::string& Image,
|
||||
const std::string& Name,
|
||||
const std::string& EntryPoint = "",
|
||||
const std::vector<std::string>& Arguments = {},
|
||||
const std::vector<std::string>& Environment = {},
|
||||
ProcessFlags Flags = ProcessFlags::Stdout | ProcessFlags::Stderr);
|
||||
|
||||
void AddVolume(const std::string& HostPath, const std::string& ContainerPath, bool ReadOnly);
|
||||
void AddPort(uint16_t WindowsPort, uint16_t ContainerPort, int Family);
|
||||
|
||||
RunningWSLAContainer Launch(IWSLASession& Session);
|
||||
|
||||
private:
|
||||
std::string m_image;
|
||||
std::string m_name;
|
||||
};
|
||||
} // namespace wsl::windows::common
|
||||
@ -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));
|
||||
}
|
||||
@ -150,7 +158,7 @@ ClientRunningWSLAProcess WSLAProcessLauncher::Launch(IWSLASession& Session)
|
||||
THROW_HR_MSG(hresult, "Failed to launch process: %hs (commandline: %hs). Errno = %i", m_executable.c_str(), commandLine.c_str(), error);
|
||||
}
|
||||
|
||||
return process.value();
|
||||
return std::move(process.value());
|
||||
}
|
||||
|
||||
std::tuple<HRESULT, int, std::optional<ClientRunningWSLAProcess>> WSLAProcessLauncher::LaunchNoThrow(IWSLASession& Session)
|
||||
|
||||
@ -43,6 +43,9 @@ public:
|
||||
};
|
||||
|
||||
RunningWSLAProcess(std::vector<WSLA_PROCESS_FD>&& fds);
|
||||
NON_COPYABLE(RunningWSLAProcess);
|
||||
DEFAULT_MOVABLE(RunningWSLAProcess);
|
||||
|
||||
ProcessResult WaitAndCaptureOutput(DWORD TimeoutMs = INFINITE, std::vector<std::unique_ptr<relay::OverlappedIOHandle>>&& ExtraHandles = {});
|
||||
virtual wil::unique_handle GetStdHandle(int Index) = 0;
|
||||
virtual wil::unique_event GetExitEvent() = 0;
|
||||
@ -57,6 +60,9 @@ protected:
|
||||
class ClientRunningWSLAProcess : public RunningWSLAProcess
|
||||
{
|
||||
public:
|
||||
NON_COPYABLE(ClientRunningWSLAProcess);
|
||||
DEFAULT_MOVABLE(ClientRunningWSLAProcess);
|
||||
|
||||
ClientRunningWSLAProcess(wil::com_ptr<IWSLAProcess>&& process, std::vector<WSLA_PROCESS_FD>&& fds);
|
||||
wil::unique_handle GetStdHandle(int Index) override;
|
||||
wil::unique_event GetExitEvent() override;
|
||||
@ -68,7 +74,6 @@ protected:
|
||||
private:
|
||||
wil::com_ptr<IWSLAProcess> m_process;
|
||||
};
|
||||
|
||||
class WSLAProcessLauncher
|
||||
{
|
||||
public:
|
||||
@ -82,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);
|
||||
@ -95,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
|
||||
@ -1525,64 +1525,61 @@ int RunDebugShell()
|
||||
THROW_HR(HCS_E_CONNECTION_CLOSED);
|
||||
}
|
||||
|
||||
DEFINE_ENUM_FLAG_OPERATORS(WSLAFeatureFlags);
|
||||
|
||||
// Temporary debugging tool for WSLA
|
||||
int WslaShell(_In_ std::wstring_view commandLine)
|
||||
{
|
||||
#ifdef WSLA_TEST_DISTRO_PATH
|
||||
WSLA_SESSION_SETTINGS sessionSettings{};
|
||||
sessionSettings.DisplayName = L"WSLAShell";
|
||||
sessionSettings.CpuCount = 4;
|
||||
sessionSettings.MemoryMb = 4096;
|
||||
sessionSettings.NetworkingMode = WSLANetworkingModeNAT;
|
||||
sessionSettings.BootTimeoutMs = 30 * 1000;
|
||||
sessionSettings.MaximumStorageSizeMb = 4096;
|
||||
|
||||
std::wstring vhd = TEXT(WSLA_TEST_DISTRO_PATH);
|
||||
std::string shell = "/bin/sh";
|
||||
std::string fsType = "squashfs";
|
||||
|
||||
#else
|
||||
|
||||
std::wstring vhd = wsl::windows::common::wslutil::GetMsiPackagePath().value() + L"/system.vhd";
|
||||
std::string shell = "/bin/bash";
|
||||
std::string fsType = "ext4";
|
||||
|
||||
#endif
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 1024;
|
||||
settings.BootTimeoutMs = 30000;
|
||||
settings.NetworkingMode = WSLANetworkingModeNAT;
|
||||
std::wstring containerRootVhd;
|
||||
std::string containerImage;
|
||||
bool help = false;
|
||||
std::wstring debugShell;
|
||||
|
||||
std::wstring storagePath;
|
||||
std::wstring rootVhdOverride;
|
||||
std::string rootVhdTypeOverride;
|
||||
ArgumentParser parser(std::wstring{commandLine}, WSL_BINARY_NAME);
|
||||
parser.AddArgument(vhd, L"--vhd");
|
||||
parser.AddArgument(rootVhdOverride, L"--vhd");
|
||||
parser.AddArgument(Utf8String(shell), L"--shell");
|
||||
parser.AddArgument(reinterpret_cast<bool&>(settings.EnableDnsTunneling), L"--dns-tunneling");
|
||||
parser.AddArgument(Integer(settings.MemoryMb), L"--memory");
|
||||
parser.AddArgument(Integer(settings.CpuCount), L"--cpu");
|
||||
parser.AddArgument(Utf8String(fsType), L"--fstype");
|
||||
parser.AddArgument(containerRootVhd, L"--container-vhd");
|
||||
parser.AddArgument(
|
||||
SetFlag<int, WslaFeatureFlagsDnsTunneling>(reinterpret_cast<int&>(sessionSettings.FeatureFlags)), L"--dns-tunneling");
|
||||
parser.AddArgument(Integer(sessionSettings.MemoryMb), L"--memory");
|
||||
parser.AddArgument(Integer(sessionSettings.CpuCount), L"--cpu");
|
||||
parser.AddArgument(Utf8String(rootVhdTypeOverride), L"--fstype");
|
||||
parser.AddArgument(storagePath, L"--storage");
|
||||
parser.AddArgument(Integer(reinterpret_cast<int&>(sessionSettings.NetworkingMode)), L"--networking-mode");
|
||||
parser.AddArgument(Utf8String(containerImage), L"--image");
|
||||
parser.AddArgument(debugShell, L"--debug-shell");
|
||||
parser.AddArgument(help, L"--help");
|
||||
parser.Parse();
|
||||
|
||||
if (help)
|
||||
{
|
||||
const auto usage = std::format(
|
||||
LR"({} --wsla [--vhd </path/to/vhd>] [--shell </path/to/shell>] [--memory <memory-mb>] [--cpu <cpus>] [--dns-tunneling] [--fstype <fstype>] [--container-vhd </path/to/vhd>] [--help])",
|
||||
LR"({} --wsla [--vhd </path/to/vhd>] [--shell </path/to/shell>] [--memory <memory-mb>] [--cpu <cpus>] [--dns-tunneling] [--networking-mode <mode>] [--fstype <fstype>] [--container-vhd </path/to/vhd>] [--help])",
|
||||
WSL_BINARY_NAME);
|
||||
|
||||
wprintf(L"%ls\n", usage.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!containerRootVhd.empty())
|
||||
switch (sessionSettings.NetworkingMode)
|
||||
{
|
||||
settings.ContainerRootVhd = containerRootVhd.c_str();
|
||||
|
||||
if (!std::filesystem::exists(containerRootVhd))
|
||||
{
|
||||
auto token = wil::open_current_access_token();
|
||||
auto tokenInfo = wil::get_token_information<TOKEN_USER>(token.get());
|
||||
wsl::core::filesystem::CreateVhd(containerRootVhd.c_str(), 5368709120 /* 5 GB */, tokenInfo->User.Sid, FALSE, FALSE);
|
||||
settings.FormatContainerRootVhd = TRUE;
|
||||
}
|
||||
case WSLANetworkingMode::WSLANetworkingModeNone:
|
||||
case WSLANetworkingMode::WSLANetworkingModeNAT:
|
||||
case WSLANetworkingMode::WSLANetworkingModeVirtioProxy:
|
||||
break;
|
||||
default:
|
||||
THROW_HR(E_INVALIDARG);
|
||||
}
|
||||
|
||||
wil::com_ptr<IWSLAUserSession> userSession;
|
||||
@ -1590,33 +1587,86 @@ int WslaShell(_In_ std::wstring_view commandLine)
|
||||
wsl::windows::common::security::ConfigureForCOMImpersonation(userSession.get());
|
||||
|
||||
wil::com_ptr<IWSLAVirtualMachine> virtualMachine;
|
||||
WSLA_SESSION_SETTINGS sessionSettings{L"WSLA Test Session"};
|
||||
wil::com_ptr<IWSLASession> session;
|
||||
settings.RootVhd = vhd.c_str();
|
||||
settings.RootVhdType = fsType.c_str();
|
||||
THROW_IF_FAILED(userSession->CreateSession(&sessionSettings, &settings, &session));
|
||||
|
||||
if (!rootVhdOverride.empty())
|
||||
{
|
||||
if (rootVhdTypeOverride.empty())
|
||||
{
|
||||
wprintf(L"--fstype required when --vhd is passed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
sessionSettings.RootVhdOverride = rootVhdOverride.c_str();
|
||||
sessionSettings.RootVhdTypeOverride = rootVhdTypeOverride.c_str();
|
||||
}
|
||||
|
||||
if (!storagePath.empty())
|
||||
{
|
||||
storagePath = std::filesystem::weakly_canonical(storagePath).wstring();
|
||||
sessionSettings.StoragePath = storagePath.c_str();
|
||||
}
|
||||
|
||||
if (!debugShell.empty())
|
||||
{
|
||||
THROW_IF_FAILED(userSession->OpenSessionByName(debugShell.c_str(), &session));
|
||||
}
|
||||
else
|
||||
{
|
||||
THROW_IF_FAILED(userSession->CreateSession(&sessionSettings, &session));
|
||||
THROW_IF_FAILED(session->GetVirtualMachine(&virtualMachine));
|
||||
|
||||
wsl::windows::common::security::ConfigureForCOMImpersonation(userSession.get());
|
||||
|
||||
if (!containerRootVhd.empty())
|
||||
{
|
||||
wsl::windows::common::WSLAProcessLauncher initProcessLauncher{shell, {shell, "/etc/lsw-init.sh"}};
|
||||
auto initProcess = initProcessLauncher.Launch(*session);
|
||||
THROW_HR_IF(E_FAIL, initProcess.WaitAndCaptureOutput().Code != 0);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
auto process = launcher.Launch(*session);
|
||||
if (containerImage.empty())
|
||||
{
|
||||
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});
|
||||
|
||||
process = launcher.Launch(*session);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<WSLA_PROCESS_FD> fds{
|
||||
WSLA_PROCESS_FD{.Fd = 0, .Type = WSLAFdTypeTerminalInput},
|
||||
WSLA_PROCESS_FD{.Fd = 1, .Type = WSLAFdTypeTerminalOutput},
|
||||
WSLA_PROCESS_FD{.Fd = 2, .Type = WSLAFdTypeTerminalControl},
|
||||
};
|
||||
|
||||
WSLA_CONTAINER_OPTIONS containerOptions{};
|
||||
containerOptions.Image = containerImage.c_str();
|
||||
containerOptions.Name = "test-container";
|
||||
containerOptions.InitProcessOptions.Fds = fds.data();
|
||||
containerOptions.InitProcessOptions.FdsCount = static_cast<DWORD>(fds.size());
|
||||
|
||||
container.emplace();
|
||||
THROW_IF_FAILED(session->CreateContainer(&containerOptions, &container.value()));
|
||||
|
||||
wil::com_ptr<IWSLAProcess> initProcess;
|
||||
THROW_IF_FAILED((*container)->GetInitProcess(&initProcess));
|
||||
process.emplace(std::move(initProcess), std::move(fds));
|
||||
}
|
||||
|
||||
// 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));
|
||||
@ -1641,7 +1691,7 @@ int WslaShell(_In_ std::wstring_view commandLine)
|
||||
auto exitEvent = wil::unique_event(wil::EventOptions::ManualReset);
|
||||
|
||||
wsl::shared::SocketChannel controlChannel{
|
||||
wil::unique_socket{(SOCKET)process.GetStdHandle(2).release()}, "TerminalControl", exitEvent.get()};
|
||||
wil::unique_socket{(SOCKET)process->GetStdHandle(2).release()}, "TerminalControl", exitEvent.get()};
|
||||
|
||||
std::thread inputThread([&]() {
|
||||
auto updateTerminal = [&controlChannel, &Stdout]() {
|
||||
@ -1657,7 +1707,7 @@ int WslaShell(_In_ std::wstring_view commandLine)
|
||||
controlChannel.SendMessage(message);
|
||||
};
|
||||
|
||||
wsl::windows::common::relay::StandardInputRelay(Stdin, process.GetStdHandle(0).get(), updateTerminal, exitEvent.get());
|
||||
wsl::windows::common::relay::StandardInputRelay(Stdin, process->GetStdHandle(0).get(), updateTerminal, exitEvent.get());
|
||||
});
|
||||
|
||||
auto joinThread = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
|
||||
@ -1666,12 +1716,12 @@ int WslaShell(_In_ std::wstring_view commandLine)
|
||||
});
|
||||
|
||||
// Relay the contents of the pipe to stdout.
|
||||
wsl::windows::common::relay::InterruptableRelay(process.GetStdHandle(1).get(), Stdout);
|
||||
wsl::windows::common::relay::InterruptableRelay(process->GetStdHandle(1).get(), Stdout);
|
||||
}
|
||||
|
||||
process.GetExitEvent().wait();
|
||||
process->GetExitEvent().wait();
|
||||
|
||||
auto [code, signalled] = process.GetExitState();
|
||||
auto [code, signalled] = process->GetExitState();
|
||||
wprintf(L"%hs exited with: %i%hs", shell.c_str(), code, signalled ? " (signalled)" : "");
|
||||
|
||||
return code;
|
||||
|
||||
@ -29,7 +29,9 @@ wil::unique_hfile wsl::core::filesystem::CreateFile(
|
||||
|
||||
void wsl::core::filesystem::CreateVhd(_In_ LPCWSTR target, _In_ ULONGLONG maximumSize, _In_ PSID userSid, _In_ BOOL sparse, _In_ BOOL fixed)
|
||||
{
|
||||
WI_ASSERT(wsl::windows::common::string::IsPathComponentEqual(
|
||||
THROW_HR_IF(
|
||||
E_INVALIDARG,
|
||||
!wsl::windows::common::string::IsPathComponentEqual(
|
||||
std::filesystem::path{target}.extension().native(), windows::common::wslutil::c_vhdxFileExtension));
|
||||
|
||||
// Disable creation of sparse VHDs while data corruption is being debugged.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -143,7 +143,8 @@ static const std::map<HRESULT, LPCWSTR> g_commonErrors{
|
||||
X_WIN32(ERROR_OPERATION_ABORTED),
|
||||
X_WIN32(WSAECONNREFUSED),
|
||||
X_WIN32(ERROR_BAD_PATHNAME),
|
||||
X(WININET_E_TIMEOUT)};
|
||||
X(WININET_E_TIMEOUT),
|
||||
X_WIN32(ERROR_INVALID_SID)};
|
||||
|
||||
#undef X
|
||||
|
||||
@ -624,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})
|
||||
{
|
||||
@ -633,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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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{};
|
||||
|
||||
@ -4,13 +4,11 @@ set(SOURCES
|
||||
LxssUserSession.cpp
|
||||
LxssUserSessionFactory.cpp
|
||||
LxssIptables.cpp
|
||||
LxssUserCallback.cpp
|
||||
LxssHttpProxy.cpp
|
||||
LxssInstance.cpp
|
||||
PluginManager.cpp
|
||||
ServiceMain.cpp
|
||||
BridgedNetworking.cpp
|
||||
GnsPortTrackerChannel.cpp
|
||||
GnsRpcServer.cpp
|
||||
GuestTelemetryLogger.cpp
|
||||
Lifetime.cpp
|
||||
@ -22,7 +20,6 @@ set(SOURCES
|
||||
WslMirroredNetworking.cpp
|
||||
WslCoreTcpIpStateTracking.cpp
|
||||
WslCoreVm.cpp
|
||||
VirtioNetworking.cpp
|
||||
main.rc
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../mc/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE}/wsleventschema.rc
|
||||
application.manifest)
|
||||
@ -38,7 +35,6 @@ set(HEADERS
|
||||
PluginManager.h
|
||||
LxssInstance.h
|
||||
BridgedNetworking.h
|
||||
GnsPortTrackerChannel.h
|
||||
GnsRpcServer.h
|
||||
GuestTelemetryLogger.h
|
||||
IMirroredNetworkManager.h
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -39,27 +39,18 @@ 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
|
||||
|
||||
// WSL-specific virtio device class IDs.
|
||||
DEFINE_GUID(VIRTIO_FS_ADMIN_CLASS_ID, 0x7E6AD219, 0xD1B3, 0x42D5, 0xB8, 0xEE, 0xD9, 0x63, 0x24, 0xE6, 0x4F, 0xF6); // {7E6AD219-D1B3-42D5-B8EE-D96324E64FF6}
|
||||
DEFINE_GUID(VIRTIO_FS_CLASS_ID, 0x60285AE6, 0xAAF3, 0x4456, 0xB4, 0x44, 0xA6, 0xC2, 0xD0, 0xDE, 0xDA, 0x38); // {60285AE6-AAF3-4456-B444-A6C2D0DEDA38}
|
||||
DEFINE_GUID(VIRTIO_NET_CLASS_ID, 0x16479D2E, 0xF0C3, 0x4DBA, 0xBF, 0x7A, 0x04, 0xFF, 0xF0, 0x89, 0x2B, 0x07); // {16479D2E-F0C3-4DBA-BF7A-04FFF0892B07}
|
||||
DEFINE_GUID(VIRTIO_PMEM_CLASS_ID, 0xABB755FC, 0x1B86, 0x4255, 0x83, 0xE2, 0xE5, 0x78, 0x7A, 0xBC, 0xF6, 0xC2); // {ABB755FC-1B86-4255-83E2-E5787ABCF6C2}
|
||||
|
||||
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 +61,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 +324,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 +452,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 +590,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, VIRTIO_NET_CLASS_ID, m_userToken);
|
||||
}
|
||||
else if (m_vmConfig.NetworkingMode == NetworkingMode::Bridged)
|
||||
{
|
||||
@ -831,9 +774,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 +899,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 +912,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 +943,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 +1755,8 @@ void WslCoreVm::InitializeGuest()
|
||||
{
|
||||
try
|
||||
{
|
||||
MountSharedMemoryDevice(c_virtiofsClassId, L"wslg", L"wslg", WSLG_SHARED_MEMORY_SIZE_MB);
|
||||
m_guestDeviceManager->AddSharedMemoryDevice(
|
||||
VIRTIO_FS_CLASS_ID, 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 +1855,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 +1887,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 +1992,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 +2015,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 +2026,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 +2076,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 +2108,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_FS_DEVICE_ID,
|
||||
Admin ? VIRTIO_FS_ADMIN_CLASS_ID : VIRTIO_FS_CLASS_ID,
|
||||
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 +2465,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 +2635,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
|
||||
{
|
||||
|
||||
@ -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)
|
||||
@ -39,11 +40,6 @@ inline constexpr auto c_optionsValueName = L"Options";
|
||||
inline constexpr auto c_typeValueName = L"Type";
|
||||
inline constexpr auto c_mountNameValueName = L"Name";
|
||||
|
||||
static constexpr GUID c_virtiofsAdminClassId = {0x7e6ad219, 0xd1b3, 0x42d5, {0xb8, 0xee, 0xd9, 0x63, 0x24, 0xe6, 0x4f, 0xf6}};
|
||||
|
||||
// {60285AE6-AAF3-4456-B444-A6C2D0DEDA38}
|
||||
static constexpr GUID c_virtiofsClassId = {0x60285ae6, 0xaaf3, 0x4456, {0xb4, 0x44, 0xa6, 0xc2, 0xd0, 0xde, 0xda, 0x38}};
|
||||
|
||||
namespace wrl = Microsoft::WRL;
|
||||
|
||||
/// <summary>
|
||||
@ -114,10 +110,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 +123,7 @@ public:
|
||||
void SaveAttachedDisksState();
|
||||
|
||||
_Requires_lock_held_(m_guestDeviceLock)
|
||||
void VerifyDrvFsServers();
|
||||
void VerifyPlan9Servers();
|
||||
|
||||
enum DiskStateFlags
|
||||
{
|
||||
@ -166,14 +160,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 +182,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 +190,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 +199,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 +227,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 +257,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 +296,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 +317,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);
|
||||
|
||||
@ -24,7 +24,7 @@ class ServiceRunningProcess : public common::RunningWSLAProcess
|
||||
{
|
||||
public:
|
||||
NON_COPYABLE(ServiceRunningProcess);
|
||||
NON_MOVABLE(ServiceRunningProcess);
|
||||
DEFAULT_MOVABLE(ServiceRunningProcess);
|
||||
|
||||
ServiceRunningProcess(const Microsoft::WRL::ComPtr<WSLAProcess>& process, std::vector<WSLA_PROCESS_FD>&& fds);
|
||||
wil::unique_handle GetStdHandle(int Index) override;
|
||||
|
||||
@ -18,6 +18,14 @@ Abstract:
|
||||
|
||||
using wsl::windows::service::wsla::WSLAContainer;
|
||||
|
||||
constexpr const char* nerdctlPath = "/usr/bin/nerdctl";
|
||||
|
||||
// Constants for required default arguments for "nerdctl run..."
|
||||
static std::vector<std::string> defaultNerdctlRunArgs{//"--pull=never", // TODO: Uncomment once PullImage() is implemented.
|
||||
"--net=host", // TODO: default for now, change later
|
||||
"--ulimit",
|
||||
"nofile=65536:65536"};
|
||||
|
||||
HRESULT WSLAContainer::Start()
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
@ -38,10 +46,12 @@ HRESULT WSLAContainer::GetState(WSLA_CONTAINER_STATE* State)
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT WSLAContainer::GetInitProcess(IWSLAProcess** process)
|
||||
HRESULT WSLAContainer::GetInitProcess(IWSLAProcess** Process)
|
||||
try
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
return m_containerProcess.Get().QueryInterface(__uuidof(IWSLAProcess), (void**)Process);
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
HRESULT WSLAContainer::Exec(const WSLA_PROCESS_OPTIONS* Options, IWSLAProcess** Process, int* Errno)
|
||||
try
|
||||
@ -53,3 +63,101 @@ try
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
Microsoft::WRL::ComPtr<WSLAContainer> WSLAContainer::Create(const WSLA_CONTAINER_OPTIONS& containerOptions, WSLAVirtualMachine& parentVM)
|
||||
{
|
||||
// TODO: Switch to nerdctl create, and call nerdctl start in Start().
|
||||
|
||||
bool hasStdin = false;
|
||||
bool hasTty = false;
|
||||
for (size_t i = 0; i < containerOptions.InitProcessOptions.FdsCount; i++)
|
||||
{
|
||||
if (containerOptions.InitProcessOptions.Fds[i].Fd == 0)
|
||||
{
|
||||
hasStdin = true;
|
||||
}
|
||||
|
||||
if (containerOptions.InitProcessOptions.Fds[i].Type == WSLAFdTypeTerminalInput ||
|
||||
containerOptions.InitProcessOptions.Fds[i].Type == WSLAFdTypeTerminalOutput)
|
||||
{
|
||||
hasTty = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> inputOptions;
|
||||
if (hasStdin)
|
||||
{
|
||||
inputOptions.push_back("-i");
|
||||
}
|
||||
|
||||
if (hasTty)
|
||||
{
|
||||
inputOptions.push_back("-t");
|
||||
}
|
||||
|
||||
auto args = PrepareNerdctlRunCommand(containerOptions, std::move(inputOptions));
|
||||
|
||||
ServiceProcessLauncher launcher(nerdctlPath, args, {}, common::ProcessFlags::None);
|
||||
for (size_t i = 0; i < containerOptions.InitProcessOptions.FdsCount; i++)
|
||||
{
|
||||
launcher.AddFd(containerOptions.InitProcessOptions.Fds[i]);
|
||||
}
|
||||
|
||||
return wil::MakeOrThrow<WSLAContainer>(&parentVM, launcher.Launch(parentVM));
|
||||
}
|
||||
|
||||
std::vector<std::string> WSLAContainer::PrepareNerdctlRunCommand(const WSLA_CONTAINER_OPTIONS& options, std::vector<std::string>&& inputOptions)
|
||||
{
|
||||
std::vector<std::string> args{nerdctlPath};
|
||||
args.push_back("run");
|
||||
args.push_back("--name");
|
||||
args.push_back(options.Name);
|
||||
if (options.ShmSize > 0)
|
||||
{
|
||||
args.push_back(std::format("--shm-size={}m", options.ShmSize));
|
||||
}
|
||||
if (options.Flags & WSLA_CONTAINER_FLAG_ENABLE_GPU)
|
||||
{
|
||||
args.push_back("--gpus");
|
||||
// TODO: Parse GPU device list from WSLA_CONTAINER_OPTIONS. For now, just enable all GPUs.
|
||||
args.push_back("all");
|
||||
}
|
||||
|
||||
args.insert(args.end(), defaultNerdctlRunArgs.begin(), defaultNerdctlRunArgs.end());
|
||||
args.insert(args.end(), inputOptions.begin(), inputOptions.end());
|
||||
|
||||
for (ULONG i = 0; i < options.InitProcessOptions.EnvironmentCount; i++)
|
||||
{
|
||||
THROW_HR_IF_MSG(
|
||||
E_INVALIDARG,
|
||||
options.InitProcessOptions.Environment[i][0] == L'-',
|
||||
"Invalid environment string: %hs",
|
||||
options.InitProcessOptions.Environment[i]);
|
||||
|
||||
args.insert(args.end(), {"-e", options.InitProcessOptions.Environment[i]});
|
||||
}
|
||||
|
||||
if (options.InitProcessOptions.Executable != nullptr)
|
||||
{
|
||||
args.push_back("--entrypoint");
|
||||
args.push_back(options.InitProcessOptions.Executable);
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// - Implement volume mounts
|
||||
// - Implement port mapping
|
||||
|
||||
args.push_back(options.Image);
|
||||
|
||||
if (options.InitProcessOptions.CommandLineCount > 0)
|
||||
{
|
||||
args.push_back("--");
|
||||
|
||||
for (ULONG i = 0; i < options.InitProcessOptions.CommandLineCount; i++)
|
||||
{
|
||||
args.push_back(options.InitProcessOptions.CommandLine[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
@ -14,7 +14,9 @@ Abstract:
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ServiceProcessLauncher.h"
|
||||
#include "wslaservice.h"
|
||||
#include "WSLAVirtualMachine.h"
|
||||
|
||||
namespace wsl::windows::service::wsla {
|
||||
|
||||
@ -23,6 +25,10 @@ class DECLSPEC_UUID("B1F1C4E3-C225-4CAE-AD8A-34C004DE1AE4") WSLAContainer
|
||||
{
|
||||
public:
|
||||
WSLAContainer() = default; // TODO
|
||||
WSLAContainer(WSLAVirtualMachine* parentVM, ServiceRunningProcess&& containerProcess) :
|
||||
m_parentVM(parentVM), m_containerProcess(std::move(containerProcess))
|
||||
{
|
||||
}
|
||||
WSLAContainer(const WSLAContainer&) = delete;
|
||||
WSLAContainer& operator=(const WSLAContainer&) = delete;
|
||||
|
||||
@ -33,6 +39,12 @@ public:
|
||||
IFACEMETHOD(GetInitProcess)(_Out_ IWSLAProcess** process) override;
|
||||
IFACEMETHOD(Exec)(_In_ const WSLA_PROCESS_OPTIONS* Options, _Out_ IWSLAProcess** Process, _Out_ int* Errno) override;
|
||||
|
||||
static Microsoft::WRL::ComPtr<WSLAContainer> Create(const WSLA_CONTAINER_OPTIONS& Options, WSLAVirtualMachine& parentVM);
|
||||
|
||||
private:
|
||||
ServiceRunningProcess m_containerProcess;
|
||||
WSLAVirtualMachine* m_parentVM = nullptr;
|
||||
|
||||
static std::vector<std::string> PrepareNerdctlRunCommand(const WSLA_CONTAINER_OPTIONS& options, std::vector<std::string>&& inputOptions);
|
||||
};
|
||||
} // namespace wsl::windows::service::wsla
|
||||
@ -44,6 +44,8 @@ void WSLAProcess::OnVmTerminated()
|
||||
{
|
||||
m_state = WslaProcessStateSignalled;
|
||||
m_exitedCode = 9; // SIGKILL
|
||||
|
||||
m_exitEvent.SetEvent();
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,6 +76,11 @@ try
|
||||
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !socket.is_valid());
|
||||
|
||||
*Handle = HandleToUlong(common::wslutil::DuplicateHandleToCallingProcess(socket.get()));
|
||||
WSL_LOG(
|
||||
"GetStdHandle",
|
||||
TraceLoggingValue(Index, "fd"),
|
||||
TraceLoggingValue(socket.get(), "handle"),
|
||||
TraceLoggingValue(*Handle, "remoteHandle"));
|
||||
|
||||
socket.reset();
|
||||
return S_OK;
|
||||
|
||||
@ -17,23 +17,85 @@ Abstract:
|
||||
#include "WSLAUserSession.h"
|
||||
#include "WSLAContainer.h"
|
||||
#include "ServiceProcessLauncher.h"
|
||||
#include "WslCoreFilesystem.h"
|
||||
|
||||
using wsl::windows::service::wsla::WSLASession;
|
||||
using wsl::windows::service::wsla::WSLAVirtualMachine;
|
||||
|
||||
WSLASession::WSLASession(const WSLA_SESSION_SETTINGS& Settings, WSLAUserSessionImpl& userSessionImpl, const VIRTUAL_MACHINE_SETTINGS& VmSettings) :
|
||||
m_sessionSettings(Settings),
|
||||
m_userSession(&userSessionImpl),
|
||||
m_virtualMachine(wil::MakeOrThrow<WSLAVirtualMachine>(VmSettings, userSessionImpl.GetUserSid(), &userSessionImpl)),
|
||||
m_displayName(Settings.DisplayName)
|
||||
WSLASession::WSLASession(const WSLA_SESSION_SETTINGS& Settings, WSLAUserSessionImpl& userSessionImpl) :
|
||||
m_sessionSettings(Settings), m_userSession(&userSessionImpl), m_displayName(Settings.DisplayName)
|
||||
{
|
||||
WSL_LOG("SessionCreated", TraceLoggingValue(m_displayName.c_str(), "DisplayName"));
|
||||
|
||||
m_virtualMachine = wil::MakeOrThrow<WSLAVirtualMachine>(CreateVmSettings(Settings), userSessionImpl.GetUserSid());
|
||||
|
||||
if (Settings.TerminationCallback != nullptr)
|
||||
{
|
||||
m_virtualMachine->RegisterCallback(Settings.TerminationCallback);
|
||||
}
|
||||
|
||||
m_virtualMachine->Start();
|
||||
|
||||
ConfigureStorage(Settings);
|
||||
|
||||
// Launch the init script.
|
||||
// TODO: Replace with something more robust once the final VHD is ready.
|
||||
try
|
||||
{
|
||||
ServiceProcessLauncher launcher{"/bin/sh", {"/bin/sh", "-c", "/etc/lsw-init.sh"}};
|
||||
auto result = launcher.Launch(*m_virtualMachine.Get()).WaitAndCaptureOutput();
|
||||
|
||||
THROW_HR_IF_MSG(E_FAIL, result.Code != 0, "Init script failed: %hs", launcher.FormatResult(result).c_str());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Ignore issues launching the init script with custom root VHD's, for convenience.
|
||||
// TODO: Remove once the final VHD is ready.
|
||||
if (Settings.RootVhdOverride == nullptr)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WSLAVirtualMachine::Settings WSLASession::CreateVmSettings(const WSLA_SESSION_SETTINGS& Settings)
|
||||
{
|
||||
WSLAVirtualMachine::Settings vmSettings{};
|
||||
vmSettings.CpuCount = Settings.CpuCount;
|
||||
vmSettings.MemoryMb = Settings.MemoryMb;
|
||||
vmSettings.NetworkingMode = Settings.NetworkingMode;
|
||||
vmSettings.BootTimeoutMs = Settings.BootTimeoutMs;
|
||||
vmSettings.FeatureFlags = static_cast<WSLAFeatureFlags>(Settings.FeatureFlags);
|
||||
vmSettings.DisplayName = Settings.DisplayName;
|
||||
|
||||
if (Settings.RootVhdOverride != nullptr)
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, Settings.RootVhdTypeOverride == nullptr);
|
||||
|
||||
vmSettings.RootVhd = Settings.RootVhdOverride;
|
||||
vmSettings.RootVhdType = Settings.RootVhdTypeOverride;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
#ifdef WSLA_TEST_DISTRO_PATH
|
||||
|
||||
vmSettings.RootVhd = TEXT(WSLA_TEST_DISTRO_PATH);
|
||||
|
||||
#else
|
||||
vmSettings.RootVhd = std::filesystem::path(common::wslutil::GetMsiPackagePath().value()) / L"wslarootfs.vhd";
|
||||
|
||||
#endif
|
||||
|
||||
vmSettings.RootVhdType = "squashfs";
|
||||
}
|
||||
|
||||
if (Settings.DmesgOutput != 0)
|
||||
{
|
||||
vmSettings.DmesgHandle.reset(wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(ULongToHandle(Settings.DmesgOutput)));
|
||||
}
|
||||
|
||||
return vmSettings;
|
||||
}
|
||||
|
||||
WSLASession::~WSLASession()
|
||||
@ -54,12 +116,82 @@ WSLASession::~WSLASession()
|
||||
}
|
||||
}
|
||||
|
||||
void WSLASession::ConfigureStorage(const WSLA_SESSION_SETTINGS& Settings)
|
||||
{
|
||||
if (Settings.StoragePath == nullptr)
|
||||
{
|
||||
// If no storage path is specified, use a tmpfs for convenience.
|
||||
m_virtualMachine->Mount("", "/root", "tmpfs", "", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
std::filesystem::path storagePath{Settings.StoragePath};
|
||||
THROW_HR_IF_MSG(E_INVALIDARG, !storagePath.is_absolute(), "Storage path is not absolute: %ls", storagePath.c_str());
|
||||
|
||||
m_storageVhdPath = storagePath / "storage.vhdx";
|
||||
|
||||
std::string diskDevice;
|
||||
std::optional<ULONG> diskLun{};
|
||||
bool vhdCreated = false;
|
||||
|
||||
auto deleteVhdOnFailure = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
|
||||
if (vhdCreated)
|
||||
{
|
||||
if (diskLun.has_value())
|
||||
{
|
||||
m_virtualMachine->DetachDisk(diskLun.value());
|
||||
}
|
||||
|
||||
auto runAsUser = wil::CoImpersonateClient();
|
||||
LOG_IF_WIN32_BOOL_FALSE(DeleteFileW(m_storageVhdPath.c_str()));
|
||||
}
|
||||
});
|
||||
|
||||
auto result =
|
||||
wil::ResultFromException([&]() { diskDevice = m_virtualMachine->AttachDisk(m_storageVhdPath.c_str(), false).second; });
|
||||
|
||||
if (FAILED(result))
|
||||
{
|
||||
THROW_HR_IF_MSG(
|
||||
result,
|
||||
result != HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) && result != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
|
||||
"Failed to attach vhd: %ls",
|
||||
m_storageVhdPath.c_str());
|
||||
|
||||
// If the VHD wasn't found, create it.
|
||||
WSL_LOG("CreateStorageVhd", TraceLoggingValue(m_storageVhdPath.c_str(), "StorageVhdPath"));
|
||||
|
||||
auto runAsUser = wil::CoImpersonateClient();
|
||||
|
||||
std::filesystem::create_directories(storagePath);
|
||||
wsl::core::filesystem::CreateVhd(
|
||||
m_storageVhdPath.c_str(), Settings.MaximumStorageSizeMb * _1MB, m_userSession->GetUserSid(), false, false);
|
||||
vhdCreated = true;
|
||||
|
||||
// Then attach the new disk.
|
||||
std::tie(diskLun, diskDevice) = m_virtualMachine->AttachDisk(m_storageVhdPath.c_str(), false);
|
||||
|
||||
// Then format it.
|
||||
Ext4Format(diskDevice);
|
||||
}
|
||||
|
||||
// Mount the device to /root.
|
||||
m_virtualMachine->Mount(diskDevice.c_str(), "/root", "ext4", "", 0);
|
||||
|
||||
deleteVhdOnFailure.release();
|
||||
}
|
||||
|
||||
HRESULT WSLASession::GetDisplayName(LPWSTR* DisplayName)
|
||||
{
|
||||
*DisplayName = wil::make_unique_string<wil::unique_cotaskmem_string>(m_displayName.c_str()).release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
const std::wstring& WSLASession::DisplayName() const
|
||||
{
|
||||
return m_displayName;
|
||||
}
|
||||
|
||||
HRESULT WSLASession::PullImage(LPCWSTR Image, const WSLA_REGISTRY_AUTHENTICATION_INFORMATION* RegistryInformation, IProgressCallback* ProgressCallback)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
@ -80,14 +212,17 @@ HRESULT WSLASession::DeleteImage(LPCWSTR Image)
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT WSLASession::CreateContainer(const WSLA_CONTAINER_OPTIONS* Options, IWSLAContainer** Container)
|
||||
HRESULT WSLASession::CreateContainer(const WSLA_CONTAINER_OPTIONS* containerOptions, IWSLAContainer** Container)
|
||||
try
|
||||
{
|
||||
// Basic instanciation for testing.
|
||||
// TODO: Implement.
|
||||
RETURN_HR_IF_NULL(E_POINTER, containerOptions);
|
||||
|
||||
auto container = wil::MakeOrThrow<WSLAContainer>();
|
||||
container.CopyTo(__uuidof(IWSLAContainer), (void**)Container);
|
||||
std::lock_guard lock{m_lock};
|
||||
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_virtualMachine);
|
||||
|
||||
// TODO: Log entrance into the function.
|
||||
auto container = WSLAContainer::Create(*containerOptions, *m_virtualMachine.Get());
|
||||
THROW_IF_FAILED(container.CopyTo(__uuidof(IWSLAContainer), (void**)Container));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@ -127,6 +262,15 @@ try
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
void WSLASession::Ext4Format(const std::string& Device)
|
||||
{
|
||||
constexpr auto mkfsPath = "/usr/sbin/mkfs.ext4";
|
||||
ServiceProcessLauncher launcher(mkfsPath, {mkfsPath, Device});
|
||||
auto result = launcher.Launch(*m_virtualMachine.Get()).WaitAndCaptureOutput();
|
||||
|
||||
THROW_HR_IF_MSG(E_FAIL, result.Code != 0, "%hs", launcher.FormatResult(result).c_str());
|
||||
}
|
||||
|
||||
HRESULT WSLASession::FormatVirtualDisk(LPCWSTR Path)
|
||||
try
|
||||
{
|
||||
@ -142,11 +286,7 @@ try
|
||||
auto detachDisk = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [this, lun]() { m_virtualMachine->DetachDisk(lun); });
|
||||
|
||||
// Format it to ext4.
|
||||
constexpr auto mkfsPath = "/usr/sbin/mkfs.ext4";
|
||||
ServiceProcessLauncher launcher(mkfsPath, {mkfsPath, device});
|
||||
auto result = launcher.Launch(*m_virtualMachine.Get()).WaitAndCaptureOutput();
|
||||
|
||||
THROW_HR_IF_MSG(E_FAIL, result.Code != 0, "%hs", launcher.FormatResult(result).c_str());
|
||||
Ext4Format(device);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@ -23,10 +23,11 @@ class DECLSPEC_UUID("4877FEFC-4977-4929-A958-9F36AA1892A4") WSLASession
|
||||
: public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IWSLASession, IFastRundown>
|
||||
{
|
||||
public:
|
||||
WSLASession(const WSLA_SESSION_SETTINGS& Settings, WSLAUserSessionImpl& userSessionImpl, const VIRTUAL_MACHINE_SETTINGS& VmSettings);
|
||||
WSLASession(const WSLA_SESSION_SETTINGS& Settings, WSLAUserSessionImpl& userSessionImpl);
|
||||
~WSLASession();
|
||||
|
||||
IFACEMETHOD(GetDisplayName)(LPWSTR* DisplayName) override;
|
||||
const std::wstring& DisplayName() const;
|
||||
|
||||
// Image management.
|
||||
IFACEMETHOD(PullImage)(_In_ LPCWSTR Image, _In_ const WSLA_REGISTRY_AUTHENTICATION_INFORMATION* RegistryInformation, _In_ IProgressCallback* ProgressCallback) override;
|
||||
@ -51,11 +52,19 @@ public:
|
||||
void OnUserSessionTerminating();
|
||||
|
||||
private:
|
||||
static WSLAVirtualMachine::Settings CreateVmSettings(const WSLA_SESSION_SETTINGS& Settings);
|
||||
|
||||
void ConfigureStorage(const WSLA_SESSION_SETTINGS& Settings);
|
||||
void Ext4Format(const std::string& Device);
|
||||
|
||||
WSLA_SESSION_SETTINGS m_sessionSettings; // TODO: Revisit to see if we should have session settings as a member or not
|
||||
WSLAUserSessionImpl* m_userSession = nullptr;
|
||||
Microsoft::WRL::ComPtr<WSLAVirtualMachine> m_virtualMachine;
|
||||
std::wstring m_displayName;
|
||||
std::filesystem::path m_storageVhdPath;
|
||||
std::mutex m_lock;
|
||||
|
||||
// TODO: Add container tracking here. Could reuse m_lock for that.
|
||||
};
|
||||
|
||||
} // namespace wsl::windows::service::wsla
|
||||
@ -47,10 +47,9 @@ PSID WSLAUserSessionImpl::GetUserSid() const
|
||||
return m_tokenInfo->User.Sid;
|
||||
}
|
||||
|
||||
HRESULT wsl::windows::service::wsla::WSLAUserSessionImpl::CreateSession(
|
||||
const WSLA_SESSION_SETTINGS* Settings, const VIRTUAL_MACHINE_SETTINGS* VmSettings, IWSLASession** WslaSession)
|
||||
HRESULT WSLAUserSessionImpl::CreateSession(const WSLA_SESSION_SETTINGS* Settings, IWSLASession** WslaSession)
|
||||
{
|
||||
auto session = wil::MakeOrThrow<WSLASession>(*Settings, *this, *VmSettings);
|
||||
auto session = wil::MakeOrThrow<WSLASession>(*Settings, *this);
|
||||
|
||||
std::lock_guard lock(m_wslaSessionsLock);
|
||||
auto it = m_sessions.emplace(session.Get());
|
||||
@ -63,6 +62,24 @@ HRESULT wsl::windows::service::wsla::WSLAUserSessionImpl::CreateSession(
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT WSLAUserSessionImpl::OpenSessionByName(LPCWSTR DisplayName, IWSLASession** Session)
|
||||
{
|
||||
std::lock_guard lock(m_wslaSessionsLock);
|
||||
|
||||
// TODO: ACL check
|
||||
// TODO: Check for duplicate on session creation.
|
||||
for (auto& e : m_sessions)
|
||||
{
|
||||
if (e->DisplayName() == DisplayName)
|
||||
{
|
||||
THROW_IF_FAILED(e->QueryInterface(__uuidof(IWSLASession), (void**)Session));
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
|
||||
}
|
||||
|
||||
wsl::windows::service::wsla::WSLAUserSession::WSLAUserSession(std::weak_ptr<WSLAUserSessionImpl>&& Session) :
|
||||
m_session(std::move(Session))
|
||||
{
|
||||
@ -77,14 +94,13 @@ HRESULT wsl::windows::service::wsla::WSLAUserSession::GetVersion(_Out_ WSLA_VERS
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT wsl::windows::service::wsla::WSLAUserSession::CreateSession(
|
||||
const WSLA_SESSION_SETTINGS* Settings, const VIRTUAL_MACHINE_SETTINGS* VmSettings, IWSLASession** WslaSession)
|
||||
HRESULT wsl::windows::service::wsla::WSLAUserSession::CreateSession(const WSLA_SESSION_SETTINGS* Settings, IWSLASession** WslaSession)
|
||||
try
|
||||
{
|
||||
auto session = m_session.lock();
|
||||
RETURN_HR_IF(RPC_E_DISCONNECTED, !session);
|
||||
|
||||
return session->CreateSession(Settings, VmSettings, WslaSession);
|
||||
return session->CreateSession(Settings, WslaSession);
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
@ -92,7 +108,18 @@ HRESULT wsl::windows::service::wsla::WSLAUserSession::ListSessions(WSLA_SESSION_
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT wsl::windows::service::wsla::WSLAUserSession::OpenSession(ULONG Id, IWSLASession** Session)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT wsl::windows::service::wsla::WSLAUserSession::OpenSessionByName(LPCWSTR DisplayName, IWSLASession** Session)
|
||||
try
|
||||
{
|
||||
auto session = m_session.lock();
|
||||
RETURN_HR_IF(RPC_E_DISCONNECTED, !session);
|
||||
|
||||
return session->OpenSessionByName(DisplayName, Session);
|
||||
}
|
||||
CATCH_RETURN();
|
||||
@ -29,7 +29,8 @@ public:
|
||||
|
||||
PSID GetUserSid() const;
|
||||
|
||||
HRESULT CreateSession(const WSLA_SESSION_SETTINGS* Settings, const VIRTUAL_MACHINE_SETTINGS* VmSettings, IWSLASession** WslaSession);
|
||||
HRESULT CreateSession(const WSLA_SESSION_SETTINGS* Settings, IWSLASession** WslaSession);
|
||||
HRESULT OpenSessionByName(_In_ LPCWSTR DisplayName, _Out_ IWSLASession** Session);
|
||||
|
||||
void OnSessionTerminated(WSLASession* Session);
|
||||
|
||||
@ -52,9 +53,10 @@ public:
|
||||
WSLAUserSession& operator=(const WSLAUserSession&) = delete;
|
||||
|
||||
IFACEMETHOD(GetVersion)(_Out_ WSLA_VERSION* Version) override;
|
||||
IFACEMETHOD(CreateSession)(const WSLA_SESSION_SETTINGS* WslaSessionSettings, const VIRTUAL_MACHINE_SETTINGS* VmSettings, IWSLASession** WslaSession) override;
|
||||
IFACEMETHOD(CreateSession)(const WSLA_SESSION_SETTINGS* WslaSessionSettings, IWSLASession** WslaSession) override;
|
||||
IFACEMETHOD(ListSessions)(_Out_ WSLA_SESSION_INFORMATION** Sessions, _Out_ ULONG* SessionsCount) override;
|
||||
IFACEMETHOD(OpenSession)(_In_ ULONG Id, _Out_ IWSLASession** Session) override;
|
||||
IFACEMETHOD(OpenSessionByName)(_In_ LPCWSTR DisplayName, _Out_ IWSLASession** Session) override;
|
||||
|
||||
private:
|
||||
std::weak_ptr<WSLAUserSessionImpl> m_session;
|
||||
|
||||
@ -50,11 +50,15 @@ HRESULT WSLAUserSessionFactory::CreateInstance(_In_ IUnknown* pUnkOuter, _In_ RE
|
||||
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);
|
||||
return EqualSid(it->GetUserSid(), tokenInfo->User.Sid);
|
||||
});
|
||||
|
||||
if (session == g_sessions->end())
|
||||
{
|
||||
wil::unique_hlocal_string sid;
|
||||
THROW_IF_WIN32_BOOL_FALSE(ConvertSidToStringSid(tokenInfo->User.Sid, &sid));
|
||||
WSL_LOG("WSLAUserSession created", TraceLoggingValue(sid.get(), "sid"));
|
||||
|
||||
session = g_sessions->insert(g_sessions->end(), std::make_shared<WSLAUserSessionImpl>(userToken.get(), std::move(tokenInfo)));
|
||||
}
|
||||
|
||||
|
||||
@ -16,9 +16,9 @@ Abstract:
|
||||
#include <format>
|
||||
#include <filesystem>
|
||||
#include "hcs_schema.h"
|
||||
#include "VirtioNetworking.h"
|
||||
#include "NatNetworking.h"
|
||||
#include "WSLAUserSession.h"
|
||||
#include "DnsResolver.h"
|
||||
#include "ServiceProcessLauncher.h"
|
||||
|
||||
using namespace wsl::windows::common;
|
||||
@ -33,28 +33,17 @@ constexpr auto SAVED_STATE_FILE_EXTENSION = L".vmrs";
|
||||
constexpr auto SAVED_STATE_FILE_PREFIX = L"saved-state-";
|
||||
constexpr auto RECEIVE_TIMEOUT = 30 * 1000;
|
||||
|
||||
WSLAVirtualMachine::WSLAVirtualMachine(const VIRTUAL_MACHINE_SETTINGS& Settings, PSID UserSid, WSLAUserSessionImpl* Session) :
|
||||
m_settings(Settings), m_userSid(UserSid)
|
||||
// WSLA-specific virtio device class IDs.
|
||||
DEFINE_GUID(WSLA_VIRTIO_NET_CLASS_ID, 0x7B3C9A42, 0x8E1F, 0x4D5A, 0x9F, 0x2E, 0xC4, 0xA7, 0xB8, 0xD3, 0xE6, 0xF1); // {7B3C9A42-8E1F-4D5A-9F2E-C4A7B8D3E6F1}
|
||||
|
||||
WSLAVirtualMachine::WSLAVirtualMachine(WSLAVirtualMachine::Settings&& Settings, PSID UserSid) :
|
||||
m_settings(std::move(Settings)), m_userSid(UserSid)
|
||||
{
|
||||
THROW_IF_FAILED(CoCreateGuid(&m_vmId));
|
||||
|
||||
m_vmIdString = wsl::shared::string::GuidToString<wchar_t>(m_vmId, wsl::shared::string::GuidToStringFlags::Uppercase);
|
||||
m_userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);
|
||||
m_crashDumpFolder = GetCrashDumpFolder();
|
||||
|
||||
if (Settings.EnableDebugShell)
|
||||
{
|
||||
m_debugShellPipe = wsl::windows::common::wslutil::GetDebugShellPipeName(m_userSid) + m_settings.DisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT WSLAVirtualMachine::GetDebugShellPipe(LPWSTR* pipePath)
|
||||
{
|
||||
RETURN_HR_IF(E_INVALIDARG, m_debugShellPipe.empty());
|
||||
|
||||
*pipePath = wil::make_unique_string<wil::unique_cotaskmem_string>(m_debugShellPipe.c_str()).release();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void WSLAVirtualMachine::OnSessionTerminated()
|
||||
@ -76,6 +65,11 @@ void WSLAVirtualMachine::OnSessionTerminated()
|
||||
WSLAVirtualMachine::~WSLAVirtualMachine()
|
||||
{
|
||||
WSL_LOG("WSLATerminateVmStart", TraceLoggingValue(m_running, "running"));
|
||||
if (!m_computeSystem)
|
||||
{
|
||||
// If m_computeSystem is null, don't try to stop the VM since it never started.
|
||||
return;
|
||||
}
|
||||
|
||||
m_initChannel.Close();
|
||||
|
||||
@ -94,6 +88,12 @@ WSLAVirtualMachine::~WSLAVirtualMachine()
|
||||
|
||||
WSL_LOG("WSLATerminateVm", TraceLoggingValue(forceTerminate, "forced"), TraceLoggingValue(m_running, "running"));
|
||||
|
||||
// Shutdown DeviceHostProxy before resetting compute system
|
||||
if (m_guestDeviceManager)
|
||||
{
|
||||
m_guestDeviceManager->Shutdown();
|
||||
}
|
||||
|
||||
m_computeSystem.reset();
|
||||
|
||||
for (const auto& e : m_attachedDisks)
|
||||
@ -203,14 +203,11 @@ void WSLAVirtualMachine::Start()
|
||||
kernelCmdLine += L" hv_utils.timesync_implicit=1";
|
||||
|
||||
wil::unique_handle dmesgOutput;
|
||||
if (m_settings.DmesgOutput != 0)
|
||||
{
|
||||
dmesgOutput.reset(wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(ULongToHandle(m_settings.DmesgOutput)));
|
||||
}
|
||||
dmesgOutput = std::move(m_settings.DmesgHandle);
|
||||
|
||||
m_dmesgCollector = DmesgCollector::Create(m_vmId, m_vmExitEvent, true, false, L"", true, std::move(dmesgOutput));
|
||||
|
||||
if (m_settings.EnableEarlyBootDmesg)
|
||||
if (FeatureEnabled(WslaFeatureFlagsEarlyBootDmesg))
|
||||
{
|
||||
kernelCmdLine += L" earlycon=uart8250,io,0x3f8,115200";
|
||||
vmSettings.Devices.ComPorts["0"] = hcs::ComPort{m_dmesgCollector->EarlyConsoleName()};
|
||||
@ -308,9 +305,17 @@ void WSLAVirtualMachine::Start()
|
||||
auto runtimeId = wsl::windows::common::hcs::GetRuntimeId(m_computeSystem.get());
|
||||
WI_ASSERT(IsEqualGUID(m_vmId, runtimeId));
|
||||
|
||||
// Initialize DeviceHostProxy for virtio device support.
|
||||
// N.B. This is currently only needed for VirtioProxy networking mode but would also be needed for virtiofs.
|
||||
if (m_settings.NetworkingMode == WSLANetworkingModeVirtioProxy)
|
||||
{
|
||||
m_guestDeviceManager = std::make_shared<GuestDeviceManager>(m_vmIdString, m_vmId);
|
||||
}
|
||||
|
||||
wsl::windows::common::hcs::RegisterCallback(m_computeSystem.get(), &s_OnExit, this);
|
||||
|
||||
wsl::windows::common::hcs::StartComputeSystem(m_computeSystem.get(), json.c_str());
|
||||
m_running = true;
|
||||
|
||||
// Create a socket listening for crash dumps.
|
||||
auto crashDumpSocket = wsl::windows::common::hvsocket::Listen(runtimeId, LX_INIT_UTILITY_VM_CRASH_DUMP_PORT);
|
||||
@ -351,7 +356,7 @@ void WSLAVirtualMachine::Start()
|
||||
Mount(m_initChannel, device.c_str(), "", "ext4", "ro", WSLA_MOUNT::KernelModules);
|
||||
|
||||
// Configure GPU if requested.
|
||||
if (m_settings.EnableGPU)
|
||||
if (FeatureEnabled(WslaFeatureFlagsGPU))
|
||||
{
|
||||
hcs::ModifySettingRequest<hcs::GpuConfiguration> gpuRequest{};
|
||||
gpuRequest.ResourcePath = L"VirtualMachine/ComputeTopology/Gpu";
|
||||
@ -372,32 +377,23 @@ void WSLAVirtualMachine::Start()
|
||||
|
||||
void WSLAVirtualMachine::ConfigureMounts()
|
||||
{
|
||||
auto [_, device] = AttachDisk(m_settings.RootVhd, true);
|
||||
auto [_, device] = AttachDisk(m_settings.RootVhd.c_str(), true);
|
||||
|
||||
Mount(m_initChannel, device.c_str(), "/mnt", m_settings.RootVhdType, "ro", WSLAMountFlagsChroot | WSLAMountFlagsWriteableOverlayFs);
|
||||
Mount(m_initChannel, device.c_str(), "/mnt", m_settings.RootVhdType.c_str(), "ro", WSLAMountFlagsChroot | WSLAMountFlagsWriteableOverlayFs);
|
||||
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);
|
||||
|
||||
if (m_settings.EnableGPU) // TODO: re-think how GPU settings should work at the session level API.
|
||||
if (FeatureEnabled(WslaFeatureFlagsGPU)) // TODO: re-think how GPU settings should work at the session level API.
|
||||
{
|
||||
MountGpuLibraries("/usr/lib/wsl/lib", "/usr/lib/wsl/drivers", WSLAMountFlagsNone);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_settings.ContainerRootVhd) // TODO: re-think how container root settings should work at the session level API.
|
||||
{
|
||||
auto [_, containerRootDevice] = AttachDisk(m_settings.ContainerRootVhd, false);
|
||||
|
||||
if (m_settings.FormatContainerRootVhd)
|
||||
{
|
||||
ServiceProcessLauncher formatProcessLauncher{"/usr/sbin/mkfs.ext4", {"/usr/sbin/mkfs.ext4", containerRootDevice}};
|
||||
auto formatProcess = formatProcessLauncher.Launch(*this);
|
||||
THROW_HR_IF(E_FAIL, formatProcess.WaitAndCaptureOutput().Code != 0);
|
||||
}
|
||||
|
||||
Mount(m_initChannel, containerRootDevice.c_str(), "/root", "ext4", "rw", 0);
|
||||
}
|
||||
bool WSLAVirtualMachine::FeatureEnabled(WSLAFeatureFlags Value) const
|
||||
{
|
||||
return static_cast<ULONG>(m_settings.FeatureFlags) & static_cast<ULONG>(Value);
|
||||
}
|
||||
|
||||
void WSLAVirtualMachine::WatchForExitedProcesses(wsl::shared::SocketChannel& Channel)
|
||||
@ -445,12 +441,17 @@ CATCH_LOG();
|
||||
|
||||
void WSLAVirtualMachine::ConfigureNetworking()
|
||||
{
|
||||
if (m_settings.NetworkingMode == WSLANetworkingModeNone)
|
||||
switch (m_settings.NetworkingMode)
|
||||
{
|
||||
case WSLANetworkingModeNone:
|
||||
return;
|
||||
case WSLANetworkingModeNAT:
|
||||
case WSLANetworkingModeVirtioProxy:
|
||||
break;
|
||||
default:
|
||||
THROW_HR_MSG(E_INVALIDARG, "Invalid networking mode: %lu", m_settings.NetworkingMode);
|
||||
}
|
||||
else if (m_settings.NetworkingMode == WSLANetworkingModeNAT)
|
||||
{
|
||||
|
||||
// Launch GNS
|
||||
std::vector<WSLA_PROCESS_FD> fds(1);
|
||||
fds[0].Fd = -1;
|
||||
@ -459,8 +460,13 @@ void WSLAVirtualMachine::ConfigureNetworking()
|
||||
std::vector<const char*> cmd{"/gns", LX_INIT_GNS_SOCKET_ARG};
|
||||
|
||||
// If DNS tunnelling is enabled, use an additional for its channel.
|
||||
if (m_settings.EnableDnsTunneling)
|
||||
if (FeatureEnabled(WslaFeatureFlagsDnsTunneling))
|
||||
{
|
||||
THROW_HR_IF_MSG(
|
||||
E_NOTIMPL,
|
||||
m_settings.NetworkingMode == WSLANetworkingModeVirtioProxy,
|
||||
"DNS tunneling not currently supported for VirtioProxy");
|
||||
|
||||
fds.emplace_back(WSLA_PROCESS_FD{.Fd = -1, .Type = WSLAFdType::WSLAFdTypeDefault});
|
||||
THROW_IF_FAILED(wsl::core::networking::DnsResolver::LoadDnsResolverMethods());
|
||||
}
|
||||
@ -472,7 +478,6 @@ void WSLAVirtualMachine::ConfigureNetworking()
|
||||
|
||||
// Because the file descriptors numbers aren't known in advance, the command line needs to be generated after the file
|
||||
// descriptors are allocated.
|
||||
|
||||
std::string socketFdArg;
|
||||
std::string dnsFdArg;
|
||||
int gnsChannelFd = -1;
|
||||
@ -497,7 +502,10 @@ void WSLAVirtualMachine::ConfigureNetworking()
|
||||
};
|
||||
|
||||
auto process = CreateLinuxProcess(options, nullptr, prepareCommandLine);
|
||||
auto gnsChannel = wsl::core::GnsChannel(wil::unique_socket{(SOCKET)process->GetStdHandle(gnsChannelFd).release()});
|
||||
|
||||
if (m_settings.NetworkingMode == WSLANetworkingModeNAT)
|
||||
{
|
||||
// TODO: refactor this to avoid using wsl config
|
||||
static wsl::core::Config config(nullptr);
|
||||
|
||||
@ -510,18 +518,19 @@ void WSLAVirtualMachine::ConfigureNetworking()
|
||||
m_networkEngine = std::make_unique<wsl::core::NatNetworking>(
|
||||
m_computeSystem.get(),
|
||||
wsl::core::NatNetworking::CreateNetwork(config),
|
||||
wil::unique_socket{(SOCKET)process->GetStdHandle(gnsChannelFd).release()},
|
||||
std::move(gnsChannel),
|
||||
config,
|
||||
dnsChannelFd != -1 ? wil::unique_socket{(SOCKET)process->GetStdHandle(dnsChannelFd).release()} : wil::unique_socket{});
|
||||
}
|
||||
else
|
||||
{
|
||||
m_networkEngine = std::make_unique<wsl::core::VirtioNetworking>(
|
||||
std::move(gnsChannel), true, m_guestDeviceManager, WSLA_VIRTIO_NET_CLASS_ID, m_userToken);
|
||||
}
|
||||
|
||||
m_networkEngine->Initialize();
|
||||
|
||||
LaunchPortRelay();
|
||||
}
|
||||
else
|
||||
{
|
||||
THROW_HR_MSG(E_INVALIDARG, "Invalid networking mode: %lu", m_settings.NetworkingMode);
|
||||
}
|
||||
}
|
||||
|
||||
void CALLBACK WSLAVirtualMachine::s_OnExit(_In_ HCS_EVENT* Event, _In_opt_ void* Context)
|
||||
@ -605,7 +614,7 @@ std::pair<ULONG, std::string> WSLAVirtualMachine::AttachDisk(_In_ PCWSTR Path, _
|
||||
|
||||
auto result = wil::ResultFromException([&]() {
|
||||
std::lock_guard lock{m_lock};
|
||||
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_running);
|
||||
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_running);
|
||||
|
||||
AttachedDisk disk{Path};
|
||||
|
||||
@ -727,18 +736,19 @@ 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{};
|
||||
int32_t ptyMaster{};
|
||||
{
|
||||
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_running);
|
||||
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_running);
|
||||
|
||||
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;
|
||||
@ -857,7 +867,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;
|
||||
@ -911,6 +921,13 @@ Microsoft::WRL::ComPtr<WSLAProcess> WSLAVirtualMachine::CreateLinuxProcess(_In_
|
||||
return process;
|
||||
}
|
||||
|
||||
void WSLAVirtualMachine::Mount(LPCSTR Source, LPCSTR Target, LPCSTR Type, LPCSTR Options, ULONG Flags)
|
||||
{
|
||||
std::lock_guard lock{m_lock};
|
||||
|
||||
Mount(m_initChannel, Source, Target, Type, Options, Flags);
|
||||
}
|
||||
|
||||
void WSLAVirtualMachine::Mount(shared::SocketChannel& Channel, LPCSTR Source, LPCSTR Target, LPCSTR Type, LPCSTR Options, ULONG Flags)
|
||||
{
|
||||
static_assert(WSLAMountFlagsNone == WSLA_MOUNT::None);
|
||||
@ -984,7 +1001,7 @@ try
|
||||
{
|
||||
std::lock_guard lock(m_lock);
|
||||
|
||||
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_running);
|
||||
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_running);
|
||||
|
||||
WSLA_SHUTDOWN message{};
|
||||
m_initChannel.SendMessage(message);
|
||||
@ -1001,7 +1018,7 @@ HRESULT WSLAVirtualMachine::Signal(_In_ LONG Pid, _In_ int Signal)
|
||||
try
|
||||
{
|
||||
std::lock_guard lock(m_lock);
|
||||
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_running);
|
||||
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_running);
|
||||
|
||||
WSLA_SIGNAL message;
|
||||
message.Pid = Pid;
|
||||
@ -1220,7 +1237,7 @@ CATCH_RETURN();
|
||||
|
||||
void WSLAVirtualMachine::MountGpuLibraries(_In_ LPCSTR LibrariesMountPoint, _In_ LPCSTR DriversMountpoint, _In_ DWORD Flags)
|
||||
{
|
||||
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_CONFIG_VALUE), !m_settings.EnableGPU);
|
||||
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_CONFIG_VALUE), !FeatureEnabled(WslaFeatureFlagsGPU));
|
||||
|
||||
auto [channel, _, __] = Fork(WSLA_FORK::Thread);
|
||||
|
||||
|
||||
@ -16,6 +16,8 @@ Abstract:
|
||||
#include "INetworkingEngine.h"
|
||||
#include "hcs.hpp"
|
||||
#include "Dmesg.h"
|
||||
#include "DnsResolver.h"
|
||||
#include "GuestDeviceManager.h"
|
||||
#include "WSLAApi.h"
|
||||
#include "WSLAProcess.h"
|
||||
|
||||
@ -41,9 +43,22 @@ public:
|
||||
wil::unique_socket Socket;
|
||||
};
|
||||
|
||||
struct Settings
|
||||
{
|
||||
std::wstring DisplayName;
|
||||
ULONGLONG MemoryMb{};
|
||||
ULONG CpuCount;
|
||||
ULONG BootTimeoutMs{};
|
||||
WSLANetworkingMode NetworkingMode{};
|
||||
WSLAFeatureFlags FeatureFlags{};
|
||||
wil::unique_handle DmesgHandle;
|
||||
std::filesystem::path RootVhd;
|
||||
std::string RootVhdType;
|
||||
};
|
||||
|
||||
using TPrepareCommandLine = std::function<void(const std::vector<ConnectedSocket>&)>;
|
||||
|
||||
WSLAVirtualMachine(const VIRTUAL_MACHINE_SETTINGS& Settings, PSID Sid, WSLAUserSessionImpl* UserSession);
|
||||
WSLAVirtualMachine(Settings&& Settings, PSID Sid);
|
||||
|
||||
~WSLAVirtualMachine();
|
||||
|
||||
@ -54,7 +69,6 @@ public:
|
||||
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(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(MountWindowsFolder(_In_ LPCWSTR WindowsPath, _In_ LPCSTR LinuxPath, _In_ BOOL ReadOnly)) override;
|
||||
@ -69,6 +83,7 @@ public:
|
||||
|
||||
std::pair<ULONG, std::string> AttachDisk(_In_ PCWSTR Path, _In_ BOOL ReadOnly);
|
||||
void DetachDisk(_In_ ULONG Lun);
|
||||
void Mount(_In_ LPCSTR Source, _In_ LPCSTR Target, _In_ LPCSTR Type, _In_ LPCSTR Options, _In_ ULONG Flags);
|
||||
|
||||
private:
|
||||
static void Mount(wsl::shared::SocketChannel& Channel, LPCSTR Source, _In_ LPCSTR Target, _In_ LPCSTR Type, _In_ LPCSTR Options, _In_ ULONG Flags);
|
||||
@ -80,9 +95,11 @@ private:
|
||||
void ConfigureMounts();
|
||||
void OnExit(_In_ const HCS_EVENT* Event);
|
||||
void OnCrash(_In_ const HCS_EVENT* Event);
|
||||
bool FeatureEnabled(WSLAFeatureFlags Flag) const;
|
||||
|
||||
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);
|
||||
@ -109,7 +126,7 @@ private:
|
||||
bool AccessGranted = false;
|
||||
};
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS m_settings;
|
||||
Settings m_settings;
|
||||
std::thread m_processExitThread;
|
||||
std::thread m_crashDumpCollectionThread;
|
||||
|
||||
@ -119,7 +136,7 @@ private:
|
||||
int m_coldDiscardShiftSize{};
|
||||
bool m_running = false;
|
||||
PSID m_userSid{};
|
||||
wil::unique_handle m_userToken;
|
||||
wil::shared_handle m_userToken;
|
||||
std::wstring m_debugShellPipe;
|
||||
|
||||
std::mutex m_trackedProcessesLock;
|
||||
@ -132,6 +149,7 @@ private:
|
||||
bool m_vmSavedStateCaptured = false;
|
||||
bool m_crashLogCaptured = false;
|
||||
|
||||
std::shared_ptr<GuestDeviceManager> m_guestDeviceManager;
|
||||
std::shared_ptr<DmesgCollector> m_dmesgCollector;
|
||||
wil::unique_event m_vmExitEvent{wil::EventOptions::ManualReset};
|
||||
wil::unique_event m_vmTerminatingEvent{wil::EventOptions::ManualReset};
|
||||
|
||||
@ -104,7 +104,7 @@ struct WSLA_IMAGE_INFORMATION
|
||||
|
||||
struct WSLA_PROCESS_OPTIONS
|
||||
{
|
||||
LPCSTR Executable;
|
||||
[unique] LPCSTR Executable;
|
||||
[unique] LPCSTR CurrentDirectory;
|
||||
[size_is(CommandLineCount)] LPCSTR* CommandLine;
|
||||
ULONG CommandLineCount;
|
||||
@ -112,12 +112,15 @@ 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
|
||||
{
|
||||
LPCSTR HostPath;
|
||||
LPCSTR ContainerHostPath;
|
||||
LPCSTR ContainerPath;
|
||||
BOOL ReadOnly;
|
||||
};
|
||||
|
||||
struct WSLA_PORT_MAPPING
|
||||
@ -126,11 +129,17 @@ struct WSLA_PORT_MAPPING
|
||||
USHORT ContainerPort;
|
||||
};
|
||||
|
||||
enum WSLA_CONTAINER_FLAGS
|
||||
{
|
||||
WSLA_CONTAINER_FLAG_ENABLE_GPU = 1
|
||||
} ;
|
||||
|
||||
|
||||
struct WSLA_CONTAINER_OPTIONS
|
||||
{
|
||||
LPCSTR Image;
|
||||
LPCSTR Name;
|
||||
struct WSLA_PROCESS_OPTIONS* InitProcessOptions;
|
||||
struct WSLA_PROCESS_OPTIONS InitProcessOptions;
|
||||
[unique, size_is(VolumesCount)] struct WSLA_VOLUME* Volumes;
|
||||
ULONG VolumesCount;
|
||||
[unique, size_is(PortsCount)] struct WSLA_PORT_MAPPING* Ports;
|
||||
@ -197,7 +206,6 @@ interface IWSLAVirtualMachine : IUnknown
|
||||
HRESULT WaitPid([in] LONG Pid, [in] ULONGLONG TimeoutMs, [out] ULONG* State, [out] int* Code);
|
||||
HRESULT Signal([in] LONG Pid, [in] int Signal);
|
||||
HRESULT Shutdown([in] ULONGLONG TimeoutMs);
|
||||
HRESULT GetDebugShellPipe([out] LPWSTR* pipePath);
|
||||
HRESULT MapPort([in] int Family, [in] short WindowsPort, [in] short LinuxPort, [in] BOOL Remove);
|
||||
HRESULT Unmount([in] LPCSTR Path);
|
||||
HRESULT MountWindowsFolder([in] LPCWSTR WindowsPath, [in] LPCSTR LinuxPath, [in] BOOL ReadOnly);
|
||||
@ -207,35 +215,33 @@ interface IWSLAVirtualMachine : IUnknown
|
||||
typedef enum _WSLANetworkingMode
|
||||
{
|
||||
WSLANetworkingModeNone,
|
||||
WSLANetworkingModeNAT
|
||||
WSLANetworkingModeNAT,
|
||||
WSLANetworkingModeVirtioProxy
|
||||
} WSLANetworkingMode;
|
||||
|
||||
typedef
|
||||
struct _VIRTUAL_MACHINE_SETTINGS { // TODO: Delete once the new API is wired.
|
||||
LPCWSTR DisplayName;
|
||||
ULONGLONG MemoryMb;
|
||||
ULONG CpuCount;
|
||||
ULONG BootTimeoutMs;
|
||||
ULONG DmesgOutput;
|
||||
WSLANetworkingMode NetworkingMode;
|
||||
BOOL EnableDnsTunneling;
|
||||
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.
|
||||
LPCWSTR ContainerRootVhd;
|
||||
BOOL FormatContainerRootVhd;
|
||||
} VIRTUAL_MACHINE_SETTINGS;
|
||||
|
||||
|
||||
typedef enum _WSLAFeatureFlags
|
||||
{
|
||||
WslaFeatureFlagsNone = 0,
|
||||
WslaFeatureFlagsDnsTunneling = 1,
|
||||
WslaFeatureFlagsEarlyBootDmesg = 2,
|
||||
WslaFeatureFlagsGPU = 4,
|
||||
} WSLAFeatureFlags;
|
||||
|
||||
struct WSLA_SESSION_SETTINGS {
|
||||
LPCWSTR DisplayName;
|
||||
LPCWSTR StoragePath;
|
||||
ULONGLONG MaximumStorageSizeMb;
|
||||
ULONG CpuCount;
|
||||
ULONG MemoryMb;
|
||||
ULONG BootTimeoutMs;
|
||||
WSLANetworkingMode NetworkingMode;
|
||||
[unique] ITerminationCallback* TerminationCallback;
|
||||
ULONG FeatureFlags;
|
||||
ULONG DmesgOutput;
|
||||
|
||||
// TODO: Termination callback, flags
|
||||
// Below options are used for debugging purposes only.
|
||||
[unique] LPCWSTR RootVhdOverride;
|
||||
[unique] LPCSTR RootVhdTypeOverride;
|
||||
};
|
||||
|
||||
|
||||
@ -309,9 +315,10 @@ interface IWSLAUserSession : IUnknown
|
||||
HRESULT GetVersion([out] WSLA_VERSION* Version);
|
||||
|
||||
// Session managment.
|
||||
HRESULT CreateSession([in] const struct WSLA_SESSION_SETTINGS* Settings, [in] const VIRTUAL_MACHINE_SETTINGS* VmSettings, [out]IWSLASession** Session);
|
||||
HRESULT CreateSession([in] const struct WSLA_SESSION_SETTINGS* Settings, [out]IWSLASession** Session);
|
||||
HRESULT ListSessions([out, size_is(, *SessionsCount)] struct WSLA_SESSION_INFORMATION** Sessions, [out] ULONG* SessionsCount);
|
||||
HRESULT OpenSession([in] ULONG Id, [out]IWSLASession** Session);
|
||||
HRESULT OpenSessionByName([in] LPCWSTR DisplayName, [out] IWSLASession** Session);
|
||||
|
||||
// TODO: Do we need 'TerminateSession()' ?
|
||||
}
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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));
|
||||
|
||||
|
||||
@ -17,15 +17,20 @@ Abstract:
|
||||
#include "WSLAApi.h"
|
||||
#include "wslaservice.h"
|
||||
#include "WSLAProcessLauncher.h"
|
||||
#include "WSLAContainerLauncher.h"
|
||||
#include "WslCoreFilesystem.h"
|
||||
|
||||
using namespace wsl::windows::common::registry;
|
||||
using wsl::windows::common::ProcessFlags;
|
||||
using wsl::windows::common::RunningWSLAContainer;
|
||||
using wsl::windows::common::RunningWSLAProcess;
|
||||
using wsl::windows::common::WSLAContainerLauncher;
|
||||
using wsl::windows::common::WSLAProcessLauncher;
|
||||
using wsl::windows::common::relay::OverlappedIOHandle;
|
||||
using wsl::windows::common::relay::WriteHandle;
|
||||
|
||||
DEFINE_ENUM_FLAG_OPERATORS(WSLAFeatureFlags);
|
||||
|
||||
class WSLATests
|
||||
{
|
||||
WSL_TEST_CLASS(WSLATests)
|
||||
@ -51,17 +56,25 @@ class WSLATests
|
||||
return true;
|
||||
}
|
||||
|
||||
wil::com_ptr<IWSLASession> CreateSession(VIRTUAL_MACHINE_SETTINGS& vmSettings, const WSLA_SESSION_SETTINGS& sessionSettings = {L"wsla-test"})
|
||||
static WSLA_SESSION_SETTINGS GetDefaultSessionSettings()
|
||||
{
|
||||
vmSettings.RootVhdType = "ext4";
|
||||
WSLA_SESSION_SETTINGS settings{};
|
||||
settings.DisplayName = L"wsla-test";
|
||||
settings.CpuCount = 4;
|
||||
settings.MemoryMb = 2024;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
return settings;
|
||||
}
|
||||
|
||||
wil::com_ptr<IWSLASession> CreateSession(const WSLA_SESSION_SETTINGS& sessionSettings = GetDefaultSessionSettings())
|
||||
{
|
||||
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());
|
||||
|
||||
wil::com_ptr<IWSLASession> session;
|
||||
|
||||
VERIFY_SUCCEEDED(userSession->CreateSession(&sessionSettings, &vmSettings, &session));
|
||||
VERIFY_SUCCEEDED(userSession->CreateSession(&sessionSettings, &session));
|
||||
wsl::windows::common::security::ConfigureForCOMImpersonation(session.get());
|
||||
|
||||
return session;
|
||||
@ -132,6 +145,38 @@ class WSLATests
|
||||
return result;
|
||||
}
|
||||
|
||||
void ValidateProcessOutput(RunningWSLAProcess& process, const std::map<int, std::string>& expectedOutput, int expectedResult = 0)
|
||||
{
|
||||
auto result = process.WaitAndCaptureOutput();
|
||||
|
||||
if (result.Code != expectedResult)
|
||||
{
|
||||
LogError(
|
||||
"Comman didn't return expected code (%i). ExitCode: %i, Stdout: '%hs', Stderr: '%hs'",
|
||||
expectedResult,
|
||||
result.Code,
|
||||
result.Output[1].c_str(),
|
||||
result.Output[2].c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& [fd, expected] : expectedOutput)
|
||||
{
|
||||
auto it = result.Output.find(fd);
|
||||
if (it == result.Output.end())
|
||||
{
|
||||
LogError("Expected output on fd %i, but none found.", fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (it->second != expected)
|
||||
{
|
||||
LogError("Unexpected output on fd %i. Expected: '%hs', Actual: '%hs'", fd, expected.c_str(), it->second.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CustomDmesgOutput)
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
@ -139,14 +184,9 @@ class WSLATests
|
||||
auto createVmWithDmesg = [this](bool earlyBootLogging) {
|
||||
auto [read, write] = CreateSubprocessPipe(false, false);
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 2048;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
auto settings = GetDefaultSessionSettings();
|
||||
settings.DmesgOutput = (ULONG) reinterpret_cast<ULONG_PTR>(write.get());
|
||||
settings.EnableEarlyBootDmesg = earlyBootLogging;
|
||||
settings.RootVhd = testVhd.c_str();
|
||||
WI_SetFlagIf(settings.FeatureFlags, WslaFeatureFlagsEarlyBootDmesg, earlyBootLogging);
|
||||
|
||||
std::vector<char> dmesgContent;
|
||||
auto readDmesg = [read = read.get(), &dmesgContent]() mutable {
|
||||
@ -185,7 +225,7 @@ class WSLATests
|
||||
|
||||
write.reset();
|
||||
|
||||
ExpectCommandResult(session.get(), {"/bin/bash", "-c", "echo DmesgTest > /dev/kmsg"}, 0);
|
||||
ExpectCommandResult(session.get(), {"/bin/sh", "-c", "echo DmesgTest > /dev/kmsg"}, 0);
|
||||
|
||||
VERIFY_ARE_EQUAL(session->Shutdown(30 * 1000), S_OK);
|
||||
detach.reset();
|
||||
@ -243,23 +283,16 @@ class WSLATests
|
||||
std::function<void(WSLAVirtualMachineTerminationReason, LPCWSTR)> m_callback;
|
||||
};
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 2048;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
settings.RootVhd = testVhd.c_str();
|
||||
|
||||
std::promise<std::pair<WSLAVirtualMachineTerminationReason, std::wstring>> promise;
|
||||
|
||||
CallbackInstance callback{[&](WSLAVirtualMachineTerminationReason reason, LPCWSTR details) {
|
||||
promise.set_value(std::make_pair(reason, details));
|
||||
}};
|
||||
|
||||
WSLA_SESSION_SETTINGS sessionSettings{L"wsla-test"};
|
||||
WSLA_SESSION_SETTINGS sessionSettings = GetDefaultSessionSettings();
|
||||
sessionSettings.TerminationCallback = &callback;
|
||||
|
||||
auto session = CreateSession(settings, sessionSettings);
|
||||
auto session = CreateSession(sessionSettings);
|
||||
|
||||
wil::com_ptr<IWSLAVirtualMachine> vm;
|
||||
VERIFY_SUCCEEDED(session->GetVirtualMachine(&vm));
|
||||
@ -275,14 +308,7 @@ class WSLATests
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 2048;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
settings.RootVhd = testVhd.c_str();
|
||||
|
||||
auto session = CreateSession(settings);
|
||||
auto session = CreateSession();
|
||||
|
||||
WSLAProcessLauncher launcher("/bin/sh", {"/bin/sh"}, {"TERM=xterm-256color"}, ProcessFlags::None);
|
||||
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 0, .Type = WSLAFdTypeTerminalInput});
|
||||
@ -316,7 +342,7 @@ class WSLATests
|
||||
};
|
||||
|
||||
// Expect the shell prompt to be displayed
|
||||
validateTtyOutput("#");
|
||||
validateTtyOutput("/ #");
|
||||
writeTty("echo OK\n");
|
||||
validateTtyOutput(" echo OK\r\nOK");
|
||||
|
||||
@ -330,20 +356,15 @@ class WSLATests
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 2048;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
auto settings = GetDefaultSessionSettings();
|
||||
settings.NetworkingMode = WSLANetworkingModeNAT;
|
||||
settings.RootVhd = testVhd.c_str();
|
||||
|
||||
auto session = CreateSession(settings);
|
||||
|
||||
// Validate that eth0 has an ip address
|
||||
ExpectCommandResult(
|
||||
session.get(),
|
||||
{"/bin/bash",
|
||||
{"/bin/sh",
|
||||
"-c",
|
||||
"ip a show dev eth0 | grep -iF 'inet ' | grep -E '[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}'"},
|
||||
0);
|
||||
@ -355,21 +376,16 @@ class WSLATests
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 2048;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
auto settings = GetDefaultSessionSettings();
|
||||
settings.NetworkingMode = WSLANetworkingModeNAT;
|
||||
settings.EnableDnsTunneling = true;
|
||||
settings.RootVhd = testVhd.c_str();
|
||||
WI_SetFlag(settings.FeatureFlags, WslaFeatureFlagsDnsTunneling);
|
||||
|
||||
auto session = CreateSession(settings);
|
||||
|
||||
// Validate that eth0 has an ip address
|
||||
ExpectCommandResult(
|
||||
session.get(),
|
||||
{"/bin/bash",
|
||||
{"/bin/sh",
|
||||
"-c",
|
||||
"ip a show dev eth0 | grep -iF 'inet ' | grep -E '[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}'"},
|
||||
0);
|
||||
@ -384,14 +400,7 @@ class WSLATests
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 2048;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
settings.RootVhd = testVhd.c_str();
|
||||
|
||||
auto session = CreateSession(settings);
|
||||
auto session = CreateSession();
|
||||
|
||||
struct FileFd
|
||||
{
|
||||
@ -411,7 +420,7 @@ class WSLATests
|
||||
auto [hresult, _, process] = launcher.LaunchNoThrow(*session);
|
||||
VERIFY_ARE_EQUAL(hresult, expectedError);
|
||||
|
||||
return process;
|
||||
return std::move(process);
|
||||
};
|
||||
|
||||
{
|
||||
@ -481,7 +490,7 @@ class WSLATests
|
||||
{{0, WSLAFdTypeLinuxFileInput, "/proc/self/comm"}, {1, WSLAFdTypeLinuxFileInput, "/tmp/output"}, {2, WSLAFdTypeDefault, nullptr}});
|
||||
|
||||
auto result = process->WaitAndCaptureOutput();
|
||||
VERIFY_ARE_EQUAL(result.Output[2], "/bin/cat: write error: Bad file descriptor\n");
|
||||
VERIFY_ARE_EQUAL(result.Output[2], "cat: write error: Bad file descriptor\n");
|
||||
VERIFY_ARE_EQUAL(result.Code, 1);
|
||||
}
|
||||
|
||||
@ -489,7 +498,7 @@ class WSLATests
|
||||
auto process = createProcess({"/bin/cat"}, {{0, WSLAFdTypeLinuxFileOutput, "/tmp/output"}, {2, WSLAFdTypeDefault, nullptr}});
|
||||
auto result = process->WaitAndCaptureOutput();
|
||||
|
||||
VERIFY_ARE_EQUAL(result.Output[2], "/bin/cat: standard output: Bad file descriptor\n");
|
||||
VERIFY_ARE_EQUAL(result.Output[2], "cat: read error: Bad file descriptor\n");
|
||||
VERIFY_ARE_EQUAL(result.Code, 1);
|
||||
}
|
||||
}
|
||||
@ -498,13 +507,10 @@ class WSLATests
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 2048;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
auto settings = GetDefaultSessionSettings();
|
||||
settings.RootVhdOverride = testVhd.c_str(); // socat is required to run this test case.
|
||||
settings.RootVhdTypeOverride = "ext4";
|
||||
settings.NetworkingMode = WSLANetworkingModeNAT;
|
||||
settings.RootVhd = testVhd.c_str();
|
||||
|
||||
auto session = CreateSession(settings);
|
||||
|
||||
@ -544,7 +550,7 @@ class WSLATests
|
||||
|
||||
auto listen = [&](short port, const char* content, bool ipv6) {
|
||||
auto cmd = std::format("echo -n '{}' | /usr/bin/socat -dd TCP{}-LISTEN:{},reuseaddr -", content, ipv6 ? "6" : "", port);
|
||||
auto process = WSLAProcessLauncher("/bin/bash", {"/bin/bash", "-c", cmd}).Launch(*session);
|
||||
auto process = WSLAProcessLauncher("/bin/sh", {"/bin/sh", "-c", cmd}).Launch(*session);
|
||||
waitForOutput(process.GetStdHandle(2).get(), "listening on");
|
||||
|
||||
return process;
|
||||
@ -639,14 +645,7 @@ class WSLATests
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 2048;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
settings.RootVhd = testVhd.c_str();
|
||||
|
||||
auto session = CreateSession(settings);
|
||||
auto session = CreateSession();
|
||||
|
||||
// Create a 'stuck' process
|
||||
auto process = WSLAProcessLauncher{"/bin/cat", {"/bin/cat"}, {}, ProcessFlags::Stdin | ProcessFlags::Stdout}.Launch(*session);
|
||||
@ -659,14 +658,7 @@ class WSLATests
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 2048;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
settings.RootVhd = testVhd.c_str();
|
||||
|
||||
auto session = CreateSession(settings);
|
||||
auto session = CreateSession();
|
||||
|
||||
wil::com_ptr<IWSLAVirtualMachine> vm;
|
||||
VERIFY_SUCCEEDED(session->GetVirtualMachine(&vm));
|
||||
@ -675,7 +667,7 @@ class WSLATests
|
||||
auto expectMount = [&](const std::string& target, const std::optional<std::string>& options) {
|
||||
auto cmd = std::format("set -o pipefail ; findmnt '{}' | tail -n 1", target);
|
||||
|
||||
auto result = ExpectCommandResult(session.get(), {"/bin/bash", "-c", cmd}, options.has_value() ? 0 : 1);
|
||||
auto result = ExpectCommandResult(session.get(), {"/bin/sh", "-c", cmd}, options.has_value() ? 0 : 1);
|
||||
|
||||
const auto& output = result.Output[1];
|
||||
const auto& error = result.Output[2];
|
||||
@ -706,7 +698,7 @@ class WSLATests
|
||||
VERIFY_ARE_EQUAL(vm->MountWindowsFolder(testFolder.c_str(), "/win-path", false), HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS));
|
||||
|
||||
// Validate that folder is writeable from linux
|
||||
ExpectCommandResult(session.get(), {"/bin/bash", "-c", "echo -n content > /win-path/file.txt && sync"}, 0);
|
||||
ExpectCommandResult(session.get(), {"/bin/sh", "-c", "echo -n content > /win-path/file.txt && sync"}, 0);
|
||||
VERIFY_ARE_EQUAL(ReadFileContent(testFolder / "file.txt"), L"content");
|
||||
|
||||
VERIFY_SUCCEEDED(vm->UnmountWindowsFolder("/win-path"));
|
||||
@ -719,7 +711,7 @@ class WSLATests
|
||||
expectMount("/win-path", "/win-path*9p*rw,relatime,aname=*,cache=5,access=client,msize=65536,trans=fd,rfd=*,wfd=*");
|
||||
|
||||
// Validate that folder is not writeable from linux
|
||||
ExpectCommandResult(session.get(), {"/bin/bash", "-c", "echo -n content > /win-path/file.txt"}, 1);
|
||||
ExpectCommandResult(session.get(), {"/bin/sh", "-c", "echo -n content > /win-path/file.txt"}, 1);
|
||||
|
||||
VERIFY_SUCCEEDED(vm->UnmountWindowsFolder("/win-path"));
|
||||
expectMount("/win-path", {});
|
||||
@ -746,19 +738,12 @@ class WSLATests
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 2048;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
settings.RootVhd = testVhd.c_str();
|
||||
|
||||
auto session = CreateSession(settings);
|
||||
auto result = ExpectCommandResult(
|
||||
session.get(), {"/bin/bash", "-c", "echo /proc/self/fd/* && (readlink -v /proc/self/fd/* || true)"}, 0);
|
||||
auto session = CreateSession();
|
||||
auto result =
|
||||
ExpectCommandResult(session.get(), {"/bin/sh", "-c", "echo /proc/self/fd/* && (readlink -v /proc/self/fd/* || true)"}, 0);
|
||||
|
||||
// Note: fd/0 is opened by readlink to read the actual content of /proc/self/fd.
|
||||
if (!PathMatchSpecA(result.Output[1].c_str(), "/proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2\nsocket:[*]\nsocket:[*]\n"))
|
||||
if (!PathMatchSpecA(result.Output[1].c_str(), "/proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2\n"))
|
||||
{
|
||||
LogInfo("Found additional fds: %hs", result.Output[1].c_str());
|
||||
VERIFY_FAIL();
|
||||
@ -769,13 +754,8 @@ class WSLATests
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 2048;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
settings.EnableGPU = true;
|
||||
settings.RootVhd = testVhd.c_str();
|
||||
auto settings = GetDefaultSessionSettings();
|
||||
WI_SetFlag(settings.FeatureFlags, WslaFeatureFlagsGPU);
|
||||
|
||||
auto session = CreateSession(settings);
|
||||
|
||||
@ -783,10 +763,10 @@ class WSLATests
|
||||
VERIFY_SUCCEEDED(session->GetVirtualMachine(&vm));
|
||||
|
||||
// Validate that the GPU device is available.
|
||||
ExpectCommandResult(session.get(), {"/bin/bash", "-c", "test -c /dev/dxg"}, 0);
|
||||
ExpectCommandResult(session.get(), {"/bin/sh", "-c", "test -c /dev/dxg"}, 0);
|
||||
auto expectMount = [&](const std::string& target, const std::optional<std::string>& options) {
|
||||
auto cmd = std::format("set -o pipefail ; findmnt '{}' | tail -n 1", target);
|
||||
WSLAProcessLauncher launcher{"/bin/bash", {"/bin/bash", "-c", cmd}};
|
||||
WSLAProcessLauncher launcher{"/bin/sh", {"/bin/sh", "-c", cmd}};
|
||||
|
||||
auto result = launcher.Launch(*session).WaitAndCaptureOutput();
|
||||
const auto& output = result.Output[1];
|
||||
@ -815,7 +795,7 @@ class WSLATests
|
||||
|
||||
// Validate that trying to mount the shares without GPU support disabled fails.
|
||||
{
|
||||
settings.EnableGPU = false;
|
||||
WI_ClearFlag(settings.FeatureFlags, WslaFeatureFlagsGPU);
|
||||
session = CreateSession(settings);
|
||||
|
||||
wil::com_ptr<IWSLAVirtualMachine> vm;
|
||||
@ -831,49 +811,23 @@ class WSLATests
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 2048;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
settings.RootVhd = testVhd.c_str();
|
||||
|
||||
// Use the system distro vhd for modprobe & lsmod.
|
||||
|
||||
#ifdef WSL_SYSTEM_DISTRO_PATH
|
||||
|
||||
auto rootfs = std::filesystem::path(TEXT(WSL_SYSTEM_DISTRO_PATH));
|
||||
|
||||
#else
|
||||
auto rootfs = std::filesystem::path(wsl::windows::common::wslutil::GetMsiPackagePath().value()) / L"system.vhd";
|
||||
|
||||
#endif
|
||||
settings.RootVhd = rootfs.c_str();
|
||||
|
||||
auto session = CreateSession(settings);
|
||||
auto session = CreateSession();
|
||||
|
||||
// Sanity check.
|
||||
ExpectCommandResult(session.get(), {"/bin/bash", "-c", "lsmod | grep ^xsk_diag"}, 1);
|
||||
ExpectCommandResult(session.get(), {"/bin/sh", "-c", "lsmod | grep ^xsk_diag"}, 1);
|
||||
|
||||
// Validate that modules can be loaded.
|
||||
ExpectCommandResult(session.get(), {"/usr/sbin/modprobe", "xsk_diag"}, 0);
|
||||
|
||||
// Validate that xsk_diag is now loaded.
|
||||
ExpectCommandResult(session.get(), {"/bin/bash", "-c", "lsmod | grep ^xsk_diag"}, 0);
|
||||
ExpectCommandResult(session.get(), {"/bin/sh", "-c", "lsmod | grep ^xsk_diag"}, 0);
|
||||
}
|
||||
|
||||
TEST_METHOD(CreateRootNamespaceProcess)
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 2048;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
settings.RootVhd = testVhd.c_str();
|
||||
|
||||
auto session = CreateSession(settings);
|
||||
auto session = CreateSession();
|
||||
|
||||
// Simple case
|
||||
{
|
||||
@ -993,14 +947,7 @@ class WSLATests
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 2048;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
settings.RootVhd = testVhd.c_str();
|
||||
|
||||
auto session = CreateSession(settings);
|
||||
auto session = CreateSession();
|
||||
int processId = 0;
|
||||
|
||||
// Cache the existing crash dumps so we can check that a new one is created.
|
||||
@ -1035,7 +982,7 @@ class WSLATests
|
||||
|
||||
// Dumps files are named with the format: wsl-crash-<sessionId>-<pid>-<processname>-<code>.dmp
|
||||
// Check if a new file was added in crashDumpsDir matching the pattern and not in existingDumps.
|
||||
std::string expectedPattern = std::format("wsl-crash-*-{}-_usr_bin_cat-11.dmp", processId);
|
||||
std::string expectedPattern = std::format("wsl-crash-*-{}-_usr_bin_busybox-11.dmp", processId);
|
||||
|
||||
auto dumpFile = wsl::shared::retry::RetryWithTimeout<std::filesystem::path>(
|
||||
[crashDumpsDir, expectedPattern, existingDumps]() {
|
||||
@ -1070,14 +1017,7 @@ class WSLATests
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
|
||||
VIRTUAL_MACHINE_SETTINGS settings{};
|
||||
settings.CpuCount = 4;
|
||||
settings.DisplayName = L"WSLA";
|
||||
settings.MemoryMb = 2048;
|
||||
settings.BootTimeoutMs = 30 * 1000;
|
||||
settings.RootVhd = testVhd.c_str();
|
||||
|
||||
auto session = CreateSession(settings);
|
||||
auto session = CreateSession();
|
||||
|
||||
constexpr auto formatedVhd = L"test-format-vhd.vhdx";
|
||||
|
||||
@ -1098,4 +1038,94 @@ class WSLATests
|
||||
VERIFY_ARE_EQUAL(session->FormatVirtualDisk(L"DoesNotExist.vhdx"), E_INVALIDARG);
|
||||
VERIFY_ARE_EQUAL(session->FormatVirtualDisk(L"C:\\DoesNotExist.vhdx"), HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
|
||||
}
|
||||
|
||||
TEST_METHOD(CreateContainer)
|
||||
{
|
||||
WSL2_TEST_ONLY();
|
||||
|
||||
#ifdef _ARM64_
|
||||
|
||||
LogSkipped("Skipping CreateContainer test case for ARM64");
|
||||
return;
|
||||
|
||||
#else
|
||||
|
||||
auto storagePath = std::filesystem::current_path() / "test-storage";
|
||||
|
||||
auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
|
||||
std::error_code error;
|
||||
|
||||
std::filesystem::remove_all(storagePath, error);
|
||||
if (error)
|
||||
{
|
||||
LogError("Failed to cleanup storage path %ws: %s", storagePath.c_str(), error.message().c_str());
|
||||
}
|
||||
});
|
||||
|
||||
auto installedVhdPath =
|
||||
std::filesystem::path(wsl::windows::common::wslutil::GetMsiPackagePath().value()) / L"wslarootfs.vhd";
|
||||
|
||||
auto settings = GetDefaultSessionSettings();
|
||||
settings.NetworkingMode = WSLANetworkingModeNAT;
|
||||
settings.StoragePath = storagePath.c_str();
|
||||
settings.MaximumStorageSizeMb = 1024;
|
||||
|
||||
auto session = CreateSession(settings);
|
||||
|
||||
// Test a simple container start.
|
||||
{
|
||||
WSLAContainerLauncher launcher("debian:latest", "test-simple", "echo", {"OK"});
|
||||
auto container = launcher.Launch(*session);
|
||||
auto process = container.GetInitProcess();
|
||||
|
||||
ValidateProcessOutput(process, {{1, "OK\n"}});
|
||||
}
|
||||
|
||||
// Validate that env is correctly wired.
|
||||
{
|
||||
WSLAContainerLauncher launcher("debian:latest", "test-env", "/bin/sh", {"-c", "echo $testenv"}, {{"testenv=testvalue"}});
|
||||
auto container = launcher.Launch(*session);
|
||||
auto process = container.GetInitProcess();
|
||||
|
||||
ValidateProcessOutput(process, {{1, "testvalue\n"}});
|
||||
}
|
||||
|
||||
// Validate that starting containers works with the default entrypoint and content on stdin
|
||||
{
|
||||
WSLAContainerLauncher launcher(
|
||||
"debian:latest", "test-default-entrypoint", "/bin/cat", {}, {}, ProcessFlags::Stdin | ProcessFlags::Stdout | ProcessFlags::Stderr);
|
||||
|
||||
// For now, validate that trying to use stdin without a tty returns the appropriate error.
|
||||
auto container = launcher.Launch(*session);
|
||||
|
||||
// TODO: nerdctl hangs if stdin is closed without writing to it.
|
||||
// Add test coverage for that usecase once the hang is fixed.
|
||||
auto process = container.GetInitProcess();
|
||||
auto input = process.GetStdHandle(0);
|
||||
|
||||
std::string shellInput = "foo";
|
||||
std::vector<char> inputBuffer{shellInput.begin(), shellInput.end()};
|
||||
|
||||
std::unique_ptr<OverlappedIOHandle> writeStdin(new WriteHandle(std::move(input), inputBuffer));
|
||||
|
||||
std::vector<std::unique_ptr<OverlappedIOHandle>> extraHandles;
|
||||
extraHandles.emplace_back(std::move(writeStdin));
|
||||
|
||||
auto result = process.WaitAndCaptureOutput(INFINITE, std::move(extraHandles));
|
||||
|
||||
VERIFY_ARE_EQUAL(result.Output[2], "");
|
||||
VERIFY_ARE_EQUAL(result.Output[1], "foo");
|
||||
}
|
||||
|
||||
// Validate that stdin is empty if ProcessFlags::Stdin is not passed.
|
||||
{
|
||||
WSLAContainerLauncher launcher("debian:latest", "test-stdin", "/bin/cat");
|
||||
auto container = launcher.Launch(*session);
|
||||
auto process = container.GetInitProcess();
|
||||
|
||||
ValidateProcessOutput(process, {{1, ""}});
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user