ntdll: Implement a read_directory_getattrlist() function. (v2)

Matteo Bruni mbruni at codeweavers.com
Tue May 19 16:59:06 CDT 2015


When searching for a specific filename on a case-insensitive filesystem
we first try with stat(). If stat() does find the file we currently
return the requested filename back.

That presents an issue when the application cares about the casing of the
actual file stored on-disk. Specifically, NtQueryDirectoryFile is
supposed to return the actual filename with correct casing.

One possible solution to the issue, without having to resort to manually
scanning the directory entries, is to make use of the OS X getattrlist()
function, since it can return the filename stored on the filesystem.
This patch does just that, in preference to the stat() method.

Thanks to Ken for coming up with a nice way to fix this issue, I really
just had to implement what he suggested.

This fixes World of Warships on OS X with the default case-insensitive
filesystem.

v2: Don't break case-sensitive filesystems.
---
 dlls/ntdll/directory.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 79 insertions(+), 2 deletions(-)

diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c
index cbfc022..81b5c2e 100644
--- a/dlls/ntdll/directory.c
+++ b/dlls/ntdll/directory.c
@@ -2112,6 +2112,77 @@ done:
     return ret;
 }
 
+#ifdef HAVE_GETATTRLIST
+/***********************************************************************
+ *           read_directory_getattrlist
+ *
+ * Read a single file from a directory by determining whether the file
+ * identified by mask exists using getattrlist.
+ */
+static int read_directory_getattrlist( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
+                                       BOOLEAN single_entry, const UNICODE_STRING *mask,
+                                       BOOLEAN restart_scan, FILE_INFORMATION_CLASS class )
+{
+    int unix_len, ret, used_default;
+    char *unix_name;
+    struct attrlist attrlist;
+    struct
+    {
+        u_int32_t length;
+        struct attrreference name_reference;
+        char name[NAME_MAX + 1];
+    } attrlist_buffer;
+
+    TRACE("looking up file %s\n", debugstr_us( mask ));
+
+    unix_len = ntdll_wcstoumbs( 0, mask->Buffer, mask->Length / sizeof(WCHAR), NULL, 0, NULL, NULL );
+    if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len + 1)))
+    {
+        io->u.Status = STATUS_NO_MEMORY;
+        return 0;
+    }
+    ret = ntdll_wcstoumbs( 0, mask->Buffer, mask->Length / sizeof(WCHAR), unix_name, unix_len,
+                           NULL, &used_default );
+    if (ret > 0 && !used_default)
+    {
+        unix_name[ret] = 0;
+        if (restart_scan)
+        {
+            lseek( fd, 0, SEEK_SET );
+        }
+        else if (lseek( fd, 0, SEEK_CUR ) != 0)
+        {
+            io->u.Status = STATUS_NO_MORE_FILES;
+            ret = 0;
+            goto done;
+        }
+
+        memset( &attrlist, 0, sizeof(attrlist) );
+        attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
+        attrlist.commonattr = ATTR_CMN_NAME;
+        ret = getattrlist( unix_name, &attrlist, &attrlist_buffer, sizeof(attrlist_buffer), 0 );
+        if (!ret)
+        {
+            union file_directory_info *info = append_entry( buffer, io, length, attrlist_buffer.name, NULL, NULL, class );
+            if (info)
+            {
+                info->next = 0;
+                if (io->u.Status != STATUS_BUFFER_OVERFLOW) lseek( fd, 1, SEEK_CUR );
+            }
+            else io->u.Status = STATUS_NO_MORE_FILES;
+        }
+    }
+    else ret = -1;
+
+done:
+    RtlFreeHeap( GetProcessHeap(), 0, unix_name );
+
+    TRACE("returning %d\n", ret);
+
+    return ret;
+}
+#endif
+
 
 /******************************************************************************
  *  NtQueryDirectoryFile	[NTDLL.@]
@@ -2173,9 +2244,15 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event,
         if ((read_directory_vfat( fd, io, buffer, length, single_entry,
                                   mask, restart_scan, info_class )) != -1) goto done;
 #endif
-        if (!has_wildcard( mask ) &&
-            read_directory_stat( fd, io, buffer, length, single_entry,
+        if (!has_wildcard( mask ))
+        {
+#ifdef HAVE_GETATTRLIST
+            if (read_directory_getattrlist( fd, io, buffer, length, single_entry,
                                  mask, restart_scan, info_class ) != -1) goto done;
+#endif
+            if (read_directory_stat( fd, io, buffer, length, single_entry,
+                                 mask, restart_scan, info_class ) != -1) goto done;
+        }
 #ifdef USE_GETDENTS
         if ((read_directory_getdents( fd, io, buffer, length, single_entry,
                                       mask, restart_scan, info_class )) != -1) goto done;
-- 
2.3.6




More information about the wine-patches mailing list