Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;

namespace System.IO.Abstractions.TestingHelpers;

Expand All @@ -15,6 +16,10 @@ public class PathVerifier
private static readonly char[] AdditionalInvalidPathChars = { '*', '?' };
private readonly IMockFileDataAccessor _mockFileDataAccessor;

// Windows supports extended-length paths with a `\\?\` prefix, to work around low path length limits.
// Ref: https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry
private const string WINDOWS_EXTENDED_LENGTH_PATH_PREFIX = @"\\?\";

/// <summary>
/// Creates a new verifier instance.
/// </summary>
Expand Down Expand Up @@ -64,6 +69,12 @@ public void IsLegalAbsoluteOrRelative(string path, string paramName)

private static bool IsValidUseOfVolumeSeparatorChar(string path)
{
if (XFS.IsWindowsPlatform() && path.StartsWith(WINDOWS_EXTENDED_LENGTH_PATH_PREFIX))
{
// Skip over the `\\?\` prefix if there is one.
path = path.Substring(WINDOWS_EXTENDED_LENGTH_PATH_PREFIX.Length);
}

var lastVolSepIndex = path.LastIndexOf(Path.VolumeSeparatorChar);
return lastVolSepIndex == -1 || lastVolSepIndex == 1 && char.IsLetter(path[0]);
}
Expand Down Expand Up @@ -97,6 +108,14 @@ public bool HasIllegalCharacters(string path, bool checkAdditional)

if (checkAdditional)
{
// AdditionalInvalidPathChars includes '?', but this character is allowed in extended-length
// windows path prefixes (`\\?\`). If we're dealing with such a path, check for invalid
// characters after the prefix.
if (XFS.IsWindowsPlatform() && path.StartsWith(WINDOWS_EXTENDED_LENGTH_PATH_PREFIX))
{
path = path.Substring(WINDOWS_EXTENDED_LENGTH_PATH_PREFIX.Length);
}

return path.IndexOfAny(invalidPathChars.Concat(AdditionalInvalidPathChars).ToArray()) >= 0;
}

Expand Down Expand Up @@ -164,4 +183,4 @@ public bool TryNormalizeDriveName(string name, out string result)
result = name;
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,25 @@ public async Task MockDirectory_CreateDirectory_ShouldThrowIfIllegalCharacterInP
await That(createDelegate).Throws<ArgumentException>();
}

[Test]
[WindowsOnly(WindowsSpecifics.UNCPaths)]
public async Task MockDirectory_CreateDirectory_ShouldSupportExtendedLengthPaths()
{
// Arrange
var fileSystem = new MockFileSystem();

// Act
var directoryInfo = fileSystem.Directory.CreateDirectory(XFS.Path(@"\\?\c:\bar"));
fileSystem.File.WriteAllText(@"\\?\c:\bar\grok.txt", "hello world\n");

// Assert
await That(fileSystem.Directory.Exists(XFS.Path(@"\\?\c:\bar"))).IsTrue();
await That(directoryInfo.FullName).IsEqualTo(@"\\?\c:\bar");
await That(fileSystem.File.ReadAllText(@"\\?\c:\bar\grok.txt")).IsEqualTo("hello world\n");
await That(fileSystem.Directory.GetFiles(@"\\?\c:\bar")).HasSingle()
.Which.IsEqualTo(@"\\?\c:\bar\grok.txt");
}

// Issue #210
[Test]
public async Task MockDirectory_CreateDirectory_ShouldIgnoreExistingDirectoryRegardlessOfTrailingSlash()
Expand Down Expand Up @@ -2219,4 +2238,4 @@ public static void MockDirectory_Move_ShouldNotThrowException_InWindows_When_Sou
// Act & Assert
Assert.DoesNotThrow(() => mockFs.Directory.Move(src, dest));
}
}
}