[PATCH 2/3] ntdll: Implement reading multiple entries in NtQueryDirectoryObject.

Jinoh Kang jinoh.kang.kr at gmail.com
Sun May 8 08:49:11 CDT 2022


From: Zebediah Figura <zfigura at codeweavers.com>

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52585
Signed-off-by: Jinoh Kang <jinoh.kang.kr at gmail.com>
---

Notes:
    v3: fix a missing dereference in sizeof (thanks Jinoh Kang)
    
    v3 -> v4: split patch, undo parameter rename to minimize patch

 dlls/ntdll/tests/om.c  | 57 ++++++++++++----------------
 dlls/ntdll/unix/sync.c | 86 ++++++++++++++++++++++++++++++++++++++----
 server/directory.c     | 75 ++++++++++++++++++++++++++++++++++++
 server/protocol.def    | 18 +++++++++
 server/trace.c         | 34 +++++++++++++++++
 5 files changed, 229 insertions(+), 41 deletions(-)

diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c
index 2e03abd13ed..a4408b6e0e6 100644
--- a/dlls/ntdll/tests/om.c
+++ b/dlls/ntdll/tests/om.c
@@ -2537,9 +2537,9 @@ static void test_query_directory(void)
     context = 0xdeadbeef;
     size = 0xdeadbeef;
     status = NtQueryDirectoryObject( dir, info, 0, FALSE, TRUE, &context, &size );
-    todo_wine ok( status == STATUS_NO_MORE_ENTRIES, "got %#lx\n", status );
+    ok( status == STATUS_NO_MORE_ENTRIES, "got %#lx\n", status );
     ok( context == 0xdeadbeef, "got context %#lx\n", context );
-    todo_wine ok( size == sizeof(*info) || broken(!size) /* WoW64 */, "got size %lu\n", size );
+    ok( size == sizeof(*info) || broken(!size) /* WoW64 */, "got size %lu\n", size );
 
     context = 0xdeadbeef;
     size = 0xdeadbeef;
@@ -2555,9 +2555,9 @@ static void test_query_directory(void)
     size = 0xdeadbeef;
     memset( buffer, 0xcc, sizeof(buffer) );
     status = NtQueryDirectoryObject( dir, info, sizeof(buffer), FALSE, TRUE, &context, &size );
-    todo_wine ok( status == STATUS_NO_MORE_ENTRIES, "got %#lx\n", status );
+    ok( status == STATUS_NO_MORE_ENTRIES, "got %#lx\n", status );
     ok( context == 0xdeadbeef, "got context %#lx\n", context );
-    todo_wine ok( size == sizeof(*info) || broken(!size) /* WoW64 */, "got size %lu\n", size );
+    ok( size == sizeof(*info) || broken(!size) /* WoW64 */, "got size %lu\n", size );
     if (size == sizeof(*info))
         ok( !memcmp( &info[0], &empty_info, sizeof(*info) ), "entry was not cleared\n" );
 
@@ -2653,37 +2653,31 @@ static void test_query_directory(void)
 
     memset( buffer, 0xcc, sizeof(buffer) );
     status = NtQueryDirectoryObject( dir, info, sizeof(buffer), FALSE, TRUE, &context, &size );
-    todo_wine ok( !status, "got %#lx\n", status );
-    if (!status)
-    {
-        ok( context == 2, "got context %#lx\n", context );
-        check_unicode_string( &info[0].ObjectName, name1 );
-        check_unicode_string( &info[0].ObjectTypeName, L"Mutant" );
-        check_unicode_string( &info[1].ObjectName, name2 );
-        check_unicode_string( &info[1].ObjectTypeName, L"Mutant" );
-        ok( !memcmp( &info[2], &empty_info, sizeof(*info) ), "entry was not cleared\n" );
-    }
+    ok( !status, "got %#lx\n", status );
+    ok( context == 2, "got context %#lx\n", context );
+    check_unicode_string( &info[0].ObjectName, name1 );
+    check_unicode_string( &info[0].ObjectTypeName, L"Mutant" );
+    check_unicode_string( &info[1].ObjectName, name2 );
+    check_unicode_string( &info[1].ObjectTypeName, L"Mutant" );
+    ok( !memcmp( &info[2], &empty_info, sizeof(*info) ), "entry was not cleared\n" );
 
     needed_size = size;
     size = 0xdeadbeef;
     context = 0xdeadbeef;
     memset( buffer, 0xcc, sizeof(buffer) );
     status = NtQueryDirectoryObject( dir, info, needed_size - 1, FALSE, TRUE, &context, &size );
