|
Packit |
8cecbd |
{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, BangPatterns #-}
|
|
Packit |
8cecbd |
-----------------------------------------------------------------------------
|
|
Packit |
8cecbd |
-- |
|
|
Packit |
8cecbd |
-- Module : Codec.Archive.Tar.Types
|
|
Packit |
8cecbd |
-- Copyright : (c) 2007 Bjorn Bringert,
|
|
Packit |
8cecbd |
-- 2008 Andrea Vezzosi,
|
|
Packit |
8cecbd |
-- 2008-2009 Duncan Coutts
|
|
Packit |
8cecbd |
-- 2011 Max Bolingbroke
|
|
Packit |
8cecbd |
-- License : BSD3
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- Maintainer : duncan@community.haskell.org
|
|
Packit |
8cecbd |
-- Portability : portable
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- Types to represent the content of @.tar@ archives.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-----------------------------------------------------------------------------
|
|
Packit |
8cecbd |
module Codec.Archive.Tar.Types (
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
Entry(..),
|
|
Packit |
8cecbd |
entryPath,
|
|
Packit |
8cecbd |
EntryContent(..),
|
|
Packit |
8cecbd |
FileSize,
|
|
Packit |
8cecbd |
Permissions,
|
|
Packit |
8cecbd |
Ownership(..),
|
|
Packit |
8cecbd |
EpochTime,
|
|
Packit |
8cecbd |
TypeCode,
|
|
Packit |
8cecbd |
DevMajor,
|
|
Packit |
8cecbd |
DevMinor,
|
|
Packit |
8cecbd |
Format(..),
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
simpleEntry,
|
|
Packit |
8cecbd |
fileEntry,
|
|
Packit |
8cecbd |
directoryEntry,
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
ordinaryFilePermissions,
|
|
Packit |
8cecbd |
executableFilePermissions,
|
|
Packit |
8cecbd |
directoryPermissions,
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
TarPath(..),
|
|
Packit |
8cecbd |
toTarPath,
|
|
Packit |
8cecbd |
fromTarPath,
|
|
Packit |
8cecbd |
fromTarPathToPosixPath,
|
|
Packit |
8cecbd |
fromTarPathToWindowsPath,
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
LinkTarget(..),
|
|
Packit |
8cecbd |
toLinkTarget,
|
|
Packit |
8cecbd |
fromLinkTarget,
|
|
Packit |
8cecbd |
fromLinkTargetToPosixPath,
|
|
Packit |
8cecbd |
fromLinkTargetToWindowsPath,
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
Entries(..),
|
|
Packit |
8cecbd |
mapEntries,
|
|
Packit |
8cecbd |
mapEntriesNoFail,
|
|
Packit |
8cecbd |
foldEntries,
|
|
Packit |
8cecbd |
foldlEntries,
|
|
Packit |
8cecbd |
unfoldEntries,
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
#ifdef TESTS
|
|
Packit |
8cecbd |
limitToV7FormatCompat
|
|
Packit |
8cecbd |
#endif
|
|
Packit |
8cecbd |
) where
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
import Data.Int (Int64)
|
|
Packit |
8cecbd |
import Data.Monoid (Monoid(..))
|
|
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 Control.DeepSeq
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
import qualified System.FilePath as FilePath.Native
|
|
Packit |
8cecbd |
( joinPath, splitDirectories, addTrailingPathSeparator )
|
|
Packit |
8cecbd |
import qualified System.FilePath.Posix as FilePath.Posix
|
|
Packit |
8cecbd |
( joinPath, splitPath, splitDirectories, hasTrailingPathSeparator
|
|
Packit |
8cecbd |
, addTrailingPathSeparator )
|
|
Packit |
8cecbd |
import qualified System.FilePath.Windows as FilePath.Windows
|
|
Packit |
8cecbd |
( joinPath, addTrailingPathSeparator )
|
|
Packit |
8cecbd |
import System.Posix.Types
|
|
Packit |
8cecbd |
( FileMode )
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
#ifdef TESTS
|
|
Packit |
8cecbd |
import Test.QuickCheck
|
|
Packit |
8cecbd |
import Control.Applicative ((<$>), pure, (<*>))
|
|
Packit |
8cecbd |
#endif
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
type FileSize = Int64
|
|
Packit |
8cecbd |
-- | The number of seconds since the UNIX epoch
|
|
Packit |
8cecbd |
type EpochTime = Int64
|
|
Packit |
8cecbd |
type DevMajor = Int
|
|
Packit |
8cecbd |
type DevMinor = Int
|
|
Packit |
8cecbd |
type TypeCode = Char
|
|
Packit |
8cecbd |
type Permissions = FileMode
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | Tar archive entry.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
data Entry = Entry {
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | The path of the file or directory within the archive. This is in a
|
|
Packit |
8cecbd |
-- tar-specific form. Use 'entryPath' to get a native 'FilePath'.
|
|
Packit |
8cecbd |
entryTarPath :: {-# UNPACK #-} !TarPath,
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | The real content of the entry. For 'NormalFile' this includes the
|
|
Packit |
8cecbd |
-- file data. An entry usually contains a 'NormalFile' or a 'Directory'.
|
|
Packit |
8cecbd |
entryContent :: !EntryContent,
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | File permissions (Unix style file mode).
|
|
Packit |
8cecbd |
entryPermissions :: {-# UNPACK #-} !Permissions,
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | The user and group to which this file belongs.
|
|
Packit |
8cecbd |
entryOwnership :: {-# UNPACK #-} !Ownership,
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | The time the file was last modified.
|
|
Packit |
8cecbd |
entryTime :: {-# UNPACK #-} !EpochTime,
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | The tar format the archive is using.
|
|
Packit |
8cecbd |
entryFormat :: !Format
|
|
Packit |
8cecbd |
}
|
|
Packit |
8cecbd |
deriving (Eq, Show)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | Native 'FilePath' of the file or directory within the archive.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
entryPath :: Entry -> FilePath
|
|
Packit |
8cecbd |
entryPath = fromTarPath . entryTarPath
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | The content of a tar archive entry, which depends on the type of entry.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- Portable archives should contain only 'NormalFile' and 'Directory'.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
data EntryContent = NormalFile LBS.ByteString {-# UNPACK #-} !FileSize
|
|
Packit |
8cecbd |
| Directory
|
|
Packit |
8cecbd |
| SymbolicLink !LinkTarget
|
|
Packit |
8cecbd |
| HardLink !LinkTarget
|
|
Packit |
8cecbd |
| CharacterDevice {-# UNPACK #-} !DevMajor
|
|
Packit |
8cecbd |
{-# UNPACK #-} !DevMinor
|
|
Packit |
8cecbd |
| BlockDevice {-# UNPACK #-} !DevMajor
|
|
Packit |
8cecbd |
{-# UNPACK #-} !DevMinor
|
|
Packit |
8cecbd |
| NamedPipe
|
|
Packit |
8cecbd |
| OtherEntryType {-# UNPACK #-} !TypeCode LBS.ByteString
|
|
Packit |
8cecbd |
{-# UNPACK #-} !FileSize
|
|
Packit |
8cecbd |
deriving (Eq, Ord, Show)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
data Ownership = Ownership {
|
|
Packit |
8cecbd |
-- | The owner user name. Should be set to @\"\"@ if unknown.
|
|
Packit |
8cecbd |
ownerName :: String,
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | The owner group name. Should be set to @\"\"@ if unknown.
|
|
Packit |
8cecbd |
groupName :: String,
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | Numeric owner user id. Should be set to @0@ if unknown.
|
|
Packit |
8cecbd |
ownerId :: {-# UNPACK #-} !Int,
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | Numeric owner group id. Should be set to @0@ if unknown.
|
|
Packit |
8cecbd |
groupId :: {-# UNPACK #-} !Int
|
|
Packit |
8cecbd |
}
|
|
Packit |
8cecbd |
deriving (Eq, Ord, Show)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | There have been a number of extensions to the tar file format over the
|
|
Packit |
8cecbd |
-- years. They all share the basic entry fields and put more meta-data in
|
|
Packit |
8cecbd |
-- different extended headers.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
data Format =
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | This is the classic Unix V7 tar format. It does not support owner and
|
|
Packit |
8cecbd |
-- group names, just numeric Ids. It also does not support device numbers.
|
|
Packit |
8cecbd |
V7Format
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | The \"USTAR\" format is an extension of the classic V7 format. It was
|
|
Packit |
8cecbd |
-- later standardised by POSIX. It has some restrictions but is the most
|
|
Packit |
8cecbd |
-- portable format.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
| UstarFormat
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | The GNU tar implementation also extends the classic V7 format, though
|
|
Packit |
8cecbd |
-- in a slightly different way from the USTAR format. In general for new
|
|
Packit |
8cecbd |
-- archives the standard USTAR/POSIX should be used.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
| GnuFormat
|
|
Packit |
8cecbd |
deriving (Eq, Ord, Show)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance NFData Entry where
|
|
Packit |
8cecbd |
rnf (Entry _ c _ _ _ _) = rnf c
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance NFData EntryContent where
|
|
Packit |
8cecbd |
rnf x = case x of
|
|
Packit |
8cecbd |
NormalFile c _ -> rnflbs c
|
|
Packit |
8cecbd |
OtherEntryType _ c _ -> rnflbs c
|
|
Packit |
8cecbd |
_ -> seq x ()
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
#if MIN_VERSION_bytestring(0,10,0)
|
|
Packit |
8cecbd |
rnflbs = rnf
|
|
Packit |
8cecbd |
#else
|
|
Packit |
8cecbd |
rnflbs = foldr (\ !_bs r -> r) () . LBS.toChunks
|
|
Packit |
8cecbd |
#endif
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance NFData Ownership where
|
|
Packit |
8cecbd |
rnf (Ownership o g _ _) = rnf o `seq` rnf g
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | @rw-r--r--@ for normal files
|
|
Packit |
8cecbd |
ordinaryFilePermissions :: Permissions
|
|
Packit |
8cecbd |
ordinaryFilePermissions = 0o0644
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | @rwxr-xr-x@ for executable files
|
|
Packit |
8cecbd |
executableFilePermissions :: Permissions
|
|
Packit |
8cecbd |
executableFilePermissions = 0o0755
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | @rwxr-xr-x@ for directories
|
|
Packit |
8cecbd |
directoryPermissions :: Permissions
|
|
Packit |
8cecbd |
directoryPermissions = 0o0755
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | An 'Entry' with all default values except for the file name and type. It
|
|
Packit |
8cecbd |
-- uses the portable USTAR/POSIX format (see 'UstarHeader').
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- You can use this as a basis and override specific fields, eg:
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- > (emptyEntry name HardLink) { linkTarget = target }
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
simpleEntry :: TarPath -> EntryContent -> Entry
|
|
Packit |
8cecbd |
simpleEntry tarpath content = Entry {
|
|
Packit |
8cecbd |
entryTarPath = tarpath,
|
|
Packit |
8cecbd |
entryContent = content,
|
|
Packit |
8cecbd |
entryPermissions = case content of
|
|
Packit |
8cecbd |
Directory -> directoryPermissions
|
|
Packit |
8cecbd |
_ -> ordinaryFilePermissions,
|
|
Packit |
8cecbd |
entryOwnership = Ownership "" "" 0 0,
|
|
Packit |
8cecbd |
entryTime = 0,
|
|
Packit |
8cecbd |
entryFormat = UstarFormat
|
|
Packit |
8cecbd |
}
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | A tar 'Entry' for a file.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- Entry fields such as file permissions and ownership have default values.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- You can use this as a basis and override specific fields. For example if you
|
|
Packit |
8cecbd |
-- need an executable file you could use:
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- > (fileEntry name content) { fileMode = executableFileMode }
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
fileEntry :: TarPath -> LBS.ByteString -> Entry
|
|
Packit |
8cecbd |
fileEntry name fileContent =
|
|
Packit |
8cecbd |
simpleEntry name (NormalFile fileContent (LBS.length fileContent))
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | A tar 'Entry' for a directory.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- Entry fields such as file permissions and ownership have default values.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
directoryEntry :: TarPath -> Entry
|
|
Packit |
8cecbd |
directoryEntry name = simpleEntry name Directory
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- * Tar paths
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | The classic tar format allowed just 100 characters for the file name. The
|
|
Packit |
8cecbd |
-- USTAR format extended this with an extra 155 characters, however it uses a
|
|
Packit |
8cecbd |
-- complex method of splitting the name between the two sections.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- Instead of just putting any overflow into the extended area, it uses the
|
|
Packit |
8cecbd |
-- extended area as a prefix. The aggravating insane bit however is that the
|
|
Packit |
8cecbd |
-- prefix (if any) must only contain a directory prefix. That is the split
|
|
Packit |
8cecbd |
-- between the two areas must be on a directory separator boundary. So there is
|
|
Packit |
8cecbd |
-- no simple calculation to work out if a file name is too long. Instead we
|
|
Packit |
8cecbd |
-- have to try to find a valid split that makes the name fit in the two areas.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- The rationale presumably was to make it a bit more compatible with old tar
|
|
Packit |
8cecbd |
-- programs that only understand the classic format. A classic tar would be
|
|
Packit |
8cecbd |
-- able to extract the file name and possibly some dir prefix, but not the
|
|
Packit |
8cecbd |
-- full dir prefix. So the files would end up in the wrong place, but that's
|
|
Packit |
8cecbd |
-- probably better than ending up with the wrong names too.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- So it's understandable but rather annoying.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- * Tar paths use Posix format (ie @\'/\'@ directory separators), irrespective
|
|
Packit |
8cecbd |
-- of the local path conventions.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- * The directory separator between the prefix and name is /not/ stored.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
data TarPath = TarPath {-# UNPACK #-} !BS.ByteString -- path name, 100 characters max.
|
|
Packit |
8cecbd |
{-# UNPACK #-} !BS.ByteString -- path prefix, 155 characters max.
|
|
Packit |
8cecbd |
deriving (Eq, Ord)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance NFData TarPath where
|
|
Packit |
8cecbd |
rnf (TarPath _ _) = () -- fully strict by construction
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance Show TarPath where
|
|
Packit |
8cecbd |
show = show . fromTarPath
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | Convert a 'TarPath' to a native 'FilePath'.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- The native 'FilePath' will use the native directory separator but it is not
|
|
Packit |
8cecbd |
-- otherwise checked for validity or sanity. In particular:
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- * The tar path may be invalid as a native path, eg the file name @\"nul\"@
|
|
Packit |
8cecbd |
-- is not valid on Windows.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- * The tar path may be an absolute path or may contain @\"..\"@ components.
|
|
Packit |
8cecbd |
-- For security reasons this should not usually be allowed, but it is your
|
|
Packit |
8cecbd |
-- responsibility to check for these conditions (eg using 'checkSecurity').
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
fromTarPath :: TarPath -> FilePath
|
|
Packit |
8cecbd |
fromTarPath (TarPath namebs prefixbs) = adjustDirectory $
|
|
Packit |
8cecbd |
FilePath.Native.joinPath $ FilePath.Posix.splitDirectories prefix
|
|
Packit |
8cecbd |
++ FilePath.Posix.splitDirectories name
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
name = BS.Char8.unpack namebs
|
|
Packit |
8cecbd |
prefix = BS.Char8.unpack prefixbs
|
|
Packit |
8cecbd |
adjustDirectory | FilePath.Posix.hasTrailingPathSeparator name
|
|
Packit |
8cecbd |
= FilePath.Native.addTrailingPathSeparator
|
|
Packit |
8cecbd |
| otherwise = id
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | Convert a 'TarPath' to a Unix\/Posix 'FilePath'.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- The difference compared to 'fromTarPath' is that it always returns a Unix
|
|
Packit |
8cecbd |
-- style path irrespective of the current operating system.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- This is useful to check how a 'TarPath' would be interpreted on a specific
|
|
Packit |
8cecbd |
-- operating system, eg to perform portability checks.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
fromTarPathToPosixPath :: TarPath -> FilePath
|
|
Packit |
8cecbd |
fromTarPathToPosixPath (TarPath namebs prefixbs) = adjustDirectory $
|
|
Packit |
8cecbd |
FilePath.Posix.joinPath $ FilePath.Posix.splitDirectories prefix
|
|
Packit |
8cecbd |
++ FilePath.Posix.splitDirectories name
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
name = BS.Char8.unpack namebs
|
|
Packit |
8cecbd |
prefix = BS.Char8.unpack prefixbs
|
|
Packit |
8cecbd |
adjustDirectory | FilePath.Posix.hasTrailingPathSeparator name
|
|
Packit |
8cecbd |
= FilePath.Posix.addTrailingPathSeparator
|
|
Packit |
8cecbd |
| otherwise = id
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | Convert a 'TarPath' to a Windows 'FilePath'.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- The only difference compared to 'fromTarPath' is that it always returns a
|
|
Packit |
8cecbd |
-- Windows style path irrespective of the current operating system.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- This is useful to check how a 'TarPath' would be interpreted on a specific
|
|
Packit |
8cecbd |
-- operating system, eg to perform portability checks.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
fromTarPathToWindowsPath :: TarPath -> FilePath
|
|
Packit |
8cecbd |
fromTarPathToWindowsPath (TarPath namebs prefixbs) = adjustDirectory $
|
|
Packit |
8cecbd |
FilePath.Windows.joinPath $ FilePath.Posix.splitDirectories prefix
|
|
Packit |
8cecbd |
++ FilePath.Posix.splitDirectories name
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
name = BS.Char8.unpack namebs
|
|
Packit |
8cecbd |
prefix = BS.Char8.unpack prefixbs
|
|
Packit |
8cecbd |
adjustDirectory | FilePath.Posix.hasTrailingPathSeparator name
|
|
Packit |
8cecbd |
= FilePath.Windows.addTrailingPathSeparator
|
|
Packit |
8cecbd |
| otherwise = id
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | Convert a native 'FilePath' to a 'TarPath'.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- The conversion may fail if the 'FilePath' is too long. See 'TarPath' for a
|
|
Packit |
8cecbd |
-- description of the problem with splitting long 'FilePath's.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
toTarPath :: Bool -- ^ Is the path for a directory? This is needed because for
|
|
Packit |
8cecbd |
-- directories a 'TarPath' must always use a trailing @\/@.
|
|
Packit |
8cecbd |
-> FilePath -> Either String TarPath
|
|
Packit |
8cecbd |
toTarPath isDir = splitLongPath
|
|
Packit |
8cecbd |
. addTrailingSep
|
|
Packit |
8cecbd |
. FilePath.Posix.joinPath
|
|
Packit |
8cecbd |
. FilePath.Native.splitDirectories
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
addTrailingSep | isDir = FilePath.Posix.addTrailingPathSeparator
|
|
Packit |
8cecbd |
| otherwise = id
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | Take a sanitised path, split on directory separators and try to pack it
|
|
Packit |
8cecbd |
-- into the 155 + 100 tar file name format.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- The strategy is this: take the name-directory components in reverse order
|
|
Packit |
8cecbd |
-- and try to fit as many components into the 100 long name area as possible.
|
|
Packit |
8cecbd |
-- If all the remaining components fit in the 155 name area then we win.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
splitLongPath :: FilePath -> Either String TarPath
|
|
Packit |
8cecbd |
splitLongPath path =
|
|
Packit |
8cecbd |
case packName nameMax (reverse (FilePath.Posix.splitPath path)) of
|
|
Packit |
8cecbd |
Left err -> Left err
|
|
Packit |
8cecbd |
Right (name, []) -> Right $! TarPath (BS.Char8.pack name)
|
|
Packit |
8cecbd |
BS.empty
|
|
Packit |
8cecbd |
Right (name, first:rest) -> case packName prefixMax remainder of
|
|
Packit |
8cecbd |
Left err -> Left err
|
|
Packit |
8cecbd |
Right (_ , (_:_)) -> Left "File name too long (cannot split)"
|
|
Packit |
8cecbd |
Right (prefix, []) -> Right $! TarPath (BS.Char8.pack name)
|
|
Packit |
8cecbd |
(BS.Char8.pack prefix)
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
-- drop the '/' between the name and prefix:
|
|
Packit |
8cecbd |
remainder = init first : rest
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
nameMax, prefixMax :: Int
|
|
Packit |
8cecbd |
nameMax = 100
|
|
Packit |
8cecbd |
prefixMax = 155
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
packName _ [] = Left "File name empty"
|
|
Packit |
8cecbd |
packName maxLen (c:cs)
|
|
Packit |
8cecbd |
| n > maxLen = Left "File name too long"
|
|
Packit |
8cecbd |
| otherwise = Right (packName' maxLen n [c] cs)
|
|
Packit |
8cecbd |
where n = length c
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
packName' maxLen n ok (c:cs)
|
|
Packit |
8cecbd |
| n' <= maxLen = packName' maxLen n' (c:ok) cs
|
|
Packit |
8cecbd |
where n' = n + length c
|
|
Packit |
8cecbd |
packName' _ _ ok cs = (FilePath.Posix.joinPath ok, cs)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | The tar format allows just 100 ASCII characters for the 'SymbolicLink' and
|
|
Packit |
8cecbd |
-- 'HardLink' entry types.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
newtype LinkTarget = LinkTarget BS.ByteString
|
|
Packit |
8cecbd |
deriving (Eq, Ord, Show)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance NFData LinkTarget where
|
|
Packit |
8cecbd |
#if MIN_VERSION_bytestring(0,10,0)
|
|
Packit |
8cecbd |
rnf (LinkTarget bs) = rnf bs
|
|
Packit |
8cecbd |
#else
|
|
Packit |
8cecbd |
rnf (LinkTarget !_bs) = ()
|
|
Packit |
8cecbd |
#endif
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | Convert a native 'FilePath' to a tar 'LinkTarget'. This may fail if the
|
|
Packit |
8cecbd |
-- string is longer than 100 characters or if it contains non-portable
|
|
Packit |
8cecbd |
-- characters.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
toLinkTarget :: FilePath -> Maybe LinkTarget
|
|
Packit |
8cecbd |
toLinkTarget path | length path <= 100 = Just $! LinkTarget (BS.Char8.pack path)
|
|
Packit |
8cecbd |
| otherwise = Nothing
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | Convert a tar 'LinkTarget' to a native 'FilePath'.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
fromLinkTarget :: LinkTarget -> FilePath
|
|
Packit |
8cecbd |
fromLinkTarget (LinkTarget pathbs) = adjustDirectory $
|
|
Packit |
8cecbd |
FilePath.Native.joinPath $ FilePath.Posix.splitDirectories path
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
path = BS.Char8.unpack pathbs
|
|
Packit |
8cecbd |
adjustDirectory | FilePath.Posix.hasTrailingPathSeparator path
|
|
Packit |
8cecbd |
= FilePath.Native.addTrailingPathSeparator
|
|
Packit |
8cecbd |
| otherwise = id
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | Convert a tar 'LinkTarget' to a Unix/Posix 'FilePath'.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
fromLinkTargetToPosixPath :: LinkTarget -> FilePath
|
|
Packit |
8cecbd |
fromLinkTargetToPosixPath (LinkTarget pathbs) = adjustDirectory $
|
|
Packit |
8cecbd |
FilePath.Posix.joinPath $ FilePath.Posix.splitDirectories path
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
path = BS.Char8.unpack pathbs
|
|
Packit |
8cecbd |
adjustDirectory | FilePath.Posix.hasTrailingPathSeparator path
|
|
Packit |
8cecbd |
= FilePath.Native.addTrailingPathSeparator
|
|
Packit |
8cecbd |
| otherwise = id
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | Convert a tar 'LinkTarget' to a Windows 'FilePath'.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
fromLinkTargetToWindowsPath :: LinkTarget -> FilePath
|
|
Packit |
8cecbd |
fromLinkTargetToWindowsPath (LinkTarget pathbs) = adjustDirectory $
|
|
Packit |
8cecbd |
FilePath.Windows.joinPath $ FilePath.Posix.splitDirectories path
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
path = BS.Char8.unpack pathbs
|
|
Packit |
8cecbd |
adjustDirectory | FilePath.Posix.hasTrailingPathSeparator path
|
|
Packit |
8cecbd |
= FilePath.Windows.addTrailingPathSeparator
|
|
Packit |
8cecbd |
| otherwise = id
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- * Entries type
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | A tar archive is a sequence of entries.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- The point of this type as opposed to just using a list is that it makes the
|
|
Packit |
8cecbd |
-- failure case explicit. We need this because the sequence of entries we get
|
|
Packit |
8cecbd |
-- from reading a tarball can include errors.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- It is a concrete data type so you can manipulate it directly but it is often
|
|
Packit |
8cecbd |
-- clearer to use the provided functions for mapping, folding and unfolding.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- Converting from a list can be done with just @foldr Next Done@. Converting
|
|
Packit |
8cecbd |
-- back into a list can be done with 'foldEntries' however in that case you
|
|
Packit |
8cecbd |
-- must be prepared to handle the 'Fail' case inherent in the 'Entries' type.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- The 'Monoid' instance lets you concatenate archives or append entries to an
|
|
Packit |
8cecbd |
-- archive.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
data Entries e = Next Entry (Entries e)
|
|
Packit |
8cecbd |
| Done
|
|
Packit |
8cecbd |
| Fail e
|
|
Packit |
8cecbd |
deriving (Eq, Show)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
infixr 5 `Next`
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | This is like the standard 'unfoldr' function on lists, but for 'Entries'.
|
|
Packit |
8cecbd |
-- It includes failure as an extra possibility that the stepper function may
|
|
Packit |
8cecbd |
-- return.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- It can be used to generate 'Entries' from some other type. For example it is
|
|
Packit |
8cecbd |
-- used internally to lazily unfold entries from a 'LBS.ByteString'.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
unfoldEntries :: (a -> Either e (Maybe (Entry, a))) -> a -> Entries e
|
|
Packit |
8cecbd |
unfoldEntries f = unfold
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
unfold x = case f x of
|
|
Packit |
8cecbd |
Left err -> Fail err
|
|
Packit |
8cecbd |
Right Nothing -> Done
|
|
Packit |
8cecbd |
Right (Just (e, x')) -> Next e (unfold x')
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | This is like the standard 'foldr' function on lists, but for 'Entries'.
|
|
Packit |
8cecbd |
-- Compared to 'foldr' it takes an extra function to account for the
|
|
Packit |
8cecbd |
-- possibility of failure.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- This is used to consume a sequence of entries. For example it could be used
|
|
Packit |
8cecbd |
-- to scan a tarball for problems or to collect an index of the contents.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
foldEntries :: (Entry -> a -> a) -> a -> (e -> a) -> Entries e -> a
|
|
Packit |
8cecbd |
foldEntries next done fail' = fold
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
fold (Next e es) = next e (fold es)
|
|
Packit |
8cecbd |
fold Done = done
|
|
Packit |
8cecbd |
fold (Fail err) = fail' err
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | A 'foldl'-like function on Entries. It either returns the final
|
|
Packit |
8cecbd |
-- accumulator result, or the failure along with the intermediate accumulator
|
|
Packit |
8cecbd |
-- value.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
foldlEntries :: (a -> Entry -> a) -> a -> Entries e -> Either (e, a) a
|
|
Packit |
8cecbd |
foldlEntries f z = go z
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
go !acc (Next e es) = go (f acc e) es
|
|
Packit |
8cecbd |
go !acc Done = Right acc
|
|
Packit |
8cecbd |
go !acc (Fail err) = Left (err, acc)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | This is like the standard 'map' function on lists, but for 'Entries'. It
|
|
Packit |
8cecbd |
-- includes failure as a extra possible outcome of the mapping function.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
-- If your mapping function cannot fail it may be more convenient to use
|
|
Packit |
8cecbd |
-- 'mapEntriesNoFail'
|
|
Packit |
8cecbd |
mapEntries :: (Entry -> Either e' Entry) -> Entries e -> Entries (Either e e')
|
|
Packit |
8cecbd |
mapEntries f =
|
|
Packit |
8cecbd |
foldEntries (\entry rest -> either (Fail . Right) (flip Next rest) (f entry)) Done (Fail . Left)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- | Like 'mapEntries' but the mapping function itself cannot fail.
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
mapEntriesNoFail :: (Entry -> Entry) -> Entries e -> Entries e
|
|
Packit |
8cecbd |
mapEntriesNoFail f =
|
|
Packit |
8cecbd |
foldEntries (\entry -> Next (f entry)) Done Fail
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance Monoid (Entries e) where
|
|
Packit |
8cecbd |
mempty = Done
|
|
Packit |
8cecbd |
mappend a b = foldEntries Next b Fail a
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance Functor Entries where
|
|
Packit |
8cecbd |
fmap f = foldEntries Next Done (Fail . f)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance NFData e => NFData (Entries e) where
|
|
Packit |
8cecbd |
rnf (Next e es) = rnf e `seq` rnf es
|
|
Packit |
8cecbd |
rnf Done = ()
|
|
Packit |
8cecbd |
rnf (Fail e) = rnf e
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-------------------------
|
|
Packit |
8cecbd |
-- QuickCheck instances
|
|
Packit |
8cecbd |
--
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
#ifdef TESTS
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance Arbitrary Entry where
|
|
Packit |
8cecbd |
arbitrary = Entry <$> arbitrary <*> arbitrary <*> arbitraryPermissions
|
|
Packit |
8cecbd |
<*> arbitrary <*> arbitraryEpochTime <*> arbitrary
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
arbitraryPermissions :: Gen Permissions
|
|
Packit |
8cecbd |
arbitraryPermissions = fromIntegral <$> (arbitraryOctal 7 :: Gen Int)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
arbitraryEpochTime :: Gen EpochTime
|
|
Packit |
8cecbd |
arbitraryEpochTime = fromIntegral <$> (arbitraryOctal 11 :: Gen Int)
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
shrink (Entry path content perms author time format) =
|
|
Packit |
8cecbd |
[ Entry path' content' perms author' time' format
|
|
Packit |
8cecbd |
| (path', content', author', time') <-
|
|
Packit |
8cecbd |
shrink (path, content, author, time) ]
|
|
Packit |
8cecbd |
++ [ Entry path content perms' author time format
|
|
Packit |
8cecbd |
| perms' <- shrinkIntegral perms ]
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance Arbitrary TarPath where
|
|
Packit |
8cecbd |
arbitrary = either error id
|
|
Packit |
8cecbd |
. toTarPath False
|
|
Packit |
8cecbd |
. FilePath.Posix.joinPath
|
|
Packit |
8cecbd |
<$> listOf1ToN (255 `div` 5)
|
|
Packit |
8cecbd |
(elements (map (replicate 4) "abcd"))
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
shrink = map (either error id . toTarPath False)
|
|
Packit |
8cecbd |
. map FilePath.Posix.joinPath
|
|
Packit |
8cecbd |
. filter (not . null)
|
|
Packit |
8cecbd |
. shrinkList shrinkNothing
|
|
Packit |
8cecbd |
. FilePath.Posix.splitPath
|
|
Packit |
8cecbd |
. fromTarPathToPosixPath
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance Arbitrary LinkTarget where
|
|
Packit |
8cecbd |
arbitrary = maybe (error "link target too large") id
|
|
Packit |
8cecbd |
. toLinkTarget
|
|
Packit |
8cecbd |
. FilePath.Native.joinPath
|
|
Packit |
8cecbd |
<$> listOf1ToN (100 `div` 5)
|
|
Packit |
8cecbd |
(elements (map (replicate 4) "abcd"))
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
shrink = map (maybe (error "link target too large") id . toLinkTarget)
|
|
Packit |
8cecbd |
. map FilePath.Posix.joinPath
|
|
Packit |
8cecbd |
. filter (not . null)
|
|
Packit |
8cecbd |
. shrinkList shrinkNothing
|
|
Packit |
8cecbd |
. FilePath.Posix.splitPath
|
|
Packit |
8cecbd |
. fromLinkTargetToPosixPath
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
listOf1ToN :: Int -> Gen a -> Gen [a]
|
|
Packit |
8cecbd |
listOf1ToN n g = sized $ \sz -> do
|
|
Packit |
8cecbd |
n <- choose (1, min n (max 1 sz))
|
|
Packit |
8cecbd |
vectorOf n g
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
listOf0ToN :: Int -> Gen a -> Gen [a]
|
|
Packit |
8cecbd |
listOf0ToN n g = sized $ \sz -> do
|
|
Packit |
8cecbd |
n <- choose (0, min n sz)
|
|
Packit |
8cecbd |
vectorOf n g
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance Arbitrary EntryContent where
|
|
Packit |
8cecbd |
arbitrary =
|
|
Packit |
8cecbd |
frequency
|
|
Packit |
8cecbd |
[ (16, do bs <- arbitrary;
|
|
Packit |
8cecbd |
return (NormalFile bs (LBS.length bs)))
|
|
Packit |
8cecbd |
, (2, pure Directory)
|
|
Packit |
8cecbd |
, (1, SymbolicLink <$> arbitrary)
|
|
Packit |
8cecbd |
, (1, HardLink <$> arbitrary)
|
|
Packit |
8cecbd |
, (1, CharacterDevice <$> arbitraryOctal 7 <*> arbitraryOctal 7)
|
|
Packit |
8cecbd |
, (1, BlockDevice <$> arbitraryOctal 7 <*> arbitraryOctal 7)
|
|
Packit |
8cecbd |
, (1, pure NamedPipe)
|
|
Packit |
8cecbd |
, (1, do c <- elements (['A'..'Z']++['a'..'z'])
|
|
Packit |
8cecbd |
bs <- arbitrary;
|
|
Packit |
8cecbd |
return (OtherEntryType c bs (LBS.length bs)))
|
|
Packit |
8cecbd |
]
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
shrink (NormalFile bs _) = [ NormalFile bs' (LBS.length bs')
|
|
Packit |
8cecbd |
| bs' <- shrink bs ]
|
|
Packit |
8cecbd |
shrink Directory = []
|
|
Packit |
8cecbd |
shrink (SymbolicLink link) = [ SymbolicLink link' | link' <- shrink link ]
|
|
Packit |
8cecbd |
shrink (HardLink link) = [ HardLink link' | link' <- shrink link ]
|
|
Packit |
8cecbd |
shrink (CharacterDevice ma mi) = [ CharacterDevice ma' mi'
|
|
Packit |
8cecbd |
| (ma', mi') <- shrink (ma, mi) ]
|
|
Packit |
8cecbd |
shrink (BlockDevice ma mi) = [ BlockDevice ma' mi'
|
|
Packit |
8cecbd |
| (ma', mi') <- shrink (ma, mi) ]
|
|
Packit |
8cecbd |
shrink NamedPipe = []
|
|
Packit |
8cecbd |
shrink (OtherEntryType c bs _) = [ OtherEntryType c bs' (LBS.length bs')
|
|
Packit |
8cecbd |
| bs' <- shrink bs ]
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance Arbitrary LBS.ByteString where
|
|
Packit |
8cecbd |
arbitrary = fmap LBS.pack arbitrary
|
|
Packit |
8cecbd |
shrink = map LBS.pack . shrink . LBS.unpack
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance Arbitrary BS.ByteString where
|
|
Packit |
8cecbd |
arbitrary = fmap BS.pack arbitrary
|
|
Packit |
8cecbd |
shrink = map BS.pack . shrink . BS.unpack
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance Arbitrary Ownership where
|
|
Packit |
8cecbd |
arbitrary = Ownership <$> name <*> name
|
|
Packit |
8cecbd |
<*> idno <*> idno
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
name = listOf0ToN 32 (arbitrary `suchThat` (/= '\0'))
|
|
Packit |
8cecbd |
idno = arbitraryOctal 7
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
shrink (Ownership oname gname oid gid) =
|
|
Packit |
8cecbd |
[ Ownership oname' gname' oid' gid'
|
|
Packit |
8cecbd |
| (oname', gname', oid', gid') <- shrink (oname, gname, oid, gid) ]
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
instance Arbitrary Format where
|
|
Packit |
8cecbd |
arbitrary = elements [V7Format, UstarFormat, GnuFormat]
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
--arbitraryOctal :: (Integral n, Random n) => Int -> Gen n
|
|
Packit |
8cecbd |
arbitraryOctal n =
|
|
Packit |
8cecbd |
oneof [ pure 0
|
|
Packit |
8cecbd |
, choose (0, upperBound)
|
|
Packit |
8cecbd |
, pure upperBound
|
|
Packit |
8cecbd |
]
|
|
Packit |
8cecbd |
where
|
|
Packit |
8cecbd |
upperBound = 8^n-1
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
-- For QC tests it's useful to have a way to limit the info to that which can
|
|
Packit |
8cecbd |
-- be expressed in the old V7 format
|
|
Packit |
8cecbd |
limitToV7FormatCompat :: Entry -> Entry
|
|
Packit |
8cecbd |
limitToV7FormatCompat entry@Entry { entryFormat = V7Format } =
|
|
Packit |
8cecbd |
entry {
|
|
Packit |
8cecbd |
entryContent = case entryContent entry of
|
|
Packit |
8cecbd |
CharacterDevice _ _ -> OtherEntryType '3' LBS.empty 0
|
|
Packit |
8cecbd |
BlockDevice _ _ -> OtherEntryType '4' LBS.empty 0
|
|
Packit |
8cecbd |
Directory -> OtherEntryType '5' LBS.empty 0
|
|
Packit |
8cecbd |
NamedPipe -> OtherEntryType '6' LBS.empty 0
|
|
Packit |
8cecbd |
other -> other,
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
entryOwnership = (entryOwnership entry) {
|
|
Packit |
8cecbd |
groupName = "",
|
|
Packit |
8cecbd |
ownerName = ""
|
|
Packit |
8cecbd |
},
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
entryTarPath = let TarPath name _prefix = entryTarPath entry
|
|
Packit |
8cecbd |
in TarPath name BS.empty
|
|
Packit |
8cecbd |
}
|
|
Packit |
8cecbd |
limitToV7FormatCompat entry = entry
|
|
Packit |
8cecbd |
|
|
Packit |
8cecbd |
#endif
|
|
Packit |
8cecbd |
|