git-annex/Backend.hs

201 lines
6.3 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,
upgradableKey,
lookupFile,
2010-11-15 22:04:19 +00:00
chooseBackends,
keyBackend,
lookupBackendName,
maybeLookupBackendName
2010-10-11 21:52:46 +00:00
) where
2010-10-10 17:47:04 +00:00
2011-04-26 23:59:01 +00:00
import Control.Monad.State (liftIO, when)
2010-11-22 21:51:55 +00:00
import System.IO.Error (try)
import System.FilePath
import System.Posix.Files
import System.Directory
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
import Types.Key
import qualified Types.Backend as B
2010-11-08 19:15:21 +00:00
import Messages
import Content
import DataUnits
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
s <- getstandard
d <- Annex.getState Annex.forcebackend
handle d s
where
parseBackendList l [] = l
parseBackendList bs s = map (lookupBackendName bs) $ words s
handle Nothing s = return s
handle (Just "") s = return s
handle (Just name) s = do
bs <- Annex.getState Annex.supportedBackends
let l' = (lookupBackendName bs name):s
Annex.changeState $ \state -> state { Annex.backends = l' }
return l'
getstandard = do
bs <- Annex.getState Annex.supportedBackends
g <- Annex.gitRepo
return $ parseBackendList bs $
Git.configGet g "annex.backends" ""
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
2011-05-15 06:49:43 +00:00
lookupBackendName bs s = maybe unknown id $ maybeLookupBackendName bs s
where
unknown = 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
where matches = filter (\b -> s == B.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
2011-05-15 06:49:43 +00:00
let bs' = maybe bs (:bs) trybackend
storeFileKey' bs' file
storeFileKey' :: [Backend Annex] -> FilePath -> Annex (Maybe (Key, Backend Annex))
storeFileKey' [] _ = return Nothing
2011-05-15 06:49:43 +00:00
storeFileKey' (b:bs) file = maybe nextbackend store =<< (B.getKey b) file
where
nextbackend = storeFileKey' bs file
store key = do
stored <- (B.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: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
retrieveKeyFile backend key dest = (B.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 = (B.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
(B.hasKey backend) key
2010-10-14 21:37:20 +00:00
{- Checks a key for problems. -}
fsckKey :: Backend Annex -> Key -> Maybe FilePath -> Maybe Int -> Annex Bool
fsckKey backend key file numcopies = do
size_ok <- checkKeySize key
backend_ok <-(B.fsckKey backend) key file numcopies
return $ size_ok && backend_ok
{- Checks if a key is upgradable to a newer representation. -}
upgradableKey :: Backend Annex -> Key -> Annex Bool
upgradableKey backend key = (B.upgradableKey backend) key
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
2011-05-15 06:49:43 +00:00
makekey bs l = maybe (return Nothing) (makeret bs l) (fileKey l)
makeret bs l k =
2010-11-06 21:07:11 +00:00
case maybeLookupBackendName bs bname of
Just backend -> return $ Just (k, backend)
Nothing -> do
when (isLinkToAnnex l) $
warning skip
return Nothing
2010-10-15 00:05:04 +00:00
where
bname = keyBackendName k
2010-10-31 22:04:34 +00:00
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
forced <- Annex.getState Annex.forcebackend
if forced /= Nothing
then do
l <- list
return $ map (\f -> (f, Just $ head l)) fs
else do
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
return $ lookupBackendName bs $ keyBackendName key
{- The size of the data for a key is checked against the size encoded in
- the key's metadata, if available. -}
checkKeySize :: Key -> Annex Bool
checkKeySize key = do
g <- Annex.gitRepo
let file = gitAnnexLocation g key
present <- liftIO $ doesFileExist file
case (present, keySize key) of
(_, Nothing) -> return True
(False, _) -> return True
(True, Just size) -> do
stat <- liftIO $ getFileStatus file
let size' = fromIntegral (fileSize stat)
if size == size'
then return True
else do
dest <- moveBad key
2011-03-23 06:42:14 +00:00
warning $ "Bad file size (" ++
compareSizes storageUnits True size size' ++
2011-03-23 06:42:14 +00:00
"); moved to " ++ dest
return False