[PATCH] kernel32: implemented "Basic" mode of findfirstfilex (2nd try)

Marcus Meissner marcus at jet.franken.de
Sun Mar 23 09:33:26 CDT 2014


FindExInfoBasic was not handled, rewrite it a bit to handle it.

Added testcase, handle case where Windows OS does not support it yet.
---
 dlls/kernel32/file.c       | 112 +++++++++++++++++++++++++++++----------------
 dlls/kernel32/tests/file.c |  47 +++++++++++++++++++
 2 files changed, 120 insertions(+), 39 deletions(-)

diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c
index e205050..e986110 100644
--- a/dlls/kernel32/file.c
+++ b/dlls/kernel32/file.c
@@ -57,6 +57,7 @@ typedef struct
     HANDLE            handle;      /* handle to directory */
     CRITICAL_SECTION  cs;          /* crit section protecting this structure */
     FINDEX_SEARCH_OPS search_op;   /* Flags passed to FindFirst.  */
+    FILE_INFORMATION_CLASS infoclass; /* information requested */
     UNICODE_STRING    mask;        /* file mask */
     UNICODE_STRING    path;        /* NT path used to open the directory */
     BOOL              is_root;     /* is directory the root of the drive? */
@@ -118,7 +119,7 @@ static HANDLE create_file_OF( LPCSTR path, INT mode )
  *
  * Check if a dir symlink should be returned by FindNextFile.
  */
