public inbox for newlib-cvs@sourceware.org
help / color / mirror / Atom feed
* [newlib-cygwin/main] Cygwin: posix_getdents: implement per SUS Base Specifications Issue 8 draft
@ 2024-02-01 11:39 Corinna Vinschen
  0 siblings, 0 replies; only message in thread
From: Corinna Vinschen @ 2024-02-01 11:39 UTC (permalink / raw)
  To: cygwin-cvs, newlib-cvs

https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=62ca95721a14f43c680f3c4d77a872a607b553a6

commit 62ca95721a14f43c680f3c4d77a872a607b553a6
Author:     Corinna Vinschen <corinna@vinschen.de>
AuthorDate: Tue Jan 23 21:49:48 2024 +0100
Commit:     Corinna Vinschen <corinna@vinschen.de>
CommitDate: Wed Jan 31 20:11:58 2024 +0100

    Cygwin: posix_getdents: implement per SUS Base Specifications Issue 8 draft
    
    - Basically maintain a hidden DIR* inside fhandlers.
    
    - lseek has to be tweaked to allow basic seeking on the directory
      descriptor.
    
    - the current implementation does not keep the dir positions
      between duplicated descriptor in sync.  In fact, every descriptor
      keeps its own copy of the DIR* and after dup/fork/exec, the
      directory position is reset to 0, i. e., to the start of the
      directory, as if rewinddir() has been called on the new descriptors.
    
      While this behaviour isn't yet covered by the Issue 8 draft,
      a bug report along these lines exists and will probably be
      picked up for TC1.
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 newlib/libc/include/dirent.h            |  3 ++
 winsup/cygwin/cygwin.din                |  1 +
 winsup/cygwin/dir.cc                    | 58 +++++++++++++++++++++++++++++++++
 winsup/cygwin/fhandler/base.cc          |  6 ++++
 winsup/cygwin/include/cygwin/version.h  |  3 +-
 winsup/cygwin/include/sys/dirent.h      | 23 +++++++++++--
 winsup/cygwin/local_includes/fhandler.h | 14 ++++++++
 winsup/cygwin/release/3.6.0             |  2 +-
 winsup/cygwin/syscalls.cc               | 29 +++++++++++++++--
 9 files changed, 131 insertions(+), 8 deletions(-)

diff --git a/newlib/libc/include/dirent.h b/newlib/libc/include/dirent.h
index cdfa21fa7835..cc7e9e0f09cb 100644
--- a/newlib/libc/include/dirent.h
+++ b/newlib/libc/include/dirent.h
@@ -80,6 +80,9 @@ int	 scandirat(int, const char *, struct dirent ***,
 	    const struct dirent **));
 int	 versionsort(const struct dirent **, const struct dirent **);
 #endif
+#if __POSIX_VISIBLE >= 200809
+ssize_t	posix_getdents(int, void *, size_t, int);
+#endif /* __POSIX_VISIBLE >= 200809 */
 __END_DECLS
 
 #endif /*_DIRENT_H_*/
diff --git a/winsup/cygwin/cygwin.din b/winsup/cygwin/cygwin.din
index 049be4cd0377..6bac40cc066f 100644
--- a/winsup/cygwin/cygwin.din
+++ b/winsup/cygwin/cygwin.din
@@ -1046,6 +1046,7 @@ poll SIGFE
 popen SIGFE
 posix_fadvise SIGFE
 posix_fallocate SIGFE
+posix_getdents SIGFE
 posix_madvise SIGFE
 posix_memalign SIGFE
 posix_openpt SIGFE
diff --git a/winsup/cygwin/dir.cc b/winsup/cygwin/dir.cc
index 82797a41f4c1..c2cba839eb5a 100644
--- a/winsup/cygwin/dir.cc
+++ b/winsup/cygwin/dir.cc
@@ -9,6 +9,7 @@ details. */
 #include "winsup.h"
 #include <stdlib.h>
 #include <unistd.h>
+#include <sys/stat.h>
 
 #define _LIBC
 #include <dirent.h>
@@ -201,6 +202,63 @@ readdir_r (DIR *__restrict dir, dirent *__restrict de, dirent **__restrict ode)
   return res;
 }
 
