Merge pull request #32 from haskell-nix/readonly-store-hash

Implement readonly store path hashing
This commit is contained in:
Doug Beardsley 2019-03-10 13:13:01 -04:00 committed by GitHub
commit 8965dc85bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 80 additions and 19 deletions

View File

@ -24,9 +24,11 @@ library
, System.Nix.Internal.Hash
, System.Nix.Nar
, System.Nix.Path
, System.Nix.ReadonlyStore
, System.Nix.Store
, System.Nix.Util
build-depends: base >=4.10 && <4.12
, base16-bytestring
, bytestring
, binary
, bytestring
@ -61,7 +63,7 @@ test-suite format-tests
NarFormat
Hash
hs-source-dirs:
tests
tests
build-depends:
hnix-store-core
, base

View File

@ -12,25 +12,29 @@ Maintainer : Greg Hale <imalsogreg@gmail.com>
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE OverloadedStrings #-}
module System.Nix.Internal.Hash where
import qualified Crypto.Hash.MD5 as MD5
import qualified Crypto.Hash.SHA1 as SHA1
import qualified Crypto.Hash.SHA256 as SHA256
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import Data.Bits (xor)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import qualified Data.Hashable as DataHashable
import Data.Kind (Type)
import Data.List (foldl')
import Data.Proxy (Proxy(Proxy))
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Vector as V
import Data.Word (Word8)
import qualified Crypto.Hash.MD5 as MD5
import qualified Crypto.Hash.SHA1 as SHA1
import qualified Crypto.Hash.SHA256 as SHA256
import qualified Data.ByteString as BS
import qualified Data.ByteString.Base16 as Base16
import qualified Data.ByteString.Char8 as BSC
import Data.Bits (xor)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import qualified Data.Hashable as DataHashable
import Data.Kind (Type)
import Data.List (foldl')
import Data.Monoid
import Data.Proxy (Proxy(Proxy))
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Vector as V
import Data.Word (Word8)
import GHC.TypeLits
-- | A tag for different hashing algorithms
@ -45,6 +49,18 @@ data HashAlgorithm' n
| Truncated n (HashAlgorithm' n)
deriving (Eq, Show)
class HashAlgoText a where
algoString :: Proxy a -> Text
instance HashAlgoText 'MD5 where
algoString (Proxy :: Proxy 'MD5) = "md5"
instance HashAlgoText 'SHA1 where
algoString (Proxy :: Proxy 'SHA1) = "sha1"
instance HashAlgoText 'SHA256 where
algoString (Proxy :: Proxy 'SHA256) = "sha256"
type HashAlgorithm = HashAlgorithm' Nat
-- | Types with kind @HashAlgorithm@ may be a @HasDigest@ instance
@ -80,8 +96,11 @@ hashLazy :: forall a.HasDigest a => BSL.ByteString -> Digest a
hashLazy bsl =
finalize $ foldl' (update @a) (initialize @a) (BSL.toChunks bsl)
digestText32 :: forall a. HashAlgoText a => Digest a -> T.Text
digestText32 d = algoString (Proxy :: Proxy a) <> ":" <> printAsBase32 d
digestText16 :: forall a. HashAlgoText a => Digest a -> T.Text
digestText16 (Digest bs) = algoString (Proxy :: Proxy a) <> ":" <> T.decodeUtf8 (Base16.encode bs)
-- | Convert any Digest to a base32-encoded string.
-- This is not used in producing store path hashes

View File

@ -3,11 +3,13 @@ Description : Types and effects for interacting with the Nix store.
Maintainer : Shea Levy <shea@shealevy.com>
-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module System.Nix.Path
( FilePathPart(..)
, PathHashAlgo
, Path(..)
, pathToText
, PathSet
, SubstitutablePathInfo(..)
, ValidPathInfo(..)
@ -19,12 +21,14 @@ module System.Nix.Path
import System.Nix.Hash (Digest(..),
HashAlgorithm'(Truncated, SHA256))
import System.Nix.Internal.Hash
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import Data.Hashable (Hashable (..), hashPtrWithSalt)
import Data.HashMap.Strict (HashMap)
import Data.HashSet (HashSet)
import Data.Map.Strict (Map)
import Data.Monoid
import Data.Text (Text)
import qualified Data.Text as T
import System.IO.Unsafe (unsafeDupablePerformIO)
@ -46,7 +50,7 @@ newtype PathName = PathName
-- | A regular expression for matching a valid 'PathName'
nameRegex :: Regex
nameRegex =
makeRegex "[a-zA-Z0-9\\+\\-\\_\\?\\=][a-zA-Z0-9\\+\\-\\.\\_\\?\\=]*"
makeRegex ("[a-zA-Z0-9\\+\\-\\_\\?\\=][a-zA-Z0-9\\+\\-\\.\\_\\?\\=]*" :: String)
-- | Construct a 'PathName', assuming the provided contents are valid.
pathName :: Text -> Maybe PathName
@ -58,6 +62,9 @@ pathName n = case matchTest nameRegex n of
data Path = Path !(Digest PathHashAlgo) !PathName
deriving (Eq, Ord, Show)
pathToText :: Text -> Path -> Text
pathToText storeDir (Path h nm) = storeDir <> "/" <> printAsBase32 h <> "-" <> pathNameContents nm
type PathSet = HashSet Path
-- | Information about substitutes for a 'Path'.

View File

@ -0,0 +1,33 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module System.Nix.ReadonlyStore where
import Data.ByteString (ByteString)
import Data.ByteString.Base16 as Base16
import qualified Data.HashSet as HS
import Data.Text (Text)
import qualified Data.Text as T
import Data.Text.Encoding
import System.Nix.Internal.Hash
import System.Nix.Path
makeStorePath :: Text -> Text -> Digest 'SHA256 -> Text -> Path
makeStorePath storeDir ty h nm = Path storeHash (PathName nm)
where
s = T.intercalate ":"
[ ty
, digestText16 h
, storeDir
, nm
]
storeHash = truncateDigest $ hash $ encodeUtf8 s
makeTextPath :: Text -> Text -> Digest 'SHA256 -> PathSet -> Path
makeTextPath storeDir nm h refs = makeStorePath storeDir ty h nm
where
ty = T.intercalate ":" ("text" : map (pathToText storeDir) (HS.toList refs))
computeStorePathForText :: Text -> Text -> ByteString -> PathSet -> Path
computeStorePathForText storeDir nm s refs = makeTextPath storeDir nm (hash s) refs