mask special remote working

Still needs some handling of edge cases, cycles, etc.
This commit is contained in:
Joey Hess 2025-04-11 11:17:24 -04:00
parent d416107c7d
commit 90c502e675
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
2 changed files with 76 additions and 36 deletions

View file

@ -1,5 +1,6 @@
git-annex (10.20250321) UNRELEASED; urgency=medium git-annex (10.20250321) UNRELEASED; urgency=medium
* Added the mask special remote.
* updatecluster, updateproxy: When a remote that has no annex-uuid is * updatecluster, updateproxy: When a remote that has no annex-uuid is
configured as annex-cluster-node, warn and avoid writing bad data to configured as annex-cluster-node, warn and avoid writing bad data to
the git-annex branch. the git-annex branch.

View file

@ -23,8 +23,10 @@ import Annex.UUID
import Types.ProposedAccepted import Types.ProposedAccepted
import Annex.SpecialRemote.Config import Annex.SpecialRemote.Config
import Logs.UUID import Logs.UUID
import Utility.Metered
import qualified Remote.Git import qualified Remote.Git
import Control.Concurrent.STM
import qualified Data.Map as M import qualified Data.Map as M
remote :: RemoteType remote :: RemoteType
@ -44,13 +46,9 @@ remote = specialRemoteType $ RemoteType
gen :: Git.Repo -> UUID -> RemoteConfig -> RemoteGitConfig -> RemoteStateHandle -> Annex (Maybe Remote) gen :: Git.Repo -> UUID -> RemoteConfig -> RemoteGitConfig -> RemoteStateHandle -> Annex (Maybe Remote)
gen r u rc gc rs = do gen r u rc gc rs = do
maskedremote <- getMaskedRemote rc gc maskedremote <- mkMaskedRemote rc gc
let inherited d f = case maskedremote of
Right mr -> f mr
Left _ -> d
c <- parsedRemoteConfig remote rc c <- parsedRemoteConfig remote rc
cst <- remoteCost gc c $ encryptedRemoteCostAdj + cst <- remoteCost gc c $ encryptedRemoteCostAdj + semiExpensiveRemoteCost
inherited semiExpensiveRemoteCost cost
let this = Remote let this = Remote
{ uuid = u { uuid = u
, cost = cst , cost = cst
@ -59,11 +57,11 @@ gen r u rc gc rs = do
, retrieveKeyFile = retrieveKeyFileDummy , retrieveKeyFile = retrieveKeyFileDummy
, retrieveKeyFileInOrder = pure True , retrieveKeyFileInOrder = pure True
, retrieveKeyFileCheap = Nothing , retrieveKeyFileCheap = Nothing
, retrievalSecurityPolicy = inherited RetrievalVerifiableKeysSecure retrievalSecurityPolicy , retrievalSecurityPolicy = RetrievalVerifiableKeysSecure
, removeKey = removeKeyDummy , removeKey = removeKeyDummy
, lockContent = Nothing , lockContent = Nothing
, checkPresent = checkPresentDummy , checkPresent = checkPresentDummy
, checkPresentCheap = inherited False checkPresentCheap , checkPresentCheap = False
, exportActions = exportUnsupported , exportActions = exportUnsupported
, importActions = importUnsupported , importActions = importUnsupported
, whereisKey = Nothing , whereisKey = Nothing
@ -74,12 +72,12 @@ gen r u rc gc rs = do
, gitconfig = gc , gitconfig = gc
, localpath = Nothing , localpath = Nothing
, remotetype = remote , remotetype = remote
, availability = inherited (pure Unavailable) availability , availability = pure LocallyAvailable
, readonly = inherited False readonly , readonly = False
, appendonly = inherited False appendonly , appendonly = False
, untrustworthy = inherited False untrustworthy , untrustworthy = False
, mkUnavailable = return Nothing , mkUnavailable = return Nothing
, getInfo = inherited (pure []) getInfo , getInfo = getInfo =<< getMaskedRemote maskedremote
, claimUrl = Nothing , claimUrl = Nothing
, checkUrl = Nothing , checkUrl = Nothing
, remoteStateHandle = rs , remoteStateHandle = rs
@ -149,27 +147,44 @@ maskSetup setupstage mu _ c gc = do
giveup "It's not secure to use encryption=shared with a git remote." giveup "It's not secure to use encryption=shared with a git remote."
_ -> noop _ -> noop
getMaskedRemote :: RemoteConfig -> RemoteGitConfig -> Annex (Either UUID Remote) newtype MaskedRemote = MaskedRemote { getMaskedRemote :: Annex Remote }
getMaskedRemote c gc = case remoteAnnexMask gc of
-- findMaskedRemote won't work until the remote list has been populated,
-- so has to be done on the fly rather than at generation time.
-- This caches it for speed.
mkMaskedRemote :: RemoteConfig -> RemoteGitConfig -> Annex MaskedRemote
mkMaskedRemote c gc = do
v <- liftIO $ newTMVarIO Nothing
return $ MaskedRemote $
liftIO (atomically (takeTMVar v)) >>= \case
Just maskedremote -> return maskedremote
Nothing -> do
maskedremote <- findMaskedRemote c gc
liftIO $ atomically $ putTMVar v (Just maskedremote)
return maskedremote
-- XXX prevent using self as masked remote, and prevent using mask special
-- remote, to avoid cycles
findMaskedRemote :: RemoteConfig -> RemoteGitConfig -> Annex Remote
findMaskedRemote c gc = case remoteAnnexMask gc of
-- This remote was autoenabled, so use any remote with the -- This remote was autoenabled, so use any remote with the
-- uuid of the masked remote, so that it can also be autoenabled. -- uuid of the masked remote, so that it can also be autoenabled.
Just "true" -> Just "true" ->
case getmaskedremoteuuid of case getmaskedremoteuuid of
Just maskremoteuuid -> Just maskremoteuuid ->
selectremote (\r -> uuid r == maskremoteuuid) selectremote maskremoteuuid
maskremoteuuid (\r -> uuid r == maskremoteuuid)
Nothing -> return (Left NoUUID) Nothing -> missingMaskedRemote NoUUID
Just maskremotename -> Just maskremotename ->
selectremote (\r -> name r == maskremotename) $ selectremote NoUUID (\r -> name r == maskremotename)
(fromMaybe NoUUID getmaskedremoteuuid) Nothing -> missingMaskedRemote NoUUID
Nothing -> return (Left NoUUID)
where where
getmaskedremoteuuid = toUUID . fromProposedAccepted <$> M.lookup remoteField c getmaskedremoteuuid = toUUID . fromProposedAccepted <$> M.lookup remoteField c
selectremote f fallback = do selectremote u f = do
remotelist <- Annex.getState Annex.remotes remotelist <- Annex.getState Annex.remotes
case filter f remotelist of case filter f remotelist of
(r:_) -> return (Right r) (r:_) -> return r
[] -> return (Left fallback) [] -> missingMaskedRemote u
missingMaskedRemote :: UUID -> Annex a missingMaskedRemote :: UUID -> Annex a
missingMaskedRemote maskremoteuuid = do missingMaskedRemote maskremoteuuid = do
@ -182,21 +197,45 @@ missingMaskedRemote maskremoteuuid = do
, " " ++ fromUUID maskremoteuuid ++ " -- " ++ desc , " " ++ fromUUID maskremoteuuid ++ " -- " ++ desc
] ]
store :: Either UUID Remote -> Storer store :: MaskedRemote -> Storer
store (Right maskedremote) k src p = undefined store maskedremote k src p = do
store (Left maskedremoteuuid) _ _ _ = missingMaskedRemote maskedremoteuuid r <- getMaskedRemote maskedremote
storeMasked r k src p
retrieve :: Either UUID Remote -> Retriever storeMasked :: Remote -> Storer
retrieve (Right maskedremote) k p dest iv callback = undefined storeMasked maskedremote =
retrieve (Left maskedremoteuuid) _ _ _ _ _ = missingMaskedRemote maskedremoteuuid fileStorer $ \k f p -> storeKey maskedremote k af (Just f) p
where
af = AssociatedFile Nothing
remove :: Either UUID Remote -> Remover retrieve :: MaskedRemote -> Retriever
remove (Right maskedremote) proof k = undefined retrieve maskedremote k p dest iv callback = do
remove (Left maskedremoteuuid) _ _ = missingMaskedRemote maskedremoteuuid r <- getMaskedRemote maskedremote
fileRetriever (retrieveMasked r) k p dest iv callback
checkKey :: Either UUID Remote -> CheckPresent retrieveMasked :: Remote -> OsPath -> Key -> MeterUpdate -> Annex ()
checkKey (Right maskedremote) k = undefined retrieveMasked maskedremote dest k p =
checkKey (Left maskedremoteuuid) _ = missingMaskedRemote maskedremoteuuid -- The masked remote does not need to verify, because fileRetriever
-- does its own verification.
void $ retrieveKeyFile maskedremote k af dest p NoVerify
where
af = AssociatedFile Nothing
remove :: MaskedRemote -> Remover
remove maskedremote proof k = do
r <- getMaskedRemote maskedremote
removeMasked r proof k
removeMasked :: Remote -> Remover
removeMasked maskedremote = removeKey maskedremote
checkKey :: MaskedRemote -> CheckPresent
checkKey maskedremote k = do
r <- getMaskedRemote maskedremote
checkKeyMasked r k
checkKeyMasked :: Remote -> CheckPresent
checkKeyMasked maskedremote = checkPresent maskedremote
remoteField :: RemoteConfigField remoteField :: RemoteConfigField
remoteField = Accepted "remote" remoteField = Accepted "remote"