From 33767942e97c0c4a32c1e30dd347f1a69ec5707e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Wed, 8 Mar 2017 02:12:37 +0100
Subject: [PATCH] ntdll: Implement ObjectTypesInformation in NtQueryObject.

---
 dlls/ntdll/tests/om.c  | 57 ++++++++++++++++++++++++++++++++++++++++++
 dlls/ntdll/unix/file.c | 53 +++++++++++++++++++++++++++++++++++++++
 include/winternl.h     | 30 ++++++++++++++++++++--
 server/directory.c     | 19 +++++++++++++-
 server/protocol.def    |  9 +++++++
 5 files changed, 165 insertions(+), 3 deletions(-)

diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c
index fba64b8f0d6..e38e4d633b6 100644
--- a/dlls/ntdll/tests/om.c
+++ b/dlls/ntdll/tests/om.c
@@ -78,6 +78,21 @@ static void     (WINAPI *pRtlWakeAddressSingle)( const void * );
 #define KEYEDEVENT_WAKE       0x0002
 #define KEYEDEVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x0003)
 
+#define ROUND_UP(value, alignment) (((value) + ((alignment) - 1)) & ~((alignment)-1))
+
+static LPCSTR wine_dbgstr_us( const UNICODE_STRING *us )
+{
+    if (!us) return "(null)";
+    return wine_dbgstr_wn(us->Buffer, us->Length / sizeof(WCHAR));
+}
+
+static inline int strncmpW( const WCHAR *str1, const WCHAR *str2, int n )
+{
+    if (n <= 0) return 0;
+    while ((--n > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; }
+    return *str1 - *str2;
+}
+
 static void test_case_sensitive (void)
 {
     NTSTATUS status;
@@ -1495,6 +1510,47 @@ static void test_query_object(void)
     pNtClose(handle);
 }
 
+static void test_query_object_types(void)
+{
+    static const WCHAR typeW[] = {'T','y','p','e'};
+    OBJECT_TYPES_INFORMATION *buffer;
+    OBJECT_TYPE_INFORMATION *type;
+    NTSTATUS status;
+    ULONG len, i;
+
+    buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(OBJECT_TYPES_INFORMATION) );
+    ok( buffer != NULL, "Failed to allocate memory\n" );
+
+    status = pNtQueryObject( NULL, ObjectTypesInformation, buffer, sizeof(OBJECT_TYPES_INFORMATION), &len );
+    ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status );
+    ok( len, "len is zero\n");
+
+    buffer = HeapReAlloc( GetProcessHeap(), 0, buffer, len );
+    ok( buffer != NULL, "Failed to allocate memory\n" );
+
+    memset( buffer, 0, len );
+    status = pNtQueryObject( NULL, ObjectTypesInformation, buffer, len, &len );
+    ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status );
+    ok( buffer->NumberOfTypes, "NumberOfTypes is zero\n" );
+
+    type = (OBJECT_TYPE_INFORMATION *)(buffer + 1);
+    for (i = 0; i < buffer->NumberOfTypes; i++)
+    {
+        USHORT length = type->TypeName.MaximumLength;
+        trace( "Type %u: %s\n", i, wine_dbgstr_us(&type->TypeName) );
+
+        if (i == 0)
+        {
+            todo_wine ok( type->TypeName.Length == sizeof(typeW) && !strncmpW(typeW, type->TypeName.Buffer, 4),
+                "Expected 'Type' as first type, got %s\n", wine_dbgstr_us(&type->TypeName) );
+        }
+
+        type = (OBJECT_TYPE_INFORMATION *)ROUND_UP( (DWORD_PTR)(type + 1) + length, sizeof(DWORD_PTR) );
+    }
+
+    HeapFree( GetProcessHeap(), 0, buffer );
+}
+
 static void test_type_mismatch(void)
 {
     HANDLE h;
@@ -2092,6 +2148,7 @@ START_TEST(om)
     test_directory();
     test_symboliclink();
     test_query_object();
+    test_query_object_types();
     test_type_mismatch();
     test_event();
     test_mutant();
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index 1800cc1f21d..a377ce7bd7b 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -132,6 +132,8 @@
 WINE_DEFAULT_DEBUG_CHANNEL(file);
 WINE_DECLARE_DEBUG_CHANNEL(winediag);
 
+#define ROUND_UP(value, alignment) (((value) + ((alignment) - 1)) & ~((alignment)-1))
+
 #define MAX_DOS_DRIVES 26
 
 #define FILE_WRITE_TO_END_OF_FILE      ((LONGLONG)-1)
@@ -7438,6 +7440,57 @@ NTSTATUS WINAPI NtQueryObject( HANDLE handle, OBJECT_INFORMATION_CLASS info_clas
         break;
     }
 
+    case ObjectTypesInformation:
+    {
+        OBJECT_TYPES_INFORMATION *p = ptr;
+        OBJECT_TYPE_INFORMATION *type = (OBJECT_TYPE_INFORMATION *)(p + 1);
+        ULONG count, type_len, req_len = sizeof(OBJECT_TYPES_INFORMATION);
+
+        for (count = 0, status = STATUS_SUCCESS; !status; count++)
+        {
+            SERVER_START_REQ( get_object_type_by_index )
+            {
+                req->index = count;
+                if (len > sizeof(*type))
+                    wine_server_set_reply( req, type + 1, len - sizeof(*type) );
+                status = wine_server_call( req );
+                if (status == STATUS_SUCCESS)
+                {
+                    type_len = sizeof(*type);
+                    if (reply->total)
+                        type_len += ROUND_UP( reply->total + sizeof(WCHAR), sizeof(DWORD_PTR) );
+                    req_len += type_len;
+                }
+                if (status == STATUS_SUCCESS && len >= req_len)
+                {
+                    ULONG res = wine_server_reply_size( reply );
+                    memset( type, 0, sizeof(*type) );
+                    if (reply->total)
+                    {
+                        type->TypeName.Buffer = (WCHAR *)(type + 1);
+                        type->TypeName.Length = res;
+                        type->TypeName.MaximumLength = res + sizeof(WCHAR);
+                        type->TypeName.Buffer[res / sizeof(WCHAR)] = 0;
+                    }
+                    type->TypeIndex = count;
+                    type = (OBJECT_TYPE_INFORMATION *)((char *)type + type_len);
+                }
+            }
+            SERVER_END_REQ;
+        }
+
+        if (status != STATUS_NO_MORE_ENTRIES)
+            return status;
+
+        if (used_len) *used_len = req_len;
+        if (len < req_len)
+            return STATUS_INFO_LENGTH_MISMATCH;
+
+        p->NumberOfTypes = count - 1;
+        status = STATUS_SUCCESS;
+        break;
+    }
+
     case ObjectDataInformation:
     {
         OBJECT_DATA_INFORMATION* p = ptr;
diff --git a/include/winternl.h b/include/winternl.h
index ac1e0880d59..8a3bdd2b95c 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -1116,7 +1116,7 @@ typedef enum _OBJECT_INFORMATION_CLASS {
     ObjectBasicInformation,
     ObjectNameInformation,
     ObjectTypeInformation,
-    ObjectAllInformation,
+    ObjectTypesInformation,
     ObjectDataInformation
 } OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;
 
@@ -1576,9 +1576,35 @@ typedef struct _OBJECT_NAME_INFORMATION {
 
 typedef struct __OBJECT_TYPE_INFORMATION {
     UNICODE_STRING TypeName;
-    ULONG Reserved [22];
+    ULONG TotalNumberOfObjects;
+    ULONG TotalNumberOfHandles;
+    ULONG TotalPagedPoolUsage;
+    ULONG TotalNonPagedPoolUsage;
+    ULONG TotalNamePoolUsage;
+    ULONG TotalHandleTableUsage;
+    ULONG HighWaterNumberOfObjects;
+    ULONG HighWaterNumberOfHandles;
+    ULONG HighWaterPagedPoolUsage;
+    ULONG HighWaterNonPagedPoolUsage;
+    ULONG HighWaterNamePoolUsage;
+    ULONG HighWaterHandleTableUsage;
+    ULONG InvalidAttributes;
+    GENERIC_MAPPING GenericMapping;
+    ULONG ValidAccessMask;
+    BOOLEAN SecurityRequired;
+    BOOLEAN MaintainHandleCount;
+    UCHAR TypeIndex;
+    CHAR Reserved;
+    ULONG PoolType;
+    ULONG DefaultPagedPoolCharge;
+    ULONG DefaultNonPagedPoolCharge;
 } OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
 
+typedef struct _OBJECT_TYPES_INFORMATION
+{
+    ULONG NumberOfTypes;
+} OBJECT_TYPES_INFORMATION, *POBJECT_TYPES_INFORMATION;
+
 typedef struct _PROCESS_BASIC_INFORMATION {
 #ifdef __WINESRC__
     DWORD_PTR ExitStatus;
diff --git a/server/directory.c b/server/directory.c
index b735602a805..d89da8b169b 100644
--- a/server/directory.c
+++ b/server/directory.c
@@ -73,6 +73,8 @@ static const struct object_ops object_type_ops =
     no_destroy                    /* destroy */
 };
 
+static struct object_type *object_type_list[64];
+static unsigned int object_type_count;
 
 struct directory
 {
@@ -238,7 +240,8 @@ struct object_type *get_object_type( const struct unicode_str *name )
     {
         if (get_error() != STATUS_OBJECT_NAME_EXISTS)
         {
-            grab_object( type );
+            assert( object_type_count < sizeof(object_type_list)/sizeof(object_type_list[0]) );
+            object_type_list[ object_type_count++ ] = (struct object_type *)grab_object( type );
             make_object_static( &type->obj );
         }
         clear_error();
@@ -553,3 +556,17 @@ DECL_HANDLER(get_object_type)
     }
     release_object( obj );
 }
+
+/* query object type name information by index */
+DECL_HANDLER(get_object_type_by_index)
+{
+    struct object_type *type;
+    const WCHAR *name;
+
+    if (req->index < object_type_count && (type = object_type_list[ req->index ]))
+    {
+        if ((name = get_object_name( &type->obj, &reply->total )))
+            set_reply_data( name, min( reply->total, get_reply_max_size() ) );
+    }
+    else set_error( STATUS_NO_MORE_ENTRIES );
+}
diff --git a/server/protocol.def b/server/protocol.def
index ab5efef36e4..ade5e85d10b 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3408,6 +3408,15 @@ struct handle_info
 @END
 
 
+/* Query object type name information */
+@REQ(get_object_type_by_index)
+    unsigned int   index;         /* index of the type */
+@REPLY
+    data_size_t    total;         /* needed size for type name */
+    VARARG(type,unicode_str);     /* type name */
+@END
+
+
 /* Query the impersonation level of an impersonation token */
 @REQ(get_token_impersonation_level)
     obj_handle_t   handle;        /* handle to the object */
-- 
2.27.0

