module Data.Vhd.Block
( Block
, blockAddr
, BlockDataMapper
, sectorPerBlock
, blockSectorToByte
, bitmapSizeOfBlockSize
, bitmapOfBlock
, withBlock
, readBitmap
, readData
, readDataRange
, unsafeReadData
, unsafeReadDataRange
, writeDataRange
, readSector
, writeSector
, sectorLength
, iterateSectors
) where
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import qualified Data.ByteString.Unsafe as B
import qualified Data.ByteString.Internal as B
import Data.Vhd.Bitmap
import Data.Vhd.Types
import Data.Vhd.Const
import Data.Vhd.Utils
import Data.Word
import Control.Applicative
import Control.Monad
import Foreign.Ptr
import Foreign.ForeignPtr (newForeignPtr_)
import System.IO.MMap
import Data.Byteable
type BlockDataMapper = VirtualBlockAddress -> BlockByteAddress -> ByteString -> ByteString
data Block = Block
{ blockSize :: BlockSize
, blockAddr :: VirtualBlockAddress
, blockPtr :: Ptr Word8
}
newtype Data = Data (Ptr Word8)
blockSectorToByte :: BlockSectorAddress -> BlockByteAddress
blockSectorToByte (BlockSectorAddress s) = BlockByteAddress (s * sectorLength)
sectorPerBlock :: Block -> BlockSectorAddress
sectorPerBlock block = BlockSectorAddress (fromIntegral bsz `div` sectorLength)
where BlockSize bsz = blockSize block
bitmapSizeOfBlock :: Block -> Int
bitmapSizeOfBlock block = bitmapSizeOfBlockSize $ blockSize block
bitmapSizeOfBlockSize :: BlockSize -> Int
bitmapSizeOfBlockSize (BlockSize blocksz) = fromIntegral ((nbSector `divRoundUp` 8) `roundUpToModulo` sectorLength)
where nbSector = blocksz `divRoundUp` sectorLength
bitmapOfBlock :: Block -> Bitmap
bitmapOfBlock block = Bitmap $ blockPtr block
dataOfBlock :: Block -> Data
dataOfBlock (Block bs _ ptr) = Data $ ptr `plusPtr` (bitmapSizeOfBlockSize bs)
pointerOfData :: Data -> Ptr Word8
pointerOfData (Data ptr) = ptr
withBlock :: FilePath -> BlockSize -> VirtualBlockAddress -> PhysicalSectorAddress -> (Block -> IO a) -> IO a
withBlock file blocksz@(BlockSize bsz) vba sectorOffset f =
mmapWithFilePtr file ReadWrite (Just (offset, len)) $ \(ptr, _) ->
f (Block blocksz vba $ castPtr ptr)
where
offset = (fromIntegral sectorOffset) * sectorLength
len = (fromIntegral bsz) + (fromIntegral $ bitmapSizeOfBlockSize blocksz)
readBitmap :: Block -> IO ByteString
readBitmap block = B.create (fromIntegral len) create
where
len = bitmapSizeOfBlock block
create byteStringPtr = B.memcpy target source (fromIntegral len) where
source = case bitmapOfBlock block of Bitmap b -> b
target = castPtr byteStringPtr
readData :: Maybe BlockDataMapper -> Block -> IO ByteString
readData blockMapper block = readDataRange blockMapper block 0 sz
where (BlockSize sz) = blockSize block
readDataRange :: Maybe BlockDataMapper -> Block -> BlockByteAddress -> Word32 -> IO ByteString
readDataRange blockDataMapper block offset len = B.create (fromIntegral len) $
unsafeReadDataRange blockDataMapper block offset len
unsafeReadData :: Maybe BlockDataMapper -> Block -> Ptr Word8 -> IO ()
unsafeReadData blockDataMapper block =
unsafeReadDataRange blockDataMapper block 0 (fromIntegral sz)
where (BlockSize sz) = blockSize block
unsafeReadDataRange :: Maybe BlockDataMapper
-> Block
-> BlockByteAddress
-> Word32
-> Ptr Word8
-> IO ()
unsafeReadDataRange blockDataMapper block bba@(BlockByteAddress offset) len target =
case blockDataMapper of
Nothing -> B.memcpy target source (fromIntegral len)
Just bmap -> do fptr <- newForeignPtr_ source
let mappedSource = bmap (blockAddr block) bba $ B.fromForeignPtr fptr 0 (fromIntegral len)
withBytePtr mappedSource $ \src ->
B.memcpy target src (fromIntegral len)
where
source = (pointerOfData $ dataOfBlock block) `plusPtr` (fromIntegral offset)
writeDataRange :: Maybe BlockDataMapper
-> Block
-> BlockByteAddress
-> ByteString
-> IO ()
writeDataRange blockMapper block bba@(BlockByteAddress offset) content = do
bitmapSetRange bitmap (fromIntegral sectorStart) (fromIntegral sectorEnd)
B.unsafeUseAsCString (maybe id (\bm -> bm (blockAddr block) bba) blockMapper $ content) (\source -> B.memcpy target (castPtr source) len)
where
len = fromIntegral $ B.length content
bitmap = bitmapOfBlock block
target = (pointerOfData $ dataOfBlock block) `plusPtr` (fromIntegral offset)
sectorStart = offset `div` sectorLength
sectorEnd = (fromIntegral offset + B.length content) `div` sectorLength
readSector :: Maybe BlockDataMapper
-> Block
-> BlockSectorAddress
-> IO (Maybe ByteString)
readSector blockMapper block (BlockSectorAddress bsa) =
allocated >>= \isAllocated ->
case isAllocated of
False -> return Nothing
True -> Just . applyMapper <$> B.create sectorLength copy
where
allocated = bitmapGet bitmap (fromIntegral bsa)
applyMapper = maybe id (\bm -> bm (blockAddr block) bba) blockMapper
bba = BlockByteAddress $ fromIntegral offset
bitmap = bitmapOfBlock block
offset = fromIntegral bsa * sectorLength
addr = (pointerOfData $ dataOfBlock block) `plusPtr` offset
copy dst = B.memcpy dst addr sectorLength
writeSector :: Maybe BlockDataMapper
-> Block
-> BlockSectorAddress
-> ByteString
-> IO ()
writeSector blockMapper block (BlockSectorAddress bsa) content
| B.length content /= sectorLength = error "writeSector data need to be sector'ed size"
| otherwise = do
bitmapSet bitmap (fromIntegral bsa)
B.unsafeUseAsCString (applyMapper content) $ \source ->
B.memcpy target (castPtr source) sectorLength
where
applyMapper = maybe id (\bm -> bm (blockAddr block) bba) blockMapper
bba = BlockByteAddress $ fromIntegral offset
bitmap = bitmapOfBlock block
offset = fromIntegral bsa * sectorLength
target = (pointerOfData $ dataOfBlock block) `plusPtr` offset
iterateSectors :: Block
-> (BlockSectorAddress -> Bool -> IO ())
-> IO ()
iterateSectors block f =
forM_ [0..(nbSectors1)] $ \sector@(BlockSectorAddress bsa) ->
bitmapGet (bitmapOfBlock block) (fromIntegral bsa) >>= f sector
where nbSectors = sectorPerBlock block