Merge branch 'user/ptrivedi/cont-network-mode' of https://github.com/microsoft/WSL into user/ptrivedi/cont-network-mode

This commit is contained in:
Pooja Trivedi 2025-12-15 12:11:09 -05:00
commit c19e851f52
38 changed files with 2124 additions and 1353 deletions

View File

@ -29,7 +29,7 @@ parameters:
type: object
default:
- target: "wsl;libwsl;wslg;wslservice;wslhost;wslrelay;wslinstaller;wslinstall;initramfs;wslserviceproxystub;wslsettings;wslinstallerproxystub;testplugin;wslaclient;wsltests"
pattern: "wsl.exe,libwsl.dll,wslg.exe,wslservice.exe,wslhost.exe,wslrelay.exe,wslinstaller.exe,wslinstall.dll,wslserviceproxystub.dll,wslsettings.dll,wslsettings.exe,wslinstallerproxystub.dll,wsldevicehost.dll,WSLDVCPlugin.dll,testplugin.dll,wsldeps.dll,wslaclient.dll"
pattern: "wsl.exe,libwsl.dll,wslg.exe,wslservice.exe,wslhost.exe,wslrelay.exe,wslinstaller.exe,wslinstall.dll,wslserviceproxystub.dll,wslsettings/wslsettings.dll,wslsettings/wslsettings.exe,wslinstallerproxystub.dll,wsldevicehost.dll,WSLDVCPlugin.dll,testplugin.dll,wsldeps.dll,wslaclient.dll"
- target: "msixgluepackage"
pattern: "gluepackage.msix"
- target: "msipackage"
@ -125,6 +125,11 @@ stages:
ob_sdl_codeSignValidation_excludes: -|**testbin\**
Codeql.PublishDatabaseLog: true
Codeql.SourceRoot: src
packageStagingDir: '$(Build.SourcesDirectory)\packageStagingDir'
${{ if eq(parameters.isRelease, 'true') }}:
packageInputDirArg: '-DPACKAGE_INPUT_DIR=$(packageStagingDir)'
${{ else }}:
packageInputDirArg: ''
steps:
@ -171,7 +176,7 @@ stages:
displayName: "CMake ${{ platform }}"
inputs:
workingDirectory: "."
cmakeArgs: . --fresh -A ${{ platform }} -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_VERSION=10.0.26100.0 -DPACKAGE_VERSION=$(version.WSL_PACKAGE_VERSION) -DWSL_NUGET_PACKAGE_VERSION=$(version.WSL_NUGET_PACKAGE_VERSION) -DSKIP_PACKAGE_SIGNING=${{ parameters.isRelease }} -DOFFICIAL_BUILD=${{ parameters.isRelease }} -DPIPELINE_BUILD_ID=$(Build.BuildId) -DVSO_ORG=${{ parameters.vsoOrg }} -DVSO_PROJECT=${{ parameters.vsoProject }} -DWSL_BUILD_WSL_SETTINGS=true
cmakeArgs: . --fresh -A ${{ platform }} -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_VERSION=10.0.26100.0 -DPACKAGE_VERSION=$(version.WSL_PACKAGE_VERSION) -DWSL_NUGET_PACKAGE_VERSION=$(version.WSL_NUGET_PACKAGE_VERSION) -DSKIP_PACKAGE_SIGNING=${{ parameters.isRelease }} -DOFFICIAL_BUILD=${{ parameters.isRelease }} -DPIPELINE_BUILD_ID=$(Build.BuildId) -DVSO_ORG=${{ parameters.vsoOrg }} -DVSO_PROJECT=${{ parameters.vsoProject }} -DWSL_BUILD_WSL_SETTINGS=true $(packageInputDirArg)\${{ platform }}
# This additional Restore NuGet package task is added as a workaround for WSL Settings to have its packages restored properly.
# Without this, building wsl settings may encounter the following error:
@ -201,7 +206,7 @@ stages:
AuthSignCertName: ${{ parameters.esrp.AuthSignCertName }}
AppRegistrationClientId: ${{ parameters.esrp.AppRegistrationClientId }}
AppRegistrationTenantId: ${{ parameters.esrp.AppRegistrationTenantId }}
FolderPath: "bin\\${{ platform }}"
FolderPath: "bin\\${{ platform }}\\Release"
Pattern: "${{ target.pattern }}"
UseMSIAuthentication: true
EsrpClientId: ${{ parameters.esrp.EsrpClientId }}
@ -229,20 +234,27 @@ stages:
}
]
# Replace the intermediate wslsettings binaries file with the signed versions so that any future steps building wslsettings will use the signed versions
- task: PowerShell@2
displayName: 'Replace wslsettings binaries in intermediate folder with signed versions'
condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))
inputs:
targetType: inline
script: |
$arch = '${{ platform }}'
$wslsettingsbinpath = "bin\$arch\release\wslsettings"
$wslsettingsobjpath = "src\windows\wslsettings\obj\$arch\release"
# Update the timestamp of wslsettings.exe so that it doesn't get rebuilt
(Get-Item $wslsettingsbinpath\wslsettings.exe).LastWriteTime = Get-Date
Copy-Item $wslsettingsbinpath\wslsettings.dll $wslsettingsobjpath\wslsettings.dll -Force
Copy-Item $wslsettingsbinpath\wslsettings.exe $wslsettingsobjpath\apphost.exe -Force
- task: PowerShell@2
displayName: "Copy signed ${{ target.target }} to staging (${{ platform }})"
condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))
inputs:
targetType: inline
script: |
$arch = '${{ platform }}'
$pattern = '${{ target.pattern }}'
$inputDir = "bin\$arch\Release"
$outputDir = "$(packageStagingDir)\$arch"
New-Item -ItemType Directory -Path "$outputDir\wslsettings" -Force
foreach ($file in $pattern.Split(',')) {
$sourcePath = Join-Path $inputDir $file
if (Test-Path $sourcePath) {
$destPath = Join-Path $outputDir $file
Write-Host "Copying signed file: $sourcePath -> $destPath"
Copy-Item -Path $sourcePath -Destination $destPath -Force
} else {
Write-Warning "File not found: $sourcePath"
}
}
- script: cmake --build . --config Release -- -m
displayName: "Build installer msix and tests (${{ platform }})"
@ -388,7 +400,7 @@ stages:
Move-Item -Path "bin\x64\cloudtest" -Destination "$(ob_outputDirectory)\testbin\x64\cloudtest"
Move-Item -Path "tools\test\test-setup.ps1" -Destination "$(ob_outputDirectory)\testbin\test-setup.ps1"
Move-Item -Path "tools\test\CloudTest-Setup.bat" -Destination "$(ob_outputDirectory)\testbin\CloudTest-Setup.bat"
Move-Item -Path "tools\wsl.wprp" -Destination "$(ob_outputDirectory)\testbin\wsl.wprp"
Move-Item -Path "diagnostics\wsl.wprp" -Destination "$(ob_outputDirectory)\testbin\wsl.wprp"
Move-Item -Path "test\linux\unit_tests" -Destination "$(ob_outputDirectory)\testbin\unit_tests"
Move-Item -Path bundle\release\* -Destination $(ob_outputDirectory)\bundle
@ -417,3 +429,4 @@ stages:
- task: CodeQL3000Finalize@0
condition: ${{ and(parameters.isNightly, eq(variables['Build.SourceBranch'], 'refs/heads/main'))}}

View File

@ -65,7 +65,7 @@ find_nuget_package(Microsoft.WSL.Kernel KERNEL /build/native)
find_nuget_package(Microsoft.WSL.bsdtar BSDTARD /build/native/bin)
find_nuget_package(Microsoft.WSL.LinuxSdk LINUXSDK /)
find_nuget_package(Microsoft.WSL.TestDistro TEST_DISTRO /)
find_nuget_package(Microsoft.WSL.WSLATestDistro WSLA_TEST_DISTRO /)
find_nuget_package(Microsoft.WSL.WslaRootfs WSLA_TEST_DISTRO /build/native/bin/${TARGET_PLATFORM})
find_nuget_package(Microsoft.WSLg WSLG /build/native/bin)
find_nuget_package(StrawberryPerl PERL /)
find_nuget_package(vswhere VSWHERE /tools)
@ -376,7 +376,7 @@ if (DEFINED WSL_DEV_BINARY_PATH) # Development shortcut to make the package smal
WSL_GPU_LIB_PATH="${WSL_DEV_BINARY_PATH}/lib")
if (NOT OFFICIAL_BUILD AND ${TARGET_PLATFORM} STREQUAL "x64")
add_compile_definitions(WSLA_TEST_DISTRO_PATH="${WSLA_TEST_DISTRO_SOURCE_DIR}/wslatestrootfs.vhd")
add_compile_definitions(WSLA_TEST_DISTRO_PATH="${WSLA_TEST_DISTRO_SOURCE_DIR}/rootfs.vhd")
endif()
endif()

View File

@ -643,6 +643,159 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
Microsoft.NETCore.App.Runtime.win-arm64 10.0.0 - MIT
Copyright (c) 2021
Copyright (c) Six Labors
(c) Microsoft Corporation
Copyright (c) 2022 FormatJS
Copyright (c) Andrew Arnott
Copyright 2019 LLVM Project
Copyright (c) 1998 Microsoft
Copyright 2018 Daniel Lemire
Copyright (c) .NET Foundation
Copyright 1995-2022 Mark Adler
Copyright 1995-2024 Mark Adler
Copyright (c) 2011, Google Inc.
Copyright (c) 2020 Dan Shechter
(c) 1997-2005 Sean Eron Anderson
Copyright (c) 2015 Andrew Gallant
Copyright (c) 2022, Wojciech Mula
Copyright (c) 2017 Yoshifumi Kawai
Copyright (c) 2022, Geoff Langdale
Copyright (c) 2005-2020 Rich Felker
Copyright (c) 2012-2021 Yann Collet
Copyright (c) Microsoft Corporation
Copyright (c) 2007 James Newton-King
Copyright (c) 1991-2024 Unicode, Inc.
Copyright (c) 2013-2017, Alfred Klomp
Copyright (c) 2018 Nemanja Mijailovic
Copyright 2012 the V8 project authors
Copyright (c) 1999 Lucent Technologies
Copyright (c) 2008-2016, Wojciech Mula
Copyright (c) 2011-2020 Microsoft Corp
Copyright (c) 2015-2017, Wojciech Mula
Copyright (c) 2015-2018, Wojciech Mula
Copyright (c) 2005-2007, Nick Galbreath
Copyright (c) 2015 The Chromium Authors
Copyright (c) 2018 Alexander Chermyanin
Copyright (c) The Internet Society 1997
Copyright (c) 2004-2006 Intel Corporation
Copyright (c) 2011-2015 Intel Corporation
Copyright (c) 2013-2017, Milosz Krajewski
Copyright (c) 2016-2017, Matthieu Darbois
Copyright (c) The Internet Society (2003)
Copyright (c) .NET Foundation Contributors
(c) 1995-2024 Jean-loup Gailly and Mark Adler
Copyright (c) 2020 Mara Bos <m-ou.se@m-ou.se>
Copyright (c) .NET Foundation and Contributors
Copyright (c) 2012 - present, Victor Zverovich
Copyright (c) 2006 Jb Evain (jbevain@gmail.com)
Copyright (c) 2008-2020 Advanced Micro Devices, Inc.
Copyright (c) 2019 Microsoft Corporation, Daan Leijen
Copyright (c) 2011 Novell, Inc (http://www.novell.com)
Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com)
Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors
Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com
Copyright 1995-2024 Jean-loup Gailly and Mark Adler Qkkbal
Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
Portions (c) International Organization for Standardization 1986
Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers
Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip
Copyright (c) 1980, 1986, 1993 The Regents of the University of California
Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California
Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass
The MIT License (MIT)
Copyright (c) .NET Foundation and Contributors
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---------------------------------------------------------
---------------------------------------------------------
Microsoft.NETCore.App.Runtime.win-x64 10.0.0 - MIT
Copyright (c) 2021
Copyright (c) Six Labors
(c) Microsoft Corporation
Copyright (c) 2022 FormatJS
Copyright (c) Andrew Arnott
Copyright 2019 LLVM Project
Copyright (c) 1998 Microsoft
Copyright 2018 Daniel Lemire
Copyright (c) .NET Foundation
Copyright 1995-2022 Mark Adler
Copyright 1995-2024 Mark Adler
Copyright (c) 2011, Google Inc.
Copyright (c) 2020 Dan Shechter
(c) 1997-2005 Sean Eron Anderson
Copyright (c) 2015 Andrew Gallant
Copyright (c) 2022, Wojciech Mula
Copyright (c) 2017 Yoshifumi Kawai
Copyright (c) 2022, Geoff Langdale
Copyright (c) 2005-2020 Rich Felker
Copyright (c) 2012-2021 Yann Collet
Copyright (c) Microsoft Corporation
Copyright (c) 2007 James Newton-King
Copyright (c) 1991-2024 Unicode, Inc.
Copyright (c) 2013-2017, Alfred Klomp
Copyright (c) 2018 Nemanja Mijailovic
Copyright 2012 the V8 project authors
Copyright (c) 1999 Lucent Technologies
Copyright (c) 2008-2016, Wojciech Mula
Copyright (c) 2011-2020 Microsoft Corp
Copyright (c) 2015-2017, Wojciech Mula
Copyright (c) 2015-2018, Wojciech Mula
Copyright (c) 2005-2007, Nick Galbreath
Copyright (c) 2015 The Chromium Authors
Copyright (c) 2018 Alexander Chermyanin
Copyright (c) The Internet Society 1997
Copyright (c) 2004-2006 Intel Corporation
Copyright (c) 2011-2015 Intel Corporation
Copyright (c) 2013-2017, Milosz Krajewski
Copyright (c) 2016-2017, Matthieu Darbois
Copyright (c) The Internet Society (2003)
Copyright (c) .NET Foundation Contributors
(c) 1995-2024 Jean-loup Gailly and Mark Adler
Copyright (c) 2020 Mara Bos <m-ou.se@m-ou.se>
Copyright (c) .NET Foundation and Contributors
Copyright (c) 2012 - present, Victor Zverovich
Copyright (c) 2006 Jb Evain (jbevain@gmail.com)
Copyright (c) 2008-2020 Advanced Micro Devices, Inc.
Copyright (c) 2019 Microsoft Corporation, Daan Leijen
Copyright (c) 2011 Novell, Inc (http://www.novell.com)
Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com)
Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors
Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com
Copyright 1995-2024 Jean-loup Gailly and Mark Adler Qkkbal
Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
Portions (c) International Organization for Standardization 1986
Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers
Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip
Copyright (c) 1980, 1986, 1993 The Regents of the University of California
Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California
Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass
The MIT License (MIT)

