[PATCH 1/4] ntdll: Make append_entry fail if the new entry doesn't fit.

Matteo Bruni mbruni at codeweavers.com
Thu Aug 27 14:08:38 CDT 2015


Instead of checking in each helper if there is space for the largest
possible entry just handle append_entry() failure accordingly.

Not sure what's the deal with the "partial" entries mentioned in some
helpers, I couldn't find anything in my tests.
---
 dlls/ntdll/directory.c | 195 ++++++++++++++++++++++---------------------------
 1 file changed, 87 insertions(+), 108 deletions(-)

diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c
index 0e02f2e..f9562d2 100644
--- a/dlls/ntdll/directory.c
+++ b/dlls/ntdll/directory.c
@@ -246,11 +246,6 @@ static inline unsigned int dir_info_size( FILE_INFORMATION_CLASS class, unsigned
     }
 }
 
-static inline unsigned int max_dir_info_size( FILE_INFORMATION_CLASS class )
-{
-    return dir_info_size( class, MAX_DIR_ENTRY_LEN );
-}
-
 static inline BOOL has_wildcard( const UNICODE_STRING *mask )
 {
     return (!mask ||
@@ -1432,8 +1427,8 @@ static union file_directory_info *append_entry( void *info_ptr, IO_STATUS_BLOCK
     total_len = dir_info_size( class, long_len );
     if (io->Information + total_len > max_length)
     {
-        total_len = max_length - io->Information;
         io->u.Status = STATUS_BUFFER_OVERFLOW;
+        return NULL;
     }
     info = (union file_directory_info *)((char *)info_ptr + io->Information);
     if (st.st_dev != curdir.dev) st.st_ino = 0;  /* ignore inode if on a different device */
@@ -1542,69 +1537,48 @@ static int read_directory_vfat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG
     size_t len;
     KERNEL_DIRENT *de;
     union file_directory_info *info, *last_info = NULL;
+    off_t old_pos;
 
     io->u.Status = STATUS_SUCCESS;
 
     if (restart_scan) lseek( fd, 0, SEEK_SET );
 
-    if (length < max_dir_info_size(class))  /* we may have to return a partial entry here */
-    {
-        off_t old_pos = lseek( fd, 0, SEEK_CUR );
-
-        if (!(de = start_vfat_ioctl( fd ))) return -1;  /* not supported */
+    old_pos = lseek( fd, 0, SEEK_CUR );
 
-        while (de[0].d_reclen)
-        {
-            /* make sure names are null-terminated to work around an x86-64 kernel bug */
-            len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
-            de[0].d_name[len] = 0;
-            len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
-            de[1].d_name[len] = 0;
+    if (!(de = start_vfat_ioctl( fd ))) return -1;  /* not supported */
 
-            if (de[1].d_name[0])
-                info = append_entry( buffer, io, length, de[1].d_name, de[0].d_name, mask, class );
-            else
-                info = append_entry( buffer, io, length, de[0].d_name, NULL, mask, class );
-            if (info)
-            {
-                last_info = info;
-                if (io->u.Status == STATUS_BUFFER_OVERFLOW)
-                    lseek( fd, old_pos, SEEK_SET );  /* restore pos to previous entry */
-                break;
-            }
-            old_pos = lseek( fd, 0, SEEK_CUR );
-            if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
-        }
-    }
-    else  /* we'll only return full entries, no need to worry about overflow */
+    while (de[0].d_reclen)
     {
-        if (!(de = start_vfat_ioctl( fd ))) return -1;  /* not supported */
+        /* make sure names are null-terminated to work around an x86-64 kernel bug */
+        len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
+        de[0].d_name[len] = 0;
+        len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
+        de[1].d_name[len] = 0;
 
-        while (de[0].d_reclen)
+        if (de[1].d_name[0])
+            info = append_entry( buffer, io, length, de[1].d_name, de[0].d_name, mask, class );
+        else
+            info = append_entry( buffer, io, length, de[0].d_name, NULL, mask, class );
+        if (info)
         {
-            /* make sure names are null-terminated to work around an x86-64 kernel bug */
-            len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
-            de[0].d_name[len] = 0;
-            len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
-            de[1].d_name[len] = 0;
-
-            if (de[1].d_name[0])
-                info = append_entry( buffer, io, length, de[1].d_name, de[0].d_name, mask, class );
-            else
-                info = append_entry( buffer, io, length, de[0].d_name, NULL, mask, class );
-            if (info)
-            {
-                last_info = info;
-                if (single_entry) break;
-                /* check if we still have enough space for the largest possible entry */
-                if (io->Information + max_dir_info_size(class) > length) break;
-            }
-            if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
+            last_info = info;
+            if (single_entry) break;
         }
+        else if (io->u.Status == STATUS_BUFFER_OVERFLOW)
+        {
+            lseek( fd, old_pos, SEEK_SET );  /* restore pos to previous entry */
+            if (last_info)
+                io->u.Status = STATUS_SUCCESS;
+            break;
+        }
+        old_pos = lseek( fd, 0, SEEK_CUR );
+        if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
     }
 
-    if (last_info) last_info->next = 0;
-    else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
+    if (last_info)
+        last_info->next = 0;
+    else if (io->u.Status != STATUS_BUFFER_OVERFLOW)
+        io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
     return 0;
 }
 #endif /* VFAT_IOCTL_READDIR_BOTH */
@@ -1749,15 +1723,14 @@ static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, U
         if (filename && (info = append_entry( buffer, io, length, filename, NULL, mask, class )))
         {
             last_info = info;
+        }
+        else if (filename)
+        {
             if (io->u.Status == STATUS_BUFFER_OVERFLOW)
             {
                 lseek( fd, old_pos, SEEK_SET );  /* restore pos to previous entry */
-                break;
-            }
-            /* check if we still have enough space for the largest possible entry */
-            if (single_entry || io->Information + max_dir_info_size(class) > length)
-            {
-                if (res > 0) lseek( fd, next_pos, SEEK_SET );  /* set pos to next entry */
+                if (last_info)
+                    io->u.Status = STATUS_SUCCESS;
                 break;
             }
         }
@@ -1772,8 +1745,10 @@ static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, U
         }
     }
 
