module Halfs.SyncStructures (shortSyncFSRoot, syncFSRoot, syncDirectoryToFile) where
import Halfs.Directory(Directory(..), DirectoryCache(..), addDirectoryToCache,
dirCacheDirty, directoryCacheToList)
import Halfs.FSRoot(FSRoot(..), fsRootUpdateInodeCache, fsRootBmInode, FSStatus(..))
import Halfs.Inode (Inode(..), InodeMetadata(..))
import Halfs.FileHandle (fhInode, fileHandle, fhOpenTruncate, fhWrite, fileHandle,
FileHandle(..), FileMode(..))
import Halfs.BasicIO (BinHandle, writeDirectoryBin, syncInode,
writeBlockMapBin, writeInodeMapBin)
import Halfs.BinaryMonad(openBinMemRW, sizeBinMemRW)
import Halfs.TheBlockMap(TheBlockMap,bmDirty)
import Halfs.TheInodeMap(TheInodeMap(..))
import Data.Integral(INInt, intToINInt, INLong)
import Halfs.Utils(bytesPerBlock,
inodeMapInodeNum, blockMapInodeNum)
import Halfs.BufferBlockCache(syncBBCache)
import Halfs.FSState (FSWrite, modify, unsafeWriteGet, readToWrite,
putStrLnWriteRead)
import Control.Exception(assert)
import Control.Monad(mapM_, when)
import qualified Data.Map as Map
syncBlockMapFile :: FSWrite ()
syncBlockMapFile = do
fsroot@FSRoot{blockMap=theBlockMap} <- unsafeWriteGet
let oldSize = num_bytes $ metaData $ fsRootBmInode fsroot
syncObjectToFile theBlockMap
(bmDirty theBlockMap)
blockMapInodeNum
writeBlockMapBin
(updater oldSize) False
where
updater :: INLong -> Inode -> FSRoot -> FSRoot
updater oldSize inode fsroot'@FSRoot{blockMap=bm}
= let isDirty = (oldSize /= (num_bytes $ metaData inode))
in fsroot'{blockMap=bm{bmDirty=isDirty}}
syncInodeMapFile :: FSWrite ()
syncInodeMapFile = do
fsroot <- unsafeWriteGet
let inMap = inodeMap fsroot
syncObjectToFile inMap
(imDirty inMap)
inodeMapInodeNum
writeInodeMapBin updater True
where
updater :: Inode -> FSRoot -> FSRoot
updater _inode fsroot'@FSRoot{inodeMap=im}
= fsroot'{inodeMap=im{imDirty=False}}
syncDirectoryToFile :: Directory -> FSWrite ()
syncDirectoryToFile d = do
let inodeNum = fhInodeNum $ dirFile d
syncObjectToFile (dirContents (assert (dirHasItsInodeNum d) d))
(dirDirty d)
inodeNum writeDirectoryBin (updater d) True
where
updater :: Directory -> Inode -> FSRoot -> FSRoot
updater dir i fsr = let newR@FSRoot{directoryCache=c}
= fsRootUpdateInodeCache fsr i
in newR {directoryCache=addDirectoryToCache c dir{dirDirty=False}}
dirHasItsInodeNum :: Directory -> Bool
dirHasItsInodeNum (Directory FileHandle{fhInodeNum=i} c _)
= case Map.lookup "." c of
Nothing -> True
Just a -> a == i
syncDirectoryCache :: FSWrite ()
syncDirectoryCache = do
FSRoot{directoryCache=c} <- unsafeWriteGet
when (dirCacheDirty c) (mapM_ syncDirectoryToFile (directoryCacheToList c))
modify (\fsroot@FSRoot{directoryCache=_c} -> fsroot)
syncObjectToFile :: a
-> Bool
-> INInt
-> (BinHandle -> a -> FSWrite ())
-> (Inode -> FSRoot -> FSRoot)
-> Bool
-> FSWrite ()
syncObjectToFile _ False _ _ _ _ = return ()
syncObjectToFile obj True inodeNum writer updater trunc = do
newFileHandle <- if trunc
then fhOpenTruncate inodeNum
else return $ fileHandle inodeNum WriteMode
buffer <- openBinMemRW bytesPerBlock
writer buffer obj
objSize <- sizeBinMemRW buffer
(newFH, writtenNum)
<- fhWrite newFileHandle buffer 0 (intToINInt objSize)
inode <- readToWrite $ fhInode newFH
assert (writtenNum == intToINInt objSize) $
modify (updater inode)
syncInodeCache :: FSWrite ()
syncInodeCache = do
FSRoot{inodeCache=(inCache,isDirty)} <- unsafeWriteGet
when isDirty (mapM_ syncInode (Map.elems inCache)
>> modify (\fsroot -> fsroot{inodeCache=(fst $ inodeCache fsroot, False)}))
keepSyncingBlockMap :: FSWrite ()
keepSyncingBlockMap = do
return()
FSRoot{blockMap=theBlockMap} <- unsafeWriteGet
when (bmDirty theBlockMap) (do syncBlockMapFile
syncInodeCache
FSRoot{blockMap=theBlockMap1} <- unsafeWriteGet
when (bmDirty theBlockMap1) keepSyncingBlockMap)
syncFSRoot :: FSWrite ()
syncFSRoot = do
putS "syncing... "
FSRoot{fsStatus=status, bbCache=cache} <- unsafeWriteGet
let syncBBC = syncBBCache cache
assert (status == FsReadWrite) syncBBC
syncDirectoryCache
syncBBC
syncInodeMapFile
syncBBC
syncBlockMapFile
syncBBC
syncInodeCache
keepSyncingBlockMap
syncBBC
shortSyncFSRoot :: FSWrite ()
shortSyncFSRoot = do
putS "syncing (sans free list) ... "
FSRoot{fsStatus=status, bbCache=cache} <- unsafeWriteGet
let syncBBC = syncBBCache cache
assert (status == FsReadWrite) $ return ()
syncDirectoryCache
syncInodeMapFile
syncInodeCache
syncBBC
putS :: String -> FSWrite ()
putS = putStrLnWriteRead