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);
     }

Reply via email to