diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw
index 0ffff8fd7a..3df349b4fe 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 <FsType>
+ Specify the filesystem type to use for the distribution root.
+ Defaults to ext4.
+
+ --fs-mount-options <Options>
+ Specify additional mount options for the filesystem.
+
--legacy
Use the legacy distribution manifest.
@@ -499,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.
@@ -574,6 +584,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 <FsType>
+ Specify the filesystem type to use for the distribution root.
+ Defaults to ext4.
+
+ --fs-mount-options <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.
@@ -614,11 +631,10 @@ 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="--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
@@ -1107,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, 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 a07cc25850..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"
@@ -136,7 +139,8 @@ 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);
+int CreateBtrfsSubvolumeOnDevice(unsigned int Lun, const char* MountOptions);
std::string GetLunDeviceName(unsigned int Lun);
@@ -721,19 +725,23 @@ Return Value:
return Result;
}
-int FormatDevice(unsigned int Lun)
+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
+ 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:
Lun - Supplies the LUN number of the SCSI device.
+ FsType - The filesystem type to format the device with.
Return Value:
@@ -747,7 +755,30 @@ try
WaitForBlockDevice(DevicePath.c_str());
- std::string CommandLine = std::format("/usr/sbin/mkfs.ext4 -G 4096 '{}'", DevicePath);
+ if (FsType == nullptr)
+ {
+ FsType = "ext4";
+ }
+
+ std::string CommandLine;
+ if (strcmp(FsType, "ext4") == 0)
+ {
+ CommandLine = std::format("/usr/sbin/mkfs.ext4 -G 4096 '{}'", DevicePath);
+ }
+ else if (strcmp(FsType, "btrfs") == 0)
+ {
+ 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);
+ errno = ENOSYS;
+ return -1;
+ }
if (UtilExecCommandLine(CommandLine.c_str(), nullptr) < 0)
{
return -1;
@@ -757,6 +788,84 @@ try
}
CATCH_RETURN_ERRNO()
+int CreateBtrfsSubvolumeOnDevice(unsigned int Lun, const char* MountOptions)
+/*++
+ 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:
+
+ 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
+{
+ 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;
+ }
+
+ THROW_LAST_ERROR_IF(errno != ENOENT);
+
+ 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;
+}
+CATCH_RETURN_ERRNO()
+
std::string GetLunDeviceName(unsigned int Lun)
/*++
@@ -2524,13 +2633,20 @@ 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);
+
+ if (FsType != nullptr && strcmp(FsType, "btrfs") == 0)
+ {
+ // create the subvolume if specified in mount options
+ THROW_LAST_ERROR_IF(CreateBtrfsSubvolumeOnDevice(Message->DeviceId, MountOptions) < 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..50515657b4 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();
@@ -452,6 +464,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 +483,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 +541,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 +569,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())
@@ -893,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);
@@ -902,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();
@@ -910,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);
}
@@ -964,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/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..00a5a81b66 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_opt_ LPCWSTR FsType,
+ _In_opt_ LPCWSTR FsMountOptions,
_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,
+ FsMountOptions,
PackageFamilyName,
&installedName,
context.OutError(),
@@ -605,6 +609,8 @@ std::pair wsl::windows::common::SvcComm::Reg
TargetDirectory,
Flags,
VhdSize.value_or(0),
+ FsType,
+ FsMountOptions,
PackageFamilyName,
&installedName,
context.OutError(),
@@ -633,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 47e13d50a6..43046436a0 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_opt_ LPCWSTR FsType = nullptr,
+ _In_opt_ LPCWSTR FsMountOptions = nullptr,
_In_opt_ LPCWSTR PackageFamilyName = nullptr) const;
HRESULT
@@ -97,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/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/inc/wsl.h b/src/windows/inc/wsl.h
index f9bb847f54..5aa3413649 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"
@@ -40,6 +42,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"
@@ -76,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/DistributionRegistration.cpp b/src/windows/service/exe/DistributionRegistration.cpp
index 03cdc26f09..1103331969 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,26 @@ 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);
+ }
+ 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();
return distribution;
diff --git a/src/windows/service/exe/DistributionRegistration.h b/src/windows/service/exe/DistributionRegistration.h
index 1da0848a91..eadbbe2e9d 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", 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/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..a8562e71d5 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_opt_ LPCWSTR FsType,
+ _In_opt_ 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_opt_ LPCWSTR FsType,
+ _In_opt_ 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()
@@ -480,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
{
@@ -1329,6 +1347,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 +1401,8 @@ HRESULT LxssUserSessionImpl::RegisterDistribution(
_In_ LPCWSTR TargetDirectory,
_In_ ULONG Flags,
_In_ ULONG64 VhdSize,
+ _In_opt_ LPCWSTR FsType,
+ _In_opt_ LPCWSTR FsMountOptions,
_In_opt_ LPCWSTR PackageFamilyName,
_Out_opt_ LPWSTR* InstalledDistributionName,
_Out_ GUID* pDistroGuid)
@@ -1519,6 +1541,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);
@@ -1793,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
{
@@ -2535,7 +2583,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);
}
@@ -4122,6 +4170,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..12aa0d9a42 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_opt_ LPCWSTR FsType,
+ _In_opt_ 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_opt_ LPCWSTR FsType,
+ _In_opt_ LPCWSTR FsMountOptions,
_In_opt_ LPCWSTR PackageFamilyName,
_Out_ LPWSTR* InstalledDistributionName,
_Out_ LXSS_ERROR_INFO* Error,
@@ -205,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.
///
@@ -462,6 +471,8 @@ class LxssUserSessionImpl
_In_ LPCWSTR TargetDirectory,
_In_ ULONG Flags,
_In_ ULONG64 VhdSize,
+ _In_opt_ LPCWSTR FsType,
+ _In_opt_ LPCWSTR FsMountOptions,
_In_opt_ LPCWSTR PackageFamilyName,
_Out_opt_ LPWSTR* InstalledDistributionName,
_Out_ GUID* pDistroGuid);
@@ -490,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/exe/WslCoreInstance.cpp b/src/windows/service/exe/WslCoreInstance.cpp
index 85387ed146..7dd31498a8 100644
--- a/src/windows/service/exe/WslCoreInstance.cpp
+++ b/src/windows/service/exe/WslCoreInstance.cpp
@@ -68,7 +68,7 @@ WslCoreInstance::WslCoreInstance(
// N.B. EUCLEAN (117) can be returned if the disk's journal is corrupted.
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/exe/WslCoreVm.cpp b/src/windows/service/exe/WslCoreVm.cpp
index 5a1a60b014..0096154289 100644
--- a/src/windows/service/exe/WslCoreVm.cpp
+++ b/src/windows/service/exe/WslCoreVm.cpp
@@ -1222,8 +1222,8 @@ 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");
+ 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..c981a834ad 100644
--- a/src/windows/service/inc/wslservice.idl
+++ b/src/windows/service/inc/wslservice.idl
@@ -102,6 +102,12 @@ 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_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")
cpp_quote("#define LXSS_DISTRO_FLAGS_ENABLE_DRIVE_MOUNTING 0x4")
@@ -141,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\"")
@@ -191,6 +197,8 @@ interface ILxssUserSession : IUnknown
[in, unique] LPCWSTR TargetDirectory,
[in] ULONG Flags,
[in] ULONG64 VhdSize,
+ [in, unique] LPCWSTR FsType,
+ [in, unique] LPCWSTR FsMountOptions,
[in, unique] LPCWSTR PackageFamilyName,
[out] LPWSTR* InstalledDistributionName,
[in, out] LXSS_ERROR_INFO* Error,
@@ -205,6 +213,8 @@ interface ILxssUserSession : IUnknown
[in, unique] LPCWSTR TargetDirectory,
[in] ULONG Flags,
[in] ULONG64 VhdSize,
+ [in, unique] LPCWSTR FsType,
+ [in, unique] LPCWSTR FsMountOptions,
[in, unique] LPCWSTR PackageFamilyName,
[out] LPWSTR* InstalledDistributionName,
[in, out] LXSS_ERROR_INFO* Error,
@@ -351,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);
};
@@ -398,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 035323b75a..02d8afa94d 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
{
@@ -1278,6 +1278,140 @@ class UnitTests
VERIFY_ARE_EQUAL(err, L"bsdtar: Error opening archive: Unrecognized archive format\n");
}
+ WSL2_TEST_METHOD(ImportDistroWithFsType)
+ {
+ //
+ // 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");
+ }
+
+ WSL2_TEST_METHOD(ImportDistroWithFsMountOptions)
+ {
+ //
+ // Test importing a distribution with custom filesystem mount options.
+ // Covers ext4, btrfs (with subvol), and xfs filesystem types.
+ //
+
+ 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);
+ });
+
+ 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");
+
+ auto [mountOut, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L"-d {} -- mount | grep ' / '", distroName));
+ for (const auto& expected : expectedStrings)
+ {
+ VERIFY_IS_TRUE(mountOut.find(expected) != std::wstring::npos);
+ }
+
+ WslShutdown();
+ };
+
+ // Test ext4 with kernel mount options
+ importAndValidate(L"ext4", L"--fs-mount-options discard,data=writeback", {L"ext4", L"data=writeback"});
+
+ // 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"});
+
+ // 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"});
+ }
+
+ WSL2_TEST_METHOD(ManageSetFsMountOptions)
+ {
+ //
+ // Test the --manage --set-fs-mount-options command across different filesystem types.
+ //
+
+ 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);
+ });
+
+ // 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 {}", 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);
+ }
+
+ 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"});
+ }
+
+
TEST_METHOD(AppxDistroDeletion)
{
// Create a dummy distro registration
@@ -1296,7 +1430,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());
@@ -1501,7 +1635,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");
@@ -1574,6 +1708,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.",
@@ -1753,6 +1892,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.
@@ -1796,6 +1942,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.
@@ -1871,6 +2020,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.
@@ -2704,14 +2860,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, 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.\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, 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.
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--shutdown"), 0L);
@@ -2720,8 +2876,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, 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.
VERIFY_ARE_EQUAL(DeleteFileW(vhdPath.c_str()), TRUE);
@@ -3102,7 +3258,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();
@@ -3114,7 +3270,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
@@ -3132,7 +3288,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
@@ -3147,7 +3303,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)));
}
}
@@ -3581,8 +3737,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""},
};
@@ -3603,7 +3759,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""},
},
},
{