From: David Naylor Subject: ntdll: fix read_directory_getdirentries() under FreeBSD. Message-Id: <19721289.rsjA3tMgOa@dragon.dg> Date: Sun, 28 Dec 2014 23:09:01 +0300 Change the handling of getdirentries(2) by: - tracking the data pointer instead of subtracting 'res' - simplify rewind by skipping re-appending entries onto the list - try make the logic easier to follow --- dlls/ntdll/directory.c | 73 +++++++++++++++++++------------------------------- 1 file changed, 28 insertions(+), 45 deletions(-) diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c index 9b5852a..668ad56 100644 --- a/dlls/ntdll/directory.c +++ b/dlls/ntdll/directory.c @@ -1849,12 +1849,11 @@ 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; + int res, fake_dot_dot = 1, stop_get = 0; char *data, local_buffer[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; @@ -1908,9 +1907,6 @@ static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buff if ((info = append_entry( buffer, io, length, "..", NULL, mask, class ))) 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(class) > length) { @@ -1920,54 +1916,41 @@ static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buff } } - while (res > 0) + do { - 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 )))) + while ((char *)de - data < res && !stop_get) { - last_info = info; - if (io->u.Status == STATUS_BUFFER_OVERFLOW) + 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 )))) { - 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 (io->u.Status == STATUS_BUFFER_OVERFLOW) { - io->u.Status = STATUS_SUCCESS; - io->Information = restart_info_pos; - last_info = restart_last_info; - break; + stop_get = 1; + break; /* do not update de as the current entry was not added */ } - /* 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)) - { - 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; + /* FIXME: why is this here and not in read_directory_readdir or read_directory_getdents? */ + if (!has_wildcard( mask )) break; + + if (single_entry || io->Information + max_dir_info_size(class) > length) stop_get = 1; } + /* move on to the next entry */ + de = (struct dirent *)((char *)de + dir_reclen(de)); } - /* move on to the next entry */ - if (res > 0) + if (!stop_get) { - de = (struct dirent *)((char *)de + dir_reclen(de)); - continue; + res = wine_getdirentries( fd, data, size, &restart_pos ); + de = (struct dirent *)data; } - 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; + } + while (res > 0 && !stop_get); + + /* Rewind fd to resume at the current de */ + if (stop_get && (char *)de - data < res) + { + lseek( fd, restart_pos, SEEK_SET); + wine_getdirentries( fd, data, (char *)de - data, &restart_pos ); } if (last_info) last_info->next = 0; -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (FreeBSD) iQJ8BAABCgBmBQJUoHFwXxSAAAAAAC4AKGlzc3Vlci1mcHJAbm90YXRpb25zLm9w ZW5wZ3AuZmlmdGhob3JzZW1hbi5uZXQ5NDhFQzUxMUEyN0YwMzAyRTc5OUI1M0FB RDYzRkE4REQ2QjJEQTU1AAoJEK1j+o3WstpV2nUQALL5VXSJwxr7GECXO/l6tSm9 U/HfBRMpTikaiDUNnky5ZdJO4CtgqI9qRzPbDR/cCapvgoX4AeBD/VsEDfqU4Dgb p3yXKOSlYo3buYgfHqzV0bvpK6IHqKGj+D58roctBymUri1IDzUqimbllohDsSB5 4CUEZuHnRqHA7Tpoc7EmvTC37f+dF1GPdkztBT4PdQhzv00EtQurcG1zZeJmmVrl 83mm9lb/07lS6scUqXCjPPZxOgIkDazWomsQTvmxU7nG4jM76l9PX9R9Wc+98eyn Ol+khECKYdzbP+InCiU1hUPHTQFFmg0LjE322ryzG3x2MuQBXKUSNA8KH1EigcHT /tVg1P9WexvDBLYmJEl+eq7DlFqhRPCMt7Xnz/TUe1ffFsjRdvyV+7hebc49y7Tm JePceBY9FfFj/aggLXKYjEOT6YY0zLJCTrbc7MyEyaPZfdg7FGXbK295GNbMJoqt 3I2oLjSYajEuvb5zQx6b3JUw3mp9SILUm/UAnSGG3EI8gJ3uYBZCg2KH+e/Rhq9v vduQHWccikcjzxSM8MqHqP48p5pvGFQLo90LeLusXC9ZAnGdVIYrBjE/GVw1UWEH KbQX+Fhk03AOb9AfPLk7M4S5FWdBUiomM1Kxz3G8D5l3O1wlqOU6a37XbRVgm7V3 AEqs+IStSQjWEMgULyOx =lld4 -----END PGP SIGNATURE-----