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>
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>
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>
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>
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>
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>
* 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>
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>
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>
* 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()
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>
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>
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>
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>
* 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>
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>
* 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>
* 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>
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>
* 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>
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>