From: Matteo Bruni Subject: [PATCH 1/4] ntdll: Make append_entry fail if the new entry doesn't fit. Message-Id: <1440702521-3673-1-git-send-email-mbruni@codeweavers.com> Date: Thu, 27 Aug 2015 21:08:38 +0200 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