mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Update PathFormatter and PathNormalizer for 13.1.0
This commit is contained in:
parent
e1fd31c1ff
commit
61b29e57a3
@ -19,12 +19,14 @@ public struct PathFlags
|
|||||||
public void AllowEmptyPath() => _value |= 1 << 2;
|
public void AllowEmptyPath() => _value |= 1 << 2;
|
||||||
public void AllowMountName() => _value |= 1 << 3;
|
public void AllowMountName() => _value |= 1 << 3;
|
||||||
public void AllowBackslash() => _value |= 1 << 4;
|
public void AllowBackslash() => _value |= 1 << 4;
|
||||||
|
public void AllowAllCharacters() => _value |= 1 << 5;
|
||||||
|
|
||||||
public bool IsWindowsPathAllowed() => (_value & (1 << 0)) != 0;
|
public bool IsWindowsPathAllowed() => (_value & (1 << 0)) != 0;
|
||||||
public bool IsRelativePathAllowed() => (_value & (1 << 1)) != 0;
|
public bool IsRelativePathAllowed() => (_value & (1 << 1)) != 0;
|
||||||
public bool IsEmptyPathAllowed() => (_value & (1 << 2)) != 0;
|
public bool IsEmptyPathAllowed() => (_value & (1 << 2)) != 0;
|
||||||
public bool IsMountNameAllowed() => (_value & (1 << 3)) != 0;
|
public bool IsMountNameAllowed() => (_value & (1 << 3)) != 0;
|
||||||
public bool IsBackslashAllowed() => (_value & (1 << 4)) != 0;
|
public bool IsBackslashAllowed() => (_value & (1 << 4)) != 0;
|
||||||
|
public bool AreAllCharactersAllowed() => (_value & (1 << 5)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -13,9 +13,19 @@ namespace LibHac.Fs;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains functions for working with path formatting and normalization.
|
/// Contains functions for working with path formatting and normalization.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Based on FS 12.1.0 (nnSdk 12.3.1)</remarks>
|
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||||
public static class PathFormatter
|
public static class PathFormatter
|
||||||
{
|
{
|
||||||
|
private static ReadOnlySpan<byte> InvalidCharacter =>
|
||||||
|
new[] { (byte)':', (byte)'*', (byte)'?', (byte)'<', (byte)'>', (byte)'|' };
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> InvalidCharacterForHostName =>
|
||||||
|
new[] { (byte)':', (byte)'*', (byte)'<', (byte)'>', (byte)'|', (byte)'$' };
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> InvalidCharacterForMountName =>
|
||||||
|
new[] { (byte)'*', (byte)'?', (byte)'<', (byte)'>', (byte)'|' };
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static Result CheckHostName(ReadOnlySpan<byte> name)
|
private static Result CheckHostName(ReadOnlySpan<byte> name)
|
||||||
{
|
{
|
||||||
@ -24,8 +34,11 @@ public static class PathFormatter
|
|||||||
|
|
||||||
for (int i = 0; i < name.Length; i++)
|
for (int i = 0; i < name.Length; i++)
|
||||||
{
|
{
|
||||||
if (name[i] == ':' || name[i] == '$')
|
foreach (byte c in InvalidCharacterForHostName)
|
||||||
return ResultFs.InvalidPathFormat.Log();
|
{
|
||||||
|
if (name[i] == c)
|
||||||
|
return ResultFs.InvalidCharacter.Log();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
@ -41,8 +54,11 @@ public static class PathFormatter
|
|||||||
|
|
||||||
for (int i = 0; i < name.Length; i++)
|
for (int i = 0; i < name.Length; i++)
|
||||||
{
|
{
|
||||||
if (name[i] == ':')
|
foreach (byte c in InvalidCharacter)
|
||||||
return ResultFs.InvalidPathFormat.Log();
|
{
|
||||||
|
if (name[i] == c)
|
||||||
|
return ResultFs.InvalidCharacter.Log();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
@ -90,8 +106,11 @@ public static class PathFormatter
|
|||||||
|
|
||||||
for (int i = 0; i < mountLength; i++)
|
for (int i = 0; i < mountLength; i++)
|
||||||
{
|
{
|
||||||
if (path.At(i) is (byte)'*' or (byte)'?' or (byte)'<' or (byte)'>' or (byte)'|')
|
foreach (byte c in InvalidCharacterForMountName)
|
||||||
return ResultFs.InvalidCharacter.Log();
|
{
|
||||||
|
if (path.At(i) == c)
|
||||||
|
return ResultFs.InvalidCharacter.Log();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!outMountNameBuffer.IsEmpty)
|
if (!outMountNameBuffer.IsEmpty)
|
||||||
@ -150,6 +169,12 @@ public static class PathFormatter
|
|||||||
int winPathLength;
|
int winPathLength;
|
||||||
for (winPathLength = 2; currentPath.At(winPathLength) != NullTerminator; winPathLength++)
|
for (winPathLength = 2; currentPath.At(winPathLength) != NullTerminator; winPathLength++)
|
||||||
{
|
{
|
||||||
|
foreach (byte c in InvalidCharacter)
|
||||||
|
{
|
||||||
|
if (currentPath[winPathLength] == c)
|
||||||
|
return ResultFs.InvalidCharacter.Log();
|
||||||
|
}
|
||||||
|
|
||||||
if (currentPath[winPathLength] == DirectorySeparator ||
|
if (currentPath[winPathLength] == DirectorySeparator ||
|
||||||
currentPath[winPathLength] == AltDirectorySeparator)
|
currentPath[winPathLength] == AltDirectorySeparator)
|
||||||
{
|
{
|
||||||
@ -478,8 +503,11 @@ public static class PathFormatter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PathNormalizer.IsParentDirectoryPathReplacementNeeded(buffer))
|
if (flags.IsBackslashAllowed() && PathNormalizer.IsParentDirectoryPathReplacementNeeded(buffer))
|
||||||
return ResultFs.DirectoryUnobtainable.Log();
|
{
|
||||||
|
isNormalized = false;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
rc = PathUtility.CheckInvalidBackslash(out bool isBackslashContained, buffer,
|
rc = PathUtility.CheckInvalidBackslash(out bool isBackslashContained, buffer,
|
||||||
flags.IsWindowsPathAllowed() || flags.IsBackslashAllowed());
|
flags.IsWindowsPathAllowed() || flags.IsBackslashAllowed());
|
||||||
@ -491,7 +519,7 @@ public static class PathFormatter
|
|||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = PathNormalizer.IsNormalized(out isNormalized, out int length, buffer);
|
rc = PathNormalizer.IsNormalized(out isNormalized, out int length, buffer, flags.AreAllCharactersAllowed());
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
totalLength += length;
|
totalLength += length;
|
||||||
@ -612,7 +640,8 @@ public static class PathFormatter
|
|||||||
src = srcBufferSlashReplaced.AsSpan(srcOffset);
|
src = srcBufferSlashReplaced.AsSpan(srcOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = PathNormalizer.Normalize(outputBuffer.Slice(currentPos), out _, src, isWindowsPath, isDriveRelative);
|
rc = PathNormalizer.Normalize(outputBuffer.Slice(currentPos), out _, src, isWindowsPath, isDriveRelative,
|
||||||
|
flags.AreAllCharactersAllowed());
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
@ -10,7 +10,7 @@ namespace LibHac.Fs;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains functions for doing with basic path normalization.
|
/// Contains functions for doing with basic path normalization.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Based on FS 12.1.0 (nnSdk 12.3.1)</remarks>
|
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||||
public static class PathNormalizer
|
public static class PathNormalizer
|
||||||
{
|
{
|
||||||
private enum PathState
|
private enum PathState
|
||||||
@ -25,6 +25,12 @@ public static class PathNormalizer
|
|||||||
|
|
||||||
public static Result Normalize(Span<byte> outputBuffer, out int length, ReadOnlySpan<byte> path, bool isWindowsPath,
|
public static Result Normalize(Span<byte> outputBuffer, out int length, ReadOnlySpan<byte> path, bool isWindowsPath,
|
||||||
bool isDriveRelativePath)
|
bool isDriveRelativePath)
|
||||||
|
{
|
||||||
|
return Normalize(outputBuffer, out length, path, isWindowsPath, isDriveRelativePath, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result Normalize(Span<byte> outputBuffer, out int length, ReadOnlySpan<byte> path, bool isWindowsPath,
|
||||||
|
bool isDriveRelativePath, bool allowAllCharacters)
|
||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out length);
|
UnsafeHelpers.SkipParamInit(out length);
|
||||||
|
|
||||||
@ -32,7 +38,7 @@ public static class PathNormalizer
|
|||||||
int totalLength = 0;
|
int totalLength = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
if (!IsSeparator(path.At(0)))
|
if (path.At(0) != DirectorySeparator)
|
||||||
{
|
{
|
||||||
if (!isDriveRelativePath)
|
if (!isDriveRelativePath)
|
||||||
return ResultFs.InvalidPathFormat.Log();
|
return ResultFs.InvalidPathFormat.Log();
|
||||||
@ -43,6 +49,7 @@ public static class PathNormalizer
|
|||||||
var convertedPath = new RentedArray<byte>();
|
var convertedPath = new RentedArray<byte>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
Result rc;
|
||||||
// Check if parent directory path replacement is needed.
|
// Check if parent directory path replacement is needed.
|
||||||
if (IsParentDirectoryPathReplacementNeeded(currentPath))
|
if (IsParentDirectoryPathReplacementNeeded(currentPath))
|
||||||
{
|
{
|
||||||
@ -58,20 +65,22 @@ public static class PathNormalizer
|
|||||||
|
|
||||||
bool skipNextSeparator = false;
|
bool skipNextSeparator = false;
|
||||||
|
|
||||||
while (!IsNul(currentPath.At(i)))
|
while (currentPath.At(i) != NullTerminator)
|
||||||
{
|
{
|
||||||
if (IsSeparator(currentPath[i]))
|
if (currentPath[i] == DirectorySeparator)
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
} while (IsSeparator(currentPath.At(i)));
|
} while (currentPath.At(i) == DirectorySeparator);
|
||||||
|
|
||||||
if (IsNul(currentPath.At(i)))
|
if (currentPath.At(i) == NullTerminator)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (!skipNextSeparator)
|
if (!skipNextSeparator)
|
||||||
{
|
{
|
||||||
|
// Note: Nintendo returns TooLongPath in some cases where the output buffer is actually long
|
||||||
|
// enough to hold the normalized path. e.g. "/aa/bb/." with an output buffer length of 7
|
||||||
if (totalLength + 1 == outputBuffer.Length)
|
if (totalLength + 1 == outputBuffer.Length)
|
||||||
{
|
{
|
||||||
outputBuffer[totalLength] = NullTerminator;
|
outputBuffer[totalLength] = NullTerminator;
|
||||||
@ -87,8 +96,14 @@ public static class PathNormalizer
|
|||||||
}
|
}
|
||||||
|
|
||||||
int dirLen = 0;
|
int dirLen = 0;
|
||||||
while (!IsSeparator(currentPath.At(i + dirLen)) && !IsNul(currentPath.At(i + dirLen)))
|
while (currentPath.At(i + dirLen) != DirectorySeparator && currentPath.At(i + dirLen) != NullTerminator)
|
||||||
{
|
{
|
||||||
|
if (!allowAllCharacters)
|
||||||
|
{
|
||||||
|
rc = CheckInvalidCharacter(currentPath[i + dirLen]);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
}
|
||||||
|
|
||||||
dirLen++;
|
dirLen++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,12 +178,14 @@ public static class PathNormalizer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Note: This bug is in the original code. They probably meant to put "totalLength + 1"
|
// Note: This bug is in the original code. They probably meant to put "totalLength + 1"
|
||||||
if (totalLength - 1 > outputBuffer.Length)
|
// The buffer needs to be able to contain the total length of the normalized string plus
|
||||||
|
// one for the null terminator
|
||||||
|
if (outputBuffer.Length < totalLength - 1)
|
||||||
return ResultFs.TooLongPath.Log();
|
return ResultFs.TooLongPath.Log();
|
||||||
|
|
||||||
outputBuffer[totalLength] = NullTerminator;
|
outputBuffer[totalLength] = NullTerminator;
|
||||||
|
|
||||||
Result rc = IsNormalized(out bool isNormalized, out _, outputBuffer);
|
rc = IsNormalized(out bool isNormalized, out _, outputBuffer, allowAllCharacters);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
Assert.SdkAssert(isNormalized);
|
Assert.SdkAssert(isNormalized);
|
||||||
@ -200,6 +217,12 @@ public static class PathNormalizer
|
|||||||
/// <see cref="ResultFs.InvalidCharacter"/>: The path contains an invalid character.<br/>
|
/// <see cref="ResultFs.InvalidCharacter"/>: The path contains an invalid character.<br/>
|
||||||
/// <see cref="ResultFs.InvalidPathFormat"/>: The path is not in a valid format.</returns>
|
/// <see cref="ResultFs.InvalidPathFormat"/>: The path is not in a valid format.</returns>
|
||||||
public static Result IsNormalized(out bool isNormalized, out int length, ReadOnlySpan<byte> path)
|
public static Result IsNormalized(out bool isNormalized, out int length, ReadOnlySpan<byte> path)
|
||||||
|
{
|
||||||
|
return IsNormalized(out isNormalized, out length, path, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result IsNormalized(out bool isNormalized, out int length, ReadOnlySpan<byte> path,
|
||||||
|
bool allowAllCharacters)
|
||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out isNormalized, out length);
|
UnsafeHelpers.SkipParamInit(out isNormalized, out length);
|
||||||
|
|
||||||
@ -213,7 +236,7 @@ public static class PathNormalizer
|
|||||||
|
|
||||||
pathLength++;
|
pathLength++;
|
||||||
|
|
||||||
if (state != PathState.Initial)
|
if (!allowAllCharacters && state != PathState.Initial)
|
||||||
{
|
{
|
||||||
Result rc = CheckInvalidCharacter(c);
|
Result rc = CheckInvalidCharacter(c);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
@ -292,7 +315,6 @@ public static class PathNormalizer
|
|||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if a path begins with / or \ and contains any of these patterns:
|
/// Checks if a path begins with / or \ and contains any of these patterns:
|
||||||
/// "/..\", "\..\", "\../", "\..0" where '0' is the null terminator.
|
/// "/..\", "\..\", "\../", "\..0" where '0' is the null terminator.
|
||||||
|
@ -54,17 +54,18 @@ public class PathFormatterTests
|
|||||||
{ @"\\?\c:\", "", @"", ResultFs.InvalidCharacter.Value },
|
{ @"\\?\c:\", "", @"", ResultFs.InvalidCharacter.Value },
|
||||||
{ @"mount:\\host\share\aa\bb", "M", @"mount:", ResultFs.InvalidCharacter.Value },
|
{ @"mount:\\host\share\aa\bb", "M", @"mount:", ResultFs.InvalidCharacter.Value },
|
||||||
{ @"mount:\\host/share\aa\bb", "M", @"mount:", ResultFs.InvalidCharacter.Value },
|
{ @"mount:\\host/share\aa\bb", "M", @"mount:", ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"c:\aa\..\..\..\bb", "W", @"c:/bb", Result.Success },
|
||||||
{ @"mount:/\\aa\..\bb", "MW", @"mount:", ResultFs.InvalidPathFormat.Value },
|
{ @"mount:/\\aa\..\bb", "MW", @"mount:", ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"mount:/c:\aa\..\bb", "MW", @"mount:c:/bb", Result.Success },
|
{ @"mount:/c:\aa\..\bb", "MW", @"mount:c:/bb", Result.Success },
|
||||||
{ @"mount:/aa/bb", "MW", @"mount:/aa/bb", Result.Success },
|
{ @"mount:/aa/bb", "MW", @"mount:/aa/bb", Result.Success },
|
||||||
{ @"/mount:/aa/bb", "MW", @"/mount:/aa/bb", ResultFs.InvalidCharacter.Value },
|
{ @"/mount:/aa/bb", "MW", @"/", ResultFs.InvalidCharacter.Value },
|
||||||
{ @"/mount:/aa/bb", "W", @"/mount:/aa/bb", ResultFs.InvalidCharacter.Value },
|
{ @"/mount:/aa/bb", "W", @"/", ResultFs.InvalidCharacter.Value },
|
||||||
{ @"a:aa/../bb", "MW", @"a:aa/bb", Result.Success },
|
{ @"a:aa/../bb", "MW", @"a:aa/bb", Result.Success },
|
||||||
{ @"a:aa\..\bb", "MW", @"a:aa/bb", Result.Success },
|
{ @"a:aa\..\bb", "MW", @"a:aa/bb", Result.Success },
|
||||||
{ @"/a:aa\..\bb", "W", @"/bb", Result.Success },
|
{ @"/a:aa\..\bb", "W", @"/", ResultFs.InvalidCharacter.Value },
|
||||||
{ @"\\?\c:\.\aa", "W", @"\\?\c:/aa", Result.Success },
|
{ @"\\?\c:\.\aa", "W", @"\\?\c:/aa", Result.Success },
|
||||||
{ @"\\.\c:\.\aa", "W", @"\\.\c:/aa", Result.Success },
|
{ @"\\.\c:\.\aa", "W", @"\\.\c:/aa", Result.Success },
|
||||||
{ @"\\.\mount:\.\aa", "W", @"\\./mount:/aa", ResultFs.InvalidCharacter.Value },
|
{ @"\\.\mount:\.\aa", "W", @"\\./", ResultFs.InvalidCharacter.Value },
|
||||||
{ @"\\./.\aa", "W", @"\\./aa", Result.Success },
|
{ @"\\./.\aa", "W", @"\\./aa", Result.Success },
|
||||||
{ @"\\/aa", "W", @"", ResultFs.InvalidPathFormat.Value },
|
{ @"\\/aa", "W", @"", ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"\\\aa", "W", @"", ResultFs.InvalidPathFormat.Value },
|
{ @"\\\aa", "W", @"", ResultFs.InvalidPathFormat.Value },
|
||||||
@ -73,13 +74,13 @@ public class PathFormatterTests
|
|||||||
{ @"\\host\share\path", "W", @"\\host\share/path", Result.Success },
|
{ @"\\host\share\path", "W", @"\\host\share/path", Result.Success },
|
||||||
{ @"\\host\share\path\aa\bb\..\cc\.", "W", @"\\host\share/path/aa/cc", Result.Success },
|
{ @"\\host\share\path\aa\bb\..\cc\.", "W", @"\\host\share/path/aa/cc", Result.Success },
|
||||||
{ @"\\host\", "W", @"", ResultFs.InvalidPathFormat.Value },
|
{ @"\\host\", "W", @"", ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"\\ho$st\share\path", "W", @"", ResultFs.InvalidPathFormat.Value },
|
{ @"\\ho$st\share\path", "W", @"", ResultFs.InvalidCharacter.Value },
|
||||||
{ @"\\host:\share\path", "W", @"", ResultFs.InvalidPathFormat.Value },
|
{ @"\\host:\share\path", "W", @"", ResultFs.InvalidCharacter.Value },
|
||||||
{ @"\\..\share\path", "W", @"", ResultFs.InvalidPathFormat.Value },
|
{ @"\\..\share\path", "W", @"", ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"\\host\s:hare\path", "W", @"", ResultFs.InvalidPathFormat.Value },
|
{ @"\\host\s:hare\path", "W", @"", ResultFs.InvalidCharacter.Value },
|
||||||
{ @"\\host\.\path", "W", @"", ResultFs.InvalidPathFormat.Value },
|
{ @"\\host\.\path", "W", @"", ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"\\host\..\path", "W", @"", ResultFs.InvalidPathFormat.Value },
|
{ @"\\host\..\path", "W", @"", ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"\\host\sha:re", "W", @"", ResultFs.InvalidPathFormat.Value },
|
{ @"\\host\sha:re", "W", @"", ResultFs.InvalidCharacter.Value },
|
||||||
{ @".\\host\share", "RW", @"..\\host\share/", Result.Success }
|
{ @".\\host\share", "RW", @"..\\host\share/", Result.Success }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -128,14 +129,46 @@ public class PathFormatterTests
|
|||||||
NormalizeImpl(path, pathFlags, 0x301, expectedNormalized, expectedResult);
|
NormalizeImpl(path, pathFlags, 0x301, expectedNormalized, expectedResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static TheoryData<string, string, string, Result> TestData_Normalize_AllowAllChars => new()
|
||||||
|
{
|
||||||
|
{ @"/aa/b:b/cc", "", @"/aa/", ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"/aa/b*b/cc", "", @"/aa/", ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"/aa/b?b/cc", "", @"/aa/", ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"/aa/b<b/cc", "", @"/aa/", ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"/aa/b>b/cc", "", @"/aa/", ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"/aa/b|b/cc", "", @"/aa/", ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"/aa/b:b/cc", "C", @"/aa/b:b/cc", Result.Success },
|
||||||
|
{ @"/aa/b*b/cc", "C", @"/aa/b*b/cc", Result.Success },
|
||||||
|
{ @"/aa/b?b/cc", "C", @"/aa/b?b/cc", Result.Success },
|
||||||
|
{ @"/aa/b<b/cc", "C", @"/aa/b<b/cc", Result.Success },
|
||||||
|
{ @"/aa/b>b/cc", "C", @"/aa/b>b/cc", Result.Success },
|
||||||
|
{ @"/aa/b|b/cc", "C", @"/aa/b|b/cc", Result.Success },
|
||||||
|
{ @"/aa/b'b/cc", "", @"/aa/b'b/cc", Result.Success },
|
||||||
|
{ @"/aa/b""b/cc", "", @"/aa/b""b/cc", Result.Success },
|
||||||
|
{ @"/aa/b(b/cc", "", @"/aa/b(b/cc", Result.Success },
|
||||||
|
{ @"/aa/b)b/cc", "", @"/aa/b)b/cc", Result.Success },
|
||||||
|
{ @"/aa/b'b/cc", "C", @"/aa/b'b/cc", Result.Success },
|
||||||
|
{ @"/aa/b""b/cc", "C", @"/aa/b""b/cc", Result.Success },
|
||||||
|
{ @"/aa/b(b/cc", "C", @"/aa/b(b/cc", Result.Success },
|
||||||
|
{ @"/aa/b)b/cc", "C", @"/aa/b)b/cc", Result.Success },
|
||||||
|
{ @"mount:/aa/b<b/cc", "MC", @"mount:/aa/b<b/cc", Result.Success },
|
||||||
|
{ @"mo>unt:/aa/bb/cc", "MC", @"", ResultFs.InvalidCharacter.Value }
|
||||||
|
};
|
||||||
|
|
||||||
|
[Theory, MemberData(nameof(TestData_Normalize_AllowAllChars))]
|
||||||
|
public static void Normalize_AllowAllChars(string path, string pathFlags, string expectedNormalized, Result expectedResult)
|
||||||
|
{
|
||||||
|
NormalizeImpl(path, pathFlags, 0x301, expectedNormalized, expectedResult);
|
||||||
|
}
|
||||||
|
|
||||||
public static TheoryData<string, string, string, Result> TestData_Normalize_All => new()
|
public static TheoryData<string, string, string, Result> TestData_Normalize_All => new()
|
||||||
{
|
{
|
||||||
{ @"mount:./aa/bb", "WRM", @"mount:./aa/bb", Result.Success },
|
{ @"mount:./aa/bb", "WRM", @"mount:./aa/bb", Result.Success },
|
||||||
{ @"mount:./aa/bb\cc/dd", "WRM", @"mount:./aa/bb/cc/dd", Result.Success },
|
{ @"mount:./aa/bb\cc/dd", "WRM", @"mount:./aa/bb/cc/dd", Result.Success },
|
||||||
{ @"mount:./aa/bb\cc/dd", "WRMB", @"mount:./aa/bb/cc/dd", Result.Success },
|
{ @"mount:./aa/bb\cc/dd", "WRMB", @"mount:./aa/bb/cc/dd", Result.Success },
|
||||||
{ @"mount:./.c:/aa/bb", "RM", @"mount:./.c:/aa/bb", ResultFs.InvalidCharacter.Value },
|
{ @"mount:./.c:/aa/bb", "RM", @"mount:./", ResultFs.InvalidCharacter.Value },
|
||||||
{ @"mount:.c:/aa/bb", "WRM", @"mount:./.c:/aa/bb", ResultFs.InvalidCharacter.Value },
|
{ @"mount:.c:/aa/bb", "WRM", @"mount:./", ResultFs.InvalidCharacter.Value },
|
||||||
{ @"mount:./cc:/aa/bb", "WRM", @"mount:./cc:/aa/bb", ResultFs.InvalidCharacter.Value },
|
{ @"mount:./cc:/aa/bb", "WRM", @"mount:./", ResultFs.InvalidCharacter.Value },
|
||||||
{ @"mount:./\\host\share/aa/bb", "MW", @"mount:", ResultFs.InvalidPathFormat.Value },
|
{ @"mount:./\\host\share/aa/bb", "MW", @"mount:", ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"mount:./\\host\share/aa/bb", "WRM", @"mount:.\\host\share/aa/bb", Result.Success },
|
{ @"mount:./\\host\share/aa/bb", "WRM", @"mount:.\\host\share/aa/bb", Result.Success },
|
||||||
{ @"mount:.\\host\share/aa/bb", "WRM", @"mount:..\\host\share/aa/bb", Result.Success },
|
{ @"mount:.\\host\share/aa/bb", "WRM", @"mount:..\\host\share/aa/bb", Result.Success },
|
||||||
@ -147,7 +180,8 @@ public class PathFormatterTests
|
|||||||
{ @"mount:/aa\bb", "BM", @"mount:/aa\bb", Result.Success },
|
{ @"mount:/aa\bb", "BM", @"mount:/aa\bb", Result.Success },
|
||||||
{ @".//aa/bb", "RW", @"./aa/bb", Result.Success },
|
{ @".//aa/bb", "RW", @"./aa/bb", Result.Success },
|
||||||
{ @"./aa/bb", "R", @"./aa/bb", Result.Success },
|
{ @"./aa/bb", "R", @"./aa/bb", Result.Success },
|
||||||
{ @"./c:/aa/bb", "RW", @"./c:/aa/bb", ResultFs.InvalidCharacter.Value }
|
{ @"./c:/aa/bb", "RW", @"./", ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"mount:./aa/b:b\cc/dd", "WRMBC", @"mount:./aa/b:b/cc/dd", Result.Success }
|
||||||
};
|
};
|
||||||
|
|
||||||
[Theory, MemberData(nameof(TestData_Normalize_All))]
|
[Theory, MemberData(nameof(TestData_Normalize_All))]
|
||||||
@ -219,7 +253,6 @@ public class PathFormatterTests
|
|||||||
|
|
||||||
public static TheoryData<string, string, bool, int, Result> TestData_IsNormalized_WindowsPath => new()
|
public static TheoryData<string, string, bool, int, Result> TestData_IsNormalized_WindowsPath => new()
|
||||||
{
|
{
|
||||||
{ @"c:/aa/bb", "", false, 0, ResultFs.InvalidPathFormat.Value },
|
|
||||||
{ @"c:/aa/bb", "", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"c:/aa/bb", "", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"c:\aa\bb", "", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"c:\aa\bb", "", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"\\host\share", "", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"\\host\share", "", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
@ -228,6 +261,7 @@ public class PathFormatterTests
|
|||||||
{ @"\\?\c:\", "", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"\\?\c:\", "", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"mount:\\host\share\aa\bb", "M", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"mount:\\host\share\aa\bb", "M", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"mount:\\host/share\aa\bb", "M", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"mount:\\host/share\aa\bb", "M", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
|
{ @"c:\aa\..\..\..\bb", "W", false, 0, Result.Success },
|
||||||
{ @"mount:/\\aa\..\bb", "MW", false, 0, Result.Success },
|
{ @"mount:/\\aa\..\bb", "MW", false, 0, Result.Success },
|
||||||
{ @"mount:/c:\aa\..\bb", "MW", false, 0, Result.Success },
|
{ @"mount:/c:\aa\..\bb", "MW", false, 0, Result.Success },
|
||||||
{ @"mount:/aa/bb", "MW", true, 12, Result.Success },
|
{ @"mount:/aa/bb", "MW", true, 12, Result.Success },
|
||||||
@ -235,7 +269,7 @@ public class PathFormatterTests
|
|||||||
{ @"/mount:/aa/bb", "W", false, 0, ResultFs.InvalidCharacter.Value },
|
{ @"/mount:/aa/bb", "W", false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
{ @"a:aa/../bb", "MW", false, 8, Result.Success },
|
{ @"a:aa/../bb", "MW", false, 8, Result.Success },
|
||||||
{ @"a:aa\..\bb", "MW", false, 0, Result.Success },
|
{ @"a:aa\..\bb", "MW", false, 0, Result.Success },
|
||||||
{ @"/a:aa\..\bb", "W", false, 0, ResultFs.DirectoryUnobtainable.Value },
|
{ @"/a:aa\..\bb", "W", false, 0, Result.Success },
|
||||||
{ @"\\?\c:\.\aa", "W", false, 0, Result.Success },
|
{ @"\\?\c:\.\aa", "W", false, 0, Result.Success },
|
||||||
{ @"\\.\c:\.\aa", "W", false, 0, Result.Success },
|
{ @"\\.\c:\.\aa", "W", false, 0, Result.Success },
|
||||||
{ @"\\.\mount:\.\aa", "W", false, 0, Result.Success },
|
{ @"\\.\mount:\.\aa", "W", false, 0, Result.Success },
|
||||||
@ -247,13 +281,13 @@ public class PathFormatterTests
|
|||||||
{ @"\\host\share\path", "W", false, 0, Result.Success },
|
{ @"\\host\share\path", "W", false, 0, Result.Success },
|
||||||
{ @"\\host\share\path\aa\bb\..\cc\.", "W", false, 0, Result.Success },
|
{ @"\\host\share\path\aa\bb\..\cc\.", "W", false, 0, Result.Success },
|
||||||
{ @"\\host\", "W", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"\\host\", "W", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"\\ho$st\share\path", "W", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"\\ho$st\share\path", "W", false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
{ @"\\host:\share\path", "W", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"\\host:\share\path", "W", false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
{ @"\\..\share\path", "W", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"\\..\share\path", "W", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"\\host\s:hare\path", "W", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"\\host\s:hare\path", "W", false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
{ @"\\host\.\path", "W", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"\\host\.\path", "W", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"\\host\..\path", "W", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"\\host\..\path", "W", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"\\host\sha:re", "W", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"\\host\sha:re", "W", false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
{ @".\\host\share", "RW", false, 0, Result.Success }
|
{ @".\\host\share", "RW", false, 0, Result.Success }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -288,14 +322,14 @@ public class PathFormatterTests
|
|||||||
{
|
{
|
||||||
{ @"\aa\bb\..\cc", "", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"\aa\bb\..\cc", "", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"\aa\bb\..\cc", "B", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"\aa\bb\..\cc", "B", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"/aa\bb\..\cc", "", false, 0, ResultFs.DirectoryUnobtainable.Value },
|
{ @"/aa\bb\..\cc", "", false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
{ @"/aa\bb\..\cc", "B", false, 0, ResultFs.DirectoryUnobtainable.Value },
|
{ @"/aa\bb\..\cc", "B", false, 0, Result.Success },
|
||||||
{ @"/aa\bb\cc", "", false, 0, ResultFs.InvalidCharacter.Value },
|
{ @"/aa\bb\cc", "", false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
{ @"/aa\bb\cc", "B", true, 9, Result.Success },
|
{ @"/aa\bb\cc", "B", true, 9, Result.Success },
|
||||||
{ @"\\host\share\path\aa\bb\cc", "W", false, 0, Result.Success },
|
{ @"\\host\share\path\aa\bb\cc", "W", false, 0, Result.Success },
|
||||||
{ @"\\host\share\path\aa\bb\cc", "WB", false, 0, Result.Success },
|
{ @"\\host\share\path\aa\bb\cc", "WB", false, 0, Result.Success },
|
||||||
{ @"/aa/bb\../cc/..\dd\..\ee/..", "", false, 0, ResultFs.DirectoryUnobtainable.Value },
|
{ @"/aa/bb\../cc/..\dd\..\ee/..", "", false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
{ @"/aa/bb\../cc/..\dd\..\ee/..", "B", false, 0, ResultFs.DirectoryUnobtainable.Value }
|
{ @"/aa/bb\../cc/..\dd\..\ee/..", "B", false, 0, Result.Success }
|
||||||
};
|
};
|
||||||
|
|
||||||
[Theory, MemberData(nameof(TestData_IsNormalized_Backslash))]
|
[Theory, MemberData(nameof(TestData_IsNormalized_Backslash))]
|
||||||
@ -305,6 +339,39 @@ public class PathFormatterTests
|
|||||||
IsNormalizedImpl(path, pathFlags, expectedIsNormalized, expectedLength, expectedResult);
|
IsNormalizedImpl(path, pathFlags, expectedIsNormalized, expectedLength, expectedResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static TheoryData<string, string, bool, int, Result> TestData_IsNormalized_AllowAllChars => new()
|
||||||
|
{
|
||||||
|
{ @"/aa/b:b/cc", "", false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"/aa/b*b/cc", "", false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"/aa/b?b/cc", "", false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"/aa/b<b/cc", "", false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"/aa/b>b/cc", "", false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"/aa/b|b/cc", "", false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"/aa/b:b/cc", "C", true, 10, Result.Success },
|
||||||
|
{ @"/aa/b*b/cc", "C", true, 10, Result.Success },
|
||||||
|
{ @"/aa/b?b/cc", "C", true, 10, Result.Success },
|
||||||
|
{ @"/aa/b<b/cc", "C", true, 10, Result.Success },
|
||||||
|
{ @"/aa/b>b/cc", "C", true, 10, Result.Success },
|
||||||
|
{ @"/aa/b|b/cc", "C", true, 10, Result.Success },
|
||||||
|
{ @"/aa/b'b/cc", "", true, 10, Result.Success },
|
||||||
|
{ @"/aa/b""b/cc", "", true, 10, Result.Success },
|
||||||
|
{ @"/aa/b(b/cc", "", true, 10, Result.Success },
|
||||||
|
{ @"/aa/b)b/cc", "", true, 10, Result.Success },
|
||||||
|
{ @"/aa/b'b/cc", "C", true, 10, Result.Success },
|
||||||
|
{ @"/aa/b""b/cc", "C", true, 10, Result.Success },
|
||||||
|
{ @"/aa/b(b/cc", "C", true, 10, Result.Success },
|
||||||
|
{ @"/aa/b)b/cc", "C", true, 10, Result.Success },
|
||||||
|
{ @"mount:/aa/b<b/cc", "MC", true, 16, Result.Success },
|
||||||
|
{ @"mo>unt:/aa/bb/cc", "MC", false, 0, ResultFs.InvalidCharacter.Value }
|
||||||
|
};
|
||||||
|
|
||||||
|
[Theory, MemberData(nameof(TestData_IsNormalized_AllowAllChars))]
|
||||||
|
public static void IsNormalized_AllowAllChars(string path, string pathFlags, bool expectedIsNormalized, long expectedLength,
|
||||||
|
Result expectedResult)
|
||||||
|
{
|
||||||
|
IsNormalizedImpl(path, pathFlags, expectedIsNormalized, expectedLength, expectedResult);
|
||||||
|
}
|
||||||
|
|
||||||
public static TheoryData<string, string, bool, int, Result> TestData_IsNormalized_All => new()
|
public static TheoryData<string, string, bool, int, Result> TestData_IsNormalized_All => new()
|
||||||
{
|
{
|
||||||
{ @"mount:./aa/bb", "WRM", true, 13, Result.Success },
|
{ @"mount:./aa/bb", "WRM", true, 13, Result.Success },
|
||||||
@ -324,7 +391,8 @@ public class PathFormatterTests
|
|||||||
{ @"mount:/aa\bb", "BM", true, 12, Result.Success },
|
{ @"mount:/aa\bb", "BM", true, 12, Result.Success },
|
||||||
{ @".//aa/bb", "RW", false, 1, Result.Success },
|
{ @".//aa/bb", "RW", false, 1, Result.Success },
|
||||||
{ @"./aa/bb", "R", true, 7, Result.Success },
|
{ @"./aa/bb", "R", true, 7, Result.Success },
|
||||||
{ @"./c:/aa/bb", "RW", false, 0, ResultFs.InvalidCharacter.Value }
|
{ @"./c:/aa/bb", "RW", false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"mount:./aa/b:b\cc/dd", "WRMBC", true, 20, Result.Success }
|
||||||
};
|
};
|
||||||
|
|
||||||
[Theory, MemberData(nameof(TestData_IsNormalized_All))]
|
[Theory, MemberData(nameof(TestData_IsNormalized_All))]
|
||||||
@ -386,9 +454,14 @@ public class PathFormatterTests
|
|||||||
case 'W':
|
case 'W':
|
||||||
flags.AllowWindowsPath();
|
flags.AllowWindowsPath();
|
||||||
break;
|
break;
|
||||||
|
case 'C':
|
||||||
|
flags.AllowAllCharacters();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,7 +14,7 @@ namespace nn::fs::detail {
|
|||||||
bool IsEnabledAccessLog();
|
bool IsEnabledAccessLog();
|
||||||
}
|
}
|
||||||
|
|
||||||
// SDK 12
|
// SDK 13
|
||||||
namespace nn::fs {
|
namespace nn::fs {
|
||||||
bool IsSubPath(const char* path1, const char* path2);
|
bool IsSubPath(const char* path1, const char* path2);
|
||||||
|
|
||||||
@ -29,12 +29,32 @@ namespace nn::fs {
|
|||||||
void AllowEmptyPath() { value |= (1 << 2); }
|
void AllowEmptyPath() { value |= (1 << 2); }
|
||||||
void AllowMountName() { value |= (1 << 3); }
|
void AllowMountName() { value |= (1 << 3); }
|
||||||
void AllowBackslash() { value |= (1 << 4); }
|
void AllowBackslash() { value |= (1 << 4); }
|
||||||
|
void AllowAllCharacters() { value |= (1 << 5); }
|
||||||
|
|
||||||
const bool IsWindowsPathAllowed() { return (value & (1 << 0)) != 0; }
|
bool IsWindowsPathAllowed()const { return (value & (1 << 0)) != 0; }
|
||||||
const bool IsRelativePathAllowed() { return (value & (1 << 1)) != 0; }
|
bool IsRelativePathAllowed()const { return (value & (1 << 1)) != 0; }
|
||||||
const bool IsEmptyPathAllowed() { return (value & (1 << 2)) != 0; }
|
bool IsEmptyPathAllowed()const { return (value & (1 << 2)) != 0; }
|
||||||
const bool IsMountNameAllowed() { return (value & (1 << 3)) != 0; }
|
bool IsMountNameAllowed()const { return (value & (1 << 3)) != 0; }
|
||||||
const bool IsBackslashAllowed() { return (value & (1 << 4)) != 0; }
|
bool IsBackslashAllowed()const { return (value & (1 << 4)) != 0; }
|
||||||
|
bool AreAllCharactersAllowed()const { return (value & (1 << 5)) != 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Path {
|
||||||
|
public:
|
||||||
|
char* m_String;
|
||||||
|
char* m_WriteBuffer;
|
||||||
|
uint64_t m_UniquePtrLength;
|
||||||
|
uint64_t m_WriteBufferLength;
|
||||||
|
bool m_IsNormalized;
|
||||||
|
|
||||||
|
Path();
|
||||||
|
nn::Result Initialize(char const* path);
|
||||||
|
nn::Result InitializeWithNormalization(char const* path);
|
||||||
|
nn::Result InitializeWithReplaceUnc(char const* path);
|
||||||
|
nn::Result Initialize(char const* path, uint64_t pathLength);
|
||||||
|
nn::Result InsertParent(char const* path);
|
||||||
|
nn::Result RemoveChild();
|
||||||
|
nn::Result Normalize(const nn::fs::PathFlags&);
|
||||||
};
|
};
|
||||||
|
|
||||||
class PathFormatter {
|
class PathFormatter {
|
||||||
@ -48,7 +68,9 @@ namespace nn::fs {
|
|||||||
class PathNormalizer {
|
class PathNormalizer {
|
||||||
public:
|
public:
|
||||||
static nn::Result Normalize(char* outBuffer, uint64_t* outLength, const char* path, uint64_t outBufferLength, bool isWindowsPath, bool isDriveRelative);
|
static nn::Result Normalize(char* outBuffer, uint64_t* outLength, const char* path, uint64_t outBufferLength, bool isWindowsPath, bool isDriveRelative);
|
||||||
|
static nn::Result Normalize(char* outBuffer, uint64_t* outLength, const char* path, uint64_t outBufferLength, bool isWindowsPath, bool isDriveRelative, bool allowAllCharacters);
|
||||||
static nn::Result IsNormalized(bool* outIsNormalized, uint64_t* outNormalizedPathLength, const char* path);
|
static nn::Result IsNormalized(bool* outIsNormalized, uint64_t* outNormalizedPathLength, const char* path);
|
||||||
|
static nn::Result IsNormalized(bool* outIsNormalized, uint64_t* outNormalizedPathLength, const char* path, bool allowAllCharacters);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,6 +97,7 @@ void CreateTest(const char* name, void (*func)(Ts...), const std::array<std::tup
|
|||||||
const char* GetResultName(nn::Result result) {
|
const char* GetResultName(nn::Result result) {
|
||||||
switch (result.GetValue()) {
|
switch (result.GetValue()) {
|
||||||
case 0: return "Result.Success";
|
case 0: return "Result.Success";
|
||||||
|
case 0x177202: return "ResultFs.NotImplemented.Value";
|
||||||
case 0x2EE402: return "ResultFs.InvalidPath.Value";
|
case 0x2EE402: return "ResultFs.InvalidPath.Value";
|
||||||
case 0x2EE602: return "ResultFs.TooLongPath.Value";
|
case 0x2EE602: return "ResultFs.TooLongPath.Value";
|
||||||
case 0x2EE802: return "ResultFs.InvalidCharacter.Value";
|
case 0x2EE802: return "ResultFs.InvalidCharacter.Value";
|
||||||
@ -111,12 +134,33 @@ nn::fs::PathFlags GetPathFlags(char const* pathFlags) {
|
|||||||
case 'W':
|
case 'W':
|
||||||
flags.AllowWindowsPath();
|
flags.AllowWindowsPath();
|
||||||
break;
|
break;
|
||||||
|
case 'C':
|
||||||
|
flags.AllowAllCharacters();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Escape single double-quotes to double double-quotes for C# verbatim string literals
|
||||||
|
void EscapeQuotes(char* dst, char const* src) {
|
||||||
|
while (*src) {
|
||||||
|
if (*src == '"')
|
||||||
|
*dst++ = '"';
|
||||||
|
|
||||||
|
*dst++ = *src++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetEscaped(const char* s) {
|
||||||
|
char escaped[0x200] = { 0 };
|
||||||
|
EscapeQuotes(escaped, s);
|
||||||
|
return std::string(escaped);
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr const auto TestData_PathFormatterNormalize_EmptyPath = make_array(
|
static constexpr const auto TestData_PathFormatterNormalize_EmptyPath = make_array(
|
||||||
// Check AllowEmptyPath option
|
// Check AllowEmptyPath option
|
||||||
std::make_tuple("", ""),
|
std::make_tuple("", ""),
|
||||||
@ -150,6 +194,7 @@ static constexpr const auto TestData_PathFormatterNormalize_WindowsPath = make_a
|
|||||||
std::make_tuple(R"(mount:\\host\share\aa\bb)", "M"), // Catch instances where the Windows path comes after other parts in the path
|
std::make_tuple(R"(mount:\\host\share\aa\bb)", "M"), // Catch instances where the Windows path comes after other parts in the path
|
||||||
std::make_tuple(R"(mount:\\host/share\aa\bb)", "M"), // And do it again with the UNC path not normalized
|
std::make_tuple(R"(mount:\\host/share\aa\bb)", "M"), // And do it again with the UNC path not normalized
|
||||||
|
|
||||||
|
std::make_tuple(R"(c:\aa\..\..\..\bb)", "W"), // Windows paths won't error when trying to navigate to the parent of the root directory
|
||||||
std::make_tuple(R"(mount:/\\aa\..\bb)", "MW"),
|
std::make_tuple(R"(mount:/\\aa\..\bb)", "MW"),
|
||||||
std::make_tuple(R"(mount:/c:\aa\..\bb)", "MW"),
|
std::make_tuple(R"(mount:/c:\aa\..\bb)", "MW"),
|
||||||
std::make_tuple(R"(mount:/aa/bb)", "MW"),
|
std::make_tuple(R"(mount:/aa/bb)", "MW"),
|
||||||
@ -171,9 +216,9 @@ static constexpr const auto TestData_PathFormatterNormalize_WindowsPath = make_a
|
|||||||
std::make_tuple(R"(\\host\)", "W"), // Share name cannot be empty
|
std::make_tuple(R"(\\host\)", "W"), // Share name cannot be empty
|
||||||
std::make_tuple(R"(\\ho$st\share\path)", "W"), // Invalid character '$' in host name
|
std::make_tuple(R"(\\ho$st\share\path)", "W"), // Invalid character '$' in host name
|
||||||
std::make_tuple(R"(\\host:\share\path)", "W"), // Invalid character ':' in host name
|
std::make_tuple(R"(\\host:\share\path)", "W"), // Invalid character ':' in host name
|
||||||
std::make_tuple(R"(\\..\share\path)", "W"), // Host name can't be ".."
|
std::make_tuple(R"(\\..\share\path)", "W"), // Host name can't be ".."
|
||||||
std::make_tuple(R"(\\host\s:hare\path)", "W"), // Invalid character ':' in host name
|
std::make_tuple(R"(\\host\s:hare\path)", "W"), // Invalid character ':' in host name
|
||||||
std::make_tuple(R"(\\host\.\path)", "W"), // Share name can't be "."
|
std::make_tuple(R"(\\host\.\path)", "W"), // Share name can't be "."
|
||||||
std::make_tuple(R"(\\host\..\path)", "W"), // Share name can't be ".."
|
std::make_tuple(R"(\\host\..\path)", "W"), // Share name can't be ".."
|
||||||
std::make_tuple(R"(\\host\sha:re)", "W"), // Invalid share name when nothing follows it
|
std::make_tuple(R"(\\host\sha:re)", "W"), // Invalid share name when nothing follows it
|
||||||
std::make_tuple(R"(.\\host\share)", "RW") // Can't have a relative Windows path
|
std::make_tuple(R"(.\\host\share)", "RW") // Can't have a relative Windows path
|
||||||
@ -204,6 +249,33 @@ static constexpr const auto TestData_PathFormatterNormalize_Backslash = make_arr
|
|||||||
std::make_tuple(R"(/aa/bb\../cc/..\dd\..\ee/..)", "B")
|
std::make_tuple(R"(/aa/bb\../cc/..\dd\..\ee/..)", "B")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static constexpr const auto TestData_PathFormatterNormalize_AllowAllChars = make_array(
|
||||||
|
std::make_tuple(R"(/aa/b:b/cc)", ""), // Test each of the characters that normally aren't allowed
|
||||||
|
std::make_tuple(R"(/aa/b*b/cc)", ""),
|
||||||
|
std::make_tuple(R"(/aa/b?b/cc)", ""),
|
||||||
|
std::make_tuple(R"(/aa/b<b/cc)", ""),
|
||||||
|
std::make_tuple(R"(/aa/b>b/cc)", ""),
|
||||||
|
std::make_tuple(R"(/aa/b|b/cc)", ""),
|
||||||
|
std::make_tuple(R"(/aa/b:b/cc)", "C"),
|
||||||
|
std::make_tuple(R"(/aa/b*b/cc)", "C"),
|
||||||
|
std::make_tuple(R"(/aa/b?b/cc)", "C"),
|
||||||
|
std::make_tuple(R"(/aa/b<b/cc)", "C"),
|
||||||
|
std::make_tuple(R"(/aa/b>b/cc)", "C"),
|
||||||
|
std::make_tuple(R"(/aa/b|b/cc)", "C"),
|
||||||
|
|
||||||
|
std::make_tuple(R"(/aa/b'b/cc)", ""), // Test some symbols that are normally allowed
|
||||||
|
std::make_tuple(R"(/aa/b"b/cc)", ""),
|
||||||
|
std::make_tuple(R"(/aa/b(b/cc)", ""),
|
||||||
|
std::make_tuple(R"(/aa/b)b/cc)", ""),
|
||||||
|
std::make_tuple(R"(/aa/b'b/cc)", "C"),
|
||||||
|
std::make_tuple(R"(/aa/b"b/cc)", "C"),
|
||||||
|
std::make_tuple(R"(/aa/b(b/cc)", "C"),
|
||||||
|
std::make_tuple(R"(/aa/b)b/cc)", "C"),
|
||||||
|
|
||||||
|
std::make_tuple(R"(mount:/aa/b<b/cc)", "MC"),
|
||||||
|
std::make_tuple(R"(mo>unt:/aa/bb/cc)", "MC") // Invalid character in mount name
|
||||||
|
);
|
||||||
|
|
||||||
static constexpr const auto TestData_PathFormatterNormalize_All = make_array(
|
static constexpr const auto TestData_PathFormatterNormalize_All = make_array(
|
||||||
std::make_tuple(R"(mount:./aa/bb)", "WRM"), // Normalized path with both mount name and relative path
|
std::make_tuple(R"(mount:./aa/bb)", "WRM"), // Normalized path with both mount name and relative path
|
||||||
std::make_tuple(R"(mount:./aa/bb\cc/dd)", "WRM"), // Path with backslashes
|
std::make_tuple(R"(mount:./aa/bb\cc/dd)", "WRM"), // Path with backslashes
|
||||||
@ -215,14 +287,15 @@ static constexpr const auto TestData_PathFormatterNormalize_All = make_array(
|
|||||||
std::make_tuple(R"(mount:./\\host\share/aa/bb)", "WRM"), // These next 3 form a chain where if you normalize one it'll turn into the next
|
std::make_tuple(R"(mount:./\\host\share/aa/bb)", "WRM"), // These next 3 form a chain where if you normalize one it'll turn into the next
|
||||||
std::make_tuple(R"(mount:.\\host\share/aa/bb)", "WRM"),
|
std::make_tuple(R"(mount:.\\host\share/aa/bb)", "WRM"),
|
||||||
std::make_tuple(R"(mount:..\\host\share/aa/bb)", "WRM"),
|
std::make_tuple(R"(mount:..\\host\share/aa/bb)", "WRM"),
|
||||||
std::make_tuple(R"(.\\host\share/aa/bb)", "WRM"), // These next 2 form a chain where if you normalize one it'll turn into the next
|
std::make_tuple(R"(.\\host\share/aa/bb)", "WRM"), // These next 2 form a chain where if you normalize one it'll turn into the next
|
||||||
std::make_tuple(R"(..\\host\share/aa/bb)", "WRM"),
|
std::make_tuple(R"(..\\host\share/aa/bb)", "WRM"),
|
||||||
std::make_tuple(R"(mount:\\host\share/aa/bb)", "MW"), // Use a mount name and windows path together
|
std::make_tuple(R"(mount:\\host\share/aa/bb)", "MW"), // Use a mount name and windows path together
|
||||||
std::make_tuple(R"(mount:\aa\bb)", "BM"), // Backslashes are never allowed directly after a mount name even with AllowBackslashes
|
std::make_tuple(R"(mount:\aa\bb)", "BM"), // Backslashes are never allowed directly after a mount name even with AllowBackslashes
|
||||||
std::make_tuple(R"(mount:/aa\bb)", "BM"),
|
std::make_tuple(R"(mount:/aa\bb)", "BM"),
|
||||||
std::make_tuple(R"(.//aa/bb)", "RW"), // Relative path followed by a Windows path won't work
|
std::make_tuple(R"(.//aa/bb)", "RW"), // Relative path followed by a Windows path won't work
|
||||||
std::make_tuple(R"(./aa/bb)", "R"),
|
std::make_tuple(R"(./aa/bb)", "R"),
|
||||||
std::make_tuple(R"(./c:/aa/bb)", "RW")
|
std::make_tuple(R"(./c:/aa/bb)", "RW"),
|
||||||
|
std::make_tuple(R"(mount:./aa/b:b\cc/dd)", "WRMBC") // This path is considered normalized but the backslashes still normalize to forward slashes
|
||||||
);
|
);
|
||||||
|
|
||||||
void CreateTest_PathFormatterNormalize(char const* path, char const* pathFlags) {
|
void CreateTest_PathFormatterNormalize(char const* path, char const* pathFlags) {
|
||||||
@ -232,7 +305,7 @@ void CreateTest_PathFormatterNormalize(char const* path, char const* pathFlags)
|
|||||||
nn::Result result = nn::fs::PathFormatter::Normalize(normalized, 0x200, path, 0x200, flags);
|
nn::Result result = nn::fs::PathFormatter::Normalize(normalized, 0x200, path, 0x200, flags);
|
||||||
|
|
||||||
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", \"%s\", @\"%s\", %s},\n",
|
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", \"%s\", @\"%s\", %s},\n",
|
||||||
path, pathFlags, normalized, GetResultName(result));
|
GetEscaped(path).c_str(), pathFlags, GetEscaped(normalized).c_str(), GetResultName(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateTest_PathFormatterIsNormalized(char const* path, char const* pathFlags) {
|
void CreateTest_PathFormatterIsNormalized(char const* path, char const* pathFlags) {
|
||||||
@ -243,7 +316,7 @@ void CreateTest_PathFormatterIsNormalized(char const* path, char const* pathFlag
|
|||||||
nn::Result result = nn::fs::PathFormatter::IsNormalized(&isNormalized, &normalizedLength, path, flags);
|
nn::Result result = nn::fs::PathFormatter::IsNormalized(&isNormalized, &normalizedLength, path, flags);
|
||||||
|
|
||||||
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", \"%s\", %s, %ld, %s},\n",
|
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", \"%s\", %s, %ld, %s},\n",
|
||||||
path, pathFlags, BoolStr(isNormalized), normalizedLength, GetResultName(result));
|
GetEscaped(path).c_str(), pathFlags, BoolStr(isNormalized), normalizedLength, GetResultName(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr const auto TestData_PathFormatterNormalize_SmallBuffer = make_array(
|
static constexpr const auto TestData_PathFormatterNormalize_SmallBuffer = make_array(
|
||||||
@ -259,68 +332,72 @@ void CreateTest_PathFormatterNormalize_SmallBuffer(char const* path, char const*
|
|||||||
char normalized[0x200] = { 0 };
|
char normalized[0x200] = { 0 };
|
||||||
nn::fs::PathFlags flags = GetPathFlags(pathFlags);
|
nn::fs::PathFlags flags = GetPathFlags(pathFlags);
|
||||||
|
|
||||||
svcOutputDebugString(path, strnlen(path, 0x200));
|
|
||||||
|
|
||||||
nn::Result result = nn::fs::PathFormatter::Normalize(normalized, bufferSize, path, 0x200, flags);
|
nn::Result result = nn::fs::PathFormatter::Normalize(normalized, bufferSize, path, 0x200, flags);
|
||||||
|
|
||||||
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", \"%s\", %d, @\"%s\", %s},\n",
|
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", \"%s\", %d, @\"%s\", %s},\n",
|
||||||
path, pathFlags, bufferSize, normalized, GetResultName(result));
|
GetEscaped(path).c_str(), pathFlags, bufferSize, GetEscaped(normalized).c_str(), GetResultName(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr const auto TestData_PathNormalizerNormalize = make_array(
|
static constexpr const auto TestData_PathNormalizerNormalize = make_array(
|
||||||
std::make_tuple("/aa/bb/c/", false, true),
|
std::make_tuple("/aa/bb/c/", false, true, false),
|
||||||
std::make_tuple("aa/bb/c/", false, false),
|
std::make_tuple("aa/bb/c/", false, false, false),
|
||||||
std::make_tuple("aa/bb/c/", false, true),
|
std::make_tuple("aa/bb/c/", false, true, false),
|
||||||
std::make_tuple("mount:a/b", false, true),
|
std::make_tuple("mount:a/b", false, true, false),
|
||||||
std::make_tuple("/aa/bb/../..", true, false),
|
std::make_tuple("mo|unt:a/b", false, true, true),
|
||||||
std::make_tuple("/aa/bb/../../..", true, false),
|
std::make_tuple("/aa/bb/../..", true, false, false), // Windows paths won't error when trying to navigate to the parent of the root directory
|
||||||
std::make_tuple("/aa/bb/../../..", false, false),
|
std::make_tuple("/aa/bb/../../..", true, false, false),
|
||||||
std::make_tuple("aa/bb/../../..", true, true),
|
std::make_tuple("/aa/bb/../../..", false, false, false),
|
||||||
std::make_tuple("aa/bb/../../..", false, true),
|
std::make_tuple("aa/bb/../../..", true, true, false),
|
||||||
std::make_tuple("", false, false),
|
std::make_tuple("aa/bb/../../..", false, true, false),
|
||||||
std::make_tuple("/", false, false),
|
std::make_tuple("mount:a/b", false, true, true), // Test allowing invalid characters
|
||||||
std::make_tuple("/.", false, false),
|
std::make_tuple("/a|/bb/cc", false, false, true),
|
||||||
std::make_tuple("/./", false, false),
|
std::make_tuple("/>a/bb/cc", false, false, true),
|
||||||
std::make_tuple("/..", false, false),
|
std::make_tuple("/aa/.</cc", false, false, true),
|
||||||
std::make_tuple("//.", false, false),
|
std::make_tuple("/aa/..</cc", false, false, true),
|
||||||
std::make_tuple("/ ..", false, false),
|
std::make_tuple("", false, false, false),
|
||||||
std::make_tuple("/.. /", false, false),
|
std::make_tuple("/", false, false, false),
|
||||||
std::make_tuple("/. /.", false, false),
|
std::make_tuple("/.", false, false, false),
|
||||||
std::make_tuple("/aa/bb/cc/dd/./.././../..", false, false),
|
std::make_tuple("/./", false, false, false),
|
||||||
std::make_tuple("/aa/bb/cc/dd/./.././../../..", false, false),
|
std::make_tuple("/..", false, false, false),
|
||||||
std::make_tuple("/./aa/./bb/./cc/./dd/.", false, false),
|
std::make_tuple("//.", false, false, false),
|
||||||
std::make_tuple("/aa\\bb/cc", false, false),
|
std::make_tuple("/ ..", false, false, false),
|
||||||
std::make_tuple("/aa\\bb/cc", false, false),
|
std::make_tuple("/.. /", false, false, false),
|
||||||
std::make_tuple("/a|/bb/cc", false, false),
|
std::make_tuple("/. /.", false, false, false),
|
||||||
std::make_tuple("/>a/bb/cc", false, false),
|
std::make_tuple("/aa/bb/cc/dd/./.././../..", false, false, false),
|
||||||
std::make_tuple("/aa/.</cc", false, false),
|
std::make_tuple("/aa/bb/cc/dd/./.././../../..", false, false, false),
|
||||||
std::make_tuple("/aa/..</cc", false, false),
|
std::make_tuple("/./aa/./bb/./cc/./dd/.", false, false, false),
|
||||||
std::make_tuple("\\\\aa/bb/cc", false, false),
|
std::make_tuple("/aa\\bb/cc", false, false, false),
|
||||||
std::make_tuple("\\\\aa\\bb\\cc", false, false),
|
std::make_tuple("/aa\\bb/cc", false, false, false),
|
||||||
std::make_tuple("/aa/bb/..\\cc", false, false),
|
std::make_tuple("/a|/bb/cc", false, false, false),
|
||||||
std::make_tuple("/aa/bb\\..\\cc", false, false),
|
std::make_tuple("/>a/bb/cc", false, false, false),
|
||||||
std::make_tuple("/aa/bb\\..", false, false),
|
std::make_tuple("/aa/.</cc", false, false, false),
|
||||||
std::make_tuple("/aa\\bb/../cc", false, false)
|
std::make_tuple("/aa/..</cc", false, false, false),
|
||||||
|
std::make_tuple("\\\\aa/bb/cc", false, false, false),
|
||||||
|
std::make_tuple("\\\\aa\\bb\\cc", false, false, false),
|
||||||
|
std::make_tuple("/aa/bb/..\\cc", false, false, false),
|
||||||
|
std::make_tuple("/aa/bb\\..\\cc", false, false, false),
|
||||||
|
std::make_tuple("/aa/bb\\..", false, false, false),
|
||||||
|
std::make_tuple("/aa\\bb/../cc", false, false, false)
|
||||||
);
|
);
|
||||||
|
|
||||||
void CreateTest_PathNormalizerNormalize(char const* path, bool isWindowsPath, bool isRelativePath) {
|
void CreateTest_PathNormalizerNormalize(char const* path, bool isWindowsPath, bool isRelativePath, bool allowAllCharacters) {
|
||||||
char normalized[0x200] = { 0 };
|
char normalized[0x200] = { 0 };
|
||||||
uint64_t normalizedLength = 0;
|
uint64_t normalizedLength = 0;
|
||||||
|
|
||||||
nn::Result result = nn::fs::PathNormalizer::Normalize(normalized, &normalizedLength, path, 0x200, isWindowsPath, isRelativePath);
|
nn::Result result = nn::fs::PathNormalizer::Normalize(normalized, &normalizedLength, path, 0x200, isWindowsPath, isRelativePath, allowAllCharacters);
|
||||||
|
|
||||||
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", %s, %s, @\"%s\", %ld, %s},\n",
|
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", %s, %s, %s, @\"%s\", %ld, %s},\n",
|
||||||
path, BoolStr(isWindowsPath), BoolStr(isRelativePath), normalized, normalizedLength, GetResultName(result));
|
GetEscaped(path).c_str(), BoolStr(isWindowsPath), BoolStr(isRelativePath), BoolStr(allowAllCharacters), GetEscaped(normalized).c_str(), normalizedLength, GetResultName(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateTest_PathNormalizerIsNormalized(char const* path, bool isWindowsPath, bool isRelativePath) {
|
void CreateTest_PathNormalizerIsNormalized(char const* path, bool isWindowsPath, bool isRelativePath, bool allowAllCharacters) {
|
||||||
bool isNormalized = false;
|
bool isNormalized = false;
|
||||||
uint64_t normalizedLength = 0;
|
uint64_t normalizedLength = 0;
|
||||||
|
|
||||||
nn::Result result = nn::fs::PathNormalizer::IsNormalized(&isNormalized, &normalizedLength, path);
|
nn::Result result = nn::fs::PathNormalizer::IsNormalized(&isNormalized, &normalizedLength, path, allowAllCharacters);
|
||||||
|
|
||||||
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", %s, %ld, %s},\n",
|
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", %s, %s, %ld, %s},\n",
|
||||||
path, BoolStr(isNormalized), normalizedLength, GetResultName(result));
|
GetEscaped(path).c_str(), BoolStr(allowAllCharacters), BoolStr(isNormalized), normalizedLength, GetResultName(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr const auto TestData_PathNormalizerNormalize_SmallBuffer = make_array(
|
static constexpr const auto TestData_PathNormalizerNormalize_SmallBuffer = make_array(
|
||||||
@ -350,7 +427,7 @@ void CreateTest_PathNormalizerNormalize_SmallBuffer(char const* path, int buffer
|
|||||||
nn::Result result = nn::fs::PathNormalizer::Normalize(normalized, &normalizedLength, path, bufferSize, false, false);
|
nn::Result result = nn::fs::PathNormalizer::Normalize(normalized, &normalizedLength, path, bufferSize, false, false);
|
||||||
|
|
||||||
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", %d, @\"%s\", %ld, %s},\n",
|
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", %d, @\"%s\", %ld, %s},\n",
|
||||||
path, bufferSize, normalized, normalizedLength, GetResultName(result));
|
GetEscaped(path).c_str(), bufferSize, GetEscaped(normalized).c_str(), normalizedLength, GetResultName(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr const auto TestData_PathUtility_IsSubPath = make_array(
|
static constexpr const auto TestData_PathUtility_IsSubPath = make_array(
|
||||||
@ -377,9 +454,62 @@ void CreateTest_PathUtility_IsSubPath(const char* path1, const char* path2) {
|
|||||||
bool result = nn::fs::IsSubPath(path1, path2);
|
bool result = nn::fs::IsSubPath(path1, path2);
|
||||||
|
|
||||||
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", @\"%s\", %s},\n",
|
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", @\"%s\", %s},\n",
|
||||||
path1, path2, BoolStr(result));
|
GetEscaped(path1).c_str(), GetEscaped(path2).c_str(), BoolStr(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RunTest_Path_RemoveChild() {
|
||||||
|
auto path = nn::fs::Path();
|
||||||
|
nn::Result result = path.InitializeWithReplaceUnc("/aa/bb/./cc");
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n", GetResultName(result));
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n", path.m_String);
|
||||||
|
|
||||||
|
result = path.InitializeWithReplaceUnc("//aa/bb");
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n", GetResultName(result));
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n", path.m_String);
|
||||||
|
|
||||||
|
result = path.InitializeWithReplaceUnc("@Host://aa/bb");
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n", GetResultName(result));
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n", path.m_String);
|
||||||
|
|
||||||
|
result = path.InitializeWithReplaceUnc("mount:///aa/bb");
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n", GetResultName(result));
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n", path.m_String);
|
||||||
|
|
||||||
|
result = path.InitializeWithReplaceUnc("//mount:///aa/bb");
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n", GetResultName(result));
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n", path.m_String);
|
||||||
|
|
||||||
|
svcOutputDebugString(Buf, BufPos);
|
||||||
|
};
|
||||||
|
|
||||||
|
void RunTest_Path_InsertParent() {
|
||||||
|
auto path = nn::fs::Path();
|
||||||
|
nn::Result result1 = path.Initialize("/cc/dd");
|
||||||
|
nn::Result result2 = path.InsertParent("/aa/bb");
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n%s\n%s\n", GetResultName(result1), GetResultName(result2), path.m_String);
|
||||||
|
|
||||||
|
result1 = path.Initialize("/cc/dd");
|
||||||
|
result2 = path.InsertParent("aa/bb");
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n%s\n%s\n", GetResultName(result1), GetResultName(result2), path.m_String);
|
||||||
|
|
||||||
|
result1 = path.Initialize("/cc/dd/");
|
||||||
|
result2 = path.InsertParent("aa/bb");
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n%s\n%s\n", GetResultName(result1), GetResultName(result2), path.m_String);
|
||||||
|
|
||||||
|
result1 = path.Initialize("/cc/dd/");
|
||||||
|
result2 = path.InsertParent("/aa/bb");
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n%s\n%s\n", GetResultName(result1), GetResultName(result2), path.m_String);
|
||||||
|
|
||||||
|
result1 = path.Initialize("/cc/dd/");
|
||||||
|
result2 = path.Normalize(GetPathFlags(""));
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n%s\n%s\n", GetResultName(result1), GetResultName(result2), BoolStr(path.m_IsNormalized));
|
||||||
|
|
||||||
|
result2 = path.InsertParent("/aa/../bb");
|
||||||
|
BufPos += sprintf(&Buf[BufPos], "%s\n%s\n%s\n", GetResultName(result2), BoolStr(path.m_IsNormalized), path.m_String);
|
||||||
|
|
||||||
|
svcOutputDebugString(Buf, BufPos);
|
||||||
|
};
|
||||||
|
|
||||||
extern "C" void nnMain(void) {
|
extern "C" void nnMain(void) {
|
||||||
// nn::fs::detail::IsEnabledAccessLog(); // Adds the sdk version to the output
|
// nn::fs::detail::IsEnabledAccessLog(); // Adds the sdk version to the output
|
||||||
|
|
||||||
@ -388,6 +518,7 @@ extern "C" void nnMain(void) {
|
|||||||
CreateTest("TestData_PathFormatter_Normalize_WindowsPath", CreateTest_PathFormatterNormalize, TestData_PathFormatterNormalize_WindowsPath);
|
CreateTest("TestData_PathFormatter_Normalize_WindowsPath", CreateTest_PathFormatterNormalize, TestData_PathFormatterNormalize_WindowsPath);
|
||||||
CreateTest("TestData_PathFormatter_Normalize_RelativePath", CreateTest_PathFormatterNormalize, TestData_PathFormatterNormalize_RelativePath);
|
CreateTest("TestData_PathFormatter_Normalize_RelativePath", CreateTest_PathFormatterNormalize, TestData_PathFormatterNormalize_RelativePath);
|
||||||
CreateTest("TestData_PathFormatter_Normalize_Backslash", CreateTest_PathFormatterNormalize, TestData_PathFormatterNormalize_Backslash);
|
CreateTest("TestData_PathFormatter_Normalize_Backslash", CreateTest_PathFormatterNormalize, TestData_PathFormatterNormalize_Backslash);
|
||||||
|
CreateTest("TestData_PathFormatter_Normalize_AllowAllChars", CreateTest_PathFormatterNormalize, TestData_PathFormatterNormalize_AllowAllChars);
|
||||||
CreateTest("TestData_PathFormatter_Normalize_All", CreateTest_PathFormatterNormalize, TestData_PathFormatterNormalize_All);
|
CreateTest("TestData_PathFormatter_Normalize_All", CreateTest_PathFormatterNormalize, TestData_PathFormatterNormalize_All);
|
||||||
CreateTest("TestData_PathFormatter_Normalize_SmallBuffer", CreateTest_PathFormatterNormalize_SmallBuffer, TestData_PathFormatterNormalize_SmallBuffer);
|
CreateTest("TestData_PathFormatter_Normalize_SmallBuffer", CreateTest_PathFormatterNormalize_SmallBuffer, TestData_PathFormatterNormalize_SmallBuffer);
|
||||||
|
|
||||||
@ -396,6 +527,7 @@ extern "C" void nnMain(void) {
|
|||||||
CreateTest("TestData_PathFormatter_IsNormalized_WindowsPath", CreateTest_PathFormatterIsNormalized, TestData_PathFormatterNormalize_WindowsPath);
|
CreateTest("TestData_PathFormatter_IsNormalized_WindowsPath", CreateTest_PathFormatterIsNormalized, TestData_PathFormatterNormalize_WindowsPath);
|
||||||
CreateTest("TestData_PathFormatter_IsNormalized_RelativePath", CreateTest_PathFormatterIsNormalized, TestData_PathFormatterNormalize_RelativePath);
|
CreateTest("TestData_PathFormatter_IsNormalized_RelativePath", CreateTest_PathFormatterIsNormalized, TestData_PathFormatterNormalize_RelativePath);
|
||||||
CreateTest("TestData_PathFormatter_IsNormalized_Backslash", CreateTest_PathFormatterIsNormalized, TestData_PathFormatterNormalize_Backslash);
|
CreateTest("TestData_PathFormatter_IsNormalized_Backslash", CreateTest_PathFormatterIsNormalized, TestData_PathFormatterNormalize_Backslash);
|
||||||
|
CreateTest("TestData_PathFormatter_IsNormalized_AllowAllChars", CreateTest_PathFormatterIsNormalized, TestData_PathFormatterNormalize_AllowAllChars);
|
||||||
CreateTest("TestData_PathFormatter_IsNormalized_All", CreateTest_PathFormatterIsNormalized, TestData_PathFormatterNormalize_All);
|
CreateTest("TestData_PathFormatter_IsNormalized_All", CreateTest_PathFormatterIsNormalized, TestData_PathFormatterNormalize_All);
|
||||||
|
|
||||||
CreateTest("TestData_PathNormalizer_Normalize", CreateTest_PathNormalizerNormalize, TestData_PathNormalizerNormalize);
|
CreateTest("TestData_PathNormalizer_Normalize", CreateTest_PathNormalizerNormalize, TestData_PathNormalizerNormalize);
|
||||||
|
@ -9,51 +9,57 @@ namespace LibHac.Tests.Fs;
|
|||||||
|
|
||||||
public class PathNormalizerTests
|
public class PathNormalizerTests
|
||||||
{
|
{
|
||||||
public static TheoryData<string, bool, bool, string, long, Result> TestData_Normalize => new()
|
public static TheoryData<string, bool, bool, bool, string, long, Result> TestData_Normalize => new()
|
||||||
{
|
{
|
||||||
{ @"/aa/bb/c/", false, true, @"/aa/bb/c", 8, Result.Success },
|
{ @"/aa/bb/c/", false, true, false, @"/aa/bb/c", 8, Result.Success },
|
||||||
{ @"aa/bb/c/", false, false, @"", 0, ResultFs.InvalidPathFormat.Value },
|
{ @"aa/bb/c/", false, false, false, @"", 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"aa/bb/c/", false, true, @"/aa/bb/c", 8, Result.Success },
|
{ @"aa/bb/c/", false, true, false, @"/aa/bb/c", 8, Result.Success },
|
||||||
{ @"mount:a/b", false, true, @"/mount:a/b", 0, ResultFs.InvalidCharacter.Value },
|
{ @"mount:a/b", false, true, false, @"/", 0, ResultFs.InvalidCharacter.Value },
|
||||||
{ @"/aa/bb/../..", true, false, @"/", 1, Result.Success },
|
{ @"mo|unt:a/b", false, true, true, @"/mo|unt:a/b", 11, Result.Success },
|
||||||
{ @"/aa/bb/../../..", true, false, @"/", 1, Result.Success },
|
{ @"/aa/bb/../..", true, false, false, @"/", 1, Result.Success },
|
||||||
{ @"/aa/bb/../../..", false, false, @"/aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value },
|
{ @"/aa/bb/../../..", true, false, false, @"/", 1, Result.Success },
|
||||||
{ @"aa/bb/../../..", true, true, @"/", 1, Result.Success },
|
{ @"/aa/bb/../../..", false, false, false, @"/aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value },
|
||||||
{ @"aa/bb/../../..", false, true, @"/aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value },
|
{ @"aa/bb/../../..", true, true, false, @"/", 1, Result.Success },
|
||||||
{ @"", false, false, @"", 0, ResultFs.InvalidPathFormat.Value },
|
{ @"aa/bb/../../..", false, true, false, @"/aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value },
|
||||||
{ @"/", false, false, @"/", 1, Result.Success },
|
{ @"mount:a/b", false, true, true, @"/mount:a/b", 10, Result.Success },
|
||||||
{ @"/.", false, false, @"/", 1, Result.Success },
|
{ @"/a|/bb/cc", false, false, true, @"/a|/bb/cc", 9, Result.Success },
|
||||||
{ @"/./", false, false, @"/", 1, Result.Success },
|
{ @"/>a/bb/cc", false, false, true, @"/>a/bb/cc", 9, Result.Success },
|
||||||
{ @"/..", false, false, @"/", 0, ResultFs.DirectoryUnobtainable.Value },
|
{ @"/aa/.</cc", false, false, true, @"/aa/.</cc", 9, Result.Success },
|
||||||
{ @"//.", false, false, @"/", 1, Result.Success },
|
{ @"/aa/..</cc", false, false, true, @"/aa/..</cc", 10, Result.Success },
|
||||||
{ @"/ ..", false, false, @"/ ..", 4, Result.Success },
|
{ @"", false, false, false, @"", 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"/.. /", false, false, @"/.. ", 4, Result.Success },
|
{ @"/", false, false, false, @"/", 1, Result.Success },
|
||||||
{ @"/. /.", false, false, @"/. ", 3, Result.Success },
|
{ @"/.", false, false, false, @"/", 1, Result.Success },
|
||||||
{ @"/aa/bb/cc/dd/./.././../..", false, false, @"/aa", 3, Result.Success },
|
{ @"/./", false, false, false, @"/", 1, Result.Success },
|
||||||
{ @"/aa/bb/cc/dd/./.././../../..", false, false, @"/", 1, Result.Success },
|
{ @"/..", false, false, false, @"/", 0, ResultFs.DirectoryUnobtainable.Value },
|
||||||
{ @"/./aa/./bb/./cc/./dd/.", false, false, @"/aa/bb/cc/dd", 12, Result.Success },
|
{ @"//.", false, false, false, @"/", 1, Result.Success },
|
||||||
{ @"/aa\bb/cc", false, false, @"/aa\bb/cc", 9, Result.Success },
|
{ @"/ ..", false, false, false, @"/ ..", 4, Result.Success },
|
||||||
{ @"/aa\bb/cc", false, false, @"/aa\bb/cc", 9, Result.Success },
|
{ @"/.. /", false, false, false, @"/.. ", 4, Result.Success },
|
||||||
{ @"/a|/bb/cc", false, false, @"/a|/bb/cc", 0, ResultFs.InvalidCharacter.Value },
|
{ @"/. /.", false, false, false, @"/. ", 3, Result.Success },
|
||||||
{ @"/>a/bb/cc", false, false, @"/>a/bb/cc", 0, ResultFs.InvalidCharacter.Value },
|
{ @"/aa/bb/cc/dd/./.././../..", false, false, false, @"/aa", 3, Result.Success },
|
||||||
{ @"/aa/.</cc", false, false, @"/aa/.</cc", 0, ResultFs.InvalidCharacter.Value },
|
{ @"/aa/bb/cc/dd/./.././../../..", false, false, false, @"/", 1, Result.Success },
|
||||||
{ @"/aa/..</cc", false, false, @"/aa/..</cc", 0, ResultFs.InvalidCharacter.Value },
|
{ @"/./aa/./bb/./cc/./dd/.", false, false, false, @"/aa/bb/cc/dd", 12, Result.Success },
|
||||||
{ @"\\aa/bb/cc", false, false, @"", 0, ResultFs.InvalidPathFormat.Value },
|
{ @"/aa\bb/cc", false, false, false, @"/aa\bb/cc", 9, Result.Success },
|
||||||
{ @"\\aa\bb\cc", false, false, @"", 0, ResultFs.InvalidPathFormat.Value },
|
{ @"/aa\bb/cc", false, false, false, @"/aa\bb/cc", 9, Result.Success },
|
||||||
{ @"/aa/bb/..\cc", false, false, @"/aa/cc", 6, Result.Success },
|
{ @"/a|/bb/cc", false, false, false, @"/", 0, ResultFs.InvalidCharacter.Value },
|
||||||
{ @"/aa/bb\..\cc", false, false, @"/aa/cc", 6, Result.Success },
|
{ @"/>a/bb/cc", false, false, false, @"/", 0, ResultFs.InvalidCharacter.Value },
|
||||||
{ @"/aa/bb\..", false, false, @"/aa", 3, Result.Success },
|
{ @"/aa/.</cc", false, false, false, @"/aa/", 0, ResultFs.InvalidCharacter.Value },
|
||||||
{ @"/aa\bb/../cc", false, false, @"/cc", 3, Result.Success }
|
{ @"/aa/..</cc", false, false, false, @"/aa/", 0, ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"\\aa/bb/cc", false, false, false, @"", 0, ResultFs.InvalidPathFormat.Value },
|
||||||
|
{ @"\\aa\bb\cc", false, false, false, @"", 0, ResultFs.InvalidPathFormat.Value },
|
||||||
|
{ @"/aa/bb/..\cc", false, false, false, @"/aa/cc", 6, Result.Success },
|
||||||
|
{ @"/aa/bb\..\cc", false, false, false, @"/aa/cc", 6, Result.Success },
|
||||||
|
{ @"/aa/bb\..", false, false, false, @"/aa", 3, Result.Success },
|
||||||
|
{ @"/aa\bb/../cc", false, false, false, @"/cc", 3, Result.Success }
|
||||||
};
|
};
|
||||||
|
|
||||||
[Theory, MemberData(nameof(TestData_Normalize))]
|
[Theory, MemberData(nameof(TestData_Normalize))]
|
||||||
public static void Normalize(string path, bool isWindowsPath, bool isDriveRelativePath, string expectedNormalized,
|
public static void Normalize(string path, bool isWindowsPath, bool isDriveRelativePath, bool allowAllCharacters,
|
||||||
long expectedLength, Result expectedResult)
|
string expectedNormalized, long expectedLength, Result expectedResult)
|
||||||
{
|
{
|
||||||
byte[] buffer = new byte[0x301];
|
byte[] buffer = new byte[0x301];
|
||||||
|
|
||||||
Result result = PathNormalizer.Normalize(buffer, out int normalizedLength, path.ToU8Span(), isWindowsPath,
|
Result result = PathNormalizer.Normalize(buffer, out int normalizedLength, path.ToU8Span(), isWindowsPath,
|
||||||
isDriveRelativePath);
|
isDriveRelativePath, allowAllCharacters);
|
||||||
|
|
||||||
Assert.Equal(expectedResult, result);
|
Assert.Equal(expectedResult, result);
|
||||||
Assert.Equal(expectedNormalized, StringUtils.Utf8ZToString(buffer));
|
Assert.Equal(expectedNormalized, StringUtils.Utf8ZToString(buffer));
|
||||||
@ -93,47 +99,53 @@ public class PathNormalizerTests
|
|||||||
Assert.Equal(expectedLength, normalizedLength);
|
Assert.Equal(expectedLength, normalizedLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TheoryData<string, bool, long, Result> TestData_IsNormalized => new()
|
public static TheoryData<string, bool, bool, long, Result> TestData_IsNormalized => new()
|
||||||
{
|
{
|
||||||
{ @"/aa/bb/c/", false, 9, Result.Success },
|
{ @"/aa/bb/c/", false, false, 9, Result.Success },
|
||||||
{ @"aa/bb/c/", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"aa/bb/c/", false, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"aa/bb/c/", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"aa/bb/c/", false, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"mount:a/b", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"mount:a/b", false, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"/aa/bb/../..", false, 0, Result.Success },
|
{ @"mo|unt:a/b", true, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"/aa/bb/../../..", false, 0, Result.Success },
|
{ @"/aa/bb/../..", false, false, 0, Result.Success },
|
||||||
{ @"/aa/bb/../../..", false, 0, Result.Success },
|
{ @"/aa/bb/../../..", false, false, 0, Result.Success },
|
||||||
{ @"aa/bb/../../..", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"/aa/bb/../../..", false, false, 0, Result.Success },
|
||||||
{ @"aa/bb/../../..", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"aa/bb/../../..", false, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"aa/bb/../../..", false, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"/", true, 1, Result.Success },
|
{ @"mount:a/b", true, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"/.", false, 2, Result.Success },
|
{ @"/a|/bb/cc", true, true, 9, Result.Success },
|
||||||
{ @"/./", false, 0, Result.Success },
|
{ @"/>a/bb/cc", true, true, 9, Result.Success },
|
||||||
{ @"/..", false, 3, Result.Success },
|
{ @"/aa/.</cc", true, true, 9, Result.Success },
|
||||||
{ @"//.", false, 0, Result.Success },
|
{ @"/aa/..</cc", true, true, 10, Result.Success },
|
||||||
{ @"/ ..", true, 4, Result.Success },
|
{ @"", false, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
{ @"/.. /", false, 5, Result.Success },
|
{ @"/", false, true, 1, Result.Success },
|
||||||
{ @"/. /.", false, 5, Result.Success },
|
{ @"/.", false, false, 2, Result.Success },
|
||||||
{ @"/aa/bb/cc/dd/./.././../..", false, 0, Result.Success },
|
{ @"/./", false, false, 0, Result.Success },
|
||||||
{ @"/aa/bb/cc/dd/./.././../../..", false, 0, Result.Success },
|
{ @"/..", false, false, 3, Result.Success },
|
||||||
{ @"/./aa/./bb/./cc/./dd/.", false, 0, Result.Success },
|
{ @"//.", false, false, 0, Result.Success },
|
||||||
{ @"/aa\bb/cc", true, 9, Result.Success },
|
{ @"/ ..", false, true, 4, Result.Success },
|
||||||
{ @"/aa\bb/cc", true, 9, Result.Success },
|
{ @"/.. /", false, false, 5, Result.Success },
|
||||||
{ @"/a|/bb/cc", false, 0, ResultFs.InvalidCharacter.Value },
|
{ @"/. /.", false, false, 5, Result.Success },
|
||||||
{ @"/>a/bb/cc", false, 0, ResultFs.InvalidCharacter.Value },
|
{ @"/aa/bb/cc/dd/./.././../..", false, false, 0, Result.Success },
|
||||||
{ @"/aa/.</cc", false, 0, ResultFs.InvalidCharacter.Value },
|
{ @"/aa/bb/cc/dd/./.././../../..", false, false, 0, Result.Success },
|
||||||
{ @"/aa/..</cc", false, 0, ResultFs.InvalidCharacter.Value },
|
{ @"/./aa/./bb/./cc/./dd/.", false, false, 0, Result.Success },
|
||||||
{ @"\\aa/bb/cc", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"/aa\bb/cc", false, true, 9, Result.Success },
|
||||||
{ @"\\aa\bb\cc", false, 0, ResultFs.InvalidPathFormat.Value },
|
{ @"/aa\bb/cc", false, true, 9, Result.Success },
|
||||||
{ @"/aa/bb/..\cc", true, 12, Result.Success },
|
{ @"/a|/bb/cc", false, false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
{ @"/aa/bb\..\cc", true, 12, Result.Success },
|
{ @"/>a/bb/cc", false, false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
{ @"/aa/bb\..", true, 9, Result.Success },
|
{ @"/aa/.</cc", false, false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
{ @"/aa\bb/../cc", false, 0, Result.Success }
|
{ @"/aa/..</cc", false, false, 0, ResultFs.InvalidCharacter.Value },
|
||||||
|
{ @"\\aa/bb/cc", false, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
|
{ @"\\aa\bb\cc", false, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||||
|
{ @"/aa/bb/..\cc", false, true, 12, Result.Success },
|
||||||
|
{ @"/aa/bb\..\cc", false, true, 12, Result.Success },
|
||||||
|
{ @"/aa/bb\..", false, true, 9, Result.Success },
|
||||||
|
{ @"/aa\bb/../cc", false, false, 0, Result.Success }
|
||||||
};
|
};
|
||||||
|
|
||||||
[Theory, MemberData(nameof(TestData_IsNormalized))]
|
[Theory, MemberData(nameof(TestData_IsNormalized))]
|
||||||
public static void IsNormalized(string path, bool expectedIsNormalized, long expectedLength, Result expectedResult)
|
public static void IsNormalized(string path, bool allowAllCharacters, bool expectedIsNormalized, long expectedLength, Result expectedResult)
|
||||||
{
|
{
|
||||||
Result result = PathNormalizer.IsNormalized(out bool isNormalized, out int length, path.ToU8Span());
|
Result result = PathNormalizer.IsNormalized(out bool isNormalized, out int length, path.ToU8Span(), allowAllCharacters);
|
||||||
|
|
||||||
Assert.Equal(expectedResult, result);
|
Assert.Equal(expectedResult, result);
|
||||||
Assert.Equal(expectedLength, length);
|
Assert.Equal(expectedLength, length);
|
||||||
@ -143,4 +155,4 @@ public class PathNormalizerTests
|
|||||||
Assert.Equal(expectedIsNormalized, isNormalized);
|
Assert.Equal(expectedIsNormalized, isNormalized);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user