Add PathTool.IsSubpath

This commit is contained in:
Alex Barney 2020-02-10 00:31:16 -07:00
parent bc11d7ceaf
commit cb6827e6c2
5 changed files with 188 additions and 87 deletions

View file

@ -1,13 +1,12 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
namespace LibHac.Common
{
[DebuggerDisplay("{ToString()}")]
public ref struct U8Span
public readonly ref struct U8Span
{
private readonly ReadOnlySpan<byte> _buffer;
@ -33,11 +32,10 @@ namespace LibHac.Common
public byte GetOrNull(int i)
{
byte value = 0;
ReadOnlySpan<byte> b = _buffer;
if ((uint)i < (uint)b.Length)
if ((uint)i < (uint)_buffer.Length)
{
value = b[i];
value = GetUnsafe(i);
}
return value;
@ -46,7 +44,11 @@ namespace LibHac.Common
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetUnsafe(int i)
{
#if DEBUG
return _buffer[i];
#else
return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i);
#endif
}
public U8Span Slice(int start)

View file

@ -437,7 +437,40 @@ namespace LibHac.Fs
return Result.Success;
}
public static Result ParseMountName(out U8Span pathAfterMount, Span<byte> outMountNameBuffer,
public static bool IsSubpath(U8Span lhs, U8Span rhs)
{
bool isUncLhs = PathUtility.IsUnc(lhs);
bool isUncRhs = PathUtility.IsUnc(rhs);
if (isUncLhs && !isUncRhs || !isUncLhs && isUncRhs)
return false;
if (IsSeparator(lhs.GetOrNull(0)) && IsNullTerminator(lhs.GetOrNull(1)) &&
IsSeparator(rhs.GetOrNull(0)) && !IsNullTerminator(rhs.GetOrNull(1)))
return true;
if (IsSeparator(rhs.GetOrNull(0)) && IsNullTerminator(rhs.GetOrNull(1)) &&
IsSeparator(lhs.GetOrNull(0)) && !IsNullTerminator(lhs.GetOrNull(1)))
return true;
for (int i = 0; ; i++)
{
if (IsNullTerminator(lhs.GetOrNull(i)))
{
return IsSeparator(rhs.GetOrNull(i));
}
else if (IsNullTerminator(rhs.GetOrNull(i)))
{
return IsSeparator(lhs.GetOrNull(i));
}
else if (lhs.GetOrNull(i) != rhs.GetOrNull(i))
{
return false;
}
}
}
private static Result ParseMountName(out U8Span pathAfterMount, Span<byte> outMountNameBuffer,
out long mountNameLength, U8Span path)
{
pathAfterMount = default;

View file

@ -25,8 +25,8 @@ namespace LibHac.Fs
public static bool IsUnc(U8Span path)
{
return (uint)path.Length > 1 &&
(IsSeparator(path[0]) && IsSeparator(path[1]) ||
IsAltSeparator(path[0]) && IsAltSeparator(path[1]));
(IsSeparator(path.GetUnsafe(0)) && IsSeparator(path.GetUnsafe(1)) ||
IsAltSeparator(path.GetUnsafe(0)) && IsAltSeparator(path.GetUnsafe(1)));
}
}
}

View file

@ -18,6 +18,8 @@ namespace nn::fs::PathTool {
// SDK >= 7
nn::Result Normalize(char* buffer, uint64_t* outNormalizedPathLength, const char* path, uint64_t bufferLength, bool preserveUnc, bool hasMountName);
nn::Result IsNormalized(bool* outIsNormalized, const char* path, bool preserveUnc, bool hasMountName);
bool IsSubpath(const char* path1, const char* path2);
}
void* allocate(size_t size)
@ -58,7 +60,7 @@ const char* GetResultName(nn::Result result) {
}
}
void CreateNormalizeTestData(char const* path, bool preserveUnc, bool hasMountName) {
void CreateNormalizeTestItem(char const* path, bool preserveUnc, bool hasMountName) {
char normalized[0x200];
uint64_t normalizedLen = 0;
memset(normalized, 0, 0x200);
@ -73,7 +75,7 @@ void CreateNormalizeTestData(char const* path, bool preserveUnc, bool hasMountNa
path, preserveUncStr, hasMountNameStr, normalized, normalizedLen, GetResultName(result));
}
void CreateIsNormalizedTestData(char const* path, bool preserveUnc, bool hasMountName) {
void CreateIsNormalizedTestItem(char const* path, bool preserveUnc, bool hasMountName) {
bool isNormalized = false;
nn::Result result = nn::fs::PathTool::IsNormalized(&isNormalized, path, preserveUnc, hasMountName);
@ -85,7 +87,15 @@ void CreateIsNormalizedTestData(char const* path, bool preserveUnc, bool hasMoun
path, preserveUncStr, hasMountNameStr, isNormalizedStr, GetResultName(result));
}
void CreateTestDataWithParentDirs(char const* path, bool preserveUnc, bool hasMountName, void (*func)(char const*, bool, bool), int parentCount) {
void CreateIsSubpathTestItem(const char* path1, const char* path2) {
bool result = nn::fs::PathTool::IsSubpath(path1, path2);
const char* resultStr = result ? "true" : "false";
BufPos += sprintf(&Buf[BufPos], "new object[] {@\"%s\", @\"%s\", %s},\n",
path1, path2, resultStr);
}
void CreateTestItemWithParentDirs(char const* path, bool preserveUnc, bool hasMountName, void (*func)(char const*, bool, bool), int parentCount) {
char parentPath[0x200];
memset(parentPath, 0, sizeof(parentPath));
@ -98,11 +108,11 @@ void CreateTestDataWithParentDirs(char const* path, bool preserveUnc, bool hasMo
}
}
void CreateTestDataWithParentDirs(char const* path, bool preserveUnc, bool hasMountName, void (*func)(char const*, bool, bool)) {
CreateTestDataWithParentDirs(path, preserveUnc, hasMountName, func, 3);
void CreateTestItemWithParentDirs(char const* path, bool preserveUnc, bool hasMountName, void (*func)(char const*, bool, bool)) {
CreateTestItemWithParentDirs(path, preserveUnc, hasMountName, func, 3);
}
void CreateTestData(void (*func)(char const*, bool, bool)) {
void CreateNormalizationTestData(void (*func)(char const*, bool, bool)) {
Buf[0] = '\n';
BufPos = 1;
@ -190,90 +200,116 @@ void CreateTestData(void (*func)(char const*, bool, bool)) {
for (int i = 0; i < 2; i++) {
preserveUnc = (bool)i;
CreateTestDataWithParentDirs("//$abc/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//:abc/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("\\\\\\asd", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("\\\\/asd", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("\\\\//asd", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a/b/cc/../d", preserveUnc, false, func);
CreateTestDataWithParentDirs("c:/aa/bb", preserveUnc, true, func);
CreateTestDataWithParentDirs("mount:\\c:/aa", preserveUnc, true, func);
CreateTestDataWithParentDirs("mount:/c:\\aa/bb", preserveUnc, true, func);
CreateTestDataWithParentDirs("mount:////c:\\aa/bb", preserveUnc, true, func);
CreateTestDataWithParentDirs("mount:/\\aa/bb", preserveUnc, true, func);
CreateTestDataWithParentDirs("mount:/c:/aa/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("mount:c:/aa/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("mount:c:/aa/bb", preserveUnc, true, func, 0);
CreateTestDataWithParentDirs("mount:/\\aa/../b", preserveUnc, true, func, 2);
CreateTestDataWithParentDirs("mount://aa/bb", preserveUnc, true, func, 1);
CreateTestDataWithParentDirs("//aa/bb", preserveUnc, true, func, 1);
CreateTestDataWithParentDirs("//aa/bb", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("/aa/bb", preserveUnc, false, func);
CreateTestDataWithParentDirs("c:/aa", preserveUnc, false, func, 2);
CreateTestDataWithParentDirs("c:abcde/aa/bb", preserveUnc, false, func);
CreateTestDataWithParentDirs("c:abcde", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("c:abcde/", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("///aa", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa//bb", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//./bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//../bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//.../bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa$abc/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa$/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa:/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa/bb$b/cc$", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa/bb/cc$c", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//aa/bb/cc$c/dd", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa/bb/cc//dd", preserveUnc, false, func);
CreateTestDataWithParentDirs("//aa/bb/cc\\/dd", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa/bb/cc//dd", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa/bb/cc/dd", preserveUnc, false, func);
CreateTestDataWithParentDirs("//aa/bb/cc/\\dd", preserveUnc, false, func);
CreateTestDataWithParentDirs("//aa/../", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa//", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa/bb..", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//aa/bb../", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("/\\\\aa/bb/cc/..", preserveUnc, true, func);
CreateTestItemWithParentDirs("//$abc/bb", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//:abc/bb", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("\\\\\\asd", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("\\\\/asd", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("\\\\//asd", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("\\\\a/b/cc/../d", preserveUnc, false, func);
CreateTestItemWithParentDirs("c:/aa/bb", preserveUnc, true, func);
CreateTestItemWithParentDirs("mount:\\c:/aa", preserveUnc, true, func);
CreateTestItemWithParentDirs("mount:/c:\\aa/bb", preserveUnc, true, func);
CreateTestItemWithParentDirs("mount:////c:\\aa/bb", preserveUnc, true, func);
CreateTestItemWithParentDirs("mount:/\\aa/bb", preserveUnc, true, func);
CreateTestItemWithParentDirs("mount:/c:/aa/bb", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("mount:c:/aa/bb", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("mount:c:/aa/bb", preserveUnc, true, func, 0);
CreateTestItemWithParentDirs("mount:/\\aa/../b", preserveUnc, true, func, 2);
CreateTestItemWithParentDirs("mount://aa/bb", preserveUnc, true, func, 1);
CreateTestItemWithParentDirs("//aa/bb", preserveUnc, true, func, 1);
CreateTestItemWithParentDirs("//aa/bb", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("/aa/bb", preserveUnc, false, func);
CreateTestItemWithParentDirs("c:/aa", preserveUnc, false, func, 2);
CreateTestItemWithParentDirs("c:abcde/aa/bb", preserveUnc, false, func);
CreateTestItemWithParentDirs("c:abcde", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("c:abcde/", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("///aa", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//aa//bb", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("//./bb", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//../bb", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//.../bb", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//aa$abc/bb", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//aa$/bb", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//aa:/bb", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//aa/bb$b/cc$", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//aa/bb/cc$c", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("//aa/bb/cc$c/dd", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//aa/bb", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//aa/bb/cc//dd", preserveUnc, false, func);
CreateTestItemWithParentDirs("//aa/bb/cc\\/dd", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//aa/bb/cc//dd", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//aa/bb/cc/dd", preserveUnc, false, func);
CreateTestItemWithParentDirs("//aa/bb/cc/\\dd", preserveUnc, false, func);
CreateTestItemWithParentDirs("//aa/../", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//aa//", preserveUnc, false, func, 0);
CreateTestItemWithParentDirs("//aa/bb..", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("//aa/bb../", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("/\\\\aa/bb/cc/..", preserveUnc, true, func);
CreateTestDataWithParentDirs("c:aa\\bb/cc", preserveUnc, false, func);
CreateTestDataWithParentDirs("c:\\//\\aa\\bb", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("c:aa\\bb/cc", preserveUnc, false, func);
CreateTestItemWithParentDirs("c:\\//\\aa\\bb", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("mount://////a/bb/c", preserveUnc, true, func, 2);
CreateTestItemWithParentDirs("mount://////a/bb/c", preserveUnc, true, func, 2);
CreateTestDataWithParentDirs("//", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//a", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//a", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//a/", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//a/b", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//a/b/", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//a/b/c", preserveUnc, false, func, 2);
CreateTestDataWithParentDirs("//a/b/c/", preserveUnc, false, func, 2);
CreateTestItemWithParentDirs("//", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("//a", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("//a", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("//a/", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("//a/b", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("//a/b/", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("//a/b/c", preserveUnc, false, func, 2);
CreateTestItemWithParentDirs("//a/b/c/", preserveUnc, false, func, 2);
CreateTestDataWithParentDirs("\\\\", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a/", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a/b", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a/b/", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a/b/c", preserveUnc, false, func, 2);
CreateTestDataWithParentDirs("\\\\a/b/c/", preserveUnc, false, func, 2);
CreateTestItemWithParentDirs("\\\\", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("\\\\a", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("\\\\a/", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("\\\\a/b", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("\\\\a/b/", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("\\\\a/b/c", preserveUnc, false, func, 2);
CreateTestItemWithParentDirs("\\\\a/b/c/", preserveUnc, false, func, 2);
CreateTestDataWithParentDirs("\\\\", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a\\", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a\\b", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a\\b\\", preserveUnc, false, func, 1); // "\\a\b\/../.." crashes nnsdk
CreateTestDataWithParentDirs("\\\\a\\b\\c", preserveUnc, false, func, 2);
CreateTestDataWithParentDirs("\\\\a\\b\\c\\", preserveUnc, false, func, 2);
CreateTestItemWithParentDirs("\\\\", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("\\\\a", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("\\\\a\\", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("\\\\a\\b", preserveUnc, false, func, 1);
CreateTestItemWithParentDirs("\\\\a\\b\\", preserveUnc, false, func, 1); // "\\a\b\/../.." crashes nnsdk
CreateTestItemWithParentDirs("\\\\a\\b\\c", preserveUnc, false, func, 2);
CreateTestItemWithParentDirs("\\\\a\\b\\c\\", preserveUnc, false, func, 2);
}
svcOutputDebugString(Buf, BufPos);
}
void CreateSubpathTestData() {
Buf[0] = '\n';
BufPos = 1;
CreateIsSubpathTestItem("//a/b", "/a");
CreateIsSubpathTestItem("/a", "//a/b");
CreateIsSubpathTestItem("//a/b", "\\\\a");
CreateIsSubpathTestItem("//a/b", "//a");
CreateIsSubpathTestItem("/", "/a");
CreateIsSubpathTestItem("/a", "/");
CreateIsSubpathTestItem("/", "/");
CreateIsSubpathTestItem("", "");
CreateIsSubpathTestItem("/", "");
CreateIsSubpathTestItem("/", "mount:/a");
CreateIsSubpathTestItem("mount:/", "mount:/");
CreateIsSubpathTestItem("mount:/a/b", "mount:/a/b");
CreateIsSubpathTestItem("mount:/a/b", "mount:/a/b/c");
CreateIsSubpathTestItem("/a/b", "/a/b/c");
CreateIsSubpathTestItem("/a/b/c", "/a/b");
CreateIsSubpathTestItem("/a/b", "/a/b");
CreateIsSubpathTestItem("/a/b", "/a/b\\c");
svcOutputDebugString(Buf, BufPos);
}
extern "C" void nnMain(void) {
//setAllocators();
nn::fs::detail::IsEnabledAccessLog(); // Adds the sdk version to the output when not calling setAllocators
CreateTestData(CreateNormalizeTestData);
CreateTestData(CreateIsNormalizedTestData);
CreateNormalizationTestData(CreateNormalizeTestItem);
CreateNormalizationTestData(CreateIsNormalizedTestItem);
CreateSubpathTestData();
}

View file

@ -36,6 +36,15 @@ namespace LibHac.Tests.Fs
}
}
[Theory]
[MemberData(nameof(SubpathTestItems))]
public static void IsSubpath(string path1, string path2, bool expectedResult)
{
bool result = PathTool.IsSubpath(path1.ToU8Span(), path2.ToU8Span());
Assert.Equal(expectedResult, result);
}
public static object[][] NormalizeTestItems =
{
new object[] {@"", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
@ -813,5 +822,26 @@ namespace LibHac.Tests.Fs
new object[] {@"\\a\b\c\/..", true, false, false, Result.Success},
new object[] {@"\\a\b\c\/../..", true, false, false, Result.Success}
};
public static object[][] SubpathTestItems =
{
new object[] {@"//a/b", @"/a", false},
new object[] {@"/a", @"//a/b", false},
new object[] {@"//a/b", @"\\a", false},
new object[] {@"//a/b", @"//a", true},
new object[] {@"/", @"/a", true},
new object[] {@"/a", @"/", true},
new object[] {@"/", @"/", false},
new object[] {@"", @"", false},
new object[] {@"/", @"", true},
new object[] {@"/", @"mount:/a", false},
new object[] {@"mount:/", @"mount:/", false},
new object[] {@"mount:/a/b", @"mount:/a/b", false},
new object[] {@"mount:/a/b", @"mount:/a/b/c", true},
new object[] {@"/a/b", @"/a/b/c", true},
new object[] {@"/a/b/c", @"/a/b", true},
new object[] {@"/a/b", @"/a/b", false},
new object[] {@"/a/b", @"/a/b\c", false}
};
}
}