Skip to content
Snippets Groups Projects
Backend.hs 3.56 KiB
Newer Older
{- git-annex key/value backends
Joey Hess's avatar
Joey Hess committed
 -
 - Copyright 2010 Joey Hess <joey@kitenet.net>
 -
 - Licensed under the GNU GPL version 3 or higher.
 -}
Joey Hess's avatar
Joey Hess committed

Joey Hess's avatar
Joey Hess committed
module Backend (
Joey Hess's avatar
Joey Hess committed
	BackendFile,
	orderedList,
	genKey,
	lookupFile,
Joey Hess's avatar
Joey Hess committed
	chooseBackends,
Joey Hess's avatar
Joey Hess committed
	lookupBackendName,
	maybeLookupBackendName
Joey Hess's avatar
Joey Hess committed
) where
Joey Hess's avatar
Joey Hess committed

Joey Hess's avatar
Joey Hess committed
import System.IO.Error (try)
import System.Posix.Files
Joey Hess's avatar
Joey Hess committed

Joey Hess's avatar
Joey Hess committed
import Common.Annex
import qualified Git.Config
import qualified Git.CheckAttr
import qualified Annex
import Types.Key
import qualified Types.Backend as B

-- When adding a new backend, import it here and add it to the list.
import qualified Backend.SHA
Joey Hess's avatar
Joey Hess committed
import qualified Backend.WORM
Joey Hess's avatar
Joey Hess committed
list :: [Backend]
Joey Hess's avatar
Joey Hess committed
list = Backend.SHA.backends ++ Backend.WORM.backends ++ Backend.URL.backends
Joey Hess's avatar
Joey Hess committed

{- List of backends in the order to try them when storing a new key. -}
Joey Hess's avatar
Joey Hess committed
orderedList :: Annex [Backend]
orderedList = do
	l <- Annex.getState Annex.backends -- list is cached here
Joey Hess's avatar
Joey Hess committed
	if not $ null l
Joey Hess's avatar
Joey Hess committed
		then return l
Joey Hess's avatar
Joey Hess committed
		else handle =<< Annex.getState Annex.forcebackend
Joey Hess's avatar
Joey Hess committed
	where
Joey Hess's avatar
Joey Hess committed
		handle Nothing = standard
		handle (Just "") = standard
		handle (Just name) = do
			l' <- (lookupBackendName name :) <$> standard
			Annex.changeState $ \s -> s { Annex.backends = l' }
		standard = fromRepo $ parseBackendList . Git.Config.get "annex.backends" ""
Joey Hess's avatar
Joey Hess committed
		parseBackendList [] = list
		parseBackendList s = map lookupBackendName $ words s
{- Generates a key for a file, trying each backend in turn until one
 - accepts it. -}
Joey Hess's avatar
Joey Hess committed
genKey :: FilePath -> Maybe Backend -> Annex (Maybe (Key, Backend))
genKey file trybackend = do
	bs <- orderedList
Joey Hess's avatar
Joey Hess committed
	let bs' = maybe bs (: bs) trybackend
	genKey' bs' file
Joey Hess's avatar
Joey Hess committed
genKey' :: [Backend] -> FilePath -> Annex (Maybe (Key, Backend))
genKey' [] _ = return Nothing
genKey' (b:bs) file = do
	r <- (B.getKey b) file
	case r of
		Nothing -> genKey' bs file
		Just k -> return $ Just (makesane k, b)
	where
		-- keyNames should not contain newline characters.
		makesane k = k { keyName = map fixbadchar (keyName k) }
		fixbadchar c
			| c == '\n' = '_'
			| otherwise = c
Joey Hess's avatar
Joey Hess committed
{- Looks up the key and backend corresponding to an annexed file,
 - by examining what the file symlinks to. -}
Joey Hess's avatar
Joey Hess committed
lookupFile :: FilePath -> Annex (Maybe (Key, Backend))
Joey Hess's avatar
Joey Hess committed
lookupFile file = do
	tl <- liftIO $ try getsymlink
	case tl of
Joey Hess's avatar
Joey Hess committed
		Left _ -> return Nothing
		Right l -> makekey l
	where
		getsymlink = takeFileName <$> readSymbolicLink file
		makekey l = maybe (return Nothing) (makeret l) (fileKey l)
Joey Hess's avatar
Joey Hess committed
		makeret l k = let bname = keyBackendName k in
			case maybeLookupBackendName bname of
Joey Hess's avatar
Joey Hess committed
				Just backend -> return $ Just (k, backend)
				Nothing -> do
					when (isLinkToAnnex l) $ warning $
						"skipping " ++ file ++
						" (unknown backend " ++
						bname ++ ")"
					return Nothing
Joey Hess's avatar
Joey Hess committed
type BackendFile = (Maybe Backend, FilePath)
Joey Hess's avatar
Joey Hess committed

{- 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.
 -}
Joey Hess's avatar
Joey Hess committed
chooseBackends :: [FilePath] -> Annex [BackendFile]
Joey Hess's avatar
Joey Hess committed
chooseBackends fs = Annex.getState Annex.forcebackend >>= go
	where
		go Nothing = do
			pairs <- inRepo $ Git.CheckAttr.lookup "annex.backend" fs
Joey Hess's avatar
Joey Hess committed
			return $ map (\(f,b) -> (maybeLookupBackendName b, f)) pairs
		go (Just _) = do
			l <- orderedList
Joey Hess's avatar
Joey Hess committed
			return $ map (\f -> (Just $ Prelude.head l, f)) fs
{- Looks up a backend by name. May fail if unknown. -}
Joey Hess's avatar
Joey Hess committed
lookupBackendName :: String -> Backend
Joey Hess's avatar
Joey Hess committed
lookupBackendName s = fromMaybe unknown $ maybeLookupBackendName s
	where
		unknown = error $ "unknown backend " ++ s
Joey Hess's avatar
Joey Hess committed
maybeLookupBackendName :: String -> Maybe Backend
Joey Hess's avatar
Joey Hess committed
maybeLookupBackendName s = headMaybe matches
Joey Hess's avatar
Joey Hess committed
	where
		matches = filter (\b -> s == B.name b) list