-static BOOL check_dir_symlink( FIND_FIRST_INFO *info, const FILE_BOTH_DIR_INFORMATION *file_info )
+static BOOL check_dir_symlink( FIND_FIRST_INFO *info, ULONG FileNameLength, WCHAR *FileName )
 {
     UNICODE_STRING str;
     ANSI_STRING unix_name;
@@ -126,13 +127,13 @@ static BOOL check_dir_symlink( FIND_FIRST_INFO *info, const FILE_BOTH_DIR_INFORM
     BOOL ret = TRUE;
     DWORD len;
 
-    str.MaximumLength = info->path.Length + sizeof(WCHAR) + file_info->FileNameLength;
+    str.MaximumLength = info->path.Length + sizeof(WCHAR) + FileNameLength;
     if (!(str.Buffer = HeapAlloc( GetProcessHeap(), 0, str.MaximumLength ))) return TRUE;
     memcpy( str.Buffer, info->path.Buffer, info->path.Length );
     len = info->path.Length / sizeof(WCHAR);
     if (!len || str.Buffer[len-1] != '\\') str.Buffer[len++] = '\\';
-    memcpy( str.Buffer + len, file_info->FileName, file_info->FileNameLength );
-    str.Length = len * sizeof(WCHAR) + file_info->FileNameLength;
+    memcpy( str.Buffer + len, FileName, FileNameLength );
+    str.Length = len * sizeof(WCHAR) + FileNameLength;
 
     unix_name.Buffer = NULL;
     if (!wine_nt_to_unix_file_name( &str, &unix_name, OPEN_EXISTING, FALSE ) &&
@@ -1858,6 +1859,7 @@ HANDLE WINAPI FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_LEVELS level,
     IO_STATUS_BLOCK io;
     NTSTATUS status;
     DWORD device = 0;
+    FILE_INFORMATION_CLASS infoclass;
 
     TRACE("%s %d %p %d %p %x\n", debugstr_w(filename), level, data, search_op, filter, flags);
 
@@ -1867,8 +1869,14 @@ HANDLE WINAPI FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_LEVELS level,
         FIXME("options not implemented 0x%08x 0x%08x\n", search_op, flags );
         return INVALID_HANDLE_VALUE;
     }
-    if (level != FindExInfoStandard)
-    {
+    switch (level) {
+    case FindExInfoStandard: 
+        infoclass = FileBothDirectoryInformation;
+        break;
+    case FindExInfoBasic: 
+        infoclass = FileFullDirectoryInformation;
+        break;
+    default:
         FIXME("info level %d not implemented\n", level );
         return INVALID_HANDLE_VALUE;
     }
@@ -1970,6 +1978,7 @@ HANDLE WINAPI FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_LEVELS level,
     info->data_size = 0;
     info->data      = NULL;
     info->search_op = search_op;
+    info->infoclass = infoclass;
 
     if (device)
     {
@@ -1998,7 +2007,7 @@ HANDLE WINAPI FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_LEVELS level,
             }
 
             NtQueryDirectoryFile( info->handle, 0, NULL, NULL, &io, info->data, info->data_size,
-                                  FileBothDirectoryInformation, FALSE, &info->mask, TRUE );
+                                  infoclass, FALSE, &info->mask, TRUE );
             if (io.u.Status)
             {
                 FindClose( info );
@@ -2052,8 +2061,10 @@ error:
 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
 {
     FIND_FIRST_INFO *info;
-    FILE_BOTH_DIR_INFORMATION *dir_info;
+    FILE_BOTH_DIR_INFORMATION *both_info;
     BOOL ret = FALSE;
+    WCHAR *filename;
+    ULONG filenamelength;
 
     TRACE("%p %p\n", handle, data);
 
@@ -2080,7 +2091,7 @@ BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
 
             if (info->data_size)
                 NtQueryDirectoryFile( info->handle, 0, NULL, NULL, &io, info->data, info->data_size,
-                                      FileBothDirectoryInformation, FALSE, &info->mask, FALSE );
+                                      info->infoclass, FALSE, &info->mask, FALSE );
             else
                 io.u.Status = STATUS_NO_MORE_FILES;
 
@@ -2100,41 +2111,64 @@ BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
             info->data_pos = 0;
         }
 
-        dir_info = (FILE_BOTH_DIR_INFORMATION *)(info->data + info->data_pos);
+        /* layout between full and both at the beginning is the same */
+        both_info = (FILE_BOTH_DIR_INFORMATION *)(info->data + info->data_pos);
+
+        /* The only differences are for FileName and ShortName */
+        if (info->infoclass == FileBothDirectoryInformation) {
+            filename = both_info->FileName;
+            filenamelength = both_info->FileNameLength;
+            FIXME("both fn length %d, fn %s\n", filenamelength, debugstr_w(filename));
+        } else if (info->infoclass == FileFullDirectoryInformation) {
+            FILE_FULL_DIR_INFORMATION *full_info = (FILE_FULL_DIR_INFORMATION *)(info->data + info->data_pos);
+
+            filename = full_info->FileName;
+            filenamelength = full_info->FileNameLength;
+            FIXME("full fn length %d, fn %s\n", filenamelength, debugstr_w(filename));
+        } else {
+            FIXME("infoclass %d, should not arrive here.\n", info->infoclass);
+            SetLastError( ERROR_INVALID_HANDLE );
+            return FALSE;
+        }
 
-        if (dir_info->NextEntryOffset) info->data_pos += dir_info->NextEntryOffset;
+        if (both_info->NextEntryOffset) info->data_pos += both_info->NextEntryOffset;
         else info->data_pos = info->data_len;
 
-        /* don't return '.' and '..' in the root of the drive */
-        if (info->is_root)
-        {
-            if (dir_info->FileNameLength == sizeof(WCHAR) && dir_info->FileName[0] == '.') continue;
-            if (dir_info->FileNameLength == 2 * sizeof(WCHAR) &&
-                dir_info->FileName[0] == '.' && dir_info->FileName[1] == '.') continue;
+	/* don't return '.' and '..' in the root of the drive */
+	if (info->is_root)
+	{
+	    if (filenamelength == sizeof(WCHAR) && filename[0] == '.') continue;
+	    if (filenamelength == 2 * sizeof(WCHAR) &&
+		filename[0] == '.' && filename[1] == '.') continue;
+	}
+
+	/* check for dir symlink */
+	if ((both_info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+	    (both_info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+	    strpbrkW( info->mask.Buffer, wildcardsW ))
+	{
+	    if (!check_dir_symlink( info, filenamelength, filename )) continue;
+	}
+
+	data->dwFileAttributes = both_info->FileAttributes;
+	data->ftCreationTime   = *(FILETIME *)&both_info->CreationTime;
+	data->ftLastAccessTime = *(FILETIME *)&both_info->LastAccessTime;
+	data->ftLastWriteTime  = *(FILETIME *)&both_info->LastWriteTime;
+	data->nFileSizeHigh    = both_info->EndOfFile.QuadPart >> 32;
+	data->nFileSizeLow     = (DWORD)both_info->EndOfFile.QuadPart;
+	data->dwReserved0      = 0;
+	data->dwReserved1      = 0;
+
+	memcpy( data->cFileName, filename, filenamelength );
+	data->cFileName[filenamelength/sizeof(WCHAR)] = 0;
+
+        if (info->infoclass == FileBothDirectoryInformation) {
+	    memcpy( data->cAlternateFileName, both_info->ShortName, both_info->ShortNameLength );
+	    data->cAlternateFileName[both_info->ShortNameLength/sizeof(WCHAR)] = 0;
+        } else {
+	    data->cAlternateFileName[0] = 0;
         }
 
-        /* check for dir symlink */
-        if ((dir_info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
-            (dir_info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
-            strpbrkW( info->mask.Buffer, wildcardsW ))
-        {
-            if (!check_dir_symlink( info, dir_info )) continue;
-        }
-
-        data->dwFileAttributes = dir_info->FileAttributes;
-        data->ftCreationTime   = *(FILETIME *)&dir_info->CreationTime;
-        data->ftLastAccessTime = *(FILETIME *)&dir_info->LastAccessTime;
-        data->ftLastWriteTime  = *(FILETIME *)&dir_info->LastWriteTime;
-        data->nFileSizeHigh    = dir_info->EndOfFile.QuadPart >> 32;
-        data->nFileSizeLow     = (DWORD)dir_info->EndOfFile.QuadPart;
-        data->dwReserved0      = 0;
-        data->dwReserved1      = 0;
-
-        memcpy( data->cFileName, dir_info->FileName, dir_info->FileNameLength );
-        data->cFileName[dir_info->FileNameLength/sizeof(WCHAR)] = 0;
-        memcpy( data->cAlternateFileName, dir_info->ShortName, dir_info->ShortNameLength );
-        data->cAlternateFileName[dir_info->ShortNameLength/sizeof(WCHAR)] = 0;
-
         TRACE("returning %s (%s)\n",
               debugstr_w(data->cFileName), debugstr_w(data->cAlternateFileName) );
 
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c
index 492a34f..345136d 100644
--- a/dlls/kernel32/tests/file.c
+++ b/dlls/kernel32/tests/file.c
@@ -2619,6 +2619,53 @@ static void test_FindFirstFileExA(FINDEX_SEARCH_OPS search_ops)
 
     FindClose( handle );
 
+    /* Now do it without short names */
+
+    handle = pFindFirstFileExA("test-dir\\*", FindExInfoBasic, &search_results, search_ops, NULL, 0);
+    if (handle == INVALID_HANDLE_VALUE && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+    {
+        win_skip("FindFirstFileExA is not implemented\n");
+        goto cleanup;
+    }
+    if (handle == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
+    {
+        win_skip("FindFirstFileExA FindExInfoBasic is not implemented\n");
+        goto cleanup;
+    }
+    ok(handle != INVALID_HANDLE_VALUE, "FindFirstFile failed (err=%u)\n", GetLastError());
+    ok(strcmp(search_results.cFileName, ".") == 0, "First entry should be '.', is %s\n", search_results.cFileName);
+
+#define CHECK_NAME(fn) (strcmp((fn), "file1") == 0 || strcmp((fn), "file2") == 0 || strcmp((fn), "dir1") == 0)
+
+    ok(FindNextFileA(handle, &search_results), "Fetching second file failed\n");
+    ok(strcmp(search_results.cFileName, "..") == 0, "Second entry should be '..' is %s\n", search_results.cFileName);
+
+    ok(FindNextFileA(handle, &search_results), "Fetching third file failed\n");
+    ok(CHECK_NAME(search_results.cFileName), "Invalid third entry - %s\n", search_results.cFileName);
+
+    SetLastError(0xdeadbeef);
+    ret = FindNextFileA(handle, &search_results);
+    if (!ret && (GetLastError() == ERROR_NO_MORE_FILES) && (search_ops == FindExSearchLimitToDirectories))
+    {
+        skip("File system supports directory filtering\n");
+        /* Results from the previous call are not cleared */
+        ok(strcmp(search_results.cFileName, "dir1") == 0, "Third entry should be 'dir1' is %s\n", search_results.cFileName);
+        FindClose( handle );
+        goto cleanup;
+    }
+
+    ok(ret, "Fetching fourth file failed\n");
+    ok(CHECK_NAME(search_results.cFileName), "Invalid fourth entry - %s\n", search_results.cFileName);
+
+    ok(FindNextFileA(handle, &search_results), "Fetching fifth file failed\n");
+    ok(CHECK_NAME(search_results.cFileName), "Invalid fifth entry - %s\n", search_results.cFileName);
+
+#undef CHECK_NAME
+
+    ok(FindNextFileA(handle, &search_results) == FALSE, "Fetching sixth file should fail\n");
+
+    FindClose( handle );
+
 cleanup:
     DeleteFileA("test-dir\\file1");
     DeleteFileA("test-dir\\file2");
-- 
1.8.4.5




More information about the wine-patches mailing list