--- /dev/null
+{- Using borg as a remote.
+ -
+ - Copyright 2020 Joey Hess <id@joeyh.name>
+ -
+ - Licensed under the GNU AGPL version 3 or higher.
+ -}
+
+module Remote.Borg (remote) where
+
+import Annex.Common
+import Types.Remote
+import Types.Creds
+import qualified Git
+import Config
+import Config.Cost
+import Annex.SpecialRemote.Config
+import Remote.Helper.Special
+import Remote.Helper.ExportImport
+import Annex.UUID
+import Types.ProposedAccepted
+
+import qualified Data.Map as M
+
+type BorgRepo = String
+
+remote :: RemoteType
+remote = RemoteType
+ { typename = "borg"
+ , enumerate = const (findSpecialRemotes "borgrepo")
+ , generate = gen
+ , configParser = mkRemoteConfigParser
+ [ optionalStringParser borgrepoField
+ (FieldDesc "(required) borg repository to use")
+ ]
+ , setup = borgSetup
+ , exportSupported = exportUnsupported
+ , importSupported = importIsSupported
+ , thirdPartyPopulated = True
+ }
+
+borgrepoField :: RemoteConfigField
+borgrepoField = Accepted "borgrepo"
+
+gen :: Git.Repo -> UUID -> RemoteConfig -> RemoteGitConfig -> RemoteStateHandle -> Annex (Maybe Remote)
+gen r u rc gc rs = do
+ c <- parsedRemoteConfig remote rc
+ cst <- remoteCost gc $
+ if borgLocal borgrepo
+ then nearlyCheapRemoteCost
+ else expensiveRemoteCost
+ return $ Just $ Remote
+ { uuid = u
+ , cost = cst
+ , name = Git.repoDescribe r
+ , storeKey = storeKeyDummy
+ , retrieveKeyFile = retrieveKeyFileDummy
+ , retrieveKeyFileCheap = Nothing
+ -- Borg cryptographically verifies content.
+ , retrievalSecurityPolicy = RetrievalAllKeysSecure
+ , removeKey = removeKeyDummy
+ , lockContent = Nothing
+ , checkPresent = checkPresentDummy
+ , checkPresentCheap = borgLocal borgrepo
+ , exportActions = exportUnsupported
+ , importActions = importUnsupported
+ , whereisKey = Nothing
+ , remoteFsck = Nothing
+ , repairRepo = Nothing
+ , config = c
+ , getRepo = return r
+ , gitconfig = gc
+ , localpath = if borgLocal borgrepo && not (null borgrepo)
+ then Just borgrepo
+ else Nothing
+ , remotetype = remote
+ , availability = if borgLocal borgrepo then LocallyAvailable else GloballyAvailable
+ , readonly = False
+ , appendonly = False
+ , mkUnavailable = return Nothing
+ , getInfo = return [("repo", borgrepo)]
+ , claimUrl = Nothing
+ , checkUrl = Nothing
+ , remoteStateHandle = rs
+ }
+ where
+ borgrepo = fromMaybe (giveup "missing borgrepo") $ remoteAnnexBorgRepo gc
+
+borgSetup :: SetupStage -> Maybe UUID -> Maybe CredPair -> RemoteConfig -> RemoteGitConfig -> Annex (RemoteConfig, UUID)
+borgSetup _ mu _ c _gc = do
+ u <- maybe (liftIO genUUID) return mu
+
+ -- verify configuration is sane
+ let borgrepo = maybe (giveup "Specify borgrepo=") fromProposedAccepted $
+ M.lookup borgrepoField c
+
+ -- The borgrepo is stored in git config, as well as this repo's
+ -- persistant state, so it can vary between hosts.
+ gitConfigSpecialRemote u c [("borgrepo", borgrepo)]
+
+ -- TODO: untrusted by default, but allow overriding that
+
+ return (c, u)
+
+borgLocal :: BorgRepo -> Bool
+borgLocal = notElem ':'
, remoteAnnexGnupgDecryptOptions :: [String]
, remoteAnnexRsyncUrl :: Maybe String
, remoteAnnexBupRepo :: Maybe String
+ , remoteAnnexBorgRepo :: Maybe String
, remoteAnnexTahoe :: Maybe FilePath
, remoteAnnexBupSplitOptions :: [String]
, remoteAnnexDirectory :: Maybe FilePath
, remoteAnnexGnupgDecryptOptions = getoptions "gnupg-decrypt-options"
, remoteAnnexRsyncUrl = notempty $ getmaybe "rsyncurl"
, remoteAnnexBupRepo = getmaybe "buprepo"
+ , remoteAnnexBorgRepo = getmaybe "borgrepo"
, remoteAnnexTahoe = getmaybe "tahoe"
, remoteAnnexBupSplitOptions = getoptions "bup-split-options"
, remoteAnnexDirectory = notempty $ getmaybe "directory"
--- /dev/null
+Finally gotten started on the borg special remote idea. A prerequisite of
+that is remotes that can be imported from, but not exported to. So I
+actually started by allowing setting importtree=yes without
+exporttree=yes. A lot of code had assumptions about that not being allowed,
+so it took a while to chase down everything. Finished most of that yesterday.
+
+What I've done today is added a `thirdPartyPopulated` type of remote,
+which `git-annex sync` can "pull" from by using the existing import
+interface to list files on it, and determine which of them are annex object
+files. I have not started on the actual borg remote at all, but this should
+be all the groundwork for it done.
+
+(I also finished up annex.stalldetection earlier this week.)
+
+---
+
+This work was sponsored by Jake Vosloo [on Patreon](https://patreon.com/joeyh).
These parameters can be passed to `git annex initremote` to configure the
remote:
-* `repository` - The location of a borg repository, eg a path, or
+* `borgrepo` - The location of a borg repository, eg a path, or
`user@host:path` for ssh access.
* `scan` - The path, within the borg repository, to scan for
## setup example
# borg init --encryption=keyfile /path/to/borgrepo
- # git annex initremote borg type=borg repository=/path/to/borgrepo scan=`pwd`
+ # git annex initremote borg type=borg borgrepo=/path/to/borgrepo scan=`pwd`
# borg create /path/to/borgrepo `pwd`::{now}
# git annex sync borg