Compare commits

...

15 Commits

Author SHA1 Message Date
Ben Hillis c5fd672847
test: add more path translation variations (#13927)
* test: add more path translation variations

* Update test/windows/SimpleTests.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
2025-12-16 18:53:04 -08:00
Ben Hillis d0609393e4
[GH 13837] Remove trailing slash from $XDG_RUNTIME_DIR (#13929)
* [GH 13837] Remove trailing slash from $XDG_RUNTIME_DIR

* pr feedback

---------

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
2025-12-16 18:52:15 -08:00
Blue 1e3ef15f6d
Remove the explicit desktop field when creating a Windows process (#13930) 2025-12-16 18:49:22 -08:00
Ben Hillis f02f8ef91c
cleanup: remove duplicate AppId registration for wsldevicehost.dll (#13928)
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
2025-12-16 16:30:46 -08:00
Ben Hillis d48d374c8c
test: extend networking test coverage (#13914)
* test: extend coverage of virtioproxy networking mode

* test: add dns test variations to all networking classes

* remove bridged dns variations

* pr feedback

---------

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
2025-12-16 14:20:26 -08:00
Ben Hillis c6ac7433b1
Fix minor typo in UserConfig.cmake.sample (.exe instead of .dll) (#13926)
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
2025-12-16 11:37:03 -08:00
Ben Hillis e24df7e0d1
Resolve issue with buttons on notifications not working correctly (#13921)
* Resolve issue with buttons on notifications not working correctly

* pr feedback

---------

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
2025-12-15 20:50:49 -08:00
Ben Hillis 180d811099
Resolve issue with config file writing sections outside of their expected header. (#13898)
* Resolve issue with config file writing sections outside of their expected header.

* add more writewslconfig variations

* formatting

---------

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
2025-12-15 13:03:02 -08:00
Blue 9ed5c130f4
Localization change from build: 135812430 (#13905)
* Localization change from build: 135812430

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* remove unused string

---------

Co-authored-by: WSL localization <noreply@microsoft.com>
Co-authored-by: Ben Hillis <benhillis@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
2025-12-15 13:36:56 -05:00
Ben Hillis b4c0ced6b8
cleanup: minor tweaks to path translation failed messages (#13892)
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
2025-12-13 10:59:55 -08:00
Ben Hillis f1e20b21c9
Clean up localhost relay implementation to not rely on procfs parsing. (#13836)
* Clean up localhost relay implementation to not rely on procfs parsing.

* pr feedback

---------

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
2025-12-12 14:18:07 -08:00
Ben Hillis 7f8422654e
test: fix and issue with the test-setup.ps1 script that was preventing it from being run from some version of powershell (#13834)
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
2025-12-12 14:16:47 -08:00
Blue 00b76b1d4b
Notice change from build: 135617549 (#13879)
Co-authored-by: WSL notice <noreply@microsoft.com>
2025-12-12 10:46:46 -08:00
Ben Hillis 8c166d7383
cleanup: remove stale version of wsl.wprp (#13861)
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
2025-12-11 15:37:57 -08:00
Ben Hillis 19f06f9f51
virtiofs: fix an issue where if the VM is launched by an elevated user, non-elevated shells will have elevated virtiofs access. (#13877)
* virtiofs: fix an issue where if the VM is launched by an elevated user, non-elevated shells will have elevated virtiofs access.

* rename keelAlive -> keepAlive

---------

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
2025-12-11 15:21:27 -08:00
50 changed files with 838 additions and 375 deletions

View File

@ -66,12 +66,30 @@ Build parameters:
## Testing ## Testing
### Unit Tests (Windows Only - TAEF Framework) ### Unit Tests (Windows Only - TAEF Framework)
**CRITICAL: ALWAYS build the ENTIRE project before running tests:**
```powershell
# Build everything first - this is required!
cmake --build . -- -m
# Then run tests
bin\<platform>\<target>\test.bat
```
**Why full build is required:**
- Tests depend on multiple components (libwsl.dll, wsltests.dll, wslservice.exe, etc.)
- Partial builds (e.g., only `configfile` or `wsltests`) will cause test failures
- Changed components must be built together to ensure compatibility
- **DO NOT skip the full build step even if only one file changed**
Test execution:
- Run all tests: `bin\<platform>\<target>\test.bat` - Run all tests: `bin\<platform>\<target>\test.bat`
- **NEVER CANCEL: Full test suite takes 30-60 minutes. Set timeout to 90+ minutes.** - **NEVER CANCEL: Full test suite takes 30-60 minutes. Set timeout to 90+ minutes.**
- Run subset: `bin\<platform>\<target>\test.bat /name:*UnitTest*` - Run subset: `bin\<platform>\<target>\test.bat /name:*UnitTest*`
- Run specific test: `bin\<platform>\<target>\test.bat /name:<class>::<test>` - Run specific test: `bin\<platform>\<target>\test.bat /name:<class>::<test>`
- WSL1 tests: Add `-Version 1` flag - WSL1 tests: Add `-Version 1` flag
- Fast mode (after first run): Add `-f` flag (requires `wsl --set-default test_distro`) - Fast mode (after first run): Add `-f` flag (requires `wsl --set-default test_distro`)
- **Requires Administrator privileges** - test.bat will fail without admin rights
Test debugging: Test debugging:
- Wait for debugger: `/waitfordebugger` - Wait for debugger: `/waitfordebugger`

View File

@ -394,7 +394,7 @@ stages:
Move-Item -Path "bin\x64\cloudtest" -Destination "$(ob_outputDirectory)\testbin\x64\cloudtest" 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\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\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 "test\linux\unit_tests" -Destination "$(ob_outputDirectory)\testbin\unit_tests"
Move-Item -Path bundle\release\* -Destination $(ob_outputDirectory)\bundle Move-Item -Path bundle\release\* -Destination $(ob_outputDirectory)\bundle

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 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) The MIT License (MIT)

View File

@ -16,7 +16,7 @@ message(STATUS "Loading user configuration")
# file(CREATE_LINK "${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}/rdpnanoTransport.dll" "${WSL_DEV_BINARY_PATH}/rdpnanoTransport.dll" SYMBOLIC) # file(CREATE_LINK "${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}/rdpnanoTransport.dll" "${WSL_DEV_BINARY_PATH}/rdpnanoTransport.dll" SYMBOLIC)
# file(CREATE_LINK "${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}/RdpWinStlHelper.dll" "${WSL_DEV_BINARY_PATH}/RdpWinStlHelper.dll" SYMBOLIC) # file(CREATE_LINK "${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}/RdpWinStlHelper.dll" "${WSL_DEV_BINARY_PATH}/RdpWinStlHelper.dll" SYMBOLIC)
# file(CREATE_LINK "${MSAL_SOURCE_DIR}/${TARGET_PLATFORM}/msal.wsl.proxy.exe" "${WSL_DEV_BINARY_PATH}/msal.wsl.proxy.exe" SYMBOLIC) # file(CREATE_LINK "${MSAL_SOURCE_DIR}/${TARGET_PLATFORM}/msal.wsl.proxy.exe" "${WSL_DEV_BINARY_PATH}/msal.wsl.proxy.exe" SYMBOLIC)
# file(CREATE_LINK "${BIN}/wsldevicehost.dll" "${WSL_DEV_BINARY_PATH}/wsldevicehost.exe" SYMBOLIC) # file(CREATE_LINK "${BIN}/wsldevicehost.dll" "${WSL_DEV_BINARY_PATH}/wsldevicehost.dll" SYMBOLIC)
# file(CREATE_LINK "${DIRECT3D_SOURCE_DIR}/lib/${TARGET_PLATFORM}" "${WSL_DEV_BINARY_PATH}/lib" SYMBOLIC) # file(CREATE_LINK "${DIRECT3D_SOURCE_DIR}/lib/${TARGET_PLATFORM}" "${WSL_DEV_BINARY_PATH}/lib" SYMBOLIC)
# foreach(LANG ${SUPPORTED_LANGS}) # foreach(LANG ${SUPPORTED_LANGS})

View File

@ -12,6 +12,7 @@
<EventProvider Id="wsl_devicehost" Name="9d6c7b9e-2581-4d8a-b8c5-b90b4a17094a"/> <EventProvider Id="wsl_devicehost" Name="9d6c7b9e-2581-4d8a-b8c5-b90b4a17094a"/>
<EventProvider Id="wslclient" Name="8cbb7724-7223-5d6f-8137-564dac45104d"/> <EventProvider Id="wslclient" Name="8cbb7724-7223-5d6f-8137-564dac45104d"/>
<EventProvider Id="wslapi" Name="beb94edf-1a7b-5058-0696-ff9e6b1798d1"/> <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="vm_chipset" Name="de9ba731-7f33-4f44-98c9-6cac856b9f83"/>
<EventProvider Id="vmcompute_dll" Name="AF7FD3A7-B248-460C-A9F5-FEC39EF8468C"/> <EventProvider Id="vmcompute_dll" Name="AF7FD3A7-B248-460C-A9F5-FEC39EF8468C"/>
<EventProvider Id="vmcompute" Name="17103E3F-3C6E-4677-BB17-3B267EB5BE57"/> <EventProvider Id="vmcompute" Name="17103E3F-3C6E-4677-BB17-3B267EB5BE57"/>
@ -19,7 +20,7 @@
<EventProvider Id="vmwp" Name="51DDFA29-D5C8-4803-BE4B-2ECB715570FE"/> <EventProvider Id="vmwp" Name="51DDFA29-D5C8-4803-BE4B-2ECB715570FE"/>
<EventProvider Id="9p" Name="e13c8d52-b153-571f-78c5-1d4098af2a1e"/> <EventProvider Id="9p" Name="e13c8d52-b153-571f-78c5-1d4098af2a1e"/>
<EventProvider Id="9p_errors" Name="06C601B3-6957-4F8C-A15F-74875B24429D" /> <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="mup" Name="20c46239-d059-4214-a11e-7d6769cbe020" />
<EventProvider Id="rfsmon" Name="51734B23-5B7E-4892-BA8E-45BC110B735C" /> <EventProvider Id="rfsmon" Name="51734B23-5B7E-4892-BA8E-45BC110B735C" />
<EventProvider Id="hyperv_storage" Name="c7ad62c6-5c99-5a1b-bbc4-0821ae5b765e" /> <EventProvider Id="hyperv_storage" Name="c7ad62c6-5c99-5a1b-bbc4-0821ae5b765e" />
@ -115,6 +116,7 @@
<EventProviderId Value="wsl_devicehost"/> <EventProviderId Value="wsl_devicehost"/>
<EventProviderId Value="wslclient"/> <EventProviderId Value="wslclient"/>
<EventProviderId Value="wslapi"/> <EventProviderId Value="wslapi"/>
<EventProviderId Value="vfpext"/>
<EventProviderId Value="vm_chipset"/> <EventProviderId Value="vm_chipset"/>
<EventProviderId Value="vmcompute_dll"/> <EventProviderId Value="vmcompute_dll"/>
<EventProviderId Value="vmcompute"/> <EventProviderId Value="vmcompute"/>

View File

@ -730,9 +730,6 @@ Může být nutné restartovat systém, aby se změny projevily.</value>
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Změna velikosti disku se nezdařila.</value> <value>Změna velikosti disku se nezdařila.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Cestu se nepodařilo přeložit.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>Nebyla nalezena žádná hodnota.</value> <value>Nebyla nalezena žádná hodnota.</value>
</data> </data>
@ -1060,7 +1057,7 @@ Návrat k sítím NAT.</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment> <comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
</data> </data>
<data name="MessagePassVhdFlag" xml:space="preserve"> <data name="MessagePassVhdFlag" xml:space="preserve">
<value>This looks like a VHD file. Use --vhd to import a VHD instead of a tar.</value> <value>Vypadá to jako soubor VHD. K importu virtuálního pevného disku místo tar použijte --vhd </value>
<comment>{Locked="--vhd "}Command line arguments, file names and string inserts should not be translated</comment> <comment>{Locked="--vhd "}Command line arguments, file names and string inserts should not be translated</comment>
</data> </data>
<data name="MessageDistroStoreInstallFailed" xml:space="preserve"> <data name="MessageDistroStoreInstallFailed" xml:space="preserve">

View File

@ -730,9 +730,6 @@ Systemet skal muligvis genstartes, så ændringerne kan træde i kraft.</value>
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Diskens størrelsen kunne ikke tilpasses.</value> <value>Diskens størrelsen kunne ikke tilpasses.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Det mislykkedes at oversætte stien.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>Der blev ikke fundet nogen værdi.</value> <value>Der blev ikke fundet nogen værdi.</value>
</data> </data>

View File

@ -736,9 +736,6 @@ Das System muss möglicherweise neu gestartet werden, damit die Änderungen wirk
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Fehler beim Ändern der Größe des Datenträgers.</value> <value>Fehler beim Ändern der Größe des Datenträgers.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Fehler beim Übersetzen des Pfads.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>Kein Wert gefunden.</value> <value>Kein Wert gefunden.</value>
</data> </data>

View File

@ -730,9 +730,6 @@ The system may need to be restarted so the changes can take effect.</value>
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Failed to resize disk.</value> <value>Failed to resize disk.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Failed to translate path.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>No value found.</value> <value>No value found.</value>
</data> </data>

View File

@ -730,9 +730,6 @@ The system may need to be restarted so the changes can take effect.</value>
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Failed to resize disk.</value> <value>Failed to resize disk.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Failed to translate path.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>No value found.</value> <value>No value found.</value>
</data> </data>

View File

@ -736,9 +736,6 @@ Es posible que sea necesario reiniciar el sistema para que los cambios surtan ef
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Error al cambiar el tamaño del disco.</value> <value>Error al cambiar el tamaño del disco.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>No se pudo traducir la ruta de acceso.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>No se encontró ningún valor.</value> <value>No se encontró ningún valor.</value>
</data> </data>

View File

@ -730,9 +730,6 @@ Järjestelmä on ehkä käynnistettävä uudelleen, jotta muutokset tulevat voim
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Levyn koon muuttaminen epäonnistui.</value> <value>Levyn koon muuttaminen epäonnistui.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Polun kääntäminen epäonnistui.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>Arvoa ei löytynyt.</value> <value>Arvoa ei löytynyt.</value>
</data> </data>

View File

@ -737,9 +737,6 @@ Le système devra peut-être être redémarré pour que les modifications prenne
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Échec de redimensionnement de disque.</value> <value>Échec de redimensionnement de disque.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Échec de la traduction du chemin daccès.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>Valeur non trouvée.</value> <value>Valeur non trouvée.</value>
</data> </data>

View File

@ -730,9 +730,6 @@ Lehet, hogy újra kell indítani a rendszert, hogy a módosítások érvénybe l
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Nem sikerült a lemez átméretezése.</value> <value>Nem sikerült a lemez átméretezése.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Az útvonal fordítása nem sikerült.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>Nem található érték.</value> <value>Nem található érték.</value>
</data> </data>

View File

@ -736,9 +736,6 @@ Potrebbe essere necessario riavviare il sistema per rendere effettive le modific
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Non è possibile ridimensionare il disco.</value> <value>Non è possibile ridimensionare il disco.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Non è stato possibile tradurre il percorso.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>Nessun valore trovato.</value> <value>Nessun valore trovato.</value>
</data> </data>

View File

@ -736,9 +736,6 @@ Windows バージョン: {}</value>
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>ディスクのサイズを変更できませんでした。</value> <value>ディスクのサイズを変更できませんでした。</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>パスを変換できませんでした。</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>値が見つかりません。</value> <value>値が見つかりません。</value>
</data> </data>

View File

@ -736,9 +736,6 @@ Windows 버전: {}</value>
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>디스크 크기를 조정하지 못했습니다.</value> <value>디스크 크기를 조정하지 못했습니다.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>경로를 변환하지 못했습니다.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>값을 찾을 수 없습니다.</value> <value>값을 찾을 수 없습니다.</value>
</data> </data>

View File

@ -730,9 +730,6 @@ Systemet må kanskje startes på nytt slik at endringene kan tre i kraft.</value
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Kan ikke endre størrelsen på disken.</value> <value>Kan ikke endre størrelsen på disken.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Kan ikke oversette banen.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>Finner ingen verdi.</value> <value>Finner ingen verdi.</value>
</data> </data>

View File

@ -730,9 +730,6 @@ Het systeem moet mogelijk opnieuw worden opgestart, zodat de wijzigingen van kra
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Kan het formaat van de schijf niet wijzigen.</value> <value>Kan het formaat van de schijf niet wijzigen.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Kan pad niet vertalen.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>Geen waarde gevonden.</value> <value>Geen waarde gevonden.</value>
</data> </data>

View File

@ -730,9 +730,6 @@ Może być konieczne ponowne uruchomienie systemu, aby zmiany zostały wprowadzo
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Nie można zmienić rozmiaru dysku.</value> <value>Nie można zmienić rozmiaru dysku.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Nie można przetłumaczyć ścieżki.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>Nie znaleziono wartości.</value> <value>Nie znaleziono wartości.</value>
</data> </data>
@ -1060,7 +1057,7 @@ Powrót do sieci NAT.</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment> <comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
</data> </data>
<data name="MessagePassVhdFlag" xml:space="preserve"> <data name="MessagePassVhdFlag" xml:space="preserve">
<value>This looks like a VHD file. Use --vhd to import a VHD instead of a tar.</value> <value>Wygląda to na plik VHD. Użyj polecenia --vhd aby zaimportować plik VHD zamiast tar.</value>
<comment>{Locked="--vhd "}Command line arguments, file names and string inserts should not be translated</comment> <comment>{Locked="--vhd "}Command line arguments, file names and string inserts should not be translated</comment>
</data> </data>
<data name="MessageDistroStoreInstallFailed" xml:space="preserve"> <data name="MessageDistroStoreInstallFailed" xml:space="preserve">

View File

@ -737,9 +737,6 @@ Talvez seja necessário reiniciar o sistema para que as alterações entrem em v
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Falha ao redimensionar o disco.</value> <value>Falha ao redimensionar o disco.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Falha ao converter o caminho.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>Nenhum valor encontrado.</value> <value>Nenhum valor encontrado.</value>
</data> </data>

View File

@ -730,9 +730,6 @@ O sistema poderá ter de ser reiniciado para que as alterações possam ter efei
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Falha ao redimensionar o disco.</value> <value>Falha ao redimensionar o disco.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Falha ao traduzir caminho.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>Não foi encontrado nenhum valor.</value> <value>Não foi encontrado nenhum valor.</value>
</data> </data>

View File

@ -737,9 +737,6 @@
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Не удалось изменить размер диска.</value> <value>Не удалось изменить размер диска.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Не удалось преобразовать путь.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>Значение не найдено.</value> <value>Значение не найдено.</value>
</data> </data>

View File

@ -730,9 +730,6 @@ Systemet kan behöva startas om så att ändringarna kan börja gälla.</value>
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Det gick inte att ändra storlek på disken.</value> <value>Det gick inte att ändra storlek på disken.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Det gick inte att översätta sökvägen.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>Inget värde hittades.</value> <value>Inget värde hittades.</value>
</data> </data>

View File

@ -730,9 +730,6 @@ Değişikliklerin etkili olması için sistemin yeniden başlatılması gerekebi
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>Disk yeniden boyutlandırılamadı.</value> <value>Disk yeniden boyutlandırılamadı.</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>Yol çevrilemedi.</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>Değer bulunamadı.</value> <value>Değer bulunamadı.</value>
</data> </data>

View File

@ -736,9 +736,6 @@ Windows: {}</value>
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>未能重设磁盘大小。</value> <value>未能重设磁盘大小。</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>无法转换路径。</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>找不到值。</value> <value>找不到值。</value>
</data> </data>

View File

@ -736,9 +736,6 @@ Windows 版本: {}</value>
<data name="MessageFailedToResizeDisk" xml:space="preserve"> <data name="MessageFailedToResizeDisk" xml:space="preserve">
<value>無法調整磁碟。</value> <value>無法調整磁碟。</value>
</data> </data>
<data name="MessageFailedToTranslatePath" xml:space="preserve">
<value>無法翻譯路徑。</value>
</data>
<data name="MessageNoValueFound" xml:space="preserve"> <data name="MessageNoValueFound" xml:space="preserve">
<value>找不到任何值。</value> <value>找不到任何值。</value>
</data> </data>

View File

@ -183,7 +183,7 @@
<!-- WslDeviceHost_VirtioFs (admin) --> <!-- WslDeviceHost_VirtioFs (admin) -->
<RegistryKey Root="HKCR" Key="CLSID\{7e6ad219-d1b3-42d5-b8ee-d96324e64ff6}"> <RegistryKey Root="HKCR" Key="CLSID\{7e6ad219-d1b3-42d5-b8ee-d96324e64ff6}">
<RegistryValue Value="WslDeviceHost_VirtioFs_Admin" Type="string"/> <RegistryValue Value="WslDeviceHost_VirtioFs_Admin" Type="string"/>
<RegistryValue Name="AppId" Value="{7F82AD86-755B-4870-86B1-D2E68DFE8A49}" Type="string"/> <RegistryValue Name="AppId" Value="{17696EAC-9568-4CF5-BB8C-82515AAD6C09}" Type="string"/>
<RegistryKey Key="InProcServer32"> <RegistryKey Key="InProcServer32">
<RegistryValue Value="[INSTALLDIR]wsldevicehost.dll" Type="string"/> <RegistryValue Value="[INSTALLDIR]wsldevicehost.dll" Type="string"/>
@ -191,15 +191,6 @@
</RegistryKey> </RegistryKey>
</RegistryKey> </RegistryKey>
<RegistryKey Root="HKCR" Key="AppID\{7F82AD86-755B-4870-86B1-D2E68DFE8A49}">
<RegistryValue Name="DllSurrogate" Value="" Type="string"/>
<RegistryValue Name="AppIDFlags" Value="2048" Type="integer"/><!--0x800-->
<!-- O:BAG:BAD:(A;;CCDCSW;;;AU)(A;;CCDCSW;;;PS)(A;;CCDCSW;;;SY) -->
<RegistryValue Name="AccessPermission" Value="01000480580000006800000000000000140000000200440003000000000014000B00000001010000000000050B000000000014000B00000001010000000000050A000000000014000B0000000101000000000005120000000102000000000005200000002002000001020000000000052000000020020000" Type="binary" />
<RegistryValue Name="LaunchPermission" Value="01000480580000006800000000000000140000000200440003000000000014000B00000001010000000000050B000000000014000B00000001010000000000050A000000000014000B0000000101000000000005120000000102000000000005200000002002000001020000000000052000000020020000" Type="binary" />
</RegistryKey>
<!-- WslDeviceHost_VirtioFs --> <!-- WslDeviceHost_VirtioFs -->
<RegistryKey Root="HKCR" Key="CLSID\{60285AE6-AAF3-4456-B444-A6C2D0DEDA38}"> <RegistryKey Root="HKCR" Key="CLSID\{60285AE6-AAF3-4456-B444-A6C2D0DEDA38}">
<RegistryValue Value="WslDeviceHost_VirtioFs" Type="string" /> <RegistryValue Value="WslDeviceHost_VirtioFs" Type="string" />

View File

@ -690,7 +690,7 @@ try
} }
Common->Environment.AddVariable("DBUS_SESSION_BUS_ADDRESS", std::format("unix:path=/run/user/{}/bus", PasswordEntry->pw_uid)); Common->Environment.AddVariable("DBUS_SESSION_BUS_ADDRESS", std::format("unix:path=/run/user/{}/bus", PasswordEntry->pw_uid));
Common->Environment.AddVariable(XDG_RUNTIME_DIR_ENV, std::format("/run/user/{}/", PasswordEntry->pw_uid)); Common->Environment.AddVariable(XDG_RUNTIME_DIR_ENV, std::format("/run/user/{}", PasswordEntry->pw_uid));
} }
// //

View File

@ -12,6 +12,8 @@
#include <netinet/ip.h> #include <netinet/ip.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/sock_diag.h>
#include <linux/inet_diag.h>
#include <lxwil.h> #include <lxwil.h>
#include <linux/if_tun.h> #include <linux/if_tun.h>
@ -21,6 +23,8 @@
#include "SecCompDispatcher.h" #include "SecCompDispatcher.h"
#include "seccomp_defs.h" #include "seccomp_defs.h"
#include "CommandLine.h" #include "CommandLine.h"
#include "NetlinkChannel.h"
#include "NetlinkTransactionError.h"
#define TCP_LISTEN 10 #define TCP_LISTEN 10
@ -145,79 +149,59 @@ void ListenThread(sockaddr_vm hvSocketAddress, int listenSocket)
return; return;
} }
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{}; std::vector<sockaddr_storage> sockets{};
while ((bytesRead = getline(&line, &lineLength, file)) != -1) try
{ {
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{}; sockaddr_storage sock{};
int index = 0;
int status = 0; if (payload->idiag_family == AF_INET)
for (char *sp, *field = strtok_r(line, " \n", &sp); field != nullptr; field = strtok_r(NULL, " \n", &sp))
{ {
if (index == 1) auto* ipv4 = reinterpret_cast<sockaddr_in*>(&sock);
{ ipv4->sin_family = AF_INET;
int port; ipv4->sin_addr.s_addr = payload->id.idiag_src[0];
const char* portString = strchr(field, ':'); ipv4->sin_port = payload->id.idiag_sport;
if (portString == nullptr)
{
break;
} }
portString += 1; else if (payload->idiag_family == AF_INET6)
port = static_cast<int>(strtol(portString, nullptr, 16));
if (port == 0)
{ {
break; 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;
} }
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;
}
if ((status == TCP_LISTEN) && (sock.ss_family != 0))
{
sockets.emplace_back(sock); sockets.emplace_back(sock);
} }
};
// Query IPv4 listening sockets.
{
message.sdiag_family = AF_INET;
auto transaction = channel.CreateTransaction(message, SOCK_DIAG_BY_FAMILY, NLM_F_DUMP);
transaction.Execute(onMessage);
}
// Query IPv6 listening sockets.
{
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; return sockets;
@ -246,12 +230,12 @@ LX_GNS_PORT_LISTENER_RELAY SockToRelayMessage(const sockaddr_storage& sock)
{ {
auto ipv4 = reinterpret_cast<const sockaddr_in*>(&sock); auto ipv4 = reinterpret_cast<const sockaddr_in*>(&sock);
message.Address[0] = ipv4->sin_addr.s_addr; 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) else if (sock.ss_family == AF_INET6)
{ {
auto ipv6 = reinterpret_cast<const sockaddr_in6*>(&sock); 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)); memcpy(message.Address, ipv6->sin6_addr.__in6_union.__s6_addr, sizeof(message.Address));
} }
return message; return message;
@ -296,53 +280,23 @@ bool IsSameSockAddr(const sockaddr_storage& left, const sockaddr_storage& right)
{ {
auto leftIpv6 = reinterpret_cast<const sockaddr_in6*>(&left); auto leftIpv6 = reinterpret_cast<const sockaddr_in6*>(&left);
auto rightIpv6 = reinterpret_cast<const sockaddr_in6*>(&right); auto rightIpv6 = reinterpret_cast<const sockaddr_in6*>(&right);
if (leftIpv6->sin6_port != rightIpv6->sin6_port) return (leftIpv6->sin6_port == rightIpv6->sin6_port && memcmp(&leftIpv6->sin6_addr, &rightIpv6->sin6_addr, sizeof(in6_addr)) == 0);
{
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); FATAL_ERROR("Unrecognized socket family {}", left.ss_family);
return false; return false;
}
} }
// Start looking for ports bound to localhost or wildcard. // Monitor listening TCP sockets using sock_diag netlink interface.
int ScanProcNetTCP(wsl::shared::SocketChannel& channel) 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{}; std::vector<sockaddr_storage> relays{};
int result = 0; int result = 0;
for (;;) for (;;)
{ {
std::vector<sockaddr_storage> sockets; auto sockets = QueryListeningSockets(netlinkChannel);
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;
}
// Stop any relays that no longer match listening ports. // Stop any relays that no longer match listening ports.
std::erase_if(relays, [&](const auto& entry) { std::erase_if(relays, [&](const auto& entry) {
@ -386,9 +340,7 @@ int ScanProcNetTCP(wsl::shared::SocketChannel& channel)
} }
// Sleep before scanning again. // Sleep before scanning again.
// std::this_thread::sleep_for(std::chrono::seconds(1));
// TODO: Investigate using EBPF notifications instead of a sleep.
sleep(1);
} }
return result; return result;
@ -432,7 +384,7 @@ try
if (ScanForPorts) if (ScanForPorts)
{ {
return ScanProcNetTCP(channel); return MonitorListeningSockets(channel);
} }
return 0; return 0;

View File

@ -2884,14 +2884,16 @@ Return Value:
std::string TranslatedPath = WslPathTranslate(Path, 0, Mode); std::string TranslatedPath = WslPathTranslate(Path, 0, Mode);
if (TranslatedPath.empty()) if (TranslatedPath.empty())
{ {
auto WarningMessage = wsl::shared::Localization::MessageFailedToTranslate(Path);
if (wil::ScopedWarningsCollector::CanCollectWarning()) if (wil::ScopedWarningsCollector::CanCollectWarning())
{ {
EMIT_USER_WARNING(wsl::shared::Localization::MessageFailedToTranslate(Path)); EMIT_USER_WARNING(std::move(WarningMessage));
} }
else else
{ {
LOG_ERROR("Failed to translate {}", Path); LOG_WARNING("{}", WarningMessage);
} }
continue; continue;
} }

View File

@ -137,7 +137,7 @@ Return Value:
auto translatedPath = WslPathTranslate(value.data(), TRANSLATE_FLAG_ABSOLUTE, TRANSLATE_MODE_UNIX); auto translatedPath = WslPathTranslate(value.data(), TRANSLATE_FLAG_ABSOLUTE, TRANSLATE_MODE_UNIX);
if (translatedPath.empty()) if (translatedPath.empty())
{ {
std::cerr << Localization::MessageFailedToTranslatePath() << "\n"; std::cerr << Localization::MessageFailedToTranslate(value.data()) << "\n";
return 1; return 1;
} }

View File

@ -240,7 +240,6 @@ int ParseConfigFile(std::vector<ConfigKey>& keys, FILE* file, int flags, const w
{ {
wint_t ch = 0; wint_t ch = 0;
unsigned long line = 0; unsigned long line = 0;
size_t newKeyValueInsertPos = 0;
bool trailingComment = false; bool trailingComment = false;
bool inQuote = false; bool inQuote = false;
size_t trimmedLength = 0; size_t trimmedLength = 0;
@ -328,8 +327,6 @@ NewLine:
if (trailingComment) if (trailingComment)
{ {
// Subtract 1 to account for ch being '\n' or WEOF.
newKeyValueInsertPos = configFileOutput.length() - 1;
trailingComment = false; trailingComment = false;
} }
} }
@ -390,6 +387,37 @@ NewLine:
break; break;
case '[': case '[':
// We're about to parse a new section. If we have an unwritten key-value
// and the current section matches, write it now before moving to the new section.
if (updateConfigFile && !outputKeyValueUpdated && !removeKey && sectionLength > 0)
{
const auto& outputConfigKey = outputKey.value();
if (outputConfigKey.Matches(key.c_str(), sectionLength))
{
const auto& keyNames = outputConfigKey.GetNames();
// Config key without name.
FAIL_FAST_IF(keyNames.empty());
const auto keyNameUtf8 = keyNames.front();
const auto keyName = wsl::shared::string::MultiByteToWide(keyNameUtf8);
const auto sectionKeySeparatorPos = keyName.find('.');
// Config key without separated section/key name
FAIL_FAST_IF(sectionKeySeparatorPos == std::string_view::npos);
// Config key without section name
FAIL_FAST_IF(sectionKeySeparatorPos == 0);
// Config key without key name
FAIL_FAST_IF(sectionKeySeparatorPos == (keyName.length() - 1));
// Remove any trailing newlines before inserting the new key-value
while (!configFileOutput.empty() && configFileOutput.back() == L'\n')
{
configFileOutput.pop_back();
}
auto keyValue = std::format(L"\n{}={}\n\n", keyName.substr(sectionKeySeparatorPos + 1), outputKey.value().GetValue());
configFileOutput += keyValue;
outputKeyValueUpdated = true;
}
}
goto ParseSection; goto ParseSection;
default: default:
@ -418,30 +446,6 @@ NewLine:
} }
ParseSection: ParseSection:
if (updateConfigFile && !outputKeyValueUpdated && !removeKey && sectionLength > 0)
{
const auto& outputConfigKey = outputKey.value();
if (outputConfigKey.Matches(key.c_str(), sectionLength))
{
const auto& keyNames = outputConfigKey.GetNames();
// Config key without name.
FAIL_FAST_IF(keyNames.empty());
const auto keyNameUtf8 = keyNames.front();
const auto keyName = wsl::shared::string::MultiByteToWide(keyNameUtf8);
const auto sectionKeySeparatorPos = keyName.find('.');
// Config key without separated section/key name
FAIL_FAST_IF(sectionKeySeparatorPos == std::string_view::npos);
// Config key without section name
FAIL_FAST_IF(sectionKeySeparatorPos == 0);
// Config key without key name
FAIL_FAST_IF(sectionKeySeparatorPos == (keyName.length() - 1));
auto keyValue = std::format(L"\n{}={}", keyName.substr(sectionKeySeparatorPos + 1), outputKey.value().GetValue());
configFileOutput.insert(newKeyValueInsertPos, keyValue);
outputKeyValueUpdated = true;
}
}
// parse [section] ([ is already parsed) // parse [section] ([ is already parsed)
if (updateConfigFile) if (updateConfigFile)
{ {
@ -796,7 +800,6 @@ ValueDone:
// Trim any trailing space. // Trim any trailing space.
value.resize(trimmedLength); value.resize(trimmedLength);
SetConfig(keys, key.c_str(), value.c_str(), flags & CFG_DEBUG, filePath, line); SetConfig(keys, key.c_str(), value.c_str(), flags & CFG_DEBUG, filePath, line);
newKeyValueInsertPos = configFileOutput.length();
} }
goto NewLine; goto NewLine;

View File

@ -92,11 +92,6 @@ void SubProcess::SetFlags(DWORD Flag)
WI_SetAllFlags(m_flags, Flag); WI_SetAllFlags(m_flags, Flag);
} }
void SubProcess::SetDesktop(LPCWSTR Desktop)
{
m_desktop = Desktop;
}
void SubProcess::SetToken(HANDLE Token) void SubProcess::SetToken(HANDLE Token)
{ {
m_token = Token; m_token = Token;

View File

@ -37,7 +37,6 @@ public:
void InheritHandle(HANDLE Handle); void InheritHandle(HANDLE Handle);
void SetEnvironment(LPVOID Environment); void SetEnvironment(LPVOID Environment);
void SetWorkingDirectory(LPCWSTR Directory); void SetWorkingDirectory(LPCWSTR Directory);
void SetDesktop(LPCWSTR Desktop);
void SetToken(HANDLE Token); void SetToken(HANDLE Token);
void SetShowWindow(WORD Show); void SetShowWindow(WORD Show);
void SetFlags(DWORD Flag); void SetFlags(DWORD Flag);

View File

@ -195,7 +195,6 @@ CreateProcessResult CreateProcess(_In_ CreateProcessParsed* Parsed, _In_ HANDLE
wsl::windows::common::helpers::SetHandleInheritable(StdErr); wsl::windows::common::helpers::SetHandleInheritable(StdErr);
wsl::windows::common::SubProcess process(Parsed->ApplicationName.c_str(), Parsed->CommandLine(), CREATE_UNICODE_ENVIRONMENT); wsl::windows::common::SubProcess process(Parsed->ApplicationName.c_str(), Parsed->CommandLine(), CREATE_UNICODE_ENVIRONMENT);
process.SetDesktop(L"winsta0\\default");
CreateProcessResult Result{}; CreateProcessResult Result{};
if (Parsed->CreatePseudoconsole) if (Parsed->CreatePseudoconsole)

View File

@ -86,8 +86,8 @@ try
</binding> </binding>
</visual> </visual>
<actions> <actions>
<action arguments='--{}' content='{}'/> <action arguments='{}' content='{}'/>
<action arguments='--{}' content='{}'/> <action arguments='{}' content='{}'/>
</actions> </actions>
</toast>)", </toast>)",
Localization::MessageNewWslVersionAvailable(Localization::Options::DontImpersonate), Localization::MessageNewWslVersionAvailable(Localization::Options::DontImpersonate),
@ -118,8 +118,8 @@ try
</binding> </binding>
</visual> </visual>
<actions> <actions>
<action arguments='--{} {}' content='{}'/> <action arguments='{} {}' content='{}'/>
<action arguments='--{} {}' content="{}"/> <action arguments='{} {}' content="{}"/>
</actions> </actions>
</toast>)", </toast>)",
Localization::MessagePerformanceTip(Localization::Options::DontImpersonate), Localization::MessagePerformanceTip(Localization::Options::DontImpersonate),
@ -151,7 +151,7 @@ try
</binding> </binding>
</visual> </visual>
<actions> <actions>
<action arguments='--{}' content='{}'/> <action arguments='{}' content='{}'/>
</actions> </actions>
</toast>)", </toast>)",
Localization::MessageWarningDuringStartup(), Localization::MessageWarningDuringStartup(),
@ -176,7 +176,7 @@ try
</binding> </binding>
</visual> </visual>
<actions> <actions>
<action arguments='--{}' content='{}'/> <action arguments='{}' content='{}'/>
</actions> </actions>
</toast>)", </toast>)",
Localization::MessageMissingOptionalComponents(), Localization::MessageMissingOptionalComponents(),

View File

@ -33,5 +33,5 @@ LPCWSTR const handle_option = L"--handle";
LPCWSTR const event_option = L"--event"; LPCWSTR const event_option = L"--event";
LPCWSTR const parent_option = L"--parent"; LPCWSTR const parent_option = L"--parent";
LPCWSTR const vm_id_option = L"--vm-id"; LPCWSTR const vm_id_option = L"--vm-id";
LPCWSTR const embedding_option = L"--Embedding"; LPCWSTR const embedding_option = L"-Embedding";
} // namespace wslhost } // namespace wslhost

View File

@ -2172,13 +2172,22 @@ HRESULT LxssUserSessionImpl::Shutdown(_In_ bool PreventNewInstances, ShutdownBeh
return S_OK; return S_OK;
} }
void LxssUserSessionImpl::TelemetryWorker(_In_ wil::unique_socket&& socket, _In_ bool drvFsNotifications) const void LxssUserSessionImpl::TelemetryWorker(_In_ wil::unique_socket&& socket) const
try try
{ {
wsl::windows::common::wslutil::SetThreadDescription(L"Telemetry"); wsl::windows::common::wslutil::SetThreadDescription(L"Telemetry");
wsl::shared::SocketChannel channel(std::move(socket), "Telemetry", m_vmTerminating.get()); wsl::shared::SocketChannel channel(std::move(socket), "Telemetry", m_vmTerminating.get());
// Check if drvfs notifications are enabled for the user.
bool drvFsNotifications{};
{
auto impersonate = wil::impersonate_token(m_userToken.get());
const auto lxssKey = wsl::windows::common::registry::OpenLxssUserKey();
drvFsNotifications =
wsl::windows::common::registry::ReadDword(lxssKey.get(), LXSS_NOTIFICATIONS_KEY, LXSS_NOTIFICATION_DRVFS_PERF_DISABLED, 0) == 0;
}
// Aggregate information about what is running inside the VM. This is logged // Aggregate information about what is running inside the VM. This is logged
// periodically because logging each event individually would be too noisy. // periodically because logging each event individually would be too noisy.
for (;;) for (;;)
@ -2852,17 +2861,9 @@ void LxssUserSessionImpl::_CreateVm()
// If the telemetry is enabled, launch the telemetry agent inside the VM. // If the telemetry is enabled, launch the telemetry agent inside the VM.
if (m_utilityVm->GetConfig().EnableTelemetry && TraceLoggingProviderEnabled(g_hTraceLoggingProvider, WINEVENT_LEVEL_INFO, 0)) if (m_utilityVm->GetConfig().EnableTelemetry && TraceLoggingProviderEnabled(g_hTraceLoggingProvider, WINEVENT_LEVEL_INFO, 0))
{ {
bool drvFsNotifications = false;
{
auto impersonate = wil::impersonate_token(m_userToken.get());
const auto lxssKey = wsl::windows::common::registry::OpenLxssUserKey();
drvFsNotifications = wsl::windows::common::registry::ReadDword(
lxssKey.get(), LXSS_NOTIFICATIONS_KEY, LXSS_NOTIFICATION_DRVFS_PERF_DISABLED, 0) == 0;
}
LPCSTR Arguments[] = {LX_INIT_TELEMETRY_AGENT, nullptr}; LPCSTR Arguments[] = {LX_INIT_TELEMETRY_AGENT, nullptr};
auto socket = m_utilityVm->CreateRootNamespaceProcess(LX_INIT_PATH, Arguments); auto socket = m_utilityVm->CreateRootNamespaceProcess(LX_INIT_PATH, Arguments);
m_telemetryThread = std::thread(&LxssUserSessionImpl::TelemetryWorker, this, std::move(socket), drvFsNotifications); m_telemetryThread = std::thread(&LxssUserSessionImpl::TelemetryWorker, this, std::move(socket));
} }
m_pluginManager.OnVmStarted(&m_session, &userSettings); m_pluginManager.OnVmStarted(&m_session, &userSettings);

View File

@ -503,7 +503,7 @@ public:
/// <summary> /// <summary>
/// Worker thread for logging telemetry about processes running inside of WSL. /// Worker thread for logging telemetry about processes running inside of WSL.
/// </summary> /// </summary>
void TelemetryWorker(_In_ wil::unique_socket&& socket, _In_ bool drvFsNotifications) const; void TelemetryWorker(_In_ wil::unique_socket&& socket) const;
/// <summary> /// <summary>
/// Terminates a distribution by it's client identifier. /// Terminates a distribution by it's client identifier.

View File

@ -1793,8 +1793,10 @@ void WslCoreVm::InitializeGuest()
{ {
try try
{ {
m_guestDeviceManager->AddSharedMemoryDevice( // Use the appropriate virtiofs class ID based on m_userToken elevation.
VIRTIO_FS_CLASS_ID, L"wslg", L"wslg", WSLG_SHARED_MEMORY_SIZE_MB, m_userToken.get()); 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); m_sharedMemoryRoot = std::format(L"WSL\\{}\\wslg", m_machineId);
} }
CATCH_LOG() CATCH_LOG()

View File

@ -74,12 +74,7 @@ public:
// Log telemetry when a WSL notification is activated, used to determine user engagement for notifications // Log telemetry when a WSL notification is activated, used to determine user engagement for notifications
WSL_LOG_TELEMETRY("NotificationActivate", PDT_ProductAndServicePerformance, TraceLoggingValue(invokedArgs, "Arguments")); WSL_LOG_TELEMETRY("NotificationActivate", PDT_ProductAndServicePerformance, TraceLoggingValue(invokedArgs, "Arguments"));
// Prepend the executable name to the arguments so getopt can be used to parse the arguments. ArgumentParser parser(invokedArgs, wslhost::binary_name, 0);
auto commandLine = wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle());
commandLine += L" ";
commandLine += invokedArgs;
ArgumentParser parser(GetCommandLineW(), wslhost::binary_name);
parser.AddArgument( parser.AddArgument(
[]() { []() {
std::wstring path; std::wstring path;

View File

@ -461,7 +461,7 @@ extern "C" UINT __stdcall CleanMsixState(MSIHANDLE install)
const std::map<LPCWSTR, LPCWSTR> keys{ const std::map<LPCWSTR, LPCWSTR> keys{
{L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application", L"WSL"}, {L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application", L"WSL"},
{L"SOFTWARE\\Classes\\CLSID", L"{7e6ad219-d1b3-42d5-b8ee-d96324e64ff6}"}, {L"SOFTWARE\\Classes\\CLSID", L"{7e6ad219-d1b3-42d5-b8ee-d96324e64ff6}"},
{L"SOFTWARE\\Classes\\AppID", L"{7F82AD86-755B-4870-86B1-D2E68DFE8A49}"}, {L"SOFTWARE\\Classes\\AppID", L"{17696EAC-9568-4CF5-BB8C-82515AAD6C09}"},
{L"SOFTWARE\\Microsoft\\Terminal Server Client", L"Default"}, {L"SOFTWARE\\Microsoft\\Terminal Server Client", L"Default"},
{L"SOFTWARE\\Microsoft\\Terminal Server Client\\Default", L"OptionalAddIns"}, {L"SOFTWARE\\Microsoft\\Terminal Server Client\\Default", L"OptionalAddIns"},
{L"SOFTWARE\\Microsoft\\Terminal Server Client\\Default\\OptionalAddIns", L"WSLDVC_PACKAGE"}}; {L"SOFTWARE\\Microsoft\\Terminal Server Client\\Default\\OptionalAddIns", L"WSLDVC_PACKAGE"}};

View File

@ -163,6 +163,13 @@ Return Value:
LxtCheckResult(LxtCheckWslPathTranslation("C:/Foo/bar", "/mnt/c/Foo/bar", true)); LxtCheckResult(LxtCheckWslPathTranslation("C:/Foo/bar", "/mnt/c/Foo/bar", true));
LxtCheckResult(LxtCheckWslPathTranslation("foo", "foo", true)); LxtCheckResult(LxtCheckWslPathTranslation("foo", "foo", true));
LxtCheckResult(LxtCheckWslPathTranslation("foo\\", "foo/", true)); LxtCheckResult(LxtCheckWslPathTranslation("foo\\", "foo/", true));
LxtCheckResult(LxtCheckWslPathTranslation("C:\\Program Files\\Git", "/mnt/c/Program Files/Git", true));
LxtCheckResult(LxtCheckWslPathTranslation("C:\\Program Files\\PowerShell\\7", "/mnt/c/Program Files/PowerShell/7", true));
LxtCheckResult(LxtCheckWslPathTranslation("C:\\Program Files (x86)\\Common Files", "/mnt/c/Program Files (x86)/Common Files", true));
LxtCheckResult(LxtCheckWslPathTranslation(
"C:\\Users\\Test User\\AppData\\Local\\Programs\\Microsoft VS Code\\bin",
"/mnt/c/Users/Test User/AppData/Local/Programs/Microsoft VS Code/bin",
true));
ErrorExit: ErrorExit:
return Result; return Result;
@ -241,6 +248,13 @@ Return Value:
LxtCheckResult(LxtCheckWslPathTranslation("/mnt/c/Users", "C:\\Users", false)); LxtCheckResult(LxtCheckWslPathTranslation("/mnt/c/Users", "C:\\Users", false));
LxtCheckResult(LxtCheckWslPathTranslation("/mnt/c/Users/", "C:\\Users\\", false)); LxtCheckResult(LxtCheckWslPathTranslation("/mnt/c/Users/", "C:\\Users\\", false));
LxtCheckResult(LxtCheckWslPathTranslation("/mnt/c/DOESNOTEXIST/", "C:\\DOESNOTEXIST\\", false)); LxtCheckResult(LxtCheckWslPathTranslation("/mnt/c/DOESNOTEXIST/", "C:\\DOESNOTEXIST\\", false));
LxtCheckResult(LxtCheckWslPathTranslation("/mnt/c/Program Files/Git", "C:\\Program Files\\Git", false));
LxtCheckResult(LxtCheckWslPathTranslation("/mnt/c/Program Files/PowerShell/7", "C:\\Program Files\\PowerShell\\7", false));
LxtCheckResult(LxtCheckWslPathTranslation("/mnt/c/Program Files (x86)/Common Files", "C:\\Program Files (x86)\\Common Files", false));
LxtCheckResult(LxtCheckWslPathTranslation(
"/mnt/c/Users/Test User/AppData/Local/Programs/Microsoft VS Code/bin",
"C:\\Users\\Test User\\AppData\\Local\\Programs\\Microsoft VS Code\\bin",
false));
ErrorExit: ErrorExit:
return Result; return Result;

View File

@ -276,7 +276,7 @@ public:
SKIP_TEST_ARM64(); SKIP_TEST_ARM64();
TerminateDistribution(); TerminateDistribution();
WslKeepAlive keelAlive; WslKeepAlive keepAlive;
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode); ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode);
} }
@ -288,7 +288,7 @@ public:
SKIP_TEST_ARM64(); SKIP_TEST_ARM64();
TerminateDistribution(); TerminateDistribution();
WslKeepAlive keelAlive; WslKeepAlive keepAlive;
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, Mode); ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, Mode);
} }
@ -302,7 +302,7 @@ public:
TerminateDistribution(); TerminateDistribution();
const auto nonElevatedToken = GetNonElevatedToken(); const auto nonElevatedToken = GetNonElevatedToken();
WslKeepAlive keelAlive(nonElevatedToken.get()); WslKeepAlive keepAlive(nonElevatedToken.get());
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode); ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode);
} }
@ -316,11 +316,37 @@ public:
TerminateDistribution(); TerminateDistribution();
const auto nonElevatedToken = GetNonElevatedToken(); const auto nonElevatedToken = GetNonElevatedToken();
WslKeepAlive keelAlive(nonElevatedToken.get()); WslKeepAlive keepAlive(nonElevatedToken.get());
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, Mode); 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) static void XattrDrvFs(DrvFsMode Mode)
{ {
SKIP_TEST_ARM64(); SKIP_TEST_ARM64();
@ -946,6 +972,31 @@ private:
const auto nonElevatedToken = GetNonElevatedToken(); const auto nonElevatedToken = GetNonElevatedToken();
validate(nonElevatedType, nonElevatedToken.get()); 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) static VOID VerifyDrvFsSymlink(const std::wstring& Path, const std::wstring& ExpectedTarget, bool Directory)
@ -1198,6 +1249,18 @@ class WSL1 : public DrvFsTests
WSL2_TEST_ONLY(); \ WSL2_TEST_ONLY(); \
DrvFsTests::DrvfsMountNonElevatedDifferentConsole(DrvFsMode::##_mode##); \ 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) \ TEST_METHOD(XattrDrvFs) \
{ \ { \

View File

@ -175,6 +175,7 @@ class NetworkTests
WSL_TEST_CLASS(NetworkTests) WSL_TEST_CLASS(NetworkTests)
friend class MirroredTests; friend class MirroredTests;
friend class BridgedTests;
friend class VirtioProxyTests; friend class VirtioProxyTests;
static std::wstring SockaddrToString(const SOCKADDR_INET* sockAddr) static std::wstring SockaddrToString(const SOCKADDR_INET* sockAddr)
@ -790,6 +791,51 @@ class NetworkTests
VERIFY_IS_TRUE(!out.empty()); VERIFY_IS_TRUE(!out.empty());
} }
static void VerifyDnsResolutionBasic()
{
// Verify basic DNS resolution using getent
auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L"getent ahosts bing.com", 0);
VERIFY_IS_TRUE(!out.empty());
}
static void VerifyDnsResolutionDig()
{
if (HostHasInternetConnectivity(AF_INET))
{
// Test A record resolution (IPv4) with both UDP and TCP
VerifyDigDnsResolution(L"dig +short +time=5 A bing.com");
VerifyDigDnsResolution(L"dig +tcp +short +time=5 A bing.com");
// Test reverse DNS lookup
VerifyDigDnsResolution(L"dig +short +time=5 -x 8.8.8.8");
VerifyDigDnsResolution(L"dig +tcp +short +time=5 -x 8.8.8.8");
}
else
{
LogSkipped("Host does not have IPv4 internet connectivity. Skipping IPv4 DNS tests.");
}
if (HostHasInternetConnectivity(AF_INET6))
{
// Test AAAA record resolution (IPv6) with both UDP and TCP
VerifyDigDnsResolution(L"dig +short +time=5 AAAA bing.com");
VerifyDigDnsResolution(L"dig +tcp +short +time=5 AAAA bing.com");
}
else
{
LogSkipped("Host does not have IPv6 internet connectivity. Skipping IPv6 DNS tests.");
}
}
static void VerifyDnsResolutionRecordTypes()
{
// Test various DNS record types
VerifyDigDnsResolution(L"dig +short +time=5 MX bing.com");
VerifyDigDnsResolution(L"dig +short +time=5 NS bing.com");
VerifyDigDnsResolution(L"dig +short +time=5 TXT bing.com");
VerifyDigDnsResolution(L"dig +short +time=5 SOA bing.com");
}
static void VerifyDnsQueries() static void VerifyDnsQueries()
{ {
// query for A/IPv4 records // query for A/IPv4 records
@ -1006,6 +1052,27 @@ class NetworkTests
VERIFY_ARE_EQUAL(expected, out.c_str()); VERIFY_ARE_EQUAL(expected, out.c_str());
} }
TEST_METHOD(DnsResolutionBasic)
{
WSL2_TEST_ONLY();
NetworkTests::VerifyDnsResolutionBasic();
}
TEST_METHOD(DnsResolutionDig)
{
WSL2_TEST_ONLY();
NetworkTests::VerifyDnsResolutionDig();
}
TEST_METHOD(DnsResolutionRecordTypes)
{
WSL2_TEST_ONLY();
NetworkTests::VerifyDnsResolutionRecordTypes();
}
static void ClearHttpProxySettings(bool userScope) static void ClearHttpProxySettings(bool userScope)
{ {
auto command = L"Set-WinhttpProxy -SettingScope Machine -Proxy \\\"\\\""; auto command = L"Set-WinhttpProxy -SettingScope Machine -Proxy \\\"\\\"";
@ -2095,20 +2162,23 @@ class NetworkTests
VerifyNotBoundLoopback(port, false); VerifyNotBoundLoopback(port, false);
} }
static void ValidateLocalhostRelayTraffic(bool ipv6) static void ValidateLocalhostRelayTraffic(ADDRESS_FAMILY addressFamily)
{ {
THROW_HR_IF(E_INVALIDARG, addressFamily != AF_INET && addressFamily != AF_INET6);
// Bind a port in the guest. // Bind a port in the guest.
auto [guestProcess, read] = BindGuestPort(ipv6 ? L"TCP6-LISTEN:1234,bind=::1" : L"TCP4-LISTEN:1234,bind=127.0.0.1", true); auto [guestProcess, read] =
BindGuestPort(addressFamily == AF_INET6 ? L"TCP6-LISTEN:1234,bind=::1" : L"TCP4-LISTEN:1234,bind=127.0.0.1", true);
// Connect to the port via the localhost relay // Connect to the port via the localhost relay
wil::unique_socket hostSocket; wil::unique_socket hostSocket;
SOCKADDR_INET addr{}; SOCKADDR_INET addr{};
addr.si_family = ipv6 ? AF_INET6 : AF_INET; addr.si_family = addressFamily;
INETADDR_SETLOOPBACK((PSOCKADDR)&addr); INETADDR_SETLOOPBACK((PSOCKADDR)&addr);
SS_PORT(&addr) = htons(1234); SS_PORT(&addr) = htons(1234);
auto pred = [&]() { auto pred = [&]() {
hostSocket.reset(socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP)); hostSocket.reset(socket(addressFamily, SOCK_STREAM, IPPROTO_TCP));
THROW_HR_IF(E_ABORT, !hostSocket); THROW_HR_IF(E_ABORT, !hostSocket);
THROW_HR_IF(E_FAIL, connect(hostSocket.get(), reinterpret_cast<SOCKADDR*>(&addr), sizeof(addr)) == SOCKET_ERROR); THROW_HR_IF(E_FAIL, connect(hostSocket.get(), reinterpret_cast<SOCKADDR*>(&addr), sizeof(addr)) == SOCKET_ERROR);
}; };
@ -2149,8 +2219,8 @@ class NetworkTests
WSL2_TEST_ONLY(); WSL2_TEST_ONLY();
WslKeepAlive keepAlive; WslKeepAlive keepAlive;
ValidateLocalhostRelayTraffic(false); ValidateLocalhostRelayTraffic(AF_INET);
ValidateLocalhostRelayTraffic(true); ValidateLocalhostRelayTraffic(AF_INET6);
} }
TEST_METHOD(NatLocalhostRelayNoIpv6) TEST_METHOD(NatLocalhostRelayNoIpv6)
@ -2161,7 +2231,7 @@ class NetworkTests
WslKeepAlive keepAlive; WslKeepAlive keepAlive;
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"test -f /proc/net/tcp6"), 1L); VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"test -f /proc/net/tcp6"), 1L);
ValidateLocalhostRelayTraffic(false); ValidateLocalhostRelayTraffic(AF_INET);
} }
static void TestNonRootNamespaceEphemeralBind() static void TestNonRootNamespaceEphemeralBind()
@ -4299,6 +4369,36 @@ class MirroredTests
VERIFY_IS_FALSE(Watchdog.IsExpired()); VERIFY_IS_FALSE(Watchdog.IsExpired());
} }
TEST_METHOD(DnsResolutionBasic)
{
MIRRORED_NETWORKING_TEST_ONLY();
m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));
WaitForMirroredStateInLinux();
NetworkTests::VerifyDnsResolutionBasic();
}
TEST_METHOD(DnsResolutionDig)
{
MIRRORED_NETWORKING_TEST_ONLY();
m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));
WaitForMirroredStateInLinux();
NetworkTests::VerifyDnsResolutionDig();
}
TEST_METHOD(DnsResolutionRecordTypes)
{
MIRRORED_NETWORKING_TEST_ONLY();
m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));
WaitForMirroredStateInLinux();
NetworkTests::VerifyDnsResolutionRecordTypes();
}
}; };
class BridgedTests class BridgedTests
@ -4413,6 +4513,8 @@ class VirtioProxyTests
{ {
VIRTIOPROXY_TEST_ONLY(); VIRTIOPROXY_TEST_ONLY();
m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));
// Verify that we have a working connection // Verify that we have a working connection
NetworkTests::GuestClient(L"tcp-connect:bing.com:80"); NetworkTests::GuestClient(L"tcp-connect:bing.com:80");
} }
@ -4421,6 +4523,8 @@ class VirtioProxyTests
{ {
VIRTIOPROXY_TEST_ONLY(); VIRTIOPROXY_TEST_ONLY();
m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));
if (!NetworkTests::HostHasInternetConnectivity(AF_INET)) if (!NetworkTests::HostHasInternetConnectivity(AF_INET))
{ {
LogSkipped("Host does not have IPv4 internet connectivity. Skipping..."); LogSkipped("Host does not have IPv4 internet connectivity. Skipping...");
@ -4434,6 +4538,8 @@ class VirtioProxyTests
{ {
VIRTIOPROXY_TEST_ONLY(); VIRTIOPROXY_TEST_ONLY();
m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));
if (!NetworkTests::HostHasInternetConnectivity(AF_INET6)) if (!NetworkTests::HostHasInternetConnectivity(AF_INET6))
{ {
LogSkipped("Host does not have IPv6 internet connectivity. Skipping..."); LogSkipped("Host does not have IPv6 internet connectivity. Skipping...");
@ -4447,6 +4553,8 @@ class VirtioProxyTests
{ {
VIRTIOPROXY_TEST_ONLY(); VIRTIOPROXY_TEST_ONLY();
m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));
const auto state = NetworkTests::GetInterfaceState(L"eth0"); const auto state = NetworkTests::GetInterfaceState(L"eth0");
VERIFY_IS_FALSE(state.V4Addresses.empty()); VERIFY_IS_FALSE(state.V4Addresses.empty());
VERIFY_IS_TRUE(state.Gateway.has_value()); VERIFY_IS_TRUE(state.Gateway.has_value());
@ -4461,6 +4569,8 @@ class VirtioProxyTests
{ {
VIRTIOPROXY_TEST_ONLY(); VIRTIOPROXY_TEST_ONLY();
m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));
// Make sure the VM doesn't time out // Make sure the VM doesn't time out
WslKeepAlive keepAlive; WslKeepAlive keepAlive;
@ -4488,6 +4598,67 @@ class VirtioProxyTests
VERIFY_IS_TRUE(bound); VERIFY_IS_TRUE(bound);
} }
TEST_METHOD(LoopbackGuestToHost)
{
VIRTIOPROXY_TEST_ONLY();
m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));
// Verify guest can connect to host on loopback (TCP only, UDP not supported)
NetworkTests::VerifyLoopbackGuestToHost(L"127.0.0.1", IPPROTO_TCP);
NetworkTests::VerifyLoopbackGuestToHost(L"0.0.0.0", IPPROTO_TCP);
// TODO: enable when v6 loopback is supported
// NetworkTests::VerifyLoopbackGuestToHost(L"::1", IPPROTO_TCP);
// NetworkTests::VerifyLoopbackGuestToHost(L"::", IPPROTO_TCP);
}
TEST_METHOD(UdpBindDoesNotPreventTcpBind)
{
VIRTIOPROXY_TEST_ONLY();
m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));
auto tcpPort = NetworkTests::BindGuestPort(L"TCP4-LISTEN:1234", true);
auto udpPort = NetworkTests::BindGuestPort(L"UDP4-LISTEN:1234", true);
}
TEST_METHOD(HostUdpBindDoesNotPreventGuestTcpBind)
{
VIRTIOPROXY_TEST_ONLY();
m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));
auto udpPort = NetworkTests::BindHostPort(2345, SOCK_DGRAM, IPPROTO_UDP, true);
auto tcpPort = NetworkTests::BindGuestPort(L"TCP4-LISTEN:2345", true);
}
TEST_METHOD(DnsResolutionBasic)
{
VIRTIOPROXY_TEST_ONLY();
m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));
NetworkTests::VerifyDnsResolutionBasic();
}
TEST_METHOD(DnsResolutionDig)
{
VIRTIOPROXY_TEST_ONLY();
m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));
NetworkTests::VerifyDnsResolutionDig();
}
TEST_METHOD(DnsResolutionRecordTypes)
{
VIRTIOPROXY_TEST_ONLY();
m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));
NetworkTests::VerifyDnsResolutionRecordTypes();
}
TEST_METHOD(HttpProxySimple) TEST_METHOD(HttpProxySimple)
{ {
VIRTIOPROXY_TEST_ONLY(); VIRTIOPROXY_TEST_ONLY();

View File

@ -262,5 +262,39 @@ class SimpleTests
std::transform(upperCaseGuidStringWide.begin(), upperCaseGuidStringWide.end(), upperCaseGuidStringWide.begin(), toupper); std::transform(upperCaseGuidStringWide.begin(), upperCaseGuidStringWide.end(), upperCaseGuidStringWide.begin(), toupper);
VERIFY_ARE_EQUAL(upperCaseGuidStringWide, wsl::shared::string::GuidToString<wchar_t>(guid, wsl::shared::string::GuidToStringFlags::Uppercase)); VERIFY_ARE_EQUAL(upperCaseGuidStringWide, wsl::shared::string::GuidToString<wchar_t>(guid, wsl::shared::string::GuidToStringFlags::Uppercase));
} }
TEST_METHOD(WindowsPathWithSpaces)
{
wil::unique_environstrings_ptr originalPath;
const DWORD pathLength = GetEnvironmentVariableW(L"PATH", nullptr, 0);
if (pathLength > 0)
{
originalPath.reset(static_cast<PWSTR>(HeapAlloc(GetProcessHeap(), 0, pathLength * sizeof(wchar_t))));
THROW_LAST_ERROR_IF_NULL(originalPath.get());
THROW_LAST_ERROR_IF(GetEnvironmentVariableW(L"PATH", originalPath.get(), pathLength) == 0);
}
auto cleanup = wil::scope_exit([&]() {
if (originalPath)
{
THROW_LAST_ERROR_IF(!SetEnvironmentVariableW(L"PATH", originalPath.get()));
}
});
const wchar_t* testPath =
L"C:\\Program Files\\Git\\cmd;"
L"C:\\Program Files\\PowerShell\\7;"
L"C:\\Program Files (x86)\\Common Files;"
L"C:\\Users\\Test User\\AppData\\Local\\Programs\\Microsoft VS Code\\bin";
THROW_LAST_ERROR_IF(!SetEnvironmentVariableW(L"PATH", testPath));
auto [output, _] = LxsstuLaunchWslAndCaptureOutput(L"echo $PATH");
VERIFY_IS_TRUE(output.find(L"/mnt/c/Program Files/Git/cmd") != std::wstring::npos);
VERIFY_IS_TRUE(output.find(L"/mnt/c/Program Files/PowerShell/7") != std::wstring::npos);
VERIFY_IS_TRUE(output.find(L"/mnt/c/Program Files (x86)/Common Files") != std::wstring::npos);
VERIFY_IS_TRUE(output.find(L"/mnt/c/Users/Test User/AppData/Local/Programs/Microsoft VS Code/bin") != std::wstring::npos);
}
}; };
} // namespace SimpleTests } // namespace SimpleTests

