From f4370e501d19c13ebb477acbe6a1cdb88a72abd8 Mon Sep 17 00:00:00 2001 From: artiga033 Date: Thu, 20 Nov 2025 23:36:08 +0800 Subject: [PATCH 01/20] PoC --- localization/strings/en-US/Resources.resw | 7 ++++++ src/linux/init/main.cpp | 23 ++++++++++++++----- src/windows/common/WslClient.cpp | 10 ++++++-- src/windows/common/WslInstall.cpp | 18 +++++++++++---- src/windows/common/WslInstall.h | 8 +++++-- src/windows/common/svccomm.cpp | 6 +++++ src/windows/common/svccomm.hpp | 2 ++ src/windows/inc/wsl.h | 2 ++ .../service/exe/DistributionRegistration.cpp | 23 ++++++++++++++++++- .../service/exe/DistributionRegistration.h | 4 ++++ src/windows/service/exe/LxssCreateProcess.h | 2 ++ src/windows/service/exe/LxssUserSession.cpp | 21 ++++++++++++++--- src/windows/service/exe/LxssUserSession.h | 6 +++++ src/windows/service/exe/WslCoreVm.cpp | 5 ++-- src/windows/service/inc/wslservice.idl | 4 ++++ 15 files changed, 120 insertions(+), 21 deletions(-) diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw index 0ffff8fd7a..fec08be4f7 100644 --- a/localization/strings/en-US/Resources.resw +++ b/localization/strings/en-US/Resources.resw @@ -456,6 +456,13 @@ Arguments for managing Windows Subsystem for Linux: --from-file <Path> Install a distribution from a local file. + --fs-type + Specify the filesystem type to use for the distribution root. + Defaults to ext4. + + --fs-mount-options + Specify additional mount options for the filesystem. + --legacy Use the legacy distribution manifest. diff --git a/src/linux/init/main.cpp b/src/linux/init/main.cpp index a07cc25850..eddc649958 100644 --- a/src/linux/init/main.cpp +++ b/src/linux/init/main.cpp @@ -136,7 +136,7 @@ int EnableInterface(int Socket, const char* Name); int ExportToSocket(const char* Source, int Socket, int ErrorSocket, unsigned int flags); -int FormatDevice(unsigned int Lun); +int FormatDevice(unsigned int Lun, const char* FsType); std::string GetLunDeviceName(unsigned int Lun); @@ -721,7 +721,7 @@ Return Value: return Result; } -int FormatDevice(unsigned int Lun) +int FormatDevice(unsigned int Lun, const char* FsType) /*++ @@ -734,6 +734,7 @@ Routine Description: Arguments: Lun - Supplies the LUN number of the SCSI device. + FsType - The filesystem type to format the device with. Return Value: @@ -747,7 +748,16 @@ try WaitForBlockDevice(DevicePath.c_str()); - std::string CommandLine = std::format("/usr/sbin/mkfs.ext4 -G 4096 '{}'", DevicePath); + std::string CommandLine; + std::string FsType_s = std::string(FsType); + if (FsType_s == "ext4") + { + CommandLine = std::format("/usr/sbin/mkfs.ext4 -G 4096 '{}'", DevicePath); + } + else if (FsType_s == "btrfs") + { + CommandLine = std::format("/usr/sbin/mkfs.btrfs '{}'", DevicePath); + } if (UtilExecCommandLine(CommandLine.c_str(), nullptr) < 0) { return -1; @@ -2524,13 +2534,14 @@ void ProcessImportExportMessage(gsl::span Buffer, wsl::shared::Socket ListenSocket = UtilListenVsockAnyPort(&ListenAddress, 2, true); THROW_LAST_ERROR_IF(!ListenSocket); + auto* FsType = wsl::shared::string::FromSpan(Buffer, Message->FsTypeOffset); + auto* MountOptions = wsl::shared::string::FromSpan(Buffer, Message->MountOptionsOffset); + if (Message->Header.MessageType == LxMiniInitMessageImport) { - THROW_LAST_ERROR_IF(FormatDevice(Message->DeviceId) < 0); + THROW_LAST_ERROR_IF(FormatDevice(Message->DeviceId, FsType) < 0); } - auto* FsType = wsl::shared::string::FromSpan(Buffer, Message->FsTypeOffset); - auto* MountOptions = wsl::shared::string::FromSpan(Buffer, Message->MountOptionsOffset); THROW_LAST_ERROR_IF(MountDevice(Message->MountDeviceType, Message->DeviceId, DISTRO_PATH, FsType, Message->Flags, MountOptions) < 0); Result = 0; diff --git a/src/windows/common/WslClient.cpp b/src/windows/common/WslClient.cpp index 3c120c8cec..820d438e6d 100644 --- a/src/windows/common/WslClient.cpp +++ b/src/windows/common/WslClient.cpp @@ -452,6 +452,8 @@ int Install(_In_ std::wstring_view commandLine) bool noDistribution = false; bool legacy = false; bool webDownload = IsWindowsServer(); + std::optional fsType; + std::optional fsMountOptions; ArgumentParser parser(std::wstring{commandLine}, WSL_BINARY_NAME); parser.AddPositionalArgument(distroArgument, 0); @@ -469,6 +471,8 @@ int Install(_In_ std::wstring_view commandLine) parser.AddArgument(g_promptBeforeExit, WSL_INSTALL_ARG_PROMPT_BEFORE_EXIT_OPTION); parser.AddArgument(SizeString(vhdSize), WSL_INSTALL_ARG_VHD_SIZE); parser.AddArgument(fixedVhd, WSL_INSTALL_ARG_FIXED_VHD); + parser.AddArgument(fsType, WSL_INSTALL_ARG_FS_TYPE); + parser.AddArgument(fsMountOptions, WSL_INSTALL_ARG_FS_MOUNT_OPTIONS); parser.Parse(); @@ -525,7 +529,9 @@ int Install(_In_ std::wstring_view commandLine) file, location.has_value() ? location->c_str() : nullptr, fixedVhd ? LXSS_IMPORT_DISTRO_FLAGS_FIXED_VHD : 0, - vhdSize); + vhdSize, + fsType.has_value() ? fsType->c_str() : nullptr, + fsMountOptions.has_value() ? fsMountOptions->c_str() : nullptr); wsl::windows::common::wslutil::PrintMessage(Localization::MessageDistributionInstalled(installedName.get()), stdout); @@ -551,7 +557,7 @@ int Install(_In_ std::wstring_view commandLine) if (!noDistribution && (legacy || !rebootRequired)) { auto result = WslInstall::InstallDistribution( - installResult, distroArgument, version, !noLaunchAfterInstall, webDownload, legacy, fixedVhd, name, location, vhdSize); + installResult, distroArgument, version, !noLaunchAfterInstall, webDownload, legacy, fixedVhd, name, location, vhdSize, fsType, fsMountOptions); std::optional flavor; if (installResult.Distribution.has_value()) diff --git a/src/windows/common/WslInstall.cpp b/src/windows/common/WslInstall.cpp index b8ed5bce21..0e7bb2f3c5 100644 --- a/src/windows/common/WslInstall.cpp +++ b/src/windows/common/WslInstall.cpp @@ -72,7 +72,9 @@ HRESULT WslInstall::InstallDistribution( _In_ bool fixedVhd, _In_ const std::optional& localName, _In_ const std::optional& location, - _In_ const std::optional& vhdSize) + _In_ const std::optional& vhdSize, + _In_ const std::optional& fsType, + _In_ const std::optional& fsMountOptions) try { wsl::windows::common::ExecutionContext context(wsl::windows::common::InstallDistro); @@ -114,7 +116,7 @@ try if (const auto* distro = std::get_if(&*installResult.Distribution)) { std::tie(installResult.Name, installResult.Id) = - InstallModernDistribution(*distro, version, localName, location, vhdSize, fixedVhd); + InstallModernDistribution(*distro, version, localName, location, vhdSize, fixedVhd, fsType, fsMountOptions); installResult.InstalledViaGitHub = true; } @@ -124,7 +126,9 @@ try {localName.has_value(), WSL_INSTALL_ARG_NAME_LONG}, {location.has_value(), WSL_INSTALL_ARG_LOCATION_LONG}, {vhdSize.has_value(), WSL_INSTALL_ARG_VHD_SIZE}, - {fixedVhd, WSL_INSTALL_ARG_FIXED_VHD}}; + {fixedVhd, WSL_INSTALL_ARG_FIXED_VHD}, + {fsType.has_value(), WSL_INSTALL_ARG_FS_TYPE}, + {fsMountOptions.has_value(), WSL_INSTALL_ARG_FS_MOUNT_OPTIONS}}; for (const auto& [condition, argument] : unsupportedArguments) { @@ -257,7 +261,9 @@ std::pair WslInstall::InstallModernDistribution( const std::optional& name, const std::optional& location, const std::optional& vhdSize, - const bool fixedVhd) + const bool fixedVhd, + const std::optional& fsType, + const std::optional& fsMountOptions) { wsl::windows::common::SvcComm service; @@ -307,7 +313,9 @@ std::pair WslInstall::InstallModernDistribution( file.get(), location.has_value() ? location->c_str() : nullptr, fixedVhd ? LXSS_IMPORT_DISTRO_FLAGS_FIXED_VHD : 0, - vhdSize); + vhdSize, + fsType.has_value() ? fsType->c_str() : nullptr, + fsMountOptions.has_value() ? fsMountOptions->c_str() : nullptr); return {installedName.get(), id}; } \ No newline at end of file diff --git a/src/windows/common/WslInstall.h b/src/windows/common/WslInstall.h index 6e141d1d7d..7a1e9c4ecf 100644 --- a/src/windows/common/WslInstall.h +++ b/src/windows/common/WslInstall.h @@ -41,7 +41,9 @@ class WslInstall _In_ bool fixedVhd, _In_ const std::optional& localName, _In_ const std::optional& location, - _In_ const std::optional& vhdSize); + _In_ const std::optional& vhdSize, + _In_ const std::optional& fsType, + _In_ const std::optional& fsMountOptions); static std::pair> CheckForMissingOptionalComponents(_In_ bool requireWslOptionalComponent); @@ -55,5 +57,7 @@ class WslInstall const std::optional& name, const std::optional& location, const std::optional& vhdSize, - const bool fixedVhd); + const bool fixedVhd, + const std::optional& fsType, + const std::optional& fsMountOptions); }; diff --git a/src/windows/common/svccomm.cpp b/src/windows/common/svccomm.cpp index b63491538e..3414570c65 100644 --- a/src/windows/common/svccomm.cpp +++ b/src/windows/common/svccomm.cpp @@ -565,6 +565,8 @@ std::pair wsl::windows::common::SvcComm::Reg _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ std::optional VhdSize, + _In_ LPCWSTR FsType, + _In_ LPCWSTR MountOptions, _In_opt_ LPCWSTR PackageFamilyName) const { ClientExecutionContext context; @@ -590,6 +592,8 @@ std::pair wsl::windows::common::SvcComm::Reg TargetDirectory, Flags, VhdSize.value_or(0), + FsType, + MountOptions, PackageFamilyName, &installedName, context.OutError(), @@ -605,6 +609,8 @@ std::pair wsl::windows::common::SvcComm::Reg TargetDirectory, Flags, VhdSize.value_or(0), + FsType, + MountOptions, PackageFamilyName, &installedName, context.OutError(), diff --git a/src/windows/common/svccomm.hpp b/src/windows/common/svccomm.hpp index 47e13d50a6..73a78bdf2c 100644 --- a/src/windows/common/svccomm.hpp +++ b/src/windows/common/svccomm.hpp @@ -84,6 +84,8 @@ class SvcComm _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ std::optional VhdSize = std::nullopt, + _In_ LPCWSTR FsType = L"ext4", + _In_ LPCWSTR FsMountOptions = L"discard,errors=remount-ro,data=ordered", _In_opt_ LPCWSTR PackageFamilyName = nullptr) const; HRESULT diff --git a/src/windows/inc/wsl.h b/src/windows/inc/wsl.h index f9bb847f54..d5002705a1 100644 --- a/src/windows/inc/wsl.h +++ b/src/windows/inc/wsl.h @@ -40,6 +40,8 @@ Module Name: #define WSL_INSTALL_ARG_FIXED_VHD L"--fixed-vhd" #define WSL_INSTALL_ARG_FROM_FILE_OPTION L'f' #define WSL_INSTALL_ARG_FROM_FILE_LONG L"--from-file" +#define WSL_INSTALL_ARG_FS_MOUNT_OPTIONS L"--fs-mount-options" +#define WSL_INSTALL_ARG_FS_TYPE L"--fs-type" #define WSL_INSTALL_ARG_LEGACY_LONG L"--legacy" #define WSL_INSTALL_ARG_LOCATION_OPTION L'l' #define WSL_INSTALL_ARG_LOCATION_LONG L"--location" diff --git a/src/windows/service/exe/DistributionRegistration.cpp b/src/windows/service/exe/DistributionRegistration.cpp index 03cdc26f09..15a2e28adf 100644 --- a/src/windows/service/exe/DistributionRegistration.cpp +++ b/src/windows/service/exe/DistributionRegistration.cpp @@ -57,7 +57,18 @@ DistributionRegistration DistributionRegistration::Open(HKEY LxssKey, const GUID } DistributionRegistration DistributionRegistration::Create( - HKEY LxssKey, const std::optional& Id, LPCWSTR Name, ULONG Version, LPCWSTR BasePath, ULONG Flags, ULONG DefaultUID, LPCWSTR PackageFamilyName, LPCWSTR VhdFileName, bool EnableOobe) + HKEY LxssKey, + const std::optional& Id, + LPCWSTR Name, + ULONG Version, + LPCWSTR BasePath, + ULONG Flags, + ULONG DefaultUID, + LPCWSTR PackageFamilyName, + LPCWSTR VhdFileName, + LPCWSTR FsType, + LPCWSTR FsMountOptions, + bool EnableOobe) { std::wstring distroGuidString; GUID distroId{}; @@ -111,6 +122,16 @@ DistributionRegistration DistributionRegistration::Create( distribution.Write(Property::VhdFileName, VhdFileName); } + if (ARGUMENT_PRESENT(FsType)) + { + distribution.Write(Property::FsType, FsType); + } + + if (ARGUMENT_PRESENT(FsMountOptions)) + { + distribution.Write(Property::FsMountOptions, FsMountOptions); + } + // Dismiss the scope exit member so the key is persisted. cleanup.release(); return distribution; diff --git a/src/windows/service/exe/DistributionRegistration.h b/src/windows/service/exe/DistributionRegistration.h index 1da0848a91..cd67214b44 100644 --- a/src/windows/service/exe/DistributionRegistration.h +++ b/src/windows/service/exe/DistributionRegistration.h @@ -55,6 +55,8 @@ class DistributionRegistration ULONG DefaultUID, LPCWSTR PackageFamilyName, LPCWSTR VhdFileName, + LPCWSTR FsType, + LPCWSTR FsMountOptions, bool EnableOobe); static DistributionRegistration Open(HKEY LxssKey, const GUID& Id); @@ -91,6 +93,8 @@ namespace Property { inline DistributionPropertyWithDefault PackageFamilyName{L"PackageFamilyName", L""}; inline DistributionPropertyWithDefault KernelCommandLine{L"KernelCommandLine", L""}; inline DistributionPropertyWithDefault VhdFileName{L"VhdFileName", LXSS_VM_MODE_VHD_NAME}; + inline DistributionPropertyWithDefault FsType{L"FsType", L"ext4"}; + inline DistributionPropertyWithDefault FsMountOptions{L"FsMountOptions", L"discard,errors=remount-ro,data=ordered"}; inline ExpectedProperty Name{L"DistributionName"}; inline ExpectedProperty BasePath{L"BasePath"}; inline DistributionProperty Flavor{L"Flavor"}; diff --git a/src/windows/service/exe/LxssCreateProcess.h b/src/windows/service/exe/LxssCreateProcess.h index 8471e7b408..365ae76738 100644 --- a/src/windows/service/exe/LxssCreateProcess.h +++ b/src/windows/service/exe/LxssCreateProcess.h @@ -110,6 +110,8 @@ typedef struct _LXSS_DISTRO_CONFIGURATION std::filesystem::path BasePath; std::wstring PackageFamilyName; std::filesystem::path VhdFilePath; + std::wstring FsType; + std::wstring FsMountOptions; ULONG Flags; std::wstring Flavor; std::wstring OsVersion; diff --git a/src/windows/service/exe/LxssUserSession.cpp b/src/windows/service/exe/LxssUserSession.cpp index 866c26614e..51e8f28c39 100644 --- a/src/windows/service/exe/LxssUserSession.cpp +++ b/src/windows/service/exe/LxssUserSession.cpp @@ -383,6 +383,8 @@ HRESULT STDMETHODCALLTYPE LxssUserSession::RegisterDistribution( _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ ULONG64 VhdSize, + _In_ LPCWSTR FsType, + _In_ LPCWSTR FsMountOptions, _In_opt_ LPCWSTR PackageFamilyName, _Out_ LPWSTR* InstalledDistributionName, _Out_ LXSS_ERROR_INFO* Error, @@ -395,7 +397,7 @@ try RETURN_HR_IF(RPC_E_DISCONNECTED, !session); return session->RegisterDistribution( - DistributionName, Version, FileHandle, ErrorHandle, TargetDirectory, Flags, VhdSize, PackageFamilyName, InstalledDistributionName, pDistroGuid); + DistributionName, Version, FileHandle, ErrorHandle, TargetDirectory, Flags, VhdSize, FsType, FsMountOptions, PackageFamilyName, InstalledDistributionName, pDistroGuid); } CATCH_RETURN() @@ -415,6 +417,8 @@ try TargetDirectory, 0, 0, + nullptr, + nullptr, packageFamilyName.empty() ? nullptr : packageFamilyName.c_str(), nullptr, nullptr, @@ -430,6 +434,8 @@ HRESULT STDMETHODCALLTYPE LxssUserSession::RegisterDistributionPipe( _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ ULONG64 VhdSize, + _In_ LPCWSTR FsType, + _In_ LPCWSTR FsMountOptions, _In_opt_ LPCWSTR PackageFamilyName, _Out_ LPWSTR* InstalledDistributionName, _Out_ LXSS_ERROR_INFO* Error, @@ -442,7 +448,7 @@ try RETURN_HR_IF(RPC_E_DISCONNECTED, !session); return session->RegisterDistribution( - DistributionName, Version, PipeHandle, ErrorHandle, TargetDirectory, Flags, VhdSize, PackageFamilyName, InstalledDistributionName, pDistroGuid); + DistributionName, Version, PipeHandle, ErrorHandle, TargetDirectory, Flags, VhdSize, FsType, FsMountOptions, PackageFamilyName, InstalledDistributionName, pDistroGuid); } CATCH_RETURN() @@ -1329,6 +1335,8 @@ LxssUserSessionImpl::ImportDistributionInplace(_In_ LPCWSTR DistributionName, _I LX_UID_ROOT, nullptr, path.filename().c_str(), + nullptr, + nullptr, false); auto configuration = s_GetDistributionConfiguration(registration); @@ -1381,6 +1389,8 @@ HRESULT LxssUserSessionImpl::RegisterDistribution( _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ ULONG64 VhdSize, + _In_ LPCWSTR FsType, + _In_ LPCWSTR FsMountOptions, _In_opt_ LPCWSTR PackageFamilyName, _Out_opt_ LPWSTR* InstalledDistributionName, _Out_ GUID* pDistroGuid) @@ -1519,6 +1529,8 @@ HRESULT LxssUserSessionImpl::RegisterDistribution( LX_UID_ROOT, PackageFamilyName, vhdName.c_str(), + FsType, + FsMountOptions, WI_IsFlagClear(Flags, LXSS_IMPORT_DISTRO_FLAGS_NO_OOBE)); configuration = s_GetDistributionConfiguration(registration, DistributionName == nullptr); @@ -2535,7 +2547,7 @@ void LxssUserSessionImpl::_CreateLegacyRegistration(_In_ HKEY LxssKey, _In_ HAND const auto basePath = wsl::windows::common::filesystem::GetLegacyBasePath(UserToken); DistributionRegistration::Create( - LxssKey, LXSS_LEGACY_DISTRO_GUID, LXSS_LEGACY_INSTALL_NAME, LXSS_DISTRO_VERSION_LEGACY, basePath.c_str(), configFlags, defaultUid, nullptr, LXSS_VM_MODE_VHD_NAME, false); + LxssKey, LXSS_LEGACY_DISTRO_GUID, LXSS_LEGACY_INSTALL_NAME, LXSS_DISTRO_VERSION_LEGACY, basePath.c_str(), configFlags, defaultUid, nullptr, LXSS_VM_MODE_VHD_NAME, nullptr, nullptr, false); _SetDistributionInstalled(LxssKey, LXSS_LEGACY_DISTRO_GUID); } @@ -2561,6 +2573,7 @@ std::vector LxssUserSess return mounts; } +// TODO _Requires_lock_not_held_(m_instanceLock) std::shared_ptr LxssUserSessionImpl::_CreateInstance(_In_opt_ LPCGUID DistroGuid, _In_ ULONG Flags) { @@ -4122,6 +4135,8 @@ LxssUserSessionImpl::s_GetDistributionConfiguration(const DistributionRegistrati // Read the vhd file name and append to the base path. configuration.VhdFilePath = configuration.BasePath / Distro.Read(Property::VhdFileName); + configuration.FsType = Distro.Read(Property::FsType); + configuration.FsMountOptions = Distro.Read(Property::FsMountOptions); configuration.Flags = Distro.Read(Property::Flags); configuration.OsVersion = Distro.Read(Property::OsVersion).value_or(L""); diff --git a/src/windows/service/exe/LxssUserSession.h b/src/windows/service/exe/LxssUserSession.h index b97e7d5114..ea6b90c6bf 100644 --- a/src/windows/service/exe/LxssUserSession.h +++ b/src/windows/service/exe/LxssUserSession.h @@ -164,6 +164,8 @@ class DECLSPEC_UUID("a9b7a1b9-0671-405c-95f1-e0612cb4ce7e") LxssUserSession _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ ULONG64 VhdSize, + _In_ LPCWSTR FsType, + _In_ LPCWSTR FsMountOptions, _In_opt_ LPCWSTR PackageFamilyName, _Out_ LPWSTR* InstalledDistributionName, _Out_ LXSS_ERROR_INFO* Error, @@ -180,6 +182,8 @@ class DECLSPEC_UUID("a9b7a1b9-0671-405c-95f1-e0612cb4ce7e") LxssUserSession _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ ULONG64 VhdSize, + _In_ LPCWSTR FsType, + _In_ LPCWSTR FsMountOptions, _In_opt_ LPCWSTR PackageFamilyName, _Out_ LPWSTR* InstalledDistributionName, _Out_ LXSS_ERROR_INFO* Error, @@ -462,6 +466,8 @@ class LxssUserSessionImpl _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ ULONG64 VhdSize, + _In_ LPCWSTR FsType, + _In_ LPCWSTR FsMountOptions, _In_opt_ LPCWSTR PackageFamilyName, _Out_opt_ LPWSTR* InstalledDistributionName, _Out_ GUID* pDistroGuid); diff --git a/src/windows/service/exe/WslCoreVm.cpp b/src/windows/service/exe/WslCoreVm.cpp index 5a1a60b014..8794429f11 100644 --- a/src/windows/service/exe/WslCoreVm.cpp +++ b/src/windows/service/exe/WslCoreVm.cpp @@ -1222,8 +1222,9 @@ std::shared_ptr WslCoreVm::CreateInstance( message->MountDeviceType = LxMiniInitMountDeviceTypeLun; message->DeviceId = lun; message->Flags = flags; - message.WriteString(message->FsTypeOffset, "ext4"); - message.WriteString(message->MountOptionsOffset, "discard,errors=remount-ro,data=ordered"); + // TODO: add configuration for fsType and mountOptions + message.WriteString(message->FsTypeOffset, Configuration.FsType); + message.WriteString(message->MountOptionsOffset, Configuration.FsMountOptions); message.WriteString(message->VmIdOffset, m_machineId); message.WriteString(message->DistributionNameOffset, Configuration.Name); message.WriteString(message->SharedMemoryRootOffset, sharedMemoryRoot); diff --git a/src/windows/service/inc/wslservice.idl b/src/windows/service/inc/wslservice.idl index 07d993996c..1fbf71b9c8 100644 --- a/src/windows/service/inc/wslservice.idl +++ b/src/windows/service/inc/wslservice.idl @@ -191,6 +191,8 @@ interface ILxssUserSession : IUnknown [in, unique] LPCWSTR TargetDirectory, [in] ULONG Flags, [in] ULONG64 VhdSize, + [in] LPCWSTR FsType, + [in] LPCWSTR FsMountOptions, [in, unique] LPCWSTR PackageFamilyName, [out] LPWSTR* InstalledDistributionName, [in, out] LXSS_ERROR_INFO* Error, @@ -205,6 +207,8 @@ interface ILxssUserSession : IUnknown [in, unique] LPCWSTR TargetDirectory, [in] ULONG Flags, [in] ULONG64 VhdSize, + [in] LPCWSTR FsType, + [in] LPCWSTR FsMountOptions, [in, unique] LPCWSTR PackageFamilyName, [out] LPWSTR* InstalledDistributionName, [in, out] LXSS_ERROR_INFO* Error, From c6f271305c50955ebf2dd0c817458c6ec127606d Mon Sep 17 00:00:00 2001 From: artiga033 Date: Wed, 21 Jan 2026 16:27:22 +0800 Subject: [PATCH 02/20] fix --- src/windows/common/svccomm.cpp | 4 ++-- src/windows/common/svccomm.hpp | 4 ++-- src/windows/service/exe/DistributionRegistration.h | 4 ++-- src/windows/service/exe/LxssUserSession.cpp | 12 ++++++------ src/windows/service/exe/LxssUserSession.h | 12 ++++++------ src/windows/service/inc/wslservice.idl | 11 +++++++---- 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/windows/common/svccomm.cpp b/src/windows/common/svccomm.cpp index 3414570c65..bcdd56d307 100644 --- a/src/windows/common/svccomm.cpp +++ b/src/windows/common/svccomm.cpp @@ -565,8 +565,8 @@ std::pair wsl::windows::common::SvcComm::Reg _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ std::optional VhdSize, - _In_ LPCWSTR FsType, - _In_ LPCWSTR MountOptions, + _In_opt_ LPCWSTR FsType, + _In_opt_ LPCWSTR MountOptions, _In_opt_ LPCWSTR PackageFamilyName) const { ClientExecutionContext context; diff --git a/src/windows/common/svccomm.hpp b/src/windows/common/svccomm.hpp index 73a78bdf2c..7c9258127a 100644 --- a/src/windows/common/svccomm.hpp +++ b/src/windows/common/svccomm.hpp @@ -84,8 +84,8 @@ class SvcComm _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ std::optional VhdSize = std::nullopt, - _In_ LPCWSTR FsType = L"ext4", - _In_ LPCWSTR FsMountOptions = L"discard,errors=remount-ro,data=ordered", + _In_opt_ LPCWSTR FsType = nullptr, + _In_opt_ LPCWSTR FsMountOptions = nullptr, _In_opt_ LPCWSTR PackageFamilyName = nullptr) const; HRESULT diff --git a/src/windows/service/exe/DistributionRegistration.h b/src/windows/service/exe/DistributionRegistration.h index cd67214b44..eadbbe2e9d 100644 --- a/src/windows/service/exe/DistributionRegistration.h +++ b/src/windows/service/exe/DistributionRegistration.h @@ -93,8 +93,8 @@ namespace Property { inline DistributionPropertyWithDefault PackageFamilyName{L"PackageFamilyName", L""}; inline DistributionPropertyWithDefault KernelCommandLine{L"KernelCommandLine", L""}; inline DistributionPropertyWithDefault VhdFileName{L"VhdFileName", LXSS_VM_MODE_VHD_NAME}; - inline DistributionPropertyWithDefault FsType{L"FsType", L"ext4"}; - inline DistributionPropertyWithDefault FsMountOptions{L"FsMountOptions", L"discard,errors=remount-ro,data=ordered"}; + inline DistributionPropertyWithDefault FsType{L"FsType", LXSS_DISTRO_DEFAULT_FS_TYPE}; + inline DistributionPropertyWithDefault FsMountOptions{L"FsMountOptions", LXSS_DISTRO_DEFAULT_FS_MOUNT_OPTIONS}; inline ExpectedProperty Name{L"DistributionName"}; inline ExpectedProperty BasePath{L"BasePath"}; inline DistributionProperty Flavor{L"Flavor"}; diff --git a/src/windows/service/exe/LxssUserSession.cpp b/src/windows/service/exe/LxssUserSession.cpp index 51e8f28c39..a26391d7c8 100644 --- a/src/windows/service/exe/LxssUserSession.cpp +++ b/src/windows/service/exe/LxssUserSession.cpp @@ -383,8 +383,8 @@ HRESULT STDMETHODCALLTYPE LxssUserSession::RegisterDistribution( _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ ULONG64 VhdSize, - _In_ LPCWSTR FsType, - _In_ LPCWSTR FsMountOptions, + _In_opt_ LPCWSTR FsType, + _In_opt_ LPCWSTR FsMountOptions, _In_opt_ LPCWSTR PackageFamilyName, _Out_ LPWSTR* InstalledDistributionName, _Out_ LXSS_ERROR_INFO* Error, @@ -434,8 +434,8 @@ HRESULT STDMETHODCALLTYPE LxssUserSession::RegisterDistributionPipe( _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ ULONG64 VhdSize, - _In_ LPCWSTR FsType, - _In_ LPCWSTR FsMountOptions, + _In_opt_ LPCWSTR FsType, + _In_opt_ LPCWSTR FsMountOptions, _In_opt_ LPCWSTR PackageFamilyName, _Out_ LPWSTR* InstalledDistributionName, _Out_ LXSS_ERROR_INFO* Error, @@ -1389,8 +1389,8 @@ HRESULT LxssUserSessionImpl::RegisterDistribution( _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ ULONG64 VhdSize, - _In_ LPCWSTR FsType, - _In_ LPCWSTR FsMountOptions, + _In_opt_ LPCWSTR FsType, + _In_opt_ LPCWSTR FsMountOptions, _In_opt_ LPCWSTR PackageFamilyName, _Out_opt_ LPWSTR* InstalledDistributionName, _Out_ GUID* pDistroGuid) diff --git a/src/windows/service/exe/LxssUserSession.h b/src/windows/service/exe/LxssUserSession.h index ea6b90c6bf..0b5b256622 100644 --- a/src/windows/service/exe/LxssUserSession.h +++ b/src/windows/service/exe/LxssUserSession.h @@ -164,8 +164,8 @@ class DECLSPEC_UUID("a9b7a1b9-0671-405c-95f1-e0612cb4ce7e") LxssUserSession _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ ULONG64 VhdSize, - _In_ LPCWSTR FsType, - _In_ LPCWSTR FsMountOptions, + _In_opt_ LPCWSTR FsType, + _In_opt_ LPCWSTR FsMountOptions, _In_opt_ LPCWSTR PackageFamilyName, _Out_ LPWSTR* InstalledDistributionName, _Out_ LXSS_ERROR_INFO* Error, @@ -182,8 +182,8 @@ class DECLSPEC_UUID("a9b7a1b9-0671-405c-95f1-e0612cb4ce7e") LxssUserSession _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ ULONG64 VhdSize, - _In_ LPCWSTR FsType, - _In_ LPCWSTR FsMountOptions, + _In_opt_ LPCWSTR FsType, + _In_opt_ LPCWSTR FsMountOptions, _In_opt_ LPCWSTR PackageFamilyName, _Out_ LPWSTR* InstalledDistributionName, _Out_ LXSS_ERROR_INFO* Error, @@ -466,8 +466,8 @@ class LxssUserSessionImpl _In_ LPCWSTR TargetDirectory, _In_ ULONG Flags, _In_ ULONG64 VhdSize, - _In_ LPCWSTR FsType, - _In_ LPCWSTR FsMountOptions, + _In_opt_ LPCWSTR FsType, + _In_opt_ LPCWSTR FsMountOptions, _In_opt_ LPCWSTR PackageFamilyName, _Out_opt_ LPWSTR* InstalledDistributionName, _Out_ GUID* pDistroGuid); diff --git a/src/windows/service/inc/wslservice.idl b/src/windows/service/inc/wslservice.idl index 1fbf71b9c8..aaab24f980 100644 --- a/src/windows/service/inc/wslservice.idl +++ b/src/windows/service/inc/wslservice.idl @@ -102,6 +102,9 @@ cpp_quote("#define LXSS_DISTRO_DEFAULT_ENVIRONMENT \"HOSTTYPE=\" DISTRO_HOSTTYPE cpp_quote("#define LXSS_DISTRO_DEFAULT_KERNEL_COMMAND_LINE \"BOOT_IMAGE=/kernel init=/init\"") +cpp_quote("#define LXSS_DISTRO_DEFAULT_FS_TYPE L\"ext4\"") +cpp_quote("#define LXSS_DISTRO_DEFAULT_FS_MOUNT_OPTIONS L\"discard,errors=remount-ro,data=ordered\"") + cpp_quote("#define LXSS_DISTRO_FLAGS_ENABLE_INTEROP 0x1") cpp_quote("#define LXSS_DISTRO_FLAGS_APPEND_NT_PATH 0x2") cpp_quote("#define LXSS_DISTRO_FLAGS_ENABLE_DRIVE_MOUNTING 0x4") @@ -191,8 +194,8 @@ interface ILxssUserSession : IUnknown [in, unique] LPCWSTR TargetDirectory, [in] ULONG Flags, [in] ULONG64 VhdSize, - [in] LPCWSTR FsType, - [in] LPCWSTR FsMountOptions, + [in, unique] LPCWSTR FsType, + [in, unique] LPCWSTR FsMountOptions, [in, unique] LPCWSTR PackageFamilyName, [out] LPWSTR* InstalledDistributionName, [in, out] LXSS_ERROR_INFO* Error, @@ -207,8 +210,8 @@ interface ILxssUserSession : IUnknown [in, unique] LPCWSTR TargetDirectory, [in] ULONG Flags, [in] ULONG64 VhdSize, - [in] LPCWSTR FsType, - [in] LPCWSTR FsMountOptions, + [in, unique] LPCWSTR FsType, + [in, unique] LPCWSTR FsMountOptions, [in, unique] LPCWSTR PackageFamilyName, [out] LPWSTR* InstalledDistributionName, [in, out] LXSS_ERROR_INFO* Error, From 63690f57f49ddbb8eca663d0f0486ef45478fbc8 Mon Sep 17 00:00:00 2001 From: artiga033 Date: Tue, 20 Jan 2026 23:32:57 +0800 Subject: [PATCH 03/20] support import --- localization/strings/en-US/Resources.resw | 7 +++++++ src/linux/init/main.cpp | 5 +++++ src/windows/common/WslClient.cpp | 14 +++++++++++++- src/windows/inc/wsl.h | 2 ++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw index fec08be4f7..34dfbed35c 100644 --- a/localization/strings/en-US/Resources.resw +++ b/localization/strings/en-US/Resources.resw @@ -581,6 +581,13 @@ Arguments for managing distributions in Windows Subsystem for Linux: Specifies that the provided file is a .vhd or .vhdx file, not a tar file. This operation makes a copy of the VHD file at the specified install location. + --fs-type + Specify the filesystem type to use for the distribution root. + Defaults to ext4. + + --fs-mount-options + Specify additional mount options for the filesystem. + --import-in-place <Distro> <FileName> Imports the specified VHD file as a new distribution. This virtual hard disk must be formatted with the ext4 filesystem type. diff --git a/src/linux/init/main.cpp b/src/linux/init/main.cpp index eddc649958..be92f368d4 100644 --- a/src/linux/init/main.cpp +++ b/src/linux/init/main.cpp @@ -758,6 +758,11 @@ try { CommandLine = std::format("/usr/sbin/mkfs.btrfs '{}'", DevicePath); } + else + { + LOG_ERROR("Unsupported filesystem type: {}", FsType_s); + return -1; + } if (UtilExecCommandLine(CommandLine.c_str(), nullptr) < 0) { return -1; diff --git a/src/windows/common/WslClient.cpp b/src/windows/common/WslClient.cpp index 820d438e6d..dc59ffca35 100644 --- a/src/windows/common/WslClient.cpp +++ b/src/windows/common/WslClient.cpp @@ -313,12 +313,16 @@ int ImportDistribution(_In_ std::wstring_view commandLine) std::filesystem::path filePath; ULONG flags = LXSS_IMPORT_DISTRO_FLAGS_NO_OOBE; DWORD version = LXSS_WSL_VERSION_DEFAULT; + std::optional fsType; + std::optional fsMountOptions; parser.AddPositionalArgument(name, 0); parser.AddPositionalArgument(AbsolutePath(installPath), 1); parser.AddPositionalArgument(filePath, 2); parser.AddArgument(WslVersion(version), WSL_IMPORT_ARG_VERSION); parser.AddArgument(SetFlag{flags}, WSL_IMPORT_ARG_VHD); + parser.AddArgument(fsType, WSL_IMPORT_ARG_FS_TYPE); + parser.AddArgument(fsMountOptions, WSL_IMPORT_ARG_FS_MOUNT_OPTIONS); parser.Parse(); @@ -379,7 +383,15 @@ int ImportDistribution(_In_ std::wstring_view commandLine) { wsl::windows::common::HandleConsoleProgressBar progressBar(fileHandle, Localization::MessageImportProgress()); wsl::windows::common::SvcComm service; - service.RegisterDistribution(name, version, fileHandle, installPath->c_str(), flags); + service.RegisterDistribution( + name, + version, + fileHandle, + installPath->c_str(), + flags, + std::nullopt, + fsType.has_value() ? fsType->c_str() : nullptr, + fsMountOptions.has_value() ? fsMountOptions->c_str() : nullptr); } directory_cleanup.release(); diff --git a/src/windows/inc/wsl.h b/src/windows/inc/wsl.h index d5002705a1..43434b9549 100644 --- a/src/windows/inc/wsl.h +++ b/src/windows/inc/wsl.h @@ -29,6 +29,8 @@ Module Name: #define WSL_EXPORT_ARG_VHD_OPTION L"--vhd" #define WSL_HELP_ARG L"--help" #define WSL_IMPORT_ARG L"--import" +#define WSL_IMPORT_ARG_FS_MOUNT_OPTIONS L"--fs-mount-options" +#define WSL_IMPORT_ARG_FS_TYPE L"--fs-type" #define WSL_IMPORT_ARG_STDIN L"-" #define WSL_IMPORT_ARG_VERSION L"--version" #define WSL_IMPORT_ARG_VHD L"--vhd" From 318fc91d644fde0335599e99fb1d2caf82b87cfe Mon Sep 17 00:00:00 2001 From: artiga033 Date: Wed, 21 Jan 2026 18:01:44 +0800 Subject: [PATCH 04/20] don't check einval because invalid mount options also return this --- src/windows/service/exe/WslCoreInstance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows/service/exe/WslCoreInstance.cpp b/src/windows/service/exe/WslCoreInstance.cpp index 85387ed146..506de9a6bb 100644 --- a/src/windows/service/exe/WslCoreInstance.cpp +++ b/src/windows/service/exe/WslCoreInstance.cpp @@ -66,7 +66,7 @@ WslCoreInstance::WslCoreInstance( if (result.Result != 0) { // N.B. EUCLEAN (117) can be returned if the disk's journal is corrupted. - if ((result.Result == EINVAL || result.Result == 117) && result.FailureStep == LxInitCreateInstanceStepMountDisk) + if ((result.Result == 117) && result.FailureStep == LxInitCreateInstanceStepMountDisk) { THROW_HR(WSL_E_DISK_CORRUPTED); } From 8de8ddb0410e7d24f6d3aacca8f52f047d60b783 Mon Sep 17 00:00:00 2001 From: artiga033 Date: Wed, 21 Jan 2026 20:30:13 +0800 Subject: [PATCH 05/20] support for a default btrfs subvol --- src/linux/init/main.cpp | 97 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 7 deletions(-) diff --git a/src/linux/init/main.cpp b/src/linux/init/main.cpp index be92f368d4..6f21ef4fb4 100644 --- a/src/linux/init/main.cpp +++ b/src/linux/init/main.cpp @@ -137,6 +137,7 @@ int EnableInterface(int Socket, const char* Name); int ExportToSocket(const char* Source, int Socket, int ErrorSocket, unsigned int flags); int FormatDevice(unsigned int Lun, const char* FsType); +int CreateBtrfsSubvolumeOnDevice(unsigned int Lun, const char* MountOptions); std::string GetLunDeviceName(unsigned int Lun); @@ -727,8 +728,8 @@ int FormatDevice(unsigned int Lun, const char* FsType) Routine Description: - This routine formats the specified SCSI device with the ext4 file system. - N.B. The group size was chosen based on the best practices for Linux VHDs: + This routine formats the specified SCSI device with the file system. + N.B. The ext4 group size was chosen based on the best practices for Linux VHDs: https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/best-practices-for-running-linux-on-hyper-v Arguments: @@ -747,20 +748,25 @@ try std::string DevicePath = GetLunDevicePath(Lun); WaitForBlockDevice(DevicePath.c_str()); - + + if (FsType == nullptr) + { + FsType = "ext4"; + } + std::string CommandLine; - std::string FsType_s = std::string(FsType); - if (FsType_s == "ext4") + if (strcmp(FsType, "ext4") == 0) { CommandLine = std::format("/usr/sbin/mkfs.ext4 -G 4096 '{}'", DevicePath); } - else if (FsType_s == "btrfs") + else if (strcmp(FsType, "btrfs") == 0) { CommandLine = std::format("/usr/sbin/mkfs.btrfs '{}'", DevicePath); } else { - LOG_ERROR("Unsupported filesystem type: {}", FsType_s); + LOG_ERROR("Unsupported filesystem type: {}", FsType); + errno = ENOSYS; return -1; } if (UtilExecCommandLine(CommandLine.c_str(), nullptr) < 0) @@ -772,6 +778,77 @@ try } CATCH_RETURN_ERRNO() +int CreateBtrfsSubvolumeOnDevice(unsigned int Lun, const char* MountOptions) +/*++ +Routine Description: + + This routine creates a btrfs subvolume on the specified SCSI device. No-op if there is no subvolume name in the mount options or the subvolume already exists. +Arguments: + Lun - Supplies the LUN number of the SCSI device. + MountOptions - The mount options to use when mounting the device. Subvolume name is extracted from it. +Return Value: + 0 on success, < 0 on failure. +--*/ +try +{ + if (MountOptions == nullptr) + { + return 0; + } + + std::string_view options(MountOptions); + std::string_view subvolName; + + size_t pos = 0; + while (pos < options.size()) + { + size_t end = options.find(',', pos); + if (end == std::string_view::npos) + { + end = options.size(); + } + + std::string_view option = options.substr(pos, end - pos); + if (option.starts_with("subvol=")) + { + subvolName = option.substr(7); // Skip "subvol=" + break; + } + + pos = end + 1; + } + + if (subvolName.empty()) + { + return 0; + } + + std::string DevicePath = GetLunDevicePath(Lun); + std::string TempMountPath; + THROW_LAST_ERROR_IF(CreateTempDirectory("/tmp", TempMountPath) < 0); + + auto unmountOnExit = wil::scope_exit([&TempMountPath]() { + umount(TempMountPath.c_str()); + rmdir(TempMountPath.c_str()); + }); + + THROW_LAST_ERROR_IF(UtilMount(DevicePath.c_str(), TempMountPath.c_str(), "btrfs", 0, nullptr, c_defaultRetryTimeout) < 0); + + std::string SubvolPath = std::format("{}/{}", TempMountPath, subvolName); + + struct stat st; + if (stat(SubvolPath.c_str(), &st) == 0) + { + return 0; + } + + std::string CommandLine = std::format("/usr/sbin/btrfs subvolume create '{}'", SubvolPath); + THROW_LAST_ERROR_IF(UtilExecCommandLine(CommandLine.c_str(), nullptr) < 0); + + return 0; +} +CATCH_RETURN_ERRNO() + std::string GetLunDeviceName(unsigned int Lun) /*++ @@ -2547,6 +2624,12 @@ void ProcessImportExportMessage(gsl::span Buffer, wsl::shared::Socket THROW_LAST_ERROR_IF(FormatDevice(Message->DeviceId, FsType) < 0); } + if (FsType != nullptr && strcmp(FsType, "btrfs") == 0) + { + // create the subvolume if specified in mount options + CreateBtrfsSubvolumeOnDevice(Message->DeviceId, MountOptions); + } + THROW_LAST_ERROR_IF(MountDevice(Message->MountDeviceType, Message->DeviceId, DISTRO_PATH, FsType, Message->Flags, MountOptions) < 0); Result = 0; From 1040cb45db7d78a265b4336304dc9cb0e1a063dc Mon Sep 17 00:00:00 2001 From: artiga033 Date: Wed, 21 Jan 2026 21:47:59 +0800 Subject: [PATCH 06/20] use proper default mount options for non-default fs --- src/windows/service/exe/DistributionRegistration.cpp | 10 ++++++++++ src/windows/service/inc/wslservice.idl | 5 ++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/windows/service/exe/DistributionRegistration.cpp b/src/windows/service/exe/DistributionRegistration.cpp index 15a2e28adf..1103331969 100644 --- a/src/windows/service/exe/DistributionRegistration.cpp +++ b/src/windows/service/exe/DistributionRegistration.cpp @@ -131,6 +131,16 @@ DistributionRegistration DistributionRegistration::Create( { distribution.Write(Property::FsMountOptions, FsMountOptions); } + else if (ARGUMENT_PRESENT(FsType) && wcscmp(FsType, LXSS_DISTRO_DEFAULT_FS_TYPE) != 0) + { + // FsType-specific default mount options + if (wcscmp(FsType, L"ext4") == 0) + distribution.Write(Property::FsMountOptions, LXSS_DISTRO_DEFAULT_FS_MOUNT_OPTIONS_EXT4); + else if (wcscmp(FsType, L"btrfs") == 0) + distribution.Write(Property::FsMountOptions, LXSS_DISTRO_DEFAULT_FS_MOUNT_OPTIONS_BTRFS); + else if (wcscmp(FsType, L"xfs") == 0) + distribution.Write(Property::FsMountOptions, LXSS_DISTRO_DEFAULT_FS_MOUNT_OPTIONS_XFS); + } // Dismiss the scope exit member so the key is persisted. cleanup.release(); diff --git a/src/windows/service/inc/wslservice.idl b/src/windows/service/inc/wslservice.idl index aaab24f980..9235d87d79 100644 --- a/src/windows/service/inc/wslservice.idl +++ b/src/windows/service/inc/wslservice.idl @@ -103,7 +103,10 @@ cpp_quote("#define LXSS_DISTRO_DEFAULT_ENVIRONMENT \"HOSTTYPE=\" DISTRO_HOSTTYPE cpp_quote("#define LXSS_DISTRO_DEFAULT_KERNEL_COMMAND_LINE \"BOOT_IMAGE=/kernel init=/init\"") cpp_quote("#define LXSS_DISTRO_DEFAULT_FS_TYPE L\"ext4\"") -cpp_quote("#define LXSS_DISTRO_DEFAULT_FS_MOUNT_OPTIONS L\"discard,errors=remount-ro,data=ordered\"") +cpp_quote("#define LXSS_DISTRO_DEFAULT_FS_MOUNT_OPTIONS_EXT4 L\"discard,errors=remount-ro,data=ordered\"") +cpp_quote("#define LXSS_DISTRO_DEFAULT_FS_MOUNT_OPTIONS_BTRFS L\"discard\"") +cpp_quote("#define LXSS_DISTRO_DEFAULT_FS_MOUNT_OPTIONS_XFS L\"discard\"") +cpp_quote("#define LXSS_DISTRO_DEFAULT_FS_MOUNT_OPTIONS LXSS_DISTRO_DEFAULT_FS_MOUNT_OPTIONS_EXT4") cpp_quote("#define LXSS_DISTRO_FLAGS_ENABLE_INTEROP 0x1") cpp_quote("#define LXSS_DISTRO_FLAGS_APPEND_NT_PATH 0x2") From 0f0416cc8c0eb72aa5940527d802ff76b0d22f5e Mon Sep 17 00:00:00 2001 From: artiga033 Date: Wed, 21 Jan 2026 22:26:00 +0800 Subject: [PATCH 07/20] complete support for xfs --- src/linux/init/main.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/linux/init/main.cpp b/src/linux/init/main.cpp index 6f21ef4fb4..735d543b3f 100644 --- a/src/linux/init/main.cpp +++ b/src/linux/init/main.cpp @@ -732,6 +732,7 @@ Routine Description: N.B. The ext4 group size was chosen based on the best practices for Linux VHDs: https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/best-practices-for-running-linux-on-hyper-v + N.B. The xfs data section options (-d) is also determined based on the VHD sector size of 1MB, as suggested in the link above. Arguments: Lun - Supplies the LUN number of the SCSI device. @@ -748,12 +749,12 @@ try std::string DevicePath = GetLunDevicePath(Lun); WaitForBlockDevice(DevicePath.c_str()); - + if (FsType == nullptr) { FsType = "ext4"; } - + std::string CommandLine; if (strcmp(FsType, "ext4") == 0) { @@ -763,6 +764,10 @@ try { CommandLine = std::format("/usr/sbin/mkfs.btrfs '{}'", DevicePath); } + else if (strcmp(FsType, "xfs") == 0) + { + CommandLine = std::format("/usr/sbin/mkfs.xfs -s size=4096 -d su=1m,sw=1 '{}'", DevicePath); + } else { LOG_ERROR("Unsupported filesystem type: {}", FsType); @@ -778,16 +783,13 @@ try } CATCH_RETURN_ERRNO() -int CreateBtrfsSubvolumeOnDevice(unsigned int Lun, const char* MountOptions) +int CreateBtrfsSubvolumeOnDevice(unsigned int Lun, const char* MountOptions) /*++ Routine Description: - This routine creates a btrfs subvolume on the specified SCSI device. No-op if there is no subvolume name in the mount options or the subvolume already exists. -Arguments: - Lun - Supplies the LUN number of the SCSI device. - MountOptions - The mount options to use when mounting the device. Subvolume name is extracted from it. -Return Value: - 0 on success, < 0 on failure. + This routine creates a btrfs subvolume on the specified SCSI device. No-op if there is no subvolume name in the mount options +or the subvolume already exists. Arguments: Lun - Supplies the LUN number of the SCSI device. MountOptions - The mount options to +use when mounting the device. Subvolume name is extracted from it. Return Value: 0 on success, < 0 on failure. --*/ try { From bfea05bdfd3fe301179d5b41cba685a830c51f3f Mon Sep 17 00:00:00 2001 From: artiga033 Date: Sat, 31 Jan 2026 13:00:24 +0800 Subject: [PATCH 08/20] cli arg for set fs mount options --- localization/strings/en-US/Resources.resw | 6 ++-- src/windows/common/WslClient.cpp | 8 ++++- src/windows/common/svccomm.cpp | 7 ++++ src/windows/common/svccomm.hpp | 2 ++ src/windows/inc/wsl.h | 1 + src/windows/service/exe/LxssUserSession.cpp | 36 +++++++++++++++++++++ src/windows/service/exe/LxssUserSession.h | 11 +++++++ src/windows/service/inc/wslservice.idl | 5 +++ 8 files changed, 73 insertions(+), 3 deletions(-) diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw index 34dfbed35c..284766b129 100644 --- a/localization/strings/en-US/Resources.resw +++ b/localization/strings/en-US/Resources.resw @@ -506,6 +506,9 @@ Arguments for managing Windows Subsystem for Linux: --compact Compact the VHDX file of a WSL 2 distribution. + --set-fs-mount-options <Options> + Set the filesystem mount options for the distribution root. + --mount <Disk> Attaches and mounts a physical or virtual disk in all WSL 2 distributions. @@ -631,8 +634,7 @@ Arguments for managing distributions in Windows Subsystem for Linux: "}{Locked="--from-file "}{Locked="--legacy "}{Locked="--location "}{Locked="--name "}{Locked="--no-distribution "}{Locked="--no-launch,"}{Locked="--version "}{Locked="--vhd-size "}{Locked="--web-download -"}{Locked="--manage "}{Locked="--move "}{Locked="--set-sparse,"}{Locked="--set-default-user "}{Locked="--resize "}{Locked="--compact -"}{Locked="--mount "}{Locked="--vhd +"}{Locked="--manage "}{Locked="--move "}{Locked="--set-sparse,"}{Locked="--set-default-user "}{Locked="--resize "}{Locked="--compact"}{Locked="--set-fs-mount-options "}{Locked="--mount "}{Locked="--vhd "}{Locked="--bare "}{Locked="--name "}{Locked="--type "}{Locked="--options "}{Locked="--partition "}{Locked="--set-default-version "}{Locked="--shutdown "}{Locked="--force diff --git a/src/windows/common/WslClient.cpp b/src/windows/common/WslClient.cpp index dc59ffca35..50515657b4 100644 --- a/src/windows/common/WslClient.cpp +++ b/src/windows/common/WslClient.cpp @@ -911,6 +911,7 @@ int Manage(_In_ std::wstring_view commandLine) std::optional defaultUser; std::optional resize; bool compact = false; + std::optional fsMountOptions; bool allowUnsafe = false; ArgumentParser parser(std::wstring{commandLine}, WSL_BINARY_NAME, 0); @@ -920,6 +921,7 @@ int Manage(_In_ std::wstring_view commandLine) parser.AddArgument(defaultUser, WSL_MANAGE_ARG_SET_DEFAULT_USER_OPTION_LONG); parser.AddArgument(SizeString(resize), WSL_MANAGE_ARG_RESIZE_OPTION_LONG, WSL_MANAGE_ARG_RESIZE_OPTION); parser.AddArgument(compact, WSL_MANAGE_ARG_COMPACT_OPTION_LONG); + parser.AddArgument(fsMountOptions, WSL_MANAGE_ARG_SET_FS_MOUNT_OPTIONS_LONG); parser.AddArgument(allowUnsafe, WSL_MANAGE_ARG_ALLOW_UNSAFE); parser.Parse(); @@ -928,7 +930,7 @@ int Manage(_In_ std::wstring_view commandLine) wsl::windows::common::SvcComm service; auto distroGuid = service.GetDistributionId(distribution); - if (sparse.has_value() + move.has_value() + defaultUser.has_value() + resize.has_value() + compact != 1) + if (sparse.has_value() + move.has_value() + defaultUser.has_value() + resize.has_value() + compact + fsMountOptions.has_value() != 1) { THROW_HR(WSL_E_INVALID_USAGE); } @@ -982,6 +984,10 @@ int Manage(_In_ std::wstring_view commandLine) progress.End(); THROW_IF_FAILED(result); } + else if (fsMountOptions) + { + service.SetFsMountOptions(&distroGuid, fsMountOptions->c_str()); + } wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS); return 0; diff --git a/src/windows/common/svccomm.cpp b/src/windows/common/svccomm.cpp index bcdd56d307..d4272be96a 100644 --- a/src/windows/common/svccomm.cpp +++ b/src/windows/common/svccomm.cpp @@ -639,6 +639,13 @@ wsl::windows::common::SvcComm::SetSparse(_In_ LPCGUID DistroGuid, _In_ BOOL Spar RETURN_HR(m_userSession->SetSparse(DistroGuid, Sparse, AllowUnsafe, context.OutError())); } +void wsl::windows::common::SvcComm::SetFsMountOptions(_In_ LPCGUID DistroGuid, _In_ LPCWSTR FsMountOptions) const +{ + ClientExecutionContext context; + + THROW_IF_FAILED(m_userSession->SetFsMountOptions(DistroGuid, FsMountOptions, context.OutError())); +} + HRESULT wsl::windows::common::SvcComm::ResizeDistribution(_In_ LPCGUID DistroGuid, _In_ ULONG64 NewSize) const { diff --git a/src/windows/common/svccomm.hpp b/src/windows/common/svccomm.hpp index 7c9258127a..43046436a0 100644 --- a/src/windows/common/svccomm.hpp +++ b/src/windows/common/svccomm.hpp @@ -99,6 +99,8 @@ class SvcComm HRESULT SetSparse(_In_ LPCGUID DistroGuid, _In_ BOOL Sparse, _In_ BOOL AllowUnsafe) const; + void SetFsMountOptions(_In_ LPCGUID DistroGuid, _In_ LPCWSTR FsMountOptions) const; + HRESULT SetVersion(_In_ LPCGUID DistroGuid, _In_ ULONG Version) const; diff --git a/src/windows/inc/wsl.h b/src/windows/inc/wsl.h index 43434b9549..5aa3413649 100644 --- a/src/windows/inc/wsl.h +++ b/src/windows/inc/wsl.h @@ -80,6 +80,7 @@ Module Name: #define WSL_MANAGE_ARG_SET_SPARSE_OPTION_LONG L"--set-sparse" #define WSL_MANAGE_ARG_SET_DEFAULT_USER_OPTION_LONG L"--set-default-user" #define WSL_MANAGE_ARG_COMPACT_OPTION_LONG L"--compact" +#define WSL_MANAGE_ARG_SET_FS_MOUNT_OPTIONS_LONG L"--set-fs-mount-options" #define WSL_MOUNT_ARG L"--mount" #define WSL_MOUNT_ARG_VHD_OPTION_LONG L"--vhd" #define WSL_MOUNT_ARG_BARE_OPTION_LONG L"--bare" diff --git a/src/windows/service/exe/LxssUserSession.cpp b/src/windows/service/exe/LxssUserSession.cpp index a26391d7c8..29d9951018 100644 --- a/src/windows/service/exe/LxssUserSession.cpp +++ b/src/windows/service/exe/LxssUserSession.cpp @@ -486,6 +486,18 @@ try } CATCH_RETURN() +HRESULT STDMETHODCALLTYPE LxssUserSession::SetFsMountOptions(_In_ LPCGUID DistroGuid, _In_ LPCWSTR FsMountOptions, _Out_ LXSS_ERROR_INFO* Error) +try +{ + ServiceExecutionContext context(Error); + + const auto session = m_session.lock(); + RETURN_HR_IF(RPC_E_DISCONNECTED, !session); + + return session->SetFsMountOptions(DistroGuid, FsMountOptions); +} +CATCH_RETURN() + HRESULT STDMETHODCALLTYPE LxssUserSession::ResizeDistribution(_In_ LPCGUID DistroGuid, _In_ HANDLE OutputHandle, _In_ ULONG64 NewSize, _Out_ LXSS_ERROR_INFO* Error) try { @@ -1805,6 +1817,30 @@ try } CATCH_RETURN() +HRESULT LxssUserSessionImpl::SetFsMountOptions(_In_ LPCGUID DistroGuid, _In_ LPCWSTR FsMountOptions) +try +{ + ExecutionContext context(Context::ConfigureDistro); + + WSL_LOG("SetFsMountOptions", TraceLoggingValue(FsMountOptions, "FsMountOptions")); + + const auto userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation); + const wil::unique_hkey lxssKey = s_OpenLxssUserKey(userToken.get()); + std::lock_guard lock(m_instanceLock); + + // Ensure the distribution exists. + auto distribution = DistributionRegistration::Open(lxssKey.get(), *DistroGuid); + + // Write the new mount options. + distribution.Write(Property::FsMountOptions, FsMountOptions); + + // Terminate the distribution so the new settings will take effect. + _TerminateInstanceInternal(&distribution.Id(), false); + + return S_OK; +} +CATCH_RETURN() + HRESULT LxssUserSessionImpl::ResizeDistribution(_In_ LPCGUID DistroGuid, _In_ HANDLE OutputHandle, _In_ ULONG64 NewSize) try { diff --git a/src/windows/service/exe/LxssUserSession.h b/src/windows/service/exe/LxssUserSession.h index 0b5b256622..12aa0d9a42 100644 --- a/src/windows/service/exe/LxssUserSession.h +++ b/src/windows/service/exe/LxssUserSession.h @@ -209,6 +209,11 @@ class DECLSPEC_UUID("a9b7a1b9-0671-405c-95f1-e0612cb4ce7e") LxssUserSession /// IFACEMETHOD(SetSparse)(_In_ LPCGUID DistroGuid, _In_ BOOLEAN Sparse, _In_ BOOLEAN AllowUnsafe, _Out_ LXSS_ERROR_INFO* Error) override; + /// + /// Sets the filesystem mount options for a distribution. + /// + IFACEMETHOD(SetFsMountOptions)(_In_ LPCGUID DistroGuid, _In_ LPCWSTR FsMountOptions, _Out_ LXSS_ERROR_INFO* Error) override; + /// /// Sets the version for a distribution. /// @@ -496,6 +501,12 @@ class LxssUserSessionImpl HRESULT SetSparse(_In_ LPCGUID DistroGuid, _In_ BOOLEAN Sparse, _In_ BOOLEAN AllowUnsafe); + /// + /// Sets the filesystem mount options for a distribution. + /// + HRESULT + SetFsMountOptions(_In_ LPCGUID DistroGuid, _In_ LPCWSTR FsMountOptions); + /// /// Sets the version for a distribution. /// diff --git a/src/windows/service/inc/wslservice.idl b/src/windows/service/inc/wslservice.idl index 9235d87d79..6c345bc351 100644 --- a/src/windows/service/inc/wslservice.idl +++ b/src/windows/service/inc/wslservice.idl @@ -270,6 +270,11 @@ interface ILxssUserSession : IUnknown [in] BOOLEAN AllowUnsafe, [in, out] LXSS_ERROR_INFO* Error); + HRESULT SetFsMountOptions( + [in] LPCGUID DistroGuid, + [in] LPCWSTR FsMountOptions, + [in, out] LXSS_ERROR_INFO* Error); + HRESULT EnumerateDistributions( [out] ULONG *DistributionCount, [out, size_is(, *DistributionCount)] LXSS_ENUMERATE_INFO **Distributions, From 4664f6e3d77003d6bc2b2d7549a7bd1e0e9346dd Mon Sep 17 00:00:00 2001 From: artiga033 Date: Sat, 31 Jan 2026 18:42:25 +0800 Subject: [PATCH 09/20] update localization --- localization/strings/en-US/Resources.resw | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw index 284766b129..ff9edb9ae4 100644 --- a/localization/strings/en-US/Resources.resw +++ b/localization/strings/en-US/Resources.resw @@ -631,7 +631,9 @@ Arguments for managing distributions in Windows Subsystem for Linux: "}{Locked="--debug-shell "}{Locked="--install "}{Locked="--list "}{Locked="--online'"}{Locked="--enable-wsl1 "}{Locked="--fixed-vhd -"}{Locked="--from-file "}{Locked="--legacy +"}{Locked="--from-file "}{Locked="--fs-type +"}{Locked="--fs-mount-options +"}{Locked="--legacy "}{Locked="--location "}{Locked="--name "}{Locked="--no-distribution "}{Locked="--no-launch,"}{Locked="--version "}{Locked="--vhd-size "}{Locked="--web-download "}{Locked="--manage "}{Locked="--move "}{Locked="--set-sparse,"}{Locked="--set-default-user "}{Locked="--resize "}{Locked="--compact"}{Locked="--set-fs-mount-options "}{Locked="--mount "}{Locked="--vhd From 4aed11042fd0bb2783578ee3f6664383838c1da6 Mon Sep 17 00:00:00 2001 From: artiga033 Date: Sat, 31 Jan 2026 19:33:19 +0800 Subject: [PATCH 10/20] update default vhdx name --- src/windows/service/inc/wslservice.idl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows/service/inc/wslservice.idl b/src/windows/service/inc/wslservice.idl index 6c345bc351..95e5c70693 100644 --- a/src/windows/service/inc/wslservice.idl +++ b/src/windows/service/inc/wslservice.idl @@ -147,7 +147,7 @@ cpp_quote("#define LXSS_PLAN9_UNIX_SOCKET L\"fsserver\"") cpp_quote("#define LXSS_VM_MODE_INITRD_NAME L\"initrd.img\"") cpp_quote("#define LXSS_VM_MODE_KERNEL_NAME L\"kernel\"") -cpp_quote("#define LXSS_VM_MODE_VHD_NAME L\"ext4.vhdx\"") +cpp_quote("#define LXSS_VM_MODE_VHD_NAME L\"distro.vhdx\"") cpp_quote("#define LXSS_TOOLS_DIRECTORY L\"tools\"") From 50e3ea00b033d83f29465e2b66dbf4a2081f1ed7 Mon Sep 17 00:00:00 2001 From: artiga033 Date: Sat, 31 Jan 2026 19:48:06 +0800 Subject: [PATCH 11/20] add unit tests --- test/windows/UnitTests.cpp | 144 +++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/test/windows/UnitTests.cpp b/test/windows/UnitTests.cpp index 035323b75a..4cf7772ea4 100644 --- a/test/windows/UnitTests.cpp +++ b/test/windows/UnitTests.cpp @@ -1278,6 +1278,145 @@ class UnitTests VERIFY_ARE_EQUAL(err, L"bsdtar: Error opening archive: Unrecognized archive format\n"); } + TEST_METHOD(ImportDistroWithFsType) + { + WSL2_TEST_ONLY(); + + // + // Test importing a distribution with custom filesystem type. + // Validates ext4, btrfs, and xfs filesystem types. + // + + auto validateOutput = [](LPCWSTR commandLine, LPCWSTR expectedOutput, DWORD expectedExitCode = 0) { + auto [out, err] = LxsstuLaunchWslAndCaptureOutput(commandLine, expectedExitCode); + VERIFY_ARE_EQUAL(expectedOutput, out); + }; + + auto testFsType = [&](LPCWSTR fsType, LPCWSTR expectedFsType) { + const auto distroName = std::format(L"fstype-test-{}", fsType); + + auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { + LxsstuLaunchWsl(std::format(L"--unregister {}", distroName)); + std::filesystem::remove_all(LXSST_IMPORT_DISTRO_TEST_DIR); + }); + + // Import with custom fs type + validateOutput( + std::format(L"--import {} {} \"{}\" --version 2 --fs-type {}", distroName, LXSST_IMPORT_DISTRO_TEST_DIR, g_testDistroPath, fsType).c_str(), + L"The operation completed successfully. \r\n", + 0); + + // Verify the filesystem type is correct by checking /etc/fstab or mount output + auto [mountOut, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L"-d {} -- mount | grep ' / '", distroName)); + VERIFY_IS_TRUE(mountOut.find(expectedFsType) != std::wstring::npos); + + // Cleanup + WslShutdown(); + }; + + // Test ext4 (default) + testFsType(L"ext4", L"ext4"); + + // Test btrfs + testFsType(L"btrfs", L"btrfs"); + + // Test xfs + testFsType(L"xfs", L"xfs"); + } + + TEST_METHOD(ImportDistroWithFsMountOptions) + { + WSL2_TEST_ONLY(); + + // + // Test importing a distribution with custom filesystem mount options. + // + + constexpr auto distroName = L"fsmount-test"; + + auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { + LxsstuLaunchWsl(std::format(L"--unregister {}", distroName)); + std::filesystem::remove_all(LXSST_IMPORT_DISTRO_TEST_DIR); + }); + + // Import with custom mount options + auto [out, err] = LxsstuLaunchWslAndCaptureOutput( + std::format(L"--import {} {} \"{}\" --version 2 --fs-mount-options discard,noatime", distroName, LXSST_IMPORT_DISTRO_TEST_DIR, g_testDistroPath), + 0); + VERIFY_ARE_EQUAL(out, L"The operation completed successfully. \r\n"); + + // Verify the mount options are applied + auto [mountOut, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L"-d {} -- mount | grep ' / '", distroName)); + VERIFY_IS_TRUE(mountOut.find(L"noatime") != std::wstring::npos); + + WslShutdown(); + } + + TEST_METHOD(ImportDistroWithBtrfsSubvol) + { + WSL2_TEST_ONLY(); + + // + // Test importing a distribution with btrfs and subvol mount option. + // + + constexpr auto distroName = L"btrfs-subvol-test"; + + auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { + LxsstuLaunchWsl(std::format(L"--unregister {}", distroName)); + std::filesystem::remove_all(LXSST_IMPORT_DISTRO_TEST_DIR); + }); + + // Import with btrfs and subvol mount option + auto [out, err] = LxsstuLaunchWslAndCaptureOutput( + std::format(L"--import {} {} \"{}\" --version 2 --fs-type btrfs --fs-mount-options subvol=@", distroName, LXSST_IMPORT_DISTRO_TEST_DIR, g_testDistroPath), + 0); + VERIFY_ARE_EQUAL(out, L"The operation completed successfully. \r\n"); + + // Verify the subvolume is used + auto [mountOut, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L"-d {} -- mount | grep ' / '", distroName)); + VERIFY_IS_TRUE(mountOut.find(L"btrfs") != std::wstring::npos); + VERIFY_IS_TRUE(mountOut.find(L"subvol=/@") != std::wstring::npos); + + WslShutdown(); + } + + TEST_METHOD(ManageSetFsMountOptions) + { + WSL2_TEST_ONLY(); + + // + // Test the --manage --set-fs-mount-options command. + // + + constexpr auto distroName = L"manage-fsmount-test"; + + auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { + LxsstuLaunchWsl(std::format(L"--unregister {}", distroName)); + std::filesystem::remove_all(LXSST_IMPORT_DISTRO_TEST_DIR); + }); + + // Import a distribution first + auto [out, err] = LxsstuLaunchWslAndCaptureOutput( + std::format(L"--import {} {} \"{}\" --version 2", distroName, LXSST_IMPORT_DISTRO_TEST_DIR, g_testDistroPath), + 0); + VERIFY_ARE_EQUAL(out, L"The operation completed successfully. \r\n"); + + // Set the mount options using --manage + WslShutdown(); + std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput( + std::format(L"--manage {} --set-fs-mount-options discard,noatime", distroName), + 0); + VERIFY_ARE_EQUAL(out, L"The operation completed successfully. \r\n"); + + // Verify the mount options are applied after restart + auto [mountOut, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L"-d {} -- mount | grep ' / '", distroName)); + VERIFY_IS_TRUE(mountOut.find(L"noatime") != std::wstring::npos); + + WslShutdown(); + } + + TEST_METHOD(AppxDistroDeletion) { // Create a dummy distro registration @@ -1574,6 +1713,11 @@ class UnitTests ValidateErrorMessage(L"--manage test_distro --resize foo", L"Invalid size: foo", L"Wsl/E_INVALIDARG"); + ValidateErrorMessage( + L"--manage DoesNotExist --set-fs-mount-options discard", + L"There is no distribution with the supplied name.", + L"Wsl/Service/WSL_E_DISTRO_NOT_FOUND"); + ValidateErrorMessage( L"--install --distribution debian --no-distribution", L"Arguments --no-distribution and --distribution can't be specified at same time.", From 3a69f854d628f61e1c660a2a99c7aa35b1da78fc Mon Sep 17 00:00:00 2001 From: artiga033 Date: Sun, 8 Mar 2026 11:47:01 +0800 Subject: [PATCH 12/20] update unit tests --- test/windows/UnitTests.cpp | 123 +++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 61 deletions(-) diff --git a/test/windows/UnitTests.cpp b/test/windows/UnitTests.cpp index 4cf7772ea4..a9678cbdc6 100644 --- a/test/windows/UnitTests.cpp +++ b/test/windows/UnitTests.cpp @@ -1330,55 +1330,41 @@ class UnitTests // // Test importing a distribution with custom filesystem mount options. + // Covers ext4, btrfs (with subvol), and xfs filesystem types. // - constexpr auto distroName = L"fsmount-test"; + auto importAndValidate = [&](LPCWSTR testName, const std::wstring& extraArgs, const std::vector& expectedStrings) { + const auto distroName = std::format(L"fsmount-test-{}", testName); - auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { - LxsstuLaunchWsl(std::format(L"--unregister {}", distroName)); - std::filesystem::remove_all(LXSST_IMPORT_DISTRO_TEST_DIR); - }); - - // Import with custom mount options - auto [out, err] = LxsstuLaunchWslAndCaptureOutput( - std::format(L"--import {} {} \"{}\" --version 2 --fs-mount-options discard,noatime", distroName, LXSST_IMPORT_DISTRO_TEST_DIR, g_testDistroPath), - 0); - VERIFY_ARE_EQUAL(out, L"The operation completed successfully. \r\n"); - - // Verify the mount options are applied - auto [mountOut, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L"-d {} -- mount | grep ' / '", distroName)); - VERIFY_IS_TRUE(mountOut.find(L"noatime") != std::wstring::npos); - - WslShutdown(); - } - - TEST_METHOD(ImportDistroWithBtrfsSubvol) - { - WSL2_TEST_ONLY(); + auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { + LxsstuLaunchWsl(std::format(L"--unregister {}", distroName)); + std::filesystem::remove_all(LXSST_IMPORT_DISTRO_TEST_DIR); + }); - // - // Test importing a distribution with btrfs and subvol mount option. - // + auto [out, err] = LxsstuLaunchWslAndCaptureOutput( + std::format(L"--import {} {} \"{}\" --version 2 {}", distroName, LXSST_IMPORT_DISTRO_TEST_DIR, g_testDistroPath, extraArgs), + 0); + VERIFY_ARE_EQUAL(out, L"The operation completed successfully. \r\n"); - constexpr auto distroName = L"btrfs-subvol-test"; + auto [mountOut, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L"-d {} -- mount | grep ' / '", distroName)); + for (const auto& expected : expectedStrings) + { + VERIFY_IS_TRUE(mountOut.find(expected) != std::wstring::npos); + } - auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { - LxsstuLaunchWsl(std::format(L"--unregister {}", distroName)); - std::filesystem::remove_all(LXSST_IMPORT_DISTRO_TEST_DIR); - }); + WslShutdown(); + }; - // Import with btrfs and subvol mount option - auto [out, err] = LxsstuLaunchWslAndCaptureOutput( - std::format(L"--import {} {} \"{}\" --version 2 --fs-type btrfs --fs-mount-options subvol=@", distroName, LXSST_IMPORT_DISTRO_TEST_DIR, g_testDistroPath), - 0); - VERIFY_ARE_EQUAL(out, L"The operation completed successfully. \r\n"); + // Test ext4 with kernel mount options + importAndValidate(L"ext4", L"--fs-mount-options discard,data=writeback", {L"ext4", L"data=writeback"}); - // Verify the subvolume is used - auto [mountOut, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L"-d {} -- mount | grep ' / '", distroName)); - VERIFY_IS_TRUE(mountOut.find(L"btrfs") != std::wstring::npos); - VERIFY_IS_TRUE(mountOut.find(L"subvol=/@") != std::wstring::npos); + // Test btrfs with compress, subvol, and ssd options + importAndValidate(L"btrfs", L"--fs-type btrfs --fs-mount-options compress=zstd,subvol=@,ssd", + {L"btrfs", L"compress=zstd", L"subvol=/@", L"ssd"}); - WslShutdown(); + // Test xfs with quota options (uquota/gquota are displayed as usrquota/grpquota by mount) + importAndValidate(L"xfs", L"--fs-type xfs --fs-mount-options uquota,gquota", + {L"xfs", L"usrquota", L"grpquota"}); } TEST_METHOD(ManageSetFsMountOptions) @@ -1386,34 +1372,49 @@ class UnitTests WSL2_TEST_ONLY(); // - // Test the --manage --set-fs-mount-options command. + // Test the --manage --set-fs-mount-options command across different filesystem types. // - constexpr auto distroName = L"manage-fsmount-test"; + auto testManageMountOptions = [&](LPCWSTR testName, const std::wstring& importExtraArgs, + const std::wstring& mountOptions, const std::vector& expectedStrings) { + const auto distroName = std::format(L"manage-fsmount-{}", testName); - auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { - LxsstuLaunchWsl(std::format(L"--unregister {}", distroName)); - std::filesystem::remove_all(LXSST_IMPORT_DISTRO_TEST_DIR); - }); + auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { + LxsstuLaunchWsl(std::format(L"--unregister {}", distroName)); + std::filesystem::remove_all(LXSST_IMPORT_DISTRO_TEST_DIR); + }); - // Import a distribution first - auto [out, err] = LxsstuLaunchWslAndCaptureOutput( - std::format(L"--import {} {} \"{}\" --version 2", distroName, LXSST_IMPORT_DISTRO_TEST_DIR, g_testDistroPath), - 0); - VERIFY_ARE_EQUAL(out, L"The operation completed successfully. \r\n"); + // Import a distribution + auto [out, err] = LxsstuLaunchWslAndCaptureOutput( + std::format(L"--import {} {} \"{}\" --version 2 {}", distroName, LXSST_IMPORT_DISTRO_TEST_DIR, g_testDistroPath, importExtraArgs), + 0); + VERIFY_ARE_EQUAL(out, L"The operation completed successfully. \r\n"); - // Set the mount options using --manage - WslShutdown(); - std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput( - std::format(L"--manage {} --set-fs-mount-options discard,noatime", distroName), - 0); - VERIFY_ARE_EQUAL(out, L"The operation completed successfully. \r\n"); + // Set the mount options using --manage + WslShutdown(); + std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput( + std::format(L"--manage {} --set-fs-mount-options {}", distroName, mountOptions), + 0); + VERIFY_ARE_EQUAL(out, L"The operation completed successfully. \r\n"); + + // Verify the mount options are applied after restart + auto [mountOut, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L"-d {} -- mount | grep ' / '", distroName)); + for (const auto& expected : expectedStrings) + { + VERIFY_IS_TRUE(mountOut.find(expected) != std::wstring::npos); + } - // Verify the mount options are applied after restart - auto [mountOut, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L"-d {} -- mount | grep ' / '", distroName)); - VERIFY_IS_TRUE(mountOut.find(L"noatime") != std::wstring::npos); + WslShutdown(); + }; - WslShutdown(); + // Test changing data options on ext4 + testManageMountOptions(L"ext4", L"--fs-type ext4", L"data=journal", {L"ext4", L"data=journal"}); + + // Test changing compress option on btrfs + testManageMountOptions(L"btrfs", L"--fs-type btrfs", L"compress=lzo", {L"btrfs", L"compress=lzo"}); + + // Test enabling quotas on xfs + testManageMountOptions(L"xfs", L"--fs-type xfs", L"uquota,gquota", {L"xfs", L"usrquota", L"grpquota"}); } From 91055fa25ce3e496f2e5e64bd533c36aa8676427 Mon Sep 17 00:00:00 2001 From: artiga033 Date: Sun, 8 Mar 2026 13:41:50 +0800 Subject: [PATCH 13/20] remove finished todo comments --- src/windows/service/exe/LxssUserSession.cpp | 1 - src/windows/service/exe/WslCoreVm.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/windows/service/exe/LxssUserSession.cpp b/src/windows/service/exe/LxssUserSession.cpp index 29d9951018..a8562e71d5 100644 --- a/src/windows/service/exe/LxssUserSession.cpp +++ b/src/windows/service/exe/LxssUserSession.cpp @@ -2609,7 +2609,6 @@ std::vector LxssUserSess return mounts; } -// TODO _Requires_lock_not_held_(m_instanceLock) std::shared_ptr LxssUserSessionImpl::_CreateInstance(_In_opt_ LPCGUID DistroGuid, _In_ ULONG Flags) { diff --git a/src/windows/service/exe/WslCoreVm.cpp b/src/windows/service/exe/WslCoreVm.cpp index 8794429f11..0096154289 100644 --- a/src/windows/service/exe/WslCoreVm.cpp +++ b/src/windows/service/exe/WslCoreVm.cpp @@ -1222,7 +1222,6 @@ std::shared_ptr WslCoreVm::CreateInstance( message->MountDeviceType = LxMiniInitMountDeviceTypeLun; message->DeviceId = lun; message->Flags = flags; - // TODO: add configuration for fsType and mountOptions message.WriteString(message->FsTypeOffset, Configuration.FsType); message.WriteString(message->MountOptionsOffset, Configuration.FsMountOptions); message.WriteString(message->VmIdOffset, m_machineId); From b5bcf60fa3876606ff49794ee6b155349eae7e7e Mon Sep 17 00:00:00 2001 From: artiga033 Date: Mon, 9 Mar 2026 10:58:55 +0800 Subject: [PATCH 14/20] fix break of existing tests --- localization/strings/en-US/Resources.resw | 12 +++----- test/windows/UnitTests.cpp | 37 +++++++++++++++++------ 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw index ff9edb9ae4..54338b37b9 100644 --- a/localization/strings/en-US/Resources.resw +++ b/localization/strings/en-US/Resources.resw @@ -456,11 +456,11 @@ Arguments for managing Windows Subsystem for Linux: --from-file <Path> Install a distribution from a local file. - --fs-type + --fs-type <FsType> Specify the filesystem type to use for the distribution root. Defaults to ext4. - --fs-mount-options + --fs-mount-options <Options> Specify additional mount options for the filesystem. --legacy @@ -584,11 +584,11 @@ Arguments for managing distributions in Windows Subsystem for Linux: Specifies that the provided file is a .vhd or .vhdx file, not a tar file. This operation makes a copy of the VHD file at the specified install location. - --fs-type + --fs-type <FsType> Specify the filesystem type to use for the distribution root. Defaults to ext4. - --fs-mount-options + --fs-mount-options <Options> Specify additional mount options for the filesystem. --import-in-place <Distro> <FileName> @@ -631,9 +631,7 @@ Arguments for managing distributions in Windows Subsystem for Linux: "}{Locked="--debug-shell "}{Locked="--install "}{Locked="--list "}{Locked="--online'"}{Locked="--enable-wsl1 "}{Locked="--fixed-vhd -"}{Locked="--from-file "}{Locked="--fs-type -"}{Locked="--fs-mount-options -"}{Locked="--legacy +"}{Locked="--from-file "}{Locked="--fs-type "}{Locked="--fs-mount-options "}{Locked="--legacy "}{Locked="--location "}{Locked="--name "}{Locked="--no-distribution "}{Locked="--no-launch,"}{Locked="--version "}{Locked="--vhd-size "}{Locked="--web-download "}{Locked="--manage "}{Locked="--move "}{Locked="--set-sparse,"}{Locked="--set-default-user "}{Locked="--resize "}{Locked="--compact"}{Locked="--set-fs-mount-options "}{Locked="--mount "}{Locked="--vhd diff --git a/test/windows/UnitTests.cpp b/test/windows/UnitTests.cpp index a9678cbdc6..240768cd99 100644 --- a/test/windows/UnitTests.cpp +++ b/test/windows/UnitTests.cpp @@ -1144,7 +1144,7 @@ class UnitTests { const auto tarFileName = LXSST_IMPORT_DISTRO_TEST_DIR L"test.tar"; const auto rootfsDirectoryName = LXSST_IMPORT_DISTRO_TEST_DIR L"rootfs"; - const auto vhdFileName = LXSST_IMPORT_DISTRO_TEST_DIR L"ext4.vhdx"; + const auto vhdFileName = LXSST_IMPORT_DISTRO_TEST_DIR L"distro.vhdx"; auto cleanup = wil::scope_exit([&] { try { @@ -1436,7 +1436,7 @@ class UnitTests wsl::windows::common::registry::WriteDword(key.get(), nullptr, L"Flags", LXSS_DISTRO_FLAGS_VM_MODE); // Create a dummy vhd - const auto vhdPath = vhdDir.string() + "\\ext4.vhdx"; + const auto vhdPath = vhdDir.string() + "\\distro.vhdx"; wil::unique_handle vhdHandle(CreateFileA(vhdPath.c_str(), GENERIC_READ, 0, nullptr, CREATE_ALWAYS, 0, nullptr)); VERIFY_IS_TRUE(vhdHandle.is_valid()); @@ -1641,7 +1641,7 @@ class UnitTests ValidateErrorMessage( L"-d DummyBrokenDistro", - L"Failed to attach disk 'C:\\DoesNotExit\\ext4.vhdx' to WSL2: The system cannot find the path " + L"Failed to attach disk 'C:\\DoesNotExit\\distro.vhdx' to WSL2: The system cannot find the path " L"specified. ", L"Wsl/Service/CreateInstance/MountDisk/HCS/ERROR_PATH_NOT_FOUND"); @@ -1898,6 +1898,13 @@ Arguments for managing Windows Subsystem for Linux: --from-file Install a distribution from a local file. + --fs-type + Specify the filesystem type to use for the distribution root. + Defaults to ext4. + + --fs-mount-options + Specify additional mount options for the filesystem. + --legacy Use the legacy distribution manifest. @@ -1941,6 +1948,9 @@ Arguments for managing Windows Subsystem for Linux: --compact Compact the VHDX file of a WSL 2 distribution. + --set-fs-mount-options + Set the filesystem mount options for the distribution root. + --mount Attaches and mounts a physical or virtual disk in all WSL 2 distributions. @@ -2016,6 +2026,13 @@ Arguments for managing distributions in Windows Subsystem for Linux: Specifies that the provided file is a .vhd or .vhdx file, not a tar file. This operation makes a copy of the VHD file at the specified install location. + --fs-type + Specify the filesystem type to use for the distribution root. + Defaults to ext4. + + --fs-mount-options + Specify additional mount options for the filesystem. + --import-in-place Imports the specified VHD file as a new distribution. This virtual hard disk must be formatted with the ext4 filesystem type. @@ -3247,7 +3264,7 @@ Error code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND // Validate that the distribution still starts validateDistro(); - VERIFY_IS_TRUE(std::filesystem::exists(std::format(L"{}\\ext4.vhdx", testFolder))); + VERIFY_IS_TRUE(std::filesystem::exists(std::format(L"{}\\distro.vhdx", testFolder))); } auto absolutePath = wsl::windows::common::filesystem::GetCanonicalPath(".").wstring(); @@ -3259,7 +3276,7 @@ Error code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND // Validate that the distribution still starts validateDistro(); - VERIFY_IS_TRUE(std::filesystem::exists(std::format(L"{}\\ext4.vhdx", absolutePath))); + VERIFY_IS_TRUE(std::filesystem::exists(std::format(L"{}\\distro.vhdx", absolutePath))); } // Try to move the distribution to a folder that's already in use @@ -3277,7 +3294,7 @@ Error code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND L"Wsl/Service/MoveDistro/ERROR_FILE_EXISTS\r\n"); // Validate that the distribution still starts and that the vhd hasn't moved. validateDistro(); - VERIFY_IS_TRUE(std::filesystem::exists(std::format(L"{}\\ext4.vhdx", absolutePath))); + VERIFY_IS_TRUE(std::filesystem::exists(std::format(L"{}\\distro.vhdx", absolutePath))); } // Try to move the distribution to an invalid path @@ -3292,7 +3309,7 @@ Error code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND L"Wsl/Service/MoveDistro/ERROR_INVALID_NAME\r\n"); // Validate that the distribution still starts and that the vhd hasn't moved. validateDistro(); - VERIFY_IS_TRUE(std::filesystem::exists(std::format(L"{}\\ext4.vhdx", absolutePath))); + VERIFY_IS_TRUE(std::filesystem::exists(std::format(L"{}\\distro.vhdx", absolutePath))); } } @@ -3726,8 +3743,8 @@ Error code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND // std::pair[0] = Written value, std::pair[1] = Actual/Expected value static const std::vector> filePathsToTest{ - {L"C:\\DoesNotExit\\ext4.vhdx", L"C:\\DoesNotExit\\ext4.vhdx"}, - {L"\\DoesNotExit\\ext4.vhdx", L"\\DoesNotExit\\ext4.vhdx"}, + {L"C:\\DoesNotExit\\distro.vhdx", L"C:\\DoesNotExit\\distro.vhdx"}, + {L"\\DoesNotExit\\distro.vhdx", L"\\DoesNotExit\\distro.vhdx"}, {L"", L""}, }; @@ -3748,7 +3765,7 @@ Error code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND {L"", L""}, {L"notaport", L""}, {L"-5555", L""}, - {L"C:\\DoesNotExit\\ext4.vhdx", L""}, + {L"C:\\DoesNotExit\\distro.vhdx", L""}, }, }, { From d6e5d6626ea23515f485ed04d5d972efd338bc7d Mon Sep 17 00:00:00 2001 From: artiga033 Date: Mon, 9 Mar 2026 20:51:47 +0800 Subject: [PATCH 15/20] code style fix --- src/linux/init/main.cpp | 18 ++++++++++++++---- src/windows/common/svccomm.cpp | 6 +++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/linux/init/main.cpp b/src/linux/init/main.cpp index 735d543b3f..48aef763c1 100644 --- a/src/linux/init/main.cpp +++ b/src/linux/init/main.cpp @@ -729,10 +729,12 @@ int FormatDevice(unsigned int Lun, const char* FsType) Routine Description: This routine formats the specified SCSI device with the file system. + N.B. The ext4 group size was chosen based on the best practices for Linux VHDs: https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/best-practices-for-running-linux-on-hyper-v N.B. The xfs data section options (-d) is also determined based on the VHD sector size of 1MB, as suggested in the link above. + Arguments: Lun - Supplies the LUN number of the SCSI device. @@ -785,11 +787,19 @@ CATCH_RETURN_ERRNO() int CreateBtrfsSubvolumeOnDevice(unsigned int Lun, const char* MountOptions) /*++ -Routine Description: + This routine creates a btrfs subvolume on the specified SCSI device. + It is a no-op if there is no subvolume name in the mount options or + the subvolume already exists. + +Arguments: - This routine creates a btrfs subvolume on the specified SCSI device. No-op if there is no subvolume name in the mount options -or the subvolume already exists. Arguments: Lun - Supplies the LUN number of the SCSI device. MountOptions - The mount options to -use when mounting the device. Subvolume name is extracted from it. Return Value: 0 on success, < 0 on failure. + Lun - Supplies the LUN number of the SCSI device. + MountOptions - The mount options to use when mounting the device. + The subvolume name is extracted from these options. + +Return Value: + + 0 on success, < 0 on failure. --*/ try { diff --git a/src/windows/common/svccomm.cpp b/src/windows/common/svccomm.cpp index d4272be96a..00a5a81b66 100644 --- a/src/windows/common/svccomm.cpp +++ b/src/windows/common/svccomm.cpp @@ -566,7 +566,7 @@ std::pair wsl::windows::common::SvcComm::Reg _In_ ULONG Flags, _In_ std::optional VhdSize, _In_opt_ LPCWSTR FsType, - _In_opt_ LPCWSTR MountOptions, + _In_opt_ LPCWSTR FsMountOptions, _In_opt_ LPCWSTR PackageFamilyName) const { ClientExecutionContext context; @@ -593,7 +593,7 @@ std::pair wsl::windows::common::SvcComm::Reg Flags, VhdSize.value_or(0), FsType, - MountOptions, + FsMountOptions, PackageFamilyName, &installedName, context.OutError(), @@ -610,7 +610,7 @@ std::pair wsl::windows::common::SvcComm::Reg Flags, VhdSize.value_or(0), FsType, - MountOptions, + FsMountOptions, PackageFamilyName, &installedName, context.OutError(), From 467e0227c89afbf6eebd6e489431b88ccc105b4b Mon Sep 17 00:00:00 2001 From: artiga033 Date: Mon, 9 Mar 2026 21:16:14 +0800 Subject: [PATCH 16/20] minor fixes --- src/linux/init/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/linux/init/main.cpp b/src/linux/init/main.cpp index 48aef763c1..9619977c0d 100644 --- a/src/linux/init/main.cpp +++ b/src/linux/init/main.cpp @@ -854,6 +854,8 @@ try return 0; } + THROW_LAST_ERROR_IF(errno != ENOENT); + std::string CommandLine = std::format("/usr/sbin/btrfs subvolume create '{}'", SubvolPath); THROW_LAST_ERROR_IF(UtilExecCommandLine(CommandLine.c_str(), nullptr) < 0); @@ -2639,7 +2641,7 @@ void ProcessImportExportMessage(gsl::span Buffer, wsl::shared::Socket if (FsType != nullptr && strcmp(FsType, "btrfs") == 0) { // create the subvolume if specified in mount options - CreateBtrfsSubvolumeOnDevice(Message->DeviceId, MountOptions); + THROW_LAST_ERROR_IF(CreateBtrfsSubvolumeOnDevice(Message->DeviceId, MountOptions) < 0); } THROW_LAST_ERROR_IF(MountDevice(Message->MountDeviceType, Message->DeviceId, DISTRO_PATH, FsType, Message->Flags, MountOptions) < 0); From 1838e9744ab46b095b258df6caf8deb473ab275d Mon Sep 17 00:00:00 2001 From: artiga033 Date: Fri, 13 Mar 2026 11:42:32 +0800 Subject: [PATCH 17/20] handle CreateInstance mount failure error It is hard to distinguish between a corrupted disk and bad fsType/fsMountOptions for failures of mounting the rootfs. This change renamed the WSL_E_DISK_CORRUPTED into WSL_E_MOUNT_FAILED, and changed the error message to inform the user the possible causes of the failure. --- localization/strings/en-US/Resources.resw | 4 ++-- src/windows/common/wslutil.cpp | 6 +++--- src/windows/service/exe/WslCoreInstance.cpp | 4 ++-- src/windows/service/inc/wslservice.idl | 2 +- test/windows/UnitTests.cpp | 12 ++++++------ 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw index 54338b37b9..c36f081802 100644 --- a/localization/strings/en-US/Resources.resw +++ b/localization/strings/en-US/Resources.resw @@ -1123,8 +1123,8 @@ Falling back to NAT networking. The distribution failed to start. Error code: {}, failure step: {} {FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated - - The distribution failed to start because its virtual disk is corrupted. + + The distribution failed to start because its virtual disk is corrupted, of non-expected filesystem, or invalid mount options. Duplicated config key '{}' in {}:{} (Conflicting key: '{}' in {}:{}) diff --git a/src/windows/common/wslutil.cpp b/src/windows/common/wslutil.cpp index 2e914a0696..f2b86b94d6 100644 --- a/src/windows/common/wslutil.cpp +++ b/src/windows/common/wslutil.cpp @@ -84,7 +84,7 @@ static const std::map g_commonErrors{ X(WSL_E_WSL1_DISABLED), X(WSL_E_VIRTUAL_MACHINE_PLATFORM_REQUIRED), X(WSL_E_LOCAL_SYSTEM_NOT_SUPPORTED), - X(WSL_E_DISK_CORRUPTED), + X(WSL_E_MOUNT_FAILED), X(WSL_E_DISTRIBUTION_NAME_NEEDED), X(WSL_E_INVALID_JSON), X(WSL_E_VM_CRASHED), @@ -884,8 +884,8 @@ std::wstring wsl::windows::common::wslutil::GetErrorString(HRESULT result) case WSL_E_LOCAL_SYSTEM_NOT_SUPPORTED: return Localization::MessageLocalSystemNotSupported(); - case WSL_E_DISK_CORRUPTED: - return Localization::MessageDiskCorrupted(); + case WSL_E_MOUNT_FAILED: + return Localization::MessageMountFailed(); case WSL_E_NOT_A_LINUX_DISTRO: return Localization::MessageInvalidDistributionTar(); diff --git a/src/windows/service/exe/WslCoreInstance.cpp b/src/windows/service/exe/WslCoreInstance.cpp index 506de9a6bb..7dd31498a8 100644 --- a/src/windows/service/exe/WslCoreInstance.cpp +++ b/src/windows/service/exe/WslCoreInstance.cpp @@ -66,9 +66,9 @@ WslCoreInstance::WslCoreInstance( if (result.Result != 0) { // N.B. EUCLEAN (117) can be returned if the disk's journal is corrupted. - if ((result.Result == 117) && result.FailureStep == LxInitCreateInstanceStepMountDisk) + if ((result.Result == EINVAL || result.Result == 117) && result.FailureStep == LxInitCreateInstanceStepMountDisk) { - THROW_HR(WSL_E_DISK_CORRUPTED); + THROW_HR(WSL_E_MOUNT_FAILED); } else { diff --git a/src/windows/service/inc/wslservice.idl b/src/windows/service/inc/wslservice.idl index 95e5c70693..6b5c0dcf22 100644 --- a/src/windows/service/inc/wslservice.idl +++ b/src/windows/service/inc/wslservice.idl @@ -413,7 +413,7 @@ cpp_quote("#define WSL_E_DISK_MOUNT_DISABLED MAKE_HRESULT(SEVERITY_ERROR, FACILI cpp_quote("#define WSL_E_WSL1_DISABLED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x2C) /* 0x8004032C */") cpp_quote("#define WSL_E_VIRTUAL_MACHINE_PLATFORM_REQUIRED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x2D) /* 0x8004032D */") cpp_quote("#define WSL_E_LOCAL_SYSTEM_NOT_SUPPORTED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x2E) /* 0x8004032E */") -cpp_quote("#define WSL_E_DISK_CORRUPTED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x2F) /* 0x8004032F */") +cpp_quote("#define WSL_E_MOUNT_FAILED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x2F) /* 0x8004032F */") cpp_quote("#define WSL_E_DISTRIBUTION_NAME_NEEDED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x30) /* 0x80040330 */") cpp_quote("#define WSL_E_INVALID_JSON MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x31) /* 0x80040331 */") cpp_quote("#define WSL_E_VM_CRASHED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x32) /* 0x80040332 */") diff --git a/test/windows/UnitTests.cpp b/test/windows/UnitTests.cpp index 240768cd99..2699350140 100644 --- a/test/windows/UnitTests.cpp +++ b/test/windows/UnitTests.cpp @@ -2866,14 +2866,14 @@ Error code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND // Validate that starting the distribution fails with the correct error code. validateOutput( L"-d BrokenDistro echo ok", - L"The distribution failed to start because its virtual disk is corrupted.\r\n" - L"Error code: Wsl/Service/CreateInstance/WSL_E_DISK_CORRUPTED\r\n"); + L"The distribution failed to start because its virtual disk is corrupted, of non-expected filesystem, or invalid mount options.\r\n" + L"Error code: Wsl/Service/CreateInstance/WSL_E_MOUNT_FAILED\r\n"); // Validate that trying to export the distribution fails with the correct error code. validateOutput( L"--export BrokenDistro dummy.tar", - L"The distribution failed to start because its virtual disk is corrupted.\r\n" - L"Error code: Wsl/Service/WSL_E_DISK_CORRUPTED\r\n"); + L"The distribution failed to start because its virtual disk is corrupted, of non-expected filesystem, or invalid mount options.\r\n" + L"Error code: Wsl/Service/WSL_E_MOUNT_FAILED\r\n"); // Shutdown WSL to force the disk to detach. VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--shutdown"), 0L); @@ -2882,8 +2882,8 @@ Error code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND // Import a corrupted vhd. validateOutput( std::format(L"--import-in-place test-distro-corrupted \"{}\"", vhdPath.wstring()), - L"The distribution failed to start because its virtual disk is corrupted.\r\n" - L"Error code: Wsl/Service/RegisterDistro/WSL_E_DISK_CORRUPTED\r\n"); + L"The distribution failed to start because its virtual disk is corrupted, of non-expected filesystem, or invalid mount options.\r\n" + L"Error code: Wsl/Service/RegisterDistro/WSL_E_MOUNT_FAILED\r\n"); // Ensure the VHD can be deleted to make sure it was properly ejected from the VM. VERIFY_ARE_EQUAL(DeleteFileW(vhdPath.c_str()), TRUE); From a1ed198ccc513754108c464c0ad3a5bac2773612 Mon Sep 17 00:00:00 2001 From: artiga033 Date: Fri, 13 Mar 2026 19:33:22 +0800 Subject: [PATCH 18/20] apply some copilot reviews --- localization/strings/en-US/Resources.resw | 2 +- src/linux/init/main.cpp | 19 +++++++++++-------- src/windows/service/inc/wslservice.idl | 10 +++++----- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw index c36f081802..3df349b4fe 100644 --- a/localization/strings/en-US/Resources.resw +++ b/localization/strings/en-US/Resources.resw @@ -1124,7 +1124,7 @@ Falling back to NAT networking. {FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated - The distribution failed to start because its virtual disk is corrupted, of non-expected filesystem, or invalid mount options. + The distribution failed to start because its virtual disk is corrupted, has an unexpected filesystem type, or has invalid mount options. Duplicated config key '{}' in {}:{} (Conflicting key: '{}' in {}:{}) diff --git a/src/linux/init/main.cpp b/src/linux/init/main.cpp index 9619977c0d..e7ddc496f5 100644 --- a/src/linux/init/main.cpp +++ b/src/linux/init/main.cpp @@ -66,6 +66,9 @@ Module Name: #define BSDTAR_PATH "/usr/bin/bsdtar" #define BINFMT_REGISTER_STRING BINFMT_INTEROP_REGISTRATION_STRING_VM(LX_INIT_BINFMT_NAME) "\n" +#define BTRFS_CREATE_ARG "create" +#define BTRFS_PATH "/usr/sbin/btrfs" +#define BTRFS_SUBVOLUME_ARG "subvolume" #define BINFMT_PATH PROCFS_PATH "/sys/fs/binfmt_misc" #define CHRONY_CONF_PATH ETC_PATH "/chrony.conf" #define CHRONYD_PATH "/sbin/chronyd" @@ -733,7 +736,7 @@ Routine Description: N.B. The ext4 group size was chosen based on the best practices for Linux VHDs: https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/best-practices-for-running-linux-on-hyper-v - N.B. The xfs data section options (-d) is also determined based on the VHD sector size of 1MB, as suggested in the link above. + N.B. The xfs data section options (-d) are also determined based on the VHD sector size of 1MB, as suggested in the link above. Arguments: @@ -856,8 +859,8 @@ try THROW_LAST_ERROR_IF(errno != ENOENT); - std::string CommandLine = std::format("/usr/sbin/btrfs subvolume create '{}'", SubvolPath); - THROW_LAST_ERROR_IF(UtilExecCommandLine(CommandLine.c_str(), nullptr) < 0); + const char* Argv[] = {BTRFS_PATH, BTRFS_SUBVOLUME_ARG, BTRFS_CREATE_ARG, SubvolPath.c_str(), nullptr}; + THROW_LAST_ERROR_IF(UtilCreateProcessAndWait(Argv[0], Argv) < 0); return 0; } @@ -2636,12 +2639,12 @@ void ProcessImportExportMessage(gsl::span Buffer, wsl::shared::Socket if (Message->Header.MessageType == LxMiniInitMessageImport) { THROW_LAST_ERROR_IF(FormatDevice(Message->DeviceId, FsType) < 0); - } - if (FsType != nullptr && strcmp(FsType, "btrfs") == 0) - { - // create the subvolume if specified in mount options - THROW_LAST_ERROR_IF(CreateBtrfsSubvolumeOnDevice(Message->DeviceId, MountOptions) < 0); + if (FsType != nullptr && strcmp(FsType, "btrfs") == 0) + { + // create the subvolume if specified in mount options + THROW_LAST_ERROR_IF(CreateBtrfsSubvolumeOnDevice(Message->DeviceId, MountOptions) < 0); + } } THROW_LAST_ERROR_IF(MountDevice(Message->MountDeviceType, Message->DeviceId, DISTRO_PATH, FsType, Message->Flags, MountOptions) < 0); diff --git a/src/windows/service/inc/wslservice.idl b/src/windows/service/inc/wslservice.idl index 6b5c0dcf22..c981a834ad 100644 --- a/src/windows/service/inc/wslservice.idl +++ b/src/windows/service/inc/wslservice.idl @@ -270,11 +270,6 @@ interface ILxssUserSession : IUnknown [in] BOOLEAN AllowUnsafe, [in, out] LXSS_ERROR_INFO* Error); - HRESULT SetFsMountOptions( - [in] LPCGUID DistroGuid, - [in] LPCWSTR FsMountOptions, - [in, out] LXSS_ERROR_INFO* Error); - HRESULT EnumerateDistributions( [out] ULONG *DistributionCount, [out, size_is(, *DistributionCount)] LXSS_ENUMERATE_INFO **Distributions, @@ -366,6 +361,11 @@ interface ILxssUserSession : IUnknown HRESULT CompactDistribution( [in] LPCGUID DistroGuid, [in, out] LXSS_ERROR_INFO* Error); + + HRESULT SetFsMountOptions( + [in] LPCGUID DistroGuid, + [in] LPCWSTR FsMountOptions, + [in, out] LXSS_ERROR_INFO* Error); }; From 7a65fc7086a8455a86c379373656791e014d44a6 Mon Sep 17 00:00:00 2001 From: artiga033 Date: Sun, 15 Mar 2026 14:15:57 +0800 Subject: [PATCH 19/20] fix unit test --- test/windows/UnitTests.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/windows/UnitTests.cpp b/test/windows/UnitTests.cpp index 2699350140..0cb6c39828 100644 --- a/test/windows/UnitTests.cpp +++ b/test/windows/UnitTests.cpp @@ -2866,13 +2866,13 @@ Error code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND // Validate that starting the distribution fails with the correct error code. validateOutput( L"-d BrokenDistro echo ok", - L"The distribution failed to start because its virtual disk is corrupted, of non-expected filesystem, or invalid mount options.\r\n" + L"The distribution failed to start because its virtual disk is corrupted, has an unexpected filesystem type, or has invalid mount options.\r\n" L"Error code: Wsl/Service/CreateInstance/WSL_E_MOUNT_FAILED\r\n"); // Validate that trying to export the distribution fails with the correct error code. validateOutput( L"--export BrokenDistro dummy.tar", - L"The distribution failed to start because its virtual disk is corrupted, of non-expected filesystem, or invalid mount options.\r\n" + L"The distribution failed to start because its virtual disk is corrupted, has an unexpected filesystem type, or has invalid mount options.\r\n" L"Error code: Wsl/Service/WSL_E_MOUNT_FAILED\r\n"); // Shutdown WSL to force the disk to detach. @@ -2882,7 +2882,7 @@ Error code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND // Import a corrupted vhd. validateOutput( std::format(L"--import-in-place test-distro-corrupted \"{}\"", vhdPath.wstring()), - L"The distribution failed to start because its virtual disk is corrupted, of non-expected filesystem, or invalid mount options.\r\n" + L"The distribution failed to start because its virtual disk is corrupted, has an unexpected filesystem type, or has invalid mount options.\r\n" L"Error code: Wsl/Service/RegisterDistro/WSL_E_MOUNT_FAILED\r\n"); // Ensure the VHD can be deleted to make sure it was properly ejected from the VM. From bbcadd8b9f754407cf820dac9101df0826e5efdc Mon Sep 17 00:00:00 2001 From: artiga033 Date: Wed, 13 May 2026 18:25:39 +0800 Subject: [PATCH 20/20] fix unit test WSL version filtering Accoring to 813070c88b6a5180c5c58873626aa066238d50ec Co-authored-by: Copilot --- test/windows/UnitTests.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/test/windows/UnitTests.cpp b/test/windows/UnitTests.cpp index 0cb6c39828..02d8afa94d 100644 --- a/test/windows/UnitTests.cpp +++ b/test/windows/UnitTests.cpp @@ -1278,10 +1278,8 @@ class UnitTests VERIFY_ARE_EQUAL(err, L"bsdtar: Error opening archive: Unrecognized archive format\n"); } - TEST_METHOD(ImportDistroWithFsType) + WSL2_TEST_METHOD(ImportDistroWithFsType) { - WSL2_TEST_ONLY(); - // // Test importing a distribution with custom filesystem type. // Validates ext4, btrfs, and xfs filesystem types. @@ -1324,10 +1322,8 @@ class UnitTests testFsType(L"xfs", L"xfs"); } - TEST_METHOD(ImportDistroWithFsMountOptions) + WSL2_TEST_METHOD(ImportDistroWithFsMountOptions) { - WSL2_TEST_ONLY(); - // // Test importing a distribution with custom filesystem mount options. // Covers ext4, btrfs (with subvol), and xfs filesystem types. @@ -1367,10 +1363,8 @@ class UnitTests {L"xfs", L"usrquota", L"grpquota"}); } - TEST_METHOD(ManageSetFsMountOptions) + WSL2_TEST_METHOD(ManageSetFsMountOptions) { - WSL2_TEST_ONLY(); - // // Test the --manage --set-fs-mount-options command across different filesystem types. //