module System.Disk.Partitions.MBR where
import Prelude hiding (head)
import Control.Applicative
import Control.Monad
import Data.Word
import Data.Bits (shiftL, shiftR, (.|.), (.&.))
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Data.Binary
import Data.Binary.Get
import Data.Binary.Put
data Timestamp = Timestamp
{ physicalDrive :: Word8
, seconds :: Word8
, minutes :: Word8
, hours :: Word8 }
deriving (Eq, Show)
instance Binary Timestamp where
get = Timestamp <$> get <*> get <*> get <*> get
put = (sequence_ .) . sequence $ [put . physicalDrive
, put . seconds, put . minutes, put . hours]
data CHS = CHS
{
head :: Word8
, sector :: Word8
, cylinder :: Word16 }
deriving (Eq, Show)
instance Binary CHS where
get = do
(h, s, c) <- (,,) <$> getWord8 <*> getWord8 <*> getWord8
return . CHS h ((s `shiftL` 2) `shiftR` 2) $
fromIntegral c .|. ((fromIntegral s .&. 0xc0) `shiftL` 2)
put (CHS h s c) = do
putWord8 h
putWord8 $ (s .&. 0x3f) .|. fromIntegral (shiftR c 2 .&. 0xc0)
putWord8 . fromIntegral $ 0x00ff .&. c
data PartitionEntry = PartitionEntry
{
status :: Word8
, chsFirst :: CHS
, partitionType :: Word8
, chsLast :: CHS
, lbaFirst :: Word32
, sectors :: Word32 }
deriving (Eq, Show)
instance Binary PartitionEntry where
get = PartitionEntry <$> get <*> get <*> get <*> get
<*> getWord32le <*> getWord32le
put = (sequence_ .) . sequence $ [put . status, put . chsFirst
, put . partitionType, put . chsLast
, putWord32le . lbaFirst, putWord32le . sectors]
nullPartition :: PartitionEntry
nullPartition = PartitionEntry 0 (CHS 0 0 0) 0 (CHS 0 0 0) 0 0
bootable :: PartitionEntry -> Bool
bootable = ((== 1) . (`shiftR` 7)) . status
data PartitionTable = PartitionTable
{ first, second, third, fourth :: PartitionEntry }
deriving (Eq, Show)
instance Binary PartitionTable where
get = PartitionTable <$> get <*> get <*> get <*> get
put = (sequence_ .) . sequence $ [put . first, put . second
, put . third, put . fourth]
nullPartitionTable :: PartitionTable
nullPartitionTable = PartitionTable n n n n
where n = nullPartition
data BootRecord = BootRecord
{
bootloader :: ByteString
, partitions :: PartitionTable
, bootSig :: Word16 }
deriving (Eq, Show)
instance Binary BootRecord where
get = BootRecord <$> getByteString 446 <*> get <*> getWord16le
put = (sequence_ .) . sequence $ [ putByteString . B.take 446 . bootloader
, put . partitions , putWord16le . bootSig ]
emptyBootloader :: B.ByteString
emptyBootloader = B.replicate 446 0
nullBootRecord :: BootRecord
nullBootRecord = BootRecord emptyBootloader nullPartitionTable 0xaa55
getTimestamp :: BootRecord -> Maybe Timestamp
getTimestamp (BootRecord b _ _) = do
guard $ B.length b > 0x0df
guard $ B.index b 0x0da == 0 && B.index b 0x0db == 0
let _1 : _2 : _3 : _4 : _ = B.unpack . B.take 4 . B.drop 0x0dc $ b
return $ Timestamp _1 _2 _3 _4
getDiskSignature :: BootRecord -> Maybe Word32
getDiskSignature (BootRecord b _ _) = do
guard $ B.length b > 0x1bd
guard $ B.index b 0x1bc == 0 && B.index b 0x1bd == 0
let [_1, _2, _3, _4] = map fromIntegral . B.unpack . B.take 4. B.drop 0x1b8 $ b
return $ (_4 << 24) .|. (_3 << 16) .|. (_2 << 8) .|. _1
where (<<) = shiftL