diff --git a/src/windows/common/WslClient.cpp b/src/windows/common/WslClient.cpp index 04d167a39..ce47efb9a 100644 --- a/src/windows/common/WslClient.cpp +++ b/src/windows/common/WslClient.cpp @@ -504,10 +504,8 @@ int Install(_In_ std::wstring_view commandLine) E_INVALIDARG, Localization::MessageArgumentsNotValidTogether(WSL_INSTALL_ARG_NO_DISTRIBUTION_OPTION, WSL_INSTALL_ARG_DIST_OPTION_LONG)); } - if (fixedVhd && !vhdSize.has_value()) - { - THROW_HR_WITH_USER_ERROR(E_INVALIDARG, Localization::MessageArgumentNotValidWithout(WSL_INSTALL_ARG_FIXED_VHD, WSL_INSTALL_ARG_VHD_SIZE)); - } + // Note: Validation for --fixed-vhd now allows it without --vhd-size if defaultVhdSize is set in .wslconfig. + // The service will handle the validation and application of defaults. // A distribution to be installed can be specified in three ways: // wsl.exe --install --distribution Ubuntu diff --git a/src/windows/common/WslCoreConfig.cpp b/src/windows/common/WslCoreConfig.cpp index f4e39d228..870dd967b 100644 --- a/src/windows/common/WslCoreConfig.cpp +++ b/src/windows/common/WslCoreConfig.cpp @@ -103,6 +103,7 @@ void wsl::core::Config::ParseConfigFile(_In_opt_ LPCWSTR ConfigFilePath, _In_opt ConfigKey(ConfigSetting::DnsProxy, EnableDnsProxy), ConfigKey(ConfigSetting::SafeMode, EnableSafeMode), ConfigKey(ConfigSetting::DefaultVhdSize, MemoryString(VhdSizeBytes)), + ConfigKey(ConfigSetting::DefaultVhdType, wsl::core::VhdTypes, VhdDefaultType), ConfigKey(ConfigSetting::CrashDumpFolder, CrashDumpFolder), ConfigKey(ConfigSetting::MaxCrashDumpCount, MaxCrashDumpCount), ConfigKey(ConfigSetting::DistributionInstallPath, DefaultDistributionLocation), diff --git a/src/windows/common/WslCoreConfig.h b/src/windows/common/WslCoreConfig.h index 486c78451..2ad939a8f 100644 --- a/src/windows/common/WslCoreConfig.h +++ b/src/windows/common/WslCoreConfig.h @@ -32,7 +32,7 @@ Module Name: T_VALUE(c, LoadDefaultKernelModules), T_PRESENT(c, LoadKernelModulesPresence), T_VALUE(c, MaximumMemorySizeBytes), \ T_VALUE(c, MaximumProcessorCount), T_ENUM(c, MemoryReclaim), T_VALUE(c, MemorySizeBytes), T_VALUE(c, MountDeviceTimeout), \ T_ENUM(c, NetworkingMode), T_VALUE(c, ProcessorCount), T_SET(c, SwapFilePath), T_VALUE(c, SwapSizeBytes), \ - T_SET(c, SystemDistroPath), T_VALUE(c, VhdSizeBytes), T_VALUE(c, VmIdleTimeout), T_SET(c, VmSwitch) + T_SET(c, SystemDistroPath), T_VALUE(c, VhdSizeBytes), T_ENUM(c, VhdDefaultType), T_VALUE(c, VmIdleTimeout), T_SET(c, VmSwitch) namespace wsl::core { constexpr auto ToString(ConfigKeyPresence key) @@ -80,6 +80,29 @@ const std::map VhdTypes = { + {ToString(VhdType::Dynamic), VhdType::Dynamic}, + {ToString(VhdType::Fixed), VhdType::Fixed}}; + // N.B. These enum values are also used in InTune ADMX templates, if entries are added or removed ensure that existing // values are not changed. enum NetworkingMode @@ -266,6 +289,7 @@ namespace ConfigSetting { static constexpr auto DnsProxy = "wsl2.dnsProxy"; static constexpr auto SafeMode = "wsl2.safeMode"; static constexpr auto DefaultVhdSize = "wsl2.defaultVhdSize"; + static constexpr auto DefaultVhdType = "wsl2.defaultVhdType"; static constexpr auto CrashDumpFolder = "wsl2.crashDumpFolder"; static constexpr auto MaxCrashDumpCount = "wsl2.maxCrashDumpCount"; static constexpr auto DistributionInstallPath = "general.distributionInstallPath"; @@ -363,6 +387,7 @@ struct Config MemoryReclaimMode MemoryReclaim = MemoryReclaimMode::DropCache; bool EnableSparseVhd = false; UINT64 VhdSizeBytes = 0x10000000000; // 1TB + VhdType VhdDefaultType = VhdType::Dynamic; wsl::shared::string::MacAddress MacAddress; std::wstring NatIpAddress; diff --git a/src/windows/inc/WslCoreConfigInterface.h b/src/windows/inc/WslCoreConfigInterface.h index e126c4d14..f65f6e671 100644 --- a/src/windows/inc/WslCoreConfigInterface.h +++ b/src/windows/inc/WslCoreConfigInterface.h @@ -26,6 +26,7 @@ enum WslConfigEntry SwapSizeBytes, SwapFilePath, VhdSizeBytes, + VhdType, Networking, FirewallEnabled, IgnoredPorts, @@ -65,6 +66,12 @@ enum MemoryReclaimConfiguration DropCache = 2 }; +enum VhdTypeConfiguration +{ + Dynamic = 0, + Fixed = 1 +}; + typedef struct WslConfig* WslConfig_t; struct WslConfigSetting @@ -78,6 +85,7 @@ struct WslConfigSetting bool BoolValue; enum NetworkingConfiguration NetworkingConfigurationValue; enum MemoryReclaimConfiguration MemoryReclaimModeValue; + enum VhdTypeConfiguration VhdTypeValue; }; }; diff --git a/src/windows/libwsl/WslCoreConfigInterface.cpp b/src/windows/libwsl/WslCoreConfigInterface.cpp index bdb2f336e..8e432e958 100644 --- a/src/windows/libwsl/WslCoreConfigInterface.cpp +++ b/src/windows/libwsl/WslCoreConfigInterface.cpp @@ -103,6 +103,9 @@ WslConfigSetting GetWslConfigSetting(WslConfig_t wslConfig, WslConfigEntry wslCo static_assert(std::is_sameConfig.VhdSizeBytes)>::value); wslConfigSetting.UInt64Value = wslConfig->Config.VhdSizeBytes; break; + case VhdType: + wslConfigSetting.VhdTypeValue = static_cast(wslConfig->Config.VhdDefaultType); + break; case Networking: wslConfigSetting.NetworkingConfigurationValue = static_cast(wslConfig->Config.NetworkingMode); break; @@ -325,6 +328,20 @@ unsigned long SetWslConfigSetting(WslConfig_t wslConfig, WslConfigSetting wslCon MemoryString(defaultConfig.VhdSizeBytes), MemoryString(wslConfigSetting.UInt64Value), wslConfig->Config.VhdSizeBytes); + break; + case VhdType: + { + wsl::core::VhdType vhdTypeConfiguration{static_cast(wslConfigSetting.VhdTypeValue)}; + ConfigKey key(ConfigSetting::DefaultVhdType, wsl::core::VhdTypes, vhdTypeConfiguration); + const auto removeKey = defaultConfig.VhdDefaultType == vhdTypeConfiguration; + const auto result = wslConfig->Config.WriteConfigFile(wslConfig->ConfigFilePath.c_str(), key, removeKey); + if (result == 0) + { + wslConfig->Config.VhdDefaultType = vhdTypeConfiguration; + } + return result; + } + break; case Networking: { wsl::core::NetworkingMode networkingConfiguration{static_cast(wslConfigSetting.NetworkingConfigurationValue)}; diff --git a/src/windows/service/exe/LxssUserSession.cpp b/src/windows/service/exe/LxssUserSession.cpp index 2f541d4fe..6e8ebca2a 100644 --- a/src/windows/service/exe/LxssUserSession.cpp +++ b/src/windows/service/exe/LxssUserSession.cpp @@ -1407,8 +1407,7 @@ HRESULT LxssUserSessionImpl::RegisterDistribution( RETURN_HR_IF(E_INVALIDARG, WI_IsFlagSet(Flags, LXSS_IMPORT_DISTRO_FLAGS_FIXED_VHD)); } - // Registering a distro with a fixed VHD is only allowed if a size is specified. - RETURN_HR_IF(E_INVALIDARG, VhdSize == 0 && WI_IsFlagSet(Flags, LXSS_IMPORT_DISTRO_FLAGS_FIXED_VHD)); + // Note: Validation for fixed VHD without explicit size is now allowed - defaults from .wslconfig will be applied below. // Registering a WSL1 distro is not possible if the lxcore driver is not present. RETURN_HR_IF(WSL_E_WSL1_NOT_SUPPORTED, (Version == LXSS_WSL_VERSION_1) && !g_lxcoreInitialized); @@ -1508,8 +1507,12 @@ HRESULT LxssUserSessionImpl::RegisterDistribution( VhdSize = config.VhdSizeBytes; } + // Determine VHD type: use explicit flag if present, otherwise use config default + bool useFixedVhd = WI_IsFlagSet(Flags, LXSS_IMPORT_DISTRO_FLAGS_FIXED_VHD) || + (config.VhdDefaultType == wsl::core::VhdType::Fixed); + wsl::core::filesystem::CreateVhd( - configuration.VhdFilePath.c_str(), VhdSize, GetUserSid(), config.EnableSparseVhd, WI_IsFlagSet(Flags, LXSS_IMPORT_DISTRO_FLAGS_FIXED_VHD)); + configuration.VhdFilePath.c_str(), VhdSize, GetUserSid(), config.EnableSparseVhd, useFixedVhd); deleteFlags = LXSS_DELETE_DISTRO_FLAGS_VHD; } diff --git a/src/windows/wslsettings/LibWsl.cs b/src/windows/wslsettings/LibWsl.cs index 4037772fe..cd2770d92 100644 --- a/src/windows/wslsettings/LibWsl.cs +++ b/src/windows/wslsettings/LibWsl.cs @@ -23,27 +23,28 @@ public enum WslConfigEntry SwapSizeBytes = 3, SwapFilePath = 4, VhdSizeBytes = 5, - NetworkingMode = 6, - FirewallEnabled = 7, - IgnoredPorts = 8, - LocalhostForwardingEnabled = 9, - HostAddressLoopbackEnabled = 10, - AutoProxyEnabled = 11, - InitialAutoProxyTimeout = 12, - DNSProxyEnabled = 13, - DNSTunnelingEnabled = 14, - BestEffortDNSParsingEnabled = 15, - AutoMemoryReclaim = 16, - GUIApplicationsEnabled = 17, - NestedVirtualizationEnabled = 18, - SafeModeEnabled = 19, - SparseVHDEnabled = 20, - VMIdleTimeout = 21, - DebugConsoleEnabled = 22, - HardwarePerformanceCountersEnabled = 23, - KernelPath = 24, - SystemDistroPath = 25, - KernelModulesPath = 26 + VhdType = 6, + NetworkingMode = 7, + FirewallEnabled = 8, + IgnoredPorts = 9, + LocalhostForwardingEnabled = 10, + HostAddressLoopbackEnabled = 11, + AutoProxyEnabled = 12, + InitialAutoProxyTimeout = 13, + DNSProxyEnabled = 14, + DNSTunnelingEnabled = 15, + BestEffortDNSParsingEnabled = 16, + AutoMemoryReclaim = 17, + GUIApplicationsEnabled = 18, + NestedVirtualizationEnabled = 19, + SafeModeEnabled = 20, + SparseVHDEnabled = 21, + VMIdleTimeout = 22, + DebugConsoleEnabled = 23, + HardwarePerformanceCountersEnabled = 24, + KernelPath = 25, + SystemDistroPath = 26, + KernelModulesPath = 27 } public enum NetworkingConfiguration @@ -62,6 +63,12 @@ public enum MemoryReclaimMode DropCache = 2 } + public enum VhdType + { + Dynamic = 0, + Fixed = 1 + } + public unsafe partial class WslConfig { public partial struct __Internal diff --git a/test/windows/UnitTests.cpp b/test/windows/UnitTests.cpp index d9ffe6793..c23d1f1b5 100644 --- a/test/windows/UnitTests.cpp +++ b/test/windows/UnitTests.cpp @@ -169,6 +169,64 @@ class UnitTests } } + TEST_METHOD(DefaultVhdTypeConfiguration) + { + WSL2_TEST_ONLY(); + + // Test that defaultVhdType setting in .wslconfig works correctly + const auto testDistroName = L"test_vhd_type"; + const auto configContent = L"[wsl2]\ndefaultVhdSize=10GB\ndefaultVhdType=fixed\n"; + + WslConfigChange config(configContent); + + // Clean up any existing test distro + LxsstuLaunchWsl(std::format(L"--unregister {}", testDistroName)); + + auto cleanup = wil::scope_exit([&] { + LxsstuLaunchWsl(std::format(L"--unregister {}", testDistroName)); + }); + + // Lambda to validate VHD type + auto validateVhdType = [&](const wchar_t* expectedType) { + auto distroList = LxsstuEnumerateDistributions(); + auto distroIt = std::find_if(distroList.begin(), distroList.end(), + [&](const auto& d) { return d.Name == testDistroName; }); + + if (distroIt != distroList.end()) + { + auto vhdPath = distroIt->BasePath / L"ext4.vhdx"; + auto [vhdType, _] = LxsstuLaunchPowershellAndCaptureOutput( + std::format(L"(Get-VHD '{}').VhdType", vhdPath.wstring())); + VERIFY_ARE_EQUAL(vhdType, std::wstring(expectedType) + L"\r\n"); + } + }; + + // Install a distribution using --fixed-vhd without --vhd-size + // This should use the defaultVhdSize from config + auto [out, err] = LxsstuLaunchWslAndCaptureOutput( + std::format(L"--install --from-file \"{}\" --no-launch --name {} --fixed-vhd", g_testDistroPath, testDistroName)); + + // Verify installation succeeded + VERIFY_ARE_NOT_EQUAL(out.find(L"completed successfully"), std::wstring::npos); + + // Verify the VHD type is Fixed as specified in config + validateVhdType(L"Fixed"); + + // Test with defaultVhdType=dynamic (default) + config.Update(L"[wsl2]\ndefaultVhdSize=10GB\ndefaultVhdType=dynamic\n"); + + LxsstuLaunchWsl(std::format(L"--unregister {}", testDistroName)); + + // Install without --fixed-vhd, should use dynamic from config + auto [out2, err2] = LxsstuLaunchWslAndCaptureOutput( + std::format(L"--install --from-file \"{}\" --no-launch --name {}", g_testDistroPath, testDistroName)); + + VERIFY_ARE_NOT_EQUAL(out2.find(L"completed successfully"), std::wstring::npos); + + // Verify the VHD type is Dynamic + validateVhdType(L"Dynamic"); + } + TEST_METHOD(SystemdSafeMode) { WSL2_TEST_ONLY();