Commit Graph

881 Commits

Author SHA1 Message Date
Ben Hillis
aed1191a93 review: use minimal registry access and localized string in test assertion
- SetRebootRequiredMarker: KEY_ALL_ACCESS -> KEY_CREATE_SUB_KEY
- ClearRebootRequiredMarker: KEY_ALL_ACCESS -> KEY_WRITE
- Test assertion: replace brittle L"restart" substring check with
  Localization::MessageUpdateRebootRequired() for locale robustness

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-22 10:48:18 -07:00
Ben Hillis
6dc890813a test: properly clean up PendingFileRenameOperations in test cleanup
After the locked-file MSI scenario, MoveFileEx DELAY_UNTIL_REBOOT entries
remain in PendingFileRenameOperations. Add ClearPendingFileRenameOperationsForPath()
to strip WSL install path entries from that registry value before reinstalling,
allowing ValidatePackageInstalledProperly() (wsl echo OK) to succeed.

Also tightens the path match to require a separator boundary after the prefix
so sibling directories (e.g. 'WSL2') are not inadvertently matched.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-22 09:20:53 -07:00
Ben Hillis
189d97ce58 test: use ValidateInstalledVersion in reboot test cleanup
After the locked-file scenario, MoveFileEx DELAY_UNTIL_REBOOT entries are
still pending so distro launches fail until an actual reboot. Using
ValidateInstalledVersion (wsl --version) instead of ValidatePackageInstalledProperly
(wsl echo OK) for the cleanup check avoids a spurious failure.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-22 06:37:11 -07:00
Ben Hillis
bc7d36a066 Format InstallerTests.cpp
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-22 06:30:08 -07:00
Ben Hillis
9098aee907 update to warning 2026-05-21 13:44:54 -07:00
Ben Hillis
c9d99858c6 test: remove FILE_SHARE_DELETE from locked file handle in reboot test
Without FILE_SHARE_DELETE, MSI cannot open system.vhd with DELETE intent,
making the 3010 (ERROR_SUCCESS_REBOOT_REQUIRED) outcome more reliable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-21 12:50:52 -07:00
Ben Hillis
4c2094d756 install: emit warning instead of error when reboot is required after MSI update
When the MSI installer returns ERROR_SUCCESS_REBOOT_REQUIRED (files in use,
delayed rename pending), WaitForMsiInstall previously threw an error that
blocked all subsequent wsl.exe invocations. This was unnecessarily harsh —
the service's _CreateInstance gate already emits the same reboot-required
warning when a distro launch is attempted.

Change WaitForMsiInstall to emit a user warning and return normally instead
of throwing. Remove the now-dead reboot-specific rethrow in CallMsiPackage.

Verified by InstallerTests::UpgradeWithLockedFileReportsRebootRequired
(25/25 passing).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-21 11:58:40 -07:00
Ben Hillis
02969ca96b Address PR feedback: don't block non-distro commands; auto-clear marker on success
The original CallMsiPackage() early-throw blocked every MSIX-lifted wsl.exe invocation (including --version, --list, --shutdown, --update) regardless of whether it would touch the half-installed files. Remove the early throw and rely on the service-side _CreateInstance check, which already gates exactly the distro-launching paths that actually depend on the broken install dir.

Also add ClearRebootRequiredMarker() and call it from any MSI install path that completes with ERROR_SUCCESS, so a 'wsl --shutdown; wsl --update' flow can self-recover without requiring an actual reboot.

Extend the integration test to verify wsl --version still succeeds with the marker set, and that the marker is cleared after a clean reinstall.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-20 14:32:16 -07:00
Ben Hillis
df33fb3307 Surface MSI reboot-required failures instead of silently dropping VHDs
When the embedded MSI in the Microsoft Store MSIX cannot replace a locked
file (most commonly system.vhd, which is mounted whenever a WSL2 instance
is running), Windows Installer renames the existing file to
C:\Windows\Installer\Config.Msi\<hex>.rbf, schedules the new file via
MoveFileEx(MOVEFILE_DELAY_UNTIL_REBOOT), and returns
ERROR_SUCCESS_REBOOT_REQUIRED (3010).

InstallMsipackageImpl in wslinstaller silently converted 3010 to
ERROR_SUCCESS and returned success to its caller. The user was told
nothing; their next wsl invocation hit a now-empty C:\Program Files\WSL
install dir (system.vhd physically gone until reboot) and produced a
confusing "vhd missing" failure - perceived as data loss.

This change:

* Stops swallowing 3010 in InstallMsipackageImpl. The MSI log is now
  preserved on 3010 (previously deleted) to aid diagnostics.
