|
| 1 | +{-# LANGUAGE LambdaCase #-} |
| 2 | + |
| 3 | +module Codec.Archive.Tar.LongNames |
| 4 | + ( encodeLongNames |
| 5 | + , decodeLongNames |
| 6 | + , DecodeLongNamesError |
| 7 | + ) where |
| 8 | + |
| 9 | +import Codec.Archive.Tar.Types |
| 10 | +import Control.Exception |
| 11 | +import qualified Data.ByteString.Char8 as B |
| 12 | +import qualified Data.ByteString.Lazy.Char8 as BL |
| 13 | + |
| 14 | +data DecodeLongNamesError |
| 15 | + = TwoTypeKEntries |
| 16 | + | TwoTypeLEntries |
| 17 | + | NoLinkEntryAfterTypeKEntry |
| 18 | + deriving (Eq, Ord, Show) |
| 19 | + |
| 20 | +instance Exception DecodeLongNamesError |
| 21 | + |
| 22 | +encodeLongNames |
| 23 | + :: GenEntry FilePath FilePath |
| 24 | + -> [Entry] |
| 25 | +encodeLongNames e = maybe id (:) mEntry $ maybe id (:) mEntry' [e''] |
| 26 | + where |
| 27 | + (mEntry, e') = encodeLinkTarget e |
| 28 | + (mEntry', e'') = encodeTarPath e' |
| 29 | + |
| 30 | +encodeTarPath |
| 31 | + :: GenEntry FilePath linkTarget |
| 32 | + -> (Maybe (GenEntry TarPath whatever), GenEntry TarPath linkTarget) |
| 33 | + -- ^ (LongLink entry, actual entry) |
| 34 | +encodeTarPath e = case toTarPath' (entryTarPath e) of |
| 35 | + FileNameEmpty -> (Nothing, e { entryTarPath = TarPath mempty mempty }) |
| 36 | + FileNameOK tarPath -> (Nothing, e { entryTarPath = tarPath }) |
| 37 | + FileNameTooLong tarPath -> (Just $ longLinkEntry $ entryTarPath e, e { entryTarPath = tarPath }) |
| 38 | + |
| 39 | +encodeLinkTarget |
| 40 | + :: GenEntry tarPath FilePath |
| 41 | + -> (Maybe (GenEntry TarPath LinkTarget), GenEntry tarPath LinkTarget) |
| 42 | + -- ^ (LongLink symlink entry, actual entry) |
| 43 | +encodeLinkTarget e = case entryContent e of |
| 44 | + NormalFile x y -> (Nothing, e { entryContent = NormalFile x y }) |
| 45 | + Directory -> (Nothing, e { entryContent = Directory }) |
| 46 | + SymbolicLink lnk -> let (mEntry, lnk') = encodeLinkPath lnk in |
| 47 | + (mEntry, e { entryContent = SymbolicLink lnk' }) |
| 48 | + HardLink lnk -> let (mEntry, lnk') = encodeLinkPath lnk in |
| 49 | + (mEntry, e { entryContent = HardLink lnk' }) |
| 50 | + CharacterDevice x y -> (Nothing, e { entryContent = CharacterDevice x y }) |
| 51 | + BlockDevice x y -> (Nothing, e { entryContent = BlockDevice x y }) |
| 52 | + NamedPipe -> (Nothing, e { entryContent = NamedPipe }) |
| 53 | + OtherEntryType x y z -> (Nothing, e { entryContent = OtherEntryType x y z }) |
| 54 | + |
| 55 | +encodeLinkPath |
| 56 | + :: FilePath |
| 57 | + -> (Maybe (GenEntry TarPath LinkTarget), LinkTarget) |
| 58 | +encodeLinkPath lnk = case toTarPath' lnk of |
| 59 | + FileNameEmpty -> (Nothing, LinkTarget mempty) |
| 60 | + FileNameOK (TarPath name prefix) |
| 61 | + | B.null prefix -> (Nothing, LinkTarget name) |
| 62 | + | otherwise -> (Just $ longSymLinkEntry lnk, LinkTarget name) |
| 63 | + FileNameTooLong (TarPath name _) -> |
| 64 | + (Just $ longSymLinkEntry lnk, LinkTarget name) |
| 65 | + |
| 66 | +-- | Resolved 'FilePath's are still POSIX file names, not native ones. |
| 67 | +decodeLongNames |
| 68 | + :: Entries e |
| 69 | + -> GenEntries FilePath FilePath (Either e DecodeLongNamesError) |
| 70 | +decodeLongNames = go Nothing Nothing |
| 71 | + where |
| 72 | + go :: Maybe FilePath -> Maybe FilePath -> Entries e -> GenEntries FilePath FilePath (Either e DecodeLongNamesError) |
| 73 | + go _ _ (Fail err) = Fail (Left err) |
| 74 | + go _ _ Done = Done |
| 75 | + |
| 76 | + go Nothing Nothing (Next e rest) = case entryContent e of |
| 77 | + OtherEntryType 'K' fn _ -> |
| 78 | + go (Just (otherEntryPayloadToFilePath fn)) Nothing rest |
| 79 | + OtherEntryType 'L' fn _ -> |
| 80 | + go Nothing (Just (otherEntryPayloadToFilePath fn)) rest |
| 81 | + _ -> |
| 82 | + Next (castEntry e) (go Nothing Nothing rest) |
| 83 | + |
| 84 | + go Nothing (Just path) (Next e rest) = case entryContent e of |
| 85 | + OtherEntryType 'K' fn _ -> |
| 86 | + go (Just (otherEntryPayloadToFilePath fn)) (Just path) rest |
| 87 | + OtherEntryType 'L' _ _ -> |
| 88 | + Fail $ Right TwoTypeLEntries |
| 89 | + _ -> Next ((castEntry e) { entryTarPath = path }) (go Nothing Nothing rest) |
| 90 | + |
| 91 | + go (Just link) Nothing (Next e rest) = case entryContent e of |
| 92 | + OtherEntryType 'K' _ _ -> |
| 93 | + Fail $ Right TwoTypeKEntries |
| 94 | + OtherEntryType 'L' fn _ -> |
| 95 | + go (Just link) (Just (otherEntryPayloadToFilePath fn)) rest |
| 96 | + SymbolicLink{} -> |
| 97 | + Next ((castEntry e) { entryContent = SymbolicLink link }) (go Nothing Nothing rest) |
| 98 | + HardLink{} -> |
| 99 | + Next ((castEntry e) { entryContent = HardLink link }) (go Nothing Nothing rest) |
| 100 | + _ -> |
| 101 | + Fail $ Right NoLinkEntryAfterTypeKEntry |
| 102 | + |
| 103 | + go (Just link) (Just path) (Next e rest) = case entryContent e of |
| 104 | + OtherEntryType 'K' _ _ -> |
| 105 | + Fail $ Right TwoTypeKEntries |
| 106 | + OtherEntryType 'L' _ _ -> |
| 107 | + Fail $ Right TwoTypeLEntries |
| 108 | + SymbolicLink{} -> |
| 109 | + Next ((castEntry e) { entryTarPath = path, entryContent = SymbolicLink link }) (go Nothing Nothing rest) |
| 110 | + HardLink{} -> |
| 111 | + Next ((castEntry e) { entryTarPath = path, entryContent = HardLink link }) (go Nothing Nothing rest) |
| 112 | + _ -> |
| 113 | + Fail $ Right NoLinkEntryAfterTypeKEntry |
| 114 | + |
| 115 | +otherEntryPayloadToFilePath :: BL.ByteString -> FilePath |
| 116 | +otherEntryPayloadToFilePath = B.unpack . B.takeWhile (/= '\0') . BL.toStrict |
| 117 | + |
| 118 | +castEntry :: Entry -> GenEntry FilePath FilePath |
| 119 | +castEntry e = e |
| 120 | + { entryTarPath = fromTarPathToPosixPath (entryTarPath e) |
| 121 | + , entryContent = castEntryContent (entryContent e) |
| 122 | + } |
| 123 | + |
| 124 | +castEntryContent :: EntryContent -> GenEntryContent FilePath |
| 125 | +castEntryContent = \case |
| 126 | + NormalFile x y -> NormalFile x y |
| 127 | + Directory -> Directory |
| 128 | + SymbolicLink linkTarget -> SymbolicLink $ fromLinkTargetToPosixPath linkTarget |
| 129 | + HardLink linkTarget -> HardLink $ fromLinkTargetToPosixPath linkTarget |
| 130 | + CharacterDevice x y -> CharacterDevice x y |
| 131 | + BlockDevice x y -> BlockDevice x y |
| 132 | + NamedPipe -> NamedPipe |
| 133 | + OtherEntryType x y z -> OtherEntryType x y z |
0 commit comments