[PATCH 6/6] server: Implement reading multiple entries in NtQueryDirectoryObject.

Zebediah Figura zfigura at codeweavers.com
Mon Apr 4 20:27:27 CDT 2022


Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52585
Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
---
 dlls/ntdll/unix/sync.c | 112 ++++++++++++++++++++++++++++-------------
 server/directory.c     |  77 ++++++++++++++++++++++------
 server/object.c        |   1 -
 server/protocol.def    |  22 +++++---
 server/trace.c         |  34 +++++++++++++
 5 files changed, 190 insertions(+), 56 deletions(-)

diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index ff0273b49eb..4555277f1ae 100644
--- a/dlls/ntdll/unix/sync.c
+++ b/dlls/ntdll/unix/sync.c
@@ -1095,51 +1095,93 @@ NTSTATUS WINAPI NtOpenDirectoryObject( HANDLE *handle, ACCESS_MASK access, const
 /**************************************************************************
  *           NtQueryDirectoryObject   (NTDLL.@)
  */
-NTSTATUS WINAPI NtQueryDirectoryObject( HANDLE handle, DIRECTORY_BASIC_INFORMATION *buffer,
+NTSTATUS WINAPI NtQueryDirectoryObject( HANDLE handle, DIRECTORY_BASIC_INFORMATION *info,
                                         ULONG size, BOOLEAN single_entry, BOOLEAN restart,
                                         ULONG *context, ULONG *ret_size )
 {
+    unsigned int i, count, total_count, total_len, pos, used_size, used_count;
     ULONG index = restart ? 0 : *context;
-    NTSTATUS ret;
+    struct directory_entry *buffer;
+    NTSTATUS status;
+    char *p;
+
+    if (!(buffer = malloc( size ))) return STATUS_NO_MEMORY;
 
-    if (single_entry)
+    SERVER_START_REQ( get_directory_entries )
     {
-        if (size <= 2 * sizeof(*buffer) + 2 * sizeof(WCHAR)) return STATUS_BUFFER_TOO_SMALL;
+        req->handle = wine_server_obj_handle( handle );
+        req->index = index;
+        req->max_count = single_entry ? 1 : UINT_MAX;
+        wine_server_set_reply( req, buffer, size );
+        status = wine_server_call( req );
+        count = reply->count;
+        total_count = reply->total_count;
+        total_len = reply->total_len;
+    }
+    SERVER_END_REQ;
 
-        SERVER_START_REQ( get_directory_entry )
-        {
-            req->handle = wine_server_obj_handle( handle );
-            req->index = index;
-            wine_server_set_reply( req, buffer + 2, size - 2 * sizeof(*buffer) - 2 * sizeof(WCHAR) );
-            if (!(ret = wine_server_call( req )))
-            {
-                buffer->ObjectName.Buffer = (WCHAR *)(buffer + 2);
-                buffer->ObjectName.Length = reply->name_len;
-                buffer->ObjectName.MaximumLength = reply->name_len + sizeof(WCHAR);
-                buffer->ObjectTypeName.Buffer = (WCHAR *)(buffer + 2) + reply->name_len/sizeof(WCHAR) + 1;
-                buffer->ObjectTypeName.Length = wine_server_reply_size( reply ) - reply->name_len;
-                buffer->ObjectTypeName.MaximumLength = buffer->ObjectTypeName.Length + sizeof(WCHAR);
-                /* make room for the terminating null */
-                memmove( buffer->ObjectTypeName.Buffer, buffer->ObjectTypeName.Buffer - 1,
-                         buffer->ObjectTypeName.Length );
-                buffer->ObjectName.Buffer[buffer->ObjectName.Length/sizeof(WCHAR)] = 0;
-                buffer->ObjectTypeName.Buffer[buffer->ObjectTypeName.Length/sizeof(WCHAR)] = 0;
-
-                memset( &buffer[1], 0, sizeof(buffer[1]) );
-
-                *context = index + 1;
-            }
-        }
-        SERVER_END_REQ;
-        if (ret_size)
-            *ret_size = buffer->ObjectName.MaximumLength + buffer->ObjectTypeName.MaximumLength + 2 * sizeof(*buffer);
+    if (status)
+    {
+        free( buffer );
+        return status;
     }
-    else
+
+    if (!total_count)
     {
-        FIXME("multiple entries not implemented\n");
-        ret = STATUS_NOT_IMPLEMENTED;
+        free( buffer );
+        if (ret_size) *ret_size = 0;
+        return STATUS_NO_MORE_ENTRIES;
     }
-    return ret;
+
+    used_count = 0;
+    used_size = sizeof(*info); /* null terminator */
+    for (i = pos = 0; i < count; ++i)
+    {
+        const struct directory_entry *entry = (const struct directory_entry *)((char *)buffer + pos);
+        unsigned int entry_size = sizeof(*info) + entry->name_len + entry->type_len + 2 * sizeof(WCHAR);
+
+        if (used_size + entry_size > size)
+            break;
+        used_count++;
+        used_size += entry_size;
+        pos += sizeof(*entry) + ((entry->name_len + entry->type_len + 3) & ~3);
+    }
+
+    if (!used_count)
+    {
+        if (ret_size) *ret_size = total_count * (sizeof(*info) + 2 * sizeof(WCHAR)) + sizeof(*info) + total_len;
+        free( buffer );
+        return STATUS_BUFFER_TOO_SMALL;
+    }
+
+    p = (char *)&info[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 *)buffer + pos);
+
+        info[i].ObjectName.Buffer = (WCHAR *)p;
+        info[i].ObjectName.Length = entry->name_len;
+        info[i].ObjectName.MaximumLength = entry->name_len + sizeof(WCHAR);
+        memcpy( p, (entry + 1), entry->name_len );
+        info[i].ObjectName.Buffer[entry->name_len / sizeof(WCHAR)] = 0;
+        p += entry->name_len + sizeof(WCHAR);
+
+        info[i].ObjectTypeName.Buffer = (WCHAR *)p;
+        info[i].ObjectTypeName.Length = entry->type_len;
+        info[i].ObjectTypeName.MaximumLength = entry->type_len + sizeof(WCHAR);
+        memcpy( p, (char *)(entry + 1) + entry->name_len, entry->type_len );
+        info[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);
+    }
+
+    memset( &info[used_count], 0, sizeof(info[used_count]) );
+
+    if (ret_size) *ret_size = (char *)p - (char *)info;
+    *context = index + used_count;
+    free( buffer );
+    return (used_count < total_count ? STATUS_MORE_ENTRIES : STATUS_SUCCESS);
 }
 
 
