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:
parent
7db60269eb
commit
f3e47b16a5
3 changed files with 41 additions and 29 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue