Alexandre Julliard : ntdll: Always return . and .. as the first two entries in directory searches.

Alexandre Julliard julliard at wine.codeweavers.com
Fri Dec 1 06:29:33 CST 2006


Module: wine
Branch: master
Commit: df29fd3ab322b30cfd60a1c8f56ceb72ef4ae57c
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=df29fd3ab322b30cfd60a1c8f56ceb72ef4ae57c

Author: Alexandre Julliard <julliard at winehq.org>
Date:   Fri Dec  1 13:11:38 2006 +0100

ntdll: Always return . and .. as the first two entries in directory searches.

---

 dlls/kernel32/file.c   |   38 ++++++++++-----
 dlls/ntdll/directory.c |  122 ++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 134 insertions(+), 26 deletions(-)

diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c
index d0b28d0..5a0c5cb 100644
--- a/dlls/kernel32/file.c
+++ b/dlls/kernel32/file.c
@@ -1677,20 +1677,34 @@ HANDLE WINAPI FindFirstFileExW( LPCWSTR
         CloseHandle( info->handle );
         info->handle = 0;
     }
-    else if (!FindNextFileW( (HANDLE)info, data ))
-    {
-        TRACE( "%s not found\n", debugstr_w(filename) );
-        FindClose( (HANDLE)info );
-        SetLastError( ERROR_FILE_NOT_FOUND );
-        return INVALID_HANDLE_VALUE;
-    }
-    else if (!strpbrkW( info->mask.Buffer, wildcardsW ))
+    else
     {
-        /* we can't find two files with the same name */
-        CloseHandle( info->handle );
-        info->handle = 0;
+        IO_STATUS_BLOCK io;
+
+        NtQueryDirectoryFile( info->handle, 0, NULL, NULL, &io, info->data, sizeof(info->data),
+                              FileBothDirectoryInformation, FALSE, &info->mask, TRUE );
+        if (io.u.Status)
+        {
+            FindClose( info );
+            SetLastError( RtlNtStatusToDosError( io.u.Status ) );
+            return INVALID_HANDLE_VALUE;
+        }
+        info->data_len = io.Information;
+        if (!FindNextFileW( info, data ))
+        {
+            TRACE( "%s not found\n", debugstr_w(filename) );
+            FindClose( info );
+            SetLastError( ERROR_FILE_NOT_FOUND );
+            return INVALID_HANDLE_VALUE;
+        }
+        if (!strpbrkW( info->mask.Buffer, wildcardsW ))
+        {
+            /* we can't find two files with the same name */
+            CloseHandle( info->handle );
+            info->handle = 0;
+        }
     }
-    return (HANDLE)info;
+    return info;
 
 error:
     HeapFree( GetProcessHeap(), 0, info );
diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c
index 37d2d4c..3786b91 100644
--- a/dlls/ntdll/directory.c
+++ b/dlls/ntdll/directory.c
@@ -107,7 +107,7 @@ typedef struct
     char           d_name[256];
 } KERNEL_DIRENT64;
 