+/* Not exposed through sys/stat.h when building Cygwin */
+extern "C" int fstatat (int, const char *__restrict ,
+			struct stat *__restrict, int);
+
+extern "C"
+ssize_t posix_getdents(int fd, void *buf, size_t nbytes, int flags)
+{
+  struct posix_dent *dent_buf, *src;
+  ssize_t cnt = 0;
+
+  cygheap_fdget cfd (fd);
+  /* Valid descriptor? */
+  if (cfd < 0)
+    return -1;
+  /* Valid flags?  Right now only DT_FORCE_TYPE is defined */
+  if ((flags & ~DT_FORCE_TYPE) != 0)
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+  /* Create type-safe buffer pointer */
+  dent_buf = (struct posix_dent *) buf;
+  /* Check if nbytes is big enough to fit at least one struct posix_dent */
+  if (nbytes < sizeof (struct posix_dent))
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+  /* Create internal DIR * on first invocation */
+  if (!cfd->getdents_dir ())
+    {
+      cfd->getdents_dir (cfd->opendir (fd));
+      if (!cfd->getdents_dir ())
+	return -1;
+    }
+  /* Now loop until EOF or buf is full */
+  while (nbytes >= sizeof (struct posix_dent))
+    {
+      /* Our struct posix_dent is identical to struct dirent */
+      src = (struct posix_dent *) readdir (cfd->getdents_dir ());
+      if (!src)
+	break;
+      /* Handle the suggested DT_FORCE_TYPE flag */
+      if (src->d_type == DT_UNKNOWN && (flags & DT_FORCE_TYPE))
+	{
+	  struct stat st;
+
+	  if (!fstatat (fd, src->d_name, &st, AT_SYMLINK_NOFOLLOW))
+	    src->d_type = IFTODT (st.st_mode);
+	}
+      *dent_buf++ = *src;
+      ++cnt;
+      nbytes -= sizeof (struct posix_dent);
+    }
+  return cnt * sizeof (struct posix_dent);
+}
+
 /* telldir */
 extern "C" long
 telldir (DIR *dir)
diff --git a/winsup/cygwin/fhandler/base.cc b/winsup/cygwin/fhandler/base.cc
index f1ad375228f8..643236751ab1 100644
--- a/winsup/cygwin/fhandler/base.cc
+++ b/winsup/cygwin/fhandler/base.cc
@@ -1316,6 +1316,7 @@ fhandler_base::close ()
       paranoid_printf ("CloseHandle failed, %E");
       __seterrno ();
     }
+  clear_getdents ();
   return res;
 }
 
@@ -1432,7 +1433,10 @@ fhandler_base::dup (fhandler_base *child, int flags)
 
       VerifyHandle (nh);
       child->set_handle (nh);
+      /* Just set to NULL, the struct is potentially still valid
+	 in the parent fhandler. */
     }
+  child->getdents_dir (NULL);
   return 0;
 }
 
@@ -1632,6 +1636,7 @@ fhandler_base::fixup_after_fork (HANDLE parent)
   /* POSIX locks are not inherited across fork. */
   if (unique_id)
     del_my_locks (after_fork);
+  clear_getdents ();
 }
 
 void
@@ -1640,6 +1645,7 @@ fhandler_base::fixup_after_exec ()
   debug_printf ("here for '%s'", get_name ());
   if (unique_id && close_on_exec ())
     del_my_locks (after_exec);
+  getdents_dir (NULL);
   mandatory_locking (false);
 }
 
diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h
index e21e04addd8c..6679eedd9505 100644
--- a/winsup/cygwin/include/cygwin/version.h
+++ b/winsup/cygwin/include/cygwin/version.h
@@ -488,12 +488,13 @@ details. */
   351: Add getlocalename_l.
   352: Implement dirent.d_reclen.
   353: Implement fdclosedir.
+  354: Implement posix_getdents.
 
   Note that we forgot to bump the api for ualarm, strtoll, strtoull,
   sigaltstack, sethostname. */
 
 #define CYGWIN_VERSION_API_MAJOR 0
