From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2155) id 94248385802B; Thu, 1 Feb 2024 11:39:29 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 94248385802B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1706787569; bh=H1zaZO3MZrji6viHUSMOzXNAZen2AnDGaWb+Oh2P3us=; h=From:To:Subject:Date:From; b=kBKy3ys2mzytsroBxaYdOKQKmgfH8tM9wZQLXZzzAGmS7KqqDJzznOWygLVj0PcJt jUJRD7RXujs7284cSmw8XKMTDzm49u4LoHLDfT32etwYWoeznYVsJpM6yMKp+J1MSX QQnKxHmLZzds61canWgxZso9nZ3KG4U4smMNyIHI= Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable From: Corinna Vinschen To: cygwin-cvs@sourceware.org, newlib-cvs@sourceware.org Subject: [newlib-cygwin/main] Cygwin: posix_getdents: implement per SUS Base Specifications Issue 8 draft X-Act-Checkin: newlib-cygwin X-Git-Author: Corinna Vinschen X-Git-Refname: refs/heads/main X-Git-Oldrev: 219b2dff771d01d7be8e03adf068ac3b69a89363 X-Git-Newrev: 62ca95721a14f43c680f3c4d77a872a607b553a6 Message-Id: <20240201113929.94248385802B@sourceware.org> Date: Thu, 1 Feb 2024 11:39:29 +0000 (GMT) List-Id: https://sourceware.org/git/gitweb.cgi?p=3Dnewlib-cygwin.git;h=3D62ca95721a1= 4f43c680f3c4d77a872a607b553a6 commit 62ca95721a14f43c680f3c4d77a872a607b553a6 Author: Corinna Vinschen AuthorDate: Tue Jan 23 21:49:48 2024 +0100 Commit: Corinna Vinschen CommitDate: Wed Jan 31 20:11:58 2024 +0100 Cygwin: posix_getdents: implement per SUS Base Specifications Issue 8 d= raft =20 - Basically maintain a hidden DIR* inside fhandlers. =20 - lseek has to be tweaked to allow basic seeking on the directory descriptor. =20 - 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. =20 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. =20 Signed-off-by: Corinna Vinschen 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 >=3D 200809 +ssize_t posix_getdents(int, void *, size_t, int); +#endif /* __POSIX_VISIBLE >=3D 200809 */ __END_DECLS =20 #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 #include +#include =20 #define _LIBC #include @@ -201,6 +202,63 @@ readdir_r (DIR *__restrict dir, dirent *__restrict de,= dirent **__restrict ode) return res; } =20 +/* 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 =3D 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) !=3D 0) + { + set_errno (EINVAL); + return -1; + } + /* Create type-safe buffer pointer */ + dent_buf =3D (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 >=3D sizeof (struct posix_dent)) + { + /* Our struct posix_dent is identical to struct dirent */ + src =3D (struct posix_dent *) readdir (cfd->getdents_dir ()); + if (!src) + break; + /* Handle the suggested DT_FORCE_TYPE flag */ + if (src->d_type =3D=3D DT_UNKNOWN && (flags & DT_FORCE_TYPE)) + { + struct stat st; + + if (!fstatat (fd, src->d_name, &st, AT_SYMLINK_NOFOLLOW)) + src->d_type =3D IFTODT (st.st_mode); + } + *dent_buf++ =3D *src; + ++cnt; + nbytes -=3D 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; } =20 @@ -1432,7 +1433,10 @@ fhandler_base::dup (fhandler_base *child, int flags) =20 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; } =20 @@ -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 (); } =20 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); } =20 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. =20 Note that we forgot to bump the api for ualarm, strtoll, strtoull, sigaltstack, sethostname. */ =20 #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 353 +#define CYGWIN_VERSION_API_MINOR 354 =20 /* There is also a compatibity version number associated with the shared m= emory regions. It is incremented when incompatible changes are made to the s= hared 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]; }; =20 +#if __POSIX_VISIBLE >=3D 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 >=3D 200809 */ =20 #define d_fileno d_ino /* BSD compatible definition */ =20 typedef struct __DIR DIR; =20 -#if __BSD_VISIBLE -#ifdef _DIRENT_HAVE_D_TYPE +#if __BSD_VISIBLE || __POSIX_VISIBLE >=3D 200809 /* File types for `d_type'. */ enum { @@ -67,10 +83,11 @@ enum DT_WHT =3D 14 # define DT_WHT DT_WHT }; +#endif /* __BSD_VISIBLE || __POSIX_VISIBLE >=3D 200809 */ =20 +#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 =20 struct rabuf_t ra; =20 + /* 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: } =20 HANDLE get_select_sem () { return select_sem; } + + DIR *getdents_dir () const { return _getdents_dir; } + DIR *getdents_dir (DIR *_nd) { return _getdents_dir =3D _nd; } + void clear_getdents () + { + if (getdents_dir ()) + { + fdclosedir (getdents_dir ()); + getdents_dir (NULL); + } + } }; =20 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: =20 - New API call: getlocalename_l. =20 -- New API call: fdclosedir. +- New API calls: fdclosedir, posix_getdents. =20 =20 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 >=3D 0) - res =3D cfd->lseek (pos, dir); - else + if (cfd < 0) res =3D -1; + else if (cfd->getdents_dir ()) + { + if (dir !=3D SEEK_SET && dir !=3D SEEK_CUR) /* No SEEK_END */ + { + set_errno (EINVAL); + res =3D -1; + } + else + { + long cur; + + cur =3D cfd->telldir (cfd->getdents_dir ()); + if (dir =3D=3D SEEK_CUR && cur =3D=3D 0) + res =3D cur; + else + { + if (dir =3D=3D SEEK_CUR) + pos =3D cur + pos; + cfd->seekdir (cfd->getdents_dir (), pos); + res =3D pos; + } + } + } + else + res =3D cfd->lseek (pos, dir); } /* Can't use %R/%lR here since res is always 8 bytes */ syscall_printf (res =3D=3D -1 ? "%D =3D lseek(%d, %D, %d), errno %d"