Filesystem.hs (2107B)
1 module ContentStore.Filesystem 2 ( putObject 3 , getObject 4 , objectPath 5 , ensureStore 6 ) where 7 8 import ContentStore.Object 9 10 import Control.Monad (unless, when) 11 import Data.Text (unpack) 12 import System.Directory (createDirectoryIfMissing, doesFileExist, removeFile, renameFile) 13 import System.FilePath ((</>)) 14 import System.IO (hClose, openBinaryTempFile) 15 16 import qualified Data.ByteString as BS 17 18 ensureStore :: StorePath -> IO () 19 ensureStore (StorePath root) = do 20 createDirectoryIfMissing True (root </> "objects") 21 createDirectoryIfMissing True (root </> "aliases" </> "names") 22 createDirectoryIfMissing True (root </> "aliases" </> "modules") 23 createDirectoryIfMissing True (root </> "aliases" </> "packages") 24 createDirectoryIfMissing True (root </> "manifests") 25 createDirectoryIfMissing True (root </> "tmp") 26 27 objectPath :: StorePath -> ObjectHash -> FilePath 28 objectPath (StorePath root) h = root </> "objects" </> shardForHash h </> unpack h 29 30 putObject :: StorePath -> Domain -> BS.ByteString -> IO ObjectHash 31 putObject store@(StorePath root) domain payload = do 32 ensureStore store 33 let h = hashObject domain payload 34 shardDir = root </> "objects" </> shardForHash h 35 finalPath = objectPath store h 36 createDirectoryIfMissing True shardDir 37 exists <- doesFileExist finalPath 38 if exists 39 then verifyExisting finalPath 40 else do 41 let tmpDir = root </> "tmp" 42 (tmpPath, handle) <- openBinaryTempFile tmpDir (unpack h ++ ".tmp") 43 BS.hPut handle payload 44 hClose handle 45 raced <- doesFileExist finalPath 46 if raced 47 then removeFile tmpPath >> verifyExisting finalPath 48 else renameFile tmpPath finalPath 49 return h 50 where 51 verifyExisting path = do 52 existing <- BS.readFile path 53 when (existing /= payload) $ 54 fail $ "content-addressed object exists with mismatched bytes: " ++ path 55 56 getObject :: StorePath -> ObjectHash -> IO (Maybe BS.ByteString) 57 getObject store h = do 58 let path = objectPath store h 59 exists <- doesFileExist path 60 if exists then Just <$> BS.readFile path else return Nothing