aboutsummaryrefslogtreecommitdiff
path: root/Backend.hs
blob: a427234d7cbcc0f06c9224fd654b313b2c197cee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
{- git-annex key-value storage backends
 -
 - 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
 - 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.
 - -}

module Backend (
	storeFileKey,
	retrieveKeyFile,
	removeKey,
	hasKey,
	lookupFile
) where

import Control.Monad.State
import Control.Exception
import System.Directory
import System.FilePath
import Data.String.Utils
import System.Posix.Files

import Locations
import qualified GitRepo as Git
import qualified Annex
import Utility
import Types
import qualified TypeInternals as Internals

{- List of backends in the order to try them when storing a new key. -}
backendList :: Annex [Backend]
backendList = do
	l <- Annex.backends
	if (0 < length l)
		then return l
		else do
			all <- Annex.supportedBackends
			g <- Annex.gitRepo
			let l = parseBackendList all $ Git.configGet g "annex.backends" ""
			Annex.backendsChange l
			return l
	where
		parseBackendList all s = 
			if (length s == 0)
				then all
				else map (lookupBackendName all) $ words s

{- Looks up a backend in the list of supportedBackends -}
lookupBackendName :: [Backend] -> String -> Backend
lookupBackendName all s =
	if ((length matches) /= 1)
		then error $ "unknown backend " ++ s
		else matches !! 0
	where matches = filter (\b -> s == Internals.name b) all

{- Attempts to store a file in one of the backends. -}
storeFileKey :: FilePath -> Annex (Maybe (Key, Backend))
storeFileKey file = do
	g <- Annex.gitRepo
	let relfile = Git.relative g file
	b <- backendList
	storeFileKey' b file relfile
storeFileKey' [] _ _ = return Nothing
storeFileKey' (b:bs) file relfile = do
	try <- (Internals.getKey b) relfile
	case (try) of
		Nothing -> nextbackend
		Just key -> do
			stored <- (Internals.storeFileKey b) file key
			if (not stored)
				then nextbackend
				else do
					return $ Just (key, b)
	where
		nextbackend = storeFileKey' bs file relfile

{- Attempts to retrieve an key from one of the backends, saving it to
 - a specified location. -}
retrieveKeyFile :: Backend -> Key -> FilePath -> Annex Bool
retrieveKeyFile backend key dest = (Internals.retrieveKeyFile backend) key dest

{- Removes a key from a backend. -}
removeKey :: Backend -> Key -> Annex Bool
removeKey backend key = (Internals.removeKey backend)  key

{- Checks if a backend has its key. -}
hasKey :: Key -> Annex Bool
hasKey key = do
	all <- Annex.supportedBackends
	(Internals.hasKey (lookupBackendName all $ backendName key)) key

{- Looks up the key and backend corresponding to an annexed file,
 - by examining what the file symlinks to. -}
lookupFile :: FilePath -> Annex (Maybe (Key, Backend))
lookupFile file = do
	all <- Annex.supportedBackends
	result <- liftIO $ (try (lookup all)::IO (Either SomeException (Maybe (Key, Backend))))
	case (result) of
		Left err -> return Nothing
		Right succ -> return succ
	where 
		lookup all = do
			l <- readSymbolicLink file
			return $ Just $ pair all $ takeFileName l
		pair all file = (k, b)
			where
				k = fileKey file
				b = lookupBackendName all $ backendName k