diff --git a/server/directory.c b/server/directory.c
index caaf8c43d33..a7619c5a6af 100644
--- a/server/directory.c
+++ b/server/directory.c
@@ -533,30 +533,79 @@ DECL_HANDLER(open_directory)
 }
 
 /* get a directory entry by index */
-DECL_HANDLER(get_directory_entry)
+DECL_HANDLER(get_directory_entries)
 {
     struct directory *dir = (struct directory *)get_handle_obj( current->process, req->handle,
                                                                 DIRECTORY_QUERY, &directory_ops );
     if (dir)
     {
-        struct object *obj = find_object_index( dir->entries, req->index );
-        if (obj)
+        struct directory_entry *entry;
+        struct object *obj;
+        data_size_t size;
+        unsigned int i;
+        char *buffer;
+
+        reply->count = 0;
+        reply->total_count = 0;
+        reply->total_len = 0;
+
+        size = 0;
+        for (i = 0; i < req->max_count; ++i)
         {
+            const struct unicode_str *type_name;
             data_size_t name_len;
-            const struct unicode_str *type_name = &obj->ops->type->name;
-            const WCHAR *name = get_object_name( obj, &name_len );
+            size_t entry_size;
 
-            if (name_len + type_name->len <= get_reply_max_size())
+            if (!(obj = find_object_index( dir->entries, req->index + reply->total_count )))
+                break;
+            type_name = &obj->ops->type->name;
+            get_object_name( obj, &name_len );
+            entry_size = (sizeof(*entry) + name_len + type_name->len + 3) & ~3;
+
+            reply->total_count++;
+            reply->total_len += name_len + type_name->len;
+
+            if (size + entry_size <= get_reply_max_size())
             {
-                void *ptr = set_reply_data_size( name_len + type_name->len );
-                if (ptr)
-                {
-                    reply->name_len = name_len;
-                    memcpy( ptr, name, name_len );
-                    memcpy( (char *)ptr + name_len, type_name->str, type_name->len );
-                }
+                reply->count++;
+                size += entry_size;
+            }
+
+            release_object( obj );
+        }
+
+        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);
             }
-            else set_error( STATUS_BUFFER_TOO_SMALL );
 
             release_object( obj );
         }
diff --git a/server/object.c b/server/object.c
index 907bc087444..84c8e70e6a2 100644
--- a/server/object.c
+++ b/server/object.c
@@ -485,7 +485,6 @@ struct object *find_object_index( const struct namespace *namespace, unsigned in
             if (!index--) return grab_object( ptr->obj );
         }
     }
-    set_error( STATUS_NO_MORE_ENTRIES );
     return NULL;
 }
 
diff --git a/server/protocol.def b/server/protocol.def
index d9bed6855e9..7381c26645f 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 */
 
@@ -3270,14 +3278,16 @@ struct handle_info
 @END
 
 
-/* Get a directory entry by index */
- at REQ(get_directory_entry)
+/* Get directory entries */
+ at REQ(get_directory_entries)
     obj_handle_t   handle;             /* handle to the directory */
-    unsigned int   index;              /* entry index */
+    unsigned int   index;              /* index of first entry */
+    unsigned int   max_count;          /* maximum number of entries to return */
 @REPLY
-    data_size_t    name_len;           /* length of the entry name in bytes */
-    VARARG(name,unicode_str,name_len); /* entry name */
-    VARARG(type,unicode_str);          /* entry type */
+    unsigned int   count;              /* number of entries returned */
+    unsigned int   total_count;        /* total number of entries starting at index */
+    data_size_t    total_len;          /* total length of all strings starting at index */
+    VARARG(entries,directory_entries);
 @END
 
 
diff --git a/server/trace.c b/server/trace.c
index a792b74dc05..4464d3d0450 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.35.1




More information about the wine-devel mailing list