https://git.reactos.org/?p=reactos.git;a=commitdiff;h=264aaa9e05c8dc6856185…
commit 264aaa9e05c8dc68561852c738b89cb8061250fe
Author: Mark Jansen <mark.jansen(a)reactos.org>
AuthorDate: Mon Feb 15 20:11:49 2021 +0100
Commit: Mark Jansen <mark.jansen(a)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(a)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/RtlGetLengthWithoutLastFullDosOrNtPat…
+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