View File

@ -13,6 +13,7 @@
<EventProvider Id="wslclient" Name="8cbb7724-7223-5d6f-8137-564dac45104d"/>
<EventProvider Id="wslaservice" Name="0383CE62-8F86-4766-AFB2-9D66A7FB1E90"/>
<EventProvider Id="wslapi" Name="beb94edf-1a7b-5058-0696-ff9e6b1798d1"/>
<EventProvider Id="vfpext" Name="9F2660EA-CFE7-428F-9850-AECA612619B0" />
<EventProvider Id="vm_chipset" Name="de9ba731-7f33-4f44-98c9-6cac856b9f83"/>
<EventProvider Id="vmcompute_dll" Name="AF7FD3A7-B248-460C-A9F5-FEC39EF8468C"/>
<EventProvider Id="vmcompute" Name="17103E3F-3C6E-4677-BB17-3B267EB5BE57"/>
@ -20,7 +21,7 @@
<EventProvider Id="vmwp" Name="51DDFA29-D5C8-4803-BE4B-2ECB715570FE"/>
<EventProvider Id="9p" Name="e13c8d52-b153-571f-78c5-1d4098af2a1e"/>
<EventProvider Id="9p_errors" Name="06C601B3-6957-4F8C-A15F-74875B24429D" />
<EventProvider Id="p9rdr" Name="bb1d36f0-e0e0-48cc-9493-fef0e3d0b28c" />
<EventProvider Id="p9rdr" Name="bb1d36f0-e0e0-48cc-9493-fef0e3d0b28c" NonPagedMemory="true" Strict="true"/>
<EventProvider Id="mup" Name="20c46239-d059-4214-a11e-7d6769cbe020" />
<EventProvider Id="rfsmon" Name="51734B23-5B7E-4892-BA8E-45BC110B735C" />
<EventProvider Id="hyperv_storage" Name="c7ad62c6-5c99-5a1b-bbc4-0821ae5b765e" />
@ -116,6 +117,7 @@
<EventProviderId Value="wsl_devicehost"/>
<EventProviderId Value="wslclient"/>
<EventProviderId Value="wslapi"/>
<EventProviderId Value="vfpext"/>
<EventProviderId Value="vm_chipset"/>
<EventProviderId Value="vmcompute_dll"/>
<EventProviderId Value="vmcompute"/>

View File

@ -1194,7 +1194,7 @@ Zie de herstelinstructies voor: https://aka.ms/wsldiskmountrecovery</value>
<value>Een absoluut Windows-pad naar een aangepaste Linux-kernel.</value>
</data>
<data name="Settings_CustomKernelPathBrowseButton.Content" xml:space="preserve">
<value>Koor kernels bladeren</value>
<value>Door kernels bladeren</value>
</data>
<data name="Settings_CustomKernelPathTextBox.AutomationProperties.Name" xml:space="preserve">
<value>Aangepaste kernel</value>

View File

@ -1,16 +1,29 @@
set(BIN ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE})
# For pipeline builds, use PACKAGE_INPUT_DIR if specified (contains signed binaries)
# For local builds, use bin directory
if(DEFINED PACKAGE_INPUT_DIR)
message(STATUS "Using signed binaries from ${PACKAGE_INPUT_DIR} for MSI packaging")
else()
set(PACKAGE_INPUT_DIR ${BIN})
endif()
set(OUTPUT_PACKAGE ${BIN}/wsl.msi)
set(PACKAGE_WIX_IN ${CMAKE_CURRENT_LIST_DIR}/package.wix.in)
set(PACKAGE_WIX ${BIN}/package.wix)
set(CAB_CACHE ${BIN}/cab)
set(BINARIES wsl.exe;wslg.exe;wslhost.exe;wslrelay.exe;wslservice.exe;wslserviceproxystub.dll;init;initrd.img;wslinstall.dll;wslaserviceproxystub.dll;wslaservice.exe;wsladiag.exe)
set(WINDOWS_BINARIES wsl.exe;wslg.exe;wslhost.exe;wslrelay.exe;wslservice.exe;wslserviceproxystub.dll;wslinstall.dll;wslaservice.exe;wsladiag.exe)
if (WSL_BUILD_WSL_SETTINGS)
list(APPEND BINARIES_DEPENDENCIES "wslsettings/wslsettings.dll;wslsettings/wslsettings.exe;libwsl.dll")
list(APPEND WINDOWS_BINARIES "wslsettings/wslsettings.dll;wslsettings/wslsettings.exe;libwsl.dll")
endif()
set(BINARIES_DEPENDENCIES)
foreach(binary ${BINARIES})
foreach(binary ${WINDOWS_BINARIES})
list(APPEND BINARIES_DEPENDENCIES "${PACKAGE_INPUT_DIR}/${binary}")
endforeach()
set(LINUX_BINARIES init;initrd.img)
foreach(binary ${LINUX_BINARIES})
list(APPEND BINARIES_DEPENDENCIES "${BIN}/${binary}")
endforeach()

View File

@ -1,4 +1,4 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Package Name="Windows Subsystem for Linux" Language="1033" InstallerVersion="500" Version="${PACKAGE_VERSION}" Manufacturer="Microsoft Corporation" UpgradeCode="6D5B792B-1EDC-4DE9-8EAD-201B820F8E82" Scope="perMachine" Compressed="${COMPRESS_PACKAGE}">
<MajorUpgrade AllowDowngrades="yes" Disallow="no" />
<MediaTemplate EmbedCab="yes" />
@ -19,22 +19,22 @@
<DirectoryRef Id="INSTALLDIR">
<Component Id="wsl" Guid="F0C8D6BA-1502-41E7-BF72-D93DFA134730" UninstallWhenSuperseded="yes" DisableRegistryReflection="yes" Bitness="always64">
<RemoveFile Id="CleanUpWSLShortCut" Directory="ProgramMenuFolder" Name="WSL" On="uninstall"/>
<File Id="wsl.exe" Name="wsl.exe" Source="${BIN}/wsl.exe" KeyPath="yes">
<File Id="wsl.exe" Name="wsl.exe" Source="${PACKAGE_INPUT_DIR}/wsl.exe" KeyPath="yes">
<Shortcut Id="WSLShortcut" Name="WSL" Description="Windows Subsystem for Linux" Arguments="--cd ~" Advertise="yes" Directory="ProgramMenuFolder" Icon="wsl.ico">
<ShortcutProperty Key="System.AppUserModel.ID" Value="Microsoft.WSL"/>
<ShortcutProperty Key="System.AppUserModel.ToastActivatorCLSID" Value="{2B9C59C3-98F1-45C8-B87B-12AE3C7927E8}"/>
</Shortcut>
</File>
<File Id="wslg.exe" Name="wslg.exe" Source="${BIN}/wslg.exe" />
<File Id="wsladiag.exe" Name="wsladiag.exe" Source="${BIN}/wsladiag.exe" />
<File Id="wslhost.exe" Name="wslhost.exe" Source="${BIN}/wslhost.exe" />
<File Id="wslrelay.exe" Name="wslrelay.exe" Source="${BIN}/wslrelay.exe" />
<File Id="wslserviceproxystub.dll" Name="wslserviceproxystub.dll" Source="${BIN}/wslserviceproxystub.dll" />
<File Id="wsldeps.dll" Name="wsldeps.dll" Source="${BIN}/wsldeps.dll" />
<File Id="wslg.exe" Name="wslg.exe" Source="${PACKAGE_INPUT_DIR}/wslg.exe" />
<File Id="wsladiag.exe" Name="wsladiag.exe" Source="${PACKAGE_INPUT_DIR}/wsladiag.exe" />
<File Id="wslhost.exe" Name="wslhost.exe" Source="${PACKAGE_INPUT_DIR}/wslhost.exe" />
<File Id="wslrelay.exe" Name="wslrelay.exe" Source="${PACKAGE_INPUT_DIR}/wslrelay.exe" />
<File Id="wslserviceproxystub.dll" Name="wslserviceproxystub.dll" Source="${PACKAGE_INPUT_DIR}/wslserviceproxystub.dll" />
<File Id="wsldeps.dll" Name="wsldeps.dll" Source="${PACKAGE_INPUT_DIR}/wsldeps.dll" />
<?if "${WSL_BUILD_WSL_SETTINGS}" = "true" ?>
<File Id="libwsl.dll" Name="libwsl.dll" Source="${BIN}/libwsl.dll" />
<File Id="libwsl.dll" Name="libwsl.dll" Source="${PACKAGE_INPUT_DIR}/libwsl.dll" />
<?endif?>
<?if "${WSL_DEV_BINARY_PATH}" = "" ?>
@ -44,7 +44,7 @@
<!-- 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"/>
<File Id="wslarootfs.vhd" Name="wslarootfs.vhd" Source="${WSLA_TEST_DISTRO_SOURCE_DIR}/rootfs.vhd"/>
<?endif?>
<!-- Installation folder -->
@ -231,7 +231,7 @@
</RegistryKey>
<!-- Session 0 service -->
<File Id="wslservice.exe" Source="${BIN}/wslservice.exe" KeyPath="yes" />
<File Id="wslservice.exe" Source="${PACKAGE_INPUT_DIR}/wslservice.exe" KeyPath="yes" />
<ServiceInstall Name="WSLService" DisplayName="WSL Service" Description="WSL Service" Start="auto" Type="ownProcess" ErrorControl="normal" Account="LocalSystem" Vital="yes" Interactive="no" />
<!-- The service is stopped on uninstall and upgrade.
@ -345,6 +345,7 @@
</RegistryKey>
<!-- wsldevicehost.dll -->
<!-- TODO: WSL currently has two AppID registrations for wsldevicehost, is that needed? -->
<RegistryKey Root="HKCR" Key="AppID\{C457EA11-5486-4174-B90D-089909EDB170}">
<RegistryValue Name="DllSurrogate" Value="" Type="string" />
<RegistryValue Name="AppIDFlags" Value="2048" Type="integer" />
@ -354,9 +355,42 @@
<RegistryValue Name="LaunchPermission" Value="01000480580000006800000000000000140000000200440003000000000014000B00000001010000000000050B000000000014000B00000001010000000000050A000000000014000B0000000101000000000005120000000102000000000005200000002002000001020000000000052000000020020000" Type="binary" />
</RegistryKey>
<!-- WslDeviceHost_VirtioNet -->
<!-- WslDeviceHost WSLA_VIRTIO_FS_ADMIN_CLASS_ID -->
<RegistryKey Root="HKCR" Key="CLSID\{8F7C2A3B-D9E4-4C1F-A2B8-5E3D7C9F1A6E}">
<RegistryValue Value="WslDeviceHost_VirtioFsAdmin" 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>
<!-- WslDeviceHost WSLA_VIRTIO_FS_CLASS_ID -->
<RegistryKey Root="HKCR" Key="CLSID\{06ED032F-C528-41C1-B75D-905EEE823BBA}">
<RegistryValue Value="WslDeviceHost_VirtioFs" 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>
<!-- WslDeviceHost WSLA_VIRTIO_NET_CLASS_ID -->
<RegistryKey Root="HKCR" Key="CLSID\{7B3C9A42-8E1F-4D5A-9F2E-C4A7B8D3E6F1}">
<RegistryValue Value="WslDeviceHost_Net" Type="string" />
<RegistryValue Value="WslDeviceHost_VirtioNet" 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>
<!-- WslDeviceHost WSLA_VIRTIO_PMEM_CLASS_ID -->
<RegistryKey Root="HKCR" Key="CLSID\{ABB755FC-1B86-4255-83E2-E5787ABCF6C2}">
<RegistryValue Value="WslDeviceHost_VirtioPmem" Type="string" />
<RegistryValue Name="AppId" Value="{C457EA11-5486-4174-B90D-089909EDB170}" Type="string" />
<RegistryKey Key="InProcServer32">
@ -380,8 +414,8 @@
<File Id="rdpnanoTransport.dll" Source="${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}/rdpnanoTransport.dll" />
<File Id="RdpWinStlHelper.dll" Source="${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}/RdpWinStlHelper.dll" />
<?endif?>
<File Id="wsldevicehost.dll" Source="${BIN}/wsldevicehost.dll" />
<File Id="${WSLG_TS_PLUGIN_DLL}" Source="${BIN}/${WSLG_TS_PLUGIN_DLL}" />
<File Id="wsldevicehost.dll" Source="${PACKAGE_INPUT_DIR}/wsldevicehost.dll" />
<File Id="${WSLG_TS_PLUGIN_DLL}" Source="${PACKAGE_INPUT_DIR}/${WSLG_TS_PLUGIN_DLL}" />
<!-- MSRDC Plugin registration -->
<RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Terminal Server Client\Default\OptionalAddIns\WSLDVC_PACKAGE">
@ -431,7 +465,7 @@
<ComponentGroup Id="wslsettings" Directory="WSLSETTINGS" Source="${BIN}/wslsettings">
<Component Id="wslsettingsnonserver" Guid="AB166073-8855-492B-95C8-C6E5939B66A5" Bitness="always64" DisableRegistryReflection="yes" UninstallWhenSuperseded="yes" Condition="MsiNTProductType = 1">
<RemoveFile Id="CleanUpWSLSettingsShortCutNonServer" Directory="ProgramMenuFolder" Name="WSLSettings" On="uninstall"/>
<File Id="wslsettings.exe_nonserver" Source="${BIN}/wslsettings/wslsettings.exe" KeyPath="yes" ShortName="kyk8fs6a.exe">
<File Id="wslsettings.exe_nonserver" Source="${PACKAGE_INPUT_DIR}/wslsettings/wslsettings.exe" KeyPath="yes" ShortName="kyk8fs6a.exe">
<Shortcut Id="WSLSettingsShortcutNonServer" Name="WSL Settings" Description="Windows Subsystem for Linux Settings" Advertise="yes" Directory="ProgramMenuFolder" Icon="wsl.ico">
<ShortcutProperty Key="System.AppUserModel.IsSystemComponent" Value="true"/>
</Shortcut>
@ -462,7 +496,7 @@
</Component>
<Component Id="wslsettingsserver" Guid="EE2D69A0-4F55-4EC5-9576-4FAD70BC798E" Bitness="always64" DisableRegistryReflection="yes" UninstallWhenSuperseded="yes" Condition="MsiNTProductType &gt; 1">
<RemoveFile Id="CleanUpWSLSettingsShortCutServer" Directory="ProgramMenuFolder" Name="WSLSettings" On="uninstall"/>
<File Id="wslsettings.exe_server" Source="${BIN}/wslsettings/wslsettings.exe" KeyPath="yes" ShortName="kyk8fs6b.exe">
<File Id="wslsettings.exe_server" Source="${PACKAGE_INPUT_DIR}/wslsettings/wslsettings.exe" KeyPath="yes" ShortName="kyk8fs6b.exe">
<Shortcut Id="WSLSettingsShortcutServer" Name="WSL Settings" Description="Windows Subsystem for Linux Settings" Advertise="yes" Directory="ProgramMenuFolder" Icon="wsl.ico"/>
</File>
<!-- Protocol registration -->
@ -489,7 +523,10 @@
</RegistryKey>
</RegistryKey>
</Component>
<Files Include="*.dll"/>
<File Id="wslsettings.dll" Source="${PACKAGE_INPUT_DIR}/wslsettings/wslsettings.dll"/>
<Files Include="*.dll">
<Exclude Files="wslsettings.dll" />
</Files>
<Files Include="*.exe">
<Exclude Files="wslsettings.exe" />
</Files>
@ -525,8 +562,8 @@
</Feature>
<Binary Id="wslinstall.dll" SourceFile="${BIN}/wslinstall.dll" />
<Binary Id="msixpackage" SourceFile="${BIN}/gluepackage.msix"/>
<Binary Id="wslinstall.dll" SourceFile="${PACKAGE_INPUT_DIR}/wslinstall.dll" />
<Binary Id="msixpackage" SourceFile="${PACKAGE_INPUT_DIR}/gluepackage.msix"/>
<CustomAction Id="ValidateInstall"
Impersonate="no"
@ -699,3 +736,4 @@
<Property Id="MSIRMSHUTDOWN" Value="0" Secure="yes" />
</Package>
</Wix>

