convert tailVerify to not finalize the verification
Added failIncremental so it can force failure to verify. Sponsored-by: Dartmouth College's DANDI project
This commit is contained in:
parent
9d533b347f
commit
e07625df8a
3 changed files with 55 additions and 38 deletions
|
@ -36,6 +36,7 @@ import Types.Key
|
||||||
import qualified System.INotify as INotify
|
import qualified System.INotify as INotify
|
||||||
import Control.Concurrent.STM
|
import Control.Concurrent.STM
|
||||||
import qualified Data.ByteString as S
|
import qualified Data.ByteString as S
|
||||||
|
import qualified System.FilePath.ByteString as P
|
||||||
import Data.Time.Clock.POSIX
|
import Data.Time.Clock.POSIX
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -131,13 +132,13 @@ startVerifyKeyContentIncrementally verifyconfig k =
|
||||||
-- The file does not need to exist yet when this is called. It will wait
|
-- 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.
|
-- for the file to appear before opening it and starting verification.
|
||||||
--
|
--
|
||||||
-- This is not supported for all OSs, and will return False on OS's
|
-- This is not supported for all OSs, and on OS's where it is not
|
||||||
-- where it is not supported.
|
-- supported, verification will fail.
|
||||||
--
|
--
|
||||||
-- Note that there are situations where the file may fail to verify despite
|
-- Note that there are situations where the file may fail to verify despite
|
||||||
-- having the correct content. For example, when the file is written out
|
-- having the correct content. For example, when the file is written out
|
||||||
-- of order, or gets replaced part way through. To deal with such cases,
|
-- of order, or gets replaced part way through. To deal with such cases,
|
||||||
-- when False is returned, it should not be treated as if the file's
|
-- when verification fails, it should not be treated as if the file's
|
||||||
-- content is known to be incorrect, but instead as an indication that the
|
-- content is known to be incorrect, but instead as an indication that the
|
||||||
-- file should be verified again, once it's done being written to.
|
-- file should be verified again, once it's done being written to.
|
||||||
--
|
--
|
||||||
|
@ -162,27 +163,22 @@ startVerifyKeyContentIncrementally verifyconfig k =
|
||||||
-- writer has finished. If there is more work still to do, it returns an IO
|
-- writer has finished. If there is more work still to do, it returns an IO
|
||||||
-- action that will complete the work. This way, the caller can display
|
-- action that will complete the work. This way, the caller can display
|
||||||
-- something appropriate while that is running.
|
-- something appropriate while that is running.
|
||||||
tailVerify :: IncrementalVerifier -> FilePath -> TMVar () -> IO (Either (IO Bool) Bool)
|
tailVerify :: IncrementalVerifier -> RawFilePath -> TMVar () -> IO (Maybe (IO ()))
|
||||||
#if WITH_INOTIFY
|
#if WITH_INOTIFY
|
||||||
tailVerify iv f finished =
|
tailVerify iv f finished =
|
||||||
catchDefaultIO (Right False) $
|
tryIO go >>= \case
|
||||||
INotify.withINotify $ \i -> do
|
Right r -> return r
|
||||||
h <- waitforfiletoexist i
|
Left _ -> do
|
||||||
tryIO (go i h) >>= \case
|
failIncremental iv
|
||||||
Right r -> return r
|
return Nothing
|
||||||
Left _ -> do
|
|
||||||
maybe noop hClose h
|
|
||||||
return (Right False)
|
|
||||||
where
|
where
|
||||||
rf = toRawFilePath f
|
f' = fromRawFilePath f
|
||||||
d = toRawFilePath (takeDirectory f)
|
waitforfiletoexist i = tryIO (openBinaryFile f' ReadMode) >>= \case
|
||||||
|
|
||||||
waitforfiletoexist i = tryIO (openBinaryFile f ReadMode) >>= \case
|
|
||||||
Right h -> return (Just h)
|
Right h -> return (Just h)
|
||||||
Left _ -> do
|
Left _ -> do
|
||||||
hv <- newEmptyTMVarIO
|
hv <- newEmptyTMVarIO
|
||||||
wd <- inotifycreate i $
|
wd <- inotifycreate i $
|
||||||
tryIO (openBinaryFile f ReadMode) >>= \case
|
tryIO (openBinaryFile f' ReadMode) >>= \case
|
||||||
Right h ->
|
Right h ->
|
||||||
unlessM (atomically $ tryPutTMVar hv h) $
|
unlessM (atomically $ tryPutTMVar hv h) $
|
||||||
hClose h
|
hClose h
|
||||||
|
@ -200,16 +196,16 @@ tailVerify iv f finished =
|
||||||
INotify.removeWatch wd
|
INotify.removeWatch wd
|
||||||
return v
|
return v
|
||||||
|
|
||||||
inotifycreate i cont = INotify.addWatch i evs d $ \case
|
inotifycreate i cont = INotify.addWatch i evs (P.takeDirectory f) $ \case
|
||||||
-- Ignore changes to other files in the directory.
|
-- Ignore changes to other files in the directory.
|
||||||
INotify.Created { INotify.filePath = fn }
|
INotify.Created { INotify.filePath = fn }
|
||||||
| fn /= rf -> noop
|
| fn /= f -> noop
|
||||||
INotify.MovedIn { INotify.filePath = fn }
|
INotify.MovedIn { INotify.filePath = fn }
|
||||||
| fn /= rf -> noop
|
| fn /= f -> noop
|
||||||
INotify.Opened { INotify.maybeFilePath = fn }
|
INotify.Opened { INotify.maybeFilePath = fn }
|
||||||
| fn /= Just rf -> noop
|
| fn /= Just f -> noop
|
||||||
INotify.Modified { INotify.maybeFilePath = fn }
|
INotify.Modified { INotify.maybeFilePath = fn }
|
||||||
| fn /= Just rf -> noop
|
| fn /= Just f -> noop
|
||||||
_ -> cont
|
_ -> cont
|
||||||
where
|
where
|
||||||
evs =
|
evs =
|
||||||
|
@ -220,16 +216,27 @@ tailVerify iv f finished =
|
||||||
, INotify.Modify
|
, INotify.Modify
|
||||||
]
|
]
|
||||||
|
|
||||||
go i (Just h) = do
|
go = INotify.withINotify $ \i -> do
|
||||||
|
h <- waitforfiletoexist i
|
||||||
|
tryIO (go' i h) >>= \case
|
||||||
|
Right r -> return r
|
||||||
|
Left _ -> do
|
||||||
|
maybe noop hClose h
|
||||||
|
failIncremental iv
|
||||||
|
return Nothing
|
||||||
|
|
||||||
|
go' i (Just h) = do
|
||||||
modified <- newEmptyTMVarIO
|
modified <- newEmptyTMVarIO
|
||||||
wd <- INotify.addWatch i [INotify.Modify] rf $ \_event ->
|
wd <- INotify.addWatch i [INotify.Modify] f $ \_event ->
|
||||||
atomically $ void $ tryPutTMVar modified ()
|
atomically $ void $ tryPutTMVar modified ()
|
||||||
r <- follow h modified
|
r <- follow h modified
|
||||||
INotify.removeWatch wd
|
INotify.removeWatch wd
|
||||||
return r
|
return r
|
||||||
-- File never showed up, but we've been told it's done being
|
-- File never showed up, but we've been told it's done being
|
||||||
-- written to.
|
-- written to.
|
||||||
go _ Nothing = return (Right False)
|
go' _ Nothing = do
|
||||||
|
failIncremental iv
|
||||||
|
return Nothing
|
||||||
|
|
||||||
follow h modified = do
|
follow h modified = do
|
||||||
b <- S.hGetNonBlocking h chunk
|
b <- S.hGetNonBlocking h chunk
|
||||||
|
@ -259,26 +266,28 @@ tailVerify iv f finished =
|
||||||
if S.null b
|
if S.null b
|
||||||
then do
|
then do
|
||||||
hClose h
|
hClose h
|
||||||
Right <$> finalizeIncremental iv
|
return Nothing
|
||||||
else do
|
else do
|
||||||
updateIncremental iv b
|
updateIncremental iv b
|
||||||
now <- getPOSIXTime
|
now <- getPOSIXTime
|
||||||
if now - starttime > 0.5
|
if now - starttime > 0.5
|
||||||
then return (Left (deferredfinish h))
|
then return $ Just $
|
||||||
|
tryIO (deferredfinish h) >>= \case
|
||||||
|
Right () -> noop
|
||||||
|
Left _ -> failIncremental iv
|
||||||
else finish h starttime
|
else finish h starttime
|
||||||
|
|
||||||
deferredfinish h = do
|
deferredfinish h = do
|
||||||
b <- S.hGet h chunk
|
b <- S.hGet h chunk
|
||||||
if S.null b
|
if S.null b
|
||||||
then do
|
then hClose h
|
||||||
hClose h
|
|
||||||
finalizeIncremental iv
|
|
||||||
else do
|
else do
|
||||||
updateIncremental iv b
|
updateIncremental iv b
|
||||||
deferredfinish h
|
deferredfinish h
|
||||||
|
|
||||||
chunk = 65536
|
chunk = 65536
|
||||||
#else
|
#else
|
||||||
tailVerify _ _ _ = return (Right False) -- not supported
|
tailVerify iv _ _ = do
|
||||||
|
failIncremental iv
|
||||||
|
return Nothing
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -280,13 +280,19 @@ md5Hasher = mkHasher md5 md5_context
|
||||||
|
|
||||||
mkIncrementalVerifier :: HashAlgorithm h => Context h -> Key -> IO IncrementalVerifier
|
mkIncrementalVerifier :: HashAlgorithm h => Context h -> Key -> IO IncrementalVerifier
|
||||||
mkIncrementalVerifier ctx key = do
|
mkIncrementalVerifier ctx key = do
|
||||||
v <- newIORef ctx
|
v <- newIORef (Just ctx)
|
||||||
return $ IncrementalVerifier
|
return $ IncrementalVerifier
|
||||||
{ updateIncremental = modifyIORef' v . flip hashUpdate
|
{ updateIncremental = \b ->
|
||||||
, finalizeIncremental = do
|
modifyIORef' v $ \case
|
||||||
ctx' <- readIORef v
|
Just ctx' -> Just (hashUpdate ctx' b)
|
||||||
let digest = hashFinalize ctx'
|
Nothing -> Nothing
|
||||||
return $ sameCheckSum key (show digest)
|
, finalizeIncremental =
|
||||||
|
readIORef v >>= \case
|
||||||
|
Just ctx' -> do
|
||||||
|
let digest = hashFinalize ctx'
|
||||||
|
return $ sameCheckSum key (show digest)
|
||||||
|
Nothing -> return False
|
||||||
|
, failIncremental = writeIORef v Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
{- A varient of the SHA256E backend, for testing that needs special keys
|
{- A varient of the SHA256E backend, for testing that needs special keys
|
||||||
|
|
|
@ -50,4 +50,6 @@ data IncrementalVerifier = IncrementalVerifier
|
||||||
, finalizeIncremental :: IO Bool
|
, finalizeIncremental :: IO Bool
|
||||||
-- ^ Called once the full content has been sent, returns true
|
-- ^ Called once the full content has been sent, returns true
|
||||||
-- if the hash verified.
|
-- if the hash verified.
|
||||||
|
, failIncremental :: IO ()
|
||||||
|
-- ^ Call if the incremental verification needs to fail.
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue