From de1af8654d6e0a47559daf8585106ab90bc7ecef Mon Sep 17 00:00:00 2001 From: Steven Shuck Date: Sat, 5 Jul 2025 18:05:55 -0400 Subject: [PATCH] Remove readDirStreamWithPtr; replace readdir_r() with readdir() --- System/Posix/Directory/Common.hsc | 56 ++++++--------------- System/Posix/Directory/Internals.hsc | 1 - cbits/HsUnix.c | 74 ---------------------------- 3 files changed, 14 insertions(+), 117 deletions(-) diff --git a/System/Posix/Directory/Common.hsc b/System/Posix/Directory/Common.hsc index b50592b..21d81ce 100644 --- a/System/Posix/Directory/Common.hsc +++ b/System/Posix/Directory/Common.hsc @@ -53,7 +53,6 @@ module System.Posix.Directory.Common ( getRealDirType, unsafeOpenDirStreamFd, readDirStreamWith, - readDirStreamWithPtr, rewindDirStream, closeDirStream, @@ -289,46 +288,23 @@ foreign import capi unsafe "dirent.h fdopendir" -- if an entry was read and @Nothing@ if the end of the directory stream was -- reached. -- --- __NOTE:__ The lifetime of the pointer wrapped in the `DirEnt` is limited to --- invocation of the callback and it will be freed automatically after. Do not --- pass it to the outside world! +-- __NOTE:__ Accessing the `DirEnt` is not guaranteed to be valid after any +-- subsequent operations on the same `DirStream`. To be safe, do not pass +-- references to the `DirEnt` to the outside world. -- --- @since 2.8.6.0 -readDirStreamWith :: (DirEnt -> IO a) -> DirStream -> IO (Maybe a) -readDirStreamWith f dstream = alloca - (\ptr_dEnt -> readDirStreamWithPtr ptr_dEnt f dstream) - --- | A version of 'readDirStreamWith' that takes a pre-allocated pointer in --- addition to the other arguments. This pointer is used to store the pointer --- to the next directory entry, if there is any. This function is intended for --- use cases where you need to read a lot of directory entries and want to --- reuse the pointer for each of them. Using for example 'readDirStream' or --- 'readDirStreamWith' in this scenario would allocate a new pointer for each --- call of these functions. --- --- __NOTE__: You are responsible for releasing the pointer after you are done. +-- __NOTE:__ Multiple threads reading from the same `DirStream` is not safe. -- -- @since 2.8.6.0 -readDirStreamWithPtr :: Ptr DirEnt -> (DirEnt -> IO a) -> DirStream -> IO (Maybe a) -readDirStreamWithPtr ptr_dEnt f dstream@(DirStream dirp) = do +readDirStreamWith :: (DirEnt -> IO a) -> DirStream -> IO (Maybe a) +readDirStreamWith f (DirStream dirp) = do resetErrno - r <- c_readdir dirp (castPtr ptr_dEnt) - if (r == 0) - then do dEnt@(DirEnt dEntPtr) <- peek ptr_dEnt - if (dEntPtr == nullPtr) + cDirentPtr <- c_readdir dirp + if (cDirentPtr /= nullPtr) + then Just <$> f (DirEnt cDirentPtr) + else do (Errno eo) <- getErrno + if (eo == 0) then return Nothing - else do - res <- f dEnt - c_freeDirEnt dEntPtr - return (Just res) - else do errno <- getErrno - if (errno == eINTR) - then readDirStreamWithPtr ptr_dEnt f dstream - else do - let (Errno eo) = errno - if (eo == 0) - then return Nothing - else throwErrno "readDirStream" + else throwErrno "readDirStream" -- | @since 2.8.6.0 dirEntName :: DirEnt -> IO CString @@ -345,12 +321,8 @@ foreign import ccall unsafe "__hscore_d_type" d_type :: Ptr CDirent -> IO CChar -- traversing directories -foreign import ccall unsafe "__hscore_readdir" - c_readdir :: Ptr CDir -> Ptr (Ptr CDirent) -> IO CInt - -foreign import ccall unsafe "__hscore_free_dirent" - c_freeDirEnt :: Ptr CDirent -> IO () - +foreign import ccall unsafe "readdir" + c_readdir :: Ptr CDir -> IO (Ptr CDirent) -- | @rewindDirStream dp@ calls @rewinddir@ to reposition -- the directory stream @dp@ at the beginning of the directory. diff --git a/System/Posix/Directory/Internals.hsc b/System/Posix/Directory/Internals.hsc index addf46b..752e0e9 100644 --- a/System/Posix/Directory/Internals.hsc +++ b/System/Posix/Directory/Internals.hsc @@ -46,7 +46,6 @@ module System.Posix.Directory.Internals ( isWhiteoutType, getRealDirType, readDirStreamWith, - readDirStreamWithPtr, ) where import System.Posix.Directory.Common diff --git a/cbits/HsUnix.c b/cbits/HsUnix.c index 616f546..f1c06de 100644 --- a/cbits/HsUnix.c +++ b/cbits/HsUnix.c @@ -32,73 +32,6 @@ int __hsunix_push_module(int fd, const char *module) clock_t __hsunix_clocks_per_second (void) {return CLOCKS_PER_SEC;} #endif -/* - * GNU glibc 2.23 and later deprecate `readdir_r` in favour of plain old - * `readdir` which in some upcoming POSIX standard is going to required to be - * re-entrant. - * Eventually we want to drop `readdir_r` all together, but want to be - * compatible with older unixen which may not have a re-entrant `readdir`. - * Solution is to make systems with *known* re-entrant `readdir` use that and use - * `readdir_r` wherever we have it and don't *know* that `readdir` is - * re-entrant. - */ - -#if defined (__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 23)) -#define USE_READDIR_R 0 -#else -#define USE_READDIR_R 1 -#endif - -/* - * read an entry from the directory stream; opt for the - * re-entrant friendly way of doing this, if available. - */ -int __hscore_readdir( DIR *dirPtr, struct dirent **pDirEnt ) -{ -#if HAVE_READDIR_R && USE_READDIR_R - struct dirent* p; - int res; - static unsigned int nm_max = (unsigned int)-1; - - if (pDirEnt == NULL) { - return -1; - } - if (nm_max == (unsigned int)-1) { -#ifdef NAME_MAX - nm_max = NAME_MAX + 1; -#else - nm_max = pathconf(".", _PC_NAME_MAX); - if (nm_max == -1) { nm_max = 255; } - nm_max++; -#endif - } - p = (struct dirent*)malloc(sizeof(struct dirent) + nm_max); - if (p == NULL) return -1; - res = readdir_r(dirPtr, p, pDirEnt); - if (res != 0) { - *pDirEnt = NULL; - free(p); - } - else if (*pDirEnt == NULL) { - // end of stream - free(p); - } - return res; -#else - - if (pDirEnt == NULL) { - return -1; - } - - *pDirEnt = readdir(dirPtr); - if (*pDirEnt == NULL) { - return -1; - } else { - return 0; - } -#endif -} - char *__hscore_d_name( struct dirent* d ) { return (d->d_name); @@ -112,10 +45,3 @@ char __hscore_d_type( struct dirent* d ) return CONST_DT_UNKNOWN; #endif } - -void __hscore_free_dirent(struct dirent *dEnt) -{ -#if HAVE_READDIR_R && USE_READDIR_R - free(dEnt); -#endif -}