mirror of
https://github.com/microsoft/WSL.git
synced 2026-04-11 10:52:42 -05:00
* test: enable virtiofs tests and enable WSLG during testing (#14387) * test: enable virtiofs tests and enable WSLG during testing * test fix --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> * chore(distributions): Almalinux auto-update - 20260311 14:52:02 (#14404) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Fix CVE-2026-26127: bump .NET runtime from 10.0.0 to 10.0.4 (#14421) Addresses Dependabot alerts #10 and #11. The Microsoft.NETCore.App.Runtime packages (win-x64 and win-arm64) at version 10.0.0 are vulnerable to a denial of service via out-of-bounds read when decoding malformed Base64Url input (CVSS 7.5 High). Bumped to 10.0.4 which includes the fix. Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> * Notice change from build: 141806547 (#14423) Co-authored-by: WSL notice <noreply@microsoft.com> * Ship initrd.img in MSI using build-time generation via powershell script (#14424) * Ship initrd.img in MSI using build-time generation via tar.exe Replace the install-time CreateInitrd/RemoveInitrd custom actions with a build-time step that generates initrd.img using the Windows built-in tar.exe (libarchive/bsdtar) and ships it directly in the MSI. The install-time approach had a race condition: wsl.exe could launch before the CreateInitrd custom action completed, causing ERROR_FILE_NOT_FOUND for initrd.img. Changes: - Add CMake custom command to generate initrd.img via tar.exe --format=newc - Add initrd.img as a regular file in the MSI tools component - Remove CreateInitrd/RemoveInitrd custom actions from WiX, DllMain, and wslinstall.def - Remove CreateCpioInitrd helper and its tests (no longer needed) - Update pipeline build targets to build initramfs instead of init * pr feedback * more pr feedback * switch to using a powershell script instead of tar.exe * powershell script feedback * hopefully final pr feedback --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> * virtiofs: update logic so querying virtiofs mount source does not require a call to the service (#14380) * virtiofs: update logic so querying virtiofs mount source does not require a call to the service * more pr feedback * use std::filesystem::read_symlink * pr feedback and use canonical path in virtiofs symlink * make sure canonical path is always used --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> * virtio networking: add support for ipv6 (#14350) * VirtioProxy: Add IPv6 address, gateway, and route support - Add PreferredIpv6Address field and GetBestGatewayV6* methods to NetworkSettings - Extend GetHostEndpointSettings() to discover IPv6 unicast address and gateway - Add UpdateIpv6Address() using ModifyGuestEndpointSettingRequest<IPAddress> - Push IPv6 default route to guest via UpdateDefaultRoute(AF_INET6) - Remove AF_INET6 early return in ModifyOpenPorts, use INETADDR_PORT() - Add EndpointRoute::DefaultRoute() static factory - Pass client_ip_ipv6 in devicehost options (not yet parsed by devicehost) - Remove gateway_ip from devicehost options (only needed for DHCP) - Include IPv6 DNS servers in non-tunneling DNS settings - Add ConfigurationV6 and DnsResolutionAAAA tests * cleanup and add more ipv6 tests * added test coverage and minor updates * clang format * pr feedback * format source * pr feedback * test fixes --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> * Track `bind` syscall when port is 0 (#14333) * Initial work * . * pr feedback and add unit test * minor tweaks an fix use after free in logging statement * implement PR feedback * hopefully final pr feedback * pr feedback in test function * Address PR feedback: add try/catch to TrackPort and PortZeroBind queue push --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> * Add iptables to list of apps to install in WSL (#14459) There were instructions already on how to install tcpdump in WSL, but iptables are also needed for the log collection to be complete, so this PR adds instructions on how to also install iptables. Co-authored-by: Andre Muezerie <andremue@linux.microsoft.com> * Update Microsoft.WSL.DeviceHost to version 1.1.39-0 (#14460) Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> * Moves all Ubuntu distros to the tar-based format (#14463) * Move all supported Ubuntu images to the new format We backported the build pipeline so all current LTSes come out in the new tar-based format * Remove the appx based distros All WSL users can run tar-based distros by now, right? There is no benefit in maintaining both formats. * Enable DNS tunneling for VirtioProxy networking mode (#14461) - Allow VirtioProxy to keep EnableDnsTunneling=true in config, but clear socket-specific options (BestEffortDnsParsing, DnsTunnelingIpAddress) - Suppress dedicated DNS tunneling hvsocket for VirtioProxy; tunneling is handled through the VirtioNetworking device host instead - Set DnsTunneling flag on VirtioNetworkingFlags so the device host knows to tunnel DNS - Expand SWIOTLB kernel cmdline to cover VirtioFs and VirtioProxy - Bump DeviceHost package to 1.1.39-0 - Add VirtioProxy DNS test coverage for tunneling on/off - Skip GuestPortIsReleasedV6 on Windows 10 Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> * test: disable LoopbackExplicit due to OS build 29555 regression (#14477) Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> * Refactor: trim unnecessary DLL deps from COMMON_LINK_LIBRARIES (#14426) * Refactor: trim unnecessary DLL deps from COMMON_LINK_LIBRARIES - Split MSI/Wintrust install functions from wslutil.cpp into install.cpp - Remove MI.lib, wsldeps.lib, msi.lib, Wintrust.lib, computecore.lib, computenetwork.lib, Iphlpapi.lib from COMMON_LINK_LIBRARIES - Add per-target MSI_LINK_LIBRARIES, HCS_LINK_LIBRARIES, SERVICE_LINK_LIBRARIES - Delay-load msi.dll and WINTRUST.dll for wsl.exe and wslg.exe - Result: wslhost, wslrelay, wslcsdk, testplugin lose msi/wintrust startup imports; wsl.exe and wslg.exe defer msi/wintrust loading until actually needed; wslservice is the only target that imports computecore/computenetwork/Iphlpapi * minor fixes to install.cpp that were caught during PR * move to wsl::windows::common::install namespace --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> * wslc: enable IPv6 and DNS tunneling for virtio proxy networking mode --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> Co-authored-by: AlmaLinux Autobot <107999298+almalinuxautobot@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Blue <OneBlue@users.noreply.github.com> Co-authored-by: WSL notice <noreply@microsoft.com> Co-authored-by: Daman Mulye <daman_mulye@hotmail.com> Co-authored-by: Andre Muezerie <108841174+andremueiot@users.noreply.github.com> Co-authored-by: Andre Muezerie <andremue@linux.microsoft.com> Co-authored-by: Carlos Nihelton <carlos.santanadeoliveira@canonical.com>
1350 lines
51 KiB
C++
1350 lines
51 KiB
C++
/*++
|
|
|
|
Copyright (c) Microsoft. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
DrvFsTests.cpp
|
|
|
|
Abstract:
|
|
|
|
This file contains drvfs test cases.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
#include "Common.h"
|
|
#include <AclAPI.h>
|
|
#include <fstream>
|
|
#include <filesystem>
|
|
#include "wslservice.h"
|
|
#include "registry.hpp"
|
|
#include "helpers.hpp"
|
|
#include "svccomm.hpp"
|
|
#include <userenv.h>
|
|
#include "Distribution.h"
|
|
|
|
#define LXSST_DRVFS_TEST_DIR L"C:\\drvfstest"
|
|
#define LXSST_DRVFS_RWX_TEST_FILE LXSST_DRVFS_TEST_DIR L"\\rwx"
|
|
#define LXSST_DRVFS_READONLY_TEST_FILE LXSST_DRVFS_TEST_DIR L"\\readonly"
|
|
#define LXSST_DRVFS_WRITEONLY_TEST_FILE LXSST_DRVFS_TEST_DIR L"\\writeonly"
|
|
#define LXSST_DRVFS_EXECUTEONLY_TEST_FILE LXSST_DRVFS_TEST_DIR L"\\executeonly"
|
|
#define LXSST_DRVFS_READONLYATTR_TEST_FILE LXSST_DRVFS_TEST_DIR L"\\readonlyattr"
|
|
#define LXSST_DRVFS_READONLYATTRDEL_TEST_FILE LXSST_DRVFS_TEST_DIR L"\\readonlyattrdel"
|
|
#define LXSST_DRVFS_EXECUTEONLY_TEST_DIR LXSST_DRVFS_TEST_DIR L"\\executeonlydir"
|
|
#define LXSST_DRVFS_EXECUTEONLY_TEST_DIR_CHILD LXSST_DRVFS_EXECUTEONLY_TEST_DIR L"\\child"
|
|
#define LXSST_DRVFS_READONLY_TEST_DIR LXSST_DRVFS_TEST_DIR L"\\noexecutedir"
|
|
#define LXSST_DRVFS_METADATA_TEST_DIR L"C:\\metadatatest"
|
|
|
|
#define LXSST_DRVFS_REPARSE_TEST_DIR L"C:\\reparsetest"
|
|
|
|
#define LXSST_DRVFS_SYMLINK_TEST_DIR L"C:\\symlink"
|
|
|
|
#define LXSST_DRVFS_METADATA_TEST_MODE (5)
|
|
|
|
#define LXSST_TESTS_INSTALL_COMMAND_LINE L"/bin/bash -c 'cd /data/test; ./build_tests.sh'"
|
|
|
|
#define LXSST_METADATA_EA_NAME_LENGTH (RTL_NUMBER_OF(LX_FILE_METADATA_UID_EA_NAME) - 1)
|
|
|
|
#define LX_DRVFS_DISABLE_NONE (0)
|
|
#define LX_DRVFS_DISABLE_QUERY_BY_NAME (1)
|
|
#define LX_DRVFS_DISABLE_QUERY_BY_NAME_AND_STAT_INFO (2)
|
|
|
|
using wsl::windows::common::wslutil::GetSystemErrorString;
|
|
|
|
namespace DrvFsTests {
|
|
|
|
class DrvFsTests
|
|
{
|
|
public:
|
|
std::wstring SkipUnstableTestEnvVar =
|
|
L"WSL_DISABLE_VB_UNSTABLE_TESTS=" + std::wstring{wsl::windows::common::helpers::IsWindows11OrAbove() ? L"0" : L"1"};
|
|
|
|
void DrvFsCommon(int TestMode, std::optional<DrvFsMode> DrvFsMode = {}) const
|
|
{
|
|
auto cleanup = wil::scope_exit([TestMode] {
|
|
RemoveDirectory(LXSST_DRVFS_REPARSE_TEST_DIR L"\\junction");
|
|
RemoveDirectory(LXSST_DRVFS_REPARSE_TEST_DIR L"\\absolutelink");
|
|
DeleteFileW(LXSST_DRVFS_REPARSE_TEST_DIR L"\\filelink");
|
|
RemoveDirectory(LXSST_DRVFS_REPARSE_TEST_DIR L"\\relativelink");
|
|
RemoveDirectory(LXSST_DRVFS_REPARSE_TEST_DIR L"\\test\\linktarget");
|
|
DeleteFileW(LXSST_DRVFS_REPARSE_TEST_DIR L"\\test\\filetarget");
|
|
RemoveDirectory(LXSST_DRVFS_REPARSE_TEST_DIR L"\\test");
|
|
DeleteFileW(LXSST_DRVFS_REPARSE_TEST_DIR L"\\v1link");
|
|
DeleteFileW(LXSST_DRVFS_REPARSE_TEST_DIR L"\\appexeclink");
|
|
RemoveDirectory(LXSST_DRVFS_REPARSE_TEST_DIR);
|
|
SetFileAttributes(LXSST_DRVFS_RWX_TEST_FILE, FILE_ATTRIBUTE_NORMAL);
|
|
DeleteFileW(LXSST_DRVFS_RWX_TEST_FILE);
|
|
DeleteFileW(LXSST_DRVFS_READONLY_TEST_FILE);
|
|
DeleteFileW(LXSST_DRVFS_WRITEONLY_TEST_FILE);
|
|
DeleteFileW(LXSST_DRVFS_EXECUTEONLY_TEST_FILE);
|
|
DeleteFileW(LXSST_DRVFS_EXECUTEONLY_TEST_DIR_CHILD);
|
|
SetFileAttributes(LXSST_DRVFS_READONLYATTR_TEST_FILE, FILE_ATTRIBUTE_NORMAL);
|
|
|
|
DeleteFileW(LXSST_DRVFS_READONLYATTR_TEST_FILE);
|
|
SetFileAttributes(LXSST_DRVFS_READONLYATTRDEL_TEST_FILE, FILE_ATTRIBUTE_NORMAL);
|
|
|
|
DeleteFileW(LXSST_DRVFS_READONLYATTRDEL_TEST_FILE);
|
|
RemoveDirectory(LXSST_DRVFS_EXECUTEONLY_TEST_DIR);
|
|
RemoveDirectory(LXSST_DRVFS_READONLY_TEST_DIR);
|
|
RemoveDirectory(LXSST_DRVFS_TEST_DIR);
|
|
DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR "\\file.txt");
|
|
DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR L"\\foo\uf03abar");
|
|
RemoveDirectory(LXSST_DRVFS_SYMLINK_TEST_DIR "\\dir");
|
|
DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink1");
|
|
RemoveDirectory(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink2");
|
|
RemoveDirectory(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink3");
|
|
DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink4");
|
|
DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink5");
|
|
DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink6");
|
|
RemoveDirectory(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink7");
|
|
DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink8");
|
|
DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink1");
|
|
DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink2");
|
|
DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink3");
|
|
DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink4");
|
|
DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink5");
|
|
DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink6");
|
|
DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink7");
|
|
RemoveDirectory(LXSST_DRVFS_SYMLINK_TEST_DIR);
|
|
if (TestMode == LXSST_DRVFS_METADATA_TEST_MODE)
|
|
{
|
|
DeleteFileW(LXSST_DRVFS_METADATA_TEST_DIR L"\\baduid");
|
|
DeleteFileW(LXSST_DRVFS_METADATA_TEST_DIR L"\\badgid");
|
|
DeleteFileW(LXSST_DRVFS_METADATA_TEST_DIR L"\\badmode");
|
|
DeleteFileW(LXSST_DRVFS_METADATA_TEST_DIR L"\\badtype1");
|
|
DeleteFileW(LXSST_DRVFS_METADATA_TEST_DIR L"\\badtype2");
|
|
DeleteFileW(LXSST_DRVFS_METADATA_TEST_DIR L"\\nondevice");
|
|
RemoveDirectory(LXSST_DRVFS_METADATA_TEST_DIR);
|
|
}
|
|
});
|
|
|
|
VERIFY_NO_THROW(CreateDrvFsTestFiles(TestMode == LXSST_DRVFS_METADATA_TEST_MODE));
|
|
std::wstringstream Command;
|
|
|
|
Command << L"/bin/bash -c \"";
|
|
Command << SkipUnstableTestEnvVar;
|
|
Command << " /data/test/wsl_unit_tests drvfs -d $(wslpath '";
|
|
Command << LxsstuGetLxssDirectory();
|
|
Command << L"') -m ";
|
|
Command << TestMode;
|
|
Command << L"\"";
|
|
std::wstringstream Logfile;
|
|
Logfile << L"drvfs";
|
|
Logfile << TestMode;
|
|
VERIFY_NO_THROW(LxsstuRunTest(Command.str().c_str(), Logfile.str().c_str()));
|
|
|
|
//
|
|
// Check that the read-only attribute has been changed.
|
|
//
|
|
|
|
DWORD Attributes = GetFileAttributes(LXSST_DRVFS_READONLYATTR_TEST_FILE);
|
|
DWORD Expected = FILE_ATTRIBUTE_NORMAL;
|
|
VERIFY_ARE_EQUAL(Expected, Attributes);
|
|
Attributes = GetFileAttributes(LXSST_DRVFS_RWX_TEST_FILE);
|
|
Expected = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_ARCHIVE;
|
|
VERIFY_ARE_EQUAL(Expected, Attributes);
|
|
|
|
//
|
|
// Check that the second read-only file was deleted.
|
|
//
|
|
|
|
Expected = INVALID_FILE_ATTRIBUTES;
|
|
Attributes = GetFileAttributes(LXSST_DRVFS_READONLYATTRDEL_TEST_FILE);
|
|
VERIFY_ARE_EQUAL(Expected, Attributes);
|
|
|
|
//
|
|
// Check the NT symlinks.
|
|
//
|
|
|
|
VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink1", L"file.txt", false));
|
|
VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink2", L"dir", true));
|
|
VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink3", L"..", true));
|
|
VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink4", L"..\\symlink\\file.txt", false));
|
|
VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink5", L"dir\\..\\file.txt", false));
|
|
VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink6", L"ntlink1", false));
|
|
VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink7", L"ntlink2", true));
|
|
VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\ntlink8", L"foo\uf03abar", false));
|
|
|
|
VERIFY_NO_THROW(VerifyDrvFsLxSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink1"));
|
|
VERIFY_NO_THROW(VerifyDrvFsLxSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink2"));
|
|
|
|
// Since target resolution is done on the Windows side in Plan 9 and VirtioFs, it is able to create an NT
|
|
// link if the target path traverses an existing NT link (this is actually better than WSL 1).
|
|
if (LxsstuVmMode())
|
|
{
|
|
VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink3", L"ntlink2\\..\\file.txt", false));
|
|
}
|
|
else
|
|
{
|
|
VERIFY_NO_THROW(VerifyDrvFsLxSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink3"));
|
|
}
|
|
|
|
VERIFY_NO_THROW(VerifyDrvFsLxSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink4"));
|
|
VERIFY_NO_THROW(VerifyDrvFsLxSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink5"));
|
|
VERIFY_NO_THROW(VerifyDrvFsLxSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink6"));
|
|
|
|
// Plan 9 and VirtioFs don't know about the Linux mount point on "dir", so it creates an NT link in this case.
|
|
if (LxsstuVmMode())
|
|
{
|
|
VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink7", L"dir\\..\\file.txt", false));
|
|
}
|
|
else
|
|
{
|
|
VERIFY_NO_THROW(VerifyDrvFsLxSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR "\\lxlink7"));
|
|
}
|
|
|
|
//
|
|
// Check metadata is readable using Windows APIs.
|
|
//
|
|
|
|
if (TestMode == LXSST_DRVFS_METADATA_TEST_MODE)
|
|
{
|
|
VerifyDrvFsMetadata();
|
|
}
|
|
}
|
|
|
|
static void VfsAccessDrvFs()
|
|
{
|
|
VERIFY_NO_THROW(LxsstuRunTest(L"/data/test/wsl_unit_tests vfsaccess drvfs", L"vfsaccess_drvfs"));
|
|
}
|
|
|
|
static void FsCommonDrvFs()
|
|
{
|
|
VERIFY_NO_THROW(LxsstuRunTest(L"/data/test/wsl_unit_tests fscommon drvfs", L"fscommon_drvfs"));
|
|
}
|
|
|
|
void DrvFs(DrvFsMode Mode)
|
|
{
|
|
SKIP_TEST_ARM64();
|
|
|
|
VERIFY_NO_THROW(DrvFsCommon(LX_DRVFS_DISABLE_NONE, Mode));
|
|
}
|
|
|
|
void DrvFsFat(DrvFsMode Mode)
|
|
{
|
|
SKIP_TEST_ARM64();
|
|
|
|
constexpr auto MountPoint = "C:\\lxss_fat";
|
|
constexpr auto VhdPath = "C:\\lxss_fat.vhdx";
|
|
auto Cleanup = wil::scope_exit([MountPoint, VhdPath] { DeleteVolume(MountPoint, VhdPath); });
|
|
|
|
VERIFY_NO_THROW(CreateVolume("fat32", 100, MountPoint, VhdPath));
|
|
VERIFY_NO_THROW(
|
|
LxsstuRunTest((L"bash -c '" + SkipUnstableTestEnvVar + L" /data/test/wsl_unit_tests drvfs -m 3'").c_str(), L"drvfs3"));
|
|
}
|
|
|
|
void DrvFsSmb(DrvFsMode Mode)
|
|
{
|
|
SKIP_TEST_ARM64();
|
|
|
|
if (Mode == DrvFsMode::VirtioFs)
|
|
{
|
|
LogSkipped("TODO: debug virtiofs handling of //localhost/C$ style paths");
|
|
return;
|
|
}
|
|
|
|
VERIFY_NO_THROW(
|
|
LxsstuRunTest((L"bash -c '" + SkipUnstableTestEnvVar + L" /data/test/wsl_unit_tests drvfs -m 4'").c_str(), L"drvfs4"));
|
|
}
|
|
|
|
void DrvFsMetadata(DrvFsMode Mode)
|
|
{
|
|
SKIP_TEST_ARM64();
|
|
|
|
VERIFY_NO_THROW(DrvFsCommon(LXSST_DRVFS_METADATA_TEST_MODE, Mode));
|
|
}
|
|
|
|
void DrvfsMountElevated(DrvFsMode Mode)
|
|
{
|
|
WSL2_TEST_ONLY();
|
|
WINDOWS_11_TEST_ONLY(); // TODO: Enable on Windows 10 when virtio support is added
|
|
SKIP_TEST_ARM64();
|
|
|
|
TerminateDistribution();
|
|
WslKeepAlive keepAlive;
|
|
|
|
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode);
|
|
}
|
|
|
|
void DrvfsMountElevatedDifferentConsole(DrvFsMode Mode)
|
|
{
|
|
WSL2_TEST_ONLY();
|
|
WINDOWS_11_TEST_ONLY(); // TODO: Enable on Windows 10 when virtio support is added
|
|
SKIP_TEST_ARM64();
|
|
|
|
TerminateDistribution();
|
|
WslKeepAlive keepAlive;
|
|
|
|
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, Mode);
|
|
}
|
|
|
|
void DrvfsMountNonElevated(DrvFsMode Mode)
|
|
{
|
|
WSL2_TEST_ONLY();
|
|
WINDOWS_11_TEST_ONLY(); // TODO: Enable on Windows 10 when virtio support is added
|
|
SKIP_TEST_ARM64();
|
|
|
|
TerminateDistribution();
|
|
|
|
const auto nonElevatedToken = GetNonElevatedToken();
|
|
WslKeepAlive keepAlive(nonElevatedToken.get());
|
|
|
|
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode);
|
|
}
|
|
|
|
void DrvfsMountNonElevatedDifferentConsole(DrvFsMode Mode)
|
|
{
|
|
WSL2_TEST_ONLY();
|
|
WINDOWS_11_TEST_ONLY(); // TODO: Enable on Windows 10 when virtio support is added
|
|
SKIP_TEST_ARM64();
|
|
|
|
TerminateDistribution();
|
|
|
|
const auto nonElevatedToken = GetNonElevatedToken();
|
|
WslKeepAlive keepAlive(nonElevatedToken.get());
|
|
|
|
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, Mode);
|
|
}
|
|
|
|
void DrvfsMountElevatedSystemDistroEnabled(DrvFsMode Mode)
|
|
{
|
|
WSL2_TEST_ONLY();
|
|
WINDOWS_11_TEST_ONLY(); // TODO: Enable on Windows 10 when virtio support is added
|
|
SKIP_TEST_ARM64();
|
|
|
|
WslConfigChange config(LxssGenerateTestConfig({.guiApplications = true, .drvFsMode = Mode}));
|
|
WslKeepAlive keepAlive;
|
|
|
|
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode);
|
|
}
|
|
|
|
void DrvfsMountNonElevatedSystemDistroEnabled(DrvFsMode Mode)
|
|
{
|
|
WSL2_TEST_ONLY();
|
|
WINDOWS_11_TEST_ONLY(); // TODO: Enable on Windows 10 when virtio support is added
|
|
SKIP_TEST_ARM64();
|
|
|
|
WslConfigChange config(LxssGenerateTestConfig({.guiApplications = true, .drvFsMode = Mode}));
|
|
|
|
const auto nonElevatedToken = GetNonElevatedToken();
|
|
WslKeepAlive keepAlive(nonElevatedToken.get());
|
|
|
|
ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode);
|
|
}
|
|
|
|
static void XattrDrvFs(DrvFsMode Mode)
|
|
{
|
|
SKIP_TEST_ARM64();
|
|
|
|
VERIFY_NO_THROW(LxsstuRunTest(L"/data/test/wsl_unit_tests xattr drvfs", L"xattr_drvfs"));
|
|
}
|
|
|
|
void DrvFsReFs(DrvFsMode Mode)
|
|
{
|
|
SKIP_TEST_ARM64();
|
|
WSL_TEST_VERSION_REQUIRED(wsl::windows::common::helpers::WindowsBuildNumbers::Germanium);
|
|
|
|
constexpr auto MountPoint = "C:\\lxss_refs";
|
|
constexpr auto VhdPath = "C:\\lxss_refs.vhdx";
|
|
auto Cleanup = wil::scope_exit([MountPoint, VhdPath] { DeleteVolume(MountPoint, VhdPath); });
|
|
|
|
VERIFY_NO_THROW(CreateVolume("refs", 50000, MountPoint, VhdPath));
|
|
VERIFY_NO_THROW(
|
|
LxsstuRunTest((L"bash -c '" + SkipUnstableTestEnvVar + L" /data/test/wsl_unit_tests drvfs -m 6'").c_str(), L"drvfs6"));
|
|
}
|
|
|
|
void WslPath(DrvFsMode Mode)
|
|
{
|
|
VERIFY_NO_THROW(LxsstuRunTest(L"/data/test/wsl_unit_tests wslpath", L"wslpath"));
|
|
|
|
auto testWslPath = [](const std::wstring& testDir) {
|
|
auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { std::filesystem::remove_all(testDir); });
|
|
|
|
std::filesystem::create_directory(testDir);
|
|
|
|
auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L"wslpath -aw '{}'", testDir));
|
|
VERIFY_ARE_EQUAL((std::filesystem::canonical(std::filesystem::current_path()) / testDir).wstring() + L"\n", out);
|
|
|
|
std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(std::format(L"wslpath -wa '{}'", testDir));
|
|
VERIFY_ARE_EQUAL((std::filesystem::canonical(std::filesystem::current_path()) / testDir).wstring() + L"\n", out);
|
|
|
|
std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(std::format(L"wslpath '{}'", testDir));
|
|
VERIFY_ARE_EQUAL(std::format(L"{}\n", testDir), out);
|
|
|
|
std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(std::format(L"wslpath -a '{}'", testDir));
|
|
VERIFY_IS_TRUE(out.find(L"/mnt/") == 0);
|
|
};
|
|
|
|
testWslPath(L"wslpath-test-dir");
|
|
testWslPath(L"wslpath-测试目录-テスト");
|
|
}
|
|
|
|
void DrvFsMountUnicodePath(DrvFsMode Mode)
|
|
{
|
|
WSL2_TEST_ONLY();
|
|
|
|
// Create a Windows directory with unicode characters
|
|
constexpr auto unicodeDir = L"C:\\drvfs-测试-テスト";
|
|
auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { std::filesystem::remove_all(unicodeDir); });
|
|
|
|
std::filesystem::create_directory(unicodeDir);
|
|
|
|
// Create a test file inside the directory
|
|
const auto testFilePath = std::filesystem::path(unicodeDir) / L"test-file.txt";
|
|
{
|
|
std::ofstream testFile(testFilePath);
|
|
testFile << "hello from unicode path";
|
|
}
|
|
|
|
// Mount the unicode directory using mount -t drvfs
|
|
constexpr auto mountPoint = L"/tmp/unicode-mount-test";
|
|
auto unmountCleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
|
|
LxsstuLaunchWsl(std::format(L"-u root umount '{}'", mountPoint).c_str());
|
|
LxsstuLaunchWsl(std::format(L"-u root rmdir '{}'", mountPoint).c_str());
|
|
});
|
|
|
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L"-u root mkdir -p '{}'", mountPoint).c_str()), 0);
|
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L"-u root mount -t drvfs '{}' '{}'", unicodeDir, mountPoint).c_str()), 0);
|
|
|
|
// Verify we can read the test file through the mount
|
|
auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L"cat '{}/test-file.txt'", mountPoint));
|
|
VERIFY_ARE_EQUAL(L"hello from unicode path", out);
|
|
|
|
// Verify we can list the directory
|
|
std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(std::format(L"ls '{}'", mountPoint));
|
|
VERIFY_IS_TRUE(out.find(L"test-file.txt") != std::wstring::npos);
|
|
}
|
|
|
|
// DrvFsTests Private Methods
|
|
private:
|
|
static VOID CreateDrvFsTestFiles(bool Metadata)
|
|
{
|
|
|
|
THROW_LAST_ERROR_IF(!CreateDirectory(LXSST_DRVFS_TEST_DIR, NULL));
|
|
|
|
//
|
|
// The rwx and readonlyattr test files need read/write EA permission for
|
|
// the metadata test mode because chmod will be called on them.
|
|
//
|
|
|
|
CreateTestFile(LXSST_DRVFS_RWX_TEST_FILE, FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_EXECUTE | DELETE | SYNCHRONIZE, FALSE, INVALID_HANDLE_VALUE);
|
|
|
|
CreateTestFile(LXSST_DRVFS_READONLY_TEST_FILE, FILE_GENERIC_READ | DELETE | SYNCHRONIZE, FALSE, INVALID_HANDLE_VALUE);
|
|
|
|
CreateTestFile(LXSST_DRVFS_WRITEONLY_TEST_FILE, FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES | FILE_READ_EA | DELETE | SYNCHRONIZE, FALSE, INVALID_HANDLE_VALUE);
|
|
|
|
CreateTestFile(
|
|
LXSST_DRVFS_EXECUTEONLY_TEST_DIR,
|
|
FILE_TRAVERSE | FILE_DELETE_CHILD | FILE_ADD_FILE | FILE_READ_ATTRIBUTES | FILE_READ_EA | DELETE | SYNCHRONIZE | READ_CONTROL,
|
|
TRUE,
|
|
INVALID_HANDLE_VALUE);
|
|
|
|
CreateTestFile(LXSST_DRVFS_EXECUTEONLY_TEST_DIR_CHILD, FILE_GENERIC_READ | DELETE | SYNCHRONIZE, FALSE, INVALID_HANDLE_VALUE);
|
|
|
|
CreateTestFile(LXSST_DRVFS_READONLY_TEST_DIR, FILE_GENERIC_READ | DELETE | SYNCHRONIZE, TRUE, INVALID_HANDLE_VALUE);
|
|
|
|
CreateTestFile(LXSST_DRVFS_READONLYATTR_TEST_FILE, FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_EXECUTE | DELETE | SYNCHRONIZE, FALSE, INVALID_HANDLE_VALUE);
|
|
|
|
THROW_LAST_ERROR_IF(!SetFileAttributes(LXSST_DRVFS_READONLYATTR_TEST_FILE, FILE_ATTRIBUTE_READONLY));
|
|
|
|
CreateTestFile(LXSST_DRVFS_READONLYATTRDEL_TEST_FILE, FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_EXECUTE | DELETE | SYNCHRONIZE, FALSE, INVALID_HANDLE_VALUE);
|
|
|
|
THROW_LAST_ERROR_IF(!SetFileAttributes(LXSST_DRVFS_READONLYATTRDEL_TEST_FILE, FILE_ATTRIBUTE_READONLY));
|
|
|
|
//
|
|
// Copy the wsl_unit_tests executable to an execute-only file on DrvFs.
|
|
//
|
|
|
|
const std::wstring Path = L"\\\\wsl.localhost\\" LXSS_DISTRO_NAME_TEST_L L"\\data\\test\\wsl_unit_tests";
|
|
const wil::unique_hfile File(CreateFile(
|
|
Path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
|
|
|
|
THROW_LAST_ERROR_IF(!File);
|
|
CreateTestFile(
|
|
LXSST_DRVFS_EXECUTEONLY_TEST_FILE,
|
|
FILE_EXECUTE | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | DELETE | SYNCHRONIZE | READ_CONTROL,
|
|
FALSE,
|
|
File.get());
|
|
|
|
THROW_LAST_ERROR_IF(!CreateDirectory(LXSST_DRVFS_REPARSE_TEST_DIR, nullptr));
|
|
|
|
THROW_LAST_ERROR_IF(!CreateDirectory(LXSST_DRVFS_REPARSE_TEST_DIR L"\\test", nullptr));
|
|
|
|
THROW_LAST_ERROR_IF(!CreateDirectory(LXSST_DRVFS_REPARSE_TEST_DIR L"\\test\\linktarget", nullptr));
|
|
|
|
THROW_LAST_ERROR_IF(!CreateSymbolicLink(
|
|
LXSST_DRVFS_REPARSE_TEST_DIR L"\\absolutelink", LXSST_DRVFS_REPARSE_TEST_DIR L"\\test\\linktarget", SYMBOLIC_LINK_FLAG_DIRECTORY));
|
|
|
|
THROW_LAST_ERROR_IF(!CreateSymbolicLink(LXSST_DRVFS_REPARSE_TEST_DIR L"\\relativelink", L"test\\linktarget", SYMBOLIC_LINK_FLAG_DIRECTORY));
|
|
|
|
{
|
|
const wil::unique_hfile TargetFile(CreateFile(
|
|
LXSST_DRVFS_REPARSE_TEST_DIR L"\\test\\filetarget",
|
|
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
nullptr,
|
|
CREATE_NEW,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
nullptr));
|
|
|
|
THROW_LAST_ERROR_IF(!TargetFile);
|
|
}
|
|
|
|
THROW_LAST_ERROR_IF(!CreateSymbolicLink(LXSST_DRVFS_REPARSE_TEST_DIR L"\\filelink", L"test\\filetarget", 0));
|
|
|
|
CreateJunction(LXSST_DRVFS_REPARSE_TEST_DIR L"\\junction", LXSST_DRVFS_REPARSE_TEST_DIR L"\\test\\linktarget");
|
|
|
|
// DrvFs does not create V1 symlinks anymore; create one here manually to ensure it can still
|
|
// read them.
|
|
CreateV1Symlink(LXSST_DRVFS_REPARSE_TEST_DIR L"\\v1link", "/v1/symlink/target");
|
|
CreateAppExecLink(LXSST_DRVFS_REPARSE_TEST_DIR L"\\appexeclink");
|
|
|
|
if (Metadata != false)
|
|
{
|
|
THROW_LAST_ERROR_IF(!CreateDirectory(LXSST_DRVFS_METADATA_TEST_DIR, nullptr));
|
|
|
|
CreateMetadataTestFile(LXSST_DRVFS_METADATA_TEST_DIR "\\baduid", LX_UID_INVALID, 3001, LX_S_IFREG | 0644, 0, 0, false);
|
|
|
|
CreateMetadataTestFile(LXSST_DRVFS_METADATA_TEST_DIR "\\badgid", 3000, LX_GID_INVALID, LX_S_IFREG | 0644, 0, 0, false);
|
|
|
|
CreateMetadataTestFile(LXSST_DRVFS_METADATA_TEST_DIR "\\badmode", 3000, 3001, 0x10000 | LX_S_IFREG | 0644, 0, 0, false);
|
|
|
|
CreateMetadataTestFile(LXSST_DRVFS_METADATA_TEST_DIR "\\badtype1", 3000, 3001, LX_S_IFDIR | 0755, 0, 0, false);
|
|
|
|
CreateMetadataTestFile(LXSST_DRVFS_METADATA_TEST_DIR "\\badtype2", 3000, 3001, LX_S_IFLNK | 0777, 0, 0, false);
|
|
|
|
CreateMetadataTestFile(LXSST_DRVFS_METADATA_TEST_DIR "\\nondevice", 3000, 3001, LX_S_IFREG | 0644, 1, 2, true);
|
|
}
|
|
}
|
|
|
|
static VOID CreateTestFile(_In_z_ LPCWSTR Filename, _In_ DWORD Permissions, _In_ BOOLEAN Directory, _In_ HANDLE SourceFile)
|
|
{
|
|
|
|
BYTE Buffer[4096];
|
|
DWORD BytesRead;
|
|
|
|
//
|
|
// Create the SID for the BUILTIN\Administrators group.
|
|
//
|
|
|
|
auto [AdminSid, SidBuffer] =
|
|
wsl::windows::common::security::CreateSid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
|
|
|
|
//
|
|
// Set the permissions for the SID.
|
|
//
|
|
|
|
EXPLICIT_ACCESS Access;
|
|
RtlZeroMemory(&Access, sizeof(Access));
|
|
Access.grfAccessPermissions = Permissions;
|
|
Access.grfAccessMode = SET_ACCESS;
|
|
Access.grfInheritance = NO_INHERITANCE;
|
|
Access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
Access.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
|
|
Access.Trustee.ptstrName = (LPTSTR)AdminSid;
|
|
|
|
//
|
|
// Allocate an ACL with the permissions.
|
|
//
|
|
|
|
wil::unique_any<PACL, decltype(&::LocalFree), ::LocalFree> Acl;
|
|
THROW_IF_WIN32_ERROR(SetEntriesInAcl(1, &Access, NULL, &Acl));
|
|
|
|
//
|
|
// Create a security descriptor and set the ACL.
|
|
//
|
|
|
|
const wil::unique_hlocal_security_descriptor Descriptor(::LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH));
|
|
|
|
THROW_LAST_ERROR_IF(!Descriptor);
|
|
THROW_LAST_ERROR_IF(!InitializeSecurityDescriptor(Descriptor.get(), SECURITY_DESCRIPTOR_REVISION));
|
|
|
|
THROW_LAST_ERROR_IF(!SetSecurityDescriptorDacl(Descriptor.get(), TRUE, Acl.get(), FALSE));
|
|
|
|
//
|
|
// Create security attributes that point to the descriptor.
|
|
//
|
|
|
|
SECURITY_ATTRIBUTES Attributes;
|
|
RtlZeroMemory(&Attributes, sizeof(Attributes));
|
|
Attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
Attributes.lpSecurityDescriptor = Descriptor.get();
|
|
Attributes.bInheritHandle = FALSE;
|
|
|
|
//
|
|
// Create a file or directory with the security attributes.
|
|
//
|
|
|
|
if (Directory == FALSE)
|
|
{
|
|
const wil::unique_hfile File(
|
|
CreateFile(Filename, GENERIC_WRITE | SYNCHRONIZE, 0, &Attributes, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL));
|
|
|
|
THROW_LAST_ERROR_IF(!File);
|
|
|
|
//
|
|
// If a source file was specified, copy its contents.
|
|
//
|
|
|
|
if (SourceFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
THROW_LAST_ERROR_IF(!ReadFile(SourceFile, Buffer, sizeof(Buffer), &BytesRead, NULL));
|
|
|
|
while (BytesRead > 0)
|
|
{
|
|
THROW_LAST_ERROR_IF(!WriteFile(File.get(), Buffer, BytesRead, NULL, NULL));
|
|
|
|
THROW_LAST_ERROR_IF(!ReadFile(SourceFile, Buffer, sizeof(Buffer), &BytesRead, NULL));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
THROW_LAST_ERROR_IF(!CreateDirectory(Filename, &Attributes));
|
|
}
|
|
}
|
|
|
|
static VOID CreateMetadataTestFile(
|
|
_In_ LPCWSTR Filename, _In_ ULONG Uid, _In_ ULONG Gid, _In_ ULONG Mode, _In_ ULONG DeviceIdMajor, _In_ ULONG DeviceIdMinor, _In_ bool IncludeDeviceId)
|
|
{
|
|
|
|
//
|
|
// Each individual EA entry must be aligned on a 4 byte boundary, but the
|
|
// value inside each EA struct must not be. Therefore, set packing to 1
|
|
// byte, and add padding to manually align the entries.
|
|
//
|
|
|
|
#pragma pack(push, 1)
|
|
|
|
struct
|
|
{
|
|
struct
|
|
{
|
|
union
|
|
{
|
|
FILE_FULL_EA_INFORMATION Header;
|
|
CHAR Buffer[FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + LXSST_METADATA_EA_NAME_LENGTH + 1];
|
|
};
|
|
|
|
ULONG Uid;
|
|
} Uid;
|
|
|
|
CHAR Padding1;
|
|
struct
|
|
{
|
|
union
|
|
{
|
|
FILE_FULL_EA_INFORMATION Header;
|
|
CHAR Buffer[FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + LXSST_METADATA_EA_NAME_LENGTH + 1];
|
|
};
|
|
|
|
ULONG Gid;
|
|
} Gid;
|
|
|
|
CHAR Padding2;
|
|
struct
|
|
{
|
|
union
|
|
{
|
|
FILE_FULL_EA_INFORMATION Header;
|
|
CHAR Buffer[FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + LXSST_METADATA_EA_NAME_LENGTH + 1];
|
|
};
|
|
|
|
ULONG Mode;
|
|
} Mode;
|
|
|
|
CHAR Padding3;
|
|
struct
|
|
{
|
|
union
|
|
{
|
|
FILE_FULL_EA_INFORMATION Header;
|
|
CHAR Buffer[FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + LXSST_METADATA_EA_NAME_LENGTH + 1];
|
|
};
|
|
|
|
ULONG DeviceIdMajor;
|
|
ULONG DeviceIdMinor;
|
|
} DeviceId;
|
|
} EaBuffer;
|
|
|
|
#pragma pack(pop)
|
|
|
|
RtlZeroMemory(&EaBuffer, sizeof(EaBuffer));
|
|
EaBuffer.Uid.Header.EaNameLength = LXSST_METADATA_EA_NAME_LENGTH;
|
|
EaBuffer.Uid.Header.EaValueLength = sizeof(ULONG);
|
|
RtlCopyMemory(EaBuffer.Uid.Header.EaName, LX_FILE_METADATA_UID_EA_NAME, LXSST_METADATA_EA_NAME_LENGTH);
|
|
|
|
EaBuffer.Uid.Uid = Uid;
|
|
EaBuffer.Uid.Header.NextEntryOffset = (ULONG)((PUCHAR)&EaBuffer.Gid - (PUCHAR)&EaBuffer.Uid);
|
|
EaBuffer.Gid.Header.EaNameLength = LXSST_METADATA_EA_NAME_LENGTH;
|
|
EaBuffer.Gid.Header.EaValueLength = sizeof(ULONG);
|
|
RtlCopyMemory(EaBuffer.Gid.Header.EaName, LX_FILE_METADATA_GID_EA_NAME, LXSST_METADATA_EA_NAME_LENGTH);
|
|
|
|
EaBuffer.Gid.Gid = Gid;
|
|
EaBuffer.Gid.Header.NextEntryOffset = (ULONG)((PUCHAR)&EaBuffer.Mode - (PUCHAR)&EaBuffer.Gid);
|
|
EaBuffer.Mode.Header.EaNameLength = LXSST_METADATA_EA_NAME_LENGTH;
|
|
EaBuffer.Mode.Header.EaValueLength = sizeof(ULONG);
|
|
RtlCopyMemory(EaBuffer.Mode.Header.EaName, LX_FILE_METADATA_MODE_EA_NAME, LXSST_METADATA_EA_NAME_LENGTH);
|
|
|
|
EaBuffer.Mode.Mode = Mode;
|
|
if (IncludeDeviceId != false)
|
|
{
|
|
EaBuffer.Mode.Header.NextEntryOffset = (ULONG)((PUCHAR)&EaBuffer.DeviceId - (PUCHAR)&EaBuffer.Mode);
|
|
EaBuffer.DeviceId.Header.EaNameLength = LXSST_METADATA_EA_NAME_LENGTH;
|
|
EaBuffer.DeviceId.Header.EaValueLength = sizeof(ULONG);
|
|
RtlCopyMemory(EaBuffer.DeviceId.Header.EaName, LX_FILE_METADATA_DEVICE_ID_EA_NAME, LXSST_METADATA_EA_NAME_LENGTH);
|
|
|
|
EaBuffer.DeviceId.DeviceIdMajor = DeviceIdMajor;
|
|
EaBuffer.DeviceId.DeviceIdMinor = DeviceIdMinor;
|
|
}
|
|
|
|
const std::wstring NtPath{std::wstring(L"\\DosDevices\\") + Filename};
|
|
UNICODE_STRING Name;
|
|
RtlInitUnicodeString(&Name, NtPath.c_str());
|
|
OBJECT_ATTRIBUTES Attributes;
|
|
InitializeObjectAttributes(&Attributes, &Name, 0, nullptr, 0);
|
|
wil::unique_hfile File;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
THROW_IF_NTSTATUS_FAILED(NtCreateFile(
|
|
&File,
|
|
FILE_GENERIC_READ,
|
|
&Attributes,
|
|
&IoStatus,
|
|
nullptr,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
|
|
FILE_CREATE,
|
|
0,
|
|
&EaBuffer,
|
|
sizeof(EaBuffer)));
|
|
}
|
|
|
|
static VOID VerifyDrvFsMetadata()
|
|
{
|
|
wil::unique_hfile File;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
UNICODE_STRING Name;
|
|
RtlInitUnicodeString(&Name, L"\\DosDevices\\" LXSST_DRVFS_METADATA_TEST_DIR);
|
|
OBJECT_ATTRIBUTES Attributes;
|
|
InitializeObjectAttributes(&Attributes, &Name, 0, nullptr, 0);
|
|
THROW_IF_NTSTATUS_FAILED(NtCreateFile(
|
|
&File, FILE_READ_EA, &Attributes, &IoStatus, nullptr, 0, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), FILE_OPEN, FILE_DIRECTORY_FILE, nullptr, 0));
|
|
|
|
UCHAR Buffer[1000];
|
|
THROW_IF_NTSTATUS_FAILED(ZwQueryEaFile(File.get(), &IoStatus, Buffer, sizeof(Buffer), FALSE, nullptr, 0, nullptr, TRUE));
|
|
|
|
bool FoundUid = false;
|
|
bool FoundGid = false;
|
|
bool FoundMode = false;
|
|
PFILE_FULL_EA_INFORMATION EaInfo = (PFILE_FULL_EA_INFORMATION)Buffer;
|
|
for (;;)
|
|
{
|
|
VERIFY_ARE_EQUAL(EaInfo->EaNameLength, 6);
|
|
VERIFY_ARE_EQUAL(EaInfo->EaValueLength, 4);
|
|
std::string EaName{EaInfo->EaName};
|
|
ULONG Value = *(PULONG)((PCHAR)EaInfo->EaName + EaInfo->EaNameLength + 1);
|
|
if (EaName == LX_FILE_METADATA_UID_EA_NAME)
|
|
{
|
|
FoundUid = true;
|
|
VERIFY_ARE_EQUAL(Value, 0x11223344ul);
|
|
}
|
|
else if (EaName == LX_FILE_METADATA_GID_EA_NAME)
|
|
{
|
|
FoundGid = true;
|
|
VERIFY_ARE_EQUAL(Value, 0x55667788ul);
|
|
}
|
|
else if (EaName == LX_FILE_METADATA_MODE_EA_NAME)
|
|
{
|
|
FoundMode = true;
|
|
VERIFY_ARE_EQUAL(Value, (ULONG)(LX_S_IFDIR | 0775));
|
|
}
|
|
else
|
|
{
|
|
VERIFY_FAIL(L"Unexpected EA on file.");
|
|
}
|
|
|
|
if (EaInfo->NextEntryOffset == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
EaInfo = (PFILE_FULL_EA_INFORMATION)((PCHAR)EaInfo + EaInfo->NextEntryOffset);
|
|
};
|
|
|
|
VERIFY_IS_TRUE(FoundUid);
|
|
VERIFY_IS_TRUE(FoundGid);
|
|
VERIFY_IS_TRUE(FoundMode);
|
|
}
|
|
|
|
static VOID CreateJunction(_In_ const std::wstring& Junction, _In_ const std::wstring& Target)
|
|
{
|
|
|
|
//
|
|
// The logic for creating a junction was taken from mklink.
|
|
//
|
|
|
|
THROW_LAST_ERROR_IF(!CreateDirectory(Junction.c_str(), NULL));
|
|
const wil::unique_hfile Dir(CreateFile(
|
|
Junction.c_str(), FILE_GENERIC_WRITE, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr));
|
|
|
|
THROW_LAST_ERROR_IF(!Dir);
|
|
|
|
UNICODE_STRING LinkPath = {0};
|
|
auto Cleanup = wil::scope_exit([&] {
|
|
if (LinkPath.Buffer != nullptr)
|
|
{
|
|
FREE(LinkPath.Buffer);
|
|
}
|
|
});
|
|
|
|
THROW_IF_NTSTATUS_FAILED(RtlDosPathNameToNtPathName_U_WithStatus(Target.c_str(), &LinkPath, nullptr, nullptr));
|
|
|
|
//
|
|
// The buffer needs space for the substitute name and the print name, with
|
|
// NULL characters. This can't overflow since they are all paths with
|
|
// lengths less than MAXUSHORT.
|
|
//
|
|
|
|
const ULONG ReparseBufferSize = (ULONG)(FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer[0]) +
|
|
Target.length() * sizeof(WCHAR) + LinkPath.Length + 2 * sizeof(UNICODE_NULL));
|
|
|
|
//
|
|
// Allocate the reparse data buffer.
|
|
//
|
|
|
|
const std::unique_ptr<REPARSE_DATA_BUFFER> Reparse((PREPARSE_DATA_BUFFER) new char[ReparseBufferSize]);
|
|
|
|
ZeroMemory(Reparse.get(), ReparseBufferSize);
|
|
Reparse->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
|
|
|
|
//
|
|
// The data length is the buffer size excluding the header.
|
|
//
|
|
|
|
Reparse->ReparseDataLength = (USHORT)(ReparseBufferSize - REPARSE_DATA_BUFFER_HEADER_SIZE);
|
|
|
|
//
|
|
// Copy the NT path into the buffer for the substitute name.
|
|
//
|
|
|
|
Reparse->MountPointReparseBuffer.SubstituteNameLength = LinkPath.Length;
|
|
RtlCopyMemory(Reparse->MountPointReparseBuffer.PathBuffer, LinkPath.Buffer, LinkPath.Length);
|
|
|
|
const USHORT Offset = LinkPath.Length + sizeof(UNICODE_NULL);
|
|
|
|
//
|
|
// Copy the DOS path into the buffer for the print name.
|
|
//
|
|
|
|
Reparse->MountPointReparseBuffer.PrintNameOffset = Offset;
|
|
Reparse->MountPointReparseBuffer.PrintNameLength = (USHORT)(Target.length() * sizeof(WCHAR));
|
|
|
|
RtlCopyMemory((Reparse->MountPointReparseBuffer.PathBuffer + (Offset / sizeof(WCHAR))), Target.c_str(), Target.length() * sizeof(WCHAR));
|
|
|
|
//
|
|
// Set the reparse point on the file.
|
|
//
|
|
|
|
IO_STATUS_BLOCK IoStatus;
|
|
THROW_IF_NTSTATUS_FAILED(NtFsControlFile(
|
|
Dir.get(), nullptr, nullptr, nullptr, &IoStatus, FSCTL_SET_REPARSE_POINT, Reparse.get(), ReparseBufferSize, nullptr, 0));
|
|
|
|
return;
|
|
}
|
|
|
|
static VOID CreateV1Symlink(const std::wstring& Symlink, std::string_view Target)
|
|
{
|
|
|
|
//
|
|
// Create a symlink using the V1 LX symlink format, where the target is
|
|
// stored in the file data. The reparse data only contains a version
|
|
// number.
|
|
//
|
|
|
|
union
|
|
{
|
|
REPARSE_DATA_BUFFER Header;
|
|
struct
|
|
{
|
|
CHAR Buffer[REPARSE_DATA_BUFFER_HEADER_SIZE];
|
|
ULONG Version;
|
|
} Data;
|
|
} Reparse{};
|
|
|
|
const ULONG ReparseBufferSize = REPARSE_DATA_BUFFER_HEADER_SIZE + sizeof(ULONG);
|
|
|
|
//
|
|
// The data length is the buffer size excluding the header.
|
|
//
|
|
|
|
Reparse.Header.ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;
|
|
Reparse.Header.ReparseDataLength = sizeof(ULONG);
|
|
Reparse.Data.Version = 1;
|
|
|
|
const auto File = CreateReparsePoint(Symlink, &Reparse, ReparseBufferSize);
|
|
|
|
//
|
|
// Write the target to the file.
|
|
//
|
|
|
|
DWORD written;
|
|
THROW_IF_WIN32_BOOL_FALSE(WriteFile(File.get(), Target.data(), gsl::narrow_cast<DWORD>(Target.size()), &written, nullptr));
|
|
VERIFY_ARE_EQUAL(Target.size(), written);
|
|
|
|
return;
|
|
}
|
|
|
|
static VOID CreateAppExecLink(const std::wstring& Link)
|
|
{
|
|
//
|
|
// This link will not be valid from Windows's perspective, since it only
|
|
// contains the header and not any actual reparse data. However, it has the
|
|
// right reparse tag which is sufficient to test drvfs's behavior.
|
|
//
|
|
|
|
REPARSE_DATA_BUFFER Reparse{};
|
|
Reparse.ReparseTag = IO_REPARSE_TAG_APPEXECLINK;
|
|
Reparse.ReparseDataLength = 0;
|
|
CreateReparsePoint(Link, &Reparse, REPARSE_DATA_BUFFER_HEADER_SIZE);
|
|
}
|
|
|
|
static wil::unique_hfile CreateReparsePoint(_In_ const std::wstring& Path, _In_ PVOID ReparseBuffer, _In_ ULONG ReparseBufferSize)
|
|
{
|
|
wil::unique_hfile File{CreateFile(
|
|
Path.c_str(), FILE_GENERIC_WRITE, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr)};
|
|
|
|
THROW_LAST_ERROR_IF(!File);
|
|
IO_STATUS_BLOCK IoStatus;
|
|
THROW_IF_NTSTATUS_FAILED(NtFsControlFile(
|
|
File.get(), nullptr, nullptr, nullptr, &IoStatus, FSCTL_SET_REPARSE_POINT, ReparseBuffer, ReparseBufferSize, nullptr, 0));
|
|
|
|
return File;
|
|
}
|
|
|
|
static VOID CreateVolume(LPCSTR FileSystem, ULONG MaxSizeInMb, LPCSTR MountPoint, LPCSTR VhdPath)
|
|
{
|
|
THROW_LAST_ERROR_IF(!CreateDirectoryA(MountPoint, NULL));
|
|
|
|
const auto CreateScript = std::vformat(
|
|
"create vdisk file={} maximum={} type=expandable\n"
|
|
"select vdisk file={}\n"
|
|
"attach vdisk\n"
|
|
"create partition primary\n"
|
|
"select partition 1\n"
|
|
"online volume\n"
|
|
"format fs={} quick\n"
|
|
"assign mount={}\n",
|
|
std::make_format_args(VhdPath, MaxSizeInMb, VhdPath, FileSystem, MountPoint));
|
|
|
|
RunDiskpartScript(CreateScript.c_str());
|
|
}
|
|
|
|
static VOID RunDiskpartScript(LPCSTR Script)
|
|
{
|
|
const std::wstring ScriptFileName = wsl::windows::common::filesystem::GetTempFilename();
|
|
|
|
std::ofstream ScriptFile(ScriptFileName);
|
|
THROW_LAST_ERROR_IF(!ScriptFile);
|
|
|
|
auto Cleanup = wil::scope_exit([&] { DeleteFileW(ScriptFileName.c_str()); });
|
|
|
|
ScriptFile << Script;
|
|
ScriptFile.close();
|
|
|
|
std::wstring CommandLine = L"diskpart.exe /s " + ScriptFileName;
|
|
THROW_HR_IF(E_FAIL, ((wsl::windows::common::helpers::RunProcess(CommandLine)) != 0));
|
|
}
|
|
|
|
static VOID DeleteVolume(LPCSTR MountPoint, LPCSTR VhdPath)
|
|
{
|
|
const auto CleanupScript = std::vformat(
|
|
"select vdisk file={}\n"
|
|
"select partition 1\n"
|
|
"remove all\n"
|
|
"detach vdisk\n",
|
|
std::make_format_args(VhdPath));
|
|
|
|
RunDiskpartScript(CleanupScript.c_str());
|
|
|
|
RemoveDirectoryA(MountPoint);
|
|
DeleteFileA(VhdPath);
|
|
}
|
|
|
|
static void ValidateDrvfsMounts(DWORD CreateProcessFlags, DrvFsMode Mode)
|
|
{
|
|
auto validate = [CreateProcessFlags](const std::wstring& expectedType, HANDLE token) {
|
|
const auto commandLine = LxssGenerateWslCommandLine(L"mount | grep -F '/mnt/c type'");
|
|
|
|
wsl::windows::common::SubProcess process(nullptr, commandLine.c_str(), CreateProcessFlags);
|
|
process.SetToken(token);
|
|
process.SetShowWindow(SW_HIDE);
|
|
|
|
const auto output = process.RunAndCaptureOutput();
|
|
const auto lines = LxssSplitString(output.Stdout, L"\n");
|
|
|
|
VERIFY_ARE_EQUAL(lines.size(), 1);
|
|
|
|
if (!expectedType.empty())
|
|
{
|
|
VERIFY_IS_TRUE(output.Stdout.find(expectedType) == 0);
|
|
}
|
|
};
|
|
|
|
std::wstring elevatedType;
|
|
std::wstring nonElevatedType;
|
|
switch (Mode)
|
|
{
|
|
case DrvFsMode::Plan9:
|
|
elevatedType = L"C:\\";
|
|
nonElevatedType = L"C:\\";
|
|
break;
|
|
case DrvFsMode::Virtio9p:
|
|
elevatedType = L"drvfsa";
|
|
nonElevatedType = L"drvfs";
|
|
break;
|
|
case DrvFsMode::VirtioFs:
|
|
// VirtioFs uses GUIDs as the tag so the value is not predictable.
|
|
break;
|
|
|
|
default:
|
|
LogError("Unexpected mode %d", (int)Mode);
|
|
return;
|
|
}
|
|
|
|
// Validate that mount types are correct in both namespaces
|
|
validate(elevatedType, nullptr);
|
|
|
|
const auto nonElevatedToken = GetNonElevatedToken();
|
|
validate(nonElevatedType, nonElevatedToken.get());
|
|
|
|
// Elevated token should be able to create files at the root of the drive (/mnt/c)
|
|
{
|
|
const auto commandLine =
|
|
LxssGenerateWslCommandLine(L"touch /mnt/c/elevated_test_file.tmp && rm /mnt/c/elevated_test_file.tmp");
|
|
|
|
wsl::windows::common::SubProcess process(nullptr, commandLine.c_str(), CreateProcessFlags);
|
|
process.SetToken(nullptr);
|
|
process.SetShowWindow(SW_HIDE);
|
|
|
|
const auto output = process.RunAndCaptureOutput();
|
|
VERIFY_ARE_EQUAL(0, output.ExitCode, L"Elevated token should be able to create files at /mnt/c");
|
|
}
|
|
|
|
// Non-elevated token should NOT be able to create files at the root of the drive (/mnt/c)
|
|
{
|
|
const auto commandLine = LxssGenerateWslCommandLine(L"touch /mnt/c/nonelevated_test_file.tmp");
|
|
|
|
wsl::windows::common::SubProcess process(nullptr, commandLine.c_str(), CreateProcessFlags);
|
|
process.SetToken(nonElevatedToken.get());
|
|
process.SetShowWindow(SW_HIDE);
|
|
|
|
const auto output = process.RunAndCaptureOutput();
|
|
VERIFY_ARE_NOT_EQUAL(0, output.ExitCode, L"Non-elevated token should NOT be able to create files at /mnt/c (C:\\)");
|
|
}
|
|
}
|
|
|
|
static VOID VerifyDrvFsSymlink(const std::wstring& Path, const std::wstring& ExpectedTarget, bool Directory)
|
|
{
|
|
|
|
const std::wstring NtPath = L"\\DosDevices\\" + Path;
|
|
UNICODE_STRING Name;
|
|
RtlInitUnicodeString(&Name, NtPath.c_str());
|
|
OBJECT_ATTRIBUTES Attributes;
|
|
InitializeObjectAttributes(&Attributes, &Name, 0, nullptr, 0);
|
|
|
|
wil::unique_hfile Symlink;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
THROW_IF_NTSTATUS_FAILED(NtCreateFile(
|
|
&Symlink, FILE_GENERIC_READ, &Attributes, &IoStatus, nullptr, 0, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), FILE_OPEN, FILE_OPEN_REPARSE_POINT, NULL, 0));
|
|
|
|
FILE_ATTRIBUTE_TAG_INFORMATION Info;
|
|
THROW_IF_NTSTATUS_FAILED(NtQueryInformationFile(Symlink.get(), &IoStatus, &Info, sizeof(Info), FileAttributeTagInformation));
|
|
|
|
VERIFY_IS_TRUE((Info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0);
|
|
if (Directory != false)
|
|
{
|
|
VERIFY_IS_TRUE((Info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
|
|
}
|
|
else
|
|
{
|
|
VERIFY_IS_TRUE((Info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
|
|
}
|
|
|
|
VERIFY_ARE_EQUAL(Info.ReparseTag, IO_REPARSE_TAG_SYMLINK);
|
|
|
|
union
|
|
{
|
|
char Buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
|
REPARSE_DATA_BUFFER Data;
|
|
} ReparseData;
|
|
|
|
THROW_IF_NTSTATUS_FAILED(NtFsControlFile(
|
|
Symlink.get(), nullptr, nullptr, nullptr, &IoStatus, FSCTL_GET_REPARSE_POINT, nullptr, 0, &ReparseData, sizeof(ReparseData)));
|
|
|
|
VERIFY_ARE_EQUAL(ReparseData.Data.ReparseTag, IO_REPARSE_TAG_SYMLINK);
|
|
VERIFY_ARE_EQUAL(ReparseData.Data.SymbolicLinkReparseBuffer.Flags, (ULONG)SYMLINK_FLAG_RELATIVE);
|
|
const std::wstring Target(
|
|
(PWCHAR)((PCHAR)ReparseData.Data.SymbolicLinkReparseBuffer.PathBuffer + ReparseData.Data.SymbolicLinkReparseBuffer.SubstituteNameOffset),
|
|
(PWCHAR)((PCHAR)ReparseData.Data.SymbolicLinkReparseBuffer.PathBuffer + ReparseData.Data.SymbolicLinkReparseBuffer.SubstituteNameOffset +
|
|
ReparseData.Data.SymbolicLinkReparseBuffer.SubstituteNameLength));
|
|
|
|
VERIFY_ARE_EQUAL(Target, ExpectedTarget);
|
|
const std::wstring Target2(
|
|
(PWCHAR)((PCHAR)ReparseData.Data.SymbolicLinkReparseBuffer.PathBuffer + ReparseData.Data.SymbolicLinkReparseBuffer.SubstituteNameOffset),
|
|
(PWCHAR)((PCHAR)ReparseData.Data.SymbolicLinkReparseBuffer.PathBuffer + ReparseData.Data.SymbolicLinkReparseBuffer.SubstituteNameOffset +
|
|
ReparseData.Data.SymbolicLinkReparseBuffer.SubstituteNameLength));
|
|
|
|
VERIFY_ARE_EQUAL(Target2, ExpectedTarget);
|
|
}
|
|
|
|
static VOID VerifyDrvFsLxSymlink(const std::wstring& Path)
|
|
{
|
|
|
|
const std::wstring NtPath = L"\\DosDevices\\" + Path;
|
|
UNICODE_STRING Name;
|
|
RtlInitUnicodeString(&Name, NtPath.c_str());
|
|
OBJECT_ATTRIBUTES Attributes;
|
|
InitializeObjectAttributes(&Attributes, &Name, 0, nullptr, 0);
|
|
|
|
IO_STATUS_BLOCK IoStatus;
|
|
FILE_STAT_INFORMATION Info;
|
|
THROW_IF_NTSTATUS_FAILED(NtQueryInformationByName(&Attributes, &IoStatus, &Info, sizeof(Info), FileStatInformation));
|
|
|
|
VERIFY_IS_TRUE((Info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0);
|
|
VERIFY_IS_TRUE((Info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
|
|
VERIFY_ARE_EQUAL(Info.ReparseTag, IO_REPARSE_TAG_LX_SYMLINK);
|
|
}
|
|
};
|
|
|
|
class WSL1 : public DrvFsTests
|
|
{
|
|
WSL_TEST_CLASS(WSL1)
|
|
|
|
bool m_initialized{false};
|
|
TEST_CLASS_SETUP(TestClassSetup)
|
|
{
|
|
if (LxsstuVmMode())
|
|
{
|
|
LogSkipped("This test class is only applicable to WSL1");
|
|
}
|
|
else
|
|
{
|
|
VERIFY_ARE_EQUAL(LxsstuInitialize(FALSE), TRUE);
|
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(LXSST_TESTS_INSTALL_COMMAND_LINE), 0);
|
|
m_initialized = true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
TEST_CLASS_CLEANUP(TestClassCleanup)
|
|
{
|
|
if (m_initialized)
|
|
{
|
|
LxsstuUninitialize(FALSE);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
TEST_METHOD(DrvFsDisableQueryByName)
|
|
{
|
|
WSL1_TEST_ONLY();
|
|
VERIFY_NO_THROW(DrvFsCommon(LX_DRVFS_DISABLE_QUERY_BY_NAME));
|
|
}
|
|
|
|
TEST_METHOD(DrvFsDisableQueryByNameAndStatInfo)
|
|
{
|
|
WSL1_TEST_ONLY();
|
|
VERIFY_NO_THROW(DrvFsCommon(LX_DRVFS_DISABLE_QUERY_BY_NAME_AND_STAT_INFO));
|
|
}
|
|
|
|
TEST_METHOD(VfsAccessDrvFs)
|
|
{
|
|
WSL1_TEST_ONLY();
|
|
DrvFsTests::VfsAccessDrvFs();
|
|
}
|
|
|
|
TEST_METHOD(FsCommonDrvFs)
|
|
{
|
|
WSL1_TEST_ONLY();
|
|
DrvFsTests::FsCommonDrvFs();
|
|
}
|
|
|
|
TEST_METHOD(DrvFs)
|
|
{
|
|
WSL1_TEST_ONLY();
|
|
DrvFsTests::DrvFs(DrvFsMode::WSL1);
|
|
}
|
|
|
|
TEST_METHOD(DrvFsFat)
|
|
{
|
|
WSL1_TEST_ONLY();
|
|
DrvFsTests::DrvFsFat(DrvFsMode::WSL1);
|
|
}
|
|
|
|
TEST_METHOD(DrvFsSmb)
|
|
{
|
|
WSL1_TEST_ONLY();
|
|
DrvFsTests::DrvFsSmb(DrvFsMode::WSL1);
|
|
}
|
|
|
|
TEST_METHOD(DrvFsMetadata)
|
|
{
|
|
WSL1_TEST_ONLY();
|
|
DrvFsTests::DrvFsMetadata(DrvFsMode::WSL1);
|
|
}
|
|
|
|
TEST_METHOD(XattrDrvFs)
|
|
{
|
|
WSL1_TEST_ONLY();
|
|
DrvFsTests::XattrDrvFs(DrvFsMode::WSL1);
|
|
}
|
|
|
|
TEST_METHOD(WslPath)
|
|
{
|
|
WSL1_TEST_ONLY();
|
|
DrvFsTests::WslPath(DrvFsMode::WSL1);
|
|
}
|
|
};
|
|
|
|
#define WSL2_DRVFS_TEST_CLASS(_mode) \
|
|
class WSL2##_mode## : public DrvFsTests \
|
|
{ \
|
|
WSL_TEST_CLASS(WSL2##_mode##) \
|
|
std::unique_ptr<WslConfigChange> m_config; \
|
|
TEST_CLASS_SETUP(TestClassSetup) \
|
|
{ \
|
|
if (!LxsstuVmMode()) \
|
|
{ \
|
|
LogSkipped("This test class is only applicable to WSL2"); \
|
|
} \
|
|
else \
|
|
{ \
|
|
VERIFY_ARE_EQUAL(LxsstuInitialize(FALSE), TRUE); \
|
|
m_config.reset(new WslConfigChange(LxssGenerateTestConfig({.drvFsMode = DrvFsMode::##_mode##}))); \
|
|
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(LXSST_TESTS_INSTALL_COMMAND_LINE), 0); \
|
|
} \
|
|
\
|
|
return true; \
|
|
} \
|
|
\
|
|
TEST_CLASS_CLEANUP(TestClassCleanup) \
|
|
{ \
|
|
if (m_config) \
|
|
{ \
|
|
m_config.reset(); \
|
|
LxsstuUninitialize(FALSE); \
|
|
} \
|
|
\
|
|
return true; \
|
|
} \
|
|
\
|
|
TEST_METHOD(VfsAccessDrvFs) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::VfsAccessDrvFs(); \
|
|
} \
|
|
\
|
|
TEST_METHOD(FsCommonDrvFs) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::FsCommonDrvFs(); \
|
|
} \
|
|
\
|
|
TEST_METHOD(DrvFs) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::DrvFs(DrvFsMode::##_mode##); \
|
|
} \
|
|
\
|
|
TEST_METHOD(DrvFsFat) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::DrvFsFat(DrvFsMode::##_mode##); \
|
|
} \
|
|
\
|
|
TEST_METHOD(DrvFsSmb) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::DrvFsSmb(DrvFsMode::##_mode##); \
|
|
} \
|
|
\
|
|
TEST_METHOD(DrvFsMetadata) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::DrvFsMetadata(DrvFsMode::##_mode##); \
|
|
} \
|
|
\
|
|
TEST_METHOD(DrvfsMountElevated) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::DrvfsMountElevated(DrvFsMode::##_mode##); \
|
|
} \
|
|
\
|
|
TEST_METHOD(DrvfsMountElevatedDifferentConsole) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::DrvfsMountElevatedDifferentConsole(DrvFsMode::##_mode##); \
|
|
} \
|
|
\
|
|
TEST_METHOD(DrvfsMountNonElevated) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::DrvfsMountNonElevated(DrvFsMode::##_mode##); \
|
|
} \
|
|
\
|
|
TEST_METHOD(DrvfsMountNonElevatedDifferentConsole) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::DrvfsMountNonElevatedDifferentConsole(DrvFsMode::##_mode##); \
|
|
} \
|
|
\
|
|
TEST_METHOD(DrvfsMountElevatedSystemDistroEnabled) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::DrvfsMountElevatedSystemDistroEnabled(DrvFsMode::##_mode##); \
|
|
} \
|
|
\
|
|
TEST_METHOD(DrvfsMountNonElevatedSystemDistroEnabled) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::DrvfsMountNonElevatedSystemDistroEnabled(DrvFsMode::##_mode##); \
|
|
} \
|
|
\
|
|
TEST_METHOD(XattrDrvFs) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::XattrDrvFs(DrvFsMode::##_mode##); \
|
|
} \
|
|
\
|
|
TEST_METHOD(DrvFsReFs) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::DrvFsReFs(DrvFsMode::##_mode##); \
|
|
} \
|
|
\
|
|
TEST_METHOD(WslPath) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::WslPath(DrvFsMode::##_mode##); \
|
|
} \
|
|
\
|
|
TEST_METHOD(DrvFsMountUnicodePath) \
|
|
{ \
|
|
WSL2_TEST_ONLY(); \
|
|
DrvFsTests::DrvFsMountUnicodePath(DrvFsMode::##_mode##); \
|
|
} \
|
|
}
|
|
|
|
WSL2_DRVFS_TEST_CLASS(Plan9);
|
|
|
|
WSL2_DRVFS_TEST_CLASS(VirtioFs);
|
|
|
|
// Disabled while an issue with the 6.1 Linux kernel causing disk corruption is investigated.
|
|
// TODO: Enable again once the issue is resolved
|
|
// WSL2_DRVFS_TEST_CLASS(Virtio9p);
|
|
|
|
} // namespace DrvFsTests
|