import Utility.Tmp.Dir
import Utility.Metered
import Git.Types
+import qualified Database.Export as Export
import Control.Concurrent.STM
import Control.Concurrent.Async
owaitv <- liftIO newEmptyTMVarIO
iclosedv <- liftIO newEmptyTMVarIO
oclosedv <- liftIO newEmptyTMVarIO
+ exportdb <- ifM (Remote.isExportSupported r)
+ ( Just <$> Export.openDb (Remote.uuid r)
+ , pure Nothing
+ )
worker <- liftIO . async =<< forkState
- (proxySpecialRemote protoversion r ihdl ohdl owaitv oclosedv)
+ (proxySpecialRemote protoversion r ihdl ohdl owaitv oclosedv exportdb)
let remoteconn = P2PConnection
{ connRepo = Nothing
, connCheckAuth = const False
let closeremoteconn = do
liftIO $ atomically $ putTMVar oclosedv ()
join $ liftIO (wait worker)
+ maybe noop Export.closeDb exportdb
return $ Just
( remoterunst
, remoteconn
-> TMVar (Either L.ByteString Message)
-> TMVar ()
-> TMVar ()
+ -> Maybe Export.ExportHandle
-> Annex ()
-proxySpecialRemote protoversion r ihdl ohdl owaitv oclosedv = go
+proxySpecialRemote protoversion r ihdl ohdl owaitv oclosedv mexportdb = go
where
go :: Annex ()
go = liftIO receivemessage >>= \case
proxyput af k = do
liftIO $ sendmessage $ PUT_FROM (Offset 0)
withproxytmpfile k $ \tmpfile -> do
- let store = tryNonAsync (Remote.storeKey r k af (Just (decodeBS tmpfile)) nullMeterUpdate) >>= \case
+ let store = tryNonAsync (storeput k af (decodeBS tmpfile)) >>= \case
Right () -> liftIO $ sendmessage SUCCESS
Left err -> liftIO $ propagateerror err
liftIO receivemessage >>= \case
_ -> giveup "protocol error"
liftIO $ removeWhenExistsWith removeFile (fromRawFilePath tmpfile)
+ storeput k af tmpfile = case mexportdb of
+ Just exportdb -> liftIO (Export.getExportTree exportdb k) >>= \case
+ [] -> storeputkey k af tmpfile
+ locs -> do
+ havelocs <- liftIO $ S.fromList
+ <$> Export.getExportedLocation exportdb k
+ let locs' = filter (`S.notMember` havelocs) locs
+ forM_ locs' $ \loc ->
+ storeputexport exportdb k loc tmpfile
+ liftIO $ Export.flushDbQueue exportdb
+ Nothing -> storeputkey k af tmpfile
+
+ storeputkey k af tmpfile =
+ Remote.storeKey r k af (Just tmpfile) nullMeterUpdate
+
+ storeputexport exportdb k loc tmpfile = do
+ Remote.storeExport (Remote.exportActions r) tmpfile k loc nullMeterUpdate
+ liftIO $ Export.addExportedLocation exportdb k loc
+
receivetofile iv h n = liftIO receivebytestring >>= \case
Just b -> do
liftIO $ atomically $
export not supported
failed
-* These are only needed to support workflows other than `git-annex push`.
- (Since a push sends all content to the proxied remote and then pushes
- to the proxy, it happens to do things in an order where these are not
- necessary.)
- * `git-annex post-receive` of a proxied exporttree=yes special remote's
- annex-tracking-branch should check if the special remote contains all
- keys in the tree. If so, it can exporttree. If not, record
- the keys that are needed. (It could always exporttree,
- but better to avoid leaving it incomplete.)
- * After a key is received, the proxy should check if it's the *last* key
- that is needed to complete the export, and exporttree when so.
+* Prevent `enableproxy` from enabling an exporttree=yes special remote
+ that does not have annexobjects=yes, to avoid foot shooting.
* Handle cases where a single key is used by multiple files in the exported
tree. Need to download from the special remote in order to export
- multiple copies to it.
+ multiple copies to it. (In particular, this is needed when using
+ `git-annex push`. When using first `git push` followed by
+ `git-annex copy --to` the proxied remote, the received key is stored
+ to all export locations.)
* Handle case where the special remote does not support renameExport.
Each key will need to be downloaded from it in order to export the key
back to it, if the proxy is to support such a remote.