better Preparer interface

This will allow things like WebDAV to opean a single persistent connection
and reuse it for all the chunked data.

The crazy types allow for some nice code reuse.
This commit is contained in:
Joey Hess 2014-07-27 00:30:04 -04:00
parent 7db60269eb
commit f3e47b16a5
3 changed files with 41 additions and 29 deletions

View file

@ -6,6 +6,7 @@
-} -}
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE Rank2Types #-}
module Remote.Directory (remote) where module Remote.Directory (remote) where
@ -106,11 +107,10 @@ tmpDir d k = addTrailingPathSeparator $ d </> "tmp" </> keyFile k
{- Check if there is enough free disk space in the remote's directory to {- Check if there is enough free disk space in the remote's directory to
- store the key. Note that the unencrypted key size is checked. -} - store the key. Note that the unencrypted key size is checked. -}
prepareStore :: FilePath -> ChunkConfig -> PrepareStorer prepareStore :: FilePath -> ChunkConfig -> Preparer Storer
prepareStore d chunkconfig k = ifM (checkDiskSpace (Just d) k 0) prepareStore d chunkconfig = checkPrepare
( return $ Just (store d chunkconfig) (\k -> checkDiskSpace (Just d) k 0)
, return Nothing (store d chunkconfig)
)
store :: FilePath -> ChunkConfig -> Storer store :: FilePath -> ChunkConfig -> Storer
store d chunkconfig k b p = do store d chunkconfig k b p = do
@ -135,9 +135,9 @@ store d chunkconfig k b p = do
mapM_ preventWrite =<< dirContents dest mapM_ preventWrite =<< dirContents dest
preventWrite dest preventWrite dest
retrieve :: FilePath -> ChunkConfig -> PrepareRetriever retrieve :: FilePath -> ChunkConfig -> Preparer Retriever
retrieve d (LegacyChunks _) basek = Legacy.retrieve locations d basek retrieve d (LegacyChunks _) = Legacy.retrieve locations d
retrieve d _ _ = return $ Just $ \k -> L.readFile =<< getLocation d k retrieve d _ = simplyPrepare $ \k -> L.readFile =<< getLocation d k
retrieveCheap :: FilePath -> ChunkConfig -> Key -> FilePath -> Annex Bool retrieveCheap :: FilePath -> ChunkConfig -> Key -> FilePath -> Annex Bool
-- no cheap retrieval possible for chunks -- no cheap retrieval possible for chunks

View file

@ -7,6 +7,8 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
{-# LANGUAGE Rank2Types #-}
module Remote.Directory.LegacyChunked where module Remote.Directory.LegacyChunked where
import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy as L
@ -88,13 +90,13 @@ store chunksize finalizer k b p = storeHelper finalizer k $ \dests ->
- Done very innefficiently, by writing to a temp file. - Done very innefficiently, by writing to a temp file.
- :/ This is legacy code.. - :/ This is legacy code..
-} -}
retrieve :: (FilePath -> Key -> [FilePath]) -> FilePath -> PrepareRetriever retrieve :: (FilePath -> Key -> [FilePath]) -> FilePath -> Preparer Retriever
retrieve locations d basek = do retrieve locations d basek a = do
showLongNote "This remote uses the deprecated chunksize setting. So this will be quite slow." showLongNote "This remote uses the deprecated chunksize setting. So this will be quite slow."
tmpdir <- fromRepo $ gitAnnexTmpMiscDir tmpdir <- fromRepo $ gitAnnexTmpMiscDir
createAnnexDirectory tmpdir createAnnexDirectory tmpdir
let tmp = tmpdir </> keyFile basek ++ ".directorylegacy.tmp" let tmp = tmpdir </> keyFile basek ++ ".directorylegacy.tmp"
return $ Just $ \k -> do a $ Just $ \k -> do
void $ withStoredFiles d locations k $ \fs -> do void $ withStoredFiles d locations k $ \fs -> do
forM_ fs $ forM_ fs $
S.appendFile tmp <=< S.readFile S.appendFile tmp <=< S.readFile

View file

