Skip to content

Commit

Permalink
Use Base16 hash for script path.
Browse files Browse the repository at this point in the history
Issue #9334 shows that `%` characters on Windows result in invalid
paths, also `/` characters on Linux create invalid paths.

This changes from using base64 to using base16 with the same length
we use for unit-ids.
  • Loading branch information
jasagredo committed Nov 22, 2023
1 parent eaa5245 commit b0e8435
Show file tree
Hide file tree
Showing 6 changed files with 18 additions and 27 deletions.
1 change: 0 additions & 1 deletion cabal-install/cabal-install.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ library
async >= 2.0 && < 2.3,
array >= 0.4 && < 0.6,
base16-bytestring >= 0.1.1 && < 1.1.0.0,
base64-bytestring >= 1.0 && < 1.3,
binary >= 0.7.3 && < 0.9,
bytestring >= 0.10.6.0 && < 0.13,
containers >= 0.5.6.2 && < 0.8,
Expand Down
5 changes: 0 additions & 5 deletions cabal-install/src/Distribution/Client/HashValue.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ module Distribution.Client.HashValue
, hashValue
, truncateHash
, showHashValue
, showHashValueBase64
, readFileHashValue
, hashFromTUF
) where
Expand All @@ -19,7 +18,6 @@ import qualified Hackage.Security.Client as Sec

import qualified Crypto.Hash.SHA256 as SHA256
import qualified Data.ByteString.Base16 as Base16
import qualified Data.ByteString.Base64 as Base64
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as LBS

Expand Down Expand Up @@ -57,9 +55,6 @@ hashValue = HashValue . SHA256.hashlazy
showHashValue :: HashValue -> String
showHashValue (HashValue digest) = BS.unpack (Base16.encode digest)

showHashValueBase64 :: HashValue -> String
showHashValueBase64 (HashValue digest) = BS.unpack (Base64.encode digest)

-- | Hash the content of a file. Uses SHA256.
readFileHashValue :: FilePath -> IO HashValue
readFileHashValue tarball =
Expand Down
22 changes: 5 additions & 17 deletions cabal-install/src/Distribution/Client/ScriptUtils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ import Distribution.Client.DistDirLayout
)
import Distribution.Client.HashValue
( hashValue
, showHashValueBase64
, showHashValue
, truncateHash
)
import Distribution.Client.HttpUtils
( HttpTransport
Expand Down Expand Up @@ -218,33 +219,20 @@ import qualified Text.Parsec as P
-- repl to deal with the fact that the repl is relative to the working directory and not
-- the project root.

-- | Get the hash of a script's absolute path)
--
-- Two hashes will be the same as long as the absolute paths
-- are the same.
-- | Get the hash of a script's contents.
getScriptHash :: FilePath -> IO String
getScriptHash script =
-- Base64 is shorter than Base16, which helps avoid long path issues on windows
-- but it can contain /'s which aren't valid in file paths so replace them with
-- %'s. 26 chars / 130 bits is enough to practically avoid collisions.
map (\c -> if c == '/' then '%' else c)
. take 26
. showHashValueBase64
showHashValue
. truncateHash 26
. hashValue
. fromString
<$> canonicalizePath script

-- | Get the directory for caching a script build.
--
-- The only identity of a script is it's absolute path, so append the
-- hashed path to the @script-builds@ dir to get the cache directory.
getScriptCacheDirectory :: FilePath -> IO FilePath
getScriptCacheDirectory script = (</>) <$> defaultScriptBuildsDir <*> getScriptHash script

-- | Get the directory for caching a script build and ensure it exists.
--
-- The only identity of a script is it's absolute path, so append the
-- hashed path to the @script-builds@ dir to get the cache directory.
ensureScriptCacheDirectory :: Verbosity -> FilePath -> IO FilePath
ensureScriptCacheDirectory verbosity script = do
cacheDir <- getScriptCacheDirectory script
Expand Down
3 changes: 2 additions & 1 deletion cabal-testsuite/cabal-testsuite.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ library
, aeson ^>= 1.4.2.0 || ^>=1.5.0.0 || ^>= 2.0.0.0 || ^>= 2.1.0.0 || ^>= 2.2.1.0
, async ^>= 2.2.1
, attoparsec ^>= 0.13.2.2 || ^>=0.14.1
, base64-bytestring ^>= 1.0.0.0 || ^>= 1.1.0.0 || ^>= 1.2.0.0
, base16-bytestring ^>= 0.1.1.5 || ^>= 1.0
, bytestring ^>= 0.10.0.2 || ^>= 0.11.0.0 || ^>= 0.12.0.0
, containers ^>= 0.5.0.0 || ^>= 0.6.0.1
, cryptohash-sha256 ^>= 0.11.101.0
Expand Down Expand Up @@ -120,6 +120,7 @@ executable setup

-- If you require an external dependency for a test it must be listed here.
executable test-runtime-deps
default-language: Haskell2010
build-depends: cabal-testsuite,
base,
directory,
Expand Down
5 changes: 2 additions & 3 deletions cabal-testsuite/src/Test/Cabal/Prelude.hs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import Control.Monad (unless, when, void, forM_, liftM2, liftM4)
import Control.Monad.Trans.Reader (withReaderT, runReaderT)
import Control.Monad.IO.Class (MonadIO (..))
import qualified Crypto.Hash.SHA256 as SHA256
import qualified Data.ByteString.Base64 as Base64
import qualified Data.ByteString.Base16 as Base16
import qualified Data.ByteString.Char8 as C
import Data.List (isInfixOf, stripPrefix, isPrefixOf, intercalate)
import Data.List.NonEmpty (NonEmpty (..))
Expand Down Expand Up @@ -840,8 +840,7 @@ getScriptCacheDirectory :: FilePath -> TestM FilePath
getScriptCacheDirectory script = do
cabalDir <- testCabalDir `fmap` getTestEnv
hashinput <- liftIO $ canonicalizePath script
let hash = map (\c -> if c == '/' then '%' else c) . take 26
. C.unpack . Base64.encode . SHA256.hash . C.pack $ hashinput
let hash = C.unpack . Base16.encode . C.take 26 . SHA256.hash . C.pack $ hashinput
return $ cabalDir </> "script-builds" </> hash

------------------------------------------------------------------------
Expand Down
9 changes: 9 additions & 0 deletions changelog.d/base16-script-cache
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
synopsis: Script cache dir is the base16 hash of the canonical path of the script.
prs: #9459
packages: cabal-install

description: {

Script cache dir is the base16 hash of the canonical path of the script.

}

0 comments on commit b0e8435

Please sign in to comment.