-    todo_wine ok( status == STATUS_MORE_ENTRIES, "got %#lx\n", status );
-    if (status == STATUS_MORE_ENTRIES)
-    {
-        ok( context == 1, "got context %#lx\n", context );
-        ok( size > 0 && size < needed_size, "got size %lu\n", size );
-        check_unicode_string( &info[0].ObjectName, name1 );
-        check_unicode_string( &info[0].ObjectTypeName, L"Mutant" );
-        ok( !memcmp( &info[1], &empty_info, sizeof(*info) ), "entry was not cleared\n" );
-    }
+    ok( status == STATUS_MORE_ENTRIES, "got %#lx\n", status );
+    ok( context == 1, "got context %#lx\n", context );
+    ok( size > 0 && size < needed_size, "got size %lu\n", size );
+    check_unicode_string( &info[0].ObjectName, name1 );
+    check_unicode_string( &info[0].ObjectTypeName, L"Mutant" );
+    ok( !memcmp( &info[1], &empty_info, sizeof(*info) ), "entry was not cleared\n" );
 
     size = 0xdeadbeef;
     context = 0xdeadbeef;
     memset( buffer, 0xcc, sizeof(buffer) );
     status = NtQueryDirectoryObject( dir, info, sizeof(*info), FALSE, TRUE, &context, &size );
