diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,21 @@
+## Zip 1.0.0
+
+* Works with `conduit-1.3.0`, `conduit-extra-1.3.0`, `resourcet-1.2.0` and
+  `bzlib-conduit-0.3.0`.
+
+* Stop depending on `path`, `path-io`, and `plub-b`.
+
+* Made the module `Codec.Archive.Zip.Type` non-public.
+
+* Remove derived instances of `Data` and `Generic` for `EntrySelector` not
+  to expose its inner structure.
+
+* Change signature of the `loadEntry` function, its second argument is now
+  just `EntrySelector` of the entry to add.
+
+* The second argument of `packDirRecur` now receives paths that are relative
+  to the root of the directory we pack.
+
 ## Zip 0.2.0
 
 * Added `MonadBase` and `MonadBaseControl` instances for the `ZipArchive`
diff --git a/Codec/Archive/Zip.hs b/Codec/Archive/Zip.hs
--- a/Codec/Archive/Zip.hs
+++ b/Codec/Archive/Zip.hs
@@ -1,6 +1,6 @@
 -- |
 -- Module      :  Codec.Archive.Zip
--- Copyright   :  © 2016–2017 Mark Karpov
+-- Copyright   :  © 2016–2018 Mark Karpov
 -- License     :  BSD 3 clause
 --
 -- Maintainer  :  Mark Karpov <markkarpov92@gmail.com>
@@ -11,13 +11,10 @@
 -- There are three things that should be clarified right away, to avoid
 -- confusion in the future.
 --
--- First, we use the 'EntrySelector' type that can be obtained from 'Path'
--- 'Rel' 'File' paths. This method may seem awkward at first, but it will
--- protect you from the problems with portability when your archive is
--- unpacked on a different platform. Using well-typed paths is also
--- something you should consider doing in your projects anyway. Even if you
--- don't want to use the "Path" module in your project, it's easy to marshal
--- 'FilePath' to 'Path' just before using functions from the library.
+-- First, we use the 'EntrySelector' type that can be obtained from relative
+-- 'FilePath's (paths to directories are not allowed). This method may seem
+-- awkward at first, but it will protect you from the problems with
+-- portability when your archive is unpacked on a different platform.
 --
 -- The second thing, that is rather a consequence of the first, is that
 -- there is no way to add directories, or to be precise, /empty directories/
@@ -38,48 +35,41 @@
 -- An example of a program that prints a list of archive entries:
 --
 -- > import Codec.Archive.Zip
