2016-12-02 17:50:56 +00:00
|
|
|
{- P2P protocol, Annex implementation
|
|
|
|
-
|
|
|
|
- Copyright 2016 Joey Hess <id@joeyh.name>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
|
|
|
{-# LANGUAGE RankNTypes, FlexibleContexts #-}
|
|
|
|
|
|
|
|
module P2P.Annex
|
2016-12-02 19:34:15 +00:00
|
|
|
( RunMode(..)
|
|
|
|
, RunEnv(..)
|
2016-12-02 17:50:56 +00:00
|
|
|
, runFullProto
|
|
|
|
) where
|
|
|
|
|
|
|
|
import Annex.Common
|
|
|
|
import Annex.Content
|
2016-12-02 20:39:01 +00:00
|
|
|
import Annex.Transfer
|
|
|
|
import Annex.Notification
|
2016-12-02 17:50:56 +00:00
|
|
|
import P2P.Protocol
|
|
|
|
import P2P.IO
|
2016-12-02 18:49:22 +00:00
|
|
|
import Logs.Location
|
|
|
|
import Types.NumCopies
|
2016-12-02 17:50:56 +00:00
|
|
|
|
|
|
|
import Control.Monad.Free
|
2016-12-02 18:49:22 +00:00
|
|
|
import qualified Data.ByteString.Lazy as L
|
2016-12-02 17:50:56 +00:00
|
|
|
|
2016-12-02 19:34:15 +00:00
|
|
|
-- When we're serving a peer, we know their uuid, and can use it to update
|
|
|
|
-- transfer logs.
|
|
|
|
data RunMode
|
|
|
|
= Serving UUID
|
|
|
|
| Client
|
|
|
|
|
2016-12-02 17:50:56 +00:00
|
|
|
-- Full interpreter for Proto, that can receive and send objects.
|
2016-12-02 19:34:15 +00:00
|
|
|
runFullProto :: RunMode -> RunEnv -> Proto a -> Annex (Maybe a)
|
|
|
|
runFullProto runmode runenv = go
|
2016-12-02 17:50:56 +00:00
|
|
|
where
|
|
|
|
go :: RunProto Annex
|
|
|
|
go (Pure v) = pure (Just v)
|
|
|
|
go (Free (Net n)) = runNet runenv go n
|
2016-12-02 19:34:15 +00:00
|
|
|
go (Free (Local l)) = runLocal runmode go l
|
2016-12-02 17:50:56 +00:00
|
|
|
|
2016-12-02 19:34:15 +00:00
|
|
|
runLocal :: RunMode -> RunProto Annex -> LocalF (Proto a) -> Annex (Maybe a)
|
|
|
|
runLocal runmode runner a = case a of
|
2016-12-02 17:50:56 +00:00
|
|
|
TmpContentSize k next -> do
|
|
|
|
tmp <- fromRepo $ gitAnnexTmpObjectLocation k
|
|
|
|
size <- liftIO $ catchDefaultIO 0 $ getFileSize tmp
|
|
|
|
runner (next (Len size))
|
2016-12-02 18:49:22 +00:00
|
|
|
ContentSize k next -> do
|
|
|
|
let getsize = liftIO . catchMaybeIO . getFileSize
|
|
|
|
size <- inAnnex' isJust Nothing getsize k
|
|
|
|
runner (next (Len <$> size))
|
2016-12-02 20:39:01 +00:00
|
|
|
-- TODO transfer log not updated
|
|
|
|
ReadContent k af (Offset o) next -> do
|
2016-12-02 18:49:22 +00:00
|
|
|
v <- tryNonAsync $ prepSendAnnex k
|
|
|
|
case v of
|
|
|
|
-- The check can detect a problem after the
|
|
|
|
-- content is sent, but we don't use it.
|
|
|
|
-- Instead, the receiving peer must AlwaysVerify
|
|
|
|
-- the content it receives.
|
|
|
|
Right (Just (f, _check)) -> do
|
2016-12-02 20:39:01 +00:00
|
|
|
v' <- tryNonAsync $ -- transfer upload k af $
|
|
|
|
liftIO $ do
|
|
|
|
h <- openBinaryFile f ReadMode
|
|
|
|
when (o /= 0) $
|
|
|
|
hSeek h AbsoluteSeek o
|
|
|
|
L.hGetContents h
|
2016-12-02 18:49:22 +00:00
|
|
|
case v' of
|
|
|
|
Left _ -> return Nothing
|
|
|
|
Right b -> runner (next b)
|
|
|
|
_ -> return Nothing
|
2016-12-02 20:39:01 +00:00
|
|
|
WriteContent k af (Offset o) (Len l) b next -> do
|
2016-12-02 18:49:22 +00:00
|
|
|
ok <- flip catchNonAsync (const $ return False) $
|
2016-12-02 20:39:01 +00:00
|
|
|
transfer download k af $
|
|
|
|
getViaTmp AlwaysVerify k $ \tmp -> liftIO $ do
|
|
|
|
withBinaryFile tmp WriteMode $ \h -> do
|
|
|
|
when (o /= 0) $
|
|
|
|
hSeek h AbsoluteSeek o
|
|
|
|
L.hPut h b
|
|
|
|
sz <- getFileSize tmp
|
|
|
|
return (toInteger sz == l, UnVerified)
|
2016-12-02 18:49:22 +00:00
|
|
|
runner (next ok)
|
|
|
|
SetPresent k u next -> do
|
|
|
|
v <- tryNonAsync $ logChange k u InfoPresent
|
|
|
|
case v of
|
|
|
|
Left _ -> return Nothing
|
|
|
|
Right () -> runner next
|
|
|
|
CheckContentPresent k next -> do
|
|
|
|
v <- tryNonAsync $ inAnnex k
|
|
|
|
case v of
|
|
|
|
Left _ -> return Nothing
|
|
|
|
Right result -> runner (next result)
|
|
|
|
RemoveContent k next -> do
|
|
|
|
v <- tryNonAsync $ lockContentForRemoval k $ \contentlock -> do
|
|
|
|
removeAnnex contentlock
|
|
|
|
logStatus k InfoMissing
|
|
|
|
return True
|
|
|
|
case v of
|
|
|
|
Left _ -> return Nothing
|
|
|
|
Right result -> runner (next result)
|
|
|
|
TryLockContent k protoaction next -> do
|
|
|
|
v <- tryNonAsync $ lockContentShared k $ \verifiedcopy ->
|
|
|
|
case verifiedcopy of
|
|
|
|
LockedCopy _ -> runner (protoaction True)
|
|
|
|
_ -> runner (protoaction False)
|
|
|
|
-- If locking fails, lockContentShared throws an exception.
|
|
|
|
-- Let the peer know it failed.
|
|
|
|
case v of
|
|
|
|
Left _ -> runner $ do
|
|
|
|
protoaction False
|
|
|
|
next
|
|
|
|
Right _ -> runner next
|
2016-12-02 20:39:01 +00:00
|
|
|
where
|
|
|
|
transfer mk k af a = case runmode of
|
|
|
|
-- Update transfer logs when serving.
|
|
|
|
Serving theiruuid ->
|
|
|
|
mk theiruuid k af noRetry (const a) noNotification
|
|
|
|
-- Transfer logs are updated higher in the stack when
|
|
|
|
-- a client.
|
|
|
|
Client -> a
|