* Sets a volatile registry marker
  (HKLM\Software\Microsoft\Windows\CurrentVersion\Lxss\MSI\RebootPending)
  using REG_OPTION_VOLATILE so the kernel auto-clears it on reboot. No
  cleanup path is needed; the marker is gone iff the user has rebooted.
* Adds a marker check in LxssUserSession::_CreateInstance (service
  side) which throws a localized user-facing error
  (MessageUpdateRebootRequired) any time a client tries to launch a
  distro while the reboot is pending. This catches all distro-launching
  client paths through the single service entry point: wsl.exe (lifted
  and MSI-installed), wslg.exe, bash.exe, VS Code Remote-WSL, etc.
* Also checks the marker on entry to CallMsiPackage and throws on 3010
  in WaitForMsiInstall, so the wsl --update / lifted-client paths
  surface the same error.

User-visible behavior:

  > wsl
  WSL was updated but a system restart is required to complete the
  installation. Please reboot your machine and try again.
  Error code: Wsl/Service/CreateInstance/0x80070bc2

The user reboots; the volatile key is destroyed by the kernel; Windows'
pending file-rename queue swaps the staged file into place; WSL works
again.

Adds an integration test, InstallerTests::UpgradeWithLockedFileReportsRebootRequired,
that exercises the full path: uninstalls the MSI, memory-maps a dummy
file at the install path to make it unrenameable, runs the MSIX
installer to drive the WslInstaller service, polls for the marker, then
verifies wsl echo OK fails with the expected message before cleaning up.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-20 13:48:57 -07:00
Ben Hillis
680fe6545e cloudtest: skip WSLC test suite on Windows Server images (#40559)
* cloudtest: skip WSLC test suite on Windows Server images

The WSLC TAEF job consistently times out on Windows Server images
because of a VMBus PowerOff hang during VM teardown on Server SKUs.

Skip the wslc TAEF group on Server images until the host issue is
resolved. The wsl1 and wsl2 groups continue to run on Server. Client
images (rs_prerelease, win11-23h2, win10-22h2) are unchanged and still
run all three groups.

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

* Address review feedback: dereference loop variable in IN_LIST check

Use "${image}" instead of bare image in the IN_LIST condition for
clarity and to avoid relying on if() implicit variable lookup.

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

* .pipelines: skip WSLC test job on Server images

Companion change to the cloudtest/CMakeLists.txt skip: tag the
fe_release image as `server: true` and gate the WSLC test job on
`image.server` so the pipeline does not try to load a TestMap.xml
that is no longer generated for the WS22+wslc combination.

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

---------

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-18 17:23:39 +00:00
Blue
b2ba89de80 Quote msiexec arguments in the InstallerTests (#40566) 2026-05-18 10:02:54 -07:00
Blue
a4f34ca533 Localization change from build: 147167727 (#40575)
Co-authored-by: WSL localization <noreply@microsoft.com>
2026-05-17 07:32:18 -07:00
Robert Haist
56ce826cb1 Update Debian URLs and SHA256 checksums to v1.26.0.0 (#40572)
Unfortunately we introduced a bug by not shipping a customized /etc/profile in v1.25.0.0 so this is the bug-fix follow-up release.
2026-05-16 20:25:12 +00:00
Blue
bc09920981 Localization change from build: 147088610 (#40571)
Co-authored-by: WSL localization <noreply@microsoft.com>
2026-05-16 05:49:16 -07:00
Ben Hillis
7d92c41e39 Update Microsoft.WSLg to 1.0.77 (#40568)
Picks up the fix for GUI app icons disappearing from the Start menu
on Azure Linux 3 system distros. The system distro upgrade in WSLg
1.0.72 brought in librsvg 2.58, which lazily spawns a rayon thread
pool the first time it has to render a complex SVG icon. Those
worker threads share fs_struct with weston's app-list enumeration
thread and break the subsequent setns(CLONE_NEWNS) into the user
distro's mount namespace, causing every app processed after the
first complex-icon one to fall off the Start menu.

Fixed in microsoft/weston-mirror#162; rolled into Microsoft.WSLg
1.0.77.

Fixes microsoft/wslg#1444
Fixes #40538

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-16 01:22:52 +00:00
Ben Hillis
96b0912913 wslc: assert Pid > 0 in DockerExecProcessControl::SetPid (#40567)
Defense-in-depth follow-up to PR #40550. The exec polling loop in
WSLCContainerImpl::Exec now correctly filters Pid > 0 before calling
SetPid, but a future caller that bypassed that check would silently
hang the process wait (because Docker never emits exec_die for a
process that never spawned). Assert at the lowest level so any such
regression fires loudly in Debug builds.

Suggested by @OneBlue in the PR #40550 review.

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-16 00:19:14 +00:00
Blue
c9e4671b27 Implement the WSLC plugin API (#40521)
* Save state

* Save state

* Save state

* Save state

* Save state

* Format

* Save state

* Save state

* Format

* Save state

* Save state

* Reorganize tests

* Cleanup before review

* Add missing file

* Apply PR feedback

* Apply PR feedback

* Format

* Apply PR feedback

* merge

* Apply PR feedback

* Format

* Apply PR feedback

* Use iswalnum()
2026-05-16 00:08:41 +00:00
beena352
104e05124e Add container:<name id> network mode for WSLC containers (#40502) 2026-05-15 15:57:29 -07:00
JohnMcPMS
2933bff00c Ignore GSL warning (#40560) 2026-05-15 15:05:52 -07:00
Scott Bradnick
7170ab2b1c Update SLE 16.0 for QU2 (#40562)
Also removing Leap 15.6 as it reached EOL.
2026-05-15 21:17:33 +00:00
Blue
fc174c65f8 Localization change from build: 147055100 (#40561)
Co-authored-by: WSL localization <noreply@microsoft.com>
2026-05-15 21:11:32 +00:00
Robert Haist
ff880e1376 Update Debian URLs and SHA256 hashes in DistributionInfo (#40554) 2026-05-15 19:40:54 +00:00
David Bennett
10dca9a9dd Create alias to wslc.exe (#40556) 2026-05-15 19:03:44 +00:00
Ben Hillis
5b7206bb8b Fix WSLC exec hang on fast runc failure (e.g. invalid user/group) (#40550)
WSLCContainerImpl::Exec polls Docker's exec inspect endpoint after
StartExec to learn whether the user process is running or has already
failed. The Running branch was guarded by `state.Pid.has_value()`,
which is meaningless because Docker's wire schema declares Pid as a
non-nullable Go int that is 0 until runc forks the user process - so
the JSON always contains `"Pid": 0` and nlohmann always deserializes
that as `optional<int>(0)` with `has_value() == true`.

When runc fails before forking (e.g. `-u root:badgid`), Docker
briefly reports `{Running: true, Pid: 0, ExitCode: null}` in the
window between logging the error and running its deferred cleanup that
sets `Running=false, ExitCode=126`. The polling loop accepted Pid=0
as a valid PID, called SetPid(0), broke out, and returned the process
to wslc. wslc then waited on the exit event forever, because Docker
never emits an `exec_die` event when the user process never spawned.

Change InspectExec.Pid from `std::optional<int>` to `int` to match
the wire format, and check `state.Pid > 0` at the call site. With
this change the loop continues polling on Pid=0; on the next iteration
Docker has settled state and the existing ExitCode branch fires with
the correct exit code (126).

Verified against the failing test
WSLCE2EContainerExecTests::WSLCE2E_Container_Exec_UserOption_InvalidGroup_Fails,
which is the regression test for this bug.

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-15 11:58:55 -07:00
Blue
d4359f1fdf Localization change from build: 147018536 (#40555)
Co-authored-by: WSL localization <noreply@microsoft.com>
2026-05-15 06:02:25 -07:00
Ben Hillis
b011cf7785 Pipelines: exclude in-repo .ps1 scripts from CodeSign post-analysis (#40541)
The Guardian CodeSign tool was scanning all .ps1 files in the source tree
and failing the release build on diagnostic / dev / test helper scripts that
aren't shipped (the OneBranch codesign targetGlob already restricts signing
to .dll/.exe/.sys/.msi/.msix/.appx/.nupkg).

Set ob_sdl_codeSignValidation_excludes to skip **\*.ps1 at the pipeline
level for the release, nightly, and PR OneBranch pipelines, and combine
with the existing testbin\** exclude in build-job.yml.

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-14 23:34:19 -07:00
Copilot
e04be45185 wslc: update volume create --driver help text to enumerate supported drivers (#40546)
* Update volume driver help text and E2E expectation

Agent-Logs-Url: https://github.com/microsoft/WSL/sessions/4ebb1292-9273-4d17-9aea-e1ab78c3b75a

Co-authored-by: AmelBawa-msft <104940545+AmelBawa-msft@users.noreply.github.com>

* Adjust driver arg localization comments after placeholder removal

Agent-Logs-Url: https://github.com/microsoft/WSL/sessions/4ebb1292-9273-4d17-9aea-e1ab78c3b75a

Co-authored-by: AmelBawa-msft <104940545+AmelBawa-msft@users.noreply.github.com>

* Remove WSLCCLI_DriverArgDescription from all non-en-US locale files

Agent-Logs-Url: https://github.com/microsoft/WSL/sessions/1b61b8da-a0f1-4f4c-b508-e153b979a616

Co-authored-by: AmelBawa-msft <104940545+AmelBawa-msft@users.noreply.github.com>

* Apply clang-format to WSLCE2EVolumeCreateTests.cpp

Agent-Logs-Url: https://github.com/microsoft/WSL/sessions/77b8fda5-44ab-462a-90ef-8000cb7ac79b

Co-authored-by: AmelBawa-msft <104940545+AmelBawa-msft@users.noreply.github.com>

* Rename WSLCCLI_DriverArgDescription to WSLCCLI_DriverOptionDescription

Agent-Logs-Url: https://github.com/microsoft/WSL/sessions/7fbee671-7250-492b-a575-2927a9a4f2bd

Co-authored-by: AmelBawa-msft <104940545+AmelBawa-msft@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: AmelBawa-msft <104940545+AmelBawa-msft@users.noreply.github.com>
2026-05-15 03:26:39 +00:00
Ben Hillis
5bc0e0184e Use inline _locComment syntax for locked tokens in en-US WSL.adml (#40544)
The standalone <!-- {Locked="..."} --> XML comments above each <string>
are silently ignored by the POMXML parser used by TouchdownBuildTask, so
locked tokens were getting translated in every locale (NetworkingMode
values "Mirrored"/"None"/"NAT", ".wslconfig" key names, etc.).

Move the directives inside each <string> using the inline form the parser
actually honors:
  <string id="X"><!-- _locComment='{Locked="Foo"}' -->...Foo...</string>
  <string id="NetworkingModeMirrored"><!-- _locComment="{Locked}" -->Mirrored</string>

Update validate-localization.py to extract tokens from the inline form so
the baseline-token-presence check still catches authoring mistakes.

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-15 00:32:38 +00:00
Blue
7ddc5f2973 Localization change from build: 146979634 (#40543)
Co-authored-by: WSL localization <noreply@microsoft.com>
2026-05-14 23:12:32 +00:00
Blue
a2a4ab8ab9 Disable the WSL1 Timer test case (#40539) 2026-05-14 14:38:08 -07:00
Blue
d4b49348fb Move IO related classes to their own file & namespace (#40534)
* Move IO related classes to their own file & namespace

* Add file

* Format
2026-05-14 12:46:55 -07:00
Kevin Vega
17aca91856 Add container list filters (#40513) 2.8.6 2026-05-14 10:35:25 -07:00
Blue
bc5fe6509b Localization change from build: 146927655 (#40536)
Co-authored-by: WSL localization <noreply@microsoft.com>
2026-05-14 09:28:50 -07:00
David Bennett
5a61e777d1 CLI: Add container stats command (#40500) 2026-05-13 23:20:28 -07:00
David Bennett
7c3394fe67 CLI: Fix parser treating stdin positional ('-') as an argument specifier error. (#40527) 2026-05-13 23:19:47 -07:00
Ben Hillis
ca6c0e8d6e validate-localization: downgrade ADML locked-token mismatch to warning (#40533)
The Touchdown `.adml` parser does not honor `<!-- {Locked=...} -->` XML
comments the way the `.resw` parser honors `<comment>{Locked=...}</comment>`,
so every nightly localization PR comes back with locked tokens translated
(`Mirrored` -> `镜像`, `None` -> `Нет`, etc.) and fails CI on the
per-locale token check we added in #40515.

Keep the baseline en-US locked-token check and all ID/presentation parity
checks as errors. Downgrade the per-locale locked-token check to a warning
so loc PRs can land while we figure out the right LocVer placement for
.adml with the Touchdown team.

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-14 00:39:18 +00:00
Flor Chacón
ab71550431 Definition for SDK projection / object model (#40121) 2026-05-14 00:07:55 +00:00
Ben Hillis
d89742f3cd Address WSLC policy review follow-ups from #40466 (#40529)
* Address WSLC policy review follow-ups from #40466

Three items @OneBlue flagged in the merged PR were tagged "follow-up";
this change addresses all of them.

1. Refactor EnumerateRegistryAllowlist to use shared registry helpers
   - Add wsl::windows::common::registry::EnumStringValues(HKEY) returning
     a name->value map for REG_SZ/REG_EXPAND_SZ values (skipping other
     types). Mirrors the suggestion to centralise the EnumValues+ReadString
     pattern used by PluginManager::LoadPlugins.
   - wslpolicies.h's EnumerateRegistryAllowlist now calls EnumStringValues
     instead of hand-rolling RegQueryInfoKeyW + RegEnumValueW. Empty-entry
     filter and fail-open catch are preserved.
   - wslpolicies.h now explicitly includes registry.hpp instead of relying
     on precomp include order.

2. Reclassify the new HRESULTs as WSLC_E_* and surface them in wslcsdk.h
   - Move WSL_E_CONTAINER_DISABLED / WSL_E_REGISTRY_BLOCKED_BY_POLICY out of
     wslservice.idl and redefine them as WSLC_E_CONTAINER_DISABLED (0x8004060C)
     and WSLC_E_REGISTRY_BLOCKED_BY_POLICY (0x8004060D) in wslc.idl alongside
     the rest of the WSLC_E_* block.
   - Mirror the definitions in wslcsdk.h so SDK consumers can reference them
     by name without depending on the generated wslservice_h.h.
   - Update the service factory, wslcsession, wslutil error-code map, and
     PolicyTests to use the new names.

3. Tighten WSLContainerDisabledCli test
   - Validate stdoutText is empty (locks down which HANDLE the disabled
     message goes to).
   - Validate stderrText equals exactly
     MessageWSLContainerDisabled() + "\r\nError code: WSLC_E_CONTAINER_DISABLED\r\n"
     using the localization helper, so the message text and the error-code
     mapping are both locked in.

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

* Remove unused CreatePoliciesKey helper from wslpolicies.h

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

---------

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 23:33:15 +00:00
AmirMS
4f90788ca7 CLI: Add image import command (#40473)
* Add image import command

* Remove import from root test

* Addressed comments

* Fix build

* Addressed comments

* Addressed comments

* Added TODO

* Addressed comments
2026-05-13 16:21:00 -07:00
Blue
2cace933f2 Skip the 'TestAttachFailsWithoutWsl2Distro' test case if a WSL2 distro is installed (#40456) 2026-05-13 16:14:21 -07:00
Blue
334696eb0e Update localization script to reconfigure output streams as utf8 (#40528) 2026-05-13 16:11:42 -07:00
Copilot
2afdc87fa1 Service: Best-effort grant of VMWP access to user-supplied VHDs (#40485)
HCS fails with E_ACCESSDENIED when starting a VM whose user-supplied
kernelModules or systemDistro VHDs live somewhere VMWP cannot read
(e.g. under the user profile). Eagerly call HcsGrantVmAccess on those
paths while impersonating the user, before the VM is started.

The grant is best-effort: it requires WRITE_DAC on the file (typically
via ownership), which the impersonated user may lack for VHDs they only
have READ access to (e.g. SYSTEM-owned VHDs reachable via inherited
folder ACLs). Failures are logged via CATCH_LOG; if VMWP truly cannot
read the VHD, StartComputeSystem will still surface a clear
E_ACCESSDENIED.

Adds two regression tests:
- CustomVhdsInUserProfile: VHDs under %TEMP%, exercises the grant path.
- CustomVhdsAccessibleViaInheritedAcls: VHDs in the install dir launched
  as a non-elevated user, exercises the swallowed-grant-failure path.

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 14:59:35 -07:00
beena352
83e9cfcd89 Add support for starting a container in multiple networks (#40434) 2026-05-13 14:28:58 -07:00
Ben Hillis
aa585a2083 Implement WSLC container group policies (#40466)
* Implement WSLC container group policies

Adds enforcement for two group policies whose ADMX templates landed in
PR #40422:

- AllowWSLContainer (DWORD): master switch for wslc.exe and the
  WSLCSessionManager COM API. Enforced inside WSLCSessionManagerFactory
  so CoCreateInstance returns WSL_E_CONTAINER_DISABLED directly when the
  policy is off; every caller (wslc.exe, WslcSDK, plugins) fails fast
  through one code path rather than getting a manager whose every method
  errors out individually.

- WSLContainerRegistryAllowlist: ADMX `<list valuePrefix=\"AllowedRegistry\">`
  policy stored as a sub-key whose REG_SZ value data is each allowed
  registry hostname. Enforced in PullImage and PushImage via a shared
  helper. BuildImage is rejected outright when an allowlist is
  configured, since the in-VM docker daemon fetches FROM base images
  through its own pull mechanism and cannot be reliably gated
  per-registry. The policy is fail-open: an absent or empty sub-key is
  treated as no restriction, and registry I/O errors fall through to
  allow rather than break all container operations on a transient
  hiccup.

Adds two new HRESULTs:
- WSL_E_CONTAINER_DISABLED (0x33)
- WSL_E_REGISTRY_BLOCKED_BY_POLICY (0x34)

Adds PolicyTests covering disabled-state, CLI surfacing, allowlist
denial, build rejection, and pure-function unit tests for the allowlist
evaluator.

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

* Address review feedback on PolicyTests

* SetRegistryAllowlist: delete the WSLContainerRegistryAllowlist
  sub-key before recreating it so stale AllowedRegistryN values from
  a previous (possibly interrupted) test run can't leak into the
  current test.
* GetWslcExePath: avoid std::optional::value() throwing
  bad_optional_access; use THROW_HR_IF_MSG with a clear diagnostic
  when the MSI install location can't be read from the registry.

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

---------

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: benhillis <17727402+benhillis@users.noreply.github.com>
2026-05-13 12:43:04 -07:00
Ben Hillis
41498d358d Add Uid/Gid/Fixed driver options to WSLC VHD volumes and expose via SDK (#40476)
* Add Uid/Gid/Mode/Fixed driver options to WSLC VHD volumes; expose via SDK

The WSLC named-volume "vhd" driver only supported a single SizeBytes
option, so containers running as a non-root user could not write to
their own persistent volumes (mkfs.ext4 leaves the root owned by
root:root with mode 0755). It also could not produce a fully-allocated
VHD, which some workloads need for predictable I/O.

Service side
============

* Adds new VHD driver options on top of SizeBytes:
    - Fixed=true|false   pre-allocate the underlying VHD
    - Uid=<n>            chown the volume root to uid (paired with Gid)
    - Gid=<n>            chown the volume root to gid (paired with Uid)
    - Mode=<octal>       chmod the volume root, max 07777, must be > 0
* Extracts a reusable OptionParser helper for typed option parsing
  with errno-capture, end-pointer validation, leading-sign rejection,
  consumed-key tracking, and a final RejectUnknown() pass. Used by
  WSLCVhdVolume's Create and Open paths so persisted metadata is
  validated identically on reload.

Public C SDK
============

WslcVhdRequirements grows three new uint32_t fields (uid/gid/mode) and
a WslcVhdRequirementsFlags bitmask. WslcCreateSessionVhdVolume:
* honors WSLC_VHD_TYPE_FIXED (was previously E_NOTIMPL)
* dynamically builds WSLCDriverOption[] based on which flags are set
* rejects unknown type values, unknown flag bits, and mode == 0 with
  E_INVALIDARG so future flag additions cannot be silently ignored
  by older SDK versions and obvious foot-guns are caught client-side.

WslcSetSessionSettingsVhd does NOT plumb owner/mode/fixed through the
session rootfs VHD path, and now rejects flags != NONE with
E_INVALIDARG instead of silently ignoring them.

WSLC_SESSION_OPTIONS_SIZE bumps 80 -> 96 to match the wider embedded
WslcVhdRequirements; this is an ABI break, callers must recompile.

WinRT projection
================

VhdRequirements gains:
    void SetOwner(UInt32 uid, UInt32 gid);
    void SetMode(UInt32 mode);

These set the corresponding flag bit and field on the underlying
struct. Pair-based SetOwner avoids the half-set foot-gun that
per-property setters would create.

Tests
=====

* WSLCTests.cpp: NamedVolumeVhdOptionsParseTest covers SizeBytes,
  unknown keys, sign rejection, range and base validation; a
  positive owner+mode test exercises chown/chmod end-to-end; a
  Fixed-allocation test asserts on-disk file_size >= requested size.
* WslcSdkTests.cpp adds invalid-type, fixed-allocation, owner+mode
  positive, mode-out-of-range negative, mode==0 negative, unknown
  flag negative, and flags=NONE-ignores-uid/gid positive cases.

The WinRT projection has no test infrastructure in the repo and is
not unit-tested; behavior is covered at the C SDK layer that the
projection delegates to.

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

* Address Copilot review feedback on PR #40476

Five findings from the Copilot pull-request reviewer:

1. wslcsdk.cpp: WslcCreateSessionVhdVolume unconditionally formatted

   options->uid / gid / mode via std::to_string and std::format even

   when the corresponding flag was not set. The header documents those

   fields as honored only when the flag is set, so a defensive caller

   could leave them uninitialized. Reading uninitialized memory is UB.

   Now only materialize uid/gid strings when FLAG_OWNER is set, and

   mode string when FLAG_MODE is set.

2. wslcsdk.idl: SetOwner/SetMode comments said they 'have no effect'

   on a VhdRequirements used with the session rootfs VHD. With the

   newly-strict WslcSetSessionSettingsVhd those flags now produce

   E_INVALIDARG instead of being silently ignored. Updated the IDL

   doc-comments to say the assignment will fail.

3. WSLCVhdVolume.cpp: service-side parser still accepted Mode=0,

   leaving direct COM callers (and persisted metadata reload) able

   to bypass the SDK-side check. Mode==0 is now rejected by Parse()

   for parity across all entry points.

4. WslcSdkTests.cpp: the owner+mode positive case only created and

   deleted the volume; nothing actually verified that chown/chmod

   were applied. Now mounts the volume into a debian:latest container

   and runs 'stat -c %u %g %a /data', asserting the output matches

   the requested 65534 65534 750.

5. OptionParser.h: lifetime-contract doc-comment was misleading —

   it implied accessors return references into the input map. In

   practice only Find() returns a pointer (used internally); the

   numeric/bool accessors return parsed values by value. Reworded.

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

* Add Mode=0 negative test for WSLC vhd parser

Reviewer pointed out the service-side Mode parse tests had thorough

coverage for non-octal, too-large, signed, and empty values, but no

explicit case for the documented invalid value Mode=0 (spec is

1..07777). Mode==0 was already rejected by Parse() in the prior

commit; this just locks the behavior in place.

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

* Validate VhdRequirements::SetMode arguments at WinRT boundary

Reviewer noted that the IDL doc-comment promised SetMode rejected

out-of-range/zero values, but the WinRT setter blindly stored the

value and validation only fired hours later inside CreateVolume.

SetMode now throws hresult_invalid_argument for mode == 0 or

mode > 07777 so callers see immediate failure at the API boundary.

SetOwner doesn't need a parallel check — uid/gid are uint32_t and

all values are valid POSIX user/group IDs.

Also tightened the IDL comment to say validation happens at the

setter (not deferred to creation).

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

* Echo caller-provided value in SizeBytes/Mode validation errors

Reviewer noted the SizeBytes==0 and Mode==0 rejection paths in
VhdVolumeOptions::Parse hard-coded the literal 0 in their error
messages instead of echoing the original input from DriverOpts.
If a caller passed SizeBytes=00 or Mode=000, the error said '0',
diverging from OptionParser's usual 'Invalid value for option
<name>: <original>' wording.

Both keys are guaranteed present in DriverOpts when these checks
fire (Required<> already succeeded for SizeBytes; Mode.has_value()
is the precondition for the Mode check), so .at() will not throw.

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

* Reject mode>07777 in C SDK and trim verbose comments

The C SDK only rejected mode==0; the WinRT setter and the public
header both promise mode<=07777 too. Aligning all three layers so
callers see immediate, consistent E_INVALIDARG.

Also a comment-bloat pass on this PR: kept "why" notes (uid/gid
foot-gun, chmod 0 rationale, c_str lifetime), dropped restatements
of what the code already says.

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

* Move volume Uid/Gid into mkfs -E root_owner; drop Mode option

Per OneBlue review feedback: bake ownership into the ext4 root inode at format
time (mkfs.ext4 -E root_owner=UID:GID) instead of spawning a post-mount chown
helper inside the VM. For a fresh volume the root is the only user-visible
inode so this is equivalent — anything the container later creates inherits
its own uid/gid.

Drop the Mode option entirely. Containers that need non-default permissions
can chmod from inside (it's a per-process concern); the SDK surface stays
minimal. Also drops the now-unused Base parameter from OptionParser.

ABI: WslcVhdRequirements shrinks 40 -> 32 bytes; WSLC_SESSION_OPTIONS_SIZE
96 -> 88. WSLC_VHD_REQ_FLAG_MODE and VhdRequirements::SetMode are removed.

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

* WSLC: tidy comments and add Ext4Format Uid/Gid contract assert

* wslcsdk.cpp: drop stale 'Owner / mode' wording from VHD-rootfs rejection comment.
* wslcsdk.idl: clarify that owner-on-rootfs fails at property-set time (via SetSessionSettingsVhd), not at session creation.
* WSLCVirtualMachine.cpp::Ext4Format: assert Uid.has_value() == Gid.has_value() so a future caller bypassing the parser can't silently drop ownership.

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

* WSLC: drop misleading std::move on Ext4Format mkfs args

WSLCProcessLauncher's constructor takes its arguments vector by
const-ref, so std::move(args) here is a no-op and only obscures intent.

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

* WSLC: trim verbose comments around VHD options

Compress over-explained rationale comments in WSLCVhdVolume.cpp,
WSLCVirtualMachine.cpp, OptionParser.h, wslcsdk.cpp/idl, and the
matching tests. No behavior change.

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

* WSLC: address reviewer feedback on VHD option parser

* OptionParser.h: include <cerrno> directly so the header is self-contained.
* OptionParser: distinguish unknown keys from invalid values. RejectUnknown
  now throws via ThrowUnknown with a new MessageWslcUnknownVolumeOption
  string ('Unknown option: ...') instead of the misleading 'Invalid value
  for option ...' message.
* WSLCVirtualMachine::Ext4Format: replace WI_ASSERT with THROW_HR_IF so a
  paired-Uid/Gid contract violation surfaces as a structured failure
  instead of a process-termination assert in production builds.
* WslcSdkTests::SessionCreateVhd: add wil::scope_exit cleanup for the
  Fixed-VHD sub-test so a mid-test VERIFY failure can't leak the volume.

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

---------

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 12:41:12 -07:00
Ben Hillis
1d6133f96f Localize ADMX Group Policy strings via Touchdown (#40515)
Add the WSL ADMX policy strings to the nightly Touchdown localization
pipeline so translated .adml files are produced for every locale already
covered by Resources.resw. Translated files land at
intune/<locale>/WSL.adml, matching the Group Policy on-disk layout (the
language-neutral WSL.admx stays at intune/WSL.admx).

Changes:

* intune/en-US/WSL.adml: new baseline ADML file. Each translatable
  string is annotated with {Locked="..."} XML comments so translators
  preserve product names (Windows Subsystem for Linux, WSL, WSL1, WSL2,
  Store WSL), .wslconfig keys (wsl2.kernel, wsl2.systemDistro,
  wsl2.kernelCommandLine, wsl2.nestedVirtualization,
  wsl2.kernelDebugPort, wsl2.networkingmode, wsl2.firewall), command
  names (wsl.exe, debug-shell, mount), networking-mode enum values
  (None, NAT, Mirrored, VirtioProxy), and the policy state literal
  Disabled.

* .pipelines/wsl-build-nightly-localization.yml: add intune/en-US/WSL.adml
  to the trigger paths and to the TouchdownBuildTask resourceFilePath
  block. The combined ';O:intune\' mapping puts translated files at
  intune/<locale>/WSL.adml.

* tools/devops/validate-localization.py: add validate_adml() which
  enforces the same locked-token invariant as .resw (every {Locked="X"}
  token must appear verbatim in its target string value), validates
  that translated locales have the same string ids as the en-US
  baseline, and is parameterized by the ADML folder name. The argv
  shortcut used in CI to avoid a pip install dependency on click now
  accepts both 3 args (back-compat) and 4 args (with explicit ADML
  folder).

* .pipelines/build-stage.yml: pass 'intune' explicitly to
  validate-localization.py so the validation invocation reads
  'localization/strings en-US intune' -- both folders visible at the
  call site, consistent with the rest of the script's parameterization.

* tools/devops/create-change.py: include untracked files in the
  'Changed files:' report and stage them with 'git add -A' (via
  repo.git.add(A=True)) before commit. Without this, Touchdown's
  first-time ADML outputs land as untracked files in newly created
  intune/<locale>/ directories and are silently dropped by 'git commit
  -a', which only stages modifications and deletions of already-tracked
  files. Confirmed against ADO build 146781281: Touchdown returned
  WSL.adml translations for 20 locales and saved them on disk, but the
  resulting auto-PR (#40511) contained only the resw modifications.

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 12:40:43 -07:00
Blue
8de4cefdfd Localization change from build: 146826820 (#40522)
Co-authored-by: WSL localization <noreply@microsoft.com>
2.8.5
2026-05-13 08:19:32 -07:00
Feng Wang
2e96d726a7 Avoid distro zombie state when wsl init dies in systemd mode (#40433)
This PR adds a init-watcher process to monitor the wsl init when systemd mode is enabled.
---------

Co-authored-by: Copilot <copilot@github.com>
2026-05-13 13:34:14 +08:00
Gordon Lam
c1fab7f8ee fix(l10n): add localization guidance comments for technical terms (#14441)
* fix(l10n): add localization guidance comments for technical terms

Add <comment> elements to en-US/Resources.resw providing translation
guidance for technical terms that are frequently mistranslated:
- File formats (tar, VHD) should not be translated
- Technical terms (mount, swap, sparse, DNS, proxy) need locale-appropriate terms
- Safe mode should use standard OS terminology

These comments help the localization team produce more accurate
translations without directly modifying locale-specific files.

Ref #14090

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

* fix(l10n): add period separators and glossary comment

- Add '.' between existing guidance and appended technical term
  guidance in 16 comment lines (fixes run-on sentences)
- Add top-level XML glossary comment listing all technical terms
  (mount, VHD, tar, swap, sparse, DNS, proxy, safe mode) for
  developer/reviewer reference

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 19:10:52 -07:00
Ben Hillis
c810e27b20 Add serviceTreeId to TouchdownBuildTask (#40517)
The Touchdown build task is warning that 'serviceTreeId' will become
mandatory in FY26. Add the WSL service GUID to the loc pipeline so the
warning goes away and we don't break when the field becomes required.

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 00:32:00 +00:00