diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 *Path IO follows [SemVer](https://semver.org/).*
 
+## Path IO 1.8.1
+
+* Fixed a “permission denied” error occurring when `copyDirRecur` is used to
+  copy a tree with non-empty read-only directories. [PR
+  82](https://github.com/mrkkrp/path-io/pull/82).
+
 ## Path IO 1.8.0
 
 * Added instances of `AnyPath` for `SomeBase`. [PR
diff --git a/Path/IO.hs b/Path/IO.hs
--- a/Path/IO.hs
+++ b/Path/IO.hs
@@ -175,14 +175,14 @@
 -- * 'InappropriateType'
 -- The path refers to an existing non-directory object.
 -- @[EEXIST]@
-createDir :: MonadIO m => Path b Dir -> m ()
+createDir :: (MonadIO m) => Path b Dir -> m ()
 createDir = liftD D.createDirectory
 
 -- | @'createDirIfMissing' parents dir@ creates a new directory @dir@ if it
 -- doesn't exist. If the first argument is 'True' the function will also
 -- create all parent directories if they are missing.
 createDirIfMissing ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Create its parents too?
   Bool ->
   -- | The path to the directory you want to make
@@ -196,7 +196,7 @@
 -- > ensureDir = createDirIfMissing True
 --
 -- @since 0.3.1
-ensureDir :: MonadIO m => Path b Dir -> m ()
+ensureDir :: (MonadIO m) => Path b Dir -> m ()
 ensureDir = createDirIfMissing True
 
 -- | @'removeDir' dir@ removes an existing directory @dir@. The
@@ -236,13 +236,13 @@
 -- * 'InappropriateType'
 -- The operand refers to an existing non-directory object.
 -- @[ENOTDIR]@
-removeDir :: MonadIO m => Path b Dir -> m ()
+removeDir :: (MonadIO m) => Path b Dir -> m ()
 removeDir = liftD D.removeDirectory
 
 -- | @'removeDirRecur' dir@ removes an existing directory @dir@ together
 -- with its contents and sub-directories. Within this directory, symbolic
 -- links are removed without affecting their targets.
-removeDirRecur :: MonadIO m => Path b Dir -> m ()
+removeDirRecur :: (MonadIO m) => Path b Dir -> m ()
 removeDirRecur = liftD D.removeDirectoryRecursive
 
 -- | Remove a file or directory at /path/ together with its contents and
@@ -263,7 +263,7 @@
 -- exception.  The first exception that it encountered is re-thrown.
 --
 -- @since 1.7.0
-removePathForcibly :: MonadIO m => Path b t -> m ()
+removePathForcibly :: (MonadIO m) => Path b t -> m ()
 removePathForcibly = liftD D.removePathForcibly
 
 -- | @'renameDir' old new@ changes the name of an existing directory from
@@ -312,7 +312,7 @@
 --  Either path refers to an existing non-directory object.
 --  @[ENOTDIR, EISDIR]@
 renameDir ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Old name
   Path b0 Dir ->
   -- | New name
@@ -362,7 +362,7 @@
 -- @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
 --
 -- @since 1.7.0
-renamePath :: MonadIO m => Path b0 t -> Path b1 t -> m ()
+renamePath :: (MonadIO m) => Path b0 t -> Path b1 t -> m ()
 renamePath = liftD2 D.renamePath
 
 -- | @'listDir' dir@ returns a list of /all/ entries in @dir@ without the
@@ -394,7 +394,7 @@
 --   The path refers to an existing non-directory object.
 --   @[ENOTDIR]@
 listDir ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Directory to list
   Path b Dir ->
   -- | Sub-directories and files
@@ -411,7 +411,7 @@
 --
 -- @since 1.4.0
 listDirRel ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Directory to list
   Path b Dir ->
   -- | Sub-directories and files
@@ -431,7 +431,7 @@
 --
 -- __Note__: before version /1.3.0/, this function followed symlinks.
 listDirRecur ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Directory to list
   Path b Dir ->
   -- | Sub-directories and files
@@ -453,7 +453,7 @@
 --
 -- @since 1.4.2
 listDirRecurRel ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Directory to list
   Path b Dir ->
   -- | Sub-directories and files
@@ -524,7 +524,7 @@
 -- to preserve directory permissions or not. /Does not/ follow symbolic
 -- links. Internal function.
 copyDirRecurGen ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Should we preserve directory permissions?
   Bool ->
   -- | Source
@@ -545,7 +545,7 @@
         (new </>)
           <$> stripProperPrefix old path
   ensureDir bdest
-  forM_ dirs $ \srcDir -> do
+  copyPermissionsIOs <- forM dirs $ \srcDir -> do
     destDir <- swapParent bsrc bdest srcDir
     dirIsSymlink <- isSymlink srcDir
     if dirIsSymlink
@@ -554,8 +554,7 @@
         D.createDirectoryLink target $
           toFilePath' destDir
       else ensureDir destDir
-    when preserveDirPermissions $
-      ignoringIOErrors (copyPermissions srcDir destDir)
+    pure $ ignoringIOErrors (copyPermissions srcDir destDir)
   forM_ files $ \srcFile -> do
     destFile <- swapParent bsrc bdest srcFile
     fileIsSymlink <- isSymlink srcFile
@@ -564,8 +563,9 @@
         target <- getSymlinkTarget srcFile
         D.createFileLink target (toFilePath destFile)
       else copyFile srcFile destFile
-  when preserveDirPermissions $
+  when preserveDirPermissions $ do
     ignoringIOErrors (copyPermissions bsrc bdest)
+    sequence_ copyPermissionsIOs
 
 ----------------------------------------------------------------------------
 -- Walking directory trees
@@ -626,7 +626,7 @@
 --
 -- @since 1.2.0
 walkDir ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Handler (@dir -> subdirs -> files -> 'WalkAction'@)
   (Path Abs Dir -> [Path Abs Dir] -> [Path Abs File] -> m (WalkAction Abs)) ->
   -- | Directory where traversal begins
@@ -668,7 +668,7 @@
 --
 -- @since 1.4.2
 walkDirRel ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Handler (@dir -> subdirs -> files -> 'WalkAction'@)
   ( Path Rel Dir ->
     [Path Rel Dir] ->
@@ -809,7 +809,7 @@
 --
 -- * 'UnsupportedOperation'
 -- The operating system has no notion of current working directory.
-getCurrentDir :: MonadIO m => m (Path Abs Dir)
+getCurrentDir :: (MonadIO m) => m (Path Abs Dir)
 getCurrentDir = liftIO $ D.getCurrentDirectory >>= parseAbsDir
 
 -- | Change the working directory to the given path.
@@ -844,7 +844,7 @@
 -- * 'InappropriateType'
 -- The path refers to an existing non-directory object.
 -- @[ENOTDIR]@
-setCurrentDir :: MonadIO m => Path b Dir -> m ()
+setCurrentDir :: (MonadIO m) => Path b Dir -> m ()
 setCurrentDir = liftD D.setCurrentDirectory
 
 -- | Run an 'IO' action with the given working directory and restore the
@@ -884,7 +884,7 @@
 -- * 'isDoesNotExistError'
 -- The home directory for the current user does not exist, or
 -- cannot be found.
-getHomeDir :: MonadIO m => m (Path Abs Dir)
+getHomeDir :: (MonadIO m) => m (Path Abs Dir)
 getHomeDir = liftIO D.getHomeDirectory >>= resolveDir'
 
 -- | Obtain the path to a special directory for storing user-specific
@@ -911,7 +911,7 @@
 --   The home directory for the current user does not exist, or cannot be
 --   found.
 getAppUserDataDir ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Name of application (used in path construction)
   String ->
   m (Path Abs Dir)
@@ -935,7 +935,7 @@
 -- * 'isDoesNotExistError'
 -- The document directory for the current user does not exist, or
 -- cannot be found.
-getUserDocsDir :: MonadIO m => m (Path Abs Dir)
+getUserDocsDir :: (MonadIO m) => m (Path Abs Dir)
 getUserDocsDir = liftIO $ D.getUserDocumentsDirectory >>= parseAbsDir
 
 -- | Return the current directory for temporary files.
@@ -963,7 +963,7 @@
 -- The operating system has no notion of temporary directory.
 --
 -- The function doesn't verify whether the path exists.
-getTempDir :: MonadIO m => m (Path Abs Dir)
+getTempDir :: (MonadIO m) => m (Path Abs Dir)
 getTempDir = liftIO D.getTemporaryDirectory >>= resolveDir'
 
 -- | Obtain the paths to special directories for storing user-specific
@@ -983,7 +983,7 @@
 --
 -- @since 1.2.1
 getXdgDir ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Which special directory
   D.XdgDirectory ->
   -- | A relative path that is appended to the path; if 'Nothing', the
@@ -1003,7 +1003,7 @@
 --
 -- @since 1.5.0
 getXdgDirList ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Which special directory list
   D.XdgDirectoryList ->
   m [Path Abs Dir]
@@ -1051,7 +1051,7 @@
   -- Please note that before version 1.2.3.0 of the @directory@ package,
   -- this function had unpredictable behavior on non-existent paths.
   canonicalizePath ::
-    MonadIO m =>
+    (MonadIO m) =>
     path ->
     m (AbsPath path)
 
@@ -1061,7 +1061,7 @@
   -- If the path is already absolute, the operation never fails. Otherwise,
   -- the operation may fail with the same exceptions as 'getCurrentDir'.
   makeAbsolute ::
-    MonadIO m =>
+    (MonadIO m) =>
     path ->
     m (AbsPath path)
 
@@ -1069,7 +1069,7 @@
   --
   -- @since 0.3.0
   makeRelative ::
-    MonadThrow m =>
+    (MonadThrow m) =>
     -- | Base directory
     Path Abs Dir ->
     -- | Path that will be made relative to base directory
@@ -1080,7 +1080,7 @@
   --
   -- @since 0.3.0
   makeRelativeToCurrentDir ::
-    MonadIO m =>
+    (MonadIO m) =>
     path ->
     m (RelPath path)
 
@@ -1149,7 +1149,7 @@
 --
 -- @since 0.3.0
 resolveFile ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Base directory
   Path Abs Dir ->
   -- | Path to resolve
@@ -1161,7 +1161,7 @@
 --
 -- @since 0.3.0
 resolveFile' ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Path to resolve
   FilePath ->
   m (Path Abs File)
@@ -1171,7 +1171,7 @@
 --
 -- @since 0.3.0
 resolveDir ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Base directory
   Path Abs Dir ->
   -- | Path to resolve
@@ -1183,7 +1183,7 @@
 --
 -- @since 0.3.0
 resolveDir' ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Path to resolve
   FilePath ->
   m (Path Abs Dir)
@@ -1222,7 +1222,7 @@
 -- * 'InappropriateType'
 -- The operand refers to an existing directory.
 -- @[EPERM, EINVAL]@
-removeFile :: MonadIO m => Path b File -> m ()
+removeFile :: (MonadIO m) => Path b File -> m ()
 removeFile = liftD D.removeFile
 
 -- | @'renameFile' old new@ changes the name of an existing file system
@@ -1266,7 +1266,7 @@
 -- Either path refers to an existing directory.
 -- @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
 renameFile ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Original location
   Path b0 File ->
   -- | New location
@@ -1279,7 +1279,7 @@
 -- file. Neither path may refer to an existing directory. The permissions of
 -- @old@ are copied to @new@, if possible.
 copyFile ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Original location
   Path b0 File ->
   -- | Where to put copy
@@ -1290,7 +1290,7 @@
 -- | Obtain the size of a file in bytes.
 --
 -- @since 1.7.0
-getFileSize :: MonadIO m => Path b File -> m Integer
+getFileSize :: (MonadIO m) => Path b File -> m Integer
 getFileSize = liftD D.getFileSize
 
 -- | Given an executable file name, search for such file in the directories
@@ -1308,7 +1308,7 @@
 -- the directory containing the current executable. See
 -- <http://msdn.microsoft.com/en-us/library/aa365527.aspx> for more details.
 findExecutable ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Executable file name
   Path Rel File ->
   -- | Path to found executable
@@ -1317,7 +1317,7 @@
 
 -- | Search through the given set of directories for the given file.
 findFile ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Set of directories to search in
   [Path b Dir] ->
   -- | Filename of interest
@@ -1335,7 +1335,7 @@
 -- | Search through the given set of directories for the given file and
 -- return a list of paths where the given file exists.
 findFiles ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Set of directories to search in
   [Path b Dir] ->
   -- | Filename of interest
@@ -1348,7 +1348,7 @@
 -- the given property (usually permissions) and return a list of paths where
 -- the given file exists and has the property.
 findFilesWith ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | How to test the files
   (Path Abs File -> m Bool) ->
   -- | Set of directories to search in
@@ -1397,7 +1397,7 @@
 --
 -- @since 1.5.0
 createFileLink ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Path to the target file
   Path b0 File ->
   -- | Path to the link to be created
@@ -1434,7 +1434,7 @@
 --
 -- @since 1.5.0
 createDirLink ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Path to the target directory
   Path b0 Dir ->
   -- | Path to the link to be created
@@ -1454,7 +1454,7 @@
 --
 -- @since 1.5.0
 removeDirLink ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Path to the link to be removed
   Path b Dir ->
   m ()
@@ -1473,7 +1473,7 @@
 --
 -- @since 1.5.0
 getSymlinkTarget ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Symlink path
   Path b t ->
   m FilePath
@@ -1491,7 +1491,7 @@
 -- | Check if the given path is a symbolic link.
 --
 -- @since 1.3.0
-isSymlink :: MonadIO m => Path b t -> m Bool
+isSymlink :: (MonadIO m) => Path b t -> m Bool
 isSymlink = liftD D.pathIsSymbolicLink
 
 ----------------------------------------------------------------------------
@@ -1589,7 +1589,7 @@
 --
 -- @since 0.2.0
 openTempFile ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Directory to create file in
   Path b Dir ->
   -- | File name template; if the template is "foo.ext" then the created
@@ -1612,7 +1612,7 @@
 --
 -- @since 0.2.0
 openBinaryTempFile ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Directory to create file in
   Path b Dir ->
   -- | File name template, see 'openTempFile'
@@ -1632,7 +1632,7 @@
 --
 -- @since 0.2.0
 createTempDir ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Directory to create file in
   Path b Dir ->
   -- | Directory name template, see 'openTempFile'
@@ -1652,22 +1652,22 @@
 -- this function may return false even if the file does actually exist.
 --
 -- @since 1.7.0
-doesPathExist :: MonadIO m => Path b t -> m Bool
+doesPathExist :: (MonadIO m) => Path b t -> m Bool
 doesPathExist = liftD D.doesPathExist
 
 -- | The operation 'doesFileExist' returns 'True' if the argument file
 -- exists and is not a directory, and 'False' otherwise.
-doesFileExist :: MonadIO m => Path b File -> m Bool
+doesFileExist :: (MonadIO m) => Path b File -> m Bool
 doesFileExist = liftD D.doesFileExist
 
 -- | The operation 'doesDirExist' returns 'True' if the argument file exists
 -- and is either a directory or a symbolic link to a directory, and 'False'
 -- otherwise.
-doesDirExist :: MonadIO m => Path b Dir -> m Bool
+doesDirExist :: (MonadIO m) => Path b Dir -> m Bool
 doesDirExist = liftD D.doesDirectoryExist
 
 -- | Check if there is a file or directory on specified path.
-isLocationOccupied :: MonadIO m => Path b t -> m Bool
+isLocationOccupied :: (MonadIO m) => Path b t -> m Bool
 isLocationOccupied path = do
   let fp = toFilePath path
   file <- liftIO (D.doesFileExist fp)
@@ -1704,7 +1704,7 @@
 --   the permissions; or
 --
 -- * 'isDoesNotExistError' if the file or directory does not exist.
-getPermissions :: MonadIO m => Path b t -> m D.Permissions
+getPermissions :: (MonadIO m) => Path b t -> m D.Permissions
 getPermissions = liftD D.getPermissions
 
 -- | The 'setPermissions' operation sets the permissions for the file or
@@ -1716,13 +1716,13 @@
 --   the permissions; or
 --
 -- * 'isDoesNotExistError' if the file or directory does not exist.
-setPermissions :: MonadIO m => Path b t -> D.Permissions -> m ()
+setPermissions :: (MonadIO m) => Path b t -> D.Permissions -> m ()
 setPermissions = liftD2' D.setPermissions
 
 -- | Set permissions for the object found on second given path so they match
 -- permissions of the object on the first path.
 copyPermissions ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | From where to copy
   Path b0 t0 ->
   -- | What to modify
@@ -1748,7 +1748,7 @@
 --
 -- Note: this is a piece of conditional API, only available if
 -- @directory-1.2.3.0@ or later is used.
-getAccessTime :: MonadIO m => Path b t -> m UTCTime
+getAccessTime :: (MonadIO m) => Path b t -> m UTCTime
 getAccessTime = liftD D.getAccessTime
 
 -- | Change the time at which the file or directory was last accessed.
@@ -1775,7 +1775,7 @@
 --
 -- Note: this is a piece of conditional API, only available if
 -- @directory-1.2.3.0@ or later is used.
-setAccessTime :: MonadIO m => Path b t -> UTCTime -> m ()
+setAccessTime :: (MonadIO m) => Path b t -> UTCTime -> m ()
 setAccessTime = liftD2' D.setAccessTime
 
 -- | Change the time at which the file or directory was last modified.
@@ -1802,7 +1802,7 @@
 --
 -- Note: this is a piece of conditional API, only available if
 -- @directory-1.2.3.0@ or later is used.
-setModificationTime :: MonadIO m => Path b t -> UTCTime -> m ()
+setModificationTime :: (MonadIO m) => Path b t -> UTCTime -> m ()
 setModificationTime = liftD2' D.setModificationTime
 
 -- | Obtain the time at which the file or directory was last modified.
@@ -1817,7 +1817,7 @@
 -- Caveat for POSIX systems: This function returns a timestamp with
 -- sub-second resolution only if this package is compiled against
 -- @unix-2.6.0.0@ or later and the underlying filesystem supports them.
-getModificationTime :: MonadIO m => Path b t -> m UTCTime
+getModificationTime :: (MonadIO m) => Path b t -> m UTCTime
 getModificationTime = liftD D.getModificationTime
 
 ----------------------------------------------------------------------------
@@ -1826,7 +1826,7 @@
 -- | Lift an action in 'IO' that takes 'FilePath' into an action in slightly
 -- more abstract monad that takes 'Path'.
 liftD ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Original action
   (FilePath -> IO a) ->
   -- | 'Path' argument
@@ -1838,7 +1838,7 @@
 
 -- | Similar to 'liftD' but for functions with arity 2.
 liftD2 ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Original action
   (FilePath -> FilePath -> IO a) ->
   -- | First 'Path' argument
@@ -1852,7 +1852,7 @@
 -- | Similar to 'liftD2', but allows us to pass second argument of arbitrary
 -- type.
 liftD2' ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Original action
   (FilePath -> v -> IO a) ->
   -- | First 'Path' argument
@@ -1871,5 +1871,5 @@
 ignoringIOErrors :: IO () -> IO ()
 ignoringIOErrors ioe = ioe `catch` handler
   where
-    handler :: Monad m => IOError -> m ()
+    handler :: (Monad m) => IOError -> m ()
     handler = const (return ())
diff --git a/path-io.cabal b/path-io.cabal
--- a/path-io.cabal
+++ b/path-io.cabal
@@ -1,11 +1,11 @@
 cabal-version:   2.4
 name:            path-io
-version:         1.8.0
+version:         1.8.1
 license:         BSD-3-Clause
 license-file:    LICENSE.md
 maintainer:      Mark Karpov <markkarpov92@gmail.com>
 author:          Mark Karpov <markkarpov92@gmail.com>
-tested-with:     ghc ==9.0.2 ghc ==9.2.4 ghc ==9.4.1
+tested-with:     ghc ==9.0.2 ghc ==9.2.5 ghc ==9.4.4
 homepage:        https://github.com/mrkkrp/path-io
 bug-reports:     https://github.com/mrkkrp/path-io/issues
 synopsis:        Interface to ‘directory’ package for users of ‘path’
diff --git a/tests/Main.hs b/tests/Main.hs
--- a/tests/Main.hs
+++ b/tests/Main.hs
@@ -274,12 +274,21 @@
       pdir = root </> $(mkRelDir "pdir")
       withinSandbox = (pdir </>)
   ensureDir pdir
-  ensureDir $ withinSandbox $(mkRelDir "b")
+  let b = withinSandbox $(mkRelDir "b")
+  ensureDir b
   ensureDir $ withinSandbox $(mkRelDir "b/c")
-  -- to verify that we do not follow symbolic links. We should not list b's
-  -- tree under 'a'.
+  -- Create a read-only directory with a file inside to test that the code
+  -- that copies directory permissions can handle that gracefully.
+  --
+  -- See: https://github.com/mrkkrp/path-io/pull/82
+  let readonlyDir = withinSandbox $(mkRelDir "readonly-dir")
+  ensureDir readonlyDir
+  -- We should not list b's tree under 'a' in order to verify that we do not
+  -- follow symbolic links.
   createSymbolicLink "b" (toFilePath $ withinSandbox $(mkRelFile "a"))
   forM_ files $ (`writeFile` "") . toFilePath . withinSandbox
+  getPermissions readonlyDir
+    >>= setPermissions readonlyDir . setOwnerWritable False
   return pdir
 
 -- | Get the inner structure of a directory. Items are sorted, so it's
@@ -312,11 +321,13 @@
 populatedDirStructure =
   ( [ $(mkRelDir "a"),
       $(mkRelDir "b"),
-      $(mkRelDir "b/c")
+      $(mkRelDir "b/c"),
+      $(mkRelDir "readonly-dir")
     ],
     [ $(mkRelFile "b/c/three.txt"),
       $(mkRelFile "b/two.txt"),
-      $(mkRelFile "one.txt")
+      $(mkRelFile "one.txt"),
+      $(mkRelFile "readonly-dir/two.txt")
     ]
   )
 
@@ -359,26 +370,28 @@
   createSymbolicLink "../../e" (toFilePath $ withinSandbox $(mkRelFile "e/f/g"))
   return pdir
 
--- | Top-level structure of populated directory as it should be scanned by
--- the 'listDir' function.
+-- | Top-level structure of the populated directory as it should be scanned
+-- by the 'listDir' function.
 populatedDirTop :: ([Path Rel Dir], [Path Rel File])
 populatedDirTop =
   ( [ $(mkRelDir "a"),
-      $(mkRelDir "b")
+      $(mkRelDir "b"),
+      $(mkRelDir "readonly-dir")
     ],
     [ $(mkRelFile "one.txt")
     ]
   )
 
--- | Structure of populated directory as it should be scanned by
--- 'listDirRecurWith' function using predicates to filter out dir 'c' and the
--- file 'two.txt'
+-- | Structure of the populated directory as it should be scanned by
+-- 'listDirRecurWith' function using predicates to filter out dir 'c' and
+-- the file @two.txt@.
 populatedDirRecurWith :: ([Path Rel Dir], [Path Rel File])
 populatedDirRecurWith =
   ( [ $(mkRelDir "a"),
-      $(mkRelDir "b")
+      $(mkRelDir "b"),
+      $(mkRelDir "readonly-dir")
     ],
-    [ $(mkRelFile "a/c/three.txt"), -- via symbolic link
+    [ $(mkRelFile "a/c/three.txt"), -- via a symbolic link
       $(mkRelFile "b/c/three.txt"),
       $(mkRelFile "one.txt")
     ]