--- > import Path.IO (resolveFile')
 -- > import System.Environment (getArgs)
 -- > import qualified Data.Map as M
 -- >
 -- > main :: IO ()
 -- > main = do
--- >   [fp]    <- getArgs
--- >   path    <- resolveFile' fp
+-- >   [path]  <- getArgs
 -- >   entries <- withArchive path (M.keys <$> getEntries)
 -- >   mapM_ print entries
 --
 -- Create a Zip archive with a “Hello World” file:
 --
 -- > import Codec.Archive.Zip
--- > import Path (parseRelFile)
--- > import Path.IO (resolveFile')
 -- > import System.Environment (getArgs)
 -- >
 -- > main :: IO ()
 -- > main = do
--- >   [fp]    <- getArgs
--- >   path    <- resolveFile' fp
--- >   s       <- parseRelFile "hello-world.txt" >>= mkEntrySelector
+-- >   [path] <- getArgs
+-- >   s      <- mkEntrySelector "hello-world.txt"
 -- >   createArchive path (addEntry Store "Hello, World!" s)
 --
 -- Extract contents of a specific file and print them:
 --
 -- > import Codec.Archive.Zip
--- > import Path (parseRelFile)
--- > import Path.IO (resolveFile')
 -- > import System.Environment (getArgs)
 -- > import qualified Data.ByteString.Char8 as B
 -- >
 -- > main :: IO ()
 -- > main = do
--- >   [fp,f]  <- getArgs
--- >   path    <- resolveFile' fp
--- >   s       <- parseRelFile f >>= mkEntrySelector
--- >   bs      <- withArchive path (getEntry s)
+-- >   [path,f] <- getArgs
+-- >   s        <- mkEntrySelector f
+-- >   bs       <- withArchive path (getEntry s)
 -- >   B.putStrLn bs
 
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE LambdaCase                 #-}
 {-# LANGUAGE MultiParamTypeClasses      #-}
 {-# LANGUAGE TypeFamilies               #-}
 
@@ -143,6 +133,7 @@
 where
 
 import Codec.Archive.Zip.Type
+import Conduit (PrimMonad)
 import Control.Monad
 import Control.Monad.Base (MonadBase (..))
 import Control.Monad.Catch
@@ -150,21 +141,25 @@
 import Control.Monad.Trans.Control (MonadBaseControl (..))
 import Control.Monad.Trans.Resource (ResourceT, MonadResource)
 import Data.ByteString (ByteString)
-import Data.Conduit (Source, Sink, (.|))
+import Data.Conduit (ConduitT, (.|))
 import Data.Map.Strict (Map, (!))
 import Data.Sequence (Seq, (|>))
 import Data.Text (Text)
 import Data.Time.Clock (UTCTime)
+import Data.Void
 import Data.Word (Word16)
-import Path
-import Path.IO
+import System.Directory
+import System.FilePath ((</>))
+import System.IO.Error (isDoesNotExistError)
 import qualified Codec.Archive.Zip.Internal as I
 import qualified Data.Conduit               as C
 import qualified Data.Conduit.Binary        as CB
 import qualified Data.Conduit.List          as CL
+import qualified Data.DList                 as DList
 import qualified Data.Map.Strict            as M
 import qualified Data.Sequence              as S
 import qualified Data.Set                   as E
+import qualified System.FilePath            as FP
 
 ----------------------------------------------------------------------------
 -- Archive monad
@@ -205,8 +200,8 @@
 -- @since 0.2.0
 
 data ZipState = ZipState
-  { zsFilePath  :: Path Abs File
-    -- ^ Absolute path to zip archive
+  { zsFilePath  :: FilePath
+    -- ^ Path to zip archive
   , zsEntries   :: Map EntrySelector EntryDescription
     -- ^ Actual collection of entries
   , zsArchive   :: ArchiveDescription
@@ -221,7 +216,7 @@
 -- work with an existing archive.
 
 createArchive :: MonadIO m
-  => Path b File       -- ^ Location of archive file to create
+  => FilePath          -- ^ Location of archive file to create
   -> ZipArchive a      -- ^ Actions that form archive's content
   -> m a
 createArchive path m = liftIO $ do
@@ -261,7 +256,7 @@
 -- with this library.
 
 withArchive :: MonadIO m
-  => Path b File       -- ^ Location of archive to work with
+  => FilePath          -- ^ Location of archive to work with
   -> ZipArchive a      -- ^ Actions on that archive
   -> m a
 withArchive path m = liftIO $ do
@@ -324,9 +319,9 @@
 -- @since 0.1.3
 
 getEntrySource
-  :: MonadResource m
-  => EntrySelector
-  -> ZipArchive (Source m ByteString)
+  :: (PrimMonad m, MonadThrow m, MonadResource m)
+  => EntrySelector     -- ^ Selector that identifies archive entry
+  -> ZipArchive (ConduitT () ByteString m ())
 getEntrySource s = do
   path  <- getFilePath
   mdesc <- M.lookup s <$> getEntries
@@ -341,7 +336,7 @@
 sourceEntry
   :: EntrySelector
      -- ^ Selector that identifies archive entry
-  -> Sink ByteString (ResourceT IO) a
+  -> ConduitT ByteString Void (ResourceT IO) a
      -- ^ Sink where to stream entry contents
   -> ZipArchive a
      -- ^ Contents of the entry (if found)
@@ -355,9 +350,9 @@
 
 saveEntry
   :: EntrySelector     -- ^ Selector that identifies archive entry
-  -> Path b File       -- ^ Where to save the file
+  -> FilePath          -- ^ Where to save the file
   -> ZipArchive ()
-saveEntry s path = sourceEntry s (CB.sinkFile (toFilePath path))
+saveEntry s path = sourceEntry s (CB.sinkFile path)
 
 -- | Calculate CRC32 check sum and compare it with the value read from the
 -- archive. The function returns 'True' when the check sums are the
@@ -378,14 +373,14 @@
 -- | Unpack the entire archive into the specified directory. The directory
 -- will be created if it does not exist.
 
-unpackInto :: Path b Dir -> ZipArchive ()
+unpackInto :: FilePath -> ZipArchive ()
 unpackInto dir' = do
   selectors <- M.keysSet <$> getEntries
   unless (null selectors) $ do
     dir <- liftIO (makeAbsolute dir')
-    liftIO (ensureDir dir)
-    let dirs = E.map (parent . (dir </>) . unEntrySelector) selectors
-    forM_ dirs (liftIO . ensureDir)
+    liftIO (createDirectoryIfMissing True dir)
+    let dirs = E.map (FP.takeDirectory . (dir </>) . unEntrySelector) selectors
+    forM_ dirs (liftIO . createDirectoryIfMissing True)
     forM_ selectors $ \s ->
       saveEntry s (dir </> unEntrySelector s)
 
@@ -415,7 +410,7 @@
 
 sinkEntry
   :: CompressionMethod -- ^ Compression method to use
-  -> Source (ResourceT IO) ByteString -- ^ Source of entry contents
+  -> ConduitT () ByteString (ResourceT IO) () -- ^ Source of entry contents
   -> EntrySelector     -- ^ Name of entry to add
   -> ZipArchive ()
 sinkEntry t src s = addPending (I.SinkEntry t src s)
@@ -424,14 +419,13 @@
 
 loadEntry
   :: CompressionMethod -- ^ Compression method to use
-  -> (Path Abs File -> ZipArchive EntrySelector) -- ^ How to get 'EntrySelector'
-  -> Path b File       -- ^ Path to file to add
+  -> EntrySelector     -- ^ Name of entry to add
+  -> FilePath          -- ^ Path to file to add
   -> ZipArchive ()
-loadEntry t f path = do
+loadEntry t s path = do
   apath   <- liftIO (canonicalizePath path)
-  s       <- f apath
   modTime <- liftIO (getModificationTime path)
-  let src = CB.sourceFile (toFilePath apath)
+  let src = CB.sourceFile apath
   addPending (I.SinkEntry t src s)
   addPending (I.SetModTime modTime s)
 
@@ -439,9 +433,9 @@
 -- exist in that archive, 'EntryDoesNotExist' will be eventually thrown.
 
 copyEntry
-  :: Path b File       -- ^ Path to archive to copy from
+  :: FilePath          -- ^ Path to archive to copy from
   -> EntrySelector     -- ^ Name of entry (in source archive) to copy
-  -> EntrySelector     -- ^ Name of entry to insert (in actual archive)
+  -> EntrySelector     -- ^ Name of entry to insert (in current archive)
   -> ZipArchive ()
 copyEntry path s' s = do
   apath <- liftIO (canonicalizePath path)
@@ -450,17 +444,20 @@
 -- | Add an entire directory to the archive. Please note that due to the
 -- design of the library, empty sub-directories won't be added.
 --
--- The action can throw the same exceptions as 'listDirRecur' and
--- 'InvalidEntrySelector'.
+-- The action can throw 'InvalidEntrySelector'.
 
 packDirRecur
   :: CompressionMethod -- ^ Compression method to use
-  -> (Path Abs File -> ZipArchive EntrySelector) -- ^ How to get 'EntrySelector'
-  -> Path b Dir        -- ^ Path to directory to add
+  -> (FilePath -> ZipArchive EntrySelector)
+     -- ^ How to get 'EntrySelector' from a path relative to the root of the
+     -- directory we pack
+  -> FilePath          -- ^ Path to directory to add
   -> ZipArchive ()
 packDirRecur t f path = do
-  files <- snd <$> liftIO (listDirRecur path)
-  mapM_ (loadEntry t f) files
+  files <- liftIO (listDirRecur path)
+  forM_ files $ \x -> do
+    s <- f x
+    loadEntry t s (path </> x)
 
 -- | Rename an entry in the archive. If the entry does not exist, nothing
 -- will happen.
@@ -578,7 +575,7 @@
   odesc    <- getArchiveDescription
   oentries <- getEntries
   actions  <- getPending
-  exists   <- doesFileExist file
+  exists   <- liftIO (doesFileExist file)
   unless (S.null actions && exists) $ do
     liftIO (I.commit file odesc oentries actions)
     -- NOTE The most robust way to update internal description of the
@@ -597,7 +594,7 @@
 -- | Get the path of the actual archive file from inside of 'ZipArchive'
 -- monad.
 
-getFilePath :: ZipArchive (Path Abs File)
+getFilePath :: ZipArchive FilePath
 getFilePath = ZipArchive (gets zsFilePath)
 
 -- | Get the collection of pending actions.
@@ -615,3 +612,34 @@
 
 addPending :: I.PendingAction -> ZipArchive ()
 addPending a = modifyActions (|> a)
+
+-- | Recursively list a directory. Do not return paths to empty directories.
+
+listDirRecur :: FilePath -> IO [FilePath]
+listDirRecur path = DList.toList <$> go ""
+  where
+    go adir = do
+      let cdir = path </> adir
+      raw <- listDirectory cdir
+      fmap mconcat . forM raw $ \case
+        ""   -> return mempty
+        "."  -> return mempty
+        ".." -> return mempty
+        x    -> do
+          let fullx = cdir </> x
+              adir' = adir </> x
+          isFile <- doesFileExist      fullx
+          isDir  <- doesDirectoryExist fullx
+          if isFile
+            then return (DList.singleton adir')
+            else if isDir
+                   then go adir'
+                   else return mempty
+
+-- | Perform an action ignoring IO exceptions it may throw.
+
+ignoringAbsence :: IO () -> IO ()
+ignoringAbsence io = catchJust select io handler
+  where
+    select e = if isDoesNotExistError e then Just e else Nothing
+    handler = const (return ())
diff --git a/Codec/Archive/Zip/CP437.hs b/Codec/Archive/Zip/CP437.hs
--- a/Codec/Archive/Zip/CP437.hs
+++ b/Codec/Archive/Zip/CP437.hs
@@ -1,6 +1,6 @@
 -- |
 -- Module      :  Codec.Archive.Zip.CP437
--- Copyright   :  © 2016–2017 Mark Karpov
+-- Copyright   :  © 2016–2018 Mark Karpov
 -- License     :  BSD 3 clause
 --
 -- Maintainer  :  Mark Karpov <markkarpov92@gmail.com>
diff --git a/Codec/Archive/Zip/Internal.hs b/Codec/Archive/Zip/Internal.hs
--- a/Codec/Archive/Zip/Internal.hs
+++ b/Codec/Archive/Zip/Internal.hs
@@ -1,6 +1,6 @@
 -- |
 -- Module      :  Codec.Archive.Zip.Internal
--- Copyright   :  © 2016–2017 Mark Karpov
+-- Copyright   :  © 2016–2018 Mark Karpov
 -- License     :  BSD 3 clause
 --
 -- Maintainer  :  Mark Karpov <markkarpov92@gmail.com>
@@ -22,32 +22,34 @@
 
 import Codec.Archive.Zip.CP437 (decodeCP437)
 import Codec.Archive.Zip.Type
+import Conduit (PrimMonad)
 import Control.Applicative (many, (<|>))
+import Control.Exception (bracketOnError)
 import Control.Monad
-import Control.Monad.Catch
+import Control.Monad.Catch (MonadThrow (..))
 import Control.Monad.Trans.Maybe
 import Control.Monad.Trans.Resource (ResourceT, MonadResource)
 import Data.Bits
 import Data.Bool (bool)
 import Data.ByteString (ByteString)
 import Data.Char (ord)
-import Data.Conduit (Conduit, Source, Sink, (.|), ZipSink (..))
+import Data.Conduit (ConduitT, (.|), ZipSink (..))
 import Data.Digest.CRC32 (crc32Update)
 import Data.Fixed (Fixed (..))
 import Data.Foldable (foldl')
 import Data.Map.Strict (Map, (!))
 import Data.Maybe (fromJust, catMaybes, isNothing)
-import Data.Monoid ((<>))
 import Data.Sequence (Seq, (><), (|>))
 import Data.Serialize
 import Data.Text (Text)
 import Data.Time
 import Data.Version
+import Data.Void
 import Data.Word (Word16, Word32)
 import Numeric.Natural (Natural)
-import Path
+import System.Directory
+import System.FilePath
 import System.IO
-import System.PlanB
 import qualified Data.ByteString     as B
 import qualified Data.Conduit        as C
 import qualified Data.Conduit.BZlib  as BZ
@@ -67,9 +69,11 @@
 -- archive.
 
 data PendingAction
-  = SinkEntry CompressionMethod (Source (ResourceT IO) ByteString) EntrySelector
+  = SinkEntry CompressionMethod
+              (ConduitT () ByteString (ResourceT IO) ())
+              EntrySelector
     -- ^ Add entry given its 'Source'
-  | CopyEntry (Path Abs File) EntrySelector EntrySelector
+  | CopyEntry FilePath EntrySelector EntrySelector
     -- ^ Copy an entry form another archive without re-compression
   | RenameEntry EntrySelector EntrySelector
     -- ^ Change name the entry inside archive
@@ -96,8 +100,9 @@
 -- archive.
 
 data ProducingActions = ProducingActions
-  { paCopyEntry :: Map (Path Abs File) (Map EntrySelector EntrySelector)
-  , paSinkEntry :: Map EntrySelector (Source (ResourceT IO) ByteString) }
+  { paCopyEntry :: Map FilePath (Map EntrySelector EntrySelector)
+  , paSinkEntry :: Map EntrySelector (ConduitT () ByteString (ResourceT IO) ())
+  }
 
 -- | Collection of editing actions, that is, actions that modify already
 -- existing entries.
@@ -187,9 +192,9 @@
 -- with this library.
 
 scanArchive
-  :: Path Abs File     -- ^ Path to archive to scan
+  :: FilePath     -- ^ Path to archive to scan
   -> IO (ArchiveDescription, Map EntrySelector EntryDescription)
-scanArchive path = withBinaryFile (toFilePath path) ReadMode $ \h -> do
+scanArchive path = withBinaryFile path ReadMode $ \h -> do
   mecdOffset <- locateECD path h
   case mecdOffset of
     Just ecdOffset -> do
@@ -212,16 +217,16 @@
 -- compressed or uncompressed depending on the third argument.
 
 sourceEntry
-  :: MonadResource m
-  => Path Abs File     -- ^ Path to archive that contains the entry
+  :: (PrimMonad m, MonadThrow m, MonadResource m)
+  => FilePath          -- ^ Path to archive that contains the entry
   -> EntryDescription  -- ^ Information needed to extract entry of interest
   -> Bool              -- ^ Should we stream uncompressed data?
-  -> Source m ByteString -- ^ Source of uncompressed data
+  -> ConduitT () ByteString m () -- ^ Source of uncompressed data
 sourceEntry path EntryDescription {..} d =
   source .| CB.isolate (fromIntegral edCompressedSize) .| decompress
   where
     source = CB.sourceIOHandle $ do
-      h <- openFile (toFilePath path) ReadMode
+      h <- openFile path ReadMode
       hSeek h AbsoluteSeek (fromIntegral edOffset)
       localHeader <- B.hGet h 30
       case runGet getLocalHeaderGap localHeader of
@@ -238,27 +243,42 @@
 -- in one pass, and then they are performed in the most efficient way.
 
 commit
-  :: Path Abs File     -- ^ Location of archive file to edit or create
+  :: FilePath          -- ^ Location of archive file to edit or create
   -> ArchiveDescription -- ^ Archive description
   -> Map EntrySelector EntryDescription -- ^ Current list of entires
   -> Seq PendingAction -- ^ Collection of pending actions
   -> IO ()
 commit path ArchiveDescription {..} entries xs =
-  withNewFile (overrideIfExists      <>
-               nameTemplate ".zip"   <>
-               tempDir (parent path) <>
-               moveByRenaming) path $ \temp -> do
+  withNewFile path $ \h -> do
     let (ProducingActions coping sinking, editing) =
           optimize (toRecreatingActions path entries >< xs)
         comment = predictComment adComment xs
-    withBinaryFile (toFilePath temp) WriteMode $ \h -> do
-      copiedCD <- M.unions <$> forM (M.keys coping) (\srcPath ->
-        copyEntries h srcPath (coping ! srcPath) editing)
-      let sinkingKeys = M.keys $ sinking `M.difference` copiedCD
-      sunkCD   <- M.fromList <$> forM sinkingKeys (\selector ->
-        sinkEntry h selector GenericOrigin (sinking ! selector) editing)
-      writeCD h comment (copiedCD `M.union` sunkCD)
+    copiedCD <- M.unions <$> forM (M.keys coping) (\srcPath ->
+      copyEntries h srcPath (coping ! srcPath) editing)
+    let sinkingKeys = M.keys $ sinking `M.difference` copiedCD
+    sunkCD   <- M.fromList <$> forM sinkingKeys (\selector ->
+      sinkEntry h selector GenericOrigin (sinking ! selector) editing)
+    writeCD h comment (copiedCD `M.union` sunkCD)
 
+-- | Create a new file with the guarantee that in case of exception the old
+-- file will be preserved intact. The file is only updated\/replaced if the
+-- second argument finishes without exceptions.
+
+withNewFile
+  :: FilePath          -- ^ Name of file to create
+  -> (Handle -> IO ()) -- ^ Action that writes to given 'Handle'
+  -> IO ()
+withNewFile fpath action =
+  bracketOnError allocate release $ \(path, h) -> do
+    action h
+    hClose h
+    renameFile path fpath
+  where
+    allocate = openBinaryTempFile (takeDirectory fpath) ".zip"
+    release (path, h) = do
+      hClose h
+      removeFile path
+
 -- | Determine what comment in new archive will look like given its original
 -- value and a collection of pending actions.
 
@@ -274,7 +294,7 @@
 -- actions that re-create those entires.
 
 toRecreatingActions
-  :: Path Abs File     -- ^ Name of the archive file where entires are found
+  :: FilePath     -- ^ Name of the archive file where entires are found
   -> Map EntrySelector EntryDescription -- ^ Actual list of entires
   -> Seq PendingAction -- ^ Actions that recreate the archive entries
 toRecreatingActions path entries = E.foldl' f S.empty (M.keysSet entries)
@@ -355,7 +375,7 @@
 
 copyEntries
   :: Handle            -- ^ Opened 'Handle' of zip archive file
-  -> Path Abs File     -- ^ Path to file from which to copy the entries
+  -> FilePath          -- ^ Path to the file to copy the entries from
   -> Map EntrySelector EntrySelector
      -- ^ 'Map' from original name to name to use in new archive
   -> EditingActions    -- ^ Additional info that can influence result
@@ -377,7 +397,7 @@
   :: Handle            -- ^ Opened 'Handle' of zip archive file
   -> EntrySelector     -- ^ Name of entry to add
   -> EntryOrigin       -- ^ Origin of entry (can contain additional info)
-  -> Source (ResourceT IO) ByteString -- ^ Source of entry contents
+  -> ConduitT () ByteString (ResourceT IO) () -- ^ Source of entry contents
   -> EditingActions    -- ^ Additional info that can influence result
   -> IO (EntrySelector, EntryDescription)
      -- ^ Info to generate central directory file headers later
@@ -449,7 +469,7 @@
 sinkData
   :: Handle            -- ^ Opened 'Handle' of zip archive file
   -> CompressionMethod -- ^ Compression method to apply
-  -> Sink ByteString (ResourceT IO) DataDescriptor
+  -> ConduitT ByteString Void (ResourceT IO) DataDescriptor
      -- ^ 'Sink' where to stream data
 sinkData h compression = do
   let sizeSink  = CL.fold (\acc input -> fromIntegral (B.length input) + acc) 0
@@ -581,8 +601,7 @@
             , edOffset           = z64efOffset           z64ef
             , edComment = if commentSize == 0 then Nothing else comment
             , edExtraField       = extraField }
-      in return $ (,desc) <$>
-         (fileName >>= parseRelFile . T.unpack >>= mkEntrySelector)
+      in return $ (,desc) <$> (fileName >>= mkEntrySelector . T.unpack)
 
 -- | Parse an extra-field.
 
@@ -791,7 +810,7 @@
 -- | Find absolute offset of end of central directory record or, if present,
 -- Zip64 end of central directory record.
 
-locateECD :: Path Abs File -> Handle -> IO (Maybe Integer)
+locateECD :: FilePath -> Handle -> IO (Maybe Integer)
 locateECD path h = sizeCheck
   where
 
@@ -969,16 +988,16 @@
 -- method.
 
 decompressingPipe
-  :: MonadResource m
+  :: (PrimMonad m, MonadThrow m, MonadResource m)
   => CompressionMethod
-  -> Conduit ByteString m ByteString
+  -> ConduitT ByteString ByteString m ()
 decompressingPipe Store   = C.awaitForever C.yield
 decompressingPipe Deflate = Z.decompress $ Z.WindowBits (-15)
 decompressingPipe BZip2   = BZ.bunzip2
 
 -- | Sink that calculates CRC32 check sum for incoming stream.
 
-crc32Sink :: Sink ByteString (ResourceT IO) Word32
+crc32Sink :: ConduitT ByteString Void (ResourceT IO) Word32
 crc32Sink = CL.fold crc32Update 0
 
 -- | Convert 'UTCTime' to MS-DOS time format.
diff --git a/Codec/Archive/Zip/Type.hs b/Codec/Archive/Zip/Type.hs
--- a/Codec/Archive/Zip/Type.hs
+++ b/Codec/Archive/Zip/Type.hs
@@ -1,18 +1,15 @@
 -- |
 -- Module      :  Codec.Archive.Zip.Type
--- Copyright   :  © 2016–2017 Mark Karpov
+-- Copyright   :  © 2016–2018 Mark Karpov
 -- License     :  BSD 3 clause
 --
 -- Maintainer  :  Mark Karpov <markkarpov92@gmail.com>
 -- Stability   :  experimental
 -- Portability :  portable
 --
--- Types used by the package. You don't usually need to import this module,
--- because "Codec.Archive.Zip" re-exports everything you may need, import
--- that module instead.
+-- Types used by the package.
 
 {-# LANGUAGE DeriveDataTypeable #-}
-{-# LANGUAGE DeriveGeneric      #-}
 
 module Codec.Archive.Zip.Type
   ( -- * Entry selector
@@ -24,13 +21,12 @@
     -- * Entry description
   , EntryDescription (..)
   , CompressionMethod (..)
-    -- * Archive desrciption
+    -- * Archive description
   , ArchiveDescription (..)
     -- * Exceptions
   , ZipException (..) )
 where
 
-import Control.Arrow ((>>>))
 import Control.Exception (Exception)
 import Control.Monad.Catch (MonadThrow (..))
 import Data.ByteString (ByteString)
@@ -38,15 +34,13 @@
 import Data.Data (Data)
 import Data.List.NonEmpty (NonEmpty)
 import Data.Map (Map)
-import Data.Maybe (mapMaybe, fromJust)
+import Data.Maybe (mapMaybe)
 import Data.Text (Text)
 import Data.Time.Clock (UTCTime)
 import Data.Typeable (Typeable)
 import Data.Version (Version)
 import Data.Word (Word16, Word32)
-import GHC.Generics (Generic)
 import Numeric.Natural
-import Path
 import qualified Data.ByteString         as B
 import qualified Data.CaseInsensitive    as CI
 import qualified Data.List.NonEmpty      as NE
@@ -60,9 +54,9 @@
 -- Entry selector
 
 -- | This data type serves for naming and selection of archive entries. It
--- can be created only with the help of the smart constructor
--- 'mkEntrySelector', and it's the only “key” that can be used to select
--- files in archive or to name new files.
+-- can be created only with help of the smart constructor 'mkEntrySelector',
+-- and it's the only “key” that can be used to refer to files in archive or
+-- to name new archive entries.
 --
 -- The abstraction is crucial for ensuring that created archives are
 -- portable across operating systems, file systems, and different platforms.
@@ -71,74 +65,73 @@
 -- are used to name files inside archive, as it's recommended in the
 -- specification. It also guarantees that forward slashes are used when the
 -- path is stored inside archive for compatibility with Unix-like operating
--- systems (as it is recommended in the specification). On the other hand,
--- in can be rendered as ordinary relative file path in OS-specific format,
--- when needed.
+-- systems (as recommended in the specification). On the other hand, in can
+-- be rendered as an ordinary relative file path in OS-specific format when
+-- needed.
 
 newtype EntrySelector = EntrySelector
   { unES :: NonEmpty (CI String)
     -- ^ Path pieces of relative path inside archive
-  } deriving (Eq, Ord, Typeable, Data, Generic)
+  } deriving (Eq, Ord, Typeable)
 
 instance Show EntrySelector where
   show = show . unEntrySelector
 
--- | Create an 'EntrySelector' from @'Path' 'Rel' 'File'@. To avoid problems
--- with distribution of the archive, characters that some operating systems
--- do not expect in paths are not allowed. Proper paths should pass these
--- checks:
+-- | Create an 'EntrySelector' from a 'FilePath'. To avoid problems with
+-- distribution of the archive, characters that some operating systems do
+-- not expect in paths are not allowed.
 --
+-- Argument to 'mkEntrySelector' should pass these checks:
+--
 --     * 'System.FilePath.Posix.isValid'
 --     * 'System.FilePath.Windows.isValid'
---     * binary representation of normalized path should be not longer than
+--     * it is a relative path without slash at the end
+--     * binary representations of normalized path should be not longer than
 --       65535 bytes
 --
 -- This function can throw an 'EntrySelectorException'.
 
-mkEntrySelector :: MonadThrow m => Path Rel File -> m EntrySelector
+mkEntrySelector :: MonadThrow m => FilePath -> m EntrySelector
 mkEntrySelector path =
-  let fp           = toFilePath path
-      g x          = if null x then Nothing else Just (CI.mk x)
-      preparePiece = g . filter (not . FP.isPathSeparator)
-      pieces       = mapMaybe preparePiece (FP.splitPath fp)
-      selector     = EntrySelector (NE.fromList pieces)
-      binLength    = B.length . T.encodeUtf8 . getEntryName
-  in if Posix.isValid fp   &&
-        Windows.isValid fp &&
-        fp /= "."          && -- work around a bug in the path package
-        not (null pieces)  &&
-        binLength selector <= 0xffff
-       then return selector
-       else throwM (InvalidEntrySelector path)
+  let f x =
+        case filter (not . FP.isPathSeparator) x of
+          [] -> Nothing
+          xs -> Just (CI.mk xs)
+      giveup = throwM (InvalidEntrySelector path)
+  in case NE.nonEmpty (mapMaybe f (FP.splitPath path)) of
+       Nothing -> giveup
+       Just pieces ->
+         let selector  = EntrySelector pieces
+             binLength = B.length . T.encodeUtf8 . getEntryName
+         in if Posix.isValid   path &&
+               Windows.isValid path &&
+               not (FP.isAbsolute path || FP.hasTrailingPathSeparator path) &&
+               (CI.mk "."  `notElem` pieces) &&
+               (CI.mk ".." `notElem` pieces) &&
+               binLength selector <= 0xffff
+              then return selector
+              else giveup
 
--- | Make a relative path from 'EntrySelector'. Every 'EntrySelector'
--- produces a single @'Path' 'Rel' 'File'@ that corresponds to it.
+-- | Restore a relative path from 'EntrySelector'. Every 'EntrySelector'
+-- corresponds to a single 'FilePath'.
 
-unEntrySelector :: EntrySelector -> Path Rel File
-unEntrySelector = unES
-  >>> NE.toList
-  >>> fmap CI.original
-  >>> FP.joinPath
-  >>> parseRelFile
-  >>> fromJust
+unEntrySelector :: EntrySelector -> FilePath
+unEntrySelector =
+  FP.joinPath . fmap CI.original . NE.toList . unES
 
 -- | Get an entry name in the from that is suitable for writing to file
 -- header, given an 'EntrySelector'.
 
 getEntryName :: EntrySelector -> Text
-getEntryName = unES
-  >>> fmap CI.original
-  >>> NE.intersperse "/"
-  >>> NE.toList
-  >>> concat
-  >>> T.pack
+getEntryName =
+  T.pack . concat . NE.toList . NE.intersperse "/" . fmap CI.original . unES
 
--- | The exception describing various troubles you can have with
+-- | The exception represents various troubles you can have with
 -- 'EntrySelector'.
 
 data EntrySelectorException
-  = InvalidEntrySelector (Path Rel File)
-    -- ^ Selector cannot be created from this path
+  = InvalidEntrySelector FilePath
+    -- ^ 'EntrySelector' cannot be created from this path
   deriving (Eq, Ord, Typeable)
 
 instance Show EntrySelectorException where
@@ -151,8 +144,8 @@
 
 -- | This record represents all information about archive entry that can be
 -- stored in a zip archive. It does not mirror local file header or central
--- directory file header, but their binary representation can be built given
--- this data structure and the actual archive contents.
+-- directory file header, but their binary representations can be built
+-- given this data structure and the actual archive contents.
 
 data EntryDescription = EntryDescription
   { edVersionMadeBy    :: Version -- ^ Version made by
@@ -192,9 +185,9 @@
 -- | The bad things that can happen when you use the library.
 
 data ZipException
-  = EntryDoesNotExist (Path Abs File) EntrySelector
+  = EntryDoesNotExist FilePath EntrySelector
     -- ^ Thrown when you try to get contents of non-existing entry
-  | ParsingFailed     (Path Abs File) String
+  | ParsingFailed FilePath String
     -- ^ Thrown when archive structure cannot be parsed
   deriving (Eq, Ord, Typeable)
 
diff --git a/LICENSE.md b/LICENSE.md
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,4 +1,4 @@
-Copyright © 2016–2017 Mark Karpov
+Copyright © 2016–2018 Mark Karpov
 
 All rights reserved.
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -165,13 +165,10 @@
 manipulate Zip archives. There are three things that should be clarified
 right away, to avoid confusion in the future.
 
-First, we use the `EntrySelector` type that can be obtained from `Path Rel
-File` paths. This method may seem awkward at first, but it will protect you
-from the problems with portability when your archive is unpacked on a
-different platform. Using well-typed paths is also something you should
-consider doing in your projects anyway. Even if you don't want to use the
-`Path` module in your project, it's easy to marshal `FilePath` to `Path`
-just before using functions from the library.
+First, we use the `EntrySelector` type that can be obtained from relative
+`FilePath`s (paths to directories are not allowed). This method may seem
+awkward at first, but it will protect you from the problems with portability
+when your archive is unpacked on a different platform.
 
 The second thing, that is rather a consequence of the first, is that there
 is no way to add directories, or to be precise, *empty directories* to your
@@ -239,7 +236,7 @@
 To add contents from a file, use `loadEntry`:
 
 ```haskell
-λ> let toSelector = const $ parseRelFile "my-entry.txt" >>= mkEntrySelector
+λ> let toSelector = const (mkEntrySelector "my-entry.txt")
 λ> createArchive archivePath (loadEntry BZip2 toSelector myFilePath)
 ```
 
@@ -253,8 +250,7 @@
 It's often desirable to just pack a directory:
 
 ```haskell
-λ> let f = stripDir dir >=> mkEntrySelector
-λ> createArchive archivePath (packDirRecur Deflate f dir)
+λ> createArchive archivePath (packDirRecur Deflate mkEntrySelector dir)
 ```
 
 It's also possible to:
@@ -282,6 +278,6 @@
 
 ## License
 
-Copyright © 2016–2017 Mark Karpov
+Copyright © 2016–2018 Mark Karpov
 
 Distributed under BSD 3 clause license.
diff --git a/tests/Main.hs b/tests/Main.hs
--- a/tests/Main.hs
+++ b/tests/Main.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE CPP               #-}
 {-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE LambdaCase        #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TemplateHaskell   #-}
 {-# OPTIONS -fno-warn-orphans  #-}
 
 module Main (main) where
@@ -9,11 +9,9 @@
 import Codec.Archive.Zip
 import Codec.Archive.Zip.CP437
 import Control.Monad
-import Control.Monad.Catch (catchIOError)
 import Control.Monad.IO.Class
 import Data.Bits (complement)
 import Data.ByteString (ByteString)
-import Data.Foldable (foldl')
 import Data.List (intercalate)
 import Data.Map (Map, (!))
 import Data.Maybe (fromJust)
@@ -21,10 +19,11 @@
 import Data.Text (Text)
 import Data.Time
 import Data.Version
-import Path
-import Path.IO
+import System.Directory
+import System.FilePath ((</>))
 import System.IO
 import System.IO.Error (isDoesNotExistError)
+import System.IO.Temp
 import Test.Hspec
 import Test.QuickCheck
 import qualified Data.ByteString         as B
@@ -32,11 +31,12 @@
 import qualified Data.ByteString.Lazy    as LB
 import qualified Data.Conduit            as C
 import qualified Data.Conduit.List       as CL
-import qualified Data.Map                as M
+import qualified Data.DList              as DList
+import qualified Data.Map.Strict         as M
 import qualified Data.Set                as E
 import qualified Data.Text               as T
 import qualified Data.Text.Encoding      as T
-import qualified System.FilePath.Windows as Windows
+import qualified System.FilePath         as FP
 
 -- | Zip tests. Please note that Zip64 feature is not currently tested
 -- automatically because for it to expose itself we need > 4GB of
@@ -91,16 +91,23 @@
     <$> (ModifiedJulianDay <$> choose (44239, 90989))
     <*> (secondsToDiffTime <$> choose (0, 86399))
 
-instance Arbitrary (Path Rel File) where
+newtype RelPath = RelPath FilePath
+
+instance Show RelPath where
+  show (RelPath path) = show path
+
+instance Arbitrary RelPath where
   arbitrary = do
-    x <- intercalate "/" <$> listOf1 (listOf1 charGen)
-    case parseRelFile x of
+    p <- intercalate "/" <$> listOf1
+      ((++) <$> vectorOf 3 charGen
+            <*> listOf1 charGen)
+    case mkEntrySelector p of
       Nothing -> arbitrary
-      Just path -> return path
+      Just  _ -> return (RelPath p)
 
 instance Arbitrary EntrySelector where
   arbitrary = do
-    x <- arbitrary
+    RelPath x <- arbitrary
     case mkEntrySelector x of
       Nothing -> arbitrary
       Just s  -> return s
@@ -137,8 +144,10 @@
 data EC = EC (Map EntrySelector EntryDescription) (ZipArchive ()) deriving Show
 
 instance Arbitrary EC where
-  arbitrary = foldl' f (EC M.empty (return ())) <$> listOf1 arbitrary
-    where f (EC m z') (EM s desc z) = EC (M.insert s desc m) (z' >> z)
+  arbitrary = do
+    let f (EM s d z) = (s, (d, z))
+    m <- M.fromList . fmap f <$> listOf arbitrary
+    return (EC (M.map fst m) (sequence_ $ snd <$> M.elems m))
 
 charGen :: Gen Char
 charGen = frequency
@@ -169,27 +178,43 @@
 
 mkEntrySelectorSpec :: Spec
 mkEntrySelectorSpec = do
-  context "when incorrect Windows paths are passed" $
-    it "rejects them" . property $ \path ->
-      (not . Windows.isValid . toFilePath $ path)
-        ==> mkEntrySelector path === Nothing
+  let rejects x =
+        mkEntrySelector x `shouldThrow` isEntrySelectorException x
+      accepts x = do
+        s <- mkEntrySelector x
+        getEntryName s `shouldBe` T.pack x
+  context "when absolute paths are passed" $
+    it "they are rejected" $ property $ \(RelPath x) ->
+      rejects ('/' : x)
+  context "when paths with trailing path separator are passed" $
+    it "they are rejected" $ do
+      rejects "foo/"
+      rejects "foo/bar/"
+  context "when paths with dot as path segment are passed" $
+    it "they are rejected" $ do
+      rejects "./foo/bar"
+      rejects "foo/./bar"
+      rejects "foo/bar/."
+  context "when paths with double dot as path segment are passed" $
+    it "they are rejected" $ do
+      rejects "../foo/bar"
+      rejects "foo/../bar"
+      rejects "foo/bar/.."
   context "when too long paths are passed" $
     it "rejects them" $ do
-      path <- parseRelFile (replicate 0x10000 'a')
+      let path = replicate 0x10000 'a'
       mkEntrySelector path `shouldThrow` isEntrySelectorException path
   context "when correct paths are passed" $
     it "adequately represents them" $ do
-      let c str = do
-            s <- parseRelFile str >>= mkEntrySelector
-            getEntryName s `shouldBe` T.pack str
-      c "one/two/three"
-      c "something.txt"
+      accepts "foo"
+      accepts "one/two/three"
+      accepts "something.txt"
 
 unEntrySelectorSpec :: Spec
 unEntrySelectorSpec =
   context "when entry selector exists" $
     it "has corresponding path" . property $ \s ->
-      not . null . toFilePath . unEntrySelector $ s
+      not . null . unEntrySelector $ s
 
 getEntryNameSpec :: Spec
 getEntryNameSpec =
@@ -214,36 +239,36 @@
 ----------------------------------------------------------------------------
 -- Primitive editing/querying actions
 
-createArchiveSpec :: SpecWith (Path Abs File)
+createArchiveSpec :: SpecWith FilePath
 createArchiveSpec = do
   context "when called with non-existent path and empty recipe" $
     it "creates correct representation of empty archive" $ \path -> do
       createArchive path (return ())
-      B.readFile (toFilePath path) `shouldReturn` emptyArchive
-  context "when called with occupied path" $
+      B.readFile path `shouldReturn` emptyArchive
+  context "when called with an occupied path" $
     it "overwrites it" $ \path -> do
-      B.writeFile (toFilePath path) B.empty
+      B.writeFile path B.empty
       createArchive path (return ())
-      B.readFile (toFilePath path) `shouldNotReturn` B.empty
+      B.readFile path `shouldNotReturn` B.empty
 
-withArchiveSpec :: SpecWith (Path Abs File)
+withArchiveSpec :: SpecWith FilePath
 withArchiveSpec = do
   context "when called with non-existent path" $
     it "throws 'isDoesNotExistError' exception" $ \path ->
       withArchive path (return ()) `shouldThrow` isDoesNotExistError
   context "when called with occupied path (empty file)" $
     it "throws 'ParsingFailed' exception" $ \path -> do
-      B.writeFile (toFilePath path) B.empty
+      B.writeFile path B.empty
       withArchive path (return ()) `shouldThrow`
         isParsingFailed path "Cannot locate end of central directory"
   context "when called with occupied path (empty archive)" $
     it "does not overwrite the file unnecessarily" $ \path -> do
-      let fp = toFilePath path
-      B.writeFile fp emptyArchive
-      withArchive path . liftIO $ B.writeFile fp B.empty
-      B.readFile fp `shouldNotReturn` emptyArchive
+      B.writeFile path emptyArchive
+      withArchive path $
+        liftIO $ B.writeFile path B.empty
+      B.readFile path `shouldNotReturn` emptyArchive
 
-archiveCommentSpec :: SpecWith (Path Abs File)
+archiveCommentSpec :: SpecWith FilePath
 archiveCommentSpec = do
   context "when new archive is created" $
     it "returns no archive comment" $ \path ->
@@ -290,14 +315,14 @@
         getArchiveComment
       comment `shouldBe` Nothing
 
-getEntryDescSpec :: SpecWith (Path Abs File)
+getEntryDescSpec :: SpecWith FilePath
 getEntryDescSpec =
   it "always returns correct description" $
     \path -> property $ \(EM s desc z) -> do
       desc' <- fromJust <$> createArchive path (z >> commit >> getEntryDesc s)
       desc' `shouldSatisfy` softEq desc
 
-versionNeededSpec :: SpecWith (Path Abs File)
+versionNeededSpec :: SpecWith FilePath
 versionNeededSpec =
   it "writes correct version that is needed to extract archive" $
     -- NOTE for now we check only how version depends on compression method,
@@ -310,7 +335,7 @@
           Deflate -> [2,0]
           BZip2   -> [4,6])
 
-addEntrySpec :: SpecWith (Path Abs File)
+addEntrySpec :: SpecWith FilePath
 addEntrySpec =
   context "when an entry is added" $
     it "is there" $ \path -> property $ \m b s -> do
@@ -320,7 +345,7 @@
         (,) <$> getEntry s <*> (edCompression . (! s) <$> getEntries)
       info `shouldBe` (b, m)
 
-sinkEntrySpec :: SpecWith (Path Abs File)
+sinkEntrySpec :: SpecWith FilePath
 sinkEntrySpec =
   context "when an entry is sunk" $
     it "is there" $ \path -> property $ \m b s -> do
@@ -331,20 +356,20 @@
           <*> (edCompression . (! s) <$> getEntries)
       info `shouldBe` (b, m)
 
-loadEntrySpec :: SpecWith (Path Abs File)
+loadEntrySpec :: SpecWith FilePath
 loadEntrySpec =
   context "when an entry is loaded" $
     it "is there" $ \path -> property $ \m b s -> do
       let vpath = deriveVacant path
-      B.writeFile (toFilePath vpath) b
+      B.writeFile vpath b
       createArchive path $ do
-        loadEntry m (const $ return s) vpath
+        loadEntry m s vpath
         commit
         liftIO (removeFile vpath)
         saveEntry s vpath
-      B.readFile (toFilePath vpath) `shouldReturn` b
+      B.readFile vpath `shouldReturn` b
 
-copyEntrySpec :: SpecWith (Path Abs File)
+copyEntrySpec :: SpecWith FilePath
 copyEntrySpec =
   context "when entry is copied form another archive" $
     it "is there" $ \path -> property $ \m b s -> do
@@ -356,7 +381,7 @@
         (,) <$> getEntry s <*> (edCompression . (! s) <$> getEntries)
       info `shouldBe` (b, m)
 
-checkEntrySpec :: SpecWith (Path Abs File)
+checkEntrySpec :: SpecWith FilePath
 checkEntrySpec = do
   context "when entry is intact" $
     it "passes the check" $ \path -> property $ \m b s -> do
@@ -373,14 +398,14 @@
           addEntry Store b s
           commit
           fromIntegral . edOffset . (! s) <$> getEntries
-        withFile (toFilePath path) ReadWriteMode $ \h -> do
+        withFile path ReadWriteMode $ \h -> do
           hSeek h AbsoluteSeek (offset + fromIntegral r)
           byte <- B.map complement <$> B.hGet h 1
           hSeek h RelativeSeek (-1)
           B.hPut h byte
         withArchive path (checkEntry s) `shouldReturn` False
 
-recompressSpec :: SpecWith (Path Abs File)
+recompressSpec :: SpecWith FilePath
 recompressSpec =
   context "when recompression is used" $
     it "gets recompressed" $ \path -> property $ \m m' b s -> do
@@ -392,7 +417,7 @@
         (,) <$> getEntry s <*> (edCompression . (! s) <$> getEntries)
       info `shouldBe` (b, m')
 
-entryCommentSpec :: SpecWith (Path Abs File)
+entryCommentSpec :: SpecWith FilePath
 entryCommentSpec = do
   context "when comment is committed (delete/set)" $
     it "reads it and updates" $ \path -> property $ \txt s -> do
@@ -433,7 +458,7 @@
         edComment . (! s) <$> getEntries
       comment `shouldBe` Nothing
 
-setModTimeSpec :: SpecWith (Path Abs File)
+setModTimeSpec :: SpecWith FilePath
 setModTimeSpec = do
   context "when mod time is set (after creation)" $
     it "reads it and updates" $ \path -> property $ \time s -> do
@@ -453,7 +478,7 @@
           edModTime . (! s) <$> getEntries
         modTime `shouldNotSatisfy` isCloseTo time
 
-extraFieldSpec :: SpecWith (Path Abs File)
+extraFieldSpec :: SpecWith FilePath
 extraFieldSpec = do
   context "when extra field is committed (delete/set)" $
     it "reads it and updates" $ \path -> property $ \n b s ->
@@ -498,7 +523,7 @@
           M.lookup n . edExtraField . (! s) <$> getEntries
         efield `shouldBe` Nothing
 
-renameEntrySpec :: SpecWith (Path Abs File)
+renameEntrySpec :: SpecWith FilePath
 renameEntrySpec = do
   context "when renaming after editing of new entry" $
     it "produces correct result" $ \path -> property $ \(EM s desc z) s' -> do
@@ -518,7 +543,7 @@
         (! s') <$> getEntries
       desc' `shouldSatisfy` softEq desc
 
-deleteEntrySpec :: SpecWith (Path Abs File)
+deleteEntrySpec :: SpecWith FilePath
 deleteEntrySpec = do
   context "when deleting after editing of new entry" $
     it "produces correct result" $ \path -> property $ \(EM s _ z) -> do
@@ -538,7 +563,7 @@
         doesEntryExist s
       member `shouldBe` False
 
-forEntriesSpec :: SpecWith (Path Abs File)
+forEntriesSpec :: SpecWith FilePath
 forEntriesSpec =
   it "affects all existing entries" $ \path -> property $ \(EC m z) txt -> do
     m' <- createArchive path $ do
@@ -550,7 +575,7 @@
     let f ed = ed { edComment = Just txt }
     m' `shouldSatisfy` softEqMap (M.map f m)
 
-undoEntryChangesSpec :: SpecWith (Path Abs File)
+undoEntryChangesSpec :: SpecWith FilePath
 undoEntryChangesSpec =
   it "cancels all actions for specified entry" $
     \path -> property $ \(EM s _ z) -> do
@@ -561,7 +586,7 @@
         doesEntryExist s
       member `shouldBe` False
 
-undoArchiveChangesSpec :: SpecWith (Path Abs File)
+undoArchiveChangesSpec :: SpecWith FilePath
 undoArchiveChangesSpec = do
   it "cancels archive comment editing" $ \path -> property $ \txt -> do
     comment <- createArchive path $ do
@@ -580,22 +605,21 @@
       getArchiveComment
     comment `shouldBe` Just txt
 
-undoAllSpec :: SpecWith (Path Abs File)
+undoAllSpec :: SpecWith FilePath
 undoAllSpec =
   it "cancels all editing at once" $ \path -> property $ \(EC _ z) txt -> do
-    let fp = toFilePath path
     createArchive path (return ())
     withArchive path $ do
       z
       setArchiveComment txt
       undoAll
-      liftIO (B.writeFile fp B.empty)
-    B.readFile fp `shouldReturn` B.empty
+      liftIO (B.writeFile path B.empty)
+    B.readFile path `shouldReturn` B.empty
 
 ----------------------------------------------------------------------------
 -- Complex construction/restoration
 
-consistencySpec :: SpecWith (Path Abs File)
+consistencySpec :: SpecWith FilePath
 consistencySpec =
   it "can save and restore arbitrary archive" $
     \path -> property $ \(EC m z) txt -> do
@@ -607,49 +631,35 @@
       txt' `shouldBe` Just txt
       m' `shouldSatisfy` softEqMap m
 
-packDirRecurSpec :: SpecWith (Path Abs File)
+packDirRecurSpec :: SpecWith FilePath
 packDirRecurSpec =
   it "packs arbitrary directory recursively" $
-    \path -> property $ \contents -> do
-      let dir = parent path
-#if MIN_VERSION_path(0,6,0)
-          f   = stripProperPrefix dir >=> mkEntrySelector
-#else
-          f   = stripDir dir >=> mkEntrySelector
-#endif
-
-      blew <- catchIOError (do
+    \path -> property $ \contents ->
+      withSystemTempDirectory "zip-sandbox" $ \dir -> do
         forM_ contents $ \s -> do
           let item = dir </> unEntrySelector s
-          ensureDir (parent item)
-          B.writeFile (toFilePath item) "foo"
-        return False)
-        (const $ return True)
-      when blew discard -- TODO
-      selectors <- M.keysSet <$>
-        createArchive path (packDirRecur Store f dir >> commit >> getEntries)
-      selectors `shouldBe` E.fromList contents
+          createDirectoryIfMissing True (FP.takeDirectory item)
+          B.writeFile item "foo"
+        selectors <-
+          createArchive path $ do
+            packDirRecur Store mkEntrySelector dir
+            commit
+            M.keysSet <$> getEntries
+        selectors `shouldBe` E.fromList contents
 
-unpackIntoSpec :: SpecWith (Path Abs File)
+unpackIntoSpec :: SpecWith FilePath
 unpackIntoSpec =
   it "unpacks archive contents into directory" $
-    \path -> property $ \(EC m z) -> do
-      let dir = parent path
-      blew <- createArchive path $ do
-        z
-        commit
-        catchIOError
-          (unpackInto dir >> return False)
-          (const $ return True)
-      when blew discard -- TODO
-      removeFile path
-      selectors <- listDirRecur dir >>=
-#if MIN_VERSION_path(0,6,0)
-        mapM (stripProperPrefix dir >=> mkEntrySelector) . snd
-#else
-        mapM (stripDir dir >=> mkEntrySelector) . snd
-#endif
-      E.fromList selectors `shouldBe` M.keysSet m
+    \path -> property $ \(EC m z) ->
+      withSystemTempDirectory "zip-sandbox" $ \dir -> do
+        createArchive path $ do
+          z
+          commit
+          unpackInto dir
+        selectors <- listDirRecur dir >>= mapM mkEntrySelector
+        let x = E.fromList selectors
+            y = M.keysSet m
+        E.difference x y `shouldBe` E.empty
 
 ----------------------------------------------------------------------------
 -- Helpers
@@ -657,13 +667,13 @@
 -- | Check whether given exception is 'EntrySelectorException' with specific
 -- path inside.
 
-isEntrySelectorException :: Path Rel File -> EntrySelectorException -> Bool
+isEntrySelectorException :: FilePath -> EntrySelectorException -> Bool
 isEntrySelectorException path (InvalidEntrySelector p) = p == path
 
 -- | Check whether given exception is 'ParsingFailed' exception with
 -- specific path and error message inside.
 
-isParsingFailed :: Path Abs File -> String -> ZipException -> Bool
+isParsingFailed :: FilePath -> String -> ZipException -> Bool
 isParsingFailed path msg (ParsingFailed path' msg') =
   path == path' && msg == msg'
 isParsingFailed _ _ _ = False
@@ -673,15 +683,15 @@
 -- case to avoid contamination and it's unconditionally deleted after test
 -- case finishes. The function returns vacant file path in that directory.
 
-withSandbox :: ActionWith (Path Abs File) -> IO ()
-withSandbox action = withSystemTempDir "zip-sandbox" $ \dir ->
-  action (dir </> $(mkRelFile "foo.zip"))
+withSandbox :: ActionWith FilePath -> IO ()
+withSandbox action = withSystemTempDirectory "zip-sandbox" $ \dir ->
+  action (dir </> "foo.zip")
 
 -- | Given primary name (name of archive), generate a name that does not
 -- collide with it.
 
-deriveVacant :: Path Abs File -> Path Abs File
-deriveVacant = (</> $(mkRelFile "bar")) . parent
+deriveVacant :: FilePath -> FilePath
+deriveVacant = (</> "bar") . FP.takeDirectory
 
 -- | Compare times forgiving minor difference.
 
@@ -714,3 +724,26 @@
 emptyArchive = B.pack
   [ 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
   , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+
+-- | Recursively list a directory. Do not return paths to empty directories.
+
+listDirRecur :: FilePath -> IO [FilePath]
+listDirRecur path = DList.toList <$> go ""
+  where
+    go adir = do
+      let cdir = path </> adir
+      raw <- listDirectory cdir
+      fmap mconcat . forM raw $ \case
+        ""   -> return mempty
+        "."  -> return mempty
+        ".." -> return mempty
+        x    -> do
+          let fullx = cdir </> x
+              adir' = adir </> x
+          isFile <- doesFileExist      fullx
+          isDir  <- doesDirectoryExist fullx
+          if isFile
+            then return (DList.singleton adir')
+            else if isDir
+                   then go adir'
+                   else return mempty
diff --git a/zip.cabal b/zip.cabal
--- a/zip.cabal
+++ b/zip.cabal
@@ -1,7 +1,7 @@
 name:                 zip
-version:              0.2.0
+version:              1.0.0
 cabal-version:        >= 1.18
-tested-with:          GHC==7.10.3, GHC==8.0.2, GHC==8.2.1
+tested-with:          GHC==8.0.2, GHC==8.2.2
 license:              BSD3
 license-file:         LICENSE.md
 author:               Mark Karpov <markkarpov92@gmail.com>
@@ -21,23 +21,22 @@
   default:            False
 
 library
-  build-depends:      base             >= 4.8     && < 5.0
+  build-depends:      base             >= 4.9     && < 5.0
                     , bytestring       >= 0.9     && < 0.11
-                    , bzlib-conduit    >= 0.2     && < 0.3
+                    , bzlib-conduit    >= 0.3     && < 0.4
                     , case-insensitive >= 1.2.0.2 && < 1.3
                     , cereal           >= 0.3     && < 0.6
-                    , conduit          >= 1.2.8   && < 2.0
-                    , conduit-extra    >= 1.1     && < 2.0
+                    , conduit          >= 1.3     && < 1.4
+                    , conduit-extra    >= 1.3     && < 1.4
                     , containers       >= 0.5.6.2 && < 0.6
                     , digest           < 0.1
+                    , directory        >= 1.2.2   && < 1.4
+                    , dlist            >= 0.8     && < 0.9
                     , exceptions       >= 0.6     && < 0.9
                     , filepath         >= 1.2     && < 1.5
                     , monad-control    >= 1.0     && < 1.1
                     , mtl              >= 2.0     && < 3.0
-                    , path             >= 0.5     && < 0.7
-                    , path-io          >= 1.0.1   && < 2.0
-                    , plan-b           >= 0.2     && < 0.3
-                    , resourcet        >= 1.0     && < 2.0
+                    , resourcet        >= 1.2     && < 1.3
                     , text             >= 0.2     && < 1.3
                     , time             >= 1.4     && < 1.9
                     , transformers     >= 0.4     && < 0.6
@@ -48,10 +47,10 @@
                     , TupleSections
   exposed-modules:    Codec.Archive.Zip
                     , Codec.Archive.Zip.CP437
-                    , Codec.Archive.Zip.Type
   other-modules:      Codec.Archive.Zip.Internal
+                    , Codec.Archive.Zip.Type
   if flag(dev)
-    ghc-options:      -Wall -Werror
+    ghc-options:      -O0 -Wall -Werror
   else
     ghc-options:      -O2 -Wall
   default-language:   Haskell2010
@@ -60,24 +59,25 @@
   main-is:            Main.hs
   hs-source-dirs:     tests
   type:               exitcode-stdio-1.0
-  if flag(dev)
-    ghc-options:      -Wall -Werror
-  else
-    ghc-options:      -O2 -Wall
-  build-depends:      base             >= 4.8     && < 5.0
+  build-depends:      base             >= 4.9     && < 5.0
+                    , QuickCheck       >= 2.4     && < 3.0
                     , bytestring       >= 0.9     && < 0.11
-                    , conduit          >= 1.2.8   && < 2.0
+                    , conduit          >= 1.3     && < 1.4
                     , containers       >= 0.5.6.2 && < 0.6
+                    , directory        >= 1.2.2   && < 1.4
+                    , dlist            >= 0.8     && < 0.9
                     , exceptions       >= 0.6     && < 0.9
                     , filepath         >= 1.2     && < 1.5
-                    , QuickCheck       >= 2.4     && < 3.0
                     , hspec            >= 2.0     && < 3.0
-                    , path             >= 0.5     && < 0.7
-                    , path-io          >= 1.0.1   && < 2.0
+                    , temporary        >= 1.1     && < 1.3
                     , text             >= 0.2     && < 1.3
                     , time             >= 1.4     && < 1.9
                     , transformers     >= 0.4     && < 0.6
                     , zip
+  if flag(dev)
+    ghc-options:      -O0 -Wall -Werror
+  else
+    ghc-options:      -O2 -Wall
   default-language:   Haskell2010
 
 source-repository head
