https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=6d936915477ca8f64ecba31c9876103d69827fb2
commit 6d936915477ca8f64ecba31c9876103d69827fb2 Author: Corinna Vinschen <cori...@vinschen.de> AuthorDate: Fri Mar 1 15:16:44 2024 +0100 Commit: Corinna Vinschen <cori...@vinschen.de> CommitDate: Fri Mar 1 15:32:39 2024 +0100 Cygwin: lseek: improve seeking posix_getdents descriptors Transfer code lseeking on posix_getdents() directory descriptor into its own static function and rework it so SEEK_END, SEEK_DATA and SEEK_HOLE work here as expected, too. Signed-off-by: Corinna Vinschen <cori...@vinschen.de> Diff: --- winsup/cygwin/syscalls.cc | 83 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 22 deletions(-) diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 600c6c54e343..2c5bf7cbe08b 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -1596,6 +1596,66 @@ open (const char *unix_path, int flags, ...) EXPORT_ALIAS (open, _open ) +static int +posix_getdents_lseek (cygheap_fdget &cfd, off_t pos, int dir) +{ + long cur = cfd->telldir (cfd->getdents_dir ()); + long abs_pos; + + switch (dir) + { + case SEEK_CUR: + abs_pos = cur + pos; + break; + case SEEK_SET: + case SEEK_DATA: + abs_pos = pos; + break; + case SEEK_END: + case SEEK_HOLE: + /* First read full dir to learn end-of-dir position. */ + while (::readdir (cfd->getdents_dir ())) + ; + long eod = cfd->telldir (cfd->getdents_dir ()); + /* Seek back so it looks like nothing happend in error case */ + cfd->seekdir (cfd->getdents_dir (), cur); + if (dir == SEEK_HOLE) + { + if (pos > eod) + { + set_errno (ENXIO); + return -1; + } + abs_pos = eod; + } + else + abs_pos = eod + pos; + break; + } + if (abs_pos < 0) + { + set_errno (EINVAL); + return -1; + } + if (abs_pos != cur) + { + cfd->seekdir (cfd->getdents_dir (), abs_pos); + /* In SEEK_DATA case, check that we didn't seek beyond EOF */ + if (dir == SEEK_DATA || dir == SEEK_HOLE) + { + pos = cfd->telldir (cfd->getdents_dir ()); + if (pos < abs_pos) + { + /* Seek back so it looks like nothing happend */ + cfd->seekdir (cfd->getdents_dir (), cur); + set_errno (ENXIO); + return -1; + } + } + } + return abs_pos; +} + extern "C" off_t lseek (int fd, off_t pos, int dir) { @@ -1612,28 +1672,7 @@ lseek (int fd, off_t pos, int dir) if (cfd < 0) res = -1; else if (cfd->getdents_dir ()) - { - if (dir != SEEK_SET && dir != SEEK_CUR) /* No SEEK_END */ - { - set_errno (EINVAL); - res = -1; - } - else - { - long cur; - - cur = cfd->telldir (cfd->getdents_dir ()); - if (dir == SEEK_CUR && cur == 0) - res = cur; - else - { - if (dir == SEEK_CUR) - pos = cur + pos; - cfd->seekdir (cfd->getdents_dir (), pos); - res = pos; - } - } - } + res = posix_getdents_lseek (cfd, pos, dir); else res = cfd->lseek (pos, dir); }