git-annex/Backend.hs

162 lines
5.1 KiB
Haskell
Raw Normal View History

2010-10-16 20:15:31 +00:00
{- git-annex key-value storage backends
2010-10-10 17:47:04 +00:00
-
2010-10-16 20:15:31 +00:00
- git-annex uses a key-value abstraction layer to allow files contents to be
- stored in different ways. In theory, any key-value storage system could be
2010-10-10 17:47:04 +00:00
- used to store the file contents, and git-annex would then retrieve them
- as needed and put them in `.git/annex/`.
-
- When a file is annexed, a key is generated from its content and/or metadata.
- This key can later be used to retrieve the file's content (its value). This
- key generation must be stable for a given file content, name, and size.
-
- Multiple pluggable backends are supported, and more than one can be used
- to store different files' contents in a given repository.
2010-10-27 20:53:54 +00:00
-
- Copyright 2010 Joey Hess <joey@kitenet.net>
-
- Licensed under the GNU GPL version 3 or higher.
-}
2010-10-10 17:47:04 +00:00
2010-10-11 21:52:46 +00:00
module Backend (
list,
2010-10-14 18:14:19 +00:00
storeFileKey,
retrieveKeyFile,
2010-10-14 21:37:20 +00:00
removeKey,
hasKey,
fsckKey,
lookupFile,
2010-11-15 22:04:19 +00:00
chooseBackends,
keyBackend
2010-10-11 21:52:46 +00:00
) where
2010-10-10 17:47:04 +00:00
2010-10-14 01:28:47 +00:00
import Control.Monad.State
2010-11-22 21:51:55 +00:00
import System.IO.Error (try)
import System.FilePath
import System.Posix.Files
2010-10-16 20:20:49 +00:00
import Locations
2010-10-14 06:36:41 +00:00
import qualified GitRepo as Git
2010-10-14 07:18:11 +00:00
import qualified Annex
import Types
2010-10-18 06:06:27 +00:00
import qualified TypeInternals as Internals
2010-11-08 19:15:21 +00:00
import Messages
2010-10-14 19:58:53 +00:00
{- List of backends in the order to try them when storing a new key. -}
list :: Annex [Backend Annex]
list = do
l <- Annex.getState Annex.backends -- list is cached here
if not $ null l
2010-10-14 19:58:53 +00:00
then return l
else do
bs <- Annex.getState Annex.supportedBackends
2010-10-14 19:58:53 +00:00
g <- Annex.gitRepo
2010-10-31 18:39:53 +00:00
let defaults = parseBackendList bs $ Git.configGet g "annex.backends" ""
backendflag <- Annex.flagGet "backend"
let l' = if not $ null backendflag
2010-10-31 18:39:53 +00:00
then (lookupBackendName bs backendflag):defaults
else defaults
2010-11-06 21:07:11 +00:00
Annex.backendsChange l'
return l'
where
2010-10-31 18:39:53 +00:00
parseBackendList bs s =
if null s
2010-10-31 18:39:53 +00:00
then bs
else map (lookupBackendName bs) $ words s
2010-10-31 22:04:34 +00:00
{- Looks up a backend in a list. May fail if unknown. -}
lookupBackendName :: [Backend Annex] -> String -> Backend Annex
2010-10-31 18:39:53 +00:00
lookupBackendName bs s =
2010-10-31 22:04:34 +00:00
case maybeLookupBackendName bs s of
Just b -> b
Nothing -> error $ "unknown backend " ++ s
maybeLookupBackendName :: [Backend Annex] -> String -> Maybe (Backend Annex)
2010-10-31 22:04:34 +00:00
maybeLookupBackendName bs s =
if 1 /= length matches
2010-10-31 22:04:34 +00:00
then Nothing
2010-11-06 21:07:11 +00:00
else Just $ head matches
2010-10-31 18:39:53 +00:00
where matches = filter (\b -> s == Internals.name b) bs
2010-10-10 19:04:18 +00:00
2010-10-13 00:04:36 +00:00
{- Attempts to store a file in one of the backends. -}
storeFileKey :: FilePath -> Maybe (Backend Annex) -> Annex (Maybe (Key, Backend Annex))
storeFileKey file trybackend = do
bs <- list
let bs' = case trybackend of
Nothing -> bs
Just backend -> backend:bs
storeFileKey' bs' file
storeFileKey' :: [Backend Annex] -> FilePath -> Annex (Maybe (Key, Backend Annex))
storeFileKey' [] _ = return Nothing
storeFileKey' (b:bs) file = do
result <- (Internals.getKey b) file
2010-11-06 21:07:11 +00:00
case result of
2010-10-10 19:21:17 +00:00
Nothing -> nextbackend
2010-10-10 19:04:18 +00:00
Just key -> do
2010-10-18 06:06:27 +00:00
stored <- (Internals.storeFileKey b) file key
2010-10-10 19:21:17 +00:00
if (not stored)
then nextbackend
2010-11-06 21:07:11 +00:00
else return $ Just (key, b)
2010-10-10 19:21:17 +00:00
where
nextbackend = storeFileKey' bs file
2010-10-10 19:04:18 +00:00
{- Attempts to retrieve an key from one of the backends, saving it to
2010-10-10 19:04:18 +00:00
- a specified location. -}
retrieveKeyFile :: Backend Annex -> Key -> FilePath -> Annex Bool
2010-10-18 06:06:27 +00:00
retrieveKeyFile backend key dest = (Internals.retrieveKeyFile backend) key dest
2010-10-10 17:47:04 +00:00
2010-10-14 18:14:19 +00:00
{- Removes a key from a backend. -}
removeKey :: Backend Annex -> Key -> Maybe Int -> Annex Bool
removeKey backend key numcopies = (Internals.removeKey backend) key numcopies
2010-10-12 20:39:10 +00:00
2010-11-07 22:22:25 +00:00
{- Checks if a key is present in its backend. -}
2010-10-14 21:37:20 +00:00
hasKey :: Key -> Annex Bool
hasKey key = do
2010-11-15 22:04:19 +00:00
backend <- keyBackend key
(Internals.hasKey backend) key
2010-10-14 21:37:20 +00:00
{- Checks a key's backend for problems. -}
fsckKey :: Backend Annex -> Key -> Maybe Int -> Annex Bool
fsckKey backend key numcopies = (Internals.fsckKey backend) key numcopies
2010-10-13 07:20:05 +00:00
{- Looks up the key and backend corresponding to an annexed file,
- by examining what the file symlinks to. -}
lookupFile :: FilePath -> Annex (Maybe (Key, Backend Annex))
2010-10-13 07:20:05 +00:00
lookupFile file = do
bs <- Annex.getState Annex.supportedBackends
2010-10-31 22:04:34 +00:00
tl <- liftIO $ try getsymlink
case tl of
2010-10-31 18:39:53 +00:00
Left _ -> return Nothing
2010-10-31 22:04:34 +00:00
Right l -> makekey bs l
where
getsymlink = do
2010-10-13 07:20:05 +00:00
l <- readSymbolicLink file
2010-10-31 22:04:34 +00:00
return $ takeFileName l
makekey bs l = do
2010-11-06 21:07:11 +00:00
case maybeLookupBackendName bs bname of
2010-10-31 22:04:34 +00:00
Nothing -> do
unless (null kname || null bname) $
warning skip
return Nothing
Just backend -> return $ Just (k, backend)
2010-10-15 00:05:04 +00:00
where
2010-10-31 22:04:34 +00:00
k = fileKey l
bname = backendName k
kname = keyName k
skip = "skipping " ++ file ++
" (unknown backend " ++ bname ++ ")"
{- Looks up the backends that should be used for each file in a list.
- That can be configured on a per-file basis in the gitattributes file.
-}
chooseBackends :: [FilePath] -> Annex [(FilePath, Maybe (Backend Annex))]
chooseBackends fs = do
g <- Annex.gitRepo
bs <- Annex.getState Annex.supportedBackends
pairs <- liftIO $ Git.checkAttr g "annex.backend" fs
return $ map (\(f,b) -> (f, maybeLookupBackendName bs b)) pairs
2010-11-15 22:04:19 +00:00
{- Returns the backend to use for a key. -}
keyBackend :: Key -> Annex (Backend Annex)
2010-11-15 22:04:19 +00:00
keyBackend key = do
bs <- Annex.getState Annex.supportedBackends
2010-11-15 22:04:19 +00:00
return $ lookupBackendName bs $ backendName key