Only for export remotes so far, not export/import.
Sponsored-by: Dartmouth College's Datalad project
{- verification
-
- - Copyright 2010-2021 Joey Hess <id@joeyh.name>
+ - Copyright 2010-2022 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
isVerifiable,
startVerifyKeyContentIncrementally,
finishVerifyKeyContentIncrementally,
+ verifyKeyContentIncrementally,
IncrementalVerifier(..),
tailVerify,
) where
import Types.Key
import Control.Concurrent.STM
+import Control.Concurrent.Async
import qualified Data.ByteString as S
#if WITH_INOTIFY
import qualified System.INotify as INotify
-- Incremental verification was not able to be done.
Nothing -> return (True, UnVerified)
--- | Reads the file as it grows, and feeds it to the incremental verifier.
+verifyKeyContentIncrementally :: VerifyConfig -> Key -> (Maybe IncrementalVerifier -> Annex ()) -> Annex Verification
+verifyKeyContentIncrementally verifyconfig k a = do
+ miv <- startVerifyKeyContentIncrementally verifyconfig k
+ a miv
+ snd <$> finishVerifyKeyContentIncrementally miv
+
+-- | Runs a writer action that retrieves to a file. In another thread,
+-- reads the file as it grows, and feeds it to the incremental verifier.
--
--- The TMVar must start out empty, and be filled once whatever is
--- writing to the file finishes. Once the writer finishes, this returns
--- quickly. It may not feed the entire content of the file to the
--- incremental verifier.
+-- Once the writer finishes, this returns quickly. It may not feed
+-- the entire content of the file to the incremental verifier.
--
-- The file does not need to exist yet when this is called. It will wait
-- for the file to appear before opening it and starting verification.
-- and if the disk is slow, the reader may never catch up to the writer,
-- and the disk cache may never speed up reads. So this should only be
-- used when there's not a better way to incrementally verify.
-tailVerify :: IncrementalVerifier -> RawFilePath -> TMVar () -> IO ()
+tailVerify :: Maybe IncrementalVerifier -> RawFilePath -> Annex a -> Annex a
+tailVerify (Just iv) f writer = do
+ finished <- liftIO newEmptyTMVarIO
+ t <- liftIO $ async $ tailVerify' iv f finished
+ let finishtail = do
+ liftIO $ atomically $ putTMVar finished ()
+ liftIO (wait t)
+ writer `finally` finishtail
+tailVerify Nothing _ writer = writer
+
+tailVerify' :: IncrementalVerifier -> RawFilePath -> TMVar () -> IO ()
#if WITH_INOTIFY
-tailVerify iv f finished =
+tailVerify' iv f finished =
tryNonAsync go >>= \case
Right r -> return r
Left _ -> unableIncrementalVerifier iv
chunk = 65536
#else
-tailVerify iv _ _ = unableIncrementalVerifier iv
+tailVerify' iv _ _ = unableIncrementalVerifier iv
#endif
{- Remote on Android device accessed using adb.
-
- - Copyright 2018-2020 Joey Hess <id@joeyh.name>
+ - Copyright 2018-2022 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
import Utility.Metered
import Types.ProposedAccepted
import Annex.SpecialRemote.Config
+import Annex.Verify
import qualified Data.Map as M
import qualified System.FilePath.Posix as Posix
dest = androidExportLocation adir loc
retrieveExportM :: AndroidSerial -> AndroidPath -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification
-retrieveExportM serial adir _k loc dest _p = do
- retrieve' serial src dest
- return UnVerified
+retrieveExportM serial adir k loc dest _p =
+ verifyKeyContentIncrementally AlwaysVerify k $ \iv ->
+ tailVerify iv (toRawFilePath dest) $
+ retrieve' serial src dest
where
src = androidExportLocation adir loc
import Annex.Content
import Annex.Perms
import Annex.UUID
+import Annex.Verify
import Backend
import Types.KeySource
import Types.ProposedAccepted
go tmp () = void $ fileCopier cow src tmp p Nothing
retrieveExportM :: RawFilePath -> CopyCoWTried -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification
-retrieveExportM d cow _k loc dest p = do
- void $ fileCopier cow src dest p Nothing
- return UnVerified
+retrieveExportM d cow k loc dest p =
+ verifyKeyContentIncrementally AlwaysVerify k $ \iv ->
+ void $ fileCopier cow src dest p iv
where
src = fromRawFilePath $ exportPath d loc
{- External special remote interface.
-
- - Copyright 2013-2020 Joey Hess <id@joeyh.name>
+ - Copyright 2013-2022 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
import Annex.Content
import Annex.Url
import Annex.UUID
+import Annex.Verify
import Creds
import Control.Concurrent.STM
req sk = TRANSFEREXPORT Upload sk f
retrieveExportM :: External -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification
-retrieveExportM external k loc d p = do
- either giveup return =<< go
- return UnVerified
+retrieveExportM external k loc dest p = do
+ verifyKeyContentIncrementally AlwaysVerify k $ \iv ->
+ tailVerify iv (toRawFilePath dest) $
+ either giveup return =<< go
where
go = handleRequestExport external loc req k (Just p) $ \resp -> case resp of
TRANSFER_SUCCESS Download k'
UNSUPPORTED_REQUEST ->
result $ Left "TRANSFEREXPORT not implemented by external special remote"
_ -> Nothing
- req sk = TRANSFEREXPORT Download sk d
+ req sk = TRANSFEREXPORT Download sk dest
checkPresentExportM :: External -> Key -> ExportLocation -> Annex Bool
checkPresentExportM external k loc = either giveup id <$> go
copyFromRemote'' :: Git.Repo -> Remote -> State -> Key -> AssociatedFile -> FilePath -> MeterUpdate -> VerifyConfig -> Annex Verification
copyFromRemote'' repo r st@(State connpool _ _ _ _) key file dest meterupdate vc
- | Git.repoIsHttp repo = do
- iv <- startVerifyKeyContentIncrementally vc key
+ | Git.repoIsHttp repo = verifyKeyContentIncrementally vc key $ \iv -> do
gc <- Annex.getGitConfig
ok <- Url.withUrlOptionsPromptingCreds $
Annex.Content.downloadUrl False key meterupdate iv (keyUrls gc repo r key) dest
unless ok $
giveup "failed to download content"
- snd <$> finishVerifyKeyContentIncrementally iv
| not $ Git.repoIsUrl repo = guardUsable repo (giveup "cannot access remote") $ do
u <- getUUID
hardlink <- wantHardLink
import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy as L
import qualified Data.Map as M
-import Control.Concurrent.STM
-import Control.Concurrent.Async
{- Special remotes don't have a configured url, so Git.Repo does not
- automatically generate remotes for them. This looks for a different
-- the action writes to the file, but may not be updated with the entire
-- content of the file.
fileRetriever :: (RawFilePath -> Key -> MeterUpdate -> Annex ()) -> Retriever
-fileRetriever a = fileRetriever' $ \f k m miv -> do
+fileRetriever a = fileRetriever' $ \f k m miv ->
let retrieve = a f k m
- case miv of
- Nothing -> retrieve
- Just iv -> do
- finished <- liftIO newEmptyTMVarIO
- t <- liftIO $ async $ tailVerify iv f finished
- let finishtail = do
- liftIO $ atomically $ putTMVar finished ()
- liftIO (wait t)
- retrieve `finally` finishtail
+ in tailVerify miv f retrieve
{- A Retriever that writes the content of a Key to a provided file.
- The action is responsible for updating the progress meter and the
downloadKey :: Maybe URLString -> LearnedLayout -> Key -> AssociatedFile -> FilePath -> MeterUpdate -> VerifyConfig -> Annex Verification
downloadKey baseurl ll key _af dest p vc = do
- iv <- startVerifyKeyContentIncrementally vc key
- downloadAction dest p iv key (keyUrlAction baseurl ll key)
- snd <$> finishVerifyKeyContentIncrementally iv
+ verifyKeyContentIncrementally vc key $ \iv ->
+ downloadAction dest p iv key (keyUrlAction baseurl ll key)
retriveExportHttpAlso :: Maybe URLString -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification
retriveExportHttpAlso baseurl key loc dest p = do
- downloadAction dest p Nothing key (exportLocationUrlAction baseurl loc)
- return UnVerified
+ verifyKeyContentIncrementally AlwaysVerify key $ \iv ->
+ downloadAction dest p iv key (exportLocationUrlAction baseurl loc)
downloadAction :: FilePath -> MeterUpdate -> Maybe IncrementalVerifier -> Key -> ((URLString -> Annex (Either String ())) -> Annex (Either String ())) -> Annex ()
downloadAction dest p iv key run =
import Utility.Tmp.Dir
import Utility.SshHost
import Annex.SpecialRemote.Config
+import Annex.Verify
import qualified Data.Map as M
populatedest = liftIO . createLinkOrCopy src
retrieveExportM :: RsyncOpts -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification
-retrieveExportM o _k loc dest p = do
- rsyncRetrieve o [rsyncurl] dest (Just p)
- return UnVerified
+retrieveExportM o k loc dest p =
+ verifyKeyContentIncrementally AlwaysVerify k $ \iv ->
+ tailVerify iv (toRawFilePath dest) $
+ rsyncRetrieve o [rsyncurl] dest (Just p)
where
rsyncurl = mkRsyncUrl o (fromRawFilePath (fromExportLocation loc))
import Types.ProposedAccepted
import Types.NumCopies
import Utility.Metered
-import Utility.Hash (IncrementalVerifier)
import Utility.DataUnits
import Annex.Content
import qualified Annex.Url as Url
import Utility.Url (extractFromResourceT)
import Annex.Url (getUrlOptions, withUrlOptions, UrlOptions(..))
import Utility.Env
+import Annex.Verify
type BucketName = String
type BucketObject = String
return (metag, mvid)
retrieveExportS3 :: S3HandleVar -> Remote -> S3Info -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification
-retrieveExportS3 hv r info _k loc f p = do
+retrieveExportS3 hv r info k loc f p = verifyKeyContentIncrementally AlwaysVerify k $ \iv ->
withS3Handle hv $ \case
- Just h -> retrieveHelper info h (Left (T.pack exportloc)) f p Nothing
+ Just h -> retrieveHelper info h (Left (T.pack exportloc)) f p iv
Nothing -> case getPublicUrlMaker info of
Just geturl -> either giveup return =<<
Url.withUrlOptions
- (Url.download' p Nothing (geturl exportloc) f)
+ (Url.download' p iv (geturl exportloc) f)
Nothing -> giveup $ needS3Creds (uuid r)
- return UnVerified
where
exportloc = bucketExportLocation info loc
import Creds
import Utility.Metered
import Utility.Url (URLString, matchStatusCodeException, matchHttpExceptionContent)
-import Utility.Hash (IncrementalVerifier(..))
+import Annex.Verify
import Annex.UUID
import Remote.WebDAV.DavLocation
import Types.ProposedAccepted
Left err -> giveup err
retrieveExportDav :: DavHandleVar -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification
-retrieveExportDav hdl _k loc d p = case exportLocation loc of
- Right src -> withDavHandle hdl $ \h -> runExport h $ \_dav -> do
- retrieveHelper src d p Nothing
- return UnVerified
+retrieveExportDav hdl k loc d p = case exportLocation loc of
+ Right src -> verifyKeyContentIncrementally AlwaysVerify k $ \iv ->
+ withDavHandle hdl $ \h -> runExport h $ \_dav ->
+ retrieveHelper src d p iv
Left err -> giveup err
checkPresentExportDav :: DavHandleVar -> Remote -> Key -> ExportLocation -> Annex Bool
--- /dev/null
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 17"""
+ date="2022-05-09T17:48:31Z"
+ content="""
+Update: incremental hashing is also now done for all export remotes.
+Only import (and export+import) remotes don't support incremental hashing
+now.
+"""]]