https://git.reactos.org/?p=reactos.git;a=commitdiff;h=264aaa9e05c8dc68561852c738b89cb8061250fe

commit 264aaa9e05c8dc68561852c738b89cb8061250fe
Author:     Mark Jansen <[email protected]>
AuthorDate: Mon Feb 15 20:11:49 2021 +0100
Commit:     Mark Jansen <[email protected]>
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 <[email protected]>
+ */
+
+#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/RtlGetLengthWithoutLastFullDosOrNtPathElement.php
+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

Reply via email to