-
Notifications
You must be signed in to change notification settings - Fork 70
Cleanup and document readdir options #3134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| -- | | ||
| -- Module : Streamly.Internal.FileSystem.DirOptions | ||
| -- Copyright : (c) 2024 Composewell Technologies | ||
| -- | ||
| -- License : BSD3 | ||
| -- Maintainer : streamly@composewell.com | ||
| -- Portability : GHC | ||
|
|
||
| module Streamly.Internal.FileSystem.DirOptions | ||
| ( | ||
| ReadOptions (..) | ||
| , followSymlinks | ||
| , ignoreMissing | ||
| , ignoreSymlinkLoops | ||
| , ignoreInaccessible | ||
| , defaultReadOptions | ||
| ) | ||
| where | ||
|
|
||
| -- NOTE: If we are following symlinks, then we want to determine the type of | ||
| -- the link destination not the link itself, so we need to use stat instead of | ||
| -- lstat for resolving the symlink. | ||
| -- | ||
| -- For recursive traversal, instead of classifying the dirents using stat, we | ||
| -- can leave them unclassified, and deal with ENOTDIR when doing an opendir. We | ||
| -- can just ignore that error if it is not a dir. This way we do not need to do | ||
| -- stat at all. Or we can basically say don't try to determine the type of | ||
| -- symlinks and always try to read symlinks as dirs. We can have an option for | ||
| -- classifying symlinks or DT_UNKNOWN as potential dirs. | ||
|
|
||
| -- When resolving a symlink we may encounter errors only if the directory entry | ||
| -- is a symlink. If the directory entry is not a symlink then stat on it will | ||
| -- have permissions, it will not give ELOOP or ENOENT unless the file was | ||
| -- deleted or recreated after we read the dirent. | ||
|
|
||
| -- | Options controlling the behavior of directory read. | ||
| data ReadOptions = | ||
| ReadOptions | ||
| { _followSymlinks :: Bool | ||
| , _ignoreELOOP :: Bool | ||
| , _ignoreENOENT :: Bool | ||
| , _ignoreEACCESS :: Bool | ||
| } | ||
|
|
||
| -- | Control how symbolic links are handled when determining the type | ||
| -- of a directory entry. | ||
| -- | ||
| -- * If set to 'True', symbolic links are resolved before classification. | ||
| -- This means a symlink pointing to a directory will be treated as a | ||
| -- directory, and a symlink pointing to a file will be treated as a | ||
| -- non-directory. | ||
| -- | ||
| -- * If set to 'False', all symbolic links are classified as non-directories, | ||
| -- without attempting to resolve their targets. | ||
| -- | ||
| -- Enabling resolution may cause additional errors to occur due to | ||
| -- insufficient permissions, broken links, or symlink loops. Such errors | ||
| -- can be ignored or handled using the appropriate options. | ||
| -- | ||
| -- The default is 'False'. | ||
| -- | ||
| -- On Windows this option has no effect as of now, symlinks are not followed to | ||
| -- determine the type. | ||
| followSymlinks :: Bool -> ReadOptions -> ReadOptions | ||
| followSymlinks x opts = opts {_followSymlinks = x} | ||
|
|
||
| -- | When the 'followSymlinks' option is enabled and a directory entry is a | ||
| -- symbolic link, we resolve it to determine the type of the symlink target. | ||
| -- This option controls the behavior when encountering symlink loop errors | ||
| -- during resolution. | ||
| -- | ||
| -- When set to 'True', symlink loop errors are ignored, and the type is | ||
| -- reported as not a directory. When set to 'False', the directory read | ||
| -- operation fails with an error. | ||
| -- | ||
| -- The default is 'False'. | ||
| -- | ||
| -- On Windows this option has no effect as of now, symlinks are not followed to | ||
| -- determine the type. | ||
| ignoreSymlinkLoops :: Bool -> ReadOptions -> ReadOptions | ||
| ignoreSymlinkLoops x opts = opts {_ignoreELOOP = x} | ||
|
|
||
| -- | When the 'followSymlinks' option is enabled and a directory entry is a | ||
| -- symbolic link, we resolve it to determine the type of the symlink target. | ||
| -- This option controls the behavior when encountering broken symlink errors | ||
| -- during resolution. | ||
| -- | ||
| -- When set to 'True', broken symlink errors are ignored, and the type is | ||
| -- reported as not a directory. When set to 'False', the directory read | ||
| -- operation fails with an error. | ||
| -- | ||
| -- The default is 'False'. | ||
| -- | ||
| -- On Windows this option has no effect as of now, symlinks are not followed to | ||
| -- determine the type. | ||
| ignoreMissing :: Bool -> ReadOptions -> ReadOptions | ||
| ignoreMissing x opts = opts {_ignoreENOENT = x} | ||
|
|
||
| -- | When the 'followSymlinks' option is enabled and a directory entry is a | ||
| -- symbolic link, we resolve it to determine the type of the symlink target. | ||
| -- This option controls the behavior when encountering permission errors | ||
| -- during resolution. | ||
| -- | ||
| -- When set to 'True', any permission errors are ignored, and the type is | ||
| -- reported as not a directory. When set to 'False', the directory read | ||
| -- operation fails with an error. | ||
| -- | ||
| -- The default is 'False'. | ||
| -- | ||
| -- On Windows this option has no effect as of now, symlinks are not followed to | ||
| -- determine the type. | ||
| ignoreInaccessible :: Bool -> ReadOptions -> ReadOptions | ||
| ignoreInaccessible x opts = opts {_ignoreEACCESS = x} | ||
|
|
||
| -- XXX find ignores errors when following symlinks, by default. | ||
| -- NOTE: The defaultReadOptions emulates the behaviour of "find". | ||
| -- | ||
| defaultReadOptions :: ReadOptions | ||
| defaultReadOptions = | ||
| ReadOptions | ||
| { _followSymlinks = False | ||
| , _ignoreELOOP = False | ||
| , _ignoreENOENT = False | ||
| , _ignoreEACCESS = False | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,14 +9,7 @@ | |
| module Streamly.Internal.FileSystem.Posix.ReadDir | ||
| ( | ||
| #if !defined(mingw32_HOST_OS) && !defined(__MINGW32__) | ||
| ReadOptions | ||
| , followSymlinks | ||
| , ignoreNonExisting | ||
| , ignoreLoopErrors | ||
| , ignoreInAccessible | ||
| , defaultReadOptions | ||
|
|
||
| , readScanWith_ | ||
| readScanWith_ | ||
| , readScanWith | ||
| , readPlusScanWith | ||
|
|
||
|
|
@@ -68,6 +61,8 @@ import qualified Streamly.Internal.Data.Unfold as UF (bracketIO) | |
| import qualified Streamly.Internal.FileSystem.Path.Common as PathC | ||
| import qualified Streamly.Internal.FileSystem.PosixPath as Path | ||
|
|
||
| import Streamly.Internal.FileSystem.DirOptions | ||
|
|
||
| #include <dirent.h> | ||
| #include <sys/stat.h> | ||
|
|
||
|
|
@@ -79,37 +74,6 @@ data {-# CTYPE "struct stat" #-} CStat | |
|
|
||
| newtype DirStream = DirStream (Ptr CDir) | ||
|
|
||
| data ReadOptions = | ||
| ReadOptions | ||
| { _followSymlinks :: Bool | ||
| , _ignoreSymlinkLoopErrors :: Bool | ||
| , _ignoreNonExistingFiles :: Bool | ||
| , _ignoreInAccessibleFiles :: Bool | ||
| } | ||
|
|
||
| followSymlinks :: Bool -> ReadOptions -> ReadOptions | ||
| followSymlinks x opts = opts {_followSymlinks = x} | ||
|
|
||
| ignoreLoopErrors :: Bool -> ReadOptions -> ReadOptions | ||
| ignoreLoopErrors x opts = opts {_ignoreSymlinkLoopErrors = x} | ||
|
|
||
| ignoreNonExisting :: Bool -> ReadOptions -> ReadOptions | ||
| ignoreNonExisting x opts = opts {_ignoreNonExistingFiles = x} | ||
|
|
||
| ignoreInAccessible :: Bool -> ReadOptions -> ReadOptions | ||
| ignoreInAccessible x opts = opts {_ignoreInAccessibleFiles = x} | ||
|
|
||
| -- NOTE: The defaultReadOptions emulate the behaviour of "find". | ||
| -- | ||
| defaultReadOptions :: ReadOptions | ||
| defaultReadOptions = | ||
| ReadOptions | ||
| { _followSymlinks = False | ||
| , _ignoreSymlinkLoopErrors = False | ||
| , _ignoreNonExistingFiles = False | ||
| , _ignoreInAccessibleFiles = False | ||
| } | ||
|
|
||
| -- | Minimal read without any metadata. | ||
| {-# INLINE readScanWith_ #-} | ||
| readScanWith_ :: -- (MonadIO m, MonadCatch m) => | ||
|
|
@@ -305,13 +269,13 @@ statEntryType conf parent dname = do | |
| else EntryIsNotDir | ||
| Left errno -> do | ||
| if errno == eNOENT | ||
| then unless (_ignoreNonExistingFiles conf) $ | ||
| then unless (_ignoreENOENT conf) $ | ||
| throwErrno (errMsg path) | ||
| else if errno == eACCES | ||
| then unless (_ignoreInAccessibleFiles conf) $ | ||
| then unless (_ignoreEACCESS conf) $ | ||
| throwErrno (errMsg path) | ||
| else if errno == eLOOP | ||
| then unless (_ignoreSymlinkLoopErrors conf) $ | ||
| then unless (_ignoreELOOP conf) $ | ||
| throwErrno (errMsg path) | ||
| else throwErrno (errMsg path) | ||
| pure $ EntryIgnored | ||
|
|
@@ -414,10 +378,8 @@ streamEitherReader confMod = Unfold step return | |
| Just x -> return $ Yield x s | ||
|
|
||
| {-# INLINE streamReader #-} | ||
| streamReader | ||
| :: MonadIO m | ||
| => (ReadOptions -> ReadOptions) -> Unfold m (PosixPath, DirStream) Path | ||
| streamReader confMod = fmap (either id id) (streamEitherReader confMod) | ||
| streamReader :: MonadIO m => Unfold m (PosixPath, DirStream) Path | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the reasoning of where to keep readoptions?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where it is needed. |
||
| streamReader = fmap (either id id) (streamEitherReader id) | ||
|
|
||
| {-# INLINE before #-} | ||
| before :: PosixPath -> IO (PosixPath, DirStream) | ||
|
|
@@ -433,14 +395,13 @@ after (_, dirStream) = closeDirStream dirStream | |
| -- /Internal/ | ||
| -- | ||
| {-# INLINE reader #-} | ||
| reader :: (MonadIO m, MonadCatch m) | ||
| => (ReadOptions -> ReadOptions) -> Unfold m Path Path | ||
| reader confMod = | ||
| reader :: (MonadIO m, MonadCatch m) => Unfold m Path Path | ||
| reader = | ||
| -- XXX Instead of using bracketIO for each iteration of the loop we should | ||
| -- instead yield a buffer of dir entries in each iteration and then use an | ||
| -- unfold and concat to flatten those entries. That should improve the | ||
| -- performance. | ||
| UF.bracketIO before after (streamReader confMod) | ||
| UF.bracketIO before after (streamReader) | ||
|
|
||
| -- | Read directories as Left and files as Right. Filter out "." and ".." | ||
| -- entries. | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you remove readoptions here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is not used, then why pass it. There is no reason for this operation to have an option.