View File

@ -26,4 +26,3 @@
<clear />
</disabledPackageSources>
</configuration>

View File

@ -18,11 +18,11 @@
<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-20251202.1" />
<package id="Microsoft.WSL.DeviceHost" version="1.1.6-0" />
<package id="Microsoft.WSL.Kernel" version="6.6.114.1-1" targetFramework="native" />
<package id="Microsoft.WSL.LinuxSdk" version="1.20.0" targetFramework="native" />
<package id="Microsoft.WSL.TestDistro" version="2.5.7-47" />
<package id="Microsoft.WSL.WSLATestDistro" version="0.1.0" />
<package id="Microsoft.WSL.WslaRootfs" 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.32.1.1" />

View File

@ -12,6 +12,8 @@
#include <netinet/ip.h>
#include <sys/syscall.h>
#include <linux/unistd.h>
#include <linux/sock_diag.h>
#include <linux/inet_diag.h>
#include <lxwil.h>
#include <linux/if_tun.h>
@ -21,85 +23,67 @@
#include "SecCompDispatcher.h"
#include "seccomp_defs.h"
#include "CommandLine.h"
#include "NetlinkChannel.h"
#include "NetlinkTransactionError.h"
#define TCP_LISTEN 10
namespace {
std::vector<sockaddr_storage> ParseTcpFile(int family, FILE* file)
std::vector<sockaddr_storage> QueryListeningSockets(NetlinkChannel& channel)
{
char* line = nullptr;
auto freeLine = wil::scope_exit([&line]() { free(line); });
// Skip the first line which contains a header.
size_t lineLength = 0;
auto bytesRead = getline(&line, &lineLength, file);
THROW_LAST_ERROR_IF(bytesRead < 0);
// Each line contains information about TCP sockets on the system, the fields
// we are interested are for sockets that are or have been listening:
// 1: Socket address and port number
// 3: Socket status
std::vector<sockaddr_storage> sockets{};
while ((bytesRead = getline(&line, &lineLength, file)) != -1)
try
{
sockaddr_storage sock{};
int index = 0;
int status = 0;
for (char *sp, *field = strtok_r(line, " \n", &sp); field != nullptr; field = strtok_r(NULL, " \n", &sp))
inet_diag_req_v2 message{};
message.sdiag_protocol = IPPROTO_TCP;
message.idiag_states = (1 << TCP_LISTEN);
auto onMessage = [&](const NetlinkResponse& response) {
for (const auto& e : response.Messages<inet_diag_msg>(SOCK_DIAG_BY_FAMILY))
{
const auto* payload = e.Payload();
sockaddr_storage sock{};
if (payload->idiag_family == AF_INET)
{
auto* ipv4 = reinterpret_cast<sockaddr_in*>(&sock);
ipv4->sin_family = AF_INET;
ipv4->sin_addr.s_addr = payload->id.idiag_src[0];
ipv4->sin_port = payload->id.idiag_sport;
}
else if (payload->idiag_family == AF_INET6)
{
auto* ipv6 = reinterpret_cast<sockaddr_in6*>(&sock);
ipv6->sin6_family = AF_INET6;
static_assert(sizeof(ipv6->sin6_addr.s6_addr32) == sizeof(payload->id.idiag_src));
memcpy(ipv6->sin6_addr.s6_addr32, payload->id.idiag_src, sizeof(ipv6->sin6_addr.s6_addr32));
ipv6->sin6_port = payload->id.idiag_sport;
}
sockets.emplace_back(sock);
}
};
// Query IPv4 listening sockets.
{
if (index == 1)
{
int port;
const char* portString = strchr(field, ':');
if (portString == nullptr)
{
break;
}
portString += 1;
port = static_cast<int>(strtol(portString, nullptr, 16));
if (port == 0)
{
break;
}
if (family == AF_INET)
{
sockaddr_in ipv4Sock{};
ipv4Sock.sin_family = family;
ipv4Sock.sin_addr.s_addr = strtol(field, nullptr, 16);
ipv4Sock.sin_port = port;
memcpy(&sock, &ipv4Sock, sizeof(ipv4Sock));
}
else if (family == AF_INET6)
{
sockaddr_in6 ipv6Sock{};
ipv6Sock.sin6_family = family;
ipv6Sock.sin6_port = port;
for (int part = 0; part < 4; ++part)
{
char next[5];
next[4] = 0;
memcpy(next, field + part * 4, 4);
ipv6Sock.sin6_addr.__in6_union.__s6_addr32[part] = strtol(next, nullptr, 16);
}
memcpy(&sock, &ipv6Sock, sizeof(ipv6Sock));
}
}
else if (index == 3)
{
status = static_cast<int>(strtol(field, nullptr, 16));
break;
}
index += 1;
message.sdiag_family = AF_INET;
auto transaction = channel.CreateTransaction(message, SOCK_DIAG_BY_FAMILY, NLM_F_DUMP);
transaction.Execute(onMessage);
}
if ((status == TCP_LISTEN) && (sock.ss_family != 0))
// Query IPv6 listening sockets.
{
sockets.emplace_back(sock);
message.sdiag_family = AF_INET6;
auto transaction = channel.CreateTransaction(message, SOCK_DIAG_BY_FAMILY, NLM_F_DUMP);
transaction.Execute(onMessage);
}
}
catch (const NetlinkTransactionError& e)
{
// Log but don't fail - network state might be temporarily unavailable
LOG_ERROR("Failed to query listening sockets via sock_diag: {}", e.what());
}
return sockets;
}
@ -127,12 +111,12 @@ LX_GNS_PORT_LISTENER_RELAY SockToRelayMessage(const sockaddr_storage& sock)
{
auto ipv4 = reinterpret_cast<const sockaddr_in*>(&sock);
message.Address[0] = ipv4->sin_addr.s_addr;
message.Port = ipv4->sin_port;
message.Port = ntohs(ipv4->sin_port);
}
else if (sock.ss_family == AF_INET6)
{
auto ipv6 = reinterpret_cast<const sockaddr_in6*>(&sock);
message.Port = ipv6->sin6_port;
message.Port = ntohs(ipv6->sin6_port);
memcpy(message.Address, ipv6->sin6_addr.__in6_union.__s6_addr, sizeof(message.Address));
}
return message;
@ -177,53 +161,23 @@ bool IsSameSockAddr(const sockaddr_storage& left, const sockaddr_storage& right)
{
auto leftIpv6 = reinterpret_cast<const sockaddr_in6*>(&left);
auto rightIpv6 = reinterpret_cast<const sockaddr_in6*>(&right);
if (leftIpv6->sin6_port != rightIpv6->sin6_port)
{
return false;
}
for (int part = 0; part < 4; ++part)
{
if (leftIpv6->sin6_addr.__in6_union.__s6_addr32[part] != rightIpv6->sin6_addr.__in6_union.__s6_addr32[part])
{
return false;
}
}
return true;
}
else
{
FATAL_ERROR("Unrecognized socket family {}", left.ss_family);
return false;
return (leftIpv6->sin6_port == rightIpv6->sin6_port && memcmp(&leftIpv6->sin6_addr, &rightIpv6->sin6_addr, sizeof(in6_addr)) == 0);
}
FATAL_ERROR("Unrecognized socket family {}", left.ss_family);
return false;
}
// Start looking for ports bound to localhost or wildcard.
int ScanProcNetTCP(wsl::shared::SocketChannel& channel)
// Monitor listening TCP sockets using sock_diag netlink interface.
int MonitorListeningSockets(wsl::shared::SocketChannel& channel)
{
// Periodically scan procfs for listening TCP sockets.
NetlinkChannel netlinkChannel(SOCK_RAW, NETLINK_SOCK_DIAG);
std::vector<sockaddr_storage> relays{};
int result = 0;
for (;;)
{
std::vector<sockaddr_storage> sockets;
wil::unique_file tcp4File{fopen("/proc/net/tcp", "r")};
if (tcp4File)
{
sockets = ParseTcpFile(AF_INET, tcp4File.get());
}
wil::unique_file tcp6File{fopen("/proc/net/tcp6", "r")};
if (tcp6File)
{
auto ipv6Sockets = ParseTcpFile(AF_INET6, tcp6File.get());
sockets.insert(sockets.end(), ipv6Sockets.begin(), ipv6Sockets.end());
}
if (!tcp4File && !tcp6File)
{
LOG_ERROR("Failed to open /proc/net/tcp and /proc/net/tcp6, closing port relay");
return 1;
}
auto sockets = QueryListeningSockets(netlinkChannel);
// Stop any relays that no longer match listening ports.
std::erase_if(relays, [&](const auto& entry) {
@ -267,9 +221,7 @@ int ScanProcNetTCP(wsl::shared::SocketChannel& channel)
}
// Sleep before scanning again.
//
// TODO: Investigate using EBPF notifications instead of a sleep.
sleep(1);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
return result;
@ -433,7 +385,7 @@ try
if (ScanForPorts)
{
return ScanProcNetTCP(channel);
return MonitorListeningSockets(channel);
}
return 0;

View File

@ -1741,7 +1741,7 @@ Return Value:
TimeoutSeconds.value(),
[&]() {
errno = wil::ResultFromCaughtException();
return errno == ENOENT || errno == ENXIO || errno == EIO;
return errno == ENOENT || errno == ENXIO || errno == EIO || ((strcmp(Type, VIRTIO_FS_TYPE) == 0) && (errno == EINVAL));
});
}
else
@ -1752,7 +1752,7 @@ Return Value:
catch (...)
{
errno = wil::ResultFromCaughtException();
LOG_ERROR("mount({}, {}, {}, 0x{}x, {}) failed {}", Source, Target, Type, MountFlags, Options, errno);
LOG_ERROR("mount({}, {}, {}, {:#x}, {}) failed {}", Source, Target, Type, MountFlags, Options, errno);
return -errno;
}

View File

@ -1584,9 +1584,10 @@ struct WSLA_MOUNT
enum MountType : uint8_t
{
None,
Chroot = 1,
OverlayFs = 2,
KernelModules = 4
ReadOnly = 1,
Chroot = 2,
OverlayFs = 4,
KernelModules = 8
};
char Buffer[];

View File

@ -64,6 +64,30 @@ GUID DeviceHostProxy::AddNewDevice(const GUID& Type, const wil::com_ptr<IPlan9Fi
return instanceId;
}
void DeviceHostProxy::RemoveDevice(const GUID& Type, const GUID& InstanceId)
{
{
auto lock = m_devicesLock.lock_exclusive();
THROW_HR_IF(E_CHANGED_STATE, m_devicesShutdown);
THROW_HR_IF(E_INVALIDARG, m_devices.find(InstanceId) == m_devices.end());
m_devices.erase(InstanceId);
}
// N.B. Removing the FlexIov device is best effort since not all versions of Windows support it.
try
{
ModifySettingRequest<FlexibleIoDevice> request;
request.RequestType = ModifyRequestType::Remove;
request.ResourcePath = L"VirtualMachine/Devices/FlexibleIov/";
request.ResourcePath += wsl::shared::string::GuidToString<wchar_t>(InstanceId, wsl::shared::string::GuidToStringFlags::None);
request.Settings.EmulatorId = Type;
request.Settings.HostingModel = FlexibleIoDeviceHostingModel::ExternalRestricted;
wsl::windows::common::hcs::ModifyComputeSystem(m_system.get(), wsl::shared::ToJsonW(request).c_str());
}
CATCH_LOG()
}
void DeviceHostProxy::AddRemoteFileSystem(const GUID& ImplementationClsid, const std::wstring& Tag, const wil::com_ptr<IPlan9FileSystem>& Plan9Fs)
{
auto lock = m_lock.lock_exclusive();

View File

@ -14,6 +14,8 @@ public:
GUID AddNewDevice(const GUID& Type, const wil::com_ptr<IPlan9FileSystem>& Plan9Fs, const std::wstring& VirtIoTag);
void RemoveDevice(const GUID& Type, const GUID& InstanceId);
void AddRemoteFileSystem(const GUID& ImplementationClsid, const std::wstring& Tag, const wil::com_ptr<IPlan9FileSystem>& Plan9Fs);
wil::com_ptr<IPlan9FileSystem> GetRemoteFileSystem(const GUID& ImplementationClsid, std::wstring_view Tag);

View File

@ -9,6 +9,15 @@ GuestDeviceManager::GuestDeviceManager(_In_ const std::wstring& machineId, _In_
{
}
GuestDeviceManager::~GuestDeviceManager()
{
try
{
m_deviceHostSupport->Shutdown();
}
CATCH_LOG()
}
_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)
@ -40,18 +49,20 @@ GUID GuestDeviceManager::AddHdvShareWithOptions(
if (!server)
{
server = wil::CoCreateInstance<IPlan9FileSystem>(ImplementationClsid, (CLSCTX_LOCAL_SERVER | CLSCTX_ENABLE_CLOAKING | CLSCTX_ENABLE_AAA));
AddRemoteFileSystem(ImplementationClsid, c_defaultDeviceTag.c_str(), server);
m_deviceHostSupport->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);
return m_deviceHostSupport->AddNewDevice(DeviceId, server, AccessName);
}
_Requires_lock_not_held_(m_lock)
GUID GuestDeviceManager::AddNewDevice(_In_ const GUID& deviceId, _In_ const wil::com_ptr<IPlan9FileSystem>& server, _In_ PCWSTR tag)
{
auto guestDeviceLock = m_lock.lock_exclusive();
return m_deviceHostSupport->AddNewDevice(deviceId, server, tag);
}
@ -135,9 +146,9 @@ wil::com_ptr<IPlan9FileSystem> GuestDeviceManager::GetRemoteFileSystem(_In_ REFC
return m_deviceHostSupport->GetRemoteFileSystem(clsid, tag);
}
void GuestDeviceManager::Shutdown()
try
_Requires_lock_not_held_(m_lock)
void GuestDeviceManager::RemoveGuestDevice(_In_ const GUID& DeviceId, _In_ const GUID& InstanceId)
{
m_deviceHostSupport->Shutdown();
auto guestDeviceLock = m_lock.lock_exclusive();
m_deviceHostSupport->RemoveDevice(DeviceId, InstanceId);
}
CATCH_LOG()

View File

@ -22,6 +22,7 @@ class GuestDeviceManager
{
public:
GuestDeviceManager(_In_ const std::wstring& machineId, _In_ const GUID& runtimeId);
~GuestDeviceManager();
_Requires_lock_not_held_(m_lock)
GUID AddGuestDevice(
@ -33,6 +34,7 @@ public:
_In_ UINT32 Flags,
_In_ HANDLE UserToken);
_Requires_lock_not_held_(m_lock)
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);
@ -41,7 +43,8 @@ public:
wil::com_ptr<IPlan9FileSystem> GetRemoteFileSystem(_In_ REFCLSID clsid, _In_ std::wstring_view tag);
void Shutdown();
_Requires_lock_not_held_(m_lock)
void RemoveGuestDevice(_In_ const GUID& DeviceId, _In_ const GUID& InstanceId);
private:
_Requires_lock_held_(m_lock)

View File

@ -169,18 +169,6 @@ RunningWSLAProcess::ProcessResult RunningWSLAProcess::WaitAndCaptureOutput(DWORD
return result;
}
ClientRunningWSLAProcess WSLAProcessLauncher::Launch(IWSLASession& Session)
{
auto [hresult, error, process] = LaunchNoThrow(Session);
if (FAILED(hresult))
{
auto commandLine = wsl::shared::string::Join(m_arguments, ' ');
THROW_HR_MSG(hresult, "Failed to launch process: %hs (commandline: %hs). Errno = %i", m_executable.c_str(), commandLine.c_str(), error);
}
return std::move(process.value());
}
std::tuple<HRESULT, int, std::optional<ClientRunningWSLAProcess>> WSLAProcessLauncher::LaunchNoThrow(IWSLASession& Session)
{
auto [options, commandLine, env] = CreateProcessOptions();
@ -198,6 +186,24 @@ std::tuple<HRESULT, int, std::optional<ClientRunningWSLAProcess>> WSLAProcessLau
return {S_OK, 0, ClientRunningWSLAProcess{std::move(process), std::move(m_fds)}};
}
std::tuple<HRESULT, int, std::optional<ClientRunningWSLAProcess>> WSLAProcessLauncher::LaunchNoThrow(IWSLAContainer& Container)
{
auto [options, commandLine, env] = CreateProcessOptions();
options.Executable = nullptr; // Must be null for exec.
wil::com_ptr<IWSLAProcess> process;
int error = -1;
auto result = Container.Exec(&options, &process, &error);
if (FAILED(result))
{
return std::make_tuple(result, error, std::optional<ClientRunningWSLAProcess>());
}
wsl::windows::common::security::ConfigureForCOMImpersonation(process.get());
return {S_OK, 0, ClientRunningWSLAProcess{std::move(process), std::move(m_fds)}};
}
IWSLAProcess& ClientRunningWSLAProcess::Get()
{
return *m_process.get();

View File

@ -91,9 +91,23 @@ public:
void AddFd(WSLA_PROCESS_FD Fd);
void SetTtySize(ULONG Rows, ULONG Columns);
// TODO: Add overloads for IWSLAContainer once implemented.
ClientRunningWSLAProcess Launch(IWSLASession& Session);
std::tuple<HRESULT, int, std::optional<ClientRunningWSLAProcess>> LaunchNoThrow(IWSLASession& Session);
std::tuple<HRESULT, int, std::optional<ClientRunningWSLAProcess>> LaunchNoThrow(IWSLAContainer& Container);
template <typename T>
auto Launch(T& Context)
{
auto [hresult, error, process] = LaunchNoThrow(Context);
if (FAILED(hresult))
{
auto commandLine = wsl::shared::string::Join(m_arguments, ' ');
THROW_HR_MSG(
hresult, "Failed to launch process: %hs (commandline: %hs). Errno = %i", m_executable.c_str(), commandLine.c_str(), error);
}
return std::move(process.value());
}
std::string FormatResult(const RunningWSLAProcess::ProcessResult& result);
std::string FormatResult(const int code);

View File

@ -1552,6 +1552,8 @@ int WslaShell(_In_ std::wstring_view commandLine)
parser.AddArgument(Utf8String(shell), L"--shell");
parser.AddArgument(
SetFlag<int, WslaFeatureFlagsDnsTunneling>(reinterpret_cast<int&>(sessionSettings.FeatureFlags)), L"--dns-tunneling");
parser.AddArgument(
SetFlag<int, WslaFeatureFlagsVirtioFs>(reinterpret_cast<int&>(sessionSettings.FeatureFlags)), L"--virtiofs");
parser.AddArgument(Integer(sessionSettings.MemoryMb), L"--memory");
parser.AddArgument(Integer(sessionSettings.CpuCount), L"--cpu");
parser.AddArgument(Utf8String(rootVhdTypeOverride), L"--fstype");
@ -1565,7 +1567,7 @@ int WslaShell(_In_ std::wstring_view commandLine)
if (help)
{
const auto usage = std::format(
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])",
LR"({} --wsla [--vhd </path/to/vhd>] [--shell </path/to/shell>] [--memory <memory-mb>] [--cpu <cpus>] [--dns-tunneling] [--virtiofs] [--networking-mode <mode>] [--fstype <fstype>] [--container-vhd </path/to/vhd>] [--help])",
WSL_BINARY_NAME);
wprintf(L"%ls\n", usage.c_str());

View File

@ -67,8 +67,10 @@ void wsl::core::filesystem::CreateVhd(_In_ LPCWSTR target, _In_ ULONGLONG maximu
// to the VHD because the operation is done while impersonating the user.
auto sd = windows::common::security::CreateSecurityDescriptor(userSid);
wil::unique_hfile vhd{};
THROW_IF_WIN32_ERROR(
::CreateVirtualDisk(&storageType, target, VIRTUAL_DISK_ACCESS_NONE, &sd, flags, 0, &createVhdParameters, nullptr, &vhd));
THROW_IF_WIN32_ERROR_MSG(
::CreateVirtualDisk(&storageType, target, VIRTUAL_DISK_ACCESS_NONE, &sd, flags, 0, &createVhdParameters, nullptr, &vhd),
"Path: %ls",
target);
}
wil::unique_handle wsl::core::filesystem::OpenVhd(_In_ LPCWSTR Path, _In_ VIRTUAL_DISK_ACCESS_MASK Mask)

View File

@ -18,6 +18,7 @@ Abstract:
using wsl::windows::common::relay::EventHandle;
using wsl::windows::common::relay::IOHandleStatus;
using wsl::windows::common::relay::LineBasedReadHandle;
using wsl::windows::common::relay::MultiHandleWait;
using wsl::windows::common::relay::OverlappedIOHandle;
using wsl::windows::common::relay::ReadHandle;
@ -1123,6 +1124,38 @@ HANDLE ReadHandle::GetHandle() const
return Event.get();
}
LineBasedReadHandle::LineBasedReadHandle(wil::unique_handle&& MovedHandle, std::function<void(const gsl::span<char>& Line)>&& OnLine) :
ReadHandle(std::move(MovedHandle), [this](const gsl::span<char>& Buffer) { OnRead(Buffer); }), OnLine(OnLine)
{
}
LineBasedReadHandle::~LineBasedReadHandle()
{
// Call the callback with any pending data (in case of an incomplete line).
if (!PendingBuffer.empty())
{
OnLine(PendingBuffer);
}
}
void LineBasedReadHandle::OnRead(const gsl::span<char>& Buffer)
{
auto begin = Buffer.begin();
auto end = std::ranges::find(Buffer, '\n');
while (end != Buffer.end())
{
PendingBuffer.insert(PendingBuffer.end(), begin, end);
OnLine(PendingBuffer);
PendingBuffer.clear();
begin = end + 1;
end = std::ranges::find(begin, Buffer.end(), '\n');
}
PendingBuffer.insert(PendingBuffer.end(), begin, end);
}
WriteHandle::WriteHandle(wil::unique_handle&& MovedHandle, const std::vector<char>& Buffer) :
Handle(std::move(MovedHandle)), Buffer(Buffer)
{

View File

@ -201,7 +201,7 @@ public:
NON_MOVABLE(ReadHandle);
ReadHandle(wil::unique_handle&& MovedHandle, std::function<void(const gsl::span<char>& Buffer)>&& OnRead);
~ReadHandle();
virtual ~ReadHandle();
void Schedule() override;
void Collect() override;
HANDLE GetHandle() const override;
@ -214,6 +214,22 @@ private:
std::vector<char> Buffer = std::vector<char>(LX_RELAY_BUFFER_SIZE);
};
class LineBasedReadHandle : public ReadHandle
{
public:
NON_COPYABLE(LineBasedReadHandle);
NON_MOVABLE(LineBasedReadHandle);
LineBasedReadHandle(wil::unique_handle&& MovedHandle, std::function<void(const gsl::span<char>& Buffer)>&& OneLine);
~LineBasedReadHandle();
private:
void OnRead(const gsl::span<char>& Buffer);
std::function<void(const gsl::span<char>& Buffer)> OnLine;
std::string PendingBuffer;
};
class WriteHandle : public OverlappedIOHandle
{
public:

View File

@ -774,10 +774,7 @@ WslCoreVm::~WslCoreVm() noexcept
}
// Shutdown virtio device hosts.
if (m_guestDeviceManager)
{
m_guestDeviceManager->Shutdown();
}
m_guestDeviceManager.reset();
// Call RevokeVmAccess on each VHD that was added to the utility VM. This
// ensures that the ACL on the VHD does not grow unbounded.
@ -1755,8 +1752,10 @@ void WslCoreVm::InitializeGuest()
{
try
{
m_guestDeviceManager->AddSharedMemoryDevice(
VIRTIO_FS_CLASS_ID, L"wslg", L"wslg", WSLG_SHARED_MEMORY_SIZE_MB, m_userToken.get());
// Use the appropriate virtiofs class ID based on m_userToken elevation.
const bool admin = wsl::windows::common::security::IsTokenElevated(m_userToken.get());
const GUID classId = admin ? VIRTIO_FS_ADMIN_CLASS_ID : VIRTIO_FS_CLASS_ID;
m_guestDeviceManager->AddSharedMemoryDevice(classId, L"wslg", L"wslg", WSLG_SHARED_MEMORY_SIZE_MB, m_userToken.get());
m_sharedMemoryRoot = std::format(L"WSL\\{}\\wslg", m_machineId);
}
CATCH_LOG()

View File

@ -128,36 +128,20 @@ void ContainerEventTracker::Run(ServiceRunningProcess& process)
{
try
{
std::string pendingBuffer;
wsl::windows::common::relay::MultiHandleWait io;
auto onStdout = [&](const gsl::span<char>& buffer) {
auto oneLineWritten = [&](const gsl::span<char>& buffer) {
// nerdctl events' output is line based. Call OnEvent() for each completed line.
auto begin = buffer.begin();
auto end = std::ranges::find(buffer, '\n');
while (end != buffer.end())
if (!buffer.empty()) // nerdctl inserts empty lines between events, skip those.
{
pendingBuffer.insert(pendingBuffer.end(), begin, end);
if (!pendingBuffer.empty()) // nerdctl inserts empty lines between events, skip those.
{
OnEvent(pendingBuffer);
}
pendingBuffer.clear();
begin = end + 1;
end = std::ranges::find(begin, buffer.end(), '\n');
OnEvent(std::string{buffer.begin(), buffer.end()});
}
pendingBuffer.insert(pendingBuffer.end(), begin, end);
};
auto onStop = [&]() { io.Cancel(); };
io.AddHandle(std::make_unique<common::relay::ReadHandle>(process.GetStdHandle(1), std::move(onStdout)));
io.AddHandle(std::make_unique<common::relay::LineBasedReadHandle>(process.GetStdHandle(1), std::move(oneLineWritten)));
io.AddHandle(std::make_unique<common::relay::EventHandle>(m_stopEvent.get(), std::move(onStop)));
if (io.Run({}))

View File

@ -51,10 +51,26 @@ void ServiceRunningProcess::GetState(WSLA_PROCESS_STATE* State, int* Code)
THROW_IF_FAILED(m_process->GetState(State, Code));
}
ServiceRunningProcess ServiceProcessLauncher::Launch(WSLAVirtualMachine& virtualMachine)
std::tuple<HRESULT, int, std::optional<ServiceRunningProcess>> ServiceProcessLauncher::LaunchNoThrow(WSLAVirtualMachine& virtualMachine)
{
auto [options, commandLine, env] = CreateProcessOptions();
int Error = -1;
int error = -1;
return ServiceRunningProcess(virtualMachine.CreateLinuxProcess(options, &Error), std::move(m_fds));
std::optional<ServiceRunningProcess> process;
auto result =
wil::ResultFromException([&]() { process.emplace(virtualMachine.CreateLinuxProcess(options, &error), std::move(m_fds)); });
return {result, error, std::move(process)};
}
ServiceRunningProcess ServiceProcessLauncher::Launch(WSLAVirtualMachine& virtualMachine)
{
auto [hresult, error, process] = LaunchNoThrow(virtualMachine);
if (FAILED(hresult))
{
auto commandLine = wsl::shared::string::Join(m_arguments, ' ');
THROW_HR_MSG(hresult, "Failed to launch process: %hs (commandline: %hs). Errno = %i", m_executable.c_str(), commandLine.c_str(), error);
}
return std::move(process.value());
}

View File

@ -45,6 +45,7 @@ public:
NON_MOVABLE(ServiceProcessLauncher);
using WSLAProcessLauncher::WSLAProcessLauncher;
std::tuple<HRESULT, int, std::optional<ServiceRunningProcess>> LaunchNoThrow(WSLAVirtualMachine& virtualMachine);
ServiceRunningProcess Launch(WSLAVirtualMachine& virtualMachine);
};
} // namespace wsl::windows::service::wsla

View File

@ -126,6 +126,7 @@ try
m_state);
ServiceProcessLauncher launcher(
nerdctlPath, {nerdctlPath, "stop", m_name, "--time", std::to_string(static_cast<ULONG>(std::round(TimeoutMs / 1000)))});
// TODO: Figure out how we want to handle custom signals.
// nerdctl stop has a --time and a --signal option that can be used
// By default, it uses SIGTERM and a default timeout of 10 seconds.
@ -195,35 +196,93 @@ CATCH_RETURN();
HRESULT WSLAContainer::Exec(const WSLA_PROCESS_OPTIONS* Options, IWSLAProcess** Process, int* Errno)
try
{
// auto process = wil::MakeOrThrow<WSLAProcess>();
*Errno = -1;
THROW_HR_IF_MSG(E_INVALIDARG, Options->Executable != nullptr, "Executable must be null");
// process.CopyTo(__uuidof(IWSLAProcess), (void**)Process);
std::lock_guard lock{m_lock};
THROW_HR_IF_MSG(
HRESULT_FROM_WIN32(ERROR_INVALID_STATE),
State() != WslaContainerStateRunning,
"Container %hs is not running. State: %i",
m_name.c_str(),
m_state);
auto [hasStdin, hasTty] = ParseFdStatus(*Options);
std::vector<std::string> args{nerdctlPath, "exec"};
if (hasStdin)
{
args.push_back("-i");
}
if (hasTty)
{
args.push_back("-t");
}
AddEnvironmentVariables(args, *Options);
args.emplace_back(m_id);
for (ULONG i = 0; i < Options->CommandLineCount; i++)
{
args.emplace_back(Options->CommandLine[i]);
}
ServiceProcessLauncher launcher(nerdctlPath, args, {}, common::ProcessFlags::None);
for (auto i = 0; i < Options->FdsCount; i++)
{
launcher.AddFd(Options->Fds[i]);
}
std::optional<ServiceRunningProcess> process;
HRESULT result = E_FAIL;
std::tie(result, *Errno, process) = launcher.LaunchNoThrow(*m_parentVM);
THROW_IF_FAILED(result);
THROW_IF_FAILED(process->Get().QueryInterface(__uuidof(IWSLAProcess), (void**)Process));
return S_OK;
}
CATCH_RETURN();
Microsoft::WRL::ComPtr<WSLAContainer> WSLAContainer::Create(
const WSLA_CONTAINER_OPTIONS& containerOptions, WSLAVirtualMachine& parentVM, ContainerEventTracker& eventTracker)
std::pair<bool, bool> WSLAContainer::ParseFdStatus(const WSLA_PROCESS_OPTIONS& Options)
{
// 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++)
for (size_t i = 0; i < Options.FdsCount; i++)
{
if (containerOptions.InitProcessOptions.Fds[i].Fd == 0)
if (Options.Fds[i].Fd == 0)
{
hasStdin = true;
}
if (containerOptions.InitProcessOptions.Fds[i].Type == WSLAFdTypeTerminalInput ||
containerOptions.InitProcessOptions.Fds[i].Type == WSLAFdTypeTerminalOutput)
if (Options.Fds[i].Type == WSLAFdTypeTerminalInput || Options.Fds[i].Type == WSLAFdTypeTerminalOutput)
{
hasTty = true;
}
}
return {hasStdin, hasTty};
}
void WSLAContainer::AddEnvironmentVariables(std::vector<std::string>& args, const WSLA_PROCESS_OPTIONS& options)
{
for (ULONG i = 0; i < options.EnvironmentCount; i++)
{
THROW_HR_IF_MSG(E_INVALIDARG, options.Environment[i][0] == '-', "Invalid environment string: %hs", options.Environment[i]);
args.insert(args.end(), {"-e", options.Environment[i]});
}
}
Microsoft::WRL::ComPtr<WSLAContainer> WSLAContainer::Create(
const WSLA_CONTAINER_OPTIONS& containerOptions, WSLAVirtualMachine& parentVM, ContainerEventTracker& eventTracker)
{
auto [hasStdin, hasTty] = ParseFdStatus(containerOptions.InitProcessOptions);
// Don't support stdin for now since it will hang.
// TODO: Remove once stdin is fixed in nerdctl.
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), hasStdin && !hasTty);
@ -239,6 +298,8 @@ Microsoft::WRL::ComPtr<WSLAContainer> WSLAContainer::Create(
inputOptions.push_back("-t");
}
AddEnvironmentVariables(inputOptions, containerOptions.InitProcessOptions);
auto args = PrepareNerdctlCreateCommand(containerOptions, std::move(inputOptions));
ServiceProcessLauncher launcher(nerdctlPath, args, {});
@ -301,17 +362,6 @@ std::vector<std::string> WSLAContainer::PrepareNerdctlCreateCommand(const WSLA_C
args.insert(args.end(), defaultNerdctlCreateArgs.begin(), defaultNerdctlCreateArgs.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");

View File

@ -49,6 +49,7 @@ private:
std::optional<std::string> GetNerdctlStatus();
std::recursive_mutex m_lock;
wil::unique_event m_startedEvent{wil::EventOptions::ManualReset};
std::optional<ServiceRunningProcess> m_containerProcess;
std::string m_name;
@ -56,9 +57,10 @@ private:
std::string m_id;
WSLA_CONTAINER_STATE m_state = WslaContainerStateInvalid;
WSLAVirtualMachine* m_parentVM = nullptr;
std::recursive_mutex m_lock;
ContainerEventTracker::ContainerTrackingReference m_trackingReference;
static std::vector<std::string> PrepareNerdctlCreateCommand(const WSLA_CONTAINER_OPTIONS& options, std::vector<std::string>&& inputOptions);
static std::pair<bool, bool> ParseFdStatus(const WSLA_PROCESS_OPTIONS& Options);
static void AddEnvironmentVariables(std::vector<std::string>& args, const WSLA_PROCESS_OPTIONS& options);
};
} // namespace wsl::windows::service::wsla

