mirror of
https://github.com/microsoft/WSL.git
synced 2025-07-03 15:23:22 +00:00
1520 lines
52 KiB
C++
1520 lines
52 KiB
C++
![]() |
/*++
|
||
|
|
||
|
Copyright (c) Microsoft. All rights reserved.
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
MountTests.cpp
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This file contains test cases for the disk mounting logic.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "precomp.h"
|
||
|
#include "Common.h"
|
||
|
|
||
|
#define TEST_MOUNT_DISK L"TestDisk.vhd"
|
||
|
#define TEST_MOUNT_VHD L"TestVhd.vhd"
|
||
|
#define TEST_UNMOUNT_VHD_DNE L"TestVhdNotHere.vhd"
|
||
|
#define TEST_MOUNT_NAME L"testmount"
|
||
|
|
||
|
#define SKIP_UNSUPPORTED_ARM64_MOUNT_TEST() \
|
||
|
if constexpr (wsl::shared::Arm64) \
|
||
|
{ \
|
||
|
WSL_TEST_VERSION_REQUIRED(27653); \
|
||
|
}
|
||
|
|
||
|
namespace MountTests {
|
||
|
|
||
|
// Disks sometimes take a bit of time to become available when attached back to the host.
|
||
|
constexpr auto c_diskOpenTimeoutMs = 120000;
|
||
|
|
||
|
class SetAutoMountPolicy
|
||
|
{
|
||
|
public:
|
||
|
SetAutoMountPolicy() = delete;
|
||
|
SetAutoMountPolicy(const SetAutoMountPolicy&) = delete;
|
||
|
SetAutoMountPolicy& operator=(const SetAutoMountPolicy&) = delete;
|
||
|
|
||
|
SetAutoMountPolicy(SetAutoMountPolicy&& Other) = default;
|
||
|
SetAutoMountPolicy& operator=(SetAutoMountPolicy&&) = default;
|
||
|
|
||
|
SetAutoMountPolicy(bool Enable) : PreviousState(GetAutoMountState())
|
||
|
{
|
||
|
if (Enable != PreviousState)
|
||
|
{
|
||
|
SetAutoMountState(Enable);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PreviousState.reset();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
~SetAutoMountPolicy()
|
||
|
{
|
||
|
if (PreviousState.has_value())
|
||
|
{
|
||
|
SetAutoMountState(PreviousState.value());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
static bool GetAutoMountStateFromOutput(const std::wstring& Output)
|
||
|
{
|
||
|
if (Output.find(L"Automatic mounting of new volumes enabled") != std::wstring::npos)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
else if (Output.find(L"Automatic mounting of new volumes disabled") != std::wstring::npos)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
LogError("Unexpected diskpart output: '%s'", Output.c_str());
|
||
|
VERIFY_FAIL(L"Failed to parse diskpart's output");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static bool GetAutoMountState()
|
||
|
{
|
||
|
std::wstring cmd = L"diskpart.exe";
|
||
|
return GetAutoMountStateFromOutput(LxsstuLaunchCommandAndCaptureOutput(cmd.data(), "automount\r\n").first);
|
||
|
}
|
||
|
|
||
|
static void SetAutoMountState(bool Enabled)
|
||
|
{
|
||
|
LogInfo("Setting automount policy to %i", Enabled);
|
||
|
|
||
|
std::wstring cmd = L"diskpart.exe";
|
||
|
const auto input = std::string("automount ") + (Enabled ? "enable\r\n" : "disable\r\n");
|
||
|
auto [output, _] = LxsstuLaunchCommandAndCaptureOutput(cmd.data(), input.c_str());
|
||
|
|
||
|
VERIFY_ARE_EQUAL(Enabled, GetAutoMountStateFromOutput(output));
|
||
|
}
|
||
|
|
||
|
std::optional<bool> PreviousState;
|
||
|
};
|
||
|
|
||
|
class MountTests
|
||
|
{
|
||
|
std::wstring DiskDevice;
|
||
|
std::wstring VhdDevice;
|
||
|
wil::unique_tokeninfo_ptr<TOKEN_USER> User = wil::get_token_information<TOKEN_USER>();
|
||
|
std::unique_ptr<wsl::windows::common::security::privilege_context> PrivilegeState;
|
||
|
DWORD DiskNumber = 0;
|
||
|
SetAutoMountPolicy AutoMountPolicy{false};
|
||
|
|
||
|
struct ExpectedMountState
|
||
|
{
|
||
|
size_t PartitionIndex;
|
||
|
std::optional<std::wstring> Type;
|
||
|
std::optional<std::wstring> Options;
|
||
|
};
|
||
|
|
||
|
struct ExpectedDiskState
|
||
|
{
|
||
|
std::wstring Path;
|
||
|
std::vector<ExpectedMountState> Mounts;
|
||
|
};
|
||
|
|
||
|
WSL_TEST_CLASS(MountTests)
|
||
|
|
||
|
TEST_CLASS_SETUP(TestClassSetup)
|
||
|
{
|
||
|
VERIFY_ARE_EQUAL(LxsstuInitialize(false), TRUE);
|
||
|
|
||
|
if (!LxsstuVmMode())
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Needed to open processes under te
|
||
|
PrivilegeState = wsl::windows::common::security::AcquirePrivilege(SE_DEBUG_NAME);
|
||
|
|
||
|
// Create a 20MB vhd for testing mounting passthrough disks
|
||
|
DeleteFileW(TEST_MOUNT_DISK);
|
||
|
|
||
|
try
|
||
|
{
|
||
|
LxsstuLaunchPowershellAndCaptureOutput(L"New-Vhd -Path " TEST_MOUNT_DISK " -SizeBytes 20MB");
|
||
|
}
|
||
|
CATCH_LOG()
|
||
|
|
||
|
// Mount it in Windows
|
||
|
auto [output, _] = LxsstuLaunchPowershellAndCaptureOutput(L"(Mount-VHD " TEST_MOUNT_DISK " -PassThru | Get-Disk).Number");
|
||
|
|
||
|
Trim(output);
|
||
|
DiskNumber = std::stoul(output);
|
||
|
|
||
|
// Construct the disk path
|
||
|
DiskDevice = L"\\\\.\\PhysicalDrive" + output;
|
||
|
LogInfo("Mounted the passthrough test vhd as %ls", DiskDevice.c_str());
|
||
|
|
||
|
// Create a 20MB vhd for testing mount --vhd
|
||
|
DeleteFileW(TEST_MOUNT_VHD);
|
||
|
|
||
|
LxsstuLaunchPowershellAndCaptureOutput(L"New-Vhd -Path " TEST_MOUNT_VHD " -SizeBytes 20MB");
|
||
|
|
||
|
VhdDevice = wsl::windows::common::filesystem::GetFullPath(TEST_MOUNT_VHD);
|
||
|
LogInfo("Create mount --vhd test vhd as %ls", VhdDevice.c_str());
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Uninitialize the tests.
|
||
|
TEST_CLASS_CLEANUP(TestClassCleanup)
|
||
|
{
|
||
|
if (LxsstuVmMode())
|
||
|
{
|
||
|
PrivilegeState.reset();
|
||
|
|
||
|
LxsstuLaunchWsl(L"--unmount");
|
||
|
WaitForDiskReady();
|
||
|
|
||
|
try
|
||
|
{
|
||
|
LxsstuLaunchPowershellAndCaptureOutput(L"Dismount-Vhd -Path " TEST_MOUNT_DISK);
|
||
|
}
|
||
|
CATCH_LOG()
|
||
|
|
||
|
DeleteFileW(TEST_MOUNT_DISK);
|
||
|
DeleteFileW(TEST_MOUNT_VHD);
|
||
|
}
|
||
|
|
||
|
VERIFY_NO_THROW(LxsstuUninitialize(false));
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
TEST_METHOD_CLEANUP(MethodCleanup)
|
||
|
{
|
||
|
if (!LxsstuVmMode())
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
LxssLogKernelOutput();
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount"), (DWORD)0);
|
||
|
WaitForDiskReady();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Attach a vhd, but don't mount it
|
||
|
TEST_METHOD(TestBareMountVhd)
|
||
|
{
|
||
|
TestBareMountImpl(true);
|
||
|
}
|
||
|
|
||
|
// Mount one partition using --vhd and validate that options are correctly applied
|
||
|
TEST_METHOD(TestMountOnePartitionVhd)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestMountOnePartitionImpl(true);
|
||
|
}
|
||
|
|
||
|
// Mount two partitions using --vhd on the same disk
|
||
|
TEST_METHOD(TestMountTwoPartitionsVhd)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestMountTwoPartitionsImpl(true);
|
||
|
}
|
||
|
|
||
|
// Run a bare mount using --vhd and then mount a partition
|
||
|
TEST_METHOD(TestAttachThenMountVhd)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestAttachThenMountImpl(true);
|
||
|
}
|
||
|
|
||
|
// Mount the disk directly
|
||
|
TEST_METHOD(TestMountWholeDiskVhd)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestMountWholeDiskImpl(true);
|
||
|
}
|
||
|
|
||
|
// Test that mount state is deleted on shutdown (--vhd)
|
||
|
TEST_METHOD(TestMountStateIsDeletedOnShutdownVhd)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestMountStateIsDeletedOnShutdownImpl(true);
|
||
|
}
|
||
|
|
||
|
TEST_METHOD(TestFilesystemDetectionWholeDisk)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestFilesystemDetectionWholeDiskImpl(false);
|
||
|
}
|
||
|
|
||
|
TEST_METHOD(TestFilesystemDetectionWholeDiskVhd)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestFilesystemDetectionWholeDiskImpl(true);
|
||
|
}
|
||
|
|
||
|
TEST_METHOD(TestMountTwoPartitionsWithDetection)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestMountTwoPartitionsWithDetectionImpl(false);
|
||
|
}
|
||
|
|
||
|
TEST_METHOD(TestMountTwoPartitionsWithDetectionVhd)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestMountTwoPartitionsWithDetectionImpl(true);
|
||
|
}
|
||
|
|
||
|
TEST_METHOD(TestFilesystemDetectionFail)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestFilesystemDetectionFailImpl(false);
|
||
|
}
|
||
|
|
||
|
TEST_METHOD(TestFilesystemDetectionFailVhd)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestFilesystemDetectionFailImpl(true);
|
||
|
}
|
||
|
|
||
|
// Test specifying a mount name for a vhd
|
||
|
TEST_METHOD(SpecifyMountName)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
const auto mountCommand = L"--mount " + VhdDevice + L" --vhd --name " + TEST_MOUNT_NAME;
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Create a MBR disk with 1 ext4 partition
|
||
|
FormatDisk({L"ext4"}, true);
|
||
|
|
||
|
// Mount it
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --partition 1"), (DWORD)0);
|
||
|
auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// Validate that the mount succeeded
|
||
|
const std::wstring diskName(TEST_MOUNT_NAME);
|
||
|
auto mountTarget = L"/mnt/wsl/" + diskName;
|
||
|
|
||
|
ValidateMountPoint(disk + L"1", mountTarget);
|
||
|
|
||
|
ValidateDiskState({VhdDevice, {{1, {}, {}}}}, keepAlive);
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + VhdDevice), (DWORD)0);
|
||
|
WaitForDiskReady();
|
||
|
|
||
|
// Validate that the mount folder was deleted
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"test -e " + mountTarget), (DWORD)1);
|
||
|
|
||
|
// Mount the same partition, but with a specific mount option
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --partition 1 --options \"data=ordered\""), (DWORD)0);
|
||
|
|
||
|
// Validate that the mount option was properly passed
|
||
|
disk = GetBlockDeviceInWsl();
|
||
|
ValidateMountPoint(disk + L"1", mountTarget, L"data=ordered");
|
||
|
ValidateDiskState({VhdDevice, {{1, {}, L"data=ordered"}}}, keepAlive);
|
||
|
|
||
|
// Let the VM timeout
|
||
|
WaitForVmTimeout(keepAlive);
|
||
|
|
||
|
// Validate that the disk is re-mounted in the same place
|
||
|
disk = GetBlockDeviceInWsl();
|
||
|
ValidateMountPoint(disk + L"1", mountTarget);
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + VhdDevice), (DWORD)0);
|
||
|
WaitForDiskReady();
|
||
|
}
|
||
|
|
||
|
// Test ensuring that name collision detection works in --mount --name
|
||
|
TEST_METHOD(SpecifyMountNameCollision)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
const auto mountCommand = L"--mount " + VhdDevice + L" --vhd --name " + TEST_MOUNT_NAME;
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Create a MBR disk with 1 ext4 partition and one fat partitions
|
||
|
FormatDisk({L"ext4", L"vfat"}, true);
|
||
|
|
||
|
// Attempt to mount both partitions with the same mount name; partition 2 should fail
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --partition 1"), (DWORD)0);
|
||
|
VERIFY_ARE_NOT_EQUAL(LxsstuLaunchWsl(mountCommand + L" --partition 2 --type vfat"), (DWORD)0);
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// Validate that the mount first mount did succeed
|
||
|
const std::wstring diskName(TEST_MOUNT_NAME);
|
||
|
ValidateMountPoint(disk + L"1", L"/mnt/wsl/" + diskName, {}, L"ext4");
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + VhdDevice), (DWORD)0);
|
||
|
WaitForDiskReady();
|
||
|
}
|
||
|
|
||
|
// Test that multiple partitions can be mounted with --name
|
||
|
TEST_METHOD(SpecifyMountNameTwoPartitions)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
const auto mountCommandOne = L"--mount " + VhdDevice + L" --vhd --name " + TEST_MOUNT_NAME + L"p1";
|
||
|
const auto mountCommandTwo = L"--mount " + VhdDevice + L" --vhd --name " + TEST_MOUNT_NAME + L"p2";
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Create a MBR disk with 1 ext4 partition and one fat partitions
|
||
|
FormatDisk({L"ext4", L"vfat"}, true);
|
||
|
|
||
|
// Attempt to mount both partitions with the same mount name; partition 2 should fail
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommandOne + L" --partition 1"), (DWORD)0);
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommandTwo + L" --partition 2 --type vfat"), (DWORD)0);
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// Validate that the mount first mount did succeed
|
||
|
const std::wstring diskName(TEST_MOUNT_NAME);
|
||
|
ValidateMountPoint(disk + L"1", L"/mnt/wsl/" + diskName + L"p1", {}, L"ext4");
|
||
|
ValidateMountPoint(disk + L"2", L"/mnt/wsl/" + diskName + L"p2", {}, L"vfat");
|
||
|
ValidateDiskState({VhdDevice, {{1, {}, {}}, {2, {L"vfat"}, {}}}}, keepAlive);
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + VhdDevice), (DWORD)0);
|
||
|
WaitForDiskReady();
|
||
|
}
|
||
|
|
||
|
// Test relative mount/unmounting of a --vhd
|
||
|
TEST_METHOD(RelativePathUnmount)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " TEST_MOUNT_VHD L" --vhd --bare"), (DWORD)0);
|
||
|
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " TEST_MOUNT_VHD), (DWORD)0);
|
||
|
}
|
||
|
|
||
|
// Test relative mount/unmounting of a --vhd that does not exist
|
||
|
TEST_METHOD(RelativePathUnmountNoFileExists)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " TEST_MOUNT_VHD L" --vhd --bare"), (DWORD)0);
|
||
|
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// Try unmounting a VHD not created and verify that it was not successful
|
||
|
VERIFY_ARE_NOT_EQUAL(LxsstuLaunchWsl(L"--unmount " TEST_UNMOUNT_VHD_DNE), (DWORD)0);
|
||
|
}
|
||
|
|
||
|
TEST_METHOD(AbsolutePathVhdUnmount)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " TEST_MOUNT_VHD L" --vhd --bare"), (DWORD)0);
|
||
|
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
const auto absolutePath = std::filesystem::absolute(TEST_MOUNT_VHD);
|
||
|
|
||
|
// Validate that the vhd path doesn't start with '\\?'
|
||
|
VERIFY_IS_FALSE(absolutePath.wstring().starts_with(L"\\"));
|
||
|
|
||
|
// Validate the unmounting by absolute path is successful
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + absolutePath.wstring()), (DWORD)0);
|
||
|
}
|
||
|
|
||
|
// Attach a disk, but don't mount it
|
||
|
TEST_METHOD(TestBareMount)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestBareMountImpl(false);
|
||
|
}
|
||
|
|
||
|
// Validate that attached disks that were offline when attached
|
||
|
// are still offline when detached
|
||
|
TEST_METHOD(TestOfflineDiskStaysOffline)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
auto diskHandle = wsl::windows::common::disk::OpenDevice(DiskDevice.c_str(), GENERIC_ALL, c_diskOpenTimeoutMs);
|
||
|
wsl::windows::common::disk::SetOnline(diskHandle.get(), false);
|
||
|
diskHandle.reset();
|
||
|
|
||
|
ValidateOffline(true);
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " + DiskDevice + L" --bare"), (DWORD)0);
|
||
|
|
||
|
auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
ValidateDiskState({DiskDevice, {}}, keepAlive);
|
||
|
|
||
|
disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_FALSE(GetBlockDeviceMount(disk).has_value());
|
||
|
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + DiskDevice), (DWORD)0);
|
||
|
|
||
|
ValidateOffline(true);
|
||
|
diskHandle = wsl::windows::common::disk::OpenDevice(DiskDevice.c_str(), GENERIC_ALL, c_diskOpenTimeoutMs);
|
||
|
wsl::windows::common::disk::SetOnline(diskHandle.get(), true);
|
||
|
diskHandle.reset();
|
||
|
|
||
|
ValidateOffline(false);
|
||
|
}
|
||
|
|
||
|
// Mount one partition and validate that options are correctly applied
|
||
|
TEST_METHOD(TestMountOnePartition)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestMountOnePartitionImpl(false);
|
||
|
}
|
||
|
|
||
|
// Mount two partitions on the same disk
|
||
|
TEST_METHOD(TestMountTwoPartitions)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestMountTwoPartitionsImpl(false);
|
||
|
}
|
||
|
|
||
|
// Mount a fat partition
|
||
|
TEST_METHOD(TestMountFatPartition)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Create a MBR disk with 1 ntfs partition
|
||
|
FormatDisk({L"vfat"}, false);
|
||
|
|
||
|
// Mount it
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " + DiskDevice + L" --partition 1" + L" --type vfat"), (DWORD)0);
|
||
|
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// Validate that the mount succeeded
|
||
|
std::wstring trimmedDiskName(DiskDevice);
|
||
|
Trim(trimmedDiskName);
|
||
|
auto mountTarget = L"/mnt/wsl/" + trimmedDiskName + L"p1";
|
||
|
ValidateMountPoint(disk + L"1", mountTarget, {}, L"vfat");
|
||
|
ValidateDiskState({DiskDevice, {{1, {L"vfat"}, {}}}}, keepAlive);
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + DiskDevice), (DWORD)0);
|
||
|
WaitForDiskReady();
|
||
|
ValidateOffline(false);
|
||
|
}
|
||
|
|
||
|
// Mount the disk directly
|
||
|
TEST_METHOD(TestMountWholeDisk)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestMountWholeDiskImpl(false);
|
||
|
}
|
||
|
|
||
|
TEST_METHOD(TestMountStateIsDeletedOnShutdown)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestMountStateIsDeletedOnShutdownImpl(false);
|
||
|
}
|
||
|
|
||
|
// Validate that a failure to mount a disk isn't fatal
|
||
|
TEST_METHOD(TestMountFailuresArentFatal)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Create a MBR disk with 1 ext4 partition
|
||
|
FormatDisk({L"ext4"}, false);
|
||
|
|
||
|
// Mount it
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " + DiskDevice + L" --partition 1 --type ext4"), (DWORD)0);
|
||
|
auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
ValidateDiskState({DiskDevice, {{1, {L"ext4"}, {}}}}, keepAlive);
|
||
|
|
||
|
// Check that the disk is still mounted properly (ValidateDiskState restarts the VM)
|
||
|
disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
std::wstring trimmedDiskName(DiskDevice);
|
||
|
Trim(trimmedDiskName);
|
||
|
ValidateMountPoint(disk + L"1", L"/mnt/wsl/" + trimmedDiskName + L"p1", {}, L"ext4");
|
||
|
|
||
|
// Wait for vm timeout
|
||
|
WaitForVmTimeout(keepAlive);
|
||
|
|
||
|
// Voluntarily set a wrong filesystem in the saved state
|
||
|
auto key = wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(User->User.Sid);
|
||
|
auto subKeys = wsl::windows::common::registry::EnumKeys(key.get(), KEY_ALL_ACCESS);
|
||
|
VERIFY_ARE_EQUAL(subKeys.size(), 1);
|
||
|
|
||
|
wsl::windows::common::registry::WriteString(subKeys.begin()->second.get(), L"1", L"Type", L"badfs");
|
||
|
keepAlive.Set();
|
||
|
|
||
|
// The disk should be present
|
||
|
disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// But not mounted
|
||
|
ValidateMountPoint(disk + L"1", {});
|
||
|
|
||
|
// Now put a bad disk path, so that the disk fails to attach
|
||
|
WaitForVmTimeout(keepAlive);
|
||
|
key = wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(User->User.Sid);
|
||
|
subKeys = wsl::windows::common::registry::EnumKeys(key.get(), KEY_ALL_ACCESS);
|
||
|
VERIFY_ARE_EQUAL(subKeys.size(), 1);
|
||
|
wsl::windows::common::registry::WriteString(subKeys.begin()->second.get(), nullptr, L"Disk", L"BadDisk");
|
||
|
keepAlive.Reset();
|
||
|
|
||
|
// Restart the service
|
||
|
RestartWslService();
|
||
|
|
||
|
// Run a dummy command to trigger a VM start
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"echo foo"), (DWORD)0);
|
||
|
|
||
|
// The disk should still be online, because it failed to attach
|
||
|
ValidateOffline(false);
|
||
|
}
|
||
|
|
||
|
// wsl --unmount should succeed even when no disk is mounted
|
||
|
TEST_METHOD(UnmountWithoutAnyDisk)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount"), (DWORD)0);
|
||
|
}
|
||
|
|
||
|
// Mount two partitions on the same disk and validate that the mount is restored
|
||
|
TEST_METHOD(TestMountTwoPartitionsAfterTimeout)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Create a MBR disk with 1 ext4 partition and one fat partitions
|
||
|
FormatDisk({L"ext4", L"vfat"}, false);
|
||
|
|
||
|
// Mount then both
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " + DiskDevice + L" --partition 1"), (DWORD)0);
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " + DiskDevice + L" --partition 2 --type vfat"), (DWORD)0);
|
||
|
|
||
|
ValidateDiskState({DiskDevice, {{1, {}, {}}, {2, {L"vfat"}, {}}}}, keepAlive);
|
||
|
|
||
|
// Validate that our disk is still mounted
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// Validate that the mount succeeded
|
||
|
std::wstring trimmedDiskName(DiskDevice);
|
||
|
Trim(trimmedDiskName);
|
||
|
|
||
|
ValidateMountPoint(disk + L"1", L"/mnt/wsl/" + trimmedDiskName + L"p1", {}, L"ext4");
|
||
|
ValidateMountPoint(disk + L"2", L"/mnt/wsl/" + trimmedDiskName + L"p2", {}, L"vfat");
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + DiskDevice), (DWORD)0);
|
||
|
}
|
||
|
|
||
|
// Validate that non-admin can remount saved disks
|
||
|
TEST_METHOD(TestMount1PartitionAndRemountAsNonAdmin)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
FormatDisk({L"ext4"}, false);
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " + DiskDevice + L" --partition 1"), (DWORD)0);
|
||
|
|
||
|
ValidateDiskState({DiskDevice, {{1, {}, {}}}}, keepAlive);
|
||
|
auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// Let the UVM timeout
|
||
|
WaitForVmTimeout(keepAlive);
|
||
|
|
||
|
// Restart wsl as a non-elevated user
|
||
|
const auto nonElevatedToken = GetNonElevatedToken();
|
||
|
|
||
|
// Launch wsl non-elevated
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"echo dummy", nullptr, nullptr, nullptr, nonElevatedToken.get()), (DWORD)0);
|
||
|
keepAlive.Set();
|
||
|
|
||
|
// Validate that our disk is still attached
|
||
|
disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// Validate that the mount succeeded
|
||
|
std::wstring trimmedDiskName(DiskDevice);
|
||
|
Trim(trimmedDiskName);
|
||
|
|
||
|
ValidateMountPoint(disk + L"1", L"/mnt/wsl/" + trimmedDiskName + L"p1", {}, L"ext4");
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + DiskDevice), (DWORD)0);
|
||
|
}
|
||
|
|
||
|
// Run a bare mount and then mount a partition
|
||
|
TEST_METHOD(TestAttachThenMount)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
|
||
|
TestAttachThenMountImpl(false);
|
||
|
}
|
||
|
|
||
|
// Validate that unmounting works when the UVM is not running
|
||
|
TEST_METHOD(TestMountOnePartitionAfterTimeout)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Create a MBR disk with 1 ext4 partition
|
||
|
FormatDisk({L"ext4"}, false);
|
||
|
|
||
|
// Mount it
|
||
|
ValidateOffline(false);
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " + DiskDevice + L" --partition 1"), (DWORD)0);
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
ValidateOffline(true);
|
||
|
|
||
|
// Wait for vm timeout
|
||
|
WaitForVmTimeout(keepAlive);
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + DiskDevice), (DWORD)0);
|
||
|
|
||
|
// The UVM shouldn't be running
|
||
|
VERIFY_IS_FALSE(GetVmmempPid().has_value());
|
||
|
|
||
|
// No state should be left in registry
|
||
|
const auto key = wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(User->User.Sid);
|
||
|
VERIFY_ARE_EQUAL(wsl::windows::common::registry::EnumKeys(key.get(), KEY_READ).size(), 0);
|
||
|
}
|
||
|
|
||
|
// Validate that the proper mount error is returned if the filesystem type is wrong
|
||
|
TEST_METHOD(TestMountPartitionWithWrongFs)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Create a MBR disk with 1 ext4 partition
|
||
|
FormatDisk({L"ext4"}, false);
|
||
|
|
||
|
// Mount it
|
||
|
wsl::windows::common::SvcComm service;
|
||
|
VERIFY_ARE_EQUAL(service.AttachDisk(DiskDevice.c_str(), LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH), S_OK);
|
||
|
|
||
|
const auto result = service.MountDisk(DiskDevice.c_str(), LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH, 1, nullptr, L"vfat", nullptr);
|
||
|
|
||
|
VERIFY_ARE_EQUAL(result.Result, -22); //-EINVAL
|
||
|
VERIFY_ARE_EQUAL(result.Step, 3); // LxMiniInitMountStepMount
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + DiskDevice), (DWORD)0);
|
||
|
}
|
||
|
|
||
|
// Validate that the proper mount error is returned if the partition can't be found
|
||
|
TEST_METHOD(TestMountPartitionWithBadPartitionIndex)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Create a MBR disk with 1 fat partition
|
||
|
FormatDisk({L"vfat"}, false);
|
||
|
|
||
|
// Try to mount a partition that doesn't exist
|
||
|
wsl::windows::common::SvcComm service;
|
||
|
VERIFY_ARE_EQUAL(service.AttachDisk(DiskDevice.c_str(), LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH), S_OK);
|
||
|
|
||
|
const auto result = service.MountDisk(DiskDevice.c_str(), LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH, 2, nullptr, L"vfat", nullptr);
|
||
|
|
||
|
VERIFY_ARE_EQUAL(result.Result, -2); // -ENOENT
|
||
|
VERIFY_ARE_EQUAL(result.Step, 2); // LxMiniInitMountStepFindPartition
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + DiskDevice), (DWORD)0);
|
||
|
}
|
||
|
|
||
|
// Validate that disk aren't detached if in use by other processes
|
||
|
TEST_METHOD(TestDeviceCantBeMountedIfInUse)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
{
|
||
|
// Format-Volume fails without automount enabled
|
||
|
SetAutoMountPolicy AutoMountPolicy{true};
|
||
|
|
||
|
// Reset the disk
|
||
|
LxsstuLaunchPowershellAndCaptureOutput(L"Clear-Disk -confirm:$false -RemoveData -Number " + std::to_wstring(DiskNumber));
|
||
|
|
||
|
LxsstuLaunchPowershellAndCaptureOutput(L"Initialize-Disk -confirm:$false -Number " + std::to_wstring(DiskNumber));
|
||
|
|
||
|
// Create one fat partition
|
||
|
LxsstuLaunchPowershellAndCaptureOutput(
|
||
|
L"New-Partition -DiskNumber " + std::to_wstring(DiskNumber) +
|
||
|
L" -UseMaximumSize \
|
||
|
| Format-Volume -FileSystem FAT");
|
||
|
}
|
||
|
|
||
|
// Mount it in Windows
|
||
|
auto [letter, _] = LxsstuLaunchPowershellAndCaptureOutput(
|
||
|
L"Set-Partition -DiskNumber " + std::to_wstring(DiskNumber) + L" -PartitionNumber 1" + L" -NewDriveLetter Y");
|
||
|
|
||
|
// Open a file under that partition
|
||
|
wil::unique_handle file(CreateFile(L"Y:\\foo.txt", GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr));
|
||
|
|
||
|
const char* fileContent = "LOW!";
|
||
|
THROW_LAST_ERROR_IF(!WriteFile(file.get(), fileContent, static_cast<DWORD>(strlen(fileContent)), nullptr, nullptr));
|
||
|
|
||
|
// Validate that the disk can't be mounted (TODO: Find a way to validate the failure reason)
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " + DiskDevice + L" --partition 1 --type vfat"), (DWORD)-1);
|
||
|
|
||
|
// Close the file and mount it
|
||
|
file.reset();
|
||
|
WaitForDiskReady();
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " + DiskDevice + L" --partition 1 --type vfat"), (DWORD)0);
|
||
|
|
||
|
// Validate that the file content is correct
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// Validate that the mount succeeded
|
||
|
std::wstring trimmedDiskName(DiskDevice);
|
||
|
Trim(trimmedDiskName);
|
||
|
|
||
|
ValidateMountPoint(disk + L"1", {L"/mnt/wsl/" + trimmedDiskName + L"p1"}, {}, L"vfat");
|
||
|
auto [output, __] = LxsstuLaunchWslAndCaptureOutput(L"cat /mnt/wsl/" + trimmedDiskName + L"p1/foo.txt");
|
||
|
|
||
|
VERIFY_ARE_EQUAL(output, wsl::shared::string::MultiByteToWide(fileContent));
|
||
|
}
|
||
|
|
||
|
TEST_METHOD(TestMountWithFlagOption)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Create a MBR disk with 1 ext4 partition
|
||
|
FormatDisk({L"ext4"}, false);
|
||
|
|
||
|
// Mount it
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " + DiskDevice + L" --partition 1 --options sync"), (DWORD)0);
|
||
|
auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// Validate that the mount succeeded
|
||
|
std::wstring trimmedDiskName(DiskDevice);
|
||
|
Trim(trimmedDiskName);
|
||
|
auto mountTarget = L"/mnt/wsl/" + trimmedDiskName + L"p1";
|
||
|
|
||
|
ValidateMountPoint(disk + L"1", mountTarget, L"sync");
|
||
|
ValidateDiskState({DiskDevice, {{1, {}, L"sync"}}}, keepAlive);
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + DiskDevice), (DWORD)0);
|
||
|
WaitForDiskReady();
|
||
|
|
||
|
// Mount the same partition, but with both a flag and a non-flag option
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " + DiskDevice + L" --partition 1 --options data=ordered,sync"), (DWORD)0);
|
||
|
|
||
|
// Validate that the mount option was properly passed
|
||
|
disk = GetBlockDeviceInWsl();
|
||
|
|
||
|
ValidateMountPoint(disk + L"1", mountTarget, L"ync,relatime,data=ordered");
|
||
|
|
||
|
// Note: relatime is set by default
|
||
|
ValidateDiskState({DiskDevice, {{1, {}, L"data=ordered,sync"}}}, keepAlive);
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + DiskDevice), (DWORD)0);
|
||
|
WaitForDiskReady();
|
||
|
}
|
||
|
|
||
|
TEST_METHOD(TestAttachFailsWithoutWsl2Distro)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL1_TEST_ONLY();
|
||
|
|
||
|
// Attempt to mount a disk with only a WSL1 distro
|
||
|
wsl::windows::common::SvcComm service;
|
||
|
VERIFY_ARE_EQUAL(service.AttachDisk(L"Dummy", LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH), WSL_E_WSL2_NEEDED);
|
||
|
}
|
||
|
|
||
|
TEST_METHOD(VhdWithSpaces)
|
||
|
{
|
||
|
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
LxsstuLaunchPowershellAndCaptureOutput(L"New-Vhd -Path 'vhd with spaces.vhdx' -SizeBytes 20MB");
|
||
|
|
||
|
auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() {
|
||
|
WslShutdown();
|
||
|
|
||
|
if (!DeleteFile(L"vhd with spaces.vhdx"))
|
||
|
{
|
||
|
LogInfo("Failed to delete vhd, %i", GetLastError());
|
||
|
};
|
||
|
});
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Validate that relative path mounting and unmounting works
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount \"vhd with spaces.vhdx\" --bare --vhd"), (DWORD)0);
|
||
|
auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount \"vhd with spaces.vhdx\""), (DWORD)0);
|
||
|
|
||
|
// Validate that absolute path mounting and unmounting works
|
||
|
const std::wstring fullPath = wsl::windows::common::filesystem::GetFullPath(L"vhd with spaces.vhdx");
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount \"" + fullPath + L"\" --bare --vhd"), (DWORD)0);
|
||
|
disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount \"" + fullPath + L"\""), (DWORD)0);
|
||
|
}
|
||
|
|
||
|
void WaitForDiskReady() const
|
||
|
{
|
||
|
const auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(30);
|
||
|
while (timeout > std::chrono::steady_clock::now())
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
auto disk = wsl::windows::common::disk::OpenDevice(DiskDevice.c_str(), GENERIC_READ, c_diskOpenTimeoutMs);
|
||
|
wsl::windows::common::disk::ValidateDiskVolumesAreReady(disk.get());
|
||
|
return;
|
||
|
}
|
||
|
catch (...)
|
||
|
{
|
||
|
auto error = std::system_category().message(wil::ResultFromCaughtException());
|
||
|
LogInfo("Caught '%S' while waiting for disk", error.c_str());
|
||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VERIFY_FAIL(L"Timeout waiting for disk");
|
||
|
}
|
||
|
|
||
|
void ValidateOffline(bool offline) const
|
||
|
{
|
||
|
const auto disk = wsl::windows::common::disk::OpenDevice(DiskDevice.c_str(), FILE_READ_ATTRIBUTES, c_diskOpenTimeoutMs);
|
||
|
VERIFY_ARE_EQUAL(!offline, wsl::windows::common::disk::IsDiskOnline(disk.get()));
|
||
|
}
|
||
|
|
||
|
static std::wstring GetBlockDeviceInWsl()
|
||
|
{
|
||
|
// Wait for the disk to be attached
|
||
|
const auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(30);
|
||
|
|
||
|
bool done = false;
|
||
|
while (true)
|
||
|
{
|
||
|
for (wchar_t name = 'a'; name < 'z'; name++)
|
||
|
{
|
||
|
std::wstring cmd = L"-u root blockdev --getsize64 /dev/sd";
|
||
|
cmd += name;
|
||
|
|
||
|
std::wstring out;
|
||
|
try
|
||
|
{
|
||
|
out = LxsstuLaunchWslAndCaptureOutput(cmd.data()).first;
|
||
|
}
|
||
|
CATCH_LOG()
|
||
|
|
||
|
Trim(out);
|
||
|
|
||
|
// Disk size is 20MB, so 20 * 1024 * 1024 bytes
|
||
|
if (out == L"20971520")
|
||
|
{
|
||
|
return std::wstring(L"/dev/sd") + name;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (done)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
done = std::chrono::steady_clock::now() > timeout;
|
||
|
}
|
||
|
|
||
|
VERIFY_FAIL(L"Failed to find the block device in WSL");
|
||
|
|
||
|
// Unreachable.
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
static bool IsBlockDevicePresent(const std::wstring& Device)
|
||
|
{
|
||
|
const auto Cmd = L"test -e " + Device;
|
||
|
return LxsstuLaunchWsl(Cmd.data()) == 0;
|
||
|
}
|
||
|
|
||
|
static std::optional<std::vector<std::wstring>> GetBlockDeviceMount(const std::wstring& device)
|
||
|
{
|
||
|
const std::wstring cmd(L"cat /proc/mounts");
|
||
|
auto [out, _] = LxsstuLaunchWslAndCaptureOutput(cmd.data());
|
||
|
|
||
|
LogInfo("/proc/mounts content: '%ls'", out.c_str());
|
||
|
std::wistringstream output(out);
|
||
|
std::wstring line;
|
||
|
|
||
|
while (std::getline(output, line))
|
||
|
{
|
||
|
if (wcsstr(line.data(), device.data()) == line.data())
|
||
|
{
|
||
|
return LxssSplitString(line);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
void ValidateDiskState(const ExpectedDiskState& State, WslKeepAlive& KeepAlive)
|
||
|
{
|
||
|
WaitForVmTimeout(KeepAlive);
|
||
|
const auto key = wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(User->User.Sid);
|
||
|
const auto subKeys = wsl::windows::common::registry::EnumKeys(key.get(), KEY_READ);
|
||
|
|
||
|
VERIFY_ARE_EQUAL(subKeys.size(), 1);
|
||
|
|
||
|
const auto& diskKey = subKeys.begin()->second;
|
||
|
|
||
|
auto read = [](const auto& Key, LPCWSTR Name) -> std::optional<std::wstring> {
|
||
|
try
|
||
|
{
|
||
|
return wsl::windows::common::registry::ReadString(Key.get(), nullptr, Name);
|
||
|
}
|
||
|
catch (...)
|
||
|
{
|
||
|
return {};
|
||
|
}
|
||
|
};
|
||
|
|
||
|
VERIFY_ARE_EQUAL(read(diskKey, L"Disk").value(), State.Path);
|
||
|
VERIFY_ARE_EQUAL(wsl::windows::common::registry::EnumKeys(diskKey.get(), KEY_READ).size(), State.Mounts.size());
|
||
|
|
||
|
for (const auto& e : State.Mounts)
|
||
|
{
|
||
|
auto keyName = std::to_wstring(e.PartitionIndex);
|
||
|
|
||
|
auto mountKey = wsl::windows::common::registry::OpenKey(diskKey.get(), keyName.c_str(), KEY_READ);
|
||
|
|
||
|
VERIFY_ARE_EQUAL(read(mountKey, L"Options"), e.Options);
|
||
|
VERIFY_ARE_EQUAL(read(mountKey, L"Type"), e.Type);
|
||
|
}
|
||
|
|
||
|
KeepAlive.Set();
|
||
|
}
|
||
|
|
||
|
void WaitForVmTimeout(WslKeepAlive& KeepAlive)
|
||
|
{
|
||
|
const auto pid = GetVmmempPid();
|
||
|
VERIFY_IS_TRUE(pid.has_value());
|
||
|
KeepAlive.Reset();
|
||
|
const std::wstring cmd = std::wstring(L"-t ") + std::wstring(LXSS_DISTRO_NAME_TEST_L);
|
||
|
|
||
|
// Terminate the distro to make the vm timeout faster
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(cmd.c_str()), (DWORD)0);
|
||
|
|
||
|
const wil::unique_process_handle process(OpenProcess(SYNCHRONIZE, false, pid.value()));
|
||
|
VERIFY_IS_NOT_NULL(process.get());
|
||
|
|
||
|
VERIFY_ARE_EQUAL((DWORD)WAIT_OBJECT_0, WaitForSingleObject(process.get(), INFINITE));
|
||
|
}
|
||
|
|
||
|
static std::optional<DWORD> GetVmmempPid()
|
||
|
{
|
||
|
for (auto pid : wsl::windows::common::wslutil::ListRunningProcesses())
|
||
|
{
|
||
|
wil::unique_process_handle process(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid));
|
||
|
if (!process)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
std::wstring imageName(MAX_PATH, '\0');
|
||
|
const DWORD length = GetProcessImageFileName(process.get(), imageName.data(), (DWORD)imageName.size() + 1);
|
||
|
if (length == 0)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
imageName.resize(length);
|
||
|
if (imageName == L"vmmemWSL" || (!wsl::windows::common::helpers::IsWindows11OrAbove() && imageName == L"vmmem"))
|
||
|
{
|
||
|
return pid;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return {}; // Unreachable
|
||
|
}
|
||
|
|
||
|
void FormatDisk(const std::vector<std::wstring>& Partitions, bool isVhdTest)
|
||
|
{
|
||
|
WaitForDiskReady();
|
||
|
const auto deviceName = (isVhdTest) ? VhdDevice : DiskDevice;
|
||
|
if (isVhdTest)
|
||
|
{
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " + deviceName + L" --vhd --bare"), (DWORD)0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--mount " + deviceName + L" --bare"), (DWORD)0);
|
||
|
}
|
||
|
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// Create a partition table
|
||
|
std::wstringstream Cmd;
|
||
|
Cmd << "bash -c \"(";
|
||
|
Cmd << L"echo -e o\n"; // Create a new partition table
|
||
|
|
||
|
for (size_t i = 0; i < Partitions.size(); i++)
|
||
|
{
|
||
|
Cmd << L"echo -e n\n"; // Add a new partition
|
||
|
Cmd << L"echo -e p\n"; // Primary partition
|
||
|
Cmd << L"echo -e " << (i + 1) << L"\n"; // Partition number
|
||
|
Cmd << L"echo -e\n"; // First sector (Accept default)
|
||
|
Cmd << L"echo " << 2049 + (i + 1) * 4096 << L"\n"; // Last sector
|
||
|
}
|
||
|
|
||
|
Cmd << L"echo -e w\n"; // Write changes
|
||
|
Cmd << L") | fdisk " + disk + L"\"";
|
||
|
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(Cmd.str()), (DWORD)0);
|
||
|
|
||
|
for (size_t i = 1; i <= Partitions.size(); i++)
|
||
|
{
|
||
|
auto partition = disk + std::to_wstring(i);
|
||
|
|
||
|
// mkfs.ext4 interactively asks for confirmation, -F disables that behavior
|
||
|
const auto forceFlag = Partitions[i - 1] == L"ext4" ? L" -F " : L"";
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"mkfs." + Partitions[i - 1] + forceFlag + L" " + partition), (DWORD)0);
|
||
|
}
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + deviceName), (DWORD)0);
|
||
|
|
||
|
if (!isVhdTest)
|
||
|
{
|
||
|
WaitForDiskReady();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ValidateMountPoint(
|
||
|
const std::wstring& BlockDevice,
|
||
|
const std::optional<std::wstring>& Mountpoint,
|
||
|
const std::optional<std::wstring>& ExpectedOption = {},
|
||
|
const std::optional<std::wstring>& ExpectedType = {})
|
||
|
{
|
||
|
auto mount = GetBlockDeviceMount(BlockDevice);
|
||
|
if (Mountpoint.has_value())
|
||
|
{
|
||
|
VERIFY_IS_TRUE(mount.has_value());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
VERIFY_IS_FALSE(mount.has_value());
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
VERIFY_ARE_EQUAL(mount.value()[1], Mountpoint.value());
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"test -d " + Mountpoint.value()), (DWORD)0);
|
||
|
|
||
|
// If specifed, validate that ExpectedOption is in the mount options
|
||
|
// (We don't want to do a direct compare because the kernel might add some like rw, ...)
|
||
|
if (ExpectedOption.has_value())
|
||
|
{
|
||
|
VERIFY_ARE_NOT_EQUAL(mount.value()[3].find(ExpectedOption.value()), std::string::npos);
|
||
|
}
|
||
|
|
||
|
// If specified, validate the filesystem
|
||
|
if (ExpectedType.has_value())
|
||
|
{
|
||
|
VERIFY_ARE_EQUAL(mount.value()[2], ExpectedType.value());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void TestBareMountImpl(bool isVhd)
|
||
|
{
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;
|
||
|
const auto mountCommand = (isVhd) ? (L"--mount " + deviceName + L" --vhd") : (L"--mount " + deviceName);
|
||
|
|
||
|
if (isVhd)
|
||
|
{
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --bare"), (DWORD)0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ValidateOffline(false);
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --bare"), (DWORD)0);
|
||
|
ValidateOffline(true);
|
||
|
}
|
||
|
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
VERIFY_IS_FALSE(GetBlockDeviceMount(disk).has_value());
|
||
|
|
||
|
ValidateDiskState({deviceName, {}}, keepAlive);
|
||
|
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + deviceName), (DWORD)0);
|
||
|
|
||
|
if (!isVhd)
|
||
|
{
|
||
|
ValidateOffline(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void TestMountOnePartitionImpl(bool isVhd)
|
||
|
{
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;
|
||
|
const auto mountCommand = (isVhd) ? (L"--mount " + deviceName + L" --vhd") : (L"--mount " + deviceName);
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Create a MBR disk with 1 ext4 partition
|
||
|
FormatDisk({L"ext4"}, isVhd);
|
||
|
|
||
|
// Mount it
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --partition 1"), (DWORD)0);
|
||
|
auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// Validate that the mount succeeded
|
||
|
std::wstring trimmedDiskName(deviceName);
|
||
|
Trim(trimmedDiskName);
|
||
|
auto mountTarget = L"/mnt/wsl/" + trimmedDiskName + L"p1";
|
||
|
|
||
|
ValidateMountPoint(disk + L"1", mountTarget);
|
||
|
|
||
|
ValidateDiskState({deviceName, {{1, {}, {}}}}, keepAlive);
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + deviceName), (DWORD)0);
|
||
|
WaitForDiskReady();
|
||
|
|
||
|
if (!isVhd)
|
||
|
{
|
||
|
ValidateOffline(false);
|
||
|
}
|
||
|
|
||
|
// Validate that the mount folder was deleted
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"test -e " + mountTarget), (DWORD)1);
|
||
|
|
||
|
// Mount the same partition, but with a specific mount option
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --partition 1 --options \"data=ordered\""), (DWORD)0);
|
||
|
|
||
|
// Validate that the mount option was properly passed
|
||
|
disk = GetBlockDeviceInWsl();
|
||
|
ValidateMountPoint(disk + L"1", mountTarget, L"data=ordered");
|
||
|
ValidateDiskState({deviceName, {{1, {}, L"data=ordered"}}}, keepAlive);
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + deviceName), (DWORD)0);
|
||
|
WaitForDiskReady();
|
||
|
|
||
|
if (!isVhd)
|
||
|
{
|
||
|
ValidateOffline(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void TestMountTwoPartitionsImpl(bool isVhd)
|
||
|
{
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;
|
||
|
const auto mountCommand = (isVhd) ? (L"--mount " + deviceName + L" --vhd") : (L"--mount " + deviceName);
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Create a MBR disk with 1 ext4 partition and one fat partitions
|
||
|
FormatDisk({L"ext4", L"vfat"}, isVhd);
|
||
|
|
||
|
// Mount then both
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --partition 1"), (DWORD)0);
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --partition 2 --type vfat"), (DWORD)0);
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// Validate that the mount succeeded
|
||
|
std::wstring trimmedDiskName(deviceName);
|
||
|
Trim(trimmedDiskName);
|
||
|
|
||
|
ValidateMountPoint(disk + L"1", L"/mnt/wsl/" + trimmedDiskName + L"p1", {}, L"ext4");
|
||
|
ValidateMountPoint(disk + L"2", L"/mnt/wsl/" + trimmedDiskName + L"p2", {}, L"vfat");
|
||
|
ValidateDiskState({deviceName, {{1, {}, {}}, {2, {L"vfat"}, {}}}}, keepAlive);
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + deviceName), (DWORD)0);
|
||
|
WaitForDiskReady();
|
||
|
|
||
|
if (!isVhd)
|
||
|
{
|
||
|
ValidateOffline(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void TestAttachThenMountImpl(bool isVhd)
|
||
|
{
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;
|
||
|
const auto mountCommand = (isVhd) ? (L"--mount " + deviceName + L" --vhd") : (L"--mount " + deviceName);
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
FormatDisk({L"ext4"}, isVhd);
|
||
|
|
||
|
// Mount then both
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --bare"), (DWORD)0);
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --partition 1"), (DWORD)0);
|
||
|
|
||
|
ValidateDiskState({deviceName, {{1, {}, {}}}}, keepAlive);
|
||
|
|
||
|
// Validate that our disk is still mounted
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// Validate that the mount succeeded
|
||
|
std::wstring trimmedDiskName(deviceName);
|
||
|
Trim(trimmedDiskName);
|
||
|
|
||
|
ValidateMountPoint(disk + L"1", L"/mnt/wsl/" + trimmedDiskName + L"p1", {}, {});
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + deviceName), (DWORD)0);
|
||
|
}
|
||
|
|
||
|
void TestMountWholeDiskImpl(bool isVhd)
|
||
|
{
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;
|
||
|
const auto mountCommand = (isVhd) ? (L"--mount " + deviceName + L" --vhd") : (L"--mount " + deviceName);
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Format the volume as ext4
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --bare"), (DWORD)0);
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"mkfs.ext4 -F " + disk), (DWORD)0);
|
||
|
|
||
|
// Then mount it
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --type ext4"), (DWORD)0);
|
||
|
|
||
|
// Validate that the mount succeeded
|
||
|
std::wstring trimmedDiskName(deviceName);
|
||
|
Trim(trimmedDiskName);
|
||
|
auto mountTarget = L"/mnt/wsl/" + trimmedDiskName;
|
||
|
ValidateMountPoint(disk, mountTarget, {}, L"ext4");
|
||
|
ValidateDiskState({deviceName, {{0, {L"ext4"}, {}}}}, keepAlive);
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + deviceName), (DWORD)0);
|
||
|
|
||
|
if (!isVhd)
|
||
|
{
|
||
|
WaitForDiskReady();
|
||
|
ValidateOffline(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void TestMountStateIsDeletedOnShutdownImpl(bool isVhd)
|
||
|
{
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;
|
||
|
const auto mountCommand = (isVhd) ? (L"--mount " + deviceName + L" --vhd") : (L"--mount " + deviceName);
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Create a MBR disk with 1 ext4 partition
|
||
|
FormatDisk({L"ext4"}, isVhd);
|
||
|
|
||
|
// Mount it
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --partition 1 --type ext4"), (DWORD)0);
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
ValidateDiskState({deviceName, {{1, {L"ext4"}, {}}}}, keepAlive);
|
||
|
keepAlive.Reset();
|
||
|
|
||
|
// wsl --shutdown clears any disk state
|
||
|
WslShutdown();
|
||
|
|
||
|
if (!isVhd)
|
||
|
{
|
||
|
ValidateOffline(false);
|
||
|
}
|
||
|
|
||
|
// No state should be left in registry
|
||
|
const auto key = wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(User->User.Sid);
|
||
|
VERIFY_ARE_EQUAL(wsl::windows::common::registry::EnumKeys(key.get(), KEY_READ).size(), 0);
|
||
|
}
|
||
|
|
||
|
void TestFilesystemDetectionWholeDiskImpl(bool isVhd)
|
||
|
{
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;
|
||
|
const auto mountCommand = (isVhd) ? (L"--mount " + deviceName + L" --vhd") : (L"--mount " + deviceName);
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Format the volume as fat
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --bare"), (DWORD)0);
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"mkfs.fat --mbr=no -I " + disk), (DWORD)0);
|
||
|
|
||
|
// Then mount it. The filesystem should be autodetected
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand), (DWORD)0);
|
||
|
|
||
|
// Validate that the mount succeeded
|
||
|
std::wstring trimmedDiskName(deviceName);
|
||
|
Trim(trimmedDiskName);
|
||
|
auto mountTarget = L"/mnt/wsl/" + trimmedDiskName;
|
||
|
ValidateMountPoint(disk, mountTarget, {}, L"vfat");
|
||
|
ValidateDiskState({deviceName, {{0, {}, {}}}}, keepAlive);
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + deviceName), (DWORD)0);
|
||
|
|
||
|
if (!isVhd)
|
||
|
{
|
||
|
WaitForDiskReady();
|
||
|
ValidateOffline(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void TestMountTwoPartitionsWithDetectionImpl(bool isVhd)
|
||
|
{
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;
|
||
|
const auto mountCommand = (isVhd) ? (L"--mount " + deviceName + L" --vhd") : (L"--mount " + deviceName);
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Create a MBR disk with 1 ext4 partition and one fat partitions
|
||
|
FormatDisk({L"ext4", L"vfat"}, isVhd);
|
||
|
|
||
|
// Mount then both (filesystems should be detected).
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --partition 1"), (DWORD)0);
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --partition 2"), (DWORD)0);
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
|
||
|
// Validate that the mount succeeded
|
||
|
std::wstring trimmedDiskName(deviceName);
|
||
|
Trim(trimmedDiskName);
|
||
|
|
||
|
ValidateMountPoint(disk + L"1", L"/mnt/wsl/" + trimmedDiskName + L"p1", {}, L"ext4");
|
||
|
ValidateMountPoint(disk + L"2", L"/mnt/wsl/" + trimmedDiskName + L"p2", {}, L"vfat");
|
||
|
ValidateDiskState({deviceName, {{1, {}, {}}, {2, {}, {}}}}, keepAlive);
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + deviceName), (DWORD)0);
|
||
|
|
||
|
if (!isVhd)
|
||
|
{
|
||
|
WaitForDiskReady();
|
||
|
ValidateOffline(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void TestFilesystemDetectionFailImpl(bool isVhd)
|
||
|
{
|
||
|
WSL2_TEST_ONLY();
|
||
|
|
||
|
const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;
|
||
|
const auto mountCommand = (isVhd) ? (L"--mount " + deviceName + L" --vhd") : (L"--mount " + deviceName);
|
||
|
|
||
|
WslKeepAlive keepAlive;
|
||
|
|
||
|
// Write zeroes in the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L" --bare"), (DWORD)0);
|
||
|
const auto disk = GetBlockDeviceInWsl();
|
||
|
VERIFY_IS_TRUE(IsBlockDevicePresent(disk));
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"dd bs=4M count=1 if=/dev/zero of=" + disk), (DWORD)0);
|
||
|
|
||
|
// Then try to mount it
|
||
|
wsl::windows::common::SvcComm service;
|
||
|
const auto result = service.MountDisk(
|
||
|
deviceName.c_str(), isVhd ? LXSS_ATTACH_MOUNT_FLAGS_VHD : LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH, 0, nullptr, nullptr, nullptr);
|
||
|
|
||
|
// Validate that the mount fail because the filesystem couldn't be detected
|
||
|
VERIFY_ARE_EQUAL(result.Result, -1); //-EINVAL
|
||
|
VERIFY_ARE_EQUAL(result.Step, 6); // LxMiniInitMountStepDetectFilesystem
|
||
|
|
||
|
// Unmount the disk
|
||
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + deviceName), (DWORD)0);
|
||
|
|
||
|
if (!isVhd)
|
||
|
{
|
||
|
WaitForDiskReady();
|
||
|
ValidateOffline(false);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
} // namespace MountTests
|