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 AllowMountName() => _value |= 1 << 3;
|
||||
public void AllowBackslash() => _value |= 1 << 4;
|
||||
public void AllowAllCharacters() => _value |= 1 << 5;
|
||||
|
||||
public bool IsWindowsPathAllowed() => (_value & (1 << 0)) != 0;
|
||||
public bool IsRelativePathAllowed() => (_value & (1 << 1)) != 0;
|
||||
public bool IsEmptyPathAllowed() => (_value & (1 << 2)) != 0;
|
||||
public bool IsMountNameAllowed() => (_value & (1 << 3)) != 0;
|
||||
public bool IsBackslashAllowed() => (_value & (1 << 4)) != 0;
|
||||
public bool AreAllCharactersAllowed() => (_value & (1 << 5)) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -13,9 +13,19 @@ namespace LibHac.Fs;
|
||||
/// <summary>
|
||||
/// Contains functions for working with path formatting and normalization.
|
||||
/// </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
|
||||
{
|
||||
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)]
|
||||
private static Result CheckHostName(ReadOnlySpan<byte> name)
|
||||
{
|
||||
@ -24,8 +34,11 @@ public static class PathFormatter
|
||||
|
||||
for (int i = 0; i < name.Length; i++)
|
||||
{
|
||||
if (name[i] == ':' || name[i] == '$')
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
foreach (byte c in InvalidCharacterForHostName)
|
||||
{
|
||||
if (name[i] == c)
|
||||
return ResultFs.InvalidCharacter.Log();
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
@ -41,8 +54,11 @@ public static class PathFormatter
|
||||
|
||||
for (int i = 0; i < name.Length; i++)
|
||||
{
|
||||
if (name[i] == ':')
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
foreach (byte c in InvalidCharacter)
|
||||
{
|
||||
if (name[i] == c)
|
||||
return ResultFs.InvalidCharacter.Log();
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
@ -90,8 +106,11 @@ public static class PathFormatter
|
||||
|
||||
for (int i = 0; i < mountLength; i++)
|
||||
{
|
||||
if (path.At(i) is (byte)'*' or (byte)'?' or (byte)'<' or (byte)'>' or (byte)'|')
|
||||
return ResultFs.InvalidCharacter.Log();
|
||||
foreach (byte c in InvalidCharacterForMountName)
|
||||
{
|
||||
if (path.At(i) == c)
|
||||
return ResultFs.InvalidCharacter.Log();
|
||||
}
|
||||
}
|
||||
|
||||
if (!outMountNameBuffer.IsEmpty)
|
||||
@ -150,6 +169,12 @@ public static class PathFormatter
|
||||
int 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 ||
|
||||
currentPath[winPathLength] == AltDirectorySeparator)
|
||||
{
|
||||
@ -478,8 +503,11 @@ public static class PathFormatter
|
||||
}
|
||||
}
|
||||
|
||||
if (PathNormalizer.IsParentDirectoryPathReplacementNeeded(buffer))
|
||||
return ResultFs.DirectoryUnobtainable.Log();
|
||||
if (flags.IsBackslashAllowed() && PathNormalizer.IsParentDirectoryPathReplacementNeeded(buffer))
|
||||
{
|
||||
isNormalized = false;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
rc = PathUtility.CheckInvalidBackslash(out bool isBackslashContained, buffer,
|
||||
flags.IsWindowsPathAllowed() || flags.IsBackslashAllowed());
|
||||
@ -491,7 +519,7 @@ public static class PathFormatter
|
||||
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;
|
||||
|
||||
totalLength += length;
|
||||
@ -612,7 +640,8 @@ public static class PathFormatter
|
||||
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;
|
||||
|
||||
return Result.Success;
|
||||
|
@ -10,7 +10,7 @@ namespace LibHac.Fs;
|
||||
/// <summary>
|
||||
/// Contains functions for doing with basic path normalization.
|
||||
/// </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
|
||||
{
|
||||
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,
|
||||
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);
|
||||
|
||||
@ -32,7 +38,7 @@ public static class PathNormalizer
|
||||
int totalLength = 0;
|
||||
int i = 0;
|
||||
|
||||
if (!IsSeparator(path.At(0)))
|
||||
if (path.At(0) != DirectorySeparator)
|
||||
{
|
||||
if (!isDriveRelativePath)
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
@ -43,6 +49,7 @@ public static class PathNormalizer
|
||||
var convertedPath = new RentedArray<byte>();
|
||||
try
|
||||
{
|
||||
Result rc;
|
||||
// Check if parent directory path replacement is needed.
|
||||
if (IsParentDirectoryPathReplacementNeeded(currentPath))
|
||||
{
|
||||
@ -58,20 +65,22 @@ public static class PathNormalizer
|
||||
|
||||
bool skipNextSeparator = false;
|
||||
|
||||
while (!IsNul(currentPath.At(i)))
|
||||
while (currentPath.At(i) != NullTerminator)
|
||||
{
|
||||
if (IsSeparator(currentPath[i]))
|
||||
if (currentPath[i] == DirectorySeparator)
|
||||
{
|
||||
do
|
||||
{
|
||||
i++;
|
||||
} while (IsSeparator(currentPath.At(i)));
|
||||
} while (currentPath.At(i) == DirectorySeparator);
|
||||
|
||||
if (IsNul(currentPath.At(i)))
|
||||
if (currentPath.At(i) == NullTerminator)
|
||||
break;
|
||||
|
||||
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)
|
||||
{
|
||||
outputBuffer[totalLength] = NullTerminator;
|
||||
@ -87,8 +96,14 @@ public static class PathNormalizer
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
@ -163,12 +178,14 @@ public static class PathNormalizer
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
outputBuffer[totalLength] = NullTerminator;
|
||||
|
||||
Result rc = IsNormalized(out bool isNormalized, out _, outputBuffer);
|
||||
rc = IsNormalized(out bool isNormalized, out _, outputBuffer, allowAllCharacters);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
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.InvalidPathFormat"/>: The path is not in a valid format.</returns>
|
||||
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);
|
||||
|
||||
@ -213,7 +236,7 @@ public static class PathNormalizer
|
||||
|
||||
pathLength++;
|
||||
|
||||
if (state != PathState.Initial)
|
||||
if (!allowAllCharacters && state != PathState.Initial)
|
||||
{
|
||||
Result rc = CheckInvalidCharacter(c);
|
||||
if (rc.IsFailure()) return rc;
|
||||
@ -292,7 +315,6 @@ public static class PathNormalizer
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a path begins with / or \ and contains any of these patterns:
|
||||
/// "/..\", "\..\", "\../", "\..0" where '0' is the null terminator.
|
||||
|
@ -54,17 +54,18 @@ public class PathFormatterTests
|
||||
{ @"\\?\c:\", "", @"", 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:/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", ResultFs.InvalidCharacter.Value },
|
||||
{ @"/mount:/aa/bb", "W", @"/mount:/aa/bb", ResultFs.InvalidCharacter.Value },
|
||||
{ @"/mount:/aa/bb", "MW", @"/", 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", "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 },
|
||||
{ @"\\.\mount:\.\aa", "W", @"\\./mount:/aa", ResultFs.InvalidCharacter.Value },
|
||||
{ @"\\.\mount:\.\aa", "W", @"\\./", ResultFs.InvalidCharacter.Value },
|
||||
{ @"\\./.\aa", "W", @"\\./aa", Result.Success },
|
||||
{ @"\\/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\aa\bb\..\cc\.", "W", @"\\host\share/path/aa/cc", Result.Success },
|
||||
{ @"\\host\", "W", @"", ResultFs.InvalidPathFormat.Value },
|
||||
{ @"\\ho$st\share\path", "W", @"", ResultFs.InvalidPathFormat.Value },
|
||||
{ @"\\host:\share\path", "W", @"", ResultFs.InvalidPathFormat.Value },
|
||||
{ @"\\ho$st\share\path", "W", @"", ResultFs.InvalidCharacter.Value },
|
||||
{ @"\\host:\share\path", "W", @"", ResultFs.InvalidCharacter.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\sha:re", "W", @"", ResultFs.InvalidPathFormat.Value },
|
||||
{ @"\\host\sha:re", "W", @"", ResultFs.InvalidCharacter.Value },
|
||||
{ @".\\host\share", "RW", @"..\\host\share/", Result.Success }
|
||||
};
|
||||
|
||||
@ -128,14 +129,46 @@ public class PathFormatterTests
|
||||
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()
|
||||
{
|
||||
{ @"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", "WRMB", @"mount:./aa/bb/cc/dd", Result.Success },
|
||||
{ @"mount:./.c:/aa/bb", "RM", @"mount:./.c:/aa/bb", ResultFs.InvalidCharacter.Value },
|
||||
{ @"mount:.c:/aa/bb", "WRM", @"mount:./.c:/aa/bb", ResultFs.InvalidCharacter.Value },
|
||||
{ @"mount:./cc:/aa/bb", "WRM", @"mount:./cc:/aa/bb", ResultFs.InvalidCharacter.Value },
|
||||
{ @"mount:./.c:/aa/bb", "RM", @"mount:./", ResultFs.InvalidCharacter.Value },
|
||||
{ @"mount:.c:/aa/bb", "WRM", @"mount:./", 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", "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 },
|
||||
{ @".//aa/bb", "RW", @"./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))]
|
||||
@ -219,7 +253,6 @@ public class PathFormatterTests
|
||||
|
||||
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 },
|
||||
{ @"\\host\share", "", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
@ -228,6 +261,7 @@ public class PathFormatterTests
|
||||
{ @"\\?\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 },
|
||||
{ @"c:\aa\..\..\..\bb", "W", false, 0, Result.Success },
|
||||
{ @"mount:/\\aa\..\bb", "MW", false, 0, Result.Success },
|
||||
{ @"mount:/c:\aa\..\bb", "MW", false, 0, 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 },
|
||||
{ @"a:aa/../bb", "MW", false, 8, 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 },
|
||||
{ @"\\.\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\aa\bb\..\cc\.", "W", false, 0, Result.Success },
|
||||
{ @"\\host\", "W", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"\\ho$st\share\path", "W", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"\\host:\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.InvalidCharacter.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\sha:re", "W", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"\\host\sha:re", "W", false, 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @".\\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", "B", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"/aa\bb\..\cc", "", false, 0, ResultFs.DirectoryUnobtainable.Value },
|
||||
{ @"/aa\bb\..\cc", "B", false, 0, ResultFs.DirectoryUnobtainable.Value },
|
||||
{ @"/aa\bb\..\cc", "", false, 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/aa\bb\..\cc", "B", false, 0, Result.Success },
|
||||
{ @"/aa\bb\cc", "", false, 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/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", "WB", false, 0, Result.Success },
|
||||
{ @"/aa/bb\../cc/..\dd\..\ee/..", "", false, 0, ResultFs.DirectoryUnobtainable.Value },
|
||||
{ @"/aa/bb\../cc/..\dd\..\ee/..", "B", false, 0, ResultFs.DirectoryUnobtainable.Value }
|
||||
{ @"/aa/bb\../cc/..\dd\..\ee/..", "", false, 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/aa/bb\../cc/..\dd\..\ee/..", "B", false, 0, Result.Success }
|
||||
};
|
||||
|
||||
[Theory, MemberData(nameof(TestData_IsNormalized_Backslash))]
|
||||
@ -305,6 +339,39 @@ public class PathFormatterTests
|
||||
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()
|
||||
{
|
||||
{ @"mount:./aa/bb", "WRM", true, 13, Result.Success },
|
||||
@ -324,7 +391,8 @@ public class PathFormatterTests
|
||||
{ @"mount:/aa\bb", "BM", true, 12, Result.Success },
|
||||
{ @".//aa/bb", "RW", false, 1, 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))]
|
||||
@ -386,6 +454,11 @@ public class PathFormatterTests
|
||||
case 'W':
|
||||
flags.AllowWindowsPath();
|
||||
break;
|
||||
case 'C':
|
||||
flags.AllowAllCharacters();
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ namespace nn::fs::detail {
|
||||
bool IsEnabledAccessLog();
|
||||
}
|
||||
|
||||
// SDK 12
|
||||
// SDK 13
|
||||
namespace nn::fs {
|
||||
bool IsSubPath(const char* path1, const char* path2);
|
||||
|
||||
@ -29,12 +29,32 @@ namespace nn::fs {
|
||||
void AllowEmptyPath() { value |= (1 << 2); }
|
||||
void AllowMountName() { value |= (1 << 3); }
|
||||
void AllowBackslash() { value |= (1 << 4); }
|
||||
void AllowAllCharacters() { value |= (1 << 5); }
|
||||
|
||||
const bool IsWindowsPathAllowed() { return (value & (1 << 0)) != 0; }
|
||||
const bool IsRelativePathAllowed() { return (value & (1 << 1)) != 0; }
|
||||
const bool IsEmptyPathAllowed() { return (value & (1 << 2)) != 0; }
|
||||
const bool IsMountNameAllowed() { return (value & (1 << 3)) != 0; }
|
||||
const bool IsBackslashAllowed() { return (value & (1 << 4)) != 0; }
|
||||
bool IsWindowsPathAllowed()const { return (value & (1 << 0)) != 0; }
|
||||
bool IsRelativePathAllowed()const { return (value & (1 << 1)) != 0; }
|
||||
bool IsEmptyPathAllowed()const { return (value & (1 << 2)) != 0; }
|
||||
bool IsMountNameAllowed()const { return (value & (1 << 3)) != 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 {
|
||||
@ -48,7 +68,9 @@ namespace nn::fs {
|
||||
class PathNormalizer {
|
||||
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, 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, 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) {
|
||||
switch (result.GetValue()) {
|
||||
case 0: return "Result.Success";
|
||||
case 0x177202: return "ResultFs.NotImplemented.Value";
|
||||
case 0x2EE402: return "ResultFs.InvalidPath.Value";
|
||||
case 0x2EE602: return "ResultFs.TooLongPath.Value";
|
||||
case 0x2EE802: return "ResultFs.InvalidCharacter.Value";
|
||||
@ -111,12 +134,33 @@ nn::fs::PathFlags GetPathFlags(char const* pathFlags) {
|
||||
case 'W':
|
||||
flags.AllowWindowsPath();
|
||||
break;
|
||||
case 'C':
|
||||
flags.AllowAllCharacters();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
// Check AllowEmptyPath option
|
||||
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"), // 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:/c:\aa\..\bb)", "MW"),
|
||||
std::make_tuple(R"(mount:/aa/bb)", "MW"),
|
||||
@ -204,6 +249,33 @@ static constexpr const auto TestData_PathFormatterNormalize_Backslash = make_arr
|
||||
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(
|
||||
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
|
||||
@ -222,7 +294,8 @@ static constexpr const auto TestData_PathFormatterNormalize_All = make_array(
|
||||
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)", "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) {
|
||||
@ -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);
|
||||
|
||||
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) {
|
||||
@ -243,7 +316,7 @@ void CreateTest_PathFormatterIsNormalized(char const* path, char const* pathFlag
|
||||
nn::Result result = nn::fs::PathFormatter::IsNormalized(&isNormalized, &normalizedLength, path, flags);
|
||||
|
||||
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(
|
||||
@ -259,68 +332,72 @@ void CreateTest_PathFormatterNormalize_SmallBuffer(char const* path, char const*
|
||||
char normalized[0x200] = { 0 };
|
||||
nn::fs::PathFlags flags = GetPathFlags(pathFlags);
|
||||
|
||||
svcOutputDebugString(path, strnlen(path, 0x200));
|
||||
|
||||
nn::Result result = nn::fs::PathFormatter::Normalize(normalized, bufferSize, path, 0x200, flags);
|
||||
|
||||
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(
|
||||
std::make_tuple("/aa/bb/c/", false, true),
|
||||
std::make_tuple("aa/bb/c/", false, false),
|
||||
std::make_tuple("aa/bb/c/", false, true),
|
||||
std::make_tuple("mount:a/b", false, true),
|
||||
std::make_tuple("/aa/bb/../..", true, false),
|
||||
std::make_tuple("/aa/bb/../../..", true, false),
|
||||
std::make_tuple("/aa/bb/../../..", false, false),
|
||||
std::make_tuple("aa/bb/../../..", true, true),
|
||||
std::make_tuple("aa/bb/../../..", false, true),
|
||||
std::make_tuple("", false, false),
|
||||
std::make_tuple("/", false, false),
|
||||
std::make_tuple("/.", false, false),
|
||||
std::make_tuple("/./", false, false),
|
||||
std::make_tuple("/..", false, false),
|
||||
std::make_tuple("//.", false, false),
|
||||
std::make_tuple("/ ..", false, false),
|
||||
std::make_tuple("/.. /", false, false),
|
||||
std::make_tuple("/. /.", false, false),
|
||||
std::make_tuple("/aa/bb/cc/dd/./.././../..", false, false),
|
||||
std::make_tuple("/aa/bb/cc/dd/./.././../../..", false, false),
|
||||
std::make_tuple("/./aa/./bb/./cc/./dd/.", false, false),
|
||||
std::make_tuple("/aa\\bb/cc", false, false),
|
||||
std::make_tuple("/aa\\bb/cc", false, false),
|
||||
std::make_tuple("/a|/bb/cc", false, false),
|
||||
std::make_tuple("/>a/bb/cc", false, false),
|
||||
std::make_tuple("/aa/.</cc", false, false),
|
||||
std::make_tuple("/aa/..</cc", false, false),
|
||||
std::make_tuple("\\\\aa/bb/cc", false, false),
|
||||
std::make_tuple("\\\\aa\\bb\\cc", false, false),
|
||||
std::make_tuple("/aa/bb/..\\cc", false, false),
|
||||
std::make_tuple("/aa/bb\\..\\cc", false, false),
|
||||
std::make_tuple("/aa/bb\\..", false, false),
|
||||
std::make_tuple("/aa\\bb/../cc", false, false)
|
||||
std::make_tuple("/aa/bb/c/", false, true, false),
|
||||
std::make_tuple("aa/bb/c/", false, false, false),
|
||||
std::make_tuple("aa/bb/c/", false, true, false),
|
||||
std::make_tuple("mount:a/b", false, true, false),
|
||||
std::make_tuple("mo|unt:a/b", false, true, true),
|
||||
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/../../..", true, false, false),
|
||||
std::make_tuple("/aa/bb/../../..", false, false, false),
|
||||
std::make_tuple("aa/bb/../../..", true, true, false),
|
||||
std::make_tuple("aa/bb/../../..", false, true, false),
|
||||
std::make_tuple("mount:a/b", false, true, true), // Test allowing invalid characters
|
||||
std::make_tuple("/a|/bb/cc", false, false, true),
|
||||
std::make_tuple("/>a/bb/cc", false, false, true),
|
||||
std::make_tuple("/aa/.</cc", false, false, true),
|
||||
std::make_tuple("/aa/..</cc", false, false, true),
|
||||
std::make_tuple("", false, false, false),
|
||||
std::make_tuple("/", false, false, false),
|
||||
std::make_tuple("/.", false, false, false),
|
||||
std::make_tuple("/./", false, false, false),
|
||||
std::make_tuple("/..", false, false, false),
|
||||
std::make_tuple("//.", false, false, false),
|
||||
std::make_tuple("/ ..", false, false, false),
|
||||
std::make_tuple("/.. /", false, false, false),
|
||||
std::make_tuple("/. /.", false, false, false),
|
||||
std::make_tuple("/aa/bb/cc/dd/./.././../..", false, false, false),
|
||||
std::make_tuple("/aa/bb/cc/dd/./.././../../..", false, false, false),
|
||||
std::make_tuple("/./aa/./bb/./cc/./dd/.", false, false, false),
|
||||
std::make_tuple("/aa\\bb/cc", false, false, false),
|
||||
std::make_tuple("/aa\\bb/cc", false, false, false),
|
||||
std::make_tuple("/a|/bb/cc", false, false, false),
|
||||
std::make_tuple("/>a/bb/cc", false, false, false),
|
||||
std::make_tuple("/aa/.</cc", false, 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 };
|
||||
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",
|
||||
path, BoolStr(isWindowsPath), BoolStr(isRelativePath), normalized, normalizedLength, GetResultName(result));
|
||||
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", %s, %s, %s, @\"%s\", %ld, %s},\n",
|
||||
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;
|
||||
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",
|
||||
path, BoolStr(isNormalized), normalizedLength, GetResultName(result));
|
||||
BufPos += sprintf(&Buf[BufPos], "{@\"%s\", %s, %s, %ld, %s},\n",
|
||||
GetEscaped(path).c_str(), BoolStr(allowAllCharacters), BoolStr(isNormalized), normalizedLength, GetResultName(result));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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(
|
||||
@ -377,9 +454,62 @@ void CreateTest_PathUtility_IsSubPath(const char* path1, const char* path2) {
|
||||
bool result = nn::fs::IsSubPath(path1, path2);
|
||||
|
||||
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) {
|
||||
// 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_RelativePath", CreateTest_PathFormatterNormalize, TestData_PathFormatterNormalize_RelativePath);
|
||||
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_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_RelativePath", CreateTest_PathFormatterIsNormalized, TestData_PathFormatterNormalize_RelativePath);
|
||||
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_PathNormalizer_Normalize", CreateTest_PathNormalizerNormalize, TestData_PathNormalizerNormalize);
|
||||
|
@ -9,51 +9,57 @@ namespace LibHac.Tests.Fs;
|
||||
|
||||
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, false, @"", 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"aa/bb/c/", false, true, @"/aa/bb/c", 8, Result.Success },
|
||||
{ @"mount:a/b", false, true, @"/mount:a/b", 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/aa/bb/../..", true, false, @"/", 1, Result.Success },
|
||||
{ @"/aa/bb/../../..", true, false, @"/", 1, Result.Success },
|
||||
{ @"/aa/bb/../../..", false, false, @"/aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value },
|
||||
{ @"aa/bb/../../..", true, true, @"/", 1, Result.Success },
|
||||
{ @"aa/bb/../../..", false, true, @"/aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value },
|
||||
{ @"", false, false, @"", 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"/", false, false, @"/", 1, Result.Success },
|
||||
{ @"/.", false, false, @"/", 1, Result.Success },
|
||||
{ @"/./", false, false, @"/", 1, Result.Success },
|
||||
{ @"/..", false, false, @"/", 0, ResultFs.DirectoryUnobtainable.Value },
|
||||
{ @"//.", false, false, @"/", 1, Result.Success },
|
||||
{ @"/ ..", false, false, @"/ ..", 4, Result.Success },
|
||||
{ @"/.. /", false, false, @"/.. ", 4, Result.Success },
|
||||
{ @"/. /.", false, false, @"/. ", 3, Result.Success },
|
||||
{ @"/aa/bb/cc/dd/./.././../..", false, false, @"/aa", 3, Result.Success },
|
||||
{ @"/aa/bb/cc/dd/./.././../../..", false, false, @"/", 1, Result.Success },
|
||||
{ @"/./aa/./bb/./cc/./dd/.", false, false, @"/aa/bb/cc/dd", 12, Result.Success },
|
||||
{ @"/aa\bb/cc", false, false, @"/aa\bb/cc", 9, Result.Success },
|
||||
{ @"/aa\bb/cc", false, false, @"/aa\bb/cc", 9, Result.Success },
|
||||
{ @"/a|/bb/cc", false, false, @"/a|/bb/cc", 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/>a/bb/cc", false, false, @"/>a/bb/cc", 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/aa/.</cc", false, false, @"/aa/.</cc", 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/aa/..</cc", false, false, @"/aa/..</cc", 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, false, @"/aa/cc", 6, Result.Success },
|
||||
{ @"/aa/bb\..\cc", false, false, @"/aa/cc", 6, Result.Success },
|
||||
{ @"/aa/bb\..", false, false, @"/aa", 3, Result.Success },
|
||||
{ @"/aa\bb/../cc", false, false, @"/cc", 3, Result.Success }
|
||||
{ @"/aa/bb/c/", false, true, false, @"/aa/bb/c", 8, Result.Success },
|
||||
{ @"aa/bb/c/", false, false, false, @"", 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"aa/bb/c/", false, true, false, @"/aa/bb/c", 8, Result.Success },
|
||||
{ @"mount:a/b", false, true, false, @"/", 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"mo|unt:a/b", false, true, true, @"/mo|unt:a/b", 11, Result.Success },
|
||||
{ @"/aa/bb/../..", true, false, false, @"/", 1, Result.Success },
|
||||
{ @"/aa/bb/../../..", true, false, false, @"/", 1, Result.Success },
|
||||
{ @"/aa/bb/../../..", false, false, false, @"/aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value },
|
||||
{ @"aa/bb/../../..", true, true, false, @"/", 1, Result.Success },
|
||||
{ @"aa/bb/../../..", false, true, false, @"/aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value },
|
||||
{ @"mount:a/b", false, true, true, @"/mount:a/b", 10, Result.Success },
|
||||
{ @"/a|/bb/cc", false, false, true, @"/a|/bb/cc", 9, Result.Success },
|
||||
{ @"/>a/bb/cc", false, false, true, @"/>a/bb/cc", 9, Result.Success },
|
||||
{ @"/aa/.</cc", false, false, true, @"/aa/.</cc", 9, Result.Success },
|
||||
{ @"/aa/..</cc", false, false, true, @"/aa/..</cc", 10, Result.Success },
|
||||
{ @"", false, false, false, @"", 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"/", false, false, false, @"/", 1, Result.Success },
|
||||
{ @"/.", false, false, false, @"/", 1, Result.Success },
|
||||
{ @"/./", false, false, false, @"/", 1, Result.Success },
|
||||
{ @"/..", false, false, false, @"/", 0, ResultFs.DirectoryUnobtainable.Value },
|
||||
{ @"//.", false, false, false, @"/", 1, Result.Success },
|
||||
{ @"/ ..", false, false, false, @"/ ..", 4, Result.Success },
|
||||
{ @"/.. /", false, false, false, @"/.. ", 4, Result.Success },
|
||||
{ @"/. /.", false, false, false, @"/. ", 3, Result.Success },
|
||||
{ @"/aa/bb/cc/dd/./.././../..", false, false, false, @"/aa", 3, Result.Success },
|
||||
{ @"/aa/bb/cc/dd/./.././../../..", false, false, false, @"/", 1, Result.Success },
|
||||
{ @"/./aa/./bb/./cc/./dd/.", false, false, false, @"/aa/bb/cc/dd", 12, Result.Success },
|
||||
{ @"/aa\bb/cc", false, false, false, @"/aa\bb/cc", 9, Result.Success },
|
||||
{ @"/aa\bb/cc", false, false, false, @"/aa\bb/cc", 9, Result.Success },
|
||||
{ @"/a|/bb/cc", false, false, false, @"/", 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/>a/bb/cc", false, false, false, @"/", 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/aa/.</cc", false, false, false, @"/aa/", 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/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))]
|
||||
public static void Normalize(string path, bool isWindowsPath, bool isDriveRelativePath, string expectedNormalized,
|
||||
long expectedLength, Result expectedResult)
|
||||
public static void Normalize(string path, bool isWindowsPath, bool isDriveRelativePath, bool allowAllCharacters,
|
||||
string expectedNormalized, long expectedLength, Result expectedResult)
|
||||
{
|
||||
byte[] buffer = new byte[0x301];
|
||||
|
||||
Result result = PathNormalizer.Normalize(buffer, out int normalizedLength, path.ToU8Span(), isWindowsPath,
|
||||
isDriveRelativePath);
|
||||
isDriveRelativePath, allowAllCharacters);
|
||||
|
||||
Assert.Equal(expectedResult, result);
|
||||
Assert.Equal(expectedNormalized, StringUtils.Utf8ZToString(buffer));
|
||||
@ -93,47 +99,53 @@ public class PathNormalizerTests
|
||||
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, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"aa/bb/c/", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"mount:a/b", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"/aa/bb/../..", false, 0, Result.Success },
|
||||
{ @"/aa/bb/../../..", false, 0, Result.Success },
|
||||
{ @"/aa/bb/../../..", false, 0, Result.Success },
|
||||
{ @"aa/bb/../../..", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"aa/bb/../../..", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"/", true, 1, Result.Success },
|
||||
{ @"/.", false, 2, Result.Success },
|
||||
{ @"/./", false, 0, Result.Success },
|
||||
{ @"/..", false, 3, Result.Success },
|
||||
{ @"//.", false, 0, Result.Success },
|
||||
{ @"/ ..", true, 4, Result.Success },
|
||||
{ @"/.. /", false, 5, Result.Success },
|
||||
{ @"/. /.", false, 5, Result.Success },
|
||||
{ @"/aa/bb/cc/dd/./.././../..", false, 0, Result.Success },
|
||||
{ @"/aa/bb/cc/dd/./.././../../..", false, 0, Result.Success },
|
||||
{ @"/./aa/./bb/./cc/./dd/.", false, 0, Result.Success },
|
||||
{ @"/aa\bb/cc", true, 9, Result.Success },
|
||||
{ @"/aa\bb/cc", true, 9, Result.Success },
|
||||
{ @"/a|/bb/cc", false, 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/>a/bb/cc", false, 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/aa/.</cc", false, 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/aa/..</cc", false, 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"\\aa/bb/cc", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"\\aa\bb\cc", false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"/aa/bb/..\cc", true, 12, Result.Success },
|
||||
{ @"/aa/bb\..\cc", true, 12, Result.Success },
|
||||
{ @"/aa/bb\..", true, 9, Result.Success },
|
||||
{ @"/aa\bb/../cc", false, 0, Result.Success }
|
||||
{ @"/aa/bb/c/", false, false, 9, Result.Success },
|
||||
{ @"aa/bb/c/", false, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"aa/bb/c/", false, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"mount:a/b", false, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"mo|unt:a/b", true, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"/aa/bb/../..", false, false, 0, Result.Success },
|
||||
{ @"/aa/bb/../../..", false, false, 0, Result.Success },
|
||||
{ @"/aa/bb/../../..", false, false, 0, Result.Success },
|
||||
{ @"aa/bb/../../..", false, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"aa/bb/../../..", false, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"mount:a/b", true, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"/a|/bb/cc", true, true, 9, Result.Success },
|
||||
{ @"/>a/bb/cc", true, true, 9, Result.Success },
|
||||
{ @"/aa/.</cc", true, true, 9, Result.Success },
|
||||
{ @"/aa/..</cc", true, true, 10, Result.Success },
|
||||
{ @"", false, false, 0, ResultFs.InvalidPathFormat.Value },
|
||||
{ @"/", false, true, 1, Result.Success },
|
||||
{ @"/.", false, false, 2, Result.Success },
|
||||
{ @"/./", false, false, 0, Result.Success },
|
||||
{ @"/..", false, false, 3, Result.Success },
|
||||
{ @"//.", false, false, 0, Result.Success },
|
||||
{ @"/ ..", false, true, 4, Result.Success },
|
||||
{ @"/.. /", false, false, 5, Result.Success },
|
||||
{ @"/. /.", false, false, 5, Result.Success },
|
||||
{ @"/aa/bb/cc/dd/./.././../..", false, false, 0, Result.Success },
|
||||
{ @"/aa/bb/cc/dd/./.././../../..", false, false, 0, Result.Success },
|
||||
{ @"/./aa/./bb/./cc/./dd/.", false, false, 0, Result.Success },
|
||||
{ @"/aa\bb/cc", false, true, 9, Result.Success },
|
||||
{ @"/aa\bb/cc", false, true, 9, Result.Success },
|
||||
{ @"/a|/bb/cc", false, false, 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/>a/bb/cc", false, false, 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/aa/.</cc", false, false, 0, ResultFs.InvalidCharacter.Value },
|
||||
{ @"/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))]
|
||||
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(expectedLength, length);
|
||||
|
Loading…
x
Reference in New Issue
Block a user