Skip to content

Commit 4a7be95

Browse files
committed
Fix bug in unPathNative
When passing a path like "F:/foo/bar" on windows (both '/' and '\' are valid path separators on windows) we'll get garbage output like so: > mkPathNative "F:/foo/bar" "F:/foo/bar" > unPathNative "F:/foo/bar" "F:foo\\bar" ...effectively turning an absolute path into a relative path ("F:foo\\bar" on windows is the directory "foo\\bar" relative to the current working directory on drive F). This is because Posix and Windows splitDirectories behave differently: > System.FilePath.Posix.splitDirectories $ "F:/foo/bar" ["F:","foo","bar"] > System.FilePath.Windows.splitDirectories $ "F:/foo/bar" ["F:/","foo","bar"] When joining paths on windows, the filepath library does not assume a trailing path separator after the drive (here "F:"). This is because as described above, "F:foo" is valid relative filepath.
1 parent 67507e2 commit 4a7be95

File tree

2 files changed

+24
-3
lines changed

2 files changed

+24
-3
lines changed

hackage-security/src/Hackage/Security/Util/Path.hs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ module Hackage.Security.Util.Path (
7171
, fromURIPath
7272
, uriPath
7373
, modifyUriPath
74+
-- * Internals
75+
, mkPathNative
76+
, unPathNative
7477
-- * Re-exports
7578
, IOMode(..)
7679
, BufferMode(..)
@@ -118,10 +121,17 @@ newtype Path a = Path FilePath -- always a Posix style path internally
118121
deriving (Show, Eq, Ord)
119122

120123
mkPathNative :: FilePath -> Path a
121-
mkPathNative = Path . FP.Posix.joinPath . FP.Native.splitDirectories
124+
mkPathNative = Path . canonicalizePathSeparator
122125

123126
unPathNative :: Path a -> FilePath
124-
unPathNative (Path fp) = FP.Native.joinPath . FP.Posix.splitDirectories $ fp
127+
unPathNative (Path fp) = fp
128+
129+
canonicalizePathSeparator :: FilePath -> FilePath
130+
canonicalizePathSeparator = map (replaceSeparator)
131+
where
132+
replaceSeparator c
133+
| FP.Native.isPathSeparator c = '/'
134+
| otherwise = c
125135

126136
mkPathPosix :: FilePath -> Path a
127137
mkPathPosix = Path

hackage-security/tests/TestSuite.hs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import Data.Time ( UTCTime, getCurrentTime )
99
import Network.URI ( URI, parseURI )
1010
import Test.Tasty ( defaultMain, testGroup, TestTree )
1111
import Test.Tasty.HUnit ( testCase, (@?=), assertEqual, assertFailure, Assertion )
12-
import Test.Tasty.QuickCheck ( testProperty )
12+
import Test.Tasty.QuickCheck ( testProperty, Property, (===), property )
1313
import System.IO.Temp (withSystemTempDirectory)
1414
import qualified Codec.Archive.Tar.Entry as Tar
1515
import qualified Data.ByteString.Lazy.Char8 as BS
@@ -72,6 +72,9 @@ tests = testGroup "hackage-security" [
7272
, testProperty "prop_canonical_pretty" JSON.prop_canonical_pretty
7373
, testProperty "prop_aeson_canonical" JSON.prop_aeson_canonical
7474
]
75+
, testGroup "Path" [
76+
testProperty "Hackage.Security.Util.Path.mkPathNative" prop_mkPathNative
77+
]
7578
]
7679

7780
{-------------------------------------------------------------------------------
@@ -547,3 +550,11 @@ checkExpiry = Just `fmap` getCurrentTime
547550
mkPackageName :: String -> PackageName
548551
mkPackageName = PackageName
549552
#endif
553+
554+
{-------------------------------------------------------------------------------
555+
Path tests
556+
-------------------------------------------------------------------------------}
557+
558+
prop_mkPathNative :: Property
559+
prop_mkPathNative = property $ \(fp :: FilePath) -> (mkPathNative . unPathNative . mkPathNative) fp === mkPathNative fp
560+

0 commit comments

Comments
 (0)