-static inline int getdents64( int fd, KERNEL_DIRENT64 *de, unsigned int size )
+static inline int getdents64( int fd, char *de, unsigned int size )
 {
     int ret;
     __asm__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
@@ -1060,15 +1060,15 @@ static int read_directory_getdents( int
 {
     off_t old_pos = 0;
     size_t size = length;
-    int res;
-    char local_buffer[8192];
-    KERNEL_DIRENT64 *data, *de;
+    int res, fake_dot_dot = 1;
+    char *data, local_buffer[8192];
+    KERNEL_DIRENT64 *de;
     FILE_BOTH_DIR_INFORMATION *info, *last_info = NULL;
 
     if (size <= sizeof(local_buffer) || !(data = RtlAllocateHeap( GetProcessHeap(), 0, size )))
     {
         size = sizeof(local_buffer);
-        data = (KERNEL_DIRENT64 *)local_buffer;
+        data = local_buffer;
     }
 
     if (restart_scan) lseek( fd, 0, SEEK_SET );
@@ -1096,13 +1096,52 @@ static int read_directory_getdents( int
         goto done;
     }
 
-    de = data;
+    de = (KERNEL_DIRENT64 *)data;
+
+    if (restart_scan)
+    {
+        /* check if we got . and .. from getdents */
+        if (res > 0)
+        {
+            if (!strcmp( de->d_name, "." ) && res > de->d_reclen)
+            {
+                KERNEL_DIRENT64 *next_de = (KERNEL_DIRENT64 *)(data + de->d_reclen);
+                if (!strcmp( next_de->d_name, ".." )) fake_dot_dot = 0;
+            }
+        }
+        /* make sure we have enough room for both entries */
+        if (fake_dot_dot)
+        {
+            static const ULONG min_info_size = (FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName[1]) +
+                                                FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName[2]) + 3) & ~3;
+            if (length < min_info_size || single_entry)
+            {
+                FIXME( "not enough room %u/%u for fake . and .. entries\n", length, single_entry );
+                fake_dot_dot = 0;
+            }
+        }
+
+        if (fake_dot_dot)
+        {
+            if ((info = append_entry( buffer, &io->Information, length, ".", NULL, mask )))
+                last_info = info;
+            if ((info = append_entry( buffer, &io->Information, length, "..", NULL, mask )))
+                last_info = info;
+
+            /* check if we still have enough space for the largest possible entry */
+            if (last_info && io->Information + max_dir_info_size > length)
+            {
+                lseek( fd, 0, SEEK_SET );  /* reset pos to first entry */
+                res = 0;
+            }
+        }
+    }
 
     while (res > 0)
     {
         res -= de->d_reclen;
-        info = append_entry( buffer, &io->Information, length, de->d_name, NULL, mask );
-        if (info)
+        if (!(fake_dot_dot && (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." ))) &&
+            (info = append_entry( buffer, &io->Information, length, de->d_name, NULL, mask )))
         {
             last_info = info;
             if ((char *)info->FileName + info->FileNameLength > (char *)buffer + length)
@@ -1124,7 +1163,7 @@ static int read_directory_getdents( int
         else
         {
             res = getdents64( fd, data, size );
-            de = data;
+            de = (KERNEL_DIRENT64 *)data;
         }
     }
 
@@ -1132,7 +1171,7 @@ static int read_directory_getdents( int
     else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
     res = 0;
 done:
-    if ((char *)data != local_buffer) RtlFreeHeap( GetProcessHeap(), 0, data );
+    if (data != local_buffer) RtlFreeHeap( GetProcessHeap(), 0, data );
     return res;
 }
 
@@ -1150,7 +1189,7 @@ static int read_directory_getdirentries(
     long restart_pos;
     ULONG_PTR restart_info_pos = 0;
     size_t size, initial_size = length;
-    int res;
+    int res, fake_dot_dot = 1;
     char *data, local_buffer[8192];
     struct dirent *de;
     FILE_BOTH_DIR_INFORMATION *info, *last_info = NULL, *restart_last_info = NULL;
@@ -1178,10 +1217,53 @@ static int read_directory_getdirentries(
 
     de = (struct dirent *)data;
 
+    if (restart_scan)
+    {
+        /* check if we got . and .. from getdirentries */
+        if (res > 0)
+        {
+            if (!strcmp( de->d_name, "." ) && res > de->d_reclen)
+            {
+                struct dirent *next_de = (struct dirent *)(data + de->d_reclen);
+                if (!strcmp( next_de->d_name, ".." )) fake_dot_dot = 0;
+            }
+        }
+        /* make sure we have enough room for both entries */
+        if (fake_dot_dot)
+        {
+            static const ULONG min_info_size = (FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName[1]) +
+                                                FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName[2]) + 3) & ~3;
+            if (length < min_info_size || single_entry)
+            {
+                FIXME( "not enough room %u/%u for fake . and .. entries\n", length, single_entry );
+                fake_dot_dot = 0;
+            }
+        }
+
+        if (fake_dot_dot)
+        {
+            if ((info = append_entry( buffer, &io->Information, length, ".", NULL, mask )))
+                last_info = info;
+            if ((info = append_entry( buffer, &io->Information, length, "..", NULL, mask )))
+                last_info = info;
+
+            restart_last_info = last_info;
+            restart_info_pos = io->Information;
+
+            /* check if we still have enough space for the largest possible entry */
+            if (last_info && io->Information + max_dir_info_size > length)
+            {
+                lseek( fd, 0, SEEK_SET );  /* reset pos to first entry */
+                res = 0;
+            }
+        }
+    }
+
     while (res > 0)
     {
         res -= de->d_reclen;
         if (de->d_fileno &&
+            !(fake_dot_dot && (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." ))) &&
             ((info = append_entry( buffer, &io->Information, length, de->d_name, NULL, mask ))))
         {
             last_info = info;
@@ -1266,7 +1348,7 @@ static void read_directory_readdir( int
     {
         old_pos = lseek( fd, 0, SEEK_CUR );
         /* skip the right number of entries */
-        for (i = 0; i < old_pos; i++)
+        for (i = 0; i < old_pos - 2; i++)
         {
             if (!readdir( dir ))
             {
@@ -1278,10 +1360,22 @@ static void read_directory_readdir( int
     }
     io->u.Status = STATUS_SUCCESS;
 
-    while ((de = readdir( dir )))
+    for (;;)
     {
+        if (old_pos == 0)
+            info = append_entry( buffer, &io->Information, length, ".", NULL, mask );
+        else if (old_pos == 1)
+            info = append_entry( buffer, &io->Information, length, "..", NULL, mask );
+        else if ((de = readdir( dir )))
+        {
+            if (strcmp( de->d_name, "." ) && strcmp( de->d_name, ".." ))
+                info = append_entry( buffer, &io->Information, length, de->d_name, NULL, mask );
+            else
+                info = NULL;
+        }
+        else
+            break;
         old_pos++;
-        info = append_entry( buffer, &io->Information, length, de->d_name, NULL, mask );
         if (info)
         {
             last_info = info;




More information about the wine-cvs mailing list