diff options
Diffstat (limited to 'winsup/cygwin')
-rw-r--r-- | winsup/cygwin/cygwin.din | 1 | ||||
-rw-r--r-- | winsup/cygwin/dir.cc | 58 | ||||
-rw-r--r-- | winsup/cygwin/fhandler/base.cc | 6 | ||||
-rw-r--r-- | winsup/cygwin/include/cygwin/version.h | 3 | ||||
-rw-r--r-- | winsup/cygwin/include/sys/dirent.h | 23 | ||||
-rw-r--r-- | winsup/cygwin/local_includes/fhandler.h | 14 | ||||
-rw-r--r-- | winsup/cygwin/release/3.6.0 | 2 | ||||
-rw-r--r-- | winsup/cygwin/syscalls.cc | 29 |
8 files changed, 128 insertions, 8 deletions
diff --git a/winsup/cygwin/cygwin.din b/winsup/cygwin/cygwin.din index 049be4c..6bac40c 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 82797a4..c2cba83 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 f1ad375..6432367 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 e21e04a..6679eed 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 dae324a..6272ea3 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 1dc0260..8e30884 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 fe933d8..f7754bd 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 9d88b60..600c6c5 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" |