2015-10-08 20:55:11 +00:00
|
|
|
{- git-annex numcopies types
|
2014-01-21 20:08:19 +00:00
|
|
|
-
|
2022-03-28 19:19:52 +00:00
|
|
|
- Copyright 2014-2022 Joey Hess <id@joeyh.name>
|
2014-01-21 20:08:19 +00:00
|
|
|
-
|
2019-03-13 19:48:14 +00:00
|
|
|
- Licensed under the GNU AGPL version 3 or higher.
|
2014-01-21 20:08:19 +00:00
|
|
|
-}
|
|
|
|
|
2015-10-08 21:58:32 +00:00
|
|
|
module Types.NumCopies (
|
2022-03-28 19:19:52 +00:00
|
|
|
NumCopies,
|
|
|
|
configuredNumCopies,
|
2015-10-08 21:58:32 +00:00
|
|
|
fromNumCopies,
|
2022-03-28 19:19:52 +00:00
|
|
|
MinCopies,
|
|
|
|
configuredMinCopies,
|
2021-01-06 18:11:08 +00:00
|
|
|
fromMinCopies,
|
2015-10-08 21:58:32 +00:00
|
|
|
VerifiedCopy(..),
|
|
|
|
checkVerifiedCopy,
|
|
|
|
invalidateVerifiedCopy,
|
|
|
|
strongestVerifiedCopy,
|
|
|
|
deDupVerifiedCopies,
|
|
|
|
mkVerifiedCopy,
|
|
|
|
invalidatableVerifiedCopy,
|
2015-10-09 14:30:22 +00:00
|
|
|
withVerifiedCopy,
|
2015-10-09 15:09:46 +00:00
|
|
|
isSafeDrop,
|
|
|
|
SafeDropProof,
|
|
|
|
mkSafeDropProof,
|
2015-10-09 19:48:02 +00:00
|
|
|
ContentRemovalLock(..),
|
2015-10-08 21:58:32 +00:00
|
|
|
) where
|
2014-01-21 20:08:19 +00:00
|
|
|
|
2015-10-08 20:55:11 +00:00
|
|
|
import Types.UUID
|
2015-10-09 19:48:02 +00:00
|
|
|
import Types.Key
|
2015-10-09 14:30:22 +00:00
|
|
|
import Utility.Exception (bracketIO)
|
2015-10-09 20:55:41 +00:00
|
|
|
import Utility.Monad
|
2015-10-08 20:55:11 +00:00
|
|
|
|
|
|
|
import qualified Data.Map as M
|
2015-10-08 21:58:32 +00:00
|
|
|
import Control.Concurrent.MVar
|
2015-10-09 14:30:22 +00:00
|
|
|
import Control.Monad.Catch (MonadMask)
|
|
|
|
import Control.Monad.IO.Class (MonadIO)
|
2015-10-09 15:09:46 +00:00
|
|
|
import Control.Monad
|
2015-10-08 20:55:11 +00:00
|
|
|
|
2014-01-21 20:08:19 +00:00
|
|
|
newtype NumCopies = NumCopies Int
|
2015-10-09 17:47:19 +00:00
|
|
|
deriving (Ord, Eq, Show)
|
2014-01-21 20:08:19 +00:00
|
|
|
|
2022-03-28 19:19:52 +00:00
|
|
|
-- Smart constructor; prevent configuring numcopies to 0 which would
|
|
|
|
-- cause data loss.
|
|
|
|
configuredNumCopies :: Int -> NumCopies
|
|
|
|
configuredNumCopies n
|
|
|
|
| n > 0 = NumCopies n
|
|
|
|
| otherwise = NumCopies 1
|
|
|
|
|
2014-01-21 20:08:19 +00:00
|
|
|
fromNumCopies :: NumCopies -> Int
|
|
|
|
fromNumCopies (NumCopies n) = n
|
2015-10-08 20:55:11 +00:00
|
|
|
|
2021-01-06 18:11:08 +00:00
|
|
|
newtype MinCopies = MinCopies Int
|
|
|
|
deriving (Ord, Eq, Show)
|
|
|
|
|
2022-03-28 19:19:52 +00:00
|
|
|
configuredMinCopies :: Int -> MinCopies
|
|
|
|
configuredMinCopies n
|
|
|
|
| n > 0 = MinCopies n
|
|
|
|
| otherwise = MinCopies 1
|
|
|
|
|
2021-01-06 18:11:08 +00:00
|
|
|
fromMinCopies :: MinCopies -> Int
|
|
|
|
fromMinCopies (MinCopies n) = n
|
|
|
|
|
2015-10-09 19:48:02 +00:00
|
|
|
-- Indicates that a key's content is exclusively
|
|
|
|
-- locked locally, pending removal.
|
|
|
|
newtype ContentRemovalLock = ContentRemovalLock Key
|
|
|
|
deriving (Show)
|
|
|
|
|
2015-10-08 21:58:32 +00:00
|
|
|
-- A verification that a copy of a key exists in a repository.
|
2015-10-08 20:55:11 +00:00
|
|
|
data VerifiedCopy
|
|
|
|
{- Represents a recent verification that a copy of an
|
|
|
|
- object exists in a repository with the given UUID. -}
|
2015-10-08 22:32:31 +00:00
|
|
|
= RecentlyVerifiedCopy V
|
|
|
|
{- Use when a repository cannot be accessed, but it's
|
|
|
|
- a trusted repository, which is on record as containing a key
|
|
|
|
- and is presumably not going to lose its copy. -}
|
|
|
|
| TrustedCopy V
|
2015-10-08 20:55:11 +00:00
|
|
|
{- The strongest proof of the existence of a copy.
|
|
|
|
- Until its associated action is called to unlock it,
|
|
|
|
- the copy is locked in the repository and is guaranteed
|
2015-10-09 19:48:02 +00:00
|
|
|
- not to be removed by any git-annex process. -}
|
2015-10-09 19:01:16 +00:00
|
|
|
| LockedCopy V
|
2015-10-08 21:58:32 +00:00
|
|
|
deriving (Show)
|
2015-10-08 20:55:11 +00:00
|
|
|
|
2015-10-09 14:30:22 +00:00
|
|
|
data V = V
|
|
|
|
{ _getUUID :: UUID
|
|
|
|
, _checkVerifiedCopy :: IO Bool
|
|
|
|
, _invalidateVerifiedCopy :: IO ()
|
|
|
|
}
|
|
|
|
|
|
|
|
instance Show V where
|
|
|
|
show v = show (_getUUID v)
|
|
|
|
|
2015-10-08 20:55:11 +00:00
|
|
|
instance ToUUID VerifiedCopy where
|
2015-10-08 21:58:32 +00:00
|
|
|
toUUID = _getUUID . toV
|
|
|
|
|
|
|
|
toV :: VerifiedCopy -> V
|
|
|
|
toV (TrustedCopy v) = v
|
|
|
|
toV (RecentlyVerifiedCopy v) = v
|
2015-10-09 19:01:16 +00:00
|
|
|
toV (LockedCopy v) = v
|
2015-10-08 20:55:11 +00:00
|
|
|
|
2015-10-08 21:58:32 +00:00
|
|
|
-- Checks that it's still valid.
|
|
|
|
checkVerifiedCopy :: VerifiedCopy -> IO Bool
|
|
|
|
checkVerifiedCopy = _checkVerifiedCopy . toV
|
|
|
|
|
|
|
|
invalidateVerifiedCopy :: VerifiedCopy -> IO ()
|
|
|
|
invalidateVerifiedCopy = _invalidateVerifiedCopy . toV
|
|
|
|
|
2015-10-08 20:55:11 +00:00
|
|
|
strongestVerifiedCopy :: VerifiedCopy -> VerifiedCopy -> VerifiedCopy
|
2015-10-09 19:01:16 +00:00
|
|
|
strongestVerifiedCopy a@(LockedCopy _) _ = a
|
|
|
|
strongestVerifiedCopy _ b@(LockedCopy _) = b
|
2015-10-08 22:32:31 +00:00
|
|
|
strongestVerifiedCopy a@(TrustedCopy _) _ = a
|
|
|
|
strongestVerifiedCopy _ b@(TrustedCopy _) = b
|
2015-10-08 21:58:32 +00:00
|
|
|
strongestVerifiedCopy a@(RecentlyVerifiedCopy _) _ = a
|
2015-10-08 20:55:11 +00:00
|
|
|
|
|
|
|
-- Retains stronger verifications over weaker for the same uuid.
|
|
|
|
deDupVerifiedCopies :: [VerifiedCopy] -> [VerifiedCopy]
|
|
|
|
deDupVerifiedCopies l = M.elems $
|
|
|
|
M.fromListWith strongestVerifiedCopy (zip (map toUUID l) l)
|
2015-10-08 21:58:32 +00:00
|
|
|
|
|
|
|
mkVerifiedCopy :: ToUUID u => (V -> VerifiedCopy) -> u -> VerifiedCopy
|
|
|
|
mkVerifiedCopy mk u = mk $ V (toUUID u) (return True) (return ())
|
|
|
|
|
2015-10-09 20:55:41 +00:00
|
|
|
invalidatableVerifiedCopy :: ToUUID u => (V -> VerifiedCopy) -> u -> IO Bool -> IO VerifiedCopy
|
|
|
|
invalidatableVerifiedCopy mk u check = do
|
2015-10-08 21:58:32 +00:00
|
|
|
v <- newEmptyMVar
|
|
|
|
let invalidate = do
|
|
|
|
_ <- tryPutMVar v ()
|
|
|
|
return ()
|
2015-10-09 20:55:41 +00:00
|
|
|
let check' = isEmptyMVar v <&&> check
|
|
|
|
return $ mk $ V (toUUID u) check' invalidate
|
2015-10-09 14:30:22 +00:00
|
|
|
|
|
|
|
-- Constructs a VerifiedCopy, and runs the action, ensuring that the
|
|
|
|
-- verified copy is invalidated when the action returns, or on error.
|
|
|
|
withVerifiedCopy
|
2016-01-27 13:50:33 +00:00
|
|
|
:: (MonadMask m, MonadIO m, ToUUID u)
|
2015-10-09 14:30:22 +00:00
|
|
|
=> (V -> VerifiedCopy)
|
|
|
|
-> u
|
2015-10-09 20:55:41 +00:00
|
|
|
-> IO Bool
|
2015-10-09 14:30:22 +00:00
|
|
|
-> (VerifiedCopy -> m a)
|
|
|
|
-> m a
|
2015-10-09 20:55:41 +00:00
|
|
|
withVerifiedCopy mk u check = bracketIO setup cleanup
|
2015-10-09 14:30:22 +00:00
|
|
|
where
|
2015-10-09 20:55:41 +00:00
|
|
|
setup = invalidatableVerifiedCopy mk u check
|
2015-10-09 14:30:22 +00:00
|
|
|
cleanup = invalidateVerifiedCopy
|
2015-10-09 15:09:46 +00:00
|
|
|
|
|
|
|
{- Check whether enough verification has been done of copies to allow
|
|
|
|
- dropping content safely.
|
|
|
|
-
|
2015-10-09 19:48:02 +00:00
|
|
|
- This is carefully balanced to prevent data loss when there are races
|
|
|
|
- between concurrent drops of the same content in different repos,
|
|
|
|
- without requiring impractical amounts of locking.
|
|
|
|
-
|
|
|
|
- In particular, concurrent drop races may cause the number of copies
|
2021-01-06 18:11:08 +00:00
|
|
|
- to fall below NumCopies, but it will never fall below MinCopies.
|
2015-10-09 19:48:02 +00:00
|
|
|
-}
|
2021-01-06 18:11:08 +00:00
|
|
|
isSafeDrop :: NumCopies -> MinCopies -> [VerifiedCopy] -> Maybe ContentRemovalLock -> Bool
|
2015-10-09 19:48:02 +00:00
|
|
|
{- When a ContentRemovalLock is provided, the content is being
|
|
|
|
- dropped from the local repo. That lock will prevent other git repos
|
|
|
|
- that are concurrently dropping from using the local copy as a VerifiedCopy.
|
|
|
|
- So, no additional locking is needed; all we need is verifications
|
2021-04-27 17:37:03 +00:00
|
|
|
- of any kind of enough other copies of the content. -}
|
|
|
|
isSafeDrop (NumCopies n) (MinCopies m) l (Just (ContentRemovalLock _)) =
|
|
|
|
length (deDupVerifiedCopies l) >= max n m
|
2015-10-09 19:48:02 +00:00
|
|
|
{- Dropping from a remote repo.
|
|
|
|
-
|
2021-01-06 18:11:08 +00:00
|
|
|
- To guarantee MinCopies is never violated, at least that many LockedCopy
|
|
|
|
- or TrustedCopy are required. A LockedCopy prevents races between
|
|
|
|
- concurrent drops from dropping the last copy, no matter what.
|
2015-10-09 15:09:46 +00:00
|
|
|
-
|
2021-01-06 18:11:08 +00:00
|
|
|
- The other copies required by NumCopies can be less strong verifications,
|
|
|
|
- like RecentlyVerifiedCopy. While those are subject to concurrent drop
|
|
|
|
- races, and so could be dropped all at once, causing NumCopies to be
|
|
|
|
- violated, this is the best that can be done without requiring that
|
2015-10-09 19:48:02 +00:00
|
|
|
- all special remotes support locking.
|
2015-10-09 15:09:46 +00:00
|
|
|
-}
|
2021-01-06 18:11:08 +00:00
|
|
|
isSafeDrop (NumCopies n) (MinCopies m) l Nothing
|
|
|
|
| n == 0 && m == 0 = True
|
2015-10-09 19:48:02 +00:00
|
|
|
| otherwise = and
|
|
|
|
[ length (deDupVerifiedCopies l) >= n
|
2021-01-06 18:11:08 +00:00
|
|
|
, length (filter fullVerification l) >= m
|
2015-10-09 19:48:02 +00:00
|
|
|
]
|
2015-10-09 15:09:46 +00:00
|
|
|
|
|
|
|
fullVerification :: VerifiedCopy -> Bool
|
2015-10-09 19:01:16 +00:00
|
|
|
fullVerification (LockedCopy _) = True
|
2015-10-09 15:09:46 +00:00
|
|
|
fullVerification (TrustedCopy _) = True
|
|
|
|
fullVerification (RecentlyVerifiedCopy _) = False
|
|
|
|
|
|
|
|
-- A proof that it's currently safe to drop an object.
|
2021-01-06 18:11:08 +00:00
|
|
|
data SafeDropProof = SafeDropProof NumCopies MinCopies [VerifiedCopy] (Maybe ContentRemovalLock)
|
2015-10-09 17:47:19 +00:00
|
|
|
deriving (Show)
|
2015-10-09 15:09:46 +00:00
|
|
|
|
2021-01-06 18:11:08 +00:00
|
|
|
-- Makes sure that none of the VerifiedCopies have become invalidated
|
2015-10-09 15:09:46 +00:00
|
|
|
-- before constructing proof.
|
2021-01-06 18:11:08 +00:00
|
|
|
mkSafeDropProof :: NumCopies -> MinCopies -> [VerifiedCopy] -> Maybe ContentRemovalLock -> IO (Either [VerifiedCopy] SafeDropProof)
|
|
|
|
mkSafeDropProof need mincopies have removallock = do
|
2015-10-09 15:09:46 +00:00
|
|
|
stillhave <- filterM checkVerifiedCopy have
|
2021-01-06 18:11:08 +00:00
|
|
|
return $ if isSafeDrop need mincopies stillhave removallock
|
|
|
|
then Right (SafeDropProof need mincopies stillhave removallock)
|
2015-10-09 15:09:46 +00:00
|
|
|
else Left stillhave
|