Skip to content

Commit c1d7edb

Browse files
Use the same ReadOptions in Windows and Posix
1 parent 9145ba3 commit c1d7edb

5 files changed

Lines changed: 134 additions & 129 deletions

File tree

core/src/Streamly/Internal/FileSystem/DirIO.hs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,7 @@ module Streamly.Internal.FileSystem.DirIO
8181
-- getMetadata GetMetadata (followSymlinks, noAutoMount - see fstatat)
8282

8383
-- * Configuration
84-
ReadOptions
85-
, defaultReadOptions
86-
#if !defined(mingw32_HOST_OS) && !defined(__MINGW32__)
87-
, followSymlinks
88-
, ignoreMissing
89-
, ignoreSymlinkLoops
90-
, ignoreInaccessible
91-
#endif
84+
module Streamly.Internal.FileSystem.DirOptions
9285

9386
-- * Streams
9487
, read
@@ -160,14 +153,13 @@ import Streamly.Internal.FileSystem.Windows.ReadDir
160153
(eitherReader, reader, ReadOptions, defaultReadOptions)
161154
#else
162155
import Streamly.Internal.FileSystem.Posix.ReadDir
163-
( readEitherChunks, eitherReader, reader, ReadOptions, defaultReadOptions
164-
, followSymlinks, ignoreMissing, ignoreSymlinkLoops, ignoreInaccessible
165-
)
156+
( readEitherChunks, eitherReader, reader)
166157
#endif
167158
import qualified Streamly.Internal.Data.Stream as S
168159
import qualified Streamly.Data.Unfold as UF
169160
import qualified Streamly.Internal.FileSystem.Path as Path
170161

162+
import Streamly.Internal.FileSystem.DirOptions
171163
import Prelude hiding (read)
172164

