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

View file

@ -437,7 +437,40 @@ namespace LibHac.Fs
return Result.Success; 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) out long mountNameLength, U8Span path)
{ {
pathAfterMount = default; pathAfterMount = default;

View file

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

View file

@ -18,6 +18,8 @@ namespace nn::fs::PathTool {
// SDK >= 7 // SDK >= 7
nn::Result Normalize(char* buffer, uint64_t* outNormalizedPathLength, const char* path, uint64_t bufferLength, bool preserveUnc, bool hasMountName); 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); 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) 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]; char normalized[0x200];
uint64_t normalizedLen = 0; uint64_t normalizedLen = 0;
memset(normalized, 0, 0x200); memset(normalized, 0, 0x200);
@ -73,7 +75,7 @@ void CreateNormalizeTestData(char const* path, bool preserveUnc, bool hasMountNa
path, preserveUncStr, hasMountNameStr, normalized, normalizedLen, GetResultName(result)); 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; bool isNormalized = false;
nn::Result result = nn::fs::PathTool::IsNormalized(&isNormalized, path, preserveUnc, hasMountName); 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)); 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]; char parentPath[0x200];
memset(parentPath, 0, sizeof(parentPath)); 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)) { void CreateTestItemWithParentDirs(char const* path, bool preserveUnc, bool hasMountName, void (*func)(char const*, bool, bool)) {
CreateTestDataWithParentDirs(path, preserveUnc, hasMountName, func, 3); 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'; Buf[0] = '\n';
BufPos = 1; BufPos = 1;
@ -190,90 +200,116 @@ void CreateTestData(void (*func)(char const*, bool, bool)) {
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
preserveUnc = (bool)i; preserveUnc = (bool)i;
CreateTestDataWithParentDirs("//$abc/bb", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("//$abc/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//:abc/bb", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("//:abc/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("\\\\\\asd", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("\\\\\\asd", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("\\\\/asd", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("\\\\/asd", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("\\\\//asd", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("\\\\//asd", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("//", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a/b/cc/../d", preserveUnc, false, func); CreateTestItemWithParentDirs("\\\\a/b/cc/../d", preserveUnc, false, func);
CreateTestDataWithParentDirs("c:/aa/bb", preserveUnc, true, func); CreateTestItemWithParentDirs("c:/aa/bb", preserveUnc, true, func);
CreateTestDataWithParentDirs("mount:\\c:/aa", preserveUnc, true, func); CreateTestItemWithParentDirs("mount:\\c:/aa", preserveUnc, true, func);
CreateTestDataWithParentDirs("mount:/c:\\aa/bb", preserveUnc, true, func); CreateTestItemWithParentDirs("mount:/c:\\aa/bb", preserveUnc, true, func);
CreateTestDataWithParentDirs("mount:////c:\\aa/bb", preserveUnc, true, func); CreateTestItemWithParentDirs("mount:////c:\\aa/bb", preserveUnc, true, func);
CreateTestDataWithParentDirs("mount:/\\aa/bb", preserveUnc, true, func); CreateTestItemWithParentDirs("mount:/\\aa/bb", preserveUnc, true, func);
CreateTestDataWithParentDirs("mount:/c:/aa/bb", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("mount:/c:/aa/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("mount:c:/aa/bb", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("mount:c:/aa/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("mount:c:/aa/bb", preserveUnc, true, func, 0); CreateTestItemWithParentDirs("mount:c:/aa/bb", preserveUnc, true, func, 0);
CreateTestDataWithParentDirs("mount:/\\aa/../b", preserveUnc, true, func, 2); CreateTestItemWithParentDirs("mount:/\\aa/../b", preserveUnc, true, func, 2);
CreateTestDataWithParentDirs("mount://aa/bb", preserveUnc, true, func, 1); CreateTestItemWithParentDirs("mount://aa/bb", preserveUnc, true, func, 1);
CreateTestDataWithParentDirs("//aa/bb", preserveUnc, true, func, 1); CreateTestItemWithParentDirs("//aa/bb", preserveUnc, true, func, 1);
CreateTestDataWithParentDirs("//aa/bb", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("//aa/bb", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("/aa/bb", preserveUnc, false, func); CreateTestItemWithParentDirs("/aa/bb", preserveUnc, false, func);
CreateTestDataWithParentDirs("c:/aa", preserveUnc, false, func, 2); CreateTestItemWithParentDirs("c:/aa", preserveUnc, false, func, 2);
CreateTestDataWithParentDirs("c:abcde/aa/bb", preserveUnc, false, func); CreateTestItemWithParentDirs("c:abcde/aa/bb", preserveUnc, false, func);
CreateTestDataWithParentDirs("c:abcde", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("c:abcde", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("c:abcde/", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("c:abcde/", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("///aa", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("///aa", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa//bb", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("//aa//bb", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//./bb", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("//./bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//../bb", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("//../bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//.../bb", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("//.../bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa$abc/bb", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("//aa$abc/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa$/bb", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("//aa$/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa:/bb", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("//aa:/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa/bb$b/cc$", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("//aa/bb$b/cc$", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa/bb/cc$c", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("//aa/bb/cc$c", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//aa/bb/cc$c/dd", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("//aa/bb/cc$c/dd", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa/bb", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("//aa/bb", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa/bb/cc//dd", preserveUnc, false, func); CreateTestItemWithParentDirs("//aa/bb/cc//dd", preserveUnc, false, func);
CreateTestDataWithParentDirs("//aa/bb/cc\\/dd", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("//aa/bb/cc\\/dd", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa/bb/cc//dd", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("//aa/bb/cc//dd", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa/bb/cc/dd", preserveUnc, false, func); CreateTestItemWithParentDirs("//aa/bb/cc/dd", preserveUnc, false, func);
CreateTestDataWithParentDirs("//aa/bb/cc/\\dd", preserveUnc, false, func); CreateTestItemWithParentDirs("//aa/bb/cc/\\dd", preserveUnc, false, func);
CreateTestDataWithParentDirs("//aa/../", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("//aa/../", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa//", preserveUnc, false, func, 0); CreateTestItemWithParentDirs("//aa//", preserveUnc, false, func, 0);
CreateTestDataWithParentDirs("//aa/bb..", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("//aa/bb..", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//aa/bb../", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("//aa/bb../", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("/\\\\aa/bb/cc/..", preserveUnc, true, func); CreateTestItemWithParentDirs("/\\\\aa/bb/cc/..", preserveUnc, true, func);
CreateTestDataWithParentDirs("c:aa\\bb/cc", preserveUnc, false, func); CreateTestItemWithParentDirs("c:aa\\bb/cc", preserveUnc, false, func);
CreateTestDataWithParentDirs("c:\\//\\aa\\bb", preserveUnc, false, func, 1); 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); CreateTestItemWithParentDirs("//", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//a", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("//a", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//a", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("//a", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//a/", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("//a/", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//a/b", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("//a/b", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//a/b/", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("//a/b/", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("//a/b/c", preserveUnc, false, func, 2); CreateTestItemWithParentDirs("//a/b/c", preserveUnc, false, func, 2);
CreateTestDataWithParentDirs("//a/b/c/", preserveUnc, false, func, 2); CreateTestItemWithParentDirs("//a/b/c/", preserveUnc, false, func, 2);
CreateTestDataWithParentDirs("\\\\", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("\\\\", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("\\\\a", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a/", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("\\\\a/", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a/b", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("\\\\a/b", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a/b/", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("\\\\a/b/", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a/b/c", preserveUnc, false, func, 2); CreateTestItemWithParentDirs("\\\\a/b/c", preserveUnc, false, func, 2);
CreateTestDataWithParentDirs("\\\\a/b/c/", preserveUnc, false, func, 2); CreateTestItemWithParentDirs("\\\\a/b/c/", preserveUnc, false, func, 2);
CreateTestDataWithParentDirs("\\\\", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("\\\\", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("\\\\a", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a\\", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("\\\\a\\", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a\\b", preserveUnc, false, func, 1); CreateTestItemWithParentDirs("\\\\a\\b", preserveUnc, false, func, 1);
CreateTestDataWithParentDirs("\\\\a\\b\\", preserveUnc, false, func, 1); // "\\a\b\/../.." crashes nnsdk CreateTestItemWithParentDirs("\\\\a\\b\\", preserveUnc, false, func, 1); // "\\a\b\/../.." crashes nnsdk
CreateTestDataWithParentDirs("\\\\a\\b\\c", preserveUnc, false, func, 2); CreateTestItemWithParentDirs("\\\\a\\b\\c", preserveUnc, false, func, 2);
CreateTestDataWithParentDirs("\\\\a\\b\\c\\", preserveUnc, false, func, 2); CreateTestItemWithParentDirs("\\\\a\\b\\c\\", preserveUnc, false, func, 2);
} }
svcOutputDebugString(Buf, BufPos); 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) { extern "C" void nnMain(void) {
//setAllocators(); //setAllocators();
nn::fs::detail::IsEnabledAccessLog(); // Adds the sdk version to the output when not calling setAllocators nn::fs::detail::IsEnabledAccessLog(); // Adds the sdk version to the output when not calling setAllocators
CreateTestData(CreateNormalizeTestData); CreateNormalizationTestData(CreateNormalizeTestItem);
CreateTestData(CreateIsNormalizedTestData); 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 = public static object[][] NormalizeTestItems =
{ {
new object[] {@"", false, false, @"", 0, ResultFs.InvalidPathFormat.Value}, 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},
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}
};
} }
} }