@ -5,12 +5,15 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
{-# LANGUAGE Rank2Types #-}
module Remote.Helper.ChunkedEncryptable ( module Remote.Helper.ChunkedEncryptable (
chunkedEncryptableRemote, Preparer,
PrepareStorer, simplyPrepare,
checkPrepare,
Storer, Storer,
PrepareRetriever,
Retriever, Retriever,
chunkedEncryptableRemote,
storeKeyDummy, storeKeyDummy,
retreiveKeyFileDummy, retreiveKeyFileDummy,
module X module X
@ -28,18 +31,23 @@ import Remote.Helper.Encryptable as X
import Annex.Content import Annex.Content
import Annex.Exception import Annex.Exception
-- Prepares to store a Key, and returns a Storer action if possible. -- Prepares for and then runs an action that will act on a Key,
-- May throw exceptions. -- passing it a helper when the preparation is successful.
type PrepareStorer = Key -> Annex (Maybe Storer) type Preparer helper = forall a. Key -> (Maybe helper -> Annex a) -> Annex a
simplyPrepare :: helper -> Preparer helper
simplyPrepare helper _ a = a $ Just helper
checkPrepare :: (Key -> Annex Bool) -> helper -> Preparer helper
checkPrepare checker helper k a = ifM (checker k)
( a (Just helper)
, a Nothing
)
-- Stores a Key, which may be encrypted and/or a chunk key. -- Stores a Key, which may be encrypted and/or a chunk key.
-- May throw exceptions. -- May throw exceptions.
type Storer = Key -> L.ByteString -> MeterUpdate -> IO Bool type Storer = Key -> L.ByteString -> MeterUpdate -> IO Bool
-- Prepares to retrieve a Key, and returns a Retriever action if possible.
-- May throw exceptions.
type PrepareRetriever = Key -> Annex (Maybe Retriever)
-- Retrieves a Key, which may be encrypted and/or a chunk key. -- Retrieves a Key, which may be encrypted and/or a chunk key.
-- Throws exception if key is not present, or remote is not accessible. -- Throws exception if key is not present, or remote is not accessible.
type Retriever = Key -> IO L.ByteString type Retriever = Key -> IO L.ByteString
@ -48,8 +56,8 @@ type Retriever = Key -> IO L.ByteString
-} -}
chunkedEncryptableRemote chunkedEncryptableRemote
:: RemoteConfig :: RemoteConfig
-> PrepareStorer -> Preparer Storer
-> PrepareRetriever -> Preparer Retriever
-> Remote -> Remote
-> Remote -> Remote
chunkedEncryptableRemote c preparestorer prepareretriever r = encr chunkedEncryptableRemote c preparestorer prepareretriever r = encr
@ -74,13 +82,14 @@ chunkedEncryptableRemote c preparestorer prepareretriever r = encr
safely a = catchNonAsyncAnnex a (\e -> warning (show e) >> return False) safely a = catchNonAsyncAnnex a (\e -> warning (show e) >> return False)
-- chunk, then encrypt, then feed to the storer -- chunk, then encrypt, then feed to the storer
storeKeyGen k p enc = safely $ storeKeyGen k p enc =
maybe (return False) go =<< preparestorer k safely $ preparestorer k $ safely . go
where where
go storer = sendAnnex k rollback $ \src -> go (Just storer) = sendAnnex k rollback $ \src ->
metered (Just p) k $ \p' -> metered (Just p) k $ \p' ->
storeChunks (uuid r) chunkconfig k src p' $ storeChunks (uuid r) chunkconfig k src p' $
storechunk storer storechunk storer
go Nothing = return False
rollback = void $ removeKey encr k rollback = void $ removeKey encr k
storechunk storer k' b p' = case enc of storechunk storer k' b p' = case enc of
Nothing -> storer k' b p' Nothing -> storer k' b p'
@ -90,13 +99,14 @@ chunkedEncryptableRemote c preparestorer prepareretriever r = encr
storer (enck k') encb p' storer (enck k') encb p'
-- call retriever to get chunks; decrypt them; stream to dest file -- call retriever to get chunks; decrypt them; stream to dest file
retrieveKeyFileGen k dest p enc = safely $ retrieveKeyFileGen k dest p enc =
maybe (return False) go =<< prepareretriever k safely $ prepareretriever k $ safely . go
where where
go retriever = metered (Just p) k $ \p' -> go (Just retriever) = metered (Just p) k $ \p' ->
bracketIO (openBinaryFile dest WriteMode) hClose $ \h -> bracketIO (openBinaryFile dest WriteMode) hClose $ \h ->
retrieveChunks retriever (uuid r) chunkconfig enck k p' $ retrieveChunks retriever (uuid r) chunkconfig enck k p' $
sink h sink h
go Nothing = return False
sink h p' b = do sink h p' b = do
let write = meteredWrite p' h let write = meteredWrite p' h
case enc of case enc of