Skip to content

Commit dfc8fc4

Browse files
committed
Migrate TarPath to PosixPath
1 parent d949d5f commit dfc8fc4

File tree

8 files changed

+87
-35
lines changed

8 files changed

+87
-35
lines changed

Codec/Archive/Tar/Index/Internal.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ type FilePathBS = BS.ByteString
285285

286286
splitTarPath :: TarPath -> [FilePathBS]
287287
splitTarPath (TarPath name prefix) =
288-
splitDirectories prefix ++ splitDirectories name
288+
splitDirectories (posixToByteString prefix) ++ splitDirectories (posixToByteString name)
289289

290290
splitDirectories :: FilePathBS -> [FilePathBS]
291291
splitDirectories bs =

Codec/Archive/Tar/LongNames.hs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
{-# LANGUAGE LambdaCase #-}
2+
{-# LANGUAGE PackageImports #-}
23

34
module Codec.Archive.Tar.LongNames
45
( encodeLongNames
56
, decodeLongNames
67
, DecodeLongNamesError(..)
78
) where
89

10+
import Codec.Archive.Tar.PackAscii
911
import Codec.Archive.Tar.Types
1012
import Control.Exception
1113
import qualified Data.ByteString.Char8 as B
1214
import qualified Data.ByteString.Lazy.Char8 as BL
15+
import "os-string" System.OsString.Posix (PosixString, PosixChar)
16+
import qualified "os-string" System.OsString.Posix as PS
1317

1418
-- | Errors raised by 'decodeLongNames'.
1519
--
@@ -71,10 +75,10 @@ encodeLinkPath
7175
encodeLinkPath lnk = case toTarPath' lnk of
7276
FileNameEmpty -> (Nothing, LinkTarget mempty)
7377
FileNameOK (TarPath name prefix)
74-
| B.null prefix -> (Nothing, LinkTarget name)
75-
| otherwise -> (Just $ longSymLinkEntry lnk, LinkTarget name)
78+
| PS.null prefix -> (Nothing, LinkTarget $ posixToByteString name)
79+
| otherwise -> (Just $ longSymLinkEntry lnk, LinkTarget $ posixToByteString name)
7680
FileNameTooLong (TarPath name _) ->
77-
(Just $ longSymLinkEntry lnk, LinkTarget name)
81+
(Just $ longSymLinkEntry lnk, LinkTarget $ posixToByteString name)
7882

7983
-- | Translate low-level entries (usually freshly deserialized) into
8084
-- high-level entries with POSIX 'FilePath's for files and symlinks

Codec/Archive/Tar/PackAscii.hs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,37 @@
1+
{-# LANGUAGE PackageImports #-}
2+
13
module Codec.Archive.Tar.PackAscii
24
( packAscii
5+
, toPosixString
6+
, fromPosixString
7+
, posixToByteString
8+
, byteToPosixString
39
) where
410

511
import qualified Data.ByteString.Char8 as BS.Char8
12+
import qualified Data.ByteString.Short as Sh
613
import Data.Char
714
import GHC.Stack
15+
import System.IO.Unsafe (unsafePerformIO)
16+
import "os-string" System.OsString.Posix (PosixString)
17+
import qualified "os-string" System.OsString.Posix as PS
18+
import qualified "os-string" System.OsString.Internal.Types as PS
819

920
-- | We should really migrate to @OsPath@ from @filepath@ package,
1021
-- but for now let's not corrupt data silently.
1122
packAscii :: HasCallStack => FilePath -> BS.Char8.ByteString
1223
packAscii xs
1324
| all isAscii xs = BS.Char8.pack xs
1425
| otherwise = error $ "packAscii: only ASCII filenames are supported, but got " ++ xs
26+
27+
toPosixString :: FilePath -> PosixString
28+
toPosixString = unsafePerformIO . PS.encodeFS
29+
30+
fromPosixString :: PosixString -> FilePath
31+
fromPosixString = unsafePerformIO . PS.decodeFS
32+
33+
posixToByteString :: PosixString -> BS.Char8.ByteString
34+
posixToByteString = Sh.fromShort . PS.getPosixString
35+
36+
byteToPosixString :: BS.Char8.ByteString -> PosixString
37+
byteToPosixString = PS.PosixString . Sh.toShort

Codec/Archive/Tar/Read.hs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE BangPatterns #-}
2+
{-# LANGUAGE PackageImports #-}
23
-----------------------------------------------------------------------------
34
-- |
45
-- Module : Codec.Archive.Tar.Read
@@ -17,6 +18,7 @@ module Codec.Archive.Tar.Read
1718
, FormatError(..)
1819
) where
1920

21+
import Codec.Archive.Tar.PackAscii
2022
import Codec.Archive.Tar.Types
2123

2224
import Data.Char (ord)
@@ -33,6 +35,9 @@ import qualified Data.ByteString as BS
3335
import qualified Data.ByteString.Char8 as BS.Char8
3436
import qualified Data.ByteString.Unsafe as BS
3537
import qualified Data.ByteString.Lazy as LBS
38+
import System.IO.Unsafe (unsafePerformIO)
39+
import "os-string" System.OsString.Posix (PosixString, PosixChar)
40+
import qualified "os-string" System.OsString.Posix as PS
3641

3742
import Prelude hiding (read)
3843

@@ -124,7 +129,7 @@ getEntryStreaming getN getAll = do
124129
let content = LBS.take size paddedContent
125130

126131
pure $ Right $ Just $ Entry {
127-
entryTarPath = TarPath name prefix,
132+
entryTarPath = TarPath (byteToPosixString name) (byteToPosixString prefix),
128133
entryContent = case typecode of
129134
'\0' -> NormalFile content size
130135
'0' -> NormalFile content size

Codec/Archive/Tar/Types.hs

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, BangPatterns, DeriveTraversable, ScopedTypeVariables, RankNTypes #-}
1+
{-# LANGUAGE BangPatterns #-}
2+
{-# LANGUAGE DeriveTraversable #-}
3+
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4+
{-# LANGUAGE PackageImports #-}
5+
{-# LANGUAGE QuasiQuotes #-}
6+
{-# LANGUAGE RankNTypes #-}
7+
{-# LANGUAGE ScopedTypeVariables #-}
8+
29
-----------------------------------------------------------------------------
310
-- |
411
-- Module : Codec.Archive.Tar.Types
@@ -88,6 +95,8 @@ import qualified System.FilePath.Windows as FilePath.Windows
8895
( joinPath, addTrailingPathSeparator, pathSeparator )
8996
import System.Posix.Types
9097
( FileMode )
98+
import "os-string" System.OsString.Posix (PosixString, PosixChar)
99+
import qualified "os-string" System.OsString.Posix as PS
91100

92101
import Codec.Archive.Tar.PackAscii
93102

@@ -278,7 +287,7 @@ symlinkEntry name targetLink =
278287
-- @since 0.6.0.0
279288
longLinkEntry :: FilePath -> GenEntry TarPath linkTarget
280289
longLinkEntry tarpath = Entry {
281-
entryTarPath = TarPath (BS.Char8.pack "././@LongLink") BS.empty,
290+
entryTarPath = TarPath [PS.pstr|././@LongLink|] mempty,
282291
entryContent = OtherEntryType 'L' (LBS.fromStrict $ packAscii tarpath) (fromIntegral $ length tarpath),
283292
entryPermissions = ordinaryFilePermissions,
284293
entryOwnership = Ownership "" "" 0 0,
@@ -295,7 +304,7 @@ longLinkEntry tarpath = Entry {
295304
-- @since 0.6.0.0
296305
longSymLinkEntry :: FilePath -> GenEntry TarPath linkTarget
297306
longSymLinkEntry linkTarget = Entry {
298-
entryTarPath = TarPath (BS.Char8.pack "././@LongLink") BS.empty,
307+
entryTarPath = TarPath [PS.pstr|././@LongLink|] mempty,
299308
entryContent = OtherEntryType 'K' (LBS.fromStrict . packAscii $ linkTarget) (fromIntegral $ length linkTarget),
300309
entryPermissions = ordinaryFilePermissions,
301310
entryOwnership = Ownership "" "" 0 0,
@@ -338,8 +347,11 @@ directoryEntry name = simpleEntry name Directory
338347
--
339348
-- * The directory separator between the prefix and name is /not/ stored.
340349
--
341-
data TarPath = TarPath {-# UNPACK #-} !BS.ByteString -- path name, 100 characters max.
342-
{-# UNPACK #-} !BS.ByteString -- path prefix, 155 characters max.
350+
data TarPath = TarPath
351+
{-# UNPACK #-} !PosixString
352+
-- ^ path name, 100 characters max.
353+
{-# UNPACK #-} !PosixString
354+
-- ^ path prefix, 155 characters max.
343355
deriving (Eq, Ord)
344356

345357
instance NFData TarPath where
@@ -362,7 +374,7 @@ instance Show TarPath where
362374
-- (e.g., using 'Codec.Archive.Tar.Check.checkEntrySecurity').
363375
--
364376
fromTarPath :: TarPath -> FilePath
365-
fromTarPath = BS.Char8.unpack . fromTarPathInternal FilePath.Native.pathSeparator
377+
fromTarPath = fromPosixString . fromTarPathInternal (PS.unsafeFromChar FilePath.Native.pathSeparator)
366378

367379
-- | Convert a 'TarPath' to a Unix\/Posix 'FilePath'.
368380
--
@@ -373,7 +385,7 @@ fromTarPath = BS.Char8.unpack . fromTarPathInternal FilePath.Native.pathSeparato
373385
-- operating system, eg to perform portability checks.
374386
--
375387
fromTarPathToPosixPath :: TarPath -> FilePath
376-
fromTarPathToPosixPath = BS.Char8.unpack . fromTarPathInternal FilePath.Posix.pathSeparator
388+
fromTarPathToPosixPath = fromPosixString . fromTarPathInternal (PS.unsafeFromChar FilePath.Posix.pathSeparator)
377389

378390
-- | Convert a 'TarPath' to a Windows 'FilePath'.
379391
--
@@ -384,18 +396,18 @@ fromTarPathToPosixPath = BS.Char8.unpack . fromTarPathInternal FilePath.Posix.pa
384396
-- operating system, eg to perform portability checks.
385397
--
386398
fromTarPathToWindowsPath :: TarPath -> FilePath
387-
fromTarPathToWindowsPath = BS.Char8.unpack . fromTarPathInternal FilePath.Windows.pathSeparator
399+
fromTarPathToWindowsPath = fromPosixString . fromTarPathInternal (PS.unsafeFromChar FilePath.Windows.pathSeparator)
388400

389-
fromTarPathInternal :: Char -> TarPath -> BS.ByteString
401+
fromTarPathInternal :: PosixChar -> TarPath -> PosixString
390402
fromTarPathInternal sep = go
391403
where
392-
posixSep = FilePath.Posix.pathSeparator
404+
posixSep = PS.unsafeFromChar FilePath.Posix.pathSeparator
393405
adjustSeps = if sep == posixSep then id else
394-
BS.Char8.map $ \c -> if c == posixSep then sep else c
406+
PS.map $ \c -> if c == posixSep then sep else c
395407
go (TarPath name prefix)
396-
| BS.null prefix = adjustSeps name
397-
| BS.null name = adjustSeps prefix
398-
| otherwise = adjustSeps prefix <> BS.Char8.cons sep (adjustSeps name)
408+
| PS.null prefix = adjustSeps name
409+
| PS.null name = adjustSeps prefix
410+
| otherwise = adjustSeps prefix <> PS.cons sep (adjustSeps name)
399411
{-# INLINE fromTarPathInternal #-}
400412

401413
-- | Convert a native 'FilePath' to a 'TarPath'.
@@ -453,12 +465,12 @@ splitLongPath :: FilePath -> ToTarPathResult
453465
splitLongPath path = case reverse (FilePath.Posix.splitPath path) of
454466
[] -> FileNameEmpty
455467
c : cs -> case packName nameMax (c :| cs) of
456-
Nothing -> FileNameTooLong $ TarPath (packAscii $ take 100 path) BS.empty
457-
Just (name, []) -> FileNameOK $! TarPath (packAscii name) BS.empty
468+
Nothing -> FileNameTooLong $ TarPath (toPosixString $ take 100 path) mempty
469+
Just (name, []) -> FileNameOK $! TarPath (toPosixString name) mempty
458470
Just (name, first:rest) -> case packName prefixMax remainder of
459-
Nothing -> FileNameTooLong $ TarPath (packAscii $ take 100 path) BS.empty
460-
Just (_ , _:_) -> FileNameTooLong $ TarPath (packAscii $ take 100 path) BS.empty
461-
Just (prefix, []) -> FileNameOK $! TarPath (packAscii name) (packAscii prefix)
471+
Nothing -> FileNameTooLong $ TarPath (toPosixString $ take 100 path) mempty
472+
Just (_ , _:_) -> FileNameTooLong $ TarPath (toPosixString $ take 100 path) mempty
473+
Just (prefix, []) -> FileNameOK $! TarPath (toPosixString name) (toPosixString prefix)
462474
where
463475
-- drop the '/' between the name and prefix:
464476
remainder = init first :| rest

Codec/Archive/Tar/Write.hs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE PackageImports #-}
12
-----------------------------------------------------------------------------
23
-- |
34
-- Module : Codec.Archive.Tar.Write
@@ -12,6 +13,7 @@
1213
-----------------------------------------------------------------------------
1314
module Codec.Archive.Tar.Write (write) where
1415

16+
import Codec.Archive.Tar.PackAscii
1517
import Codec.Archive.Tar.Types
1618

1719
import Data.Bits
@@ -25,7 +27,8 @@ import qualified Data.ByteString as BS
2527
import qualified Data.ByteString.Char8 as BS.Char8
2628
import qualified Data.ByteString.Lazy as LBS
2729
import qualified Data.ByteString.Lazy.Char8 as LBS.Char8
28-
30+
import "os-string" System.OsString.Posix (PosixString)
31+
import qualified "os-string" System.OsString.Posix as PS
2932

3033
-- | Create the external representation of a tar archive by serialising a list
3134
-- of tar entries.
@@ -76,7 +79,7 @@ putHeaderNoChkSum Entry {
7679
} =
7780

7881
concat
79-
[ putBString 100 name
82+
[ putPosixString 100 name
8083
, putOct 8 permissions
8184
, putOct 8 $ ownerId ownership
8285
, putOct 8 $ groupId ownership
@@ -95,7 +98,7 @@ putHeaderNoChkSum Entry {
9598
, putString 32 $ groupName ownership
9699
, putOct 8 deviceMajor
97100
, putOct 8 deviceMinor
98-
, putBString 155 prefix
101+
, putPosixString 155 prefix
99102
, replicate 12 '\NUL'
100103
]
101104
GnuFormat -> concat
@@ -104,7 +107,7 @@ putHeaderNoChkSum Entry {
104107
, putString 32 $ groupName ownership
105108
, putGnuDev 8 deviceMajor
106109
, putGnuDev 8 deviceMinor
107-
, putBString 155 prefix
110+
, putPosixString 155 prefix
108111
, replicate 12 '\NUL'
109112
]
110113
where
@@ -142,6 +145,9 @@ type FieldWidth = Int
142145
putBString :: FieldWidth -> BS.ByteString -> String
143146
putBString n s = BS.Char8.unpack (BS.take n s) ++ replicate (n - BS.length s) '\NUL'
144147

148+
putPosixString :: FieldWidth -> PosixString -> String
149+
putPosixString n s = fromPosixString (PS.take n s) ++ replicate (n - PS.length s) '\NUL'
150+
145151
putString :: FieldWidth -> String -> String
146152
putString n s = take n s ++ replicate (n - length s) '\NUL'
147153

tar.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ library tar-internal
5555
deepseq >= 1.1 && < 1.6,
5656
directory >= 1.3.1 && < 1.4,
5757
filepath < 1.6,
58+
os-string >= 2.0 && < 2.1,
5859
time < 1.13,
5960
transformers < 0.7,
6061

test/Codec/Archive/Tar/Types/Tests.hs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module Codec.Archive.Tar.Types.Tests
1717
, prop_fromTarPathToWindowsPath
1818
) where
1919

20+
import Codec.Archive.Tar.PackAscii
2021
import Codec.Archive.Tar.Types
2122

2223
import qualified Data.ByteString as BS
@@ -49,8 +50,8 @@ fromTarPathRef (TarPath namebs prefixbs) = adjustDirectory $
4950
FilePath.Native.joinPath $ FilePath.Posix.splitDirectories prefix
5051
++ FilePath.Posix.splitDirectories name
5152
where
52-
name = BS.Char8.unpack namebs
53-
prefix = BS.Char8.unpack prefixbs
53+
name = BS.Char8.unpack $ posixToByteString namebs
54+
prefix = BS.Char8.unpack $ posixToByteString prefixbs
5455
adjustDirectory | FilePath.Posix.hasTrailingPathSeparator name
5556
= FilePath.Native.addTrailingPathSeparator
5657
| otherwise = id
@@ -60,8 +61,8 @@ fromTarPathToPosixPathRef (TarPath namebs prefixbs) = adjustDirectory $
6061
FilePath.Posix.joinPath $ FilePath.Posix.splitDirectories prefix
6162
++ FilePath.Posix.splitDirectories name
6263
where
63-
name = BS.Char8.unpack namebs
64-
prefix = BS.Char8.unpack prefixbs
64+
name = BS.Char8.unpack $ posixToByteString namebs
65+
prefix = BS.Char8.unpack $ posixToByteString prefixbs
6566
adjustDirectory | FilePath.Posix.hasTrailingPathSeparator name
6667
= FilePath.Posix.addTrailingPathSeparator
6768
| otherwise = id
@@ -71,8 +72,8 @@ fromTarPathToWindowsPathRef (TarPath namebs prefixbs) = adjustDirectory $
7172
FilePath.Windows.joinPath $ FilePath.Posix.splitDirectories prefix
7273
++ FilePath.Posix.splitDirectories name
7374
where
74-
name = BS.Char8.unpack namebs
75-
prefix = BS.Char8.unpack prefixbs
75+
name = BS.Char8.unpack $ posixToByteString namebs
76+
prefix = BS.Char8.unpack $ posixToByteString prefixbs
7677
adjustDirectory | FilePath.Posix.hasTrailingPathSeparator name
7778
= FilePath.Windows.addTrailingPathSeparator
7879
| otherwise = id
@@ -220,6 +221,6 @@ limitToV7FormatCompat entry@Entry { entryFormat = V7Format } =
220221
},
221222

222223
entryTarPath = let TarPath name _prefix = entryTarPath entry
223-
in TarPath name BS.empty
224+
in TarPath name mempty
224225
}
225226
limitToV7FormatCompat entry = entry

0 commit comments

Comments
 (0)