-    todo_wine ok( status == STATUS_MORE_ENTRIES
+    ok( status == STATUS_MORE_ENTRIES
             || broken(status == STATUS_BUFFER_TOO_SMALL) /* wow64 */, "got %#lx\n", status );
     if (status == STATUS_MORE_ENTRIES)
     {
@@ -2695,7 +2689,7 @@ static void test_query_directory(void)
     size = 0xdeadbeef;
     context = 0xdeadbeef;
     status = NtQueryDirectoryObject( dir, info, 0, FALSE, TRUE, &context, &size );
-    todo_wine ok( status == STATUS_MORE_ENTRIES
+    ok( status == STATUS_MORE_ENTRIES
             || broken(status == STATUS_BUFFER_TOO_SMALL) /* wow64 */, "got %#lx\n", status );
     if (status == STATUS_MORE_ENTRIES)
     {
@@ -2706,14 +2700,11 @@ static void test_query_directory(void)
     context = 1;
     memset( buffer, 0xcc, sizeof(buffer) );
     status = NtQueryDirectoryObject( dir, info, sizeof(buffer), FALSE, FALSE, &context, &size );
-    todo_wine ok( !status, "got %#lx\n", status );
-    if (!status)
-    {
-        ok( context == 2, "got context %#lx\n", context );
-        check_unicode_string( &info[0].ObjectName, name2 );
-        check_unicode_string( &info[0].ObjectTypeName, L"Mutant" );
-        ok( !memcmp( &info[1], &empty_info, sizeof(*info) ), "entry was not cleared\n" );
-    }
+    ok( !status, "got %#lx\n", status );
+    ok( context == 2, "got context %#lx\n", context );
+    check_unicode_string( &info[0].ObjectName, name2 );
+    check_unicode_string( &info[0].ObjectTypeName, L"Mutant" );
+    ok( !memcmp( &info[1], &empty_info, sizeof(*info) ), "entry was not cleared\n" );
 
     NtClose( child1 );
     NtClose( child2 );
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index 1194ee514b5..cce1ac6f264 100644
--- a/dlls/ntdll/unix/sync.c
+++ b/dlls/ntdll/unix/sync.c
@@ -1110,8 +1110,11 @@ NTSTATUS WINAPI NtQueryDirectoryObject( HANDLE handle, DIRECTORY_BASIC_INFORMATI
                                         ULONG size, BOOLEAN single_entry, BOOLEAN restart,
                                         ULONG *context, ULONG *ret_size )
 {
+    unsigned int i, count, pos, used_size, used_count;
     ULONG index = restart ? 0 : *context;
-    NTSTATUS ret;
+    struct directory_entry *entries;
+    NTSTATUS status;
+    char *p;
 
     if (single_entry)
     {
@@ -1121,7 +1124,7 @@ NTSTATUS WINAPI NtQueryDirectoryObject( HANDLE handle, DIRECTORY_BASIC_INFORMATI
             req->index = index;
             if (size >= 2 * sizeof(*buffer) + 2 * sizeof(WCHAR))
                 wine_server_set_reply( req, buffer + 2, size - 2 * sizeof(*buffer) - 2 * sizeof(WCHAR) );
-            if (!(ret = wine_server_call( req )))
+            if (!(status = wine_server_call( req )))
             {
                 buffer->ObjectName.Buffer = (WCHAR *)(buffer + 2);
                 buffer->ObjectName.Length = reply->name_len;
@@ -1139,24 +1142,91 @@ NTSTATUS WINAPI NtQueryDirectoryObject( HANDLE handle, DIRECTORY_BASIC_INFORMATI
 
                 *context = index + 1;
             }
-            else if (ret == STATUS_NO_MORE_ENTRIES)
+            else if (status == STATUS_NO_MORE_ENTRIES)
             {
                 if (size > sizeof(*buffer))
                     memset( buffer, 0, sizeof(*buffer) );
                 if (ret_size) *ret_size = sizeof(*buffer);
             }
 
-            if (ret_size && (!ret || ret == STATUS_BUFFER_TOO_SMALL))
+            if (ret_size && (!status || status == STATUS_BUFFER_TOO_SMALL))
                 *ret_size = 2 * sizeof(*buffer) + reply->total_len + 2 * sizeof(WCHAR);
         }
         SERVER_END_REQ;
+        return status;
     }
-    else
+
+    if (!(entries = malloc( size ))) return STATUS_NO_MEMORY;
+
+    SERVER_START_REQ( get_directory_entries )
     {
-        FIXME("multiple entries not implemented\n");
-        ret = STATUS_NOT_IMPLEMENTED;
+        req->handle = wine_server_obj_handle( handle );
+        req->index = index;
+        wine_server_set_reply( req, entries, size );
+        status = wine_server_call( req );
+        count = reply->count;
     }
-    return ret;
+    SERVER_END_REQ;
+
+    if (status && status != STATUS_MORE_ENTRIES)
+    {
+        free( entries );
+        return status;
+    }
+
+    used_count = 0;
+    used_size = sizeof(*buffer); /* null terminator */
+    for (i = pos = 0; i < count; ++i)
+    {
+        const struct directory_entry *entry = (const struct directory_entry *)((char *)entries + pos);
+        unsigned int entry_size = sizeof(*buffer) + entry->name_len + entry->type_len + 2 * sizeof(WCHAR);
+
+        if (used_size + entry_size > size)
+        {
+            status = STATUS_MORE_ENTRIES;
+            break;
+        }
+        used_count++;
+        used_size += entry_size;
+        pos += sizeof(*entry) + ((entry->name_len + entry->type_len + 3) & ~3);
+    }
+
+    p = (char *)&buffer[used_count + 1]; /* after the null terminator */
+    for (i = pos = 0; i < used_count; ++i)
+    {
+        const struct directory_entry *entry = (const struct directory_entry *)((char *)entries + pos);
+
+        buffer[i].ObjectName.Buffer = (WCHAR *)p;
+        buffer[i].ObjectName.Length = entry->name_len;
+        buffer[i].ObjectName.MaximumLength = entry->name_len + sizeof(WCHAR);
+        memcpy( p, (entry + 1), entry->name_len );
+        buffer[i].ObjectName.Buffer[entry->name_len / sizeof(WCHAR)] = 0;
+        p += entry->name_len + sizeof(WCHAR);
+
+        buffer[i].ObjectTypeName.Buffer = (WCHAR *)p;
+        buffer[i].ObjectTypeName.Length = entry->type_len;
+        buffer[i].ObjectTypeName.MaximumLength = entry->type_len + sizeof(WCHAR);
+        memcpy( p, (char *)(entry + 1) + entry->name_len, entry->type_len );
+        buffer[i].ObjectTypeName.Buffer[entry->type_len / sizeof(WCHAR)] = 0;
+        p += entry->type_len + sizeof(WCHAR);
+
+        pos += sizeof(*entry) + ((entry->name_len + entry->type_len + 3) & ~3);
+    }
+
+    if (size >= sizeof(*buffer))
+        memset( &buffer[used_count], 0, sizeof(buffer[used_count]) );
+
+    free( entries );
+
+    if (!count && !status)
+    {
+        if (ret_size) *ret_size = sizeof(*buffer);
+        return STATUS_NO_MORE_ENTRIES;
+    }
+
+    *context = index + used_count;
+    if (ret_size) *ret_size = (char *)p - (char *)buffer;
+    return status;
 }
 
 
diff --git a/server/directory.c b/server/directory.c
index fdcfe9bef21..f35e5f35ede 100644
--- a/server/directory.c
+++ b/server/directory.c
@@ -532,6 +532,81 @@ DECL_HANDLER(open_directory)
                                  &directory_ops, &name, req->attributes );
 }
 
+/* get directory entries */
+DECL_HANDLER(get_directory_entries)
+{
+    struct directory *dir = (struct directory *)get_handle_obj( current->process, req->handle,
+                                                                DIRECTORY_QUERY, &directory_ops );
+    if (dir)
+    {
+        struct directory_entry *entry;
+        struct object *obj;
+        data_size_t size;
+        unsigned int i;
+        char *buffer;
+
+        size = 0;
+        for (i = 0; ; ++i)
+        {
+            const struct unicode_str *type_name;
+            data_size_t name_len;
+            size_t entry_size;
+
+            if (!(obj = find_object_index( dir->entries, req->index + i )))
+                break;
+            type_name = &obj->ops->type->name;
+            get_object_name( obj, &name_len );
+            entry_size = (sizeof(*entry) + name_len + type_name->len + 3) & ~3;
+            release_object( obj );
+
+            if (size + entry_size > get_reply_max_size())
+            {
+                set_error( STATUS_MORE_ENTRIES );
+                break;
+            }
+            size += entry_size;
+        }
+        reply->count = i;
+
+        if (!(buffer = set_reply_data_size( size )))
+        {
+            release_object( dir );
+            return;
+        }
+
+        size = 0;
+        for (i = 0; i < reply->count; ++i)
+        {
+            const struct unicode_str *type_name;
+            data_size_t name_len;
+            const WCHAR *name;
+
+            obj = find_object_index( dir->entries, req->index + i );
+            assert( obj );
+            type_name = &obj->ops->type->name;
+            name = get_object_name( obj, &name_len );
+
+            entry = (struct directory_entry *)(buffer + size);
+            entry->name_len = name_len;
+            entry->type_len = type_name->len;
+
+            size += sizeof(*entry);
+            memcpy( buffer + size, name, name_len );
+            size += name_len;
+            memcpy( buffer + size, type_name->str, type_name->len );
+            size += type_name->len;
+            if (size & 3)
+            {
+                memset( buffer + size, 0, 4 - (size & 3) );
+                size += 4 - (size & 3);
+            }
+
+            release_object( obj );
+        }
+        release_object( dir );
+    }
+}
+
 /* get a directory entry by index */
 DECL_HANDLER(get_directory_entry)
 {
diff --git a/server/protocol.def b/server/protocol.def
index 2be1658fca2..7a06a6e8455 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -847,6 +847,14 @@ typedef struct
     lparam_t info;
 } cursor_pos_t;
 
+struct directory_entry
+{
+    data_size_t name_len;
+    data_size_t type_len;
+    /* VARARG(name,unicode_str,name_len); */
+    /* VARARG(type,unicode_str,type_len); */
+};
+
 /****************************************************************/
 /* Request declarations */
 
@@ -3271,6 +3279,16 @@ struct handle_info
 @END
 
 
+/* Get directory entries */
+ at REQ(get_directory_entries)
+    obj_handle_t   handle;             /* handle to the directory */
+    unsigned int   index;              /* index of first entry */
+ at REPLY
+    unsigned int   count;              /* number of entries returned */
+    VARARG(entries,directory_entries);
+ at END
+
+
 /* Get a directory entry by index */
 @REQ(get_directory_entry)
     obj_handle_t   handle;             /* handle to the directory */
diff --git a/server/trace.c b/server/trace.c
index 15ca4e7d71e..4482b02a914 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1349,6 +1349,40 @@ static void dump_varargs_handle_infos( const char *prefix, data_size_t size )
     fputc( '}', stderr );
 }
 
+static void dump_varargs_directory_entries( const char *prefix, data_size_t size )
+{
+    fprintf( stderr, "%s{", prefix );
+    while (size)
+    {
+        const struct directory_entry *entry = cur_data;
+        data_size_t entry_size;
+        const char *next;
+
+        if (size < sizeof(*entry) ||
+            (size - sizeof(*entry) < entry->name_len) ||
+            (size - sizeof(*entry) - entry->name_len < entry->type_len))
+        {
+            fprintf( stderr, "***invalid***}" );
+            remove_data( size );
+            return;
+        }
+
+        next = (const char *)(entry + 1);
+        fprintf( stderr, "{name=L\"" );
+        dump_strW( (const WCHAR *)next, entry->name_len, stderr, "\"\"" );
+        next += entry->name_len;
+        fprintf( stderr, "\",type=L\"" );
+        dump_strW( (const WCHAR *)next, entry->type_len, stderr, "\"\"" );
+        fprintf( stderr, "\"}" );
+
+        entry_size = min( size, (sizeof(*entry) + entry->name_len + entry->type_len + 3) & ~3 );
+        size -= entry_size;
+        remove_data( entry_size );
+        if (size) fputc( ',', stderr );
+    }
+    fputc( '}', stderr );
+}
+
 typedef void (*dump_func)( const void *req );
 
 /* Everything below this line is generated automatically by tools/make_requests */
-- 
2.34.1


-- 
Sincerely,
Jinoh Kang



More information about the wine-devel mailing list