diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/DumpDriveInformation.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/DumpDriveInformation.cs
new file mode 100644
index 00000000000000..10c6fac755729f
--- /dev/null
+++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/DumpDriveInformation.cs
@@ -0,0 +1,82 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using Microsoft.DotNet.XUnitExtensions;
+using Xunit;
+
+namespace System.IO.Tests
+{
+ public class DumpDriveInformation
+ {
+ // When running both inner and outer loop together, dump only once
+ private static bool s_dumped = false;
+
+ [Fact]
+ [SkipOnPlatform(TestPlatforms.Browser | TestPlatforms.Wasi, "Not applicable")]
+ public void DumpDriveInformationToConsole()
+ {
+ if (s_dumped || !PlatformDetection.IsInHelix)
+ return;
+
+ s_dumped = true;
+
+ // Not really a test, but useful to dump drive/volume information to the test log
+ // to help debug environmental issues with mount volume tests in CI.
+ // Follows the pattern of DescriptionNameTests.DumpRuntimeInformationToConsole.
+
+ Console.WriteLine("### DRIVE INFORMATION");
+ Console.WriteLine($"### Machine: {Environment.MachineName}");
+ Console.WriteLine($"### OS: {RuntimeInformation.OSDescription} ({RuntimeInformation.OSArchitecture})");
+ Console.WriteLine($"### Current directory: {Directory.GetCurrentDirectory()}");
+
+ foreach (DriveInfo drive in DriveInfo.GetDrives())
+ {
+ try
+ {
+ string info = $"### Drive {drive.Name}: Type={drive.DriveType}";
+ if (drive.IsReady)
+ {
+ info += $" Format={drive.DriveFormat} Size={drive.TotalSize / (1024 * 1024)}MB";
+
+ if (OperatingSystem.IsWindows())
+ {
+ char[] volName = new char[260];
+ bool hasGuid = DllImports.GetVolumeNameForVolumeMountPoint(drive.Name, volName, volName.Length);
+ info += hasGuid
+ ? $" VolumeGUID={new string(volName).TrimEnd('\0')}"
+ : $" VolumeGUID=NONE({Marshal.GetLastPInvokeErrorMessage()})";
+ }
+ }
+ else
+ {
+ info += " NotReady";
+ }
+ Console.WriteLine(info);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"### Drive {drive.Name}: error probing: {ex.GetType().Name}: {ex.Message}");
+ }
+ }
+
+ if (OperatingSystem.IsWindows())
+ {
+ string otherNtfs = IOServices.GetNtfsDriveOtherThanCurrent();
+ Console.WriteLine($"### GetNtfsDriveOtherThanCurrent() = {otherNtfs ?? "(null)"}");
+ }
+ }
+
+ [Fact]
+ [OuterLoop]
+ [SkipOnPlatform(TestPlatforms.Browser | TestPlatforms.Wasi, "Not applicable")]
+ public void DumpDriveInformationToConsoleOuter()
+ {
+ // Outer loop runs don't run inner loop tests.
+ // But we want to log this data for any Helix run.
+ DumpDriveInformationToConsole();
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/PortedCommon/DllImports.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/PortedCommon/DllImports.cs
index 41cab0dc0e1ca9..9e3614abae71f3 100644
--- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/PortedCommon/DllImports.cs
+++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/PortedCommon/DllImports.cs
@@ -3,7 +3,6 @@
using System;
using System.Runtime.InteropServices;
-using System.Text;
internal static partial class DllImports
{
@@ -16,4 +15,8 @@ internal static partial class DllImports
[LibraryImport("kernel32.dll", EntryPoint = "GetDriveTypeW", StringMarshalling = StringMarshalling.Utf16, SetLastError = true)]
internal static partial int GetDriveType(string drive);
+
+ [LibraryImport("kernel32.dll", EntryPoint = "GetVolumeNameForVolumeMountPointW", StringMarshalling = StringMarshalling.Utf16, SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static partial bool GetVolumeNameForVolumeMountPoint(string volumeMountPoint, [Out] char[] volumeName, int bufferLength);
}
diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/PortedCommon/IOServices.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/PortedCommon/IOServices.cs
index 9044a5d82cd245..13a3f0b08bb5a6 100644
--- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/PortedCommon/IOServices.cs
+++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/PortedCommon/IOServices.cs
@@ -64,13 +64,27 @@ public static string GetNtfsDriveOtherThan(string drive)
if (!IsReady(otherDrive))
continue;
- if (IsDriveNTFS(otherDrive))
- return otherDrive;
+ if (!IsDriveNTFS(otherDrive))
+ continue;
+
+ // Filter out drives that don't support volume mount point operations
+ // (e.g., SUBST drives, Azure resource disks without volume GUIDs).
+ // These report as Fixed/NTFS/Ready but GetVolumeNameForVolumeMountPoint fails.
+ if (!HasVolumeGuid(otherDrive))
+ continue;
+
+ return otherDrive;
}
return null;
}
+ private static bool HasVolumeGuid(string drive)
+ {
+ char[] volumeName = new char[260];
+ return DllImports.GetVolumeNameForVolumeMountPoint(drive, volumeName, volumeName.Length);
+ }
+
public static string GetNonNtfsDriveOtherThanCurrent()
{
return GetNonNtfsDriveOtherThan(GetCurrentDrive());
diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/System.IO.FileSystem.Tests.csproj
index ca891f26eab4e0..14ae1f46b52bc3 100644
--- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/System.IO.FileSystem.Tests.csproj
+++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/System.IO.FileSystem.Tests.csproj
@@ -42,6 +42,7 @@
+