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>
This commit is contained in:
Corinna Vinschen 2024-01-23 21:49:48 +01:00
parent 219b2dff77
commit 62ca95721a
9 changed files with 131 additions and 8 deletions

View File

@ -80,6 +80,9 @@ int scandirat(int, const char *, struct dirent ***,
const struct dirent **)); const struct dirent **));
int versionsort(const struct dirent **, const struct dirent **); int versionsort(const struct dirent **, const struct dirent **);
#endif #endif
#if __POSIX_VISIBLE >= 200809
ssize_t posix_getdents(int, void *, size_t, int);
#endif /* __POSIX_VISIBLE >= 200809 */
__END_DECLS __END_DECLS
#endif /*_DIRENT_H_*/ #endif /*_DIRENT_H_*/

View File

@ -1046,6 +1046,7 @@ poll SIGFE
popen SIGFE popen SIGFE
posix_fadvise SIGFE posix_fadvise SIGFE
posix_fallocate SIGFE posix_fallocate SIGFE
posix_getdents SIGFE
posix_madvise SIGFE posix_madvise SIGFE
posix_memalign SIGFE posix_memalign SIGFE
posix_openpt SIGFE posix_openpt SIGFE

View File

@ -9,6 +9,7 @@ details. */
#include "winsup.h" #include "winsup.h"
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h>
#define _LIBC #define _LIBC
#include <dirent.h> #include <dirent.h>
@ -201,6 +202,63 @@ readdir_r (DIR *__restrict dir, dirent *__restrict de, dirent **__restrict ode)
return res; 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 */ /* telldir */
extern "C" long extern "C" long
telldir (DIR *dir) telldir (DIR *dir)

View File

@ -1316,6 +1316,7 @@ fhandler_base::close ()
paranoid_printf ("CloseHandle failed, %E"); paranoid_printf ("CloseHandle failed, %E");
__seterrno (); __seterrno ();
} }
clear_getdents ();
return res; return res;
} }
@ -1432,7 +1433,10 @@ fhandler_base::dup (fhandler_base *child, int flags)
VerifyHandle (nh); VerifyHandle (nh);
child->set_handle (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; return 0;
} }
@ -1632,6 +1636,7 @@ fhandler_base::fixup_after_fork (HANDLE parent)
/* POSIX locks are not inherited across fork. */ /* POSIX locks are not inherited across fork. */
if (unique_id) if (unique_id)
del_my_locks (after_fork); del_my_locks (after_fork);
clear_getdents ();
} }
void void
@ -1640,6 +1645,7 @@ fhandler_base::fixup_after_exec ()
debug_printf ("here for '%s'", get_name ()); debug_printf ("here for '%s'", get_name ());
if (unique_id && close_on_exec ()) if (unique_id && close_on_exec ())
del_my_locks (after_exec); del_my_locks (after_exec);
getdents_dir (NULL);
mandatory_locking (false); mandatory_locking (false);
} }

View File

@ -488,12 +488,13 @@ details. */
351: Add getlocalename_l. 351: Add getlocalename_l.
352: Implement dirent.d_reclen. 352: Implement dirent.d_reclen.
353: Implement fdclosedir. 353: Implement fdclosedir.
354: Implement posix_getdents.
Note that we forgot to bump the api for ualarm, strtoll, strtoull, Note that we forgot to bump the api for ualarm, strtoll, strtoull,
sigaltstack, sethostname. */ sigaltstack, sethostname. */
#define CYGWIN_VERSION_API_MAJOR 0 #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 /* There is also a compatibity version number associated with the shared memory
regions. It is incremented when incompatible changes are made to the shared regions. It is incremented when incompatible changes are made to the shared

View File

@ -37,14 +37,30 @@ struct dirent
char d_name[NAME_MAX + 1]; 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 */ #define d_fileno d_ino /* BSD compatible definition */
typedef struct __DIR DIR; typedef struct __DIR DIR;
#if __BSD_VISIBLE #if __BSD_VISIBLE || __POSIX_VISIBLE >= 200809
#ifdef _DIRENT_HAVE_D_TYPE
/* File types for `d_type'. */ /* File types for `d_type'. */
enum enum
{ {
@ -67,10 +83,11 @@ enum
DT_WHT = 14 DT_WHT = 14
# define DT_WHT DT_WHT # define DT_WHT DT_WHT
}; };
#endif /* __BSD_VISIBLE || __POSIX_VISIBLE >= 200809 */
#if __BSD_VISIBLE
/* Convert between stat structure types and directory types. */ /* Convert between stat structure types and directory types. */
# define IFTODT(mode) (((mode) & 0170000) >> 12) # define IFTODT(mode) (((mode) & 0170000) >> 12)
# define DTTOIF(dirtype) ((dirtype) << 12) # define DTTOIF(dirtype) ((dirtype) << 12)
#endif /* _DIRENT_HAVE_D_TYPE */
#endif /* __BSD_VISIBLE */ #endif /* __BSD_VISIBLE */
#endif /*_SYS_DIRENT_H*/ #endif /*_SYS_DIRENT_H*/

View File

@ -214,6 +214,9 @@ class fhandler_base
struct rabuf_t ra; struct rabuf_t ra;
/* Used for posix_getdents () */
DIR *_getdents_dir;
/* Used for advisory file locking. See flock.cc. */ /* Used for advisory file locking. See flock.cc. */
int64_t unique_id; int64_t unique_id;
void del_my_locks (del_lock_called_from); void del_my_locks (del_lock_called_from);
@ -526,6 +529,17 @@ public:
} }
HANDLE get_select_sem () { return select_sem; } 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 struct wsa_event

View File

@ -3,7 +3,7 @@ What's new:
- New API call: getlocalename_l. - New API call: getlocalename_l.
- New API call: fdclosedir. - New API calls: fdclosedir, posix_getdents.
What changed: What changed:

View File

@ -1609,10 +1609,33 @@ lseek (int fd, off_t pos, int dir)
else else
{ {
cygheap_fdget cfd (fd); cygheap_fdget cfd (fd);
if (cfd >= 0) if (cfd < 0)
res = cfd->lseek (pos, dir);
else
res = -1; 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 */ /* Can't use %R/%lR here since res is always 8 bytes */
syscall_printf (res == -1 ? "%D = lseek(%d, %D, %d), errno %d" syscall_printf (res == -1 ? "%D = lseek(%d, %D, %d), errno %d"