-    if (last_info) last_info->next = 0;
-    else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
+    if (last_info)
+        last_info->next = 0;
+    else if (io->u.Status != STATUS_BUFFER_OVERFLOW)
+        io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
     res = 0;
 done:
     if (data != local_buffer) RtlFreeHeap( GetProcessHeap(), 0, data );
@@ -1906,13 +1881,6 @@ static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buff
 
             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(class) > length)
-            {
-                lseek( fd, 0, SEEK_SET );  /* reset pos to first entry */
-                res = 0;
-            }
         }
     }
 
@@ -1920,36 +1888,41 @@ static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buff
     {
         res -= dir_reclen(de);
         if (de->d_fileno &&
-            !(fake_dot_dot && (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." ))) &&
-            ((info = append_entry( buffer, io, length, de->d_name, NULL, mask, class ))))
+            !(fake_dot_dot && (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." ))))
         {
-            last_info = info;
-            if (io->u.Status == STATUS_BUFFER_OVERFLOW)
+            if ((info = append_entry( buffer, io, length, de->d_name, NULL, mask, class )))
             {
-                lseek( fd, (unsigned long)restart_pos, SEEK_SET );
-                if (restart_info_pos)  /* if we have a complete read already, return it */
+                last_info = info;
+                if (!has_wildcard( mask )) break;
+                /* 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(class) > length))
                 {
-                    io->u.Status = STATUS_SUCCESS;
+                    lseek( fd, (unsigned long)restart_pos, SEEK_SET );
+                    size = (char *)de + dir_reclen(de) - data;
                     io->Information = restart_info_pos;
                     last_info = restart_last_info;
-                    break;
+                    goto restart;
                 }
-                /* otherwise restart from the start with a smaller size */
-                size = (char *)de - data;
-                if (!size) break;
-                io->Information = 0;
-                last_info = NULL;
-                goto restart;
             }
-            if (!has_wildcard( mask )) break;
-            /* 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(class) > length))
+            else
             {
-                lseek( fd, (unsigned long)restart_pos, SEEK_SET );
-                size = (char *)de + dir_reclen(de) - data;
-                io->Information = restart_info_pos;
-                last_info = restart_last_info;
-                goto restart;
+                if (io->u.Status == STATUS_BUFFER_OVERFLOW)
+                {
+                    lseek( fd, (unsigned long)restart_pos, SEEK_SET );
+                    if (restart_info_pos)  /* if we have a complete read already, return it */
+                    {
+                        io->u.Status = STATUS_SUCCESS;
+                        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) break;
+                    io->Information = 0;
+                    last_info = NULL;
+                    goto restart;
+                }
             }
         }
         /* move on to the next entry */
@@ -1966,8 +1939,10 @@ static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buff
         de = (struct dirent *)data;
     }
 
-    if (last_info) last_info->next = 0;
-    else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
+    if (last_info)
+        last_info->next = 0;
+    else if (io->u.Status != STATUS_BUFFER_OVERFLOW)
+        io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
     res = 0;
 done:
     if (data != local_buffer) RtlFreeHeap( GetProcessHeap(), 0, data );
@@ -2037,22 +2012,24 @@ static void read_directory_readdir( int fd, IO_STATUS_BLOCK *io, void *buffer, U
         if (info)
         {
             last_info = info;
-            if (io->u.Status == STATUS_BUFFER_OVERFLOW)
-            {
-                old_pos--;  /* restore pos to previous entry */
-                break;
-            }
             if (single_entry) break;
-            /* check if we still have enough space for the largest possible entry */
-            if (io->Information + max_dir_info_size(class) > length) break;
+        }
+        else if (io->u.Status == STATUS_BUFFER_OVERFLOW)
+        {
+            old_pos--;  /* restore pos to previous entry */
+            if (last_info)
+                io->u.Status = STATUS_SUCCESS;
+            break;
         }
     }
 
     lseek( fd, old_pos, SEEK_SET );  /* store dir offset as filepos for fd */
     closedir( dir );
 
-    if (last_info) last_info->next = 0;
-    else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
+    if (last_info)
+        last_info->next = 0;
+    else if (io->u.Status != STATUS_BUFFER_OVERFLOW)
+        io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
 }
 
 /***********************************************************************
@@ -2101,9 +2078,10 @@ static int read_directory_stat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG
             if (info)
             {
                 info->next = 0;
-                if (io->u.Status != STATUS_BUFFER_OVERFLOW) lseek( fd, 1, SEEK_CUR );
+                lseek( fd, 1, SEEK_CUR );
             }
-            else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
+            else if (io->u.Status != STATUS_BUFFER_OVERFLOW)
+                io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
         }
         else if (!case_sensitive && ret && (errno == ENOENT || errno == ENOTDIR))
         {
@@ -2195,9 +2173,10 @@ static int read_directory_getattrlist( int fd, IO_STATUS_BLOCK *io, void *buffer
             if (info)
             {
                 info->next = 0;
-                if (io->u.Status != STATUS_BUFFER_OVERFLOW) lseek( fd, 1, SEEK_CUR );
+                lseek( fd, 1, SEEK_CUR );
             }
-            else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
+            else if (io->u.Status != STATUS_BUFFER_OVERFLOW)
+                io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
         }
         else if ((errno == ENOENT || errno == ENOTDIR) && !get_dir_case_sensitivity("."))
         {
-- 
2.4.6




More information about the wine-patches mailing list