https://git.reactos.org/?p=reactos.git;a=commitdiff;h=264aaa9e05c8dc68561852...
commit 264aaa9e05c8dc68561852c738b89cb8061250fe Author: Mark Jansen mark.jansen@reactos.org AuthorDate: Mon Feb 15 20:11:49 2021 +0100 Commit: Mark Jansen mark.jansen@reactos.org CommitDate: Sat May 8 19:24:23 2021 +0200
[RTL] Implement RtlGetLengthWithoutLastFullDorOrNtPathElement
CORE-17248 --- modules/rostests/apitests/ntdll/CMakeLists.txt | 1 + ...RtlGetLengthWithoutLastFullDosOrNtPathElement.c | 109 +++++++++++++++++++++ modules/rostests/apitests/ntdll/testlist.c | 2 + sdk/lib/rtl/path.c | 86 +++++++++++++++- 4 files changed, 195 insertions(+), 3 deletions(-)
diff --git a/modules/rostests/apitests/ntdll/CMakeLists.txt b/modules/rostests/apitests/ntdll/CMakeLists.txt index 545d935e144..85dba02c31d 100644 --- a/modules/rostests/apitests/ntdll/CMakeLists.txt +++ b/modules/rostests/apitests/ntdll/CMakeLists.txt @@ -62,6 +62,7 @@ list(APPEND SOURCE RtlGetFullPathName_U.c RtlGetFullPathName_Ustr.c RtlGetFullPathName_UstrEx.c + RtlGetLengthWithoutLastFullDosOrNtPathElement.c RtlGetLengthWithoutTrailingPathSeperators.c RtlGetLongestNtPathLength.c RtlGetNtProductType.c diff --git a/modules/rostests/apitests/ntdll/RtlGetLengthWithoutLastFullDosOrNtPathElement.c b/modules/rostests/apitests/ntdll/RtlGetLengthWithoutLastFullDosOrNtPathElement.c new file mode 100644 index 00000000000..d6d7c0dff9b --- /dev/null +++ b/modules/rostests/apitests/ntdll/RtlGetLengthWithoutLastFullDosOrNtPathElement.c @@ -0,0 +1,109 @@ +/* + * PROJECT: ReactOS API Tests + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Test for RtlGetLengthWithoutLastFullDosOrNtPathElement + * COPYRIGHT: Copyright 2021 Mark Jansen mark.jansen@reactos.org + */ + +#include "precomp.h" + + +NTSTATUS +NTAPI +RtlGetLengthWithoutLastFullDosOrNtPathElement( + IN ULONG Flags, + IN PCUNICODE_STRING Path, + OUT PULONG LengthOut); + + +typedef struct _rtl_test_data +{ + LPCWSTR Path; + ULONG Length; + NTSTATUS Status; +} rtl_test_data; + +// Based on http://undoc.airesoft.co.uk/ntdll.dll/RtlGetLengthWithoutLastFullDosOrNtPath... +rtl_test_data tests[] = { + { L"", 0, STATUS_SUCCESS, }, + { L"C", 0, STATUS_INVALID_PARAMETER, }, + { L"C:", 0, STATUS_INVALID_PARAMETER, }, + { L"C:\", 0, STATUS_SUCCESS, }, + { L"C:\test", 3, STATUS_SUCCESS, }, + { L"C:\test\", 3, STATUS_SUCCESS, }, + { L"C:\test\a", 8, STATUS_SUCCESS, }, + { L"C:\test\a\", 8, STATUS_SUCCESS, }, + { L"C://test", 3, STATUS_SUCCESS, }, + { L"C://test\", 3, STATUS_SUCCESS, }, + { L"C://test\\", 3, STATUS_SUCCESS, }, + { L"C://test/", 3, STATUS_SUCCESS, }, + { L"C://test//", 3, STATUS_SUCCESS, }, + { L"C://test\a", 9, STATUS_SUCCESS, }, + { L"C://test\\a", 9, STATUS_SUCCESS, }, + { L"C://test/a", 9, STATUS_SUCCESS, }, + { L"C://test//a", 9, STATUS_SUCCESS, }, + { L"C://test\a\", 9, STATUS_SUCCESS, }, + { L"C://test//a//", 9, STATUS_SUCCESS, }, + { L"C://test//a/", 9, STATUS_SUCCESS, }, + { L"X", 0, STATUS_INVALID_PARAMETER, }, + { L"X:", 0, STATUS_INVALID_PARAMETER, }, + { L"X:\", 0, STATUS_SUCCESS, }, + { L"D:\Test\hello.ext", 8, STATUS_SUCCESS, }, + { L"\\?\C", 0, STATUS_INVALID_PARAMETER, }, + { L"\\?\C:", 0, STATUS_INVALID_PARAMETER, }, + { L"\\?\CC", 0, STATUS_INVALID_PARAMETER, }, + { L"\\?\C:\", 4, STATUS_SUCCESS, }, + { L"\\?\::\", 4, STATUS_SUCCESS, }, + { L"\\?\CCC", 0, STATUS_INVALID_PARAMETER, }, + { L"\\?\CCC\", 0, STATUS_INVALID_PARAMETER, }, + { L"\??\UNC\Mytest", 8, STATUS_SUCCESS, }, + { L"\SystemRoot", 0, STATUS_SUCCESS, }, + { L"\SystemRoot\", 0, STATUS_SUCCESS, }, + { L"\SystemRoot\ntdll.dll", 12, STATUS_SUCCESS, }, + { L"\Device\HarddiskVolume9000", 8, STATUS_SUCCESS, }, + { L"\Stuff\doesnt\really\matter", 21, STATUS_SUCCESS, }, + { L"this\doesnt\really\work", 0, STATUS_INVALID_PARAMETER, }, + { L"multi(0)disk(0)rdisk(0)partition(1)", 0, STATUS_INVALID_PARAMETER, }, + { L"multi(0)disk(0)rdisk(0)partition(1)\test", 0, STATUS_INVALID_PARAMETER, }, + { L"xyz", 0, STATUS_INVALID_PARAMETER, }, + { L"CON", 0, STATUS_INVALID_PARAMETER, }, + { L":", 0, STATUS_INVALID_PARAMETER, }, + { L"\\", 0, STATUS_SUCCESS, }, +}; + + +START_TEST(RtlGetLengthWithoutLastFullDosOrNtPathElement) +{ + UNICODE_STRING Dum; + NTSTATUS Status; + ULONG Length; + RtlInitUnicodeString(&Dum, L"c:\test\"); + + Length = 333; + Status = RtlGetLengthWithoutLastFullDosOrNtPathElement(0, NULL, &Length); + ok_int(Length, 0); + ok_hex(Status, STATUS_INVALID_PARAMETER); + + Status = RtlGetLengthWithoutLastFullDosOrNtPathElement(0, &Dum, NULL); + ok_hex(Status, STATUS_INVALID_PARAMETER); + + for (ULONG n = 0; n < 32; ++n) + { + Length = 333; + Status = RtlGetLengthWithoutLastFullDosOrNtPathElement((1 << n), &Dum, &Length); + ok_int(Length, 0); + ok_hex(Status, STATUS_INVALID_PARAMETER); + } + + for (ULONG n = 0; n < ARRAYSIZE(tests); ++n) + { + UNICODE_STRING Str; + Length = 333; + + RtlInitUnicodeString(&Str, tests[n].Path); + + Status = RtlGetLengthWithoutLastFullDosOrNtPathElement(0, &Str, &Length); + ok(Status == tests[n].Status, "Got Status=0x%lx, expected 0x%lx (%S)\n", Status, tests[n].Status, Str.Buffer); + ok(Length == tests[n].Length, "Got Length=0x%lx, expected 0x%lx (%S)\n", Length, tests[n].Length, Str.Buffer); + } +} diff --git a/modules/rostests/apitests/ntdll/testlist.c b/modules/rostests/apitests/ntdll/testlist.c index 7c9cdb2f694..141a1305f61 100644 --- a/modules/rostests/apitests/ntdll/testlist.c +++ b/modules/rostests/apitests/ntdll/testlist.c @@ -59,6 +59,7 @@ extern void func_RtlGenerate8dot3Name(void); extern void func_RtlGetFullPathName_U(void); extern void func_RtlGetFullPathName_Ustr(void); extern void func_RtlGetFullPathName_UstrEx(void); +extern void func_RtlGetLengthWithoutLastFullDosOrNtPathElement(void); extern void func_RtlGetLengthWithoutTrailingPathSeperators(void); extern void func_RtlGetLongestNtPathLength(void); extern void func_RtlGetNtProductType(void); @@ -138,6 +139,7 @@ const struct test winetest_testlist[] = { "RtlGetFullPathName_U", func_RtlGetFullPathName_U }, { "RtlGetFullPathName_Ustr", func_RtlGetFullPathName_Ustr }, { "RtlGetFullPathName_UstrEx", func_RtlGetFullPathName_UstrEx }, + { "RtlGetLengthWithoutLastFullDosOrNtPathElement", func_RtlGetLengthWithoutLastFullDosOrNtPathElement }, { "RtlGetLengthWithoutTrailingPathSeperators", func_RtlGetLengthWithoutTrailingPathSeperators }, { "RtlGetLongestNtPathLength", func_RtlGetLongestNtPathLength }, { "RtlGetNtProductType", func_RtlGetNtProductType }, diff --git a/sdk/lib/rtl/path.c b/sdk/lib/rtl/path.c index fb28160141b..360c68df735 100644 --- a/sdk/lib/rtl/path.c +++ b/sdk/lib/rtl/path.c @@ -494,11 +494,91 @@ RtlpApplyLengthFunction(IN ULONG Flags, NTSTATUS NTAPI RtlGetLengthWithoutLastFullDosOrNtPathElement(IN ULONG Flags, - IN PWCHAR Path, + IN PCUNICODE_STRING Path, OUT PULONG LengthOut) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + static const UNICODE_STRING PathDividers = RTL_CONSTANT_STRING(L"\/"); + USHORT Position; + RTL_PATH_TYPE PathType; + + /* All failure paths have this in common, so simplify code */ + if (LengthOut) + *LengthOut = 0; + + if (Flags || !Path || !LengthOut) + { + return STATUS_INVALID_PARAMETER; + } + + if ((Path->Length / sizeof(WCHAR)) == 0) + { + /* Nothing to do here */ + return STATUS_SUCCESS; + } + + + PathType = RtlDetermineDosPathNameType_Ustr(Path); + switch (PathType) + { + case RtlPathTypeLocalDevice: + // Handle \\?\C:\ with the last ':' or '\' missing: + if (Path->Length / sizeof(WCHAR) < 7 || + Path->Buffer[5] != ':' || + !IS_PATH_SEPARATOR(Path->Buffer[6])) + { + return STATUS_INVALID_PARAMETER; + } + break; + case RtlPathTypeRooted: + // "\??\" + break; + case RtlPathTypeUncAbsolute: + // "\\" + break; + case RtlPathTypeDriveAbsolute: + // "C:\" + break; + default: + return STATUS_INVALID_PARAMETER; + } + + /* Find the last path separator */ + if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END, Path, &PathDividers, &Position))) + Position = 0; + + /* Is it the last char of the string? */ + if (Position && Position + sizeof(WCHAR) == Path->Length) + { + UNICODE_STRING Tmp = *Path; + Tmp.Length = Position; + + /* Keep walking path separators to eliminate multiple next to eachother */ + while (Tmp.Length > sizeof(WCHAR) && IS_PATH_SEPARATOR(Tmp.Buffer[Tmp.Length / sizeof(WCHAR)])) + Tmp.Length -= sizeof(WCHAR); + + /* Find the previous occurence */ + if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END, &Tmp, &PathDividers, &Position))) + Position = 0; + } + + /* Simplify code by working in chars instead of bytes */ + if (Position) + Position /= sizeof(WCHAR); + + if (Position) + { + // Keep walking path separators to eliminate multiple next to eachother, but ensure we leave one in place! + while (Position > 1 && IS_PATH_SEPARATOR(Path->Buffer[Position - 1])) + Position--; + } + + if (Position > 0) + { + /* Return a length, not an index */ + *LengthOut = Position + 1; + } + + return STATUS_SUCCESS; }
NTSTATUS