2013-09-07 22:38:00 +00:00
|
|
|
|
{- git remotes encrypted using git-remote-gcrypt
|
|
|
|
|
-
|
|
|
|
|
- Copyright 2013 Joey Hess <joey@kitenet.net>
|
|
|
|
|
-
|
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
|
-}
|
|
|
|
|
|
2013-09-24 21:25:47 +00:00
|
|
|
|
module Remote.GCrypt (
|
|
|
|
|
remote,
|
|
|
|
|
gen,
|
|
|
|
|
getGCryptUUID,
|
2013-10-01 21:20:51 +00:00
|
|
|
|
coreGCryptId,
|
|
|
|
|
setupRepo
|
2013-09-24 21:25:47 +00:00
|
|
|
|
) where
|
2013-09-07 22:38:00 +00:00
|
|
|
|
|
|
|
|
|
import qualified Data.Map as M
|
2013-09-08 17:00:48 +00:00
|
|
|
|
import qualified Data.ByteString.Lazy as L
|
2013-09-27 23:52:36 +00:00
|
|
|
|
import Control.Exception.Extensible
|
2013-09-07 22:38:00 +00:00
|
|
|
|
|
|
|
|
|
import Common.Annex
|
|
|
|
|
import Types.Remote
|
|
|
|
|
import Types.GitConfig
|
|
|
|
|
import Types.Crypto
|
2014-02-11 18:06:50 +00:00
|
|
|
|
import Types.Creds
|
2013-09-07 22:38:00 +00:00
|
|
|
|
import qualified Git
|
|
|
|
|
import qualified Git.Command
|
|
|
|
|
import qualified Git.Config
|
|
|
|
|
import qualified Git.GCrypt
|
2013-09-12 19:54:35 +00:00
|
|
|
|
import qualified Git.Construct
|
2013-09-07 22:38:00 +00:00
|
|
|
|
import qualified Git.Types as Git ()
|
|
|
|
|
import qualified Annex.Branch
|
|
|
|
|
import qualified Annex.Content
|
|
|
|
|
import Config
|
|
|
|
|
import Config.Cost
|
|
|
|
|
import Remote.Helper.Git
|
|
|
|
|
import Remote.Helper.Encryptable
|
2013-09-08 19:19:14 +00:00
|
|
|
|
import Remote.Helper.Special
|
2013-09-24 21:25:47 +00:00
|
|
|
|
import Remote.Helper.Messages
|
|
|
|
|
import qualified Remote.Helper.Ssh as Ssh
|
2013-09-07 22:38:00 +00:00
|
|
|
|
import Utility.Metered
|
|
|
|
|
import Crypto
|
|
|
|
|
import Annex.UUID
|
2013-09-08 18:54:28 +00:00
|
|
|
|
import Annex.Ssh
|
|
|
|
|
import qualified Remote.Rsync
|
|
|
|
|
import Utility.Rsync
|
2013-09-27 20:21:56 +00:00
|
|
|
|
import Utility.Tmp
|
2013-09-12 19:54:35 +00:00
|
|
|
|
import Logs.Remote
|
2013-09-24 21:25:47 +00:00
|
|
|
|
import Logs.Transfer
|
2013-09-17 20:06:29 +00:00
|
|
|
|
import Utility.Gpg
|
2013-09-24 21:25:47 +00:00
|
|
|
|
import Annex.Content
|
2013-09-07 22:38:00 +00:00
|
|
|
|
|
|
|
|
|
remote :: RemoteType
|
|
|
|
|
remote = RemoteType {
|
|
|
|
|
typename = "gcrypt",
|
|
|
|
|
-- Remote.Git takes care of enumerating gcrypt remotes too,
|
|
|
|
|
-- and will call our gen on them.
|
|
|
|
|
enumerate = return [],
|
|
|
|
|
generate = gen,
|
|
|
|
|
setup = gCryptSetup
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-12 19:54:35 +00:00
|
|
|
|
gen :: Git.Repo -> UUID -> RemoteConfig -> RemoteGitConfig -> Annex (Maybe Remote)
|
2013-09-07 22:38:00 +00:00
|
|
|
|
gen gcryptr u c gc = do
|
|
|
|
|
g <- gitRepo
|
|
|
|
|
-- get underlying git repo with real path, not gcrypt path
|
2013-09-19 16:53:24 +00:00
|
|
|
|
r <- liftIO $ Git.GCrypt.encryptedRemote g gcryptr
|
2013-09-07 22:38:00 +00:00
|
|
|
|
let r' = r { Git.remoteName = Git.remoteName gcryptr }
|
2013-09-27 20:21:56 +00:00
|
|
|
|
-- doublecheck that cache matches underlying repo's gcrypt-id
|
|
|
|
|
-- (which might not be set), only for local repos
|
|
|
|
|
(mgcryptid, r'') <- getGCryptId True r'
|
2013-09-18 19:30:53 +00:00
|
|
|
|
case (mgcryptid, Git.GCrypt.remoteRepoId g (Git.remoteName gcryptr)) of
|
2013-09-12 19:54:35 +00:00
|
|
|
|
(Just gcryptid, Just cachedgcryptid)
|
|
|
|
|
| gcryptid /= cachedgcryptid -> resetup gcryptid r''
|
|
|
|
|
_ -> gen' r'' u c gc
|
|
|
|
|
where
|
|
|
|
|
-- A different drive may have been mounted, making a different
|
|
|
|
|
-- gcrypt remote available. So need to set the cached
|
|
|
|
|
-- gcrypt-id and annex-uuid of the remote to match the remote
|
|
|
|
|
-- that is now available. Also need to set the gcrypt particiants
|
|
|
|
|
-- correctly.
|
|
|
|
|
resetup gcryptid r = do
|
|
|
|
|
let u' = genUUIDInNameSpace gCryptNameSpace gcryptid
|
2013-09-26 03:19:01 +00:00
|
|
|
|
v <- M.lookup u' <$> readRemoteLog
|
2013-09-12 19:54:35 +00:00
|
|
|
|
case (Git.remoteName gcryptr, v) of
|
|
|
|
|
(Just remotename, Just c') -> do
|
|
|
|
|
setGcryptEncryption c' remotename
|
|
|
|
|
setConfig (remoteConfig gcryptr "uuid") (fromUUID u')
|
|
|
|
|
setConfig (ConfigKey $ Git.GCrypt.remoteConfigKey "gcrypt-id" remotename) gcryptid
|
|
|
|
|
gen' r u' c' gc
|
|
|
|
|
_ -> do
|
|
|
|
|
warning $ "not using unknown gcrypt repository pointed to by remote " ++ Git.repoDescribe r
|
|
|
|
|
return Nothing
|
2013-09-07 22:38:00 +00:00
|
|
|
|
|
2013-09-12 19:54:35 +00:00
|
|
|
|
gen' :: Git.Repo -> UUID -> RemoteConfig -> RemoteGitConfig -> Annex (Maybe Remote)
|
2013-09-08 18:54:28 +00:00
|
|
|
|
gen' r u c gc = do
|
|
|
|
|
cst <- remoteCost gc $
|
|
|
|
|
if repoCheap r then nearlyCheapRemoteCost else expensiveRemoteCost
|
2013-09-24 21:25:47 +00:00
|
|
|
|
(rsynctransport, rsyncurl) <- rsyncTransportToObjects r
|
2013-09-08 18:54:28 +00:00
|
|
|
|
let rsyncopts = Remote.Rsync.genRsyncOpts c gc rsynctransport rsyncurl
|
|
|
|
|
let this = Remote
|
|
|
|
|
{ uuid = u
|
|
|
|
|
, cost = cst
|
|
|
|
|
, name = Git.repoDescribe r
|
|
|
|
|
, storeKey = \_ _ _ -> noCrypto
|
|
|
|
|
, retrieveKeyFile = \_ _ _ _ -> noCrypto
|
|
|
|
|
, retrieveKeyFileCheap = \_ _ -> return False
|
|
|
|
|
, removeKey = remove this rsyncopts
|
|
|
|
|
, hasKey = checkPresent this rsyncopts
|
|
|
|
|
, hasKeyCheap = repoCheap r
|
|
|
|
|
, whereisKey = Nothing
|
2013-10-11 20:03:18 +00:00
|
|
|
|
, remoteFsck = Nothing
|
2013-10-27 19:38:59 +00:00
|
|
|
|
, repairRepo = Nothing
|
2013-11-03 00:10:54 +00:00
|
|
|
|
, config = c
|
2013-09-08 18:54:28 +00:00
|
|
|
|
, localpath = localpathCalc r
|
|
|
|
|
, repo = r
|
|
|
|
|
, gitconfig = gc { remoteGitConfig = Just $ extractGitConfig r }
|
|
|
|
|
, readonly = Git.repoIsHttp r
|
2014-01-13 18:41:10 +00:00
|
|
|
|
, availability = availabilityCalc r
|
2013-09-08 18:54:28 +00:00
|
|
|
|
, remotetype = remote
|
|
|
|
|
}
|
2013-09-12 19:54:35 +00:00
|
|
|
|
return $ Just $ encryptableRemote c
|
2013-09-08 18:54:28 +00:00
|
|
|
|
(store this rsyncopts)
|
|
|
|
|
(retrieve this rsyncopts)
|
2013-09-07 22:38:00 +00:00
|
|
|
|
this
|
2013-09-08 18:54:28 +00:00
|
|
|
|
|
2013-09-24 21:25:47 +00:00
|
|
|
|
rsyncTransportToObjects :: Git.Repo -> Annex ([CommandParam], String)
|
|
|
|
|
rsyncTransportToObjects r = do
|
|
|
|
|
(rsynctransport, rsyncurl, _) <- rsyncTransport r
|
|
|
|
|
return (rsynctransport, rsyncurl ++ "/annex/objects")
|
|
|
|
|
|
|
|
|
|
rsyncTransport :: Git.Repo -> Annex ([CommandParam], String, AccessMethod)
|
2013-09-08 18:54:28 +00:00
|
|
|
|
rsyncTransport r
|
|
|
|
|
| "ssh://" `isPrefixOf` loc = sshtransport $ break (== '/') $ drop (length "ssh://") loc
|
|
|
|
|
| "//:" `isInfixOf` loc = othertransport
|
|
|
|
|
| ":" `isInfixOf` loc = sshtransport $ separate (== ':') loc
|
|
|
|
|
| otherwise = othertransport
|
|
|
|
|
where
|
|
|
|
|
loc = Git.repoLocation r
|
|
|
|
|
sshtransport (host, path) = do
|
2013-09-26 19:02:27 +00:00
|
|
|
|
let rsyncpath = if "/~/" `isPrefixOf` path
|
|
|
|
|
then drop 3 path
|
|
|
|
|
else path
|
2013-09-08 18:54:28 +00:00
|
|
|
|
opts <- sshCachingOptions (host, Nothing) []
|
2013-09-26 19:02:27 +00:00
|
|
|
|
return (rsyncShell $ Param "ssh" : opts, host ++ ":" ++ rsyncpath, AccessShell)
|
2013-09-24 21:25:47 +00:00
|
|
|
|
othertransport = return ([], loc, AccessDirect)
|
2013-09-07 22:38:00 +00:00
|
|
|
|
|
|
|
|
|
noCrypto :: Annex a
|
|
|
|
|
noCrypto = error "cannot use gcrypt remote without encryption enabled"
|
|
|
|
|
|
2013-09-08 17:00:48 +00:00
|
|
|
|
unsupportedUrl :: Annex a
|
|
|
|
|
unsupportedUrl = error "using non-ssh remote repo url with gcrypt is not supported"
|
|
|
|
|
|
2014-02-11 18:06:50 +00:00
|
|
|
|
gCryptSetup :: Maybe UUID -> Maybe CredPair -> RemoteConfig -> Annex (RemoteConfig, UUID)
|
|
|
|
|
gCryptSetup mu _ c = go $ M.lookup "gitrepo" c
|
2013-09-07 22:38:00 +00:00
|
|
|
|
where
|
|
|
|
|
remotename = fromJust (M.lookup "name" c)
|
|
|
|
|
go Nothing = error "Specify gitrepo="
|
|
|
|
|
go (Just gitrepo) = do
|
|
|
|
|
c' <- encryptionSetup c
|
|
|
|
|
inRepo $ Git.Command.run
|
|
|
|
|
[ Params "remote add"
|
|
|
|
|
, Param remotename
|
|
|
|
|
, Param $ Git.GCrypt.urlPrefix ++ gitrepo
|
|
|
|
|
]
|
|
|
|
|
|
2013-09-12 19:54:35 +00:00
|
|
|
|
setGcryptEncryption c' remotename
|
2013-09-07 22:38:00 +00:00
|
|
|
|
|
|
|
|
|
{- Run a git fetch and a push to the git repo in order to get
|
|
|
|
|
- its gcrypt-id set up, so that later git annex commands
|
2013-10-01 23:10:45 +00:00
|
|
|
|
- will use the remote as a gcrypt remote. The fetch is
|
2013-09-07 22:38:00 +00:00
|
|
|
|
- needed if the repo already exists; the push is needed
|
|
|
|
|
- if the repo has not yet been initialized by gcrypt. -}
|
|
|
|
|
void $ inRepo $ Git.Command.runBool
|
|
|
|
|
[ Param "fetch"
|
|
|
|
|
, Param remotename
|
|
|
|
|
]
|
|
|
|
|
void $ inRepo $ Git.Command.runBool
|
|
|
|
|
[ Param "push"
|
|
|
|
|
, Param remotename
|
2014-02-19 05:09:17 +00:00
|
|
|
|
, Param $ Git.fromRef Annex.Branch.fullname
|
2013-09-07 22:38:00 +00:00
|
|
|
|
]
|
|
|
|
|
g <- inRepo Git.Config.reRead
|
|
|
|
|
case Git.GCrypt.remoteRepoId g (Just remotename) of
|
|
|
|
|
Nothing -> error "unable to determine gcrypt-id of remote"
|
2013-09-12 19:54:35 +00:00
|
|
|
|
Just gcryptid -> do
|
|
|
|
|
let u = genUUIDInNameSpace gCryptNameSpace gcryptid
|
2013-09-26 03:19:01 +00:00
|
|
|
|
if Just u == mu || isNothing mu
|
2013-09-08 19:19:14 +00:00
|
|
|
|
then do
|
2013-09-24 21:25:47 +00:00
|
|
|
|
method <- setupRepo gcryptid =<< inRepo (Git.Construct.fromRemoteLocation gitrepo)
|
|
|
|
|
gitConfigSpecialRemote u c' "gcrypt" (fromAccessMethod method)
|
2013-09-08 19:19:14 +00:00
|
|
|
|
return (c', u)
|
2013-10-01 23:10:45 +00:00
|
|
|
|
else error $ "uuid mismatch " ++ show (u, mu, gcryptid)
|
2013-09-07 22:38:00 +00:00
|
|
|
|
|
2013-09-24 21:25:47 +00:00
|
|
|
|
{- Sets up the gcrypt repository. The repository is either a local
|
|
|
|
|
- repo, or it is accessed via rsync directly, or it is accessed over ssh
|
|
|
|
|
- and git-annex-shell is available to manage it.
|
|
|
|
|
-
|
2013-10-01 19:16:20 +00:00
|
|
|
|
- The GCryptID is recorded in the repository's git config for later use.
|
|
|
|
|
- Also, if the git config has receive.denyNonFastForwards set, disable
|
|
|
|
|
- it; gcrypt relies on being able to fast-forward branches.
|
2013-09-24 21:25:47 +00:00
|
|
|
|
-}
|
|
|
|
|
setupRepo :: Git.GCrypt.GCryptId -> Git.Repo -> Annex AccessMethod
|
|
|
|
|
setupRepo gcryptid r
|
2013-09-24 21:51:12 +00:00
|
|
|
|
| Git.repoIsUrl r = do
|
2013-10-01 21:20:51 +00:00
|
|
|
|
(_, _, accessmethod) <- rsyncTransport r
|
2013-09-24 21:51:12 +00:00
|
|
|
|
case accessmethod of
|
2013-10-01 21:20:51 +00:00
|
|
|
|
AccessDirect -> rsyncsetup
|
|
|
|
|
AccessShell -> ifM gitannexshellsetup
|
2013-09-24 21:51:12 +00:00
|
|
|
|
( return AccessShell
|
2013-10-01 21:20:51 +00:00
|
|
|
|
, rsyncsetup
|
2013-09-24 21:51:12 +00:00
|
|
|
|
)
|
2013-09-24 21:25:47 +00:00
|
|
|
|
| Git.repoIsLocalUnknown r = localsetup =<< liftIO (Git.Config.read r)
|
|
|
|
|
| otherwise = localsetup r
|
|
|
|
|
where
|
|
|
|
|
localsetup r' = do
|
2013-10-01 19:16:20 +00:00
|
|
|
|
let setconfig k v = liftIO $ Git.Command.run [Param "config", Param k, Param v] r'
|
|
|
|
|
setconfig coreGCryptId gcryptid
|
|
|
|
|
setconfig denyNonFastForwards (Git.Config.boolConfig False)
|
2013-09-24 21:25:47 +00:00
|
|
|
|
return AccessDirect
|
|
|
|
|
|
2013-10-01 19:16:20 +00:00
|
|
|
|
{- As well as modifying the remote's git config,
|
|
|
|
|
- create the objectDir on the remote,
|
|
|
|
|
- which is needed for direct rsync of objects to work.
|
2013-09-24 21:25:47 +00:00
|
|
|
|
-}
|
|
|
|
|
rsyncsetup = Remote.Rsync.withRsyncScratchDir $ \tmp -> do
|
|
|
|
|
liftIO $ createDirectoryIfMissing True $ tmp </> objectDir
|
2013-10-01 21:20:51 +00:00
|
|
|
|
(rsynctransport, rsyncurl, _) <- rsyncTransport r
|
2013-09-24 21:25:47 +00:00
|
|
|
|
let tmpconfig = tmp </> "config"
|
|
|
|
|
void $ liftIO $ rsync $ rsynctransport ++
|
|
|
|
|
[ Param $ rsyncurl ++ "/config"
|
|
|
|
|
, Param tmpconfig
|
|
|
|
|
]
|
2013-10-01 19:16:20 +00:00
|
|
|
|
liftIO $ do
|
2013-10-01 21:20:51 +00:00
|
|
|
|
void $ Git.Config.changeFile tmpconfig coreGCryptId gcryptid
|
|
|
|
|
void $ Git.Config.changeFile tmpconfig denyNonFastForwards (Git.Config.boolConfig False)
|
2013-09-24 21:25:47 +00:00
|
|
|
|
ok <- liftIO $ rsync $ rsynctransport ++
|
|
|
|
|
[ Params "--recursive"
|
|
|
|
|
, Param $ tmp ++ "/"
|
2013-09-26 03:19:01 +00:00
|
|
|
|
, Param rsyncurl
|
2013-09-24 21:25:47 +00:00
|
|
|
|
]
|
|
|
|
|
unless ok $
|
|
|
|
|
error "Failed to connect to remote to set it up."
|
2013-10-01 21:20:51 +00:00
|
|
|
|
return AccessDirect
|
2013-09-24 21:25:47 +00:00
|
|
|
|
|
2013-10-01 21:20:51 +00:00
|
|
|
|
{- Ask git-annex-shell to configure the repository as a gcrypt
|
|
|
|
|
- repository. May fail if it is too old. -}
|
|
|
|
|
gitannexshellsetup = Ssh.onRemote r (boolSystem, False)
|
|
|
|
|
"gcryptsetup" [ Param gcryptid ] []
|
2013-09-24 21:51:12 +00:00
|
|
|
|
|
2013-10-01 19:16:20 +00:00
|
|
|
|
denyNonFastForwards = "receive.denyNonFastForwards"
|
|
|
|
|
|
2013-09-24 21:25:47 +00:00
|
|
|
|
shellOrRsync :: Remote -> Annex a -> Annex a -> Annex a
|
|
|
|
|
shellOrRsync r ashell arsync = case method of
|
|
|
|
|
AccessShell -> ashell
|
|
|
|
|
_ -> arsync
|
|
|
|
|
where
|
|
|
|
|
method = toAccessMethod $ fromMaybe "" $
|
|
|
|
|
remoteAnnexGCrypt $ gitconfig r
|
|
|
|
|
|
2013-09-12 19:54:35 +00:00
|
|
|
|
{- Configure gcrypt to use the same list of keyids that
|
2013-09-17 20:06:29 +00:00
|
|
|
|
- were passed to initremote as its participants.
|
|
|
|
|
- Also, configure it to use a signing key that is in the list of
|
|
|
|
|
- participants, which gcrypt requires is the case, and may not be
|
|
|
|
|
- depending on system configuration.
|
|
|
|
|
-
|
2014-07-15 21:33:14 +00:00
|
|
|
|
- (For shared encryption, gcrypt's default behavior is used.)
|
|
|
|
|
-
|
|
|
|
|
- Also, sets gcrypt-publish-participants to avoid unncessary gpg
|
|
|
|
|
- passphrase prompts.
|
|
|
|
|
-}
|
2013-09-12 19:54:35 +00:00
|
|
|
|
setGcryptEncryption :: RemoteConfig -> String -> Annex ()
|
|
|
|
|
setGcryptEncryption c remotename = do
|
2014-07-15 21:33:14 +00:00
|
|
|
|
let participants = remoteconfig Git.GCrypt.remoteParticipantConfigKey
|
2013-09-12 19:54:35 +00:00
|
|
|
|
case extractCipher c of
|
|
|
|
|
Nothing -> noCrypto
|
2013-09-17 20:06:29 +00:00
|
|
|
|
Just (EncryptedCipher _ _ (KeyIds { keyIds = ks})) -> do
|
2013-09-12 19:54:35 +00:00
|
|
|
|
setConfig participants (unwords ks)
|
2013-09-17 20:06:29 +00:00
|
|
|
|
let signingkey = ConfigKey $ Git.GCrypt.remoteSigningKey remotename
|
|
|
|
|
skeys <- M.keys <$> liftIO secretKeys
|
|
|
|
|
case filter (`elem` ks) skeys of
|
|
|
|
|
[] -> noop
|
|
|
|
|
(k:_) -> setConfig signingkey k
|
2013-09-12 19:54:35 +00:00
|
|
|
|
Just (SharedCipher _) ->
|
|
|
|
|
unsetConfig participants
|
2014-07-15 21:33:14 +00:00
|
|
|
|
setConfig (remoteconfig Git.GCrypt.remotePublishParticipantConfigKey)
|
|
|
|
|
(Git.Config.boolConfig True)
|
|
|
|
|
where
|
|
|
|
|
remoteconfig n = ConfigKey $ n remotename
|
2013-09-12 19:54:35 +00:00
|
|
|
|
|
2013-09-08 18:54:28 +00:00
|
|
|
|
store :: Remote -> Remote.Rsync.RsyncOpts -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
|
|
|
|
|
store r rsyncopts (cipher, enck) k p
|
2013-09-07 22:38:00 +00:00
|
|
|
|
| not $ Git.repoIsUrl (repo r) = guardUsable (repo r) False $
|
2013-09-24 21:25:47 +00:00
|
|
|
|
metered (Just p) k $ \meterupdate -> spoolencrypted $ \h -> do
|
|
|
|
|
let dest = gCryptLocation r enck
|
2013-09-07 22:38:00 +00:00
|
|
|
|
createDirectoryIfMissing True $ parentDir dest
|
|
|
|
|
readBytes (meteredWriteFile meterupdate dest) h
|
|
|
|
|
return True
|
2013-09-24 21:25:47 +00:00
|
|
|
|
| Git.repoIsSsh (repo r) = shellOrRsync r storeshell storersync
|
2013-09-08 17:00:48 +00:00
|
|
|
|
| otherwise = unsupportedUrl
|
2013-09-07 22:38:00 +00:00
|
|
|
|
where
|
2013-09-08 18:54:28 +00:00
|
|
|
|
gpgopts = getGpgEncParams r
|
2013-09-24 21:25:47 +00:00
|
|
|
|
storersync = Remote.Rsync.storeEncrypted rsyncopts gpgopts (cipher, enck) k p
|
|
|
|
|
storeshell = withTmp enck $ \tmp ->
|
|
|
|
|
ifM (spoolencrypted $ readBytes $ \b -> catchBoolIO $ L.writeFile tmp b >> return True)
|
|
|
|
|
( Ssh.rsyncHelper (Just p)
|
2013-10-01 18:10:45 +00:00
|
|
|
|
=<< Ssh.rsyncParamsRemote False r Upload enck tmp Nothing
|
2013-09-24 21:25:47 +00:00
|
|
|
|
, return False
|
|
|
|
|
)
|
|
|
|
|
spoolencrypted a = Annex.Content.sendAnnex k noop $ \src ->
|
|
|
|
|
liftIO $ catchBoolIO $
|
|
|
|
|
encrypt gpgopts cipher (feedFile src) a
|
2013-09-07 22:38:00 +00:00
|
|
|
|
|
2013-09-08 18:54:28 +00:00
|
|
|
|
retrieve :: Remote -> Remote.Rsync.RsyncOpts -> (Cipher, Key) -> Key -> FilePath -> MeterUpdate -> Annex Bool
|
|
|
|
|
retrieve r rsyncopts (cipher, enck) k d p
|
2013-09-08 17:00:48 +00:00
|
|
|
|
| not $ Git.repoIsUrl (repo r) = guardUsable (repo r) False $ do
|
|
|
|
|
retrievewith $ L.readFile src
|
|
|
|
|
return True
|
2013-09-24 21:25:47 +00:00
|
|
|
|
| Git.repoIsSsh (repo r) = shellOrRsync r retrieveshell retrieversync
|
2013-09-08 17:00:48 +00:00
|
|
|
|
| otherwise = unsupportedUrl
|
|
|
|
|
where
|
|
|
|
|
src = gCryptLocation r enck
|
|
|
|
|
retrievewith a = metered (Just p) k $ \meterupdate -> liftIO $
|
|
|
|
|
a >>= \b ->
|
|
|
|
|
decrypt cipher (feedBytes b)
|
|
|
|
|
(readBytes $ meteredWriteFile meterupdate d)
|
2013-09-24 21:25:47 +00:00
|
|
|
|
retrieversync = Remote.Rsync.retrieveEncrypted rsyncopts (cipher, enck) k d p
|
|
|
|
|
retrieveshell = withTmp enck $ \tmp ->
|
2013-10-01 18:10:45 +00:00
|
|
|
|
ifM (Ssh.rsyncHelper (Just p) =<< Ssh.rsyncParamsRemote False r Download enck tmp Nothing)
|
2013-09-24 21:25:47 +00:00
|
|
|
|
( liftIO $ catchBoolIO $ do
|
|
|
|
|
decrypt cipher (feedFile tmp) $
|
|
|
|
|
readBytes $ L.writeFile d
|
|
|
|
|
return True
|
|
|
|
|
, return False
|
|
|
|
|
)
|
2013-09-07 22:38:00 +00:00
|
|
|
|
|
2013-09-08 18:54:28 +00:00
|
|
|
|
remove :: Remote -> Remote.Rsync.RsyncOpts -> Key -> Annex Bool
|
|
|
|
|
remove r rsyncopts k
|
2013-09-08 17:00:48 +00:00
|
|
|
|
| not $ Git.repoIsUrl (repo r) = guardUsable (repo r) False $ do
|
2013-09-24 21:25:47 +00:00
|
|
|
|
liftIO $ removeDirectoryRecursive $ parentDir $ gCryptLocation r k
|
2013-09-08 17:00:48 +00:00
|
|
|
|
return True
|
2013-09-24 21:25:47 +00:00
|
|
|
|
| Git.repoIsSsh (repo r) = shellOrRsync r removeshell removersync
|
2013-09-08 17:00:48 +00:00
|
|
|
|
| otherwise = unsupportedUrl
|
|
|
|
|
where
|
2013-09-24 21:25:47 +00:00
|
|
|
|
removersync = Remote.Rsync.remove rsyncopts k
|
|
|
|
|
removeshell = Ssh.dropKey (repo r) k
|
2013-09-07 22:38:00 +00:00
|
|
|
|
|
2013-09-08 18:54:28 +00:00
|
|
|
|
checkPresent :: Remote -> Remote.Rsync.RsyncOpts -> Key -> Annex (Either String Bool)
|
|
|
|
|
checkPresent r rsyncopts k
|
2013-09-07 22:38:00 +00:00
|
|
|
|
| not $ Git.repoIsUrl (repo r) =
|
2013-09-24 21:25:47 +00:00
|
|
|
|
guardUsable (repo r) (cantCheck $ repo r) $
|
|
|
|
|
liftIO $ catchDefaultIO (cantCheck $ repo r) $
|
2013-09-07 22:38:00 +00:00
|
|
|
|
Right <$> doesFileExist (gCryptLocation r k)
|
2013-09-24 21:25:47 +00:00
|
|
|
|
| Git.repoIsSsh (repo r) = shellOrRsync r checkshell checkrsync
|
2013-09-08 17:00:48 +00:00
|
|
|
|
| otherwise = unsupportedUrl
|
2013-09-07 22:38:00 +00:00
|
|
|
|
where
|
2013-09-24 21:25:47 +00:00
|
|
|
|
checkrsync = Remote.Rsync.checkPresent (repo r) rsyncopts k
|
|
|
|
|
checkshell = Ssh.inAnnex (repo r) k
|
2013-09-07 22:38:00 +00:00
|
|
|
|
|
2013-09-24 21:25:47 +00:00
|
|
|
|
{- Annexed objects are hashed using lower-case directories for max
|
2013-09-08 18:54:28 +00:00
|
|
|
|
- portability. -}
|
2013-09-07 22:38:00 +00:00
|
|
|
|
gCryptLocation :: Remote -> Key -> FilePath
|
2013-09-24 21:25:47 +00:00
|
|
|
|
gCryptLocation r key = Git.repoLocation (repo r) </> objectDir </> keyPath key hashDirLower
|
|
|
|
|
|
|
|
|
|
data AccessMethod = AccessDirect | AccessShell
|
|
|
|
|
|
|
|
|
|
fromAccessMethod :: AccessMethod -> String
|
|
|
|
|
fromAccessMethod AccessShell = "shell"
|
|
|
|
|
fromAccessMethod AccessDirect = "true"
|
|
|
|
|
|
|
|
|
|
toAccessMethod :: String -> AccessMethod
|
|
|
|
|
toAccessMethod "shell" = AccessShell
|
|
|
|
|
toAccessMethod _ = AccessDirect
|
|
|
|
|
|
2013-09-27 20:21:56 +00:00
|
|
|
|
getGCryptUUID :: Bool -> Git.Repo -> Annex (Maybe UUID)
|
|
|
|
|
getGCryptUUID fast r = (genUUIDInNameSpace gCryptNameSpace <$>) . fst
|
|
|
|
|
<$> getGCryptId fast r
|
|
|
|
|
|
|
|
|
|
coreGCryptId :: String
|
|
|
|
|
coreGCryptId = "core.gcrypt-id"
|
|
|
|
|
|
|
|
|
|
{- gcrypt repos set up by git-annex as special remotes have a
|
|
|
|
|
- core.gcrypt-id setting in their config, which can be mapped back to
|
|
|
|
|
- the remote's UUID.
|
|
|
|
|
-
|
|
|
|
|
- In fast mode, only checks local repos. To check a remote repo,
|
|
|
|
|
- tries git-annex-shell and direct rsync of the git config file.
|
|
|
|
|
-
|
|
|
|
|
- (Also returns a version of input repo with its config read.) -}
|
|
|
|
|
getGCryptId :: Bool -> Git.Repo -> Annex (Maybe Git.GCrypt.GCryptId, Git.Repo)
|
|
|
|
|
getGCryptId fast r
|
2013-10-01 18:38:20 +00:00
|
|
|
|
| Git.repoIsLocal r || Git.repoIsLocalUnknown r = extract <$>
|
2013-09-27 23:52:36 +00:00
|
|
|
|
liftIO (catchMaybeIO $ Git.Config.read r)
|
|
|
|
|
| not fast = extract . liftM fst <$> getM (eitherToMaybe <$>)
|
|
|
|
|
[ Ssh.onRemote r (Git.Config.fromPipe r, Left undefined) "configlist" [] []
|
|
|
|
|
, getConfigViaRsync r
|
|
|
|
|
]
|
2013-09-27 20:21:56 +00:00
|
|
|
|
| otherwise = return (Nothing, r)
|
|
|
|
|
where
|
2013-09-27 23:52:36 +00:00
|
|
|
|
extract Nothing = (Nothing, r)
|
|
|
|
|
extract (Just r') = (Git.Config.getMaybe coreGCryptId r', r')
|
|
|
|
|
|
|
|
|
|
getConfigViaRsync :: Git.Repo -> Annex (Either SomeException (Git.Repo, String))
|
|
|
|
|
getConfigViaRsync r = do
|
|
|
|
|
(rsynctransport, rsyncurl, _) <- rsyncTransport r
|
|
|
|
|
liftIO $ do
|
|
|
|
|
withTmpFile "tmpconfig" $ \tmpconfig _ -> do
|
|
|
|
|
void $ rsync $ rsynctransport ++
|
|
|
|
|
[ Param $ rsyncurl ++ "/config"
|
|
|
|
|
, Param tmpconfig
|
|
|
|
|
]
|
|
|
|
|
Git.Config.fromFile r tmpconfig
|