173165
{-
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
-- |
2+
-- Module : Streamly.Internal.FileSystem.DirOptions
3+
-- Copyright : (c) 2024 Composewell Technologies
4+
--
5+
-- License : BSD3
6+
-- Maintainer : streamly@composewell.com
7+
-- Portability : GHC
8+
9+
module Streamly.Internal.FileSystem.DirOptions
10+
(
11+
ReadOptions (..)
12+
, followSymlinks
13+
, ignoreMissing
14+
, ignoreSymlinkLoops
15+
, ignoreInaccessible
16+
, defaultReadOptions
17+
)
18+
where
19+
20+
-- NOTE: If we are following symlinks, then we want to determine the type of
21+
-- the link destination not the link itself, so we need to use stat instead of
22+
-- lstat for resolving the symlink.
23+
--
24+
-- For recursive traversal, instead of classifying the dirents using stat, we
25+
-- can leave them unclassified, and deal with ENOTDIR when doing an opendir. We
26+
-- can just ignore that error if it is not a dir. This way we do not need to do
27+
-- stat at all. Or we can basically say don't try to determine the type of
28+
-- symlinks and always try to read symlinks as dirs. We can have an option for
29+
-- classifying symlinks or DT_UNKNOWN as potential dirs.
30+
31+
-- When resolving a symlink we may encounter errors only if the directory entry
32+
-- is a symlink. If the directory entry is not a symlink then stat on it will
33+
-- have permissions, it will not give ELOOP or ENOENT unless the file was
34+
-- deleted or recreated after we read the dirent.
35+
36+
-- | Options controlling the behavior of directory read.
37+
data ReadOptions =
38+
ReadOptions
39+
{ _followSymlinks :: Bool
40+
, _ignoreELOOP :: Bool
41+
, _ignoreENOENT :: Bool
42+
, _ignoreEACCESS :: Bool
43+
}
44+
45+
-- | Control how symbolic links are handled when determining the type
46+
-- of a directory entry.
47+
--
48+
-- * If set to 'True', symbolic links are resolved before classification.
49+
-- This means a symlink pointing to a directory will be treated as a
50+
-- directory, and a symlink pointing to a file will be treated as a
51+
-- non-directory.
52+
--
53+
-- * If set to 'False', all symbolic links are classified as non-directories,
54+
-- without attempting to resolve their targets.
55+
--
56+
-- Enabling resolution may cause additional errors to occur due to
57+
-- insufficient permissions, broken links, or symlink loops. Such errors
58+
-- can be ignored or handled using the appropriate options.
59+
--
60+
-- The default is 'False'.
61+
--
62+
-- On Windows this option has no effect as of now, symlinks are not followed to
63+
-- determine the type.
64+
followSymlinks :: Bool -> ReadOptions -> ReadOptions
65+
followSymlinks x opts = opts {_followSymlinks = x}
66+
67+
-- | When the 'followSymlinks' option is enabled and a directory entry is a
68+
-- symbolic link, we resolve it to determine the type of the symlink target.
69+
-- This option controls the behavior when encountering symlink loop errors
70+
-- during resolution.
71+
--
72+
-- When set to 'True', symlink loop errors are ignored, and the type is
73+
-- reported as not a directory. When set to 'False', the directory read
74+
-- operation fails with an error.
75+
--
76+
-- The default is 'False'.
77+
--
78+
-- On Windows this option has no effect as of now, symlinks are not followed to
79+
-- determine the type.
80+
ignoreSymlinkLoops :: Bool -> ReadOptions -> ReadOptions
81+
ignoreSymlinkLoops x opts = opts {_ignoreELOOP = x}
82+
83+
-- | When the 'followSymlinks' option is enabled and a directory entry is a
84+
-- symbolic link, we resolve it to determine the type of the symlink target.
85+
-- This option controls the behavior when encountering broken symlink errors
86+
-- during resolution.
87+
--
88+
-- When set to 'True', broken symlink errors are ignored, and the type is
89+
-- reported as not a directory. When set to 'False', the directory read
90+
-- operation fails with an error.
91+
--
92+
-- The default is 'False'.
93+
--
94+
-- On Windows this option has no effect as of now, symlinks are not followed to
95+
-- determine the type.
96+
ignoreMissing :: Bool -> ReadOptions -> ReadOptions
97+
ignoreMissing x opts = opts {_ignoreENOENT = x}
98+
99+
-- | When the 'followSymlinks' option is enabled and a directory entry is a
100+
-- symbolic link, we resolve it to determine the type of the symlink target.
101+
-- This option controls the behavior when encountering permission errors
102+
-- during resolution.
103+
--
104+
-- When set to 'True', any permission errors are ignored, and the type is
105+
-- reported as not a directory. When set to 'False', the directory read
106+
-- operation fails with an error.
107+
--
108+
-- The default is 'False'.
109+
--
110+
-- On Windows this option has no effect as of now, symlinks are not followed to
111+
-- determine the type.
112+
ignoreInaccessible :: Bool -> ReadOptions -> ReadOptions
113+
ignoreInaccessible x opts = opts {_ignoreEACCESS = x}
114+
115+
-- XXX find ignores errors when following symlinks, by default.
116+
-- NOTE: The defaultReadOptions emulates the behaviour of "find".
117+
--
118+
defaultReadOptions :: ReadOptions
119+
defaultReadOptions =
120+
ReadOptions
121+
{ _followSymlinks = False
122+
, _ignoreELOOP = False
123+
, _ignoreENOENT = False
124+
, _ignoreEACCESS = False
125+
}

core/src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc

Lines changed: 3 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,7 @@
99
module Streamly.Internal.FileSystem.Posix.ReadDir
1010
(
1111
#if !defined(mingw32_HOST_OS) && !defined(__MINGW32__)
12-
ReadOptions
13-
, followSymlinks
14-
, ignoreMissing
15-
, ignoreSymlinkLoops
16-
, ignoreInaccessible
17-
, defaultReadOptions
18-
19-
, readScanWith_
12+
readScanWith_
2013
, readScanWith
2114
, readPlusScanWith
2215

@@ -68,6 +61,8 @@ import qualified Streamly.Internal.Data.Unfold as UF (bracketIO)
6861
import qualified Streamly.Internal.FileSystem.Path.Common as PathC
6962
import qualified Streamly.Internal.FileSystem.PosixPath as Path
7063

64+
import Streamly.Internal.FileSystem.DirOptions
65+
7166
#include <dirent.h>
7267
#include <sys/stat.h>
7368

@@ -79,103 +74,6 @@ data {-# CTYPE "struct stat" #-} CStat
7974

8075
newtype DirStream = DirStream (Ptr CDir)
8176

82-
-- NOTE: If we are following symlinks, then we want to determine the type of
83-
-- the link destination not the link itself, so we need to use stat instead of
84-
-- lstat for resolving the symlink.
85-
--
86-
-- For recursive traversal, instead of classifying the dirents using stat, we
87-
-- can leave them unclassified, and deal with ENOTDIR when doing an opendir. We
88-
-- can just ignore that error if it is not a dir. This way we do not need to do
89-
-- stat at all. Or we can basically say don't try to determine the type of
90-
-- symlinks and always try to read symlinks as dirs. We can have an option for
91-
-- classifying symlinks or DT_UNKNOWN as potential dirs.
92-
93-
-- When resolving a symlink we may encounter errors only if the directory entry
94-
-- is a symlink. If the directory entry is not a symlink then stat on it will
95-
-- have permissions, it will not give ELOOP or ENOENT unless the file was
96-
-- deleted or recreated after we read the dirent.
97-
98-
-- | Options controlling the behavior of directory read.
99-
data ReadOptions =
100-
ReadOptions
101-
{ _followSymlinks :: Bool
102-
, _ignoreELOOP :: Bool
103-
, _ignoreENOENT :: Bool
104-
, _ignoreEACCESS :: Bool
105-
}
106-
107-
-- | Control how symbolic links are handled when determining the type
108-
-- of a directory entry.
109-
--
110-
-- * If set to 'True', symbolic links are resolved before classification.
111-
-- This means a symlink pointing to a directory will be treated as a
112-
-- directory, and a symlink pointing to a file will be treated as a
113-
-- non-directory.
114-
--
115-
-- * If set to 'False', all symbolic links are classified as non-directories,
116-
-- without attempting to resolve their targets.
117-
--
118-
-- Enabling resolution may cause additional errors to occur due to
119-
-- insufficient permissions, broken links, or symlink loops. Such errors
120-
-- can be ignored or handled using the appropriate options.
121-
--
122-
-- The default is 'False'.
123-
--
124-
-- On Windows this option does nothing as of now, symlinks are not followed to
125-
-- determine the type.
126-
followSymlinks :: Bool -> ReadOptions -> ReadOptions
127-
followSymlinks x opts = opts {_followSymlinks = x}
128-
129-
-- | When the 'followSymlinks' option is enabled and a directory entry is a
130-
-- symbolic link, we resolve it to determine the type of the symlink target.
131-
-- This option controls the behavior when encountering symlink loop errors
132-
-- during resolution.
133-
--
134-
-- When set to 'True', symlink loop errors are ignored, and the type is
135-
-- reported as not a directory. When set to 'False', the directory read
136-
-- operation fails with an error.
137-
--
138-
-- The default is 'False'.
139-
ignoreSymlinkLoops :: Bool -> ReadOptions -> ReadOptions
140-
ignoreSymlinkLoops x opts = opts {_ignoreELOOP = x}
141-
142-
-- | When the 'followSymlinks' option is enabled and a directory entry is a
143-
-- symbolic link, we resolve it to determine the type of the symlink target.
144-
-- This option controls the behavior when encountering broken symlink errors
145-
-- during resolution.
146-
--
147-
-- When set to 'True', broken symlink errors are ignored, and the type is
148-
-- reported as not a directory. When set to 'False', the directory read
149-
-- operation fails with an error.
150-
--
151-
-- The default is 'False'.
152-
ignoreMissing :: Bool -> ReadOptions -> ReadOptions
153-
ignoreMissing x opts = opts {_ignoreENOENT = x}
154-
155-
-- | When the 'followSymlinks' option is enabled and a directory entry is a
156-
-- symbolic link, we resolve it to determine the type of the symlink target.
157-
-- This option controls the behavior when encountering permission errors
158-
-- during resolution.
159-
--
160-
-- When set to 'True', any permission errors are ignored, and the type is
161-
-- reported as not a directory. When set to 'False', the directory read
162-
-- operation fails with an error.
163-
--
164-
-- The default is 'False'.
165-
ignoreInaccessible :: Bool -> ReadOptions -> ReadOptions
166-
ignoreInaccessible x opts = opts {_ignoreEACCESS = x}
167-
168-
-- NOTE: The defaultReadOptions emulate the behaviour of "find".
169-
--
170-
defaultReadOptions :: ReadOptions
171-
defaultReadOptions =
172-
ReadOptions
173-
{ _followSymlinks = False
174-
, _ignoreELOOP = False
175-
, _ignoreENOENT = False
176-
, _ignoreEACCESS = False
177-
}
178-
17977
-- | Minimal read without any metadata.
18078
{-# INLINE readScanWith_ #-}
18179
readScanWith_ :: -- (MonadIO m, MonadCatch m) =>

core/src/Streamly/Internal/FileSystem/Windows/ReadDir.hsc

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@
99
module Streamly.Internal.FileSystem.Windows.ReadDir
1010
(
1111
#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
12-
ReadOptions
13-
, defaultReadOptions
14-
15-
, DirStream
12+
DirStream
1613
, openDirStream
1714
, closeDirStream
1815
, readDirStreamEither
@@ -43,6 +40,7 @@ import qualified Streamly.Internal.Data.Unfold as UF (bracketIO)
4340
import qualified Streamly.Internal.FileSystem.WindowsPath as Path
4441
import qualified System.Win32 as Win32 (failWith)
4542

43+
import Streamly.Internal.FileSystem.DirOptions
4644
import Foreign hiding (void)
4745

4846
#include <windows.h>
@@ -137,15 +135,6 @@ failIf p wh act = do
137135
iNVALID_HANDLE_VALUE :: HANDLE
138136
iNVALID_HANDLE_VALUE = castUINTPtrToPtr maxBound
139137

140-
------------------------------------------------------------------------------
141-
-- Config
142-
------------------------------------------------------------------------------
143-
144-
data ReadOptions = ReadOptions
145-
146-
defaultReadOptions :: ReadOptions
147-
defaultReadOptions = ReadOptions
148-
149138
------------------------------------------------------------------------------
150139
-- Dir stream implementation
151140
------------------------------------------------------------------------------

core/streamly-core.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,7 @@ library
565565

566566
, Streamly.Internal.Data.Time.Clock.Type
567567
, Streamly.Internal.FileSystem.Path.Common
568+
, Streamly.Internal.FileSystem.DirOptions
568569

569570
if flag(internal-dev)
570571
exposed-modules:

0 commit comments

Comments
 (0)