View File

@ -22,6 +22,8 @@ Abstract:
using wsl::windows::service::wsla::WSLASession;
using wsl::windows::service::wsla::WSLAVirtualMachine;
constexpr auto c_containerdStorage = "/var/lib/containerd";
WSLASession::WSLASession(ULONG id, const WSLA_SESSION_SETTINGS& Settings, WSLAUserSessionImpl& userSessionImpl) :
m_id(id), m_sessionSettings(Settings), m_userSession(&userSessionImpl), m_displayName(Settings.DisplayName)
@ -39,27 +41,33 @@ WSLASession::WSLASession(ULONG id, const WSLA_SESSION_SETTINGS& Settings, WSLAUs
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();
// Make sure that everything is destroyed correctly if an exception is thrown.
auto errorCleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
m_sessionTerminatingEvent.SetEvent();
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)
if (m_containerdThread.joinable())
{
throw;
m_containerdThread.join();
}
}
});
// Launch containerd
// TODO: Rework the daemon logic so we can have only one thread watching all daemons.
ServiceProcessLauncher launcher{
"/usr/bin/containerd",
{"/usr/bin/containerd"},
{{"PATH=/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin"}},
common::ProcessFlags::Stdout | common::ProcessFlags::Stderr};
m_containerdThread = std::thread(&WSLASession::MonitorContainerd, this, launcher.Launch(*m_virtualMachine.Get()));
// Wait for containerd to be ready before starting the event tracker.
// TODO: Configurable timeout.
THROW_WIN32_IF_MSG(ERROR_TIMEOUT, !m_containerdReadyEvent.wait(10 * 1000), "Timed out waiting for containerd to start");
// Start the event tracker.
m_eventTracker.emplace(*m_virtualMachine.Get());
errorCleanup.release();
}
WSLAVirtualMachine::Settings WSLASession::CreateVmSettings(const WSLA_SESSION_SETTINGS& Settings)
@ -91,7 +99,7 @@ WSLAVirtualMachine::Settings WSLASession::CreateVmSettings(const WSLA_SESSION_SE
#endif
vmSettings.RootVhdType = "squashfs";
vmSettings.RootVhdType = "ext4";
}
if (Settings.DmesgOutput != 0)
@ -116,12 +124,20 @@ WSLASession::~WSLASession()
m_containers.clear();
m_sessionTerminatingEvent.SetEvent();
// N.B. The containerd thread can only run if the VM is running.
if (m_containerdThread.joinable())
{
m_containerdThread.join();
}
if (m_virtualMachine)
{
m_virtualMachine->OnSessionTerminated();
// N.B. containerd has exited by this point, so unmounting the VHD is safe since no container can be running.
// TODO: Signal containerd to exit before unmounting /root.
LOG_IF_FAILED(m_virtualMachine->Unmount("/root"));
m_virtualMachine->OnSessionTerminated();
LOG_IF_FAILED(m_virtualMachine->Unmount(c_containerdStorage));
m_virtualMachine.Reset();
}
@ -137,7 +153,7 @@ 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);
m_virtualMachine->Mount("", c_containerdStorage, "tmpfs", "", 0);
return;
}
@ -192,7 +208,7 @@ void WSLASession::ConfigureStorage(const WSLA_SESSION_SETTINGS& Settings)
}
// Mount the device to /root.
m_virtualMachine->Mount(diskDevice.c_str(), "/root", "ext4", "", 0);
m_virtualMachine->Mount(diskDevice.c_str(), c_containerdStorage, "ext4", "", 0);
deleteVhdOnFailure.release();
}
@ -213,6 +229,68 @@ void WSLASession::CopyDisplayName(_Out_writes_z_(bufferLength) PWSTR buffer, siz
wcscpy_s(buffer, bufferLength, m_displayName.c_str());
}
void WSLASession::OnContainerdLog(const gsl::span<char>& buffer)
try
{
if (buffer.empty())
{
return;
}
constexpr auto c_containerdReadyLogLine = "containerd successfully booted";
std::string entry = {buffer.begin(), buffer.end()};
WSL_LOG("ContainerdLog", TraceLoggingValue(entry.c_str(), "Content"), TraceLoggingValue(m_displayName.c_str(), "Name"));
auto parsed = nlohmann::json::parse(entry);
if (!m_containerdReadyEvent.is_signaled())
{
auto it = parsed.find("msg");
if (it != parsed.end())
{
if (it->get<std::string>().starts_with(c_containerdReadyLogLine))
{
m_containerdReadyEvent.SetEvent();
}
}
}
}
CATCH_LOG();
void WSLASession::MonitorContainerd(ServiceRunningProcess&& process)
try
{
windows::common::relay::MultiHandleWait io;
// Read stdout & stderr.
io.AddHandle(std::make_unique<windows::common::relay::LineBasedReadHandle>(
process.GetStdHandle(1), [&](const auto& data) { OnContainerdLog(data); }));
io.AddHandle(std::make_unique<windows::common::relay::LineBasedReadHandle>(
process.GetStdHandle(2), [&](const auto& data) { OnContainerdLog(data); }));
// Exit if either the VM terminates or containerd exits.
io.AddHandle(std::make_unique<windows::common::relay::EventHandle>(process.GetExitEvent(), [&]() { io.Cancel(); }));
io.AddHandle(std::make_unique<windows::common::relay::EventHandle>(m_sessionTerminatingEvent.get(), [&]() { io.Cancel(); }));
io.Run({});
if (!m_sessionTerminatingEvent.is_signaled())
{
// If containerd exited before the VM starts terminating, then it exited unexpectedly.
WSL_LOG("UnexpectedContainerdExit", TraceLoggingValue(m_displayName.c_str(), "SessionDisplayName"));
}
else
{
// Otherwise, the session is shutting down; terminate containerd before exiting.
process.Get().Signal(15); // SIGTERM
process.Wait(30 * 1000); // TODO: Configurable timeout.
}
}
CATCH_LOG();
HRESULT WSLASession::PullImage(LPCWSTR Image, const WSLA_REGISTRY_AUTHENTICATION_INFORMATION* RegistryInformation, IProgressCallback* ProgressCallback)
{
return E_NOTIMPL;

View File

@ -65,14 +65,19 @@ private:
void ConfigureStorage(const WSLA_SESSION_SETTINGS& Settings);
void Ext4Format(const std::string& Device);
void ClearDeletedContainers();
void OnContainerdLog(const gsl::span<char>& Data);
void MonitorContainerd(ServiceRunningProcess&& process);
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::optional<ContainerEventTracker> m_eventTracker;
wil::unique_event m_containerdReadyEvent{wil::EventOptions::ManualReset};
std::thread m_containerdThread;
std::wstring m_displayName;
std::filesystem::path m_storageVhdPath;
std::map<std::string, Microsoft::WRL::ComPtr<WSLAContainer>> m_containers;
wil::unique_event m_sessionTerminatingEvent{wil::EventOptions::ManualReset};
std::recursive_mutex m_lock;
};

