From a7e549f0d104e7552a198033039de7dd0b63ff9e Mon Sep 17 00:00:00 2001 From: James King Date: Thu, 25 Jul 2024 10:45:01 +0100 Subject: [PATCH 1/2] Added failing test for rooted path without volume on Windows xoofx/zio#92 --- src/Zio.Tests/FileSystems/TestPhysicalFileSystem.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Zio.Tests/FileSystems/TestPhysicalFileSystem.cs b/src/Zio.Tests/FileSystems/TestPhysicalFileSystem.cs index 069630c..0470b04 100644 --- a/src/Zio.Tests/FileSystems/TestPhysicalFileSystem.cs +++ b/src/Zio.Tests/FileSystems/TestPhysicalFileSystem.cs @@ -31,6 +31,19 @@ public void TestFileSystemInvalidDriveLetterOnWindows() } } + [SkippableFact] + public void TestRootedPathWithoutDriveOnWindows() + { + Skip.IfNot(IsWindows, "Testing Windows-specific behavior"); + + var driveLetter = Directory.GetCurrentDirectory()[0]; + var fs = new PhysicalFileSystem(); + + var resolvedPath = fs.ConvertPathFromInternal("/test"); + + Assert.Equal($"/mnt/{driveLetter}/test", resolvedPath.ToString(), StringComparer.OrdinalIgnoreCase); + } + [Fact] public void TestWatcher() { From 86b2af6ac79deaab6f446168697dafa061276312 Mon Sep 17 00:00:00 2001 From: James King Date: Thu, 25 Jul 2024 10:56:03 +0100 Subject: [PATCH 2/2] Fix regression with rooted paths without volume labels on Windows Also explain why we're not just always calling Path.GetFullPath xoofx/zio#92 --- src/Zio/FileSystems/PhysicalFileSystem.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Zio/FileSystems/PhysicalFileSystem.cs b/src/Zio/FileSystems/PhysicalFileSystem.cs index ffc659f..3705b06 100644 --- a/src/Zio/FileSystems/PhysicalFileSystem.cs +++ b/src/Zio/FileSystems/PhysicalFileSystem.cs @@ -977,9 +977,12 @@ protected override UPath ConvertPathFromInternalImpl(string innerPath) if (innerPath.StartsWith(@"\\", StringComparison.Ordinal) || innerPath.StartsWith(@"\?", StringComparison.Ordinal)) throw new NotSupportedException($"Path starting with `\\\\` or `\\?` are not supported -> `{innerPath}` "); - var absolutePath = Path.IsPathRooted(innerPath) ? innerPath : Path.GetFullPath(innerPath); - var driveIndex = absolutePath.IndexOf(":\\", StringComparison.Ordinal); - if (driveIndex != 1) + // We want to avoid using Path.GetFullPath unless absolutely necessary, + // because it can change the case of already rooted paths that contain a ~ + var absolutePath = HasWindowsVolumeLabel(innerPath) ? innerPath : Path.GetFullPath(innerPath); + + // Assert that Path.GetFullPath returned the format we expect + if (!HasWindowsVolumeLabel(absolutePath)) throw new ArgumentException($"Expecting a drive for the path `{absolutePath}`"); var builder = UPath.GetSharedStringBuilder(); @@ -1032,4 +1035,12 @@ private static bool IsDriveLetter(char c) { return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'; } + + private static bool HasWindowsVolumeLabel( string path ) + { + if ( !IsOnWindows ) + throw new NotSupportedException( $"{nameof( HasWindowsVolumeLabel )} is only supported on Windows platforms." ); + + return path.Length >= 3 && path[1] == ':' && path[2] is '\\' or '/'; + } } \ No newline at end of file