wip, storage audit log, disabled by default

This commit is contained in:
voidlizard 2025-08-27 10:27:12 +03:00
parent dfead1d585
commit 65aa06bafc
6 changed files with 156 additions and 4 deletions

View File

@ -61,6 +61,9 @@ newtype IndexFile a = IndexFile a
newtype StateFile a = StateFile a newtype StateFile a = StateFile a
deriving newtype (IsString,Eq,Ord,Pretty) deriving newtype (IsString,Eq,Ord,Pretty)
data AuditFile = AuditFile
deriving stock (Eq,Ord)
class ToFileName a where class ToFileName a where
toFileName :: a -> FilePath toFileName :: a -> FilePath
@ -85,6 +88,9 @@ instance ToFileName (IndexFile FilePath) where
instance ToFileName (StateFile FileKey) where instance ToFileName (StateFile FileKey) where
toFileName (StateFile fk) = toFileName fk toFileName (StateFile fk) = toFileName fk
instance ToFileName AuditFile where
toFileName = const "audit.log"
newtype FilePrio = FilePrio (Down TimeSpec) newtype FilePrio = FilePrio (Down TimeSpec)
deriving newtype (Eq,Ord) deriving newtype (Eq,Ord)
deriving stock (Generic,Show) deriving stock (Generic,Show)
@ -197,6 +203,24 @@ ncqMakeSectionBS t h bs = do
{-# INLINE ncqMakeSectionBS #-} {-# INLINE ncqMakeSectionBS #-}
ncqMakeAuditSectionBS :: HashRef
-> ByteString
-> NCQSectionType
-> Maybe ByteString
ncqMakeAuditSectionBS h bs = \case
B -> Just $ section ncqBlockPrefix h ""
T -> Just $ section ncqTombPrefix h ""
R -> Just $ section ncqRefPrefix h (BS.take 32 bs)
_ -> Nothing
where
section pref hash pl = do
let slen = ncqKeyLen + fromIntegral (BS.length pref) + fromIntegral (BS.length bs)
let ss = N.bytestring32 slen
ss <> coerce h <> pref <> pl
{-# INLINE section #-}
{-# INLINE ncqMakeAuditSectionBS #-}
data NCQFsckException = data NCQFsckException =
NCQFsckException | NCQFsckIssueExt NCQFsckIssueType NCQFsckException | NCQFsckIssueExt NCQFsckIssueType
deriving stock (Show,Typeable) deriving stock (Show,Typeable)

View File

@ -1,4 +1,5 @@
{-# Language RecordWildCards #-} {-# Language RecordWildCards #-}
{-# Language ViewPatterns #-}
{-# Language MultiWayIf #-} {-# Language MultiWayIf #-}
module HBS2.Storage.NCQ3.Internal where module HBS2.Storage.NCQ3.Internal where
@ -12,6 +13,8 @@ import HBS2.Storage.NCQ3.Internal.Fossil
import HBS2.Storage.NCQ3.Internal.Index import HBS2.Storage.NCQ3.Internal.Index
import HBS2.Storage.NCQ3.Internal.MMapCache import HBS2.Storage.NCQ3.Internal.MMapCache
import Data.Config.Suckless.Script
import Control.Monad.Trans.Cont import Control.Monad.Trans.Cont
import Data.HashPSQ qualified as HPSQ import Data.HashPSQ qualified as HPSQ
import Data.Vector qualified as V import Data.Vector qualified as V
@ -24,11 +27,14 @@ import Data.ByteString.Lazy qualified as LBS
import System.Posix.Files qualified as PFS import System.Posix.Files qualified as PFS
import Control.Concurrent.STM.TSem import Control.Concurrent.STM.TSem
import System.FileLock as FL import System.FileLock as FL
import Lens.Micro.Platform
ncqStorageOpen :: MonadIO m => FilePath -> (NCQStorage -> NCQStorage) -> m NCQStorage ncqStorageOpen :: MonadIO m => FilePath -> (NCQStorage -> NCQStorage) -> m NCQStorage
ncqStorageOpen fp upd = do ncqStorageOpen fp upd = do
let ncqRoot = fp let ncqRoot = fp
let ncqGen = 0 let ncqGen = 0
let ncqAuditEnabled = False
-- let ncqFsync = 16 * megabytes -- let ncqFsync = 16 * megabytes
let ncqFsync = 16 * megabytes let ncqFsync = 16 * megabytes
let ncqWriteQLen = 1024 * 4 let ncqWriteQLen = 1024 * 4
@ -54,6 +60,7 @@ ncqStorageOpen fp upd = do
let !ncqReadThreads = wopNum * 4 let !ncqReadThreads = wopNum * 4
ncqWriteQ <- newTVarIO mempty ncqWriteQ <- newTVarIO mempty
ncqAuditQ <- newTQueueIO
ncqMemTable <- V.fromList <$> replicateM shardNum (newTVarIO mempty) ncqMemTable <- V.fromList <$> replicateM shardNum (newTVarIO mempty)
ncqMMapCachedIdx <- newTVarIO HPSQ.empty ncqMMapCachedIdx <- newTVarIO HPSQ.empty
ncqMMapCachedData <- newTVarIO HPSQ.empty ncqMMapCachedData <- newTVarIO HPSQ.empty
@ -80,7 +87,28 @@ ncqStorageOpen fp upd = do
ncqCurrentFossils <- newTVarIO mempty ncqCurrentFossils <- newTVarIO mempty
ncqReplQueue <- newTVarIO mempty ncqReplQueue <- newTVarIO mempty
let ncq = NCQStorage{..} & upd let ncq0 = NCQStorage{..} -- & upd
let confFile = fp </> "config"
touch confFile
conf <- liftIO (try @_ @SomeException (readFile confFile)) >>= \case
Left e -> warn (viaShow e) >> pure mempty
Right s -> either (\e -> warn (viaShow e) >> pure mempty) pure (parseTop s)
let auditFlag = \case
ListVal [ SymbolVal "audit", StringLike "off" ] -> Just False
ListVal [ SymbolVal "audit", StringLike "on" ] -> Just True
ListVal [ SymbolVal "audit", LitBoolVal f ] -> Just f
_ -> Nothing
let audit = lastDef ncqAuditEnabled [ f | (auditFlag -> Just f) <- conf ]
let auditSet = set #ncqAuditEnabled audit
let applySettings x = foldl (flip ($)) x [auditSet]
let ncq = ncq0 & applySettings & upd
mkdir (ncqGetWorkDir ncq) mkdir (ncqGetWorkDir ncq)
@ -201,6 +229,8 @@ ncqPutBS0 wait ncq@NCQStorage{..} mtp mhref bs' = ncqOperation ncq (pure $ fromM
when upd do when upd do
modifyTVar ncqWriteQ (|> h) modifyTVar ncqWriteQ (|> h)
when ncqAuditEnabled do
for_ (ncqMakeAuditSectionBS h bs' =<< mtp) $ \x -> writeTQueue ncqAuditQ x
putTMVar waiter h putTMVar waiter h

View File

@ -1,4 +1,5 @@
{-# Language ViewPatterns #-} {-# Language ViewPatterns #-}
{-# Language MultiWayIf #-}
module HBS2.Storage.NCQ3.Internal.CLI where module HBS2.Storage.NCQ3.Internal.CLI where
import HBS2.Storage.NCQ3.Internal.Prelude import HBS2.Storage.NCQ3.Internal.Prelude
@ -15,6 +16,7 @@ import HBS2.Net.Auth.Credentials
import Data.Config.Suckless.Script import Data.Config.Suckless.Script
import Control.Monad.Trans.Cont
import Network.ByteOrder qualified as N import Network.ByteOrder qualified as N
import Data.Fixed import Data.Fixed
import Data.Text.Encoding qualified as TE import Data.Text.Encoding qualified as TE
@ -24,6 +26,7 @@ import Data.ByteString (ByteString)
import Data.ByteString qualified as BS import Data.ByteString qualified as BS
import Data.ByteString.Lazy qualified as LBS import Data.ByteString.Lazy qualified as LBS
import Data.HashMap.Strict qualified as HM import Data.HashMap.Strict qualified as HM
import System.IO qualified as IO
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
import System.Environment import System.Environment
import UnliftIO import UnliftIO
@ -303,7 +306,7 @@ entries instances = do
Just (bs,nw) -> do Just (bs,nw) -> do
mval <- nwayHashLookup nw bs (coerce h) mval <- nwayHashLookup nw bs (coerce h)
case mval of case mval of
Nothing -> debug "fucking nothing!" >> pure () Nothing -> pure ()
Just entryBs -> do Just entryBs -> do
let IndexEntry fk off sz = unpackIndexEntry entryBs let IndexEntry fk off sz = unpackIndexEntry entryBs
print $ print $
@ -475,6 +478,66 @@ entries instances = do
e -> throwIO $ BadFormException (mkList e) e -> throwIO $ BadFormException (mkList e)
entry $ bindMatch "ncq3:audit:scan" $ nil_ \case
[ StringLike path ] -> flip runContT pure $ callCC \exit -> do
sto <- ncqStorageOpen path id
let fn = ncqGetFileName sto AuditFile
here <- doesFileExist fn
liftIO $ print $ "WYF?" <+> pretty fn
let readField n bs = if BS.length bs < n then Left bs else Right (BS.take n bs, BS.drop n bs)
unless here do
err $ pretty (toFileName AuditFile) <+> "not found"
exit ()
mmaped <- liftIO $ mmapFileByteString fn Nothing
void $ flip runContT pure $ callCC \stop -> do
flip fix mmaped $ \next bs -> do
(s, rest) <- case readField 4 bs of
Left r -> stop (Left r)
Right s -> pure s
(k, rest1) <- case readField ncqKeyLen rest of
Left r -> stop (Left r)
Right s -> pure s
(p, rest2) <- case readField ncqPrefixLen rest1 of
Left r -> stop (Left r)
Right s -> pure s
let t = if | p == ncqBlockPrefix -> Just B
| p == ncqRefPrefix -> Just R
| p == ncqTombPrefix -> Just T
| otherwise -> Nothing
case t of
Just B -> do
liftIO $ IO.hPrint stdout ("B" <+> pretty (coerce @_ @HashRef k))
next rest2
Just T -> do
liftIO $ IO.hPrint stdout ("T" <+> pretty (coerce @_ @HashRef k))
next rest2
Just R -> do
(v, rest3) <- case readField ncqKeyLen rest2 of
Left r -> stop (Left r)
Right s -> pure s
liftIO $ IO.hPrint stdout ("R" <+> pretty (coerce @_ @HashRef k) <+> pretty (coerce @_ @HashRef v))
next rest3
_ -> do
liftIO $ IO.hPrint stdout ("E" <+> "audit file damaged or incomplete")
pure $ Left rest2
e -> throwIO $ BadFormException (mkList e)
printDataEntry :: MonadUnliftIO m => NCQOffset -> NCQSize -> HashRef -> ByteString -> m () printDataEntry :: MonadUnliftIO m => NCQOffset -> NCQSize -> HashRef -> ByteString -> m ()
printDataEntry offset size key val = do printDataEntry offset size key val = do

View File

@ -4,12 +4,14 @@ module HBS2.Storage.NCQ3.Internal.Prelude
, megabytes , megabytes
, gigabytes , gigabytes
, ncqMakeSectionBS , ncqMakeSectionBS
, ncqMakeAuditSectionBS
, ncqSLen , ncqSLen
, ncqKeyLen , ncqKeyLen
, ncqPrefixLen , ncqPrefixLen
, ncqRefPrefix , ncqRefPrefix
, ncqBlockPrefix , ncqBlockPrefix
, ncqMetaPrefix , ncqMetaPrefix
, ncqTombPrefix
, ncqIsMeta , ncqIsMeta
, ncqFullDataLen , ncqFullDataLen
, ncqEntryPayloadSize , ncqEntryPayloadSize
@ -18,6 +20,7 @@ module HBS2.Storage.NCQ3.Internal.Prelude
, IndexFile(..) , IndexFile(..)
, DataFile(..) , DataFile(..)
, StateFile(..) , StateFile(..)
, AuditFile(..)
, FilePrio(..) , FilePrio(..)
, NCQStorageException(..) , NCQStorageException(..)
, NCQFsckException(..) , NCQFsckException(..)

View File

@ -32,6 +32,8 @@ import System.Posix.IO.ByteString as Posix
import System.Posix.Types as Posix import System.Posix.Types as Posix
import System.Posix.Unistd import System.Posix.Unistd
import Data.ByteString qualified as BS
{- HLINT ignore "Eta reduce" -} {- HLINT ignore "Eta reduce" -}
ncqStorageStop :: forall m . MonadUnliftIO m => NCQStorage -> m () ncqStorageStop :: forall m . MonadUnliftIO m => NCQStorage -> m ()
@ -109,7 +111,11 @@ ncqTryLoadState me@NCQStorage{..} = do
let best = if i < 1 then max s o else s let best = if i < 1 then max s o else s
warn $ red "trim" <+> pretty s <+> pretty best <+> red (pretty (fromIntegral best - realSize)) <+> pretty (takeFileName path) warn $ red "trim"
<+> pretty s
<+> pretty best
<+> red (pretty (fromIntegral best - realSize))
<+> pretty (takeFileName path)
liftIO $ PFS.setFileSize path (fromIntegral best) liftIO $ PFS.setFileSize path (fromIntegral best)
@ -235,6 +241,30 @@ ncqStorageRun ncq@NCQStorage{..} = withSem ncqRunSem $ flip runContT pure do
spawnActivity (ncqStateUpdateLoop ncq) spawnActivity (ncqStateUpdateLoop ncq)
spawnActivity $ flip runContT pure $ callCC \exit -> do
unless ncqAuditEnabled $ exit ()
let auditName = ncqGetFileName ncq AuditFile
touch auditName
let flags = defaultFileFlags { exclusive = False, append = True }
fd <- liftIO (PosixBase.openFd auditName Posix.WriteOnly flags)
void $ ContT $ bracket (pure fd) $ \h -> liftIO do
bss <- atomically $ STM.flushTQueue ncqAuditQ
void $ Posix.fdWrite h (mconcat bss)
closeFd h
forever do
flip fix mempty $ \next bss -> do
-- if BS.length bss >= 4096 then do
if True then do
liftIO (Posix.fdWrite fd bss >> fileSynchronisePortable fd)
next mempty
else do
s <- atomically (readTQueue ncqAuditQ)
next (bss <> s)
spawnActivity $ forever do spawnActivity $ forever do
pause @'Seconds 30 pause @'Seconds 30
ema <- readTVarIO ncqWriteEMA ema <- readTVarIO ncqWriteEMA
@ -406,7 +436,7 @@ ncqStorageRun ncq@NCQStorage{..} = withSem ncqRunSem $ flip runContT pure do
let fname = ncqGetFileName ncq (DataFile fk) let fname = ncqGetFileName ncq (DataFile fk)
-- touch fname -- touch fname
let flags = defaultFileFlags { exclusive = False, creat = Just 0o666 } let flags = defaultFileFlags { exclusive = False, creat = Just 0o666 }
(fk,) <$> liftIO (PosixBase.openFd fname Posix.ReadWrite flags) (fk,) <$> liftIO (PosixBase.openFd fname Posix.WriteOnly flags)
spawnActivity m = do spawnActivity m = do
a <- ContT $ withAsync m a <- ContT $ withAsync m

View File

@ -85,6 +85,7 @@ data NCQState =
data NCQStorage = data NCQStorage =
NCQStorage NCQStorage
{ ncqRoot :: FilePath { ncqRoot :: FilePath
, ncqAuditEnabled :: Bool
, ncqGen :: Int , ncqGen :: Int
, ncqSalt :: HashRef , ncqSalt :: HashRef
, ncqPostponeService :: Timeout 'Seconds , ncqPostponeService :: Timeout 'Seconds
@ -112,6 +113,7 @@ data NCQStorage =
, ncqWrites :: TVar Int , ncqWrites :: TVar Int
, ncqWriteEMA :: TVar Double -- for writes-per-seconds , ncqWriteEMA :: TVar Double -- for writes-per-seconds
, ncqWriteQ :: TVar (Seq HashRef) , ncqWriteQ :: TVar (Seq HashRef)
, ncqAuditQ :: TQueue ByteString
, ncqWriteOps :: Vector (TQueue (IO ())) , ncqWriteOps :: Vector (TQueue (IO ()))
, ncqSyncOps :: TQueue (IO ()) , ncqSyncOps :: TQueue (IO ())
, ncqReadReq :: TQueue (HashRef, TMVar (Maybe Location)) , ncqReadReq :: TQueue (HashRef, TMVar (Maybe Location))