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.

(cherry picked from commit 97f9917)

# Conflicts:
#	cabal-install/src/Distribution/Client/HashValue.hs
#	cabal-install/src/Distribution/Client/ScriptUtils.hs
#	cabal-testsuite/cabal-testsuite.cabal
  • Loading branch information
jasagredo authored and mergify[bot] committed Nov 29, 2023
1 parent 5d97fcf commit 37213e8
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 9 deletions.
1 change: 0 additions & 1 deletion cabal-install/cabal-install.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,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.7,
Expand Down
22 changes: 19 additions & 3 deletions cabal-install/src/Distribution/Client/HashValue.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
<<<<<<< HEAD
{-# LANGUAGE DeriveGeneric #-}
module Distribution.Client.HashValue (
HashValue,
Expand All @@ -10,16 +11,34 @@ module Distribution.Client.HashValue (
readFileHashValue,
hashFromTUF,
) where
=======
{-# LANGUAGE DeriveGeneric #-}

module Distribution.Client.HashValue
( HashValue
, hashValue
, truncateHash
, showHashValue
, readFileHashValue
, hashFromTUF
) where
>>>>>>> 97f99171b (Use Base16 hash for script path.)

import Distribution.Client.Compat.Prelude
import Prelude ()

import qualified Hackage.Security.Client as Sec

<<<<<<< HEAD
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 Crypto.Hash.SHA256 as SHA256
import qualified Data.ByteString.Base16 as Base16
import qualified Data.ByteString.Char8 as BS
>>>>>>> 97f99171b (Use Base16 hash for script path.)
import qualified Data.ByteString.Lazy.Char8 as LBS

import System.IO (IOMode (..), withBinaryFile)
Expand Down Expand Up @@ -57,9 +76,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
Expand Down
54 changes: 53 additions & 1 deletion cabal-install/src/Distribution/Client/ScriptUtils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,49 @@ import Distribution.Compat.Lens
import qualified Distribution.Types.Lens as L

import Distribution.CabalSpecVersion
<<<<<<< HEAD
( CabalSpecVersion (..), cabalSpecLatest)
=======
( CabalSpecVersion (..)
, cabalSpecLatest
)
import Distribution.Client.Config
( defaultScriptBuildsDir
)
import Distribution.Client.DistDirLayout
( DistDirLayout (..)
, DistDirParams (..)
)
import Distribution.Client.HashValue
( hashValue
, showHashValue
, truncateHash
)
import Distribution.Client.HttpUtils
( HttpTransport
, configureTransport
)
import Distribution.Client.NixStyleOptions
( NixStyleFlags (..)
)
import Distribution.Client.ProjectConfig
( PackageConfig (..)
, ProjectConfig (..)
, ProjectConfigShared (..)
, projectConfigHttpTransport
, reportParseResult
, withGlobalConfig
, withProjectOrGlobalConfig
)
import Distribution.Client.ProjectConfig.Legacy
( ProjectConfigSkeleton
, instantiateProjectConfigSkeletonFetchingCompiler
, parseProjectSkeleton
)
import Distribution.Client.ProjectFlags
( flagIgnoreProject
)
>>>>>>> 97f99171b (Use Base16 hash for script path.)
import Distribution.Client.ProjectOrchestration
import Distribution.Client.Config
( defaultScriptBuildsDir )
Expand Down Expand Up @@ -131,17 +173,27 @@ 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)
-- | Get the hash of a script's absolute path.
--
-- Two hashes will be the same as long as the absolute paths
-- are the same.
getScriptHash :: FilePath -> IO String
<<<<<<< HEAD
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 . hashValue . fromString <$> canonicalizePath script
=======
getScriptHash script =
-- Truncation here tries to help with long path issues on Windows.
showHashValue
. truncateHash 26
. hashValue
. fromString
<$> canonicalizePath script
>>>>>>> 97f99171b (Use Base16 hash for script path.)

-- | Get the directory for caching a script build.
--
Expand Down
32 changes: 31 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 @@ -110,6 +110,36 @@ executable setup
import: shared
main-is: Setup.simple.hs

<<<<<<< HEAD
=======
-- This executable component is used to describe the runtime dependencies of
-- the tests. The Main.hs file and resulting executable are not useful in any way.

-- Ideally this would be an empty library, but because build-type: Custom, we can't
-- have sublibraries.

-- 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,
Cabal,
Cabal-syntax,
filepath,
transformers,
bytestring,
time,
process,
exceptions
main-is: static/Main.hs
if !os(windows)
build-depends: unix
else
build-depends:
, Win32

>>>>>>> 97f99171b (Use Base16 hash for script path.)
custom-setup
-- we only depend on even stable releases of lib:Cabal
-- and due to Custom complexity and ConstraintSetupCabalMaxVersion
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 @@ -52,7 +52,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 @@ -842,8 +842,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 37213e8

Please sign in to comment.