Enable rollback on install failures during update (#40524)
* Fix system.vhd loss during failed MSI upgrade (#40488) Move MajorUpgrade Schedule to afterInstallInitialize so RemoveExistingProducts runs inside the MSI transaction. On upgrade failure, the old product is restored instead of leaving files permanently deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add rollback regression test for MSI upgrade failures Add MsiUpgradeRollbackRestoresFiles test that validates the Schedule="afterInstallInitialize" fix by: 1. Installing an older WSL version (2.0.2) 2. Locking wsl.exe to force the upgrade to fail 3. Verifying rollback restores files and MSI registration 4. Reinstalling current version for subsequent tests Follows the same pattern as MsixUpgradeFails() but tests the MSI-to-MSI upgrade path with rollback verification. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: rewrite MsiUpgradeRollbackRestoresFiles to properly test rollback Replace the minimal smoke test with a proper MajorUpgrade rollback test. The test uses the Windows Installer API to create a modified copy of the current MSI with: - A new ProductCode (avoids maintenance mode) - Bumped version to 99.99.99 - A Type 19 custom action (ForceFailure) at sequence 1599 - Regenerated PackageCode This triggers MajorUpgrade (same UpgradeCode), RemoveExistingProducts runs inside the transaction (Schedule=afterInstallInitialize), then ForceFailure aborts the install causing rollback. The test verifies: - msiexec returns ERROR_INSTALL_FAILURE (1603) - Original files (wsl.exe, wslservice.exe) are restored with same sizes - MSI package remains registered - MSI log confirms RemoveExistingProducts ran before ForceFailure Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: add scope_exit cleanup and logging for MsiUpgradeRollbackRestoresFiles Add wil::scope_exit_log to reinstall the current MSI if the test fails partway through, preventing state corruption for subsequent tests. Also add exit code logging for easier CI debugging. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: replace runtime rollback test with static MSI database verification Replace the runtime MSI upgrade+rollback test (which failed CI twice due to CloudTest environment issues) with a static verification that directly queries the MSI database to confirm RemoveExistingProducts is scheduled inside the MSI transaction (between InstallInitialize and InstallFinalize). This approach: - Directly verifies the Schedule='afterInstallInitialize' fix - No MSI installation/uninstallation (no state changes) - No cleanup needed - Deterministic and fast Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: fix MsiViewFetch error handling and rename test for clarity - Check MsiViewFetch return value for errors other than ERROR_NO_MORE_ITEMS - Rename test to MsiRemoveExistingProductsScheduledInsideTransaction to accurately describe what it verifies (static scheduling check, not runtime rollback) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: add e2e MSI rollback test with forced failure CA Add WslTestForceInstallFailure deferred CA that returns ERROR_INSTALL_FAILURE when WSL_TEST_FORCE_INSTALL_FAILURE=1 is passed to msiexec. Sequenced after FinalizeInstall inside the transaction so MSI rollback restores files. New MsiUpgradeFailureRestoresFiles test installs the MSI, attempts an upgrade with the forced failure, then asserts wsl.exe and wslservice.exe survived. Also fixes MsiRemoveExistingProductsScheduledInsideTransaction per review: - Use MSI_NULL_INTEGER instead of -1 for null sequence detection - Drop redundant InstallFinalize bound check Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>