halfs-0.2: Halfs/Directory.hs
module Halfs.Directory (Directory(..), getChildrenInodeNums, addChild,
removeChild, getChildWithName, getChildrenNames,
filePathsNoDots, hasChild, DirectoryMap,
dirInodeNum,
-- intentionally not exporting DirectoryCache
-- constructor because we don't want anyone
-- messing with its dirty bit.
DirectoryCache(..), emptyDirectoryCache,
addDirectoryToCache, getDirectoryFromCache,
directoryCacheToList, dirCacheDirty,
markDirectoryCacheClean, rmDirectoryFromCache)
where
import Halfs.Inode(Inode(..), InodeMetadata(..), inodeAddLink)
import {-# SOURCE #-} Halfs.FileHandle (FileHandle, fhInodeNum)
import Data.Integral (INInt)
--base
import Data.Map(Map)
import qualified Data.Map as Map
import Control.Exception(assert)
-- |Maps strings to inode numbers
type DirectoryMap = Map String INInt
data Directory = Directory {dirFile :: FileHandle
,dirContents :: DirectoryMap
,dirDirty :: Bool
}
dirInodeNum :: Directory -> INInt
dirInodeNum d = fhInodeNum $ dirFile d
addChild :: Directory -- ^to add to
-> Inode -- ^new child's inode
-> String -- ^Name
-> Maybe (Directory, Inode) -- ^Nothing if already exists (updated version of parent directory, updated version of child's inode)
addChild (Directory dirFH theContents _) inode name =
case Map.lookup name theContents of
Just _ -> Nothing -- shouldn't be there!
Nothing -> Just ((Directory dirFH (Map.insert name
(assert (inodeNum > 0) inodeNum)
theContents)
True
,inodeAddLink inode))
where inodeNum = inode_num $ metaData inode
-- |Does this directory have the given file in it?
hasChild :: Directory -> String -> Bool
hasChild Directory{dirContents=theMap} s
= Map.member s theMap
removeChild :: Directory -> String -> Directory
removeChild (Directory f c _) k
= Directory f (Map.delete k c) True
-- |Get the inode numbers from the contents of this directory
getChildrenInodeNums :: Directory -> [INInt]
getChildrenInodeNums = Map.elems . dirContents
getChildrenNames :: Directory -> [String]
getChildrenNames = Map.keys . dirContents
getChildWithName :: Directory -> String -> Maybe INInt
getChildWithName dir str
= let e = Map.lookup str (dirContents dir)
in assert (notLEZ e) e
-- Check to make sure that the number is valid; 0 is invalid because
-- its not the child of anything.
where notLEZ Nothing = True
notLEZ (Just n) = n > 0
filePathsNoDots :: [FilePath] -> [FilePath]
filePathsNoDots = filter (\x -> (x /= ".") && (x /= ".."))
-- ------------------------------------------------------------
-- * directory cache
-- ------------------------------------------------------------
-- |Maps from inode numbers to directories.
data DirectoryCache = DirectoryCache { dirCacheDirty :: Bool
, _dirCache :: Map INInt Directory}
emptyDirectoryCache :: DirectoryCache
emptyDirectoryCache = DirectoryCache False Map.empty
directoryCacheToList :: DirectoryCache -> [Directory]
directoryCacheToList (DirectoryCache _ c) = Map.elems c
-- |Also used for updating. If this directory is marked dirty, then
-- the cache will also be marked dirty.
addDirectoryToCache :: DirectoryCache -> Directory -> DirectoryCache
addDirectoryToCache (DirectoryCache cacheDirty c) d
= DirectoryCache (if dirDirty d
then True
else cacheDirty)
(Map.insert (dirInodeNum d) d c)
markDirectoryCacheClean :: DirectoryCache -> DirectoryCache
markDirectoryCacheClean (DirectoryCache _ c)
= DirectoryCache False c
getDirectoryFromCache :: DirectoryCache -> INInt -> Maybe Directory
getDirectoryFromCache (DirectoryCache _ c) i
= Map.lookup i c
-- |Does not complain if the item does not exist. Directory cache
-- stays clean.
rmDirectoryFromCache :: DirectoryCache -> INInt -> DirectoryCache
rmDirectoryFromCache (DirectoryCache _ c) i
= DirectoryCache False (Map.delete i c)