git-annex 10.20240927 → 10.20241031
raw patch · 46 files changed
+657/−226 lines, 46 filesdep ~stm
Dependency ranges changed: stm
Files
- Annex/AdjustedBranch.hs +4/−4
- Annex/AdjustedBranch/Name.hs +3/−3
- Annex/Ingest.hs +2/−2
- Annex/Proxy.hs +141/−43
- Annex/View.hs +1/−1
- CHANGELOG +29/−0
- Command/Adjust.hs +20/−8
- Command/P2PHttp.hs +31/−6
- Database/RepoSize.hs +5/−0
- P2P/Http.hs +14/−1
- P2P/Http/Client.hs +77/−63
- P2P/Http/Server.hs +86/−37
- P2P/Http/State.hs +35/−11
- P2P/Http/Types.hs +4/−0
- P2P/Protocol.hs +33/−11
- P2P/Proxy.hs +25/−3
- Remote/Adb.hs +1/−0
- Remote/BitTorrent.hs +3/−0
- Remote/Borg.hs +1/−0
- Remote/Bup.hs +1/−0
- Remote/Ddar.hs +1/−0
- Remote/Directory.hs +2/−1
- Remote/Directory/LegacyChunked.hs +2/−2
- Remote/External.hs +14/−3
- Remote/External/Types.hs +6/−0
- Remote/GCrypt.hs +3/−2
- Remote/Git.hs +2/−1
- Remote/GitLFS.hs +1/−0
- Remote/Glacier.hs +2/−1
- Remote/Helper/Chunked.hs +11/−5
- Remote/Helper/Special.hs +9/−8
- Remote/Hook.hs +1/−0
- Remote/HttpAlso.hs +1/−0
- Remote/P2P.hs +1/−0
- Remote/Rsync.hs +1/−0
- Remote/S3.hs +1/−0
- Remote/Tahoe.hs +3/−0
- Remote/Web.hs +1/−0
- Remote/WebDAV.hs +1/−0
- Types/AdjustedBranch.hs +5/−5
- Types/Key.hs +1/−0
- Types/Remote.hs +2/−0
- Types/StoreRetrieve.hs +5/−2
- Utility/OpenFile.hs +38/−0
- Utility/STM.hs +23/−0
- git-annex.cabal +4/−3
Annex/AdjustedBranch.hs view
@@ -11,7 +11,7 @@ Adjustment(..), LinkAdjustment(..), PresenceAdjustment(..),- LinkPresentAdjustment(..),+ LockUnlockPresentAdjustment(..), adjustmentHidesFiles, adjustmentIsStable, OrigBranch,@@ -88,11 +88,11 @@ adjustTreeItem p t >>= \case Nothing -> return Nothing Just t' -> adjustTreeItem l t'- adjustTreeItem (LinkPresentAdjustment l) t = adjustTreeItem l t+ adjustTreeItem (LockUnlockPresentAdjustment l) t = adjustTreeItem l t adjustmentIsStable (LinkAdjustment l) = adjustmentIsStable l adjustmentIsStable (PresenceAdjustment p _) = adjustmentIsStable p- adjustmentIsStable (LinkPresentAdjustment l) = adjustmentIsStable l+ adjustmentIsStable (LockUnlockPresentAdjustment l) = adjustmentIsStable l instance AdjustTreeItem LinkAdjustment where adjustTreeItem UnlockAdjustment =@@ -115,7 +115,7 @@ adjustmentIsStable HideMissingAdjustment = False adjustmentIsStable ShowMissingAdjustment = True -instance AdjustTreeItem LinkPresentAdjustment where+instance AdjustTreeItem LockUnlockPresentAdjustment where adjustTreeItem UnlockPresentAdjustment = ifPresent adjustToPointer adjustToSymlink adjustTreeItem LockPresentAdjustment =
Annex/AdjustedBranch/Name.hs view
@@ -37,7 +37,7 @@ serializeAdjustment p serializeAdjustment (PresenceAdjustment p (Just l)) = serializeAdjustment p <> "-" <> serializeAdjustment l- serializeAdjustment (LinkPresentAdjustment l) =+ serializeAdjustment (LockUnlockPresentAdjustment l) = serializeAdjustment l deserializeAdjustment s = (LinkAdjustment <$> deserializeAdjustment s)@@ -46,7 +46,7 @@ <|> (PresenceAdjustment <$> deserializeAdjustment s <*> pure Nothing) <|>- (LinkPresentAdjustment <$> deserializeAdjustment s)+ (LockUnlockPresentAdjustment <$> deserializeAdjustment s) where (s1, s2) = separate' (== (fromIntegral (ord '-'))) s @@ -68,7 +68,7 @@ deserializeAdjustment "showmissing" = Just ShowMissingAdjustment deserializeAdjustment _ = Nothing -instance SerializeAdjustment LinkPresentAdjustment where+instance SerializeAdjustment LockUnlockPresentAdjustment where serializeAdjustment UnlockPresentAdjustment = "unlockpresent" serializeAdjustment LockPresentAdjustment = "lockpresent" deserializeAdjustment "unlockpresent" = Just UnlockPresentAdjustment
Annex/Ingest.hs view
@@ -359,8 +359,8 @@ go (LinkAdjustment UnFixAdjustment) = False go (PresenceAdjustment _ (Just la)) = go (LinkAdjustment la) go (PresenceAdjustment _ Nothing) = False- go (LinkPresentAdjustment UnlockPresentAdjustment) = contentpresent- go (LinkPresentAdjustment LockPresentAdjustment) = False+ go (LockUnlockPresentAdjustment UnlockPresentAdjustment) = contentpresent+ go (LockUnlockPresentAdjustment LockPresentAdjustment) = False {- Adds a file to the work tree for the key, and stages it in the index. - The content of the key may be provided in a temp file, which will be
Annex/Proxy.hs view
@@ -5,6 +5,8 @@ - Licensed under the GNU AGPL version 3 or higher. -} +{-# LANGUAGE CPP #-}+ module Annex.Proxy where import Annex.Common@@ -28,14 +30,20 @@ import Utility.Metered import Git.Types import qualified Database.Export as Export+#ifndef mingw32_HOST_OS+import Utility.OpenFile+#endif +import Control.Concurrent import Control.Concurrent.STM import Control.Concurrent.Async import qualified Data.ByteString as B+import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as L import qualified System.FilePath.ByteString as P import qualified Data.Map as M import qualified Data.Set as S+import System.IO.Unsafe proxyRemoteSide :: ProtocolVersion -> Bypass -> Remote -> Annex RemoteSide proxyRemoteSide clientmaxversion bypass r@@ -104,10 +112,7 @@ go :: Annex () go = liftIO receivemessage >>= \case Just (CHECKPRESENT k) -> do- tryNonAsync (Remote.checkPresent r k) >>= \case- Right True -> liftIO $ sendmessage SUCCESS- Right False -> liftIO $ sendmessage FAILURE- Left err -> liftIO $ propagateerror err+ checkpresent k go Just (LOCKCONTENT _) -> do -- Special remotes do not support locking content.@@ -171,35 +176,47 @@ withTmpDirIn (fromRawFilePath othertmpdir) "proxy" $ \tmpdir -> a (toRawFilePath tmpdir P.</> keyFile k) - -- Verify the content received from the client, to avoid bad content- -- being stored in the special remote. proxyput af k = do liftIO $ sendmessage $ PUT_FROM (Offset 0)- withproxytmpfile k $ \tmpfile -> do- let store = tryNonAsync (storeput k af (decodeBS tmpfile)) >>= \case- Right () -> liftIO $ sendmessage SUCCESS- Left err -> liftIO $ propagateerror err- liftIO receivemessage >>= \case- Just (DATA (Len len)) -> do- iv <- startVerifyKeyContentIncrementally Remote.AlwaysVerify k- h <- liftIO $ openFile (fromRawFilePath tmpfile) WriteMode- gotall <- liftIO $ receivetofile iv h len- liftIO $ hClose h- verified <- if gotall- then fst <$> finishVerifyKeyContentIncrementally' True iv- else pure False- if protoversion > ProtocolVersion 1- then liftIO receivemessage >>= \case- Just (VALIDITY Valid)- | verified -> store- | otherwise -> liftIO $ sendmessage FAILURE- Just (VALIDITY Invalid) ->+ liftIO receivemessage >>= \case+ Just (DATA (Len len)) -> withproxytmpfile k $ \tmpfile -> do+ -- Verify the content received from+ -- the client, to avoid bad content+ -- being stored in the special remote.+ iv <- startVerifyKeyContentIncrementally Remote.AlwaysVerify k+ h <- liftIO $ openFile (fromRawFilePath tmpfile) WriteMode+ let nuketmp = liftIO $ removeWhenExistsWith removeFile (fromRawFilePath tmpfile)+ gotall <- liftIO $ receivetofile iv h len+ liftIO $ hClose h+ verified <- if gotall+ then fst <$> finishVerifyKeyContentIncrementally' True iv+ else pure False+ let store = tryNonAsync (storeput k af (decodeBS tmpfile)) >>= \case+ Right () -> liftIO $ sendmessage SUCCESS+ Left err -> liftIO $ propagateerror err+ if protoversion > ProtocolVersion 1+ then liftIO receivemessage >>= \case+ Just (VALIDITY Valid)+ | verified -> store >> nuketmp+ | otherwise -> do+ nuketmp liftIO $ sendmessage FAILURE- _ -> giveup "protocol error"- else store- _ -> giveup "protocol error"- liftIO $ removeWhenExistsWith removeFile (fromRawFilePath tmpfile)+ Just (VALIDITY Invalid) -> do+ nuketmp+ liftIO $ sendmessage FAILURE+ _ -> do+ nuketmp+ giveup "protocol error"+ else store >> nuketmp+ Just DATA_PRESENT -> checkpresent k+ _ -> giveup "protocol error" + checkpresent k = + tryNonAsync (Remote.checkPresent r k) >>= \case+ Right True -> liftIO $ sendmessage SUCCESS+ Right False -> liftIO $ sendmessage FAILURE+ Left err -> liftIO $ propagateerror err+ storeput k af tmpfile = case mexportdb of Just exportdb -> liftIO (Export.getExportTree exportdb k) >>= \case [] -> storeputkey k af tmpfile@@ -221,11 +238,11 @@ receivetofile iv h n = liftIO receivebytestring >>= \case Just b -> do+ n' <- storetofile iv h n (L.toChunks b) liftIO $ atomically $ putTMVar owaitv () `orElse` readTMVar oclosedv- n' <- storetofile iv h n (L.toChunks b) -- Normally all the data is sent in a single -- lazy bytestring. However, when the special -- remote is a node in a cluster, a PUT is@@ -241,25 +258,101 @@ storetofile iv h (n - fromIntegral (B.length b)) bs proxyget offset af k = withproxytmpfile k $ \tmpfile -> do+ let retrieve = tryNonAsync $ Remote.retrieveKeyFile r k af+ (fromRawFilePath tmpfile) nullMeterUpdate vc+ ordered <- Remote.retrieveKeyFileInOrder r+ case fromKey keySize k of+#ifndef mingw32_HOST_OS+ Just size | size > 0 && ordered -> do+ cancelv <- liftIO newEmptyMVar+ donev <- liftIO newEmptyMVar+ streamer <- liftIO $ async $+ streamdata offset tmpfile size cancelv donev+ retrieve >>= \case+ Right _ -> liftIO $ do+ putMVar donev ()+ wait streamer+ Left err -> liftIO $ do+ putMVar cancelv ()+ wait streamer+ propagateerror err+#endif+ _ -> retrieve >>= \case+ Right _ -> liftIO $ senddata offset tmpfile+ Left err -> liftIO $ propagateerror err+ where -- Don't verify the content from the remote, -- because the client will do its own verification.- let vc = Remote.NoVerify- tryNonAsync (Remote.retrieveKeyFile r k af (fromRawFilePath tmpfile) nullMeterUpdate vc) >>= \case- Right _ -> liftIO $ senddata offset tmpfile- Left err -> liftIO $ propagateerror err- + vc = Remote.NoVerify++#ifndef mingw32_HOST_OS+ streamdata (Offset offset) f size cancelv donev = do+ sendlen offset size+ waitforfile+ x <- tryNonAsync $ do+ h <- openFileBeingWritten f+ hSeek h AbsoluteSeek offset+ senddata' h (getcontents size)+ case x of+ Left err -> do+ hPutStrLn stderr (show err)+ throwM err+ Right res -> return res+ where+ -- The file doesn't exist at the start.+ -- Wait for some data to be written to it as well,+ -- in case an empty file is first created and then+ -- overwritten. When there is an offset, wait for+ -- the file to get that large. Note that this is not used+ -- when the size is 0.+ waitforfile = tryNonAsync (fromIntegral <$> getFileSize f) >>= \case+ Right sz | sz > 0 && sz >= offset -> return ()+ _ -> ifM (isEmptyMVar cancelv <&&> isEmptyMVar donev)+ ( do+ threadDelay 40000+ waitforfile+ , do+ return ()+ )++ getcontents n h = unsafeInterleaveIO $ do+ isdone <- (not <$> isEmptyMVar donev) <||> (not <$> isEmptyMVar cancelv)+ c <- BS.hGet h defaultChunkSize+ let n' = n - fromIntegral (BS.length c)+ let c' = L.fromChunks [BS.take (fromIntegral n) c]+ if BS.null c+ then if isdone+ then return mempty+ else do+ -- Wait for more data to be+ -- written to the file.+ threadDelay 40000+ getcontents n h+ else if n' > 0+ then do+ -- unsafeInterleaveIO causes + -- this to be deferred until+ -- data is read from the lazy+ -- ByteString.+ cs <- getcontents n' h+ return $ L.append c' cs+ else return c'+#endif+ senddata (Offset offset) f = do size <- fromIntegral <$> getFileSize f- let n = max 0 (size - offset)- sendmessage $ DATA (Len n)+ sendlen offset size withBinaryFile (fromRawFilePath f) ReadMode $ \h -> do hSeek h AbsoluteSeek offset- sendbs =<< L.hGetContents h- -- Important to keep the handle open until- -- the client responds. The bytestring- -- could still be lazily streaming out to- -- the client.- waitclientresponse+ senddata' h L.hGetContents++ senddata' h getcontents = do+ sendbs =<< getcontents h+ -- Important to keep the handle open until+ -- the client responds. The bytestring+ -- could still be lazily streaming out to+ -- the client.+ waitclientresponse where sendbs bs = do sendbytestring bs@@ -272,6 +365,11 @@ Just FAILURE -> return () Just _ -> giveup "protocol error" Nothing -> return ()+ + sendlen offset size = do+ let n = max 0 (size - offset)+ sendmessage $ DATA (Len n)+ {- Check if this repository can proxy for a specified remote uuid, - and if so enable proxying for it. -}
Annex/View.hs view
@@ -513,7 +513,7 @@ Nothing -> stagesymlink uh f k Just (LinkAdjustment UnlockAdjustment) -> stagepointerfile uh f k mtreeitemtype- Just (LinkPresentAdjustment UnlockPresentAdjustment) ->+ Just (LockUnlockPresentAdjustment UnlockPresentAdjustment) -> ifM (inAnnex k) ( stagepointerfile uh f k mtreeitemtype , stagesymlink uh f k
CHANGELOG view
@@ -1,3 +1,32 @@+git-annex (10.20241031) upstream; urgency=medium++ * Sped up proxied downloads from special remotes, by streaming.+ * Added GETORDERED request to external special remote protocol. + When the external special remote responds with ORDERED, it can stream+ through a proxy.+ * p2phttp: Support serving unauthenticated users while requesting+ authentication for operations that need it. Eg, --unauth-readonly+ can be combined with --authenv.+ * p2phttp: Allow unauthenticated users to lock content by default.+ * p2phttp: Added --unauth-nolocking option to prevent unauthenticated+ users from locking content.+ * Allow enabling the servant build flag with older versions of stm,+ allowing building with ghc 9.0.2.+ * git-remote-annex: Fix bug that prevented using it with external special+ remotes, leading to protocol error messages involving "GITMANIFEST".+ * adjust: Allow any order of options when combining --hide-missing with+ options like --unlock.+ * Support P2P protocol version 4. This allows DATA-PRESENT to be sent+ after PUT (and in the HTTP P2P protocol, v4/put has a data-present+ parameter). When used with a proxy to a special remote like a S3+ bucket, this allows a custom client to upload content to S3 itself,+ and then use the P2P protocol to inform the proxy that the content has+ been stored there, which will result in the same git-annex branch state+ updates as sending DATA via the proxy.+ * Fix hang when receiving a large file into a proxied special remote.++ -- Joey Hess <id@joeyh.name> Thu, 31 Oct 2024 17:19:56 -0400+ git-annex (10.20240927) upstream; urgency=medium * Detect when a preferred content expression contains "not present",
Command/Adjust.hs view
@@ -17,9 +17,24 @@ optParser :: CmdParamsDesc -> Parser Adjustment optParser _ =- (LinkAdjustment <$> linkAdjustmentParser)- <|> (PresenceAdjustment <$> presenceAdjustmentParser <*> maybeLinkAdjustmentParser)- <|> (LinkPresentAdjustment <$> linkPresentAdjustmentParser)+ linkPresentAdjustmentParser+ <|> (LockUnlockPresentAdjustment <$> lockUnlockPresentAdjustmentParser)+ +linkPresentAdjustmentParser :: Parser Adjustment+linkPresentAdjustmentParser = comb <$> some ps+ where+ ps = (LinkAdjustment <$> linkAdjustmentParser)+ <|> (PresenceAdjustment <$> presenceAdjustmentParser <*> pure Nothing)+ comb (LinkAdjustment _ : LinkAdjustment b : c) =+ comb (LinkAdjustment b : c)+ comb (PresenceAdjustment _a1 a2 : PresenceAdjustment b1 b2 : c) = + comb (PresenceAdjustment b1 (b2 <|> a2) : c)+ comb (LinkAdjustment a : PresenceAdjustment b1 b2 : c) =+ comb (PresenceAdjustment b1 (b2 <|> Just a) : c)+ comb (PresenceAdjustment a1 _a2 : LinkAdjustment b : c) =+ comb (PresenceAdjustment a1 (Just b) : c)+ comb (a : _) = a+ comb [] = error "internal" linkAdjustmentParser :: Parser LinkAdjustment linkAdjustmentParser =@@ -36,9 +51,6 @@ <> help "fix symlinks to annnexed files" ) -maybeLinkAdjustmentParser :: Parser (Maybe LinkAdjustment)-maybeLinkAdjustmentParser = Just <$> linkAdjustmentParser <|> pure Nothing- presenceAdjustmentParser :: Parser PresenceAdjustment presenceAdjustmentParser = flag' HideMissingAdjustment@@ -46,8 +58,8 @@ <> help "hide annexed files whose content is not present" ) -linkPresentAdjustmentParser :: Parser LinkPresentAdjustment-linkPresentAdjustmentParser =+lockUnlockPresentAdjustmentParser :: Parser LockUnlockPresentAdjustment+lockUnlockPresentAdjustmentParser = flag' UnlockPresentAdjustment ( long "unlock-present" <> help "unlock files whose content is present; lock rest"
Command/P2PHttp.hs view
@@ -40,6 +40,7 @@ , authEnvHttpOption :: Bool , unauthReadOnlyOption :: Bool , unauthAppendOnlyOption :: Bool+ , unauthNoLockingOption :: Bool , wideOpenOption :: Bool , proxyConnectionsOption :: Maybe Integer , clusterJobsOption :: Maybe Int@@ -84,6 +85,10 @@ <> help "allow unauthenticated users to read and append to the repository" ) <*> switch+ ( long "unauth-nolocking"+ <> help "prevent unauthenticated users from locking content in the repository"+ )+ <*> switch ( long "wideopen" <> help "give unauthenticated users full read+write access" )@@ -128,10 +133,25 @@ mkGetServerMode :: M.Map Auth P2P.ServerMode -> Options -> GetServerMode mkGetServerMode _ o _ Nothing- | wideOpenOption o = Just P2P.ServeReadWrite- | unauthAppendOnlyOption o = Just P2P.ServeAppendOnly- | unauthReadOnlyOption o = Just P2P.ServeReadOnly- | otherwise = Nothing+ | wideOpenOption o = ServerMode+ { serverMode = P2P.ServeReadWrite+ , unauthenticatedLockingAllowed = unauthlock+ , authenticationAllowed = False + }+ | unauthAppendOnlyOption o = ServerMode + { serverMode = P2P.ServeAppendOnly+ , unauthenticatedLockingAllowed = unauthlock+ , authenticationAllowed = canauth+ }+ | unauthReadOnlyOption o = ServerMode+ { serverMode = P2P.ServeReadOnly+ , unauthenticatedLockingAllowed = unauthlock+ , authenticationAllowed = canauth+ }+ | otherwise = CannotServeRequests+ where+ canauth = authEnvOption o || authEnvHttpOption o+ unauthlock = not (unauthNoLockingOption o) mkGetServerMode authenv o issecure (Just auth) = case (issecure, authEnvOption o, authEnvHttpOption o) of (Secure, True, _) -> checkauth@@ -139,9 +159,14 @@ _ -> noauth where checkauth = case M.lookup auth authenv of- Just servermode -> Just servermode+ Just servermode -> ServerMode + { serverMode = servermode+ , authenticationAllowed = False+ , unauthenticatedLockingAllowed = False+ } Nothing -> noauth- noauth = mkGetServerMode authenv o issecure Nothing+ noauth = mkGetServerMode authenv noautho issecure Nothing+ noautho = o { authEnvOption = False, authEnvHttpOption = False } getAuthEnv :: IO (M.Map Auth P2P.ServerMode) getAuthEnv = do
Database/RepoSize.hs view
@@ -400,6 +400,11 @@ map (\(k, v) -> (k, [v])) $ fromMaybe [] $ M.lookup u livechanges+ -- This could be optimised to a single SQL join, rather+ -- than querying once for each live change. That would make+ -- it less expensive when there are a lot happening at the+ -- same time. Persistent is not capable of that join,+ -- it would need a dependency on esquelito. livechanges' <- combinelikelivechanges <$> filterM (nonredundantlivechange livechangesbykey u) (fromMaybe [] $ M.lookup u livechanges)
P2P/Http.hs view
@@ -25,34 +25,46 @@ import qualified Data.ByteString as B type P2PHttpAPI- = "git-annex" :> SU :> PV3 :> "key" :> GetAPI+ = "git-annex" :> SU :> PV4 :> "key" :> GetAPI+ :<|> "git-annex" :> SU :> PV3 :> "key" :> GetAPI :<|> "git-annex" :> SU :> PV2 :> "key" :> GetAPI :<|> "git-annex" :> SU :> PV1 :> "key" :> GetAPI :<|> "git-annex" :> SU :> PV0 :> "key" :> GetAPI+ :<|> "git-annex" :> SU :> PV4 :> "checkpresent" :> CheckPresentAPI :<|> "git-annex" :> SU :> PV3 :> "checkpresent" :> CheckPresentAPI :<|> "git-annex" :> SU :> PV2 :> "checkpresent" :> CheckPresentAPI :<|> "git-annex" :> SU :> PV1 :> "checkpresent" :> CheckPresentAPI :<|> "git-annex" :> SU :> PV0 :> "checkpresent" :> CheckPresentAPI+ :<|> "git-annex" :> SU :> PV4 :> "remove" :> RemoveAPI RemoveResultPlus :<|> "git-annex" :> SU :> PV3 :> "remove" :> RemoveAPI RemoveResultPlus :<|> "git-annex" :> SU :> PV2 :> "remove" :> RemoveAPI RemoveResultPlus :<|> "git-annex" :> SU :> PV1 :> "remove" :> RemoveAPI RemoveResult :<|> "git-annex" :> SU :> PV0 :> "remove" :> RemoveAPI RemoveResult+ :<|> "git-annex" :> SU :> PV4 :> "remove-before" :> RemoveBeforeAPI :<|> "git-annex" :> SU :> PV3 :> "remove-before" :> RemoveBeforeAPI+ :<|> "git-annex" :> SU :> PV4 :> "gettimestamp" :> GetTimestampAPI :<|> "git-annex" :> SU :> PV3 :> "gettimestamp" :> GetTimestampAPI+ :<|> "git-annex" :> SU :> PV4 :> "put" + :> QueryParam "data-present" Bool+ :> PutAPI PutResultPlus :<|> "git-annex" :> SU :> PV3 :> "put" :> PutAPI PutResultPlus :<|> "git-annex" :> SU :> PV2 :> "put" :> PutAPI PutResultPlus :<|> "git-annex" :> SU :> PV1 :> "put" :> PutAPI PutResult :<|> "git-annex" :> SU :> PV0 :> "put" :> PutAPI PutResult+ :<|> "git-annex" :> SU :> PV4 :> "putoffset"+ :> PutOffsetAPI PutOffsetResultPlus :<|> "git-annex" :> SU :> PV3 :> "putoffset" :> PutOffsetAPI PutOffsetResultPlus :<|> "git-annex" :> SU :> PV2 :> "putoffset" :> PutOffsetAPI PutOffsetResultPlus :<|> "git-annex" :> SU :> PV1 :> "putoffset" :> PutOffsetAPI PutOffsetResult+ :<|> "git-annex" :> SU :> PV4 :> "lockcontent" :> LockContentAPI :<|> "git-annex" :> SU :> PV3 :> "lockcontent" :> LockContentAPI :<|> "git-annex" :> SU :> PV2 :> "lockcontent" :> LockContentAPI :<|> "git-annex" :> SU :> PV1 :> "lockcontent" :> LockContentAPI :<|> "git-annex" :> SU :> PV0 :> "lockcontent" :> LockContentAPI+ :<|> "git-annex" :> SU :> PV4 :> "keeplocked" :> KeepLockedAPI :<|> "git-annex" :> SU :> PV3 :> "keeplocked" :> KeepLockedAPI :<|> "git-annex" :> SU :> PV2 :> "keeplocked" :> KeepLockedAPI :<|> "git-annex" :> SU :> PV1 :> "keeplocked" :> KeepLockedAPI@@ -177,6 +189,7 @@ type AuthHeader = Header "Authorization" Auth +type PV4 = Capture "v4" V4 type PV3 = Capture "v3" V3 type PV2 = Capture "v2" V2 type PV1 = Capture "v1" V1
P2P/Http/Client.hs view
@@ -36,6 +36,7 @@ import Annex.Concurrent import Utility.Url (BasicAuth(..)) import Utility.HumanTime+import Utility.STM import qualified Git.Credential as Git import Servant hiding (BasicAuthData(..))@@ -46,7 +47,6 @@ import qualified Data.ByteString as B import qualified Data.ByteString.Lazy.Internal as LI import qualified Data.Map as M-import Control.Concurrent.STM import Control.Concurrent.Async import Control.Concurrent import System.IO.Unsafe@@ -187,13 +187,14 @@ if dl == len then Valid else Invalid where cli =case ver of+ 4 -> v4 su V4 3 -> v3 su V3 2 -> v2 su V2 1 -> v1 su V1 0 -> v0 su V0 _ -> error "unsupported protocol version" - v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI+ v4 :<|> v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI gather = unsafeInterleaveIO . gather' gather' S.Stop = return LI.Empty@@ -215,14 +216,15 @@ Right (CheckPresentResult res) -> return (Right res) where cli = case ver of+ 4 -> flip v4 V4 3 -> flip v3 V3 2 -> flip v2 V2 1 -> flip v1 V1 0 -> flip v0 V0 _ -> error "unsupported protocol version" - _ :<|> _ :<|> _ :<|> _ :<|>- v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ v4 :<|> v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI #else clientCheckPresent _ = () #endif@@ -264,15 +266,16 @@ bk = B64Key k cli = case ver of+ 4 -> v4 su V4 bk cu bypass auth 3 -> v3 su V3 bk cu bypass auth 2 -> v2 su V2 bk cu bypass auth 1 -> plus <$> v1 su V1 bk cu bypass auth 0 -> plus <$> v0 su V0 bk cu bypass auth _ -> error "unsupported protocol version" - _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|> _ :<|> _ :<|> _ :<|>- v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ v4 :<|> v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI #else clientRemove _ = () #endif@@ -286,13 +289,14 @@ liftIO $ withClientM (cli su (B64Key k) cu bypass ts auth) clientenv return where cli = case ver of+ 4 -> flip v4 V4 3 -> flip v3 V3 _ -> error "unsupported protocol version" - _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|> _ :<|> _ :<|> _ :<|>- v3 :<|> _ = client p2pHttpAPI+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|>_ :<|> _ :<|> _ :<|> _ :<|>+ v4 :<|> v3 :<|> _ = client p2pHttpAPI #else clientRemoveBefore _ _ = () #endif@@ -303,14 +307,15 @@ liftIO $ withClientM (cli su cu bypass auth) clientenv return where cli = case ver of+ 4 -> flip v4 V4 3 -> flip v3 V3 _ -> error "unsupported protocol version" - _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|>- v3 :<|> _ = client p2pHttpAPI+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|>+ v4 :<|> v3 :<|> _ = client p2pHttpAPI #else clientGetTimestamp = () #endif@@ -324,27 +329,32 @@ -> FileSize -> Annex Bool -- ^ Called after sending the file to check if it's valid.+ -> Bool+ -- ^ Set data-present parameter and do not actually send data+ -- (v4+ only) -> ClientAction PutResultPlus #ifdef WITH_SERVANT-clientPut meterupdate k moffset af contentfile contentfilesize validitycheck clientenv (ProtocolVersion ver) su cu bypass auth = do- checkv <- liftIO newEmptyTMVarIO- checkresultv <- liftIO newEmptyTMVarIO- let checker = do- liftIO $ atomically $ takeTMVar checkv- validitycheck >>= liftIO . atomically . putTMVar checkresultv- checkerthread <- liftIO . async =<< forkState checker- v <- liftIO $ withBinaryFile contentfile ReadMode $ \h -> do- when (offset /= 0) $- hSeek h AbsoluteSeek offset- withClientM (cli (stream h checkv checkresultv)) clientenv return- case v of- Left err -> do- void $ liftIO $ atomically $ tryPutTMVar checkv ()- join $ liftIO (wait checkerthread)- return (Left err)- Right res -> do- join $ liftIO (wait checkerthread)- return (Right res)+clientPut meterupdate k moffset af contentfile contentfilesize validitycheck datapresent clientenv (ProtocolVersion ver) su cu bypass auth+ | datapresent = liftIO $ withClientM (cli mempty) clientenv return+ | otherwise = do+ checkv <- liftIO newEmptyTMVarIO+ checkresultv <- liftIO newEmptyTMVarIO+ let checker = do+ liftIO $ atomically $ takeTMVar checkv+ validitycheck >>= liftIO . atomically . putTMVar checkresultv+ checkerthread <- liftIO . async =<< forkState checker+ v <- liftIO $ withBinaryFile contentfile ReadMode $ \h -> do+ when (offset /= 0) $+ hSeek h AbsoluteSeek offset+ withClientM (cli (stream h checkv checkresultv)) clientenv return+ case v of+ Left err -> do+ void $ liftIO $ atomically $ tryPutTMVar checkv ()+ join $ liftIO (wait checkerthread)+ return (Left err)+ Right res -> do+ join $ liftIO (wait checkerthread)+ return (Right res) where stream h checkv checkresultv = S.SourceT $ \a -> do bl <- hGetContentsMetered h meterupdate@@ -396,18 +406,19 @@ bk = B64Key k cli src = case ver of+ 4 -> v4 su V4 (if datapresent then Just True else Nothing) len bk cu bypass baf moffset src auth 3 -> v3 su V3 len bk cu bypass baf moffset src auth 2 -> v2 su V2 len bk cu bypass baf moffset src auth 1 -> plus <$> v1 su V1 len bk cu bypass baf moffset src auth 0 -> plus <$> v0 su V0 len bk cu bypass baf moffset src auth _ -> error "unsupported protocol version" - _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|>- _ :<|>- v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|>+ _ :<|> _ :<|>+ v4 :<|> v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI #else clientPut _ _ _ _ _ _ _ = () #endif@@ -423,18 +434,19 @@ bk = B64Key k cli = case ver of+ 4 -> v4 su V4 bk cu bypass auth 3 -> v3 su V3 bk cu bypass auth 2 -> v2 su V2 bk cu bypass auth 1 -> plus <$> v1 su V1 bk cu bypass auth _ -> error "unsupported protocol version" - _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|>- _ :<|>- _ :<|> _ :<|> _ :<|> _ :<|>- v3 :<|> v2 :<|> v1 :<|> _ = client p2pHttpAPI+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|>+ _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ v4 :<|> v3 :<|> v2 :<|> v1 :<|> _ = client p2pHttpAPI #else clientPutOffset _ = () #endif@@ -447,20 +459,21 @@ liftIO $ withClientM (cli (B64Key k) cu bypass auth) clientenv return where cli = case ver of+ 4 -> v4 su V4 3 -> v3 su V3 2 -> v2 su V2 1 -> v1 su V1 0 -> v0 su V0 _ -> error "unsupported protocol version" - _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|>- _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|>+ _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|> _ :<|> _ :<|>- v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI+ v4 :<|> v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI #else clientLockContent _ = () #endif@@ -518,21 +531,22 @@ else S.Yield (UnlockRequest True) S.Stop cli = case ver of+ 4 -> v4 su V4 3 -> v3 su V3 2 -> v2 su V2 1 -> v1 su V1 0 -> v0 su V0 _ -> error "unsupported protocol version" - _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|>- _ :<|>- _ :<|> _ :<|> _ :<|> _ :<|>- _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ _ :<|> _ :<|>+ _ :<|> _ :<|>+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>- v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI+ _ :<|> _ :<|> _ :<|> _ :<|> _ :<|>+ v4 :<|> v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI #else clientKeepLocked _ _ _ _ = () #endif
P2P/Http/Server.hs view
@@ -26,19 +26,20 @@ import P2P.Http.Types import P2P.Http.State import P2P.Protocol hiding (Offset, Bypass, auth)+import qualified P2P.Protocol import P2P.IO import P2P.Annex import Annex.WorkerPool import Types.WorkerPool import Types.Direction import Utility.Metered+import Utility.STM import Servant import qualified Servant.Types.SourceT as S import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy.Internal as LI-import Control.Concurrent.STM import Control.Concurrent.Async import Control.Concurrent import System.IO.Unsafe@@ -53,31 +54,40 @@ :<|> serveGet st :<|> serveGet st :<|> serveGet st+ :<|> serveGet st :<|> serveCheckPresent st :<|> serveCheckPresent st :<|> serveCheckPresent st :<|> serveCheckPresent st+ :<|> serveCheckPresent st :<|> serveRemove st id :<|> serveRemove st id+ :<|> serveRemove st id :<|> serveRemove st dePlus :<|> serveRemove st dePlus :<|> serveRemoveBefore st+ :<|> serveRemoveBefore st :<|> serveGetTimestamp st- :<|> servePut st id+ :<|> serveGetTimestamp st :<|> servePut st id- :<|> servePut st dePlus- :<|> servePut st dePlus+ :<|> servePut' st id+ :<|> servePut' st id+ :<|> servePut' st dePlus+ :<|> servePut' st dePlus :<|> servePutOffset st id :<|> servePutOffset st id+ :<|> servePutOffset st id :<|> servePutOffset st dePlus :<|> serveLockContent st :<|> serveLockContent st :<|> serveLockContent st :<|> serveLockContent st+ :<|> serveLockContent st :<|> serveKeepLocked st :<|> serveKeepLocked st :<|> serveKeepLocked st :<|> serveKeepLocked st+ :<|> serveKeepLocked st :<|> serveGetGeneric st serveGetGeneric@@ -125,10 +135,11 @@ return $ \v -> do liftIO $ atomically $ putTMVar validityv v return True+ let noothermessages = const Nothing enteringStage (TransferStage Upload) $ runFullProto (clientRunState conn) (clientP2PConnection conn) $ void $ receiveContent Nothing nullMeterUpdate- sizer storer getreq+ sizer storer noothermessages getreq void $ liftIO $ forkIO $ waitfinal endv finalv conn annexworker (Len len, bs) <- liftIO $ atomically $ takeTMVar bsv bv <- liftIO $ newMVar (filter (not . B.null) (L.toChunks bs))@@ -297,6 +308,7 @@ -> (PutResultPlus -> t) -> B64UUID ServerSide -> v+ -> Maybe Bool -> DataLength -> B64Key -> B64UUID ClientSide@@ -307,7 +319,15 @@ -> IsSecure -> Maybe Auth -> Handler t-servePut st resultmangle su apiver (DataLength len) (B64Key k) cu bypass baf moffset stream sec auth = do+servePut st resultmangle su apiver (Just True) _ k cu bypass baf _ _ sec auth = do+ res <- withP2PConnection' apiver st cu su bypass sec auth WriteAction+ (\cst -> cst { connectionWaitVar = False }) (liftIO . protoaction)+ servePutResult resultmangle res+ where+ protoaction conn = servePutAction st conn k baf $ \_offset -> do+ net $ sendMessage DATA_PRESENT+ checkSuccessPlus+servePut st resultmangle su apiver _datapresent (DataLength len) k cu bypass baf moffset stream sec auth = do validityv <- liftIO newEmptyTMVarIO let validitycheck = local $ runValidityCheck $ liftIO $ atomically $ readTMVar validityv@@ -317,41 +337,27 @@ (\cst -> cst { connectionWaitVar = False }) $ \conn -> do liftIO $ void $ async $ checktooshort conn tooshortv liftIO (protoaction conn content validitycheck)- case res of- Right (Right (Just plusuuids)) -> return $ resultmangle $- PutResultPlus True (map B64UUID plusuuids)- Right (Right Nothing) -> return $ resultmangle $- PutResultPlus False []- Right (Left protofail) -> throwError $- err500 { errBody = encodeBL (describeProtoFailure protofail) }- Left err -> throwError $- err500 { errBody = encodeBL (show err) }+ servePutResult resultmangle res where- protoaction conn content validitycheck = inAnnexWorker st $- enteringStage (TransferStage Download) $- runFullProto (clientRunState conn) (clientP2PConnection conn) $- protoaction' content validitycheck- - protoaction' content validitycheck = put' k af $ \offset' ->- let offsetdelta = offset' - offset- in case compare offset' offset of- EQ -> sendContent' nullMeterUpdate (Len len)- content validitycheck- GT -> sendContent' nullMeterUpdate- (Len (len - fromIntegral offsetdelta))- (L.drop (fromIntegral offsetdelta) content)- validitycheck- LT -> sendContent' nullMeterUpdate- (Len len)- content- (validitycheck >>= \_ -> return Invalid)+ protoaction conn content validitycheck = + servePutAction st conn k baf $ \offset' ->+ let offsetdelta = offset' - offset+ in case compare offset' offset of+ EQ -> sendContent' nullMeterUpdate (Len len)+ content validitycheck+ GT -> sendContent' nullMeterUpdate+ (Len (len - fromIntegral offsetdelta))+ (L.drop (fromIntegral offsetdelta) content)+ validitycheck+ LT -> sendContent' nullMeterUpdate+ (Len len)+ content+ (validitycheck >>= \_ -> return Invalid) offset = case moffset of Just (Offset o) -> o Nothing -> 0 - af = b64FilePathToAssociatedFile baf- -- Streams the ByteString from the client. Avoids returning a longer -- than expected ByteString by truncating to the expected length. -- Returns a shorter than expected ByteString when the data is not@@ -389,6 +395,49 @@ liftIO $ whenM (atomically $ takeTMVar tooshortv) $ closeP2PConnection conn +servePutAction+ :: P2PHttpServerState+ -> P2PConnectionPair+ -> B64Key+ -> Maybe B64FilePath+ -> (P2P.Protocol.Offset -> Proto (Maybe [UUID]))+ -> IO (Either SomeException (Either ProtoFailure (Maybe [UUID])))+servePutAction st conn (B64Key k) baf a = inAnnexWorker st $+ enteringStage (TransferStage Download) $+ runFullProto (clientRunState conn) (clientP2PConnection conn) $+ put' k af a+ where+ af = b64FilePathToAssociatedFile baf++servePutResult :: (PutResultPlus -> t) -> Either SomeException (Either ProtoFailure (Maybe [UUID])) -> Handler t+servePutResult resultmangle res = case res of+ Right (Right (Just plusuuids)) -> return $ resultmangle $+ PutResultPlus True (map B64UUID plusuuids)+ Right (Right Nothing) -> return $ resultmangle $+ PutResultPlus False []+ Right (Left protofail) -> throwError $+ err500 { errBody = encodeBL (describeProtoFailure protofail) }+ Left err -> throwError $+ err500 { errBody = encodeBL (show err) }++servePut'+ :: APIVersion v+ => P2PHttpServerState+ -> (PutResultPlus -> t)+ -> B64UUID ServerSide+ -> v+ -> DataLength+ -> B64Key+ -> B64UUID ClientSide+ -> [B64UUID Bypass]+ -> Maybe B64FilePath+ -> Maybe Offset+ -> S.SourceT IO B.ByteString+ -> IsSecure+ -> Maybe Auth+ -> Handler t+servePut' st resultmangle su v = servePut st resultmangle su v Nothing+ servePutOffset :: APIVersion v => P2PHttpServerState@@ -425,7 +474,7 @@ -> Maybe Auth -> Handler LockResult serveLockContent st su apiver (B64Key k) cu bypass sec auth = do- conn <- getP2PConnection apiver st cu su bypass sec auth WriteAction id+ conn <- getP2PConnection apiver st cu su bypass sec auth LockAction id let lock = do lockresv <- newEmptyTMVarIO unlockv <- newEmptyTMVarIO@@ -465,7 +514,7 @@ -> S.SourceT IO UnlockRequest -> Handler LockResult serveKeepLocked st _su _apiver lckid _cu _bypass sec auth _ _ unlockrequeststream = do- checkAuthActionClass st sec auth WriteAction $ \_ -> do+ checkAuthActionClass st sec auth LockAction $ \_ -> do liftIO $ keepingLocked lckid st _ <- liftIO $ S.unSourceT unlockrequeststream go return (LockResult False Nothing)
P2P/Http/State.hs view
@@ -35,12 +35,12 @@ import Annex.Cluster import qualified P2P.Proxy as Proxy import qualified Types.Remote as Remote+import Utility.STM import Servant import qualified Data.Map.Strict as M import qualified Data.Set as S import Control.Concurrent.Async-import Control.Concurrent.STM import Data.Time.Clock.POSIX data P2PHttpServerState = P2PHttpServerState@@ -52,9 +52,16 @@ type AnnexWorkerPool = TMVar (WorkerPool (Annex.AnnexState, Annex.AnnexRead)) --- Nothing when the server is not allowed to serve any requests.-type GetServerMode = IsSecure -> Maybe Auth -> Maybe P2P.ServerMode+type GetServerMode = IsSecure -> Maybe Auth -> ServerMode +data ServerMode+ = ServerMode+ { serverMode :: P2P.ServerMode+ , unauthenticatedLockingAllowed :: Bool+ , authenticationAllowed :: Bool+ }+ | CannotServeRequests+ mkP2PHttpServerState :: AcquireP2PConnection -> AnnexWorkerPool -> GetServerMode -> IO P2PHttpServerState mkP2PHttpServerState acquireconn annexworkerpool getservermode = P2PHttpServerState <$> pure acquireconn@@ -62,7 +69,7 @@ <*> pure getservermode <*> newTMVarIO mempty -data ActionClass = ReadAction | WriteAction | RemoveAction+data ActionClass = ReadAction | WriteAction | RemoveAction | LockAction deriving (Eq) withP2PConnection@@ -143,19 +150,36 @@ -> (P2P.ServerMode -> Handler a) -> Handler a checkAuthActionClass st sec auth actionclass go =- case (getServerMode st sec auth, actionclass) of- (Just P2P.ServeReadWrite, _) -> go P2P.ServeReadWrite- (Just P2P.ServeAppendOnly, RemoveAction) -> throwError err403- (Just P2P.ServeAppendOnly, _) -> go P2P.ServeAppendOnly- (Just P2P.ServeReadOnly, ReadAction) -> go P2P.ServeReadOnly- (Just P2P.ServeReadOnly, _) -> throwError err403- (Nothing, _) -> throwError basicAuthRequired+ case (sm, actionclass) of+ (ServerMode { serverMode = P2P.ServeReadWrite }, _) ->+ go P2P.ServeReadWrite+ (ServerMode { unauthenticatedLockingAllowed = True }, LockAction) ->+ go P2P.ServeReadOnly+ (ServerMode { serverMode = P2P.ServeAppendOnly }, RemoveAction) -> + throwError $ forbiddenWithoutAuth sm+ (ServerMode { serverMode = P2P.ServeAppendOnly }, _) ->+ go P2P.ServeAppendOnly+ (ServerMode { serverMode = P2P.ServeReadOnly }, ReadAction) ->+ go P2P.ServeReadOnly+ (ServerMode { serverMode = P2P.ServeReadOnly }, _) -> + throwError $ forbiddenWithoutAuth sm+ (CannotServeRequests, _) -> throwError basicAuthRequired+ where+ sm = getServerMode st sec auth +forbiddenAction :: ServerError+forbiddenAction = err403+ basicAuthRequired :: ServerError basicAuthRequired = err401 { errHeaders = [(h, v)] } where h = "WWW-Authenticate" v = "Basic realm=\"git-annex\", charset=\"UTF-8\""++forbiddenWithoutAuth :: ServerMode -> ServerError+forbiddenWithoutAuth sm+ | authenticationAllowed sm = basicAuthRequired+ | otherwise = forbiddenAction data ConnectionParams = ConnectionParams { connectionProtocolVersion :: P2P.ProtocolVersion
P2P/Http/Types.hs view
@@ -32,6 +32,7 @@ import Control.DeepSeq import GHC.Generics (Generic) +data V4 = V4 deriving (Show) data V3 = V3 deriving (Show) data V2 = V2 deriving (Show) data V1 = V1 deriving (Show)@@ -40,6 +41,7 @@ class APIVersion v where protocolVersion :: v -> P2P.ProtocolVersion +instance APIVersion V4 where protocolVersion _ = P2P.ProtocolVersion 4 instance APIVersion V3 where protocolVersion _ = P2P.ProtocolVersion 3 instance APIVersion V2 where protocolVersion _ = P2P.ProtocolVersion 2 instance APIVersion V1 where protocolVersion _ = P2P.ProtocolVersion 1@@ -194,11 +196,13 @@ instance FromHttpApiData KeepAlive where parseUrlPiece = Right . KeepAlive +instance ToHttpApiData V4 where toUrlPiece _ = "v4" instance ToHttpApiData V3 where toUrlPiece _ = "v3" instance ToHttpApiData V2 where toUrlPiece _ = "v2" instance ToHttpApiData V1 where toUrlPiece _ = "v1" instance ToHttpApiData V0 where toUrlPiece _ = "v0" +instance FromHttpApiData V4 where parseUrlPiece = parseAPIVersion V4 "v4" instance FromHttpApiData V3 where parseUrlPiece = parseAPIVersion V3 "v3" instance FromHttpApiData V2 where parseUrlPiece = parseAPIVersion V2 "v2" instance FromHttpApiData V1 where parseUrlPiece = parseAPIVersion V1 "v1"
P2P/Protocol.hs view
@@ -61,12 +61,13 @@ defaultProtocolVersion = ProtocolVersion 0 maxProtocolVersion :: ProtocolVersion-maxProtocolVersion = ProtocolVersion 3+maxProtocolVersion = ProtocolVersion 4 -- In order from newest to oldest. allProtocolVersions :: [ProtocolVersion] allProtocolVersions =- [ ProtocolVersion 3+ [ ProtocolVersion 4+ , ProtocolVersion 3 , ProtocolVersion 2 , ProtocolVersion 1 , ProtocolVersion 0@@ -113,6 +114,7 @@ | FAILURE_PLUS [UUID] | BYPASS Bypass | DATA Len -- followed by bytes of data+ | DATA_PRESENT | VALIDITY Validity | TIMESTAMP MonotonicTimestamp | ERROR String@@ -144,6 +146,7 @@ formatMessage (FAILURE_PLUS uuids) = ("FAILURE-PLUS":map Proto.serialize uuids) formatMessage (BYPASS (Bypass uuids)) = ("BYPASS":map Proto.serialize (S.toList uuids)) formatMessage (DATA len) = ["DATA", Proto.serialize len]+ formatMessage DATA_PRESENT = ["DATA-PRESENT"] formatMessage (VALIDITY Valid) = ["VALID"] formatMessage (VALIDITY Invalid) = ["INVALID"] formatMessage (TIMESTAMP ts) = ["TIMESTAMP", Proto.serialize ts]@@ -175,6 +178,7 @@ parseCommand "FAILURE-PLUS" = Proto.parseList FAILURE_PLUS parseCommand "BYPASS" = Proto.parseList (BYPASS . Bypass . S.fromList) parseCommand "DATA" = Proto.parse1 DATA+ parseCommand "DATA-PRESENT" = Proto.parse0 DATA_PRESENT parseCommand "VALID" = Proto.parse0 (VALIDITY Valid) parseCommand "INVALID" = Proto.parse0 (VALIDITY Invalid) parseCommand "TIMESTAMP" = Proto.parse1 TIMESTAMP@@ -477,11 +481,12 @@ get :: FilePath -> Key -> Maybe IncrementalVerifier -> AssociatedFile -> Meter -> MeterUpdate -> Proto (Bool, Verification) get dest key iv af m p = - receiveContent (Just m) p sizer storer $ \offset ->+ receiveContent (Just m) p sizer storer noothermessages $ \offset -> GET offset (ProtoAssociatedFile af) key where sizer = fileSize dest storer = storeContentTo dest iv+ noothermessages = const Nothing put :: Key -> AssociatedFile -> MeterUpdate -> Proto (Maybe [UUID]) put key af p = put' key af $ \offset ->@@ -664,7 +669,16 @@ else do let sizer = tmpContentSize key let storer = storeContent key af- v <- receiveContent Nothing nullMeterUpdate sizer storer PUT_FROM+ ver <- net getProtocolVersion+ let handleothermessages+ | ver >= ProtocolVersion 4 = \case+ DATA_PRESENT -> Just $+ checkContentPresent key+ _ -> Nothing+ | otherwise = const Nothing+ v <- receiveContent Nothing nullMeterUpdate + sizer storer handleothermessages + PUT_FROM when (observeBool v) $ local $ setPresent key myuuid return ServerContinue@@ -743,9 +757,10 @@ -> MeterUpdate -> Local Len -> (Offset -> Len -> Proto L.ByteString -> Proto (Maybe Validity) -> Local t)+ -> (Message -> Maybe (Local t)) -> (Offset -> Message) -> Proto t-receiveContent mm p sizer storer mkmsg = do+receiveContent mm p sizer storer handleothermessages mkmsg = do Len n <- local sizer let p' = offsetMeterUpdate p (toBytesProcessed n) let offset = Offset n@@ -764,16 +779,23 @@ net $ sendMessage (ERROR "expected VALID or INVALID") return Nothing else return Nothing- v <- local $ storer offset len+ sendresultof $ storer offset len (net (receiveBytes len p')) validitycheck- sendSuccess (observeBool v)- return v Just (ERROR _err) -> return observeFailure- _ -> do- net $ sendMessage (ERROR "expected DATA")- return observeFailure+ Just msg -> + maybe unsupportedmessage sendresultof+ (handleothermessages msg)+ Nothing -> unsupportedmessage+ where+ unsupportedmessage = do+ net $ sendMessage (ERROR "expected DATA")+ return observeFailure+ sendresultof a = do+ v <- local a+ sendSuccess (observeBool v)+ return v checkSuccess :: Proto Bool checkSuccess = either (const False) id <$> checkSuccess'
P2P/Proxy.hs view
@@ -302,6 +302,7 @@ FAILURE -> protoerr FAILURE_PLUS _ -> protoerr DATA _ -> protoerr+ DATA_PRESENT -> protoerr VALIDITY _ -> protoerr UNLOCKCONTENT -> protoerr -- If the client errors out, give up.@@ -486,7 +487,7 @@ getresponse client resp $ withDATA (relayPUT remoteside k)- (const protoerr)+ (handlePut_DATA_PRESENT remoteside k) _ -> protoerr handlePUT [] _ _ = protoerrhandler requestcomplete $@@ -563,8 +564,8 @@ let minoffset = minimum (map snd l') getresponse client (PUT_FROM (Offset minoffset)) $ withDATA (relayPUTMulti minoffset l' k)- (const protoerr)- + (handlePutMulti_DATA_PRESENT l' k)+ relayPUTMulti minoffset remotes k (Len datalen) _ = do -- Tell each remote how much data to expect, depending -- on the remote's offset.@@ -664,6 +665,9 @@ Just (Just resp) -> relayPUTRecord k r resp _ -> return Nothing+ relayDATAFinishMulti' storeduuids++ relayDATAFinishMulti' storeduuids = protoerrhandler requestcomplete $ client $ net $ sendMessage $ case concat (catMaybes storeduuids) of@@ -671,6 +675,24 @@ us | proxyClientProtocolVersion proxyparams < ProtocolVersion 2 -> SUCCESS | otherwise -> SUCCESS_PLUS us+ + handlePutMulti_DATA_PRESENT remotes k DATA_PRESENT = do+ storeduuids <- forMC (proxyConcurrencyConfig proxyparams) remotes $ \(remoteside, _) -> do+ resp <- runRemoteSideOrSkipFailed remoteside $ do+ net $ sendMessage DATA_PRESENT+ net receiveMessage+ case resp of+ Just (Just resp') -> relayPUTRecord k remoteside resp'+ _ -> return Nothing+ relayDATAFinishMulti' storeduuids+ handlePutMulti_DATA_PRESENT _ _ _ = protoerr++ handlePut_DATA_PRESENT remoteside k DATA_PRESENT =+ getresponse (runRemoteSide remoteside) DATA_PRESENT $ \resp -> do+ void $ relayPUTRecord k remoteside resp+ protoerrhandler requestcomplete $+ client $ net $ sendMessage resp+ handlePut_DATA_PRESENT _ _ _ = protoerr -- The associated file received from the P2P protocol -- is relative to the top of the git repository. But this process
Remote/Adb.hs view
@@ -81,6 +81,7 @@ , name = Git.repoDescribe r , storeKey = storeKeyDummy , retrieveKeyFile = retrieveKeyFileDummy+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = Nothing , retrievalSecurityPolicy = RetrievalAllKeysSecure , removeKey = removeKeyDummy
Remote/BitTorrent.hs view
@@ -69,6 +69,9 @@ , name = Git.repoDescribe r , storeKey = uploadKey , retrieveKeyFile = downloadKey+ -- Bittorrent downloads out of order, but downloadTorrentContent+ -- moves the downloaded file to the destination at the end.+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = Nothing -- Bittorrent does its own hash checks. , retrievalSecurityPolicy = RetrievalAllKeysSecure
Remote/Borg.hs view
@@ -86,6 +86,7 @@ , name = Git.repoDescribe r , storeKey = storeKeyDummy , retrieveKeyFile = retrieveKeyFileDummy+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = Nothing -- Borg cryptographically verifies content. , retrievalSecurityPolicy = RetrievalAllKeysSecure
Remote/Bup.hs view
@@ -78,6 +78,7 @@ , name = Git.repoDescribe r , storeKey = storeKeyDummy , retrieveKeyFile = retrieveKeyFileDummy+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = Nothing -- Bup uses git, which cryptographically verifies content -- (with SHA1, but sufficiently for this).
Remote/Ddar.hs view
@@ -79,6 +79,7 @@ , name = Git.repoDescribe r , storeKey = storeKeyDummy , retrieveKeyFile = retrieveKeyFileDummy+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = Nothing -- ddar communicates over ssh, not subject to http redirect -- type attacks
Remote/Directory.hs view
@@ -98,6 +98,7 @@ , name = Git.repoDescribe r , storeKey = storeKeyDummy , retrieveKeyFile = retrieveKeyFileDummy+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = retrieveKeyFileCheapM dir chunkconfig , retrievalSecurityPolicy = RetrievalAllKeysSecure , removeKey = removeKeyDummy@@ -173,7 +174,7 @@ locations' :: RawFilePath -> Key -> [RawFilePath] locations' d k = NE.toList (locations d k) -{- Returns the location off a Key in the directory. If the key is+{- Returns the location of a Key in the directory. If the key is - present, returns the location that is actually used, otherwise - returns the first, default location. -} getLocation :: RawFilePath -> Key -> IO RawFilePath
Remote/Directory/LegacyChunked.hs view
@@ -98,7 +98,7 @@ - :/ This is legacy code.. -} retrieve :: (RawFilePath -> Key -> [RawFilePath]) -> RawFilePath -> Retriever-retrieve locations d basek p miv c = withOtherTmp $ \tmpdir -> do+retrieve locations d basek p _dest miv c = withOtherTmp $ \tmpdir -> do showLongNote "This remote uses the deprecated chunksize setting. So this will be quite slow." let tmp = tmpdir P.</> keyFile basek <> ".directorylegacy.tmp" let tmp' = fromRawFilePath tmp@@ -110,7 +110,7 @@ b <- liftIO $ L.readFile tmp' liftIO $ removeWhenExistsWith R.removeLink tmp sink b- byteRetriever go basek p miv c+ byteRetriever go basek p tmp miv c checkKey :: RawFilePath -> (RawFilePath -> Key -> [RawFilePath]) -> Key -> Annex Bool checkKey d locations k = liftIO $
Remote/External.hs view
@@ -68,7 +68,7 @@ | externalprogram' == ExternalType "readonly" = do c <- parsedRemoteConfig remote rc cst <- remoteCost gc c expensiveRemoteCost- let rmt = mk c cst (pure GloballyAvailable)+ let rmt = mk c cst (pure True) (pure GloballyAvailable) Nothing (externalInfo externalprogram') Nothing@@ -105,7 +105,9 @@ let cheapexportsupported = if exportsupported then exportIsSupported else exportUnsupported- let rmt = mk c cst (getAvailability external)+ let rmt = mk c cst+ (getOrdered external)+ (getAvailability external) (Just (whereisKeyM external)) (getInfoM external) (Just (claimUrlM external))@@ -119,13 +121,14 @@ (checkPresentM external) rmt where- mk c cst avail towhereis togetinfo toclaimurl tocheckurl exportactions cheapexportsupported =+ mk c cst ordered avail towhereis togetinfo toclaimurl tocheckurl exportactions cheapexportsupported = Remote { uuid = u , cost = cst , name = Git.repoDescribe r , storeKey = storeKeyDummy , retrieveKeyFile = retrieveKeyFileDummy+ , retrieveKeyFileInOrder = ordered , retrieveKeyFileCheap = Nothing -- External special remotes use many http libraries -- and have no protection against redirects to@@ -800,6 +803,14 @@ UNSUPPORTED_REQUEST -> result defavail _ -> Nothing defavail = GloballyAvailable++getOrdered :: External -> Annex Bool+getOrdered external = catchNonAsync query (const (pure False))+ where+ query = handleRequest external GETORDERED Nothing $ \req -> case req of+ ORDERED -> result True+ UNORDERED -> result False+ _ -> result False claimUrlM :: External -> URLString -> Annex Bool claimUrlM external url =
Remote/External/Types.hs view
@@ -168,6 +168,7 @@ | INITREMOTE | GETCOST | GETAVAILABILITY+ | GETORDERED | CLAIMURL URLString | CHECKURL URLString | TRANSFER Direction SafeKey FilePath@@ -200,6 +201,7 @@ formatMessage INITREMOTE = ["INITREMOTE"] formatMessage GETCOST = ["GETCOST"] formatMessage GETAVAILABILITY = ["GETAVAILABILITY"]+ formatMessage GETORDERED = ["GETORDERED"] formatMessage (CLAIMURL url) = [ "CLAIMURL", Proto.serialize url ] formatMessage (CHECKURL url) = [ "CHECKURL", Proto.serialize url ] formatMessage (TRANSFER direction key file) =@@ -248,6 +250,8 @@ | REMOVE_FAILURE Key ErrorMsg | COST Cost | AVAILABILITY Availability+ | ORDERED+ | UNORDERED | INITREMOTE_SUCCESS | INITREMOTE_FAILURE ErrorMsg | CLAIMURL_SUCCESS@@ -284,6 +288,8 @@ parseCommand "REMOVE-FAILURE" = Proto.parse2 REMOVE_FAILURE parseCommand "COST" = Proto.parse1 COST parseCommand "AVAILABILITY" = Proto.parse1 AVAILABILITY+ parseCommand "ORDERED" = Proto.parse0 ORDERED+ parseCommand "UNORDERED" = Proto.parse0 UNORDERED parseCommand "INITREMOTE-SUCCESS" = Proto.parse0 INITREMOTE_SUCCESS parseCommand "INITREMOTE-FAILURE" = Proto.parse1 INITREMOTE_FAILURE parseCommand "CLAIMURL-SUCCESS" = Proto.parse0 CLAIMURL_SUCCESS
Remote/GCrypt.hs view
@@ -140,6 +140,7 @@ , name = Git.repoDescribe r , storeKey = storeKeyDummy , retrieveKeyFile = retrieveKeyFileDummy+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = Nothing , retrievalSecurityPolicy = RetrievalAllKeysSecure , removeKey = removeKeyDummy@@ -409,9 +410,9 @@ storersync = fileStorer $ Remote.Rsync.store rsyncopts retrieve :: Remote -> Remote.Rsync.RsyncOpts -> AccessMethod -> Retriever-retrieve r rsyncopts accessmethod k p miv sink = do+retrieve r rsyncopts accessmethod k p dest miv sink = do repo <- getRepo r- retrieve' repo r rsyncopts accessmethod k p miv sink+ retrieve' repo r rsyncopts accessmethod k p dest miv sink retrieve' :: Git.Repo -> Remote -> Remote.Rsync.RsyncOpts -> AccessMethod -> Retriever retrieve' repo r rsyncopts accessmethod
Remote/Git.hs view
@@ -210,6 +210,7 @@ , name = Git.repoDescribe r , storeKey = copyToRemote new st , retrieveKeyFile = copyFromRemote new st+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = copyFromRemoteCheap st r , retrievalSecurityPolicy = RetrievalAllKeysSecure , removeKey = dropKey new st@@ -686,7 +687,7 @@ metered (Just meterupdate) key bwlimit $ \_ p -> do let p' = offsetMeterUpdate p (BytesProcessed n) res <- p2pHttpClient r giveup $- clientPut p' key (Just offset) af object sz check'+ clientPut p' key (Just offset) af object sz check' False case res of PutResultPlus False fanoutuuids -> do storefanout fanoutuuids
Remote/GitLFS.hs view
@@ -105,6 +105,7 @@ , name = Git.repoDescribe r , storeKey = storeKeyDummy , retrieveKeyFile = retrieveKeyFileDummy+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = Nothing -- content stored on git-lfs is hashed with SHA256 -- no matter what git-annex key it's for, and the hash
Remote/Glacier.hs view
@@ -81,6 +81,7 @@ , name = Git.repoDescribe r , storeKey = storeKeyDummy , retrieveKeyFile = retrieveKeyFileDummy+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = Nothing -- glacier-cli does not follow redirects and does -- not support file://, as far as we know, but@@ -177,7 +178,7 @@ forceSuccessProcess cmd pid go' _ _ _ _ _ = error "internal" -retrieve :: forall a. Remote -> Key -> MeterUpdate -> Maybe IncrementalVerifier -> (ContentSource -> Annex a) -> Annex a+retrieve :: forall a. Remote -> Key -> MeterUpdate -> RawFilePath -> Maybe IncrementalVerifier -> (ContentSource -> Annex a) -> Annex a retrieve = byteRetriever . retrieve' retrieve' :: forall a. Remote -> Key -> (L.ByteString -> Annex a) -> Annex a
Remote/Helper/Chunked.hs view
@@ -294,8 +294,10 @@ let p = maybe basep (offsetMeterUpdate basep . toBytesProcessed) offset- v <- tryNonAsync $- retriever (encryptor k) p Nothing $ \content ->+ v <- tryNonAsync $ do+ let enck = encryptor k+ objloc <- fromRepo $ gitAnnexTmpObjectLocation enck+ retriever enck p objloc Nothing $ \content -> bracket (maybe opennew openresume offset) (liftIO . hClose . fst) $ \(h, iv) -> do retrieved iv (Just h) p content let sz = toBytesProcessed $@@ -316,7 +318,9 @@ getrest p h iv sz bytesprocessed (k:ks) = do let p' = offsetMeterUpdate p bytesprocessed liftIO $ p' zeroBytesProcessed- retriever (encryptor k) p' Nothing $ + let enck = encryptor k+ objloc <- fromRepo $ gitAnnexTmpObjectLocation enck+ retriever enck p' objloc Nothing $ retrieved iv (Just h) p' getrest p h iv sz (addBytesProcessed bytesprocessed sz) ks @@ -324,7 +328,9 @@ iv <- startVerifyKeyContentIncrementally vc basek case enc of Just _ -> do- retriever (encryptor basek) basep Nothing $+ let enck = encryptor basek+ objloc <- fromRepo $ gitAnnexTmpObjectLocation enck+ retriever (encryptor basek) basep objloc Nothing $ retrieved iv Nothing basep return (Right iv) -- Not chunked and not encrypted, so ask the@@ -333,7 +339,7 @@ -- passing the whole file content to the -- incremental verifier though. Nothing -> do- retriever (encryptor basek) basep iv $+ retriever (encryptor basek) basep (toRawFilePath dest) iv $ retrieved iv Nothing basep return $ case iv of Nothing -> Right iv
Remote/Helper/Special.hs view
@@ -42,6 +42,7 @@ import Types.Remote import Annex.Verify import Annex.UUID+import Annex.Perms import Config import Config.Cost import Utility.Metered@@ -106,10 +107,10 @@ -- A Retriever that generates a lazy ByteString containing the Key's -- content, and passes it to a callback action which will fully consume it -- before returning.-byteRetriever :: (Key -> (L.ByteString -> Annex a) -> Annex a) -> Key -> MeterUpdate -> Maybe IncrementalVerifier -> (ContentSource -> Annex a) -> Annex a-byteRetriever a k _m _miv callback = a k (callback . ByteContent)+byteRetriever :: (Key -> (L.ByteString -> Annex a) -> Annex a) -> Key -> MeterUpdate -> RawFilePath -> Maybe IncrementalVerifier -> (ContentSource -> Annex a) -> Annex a+byteRetriever a k _m _dest _miv callback = a k (callback . ByteContent) --- A Retriever that writes the content of a Key to a provided file.+-- A Retriever that writes the content of a Key to a file. -- The action is responsible for updating the progress meter as it -- retrieves data. The incremental verifier is updated in the background as -- the action writes to the file, but may not be updated with the entire@@ -119,15 +120,15 @@ let retrieve = a f k m in tailVerify miv f retrieve -{- A Retriever that writes the content of a Key to a provided file.+{- A Retriever that writes the content of a Key to a file. - The action is responsible for updating the progress meter and the - incremental verifier as it retrieves data. -} fileRetriever' :: (RawFilePath -> Key -> MeterUpdate -> Maybe IncrementalVerifier -> Annex ()) -> Retriever-fileRetriever' a k m miv callback = do- f <- prepTmp k- a f k m miv- pruneTmpWorkDirBefore f (callback . FileContent . fromRawFilePath)+fileRetriever' a k m dest miv callback = do+ createAnnexDirectory (parentDir dest)+ a dest k m miv+ pruneTmpWorkDirBefore dest (callback . FileContent . fromRawFilePath) {- The base Remote that is provided to specialRemote needs to have - storeKey, retrieveKeyFile, removeKey, and checkPresent methods,
Remote/Hook.hs view
@@ -62,6 +62,7 @@ , name = Git.repoDescribe r , storeKey = storeKeyDummy , retrieveKeyFile = retrieveKeyFileDummy+ , retrieveKeyFileInOrder = pure False , retrieveKeyFileCheap = Nothing -- A hook could use http and be vulnerable to -- redirect to file:// attacks, etc.
Remote/HttpAlso.hs view
@@ -67,6 +67,7 @@ , name = Git.repoDescribe r , storeKey = cannotModify , retrieveKeyFile = retrieveKeyFileDummy+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = Nothing -- HttpManagerRestricted is used here, so this is -- secure.
Remote/P2P.hs view
@@ -59,6 +59,7 @@ , name = Git.repoDescribe r , storeKey = store u gc protorunner , retrieveKeyFile = retrieve gc protorunner+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = Nothing , retrievalSecurityPolicy = RetrievalAllKeysSecure , removeKey = remove u protorunner
Remote/Rsync.hs view
@@ -94,6 +94,7 @@ , name = Git.repoDescribe r , storeKey = storeKeyDummy , retrieveKeyFile = retrieveKeyFileDummy+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = Just (retrieveCheap o) , retrievalSecurityPolicy = RetrievalAllKeysSecure , removeKey = removeKeyDummy
Remote/S3.hs view
@@ -209,6 +209,7 @@ , name = Git.repoDescribe r , storeKey = storeKeyDummy , retrieveKeyFile = retrieveKeyFileDummy+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = Nothing -- HttpManagerRestricted is used here, so this is -- secure.
Remote/Tahoe.hs view
@@ -89,6 +89,9 @@ , name = Git.repoDescribe r , storeKey = store rs hdl , retrieveKeyFile = retrieve rs hdl+ -- Unsure about whether tahoe might sometimes write chunks+ -- out of order.+ , retrieveKeyFileInOrder = pure False , retrieveKeyFileCheap = Nothing -- Tahoe cryptographically verifies content. , retrievalSecurityPolicy = RetrievalAllKeysSecure
Remote/Web.hs view
@@ -77,6 +77,7 @@ , name = Git.repoDescribe r , storeKey = uploadKey , retrieveKeyFile = downloadKey urlincludeexclude+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = Nothing -- HttpManagerRestricted is used here, so this is -- secure.
Remote/WebDAV.hs view
@@ -88,6 +88,7 @@ , name = Git.repoDescribe r , storeKey = storeKeyDummy , retrieveKeyFile = retrieveKeyFileDummy+ , retrieveKeyFileInOrder = pure True , retrieveKeyFileCheap = Nothing -- HttpManagerRestricted is used here, so this is -- secure.
Types/AdjustedBranch.hs view
@@ -10,7 +10,7 @@ data Adjustment = LinkAdjustment LinkAdjustment | PresenceAdjustment PresenceAdjustment (Maybe LinkAdjustment)- | LinkPresentAdjustment LinkPresentAdjustment+ | LockUnlockPresentAdjustment LockUnlockPresentAdjustment deriving (Show, Eq) data LinkAdjustment@@ -25,7 +25,7 @@ | ShowMissingAdjustment deriving (Show, Eq) -data LinkPresentAdjustment+data LockUnlockPresentAdjustment = UnlockPresentAdjustment | LockPresentAdjustment deriving (Show, Eq)@@ -41,8 +41,8 @@ LinkAdjustment (reverseAdjustment l) reverseAdjustment (PresenceAdjustment p ml) = PresenceAdjustment (reverseAdjustment p) (fmap reverseAdjustment ml)- reverseAdjustment (LinkPresentAdjustment l) =- LinkPresentAdjustment (reverseAdjustment l)+ reverseAdjustment (LockUnlockPresentAdjustment l) =+ LockUnlockPresentAdjustment (reverseAdjustment l) instance ReversableAdjustment LinkAdjustment where reverseAdjustment UnlockAdjustment = LockAdjustment@@ -55,7 +55,7 @@ reverseAdjustment HideMissingAdjustment = ShowMissingAdjustment reverseAdjustment ShowMissingAdjustment = HideMissingAdjustment -instance ReversableAdjustment LinkPresentAdjustment where+instance ReversableAdjustment LockUnlockPresentAdjustment where reverseAdjustment UnlockPresentAdjustment = LockPresentAdjustment reverseAdjustment LockPresentAdjustment = UnlockPresentAdjustment
Types/Key.hs view
@@ -354,6 +354,7 @@ parseKeyVariety "URL" = URLKey parseKeyVariety "VURL" = VURLKey parseKeyVariety "GITBUNDLE" = GitBundleKey+parseKeyVariety "GITMANIFEST" = GitManifestKey parseKeyVariety b | "X" `S.isPrefixOf` b = let b' = S.tail b
Types/Remote.hs view
@@ -98,6 +98,8 @@ -- sequentially to the file.) -- Throws exception on failure. , retrieveKeyFile :: Key -> AssociatedFile -> FilePath -> MeterUpdate -> VerifyConfigA a -> a Verification+ {- Will retrieveKeyFile write to the file in order? -}+ , retrieveKeyFileInOrder :: a Bool -- Retrieves a key's contents to a tmp file, if it can be done cheaply. -- It's ok to create a symlink or hardlink. -- Throws exception on failure.
Types/StoreRetrieve.hs view
@@ -35,12 +35,15 @@ -- Throws exception if key is not present, or remote is not accessible. -- -- When it retrieves FileContent, it is responsible for updating the--- MeterUpdate. And when the IncrementalVerifier is passed to it,+-- MeterUpdate, and the provided FilePath can be used to store the file+-- it retrieves. +--+-- When the IncrementalVerifier is passed to it, -- and it retrieves FileContent, it can feed some or all of the file's -- content to the verifier before running the callback. -- This should not be done when it retrieves ByteContent. type Retriever = forall a.- Key -> MeterUpdate -> Maybe IncrementalVerifier+ Key -> MeterUpdate -> RawFilePath -> Maybe IncrementalVerifier -> (ContentSource -> Annex a) -> Annex a -- Action that removes a Key's content from a remote.
+ Utility/OpenFile.hs view
@@ -0,0 +1,38 @@+{- Opening files+ -+ - Copyright 2024 Joey Hess <id@joeyh.name>+ -+ - License: BSD-2-clause+ -}++{-# LANGUAGE CPP #-}++module Utility.OpenFile where++#ifndef mingw32_HOST_OS++import System.IO+import System.Posix.IO+import GHC.IO.FD+import GHC.IO.Handle.FD+import GHC.IO.Device++import Utility.OpenFd+import Utility.RawFilePath+import Utility.FileSystemEncoding++{- Usually, opening a Handle to a file that another thread also has open+ - for write is prevented, which avoids a lot of concurrency bugs especially+ - with lazy IO.+ - + - However, sometimes one thread is writing and another thread really wants+ - to read from the same file. This bypasses the usual locking, by claiming+ - that an opened FD is a Stream.+ -}+openFileBeingWritten :: RawFilePath -> IO Handle+openFileBeingWritten f = do+ fd <- openFdWithMode f ReadOnly Nothing defaultFileFlags+ (fd', fdtype) <- mkFD (fromIntegral fd) ReadMode (Just (Stream, 0, 0)) False False+ mkHandleFromFD fd' fdtype (fromRawFilePath f) ReadMode False Nothing++#endif
+ Utility/STM.hs view
@@ -0,0 +1,23 @@+{- support for old versions of the stm package+ -+ - Copyright 2024 Joey Hess <id@joeyh.name>+ -+ - License: BSD-2-clause+ -}++{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-tabs #-}++module Utility.STM (+ module Control.Concurrent.STM,+#if ! MIN_VERSION_stm(2,5,1)+ writeTMVar+#endif+) where++import Control.Concurrent.STM++#if ! MIN_VERSION_stm(2,5,1)+writeTMVar :: TMVar t -> t -> STM ()+writeTMVar t new = tryTakeTMVar t >> putTMVar t new+#endif
git-annex.cabal view
@@ -1,5 +1,5 @@ Name: git-annex-Version: 10.20240927+Version: 10.20241031 Cabal-Version: 1.12 License: AGPL-3 Maintainer: Joey Hess <id@joeyh.name>@@ -321,8 +321,7 @@ servant-client, servant-client-core, warp (>= 3.2.8),- warp-tls (>= 3.2.2),- stm (>= 2.5.1)+ warp-tls (>= 3.2.2) CPP-Options: -DWITH_SERVANT Other-Modules: Command.P2PHttp@@ -1091,6 +1090,7 @@ Utility.Network Utility.NotificationBroadcaster Utility.OpenFd+ Utility.OpenFile Utility.OptParse Utility.OSX Utility.PID@@ -1119,6 +1119,7 @@ Utility.SshConfig Utility.SshHost Utility.StatelessOpenPGP+ Utility.STM Utility.Su Utility.SystemDirectory Utility.Terminal