Alexandre Julliard : ntdll: Added support for reading directories using the BSD getdirentries function .

Alexandre Julliard julliard at wine.codeweavers.com
Mon Nov 13 09:24:35 CST 2006


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

Author: Alexandre Julliard <julliard at winehq.org>
Date:   Mon Nov 13 15:42:48 2006 +0100

ntdll: Added support for reading directories using the BSD getdirentries function.

---

 configure              |    2 +
 configure.ac           |    1 +
 dlls/ntdll/directory.c |  114 ++++++++++++++++++++++++++++++++++++++++++++++--
 include/config.h.in    |    3 +
 4 files changed, 116 insertions(+), 4 deletions(-)

diff --git a/configure b/configure
index 54e689e..64e16d1 100755
--- a/configure
+++ b/configure
@@ -18185,6 +18185,7 @@ fi
 
 
 
+
 for ac_func in \
 	_pclose \
 	_popen \
@@ -18209,6 +18210,7 @@ for ac_func in \
 	futimes \
 	futimesat \
 	getaddrinfo \
+	getdirentries \
 	gethostbyname \
 	getnameinfo \
 	getnetbyname \
diff --git a/configure.ac b/configure.ac
index 958a6c8..37cc2ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1200,6 +1200,7 @@ AC_CHECK_FUNCS(\
 	futimes \
 	futimesat \
 	getaddrinfo \
+	getdirentries \
 	gethostbyname \
 	getnameinfo \
 	getnetbyname \
diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c
index d2c0d16..042daef 100644
--- a/dlls/ntdll/directory.c
+++ b/dlls/ntdll/directory.c
@@ -135,6 +135,8 @@ #define INVALID_DOS_CHARS  INVALID_NT_CH
 
 #define MAX_DIR_ENTRY_LEN 255  /* max length of a directory entry in chars */
 
+static const unsigned int max_dir_info_size = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName[MAX_DIR_ENTRY_LEN] );
+
 static int show_dot_files = -1;
 
 /* at some point we may want to allow Winelib apps to set this */