View File

@ -34,7 +34,10 @@ constexpr auto SAVED_STATE_FILE_PREFIX = L"saved-state-";
constexpr auto RECEIVE_TIMEOUT = 30 * 1000;
// WSLA-specific virtio device class IDs.
DEFINE_GUID(WSLA_VIRTIO_FS_ADMIN_CLASS_ID, 0x8F7C2A3B, 0xD9E4, 0x4C1F, 0xA2, 0xB8, 0x5E, 0x3D, 0x7C, 0x9F, 0x1A, 0x6E); // {8F7C2A3B-D9E4-4C1F-A2B8-5E3D7C9F1A6E}
DEFINE_GUID(WSLA_VIRTIO_FS_CLASS_ID, 0x06ED032F, 0xC528, 0x41C1, 0xB7, 0x5D, 0x90, 0x5E, 0xEE, 0x82, 0x3B, 0xBA); // {06ED032F-C528-41C1-B75D-905EEE823BBA}
DEFINE_GUID(WSLA_VIRTIO_NET_CLASS_ID, 0x7B3C9A42, 0x8E1F, 0x4D5A, 0x9F, 0x2E, 0xC4, 0xA7, 0xB8, 0xD3, 0xE6, 0xF1); // {7B3C9A42-8E1F-4D5A-9F2E-C4A7B8D3E6F1}
DEFINE_GUID(WSLA_VIRTIO_PMEM_CLASS_ID, 0xABB755FC, 0x1B86, 0x4255, 0x83, 0xE2, 0xE5, 0x78, 0x7A, 0xBC, 0xF6, 0xC2); // {ABB755FC-1B86-4255-83E2-E5787ABCF6C2}
WSLAVirtualMachine::WSLAVirtualMachine(WSLAVirtualMachine::Settings&& Settings, PSID UserSid) :
m_settings(std::move(Settings)), m_userSid(UserSid)
@ -89,10 +92,7 @@ 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_guestDeviceManager.reset();
m_computeSystem.reset();
@ -306,8 +306,7 @@ void WSLAVirtualMachine::Start()
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)
if (FeatureEnabled(WslaFeatureFlagsVirtioFs) || m_settings.NetworkingMode == WSLANetworkingModeVirtioProxy)
{
m_guestDeviceManager = std::make_shared<GuestDeviceManager>(m_vmIdString, m_vmId);
}
@ -384,10 +383,11 @@ void WSLAVirtualMachine::ConfigureMounts()
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);
Mount(m_initChannel, nullptr, "/sys/fs/cgroup", "cgroup2", "", 0);
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);
MountGpuLibraries("/usr/lib/wsl/lib", "/usr/lib/wsl/drivers");
}
}
@ -396,6 +396,11 @@ bool WSLAVirtualMachine::FeatureEnabled(WSLAFeatureFlags Value) const
return static_cast<ULONG>(m_settings.FeatureFlags) & static_cast<ULONG>(Value);
}
const wil::unique_event& WSLAVirtualMachine::TerminatingEvent()
{
return m_vmTerminatingEvent;
}
void WSLAVirtualMachine::WatchForExitedProcesses(wsl::shared::SocketChannel& Channel)
try
{
@ -931,6 +936,7 @@ void WSLAVirtualMachine::Mount(LPCSTR Source, LPCSTR Target, LPCSTR Type, LPCSTR
void WSLAVirtualMachine::Mount(shared::SocketChannel& Channel, LPCSTR Source, LPCSTR Target, LPCSTR Type, LPCSTR Options, ULONG Flags)
{
static_assert(WSLAMountFlagsNone == WSLA_MOUNT::None);
static_assert(WSLAMountFlagsReadOnly == WSLA_MOUNT::ReadOnly);
static_assert(WSLAMountFlagsChroot == WSLA_MOUNT::Chroot);
static_assert(WSLAMountFlagsWriteableOverlayFs == WSLA_MOUNT::OverlayFs);
@ -1151,91 +1157,144 @@ CATCH_RETURN();
HRESULT WSLAVirtualMachine::MountWindowsFolder(_In_ LPCWSTR WindowsPath, _In_ LPCSTR LinuxPath, _In_ BOOL ReadOnly)
{
return MountWindowsFolderImpl(WindowsPath, LinuxPath, ReadOnly, WSLAMountFlagsNone);
return MountWindowsFolderImpl(WindowsPath, LinuxPath, ReadOnly ? WSLAMountFlagsReadOnly : WSLAMountFlagsNone);
}
HRESULT WSLAVirtualMachine::MountWindowsFolderImpl(_In_ LPCWSTR WindowsPath, _In_ LPCSTR LinuxPath, _In_ BOOL ReadOnly, _In_ WSLAMountFlags Flags)
HRESULT WSLAVirtualMachine::MountWindowsFolderImpl(_In_ LPCWSTR WindowsPath, _In_ LPCSTR LinuxPath, _In_ WSLAMountFlags Flags)
try
{
std::filesystem::path Path(WindowsPath);
THROW_HR_IF_MSG(E_INVALIDARG, !Path.is_absolute(), "Path is not absolute: '%ls'", WindowsPath);
std::filesystem::path path(WindowsPath);
THROW_HR_IF_MSG(E_INVALIDARG, !path.is_absolute(), "Path is not absolute: '%ls'", WindowsPath);
THROW_HR_IF_MSG(
HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), !std::filesystem::is_directory(Path), "Path is not a directory: '%ls'", WindowsPath);
HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), !std::filesystem::is_directory(path), "Path is not a directory: '%ls'", WindowsPath);
GUID shareGuid{};
THROW_IF_FAILED(CoCreateGuid(&shareGuid));
auto shareName = shared::string::GuidToString<wchar_t>(shareGuid, shared::string::None);
const auto userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);
std::optional<GUID> instanceId;
{
// Create the plan9 share on the host
// Create the share on the host.
std::lock_guard lock(m_lock);
// Verify that this folder isn't already mounted.
auto it = m_plan9Mounts.find(LinuxPath);
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), it != m_plan9Mounts.end());
auto it = m_mountedWindowsFolders.find(LinuxPath);
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), it != m_mountedWindowsFolders.end());
hcs::AddPlan9Share(
m_computeSystem.get(),
shareName.c_str(),
shareName.c_str(),
WindowsPath,
LX_INIT_UTILITY_VM_PLAN9_PORT,
hcs::Plan9ShareFlags::AllowOptions | (ReadOnly ? hcs::Plan9ShareFlags::ReadOnly : hcs::Plan9ShareFlags::None),
wsl::windows::common::security::GetUserToken(TokenImpersonation).get());
if (!FeatureEnabled(WslaFeatureFlagsVirtioFs))
{
auto flags = hcs::Plan9ShareFlags::AllowOptions;
WI_SetFlagIf(flags, hcs::Plan9ShareFlags::ReadOnly, WI_IsFlagSet(Flags, WSLAMountFlagsReadOnly));
hcs::AddPlan9Share(
m_computeSystem.get(),
shareName.c_str(),
shareName.c_str(),
WindowsPath,
LX_INIT_UTILITY_VM_PLAN9_PORT,
flags,
userToken.get());
}
else
{
const bool admin = wsl::windows::common::security::IsTokenElevated(userToken.get());
m_guestDeviceManager->AddGuestDevice(
VIRTIO_FS_DEVICE_ID,
admin ? WSLA_VIRTIO_FS_ADMIN_CLASS_ID : WSLA_VIRTIO_FS_CLASS_ID,
shareName.c_str(),
L"",
WindowsPath,
VIRTIO_FS_FLAGS_TYPE_FILES,
userToken.get());
}
m_plan9Mounts.emplace(LinuxPath, shareName);
m_mountedWindowsFolders.emplace(LinuxPath, MountedFolderInfo{shareName, instanceId});
}
auto deleteOnFailure = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
std::lock_guard lock(m_lock);
LOG_HR_IF(E_UNEXPECTED, m_plan9Mounts.erase(LinuxPath) != 1);
auto mountIt = m_mountedWindowsFolders.find(LinuxPath);
if (WI_VERIFY(mountIt != m_mountedWindowsFolders.end()))
{
auto mountInfo = mountIt->second;
m_mountedWindowsFolders.erase(mountIt);
RemoveShare(mountInfo);
}
});
// Create the guest mount
auto [_, __, channel] = Fork(WSLA_FORK::Thread);
WSLA_CONNECT message;
message.HostPort = LX_INIT_UTILITY_VM_PLAN9_PORT;
auto fd = channel.Transaction(message).Result;
THROW_HR_IF_MSG(E_FAIL, fd < 0, "WSLA_CONNECT failed with %i", fd);
auto shareNameUtf8 = shared::string::WideToMultiByte(shareName);
auto mountOptions =
std::format("msize={},trans=fd,rfdno={},wfdno={},aname={},cache=mmap", LX_INIT_UTILITY_VM_PLAN9_BUFFER_SIZE, fd, fd, shareNameUtf8);
if (!FeatureEnabled(WslaFeatureFlagsVirtioFs))
{
auto [_, __, channel] = Fork(WSLA_FORK::Thread);
Mount(channel, shareNameUtf8.c_str(), LinuxPath, "9p", mountOptions.c_str(), Flags);
WSLA_CONNECT message;
message.HostPort = LX_INIT_UTILITY_VM_PLAN9_PORT;
auto fd = channel.Transaction(message).Result;
THROW_HR_IF_MSG(E_FAIL, fd < 0, "WSLA_CONNECT failed with %i", fd);
auto mountOptions = std::format(
"{},msize={},trans=fd,rfdno={},wfdno={},aname={},cache=mmap",
WI_IsFlagSet(Flags, WSLAMountFlagsReadOnly) ? "ro" : "rw",
LX_INIT_UTILITY_VM_PLAN9_BUFFER_SIZE,
fd,
fd,
shareNameUtf8);
Mount(channel, shareNameUtf8.c_str(), LinuxPath, "9p", mountOptions.c_str(), Flags);
}
else
{
std::string options = WI_IsFlagSet(Flags, WSLAMountFlagsReadOnly) ? "ro" : "rw";
Mount(m_initChannel, shareNameUtf8.c_str(), LinuxPath, "virtiofs", options.c_str(), Flags);
}
deleteOnFailure.release();
return S_OK;
}
CATCH_RETURN();
void WSLAVirtualMachine::RemoveShare(_In_ const MountedFolderInfo& MountInfo)
{
if (!FeatureEnabled(WslaFeatureFlagsVirtioFs))
{
WI_ASSERT(!MountInfo.InstanceId.has_value());
hcs::RemovePlan9Share(m_computeSystem.get(), MountInfo.ShareName.c_str(), LX_INIT_UTILITY_VM_PLAN9_PORT);
}
else if (WI_VERIFY(MountInfo.InstanceId.has_value()))
{
m_guestDeviceManager->RemoveGuestDevice(VIRTIO_FS_DEVICE_ID, MountInfo.InstanceId.value());
}
}
HRESULT WSLAVirtualMachine::UnmountWindowsFolder(_In_ LPCSTR LinuxPath)
try
{
std::lock_guard lock(m_lock);
// Verify that this folder is mounted.
auto it = m_plan9Mounts.find(LinuxPath);
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), it == m_plan9Mounts.end());
auto it = m_mountedWindowsFolders.find(LinuxPath);
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), it == m_mountedWindowsFolders.end());
// Unmount the folder from the guest. If the mount is not found, this most likely means that the guest unmounted it.
auto result = Unmount(LinuxPath);
THROW_HR_IF(result, FAILED(result) && result != HRESULT_FROM_WIN32(ERROR_NOT_FOUND));
// Remove the share from the host
hcs::RemovePlan9Share(m_computeSystem.get(), it->second.c_str(), LX_INIT_UTILITY_VM_PLAN9_PORT);
auto mountInfo = it->second;
m_mountedWindowsFolders.erase(it);
m_plan9Mounts.erase(it);
// Remove the share from the host
RemoveShare(mountInfo);
return S_OK;
}
CATCH_RETURN();
void WSLAVirtualMachine::MountGpuLibraries(_In_ LPCSTR LibrariesMountPoint, _In_ LPCSTR DriversMountpoint, _In_ DWORD Flags)
void WSLAVirtualMachine::MountGpuLibraries(_In_ LPCSTR LibrariesMountPoint, _In_ LPCSTR DriversMountpoint)
{
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_CONFIG_VALUE), !FeatureEnabled(WslaFeatureFlagsGPU));
@ -1245,7 +1304,7 @@ void WSLAVirtualMachine::MountGpuLibraries(_In_ LPCSTR LibrariesMountPoint, _In_
// Mount drivers.
THROW_IF_FAILED(MountWindowsFolderImpl(
std::format(L"{}\\System32\\DriverStore\\FileRepository", windowsPath).c_str(), DriversMountpoint, true, static_cast<WSLAMountFlags>(Flags)));
std::format(L"{}\\System32\\DriverStore\\FileRepository", windowsPath).c_str(), DriversMountpoint, WSLAMountFlagsReadOnly));
// Mount the inbox libraries.
auto inboxLibPath = std::format(L"{}\\System32\\lxss\\lib", windowsPath);
@ -1253,7 +1312,7 @@ void WSLAVirtualMachine::MountGpuLibraries(_In_ LPCSTR LibrariesMountPoint, _In_
if (std::filesystem::is_directory(inboxLibPath))
{
inboxLibMountPoint = std::format("{}/inbox", LibrariesMountPoint);
THROW_IF_FAILED(MountWindowsFolder(inboxLibPath.c_str(), inboxLibMountPoint->c_str(), true));
THROW_IF_FAILED(MountWindowsFolderImpl(inboxLibPath.c_str(), inboxLibMountPoint->c_str(), WSLAMountFlagsReadOnly));
}
// Mount the packaged libraries.
@ -1269,7 +1328,7 @@ void WSLAVirtualMachine::MountGpuLibraries(_In_ LPCSTR LibrariesMountPoint, _In_
#endif
auto packagedLibMountPoint = std::format("{}/packaged", LibrariesMountPoint);
THROW_IF_FAILED(MountWindowsFolder(packagedLibPath.c_str(), packagedLibMountPoint.c_str(), true));
THROW_IF_FAILED(MountWindowsFolderImpl(packagedLibPath.c_str(), packagedLibMountPoint.c_str(), WSLAMountFlagsReadOnly));
// Mount an overlay containing both inbox and packaged libraries (the packaged mount takes precedence).
std::string options = "lowerdir=" + packagedLibMountPoint;
@ -1278,7 +1337,7 @@ void WSLAVirtualMachine::MountGpuLibraries(_In_ LPCSTR LibrariesMountPoint, _In_
options += ":" + inboxLibMountPoint.value();
}
Mount(m_initChannel, "none", LibrariesMountPoint, "overlay", options.c_str(), Flags);
Mount(m_initChannel, "none", LibrariesMountPoint, "overlay", options.c_str(), 0);
}
std::filesystem::path WSLAVirtualMachine::GetCrashDumpFolder()

View File

@ -27,8 +27,9 @@ namespace wsl::windows::service::wsla {
enum WSLAMountFlags
{
WSLAMountFlagsNone = 0,
WSLAMountFlagsChroot = 1,
WSLAMountFlagsWriteableOverlayFs = 2,
WSLAMountFlagsReadOnly = 1,
WSLAMountFlagsChroot = 2,
WSLAMountFlagsWriteableOverlayFs = 4,
};
class WSLAUserSessionImpl;
@ -44,6 +45,12 @@ public:
wil::unique_socket Socket;
};
struct MountedFolderInfo
{
std::wstring ShareName;
std::optional<GUID> InstanceId; // Only used for VirtioFS devices
};
struct Settings
{
std::wstring DisplayName;
@ -74,7 +81,6 @@ public:
IFACEMETHOD(Unmount(_In_ const char* Path)) override;
IFACEMETHOD(MountWindowsFolder(_In_ LPCWSTR WindowsPath, _In_ LPCSTR LinuxPath, _In_ BOOL ReadOnly)) override;
IFACEMETHOD(UnmountWindowsFolder(_In_ LPCSTR LinuxPath)) override;
void MountGpuLibraries(_In_ LPCSTR LibrariesMountPoint, _In_ LPCSTR DriversMountpoint, _In_ DWORD Flags);
void OnProcessReleased(int Pid);
void RegisterCallback(_In_ ITerminationCallback* callback);
@ -86,8 +92,11 @@ public:
void DetachDisk(_In_ ULONG Lun);
void Mount(_In_ LPCSTR Source, _In_ LPCSTR Target, _In_ LPCSTR Type, _In_ LPCSTR Options, _In_ ULONG Flags);
const wil::unique_event& TerminatingEvent();
private:
static void Mount(wsl::shared::SocketChannel& Channel, LPCSTR Source, _In_ LPCSTR Target, _In_ LPCSTR Type, _In_ LPCSTR Options, _In_ ULONG Flags);
void MountGpuLibraries(_In_ LPCSTR LibrariesMountPoint, _In_ LPCSTR DriversMountpoint);
static void CALLBACK s_OnExit(_In_ HCS_EVENT* Event, _In_opt_ void* Context);
static bool ParseTtyInformation(
const WSLA_PROCESS_FD* Fds, ULONG FdCount, const WSLA_PROCESS_FD** TtyInput, const WSLA_PROCESS_FD** TtyOutput, const WSLA_PROCESS_FD** TtyControl);
@ -106,6 +115,7 @@ private:
ConnectedSocket ConnectSocket(wsl::shared::SocketChannel& Channel, int32_t Fd);
static void OpenLinuxFile(wsl::shared::SocketChannel& Channel, const char* Path, uint32_t Flags, int32_t Fd);
void LaunchPortRelay();
void RemoveShare(_In_ const MountedFolderInfo& MountInfo);
std::filesystem::path GetCrashDumpFolder();
void CreateVmSavedStateFile();
@ -116,7 +126,7 @@ private:
Microsoft::WRL::ComPtr<WSLAProcess> CreateLinuxProcessImpl(
_In_ const WSLA_PROCESS_OPTIONS& Options, int* Errno = nullptr, const TPrepareCommandLine& PrepareCommandLine = [](const auto&) {});
HRESULT MountWindowsFolderImpl(_In_ LPCWSTR WindowsPath, _In_ LPCSTR LinuxPath, _In_ BOOL ReadOnly, _In_ WSLAMountFlags Flags);
HRESULT MountWindowsFolderImpl(_In_ LPCWSTR WindowsPath, _In_ LPCSTR LinuxPath, _In_ WSLAMountFlags Flags = WSLAMountFlagsNone);
void WatchForExitedProcesses(wsl::shared::SocketChannel& Channel);
@ -162,7 +172,7 @@ private:
wil::unique_handle m_portRelayChannelWrite;
std::map<ULONG, AttachedDisk> m_attachedDisks;
std::map<std::string, std::wstring> m_plan9Mounts;
std::map<std::string, MountedFolderInfo> m_mountedWindowsFolders;
std::recursive_mutex m_lock;
std::mutex m_portRelaylock;
};

View File

@ -242,10 +242,11 @@ typedef enum _WSLANetworkingMode
typedef enum _WSLAFeatureFlags
{
WslaFeatureFlagsNone = 0,
WslaFeatureFlagsDnsTunneling = 1,
WslaFeatureFlagsEarlyBootDmesg = 2,
WslaFeatureFlagsGPU = 4,
WslaFeatureFlagsNone = 0,
WslaFeatureFlagsDnsTunneling = 1,
WslaFeatureFlagsEarlyBootDmesg = 2,
WslaFeatureFlagsGPU = 4,
WslaFeatureFlagsVirtioFs = 8,
} WSLAFeatureFlags;
struct WSLA_SESSION_SETTINGS {
@ -261,7 +262,7 @@ struct WSLA_SESSION_SETTINGS {
ULONG DmesgOutput;
// Below options are used for debugging purposes only.
[unique] LPCWSTR RootVhdOverride;
[unique] LPCWSTR RootVhdOverride;
[unique] LPCSTR RootVhdTypeOverride;
};

View File

@ -276,7 +276,7 @@ public:
SKIP_TEST_ARM64();
TerminateDistribution();
WslKeepAlive keelAlive;
WslKeepAlive keepAlive;
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode);
}
@ -288,7 +288,7 @@ public:
SKIP_TEST_ARM64();
TerminateDistribution();
WslKeepAlive keelAlive;
WslKeepAlive keepAlive;
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, Mode);
}
@ -302,7 +302,7 @@ public:
TerminateDistribution();
const auto nonElevatedToken = GetNonElevatedToken();
WslKeepAlive keelAlive(nonElevatedToken.get());
WslKeepAlive keepAlive(nonElevatedToken.get());
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode);
}
@ -316,11 +316,37 @@ public:
TerminateDistribution();
const auto nonElevatedToken = GetNonElevatedToken();
WslKeepAlive keelAlive(nonElevatedToken.get());
WslKeepAlive keepAlive(nonElevatedToken.get());
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, Mode);
}
void DrvfsMountElevatedSystemDistroEnabled(DrvFsMode Mode)
{
WSL2_TEST_ONLY();
WINDOWS_11_TEST_ONLY(); // TODO: Enable on Windows 10 when virtio support is added
SKIP_TEST_ARM64();
WslConfigChange config(LxssGenerateTestConfig({.guiApplications = true, .drvFsMode = Mode}));
WslKeepAlive keepAlive;
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode);
}
void DrvfsMountNonElevatedSystemDistroEnabled(DrvFsMode Mode)
{
WSL2_TEST_ONLY();
WINDOWS_11_TEST_ONLY(); // TODO: Enable on Windows 10 when virtio support is added
SKIP_TEST_ARM64();
WslConfigChange config(LxssGenerateTestConfig({.guiApplications = true, .drvFsMode = Mode}));
const auto nonElevatedToken = GetNonElevatedToken();
WslKeepAlive keepAlive(nonElevatedToken.get());
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode);
}
static void XattrDrvFs(DrvFsMode Mode)
{
SKIP_TEST_ARM64();
@ -946,6 +972,31 @@ private:
const auto nonElevatedToken = GetNonElevatedToken();
validate(nonElevatedType, nonElevatedToken.get());
// Elevated token should be able to create files at the root of the drive (/mnt/c)
{
const auto commandLine =
LxssGenerateWslCommandLine(L"touch /mnt/c/elevated_test_file.tmp && rm /mnt/c/elevated_test_file.tmp");
wsl::windows::common::SubProcess process(nullptr, commandLine.c_str(), CreateProcessFlags);
process.SetToken(nullptr);
process.SetShowWindow(SW_HIDE);
const auto output = process.RunAndCaptureOutput();
VERIFY_ARE_EQUAL(0, output.ExitCode, L"Elevated token should be able to create files at /mnt/c");
}
// Non-elevated token should NOT be able to create files at the root of the drive (/mnt/c)
{
const auto commandLine = LxssGenerateWslCommandLine(L"touch /mnt/c/nonelevated_test_file.tmp");
wsl::windows::common::SubProcess process(nullptr, commandLine.c_str(), CreateProcessFlags);
process.SetToken(nonElevatedToken.get());
process.SetShowWindow(SW_HIDE);
const auto output = process.RunAndCaptureOutput();
VERIFY_ARE_NOT_EQUAL(0, output.ExitCode, L"Non-elevated token should NOT be able to create files at /mnt/c (C:\\)");
}
}
static VOID VerifyDrvFsSymlink(const std::wstring& Path, const std::wstring& ExpectedTarget, bool Directory)
@ -1198,6 +1249,18 @@ class WSL1 : public DrvFsTests
WSL2_TEST_ONLY(); \
DrvFsTests::DrvfsMountNonElevatedDifferentConsole(DrvFsMode::##_mode##); \
} \
\
TEST_METHOD(DrvfsMountElevatedSystemDistroEnabled) \
{ \
WSL2_TEST_ONLY(); \
DrvFsTests::DrvfsMountElevatedSystemDistroEnabled(DrvFsMode::##_mode##); \
} \
\
TEST_METHOD(DrvfsMountNonElevatedSystemDistroEnabled) \
{ \
WSL2_TEST_ONLY(); \
DrvFsTests::DrvfsMountNonElevatedSystemDistroEnabled(DrvFsMode::##_mode##); \
} \
\
TEST_METHOD(XattrDrvFs) \
{ \

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,8 @@ using wsl::windows::common::relay::WriteHandle;
DEFINE_ENUM_FLAG_OPERATORS(WSLAFeatureFlags);
static std::filesystem::path storagePath;
class WSLATests
{
WSL_TEST_CLASS(WSLATests)
@ -46,6 +48,7 @@ class WSLATests
auto vhdPath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L"BasePath");
testVhd = std::filesystem::path{vhdPath} / "ext4.vhdx";
storagePath = std::filesystem::current_path() / "test-storage";
WslShutdown();
return true;
@ -53,6 +56,16 @@ class WSLATests
TEST_CLASS_CLEANUP(TestClassCleanup)
{
if (!storagePath.empty())
{
std::error_code error;
std::filesystem::remove_all(storagePath, error);
if (error)
{
LogError("Failed to cleanup storage path %ws: %hs", storagePath.c_str(), error.message().c_str());
}
}
return true;
}
@ -63,6 +76,9 @@ class WSLATests
settings.CpuCount = 4;
settings.MemoryMb = 2024;
settings.BootTimeoutMs = 30 * 1000;
settings.StoragePath = storagePath.c_str();
settings.MaximumStorageSizeMb = 1000; // 1GB.
return settings;
}
@ -182,6 +198,7 @@ class WSLATests
WSL2_TEST_ONLY();
auto settings = GetDefaultSessionSettings();
settings.StoragePath = nullptr;
settings.DisplayName = L"wsla-test-list";
wil::com_ptr<IWSLAUserSession> userSession;
@ -209,6 +226,7 @@ class WSLATests
WSL2_TEST_ONLY();
auto settings = GetDefaultSessionSettings();
settings.StoragePath = nullptr;
settings.DisplayName = L"wsla-open-by-name-test";
wil::com_ptr<IWSLAUserSession> userSession;
@ -395,9 +413,9 @@ class WSLATests
};
// Expect the shell prompt to be displayed
validateTtyOutput("/ #");
validateTtyOutput("\033[?2004hsh-5.2# ");
writeTty("echo OK\n");
validateTtyOutput(" echo OK\r\nOK");
validateTtyOutput("echo OK\r\n\033[?2004l\rOK");
// Exit the shell
writeTty("exit\n");
@ -543,7 +561,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], "cat: write error: Bad file descriptor\n");
VERIFY_ARE_EQUAL(result.Output[2], "/bin/cat: write error: Bad file descriptor\n");
VERIFY_ARE_EQUAL(result.Code, 1);
}
@ -551,7 +569,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], "cat: read error: Bad file descriptor\n");
VERIFY_ARE_EQUAL(result.Output[2], "/bin/cat: standard output: Bad file descriptor\n");
VERIFY_ARE_EQUAL(result.Code, 1);
}
}
@ -711,16 +729,29 @@ class WSLATests
StopWslaService();
}
TEST_METHOD(WindowsMounts)
void ValidateWindowsMounts(bool enableVirtioFs)
{
WSL2_TEST_ONLY();
auto settings = GetDefaultSessionSettings();
WI_SetFlagIf(settings.FeatureFlags, WslaFeatureFlagsVirtioFs, enableVirtioFs);
auto session = CreateSession();
auto session = CreateSession(settings);
wil::com_ptr<IWSLAVirtualMachine> vm;
VERIFY_SUCCEEDED(session->GetVirtualMachine(&vm));
wsl::windows::common::security::ConfigureForCOMImpersonation(vm.get());
auto expectedMountOptions = [&](bool readOnly) -> std::string {
if (enableVirtioFs)
{
return std::format("/win-path*virtiofs*{},relatime*", readOnly ? "ro" : "rw");
}
else
{
return std::format(
"/win-path*9p*{},relatime,aname=*,cache=5,access=client,msize=65536,trans=fd,rfd=*,wfd=*", readOnly ? "ro" : "rw");
}
};
auto expectMount = [&](const std::string& target, const std::optional<std::string>& options) {
auto cmd = std::format("set -o pipefail ; findmnt '{}' | tail -n 1", target);
@ -749,7 +780,7 @@ class WSLATests
// Validate writeable mount.
{
VERIFY_SUCCEEDED(vm->MountWindowsFolder(testFolder.c_str(), "/win-path", false));
expectMount("/win-path", "/win-path*9p*rw,relatime,aname=*,cache=5,access=client,msize=65536,trans=fd,rfd=*,wfd=*");
expectMount("/win-path", expectedMountOptions(false));
// Validate that mount can't be stacked on each other
VERIFY_ARE_EQUAL(vm->MountWindowsFolder(testFolder.c_str(), "/win-path", false), HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS));
@ -765,7 +796,7 @@ class WSLATests
// Validate read-only mount.
{
VERIFY_SUCCEEDED(vm->MountWindowsFolder(testFolder.c_str(), "/win-path", true));
expectMount("/win-path", "/win-path*9p*rw,relatime,aname=*,cache=5,access=client,msize=65536,trans=fd,rfd=*,wfd=*");
expectMount("/win-path", expectedMountOptions(true));
// Validate that folder is not writeable from linux
ExpectCommandResult(session.get(), {"/bin/sh", "-c", "echo -n content > /win-path/file.txt"}, 1);
@ -783,13 +814,25 @@ class WSLATests
// Validate that folders that are manually unmounted from the guest are handled properly
VERIFY_SUCCEEDED(vm->MountWindowsFolder(testFolder.c_str(), "/win-path", true));
expectMount("/win-path", "/win-path*9p*rw,relatime,aname=*,cache=5,access=client,msize=65536,trans=fd,rfd=*,wfd=*");
expectMount("/win-path", expectedMountOptions(true));
ExpectCommandResult(session.get(), {"/usr/bin/umount", "/win-path"}, 0);
VERIFY_SUCCEEDED(vm->UnmountWindowsFolder("/win-path"));
}
}
TEST_METHOD(WindowsMounts)
{
WSL2_TEST_ONLY();
ValidateWindowsMounts(false);
}
TEST_METHOD(WindowsMountsVirtioFs)
{
WSL2_TEST_ONLY();
ValidateWindowsMounts(true);
}
// This test case validates that no file descriptors are leaked to user processes.
TEST_METHOD(Fd)
{
@ -800,7 +843,7 @@ class WSLATests
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\n"))
if (!PathMatchSpecA(result.Output[1].c_str(), "/proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2\nsocket:*\nsocket:*"))
{
LogInfo("Found additional fds: %hs", result.Output[1].c_str());
VERIFY_FAIL();
@ -816,9 +859,6 @@ class WSLATests
auto session = CreateSession(settings);
wil::com_ptr<IWSLAVirtualMachine> vm;
VERIFY_SUCCEEDED(session->GetVirtualMachine(&vm));
// Validate that the GPU device is available.
ExpectCommandResult(session.get(), {"/bin/sh", "-c", "test -c /dev/dxg"}, 0);
auto expectMount = [&](const std::string& target, const std::optional<std::string>& options) {
@ -852,6 +892,8 @@ class WSLATests
// Validate that trying to mount the shares without GPU support disabled fails.
{
session.reset(); // Required to close the storage VHD.
WI_ClearFlag(settings.FeatureFlags, WslaFeatureFlagsGPU);
session = CreateSession(settings);
@ -1039,7 +1081,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_busybox-11.dmp", processId);
std::string expectedPattern = std::format("wsl-crash-*-{}-_usr_bin_cat-11.dmp", processId);
auto dumpFile = wsl::shared::retry::RetryWithTimeout<std::filesystem::path>(
[crashDumpsDir, expectedPattern, existingDumps]() {
@ -1101,22 +1143,8 @@ class WSLATests
WSL2_TEST_ONLY();
SKIP_TEST_ARM64();
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: %hs", storagePath.c_str(), error.message().c_str());
}
});
auto settings = GetDefaultSessionSettings();
settings.NetworkingMode = WSLANetworkingModeNAT;
settings.StoragePath = storagePath.c_str();
settings.MaximumStorageSizeMb = 1024;
auto session = CreateSession(settings);
@ -1210,22 +1238,8 @@ class WSLATests
WSL2_TEST_ONLY();
SKIP_TEST_ARM64();
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: %hs", storagePath.c_str(), error.message().c_str());
}
});
auto settings = GetDefaultSessionSettings();
settings.NetworkingMode = WSLANetworkingModeNAT;
settings.StoragePath = storagePath.c_str();
settings.MaximumStorageSizeMb = 1024;
auto session = CreateSession(settings);
@ -1566,4 +1580,81 @@ class WSLATests
}
*/
}
TEST_METHOD(Exec)
{
WSL2_TEST_ONLY();
SKIP_TEST_ARM64();
auto settings = GetDefaultSessionSettings();
settings.NetworkingMode = WSLANetworkingModeNAT;
auto session = CreateSession(settings);
// Create a container.
WSLAContainerLauncher launcher(
"debian:latest", "test-container-exec", {}, {"sleep", "99999"}, {}, ProcessFlags::Stdout | ProcessFlags::Stderr);
auto container = launcher.Launch(*session);
// Simple exec case.
{
auto process =
WSLAProcessLauncher("/bin/echo", {"echo", "OK"}, {}, ProcessFlags::Stdout | ProcessFlags::Stderr).Launch(container.Get());
ValidateProcessOutput(process, {{1, "OK\n"}});
}
// Validate that stdin is correctly wired.
// TODO: Add test coverage for stdin being closed without anything written to it once the stdin hang issue is solved.
{
auto process = WSLAProcessLauncher({}, {"/bin/cat"}, {}, ProcessFlags::Stdin | ProcessFlags::Stdout | ProcessFlags::Stderr)
.Launch(container.Get());
std::string shellInput = "foo";
std::vector<char> inputBuffer{shellInput.begin(), shellInput.end()};
std::unique_ptr<OverlappedIOHandle> writeStdin(new WriteHandle(process.GetStdHandle(0), 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");
VERIFY_ARE_EQUAL(result.Code, 0);
}
// Validate that environmnent is correctly wired.
{
auto process =
WSLAProcessLauncher({}, {"/bin/sh", "-c", "echo $testenv"}, {{"testenv=testvalue"}}, ProcessFlags::Stdout | ProcessFlags::Stderr)
.Launch(container.Get());
ValidateProcessOutput(process, {{1, "testvalue\n"}});
}
// Validate that an exec'd command returns when the container is stopped.
{
auto process = WSLAProcessLauncher({}, {"/bin/cat"}, {}, ProcessFlags::Stdin | ProcessFlags::Stdout | ProcessFlags::Stderr)
.Launch(container.Get());
VERIFY_SUCCEEDED(container.Get().Stop(9, 0));
ExpectCommandResult(session.get(), {"/usr/bin/nerdctl", "stop", "-t", "0", "test-container-exec"}, 0);
auto result = process.WaitAndCaptureOutput();
VERIFY_ARE_EQUAL(result.Code, 1);
}
// Validate error paths
{
// Validate that processes can't be launched in stopped containers.
auto [result, _, __] = WSLAProcessLauncher({}, {"/bin/cat"}).LaunchNoThrow(container.Get());
VERIFY_ARE_EQUAL(result, HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
// TODO: Implement proper handling of executables that don't exist in the container.
}
}
};

View File

@ -71,23 +71,41 @@ if ($Package) {
try {
if ($AllowUnsigned)
{
# unfortunately -AllowUnsigned isn't supported on vb so we need to manually import the certificate and trust it.
(Get-AuthenticodeSignature $Package).SignerCertificate | Export-Certificate -FilePath private-wsl.cert | Out-Null
try
{
Import-Certificate -FilePath .\private-wsl.cert -CertStoreLocation Cert:\LocalMachine\Root | Out-Null
# Try to add with -AllowUnsigned first (supported in newer PowerShell)
try {
Add-AppxPackage $Package -AllowUnsigned -ErrorAction Stop
}
finally
{
Remove-Item -Path .\private-wsl.cert
catch {
# Fallback: manually import the certificate and trust it
Write-Host "Attempting to import package certificate..."
$signature = Get-AuthenticodeSignature -LiteralPath $Package
if (-not $signature.SignerCertificate) {
Write-Error "Package is not signed or has no certificate. Cannot import certificate."
exit 1
}
$cert = $signature.SignerCertificate
$certPath = Join-Path $env:TEMP "wsl-package-cert.cer"
try {
$cert | Export-Certificate -FilePath $certPath | Out-Null
Import-Certificate -FilePath $certPath -CertStoreLocation Cert:\LocalMachine\Root | Out-Null
Write-Host "Certificate imported successfully. Retrying package installation..."
}
finally {
Remove-Item -Path $certPath -ErrorAction SilentlyContinue
}
# Retry installation after importing certificate
Add-AppxPackage $Package -ErrorAction Stop
}
}
Add-AppxPackage $Package
else {
Add-AppxPackage $Package -ErrorAction Stop
}
}
catch {
Write-Host $_
Get-AppPackageLog -All
Write-Host "Error installing package: $_"
Get-AppPackageLog | Select-Object -First 64 | Format-List
exit 1
}
}

View File

@ -1,92 +0,0 @@
<?xml version="1.0"?>
<WindowsPerformanceRecorder Version="1">
<Profiles>
<EventCollector Id="Collector" Name="Collector">
<BufferSize Value="256"/>
<Buffers Value="1024"/>
</EventCollector>
<EventProvider Id="lxcore_kernel" Name="0CD1C309-0878-4515-83DB-749843B3F5C9"/>
<EventProvider Id="lxcore_user" Name="D90B9468-67F0-5B3B-42CC-82AC81FFD960"/>
<EventProvider Id="lxcore_service" Name="B99CDB5A-039C-5046-E672-1A0DE0A40211"/>
<EventProvider Id="vm_chipset" Name="de9ba731-7f33-4f44-98c9-6cac856b9f83"/>
<EventProvider Id="vmcompute_dll" Name="AF7FD3A7-B248-460C-A9F5-FEC39EF8468C"/>
<EventProvider Id="vmcompute" Name="17103E3F-3C6E-4677-BB17-3B267EB5BE57"/>
<EventProvider Id="vmmm" Name="6066F867-7CA1-4418-85FD-36E3F9C0600C"/>
<EventProvider Id="vmwp" Name="51DDFA29-D5C8-4803-BE4B-2ECB715570FE"/>
<EventProvider Id="9p" Name="e13c8d52-b153-571f-78c5-1d4098af2a1e"/>
<EventProvider Id="p9rdr" Name="bb1d36f0-e0e0-48cc-9493-fef0e3d0b28c" NonPagedMemory="true" Strict="true"/>
<EventProvider Id="mup" Name="20c46239-d059-4214-a11e-7d6769cbe020" />
<EventProvider Id="rfsmon" Name="51734B23-5B7E-4892-BA8E-45BC110B735C" />
<EventProvider Id="hyperv_storage" Name="c7ad62c6-5c99-5a1b-bbc4-0821ae5b765e" />
<EventProvider Id="hns" Name="0c885e0d-6eb6-476c-a048-2457eed3a5c1" />
<EventProvider Id="wsl_devicehost" Name="9d6c7b9e-2581-4d8a-b8c5-b90b4a17094a"/>
<EventProvider Id="vfpext" Name="9F2660EA-CFE7-428F-9850-AECA612619B0" />
<EventProvider Id="EventProvider_Microsoft.Windows.Mobile.Provisioning.AppDownload" Name="0BBE6221-EF09-4A3F-82EE-BE00DBB6A98A" />
<EventProvider Id="EventProvider_Microsoft.Windows.Mobile.Provisioning.Datastore" Name="42C60CEA-0FE7-4541-A86B-9E11F95BD9BF" />
<EventProvider Id="EventProvider_Microsoft.Windows.Mobile.Provisioning.PhoneProvisioner" Name="B876B1FC-C7F1-443E-9012-86677F7DE580" />
<EventProvider Id="EventProvider_Microsoft.Windows.Mobile.Provisioning.PPOEM" Name="7EDBED09-1FF7-4FEE-B8C3-5DB694420830" />
<EventProvider Id="EventProvider_Microsoft.Windows.Provisioning.API" Name="82ADD491-01D7-4B85-9EAD-192C3CAACA23" />
<EventProvider Id="EventProvider_Microsoft.Windows.Provisioning.CSP" Name="16E12400-A2D8-44B7-9479-004568EC7819" />
<EventProvider Id="EventProvider_Microsoft.Windows.Provisioning.Engine" Name="A6A847B7-4429-49AA-BBA6-2AD8C191AC8C" />
<EventProvider Id="EventProvider_Microsoft.Windows.Provisioning.Handlers" Name="0383D92C-2337-4F25-A0B5-A51767F04746" />
<EventProvider Id="EventProvider_Microsoft.Windows.Provisioning.Migration" Name="A0AF985E-83F9-4E1A-B658-338DCFE27893" />
<EventProvider Id="EventProvider_Microsoft.Windows.Provisioning.Operations" Name="7F99598F-B2C1-4371-9911-63DEE13B9EB1" />
<EventProvider Id="EventProvider_Microsoft.Windows.Provisioning.Platform" Name="B1F30020-8BC3-4888-BB1B-4DD681F24209" />
<EventProvider Id="EventProvider_Microsoft.Windows.Provisioning.Plugin.Engine" Name="55239D60-0EB6-495B-874E-15DE5D5F9A70" />
<EventProvider Id="EventProvider_Microsoft.Windows.Provisioning.Plugin.RemovableMedia" Name="B55883E6-6C45-45C2-AB9D-800BB7B66B13" />
<EventProvider Id="EventProvider_Microsoft.Windows.Provisioning.ProvTool" Name="2BF4B6BA-556E-4D05-8534-CAFEDF19FED8" />
<EventProvider Id="EventProvider_Microsoft.Windows.Provisioning.CommandCsp" Name="00BB69FC-60BC-4502-9438-25608F375CCB" />
<EventProvider Id="EventProvider_Microsoft.Windows.Provisioning.ProvLaunch" Name="08FACCFA-125D-4ED6-B0B7-B4A1A912E693" />
<EventProvider Id="EventProvider_Microsoft.Windows.EMPS.Enrollment" Name="E74EFD1A-B62D-4B83-AB00-66F4A166A2D3" />
<EventProvider Id="EventProvider_Microsoft.Windows.EnterpriseManagement.Enrollment" Name="F9E3B648-9AF1-4DC3-9A8E-BF42C0FBCE9A" />
<Profile
Id="WSL.Verbose.File"
Name="WSL"
Description="Traces for all WSL components"
LoggingMode="File"
DetailLevel="Verbose"
>
<Collectors>
<EventCollectorId Value="Collector">
<EventProviders>
<EventProviderId Value="lxcore_kernel"/>
<EventProviderId Value="lxcore_user"/>
<EventProviderId Value="lxcore_service"/>
<EventProviderId Value="vm_chipset"/>
<EventProviderId Value="vmcompute_dll"/>
<EventProviderId Value="vmcompute"/>
<EventProviderId Value="vmmm"/>
<EventProviderId Value="vmwp"/>
<EventProviderId Value="9p"/>
<EventProviderId Value="p9rdr"/>
<EventProviderId Value="mup"/>
<EventProviderId Value="rfsmon"/>
<EventProviderId Value="hyperv_storage"/>
<EventProviderId Value="hns"/>
<EventProviderId Value="wsl_devicehost"/>
<EventProviderId Value="vfpext"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.Mobile.Provisioning.AppDownload"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.Mobile.Provisioning.Datastore"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.Mobile.Provisioning.PhoneProvisioner"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.Mobile.Provisioning.PPOEM"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.Provisioning.CSP"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.Provisioning.Engine"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.Provisioning.Migration"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.Provisioning.Platform"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.Provisioning.Operations"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.Provisioning.Plugin.Engine"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.Provisioning.Plugin.RemovableMedia"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.Provisioning.ProvTool"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.Provisioning.Operations"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.Provisioning.CommandCsp"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.Provisioning.ProvLaunch"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.EMPS.Enrollment"/>
<EventProviderId Value="EventProvider_Microsoft.Windows.EnterpriseManagement.Enrollment"/>
</EventProviders>
</EventCollectorId>
</Collectors>
</Profile>
</Profiles>
</WindowsPerformanceRecorder>