{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Ouroboros.Consensus.Node.DbMarker (
DbMarkerError (..)
, checkDbMarker
, dbMarkerContents
, dbMarkerFile
, dbMarkerParse
) where
import Control.Monad (void, when)
import Control.Monad.Except (ExceptT (..), runExceptT, throwError)
import Control.Monad.Trans.Class (lift)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS.Char8
import Data.ByteString.Lazy (fromStrict, toStrict)
import qualified Data.Set as Set
import Data.Text (Text)
import Data.Word
import Ouroboros.Consensus.Util.IOLike
import Ouroboros.Network.Magic (NetworkMagic (..))
import System.FS.API.Lazy
import Text.Read (readMaybe)
checkDbMarker ::
forall m h. MonadThrow m
=> HasFS m h
-> MountPoint
-> NetworkMagic
-> m (Either DbMarkerError ())
checkDbMarker :: forall (m :: * -> *) h.
MonadThrow m =>
HasFS m h
-> MountPoint -> NetworkMagic -> m (Either DbMarkerError ())
checkDbMarker HasFS m h
hasFS MountPoint
mountPoint NetworkMagic
networkMagic = ExceptT DbMarkerError m () -> m (Either DbMarkerError ())
forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT (ExceptT DbMarkerError m () -> m (Either DbMarkerError ()))
-> ExceptT DbMarkerError m () -> m (Either DbMarkerError ())
forall a b. (a -> b) -> a -> b
$ do
Bool
fileExists <- m Bool -> ExceptT DbMarkerError m Bool
forall (m :: * -> *) a. Monad m => m a -> ExceptT DbMarkerError m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m Bool -> ExceptT DbMarkerError m Bool)
-> m Bool -> ExceptT DbMarkerError m Bool
forall a b. (a -> b) -> a -> b
$ HasFS m h -> HasCallStack => FsPath -> m Bool
forall (m :: * -> *) h.
HasFS m h -> HasCallStack => FsPath -> m Bool
doesFileExist HasFS m h
hasFS FsPath
pFile
if Bool
fileExists then do
NetworkMagic
actualNetworkMagic <- ExceptT DbMarkerError m NetworkMagic
readNetworkMagicFile
Bool -> ExceptT DbMarkerError m () -> ExceptT DbMarkerError m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (NetworkMagic
actualNetworkMagic NetworkMagic -> NetworkMagic -> Bool
forall a. Eq a => a -> a -> Bool
/= NetworkMagic
networkMagic) (ExceptT DbMarkerError m () -> ExceptT DbMarkerError m ())
-> ExceptT DbMarkerError m () -> ExceptT DbMarkerError m ()
forall a b. (a -> b) -> a -> b
$
DbMarkerError -> ExceptT DbMarkerError m ()
forall a. DbMarkerError -> ExceptT DbMarkerError m a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (DbMarkerError -> ExceptT DbMarkerError m ())
-> DbMarkerError -> ExceptT DbMarkerError m ()
forall a b. (a -> b) -> a -> b
$ FilePath -> NetworkMagic -> NetworkMagic -> DbMarkerError
NetworkMagicMismatch
FilePath
fullPath
NetworkMagic
actualNetworkMagic
NetworkMagic
networkMagic
else do
m () -> ExceptT DbMarkerError m ()
forall (m :: * -> *) a. Monad m => m a -> ExceptT DbMarkerError m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ExceptT DbMarkerError m ())
-> m () -> ExceptT DbMarkerError m ()
forall a b. (a -> b) -> a -> b
$ HasFS m h -> HasCallStack => Bool -> FsPath -> m ()
forall (m :: * -> *) h.
HasFS m h -> HasCallStack => Bool -> FsPath -> m ()
createDirectoryIfMissing HasFS m h
hasFS Bool
False FsPath
root
Bool
isEmpty <- m Bool -> ExceptT DbMarkerError m Bool
forall (m :: * -> *) a. Monad m => m a -> ExceptT DbMarkerError m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m Bool -> ExceptT DbMarkerError m Bool)
-> m Bool -> ExceptT DbMarkerError m Bool
forall a b. (a -> b) -> a -> b
$ Set FilePath -> Bool
forall a. Set a -> Bool
Set.null (Set FilePath -> Bool) -> m (Set FilePath) -> m Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HasFS m h -> HasCallStack => FsPath -> m (Set FilePath)
forall (m :: * -> *) h.
HasFS m h -> HasCallStack => FsPath -> m (Set FilePath)
listDirectory HasFS m h
hasFS FsPath
root
if Bool
isEmpty then
ExceptT DbMarkerError m ()
createNetworkMagicFile
else
DbMarkerError -> ExceptT DbMarkerError m ()
forall a. DbMarkerError -> ExceptT DbMarkerError m a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (DbMarkerError -> ExceptT DbMarkerError m ())
-> DbMarkerError -> ExceptT DbMarkerError m ()
forall a b. (a -> b) -> a -> b
$ FilePath -> DbMarkerError
NoDbMarkerAndNotEmpty FilePath
fullPath
where
root :: FsPath
root = [FilePath] -> FsPath
mkFsPath []
pFile :: FsPath
pFile = [Text] -> FsPath
fsPathFromList [Text
dbMarkerFile]
fullPath :: FilePath
fullPath = MountPoint -> FsPath -> FilePath
fsToFilePath MountPoint
mountPoint FsPath
pFile
readNetworkMagicFile :: ExceptT DbMarkerError m NetworkMagic
readNetworkMagicFile :: ExceptT DbMarkerError m NetworkMagic
readNetworkMagicFile = m (Either DbMarkerError NetworkMagic)
-> ExceptT DbMarkerError m NetworkMagic
forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
ExceptT (m (Either DbMarkerError NetworkMagic)
-> ExceptT DbMarkerError m NetworkMagic)
-> m (Either DbMarkerError NetworkMagic)
-> ExceptT DbMarkerError m NetworkMagic
forall a b. (a -> b) -> a -> b
$
HasFS m h
-> FsPath
-> OpenMode
-> (Handle h -> m (Either DbMarkerError NetworkMagic))
-> m (Either DbMarkerError NetworkMagic)
forall (m :: * -> *) h a.
(HasCallStack, MonadThrow m) =>
HasFS m h -> FsPath -> OpenMode -> (Handle h -> m a) -> m a
withFile HasFS m h
hasFS FsPath
pFile OpenMode
ReadMode ((Handle h -> m (Either DbMarkerError NetworkMagic))
-> m (Either DbMarkerError NetworkMagic))
-> (Handle h -> m (Either DbMarkerError NetworkMagic))
-> m (Either DbMarkerError NetworkMagic)
forall a b. (a -> b) -> a -> b
$ \Handle h
h -> do
ByteString
bs <- ByteString -> ByteString
toStrict (ByteString -> ByteString) -> m ByteString -> m ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HasFS m h -> Handle h -> m ByteString
forall (m :: * -> *) h.
Monad m =>
HasFS m h -> Handle h -> m ByteString
hGetAll HasFS m h
hasFS Handle h
h
ExceptT DbMarkerError m NetworkMagic
-> m (Either DbMarkerError NetworkMagic)
forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT (ExceptT DbMarkerError m NetworkMagic
-> m (Either DbMarkerError NetworkMagic))
-> ExceptT DbMarkerError m NetworkMagic
-> m (Either DbMarkerError NetworkMagic)
forall a b. (a -> b) -> a -> b
$ FilePath -> ByteString -> ExceptT DbMarkerError m NetworkMagic
forall (m :: * -> *).
Monad m =>
FilePath -> ByteString -> ExceptT DbMarkerError m NetworkMagic
dbMarkerParse FilePath
fullPath ByteString
bs
createNetworkMagicFile :: ExceptT DbMarkerError m ()
createNetworkMagicFile :: ExceptT DbMarkerError m ()
createNetworkMagicFile = m () -> ExceptT DbMarkerError m ()
forall (m :: * -> *) a. Monad m => m a -> ExceptT DbMarkerError m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ExceptT DbMarkerError m ())
-> m () -> ExceptT DbMarkerError m ()
forall a b. (a -> b) -> a -> b
$
HasFS m h -> FsPath -> OpenMode -> (Handle h -> m ()) -> m ()
forall (m :: * -> *) h a.
(HasCallStack, MonadThrow m) =>
HasFS m h -> FsPath -> OpenMode -> (Handle h -> m a) -> m a
withFile HasFS m h
hasFS FsPath
pFile (AllowExisting -> OpenMode
AppendMode AllowExisting
MustBeNew) ((Handle h -> m ()) -> m ()) -> (Handle h -> m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ \Handle h
h ->
m Word64 -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m Word64 -> m ()) -> m Word64 -> m ()
forall a b. (a -> b) -> a -> b
$ HasFS m h -> Handle h -> ByteString -> m Word64
forall (m :: * -> *) h.
(HasCallStack, Monad m) =>
HasFS m h -> Handle h -> ByteString -> m Word64
hPutAll HasFS m h
hasFS Handle h
h (ByteString -> m Word64) -> ByteString -> m Word64
forall a b. (a -> b) -> a -> b
$
ByteString -> ByteString
fromStrict (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ NetworkMagic -> ByteString
dbMarkerContents NetworkMagic
networkMagic
data DbMarkerError =
NetworkMagicMismatch
FilePath
NetworkMagic
NetworkMagic
| NoDbMarkerAndNotEmpty
FilePath
| CorruptDbMarker
FilePath
deriving (DbMarkerError -> DbMarkerError -> Bool
(DbMarkerError -> DbMarkerError -> Bool)
-> (DbMarkerError -> DbMarkerError -> Bool) -> Eq DbMarkerError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: DbMarkerError -> DbMarkerError -> Bool
== :: DbMarkerError -> DbMarkerError -> Bool
$c/= :: DbMarkerError -> DbMarkerError -> Bool
/= :: DbMarkerError -> DbMarkerError -> Bool
Eq, Int -> DbMarkerError -> ShowS
[DbMarkerError] -> ShowS
DbMarkerError -> FilePath
(Int -> DbMarkerError -> ShowS)
-> (DbMarkerError -> FilePath)
-> ([DbMarkerError] -> ShowS)
-> Show DbMarkerError
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> DbMarkerError -> ShowS
showsPrec :: Int -> DbMarkerError -> ShowS
$cshow :: DbMarkerError -> FilePath
show :: DbMarkerError -> FilePath
$cshowList :: [DbMarkerError] -> ShowS
showList :: [DbMarkerError] -> ShowS
Show)
instance Exception DbMarkerError where
displayException :: DbMarkerError -> FilePath
displayException DbMarkerError
e = case DbMarkerError
e of
NetworkMagicMismatch FilePath
f NetworkMagic
actual NetworkMagic
expected ->
FilePath
"Wrong NetworkMagic in \"" FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> FilePath
f FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> FilePath
"\": " FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> NetworkMagic -> FilePath
forall a. Show a => a -> FilePath
show NetworkMagic
actual FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<>
FilePath
", but expected: " FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> NetworkMagic -> FilePath
forall a. Show a => a -> FilePath
show NetworkMagic
expected
NoDbMarkerAndNotEmpty FilePath
f ->
FilePath
"Missing \"" FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> FilePath
f FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> FilePath
"\" but the folder was not empty"
CorruptDbMarker FilePath
f ->
FilePath
"Corrupt or unreadable \"" FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> FilePath
f FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> FilePath
"\""
dbMarkerFile :: Text
dbMarkerFile :: Text
dbMarkerFile = Text
"protocolMagicId"
dbMarkerContents :: NetworkMagic -> ByteString
dbMarkerContents :: NetworkMagic -> ByteString
dbMarkerContents (NetworkMagic (Word32
nm :: Word32)) =
FilePath -> ByteString
BS.Char8.pack (FilePath -> ByteString) -> FilePath -> ByteString
forall a b. (a -> b) -> a -> b
$ Word32 -> FilePath
forall a. Show a => a -> FilePath
show Word32
nm
dbMarkerParse :: Monad m
=> FilePath
-> ByteString
-> ExceptT DbMarkerError m NetworkMagic
dbMarkerParse :: forall (m :: * -> *).
Monad m =>
FilePath -> ByteString -> ExceptT DbMarkerError m NetworkMagic
dbMarkerParse FilePath
fullPath ByteString
bs =
case FilePath -> Maybe Word32
forall a. Read a => FilePath -> Maybe a
readMaybe (ByteString -> FilePath
BS.Char8.unpack ByteString
bs) of
Just Word32
nm -> NetworkMagic -> ExceptT DbMarkerError m NetworkMagic
forall a. a -> ExceptT DbMarkerError m a
forall (m :: * -> *) a. Monad m => a -> m a
return (NetworkMagic -> ExceptT DbMarkerError m NetworkMagic)
-> NetworkMagic -> ExceptT DbMarkerError m NetworkMagic
forall a b. (a -> b) -> a -> b
$ Word32 -> NetworkMagic
NetworkMagic Word32
nm
Maybe Word32
Nothing -> DbMarkerError -> ExceptT DbMarkerError m NetworkMagic
forall a. DbMarkerError -> ExceptT DbMarkerError m a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (DbMarkerError -> ExceptT DbMarkerError m NetworkMagic)
-> DbMarkerError -> ExceptT DbMarkerError m NetworkMagic
forall a b. (a -> b) -> a -> b
$ FilePath -> DbMarkerError
CorruptDbMarker FilePath
fullPath