@@ -971,7 +973,6 @@ static int read_directory_vfat( int fd,
     size_t len;
     KERNEL_DIRENT *de;
     FILE_BOTH_DIR_INFORMATION *info, *last_info = NULL;
-    static const unsigned int max_dir_info_size = sizeof(*info) + (MAX_DIR_ENTRY_LEN-1) * sizeof(WCHAR);
 
     io->u.Status = STATUS_SUCCESS;
 
@@ -1063,7 +1064,6 @@ static int read_directory_getdents( int
     char local_buffer[8192];
     KERNEL_DIRENT64 *data, *de;
     FILE_BOTH_DIR_INFORMATION *info, *last_info = NULL;
-    static const unsigned int max_dir_info_size = sizeof(*info) + (MAX_DIR_ENTRY_LEN-1) * sizeof(WCHAR);
 
     if (size <= sizeof(local_buffer) || !(data = RtlAllocateHeap( GetProcessHeap(), 0, size )))
     {
@@ -1135,7 +1135,111 @@ done:
     if ((char *)data != local_buffer) RtlFreeHeap( GetProcessHeap(), 0, data );
     return res;
 }
-#endif  /* USE_GETDENTS */
+
+#elif defined HAVE_GETDIRENTRIES
+
+/***********************************************************************
+ *           read_directory_getdirentries
+ *
+ * Read a directory using the BSD getdirentries system call; helper for NtQueryDirectoryFile.
+ */
+static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
+                                         BOOLEAN single_entry, const UNICODE_STRING *mask,
+                                         BOOLEAN restart_scan )
+{
+    long restart_pos;
+    ULONG_PTR restart_info_pos = 0;
+    size_t size, initial_size = length;
+    int res;
+    char *data, local_buffer[8192];
+    struct dirent *de;
+    FILE_BOTH_DIR_INFORMATION *info, *last_info = NULL, *restart_last_info = NULL;
+
+    size = initial_size;
+    data = local_buffer;
+    if (size > sizeof(local_buffer) && !(data = RtlAllocateHeap( GetProcessHeap(), 0, size )))
+    {
+        io->u.Status = STATUS_NO_MEMORY;
+        return io->u.Status;
+    }
+
+    if (restart_scan) lseek( fd, 0, SEEK_SET );
+
+    io->u.Status = STATUS_SUCCESS;
+
+    /* FIXME: should make sure size is larger than filesystem block size */
+    res = getdirentries( fd, data, size, &restart_pos );
+    if (res == -1)
+    {
+        io->u.Status = FILE_GetNtStatus();
+        res = 0;
+        goto done;
+    }
+
+    de = (struct dirent *)data;
+
+    while (res > 0)
+    {
+        res -= de->d_reclen;
+        if (de->d_fileno &&
+            ((info = append_entry( buffer, &io->Information, length, de->d_name, NULL, mask ))))
+        {
+            last_info = info;
+            if ((char *)info->FileName + info->FileNameLength > (char *)buffer + length)
+            {
+                lseek( fd, (unsigned long)restart_pos, SEEK_SET );
+                if (restart_info_pos)  /* if we have a complete read already, return it */
+                {
+                    io->Information = restart_info_pos;
+                    last_info = restart_last_info;
+                    break;
+                }
+                /* otherwise restart from the start with a smaller size */
+                size = (char *)de - data;
+                if (!size)
+                {
+                    io->u.Status = STATUS_BUFFER_OVERFLOW;
+                    break;
+                }
+                io->Information = 0;
+                last_info = NULL;
+                goto restart;
+            }
+            /* if we have to return but the buffer contains more data, restart with a smaller size */
+            if (res > 0 && (single_entry || io->Information + max_dir_info_size > length))
+            {
+                lseek( fd, (unsigned long)restart_pos, SEEK_SET );
+                size = (char *)de - data;
+                io->Information = restart_info_pos;
+                last_info = restart_last_info;
+                goto restart;
+            }
+        }
+        /* move on to the next entry */
+        if (res > 0)
+        {
+            de = (struct dirent *)((char *)de + de->d_reclen);
+            continue;
+        }
+        if (size < initial_size) break;  /* already restarted once, give up now */
+        size = min( size, length - io->Information );
+        /* if size is too small don't bother to continue */
+        if (size < max_dir_info_size && last_info) break;
+        restart_last_info = last_info;
+        restart_info_pos = io->Information;
+    restart:
+        res = getdirentries( fd, data, size, &restart_pos );
+        de = (struct dirent *)data;
+    }
+
+    if (last_info) last_info->NextEntryOffset = 0;
+    else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
+    res = 0;
+done:
+    if (data != local_buffer) RtlFreeHeap( GetProcessHeap(), 0, data );
+    return res;
+}
+#endif  /* HAVE_GETDIRENTRIES */
 
 
 /***********************************************************************
@@ -1151,7 +1255,6 @@ static void read_directory_readdir( int
     off_t i, old_pos = 0;
     struct dirent *de;
     FILE_BOTH_DIR_INFORMATION *info, *last_info = NULL;
-    static const unsigned int max_dir_info_size = sizeof(*info) + (MAX_DIR_ENTRY_LEN-1) * sizeof(WCHAR);
 
     if (!(dir = opendir( "." )))
     {
@@ -1326,6 +1429,9 @@ #endif
 #ifdef USE_GETDENTS
         if ((read_directory_getdents( fd, io, buffer, length, single_entry, mask, restart_scan )) != -1)
             goto done;
+#elif defined HAVE_GETDIRENTRIES
+        if ((read_directory_getdirentries( fd, io, buffer, length, single_entry, mask, restart_scan )) != -1)
+            goto done;
 #endif
         read_directory_readdir( fd, io, buffer, length, single_entry, mask, restart_scan );
 
diff --git a/include/config.h.in b/include/config.h.in
index f26735d..69a0af9 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -180,6 +180,9 @@ #undef HAVE_FUTIMESAT
 /* Define to 1 if you have the `getaddrinfo' function. */
 #undef HAVE_GETADDRINFO
 
+/* Define to 1 if you have the `getdirentries' function. */
+#undef HAVE_GETDIRENTRIES
+
 /* Define to 1 if you have the `gethostbyname' function. */
 #undef HAVE_GETHOSTBYNAME
 




More information about the wine-cvs mailing list