From: Matteo Bruni Subject: [PATCH 2/4] ntdll: Fix read_directory_getdirentries(). Message-Id: <1440702521-3673-2-git-send-email-mbruni@codeweavers.com> Date: Thu, 27 Aug 2015 21:08:39 +0200 --- The man page for getdirentries mentions that only positions previously returned from lseek or getdirentries should be used, I'm arguably abusing the pointer to store the index and then restoring the position using SEEK_CUR. It seems to work fine for me, not sure if that might cause issues with some specific filesystem setups. --- dlls/ntdll/directory.c | 143 ++++++++++++++++++------------------------------- 1 file changed, 53 insertions(+), 90 deletions(-) diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c index f9562d2..eb6cd45 100644 --- a/dlls/ntdll/directory.c +++ b/dlls/ntdll/directory.c @@ -1820,27 +1820,27 @@ static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buff BOOLEAN restart_scan, FILE_INFORMATION_CLASS class ) { long restart_pos; - ULONG_PTR restart_info_pos = 0; - size_t size, initial_size = length; - int res, fake_dot_dot = 1; - char *data, local_buffer[8192]; + off_t newcursor; + int total, res, i; + char data[8192]; struct dirent *de; - union file_directory_info *info, *last_info = NULL, *restart_last_info = NULL; + union file_directory_info *info, *last_info = NULL; - size = initial_size; - data = local_buffer; - if (size > sizeof(local_buffer) && !(data = RtlAllocateHeap( GetProcessHeap(), 0, size ))) + io->u.Status = STATUS_SUCCESS; + + if (restart_scan) { - io->u.Status = STATUS_NO_MEMORY; - return io->u.Status; + lseek( fd, 0, SEEK_SET ); + i = 0; + } + else + { + i = lseek( fd, 0, SEEK_CUR ); + lseek( fd, 0, SEEK_SET ); + newcursor = lseek( fd, i < 2 ? 0 : i - 2, SEEK_CUR ); } - 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 = wine_getdirentries( fd, data, size, &restart_pos ); + total = res = wine_getdirentries( fd, data, sizeof(data), &restart_pos ); if (res == -1) { io->u.Status = FILE_GetNtStatus(); @@ -1850,102 +1850,65 @@ static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buff de = (struct dirent *)data; - if (restart_scan) + for (;;) { - /* check if we got . and .. from getdirentries */ - if (res > 0) + if (!i) { - if (!strcmp( de->d_name, "." ) && res > dir_reclen(de)) - { - struct dirent *next_de = (struct dirent *)(data + dir_reclen(de)); - if (!strcmp( next_de->d_name, ".." )) fake_dot_dot = 0; - } + info = append_entry( buffer, io, length, ".", NULL, mask, class ); } - /* make sure we have enough room for both entries */ - if (fake_dot_dot) + else if (i == 1) { - const ULONG min_info_size = dir_info_size( class, 1 ) + dir_info_size( class, 2 ); - 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; - } + info = append_entry( buffer, io, length, "..", NULL, mask, class ); } - - if (fake_dot_dot) + else if (res > 0) + { + res -= dir_reclen(de); + if (de->d_fileno && strcmp( de->d_name, "." ) && strcmp( de->d_name, ".." )) + info = append_entry( buffer, io, length, de->d_name, NULL, mask, class ); + else + info = NULL; + de = (struct dirent *)((char *)de + dir_reclen(de)); + } + else { - if ((info = append_entry( buffer, io, length, ".", NULL, mask, class ))) - last_info = info; - if ((info = append_entry( buffer, io, length, "..", NULL, mask, class ))) - last_info = info; + info = NULL; + } + i++; - restart_last_info = last_info; - restart_info_pos = io->Information; + if (info) + { + last_info = info; + if (single_entry) + break; + } + else if (io->u.Status == STATUS_BUFFER_OVERFLOW) + { + i--; + if (last_info) + io->u.Status = STATUS_SUCCESS; + break; } - } - while (res > 0) - { - res -= dir_reclen(de); - if (de->d_fileno && - !(fake_dot_dot && (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )))) + if (!res) { - if ((info = append_entry( buffer, io, length, de->d_name, NULL, mask, class ))) + if (total >= sizeof(struct dirent)) { - 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)) - { - 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; - } + total = res = wine_getdirentries( fd, data, sizeof(data), &restart_pos ); + de = (struct dirent *)data; } else - { - 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 */ - if (res > 0) - { - de = (struct dirent *)((char *)de + dir_reclen(de)); - continue; + break; } - if (size < initial_size) break; /* already restarted once, give up now */ - restart_last_info = last_info; - restart_info_pos = io->Information; - restart: - res = wine_getdirentries( fd, data, size, &restart_pos ); - de = (struct dirent *)data; } + /* Store the entry counter in the fd offset. */ + lseek( fd, i, SEEK_SET ); 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 ); return res; } -- 2.4.6