git-annex/Annex/NumCopies.hs

197 lines
6.3 KiB
Haskell
Raw Normal View History

2015-04-30 18:02:56 +00:00
{- git-annex numcopies configuration and checking
2014-01-21 22:08:56 +00:00
-
2015-04-30 18:02:56 +00:00
- Copyright 2014-2015 Joey Hess <id@joeyh.name>
2014-01-21 22:08:56 +00:00
-
- Licensed under the GNU GPL version 3 or higher.
-}
{-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable #-}
2015-04-30 18:02:56 +00:00
module Annex.NumCopies (
2014-01-21 22:08:56 +00:00
module Types.NumCopies,
module Logs.NumCopies,
getFileNumCopies,
getGlobalFileNumCopies,
getNumCopies,
deprecatedNumCopies,
defaultNumCopies,
numCopiesCheck,
numCopiesCheck',
2015-10-09 15:09:46 +00:00
verifyEnoughCopiesToDrop,
2015-04-30 18:02:56 +00:00
knownCopies,
2014-01-21 22:08:56 +00:00
) where
import Common.Annex
import qualified Annex
import Types.NumCopies
import Logs.NumCopies
import Logs.Trust
import Annex.CheckAttr
import qualified Remote
2015-10-09 16:36:04 +00:00
import qualified Types.Remote as Remote
2015-04-30 18:02:56 +00:00
import Annex.UUID
import Annex.Content
2014-01-21 22:08:56 +00:00
import Control.Exception
import qualified Control.Monad.Catch as M
import Data.Typeable
2014-01-21 22:08:56 +00:00
defaultNumCopies :: NumCopies
defaultNumCopies = NumCopies 1
fromSources :: [Annex (Maybe NumCopies)] -> Annex NumCopies
fromSources = fromMaybe defaultNumCopies <$$> getM id
{- The git config annex.numcopies is deprecated. -}
deprecatedNumCopies :: Annex (Maybe NumCopies)
deprecatedNumCopies = annexNumCopies <$> Annex.getGitConfig
{- Value forced on the command line by --numcopies. -}
getForcedNumCopies :: Annex (Maybe NumCopies)
getForcedNumCopies = Annex.getState Annex.forcenumcopies
{- Numcopies value from any of the non-.gitattributes configuration
- sources. -}
getNumCopies :: Annex NumCopies
getNumCopies = fromSources
[ getForcedNumCopies
, getGlobalNumCopies
, deprecatedNumCopies
]
{- Numcopies value for a file, from any configuration source, including the
- deprecated git config. -}
getFileNumCopies :: FilePath -> Annex NumCopies
getFileNumCopies f = fromSources
[ getForcedNumCopies
, getFileNumCopies' f
, deprecatedNumCopies
]
{- This is the globally visible numcopies value for a file. So it does
- not include local configuration in the git config or command line
- options. -}
getGlobalFileNumCopies :: FilePath -> Annex NumCopies
getGlobalFileNumCopies f = fromSources
[ getFileNumCopies' f
]
getFileNumCopies' :: FilePath -> Annex (Maybe NumCopies)
getFileNumCopies' file = maybe getGlobalNumCopies (return . Just) =<< getattr
where
getattr = (NumCopies <$$> readish)
<$> checkAttr "annex.numcopies" file
{- Checks if numcopies are satisfied for a file by running a comparison
- between the number of (not untrusted) copies that are
- belived to exist, and the configured value.
-
- This is good enough for everything except dropping the file, which
- requires active verification of the copies.
-}
2014-01-21 22:08:56 +00:00
numCopiesCheck :: FilePath -> Key -> (Int -> Int -> v) -> Annex v
numCopiesCheck file key vs = do
have <- trustExclude UnTrusted =<< Remote.keyLocations key
numCopiesCheck' file vs have
numCopiesCheck' :: FilePath -> (Int -> Int -> v) -> [UUID] -> Annex v
numCopiesCheck' file vs have = do
NumCopies needed <- getFileNumCopies file
2014-01-21 22:08:56 +00:00
return $ length have `vs` needed
2015-04-30 18:02:56 +00:00
{- Verifies that enough copies of a key exist amoung the listed remotes,
2015-10-09 15:09:46 +00:00
- running an action with a proof if so, and printing an informative
- message if not.
2015-04-30 18:02:56 +00:00
-}
2015-10-09 15:09:46 +00:00
verifyEnoughCopiesToDrop
2015-04-30 18:02:56 +00:00
:: String -- message to print when there are no known locations
-> Key
-> NumCopies
-> [UUID] -- repos to skip considering (generally untrusted remotes)
-> [VerifiedCopy] -- copies already verified to exist
2015-10-09 15:09:46 +00:00
-> [Remote] -- remotes to check to see if they have copies
-> (SafeDropProof -> Annex a) -- action to perform to drop
-> Annex a -- action to perform when unable to drop
-> Annex a
verifyEnoughCopiesToDrop nolocmsg key need skip preverified tocheck dropaction nodropaction =
helper [] [] preverified (nub tocheck)
2015-04-30 18:02:56 +00:00
where
2015-10-09 15:09:46 +00:00
helper bad missing have [] = do
p <- liftIO $ mkSafeDropProof need have
case p of
Right proof -> dropaction proof
Left stillhave -> do
notEnoughCopies key need stillhave (skip++missing) bad nolocmsg
nodropaction
2015-04-30 18:02:56 +00:00
helper bad missing have (r:rs)
2015-10-09 15:09:46 +00:00
| isSafeDrop need have = do
p <- liftIO $ mkSafeDropProof need have
case p of
Right proof -> dropaction proof
Left stillhave -> helper bad missing stillhave (r:rs)
2015-10-09 16:36:04 +00:00
| otherwise = case Remote.lockContent r of
Just lockcontent -> do
-- The remote's lockContent will throw
-- an exception if it is unable to lock,
-- in which case the fallback should be
-- run.
--
-- On the other hand, the callback passed
-- to the lockContent could itself throw an
-- exception (ie, the eventual drop
-- action fails), and in this case we don't
-- want to use the fallback since part
-- of the drop action may have already been
-- performed.
--
-- Differentiate between these two sorts
-- of exceptions by using DropException.
let a = lockcontent key $ \vc ->
helper bad missing (vc : have) rs
`catchNonAsync` (throw . DropException)
a `M.catches`
[ M.Handler (\ (e :: AsyncException) -> throwM e)
, M.Handler (\ (DropException e') -> throwM e')
, M.Handler (\ (_e :: SomeException) -> fallback)
]
2015-10-09 16:36:04 +00:00
Nothing -> fallback
where
fallback = do
haskey <- Remote.hasKey r key
case haskey of
Right True -> helper bad missing (mkVerifiedCopy RecentlyVerifiedCopy r : have) rs
Left _ -> helper (r:bad) missing have rs
Right False -> helper bad (Remote.uuid r:missing) have rs
2015-04-30 18:02:56 +00:00
data DropException = DropException SomeException
deriving (Typeable, Show)
instance Exception DropException
notEnoughCopies :: Key -> NumCopies -> [VerifiedCopy] -> [UUID] -> [Remote] -> String -> Annex ()
2015-04-30 18:02:56 +00:00
notEnoughCopies key need have skip bad nolocmsg = do
showNote "unsafe"
showLongNote $
"Could only verify the existence of " ++
show (length have) ++ " out of " ++ show (fromNumCopies need) ++
" necessary copies"
Remote.showTriedRemotes bad
Remote.showLocations True key (map toUUID have++skip) nolocmsg
2015-04-30 18:02:56 +00:00
{- Cost ordered lists of remotes that the location log indicates
- may have a key.
-
- Also returns a list of UUIDs that are trusted to have the key
- (some may not have configured remotes). If the current repository
- currently has the key, and is not untrusted, it is included in this list.
-}
knownCopies :: Key -> Annex ([Remote], [UUID])
knownCopies key = do
(remotes, trusteduuids) <- Remote.keyPossibilitiesTrusted key
u <- getUUID
trusteduuids' <- ifM (inAnnex key <&&> (<= SemiTrusted) <$> lookupTrust u)
( pure (u:trusteduuids)
2015-04-30 18:02:56 +00:00
, pure trusteduuids
)
return (remotes, trusteduuids')