diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,20 @@
+*Path IO follows [SemVer](https://semver.org/).*
+
+## Path IO 1.8.2
+
+* Maintenance release, compatibility with GHC 9.6, 9.8, and 9.10.
+
+## 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
+  72](https://github.com/mrkkrp/path-io/pull/72).
+
 ## Path IO 1.7.0
 
 * Added `doesPathExist`, `getFileSize`, `renamePath`, and
diff --git a/Path/IO.hs b/Path/IO.hs
--- a/Path/IO.hs
+++ b/Path/IO.hs
@@ -126,19 +126,19 @@
 import Control.Monad.Trans.Class (lift)
 import Control.Monad.Trans.Maybe (MaybeT (..), runMaybeT)
 import Control.Monad.Trans.Writer.Strict (WriterT, execWriterT, tell)
-import qualified Data.DList as DList
+import Data.DList qualified as DList
 import Data.Either (lefts, rights)
 import Data.Kind (Type)
 import Data.List ((\\))
-import qualified Data.Set as S
+import Data.Set qualified as S
 import Data.Time (UTCTime)
 import Path
-import qualified System.Directory as D
-import qualified System.FilePath as F
+import System.Directory qualified as D
+import System.FilePath qualified as F
 import System.IO (Handle)
 import System.IO.Error (isDoesNotExistError)
-import qualified System.IO.Temp as T
-import qualified System.PosixCompat.Files as P
+import System.IO.Temp qualified as T
+import System.PosixCompat.Files qualified as P
 
 ----------------------------------------------------------------------------
 -- Actions on directories
@@ -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
@@ -487,7 +487,7 @@
 -- traversing symlinked directories, but recreating symlinks in the target
 -- directory according to their targets in the source directory.
 copyDirRecur ::
-  (MonadIO m, MonadCatch m) =>
+  (MonadIO m) =>
   -- | Source
   Path b0 Dir ->
   -- | Destination
@@ -512,7 +512,7 @@
 -- traversing symlinked directories, but recreating symlinks in the target
 -- directory according to their targets in the source directory.
 copyDirRecur' ::
-  (MonadIO m, MonadCatch m) =>
+  (MonadIO m) =>
   -- | Source
   Path b0 Dir ->
   -- | Destination
@@ -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)
 
@@ -1102,12 +1102,54 @@
   makeRelative b p = parseRelDir (F.makeRelative (toFilePath b) (toFilePath p))
   makeRelativeToCurrentDir p = liftIO $ getCurrentDir >>= flip makeRelative p
 
+-- | @since 1.8.0
+instance AnyPath (SomeBase File) where
+  type AbsPath (SomeBase File) = Path Abs File
+  type RelPath (SomeBase File) = Path Rel File
+
+  canonicalizePath s = case s of
+    Abs a -> canonicalizePath a
+    Rel a -> canonicalizePath a
+
+  makeAbsolute s = case s of
+    Abs a -> makeAbsolute a
+    Rel a -> makeAbsolute a
+
+  makeRelative r s = case s of
+    Abs a -> makeRelative r a
+    Rel a -> makeRelative r a
+
+  makeRelativeToCurrentDir s = case s of
+    Abs a -> makeRelativeToCurrentDir a
+    Rel a -> makeRelativeToCurrentDir a
+
+-- | @since 1.8.0
+instance AnyPath (SomeBase Dir) where
+  type AbsPath (SomeBase Dir) = Path Abs Dir
+  type RelPath (SomeBase Dir) = Path Rel Dir
+
+  canonicalizePath s = case s of
+    Abs a -> canonicalizePath a
+    Rel a -> canonicalizePath a
+
+  makeAbsolute s = case s of
+    Abs a -> makeAbsolute a
+    Rel a -> makeAbsolute a
+
+  makeRelative r s = case s of
+    Abs a -> makeRelative r a
+    Rel a -> makeRelative r a
+
+  makeRelativeToCurrentDir s = case s of
+    Abs a -> makeRelativeToCurrentDir a
+    Rel a -> makeRelativeToCurrentDir a
+
 -- | Append stringly-typed path to an absolute path and then canonicalize
 -- it.
 --
 -- @since 0.3.0
 resolveFile ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Base directory
   Path Abs Dir ->
   -- | Path to resolve
@@ -1119,7 +1161,7 @@
 --
 -- @since 0.3.0
 resolveFile' ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Path to resolve
   FilePath ->
   m (Path Abs File)
