convert gcrypt to new regime, including chunking

Some reorg of Remote.Rsync code to export the things gcrypt needs.
This commit is contained in:
Joey Hess 2014-08-03 17:31:10 -04:00
parent f5f961215b
commit b35f7983ff
4 changed files with 84 additions and 96 deletions

View file

@ -29,7 +29,6 @@ import qualified Git.GCrypt
import qualified Git.Construct import qualified Git.Construct
import qualified Git.Types as Git () import qualified Git.Types as Git ()
import qualified Annex.Branch import qualified Annex.Branch
import qualified Annex.Content
import Config import Config
import Config.Cost import Config.Cost
import Remote.Helper.Git import Remote.Helper.Git
@ -38,7 +37,6 @@ import Remote.Helper.Special
import Remote.Helper.Messages import Remote.Helper.Messages
import qualified Remote.Helper.Ssh as Ssh import qualified Remote.Helper.Ssh as Ssh
import Utility.Metered import Utility.Metered
import Crypto
import Annex.UUID import Annex.UUID
import Annex.Ssh import Annex.Ssh
import qualified Remote.Rsync import qualified Remote.Rsync
@ -47,7 +45,6 @@ import Utility.Tmp
import Logs.Remote import Logs.Remote
import Logs.Transfer import Logs.Transfer
import Utility.Gpg import Utility.Gpg
import Annex.Content
remote :: RemoteType remote :: RemoteType
remote = RemoteType { remote = RemoteType {
@ -101,8 +98,8 @@ gen' r u c gc = do
{ uuid = u { uuid = u
, cost = cst , cost = cst
, name = Git.repoDescribe r , name = Git.repoDescribe r
, storeKey = \_ _ _ -> noCrypto , storeKey = storeKeyDummy
, retrieveKeyFile = \_ _ _ _ -> noCrypto , retrieveKeyFile = retreiveKeyFileDummy
, retrieveKeyFileCheap = \_ _ -> return False , retrieveKeyFileCheap = \_ _ -> return False
, removeKey = remove this rsyncopts , removeKey = remove this rsyncopts
, hasKey = checkPresent this rsyncopts , hasKey = checkPresent this rsyncopts
@ -118,10 +115,14 @@ gen' r u c gc = do
, availability = availabilityCalc r , availability = availabilityCalc r
, remotetype = remote , remotetype = remote
} }
return $ Just $ encryptableRemote c return $ Just $ specialRemote' specialcfg c
(store this rsyncopts) (simplyPrepare $ store this rsyncopts)
(retrieve this rsyncopts) (simplyPrepare $ retrieve this rsyncopts)
this this
where
specialcfg = (specialRemoteCfg c)
-- Rsync displays its own progress.
{ displayProgress = False }
rsyncTransportToObjects :: Git.Repo -> Annex ([CommandParam], String) rsyncTransportToObjects :: Git.Repo -> Annex ([CommandParam], String)
rsyncTransportToObjects r = do rsyncTransportToObjects r = do
@ -147,7 +148,7 @@ rsyncTransport r
noCrypto :: Annex a noCrypto :: Annex a
noCrypto = error "cannot use gcrypt remote without encryption enabled" noCrypto = error "cannot use gcrypt remote without encryption enabled"
unsupportedUrl :: Annex a unsupportedUrl :: a
unsupportedUrl = error "using non-ssh remote repo url with gcrypt is not supported" unsupportedUrl = error "using non-ssh remote repo url with gcrypt is not supported"
gCryptSetup :: Maybe UUID -> Maybe CredPair -> RemoteConfig -> Annex (RemoteConfig, UUID) gCryptSetup :: Maybe UUID -> Maybe CredPair -> RemoteConfig -> Annex (RemoteConfig, UUID)
@ -249,14 +250,19 @@ setupRepo gcryptid r
denyNonFastForwards = "receive.denyNonFastForwards" denyNonFastForwards = "receive.denyNonFastForwards"
shellOrRsync :: Remote -> Annex a -> Annex a -> Annex a isShell :: Remote -> Bool
shellOrRsync r ashell arsync = case method of isShell r = case method of
AccessShell -> ashell AccessShell -> True
_ -> arsync _ -> False
where where
method = toAccessMethod $ fromMaybe "" $ method = toAccessMethod $ fromMaybe "" $
remoteAnnexGCrypt $ gitconfig r remoteAnnexGCrypt $ gitconfig r
shellOrRsync :: Remote -> Annex a -> Annex a -> Annex a
shellOrRsync r ashell arsync
| isShell r = ashell
| otherwise = arsync
{- Configure gcrypt to use the same list of keyids that {- Configure gcrypt to use the same list of keyids that
- were passed to initremote as its participants. - were passed to initremote as its participants.
- Also, configure it to use a signing key that is in the list of - Also, configure it to use a signing key that is in the list of
@ -287,51 +293,32 @@ setGcryptEncryption c remotename = do
where where
remoteconfig n = ConfigKey $ n remotename remoteconfig n = ConfigKey $ n remotename
store :: Remote -> Remote.Rsync.RsyncOpts -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool store :: Remote -> Remote.Rsync.RsyncOpts -> Storer
store r rsyncopts (cipher, enck) k p store r rsyncopts
| not $ Git.repoIsUrl (repo r) = guardUsable (repo r) False $ | not $ Git.repoIsUrl (repo r) =
metered (Just p) k $ \meterupdate -> spoolencrypted $ \h -> do byteStorer $ \k b p -> guardUsable (repo r) False $ liftIO $ do
let dest = gCryptLocation r enck let dest = gCryptLocation r k
createDirectoryIfMissing True $ parentDir dest createDirectoryIfMissing True $ parentDir dest
readBytes (meteredWriteFile meterupdate dest) h meteredWriteFile p dest b
return True return True
| Git.repoIsSsh (repo r) = shellOrRsync r storeshell storersync | Git.repoIsSsh (repo r) = if isShell r
then fileStorer $ \k f p -> Ssh.rsyncHelper (Just p)
=<< Ssh.rsyncParamsRemote False r Upload k f Nothing
else fileStorer $ Remote.Rsync.store rsyncopts
| otherwise = unsupportedUrl | otherwise = unsupportedUrl
where
gpgopts = getGpgEncParams r
storersync = undefined -- 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)
=<< Ssh.rsyncParamsRemote False r Upload enck tmp Nothing
, return False
)
spoolencrypted a = Annex.Content.sendAnnex k noop $ \src ->
liftIO $ catchBoolIO $
encrypt gpgopts cipher (feedFile src) a
retrieve :: Remote -> Remote.Rsync.RsyncOpts -> (Cipher, Key) -> Key -> FilePath -> MeterUpdate -> Annex Bool retrieve :: Remote -> Remote.Rsync.RsyncOpts -> Retriever
retrieve r rsyncopts (cipher, enck) k d p retrieve r rsyncopts
| not $ Git.repoIsUrl (repo r) = guardUsable (repo r) False $ do | not $ Git.repoIsUrl (repo r) = byteRetriever $ \k sink ->
retrievewith $ L.readFile src guardUsable (repo r) False $
return True sink =<< liftIO (L.readFile $ gCryptLocation r k)
| Git.repoIsSsh (repo r) = shellOrRsync r retrieveshell retrieversync | Git.repoIsSsh (repo r) = if isShell r
then fileRetriever $ \f k p ->
unlessM (Ssh.rsyncHelper (Just p) =<< Ssh.rsyncParamsRemote False r Download k f Nothing) $
error "rsync failed"
else fileRetriever $ Remote.Rsync.retrieve rsyncopts
| otherwise = unsupportedUrl | otherwise = unsupportedUrl
where where
src = gCryptLocation r enck
retrievewith a = metered (Just p) k $ \meterupdate -> liftIO $
a >>= \b ->
decrypt cipher (feedBytes b)
(readBytes $ meteredWriteFile meterupdate d)
retrieversync = undefined -- Remote.Rsync.retrieveEncrypted rsyncopts (cipher, enck) k d p
retrieveshell = withTmp enck $ \tmp ->
ifM (Ssh.rsyncHelper (Just p) =<< Ssh.rsyncParamsRemote False r Download enck tmp Nothing)
( liftIO $ catchBoolIO $ do
decrypt cipher (feedFile tmp) $
readBytes $ L.writeFile d
return True
, return False
)
remove :: Remote -> Remote.Rsync.RsyncOpts -> Key -> Annex Bool remove :: Remote -> Remote.Rsync.RsyncOpts -> Key -> Annex Bool
remove r rsyncopts k remove r rsyncopts k

View file

@ -9,6 +9,8 @@
module Remote.Rsync ( module Remote.Rsync (
remote, remote,
store,
retrieve,
remove, remove,
checkPresent, checkPresent,
withRsyncScratchDir, withRsyncScratchDir,
@ -54,8 +56,8 @@ gen r u c gc = do
let o = genRsyncOpts c gc transport url let o = genRsyncOpts c gc transport url
let islocal = rsyncUrlIsPath $ rsyncUrl o let islocal = rsyncUrlIsPath $ rsyncUrl o
return $ Just $ specialRemote' specialcfg c return $ Just $ specialRemote' specialcfg c
(simplyPrepare $ store o) (simplyPrepare $ fileStorer $ store o)
(simplyPrepare $ retrieve o) (simplyPrepare $ fileRetriever $ retrieve o)
Remote Remote
{ uuid = u { uuid = u
, cost = cst , cost = cst
@ -140,11 +142,44 @@ rsyncSetup mu _ c = do
gitConfigSpecialRemote u c' "rsyncurl" url gitConfigSpecialRemote u c' "rsyncurl" url
return (c', u) return (c', u)
store :: RsyncOpts -> Storer {- To send a single key is slightly tricky; need to build up a temporary
store = fileStorer . rsyncSend - directory structure to pass to rsync so it can create the hash
- directories.
-
- This would not be necessary if the hash directory structure used locally
- was always the same as that used on the rsync remote. So if that's ever
- unified, this gets nicer.
- (When we have the right hash directory structure, we can just
- pass --include=X --include=X/Y --include=X/Y/file --exclude=*)
-}
store :: RsyncOpts -> Key -> FilePath -> MeterUpdate -> Annex Bool
store o k src meterupdate = withRsyncScratchDir $ \tmp -> do
let dest = tmp </> Prelude.head (keyPaths k)
liftIO $ createDirectoryIfMissing True $ parentDir dest
ok <- liftIO $ if canrename
then do
rename src dest
return True
else createLinkOrCopy src dest
ps <- sendParams
if ok
then showResumable $ rsyncRemote Upload o (Just meterupdate) $ ps ++
[ Param "--recursive"
, partialParams
-- tmp/ to send contents of tmp dir
, File $ addTrailingPathSeparator tmp
, Param $ rsyncUrl o
]
else return False
where
{- If the key being sent is encrypted or chunked, the file
- containing its content is a temp file, and so can be
- renamed into place. Otherwise, the file is the annexed
- object file, and has to be copied or hard linked into place. -}
canrename = isEncKey k || isChunkKey k
retrieve :: RsyncOpts -> Retriever retrieve :: RsyncOpts -> FilePath -> Key -> MeterUpdate -> Annex ()
retrieve o = fileRetriever $ \f k p -> retrieve o f k p =
unlessM (rsyncRetrieve o k f (Just p)) $ unlessM (rsyncRetrieve o k f (Just p)) $
error "rsync failed" error "rsync failed"
@ -249,39 +284,3 @@ rsyncRemote direction o callback params = do
opts opts
| direction == Download = rsyncDownloadOptions o | direction == Download = rsyncDownloadOptions o
| otherwise = rsyncUploadOptions o | otherwise = rsyncUploadOptions o
{- To send a single key is slightly tricky; need to build up a temporary
- directory structure to pass to rsync so it can create the hash
- directories.
-
- This would not be necessary if the hash directory structure used locally
- was always the same as that used on the rsync remote. So if that's ever
- unified, this gets nicer.
- (When we have the right hash directory structure, we can just
- pass --include=X --include=X/Y --include=X/Y/file --exclude=*)
-}
rsyncSend :: RsyncOpts -> Key -> FilePath -> MeterUpdate -> Annex Bool
rsyncSend o k src meterupdate = withRsyncScratchDir $ \tmp -> do
let dest = tmp </> Prelude.head (keyPaths k)
liftIO $ createDirectoryIfMissing True $ parentDir dest
ok <- liftIO $ if canrename
then do
rename src dest
return True
else createLinkOrCopy src dest
ps <- sendParams
if ok
then showResumable $ rsyncRemote Upload o (Just meterupdate) $ ps ++
[ Param "--recursive"
, partialParams
-- tmp/ to send contents of tmp dir
, File $ addTrailingPathSeparator tmp
, Param $ rsyncUrl o
]
else return False
where
{- If the key being sent is encrypted or chunked, the file
- containing its content is a temp file, and so can be
- renamed into place. Otherwise, the file is the annexed
- object file, and has to be copied or hard linked into place. -}
canrename = isEncKey k || isChunkKey k

2
debian/changelog vendored
View file

@ -1,7 +1,7 @@
git-annex (5.20140718) UNRELEASED; urgency=medium git-annex (5.20140718) UNRELEASED; urgency=medium
* New chunk= option to chunk files stored in special remotes. * New chunk= option to chunk files stored in special remotes.
Currently supported by: directory, S3, rsync, and all external Supported by: directory, S3, gcrypt, rsync, and all external
and hook special remotes. and hook special remotes.
* Partially transferred files are automatically resumed when using * Partially transferred files are automatically resumed when using
chunked remotes! chunked remotes!

View file

@ -13,7 +13,7 @@ These parameters can be passed to `git annex initremote` to configure
gcrypt: gcrypt:
* `encryption` - One of "none", "hybrid", "shared", or "pubkey". * `encryption` - One of "none", "hybrid", "shared", or "pubkey".
See [[encryption]]. Required. See [[encryption]].
* `keyid` - Specifies the gpg key to use for encryption of both the files * `keyid` - Specifies the gpg key to use for encryption of both the files
git-annex stores in the repository, as well as to encrypt the git git-annex stores in the repository, as well as to encrypt the git
@ -24,6 +24,8 @@ gcrypt:
for gcrypt to use. This repository should be either empty, or an existing for gcrypt to use. This repository should be either empty, or an existing
gcrypt repositry. gcrypt repositry.
* `chunk` - Enables [[chunking]] when storing large files.
* `shellescape` - See [[rsync]] for the details of this option. * `shellescape` - See [[rsync]] for the details of this option.
## notes ## notes