View File

@ -268,8 +268,12 @@ class UnitTests
{ {
validateUserSession(); validateUserSession();
auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L"echo $DISPLAY", LXSST_TEST_USERNAME)); auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L"--user {} echo $DISPLAY", LXSST_TEST_USERNAME));
VERIFY_ARE_EQUAL(out, L"\n"); VERIFY_ARE_EQUAL(out, L"\n");
// N.B. The XDG_RUNTIME_DIR variable is always set by init even if gui apps are disabled.
std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(std::format(L"--user {} echo $XDG_RUNTIME_DIR", LXSST_TEST_USERNAME));
VERIFY_ARE_EQUAL(out, std::format(L"/run/user/{}\n", TestUid));
} }
// Validate user sessions state with gui apps enabled. // Validate user sessions state with gui apps enabled.
@ -277,8 +281,11 @@ class UnitTests
WslConfigChange config(LxssGenerateTestConfig({.guiApplications = true})); WslConfigChange config(LxssGenerateTestConfig({.guiApplications = true}));
validateUserSession(); validateUserSession();
auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L"echo $DISPLAY", LXSST_TEST_USERNAME)); auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L"--user {} echo $DISPLAY", LXSST_TEST_USERNAME));
VERIFY_ARE_EQUAL(out, L":0\n"); VERIFY_ARE_EQUAL(out, L":0\n");
std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(std::format(L"--user {} echo $XDG_RUNTIME_DIR", LXSST_TEST_USERNAME));
VERIFY_ARE_EQUAL(out, std::format(L"/run/user/{}\n", TestUid));
} }
// Create a 'broken' /run/user and validate that the warning is correctly displayed. // Create a 'broken' /run/user and validate that the warning is correctly displayed.
@ -2312,7 +2319,7 @@ Error code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND
const std::vector<LPCWSTR> serviceKeys{ const std::vector<LPCWSTR> serviceKeys{
L"SOFTWARE\\Microsoft\\Terminal Server Client\\Default\\OptionalAddIns\\WSLDVC_PACKAGE", L"SOFTWARE\\Microsoft\\Terminal Server Client\\Default\\OptionalAddIns\\WSLDVC_PACKAGE",
L"SOFTWARE\\Classes\\CLSID\\{7e6ad219-d1b3-42d5-b8ee-d96324e64ff6}", L"SOFTWARE\\Classes\\CLSID\\{7e6ad219-d1b3-42d5-b8ee-d96324e64ff6}",
L"SOFTWARE\\Classes\\AppID\\{7F82AD86-755B-4870-86B1-D2E68DFE8A49}"}; L"SOFTWARE\\Classes\\AppID\\{17696EAC-9568-4CF5-BB8C-82515AAD6C09}"};
for (const auto* keyName : serviceKeys) for (const auto* keyName : serviceKeys)
{ {
@ -3517,6 +3524,208 @@ localhostForwarding=true
configRead.close(); configRead.close();
VERIFY_ARE_EQUAL(customWslConfigContentActual, customWslConfigContentExpected); VERIFY_ARE_EQUAL(customWslConfigContentActual, customWslConfigContentExpected);
} }
// Regression test for GitHub issue #12671:
// Ensure that section headers always appear BEFORE their key-value pairs.
// Bug: WSL Settings GUI was writing keys before the section header, causing "Unknown key" errors.
{
std::wstring bugScenarioConfig =
LR"([wsl2]
[experimental]
[wsl2]
)";
WslConfigChange config{bugScenarioConfig.c_str()};
wslConfig = createWslConfig(apiWslConfigFilePath);
VERIFY_IS_NOT_NULL(wslConfig);
auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });
// Write memory setting - this should NOT appear before the first [wsl2]
WslConfigSetting memorySetting{};
memorySetting.ConfigEntry = WslConfigEntry::MemorySizeBytes;
memorySetting.UInt64Value = 17825792000ULL; // Value from bug report
VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, memorySetting), ERROR_SUCCESS);
// Read and verify
std::wifstream configRead(apiWslConfigFilePath);
std::wstring fileContent{std::istreambuf_iterator<wchar_t>(configRead), {}};
configRead.close();
// Find FIRST occurrence of [wsl2] and memory=
auto firstWsl2Pos = fileContent.find(L"[wsl2]");
auto memoryPos = fileContent.find(L"memory=");
VERIFY_ARE_NOT_EQUAL(firstWsl2Pos, std::wstring::npos);
VERIFY_ARE_NOT_EQUAL(memoryPos, std::wstring::npos);
// The critical assertion: memory= must NOT appear before [wsl2]
VERIFY_IS_TRUE(firstWsl2Pos < memoryPos);
// Additional check: memory should appear after the first [wsl2], not after line 1
auto firstLineEnd = fileContent.find(L'\n');
VERIFY_IS_TRUE(memoryPos > firstLineEnd);
}
// Test: Empty file - should create proper [wsl2] section structure
{
std::wofstream emptyConfig(apiWslConfigFilePath, std::ios::trunc);
emptyConfig.close();
wslConfig = createWslConfig(apiWslConfigFilePath);
VERIFY_IS_NOT_NULL(wslConfig);
auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });
WslConfigSetting memorySetting{};
memorySetting.ConfigEntry = WslConfigEntry::MemorySizeBytes;
memorySetting.UInt64Value = 4294967296ULL; // 4GB
VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, memorySetting), ERROR_SUCCESS);
std::wifstream configRead(apiWslConfigFilePath);
std::wstring fileContent{std::istreambuf_iterator<wchar_t>(configRead), {}};
configRead.close();
// Should create [wsl2] section and add memory key
VERIFY_IS_TRUE(fileContent.find(L"[wsl2]") != std::wstring::npos);
VERIFY_IS_TRUE(fileContent.find(L"memory=") != std::wstring::npos);
// Verify [wsl2] comes before memory=
VERIFY_IS_TRUE(fileContent.find(L"[wsl2]") < fileContent.find(L"memory="));
}
// Test: Multiple same-section instances - should update first occurrence
{
std::wofstream configFile(apiWslConfigFilePath, std::ios::trunc);
configFile << L"[wsl2]\n";
configFile << L"processors=4\n";
configFile << L"\n";
configFile << L"[experimental]\n";
configFile << L"autoProxy=true\n";
configFile << L"\n";
configFile << L"[wsl2]\n"; // Second [wsl2] section
configFile << L"swap=0\n";
configFile.close();
wslConfig = createWslConfig(apiWslConfigFilePath);
VERIFY_IS_NOT_NULL(wslConfig);
auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });
WslConfigSetting memorySetting{};
memorySetting.ConfigEntry = WslConfigEntry::MemorySizeBytes;
memorySetting.UInt64Value = 8589934592ULL; // 8GB
VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, memorySetting), ERROR_SUCCESS);
std::wifstream configRead(apiWslConfigFilePath);
std::wstring fileContent{std::istreambuf_iterator<wchar_t>(configRead), {}};
configRead.close();
// Find first and second [wsl2]
auto firstWsl2 = fileContent.find(L"[wsl2]");
auto secondWsl2 = fileContent.find(L"[wsl2]", firstWsl2 + 1);
auto memoryPos = fileContent.find(L"memory=");
VERIFY_ARE_NOT_EQUAL(firstWsl2, std::wstring::npos);
VERIFY_ARE_NOT_EQUAL(secondWsl2, std::wstring::npos);
VERIFY_ARE_NOT_EQUAL(memoryPos, std::wstring::npos);
// Memory should be added to FIRST [wsl2] section, not second
VERIFY_IS_TRUE(memoryPos > firstWsl2);
VERIFY_IS_TRUE(memoryPos < secondWsl2);
}
// Test: EOF without trailing newline
{
std::wofstream configFile(apiWslConfigFilePath, std::ios::trunc);
configFile << L"[wsl2]\n";
configFile << L"processors=2"; // No trailing newline
configFile.close();
wslConfig = createWslConfig(apiWslConfigFilePath);
VERIFY_IS_NOT_NULL(wslConfig);
auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });
WslConfigSetting memorySetting{};
memorySetting.ConfigEntry = WslConfigEntry::MemorySizeBytes;
memorySetting.UInt64Value = 3221225472ULL; // 3GB
VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, memorySetting), ERROR_SUCCESS);
std::wifstream configRead(apiWslConfigFilePath);
std::wstring fileContent{std::istreambuf_iterator<wchar_t>(configRead), {}};
configRead.close();
// Should properly append memory key even without trailing newline on last line
VERIFY_IS_TRUE(fileContent.find(L"processors=2") != std::wstring::npos);
VERIFY_IS_TRUE(fileContent.find(L"memory=") != std::wstring::npos);
// Verify both keys are in the same section
auto wsl2Pos = fileContent.find(L"[wsl2]");
auto processorsPos = fileContent.find(L"processors=2");
auto memoryPos = fileContent.find(L"memory=");
VERIFY_IS_TRUE(wsl2Pos < processorsPos);
VERIFY_IS_TRUE(wsl2Pos < memoryPos);
// Memory should come after processors in the same section
VERIFY_IS_TRUE(processorsPos < memoryPos);
}
// Test: Empty section followed by another section
{
std::wofstream configFile(apiWslConfigFilePath, std::ios::trunc);
configFile << L"[wsl2]\n";
configFile << L"[experimental]\n";
configFile << L"autoProxy=true\n";
configFile.close();
wslConfig = createWslConfig(apiWslConfigFilePath);
VERIFY_IS_NOT_NULL(wslConfig);
auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });
WslConfigSetting memorySetting{};
memorySetting.ConfigEntry = WslConfigEntry::MemorySizeBytes;
memorySetting.UInt64Value = 5368709120ULL; // 5GB
VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, memorySetting), ERROR_SUCCESS);
std::wifstream configRead(apiWslConfigFilePath);
std::wstring fileContent{std::istreambuf_iterator<wchar_t>(configRead), {}};
configRead.close();
// Should insert memory into empty [wsl2] section before [experimental]
auto wsl2Pos = fileContent.find(L"[wsl2]");
auto memoryPos = fileContent.find(L"memory=");
auto experimentalPos = fileContent.find(L"[experimental]");
VERIFY_ARE_NOT_EQUAL(wsl2Pos, std::wstring::npos);
VERIFY_ARE_NOT_EQUAL(memoryPos, std::wstring::npos);
VERIFY_ARE_NOT_EQUAL(experimentalPos, std::wstring::npos);
// Order should be: [wsl2], memory=, [experimental]
VERIFY_IS_TRUE(wsl2Pos < memoryPos);
VERIFY_IS_TRUE(memoryPos < experimentalPos);
}
// Test: Section header at EOF with no content
{
std::wofstream configFile(apiWslConfigFilePath, std::ios::trunc);
configFile << L"[wsl2]"; // Section at EOF, no newline, no content
configFile.close();
wslConfig = createWslConfig(apiWslConfigFilePath);
VERIFY_IS_NOT_NULL(wslConfig);
auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });
WslConfigSetting memorySetting{};
memorySetting.ConfigEntry = WslConfigEntry::MemorySizeBytes;
memorySetting.UInt64Value = 6442450944ULL; // 6GB
VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, memorySetting), ERROR_SUCCESS);
std::wifstream configRead(apiWslConfigFilePath);
std::wstring fileContent{std::istreambuf_iterator<wchar_t>(configRead), {}};
configRead.close();
// Should properly add key to section at EOF
VERIFY_IS_TRUE(fileContent.find(L"[wsl2]") != std::wstring::npos);
VERIFY_IS_TRUE(fileContent.find(L"memory=") != std::wstring::npos);
VERIFY_IS_TRUE(fileContent.find(L"[wsl2]") < fileContent.find(L"memory="));
}
} }
TEST_METHOD(LaunchWslSettingsFromProtocol) TEST_METHOD(LaunchWslSettingsFromProtocol)

View File

@ -71,23 +71,41 @@ if ($Package) {
try { try {
if ($AllowUnsigned) if ($AllowUnsigned)
{ {
# unfortunately -AllowUnsigned isn't supported on vb so we need to manually import the certificate and trust it. # Try to add with -AllowUnsigned first (supported in newer PowerShell)
(Get-AuthenticodeSignature $Package).SignerCertificate | Export-Certificate -FilePath private-wsl.cert | Out-Null try {
try Add-AppxPackage $Package -AllowUnsigned -ErrorAction Stop
{
Import-Certificate -FilePath .\private-wsl.cert -CertStoreLocation Cert:\LocalMachine\Root | Out-Null
}
finally
{
Remove-Item -Path .\private-wsl.cert
}
}
Add-AppxPackage $Package
} }
catch { catch {
Write-Host $_ # Fallback: manually import the certificate and trust it
Get-AppPackageLog -All 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
}
}
else {
Add-AppxPackage $Package -ErrorAction Stop
}
}
catch {
Write-Host "Error installing package: $_"
Get-AppPackageLog | Select-Object -First 64 | Format-List
exit 1 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>