@@ -1129,7 +1171,7 @@
 --
 -- @since 0.3.0
 resolveDir ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Base directory
   Path Abs Dir ->
   -- | Path to resolve
@@ -1141,7 +1183,7 @@
 --
 -- @since 0.3.0
 resolveDir' ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Path to resolve
   FilePath ->
   m (Path Abs Dir)
@@ -1180,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
@@ -1224,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
@@ -1237,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
@@ -1248,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
@@ -1266,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
@@ -1275,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
@@ -1293,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
@@ -1306,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
@@ -1355,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
@@ -1392,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
@@ -1412,7 +1454,7 @@
 --
 -- @since 1.5.0
 removeDirLink ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Path to the link to be removed
   Path b Dir ->
   m ()
@@ -1431,7 +1473,7 @@
 --
 -- @since 1.5.0
 getSymlinkTarget ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Symlink path
   Path b t ->
   m FilePath
@@ -1449,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
 
 ----------------------------------------------------------------------------
@@ -1547,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
@@ -1570,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'
@@ -1590,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'
@@ -1610,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)
@@ -1662,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
@@ -1674,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
@@ -1706,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.
@@ -1733,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.
@@ -1760,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.
@@ -1775,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
 
 ----------------------------------------------------------------------------
@@ -1784,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
@@ -1796,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
@@ -1810,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
@@ -1829,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/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,6 +0,0 @@
-module Main (main) where
-
-import Distribution.Simple
-
-main :: IO ()
-main = defaultMain
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.7.0
+version:         1.8.2
 license:         BSD-3-Clause
 license-file:    LICENSE.md
 maintainer:      Mark Karpov <markkarpov92@gmail.com>
 author:          Mark Karpov <markkarpov92@gmail.com>
-tested-with:     ghc ==8.10.7 ghc ==9.0.2 ghc ==9.2.1
+tested-with:     ghc ==9.6.5 ghc ==9.8.2 ghc ==9.10.1
 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’
@@ -27,49 +27,48 @@
 
 library
     exposed-modules:  Path.IO
-    default-language: Haskell2010
+    default-language: GHC2021
     build-depends:
-        base >=4.13 && <5.0,
+        base >=4.15 && <5,
         containers,
         directory >=1.3.2.0 && <1.4,
-        dlist >=0.8 && <2.0,
+        dlist >=0.8 && <2,
         exceptions >=0.8 && <0.11,
-        filepath >=1.2 && <1.5,
-        path >=0.6 && <0.10,
+        filepath >=1.2 && <1.6,
+        path >=0.7.1 && <0.10,
         temporary >=1.1 && <1.4,
-        time >=1.4 && <1.13,
+        time >=1.4 && <1.15,
         transformers >=0.3 && <0.7,
         unix-compat
 
     if flag(dev)
-        ghc-options: -Wall -Werror
+        ghc-options:
+            -Wall -Werror -Wredundant-constraints -Wpartial-fields
+            -Wunused-packages
 
     else
         ghc-options: -O2 -Wall
 
-    if flag(dev)
-        ghc-options:
-            -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns
-            -Wnoncanonical-monad-instances
-
 test-suite tests
     type:             exitcode-stdio-1.0
     main-is:          Main.hs
     hs-source-dirs:   tests
-    default-language: Haskell2010
+    default-language: GHC2021
     build-depends:
-        base >=4.13 && <5.0,
-        directory >=1.3.2.0 && <1.4,
+        base >=4.15 && <5,
         exceptions >=0.8 && <0.11,
-        hspec >=2.0 && <3.0,
-        filepath >=1.2 && <1.5,
-        path >=0.6 && <0.10,
+        hspec >=2 && <3,
+        path >=0.7.1 && <0.10,
         path-io,
-        transformers >=0.3 && <0.7,
         unix-compat
 
     if flag(dev)
-        ghc-options: -Wall -Werror
+        ghc-options:
+            -Wall -Werror -Wredundant-constraints -Wpartial-fields
+            -Wunused-packages
 
     else
         ghc-options: -O2 -Wall
+
+    if impl(ghc >=9.8)
+        ghc-options: -Wno-x-partial
diff --git a/tests/Main.hs b/tests/Main.hs
--- a/tests/Main.hs
+++ b/tests/Main.hs
@@ -166,7 +166,7 @@
 
 -- | Follows symbolic links.
 listDirRecurCyclic ::
-  (MonadIO m, MonadThrow m) =>
+  (MonadIO m) =>
   -- | Directory to list
   Path b Dir ->
   -- | Sub-directories and files
@@ -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")
     ]
