|
Packit |
8cecbd |
-----------------------------------------------------------------------------
|
|
Packit |
8cecbd |
-- |
|
|
Packit |
8cecbd |
-- Module : Codec.Archive.Tar.Write
|
|
Packit |
8cecbd |
-- Copyright : (c) 2007 Bjorn Bringert,
|
|
Packit |
8cecbd |
-- 2008 Andrea Vezzosi,
|
|
Packit |
8cecbd |
-- 2008-2009 Duncan Coutts
|
|
Packit |
8cecbd |
-- License : BSD3
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- Maintainer : duncan@community.haskell.org
|
|
Packit |
8cecbd |
-- Portability : portable
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-----------------------------------------------------------------------------
|
|
Packit |
8cecbd |
module Codec.Archive.Tar.Write (write) where
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
import Codec.Archive.Tar.Types
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
import Data.Char (ord)
|
|
Packit |
8cecbd |
import Data.List (foldl')
|
|
Packit |
8cecbd |
import Data.Monoid (mempty)
|
|
Packit |
8cecbd |
import Numeric (showOct)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
import qualified Data.ByteString as BS
|
|
Packit |
8cecbd |
import qualified Data.ByteString.Char8 as BS.Char8
|
|
Packit |
8cecbd |
import qualified Data.ByteString.Lazy as LBS
|
|
Packit |
8cecbd |
import qualified Data.ByteString.Lazy.Char8 as LBS.Char8
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | Create the external representation of a tar archive by serialising a list
|
|
Packit |
8cecbd |
-- of tar entries.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- * The conversion is done lazily.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
write :: [Entry] -> LBS.ByteString
|
|
Packit |
8cecbd |
write es = LBS.concat $ map putEntry es ++ [LBS.replicate (512*2) 0]
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
putEntry :: Entry -> LBS.ByteString
|
|
Packit |
8cecbd |
putEntry entry = case entryContent entry of
|
|
Packit |
8cecbd |
NormalFile content size -> LBS.concat [ header, content, padding size ]
|
|
Packit |
8cecbd |
OtherEntryType _ content size -> LBS.concat [ header, content, padding size ]
|
|
Packit |
8cecbd |
_ -> header
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
header = putHeader entry
|
|
Packit |
8cecbd |
padding size = LBS.replicate paddingSize 0
|
|
Packit |
8cecbd |
where paddingSize = fromIntegral (negate size `mod` 512)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
putHeader :: Entry -> LBS.ByteString
|
|
Packit |
8cecbd |
putHeader entry =
|
|
Packit |
8cecbd |
LBS.Char8.pack
|
|
Packit |
8cecbd |
$ take 148 block
|
|
Packit |
8cecbd |
++ putOct 7 checksum
|
|
Packit |
8cecbd |
++ ' ' : drop 156 block
|
|
Packit |
8cecbd |
-- ++ putOct 8 checksum
|
|
Packit |
8cecbd |
-- ++ drop 156 block
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
block = putHeaderNoChkSum entry
|
|
Packit |
8cecbd |
checksum = foldl' (\x y -> x + ord y) 0 block
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
putHeaderNoChkSum :: Entry -> String
|
|
Packit |
8cecbd |
putHeaderNoChkSum Entry {
|
|
Packit |
8cecbd |
entryTarPath = TarPath name prefix,
|
|
Packit |
8cecbd |
entryContent = content,
|
|
Packit |
8cecbd |
entryPermissions = permissions,
|
|
Packit |
8cecbd |
entryOwnership = ownership,
|
|
Packit |
8cecbd |
entryTime = modTime,
|
|
Packit |
8cecbd |
entryFormat = format
|
|
Packit |
8cecbd |
} =
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
concat
|
|
Packit |
8cecbd |
[ putBString 100 $ name
|
|
Packit |
8cecbd |
, putOct 8 $ permissions
|
|
Packit |
8cecbd |
, putOct 8 $ ownerId ownership
|
|
Packit |
8cecbd |
, putOct 8 $ groupId ownership
|
|
Packit |
8cecbd |
, putOct 12 $ contentSize
|
|
Packit |
8cecbd |
, putOct 12 $ modTime
|
|
Packit |
8cecbd |
, fill 8 $ ' ' -- dummy checksum
|
|
Packit |
8cecbd |
, putChar8 $ typeCode
|
|
Packit |
8cecbd |
, putBString 100 $ linkTarget
|
|
Packit |
8cecbd |
] ++
|
|
Packit |
8cecbd |
case format of
|
|
Packit |
8cecbd |
V7Format ->
|
|
Packit |
8cecbd |
fill 255 '\NUL'
|
|
Packit |
8cecbd |
UstarFormat -> concat
|
|
Packit |
8cecbd |
[ putBString 8 $ ustarMagic
|
|
Packit |
8cecbd |
, putString 32 $ ownerName ownership
|
|
Packit |
8cecbd |
, putString 32 $ groupName ownership
|
|
Packit |
8cecbd |
, putOct 8 $ deviceMajor
|
|
Packit |
8cecbd |
, putOct 8 $ deviceMinor
|
|
Packit |
8cecbd |
, putBString 155 $ prefix
|
|
Packit |
8cecbd |
, fill 12 $ '\NUL'
|
|
Packit |
8cecbd |
]
|
|
Packit |
8cecbd |
GnuFormat -> concat
|
|
Packit |
8cecbd |
[ putBString 8 $ gnuMagic
|
|
Packit |
8cecbd |
, putString 32 $ ownerName ownership
|
|
Packit |
8cecbd |
, putString 32 $ groupName ownership
|
|
Packit |
8cecbd |
, putGnuDev 8 $ deviceMajor
|
|
Packit |
8cecbd |
, putGnuDev 8 $ deviceMinor
|
|
Packit |
8cecbd |
, putBString 155 $ prefix
|
|
Packit |
8cecbd |
, fill 12 $ '\NUL'
|
|
Packit |
8cecbd |
]
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
(typeCode, contentSize, linkTarget,
|
|
Packit |
8cecbd |
deviceMajor, deviceMinor) = case content of
|
|
Packit |
8cecbd |
NormalFile _ size -> ('0' , size, mempty, 0, 0)
|
|
Packit |
8cecbd |
Directory -> ('5' , 0, mempty, 0, 0)
|
|
Packit |
8cecbd |
SymbolicLink (LinkTarget link) -> ('2' , 0, link, 0, 0)
|
|
Packit |
8cecbd |
HardLink (LinkTarget link) -> ('1' , 0, link, 0, 0)
|
|
Packit |
8cecbd |
CharacterDevice major minor -> ('3' , 0, mempty, major, minor)
|
|
Packit |
8cecbd |
BlockDevice major minor -> ('4' , 0, mempty, major, minor)
|
|
Packit |
8cecbd |
NamedPipe -> ('6' , 0, mempty, 0, 0)
|
|
Packit |
8cecbd |
OtherEntryType code _ size -> (code, size, mempty, 0, 0)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
putGnuDev w n = case content of
|
|
Packit |
8cecbd |
CharacterDevice _ _ -> putOct w n
|
|
Packit |
8cecbd |
BlockDevice _ _ -> putOct w n
|
|
Packit |
8cecbd |
_ -> replicate w '\NUL'
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
ustarMagic, gnuMagic :: BS.ByteString
|
|
Packit |
8cecbd |
ustarMagic = BS.Char8.pack "ustar\NUL00"
|
|
Packit |
8cecbd |
gnuMagic = BS.Char8.pack "ustar \NUL"
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- * TAR format primitive output
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
type FieldWidth = Int
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
putBString :: FieldWidth -> BS.ByteString -> String
|
|
Packit |
8cecbd |
putBString n s = BS.Char8.unpack (BS.take n s) ++ fill (n - BS.length s) '\NUL'
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
putString :: FieldWidth -> String -> String
|
|
Packit |
8cecbd |
putString n s = take n s ++ fill (n - length s) '\NUL'
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
--TODO: check integer widths, eg for large file sizes
|
|
Packit |
8cecbd |
putOct :: (Integral a, Show a) => FieldWidth -> a -> String
|
|
Packit |
8cecbd |
putOct n x =
|
|
Packit |
8cecbd |
let octStr = take (n-1) $ showOct x ""
|
|
Packit |
8cecbd |
in fill (n - length octStr - 1) '0'
|
|
Packit |
8cecbd |
++ octStr
|
|
Packit |
8cecbd |
++ putChar8 '\NUL'
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
putChar8 :: Char -> String
|
|
Packit |
8cecbd |
putChar8 c = [c]
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
fill :: FieldWidth -> Char -> String
|
|
Packit |
8cecbd |
fill n c = replicate n c
|