-#define CYGWIN_VERSION_API_MINOR 353
+#define CYGWIN_VERSION_API_MINOR 354
 
 /* There is also a compatibity version number associated with the shared memory
    regions.  It is incremented when incompatible changes are made to the shared
diff --git a/winsup/cygwin/include/sys/dirent.h b/winsup/cygwin/include/sys/dirent.h
index dae324a7f6f6..6272ea329c60 100644
--- a/winsup/cygwin/include/sys/dirent.h
+++ b/winsup/cygwin/include/sys/dirent.h
@@ -37,14 +37,30 @@ struct dirent
   char		d_name[NAME_MAX + 1];
 };
 
+#if __POSIX_VISIBLE >= 200809
+#define DT_FORCE_TYPE	0x01	/* Suggested by SUS Base Specs Issue 8 */
+
+typedef __uint16_t reclen_t;
+
+/* This is a drop-in replacement for DIR, but used from posix_getdent()
+   per SUS Base Specs Issue 8 */
+struct posix_dent
+{
+  __uint32_t	__d_version;
+  ino_t		d_ino;
+  unsigned char	d_type;
+  unsigned char	__d_unused1[1];
+  reclen_t	d_reclen;
+  __uint32_t	__d_internal1;
+  char		d_name[NAME_MAX + 1];
 };
+#endif /* __POSIX_VISIBLE >= 200809 */
 
 #define d_fileno d_ino			/* BSD compatible definition */
 
 typedef struct __DIR DIR;
 
-#if __BSD_VISIBLE
-#ifdef _DIRENT_HAVE_D_TYPE
+#if __BSD_VISIBLE || __POSIX_VISIBLE >= 200809
 /* File types for `d_type'.  */
 enum
 {
@@ -67,10 +83,11 @@ enum
   DT_WHT = 14
 # define DT_WHT         DT_WHT
 };
+#endif /* __BSD_VISIBLE || __POSIX_VISIBLE >= 200809 */
 
+#if __BSD_VISIBLE
 /* Convert between stat structure types and directory types.  */
 # define IFTODT(mode)		(((mode) & 0170000) >> 12)
 # define DTTOIF(dirtype)        ((dirtype) << 12)
-#endif /* _DIRENT_HAVE_D_TYPE */
 #endif /* __BSD_VISIBLE */
 #endif /*_SYS_DIRENT_H*/
diff --git a/winsup/cygwin/local_includes/fhandler.h b/winsup/cygwin/local_includes/fhandler.h
index 1dc02608ba8a..8e30884478d7 100644
--- a/winsup/cygwin/local_includes/fhandler.h
+++ b/winsup/cygwin/local_includes/fhandler.h
@@ -214,6 +214,9 @@ class fhandler_base
 
   struct rabuf_t ra;
 
+  /* Used for posix_getdents () */
+  DIR *_getdents_dir;
+
   /* Used for advisory file locking.  See flock.cc.  */
   int64_t unique_id;
   void del_my_locks (del_lock_called_from);
@@ -526,6 +529,17 @@ public:
   }
 
   HANDLE get_select_sem () { return select_sem; }
+
+  DIR *getdents_dir () const { return _getdents_dir; }
+  DIR *getdents_dir (DIR *_nd) { return _getdents_dir = _nd; }
+  void clear_getdents ()
+  {
+    if (getdents_dir ())
+      {
+	fdclosedir (getdents_dir ());
+	getdents_dir (NULL);
+      }
+  }
 };
 
 struct wsa_event
diff --git a/winsup/cygwin/release/3.6.0 b/winsup/cygwin/release/3.6.0
index fe933d8c74cc..f7754bd19417 100644
--- a/winsup/cygwin/release/3.6.0
+++ b/winsup/cygwin/release/3.6.0
@@ -3,7 +3,7 @@ What's new:
 
 - New API call: getlocalename_l.
 
-- New API call: fdclosedir.
+- New API calls: fdclosedir, posix_getdents.
 
 
 What changed:
diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
index 9d88b60b0f92..600c6c54e343 100644
--- a/winsup/cygwin/syscalls.cc
+++ b/winsup/cygwin/syscalls.cc
@@ -1609,10 +1609,33 @@ lseek (int fd, off_t pos, int dir)
   else
     {
       cygheap_fdget cfd (fd);
-      if (cfd >= 0)
-	res = cfd->lseek (pos, dir);
-      else
+      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;
+		}
+	    }
+	}
+      else
+	res = cfd->lseek (pos, dir);
     }
   /* Can't use %R/%lR here since res is always 8 bytes */
   syscall_printf (res == -1 ? "%D = lseek(%d, %D, %d), errno %d"

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2024-02-01 11:39 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-02-01 11:39 [newlib-cygwin/main] Cygwin: posix_getdents: implement per SUS Base Specifications Issue 8 draft Corinna Vinschen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).